%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
