@@ -255,9 +255,10 @@ jobs:
255255 return f'<blockquote class="wp-block-quote">\n{content}\n</blockquote>'
256256 return HINT_BLOCK.sub(_repl, s)
257257
258- # ---------- enhance existing blockquotes with marker -> class (KEEP marker <p>) ----------
259- # Detect first <p> as a marker (**Note**, **Tip**, **Important**, etc., colon optional)
260- # and add a modifier class on the <blockquote>. Do NOT remove that first paragraph.
258+ # ---------- NEW: enhance existing blockquotes with marker -> class ----------
259+ # Detect first <p> as a marker (**Note**, **Tip**, **Important**, with optional colon)
260+ # and convert <blockquote class="wp-block-quote"> -> add 'is-note' / 'is-tip' / 'is-important'
261+ # Also removes the marker paragraph from inside the blockquote.
261262 MARKERS = {
262263 'note': 'is-note',
263264 'tip': 'is-tip',
@@ -267,40 +268,57 @@ jobs:
267268 'info': 'is-info',
268269 }
269270
271+ # Matches a blockquote, capturing entire inner HTML
270272 BQ = re.compile(
271273 r'<blockquote\s+class="([^"]*\bwp-block-quote\b[^"]*)">\s*(.*?)\s*</blockquote>',
272274 re.I | re.S
273275 )
274- FIRST_P = re.compile(r'^\s*<p>(.*?)</p>', re.I | re.S)
276+ # First paragraph in the block
277+ FIRST_P = re.compile(r'^\s*<p>(.*?)</p>\s*(.*)$', re.I | re.S)
275278
276279 def strip_md_bold(s: str) -> str:
280+ # remove **strong** or __strong__
281+ s = re.sub(r'^\s*[\*_\s]*', '', s)
282+ s = re.sub(r'[\*_\s]*\s*$', '', s)
277283 s = re.sub(r'^\s*(\*\*|__)\s*', '', s)
278284 s = re.sub(r'\s*(\*\*|__)\s*$', '', s)
279285 return s.strip()
280286
281- def detect_marker(text_html : str) -> str:
282- t = re.sub(r'<[^>]+>', '', text_html)
283- t = strip_md_bold(t )
287+ def detect_marker(text : str) -> str:
288+ # Normalize: remove **, trailing colon, casefold
289+ t = strip_md_bold(text )
284290 t = re.sub(r':\s*$', '', t).strip().lower()
285291 return t
286292
287- def enhance_blockquotes_keep_marker (s: str) -> str:
293+ def enhance_blockquotes (s: str) -> str:
288294 def _repl(m):
289295 classes = m.group(1)
290296 inner = m.group(2)
291- fm = FIRST_P.search(inner)
297+
298+ fm = FIRST_P.match(inner)
292299 if not fm:
293300 return m.group(0)
301+
294302 first_p_html = fm.group(1)
295- key = detect_marker(first_p_html)
296- mod = MARKERS.get(key)
297- if not mod:
303+ rest_html = fm.group(2)
304+
305+ # Pull plain-ish text out of the first <p> (not perfect, but good enough)
306+ first_text = re.sub(r'<[^>]+>', '', first_p_html).strip()
307+ key = detect_marker(first_text)
308+
309+ if key in MARKERS:
310+ mod = MARKERS[key]
311+ # avoid duplicates
312+ class_list = classes.split()
313+ if mod not in class_list:
314+ classes = classes + " " + mod
315+ # drop the first <p> marker
316+ new_inner = rest_html.strip()
317+ if not new_inner:
318+ new_inner = "" # allow empty
319+ return f'<blockquote class="{classes}">\n{new_inner}\n</blockquote>'
320+ else:
298321 return m.group(0)
299- cls = classes.split()
300- if mod not in cls:
301- classes = classes + " " + mod
302- # return blockquote with SAME inner (keep marker paragraph)
303- return f'<blockquote class="{classes}">\n{inner}\n</blockquote>'
304322 return BQ.sub(_repl, s)
305323
306324 # ---------- image normalizations (your existing logic) ----------
@@ -408,8 +426,8 @@ jobs:
408426 # 7) Remove lingering ../ or ./ before _images
409427 s = re.sub(r'\]\(\s*<?(?:\.\./|\.?/)+_images/', '](/_images/', s)
410428
411- # 8) Enhance blockquotes (add modifier class; KEEP marker <p>)
412- s = enhance_blockquotes_keep_marker (s)
429+ # 8) NEW: enhance blockquotes (add classes + remove marker <p>)
430+ s = enhance_blockquotes (s)
413431
414432 if s != o:
415433 with open(path, "w", encoding="utf-8", newline="") as f:
@@ -420,7 +438,6 @@ jobs:
420438 if not any_changed:
421439 print("No image links or blockquotes needed normalization.")
422440 PY
423-
424441
425442
426443 # FINAL PATCH: strip quoted fragments inside filename parentheses before .ext
0 commit comments