@@ -59,8 +59,7 @@ local function process_metadata(meta)
5959
6060 local function add_list (list )
6161 if not list then return end
62- local items = (pandoc .utils .type (list ) == ' List' ) and list or { list }
63- for _ , item in ipairs (items ) do
62+ for _ , item in ipairs (list ) do
6463 local t = pandoc .utils .type (item )
6564 if t == ' Meta' or t == ' table' then
6665 -- remap entry: {note.title: notetitle}
@@ -152,33 +151,84 @@ local function wrap_element(el, environment, is_inline)
152151 end
153152end
154153
155- --- Walks children looking for trigger.class whitelist matches.
156- --- trigger : remaining path — current level name, used for child lookup and environment
154+ --- Pass 1: mark matching children with _cw_env and _cw_acc attributes.
155+ --- Does not substitute — lets the walk descend fully into nested levels
156+ --- before any replacement occurs. This avoids a Pandoc 3.9 behaviour where
157+ --- returning a replacement from a walk handler stops further descent.
158+ local function mark_children (el , accumulated )
159+ return el :walk {
160+ Div = function (child )
161+ local class = child .classes :find_if (function (c )
162+ return Whitelist [accumulated .. ' .' .. c ]
163+ end )
164+ if class then
165+ local entry = Whitelist [accumulated .. ' .' .. class ]
166+ local env = (entry == true ) and class or entry
167+ local acc = accumulated .. ' .' .. class
168+ child = mark_children (child , acc )
169+ child .attributes [' _cw_env' ] = env
170+ child .attributes [' _cw_acc' ] = acc
171+ return child
172+ end
173+ end ,
174+ Span = function (child )
175+ local class = child .classes :find_if (function (c )
176+ return Whitelist [accumulated .. ' .' .. c ]
177+ end )
178+ if class then
179+ local entry = Whitelist [accumulated .. ' .' .. class ]
180+ local env = (entry == true ) and class or entry
181+ local acc = accumulated .. ' .' .. class
182+ child = mark_children (child , acc )
183+ child .attributes [' _cw_env' ] = env
184+ child .attributes [' _cw_acc' ] = acc
185+ return child
186+ end
187+ end ,
188+ }
189+ end
190+
191+ --- Pass 2: substitute marked elements with their format-native wrapping.
192+ --- Runs bottom-up so inner elements are wrapped before outer ones.
193+ local function wrap_marked (el )
194+ return el :walk {
195+ Div = function (child )
196+ local env = child .attributes [' _cw_env' ]
197+ local acc = child .attributes [' _cw_acc' ]
198+ if env then
199+ child .attributes [' _cw_env' ] = nil
200+ child .attributes [' _cw_acc' ] = nil
201+ if in_whitelist (acc ) then
202+ return wrap_element (child , env , false )
203+ end
204+ end
205+ end ,
206+ Span = function (child )
207+ local env = child .attributes [' _cw_env' ]
208+ local acc = child .attributes [' _cw_acc' ]
209+ if env then
210+ child .attributes [' _cw_env' ] = nil
211+ child .attributes [' _cw_acc' ] = nil
212+ if in_whitelist (acc ) then
213+ return wrap_element (child , env , true )
214+ end
215+ end
216+ end ,
217+ }
218+ end
219+
220+ --- Processes children in two passes to support chains (note.title.icon) across
221+ --- all Pandoc versions. A single-pass approach (mark+substitute in one walk)
222+ --- breaks in Pandoc 3.9+: returning a replacement from a walk handler stops
223+ --- further descent into nested levels, so deeper chain entries are never seen.
224+ --- Pass 1 (mark_children) annotates matching elements without substituting,
225+ --- letting the walk descend fully. Pass 2 (wrap_marked) substitutes bottom-up.
226+ --- trigger : current level name, used as environment
157227--- accumulated : full dot-chain so far, used for whitelist wrap check
158228local function walk_element (el , is_inline , trigger , accumulated )
159229 if FORMAT ~= ' html' and FORMAT ~= ' epub' then
160- el = el :walk {
161- Div = function (child )
162- local class = child .classes :find_if (function (c )
163- return Whitelist [trigger .. ' .' .. c ]
164- end )
165- if class then
166- local entry = Whitelist [trigger .. ' .' .. class ]
167- local env = (entry == true ) and class or entry
168- return walk_element (child , false , env , accumulated .. ' .' .. class )
169- end
170- end ,
171- Span = function (child )
172- local class = child .classes :find_if (function (c )
173- return Whitelist [trigger .. ' .' .. c ]
174- end )
175- if class then
176- local entry = Whitelist [trigger .. ' .' .. class ]
177- local env = (entry == true ) and class or entry
178- return walk_element (child , true , env , accumulated .. ' .' .. class )
179- end
180- end ,
181- }
230+ el = mark_children (el , accumulated )
231+ el = wrap_marked (el )
182232 end
183233 if in_whitelist (accumulated ) then
184234 return wrap_element (el , trigger , is_inline )
0 commit comments