%% dirtreex.sty — Enhanced directory tree rendering with TikZ
%% v1.0  2026/04/26
%%
%% Copyright (C) 2026 CloudCauldron <w.yizheng@qq.com>
%% Repository: https://github.com/CloudCauldron/dirtreex
%%
%% 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 `author-maintained'.
%% The Current Maintainer of this work is CloudCauldron.
%%
%% This work consists of the file dirtreex.sty.
\NeedsTeXFormat{LaTeX2e}[2020/10/01]
\ProvidesPackage{dirtreex}[2026/04/26 v1.0 Enhanced directory tree package]

%% ------------------------------------------------------------
%%  Engine requirement check.
%%
%%  This package relies on the e-TeX extensions (\numexpr,
%%  \dimexpr, \ifcsname) throughout. Without them the code below
%%  would produce cascading "undefined control sequence" errors,
%%  so we stop immediately with a single clear message.
%%
%%  The error text deliberately avoids naming any \if-family
%%  primitive: TeX's conditional-skipping scanner tracks \if
%%  nesting even inside argument braces, so mentioning e.g.
%%  \ifcsname inside the surrounding \ifx would unbalance it.
\expandafter\ifx\csname numexpr\endcsname\relax
  \PackageError{dirtreex}%
    {e-TeX primitives required (e.g. \string\numexpr, \string\dimexpr)}%
    {Compile with a TeX engine that provides e-TeX -- pdfTeX,
     XeTeX, and LuaTeX all qualify. On LaTeX2e this is the
     default; if you see this, you are likely running a very
     old engine or an unusual format.}%
\fi
\expandafter\ifx\csname numexpr\endcsname\relax \endinput \fi

\RequirePackage{tikz}
\usetikzlibrary{backgrounds}
\RequirePackage{xcolor}
\RequirePackage{pgfkeys}
\RequirePackage{zref-abspage}
\RequirePackage{environ}
\RequirePackage{xparse}

\makeatletter

%% ============================================================
%%  Booleans
%% ============================================================
\newif\ifdte@showbox    \dte@showboxtrue
\newif\ifdte@pagebreak  \dte@pagebreaktrue
\newif\ifdte@splitflag
%% True only while the body of a dirtreex environment is being
%% captured.  \dir and \file read this flag to raise an immediate
%% \PackageError at the call site when they are invoked outside
%% the environment, rather than silently advancing global counters
%% that the next environment's \dte@flush@state would then have
%% to clean up.
\newif\ifdte@inenv
%% Raised (globally) whenever a \zref@extractdefault lookup for an
%% entry's abspage falls back to the 0 default -- meaning the zref
%% label has not yet been written to the .aux file. Consumed by
%% the \AtEndDocument hook at the bottom of this file, which emits
%% a single "Rerun LaTeX..." \PackageWarningNoLine so that latexmk
%% and rerunfilecheck know to schedule another pass.
\newif\ifdte@needrerun
%% Raised by the `tree break at' pgfkeys handler so
%% \dte@enforce@boxfalse@breakat can distinguish a user-supplied
%% override (which must survive `box=false') from the package-default
%% 1em that \dte@apply@breakat@defaults stamps on every env entry.
%% Without this flag a user writing `[box=false]' would silently
%% inherit `tree break at = 1em' (the box=true default) because the
%% defaults macro runs before pgfkeys flips the showbox flag, leaving
%% `tbA / tbB' = 1em with no way to tell `default' from `user'.
%% Reset to false at every env entry; set true inside the `tree
%% break at' handler.
\newif\ifdte@tbset

%% ============================================================
%%  Counters
%% ============================================================
\newcount\dte@cnt         % number of entries in the current tree
\newcount\dte@depth       % current nesting depth while capturing
\newcount\dte@tnum        % tree index (one per environment instance)
\newcount\dte@i           % general-purpose outer-loop counter
\newcount\dte@j           % general-purpose inner-loop counter
\newcount\dte@k           % general-purpose third-level counter
\newcount\dte@scanidx     % linear entry scan index
\newcount\dte@probeIdx    % active-line probe index
\newcount\dte@pagecount   % pieces emitted so far in the current tree
\newcount\dte@breakcount  % number of cross-page breaks detected
\newcount\dte@piecenum    % 1-based index of the piece currently drawn

%% ============================================================
%%  Boxes
%% ============================================================
\newsavebox\dte@tbox  % per-entry text box (name + comment)

%% ============================================================
%%  Dimensions
%% ============================================================
\newdimen\dte@offset      \dte@offset=0.2em     % left gutter before every connector
\newdimen\dte@width       \dte@width=1em        % horizontal arm of each `└` / `├`
\newdimen\dte@sep         \dte@sep=0.2em        % gap between connector and entry text
\newdimen\dte@rulewidth   \dte@rulewidth=0.4pt  % default line width for trunks/arms
\newdimen\dte@all         % sum of \dte@offset + \dte@width + \dte@sep (per-depth stride)
\newdimen\dte@bls         % tree's baselineskip, captured under the tree font
\newdimen\dte@tempdim     % allocated scratch dimen, consumed in-statement
\newdimen\dte@availht     % available height on the current page for a piece
\newdimen\dte@elbowR      \dte@elbowR=0pt       % elbow-arc radius (0pt = sharp `└`/`├`)
\newdimen\dte@ptE         % pass-through effective elbow radius (clamped connE)
\newdimen\dte@ptRaise     % pass-through upper-half raise
\newdimen\dte@ptH         % pass-through vrule height scratch
\newdimen\dte@scratch     % general-purpose allocated scratch dimen
\newcount\dte@scratchcnt  % general-purpose allocated scratch count
\newdimen\dte@leader@dim  % leader space prepended to every non-first piece
\newdimen\dte@contentwd   % configured content-area width (framebox hsize)

\newbox\dte@framebox      % the tree as a single vbox, before splitting
\newbox\dte@splitresult   % one \vsplit chunk of the above
\newbox\dte@extbox        % reserved for extension drawing

%% ============================================================
%%  Style defaults
%% ============================================================
\def\dte@fontsize{\small}
\def\dte@textstyle{\ttfamily}
\def\dte@commentstyle{\rmfamily}

\def\dte@bordercolor{black}
\def\dte@borderwidth{0.4pt}
\def\dte@bgcolor{white}
%% Frame corner radii: top-left, top-right, bottom-right, bottom-left.
\def\dte@cTL{0pt}\def\dte@cTR{0pt}
\def\dte@cBR{0pt}\def\dte@cBL{0pt}
%% Frame padding (inner margin): top, right, bottom, left.
\def\dte@padT{6pt}\def\dte@padR{6pt}
\def\dte@padB{6pt}\def\dte@padL{6pt}

%% Break-at distances applied to every page break.
%%   bbA = first-piece bottom offset above the break line.
%%   bbB = second-piece top offset below the break line.
%%   tbA / tbB play the same role for tree-extension vrules.
%% Defaults are applied per environment by
%% \dte@apply@breakat@defaults (see environment open).

\def\dte@linecolor{black}

%% ============================================================
%%  pgfkeys — option interface
%% ============================================================
%% Parse a strict 1-or-4 comma-separated value list.
%%   Usage: \dte@parsefour{val or v1,v2,v3,v4}\macA\macB\macC\macD
%% A single value is broadcast to all four outputs; four values are
%% distributed in order. Any other arity (2, 3, >=5) raises a
%% \PackageError so malformed user input is reported instead of
%% silently corrupting the layout.
\def\dte@pf@sentinel{\dte@pf@SENTINEL}%
\def\dte@parsefour#1#2#3#4#5{%
  \dte@pf@do#1,\dte@pf@sentinel,\dte@pf@sentinel,\dte@pf@sentinel,%
    \dte@pf@sentinel\dte@pf@end{#2}{#3}{#4}{#5}%
}
\def\dte@pf@do#1,#2,#3,#4,#5\dte@pf@end#6#7#8#9{%
  \def\dte@pf@tA{#2}%
  \def\dte@pf@sOne{\dte@pf@sentinel}%
  \ifx\dte@pf@tA\dte@pf@sOne
    % Exactly one value supplied: #2 is the lone sentinel.
    \def#6{#1}\def#7{#1}\def#8{#1}\def#9{#1}%
  \else
    \def\dte@pf@tB{#5}%
    \def\dte@pf@sFour{\dte@pf@sentinel,\dte@pf@sentinel,\dte@pf@sentinel,\dte@pf@sentinel}%
    \ifx\dte@pf@tB\dte@pf@sFour
      % Exactly four values supplied: #5 is the four-sentinel tail.
      \def#6{#1}\def#7{#2}\def#8{#3}\def#9{#4}%
    \else
      \PackageError{dirtreex}%
        {parsefour expects 1 or 4 comma-separated values}%
        {Provide either one value (applied to all four sides)
         or four values (TL,TR,BR,BL).}%
      \def#6{0pt}\def#7{0pt}\def#8{0pt}\def#9{0pt}%
    \fi
  \fi
}
%% Parse a strict 1-or-2 comma-separated value list (sibling of
%% \dte@parsefour).
%%   Usage: \dte@parsetwo{val or v1,v2}\macA\macB
%% A single value is broadcast to both outputs; two values are
%% distributed in order. Any other arity (3, >=4) raises a
%% \PackageError.
\def\dte@pt@sentinel{\dte@pt@SENTINEL}%
\def\dte@parsetwo#1#2#3{%
  \dte@pt@do#1,\dte@pt@sentinel,\dte@pt@sentinel\dte@pt@end{#2}{#3}%
}
\def\dte@pt@do#1,#2,#3\dte@pt@end#4#5{%
  \def\dte@pt@tA{#2}%
  \def\dte@pt@sOne{\dte@pt@sentinel}%
  \ifx\dte@pt@tA\dte@pt@sOne
    % Exactly one value supplied: #2 is the lone sentinel.
    \def#4{#1}\def#5{#1}%
  \else
    \def\dte@pt@tB{#3}%
    \def\dte@pt@sTwo{\dte@pt@sentinel,\dte@pt@sentinel}%
    \ifx\dte@pt@tB\dte@pt@sTwo
      % Exactly two values supplied: #3 is the two-sentinel tail.
      \def#4{#1}\def#5{#2}%
    \else
      \PackageError{dirtreex}%
        {parsetwo expects 1 or 2 comma-separated values}%
        {Provide either one value (applied to both sides) or
         two values (first-piece-bottom, next-piece-top).}%
      \def#4{0pt}\def#5{0pt}%
    \fi
  \fi
}

\pgfkeys{
  /dte/.cd,
  fontsize/.code={\def\dte@fontsize{#1}},
  %% --- frame box ---
  box/.code={\pgfkeys{/dte/box/.cd,#1}},
  /dte/box/.cd,
  true/.code={\dte@showboxtrue},
  false/.code={\dte@showboxfalse},
  corners/.code={\dte@parsefour{#1}\dte@cTL\dte@cTR\dte@cBR\dte@cBL},
  border color/.store in=\dte@bordercolor,
  border width/.store in=\dte@borderwidth,
  background color/.store in=\dte@bgcolor,
  margin/.code={\dte@parsefour{#1}\dte@padT\dte@padR\dte@padB\dte@padL},
  %% --- page break behaviour ---
  /dte/.cd,
  pagebreak/.code={\pgfkeys{/dte/pagebreak/.cd,#1}},
  /dte/pagebreak/.cd,
  true/.code={\dte@pagebreaktrue},
  false/.code={\dte@pagebreakfalse},
  box break at/.code={%
    \dte@parsetwo{#1}\dte@bbA\dte@bbB
    \edef\dte@bbA{\the\dimexpr\dte@bbA\relax}%
    \edef\dte@bbB{\the\dimexpr\dte@bbB\relax}%
  },
  tree break at/.code={%
    \dte@parsetwo{#1}\dte@tbA\dte@tbB
    \edef\dte@tbA{\the\dimexpr\dte@tbA\relax}%
    \edef\dte@tbB{\the\dimexpr\dte@tbB\relax}%
    \dte@tbsettrue
  },
  %% --- line style ---
  /dte/.cd,
  line color/.store in=\dte@linecolor,
  line width/.code={\dte@rulewidth=#1\relax},
  %% --- elbow shape ---
  %% A single key controls the elbow geometry: 0pt (the default)
  %% produces sharp `└`/`├` right angles; any positive length gives
  %% a rounded arc of that radius. The dispatch in
  %% \dte@draw@connector reads the resolved dimen directly.
  elbow radius/.code={\dte@elbowR=#1\relax},
  %% --- per-entry style overrides (consumed by \dir[...] / \file[...]) ---
  /dte/entry/.cd,
  line color/.code={\gdef\dte@tmp@entry@lc{#1}},
  line width/.code={\gdef\dte@tmp@entry@lw{#1}},
  elbow radius/.code={\gdef\dte@tmp@entry@er{#1}},
}

%% ============================================================
%%  State lifecycle
%% ============================================================
%% Clear every per-entry and per-break global left over from a
%% previous dirtreex environment. Must run BEFORE \dte@cnt and
%% \dte@breakcount are reset to zero at the start of a new
%% environment, because the loops below use those counters as
%% their upper bound.
%%
%% Also clears the per-entry render-time anchor cache
%% \dte@pos@<N>, written by \dte@render@root / \dte@render@entry.
%% That family is unique among the package's scratch slots in that
%% its index range grows with the entry count of the largest tree
%% seen so far, so leaving it uncleaned would let the macro hash
%% table accumulate one stale slot per max(N) across the document.
\def\dte@flush@state{%
  \ifnum\dte@cnt>0
    \dte@i=1\relax
    \loop\ifnum\dte@i<\numexpr\dte@cnt+1\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i d\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i n\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i c\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i t\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i l\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i a\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i lc\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i lw\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@e\the\dte@i er\endcsname\relax
      \expandafter\global\expandafter\let\csname dte@pos@\the\dte@i\endcsname\relax
      \advance\dte@i by 1\relax
    \repeat
  \fi
  \ifnum\dte@breakcount>0
    \dte@i=1\relax
    \loop\ifnum\dte@i<\numexpr\dte@breakcount+1\relax
      \expandafter\global\expandafter\let
        \csname dte@breaklines@\the\dte@i\endcsname\relax
      \expandafter\global\expandafter\let
        \csname dte@breakidx@\the\dte@i\endcsname\relax
      \advance\dte@i by 1\relax
    \repeat
  \fi
}

%% ------------------------------------------------------------
%%  Expansion-safe accessor for csname-keyed slots that
%%  \dte@flush@state may have left as \let \relax.
%%
%%  \ifcsname is true for both `defined' and `let \relax', so a
%%  raw \edef\foo{\csname slot\endcsname} can store the literal
%%  token \relax when the slot has been cleaned but not yet
%%  re-populated. Downstream consumers (\@for, \ifnum,
%%  \dimexpr) then trip a `Missing number' error.
%%
%%  Use this accessor in any expansion-time read of a slot whose
%%  producer might not have run yet on the current pass.
%%  Returns \@empty when the slot is undefined or \relax,
%%  otherwise expands once to the slot's stored tokens.
\def\dte@safeget#1{%
  \expandafter\ifx\csname #1\endcsname\relax \@empty
  \else \csname #1\endcsname \fi}

%% To extract the captured \baselineskip out of the framebox vbox,
%% the package uses a simple \global assignment rather than a brittle \aftergroup
%% token-chain trick. A token-chain approach (ejecting `\dte@bls = <dim> \relax`)
%% would be overly fragile, depending on \the producing default catcodes
%% and risking scope collisions with \let \relax.
%%
%% By using \global\dte@bls = \baselineskip (see the dirtreex env body),
%% the code keeps the logic robust. \dte@bls is a \newdimen register that is
%% naturally re-assigned by the next environment, requiring no explicit flush.

%% ============================================================
%%  Entry storage
%% ============================================================
%% Each entry is stored as a family of csname'd macros keyed by
%% its 1-based index N and a single-letter field suffix:
%%   d = depth, n = name, c = comment, t = type (1=dir, 0=file),
%%   l = is-last-child flag, a = active-depth list,
%%   lc = line colour override, lw = line width override,
%%   er = elbow-radius override.
%% Depth and type are \xdef'd (they are numeric literals and
%% safe to expand); the remaining textual fields are \gdef'd to
%% preserve any unexpanded tokens the user supplied.
\def\dte@estore#1#2#3#4#5{%
  \expandafter\xdef\csname dte@e#1d\endcsname{#2}%
  \expandafter\gdef\csname dte@e#1n\endcsname{#3}%
  \expandafter\gdef\csname dte@e#1c\endcsname{#4}%
  \expandafter\xdef\csname dte@e#1t\endcsname{#5}%
}
\def\dte@eget#1#2{\csname dte@e#1#2\endcsname}

%% Parse the optional bracketed style block of a \dir[...] or
%% \file[...] call and bind the three per-entry override slots.
%%   #1 = option string, #2 = entry index.
%% The three slots always exist after this call (possibly empty),
%% which simplifies downstream retrieval.
\def\dte@store@entry@opts#1#2{%
  \gdef\dte@tmp@entry@lc{}\gdef\dte@tmp@entry@lw{}\gdef\dte@tmp@entry@er{}%
  \def\dte@tmp@opts{#1}%
  \ifx\dte@tmp@opts\@empty\else
    \pgfkeys{/dte/entry/.cd,#1}%
  \fi
  \expandafter\xdef\csname dte@e#2lc\endcsname{\dte@tmp@entry@lc}%
  \expandafter\xdef\csname dte@e#2lw\endcsname{\dte@tmp@entry@lw}%
  \expandafter\xdef\csname dte@e#2er\endcsname{\dte@tmp@entry@er}%
}

%% ============================================================
%%  \dir / \file  (public API, with optional per-entry style)
%% ============================================================
\NewDocumentCommand{\dir}{O{} m m +m}{%
  \ifdte@inenv\else
    \PackageError{dirtreex}%
      {\string\dir\space used outside dirtreex environment}%
      {Every \string\dir\space must appear inside the body of a
       dirtreex environment.}%
  \fi
  \global\advance\dte@cnt by 1\relax
  \dte@estore{\the\dte@cnt}{\the\dte@depth}{#2}{#3}{1}%
  \dte@store@entry@opts{#1}{\the\dte@cnt}%
  % Advance \dte@depth for children, scoped to a local group so
  % \endgroup restores it automatically even if an error occurs
  % inside #4. The parent's depth has already been captured by
  % \dte@estore above, so each child's \the\dte@depth reads the
  % group-local advanced value while every stored depth remains
  % frozen at its original call-time value.
  \begingroup
    \advance\dte@depth by 1\relax
    #4%
  \endgroup
}

\NewDocumentCommand{\file}{O{} m m}{%
  \ifdte@inenv\else
    \PackageError{dirtreex}%
      {\string\file\space used outside dirtreex environment}%
      {Every \string\file\space must appear inside the body of a
       dirtreex environment.}%
  \fi
  \global\advance\dte@cnt by 1\relax
  \dte@estore{\the\dte@cnt}{\the\dte@depth}{#2}{#3}{0}%
  \dte@store@entry@opts{#1}{\the\dte@cnt}%
}

%% ============================================================
%%  Preprocessing (runs after the body is captured)
%% ============================================================
\def\dte@preprocess{%
  \dte@i=1\relax
  \loop\ifnum\dte@i<\numexpr\dte@cnt+1\relax
    \dte@computelast{\the\dte@i}%
    \advance\dte@i by 1\relax
  \repeat
  \dte@computeactive
}

%% ------------------------------------------------------------
%%  Is-last-child flag.
%%
%%  Scans forward from entry #1; entry is "last child" iff no
%%  subsequent entry at the same depth exists before the scan
%%  encounters a strictly shallower depth (which marks leaving
%%  the subtree).
\def\dte@computelast#1{%
  \edef\dte@cld{\dte@eget{#1}{d}}%
  \expandafter\xdef\csname dte@e#1l\endcsname{1}%
  \dte@scanidx=#1\relax
  \dte@cll@loop{#1}%
}
\def\dte@cll@loop#1{%
  \advance\dte@scanidx by 1\relax
  \ifnum\dte@scanidx>\dte@cnt\relax\else
    \edef\dte@nxtd{\dte@eget{\the\dte@scanidx}{d}}%
    \ifnum\dte@nxtd=\dte@cld\relax
      % Sibling found -> NOT last child.
      \expandafter\xdef\csname dte@e#1l\endcsname{0}%
    \else
      \ifnum\dte@nxtd>\dte@cld\relax
        \dte@cll@loop{#1}%
      \fi
    \fi
  \fi
}

%% ------------------------------------------------------------
%%  Active vertical lines at each entry.
%%
%%  For every entry, compute the list of ancestor depths at
%%  which a vertical trunk should be drawn -- i.e. depths that
%%  still have a future sibling to connect to. Stored per entry
%%  as a comma-separated list in \dte@e<N>a. Stored without a
%%  leading comma so every downstream \@for iteration sees a
%%  real element (no empty first iteration to guard against).
\def\dte@computeactive{%
  \dte@i=1\relax
  \loop\ifnum\dte@i<\numexpr\dte@cnt+1\relax
    \expandafter\xdef\csname dte@e\the\dte@i a\endcsname{}%
    \edef\dte@cad{\dte@eget{\the\dte@i}{d}}%
    \dte@j=1\relax
    \dte@ca@depthloop
    \advance\dte@i by 1\relax
  \repeat
}
\def\dte@ca@depthloop{%
  \ifnum\dte@j>\dte@cad\relax\else
    \dte@ca@probe{\the\dte@j}%
    \advance\dte@j by 1\relax
    \expandafter\dte@ca@depthloop
  \fi
}
\def\dte@ca@probe#1{%
  \dte@probeIdx=\dte@i\relax
  \dte@ca@probe@loop{#1}%
}
\def\dte@ca@probe@loop#1{%
  \advance\dte@probeIdx by 1\relax
  \ifnum\dte@probeIdx>\dte@cnt\relax\else
    \edef\dte@pd{\dte@eget{\the\dte@probeIdx}{d}}%
    \ifnum\dte@pd=#1\relax
      % Sibling found at this depth -> active. Store bare on the
      % first append, or concatenate with a leading comma on every
      % subsequent append. This keeps \dte@e<N>a free of a leading
      % comma so consumers never need an empty-first-iteration guard.
      \expandafter\ifx\csname dte@e\the\dte@i a\endcsname\@empty
        \expandafter\xdef\csname dte@e\the\dte@i a\endcsname{#1}%
      \else
        \expandafter\xdef\csname dte@e\the\dte@i a\endcsname{%
          \csname dte@e\the\dte@i a\endcsname,#1}%
      \fi
    \else
      \ifnum\dte@pd<#1\relax
        % Left the subtree at this depth -> not active.
      \else
        \dte@ca@probe@loop{#1}%
      \fi
    \fi
  \fi
}

%% ============================================================
%%  Rendering helpers
%% ============================================================
%% ------------------------------------------------------------
%%  Public extension hook: \DirtreexFormatName
%%
%%  Receives the 1-based entry index as #1. The default
%%  expansion is the stored name (slot `n') followed by a
%%  trailing `/' for directories (slot `t' = 1).
%%
%%  Users may \renewcommand this to inject icons, glyphs, or
%%  styling around the entry name. The replacement runs inside
%%  the row's name vbox under \dte@textstyle and must respect
%%  the row's \hsize and the \strut already inserted by the
%%  caller; otherwise vertical alignment between the name and
%%  its connector will drift.
%%
%%  This is a SEMVER-STABLE PUBLIC SURFACE. The package is
%%  committed to:
%%    - keeping \DirtreexFormatName's argument convention
%%      (single 1-based index) across minor versions;
%%    - keeping \dte@eget{#1}{n} and \dte@eget{#1}{t} stable
%%      enough that a user override built around them keeps
%%      compiling.
%%  Internal slot names beyond `n' / `t' are NOT part of the
%%  public surface; users overriding this hook should rely only
%%  on `n' and `t' if they want forward compatibility.
\newcommand{\DirtreexFormatName}[1]{%
  \dte@eget{#1}{n}%
  \ifnum\dte@eget{#1}{t}=1 /\fi
}
%% Internal renamer. All package-internal call sites keep going
%% through \dte@format@name so a user-side \renewcommand on
%% \DirtreexFormatName is the single point of truth.
\def\dte@format@name#1{\DirtreexFormatName{#1}}
%% Emit the comment half of an entry row. A single-line comment
%% (or a comment short enough not to wrap) is emitted as
%%   \dotfill <comment>
%% so the dot leader fills the gap between the name's right edge
%% and the comment's left edge; the comment then lands flush-right
%% under the row's \hsize because \dotfill's \hfill (level-3
%% stretch) dominates the default \parfillskip (level-2 stretch).
%%
%% A multi-line comment (one that contains \\ or wraps naturally)
%% needs the same right-flush treatment on every physical line, not
%% just the first. We achieve this by locally redefining \\ inside
%% the commentstyle group so that each forced break additionally
%% emits an \hfill at the start of the next line. That \hfill is
%% level-3 stretch and therefore dominates \parfillskip on the final
%% line too, so the whole comment block forms a ragged-LEFT rectangle
%% flush against the row's right edge.
%%
%% The local redefinition expands \\ to \@normalcr (the standard
%% LaTeX text-mode line break) followed by \null\hfill\ignorespaces.
%% We invoke \@normalcr directly rather than \let-saving and
%% replaying the call-site meaning of \\. The latter (replaying
%% whatever \\ meant at the call site) broke inside tabular / array
%% / eqnarray nesting: there the user's \\ is locally \@arraycr, and
%% replaying \@arraycr from the comment's interior emitted
%% `Misplaced \cr' because the alignment in scope was the
%% surrounding tabular row, not the comment. Forcing \@normalcr
%% here makes the line break a paragraph line break regardless of
%% what the surrounding environment has done to \\. Known
%% limitation: a user-written \\[length] form is not honoured (the
%% [length] argument is consumed as text after the line break);
%% this matches the pre-fix behaviour and is not a regression.
%%
%% We \let (not \edef) the stored slot into \dte@ctmp so the raw
%% \\ token survives to emission time, where our local redefinition
%% is in effect. The \ifx \@empty guard still fires on empty
%% comments because \let of an empty macro preserves that meaning.
%%
%% Subtlety: TeX's paragraph builder discards glue and penalties at
%% the start of a line after a forced break. A bare \hfill right
%% after \\ would therefore be dropped on the new line, leaving the
%% continuation flush-LEFT as in the pre-fix rendering. We insert a
%% zero-width \null (an empty hbox, non-discardable) before the
%% \hfill so the stretch survives to push the content right.
\def\dte@format@comment#1{%
  \expandafter\let\expandafter\dte@ctmp\csname dte@e#1c\endcsname
  \ifx\dte@ctmp\@empty\else
    \dotfill{\dte@commentstyle%
      \def\\{\@normalcr\null\hfill\ignorespaces}%
      \dte@ctmp\strut}%
  \fi
}

%% ------------------------------------------------------------
%%  Resolve the current entry's line colour and line width.
%%
%%  Both slots are bound via \expandafter\let\expandafter rather
%%  than \edef, so stored colour macros (e.g. a user-defined
%%  \mycolor from \newcommand) are carried as a token reference
%%  instead of being prematurely expanded -- which would break
%%  xcolor expressions built from non-\protected primitives.
%%
%%  Two guards per slot cover both "undefined" (\csname returns
%%  \relax under e-TeX) and "defined but empty"; both fall back
%%  to the environment-level defaults.
%% Resolve the per-entry line-colour override for entry #1 into
%% the local control sequence #2. Falls back to the env-level
%% \dte@linecolor when the slot is undefined, \let \relax, or
%% \@empty. The two-step \expandafter\let\expandafter chain
%% avoids prematurely expanding user-defined colour macros.
\def\dte@resolve@lc#1#2{%
  \expandafter\let\expandafter#2\csname dte@e#1lc\endcsname
  \ifx#2\relax  \let#2\dte@linecolor \fi
  \ifx#2\@empty \let#2\dte@linecolor \fi
}
%% Resolve the per-entry line-width override for entry #1 into
%% the local control sequence #2. Falls back to the env-level
%% \the\dte@rulewidth (an absolute dimen-literal token sequence)
%% on undefined / \relax / empty slots.
\def\dte@resolve@lw#1#2{%
  \expandafter\let\expandafter#2\csname dte@e#1lw\endcsname
  \ifx#2\relax  \edef#2{\the\dte@rulewidth}\fi
  \ifx#2\@empty \edef#2{\the\dte@rulewidth}\fi
}
\def\dte@resolve@style#1{%
  \dte@resolve@lc{#1}\dte@cur@lc
  \dte@resolve@lw{#1}\dte@cur@lw
}

%% ------------------------------------------------------------
%%  Connector drawing (dispatches sharp / rounded variants).
%%
%%  Dedicated dimens so that nested \color / TikZ calls cannot
%%  clobber an in-flight scratch value before it is consumed.
\newdimen\dte@connV   % vertical height of the connector's upper stem
\newdimen\dte@connR   % raise above baseline (0.2\baselineskip)
\newdimen\dte@connE   % effective elbow radius after clamping

\def\dte@draw@connector#1{%
  % #1 = entry index. The caller has already stored the desired
  % vertical height in \dte@scratch; copy it into a dedicated
  % register because subsequent \color / TikZ calls may reuse
  % scratch dimens internally.
  \dte@connV=\dte@scratch\relax
  \dte@connR=0.2\baselineskip\relax
  %
  % Retrieve the per-entry elbow-radius override. Three branches:
  %   \relax  -> slot undefined (e-TeX semantics for unset \csname)
  %              fall through to the env-level \dte@elbowR.
  %   \@empty -> slot defined but empty; same default.
  %   else    -> use the stored override.
  \expandafter\let\expandafter\dte@cur@er\csname dte@e#1er\endcsname
  \ifx\dte@cur@er\relax
    \dte@connE=\dte@elbowR
  \else\ifx\dte@cur@er\@empty
    \dte@connE=\dte@elbowR
  \else
    \dte@connE=\dte@cur@er\relax
  \fi\fi
  %
  \ifdim\dte@connE>0pt\relax
    \dte@connector@rounded
  \else
    \dte@connector@sharp
  \fi
}

%% Sharp connector: the vertical extends down to meet the
%% horizontal's bottom edge, and the horizontal starts at the
%% vertical's right edge, producing a seamless `└` at any width.
\def\dte@connector@sharp{%
  {\color{\dte@cur@lc}%
   \dte@rulewidth=\dte@cur@lw\relax
   \raise\dte@connR\hbox to 0pt{%
     \kern-0.5\dte@rulewidth
     \vrule width\dte@rulewidth
            height\dte@connV
            depth 0.5\dte@rulewidth\relax
     \hss
   }%
   \raise\dte@connR\hbox{%
     \kern 0.5\dte@rulewidth
     \vrule width\dimexpr\dte@width-0.5\dte@rulewidth\relax
            height 0.5\dte@rulewidth
            depth  0.5\dte@rulewidth
   }%
  }%
}

%% ------------------------------------------------------------
%%  Clamp the dimen named in #1 against the elbow-radius
%%  geometry invariants (width and half-baseline upper bounds,
%%  plus a tighter 0.2\baselineskip bound when \dte@splitflag
%%  is true). The \dte@connV clamp is NOT applied here -- the
%%  rounded connector applies it explicitly just before invoking
%%  this helper, since pass-through and connector paths have
%%  different connV semantics.
\def\dte@clampelbow#1{%
  \ifdim#1>\dte@width            #1=\dte@width             \fi
  \ifdim#1>0.5\baselineskip      #1=0.5\baselineskip       \fi
  \ifdte@splitflag
    \ifdim#1>0.2\baselineskip    #1=0.2\baselineskip       \fi
  \fi
}

\def\dte@connector@rounded{%
  % Clamp the elbow radius to what is geometrically available.
  % The connV clamp stays inline (it is caller-specific); every
  % other invariant goes through \dte@clampelbow so the connector
  % and pass-through paths share one source of truth.
  \ifdim\dte@connE>\dte@connV \dte@connE=\dte@connV \fi
  \dte@clampelbow\dte@connE
  \raise\dte@connR\hbox{%
    \tikz[x=1pt,y=1pt,baseline=0pt]{%
      \useasboundingbox (0,0) rectangle (\strip@pt\dte@width,0.01);%
      \draw[\dte@cur@lc,line width=\dte@cur@lw,line cap=round]
        (0,\strip@pt\dte@connV) -- (0,\strip@pt\dte@connE)
        arc[start angle=180,end angle=270,radius=\strip@pt\dte@connE]
        -- (\strip@pt\dte@width,0);%
    }%
  }%
}

%% ------------------------------------------------------------
%%  Find the next entry at depth #2 that lies strictly after
%%  entry #1. Matches the subtree semantics of
%%  \dte@ca@probe@loop: scan forward while depth > #2; stop at
%%  depth = #2 (sibling found) or depth < #2 (left the subtree).
%%  The result is stored in the global \dte@nextidx (0 if none).
\def\dte@find@nextsibling#1#2{%
  \gdef\dte@nextidx{0}%
  \dte@scanidx=#1\relax
  \dte@fn@loop{#2}%
}
\def\dte@fn@loop#1{%
  \advance\dte@scanidx by 1\relax
  \ifnum\dte@scanidx>\dte@cnt\relax\else
    \edef\dte@fnd{\dte@eget{\the\dte@scanidx}{d}}%
    \ifnum\dte@fnd=#1\relax
      \xdef\dte@nextidx{\the\dte@scanidx}%
    \else
      \ifnum\dte@fnd>#1\relax
        \dte@fn@loop{#1}%
      \fi
    \fi
  \fi
}

%% ------------------------------------------------------------
%%  Pass-through vertical lines at active depths.
%%
%%  A pass-through segment is the stretch of the vertical trunk
%%  that runs between two siblings at a given depth. By
%%  convention each entry owns only its own `└`-shape (upper
%%  stem + horizontal arm); the column continuing DOWN from that
%%  horizontal toward the next sibling belongs to that next
%%  sibling. So the lower half of every pass-through segment
%%  (below the horizontal-arm level) adopts the line style of
%%  the NEXT sibling at that depth.
%%
%%  At the current entry's OWN depth the segment is split at
%%  arc-top (0.2 bls + elbow-radius above the baseline):
%%    * upper half (row-top down to arc-top) uses the entry's
%%      own colour;
%%    * lower half (arc-top down through the row) uses the next
%%      sibling's colour, so the seam lands exactly where the
%%      rounded arc curves off the column.
%%  Splitting at arm-level instead would leave an own-colour
%%  stub visible between arc-top and arm-level -- i.e. a small
%%  coloured peg sitting inside the arc's concavity. For sharp
%%  connectors connE is zero, so this reduces to "split at arm
%%  level" and matches the historical behaviour exactly.
\def\dte@draw@passthrough#1{%
  \edef\dte@save@tempdim{\the\dte@tempdim}%
  \edef\dte@actlist{\dte@safeget{dte@e#1a}}%
  \edef\dte@ownd{\dte@eget{#1}{d}}%
  % Resolve the entry's effective elbow radius. This mirrors the
  % clamping in \dte@connector@rounded so the passthrough's
  % colour split aligns with the actual arc top drawn later:
  % connE is bounded by connV (0.2 bls when splitflag is true,
  % 0.5 bls otherwise) and by 0.5 bls.
  \expandafter\let\expandafter\dte@pt@er\csname dte@e#1er\endcsname
  \ifx\dte@pt@er\relax
    \dte@ptE=\dte@elbowR
  \else\ifx\dte@pt@er\@empty
    \dte@ptE=\dte@elbowR
  \else
    \dte@ptE=\dte@pt@er\relax
  \fi\fi
  \dte@clampelbow\dte@ptE
  \@for\dte@alvl:=\dte@actlist\do{%
    % \dte@actlist is built without a leading comma (see
    % \dte@ca@probe@loop), so every iteration here sees a real
    % active-depth value.
    \dte@k=\dte@alvl\relax
    \dte@tempdim=\dimexpr\numexpr\dte@k-1\relax\dte@all+\dte@offset\relax
    % Style source for this column = the next sibling at this
    % active depth (the entry whose elbow the line leads into).
    \dte@find@nextsibling{#1}{\the\dte@k}%
    \let\dte@pt@src\dte@nextidx
    \dte@resolve@lc{\dte@pt@src}\dte@pt@lc
    \dte@resolve@lw{\dte@pt@src}\dte@pt@lw
    \ifnum\dte@k=\dte@ownd\relax
      % Own-depth column. Split the column at arc-top
      % (baseline + 0.2 bls + connE):
      %   upper half -> row-top down to arc-top, in entry's
      %                 own colour;
      %   lower half -> arc-top down through the row, in the
      %                 next sibling's colour.
      \dte@resolve@lc{#1}\dte@pt@own@lc
      \dte@resolve@lw{#1}\dte@pt@own@lw
      % Upper half: from row top down to arc-top.
      %   raise = 0.2 bls + connE,  h = 0.5 bls - connE,  d = 0pt.
      \dte@ptRaise=0.2\baselineskip
      \advance\dte@ptRaise by\dte@ptE\relax
      \dte@ptH=0.5\baselineskip
      \advance\dte@ptH by-\dte@ptE\relax
      \hbox to 0pt{%
        \kern\dte@tempdim\relax
        \hbox to 0pt{%
          \raise\dte@ptRaise\hbox{%
            {\color{\dte@pt@own@lc}%
             \dte@rulewidth=\dte@pt@own@lw\relax
             \kern-0.5\dte@rulewidth
             \vrule width\dte@rulewidth
                    height\dte@ptH
                    depth  0pt\relax
             \kern-0.5\dte@rulewidth
            }%
          }%
          \hss
        }%
        \hss
      }%
      % Lower half: from arc-top down to row bottom.
      %   raise = 0,  h = 0.2 bls + connE,  d = 0.3 bls.
      \dte@ptH=0.2\baselineskip
      \advance\dte@ptH by\dte@ptE\relax
      \hbox to 0pt{%
        \kern\dte@tempdim\relax
        \hbox to 0pt{%
          {\color{\dte@pt@lc}%
           \dte@rulewidth=\dte@pt@lw\relax
           \kern-0.5\dte@rulewidth
           \vrule width\dte@rulewidth
                  height\dte@ptH
                  depth  0.3\baselineskip\relax
           \kern-0.5\dte@rulewidth
          }%
          \hss
        }%
        \hss
      }%
    \else
      % Pure pass-through column: no elbow at this depth in this
      % row, draw the full-row vrule in the next sibling's style.
      \hbox to 0pt{%
        \kern\dte@tempdim\relax
        \hbox to 0pt{%
          {\color{\dte@pt@lc}%
           \dte@rulewidth=\dte@pt@lw\relax
           \kern-0.5\dte@rulewidth
           \vrule width\dte@rulewidth
                  height 0.7\baselineskip
                  depth  0.3\baselineskip\relax
           \kern-0.5\dte@rulewidth
          }%
          \hss
        }%
        \hss
      }%
    \fi
  }%
  \dte@tempdim=\dte@save@tempdim\relax
}

%% ============================================================
%%  Main rendering
%% ============================================================
%% Horizontal stride per active depth = offset + width + sep.
%% Used by \dte@render@tree and by the dirtreex env body to
%% pin the per-row geometry; both call sites must agree, so
%% extracted here to keep the formula in one place.
\def\dte@compute@stride{%
  \dte@all=\dte@offset
  \advance\dte@all by\dte@width
  \advance\dte@all by\dte@sep
}
\def\dte@render@tree{%
  % Save \parindent / \parskip / \baselineskip / \strut so we
  % can restore the surrounding document's paragraph metrics on
  % exit. Inside the tree we pin baselineskip to \dte@bls (the
  % captured value under the tree's own font) and use a fixed
  % \strut with explicit height/depth so every row has the same
  % 0.7 bls / 0.3 bls partition regardless of inherited metrics.
  \edef\dte@sav@pi{\the\parindent}%
  \edef\dte@sav@ps{\the\parskip}%
  \edef\dte@sav@bls{\the\baselineskip}%
  \let\dte@sav@strut\strut
  %
  \parindent=0pt\relax
  \parskip=0pt\relax
  \baselineskip=\dte@bls\relax
  %% Interline-glue policy. With \lineskiplimit=0pt and
  %% \lineskip=0pt, TeX's per-line rule -- "glue = \baselineskip
  %% - \prevdepth - \ht(new); if glue >= \lineskiplimit use it,
  %% else use \lineskip" -- gives, for line ht/dp clamped at the
  %% standard 0.7 bls / 0.3 bls partition (see \smash overrides
  %% just below):
  %%   single-line row (dp=0.3 bls) -> single-line row (ht=0.7 bls):
  %%     glue = bls - 0.3 bls - 0.7 bls = 0pt;  0pt >= 0pt, use 0pt;
  %%     stride = 0.3 bls + 0 + 0.7 bls = bls. (unchanged)
  %%   multi-line row (dp = 0.3 bls + excess) -> next row:
  %%     glue = bls - (0.3 bls + excess) - 0.7 bls = -excess;
  %%     -excess < 0 = \lineskiplimit, so TeX uses \lineskip = 0pt;
  %%     stride = (0.3 bls + excess) + 0 + 0.7 bls = bls + excess.
  %% The next row therefore drops by exactly the multi-line depth
  %% overflow -- matching the anchor maths \dte@tempdim already
  %% accumulates for \dte@pos@<N>.
  %% Both assignments are scoped to the enclosing
  %% \setbox\dte@framebox=\vbox{...} group (dirtreex env body),
  %% so they auto-revert on vbox close; no manual save/restore.
  \lineskiplimit=0pt\relax
  \lineskip=0pt\relax
  %% \smash the connector and pass-through drawers so their tall
  %% raised hboxes -- which can reach up to (anchor_distance + 0.2
  %% bls) above the entry's baseline (e.g. ~3.7 bls for a top-level
  %% file like README.md whose anchor is the depth-0 root) -- do
  %% NOT inflate the paragraph line's ht above 0.7 bls. Without
  %% this, the line ht would be the connector ht and the
  %% \lineskiplimit=0pt policy above would refuse to compress the
  %% inter-row glue, opening a multi-bls gap before every row whose
  %% anchor sits more than one bls above it. The connectors and
  %% trunks remain visually drawn -- only their ht/dp contribution
  %% to TeX's interline-glue maths is zeroed. These two overrides
  %% (and the two \dte@orig@... helpers) are local to the
  %% \dte@framebox vbox group, so they auto-revert on group close.
  \let\dte@orig@draw@connector\dte@draw@connector
  \def\dte@draw@connector##1{\smash{\dte@orig@draw@connector{##1}}}%
  \let\dte@orig@draw@passthrough\dte@draw@passthrough
  \def\dte@draw@passthrough##1{\smash{\dte@orig@draw@passthrough{##1}}}%
  \def\strut{\vrule width 0pt height 0.7\baselineskip
                                depth 0.3\baselineskip}%
  \dte@compute@stride
  %
  \ifnum\dte@cnt>0\relax
    \dte@render@root
    \dte@i=1\relax
    \loop\ifnum\dte@i<\dte@cnt\relax
      \advance\dte@i by 1\relax
      \edef\dte@curIdx{\the\dte@i}%
      \expandafter\dte@render@entry\expandafter{\dte@curIdx}%
    \repeat
  \fi
  %
  \parindent=\dte@sav@pi\relax
  \parskip=\dte@sav@ps\relax
  \baselineskip=\dte@sav@bls\relax
  \let\strut\dte@sav@strut
}

%% Root entry: rendered without connectors (nothing above it).
%% The box's height/depth are renormalised to the standard
%% 0.7 bls / remainder split so the next entry's position maths
%% is independent of the root row's intrinsic metrics.
\def\dte@render@root{%
  \setbox\dte@tbox=\hbox to\hsize{%
    \vbox{\strut
      \zref@label{dte\the\dte@tnum.1}%
      {\dte@textstyle\dte@format@name{1}\strut}%
      \dte@format@comment{1}%
    }%
  }%
  \dte@scratch=\ht\dte@tbox
  \advance\dte@scratch by\dp\dte@tbox
  \advance\dte@scratch by-0.7\baselineskip
  \ht\dte@tbox=0.7\baselineskip
  \dp\dte@tbox=\dte@scratch
  \par\leavevmode
  \box\dte@tbox
  \endgraf
  \expandafter\xdef\csname dte@pos@1\endcsname{-0.7\baselineskip}%
  \dte@tempdim=\dte@scratch\relax
}

%% Entries 2..N: rendered with pass-through trunks and a
%% connector pointing at their anchor (the previous entry at a
%% shallower-or-equal depth).
\def\dte@render@entry#1{%
  \dte@j=\dte@eget{#1}{d}\relax
  % Emit a weak inter-sibling penalty so \vsplit prefers to
  % break at entry boundaries rather than in the middle of a
  % multi-line comment. Skipped at parent -> first-child
  % transitions (current depth strictly greater than the
  % previous entry's depth), since that pair is structurally
  % a single unit. We are in internal vertical mode here (the
  % previous iteration closed with \endgraf, and so did
  % \dte@render@root), so \penalty emits a proper vlist node.
  \ifnum\dte@j>\dte@eget{\number\numexpr#1-1\relax}{d}\relax\else
    \penalty -50\relax
  \fi
  %
  % Resolve the splitflag and anchor index BEFORE drawing the
  % pass-through, so the passthrough honours the same connV
  % clamping the connector will apply at draw time.  (This keeps
  % the arc-top colour split aligned with the arc's true position
  % even when the anchor lives on the previous page.)
  \dte@k=#1\relax
  \dte@splitflagfalse
  \dte@findanchor{#1}%
  \edef\dte@anchoridx{\the\dte@k}%
  %
  % Pass-through vertical lines at active depths.
  \par\leavevmode
  \dte@draw@passthrough{#1}%
  %
  % Horizontal skip to the column for this entry's own depth.
  % Computed as (depth - 1) * \dte@all + \dte@offset, all in a
  % single \dimexpr so no shared scratch register is involved.
  \dte@scratch=\dimexpr\numexpr\dte@j-1\relax\dte@all+\dte@offset\relax
  \kern\dte@scratch\relax
  %
  % Typeset the entry's name + comment in a vbox.  \hsize is
  % trimmed to leave room for the depth*stride columns already
  % consumed to the left.
  \edef\dte@sh{\the\hsize}%
  \dte@scratch=\dte@j\dte@all
  \advance\hsize by-\dte@scratch\relax
  \setbox\dte@tbox=\vbox{%
    \strut
    \zref@label{dte\the\dte@tnum.#1}%
    {\dte@textstyle\dte@format@name{#1}\strut}%
    \dte@format@comment{#1}%
  }%
  \hsize=\dte@sh\relax
  %
  % Renormalise the entry's box height/depth: height is pinned
  % to 0.7 bls, any excess becomes depth (so multi-line comments
  % simply grow downward in the depth field).
  \dte@scratch=\ht\dte@tbox
  \advance\dte@scratch by\dp\dte@tbox
  \advance\dte@scratch by-0.7\baselineskip
  \ht\dte@tbox=0.7\baselineskip
  \dp\dte@tbox=\dte@scratch
  %
  % Cumulative vertical position, used later to compute the
  % connector length (distance from this entry back to its
  % anchor).
  \expandafter\xdef\csname dte@pos@#1\endcsname{\the\dte@tempdim}%
  \advance\dte@tempdim by\dte@scratch\relax
  \advance\dte@tempdim by 0.7\baselineskip
  %
  % Connector vertical height = distance to anchor, trimmed so
  % the vertical stops at the anchor's text bottom rather than
  % extending into the anchor row.
  \dte@scratch=\csname dte@pos@#1\endcsname\relax
  \advance\dte@scratch by-\csname dte@pos@\dte@anchoridx\endcsname\relax
  \advance\dte@scratch by -0.5\baselineskip\relax
  \ifdim\dte@scratch<0pt \dte@scratch=0pt \fi
  %
  % On a cross-page break the anchor is on the previous piece.
  % Override the computed height with a fixed 0.5 bls stub so
  % the connector's vertical bar reaches the row's top edge
  % (raise 0.2 bls + height 0.5 bls = 0.7 bls = baseline + 0.7 =
  % row top), meeting the passthrough / top-extension vrule from
  % the row above without leaving a visible gap. A shorter stub
  % (e.g. 0.2 bls) would stop 0.3 bls below the row top and break
  % the column visually on every cross-page first-row entry.
  \ifdte@splitflag
    \dte@scratch=0.5\baselineskip
  \fi
  %
  % Resolve per-entry style, draw the connector, then the
  % entry's content box.
  \dte@resolve@style{#1}%
  \dte@draw@connector{#1}%
  \kern\dte@sep
  %
  \box\dte@tbox
  \endgraf
}

%% ------------------------------------------------------------
%%  Walk backwards from entry #1 to find the anchor entry: the
%%  nearest predecessor at strictly-shallower depth that sits
%%  on the same page. If the walk crosses a page boundary the
%%  splitflag is raised and \dte@k is reset to #1 itself so the
%%  connector becomes the cross-page-stub variant.
%%
%%  A zero abspage (from \zref@extractdefault's 0 default) means
%%  the label has not yet landed in the .aux file. That
%%  condition raises \dte@needreruntrue globally BEFORE the
%%  comparison branches, so the end-of-document rerun warning
%%  fires regardless of which branch the comparison takes.
\def\dte@findanchor#1{%
  %% Floor guard. The recursion decrements \dte@k each call and
  %% terminates naturally when an entry's depth is <= \dte@j.
  %% The implicit safety net is that the root (entry 1) has
  %% depth 0, which is <= any \dte@j >= 1. If a user violates
  %% that invariant -- first top-level entry is a \file with
  %% depth >0, or an outer \begingroup shadows \dte@depth and
  %% produces an inverted depth sequence -- a \dte@k of 0 would
  %% read \csname dte@e0d\endcsname (undefined -> \relax) and
  %% raise `Missing number'. Snap to the cross-page-stub
  %% fallback instead.
  \ifnum\dte@k<2
    \dte@k=#1\relax
    \dte@splitflagtrue
  \else
    \advance\dte@k by -1\relax
    \edef\dte@pgA{\zref@extractdefault{dte\the\dte@tnum.\the\dte@k}{abspage}{0}}%
    \edef\dte@pgB{\zref@extractdefault{dte\the\dte@tnum.#1}{abspage}{0}}%
    \ifnum\dte@pgA=0 \global\dte@needreruntrue\fi
    \ifnum\dte@pgB=0 \global\dte@needreruntrue\fi
    \ifx\dte@pgA\dte@pgB
      % Same page -- keep searching shallower only if we are still
      % strictly deeper than the target.
      \ifnum\dte@eget{\the\dte@k}{d}>\dte@j\relax
        \dte@findanchor{#1}%
      \fi
    \else
      % Different page -- anchor is on the previous piece. Mark
      % the connector as a cross-page stub.
      \dte@k=#1\relax
      \dte@splitflagtrue
    \fi
  \fi
}

%% ============================================================
%%  Box output
%% ============================================================
%% Dispatch to the right output path. The breakable path handles
%% both framed and bare trees: with box=false it skips the frame
%% fill/border but still uses \vsplit so the tree can split across
%% page boundaries. Without that, a tall bare tree would be a
%% single unbreakable vbox and TeX would eject it whole to the
%% next page instead of breaking it.
\def\dte@output@framed{%
  \ifdte@pagebreak
    \dte@output@breakable
  \else
    \ifdte@showbox
      \dte@output@singleframe
    \else
      \par\noindent\box\dte@framebox\par
    \fi
  \fi
}

\def\dte@output@singleframe{%
  \par\noindent
  \begin{tikzpicture}
    \node[inner sep=0pt,outer sep=0pt,
          anchor=north west,
         ] (C) {\box\dte@framebox};
    \begin{scope}[on background layer]
    \fill[\dte@bgcolor]
        ([xshift=-\dte@padL,yshift=-\dte@padB]C.south west)
        [rounded corners=\dte@cTL]
        -- ([xshift=-\dte@padL,yshift=\dte@padT]C.north west)
        [rounded corners=\dte@cTR]
        -- ([xshift=\dte@padR,yshift=\dte@padT]C.north east)
        [rounded corners=\dte@cBR]
        -- ([xshift=\dte@padR,yshift=-\dte@padB]C.south east)
        [rounded corners=\dte@cBL]
        -- cycle;
    \end{scope}
    \draw[\dte@bordercolor,line width=\dte@borderwidth]
        ([xshift=-\dte@padL,yshift=-\dte@padB]C.south west)
        [rounded corners=\dte@cTL]
        -- ([xshift=-\dte@padL,yshift=\dte@padT]C.north west)
        [rounded corners=\dte@cTR]
        -- ([xshift=\dte@padR,yshift=\dte@padT]C.north east)
        [rounded corners=\dte@cBR]
        -- ([xshift=\dte@padR,yshift=-\dte@padB]C.south east)
        [rounded corners=\dte@cBL]
        -- cycle;
  \end{tikzpicture}\par
}

%% Leader space prepended to every non-first piece so the first
%% entry's connector has a `│` of one baselineskip feeding into
%% it from above.  \dte@leader@dim is populated from 0.5\dte@bls
%% inside \dte@output@breakable, where \dte@bls is guaranteed
%% to already hold the captured tree-font baselineskip.

%% Available height for a first piece (closed top, open bottom)
%% on the current page:
%%   remaining  = \pagegoal - \pagetotal
%%   available  = remaining - padT - tbA - 2*borderwidth - 1 baselineskip
%% The tree content bottom lands at (page bottom + tbA). The
%% frame's torn bottom at (page bottom + bbA) is drawn by
%% \dte@drawpiece via a separate yshift (see prompt 07).
%% When `box=true`, an extra \baselineskip of safety pad is reserved
%% so the last row of the first piece never collides with the
%% open-bottom frame edge; for `box=false` there is no frame to
%% collide with so the pad is omitted, but we still subtract:
%%   (a) \parskip - \dte@drawpiece's `\par\noindent' transitions
%%       vertical -> horizontal mode and TeX inserts \parskip glue
%%       ABOVE the picture. Sampled BEFORE that insertion, so the
%%       formula must compensate or the bar overshoots `tree break at'
%%       by one \parskip.
%%   (b) \lineskip - the tikzpicture has TikZ's default baseline
%%       (= bbox.bottom), so as a TeX hbox it carries ht=avail and
%%       dp=0. When TeX places it as the only hbox of a new
%%       paragraph the natural interline-glue computation
%%       `bls - prev_dp - new_ht' is large-negative (since
%%       new_ht=avail >> bls), so TeX falls back to \lineskip glue.
%%       That \lineskip pushes the picture - and therefore the
%%       bar's bottom (which sits at C.south = picture baseline) -
%%       one \lineskip below the configured `tree break at' line.
%%       The box=true branch's \baselineskip reservation already
%%       absorbs both contributions, so no separate term is added
%%       there.
\def\dte@compute@availht@first{%
  \dte@availht=\pagegoal
  \advance\dte@availht by-\pagetotal
  \ifdte@showbox
    \advance\dte@availht by-\dimexpr\dte@padT\relax
    \advance\dte@availht by-2\dimexpr\dte@borderwidth\relax
    \advance\dte@availht by-\baselineskip
  \else
    \advance\dte@availht by-\parskip
    \advance\dte@availht by-\lineskip
  \fi
  \advance\dte@availht by-\dimexpr\dte@tbA\relax
}
%% Available height for a middle piece (both sides open). Takes
%% a full textheight, subtracts tree-break-at offsets (tbB top,
%% tbA bottom), the two border widths, one baselineskip safety
%% pad, and the leader space prepended above the first row.
%% The frame's torn-edge offsets bbB / bbA are applied
%% independently inside \dte@drawpiece (see prompt 07).
%% When `box=true`, an extra \baselineskip of safety pad is reserved
%% so the last row of the middle piece never collides with the
%% open-bottom frame edge; for `box=false` there is no frame to
%% collide with and the pad is omitted (otherwise a bare-tree default
%% `tbA=0pt` would still leave a visible ~1 em gap).
\def\dte@compute@availht@middle{%
  \dte@availht=\textheight
  \advance\dte@availht by-\dimexpr\dte@tbB\relax
  \advance\dte@availht by-\dimexpr\dte@tbA\relax
  \ifdte@showbox
    \advance\dte@availht by-2\dimexpr\dte@borderwidth\relax
    \advance\dte@availht by-\baselineskip
  \fi
  \advance\dte@availht by-\dte@leader@dim
}

\def\dte@output@breakable{%
  % Materialise the leader space into an allocated dimen once
  % per environment, before any consumer
  % (\dte@compute@availht@middle or \dte@drawpiece) reads it.
  % \dte@bls was captured under the tree font by the framebox
  % vbox and ejected via \aftergroup -- see the environment body.
  \dte@leader@dim=0.5\dte@bls\relax
  % Capture the framebox's width BEFORE any \vsplit consumes it.
  % A TeX \vbox's natural width is max(contents' widths); when a
  % later \vsplit leaves a remainder that contains only invisible
  % trailing kern/glue/penalty (no \hbox nodes), that remainder's
  % \wd collapses to 0pt. Feeding such a 0pt-wide box into the
  % TikZ content node (\dte@drawpiece below) would shrink the
  % border rectangle to just padL+padR wide on the trailing page.
  % We reapply this width to every piece so the frame always
  % spans the configured content area regardless of remainder
  % density.
  \dte@contentwd=\wd\dte@framebox
  \dte@pagecount=0\relax
  \dte@compute@availht@first
  % Safety: if we are too close to the page bottom to fit any
  % useful content, flush to the next page and recompute.
  \ifdim\dte@availht<2\baselineskip\relax
    \newpage
    \dte@compute@availht@middle
  \fi
  \dte@tempdim=\ht\dte@framebox
  \advance\dte@tempdim by\dp\dte@framebox
  \ifdim\dte@tempdim>\dte@availht
    \dte@dosplit
  \else
    % Single-piece path: when the framebox fits, skip the splitter
    % entirely. With box=true we wrap it in a TikZ frame; with
    % box=false we drop it bare into the surrounding vertical list.
    \ifdte@showbox
      \dte@output@singleframe
    \else
      \par\noindent\box\dte@framebox\par
    \fi
  \fi
}

\def\dte@dosplit{%
  % First piece: split off \dte@availht worth of content from
  % the top of the framebox and draw it with a closed top / open
  % bottom. Continue on the next page.
  %
  % For box=false: clamp \splitmaxdepth=0pt so \vsplit does NOT
  % swallow arbitrary trailing kern/glue/penalty below the last
  % row. LaTeX's default leaves it at \maxdimen, which lets the
  % splitresult extend several points past its nominal \ht and
  % drives the bar bottom past the configured `tree break at'
  % boundary even after \dte@compute@availht@first has subtracted
  % \parskip. The 0pt cap pins the splitresult bottom to its last
  % row's baseline + that row's depth (no extra glue absorbed).
  % For box=true the framed-piece geometry already absorbs that
  % glue inside the bordered region, and changing \splitmaxdepth
  % would visibly resize every framed piece -- so we leave it at
  % the LaTeX default in the box=true branch.
  \splittopskip=0pt\relax
  \ifdte@showbox\else
    \splitmaxdepth=0pt\relax
  \fi
  \setbox\dte@splitresult=\vsplit\dte@framebox to\dte@availht
  \dte@drawpiece{first}%
  \newpage
  \dte@dosplit@cont
}
\def\dte@dosplit@cont{%
  % Middle or last piece. While the remaining framebox still
  % exceeds a middle-piece allotment, split another middle piece
  % off; otherwise emit the whole remainder as the last piece.
  \advance\dte@pagecount by 1\relax
  \dte@compute@availht@middle
  \dte@tempdim=\ht\dte@framebox
  \advance\dte@tempdim by\dp\dte@framebox
  \ifdim\dte@tempdim>\dte@availht
    %% Re-pin \splitmaxdepth=0pt for box=false at every middle-piece
    %% split.  \dte@dosplit set it for the first piece and the
    %% setting persists through the env group, but stamping it
    %% explicitly here keeps the intent local to the call site and
    %% survives any future refactor that hoists either macro out of
    %% the shared scope. Box=true keeps the LaTeX default to avoid
    %% resizing already-framed pieces -- see \dte@dosplit.
    \ifdte@showbox\else
      \splitmaxdepth=0pt\relax
    \fi
    \setbox\dte@splitresult=\vsplit\dte@framebox to\dte@availht
    \dte@drawpiece{middle}%
    \newpage
    \expandafter\dte@dosplit@cont
  \else
    \setbox\dte@splitresult=\box\dte@framebox
    \dte@drawpiece{last}%
  \fi
}

%% ------------------------------------------------------------
%%  Draw one piece with a partial frame.
%%
%%  Per side we decide whether the frame side is CLOSED (bordered,
%%  rounded corners, padded) or OPEN (cut at the page-break line,
%%  controlled by `box break at`):
%%    first  piece -> top closed (padT, cTL, cTR),   bottom open (bbA)
%%    middle piece -> top open   (bbB),              bottom open (bbA)
%%    last   piece -> top open   (bbB),              bottom closed (padB, cBL, cBR)
%%  Tree-extension vrules use the `tree break at` offsets
%%  (tbA / tbB) instead of the box offsets.
\def\dte@drawpiece#1{%
  \par\noindent
  \global\advance\dte@piecenum by 1\relax
  \edef\dte@piecetype{#1}%
  \def\dte@pfirst{first}\def\dte@pmiddle{middle}\def\dte@plast{last}%
  %
  % Continuation pieces (middle / last) are the FIRST hbox on a
  % new page (they immediately follow \newpage in
  % \dte@dosplit@cont). TeX places that first hbox so its
  % baseline lands at max(\topskip, \ht{hbox}) below page top.
  % We want C.north -- which is the picture's baseline -- to land
  % at exactly \dte@tbB below page top, so the tree first row sits
  % at the configured `tree break at` distance and the frame
  % torn-top (drawn at C.north + (tbB-bbB) above C.north) lands
  % at the configured `box break at` distance. Force the policy
  % by zeroing \topskip locally and extending the picture's
  % bounding box upward to include a point at yshift=\dte@tbB
  % above C.north (added inside the tikzpicture below). Without
  % this, the picture's natural ht is only (tbB-bbB) -- just the
  % frame's protrusion above C.north -- and the whole continuation
  % drifts upward by exactly bbB.
  \ifx\dte@piecetype\dte@pfirst\else
    \topskip=0pt\relax
  \fi
  %
  % Force the split remainder to advertise the full configured
  % content width. When a clean \vsplit exhausts every visible
  % node in \dte@framebox (tree ends exactly at the first piece's
  % bottom), the remainder becomes a void box -- and TeX silently
  % ignores \wd/\ht/\dp assignments on void boxes. Without this
  % guard C's bounding box would collapse to 0pt and leave the
  % closing border a sliver of width padL+padR on the trailing
  % page. Replacing void with an empty non-void \hbox makes the
  % subsequent \wd assignment stick; the assignment is a no-op
  % when the remainder already spans the full width.
  \ifvoid\dte@splitresult
    \setbox\dte@splitresult=\hbox{}%
  \fi
  \wd\dte@splitresult=\dte@contentwd\relax
  \begin{tikzpicture}
    % Content node. inner sep=0 pins C's edges to the content's
    % edges. For piece 2+ we prepend \dte@leader@dim worth of
    % blank space above the content so the first entry's
    % connector has room for a `│` leading down from the top.
    \node[inner sep=0pt,outer sep=0pt,
          anchor=north west,
         ] (C) {%
      \ifx\dte@piecetype\dte@pfirst
        \box\dte@splitresult
      \else
        \vbox{\kern\dte@leader@dim\relax\box\dte@splitresult}%
      \fi
    };
    %
    % Continuation pieces: stamp a single invisible coordinate
    % at yshift=\dte@tbB above C.north_west so the picture's
    % natural \ht reaches \dte@tbB. Together with \topskip=0pt
    % (set just before the tikzpicture in \dte@drawpiece) this
    % anchors C.north exactly \dte@tbB below the new page's top
    % edge -- which puts the tree first row at the configured
    % `tree break at' distance and the frame torn-top at the
    % configured `box break at' distance. See the lengthy
    % comment in \dte@drawpiece above the \topskip assignment.
    \ifx\dte@piecetype\dte@pfirst\else
      \path ([yshift=\dte@tbB]C.north west);
    \fi
    %
    % --- Background fill + border (shape depends on piece type) ---
    % Skipped when box=false: the bare tree has no frame to draw,
    % only the tree-extension vrules below still apply.
    \ifdte@showbox
    \ifx\dte@piecetype\dte@pfirst
      % First piece: closed top (rounded TL/TR), open bottom at +bbA.
      \begin{scope}[on background layer]
      \fill[\dte@bgcolor]
          ([xshift=-\dte@padL,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south west)
          [rounded corners=\dte@cTL]
          -- ([xshift=-\dte@padL,yshift=\dte@padT]C.north west)
          [rounded corners=\dte@cTR]
          -- ([xshift=\dte@padR,yshift=\dte@padT]C.north east)
          [sharp corners]
          -- ([xshift=\dte@padR,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south east)
          -- cycle;
      \end{scope}
      \draw[\dte@bordercolor,line width=\dte@borderwidth]
          ([xshift=-\dte@padL,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south west)
          [rounded corners=\dte@cTL]
          -- ([xshift=-\dte@padL,yshift=\dte@padT]C.north west)
          [rounded corners=\dte@cTR]
          -- ([xshift=\dte@padR,yshift=\dte@padT]C.north east)
          [sharp corners]
          -- ([xshift=\dte@padR,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south east);
    \else\ifx\dte@piecetype\dte@plast
      % Last piece: open top at -bbB, closed bottom (rounded BL/BR).
      \begin{scope}[on background layer]
      \fill[\dte@bgcolor]
          ([xshift=-\dte@padL,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north west)
          -- ([xshift=\dte@padR,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north east)
          [rounded corners=\dte@cBR]
          -- ([xshift=\dte@padR,yshift=-\dte@padB]C.south east)
          [rounded corners=\dte@cBL]
          -- ([xshift=-\dte@padL,yshift=-\dte@padB]C.south west)
          [sharp corners]
          -- cycle;
      \end{scope}
      \draw[\dte@bordercolor,line width=\dte@borderwidth]
          ([xshift=-\dte@padL,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north west)
          [rounded corners=\dte@cBL]
          -- ([xshift=-\dte@padL,yshift=-\dte@padB]C.south west)
          [rounded corners=\dte@cBR]
          -- ([xshift=\dte@padR,yshift=-\dte@padB]C.south east)
          [sharp corners]
          -- ([xshift=\dte@padR,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north east);
    \else
      % Middle piece: both sides open; fill a plain rectangle
      % and draw only the two vertical borders.
      \begin{scope}[on background layer]
      \fill[\dte@bgcolor]
          ([xshift=-\dte@padL,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north west)
          rectangle
          ([xshift=\dte@padR,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south east);
      \end{scope}
      \draw[\dte@bordercolor,line width=\dte@borderwidth]
        ([xshift=-\dte@padL,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north west)--
        ([xshift=-\dte@padL,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south west);
      \draw[\dte@bordercolor,line width=\dte@borderwidth]
        ([xshift=\dte@padR,yshift=-\dimexpr\dte@bbB-\dte@tbB\relax]C.north east)--
        ([xshift=\dte@padR,yshift=\dimexpr\dte@bbA-\dte@tbA\relax]C.south east);
    \fi\fi
    \fi
    %
    % --- Top extension vrules (middle and last pieces) ---
    %
    % Each active-depth column inherited from the previous piece
    % has to continue visually from the top of this piece down
    % into row 1 so the `│` appears unbroken across the page
    % break. We draw from yshift=-tbB (just below the top of
    % the content) down through the leader space and 0.3 bls
    % into row 1, where the row's own passthrough / elbow takes
    % over. With leader=0.5 bls the combined extension + row-1
    % passthrough produces a `│` of one baselineskip above the
    % first entry's connector, matching the spacing between
    % siblings elsewhere.
    %
    % Colour convention matches \dte@draw@passthrough: each
    % column uses the colour of the next entry at that depth
    % after the break point (the entry whose elbow the line
    % leads into).
    \ifx\dte@piecetype\dte@pfirst\else
      \expandafter\ifx
        \csname dte@breaklines@\the\numexpr\dte@piecenum-1\relax\endcsname
        \relax
      \else
        \edef\dte@prevbk{\dte@safeget{dte@breaklines@\the\numexpr\dte@piecenum-1\relax}}%
        \edef\dte@bkidx{\dte@safeget{dte@breakidx@\the\numexpr\dte@piecenum-1\relax}}%
        \@for\dte@extlvl:=\dte@prevbk\do{%
          \dte@k=\dte@extlvl\relax
          \dte@tempdim=\dimexpr\numexpr\dte@k-1\relax\dte@all+\dte@offset\relax
          \dte@find@nextsibling{\dte@bkidx}{\the\dte@k}%
          \let\dte@ext@src\dte@nextidx
          \dte@resolve@lc{\dte@ext@src}\dte@ext@lc
          \dte@resolve@lw{\dte@ext@src}\dte@ext@lw
          \draw[\dte@ext@lc,line width=\dte@ext@lw]
            ([xshift=\dte@tempdim,yshift=0pt]C.north west)--
            ([xshift=\dte@tempdim,yshift=-\dte@leader@dim-0.3\dte@bls]C.north west);
        }%
        % Feed-first-child column. When the break falls between
        % a directory and its first child, that child's column
        % is absent from the previous piece's active-depth list
        % (no future sibling exists yet). Draw one extra
        % extension vrule at the first-child's depth in the
        % first-child's colour so the top `└` / `├` on this
        % piece still has a `│` leading down into it.
        \dte@scratchcnt=\numexpr\dte@bkidx+1\relax
        \ifnum\dte@scratchcnt>\dte@cnt\else
          \edef\dte@firstd{\dte@eget{\number\dte@scratchcnt}{d}}%
          \ifnum\dte@firstd>0\relax
            \dte@tempdim=\dimexpr\numexpr\dte@firstd-1\relax\dte@all+\dte@offset\relax
            \dte@resolve@lc{\number\dte@scratchcnt}\dte@ext@lc
            \dte@resolve@lw{\number\dte@scratchcnt}\dte@ext@lw
            \draw[\dte@ext@lc,line width=\dte@ext@lw]
              ([xshift=\dte@tempdim,yshift=0pt]C.north west)--
              ([xshift=\dte@tempdim,yshift=-\dte@leader@dim-0.3\dte@bls]C.north west);
          \fi
        \fi
      \fi
    \fi
    %
    % --- Bottom extension vrules (first and middle pieces) ---
    %
    % Anchored from C.north west: each entry occupies exactly
    % one baselineskip (ht=0.7 bls, dp=0.3 bls), so row R ends
    % R*bls below C.north. For middle pieces (piecenum>1) add
    % the leader offset because the content is shifted down by
    % that amount inside C. We intentionally anchor from north
    % rather than south, because \vsplit pads \ht\splitresult
    % up to the target height and leaves invisible kern below
    % the last row -- C.south would sit below that kern and
    % miscompute the last-row baseline.
    \ifx\dte@piecetype\dte@plast\else
      \expandafter\ifx
        \csname dte@breaklines@\the\dte@piecenum\endcsname
        \relax
      \else
        \dte@scratchcnt=\csname dte@breakidx@\the\dte@piecenum\endcsname\relax
        \ifnum\dte@piecenum>1
          \edef\dte@tmp@idx{\dte@safeget{dte@breakidx@\the\numexpr\dte@piecenum-1\relax}}%
          \advance\dte@scratchcnt by -\dte@tmp@idx\relax
        \fi
        \dte@tempdim=\dte@scratchcnt\dte@bls\relax
        \ifnum\dte@piecenum>1
          \advance\dte@tempdim by\dte@leader@dim\relax
        \fi
        \edef\dte@extTopY{-\the\dte@tempdim}%
        \edef\dte@curbk{\dte@safeget{dte@breaklines@\the\dte@piecenum}}%
        \edef\dte@bkidx{\dte@safeget{dte@breakidx@\the\dte@piecenum}}%
        \@for\dte@extlvl:=\dte@curbk\do{%
          \dte@k=\dte@extlvl\relax
          \dte@tempdim=\dimexpr\numexpr\dte@k-1\relax\dte@all+\dte@offset\relax
          \dte@find@nextsibling{\dte@bkidx}{\the\dte@k}%
          \let\dte@ext@src\dte@nextidx
          \dte@resolve@lc{\dte@ext@src}\dte@ext@lc
          \dte@resolve@lw{\dte@ext@src}\dte@ext@lw
          \draw[\dte@ext@lc,line width=\dte@ext@lw]
            ([xshift=\dte@tempdim,yshift=\dte@extTopY]C.north west)--
            ([xshift=\dte@tempdim,yshift=0pt]C.south west);
        }%
        % Feed-first-child column (bottom extension). Symmetric
        % to the top-extension helper above: if the last entry
        % on this piece is a directory whose first child sits on
        % the NEXT piece, that child's depth is strictly deeper
        % than the last entry's depth and so is absent from both
        % the active-depth list and the own-depth fallback.
        % Draw one extra vrule at the first-child's depth in the
        % first-child's colour so the matching top extension on
        % the next piece meets it cleanly.
        \dte@scratchcnt=\dte@bkidx\relax
        \edef\dte@lastd{\dte@eget{\number\dte@scratchcnt}{d}}%
        \advance\dte@scratchcnt by 1\relax
        \ifnum\dte@scratchcnt>\dte@cnt\else
          \edef\dte@firstd{\dte@eget{\number\dte@scratchcnt}{d}}%
          \ifnum\dte@firstd>\dte@lastd\relax
            \dte@tempdim=\dimexpr\numexpr\dte@firstd-1\relax\dte@all+\dte@offset\relax
            \dte@resolve@lc{\number\dte@scratchcnt}\dte@ext@lc
            \dte@resolve@lw{\number\dte@scratchcnt}\dte@ext@lw
            \draw[\dte@ext@lc,line width=\dte@ext@lw]
              ([xshift=\dte@tempdim,yshift=\dte@extTopY]C.north west)--
              ([xshift=\dte@tempdim,yshift=0pt]C.south west);
          \fi
        \fi
      \fi
    \fi
  \end{tikzpicture}\par
}

%% ============================================================
%%  Cross-page break data (precomputed from zref)
%% ============================================================
%% Scan the entry list for adjacent pairs that resolve to
%% different absolute pages and record, per break, the active-
%% depth list and the last-entry index on the piece that ends
%% at that break. Consumed by \dte@drawpiece to emit the
%% extension vrules and to place the bottom extension's top
%% anchor.
%%
%% A zero abspage means the corresponding zref label has not
%% landed in the .aux yet. We raise \dte@needreruntrue in both
%% lookups BEFORE the \ifx comparison so the end-of-document
%% rerun warning fires no matter how the comparison classifies
%% the pair.
\def\dte@compute@breakdata{%
  \dte@breakcount=0\relax
  \ifnum\dte@cnt<2\relax\else
    \dte@i=2\relax
    \loop\ifnum\dte@i<\numexpr\dte@cnt+1\relax
      \edef\dte@prevIdx{\the\numexpr\dte@i-1\relax}%
      \edef\dte@pgPrev{%
        \zref@extractdefault{dte\the\dte@tnum.\dte@prevIdx}{abspage}{0}}%
      \edef\dte@pgCur{%
        \zref@extractdefault{dte\the\dte@tnum.\the\dte@i}{abspage}{0}}%
      \ifnum\dte@pgPrev=0 \global\dte@needreruntrue\fi
      \ifnum\dte@pgCur=0  \global\dte@needreruntrue\fi
      \ifx\dte@pgPrev\dte@pgCur\else
        \advance\dte@breakcount by 1\relax
        \dte@store@breaklines{\dte@prevIdx}{\the\dte@breakcount}%
      \fi
      \advance\dte@i by 1\relax
    \repeat
  \fi
}

\def\dte@store@breaklines#1#2{%
  % #1 = index of the last entry on the previous page.
  % #2 = 1-based break number.
  %
  % Start from the active-depth list of the last entry, and
  % append the entry's own depth if it is a last-child directory
  % (its own column still needs an extension into the next
  % piece). Guards against a leading comma the same way
  % \dte@ca@probe@loop does, so the consumer \@for loops never
  % see an empty first iteration.
  \edef\dte@bklist{\dte@safeget{dte@e#1a}}%
  \ifnum\dte@eget{#1}{l}=0\relax
    \ifx\dte@bklist\@empty
      \edef\dte@bklist{\dte@eget{#1}{d}}%
    \else
      \edef\dte@bklist{\dte@bklist,\dte@eget{#1}{d}}%
    \fi
  \fi
  \expandafter\xdef\csname dte@breaklines@#2\endcsname{\dte@bklist}%
  % Also remember this piece's last-entry index so \dte@drawpiece
  % can compute the natural last-baseline position without
  % relying on \ht\splitresult (which \vsplit pads up to the
  % target height with invisible kern below the last row).
  \expandafter\xdef\csname dte@breakidx@#2\endcsname{#1}%
}

%% ============================================================
%%  The dirtreex environment
%% ============================================================
%% Apply default break-at distances for the current environment.
%% Called BEFORE \pgfkeys{...,#1}, so user-supplied values override
%% these defaults. Reads \ifdte@showbox as it stands coming into
%% the environment (the persistent package default is true, set at
%% \dte@showboxtrue above); the pgfkeys pass that follows may flip
%% the flag, which is handled by \dte@enforce@boxfalse@breakat.
%% The defaults are pinned to ABSOLUTE LENGTHS at parse time via
%% \edef + \dimexpr, evaluated under the surrounding-document font.
%% This matters because the env body later switches to \dte@fontsize
%% (\small by default) at line 1598 — a stored "1em" token would
%% otherwise be re-interpreted under \small at use time, giving the
%% user a smaller em than the README and the surrounding text imply.
\def\dte@apply@breakat@defaults{%
  \ifdte@showbox
    \edef\dte@bbA{\the\dimexpr 0pt\relax}\edef\dte@bbB{\the\dimexpr 0pt\relax}%
    \edef\dte@tbA{\the\dimexpr 1em\relax}\edef\dte@tbB{\the\dimexpr 1em\relax}%
  \else
    %% box=false: box break at is ignored; force both to 0pt so
    %% any internal math that still references them reads a
    %% benign zero. tree break at defaults to 0pt (the tree is
    %% bare, no visible frame to pull away from).
    \edef\dte@bbA{\the\dimexpr 0pt\relax}\edef\dte@bbB{\the\dimexpr 0pt\relax}%
    \edef\dte@tbA{\the\dimexpr 0pt\relax}\edef\dte@tbB{\the\dimexpr 0pt\relax}%
  \fi
}
%% Enforce the "box=false => box break at is ignored" rule AFTER
%% pgfkeys has had a chance to parse user input. We zero
%% \dte@bbA / \dte@bbB unconditionally when the frame is off, so
%% any downstream math that still references them reads benign
%% zeros. For \dte@tbA / \dte@tbB the rule is conditional: if the
%% user supplied an explicit `tree break at' (\ifdte@tbset is true),
%% their value survives; otherwise we ZERO tbA/tbB so the README's
%% "tree break at defaults to 0pt when box=false" semantic actually
%% holds. Without this gating the package-default 1em (stamped by
%% \dte@apply@breakat@defaults before pgfkeys parsed `box=false')
%% would silently leak through and produce a visible ~1em gap at
%% the continuation page top and an offset bar bottom on the first
%% piece. Pinned to absolute lengths via \edef + \dimexpr for the
%% same reason as \dte@apply@breakat@defaults — see the note above.
\def\dte@enforce@boxfalse@breakat{%
  \ifdte@showbox\else
    \edef\dte@bbA{\the\dimexpr 0pt\relax}\edef\dte@bbB{\the\dimexpr 0pt\relax}%
    \ifdte@tbset\else
      \edef\dte@tbA{\the\dimexpr 0pt\relax}%
      \edef\dte@tbB{\the\dimexpr 0pt\relax}%
    \fi
  \fi
}
\NewEnviron{dirtreex}[1][]{%
  \begingroup
  \dte@inenvtrue
  \global\advance\dte@tnum by 1\relax
  \dte@flush@state
  \global\dte@cnt=0\relax
  \global\dte@depth=0\relax
  %
  \dte@tbsetfalse
  \dte@apply@breakat@defaults
  \pgfkeys{/dte/.cd,#1}%
  \dte@enforce@boxfalse@breakat
  %
  % Execute the environment body once in a throwaway box to
  % collect entry data via \dir / \file. Nothing is typeset yet
  % at this stage.
  \setbox0=\vbox{\BODY}%
  %
  \ifnum\dte@cnt=0\relax
    \PackageWarning{dirtreex}{Empty dirtreex environment; nothing rendered.}%
  \else
    \dte@preprocess
    \dte@compute@breakdata
    %
    \global\dte@piecenum=0\relax
    \dte@fontsize\relax
    \dte@compute@stride
    %
    \setbox\dte@framebox=\vbox{%
      \hsize=\dimexpr\linewidth
        \ifdte@showbox
          -\dte@padL-\dte@padR
          -2\dimexpr\dte@borderwidth\relax
        \fi\relax
      \dte@fontsize\relax
      %% Capture \baselineskip under the tree's actual font into
      %% \dte@bls. The local read inside this vbox drives
      %% \dte@render@tree's row stride; the \global write lifts the
      %% same value into the enclosing env group where \dte@drawpiece
      %% and \dte@compute@availht@middle read it via
      %% \dte@leader@dim = 0.5\dte@bls. Subsequent envs overwrite it,
      %% so no \dte@flush@state entry is needed.
      \dte@bls=\baselineskip
      \global\dte@bls=\baselineskip
      \dte@render@tree
    }%
    %
    \dte@output@framed
  \fi
  %
  \dte@inenvfalse
  \endgroup
}

%% ============================================================
%%  Rerun nudge
%% ============================================================
%% Document-end hook. Fires exactly once per LaTeX run; the
%% message contains the literal substring "Rerun" so latexmk
%% (and the rerunfilecheck package, if loaded) pick it up and
%% schedule another pass. This honours the README's claim that
%% the package emits rerunfilecheck-compatible warnings on a
%% first compile whose .aux is not yet stable.
\AtEndDocument{%
  \ifdte@needrerun
    \PackageWarningNoLine{dirtreex}%
      {Rerun LaTeX to get dirtreex page breaks right}%
  \fi
}

\makeatother
\endinput
