@crvouga/mockingbird-notifications 0.0.0-stage → 0.1.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.
package/dist/ui.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/ui/icons.ts", "../src/ui/sounds.ts", "../src/ui/app.ts", "../src/ui.ts"],
4
+ "sourcesContent": ["/** Inline SVG glyphs drawn for the device skins. Generic shapes; no vendor marks. */\nconst svg = (w: number, h: number, body: string, extra = \"\") =>\n `<svg width=\"${w}\" height=\"${h}\" viewBox=\"0 0 ${w} ${h}\" fill=\"none\" aria-hidden=\"true\" ${extra}>${body}</svg>`\n\nconst material = (d: string, size = 20) =>\n `<svg width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"${d}\"/></svg>`\n\nexport const icons = {\n signal: (c = \"currentColor\") =>\n svg(\n 18,\n 12,\n `<rect x=\"0\" y=\"8\" width=\"3\" height=\"4\" rx=\"1\" fill=\"${c}\"/><rect x=\"5\" y=\"5.5\" width=\"3\" height=\"6.5\" rx=\"1\" fill=\"${c}\"/><rect x=\"10\" y=\"3\" width=\"3\" height=\"9\" rx=\"1\" fill=\"${c}\"/><rect x=\"15\" y=\"0\" width=\"3\" height=\"12\" rx=\"1\" fill=\"${c}\"/>`,\n ),\n wifi: (c = \"currentColor\") =>\n svg(\n 16,\n 12,\n `<path d=\"M8 2.6c2.3 0 4.4.9 6 2.4l1.1-1.1A10 10 0 0 0 8 1 10 10 0 0 0 .9 3.9L2 5c1.6-1.5 3.7-2.4 6-2.4Z\" fill=\"${c}\"/><path d=\"M8 5.9c1.4 0 2.6.5 3.6 1.4l1.1-1.1A6.7 6.7 0 0 0 8 4.3c-1.8 0-3.5.7-4.7 1.9l1.1 1.1c1-.9 2.2-1.4 3.6-1.4Z\" fill=\"${c}\"/><path d=\"M10.3 8.6 8 11 5.7 8.6A3.3 3.3 0 0 1 8 7.6c.9 0 1.7.4 2.3 1Z\" fill=\"${c}\"/>`,\n ),\n battery: (c = \"currentColor\") =>\n svg(\n 27,\n 13,\n `<rect x=\".5\" y=\".5\" width=\"23\" height=\"12\" rx=\"3.8\" stroke=\"${c}\" stroke-opacity=\".4\"/><rect x=\"2\" y=\"2\" width=\"20\" height=\"9\" rx=\"2.5\" fill=\"${c}\"/><path d=\"M25 4.5v4c.8-.3 1.3-1.1 1.3-2s-.5-1.7-1.3-2Z\" fill=\"${c}\" fill-opacity=\".45\"/>`,\n ),\n compose: () =>\n svg(\n 24,\n 24,\n `<path d=\"M19 13v6.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 3 19.5v-13A1.5 1.5 0 0 1 4.5 5H11\" stroke=\"#007aff\" stroke-width=\"1.8\" stroke-linecap=\"round\"/><path d=\"m9.5 14.5.6-3.1 8.8-8.8a1.5 1.5 0 0 1 2.1 0l.9.9a1.5 1.5 0 0 1 0 2.1l-8.8 8.8-3.6.1Z\" stroke=\"#007aff\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>`,\n ),\n search: (c = \"currentColor\", s = 16) =>\n svg(\n s,\n s,\n `<circle cx=\"${s * 0.43}\" cy=\"${s * 0.43}\" r=\"${s * 0.3}\" stroke=\"${c}\" stroke-width=\"${s / 9}\"/><path d=\"m${s * 0.65} ${s * 0.65} ${s * 0.27} ${s * 0.27}\" stroke=\"${c}\" stroke-width=\"${s / 9}\" stroke-linecap=\"round\"/>`,\n ),\n mic: (c = \"#8e8e93\") =>\n svg(\n 14,\n 20,\n `<rect x=\"4\" y=\"1\" width=\"6\" height=\"11\" rx=\"3\" fill=\"${c}\"/><path d=\"M1.5 9a5.5 5.5 0 0 0 11 0M7 14.5V18\" stroke=\"${c}\" stroke-width=\"1.6\" stroke-linecap=\"round\"/>`,\n ),\n chevronRight: (c = \"#c4c4c6\") =>\n svg(\n 8,\n 13,\n `<path d=\"m1.5 1.5 5 5-5 5\" stroke=\"${c}\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\n ),\n chevronBack: () =>\n svg(\n 13,\n 21,\n `<path d=\"M11 2 2.5 10.5 11 19\" stroke=\"#007aff\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`,\n ),\n plus: () =>\n svg(\n 16,\n 16,\n `<path d=\"M8 2v12M2 8h12\" stroke=\"#8e8e93\" stroke-width=\"2.2\" stroke-linecap=\"round\"/>`,\n ),\n person: () =>\n svg(\n 26,\n 26,\n `<circle cx=\"13\" cy=\"9\" r=\"5\" fill=\"#fff\"/><path d=\"M3 24c0-5.5 4.5-9 10-9s10 3.5 10 9\" fill=\"#fff\"/>`,\n ),\n padlock: () =>\n svg(\n 16,\n 21,\n `<rect x=\"1\" y=\"9\" width=\"14\" height=\"11\" rx=\"2.5\" fill=\"#fff\"/><path d=\"M4 9V6a4 4 0 0 1 8 0v3\" stroke=\"#fff\" stroke-width=\"2\"/>`,\n ),\n flashlight: () =>\n svg(\n 14,\n 22,\n `<path d=\"M2 1h10v4L9.5 9v11a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V9L2 5V1Z\" fill=\"#fff\"/>`,\n ),\n camera: () =>\n svg(\n 22,\n 18,\n `<path d=\"M2 5.5A1.5 1.5 0 0 1 3.5 4h3l1.6-2.2h5.8L15.5 4h3A1.5 1.5 0 0 1 20 5.5v10a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 2 15.5v-10Z\" fill=\"#fff\"/><circle cx=\"11\" cy=\"10.3\" r=\"3.6\" fill=\"#555\"/>`,\n ),\n bell: () =>\n material(\n \"M12 22a2 2 0 0 0 2-2h-4a2 2 0 0 0 2 2Zm6-6V11a6 6 0 0 0-5-5.9V4a1 1 0 0 0-2 0v1.1A6 6 0 0 0 6 11v5l-2 2v1h16v-1l-2-2Z\",\n 18,\n ),\n expand: () => material(\"M7.4 8.6 12 13.2l4.6-4.6L18 10l-6 6-6-6z\", 16),\n collapse: () => material(\"M7.4 15.4 12 10.8l4.6 4.6L18 14l-6-6-6 6z\", 16),\n androidWifi: () => material(\"M12 21 23.6 6.5A17.6 17.6 0 0 0 12 2.5 17.6 17.6 0 0 0 .4 6.5Z\", 15),\n androidSignal: () => material(\"M2 22h20V2Z\", 14),\n androidBattery: () =>\n material(\n \"M16 4h-2V2h-4v2H8a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1Z\",\n 15,\n ),\n menu: () => material(\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\", 24),\n tune: () =>\n material(\n \"M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z\",\n 24,\n ),\n help: () =>\n material(\n \"M11 18h2v-2h-2v2zm1-16a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm0-14a4 4 0 0 0-4 4h2a2 2 0 1 1 4 0c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5a4 4 0 0 0-4-4z\",\n 24,\n ),\n gear: () =>\n material(\n \"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.48.48 0 0 0-.48-.41h-3.84a.48.48 0 0 0-.48.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96a.48.48 0 0 0-.59.22L2.74 8.87a.47.47 0 0 0 .12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32a.47.47 0 0 0-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 1 1 12 8.4a3.6 3.6 0 0 1 0 7.2z\",\n 24,\n ),\n apps: () =>\n material(\n \"M6 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 12a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm-6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0-6a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm4-8a2 2 0 1 0 4 0 2 2 0 0 0-4 0zm-4 2a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 6a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 6a2 2 0 1 0 0-4 2 2 0 0 0 0 4z\",\n 24,\n ),\n pencil: () =>\n material(\n \"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z\",\n 24,\n ),\n inbox: () =>\n material(\n \"M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 12h-4a3 3 0 0 1-6 0H5V5h14v10z\",\n 20,\n ),\n star: () =>\n material(\n \"M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z\",\n 20,\n ),\n clock: () =>\n material(\n \"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z\",\n 20,\n ),\n send: () =>\n material(\n \"M3.4 20.4l17.45-7.48a1 1 0 0 0 0-1.84L3.4 3.6a.99.99 0 0 0-1.39.91L2 9.12c0 .5.37.93.87.99L17 12 2.87 13.88c-.5.07-.87.5-.87 1l.01 4.61c0 .71.73 1.2 1.39.91z\",\n 20,\n ),\n draft: () =>\n material(\n \"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z\",\n 20,\n ),\n more: () =>\n material(\n \"M12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 2a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm0 6a2 2 0 1 0 0 4 2 2 0 0 0 0-4z\",\n 20,\n ),\n refresh: () =>\n material(\n \"M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\",\n 20,\n ),\n checkbox: () =>\n material(\n \"M19 5v14H5V5h14m0-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z\",\n 18,\n ),\n back: () => material(\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\", 20),\n archive: () =>\n material(\n \"M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5 6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z\",\n 20,\n ),\n report: () =>\n material(\n \"M15.73 3H8.27L3 8.27v7.46L8.27 21h7.46L21 15.73V8.27L15.73 3zM19 14.9 14.9 19H9.1L5 14.9V9.1L9.1 5h5.8L19 9.1v5.8zM11 7h2v6h-2zm0 8h2v2h-2z\",\n 20,\n ),\n trash: () =>\n material(\n \"M15 4V3H9v1H4v2h1v13a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6h1V4h-5zm2 15H7V6h10v13zM9 8h2v9H9zm4 0h2v9h-2z\",\n 20,\n ),\n reply: () => material(\"M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z\", 20),\n forward: () => material(\"M14 9V5l7 7-7 7v-4.1c-5 0-8.5 1.6-11 5.1 1-5 4-10 11-11z\", 20),\n clip: () =>\n material(\n \"M16.5 6v11.5a4 4 0 0 1-8 0V5a2.5 2.5 0 0 1 5 0v10.5a1 1 0 0 1-2 0V6H10v9.5a2.5 2.5 0 0 0 5 0V5a4 4 0 0 0-8 0v12.5a5.5 5.5 0 0 0 11 0V6h-1.5z\",\n 16,\n ),\n file: () =>\n material(\n \"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm4 18H6V4h7v5h5v11z\",\n 28,\n ),\n dropdown: () => material(\"M7 10l5 5 5-5z\", 18),\n lock: () =>\n material(\n \"M18 8h-1V6A5 5 0 0 0 7 6v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2zM9 6a3 3 0 0 1 6 0v2H9V6z\",\n 14,\n ),\n envelope: () =>\n svg(\n 30,\n 24,\n `<path d=\"M3 4h24v16a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4Z\" fill=\"#e8eaed\"/><path d=\"M3 4l12 9 12-9\" stroke=\"#5f6368\" stroke-width=\"2.2\" stroke-linejoin=\"round\"/><path d=\"M3 4h24\" stroke=\"#5f6368\" stroke-width=\"2.2\"/>`,\n ),\n pin: (size = 16) =>\n material(\n \"M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z\",\n size,\n ),\n bellSlash: (size = 16) =>\n material(\n \"M20 18.69 7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11-.23.09-.46.2-.68.31L18 14.68z\",\n size,\n ),\n volumeOn: (size = 16) =>\n material(\n \"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\",\n size,\n ),\n volumeOff: (size = 16) =>\n material(\n \"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z\",\n size,\n ),\n markUnread: (size = 20) =>\n material(\n \"M22 8.98V18c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2h10.1c-.06.32-.1.66-.1 1 0 1.48.65 2.79 1.67 3.71L12 11 4 6v2l8 5 5.3-3.32c.54.2 1.1.32 1.7.32 1.13 0 2.16-.39 3-1.02zM16 5c0 1.66 1.34 3 3 3s3-1.34 3-3-1.34-3-3-3-3 1.34-3 3z\",\n size,\n ),\n markRead: (size = 20) =>\n material(\n \"M21.99 8c0-.72-.37-1.35-.94-1.7L12 1 2.95 6.3C2.38 6.65 2 7.28 2 8v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2l-.01-10zM12 13 3.74 7.84 12 3l8.26 4.84L12 13z\",\n size,\n ),\n close: (size = 16) =>\n material(\n \"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\",\n size,\n ),\n starFilled: (size = 20) =>\n material(\n \"M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z\",\n size,\n ),\n}\n", "/// <reference lib=\"dom\" />\n/**\n * Alert sounds, synthesized with Web Audio so no audio files ship with the package. Each is a\n * generic tone in the style of its platform: a bright three-note chime for an iPhone text, a\n * glassy two-note chime for an iPhone notification, a soft marimba pluck for Android and a short\n * two-tone ding for new mail. Phones also vibrate where the browser supports it.\n *\n * Browsers only start audio after a user gesture, so `unlock()` runs on the first pointer or key\n * press inside the UI.\n */\nexport type SoundKind = \"sms\" | \"push-ios\" | \"push-android\" | \"email\"\n\nexport type SoundPlayer = {\n play(kind: SoundKind): void\n unlock(): void\n enabled: boolean\n close(): void\n}\n\ntype Ctx = AudioContext\n\n/** A struck bell: a sine with inharmonic partials and an exponential decay. */\nconst bell = (ctx: Ctx, out: AudioNode, at: number, freq: number, length: number, level = 1) => {\n for (const [ratio, gain] of [\n [1, 1],\n [2.76, 0.18],\n [5.4, 0.06],\n ] as const) {\n const osc = ctx.createOscillator()\n const env = ctx.createGain()\n osc.type = \"sine\"\n osc.frequency.value = freq * ratio\n env.gain.setValueAtTime(0, at)\n env.gain.linearRampToValueAtTime(0.32 * gain * level, at + 0.006)\n env.gain.exponentialRampToValueAtTime(0.0001, at + length / ratio ** 0.35)\n osc.connect(env).connect(out)\n osc.start(at)\n osc.stop(at + length + 0.05)\n }\n}\n\n/** A mallet: a triangle through a closing low-pass, short and warm. */\nconst mallet = (ctx: Ctx, out: AudioNode, at: number, freq: number, length: number) => {\n const osc = ctx.createOscillator()\n const filter = ctx.createBiquadFilter()\n const env = ctx.createGain()\n osc.type = \"triangle\"\n osc.frequency.value = freq\n filter.type = \"lowpass\"\n filter.frequency.setValueAtTime(freq * 6, at)\n filter.frequency.exponentialRampToValueAtTime(freq * 1.5, at + length)\n env.gain.setValueAtTime(0, at)\n env.gain.linearRampToValueAtTime(0.42, at + 0.004)\n env.gain.exponentialRampToValueAtTime(0.0001, at + length)\n osc.connect(filter).connect(env).connect(out)\n osc.start(at)\n osc.stop(at + length + 0.05)\n}\n\nconst VOICES: Record<SoundKind, (ctx: Ctx, out: AudioNode, t: number) => void> = {\n sms: (ctx, out, t) => {\n bell(ctx, out, t, 1318.5, 0.5)\n bell(ctx, out, t + 0.12, 1661.2, 0.5)\n bell(ctx, out, t + 0.24, 1975.5, 0.8)\n },\n \"push-ios\": (ctx, out, t) => {\n bell(ctx, out, t, 1760, 0.35, 0.8)\n bell(ctx, out, t + 0.09, 2349.3, 0.9, 0.9)\n },\n \"push-android\": (ctx, out, t) => {\n mallet(ctx, out, t, 1046.5, 0.22)\n mallet(ctx, out, t + 0.1, 1568, 0.22)\n mallet(ctx, out, t + 0.2, 2093, 0.45)\n },\n email: (ctx, out, t) => {\n bell(ctx, out, t, 987.8, 0.45, 0.7)\n bell(ctx, out, t + 0.16, 1318.5, 0.9, 0.7)\n },\n}\n\nconst VIBRATION: Partial<Record<SoundKind, number[]>> = {\n sms: [180, 90, 180],\n \"push-ios\": [120],\n \"push-android\": [60, 80, 60],\n}\n\nexport const createSoundPlayer = (initial = false): SoundPlayer => {\n let ctx: Ctx | null = null\n let master: GainNode | null = null\n const lastPlayed = new Map<SoundKind, number>()\n const ensure = (): Ctx | null => {\n if (ctx) return ctx\n const Ctor =\n globalThis.AudioContext ??\n (globalThis as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!Ctor) return null\n try {\n ctx = new Ctor()\n master = ctx.createGain()\n master.gain.value = 0.6\n master.connect(ctx.destination)\n } catch {\n ctx = null\n }\n return ctx\n }\n const player: SoundPlayer = {\n enabled: initial,\n unlock() {\n if (!player.enabled) return\n const c = ensure()\n if (c?.state === \"suspended\") void c.resume().catch(() => {})\n },\n play(kind) {\n if (!player.enabled) return\n const now = Date.now()\n // One alert per kind per moment, as a phone does for a burst.\n if (now - (lastPlayed.get(kind) ?? 0) < 600) return\n lastPlayed.set(kind, now)\n const pattern = VIBRATION[kind]\n if (pattern) {\n try {\n navigator.vibrate?.(pattern)\n } catch {}\n }\n const c = ensure()\n if (!c || !master) return\n if (c.state === \"suspended\") void c.resume().catch(() => {})\n VOICES[kind](c, master, c.currentTime + 0.02)\n },\n close() {\n void ctx?.close().catch(() => {})\n ctx = null\n },\n }\n return player\n}\n", "/// <reference lib=\"dom\" />\n/**\n * The harness UI. Plain DOM: each device re-renders from `state` when the harness reports a change.\n *\n * `createApp` mounts it on any element. Live, it reads a harness through `fetch` (a Fetch handler\n * for an in-process harness, or HTTP); otherwise it draws the notifications it is given, which is\n * how the Storybook catalog shows every state.\n *\n * Every element a test might look for has a `data-testid` from `../testids.ts`, and every element\n * that shows a notification carries its id, channel, service, recipient, sender and status as\n * `data-*` attributes. Every gesture (swipe, long press) also has a click or key equivalent.\n */\nimport type { Channel, Notification, SourceStatus } from \"../model.js\"\nimport { TEST_IDS, type TestId } from \"../testids.js\"\nimport { icons } from \"./icons.js\"\nimport { createSoundPlayer, type SoundKind } from \"./sounds.js\"\nimport { ROOT_CLASS } from \"./styles.js\"\n\nexport type MotionPreference = \"auto\" | \"full\" | \"reduce\"\n\nexport type AppOptions = {\n /** Read a harness and follow its event stream. Default true. */\n live?: boolean\n /**\n * How to reach the harness: an in-process harness's `fetch`, or any Fetch-compatible function.\n * Default: the global `fetch` against `baseUrl`.\n */\n fetch?: (request: Request) => Promise<Response>\n /** Where the harness API lives. Default: the page's own URL (standalone), or a placeholder origin with `fetch`. */\n baseUrl?: string\n /** Read `?view=\u2026` from the page URL and write `?namespace=` back to it. Default: true only for the standalone page. */\n syncUrl?: boolean\n /** Keep read state in `localStorage`. Default: same as `live`. */\n storage?: boolean\n /** Play alert sounds (and vibrate) on arrivals. Default false; the top bar toggles it. */\n sound?: boolean\n /** Animations. `auto` follows the viewer's reduced-motion setting. Default `auto`. */\n motion?: MotionPreference\n /** Fill the host element and scroll inside it, for a tab or panel in another app. */\n embedded?: boolean\n /** Which devices to show. Default all three. */\n channels?: Channel[]\n /** Show the top bar (namespace, filter, view tabs, sound, emulator status). Default true. */\n chrome?: boolean\n /** Called with each batch of new notifications. */\n onArrive?: (added: Notification[]) => void\n\n /** Fixed notifications to draw when not live. */\n items?: Notification[]\n sources?: SourceStatus[]\n namespaces?: string[]\n namespace?: string\n discoveryError?: string | null\n /** Live-stream indicator when not live. Default true. */\n connected?: boolean\n /** Ids already read (when not live there is no browser storage). */\n seen?: string[]\n /** Ids drawn with their arrival animation. */\n arrived?: string[]\n /** Fixed clock in epoch ms, so relative times and the status bar never move. */\n now?: number\n title?: string\n /** A query string such as `?view=sms&to=\u2026`, as a deep link would carry. */\n search?: string\n initial?: {\n view?: View\n only?: Channel | null\n search?: string\n follow?: boolean\n inspect?: string | null\n mailDetailsOpen?: boolean\n sms?: { to?: string | null; thread?: string | null; menu?: string | null }\n push?: {\n to?: string | null\n skin?: PushSkin\n expanded?: string | null\n preview?: string | null\n settings?: string | null\n openGroups?: string[]\n }\n email?: { to?: string | null; open?: string | null }\n toast?: string | null\n local?: {\n dismissed?: string[]\n pinned?: string[]\n muted?: string[]\n starred?: string[]\n silent?: string[]\n tapbacks?: Record<string, Tapback>\n }\n }\n}\n\nexport type AppHandle = {\n render(): void\n state: { items: Notification[] }\n /** Resolves once the first read has been drawn (`data-ready=\"true\"` on the root). */\n ready: Promise<void>\n /** Open one notification on its device, or a recipient on a device. */\n show(target: { id?: string; channel?: Channel; to?: string }): void\n setNamespace(namespace: string): void\n setSound(on: boolean): void\n /** Stop streams and timers, remove listeners and empty the host. */\n destroy(): void\n}\n\nexport type View = \"devices\" | \"timeline\"\nexport type PushSkin = \"auto\" | \"ios\" | \"android\"\nexport type Tapback = \"heart\" | \"like\" | \"dislike\" | \"laugh\" | \"emphasize\" | \"question\"\n\nconst TAPBACKS: { id: Tapback; glyph: string; label: string }[] = [\n { id: \"heart\", glyph: \"\u2665\", label: \"Heart\" },\n { id: \"like\", glyph: \"\uD83D\uDC4D\", label: \"Like\" },\n { id: \"dislike\", glyph: \"\uD83D\uDC4E\", label: \"Dislike\" },\n { id: \"laugh\", glyph: \"HA<br>HA\", label: \"Laugh\" },\n { id: \"emphasize\", glyph: \"\u203C\", label: \"Emphasize\" },\n { id: \"question\", glyph: \"?\", label: \"Question\" },\n]\n\nconst CHANNELS: Channel[] = [\"sms\", \"push\", \"email\"]\n\nexport function createApp(root: HTMLElement, options: AppOptions = {}): AppHandle {\n const live = options.live ?? true\n const syncUrl = options.syncUrl ?? (live && !options.fetch && !options.baseUrl)\n const storage = options.storage ?? live\n const fixedNow = options.now\n const nowMs = () => fixedNow ?? Date.now()\n const base =\n options.baseUrl ??\n (options.fetch || typeof location === \"undefined\" ? \"http://harness.local/\" : location.href)\n const send = (path: string, init?: RequestInit) => {\n const request = new Request(new URL(path, base), init)\n return options.fetch ? options.fetch(request) : fetch(request)\n }\n const listeners = new AbortController()\n const on = <K extends keyof HTMLElementEventMap>(\n type: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n capture = false,\n ) => root.addEventListener(type, handler, { signal: listeners.signal, capture })\n const timers = new Set<ReturnType<typeof setTimeout>>()\n const later = (ms: number, fn: () => void) => {\n const t = setTimeout(() => {\n timers.delete(t)\n fn()\n }, ms)\n timers.add(t)\n }\n let destroyed = false\n\n const params = new URLSearchParams(\n options.search ?? (syncUrl && typeof location !== \"undefined\" ? location.search : \"\"),\n )\n const initialView = params.get(\"view\")\n const init = options.initial ?? {}\n\n const state = {\n namespace: params.get(\"namespace\") ?? options.namespace ?? \"default\",\n namespaces: [] as string[],\n items: [] as Notification[],\n sources: [] as SourceStatus[],\n discoveryError: null as string | null,\n version: 0,\n view: (initialView === \"timeline\" ? \"timeline\" : \"devices\") as View,\n only: (initialView === \"sms\" || initialView === \"email\" || initialView === \"push\"\n ? initialView\n : null) as Channel | null,\n search: \"\",\n follow: params.get(\"follow\") !== \"0\",\n connected: false,\n sms: {\n to: null as string | null,\n thread: null as string | null,\n menu: null as string | null,\n transition: null as \"push\" | \"back\" | null,\n },\n push: {\n to: null as string | null,\n skin: \"auto\" as PushSkin,\n expanded: null as string | null,\n preview: null as string | null,\n settings: null as string | null,\n wake: false,\n },\n email: { to: null as string | null, open: null as string | null, transition: false },\n inspect: null as string | null,\n toast: null as { text: string; undo?: () => void } | null,\n arrived: new Set<string>(options.arrived ?? []),\n }\n Object.assign(state, {\n ...init,\n sms: { ...state.sms, ...init.sms },\n push: { ...state.push, ...init.push },\n email: { ...state.email, ...init.email },\n toast: init.toast ? { text: init.toast } : null,\n })\n // A view named in the link wins over `initial`.\n if (initialView === \"timeline\") state.view = \"timeline\"\n delete (state as { local?: unknown }).local\n delete (state.push as { openGroups?: unknown }).openGroups\n\n /** What the viewer did on the devices. Never sent to the harness; a reload starts fresh. */\n const local = {\n dismissed: new Set<string>(init.local?.dismissed ?? []),\n pinned: new Set<string>(init.local?.pinned ?? []),\n muted: new Set<string>(init.local?.muted ?? []),\n starred: new Set<string>(init.local?.starred ?? []),\n silent: new Set<string>(init.local?.silent ?? []),\n checked: new Set<string>(),\n tapbacks: new Map<string, Tapback>(Object.entries(init.local?.tapbacks ?? {})),\n openGroups: new Set<string>(init.push?.openGroups ?? []),\n }\n\n let mailDetailsOpen = init.mailDetailsOpen ?? false\n const pinnedTo = params.get(\"to\")\n const pinnedFrom = params.get(\"from\")\n const pinnedId = params.get(\"id\")\n /** A deep link to one recipient keeps that recipient, whatever else arrives. */\n const pinned = pinnedTo && state.only ? { channel: state.only, to: pinnedTo } : null\n if (params.get(\"follow\") === null && pinned) state.follow = false\n\n const reducedMotion = () =>\n typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n const motion = (): \"full\" | \"reduce\" =>\n (options.motion ?? \"auto\") === \"auto\"\n ? reducedMotion()\n ? \"reduce\"\n : \"full\"\n : (options.motion as \"full\" | \"reduce\")\n const animate = () => motion() === \"full\"\n\n const soundKey = \"mockingbird-notifications:sound\"\n const storedSound = (): boolean | null => {\n if (!storage) return null\n try {\n const v = localStorage.getItem(soundKey)\n return v === null ? null : v === \"1\"\n } catch {\n return null\n }\n }\n const sound = createSoundPlayer(\n params.get(\"sound\") !== null\n ? params.get(\"sound\") === \"1\"\n : (storedSound() ?? options.sound ?? false),\n )\n\n // \u2500\u2500 Read state (per namespace, per browser) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const seenKey = () => `mockingbird-notifications:seen:${state.namespace}`\n let seen = new Set<string>(options.seen ?? [])\n const loadSeen = () => {\n if (!storage) return\n try {\n seen = new Set(JSON.parse(localStorage.getItem(seenKey()) ?? \"[]\") as string[])\n } catch {\n seen = new Set()\n }\n }\n const saveSeen = () => {\n if (!storage) return\n try {\n localStorage.setItem(seenKey(), JSON.stringify([...seen].slice(-5000)))\n } catch {}\n }\n const markSeen = (ids: string[]) => {\n let changed = false\n for (const id of ids)\n if (!seen.has(id)) {\n seen.add(id)\n changed = true\n }\n if (changed) saveSeen()\n }\n const markUnseen = (ids: string[]) => {\n for (const id of ids) seen.delete(id)\n saveSeen()\n }\n\n // \u2500\u2500 Formatting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const esc = (value: unknown): string =>\n String(value ?? \"\").replace(/[&<>\"']/g, (c) => `&#${c.charCodeAt(0)};`)\n\n const tid = (id: TestId) => `data-testid=\"${id}\"`\n\n /** The attributes every element showing one notification carries. */\n const nAttrs = (n: Notification) =>\n `data-notification-id=\"${esc(n.id)}\" data-channel=\"${n.channel}\" data-service=\"${esc(n.service)}\" data-source=\"${esc(n.source)}\" data-to=\"${esc(n.to)}\" data-from=\"${esc(n.from.address)}\" data-status=\"${esc(n.status ?? \"\")}\" data-created-at=\"${esc(n.createdAt)}\" data-unread=\"${!seen.has(n.id)}\"`\n\n const linkify = (escaped: string): string =>\n escaped.replace(\n /\\bhttps?:\\/\\/[^\\s<]+[^\\s<.,;:!?)'\"]/g,\n (url) => `<a href=\"${url}\" target=\"_blank\" rel=\"noreferrer\">${url}</a>`,\n )\n\n const formatPhone = (value: string): string => {\n const us = /^\\+1(\\d{3})(\\d{3})(\\d{4})$/.exec(value)\n if (us) return `+1 (${us[1]}) ${us[2]}-${us[3]}`\n return value\n }\n\n const isPhone = (value: string) => /^\\+?\\d[\\d\\s()-]{5,}$/.test(value)\n\n const displaySender = (n: Notification): string =>\n n.from.name ?? (isPhone(n.from.address) ? formatPhone(n.from.address) : n.from.address)\n\n const initials = (value: string): string => {\n const words = value\n .replace(/[^\\p{L}\\p{N}\\s]/gu, \" \")\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n if (!words.length) return \"?\"\n return ((words[0]?.[0] ?? \"\") + (words[1]?.[0] ?? \"\")).toUpperCase()\n }\n\n const AVATAR = [\n \"#1a73e8\",\n \"#e8710a\",\n \"#188038\",\n \"#d93025\",\n \"#9334e6\",\n \"#12b5cb\",\n \"#e52592\",\n \"#f9ab00\",\n \"#5f6368\",\n \"#a142f4\",\n ]\n const APP = [\n \"#ff9500\",\n \"#34c759\",\n \"#5856d6\",\n \"#ff2d55\",\n \"#007aff\",\n \"#af52de\",\n \"#ff3b30\",\n \"#30b0c7\",\n ]\n const hash = (value: string) => [...value].reduce((h, c) => (h * 31 + c.charCodeAt(0)) >>> 0, 7)\n const colorFor = (value: string, palette = AVATAR) =>\n palette[hash(value) % palette.length] ?? \"#5f6368\"\n\n const sameDay = (a: Date, b: Date) => a.toDateString() === b.toDateString()\n const clock = (d: Date) => d.toLocaleTimeString([], { hour: \"numeric\", minute: \"2-digit\" })\n\n /** Messages list: \"9:41 AM\", \"Yesterday\", \"Tuesday\", \"11/2/26\". */\n const iosListTime = (iso: string): string => {\n const d = new Date(iso)\n const now = new Date(nowMs())\n if (sameDay(d, now)) return clock(d)\n const y = new Date(now)\n y.setDate(now.getDate() - 1)\n if (sameDay(d, y)) return \"Yesterday\"\n if (now.getTime() - d.getTime() < 6 * 864e5)\n return d.toLocaleDateString([], { weekday: \"long\" })\n return d.toLocaleDateString([], { year: \"2-digit\", month: \"numeric\", day: \"numeric\" })\n }\n\n /** Conversation stamp: \"Today 9:41 AM\". */\n const iosStamp = (iso: string): string => {\n const d = new Date(iso)\n const now = new Date(nowMs())\n if (sameDay(d, now)) return `<b>Today</b> ${clock(d)}`\n return `<b>${esc(d.toLocaleDateString([], { weekday: \"short\", month: \"short\", day: \"numeric\" }))}</b> at ${clock(d)}`\n }\n\n const ago = (iso: string): string => {\n const s = Math.max(0, (nowMs() - Date.parse(iso)) / 1000)\n if (s < 60) return \"now\"\n if (s < 3600) return `${Math.floor(s / 60)}m ago`\n if (s < 86400) return `${Math.floor(s / 3600)}h ago`\n return `${Math.floor(s / 86400)}d ago`\n }\n\n const androidAgo = (iso: string): string => {\n const s = Math.max(0, (nowMs() - Date.parse(iso)) / 1000)\n if (s < 60) return \"now\"\n if (s < 3600) return `${Math.floor(s / 60)}m`\n if (s < 86400) return `${Math.floor(s / 3600)}h`\n return `${Math.floor(s / 86400)}d`\n }\n\n /** Timeline: \"9:41:00 AM\" today, \"Nov 8, 1:41 PM\" before. */\n const timelineTime = (iso: string): string => {\n const d = new Date(iso)\n if (sameDay(d, new Date(nowMs()))) return d.toLocaleTimeString()\n return d.toLocaleString([], {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n ...(d.getFullYear() === new Date(nowMs()).getFullYear() ? {} : { year: \"numeric\" }),\n })\n }\n\n /** Mail list: \"9:41 AM\" today, \"Nov 9\" this year, \"11/9/25\" before. */\n const mailDate = (iso: string): string => {\n const d = new Date(iso)\n const now = new Date(nowMs())\n if (sameDay(d, now)) return clock(d)\n if (d.getFullYear() === now.getFullYear())\n return d.toLocaleDateString([], { month: \"short\", day: \"numeric\" })\n return d.toLocaleDateString([], { year: \"2-digit\", month: \"numeric\", day: \"numeric\" })\n }\n\n const mailLongDate = (iso: string): string => {\n const d = new Date(iso)\n const full = d.toLocaleString([], {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n })\n return `${full} (${ago(iso) === \"now\" ? \"0 minutes ago\" : ago(iso)})`\n }\n\n const SERVICE_LABEL: Record<string, string> = {\n twilio: \"Twilio\",\n resend: \"Resend\",\n fcm: \"FCM\",\n ses: \"SES\",\n sns: \"SNS\",\n customerio: \"Customer.io\",\n mailosaur: \"Mailosaur\",\n }\n\n // \u2500\u2500 Data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const matchesSearch = (n: Notification) => {\n const q = state.search.trim().toLowerCase()\n if (!q) return true\n return [\n n.to,\n n.from.address,\n n.from.name ?? \"\",\n n.preview,\n n.email?.subject ?? \"\",\n n.push?.title ?? \"\",\n n.source,\n n.service,\n ]\n .join(\"\\n\")\n .toLowerCase()\n .includes(q)\n }\n\n /** What the timeline lists: everything the harness has that matches the filter. */\n const visible = (): Notification[] => state.items.filter(matchesSearch)\n\n /** What a device shows: the same, less what the viewer cleared, archived or deleted. */\n const byChannel = (channel: Channel) =>\n visible().filter((n) => n.channel === channel && !local.dismissed.has(n.id))\n\n /** Recipients newest first, with counts. */\n const recipients = (channel: Channel) => {\n const map = new Map<\n string,\n { to: string; count: number; unread: number; latest: string; platform?: string }\n >()\n for (const n of byChannel(channel)) {\n const entry = map.get(n.to) ?? { to: n.to, count: 0, unread: 0, latest: n.createdAt }\n entry.count++\n if (!seen.has(n.id)) entry.unread++\n if (n.createdAt > entry.latest) entry.latest = n.createdAt\n if (n.push) entry.platform = n.push.platform\n map.set(n.to, entry)\n }\n return [...map.values()].sort((a, b) => (a.latest < b.latest ? 1 : -1))\n }\n\n const ensureSelection = () => {\n for (const channel of CHANNELS) {\n const list = recipients(channel)\n const slot = state[channel]\n if (pinned?.channel === channel) {\n slot.to = pinned.to\n continue\n }\n if (!slot.to || !list.some((r) => r.to === slot.to)) slot.to = list[0]?.to ?? null\n }\n }\n\n const api = async <T>(path: string): Promise<T> => {\n const response = await send(path)\n if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`)\n return (await response.json()) as T\n }\n\n const refresh = async () => {\n const q = new URLSearchParams({ namespace: state.namespace, order: \"newest\" })\n const body = await api<{ notifications: Notification[]; version?: number }>(\n `./api/notifications?${q}`,\n )\n if (destroyed) return\n state.items = body.notifications\n state.version = body.version ?? state.version\n ensureSelection()\n render()\n }\n\n const refreshSources = async () => {\n try {\n const body = await api<{ sources: SourceStatus[]; discoveryError: string | null }>(\n \"./api/sources\",\n )\n state.sources = body.sources\n state.discoveryError = body.discoveryError\n const ns = await api<{ namespaces: string[] }>(\"./api/namespaces\")\n state.namespaces = ns.namespaces\n if (!destroyed) renderTopbar()\n } catch {}\n }\n\n // \u2500\u2500 Arrivals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const threadKey = (to: string, from: string) => `${to}|${from}`\n const groupKey = (to: string, app: string) => `${to}|${app}`\n\n const alertFor = (n: Notification): SoundKind | null => {\n if (n.channel === \"sms\") return local.muted.has(threadKey(n.to, n.from.address)) ? null : \"sms\"\n if (n.channel === \"email\") return \"email\"\n const platform =\n state.push.skin === \"auto\"\n ? n.push?.platform === \"android\"\n ? \"android\"\n : \"ios\"\n : state.push.skin\n if (platform === \"android\" && local.silent.has(n.push?.app ?? \"App\")) return null\n return platform === \"android\" ? \"push-android\" : \"push-ios\"\n }\n\n const onAdded = (added: Notification[]) => {\n for (const n of added) state.arrived.add(n.id)\n const newest = [...added].sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1))\n for (const channel of CHANNELS) {\n const mine = newest.filter((n) => n.channel === channel)\n const latest = pinned?.channel === channel ? mine.find((n) => n.to === pinned.to) : mine[0]\n if (!latest) continue\n if (pinned?.channel === channel) {\n if (channel === \"sms\" && !state.sms.thread) state.sms.thread = latest.from.address\n } else if (state.follow) {\n state[channel].to = latest.to\n if (channel === \"sms\" && state.sms.thread !== null) state.sms.thread = latest.from.address\n }\n if (channel === \"push\" && state.push.to === latest.to) state.push.wake = true\n }\n const kinds = new Set(added.map(alertFor).filter((k): k is SoundKind => k !== null))\n for (const kind of kinds) sound.play(kind)\n options.onArrive?.(added)\n later(1800, () => {\n for (const n of added) state.arrived.delete(n.id)\n })\n }\n\n // \u2500\u2500 Event stream (server-sent events over fetch, so it works in process too) \u2500\u2500\n\n let stream: AbortController | null = null\n const connect = () => {\n stream?.abort()\n const controller = new AbortController()\n stream = controller\n const namespace = state.namespace\n const dispatch = (event: string, data: string) => {\n if (controller.signal.aborted) return\n if (event === \"sources\") {\n state.sources = JSON.parse(data) as SourceStatus[]\n renderTopbar()\n } else if (event === \"change\") {\n const { added, version } = JSON.parse(data) as { added: Notification[]; version: number }\n state.version = version\n onAdded(added)\n void refresh().catch(() => {})\n }\n }\n const run = async () => {\n while (!controller.signal.aborted) {\n try {\n const response = await send(`./api/events?namespace=${encodeURIComponent(namespace)}`, {\n signal: controller.signal,\n headers: { accept: \"text/event-stream\" },\n })\n if (!response.ok || !response.body) throw new Error(`events: HTTP ${response.status}`)\n state.connected = true\n renderTopbar()\n const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()\n let buffer = \"\"\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n buffer += value.replace(/\\r\\n/g, \"\\n\")\n let end = buffer.indexOf(\"\\n\\n\")\n while (end >= 0) {\n const chunk = buffer.slice(0, end)\n buffer = buffer.slice(end + 2)\n let event = \"message\"\n const data: string[] = []\n for (const line of chunk.split(\"\\n\")) {\n if (line.startsWith(\"event:\")) event = line.slice(6).trim()\n else if (line.startsWith(\"data:\")) data.push(line.slice(5).trimStart())\n }\n if (data.length) dispatch(event, data.join(\"\\n\"))\n end = buffer.indexOf(\"\\n\\n\")\n }\n }\n } catch {}\n if (controller.signal.aborted) return\n state.connected = false\n renderTopbar()\n await new Promise((resolve) => later(1000, () => resolve(null)))\n }\n }\n void run()\n }\n\n // \u2500\u2500 Chrome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n root.classList.add(ROOT_CLASS)\n if (options.embedded) root.classList.add(\"embedded\")\n root.dataset.testid = TEST_IDS.app\n root.dataset.ready = \"false\"\n root.innerHTML = `\n<header class=\"topbar\" data-region=\"topbar\" ${tid(TEST_IDS.topbar)}></header>\n<div data-region=\"notice\"></div>\n<div class=\"mbn-scroll\" data-region=\"scroll\">\n<section class=\"devices\" data-region=\"devices\">\n <div class=\"device-col\" data-region=\"col-sms\"></div>\n <div class=\"device-col\" data-region=\"col-push\"></div>\n <div class=\"device-col wide\" data-region=\"col-email\"></div>\n</section>\n<section class=\"timeline\" data-region=\"timeline\" hidden></section>\n</div>\n<aside class=\"inspector\" data-region=\"inspector\" ${tid(TEST_IDS.inspector)} hidden></aside>`\n\n const $ = (region: string) => root.querySelector(`[data-region=\"${region}\"]`) as HTMLElement\n const byTestId = <T extends HTMLElement = HTMLElement>(id: TestId) =>\n root.querySelector<T>(`[data-testid=\"${id}\"]`)\n\n const title = () => options.title ?? root.dataset.title ?? \"Notifications\"\n\n const updateRootAttrs = () => {\n Object.assign(root.dataset, {\n view: state.view,\n namespace: state.namespace,\n count: String(state.items.length),\n version: String(state.version),\n connected: String(state.connected),\n sound: String(sound.enabled),\n motion: motion(),\n })\n }\n\n const renderTopbar = () => {\n updateRootAttrs()\n const bar = $(\"topbar\")\n bar.hidden = options.chrome === false\n if (options.chrome === false) return\n const namespaces = [...new Set([...state.namespaces, state.namespace])].sort()\n const searchFocused = document.activeElement === byTestId(TEST_IDS.search)\n bar.innerHTML = `\n <div class=\"brand\"><span class=\"brand-mark\">${icons.bell()}</span><span>${esc(title())} <small>Mockingbird</small></span></div>\n <label class=\"field\">Namespace <select data-action=\"namespace\" ${tid(TEST_IDS.namespace)} aria-label=\"Namespace\">${namespaces\n .map((ns) => `<option ${ns === state.namespace ? \"selected\" : \"\"}>${esc(ns)}</option>`)\n .join(\"\")}</select></label>\n <div class=\"field search\">${icons.search(\"#9aa1b1\", 14)}<input type=\"search\" data-action=\"search\" ${tid(TEST_IDS.search)} aria-label=\"Filter notifications\" placeholder=\"Filter by recipient, sender, text\u2026\" value=\"${esc(state.search)}\"></div>\n <div class=\"tabs\" role=\"group\" aria-label=\"View\">\n <button data-action=\"view\" data-value=\"devices\" ${tid(TEST_IDS.viewDevices)} aria-pressed=\"${state.view === \"devices\"}\">Devices</button>\n <button data-action=\"view\" data-value=\"timeline\" ${tid(TEST_IDS.viewTimeline)} aria-pressed=\"${state.view === \"timeline\"}\">Timeline <span style=\"opacity:.6\">${state.items.length}</span></button>\n </div>\n <label class=\"toggle\" title=\"Jump each device to the newest arrival\"><input type=\"checkbox\" data-action=\"follow\" ${tid(TEST_IDS.follow)} ${state.follow ? \"checked\" : \"\"}> Follow new</label>\n <button class=\"icon-toggle\" data-action=\"sound\" ${tid(TEST_IDS.sound)} aria-pressed=\"${sound.enabled}\" title=\"Play alert sounds on arrivals\">${sound.enabled ? icons.volumeOn() : icons.volumeOff()}<span>Sound</span></button>\n <span class=\"live\" ${tid(TEST_IDS.connection)} data-state=\"${state.connected ? \"live\" : \"reconnecting\"}\" role=\"status\"><span class=\"dot ${state.connected ? \"ok\" : \"bad\"}\"></span>${state.connected ? \"Live\" : \"Reconnecting\"}</span>\n <div class=\"sources\">${state.sources\n .map(\n (s) =>\n `<span class=\"pill\" title=\"${esc(s.error ?? s.url)}\" ${tid(TEST_IDS.source)} data-name=\"${esc(s.name)}\" data-service=\"${esc(s.service ?? \"\")}\" data-ok=\"${s.ok}\" data-count=\"${s.count}\"><span class=\"dot ${s.ok ? \"ok\" : s.error ? \"bad\" : \"\"}\"></span>${esc(\n s.service && s.name !== s.service\n ? `${s.name} \u00B7 ${SERVICE_LABEL[s.service] ?? s.service}`\n : (SERVICE_LABEL[s.name] ?? s.name),\n )} <b>${s.count}</b></span>`,\n )\n .join(\"\")}</div>`\n if (searchFocused) {\n const input = byTestId<HTMLInputElement>(TEST_IDS.search)\n input?.focus()\n input?.setSelectionRange(input.value.length, input.value.length)\n }\n const failing = state.sources.filter((s) => s.error)\n $(\"notice\").innerHTML =\n state.discoveryError || failing.length\n ? `<div class=\"notice\" ${tid(TEST_IDS.notice)} data-kind=\"error\" role=\"alert\">${state.discoveryError ? `Fleet discovery failed: ${esc(state.discoveryError)}. ` : \"\"}${failing\n .map((s) => `<b>${esc(s.name)}</b>: ${esc(s.error)}`)\n .join(\" \u00B7 \")}</div>`\n : !state.sources.length\n ? `<div class=\"notice\" ${tid(TEST_IDS.notice)} data-kind=\"empty\" style=\"border-color:#3a3f4b;background:#171a21;color:#9aa1b1\">No messaging emulators found yet. Start the fleet with twilio, resend, fcm, ses, sns, customerio or mailosaur.</div>`\n : \"\"\n }\n\n const recipientSelect = (\n channel: Channel,\n label: string,\n testId: TestId,\n format: (to: string) => string,\n ) => {\n const list = recipients(channel)\n const current = state[channel].to\n return `<label class=\"field\">${label}<select data-action=\"recipient\" data-channel=\"${channel}\" ${tid(testId)} aria-label=\"${label}\" ${list.length ? \"\" : \"disabled\"}>${\n list.length\n ? list\n .map(\n (r) =>\n `<option value=\"${esc(r.to)}\" ${r.to === current ? \"selected\" : \"\"}>${r.unread ? \"\u25CF \" : \"\"}${esc(format(r.to))} \u00B7 ${r.count}</option>`,\n )\n .join(\"\")\n : \"<option>No recipients yet</option>\"\n }</select></label>`\n }\n\n const countBadge = (n: number, testId: TestId) =>\n n ? `<span class=\"count\" ${tid(testId)} data-count=\"${n}\">${n}</span>` : \"\"\n\n const statusBar = (light = false) => `\n <div class=\"statusbar ${light ? \"light\" : \"\"}\"><span>${clock(new Date(nowMs())).replace(/\\s?[AP]M$/i, \"\")}</span>\n <span class=\"icons\">${icons.signal()}${icons.wifi()}${icons.battery()}</span></div>`\n\n /** Animate elements with a stable `data-flip` key from where they were to where they are now. */\n const flip = (container: HTMLElement, mutate: () => void) => {\n if (!animate() || typeof container.animate !== \"function\") return mutate()\n const before = new Map<string, number>()\n for (const el of container.querySelectorAll<HTMLElement>(\"[data-flip]\"))\n before.set(el.dataset.flip ?? \"\", el.getBoundingClientRect().top)\n mutate()\n for (const el of container.querySelectorAll<HTMLElement>(\"[data-flip]\")) {\n const top = before.get(el.dataset.flip ?? \"\")\n if (top === undefined) continue\n const dy = top - el.getBoundingClientRect().top\n if (Math.abs(dy) < 1) continue\n el.animate([{ transform: `translateY(${dy}px)` }, { transform: \"none\" }], {\n duration: 380,\n easing: \"cubic-bezier(.2,.9,.25,1)\",\n })\n }\n }\n\n // \u2500\u2500 SMS: Messages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const smsThreads = (to: string) => {\n const map = new Map<string, Notification[]>()\n for (const n of byChannel(\"sms\").filter((n) => n.to === to)) {\n const key = n.from.address\n map.set(key, [...(map.get(key) ?? []), n])\n }\n return [...map.entries()]\n .map(([from, items]) => ({\n from,\n items: items.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)),\n pinned: local.pinned.has(threadKey(to, from)),\n }))\n .sort((a, b) =>\n a.pinned !== b.pinned\n ? a.pinned\n ? -1\n : 1\n : (a.items.at(-1)?.createdAt ?? \"\") < (b.items.at(-1)?.createdAt ?? \"\")\n ? 1\n : -1,\n )\n }\n\n const avatarFor = (n: Notification | undefined) => {\n const name = n ? displaySender(n) : \"\"\n return isPhone(n?.from.address ?? \"\") && !n?.from.name ? icons.person() : esc(initials(name))\n }\n\n /** The one-time code iOS would offer above the keyboard: Verify's, or a 4\u20138 digit run in a code-like text. */\n const otpOf = (n: Notification): string | null =>\n n.sms?.code ??\n (/\\b(code|otp|passcode|pin|verif)/i.test(n.sms?.body ?? \"\")\n ? (/\\b(\\d{4,8})\\b/.exec(n.sms?.body ?? \"\")?.[1] ?? null)\n : null)\n\n const highlightCode = (escaped: string, code: string | null) =>\n code\n ? escaped.replace(new RegExp(`\\\\b${code}\\\\b`), `<span class=\"code\">${code}</span>`)\n : escaped\n\n const tapbackBadge = (id: string) => {\n const t = local.tapbacks.get(id)\n const def = TAPBACKS.find((x) => x.id === t)\n return def\n ? `<span class=\"tapback\" ${tid(TEST_IDS.smsTapback)} data-tapback=\"${def.id}\" aria-label=\"${def.label}\">${def.glyph}</span>`\n : \"\"\n }\n\n const bubbleBody = (n: Notification) => {\n const body = highlightCode(linkify(esc(n.sms?.body ?? \"\")), otpOf(n))\n const media = (n.sms?.media ?? [])\n .map((src) => `<img class=\"media\" src=\"${esc(src)}\" alt=\"\" ${tid(TEST_IDS.smsBubbleMedia)}>`)\n .join(\"\")\n return media + body\n }\n\n const renderSms = () => {\n const to = state.sms.to\n const threads = to ? smsThreads(to) : []\n const unreadTotal = byChannel(\"sms\").filter((n) => !seen.has(n.id)).length\n let screen: string\n const thread = threads.find((t) => t.from === state.sms.thread)\n const transition = state.sms.transition\n state.sms.transition = null\n if (thread && to) {\n markSeen(thread.items.map((n) => n.id))\n const first = thread.items[0]\n const otherUnread = threads\n .filter((t) => t !== thread)\n .reduce((s, t) => s + t.items.filter((n) => !seen.has(n.id)).length, 0)\n let last = 0\n let prevTail = true\n const rows = thread.items\n .map((n, i) => {\n const at = Date.parse(n.createdAt)\n const stamp =\n i === 0 || at - last > 15 * 60_000\n ? `<div class=\"stamp\" ${tid(TEST_IDS.smsTimestamp)}>${i === 0 ? \"Text Message<br>\" : \"\"}${iosStamp(n.createdAt)}</div>`\n : \"\"\n const next = thread.items[i + 1]\n const tail = !next || Date.parse(next.createdAt) - at > 60_000\n const gap = !stamp && prevTail && i > 0\n last = at\n prevTail = tail\n const code = otpOf(n)\n return `${stamp}<div class=\"bubble-row ${gap ? \"gap\" : \"\"}\"><div class=\"bubble ${tail ? \"tail\" : \"\"} ${state.arrived.has(n.id) ? \"arrive\" : \"\"}\" ${nAttrs(n)} ${tid(TEST_IDS.smsBubble)} ${code ? `data-code=\"${esc(code)}\"` : \"\"} data-longpress=\"sms-bubble\" tabindex=\"0\" aria-haspopup=\"menu\">${bubbleBody(n)}${tapbackBadge(n.id)}</div><span class=\"bubble-time\" ${tid(TEST_IDS.smsBubbleTime)} aria-hidden=\"true\">${esc(clock(new Date(at)))}</span></div>`\n })\n .join(\"\")\n const latest = thread.items.at(-1)\n const code = latest ? otpOf(latest) : null\n const menuFor = state.sms.menu ? thread.items.find((n) => n.id === state.sms.menu) : null\n const muted = local.muted.has(threadKey(to, thread.from))\n screen = `<div class=\"convo ${transition === \"push\" && animate() ? \"enter\" : \"\"}\" ${tid(TEST_IDS.smsConversation)} data-from=\"${esc(thread.from)}\">\n <div class=\"convo-nav\"><div class=\"bar\">\n <button class=\"back\" data-action=\"sms-back\" ${tid(TEST_IDS.smsBack)} aria-label=\"Back to Messages\">${icons.chevronBack()}${otherUnread ? `<span class=\"badge\">${otherUnread}</span>` : \"\"}</button>\n <div class=\"avatar\">${avatarFor(first)}</div>\n <div class=\"who\" ${tid(TEST_IDS.smsConversationTitle)}>${esc(first ? displaySender(first) : thread.from)}${muted ? ` <span class=\"flag\">${icons.bellSlash(12)}</span>` : \"\"} ${icons.chevronRight(\"#8e8e93\")}</div>\n </div></div>\n <div class=\"convo-body\" data-swipe=\"sms-convo\">${rows}</div>\n ${code && latest && nowMs() - Date.parse(latest.createdAt) < 10 * 60_000 ? `<button class=\"qt\" data-action=\"sms-copy-code\" data-code=\"${esc(code)}\" ${tid(TEST_IDS.smsCode)}><small>From Messages</small>${esc(code)}</button>` : \"\"}\n <div class=\"convo-input\"><span class=\"plus\">${icons.plus()}</span><span class=\"field-ios\">Text Message<span>${icons.mic()}</span></span></div>\n ${menuFor ? smsMenu(menuFor) : \"\"}\n </div>`\n } else {\n state.sms.thread = null\n state.sms.menu = null\n screen = `<div class=\"msgs ${transition === \"back\" && animate() ? \"enter\" : \"\"}\">\n <div class=\"msgs-nav\"><div class=\"row\"><span>Edit</span>${icons.compose()}</div><h1>Messages</h1>\n <div class=\"ios-search\">${icons.search(\"rgba(60,60,67,.6)\", 15)}<span>Search</span>${icons.mic(\"rgba(60,60,67,.6)\")}</div></div>\n ${\n threads.length && to\n ? `<div class=\"threads\" role=\"list\">${threads\n .map((t) => {\n const latest = t.items.at(-1)\n const unread = t.items.some((n) => !seen.has(n.id))\n const key = threadKey(to, t.from)\n const muted = local.muted.has(key)\n const name = latest ? displaySender(latest) : t.from\n return `<div class=\"swipe thread-swipe\" data-swipe=\"sms-thread\" data-from=\"${esc(t.from)}\" data-flip=\"thread:${esc(t.from)}\" role=\"listitem\">\n <div class=\"swipe-actions\">\n <div class=\"side\"><button class=\"act-unread\" data-action=\"thread-unread\" data-from=\"${esc(t.from)}\" ${tid(TEST_IDS.smsThreadToggleUnread)}>${unread ? icons.markRead(22) : icons.markUnread(22)}${unread ? \"Read\" : \"Unread\"}</button><button class=\"act-pin\" data-action=\"thread-pin\" data-from=\"${esc(t.from)}\" ${tid(TEST_IDS.smsThreadPin)}>${icons.pin(22)}${t.pinned ? \"Unpin\" : \"Pin\"}</button></div>\n <div class=\"side\"><button class=\"act-mute\" data-action=\"thread-mute\" data-from=\"${esc(t.from)}\" ${tid(TEST_IDS.smsThreadMute)}>${icons.bellSlash(22)}${muted ? \"Show Alerts\" : \"Hide Alerts\"}</button><button class=\"act-delete\" data-action=\"thread-delete\" data-from=\"${esc(t.from)}\" ${tid(TEST_IDS.smsThreadDelete)}>${icons.trash()}Delete</button></div>\n </div>\n <div class=\"thread swipe-card\" data-action=\"sms-open\" data-from=\"${esc(t.from)}\" ${tid(TEST_IDS.smsThread)} ${latest ? nAttrs(latest) : \"\"} data-unread=\"${unread}\" data-pinned=\"${t.pinned}\" data-muted=\"${muted}\" role=\"button\" tabindex=\"0\" aria-label=\"${esc(name)}${unread ? \", unread\" : \"\"}\">\n ${unread ? `<span class=\"unread\" ${tid(TEST_IDS.smsThreadUnread)}></span>` : \"\"}<div class=\"avatar\">${avatarFor(latest)}</div>\n <div class=\"text\"><div class=\"top\"><span class=\"name-line\"><span class=\"name\" ${tid(TEST_IDS.smsThreadName)}>${esc(name)}</span>${t.pinned ? `<span class=\"flag\">${icons.pin(13)}</span>` : \"\"}${muted ? `<span class=\"flag\">${icons.bellSlash(13)}</span>` : \"\"}</span><span class=\"when\" ${tid(TEST_IDS.smsThreadTime)}>${esc(iosListTime(latest?.createdAt ?? \"\"))} ${icons.chevronRight()}</span></div>\n <div class=\"preview\" ${tid(TEST_IDS.smsThreadPreview)}>${esc(latest?.sms?.body ?? \"\")}</div></div></div></div>`\n })\n .join(\"\")}</div>`\n : `<div class=\"empty-ios\" ${tid(TEST_IDS.smsEmpty)}>${to ? \"No Messages\" : \"No text messages yet.<br><br>SMS sent through Twilio, SNS, Customer.io or Mailosaur shows up here.\"}</div>`\n }\n </div>`\n }\n const col = $(\"col-sms\")\n flip(col, () => {\n col.innerHTML = `\n <div class=\"device-head\"><h2>Text messages ${countBadge(unreadTotal, TEST_IDS.smsUnreadCount)}</h2>${recipientSelect(\"sms\", \"Phone\", TEST_IDS.smsRecipient, formatPhone)}</div>\n <div class=\"iphone\" ${tid(TEST_IDS.smsDevice)} data-screen=\"${thread ? \"conversation\" : \"threads\"}\" data-recipient=\"${esc(to ?? \"\")}\" data-thread=\"${esc(thread?.from ?? \"\")}\"><div class=\"screen\"><div class=\"island\"></div>${statusBar()}${screen}<div class=\"home-indicator\"></div></div></div>`\n })\n const body = col.querySelector<HTMLElement>(\".convo-body\")\n // Scroll to the newest message, again once the element is attached and laid out.\n const toBottom = () => {\n if (body) body.scrollTop = body.scrollHeight\n }\n toBottom()\n requestAnimationFrame(toBottom)\n }\n\n const smsMenu = (n: Notification) => {\n const current = local.tapbacks.get(n.id)\n return `<div class=\"ios-overlay\" data-action=\"close-overlay\" ${tid(TEST_IDS.smsMenu)} data-notification-id=\"${esc(n.id)}\" role=\"dialog\" aria-label=\"Message actions\">\n <div class=\"tapback-bar\" role=\"toolbar\" aria-label=\"Tapback\">${TAPBACKS.map(\n (t) =>\n `<button data-action=\"tapback\" data-id=\"${esc(n.id)}\" data-value=\"${t.id}\" ${tid(TEST_IDS.smsMenuTapback)} data-tapback=\"${t.id}\" aria-label=\"${t.label}\" aria-pressed=\"${current === t.id}\">${t.glyph}</button>`,\n ).join(\"\")}</div>\n <div class=\"bubble tail\">${bubbleBody(n)}</div>\n <div class=\"ios-menu\" role=\"menu\">\n <button role=\"menuitem\" data-action=\"close-overlay\">Reply</button>\n <button role=\"menuitem\" data-action=\"sms-copy\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.smsMenuCopy)}>Copy</button>\n <button role=\"menuitem\" data-action=\"close-overlay\">Translate</button>\n <button role=\"menuitem\" data-action=\"close-overlay\">More\u2026</button>\n </div></div>`\n }\n\n // \u2500\u2500 Push: lock screen / notification shade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const pushData = (n: Notification) => {\n const data = n.push?.data ?? {}\n const keys = Object.keys(data)\n return keys.length\n ? `<div class=\"note-data\" ${tid(TEST_IDS.pushNotificationData)}>${esc(JSON.stringify(data, null, 2))}</div>`\n : \"\"\n }\n\n const pushMeta = (n: Notification) =>\n `<div class=\"note-meta\" ${tid(TEST_IDS.pushNotificationMeta)}>${esc(SERVICE_LABEL[n.service] ?? n.service)} \u00B7 ${esc(n.status ?? \"accepted\")}${n.push?.badge !== null && n.push?.badge !== undefined ? ` \u00B7 badge ${n.push.badge}` : \"\"}${n.push?.sound ? ` \u00B7 sound ${esc(n.push.sound)}` : \"\"}</div>`\n\n const pushAttrs = (n: Notification, extra: Record<string, string | boolean> = {}) =>\n `${nAttrs(n)} ${tid(TEST_IDS.pushNotification)} data-platform=\"${esc(n.push?.platform ?? \"unknown\")}\" data-app=\"${esc(n.push?.app ?? \"App\")}\" ${Object.entries(\n extra,\n )\n .map(([k, v]) => `data-${k}=\"${esc(v)}\"`)\n .join(\" \")}`\n\n const iosCard = (\n n: Notification,\n opts: { expanded: boolean; stacked?: boolean; extra?: string },\n ) => {\n const p = n.push\n const app = p?.app ?? \"App\"\n const expanded = opts.expanded\n return `<div class=\"ios-note swipe-card ${expanded ? \"expanded\" : \"\"} ${state.arrived.has(n.id) ? \"arrive\" : \"\"}\" data-action=\"push-toggle\" data-id=\"${esc(n.id)}\" ${pushAttrs(n, { expanded, stacked: opts.stacked ?? false })} ${opts.extra ?? \"\"} role=\"button\" tabindex=\"0\" aria-expanded=\"${expanded}\" data-longpress=\"ios-note\">\n <span class=\"app-icon\" style=\"background:${colorFor(app, APP)}\" ${tid(TEST_IDS.pushNotificationApp)} aria-label=\"${esc(app)}\">${esc(initials(app).slice(0, 1))}</span>\n <div class=\"content\">\n <div class=\"line1\"><span class=\"title\" ${tid(TEST_IDS.pushNotificationTitle)}>${esc(p?.title ?? app)}</span><span class=\"time\" ${tid(TEST_IDS.pushNotificationTime)}>${esc(ago(n.createdAt))}</span></div>\n <div class=\"body\" ${tid(TEST_IDS.pushNotificationBody)}>${esc(p?.body ?? \"\")}</div>\n ${expanded && p?.imageUrl ? `<img class=\"hero\" src=\"${esc(p.imageUrl)}\" alt=\"\" ${tid(TEST_IDS.pushNotificationImage)}>` : \"\"}\n ${expanded ? pushData(n) + pushMeta(n) : \"\"}\n </div>\n ${!expanded && p?.imageUrl ? `<img class=\"thumb\" src=\"${esc(p.imageUrl)}\" alt=\"\" ${tid(TEST_IDS.pushNotificationImage)}>` : \"\"}\n </div>`\n }\n\n /** A lock-screen card with the swipe-left actions behind it. */\n const iosNote = (\n n: Notification,\n opts: { stacked?: boolean; hidden?: boolean; top?: boolean } = {},\n ) =>\n `<div class=\"swipe ios-swipe\" data-swipe=\"ios-note\" data-id=\"${esc(n.id)}\" data-flip=\"note:${esc(n.id)}\" ${opts.hidden ? \"hidden\" : \"\"}>\n <div class=\"swipe-actions\"><button data-action=\"push-options\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.pushOptions)}>Options</button><button data-action=\"push-clear\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.pushClear)}>Clear</button></div>\n ${iosCard(n, { expanded: state.push.expanded === n.id, stacked: opts.stacked ?? false, extra: opts.top ? 'data-group-top=\"true\"' : \"\" })}\n </div>`\n\n const androidNote = (n: Notification) => {\n const p = n.push\n const expanded = state.push.expanded === n.id\n const app = p?.app ?? \"App\"\n const settings = state.push.settings === n.id\n const silent = local.silent.has(app)\n return `<div class=\"a-note ${expanded ? \"expanded\" : \"\"} ${state.arrived.has(n.id) ? \"arrive\" : \"\"}\" data-action=\"push-toggle\" data-id=\"${esc(n.id)}\" ${pushAttrs(n, { expanded })} data-swipe=\"a-note\" data-longpress=\"a-note\" data-flip=\"note:${esc(n.id)}\" role=\"button\" tabindex=\"0\" aria-expanded=\"${expanded}\">\n <span class=\"a-icon\" style=\"background:${colorFor(app, APP)}\" aria-hidden=\"true\">${esc(initials(app).slice(0, 1))}</span>\n <div class=\"a-main\">\n <div class=\"a-head\"><span ${tid(TEST_IDS.pushNotificationApp)}>${esc(app)}</span><span>\u2022</span><span ${tid(TEST_IDS.pushNotificationTime)}>${esc(androidAgo(n.createdAt))}</span>${silent ? `<span>${icons.bellSlash(12)}</span>` : \"\"}<span class=\"chev\">${expanded ? icons.collapse() : icons.expand()}</span></div>\n ${p?.title ? `<div class=\"a-title\" ${tid(TEST_IDS.pushNotificationTitle)}>${esc(p.title)}</div>` : \"\"}\n <div class=\"a-body\" ${tid(TEST_IDS.pushNotificationBody)}>${esc(p?.body ?? \"\")}</div>\n ${expanded && p?.imageUrl ? `<img class=\"big-picture\" src=\"${esc(p.imageUrl)}\" alt=\"\" ${tid(TEST_IDS.pushNotificationImage)}>` : \"\"}\n ${expanded ? pushData(n) + pushMeta(n) : \"\"}\n ${\n settings\n ? `<div class=\"a-settings\" ${tid(TEST_IDS.pushSettings)} data-app=\"${esc(app)}\">\n <button class=\"opt\" data-action=\"push-alerting\" data-app=\"${esc(app)}\" data-value=\"alerting\" aria-pressed=\"${!silent}\">${icons.bell()}<span>Default<small>May ring or vibrate based on device settings</small></span></button>\n <button class=\"opt\" data-action=\"push-alerting\" data-app=\"${esc(app)}\" data-value=\"silent\" aria-pressed=\"${silent}\">${icons.bellSlash(18)}<span>Silent<small>No sound or vibration</small></span></button>\n <div class=\"row\"><button data-action=\"close-overlay\">Turn off notifications</button><button data-action=\"close-overlay\">Done</button></div></div>`\n : \"\"\n }\n </div>\n ${!expanded && p?.imageUrl ? `<img class=\"large-icon\" src=\"${esc(p.imageUrl)}\" alt=\"\" ${tid(TEST_IDS.pushNotificationImage)}>` : \"\"}\n </div>`\n }\n\n /** iOS Notification Center groups each app's notifications into a stack. */\n const iosGroups = (to: string, items: Notification[]) => {\n const order: string[] = []\n const groups = new Map<string, Notification[]>()\n for (const n of items) {\n const app = n.push?.app ?? \"App\"\n if (!groups.has(app)) order.push(app)\n groups.set(app, [...(groups.get(app) ?? []), n])\n }\n return order\n .map((app) => {\n const list = groups.get(app) ?? []\n const first = list[0]\n if (!first) return \"\"\n if (list.length === 1) return iosNote(first)\n const key = groupKey(to, app)\n const open = local.openGroups.has(key)\n const rest = list.length - 1\n if (open)\n return `<div class=\"push-group\" ${tid(TEST_IDS.pushGroup)} data-app=\"${esc(app)}\" data-count=\"${list.length}\" data-open=\"true\" data-flip=\"group:${esc(app)}\">\n <div class=\"group-head\"><h3>${esc(app)}</h3><button data-action=\"group-collapse\" data-key=\"${esc(key)}\" ${tid(TEST_IDS.pushGroupCollapse)}>Show less</button><button class=\"x\" data-action=\"group-clear\" data-key=\"${esc(key)}\" data-app=\"${esc(app)}\" aria-label=\"Clear ${esc(app)} notifications\">${icons.close(14)}</button></div>\n ${list.map((n) => iosNote(n)).join(\"\")}</div>`\n return `<div class=\"push-group\" ${tid(TEST_IDS.pushGroup)} data-app=\"${esc(app)}\" data-count=\"${list.length}\" data-open=\"false\" data-flip=\"group:${esc(app)}\">\n ${iosNote(first, { top: true })}\n <button class=\"stack-layers\" data-action=\"group-open\" data-key=\"${esc(key)}\" ${tid(TEST_IDS.pushGroupMore)} data-count=\"${rest}\" aria-label=\"Show ${rest} more notification${rest === 1 ? \"\" : \"s\"}\"><div class=\"ios-stack-more\"></div>${rest > 1 ? '<div class=\"ios-stack-more\"></div>' : \"\"}<div class=\"center-label\" style=\"font-size:13px;margin-top:6px\">${rest} more notification${rest === 1 ? \"\" : \"s\"}</div></button>\n ${list\n .slice(1)\n .map((n) => iosNote(n, { stacked: true, hidden: true }))\n .join(\"\")}</div>`\n })\n .join(\"\")\n }\n\n const renderPush = () => {\n const to = state.push.to\n const items = to ? byChannel(\"push\").filter((n) => n.to === to) : []\n markSeen(items.map((n) => n.id))\n const platform = items[0]?.push?.platform ?? \"unknown\"\n const skin =\n state.push.skin === \"auto\" ? (platform === \"android\" ? \"android\" : \"ios\") : state.push.skin\n const now = new Date(nowMs())\n const unread = byChannel(\"push\").filter((n) => !seen.has(n.id)).length\n const wake = state.push.wake && animate()\n state.push.wake = false\n let device: string\n const deviceAttrs = `${tid(TEST_IDS.pushDevice)} data-skin=\"${skin}\" data-recipient=\"${esc(to ?? \"\")}\" data-platform=\"${esc(platform)}\"`\n if (skin === \"android\") {\n device = `<div class=\"pixel\" ${deviceAttrs}><div class=\"screen\"><div class=\"punch\"></div><div class=\"shade\">\n <div class=\"sb\"><span>${clock(now).replace(/\\s?[AP]M$/i, \"\")}</span><span class=\"icons\">${icons.androidWifi()}${icons.androidSignal()}${icons.androidBattery()}</span></div>\n <div class=\"qs-head\"><span>${esc(now.toLocaleDateString([], { weekday: \"short\", month: \"short\", day: \"numeric\" }))}</span>${icons.gear()}</div>\n <div class=\"qs\"><div class=\"tile on\">${icons.androidWifi()}<div>Internet<small>Mockingbird</small></div></div><div class=\"tile\">${icons.bell()}<div>Do Not Disturb<small>Off</small></div></div>\n <div class=\"tile\">${icons.androidBattery()}<div>Battery Saver<small>Off</small></div></div><div class=\"tile\">${icons.lock()}<div>Screen lock<small>On</small></div></div></div>\n <div class=\"qs-bright\"></div>\n <div class=\"shade-list\" role=\"list\">${items.length ? items.map(androidNote).join(\"\") : `<div class=\"shade-empty\" ${tid(TEST_IDS.pushEmpty)}>No notifications</div>`}</div>\n ${items.length ? `<div class=\"shade-actions\"><button data-action=\"close-overlay\">Manage</button><button data-action=\"push-clear-all\" ${tid(TEST_IDS.pushClearAll)}>Clear all</button></div>` : \"\"}\n <div class=\"gesture\"></div></div></div></div>`\n } else {\n const preview = state.push.preview ? items.find((n) => n.id === state.push.preview) : null\n device = `<div class=\"iphone\" ${deviceAttrs}><div class=\"screen\"><div class=\"island\"></div>${statusBar(true)}<div class=\"lock ${wake ? \"wake\" : \"\"}\">\n <div class=\"padlock\">${icons.padlock()}</div>\n <div class=\"date\">${esc(now.toLocaleDateString([], { weekday: \"long\", month: \"long\", day: \"numeric\" }))}</div>\n <div class=\"clock\">${esc(clock(now).replace(/\\s?[AP]M$/i, \"\"))}</div>\n <div class=\"lock-stack\" role=\"list\">${\n items.length && to\n ? `<div class=\"center-label\">Notification Center</div>${iosGroups(to, items)}`\n : `<div class=\"lock-empty\" ${tid(TEST_IDS.pushEmpty)}>${to ? \"No Older Notifications\" : \"No push notifications yet\"}</div>`\n }</div>\n <div class=\"lock-buttons\"><span>${icons.flashlight()}</span><span>${icons.camera()}</span></div>\n ${\n preview\n ? `<div class=\"ios-overlay dark\" data-action=\"close-overlay\" ${tid(TEST_IDS.pushPreview)} data-notification-id=\"${esc(preview.id)}\" role=\"dialog\" aria-label=\"Notification preview\">${iosCard(preview, { expanded: true })}\n <div class=\"ios-menu\" role=\"menu\"><button role=\"menuitem\" data-action=\"push-clear\" data-id=\"${esc(preview.id)}\">Clear</button><button role=\"menuitem\" data-action=\"close-overlay\">Options</button></div></div>`\n : \"\"\n }\n </div><div class=\"home-indicator light\"></div></div></div>`\n }\n const tokenLabel = (t: string) => {\n const r = recipients(\"push\").find((x) => x.to === t)\n const short = t.length > 22 ? `${t.slice(0, 10)}\u2026${t.slice(-8)}` : t\n return `${short}${r?.platform && r.platform !== \"unknown\" ? ` \u00B7 ${r.platform}` : \"\"}`\n }\n const col = $(\"col-push\")\n flip(col, () => {\n col.innerHTML = `\n <div class=\"device-head\"><h2>Push ${countBadge(unread, TEST_IDS.pushUnreadCount)}</h2>\n <div class=\"controls\">${recipientSelect(\"push\", \"Device\", TEST_IDS.pushRecipient, tokenLabel)}\n <div class=\"seg\" role=\"group\" aria-label=\"Phone\">${([\"auto\", \"ios\", \"android\"] as const)\n .map(\n (s) =>\n `<button data-action=\"skin\" data-value=\"${s}\" ${tid(TEST_IDS.pushSkin)} aria-pressed=\"${state.push.skin === s}\">${s === \"auto\" ? \"Auto\" : s === \"ios\" ? \"iOS\" : \"Android\"}</button>`,\n )\n .join(\"\")}</div></div></div>${device}`\n })\n }\n\n // \u2500\u2500 Email: web mail \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const mailHtml = (n: Notification): string => {\n const html = n.email?.html ?? \"\"\n return `<!doctype html><html><head><meta charset=\"utf-8\"><base target=\"_blank\"><style>body{margin:0;font:14px/1.5 Arial,Helvetica,sans-serif;color:#222;overflow-wrap:anywhere}img{max-width:100%;height:auto}</style></head><body>${html}</body></html>`\n }\n\n const toast = (text: string, undo?: () => void) => {\n state.toast = undo ? { text, undo } : { text }\n renderEmail()\n const current = state.toast\n later(6000, () => {\n if (state.toast === current) {\n state.toast = null\n renderEmail()\n }\n })\n }\n\n const archiveEmail = (id: string, verb: \"archived\" | \"deleted\") => {\n local.dismissed.add(id)\n if (state.email.open === id) {\n state.email.open = null\n state.email.transition = true\n }\n toast(verb === \"archived\" ? \"Conversation archived.\" : \"Conversation moved to Trash.\", () => {\n local.dismissed.delete(id)\n state.toast = null\n renderEmail()\n })\n }\n\n const renderEmail = () => {\n const to = state.email.to\n const items = to ? byChannel(\"email\").filter((n) => n.to === to) : []\n const unread = items.filter((n) => !seen.has(n.id)).length\n const unreadAll = byChannel(\"email\").filter((n) => !seen.has(n.id)).length\n const open = items.find((n) => n.id === state.email.open)\n if (!open) state.email.open = null\n const transition = state.email.transition && animate()\n state.email.transition = false\n const me = to ?? \"you@example.test\"\n let main: string\n if (open) {\n markSeen([open.id])\n const e = open.email\n const sender = open.from.name ?? open.from.address\n const toLine = open.to.toLowerCase() === me.toLowerCase() ? \"me\" : open.to\n const detail = (field: string, label: string, value: string) =>\n `<span>${label}:</span><span ${tid(TEST_IDS.emailDetail)} data-field=\"${field}\">${esc(value)}</span>`\n main = `<div class=\"mail-toolbar\">\n <button class=\"icon-btn\" data-action=\"mail-back\" ${tid(TEST_IDS.emailBack)} title=\"Back to Inbox\" aria-label=\"Back to Inbox\">${icons.back()}</button>\n <button class=\"icon-btn\" data-action=\"mail-archive\" data-id=\"${esc(open.id)}\" ${tid(TEST_IDS.emailArchive)} title=\"Archive\" aria-label=\"Archive\">${icons.archive()}</button><span class=\"icon-btn\">${icons.report()}</span><button class=\"icon-btn\" data-action=\"mail-delete\" data-id=\"${esc(open.id)}\" ${tid(TEST_IDS.emailDelete)} title=\"Delete\" aria-label=\"Delete\">${icons.trash()}</button>\n <button class=\"icon-btn\" data-action=\"mail-unread\" data-id=\"${esc(open.id)}\" ${tid(TEST_IDS.emailMarkUnread)} title=\"Mark as unread\" aria-label=\"Mark as unread\">${icons.markUnread()}</button><span class=\"icon-btn\">${icons.clock()}</span><span class=\"icon-btn\">${icons.more()}</span>\n <span class=\"range\">${items.indexOf(open) + 1} of ${items.length}</span></div>\n <div class=\"reader ${transition ? \"enter\" : \"\"}\" ${nAttrs(open)} ${tid(TEST_IDS.emailOpen)}>\n <div class=\"subject-row\"><h1 ${tid(TEST_IDS.emailSubject)}>${esc(e?.subject || \"(no subject)\")}</h1><span class=\"label-chip\">Inbox</span></div>\n <div class=\"sender-row\">\n <span class=\"mail-avatar\" style=\"background:${colorFor(open.from.address)}\">${esc(initials(sender).slice(0, 1))}</span>\n <div class=\"who\"><b ${tid(TEST_IDS.emailFromName)}>${esc(sender)}</b> <span class=\"addr\">&lt;<span ${tid(TEST_IDS.emailFromAddress)}>${esc(open.from.address)}</span>&gt;</span>\n <div class=\"to\" data-action=\"mail-details\" ${tid(TEST_IDS.emailTo)} role=\"button\" tabindex=\"0\" aria-expanded=\"${mailDetailsOpen}\">to ${esc(toLine)}${e?.cc.length ? `, ${esc(e.cc.join(\", \"))}` : \"\"} ${icons.dropdown()}</div></div>\n <div class=\"meta\"><span ${tid(TEST_IDS.emailDate)}>${esc(mailLongDate(open.createdAt))}</span> <span class=\"icon-btn\">${icons.star()}</span><span class=\"icon-btn\">${icons.reply()}</span><span class=\"icon-btn\">${icons.more()}</span></div>\n </div>\n ${\n mailDetailsOpen\n ? `<div class=\"details\" ${tid(TEST_IDS.emailDetails)}>\n ${detail(\"from\", \"from\", open.from.name ? `${open.from.name} <${open.from.address}>` : open.from.address)}\n ${detail(\"to\", \"to\", open.to)}\n ${e?.cc.length ? detail(\"cc\", \"cc\", e.cc.join(\", \")) : \"\"}\n ${e?.bcc.length ? detail(\"bcc\", \"bcc\", e.bcc.join(\", \")) : \"\"}\n ${e?.replyTo.length ? detail(\"reply-to\", \"reply-to\", e.replyTo.join(\", \")) : \"\"}\n ${detail(\"date\", \"date\", new Date(open.createdAt).toLocaleString())}\n ${detail(\"subject\", \"subject\", e?.subject ?? \"\")}\n ${detail(\"mailed-by\", \"mailed-by\", `${SERVICE_LABEL[open.service] ?? open.service} (${open.source})`)}\n ${detail(\"message-id\", \"message id\", open.providerId)}\n </div>`\n : \"\"\n }\n ${e?.html ? `<iframe class=\"mail-frame\" name=\"email-body\" ${tid(TEST_IDS.emailBody)} data-format=\"html\" sandbox=\"allow-same-origin allow-popups allow-popups-to-escape-sandbox\" title=\"Email body\"></iframe>` : `<div class=\"mail-text\" ${tid(TEST_IDS.emailText)} data-format=\"text\">${linkify(esc(e?.text ?? \"\"))}</div>`}\n ${\n e?.attachments.length\n ? `<div class=\"attachments\" ${tid(TEST_IDS.emailAttachments)} data-count=\"${e.attachments.length}\"><h3>${e.attachments.length} attachment${e.attachments.length === 1 ? \"\" : \"s\"}</h3><div class=\"att-grid\">${e.attachments\n .map(\n (a) =>\n `<div class=\"att\" ${tid(TEST_IDS.emailAttachment)} data-filename=\"${esc(a.filename)}\" data-content-type=\"${esc(a.contentType ?? \"\")}\" data-size=\"${a.size ?? \"\"}\"><div class=\"att-top\">${icons.file()}</div><div class=\"att-name\">${icons.clip()} ${esc(a.filename)}${a.size ? ` <span style=\"color:#5f6368;font-weight:400\">${Math.max(1, Math.round(a.size / 1024))} KB</span>` : \"\"}</div></div>`,\n )\n .join(\"\")}</div></div>`\n : \"\"\n }\n <div class=\"reply-row\"><span>${icons.reply()} Reply</span><span>${icons.forward()} Forward</span></div>\n </div>`\n } else {\n main = `<div class=\"mail-toolbar\"><span class=\"icon-btn\">${icons.checkbox()}</span><button class=\"icon-btn\" data-action=\"refresh\" ${tid(TEST_IDS.emailRefresh)} title=\"Refresh\" aria-label=\"Refresh\">${icons.refresh()}</button><span class=\"icon-btn\">${icons.more()}</span>\n <span class=\"range\">${items.length ? `1\u2013${items.length} of ${items.length}` : \"\"}</span></div>\n <div class=\"cat-tabs\"><div class=\"on\">${icons.inbox()} Primary</div><div>${icons.star()} Promotions</div><div>${icons.send()} Updates</div></div>\n <div class=\"mail-list ${transition ? \"enter\" : \"\"}\" role=\"list\">${\n items.length\n ? items\n .map((n) => {\n const isUnread = !seen.has(n.id)\n const sender = n.from.name ?? n.from.address\n const starred = local.starred.has(n.id)\n return `<div class=\"swipe mrow-swipe\" data-swipe=\"mrow\" data-id=\"${esc(n.id)}\" data-flip=\"row:${esc(n.id)}\" role=\"listitem\">\n <div class=\"swipe-actions\">${icons.archive()}${icons.archive()}</div>\n <div class=\"mrow swipe-card ${isUnread ? \"unread\" : \"\"} ${state.arrived.has(n.id) ? \"arrive\" : \"\"}\" data-action=\"mail-open\" data-id=\"${esc(n.id)}\" ${nAttrs(n)} ${tid(TEST_IDS.emailRow)} data-starred=\"${starred}\" role=\"button\" tabindex=\"0\">\n <span class=\"chk\" data-action=\"mail-check\" data-id=\"${esc(n.id)}\" role=\"checkbox\" aria-checked=\"${local.checked.has(n.id)}\" aria-label=\"Select\">${icons.checkbox()}</span><span class=\"star\" data-action=\"mail-star\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.emailRowStar)} role=\"button\" aria-pressed=\"${starred}\" aria-label=\"${starred ? \"Starred\" : \"Not starred\"}\">${starred ? icons.starFilled() : icons.star()}</span>\n <span class=\"from\" ${tid(TEST_IDS.emailRowFrom)}>${esc(sender)}</span>\n <span class=\"subj\"><span ${tid(TEST_IDS.emailRowSubject)}>${esc(n.email?.subject || \"(no subject)\")}</span> <span class=\"snippet\">- <span ${tid(TEST_IDS.emailRowSnippet)}>${esc(n.preview)}</span></span></span>\n ${n.email?.attachments.length ? `<span class=\"clip\" ${tid(TEST_IDS.emailRowAttachment)}>${icons.clip()}</span>` : \"\"}<span class=\"svc\" ${tid(TEST_IDS.emailRowService)}>${esc(SERVICE_LABEL[n.service] ?? n.service)}</span>\n <span class=\"date\" ${tid(TEST_IDS.emailRowDate)}>${esc(mailDate(n.createdAt))}</span>\n <span class=\"hover-actions\"><button class=\"icon-btn\" data-action=\"mail-archive\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.emailRowArchive)} title=\"Archive\" aria-label=\"Archive\">${icons.archive()}</button><button class=\"icon-btn\" data-action=\"mail-delete\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.emailRowDelete)} title=\"Delete\" aria-label=\"Delete\">${icons.trash()}</button><button class=\"icon-btn\" data-action=\"mail-toggle-read\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.emailRowToggleRead)} title=\"${isUnread ? \"Mark as read\" : \"Mark as unread\"}\" aria-label=\"${isUnread ? \"Mark as read\" : \"Mark as unread\"}\">${isUnread ? icons.markRead() : icons.markUnread()}</button><span class=\"icon-btn\">${icons.clock()}</span></span></div></div>`\n })\n .join(\"\")\n : `<div class=\"mail-empty\" ${tid(TEST_IDS.emailEmpty)}>${icons.envelope()}<b>${to ? \"Your Primary tab is empty.\" : \"No email yet\"}</b>${to ? \"\" : \"Email sent through Resend, SES, SNS, Customer.io or Mailosaur lands here.\"}</div>`\n }</div>`\n }\n const col = $(\"col-email\")\n flip(col, () => {\n col.innerHTML = `\n <div class=\"device-head\"><h2>Email ${countBadge(unreadAll, TEST_IDS.emailUnreadCount)}</h2>${recipientSelect(\"email\", \"Mailbox\", TEST_IDS.emailRecipient, (t) => t)}</div>\n <div class=\"window\" ${tid(TEST_IDS.emailDevice)} data-screen=\"${open ? \"message\" : \"inbox\"}\" data-recipient=\"${esc(to ?? \"\")}\">\n <div class=\"chrome\"><span class=\"lights\"><i></i><i></i><i></i></span><span class=\"omnibox\">${icons.lock()} mail.example.test/#inbox${open ? `/${esc(open.providerId)}` : \"\"}</span></div>\n <div class=\"mail-top\"><span class=\"icon-btn\">${icons.menu()}</span><span class=\"mail-logo\">${icons.envelope()}<span class=\"lbl\">Mail</span></span>\n <div class=\"mail-search\"><span class=\"icon-btn\">${icons.search(\"#444746\", 20)}</span><span class=\"placeholder\">Search mail</span><span class=\"icon-btn\">${icons.tune()}</span></div>\n <div class=\"right\"><span class=\"icon-btn\">${icons.help()}</span><span class=\"icon-btn\">${icons.gear()}</span><span class=\"icon-btn\">${icons.apps()}</span>\n <span class=\"mail-avatar\" title=\"${esc(me)}\" style=\"background:${colorFor(me)}\">${esc(me.slice(0, 1).toUpperCase())}</span></div></div>\n <div class=\"mail-body\">\n <nav class=\"mail-nav\"><button class=\"compose\">${icons.pencil()}<span class=\"lbl\">Compose</span></button>\n <div class=\"nav-item active\">${icons.inbox()}<span class=\"lbl\">Inbox</span><span class=\"n\">${unread || \"\"}</span></div>\n <div class=\"nav-item\">${icons.star()}<span class=\"lbl\">Starred</span></div>\n <div class=\"nav-item\">${icons.clock()}<span class=\"lbl\">Snoozed</span></div>\n <div class=\"nav-item\">${icons.send()}<span class=\"lbl\">Sent</span></div>\n <div class=\"nav-item\">${icons.draft()}<span class=\"lbl\">Drafts</span></div>\n <div class=\"nav-item\">${icons.expand()}<span class=\"lbl\">More</span></div></nav>\n <main class=\"mail-main\">${main}</main>\n </div>\n ${state.toast ? `<div class=\"toast\" ${tid(TEST_IDS.toast)} role=\"status\"><span>${esc(state.toast.text)}</span>${state.toast.undo ? `<button data-action=\"toast-undo\" ${tid(TEST_IDS.toastUndo)}>Undo</button>` : \"\"}<button data-action=\"toast-close\" aria-label=\"Dismiss\">${icons.close(16)}</button></div>` : \"\"}\n </div>`\n })\n const frame = col.querySelector<HTMLIFrameElement>(\".mail-frame\")\n if (frame && open) {\n frame.addEventListener(\"load\", () => {\n const doc = frame.contentDocument\n if (doc) frame.style.height = `${Math.max(120, doc.documentElement.scrollHeight + 8)}px`\n })\n frame.srcdoc = mailHtml(open)\n }\n if (syncUrl && live) {\n const total = CHANNELS.reduce(\n (sum, c) => sum + byChannel(c).filter((n) => !seen.has(n.id)).length,\n 0,\n )\n document.title = `${total ? `(${total}) ` : \"\"}${title()} \u00B7 Mockingbird`\n }\n }\n\n // \u2500\u2500 Timeline + inspector \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const renderTimeline = () => {\n const items = visible()\n $(\"timeline\").innerHTML = items.length\n ? `<table class=\"tl\" ${tid(TEST_IDS.timeline)}><thead><tr><th>Time</th><th>Channel</th><th>Source</th><th>To</th><th>From</th><th>Content</th><th>Status</th></tr></thead><tbody>${items\n .map(\n (\n n,\n ) => `<tr data-action=\"inspect\" data-id=\"${esc(n.id)}\" ${nAttrs(n)} ${tid(TEST_IDS.timelineRow)} tabindex=\"0\">\n <td class=\"mono\" data-col=\"time\">${esc(timelineTime(n.createdAt))}</td>\n <td data-col=\"channel\"><span class=\"chan ${n.channel}\">${n.channel === \"sms\" ? \"SMS\" : n.channel === \"email\" ? \"Email\" : \"Push\"}</span></td>\n <td data-col=\"source\"><span class=\"svc-tag\">${esc(SERVICE_LABEL[n.service] ?? n.service)}${n.source !== n.service ? ` \u00B7 ${esc(n.source)}` : \"\"}</span></td>\n <td class=\"mono\" data-col=\"to\">${esc(n.channel === \"sms\" ? formatPhone(n.to) : n.to.length > 28 ? `${n.to.slice(0, 26)}\u2026` : n.to)}</td>\n <td data-col=\"from\">${esc(displaySender(n))}</td>\n <td class=\"content\" data-col=\"content\">${n.email ? `<b>${esc(n.email.subject)}</b> \u2014 ` : n.push?.title ? `<b>${esc(n.push.title)}</b> \u2014 ` : \"\"}${esc(n.channel === \"email\" ? n.preview.slice(0, 120) : (n.sms?.body ?? n.push?.body ?? n.preview))}</td>\n <td class=\"svc-tag\" data-col=\"status\">${esc(n.status ?? \"\")}</td></tr>`,\n )\n .join(\"\")}</tbody></table>`\n : `<div class=\"empty-tl\" ${tid(TEST_IDS.timelineEmpty)}>Nothing sent in namespace <b>${esc(state.namespace)}</b> yet.</div>`\n }\n\n const renderInspector = () => {\n const n = state.items.find((x) => x.id === state.inspect)\n const el = $(\"inspector\")\n if (!n) {\n el.hidden = true\n el.innerHTML = \"\"\n for (const k of Object.keys(el.dataset))\n if (k !== \"region\" && k !== \"testid\") delete el.dataset[k]\n return\n }\n el.hidden = false\n Object.assign(el.dataset, {\n notificationId: n.id,\n channel: n.channel,\n service: n.service,\n source: n.source,\n to: n.to,\n from: n.from.address,\n status: n.status ?? \"\",\n createdAt: n.createdAt,\n })\n const { raw, ...normalized } = n\n const field = (key: string, label: string, value: string) =>\n `<span>${label}</span><span ${tid(TEST_IDS.inspectorField)} data-field=\"${key}\">${esc(value)}</span>`\n el.innerHTML = `<header><h3 ${tid(TEST_IDS.inspectorTitle)}>${esc(n.channel.toUpperCase())} \u00B7 ${esc(n.email?.subject ?? n.push?.title ?? n.preview.slice(0, 60))}</h3>\n <button class=\"btn\" data-action=\"show-on-device\" data-id=\"${esc(n.id)}\" ${tid(TEST_IDS.inspectorShowOnDevice)}>Show on device</button><button class=\"btn\" data-action=\"close-inspector\" ${tid(TEST_IDS.inspectorClose)}>Close</button></header>\n <div class=\"kv\">${field(\"id\", \"Id\", n.id)}${field(\"providerId\", \"Provider id\", n.providerId)}${field(\"service\", \"Service\", `${n.service} (${n.source})`)}\n ${field(\"namespace\", \"Namespace\", n.namespace)}${field(\"to\", \"To\", n.to)}${field(\"from\", \"From\", n.from.name ? `${n.from.name} <${n.from.address}>` : n.from.address)}\n ${field(\"createdAt\", \"Created\", n.createdAt)}${field(\"status\", \"Status\", n.status ?? \"\u2014\")}</div>\n <pre ${tid(TEST_IDS.inspectorJson)}>${esc(JSON.stringify({ ...normalized, raw }, null, 2))}</pre>`\n }\n\n // \u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const shown = (channel: Channel) =>\n (state.only === null || state.only === channel) &&\n (options.channels === undefined || options.channels.includes(channel))\n\n const render = () => {\n if (destroyed) return\n renderTopbar()\n $(\"devices\").hidden = state.view !== \"devices\"\n $(\"timeline\").hidden = state.view !== \"timeline\"\n for (const channel of CHANNELS) $(`col-${channel}`).hidden = !shown(channel)\n $(\"devices\").classList.toggle(\"solo\", CHANNELS.filter(shown).length === 1)\n if (state.view === \"devices\") {\n if (shown(\"sms\")) renderSms()\n if (shown(\"push\")) renderPush()\n if (shown(\"email\")) renderEmail()\n } else renderTimeline()\n renderInspector()\n }\n\n const showOnDevice = (n: Notification) => {\n state.view = \"devices\"\n state[n.channel].to = n.to\n if (n.channel === \"sms\") state.sms.thread = n.from.address\n if (n.channel === \"email\") state.email.open = n.id\n if (n.channel === \"push\") {\n state.push.expanded = n.id\n local.openGroups.add(groupKey(n.to, n.push?.app ?? \"App\"))\n }\n local.dismissed.delete(n.id)\n state.inspect = null\n render()\n root\n .querySelector(`.devices [data-notification-id=\"${CSS.escape(n.id)}\"]`)\n ?.scrollIntoView({ block: \"nearest\" })\n }\n\n // \u2500\u2500 Dismissal with motion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Collapse an element's height to nothing, then run `then`. */\n const collapse = (el: HTMLElement, then: () => void) => {\n if (!animate()) return then()\n el.style.height = `${el.offsetHeight}px`\n el.classList.add(\"collapsing\")\n void el.offsetHeight\n Object.assign(el.style, {\n height: \"0px\",\n marginTop: \"0px\",\n marginBottom: \"0px\",\n paddingTop: \"0px\",\n paddingBottom: \"0px\",\n opacity: \"0\",\n })\n later(270, then)\n }\n\n /** Slide an element out sideways and collapse its row. */\n const flyOut = (row: HTMLElement, card: HTMLElement, direction: number, then: () => void) => {\n if (!animate()) return then()\n row.classList.add(\"settling\")\n card.classList.add(\"settling\")\n card.style.transform = `translateX(${direction * (row.offsetWidth + 40)}px)`\n card.style.opacity = \"0\"\n later(230, () => collapse(row, then))\n }\n\n const rowFor = (selector: string) => root.querySelector<HTMLElement>(selector)\n const cardIn = (row: HTMLElement) => row.querySelector<HTMLElement>(\".swipe-card\") ?? row\n\n const dismissPush = (id: string, direction = -1) => {\n const row =\n rowFor(`.ios-swipe[data-id=\"${CSS.escape(id)}\"]`) ??\n rowFor(`.a-note[data-id=\"${CSS.escape(id)}\"]`)\n const done = () => {\n local.dismissed.add(id)\n if (state.push.preview === id) state.push.preview = null\n renderPush()\n }\n if (!row) return done()\n flyOut(row, cardIn(row), direction, done)\n }\n\n const deleteThread = (from: string) => {\n const to = state.sms.to\n if (!to) return\n const ids = byChannel(\"sms\")\n .filter((n) => n.to === to && n.from.address === from)\n .map((n) => n.id)\n const row = rowFor(`.thread-swipe[data-from=\"${CSS.escape(from)}\"]`)\n const done = () => {\n for (const id of ids) local.dismissed.add(id)\n renderSms()\n }\n if (!row) return done()\n flyOut(row, cardIn(row), -1, done)\n }\n\n const removeEmail = (id: string, verb: \"archived\" | \"deleted\", direction = -1) => {\n const row = rowFor(`.mrow-swipe[data-id=\"${CSS.escape(id)}\"]`)\n if (!row) return archiveEmail(id, verb)\n flyOut(row, cardIn(row), direction, () => archiveEmail(id, verb))\n }\n\n // \u2500\u2500 Gestures: swipe and long press (pointer events; mouse, pen and touch) \u2500\u2500\u2500\u2500\n\n type Drag = {\n pointerId: number\n x0: number\n y0: number\n t0: number\n dx: number\n axis: \"x\" | \"y\" | null\n swipe: HTMLElement | null\n press: HTMLElement | null\n base: number\n timer: ReturnType<typeof setTimeout> | null\n }\n let drag: Drag | null = null\n let suppressClick = false\n\n const IOS_OPEN = 160\n const THREAD_OPEN = 148\n\n const setOffset = (row: HTMLElement, x: number) => {\n const card = cardIn(row)\n card.style.transform = x ? `translateX(${x}px)` : \"\"\n if (row.dataset.swipe === \"a-note\")\n card.style.opacity = String(1 - Math.min(0.9, Math.abs(x) / row.offsetWidth))\n }\n\n const settle = (row: HTMLElement, x: number, open: boolean) => {\n row.classList.add(\"settling\")\n row.classList.toggle(\"open\", open)\n if (row.dataset.swipe === \"a-note\") row.classList.add(\"settling\")\n setOffset(row, x)\n later(340, () => {\n row.classList.remove(\"settling\", \"swiping\")\n row.dataset.offset = String(x)\n })\n }\n\n const longPress = (el: HTMLElement) => {\n const kind = el.dataset.longpress\n const id = el.dataset.notificationId ?? el.dataset.id ?? \"\"\n if (kind === \"sms-bubble\") {\n state.sms.menu = id\n renderSms()\n try {\n navigator.vibrate?.(10)\n } catch {}\n } else if (kind === \"ios-note\") {\n state.push.preview = id\n renderPush()\n } else if (kind === \"a-note\") {\n state.push.settings = state.push.settings === id ? null : id\n renderPush()\n }\n }\n\n on(\"pointerdown\", (event) => {\n sound.unlock()\n // A gesture's own click comes before the next press. A long press redraws the element under\n // the pointer, so that click may never come; it must not swallow the next tap instead.\n suppressClick = false\n if (event.button !== 0 || !event.isPrimary) return\n const target = event.target as HTMLElement\n if (target.closest(\"select, input, .swipe-actions button, .ios-overlay\")) return\n const swipe = target.closest<HTMLElement>(\"[data-swipe]\")\n const press = target.closest<HTMLElement>(\"[data-longpress]\")\n if (!swipe && !press) return\n drag = {\n pointerId: event.pointerId,\n x0: event.clientX,\n y0: event.clientY,\n t0: performance.now(),\n dx: 0,\n axis: null,\n swipe,\n press,\n base: Number(swipe?.dataset.offset ?? 0),\n timer: null,\n }\n if (press) {\n const current = drag\n current.timer = setTimeout(() => {\n if (drag !== current || current.axis) return\n suppressClick = true\n drag = null\n longPress(press)\n }, 480)\n }\n })\n\n on(\"pointermove\", (event) => {\n if (!drag || event.pointerId !== drag.pointerId) return\n const dx = event.clientX - drag.x0\n const dy = event.clientY - drag.y0\n if (!drag.axis) {\n if (Math.hypot(dx, dy) < 8) return\n drag.axis = Math.abs(dx) > Math.abs(dy) ? \"x\" : \"y\"\n if (drag.timer) clearTimeout(drag.timer)\n if (drag.axis === \"x\" && drag.swipe) {\n drag.swipe.classList.add(\"swiping\")\n drag.swipe.classList.remove(\"settling\")\n cardIn(drag.swipe).classList.remove(\"settling\")\n try {\n drag.swipe.setPointerCapture(event.pointerId)\n } catch {}\n }\n }\n if (drag.axis !== \"x\" || !drag.swipe) return\n event.preventDefault()\n drag.dx = dx\n const kind = drag.swipe.dataset.swipe\n let x = drag.base + dx\n if (kind === \"sms-convo\") {\n const reveal = Math.max(0, Math.min(72, -dx))\n drag.swipe.style.setProperty(\"--reveal\", `${reveal}px`)\n return\n }\n // Rubber band past the end of the actions.\n if (kind === \"ios-note\" && x > 0) x = x * 0.35\n if (kind === \"mrow\" || kind === \"a-note\") x = dx\n setOffset(drag.swipe, x)\n })\n\n const endDrag = (event: PointerEvent) => {\n if (!drag || event.pointerId !== drag.pointerId) return\n const d = drag\n drag = null\n if (d.timer) clearTimeout(d.timer)\n if (d.axis !== \"x\" || !d.swipe) return\n suppressClick = true\n const row = d.swipe\n const kind = row.dataset.swipe\n const width = row.offsetWidth || 1\n const velocity = d.dx / Math.max(1, performance.now() - d.t0)\n const x = d.base + d.dx\n if (kind === \"sms-convo\") {\n row.classList.add(\"settling\")\n row.style.setProperty(\"--reveal\", \"0px\")\n later(320, () => row.classList.remove(\"settling\"))\n } else if (kind === \"ios-note\") {\n const id = row.dataset.id ?? \"\"\n if (x < -width * 0.7 || (velocity < -1.1 && x < -60)) dismissPush(id)\n else if (x < -60) settle(row, -IOS_OPEN, true)\n else if (x > 70) {\n settle(row, 0, false)\n state.push.expanded = id\n renderPush()\n } else settle(row, 0, false)\n } else if (kind === \"a-note\") {\n if (Math.abs(x) > width * 0.4 || Math.abs(velocity) > 0.7)\n dismissPush(row.dataset.id ?? \"\", Math.sign(x) || 1)\n else settle(row, 0, false)\n } else if (kind === \"sms-thread\") {\n const from = row.dataset.from ?? \"\"\n if (x < -width * 0.6) deleteThread(from)\n else if (x < -50) settle(row, -THREAD_OPEN, true)\n else if (x > 50) settle(row, THREAD_OPEN, true)\n else settle(row, 0, false)\n } else if (kind === \"mrow\") {\n if (Math.abs(x) > width * 0.35 || Math.abs(velocity) > 0.8)\n removeEmail(row.dataset.id ?? \"\", \"archived\", Math.sign(x) || 1)\n else settle(row, 0, false)\n }\n }\n on(\"pointerup\", endDrag)\n on(\"pointercancel\", endDrag)\n\n on(\"contextmenu\", (event) => {\n const press = (event.target as HTMLElement).closest<HTMLElement>(\"[data-longpress]\")\n if (!press) return\n event.preventDefault()\n longPress(press)\n })\n\n // \u2500\u2500 Clicks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n on(\n \"click\",\n (event) => {\n if (!suppressClick) return\n suppressClick = false\n event.stopPropagation()\n event.preventDefault()\n },\n true,\n )\n\n const closeOverlays = () => {\n const had = state.sms.menu || state.push.preview || state.push.settings\n state.sms.menu = null\n state.push.preview = null\n state.push.settings = null\n if (had) {\n renderSms()\n renderPush()\n }\n return Boolean(had)\n }\n\n const copy = (text: string) => {\n try {\n void navigator.clipboard?.writeText(text).catch(() => {})\n } catch {}\n }\n\n on(\"click\", (event) => {\n const target = (event.target as HTMLElement).closest<HTMLElement>(\"[data-action]\")\n if (!target) return\n // A tap on an open swipe row closes it instead of acting.\n const openRow = target.closest<HTMLElement>(\".swipe.open\")\n if (openRow && target.classList.contains(\"swipe-card\")) {\n settle(openRow, 0, false)\n return\n }\n const action = target.dataset.action\n const id = target.dataset.id ?? \"\"\n const from = target.dataset.from ?? \"\"\n const smsTo = state.sms.to ?? \"\"\n switch (action) {\n case \"view\":\n state.view = target.dataset.value === \"timeline\" ? \"timeline\" : \"devices\"\n render()\n break\n case \"sound\":\n handle.setSound(!sound.enabled)\n break\n case \"sms-open\":\n state.sms.thread = from || null\n state.sms.transition = \"push\"\n renderSms()\n break\n case \"sms-back\":\n state.sms.thread = null\n state.sms.transition = \"back\"\n renderSms()\n break\n case \"thread-delete\":\n deleteThread(from)\n break\n case \"thread-pin\": {\n const key = threadKey(smsTo, from)\n if (!local.pinned.delete(key)) local.pinned.add(key)\n renderSms()\n break\n }\n case \"thread-mute\": {\n const key = threadKey(smsTo, from)\n if (!local.muted.delete(key)) local.muted.add(key)\n renderSms()\n break\n }\n case \"thread-unread\": {\n const items = byChannel(\"sms\").filter((n) => n.to === smsTo && n.from.address === from)\n if (items.some((n) => !seen.has(n.id))) markSeen(items.map((n) => n.id))\n else {\n const latest = items.at(-1)\n if (latest) markUnseen([latest.id])\n }\n renderSms()\n break\n }\n case \"tapback\": {\n const value = target.dataset.value as Tapback\n if (local.tapbacks.get(id) === value) local.tapbacks.delete(id)\n else local.tapbacks.set(id, value)\n state.sms.menu = null\n renderSms()\n break\n }\n case \"sms-copy\": {\n const n = state.items.find((x) => x.id === id)\n if (n) copy(n.sms?.body ?? \"\")\n state.sms.menu = null\n renderSms()\n break\n }\n case \"sms-copy-code\":\n copy(target.dataset.code ?? \"\")\n break\n case \"close-overlay\":\n closeOverlays()\n break\n case \"push-toggle\": {\n if (target.dataset.groupTop === \"true\") {\n const n = state.items.find((x) => x.id === id)\n if (n) {\n local.openGroups.add(groupKey(n.to, n.push?.app ?? \"App\"))\n renderPush()\n }\n break\n }\n state.push.expanded = state.push.expanded === id ? null : id\n renderPush()\n break\n }\n case \"push-clear\":\n state.push.preview = null\n dismissPush(id)\n break\n case \"push-options\":\n state.push.preview = id\n renderPush()\n break\n case \"push-alerting\": {\n const app = target.dataset.app ?? \"App\"\n if (target.dataset.value === \"silent\") local.silent.add(app)\n else local.silent.delete(app)\n renderPush()\n break\n }\n case \"push-clear-all\": {\n const rows = [...root.querySelectorAll<HTMLElement>(\".shade-list .a-note\")]\n const ids = rows.map((r) => r.dataset.id ?? \"\")\n const done = () => {\n for (const x of ids) local.dismissed.add(x)\n renderPush()\n }\n if (!animate()) return done()\n rows.forEach((row, i) => {\n later(i * 60, () => {\n row.classList.add(\"settling\")\n row.style.transform = `translateX(${row.offsetWidth + 40}px)`\n row.style.opacity = \"0\"\n })\n })\n later(rows.length * 60 + 300, done)\n break\n }\n case \"group-open\":\n local.openGroups.add(target.dataset.key ?? \"\")\n renderPush()\n break\n case \"group-collapse\":\n local.openGroups.delete(target.dataset.key ?? \"\")\n renderPush()\n break\n case \"group-clear\": {\n const app = target.dataset.app ?? \"App\"\n for (const n of byChannel(\"push\"))\n if (n.to === state.push.to && (n.push?.app ?? \"App\") === app) local.dismissed.add(n.id)\n local.openGroups.delete(target.dataset.key ?? \"\")\n renderPush()\n break\n }\n case \"skin\":\n state.push.skin = (target.dataset.value as PushSkin) ?? \"auto\"\n renderPush()\n break\n case \"mail-open\":\n state.email.open = id\n state.email.transition = true\n mailDetailsOpen = false\n renderEmail()\n break\n case \"mail-back\":\n state.email.open = null\n state.email.transition = true\n renderEmail()\n break\n case \"mail-details\":\n mailDetailsOpen = !mailDetailsOpen\n renderEmail()\n break\n case \"mail-archive\":\n removeEmail(id, \"archived\")\n break\n case \"mail-delete\":\n removeEmail(id, \"deleted\")\n break\n case \"mail-unread\":\n markUnseen([id])\n state.email.open = null\n state.email.transition = true\n renderEmail()\n break\n case \"mail-toggle-read\":\n if (seen.has(id)) markUnseen([id])\n else markSeen([id])\n renderEmail()\n break\n case \"mail-star\":\n if (!local.starred.delete(id)) local.starred.add(id)\n renderEmail()\n break\n case \"mail-check\":\n if (!local.checked.delete(id)) local.checked.add(id)\n renderEmail()\n break\n case \"toast-undo\":\n state.toast?.undo?.()\n break\n case \"toast-close\":\n state.toast = null\n renderEmail()\n break\n case \"refresh\":\n void refresh().catch(() => {})\n break\n case \"inspect\":\n state.inspect = id\n renderInspector()\n break\n case \"close-inspector\":\n state.inspect = null\n renderInspector()\n break\n case \"show-on-device\": {\n const n = state.items.find((x) => x.id === id)\n if (n) showOnDevice(n)\n break\n }\n }\n })\n\n on(\"dblclick\", (event) => {\n const target = (event.target as HTMLElement).closest<HTMLElement>(\"[data-notification-id]\")\n if (!target || target.closest(\"[data-region=inspector]\")) return\n state.inspect = target.dataset.notificationId ?? null\n renderInspector()\n })\n\n on(\"change\", (event) => {\n const target = event.target as HTMLElement\n const action = target.dataset.action\n if (action === \"namespace\") handle.setNamespace((target as HTMLSelectElement).value)\n else if (action === \"follow\") state.follow = (target as HTMLInputElement).checked\n else if (action === \"recipient\") {\n const channel = target.dataset.channel as Channel\n state[channel].to = (target as HTMLSelectElement).value\n if (channel === \"sms\") state.sms.thread = null\n if (channel === \"email\") state.email.open = null\n render()\n }\n })\n\n on(\"input\", (event) => {\n const target = event.target as HTMLInputElement\n if (target.dataset.action !== \"search\") return\n state.search = target.value\n ensureSelection()\n render()\n })\n\n on(\"keydown\", (event) => {\n sound.unlock()\n const target = event.target as HTMLElement\n if (event.key === \"Escape\") {\n if (closeOverlays()) return\n if (state.inspect) {\n state.inspect = null\n renderInspector()\n }\n return\n }\n if (target.matches(\"input, select, textarea\")) return\n // Keyboard equivalents of the gestures.\n if (event.key === \"ContextMenu\" || (event.shiftKey && event.key === \"F10\")) {\n const press = target.closest<HTMLElement>(\"[data-longpress]\")\n if (press) {\n event.preventDefault()\n longPress(press)\n }\n return\n }\n if (\n event.key === \"Delete\" ||\n event.key === \"Backspace\" ||\n event.key === \"#\" ||\n event.key === \"e\"\n ) {\n const id = target.dataset.id ?? \"\"\n if (target.matches(\"[data-testid=push-notification]\") && event.key !== \"e\") {\n event.preventDefault()\n dismissPush(id)\n } else if (target.matches(\"[data-testid=sms-thread]\") && event.key !== \"e\") {\n event.preventDefault()\n deleteThread(target.dataset.from ?? \"\")\n } else if (target.matches(\"[data-testid=email-row]\")) {\n event.preventDefault()\n removeEmail(id, event.key === \"e\" ? \"archived\" : \"deleted\")\n }\n return\n }\n if (\n (event.key === \"Enter\" || event.key === \" \") &&\n target.matches(\"[role=button]:not(button)\")\n ) {\n event.preventDefault()\n target.click()\n }\n })\n\n // \u2500\u2500 Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const applyPins = () => {\n if (pinned) {\n state[pinned.channel].to = pinned.to\n if (pinned.channel === \"sms\")\n state.sms.thread = pinnedFrom ?? smsThreads(pinned.to)[0]?.from ?? null\n }\n if (pinnedId) {\n const n = state.items.find((x) => x.id === pinnedId)\n if (n) return showOnDevice(n)\n }\n render()\n }\n\n let markReady: () => void = () => {}\n const ready = new Promise<void>((resolve) => {\n markReady = () => {\n root.dataset.ready = \"true\"\n resolve()\n }\n })\n\n const handle: AppHandle = {\n render,\n state,\n ready,\n show(target) {\n const n = target.id ? state.items.find((x) => x.id === target.id) : undefined\n if (n) return showOnDevice(n)\n if (target.channel) {\n state.view = \"devices\"\n if (target.to) state[target.channel].to = target.to\n if (target.channel === \"sms\" && target.to)\n state.sms.thread = smsThreads(target.to)[0]?.from ?? null\n render()\n }\n },\n setNamespace(namespace) {\n if (namespace === state.namespace) return\n state.namespace = namespace\n state.items = []\n state.sms = { ...state.sms, to: null, thread: null, menu: null }\n state.email = { ...state.email, to: null, open: null }\n state.push.to = null\n loadSeen()\n if (syncUrl) {\n const url = new URL(location.href)\n url.searchParams.set(\"namespace\", namespace)\n history.replaceState(null, \"\", url)\n }\n if (live) {\n connect()\n void refresh().catch(() => {})\n } else render()\n },\n setSound(enabled) {\n sound.enabled = enabled\n if (enabled) sound.unlock()\n if (storage)\n try {\n localStorage.setItem(soundKey, enabled ? \"1\" : \"0\")\n } catch {}\n renderTopbar()\n },\n destroy() {\n destroyed = true\n listeners.abort()\n stream?.abort()\n for (const t of timers) clearTimeout(t)\n timers.clear()\n for (const t of intervals) clearInterval(t)\n sound.close()\n root.innerHTML = \"\"\n root.classList.remove(ROOT_CLASS, \"embedded\")\n },\n }\n\n const intervals: ReturnType<typeof setInterval>[] = []\n\n if (!live) {\n state.items = [...(options.items ?? [])].sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1))\n state.sources = options.sources ?? []\n state.namespaces = options.namespaces ?? []\n state.discoveryError = options.discoveryError ?? null\n state.connected = options.connected ?? true\n ensureSelection()\n applyPins()\n markReady()\n return handle\n }\n\n // Keep relative times and the status-bar clock current, unless the viewer is mid-gesture.\n intervals.push(\n setInterval(() => {\n if (drag || state.sms.menu || state.push.preview || state.push.settings) return\n if (state.view === \"devices\" && !root.querySelector(\".mail-frame\")) render()\n else renderTopbar()\n }, 30_000),\n setInterval(() => void refreshSources(), 5_000),\n )\n\n loadSeen()\n render()\n connect()\n void refreshSources()\n const boot = async () => {\n for (let attempt = 0; !destroyed; attempt++) {\n try {\n await refresh()\n applyPins()\n markReady()\n return\n } catch {\n await new Promise((resolve) =>\n later(Math.min(5000, 500 * 2 ** attempt), () => resolve(null)),\n )\n }\n }\n }\n void boot()\n return handle\n}\n", "/// <reference lib=\"dom\" />\n/**\n * The harness UI as a component, for an app that wants its notifications on a tab or panel of its\n * own page: in the browser, against an in-process harness, with no server and no network.\n *\n * const harness = createNotificationHarness({ services: { twilio, resend, fcm } })\n * const ui = mountNotifications(panel, { harness })\n * await ui.ready\n * ui.destroy()\n *\n * Every rule of the stylesheet is scoped to the UI root (`.mb-notifications`), so it can sit next\n * to the host app's own styles.\n */\nimport type { NotificationHarness } from \"./harness.js\"\nimport { type AppHandle, type AppOptions, createApp } from \"./ui/app.js\"\nimport { ROOT_CLASS, STYLES } from \"./ui/styles.js\"\n\nexport { selectors, TEST_IDS, type TestId } from \"./testids.js\"\nexport {\n type AppHandle,\n type AppOptions,\n createApp,\n type MotionPreference,\n type PushSkin,\n type View,\n} from \"./ui/app.js\"\nexport { ROOT_CLASS, STYLES }\n\nexport type MountOptions = Omit<AppOptions, \"fetch\"> & {\n /** An in-process harness (anything with a Fetch handler). Omit it to read `baseUrl` over HTTP. */\n harness?: Pick<NotificationHarness, \"fetch\">\n /** Any Fetch-compatible transport to the harness API. Ignored when `harness` is given. */\n fetch?: NonNullable<AppOptions[\"fetch\"]>\n /** Add the stylesheet to the host's document or shadow root. Default true. */\n injectStyles?: boolean\n}\n\nconst STYLE_ID = \"mockingbird-notifications-styles\"\n\nconst installStyles = (host: HTMLElement) => {\n const rootNode = host.getRootNode() as Document | ShadowRoot\n const target = rootNode instanceof ShadowRoot ? rootNode : document.head\n if (target.querySelector(`#${STYLE_ID}`)) return\n const style = document.createElement(\"style\")\n style.id = STYLE_ID\n style.textContent = STYLES\n target.append(style)\n}\n\n/**\n * Mount the live harness UI inside `host`. It fills the element and scrolls inside it\n * (`embedded`), and leaves the page URL alone unless `syncUrl` is set.\n */\nexport const mountNotifications = (host: HTMLElement, options: MountOptions = {}): AppHandle => {\n const { harness, injectStyles = true, ...rest } = options\n if (injectStyles) installStyles(host)\n if (harness) rest.fetch = (request) => harness.fetch(request)\n return createApp(host, { embedded: true, syncUrl: false, ...rest })\n}\n"],
5
+ "mappings": ";;;;;;;;;;AACA,IAAM,MAAM,CAAC,GAAW,GAAW,MAAc,QAAQ,OACvD,eAAe,CAAC,aAAa,CAAC,kBAAkB,CAAC,IAAI,CAAC,oCAAoC,KAAK,IAAI,IAAI;AAEzG,IAAM,WAAW,CAAC,GAAW,OAAO,OAClC,eAAe,IAAI,aAAa,IAAI,yEAAyE,CAAC;AAEzG,IAAM,QAAQ;AAAA,EACnB,QAAQ,CAAC,IAAI,mBACX;AAAA,IACE;AAAA,IACA;AAAA,IACA,uDAAuD,CAAC,8DAA8D,CAAC,2DAA2D,CAAC,4DAA4D,CAAC;AAAA,EAClP;AAAA,EACF,MAAM,CAAC,IAAI,mBACT;AAAA,IACE;AAAA,IACA;AAAA,IACA,kHAAkH,CAAC,gIAAgI,CAAC,mFAAmF,CAAC;AAAA,EAC1U;AAAA,EACF,SAAS,CAAC,IAAI,mBACZ;AAAA,IACE;AAAA,IACA;AAAA,IACA,+DAA+D,CAAC,iFAAiF,CAAC,mEAAmE,CAAC;AAAA,EACxN;AAAA,EACF,SAAS,MACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,CAAC,IAAI,gBAAgB,IAAI,OAC/B;AAAA,IACE;AAAA,IACA;AAAA,IACA,eAAe,IAAI,IAAI,SAAS,IAAI,IAAI,QAAQ,IAAI,GAAG,aAAa,CAAC,mBAAmB,IAAI,CAAC,gBAAgB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,aAAa,CAAC,mBAAmB,IAAI,CAAC;AAAA,EACjM;AAAA,EACF,KAAK,CAAC,IAAI,cACR;AAAA,IACE;AAAA,IACA;AAAA,IACA,wDAAwD,CAAC,4DAA4D,CAAC;AAAA,EACxH;AAAA,EACF,cAAc,CAAC,IAAI,cACjB;AAAA,IACE;AAAA,IACA;AAAA,IACA,sCAAsC,CAAC;AAAA,EACzC;AAAA,EACF,aAAa,MACX;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,MACN;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,SAAS,MACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,YAAY,MACV;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,MACN;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,MAAM,SAAS,4CAA4C,EAAE;AAAA,EACrE,UAAU,MAAM,SAAS,6CAA6C,EAAE;AAAA,EACxE,aAAa,MAAM,SAAS,kEAAkE,EAAE;AAAA,EAChG,eAAe,MAAM,SAAS,eAAe,EAAE;AAAA,EAC/C,gBAAgB,MACd;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MAAM,SAAS,iDAAiD,EAAE;AAAA,EACxE,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,MACN;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,MACL;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,MACL;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,MACL;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,SAAS,MACP;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,UAAU,MACR;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MAAM,SAAS,gEAAgE,EAAE;AAAA,EACvF,SAAS,MACP;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,QAAQ,MACN;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,MACL;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,MAAM,SAAS,4DAA4D,EAAE;AAAA,EACpF,SAAS,MAAM,SAAS,4DAA4D,EAAE;AAAA,EACtF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,UAAU,MAAM,SAAS,kBAAkB,EAAE;AAAA,EAC7C,MAAM,MACJ;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,UAAU,MACR;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACF,KAAK,CAAC,OAAO,OACX;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,WAAW,CAAC,OAAO,OACjB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,UAAU,CAAC,OAAO,OAChB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,WAAW,CAAC,OAAO,OACjB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,YAAY,CAAC,OAAO,OAClB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,UAAU,CAAC,OAAO,OAChB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,OAAO,CAAC,OAAO,OACb;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACF,YAAY,CAAC,OAAO,OAClB;AAAA,IACE;AAAA,IACA;AAAA,EACF;AACJ;;;AChOA,IAAM,OAAO,CAAC,KAAU,KAAgB,IAAY,MAAc,QAAgB,QAAQ,MAAM;AAC9F,aAAW,CAAC,OAAO,IAAI,KAAK;AAAA,IAC1B,CAAC,GAAG,CAAC;AAAA,IACL,CAAC,MAAM,IAAI;AAAA,IACX,CAAC,KAAK,IAAI;AAAA,EACZ,GAAY;AACV,UAAM,MAAM,IAAI,iBAAiB;AACjC,UAAM,MAAM,IAAI,WAAW;AAC3B,QAAI,OAAO;AACX,QAAI,UAAU,QAAQ,OAAO;AAC7B,QAAI,KAAK,eAAe,GAAG,EAAE;AAC7B,QAAI,KAAK,wBAAwB,OAAO,OAAO,OAAO,KAAK,IAAK;AAChE,QAAI,KAAK,6BAA6B,MAAQ,KAAK,SAAS,SAAS,IAAI;AACzE,QAAI,QAAQ,GAAG,EAAE,QAAQ,GAAG;AAC5B,QAAI,MAAM,EAAE;AACZ,QAAI,KAAK,KAAK,SAAS,IAAI;AAAA,EAC7B;AACF;AAGA,IAAM,SAAS,CAAC,KAAU,KAAgB,IAAY,MAAc,WAAmB;AACrF,QAAM,MAAM,IAAI,iBAAiB;AACjC,QAAM,SAAS,IAAI,mBAAmB;AACtC,QAAM,MAAM,IAAI,WAAW;AAC3B,MAAI,OAAO;AACX,MAAI,UAAU,QAAQ;AACtB,SAAO,OAAO;AACd,SAAO,UAAU,eAAe,OAAO,GAAG,EAAE;AAC5C,SAAO,UAAU,6BAA6B,OAAO,KAAK,KAAK,MAAM;AACrE,MAAI,KAAK,eAAe,GAAG,EAAE;AAC7B,MAAI,KAAK,wBAAwB,MAAM,KAAK,IAAK;AACjD,MAAI,KAAK,6BAA6B,MAAQ,KAAK,MAAM;AACzD,MAAI,QAAQ,MAAM,EAAE,QAAQ,GAAG,EAAE,QAAQ,GAAG;AAC5C,MAAI,MAAM,EAAE;AACZ,MAAI,KAAK,KAAK,SAAS,IAAI;AAC7B;AAEA,IAAM,SAA2E;AAAA,EAC/E,KAAK,CAAC,KAAK,KAAK,MAAM;AACpB,SAAK,KAAK,KAAK,GAAG,QAAQ,GAAG;AAC7B,SAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG;AACpC,SAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG;AAAA,EACtC;AAAA,EACA,YAAY,CAAC,KAAK,KAAK,MAAM;AAC3B,SAAK,KAAK,KAAK,GAAG,MAAM,MAAM,GAAG;AACjC,SAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,EAC3C;AAAA,EACA,gBAAgB,CAAC,KAAK,KAAK,MAAM;AAC/B,WAAO,KAAK,KAAK,GAAG,QAAQ,IAAI;AAChC,WAAO,KAAK,KAAK,IAAI,KAAK,MAAM,IAAI;AACpC,WAAO,KAAK,KAAK,IAAI,KAAK,MAAM,IAAI;AAAA,EACtC;AAAA,EACA,OAAO,CAAC,KAAK,KAAK,MAAM;AACtB,SAAK,KAAK,KAAK,GAAG,OAAO,MAAM,GAAG;AAClC,SAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,EAC3C;AACF;AAEA,IAAM,YAAkD;AAAA,EACtD,KAAK,CAAC,KAAK,IAAI,GAAG;AAAA,EAClB,YAAY,CAAC,GAAG;AAAA,EAChB,gBAAgB,CAAC,IAAI,IAAI,EAAE;AAC7B;AAEO,IAAM,oBAAoB,CAAC,UAAU,UAAuB;AACjE,MAAI,MAAkB;AACtB,MAAI,SAA0B;AAC9B,QAAM,aAAa,oBAAI,IAAuB;AAC9C,QAAM,SAAS,MAAkB;AAC/B,QAAI,IAAK,QAAO;AAChB,UAAM,OACJ,WAAW,gBACV,WAA4D;AAC/D,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI;AACF,YAAM,IAAI,KAAK;AACf,eAAS,IAAI,WAAW;AACxB,aAAO,KAAK,QAAQ;AACpB,aAAO,QAAQ,IAAI,WAAW;AAAA,IAChC,QAAQ;AACN,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAsB;AAAA,IAC1B,SAAS;AAAA,IACT,SAAS;AACP,UAAI,CAAC,OAAO,QAAS;AACrB,YAAM,IAAI,OAAO;AACjB,UAAI,GAAG,UAAU,YAAa,MAAK,EAAE,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9D;AAAA,IACA,KAAK,MAAM;AACT,UAAI,CAAC,OAAO,QAAS;AACrB,YAAM,MAAM,KAAK,IAAI;AAErB,UAAI,OAAO,WAAW,IAAI,IAAI,KAAK,KAAK,IAAK;AAC7C,iBAAW,IAAI,MAAM,GAAG;AACxB,YAAM,UAAU,UAAU,IAAI;AAC9B,UAAI,SAAS;AACX,YAAI;AACF,oBAAU,UAAU,OAAO;AAAA,QAC7B,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,YAAM,IAAI,OAAO;AACjB,UAAI,CAAC,KAAK,CAAC,OAAQ;AACnB,UAAI,EAAE,UAAU,YAAa,MAAK,EAAE,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC3D,aAAO,IAAI,EAAE,GAAG,QAAQ,EAAE,cAAc,IAAI;AAAA,IAC9C;AAAA,IACA,QAAQ;AACN,WAAK,KAAK,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAChC,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;;;AC1BA,IAAM,WAA4D;AAAA,EAChE,EAAE,IAAI,SAAS,OAAO,UAAK,OAAO,QAAQ;AAAA,EAC1C,EAAE,IAAI,QAAQ,OAAO,aAAM,OAAO,OAAO;AAAA,EACzC,EAAE,IAAI,WAAW,OAAO,aAAM,OAAO,UAAU;AAAA,EAC/C,EAAE,IAAI,SAAS,OAAO,YAAY,OAAO,QAAQ;AAAA,EACjD,EAAE,IAAI,aAAa,OAAO,UAAK,OAAO,YAAY;AAAA,EAClD,EAAE,IAAI,YAAY,OAAO,KAAK,OAAO,WAAW;AAClD;AAEA,IAAM,WAAsB,CAAC,OAAO,QAAQ,OAAO;AAE5C,SAAS,UAAU,MAAmB,UAAsB,CAAC,GAAc;AAChF,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,UAAU,QAAQ,YAAY,QAAQ,CAAC,QAAQ,SAAS,CAAC,QAAQ;AACvE,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW,QAAQ;AACzB,QAAM,QAAQ,MAAM,YAAY,KAAK,IAAI;AACzC,QAAM,OACJ,QAAQ,YACP,QAAQ,SAAS,OAAO,aAAa,cAAc,0BAA0B,SAAS;AACzF,QAAM,OAAO,CAAC,MAAcA,UAAuB;AACjD,UAAM,UAAU,IAAI,QAAQ,IAAI,IAAI,MAAM,IAAI,GAAGA,KAAI;AACrD,WAAO,QAAQ,QAAQ,QAAQ,MAAM,OAAO,IAAI,MAAM,OAAO;AAAA,EAC/D;AACA,QAAM,YAAY,IAAI,gBAAgB;AACtC,QAAM,KAAK,CACT,MACA,SACA,UAAU,UACP,KAAK,iBAAiB,MAAM,SAAS,EAAE,QAAQ,UAAU,QAAQ,QAAQ,CAAC;AAC/E,QAAM,SAAS,oBAAI,IAAmC;AACtD,QAAM,QAAQ,CAAC,IAAY,OAAmB;AAC5C,UAAM,IAAI,WAAW,MAAM;AACzB,aAAO,OAAO,CAAC;AACf,SAAG;AAAA,IACL,GAAG,EAAE;AACL,WAAO,IAAI,CAAC;AAAA,EACd;AACA,MAAI,YAAY;AAEhB,QAAM,SAAS,IAAI;AAAA,IACjB,QAAQ,WAAW,WAAW,OAAO,aAAa,cAAc,SAAS,SAAS;AAAA,EACpF;AACA,QAAM,cAAc,OAAO,IAAI,MAAM;AACrC,QAAM,OAAO,QAAQ,WAAW,CAAC;AAEjC,QAAM,QAAQ;AAAA,IACZ,WAAW,OAAO,IAAI,WAAW,KAAK,QAAQ,aAAa;AAAA,IAC3D,YAAY,CAAC;AAAA,IACb,OAAO,CAAC;AAAA,IACR,SAAS,CAAC;AAAA,IACV,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,MAAO,gBAAgB,aAAa,aAAa;AAAA,IACjD,MAAO,gBAAgB,SAAS,gBAAgB,WAAW,gBAAgB,SACvE,cACA;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ,OAAO,IAAI,QAAQ,MAAM;AAAA,IACjC,WAAW;AAAA,IACX,KAAK;AAAA,MACH,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,YAAY;AAAA,IACd;AAAA,IACA,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AAAA,IACA,OAAO,EAAE,IAAI,MAAuB,MAAM,MAAuB,YAAY,MAAM;AAAA,IACnF,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS,IAAI,IAAY,QAAQ,WAAW,CAAC,CAAC;AAAA,EAChD;AACA,SAAO,OAAO,OAAO;AAAA,IACnB,GAAG;AAAA,IACH,KAAK,EAAE,GAAG,MAAM,KAAK,GAAG,KAAK,IAAI;AAAA,IACjC,MAAM,EAAE,GAAG,MAAM,MAAM,GAAG,KAAK,KAAK;AAAA,IACpC,OAAO,EAAE,GAAG,MAAM,OAAO,GAAG,KAAK,MAAM;AAAA,IACvC,OAAO,KAAK,QAAQ,EAAE,MAAM,KAAK,MAAM,IAAI;AAAA,EAC7C,CAAC;AAED,MAAI,gBAAgB,WAAY,OAAM,OAAO;AAC7C,SAAQ,MAA8B;AACtC,SAAQ,MAAM,KAAkC;AAGhD,QAAM,QAAQ;AAAA,IACZ,WAAW,IAAI,IAAY,KAAK,OAAO,aAAa,CAAC,CAAC;AAAA,IACtD,QAAQ,IAAI,IAAY,KAAK,OAAO,UAAU,CAAC,CAAC;AAAA,IAChD,OAAO,IAAI,IAAY,KAAK,OAAO,SAAS,CAAC,CAAC;AAAA,IAC9C,SAAS,IAAI,IAAY,KAAK,OAAO,WAAW,CAAC,CAAC;AAAA,IAClD,QAAQ,IAAI,IAAY,KAAK,OAAO,UAAU,CAAC,CAAC;AAAA,IAChD,SAAS,oBAAI,IAAY;AAAA,IACzB,UAAU,IAAI,IAAqB,OAAO,QAAQ,KAAK,OAAO,YAAY,CAAC,CAAC,CAAC;AAAA,IAC7E,YAAY,IAAI,IAAY,KAAK,MAAM,cAAc,CAAC,CAAC;AAAA,EACzD;AAEA,MAAI,kBAAkB,KAAK,mBAAmB;AAC9C,QAAM,WAAW,OAAO,IAAI,IAAI;AAChC,QAAM,aAAa,OAAO,IAAI,MAAM;AACpC,QAAM,WAAW,OAAO,IAAI,IAAI;AAEhC,QAAM,SAAS,YAAY,MAAM,OAAO,EAAE,SAAS,MAAM,MAAM,IAAI,SAAS,IAAI;AAChF,MAAI,OAAO,IAAI,QAAQ,MAAM,QAAQ,OAAQ,OAAM,SAAS;AAE5D,QAAM,gBAAgB,MACpB,OAAO,eAAe,cAAc,WAAW,kCAAkC,EAAE;AACrF,QAAM,SAAS,OACZ,QAAQ,UAAU,YAAY,SAC3B,cAAc,IACZ,WACA,SACD,QAAQ;AACf,QAAM,UAAU,MAAM,OAAO,MAAM;AAEnC,QAAM,WAAW;AACjB,QAAM,cAAc,MAAsB;AACxC,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI;AACF,YAAM,IAAI,aAAa,QAAQ,QAAQ;AACvC,aAAO,MAAM,OAAO,OAAO,MAAM;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO,IAAI,OAAO,MAAM,OACpB,OAAO,IAAI,OAAO,MAAM,MACvB,YAAY,KAAK,QAAQ,SAAS;AAAA,EACzC;AAIA,QAAM,UAAU,MAAM,kCAAkC,MAAM,SAAS;AACvE,MAAI,OAAO,IAAI,IAAY,QAAQ,QAAQ,CAAC,CAAC;AAC7C,QAAM,WAAW,MAAM;AACrB,QAAI,CAAC,QAAS;AACd,QAAI;AACF,aAAO,IAAI,IAAI,KAAK,MAAM,aAAa,QAAQ,QAAQ,CAAC,KAAK,IAAI,CAAa;AAAA,IAChF,QAAQ;AACN,aAAO,oBAAI,IAAI;AAAA,IACjB;AAAA,EACF;AACA,QAAM,WAAW,MAAM;AACrB,QAAI,CAAC,QAAS;AACd,QAAI;AACF,mBAAa,QAAQ,QAAQ,GAAG,KAAK,UAAU,CAAC,GAAG,IAAI,EAAE,MAAM,IAAK,CAAC,CAAC;AAAA,IACxE,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,QAAM,WAAW,CAAC,QAAkB;AAClC,QAAI,UAAU;AACd,eAAW,MAAM;AACf,UAAI,CAAC,KAAK,IAAI,EAAE,GAAG;AACjB,aAAK,IAAI,EAAE;AACX,kBAAU;AAAA,MACZ;AACF,QAAI,QAAS,UAAS;AAAA,EACxB;AACA,QAAM,aAAa,CAAC,QAAkB;AACpC,eAAW,MAAM,IAAK,MAAK,OAAO,EAAE;AACpC,aAAS;AAAA,EACX;AAIA,QAAM,MAAM,CAAC,UACX,OAAO,SAAS,EAAE,EAAE,QAAQ,YAAY,CAAC,MAAM,KAAK,EAAE,WAAW,CAAC,CAAC,GAAG;AAExE,QAAM,MAAM,CAAC,OAAe,gBAAgB,EAAE;AAG9C,QAAM,SAAS,CAAC,MACd,yBAAyB,IAAI,EAAE,EAAE,CAAC,mBAAmB,EAAE,OAAO,mBAAmB,IAAI,EAAE,OAAO,CAAC,kBAAkB,IAAI,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,EAAE,KAAK,OAAO,CAAC,kBAAkB,IAAI,EAAE,UAAU,EAAE,CAAC,sBAAsB,IAAI,EAAE,SAAS,CAAC,kBAAkB,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;AAEtS,QAAM,UAAU,CAAC,YACf,QAAQ;AAAA,IACN;AAAA,IACA,CAAC,QAAQ,YAAY,GAAG,sCAAsC,GAAG;AAAA,EACnE;AAEF,QAAM,cAAc,CAAC,UAA0B;AAC7C,UAAM,KAAK,6BAA6B,KAAK,KAAK;AAClD,QAAI,GAAI,QAAO,OAAO,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;AAC9C,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,CAAC,UAAkB,uBAAuB,KAAK,KAAK;AAEpE,QAAM,gBAAgB,CAAC,MACrB,EAAE,KAAK,SAAS,QAAQ,EAAE,KAAK,OAAO,IAAI,YAAY,EAAE,KAAK,OAAO,IAAI,EAAE,KAAK;AAEjF,QAAM,WAAW,CAAC,UAA0B;AAC1C,UAAM,QAAQ,MACX,QAAQ,qBAAqB,GAAG,EAChC,KAAK,EACL,MAAM,KAAK,EACX,OAAO,OAAO;AACjB,QAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,aAAS,MAAM,CAAC,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY;AAAA,EACrE;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,MAAM;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,OAAO,CAAC,UAAkB,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,GAAG,MAAO,IAAI,KAAK,EAAE,WAAW,CAAC,MAAO,GAAG,CAAC;AAC/F,QAAM,WAAW,CAAC,OAAe,UAAU,WACzC,QAAQ,KAAK,KAAK,IAAI,QAAQ,MAAM,KAAK;AAE3C,QAAM,UAAU,CAAC,GAAS,MAAY,EAAE,aAAa,MAAM,EAAE,aAAa;AAC1E,QAAM,QAAQ,CAAC,MAAY,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAG1F,QAAM,cAAc,CAAC,QAAwB;AAC3C,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,UAAM,MAAM,IAAI,KAAK,MAAM,CAAC;AAC5B,QAAI,QAAQ,GAAG,GAAG,EAAG,QAAO,MAAM,CAAC;AACnC,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,MAAE,QAAQ,IAAI,QAAQ,IAAI,CAAC;AAC3B,QAAI,QAAQ,GAAG,CAAC,EAAG,QAAO;AAC1B,QAAI,IAAI,QAAQ,IAAI,EAAE,QAAQ,IAAI,IAAI;AACpC,aAAO,EAAE,mBAAmB,CAAC,GAAG,EAAE,SAAS,OAAO,CAAC;AACrD,WAAO,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,OAAO,WAAW,KAAK,UAAU,CAAC;AAAA,EACvF;AAGA,QAAM,WAAW,CAAC,QAAwB;AACxC,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,UAAM,MAAM,IAAI,KAAK,MAAM,CAAC;AAC5B,QAAI,QAAQ,GAAG,GAAG,EAAG,QAAO,gBAAgB,MAAM,CAAC,CAAC;AACpD,WAAO,MAAM,IAAI,EAAE,mBAAmB,CAAC,GAAG,EAAE,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC;AAAA,EACrH;AAEA,QAAM,MAAM,CAAC,QAAwB;AACnC,UAAM,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,MAAM,GAAG,KAAK,GAAI;AACxD,QAAI,IAAI,GAAI,QAAO;AACnB,QAAI,IAAI,KAAM,QAAO,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;AAC1C,QAAI,IAAI,MAAO,QAAO,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC;AAC7C,WAAO,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC;AAAA,EACjC;AAEA,QAAM,aAAa,CAAC,QAAwB;AAC1C,UAAM,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,MAAM,GAAG,KAAK,GAAI;AACxD,QAAI,IAAI,GAAI,QAAO;AACnB,QAAI,IAAI,KAAM,QAAO,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;AAC1C,QAAI,IAAI,MAAO,QAAO,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC;AAC7C,WAAO,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC;AAAA,EACjC;AAGA,QAAM,eAAe,CAAC,QAAwB;AAC5C,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,QAAI,QAAQ,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,EAAG,QAAO,EAAE,mBAAmB;AAC/D,WAAO,EAAE,eAAe,CAAC,GAAG;AAAA,MAC1B,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,GAAI,EAAE,YAAY,MAAM,IAAI,KAAK,MAAM,CAAC,EAAE,YAAY,IAAI,CAAC,IAAI,EAAE,MAAM,UAAU;AAAA,IACnF,CAAC;AAAA,EACH;AAGA,QAAM,WAAW,CAAC,QAAwB;AACxC,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,UAAM,MAAM,IAAI,KAAK,MAAM,CAAC;AAC5B,QAAI,QAAQ,GAAG,GAAG,EAAG,QAAO,MAAM,CAAC;AACnC,QAAI,EAAE,YAAY,MAAM,IAAI,YAAY;AACtC,aAAO,EAAE,mBAAmB,CAAC,GAAG,EAAE,OAAO,SAAS,KAAK,UAAU,CAAC;AACpE,WAAO,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,OAAO,WAAW,KAAK,UAAU,CAAC;AAAA,EACvF;AAEA,QAAM,eAAe,CAAC,QAAwB;AAC5C,UAAM,IAAI,IAAI,KAAK,GAAG;AACtB,UAAM,OAAO,EAAE,eAAe,CAAC,GAAG;AAAA,MAChC,SAAS;AAAA,MACT,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,QAAQ,kBAAkB,IAAI,GAAG,CAAC;AAAA,EACpE;AAEA,QAAM,gBAAwC;AAAA,IAC5C,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AAIA,QAAM,gBAAgB,CAAC,MAAoB;AACzC,UAAM,IAAI,MAAM,OAAO,KAAK,EAAE,YAAY;AAC1C,QAAI,CAAC,EAAG,QAAO;AACf,WAAO;AAAA,MACL,EAAE;AAAA,MACF,EAAE,KAAK;AAAA,MACP,EAAE,KAAK,QAAQ;AAAA,MACf,EAAE;AAAA,MACF,EAAE,OAAO,WAAW;AAAA,MACpB,EAAE,MAAM,SAAS;AAAA,MACjB,EAAE;AAAA,MACF,EAAE;AAAA,IACJ,EACG,KAAK,IAAI,EACT,YAAY,EACZ,SAAS,CAAC;AAAA,EACf;AAGA,QAAM,UAAU,MAAsB,MAAM,MAAM,OAAO,aAAa;AAGtE,QAAM,YAAY,CAAC,YACjB,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,WAAW,CAAC,MAAM,UAAU,IAAI,EAAE,EAAE,CAAC;AAG7E,QAAM,aAAa,CAAC,YAAqB;AACvC,UAAM,MAAM,oBAAI,IAGd;AACF,eAAW,KAAK,UAAU,OAAO,GAAG;AAClC,YAAM,QAAQ,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE,UAAU;AACpF,YAAM;AACN,UAAI,CAAC,KAAK,IAAI,EAAE,EAAE,EAAG,OAAM;AAC3B,UAAI,EAAE,YAAY,MAAM,OAAQ,OAAM,SAAS,EAAE;AACjD,UAAI,EAAE,KAAM,OAAM,WAAW,EAAE,KAAK;AACpC,UAAI,IAAI,EAAE,IAAI,KAAK;AAAA,IACrB;AACA,WAAO,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,SAAS,EAAE,SAAS,IAAI,EAAG;AAAA,EACxE;AAEA,QAAM,kBAAkB,MAAM;AAC5B,eAAW,WAAW,UAAU;AAC9B,YAAM,OAAO,WAAW,OAAO;AAC/B,YAAM,OAAO,MAAM,OAAO;AAC1B,UAAI,QAAQ,YAAY,SAAS;AAC/B,aAAK,KAAK,OAAO;AACjB;AAAA,MACF;AACA,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,EAAG,MAAK,KAAK,KAAK,CAAC,GAAG,MAAM;AAAA,IAChF;AAAA,EACF;AAEA,QAAM,MAAM,OAAU,SAA6B;AACjD,UAAM,WAAW,MAAM,KAAK,IAAI;AAChC,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,GAAG,IAAI,UAAU,SAAS,MAAM,EAAE;AACpE,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAEA,QAAM,UAAU,YAAY;AAC1B,UAAM,IAAI,IAAI,gBAAgB,EAAE,WAAW,MAAM,WAAW,OAAO,SAAS,CAAC;AAC7E,UAAM,OAAO,MAAM;AAAA,MACjB,uBAAuB,CAAC;AAAA,IAC1B;AACA,QAAI,UAAW;AACf,UAAM,QAAQ,KAAK;AACnB,UAAM,UAAU,KAAK,WAAW,MAAM;AACtC,oBAAgB;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,YAAY;AACjC,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB;AAAA,MACF;AACA,YAAM,UAAU,KAAK;AACrB,YAAM,iBAAiB,KAAK;AAC5B,YAAM,KAAK,MAAM,IAA8B,kBAAkB;AACjE,YAAM,aAAa,GAAG;AACtB,UAAI,CAAC,UAAW,cAAa;AAAA,IAC/B,QAAQ;AAAA,IAAC;AAAA,EACX;AAIA,QAAM,YAAY,CAAC,IAAY,SAAiB,GAAG,EAAE,IAAI,IAAI;AAC7D,QAAM,WAAW,CAAC,IAAY,QAAgB,GAAG,EAAE,IAAI,GAAG;AAE1D,QAAM,WAAW,CAAC,MAAsC;AACtD,QAAI,EAAE,YAAY,MAAO,QAAO,MAAM,MAAM,IAAI,UAAU,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,OAAO;AAC1F,QAAI,EAAE,YAAY,QAAS,QAAO;AAClC,UAAM,WACJ,MAAM,KAAK,SAAS,SAChB,EAAE,MAAM,aAAa,YACnB,YACA,QACF,MAAM,KAAK;AACjB,QAAI,aAAa,aAAa,MAAM,OAAO,IAAI,EAAE,MAAM,OAAO,KAAK,EAAG,QAAO;AAC7E,WAAO,aAAa,YAAY,iBAAiB;AAAA,EACnD;AAEA,QAAM,UAAU,CAAC,UAA0B;AACzC,eAAW,KAAK,MAAO,OAAM,QAAQ,IAAI,EAAE,EAAE;AAC7C,UAAM,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,YAAY,EAAE,YAAY,IAAI,EAAG;AAC7E,eAAW,WAAW,UAAU;AAC9B,YAAM,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,YAAY,OAAO;AACvD,YAAM,SAAS,QAAQ,YAAY,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK,CAAC;AAC1F,UAAI,CAAC,OAAQ;AACb,UAAI,QAAQ,YAAY,SAAS;AAC/B,YAAI,YAAY,SAAS,CAAC,MAAM,IAAI,OAAQ,OAAM,IAAI,SAAS,OAAO,KAAK;AAAA,MAC7E,WAAW,MAAM,QAAQ;AACvB,cAAM,OAAO,EAAE,KAAK,OAAO;AAC3B,YAAI,YAAY,SAAS,MAAM,IAAI,WAAW,KAAM,OAAM,IAAI,SAAS,OAAO,KAAK;AAAA,MACrF;AACA,UAAI,YAAY,UAAU,MAAM,KAAK,OAAO,OAAO,GAAI,OAAM,KAAK,OAAO;AAAA,IAC3E;AACA,UAAM,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAQ,EAAE,OAAO,CAAC,MAAsB,MAAM,IAAI,CAAC;AACnF,eAAW,QAAQ,MAAO,OAAM,KAAK,IAAI;AACzC,YAAQ,WAAW,KAAK;AACxB,UAAM,MAAM,MAAM;AAChB,iBAAW,KAAK,MAAO,OAAM,QAAQ,OAAO,EAAE,EAAE;AAAA,IAClD,CAAC;AAAA,EACH;AAIA,MAAI,SAAiC;AACrC,QAAM,UAAU,MAAM;AACpB,YAAQ,MAAM;AACd,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS;AACT,UAAM,YAAY,MAAM;AACxB,UAAM,WAAW,CAAC,OAAe,SAAiB;AAChD,UAAI,WAAW,OAAO,QAAS;AAC/B,UAAI,UAAU,WAAW;AACvB,cAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,qBAAa;AAAA,MACf,WAAW,UAAU,UAAU;AAC7B,cAAM,EAAE,OAAO,QAAQ,IAAI,KAAK,MAAM,IAAI;AAC1C,cAAM,UAAU;AAChB,gBAAQ,KAAK;AACb,aAAK,QAAQ,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC/B;AAAA,IACF;AACA,UAAM,MAAM,YAAY;AACtB,aAAO,CAAC,WAAW,OAAO,SAAS;AACjC,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,0BAA0B,mBAAmB,SAAS,CAAC,IAAI;AAAA,YACrF,QAAQ,WAAW;AAAA,YACnB,SAAS,EAAE,QAAQ,oBAAoB;AAAA,UACzC,CAAC;AACD,cAAI,CAAC,SAAS,MAAM,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,gBAAgB,SAAS,MAAM,EAAE;AACrF,gBAAM,YAAY;AAClB,uBAAa;AACb,gBAAM,SAAS,SAAS,KAAK,YAAY,IAAI,kBAAkB,CAAC,EAAE,UAAU;AAC5E,cAAI,SAAS;AACb,qBAAS;AACP,kBAAM,EAAE,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AACV,sBAAU,MAAM,QAAQ,SAAS,IAAI;AACrC,gBAAI,MAAM,OAAO,QAAQ,MAAM;AAC/B,mBAAO,OAAO,GAAG;AACf,oBAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;AACjC,uBAAS,OAAO,MAAM,MAAM,CAAC;AAC7B,kBAAI,QAAQ;AACZ,oBAAM,OAAiB,CAAC;AACxB,yBAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,oBAAI,KAAK,WAAW,QAAQ,EAAG,SAAQ,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,yBACjD,KAAK,WAAW,OAAO,EAAG,MAAK,KAAK,KAAK,MAAM,CAAC,EAAE,UAAU,CAAC;AAAA,cACxE;AACA,kBAAI,KAAK,OAAQ,UAAS,OAAO,KAAK,KAAK,IAAI,CAAC;AAChD,oBAAM,OAAO,QAAQ,MAAM;AAAA,YAC7B;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAC;AACT,YAAI,WAAW,OAAO,QAAS;AAC/B,cAAM,YAAY;AAClB,qBAAa;AACb,cAAM,IAAI,QAAQ,CAAC,YAAY,MAAM,KAAM,MAAM,QAAQ,IAAI,CAAC,CAAC;AAAA,MACjE;AAAA,IACF;AACA,SAAK,IAAI;AAAA,EACX;AAIA,OAAK,UAAU,IAAI,UAAU;AAC7B,MAAI,QAAQ,SAAU,MAAK,UAAU,IAAI,UAAU;AACnD,OAAK,QAAQ,SAAS,SAAS;AAC/B,OAAK,QAAQ,QAAQ;AACrB,OAAK,YAAY;AAAA,8CAC2B,IAAI,SAAS,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAUf,IAAI,SAAS,SAAS,CAAC;AAExE,QAAM,IAAI,CAAC,WAAmB,KAAK,cAAc,iBAAiB,MAAM,IAAI;AAC5E,QAAM,WAAW,CAAsC,OACrD,KAAK,cAAiB,iBAAiB,EAAE,IAAI;AAE/C,QAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,QAAQ,SAAS;AAE3D,QAAM,kBAAkB,MAAM;AAC5B,WAAO,OAAO,KAAK,SAAS;AAAA,MAC1B,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,OAAO,OAAO,MAAM,MAAM,MAAM;AAAA,MAChC,SAAS,OAAO,MAAM,OAAO;AAAA,MAC7B,WAAW,OAAO,MAAM,SAAS;AAAA,MACjC,OAAO,OAAO,MAAM,OAAO;AAAA,MAC3B,QAAQ,OAAO;AAAA,IACjB,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM;AACzB,oBAAgB;AAChB,UAAM,MAAM,EAAE,QAAQ;AACtB,QAAI,SAAS,QAAQ,WAAW;AAChC,QAAI,QAAQ,WAAW,MAAO;AAC9B,UAAM,aAAa,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,MAAM,YAAY,MAAM,SAAS,CAAC,CAAC,EAAE,KAAK;AAC7E,UAAM,gBAAgB,SAAS,kBAAkB,SAAS,SAAS,MAAM;AACzE,QAAI,YAAY;AAAA,kDAC8B,MAAM,KAAK,CAAC,gBAAgB,IAAI,MAAM,CAAC,CAAC;AAAA,qEACrB,IAAI,SAAS,SAAS,CAAC,2BAA2B,WAChH,IAAI,CAAC,OAAO,WAAW,OAAO,MAAM,YAAY,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EACrF,KAAK,EAAE,CAAC;AAAA,gCACiB,MAAM,OAAO,WAAW,EAAE,CAAC,6CAA6C,IAAI,SAAS,MAAM,CAAC,mGAA8F,IAAI,MAAM,MAAM,CAAC;AAAA;AAAA,wDAEnL,IAAI,SAAS,WAAW,CAAC,kBAAkB,MAAM,SAAS,SAAS;AAAA,yDAClE,IAAI,SAAS,YAAY,CAAC,kBAAkB,MAAM,SAAS,UAAU,uCAAuC,MAAM,MAAM,MAAM;AAAA;AAAA,uHAEhE,IAAI,SAAS,MAAM,CAAC,IAAI,MAAM,SAAS,YAAY,EAAE;AAAA,sDACtH,IAAI,SAAS,KAAK,CAAC,kBAAkB,MAAM,OAAO,2CAA2C,MAAM,UAAU,MAAM,SAAS,IAAI,MAAM,UAAU,CAAC;AAAA,yBAC9K,IAAI,SAAS,UAAU,CAAC,gBAAgB,MAAM,YAAY,SAAS,cAAc,oCAAoC,MAAM,YAAY,OAAO,KAAK,YAAY,MAAM,YAAY,SAAS,cAAc;AAAA,2BACtM,MAAM,QAC1B;AAAA,MACC,CAAC,MACC,6BAA6B,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS,MAAM,CAAC,eAAe,IAAI,EAAE,IAAI,CAAC,mBAAmB,IAAI,EAAE,WAAW,EAAE,CAAC,cAAc,EAAE,EAAE,iBAAiB,EAAE,KAAK,sBAAsB,EAAE,KAAK,OAAO,EAAE,QAAQ,QAAQ,EAAE,YAAY;AAAA,QACxP,EAAE,WAAW,EAAE,SAAS,EAAE,UACtB,GAAG,EAAE,IAAI,SAAM,cAAc,EAAE,OAAO,KAAK,EAAE,OAAO,KACnD,cAAc,EAAE,IAAI,KAAK,EAAE;AAAA,MAClC,CAAC,OAAO,EAAE,KAAK;AAAA,IACnB,EACC,KAAK,EAAE,CAAC;AACX,QAAI,eAAe;AACjB,YAAM,QAAQ,SAA2B,SAAS,MAAM;AACxD,aAAO,MAAM;AACb,aAAO,kBAAkB,MAAM,MAAM,QAAQ,MAAM,MAAM,MAAM;AAAA,IACjE;AACA,UAAM,UAAU,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,KAAK;AACnD,MAAE,QAAQ,EAAE,YACV,MAAM,kBAAkB,QAAQ,SAC5B,uBAAuB,IAAI,SAAS,MAAM,CAAC,mCAAmC,MAAM,iBAAiB,2BAA2B,IAAI,MAAM,cAAc,CAAC,OAAO,EAAE,GAAG,QAClK,IAAI,CAAC,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE,KAAK,CAAC,EAAE,EACnD,KAAK,QAAK,CAAC,WACd,CAAC,MAAM,QAAQ,SACb,uBAAuB,IAAI,SAAS,MAAM,CAAC,0MAC3C;AAAA,EACV;AAEA,QAAM,kBAAkB,CACtB,SACA,OACA,QACA,WACG;AACH,UAAM,OAAO,WAAW,OAAO;AAC/B,UAAM,UAAU,MAAM,OAAO,EAAE;AAC/B,WAAO,wBAAwB,KAAK,iDAAiD,OAAO,KAAK,IAAI,MAAM,CAAC,gBAAgB,KAAK,KAAK,KAAK,SAAS,KAAK,UAAU,IACjK,KAAK,SACD,KACG;AAAA,MACC,CAAC,MACC,kBAAkB,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,UAAU,aAAa,EAAE,IAAI,EAAE,SAAS,YAAO,EAAE,GAAG,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC,SAAM,EAAE,KAAK;AAAA,IAC/H,EACC,KAAK,EAAE,IACV,oCACN;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,GAAW,WAC7B,IAAI,uBAAuB,IAAI,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,YAAY;AAE3E,QAAM,YAAY,CAAC,QAAQ,UAAU;AAAA,0BACb,QAAQ,UAAU,EAAE,WAAW,MAAM,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,QAAQ,cAAc,EAAE,CAAC;AAAA,wBACnF,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC;AAGrE,QAAM,OAAO,CAAC,WAAwB,WAAuB;AAC3D,QAAI,CAAC,QAAQ,KAAK,OAAO,UAAU,YAAY,WAAY,QAAO,OAAO;AACzE,UAAM,SAAS,oBAAI,IAAoB;AACvC,eAAW,MAAM,UAAU,iBAA8B,aAAa;AACpE,aAAO,IAAI,GAAG,QAAQ,QAAQ,IAAI,GAAG,sBAAsB,EAAE,GAAG;AAClE,WAAO;AACP,eAAW,MAAM,UAAU,iBAA8B,aAAa,GAAG;AACvE,YAAM,MAAM,OAAO,IAAI,GAAG,QAAQ,QAAQ,EAAE;AAC5C,UAAI,QAAQ,OAAW;AACvB,YAAM,KAAK,MAAM,GAAG,sBAAsB,EAAE;AAC5C,UAAI,KAAK,IAAI,EAAE,IAAI,EAAG;AACtB,SAAG,QAAQ,CAAC,EAAE,WAAW,cAAc,EAAE,MAAM,GAAG,EAAE,WAAW,OAAO,CAAC,GAAG;AAAA,QACxE,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAIA,QAAM,aAAa,CAAC,OAAe;AACjC,UAAM,MAAM,oBAAI,IAA4B;AAC5C,eAAW,KAAK,UAAU,KAAK,EAAE,OAAO,CAACC,OAAMA,GAAE,OAAO,EAAE,GAAG;AAC3D,YAAM,MAAM,EAAE,KAAK;AACnB,UAAI,IAAI,KAAK,CAAC,GAAI,IAAI,IAAI,GAAG,KAAK,CAAC,GAAI,CAAC,CAAC;AAAA,IAC3C;AACA,WAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,EACrB,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,MACvB;AAAA,MACA,OAAO,MAAM,KAAK,CAAC,GAAG,MAAO,EAAE,YAAY,EAAE,YAAY,KAAK,CAAE;AAAA,MAChE,QAAQ,MAAM,OAAO,IAAI,UAAU,IAAI,IAAI,CAAC;AAAA,IAC9C,EAAE,EACD;AAAA,MAAK,CAAC,GAAG,MACR,EAAE,WAAW,EAAE,SACX,EAAE,SACA,KACA,KACD,EAAE,MAAM,GAAG,EAAE,GAAG,aAAa,OAAO,EAAE,MAAM,GAAG,EAAE,GAAG,aAAa,MAChE,IACA;AAAA,IACR;AAAA,EACJ;AAEA,QAAM,YAAY,CAAC,MAAgC;AACjD,UAAM,OAAO,IAAI,cAAc,CAAC,IAAI;AACpC,WAAO,QAAQ,GAAG,KAAK,WAAW,EAAE,KAAK,CAAC,GAAG,KAAK,OAAO,MAAM,OAAO,IAAI,IAAI,SAAS,IAAI,CAAC;AAAA,EAC9F;AAGA,QAAM,QAAQ,CAAC,MACb,EAAE,KAAK,SACN,mCAAmC,KAAK,EAAE,KAAK,QAAQ,EAAE,IACrD,gBAAgB,KAAK,EAAE,KAAK,QAAQ,EAAE,IAAI,CAAC,KAAK,OACjD;AAEN,QAAM,gBAAgB,CAAC,SAAiB,SACtC,OACI,QAAQ,QAAQ,IAAI,OAAO,MAAM,IAAI,KAAK,GAAG,sBAAsB,IAAI,SAAS,IAChF;AAEN,QAAM,eAAe,CAAC,OAAe;AACnC,UAAM,IAAI,MAAM,SAAS,IAAI,EAAE;AAC/B,UAAM,MAAM,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC;AAC3C,WAAO,MACH,yBAAyB,IAAI,SAAS,UAAU,CAAC,kBAAkB,IAAI,EAAE,iBAAiB,IAAI,KAAK,KAAK,IAAI,KAAK,YACjH;AAAA,EACN;AAEA,QAAM,aAAa,CAAC,MAAoB;AACtC,UAAM,OAAO,cAAc,QAAQ,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;AACpE,UAAM,SAAS,EAAE,KAAK,SAAS,CAAC,GAC7B,IAAI,CAAC,QAAQ,2BAA2B,IAAI,GAAG,CAAC,YAAY,IAAI,SAAS,cAAc,CAAC,GAAG,EAC3F,KAAK,EAAE;AACV,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,YAAY,MAAM;AACtB,UAAM,KAAK,MAAM,IAAI;AACrB,UAAM,UAAU,KAAK,WAAW,EAAE,IAAI,CAAC;AACvC,UAAM,cAAc,UAAU,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE;AACpE,QAAI;AACJ,UAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI,MAAM;AAC9D,UAAM,aAAa,MAAM,IAAI;AAC7B,UAAM,IAAI,aAAa;AACvB,QAAI,UAAU,IAAI;AAChB,eAAS,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACtC,YAAM,QAAQ,OAAO,MAAM,CAAC;AAC5B,YAAM,cAAc,QACjB,OAAO,CAAC,MAAM,MAAM,MAAM,EAC1B,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC;AACxE,UAAI,OAAO;AACX,UAAI,WAAW;AACf,YAAM,OAAO,OAAO,MACjB,IAAI,CAAC,GAAG,MAAM;AACb,cAAM,KAAK,KAAK,MAAM,EAAE,SAAS;AACjC,cAAM,QACJ,MAAM,KAAK,KAAK,OAAO,KAAK,MACxB,sBAAsB,IAAI,SAAS,YAAY,CAAC,IAAI,MAAM,IAAI,qBAAqB,EAAE,GAAG,SAAS,EAAE,SAAS,CAAC,WAC7G;AACN,cAAM,OAAO,OAAO,MAAM,IAAI,CAAC;AAC/B,cAAM,OAAO,CAAC,QAAQ,KAAK,MAAM,KAAK,SAAS,IAAI,KAAK;AACxD,cAAM,MAAM,CAAC,SAAS,YAAY,IAAI;AACtC,eAAO;AACP,mBAAW;AACX,cAAMC,QAAO,MAAM,CAAC;AACpB,eAAO,GAAG,KAAK,0BAA0B,MAAM,QAAQ,EAAE,wBAAwB,OAAO,SAAS,EAAE,IAAI,MAAM,QAAQ,IAAI,EAAE,EAAE,IAAI,WAAW,EAAE,KAAK,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,SAAS,CAAC,IAAIA,QAAO,cAAc,IAAIA,KAAI,CAAC,MAAM,EAAE,kEAAkE,WAAW,CAAC,CAAC,GAAG,aAAa,EAAE,EAAE,CAAC,mCAAmC,IAAI,SAAS,aAAa,CAAC,uBAAuB,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;AAAA,MACpb,CAAC,EACA,KAAK,EAAE;AACV,YAAM,SAAS,OAAO,MAAM,GAAG,EAAE;AACjC,YAAM,OAAO,SAAS,MAAM,MAAM,IAAI;AACtC,YAAM,UAAU,MAAM,IAAI,OAAO,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,IAAI,IAAI,IAAI;AACrF,YAAM,QAAQ,MAAM,MAAM,IAAI,UAAU,IAAI,OAAO,IAAI,CAAC;AACxD,eAAS,qBAAqB,eAAe,UAAU,QAAQ,IAAI,UAAU,EAAE,KAAK,IAAI,SAAS,eAAe,CAAC,eAAe,IAAI,OAAO,IAAI,CAAC;AAAA;AAAA,sDAEhG,IAAI,SAAS,OAAO,CAAC,kCAAkC,MAAM,YAAY,CAAC,GAAG,cAAc,uBAAuB,WAAW,YAAY,EAAE;AAAA,8BACnK,UAAU,KAAK,CAAC;AAAA,2BACnB,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,QAAQ,cAAc,KAAK,IAAI,OAAO,IAAI,CAAC,GAAG,QAAQ,uBAAuB,MAAM,UAAU,EAAE,CAAC,YAAY,EAAE,IAAI,MAAM,aAAa,SAAS,CAAC;AAAA;AAAA,uDAE7J,IAAI;AAAA,QACnD,QAAQ,UAAU,MAAM,IAAI,KAAK,MAAM,OAAO,SAAS,IAAI,KAAK,MAAS,6DAA6D,IAAI,IAAI,CAAC,KAAK,IAAI,SAAS,OAAO,CAAC,gCAAgC,IAAI,IAAI,CAAC,cAAc,EAAE;AAAA,oDACtL,MAAM,KAAK,CAAC,oDAAoD,MAAM,IAAI,CAAC;AAAA,QACvH,UAAU,QAAQ,OAAO,IAAI,EAAE;AAAA;AAAA,IAEnC,OAAO;AACL,YAAM,IAAI,SAAS;AACnB,YAAM,IAAI,OAAO;AACjB,eAAS,oBAAoB,eAAe,UAAU,QAAQ,IAAI,UAAU,EAAE;AAAA,gEACpB,MAAM,QAAQ,CAAC;AAAA,gCAC/C,MAAM,OAAO,qBAAqB,EAAE,CAAC,sBAAsB,MAAM,IAAI,mBAAmB,CAAC;AAAA,QAEjH,QAAQ,UAAU,KACd,oCAAoC,QACjC,IAAI,CAAC,MAAM;AACV,cAAM,SAAS,EAAE,MAAM,GAAG,EAAE;AAC5B,cAAM,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;AAClD,cAAM,MAAM,UAAU,IAAI,EAAE,IAAI;AAChC,cAAM,QAAQ,MAAM,MAAM,IAAI,GAAG;AACjC,cAAM,OAAO,SAAS,cAAc,MAAM,IAAI,EAAE;AAChD,eAAO,sEAAsE,IAAI,EAAE,IAAI,CAAC,uBAAuB,IAAI,EAAE,IAAI,CAAC;AAAA;AAAA,oGAEtC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,qBAAqB,CAAC,IAAI,SAAS,MAAM,SAAS,EAAE,IAAI,MAAM,WAAW,EAAE,CAAC,GAAG,SAAS,SAAS,QAAQ,wEAAwE,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,YAAY,CAAC,IAAI,MAAM,IAAI,EAAE,CAAC,GAAG,EAAE,SAAS,UAAU,KAAK;AAAA,gGAC1S,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,aAAa,CAAC,IAAI,MAAM,UAAU,EAAE,CAAC,GAAG,QAAQ,gBAAgB,aAAa,8EAA8E,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,eAAe,CAAC,IAAI,MAAM,MAAM,CAAC;AAAA;AAAA,+EAEvQ,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS,SAAS,CAAC,IAAI,SAAS,OAAO,MAAM,IAAI,EAAE,iBAAiB,MAAM,kBAAkB,EAAE,MAAM,iBAAiB,KAAK,4CAA4C,IAAI,IAAI,CAAC,GAAG,SAAS,aAAa,EAAE;AAAA,cAC/R,SAAS,wBAAwB,IAAI,SAAS,eAAe,CAAC,aAAa,EAAE,uBAAuB,UAAU,MAAM,CAAC;AAAA,4FACvC,IAAI,SAAS,aAAa,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,SAAS,sBAAsB,MAAM,IAAI,EAAE,CAAC,YAAY,EAAE,GAAG,QAAQ,sBAAsB,MAAM,UAAU,EAAE,CAAC,YAAY,EAAE,6BAA6B,IAAI,SAAS,aAAa,CAAC,IAAI,IAAI,YAAY,QAAQ,aAAa,EAAE,CAAC,CAAC,IAAI,MAAM,aAAa,CAAC;AAAA,mCACtW,IAAI,SAAS,gBAAgB,CAAC,IAAI,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;AAAA,MACnF,CAAC,EACA,KAAK,EAAE,CAAC,WACX,0BAA0B,IAAI,SAAS,QAAQ,CAAC,IAAI,KAAK,gBAAgB,oGAAoG,QACnL;AAAA;AAAA,IAEF;AACA,UAAM,MAAM,EAAE,SAAS;AACvB,SAAK,KAAK,MAAM;AACd,UAAI,YAAY;AAAA,iDAC2B,WAAW,aAAa,SAAS,cAAc,CAAC,QAAQ,gBAAgB,OAAO,SAAS,SAAS,cAAc,WAAW,CAAC;AAAA,0BAClJ,IAAI,SAAS,SAAS,CAAC,iBAAiB,SAAS,iBAAiB,SAAS,qBAAqB,IAAI,MAAM,EAAE,CAAC,kBAAkB,IAAI,QAAQ,QAAQ,EAAE,CAAC,mDAAmD,UAAU,CAAC,GAAG,MAAM;AAAA,IACnP,CAAC;AACD,UAAM,OAAO,IAAI,cAA2B,aAAa;AAEzD,UAAM,WAAW,MAAM;AACrB,UAAI,KAAM,MAAK,YAAY,KAAK;AAAA,IAClC;AACA,aAAS;AACT,0BAAsB,QAAQ;AAAA,EAChC;AAEA,QAAM,UAAU,CAAC,MAAoB;AACnC,UAAM,UAAU,MAAM,SAAS,IAAI,EAAE,EAAE;AACvC,WAAO,wDAAwD,IAAI,SAAS,OAAO,CAAC,0BAA0B,IAAI,EAAE,EAAE,CAAC;AAAA,qEACtD,SAAS;AAAA,MACtE,CAAC,MACC,0CAA0C,IAAI,EAAE,EAAE,CAAC,iBAAiB,EAAE,EAAE,KAAK,IAAI,SAAS,cAAc,CAAC,kBAAkB,EAAE,EAAE,iBAAiB,EAAE,KAAK,mBAAmB,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK;AAAA,IAC1M,EAAE,KAAK,EAAE,CAAC;AAAA,iCACiB,WAAW,CAAC,CAAC;AAAA;AAAA;AAAA,kEAGoB,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,EAIvG;AAIA,QAAM,WAAW,CAAC,MAAoB;AACpC,UAAM,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC9B,UAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,WAAO,KAAK,SACR,0BAA0B,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC,CAAC,WAClG;AAAA,EACN;AAEA,QAAM,WAAW,CAAC,MAChB,0BAA0B,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,cAAc,EAAE,OAAO,KAAK,EAAE,OAAO,CAAC,SAAM,IAAI,EAAE,UAAU,UAAU,CAAC,GAAG,EAAE,MAAM,UAAU,QAAQ,EAAE,MAAM,UAAU,SAAY,eAAY,EAAE,KAAK,KAAK,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,eAAY,IAAI,EAAE,KAAK,KAAK,CAAC,KAAK,EAAE;AAE9R,QAAM,YAAY,CAAC,GAAiB,QAA0C,CAAC,MAC7E,GAAG,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,gBAAgB,CAAC,mBAAmB,IAAI,EAAE,MAAM,YAAY,SAAS,CAAC,eAAe,IAAI,EAAE,MAAM,OAAO,KAAK,CAAC,KAAK,OAAO;AAAA,IACrJ;AAAA,EACF,EACG,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,EACvC,KAAK,GAAG,CAAC;AAEd,QAAM,UAAU,CACd,GACA,SACG;AACH,UAAM,IAAI,EAAE;AACZ,UAAM,MAAM,GAAG,OAAO;AACtB,UAAM,WAAW,KAAK;AACtB,WAAO,mCAAmC,WAAW,aAAa,EAAE,IAAI,MAAM,QAAQ,IAAI,EAAE,EAAE,IAAI,WAAW,EAAE,wCAAwC,IAAI,EAAE,EAAE,CAAC,KAAK,UAAU,GAAG,EAAE,UAAU,SAAS,KAAK,WAAW,MAAM,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,8CAA8C,QAAQ;AAAA,+CAC9P,SAAS,KAAK,GAAG,CAAC,KAAK,IAAI,SAAS,mBAAmB,CAAC,gBAAgB,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA;AAAA,+CAEnH,IAAI,SAAS,qBAAqB,CAAC,IAAI,IAAI,GAAG,SAAS,GAAG,CAAC,6BAA6B,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,IAAI,EAAE,SAAS,CAAC,CAAC;AAAA,0BACxK,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;AAAA,QAC1E,YAAY,GAAG,WAAW,0BAA0B,IAAI,EAAE,QAAQ,CAAC,YAAY,IAAI,SAAS,qBAAqB,CAAC,MAAM,EAAE;AAAA,QAC1H,WAAW,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;AAAA;AAAA,MAE3C,CAAC,YAAY,GAAG,WAAW,2BAA2B,IAAI,EAAE,QAAQ,CAAC,YAAY,IAAI,SAAS,qBAAqB,CAAC,MAAM,EAAE;AAAA;AAAA,EAEhI;AAGA,QAAM,UAAU,CACd,GACA,OAA+D,CAAC,MAEhE,+DAA+D,IAAI,EAAE,EAAE,CAAC,qBAAqB,IAAI,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,WAAW,EAAE;AAAA,+EAC3D,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,WAAW,CAAC,8DAA8D,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,SAAS,CAAC;AAAA,QACjN,QAAQ,GAAG,EAAE,UAAU,MAAM,KAAK,aAAa,EAAE,IAAI,SAAS,KAAK,WAAW,OAAO,OAAO,KAAK,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAAA;AAG5I,QAAM,cAAc,CAAC,MAAoB;AACvC,UAAM,IAAI,EAAE;AACZ,UAAM,WAAW,MAAM,KAAK,aAAa,EAAE;AAC3C,UAAM,MAAM,GAAG,OAAO;AACtB,UAAM,WAAW,MAAM,KAAK,aAAa,EAAE;AAC3C,UAAM,SAAS,MAAM,OAAO,IAAI,GAAG;AACnC,WAAO,sBAAsB,WAAW,aAAa,EAAE,IAAI,MAAM,QAAQ,IAAI,EAAE,EAAE,IAAI,WAAW,EAAE,wCAAwC,IAAI,EAAE,EAAE,CAAC,KAAK,UAAU,GAAG,EAAE,SAAS,CAAC,CAAC,gEAAgE,IAAI,EAAE,EAAE,CAAC,+CAA+C,QAAQ;AAAA,6CACzQ,SAAS,KAAK,GAAG,CAAC,wBAAwB,IAAI,SAAS,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA;AAAA,kCAEnF,IAAI,SAAS,mBAAmB,CAAC,IAAI,IAAI,GAAG,CAAC,mCAA8B,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,WAAW,EAAE,SAAS,CAAC,CAAC,UAAU,SAAS,SAAS,MAAM,UAAU,EAAE,CAAC,YAAY,EAAE,sBAAsB,WAAW,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC;AAAA,QACtS,GAAG,QAAQ,wBAAwB,IAAI,SAAS,qBAAqB,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE;AAAA,4BAC/E,IAAI,SAAS,oBAAoB,CAAC,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;AAAA,QAC5E,YAAY,GAAG,WAAW,iCAAiC,IAAI,EAAE,QAAQ,CAAC,YAAY,IAAI,SAAS,qBAAqB,CAAC,MAAM,EAAE;AAAA,QACjI,WAAW,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE;AAAA,QAEzC,WACI,2BAA2B,IAAI,SAAS,YAAY,CAAC,cAAc,IAAI,GAAG,CAAC;AAAA,oEACnB,IAAI,GAAG,CAAC,yCAAyC,CAAC,MAAM,KAAK,MAAM,KAAK,CAAC;AAAA,oEACzE,IAAI,GAAG,CAAC,uCAAuC,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC;AAAA,6JAErI,EACN;AAAA;AAAA,MAEA,CAAC,YAAY,GAAG,WAAW,gCAAgC,IAAI,EAAE,QAAQ,CAAC,YAAY,IAAI,SAAS,qBAAqB,CAAC,MAAM,EAAE;AAAA;AAAA,EAErI;AAGA,QAAM,YAAY,CAAC,IAAY,UAA0B;AACvD,UAAM,QAAkB,CAAC;AACzB,UAAM,SAAS,oBAAI,IAA4B;AAC/C,eAAW,KAAK,OAAO;AACrB,YAAM,MAAM,EAAE,MAAM,OAAO;AAC3B,UAAI,CAAC,OAAO,IAAI,GAAG,EAAG,OAAM,KAAK,GAAG;AACpC,aAAO,IAAI,KAAK,CAAC,GAAI,OAAO,IAAI,GAAG,KAAK,CAAC,GAAI,CAAC,CAAC;AAAA,IACjD;AACA,WAAO,MACJ,IAAI,CAAC,QAAQ;AACZ,YAAM,OAAO,OAAO,IAAI,GAAG,KAAK,CAAC;AACjC,YAAM,QAAQ,KAAK,CAAC;AACpB,UAAI,CAAC,MAAO,QAAO;AACnB,UAAI,KAAK,WAAW,EAAG,QAAO,QAAQ,KAAK;AAC3C,YAAM,MAAM,SAAS,IAAI,GAAG;AAC5B,YAAM,OAAO,MAAM,WAAW,IAAI,GAAG;AACrC,YAAM,OAAO,KAAK,SAAS;AAC3B,UAAI;AACF,eAAO,2BAA2B,IAAI,SAAS,SAAS,CAAC,cAAc,IAAI,GAAG,CAAC,iBAAiB,KAAK,MAAM,uCAAuC,IAAI,GAAG,CAAC;AAAA,wCAC5H,IAAI,GAAG,CAAC,uDAAuD,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,iBAAiB,CAAC,4EAA4E,IAAI,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAAC,mBAAmB,MAAM,MAAM,EAAE,CAAC;AAAA,YACnT,KAAK,IAAI,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AACxC,aAAO,2BAA2B,IAAI,SAAS,SAAS,CAAC,cAAc,IAAI,GAAG,CAAC,iBAAiB,KAAK,MAAM,wCAAwC,IAAI,GAAG,CAAC;AAAA,YACvJ,QAAQ,OAAO,EAAE,KAAK,KAAK,CAAC,CAAC;AAAA,4EACmC,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,aAAa,CAAC,gBAAgB,IAAI,sBAAsB,IAAI,qBAAqB,SAAS,IAAI,KAAK,GAAG,uCAAuC,OAAO,IAAI,uCAAuC,EAAE,mEAAmE,IAAI,qBAAqB,SAAS,IAAI,KAAK,GAAG;AAAA,YAC5Y,KACC,MAAM,CAAC,EACP,IAAI,CAAC,MAAM,QAAQ,GAAG,EAAE,SAAS,MAAM,QAAQ,KAAK,CAAC,CAAC,EACtD,KAAK,EAAE,CAAC;AAAA,IACf,CAAC,EACA,KAAK,EAAE;AAAA,EACZ;AAEA,QAAM,aAAa,MAAM;AACvB,UAAM,KAAK,MAAM,KAAK;AACtB,UAAM,QAAQ,KAAK,UAAU,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AACnE,aAAS,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC/B,UAAM,WAAW,MAAM,CAAC,GAAG,MAAM,YAAY;AAC7C,UAAM,OACJ,MAAM,KAAK,SAAS,SAAU,aAAa,YAAY,YAAY,QAAS,MAAM,KAAK;AACzF,UAAM,MAAM,IAAI,KAAK,MAAM,CAAC;AAC5B,UAAM,SAAS,UAAU,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE;AAChE,UAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ;AACxC,UAAM,KAAK,OAAO;AAClB,QAAI;AACJ,UAAM,cAAc,GAAG,IAAI,SAAS,UAAU,CAAC,eAAe,IAAI,qBAAqB,IAAI,MAAM,EAAE,CAAC,oBAAoB,IAAI,QAAQ,CAAC;AACrI,QAAI,SAAS,WAAW;AACtB,eAAS,sBAAsB,WAAW;AAAA,8BAClB,MAAM,GAAG,EAAE,QAAQ,cAAc,EAAE,CAAC,8BAA8B,MAAM,YAAY,CAAC,GAAG,MAAM,cAAc,CAAC,GAAG,MAAM,eAAe,CAAC;AAAA,mCACjI,IAAI,IAAI,mBAAmB,CAAC,GAAG,EAAE,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,MAAM,KAAK,CAAC;AAAA,6CACjG,MAAM,YAAY,CAAC,wEAAwE,MAAM,KAAK,CAAC;AAAA,0BAC1H,MAAM,eAAe,CAAC,qEAAqE,MAAM,KAAK,CAAC;AAAA;AAAA,4CAErF,MAAM,SAAS,MAAM,IAAI,WAAW,EAAE,KAAK,EAAE,IAAI,4BAA4B,IAAI,SAAS,SAAS,CAAC,yBAAyB;AAAA,QACjK,MAAM,SAAS,sHAAsH,IAAI,SAAS,YAAY,CAAC,8BAA8B,EAAE;AAAA;AAAA,IAEnM,OAAO;AACL,YAAM,UAAU,MAAM,KAAK,UAAU,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,KAAK,OAAO,IAAI;AACtF,eAAS,uBAAuB,WAAW,kDAAkD,UAAU,IAAI,CAAC,oBAAoB,OAAO,SAAS,EAAE;AAAA,6BAC3H,MAAM,QAAQ,CAAC;AAAA,0BAClB,IAAI,IAAI,mBAAmB,CAAC,GAAG,EAAE,SAAS,QAAQ,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC;AAAA,2BAClF,IAAI,MAAM,GAAG,EAAE,QAAQ,cAAc,EAAE,CAAC,CAAC;AAAA,4CAE5D,MAAM,UAAU,KACZ,sDAAsD,UAAU,IAAI,KAAK,CAAC,KAC1E,2BAA2B,IAAI,SAAS,SAAS,CAAC,IAAI,KAAK,2BAA2B,2BAA2B,QACvH;AAAA,wCACkC,MAAM,WAAW,CAAC,gBAAgB,MAAM,OAAO,CAAC;AAAA,QAEhF,UACI,6DAA6D,IAAI,SAAS,WAAW,CAAC,0BAA0B,IAAI,QAAQ,EAAE,CAAC,qDAAqD,QAAQ,SAAS,EAAE,UAAU,KAAK,CAAC,CAAC;AAAA,sGAC9H,IAAI,QAAQ,EAAE,CAAC,qGACzG,EACN;AAAA;AAAA,IAEF;AACA,UAAM,aAAa,CAAC,MAAc;AAChC,YAAM,IAAI,WAAW,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC;AACnD,YAAM,QAAQ,EAAE,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,SAAI,EAAE,MAAM,EAAE,CAAC,KAAK;AACnE,aAAO,GAAG,KAAK,GAAG,GAAG,YAAY,EAAE,aAAa,YAAY,SAAM,EAAE,QAAQ,KAAK,EAAE;AAAA,IACrF;AACA,UAAM,MAAM,EAAE,UAAU;AACxB,SAAK,KAAK,MAAM;AACd,UAAI,YAAY;AAAA,wCACkB,WAAW,QAAQ,SAAS,eAAe,CAAC;AAAA,8BACtD,gBAAgB,QAAQ,UAAU,SAAS,eAAe,UAAU,CAAC;AAAA,yDACzC,CAAC,QAAQ,OAAO,SAAS,EAC1E;AAAA,QACC,CAAC,MACC,0CAA0C,CAAC,KAAK,IAAI,SAAS,QAAQ,CAAC,kBAAkB,MAAM,KAAK,SAAS,CAAC,KAAK,MAAM,SAAS,SAAS,MAAM,QAAQ,QAAQ,SAAS;AAAA,MAC7K,EACC,KAAK,EAAE,CAAC,qBAAqB,MAAM;AAAA,IACxC,CAAC;AAAA,EACH;AAIA,QAAM,WAAW,CAAC,MAA4B;AAC5C,UAAM,OAAO,EAAE,OAAO,QAAQ;AAC9B,WAAO,8NAA8N,IAAI;AAAA,EAC3O;AAEA,QAAM,QAAQ,CAAC,MAAc,SAAsB;AACjD,UAAM,QAAQ,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,KAAK;AAC7C,gBAAY;AACZ,UAAM,UAAU,MAAM;AACtB,UAAM,KAAM,MAAM;AAChB,UAAI,MAAM,UAAU,SAAS;AAC3B,cAAM,QAAQ;AACd,oBAAY;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,CAAC,IAAY,SAAiC;AACjE,UAAM,UAAU,IAAI,EAAE;AACtB,QAAI,MAAM,MAAM,SAAS,IAAI;AAC3B,YAAM,MAAM,OAAO;AACnB,YAAM,MAAM,aAAa;AAAA,IAC3B;AACA,UAAM,SAAS,aAAa,2BAA2B,gCAAgC,MAAM;AAC3F,YAAM,UAAU,OAAO,EAAE;AACzB,YAAM,QAAQ;AACd,kBAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,MAAM;AACxB,UAAM,KAAK,MAAM,MAAM;AACvB,UAAM,QAAQ,KAAK,UAAU,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AACpE,UAAM,SAAS,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE;AACpD,UAAM,YAAY,UAAU,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE;AACpE,UAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,MAAM,IAAI;AACxD,QAAI,CAAC,KAAM,OAAM,MAAM,OAAO;AAC9B,UAAM,aAAa,MAAM,MAAM,cAAc,QAAQ;AACrD,UAAM,MAAM,aAAa;AACzB,UAAM,KAAK,MAAM;AACjB,QAAI;AACJ,QAAI,MAAM;AACR,eAAS,CAAC,KAAK,EAAE,CAAC;AAClB,YAAM,IAAI,KAAK;AACf,YAAM,SAAS,KAAK,KAAK,QAAQ,KAAK,KAAK;AAC3C,YAAM,SAAS,KAAK,GAAG,YAAY,MAAM,GAAG,YAAY,IAAI,OAAO,KAAK;AACxE,YAAM,SAAS,CAAC,OAAe,OAAe,UAC5C,SAAS,KAAK,iBAAiB,IAAI,SAAS,WAAW,CAAC,gBAAgB,KAAK,KAAK,IAAI,KAAK,CAAC;AAC9F,aAAO;AAAA,2DAC8C,IAAI,SAAS,SAAS,CAAC,qDAAqD,MAAM,KAAK,CAAC;AAAA,uEAC5E,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,SAAS,YAAY,CAAC,yCAAyC,MAAM,QAAQ,CAAC,mCAAmC,MAAM,OAAO,CAAC,sEAAsE,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,SAAS,WAAW,CAAC,uCAAuC,MAAM,MAAM,CAAC;AAAA,sEACzT,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,SAAS,eAAe,CAAC,uDAAuD,MAAM,WAAW,CAAC,mCAAmC,MAAM,MAAM,CAAC,iCAAiC,MAAM,KAAK,CAAC;AAAA,8BAC5P,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,MAAM,MAAM;AAAA,2BAC7C,aAAa,UAAU,EAAE,KAAK,OAAO,IAAI,CAAC,IAAI,IAAI,SAAS,SAAS,CAAC;AAAA,uCACzD,IAAI,SAAS,YAAY,CAAC,IAAI,IAAI,GAAG,WAAW,cAAc,CAAC;AAAA;AAAA,wDAE9C,SAAS,KAAK,KAAK,OAAO,CAAC,KAAK,IAAI,SAAS,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA,gCACzF,IAAI,SAAS,aAAa,CAAC,IAAI,IAAI,MAAM,CAAC,qCAAqC,IAAI,SAAS,gBAAgB,CAAC,IAAI,IAAI,KAAK,KAAK,OAAO,CAAC;AAAA,yDAC9G,IAAI,SAAS,OAAO,CAAC,8CAA8C,eAAe,QAAQ,IAAI,MAAM,CAAC,GAAG,GAAG,GAAG,SAAS,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,oCAChM,IAAI,SAAS,SAAS,CAAC,IAAI,IAAI,aAAa,KAAK,SAAS,CAAC,CAAC,kCAAkC,MAAM,KAAK,CAAC,iCAAiC,MAAM,MAAM,CAAC,iCAAiC,MAAM,KAAK,CAAC;AAAA;AAAA,UAG/N,kBACI,wBAAwB,IAAI,SAAS,YAAY,CAAC;AAAA,YACpD,OAAO,QAAQ,QAAQ,KAAK,KAAK,OAAO,GAAG,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,OAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AAAA,YACvG,OAAO,MAAM,MAAM,KAAK,EAAE,CAAC;AAAA,YAC3B,GAAG,GAAG,SAAS,OAAO,MAAM,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC,IAAI,EAAE;AAAA,YACvD,GAAG,IAAI,SAAS,OAAO,OAAO,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE;AAAA,YAC3D,GAAG,QAAQ,SAAS,OAAO,YAAY,YAAY,EAAE,QAAQ,KAAK,IAAI,CAAC,IAAI,EAAE;AAAA,YAC7E,OAAO,QAAQ,QAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,eAAe,CAAC,CAAC;AAAA,YACjE,OAAO,WAAW,WAAW,GAAG,WAAW,EAAE,CAAC;AAAA,YAC9C,OAAO,aAAa,aAAa,GAAG,cAAc,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,CAAC;AAAA,YACnG,OAAO,cAAc,cAAc,KAAK,UAAU,CAAC;AAAA,kBAEjD,EACN;AAAA,UACE,GAAG,OAAO,gDAAgD,IAAI,SAAS,SAAS,CAAC,6HAA6H,0BAA0B,IAAI,SAAS,SAAS,CAAC,uBAAuB,QAAQ,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC,QAAQ;AAAA,UAEzT,GAAG,YAAY,SACX,4BAA4B,IAAI,SAAS,gBAAgB,CAAC,gBAAgB,EAAE,YAAY,MAAM,SAAS,EAAE,YAAY,MAAM,cAAc,EAAE,YAAY,WAAW,IAAI,KAAK,GAAG,8BAA8B,EAAE,YAC3M;AAAA,QACC,CAAC,MACC,oBAAoB,IAAI,SAAS,eAAe,CAAC,mBAAmB,IAAI,EAAE,QAAQ,CAAC,wBAAwB,IAAI,EAAE,eAAe,EAAE,CAAC,gBAAgB,EAAE,QAAQ,EAAE,0BAA0B,MAAM,KAAK,CAAC,+BAA+B,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,gDAAgD,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,OAAO,IAAI,CAAC,CAAC,eAAe,EAAE;AAAA,MAC1X,EACC,KAAK,EAAE,CAAC,iBACX,EACN;AAAA,uCAC+B,MAAM,MAAM,CAAC,sBAAsB,MAAM,QAAQ,CAAC;AAAA;AAAA,IAErF,OAAO;AACL,aAAO,oDAAoD,MAAM,SAAS,CAAC,yDAAyD,IAAI,SAAS,YAAY,CAAC,yCAAyC,MAAM,QAAQ,CAAC,mCAAmC,MAAM,KAAK,CAAC;AAAA,8BAC7O,MAAM,SAAS,UAAK,MAAM,MAAM,OAAO,MAAM,MAAM,KAAK,EAAE;AAAA,8CAC1C,MAAM,MAAM,CAAC,sBAAsB,MAAM,KAAK,CAAC,yBAAyB,MAAM,KAAK,CAAC;AAAA,8BACpG,aAAa,UAAU,EAAE,iBAC/C,MAAM,SACF,MACG,IAAI,CAAC,MAAM;AACV,cAAM,WAAW,CAAC,KAAK,IAAI,EAAE,EAAE;AAC/B,cAAM,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK;AACrC,cAAM,UAAU,MAAM,QAAQ,IAAI,EAAE,EAAE;AACtC,eAAO,4DAA4D,IAAI,EAAE,EAAE,CAAC,oBAAoB,IAAI,EAAE,EAAE,CAAC;AAAA,yCAChF,MAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,CAAC;AAAA,0CAChC,WAAW,WAAW,EAAE,IAAI,MAAM,QAAQ,IAAI,EAAE,EAAE,IAAI,WAAW,EAAE,sCAAsC,IAAI,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,QAAQ,CAAC,kBAAkB,OAAO;AAAA,kEAC3J,IAAI,EAAE,EAAE,CAAC,mCAAmC,MAAM,QAAQ,IAAI,EAAE,EAAE,CAAC,yBAAyB,MAAM,SAAS,CAAC,8DAA8D,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,YAAY,CAAC,gCAAgC,OAAO,iBAAiB,UAAU,YAAY,aAAa,KAAK,UAAU,MAAM,WAAW,IAAI,MAAM,KAAK,CAAC;AAAA,iCAC9X,IAAI,SAAS,YAAY,CAAC,IAAI,IAAI,MAAM,CAAC;AAAA,uCACnC,IAAI,SAAS,eAAe,CAAC,IAAI,IAAI,EAAE,OAAO,WAAW,cAAc,CAAC,yCAAyC,IAAI,SAAS,eAAe,CAAC,IAAI,IAAI,EAAE,OAAO,CAAC;AAAA,cACzL,EAAE,OAAO,YAAY,SAAS,sBAAsB,IAAI,SAAS,kBAAkB,CAAC,IAAI,MAAM,KAAK,CAAC,YAAY,EAAE,qBAAqB,IAAI,SAAS,eAAe,CAAC,IAAI,IAAI,cAAc,EAAE,OAAO,KAAK,EAAE,OAAO,CAAC;AAAA,iCAC/L,IAAI,SAAS,YAAY,CAAC,IAAI,IAAI,SAAS,EAAE,SAAS,CAAC,CAAC;AAAA,uGACc,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,eAAe,CAAC,yCAAyC,MAAM,QAAQ,CAAC,wEAAwE,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,cAAc,CAAC,uCAAuC,MAAM,MAAM,CAAC,6EAA6E,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,kBAAkB,CAAC,WAAW,WAAW,iBAAiB,gBAAgB,iBAAiB,WAAW,iBAAiB,gBAAgB,KAAK,WAAW,MAAM,SAAS,IAAI,MAAM,WAAW,CAAC,mCAAmC,MAAM,MAAM,CAAC;AAAA,MACtrB,CAAC,EACA,KAAK,EAAE,IACV,2BAA2B,IAAI,SAAS,UAAU,CAAC,IAAI,MAAM,SAAS,CAAC,MAAM,KAAK,+BAA+B,cAAc,OAAO,KAAK,KAAK,2EAA2E,QACjO;AAAA,IACF;AACA,UAAM,MAAM,EAAE,WAAW;AACzB,SAAK,KAAK,MAAM;AACd,UAAI,YAAY;AAAA,yCACmB,WAAW,WAAW,SAAS,gBAAgB,CAAC,QAAQ,gBAAgB,SAAS,WAAW,SAAS,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAAA,0BAC7I,IAAI,SAAS,WAAW,CAAC,iBAAiB,OAAO,YAAY,OAAO,qBAAqB,IAAI,MAAM,EAAE,CAAC;AAAA,mGAC7B,MAAM,KAAK,CAAC,4BAA4B,OAAO,IAAI,IAAI,KAAK,UAAU,CAAC,KAAK,EAAE;AAAA,qDAC5H,MAAM,KAAK,CAAC,kCAAkC,MAAM,SAAS,CAAC;AAAA,0DACzD,MAAM,OAAO,WAAW,EAAE,CAAC,6EAA6E,MAAM,KAAK,CAAC;AAAA,oDAC1H,MAAM,KAAK,CAAC,iCAAiC,MAAM,KAAK,CAAC,iCAAiC,MAAM,KAAK,CAAC;AAAA,2CAC/G,IAAI,EAAE,CAAC,uBAAuB,SAAS,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC;AAAA;AAAA,wDAEnE,MAAM,OAAO,CAAC;AAAA,yCAC7B,MAAM,MAAM,CAAC,iDAAiD,UAAU,EAAE;AAAA,kCACjF,MAAM,KAAK,CAAC;AAAA,kCACZ,MAAM,MAAM,CAAC;AAAA,kCACb,MAAM,KAAK,CAAC;AAAA,kCACZ,MAAM,MAAM,CAAC;AAAA,kCACb,MAAM,OAAO,CAAC;AAAA,kCACd,IAAI;AAAA;AAAA,QAE9B,MAAM,QAAQ,sBAAsB,IAAI,SAAS,KAAK,CAAC,wBAAwB,IAAI,MAAM,MAAM,IAAI,CAAC,UAAU,MAAM,MAAM,OAAO,oCAAoC,IAAI,SAAS,SAAS,CAAC,mBAAmB,EAAE,0DAA0D,MAAM,MAAM,EAAE,CAAC,oBAAoB,EAAE;AAAA;AAAA,IAEpT,CAAC;AACD,UAAM,QAAQ,IAAI,cAAiC,aAAa;AAChE,QAAI,SAAS,MAAM;AACjB,YAAM,iBAAiB,QAAQ,MAAM;AACnC,cAAM,MAAM,MAAM;AAClB,YAAI,IAAK,OAAM,MAAM,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,gBAAgB,eAAe,CAAC,CAAC;AAAA,MACtF,CAAC;AACD,YAAM,SAAS,SAAS,IAAI;AAAA,IAC9B;AACA,QAAI,WAAW,MAAM;AACnB,YAAM,QAAQ,SAAS;AAAA,QACrB,CAAC,KAAK,MAAM,MAAM,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAE;AAAA,QAC9D;AAAA,MACF;AACA,eAAS,QAAQ,GAAG,QAAQ,IAAI,KAAK,OAAO,EAAE,GAAG,MAAM,CAAC;AAAA,IAC1D;AAAA,EACF;AAIA,QAAM,iBAAiB,MAAM;AAC3B,UAAM,QAAQ,QAAQ;AACtB,MAAE,UAAU,EAAE,YAAY,MAAM,SAC5B,qBAAqB,IAAI,SAAS,QAAQ,CAAC,sIAAsI,MAC9K;AAAA,MACC,CACE,MACG,sCAAsC,IAAI,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,WAAW,CAAC;AAAA,+CAC5D,IAAI,aAAa,EAAE,SAAS,CAAC,CAAC;AAAA,uDACtB,EAAE,OAAO,KAAK,EAAE,YAAY,QAAQ,QAAQ,EAAE,YAAY,UAAU,UAAU,MAAM;AAAA,0DACjF,IAAI,cAAc,EAAE,OAAO,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,SAAM,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE;AAAA,6CAC7G,IAAI,EAAE,YAAY,QAAQ,YAAY,EAAE,EAAE,IAAI,EAAE,GAAG,SAAS,KAAK,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,WAAM,EAAE,EAAE,CAAC;AAAA,kCAC3G,IAAI,cAAc,CAAC,CAAC,CAAC;AAAA,qDACF,EAAE,QAAQ,MAAM,IAAI,EAAE,MAAM,OAAO,CAAC,iBAAY,EAAE,MAAM,QAAQ,MAAM,IAAI,EAAE,KAAK,KAAK,CAAC,iBAAY,EAAE,GAAG,IAAI,EAAE,YAAY,UAAU,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,EAAE,OAAQ,CAAC;AAAA,oDAC1M,IAAI,EAAE,UAAU,EAAE,CAAC;AAAA,IAC7D,EACC,KAAK,EAAE,CAAC,qBACX,yBAAyB,IAAI,SAAS,aAAa,CAAC,iCAAiC,IAAI,MAAM,SAAS,CAAC;AAAA,EAC/G;AAEA,QAAM,kBAAkB,MAAM;AAC5B,UAAM,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,OAAO;AACxD,UAAM,KAAK,EAAE,WAAW;AACxB,QAAI,CAAC,GAAG;AACN,SAAG,SAAS;AACZ,SAAG,YAAY;AACf,iBAAW,KAAK,OAAO,KAAK,GAAG,OAAO;AACpC,YAAI,MAAM,YAAY,MAAM,SAAU,QAAO,GAAG,QAAQ,CAAC;AAC3D;AAAA,IACF;AACA,OAAG,SAAS;AACZ,WAAO,OAAO,GAAG,SAAS;AAAA,MACxB,gBAAgB,EAAE;AAAA,MAClB,SAAS,EAAE;AAAA,MACX,SAAS,EAAE;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,IAAI,EAAE;AAAA,MACN,MAAM,EAAE,KAAK;AAAA,MACb,QAAQ,EAAE,UAAU;AAAA,MACpB,WAAW,EAAE;AAAA,IACf,CAAC;AACD,UAAM,EAAE,KAAK,GAAG,WAAW,IAAI;AAC/B,UAAM,QAAQ,CAAC,KAAa,OAAe,UACzC,SAAS,KAAK,gBAAgB,IAAI,SAAS,cAAc,CAAC,gBAAgB,GAAG,KAAK,IAAI,KAAK,CAAC;AAC9F,OAAG,YAAY,eAAe,IAAI,SAAS,cAAc,CAAC,IAAI,IAAI,EAAE,QAAQ,YAAY,CAAC,CAAC,SAAM,IAAI,EAAE,OAAO,WAAW,EAAE,MAAM,SAAS,EAAE,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,kEAClG,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,qBAAqB,CAAC,6EAA6E,IAAI,SAAS,cAAc,CAAC;AAAA,sBACtM,MAAM,MAAM,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,cAAc,eAAe,EAAE,UAAU,CAAC,GAAG,MAAM,WAAW,WAAW,GAAG,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AAAA,QACpJ,MAAM,aAAa,aAAa,EAAE,SAAS,CAAC,GAAG,MAAM,MAAM,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,QAAQ,QAAQ,EAAE,KAAK,OAAO,GAAG,EAAE,KAAK,IAAI,KAAK,EAAE,KAAK,OAAO,MAAM,EAAE,KAAK,OAAO,CAAC;AAAA,QACnK,MAAM,aAAa,WAAW,EAAE,SAAS,CAAC,GAAG,MAAM,UAAU,UAAU,EAAE,UAAU,QAAG,CAAC;AAAA,WACpF,IAAI,SAAS,aAAa,CAAC,IAAI,IAAI,KAAK,UAAU,EAAE,GAAG,YAAY,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAAA,EAC5F;AAIA,QAAM,QAAQ,CAAC,aACZ,MAAM,SAAS,QAAQ,MAAM,SAAS,aACtC,QAAQ,aAAa,UAAa,QAAQ,SAAS,SAAS,OAAO;AAEtE,QAAM,SAAS,MAAM;AACnB,QAAI,UAAW;AACf,iBAAa;AACb,MAAE,SAAS,EAAE,SAAS,MAAM,SAAS;AACrC,MAAE,UAAU,EAAE,SAAS,MAAM,SAAS;AACtC,eAAW,WAAW,SAAU,GAAE,OAAO,OAAO,EAAE,EAAE,SAAS,CAAC,MAAM,OAAO;AAC3E,MAAE,SAAS,EAAE,UAAU,OAAO,QAAQ,SAAS,OAAO,KAAK,EAAE,WAAW,CAAC;AACzE,QAAI,MAAM,SAAS,WAAW;AAC5B,UAAI,MAAM,KAAK,EAAG,WAAU;AAC5B,UAAI,MAAM,MAAM,EAAG,YAAW;AAC9B,UAAI,MAAM,OAAO,EAAG,aAAY;AAAA,IAClC,MAAO,gBAAe;AACtB,oBAAgB;AAAA,EAClB;AAEA,QAAM,eAAe,CAAC,MAAoB;AACxC,UAAM,OAAO;AACb,UAAM,EAAE,OAAO,EAAE,KAAK,EAAE;AACxB,QAAI,EAAE,YAAY,MAAO,OAAM,IAAI,SAAS,EAAE,KAAK;AACnD,QAAI,EAAE,YAAY,QAAS,OAAM,MAAM,OAAO,EAAE;AAChD,QAAI,EAAE,YAAY,QAAQ;AACxB,YAAM,KAAK,WAAW,EAAE;AACxB,YAAM,WAAW,IAAI,SAAS,EAAE,IAAI,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,IAC3D;AACA,UAAM,UAAU,OAAO,EAAE,EAAE;AAC3B,UAAM,UAAU;AAChB,WAAO;AACP,SACG,cAAc,mCAAmC,IAAI,OAAO,EAAE,EAAE,CAAC,IAAI,GACpE,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,EACzC;AAKA,QAAM,WAAW,CAAC,IAAiB,SAAqB;AACtD,QAAI,CAAC,QAAQ,EAAG,QAAO,KAAK;AAC5B,OAAG,MAAM,SAAS,GAAG,GAAG,YAAY;AACpC,OAAG,UAAU,IAAI,YAAY;AAC7B,SAAK,GAAG;AACR,WAAO,OAAO,GAAG,OAAO;AAAA,MACtB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,SAAS;AAAA,IACX,CAAC;AACD,UAAM,KAAK,IAAI;AAAA,EACjB;AAGA,QAAM,SAAS,CAAC,KAAkB,MAAmB,WAAmB,SAAqB;AAC3F,QAAI,CAAC,QAAQ,EAAG,QAAO,KAAK;AAC5B,QAAI,UAAU,IAAI,UAAU;AAC5B,SAAK,UAAU,IAAI,UAAU;AAC7B,SAAK,MAAM,YAAY,cAAc,aAAa,IAAI,cAAc,GAAG;AACvE,SAAK,MAAM,UAAU;AACrB,UAAM,KAAK,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,EACtC;AAEA,QAAM,SAAS,CAAC,aAAqB,KAAK,cAA2B,QAAQ;AAC7E,QAAM,SAAS,CAAC,QAAqB,IAAI,cAA2B,aAAa,KAAK;AAEtF,QAAM,cAAc,CAAC,IAAY,YAAY,OAAO;AAClD,UAAM,MACJ,OAAO,uBAAuB,IAAI,OAAO,EAAE,CAAC,IAAI,KAChD,OAAO,oBAAoB,IAAI,OAAO,EAAE,CAAC,IAAI;AAC/C,UAAM,OAAO,MAAM;AACjB,YAAM,UAAU,IAAI,EAAE;AACtB,UAAI,MAAM,KAAK,YAAY,GAAI,OAAM,KAAK,UAAU;AACpD,iBAAW;AAAA,IACb;AACA,QAAI,CAAC,IAAK,QAAO,KAAK;AACtB,WAAO,KAAK,OAAO,GAAG,GAAG,WAAW,IAAI;AAAA,EAC1C;AAEA,QAAM,eAAe,CAAC,SAAiB;AACrC,UAAM,KAAK,MAAM,IAAI;AACrB,QAAI,CAAC,GAAI;AACT,UAAM,MAAM,UAAU,KAAK,EACxB,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,KAAK,YAAY,IAAI,EACpD,IAAI,CAAC,MAAM,EAAE,EAAE;AAClB,UAAM,MAAM,OAAO,4BAA4B,IAAI,OAAO,IAAI,CAAC,IAAI;AACnE,UAAM,OAAO,MAAM;AACjB,iBAAW,MAAM,IAAK,OAAM,UAAU,IAAI,EAAE;AAC5C,gBAAU;AAAA,IACZ;AACA,QAAI,CAAC,IAAK,QAAO,KAAK;AACtB,WAAO,KAAK,OAAO,GAAG,GAAG,IAAI,IAAI;AAAA,EACnC;AAEA,QAAM,cAAc,CAAC,IAAY,MAA8B,YAAY,OAAO;AAChF,UAAM,MAAM,OAAO,wBAAwB,IAAI,OAAO,EAAE,CAAC,IAAI;AAC7D,QAAI,CAAC,IAAK,QAAO,aAAa,IAAI,IAAI;AACtC,WAAO,KAAK,OAAO,GAAG,GAAG,WAAW,MAAM,aAAa,IAAI,IAAI,CAAC;AAAA,EAClE;AAgBA,MAAI,OAAoB;AACxB,MAAI,gBAAgB;AAEpB,QAAM,WAAW;AACjB,QAAM,cAAc;AAEpB,QAAM,YAAY,CAAC,KAAkB,MAAc;AACjD,UAAM,OAAO,OAAO,GAAG;AACvB,SAAK,MAAM,YAAY,IAAI,cAAc,CAAC,QAAQ;AAClD,QAAI,IAAI,QAAQ,UAAU;AACxB,WAAK,MAAM,UAAU,OAAO,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;AAAA,EAChF;AAEA,QAAM,SAAS,CAAC,KAAkB,GAAW,SAAkB;AAC7D,QAAI,UAAU,IAAI,UAAU;AAC5B,QAAI,UAAU,OAAO,QAAQ,IAAI;AACjC,QAAI,IAAI,QAAQ,UAAU,SAAU,KAAI,UAAU,IAAI,UAAU;AAChE,cAAU,KAAK,CAAC;AAChB,UAAM,KAAK,MAAM;AACf,UAAI,UAAU,OAAO,YAAY,SAAS;AAC1C,UAAI,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,QAAM,YAAY,CAAC,OAAoB;AACrC,UAAM,OAAO,GAAG,QAAQ;AACxB,UAAM,KAAK,GAAG,QAAQ,kBAAkB,GAAG,QAAQ,MAAM;AACzD,QAAI,SAAS,cAAc;AACzB,YAAM,IAAI,OAAO;AACjB,gBAAU;AACV,UAAI;AACF,kBAAU,UAAU,EAAE;AAAA,MACxB,QAAQ;AAAA,MAAC;AAAA,IACX,WAAW,SAAS,YAAY;AAC9B,YAAM,KAAK,UAAU;AACrB,iBAAW;AAAA,IACb,WAAW,SAAS,UAAU;AAC5B,YAAM,KAAK,WAAW,MAAM,KAAK,aAAa,KAAK,OAAO;AAC1D,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,KAAG,eAAe,CAAC,UAAU;AAC3B,UAAM,OAAO;AAGb,oBAAgB;AAChB,QAAI,MAAM,WAAW,KAAK,CAAC,MAAM,UAAW;AAC5C,UAAM,SAAS,MAAM;AACrB,QAAI,OAAO,QAAQ,oDAAoD,EAAG;AAC1E,UAAM,QAAQ,OAAO,QAAqB,cAAc;AACxD,UAAM,QAAQ,OAAO,QAAqB,kBAAkB;AAC5D,QAAI,CAAC,SAAS,CAAC,MAAO;AACtB,WAAO;AAAA,MACL,WAAW,MAAM;AAAA,MACjB,IAAI,MAAM;AAAA,MACV,IAAI,MAAM;AAAA,MACV,IAAI,YAAY,IAAI;AAAA,MACpB,IAAI;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,MAAM,OAAO,OAAO,QAAQ,UAAU,CAAC;AAAA,MACvC,OAAO;AAAA,IACT;AACA,QAAI,OAAO;AACT,YAAM,UAAU;AAChB,cAAQ,QAAQ,WAAW,MAAM;AAC/B,YAAI,SAAS,WAAW,QAAQ,KAAM;AACtC,wBAAgB;AAChB,eAAO;AACP,kBAAU,KAAK;AAAA,MACjB,GAAG,GAAG;AAAA,IACR;AAAA,EACF,CAAC;AAED,KAAG,eAAe,CAAC,UAAU;AAC3B,QAAI,CAAC,QAAQ,MAAM,cAAc,KAAK,UAAW;AACjD,UAAM,KAAK,MAAM,UAAU,KAAK;AAChC,UAAM,KAAK,MAAM,UAAU,KAAK;AAChC,QAAI,CAAC,KAAK,MAAM;AACd,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,EAAG;AAC5B,WAAK,OAAO,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,MAAM;AAChD,UAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,UAAI,KAAK,SAAS,OAAO,KAAK,OAAO;AACnC,aAAK,MAAM,UAAU,IAAI,SAAS;AAClC,aAAK,MAAM,UAAU,OAAO,UAAU;AACtC,eAAO,KAAK,KAAK,EAAE,UAAU,OAAO,UAAU;AAC9C,YAAI;AACF,eAAK,MAAM,kBAAkB,MAAM,SAAS;AAAA,QAC9C,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AACA,QAAI,KAAK,SAAS,OAAO,CAAC,KAAK,MAAO;AACtC,UAAM,eAAe;AACrB,SAAK,KAAK;AACV,UAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,QAAI,IAAI,KAAK,OAAO;AACpB,QAAI,SAAS,aAAa;AACxB,YAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC;AAC5C,WAAK,MAAM,MAAM,YAAY,YAAY,GAAG,MAAM,IAAI;AACtD;AAAA,IACF;AAEA,QAAI,SAAS,cAAc,IAAI,EAAG,KAAI,IAAI;AAC1C,QAAI,SAAS,UAAU,SAAS,SAAU,KAAI;AAC9C,cAAU,KAAK,OAAO,CAAC;AAAA,EACzB,CAAC;AAED,QAAM,UAAU,CAAC,UAAwB;AACvC,QAAI,CAAC,QAAQ,MAAM,cAAc,KAAK,UAAW;AACjD,UAAM,IAAI;AACV,WAAO;AACP,QAAI,EAAE,MAAO,cAAa,EAAE,KAAK;AACjC,QAAI,EAAE,SAAS,OAAO,CAAC,EAAE,MAAO;AAChC,oBAAgB;AAChB,UAAM,MAAM,EAAE;AACd,UAAM,OAAO,IAAI,QAAQ;AACzB,UAAM,QAAQ,IAAI,eAAe;AACjC,UAAM,WAAW,EAAE,KAAK,KAAK,IAAI,GAAG,YAAY,IAAI,IAAI,EAAE,EAAE;AAC5D,UAAM,IAAI,EAAE,OAAO,EAAE;AACrB,QAAI,SAAS,aAAa;AACxB,UAAI,UAAU,IAAI,UAAU;AAC5B,UAAI,MAAM,YAAY,YAAY,KAAK;AACvC,YAAM,KAAK,MAAM,IAAI,UAAU,OAAO,UAAU,CAAC;AAAA,IACnD,WAAW,SAAS,YAAY;AAC9B,YAAM,KAAK,IAAI,QAAQ,MAAM;AAC7B,UAAI,IAAI,CAAC,QAAQ,OAAQ,WAAW,QAAQ,IAAI,IAAM,aAAY,EAAE;AAAA,eAC3D,IAAI,IAAK,QAAO,KAAK,CAAC,UAAU,IAAI;AAAA,eACpC,IAAI,IAAI;AACf,eAAO,KAAK,GAAG,KAAK;AACpB,cAAM,KAAK,WAAW;AACtB,mBAAW;AAAA,MACb,MAAO,QAAO,KAAK,GAAG,KAAK;AAAA,IAC7B,WAAW,SAAS,UAAU;AAC5B,UAAI,KAAK,IAAI,CAAC,IAAI,QAAQ,OAAO,KAAK,IAAI,QAAQ,IAAI;AACpD,oBAAY,IAAI,QAAQ,MAAM,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC;AAAA,UAChD,QAAO,KAAK,GAAG,KAAK;AAAA,IAC3B,WAAW,SAAS,cAAc;AAChC,YAAM,OAAO,IAAI,QAAQ,QAAQ;AACjC,UAAI,IAAI,CAAC,QAAQ,IAAK,cAAa,IAAI;AAAA,eAC9B,IAAI,IAAK,QAAO,KAAK,CAAC,aAAa,IAAI;AAAA,eACvC,IAAI,GAAI,QAAO,KAAK,aAAa,IAAI;AAAA,UACzC,QAAO,KAAK,GAAG,KAAK;AAAA,IAC3B,WAAW,SAAS,QAAQ;AAC1B,UAAI,KAAK,IAAI,CAAC,IAAI,QAAQ,QAAQ,KAAK,IAAI,QAAQ,IAAI;AACrD,oBAAY,IAAI,QAAQ,MAAM,IAAI,YAAY,KAAK,KAAK,CAAC,KAAK,CAAC;AAAA,UAC5D,QAAO,KAAK,GAAG,KAAK;AAAA,IAC3B;AAAA,EACF;AACA,KAAG,aAAa,OAAO;AACvB,KAAG,iBAAiB,OAAO;AAE3B,KAAG,eAAe,CAAC,UAAU;AAC3B,UAAM,QAAS,MAAM,OAAuB,QAAqB,kBAAkB;AACnF,QAAI,CAAC,MAAO;AACZ,UAAM,eAAe;AACrB,cAAU,KAAK;AAAA,EACjB,CAAC;AAID;AAAA,IACE;AAAA,IACA,CAAC,UAAU;AACT,UAAI,CAAC,cAAe;AACpB,sBAAgB;AAChB,YAAM,gBAAgB;AACtB,YAAM,eAAe;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,gBAAgB,MAAM;AAC1B,UAAM,MAAM,MAAM,IAAI,QAAQ,MAAM,KAAK,WAAW,MAAM,KAAK;AAC/D,UAAM,IAAI,OAAO;AACjB,UAAM,KAAK,UAAU;AACrB,UAAM,KAAK,WAAW;AACtB,QAAI,KAAK;AACP,gBAAU;AACV,iBAAW;AAAA,IACb;AACA,WAAO,QAAQ,GAAG;AAAA,EACpB;AAEA,QAAM,OAAO,CAAC,SAAiB;AAC7B,QAAI;AACF,WAAK,UAAU,WAAW,UAAU,IAAI,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC1D,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,KAAG,SAAS,CAAC,UAAU;AACrB,UAAM,SAAU,MAAM,OAAuB,QAAqB,eAAe;AACjF,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,OAAO,QAAqB,aAAa;AACzD,QAAI,WAAW,OAAO,UAAU,SAAS,YAAY,GAAG;AACtD,aAAO,SAAS,GAAG,KAAK;AACxB;AAAA,IACF;AACA,UAAM,SAAS,OAAO,QAAQ;AAC9B,UAAM,KAAK,OAAO,QAAQ,MAAM;AAChC,UAAM,OAAO,OAAO,QAAQ,QAAQ;AACpC,UAAM,QAAQ,MAAM,IAAI,MAAM;AAC9B,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,cAAM,OAAO,OAAO,QAAQ,UAAU,aAAa,aAAa;AAChE,eAAO;AACP;AAAA,MACF,KAAK;AACH,eAAO,SAAS,CAAC,MAAM,OAAO;AAC9B;AAAA,MACF,KAAK;AACH,cAAM,IAAI,SAAS,QAAQ;AAC3B,cAAM,IAAI,aAAa;AACvB,kBAAU;AACV;AAAA,MACF,KAAK;AACH,cAAM,IAAI,SAAS;AACnB,cAAM,IAAI,aAAa;AACvB,kBAAU;AACV;AAAA,MACF,KAAK;AACH,qBAAa,IAAI;AACjB;AAAA,MACF,KAAK,cAAc;AACjB,cAAM,MAAM,UAAU,OAAO,IAAI;AACjC,YAAI,CAAC,MAAM,OAAO,OAAO,GAAG,EAAG,OAAM,OAAO,IAAI,GAAG;AACnD,kBAAU;AACV;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,MAAM,UAAU,OAAO,IAAI;AACjC,YAAI,CAAC,MAAM,MAAM,OAAO,GAAG,EAAG,OAAM,MAAM,IAAI,GAAG;AACjD,kBAAU;AACV;AAAA,MACF;AAAA,MACA,KAAK,iBAAiB;AACpB,cAAM,QAAQ,UAAU,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS,EAAE,KAAK,YAAY,IAAI;AACtF,YAAI,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,EAAG,UAAS,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAAA,aAClE;AACH,gBAAM,SAAS,MAAM,GAAG,EAAE;AAC1B,cAAI,OAAQ,YAAW,CAAC,OAAO,EAAE,CAAC;AAAA,QACpC;AACA,kBAAU;AACV;AAAA,MACF;AAAA,MACA,KAAK,WAAW;AACd,cAAM,QAAQ,OAAO,QAAQ;AAC7B,YAAI,MAAM,SAAS,IAAI,EAAE,MAAM,MAAO,OAAM,SAAS,OAAO,EAAE;AAAA,YACzD,OAAM,SAAS,IAAI,IAAI,KAAK;AACjC,cAAM,IAAI,OAAO;AACjB,kBAAU;AACV;AAAA,MACF;AAAA,MACA,KAAK,YAAY;AACf,cAAM,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,YAAI,EAAG,MAAK,EAAE,KAAK,QAAQ,EAAE;AAC7B,cAAM,IAAI,OAAO;AACjB,kBAAU;AACV;AAAA,MACF;AAAA,MACA,KAAK;AACH,aAAK,OAAO,QAAQ,QAAQ,EAAE;AAC9B;AAAA,MACF,KAAK;AACH,sBAAc;AACd;AAAA,MACF,KAAK,eAAe;AAClB,YAAI,OAAO,QAAQ,aAAa,QAAQ;AACtC,gBAAM,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,cAAI,GAAG;AACL,kBAAM,WAAW,IAAI,SAAS,EAAE,IAAI,EAAE,MAAM,OAAO,KAAK,CAAC;AACzD,uBAAW;AAAA,UACb;AACA;AAAA,QACF;AACA,cAAM,KAAK,WAAW,MAAM,KAAK,aAAa,KAAK,OAAO;AAC1D,mBAAW;AACX;AAAA,MACF;AAAA,MACA,KAAK;AACH,cAAM,KAAK,UAAU;AACrB,oBAAY,EAAE;AACd;AAAA,MACF,KAAK;AACH,cAAM,KAAK,UAAU;AACrB,mBAAW;AACX;AAAA,MACF,KAAK,iBAAiB;AACpB,cAAM,MAAM,OAAO,QAAQ,OAAO;AAClC,YAAI,OAAO,QAAQ,UAAU,SAAU,OAAM,OAAO,IAAI,GAAG;AAAA,YACtD,OAAM,OAAO,OAAO,GAAG;AAC5B,mBAAW;AACX;AAAA,MACF;AAAA,MACA,KAAK,kBAAkB;AACrB,cAAM,OAAO,CAAC,GAAG,KAAK,iBAA8B,qBAAqB,CAAC;AAC1E,cAAM,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM,EAAE;AAC9C,cAAM,OAAO,MAAM;AACjB,qBAAW,KAAK,IAAK,OAAM,UAAU,IAAI,CAAC;AAC1C,qBAAW;AAAA,QACb;AACA,YAAI,CAAC,QAAQ,EAAG,QAAO,KAAK;AAC5B,aAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,gBAAM,IAAI,IAAI,MAAM;AAClB,gBAAI,UAAU,IAAI,UAAU;AAC5B,gBAAI,MAAM,YAAY,cAAc,IAAI,cAAc,EAAE;AACxD,gBAAI,MAAM,UAAU;AAAA,UACtB,CAAC;AAAA,QACH,CAAC;AACD,cAAM,KAAK,SAAS,KAAK,KAAK,IAAI;AAClC;AAAA,MACF;AAAA,MACA,KAAK;AACH,cAAM,WAAW,IAAI,OAAO,QAAQ,OAAO,EAAE;AAC7C,mBAAW;AACX;AAAA,MACF,KAAK;AACH,cAAM,WAAW,OAAO,OAAO,QAAQ,OAAO,EAAE;AAChD,mBAAW;AACX;AAAA,MACF,KAAK,eAAe;AAClB,cAAM,MAAM,OAAO,QAAQ,OAAO;AAClC,mBAAW,KAAK,UAAU,MAAM;AAC9B,cAAI,EAAE,OAAO,MAAM,KAAK,OAAO,EAAE,MAAM,OAAO,WAAW,IAAK,OAAM,UAAU,IAAI,EAAE,EAAE;AACxF,cAAM,WAAW,OAAO,OAAO,QAAQ,OAAO,EAAE;AAChD,mBAAW;AACX;AAAA,MACF;AAAA,MACA,KAAK;AACH,cAAM,KAAK,OAAQ,OAAO,QAAQ,SAAsB;AACxD,mBAAW;AACX;AAAA,MACF,KAAK;AACH,cAAM,MAAM,OAAO;AACnB,cAAM,MAAM,aAAa;AACzB,0BAAkB;AAClB,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,cAAM,MAAM,OAAO;AACnB,cAAM,MAAM,aAAa;AACzB,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,0BAAkB,CAAC;AACnB,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,oBAAY,IAAI,UAAU;AAC1B;AAAA,MACF,KAAK;AACH,oBAAY,IAAI,SAAS;AACzB;AAAA,MACF,KAAK;AACH,mBAAW,CAAC,EAAE,CAAC;AACf,cAAM,MAAM,OAAO;AACnB,cAAM,MAAM,aAAa;AACzB,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,YAAI,KAAK,IAAI,EAAE,EAAG,YAAW,CAAC,EAAE,CAAC;AAAA,YAC5B,UAAS,CAAC,EAAE,CAAC;AAClB,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,YAAI,CAAC,MAAM,QAAQ,OAAO,EAAE,EAAG,OAAM,QAAQ,IAAI,EAAE;AACnD,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,YAAI,CAAC,MAAM,QAAQ,OAAO,EAAE,EAAG,OAAM,QAAQ,IAAI,EAAE;AACnD,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,cAAM,OAAO,OAAO;AACpB;AAAA,MACF,KAAK;AACH,cAAM,QAAQ;AACd,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,aAAK,QAAQ,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC7B;AAAA,MACF,KAAK;AACH,cAAM,UAAU;AAChB,wBAAgB;AAChB;AAAA,MACF,KAAK;AACH,cAAM,UAAU;AAChB,wBAAgB;AAChB;AAAA,MACF,KAAK,kBAAkB;AACrB,cAAM,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC7C,YAAI,EAAG,cAAa,CAAC;AACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,KAAG,YAAY,CAAC,UAAU;AACxB,UAAM,SAAU,MAAM,OAAuB,QAAqB,wBAAwB;AAC1F,QAAI,CAAC,UAAU,OAAO,QAAQ,yBAAyB,EAAG;AAC1D,UAAM,UAAU,OAAO,QAAQ,kBAAkB;AACjD,oBAAgB;AAAA,EAClB,CAAC;AAED,KAAG,UAAU,CAAC,UAAU;AACtB,UAAM,SAAS,MAAM;AACrB,UAAM,SAAS,OAAO,QAAQ;AAC9B,QAAI,WAAW,YAAa,QAAO,aAAc,OAA6B,KAAK;AAAA,aAC1E,WAAW,SAAU,OAAM,SAAU,OAA4B;AAAA,aACjE,WAAW,aAAa;AAC/B,YAAM,UAAU,OAAO,QAAQ;AAC/B,YAAM,OAAO,EAAE,KAAM,OAA6B;AAClD,UAAI,YAAY,MAAO,OAAM,IAAI,SAAS;AAC1C,UAAI,YAAY,QAAS,OAAM,MAAM,OAAO;AAC5C,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,KAAG,SAAS,CAAC,UAAU;AACrB,UAAM,SAAS,MAAM;AACrB,QAAI,OAAO,QAAQ,WAAW,SAAU;AACxC,UAAM,SAAS,OAAO;AACtB,oBAAgB;AAChB,WAAO;AAAA,EACT,CAAC;AAED,KAAG,WAAW,CAAC,UAAU;AACvB,UAAM,OAAO;AACb,UAAM,SAAS,MAAM;AACrB,QAAI,MAAM,QAAQ,UAAU;AAC1B,UAAI,cAAc,EAAG;AACrB,UAAI,MAAM,SAAS;AACjB,cAAM,UAAU;AAChB,wBAAgB;AAAA,MAClB;AACA;AAAA,IACF;AACA,QAAI,OAAO,QAAQ,yBAAyB,EAAG;AAE/C,QAAI,MAAM,QAAQ,iBAAkB,MAAM,YAAY,MAAM,QAAQ,OAAQ;AAC1E,YAAM,QAAQ,OAAO,QAAqB,kBAAkB;AAC5D,UAAI,OAAO;AACT,cAAM,eAAe;AACrB,kBAAU,KAAK;AAAA,MACjB;AACA;AAAA,IACF;AACA,QACE,MAAM,QAAQ,YACd,MAAM,QAAQ,eACd,MAAM,QAAQ,OACd,MAAM,QAAQ,KACd;AACA,YAAM,KAAK,OAAO,QAAQ,MAAM;AAChC,UAAI,OAAO,QAAQ,iCAAiC,KAAK,MAAM,QAAQ,KAAK;AAC1E,cAAM,eAAe;AACrB,oBAAY,EAAE;AAAA,MAChB,WAAW,OAAO,QAAQ,0BAA0B,KAAK,MAAM,QAAQ,KAAK;AAC1E,cAAM,eAAe;AACrB,qBAAa,OAAO,QAAQ,QAAQ,EAAE;AAAA,MACxC,WAAW,OAAO,QAAQ,yBAAyB,GAAG;AACpD,cAAM,eAAe;AACrB,oBAAY,IAAI,MAAM,QAAQ,MAAM,aAAa,SAAS;AAAA,MAC5D;AACA;AAAA,IACF;AACA,SACG,MAAM,QAAQ,WAAW,MAAM,QAAQ,QACxC,OAAO,QAAQ,2BAA2B,GAC1C;AACA,YAAM,eAAe;AACrB,aAAO,MAAM;AAAA,IACf;AAAA,EACF,CAAC;AAID,QAAM,YAAY,MAAM;AACtB,QAAI,QAAQ;AACV,YAAM,OAAO,OAAO,EAAE,KAAK,OAAO;AAClC,UAAI,OAAO,YAAY;AACrB,cAAM,IAAI,SAAS,cAAc,WAAW,OAAO,EAAE,EAAE,CAAC,GAAG,QAAQ;AAAA,IACvE;AACA,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AACnD,UAAI,EAAG,QAAO,aAAa,CAAC;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAEA,MAAI,YAAwB,MAAM;AAAA,EAAC;AACnC,QAAM,QAAQ,IAAI,QAAc,CAAC,YAAY;AAC3C,gBAAY,MAAM;AAChB,WAAK,QAAQ,QAAQ;AACrB,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,QAAM,SAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,IAAI,OAAO,KAAK,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI;AACpE,UAAI,EAAG,QAAO,aAAa,CAAC;AAC5B,UAAI,OAAO,SAAS;AAClB,cAAM,OAAO;AACb,YAAI,OAAO,GAAI,OAAM,OAAO,OAAO,EAAE,KAAK,OAAO;AACjD,YAAI,OAAO,YAAY,SAAS,OAAO;AACrC,gBAAM,IAAI,SAAS,WAAW,OAAO,EAAE,EAAE,CAAC,GAAG,QAAQ;AACvD,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,aAAa,WAAW;AACtB,UAAI,cAAc,MAAM,UAAW;AACnC,YAAM,YAAY;AAClB,YAAM,QAAQ,CAAC;AACf,YAAM,MAAM,EAAE,GAAG,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,MAAM,KAAK;AAC/D,YAAM,QAAQ,EAAE,GAAG,MAAM,OAAO,IAAI,MAAM,MAAM,KAAK;AACrD,YAAM,KAAK,KAAK;AAChB,eAAS;AACT,UAAI,SAAS;AACX,cAAM,MAAM,IAAI,IAAI,SAAS,IAAI;AACjC,YAAI,aAAa,IAAI,aAAa,SAAS;AAC3C,gBAAQ,aAAa,MAAM,IAAI,GAAG;AAAA,MACpC;AACA,UAAI,MAAM;AACR,gBAAQ;AACR,aAAK,QAAQ,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC/B,MAAO,QAAO;AAAA,IAChB;AAAA,IACA,SAAS,SAAS;AAChB,YAAM,UAAU;AAChB,UAAI,QAAS,OAAM,OAAO;AAC1B,UAAI;AACF,YAAI;AACF,uBAAa,QAAQ,UAAU,UAAU,MAAM,GAAG;AAAA,QACpD,QAAQ;AAAA,QAAC;AACX,mBAAa;AAAA,IACf;AAAA,IACA,UAAU;AACR,kBAAY;AACZ,gBAAU,MAAM;AAChB,cAAQ,MAAM;AACd,iBAAW,KAAK,OAAQ,cAAa,CAAC;AACtC,aAAO,MAAM;AACb,iBAAW,KAAK,UAAW,eAAc,CAAC;AAC1C,YAAM,MAAM;AACZ,WAAK,YAAY;AACjB,WAAK,UAAU,OAAO,YAAY,UAAU;AAAA,IAC9C;AAAA,EACF;AAEA,QAAM,YAA8C,CAAC;AAErD,MAAI,CAAC,MAAM;AACT,UAAM,QAAQ,CAAC,GAAI,QAAQ,SAAS,CAAC,CAAE,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,YAAY,EAAE,YAAY,IAAI,EAAG;AAC5F,UAAM,UAAU,QAAQ,WAAW,CAAC;AACpC,UAAM,aAAa,QAAQ,cAAc,CAAC;AAC1C,UAAM,iBAAiB,QAAQ,kBAAkB;AACjD,UAAM,YAAY,QAAQ,aAAa;AACvC,oBAAgB;AAChB,cAAU;AACV,cAAU;AACV,WAAO;AAAA,EACT;AAGA,YAAU;AAAA,IACR,YAAY,MAAM;AAChB,UAAI,QAAQ,MAAM,IAAI,QAAQ,MAAM,KAAK,WAAW,MAAM,KAAK,SAAU;AACzE,UAAI,MAAM,SAAS,aAAa,CAAC,KAAK,cAAc,aAAa,EAAG,QAAO;AAAA,UACtE,cAAa;AAAA,IACpB,GAAG,GAAM;AAAA,IACT,YAAY,MAAM,KAAK,eAAe,GAAG,GAAK;AAAA,EAChD;AAEA,WAAS;AACT,SAAO;AACP,UAAQ;AACR,OAAK,eAAe;AACpB,QAAM,OAAO,YAAY;AACvB,aAAS,UAAU,GAAG,CAAC,WAAW,WAAW;AAC3C,UAAI;AACF,cAAM,QAAQ;AACd,kBAAU;AACV,kBAAU;AACV;AAAA,MACF,QAAQ;AACN,cAAM,IAAI;AAAA,UAAQ,CAAC,YACjB,MAAM,KAAK,IAAI,KAAM,MAAM,KAAK,OAAO,GAAG,MAAM,QAAQ,IAAI,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,OAAK,KAAK;AACV,SAAO;AACT;;;ACt7DA,IAAM,WAAW;AAEjB,IAAM,gBAAgB,CAAC,SAAsB;AAC3C,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,SAAS,oBAAoB,aAAa,WAAW,SAAS;AACpE,MAAI,OAAO,cAAc,IAAI,QAAQ,EAAE,EAAG;AAC1C,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cAAc;AACpB,SAAO,OAAO,KAAK;AACrB;AAMO,IAAM,qBAAqB,CAAC,MAAmB,UAAwB,CAAC,MAAiB;AAC9F,QAAM,EAAE,SAAS,eAAe,MAAM,GAAG,KAAK,IAAI;AAClD,MAAI,aAAc,eAAc,IAAI;AACpC,MAAI,QAAS,MAAK,QAAQ,CAAC,YAAY,QAAQ,MAAM,OAAO;AAC5D,SAAO,UAAU,MAAM,EAAE,UAAU,MAAM,SAAS,OAAO,GAAG,KAAK,CAAC;AACpE;",
6
+ "names": ["init", "n", "code"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,90 @@
1
1
  {
2
2
  "name": "@crvouga/mockingbird-notifications",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Notification test harness for Mockingbird fleets: every SMS, email and push your app sends through the Twilio, Resend, FCM, SES, SNS, Customer.io and Mailosaur emulators, on lifelike phone and mail screens and a JSON API for tests.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": ">=22.0.0"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./server": {
23
+ "types": "./dist/server.d.ts",
24
+ "default": "./dist/server.js"
25
+ },
26
+ "./ui": {
27
+ "types": "./dist/ui.d.ts",
28
+ "default": "./dist/ui.js"
29
+ }
30
+ },
31
+ "bin": {
32
+ "mockingbird-notifications": "./dist/cli.js"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "provenance": true
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/crvouga/mockingbird.git",
41
+ "directory": "packages/notifications"
42
+ },
43
+ "homepage": "https://github.com/crvouga/mockingbird/tree/main/packages/notifications#readme",
44
+ "keywords": [
45
+ "mockingbird",
46
+ "notifications",
47
+ "sms",
48
+ "email",
49
+ "push",
50
+ "testing"
51
+ ],
52
+ "mockingbird": {
53
+ "runtime": "node",
54
+ "layer": "tool"
55
+ },
56
+ "scripts": {
57
+ "generate": "bun scripts/build-ui.ts",
58
+ "build": "bun scripts/build-ui.ts && bun ../../scripts/bundle-service.ts",
59
+ "typecheck": "tsc -p tsconfig.json --noEmit",
60
+ "lint": "biome check .",
61
+ "test": "bun test",
62
+ "dev": "bun scripts/dev.ts",
63
+ "pack:check": "bun ../../scripts/pack-check.ts",
64
+ "portability": "bun ../../scripts/portability.ts",
65
+ "storybook": "storybook dev -p 6007 --no-open",
66
+ "storybook:build": "storybook build -o storybook-static --quiet",
67
+ "catalog:screenshots": "bun scripts/catalog-screenshots.ts",
68
+ "e2e": "bun test ./e2e/ui.e2e.ts"
69
+ },
70
+ "devDependencies": {
71
+ "@aws-sdk/client-ses": "3.1001.0",
72
+ "@aws-sdk/client-sns": "3.1001.0",
73
+ "@crvouga/mockingbird-adapter-node": "0.0.0-development",
74
+ "@crvouga/mockingbird-fleet": "0.6.0",
75
+ "@crvouga/mockingbird-service-customerio": "3.11.0",
76
+ "@crvouga/mockingbird-service-fcm": "2.11.0",
77
+ "@crvouga/mockingbird-service-mailosaur": "3.11.0",
78
+ "@crvouga/mockingbird-service-resend": "3.11.0",
79
+ "@crvouga/mockingbird-service-ses": "1.12.0",
80
+ "@crvouga/mockingbird-service-sns": "1.12.0",
81
+ "@crvouga/mockingbird-service-twilio": "3.11.0",
82
+ "@playwright/test": "1.65.0",
83
+ "@storybook/html-vite": "10.6.6",
84
+ "@types/bun": "1.4.0",
85
+ "@types/node": "22.20.1",
86
+ "esbuild": "0.28.2",
87
+ "storybook": "10.6.6",
88
+ "vite": "8.3.7"
89
+ }
90
+ }