UNPKG

pangu

Version:

Opinionated paranoid text spacing: automatically inserts whitespace between CJK (Chinese, Japanese, Korean) and ANS (alphabetical letters, numerical digits and symbols)

1 lines 44.3 kB
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/shared/index.ts","../../src/node/index.ts","../../src/node/index.cts"],"sourcesContent":["// CJK is short for Chinese, Japanese, and Korean\n//\n// ANS is short for Alphabets, Numbers, and Symbols:\n// A is A-Za-z. Only the ANS_* extended sets below (feeding CJK_ANS and ANS_CJK) additionally cover Greek and Coptic, every other A/AN-named rule is bare ASCII\n// N includes 0-9\n// S varies per rule, see the symbol sets below\n//\n// For more about Unicode blocks, see https://symbl.cc/en/unicode-table/\n\n// Unicode blocks. A name that ends in a carve-out marks a range that is deliberately smaller than its whole block, so widening it back to the block boundary is a wrong edit, not a tidy-up\nexport const CJK_RADICALS_SUPPLEMENT = '\\u2e80-\\u2eff';\nexport const KANGXI_RADICALS = '\\u2f00-\\u2fdf';\nexport const HIRAGANA = '\\u3040-\\u309f';\nexport const KATAKANA_NO_MIDDLE_DOT = '\\u30a0-\\u30fa\\u30fc-\\u30ff'; // The Katakana block ends at \\u30ff, but \\u30fb is the character that MIDDLE_DOT normalizes to, so it must not read as CJK itself\nexport const BOPOMOFO = '\\u3100-\\u312f';\nexport const ENCLOSED_CJK_LETTERS_AND_MONTHS = '\\u3200-\\u32ff';\nexport const CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A = '\\u3400-\\u4dbf';\nexport const CJK_UNIFIED_IDEOGRAPHS = '\\u4e00-\\u9fff';\nexport const CJK_COMPATIBILITY_IDEOGRAPHS = '\\uf900-\\ufaff';\nexport const GREEK_AND_COPTIC = '\\u0370-\\u03ff';\nexport const LATIN_1_SUPPLEMENT_AFTER_NBSP = '\\u00a1-\\u00ff'; // The Latin-1 Supplement block starts at \\u0080, but this range starts one past NBSP (\\u00a0) so an NBSP lands in no character class at all. See ADR 0009\nexport const NUMBER_FORMS = '\\u2150-\\u218f';\nexport const LETTERLIKE_SYMBOLS = '\\u2100-\\u214f';\nexport const DINGBATS = '\\u2700-\\u27bf';\n\n// Superscript suffixes stay attached on the left. Exclude ⁽ so the following space never lands inside an opening parenthesis\n// \\u2120 and \\u2122 (SM, TM) belong here too: their NFKD decomposition is tagged <super>. \\u00ae (R) has no decomposition but renders raised and attaches to the mark before it\n// Characters: ® ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁱ ⁿ ⁺ ⁻ ⁼ ⁾ ℠ ™\nexport const SUPERSCRIPT_SUFFIXES = '\\u00ae\\u00b2\\u00b3\\u00b9\\u2070\\u2071\\u2074-\\u207c\\u207e\\u207f\\u2120\\u2122';\n\nexport const CJK = `${CJK_RADICALS_SUPPLEMENT}${KANGXI_RADICALS}${HIRAGANA}${KATAKANA_NO_MIDDLE_DOT}${BOPOMOFO}${ENCLOSED_CJK_LETTERS_AND_MONTHS}${CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A}${CJK_UNIFIED_IDEOGRAPHS}${CJK_COMPATIBILITY_IDEOGRAPHS}`;\n\n// Basic character classes\nexport const AN = 'A-Za-z0-9';\nexport const A = 'A-Za-z';\nexport const UPPER_AN = 'A-Z0-9'; // For FIX_CJK_COLON_ANS\n\n// Operators. No + because every plus in CJK contact is decided by an affix or by plus reading, which both run before the operator rules\nexport const OPERATORS = '\\\\*=&\\\\-';\nexport const GRADE_OPERATORS = '\\\\+\\\\-\\\\*'; // For single letter grades\n\n// Characters: ` \" ״\nexport const QUOTES = '\\`\"\\u05f4'; // Backtick, straight quote, Hebrew punctuation\n\n// Brackets. Each rule uses a different set\nexport const LEFT_BRACKETS_BASIC = '\\\\(\\\\[\\\\{'; // For AN_LEFT_BRACKET\nexport const RIGHT_BRACKETS_BASIC = '\\\\)\\\\]\\\\}'; // For RIGHT_BRACKET_AN and ANS_OPERATOR_CJK\n// Characters: ( [ { < > “\nexport const LEFT_BRACKETS_EXTENDED = '\\\\(\\\\[\\\\{<>\\u201c'; // For CJK_LEFT_BRACKET (includes angle brackets + curly quote)\n// Characters: ) ] } < > ”\nexport const RIGHT_BRACKETS_EXTENDED = '\\\\)\\\\]\\\\}<>\\u201d'; // For RIGHT_BRACKET_CJK\n\n// ANS extended sets. The two sets are not identical, see the inline notes\n// Both ranges start at \\u00a1, one past NBSP (\\u00a0), so an NBSP is in no character class at all. That inertness is load-bearing: an NBSP already separates the runs it sits between,\n// so no rule matches across it and none fires. pangu therefore never rewrites an author's NBSP, it only inserts a space where one is genuinely missing. See ADR 0009\nexport const ANS_CJK_AFTER = `${A}${GREEK_AND_COPTIC}0-9@\\\\$%\\\\^&\\\\*\\\\-\\\\+\\\\\\\\=${LATIN_1_SUPPLEMENT_AFTER_NBSP}${NUMBER_FORMS}${DINGBATS}${LETTERLIKE_SYMBOLS}`; // Has @, no punctuation\nexport const ANS_BEFORE_CJK = `${A}${GREEK_AND_COPTIC}0-9\\\\$%\\\\^&\\\\*\\\\-\\\\+\\\\\\\\=${LATIN_1_SUPPLEMENT_AFTER_NBSP}${NUMBER_FORMS}${DINGBATS}${LETTERLIKE_SYMBOLS}${SUPERSCRIPT_SUFFIXES}`; // No @ symbol\n\n// Common directory names in Unix and project paths\n// prettier-ignore\nexport const FILE_PATH_DIRS = 'home|root|usr|etc|var|opt|tmp|dev|mnt|proc|sys|bin|boot|lib|media|run|sbin|srv|node_modules|path|project|src|dist|test|tests|docs|templates|assets|public|static|config|scripts|tools|build|out|target|your|\\\\.claude|\\\\.git|\\\\.vscode';\nexport const FILE_PATH_CHARS = '[A-Za-z0-9_\\\\-\\\\.@\\\\+\\\\*]+';\n\n// Unix absolute paths: system directories and common project paths, for example /home, /usr/bin, /etc/nginx.conf, /.bashrc, /node_modules/@babel/core, /path/to/your/project\nexport const UNIX_ABSOLUTE_FILE_PATH = new RegExp(`/(?:\\\\.?(?:${FILE_PATH_DIRS})|\\\\.(?:[A-Za-z0-9_\\\\-]+))(?:/${FILE_PATH_CHARS})*`);\n\n// Unix relative paths that are common in documentation and blog posts, for example src/main.py, dist/index.js, test/spec.js, ./.claude/CLAUDE.md, templates/*.html\nexport const UNIX_RELATIVE_FILE_PATH = new RegExp(`(?:\\\\./)?(?:${FILE_PATH_DIRS})(?:/${FILE_PATH_CHARS})+`);\n\n// Windows paths: C:\\Users\\name\\, D:\\Program Files\\, C:\\Windows\\System32\nexport const WINDOWS_FILE_PATH = /[A-Z]:\\\\(?:[A-Za-z0-9_\\-\\. ]+\\\\?)+/;\n\nexport const ANY_CJK = new RegExp(`[${CJK}]`);\n\n// A punctuation run after CJK gets a trailing space and never converts to full-width. Space only when CJK, a letter, or a digit follows, so nothing changes at the end of the text\nexport const CJK_PUNCTUATION = new RegExp(`([${CJK}])([!;,\\\\?:]+)(?=[${CJK}${AN}])`, 'g');\n// A punctuation run directly before CJK gets a space after it, whatever sits on its left (no left anchor). An already-typed 'CJK ,CJK' shape is a typo, not preserved. See ADR 0007\n// CJK_PUNCTUATION still owns colon and punctuation before letters and digits\nexport const PUNCTUATION_CJK = new RegExp(`([!;,\\\\?]+)(?=[${CJK}])`, 'g');\n\n// Tilde has its own rule so ~= stays intact. Space only when CJK, a letter, or a digit follows\nexport const CJK_TILDE = new RegExp(`([${CJK}])(~+)(?!=)(?=[${CJK}${AN}])`, 'g');\nexport const CJK_TILDE_EQUALS = new RegExp(`([${CJK}])(~=)`, 'g');\n\n// Period has its own rule so file extensions, dot runs, and file paths stay intact; DOTS_CJK handles runs of dots first. Space only when CJK follows: the negative lookahead rejects a letter or digit,\n// which reads as a file extension and stays intact\nexport const CJK_PERIOD = new RegExp(`([${CJK}])(\\\\.)(?![${AN}\\\\./])(?=[${CJK}${AN}])`, 'g');\nexport const AN_PERIOD_CJK = new RegExp(`([${AN}])(\\\\.)([${CJK}])`, 'g');\n\nexport const AN_COLON_CJK = new RegExp(`([${AN}])(:)([${CJK}])`, 'g');\n// The only case where a colon converts to full-width: after CJK, directly before a parenthesis. The A-Z0-9 half of the class is unreachable, because CJK_PUNCTUATION runs first and owns colon before\n// letters and digits, leaving a half-width colon plus a space\nexport const FIX_CJK_COLON_ANS = new RegExp(`([${CJK}])\\\\:([${UPPER_AN}\\\\(\\\\)])`, 'g');\n\n// Characters: . …\nexport const DOTS_CJK = new RegExp(`([\\\\.]{2,}|\\u2026)([${CJK}])`, 'g');\n\n// The quote class deliberately excludes ' because single quotes have their own rules\nexport const CJK_QUOTE = new RegExp(`([${CJK}])([${QUOTES}])`, 'g');\nexport const QUOTE_CJK = new RegExp(`([${QUOTES}])([${CJK}])`, 'g');\n// The content class is [\\s\\S] rather than . so a quoted segment that spans a line break still pairs with its own closing quote. HTML source wrapping puts newlines mid-sentence, and with . that\n// closing quote is unreachable, so the scan resyncs on the next quote, pairs closing-to-opening and strips the spaces outside the quotes instead of inside\nexport const FIX_QUOTE_ANY_QUOTE = new RegExp(`([${QUOTES}]+)[ ]*([\\\\s\\\\S]+?)[ ]*([${QUOTES}]+)`, 'g');\n// Curly quotes only: CJK_QUOTE, QUOTE_CJK, and FIX_QUOTE_ANY_QUOTE already handle straight quotes\n// Quote: ”\nexport const QUOTE_AN = new RegExp(`([\\u201d])([${AN}])`, 'g');\n\n// A straight quote between CJK and AN (CJK\"AN) reads as closing a quoted CJK phrase, so the space goes after the quote\nexport const CJK_QUOTE_AN = new RegExp(`([${CJK}])(\")([${AN}])`, 'g');\n\nexport const CJK_SINGLE_QUOTE_BUT_POSSESSIVE = new RegExp(`([${CJK}])('[^s])`, 'g');\nexport const SINGLE_QUOTE_CJK = new RegExp(`(')([${CJK}])`, 'g');\nexport const FIX_POSSESSIVE_SINGLE_QUOTE = new RegExp(`([${AN}${CJK}])( )('s)`, 'g');\n// Single quotes whose content is only CJK characters\nexport const SINGLE_QUOTE_PURE_CJK = new RegExp(`(')([${CJK}]+)(')`, 'g');\n\n// Legacy name: the run between the two hashes is CJK only, the ANS in the name has never matched\nexport const HASH_ANS_CJK_HASH = new RegExp(`([${CJK}])(#)([${CJK}]+)(#)([${CJK}])`, 'g');\n// The negated class is the \"something is glued to this #, so it is a hashtag\" guard, so it has to reject an NBSP the same way it rejects a space. It stays a literal pair rather than \\S because \\S\n// also excludes zero-width characters like U+FEFF, and treating those as a gap would drop the space entirely and leave the runs flush\n// Non-breaking space: [ ] (U+00A0)\nexport const CJK_HASH = new RegExp(`([${CJK}])(#([^ \\\\u00a0]))`, 'g');\n// Non-breaking space: [ ] (U+00A0). A hashtag right after a slash in a list (/#tag) is a hashtag, not a C# shape\nexport const HASH_CJK = new RegExp(`(([^ \\\\u00a0/])#)([${CJK}])`, 'g');\n\nconst PRODUCT_NAME = 'Apple TV|CATCHPLAY|[Dd]iscovery|Disney|ESPN|Fitness|iCloud|mo ?店|Paramount|PS';\nconst PRODUCT_NAME_IN_CJK = '公視|影劇館';\nconst PRODUCT_TIER = 'Pro';\nconst CREDIT_RATING = '(?:tw)?(?:AA|BBB|BB|CCC)|tw[AB]';\nconst MULTI_LETTER_BLOOD_TYPE = 'AB|RhD|Rh';\n\n// Product names and tiers take + only; credit ratings and blood types take + or -\nconst NAME_SUFFIX = `(?:(?<![A-Za-z0-9])(?:(?:${PRODUCT_NAME}|${PRODUCT_TIER})\\\\+|(?:${CREDIT_RATING}|${MULTI_LETTER_BLOOD_TYPE})[+-])|(?:${PRODUCT_NAME_IN_CJK})\\\\+)`;\nexport const NAME_SUFFIX_AT_END = new RegExp(`${NAME_SUFFIX}$`);\n\n// A closing mark follows the suffix tight; a word or an opening bracket keeps its boundary space\nconst CLOSING_AFTER_SUFFIX = /[/)\\]}\\uff09\\u3011\\u3015\\u3009\\u300b\\u300d\\u300f\\uff0c\\u3002\\u3001\\uff1b\\uff1a\\uff01\\uff1f]/;\n\n// The operator set is - * = & only (no + | / < >). Only direct CJK contact makes a symbol an operator: a symbol between two half-width characters binds them into a joiner token (A-B, a=1, S&P)\n// and never gets spaces, so there is deliberately no between-half-width rule here\n// On the left, a closing bracket also counts as the half-width side: ]-CJK reads as an operator whose operand is the bracketed run\n// Listed name suffixes keep their signs attached\nexport const CJK_OPERATOR_ANS = new RegExp(`([${CJK}])([${OPERATORS}])([${AN}])`, 'g');\nexport const ANS_OPERATOR_CJK = new RegExp(`([${AN}${RIGHT_BRACKETS_BASIC}])([${OPERATORS}])(?<!${NAME_SUFFIX})([${CJK}])`, 'g');\n\n// Pipe patterns for separator vs joiner-token behavior, decided per line\nexport const PIPE_CJK_CONTACT = new RegExp(`[${CJK}]\\\\||\\\\|[${CJK}]`);\nexport const PIPE_SEPARATOR = /([^\\s|])[ ]*(\\|+)[ ]*(?=[^\\s|])/g;\n\n// Plus patterns for separator vs joiner-token behavior, decided per line like the pipe. The separator matches a solitary plus only: a space-adjacent plus is decided and a ++ run is a preserved\n// pattern (C++, i++). Common Chinese full-width punctuation also keeps an adjacent plus tight, even when another plus flips the line, since a word before an opening one may be a name (A+\\uff08CJK+\\uff09)\nexport const PLUS_CJK_CONTACT = new RegExp(`[${CJK}]\\\\+|\\\\+[${CJK}]`);\n// Characters: ,。;:!?、()「」『』【】《》\n// prettier-ignore\nexport const PLUS_SEPARATOR = /(?<=[^\\s+\\uff0c\\u3002\\uff1b\\uff1a\\uff01\\uff1f\\u3001\\uff08\\uff09\\u300c\\u300d\\u300e\\u300f\\u3010\\u3011\\u300a\\u300b])\\+(?=[^\\s+\\uff0c\\u3002\\uff1b\\uff1a\\uff01\\uff1f\\u3001\\uff08\\uff09\\u300c\\u300d\\u300e\\u300f\\u3010\\u3011\\u300a\\u300b])/g;\n// A closing bracket cannot carry a name suffix. Before a full-width opener, put the separator space on the closing-bracket side only\n// Characters: (「『【《\nexport const RIGHT_BRACKET_PLUS_FULL_WIDTH_LEFT_BRACKET = new RegExp(`(?<=[${RIGHT_BRACKETS_BASIC}])\\\\+(?=[\\\\uff08\\\\u300c\\\\u300e\\\\u3010\\\\u300a])`, 'g');\n\n// Single-letter grades (A+, B-, C*) before CJK get the space after the symbol, not before. The \\b keeps the letter single, not the tail of a longer word\nexport const SINGLE_LETTER_GRADE_CJK = new RegExp(`\\\\b([${A}])([${GRADE_OPERATORS}])([${CJK}])`, 'g');\n\n// Affix readings attach a symbol to its half-width side at a CJK boundary, overriding the operator reading\n// Sign: + attaches to following digits (+886). A hyphen before digits is not a sign: CJK-N falls to CJK_OPERATOR_ANS, because year ranges and site-title separators outnumber glued negative\n// numbers. See ADR 0015\nexport const CJK_SIGN_DIGIT = new RegExp(`([${CJK}])(\\\\+)([0-9])`, 'g');\n// Flag: - attaches to a following single lowercase letter (-m). [a-z] keeps a capitalized word on the operator reading, and the trailing \\b keeps a longer lowercase word there too\nexport const CJK_HYPHEN_FLAG = new RegExp(`([${CJK}])(\\\\-)([a-z])\\\\b`, 'g');\n// Suffix: + attaches to a preceding whole digit run (18+, 100+, 3.5+). The \\b keeps a digit that ends a word (S24+, HDR10+) on the separator reading, see plus reading. Word suffixes are decided\n// by the name list during plus reading. See ADR 0024\nexport const DIGIT_PLUS_CJK = new RegExp(`\\\\b([0-9]+)(\\\\+)([${CJK}])`, 'g');\n\n// < and > as comparison operators, not brackets\nexport const CJK_LESS_THAN = new RegExp(`([${CJK}])(<)([${AN}])`, 'g');\nexport const LESS_THAN_CJK = new RegExp(`([${AN}])(<)([${CJK}])`, 'g');\nexport const CJK_GREATER_THAN = new RegExp(`([${CJK}])(>)([${AN}])`, 'g');\nexport const GREATER_THAN_CJK = new RegExp(`([${AN}])(>)([${CJK}])`, 'g');\n\n// Bracket patterns: ( ) [ ] { } plus < >, which also act as comparison operators\n// The curly quotes \\u201c and \\u201d appear in CJK_LEFT_BRACKET/RIGHT_BRACKET_CJK, but the paired-quote patterns handle them primarily\n// Legacy names: the two ..._BRACKET_... rules below hold only \\u201c and \\u201d in their \"bracket\" classes and have never matched a real bracket. Real brackets belong to CJK_LEFT_BRACKET,\n// RIGHT_BRACKET_CJK, AN_LEFT_BRACKET, and RIGHT_BRACKET_AN\nexport const CJK_LEFT_BRACKET = new RegExp(`([${CJK}])([${LEFT_BRACKETS_EXTENDED}])`, 'g');\nexport const RIGHT_BRACKET_CJK = new RegExp(`([${RIGHT_BRACKETS_EXTENDED}])([${CJK}])`, 'g');\n// Quotes: “ ”\nexport const ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET = new RegExp(`([${AN}${CJK}])[ ]*([\\u201c])([${AN}${CJK}\\\\-_ ]+)([\\u201d])`, 'g');\n// Quotes: “ ”\nexport const LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK = new RegExp(`([\\u201c])([${AN}${CJK}\\\\-_ ]+)([\\u201d])[ ]*([${AN}${CJK}])`, 'g');\n// Some input habits type both quotes of a pair as closing curly quotes (\\u201d): the shape CJK\\u201dCJK\\u201d appears where CJK\\u201cCJK\\u201d was meant\n// A \\u201d only opens a \\u201d...\\u201d pair when no unclosed \\u201c precedes it on the line (the lookbehind), otherwise it closes that \\u201c\n// Runs after RIGHT_BRACKET_CJK, so the [ ]* after the opener strips the space that rule just added inside the pair\n// Not portable as a plain regex: the lookbehind is variable-length, which Python `re` and Go `regexp` reject. A port to those engines has to track the unclosed \\u201c in code instead\n// Quotes: “ ”\nexport const ANS_CJK_RIGHT_QUOTE_ANY_RIGHT_QUOTE = new RegExp(`([${AN}${CJK}])[ ]*(?<![\\u201c][^\\u201c\\u201d\\n]*)([\\u201d])[ ]*([${AN}${CJK}\\\\-_ ]+?)[ ]*([\\u201d])`, 'g');\n\n// A dotted name keeps its call parenthesis tight (`Math.floor(x)`, `array.map(fn)`), a bare name does not (`foo (x)`)\n// Not portable as a plain regex: the lookbehind is variable-length, which Python `re` and Go `regexp` reject. A port to those engines has to detect the preceding dot in code instead\nexport const AN_LEFT_BRACKET = new RegExp(`([${AN}])(?<!\\\\.[${AN}]*)([${LEFT_BRACKETS_BASIC}])`, 'g');\nexport const RIGHT_BRACKET_AN = new RegExp(`([${RIGHT_BRACKETS_BASIC}])([${AN}])`, 'g');\n\nexport const CJK_UNIX_ABSOLUTE_FILE_PATH = new RegExp(`([${CJK}])(${UNIX_ABSOLUTE_FILE_PATH.source})`, 'g');\nexport const CJK_UNIX_RELATIVE_FILE_PATH = new RegExp(`([${CJK}])(${UNIX_RELATIVE_FILE_PATH.source})`, 'g');\nexport const CJK_WINDOWS_PATH = new RegExp(`([${CJK}])(${WINDOWS_FILE_PATH.source})`, 'g');\n\nexport const UNIX_ABSOLUTE_FILE_PATH_SLASH_CJK = new RegExp(`(${UNIX_ABSOLUTE_FILE_PATH.source}/)([${CJK}])`, 'g');\nexport const UNIX_RELATIVE_FILE_PATH_SLASH_CJK = new RegExp(`(${UNIX_RELATIVE_FILE_PATH.source}/)([${CJK}])`, 'g');\n\nexport const CJK_ANS = new RegExp(`([${CJK}])(?![${SUPERSCRIPT_SUFFIXES}])([${ANS_CJK_AFTER}])(?<!${NAME_SUFFIX})`, 'g');\nexport const ANS_CJK = new RegExp(`([${ANS_BEFORE_CJK}])([${CJK}])`, 'g');\n\nexport const S_A = new RegExp(`(%)([${A}])`, 'g');\n\n// \\u00a9 is a sign before a year, not a prefix on it: `\\u00a9` + digits reads with a space after the sign\nexport const COPYRIGHT_DIGIT = /(\\u00a9)([0-9])/g;\n\n// Characters: · • ‧\nexport const MIDDLE_DOT = /([ ]*)([\\u00b7\\u2022\\u2027])([ ]*)/g;\n\n// A bare unpaired non-void tag amid prose is a tag mention, not markup: it reads as one unit and is spaced from CJK it directly touches\n// A trailing self-closing slash is still bare, but void elements render on their own (<br> or <hr>), so they stay markup even unpaired\nexport const VOID_HTML_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);\nexport const BARE_HTML_TAG = /^<([a-zA-Z][a-zA-Z0-9]*)\\s*\\/?>$/;\nexport const CLOSING_HTML_TAG = /<\\/([a-zA-Z][a-zA-Z0-9]*)/g;\n\nexport const CJK_HTML_TAG_MENTION = new RegExp(`([${CJK}])(?=\\uE004)`, 'g');\nexport const HTML_TAG_MENTION_CJK = new RegExp(`(?<=\\uE005)([${CJK}])`, 'g');\n\n// A URL reads as one unit: nothing inside it is modified, and it is spaced from CJK on its left. Scheme-anchored only, and CJK characters continue the URL (/wiki/\\u4e2d\\u6587), so CJK\n// prose written tight after a URL stays tight. The body ends at the Private Use Area too, so a URL never swallows a placeholder. See ADR 0026\nexport const HTTP_URL = /(?<![A-Za-z0-9])https?:\\/\\/[^\\s<>\"`\\u3000-\\u303f\\uff00-\\uffef\\u2018\\u2019\\u201c\\u201d\\u2026\\ue000-\\uf8ff]+/g;\n// Trailing half-width punctuation and an unbalanced closing parenthesis belong to the prose, not the URL\nconst HTTP_URL_TRAILING_PUNCTUATION = /[.,;:!?'\"]+$/;\nexport const CJK_HTTP_URL = new RegExp(`([${CJK}])(?=\\uE00A)`, 'g');\n\nfunction trimHttpUrl(url: string) {\n // Count once: a content script sees attacker text, and recounting per pass is quadratic on a long run of closing parentheses\n let unbalancedClosingParentheses = (url.match(/\\)/g) ?? []).length - (url.match(/\\(/g) ?? []).length;\n for (;;) {\n const trimmed = url.replace(HTTP_URL_TRAILING_PUNCTUATION, '');\n if (trimmed.endsWith(')') && unbalancedClosingParentheses > 0) {\n unbalancedClosingParentheses--;\n url = trimmed.slice(0, -1);\n continue;\n }\n if (trimmed === url) {\n return url;\n }\n url = trimmed;\n }\n}\n\n// Used by fixBracketSpacing to strip the spaces just inside a bracket pair; everything else between the brackets stays unchanged\nexport const BRACKET_PATTERNS = [\n { pattern: /<([^<>]*)>/g, open: '<', close: '>' },\n { pattern: /\\(([^()]*)\\)/g, open: '(', close: ')' },\n { pattern: /\\[([^\\[\\]]*)\\]/g, open: '[', close: ']' },\n { pattern: /\\{([^{}]*)\\}/g, open: '{', close: '}' },\n];\n\n// We use characters from Unicode's Private Use Area (U+E000-U+F8FF) as delimiters to make placeholders unlikely to collide with ordinary text\nexport class PlaceholderReplacer {\n private static patternCache = new Map<string, RegExp>();\n\n private items: string[] = [];\n private index = 0;\n private pattern: RegExp;\n\n constructor(\n private placeholder: string,\n private startDelimiter: string,\n private endDelimiter: string,\n ) {\n const cacheKey = `${startDelimiter}${placeholder}${endDelimiter}`;\n let pattern = PlaceholderReplacer.patternCache.get(cacheKey);\n if (!pattern) {\n const escapedStart = this.startDelimiter.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const escapedEnd = this.endDelimiter.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n pattern = new RegExp(`${escapedStart}${this.placeholder}(\\\\d+)${escapedEnd}`, 'g');\n PlaceholderReplacer.patternCache.set(cacheKey, pattern);\n }\n this.pattern = pattern;\n }\n\n store(item: string) {\n this.items[this.index] = item;\n return `${this.startDelimiter}${this.placeholder}${this.index++}${this.endDelimiter}`;\n }\n\n restore(text: string) {\n if (this.index === 0) {\n return text;\n }\n return text.replace(this.pattern, (_match, index: string) => {\n return this.items[parseInt(index, 10)] || '';\n });\n }\n}\n\nexport class Pangu {\n version: string;\n\n constructor() {\n this.version = '10.2.0';\n }\n\n public spaceText(text: string) {\n if (typeof text !== 'string') {\n console.warn(`[pangu] spaceText(text) only accepts string but got ${typeof text}`);\n return text;\n }\n\n if (text.length <= 1 || !ANY_CJK.test(text)) {\n return text;\n }\n\n let newText = text;\n\n // Hide backtick content from the quote rules; the backticks themselves still get spacing\n const backtickManager = new PlaceholderReplacer('BACKTICK_CONTENT_', '\\uE000', '\\uE001');\n newText = newText.replace(/`([^`]+)`/g, (_match, content: string) => {\n return `\\`${backtickManager.store(content)}\\``;\n });\n\n // Hide every URL from the rules. Attribute values reach spaceText() through the HTML step below, so a URL inside href=\"...\" is hidden the same way\n const urlManager = new PlaceholderReplacer('HTTP_URL_PLACEHOLDER_', '\\uE00A', '\\uE00B');\n newText = newText.replace(HTTP_URL, (match) => {\n const url = trimHttpUrl(match);\n return urlManager.store(url) + match.slice(url.length);\n });\n\n const htmlTagManager = new PlaceholderReplacer('HTML_TAG_PLACEHOLDER_', '\\uE002', '\\uE003');\n const mentionedTagManager = new PlaceholderReplacer('HTML_TAG_MENTION_', '\\uE004', '\\uE005');\n let hasHtmlTags = false;\n\n if (newText.includes('<')) {\n hasHtmlTags = true;\n // Matches only opening, closing, and self-closing tags with a real tag name, so stray < > content is not read as a tag\n const HTML_TAG_PATTERN = /<\\/?[a-zA-Z][a-zA-Z0-9]*(?:\\s+[^>]*)?>/g;\n\n // Tag names whose closing tag appears anywhere in the text: their opening tags are paired markup\n const closedTagNames = new Set<string>();\n for (const closingTag of newText.matchAll(CLOSING_HTML_TAG)) {\n closedTagNames.add(closingTag[1]!.toLowerCase());\n }\n\n // Hide every real tag behind a placeholder; attribute values get spacing first\n newText = newText.replace(HTML_TAG_PATTERN, (match) => {\n const bareTag = match.match(BARE_HTML_TAG);\n if (bareTag) {\n const tagName = bareTag[1]!.toLowerCase();\n if (!VOID_HTML_TAGS.has(tagName) && !closedTagNames.has(tagName)) {\n return mentionedTagManager.store(match);\n }\n }\n const processedTag = match.replace(/(\\w+)=\"([^\"]*)\"/g, (_attrMatch, attrName: string, attrValue: string) => {\n const processedValue = this.spaceText(attrValue);\n return `${attrName}=\"${processedValue}\"`;\n });\n\n return htmlTagManager.store(processedTag);\n });\n }\n\n // Dot runs go first, before the single-period rule\n newText = newText.replace(DOTS_CJK, '$1 $2');\n\n newText = newText.replace(CJK_PUNCTUATION, '$1$2 ');\n newText = newText.replace(PUNCTUATION_CJK, '$1 ');\n newText = newText.replace(CJK_TILDE, '$1$2 ');\n newText = newText.replace(CJK_TILDE_EQUALS, '$1 $2 ');\n newText = newText.replace(CJK_PERIOD, '$1$2 ');\n newText = newText.replace(AN_PERIOD_CJK, '$1$2 $3');\n newText = newText.replace(AN_COLON_CJK, '$1$2 $3');\n newText = newText.replace(FIX_CJK_COLON_ANS, '$1:$2');\n\n newText = newText.replace(CJK_QUOTE, '$1 $2');\n newText = newText.replace(QUOTE_CJK, '$1 $2');\n newText = newText.replace(FIX_QUOTE_ANY_QUOTE, '$1$2$3');\n\n newText = newText.replace(QUOTE_AN, '$1 $2');\n newText = newText.replace(CJK_QUOTE_AN, '$1$2 $3');\n\n newText = newText.replace(FIX_POSSESSIVE_SINGLE_QUOTE, \"$1's\");\n\n // Quoted pure-CJK content keeps its quotes tight, so hide it before the single-quote rules run\n const singleQuoteCJKManager = new PlaceholderReplacer('SINGLE_QUOTE_CJK_PLACEHOLDER_', '\\uE006', '\\uE007');\n\n newText = newText.replace(SINGLE_QUOTE_PURE_CJK, (match) => {\n return singleQuoteCJKManager.store(match);\n });\n\n newText = newText.replace(CJK_SINGLE_QUOTE_BUT_POSSESSIVE, '$1 $2');\n newText = newText.replace(SINGLE_QUOTE_CJK, '$1 $2');\n\n newText = singleQuoteCJKManager.restore(newText);\n\n // HASH_ANS_CJK_HASH pattern needs at least 5 characters\n if (newText.length >= 5) {\n newText = newText.replace(HASH_ANS_CJK_HASH, '$1 $2$3$4 $5');\n }\n newText = newText.replace(CJK_HASH, '$1 $2');\n newText = newText.replace(HASH_CJK, '$1 $3');\n\n // Protect compound words from operator spacing\n const compoundWordManager = new PlaceholderReplacer('COMPOUND_WORD_PLACEHOLDER_', '\\uE008', '\\uE009');\n\n // Hyphen-joined alphanumeric runs that read as one name, for example state-of-the-art, GPT-4o, claude-4-opus. Qualifies when a part carries a lowercase letter, or when the hyphen joins an\n // all-letters part to an all-digits part (GPT-5), or a letters-plus-digits part to anything (GPT4o-mini). An all-uppercase pair like ABC-DEF does not qualify\n const COMPOUND_WORD_PATTERN = /\\b(?:[A-Za-z0-9]*[a-z][A-Za-z0-9]*-[A-Za-z0-9]+|[A-Za-z0-9]+-[A-Za-z0-9]*[a-z][A-Za-z0-9]*|[A-Za-z]+-[0-9]+|[A-Za-z]+[0-9]+-[A-Za-z0-9]+)(?:-[A-Za-z0-9]+)*\\b/g;\n\n newText = newText.replace(COMPOUND_WORD_PATTERN, (match) => {\n return compoundWordManager.store(match);\n });\n\n // Single-letter grades run before the general operator rules so A+CJK becomes A+ CJK, not A + CJK\n newText = newText.replace(SINGLE_LETTER_GRADE_CJK, '$1$2 $3');\n\n // Affix readings run before the operator rules so the symbol stays attached to its half-width side\n newText = newText.replace(CJK_SIGN_DIGIT, '$1 $2$3');\n newText = newText.replace(CJK_HYPHEN_FLAG, '$1 $2$3');\n newText = newText.replace(DIGIT_PLUS_CJK, '$1$2 $3');\n\n // Plus reading is per line: a plus in direct contact with CJK makes every undecided plus on the line a separator with spaces on both sides, as in telecom bundle plans that chain products with +\n // A decided plus keeps its reading: space-adjacent, affix-attached (100+, +886), or in a ++ run (C++). A line with no CJK contact keeps its joiner tokens tight (A+B, 5+5)\n // It runs right after the affixes and before the operator rules, so a CJK+A contact flips the line's joiners like a CJK+CJK contact does (CJK+A+A reads CJK + A + A)\n // Name suffixes are recognized here so their CJK contact still decides the line's other pluses (Disney+CJK A+B)\n newText = newText\n .split('\\n')\n .map((line) => {\n const spaced = PLUS_CJK_CONTACT.test(line)\n ? line.replace(PLUS_SEPARATOR, (_match, offset: number) => {\n // Read through compound placeholders to recognize names such as non-Disney+ and foo-Apple TV+\n // ponytail: mixed compounds and pluses rescan prefixes; use a single name lookup pass if long lines make this slow\n if (!NAME_SUFFIX_AT_END.test(compoundWordManager.restore(line.slice(0, offset + 1)))) {\n return ' + ';\n }\n return CLOSING_AFTER_SUFFIX.test(line[offset + 1] ?? '') ? '+' : '+ ';\n })\n : line;\n return spaced.replace(RIGHT_BRACKET_PLUS_FULL_WIDTH_LEFT_BRACKET, ' +');\n })\n .join('\\n');\n\n newText = newText.replace(CJK_OPERATOR_ANS, '$1 $2 $3');\n newText = newText.replace(ANS_OPERATOR_CJK, '$1 $2 $3');\n\n newText = newText.replace(CJK_LESS_THAN, '$1 $2 $3');\n newText = newText.replace(LESS_THAN_CJK, '$1 $2 $3');\n newText = newText.replace(CJK_GREATER_THAN, '$1 $2 $3');\n newText = newText.replace(GREATER_THAN_CJK, '$1 $2 $3');\n\n newText = newText.replace(CJK_UNIX_ABSOLUTE_FILE_PATH, '$1 $2');\n newText = newText.replace(CJK_UNIX_RELATIVE_FILE_PATH, '$1 $2');\n newText = newText.replace(CJK_WINDOWS_PATH, '$1 $2');\n\n newText = newText.replace(UNIX_ABSOLUTE_FILE_PATH_SLASH_CJK, '$1 $2');\n newText = newText.replace(UNIX_RELATIVE_FILE_PATH_SLASH_CJK, '$1 $2');\n\n // Pipe reading is per line: a pipe in direct CJK contact makes every pipe on the line a separator with spaces on both sides (CJK | CJK, as in concatenated page titles)\n // A line whose pipes touch no CJK keeps them tight as joiner tokens (x|y, ps aux|grep node)\n newText = newText\n .split('\\n')\n .map((line) => {\n if (!PIPE_CJK_CONTACT.test(line)) {\n return line;\n }\n return line.replace(PIPE_SEPARATOR, '$1 $2 ');\n })\n .join('\\n');\n\n // A pipe/plus separator space can land just inside a closing quote; re-strip so the first pass already emits what a second pass would (idempotency)\n newText = newText.replace(FIX_QUOTE_ANY_QUOTE, '$1$2$3');\n\n newText = compoundWordManager.restore(newText);\n\n newText = newText.replace(CJK_LEFT_BRACKET, '$1 $2');\n newText = newText.replace(RIGHT_BRACKET_CJK, '$1 $2');\n newText = newText.replace(ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET, '$1 $2$3$4');\n newText = newText.replace(LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK, '$1$2$3 $4');\n newText = newText.replace(ANS_CJK_RIGHT_QUOTE_ANY_RIGHT_QUOTE, '$1 $2$3$4');\n\n newText = newText.replace(AN_LEFT_BRACKET, '$1 $2');\n newText = newText.replace(RIGHT_BRACKET_AN, '$1 $2');\n\n newText = newText.replace(CJK_ANS, '$1 $2');\n newText = newText.replace(ANS_CJK, '$1 $2');\n\n newText = newText.replace(S_A, '$1 $2');\n newText = newText.replace(COPYRIGHT_DIGIT, '$1 $2');\n\n newText = newText.replace(MIDDLE_DOT, '・');\n\n newText = this.fixBracketSpacing(newText);\n\n if (hasHtmlTags) {\n newText = newText.replace(CJK_HTML_TAG_MENTION, '$1 ');\n newText = newText.replace(HTML_TAG_MENTION_CJK, ' $1');\n newText = mentionedTagManager.restore(newText);\n newText = htmlTagManager.restore(newText);\n }\n\n newText = newText.replace(CJK_HTTP_URL, '$1 ');\n newText = urlManager.restore(newText);\n newText = backtickManager.restore(newText);\n\n return newText;\n }\n\n public hasProperSpacing(text: string) {\n return this.spaceText(text) === text;\n }\n\n // Strip the spaces that earlier rules left just inside a bracket pair: no space after an opening bracket or before a closing bracket\n private fixBracketSpacing(text: string) {\n for (const { pattern, open, close } of BRACKET_PATTERNS) {\n text = text.replace(pattern, (_match, innerContent: string) => {\n if (!innerContent) {\n return `${open}${close}`;\n }\n const trimmedContent = innerContent.replace(/^ +| +$/g, '');\n return `${open}${trimmedContent}${close}`;\n });\n }\n return text;\n }\n}\n\nexport const pangu = new Pangu();\n","import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { Pangu } from '../shared/index.js';\n\nexport class NodePangu extends Pangu {\n async spaceFile(path: string) {\n const data = await readFile(path, 'utf8');\n return this.spaceText(data);\n }\n\n spaceFileSync(path: string) {\n return this.spaceText(readFileSync(path, 'utf8'));\n }\n}\n\nexport const pangu = new NodePangu();\n\nexport default pangu;\n","// TypeScript 5.8+ under NodeNext models Node's require(esm) and resolves the ESM entry's types directly. Runtime never performs the require either way, since Rolldown inlines the ESM entry into this bundle.\n\n// `import =` rather than `import from` because `verbatimModuleSyntax` rejects ESM syntax in a CommonJS file (TS1286), and this file has to emit CommonJS for the `export =` at the bottom to be legal\n// at all. Unlike a bare `require()` call, this form is typed by the compiler.\n// The namespace access below (`index.NodePangu`) is also load-bearing: a top-level `NodePangu` binding would collide with the inlined `class NodePangu`, and Rolldown would rename the class to `NodePangu$1`,\n// which is observable through `constructor.name` and in stack traces\nimport index = require('./index.js');\n\n// The module is a NodePangu instance carrying the named exports as class fields, so the object is complete at construction and no cast is needed anywhere. The class declaration is also what puts the\n// properties in the emitted .d.cts, so the published types show the same surface the runtime has instead of hiding it from every require() consumer\nclass PanguModule extends index.NodePangu {\n // Allows both: const pangu = require('pangu') AND const { NodePangu } = require('pangu')\n NodePangu = index.NodePangu;\n pangu: PanguModule = this;\n // Pure surface parity, not interop. `export =` sets no `__esModule` flag, so a default import already receives the whole module and does not need this. What needs it is explicit `.default` access\n // from a require() consumer, which is undefined without it.\n default: PanguModule = this;\n}\n\nconst pangu = new PanguModule();\n\n// `export =` is not a preference here, it is the only spelling that both runs and types. `export default` and `export const` are ESM syntax, which `verbatimModuleSyntax` rejects in a CommonJS file.\n// A plain `module.exports = pangu` assignment compiles and behaves identically at runtime, but TypeScript models export assignment only in .js files, so the emitted .d.cts collapses to `export {}`\n// and every property access in a consumer becomes TS2339.\nexport = pangu;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0OA,SAAS,YAAY,KAAa;CAEhC,IAAI,gCAAgC,IAAI,MAAM,KAAK,KAAK,CAAC,EAAA,CAAG,UAAU,IAAI,MAAM,KAAK,KAAK,CAAC,EAAA,CAAG;CAC9F,SAAS;EACP,MAAM,UAAU,IAAI,QAAQ,+BAA+B,EAAE;EAC7D,IAAI,QAAQ,SAAS,GAAG,KAAK,+BAA+B,GAAG;GAC7D;GACA,MAAM,QAAQ,MAAM,GAAG,EAAE;GACzB;EACF;EACA,IAAI,YAAY,KACd,OAAO;EAET,MAAM;CACR;AACF;;;CA/Oa,0BAA0B;CAC1B,kBAAkB;CAClB,WAAW;CACX,yBAAyB;CACzB,WAAW;CACX,kCAAkC;CAClC,qCAAqC;CACrC,yBAAyB;CACzB,+BAA+B;CAC/B,mBAAmB;CACnB,gCAAgC;CAChC,eAAe;CACf,qBAAqB;CACrB,WAAW;CAKX,uBAAuB;CAEvB,MAAM,GAAG,0BAA0B,kBAAkB,WAAW,yBAAyB,WAAW,kCAAkC,qCAAqC,yBAAyB;CAGpM,KAAK;CACL,IAAI;CACJ,WAAW;CAGX,YAAY;CACZ,kBAAkB;CAGlB,SAAS;CAGT,sBAAsB;CACtB,uBAAuB;CAEvB,yBAAyB;CAEzB,0BAA0B;CAK1B,gBAAgB,GAAG,IAAI,iBAAiB,4BAA4B,gCAAgC,eAAe,WAAW;CAC9H,iBAAiB,GAAG,IAAI,iBAAiB,2BAA2B,gCAAgC,eAAe,WAAW,qBAAqB;CAInJ,iBAAiB;CACjB,kBAAkB;CAGlB,0BAA0B,IAAI,OAAO,cAAc,eAAe,gCAAgC,gBAAgB,GAAG;CAGrH,0BAA0B,IAAI,OAAO,eAAe,eAAe,OAAO,gBAAgB,GAAG;CAG7F,oBAAoB;CAEpB,UAAU,IAAI,OAAO,IAAI,IAAI,EAAE;CAG/B,kBAAkB,IAAI,OAAO,KAAK,IAAI,oBAAoB,MAAM,GAAG,KAAK,GAAG;CAG3E,kBAAkB,IAAI,OAAO,kBAAkB,IAAI,KAAK,GAAG;CAG3D,YAAY,IAAI,OAAO,KAAK,IAAI,iBAAiB,MAAM,GAAG,KAAK,GAAG;CAClE,mBAAmB,IAAI,OAAO,KAAK,IAAI,SAAS,GAAG;CAInD,aAAa,IAAI,OAAO,KAAK,IAAI,aAAa,GAAG,YAAY,MAAM,GAAG,KAAK,GAAG;CAC9E,gBAAgB,IAAI,OAAO,KAAK,GAAG,WAAW,IAAI,KAAK,GAAG;CAE1D,eAAe,IAAI,OAAO,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG;CAGvD,oBAAoB,IAAI,OAAO,KAAK,IAAI,SAAS,SAAS,WAAW,GAAG;CAGxE,WAAW,IAAI,OAAO,uBAAuB,IAAI,KAAK,GAAG;CAGzD,YAAY,IAAI,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;CACrD,YAAY,IAAI,OAAO,KAAK,OAAO,MAAM,IAAI,KAAK,GAAG;CAGrD,sBAAsB,IAAI,OAAO,KAAK,OAAO,2BAA2B,OAAO,MAAM,GAAG;CAGxF,WAAW,IAAI,OAAO,eAAe,GAAG,KAAK,GAAG;CAGhD,eAAe,IAAI,OAAO,KAAK,IAAI,SAAS,GAAG,KAAK,GAAG;CAEvD,kCAAkC,IAAI,OAAO,KAAK,IAAI,YAAY,GAAG;CACrE,mBAAmB,IAAI,OAAO,QAAQ,IAAI,KAAK,GAAG;CAClD,8BAA8B,IAAI,OAAO,KAAK,KAAK,IAAI,YAAY,GAAG;CAEtE,wBAAwB,IAAI,OAAO,QAAQ,IAAI,SAAS,GAAG;CAG3D,oBAAoB,IAAI,OAAO,KAAK,IAAI,SAAS,IAAI,UAAU,IAAI,KAAK,GAAG;CAI3E,WAAW,IAAI,OAAO,KAAK,IAAI,qBAAqB,GAAG;CAEvD,WAAW,IAAI,OAAO,sBAAsB,IAAI,KAAK,GAAG;CAS/D,cAAc;CACP,qBAAqB,IAAI,OAAO,GAAG,YAAY,EAAE;CAGxD,uBAAuB;CAMhB,mBAAmB,IAAI,OAAO,KAAK,IAAI,MAAM,UAAU,MAAM,GAAG,KAAK,GAAG;CACxE,mBAAmB,IAAI,OAAO,KAAK,KAAK,qBAAqB,MAAM,UAAU,QAAQ,YAAY,KAAK,IAAI,KAAK,GAAG;CAGlH,mBAAmB,IAAI,OAAO,IAAI,IAAI,WAAW,IAAI,EAAE;CACvD,iBAAiB;CAIjB,mBAAmB,IAAI,OAAO,IAAI,IAAI,WAAW,IAAI,EAAE;CAGvD,iBAAiB;CAGjB,6CAA6C,IAAI,OAAO,QAAQ,qBAAqB,iDAAiD,GAAG;CAGzI,0BAA0B,IAAI,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,IAAI,KAAK,GAAG;CAKvF,iBAAiB,IAAI,OAAO,KAAK,IAAI,iBAAiB,GAAG;CAEzD,kBAAkB,IAAI,OAAO,KAAK,IAAI,oBAAoB,GAAG;CAG7D,iBAAiB,IAAI,OAAO,qBAAqB,IAAI,KAAK,GAAG;CAG7D,gBAAgB,IAAI,OAAO,KAAK,IAAI,SAAS,GAAG,KAAK,GAAG;CACxD,gBAAgB,IAAI,OAAO,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG;CACxD,mBAAmB,IAAI,OAAO,KAAK,IAAI,SAAS,GAAG,KAAK,GAAG;CAC3D,mBAAmB,IAAI,OAAO,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG;CAM3D,mBAAmB,IAAI,OAAO,KAAK,IAAI,MAAM,uBAAuB,KAAK,GAAG;CAC5E,oBAAoB,IAAI,OAAO,KAAK,wBAAwB,MAAM,IAAI,KAAK,GAAG;CAE9E,yCAAyC,IAAI,OAAO,KAAK,KAAK,IAAI,oBAAoB,KAAK,IAAI,qBAAqB,GAAG;CAEvH,yCAAyC,IAAI,OAAO,eAAe,KAAK,IAAI,0BAA0B,KAAK,IAAI,KAAK,GAAG;CAMvH,sCAAsC,IAAI,OAAO,KAAK,KAAK,IAAI,uDAAuD,KAAK,IAAI,0BAA0B,GAAG;CAI5J,kBAAkB,IAAI,OAAO,KAAK,GAAG,YAAY,GAAG,OAAO,oBAAoB,KAAK,GAAG;CACvF,mBAAmB,IAAI,OAAO,KAAK,qBAAqB,MAAM,GAAG,KAAK,GAAG;CAEzE,8BAA8B,IAAI,OAAO,KAAK,IAAI,KAAK,wBAAwB,OAAO,IAAI,GAAG;CAC7F,8BAA8B,IAAI,OAAO,KAAK,IAAI,KAAK,wBAAwB,OAAO,IAAI,GAAG;CAC7F,mBAAmB,IAAI,OAAO,KAAK,IAAI,KAAK,kBAAkB,OAAO,IAAI,GAAG;CAE5E,oCAAoC,IAAI,OAAO,IAAI,wBAAwB,OAAO,MAAM,IAAI,KAAK,GAAG;CACpG,oCAAoC,IAAI,OAAO,IAAI,wBAAwB,OAAO,MAAM,IAAI,KAAK,GAAG;CAEpG,UAAU,IAAI,OAAO,KAAK,IAAI,QAAQ,qBAAqB,MAAM,cAAc,QAAQ,YAAY,IAAI,GAAG;CAC1G,UAAU,IAAI,OAAO,KAAK,eAAe,MAAM,IAAI,KAAK,GAAG;CAE3D,MAAM,IAAI,OAAO,QAAQ,EAAE,KAAK,GAAG;CAGnC,kBAAkB;CAGlB,aAAa;CAIb,iCAAiB,IAAI,IAAI;EAAC;EAAQ;EAAQ;EAAM;EAAO;EAAS;EAAM;EAAO;EAAS;EAAQ;EAAQ;EAAS;EAAU;EAAS;CAAK,CAAC;CACxI,gBAAgB;CAChB,mBAAmB;CAEnB,uBAAuB,IAAI,OAAO,KAAK,IAAI,eAAe,GAAG;CAC7D,uBAAuB,IAAI,OAAO,gBAAgB,IAAI,KAAK,GAAG;CAI9D,WAAW;CAElB,gCAAgC;CACzB,eAAe,IAAI,OAAO,KAAK,IAAI,eAAe,GAAG;CAoBrD,mBAAmB;EAC9B;GAAE,SAAS;GAAe,MAAM;GAAK,OAAO;EAAI;EAChD;GAAE,SAAS;GAAiB,MAAM;GAAK,OAAO;EAAI;EAClD;GAAE,SAAS;GAAmB,MAAM;GAAK,OAAO;EAAI;EACpD;GAAE,SAAS;GAAiB,MAAM;GAAK,OAAO;EAAI;CACpD;CAGa,sBAAb,MAAa,oBAAoB;EAQrB;EACA;EACA;EATV,OAAe,+BAAe,IAAI,IAAoB;EAEtD,QAA0B,CAAC;EAC3B,QAAgB;EAChB;EAEA,YACE,aACA,gBACA,cACA;GAHQ,KAAA,cAAA;GACA,KAAA,iBAAA;GACA,KAAA,eAAA;GAER,MAAM,WAAW,GAAG,iBAAiB,cAAc;GACnD,IAAI,UAAU,oBAAoB,aAAa,IAAI,QAAQ;GAC3D,IAAI,CAAC,SAAS;IACZ,MAAM,eAAe,KAAK,eAAe,QAAQ,uBAAuB,MAAM;IAC9E,MAAM,aAAa,KAAK,aAAa,QAAQ,uBAAuB,MAAM;IAC1E,UAAU,IAAI,OAAO,GAAG,eAAe,KAAK,YAAY,QAAQ,cAAc,GAAG;IACjF,oBAAoB,aAAa,IAAI,UAAU,OAAO;GACxD;GACA,KAAK,UAAU;EACjB;EAEA,MAAM,MAAc;GAClB,KAAK,MAAM,KAAK,SAAS;GACzB,OAAO,GAAG,KAAK,iBAAiB,KAAK,cAAc,KAAK,UAAU,KAAK;EACzE;EAEA,QAAQ,MAAc;GACpB,IAAI,KAAK,UAAU,GACjB,OAAO;GAET,OAAO,KAAK,QAAQ,KAAK,UAAU,QAAQ,UAAkB;IAC3D,OAAO,KAAK,MAAM,SAAS,OAAO,EAAE,MAAM;GAC5C,CAAC;EACH;CACF;CAEa,QAAb,MAAmB;EACjB;EAEA,cAAc;GACZ,KAAK,UAAU;EACjB;EAEA,UAAiB,MAAc;GAC7B,IAAI,OAAO,SAAS,UAAU;IAC5B,QAAQ,KAAK,uDAAuD,OAAO,MAAM;IACjF,OAAO;GACT;GAEA,IAAI,KAAK,UAAU,KAAK,CAAC,QAAQ,KAAK,IAAI,GACxC,OAAO;GAGT,IAAI,UAAU;GAGd,MAAM,kBAAkB,IAAI,oBAAoB,qBAAqB,KAAU,GAAQ;GACvF,UAAU,QAAQ,QAAQ,eAAe,QAAQ,YAAoB;IACnE,OAAO,KAAK,gBAAgB,MAAM,OAAO,EAAE;GAC7C,CAAC;GAGD,MAAM,aAAa,IAAI,oBAAoB,yBAAyB,KAAU,GAAQ;GACtF,UAAU,QAAQ,QAAQ,WAAW,UAAU;IAC7C,MAAM,MAAM,YAAY,KAAK;IAC7B,OAAO,WAAW,MAAM,GAAG,IAAI,MAAM,MAAM,IAAI,MAAM;GACvD,CAAC;GAED,MAAM,iBAAiB,IAAI,oBAAoB,yBAAyB,KAAU,GAAQ;GAC1F,MAAM,sBAAsB,IAAI,oBAAoB,qBAAqB,KAAU,GAAQ;GAC3F,IAAI,cAAc;GAElB,IAAI,QAAQ,SAAS,GAAG,GAAG;IACzB,cAAc;IAEd,MAAM,mBAAmB;IAGzB,MAAM,iCAAiB,IAAI,IAAY;IACvC,KAAK,MAAM,cAAc,QAAQ,SAAS,gBAAgB,GACxD,eAAe,IAAI,WAAW,EAAE,CAAE,YAAY,CAAC;IAIjD,UAAU,QAAQ,QAAQ,mBAAmB,UAAU;KACrD,MAAM,UAAU,MAAM,MAAM,aAAa;KACzC,IAAI,SAAS;MACX,MAAM,UAAU,QAAQ,EAAE,CAAE,YAAY;MACxC,IAAI,CAAC,eAAe,IAAI,OAAO,KAAK,CAAC,eAAe,IAAI,OAAO,GAC7D,OAAO,oBAAoB,MAAM,KAAK;KAE1C;KACA,MAAM,eAAe,MAAM,QAAQ,qBAAqB,YAAY,UAAkB,cAAsB;MAE1G,OAAO,GAAG,SAAS,IADI,KAAK,UAAU,SACf,EAAe;KACxC,CAAC;KAED,OAAO,eAAe,MAAM,YAAY;IAC1C,CAAC;GACH;GAGA,UAAU,QAAQ,QAAQ,UAAU,OAAO;GAE3C,UAAU,QAAQ,QAAQ,iBAAiB,OAAO;GAClD,UAAU,QAAQ,QAAQ,iBAAiB,KAAK;GAChD,UAAU,QAAQ,QAAQ,WAAW,OAAO;GAC5C,UAAU,QAAQ,QAAQ,kBAAkB,QAAQ;GACpD,UAAU,QAAQ,QAAQ,YAAY,OAAO;GAC7C,UAAU,QAAQ,QAAQ,eAAe,SAAS;GAClD,UAAU,QAAQ,QAAQ,cAAc,SAAS;GACjD,UAAU,QAAQ,QAAQ,mBAAmB,OAAO;GAEpD,UAAU,QAAQ,QAAQ,WAAW,OAAO;GAC5C,UAAU,QAAQ,QAAQ,WAAW,OAAO;GAC5C,UAAU,QAAQ,QAAQ,qBAAqB,QAAQ;GAEvD,UAAU,QAAQ,QAAQ,UAAU,OAAO;GAC3C,UAAU,QAAQ,QAAQ,cAAc,SAAS;GAEjD,UAAU,QAAQ,QAAQ,6BAA6B,MAAM;GAG7D,MAAM,wBAAwB,IAAI,oBAAoB,iCAAiC,KAAU,GAAQ;GAEzG,UAAU,QAAQ,QAAQ,wBAAwB,UAAU;IAC1D,OAAO,sBAAsB,MAAM,KAAK;GAC1C,CAAC;GAED,UAAU,QAAQ,QAAQ,iCAAiC,OAAO;GAClE,UAAU,QAAQ,QAAQ,kBAAkB,OAAO;GAEnD,UAAU,sBAAsB,QAAQ,OAAO;GAG/C,IAAI,QAAQ,UAAU,GACpB,UAAU,QAAQ,QAAQ,mBAAmB,cAAc;GAE7D,UAAU,QAAQ,QAAQ,UAAU,OAAO;GAC3C,UAAU,QAAQ,QAAQ,UAAU,OAAO;GAG3C,MAAM,sBAAsB,IAAI,oBAAoB,8BAA8B,KAAU,GAAQ;GAMpG,UAAU,QAAQ,QAAQ,mKAAwB,UAAU;IAC1D,OAAO,oBAAoB,MAAM,KAAK;GACxC,CAAC;GAGD,UAAU,QAAQ,QAAQ,yBAAyB,SAAS;GAG5D,UAAU,QAAQ,QAAQ,gBAAgB,SAAS;GACnD,UAAU,QAAQ,QAAQ,iBAAiB,SAAS;GACpD,UAAU,QAAQ,QAAQ,gBAAgB,SAAS;GAMnD,UAAU,QACP,MAAM,IAAI,CAAC,CACX,KAAK,SAAS;IAWb,QAVe,iBAAiB,KAAK,IAAI,IACrC,KAAK,QAAQ,iBAAiB,QAAQ,WAAmB;KAGvD,IAAI,CAAC,mBAAmB,KAAK,oBAAoB,QAAQ,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,GACjF,OAAO;KAET,OAAO,qBAAqB,KAAK,KAAK,SAAS,MAAM,EAAE,IAAI,MAAM;IACnE,CAAC,IACD,KAAA,CACU,QAAQ,4CAA4C,IAAI;GACxE,CAAC,CAAC,CACD,KAAK,IAAI;GAEZ,UAAU,QAAQ,QAAQ,kBAAkB,UAAU;GACtD,UAAU,QAAQ,QAAQ,kBAAkB,UAAU;GAEtD,UAAU,QAAQ,QAAQ,eAAe,UAAU;GACnD,UAAU,QAAQ,QAAQ,eAAe,UAAU;GACnD,UAAU,QAAQ,QAAQ,kBAAkB,UAAU;GACtD,UAAU,QAAQ,QAAQ,kBAAkB,UAAU;GAEtD,UAAU,QAAQ,QAAQ,6BAA6B,OAAO;GAC9D,UAAU,QAAQ,QAAQ,6BAA6B,OAAO;GAC9D,UAAU,QAAQ,QAAQ,kBAAkB,OAAO;GAEnD,UAAU,QAAQ,QAAQ,mCAAmC,OAAO;GACpE,UAAU,QAAQ,QAAQ,mCAAmC,OAAO;GAIpE,UAAU,QACP,MAAM,IAAI,CAAC,CACX,KAAK,SAAS;IACb,IAAI,CAAC,iBAAiB,KAAK,IAAI,GAC7B,OAAO;IAET,OAAO,KAAK,QAAQ,gBAAgB,QAAQ;GAC9C,CAAC,CAAC,CACD,KAAK,IAAI;GAGZ,UAAU,QAAQ,QAAQ,qBAAqB,QAAQ;GAEvD,UAAU,oBAAoB,QAAQ,OAAO;GAE7C,UAAU,QAAQ,QAAQ,kBAAkB,OAAO;GACnD,UAAU,QAAQ,QAAQ,mBAAmB,OAAO;GACpD,UAAU,QAAQ,QAAQ,wCAAwC,WAAW;GAC7E,UAAU,QAAQ,QAAQ,wCAAwC,WAAW;GAC7E,UAAU,QAAQ,QAAQ,qCAAqC,WAAW;GAE1E,UAAU,QAAQ,QAAQ,iBAAiB,OAAO;GAClD,UAAU,QAAQ,QAAQ,kBAAkB,OAAO;GAEnD,UAAU,QAAQ,QAAQ,SAAS,OAAO;GAC1C,UAAU,QAAQ,QAAQ,SAAS,OAAO;GAE1C,UAAU,QAAQ,QAAQ,KAAK,OAAO;GACtC,UAAU,QAAQ,QAAQ,iBAAiB,OAAO;GAElD,UAAU,QAAQ,QAAQ,YAAY,GAAG;GAEzC,UAAU,KAAK,kBAAkB,OAAO;GAExC,IAAI,aAAa;IACf,UAAU,QAAQ,QAAQ,sBAAsB,KAAK;IACrD,UAAU,QAAQ,QAAQ,sBAAsB,KAAK;IACrD,UAAU,oBAAoB,QAAQ,OAAO;IAC7C,UAAU,eAAe,QAAQ,OAAO;GAC1C;GAEA,UAAU,QAAQ,QAAQ,cAAc,KAAK;GAC7C,UAAU,WAAW,QAAQ,OAAO;GACpC,UAAU,gBAAgB,QAAQ,OAAO;GAEzC,OAAO;EACT;EAEA,iBAAwB,MAAc;GACpC,OAAO,KAAK,UAAU,IAAI,MAAM;EAClC;EAGA,kBAA0B,MAAc;GACtC,KAAK,MAAM,EAAE,SAAS,MAAM,WAAW,kBACrC,OAAO,KAAK,QAAQ,UAAU,QAAQ,iBAAyB;IAC7D,IAAI,CAAC,cACH,OAAO,GAAG,OAAO;IAEnB,MAAM,iBAAiB,aAAa,QAAQ,YAAY,EAAE;IAC1D,OAAO,GAAG,OAAO,iBAAiB;GACpC,CAAC;GAEH,OAAO;EACT;CACF;CAEa,AAAQ,IAAI,MAAM;;;;;;;;;;;;IEzgBxB;aDJe;CAET,YAAb,cAA+B,MAAM;EACnC,MAAM,UAAU,MAAc;GAC5B,MAAM,OAAO,OAAA,GAAA,iBAAA,SAAA,CAAe,MAAM,MAAM;GACxC,OAAO,KAAK,UAAU,IAAI;EAC5B;EAEA,cAAc,MAAc;GAC1B,OAAO,KAAK,WAAA,GAAA,QAAA,aAAA,CAAuB,MAAM,MAAM,CAAC;EAClD;CACF;CAEa,UAAQ,IAAI,UAAU;ECT5B,CAAA,CAAA,GAAA,aAAA,YAAA;AAIP,IAAM,cAAN,cAA0B,MAAM,UAAU;CAExC,YAAY,MAAM;CAClB,QAAqB;CAGrB,UAAuB;AACzB;AAEA,IAAM,QAAQ,IAAI,YAAY;iBAKrB"}