envprism 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-d_wGTwvJ.mjs","sources":["../../src/tui/app.ts"],"sourcesContent":["import {\n BoxRenderable,\n type CliRenderer,\n createCliRenderer,\n RGBA,\n ScrollBoxRenderable,\n TextRenderable\n} from '@opentui/core';\nimport { writeFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'pathe';\nimport { isSecretKey, maskValue } from '../core/mask.ts';\nimport { buildMatrix, type CellState, type Matrix } from '../core/matrix.ts';\nimport { rebuildKvLine, serializeEnv } from '../core/serialize.ts';\nimport type { EnvFile, KvEntry } from '../core/types.ts';\n\ntype Mode = 'browse' | 'filter' | 'prompt';\n\ntype Prompt =\n | { kind: 'edit'; key: string; file: EnvFile }\n | { kind: 'add-key'; file: EnvFile }\n | { kind: 'add-value'; key: string; file: EnvFile }\n | { kind: 'new-file' };\n\ntype Grouping = 'banner' | 'prefix';\n\ntype UndoEntry =\n | {\n kind: 'edit';\n file: EnvFile;\n entry: KvEntry;\n prevValue: string;\n prevRaw: string;\n }\n | { kind: 'add-kv'; file: EnvFile; entry: KvEntry }\n | { kind: 'delete-kv'; file: EnvFile; entry: KvEntry; idx: number };\n\nconst UNDO_LIMIT = 50;\n\ntype Pane = 'matrix' | 'sidebar';\n\ntype ItemKind = 'key' | 'divider';\ninterface MatrixItem {\n kind: ItemKind;\n // For 'key': the variable name; for 'divider': the section's lookup name\n // (or '__other__'). Holding the raw key/section here keeps focus stable\n // across rebuilds.\n ref: string;\n}\n\ninterface State {\n mode: Mode;\n filter: string;\n rowIdx: number;\n colIdx: number;\n prompt: Prompt | null;\n dirty: Set<EnvFile>;\n visibleKeys: string[]; // kept for callers that just want the keys\n visibleItems: MatrixItem[];\n message: string | null;\n driftOnly: boolean;\n confirmQuit: boolean;\n grouping: Grouping;\n helpOpen: boolean;\n undo: UndoEntry[];\n pane: Pane;\n sidebarIdx: number;\n enabled: Set<EnvFile>;\n showSecrets: boolean;\n collapsed: Set<string>;\n // (key + \"|\" + file.path) of cells the user has touched in this session.\n // Drives a green ● marker so unsaved local changes are visually distinct\n // from \"this file disagrees with base\", which uses the diff icons.\n modified: Set<string>;\n // Current value of the prompt input. We accumulate characters ourselves\n // in the global key handler instead of relying on opentui's InputRenderable,\n // because the InputRenderable swallows Esc (it interprets it as \"blur\").\n promptInput: string;\n}\n\n// Three semantic colours only. Everything else is grayscale so the eye\n// doesn't get pulled in five directions.\nconst COLORS = {\n fg: RGBA.fromHex('#cccccc'),\n fgDim: RGBA.fromHex('#666666'),\n fgHeader: RGBA.fromHex('#ffffff'),\n // accent — base file + section names (blue/purple, single accent for\n // navigational anchors).\n fgBase: RGBA.fromHex('#82aaff'),\n fgSection: RGBA.fromHex('#82aaff'),\n // drift/extra/placeholder — same yellow. They describe disagreement with\n // the base file, all one semantic.\n differs: RGBA.fromHex('#ffd866'),\n extra: RGBA.fromHex('#ffd866'),\n placeholder: RGBA.fromHex('#ffd866'),\n // user-made changes (modified cells, dirty files, unsaved counter) — green.\n // Different colour from drift on purpose: \"I just touched this\" is a\n // different signal from \"this disagrees with the base\".\n modified: RGBA.fromHex('#7fce6a'),\n fgDirty: RGBA.fromHex('#7fce6a'),\n // problem — value-is-missing red. Reserved exclusively for missing.\n missing: RGBA.fromHex('#ff6b6b'),\n focusBg: RGBA.fromHex('#3a3f4b')\n};\n\nconst PLACEHOLDER_RE =\n /^(todo|fixme|changeme|placeholder|tbd|x{3,}|your[_-]?(secret|key|token|password|api[_-]?key)(_here)?|replace[_-]?me)$/i;\n\nfunction isPlaceholderValue(value: string): boolean {\n const v = value.trim();\n if (v.length === 0) return false;\n return PLACEHOLDER_RE.test(v);\n}\n\nconst KEY_COL_WIDTH = 22;\nconst VALUE_COL_MIN = 18;\nconst SIDEBAR_WIDTH = 30;\nconst ROW_GAP = 0;\nconst CELL_PAD_X = 1;\nconst KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\ntype HelpLine =\n | { kind: 'header'; text: string }\n | { kind: 'entry'; text: string }\n | { kind: 'legend'; symbol: string; color: RGBA; description: string }\n | { kind: 'blank' };\n\nfunction buildHelpLines(): HelpLine[] {\n return [\n { kind: 'header', text: 'Panes' },\n {\n kind: 'entry',\n text: ' Tab Switch matrix ↔ files sidebar'\n },\n {\n kind: 'entry',\n text: ' ← (leftmost col) Hop from matrix into the sidebar'\n },\n { kind: 'blank' },\n { kind: 'header', text: 'Matrix navigation' },\n { kind: 'entry', text: ' ↑ ↓ ← → Move focused cell' },\n { kind: 'entry', text: ' Mouse wheel Scroll (both axes)' },\n { kind: 'blank' },\n { kind: 'header', text: 'Files sidebar' },\n { kind: 'entry', text: ' ↑ ↓ Move selection' },\n { kind: 'entry', text: ' Space Enable / disable file' },\n { kind: 'entry', text: ' b Make selected file the base' },\n { kind: 'entry', text: ' Tab / → Back to matrix' },\n { kind: 'blank' },\n { kind: 'header', text: 'Editing' },\n { kind: 'entry', text: ' e / Enter Edit focused cell value' },\n { kind: 'entry', text: ' a Add a new variable here' },\n {\n kind: 'entry',\n text: ' d Delete the variable from this file'\n },\n { kind: 'entry', text: ' n Create a new .env* file' },\n {\n kind: 'entry',\n text: ' = Sync focused value to every file'\n },\n {\n kind: 'entry',\n text: ' Ctrl-A (in edit) Apply typed value to every file'\n },\n { kind: 'entry', text: ' Ctrl-Z Undo last edit/add/delete' },\n { kind: 'entry', text: ' Ctrl-S Write all dirty files' },\n {\n kind: 'entry',\n text: ' c Collapse / expand focused section'\n },\n {\n kind: 'entry',\n text: ' Shift-C Expand every collapsed section'\n },\n { kind: 'blank' },\n { kind: 'header', text: 'View' },\n { kind: 'entry', text: ' / Filter keys' },\n { kind: 'entry', text: ' v All keys ↔ drift-only' },\n { kind: 'entry', text: ' g Group by prefix ↔ banner' },\n { kind: 'entry', text: ' Ctrl-T Show / mask secret values' },\n { kind: 'blank' },\n { kind: 'header', text: 'Help & exit' },\n { kind: 'entry', text: ' ? / ß Toggle this overlay' },\n { kind: 'entry', text: ' q Quit (twice if dirty)' },\n { kind: 'entry', text: ' Ctrl-C Force quit' },\n { kind: 'blank' },\n { kind: 'header', text: 'Cell icons' },\n {\n kind: 'legend',\n symbol: '≠ value',\n color: RGBA.fromHex('#ffd866'),\n description: 'value differs from base'\n },\n {\n kind: 'legend',\n symbol: '✗ missing',\n color: RGBA.fromHex('#ff6b6b'),\n description: 'this file has no value for the key'\n },\n {\n kind: 'legend',\n symbol: '★ value',\n color: RGBA.fromHex('#ffd866'),\n description: 'key is not in the base'\n },\n {\n kind: 'legend',\n symbol: '•••• (N)',\n color: RGBA.fromHex('#cccccc'),\n description: 'secret-suspect value masked by length'\n },\n {\n kind: 'legend',\n symbol: '⚠ TODO',\n color: RGBA.fromHex('#ffd866'),\n description: 'placeholder value (TODO, CHANGEME, xxx, …)'\n },\n {\n kind: 'legend',\n symbol: 'value ●',\n color: RGBA.fromHex('#7fce6a'),\n description: 'modified in this session — Ctrl-S to persist'\n }\n ];\n}\n\nexport async function runMatrixTui(initialMatrix: Matrix): Promise<void> {\n const renderer = await createCliRenderer({ useMouse: true });\n // The full discovered file list never changes; the matrix is rebuilt from\n // the currently *enabled* subset whenever the user toggles a file.\n const allFiles = initialMatrix.files.slice();\n let currentBase = initialMatrix.base;\n let matrix = initialMatrix;\n\n // Prefer banner grouping when the base file actually has section banners;\n // otherwise prefix grouping is more useful out of the box.\n const hasBanners = initialMatrix.keys.some(\n (k) => initialMatrix.sectionOf(k) !== undefined\n );\n const state: State = {\n mode: 'browse',\n filter: '',\n rowIdx: 0,\n colIdx: 0,\n prompt: null,\n dirty: new Set(),\n visibleKeys: matrix.keys.slice(),\n visibleItems: [],\n message: null,\n driftOnly: false,\n confirmQuit: false,\n grouping: hasBanners ? 'banner' : 'prefix',\n helpOpen: false,\n undo: [],\n pane: 'matrix',\n sidebarIdx: 0,\n enabled: new Set(allFiles),\n showSecrets: false,\n collapsed: new Set(),\n modified: new Set(),\n promptInput: ''\n };\n\n const cellKey = (key: string, file: EnvFile) => `${key}|${file.path}`;\n const markModified = (key: string, file: EnvFile) =>\n state.modified.add(cellKey(key, file));\n\n const SECTION_COLLAPSE_KEY = (name: string | undefined) =>\n name ?? '__other__';\n\n const pushUndo = (entry: UndoEntry) => {\n state.undo.push(entry);\n if (state.undo.length > UNDO_LIMIT) state.undo.shift();\n };\n\n // --- Layout ---\n const root = new BoxRenderable(renderer, {\n id: 'root',\n flexDirection: 'column',\n width: '100%',\n height: '100%'\n });\n renderer.root.add(root);\n\n const body = new BoxRenderable(renderer, {\n id: 'body',\n flexDirection: 'row',\n flexGrow: 1\n });\n root.add(body);\n\n const sidebar = new BoxRenderable(renderer, {\n id: 'sidebar',\n border: true,\n borderStyle: 'rounded',\n title: '',\n flexDirection: 'column',\n width: SIDEBAR_WIDTH,\n flexShrink: 0,\n paddingX: 1\n });\n body.add(sidebar);\n\n const matrixBox = new BoxRenderable(renderer, {\n id: 'matrix',\n border: true,\n borderStyle: 'rounded',\n title: '',\n flexDirection: 'column',\n flexGrow: 1,\n paddingX: 1,\n // Reserve a row at the bottom so the ScrollBox's horizontal scrollbar\n // does not overlap the matrix box's bottom border.\n paddingBottom: 1\n });\n body.add(matrixBox);\n\n // Header sits above the scrollable region so it doesn't scroll out of view.\n const headerHost = new BoxRenderable(renderer, {\n id: 'header-host',\n flexDirection: 'column',\n flexShrink: 0,\n // Match the ScrollBox viewport padding so the header column boundaries\n // line up with the data rows below.\n paddingRight: 1\n });\n matrixBox.add(headerHost);\n\n const scrollBox = new ScrollBoxRenderable(renderer, {\n id: 'matrix-scroll',\n flexGrow: 1,\n scrollX: true,\n scrollY: true,\n // Reserve a column on the right so the vertical scrollbar doesn't sit on\n // top of cell content, and a row at the bottom for the horizontal one.\n viewportOptions: { paddingRight: 1, paddingBottom: 1 },\n contentOptions: { flexDirection: 'column', rowGap: ROW_GAP }\n });\n matrixBox.add(scrollBox);\n\n const footer = new BoxRenderable(renderer, {\n id: 'footer',\n flexDirection: 'column',\n flexShrink: 0,\n paddingX: 1\n });\n root.add(footer);\n\n const hintA = new BoxRenderable(renderer, {\n id: 'hint-a',\n flexDirection: 'row',\n height: 1,\n flexShrink: 0\n });\n footer.add(hintA);\n\n const hintB = new BoxRenderable(renderer, {\n id: 'hint-b',\n flexDirection: 'row',\n height: 1,\n flexShrink: 0\n });\n footer.add(hintB);\n\n const status = new TextRenderable(renderer, {\n id: 'status',\n content: '',\n fg: COLORS.fgDim,\n wrapMode: 'none',\n height: 1\n });\n footer.add(status);\n\n // Filter has its own popover so we don't fight opentui's input-focus\n // behaviour. state.filter is the source of truth; characters are\n // accumulated by the global keypress handler when state.mode === 'filter'.\n const filterBox = new BoxRenderable(renderer, {\n id: 'filter-box',\n position: 'absolute',\n top: '15%',\n left: '20%',\n right: '20%',\n height: 'auto',\n zIndex: 60,\n border: true,\n borderStyle: 'rounded',\n title: ' Filter keys ',\n paddingX: 2,\n paddingY: 1,\n visible: false,\n backgroundColor: RGBA.fromHex('#1a1a1a'),\n flexDirection: 'column'\n });\n const filterField = new TextRenderable(renderer, {\n id: 'filter-field',\n content: '',\n fg: COLORS.fg,\n height: 1,\n wrapMode: 'none'\n });\n const filterStatus = new TextRenderable(renderer, {\n id: 'filter-status',\n content: '',\n fg: COLORS.fgDim,\n height: 1,\n marginTop: 1,\n wrapMode: 'none'\n });\n const filterHint = new TextRenderable(renderer, {\n id: 'filter-hint',\n content: 'Enter · keep filter Esc · clear & close',\n fg: COLORS.fg,\n height: 1,\n marginTop: 1,\n wrapMode: 'none'\n });\n filterBox.add(filterField);\n filterBox.add(filterStatus);\n filterBox.add(filterHint);\n renderer.root.add(filterBox);\n\n // --- Prompt modal (used for edit / add / new-file) ---\n const promptBox = new BoxRenderable(renderer, {\n id: 'prompt-box',\n position: 'absolute',\n top: '20%',\n left: '15%',\n right: '15%',\n height: 'auto',\n zIndex: 50,\n border: true,\n borderStyle: 'rounded',\n title: '',\n paddingX: 2,\n paddingY: 1,\n visible: false,\n backgroundColor: RGBA.fromHex('#1a1a1a'),\n flexDirection: 'column'\n });\n // Body of the modal is rebuilt on every refresh — either the context table\n // (edit / add-value) or just a single input row (add-key / new-file).\n const promptBody = new BoxRenderable(renderer, {\n id: 'prompt-body',\n flexDirection: 'column',\n flexGrow: 1\n });\n const promptHint = new TextRenderable(renderer, {\n id: 'prompt-hint',\n content: '',\n fg: COLORS.fg,\n height: 1,\n marginTop: 2,\n wrapMode: 'none'\n });\n promptBox.add(promptBody);\n promptBox.add(promptHint);\n renderer.root.add(promptBox);\n\n // Full-screen dim layer drawn below the prompt and help overlays so the\n // matrix fades back when a modal is active. Solid dark backgroundColor at\n // reduced opacity feels like a real dim/scrim.\n const dimOverlay = new BoxRenderable(renderer, {\n id: 'dim-overlay',\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n zIndex: 40,\n backgroundColor: RGBA.fromHex('#000000'),\n opacity: 0.6,\n visible: false\n });\n renderer.root.add(dimOverlay);\n\n // Floating help overlay. Hidden until '?' / 'ß' opens it. Two-column grid\n // so the overlay stays compact vertically and doesn't run off the bottom\n // on small terminals.\n // Help overlay — outer Box stays absolute-positioned; the body is rebuilt\n // every time it opens so we can switch between a one-column scrollable\n // layout (small terminals) and a two-column grid (wide terminals).\n const helpBox = new BoxRenderable(renderer, {\n id: 'help-overlay',\n position: 'absolute',\n top: 2,\n bottom: 2,\n left: '8%',\n right: '8%',\n zIndex: 100,\n border: true,\n borderStyle: 'rounded',\n title: ' Keybindings — press ? or Esc to close ',\n paddingX: 2,\n paddingY: 1,\n visible: false,\n backgroundColor: RGBA.fromHex('#1a1a1a'),\n flexDirection: 'column'\n });\n renderer.root.add(helpBox);\n\n // --- State helpers ---\n const sectionOf = (key: string): string | undefined =>\n state.grouping === 'banner' ? matrix.sectionOf(key) : prefixSection(key);\n\n const recomputeVisibleKeys = () => {\n // Two parallel structures:\n // visibleKeys — just the key names (used by editing helpers)\n // visibleItems — dividers + visible keys, in render order\n // Dividers stay in the item list even when their section is collapsed,\n // so the user can navigate onto one and expand it with 'c'.\n const visibleKeys: string[] = [];\n const items: MatrixItem[] = [];\n const orderedAll = orderedKeys(matrix, state, sectionOf);\n const seen = new Set<string>();\n const focusedRef = state.visibleItems[state.rowIdx]?.ref;\n for (const k of orderedAll) {\n if (!matchesFilter(k, state.filter)) continue;\n if (state.driftOnly && !keyDrifts(matrix, k)) continue;\n const sec = sectionOf(k);\n const secKey = SECTION_COLLAPSE_KEY(sec);\n if (!seen.has(secKey)) {\n seen.add(secKey);\n items.push({ kind: 'divider', ref: secKey });\n }\n if (state.collapsed.has(secKey)) continue;\n items.push({ kind: 'key', ref: k });\n visibleKeys.push(k);\n }\n state.visibleKeys = visibleKeys;\n state.visibleItems = items;\n // Try to keep focus on the same item across rebuilds.\n if (focusedRef) {\n const i = items.findIndex((it) => it.ref === focusedRef);\n if (i >= 0) state.rowIdx = i;\n }\n if (state.rowIdx >= items.length) {\n state.rowIdx = Math.max(0, items.length - 1);\n }\n // Make sure we don't land on an expanded divider after a rebuild.\n if (\n items[state.rowIdx]?.kind === 'divider' &&\n !state.collapsed.has(items[state.rowIdx]!.ref)\n ) {\n const next = stepRow(state, 1);\n const prev = stepRow(state, -1);\n state.rowIdx = next !== state.rowIdx ? next : prev;\n }\n };\n\n const rebuildMatrix = () => {\n const enabledList = allFiles.filter((f) => state.enabled.has(f));\n if (!state.enabled.has(currentBase)) {\n // Base got disabled — promote the first enabled file.\n const next = enabledList[0];\n if (next) currentBase = next;\n }\n matrix = buildMatrix(enabledList, currentBase);\n if (state.colIdx >= matrix.files.length) {\n state.colIdx = Math.max(0, matrix.files.length - 1);\n }\n if (state.sidebarIdx >= allFiles.length) {\n state.sidebarIdx = Math.max(0, allFiles.length - 1);\n }\n recomputeVisibleKeys();\n };\n\n const focusKey = (key: string) => {\n const idx = state.visibleKeys.indexOf(key);\n if (idx >= 0) state.rowIdx = idx;\n };\n\n const computeValueColWidth = (): number => {\n // Available width inside the matrix box (subtract sidebar, both borders\n // and the matrix's horizontal padding). If columns would have to shrink\n // below VALUE_COL_MIN to fit the viewport, keep them at the minimum and\n // let the ScrollBox handle the horizontal overflow.\n const available = Math.max(\n 0,\n renderer.terminalWidth - SIDEBAR_WIDTH - 6 - KEY_COL_WIDTH\n );\n const fair = matrix.files.length\n ? Math.floor(available / matrix.files.length)\n : VALUE_COL_MIN;\n return Math.max(VALUE_COL_MIN, fair);\n };\n\n const refreshNow = () => {\n const valueColWidth = computeValueColWidth();\n refreshSidebar(sidebar, renderer, matrix, allFiles, state);\n refreshMatrix(\n matrixBox,\n headerHost,\n scrollBox,\n renderer,\n matrix,\n state,\n valueColWidth,\n sectionOf\n );\n refreshFooter(hintA, hintB, status, renderer, state);\n refreshPrompt(promptBox, promptBody, promptHint, renderer, matrix, state);\n refreshHelp(helpBox, renderer, state);\n refreshFilter(filterBox, filterField, filterStatus, matrix, state);\n dimOverlay.visible =\n state.helpOpen || state.mode === 'prompt' || state.mode === 'filter';\n };\n\n // Coalesce burst-y refreshes (held arrow keys, fast filter typing) into\n // one render per microtask flush. The full refreshNow rebuilds every\n // matrix row, which is expensive when called for every keystroke; with\n // batching, holding an arrow key spends most of the time in opentui's\n // own redraw loop instead of in our re-render.\n let refreshScheduled = false;\n const refresh = () => {\n if (refreshScheduled) return;\n refreshScheduled = true;\n queueMicrotask(() => {\n refreshScheduled = false;\n refreshNow();\n });\n };\n\n recomputeVisibleKeys();\n refreshNow();\n\n // --- Interaction ---\n return new Promise<void>((resolve) => {\n const cleanup = () => {\n renderer._internalKeyInput.offInternal('keypress', onKey);\n renderer.destroy?.();\n resolve();\n };\n\n const openPrompt = (prompt: Prompt, value = '', placeholder = '') => {\n void placeholder; // input is rendered as plain text now, no placeholder slot\n state.prompt = prompt;\n state.mode = 'prompt';\n state.message = null;\n state.promptInput = value;\n refresh();\n };\n\n const closePrompt = (msg: string | null = null) => {\n state.prompt = null;\n state.mode = 'browse';\n state.promptInput = '';\n state.message = msg;\n refresh();\n };\n\n const focusedKey = (): string | null => {\n const item = state.visibleItems[state.rowIdx];\n return item && item.kind === 'key' ? item.ref : null;\n };\n\n const startEdit = () => {\n const key = focusedKey();\n const file = matrix.files[state.colIdx];\n if (!key || !file) {\n state.message = 'Move onto a variable row to edit.';\n refresh();\n return;\n }\n // Edit works on missing cells too — on commit we either update the\n // existing entry or append a new one.\n const entry = findKvEntry(file, key);\n openPrompt({ kind: 'edit', key, file }, entry?.value ?? '', 'value');\n };\n\n const startAdd = () => {\n const file = matrix.files[state.colIdx];\n if (!file) return;\n openPrompt({ kind: 'add-key', file }, '', 'NEW_KEY');\n };\n\n const startNewFile = () => {\n openPrompt({ kind: 'new-file' }, '', '.env.local');\n };\n\n const startDelete = () => {\n const key = focusedKey();\n const file = matrix.files[state.colIdx];\n if (!key || !file) {\n state.message = 'Move onto a variable row to delete.';\n refresh();\n return;\n }\n const entry = findKvEntry(file, key);\n if (!entry) {\n state.message = `${key} is not present in ${basename(file.path)}.`;\n refresh();\n return;\n }\n const idx = file.entries.indexOf(entry);\n if (idx >= 0) {\n pushUndo({ kind: 'delete-kv', file, entry, idx });\n file.entries.splice(idx, 1);\n }\n state.dirty.add(file);\n markModified(key, file);\n rebuildMatrix();\n state.message = `Deleted ${key} from ${basename(file.path)}. Ctrl-S to save.`;\n refresh();\n };\n\n const toggleEnabled = () => {\n const file = allFiles[state.sidebarIdx];\n if (!file) return;\n if (state.enabled.has(file)) {\n if (state.enabled.size === 1) {\n state.message = 'At least one file must stay enabled.';\n refresh();\n return;\n }\n state.enabled.delete(file);\n state.message = `Hidden ${basename(file.path)} from the matrix.`;\n } else {\n state.enabled.add(file);\n state.message = `Showing ${basename(file.path)} in the matrix.`;\n }\n rebuildMatrix();\n refresh();\n };\n\n const setBase = () => {\n const file = allFiles[state.sidebarIdx];\n if (!file) return;\n if (file === currentBase) {\n state.message = `${basename(file.path)} is already the base.`;\n refresh();\n return;\n }\n const wasDisabled = !state.enabled.has(file);\n if (wasDisabled) state.enabled.add(file);\n currentBase = file;\n rebuildMatrix();\n state.message = wasDisabled\n ? `${basename(file.path)} is now the base (re-enabled).`\n : `${basename(file.path)} is now the base.`;\n refresh();\n };\n\n const applyToAllFiles = (key: string, value: string): number => {\n // Set key=value in every enabled file. Used by '=' in the matrix and\n // Ctrl-A from the edit prompt. Each per-file mutation is undone\n // individually (one Ctrl-Z per file) — not perfect but predictable.\n let touched = 0;\n for (const file of matrix.files) {\n const existing = findKvEntry(file, key);\n if (existing) {\n if (existing.value === value) continue;\n pushUndo({\n kind: 'edit',\n file,\n entry: existing,\n prevValue: existing.value,\n prevRaw: existing.raw\n });\n existing.value = value;\n rebuildKvLine(existing);\n } else {\n const added = appendKv(file, key, value);\n pushUndo({ kind: 'add-kv', file, entry: added });\n }\n state.dirty.add(file);\n markModified(key, file);\n touched++;\n }\n return touched;\n };\n\n const syncToAll = () => {\n const key = focusedKey();\n const file = matrix.files[state.colIdx];\n if (!key || !file) {\n state.message = 'Move onto a variable row to sync.';\n refresh();\n return;\n }\n const entry = findKvEntry(file, key);\n if (!entry) {\n state.message = `${key} has no value in ${basename(file.path)} to sync.`;\n refresh();\n return;\n }\n const touched = applyToAllFiles(key, entry.value);\n rebuildMatrix();\n state.message =\n touched > 0\n ? `Synced ${key} to ${touched} file(s). Ctrl-S to save.`\n : `${key} is already in sync.`;\n refresh();\n };\n\n const undo = () => {\n const last = state.undo.pop();\n if (!last) {\n state.message = 'Nothing to undo.';\n refresh();\n return;\n }\n switch (last.kind) {\n case 'edit':\n last.entry.value = last.prevValue;\n last.entry.raw = last.prevRaw;\n state.dirty.add(last.file);\n state.message = `Undid edit on ${last.entry.key} in ${basename(last.file.path)}.`;\n break;\n case 'add-kv': {\n const i = last.file.entries.indexOf(last.entry);\n if (i >= 0) last.file.entries.splice(i, 1);\n state.dirty.add(last.file);\n state.message = `Undid add of ${last.entry.key} in ${basename(last.file.path)}.`;\n break;\n }\n case 'delete-kv':\n last.file.entries.splice(last.idx, 0, last.entry);\n state.dirty.add(last.file);\n state.message = `Undid delete of ${last.entry.key} in ${basename(last.file.path)}.`;\n break;\n }\n rebuildMatrix();\n refresh();\n };\n\n const commitPrompt = () => {\n if (!state.prompt) return;\n const p = state.prompt;\n const raw = state.promptInput;\n\n if (p.kind === 'edit') {\n const existing = findKvEntry(p.file, p.key);\n if (existing) {\n pushUndo({\n kind: 'edit',\n file: p.file,\n entry: existing,\n prevValue: existing.value,\n prevRaw: existing.raw\n });\n existing.value = raw;\n rebuildKvLine(existing);\n state.dirty.add(p.file);\n markModified(p.key, p.file);\n rebuildMatrix();\n closePrompt(\n `Edited ${p.key} in ${basename(p.file.path)}. Ctrl-S to save.`\n );\n } else {\n // Missing cell: add the key with the typed value.\n const added = appendKv(p.file, p.key, raw);\n pushUndo({ kind: 'add-kv', file: p.file, entry: added });\n state.dirty.add(p.file);\n markModified(p.key, p.file);\n rebuildMatrix();\n closePrompt(\n `Added ${p.key} to ${basename(p.file.path)}. Ctrl-S to save.`\n );\n }\n return;\n }\n\n if (p.kind === 'add-key') {\n const key = raw.trim();\n if (!KEY_RE.test(key)) {\n state.message = `Invalid key \"${key}\". Must match ${KEY_RE.source}.`;\n refresh();\n return;\n }\n if (findKvEntry(p.file, key)) {\n state.message = `${key} already exists in ${basename(p.file.path)}. Use edit instead.`;\n refresh();\n return;\n }\n openPrompt({ kind: 'add-value', key, file: p.file }, '', 'value');\n return;\n }\n\n if (p.kind === 'add-value') {\n const added = appendKv(p.file, p.key, raw);\n pushUndo({ kind: 'add-kv', file: p.file, entry: added });\n state.dirty.add(p.file);\n markModified(p.key, p.file);\n rebuildMatrix();\n focusKey(p.key);\n state.colIdx = matrix.files.indexOf(p.file);\n closePrompt(\n `Added ${p.key} to ${basename(p.file.path)}. Ctrl-S to save.`\n );\n return;\n }\n\n if (p.kind === 'new-file') {\n const name = raw.trim();\n if (!isValidEnvFileName(name)) {\n state.message =\n name.length === 0\n ? 'Filename cannot be empty.'\n : !name.startsWith('.env')\n ? `Filename must start with \".env\" (got \"${name}\").`\n : `\"${name}\" is not a valid env filename.`;\n refresh();\n return;\n }\n const newPath = join(dirname(currentBase.path), name);\n if (allFiles.some((f) => f.path === newPath)) {\n state.message = `${name} already exists.`;\n refresh();\n return;\n }\n const newFile = createEmptyEnvFile(newPath);\n allFiles.push(newFile);\n state.enabled.add(newFile);\n state.dirty.add(newFile);\n rebuildMatrix();\n state.colIdx = matrix.files.indexOf(newFile);\n closePrompt(`Created ${name}. Ctrl-S to write to disk.`);\n return;\n }\n };\n\n const cancelPrompt = () => {\n closePrompt('Cancelled.');\n };\n\n const saveDirty = async () => {\n if (state.dirty.size === 0) {\n state.message = 'Nothing to save.';\n refresh();\n return;\n }\n const count = state.dirty.size;\n const errors: string[] = [];\n for (const file of state.dirty) {\n try {\n await writeFile(file.path, serializeEnv(file), 'utf8');\n } catch (err) {\n errors.push(\n `${basename(file.path)}: ${(err as Error).message ?? String(err)}`\n );\n }\n }\n if (errors.length === 0) {\n state.dirty.clear();\n state.modified.clear();\n state.message = `Saved ${count} file${count === 1 ? '' : 's'}.`;\n } else {\n state.message = `Save failed: ${errors.join('; ')}`;\n }\n refresh();\n };\n\n const onKey = (key: {\n name: string;\n ctrl?: boolean;\n shift?: boolean;\n sequence?: string;\n preventDefault?: () => void;\n }) => {\n if (state.helpOpen) {\n if (\n key.name === 'escape' ||\n key.sequence === '?' ||\n key.sequence === 'ß' ||\n key.name === 'q'\n ) {\n state.helpOpen = false;\n refresh();\n }\n return;\n }\n\n if (state.mode === 'prompt') {\n if (key.name === 'escape') {\n cancelPrompt();\n return;\n }\n if (key.name === 'return') {\n commitPrompt();\n return;\n }\n if (key.name === 'backspace') {\n if (state.promptInput.length > 0) {\n state.promptInput = state.promptInput.slice(0, -1);\n refresh();\n }\n return;\n }\n if (key.ctrl && key.name === 't') {\n state.showSecrets = !state.showSecrets;\n refresh();\n return;\n }\n if (key.ctrl && key.name === 'a' && state.prompt) {\n const p = state.prompt;\n if (p.kind === 'edit' || p.kind === 'add-value') {\n const touched = applyToAllFiles(p.key, state.promptInput);\n rebuildMatrix();\n closePrompt(\n touched > 0\n ? `Set ${p.key} in ${touched} file(s). Ctrl-S to save.`\n : `${p.key} already had that value everywhere.`\n );\n }\n return;\n }\n const seq = key.sequence ?? '';\n if (!key.ctrl && seq.length === 1 && seq >= ' ' && seq !== '\\x7f') {\n state.promptInput += seq;\n refresh();\n }\n return;\n }\n\n if (state.mode === 'filter') {\n if (key.name === 'escape') {\n state.filter = '';\n state.mode = 'browse';\n recomputeVisibleKeys();\n refresh();\n return;\n }\n if (key.name === 'return') {\n state.mode = 'browse';\n refresh();\n return;\n }\n if (key.name === 'backspace') {\n if (state.filter.length > 0) {\n state.filter = state.filter.slice(0, -1);\n recomputeVisibleKeys();\n refresh();\n }\n return;\n }\n // Append any printable character. opentui's KeyEvent puts the actual\n // char into `sequence` for normal keystrokes.\n const seq = key.sequence ?? '';\n if (seq.length === 1 && seq >= ' ' && seq !== '\\x7f') {\n state.filter += seq;\n recomputeVisibleKeys();\n refresh();\n }\n return;\n }\n\n // Browse mode.\n if (key.ctrl && key.name === 'c') return cleanup();\n if (key.ctrl && key.name === 's') {\n state.confirmQuit = false;\n return void saveDirty();\n }\n if (key.ctrl && key.name === 'z') {\n state.confirmQuit = false;\n return undo();\n }\n if (key.ctrl && key.name === 't') {\n state.showSecrets = !state.showSecrets;\n state.message = state.showSecrets\n ? 'Showing secret values in plain text.'\n : 'Masking secret values.';\n return refresh();\n }\n\n const tryQuit = () => {\n if (state.dirty.size > 0 && !state.confirmQuit) {\n state.confirmQuit = true;\n state.message = `${state.dirty.size} unsaved file(s). Press 'q' again to quit without saving, or Ctrl-S to save first.`;\n refresh();\n return;\n }\n cleanup();\n };\n // Any key other than q clears a pending quit confirmation.\n if (state.confirmQuit && key.name !== 'q') {\n state.confirmQuit = false;\n state.message = null;\n }\n\n if (state.pane === 'sidebar') {\n switch (key.name) {\n case 'q':\n return tryQuit();\n case 'tab':\n state.pane = 'matrix';\n return refresh();\n case 'right':\n state.pane = 'matrix';\n return refresh();\n case 'up':\n state.sidebarIdx = Math.max(0, state.sidebarIdx - 1);\n return refresh();\n case 'down':\n state.sidebarIdx = Math.min(\n allFiles.length - 1,\n state.sidebarIdx + 1\n );\n return refresh();\n case 'space':\n return toggleEnabled();\n case 'b':\n return setBase();\n }\n if (key.sequence === ' ') return toggleEnabled();\n if (key.sequence === '?' || key.sequence === 'ß') {\n state.helpOpen = true;\n return refresh();\n }\n return;\n }\n\n switch (key.name) {\n case 'q':\n return tryQuit();\n case 'tab':\n state.pane = 'sidebar';\n return refresh();\n case 'up':\n state.rowIdx = stepRow(state, -1);\n return refresh();\n case 'down':\n state.rowIdx = stepRow(state, 1);\n return refresh();\n case 'left':\n if (state.colIdx === 0) {\n // Already at the leftmost matrix column — hand focus to the sidebar.\n state.pane = 'sidebar';\n return refresh();\n }\n state.colIdx = Math.max(0, state.colIdx - 1);\n return refresh();\n case 'right':\n state.colIdx = Math.min(matrix.files.length - 1, state.colIdx + 1);\n return refresh();\n case 'e':\n case 'return':\n return startEdit();\n case 'a':\n return startAdd();\n case 'd':\n return startDelete();\n case 'n':\n return startNewFile();\n case 'v':\n state.driftOnly = !state.driftOnly;\n state.message = state.driftOnly\n ? 'Drift-only view (only keys with differences).'\n : 'Full view.';\n recomputeVisibleKeys();\n return refresh();\n case 'c': {\n if (key.shift) {\n // Capital C — expand every collapsed section. Use this when all\n // keys you'd navigate to are hidden behind a fold.\n if (state.collapsed.size === 0) {\n state.message = 'Nothing collapsed.';\n return refresh();\n }\n const count = state.collapsed.size;\n state.collapsed.clear();\n state.message = `Expanded ${count} section(s).`;\n recomputeVisibleKeys();\n return refresh();\n }\n // Collapse / expand the section of the focused item. Works on\n // both key rows and section dividers; on a divider this is the\n // only way back into a collapsed section.\n const item = state.visibleItems[state.rowIdx];\n if (!item) return;\n const sectionKey =\n item.kind === 'divider'\n ? item.ref\n : (sectionOf(item.ref) ?? '__other__');\n if (state.collapsed.has(sectionKey)) {\n state.collapsed.delete(sectionKey);\n state.message = `Expanded \"${sectionKey === '__other__' ? '(other)' : sectionKey}\".`;\n } else {\n state.collapsed.add(sectionKey);\n state.message = `Collapsed \"${sectionKey === '__other__' ? '(other)' : sectionKey}\". Press Shift-C to expand all.`;\n }\n recomputeVisibleKeys();\n return refresh();\n }\n case 'g': {\n // Preserve focus across the rebuild — focusedRef in\n // recomputeVisibleKeys finds the same item by ref.\n state.grouping = state.grouping === 'banner' ? 'prefix' : 'banner';\n recomputeVisibleKeys();\n state.message =\n state.grouping === 'banner'\n ? 'Group by comment banners.'\n : 'Group by key prefix (first underscore segment).';\n return refresh();\n }\n }\n\n if (key.sequence === '/' || key.name === 'slash') {\n state.mode = 'filter';\n state.message = null;\n refresh();\n return;\n }\n\n // = sync-to-all. opentui's key.name for \"=\" is inconsistent across\n // platforms so we check the sequence directly.\n if (key.sequence === '=') return syncToAll();\n\n if (key.sequence === '?' || key.sequence === 'ß') {\n state.helpOpen = true;\n refresh();\n }\n };\n\n // Use the internal channel so our handler runs *before* the focused\n // renderable processes the event. That lets us intercept Esc / Enter\n // before opentui's InputRenderable swallows them (Esc would otherwise\n // just blur the input instead of closing the modal).\n renderer._internalKeyInput.onInternal('keypress', onKey);\n renderer.on('resize', refresh);\n });\n}\n\n// --- Refreshers ---\n\nfunction refreshSidebar(\n sidebar: BoxRenderable,\n renderer: CliRenderer,\n matrix: Matrix,\n allFiles: EnvFile[],\n state: State\n): void {\n const total = allFiles.length;\n const enabled = state.enabled.size;\n sidebar.title =\n state.pane === 'sidebar'\n ? ` Files ${enabled}/${total} • focused `\n : ` Files ${enabled}/${total} `;\n removeAllChildren(sidebar);\n for (let i = 0; i < allFiles.length; i++) {\n const file = allFiles[i]!;\n const isBase = file === matrix.base;\n const isDirty = state.dirty.has(file);\n const isEnabled = state.enabled.has(file);\n const matrixIdx = matrix.files.indexOf(file);\n const isFocusCol = isEnabled && matrixIdx === state.colIdx;\n const isPaneFocus = state.pane === 'sidebar' && i === state.sidebarIdx;\n const nameFg = !isEnabled\n ? COLORS.fgDim\n : isBase\n ? COLORS.fgBase\n : COLORS.fg;\n\n const row = new BoxRenderable(renderer, {\n id: `file-${file.path}`,\n flexDirection: 'row',\n height: 1,\n flexShrink: 0,\n ...(isPaneFocus ? { backgroundColor: COLORS.focusBg } : {})\n });\n const span = (id: string, text: string, fg: RGBA) =>\n new TextRenderable(renderer, {\n id: `${row.id}-${id}`,\n content: text,\n fg,\n height: 1,\n wrapMode: 'none'\n });\n row.add(span('focus', `${isPaneFocus ? '▶' : ' '} `, COLORS.fg));\n row.add(\n span(\n 'dirty',\n `${isDirty ? '●' : ' '} `,\n isDirty ? COLORS.fgDirty : COLORS.fgDim\n )\n );\n row.add(\n span(\n 'base',\n `${isBase ? '★' : ' '} `,\n isBase ? COLORS.fgBase : COLORS.fgDim\n )\n );\n row.add(span('col', `${isFocusCol ? '▸' : ' '} `, COLORS.fgDim));\n row.add(span('enabled', `${isEnabled ? '✓' : '☐'} `, COLORS.fgDim));\n row.add(span('name', basename(file.path), nameFg));\n sidebar.add(row);\n }\n}\n\nfunction refreshMatrix(\n matrixBox: BoxRenderable,\n headerHost: BoxRenderable,\n scrollBox: ScrollBoxRenderable,\n renderer: CliRenderer,\n matrix: Matrix,\n state: State,\n valueColWidth: number,\n sectionOf: (key: string) => string | undefined\n): void {\n matrixBox.title = matrixTitle(matrix, state);\n removeAllChildren(headerHost);\n removeAllChildren(scrollBox.content);\n\n headerHost.add(\n buildRow(renderer, 'header', [\n { text: 'KEY', fg: COLORS.fgHeader, width: KEY_COL_WIDTH },\n ...matrix.files.map((f) => ({\n text: basename(f.path),\n fg: COLORS.fgHeader,\n width: valueColWidth\n }))\n ])\n );\n\n // Walk every key (including those hidden by a collapsed section) so we can\n // render section dividers for collapsed groups too. Within an expanded\n // group we render the cell rows; within a collapsed one we render nothing\n // beyond the header.\n const totalWidth = KEY_COL_WIDTH + valueColWidth * matrix.files.length;\n const sectionStats = sectionMetadata(matrix, sectionOf, state);\n\n for (let r = 0; r < state.visibleItems.length; r++) {\n const item = state.visibleItems[r]!;\n if (item.kind === 'divider') {\n const sectionKey = item.ref;\n const sectionName = sectionKey === '__other__' ? undefined : sectionKey;\n const meta = sectionStats.get(sectionKey) ?? {\n drift: 0,\n missing: 0,\n total: 0\n };\n const focused = state.mode === 'browse' && r === state.rowIdx;\n scrollBox.content.add(\n buildSectionDivider(renderer, `row-${r}`, sectionName, totalWidth, {\n ...meta,\n collapsed: state.collapsed.has(sectionKey),\n focused\n })\n );\n continue;\n }\n const key = item.ref;\n const secret = isSecretKey(key) && !state.showSecrets;\n const cells: CellSpec[] = [\n { text: key, fg: COLORS.fg, width: KEY_COL_WIDTH }\n ];\n for (let c = 0; c < matrix.files.length; c++) {\n const file = matrix.files[c]!;\n const cell = matrix.cell(key, file);\n const focused =\n state.mode === 'browse' && r === state.rowIdx && c === state.colIdx;\n const isModified = state.modified.has(`${key}|${file.path}`);\n cells.push(\n buildValueCell(cell, secret, valueColWidth, focused, isModified)\n );\n }\n scrollBox.content.add(buildRow(renderer, `row-${r}`, cells));\n }\n\n // Keep the focused row in view. Single deferred call so layout has been\n // computed before we ask for a scroll target; doing it twice (sync +\n // deferred) was causing visible \"jumps\" because the two calls landed on\n // slightly different layouts.\n if (state.mode === 'browse' && state.visibleItems.length > 0) {\n const target = `row-${state.rowIdx}`;\n setImmediate(() => {\n try {\n scrollBox.scrollChildIntoView(target);\n } catch {\n /* row not laid out yet — next refresh will retry */\n }\n });\n }\n}\n\ninterface FooterSeg {\n text: string;\n fg: RGBA;\n}\n\nfunction bindings(specs: { key: string; label: string }[]): FooterSeg[] {\n // \"[key] label\" with the brackets dim and the key + label in normal fg.\n // Segments separated by dim \" · \".\n const out: FooterSeg[] = [];\n specs.forEach((spec, i) => {\n if (i > 0) out.push({ text: ' · ', fg: COLORS.fgDim });\n out.push({ text: '[', fg: COLORS.fgDim });\n out.push({ text: spec.key, fg: COLORS.fg });\n out.push({ text: '] ', fg: COLORS.fgDim });\n out.push({ text: spec.label, fg: COLORS.fg });\n });\n return out;\n}\n\nfunction renderHintBox(\n box: BoxRenderable,\n renderer: CliRenderer,\n segs: FooterSeg[]\n): void {\n removeAllChildren(box);\n segs.forEach((seg, i) => {\n box.add(\n new TextRenderable(renderer, {\n id: `${box.id}-seg-${i}`,\n content: seg.text,\n fg: seg.fg,\n height: 1,\n wrapMode: 'none'\n })\n );\n });\n}\n\nfunction refreshHelp(\n helpBox: BoxRenderable,\n renderer: CliRenderer,\n state: State\n): void {\n helpBox.visible = state.helpOpen;\n if (!state.helpOpen) return;\n removeAllChildren(helpBox);\n const lines = buildHelpLines();\n // Use one scrollable column when the terminal is narrow or short.\n const narrow = renderer.terminalWidth < 100;\n const short = renderer.terminalHeight < 36;\n const oneColumn = narrow || short;\n if (oneColumn) {\n const scroll = new ScrollBoxRenderable(renderer, {\n id: 'help-scroll',\n flexGrow: 1,\n scrollX: false,\n scrollY: true,\n viewportOptions: { paddingRight: 1 },\n contentOptions: { flexDirection: 'column' }\n });\n helpBox.add(scroll);\n lines.forEach((line, i) => {\n scroll.content.add(buildHelpRow(renderer, `help-${i}`, line));\n });\n return;\n }\n // Two-column grid.\n const grid = new BoxRenderable(renderer, {\n id: 'help-grid',\n flexDirection: 'row',\n flexGrow: 1,\n columnGap: 3\n });\n const left = new BoxRenderable(renderer, {\n id: 'help-left',\n flexDirection: 'column',\n flexGrow: 1,\n flexBasis: 0\n });\n const right = new BoxRenderable(renderer, {\n id: 'help-right',\n flexDirection: 'column',\n flexGrow: 1,\n flexBasis: 0\n });\n const half = Math.floor(lines.length / 2);\n let splitIdx = half;\n for (let i = half; i < lines.length; i++) {\n if (lines[i]?.kind === 'blank') {\n splitIdx = i + 1;\n break;\n }\n }\n lines\n .slice(0, splitIdx)\n .forEach((line, i) =>\n left.add(buildHelpRow(renderer, `help-l-${i}`, line))\n );\n lines\n .slice(splitIdx)\n .forEach((line, i) =>\n right.add(buildHelpRow(renderer, `help-r-${i}`, line))\n );\n grid.add(left);\n grid.add(right);\n helpBox.add(grid);\n}\n\nfunction refreshFilter(\n filterBox: BoxRenderable,\n filterField: TextRenderable,\n filterStatus: TextRenderable,\n matrix: Matrix,\n state: State\n): void {\n const open = state.mode === 'filter';\n filterBox.visible = open;\n if (!open) return;\n // Show the current filter with a fake cursor at the end. visibleKeys is\n // already filtered, so its length is the live match count.\n filterField.content = `▸ ${state.filter}▏`;\n const matches = state.visibleKeys.length;\n const total = matrix.keys.length;\n filterStatus.content =\n state.filter.length === 0\n ? 'Type to filter the keys list.'\n : `Matching ${matches} of ${total} keys.`;\n}\n\nfunction refreshFooter(\n hintA: BoxRenderable,\n hintB: BoxRenderable,\n status: TextRenderable,\n renderer: CliRenderer,\n state: State\n): void {\n const dirty = state.dirty.size;\n const dirtyTail: FooterSeg[] =\n dirty > 0\n ? [\n { text: ' ', fg: COLORS.fgDim },\n { text: '●', fg: COLORS.modified },\n { text: ` ${dirty} unsaved`, fg: COLORS.fg }\n ]\n : [];\n\n if (state.mode === 'filter') {\n renderHintBox(hintA, renderer, [\n ...bindings([\n { key: 'Enter', label: 'keep filter' },\n { key: 'Esc', label: 'clear' }\n ]),\n ...dirtyTail\n ]);\n renderHintBox(hintB, renderer, [{ text: ' Filter:', fg: COLORS.fgDim }]);\n } else if (state.mode === 'prompt') {\n renderHintBox(hintA, renderer, []);\n renderHintBox(hintB, renderer, []);\n } else if (state.pane === 'sidebar') {\n renderHintBox(hintA, renderer, [\n ...bindings([\n { key: '↑↓', label: 'move' },\n { key: 'Space', label: 'toggle' },\n { key: 'b', label: 'set base' },\n { key: 'Tab/→', label: 'matrix' },\n { key: '^S', label: 'save' },\n { key: '?', label: 'help' },\n { key: 'q', label: 'quit' }\n ]),\n ...dirtyTail\n ]);\n renderHintBox(hintB, renderer, [{ text: 'Files pane', fg: COLORS.fgDim }]);\n } else {\n renderHintBox(hintA, renderer, [\n ...bindings([\n { key: '↑↓←→', label: 'move' },\n { key: 'Tab', label: 'files' },\n { key: 'e', label: 'edit' },\n { key: 'a', label: 'add var' },\n { key: 'd', label: 'del var' },\n { key: 'n', label: 'new file' },\n { key: '=', label: 'sync to all' },\n { key: 'c', label: 'collapse' },\n { key: '^T', label: 'secrets' },\n { key: '^Z', label: 'undo' },\n { key: '^S', label: 'save' },\n { key: '/', label: 'filter' },\n { key: '?/ß', label: 'help' },\n { key: 'q', label: 'quit' }\n ]),\n ...dirtyTail\n ]);\n renderHintBox(hintB, renderer, [\n { text: 'view: ', fg: COLORS.fgDim },\n { text: state.driftOnly ? 'drift' : 'all', fg: COLORS.fg },\n { text: ' · group: ', fg: COLORS.fgDim },\n { text: state.grouping, fg: COLORS.fg },\n { text: ' · secrets: ', fg: COLORS.fgDim },\n { text: state.showSecrets ? 'shown' : 'masked', fg: COLORS.fg }\n ]);\n }\n status.content = state.message ?? '';\n}\n\nfunction refreshPrompt(\n promptBox: BoxRenderable,\n promptBody: BoxRenderable,\n promptHint: TextRenderable,\n renderer: CliRenderer,\n matrix: Matrix,\n state: State\n): void {\n const open = state.mode === 'prompt' && state.prompt !== null;\n promptBox.visible = open;\n if (!open || !state.prompt) return;\n\n promptBox.title = promptLabelText(state.prompt);\n // Hint depends on which prompt is active — only edit/add-value support\n // the apply-to-all + show-secrets shortcuts.\n const p = state.prompt;\n if (p.kind === 'edit' || p.kind === 'add-value') {\n promptHint.content =\n 'Enter · confirm Ctrl-A · apply to all ' +\n 'Ctrl-T · show/mask secrets Esc · cancel';\n } else {\n promptHint.content = 'Enter · confirm Esc · cancel';\n }\n\n // Body layout: full-width input on top, then a context table of every file\n // and its current value (read-only). For add-key / new-file there's no\n // context to show — just the input.\n removeAllChildren(promptBody);\n // Input is rendered as plain text so we can guarantee char + Esc handling\n // ourselves (opentui's InputRenderable swallows Esc as \"blur\").\n promptBody.add(\n new TextRenderable(renderer, {\n id: 'prompt-input-text',\n content: `▸ ${state.promptInput}▏`,\n fg: COLORS.fg,\n height: 1,\n wrapMode: 'none'\n })\n );\n // Show validation errors inside the modal so they aren't hidden behind\n // the dim overlay. state.message is set by commitPrompt when input is\n // invalid (and cleared on each fresh openPrompt).\n if (state.message) {\n promptBody.add(\n new TextRenderable(renderer, {\n id: 'prompt-error',\n content: `! ${state.message}`,\n fg: COLORS.missing,\n height: 1,\n marginTop: 1,\n wrapMode: 'none'\n })\n );\n }\n\n if (p.kind === 'edit' || p.kind === 'add-value') {\n const secret = isSecretKey(p.key) && !state.showSecrets;\n const nameWidth = Math.min(\n 26,\n Math.max(...matrix.files.map((f) => basename(f.path).length + 2))\n );\n\n promptBody.add(\n new TextRenderable(renderer, {\n id: 'prompt-table-header',\n content: 'Current values',\n fg: COLORS.fgSection,\n wrapMode: 'none',\n height: 1,\n marginTop: 1\n })\n );\n\n for (const file of matrix.files) {\n const isTarget = file === p.file;\n const row = new BoxRenderable(renderer, {\n id: `prompt-row-${file.path}`,\n flexDirection: 'row',\n height: 1,\n flexShrink: 0\n });\n row.add(\n new TextRenderable(renderer, {\n id: `prompt-row-${file.path}-name`,\n content: `${isTarget ? '▸' : ' '} ${basename(file.path)}`.padEnd(\n nameWidth\n ),\n fg: isTarget ? COLORS.fgBase : COLORS.fgDim,\n height: 1,\n wrapMode: 'none'\n })\n );\n const entry = findKvEntry(file, p.key);\n const current = entry ? formatValue(entry.value, secret) : '✗ missing';\n row.add(\n new TextRenderable(renderer, {\n id: `prompt-row-${file.path}-value`,\n content: current,\n fg: !entry ? COLORS.missing : isTarget ? COLORS.fg : COLORS.fgDim,\n height: 1,\n wrapMode: 'none'\n })\n );\n promptBody.add(row);\n }\n }\n}\n\nfunction promptLabelText(p: Prompt): string {\n switch (p.kind) {\n case 'edit':\n return ` Edit ${p.key} in ${basename(p.file.path)}:`;\n case 'add-key':\n return ` Add new key to ${basename(p.file.path)}:`;\n case 'add-value':\n return ` Value for ${p.key} in ${basename(p.file.path)}:`;\n case 'new-file':\n return ' New env file name (e.g. .env.local):';\n }\n}\n\n// --- Helpers ---\n\nfunction prefixSection(key: string): string | undefined {\n const idx = key.indexOf('_');\n if (idx <= 0) return undefined;\n return key.slice(0, idx);\n}\n\n/**\n * Sort by first-underscore-prefix while preserving the relative order each\n * prefix first appeared in. Keys without an underscore land in a trailing\n * \"Other\" group keeping their authored order.\n */\nfunction groupByPrefix(keys: string[]): string[] {\n const groups = new Map<string, string[]>();\n const order: string[] = [];\n const OTHER = '__other__';\n for (const k of keys) {\n const p = prefixSection(k) ?? OTHER;\n let bucket = groups.get(p);\n if (!bucket) {\n bucket = [];\n groups.set(p, bucket);\n if (p !== OTHER) order.push(p);\n }\n bucket.push(k);\n }\n if (groups.has(OTHER)) order.push(OTHER);\n return order.flatMap((p) => groups.get(p)!);\n}\n\n/**\n * Move row focus by `delta` while skipping section dividers that are not\n * collapsed. The user only needs to land on a divider when its section is\n * folded — that's the only context in which 'c' on the divider does work\n * the focused-key path doesn't already cover.\n */\nfunction stepRow(state: State, delta: number): number {\n const items = state.visibleItems;\n if (items.length === 0) return 0;\n const canFocus = (i: number) => {\n const it = items[i];\n if (!it) return false;\n if (it.kind === 'key') return true;\n // divider — focusable only when collapsed\n return state.collapsed.has(it.ref);\n };\n let i = state.rowIdx + delta;\n while (i >= 0 && i < items.length) {\n if (canFocus(i)) return i;\n i += delta;\n }\n // No focusable item further along — clamp to current.\n return state.rowIdx;\n}\n\nfunction orderedKeys(\n matrix: Matrix,\n state: State,\n sectionOf: (key: string) => string | undefined\n): string[] {\n void sectionOf;\n const filtered = matrix.keys.filter((k) => {\n if (!matchesFilter(k, state.filter)) return false;\n if (state.driftOnly && !keyDrifts(matrix, k)) return false;\n return true;\n });\n return state.grouping === 'prefix' ? groupByPrefix(filtered) : filtered;\n}\n\ninterface SectionStats {\n drift: number;\n missing: number;\n total: number;\n}\n\nfunction sectionMetadata(\n matrix: Matrix,\n sectionOf: (key: string) => string | undefined,\n state: State\n): Map<string, SectionStats> {\n const out = new Map<string, SectionStats>();\n for (const key of orderedKeys(matrix, state, sectionOf)) {\n const k = sectionOf(key) ?? '__other__';\n const bucket = out.get(k) ?? { drift: 0, missing: 0, total: 0 };\n bucket.total += 1;\n let drifts = false;\n let missing = false;\n for (const file of matrix.files) {\n if (file === matrix.base) continue;\n const s = matrix.cell(key, file).state;\n if (s === 'missing') missing = true;\n if (s === 'differs' || s === 'missing' || s === 'extra') drifts = true;\n }\n if (drifts) bucket.drift += 1;\n if (missing) bucket.missing += 1;\n out.set(k, bucket);\n }\n return out;\n}\n\nfunction buildValueCell(\n cell: { state: CellState; value: string | undefined },\n secret: boolean,\n width: number,\n focused: boolean,\n modified: boolean\n): CellSpec {\n const bg = focused ? COLORS.focusBg : undefined;\n const trailing = modified ? { char: '●', fg: COLORS.modified } : undefined;\n if (cell.state === 'missing') {\n return {\n text: 'missing',\n fg: COLORS.fgDim,\n width,\n bg,\n icon: { char: '✗', fg: COLORS.missing },\n trailing\n };\n }\n const value = cell.value ?? '';\n if (value !== '' && isPlaceholderValue(value)) {\n return {\n text: value,\n fg: COLORS.fg,\n width,\n bg,\n icon: { char: '⚠', fg: COLORS.placeholder },\n trailing\n };\n }\n const isEmpty = value === '' && !secret;\n const displayText = isEmpty ? '(empty)' : formatValue(value, secret);\n const displayFg = isEmpty ? COLORS.fgDim : COLORS.fg;\n if (cell.state === 'differs') {\n return {\n text: displayText,\n fg: displayFg,\n width,\n bg,\n icon: { char: '≠', fg: COLORS.differs },\n trailing\n };\n }\n if (cell.state === 'extra') {\n return {\n text: displayText,\n fg: displayFg,\n width,\n bg,\n icon: { char: '★', fg: COLORS.extra },\n trailing\n };\n }\n return { text: displayText, fg: displayFg, width, bg, trailing };\n}\n\nfunction keyDrifts(matrix: Matrix, key: string): boolean {\n for (const file of matrix.files) {\n if (file === matrix.base) continue;\n const s = matrix.cell(key, file).state;\n if (s === 'differs' || s === 'missing' || s === 'extra') return true;\n }\n return false;\n}\n\nfunction isValidEnvFileName(name: string): boolean {\n if (!name.startsWith('.env')) return false;\n if (name.includes('/') || name.includes('\\\\')) return false;\n if (name.endsWith('.swp') || name.endsWith('~') || name.endsWith('.bak'))\n return false;\n return true;\n}\n\nfunction createEmptyEnvFile(path: string): EnvFile {\n return {\n path,\n entries: [{ kind: 'comment', raw: `# ${basename(path)}` }],\n trailingNewline: true\n };\n}\n\nfunction appendKv(file: EnvFile, key: string, value: string): KvEntry {\n const entry: KvEntry = {\n kind: 'kv',\n key,\n rawValue: '',\n value,\n quoting: 'none',\n exportPrefix: false,\n inlineComment: '',\n raw: ''\n };\n rebuildKvLine(entry);\n file.entries.push(entry);\n // Round-trip semantics: serializeEnv joins with \\n and appends a trailing\n // newline if trailingNewline is set. Push alone gives us \"...\\nKEY=val\" when\n // trailingNewline=false, or \"...\\nKEY=val\\n\" when true. Either case is\n // sane; we make sure the file ends with a newline so editors don't complain.\n file.trailingNewline = true;\n return entry;\n}\n\nfunction buildHelpRow(\n renderer: CliRenderer,\n id: string,\n line: HelpLine\n): BoxRenderable {\n const row = new BoxRenderable(renderer, {\n id,\n flexDirection: 'row',\n height: 1,\n flexShrink: 0\n });\n if (line.kind === 'header') {\n row.add(\n new TextRenderable(renderer, {\n id: `${id}-t`,\n content: line.text,\n fg: COLORS.fgSection,\n wrapMode: 'none',\n height: 1\n })\n );\n } else if (line.kind === 'entry') {\n row.add(\n new TextRenderable(renderer, {\n id: `${id}-t`,\n content: line.text,\n fg: COLORS.fg,\n wrapMode: 'none',\n height: 1\n })\n );\n } else if (line.kind === 'legend') {\n row.add(\n new TextRenderable(renderer, {\n id: `${id}-sym`,\n content: ` ${line.symbol.padEnd(12)}`,\n fg: line.color,\n wrapMode: 'none',\n height: 1\n })\n );\n row.add(\n new TextRenderable(renderer, {\n id: `${id}-desc`,\n content: line.description,\n fg: COLORS.fgDim,\n wrapMode: 'none',\n height: 1\n })\n );\n }\n return row;\n}\n\nfunction buildSectionDivider(\n renderer: CliRenderer,\n id: string,\n name: string | undefined,\n width: number,\n meta: SectionStats & { collapsed: boolean; focused?: boolean }\n): BoxRenderable {\n // Multi-segment divider so colours can encode meaning:\n // gray ─── blue ▾ Name dim · stats gray ───\n // Drift counts go yellow; if any key in the section is missing in any\n // non-base file we surface that explicitly in red (\"✗ N missing\"), even\n // when the section also has plain differs.\n const baseName = name ?? '(other)';\n const indicator = meta.collapsed ? '▸' : '▾';\n // Icons + numbers carry colour (red for missing, yellow for drift). Name\n // and descriptive words render in the normal foreground; the \"/\" and\n // trailing whitespace stay dim.\n type Seg = { text: string; fg: RGBA };\n const segs: Seg[] = [\n { text: ` ${indicator} `, fg: COLORS.fgDim },\n { text: baseName, fg: COLORS.fg },\n { text: ' ', fg: COLORS.fgDim }\n ];\n if (meta.missing > 0) {\n segs.push({ text: '✗ ', fg: COLORS.missing });\n segs.push({ text: `${meta.missing}`, fg: COLORS.missing });\n segs.push({ text: ' missing ', fg: COLORS.fg });\n }\n if (meta.drift > 0) {\n segs.push({ text: '≠ ', fg: COLORS.differs });\n segs.push({ text: `${meta.drift}`, fg: COLORS.differs });\n segs.push({ text: '/', fg: COLORS.fgDim });\n segs.push({ text: `${meta.total}`, fg: COLORS.differs });\n segs.push({ text: ' drift ', fg: COLORS.fg });\n }\n if (meta.missing === 0 && meta.drift === 0) {\n segs.push({ text: `${meta.total}`, fg: COLORS.fgDim });\n segs.push({ text: ' keys ', fg: COLORS.fg });\n }\n segs.push({ text: ' ', fg: COLORS.fgDim });\n\n const labelLength = segs.reduce((sum, s) => sum + s.text.length, 0);\n const rule = '─';\n const visible = Math.max(0, width - 2);\n const beforeLen = Math.max(2, Math.floor((visible - labelLength) / 2));\n const afterLen = Math.max(0, visible - beforeLen - labelLength);\n\n const box = new BoxRenderable(renderer, {\n id,\n flexDirection: 'row',\n flexShrink: 0,\n height: 1,\n paddingX: 1,\n ...(meta.focused ? { backgroundColor: COLORS.focusBg } : {})\n });\n box.add(\n new TextRenderable(renderer, {\n id: `${id}-lead`,\n content: rule.repeat(beforeLen),\n fg: COLORS.fgDim,\n height: 1,\n wrapMode: 'none'\n })\n );\n segs.forEach((seg, i) => {\n box.add(\n new TextRenderable(renderer, {\n id: `${id}-seg-${i}`,\n content: seg.text,\n fg: seg.fg,\n height: 1,\n wrapMode: 'none'\n })\n );\n });\n box.add(\n new TextRenderable(renderer, {\n id: `${id}-trail`,\n content: rule.repeat(afterLen),\n fg: COLORS.fgDim,\n height: 1,\n wrapMode: 'none'\n })\n );\n return box;\n}\n\ninterface CellSpec {\n text: string;\n fg: RGBA;\n width: number;\n bg?: RGBA;\n // Optional coloured prefix — rendered in its own Text span so only the\n // icon carries the state colour, the text stays neutral.\n icon?: { char: string; fg: RGBA };\n // Optional coloured marker that sits at the right edge of the cell.\n // Used for the modified-since-load indicator.\n trailing?: { char: string; fg: RGBA };\n}\n\nfunction buildRow(\n renderer: CliRenderer,\n idPrefix: string,\n cells: CellSpec[]\n): BoxRenderable {\n const row = new BoxRenderable(renderer, {\n id: idPrefix,\n flexDirection: 'row',\n flexShrink: 0,\n height: 1\n });\n cells.forEach((cell, i) => {\n const cellOpts: ConstructorParameters<typeof BoxRenderable>[1] = {\n id: `${idPrefix}-c${i}`,\n width: cell.width,\n height: 1,\n flexDirection: 'row',\n flexShrink: 0,\n paddingX: CELL_PAD_X\n };\n if (cell.bg) cellOpts.backgroundColor = cell.bg;\n const cellBox = new BoxRenderable(renderer, cellOpts);\n const innerWidth = Math.max(0, cell.width - CELL_PAD_X * 2);\n const iconLen = cell.icon ? cell.icon.char.length + 1 : 0;\n const trailingLen = cell.trailing ? cell.trailing.char.length + 1 : 0;\n const textWidth = Math.max(0, innerWidth - iconLen - trailingLen);\n if (cell.icon) {\n cellBox.add(\n new TextRenderable(renderer, {\n id: `${idPrefix}-c${i}-icon`,\n content: `${cell.icon.char} `,\n fg: cell.icon.fg,\n height: 1,\n wrapMode: 'none'\n })\n );\n }\n cellBox.add(\n new TextRenderable(renderer, {\n id: `${idPrefix}-c${i}-t`,\n content: truncate(cell.text, textWidth),\n fg: cell.fg,\n height: 1,\n flexGrow: 1,\n wrapMode: 'none'\n })\n );\n if (cell.trailing) {\n cellBox.add(\n new TextRenderable(renderer, {\n id: `${idPrefix}-c${i}-trail`,\n content: ` ${cell.trailing.char}`,\n fg: cell.trailing.fg,\n height: 1,\n wrapMode: 'none'\n })\n );\n }\n row.add(cellBox);\n });\n return row;\n}\n\nfunction removeAllChildren(node: BoxRenderable): void {\n const ids = node.getChildren().map((c) => c.id);\n for (const id of ids) node.remove(id);\n}\n\nfunction matrixTitle(matrix: Matrix, state: State): string {\n const visible = state.visibleKeys.length;\n const total = matrix.keys.length;\n const parts: string[] = [`${total} keys`];\n if (state.driftOnly) parts.push(`drift ${visible}/${total}`);\n else if (state.filter && visible !== total) {\n parts.push(`\"${state.filter}\" ${visible}/${total}`);\n }\n return ` Matrix · ${parts.join(' · ')} `;\n}\n\nfunction formatValue(value: string | undefined, secret: boolean): string {\n if (value === undefined) return '';\n if (secret) return maskValue(value);\n return value;\n}\n\nfunction matchesFilter(key: string, filter: string): boolean {\n if (!filter) return true;\n return key.toLowerCase().includes(filter.toLowerCase());\n}\n\nfunction truncate(text: string, width: number): string {\n if (width <= 0) return '';\n if (text.length <= width) return text;\n if (width <= 1) return '…';\n return `${text.slice(0, width - 1)}…`;\n}\n\nfunction findKvEntry(file: EnvFile, key: string): KvEntry | undefined {\n for (const e of file.entries) {\n if (e.kind === 'kv' && e.key === key) return e;\n }\n return undefined;\n}\n"],"names":["i"],"mappings":";;;;;AAoCA,MAAM,aAAa;AA6CnB,MAAM,SAAS;AAAA,EACb,IAAI,KAAK,QAAQ,SAAS;AAAA,EAC1B,OAAO,KAAK,QAAQ,SAAS;AAAA,EAC7B,UAAU,KAAK,QAAQ,SAAS;AAAA;AAAA;AAAA,EAGhC,QAAQ,KAAK,QAAQ,SAAS;AAAA,EAC9B,WAAW,KAAK,QAAQ,SAAS;AAAA;AAAA;AAAA,EAGjC,SAAS,KAAK,QAAQ,SAAS;AAAA,EAC/B,OAAO,KAAK,QAAQ,SAAS;AAAA,EAC7B,aAAa,KAAK,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA,EAInC,UAAU,KAAK,QAAQ,SAAS;AAAA,EAChC,SAAS,KAAK,QAAQ,SAAS;AAAA;AAAA,EAE/B,SAAS,KAAK,QAAQ,SAAS;AAAA,EAC/B,SAAS,KAAK,QAAQ,SAAS;AACjC;AAEA,MAAM,iBACJ;AAEF,SAAS,mBAAmB,OAAwB;AAClD,QAAM,IAAI,MAAM,KAAA;AAChB,MAAI,EAAE,WAAW,EAAG,QAAO;AAC3B,SAAO,eAAe,KAAK,CAAC;AAC9B;AAEA,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AACtB,MAAM,UAAU;AAChB,MAAM,aAAa;AACnB,MAAM,SAAS;AAQf,SAAS,iBAA6B;AACpC,SAAO;AAAA,IACL,EAAE,MAAM,UAAU,MAAM,QAAA;AAAA,IACxB;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,oBAAA;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,wCAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,yCAAA;AAAA,IACvB,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,gBAAA;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,qCAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,4CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,kDAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,qCAAA;AAAA,IACvB,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,UAAA;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,8CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,8CAAA;AAAA,IACvB;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER,EAAE,MAAM,SAAS,MAAM,8CAAA;AAAA,IACvB;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER,EAAE,MAAM,SAAS,MAAM,gDAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,4CAAA;AAAA,IACvB;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,IAER,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,OAAA;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,kCAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,4CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,+CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,gDAAA;AAAA,IACvB,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,cAAA;AAAA,IACxB,EAAE,MAAM,SAAS,MAAM,0CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,4CAAA;AAAA,IACvB,EAAE,MAAM,SAAS,MAAM,iCAAA;AAAA,IACvB,EAAE,MAAM,QAAA;AAAA,IACR,EAAE,MAAM,UAAU,MAAM,aAAA;AAAA,IACxB;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,KAAK,QAAQ,SAAS;AAAA,MAC7B,aAAa;AAAA,IAAA;AAAA,EACf;AAEJ;AAEA,eAAsB,aAAa,eAAsC;AACvE,QAAM,WAAW,MAAM,kBAAkB,EAAE,UAAU,MAAM;AAG3D,QAAM,WAAW,cAAc,MAAM,MAAA;AACrC,MAAI,cAAc,cAAc;AAChC,MAAI,SAAS;AAIb,QAAM,aAAa,cAAc,KAAK;AAAA,IACpC,CAAC,MAAM,cAAc,UAAU,CAAC,MAAM;AAAA,EAAA;AAExC,QAAM,QAAe;AAAA,IACnB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,2BAAW,IAAA;AAAA,IACX,aAAa,OAAO,KAAK,MAAA;AAAA,IACzB,cAAc,CAAA;AAAA,IACd,SAAS;AAAA,IACT,WAAW;AAAA,IACX,aAAa;AAAA,IACb,UAAU,aAAa,WAAW;AAAA,IAClC,UAAU;AAAA,IACV,MAAM,CAAA;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS,IAAI,IAAI,QAAQ;AAAA,IACzB,aAAa;AAAA,IACb,+BAAe,IAAA;AAAA,IACf,8BAAc,IAAA;AAAA,IACd,aAAa;AAAA,EAAA;AAGf,QAAM,UAAU,CAAC,KAAa,SAAkB,GAAG,GAAG,IAAI,KAAK,IAAI;AACnE,QAAM,eAAe,CAAC,KAAa,SACjC,MAAM,SAAS,IAAI,QAAQ,KAAK,IAAI,CAAC;AAEvC,QAAM,uBAAuB,CAAC,SAC5B,QAAQ;AAEV,QAAM,WAAW,CAAC,UAAqB;AACrC,UAAM,KAAK,KAAK,KAAK;AACrB,QAAI,MAAM,KAAK,SAAS,WAAY,OAAM,KAAK,MAAA;AAAA,EACjD;AAGA,QAAM,OAAO,IAAI,cAAc,UAAU;AAAA,IACvC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA,CACT;AACD,WAAS,KAAK,IAAI,IAAI;AAEtB,QAAM,OAAO,IAAI,cAAc,UAAU;AAAA,IACvC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,EAAA,CACX;AACD,OAAK,IAAI,IAAI;AAEb,QAAM,UAAU,IAAI,cAAc,UAAU;AAAA,IAC1C,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,eAAe;AAAA,IACf,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,EAAA,CACX;AACD,OAAK,IAAI,OAAO;AAEhB,QAAM,YAAY,IAAI,cAAc,UAAU;AAAA,IAC5C,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,eAAe;AAAA,IACf,UAAU;AAAA,IACV,UAAU;AAAA;AAAA;AAAA,IAGV,eAAe;AAAA,EAAA,CAChB;AACD,OAAK,IAAI,SAAS;AAGlB,QAAM,aAAa,IAAI,cAAc,UAAU;AAAA,IAC7C,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,YAAY;AAAA;AAAA;AAAA,IAGZ,cAAc;AAAA,EAAA,CACf;AACD,YAAU,IAAI,UAAU;AAExB,QAAM,YAAY,IAAI,oBAAoB,UAAU;AAAA,IAClD,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA;AAAA;AAAA,IAGT,iBAAiB,EAAE,cAAc,GAAG,eAAe,EAAA;AAAA,IACnD,gBAAgB,EAAE,eAAe,UAAU,QAAQ,QAAA;AAAA,EAAQ,CAC5D;AACD,YAAU,IAAI,SAAS;AAEvB,QAAM,SAAS,IAAI,cAAc,UAAU;AAAA,IACzC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,UAAU;AAAA,EAAA,CACX;AACD,OAAK,IAAI,MAAM;AAEf,QAAM,QAAQ,IAAI,cAAc,UAAU;AAAA,IACxC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,YAAY;AAAA,EAAA,CACb;AACD,SAAO,IAAI,KAAK;AAEhB,QAAM,QAAQ,IAAI,cAAc,UAAU;AAAA,IACxC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,YAAY;AAAA,EAAA,CACb;AACD,SAAO,IAAI,KAAK;AAEhB,QAAM,SAAS,IAAI,eAAe,UAAU;AAAA,IAC1C,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,IAAI,OAAO;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,EAAA,CACT;AACD,SAAO,IAAI,MAAM;AAKjB,QAAM,YAAY,IAAI,cAAc,UAAU;AAAA,IAC5C,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS;AAAA,IACT,iBAAiB,KAAK,QAAQ,SAAS;AAAA,IACvC,eAAe;AAAA,EAAA,CAChB;AACD,QAAM,cAAc,IAAI,eAAe,UAAU;AAAA,IAC/C,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,IAAI,OAAO;AAAA,IACX,QAAQ;AAAA,IACR,UAAU;AAAA,EAAA,CACX;AACD,QAAM,eAAe,IAAI,eAAe,UAAU;AAAA,IAChD,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,IAAI,OAAO;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,EAAA,CACX;AACD,QAAM,aAAa,IAAI,eAAe,UAAU;AAAA,IAC9C,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,IAAI,OAAO;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,EAAA,CACX;AACD,YAAU,IAAI,WAAW;AACzB,YAAU,IAAI,YAAY;AAC1B,YAAU,IAAI,UAAU;AACxB,WAAS,KAAK,IAAI,SAAS;AAG3B,QAAM,YAAY,IAAI,cAAc,UAAU;AAAA,IAC5C,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS;AAAA,IACT,iBAAiB,KAAK,QAAQ,SAAS;AAAA,IACvC,eAAe;AAAA,EAAA,CAChB;AAGD,QAAM,aAAa,IAAI,cAAc,UAAU;AAAA,IAC7C,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,EAAA,CACX;AACD,QAAM,aAAa,IAAI,eAAe,UAAU;AAAA,IAC9C,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,IAAI,OAAO;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,UAAU;AAAA,EAAA,CACX;AACD,YAAU,IAAI,UAAU;AACxB,YAAU,IAAI,UAAU;AACxB,WAAS,KAAK,IAAI,SAAS;AAK3B,QAAM,aAAa,IAAI,cAAc,UAAU;AAAA,IAC7C,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,iBAAiB,KAAK,QAAQ,SAAS;AAAA,IACvC,SAAS;AAAA,IACT,SAAS;AAAA,EAAA,CACV;AACD,WAAS,KAAK,IAAI,UAAU;AAQ5B,QAAM,UAAU,IAAI,cAAc,UAAU;AAAA,IAC1C,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS;AAAA,IACT,iBAAiB,KAAK,QAAQ,SAAS;AAAA,IACvC,eAAe;AAAA,EAAA,CAChB;AACD,WAAS,KAAK,IAAI,OAAO;AAGzB,QAAM,YAAY,CAAC,QACjB,MAAM,aAAa,WAAW,OAAO,UAAU,GAAG,IAAI,cAAc,GAAG;AAEzE,QAAM,uBAAuB,MAAM;AAMjC,UAAM,cAAwB,CAAA;AAC9B,UAAM,QAAsB,CAAA;AAC5B,UAAM,aAAa,YAAY,QAAQ,KAAgB;AACvD,UAAM,2BAAW,IAAA;AACjB,UAAM,aAAa,MAAM,aAAa,MAAM,MAAM,GAAG;AACrD,eAAW,KAAK,YAAY;AAC1B,UAAI,CAAC,cAAc,GAAG,MAAM,MAAM,EAAG;AACrC,UAAI,MAAM,aAAa,CAAC,UAAU,QAAQ,CAAC,EAAG;AAC9C,YAAM,MAAM,UAAU,CAAC;AACvB,YAAM,SAAS,qBAAqB,GAAG;AACvC,UAAI,CAAC,KAAK,IAAI,MAAM,GAAG;AACrB,aAAK,IAAI,MAAM;AACf,cAAM,KAAK,EAAE,MAAM,WAAW,KAAK,QAAQ;AAAA,MAC7C;AACA,UAAI,MAAM,UAAU,IAAI,MAAM,EAAG;AACjC,YAAM,KAAK,EAAE,MAAM,OAAO,KAAK,GAAG;AAClC,kBAAY,KAAK,CAAC;AAAA,IACpB;AACA,UAAM,cAAc;AACpB,UAAM,eAAe;AAErB,QAAI,YAAY;AACd,YAAM,IAAI,MAAM,UAAU,CAAC,OAAO,GAAG,QAAQ,UAAU;AACvD,UAAI,KAAK,EAAG,OAAM,SAAS;AAAA,IAC7B;AACA,QAAI,MAAM,UAAU,MAAM,QAAQ;AAChC,YAAM,SAAS,KAAK,IAAI,GAAG,MAAM,SAAS,CAAC;AAAA,IAC7C;AAEA,QACE,MAAM,MAAM,MAAM,GAAG,SAAS,aAC9B,CAAC,MAAM,UAAU,IAAI,MAAM,MAAM,MAAM,EAAG,GAAG,GAC7C;AACA,YAAM,OAAO,QAAQ,OAAO,CAAC;AAC7B,YAAM,OAAO,QAAQ,OAAO,EAAE;AAC9B,YAAM,SAAS,SAAS,MAAM,SAAS,OAAO;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,gBAAgB,MAAM;AAC1B,UAAM,cAAc,SAAS,OAAO,CAAC,MAAM,MAAM,QAAQ,IAAI,CAAC,CAAC;AAC/D,QAAI,CAAC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAEnC,YAAM,OAAO,YAAY,CAAC;AAC1B,UAAI,KAAM,eAAc;AAAA,IAC1B;AACA,aAAS,YAAY,aAAa,WAAW;AAC7C,QAAI,MAAM,UAAU,OAAO,MAAM,QAAQ;AACvC,YAAM,SAAS,KAAK,IAAI,GAAG,OAAO,MAAM,SAAS,CAAC;AAAA,IACpD;AACA,QAAI,MAAM,cAAc,SAAS,QAAQ;AACvC,YAAM,aAAa,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC;AAAA,IACpD;AACA,yBAAA;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,QAAgB;AAChC,UAAM,MAAM,MAAM,YAAY,QAAQ,GAAG;AACzC,QAAI,OAAO,EAAG,OAAM,SAAS;AAAA,EAC/B;AAEA,QAAM,uBAAuB,MAAc;AAKzC,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,SAAS,gBAAgB,gBAAgB,IAAI;AAAA,IAAA;AAE/C,UAAM,OAAO,OAAO,MAAM,SACtB,KAAK,MAAM,YAAY,OAAO,MAAM,MAAM,IAC1C;AACJ,WAAO,KAAK,IAAI,eAAe,IAAI;AAAA,EACrC;AAEA,QAAM,aAAa,MAAM;AACvB,UAAM,gBAAgB,qBAAA;AACtB,mBAAe,SAAS,UAAU,QAAQ,UAAU,KAAK;AACzD;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,kBAAc,OAAO,OAAO,QAAQ,UAAU,KAAK;AACnD,kBAAc,WAAW,YAAY,YAAY,UAAU,QAAQ,KAAK;AACxE,gBAAY,SAAS,UAAU,KAAK;AACpC,kBAAc,WAAW,aAAa,cAAc,QAAQ,KAAK;AACjE,eAAW,UACT,MAAM,YAAY,MAAM,SAAS,YAAY,MAAM,SAAS;AAAA,EAChE;AAOA,MAAI,mBAAmB;AACvB,QAAM,UAAU,MAAM;AACpB,QAAI,iBAAkB;AACtB,uBAAmB;AACnB,mBAAe,MAAM;AACnB,yBAAmB;AACnB,iBAAA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,uBAAA;AACA,aAAA;AAGA,SAAO,IAAI,QAAc,CAAC,YAAY;AACpC,UAAM,UAAU,MAAM;AACpB,eAAS,kBAAkB,YAAY,YAAY,KAAK;AACxD,eAAS,UAAA;AACT,cAAA;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,QAAgB,QAAQ,IAAI,cAAc,OAAO;AAEnE,YAAM,SAAS;AACf,YAAM,OAAO;AACb,YAAM,UAAU;AAChB,YAAM,cAAc;AACpB,cAAA;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,MAAqB,SAAS;AACjD,YAAM,SAAS;AACf,YAAM,OAAO;AACb,YAAM,cAAc;AACpB,YAAM,UAAU;AAChB,cAAA;AAAA,IACF;AAEA,UAAM,aAAa,MAAqB;AACtC,YAAM,OAAO,MAAM,aAAa,MAAM,MAAM;AAC5C,aAAO,QAAQ,KAAK,SAAS,QAAQ,KAAK,MAAM;AAAA,IAClD;AAEA,UAAM,YAAY,MAAM;AACtB,YAAM,MAAM,WAAA;AACZ,YAAM,OAAO,OAAO,MAAM,MAAM,MAAM;AACtC,UAAI,CAAC,OAAO,CAAC,MAAM;AACjB,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AAGA,YAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,iBAAW,EAAE,MAAM,QAAQ,KAAK,QAAQ,OAAO,SAAS,IAAI,OAAO;AAAA,IACrE;AAEA,UAAM,WAAW,MAAM;AACrB,YAAM,OAAO,OAAO,MAAM,MAAM,MAAM;AACtC,UAAI,CAAC,KAAM;AACX,iBAAW,EAAE,MAAM,WAAW,KAAA,GAAQ,IAAI,SAAS;AAAA,IACrD;AAEA,UAAM,eAAe,MAAM;AACzB,iBAAW,EAAE,MAAM,WAAA,GAAc,IAAI,YAAY;AAAA,IACnD;AAEA,UAAM,cAAc,MAAM;AACxB,YAAM,MAAM,WAAA;AACZ,YAAM,OAAO,OAAO,MAAM,MAAM,MAAM;AACtC,UAAI,CAAC,OAAO,CAAC,MAAM;AACjB,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AACA,YAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,UAAI,CAAC,OAAO;AACV,cAAM,UAAU,GAAG,GAAG,sBAAsB,SAAS,KAAK,IAAI,CAAC;AAC/D,gBAAA;AACA;AAAA,MACF;AACA,YAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK;AACtC,UAAI,OAAO,GAAG;AACZ,iBAAS,EAAE,MAAM,aAAa,MAAM,OAAO,KAAK;AAChD,aAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,MAC5B;AACA,YAAM,MAAM,IAAI,IAAI;AACpB,mBAAa,KAAK,IAAI;AACtB,oBAAA;AACA,YAAM,UAAU,WAAW,GAAG,SAAS,SAAS,KAAK,IAAI,CAAC;AAC1D,cAAA;AAAA,IACF;AAEA,UAAM,gBAAgB,MAAM;AAC1B,YAAM,OAAO,SAAS,MAAM,UAAU;AACtC,UAAI,CAAC,KAAM;AACX,UAAI,MAAM,QAAQ,IAAI,IAAI,GAAG;AAC3B,YAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,gBAAM,UAAU;AAChB,kBAAA;AACA;AAAA,QACF;AACA,cAAM,QAAQ,OAAO,IAAI;AACzB,cAAM,UAAU,UAAU,SAAS,KAAK,IAAI,CAAC;AAAA,MAC/C,OAAO;AACL,cAAM,QAAQ,IAAI,IAAI;AACtB,cAAM,UAAU,WAAW,SAAS,KAAK,IAAI,CAAC;AAAA,MAChD;AACA,oBAAA;AACA,cAAA;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,YAAM,OAAO,SAAS,MAAM,UAAU;AACtC,UAAI,CAAC,KAAM;AACX,UAAI,SAAS,aAAa;AACxB,cAAM,UAAU,GAAG,SAAS,KAAK,IAAI,CAAC;AACtC,gBAAA;AACA;AAAA,MACF;AACA,YAAM,cAAc,CAAC,MAAM,QAAQ,IAAI,IAAI;AAC3C,UAAI,YAAa,OAAM,QAAQ,IAAI,IAAI;AACvC,oBAAc;AACd,oBAAA;AACA,YAAM,UAAU,cACZ,GAAG,SAAS,KAAK,IAAI,CAAC,mCACtB,GAAG,SAAS,KAAK,IAAI,CAAC;AAC1B,cAAA;AAAA,IACF;AAEA,UAAM,kBAAkB,CAAC,KAAa,UAA0B;AAI9D,UAAI,UAAU;AACd,iBAAW,QAAQ,OAAO,OAAO;AAC/B,cAAM,WAAW,YAAY,MAAM,GAAG;AACtC,YAAI,UAAU;AACZ,cAAI,SAAS,UAAU,MAAO;AAC9B,mBAAS;AAAA,YACP,MAAM;AAAA,YACN;AAAA,YACA,OAAO;AAAA,YACP,WAAW,SAAS;AAAA,YACpB,SAAS,SAAS;AAAA,UAAA,CACnB;AACD,mBAAS,QAAQ;AACjB,wBAAc,QAAQ;AAAA,QACxB,OAAO;AACL,gBAAM,QAAQ,SAAS,MAAM,KAAK,KAAK;AACvC,mBAAS,EAAE,MAAM,UAAU,MAAM,OAAO,OAAO;AAAA,QACjD;AACA,cAAM,MAAM,IAAI,IAAI;AACpB,qBAAa,KAAK,IAAI;AACtB;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,MAAM;AACtB,YAAM,MAAM,WAAA;AACZ,YAAM,OAAO,OAAO,MAAM,MAAM,MAAM;AACtC,UAAI,CAAC,OAAO,CAAC,MAAM;AACjB,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AACA,YAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,UAAI,CAAC,OAAO;AACV,cAAM,UAAU,GAAG,GAAG,oBAAoB,SAAS,KAAK,IAAI,CAAC;AAC7D,gBAAA;AACA;AAAA,MACF;AACA,YAAM,UAAU,gBAAgB,KAAK,MAAM,KAAK;AAChD,oBAAA;AACA,YAAM,UACJ,UAAU,IACN,UAAU,GAAG,OAAO,OAAO,8BAC3B,GAAG,GAAG;AACZ,cAAA;AAAA,IACF;AAEA,UAAM,OAAO,MAAM;AACjB,YAAM,OAAO,MAAM,KAAK,IAAA;AACxB,UAAI,CAAC,MAAM;AACT,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AACA,cAAQ,KAAK,MAAA;AAAA,QACX,KAAK;AACH,eAAK,MAAM,QAAQ,KAAK;AACxB,eAAK,MAAM,MAAM,KAAK;AACtB,gBAAM,MAAM,IAAI,KAAK,IAAI;AACzB,gBAAM,UAAU,iBAAiB,KAAK,MAAM,GAAG,OAAO,SAAS,KAAK,KAAK,IAAI,CAAC;AAC9E;AAAA,QACF,KAAK,UAAU;AACb,gBAAM,IAAI,KAAK,KAAK,QAAQ,QAAQ,KAAK,KAAK;AAC9C,cAAI,KAAK,EAAG,MAAK,KAAK,QAAQ,OAAO,GAAG,CAAC;AACzC,gBAAM,MAAM,IAAI,KAAK,IAAI;AACzB,gBAAM,UAAU,gBAAgB,KAAK,MAAM,GAAG,OAAO,SAAS,KAAK,KAAK,IAAI,CAAC;AAC7E;AAAA,QACF;AAAA,QACA,KAAK;AACH,eAAK,KAAK,QAAQ,OAAO,KAAK,KAAK,GAAG,KAAK,KAAK;AAChD,gBAAM,MAAM,IAAI,KAAK,IAAI;AACzB,gBAAM,UAAU,mBAAmB,KAAK,MAAM,GAAG,OAAO,SAAS,KAAK,KAAK,IAAI,CAAC;AAChF;AAAA,MAAA;AAEJ,oBAAA;AACA,cAAA;AAAA,IACF;AAEA,UAAM,eAAe,MAAM;AACzB,UAAI,CAAC,MAAM,OAAQ;AACnB,YAAM,IAAI,MAAM;AAChB,YAAM,MAAM,MAAM;AAElB,UAAI,EAAE,SAAS,QAAQ;AACrB,cAAM,WAAW,YAAY,EAAE,MAAM,EAAE,GAAG;AAC1C,YAAI,UAAU;AACZ,mBAAS;AAAA,YACP,MAAM;AAAA,YACN,MAAM,EAAE;AAAA,YACR,OAAO;AAAA,YACP,WAAW,SAAS;AAAA,YACpB,SAAS,SAAS;AAAA,UAAA,CACnB;AACD,mBAAS,QAAQ;AACjB,wBAAc,QAAQ;AACtB,gBAAM,MAAM,IAAI,EAAE,IAAI;AACtB,uBAAa,EAAE,KAAK,EAAE,IAAI;AAC1B,wBAAA;AACA;AAAA,YACE,UAAU,EAAE,GAAG,OAAO,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,UAAA;AAAA,QAE/C,OAAO;AAEL,gBAAM,QAAQ,SAAS,EAAE,MAAM,EAAE,KAAK,GAAG;AACzC,mBAAS,EAAE,MAAM,UAAU,MAAM,EAAE,MAAM,OAAO,OAAO;AACvD,gBAAM,MAAM,IAAI,EAAE,IAAI;AACtB,uBAAa,EAAE,KAAK,EAAE,IAAI;AAC1B,wBAAA;AACA;AAAA,YACE,SAAS,EAAE,GAAG,OAAO,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,UAAA;AAAA,QAE9C;AACA;AAAA,MACF;AAEA,UAAI,EAAE,SAAS,WAAW;AACxB,cAAM,MAAM,IAAI,KAAA;AAChB,YAAI,CAAC,OAAO,KAAK,GAAG,GAAG;AACrB,gBAAM,UAAU,gBAAgB,GAAG,iBAAiB,OAAO,MAAM;AACjE,kBAAA;AACA;AAAA,QACF;AACA,YAAI,YAAY,EAAE,MAAM,GAAG,GAAG;AAC5B,gBAAM,UAAU,GAAG,GAAG,sBAAsB,SAAS,EAAE,KAAK,IAAI,CAAC;AACjE,kBAAA;AACA;AAAA,QACF;AACA,mBAAW,EAAE,MAAM,aAAa,KAAK,MAAM,EAAE,KAAA,GAAQ,IAAI,OAAO;AAChE;AAAA,MACF;AAEA,UAAI,EAAE,SAAS,aAAa;AAC1B,cAAM,QAAQ,SAAS,EAAE,MAAM,EAAE,KAAK,GAAG;AACzC,iBAAS,EAAE,MAAM,UAAU,MAAM,EAAE,MAAM,OAAO,OAAO;AACvD,cAAM,MAAM,IAAI,EAAE,IAAI;AACtB,qBAAa,EAAE,KAAK,EAAE,IAAI;AAC1B,sBAAA;AACA,iBAAS,EAAE,GAAG;AACd,cAAM,SAAS,OAAO,MAAM,QAAQ,EAAE,IAAI;AAC1C;AAAA,UACE,SAAS,EAAE,GAAG,OAAO,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,QAAA;AAE5C;AAAA,MACF;AAEA,UAAI,EAAE,SAAS,YAAY;AACzB,cAAM,OAAO,IAAI,KAAA;AACjB,YAAI,CAAC,mBAAmB,IAAI,GAAG;AAC7B,gBAAM,UACJ,KAAK,WAAW,IACZ,8BACA,CAAC,KAAK,WAAW,MAAM,IACrB,yCAAyC,IAAI,QAC7C,IAAI,IAAI;AAChB,kBAAA;AACA;AAAA,QACF;AACA,cAAM,UAAU,KAAK,QAAQ,YAAY,IAAI,GAAG,IAAI;AACpD,YAAI,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,GAAG;AAC5C,gBAAM,UAAU,GAAG,IAAI;AACvB,kBAAA;AACA;AAAA,QACF;AACA,cAAM,UAAU,mBAAmB,OAAO;AAC1C,iBAAS,KAAK,OAAO;AACrB,cAAM,QAAQ,IAAI,OAAO;AACzB,cAAM,MAAM,IAAI,OAAO;AACvB,sBAAA;AACA,cAAM,SAAS,OAAO,MAAM,QAAQ,OAAO;AAC3C,oBAAY,WAAW,IAAI,4BAA4B;AACvD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,eAAe,MAAM;AACzB,kBAAY,YAAY;AAAA,IAC1B;AAEA,UAAM,YAAY,YAAY;AAC5B,UAAI,MAAM,MAAM,SAAS,GAAG;AAC1B,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,MAAM;AAC1B,YAAM,SAAmB,CAAA;AACzB,iBAAW,QAAQ,MAAM,OAAO;AAC9B,YAAI;AACF,gBAAM,UAAU,KAAK,MAAM,aAAa,IAAI,GAAG,MAAM;AAAA,QACvD,SAAS,KAAK;AACZ,iBAAO;AAAA,YACL,GAAG,SAAS,KAAK,IAAI,CAAC,KAAM,IAAc,WAAW,OAAO,GAAG,CAAC;AAAA,UAAA;AAAA,QAEpE;AAAA,MACF;AACA,UAAI,OAAO,WAAW,GAAG;AACvB,cAAM,MAAM,MAAA;AACZ,cAAM,SAAS,MAAA;AACf,cAAM,UAAU,SAAS,KAAK,QAAQ,UAAU,IAAI,KAAK,GAAG;AAAA,MAC9D,OAAO;AACL,cAAM,UAAU,gBAAgB,OAAO,KAAK,IAAI,CAAC;AAAA,MACnD;AACA,cAAA;AAAA,IACF;AAEA,UAAM,QAAQ,CAAC,QAMT;AACJ,UAAI,MAAM,UAAU;AAClB,YACE,IAAI,SAAS,YACb,IAAI,aAAa,OACjB,IAAI,aAAa,OACjB,IAAI,SAAS,KACb;AACA,gBAAM,WAAW;AACjB,kBAAA;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,MAAM,SAAS,UAAU;AAC3B,YAAI,IAAI,SAAS,UAAU;AACzB,uBAAA;AACA;AAAA,QACF;AACA,YAAI,IAAI,SAAS,UAAU;AACzB,uBAAA;AACA;AAAA,QACF;AACA,YAAI,IAAI,SAAS,aAAa;AAC5B,cAAI,MAAM,YAAY,SAAS,GAAG;AAChC,kBAAM,cAAc,MAAM,YAAY,MAAM,GAAG,EAAE;AACjD,oBAAA;AAAA,UACF;AACA;AAAA,QACF;AACA,YAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,gBAAM,cAAc,CAAC,MAAM;AAC3B,kBAAA;AACA;AAAA,QACF;AACA,YAAI,IAAI,QAAQ,IAAI,SAAS,OAAO,MAAM,QAAQ;AAChD,gBAAM,IAAI,MAAM;AAChB,cAAI,EAAE,SAAS,UAAU,EAAE,SAAS,aAAa;AAC/C,kBAAM,UAAU,gBAAgB,EAAE,KAAK,MAAM,WAAW;AACxD,0BAAA;AACA;AAAA,cACE,UAAU,IACN,OAAO,EAAE,GAAG,OAAO,OAAO,8BAC1B,GAAG,EAAE,GAAG;AAAA,YAAA;AAAA,UAEhB;AACA;AAAA,QACF;AACA,cAAM,MAAM,IAAI,YAAY;AAC5B,YAAI,CAAC,IAAI,QAAQ,IAAI,WAAW,KAAK,OAAO,OAAO,QAAQ,KAAQ;AACjE,gBAAM,eAAe;AACrB,kBAAA;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,MAAM,SAAS,UAAU;AAC3B,YAAI,IAAI,SAAS,UAAU;AACzB,gBAAM,SAAS;AACf,gBAAM,OAAO;AACb,+BAAA;AACA,kBAAA;AACA;AAAA,QACF;AACA,YAAI,IAAI,SAAS,UAAU;AACzB,gBAAM,OAAO;AACb,kBAAA;AACA;AAAA,QACF;AACA,YAAI,IAAI,SAAS,aAAa;AAC5B,cAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,kBAAM,SAAS,MAAM,OAAO,MAAM,GAAG,EAAE;AACvC,iCAAA;AACA,oBAAA;AAAA,UACF;AACA;AAAA,QACF;AAGA,cAAM,MAAM,IAAI,YAAY;AAC5B,YAAI,IAAI,WAAW,KAAK,OAAO,OAAO,QAAQ,KAAQ;AACpD,gBAAM,UAAU;AAChB,+BAAA;AACA,kBAAA;AAAA,QACF;AACA;AAAA,MACF;AAGA,UAAI,IAAI,QAAQ,IAAI,SAAS,YAAY,QAAA;AACzC,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,cAAM,cAAc;AACpB,eAAO,KAAK,UAAA;AAAA,MACd;AACA,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,cAAM,cAAc;AACpB,eAAO,KAAA;AAAA,MACT;AACA,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,cAAM,cAAc,CAAC,MAAM;AAC3B,cAAM,UAAU,MAAM,cAClB,yCACA;AACJ,eAAO,QAAA;AAAA,MACT;AAEA,YAAM,UAAU,MAAM;AACpB,YAAI,MAAM,MAAM,OAAO,KAAK,CAAC,MAAM,aAAa;AAC9C,gBAAM,cAAc;AACpB,gBAAM,UAAU,GAAG,MAAM,MAAM,IAAI;AACnC,kBAAA;AACA;AAAA,QACF;AACA,gBAAA;AAAA,MACF;AAEA,UAAI,MAAM,eAAe,IAAI,SAAS,KAAK;AACzC,cAAM,cAAc;AACpB,cAAM,UAAU;AAAA,MAClB;AAEA,UAAI,MAAM,SAAS,WAAW;AAC5B,gBAAQ,IAAI,MAAA;AAAA,UACV,KAAK;AACH,mBAAO,QAAA;AAAA,UACT,KAAK;AACH,kBAAM,OAAO;AACb,mBAAO,QAAA;AAAA,UACT,KAAK;AACH,kBAAM,OAAO;AACb,mBAAO,QAAA;AAAA,UACT,KAAK;AACH,kBAAM,aAAa,KAAK,IAAI,GAAG,MAAM,aAAa,CAAC;AACnD,mBAAO,QAAA;AAAA,UACT,KAAK;AACH,kBAAM,aAAa,KAAK;AAAA,cACtB,SAAS,SAAS;AAAA,cAClB,MAAM,aAAa;AAAA,YAAA;AAErB,mBAAO,QAAA;AAAA,UACT,KAAK;AACH,mBAAO,cAAA;AAAA,UACT,KAAK;AACH,mBAAO,QAAA;AAAA,QAAQ;AAEnB,YAAI,IAAI,aAAa,IAAK,QAAO,cAAA;AACjC,YAAI,IAAI,aAAa,OAAO,IAAI,aAAa,KAAK;AAChD,gBAAM,WAAW;AACjB,iBAAO,QAAA;AAAA,QACT;AACA;AAAA,MACF;AAEA,cAAQ,IAAI,MAAA;AAAA,QACV,KAAK;AACH,iBAAO,QAAA;AAAA,QACT,KAAK;AACH,gBAAM,OAAO;AACb,iBAAO,QAAA;AAAA,QACT,KAAK;AACH,gBAAM,SAAS,QAAQ,OAAO,EAAE;AAChC,iBAAO,QAAA;AAAA,QACT,KAAK;AACH,gBAAM,SAAS,QAAQ,OAAO,CAAC;AAC/B,iBAAO,QAAA;AAAA,QACT,KAAK;AACH,cAAI,MAAM,WAAW,GAAG;AAEtB,kBAAM,OAAO;AACb,mBAAO,QAAA;AAAA,UACT;AACA,gBAAM,SAAS,KAAK,IAAI,GAAG,MAAM,SAAS,CAAC;AAC3C,iBAAO,QAAA;AAAA,QACT,KAAK;AACH,gBAAM,SAAS,KAAK,IAAI,OAAO,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC;AACjE,iBAAO,QAAA;AAAA,QACT,KAAK;AAAA,QACL,KAAK;AACH,iBAAO,UAAA;AAAA,QACT,KAAK;AACH,iBAAO,SAAA;AAAA,QACT,KAAK;AACH,iBAAO,YAAA;AAAA,QACT,KAAK;AACH,iBAAO,aAAA;AAAA,QACT,KAAK;AACH,gBAAM,YAAY,CAAC,MAAM;AACzB,gBAAM,UAAU,MAAM,YAClB,kDACA;AACJ,+BAAA;AACA,iBAAO,QAAA;AAAA,QACT,KAAK,KAAK;AACR,cAAI,IAAI,OAAO;AAGb,gBAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,oBAAM,UAAU;AAChB,qBAAO,QAAA;AAAA,YACT;AACA,kBAAM,QAAQ,MAAM,UAAU;AAC9B,kBAAM,UAAU,MAAA;AAChB,kBAAM,UAAU,YAAY,KAAK;AACjC,iCAAA;AACA,mBAAO,QAAA;AAAA,UACT;AAIA,gBAAM,OAAO,MAAM,aAAa,MAAM,MAAM;AAC5C,cAAI,CAAC,KAAM;AACX,gBAAM,aACJ,KAAK,SAAS,YACV,KAAK,MACJ,UAAU,KAAK,GAAG,KAAK;AAC9B,cAAI,MAAM,UAAU,IAAI,UAAU,GAAG;AACnC,kBAAM,UAAU,OAAO,UAAU;AACjC,kBAAM,UAAU,aAAa,eAAe,cAAc,YAAY,UAAU;AAAA,UAClF,OAAO;AACL,kBAAM,UAAU,IAAI,UAAU;AAC9B,kBAAM,UAAU,cAAc,eAAe,cAAc,YAAY,UAAU;AAAA,UACnF;AACA,+BAAA;AACA,iBAAO,QAAA;AAAA,QACT;AAAA,QACA,KAAK,KAAK;AAGR,gBAAM,WAAW,MAAM,aAAa,WAAW,WAAW;AAC1D,+BAAA;AACA,gBAAM,UACJ,MAAM,aAAa,WACf,8BACA;AACN,iBAAO,QAAA;AAAA,QACT;AAAA,MAAA;AAGF,UAAI,IAAI,aAAa,OAAO,IAAI,SAAS,SAAS;AAChD,cAAM,OAAO;AACb,cAAM,UAAU;AAChB,gBAAA;AACA;AAAA,MACF;AAIA,UAAI,IAAI,aAAa,IAAK,QAAO,UAAA;AAEjC,UAAI,IAAI,aAAa,OAAO,IAAI,aAAa,KAAK;AAChD,cAAM,WAAW;AACjB,gBAAA;AAAA,MACF;AAAA,IACF;AAMA,aAAS,kBAAkB,WAAW,YAAY,KAAK;AACvD,aAAS,GAAG,UAAU,OAAO;AAAA,EAC/B,CAAC;AACH;AAIA,SAAS,eACP,SACA,UACA,QACA,UACA,OACM;AACN,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,MAAM,QAAQ;AAC9B,UAAQ,QACN,MAAM,SAAS,YACX,UAAU,OAAO,IAAI,KAAK,gBAC1B,UAAU,OAAO,IAAI,KAAK;AAChC,oBAAkB,OAAO;AACzB,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,OAAO,SAAS,CAAC;AACvB,UAAM,SAAS,SAAS,OAAO;AAC/B,UAAM,UAAU,MAAM,MAAM,IAAI,IAAI;AACpC,UAAM,YAAY,MAAM,QAAQ,IAAI,IAAI;AACxC,UAAM,YAAY,OAAO,MAAM,QAAQ,IAAI;AAC3C,UAAM,aAAa,aAAa,cAAc,MAAM;AACpD,UAAM,cAAc,MAAM,SAAS,aAAa,MAAM,MAAM;AAC5D,UAAM,SAAS,CAAC,YACZ,OAAO,QACP,SACE,OAAO,SACP,OAAO;AAEb,UAAM,MAAM,IAAI,cAAc,UAAU;AAAA,MACtC,IAAI,QAAQ,KAAK,IAAI;AAAA,MACrB,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,GAAI,cAAc,EAAE,iBAAiB,OAAO,QAAA,IAAY,CAAA;AAAA,IAAC,CAC1D;AACD,UAAM,OAAO,CAAC,IAAY,MAAc,OACtC,IAAI,eAAe,UAAU;AAAA,MAC3B,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE;AAAA,MACnB,SAAS;AAAA,MACT;AAAA,MACA,QAAQ;AAAA,MACR,UAAU;AAAA,IAAA,CACX;AACH,QAAI,IAAI,KAAK,SAAS,GAAG,cAAc,MAAM,GAAG,KAAK,OAAO,EAAE,CAAC;AAC/D,QAAI;AAAA,MACF;AAAA,QACE;AAAA,QACA,GAAG,UAAU,MAAM,GAAG;AAAA,QACtB,UAAU,OAAO,UAAU,OAAO;AAAA,MAAA;AAAA,IACpC;AAEF,QAAI;AAAA,MACF;AAAA,QACE;AAAA,QACA,GAAG,SAAS,MAAM,GAAG;AAAA,QACrB,SAAS,OAAO,SAAS,OAAO;AAAA,MAAA;AAAA,IAClC;AAEF,QAAI,IAAI,KAAK,OAAO,GAAG,aAAa,MAAM,GAAG,KAAK,OAAO,KAAK,CAAC;AAC/D,QAAI,IAAI,KAAK,WAAW,GAAG,YAAY,MAAM,GAAG,KAAK,OAAO,KAAK,CAAC;AAClE,QAAI,IAAI,KAAK,QAAQ,SAAS,KAAK,IAAI,GAAG,MAAM,CAAC;AACjD,YAAQ,IAAI,GAAG;AAAA,EACjB;AACF;AAEA,SAAS,cACP,WACA,YACA,WACA,UACA,QACA,OACA,eACA,WACM;AACN,YAAU,QAAQ,YAAY,QAAQ,KAAK;AAC3C,oBAAkB,UAAU;AAC5B,oBAAkB,UAAU,OAAO;AAEnC,aAAW;AAAA,IACT,SAAS,UAAU,UAAU;AAAA,MAC3B,EAAE,MAAM,OAAO,IAAI,OAAO,UAAU,OAAO,cAAA;AAAA,MAC3C,GAAG,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,QAC1B,MAAM,SAAS,EAAE,IAAI;AAAA,QACrB,IAAI,OAAO;AAAA,QACX,OAAO;AAAA,MAAA,EACP;AAAA,IAAA,CACH;AAAA,EAAA;AAOH,QAAM,aAAa,gBAAgB,gBAAgB,OAAO,MAAM;AAChE,QAAM,eAAe,gBAAgB,QAAQ,WAAW,KAAK;AAE7D,WAAS,IAAI,GAAG,IAAI,MAAM,aAAa,QAAQ,KAAK;AAClD,UAAM,OAAO,MAAM,aAAa,CAAC;AACjC,QAAI,KAAK,SAAS,WAAW;AAC3B,YAAM,aAAa,KAAK;AACxB,YAAM,cAAc,eAAe,cAAc,SAAY;AAC7D,YAAM,OAAO,aAAa,IAAI,UAAU,KAAK;AAAA,QAC3C,OAAO;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAET,YAAM,UAAU,MAAM,SAAS,YAAY,MAAM,MAAM;AACvD,gBAAU,QAAQ;AAAA,QAChB,oBAAoB,UAAU,OAAO,CAAC,IAAI,aAAa,YAAY;AAAA,UACjE,GAAG;AAAA,UACH,WAAW,MAAM,UAAU,IAAI,UAAU;AAAA,UACzC;AAAA,QAAA,CACD;AAAA,MAAA;AAEH;AAAA,IACF;AACA,UAAM,MAAM,KAAK;AACjB,UAAM,SAAS,YAAY,GAAG,KAAK,CAAC,MAAM;AAC1C,UAAM,QAAoB;AAAA,MACxB,EAAE,MAAM,KAAK,IAAI,OAAO,IAAI,OAAO,cAAA;AAAA,IAAc;AAEnD,aAAS,IAAI,GAAG,IAAI,OAAO,MAAM,QAAQ,KAAK;AAC5C,YAAM,OAAO,OAAO,MAAM,CAAC;AAC3B,YAAM,OAAO,OAAO,KAAK,KAAK,IAAI;AAClC,YAAM,UACJ,MAAM,SAAS,YAAY,MAAM,MAAM,UAAU,MAAM,MAAM;AAC/D,YAAM,aAAa,MAAM,SAAS,IAAI,GAAG,GAAG,IAAI,KAAK,IAAI,EAAE;AAC3D,YAAM;AAAA,QACJ,eAAe,MAAM,QAAQ,eAAe,SAAS,UAAU;AAAA,MAAA;AAAA,IAEnE;AACA,cAAU,QAAQ,IAAI,SAAS,UAAU,OAAO,CAAC,IAAI,KAAK,CAAC;AAAA,EAC7D;AAMA,MAAI,MAAM,SAAS,YAAY,MAAM,aAAa,SAAS,GAAG;AAC5D,UAAM,SAAS,OAAO,MAAM,MAAM;AAClC,iBAAa,MAAM;AACjB,UAAI;AACF,kBAAU,oBAAoB,MAAM;AAAA,MACtC,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAOA,SAAS,SAAS,OAAsD;AAGtE,QAAM,MAAmB,CAAA;AACzB,QAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,QAAI,IAAI,EAAG,KAAI,KAAK,EAAE,MAAM,OAAO,IAAI,OAAO,OAAO;AACrD,QAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,OAAO;AACxC,QAAI,KAAK,EAAE,MAAM,KAAK,KAAK,IAAI,OAAO,IAAI;AAC1C,QAAI,KAAK,EAAE,MAAM,MAAM,IAAI,OAAO,OAAO;AACzC,QAAI,KAAK,EAAE,MAAM,KAAK,OAAO,IAAI,OAAO,IAAI;AAAA,EAC9C,CAAC;AACD,SAAO;AACT;AAEA,SAAS,cACP,KACA,UACA,MACM;AACN,oBAAkB,GAAG;AACrB,OAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,IAAI,EAAE,QAAQ,CAAC;AAAA,QACtB,SAAS,IAAI;AAAA,QACb,IAAI,IAAI;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL,CAAC;AACH;AAEA,SAAS,YACP,SACA,UACA,OACM;AACN,UAAQ,UAAU,MAAM;AACxB,MAAI,CAAC,MAAM,SAAU;AACrB,oBAAkB,OAAO;AACzB,QAAM,QAAQ,eAAA;AAEd,QAAM,SAAS,SAAS,gBAAgB;AACxC,QAAM,QAAQ,SAAS,iBAAiB;AACxC,QAAM,YAAY,UAAU;AAC5B,MAAI,WAAW;AACb,UAAM,SAAS,IAAI,oBAAoB,UAAU;AAAA,MAC/C,IAAI;AAAA,MACJ,UAAU;AAAA,MACV,SAAS;AAAA,MACT,SAAS;AAAA,MACT,iBAAiB,EAAE,cAAc,EAAA;AAAA,MACjC,gBAAgB,EAAE,eAAe,SAAA;AAAA,IAAS,CAC3C;AACD,YAAQ,IAAI,MAAM;AAClB,UAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,aAAO,QAAQ,IAAI,aAAa,UAAU,QAAQ,CAAC,IAAI,IAAI,CAAC;AAAA,IAC9D,CAAC;AACD;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,cAAc,UAAU;AAAA,IACvC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,IACV,WAAW;AAAA,EAAA,CACZ;AACD,QAAM,OAAO,IAAI,cAAc,UAAU;AAAA,IACvC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,IACV,WAAW;AAAA,EAAA,CACZ;AACD,QAAM,QAAQ,IAAI,cAAc,UAAU;AAAA,IACxC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,IACV,WAAW;AAAA,EAAA,CACZ;AACD,QAAM,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AACxC,MAAI,WAAW;AACf,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,QAAI,MAAM,CAAC,GAAG,SAAS,SAAS;AAC9B,iBAAW,IAAI;AACf;AAAA,IACF;AAAA,EACF;AACA,QACG,MAAM,GAAG,QAAQ,EACjB;AAAA,IAAQ,CAAC,MAAM,MACd,KAAK,IAAI,aAAa,UAAU,UAAU,CAAC,IAAI,IAAI,CAAC;AAAA,EAAA;AAExD,QACG,MAAM,QAAQ,EACd;AAAA,IAAQ,CAAC,MAAM,MACd,MAAM,IAAI,aAAa,UAAU,UAAU,CAAC,IAAI,IAAI,CAAC;AAAA,EAAA;AAEzD,OAAK,IAAI,IAAI;AACb,OAAK,IAAI,KAAK;AACd,UAAQ,IAAI,IAAI;AAClB;AAEA,SAAS,cACP,WACA,aACA,cACA,QACA,OACM;AACN,QAAM,OAAO,MAAM,SAAS;AAC5B,YAAU,UAAU;AACpB,MAAI,CAAC,KAAM;AAGX,cAAY,UAAU,KAAK,MAAM,MAAM;AACvC,QAAM,UAAU,MAAM,YAAY;AAClC,QAAM,QAAQ,OAAO,KAAK;AAC1B,eAAa,UACX,MAAM,OAAO,WAAW,IACpB,kCACA,YAAY,OAAO,OAAO,KAAK;AACvC;AAEA,SAAS,cACP,OACA,OACA,QACA,UACA,OACM;AACN,QAAM,QAAQ,MAAM,MAAM;AAC1B,QAAM,YACJ,QAAQ,IACJ;AAAA,IACE,EAAE,MAAM,OAAO,IAAI,OAAO,MAAA;AAAA,IAC1B,EAAE,MAAM,KAAK,IAAI,OAAO,SAAA;AAAA,IACxB,EAAE,MAAM,IAAI,KAAK,YAAY,IAAI,OAAO,GAAA;AAAA,EAAG,IAE7C,CAAA;AAEN,MAAI,MAAM,SAAS,UAAU;AAC3B,kBAAc,OAAO,UAAU;AAAA,MAC7B,GAAG,SAAS;AAAA,QACV,EAAE,KAAK,SAAS,OAAO,cAAA;AAAA,QACvB,EAAE,KAAK,OAAO,OAAO,QAAA;AAAA,MAAQ,CAC9B;AAAA,MACD,GAAG;AAAA,IAAA,CACJ;AACD,kBAAc,OAAO,UAAU,CAAC,EAAE,MAAM,YAAY,IAAI,OAAO,MAAA,CAAO,CAAC;AAAA,EACzE,WAAW,MAAM,SAAS,UAAU;AAClC,kBAAc,OAAO,UAAU,EAAE;AACjC,kBAAc,OAAO,UAAU,EAAE;AAAA,EACnC,WAAW,MAAM,SAAS,WAAW;AACnC,kBAAc,OAAO,UAAU;AAAA,MAC7B,GAAG,SAAS;AAAA,QACV,EAAE,KAAK,MAAM,OAAO,OAAA;AAAA,QACpB,EAAE,KAAK,SAAS,OAAO,SAAA;AAAA,QACvB,EAAE,KAAK,KAAK,OAAO,WAAA;AAAA,QACnB,EAAE,KAAK,SAAS,OAAO,SAAA;AAAA,QACvB,EAAE,KAAK,MAAM,OAAO,OAAA;AAAA,QACpB,EAAE,KAAK,KAAK,OAAO,OAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,OAAA;AAAA,MAAO,CAC3B;AAAA,MACD,GAAG;AAAA,IAAA,CACJ;AACD,kBAAc,OAAO,UAAU,CAAC,EAAE,MAAM,cAAc,IAAI,OAAO,MAAA,CAAO,CAAC;AAAA,EAC3E,OAAO;AACL,kBAAc,OAAO,UAAU;AAAA,MAC7B,GAAG,SAAS;AAAA,QACV,EAAE,KAAK,QAAQ,OAAO,OAAA;AAAA,QACtB,EAAE,KAAK,OAAO,OAAO,QAAA;AAAA,QACrB,EAAE,KAAK,KAAK,OAAO,OAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,UAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,UAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,WAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,cAAA;AAAA,QACnB,EAAE,KAAK,KAAK,OAAO,WAAA;AAAA,QACnB,EAAE,KAAK,MAAM,OAAO,UAAA;AAAA,QACpB,EAAE,KAAK,MAAM,OAAO,OAAA;AAAA,QACpB,EAAE,KAAK,MAAM,OAAO,OAAA;AAAA,QACpB,EAAE,KAAK,KAAK,OAAO,SAAA;AAAA,QACnB,EAAE,KAAK,OAAO,OAAO,OAAA;AAAA,QACrB,EAAE,KAAK,KAAK,OAAO,OAAA;AAAA,MAAO,CAC3B;AAAA,MACD,GAAG;AAAA,IAAA,CACJ;AACD,kBAAc,OAAO,UAAU;AAAA,MAC7B,EAAE,MAAM,UAAU,IAAI,OAAO,MAAA;AAAA,MAC7B,EAAE,MAAM,MAAM,YAAY,UAAU,OAAO,IAAI,OAAO,GAAA;AAAA,MACtD,EAAE,MAAM,gBAAgB,IAAI,OAAO,MAAA;AAAA,MACnC,EAAE,MAAM,MAAM,UAAU,IAAI,OAAO,GAAA;AAAA,MACnC,EAAE,MAAM,kBAAkB,IAAI,OAAO,MAAA;AAAA,MACrC,EAAE,MAAM,MAAM,cAAc,UAAU,UAAU,IAAI,OAAO,GAAA;AAAA,IAAG,CAC/D;AAAA,EACH;AACA,SAAO,UAAU,MAAM,WAAW;AACpC;AAEA,SAAS,cACP,WACA,YACA,YACA,UACA,QACA,OACM;AACN,QAAM,OAAO,MAAM,SAAS,YAAY,MAAM,WAAW;AACzD,YAAU,UAAU;AACpB,MAAI,CAAC,QAAQ,CAAC,MAAM,OAAQ;AAE5B,YAAU,QAAQ,gBAAgB,MAAM,MAAM;AAG9C,QAAM,IAAI,MAAM;AAChB,MAAI,EAAE,SAAS,UAAU,EAAE,SAAS,aAAa;AAC/C,eAAW,UACT;AAAA,EAEJ,OAAO;AACL,eAAW,UAAU;AAAA,EACvB;AAKA,oBAAkB,UAAU;AAG5B,aAAW;AAAA,IACT,IAAI,eAAe,UAAU;AAAA,MAC3B,IAAI;AAAA,MACJ,SAAS,KAAK,MAAM,WAAW;AAAA,MAC/B,IAAI,OAAO;AAAA,MACX,QAAQ;AAAA,MACR,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAKH,MAAI,MAAM,SAAS;AACjB,eAAW;AAAA,MACT,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI;AAAA,QACJ,SAAS,KAAK,MAAM,OAAO;AAAA,QAC3B,IAAI,OAAO;AAAA,QACX,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL;AAEA,MAAI,EAAE,SAAS,UAAU,EAAE,SAAS,aAAa;AAC/C,UAAM,SAAS,YAAY,EAAE,GAAG,KAAK,CAAC,MAAM;AAC5C,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,KAAK,IAAI,GAAG,OAAO,MAAM,IAAI,CAAC,MAAM,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAAA,IAAA;AAGlE,eAAW;AAAA,MACT,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,IAAI,OAAO;AAAA,QACX,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,WAAW;AAAA,MAAA,CACZ;AAAA,IAAA;AAGH,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,WAAW,SAAS,EAAE;AAC5B,YAAM,MAAM,IAAI,cAAc,UAAU;AAAA,QACtC,IAAI,cAAc,KAAK,IAAI;AAAA,QAC3B,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,YAAY;AAAA,MAAA,CACb;AACD,UAAI;AAAA,QACF,IAAI,eAAe,UAAU;AAAA,UAC3B,IAAI,cAAc,KAAK,IAAI;AAAA,UAC3B,SAAS,GAAG,WAAW,MAAM,GAAG,IAAI,SAAS,KAAK,IAAI,CAAC,GAAG;AAAA,YACxD;AAAA,UAAA;AAAA,UAEF,IAAI,WAAW,OAAO,SAAS,OAAO;AAAA,UACtC,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,MAAA;AAEH,YAAM,QAAQ,YAAY,MAAM,EAAE,GAAG;AACrC,YAAM,UAAU,QAAQ,YAAY,MAAM,OAAO,MAAM,IAAI;AAC3D,UAAI;AAAA,QACF,IAAI,eAAe,UAAU;AAAA,UAC3B,IAAI,cAAc,KAAK,IAAI;AAAA,UAC3B,SAAS;AAAA,UACT,IAAI,CAAC,QAAQ,OAAO,UAAU,WAAW,OAAO,KAAK,OAAO;AAAA,UAC5D,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,MAAA;AAEH,iBAAW,IAAI,GAAG;AAAA,IACpB;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,GAAmB;AAC1C,UAAQ,EAAE,MAAA;AAAA,IACR,KAAK;AACH,aAAO,SAAS,EAAE,GAAG,OAAO,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,IACnD,KAAK;AACH,aAAO,mBAAmB,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,IACjD,KAAK;AACH,aAAO,cAAc,EAAE,GAAG,OAAO,SAAS,EAAE,KAAK,IAAI,CAAC;AAAA,IACxD,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAIA,SAAS,cAAc,KAAiC;AACtD,QAAM,MAAM,IAAI,QAAQ,GAAG;AAC3B,MAAI,OAAO,EAAG,QAAO;AACrB,SAAO,IAAI,MAAM,GAAG,GAAG;AACzB;AAOA,SAAS,cAAc,MAA0B;AAC/C,QAAM,6BAAa,IAAA;AACnB,QAAM,QAAkB,CAAA;AACxB,QAAM,QAAQ;AACd,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,cAAc,CAAC,KAAK;AAC9B,QAAI,SAAS,OAAO,IAAI,CAAC;AACzB,QAAI,CAAC,QAAQ;AACX,eAAS,CAAA;AACT,aAAO,IAAI,GAAG,MAAM;AACpB,UAAI,MAAM,MAAO,OAAM,KAAK,CAAC;AAAA,IAC/B;AACA,WAAO,KAAK,CAAC;AAAA,EACf;AACA,MAAI,OAAO,IAAI,KAAK,EAAG,OAAM,KAAK,KAAK;AACvC,SAAO,MAAM,QAAQ,CAAC,MAAM,OAAO,IAAI,CAAC,CAAE;AAC5C;AAQA,SAAS,QAAQ,OAAc,OAAuB;AACpD,QAAM,QAAQ,MAAM;AACpB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,WAAW,CAACA,OAAc;AAC9B,UAAM,KAAK,MAAMA,EAAC;AAClB,QAAI,CAAC,GAAI,QAAO;AAChB,QAAI,GAAG,SAAS,MAAO,QAAO;AAE9B,WAAO,MAAM,UAAU,IAAI,GAAG,GAAG;AAAA,EACnC;AACA,MAAI,IAAI,MAAM,SAAS;AACvB,SAAO,KAAK,KAAK,IAAI,MAAM,QAAQ;AACjC,QAAI,SAAS,CAAC,EAAG,QAAO;AACxB,SAAK;AAAA,EACP;AAEA,SAAO,MAAM;AACf;AAEA,SAAS,YACP,QACA,OACA,WACU;AAEV,QAAM,WAAW,OAAO,KAAK,OAAO,CAAC,MAAM;AACzC,QAAI,CAAC,cAAc,GAAG,MAAM,MAAM,EAAG,QAAO;AAC5C,QAAI,MAAM,aAAa,CAAC,UAAU,QAAQ,CAAC,EAAG,QAAO;AACrD,WAAO;AAAA,EACT,CAAC;AACD,SAAO,MAAM,aAAa,WAAW,cAAc,QAAQ,IAAI;AACjE;AAQA,SAAS,gBACP,QACA,WACA,OAC2B;AAC3B,QAAM,0BAAU,IAAA;AAChB,aAAW,OAAO,YAAY,QAAQ,KAAgB,GAAG;AACvD,UAAM,IAAI,UAAU,GAAG,KAAK;AAC5B,UAAM,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,EAAA;AAC5D,WAAO,SAAS;AAChB,QAAI,SAAS;AACb,QAAI,UAAU;AACd,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,SAAS,OAAO,KAAM;AAC1B,YAAM,IAAI,OAAO,KAAK,KAAK,IAAI,EAAE;AACjC,UAAI,MAAM,UAAW,WAAU;AAC/B,UAAI,MAAM,aAAa,MAAM,aAAa,MAAM,QAAS,UAAS;AAAA,IACpE;AACA,QAAI,eAAe,SAAS;AAC5B,QAAI,gBAAgB,WAAW;AAC/B,QAAI,IAAI,GAAG,MAAM;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,eACP,MACA,QACA,OACA,SACA,UACU;AACV,QAAM,KAAK,UAAU,OAAO,UAAU;AACtC,QAAM,WAAW,WAAW,EAAE,MAAM,KAAK,IAAI,OAAO,aAAa;AACjE,MAAI,KAAK,UAAU,WAAW;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA,MAAM,EAAE,MAAM,KAAK,IAAI,OAAO,QAAA;AAAA,MAC9B;AAAA,IAAA;AAAA,EAEJ;AACA,QAAM,QAAQ,KAAK,SAAS;AAC5B,MAAI,UAAU,MAAM,mBAAmB,KAAK,GAAG;AAC7C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA,MAAM,EAAE,MAAM,KAAK,IAAI,OAAO,YAAA;AAAA,MAC9B;AAAA,IAAA;AAAA,EAEJ;AACA,QAAM,UAAU,UAAU,MAAM,CAAC;AACjC,QAAM,cAAc,UAAU,YAAY,YAAY,OAAO,MAAM;AACnE,QAAM,YAAY,UAAU,OAAO,QAAQ,OAAO;AAClD,MAAI,KAAK,UAAU,WAAW;AAC5B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,MAAM,EAAE,MAAM,KAAK,IAAI,OAAO,QAAA;AAAA,MAC9B;AAAA,IAAA;AAAA,EAEJ;AACA,MAAI,KAAK,UAAU,SAAS;AAC1B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,MAAM,EAAE,MAAM,KAAK,IAAI,OAAO,MAAA;AAAA,MAC9B;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO,EAAE,MAAM,aAAa,IAAI,WAAW,OAAO,IAAI,SAAA;AACxD;AAEA,SAAS,UAAU,QAAgB,KAAsB;AACvD,aAAW,QAAQ,OAAO,OAAO;AAC/B,QAAI,SAAS,OAAO,KAAM;AAC1B,UAAM,IAAI,OAAO,KAAK,KAAK,IAAI,EAAE;AACjC,QAAI,MAAM,aAAa,MAAM,aAAa,MAAM,QAAS,QAAO;AAAA,EAClE;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAuB;AACjD,MAAI,CAAC,KAAK,WAAW,MAAM,EAAG,QAAO;AACrC,MAAI,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,EAAG,QAAO;AACtD,MAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,MAAM;AACrE,WAAO;AACT,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAuB;AACjD,SAAO;AAAA,IACL;AAAA,IACA,SAAS,CAAC,EAAE,MAAM,WAAW,KAAK,KAAK,SAAS,IAAI,CAAC,IAAI;AAAA,IACzD,iBAAiB;AAAA,EAAA;AAErB;AAEA,SAAS,SAAS,MAAe,KAAa,OAAwB;AACpE,QAAM,QAAiB;AAAA,IACrB,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,SAAS;AAAA,IACT,cAAc;AAAA,IACd,eAAe;AAAA,IACf,KAAK;AAAA,EAAA;AAEP,gBAAc,KAAK;AACnB,OAAK,QAAQ,KAAK,KAAK;AAKvB,OAAK,kBAAkB;AACvB,SAAO;AACT;AAEA,SAAS,aACP,UACA,IACA,MACe;AACf,QAAM,MAAM,IAAI,cAAc,UAAU;AAAA,IACtC;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,YAAY;AAAA,EAAA,CACb;AACD,MAAI,KAAK,SAAS,UAAU;AAC1B,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,EAAE;AAAA,QACT,SAAS,KAAK;AAAA,QACd,IAAI,OAAO;AAAA,QACX,UAAU;AAAA,QACV,QAAQ;AAAA,MAAA,CACT;AAAA,IAAA;AAAA,EAEL,WAAW,KAAK,SAAS,SAAS;AAChC,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,EAAE;AAAA,QACT,SAAS,KAAK;AAAA,QACd,IAAI,OAAO;AAAA,QACX,UAAU;AAAA,QACV,QAAQ;AAAA,MAAA,CACT;AAAA,IAAA;AAAA,EAEL,WAAW,KAAK,SAAS,UAAU;AACjC,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,EAAE;AAAA,QACT,SAAS,KAAK,KAAK,OAAO,OAAO,EAAE,CAAC;AAAA,QACpC,IAAI,KAAK;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,MAAA,CACT;AAAA,IAAA;AAEH,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,EAAE;AAAA,QACT,SAAS,KAAK;AAAA,QACd,IAAI,OAAO;AAAA,QACX,UAAU;AAAA,QACV,QAAQ;AAAA,MAAA,CACT;AAAA,IAAA;AAAA,EAEL;AACA,SAAO;AACT;AAEA,SAAS,oBACP,UACA,IACA,MACA,OACA,MACe;AAMf,QAAM,WAAW,QAAQ;AACzB,QAAM,YAAY,KAAK,YAAY,MAAM;AAKzC,QAAM,OAAc;AAAA,IAClB,EAAE,MAAM,IAAI,SAAS,KAAK,IAAI,OAAO,MAAA;AAAA,IACrC,EAAE,MAAM,UAAU,IAAI,OAAO,GAAA;AAAA,IAC7B,EAAE,MAAM,MAAM,IAAI,OAAO,MAAA;AAAA,EAAM;AAEjC,MAAI,KAAK,UAAU,GAAG;AACpB,SAAK,KAAK,EAAE,MAAM,MAAM,IAAI,OAAO,SAAS;AAC5C,SAAK,KAAK,EAAE,MAAM,GAAG,KAAK,OAAO,IAAI,IAAI,OAAO,QAAA,CAAS;AACzD,SAAK,KAAK,EAAE,MAAM,cAAc,IAAI,OAAO,IAAI;AAAA,EACjD;AACA,MAAI,KAAK,QAAQ,GAAG;AAClB,SAAK,KAAK,EAAE,MAAM,MAAM,IAAI,OAAO,SAAS;AAC5C,SAAK,KAAK,EAAE,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,OAAO,QAAA,CAAS;AACvD,SAAK,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,OAAO;AACzC,SAAK,KAAK,EAAE,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,OAAO,QAAA,CAAS;AACvD,SAAK,KAAK,EAAE,MAAM,YAAY,IAAI,OAAO,IAAI;AAAA,EAC/C;AACA,MAAI,KAAK,YAAY,KAAK,KAAK,UAAU,GAAG;AAC1C,SAAK,KAAK,EAAE,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,OAAO,MAAA,CAAO;AACrD,SAAK,KAAK,EAAE,MAAM,WAAW,IAAI,OAAO,IAAI;AAAA,EAC9C;AACA,OAAK,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,OAAO;AAEzC,QAAM,cAAc,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,KAAK,QAAQ,CAAC;AAClE,QAAM,OAAO;AACb,QAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,CAAC;AACrC,QAAM,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,UAAU,eAAe,CAAC,CAAC;AACrE,QAAM,WAAW,KAAK,IAAI,GAAG,UAAU,YAAY,WAAW;AAE9D,QAAM,MAAM,IAAI,cAAc,UAAU;AAAA,IACtC;AAAA,IACA,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,GAAI,KAAK,UAAU,EAAE,iBAAiB,OAAO,QAAA,IAAY,CAAA;AAAA,EAAC,CAC3D;AACD,MAAI;AAAA,IACF,IAAI,eAAe,UAAU;AAAA,MAC3B,IAAI,GAAG,EAAE;AAAA,MACT,SAAS,KAAK,OAAO,SAAS;AAAA,MAC9B,IAAI,OAAO;AAAA,MACX,QAAQ;AAAA,MACR,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEH,OAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,QAAI;AAAA,MACF,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,EAAE,QAAQ,CAAC;AAAA,QAClB,SAAS,IAAI;AAAA,QACb,IAAI,IAAI;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL,CAAC;AACD,MAAI;AAAA,IACF,IAAI,eAAe,UAAU;AAAA,MAC3B,IAAI,GAAG,EAAE;AAAA,MACT,SAAS,KAAK,OAAO,QAAQ;AAAA,MAC7B,IAAI,OAAO;AAAA,MACX,QAAQ;AAAA,MACR,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEH,SAAO;AACT;AAeA,SAAS,SACP,UACA,UACA,OACe;AACf,QAAM,MAAM,IAAI,cAAc,UAAU;AAAA,IACtC,IAAI;AAAA,IACJ,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,QAAQ;AAAA,EAAA,CACT;AACD,QAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,UAAM,WAA2D;AAAA,MAC/D,IAAI,GAAG,QAAQ,KAAK,CAAC;AAAA,MACrB,OAAO,KAAK;AAAA,MACZ,QAAQ;AAAA,MACR,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,UAAU;AAAA,IAAA;AAEZ,QAAI,KAAK,GAAI,UAAS,kBAAkB,KAAK;AAC7C,UAAM,UAAU,IAAI,cAAc,UAAU,QAAQ;AACpD,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,QAAQ,aAAa,CAAC;AAC1D,UAAM,UAAU,KAAK,OAAO,KAAK,KAAK,KAAK,SAAS,IAAI;AACxD,UAAM,cAAc,KAAK,WAAW,KAAK,SAAS,KAAK,SAAS,IAAI;AACpE,UAAM,YAAY,KAAK,IAAI,GAAG,aAAa,UAAU,WAAW;AAChE,QAAI,KAAK,MAAM;AACb,cAAQ;AAAA,QACN,IAAI,eAAe,UAAU;AAAA,UAC3B,IAAI,GAAG,QAAQ,KAAK,CAAC;AAAA,UACrB,SAAS,GAAG,KAAK,KAAK,IAAI;AAAA,UAC1B,IAAI,KAAK,KAAK;AAAA,UACd,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,MAAA;AAAA,IAEL;AACA,YAAQ;AAAA,MACN,IAAI,eAAe,UAAU;AAAA,QAC3B,IAAI,GAAG,QAAQ,KAAK,CAAC;AAAA,QACrB,SAAS,SAAS,KAAK,MAAM,SAAS;AAAA,QACtC,IAAI,KAAK;AAAA,QACT,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAEH,QAAI,KAAK,UAAU;AACjB,cAAQ;AAAA,QACN,IAAI,eAAe,UAAU;AAAA,UAC3B,IAAI,GAAG,QAAQ,KAAK,CAAC;AAAA,UACrB,SAAS,IAAI,KAAK,SAAS,IAAI;AAAA,UAC/B,IAAI,KAAK,SAAS;AAAA,UAClB,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA,CACX;AAAA,MAAA;AAAA,IAEL;AACA,QAAI,IAAI,OAAO;AAAA,EACjB,CAAC;AACD,SAAO;AACT;AAEA,SAAS,kBAAkB,MAA2B;AACpD,QAAM,MAAM,KAAK,YAAA,EAAc,IAAI,CAAC,MAAM,EAAE,EAAE;AAC9C,aAAW,MAAM,IAAK,MAAK,OAAO,EAAE;AACtC;AAEA,SAAS,YAAY,QAAgB,OAAsB;AACzD,QAAM,UAAU,MAAM,YAAY;AAClC,QAAM,QAAQ,OAAO,KAAK;AAC1B,QAAM,QAAkB,CAAC,GAAG,KAAK,OAAO;AACxC,MAAI,MAAM,UAAW,OAAM,KAAK,SAAS,OAAO,IAAI,KAAK,EAAE;AAAA,WAClD,MAAM,UAAU,YAAY,OAAO;AAC1C,UAAM,KAAK,IAAI,MAAM,MAAM,KAAK,OAAO,IAAI,KAAK,EAAE;AAAA,EACpD;AACA,SAAO,aAAa,MAAM,KAAK,KAAK,CAAC;AACvC;AAEA,SAAS,YAAY,OAA2B,QAAyB;AACvE,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAQ,QAAO,UAAU,KAAK;AAClC,SAAO;AACT;AAEA,SAAS,cAAc,KAAa,QAAyB;AAC3D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,IAAI,YAAA,EAAc,SAAS,OAAO,aAAa;AACxD;AAEA,SAAS,SAAS,MAAc,OAAuB;AACrD,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,KAAK,UAAU,MAAO,QAAO;AACjC,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC,CAAC;AACpC;AAEA,SAAS,YAAY,MAAe,KAAkC;AACpE,aAAW,KAAK,KAAK,SAAS;AAC5B,QAAI,EAAE,SAAS,QAAQ,EAAE,QAAQ,IAAK,QAAO;AAAA,EAC/C;AACA,SAAO;AACT;"}
@@ -0,0 +1,71 @@
1
+ function serializeEnv(file) {
2
+ const body = file.entries.map((e) => e.raw).join("\n");
3
+ return file.trailingNewline ? `${body}
4
+ ` : body;
5
+ }
6
+ function rebuildKvLine(entry) {
7
+ const prefix = entry.exportPrefix ? "export " : "";
8
+ const encoded = encodeValue(entry.value, entry.quoting);
9
+ entry.rawValue = encoded.inner;
10
+ entry.raw = `${prefix}${entry.key}=${encoded.full}${entry.inlineComment}`;
11
+ }
12
+ function encodeValue(value, quoting) {
13
+ const effective = needsQuoting(value, quoting) ? "double" : quoting;
14
+ switch (effective) {
15
+ case "none":
16
+ return { inner: value, full: value };
17
+ case "single": {
18
+ if (value.includes("'")) {
19
+ const escaped = escapeDoubleQuoted(value);
20
+ return { inner: escaped, full: `"${escaped}"` };
21
+ }
22
+ return { inner: value, full: `'${value}'` };
23
+ }
24
+ case "double": {
25
+ const escaped = escapeDoubleQuoted(value);
26
+ return { inner: escaped, full: `"${escaped}"` };
27
+ }
28
+ }
29
+ }
30
+ function needsQuoting(value, current) {
31
+ if (current !== "none") return false;
32
+ return /[\s#"'\\]/.test(value);
33
+ }
34
+ function escapeDoubleQuoted(value) {
35
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
36
+ }
37
+ const SECRET_TOKENS = [
38
+ "SECRET",
39
+ "TOKEN",
40
+ "PASSWORD",
41
+ "PASSWD",
42
+ "PWD",
43
+ "KEY",
44
+ "PRIVATE",
45
+ "CREDENTIAL",
46
+ "AUTH",
47
+ "DSN"
48
+ ];
49
+ function isSecretKey(key) {
50
+ const upper = key.toUpperCase();
51
+ const segments = upper.split("_").filter(Boolean);
52
+ if (segments.length === 0) return false;
53
+ if (segments[segments.length - 1] === "ID") return false;
54
+ if (segments[0] === "PUBLIC") return false;
55
+ if (segments.includes("PUBLIC")) return false;
56
+ return segments.some(
57
+ (seg) => SECRET_TOKENS.some((token) => seg === token || seg.endsWith(token))
58
+ );
59
+ }
60
+ function maskValue(value) {
61
+ if (value.length === 0) return "••••";
62
+ const dots = "•".repeat(Math.min(value.length, 8));
63
+ return value.length > 8 ? `${dots} (${value.length})` : dots;
64
+ }
65
+ export {
66
+ isSecretKey as i,
67
+ maskValue as m,
68
+ rebuildKvLine as r,
69
+ serializeEnv as s
70
+ };
71
+ //# sourceMappingURL=mask-Bv9W6Ei8.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask-Bv9W6Ei8.mjs","sources":["../../src/core/serialize.ts","../../src/core/mask.ts"],"sourcesContent":["import type { EnvFile, KvEntry, Quoting } from './types.ts';\n\n/**\n * Serialize an {@link EnvFile} back to a string. Unchanged entries are emitted\n * from their captured `raw` line so round-trip is byte-exact; modified entries\n * must have their `raw` field re-built via {@link rebuildKvLine} first.\n */\nexport function serializeEnv(file: EnvFile): string {\n const body = file.entries.map((e) => e.raw).join('\\n');\n return file.trailingNewline ? `${body}\\n` : body;\n}\n\n/**\n * Rebuild the `raw` line of a kv entry from its structured fields. Call this\n * after mutating `key`, `value`, `quoting`, or `exportPrefix` so the next\n * serialize emits the new line.\n */\nexport function rebuildKvLine(entry: KvEntry): void {\n const prefix = entry.exportPrefix ? 'export ' : '';\n const encoded = encodeValue(entry.value, entry.quoting);\n // Update rawValue to match the (possibly new) encoded inner form.\n entry.rawValue = encoded.inner;\n entry.raw = `${prefix}${entry.key}=${encoded.full}${entry.inlineComment}`;\n}\n\ninterface EncodedValue {\n /** Inner string without surrounding quotes (for `rawValue`). */\n inner: string;\n /** Fully formed value including surrounding quotes if any. */\n full: string;\n}\n\nfunction encodeValue(value: string, quoting: Quoting): EncodedValue {\n // Auto-promote to double quotes when an unquoted value would be ambiguous\n // (contains whitespace, `#`, or characters that require escaping). Single\n // quotes are preserved as authored but can't represent embedded `'`.\n const effective: Quoting = needsQuoting(value, quoting) ? 'double' : quoting;\n\n switch (effective) {\n case 'none':\n return { inner: value, full: value };\n case 'single': {\n if (value.includes(\"'\")) {\n // Fall back to double-quoting if a single quote sneaks in.\n const escaped = escapeDoubleQuoted(value);\n return { inner: escaped, full: `\"${escaped}\"` };\n }\n return { inner: value, full: `'${value}'` };\n }\n case 'double': {\n const escaped = escapeDoubleQuoted(value);\n return { inner: escaped, full: `\"${escaped}\"` };\n }\n }\n}\n\nfunction needsQuoting(value: string, current: Quoting): boolean {\n if (current !== 'none') return false;\n return /[\\s#\"'\\\\]/.test(value);\n}\n\nfunction escapeDoubleQuoted(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n}\n","const SECRET_TOKENS = [\n 'SECRET',\n 'TOKEN',\n 'PASSWORD',\n 'PASSWD',\n 'PWD',\n 'KEY',\n 'PRIVATE',\n 'CREDENTIAL',\n 'AUTH',\n 'DSN'\n] as const;\n\n/**\n * Heuristic: should a value with this key be masked by default? Matches any\n * underscore-separated segment of {@link SECRET_TOKENS} (case-insensitive),\n * with carve-outs for false positives like `*_PUBLIC_KEY`, `PUBLIC_*`, and\n * keys whose final segment is `ID` (e.g. `API_KEY_ID` is an identifier, not\n * a secret).\n */\nexport function isSecretKey(key: string): boolean {\n const upper = key.toUpperCase();\n const segments = upper.split('_').filter(Boolean);\n if (segments.length === 0) return false;\n if (segments[segments.length - 1] === 'ID') return false;\n if (segments[0] === 'PUBLIC') return false;\n if (segments.includes('PUBLIC')) return false;\n\n return segments.some((seg) =>\n SECRET_TOKENS.some((token) => seg === token || seg.endsWith(token))\n );\n}\n\n/**\n * Render a masked placeholder that hints at the original value's length\n * without leaking content. Returns `••••` when the input is empty.\n */\nexport function maskValue(value: string): string {\n if (value.length === 0) return '••••';\n const dots = '•'.repeat(Math.min(value.length, 8));\n return value.length > 8 ? `${dots} (${value.length})` : dots;\n}\n"],"names":[],"mappings":"AAOO,SAAS,aAAa,MAAuB;AAClD,QAAM,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,IAAI;AACrD,SAAO,KAAK,kBAAkB,GAAG,IAAI;AAAA,IAAO;AAC9C;AAOO,SAAS,cAAc,OAAsB;AAClD,QAAM,SAAS,MAAM,eAAe,YAAY;AAChD,QAAM,UAAU,YAAY,MAAM,OAAO,MAAM,OAAO;AAEtD,QAAM,WAAW,QAAQ;AACzB,QAAM,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,QAAQ,IAAI,GAAG,MAAM,aAAa;AACzE;AASA,SAAS,YAAY,OAAe,SAAgC;AAIlE,QAAM,YAAqB,aAAa,OAAO,OAAO,IAAI,WAAW;AAErE,UAAQ,WAAA;AAAA,IACN,KAAK;AACH,aAAO,EAAE,OAAO,OAAO,MAAM,MAAA;AAAA,IAC/B,KAAK,UAAU;AACb,UAAI,MAAM,SAAS,GAAG,GAAG;AAEvB,cAAM,UAAU,mBAAmB,KAAK;AACxC,eAAO,EAAE,OAAO,SAAS,MAAM,IAAI,OAAO,IAAA;AAAA,MAC5C;AACA,aAAO,EAAE,OAAO,OAAO,MAAM,IAAI,KAAK,IAAA;AAAA,IACxC;AAAA,IACA,KAAK,UAAU;AACb,YAAM,UAAU,mBAAmB,KAAK;AACxC,aAAO,EAAE,OAAO,SAAS,MAAM,IAAI,OAAO,IAAA;AAAA,IAC5C;AAAA,EAAA;AAEJ;AAEA,SAAS,aAAa,OAAe,SAA2B;AAC9D,MAAI,YAAY,OAAQ,QAAO;AAC/B,SAAO,YAAY,KAAK,KAAK;AAC/B;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MACJ,QAAQ,OAAO,MAAM,EACrB,QAAQ,MAAM,KAAK,EACnB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK;AACzB;ACpEA,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,YAAY,KAAsB;AAChD,QAAM,QAAQ,IAAI,YAAA;AAClB,QAAM,WAAW,MAAM,MAAM,GAAG,EAAE,OAAO,OAAO;AAChD,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,MAAI,SAAS,SAAS,SAAS,CAAC,MAAM,KAAM,QAAO;AACnD,MAAI,SAAS,CAAC,MAAM,SAAU,QAAO;AACrC,MAAI,SAAS,SAAS,QAAQ,EAAG,QAAO;AAExC,SAAO,SAAS;AAAA,IAAK,CAAC,QACpB,cAAc,KAAK,CAAC,UAAU,QAAQ,SAAS,IAAI,SAAS,KAAK,CAAC;AAAA,EAAA;AAEtE;AAMO,SAAS,UAAU,OAAuB;AAC/C,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,OAAO,IAAI,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC,CAAC;AACjD,SAAO,MAAM,SAAS,IAAI,GAAG,IAAI,KAAK,MAAM,MAAM,MAAM;AAC1D;"}