20
0
Files
cdsc_tex/slides_template/talk-notes.sty
Benjamin Mako Hill ab50e8e025 Extend frames for overlay-specified notes, as beamer does
A frame whose only <2> sat on a \note lost its second slide: beamer
counts note specs toward the slide count, and talk-notes did not.
Feed the spec through the class's overlay test, discarding the result;
its side effect asks for the slides the spec names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 00:11:19 +09:00

92 lines
3.4 KiB
TeX

% talk-notes.sty -- capture speaker notes for assembly outside LaTeX.
%
% The ltx-talk class has no \note command (upstream issue #156 is open, and the
% maintainer has said notes are wanted but unimplemented). This package fills
% the gap without touching the slides themselves: \note{...} typesets nothing
% and instead records its text to \jobname.notes, which the mknotes program
% reads to build the presenter PDF.
%
% The awkward part of doing this outside LaTeX is that a frame does not
% correspond to a page: overlays expand one frame into several slides, and the
% source cannot predict how many. That is why the page records are written from
% the shipout hook, which is the first point at which the frame and slide
% identity of a page is actually known.
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{talk-notes}[2026-08-14 v0.1 Capture speaker notes for ltx-talk]
\ExplSyntaxOn
\iow_new:N \g__talknotes_iow
\tl_new:N \g__talknotes_section_tl
\tl_new:N \g__talknotes_subsection_tl
\AddToHook { begindocument }
{ \iow_open:Nn \g__talknotes_iow { \c_sys_jobname_str .notes } }
% The stream is deliberately not closed by hand: LaTeX closes it at the end of
% the run, and closing it from the enddocument hook races the final shipout.
% Track the section and subsection so the notes page can show where we are.
% ltx-talk exposes the current title as \l__talk_section_tl; guard the lookup
% so that a class change does not turn this into an error.
\AddToHook { section/begin }
{
\cs_if_exist:NT \l__talk_section_tl
{
\tl_gset:NV \g__talknotes_section_tl \l__talk_section_tl
\tl_gclear:N \g__talknotes_subsection_tl
}
}
\AddToHook { subsection/begin }
{
\cs_if_exist:NT \l__talk_subsection_tl
{ \tl_gset:NV \g__talknotes_subsection_tl \l__talk_subsection_tl }
}
% One record per shipped page.
\AddToHook { shipout/before }
{
\iow_now:Nx \g__talknotes_iow
{
PAGE ~ \arabic { page } |
FRAME ~ \int_use:N \g__talk_frame_int |
SLIDE ~ \int_use:N \g__talk_slide_int
}
\iow_now:Nx \g__talknotes_iow
{ SECTION ~ \tl_to_str:N \g__talknotes_section_tl }
\iow_now:Nx \g__talknotes_iow
{ SUBSECTION ~ \tl_to_str:N \g__talknotes_subsection_tl }
}
% \note<overlay spec>[item]{text}
%
% Follows beamer's interface. A plain \note contributes running text; \note[item]
% contributes an entry to a numbered list printed after that text.
%
% The body is written out stringified, so LaTeX markup inside a note survives
% to be typeset again on the notes page. A note is recorded once per slide of
% its frame (the frame body is re-run for each), so mknotes deduplicates.
% Records are bracketed by ENDNOTE because a long note may be broken across
% output lines.
\NewDocumentCommand \note { d<> o +m }
{
% Run the spec through the class's overlay test, discarding the result:
% its side effect asks for the slides the spec names, so a frame whose
% only <2> is on a note still gets two slides, as under beamer.
\IfNoValueF {#1}
{
\cs_if_exist:NT \__talk_if_overlay:nT
{ \__talk_if_overlay:nT {#1} { } }
}
\iow_now:Nx \g__talknotes_iow
{
NOTE ~ FRAME ~ \int_use:N \g__talk_frame_int |
SPEC ~ \IfNoValueTF {#1} { - } { \tl_to_str:n {#1} } |
KIND ~ \IfNoValueTF {#2} { text } { \tl_to_str:n {#2} }
}
\iow_now:Nx \g__talknotes_iow { TEXT ~ \tl_to_str:n {#3} }
\iow_now:Nn \g__talknotes_iow { ENDNOTE }
}
\ExplSyntaxOff