%Package Author - Abhishek Ranna %License - The LaTeX Project Public License 1.3c \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{smartnotes}[2026/04/05 Dynamic side notes with overlap avoidance] \newif\ifsmartnote@disabled \smartnote@disabledfalse \DeclareOption{disable}{\smartnote@disabledtrue} \ProcessOptions\relax \RequirePackage{tikz} \RequirePackage{tikzpagenodes} \usetikzlibrary{calc} \RequirePackage{xparse} \makeatletter % ---------- configuration storage ---------- \newlength{\smartnote@width} \newlength{\smartnote@sep} \newlength{\smartnote@pagepad} \newlength{\smartnote@cornerradius} \newlength{\smartnote@stacksep} \newcommand{\smartnote@bg}{yellow!20} \newcommand{\smartnote@border}{black} \newcommand{\smartnote@text}{black} \newcommand{\smartnote@line}{orange} \newcommand{\smartnote@font}{\footnotesize} \newcommand{\smartnote@shape}{rounded} \newcount\smartnote@sidemode \smartnote@sidemode=0\relax \newif\ifsmartnoteleft \newif\ifsmartnote@fancyline \newcounter{smartnote} \newdimen\smartnote@markx \newdimen\smartnote@marky \newdimen\smartnote@centerx \newdimen\smartnote@pagewestx \newdimen\smartnote@pageeastx \newdimen\smartnote@pagenorthy \newdimen\smartnote@pagesouthy \newdimen\smartnote@textwestx \newdimen\smartnote@texteastx \newdimen\smartnote@leftx \newdimen\smartnote@rightx \newdimen\smartnote@tmp \newdimen\smartnote@noteheight \newdimen\smartnote@halfh \newdimen\smartnote@placey \newdimen\smartnote@top \newdimen\smartnote@bottom \newdimen\smartnote@minbottom \newdimen\smartnote@leftlastbottom \newdimen\smartnote@rightlastbottom \newcount\smartnote@leftpage \newcount\smartnote@rightpage \newcount\smartnote@thispage \newsavebox{\smartnote@measurebox} \smartnote@leftpage=-10000\relax \smartnote@rightpage=-10000\relax \smartnote@leftlastbottom=-16383.99999pt\relax \smartnote@rightlastbottom=-16383.99999pt\relax \pgfkeys{ /smartnote/.is family, /smartnote, default/.style={ width=2.2cm, sep=8pt, pagepad=6pt, stacksep=6pt, cornerradius=4pt, backgroundcolor=cyan!20, bordercolor=black, textcolor=black, linecolor=blue, font={\footnotesize\sffamily}, shape=rounded, side=auto, fancyline }, width/.code={\setlength{\smartnote@width}{#1}}, sep/.code={\setlength{\smartnote@sep}{#1}}, pagepad/.code={\setlength{\smartnote@pagepad}{#1}}, stacksep/.code={\setlength{\smartnote@stacksep}{#1}}, cornerradius/.code={\setlength{\smartnote@cornerradius}{#1}}, backgroundcolor/.code={\renewcommand{\smartnote@bg}{#1}}, bordercolor/.code={\renewcommand{\smartnote@border}{#1}}, textcolor/.code={\renewcommand{\smartnote@text}{#1}}, linecolor/.code={\renewcommand{\smartnote@line}{#1}}, color/.code={% \renewcommand{\smartnote@bg}{#1}% \renewcommand{\smartnote@line}{#1}% }, font/.code={\renewcommand{\smartnote@font}{#1}}, fancyline/.code={\smartnote@fancylinetrue}, nofancyline/.code={\smartnote@fancylinefalse}, shape/.is choice, shape/rectangle/.code={\renewcommand{\smartnote@shape}{rectangle}}, shape/rounded/.code={\renewcommand{\smartnote@shape}{rounded}}, rectangle/.style={shape=rectangle}, rounded/.style={shape=rounded}, /smartnote/global/.style={}, side/.is choice, side/auto/.code={\smartnote@sidemode=0\relax}, side/left/.code={\smartnote@sidemode=1\relax}, side/right/.code={\smartnote@sidemode=2\relax}, auto/.style={side=auto}, left/.style={side=left}, right/.style={side=right} } \newcommand{\notesetup}[1]{\pgfkeys{/smartnote/global/.style={#1}}} \notesetup{} \newcommand{\smartnote@setshapeopts}{% \edef\smartnote@shapetest{\smartnote@shape}% \def\smartnote@rect{rectangle}% \ifx\smartnote@shapetest\smartnote@rect \tikzset{smartnote shape/.style={rectangle,rounded corners=0pt}}% \else \tikzset{smartnote shape/.style={rectangle,rounded corners=\the\smartnote@cornerradius}}% \fi } \newcommand{\smartnote@measureheight}[1]{% \setbox\smartnote@measurebox=\vbox{% \hsize=\dimexpr\smartnote@width-1.6ex-1pt\relax \parindent=0pt \leftskip=0pt \rightskip=0pt \smartnote@font #1\par }% \smartnote@noteheight=\dimexpr\ht\smartnote@measurebox+\dp\smartnote@measurebox+1.6ex+1pt\relax \smartnote@halfh=\smartnote@noteheight \divide\smartnote@halfh by 2\relax } \newcommand{\smartnote@resolvey}[1]{% \smartnote@placey=\smartnote@marky \smartnote@top=\dimexpr\smartnote@placey+\smartnote@halfh\relax \smartnote@minbottom=\dimexpr\smartnote@pagesouthy+\smartnote@pagepad\relax \ifnum#1=1\relax \ifnum\smartnote@leftpage=\smartnote@thispage\relax \smartnote@tmp=\dimexpr\smartnote@leftlastbottom-\smartnote@stacksep\relax \ifdim\smartnote@top>\smartnote@tmp \smartnote@top=\smartnote@tmp \smartnote@placey=\dimexpr\smartnote@top-\smartnote@halfh\relax \fi \fi \smartnote@bottom=\dimexpr\smartnote@placey-\smartnote@halfh\relax \ifdim\smartnote@bottom<\smartnote@minbottom \smartnote@bottom=\smartnote@minbottom \smartnote@placey=\dimexpr\smartnote@bottom+\smartnote@halfh\relax \fi \global\smartnote@leftlastbottom=\dimexpr\smartnote@placey-\smartnote@halfh\relax \global\smartnote@leftpage=\smartnote@thispage \else \ifnum\smartnote@rightpage=\smartnote@thispage\relax \smartnote@tmp=\dimexpr\smartnote@rightlastbottom-\smartnote@stacksep\relax \ifdim\smartnote@top>\smartnote@tmp \smartnote@top=\smartnote@tmp \smartnote@placey=\dimexpr\smartnote@top-\smartnote@halfh\relax \fi \fi \smartnote@bottom=\dimexpr\smartnote@placey-\smartnote@halfh\relax \ifdim\smartnote@bottom<\smartnote@minbottom \smartnote@bottom=\smartnote@minbottom \smartnote@placey=\dimexpr\smartnote@bottom+\smartnote@halfh\relax \fi \global\smartnote@rightlastbottom=\dimexpr\smartnote@placey-\smartnote@halfh\relax \global\smartnote@rightpage=\smartnote@thispage \fi } \ifsmartnote@disabled \providecommand{\note}{} \RenewDocumentCommand{\note}{O{} +m}{}% \else \providecommand{\note}{} \RenewDocumentCommand{\note}{O{} +m}{% \begingroup \pgfqkeys{/smartnote}{default,global}% \pgfqkeys{/smartnote}{#1}% \smartnote@setshapeopts \smartnote@measureheight{#2}% \stepcounter{smartnote}% \edef\smartnote@mark{smartnote-mark-\thesmartnote}% \edef\smartnote@box{smartnote-box-\thesmartnote}% \leavevmode \tikz[remember picture,overlay,baseline=-0.6ex]{% \coordinate (\smartnote@mark) at (0,0); \pgfextractx{\smartnote@markx}{\pgfpointanchor{\smartnote@mark}{center}}% \pgfextracty{\smartnote@marky}{\pgfpointanchor{\smartnote@mark}{center}}% \pgfextractx{\smartnote@centerx}{\pgfpointanchor{current page text area}{center}}% \pgfextractx{\smartnote@pagewestx}{\pgfpointanchor{current page}{west}}% \pgfextractx{\smartnote@pageeastx}{\pgfpointanchor{current page}{east}}% \pgfextracty{\smartnote@pagenorthy}{\pgfpointanchor{current page}{north}}% \pgfextracty{\smartnote@pagesouthy}{\pgfpointanchor{current page}{south}}% \pgfextractx{\smartnote@textwestx}{\pgfpointanchor{current page text area}{west}}% \pgfextractx{\smartnote@texteastx}{\pgfpointanchor{current page text area}{east}}% \global\smartnote@thispage=\value{page}\relax % Decide side: forced left/right or automatic. \ifcase\smartnote@sidemode \ifdim\smartnote@markx<\smartnote@centerx \global\smartnotelefttrue \else \global\smartnoteleftfalse \fi \or \global\smartnotelefttrue \or \global\smartnoteleftfalse \fi % X positions. \smartnote@leftx=\smartnote@textwestx \advance\smartnote@leftx by -\smartnote@sep \advance\smartnote@leftx by -\smartnote@width \smartnote@tmp=\smartnote@pagewestx \advance\smartnote@tmp by \smartnote@pagepad \ifdim\smartnote@leftx<\smartnote@tmp \smartnote@leftx=\smartnote@tmp \fi \smartnote@rightx=\smartnote@texteastx \advance\smartnote@rightx by \smartnote@sep \smartnote@tmp=\smartnote@pageeastx \advance\smartnote@tmp by -\smartnote@pagepad \advance\smartnote@tmp by -\smartnote@width \ifdim\smartnote@rightx>\smartnote@tmp \smartnote@rightx=\smartnote@tmp \fi \ifsmartnoteleft \smartnote@resolvey{1}% \node[ draw=\smartnote@border, fill=\smartnote@bg, text=\smartnote@text, line width=0.5pt, smartnote shape, inner sep=0.8ex, text width=\dimexpr\smartnote@width-1.6ex-1pt\relax, align=left, anchor=west, font=\smartnote@font ] (\smartnote@box) at (\the\smartnote@leftx,\the\smartnote@placey) {#2}; \ifsmartnote@fancyline \tikzstyle{smartnoteboth}=[line width=3pt, draw=\smartnote@line, opacity=0.15] \tikzstyle{smartnoteline}=[shorten >=5pt, line cap=round] \tikzstyle{smartnotehead}=[shorten >=-1pt, dash pattern=on 0pt off 1pt, ->] \foreach \s in {smartnoteline,smartnotehead}{% \draw[smartnoteboth,\s] (\smartnote@box.north east).. controls +(0:0) and +(90:1.5)..([yshift=1ex] \smartnote@mark);% }% \fi \else \smartnote@resolvey{2}% \node[ draw=\smartnote@border, fill=\smartnote@bg, text=\smartnote@text, line width=0.5pt, smartnote shape, inner sep=0.8ex, text width=\dimexpr\smartnote@width-1.6ex-1pt\relax, align=left, anchor=west, font=\smartnote@font ] (\smartnote@box) at (\the\smartnote@rightx,\the\smartnote@placey) {#2}; \ifsmartnote@fancyline \tikzstyle{smartnoteboth}=[line width=3pt, draw=\smartnote@line, opacity=0.15] \tikzstyle{smartnoteline}=[shorten >=5pt, line cap=round] \tikzstyle{smartnotehead}=[shorten >=-1pt, dash pattern=on 0pt off 1pt, ->] \foreach \s in {smartnoteline,smartnotehead}{% \draw[smartnoteboth,\s] (\smartnote@box.north west).. controls +(0:0) and +(90:1.5)..([yshift=1ex] \smartnote@mark);% }% \fi \fi }% \endgroup } \fi \makeatother