#!/usr/bin/env python3 """Translate the notion-writer markdown dialect into Notion enhanced markdown for the markdown write engine. Pure functions, no I/O.""" from __future__ import annotations import re from typing import List # NOTE: `notion_writer` is imported lazily inside _translate_mentions to avoid # a circular import (notion_writer imports this module at its top level). # Skill alert type -> (emoji, Notion enhanced-markdown background color) CALLOUT_MAP = { "NOTE": ("â„šī¸", "blue_bg"), "TIP": ("💡", "green_bg"), "IMPORTANT": ("â˜ī¸", "purple_bg"), "WARNING": ("âš ī¸", "yellow_bg"), "CAUTION": ("🚨", "red_bg"), } _ALERT_RE = re.compile(r'^\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$') _MENTION_RE = re.compile(r'@\[([^\]]+)\]\(([^)\s]+)\)') def _translate_mentions(text: str) -> str: from notion_writer import extract_notion_id, format_id_with_dashes # lazy: breaks import cycle def repl(m: "re.Match") -> str: title, target = m.group(1), m.group(2) page_id = extract_notion_id(target) if page_id: return (f'' f'{title}') return f"@{title}" return _MENTION_RE.sub(repl, text) def _indent(lines: List[str]) -> List[str]: return ["\t" + ln if ln.strip() else ln for ln in lines] def translate(content: str) -> str: lines = content.split("\n") out: List[str] = [] i = 0 while i < len(lines): line = lines[i] # Callout: > [!TYPE] then contiguous > body lines if line.lstrip().startswith(">"): body_first = line.lstrip()[1:].strip() alert = _ALERT_RE.match(body_first) if alert: emoji, color = CALLOUT_MAP[alert.group(1)] i += 1 body: List[str] = [] while i < len(lines) and lines[i].lstrip().startswith(">"): body.append(lines[i].lstrip()[1:].lstrip()) i += 1 out.append(f'') out.extend(_indent([_translate_mentions(b) for b in body])) out.append("") continue # Columns: ::: columns / ::: column / ::: # State machine: "::: column" opens a column; ":::" closes a column # when one is open, otherwise closes the wrapper. The Pandoc layout # emits N "::: column" opens, N ":::" column-closes, then one final # ":::" wrapper-close. if line.strip() == "::: columns": i += 1 cols: List[List[str]] = [] cur: List[str] = None while i < len(lines): s = lines[i].strip() if s == "::: column": if cur is not None: cols.append(cur) cur = [] i += 1 elif s == ":::": if cur is not None: # close the open column cols.append(cur) cur = None i += 1 else: # close the wrapper i += 1 break else: if cur is not None: cur.append(lines[i]) i += 1 out.append("") for col in cols: out.append("\t") out.extend(_indent(_indent( [_translate_mentions(c) for c in col]))) out.append("\t") out.append("") continue # Toggle:
.. body
if line.strip() == "
": out.append("
") i += 1 if i < len(lines) and lines[i].lstrip().startswith(""): out.append(lines[i].strip()) i += 1 body = [] while i < len(lines) and lines[i].strip() != "
": body.append(_translate_mentions(lines[i])) i += 1 out.extend(_indent(body)) out.append("
") if i < len(lines): # skip closing i += 1 continue out.append(_translate_mentions(line)) i += 1 return "\n".join(out)