# -*- coding: utf-8 -*- """D1 §10 Python emoji gate — Windows grep silently fails to match multibyte emoji (the D0 grep-gate zero was a false negative). This scans templates by Unicode codepoint. Run from controller/: python scripts/emoji_gate.py Exit 1 if any emoji/pictograph remains in web+setup templates. """ import io, os, sys, unicodedata ROOTS = [ os.path.join("internal", "web", "templates"), os.path.join("internal", "setup", "templates"), ] # Codepoint ranges that count as "emoji / pictographs / dingbats" for UI-copy purposes. # Deliberately does NOT flag Hungarian letters, typographic quotes/dashes, the middle dot (·), # arrows used as affordances (→ ↗ ↻ ↑ ↓), the multiplication sign (×), or box-drawing. def is_emoji(ch): o = ord(ch) ranges = [ (0x1F300, 0x1FAFF), # Misc symbols & pictographs, emoticons, transport, supplemental, symbols-ext (0x2600, 0x26FF), # Misc symbols (☀ ⚙ ⚠ ☁ …) (0x2700, 0x27BF), # Dingbats (✅ ✂ ✈ ✏ ✓? no — see allow) (0x1F000, 0x1F0FF), # Mahjong/dominoes/cards (0xFE00, 0xFE0F), # Variation selectors (emoji presentation) (0x1F1E6, 0x1F1FF), # Regional indicators ] if any(a <= o <= b for a, b in ranges): return True return False # Dingbat codepoints that are legitimate UI glyphs (checkmarks/crosses used as plain text marks, # not emoji). We keep these OUT of the ban — they render as monochrome text, not color emoji. ALLOW = set("✓✗✔✘•●○■▶") # ✓ ✗ ✔ ✘ • ● ○ ■ ▶ def scan(path): hits = [] for lineno, line in enumerate(io.open(path, encoding="utf-8"), 1): for ch in line: if ch in ALLOW: continue if is_emoji(ch): try: name = unicodedata.name(ch) except ValueError: name = "U+%04X" % ord(ch) hits.append((lineno, ch, name)) return hits def main(): total = 0 for root in ROOTS: for fn in sorted(os.listdir(root)): if not fn.endswith(".html"): continue path = os.path.join(root, fn) for lineno, ch, name in scan(path): total += 1 print("%s:%d %s %s" % (fn, lineno, ch, name)) if total: print("EMOJI GATE FAILED: %d emoji found" % total) sys.exit(1) print("emoji gate OK — no emoji in web/setup templates") if __name__ == "__main__": main()