@ykurilov/agent-chat-react 0.74.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/voice-tags.ts","../src/voice-turn.ts","../src/labels.tsx","../src/live-demos/answer-fences.ts","../src/diagrams/mermaid-blocks.ts","../src/live-demos/live-demo-document.ts","../src/step-reset.ts","../src/chat-state/chat-state.ts","../src/close-stopped-tool-calls.ts","../src/use-agent-chat.ts","../src/chat-types.ts","../src/liveness/connection-errors.ts","../src/liveness/reconnect-attempts.ts","../src/chat-watch/chat-watch.ts","../src/liveness/silence-watchdog.ts","../src/liveness/request-deadline.ts","../src/spending-status.ts","../src/app-bridge/app-bridge.ts","../src/app-bridge/remembered-ids.ts","../src/app-bridge/browser-calls.ts","../src/pause.ts","../src/app-refusal.ts","../src/server-refusal.ts","../src/helpers/helper-call.ts","../src/widgets/use-widget-answer.ts","../src/widgets/widget-answer-keys.ts","../src/widgets/widget-messages.ts","../src/app-events.ts","../src/chat-access.ts","../src/background-tasks/use-background-tasks.ts","../src/notices/agent-notices.ts","../src/steering/corrections.ts","../src/use-model-choice.ts","../src/thinking-status/thinking-status.tsx","../src/work-view/step-icons.ts","../src/agent-chat.tsx","../src/expanded-parts.ts","../src/feed-announcement.ts","../src/without-markdown.ts","../src/message-feed.tsx","../src/message-editing/use-message-editing.ts","../src/answer-editing/answer-editing.ts","../src/diagrams/diagram-renders.ts","../src/diagrams/use-diagram-blocks.ts","../src/diagrams/unique-svg-ids.ts","../src/diagrams/use-diagram-colors.ts","../src/live-demos/use-live-demo-blocks.tsx","../src/live-demos/live-demo-frame.tsx","../src/media-viewer/collect-viewer-items.ts","../src/media-viewer/answer-file-images.ts","../src/subject-chat/subject-chat-loading.tsx","../src/subject-chat/use-subject-chat-id.ts","../src/media-viewer/open-attachments.ts","../src/media-viewer/generated-images.ts","../src/media-viewer/use-media-viewer.ts","../src/widgets/widget-view.ts","../src/widgets/form-check.ts","../src/widgets/suggested-replies.ts","../src/widgets/widget-part.tsx","../src/widgets/custom-widget-guard.tsx","../src/use-voice-dictation.ts","../src/dictation-field.ts","../src/use-voice-dialog.ts","../src/message-actions.ts","../src/composer-actions.ts","../src/turn-finished.ts","../src/work-view/step-phrases.ts","../src/work-view/work-layout.ts","../src/work-view/work-steps-part.tsx","../src/chat-list/use-chat-list.ts","../src/chat-list/active-chat-memory.ts","../src/chat-list/chat-list-client.ts","../src/chat-list/use-chat-list-view.ts","../src/chat-list/use-workspace-list-sync.ts"],"sourcesContent":["/*\n * Теги голоса: в голосовом режиме модель ставит в ответ теги ElevenLabs в квадратных скобках — «[laughs]»,\n * «[whispers]», «[warm, quiet voice]». Голос их отыгрывает, а на экране, в копии, в сводках и поиске их быть не должно.\n * Копия правила в ядре (`@ykurilov/agent-chat-core`) совпадает с этой: ядро браузеру не нужно, экран ядра не знает.\n * Согласие копий держит тест в пакете voice-dialog.\n *\n * Тег — латинские буквы, пробелы, запятые и дефисы между ними (строчные, либо слово с заглавной буквы:\n * «[Laughs]»), от 3 до 40 знаков, не больше шести слов; допустим и закрывающий «[/whispers]». Вплотную к слову тег тоже\n * тег («ну[sighs]»): голос так его и отыгрывает. Курсив и жирный вокруг тега («*[laughs]*») уходят вместе с ним.\n * Всё остальное — обычные скобки и остаётся: «[1]», «[x]», «[ ]», «[TODO]», «[смеётся]», «[sic]»,\n * ссылки Markdown «[текст](адрес)» и «[текст][ссылка]», определения ссылок «[ссылка]: адрес», строчный код и блоки кода.\n *\n * Правило применяется там, где человек видит ответ голосового хода: в тексте голосового ответа теги слышны, но не видны.\n * В ответах текстового режима и в сводках сжатия оно не применяется: там скобки — обычный текст.\n */\n\nexport interface VoiceTagOptions {\n /** Недописанный тег в конце текста («[laug») тоже вырезается: так в потоке ответа тег не мелькает на экране. */\n partialTail?: boolean;\n}\n\n/** Тег, найденный в тексте: `at` — место в тексте без тегов, где он стоял. */\nexport interface VoiceTagMatch {\n at: number;\n name: string;\n}\n\nexport interface SplitVoiceTags {\n /** Текст без тегов: рядом стоявший пробел убран, остальное не изменено. */\n plain: string;\n tags: VoiceTagMatch[];\n}\n\nconst MIN_LETTERS = 3;\nconst MAX_LENGTH = 40;\nconst MAX_WORDS = 6;\n/** Латинские слова в скобках, которые тегами голоса не бывают. */\nconst NOT_TAGS = new Set([\"sic\", \"ibid\", \"note\", \"todo\", \"edit\", \"link\"]);\n\nconst CANDIDATE = /\\[(\\/?[A-Za-z ,-]{1,40})\\]/g;\nconst PARTIAL_TAIL = /\\[\\/?[A-Za-z ,-]{0,40}$/;\nconst HORIZONTAL_SPACE = new Set([\" \", \"\\t\"]);\n/** Знаки курсива, жирного и зачёркивания: тег в них уходит вместе со знаками. */\nconst EMPHASIS = new Set([\"*\", \"_\", \"~\"]);\n\ninterface Span {\n start: number;\n end: number;\n /** `null` — недописанный тег. */\n name: string | null;\n}\n\nfunction isTagBody(body: string): boolean {\n const name = body.startsWith(\"/\") ? body.slice(1) : body;\n if (name.length > MAX_LENGTH || name !== name.trim() || !/^[A-Za-z]+(?:[ ,-]+[A-Za-z]+)*$/.test(name)) return false;\n if (name.replace(/[^A-Za-z]/g, \"\").length < MIN_LETTERS) return false;\n if (name.split(/[ ,-]+/).length > MAX_WORDS) return false;\n // Строчные слова или первое слово с заглавной буквы («Laughs», «Warm, quiet voice»); «TODO», «WIP» — не теги.\n if (name !== name.toLowerCase() && !/^[A-Z][a-z]*(?:[ ,-]+[a-z]+)*$/.test(name)) return false;\n return !NOT_TAGS.has(name.toLowerCase());\n}\n\n/** Тег не стоит впритык к закрытой скобке («[a][ref]») и не открывает ссылку или определение. */\nfunction standsAlone(text: string, start: number, end: number): boolean {\n const before = text[start - 1];\n if (before !== undefined && /[\\])]/.test(before)) return false;\n const after = text[end];\n return after === undefined || !\"([:\".includes(after);\n}\n\n/** Знаки курсива и жирного, которые обнимают тег с двух сторон («*[laughs]*», «_[sighs]_»): сколько их уходит вместе с тегом. */\nfunction emphasisAround(text: string, start: number, end: number): number {\n let left = start;\n while (left > 0 && EMPHASIS.has(text[left - 1]!)) left--;\n // Подчёркивание внутри слова («snake_[tag]») — не курсив.\n if (left > 0 && /[\\p{L}\\p{N}]/u.test(text[left - 1]!)) return 0;\n let right = end;\n while (right < text.length && EMPHASIS.has(text[right]!)) right++;\n let paired = 0;\n while (paired < start - left && paired < right - end && text[start - 1 - paired] === text[end + paired]) paired++;\n return paired;\n}\n\n/** Знаки, которые в тексте не трогаем: строчный код и блоки кода. */\nfunction codeMask(text: string): Uint8Array {\n const mask = new Uint8Array(text.length);\n let fence: { char: string; length: number } | null = null;\n let offset = 0;\n for (const line of text.split(\"\\n\")) {\n const lineEnd = offset + line.length;\n if (fence) {\n mask.fill(1, offset, lineEnd);\n const closing = /^\\s{0,3}(`{3,}|~{3,})\\s*$/.exec(line);\n if (closing && closing[1]![0] === fence.char && closing[1]!.length >= fence.length) fence = null;\n } else {\n const opening = /^\\s{0,3}(`{3,}|~{3,})(.*)$/.exec(line);\n if (opening && !(opening[1]![0] === \"`\" && opening[2]!.includes(\"`\"))) {\n mask.fill(1, offset, lineEnd);\n fence = { char: opening[1]![0]!, length: opening[1]!.length };\n } else {\n maskInlineCode(line, offset, mask);\n }\n }\n offset = lineEnd + 1;\n }\n return mask;\n}\n\nfunction maskInlineCode(line: string, offset: number, mask: Uint8Array): void {\n const runEnd = (from: number) => {\n let i = from;\n while (line[i] === \"`\") i++;\n return i;\n };\n let i = 0;\n while (i < line.length) {\n if (line[i] !== \"`\") {\n i++;\n continue;\n }\n const openEnd = runEnd(i);\n let search = openEnd;\n let closeEnd = -1;\n while (search < line.length) {\n if (line[search] !== \"`\") {\n search++;\n continue;\n }\n const end = runEnd(search);\n if (end - search === openEnd - i) {\n closeEnd = end;\n break;\n }\n search = end;\n }\n if (closeEnd < 0) {\n i = openEnd;\n continue;\n }\n mask.fill(1, offset + i, offset + closeEnd);\n i = closeEnd;\n }\n}\n\nfunction findSpans(text: string, partialTail: boolean): Span[] {\n const mask = codeMask(text);\n const spans: Span[] = [];\n for (const match of text.matchAll(CANDIDATE)) {\n const start = match.index;\n const end = start + match[0].length;\n if (mask[start] || !isTagBody(match[1]!) || !standsAlone(text, start, end)) continue;\n const wrap = emphasisAround(text, start, end);\n spans.push({ start: start - wrap, end: end + wrap, name: match[1]!.replace(/\\s+/g, \" \").trim().toLowerCase() });\n }\n if (partialTail) {\n const tail = PARTIAL_TAIL.exec(text);\n if (tail && !mask[tail.index] && standsAlone(text, tail.index, text.length) && !spans.some((span) => span.end > tail.index)) {\n spans.push({ start: tail.index, end: text.length, name: null });\n }\n }\n return spans;\n}\n\n/** Длина перевода строки («\\n» или «\\r\\n») в позиции `at`; 0 — там не конец строки. */\nfunction lineBreakLength(text: string, at: number): number {\n if (text[at] === \"\\n\") return 1;\n return text[at] === \"\\r\" && text[at + 1] === \"\\n\" ? 2 : 0;\n}\n\n/** Длина перевода строки, оканчивающегося перед позицией `at` (там стоит «\\n»; перед ним может быть «\\r»). */\nfunction lineBreakBefore(text: string, at: number): number {\n return text[at - 2] === \"\\r\" ? 2 : 1;\n}\n\n/** Теги, стоящие подряд (между ними только пробелы), вырезаются вместе: правило пробелов одно на всю цепочку. */\nfunction groupSpans(text: string, spans: Span[]): Span[][] {\n const groups: Span[][] = [];\n for (const span of spans) {\n const last = groups.at(-1);\n const previous = last?.at(-1);\n if (last && previous && [...text.slice(previous.end, span.start)].every((char) => HORIZONTAL_SPACE.has(char))) last.push(span);\n else groups.push([span]);\n }\n return groups;\n}\n\n/** Текст без тегов голоса и список вырезанных тегов с их местом. */\nexport function splitVoiceTags(text: string, options: VoiceTagOptions = {}): SplitVoiceTags {\n if (!text.includes(\"[\")) return { plain: text, tags: [] };\n const groups = groupSpans(text, findSpans(text, options.partialTail === true));\n if (groups.length === 0) return { plain: text, tags: [] };\n let plain = \"\";\n let copied = 0;\n const tags: VoiceTagMatch[] = [];\n for (const group of groups) {\n const start = group[0]!.start;\n const end = group.at(-1)!.end;\n let spaceBefore = start;\n while (spaceBefore > 0 && HORIZONTAL_SPACE.has(text[spaceBefore - 1]!)) spaceBefore--;\n let spaceAfter = end;\n while (spaceAfter < text.length && HORIZONTAL_SPACE.has(text[spaceAfter]!)) spaceAfter++;\n const lineBreakAfter = lineBreakLength(text, spaceAfter);\n const lineStart = spaceBefore === 0 || text[spaceBefore - 1] === \"\\n\";\n const lineEnd = spaceAfter === text.length || lineBreakAfter > 0;\n const beforePunctuation = spaceAfter < text.length && /[.,!?…;:)\\]»”]/.test(text[spaceAfter]!);\n const glued = spaceBefore === start && !lineStart && /[\\p{L}\\p{N}]/u.test(text[start - 1] ?? \"\");\n\n let cutFrom = start;\n let cutTo = spaceAfter;\n let filler = \"\";\n if (lineStart && lineEnd) {\n // Тег один на строке: уходит вся строка вместе с одним переводом.\n cutFrom = spaceBefore;\n if (lineBreakAfter > 0) cutTo = spaceAfter + lineBreakAfter;\n else if (spaceBefore > 0) cutFrom = spaceBefore - lineBreakBefore(text, spaceBefore);\n } else if (lineEnd || beforePunctuation) {\n cutFrom = spaceBefore;\n } else if (glued) {\n // «ну[sighs] ещё»: пробел справа остаётся; «ну[sighs]ещё»: слова не склеиваются.\n if (spaceAfter > end) cutTo = end;\n else if (/[\\p{L}\\p{N}]/u.test(text[end] ?? \"\")) filler = \" \";\n }\n plain += text.slice(copied, cutFrom);\n for (const span of group) if (span.name !== null) tags.push({ at: plain.length, name: span.name });\n plain += filler;\n copied = cutTo;\n }\n plain += text.slice(copied);\n return { plain, tags };\n}\n\n/** Текст без тегов голоса. */\nexport function stripVoiceTags(text: string, options: VoiceTagOptions = {}): string {\n return splitVoiceTags(text, options).plain;\n}\n","import { useRef } from \"react\";\nimport type { AgentChatMessage } from \"./chat-types\";\n\n/** Тип части-отметки: сообщение человека сказано голосом (то же, что `VOICE_MODE_PART_TYPE` в ядре). */\nexport const VOICE_MODE_PART_TYPE = \"data-voice-mode\";\n\nexport function isVoiceMessage(message: Pick<AgentChatMessage, \"parts\">): boolean {\n return message.parts.some((part) => part.type === VOICE_MODE_PART_TYPE);\n}\n\n/**\n * Ответы голосовых ходов: сообщения агента, идущие за сообщением человека с отметкой голосового режима.\n * Только в них теги голоса («[laughs]») слышны, но не видны; в остальных скобки — обычный текст.\n */\nexport function voiceAnswerIds(messages: readonly AgentChatMessage[]): Set<string> {\n const ids = new Set<string>();\n let voiceTurn = false;\n for (const message of messages) {\n if (message.role === \"user\") voiceTurn = isVoiceMessage(message);\n else if (message.role === \"assistant\" && voiceTurn) ids.add(message.id);\n }\n return ids;\n}\n\n/** То же, что `voiceAnswerIds`, но пока состав не изменился — тот же объект: лента не перерисовывается на каждый кусок ответа. */\nexport function useVoiceAnswerIds(messages: readonly AgentChatMessage[]): ReadonlySet<string> {\n const previous = useRef<Set<string>>(new Set());\n const next = voiceAnswerIds(messages);\n if (next.size !== previous.current.size || [...next].some((id) => !previous.current.has(id))) previous.current = next;\n return previous.current;\n}\n","import { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ChatError, ErrorNoticeKind, ReasoningLevelId } from \"./chat-types\";\nimport type { MemoryFailure } from \"./memory/memory-types\";\nimport type { ChatOriginView, HelperCallStatus, TaskStatusView } from \"./skin-contract\";\nimport type { SpendingInfo } from \"./spending-status\";\nimport type { VoiceDialogError, VoiceDialogState } from \"./voice-dialog\";\nimport type { VoiceErrorDetails, VoiceErrorKind } from \"./voice-input\";\n\n/** Почему правка не ушла или не принята: идёт ответ, нет связи, сервер не принял. */\nexport type EditErrorKind = \"busy\" | \"offline\" | \"rejected\" | \"spendingLimit\" | \"signedOut\" | \"tooManyTurns\" | \"forbiddenOrigin\";\n\n/** Надписи голосового режима (договор скина, версия 10). */\nexport interface VoiceDialogLabels {\n /** Кнопка включения режима. */\n start: string;\n /** Кнопка выхода из режима. */\n exit: string;\n /** Название меню голосов и подпись кнопки. */\n voiceMenu: string;\n /** Состояние режима словами. */\n states: Record<VoiceDialogState, string>;\n errors: Record<VoiceDialogError, string>;\n /** Отметка у ответа, озвучку которого оборвали: «Вы перебили: озвучено до «…»». */\n interruptedMark: (spokenTail: string) => string;\n}\n\n/** Надписи виджетов агента (договор скина, версия 13). */\nexport interface WidgetLabels {\n /** Пометка у виджета, на который человек ответил сообщением. */\n answeredByMessage: string;\n /** Кнопка отправки выбора, оценки и формы. */\n submit: string;\n /** Подсказка под вариантами: ответ можно написать словами. */\n choose: string;\n /** Подпись одной звезды или деления шкалы для экранного диктора: «Оценка 3 из 5». */\n ratingStars: (value: number, total: number) => string;\n /** Ответ не отправился. */\n answerFailed: string;\n /** Виджет из другой ветки: нажать его нельзя. */\n notCurrentBranch: string;\n /** Запасной вид, когда у скина нет виджета: ответить можно сообщением. */\n fallback: string;\n /** Пометка обязательного поля формы и ошибка пустого обязательного поля. */\n formRequired: string;\n /** Для экранного диктора, когда ход остановился на виджете. */\n asked: string;\n /** Для экранного диктора, когда ответ ушёл на сервер. */\n answerSent: string;\n /** Кнопка отправки формы. */\n formSubmit: string;\n /** Пределы выбора нескольких вариантов: «Выберите от 1 до 3». */\n choiceRange: (min: number, max: number) => string;\n /** Число или дата вне пределов; нет одного края — `undefined`. */\n formOutOfRange: (min: string | undefined, max: string | undefined) => string;\n /** В поле числа не число. */\n formNotNumber: string;\n /** В поле даты не дата. */\n formNotDate: string;\n /** Текст длиннее предела. */\n formTooLong: (max: number) => string;\n /** Сервер ответил, что вопрос уже не ждёт: на него ответили в другой вкладке или закрыли. */\n alreadyAnswered: string;\n /** Сервер не принял ответ (не прошёл проверку). */\n answerRejected: string;\n /** Проверка ответа на сервере не уложилась в срок: ответ не принят, виджет открыт. */\n answerCheckTimeout: string;\n}\n\n/** Надписи схем (договор скина, версия 13). */\nexport interface DiagramLabels {\n loading: string;\n failed: string;\n /** Подпись кнопки «открыть схему во весь экран». */\n open: string;\n}\n\n/** Надписи живых демонстраций (договор скина, версия 22). */\nexport interface LiveDemoLabels {\n /** Плашка, пока блок дописывается. */\n loading: string;\n /** Подпись рамки для диктора. */\n title: string;\n /** Пометка под кодом: демонстрация больше предела. */\n tooLarge: string;\n /** Пометка под кодом: ответ закончился, а блок не закрыт. */\n unfinished: string;\n /** Пометка под кодом: демонстрация попыталась уйти со своей страницы. */\n leftPage: string;\n /** Пометка под кодом: в чате есть чужой текст (интернет, файл), демонстрации в нём не запускаются. */\n externalContent: string;\n}\n\n/** Надписи картинок агента и вложений-картинок (договор скина, версия 13). */\nexport interface ImageLabels {\n loading: string;\n /** Подпись упавшей картинки; причину добавляет скин. */\n failed: string;\n /** Создание остановлено кнопкой «Стоп». */\n stopped: string;\n /** Подпись кнопки «открыть картинку в окне просмотра». */\n open: string;\n}\n\n/** Надписи видео агента (договор скина, версия 14). */\nexport interface VideoLabels {\n /** Подпись кнопки-карточки, которая запускает плеер. */\n play: string;\n /** «с 3:20» — с какой минуты начнётся видео. */\n from: (time: string) => string;\n fullscreen: string;\n}\n\n/** Надписи помощников (договор скина, версия 18). */\nexport interface HelperLabels {\n /** Заголовок задания помощнику: «Поручено». */\n assigned: string;\n /** Заголовок итога помощника: «Вернул». */\n returned: string;\n status: Record<HelperCallStatus, string>;\n}\n\n/** Надписи фоновых задач (договор скина, версия 18). */\nexport interface TaskLabels {\n status: Record<TaskStatusView, string>;\n cancel: string;\n /** Пока отмена идёт. */\n canceling: string;\n /** «идёт 2 мин 5 сек» у идущей задачи, «заняла 2 мин 5 сек» у законченной. */\n elapsed: (ms: number, finished: boolean) => string;\n}\n\n/** Надписи служебных сообщений (договор скина, версия 18). */\nexport interface NoticeLabels {\n taskDone: string;\n taskFailed: string;\n taskCanceled: string;\n taskInterrupted: string;\n taskTimedOut: string;\n taskAwaitingApproval: string;\n conversationStarted: string;\n /** Подпись реплики живого сотрудника у скина без `StaffMessage` (версия 25): «Сотрудник Ольга». */\n staffSaid: (staffName: string | undefined) => string;\n /** Служебное сообщение одной строкой — у скина без `AgentNoticeView`: «Отчёт»: задача готова. Без названия — только `status`. */\n line: (title: string, status: string) => string;\n}\n\n/** Строки статуса хода в мессенджерах: что делает агент, пока ответа ещё нет (договор скина, версия 18). */\nexport interface MessengerStatusLabels {\n thinking: string;\n /** «Использую инструмент: поиск в памяти». */\n usingTool: (title: string) => string;\n /** «Помощник «Аналитик» работает». */\n helperWorking: (name: string) => string;\n /** «Задача «Отчёт» ушла в фон». */\n taskInBackground: (title: string) => string;\n waitingForApproval: string;\n}\n\n/** Надписи простого вида работы агента (`workView: \"simple\"`, договор скина, версия 23). */\nexport interface WorkViewLabels {\n /** Шаг идёт, а фразы для инструмента нет: «Работаю». Без многоточия: его добавляет `live`. */\n stepRunning: string;\n /** Шаг готов, а фразы нет: «Сделал шаг». */\n stepDone: string;\n /** Шаг не получился: «Делаю календарь — не вышло»; `running` — фраза «идёт» без многоточия. */\n stepFailed: (running: string) => string;\n /** Шаг без фразы не получился: «Шаг не получился». */\n stepFailedUnknown: string;\n /** Ответ закончился посреди шага («Стоп», обрыв): «Делаю календарь — не закончил». */\n stepUnfinished: (running: string) => string;\n /** То же без фразы: «Шаг не закончен». */\n stepUnfinishedUnknown: string;\n /** Одинаковые шаги подряд одной строкой: «Поискал в интернете ×5». */\n stepRepeated: (phrase: string, count: number) => string;\n /** Модель думает: «Думаю». */\n thinking: string;\n /** Раздумье без заголовка в списке шагов (договор 28): «Думал 4 сек»; времени нет — «Подумал». */\n thought: (durationMs: number | null) => string;\n /** Скрытые значки длинного ряда (договор 28): «+3». */\n moreSteps: (count: number) => string;\n /** Живая строка: «Рисую календарь…». */\n live: (phrase: string) => string;\n /** Свёрнутые шаги: «Сделано 3 шага»; `count` — число вызовов, вместе с повторами. */\n stepsDone: (count: number) => string;\n /** Помощник работает и закончил, если приложение не дало фраз: «Помощник «Аналитик» работает». */\n helperRunning: (name: string) => string;\n helperDone: (name: string) => string;\n}\n\n/** Надписи окна просмотра (договор скина, версия 13). */\nexport interface ViewerLabels {\n close: string;\n next: string;\n previous: string;\n /** Кнопка «вписать в экран» после масштабирования. */\n fit: string;\n /** Счётчик «3 из 12»; только при нескольких элементах. */\n counter: (position: number, total: number) => string;\n}\n\n/** Надписи для голоса вне голосового режима (договор скина, версия 13). */\nexport interface VoiceSpeechLabels {\n /** Что слышно вместо блока схемы. */\n diagramOnScreen: string;\n /** Что слышно на месте карточки видео (версия 14). */\n videoOnScreen: string;\n /** Фраза к озвучке ждущего выбора: вопрос и варианты. */\n widgetChoices: (question: string, options: readonly string[]) => string;\n /** Фраза к озвучке ждущей формы, оценки или своего виджета; `title` — вопрос или заголовок, если есть. */\n widgetOnScreen: (title?: string) => string;\n}\n\n/**\n * Все надписи интерфейса чата. Скин берёт их отсюда и сам строк не пишет. Группы версии 13 (`widgets`, `diagram`, `images`,\n * `viewer`, `voice`) и версии 14 (`videos`) необязательные: словарь приложения, написанный раньше, работает, недостающее берётся из `russianLabels`;\n * внутри группы можно перекрыть любой один ключ.\n */\nexport interface ComposerSettingsLabels {\n levels: Record<string, string>;\n model: string; reasoning: string; tools: string; settings: string; done: string; via: string;\n search: string; showReasoning: string; project: string; file: string; image: string;\n textModels: string; imageModels: string; models: string; noResults: string; price: string; input: string; output: string;\n}\n\nexport interface ChatLabels {\n /** Приветствие пустого чата. Скины без приветствия игнорируют. */\n welcome?: { title?: string; description?: string };\n composerSettings?: Partial<ComposerSettingsLabels>;\n /**\n * Язык словаря (BCP 47): `ru` у `russianLabels`, `en` у `englishLabels`. Скин по нему выбирает свои надписи — например,\n * Тандем ставит словарь дизайн-системы на том же языке. Нет — скин решает сам (Тандем: русский или словарь приложения).\n */\n locale?: string;\n composerPlaceholder: string;\n /** Подпись поля ввода для экранного диктора. */\n composerLabel: string;\n send: string;\n stop: string;\n copyAnswer: string;\n copied: string;\n copyCode: string;\n reasoningRunning: string;\n reasoningDone: (seconds: number) => string;\n /**\n * Фразы, которые сменяют друг друга каждые 3–4 с, пока модель думает и текста ответа ещё нет. Пустой набор — одна\n * `reasoningRunning`. Нет — русские по умолчанию, а если `reasoningRunning` своя (словарь на другом языке) — она одна.\n */\n thinkingPhrases?: readonly string[];\n /** Время ожидания ответа рядом с фразой: «12 с», «1 мин 5 с». Нет, а `reasoningRunning` своя — «0:12», «1:05». */\n thinkingElapsed?: (seconds: number) => string;\n toolRunning: (toolName: string) => string;\n toolDone: (toolName: string) => string;\n toolError: (toolName: string) => string;\n /** Подпись картинки со снимком страницы для экранного диктора. */\n screenshotAlt: string;\n /** Снимок страницы не загрузился: вместо картинки этот текст и ссылка на страницу. */\n screenshotUnavailable: string;\n answerStopped: string;\n answerInterrupted: string;\n answerEmpty: string;\n /** Подпись ссылки вместо картинки с чужого адреса, если у картинки нет своей подписи. Нет — русская по умолчанию. */\n externalImageLink?: string;\n /** Отметка у ответа, который модель не дописала: упёрлась в предел длины ответа. Нет — русская по умолчанию. */\n answerCutByLength?: string;\n /** Отметка у ответа агента, который исправил человек (версия 15). Нет — русская по умолчанию. */\n answerEdited?: string;\n /** Серая отметка у ответа, который не дошёл до человека (версия 24). Нет — русская по умолчанию. */\n answerUndelivered?: string;\n /** Отметка у вызова инструмента, чей результат исправил человек (версия 15). Нет — русская по умолчанию. */\n toolOutputEdited?: string;\n /** Кнопка «изменить» у ответа агента (версия 15). Нет — русская по умолчанию. */\n editAnswer?: string;\n /** Пометка у поправки, которую агент ещё не взял (версия 21): «учтёт на следующем шаге». Нет — русская по умолчанию. */\n steeringPending?: string;\n retry: string;\n /** Идут попытки подключиться к серверу. */\n reconnecting: string;\n /** Попытки исчерпаны: сервер недоступен. */\n connectionLost: string;\n /** Чат чужой или его нет: сервер не различает эти случаи. */\n chatNotFound: string;\n errors: Record<ErrorNoticeKind, string>;\n /**\n * Подпись сбоя ответа функцией от сбоя: вид и подробности `details`, если сервер их отдал (право `errorDetails`).\n * `plain` — `errors[kind]`; вернуть его — подпись как раньше. Нет функции — `errors[kind]`. Скину уходит текстом\n * (`ErrorNotice` `text`), без разметки; диктору — тот же текст.\n */\n errorText?: (error: ChatError, plain: string) => string;\n /** Названия уровней рассуждений в меню выбора; названия моделей и провайдеров приходят с сервера. */\n reasoningLevels: Record<ReasoningLevelId, string>;\n /** Разделитель на границе сжатой истории. */\n historyCompacted: string;\n /** Пояснение под разделителем, когда открыта сводка. */\n historyCompactedHint: string;\n /** Где сейчас переключатель веток, для экранного диктора: «Вариант 2 из 3». */\n branchPosition: (position: number, count: number) => string;\n previousBranch: string;\n nextBranch: string;\n /** Кнопка у последнего ответа: новый ответ на тот же вопрос. */\n regenerate: string;\n /** Кнопка у отправленного сообщения и подпись поля правки. */\n editMessage: string;\n /** Отправить исправленное сообщение. */\n saveEdit: string;\n cancelEdit: string;\n /** Под полем правки, когда правку не удалось отправить; текст в поле остаётся. */\n editErrors: Record<EditErrorKind, string>;\n /** Под полем правки, когда сервер отказал по лимиту сообщений (у денег — `editErrors.spendingLimit`). */\n editMessageLimit?: string;\n /** Не удалось сохранить выбор модели. */\n modelChoiceFailed: string;\n /** Не удалось получить список моделей. */\n modelMenuFailed: string;\n /** Кнопка микрофона в покое. */\n voiceStart: string;\n /** Кнопка микрофона, пока идёт запись. */\n voiceStop: string;\n /** Кнопка микрофона, пока готовится запись: нажатие отменяет. */\n voiceConnecting: string;\n /** Кнопка микрофона, пока сервер причёсывает надиктованный текст (убирает «э-э», расставляет знаки). */\n voicePolishing: string;\n voiceErrors: VoiceErrorLabels;\n voiceDialog: VoiceDialogLabels;\n /** Заголовок карточки: что агент хочет сделать (`action` — название действия). */\n approvalRequested: (action: string) => string;\n /** Пока человек не решил. */\n approvalWaiting: string;\n approvalAllow: string;\n approvalDeny: string;\n approvalAlways: string;\n /** Ответ не дошёл до сервера. */\n approvalFailed: string;\n /** Ответ не принят: лимит расходов исчерпан. */\n approvalSpendingLimit: string;\n /** Ответ не принят: в чате идёт другой ход. */\n approvalBusy: string;\n /** Ответ не принят: у человека уже идут ответы в других чатах. */\n approvalTooManyTurns: string;\n approvalAllowedMark: string;\n approvalAlwaysMark: string;\n approvalDeniedMark: string;\n /** Для экранного диктора, когда ход остановился на запросе. */\n approvalAnnouncement: string;\n /** Расход подошёл к лимиту, но чат ещё работает: «Использовано 80% лимита на месяц». */\n spendingWarning: (info: SpendingInfo) => string;\n /** Лимит исчерпан, новые сообщения не отправляются: «Лимит на месяц исчерпан, обновится 1 ноября». */\n spendingExhausted: (info: SpendingInfo) => string;\n /** Окно сообщений подошло к лимиту (`unit: \"messages\"`): «Осталось 2 сообщения». */\n spendingMessagesLeft?: (remaining: number, info: SpendingInfo) => string;\n /** Окно сообщений исчерпано: «Лимит сообщений исчерпан, обновится 8 октября». */\n spendingMessagesExhausted?: (info: SpendingInfo) => string;\n /** Заголовок списка чатов и кнопка «Чаты» на узком экране. */\n chatListHeading: string;\n chatListNewChat: string;\n /** Название чата, в котором ещё ничего не писали. */\n chatListUntitled: string;\n /** Пометка чата, начатого в мессенджере: «Telegram», «ВКонтакте», «MAX». */\n chatListOrigin: Record<ChatOriginView, string>;\n /** Пометка закрытого чата в списке (причину скин берёт из `closed.message`). */\n chatListClosed: string;\n chatListLoading: string;\n chatListLoadFailed: string;\n chatListCreateFailed: string;\n chatListRenameFailed: string;\n chatListDeleteFailed: string;\n /** Кнопка меню строки: «Действия с чатом …». */\n chatListRowMenu: (title: string) => string;\n chatListRename: string;\n chatListRenameField: string;\n chatListDelete: string;\n chatListDeleteTitle: string;\n chatListDeleteBody: (title: string) => string;\n chatListDeleteConfirm: string;\n chatListDeleteCancel: string;\n /** Экран памяти агента: заголовок и счётчик «Записей: 12 из 200». */\n memoryHeading: string;\n memoryCount: (total: number, limit: number) => string;\n /** Подсказка, когда в запрос агента попадают не все записи: остальное он находит поиском. */\n memoryShownHint: (shown: number, total: number) => string;\n memoryAdd: string;\n memoryClearAll: string;\n memoryLoading: string;\n memoryEmpty: string;\n /** Сервер ответил 404: память в этом приложении не включена. */\n memoryUnavailable: string;\n memoryLoadFailed: string;\n memoryEdit: string;\n memoryDelete: string;\n /** Подписи кнопок записи для экранного диктора: «Изменить запись «Любит чай»». */\n memoryEditNote: (content: string) => string;\n memoryDeleteNote: (content: string) => string;\n /** Заголовок группы записей для экранного диктора: «Работа, записей: 3». */\n memoryGroupLabel: (name: string, count: number) => string;\n memoryNewNoteTitle: string;\n memoryEditNoteTitle: string;\n memoryPathField: string;\n memoryPathHint: string;\n memoryContentField: string;\n memorySave: string;\n memoryCancel: string;\n /** Ошибки полей: проверка до отправки. */\n memoryFieldErrors: {\n pathRequired: string;\n pathTooMany: string;\n pathTooLong: string;\n contentRequired: string;\n contentTooLong: string;\n };\n /** Почему действие с памятью не удалось. */\n memoryErrors: Record<MemoryFailure, string>;\n memoryDeleteTitle: string;\n memoryDeleteBody: (content: string) => string;\n memoryClearTitle: string;\n memoryClearBody: (total: number) => string;\n memoryConfirmDelete: string;\n memoryConfirmClear: string;\n memoryConfirmCancel: string;\n /** Блок «Объединено»: агент склеил повторы; «было» — скрытые оригиналы, «стало» — запись в списке. */\n memoryMergesHeading: string;\n /** Когда слияние сделано: «30 сентября в 12:05» (по местному времени). */\n memoryMergeDate: (finishedAt: Date) => string;\n memoryMergeBefore: string;\n memoryMergeAfter: string;\n memoryMergeRevert: string;\n /** Имя кнопки для экранного диктора: у каждого слияния своя, «Вернуть как было (30 сентября в 12:05)». */\n memoryMergeRevertFor: (when: string) => string;\n /** Вместо кнопки, когда результат склейки с тех пор менялся. */\n memoryMergeNotRevertible: string;\n /** Когда «Вернуть как было» отклонено: памяти не хватит места или запись, что вернулась бы, уже добавлена заново. */\n memoryRevertErrors: Record<\"limit_reached\" | \"duplicate\", string>;\n /** Панель агента (договор скина версии 12): кнопка в углу страницы, подписи панели и списка чатов над чатом. */\n agentPanelLauncher: string;\n agentPanelOpen: string;\n agentPanelClose: string;\n agentPanelLabel: string;\n agentPanelChats: string;\n agentPanelBackToChat: string;\n /** Значок на кнопке, пока идёт ответ. */\n agentPanelBusy: string;\n /** Ответ модели, когда браузерного инструмента нет на этой странице (функции нет у `AgentPanelProvider`). */\n agentPanelToolUnavailable: string;\n widgets?: Partial<WidgetLabels>;\n diagram?: Partial<DiagramLabels>;\n demo?: Partial<LiveDemoLabels>;\n images?: Partial<ImageLabels>;\n videos?: Partial<VideoLabels>;\n viewer?: Partial<ViewerLabels>;\n voice?: Partial<VoiceSpeechLabels>;\n helpers?: Partial<HelperLabels>;\n tasks?: Partial<TaskLabels>;\n notices?: Partial<NoticeLabels>;\n messengerStatus?: Partial<MessengerStatusLabels>;\n workView?: Partial<WorkViewLabels>;\n}\n\nconst periodPhrases = { month: \" на месяц\", week: \" на неделю\", day: \" на день\", none: \"\" } as const;\nfunction periodPhrase(info: SpendingInfo): string {\n if (info.period === \"rolling\") return info.hours ? ` на ${info.hours} ч` : \"\";\n return periodPhrases[info.period];\n}\nconst scopePhrase = (info: SpendingInfo) => (info.scope === \"chat\" ? \" этого чата\" : info.scope === \"service\" ? \" сервиса\" : \"\");\n/** Дата обновления лимита по поясу лимитов (без него — UTC); пояс, которого браузер не знает, — тоже UTC. */\nfunction resetDate(moment: Date, timeZone = \"UTC\", withTime = false): string {\n const format = (zone: string) => {\n const date = new Intl.DateTimeFormat(\"ru-RU\", { day: \"numeric\", month: \"long\", timeZone: zone }).format(moment);\n if (!withTime) return date;\n return `${date} в ${new Intl.DateTimeFormat(\"ru-RU\", { hour: \"2-digit\", minute: \"2-digit\", timeZone: zone }).format(moment)}`;\n };\n try {\n return format(timeZone);\n } catch {\n return format(\"UTC\");\n }\n}\n/** Скользящее окно обновляется посреди дня: к дате нужно время. */\nconst resetPhrase = (info: SpendingInfo) => (info.resetsAt ? `, обновится ${resetDate(info.resetsAt, info.timeZone, info.period === \"rolling\")}` : \"\");\nconst mergeDate = new Intl.DateTimeFormat(\"ru-RU\", { day: \"numeric\", month: \"long\", hour: \"2-digit\", minute: \"2-digit\" });\n\n/** Словарь, в котором есть и необязательные надписи: их подставляет `ChatLabelsProvider`. */\nexport type ResolvedChatLabels = Omit<ChatLabels, \"widgets\" | \"diagram\" | \"demo\" | \"images\" | \"videos\" | \"viewer\" | \"voice\" | \"helpers\" | \"tasks\" | \"notices\" | \"messengerStatus\" | \"workView\" | \"composerSettings\" | \"welcome\"> &\n Required<\n Pick<\n ChatLabels,\n \"externalImageLink\" | \"answerCutByLength\" | \"answerEdited\" | \"answerUndelivered\" | \"toolOutputEdited\" | \"editAnswer\" | \"steeringPending\" | \"thinkingPhrases\" | \"thinkingElapsed\" | \"spendingMessagesLeft\" | \"spendingMessagesExhausted\" | \"editMessageLimit\"\n >\n > & {\n welcome: { title: string; description: string };\n composerSettings: ComposerSettingsLabels;\n widgets: WidgetLabels;\n diagram: DiagramLabels;\n demo: LiveDemoLabels;\n images: ImageLabels;\n videos: VideoLabels;\n viewer: ViewerLabels;\n voice: VoiceSpeechLabels;\n helpers: HelperLabels;\n tasks: TaskLabels;\n notices: NoticeLabels;\n messengerStatus: MessengerStatusLabels;\n workView: WorkViewLabels;\n };\n\n/**\n * Подписи ошибок голоса. `time_limit` — строка или функция от предела записи, названного сервером (секунды).\n * `limit`, `forbidden`, `too_long` можно не задавать: возьмутся подписи по умолчанию на языке словаря (`locale`).\n */\nexport type VoiceErrorLabels = Record<Exclude<VoiceErrorKind, \"time_limit\" | \"limit\" | \"forbidden\" | \"too_long\">, string> & {\n time_limit: string | ((maxSessionSeconds: number) => string);\n limit?: string;\n forbidden?: string;\n too_long?: string;\n};\n\n/** Предел записи, если сервер его не назвал (старый сервер): как у сервера по умолчанию. */\nconst DEFAULT_VOICE_LIMIT_SECONDS = 300;\n\nlet reportedErrorTextFailure = false;\n\n/**\n * Подпись сбоя ответа от `errorText` словаря; функции нет — `undefined`: скин покажет `errors[kind]` сам. Функция\n * приложения упала — прежняя подпись `errors[kind]` и одна строка в консоль: ответ не падает при отрисовке.\n */\nexport function customErrorText(labels: Pick<ResolvedChatLabels, \"errors\" | \"errorText\">, error: ChatError | undefined): string | undefined {\n if (!error || !labels.errorText) return undefined;\n const plain = labels.errors[error.kind];\n try {\n return labels.errorText(error, plain);\n } catch (failure) {\n if (!reportedErrorTextFailure) {\n reportedErrorTextFailure = true;\n console.error(\"labels.errorText упал — показана обычная подпись сбоя.\", failure);\n }\n return plain;\n }\n}\n\n/** Подпись ошибки голоса для человека. */\nexport function voiceErrorText(errors: VoiceErrorLabels, kind: VoiceErrorKind, details?: VoiceErrorDetails): string {\n if (kind !== \"time_limit\") return errors[kind] ?? errors.other;\n const label = errors.time_limit;\n return typeof label === \"string\" ? label : label(details?.maxSessionSeconds ?? DEFAULT_VOICE_LIMIT_SECONDS);\n}\n\nconst russianPlural = new Intl.PluralRules(\"ru-RU\");\n/** «1 сообщение», «2 сообщения», «5 сообщений». */\nfunction russianCount(count: number, forms: { one: string; few: string; many: string }): string {\n const form = russianPlural.select(count);\n return `${count} ${form === \"one\" ? forms.one : form === \"few\" ? forms.few : forms.many}`;\n}\n\n/** «2 мин 5 сек» / «45 сек» для русских надписей длительности. */\nfunction russianDuration(ms: number): string {\n const seconds = Math.max(0, Math.round(ms / 1000));\n const minutes = Math.floor(seconds / 60);\n return minutes > 0 ? `${minutes} мин ${seconds % 60} сек` : `${seconds} сек`;\n}\n\nfunction englishDuration(ms: number): string {\n const seconds = Math.max(0, Math.round(ms / 1000));\n const minutes = Math.floor(seconds / 60);\n return minutes > 0 ? `${minutes} min ${seconds % 60} s` : `${seconds} s`;\n}\n\nconst russianHelpers: HelperLabels = {\n assigned: \"Поручено\",\n returned: \"Вернул\",\n status: { running: \"Работает…\", awaiting_approval: \"Ждёт подтверждения\", done: \"Готово\", failed: \"Не получилось\", background: \"Работает в фоне\" },\n};\n\nconst russianTasks: TaskLabels = {\n status: {\n queued: \"В очереди\",\n running: \"Идёт\",\n awaiting_approval: \"Ждёт подтверждения\",\n done: \"Готово\",\n failed: \"Ошибка\",\n canceled: \"Отменена\",\n interrupted: \"Прервана\",\n timed_out: \"Не уложилась во время\",\n },\n cancel: \"Отменить\",\n canceling: \"Отменяем…\",\n elapsed: (ms, finished) => `${finished ? \"заняла\" : \"идёт\"} ${russianDuration(ms)}`,\n};\n\nconst russianNotices: NoticeLabels = {\n taskDone: \"задача готова\",\n taskFailed: \"задача не удалась\",\n taskCanceled: \"задача отменена\",\n taskInterrupted: \"задача прервана\",\n taskTimedOut: \"задача не уложилась во время\",\n taskAwaitingApproval: \"задача ждёт подтверждения\",\n conversationStarted: \"агент написал первым\",\n staffSaid: (staffName) => (staffName ? `Сотрудник ${staffName}` : \"Сотрудник\"),\n line: (title, status) => (title ? `«${title}»: ${status}` : status),\n};\n\nconst russianMessengerStatus: MessengerStatusLabels = {\n thinking: \"Думаю…\",\n usingTool: (title) => `Использую инструмент: ${title}`,\n helperWorking: (name) => `Помощник «${name}» работает`,\n taskInBackground: (title) => `Задача «${title}» ушла в фон`,\n waitingForApproval: \"Жду вашего решения\",\n};\n\n/** Многоточие живой строки — одно, даже если оно уже есть во фразе. */\nconst withEllipsis = (phrase: string) => (/(…|\\.\\.\\.)$/.test(phrase) ? phrase : `${phrase}…`);\n\nconst russianWorkView: WorkViewLabels = {\n stepRunning: \"Работаю\",\n stepDone: \"Сделал шаг\",\n stepFailed: (running) => `${running} — не вышло`,\n stepFailedUnknown: \"Шаг не получился\",\n stepUnfinished: (running) => `${running} — не закончил`,\n stepUnfinishedUnknown: \"Шаг не закончен\",\n stepRepeated: (phrase, count) => `${phrase} ×${count}`,\n thinking: \"Думаю\",\n thought: (durationMs) => (durationMs === null ? \"Подумал\" : `Думал ${Math.max(1, Math.round(durationMs / 1000))} сек`),\n moreSteps: (count) => `+${count}`,\n live: withEllipsis,\n stepsDone: (count) => `${russianPlural.select(count) === \"one\" ? \"Сделан\" : \"Сделано\"} ${russianCount(count, { one: \"шаг\", few: \"шага\", many: \"шагов\" })}`,\n helperRunning: (name) => `Помощник «${name}» работает`,\n helperDone: (name) => `Помощник «${name}» закончил`,\n};\n\n/** Английские надписи простого вида работы агента (версия 23); входят в `englishLabels`. */\nexport const englishWorkViewLabels: WorkViewLabels = {\n stepRunning: \"Working\",\n stepDone: \"Did a step\",\n stepFailed: (running) => `${running} — didn’t work`,\n stepFailedUnknown: \"A step didn’t work\",\n stepUnfinished: (running) => `${running} — not finished`,\n stepUnfinishedUnknown: \"A step wasn’t finished\",\n stepRepeated: (phrase, count) => `${phrase} ×${count}`,\n thinking: \"Thinking\",\n thought: (durationMs) => (durationMs === null ? \"Thought\" : `Thought for ${Math.max(1, Math.round(durationMs / 1000))} s`),\n moreSteps: (count) => `+${count}`,\n live: withEllipsis,\n stepsDone: (count) => `${count} ${count === 1 ? \"step\" : \"steps\"} done`,\n helperRunning: (name) => `Helper “${name}” is working`,\n helperDone: (name) => `Helper “${name}” is done`,\n};\n\n/** Английские надписи версий 18 и 21 для приложений, которые подключают их через `ChatLabels`. */\nexport const englishWorkLabels: Pick<ResolvedChatLabels, \"helpers\" | \"tasks\" | \"notices\" | \"messengerStatus\" | \"steeringPending\"> = {\n steeringPending: \"The agent will take this into account at the next step\",\n helpers: {\n assigned: \"Assigned\",\n returned: \"Returned\",\n status: { running: \"Working…\", awaiting_approval: \"Waiting for approval\", done: \"Done\", failed: \"Failed\", background: \"Working in the background\" },\n },\n tasks: {\n status: {\n queued: \"Queued\",\n running: \"Running\",\n awaiting_approval: \"Waiting for approval\",\n done: \"Done\",\n failed: \"Failed\",\n canceled: \"Canceled\",\n interrupted: \"Interrupted\",\n timed_out: \"Ran out of time\",\n },\n cancel: \"Cancel\",\n canceling: \"Canceling…\",\n elapsed: (ms, finished) => `${finished ? \"took\" : \"running\"} ${englishDuration(ms)}`,\n },\n notices: {\n taskDone: \"task is done\",\n taskFailed: \"task failed\",\n taskCanceled: \"task canceled\",\n taskInterrupted: \"task interrupted\",\n taskTimedOut: \"task ran out of time\",\n taskAwaitingApproval: \"task is waiting for approval\",\n conversationStarted: \"the agent wrote first\",\n staffSaid: (staffName) => (staffName ? `Staff: ${staffName}` : \"Staff\"),\n line: (title, status) => (title ? `“${title}”: ${status}` : status),\n },\n messengerStatus: {\n thinking: \"Thinking…\",\n usingTool: (title) => `Using a tool: ${title}`,\n helperWorking: (name) => `Helper “${name}” is working`,\n taskInBackground: (title) => `Task “${title}” moved to the background`,\n waitingForApproval: \"Waiting for your decision\",\n },\n};\n\nexport const russianLabels: ResolvedChatLabels = {\n locale: \"ru\",\n welcome: { title: \"С чего начнём?\", description: \"Задайте вопрос или опишите задачу.\" },\n helpers: russianHelpers,\n tasks: russianTasks,\n notices: russianNotices,\n messengerStatus: russianMessengerStatus,\n workView: russianWorkView,\n composerSettings: { levels: { off: \"Выкл\", on: \"Вкл\", minimal: \"Минимум\", low: \"Низко\", medium: \"Средне\", high: \"Высоко\", max: \"Максимум\" }, model: \"Модель\", reasoning: \"Рассуждения\", tools: \"Инструменты\", settings: \"Настройки ответа\", done: \"Готово\", via: \"Через\", search: \"Найти модель\", showReasoning: \"Показывать ход мысли\", project: \"Проект\", file: \"Файл\", image: \"Картинка\", textModels: \"Текстовые задачи\", imageModels: \"Текст и изображения\", models: \"Модели\", noResults: \"Ничего не найдено\", price: \"USD за миллион токенов\", input: \"вход\", output: \"выход\" },\n composerPlaceholder: \"Напишите сообщение\",\n composerLabel: \"Сообщение\",\n send: \"Отправить\",\n stop: \"Остановить\",\n copyAnswer: \"Скопировать ответ\",\n copied: \"Скопировано\",\n copyCode: \"Скопировать код\",\n reasoningRunning: \"Думает…\",\n reasoningDone: (seconds) => `Думал ${seconds} сек`,\n thinkingPhrases: [\n \"Думаю…\",\n \"Прикидываю…\",\n \"Раскладываю по полочкам…\",\n \"Сверяю детали…\",\n \"Собираю мысли…\",\n \"Взвешиваю варианты…\",\n \"Ищу лучший ход…\",\n \"Проверяю себя…\",\n \"Складываю картину…\",\n \"Обдумываю…\",\n ],\n thinkingElapsed: (seconds) => {\n if (seconds < 60) return `${seconds} с`;\n const rest = seconds % 60;\n return rest === 0 ? `${Math.floor(seconds / 60)} мин` : `${Math.floor(seconds / 60)} мин ${rest} с`;\n },\n toolRunning: (toolName) => `${toolName}: выполняется`,\n toolDone: (toolName) => `${toolName}: готово`,\n toolError: (toolName) => `${toolName}: ошибка`,\n screenshotAlt: \"Снимок страницы\",\n screenshotUnavailable: \"Снимок не загрузился.\",\n answerStopped: \"Остановлено\",\n answerInterrupted: \"Ответ прерван\",\n answerEmpty: \"Модель не дала ответа\",\n externalImageLink: \"картинка\",\n answerCutByLength: \"Ответ оборван по длине\",\n answerEdited: \"Изменено человеком\",\n answerUndelivered: \"Не доставлено\",\n toolOutputEdited: \"Результат изменён\",\n steeringPending: \"Агент учтёт на следующем шаге\",\n editAnswer: \"Изменить ответ\",\n retry: \"Повторить\",\n reconnecting: \"Переподключаемся…\",\n connectionLost: \"Нет связи с сервером\",\n chatNotFound: \"Чат не найден\",\n errors: {\n overloaded: \"Модель перегружена, попробуйте ещё раз\",\n rate_limited: \"Слишком много запросов, попробуйте через минуту\",\n insufficient_funds: \"На счёте провайдера закончились деньги\",\n invalid_key: \"Ключ провайдера не подходит\",\n context_overflow: \"Переписка не помещается в модель. Начните новый чат или выберите модель с большим окном\",\n other: \"Что-то пошло не так, попробуйте ещё раз\",\n signed_out: \"Вход истёк. Войдите снова\",\n too_many_turns: \"Уже идут ответы в других чатах. Дождитесь, пока один закончится, и отправьте снова\",\n forbidden_origin: \"Сервер чата не принимает сообщения с этой страницы. Сообщите администратору сайта\",\n },\n reasoningLevels: {\n off: \"Выкл\",\n on: \"Вкл\",\n minimal: \"Минимальный\",\n low: \"Низкий\",\n medium: \"Средний\",\n high: \"Высокий\",\n max: \"Максимум\",\n },\n historyCompacted: \"История сжата\",\n historyCompactedHint: \"Вместо сообщений выше модель видит эту сводку. Сами сообщения никуда не делись.\",\n branchPosition: (position, count) => `Вариант ${position} из ${count}`,\n previousBranch: \"Предыдущий вариант\",\n nextBranch: \"Следующий вариант\",\n regenerate: \"Переделать ответ\",\n editMessage: \"Изменить сообщение\",\n saveEdit: \"Отправить\",\n cancelEdit: \"Отмена\",\n editErrors: {\n busy: \"Ответ ещё идёт. Дождитесь его конца и отправьте снова\",\n offline: \"Нет связи с сервером. Текст сохранён в поле\",\n rejected: \"Сервер не принял правку. Текст сохранён в поле\",\n spendingLimit: \"Лимит расходов исчерпан. Текст сохранён в поле\",\n signedOut: \"Вход истёк. Войдите снова. Текст сохранён в поле\",\n tooManyTurns: \"Уже идут ответы в других чатах. Текст сохранён в поле\",\n forbiddenOrigin: \"Сервер чата не принимает сообщения с этой страницы. Текст сохранён в поле\",\n },\n editMessageLimit: \"Лимит сообщений исчерпан. Текст сохранён в поле\",\n modelChoiceFailed: \"Не удалось сменить модель\",\n modelMenuFailed: \"Список моделей не загрузился\",\n voiceStart: \"Диктовать\",\n voiceStop: \"Остановить диктовку\",\n voiceConnecting: \"Подключаемся к микрофону…\",\n voicePolishing: \"Приводим текст в порядок…\",\n voiceErrors: {\n permission_denied: \"Нет доступа к микрофону. Разрешите его в настройках браузера\",\n no_microphone: \"Микрофон не найден\",\n unsupported: \"Этот браузер не умеет записывать звук\",\n network: \"Нет связи, диктовка прервана\",\n service_unavailable: \"Сервис распознавания речи недоступен\",\n other: \"Не удалось начать диктовку\",\n rate_limited: \"Слишком много диктовок подряд, попробуйте через минуту\",\n time_limit: (seconds) =>\n `Запись остановлена: предел ${seconds % 60 === 0 ? russianCount(seconds / 60, { one: \"минута\", few: \"минуты\", many: \"минут\" }) : russianCount(seconds, { one: \"секунда\", few: \"секунды\", many: \"секунд\" })}`,\n limit: \"Лимит расходов исчерпан, диктовка недоступна\",\n forbidden: \"Нет доступа к диктовке\",\n too_long: \"Запись слишком длинная\",\n },\n voiceDialog: {\n start: \"Голосовой режим\",\n exit: \"Выйти\",\n voiceMenu: \"Голос\",\n states: {\n off: \"Голосовой режим выключен\",\n connecting: \"Подключаемся…\",\n listening: \"Слушаю\",\n thinking: \"Думаю\",\n speaking: \"Говорю\",\n interrupted: \"Вы перебили\",\n error: \"Что-то пошло не так\",\n },\n errors: {\n microphone_denied: \"Нет доступа к микрофону. Разрешите его в настройках браузера\",\n microphone_missing: \"Микрофон недоступен: нужны микрофон, https и свежий браузер\",\n network: \"Нет связи, голосовой режим остановлен\",\n service_unavailable: \"Голосовой сервис недоступен\",\n recognition_lost: \"Распознавание речи оборвалось\",\n speech_unavailable: \"Озвучка недоступна: ответы приходят текстом\",\n limit: \"Лимит расходов исчерпан\",\n rate_limited: \"Слишком много включений подряд, попробуйте через минуту\",\n idle: \"Режим выключен: долго не было слышно речи\",\n backgrounded: \"Режим выключен: страница ушла в фон\",\n },\n interruptedMark: (spokenTail) => `Вы перебили: озвучено до «${spokenTail}»`,\n },\n approvalRequested: (action) => `Агент хочет: ${action}`,\n approvalWaiting: \"Ждёт вашего решения\",\n approvalAllow: \"Разрешить\",\n approvalDeny: \"Отклонить\",\n approvalAlways: \"Всегда в этом чате\",\n approvalFailed: \"Ответ не дошёл до сервера, попробуйте ещё раз\",\n approvalSpendingLimit: \"Лимит расходов исчерпан, ответить сейчас нельзя\",\n approvalBusy: \"Чат занят другим ходом, попробуйте ещё раз\",\n approvalTooManyTurns: \"Уже идут ответы в других чатах, попробуйте чуть позже\",\n approvalAllowedMark: \"Разрешено\",\n approvalAlwaysMark: \"Разрешено в этом чате\",\n approvalDeniedMark: \"Отклонено\",\n approvalAnnouncement: \"Агент ждёт вашего решения\",\n spendingWarning: (info) => {\n const percent = Math.min(99, Math.floor((info.share ?? 0) * 100 + 1e-6));\n return `Использовано ${percent}% лимита${scopePhrase(info)}${periodPhrase(info)}`;\n },\n spendingExhausted: (info) => {\n if (info.reason === \"blocked\") return \"Новые ответы для вас приостановлены\";\n if (info.reason === \"reserve\") return `Лимита${scopePhrase(info)}${periodPhrase(info)} не хватит на новый ответ${resetPhrase(info)}`;\n return `Лимит${scopePhrase(info)}${periodPhrase(info)} исчерпан${resetPhrase(info)}`;\n },\n spendingMessagesLeft: (remaining) => `Осталось ${russianCount(remaining, { one: \"сообщение\", few: \"сообщения\", many: \"сообщений\" })}`,\n spendingMessagesExhausted: (info) => `Лимит сообщений исчерпан${resetPhrase(info)}`,\n chatListHeading: \"Чаты\",\n chatListNewChat: \"Новый чат\",\n chatListUntitled: \"Новый чат\",\n chatListOrigin: { telegram: \"Telegram\", vk: \"ВКонтакте\", max: \"MAX\" },\n chatListClosed: \"Закрыт\",\n chatListLoading: \"Загружаем чаты…\",\n chatListLoadFailed: \"Список чатов не загрузился\",\n chatListCreateFailed: \"Не удалось создать чат\",\n chatListRenameFailed: \"Не удалось переименовать чат\",\n chatListDeleteFailed: \"Не удалось удалить чат\",\n chatListRowMenu: (title) => `Действия с чатом «${title}»`,\n chatListRename: \"Переименовать\",\n chatListRenameField: \"Название чата\",\n chatListDelete: \"Удалить\",\n chatListDeleteTitle: \"Удалить чат?\",\n chatListDeleteBody: (title) => `Чат «${title}» и вся его переписка будут удалены насовсем.`,\n chatListDeleteConfirm: \"Удалить\",\n chatListDeleteCancel: \"Отмена\",\n memoryHeading: \"Память\",\n memoryCount: (total, limit) => `Записей: ${total} из ${limit}`,\n memoryShownHint: (shown, total) => `Показано агенту: ${shown} из ${total}. Остальное он находит поиском.`,\n memoryAdd: \"Добавить\",\n memoryClearAll: \"Стереть всю память\",\n memoryLoading: \"Загружаем память…\",\n memoryEmpty: \"Память пуста. Агент запоминает важное по ходу разговора, а вы можете добавить запись сами.\",\n memoryUnavailable: \"Память в этом приложении не включена\",\n memoryLoadFailed: \"Память не загрузилась\",\n memoryEdit: \"Изменить\",\n memoryDelete: \"Удалить\",\n memoryEditNote: (content) => `Изменить запись «${content}»`,\n memoryDeleteNote: (content) => `Удалить запись «${content}»`,\n memoryGroupLabel: (name, count) => `${name}, записей: ${count}`,\n memoryNewNoteTitle: \"Новая запись\",\n memoryEditNoteTitle: \"Изменить запись\",\n memoryPathField: \"Раздел\",\n memoryPathHint: \"До трёх уровней через «/», например: О себе / Работа\",\n memoryContentField: \"Запись\",\n memorySave: \"Сохранить\",\n memoryCancel: \"Отмена\",\n memoryFieldErrors: {\n pathRequired: \"Укажите раздел\",\n pathTooMany: \"Не больше трёх уровней раздела\",\n pathTooLong: \"Название раздела не длиннее 60 знаков\",\n contentRequired: \"Напишите текст записи\",\n contentTooLong: \"Запись не длиннее 500 знаков\",\n },\n memoryErrors: {\n not_found: \"Такой записи уже нет. Список обновлён\",\n stale_version: \"Запись изменилась в другом месте. Список обновлён: проверьте и повторите\",\n limit_reached: \"Память заполнена. Удалите ненужные записи и повторите\",\n duplicate: \"Такая запись уже есть\",\n invalid: \"Сервер не принял запись. Проверьте разделы и текст\",\n conflict: \"Вернуть как было уже нельзя: записи с тех пор менялись. Список обновлён\",\n unavailable: \"Память в этом приложении не включена\",\n other: \"Не удалось выполнить действие, попробуйте ещё раз\",\n },\n memoryDeleteTitle: \"Удалить запись?\",\n memoryDeleteBody: (content) => `Запись «${content}» будет удалена насовсем.`,\n memoryClearTitle: \"Стереть всю память?\",\n memoryClearBody: (total) => `Все записи (${total}) будут удалены насовсем. Агент забудет всё, что знал о вас.`,\n memoryConfirmDelete: \"Удалить\",\n memoryConfirmClear: \"Стереть всё\",\n memoryConfirmCancel: \"Отмена\",\n memoryMergesHeading: \"Объединено\",\n memoryMergeDate: (finishedAt) => mergeDate.format(finishedAt),\n memoryMergeBefore: \"Было\",\n memoryMergeAfter: \"Стало\",\n memoryMergeRevert: \"Вернуть как было\",\n memoryMergeRevertFor: (when) => `Вернуть как было (${when})`,\n memoryMergeNotRevertible: \"Вернуть нельзя: записи с тех пор менялись\",\n memoryRevertErrors: {\n limit_reached: \"Вернуть как было нельзя: в памяти не хватит места. Удалите ненужные записи и повторите\",\n duplicate: \"Вернуть как было нельзя: одну из записей вы уже добавили заново. Удалите повтор и повторите\",\n },\n agentPanelLauncher: \"Агент\",\n agentPanelOpen: \"Открыть агента\",\n agentPanelClose: \"Закрыть панель агента\",\n agentPanelLabel: \"Панель агента\",\n agentPanelChats: \"Чаты\",\n agentPanelBackToChat: \"К чату\",\n agentPanelBusy: \"Агент отвечает\",\n agentPanelToolUnavailable: \"Инструмент недоступен на этой странице\",\n widgets: {\n answeredByMessage: \"Ответ дан сообщением\",\n submit: \"Готово\",\n choose: \"Выберите вариант или ответьте своими словами\",\n ratingStars: (value, total) => `Оценка ${value} из ${total}`,\n answerFailed: \"Ответ не отправился, попробуйте ещё раз\",\n notCurrentBranch: \"Вопрос из другой ветки: ответить на него можно только там\",\n fallback: \"Ответьте сообщением\",\n formRequired: \"Обязательное поле\",\n asked: \"Агент задал вопрос\",\n answerSent: \"Ответ отправлен\",\n formSubmit: \"Отправить\",\n choiceRange: (min, max) => (min === max ? `Выберите ${min}` : `Выберите от ${min} до ${max}`),\n formOutOfRange: (min, max) =>\n min !== undefined && max !== undefined ? `Допустимо от ${min} до ${max}` : min !== undefined ? `Допустимо от ${min}` : `Допустимо до ${max}`,\n formNotNumber: \"Введите число\",\n formNotDate: \"Введите дату\",\n formTooLong: (max) => `Не длиннее ${max} знаков`,\n alreadyAnswered: \"На этот вопрос уже ответили\",\n answerRejected: \"Такой ответ не подходит, ответьте сообщением\",\n answerCheckTimeout: \"Не удалось проверить ответ, попробуйте ещё раз\",\n },\n diagram: {\n loading: \"Схема рисуется…\",\n failed: \"Схему не удалось нарисовать, показан её текст\",\n open: \"Открыть схему\",\n },\n demo: {\n loading: \"Готовлю демонстрацию…\",\n title: \"Живая демонстрация\",\n tooLarge: \"Демонстрация слишком большая для запуска, показан её код\",\n unfinished: \"Демонстрация не дописана, показан её код\",\n leftPage: \"Демонстрация попыталась открыть другую страницу и остановлена, показан её код\",\n externalContent: \"В этом чате есть текст из интернета или файлов, поэтому демонстрации не запускаются — показан код\",\n },\n images: {\n loading: \"Картинка создаётся…\",\n failed: \"Картинка не создалась\",\n stopped: \"Создание остановлено\",\n open: \"Открыть картинку\",\n },\n videos: {\n play: \"Смотреть видео\",\n from: (time) => `с ${time}`,\n fullscreen: \"На весь экран\",\n },\n viewer: {\n close: \"Закрыть\",\n next: \"Следующая\",\n previous: \"Предыдущая\",\n fit: \"Вписать в экран\",\n counter: (position, total) => `${position} из ${total}`,\n },\n voice: {\n diagramOnScreen: \"схема на экране\",\n videoOnScreen: \"видео на экране\",\n widgetChoices: (question, options) => `${question} Варианты: ${options.join(\", \")}.`,\n widgetOnScreen: (title) => `${title ? `${title}. ` : \"\"}Ответьте на экране или голосом.`,\n },\n};\n\nconst englishPeriodPhrases = { month: \" for the month\", week: \" for the week\", day: \" for the day\", none: \"\" } as const;\nfunction englishPeriodPhrase(info: SpendingInfo): string {\n if (info.period === \"rolling\") return info.hours ? ` for ${info.hours} h` : \"\";\n return englishPeriodPhrases[info.period];\n}\nconst englishScopePhrase = (info: SpendingInfo) => (info.scope === \"chat\" ? \" of this chat\" : info.scope === \"service\" ? \" of the service\" : \"\");\nfunction englishResetDate(moment: Date, timeZone = \"UTC\", withTime = false): string {\n const format = (zone: string) =>\n new Intl.DateTimeFormat(\"en-US\", { day: \"numeric\", month: \"long\", timeZone: zone, ...(withTime && { hour: \"2-digit\", minute: \"2-digit\" }) }).format(moment);\n try {\n return format(timeZone);\n } catch {\n return format(\"UTC\");\n }\n}\nconst englishResetPhrase = (info: SpendingInfo) => (info.resetsAt ? `, renews ${englishResetDate(info.resetsAt, info.timeZone, info.period === \"rolling\")}` : \"\");\nconst englishCount = (count: number, one: string, many: string) => `${count} ${count === 1 ? one : many}`;\nconst englishMergeDate = new Intl.DateTimeFormat(\"en-US\", { day: \"numeric\", month: \"long\", hour: \"2-digit\", minute: \"2-digit\" });\n\n/** Полный английский словарь чата: `<ChatLabelsProvider labels={englishLabels}>`. Свои поправки — поверх него. */\nexport const englishLabels: ResolvedChatLabels = {\n locale: \"en\",\n welcome: { title: \"Where shall we start?\", description: \"Ask a question or describe a task.\" },\n ...englishWorkLabels,\n workView: englishWorkViewLabels,\n composerSettings: { levels: { off: \"Off\", on: \"On\", minimal: \"Minimal\", low: \"Low\", medium: \"Medium\", high: \"High\", max: \"Max\" }, model: \"Model\", reasoning: \"Reasoning\", tools: \"Tools\", settings: \"Response settings\", done: \"Done\", via: \"Via\", search: \"Find model\", showReasoning: \"Show reasoning\", project: \"Project\", file: \"File\", image: \"Image\", textModels: \"Text tasks\", imageModels: \"Text and images\", models: \"Models\", noResults: \"No results\", price: \"USD per million tokens\", input: \"input\", output: \"output\" },\n composerPlaceholder: \"Write a message\",\n composerLabel: \"Message\",\n send: \"Send\",\n stop: \"Stop\",\n copyAnswer: \"Copy answer\",\n copied: \"Copied\",\n copyCode: \"Copy code\",\n reasoningRunning: \"Thinking…\",\n reasoningDone: (seconds) => `Thought for ${seconds} s`,\n thinkingPhrases: [\n \"Thinking…\",\n \"Figuring it out…\",\n \"Sorting things out…\",\n \"Checking details…\",\n \"Gathering thoughts…\",\n \"Weighing options…\",\n \"Looking for the best move…\",\n \"Double-checking…\",\n \"Putting the picture together…\",\n \"Mulling it over…\",\n ],\n thinkingElapsed: (seconds) => {\n if (seconds < 60) return `${seconds} s`;\n const rest = seconds % 60;\n return rest === 0 ? `${Math.floor(seconds / 60)} min` : `${Math.floor(seconds / 60)} min ${rest} s`;\n },\n toolRunning: (toolName) => `${toolName}: running`,\n toolDone: (toolName) => `${toolName}: done`,\n toolError: (toolName) => `${toolName}: error`,\n screenshotAlt: \"Page screenshot\",\n screenshotUnavailable: \"The screenshot didn’t load.\",\n answerStopped: \"Stopped\",\n answerInterrupted: \"Reply interrupted\",\n answerEmpty: \"The model gave no reply\",\n externalImageLink: \"image\",\n answerCutByLength: \"The reply was cut off for length\",\n answerEdited: \"Edited by a person\",\n answerUndelivered: \"Not delivered\",\n toolOutputEdited: \"Result edited\",\n editAnswer: \"Edit reply\",\n retry: \"Retry\",\n reconnecting: \"Reconnecting…\",\n connectionLost: \"No connection to the server\",\n chatNotFound: \"Chat not found\",\n errors: {\n overloaded: \"The model is overloaded, please try again\",\n rate_limited: \"Too many requests, please try again in a minute\",\n insufficient_funds: \"The provider account is out of money\",\n invalid_key: \"The provider key doesn’t work\",\n context_overflow: \"The conversation doesn’t fit into the model. Start a new chat or pick a model with a bigger window\",\n other: \"Something went wrong, please try again\",\n signed_out: \"You’ve been signed out. Please sign in again\",\n too_many_turns: \"Replies are already running in other chats. Wait for one to finish and send again\",\n forbidden_origin: \"The chat server doesn’t accept messages from this page. Let the site admin know\",\n },\n reasoningLevels: { off: \"Off\", on: \"On\", minimal: \"Minimal\", low: \"Low\", medium: \"Medium\", high: \"High\", max: \"Max\" },\n historyCompacted: \"History compacted\",\n historyCompactedHint: \"Instead of the messages above, the model sees this summary. The messages themselves are still here.\",\n branchPosition: (position, count) => `Version ${position} of ${count}`,\n previousBranch: \"Previous version\",\n nextBranch: \"Next version\",\n regenerate: \"Regenerate\",\n editMessage: \"Edit message\",\n saveEdit: \"Send\",\n cancelEdit: \"Cancel\",\n editErrors: {\n busy: \"A reply is still running. Wait for it to finish and send again\",\n offline: \"No connection to the server. Your text is kept in the field\",\n rejected: \"The server didn’t accept the edit. Your text is kept in the field\",\n spendingLimit: \"The spending limit is used up. Your text is kept in the field\",\n signedOut: \"You’ve been signed out. Sign in again. Your text is kept in the field\",\n tooManyTurns: \"Replies are already running in other chats. Your text is kept in the field\",\n forbiddenOrigin: \"The chat server doesn’t accept messages from this page. Your text is kept in the field\",\n },\n editMessageLimit: \"The message limit is used up. Your text is kept in the field\",\n modelChoiceFailed: \"Couldn’t change the model\",\n modelMenuFailed: \"The model list didn’t load\",\n voiceStart: \"Dictate\",\n voiceStop: \"Stop dictation\",\n voiceConnecting: \"Connecting to the microphone…\",\n voicePolishing: \"Tidying up the text…\",\n voiceErrors: {\n permission_denied: \"No access to the microphone. Allow it in your browser settings\",\n no_microphone: \"No microphone found\",\n unsupported: \"This browser can’t record audio\",\n network: \"Connection lost, dictation stopped\",\n service_unavailable: \"Speech recognition isn’t available\",\n other: \"Couldn’t start dictation\",\n rate_limited: \"Too many dictations in a row, please try again in a minute\",\n time_limit: (seconds) =>\n `Recording stopped: the limit is ${seconds % 60 === 0 ? englishCount(seconds / 60, \"minute\", \"minutes\") : englishCount(seconds, \"second\", \"seconds\")}`,\n limit: \"Spending limit reached, dictation is unavailable\",\n forbidden: \"No access to dictation\",\n too_long: \"The recording is too long\",\n },\n voiceDialog: {\n start: \"Voice mode\",\n exit: \"Exit\",\n voiceMenu: \"Voice\",\n states: {\n off: \"Voice mode is off\",\n connecting: \"Connecting…\",\n listening: \"Listening\",\n thinking: \"Thinking\",\n speaking: \"Speaking\",\n interrupted: \"You interrupted\",\n error: \"Something went wrong\",\n },\n errors: {\n microphone_denied: \"No access to the microphone. Allow it in your browser settings\",\n microphone_missing: \"Microphone unavailable: you need a microphone, https and an up-to-date browser\",\n network: \"Connection lost, voice mode stopped\",\n service_unavailable: \"The voice service isn’t available\",\n recognition_lost: \"Speech recognition dropped\",\n speech_unavailable: \"Voice playback isn’t available: replies come as text\",\n limit: \"The spending limit is used up\",\n rate_limited: \"Too many starts in a row, please try again in a minute\",\n idle: \"Voice mode turned off: no speech for a long time\",\n backgrounded: \"Voice mode turned off: the page went to the background\",\n },\n interruptedMark: (spokenTail) => `You interrupted: spoken up to “${spokenTail}”`,\n },\n approvalRequested: (action) => `The agent wants to: ${action}`,\n approvalWaiting: \"Waiting for your decision\",\n approvalAllow: \"Allow\",\n approvalDeny: \"Deny\",\n approvalAlways: \"Always in this chat\",\n approvalFailed: \"The answer didn’t reach the server, please try again\",\n approvalSpendingLimit: \"The spending limit is used up, you can’t answer right now\",\n approvalBusy: \"The chat is busy with another turn, please try again\",\n approvalTooManyTurns: \"Replies are already running in other chats, please try a bit later\",\n approvalAllowedMark: \"Allowed\",\n approvalAlwaysMark: \"Allowed in this chat\",\n approvalDeniedMark: \"Denied\",\n approvalAnnouncement: \"The agent is waiting for your decision\",\n spendingWarning: (info) => {\n const percent = Math.min(99, Math.floor((info.share ?? 0) * 100 + 1e-6));\n return `${percent}% of the limit${englishScopePhrase(info)}${englishPeriodPhrase(info)} used`;\n },\n spendingExhausted: (info) => {\n if (info.reason === \"blocked\") return \"New replies are paused for you\";\n if (info.reason === \"reserve\") return `Not enough left in the limit${englishScopePhrase(info)}${englishPeriodPhrase(info)} for a new reply${englishResetPhrase(info)}`;\n return `The limit${englishScopePhrase(info)}${englishPeriodPhrase(info)} is used up${englishResetPhrase(info)}`;\n },\n spendingMessagesLeft: (remaining) => `${englishCount(remaining, \"message\", \"messages\")} left`,\n spendingMessagesExhausted: (info) => `The message limit is used up${englishResetPhrase(info)}`,\n chatListHeading: \"Chats\",\n chatListNewChat: \"New chat\",\n chatListUntitled: \"New chat\",\n chatListOrigin: { telegram: \"Telegram\", vk: \"VK\", max: \"MAX\" },\n chatListClosed: \"Closed\",\n chatListLoading: \"Loading chats…\",\n chatListLoadFailed: \"The chat list didn’t load\",\n chatListCreateFailed: \"Couldn’t create a chat\",\n chatListRenameFailed: \"Couldn’t rename the chat\",\n chatListDeleteFailed: \"Couldn’t delete the chat\",\n chatListRowMenu: (title) => `Actions for chat “${title}”`,\n chatListRename: \"Rename\",\n chatListRenameField: \"Chat name\",\n chatListDelete: \"Delete\",\n chatListDeleteTitle: \"Delete chat?\",\n chatListDeleteBody: (title) => `Chat “${title}” and all its messages will be deleted for good.`,\n chatListDeleteConfirm: \"Delete\",\n chatListDeleteCancel: \"Cancel\",\n memoryHeading: \"Memory\",\n memoryCount: (total, limit) => `Notes: ${total} of ${limit}`,\n memoryShownHint: (shown, total) => `Shown to the agent: ${shown} of ${total}. It finds the rest by searching.`,\n memoryAdd: \"Add\",\n memoryClearAll: \"Erase all memory\",\n memoryLoading: \"Loading memory…\",\n memoryEmpty: \"Memory is empty. The agent remembers important things as you talk, and you can add a note yourself.\",\n memoryUnavailable: \"Memory isn’t turned on in this app\",\n memoryLoadFailed: \"Memory didn’t load\",\n memoryEdit: \"Edit\",\n memoryDelete: \"Delete\",\n memoryEditNote: (content) => `Edit note “${content}”`,\n memoryDeleteNote: (content) => `Delete note “${content}”`,\n memoryGroupLabel: (name, count) => `${name}, notes: ${count}`,\n memoryNewNoteTitle: \"New note\",\n memoryEditNoteTitle: \"Edit note\",\n memoryPathField: \"Section\",\n memoryPathHint: \"Up to three levels separated by “/”, for example: About me / Work\",\n memoryContentField: \"Note\",\n memorySave: \"Save\",\n memoryCancel: \"Cancel\",\n memoryFieldErrors: {\n pathRequired: \"Enter a section\",\n pathTooMany: \"No more than three section levels\",\n pathTooLong: \"Section names: up to 60 characters\",\n contentRequired: \"Write the note text\",\n contentTooLong: \"Notes: up to 500 characters\",\n },\n memoryErrors: {\n not_found: \"This note is gone. The list is updated\",\n stale_version: \"The note changed elsewhere. The list is updated: check it and try again\",\n limit_reached: \"Memory is full. Delete notes you don’t need and try again\",\n duplicate: \"This note already exists\",\n invalid: \"The server didn’t accept the note. Check the sections and text\",\n conflict: \"It can’t be undone anymore: the notes have changed since. The list is updated\",\n unavailable: \"Memory isn’t turned on in this app\",\n other: \"Couldn’t do that, please try again\",\n },\n memoryDeleteTitle: \"Delete note?\",\n memoryDeleteBody: (content) => `Note “${content}” will be deleted for good.`,\n memoryClearTitle: \"Erase all memory?\",\n memoryClearBody: (total) => `All notes (${total}) will be deleted for good. The agent will forget everything it knew about you.`,\n memoryConfirmDelete: \"Delete\",\n memoryConfirmClear: \"Erase all\",\n memoryConfirmCancel: \"Cancel\",\n memoryMergesHeading: \"Merged\",\n memoryMergeDate: (finishedAt) => englishMergeDate.format(finishedAt),\n memoryMergeBefore: \"Before\",\n memoryMergeAfter: \"After\",\n memoryMergeRevert: \"Undo\",\n memoryMergeRevertFor: (when) => `Undo (${when})`,\n memoryMergeNotRevertible: \"Can’t undo: the notes have changed since\",\n memoryRevertErrors: {\n limit_reached: \"Can’t undo: there’s not enough room in memory. Delete notes you don’t need and try again\",\n duplicate: \"Can’t undo: you’ve already added one of the notes again. Delete the duplicate and try again\",\n },\n agentPanelLauncher: \"Agent\",\n agentPanelOpen: \"Open agent\",\n agentPanelClose: \"Close agent panel\",\n agentPanelLabel: \"Agent panel\",\n agentPanelChats: \"Chats\",\n agentPanelBackToChat: \"Back to chat\",\n agentPanelBusy: \"The agent is replying\",\n agentPanelToolUnavailable: \"This tool isn’t available on this page\",\n widgets: {\n answeredByMessage: \"Answered in a message\",\n submit: \"Done\",\n choose: \"Pick an option or answer in your own words\",\n ratingStars: (value, total) => `Rating ${value} of ${total}`,\n answerFailed: \"The answer wasn’t sent, please try again\",\n notCurrentBranch: \"This question is from another version: you can only answer it there\",\n fallback: \"Answer in a message\",\n formRequired: \"Required field\",\n asked: \"The agent asked a question\",\n answerSent: \"Answer sent\",\n formSubmit: \"Send\",\n choiceRange: (min, max) => (min === max ? `Pick ${min}` : `Pick ${min} to ${max}`),\n formOutOfRange: (min, max) =>\n min !== undefined && max !== undefined ? `Allowed from ${min} to ${max}` : min !== undefined ? `Allowed from ${min}` : `Allowed up to ${max}`,\n formNotNumber: \"Enter a number\",\n formNotDate: \"Enter a date\",\n formTooLong: (max) => `Up to ${max} characters`,\n alreadyAnswered: \"This question has already been answered\",\n answerRejected: \"That answer doesn’t fit, please answer in a message\",\n answerCheckTimeout: \"Couldn’t check the answer, please try again\",\n },\n diagram: { loading: \"Drawing the diagram…\", failed: \"Couldn’t draw the diagram, showing its text\", open: \"Open diagram\" },\n demo: {\n loading: \"Preparing the demo…\",\n title: \"Live demo\",\n tooLarge: \"The demo is too large to run, showing its code\",\n unfinished: \"The demo isn’t finished, showing its code\",\n leftPage: \"The demo tried to open another page and was stopped, showing its code\",\n externalContent: \"This chat contains text from the web or files, so demos don’t run — showing the code\",\n },\n images: { loading: \"Creating the image…\", failed: \"The image wasn’t created\", stopped: \"Creation stopped\", open: \"Open image\" },\n videos: { play: \"Watch video\", from: (time) => `from ${time}`, fullscreen: \"Full screen\" },\n viewer: { close: \"Close\", next: \"Next\", previous: \"Previous\", fit: \"Fit to screen\", counter: (position, total) => `${position} of ${total}` },\n voice: {\n diagramOnScreen: \"diagram on screen\",\n videoOnScreen: \"video on screen\",\n widgetChoices: (question, options) => `${question} Options: ${options.join(\", \")}.`,\n widgetOnScreen: (title) => `${title ? `${title}. ` : \"\"}Answer on screen or by voice.`,\n },\n};\n\n/**\n * Тексты закрытых «Стопом» вызовов: те же, что записывает сервер (core: `HANGING_TOOL_CALL_REASON`, `INTERRUPTED_REASON`,\n * `RESULT_UNKNOWN_REASON`).\n * Не надписи для человека, а часть ответа; лежат здесь, потому что надписи в коде вне словаря запрещены.\n */\nexport const stoppedToolCallReasons = {\n hanging: \"Вызов инструмента оборвался до результата: действие могло выполниться частично или целиком. Не повторяй его, не проверив, что уже сделано\",\n interrupted: \"Ход прерван раньше, чем пользователь ответил на запрос. Действие не выполнялось.\",\n resultUnknown: \"Пользователь разрешил действие, но ход прервался до результата. Неизвестно, выполнено оно или нет; не повторяй его, не проверив.\",\n} as const;\n\n/** Время без слов для словаря на другом языке: «0:12», «1:05». */\nconst clockElapsed = (seconds: number) => `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, \"0\")}`;\n\nconst LabelsContext = createContext<ResolvedChatLabels>(russianLabels);\n\nexport function ChatLabelsProvider({ labels, children }: { labels: ChatLabels; children: ReactNode }) {\n const resolved = useMemo(\n () => ({\n ...labels,\n welcome: { ...(labels.locale?.startsWith(\"en\") ? englishLabels : russianLabels).welcome, ...labels.welcome },\n composerSettings: { ...(labels.locale?.startsWith(\"en\") ? englishLabels : russianLabels).composerSettings, ...labels.composerSettings },\n externalImageLink: labels.externalImageLink ?? russianLabels.externalImageLink,\n answerCutByLength: labels.answerCutByLength ?? russianLabels.answerCutByLength,\n answerEdited: labels.answerEdited ?? russianLabels.answerEdited,\n answerUndelivered: labels.answerUndelivered ?? russianLabels.answerUndelivered,\n toolOutputEdited: labels.toolOutputEdited ?? russianLabels.toolOutputEdited,\n editAnswer: labels.editAnswer ?? russianLabels.editAnswer,\n steeringPending: labels.steeringPending ?? russianLabels.steeringPending,\n spendingMessagesLeft: labels.spendingMessagesLeft ?? russianLabels.spendingMessagesLeft,\n spendingMessagesExhausted: labels.spendingMessagesExhausted ?? russianLabels.spendingMessagesExhausted,\n editMessageLimit: labels.editMessageLimit ?? russianLabels.editMessageLimit,\n thinkingPhrases: labels.thinkingPhrases ?? (labels.reasoningRunning === russianLabels.reasoningRunning ? russianLabels.thinkingPhrases : []),\n thinkingElapsed: labels.thinkingElapsed ?? (labels.reasoningRunning === russianLabels.reasoningRunning ? russianLabels.thinkingElapsed : clockElapsed),\n widgets: { ...russianLabels.widgets, ...labels.widgets },\n diagram: { ...russianLabels.diagram, ...labels.diagram },\n demo: { ...russianLabels.demo, ...labels.demo },\n images: { ...russianLabels.images, ...labels.images },\n videos: { ...russianLabels.videos, ...labels.videos },\n viewer: { ...russianLabels.viewer, ...labels.viewer },\n voice: { ...russianLabels.voice, ...labels.voice },\n // Новые подписи ошибок голоса — на языке словаря, если свой словарь их не знает.\n voiceErrors: { ...(labels.locale?.startsWith(\"en\") ? englishLabels : russianLabels).voiceErrors, ...labels.voiceErrors },\n helpers: { ...russianLabels.helpers, ...labels.helpers },\n tasks: { ...russianLabels.tasks, ...labels.tasks },\n notices: { ...russianLabels.notices, ...labels.notices },\n messengerStatus: { ...russianLabels.messengerStatus, ...labels.messengerStatus },\n workView: { ...russianLabels.workView, ...labels.workView },\n }),\n [labels],\n );\n return <LabelsContext.Provider value={resolved}>{children}</LabelsContext.Provider>;\n}\n\nexport function useChatLabels(): ResolvedChatLabels {\n return useContext(LabelsContext);\n}\n","/** Кусок текста ответа: обычная разметка, схема ```mermaid или живая демонстрация ```html-demo. */\nexport type AnswerSegment =\n | { kind: \"markdown\"; markdown: string }\n /** `source` — текст блока без ограждений; `closed` — блок закрыт; `markdown` — блок целиком, как в тексте. */\n | { kind: \"mermaid\" | \"demo\"; source: string; closed: boolean; markdown: string };\n\n/** Какие блоки выделять: схемы (`diagrams`) и живые демонстрации (`demos`). Остальные остаются разметкой. */\nexport interface AnswerFenceKinds {\n diagrams?: boolean;\n demos?: boolean;\n}\n\n/** Язык блока живой демонстрации. */\nexport const LIVE_DEMO_LANGUAGE = \"html-demo\";\n\n// \\x60 — обратная кавычка: блок кода открывают три и больше «`» или «~» с отступом не больше трёх пробелов.\nconst FENCE_OPENING = /^( {0,3})(\\x60{3,}|~{3,})(.*)$/;\n\nfunction closesFence(line: string, fence: string): boolean {\n const trimmed = line.trim();\n const indent = line.length - line.trimStart().length;\n return indent <= 3 && trimmed.length >= fence.length && trimmed === fence[0]!.repeat(trimmed.length);\n}\n\n/** Строка без пустых строк по краям; отступ первой строки не трогается (код с отступом). */\nconst withoutBlankEdges = (text: string) => text.replace(/^(?:[ \\t]*\\n)+/, \"\").replace(/(?:\\n[ \\t]*)+$/, \"\");\n\nfunction specialKind(language: string | undefined, kinds: AnswerFenceKinds): \"mermaid\" | \"demo\" | null {\n if (language === \"mermaid\" && kinds.diagrams) return \"mermaid\";\n if (language === LIVE_DEMO_LANGUAGE && kinds.demos) return \"demo\";\n return null;\n}\n\n/**\n * Делит Markdown на обычные куски и блоки ```mermaid и ```html-demo верхнего уровня (по правилам ограждений CommonMark):\n * блок внутри другого блока кода и блок с отступом в четыре пробела — обычный код. Номер блока — его порядок среди\n * кусков того же вида; им скин зовёт `renderDiagram`/`renderDemo`, и слой поведения считает так же.\n */\nexport function splitAnswerFences(markdown: string, kinds: AnswerFenceKinds): AnswerSegment[] {\n const segments: AnswerSegment[] = [];\n let plain: string[] = [];\n let otherFence: string | null = null;\n let block: { kind: \"mermaid\" | \"demo\"; fence: string; indent: number; lines: string[]; source: string[] } | null = null;\n\n const flushPlain = () => {\n const text = withoutBlankEdges(plain.join(\"\\n\"));\n if (text.trim()) segments.push({ kind: \"markdown\", markdown: text });\n plain = [];\n };\n const finishBlock = (closed: boolean) => {\n if (!block) return;\n segments.push({ kind: block.kind, source: block.source.join(\"\\n\"), closed, markdown: block.lines.join(\"\\n\") });\n block = null;\n };\n\n for (const line of markdown.split(\"\\n\")) {\n if (block) {\n block.lines.push(line);\n if (closesFence(line, block.fence)) finishBlock(true);\n else block.source.push(line.replace(new RegExp(`^ {0,${block.indent}}`), \"\"));\n continue;\n }\n if (otherFence) {\n plain.push(line);\n if (closesFence(line, otherFence)) otherFence = null;\n continue;\n }\n const opening = FENCE_OPENING.exec(line);\n if (!opening) {\n plain.push(line);\n continue;\n }\n const [, indent = \"\", fence = \"\", info = \"\"] = opening;\n const kind = specialKind(info.trim().split(/\\s+/)[0]?.toLowerCase(), kinds);\n if (kind && !(fence.startsWith(\"\\x60\") && info.includes(\"\\x60\"))) {\n flushPlain();\n block = { kind, fence, indent: indent.length, lines: [line], source: [] };\n } else {\n otherFence = fence;\n plain.push(line);\n }\n }\n finishBlock(false);\n flushPlain();\n return segments;\n}\n","import { splitAnswerFences } from \"../live-demos/answer-fences\";\n\n/** Кусок текста ответа: обычная разметка или блок ```mermaid. */\nexport type MarkdownSegment =\n | { kind: \"markdown\"; markdown: string }\n /** `source` — текст схемы без ограждений; `closed` — блок закрыт; `markdown` — блок целиком, как в тексте. */\n | { kind: \"mermaid\"; source: string; closed: boolean; markdown: string };\n\n/**\n * Делит Markdown на обычные куски и блоки ```mermaid верхнего уровня (по правилам ограждений CommonMark): блок внутри\n * другого блока кода и блок с отступом в четыре пробела — не схема. Номер схемы — её порядок среди кусков `mermaid`;\n * им скин зовёт `renderDiagram(source, blockIndex)`, и слой поведения считает так же.\n */\nexport function splitMermaidBlocks(markdown: string): MarkdownSegment[] {\n return splitAnswerFences(markdown, { diagrams: true }) as MarkdownSegment[];\n}\n","import type { LiveDemoColors } from \"../skin-contract\";\n\n/*\n * Документ живой демонстрации (specs/043-live-demos). Код пишет модель, а модель можно подтолкнуть словами человека\n * или чужой страницы. Поэтому код идёт в рамку без своего источника (`sandbox=\"allow-scripts\"`): ни страницы, ни куки,\n * ни хранилища. Первым в документе — строгий CSP: ничего по сети. Наружу — только высота.\n *\n * CSP не мешает рамке самой уйти на чужой адрес (meta refresh, `location`, клик по ссылке) и унести данные в адресе.\n * Поэтому рамок две: внешняя получает промежуточный документ, чей CSP пускает вложенные рамки только на `about:`,\n * а код модели живёт во вложенной. Любой её переход наружу браузер блокирует правилом промежуточного документа.\n */\n\n/** Предел кода в байтах UTF-8: больше — показывается код, а не запуск. */\nexport const MAX_LIVE_DEMO_BYTES = 30 * 1024;\n/** Выше рамка не бывает: сообщение с большей высотой не принимается. */\nexport const MAX_LIVE_DEMO_HEIGHT = 10_000;\n/** Песочница рамки: скрипты, и только. Без своего источника, окон, форм, диалогов и перехода страницы. */\nexport const LIVE_DEMO_SANDBOX = \"allow-scripts\";\n/** Вид сообщения высоты из рамки. */\nexport const LIVE_DEMO_HEIGHT_MESSAGE = \"agent-chat-live-demo:height\";\n/** Вид сообщения «код ушёл со своей страницы»: его шлёт только промежуточный документ, демонстрация погашена. */\nexport const LIVE_DEMO_LEFT_MESSAGE = \"agent-chat-live-demo:left\";\n/** Кусок потока сервера: в чат попал чужой текст, демонстрации в нём больше не запускаются. */\nexport const EXTERNAL_CONTENT_PART_TYPE = \"data-external-content\";\n\n/** Ничего по сети: скрипты и стили только встроенные, картинки, шрифты и медиа — из самого кода. */\nexport const LIVE_DEMO_CSP = [\n \"default-src 'none'\",\n \"script-src 'unsafe-inline'\",\n \"style-src 'unsafe-inline'\",\n \"img-src data: blob:\",\n \"font-src data:\",\n \"media-src data: blob:\",\n \"frame-src 'none'\",\n \"worker-src 'none'\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n].join(\"; \");\n/**\n * CSP промежуточного документа. Вложенный документ наследует его вдобавок к своему, поэтому картинки, шрифты, медиа и\n * стили тут те же. Главное — `frame-src about:`: вложенная рамка не уйдёт ни на какой адрес, кроме `about:`.\n */\nexport const LIVE_DEMO_SHELL_CSP = [\n \"default-src 'none'\",\n \"script-src 'unsafe-inline'\",\n \"style-src 'unsafe-inline'\",\n \"img-src data: blob:\",\n \"font-src data:\",\n \"media-src data: blob:\",\n \"frame-src about:\",\n \"form-action 'none'\",\n \"base-uri 'none'\",\n].join(\"; \");\n\n/** Цвета, если скин своих не дал. */\nexport const FALLBACK_DEMO_COLORS: Record<\"light\" | \"dark\", LiveDemoColors> = {\n light: { background: \"#ffffff\", surface: \"#f3f4f6\", text: \"#1f2328\", muted: \"#59636e\", border: \"#d1d9e0\", accent: \"#0969da\", onAccent: \"#ffffff\", dark: false },\n dark: { background: \"#16181b\", surface: \"#24282d\", text: \"#e6e8eb\", muted: \"#9198a1\", border: \"#3d444d\", accent: \"#58a6ff\", onAccent: \"#0d1117\", dark: true },\n};\n\nconst VARIABLES: Array<[string, keyof Omit<LiveDemoColors, \"dark\">]> = [\n [\"--bg\", \"background\"],\n [\"--surface\", \"surface\"],\n [\"--text\", \"text\"],\n [\"--muted\", \"muted\"],\n [\"--border\", \"border\"],\n [\"--accent\", \"accent\"],\n [\"--on-accent\", \"onAccent\"],\n];\n\n/** Цвет, который не вырвется из правила `<style>`: `#hex`, имя, `rgb(…)`, `oklch(…)` и подобные. */\nconst SAFE_COLOR = /^[#\\w\\s(),.%/-]+$/;\n\nfunction themeCss(colors: LiveDemoColors): string {\n const fallback = FALLBACK_DEMO_COLORS[colors.dark ? \"dark\" : \"light\"];\n const variables = VARIABLES.map(([name, key]) => {\n const value = colors[key];\n return `${name}:${SAFE_COLOR.test(value) && !value.includes(\"/*\") ? value : fallback[key]}`;\n }).join(\";\");\n return (\n `:root{${variables};color-scheme:${colors.dark ? \"dark\" : \"light\"}}` +\n \"*,*::before,*::after{box-sizing:border-box}\" +\n \"html,body{margin:0}\" +\n \"body{padding:12px;background:var(--bg);color:var(--text);font:14px/1.5 system-ui,-apple-system,sans-serif;overflow-wrap:anywhere}\"\n );\n}\n\n/*\n * Свой скрипт, до кода модели. WebRTC убирается: соединение STUN идёт мимо CSP. Новую рамку со своим WebRTC код не\n * создаст: см. `NESTED_CONTENT_GUARD`. Высота документа — родителю, при каждой смене размера. Переход\n * на другой адрес отменяется ещё и тут (Navigation API); переходы по якорю внутри — можно.\n */\nconst OWN_SCRIPT = `(()=>{for(const k of Object.getOwnPropertyNames(window))if(/^(webkit|moz)?RTC/.test(k))try{delete window[k]}catch(e){}const n=window.navigation;if(n)n.addEventListener(\"navigate\",e=>{if(!e.hashChange&&e.cancelable)e.preventDefault()});let last=0;const send=()=>{const h=Math.min(${MAX_LIVE_DEMO_HEIGHT},Math.ceil(document.documentElement.getBoundingClientRect().height));if(h<1||h===last)return;last=h;parent.postMessage({type:\"${LIVE_DEMO_HEIGHT_MESSAGE}\",height:h},\"*\")};new ResizeObserver(send).observe(document.documentElement);addEventListener(\"load\",send)})();`;\n\n/*\n * Вложенные рамки и `<link>` с заблаговременным соединением. CSP их не держит: у вложенной рамки свой WebRTC, а\n * preconnect в WebKit открывает соединение мимо политики. Хост вырезает их из разметки, свой скрипт не даёт создать.\n */\nconst NESTED_TAGS = [\"iframe\", \"frame\", \"object\", \"embed\"];\nconst EARLY_LINK_RELS = [\"preconnect\", \"prefetch\", \"dns-prefetch\", \"preload\", \"modulepreload\", \"prerender\"];\n\n/*\n * Запрет до кода модели: `createElement`/`createElementNS` вместо такого элемента (и любого `<link>`) дают пустой\n * `<agent-chat-blocked>`; разметка из `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `setHTMLUnsafe`, `document.write`\n * и `createContextualFragment` проходит чистку в отдельном `<template>`. Текст скриптов и стилей не трогается.\n */\nconst NESTED_CONTENT_GUARD =\n `(()=>{const tags=${JSON.stringify(NESTED_TAGS)},rels=${JSON.stringify(EARLY_LINK_RELS)};` +\n `const D=Document.prototype,E=Element.prototype,make=D.createElement,makeNS=D.createElementNS;` +\n `const banned=n=>{n=String(n).toLowerCase().split(\":\").pop();return n===\"link\"||tags.includes(n)};` +\n `D.createElement=function(n,o){return make.call(this,banned(n)?\"agent-chat-blocked\":n,o)};` +\n `D.createElementNS=function(ns,n,o){return banned(n)?make.call(this,\"agent-chat-blocked\"):makeNS.call(this,ns,n,o)};` +\n `const html=Object.getOwnPropertyDescriptor(E,\"innerHTML\"),risky=/<\\\\s*\\\\/?\\\\s*(iframe|frame|object|embed|link)/i;` +\n `const strip=root=>{for(const el of root.querySelectorAll(tags.join(\",\")))el.remove();` +\n `for(const el of root.querySelectorAll(\"link\"))if((el.getAttribute(\"rel\")||\"\").toLowerCase().split(/\\\\s+/).some(r=>rels.includes(r)))el.remove();` +\n `for(const t of root.querySelectorAll(\"template\"))strip(t.content)};` +\n `const clean=v=>{const s=String(v);if(!risky.test(s))return s;const t=make.call(document,\"template\");html.set.call(t,s);strip(t.content);return html.get.call(t)};` +\n `const text=/^(script|style|textarea|title|xmp|noembed|noframes|noscript|plaintext)$/;` +\n `const cleanFor=(el,v)=>el&&text.test(el.localName)?v:clean(v);` +\n `const setter=(P,k,ctx)=>{const d=P&&Object.getOwnPropertyDescriptor(P,k);if(d&&d.set)Object.defineProperty(P,k,{...d,set(v){d.set.call(this,ctx?cleanFor(ctx(this),v):clean(v))}})};` +\n `setter(E,\"innerHTML\",el=>el);setter(E,\"outerHTML\",el=>el.parentElement);setter(window.ShadowRoot&&ShadowRoot.prototype,\"innerHTML\");` +\n `const method=(P,k,i)=>{const f=P&&P[k];if(typeof f===\"function\")P[k]=function(...a){if(a.length>i)a[i]=clean(a[i]);return f.apply(this,a)}};` +\n `method(E,\"insertAdjacentHTML\",1);method(E,\"setHTMLUnsafe\",0);method(window.ShadowRoot&&ShadowRoot.prototype,\"setHTMLUnsafe\",0);method(Range.prototype,\"createContextualFragment\",0);` +\n `for(const k of [\"write\",\"writeln\"]){const f=D[k];D[k]=function(...a){return f.call(this,clean(a.join(\"\")))}}})();`;\n\n/** Разметка модели без вложенных рамок и заблаговременных соединений. Нечего вырезать или нет DOMParser — как есть. */\nfunction withoutNestedContent(documentHtml: string): string {\n if (typeof DOMParser === \"undefined\" || !/<\\s*\\/?\\s*(iframe|frame|object|embed|link)/i.test(documentHtml)) return documentHtml;\n const parsed = new DOMParser().parseFromString(documentHtml, \"text/html\");\n let removed = false;\n const strip = (root: ParentNode) => {\n for (const element of root.querySelectorAll(NESTED_TAGS.join(\",\"))) {\n element.remove();\n removed = true;\n }\n for (const link of root.querySelectorAll(\"link\")) {\n if (!(link.getAttribute(\"rel\") ?? \"\").toLowerCase().split(/\\s+/).some((rel) => EARLY_LINK_RELS.includes(rel))) continue;\n link.remove();\n removed = true;\n }\n for (const template of root.querySelectorAll(\"template\")) strip(template.content);\n };\n strip(parsed);\n return removed ? `<!doctype html>${parsed.documentElement.outerHTML}` : documentHtml;\n}\n\nconst NO_DNS_PREFETCH = '<meta http-equiv=\"x-dns-prefetch-control\" content=\"off\">';\n\n/*\n * Свой скрипт промежуточного документа: создаёт вложенную рамку с кодом и пересылает наверх только высоту от неё.\n * Вторая загрузка вложенной рамки — код ушёл со своей страницы: рамка убирается, наверх — «ушла».\n */\nfunction shellScript(inner: string): string {\n const message = (type: string, extra = \"\") => `parent.postMessage({type:\"${type}\"${extra}},\"*\")`;\n return (\n `(()=>{const f=document.createElement(\"iframe\");f.setAttribute(\"sandbox\",\"${LIVE_DEMO_SANDBOX}\");f.setAttribute(\"referrerpolicy\",\"no-referrer\");` +\n `let loads=0,left=false;f.addEventListener(\"load\",()=>{if(++loads>1&&!left){left=true;f.remove();${message(LIVE_DEMO_LEFT_MESSAGE)}}});` +\n `addEventListener(\"message\",e=>{if(left||e.source!==f.contentWindow)return;const d=e.data;` +\n `if(d&&typeof d===\"object\"&&d.type===\"${LIVE_DEMO_HEIGHT_MESSAGE}\"&&typeof d.height===\"number\")${message(LIVE_DEMO_HEIGHT_MESSAGE, \",height:d.height\")}});` +\n // `<` экранирован: строка не закроет `<script>` промежуточного документа.\n `f.srcdoc=${JSON.stringify(inner).replace(/</g, \"\\\\u003c\")};document.body.append(f)})();`\n );\n}\n\n/**\n * Документ внешней рамки для `srcdoc` — промежуточный: CSP с `frame-src about:`, свой скрипт и вложенная рамка, в\n * которой `liveDemoInnerDocument`. Код модели этот документ не разбирает: он лежит строкой внутри скрипта.\n */\nexport function liveDemoDocument(source: string, colors: LiveDemoColors): string {\n const scheme = colors.dark ? \"dark\" : \"light\";\n return (\n `<!doctype html><html><head><meta charset=\"utf-8\">${NO_DNS_PREFETCH}<meta http-equiv=\"Content-Security-Policy\" content=\"${LIVE_DEMO_SHELL_CSP}\">` +\n `<style>:root{color-scheme:${scheme}}html,body{margin:0;height:100%;overflow:hidden}iframe{display:block;width:100%;height:100%;border:0}</style>` +\n `</head><body><script>${shellScript(liveDemoInnerDocument(source, colors))}</script></body></html>`\n );\n}\n\n/**\n * Документ вложенной рамки: запрет искать адреса заранее, CSP, цвета темы CSS-переменными и свои скрипты — в `<head>`,\n * код модели — в `<body>` без вложенных рамок и заблаговременных соединений. Документ модели целиком (`<html>`, `<head>`) парсер вливает в `<body>`: `<meta>`\n * с CSP там не действует, поэтому ослабить политику код не может.\n */\nexport function liveDemoInnerDocument(source: string, colors: LiveDemoColors): string {\n return withoutNestedContent(\n `<!doctype html><html><head><meta charset=\"utf-8\">${NO_DNS_PREFETCH}<meta http-equiv=\"Content-Security-Policy\" content=\"${LIVE_DEMO_CSP}\">` +\n `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><style>${themeCss(colors)}</style><script>${OWN_SCRIPT}</script>` +\n `<script>${NESTED_CONTENT_GUARD}</script></head><body>${source}</body></html>`,\n );\n}\n\n/** Помещается ли код в предел `MAX_LIVE_DEMO_BYTES`. */\nexport function liveDemoFits(source: string): boolean {\n return source.length <= MAX_LIVE_DEMO_BYTES / 3 || new TextEncoder().encode(source).length <= MAX_LIVE_DEMO_BYTES;\n}\n\n/** Сообщение «код ушёл со своей страницы» — только от своей рамки. */\nexport function demoLeftPage(event: Pick<MessageEvent, \"data\" | \"source\">, frameWindow: Window | null | undefined): boolean {\n if (!frameWindow || event.source !== frameWindow) return false;\n const data: unknown = event.data;\n return typeof data === \"object\" && data !== null && (data as { type?: unknown }).type === LIVE_DEMO_LEFT_MESSAGE;\n}\n\n/**\n * Высота из сообщения рамки: только от её окна (`event.source`), только `{ type, height }` с конечным числом в пределах\n * 1…`MAX_LIVE_DEMO_HEIGHT`, с округлением вверх. Иначе — `null`.\n */\nexport function demoHeightOf(event: Pick<MessageEvent, \"data\" | \"source\">, frameWindow: Window | null | undefined): number | null {\n if (!frameWindow || event.source !== frameWindow) return null;\n const data: unknown = event.data;\n if (typeof data !== \"object\" || data === null) return null;\n const { type, height } = data as { type?: unknown; height?: unknown };\n if (type !== LIVE_DEMO_HEIGHT_MESSAGE || typeof height !== \"number\" || !Number.isFinite(height)) return null;\n return height > 0 && height <= MAX_LIVE_DEMO_HEIGHT ? Math.ceil(height) : null;\n}\n","/**\n * Отметка ядра «шаг начат заново у другого провайдера»: всё от последней части `step-start` до неё стёрто.\n * Копия логики ядра (`withoutResetSteps`): React не зависит от ядра. Что копии совпадают, проверяет общий тест.\n */\nexport const STEP_RESET_PART_TYPE = \"data-step-reset\";\n\n/** Части ответа без стёртых шагов: каждая часть `data-step-reset` убирает себя и всё от последней `step-start` перед ней. Сброса нет — тот же массив. */\nexport function withoutResetSteps<Part extends { type: string }>(parts: readonly Part[]): Part[] {\n if (!parts.some((part) => part.type === STEP_RESET_PART_TYPE)) return parts as Part[];\n const kept: Part[] = [];\n for (const part of parts) {\n if (part.type !== STEP_RESET_PART_TYPE) {\n kept.push(part);\n continue;\n }\n const stepStart = kept.findLastIndex((candidate) => candidate.type === \"step-start\");\n if (stepStart !== -1) kept.length = stepStart;\n }\n return kept;\n}\n\n/** Сообщения без стёртых шагов; ничего не стёрто — тот же массив. */\nexport function withoutResetMessages<Message extends { parts: Array<{ type: string }> }>(messages: Message[]): Message[] {\n let changed = false;\n const shown = messages.map((message) => {\n const parts = withoutResetSteps(message.parts);\n if (parts === message.parts) return message;\n changed = true;\n return { ...message, parts };\n });\n return changed ? shown : messages;\n}\n","import { createContext, useCallback, useContext, useSyncExternalStore } from \"react\";\n\n/** Состояние чата: значения, записанные инструментами (`model.state`), без служебных ключей SDK. */\nexport type ChatStateValues = Record<string, unknown>;\n\nexport const APP_STATE_PART_TYPE = \"data-app-state\";\nconst EMPTY: ChatStateValues = Object.freeze({}) as ChatStateValues;\n\n/** Служебный ключ SDK (`$closed`): экрану не нужен, сервер его и не присылает. */\nconst isReservedPath = (path: string): boolean => path.startsWith(\"$\");\n\nconst isObject = (value: unknown): value is ChatStateValues => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** Свёртка записей состояния показанной ветки по порядку: в записи сначала `unset`, потом `set`. */\nexport function foldChatState(messages: ReadonlyArray<{ parts: ReadonlyArray<{ type: string }> }>): ChatStateValues {\n let values: ChatStateValues | undefined;\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type !== APP_STATE_PART_TYPE) continue;\n const record = (part as { data?: unknown }).data;\n if (!isObject(record)) continue;\n values ??= {};\n applyRecord(values, record);\n }\n }\n return values ?? EMPTY;\n}\n\nfunction applyRecord(values: ChatStateValues, record: ChatStateValues): void {\n const unset = Array.isArray(record.unset) ? record.unset : [];\n for (const path of unset) {\n if (typeof path !== \"string\" || isReservedPath(path)) continue;\n const keys = path.split(\".\");\n let parent: unknown = values;\n for (const key of keys.slice(0, -1)) parent = isObject(parent) && Object.hasOwn(parent, key) ? parent[key] : undefined;\n if (isObject(parent)) delete parent[keys.at(-1)!];\n }\n const set = isObject(record.set) ? record.set : {};\n for (const [path, value] of Object.entries(set)) {\n if (isReservedPath(path)) continue;\n const keys = path.split(\".\");\n let target = values;\n for (const key of keys.slice(0, -1)) {\n if (!isObject(target[key])) target[key] = {};\n target = target[key] as ChatStateValues;\n }\n target[keys.at(-1)!] = structuredClone(value);\n }\n}\n\n/** Состояние из события сервера (`state-changed`): объект без служебных ключей; не объект — `null`. */\nexport function chatStateOfEvent(state: unknown): ChatStateValues | null {\n if (!isObject(state)) return null;\n return Object.fromEntries(Object.entries(state).filter(([key]) => !isReservedPath(key)));\n}\n\n/** Одинаковые ли состояния: то же значение не перерисовывает экран и не зовёт `onStateChange` повторно. */\nexport const sameChatState = (a: ChatStateValues, b: ChatStateValues): boolean => a === b || JSON.stringify(a) === JSON.stringify(b);\n\nexport const ChatStateContext = createContext<ChatStateValues>(EMPTY);\n\n/** Чат на странице, который публикует своё состояние: последний опубликованный — главный. */\ntype Publisher = { values: ChatStateValues };\n\n/**\n * Состояние открытых на странице чатов по `chatId`: его публикует `useAgentChat` после загрузки истории, читают панели\n * вне `AgentChat`. Своих запросов нет: источник один — лента чата.\n */\nconst published = new Map<string, Publisher[]>();\nconst listeners = new Map<string, Set<() => void>>();\n\nconst notify = (chatId: string) => listeners.get(chatId)?.forEach((listener) => listener());\n\nconst publishedState = (chatId: string): ChatStateValues => published.get(chatId)?.at(-1)?.values ?? EMPTY;\n\n/** Публикует состояние чата для `useChatState(chatId)`; возвращает смену значения и снятие с публикации. */\nexport function publishChatState(chatId: string, values: ChatStateValues): { update(values: ChatStateValues): void; remove(): void } {\n const publisher: Publisher = { values };\n published.set(chatId, [...(published.get(chatId) ?? []), publisher]);\n notify(chatId);\n return {\n update(next) {\n if (publisher.values === next) return;\n publisher.values = next;\n notify(chatId);\n },\n remove() {\n const rest = (published.get(chatId) ?? []).filter((candidate) => candidate !== publisher);\n if (rest.length > 0) published.set(chatId, rest);\n else published.delete(chatId);\n notify(chatId);\n },\n };\n}\n\nfunction subscribe(chatId: string, listener: () => void): () => void {\n const set = listeners.get(chatId) ?? new Set();\n set.add(listener);\n listeners.set(chatId, set);\n return () => {\n set.delete(listener);\n if (set.size === 0) listeners.delete(chatId);\n };\n}\n\n/**\n * Состояние чата показанной ветки, без служебных ключей. Без аргумента — внутри `AgentChat` (компоненты скина, виджеты,\n * свои кнопки): живьём по ходу ответа и после смены ветки; вне `AgentChat` — пустое; с `useAgentChat` — поле\n * `chatState`. С `chatId` — где угодно на странице (панель рядом с чатом): то же состояние, что у `AgentChat` или\n * `useAgentChat` этого чата на странице, с теми же живыми обновлениями и без своих запросов. Чата на странице нет или\n * его история ещё грузится — пустое.\n */\nexport function useChatState(chatId?: string): ChatStateValues {\n const inChat = useContext(ChatStateContext);\n const key = chatId ?? \"\";\n const subscribeToChat = useCallback((listener: () => void) => (chatId === undefined ? () => {} : subscribe(key, listener)), [chatId, key]);\n const getSnapshot = useCallback(() => (chatId === undefined ? EMPTY : publishedState(key)), [chatId, key]);\n const outside = useSyncExternalStore(subscribeToChat, getSnapshot, getSnapshot);\n return chatId === undefined ? inChat : outside;\n}\n","import type { AgentChatMessage } from \"./chat-types\";\nimport { stoppedToolCallReasons } from \"./labels\";\n\ntype MessagePart = AgentChatMessage[\"parts\"][number];\n\n/*\n * После «Стопа» сервер закрывает вызовы без результата и сохраняет так ответ (core: `closeHangingToolCalls` и\n * `denyPendingApprovals`). Экран делает то же сразу, не дожидаясь перезагрузки: те же состояния и те же тексты.\n */\n\ninterface ToolPartView {\n state?: string;\n preliminary?: boolean;\n approval?: { id?: string; approved?: boolean; reason?: string };\n}\n\nconst isToolPart = (part: MessagePart) => part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\";\n\n/**\n * Вызовы без окончательного результата закрыты: запущенные — ошибкой, ждавшие ответа человека или уже разрешённые — отказом\n * (у разрешённого — «результат неизвестен», у отклонённого — причина человека). Остальное не тронуто.\n */\nexport function closeStoppedToolCalls(parts: readonly MessagePart[]): MessagePart[] {\n return parts.map((part) => {\n if (!isToolPart(part)) return part;\n const { state, preliminary, approval } = part as ToolPartView;\n if ((state === \"approval-requested\" || state === \"approval-responded\") && approval?.id) {\n const reason =\n approval.approved === false && approval.reason\n ? approval.reason\n : approval.approved\n ? stoppedToolCallReasons.resultUnknown\n : stoppedToolCallReasons.interrupted;\n return { ...part, state: \"output-denied\", approval: { id: approval.id, approved: false, reason } } as MessagePart;\n }\n if (state === \"input-available\" || (state === \"output-available\" && preliminary === true)) {\n const { output: _output, preliminary: _preliminary, ...rest } = part as MessagePart & { output?: unknown; preliminary?: boolean };\n return { ...rest, state: \"output-error\", errorText: stoppedToolCallReasons.hanging } as MessagePart;\n }\n return part;\n });\n}\n","import { closeStoppedToolCalls } from \"./close-stopped-tool-calls\";\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport type { FileUIPart } from \"ai\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ALWAYS_IN_CHAT, messageText } from \"./chat-types\";\nimport { VOICE_MODE_PART_TYPE } from \"./voice-turn\";\nimport type { AgentChatMessage, AnswerStatus, ApprovalDecision, ApprovalFailureKind, BranchFork, ChatModelChoice, CompactionMarker, VoiceCut, VoiceSound } from \"./chat-types\";\nimport { isConnectionError } from \"./liveness/connection-errors\";\nimport { DEFAULT_RECONNECT_DELAYS_MS, runReconnectAttempts } from \"./liveness/reconnect-attempts\";\nimport { watchChat } from \"./chat-watch/chat-watch\";\nimport { fetchWithDeadline } from \"./liveness/request-deadline\";\nimport { effectiveSilenceTimeoutMs } from \"./liveness/silence-watchdog\";\nimport { spendingInfoOf, spendingViewOf, untilResetCheckMs } from \"./spending-status\";\nimport type { SpendingInfo, SpendingView } from \"./spending-status\";\nimport { APP_CHANGE_PART_TYPE } from \"./app-bridge/app-bridge\";\nimport { BROWSER_CALL_PART_TYPE, runBrowserCall } from \"./app-bridge/browser-calls\";\nimport type { BrowserCallRequest } from \"./app-bridge/browser-calls\";\nimport type { AgentAppBridge, ScreenPlace } from \"./app-bridge/app-bridge\";\nimport { pause } from \"./pause\";\nimport { appRefusalOf } from \"./app-refusal\";\nimport type { AppRefusal } from \"./app-refusal\";\nimport { serverRefusalOf } from \"./server-refusal\";\nimport type { ServerRefusal } from \"./server-refusal\";\nimport { helperProgressOf, pendingHelperApprovalId } from \"./helpers/helper-call\";\nimport { useWidgetAnswer } from \"./widgets/use-widget-answer\";\nimport { appEventOf } from \"./app-events\";\nimport { ALL_CAPABILITIES, CHAT_CLOSED_PART_TYPE, capabilitiesOf, closureOf, sameCapabilities } from \"./chat-access\";\nimport type { ChatCapabilities, ChatClosure } from \"./chat-access\";\nimport { EXTERNAL_CONTENT_PART_TYPE } from \"./live-demos/live-demo-document\";\nimport type { AppEvent } from \"./app-events\";\nimport type { WidgetFailureKind } from \"./widgets/use-widget-answer\";\nimport { hasPendingApproval, withPendingWidgetsClosed, withWidgetAnswersSettled, withWidgetContinuationMarked } from \"./widgets/widget-messages\";\nimport { withoutResetMessages } from \"./step-reset\";\nimport { APP_STATE_PART_TYPE, chatStateOfEvent, foldChatState, publishChatState, sameChatState } from \"./chat-state/chat-state\";\nimport { AGENT_NOTICE_PART_TYPE } from \"./notices/agent-notices\";\nimport type { ChatStateValues } from \"./chat-state/chat-state\";\nimport { correctionMessage, correctionsAfterHistory, outgoingCorrection } from \"./steering/corrections\";\n\n/** Настройки живой связи. Всё необязательно: по умолчанию сроки из договора 002. */\nexport interface LivenessOptions {\n /** Сколько тишины терпеть в потоке ответа. По умолчанию 30 000 мс. */\n silenceTimeoutMs?: number;\n /** Паузы перед попытками переподключения. По умолчанию [0, 2000, 5000]. */\n reconnectDelaysMs?: readonly number[];\n /** Срок одной попытки и любого запроса без потока. По умолчанию 10 000 мс. */\n requestTimeoutMs?: number;\n}\n\n/** Как вкладка видит связь с сервером. */\nexport type ConnectionState = \"online\" | \"reconnecting\" | \"offline\";\n\nexport interface UseAgentChatOptions {\n /** Адрес обработчика чата, например `/api/chat`. */\n api: string;\n chatId: string;\n /** Свой `fetch` — для тестов и особых сред. */\n fetch?: typeof globalThis.fetch;\n liveness?: LivenessOptions;\n /** У сервера включены лимиты расходов: читать состояние лимита и показывать его. Отказ 429 обрабатывается всегда. */\n spendingLimits?: boolean;\n /** Связь с приложением: место, вкладка, сообщения об изменении данных, браузерные вызовы. Нет — чат как раньше. */\n appBridge?: AgentAppBridge;\n /** Текст в поле ввода при открытии чата. */\n initialDraft?: string;\n /** Приложение отказало ходу или человеку в доступе: код для программы и слова для человека. Зовётся на каждый такой отказ. */\n onTurnRefused?: (refusal: AppRefusal) => void;\n /**\n * Событие приложения пришло по каналу наблюдения: вне хода (`publishAppEvent` на сервере, без `messageId`) или от\n * хука итога, доделанного после «Стопа» (`messageId` — ответ, куда оно уже дописано; история перечитывается сама).\n * Канал открыт, пока чат свободен: событие, опубликованное во время хода, вкладка не получит. События в ответах\n * читает `AgentChat.onAppEvent`.\n */\n onAppEvent?: (event: AppEvent, messageId?: string) => void;\n /**\n * Новые события ответа `messageId` придут живыми с перечитанной историей: ответ на виджет принят без продолжения,\n * события его проверки уже дописаны в ответ. `AgentChat` передаёт сюда `expectLive` учёта ленты.\n */\n onExpectLiveAppEvents?: (messageId: string) => void;\n /**\n * Поправки на лету: пока идёт ответ, `submit` отправляет текст поправкой (`POST …/steer`), агент берёт её на ближайшем\n * шаге. Своя лента включает их явно (`true`) и рисует `corrections` после `messages`; по умолчанию выключено — во время\n * ответа поле не отправляет, как до 0.34.\n */\n steering?: boolean;\n /** Другое значение — история перечитывается с сервера (права, закрытие, лента): чат изменился не через экран. Первое значение ничего не перечитывает. */\n reloadKey?: number;\n}\n\nexport interface AgentChatState {\n messages: AgentChatMessage[];\n /** Записи сжатия истории на текущей ветке: на их границах лента рисует разделители. */\n compactions: CompactionMarker[];\n /** Развилки текущей ветки: у сообщения с соседями лента рисует переключатель «‹ 2/3 ›». */\n forks: BranchFork[];\n /** История загружена с сервера. */\n loaded: boolean;\n value: string;\n setValue: (value: string) => void;\n canSubmit: boolean;\n /**\n * Идёт ответ, и поле принимает поправки: `submit` отправит текст (или файлы) поправкой, агент возьмёт её на ближайшем\n * шаге. Пустое поле `submit` не отправит, как и при `canSubmit`.\n */\n canSteer: boolean;\n /**\n * Поправки, которые агент ещё не взял: лента рисует их после сообщений, с пометкой. Взятая поправка уходит отсюда в\n * `messages`.\n */\n corrections: AgentChatMessage[];\n /** Идёт ответ или вкладка переподключается к нему. */\n busy: boolean;\n /** Отправляет введённый текст; `files` — готовые вложения (ссылки на загруженные файлы): тогда текст может быть пуст. */\n submit: (files?: readonly FileUIPart[], onRejected?: (kind: string | undefined) => void) => boolean;\n stop: () => Promise<void>;\n /**\n * Отправляет слова из голосового режима (с признаком `voiceMode`, чтобы ответ был коротким и разговорным).\n * `false` — не ушло: пусто, чат не загружен, ответ ещё идёт, лимит исчерпан или ждёт подтверждения инструмента.\n */\n sendVoiceMessage: (text: string, options?: { sounds?: readonly VoiceSound[] }) => boolean;\n /** Ставит у ответа отметку обрыва озвучки на экране; на сервер ничего не уходит. */\n showVoiceCut: (messageId: string, cut: VoiceCut) => void;\n /**\n * Склейка голосовых фраз: останавливает ход, у которого ещё нет ничего, кроме размышления, и правкой заменяет последнее\n * голосовое сообщение новым текстом (прежнее с пустым ответом уходит в соседнюю ветку); модель отвечает на новое.\n * `sounds` — звуки человека, пойманные с этой частью фразы: уходят на сервер, на экране не видны.\n * Выполнено, когда сервер принял правку. `false` — заменить нельзя (у ответа уже есть текст или вызов инструмента,\n * сообщение не голосовое, сервер отказал): сообщение не заменено, ход мог быть остановлен.\n * Вызовы идут по очереди.\n */\n replaceLastVoiceMessage: (text: string, options?: { sounds?: readonly VoiceSound[] }) => Promise<boolean>;\n /** Последний ответ можно повторить: ошибка, стоп, прерывание или пустой ответ. */\n canRetry: boolean;\n /** Сообщение собеседника не дошло до сервера. */\n sendFailed: boolean;\n /** История чата не загрузилась: сервер ответил ошибкой. */\n loadFailed: boolean;\n /** Сервер ответил 404: чат чужой или его нет. Это окончательно, повторять нечего. */\n notFound: boolean;\n /** Выбор модели, сохранённый в чате (из истории); `null`, пока история не загружена и при скрытой модели. */\n savedModelChoice: ChatModelChoice | null;\n /** Видит ли модель чата картинки — из истории; `undefined`, пока история не пришла, модели нет в каталоге или сервер старый. */\n modelAcceptsImages: boolean | undefined;\n /**\n * В чат попал чужой текст — из истории или куском потока посреди ответа. Раз появившись, не пропадает: чужой текст\n * остаётся в истории чата. Живые демонстрации в таком чате не запускаются.\n */\n externalContent: boolean;\n /** Связь с сервером: обычная работа, идут попытки подключиться, попытки исчерпаны. */\n connection: ConnectionState;\n /**\n * Новый ответ на последний вопрос; если вопрос не дошёл до сервера — отправляет его заново;\n * если не загрузилась история — грузит её снова; при «Нет связи» — начинает попытки заново.\n */\n retry: () => void;\n /** Ответ человека на запрос подтверждения; когда отвечены все запросы, ход продолжается и лента дописывается. */\n respondToApproval: (approvalId: string, decision: ApprovalDecision) => Promise<void>;\n /** Запрос, ответ на который сейчас идёт на сервер. */\n approvalPendingId: string | null;\n /** Запрос, ответ на который не принят (нет связи, лимит, чат занят): кнопки снова доступны. */\n approvalFailure: { id: string; kind: ApprovalFailureKind; text?: string } | null;\n /**\n * Ответ человека на виджет (вызов `toolCallId`); `true` — сервер принял, ход продолжается и лента дописывается.\n * `onAccepted` — сразу, как сервер принял, не дожидаясь продолжения.\n */\n answerWidget: (toolCallId: string, answer: unknown, onAccepted?: () => void) => Promise<boolean>;\n /** Виджет, ответ на который сейчас идёт на сервер. */\n widgetPendingId: string | null;\n /** Виджет, ответ на который не принят: он снова активен. */\n widgetFailure: { id: string; kind: WidgetFailureKind; text?: string } | null;\n /** Обычная отправка готового текста мимо поля ввода (подсказка ответа); `false` — не ушло. */\n sendReply: (text: string) => boolean;\n /** Переключает текущую ветку на соседнее сообщение развилки; во время ответа не делает ничего. `true` — история новой ветки показана. */\n selectBranch: (messageId: string) => Promise<boolean>;\n /** Последний ответ можно переделать: он законченный и непустой (у остальных — «повторить»). */\n canRegenerate: boolean;\n /** Новый ответ на последний вопрос; прежний остаётся соседней веткой. */\n regenerate: () => void;\n /** Отправленное сообщение можно править: ничего не идёт и связь есть. */\n canEdit: boolean;\n /**\n * Правка отправленного сообщения: новая ветка от того же места с новым текстом и новым ответом.\n * Отвечает, как только сервер принял правку или отказал, не дожидаясь конца ответа. При отказе лента возвращается\n * к прежнему виду: тексту, который собеседник правил, есть куда вернуться.\n */\n edit: (messageId: string, text: string) => Promise<EditOutcome>;\n /** Лимит расходов подошёл к пределу или исчерпан; `null` — всё в пределах, лимитов нет или они не включены. */\n spending: SpendingView | null;\n /**\n * Сервер отказал не из-за связи: `signed_out` — вход истёк (401), `too_many_turns` — у человека уже идут ответы\n * в других чатах, `forbidden_origin` — сервер не признал страницу своей (403, адреса нет в `allowedOrigins`).\n * `null` — отказа нет. Вход снимается любым успешным запросом, остальные — принятым ходом и сменой чата.\n */\n refusal: ChatRefusal | null;\n /**\n * Приложение не пустило человека в этот чат (`resolveUser` вернул отказ) на загрузке истории: слова приложения вместо чата.\n * `null` — доступ есть.\n */\n accessRefusal: ServerRefusal | null;\n /**\n * Сервер отказал ходу 4xx с понятным текстом (`{ error: \"…\" }` или `{ error: { message: \"…\" } }`): текст до\n * 300 знаков. `final` — повтор того же запроса снова получит отказ (всё, кроме 408, 425 и 429): «Повторить» не нужна.\n * Отправленное сообщение при окончательном отказе возвращается в поле. Снимается следующим ходом и сменой чата.\n * Отказ правке показывает сам редактор. `null` — такого отказа нет.\n */\n serverRefusal: ServerRefusal | null;\n /**\n * Счётчик «список фоновых задач мог измениться»: растёт при каждом открытии подписки `GET {chat}/watch` и на её\n * событие `tasks-changed`. Сервер без подписки — всегда 0.\n */\n tasksVersion: number;\n /** Сервер не заявил, что фона у него нет (`backgroundTasks: false` в истории): задачи можно спрашивать. Старый сервер без заявки — `true`. */\n backgroundTasksAvailable: boolean;\n /** Права человека в чате (из истории); сервер до 0.27 их не присылает — всё разрешено. */\n capabilities: ChatCapabilities;\n /**\n * Чат закрыт: отправка, «Повторить», виджеты и подтверждения недоступны. Правка, «Переделать», ветки и правка ответа\n * уводят с точки закрытия и идут по правам (`capabilities`). Приходит с историей, отказом 409 `chat_closed`, куском\n * потока ответа и событиями наблюдения. `null` — чат открыт.\n */\n closed: ChatClosure | null;\n /** Состояние чата показанной ветки (`model.state` на сервере): живьём по ходу ответа и после смены ветки. */\n chatState: ChatStateValues;\n}\n\n/** Отказ сервера, о котором говорит надпись над полем ввода. */\nexport type ChatRefusal = \"signed_out\" | \"too_many_turns\" | \"forbidden_origin\";\n\n/** Причина, по которой правка не ушла на сервер. */\nexport type EditNotStartedReason = \"busy\" | \"offline\" | \"empty\" | \"unknown_message\";\n/** Причина отказа сервера: ответ уже идёт, правка неверна, сообщения в чате нет, лимит расходов исчерпан. */\nexport type EditRefusalReason =\n | \"busy\"\n | \"invalid\"\n | \"not_found\"\n | \"spending_limit\"\n | \"signed_out\"\n | \"too_many_turns\"\n | \"forbidden_origin\"\n | \"server_refused\";\n/**\n * Чем кончилась попытка правки. `started` — сервер принял, идёт новый ответ (обрыв связи после этого — обычный путь «Повторить»).\n * У `server_refused` есть `text` — причина отказа словами сервера (4xx с текстом); если отказало приложение — ещё и `code`.\n * У `spending_limit` из-за окна сообщений — `unit: \"messages\"`; у денег поля нет.\n */\nexport type EditOutcome =\n | { status: \"started\" }\n | { status: \"not_started\"; reason: EditNotStartedReason }\n | { status: \"refused\"; reason: EditRefusalReason; text?: string; code?: string; unit?: \"messages\" };\n\ninterface ServerHistory {\n chat?: Partial<ChatModelChoice> & { closed?: unknown };\n /** Нет у серверов до 0.27. */\n capabilities?: unknown;\n /** Фоновые задачи на сервере подключены. Нет у серверов до этой заявки: тогда клиент спрашивает адрес задач сам. */\n backgroundTasks?: boolean;\n /** Видит ли модель чата картинки; нет у старого сервера и у модели вне каталога. */\n modelAcceptsImages?: unknown;\n /** В чат попал чужой текст (веб, файл человека, инструмент с пометкой): живые демонстрации не запускаются. */\n externalContent?: unknown;\n messages: AgentChatMessage[];\n running: boolean;\n /**\n * Ответ в истории идёт, а живого потока у этой копии сервера нет: сервер упал и поднялся, или ответ ведёт другая копия.\n * Нет у серверов до этапа 8в.\n */\n detachedAnswer?: boolean;\n /** Нет у серверов до фичи сжатия. */\n compactions?: CompactionMarker[];\n compacting?: boolean;\n /** Нет у серверов до фичи веток. */\n forks?: BranchFork[];\n /** Поправки после идущего ответа, ещё не взятые агентом. Нет у серверов до 0.34. */\n pendingMessages?: AgentChatMessage[];\n}\n\n/** Что вышло из запроса истории: `unreachable` — сеть или срок, `signed_out` — вход истёк, `server_error` — сервер ответил отказом. */\ntype HistoryResult = ServerHistory | \"not_found\" | \"unreachable\" | \"signed_out\" | \"server_error\" | \"access_refused\";\n\n/** Правка, что ещё не дошла до сервера: `messageId` — новое сообщение, `editedMessageId` — то, что правят. */\ninterface PendingEdit {\n messageId: string;\n editedMessageId: string;\n /** Сервер ответил на запрос правки: принял (`null`) или отказал по этой причине. */\n answered: (refusal: EditRefusalReason | null) => void;\n /** Отказ сервера, если он был: после него лента возвращается, а серия попыток не нужна. */\n refusal: EditRefusalReason | null;\n /** Текст отказа словами сервера (4xx с текстом). */\n refusalText?: string;\n /** Код отказа приложения. */\n refusalCode?: string;\n /** Отказ по лимиту сообщений, а не денег. */\n refusalUnit?: \"messages\";\n}\n\nconst EDIT_REFUSALS: Record<number, EditRefusalReason> = { 400: \"invalid\", 401: \"signed_out\", 404: \"not_found\", 409: \"busy\", 429: \"spending_limit\" };\n\n/** Запросы, которые начинают ход. */\nconst TURN_ENDPOINTS = [\"/messages\", \"/retry\", \"/regenerate\", \"/edit\"];\n\n/** Чем кончилось подключение к идущему ответу. */\ntype ResumeOutcome = \"streaming\" | \"nothing\" | \"failed\";\n\n/** Ожидание итога подключения: транспорт сообщает, когда пришёл первый байт, 204 или отказ. */\ninterface ResumeProbe {\n signal: AbortSignal;\n settle: (outcome: ResumeOutcome) => void;\n}\n\nconst HEARTBEAT_HEADER = \"x-agent-chat-heartbeat-ms\";\n\n/**\n * Сервер ставит признак «идёт сжатие» до конца потока ответа, поэтому первая проверка через эту паузу не промахивается\n * мимо старта; пауза только не даёт спросить раньше, чем страница дорисует ответ.\n */\nconst COMPACTION_FIRST_CHECK_MS = 500;\n/** Как часто спрашивать, пока сжатие идёт: разделитель должен появиться не позже чем через 3 секунды после записи. */\nconst COMPACTION_POLL_INTERVAL_MS = 1_500;\n/** Сжатие на сервере обрывается через 10 минут; опрос живёт чуть дольше. Потом запись появится при следующей загрузке чата. */\nconst COMPACTION_POLL_LIMIT_MS = 12 * 60_000;\n\n/** Как часто перечитывать историю, пока ответ идёт без живого потока: сервер прерывает брошенный ответ через 15–20 с. */\nconst DETACHED_ANSWER_CHECK_EVERY_MS = 3_000;\n/** Сколько ждать итога брошенного ответа от сервера; дальше ответ прерывается на экране и можно «Повторить». */\nconst DETACHED_ANSWER_GIVE_UP_MS = 30_000;\n\n/** Сколько раз склейка пробует правку, если сервер отвечает «занят», и через сколько мс. */\nconst REPLACE_ATTEMPTS = 6;\nconst REPLACE_RETRY_MS = 150;\n/** Сколько ждать, пока чат освободится после остановки, и как часто проверять. */\nconst IDLE_WAIT_MS = 3_000;\nconst IDLE_POLL_MS = 20;\n\n/**\n * Поля тела запроса хода: часовой пояс браузера (по нему дата в запросе к модели) и от моста — вкладка и место на\n * экране на этот момент. Нет моста — только пояс.\n */\nfunction pageFields(bridge: AgentAppBridge | undefined): { tabId?: string; place?: ScreenPlace; timeZone?: string } {\n const zone = browserTimeZone();\n const timeZone = zone ? { timeZone: zone } : {};\n if (!bridge) return timeZone;\n let place: ScreenPlace | null = null;\n try {\n place = bridge.currentPlace();\n } catch {\n // Сбой в коде приложения не должен мешать отправке: сообщение уходит без места.\n }\n const sendable = place && serializablePlace(place);\n return { tabId: bridge.tabId, ...(sendable && { place: sendable }), ...timeZone };\n}\n\n/** Пояс браузера, например `Europe/Moscow`; браузер не сказал — нет пояса, сервер возьмёт свой. */\nfunction browserTimeZone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Место, которое переживёт `JSON.stringify`: данные не сериализуются (цикл, BigInt) — место без них; и описание плохое — места нет. */\nfunction serializablePlace(place: ScreenPlace): ScreenPlace | null {\n for (const candidate of [place, { description: place.description }]) {\n try {\n JSON.stringify(candidate);\n return candidate;\n } catch {\n // пробуем следующий вариант\n }\n }\n return null;\n}\n\nexport function useAgentChat({\n api,\n chatId,\n fetch: customFetch,\n liveness,\n spendingLimits = false,\n appBridge,\n initialDraft = \"\",\n onTurnRefused,\n onAppEvent,\n onExpectLiveAppEvents,\n reloadKey,\n steering = false,\n}: UseAgentChatOptions): AgentChatState {\n const chatUrl = `${api}/${encodeURIComponent(chatId)}`;\n const [refusal, setRefusal] = useState<ChatRefusal | null>(null);\n const [serverRefusal, setServerRefusal] = useState<ServerRefusal | null>(null);\n const [accessRefusal, setAccessRefusal] = useState<ServerRefusal | null>(null);\n const [capabilities, setCapabilities] = useState<ChatCapabilities>(ALL_CAPABILITIES);\n const [closed, setClosed] = useState<ChatClosure | null>(null);\n // Сервер без заявки (старый) мог бы держать фон: спрашиваем его сами, как раньше.\n const [backgroundCapability, setBackgroundCapability] = useState<{ chatUrl: string; available: boolean } | null>(null);\n const backgroundTasksAvailable = backgroundCapability?.chatUrl === chatUrl ? backgroundCapability.available : true;\n const [modelAcceptsImages, setModelAcceptsImages] = useState<boolean | undefined>(undefined);\n const [externalContent, setExternalContent] = useState(false);\n const showClosure = useCallback((next: ChatClosure | null) => {\n setClosed((current) => (current?.code === next?.code && current?.message === next?.message ? current : next));\n }, []);\n /** Права, закрытие и зрение модели из истории: то же значение не перерисовывает экран. */\n const applyAccess = useCallback(\n (history: ServerHistory) => {\n const next = capabilitiesOf(history.capabilities);\n setCapabilities((current) => (sameCapabilities(current, next) ? current : next));\n showClosure(closureOf(history.chat?.closed));\n // Partial/legacy branch envelopes do not erase a capability already learned for this chat.\n if (typeof history.backgroundTasks === \"boolean\") {\n const available = history.backgroundTasks;\n setBackgroundCapability((current) => current?.chatUrl === chatUrl && current.available === available ? current : { chatUrl, available });\n }\n setModelAcceptsImages(typeof history.modelAcceptsImages === \"boolean\" ? history.modelAcceptsImages : undefined);\n // Только включается: история, прочитанная раньше куска потока, признак не снимает.\n if (history.externalContent === true) setExternalContent(true);\n },\n [chatUrl, showClosure],\n );\n const onTurnRefusedRef = useRef(onTurnRefused);\n onTurnRefusedRef.current = onTurnRefused;\n /** Сообщает странице об отказе приложения; обычные отказы сервера (без кода) не в счёт. Отказ `chat_closed` закрывает чат. */\n const announceAppRefusal = useCallback(\n (refused: ServerRefusal | null) => {\n if (!refused?.kind || !refused.code) return;\n if (refused.kind === \"chat_closed\") showClosure({ code: refused.code, message: refused.text });\n onTurnRefusedRef.current?.({ kind: refused.kind, code: refused.code, message: refused.text });\n },\n [showClosure],\n );\n // Приложение может передавать новую функцию `fetch` при каждой перерисовке: запросы берут последнюю, а сама\n // `request` не меняется — иначе от перерисовки страницы заново грузилась бы история и пропадал набранный текст.\n const customFetchRef = useRef(customFetch);\n customFetchRef.current = customFetch;\n const warnedForbiddenOrigin = useRef(false);\n // Вход истёк — сервер отвечает 401 на любой запрос; любой успешный ответ значит, что человек снова вошёл.\n // Чужая для сервера страница получает 403 только на запись: снимает отказ успешная запись, а не чтение.\n const request = useCallback(\n async (input: string, init?: RequestInit) => {\n const response = await (customFetchRef.current ?? globalThis.fetch)(input, init);\n const writes = (init?.method ?? \"GET\").toUpperCase() !== \"GET\";\n // 401 со словами приложения — отказ в доступе, а не «вход истёк»: надпись говорит его слова.\n if (response.status === 401 && !(await appRefusalOf(response))) setRefusal(\"signed_out\");\n else if (await isForbiddenOrigin(response)) {\n // Отказ правке показывает сам редактор: одна ошибка — одно сообщение.\n if (!input.endsWith(\"/edit\")) setRefusal(\"forbidden_origin\");\n if (!warnedForbiddenOrigin.current && isDevelopment()) {\n warnedForbiddenOrigin.current = true;\n console.warn(forbiddenOriginHint());\n }\n } else if (response.ok) setRefusal((current) => (current === \"signed_out\" || (writes && current === \"forbidden_origin\") ? null : current));\n return response;\n },\n [],\n );\n const settings = {\n silenceTimeoutMs: liveness?.silenceTimeoutMs ?? 30_000,\n reconnectDelaysMs: liveness?.reconnectDelaysMs ?? DEFAULT_RECONNECT_DELAYS_MS,\n requestTimeoutMs: liveness?.requestTimeoutMs ?? 10_000,\n };\n const settingsRef = useRef(settings);\n settingsRef.current = settings;\n\n /** Id сообщения собеседника, которое не дошло до сервера: «повторить» отправляет его заново. */\n const unsentMessageId = useRef<string | null>(null);\n /** Ответ, который заменяет «повторить»: если сервер недоступен, он возвращается на место. */\n const replacedAnswer = useRef<AgentChatMessage | null>(null);\n /** Сервер отказал именно этому сообщению (400 с видом ошибки): его нужно вернуть в поле, а не «повторять». */\n const rejectedSend = useRef<{ messageId: string | undefined; kind: string | undefined } | null>(null);\n /** Id сообщения, которое уходит на сервер прямо сейчас. */\n const sendingMessageId = useRef<string | undefined>(undefined);\n /** Id сообщений, отправленных из голосового режима: их запрос несёт `voiceMode`, в том числе при повторной отправке. */\n const voiceMessageIds = useRef(new Set<string>());\n /** Звуки человека голосовых сообщений по id: уходят в запросе вместе с `voiceMode`, в сообщение на экране не попадают. */\n const voiceSoundsById = useRef(new Map<string, readonly VoiceSound[]>());\n /** Id ответа, который переделывает `regenerate`: запрос идёт на `/regenerate`, а не на `/retry`. */\n const regeneratedAnswerId = useRef<string | null>(null);\n /** Правка, что ещё не дошла до сервера. */\n const pendingEdit = useRef<PendingEdit | null>(null);\n /** Ход мог создать соседа на развилке (правка, переделка, повтор): после него развилки читаются заново. */\n const forksMayChange = useRef(false);\n const chatUrlRef = useRef(chatUrl);\n chatUrlRef.current = chatUrl;\n /** Мост приложения: место берётся в момент запроса, поэтому транспорт читает его отсюда. */\n const bridgeRef = useRef(appBridge);\n bridgeRef.current = appBridge;\n const requestRef = useRef(request);\n requestRef.current = request;\n /** Перечитывает права из истории: после отказа `action_forbidden` экран узнаёт, что именно стало нельзя. */\n const refreshAccessRef = useRef<() => Promise<void>>(async () => {});\n /** Сервер не ждёт ответа браузерного вызова: хода уже нет. Без живого потока лента перечитывает историю. */\n const browserCallGoneRef = useRef<() => void>(() => {});\n /** Обрывает функции браузерных инструментов этой вкладки: «стоп», смена чата, размонтирование. */\n const browserCallsLifetime = useRef<AbortController | null>(null);\n const browserCallsSignal = () => (browserCallsLifetime.current ??= new AbortController()).signal;\n useEffect(\n () => () => {\n browserCallsLifetime.current?.abort();\n browserCallsLifetime.current = null;\n },\n [chatId],\n );\n /** Сервер отказал в ходе из-за лимита расходов: ход не начат, ничего не записано. */\n const spendingRefusal = useRef<SpendingInfo | null>(null);\n /** Ответ, ждавший решения, каким он был до отправки нового сообщения: возвращается, если сервер сообщение не принял. */\n const approvalsClosedBySubmit = useRef<AgentChatMessage | null>(null);\n /** Последняя ошибка запроса: отличает обрыв связи от отказа сервера. */\n const lastError = useRef<Error | null>(null);\n /** Кто ждёт итога подключения к идущему ответу. */\n const resumeProbe = useRef<ResumeProbe | null>(null);\n /** Идущая серия попыток переподключения; одна на вкладку. */\n const seriesRef = useRef<AbortController | null>(null);\n /** Серия идёт (в том числе тихая первая попытка): вкладка занята, отправлять нельзя. */\n const [seriesRunning, setSeriesRunning] = useState(false);\n const [connection, setConnection] = useState<ConnectionState>(\"online\");\n /** Поправки, которые агент ещё не взял. */\n const [corrections, setCorrections] = useState<AgentChatMessage[]>([]);\n const correctionsRef = useRef(corrections);\n correctionsRef.current = corrections;\n /** Id поправок, что ещё идут на сервер. */\n const sendingCorrections = useRef(new Set<string>());\n /** Очередь запросов поправок: сервер получает их в том порядке, в каком человек их написал. */\n const steerQueue = useRef<Promise<void>>(Promise.resolve());\n /**\n * Поправка, на которую сервер не ответил (обрыв, срок): он мог её уже записать. Тот же текст с теми же файлами уходит\n * снова с тем же id — сервер узнаёт повтор, второй копии нет.\n */\n const unconfirmedCorrection = useRef<{ chatUrl: string; id: string; text: string; files: string } | null>(null);\n /** Ответ кончился, а поправки ждут: вкладка перечитывает историю и подключается к ходу по ним. */\n const [handingOff, setHandingOff] = useState(false);\n const handOff = useRef<Promise<void> | null>(null);\n const handOffToCorrectionsRef = useRef<() => Promise<void>>(async () => {});\n /** Поток ответа открыт: читается в момент вызова, `onFinish` снимает его раньше, чем придёт отрисовка. */\n const answeringRef = useRef(false);\n\n const transport = useMemo(\n () =>\n new DefaultChatTransport<AgentChatMessage>({\n api: `${chatUrl}/messages`,\n fetch: async (input, init) => {\n lastError.current = null;\n const url = input instanceof Request ? input.url : String(input);\n const { requestTimeoutMs, silenceTimeoutMs } = settingsRef.current;\n // Подключение к идущему ответу — единственный GET транспорта: за ним следит проба попытки.\n const probe = (init?.method ?? \"GET\").toUpperCase() === \"GET\" ? resumeProbe.current : null;\n const turnRequest = (init?.method ?? \"POST\").toUpperCase() === \"POST\" && TURN_ENDPOINTS.some((endpoint) => url.endsWith(endpoint));\n // Текст прежнего отказа к новому ходу не относится: сбой этого хода покажет свою надпись.\n if (turnRequest) setServerRefusal(null);\n let response: Response;\n try {\n response = await fetchWithDeadline(request, url, init, {\n timeoutMs: requestTimeoutMs,\n ...(probe && { signal: probe.signal }),\n silence: (streamed) => {\n const heartbeatMs = Number(streamed.headers.get(HEARTBEAT_HEADER)) || 0;\n return {\n silenceTimeoutMs: effectiveSilenceTimeoutMs(silenceTimeoutMs, heartbeatMs),\n // Сервер с признаком жизни шлёт пинг сразу после заголовков: тишина до него — уже обрыв.\n ...(heartbeatMs > 0 && { firstByteTimeoutMs: requestTimeoutMs }),\n onFirstByte: () => {\n probe?.settle(\"streaming\");\n // Связь есть, раз сервер ответил: если нет серии попыток, «Нет связи» убирается.\n if (!seriesRef.current) setConnection(\"online\");\n },\n onSilence: () => probe?.settle(\"failed\"),\n };\n },\n });\n } catch (error) {\n probe?.settle(\"failed\");\n throw error;\n }\n if (response.ok && (init?.method ?? \"POST\").toUpperCase() === \"POST\" && url.endsWith(\"/messages\")) {\n // Сервер принял сообщение, значит закрыл запрос на подтверждение: показанный отказ верен.\n approvalsClosedBySubmit.current = null;\n }\n const spendingLimit = response.status === 429 ? await spendingRefusalOf(response) : null;\n if (response.status === 429) spendingRefusal.current = spendingLimit;\n const tooManyTurns = turnRequest && response.status === 429 && (await errorKindOf(response)) === \"too_many_turns\";\n if (response.ok && turnRequest) setRefusal((current) => (current === \"too_many_turns\" ? null : current));\n // Отказ правке показывает сам редактор, у остальных ходов — надпись над полем.\n if (tooManyTurns && !url.endsWith(\"/edit\")) setRefusal(\"too_many_turns\");\n // 403 без `forbidden_origin` в теле (например, HTML-страница внешнего прокси) — обычный сбой.\n const forbiddenOrigin = await isForbiddenOrigin(response);\n // Отказ со своим текстом; вход, лимит, «другие чаты» и «чужая страница» говорят своими надписями.\n const accessRefusedWithWords = response.status === 401 && (await appRefusalOf(response)) !== null;\n const knownRefusal = (response.status === 401 && !accessRefusedWithWords) || spendingLimit !== null || tooManyTurns || forbiddenOrigin;\n const refusedWithText = turnRequest && !knownRefusal ? await serverRefusalOf(response) : null;\n // Закрытие говорит своей надписью над полем: второй надписи не нужно.\n if (refusedWithText && !url.endsWith(\"/edit\") && refusedWithText.kind !== \"chat_closed\") setServerRefusal(refusedWithText);\n announceAppRefusal(refusedWithText);\n // Права на сервере сменились, пока экран был открыт: запрещённая кнопка уходит с экрана, а не отказывает снова.\n if (refusedWithText?.kind === \"action_forbidden\") void refreshAccessRef.current();\n const edit = pendingEdit.current;\n if (edit && url.endsWith(\"/edit\")) {\n const refusal = response.ok\n ? null\n : tooManyTurns\n ? \"too_many_turns\"\n : forbiddenOrigin\n ? \"forbidden_origin\"\n : refusedWithText\n ? \"server_refused\"\n : (EDIT_REFUSALS[response.status] ?? null);\n if (refusal) edit.refusal = refusal;\n if (refusedWithText) edit.refusalText = refusedWithText.text;\n if (refusedWithText?.code) edit.refusalCode = refusedWithText.code;\n if (spendingLimit?.unit === \"messages\") edit.refusalUnit = \"messages\";\n // Отказ, которого мы не знаем (5xx), — обычный сбой: правка остаётся в ленте, «Повторить» отправит её снова.\n edit.answered(refusal);\n }\n // Вход истёк, страница не своя для сервера, уже идут ответы в других чатах или сервер окончательно отказал\n // своими словами — сообщение не принято, как и при 400: оно вернётся в поле.\n const refusedSend = response.status === 400 || response.status === 401 || forbiddenOrigin || tooManyTurns || refusedWithText?.final === true;\n if (refusedSend && (init?.method ?? \"POST\").toUpperCase() === \"POST\" && url.endsWith(\"/messages\")) {\n const kind = await errorKindOf(response);\n rejectedSend.current = { messageId: sendingMessageId.current, kind: response.status === 401 && !accessRefusedWithWords ? \"signed_out\" : kind };\n }\n if (response.status === 204) probe?.settle(\"nothing\");\n else if (!response.ok || !response.body) probe?.settle(\"failed\");\n return response;\n },\n // Сервер сам хранит историю: уходит только новое сообщение. «Повторить» — пустой запрос,\n // кроме сообщения, которое до сервера не дошло: оно уходит заново с тем же id.\n prepareSendMessagesRequest: ({ messages, trigger }) => {\n const last = messages.at(-1);\n sendingMessageId.current = last?.id;\n const edit = pendingEdit.current;\n if (edit && last?.id === edit.messageId) {\n // Правка-склейка голосовых фраз: отметку голоса, как у отправки, сервер ставит сам по `voiceMode`.\n const voiceMode = voiceMessageIds.current.has(last.id);\n const voiceSounds = voiceSoundsById.current.get(last.id) ?? [];\n const outgoing = { ...last, parts: last.parts.filter((part) => part.type !== VOICE_MODE_PART_TYPE) };\n return {\n api: `${chatUrl}/edit`,\n body: { editedMessageId: edit.editedMessageId, message: outgoing, ...(voiceMode && { voiceMode: true }), ...(voiceMode && voiceSounds.length > 0 && { voiceSounds }), ...pageFields(bridgeRef.current) },\n };\n }\n if (trigger === \"regenerate-message\" && regeneratedAnswerId.current) {\n return { api: `${chatUrl}/regenerate`, body: { messageId: regeneratedAnswerId.current, ...pageFields(bridgeRef.current) } };\n }\n if (trigger === \"regenerate-message\" && last?.id !== unsentMessageId.current) {\n return { api: `${chatUrl}/retry`, body: pageFields(bridgeRef.current) };\n }\n const voiceMode = last !== undefined && voiceMessageIds.current.has(last.id);\n const voiceSounds = (last && voiceSoundsById.current.get(last.id)) ?? [];\n // Отметку голосового хода сервер ставит сам по `voiceMode`; в запрос она не идёт.\n const outgoing = last && { ...last, parts: last.parts.filter((part) => part.type !== VOICE_MODE_PART_TYPE) };\n return { api: `${chatUrl}/messages`, body: { message: outgoing, ...(voiceMode && { voiceMode: true }), ...(voiceMode && voiceSounds.length > 0 && { voiceSounds }), ...pageFields(bridgeRef.current) } };\n },\n // Идущий ответ отдаётся с начала хода; 204 — хода уже нет.\n prepareReconnectToStreamRequest: () => ({ api: `${chatUrl}/stream` }),\n }),\n [chatUrl, request],\n );\n const [notFound, setNotFound] = useState(false);\n const [compactions, setCompactions] = useState<CompactionMarker[]>([]);\n const [forks, setForks] = useState<BranchFork[]>([]);\n /** Идущий опрос записей сжатия; один на вкладку. */\n const compactionPoll = useRef<AbortController | null>(null);\n\n /** Лёгкий запрос записей сжатия; `null` — не вышло (сеть, срок, отказ). */\n const loadCompactions = useCallback(\n async (signal: AbortSignal): Promise<{ compactions: CompactionMarker[]; compacting: boolean } | null> => {\n try {\n const response = await fetchWithDeadline(request, `${chatUrl}/compactions`, undefined, {\n timeoutMs: settingsRef.current.requestTimeoutMs,\n wholeBody: true,\n signal,\n });\n return response.ok ? ((await response.json()) as { compactions: CompactionMarker[]; compacting: boolean }) : null;\n } catch {\n return null;\n }\n },\n [chatUrl, request],\n );\n\n /**\n * Спрашивает записи сжатия, пока оно идёт, и показывает новые: разделитель появляется без перезагрузки страницы.\n * Сбой запроса опрос не останавливает, чат от него не ломается.\n */\n const pollCompactions = useCallback(() => {\n compactionPoll.current?.abort();\n const controller = new AbortController();\n compactionPoll.current = controller;\n const { signal } = controller;\n void (async () => {\n await pause(COMPACTION_FIRST_CHECK_MS, signal);\n for (let waited = COMPACTION_FIRST_CHECK_MS; !signal.aborted && waited < COMPACTION_POLL_LIMIT_MS; waited += COMPACTION_POLL_INTERVAL_MS) {\n const state = await loadCompactions(signal);\n if (signal.aborted) return;\n if (state) {\n setCompactions(state.compactions);\n if (!state.compacting) return;\n }\n await pause(COMPACTION_POLL_INTERVAL_MS, signal);\n }\n })();\n }, [loadCompactions]);\n /** Развилки читаются заново после хода, что мог создать соседа; сбой запроса ничего не ломает. */\n const refreshForks = useCallback(async () => {\n const asked = chatUrl;\n try {\n const response = await fetchWithDeadline(request, `${chatUrl}/branches`, undefined, {\n timeoutMs: settingsRef.current.requestTimeoutMs,\n wholeBody: true,\n });\n if (!response.ok || chatUrlRef.current !== asked) return;\n setForks(((await response.json()) as { forks: BranchFork[] }).forks);\n } catch {\n // Развилки покажутся при следующей загрузке чата.\n }\n }, [chatUrl, request]);\n\n /** Что ход, ждавший повтора, больше не нужен: ответ пришёл, сервер сохранил его или попытки кончились. */\n const clearPendingResend = () => {\n replacedAnswer.current = null;\n regeneratedAnswerId.current = null;\n pendingEdit.current = null;\n };\n\n /** История с сервера, с учётом сроков; `signal` — своя отмена (серия попыток, уход со страницы). */\n const loadHistory = useCallback(\n async (signal?: AbortSignal): Promise<HistoryResult> => {\n try {\n const response = await fetchWithDeadline(request, chatUrl, undefined, {\n timeoutMs: settingsRef.current.requestTimeoutMs,\n wholeBody: true,\n ...(signal && { signal }),\n });\n if (response.status === 404) return \"not_found\";\n // Приложение не пустило человека в чат: его слова вместо чата, а не «вход истёк».\n const refusedAccess = response.ok ? null : await serverRefusalOf(response);\n if (refusedAccess?.kind === \"access_refused\") {\n setAccessRefusal(refusedAccess);\n announceAppRefusal(refusedAccess);\n return \"access_refused\";\n }\n if (response.status === 401) return \"signed_out\";\n if (!response.ok) return \"server_error\";\n // Доступ вернули: отказ приложения больше не показывается вместо чата.\n setAccessRefusal(null);\n return (await response.json()) as ServerHistory;\n } catch (error) {\n return isConnectionError(error) ? \"unreachable\" : \"server_error\";\n }\n },\n [announceAppRefusal, chatUrl, request],\n );\n refreshAccessRef.current = async () => {\n const asked = chatUrl;\n const history = await loadHistory();\n if (typeof history !== \"string\" && chatUrlRef.current === asked) applyAccess(history);\n };\n const [sendFailed, setSendFailed] = useState(false);\n const [spending, setSpending] = useState<SpendingView | null>(null);\n const exhausted = spending?.state === \"exhausted\";\n\n /** Читает состояние лимита; сбой или сервер без лимитов (404) оставляют прежнее, чат от них не ломается. */\n const refreshSpending = useCallback(async () => {\n if (!spendingLimits) return;\n const asked = chatUrl;\n try {\n const response = await fetchWithDeadline(request, `${chatUrl}/spending`, undefined, {\n timeoutMs: settingsRef.current.requestTimeoutMs,\n wholeBody: true,\n });\n if (!response.ok || chatUrlRef.current !== asked) return;\n setSpending(spendingViewOf((await response.json()) as Parameters<typeof spendingViewOf>[0]));\n } catch {\n // Надпись появится при следующем чтении: после ответа или при открытии чата.\n }\n }, [chatUrl, request, spendingLimits]);\n\n // Размонтирование обрывает только запрос вкладки: ход на сервере идёт дальше, «стоп» — лишь по кнопке.\n const chat = useChat<AgentChatMessage>({\n id: chatId,\n transport,\n onError: (error) => {\n lastError.current = error;\n },\n // Временные куски данных в сообщения не попадают: сообщение об изменении данных идёт приложению сразу, как пришло.\n // Браузерный вызов исполняется по куску сервера, а не по `onToolCall`: кусок приходит только после «Разрешить».\n onData: (part) => {\n if (part.type === BROWSER_CALL_PART_TYPE) {\n const bridge = bridgeRef.current;\n if (bridge) {\n void runBrowserCall(part.data as BrowserCallRequest, {\n bridge,\n chatUrl: chatUrlRef.current,\n request: requestRef.current,\n signal: browserCallsSignal(),\n onNotPending: () => browserCallGoneRef.current(),\n });\n }\n return;\n }\n if (part.type === EXTERNAL_CONTENT_PART_TYPE) {\n setExternalContent(true);\n return;\n }\n if (part.type === CHAT_CLOSED_PART_TYPE) {\n const closure = closureOf(part.data);\n if (closure) showClosure(closure);\n return;\n }\n if (part.type !== APP_CHANGE_PART_TYPE) return;\n const { tag, details } = part.data as { tag: string; details?: unknown };\n if (part.id !== undefined) bridgeRef.current?.dataChanged({ id: part.id, tag, ...(details !== undefined && { details }) });\n },\n onFinish: ({ messages: received, isAbort }) => {\n answeringRef.current = false;\n // Стёртые шаги (`data-step-reset`) не видны ни ленте, ни решениям хука.\n const messages = withoutResetMessages(received);\n // Ход после ответа на виджет кончился: отвеченный вызов больше не «в пути» (widgets/widget-messages.ts).\n if (withWidgetAnswersSettled(messages) !== messages) setMessages((current) => withWidgetAnswersSettled(current));\n // Агент взял поправки (или «Стоп» оставил их ждать, или поправка сама начала ход после отказа): ход по ним идёт\n // на сервере, вкладка подключается к нему.\n const handOffToCorrections = () => {\n if (correctionsRef.current.length > 0) void handOffToCorrectionsRef.current();\n };\n const refusal = spendingRefusal.current;\n if (refusal) {\n spendingRefusal.current = null;\n // Правку вернёт `edit`: лента и поле правки прежние, здесь только «лимит исчерпан».\n if (pendingEdit.current?.refusal) setSpending({ state: \"exhausted\", info: refusal });\n else void showSpendingRefusal(refusal, messages);\n handOffToCorrections();\n return;\n }\n // Сервер отказал в правке (ленту вернёт `edit`) или сообщению (его вернут в поле): искать на сервере нечего.\n if (pendingEdit.current?.refusal || rejectedSend.current) {\n handOffToCorrections();\n return;\n }\n // Сервер всегда шлёт итог ответа перед концом потока. Нет итога — обрыв связи, даже если\n // поток закрылся без ошибки: так его закрывает прокси, когда сервер за ним упал.\n if (isAbort || isFinalStatus(lastAnswerStatus(messages))) {\n clearPendingResend();\n // Сервер принял сообщение и ответил: звуки человека больше не нужны (они держатся только на случай повторной отправки).\n if (sendingMessageId.current && isFinalStatus(lastAnswerStatus(messages))) voiceSoundsById.current.delete(sendingMessageId.current);\n // Ход оборвался (стоп, сбой), а вызов после «Разрешить» так и остался без результата: правду о нём знает сервер.\n if (hasStalledContinuation(messages)) void refreshFromServer(chatLifetime.current.signal);\n void refreshSpending();\n if (forksMayChange.current) {\n forksMayChange.current = false;\n void refreshForks();\n }\n // Сервер сжимает историю фоном сразу после штатного ответа: проверить, не появился ли разделитель.\n if (!isAbort && lastAnswerStatus(messages) === \"complete\") pollCompactions();\n handOffToCorrections();\n return;\n }\n const error = lastError.current;\n // Запрос кончился без итога ответа: обрыв связи или отказ сервера. Ленту восстанавливает серия попыток.\n startSeries({ quietFirst: error !== null && !isConnectionError(error) });\n },\n });\n const { sendMessage, regenerate, resumeStream, status, stop: stopChat } = chat;\n // Шаг, начатый заново у другого провайдера (`data-step-reset`), не виден ни в ленте, ни в решениях хука: стёртое\n // убирается при выдаче, и правки ленты получают её уже без него.\n const shownMessages = useMemo(() => withoutResetMessages(chat.messages), [chat.messages]);\n const setChatMessages = chat.setMessages;\n const setMessages = useCallback(\n (update: AgentChatMessage[] | ((current: AgentChatMessage[]) => AgentChatMessage[])) =>\n setChatMessages(typeof update === \"function\" ? (current) => update(withoutResetMessages(current)) : update),\n [setChatMessages],\n );\n const messagesRef = useRef(shownMessages);\n messagesRef.current = shownMessages;\n const [loaded, setLoaded] = useState(false);\n /** Для какого `chatId` загружена история: после смены чата `loaded` ещё один проход относится к прежнему. */\n const [loadedChatId, setLoadedChatId] = useState<string | null>(null);\n /** Отправка, нажатая до загрузки истории: уйдёт, как только история придёт. */\n const [submitWhenLoaded, setSubmitWhenLoaded] = useState(false);\n // Ожидающая отправка принадлежит чату, в котором её нажали; «Повторить» загрузку того же чата её сохраняет.\n useEffect(() => setSubmitWhenLoaded(false), [chatId]);\n const [loadFailed, setLoadFailed] = useState(false);\n const [savedModelChoice, setSavedModelChoice] = useState<ChatModelChoice | null>(null);\n /** Номер попытки загрузить историю: «повторить» после сбоя загрузки начинает новую. */\n const [loadAttempt, setLoadAttempt] = useState(0);\n /** Подключаемся к ответу, который шёл при загрузке истории. */\n const [resuming, setResuming] = useState(false);\n /** С какого мига история показывает идущий ответ без живого потока у сервера; `null` — такого ответа нет. */\n const [detachedSince, setDetachedSince] = useState<number | null>(null);\n /** Число проверок брошенного ответа: каждая, удачная или нет, назначает следующую. */\n const [detachedChecks, setDetachedChecks] = useState(0);\n const [value, setValue] = useState(initialDraft);\n const sending = useRef(false);\n\n /**\n * Лимит исчерпан, ход не начат: серия попыток не нужна. Лента возвращается к тому, что записал сервер,\n * а введённый текст — в поле ввода.\n */\n const showSpendingRefusal = useCallback(\n async (refusal: SpendingInfo, shown: AgentChatMessage[]) => {\n clearPendingResend();\n unsentMessageId.current = null;\n setSpending({ state: \"exhausted\", info: refusal });\n const history = await loadHistory();\n if (chatUrlRef.current !== chatUrl) return;\n const stored = typeof history === \"string\" ? null : history.messages;\n const last = shown.at(-1);\n const notStored = last?.role === \"user\" && !(stored ?? []).some((message) => message.id === last.id);\n if (notStored) {\n const text = messageText(last);\n setValue((current) => (current.trim().length > 0 ? current : text));\n }\n if (typeof history !== \"string\") applyAccess(history);\n if (stored) setMessages(stored);\n else if (notStored) setMessages(shown.slice(0, -1));\n },\n [applyAccess, chatUrl, loadHistory, setMessages],\n );\n\n useEffect(() => {\n if (!loaded) {\n setSpending(null);\n return;\n }\n void refreshSpending();\n }, [loaded, refreshSpending]);\n\n // Исчерпание снимается само в момент обновления лимита: вкладка спрашивает сервер, а не считает сама.\n useEffect(() => {\n if (spending?.state !== \"exhausted\") return;\n const wait = untilResetCheckMs(spending.info, Date.now());\n if (wait === null) return;\n const timer = setTimeout(() => void refreshSpending(), wait);\n return () => clearTimeout(timer);\n }, [refreshSpending, spending]);\n\n /** Подключается к идущему ответу и ждёт первого байта, а не конца ответа. */\n const resumeWithProbe = useCallback(\n (signal: AbortSignal) =>\n new Promise<ResumeOutcome>((resolve) => {\n const probe: ResumeProbe = {\n signal,\n settle: (outcome) => {\n if (resumeProbe.current === probe) resumeProbe.current = null;\n resolve(outcome);\n },\n };\n if (signal.aborted) {\n resolve(\"failed\");\n return;\n }\n resumeProbe.current = probe;\n signal.addEventListener(\"abort\", () => probe.settle(\"failed\"), { once: true });\n lastError.current = null;\n // Поток закрылся, не дав знать о себе: без ошибки — подключение было, с ошибкой — нет.\n void resumeStream().then(\n () => probe.settle(lastError.current ? \"failed\" : \"streaming\"),\n () => probe.settle(\"failed\"),\n );\n }),\n [resumeStream],\n );\n\n /**\n * Лента из истории сервера с сообщением, что до него не дошло: история перечитывается и после сбоя (подписка на события,\n * опрос), а «Повторить» отправляет это сообщение из ленты с тем же id. Сервер его всё-таки записал — повторять нечего.\n * Сервер ведёт чужой ход — сообщению после него не место: текст возвращается в поле.\n */\n const withUnsentKept = useCallback(\n (history: ServerHistory): AgentChatMessage[] => {\n const unsent = messagesRef.current.find((message) => message.id === unsentMessageId.current);\n if (!unsent) return history.messages;\n const stored = history.messages.some((message) => message.id === unsent.id);\n if (!stored && !history.running) return [...history.messages, unsent];\n unsentMessageId.current = null;\n setSendFailed(false);\n if (!stored) setValue((current) => (current.trim().length > 0 ? current : messageText(unsent)));\n return history.messages;\n },\n [],\n );\n\n /**\n * Показывает историю сервера и подключается к идущему ответу, если он есть.\n * `false` — подключиться не вышло: лента возвращается к тому, что было на экране (`before`).\n */\n const applyServerHistory = useCallback(\n async (history: ServerHistory, before: AgentChatMessage[], signal: AbortSignal): Promise<boolean> => {\n applyAccess(history);\n const shown = withUnsentKept(history);\n setMessages(history.running ? withWidgetContinuationMarked(withContinuationMarked(shown)) : shown);\n setCorrections((current) => {\n const next = correctionsAfterHistory(history.pendingMessages ?? [], shown, current, sendingCorrections.current);\n // Те же поправки — тот же массив: лента не перерисовывается.\n return next.length === current.length && next.every((message, index) => message === current[index]) ? current : next;\n });\n setCompactions(history.compactions ?? []);\n setForks(history.forks ?? []);\n setDetachedSince(history.detachedAnswer === true ? (since) => since ?? Date.now() : null);\n if (!history.running) return true;\n // История не содержит идущий ответ: он целиком придёт потоком с начала хода, без повторов.\n const outcome = await resumeWithProbe(signal);\n if (outcome === \"streaming\") return true;\n if (outcome === \"nothing\") {\n // Ход успел закончиться между загрузкой истории и подключением: готовый ответ уже в истории.\n const finished = await loadHistory(signal);\n if (finished === \"not_found\") {\n setNotFound(true);\n return true;\n }\n if (typeof finished !== \"string\") {\n applyAccess(finished);\n setMessages(finished.messages);\n setCompactions(finished.compactions ?? []);\n setForks(finished.forks ?? []);\n return true;\n }\n }\n setMessages(before);\n return false;\n },\n [applyAccess, loadHistory, resumeWithProbe, setMessages, withUnsentKept],\n );\n\n /** Ход не начат: недописанный ответ прерван, а ответ, который заменяло «Повторить», возвращается на место. */\n const restoreReplacedAnswer = useCallback(() => {\n const replaced = replacedAnswer.current;\n clearPendingResend();\n setMessages((current) => {\n const last = current.at(-1);\n if (last?.role === \"assistant\") {\n return isFinalStatus(last.metadata?.status) ? current : withLastAnswerStatus(current, \"interrupted\");\n }\n return replaced ? [...current, replaced] : current;\n });\n }, [setMessages]);\n\n /** Одна попытка переподключения: история сервера главнее того, что успела показать вкладка. */\n const reconnectAttempt = useCallback(\n async (signal: AbortSignal): Promise<boolean> => {\n const before = messagesRef.current;\n const history = await loadHistory(signal);\n if (signal.aborted) return false;\n if (history === \"not_found\") {\n setNotFound(true);\n return true;\n }\n if (history === \"signed_out\" || history === \"access_refused\") {\n // Вход истёк или приложение закрыло доступ: ждать связи бессмысленно, лента возвращается к прежнему виду, надпись говорит причину.\n restoreReplacedAnswer();\n return true;\n }\n if (typeof history === \"string\") return false;\n const last = before.at(-1);\n if (last?.role === \"user\" && !history.messages.some((message) => message.id === last.id)) {\n // Связь с сервером есть, но сообщение до него не дошло.\n unsentMessageId.current = last.id;\n setSendFailed(true);\n return true;\n }\n const connected = await applyServerHistory(history, before, signal);\n if (connected) clearPendingResend();\n return connected;\n },\n [applyServerHistory, loadHistory, restoreReplacedAnswer],\n );\n\n /** Отправка без нового текста: то же сообщение или новый ответ на последний вопрос. */\n const resend = useCallback(() => {\n if (sending.current) return;\n sending.current = true;\n const last = messagesRef.current.at(-1);\n replacedAnswer.current = last?.role === \"assistant\" ? last : null;\n if (last?.role === \"assistant\") forksMayChange.current = true;\n setSendFailed(false);\n rejectedSend.current = null;\n void regenerate().finally(() => {\n sending.current = false;\n const rejection = rejectedSend.current;\n if (!rejection) return;\n // Сервер не принял заново отправленное сообщение: как при первой отправке, оно уходит из ленты в поле.\n rejectedSend.current = null;\n clearPendingResend();\n unsentMessageId.current = null;\n const rejected = messagesRef.current.find((item) => item.id === rejection.messageId);\n setMessages((current) => current.filter((item) => item.id !== rejection.messageId));\n if (rejected?.role === \"user\") {\n const text = messageText(rejected);\n setValue((current) => (current.length === 0 ? text : current));\n }\n });\n }, [regenerate, setMessages]);\n\n /** Попытки исчерпаны: недописанный ответ прерван, «Нет связи с сервером». */\n const giveUp = useCallback(() => {\n restoreReplacedAnswer();\n setConnection(\"offline\");\n }, [restoreReplacedAnswer]);\n\n /**\n * Серия попыток переподключения. `quietFirst` — сервер ответил отказом, а не пропал: первая\n * попытка идёт без «Переподключаемся…». `resendUnsent` — собеседник сам просит связь (кнопка,\n * событие сети): не дошедшее сообщение уходит заново.\n */\n const startSeries = useCallback(\n (options: { quietFirst?: boolean; resendUnsent?: boolean } = {}) => {\n if (seriesRef.current) return;\n const controller = new AbortController();\n seriesRef.current = controller;\n setSeriesRunning(true);\n if (!options.quietFirst) setConnection(\"reconnecting\");\n void runReconnectAttempts({\n delaysMs: settingsRef.current.reconnectDelaysMs,\n attemptTimeoutMs: settingsRef.current.requestTimeoutMs,\n signal: controller.signal,\n attempt: async (signal, index) => {\n const connected = await reconnectAttempt(signal);\n if (!connected && options.quietFirst && index === 0 && !signal.aborted) setConnection(\"reconnecting\");\n return connected;\n },\n }).then((outcome) => {\n if (seriesRef.current !== controller) return;\n seriesRef.current = null;\n setSeriesRunning(false);\n if (outcome === \"exhausted\") {\n giveUp();\n return;\n }\n if (outcome !== \"connected\") return;\n setConnection(\"online\");\n const last = messagesRef.current.at(-1);\n if (options.resendUnsent && last?.role === \"user\" && last.id === unsentMessageId.current) resend();\n });\n },\n [giveUp, reconnectAttempt, resend],\n );\n\n /** Обрывает серию попыток, если она идёт, и возвращает вкладку в обычное состояние. */\n const cancelSeries = useCallback(() => {\n seriesRef.current?.abort();\n seriesRef.current = null;\n setSeriesRunning(false);\n setConnection(\"online\");\n }, []);\n\n // Загрузка истории зависит только от того, какой чат открыт, и от «Повторить»: остальное читается в момент загрузки.\n const historyLoad = useRef({ chatId, loadHistory, applyServerHistory, startSeries, pollCompactions });\n historyLoad.current = { chatId, loadHistory, applyServerHistory, startSeries, pollCompactions };\n useEffect(() => {\n const { chatId, loadHistory, applyServerHistory, startSeries, pollCompactions } = historyLoad.current;\n const controller = new AbortController();\n setLoaded(false);\n setLoadFailed(false);\n // Не дошедшее сообщение принадлежит прежнему чату.\n unsentMessageId.current = null;\n setSendFailed(false);\n setNotFound(false);\n setRefusal(null);\n setServerRefusal(null);\n setAccessRefusal(null);\n setSavedModelChoice(null);\n setModelAcceptsImages(undefined);\n setExternalContent(false);\n setCapabilities(ALL_CAPABILITIES);\n setClosed(null);\n setCompactions([]);\n setForks([]);\n setCorrections([]);\n compactionPoll.current?.abort();\n void (async () => {\n const history = await loadHistory(controller.signal);\n if (controller.signal.aborted) return;\n if (history === \"unreachable\") {\n setConnection(\"offline\");\n return;\n }\n setConnection(\"online\");\n if (history === \"not_found\") {\n setNotFound(true);\n return;\n }\n // Вход истёк: надпись «Войдите снова» уже стоит, «Повторить» загрузит чат заново.\n if (history === \"signed_out\" || history === \"access_refused\") return;\n if (history === \"server_error\") {\n setLoadFailed(true);\n return;\n }\n setSavedModelChoice(modelChoiceOf(history));\n setLoadedChatId(chatId);\n setLoaded(true);\n if (history.compacting) pollCompactions();\n // Дальше — только показ истории; «идёт ответ» включается, если сервер ещё ведёт ход.\n if (history.running) setResuming(true);\n try {\n const connected = await applyServerHistory(history, [], controller.signal);\n if (!connected && !controller.signal.aborted) startSeries();\n } finally {\n if (!controller.signal.aborted) setResuming(false);\n }\n })();\n return () => controller.abort();\n }, [chatUrl, loadAttempt]);\n\n // Уход со страницы и смена чата обрывают попытки: ответ на сервере продолжает идти.\n useEffect(() => cancelSeries, [cancelSeries, chatId]);\n // Опрос записей сжатия тоже принадлежит одному чату.\n useEffect(() => () => compactionPoll.current?.abort(), [chatId]);\n\n /** Собеседник просит связь («Повторить», сеть вернулась): грузим чат или начинаем серию заново. */\n const reconnectNow = useCallback(() => {\n if (!loaded) {\n setConnection(\"reconnecting\");\n setLoadAttempt((attempt) => attempt + 1);\n return;\n }\n startSeries({ resendUnsent: true });\n }, [loaded, startSeries]);\n\n useEffect(() => {\n if (connection !== \"offline\") return;\n const onWake = () => {\n if (document.visibilityState !== \"hidden\") reconnectNow();\n };\n window.addEventListener(\"online\", onWake);\n document.addEventListener(\"visibilitychange\", onWake);\n return () => {\n window.removeEventListener(\"online\", onWake);\n document.removeEventListener(\"visibilitychange\", onWake);\n };\n }, [connection, reconnectNow]);\n\n const answering = status === \"submitted\" || status === \"streaming\";\n answeringRef.current = answering;\n const busy = resuming || seriesRunning || handingOff || connection === \"reconnecting\" || answering;\n // Голосовой режим зовёт `sendVoiceMessage` из долгоживущего движка: читать занятость надо в момент вызова, а не из замыкания.\n const busyRef = useRef(busy);\n busyRef.current = busy;\n const canSubmit = loaded && !busy && !exhausted && !closed && value.trim().length > 0;\n /** Поправку можно отправить: ответ идёт по живому потоку, связь есть. */\n const canSteer = steering && capabilities.steer && loaded && answering && !seriesRunning && connection === \"online\" && !exhausted && !closed;\n // Сервер окончательно отказал повтору своими словами: тот же повтор получит тот же отказ.\n const canRetry =\n loaded && !busy && !exhausted && !closed && capabilities.retry && connection === \"online\" && !serverRefusal?.final && isRetryableAnswer(shownMessages.at(-1));\n\n const chatLifetime = useRef(new AbortController());\n useEffect(() => {\n chatLifetime.current = new AbortController();\n return () => chatLifetime.current.abort();\n }, [chatId]);\n const [approvalPendingId, setApprovalPendingId] = useState<string | null>(null);\n const [approvalFailure, setApprovalFailure] = useState<{ id: string; kind: ApprovalFailureKind; text?: string } | null>(null);\n const approving = useRef(false);\n\n /** Номер последнего перечитывания истории и номер самого нового из показанных. */\n const historyReads = useRef({ started: 0, shown: 0 });\n\n /**\n * Перечитывает историю с сервера и показывает её; идущий ход подхватывается. `unlessFeedChanged` — для опроса: пока\n * шёл запрос, человек отправил вопрос или нажал «Повторить» — старая история не стирает новое. Ленту, сменённую другим\n * перечитыванием, менял сервер, а не человек: более новое чтение её показывает, а запоздавшее старое — нет.\n */\n const refreshFromServer = useCallback(\n async (signal: AbortSignal, options: { unlessFeedChanged?: boolean } = {}): Promise<boolean> => {\n const shown = messagesRef.current;\n const read = ++historyReads.current.started;\n const shownBefore = historyReads.current.shown;\n const history = await loadHistory(signal);\n if (signal.aborted) return true;\n if (options.unlessFeedChanged) {\n if (busyRef.current || historyReads.current.shown > read) return true;\n if (messagesRef.current !== shown && historyReads.current.shown === shownBefore) return true;\n }\n if (typeof history !== \"string\") historyReads.current.shown = Math.max(historyReads.current.shown, read);\n if (history === \"not_found\") {\n setNotFound(true);\n return true;\n }\n // Сервер не ответил или ответил сбоем: история не прочитана. Вход и отказ в доступе говорят своими надписями.\n if (history === \"unreachable\" || history === \"server_error\") return false;\n if (typeof history === \"string\") return true;\n const connected = await applyServerHistory(history, messagesRef.current, signal);\n if (!connected && !signal.aborted) startSeries();\n return true;\n },\n [applyServerHistory, loadHistory, startSeries],\n );\n\n /**\n * Сервер сказал, что ответа или вопроса уже нет (виджет не ждёт, решение уже принято, продолжения нет, хода нет):\n * лента сразу перечитывает историю. Чтение сорвалось (сервер под нагрузкой) — серия попыток, а не застывшая лента.\n */\n const rereadHistory = useCallback(\n async (signal: AbortSignal) => {\n const read = await refreshFromServer(signal);\n if (!read && !signal.aborted) startSeries({ quietFirst: true });\n },\n [refreshFromServer, startSeries],\n );\n\n browserCallGoneRef.current = () => {\n if (!busyRef.current) void rereadHistory(chatLifetime.current.signal);\n };\n\n const shownReloadKey = useRef(reloadKey);\n useEffect(() => {\n if (shownReloadKey.current === reloadKey) return;\n shownReloadKey.current = reloadKey;\n void refreshFromServer(chatLifetime.current.signal);\n }, [reloadKey, refreshFromServer]);\n\n // Ответ идёт без живого потока: история перечитывается, пока сервер не прервёт его. Сервер так и не ответил итогом —\n // ответ прерывается на экране, и человек может нажать «Повторить» без перезагрузки страницы.\n useEffect(() => {\n if (detachedSince === null || busy) return;\n const signal = chatLifetime.current.signal;\n const giveUpAt = detachedSince + DETACHED_ANSWER_GIVE_UP_MS;\n const timer = setTimeout(\n () => {\n if (Date.now() >= giveUpAt) {\n setDetachedSince(null);\n setMessages((current) => (isFinalStatus(lastAnswerStatus(current)) ? current : withLastAnswerStatus(current, \"interrupted\")));\n return;\n }\n void refreshFromServer(signal, { unlessFeedChanged: true }).finally(() => {\n if (!signal.aborted) setDetachedChecks((count) => count + 1);\n });\n },\n Math.min(DETACHED_ANSWER_CHECK_EVERY_MS, Math.max(0, giveUpAt - Date.now())),\n );\n return () => clearTimeout(timer);\n }, [busy, detachedChecks, detachedSince, refreshFromServer, setMessages]);\n // Другой чат — свои проверки: ожидание прежнего чата не переносится.\n useEffect(() => setDetachedSince(null), [chatId]);\n\n\n const respondToApproval = useCallback(\n async (approvalId: string, decision: ApprovalDecision) => {\n if (approving.current) return;\n approving.current = true;\n const signal = chatLifetime.current.signal;\n setApprovalFailure(null);\n setApprovalPendingId(approvalId);\n try {\n let response: Response;\n try {\n response = await fetchWithDeadline(\n request,\n `${chatUrl}/approvals/${encodeURIComponent(approvalId)}`,\n { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify({ decision, ...pageFields(bridgeRef.current) }) },\n { timeoutMs: settingsRef.current.requestTimeoutMs, wholeBody: true },\n );\n } catch {\n setApprovalFailure({ id: approvalId, kind: \"connection\" });\n return;\n }\n // Приложение не пустило ход (или человека): его слова у карточки, а не «нет связи».\n const refusedByApp = response.ok ? null : await serverRefusalOf(response);\n if (refusedByApp?.kind) {\n announceAppRefusal(refusedByApp);\n setApprovalFailure({ id: approvalId, kind: \"refused\", text: refusedByApp.text });\n return;\n }\n if (response.status === 429) {\n // Лимит исчерпан: запрос остаётся ждать, поле ввода блокируется, как при отказе на отправку.\n const refusal = await spendingRefusalOf(response);\n if (refusal && !signal.aborted) setSpending({ state: \"exhausted\", info: refusal });\n const tooManyTurns = !refusal && (await errorKindOf(response)) === \"too_many_turns\";\n setApprovalFailure({ id: approvalId, kind: refusal ? \"spending_limit\" : tooManyTurns ? \"too_many_turns\" : \"connection\" });\n return;\n }\n // Чат занят другим ходом: запрос жив, ответить можно чуть позже.\n if (response.status === 409 && (await errorKindOf(response)) === \"busy\") {\n setApprovalFailure({ id: approvalId, kind: \"busy\" });\n return;\n }\n // Запрос уже решён (на другом устройстве) или закрыт новым сообщением: показываем то, что на сервере.\n if (response.status === 409 || response.status === 404) {\n await rereadHistory(signal);\n return;\n }\n if (!response.ok) {\n setApprovalFailure({ id: approvalId, kind: \"connection\" });\n return;\n }\n const { resumed } = (await response.json()) as { resumed: boolean };\n setMessages((current) => withApprovalDecision(current, approvalId, decision, resumed));\n if (!resumed) return;\n setResuming(true);\n try {\n const outcome = await resumeWithProbe(signal);\n if (outcome === \"nothing\") await rereadHistory(signal);\n else if (outcome === \"failed\" && !signal.aborted) startSeries();\n } finally {\n if (!signal.aborted) setResuming(false);\n }\n } finally {\n approving.current = false;\n if (!signal.aborted) setApprovalPendingId(null);\n }\n },\n [announceAppRefusal, chatUrl, request, rereadHistory, resumeWithProbe, setMessages, startSeries],\n );\n\n const { answerWidget, widgetPendingId, widgetFailure, answeringWidget } = useWidgetAnswer({\n chatUrl,\n request,\n requestTimeoutMs: () => settingsRef.current.requestTimeoutMs,\n pageFields: () => pageFields(bridgeRef.current),\n chatSignal: () => chatLifetime.current.signal,\n setMessages,\n setResuming,\n resumeWithProbe,\n rereadHistory,\n expectLiveEventsOf: (toolCallId) => {\n const answer = messagesRef.current.find((message) => message.parts.some((part) => (part as { toolCallId?: unknown }).toolCallId === toolCallId));\n if (answer) onExpectLiveAppEvents?.(answer.id);\n },\n startSeries,\n announceAppRefusal,\n spendingRefusalOf,\n showSpendingExhausted: (info) => setSpending({ state: \"exhausted\", info }),\n errorKindOf,\n });\n\n const ownTurnStarting = useCallback(\n () => busyRef.current || sending.current || approving.current || answeringWidget(),\n [answeringWidget],\n );\n\n // Подписка на события чата, пока ответ не идёт: ход, начатый не из этой вкладки (фон, мессенджер, другая вкладка),\n // подхватывается тем же путём, что после перезагрузки; изменения задач поднимают `tasksVersion`. Пока подписка была\n // закрыта, события терялись: каждое открытие перечитывает историю. Ход, который начал свой запрос этой вкладки\n // (сообщение, ответ на подтверждение или виджет), подключает сам запрос: второе подключение дописало бы ответ заново.\n // При «Нет связи с сервером» к ходу подключает «Повторить», а не открытие подписки.\n const connectionRef = useRef(connection);\n connectionRef.current = connection;\n const [tasksVersion, setTasksVersion] = useState(0);\n // Состояние из события `state-changed` (сменила другая вкладка): верно, пока лента та же; новая лента — снова свёртка.\n const [watchedChatState, setWatchedChatState] = useState<{ values: ChatStateValues; over: AgentChatMessage[] } | null>(null);\n const shownMessagesRef = useRef(shownMessages);\n shownMessagesRef.current = shownMessages;\n const watchUnavailableFor = useRef<string | null>(null);\n const reportAppEvent = useRef(onAppEvent);\n reportAppEvent.current = onAppEvent;\n useEffect(() => {\n if (!loaded || busy || notFound || watchUnavailableFor.current === chatUrl) return;\n const chatSignal = chatLifetime.current.signal;\n const watch = new AbortController();\n const stopWatch = () => watch.abort();\n chatSignal.addEventListener(\"abort\", stopWatch, { once: true });\n void watchChat({\n url: `${chatUrl}/watch`,\n fetch: request,\n signal: watch.signal,\n delaysMs: settingsRef.current.reconnectDelaysMs,\n silenceTimeoutMs: settingsRef.current.silenceTimeoutMs,\n onOpen: () => {\n setTasksVersion((version) => version + 1);\n if (!ownTurnStarting() && connectionRef.current !== \"offline\") void refreshFromServer(chatSignal, { unlessFeedChanged: true });\n },\n onEvent: (event) => {\n if (event.type === \"tasks-changed\") setTasksVersion((version) => version + 1);\n else if (event.type === \"chat-closed\") showClosure(closureOf(event.closed));\n else if (event.type === \"chat-reopened\") showClosure(null);\n else if (event.type === \"state-changed\") {\n const values = chatStateOfEvent(event.state);\n if (values) setWatchedChatState({ values, over: shownMessagesRef.current });\n }\n else if (event.type === \"app-event\") {\n const appEvent = appEventOf(event.event);\n const messageId = typeof event.messageId === \"string\" ? event.messageId : undefined;\n if (appEvent) reportAppEvent.current?.(appEvent, messageId);\n // Событие хука после «Стопа» уже лежит в ответе: перечитанная история покажет его в ленте.\n if (appEvent && messageId && !ownTurnStarting()) void refreshFromServer(chatSignal, { unlessFeedChanged: true });\n } else if ((event.type === \"turn-started\" || event.type === \"notice-added\" || event.type === \"message-added\") && !ownTurnStarting()) {\n void refreshFromServer(chatSignal, { unlessFeedChanged: true });\n }\n },\n }).then((end) => {\n if (end === \"unavailable\") watchUnavailableFor.current = chatUrl;\n });\n return () => {\n chatSignal.removeEventListener(\"abort\", stopWatch);\n watch.abort();\n };\n }, [busy, chatUrl, loaded, notFound, ownTurnStarting, refreshFromServer, request, showClosure]);\n useEffect(() => setTasksVersion(0), [chatId]);\n\n /** Ответ, ждавший человека, закрывается новым сообщением: запомнить, каким он был, чтобы вернуть при отказе. */\n function closeAwaitingBySend() {\n const awaiting = messagesRef.current.at(-1);\n approvalsClosedBySubmit.current = awaiting?.metadata?.status === \"awaiting_approval\" ? awaiting : null;\n setMessages((current) => withPendingApprovalsDenied(withPendingWidgetsClosed(current)));\n }\n\n /** Сообщение не ушло (нет связи, 409, 400): запрос и виджет на сервере всё ещё ждут, ответ возвращается как был. */\n function restoreAwaitingAfterSend() {\n const closed = approvalsClosedBySubmit.current;\n if (!closed) return;\n approvalsClosedBySubmit.current = null;\n setMessages((current) => current.map((item) => (item.id === closed.id ? closed : item)));\n }\n\n /**\n * Сообщение человека с текстом и файлами. Вместо ответа на запрос или виджет: сервер закроет запрос как отклонённый,\n * а виджет — пометкой «ответ дан сообщением»; лента показывает то же сразу.\n */\n const sendTyped = useCallback(\n (message: { text?: string; files?: FileUIPart[] }, text: string, onRejected?: (kind: string | undefined) => void) => {\n sending.current = true;\n setSendFailed(false);\n // Новое сообщение вместо «Повторить»: не дошедшее больше не держится в ленте поверх истории сервера.\n unsentMessageId.current = null;\n closeAwaitingBySend();\n rejectedSend.current = null;\n void sendMessage(message as Parameters<typeof sendMessage>[0]).finally(() => {\n sending.current = false;\n restoreAwaitingAfterSend();\n const rejection = rejectedSend.current;\n if (!rejection) return;\n // Сервер не принял сообщение (например, файл устарел): в ленте его нет, текст и файлы возвращаются в поле.\n rejectedSend.current = null;\n setMessages((current) => current.filter((item) => item.id !== rejection.messageId));\n setValue((current) => (current.length === 0 ? text : current));\n onRejected?.(rejection.kind);\n });\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- closeAwaitingBySend и restoreAwaitingAfterSend трогают только ref и setMessages\n [sendMessage, setMessages],\n );\n\n /**\n * Ответ кончился, а поправки ждут: дождаться их запросов (и «Стопа», если он идёт), перечитать историю и подключиться\n * к ходу по ним. Пока это идёт, вкладка занята: обычная отправка не влезает между ответами.\n */\n const handOffToCorrections = useCallback(() => {\n if (handOff.current) return handOff.current;\n const signal = chatLifetime.current.signal;\n setHandingOff(true);\n const run = (async () => {\n await steerQueue.current;\n await stopping.current;\n if (!signal.aborted) await refreshFromServer(signal);\n })().finally(() => {\n handOff.current = null;\n if (!signal.aborted) setHandingOff(false);\n });\n handOff.current = run;\n return run;\n }, [refreshFromServer]);\n handOffToCorrectionsRef.current = handOffToCorrections;\n\n /**\n * Поправка во время ответа: сразу в ленте с пометкой, на сервер — по очереди. Отказ убирает её и возвращает текст в\n * поле. Сервер принял, а поток ответа уже кончился (или ход начат самой поправкой) — вкладка подключается к ходу.\n */\n const sendCorrection = useCallback(\n (text: string, files: readonly FileUIPart[], onRejected?: (kind: string | undefined) => void) => {\n const asked = chatUrl;\n const filesKey = JSON.stringify(files.map((file) => file.url));\n const unconfirmed = unconfirmedCorrection.current;\n const repeated = unconfirmed?.chatUrl === asked && unconfirmed.text === text && unconfirmed.files === filesKey;\n unconfirmedCorrection.current = null;\n const correction = correctionMessage(repeated ? unconfirmed.id : crypto.randomUUID(), text, files);\n const signal = chatLifetime.current.signal;\n sendingCorrections.current.add(correction.id);\n setCorrections((current) => [...current, correction]);\n const refused = (kind: string | undefined) => {\n setCorrections((current) => current.filter((item) => item.id !== correction.id));\n setValue((current) => (current.length === 0 ? text : current));\n onRejected?.(kind);\n };\n const sent = steerQueue.current.then(async () => {\n let response: Response;\n try {\n response = await fetchWithDeadline(\n request,\n `${asked}/steer`,\n { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify({ message: outgoingCorrection(correction), ...pageFields(bridgeRef.current) }) },\n { timeoutMs: settingsRef.current.requestTimeoutMs, wholeBody: true },\n );\n } catch {\n if (signal.aborted) return;\n unconfirmedCorrection.current = { chatUrl: asked, id: correction.id, text, files: filesKey };\n refused(\"connection\");\n return;\n } finally {\n sendingCorrections.current.delete(correction.id);\n }\n if (signal.aborted || chatUrlRef.current !== asked) return;\n if (!response.ok) {\n const spendingLimit = response.status === 429 ? await spendingRefusalOf(response) : null;\n if (spendingLimit) setSpending({ state: \"exhausted\", info: spendingLimit });\n const refusedWithText = await serverRefusalOf(response);\n announceAppRefusal(refusedWithText);\n refused(spendingLimit ? \"spending_limit\" : (refusedWithText?.kind ?? (await errorKindOf(response))));\n return;\n }\n if (!answeringRef.current) void handOffToCorrectionsRef.current();\n });\n steerQueue.current = sent.catch(() => undefined);\n },\n [announceAppRefusal, chatUrl, request],\n );\n\n const submit = useCallback((files?: readonly FileUIPart[], onRejected?: (kind: string | undefined) => void): boolean => {\n const hasFiles = Boolean(files && files.length > 0);\n if (canSteer && (hasFiles || value.trim().length > 0)) {\n const text = value;\n setValue(\"\");\n sendCorrection(text, files ?? [], onRejected);\n return true;\n }\n // Файлы уже загружены и лежат на сервере: их отправка не ждёт истории и не откладывается.\n if (!hasFiles && !loaded && !busy && value.trim().length > 0) {\n setSubmitWhenLoaded(true);\n return false;\n }\n if (sending.current || !loaded || busy || exhausted || closed || (!hasFiles && !canSubmit)) return false;\n const text = value;\n setValue(\"\");\n sendTyped({ ...(text.trim().length > 0 && { text }), ...(hasFiles && { files: [...files!] }) }, text, onRejected);\n return true;\n }, [busy, canSubmit, closed, exhausted, loaded, canSteer, sendCorrection, sendTyped, value]);\n\n const sendReply = useCallback(\n (text: string): boolean => {\n if (sending.current || !loaded || busyRef.current || exhausted || closed || text.trim().length === 0) return false;\n sendTyped({ text }, text);\n return true;\n },\n [closed, exhausted, loaded, sendTyped],\n );\n\n useEffect(() => {\n if (!loaded || !submitWhenLoaded) return;\n setSubmitWhenLoaded(false);\n submit();\n }, [loaded, submit, submitWhenLoaded]);\n\n const sendVoiceMessage = useCallback(\n (text: string, options?: { sounds?: readonly VoiceSound[] }): boolean => {\n if (sending.current || !loaded || busyRef.current || exhausted || closed || !capabilities.voice || text.trim().length === 0) return false;\n // Ответ ждёт подтверждения инструмента: слова не должны его отклонить, подтверждает только человек кнопкой.\n // Ждущий виджет слова закрывают, как сообщение из поля.\n if (hasPendingApproval(messagesRef.current)) return false;\n sending.current = true;\n setSendFailed(false);\n // Новое сообщение вместо «Повторить»: не дошедшее больше не держится в ленте поверх истории сервера.\n unsentMessageId.current = null;\n closeAwaitingBySend();\n rejectedSend.current = null;\n const id = crypto.randomUUID();\n voiceMessageIds.current.add(id);\n if (options?.sounds?.length) voiceSoundsById.current.set(id, options.sounds);\n // Отметка стоит в самом сообщении, как у сохранённого: по ней экран узнаёт голосовой ход и прячет теги в ответе.\n void sendMessage({ id, role: \"user\", parts: [{ type: \"text\", text }, { type: VOICE_MODE_PART_TYPE, data: true }] }).finally(() => {\n sending.current = false;\n restoreAwaitingAfterSend();\n const rejection = rejectedSend.current;\n if (!rejection) return;\n // Сервер не принял сообщение: возвращать в поле нечего, в ленте его нет.\n rejectedSend.current = null;\n voiceMessageIds.current.delete(rejection.messageId ?? id);\n voiceSoundsById.current.delete(rejection.messageId ?? id);\n setMessages((current) => current.filter((item) => item.id !== rejection.messageId));\n });\n return true;\n },\n [capabilities.voice, closed, exhausted, loaded, sendMessage, setMessages],\n );\n\n /*\n * Отметки обрыва озвучки лежат отдельно от сообщений и накладываются при выдаче: пока остановка доходит до сервера, идущий поток\n * ещё дописывает ответ и каждым куском заменяет сообщение своей копией, без отметки. Отметка в самом сообщении от этого пропадала.\n */\n const [voiceCuts, setVoiceCuts] = useState<{ chatUrl: string; byMessageId: Record<string, VoiceCut> }>({ chatUrl, byMessageId: {} });\n const showVoiceCut = useCallback(\n (messageId: string, cut: VoiceCut) => {\n setVoiceCuts((current) => ({ chatUrl, byMessageId: { ...(current.chatUrl === chatUrl ? current.byMessageId : {}), [messageId]: cut } }));\n },\n [chatUrl],\n );\n const messages = useMemo(() => {\n const cuts = voiceCuts.chatUrl === chatUrl ? voiceCuts.byMessageId : {};\n if (Object.keys(cuts).length === 0) return shownMessages;\n return shownMessages.map((message) => {\n const cut = cuts[message.id];\n return cut && message.role === \"assistant\" && !message.metadata?.voiceCut ? { ...message, metadata: { ...message.metadata, voiceCut: cut } } : message;\n });\n }, [shownMessages, chatUrl, voiceCuts]);\n // То же состояние — тот же объект: экран не перерисовывается, `onStateChange` не зовётся повторно.\n const lastChatState = useRef<ChatStateValues>(foldChatState([]));\n const chatState = useMemo(() => {\n const next = watchedChatState?.over === shownMessages ? watchedChatState.values : foldChatState(shownMessages);\n if (!sameChatState(lastChatState.current, next)) lastChatState.current = next;\n return lastChatState.current;\n }, [shownMessages, watchedChatState]);\n\n /*\n * Состояние загруженного чата — панелям страницы (`useChatState(chatId)`): один источник, без своих запросов. Только\n * состояние, загруженное именно для этого `chatId`: после смены чата без `key` флаг `loaded` ещё один проход остаётся\n * от прежнего чата, а пока история нового грузится, панель видит пусто.\n */\n const publishedState = useRef<ReturnType<typeof publishChatState> | null>(null);\n const loadedChatState = loaded && loadedChatId === chatId ? chatState : null;\n const chatStateRef = useRef(loadedChatState);\n chatStateRef.current = loadedChatState;\n const published = loadedChatState !== null;\n useEffect(() => {\n if (!published || chatStateRef.current === null) return;\n const publication = publishChatState(chatId, chatStateRef.current);\n publishedState.current = publication;\n return () => {\n publishedState.current = null;\n publication.remove();\n };\n }, [chatId, published]);\n useEffect(() => {\n if (loadedChatState !== null) publishedState.current?.update(loadedChatState);\n }, [loadedChatState]);\n\n /** Идущий «стоп»: повторное нажатие ждёт его же, второго запроса не будет. */\n const stopping = useRef<Promise<void> | null>(null);\n const stop = useCallback(() => {\n if (stopping.current) return stopping.current;\n // Попытки переподключения отменяются: собеседник сказал «стоп», ждать связи не нужно.\n if (seriesRef.current) cancelSeries();\n browserCallsLifetime.current?.abort();\n browserCallsLifetime.current = null;\n const stopped = (async () => {\n await fetchWithDeadline(request, `${chatUrl}/stop`, { method: \"POST\" }, {\n timeoutMs: settingsRef.current.requestTimeoutMs,\n wholeBody: true,\n }).catch(() => undefined);\n await stopChat();\n setMessages((messages) => markLastAnswerStopped(messages));\n })().finally(() => {\n stopping.current = null;\n });\n stopping.current = stopped;\n return stopped;\n }, [cancelSeries, chatUrl, request, setMessages, stopChat]);\n\n const retry = useCallback(() => {\n if (connection === \"offline\") {\n reconnectNow();\n return;\n }\n if (loadFailed || accessRefusal || (refusal === \"signed_out\" && !loaded)) {\n setLoadAttempt((attempt) => attempt + 1);\n return;\n }\n if (refusal === \"signed_out\" && !busy) {\n // Человек вошёл снова: лента — с сервера; если вход всё ещё истёк, надпись останется.\n void refreshFromServer(chatLifetime.current.signal);\n return;\n }\n if (!loaded || busy || !(sendFailed || canRetry)) return;\n resend();\n }, [accessRefusal, busy, canRetry, connection, loadFailed, loaded, reconnectNow, refreshFromServer, refusal, resend, sendFailed]);\n\n const canRegenerate =\n loaded && !busy && !exhausted && capabilities.regenerate && connection === \"online\" && !sendFailed && isRegenerableAnswer(shownMessages.at(-1)) && shownMessages.some(isHumanMessage);\n const canEdit = loaded && !busy && !exhausted && capabilities.edit && connection === \"online\";\n\n const regenerateAnswer = useCallback(() => {\n const last = messagesRef.current.at(-1);\n if (!canRegenerate || last?.role !== \"assistant\" || sending.current) return;\n regeneratedAnswerId.current = last.id;\n forksMayChange.current = true;\n resend();\n }, [canRegenerate, resend]);\n\n /** Отправка правки: сообщение с новым id, прежнее и всё после него уходят с экрана (соседняя ветка). Проверки занятости — у вызывающего. */\n const startEdit = useCallback(\n (messageId: string, text: string, voice: boolean, sounds?: readonly VoiceSound[]): Promise<EditOutcome> => {\n const original = messagesRef.current.find((message) => message.id === messageId);\n const index = messagesRef.current.findIndex((message) => message.id === messageId);\n // Файлы сообщения при правке остаются как были; без них пустой текст — не сообщение.\n const files = original?.parts.filter((part): part is FileUIPart => part.type === \"file\") ?? [];\n const hasText = text.trim().length > 0;\n sending.current = true;\n const before = messagesRef.current;\n return new Promise<EditOutcome>((resolve) => {\n const edited: PendingEdit = {\n messageId: crypto.randomUUID(),\n editedMessageId: messageId,\n refusal: null,\n answered: (refusal) => {\n if (!refusal) resolve({ status: \"started\" });\n },\n };\n pendingEdit.current = edited;\n forksMayChange.current = true;\n if (voice) voiceMessageIds.current.add(edited.messageId);\n if (voice && sounds?.length) voiceSoundsById.current.set(edited.messageId, sounds);\n setSendFailed(false);\n // Прежнее сообщение и всё после него уходят с экрана: это соседняя ветка, она вернётся переключателем.\n setMessages(before.slice(0, index));\n const parts = [...files, ...(hasText ? [{ type: \"text\" as const, text }] : []), ...(voice ? [{ type: VOICE_MODE_PART_TYPE, data: true } as const] : [])];\n void sendMessage({ id: edited.messageId, role: \"user\", parts }).finally(() => {\n sending.current = false;\n if (edited.refusal) {\n // Сервер отказал: всё возвращается как было, ничего не отправлено.\n if (pendingEdit.current === edited) pendingEdit.current = null;\n forksMayChange.current = false;\n voiceMessageIds.current.delete(edited.messageId);\n voiceSoundsById.current.delete(edited.messageId);\n setMessages(before);\n resolve({\n status: \"refused\",\n reason: edited.refusal,\n ...(edited.refusalText && { text: edited.refusalText }),\n ...(edited.refusalCode && { code: edited.refusalCode }),\n ...(edited.refusalUnit && { unit: edited.refusalUnit }),\n });\n return;\n }\n // Запрос ушёл, но ответа не было (обрыв связи): дальше работает серия попыток и «Повторить».\n resolve({ status: \"started\" });\n });\n });\n },\n [sendMessage, setMessages],\n );\n\n const edit = useCallback(\n async (messageId: string, text: string): Promise<EditOutcome> => {\n if (sending.current || busy) return { status: \"not_started\", reason: \"busy\" };\n if (!canEdit) return { status: \"not_started\", reason: \"offline\" };\n const original = messagesRef.current.find((message) => message.id === messageId);\n if (!original || original.role !== \"user\") return { status: \"not_started\", reason: \"unknown_message\" };\n const hasFiles = original.parts.some((part) => part.type === \"file\");\n if (text.trim().length === 0 && !hasFiles) return { status: \"not_started\", reason: \"empty\" };\n return startEdit(messageId, text, false);\n },\n [busy, canEdit, startEdit],\n );\n\n /** Очередь замен: вторая начинается, когда первая закончена. */\n const replacing = useRef<Promise<unknown>>(Promise.resolve());\n\n const replaceLastVoiceMessage = useCallback(\n (text: string, options?: { sounds?: readonly VoiceSound[] }): Promise<boolean> => {\n const run = replacing.current.then(async (): Promise<boolean> => {\n const signal = chatLifetime.current.signal;\n const messages = messagesRef.current;\n const index = messages.findLastIndex((message) => message.role === \"user\");\n const target = messages[index];\n if (!loaded || exhausted || !target || !voiceMessageIds.current.has(target.id) || text.trim().length === 0) return false;\n // Заменять можно только то, что для человека и модели ничего не значит: ответа нет или он из одного размышления.\n if (!messages.slice(index + 1).every(isEmptyAnswer)) return false;\n const original = messages;\n // Экран не ждёт остановки: склеенный текст виден сразу, «Так.» и пустой ответ не мелькают.\n setMessages([...messages.slice(0, index), { ...target, parts: [{ type: \"text\", text }, { type: VOICE_MODE_PART_TYPE, data: true }] }]);\n await stop();\n if (!(await untilIdle(signal))) return restoreAfterFailure(original);\n for (let attempt = 0; attempt < REPLACE_ATTEMPTS; attempt++) {\n const outcome = await startEdit(target.id, text, true, options?.sounds);\n if (outcome.status === \"started\") return true;\n // Остановка на сервере ещё доделывается: ход чата занят. Ждём и пробуем снова.\n if (!(outcome.status === \"refused\" && outcome.reason === \"busy\")) break;\n await pause(REPLACE_RETRY_MS, signal);\n }\n return restoreAfterFailure(original);\n });\n replacing.current = run.catch(() => false);\n return run;\n },\n [exhausted, loaded, refreshFromServer, setMessages, startEdit, stop],\n );\n\n /** Склейка не вышла: на экране то, что хранит сервер, а не наша догадка. */\n function restoreAfterFailure(original: AgentChatMessage[]): boolean {\n setMessages(original);\n void refreshFromServer(chatLifetime.current.signal);\n return false;\n }\n\n /** Ждёт, пока чат освободится после остановки (отрисовка с `busy: false` уже была). `false` — не дождались. */\n async function untilIdle(signal: AbortSignal): Promise<boolean> {\n for (let waited = 0; waited <= IDLE_WAIT_MS; waited += IDLE_POLL_MS) {\n if (signal.aborted) return false;\n if (!busyRef.current && !sending.current) return true;\n await pause(IDLE_POLL_MS, signal);\n }\n return false;\n }\n\n /** Переключение ветки: сервер записывает выбор и отдаёт историю новой ветки; на отказ — свежая история. */\n const selectBranch = useCallback(\n async (messageId: string): Promise<boolean> => {\n if (!loaded || busy || connection !== \"online\" || !capabilities.branches) return false;\n const applyHistory = (history: ServerHistory) => {\n applyAccess(history);\n setMessages(history.messages);\n setCompactions(history.compactions ?? []);\n setForks(history.forks ?? []);\n };\n try {\n const response = await fetchWithDeadline(\n request,\n `${chatUrl}/branch`,\n { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify({ messageId }) },\n { timeoutMs: settingsRef.current.requestTimeoutMs, wholeBody: true },\n );\n if (chatUrlRef.current !== chatUrl) return false;\n if (response.ok) {\n applyHistory((await response.json()) as ServerHistory);\n return true;\n }\n // Чат изменился с другой вкладки или ответ начался: показываем то, что есть на сервере.\n const fresh = await loadHistory();\n if (typeof fresh !== \"string\" && chatUrlRef.current === chatUrl) applyHistory(fresh);\n return false;\n } catch {\n // Связь пропала: экран остаётся как был, следующее действие покажет «Нет связи».\n return false;\n }\n },\n [applyAccess, busy, capabilities.branches, chatUrl, connection, loadHistory, loaded, request, setMessages],\n );\n\n return {\n messages,\n compactions,\n forks,\n loaded,\n value,\n setValue,\n canSubmit,\n canSteer,\n corrections,\n busy,\n submit,\n stop,\n sendVoiceMessage,\n showVoiceCut,\n replaceLastVoiceMessage,\n canRetry,\n sendFailed,\n loadFailed,\n notFound,\n savedModelChoice,\n modelAcceptsImages,\n externalContent,\n connection,\n retry,\n selectBranch,\n canRegenerate,\n regenerate: regenerateAnswer,\n canEdit,\n edit,\n respondToApproval,\n approvalPendingId,\n approvalFailure,\n answerWidget,\n widgetPendingId,\n widgetFailure,\n sendReply,\n spending,\n refusal,\n accessRefusal,\n serverRefusal,\n tasksVersion,\n backgroundTasksAvailable,\n capabilities,\n closed,\n chatState,\n };\n}\n\ninterface ApprovalToolPart {\n type: string;\n state?: string;\n approval?: { id: string; approved?: boolean; reason?: string };\n}\n\n/*\n * ВНИМАНИЕ: приём с `input-streaming`. Продолжение хода после подтверждения дописывается в то же сообщение, но\n * `useChat.resumeStream` из `@ai-sdk/react` подхватывает готовое сообщение только тогда, когда в нём есть вызов\n * инструмента в состоянии `input-streaming`; иначе он начинает новое сообщение и прежние части пропадают из ленты.\n * Поэтому части с принятым решением, пока идёт продолжение, помечаются `input-streaming` (`withApprovalDecision`,\n * `withContinuationMarked`), а как только приходит результат, поток сам переводит их в `output-available` / `output-denied`.\n * Это поведение SDK не описано в его документации: версии `ai` и `@ai-sdk/react` закреплены точно, менять их только\n * вместе с прогоном tests/agent-chat-approval.test.tsx. Оборванный ход не оставляет вызов «в пути»: `hasStalledContinuation`.\n */\n\n/** Часть с решением человека; пока ход не продолжился, вызов остаётся без результата. */\nfunction withApprovalDecision(\n messages: AgentChatMessage[],\n approvalId: string,\n decision: ApprovalDecision,\n resumed: boolean,\n): AgentChatMessage[] {\n const decidedHere = (part: AgentChatMessage[\"parts\"][number]) =>\n (part as ApprovalToolPart).approval?.id === approvalId || pendingHelperApprovalId(part as { output?: unknown }) === approvalId;\n return messages.map((message) => {\n if (message.role !== \"assistant\" || !message.parts.some(decidedHere)) return message;\n const parts = message.parts.map((part) => {\n if (!decidedHere(part)) return part;\n const helper = helperProgressOf(part as { output?: unknown });\n if (helper) {\n // Подтверждение внутри помощника: вызов помощника ждёт продолжения, решение видно в его строке.\n const { pendingApproval: _answered, ...rest } = helper;\n const decided = decision === \"deny\" ? \"denied\" : decision === \"always\" ? \"always\" : \"allowed\";\n return { ...part, ...(resumed && { state: \"input-streaming\" }), output: { ...rest, status: \"running\", decided } } as typeof part;\n }\n return {\n ...part,\n // Продолжение хода дописывает то же сообщение: SDK так делает, только если в нём есть вызов «в пути».\n state: resumed ? \"input-streaming\" : \"approval-responded\",\n approval: {\n id: approvalId,\n approved: decision !== \"deny\",\n ...(decision === \"always\" && { reason: ALWAYS_IN_CHAT }),\n },\n } as typeof part;\n });\n return { ...message, parts, ...(resumed && { metadata: { ...message.metadata, status: \"streaming\" as const } }) };\n });\n}\n\n/** Вызов после решения всё ещё помечен «в пути» (`input-streaming`), хотя поток кончился. */\nfunction hasStalledContinuation(messages: AgentChatMessage[]): boolean {\n const last = messages.at(-1);\n return (\n last?.role === \"assistant\" &&\n last.parts.some(\n (part) =>\n (part as ApprovalToolPart).state === \"input-streaming\" &&\n (Boolean((part as ApprovalToolPart).approval) || helperProgressOf(part as { output?: unknown })?.decided !== undefined),\n )\n );\n}\n\n/**\n * Ответ, у которого решения уже получены и ход продолжается: сообщение уже есть в ленте,\n * поток дописывает его. SDK подхватывает готовое сообщение, только если в нём есть вызов «в пути».\n */\nfunction withContinuationMarked(messages: AgentChatMessage[]): AgentChatMessage[] {\n const last = messages.at(-1);\n if (last?.role !== \"assistant\" || !last.parts.some(continuesInStream)) return messages;\n const parts = last.parts.map((part) => (continuesInStream(part) ? ({ ...part, state: \"input-streaming\" } as typeof part) : part));\n return [...messages.slice(0, -1), { ...last, parts }];\n}\n\n/** Вызов, который допишет продолжение хода: решение по подтверждению получено или помощник ещё работает. */\nfunction continuesInStream(part: AgentChatMessage[\"parts\"][number]): boolean {\n const { state } = part as ApprovalToolPart;\n if (state === \"approval-responded\") return true;\n const helper = helperProgressOf(part as { output?: unknown });\n return helper !== undefined && (part as { preliminary?: boolean }).preliminary === true && helper.status === \"running\";\n}\n\n/** Все запросы последнего ответа без решения показаны отклонёнными: человек написал новое сообщение. */\nfunction withPendingApprovalsDenied(messages: AgentChatMessage[]): AgentChatMessage[] {\n const last = messages.at(-1);\n if (last?.role !== \"assistant\" || last.metadata?.status !== \"awaiting_approval\") return messages;\n const parts = last.parts.map((part) => {\n if ((part as ApprovalToolPart).state === \"approval-requested\" || (part as ApprovalToolPart).state === \"approval-responded\") {\n return { ...part, state: \"output-denied\", approval: { id: (part as ApprovalToolPart).approval!.id, approved: false } } as typeof part;\n }\n const helper = pendingHelperApprovalId(part as { output?: unknown }) ? helperProgressOf(part as { output?: unknown }) : undefined;\n if (!helper) return part;\n const { pendingApproval: _closed, ...rest } = helper;\n return { ...part, output: { ...rest, status: \"failed\", decided: \"denied\" } } as typeof part;\n });\n return [...messages.slice(0, -1), { ...last, parts, metadata: { ...last.metadata, status: \"complete\" as const } }];\n}\n\n/** Сервер чата не признал страницу своей: 403 и `forbidden_origin` в теле. */\nasync function isForbiddenOrigin(response: Response): Promise<boolean> {\n return response.status === 403 && (await errorKindOf(response)) === \"forbidden_origin\";\n}\n\n/** Сборщик приложения подставляет `process.env.NODE_ENV`; нет ни сборщика, ни `process` — подсказку не показываем. */\nfunction isDevelopment(): boolean {\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\n/** Подсказка разработчику в консоли: адрес страницы, который сервер не признал. */\nfunction forbiddenOriginHint(): string {\n const origin = typeof location === \"undefined\" ? \"https://your-frontend.example\" : location.origin;\n return (\n `Agent Chat: the chat server refused a write from ${origin} (403 forbidden_origin). ` +\n `If the frontend and the API live on different origins, add this origin to the server's allowedOrigins, ` +\n `e.g. createChatHandler({ allowedOrigins: [\"${origin}\"] }) or a subdomain pattern like \"https://*.example.com\". ` +\n `Behind a proxy, also pass Host / X-Forwarded-Host through to the server.`\n );\n}\n\n/** Тело отказа 429 «лимит исчерпан»; `null` — это другой отказ, с ним разбирается обычная обработка ошибок. */\nasync function errorKindOf(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.clone().json()) as { error?: { kind?: unknown } };\n return typeof body.error?.kind === \"string\" ? body.error.kind : undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function spendingRefusalOf(response: Response): Promise<SpendingInfo | null> {\n try {\n const body = (await response.clone().json()) as { error?: { kind?: string } & Record<string, unknown> };\n return body.error?.kind === \"spending_limit_reached\" ? spendingInfoOf(body.error as Parameters<typeof spendingInfoOf>[0]) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Сообщение человека: не служебное сообщение сервера и не запись состояния. Ответ, перед которым такого нет (приветствие),\n * переделать нельзя — сервер откажет. Держать в синхроне с `isHumanMessage` в core (`agent-run/channel-notes.ts`):\n * пакет react от core не зависит.\n */\nfunction isHumanMessage(message: AgentChatMessage): boolean {\n return (\n message.role === \"user\" &&\n !message.parts.some((part) => part.type === AGENT_NOTICE_PART_TYPE) &&\n !(message.parts.length > 0 && message.parts.every((part) => part.type === APP_STATE_PART_TYPE))\n );\n}\n\n/**\n * Ответ, который можно переделать кнопкой «Переделать»: законченный и непустой. Оборванный — тоже, если «Повторить»\n * продолжит его с вызовов инструментов: тогда «Переделать» — способ начать с чистого листа. Без вызовов «Повторить»\n * и так пишет заново, вторая кнопка не нужна.\n */\nfunction isRegenerableAnswer(message: AgentChatMessage | undefined): boolean {\n if (message?.role !== \"assistant\") return false;\n const status = message.metadata?.status;\n if (status === \"error\" || status === \"stopped\" || status === \"interrupted\") return message.parts.some(isContinuedToolCall);\n return status === \"complete\" && !isRetryableAnswer(message);\n}\n\n/** Вызов, с которого сервер продолжит оборванный ответ по «Повторить»: запущенный или с итогом (`partsForContinuation`). */\nfunction isContinuedToolCall(part: AgentChatMessage[\"parts\"][number]): boolean {\n if (!part.type.startsWith(\"tool-\") && part.type !== \"dynamic-tool\") return false;\n const { state } = part as { state?: string };\n return state === \"input-available\" || state === \"output-available\" || state === \"output-error\" || state === \"output-denied\";\n}\n\nfunction modelChoiceOf({ chat }: ServerHistory): ChatModelChoice | null {\n if (typeof chat?.modelId !== \"string\" || typeof chat.providerId !== \"string\") return null;\n return { modelId: chat.modelId, providerId: chat.providerId, ...(typeof chat.reasoning === \"string\" && { reasoning: chat.reasoning }) };\n}\n\n/** Итог ответа уже известен: всё, кроме «идёт». */\nconst isFinalStatus = (status: AnswerStatus | undefined) => status !== undefined && status !== \"streaming\";\n\nfunction lastAnswerStatus(messages: readonly AgentChatMessage[]): AnswerStatus | undefined {\n const last = messages.at(-1);\n return last?.role === \"assistant\" ? last.metadata?.status : undefined;\n}\n\n/** Та же мерка, что у сервера: ошибка, стоп, прерывание или ответ без текста. */\nfunction isRetryableAnswer(message: AgentChatMessage | undefined): boolean {\n if (message?.role !== \"assistant\") return false;\n const status = message.metadata?.status ?? \"complete\";\n if (status === \"error\" || status === \"stopped\" || status === \"interrupted\") return true;\n // По сырому тексту, как на сервере: ответ из одних тегов голоса — не пустой.\n return status === \"complete\" && messageText(message).trim().length === 0;\n}\n\nfunction withLastAnswerStatus(messages: AgentChatMessage[], status: AnswerStatus): AgentChatMessage[] {\n const last = messages.at(-1);\n if (last?.role !== \"assistant\") return messages;\n return [...messages.slice(0, -1), { ...last, metadata: { ...last.metadata, status } }];\n}\n\n/** «Стоп»: ответ помечен остановленным, а вызовы без результата закрыты так же, как их закроет сервер. */\nfunction stoppedAnswer(messages: AgentChatMessage[]): AgentChatMessage[] {\n const stopped = withLastAnswerStatus(messages, \"stopped\");\n const last = stopped.at(-1);\n return last?.role === \"assistant\" ? [...stopped.slice(0, -1), { ...last, parts: closeStoppedToolCalls(last.parts) }] : stopped;\n}\n\n/** Ответа нет или он из одного размышления: ни слов, ни вызовов инструментов, стирать нечего. */\nfunction isEmptyAnswer(message: AgentChatMessage): boolean {\n return (\n message.role === \"assistant\" &&\n message.parts.every((part) => part.type === \"step-start\" || part.type === \"reasoning\" || (part.type === \"text\" && part.text.trim().length === 0))\n );\n}\n\n/** Итог «stopped» мог прийти раньше «Стопа» на экране, а закрывающих кусков вызовов в потоке нет: их закрываем и тогда. */\nfunction markLastAnswerStopped(messages: AgentChatMessage[]): AgentChatMessage[] {\n const status = lastAnswerStatus(messages);\n return isFinalStatus(status) && status !== \"stopped\" ? messages : stoppedAnswer(messages);\n}\n","import type { UIMessage } from \"ai\";\nimport { stripVoiceTags } from \"./voice-tags\";\n\n/*\n * Клиентские копии типов ядра. Ядро — серверный пакет с провайдерами моделей,\n * браузерной сборке оно не нужно; форма совпадает с `@ykurilov/agent-chat-core`.\n */\n\n/** `awaiting_approval` — агент ждёт, разрешит ли человек вызов инструмента. */\nexport type AnswerStatus = \"streaming\" | \"complete\" | \"stopped\" | \"error\" | \"interrupted\" | \"awaiting_approval\";\n\nexport type ChatErrorKind = \"overloaded\" | \"rate_limited\" | \"insufficient_funds\" | \"invalid_key\" | \"context_overflow\" | \"other\";\n\n/**\n * Подробности сбоя провайдера: приходят, только если сервер открыл их этому человеку (право `errorDetails` ядра, обычно\n * для админа). `reason` — одна строка до 300 знаков без секретов, `resetsAt` — когда провайдер снимет предел (ISO).\n */\nexport interface ChatErrorDetails {\n providerId: string;\n modelId: string;\n statusCode?: number;\n reason?: string;\n resetsAt?: string;\n}\n\n/** Сбой ответа: вид и, если сервер их отдал, подробности. */\nexport interface ChatError {\n kind: ChatErrorKind;\n details?: ChatErrorDetails;\n}\n\n/**\n * Что показывает `ErrorNotice`: ошибка ответа или отказ сервера чату целиком. `signed_out` — вход истёк (401),\n * `too_many_turns` — у человека уже идут ответы в других чатах (429), ход не начат; `forbidden_origin` — сервер\n * не признал страницу своей (403): фронт и API на разных адресах, а адреса фронта нет в `allowedOrigins` сервера.\n */\nexport type ErrorNoticeKind = ChatErrorKind | \"signed_out\" | \"too_many_turns\" | \"forbidden_origin\";\n\n/** Уровень рассуждений модели (как в профиле модели ядра). */\nexport type ReasoningLevelId = \"off\" | \"on\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"max\";\n\n/** Решение человека по запросу подтверждения. `always` — разрешить и больше не спрашивать про этот инструмент в этом чате. */\nexport type ApprovalDecision = \"allow\" | \"deny\" | \"always\";\n\n/** Метка сервера в решении «Всегда в этом чате»; часть протокола, слово не менять. */\nexport const ALWAYS_IN_CHAT = \"always-in-chat\";\n\n/** Метка сервера в запросе (`approval.requestReason`): для этого вызова «Всегда в этом чате» предложена; часть протокола, слово не менять. */\nexport const ALWAYS_OFFERED = \"always-offered\";\n\n/**\n * Почему ответ на запрос не принят: нет связи или сбой, лимит расходов, чат занят другим ходом, идут ответы в других чатах,\n * приложение не пустило ход (`refused`: причина словами приложения лежит в `text` рядом с видом).\n */\nexport type ApprovalFailureKind = \"connection\" | \"spending_limit\" | \"busy\" | \"too_many_turns\" | \"refused\";\n\n/** Выбор собеседника, каким его хранит и отдаёт сервер. */\nexport interface ChatModelChoice {\n modelId: string;\n providerId: string;\n reasoning?: string;\n}\n\nexport interface TokenUsage {\n inputTokens: number;\n cachedInputTokens: number;\n outputTokens: number;\n reasoningTokens: number;\n /** Размер контекста после ответа: вход плюс выход последнего шага. */\n contextTokens?: number;\n /** Есть, если числа «примерно»: прикинуты по длине текста, а не сообщены провайдером. */\n estimated?: true;\n}\n\n/** Вид звука человека (смех, вздох…); копия закрытого списка ядра: ядро браузеру не нужно. */\nexport type VoiceSound = \"laughter\" | \"sigh\" | \"cough\" | \"sneeze\" | \"gasp\" | \"crying\" | \"yawn\" | \"groan\";\n\n/** Отметка обрыва озвучки: человек перебил голосом. `partIndex`/`charOffset` — где оборвалось в тексте, `spokenTail` — хвост озвученного. */\nexport interface VoiceCut {\n partIndex: number;\n charOffset: number;\n spokenTail: string;\n}\n\nexport interface ChatMessageMetadata {\n reasoningLevel?: string;\n modelId?: string;\n providerId?: string;\n usage?: TokenUsage;\n /** Длительность рассуждений по порядковому номеру части рассуждения в сообщении. */\n reasoningDurationsMs?: Record<string, number>;\n /** Заголовки раздумий по тому же номеру («Продумываю правила игры»): есть, если сервер включил `thoughtTitles`. */\n thoughtTitles?: Record<string, string>;\n status?: AnswerStatus;\n error?: ChatError;\n /** Есть, если озвучку ответа оборвало перебивание. */\n voiceCut?: VoiceCut;\n /** Ответ не дошёл до человека (сервер пометил «не доставлено»): модель его не видит. */\n undelivered?: true;\n}\n\nexport type AgentChatMessage = UIMessage<ChatMessageMetadata>;\n\n/** Запись сжатия истории: сводка заменила для модели сообщения с `fromMessageId` по `throughMessageId`. */\nexport interface CompactionMarker {\n id: string;\n fromMessageId: string;\n throughMessageId: string;\n /** Сводка в Markdown. */\n summary: string;\n summaryTokens: number;\n createdAt: string;\n}\n\n/** Развилка на текущей ветке: сообщение и все его соседи по порядку создания, включая его самого. */\nexport interface BranchFork {\n messageId: string;\n siblingIds: string[];\n}\n\n/**\n * Текст сообщения из частей: абзацы через пустую строку. У ответа голосового хода (`voiceAnswer`) теги голоса вырезаны:\n * человек видит, копирует и слышит диктором только слова. В остальных ответах скобки — обычный текст.\n */\nexport function messageText(message: AgentChatMessage, { voiceAnswer = false }: { voiceAnswer?: boolean } = {}): string {\n const texts = message.parts.flatMap((part) => (part.type === \"text\" ? [part.text] : []));\n return (voiceAnswer && message.role === \"assistant\" ? texts.map((text) => stripVoiceTags(text)) : texts).join(\"\\n\\n\");\n}\n","/**\n * Свои ошибки связи. Имя у них не `TypeError` и не `AbortError`: SDK чата принимает сетевой\n * `TypeError` за «обрыв, можно продолжить с места» и удвоит части ответа, а `AbortError` — за «стоп».\n */\n\n/** Сеть отказала, соединение оборвалось или вышел срок запроса. */\nexport class ConnectionLostError extends Error {\n constructor(message = \"Connection to the server lost\", options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ConnectionLostError\";\n }\n}\n\n/** Сервер слишком долго не присылал ни байта. */\nexport class ConnectionSilentError extends Error {\n constructor(silenceMs?: number) {\n super(silenceMs === undefined ? \"Server is silent\" : `Server has been silent for ${silenceMs} ms`);\n this.name = \"ConnectionSilentError\";\n }\n}\n\nexport function isConnectionError(error: unknown): error is ConnectionLostError | ConnectionSilentError {\n return error instanceof ConnectionLostError || error instanceof ConnectionSilentError;\n}\n","export const DEFAULT_RECONNECT_DELAYS_MS: readonly number[] = [0, 2000, 5000];\n\nexport type ReconnectOutcome = \"connected\" | \"exhausted\" | \"cancelled\";\n\nexport interface ReconnectAttemptsOptions {\n /** Пауза перед каждой попыткой; сколько пауз — столько попыток. */\n delaysMs: readonly number[];\n /** Срок попытки целиком: история, заголовки потока и первый байт. */\n attemptTimeoutMs: number;\n /** Одна попытка: `true` — подключились. Её сигнал отменяется по сроку и при отмене серии. */\n attempt: (signal: AbortSignal, index: number) => Promise<boolean>;\n /** Отмена всей серии («стоп», уход со страницы). */\n signal: AbortSignal;\n}\n\n/** Серия попыток переподключения с паузами и общим сроком на каждую попытку (research R10). */\nexport async function runReconnectAttempts(options: ReconnectAttemptsOptions): Promise<ReconnectOutcome> {\n const { delaysMs, attemptTimeoutMs, attempt, signal } = options;\n for (let index = 0; index < delaysMs.length; index++) {\n if (!(await sleep(delaysMs[index] ?? 0, signal))) return \"cancelled\";\n const outcome = await runOne(index);\n if (outcome === \"cancelled\") return \"cancelled\";\n if (outcome === \"connected\") return \"connected\";\n }\n return \"exhausted\";\n\n async function runOne(index: number): Promise<\"connected\" | \"failed\" | \"cancelled\"> {\n const attemptController = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onSeriesAbort: () => void = () => {};\n const interrupted = new Promise<\"failed\" | \"cancelled\">((resolve) => {\n timer = setTimeout(() => {\n attemptController.abort();\n resolve(\"failed\");\n }, attemptTimeoutMs);\n onSeriesAbort = () => {\n attemptController.abort();\n resolve(\"cancelled\");\n };\n signal.addEventListener(\"abort\", onSeriesAbort);\n });\n try {\n const succeeded = attempt(attemptController.signal, index).then(\n (ok) => (ok ? (\"connected\" as const) : (\"failed\" as const)),\n () => \"failed\" as const,\n );\n const outcome = await Promise.race([succeeded, interrupted]);\n return signal.aborted ? \"cancelled\" : outcome;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", onSeriesAbort);\n }\n }\n}\n\n/** Ждёт `ms`; `false` — отменили. */\nfunction sleep(ms: number, signal: AbortSignal): Promise<boolean> {\n if (signal.aborted) return Promise.resolve(false);\n if (ms <= 0) return Promise.resolve(true);\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(true);\n }, ms);\n function onAbort() {\n clearTimeout(timer);\n resolve(false);\n }\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * Подписка на события чата: `GET /:chatId/watch` отдаёт поток SSE, пока вкладка открыта.\n * Обрыв или затянувшаяся тишина — переподключение с паузами; адреса нет — подписка заканчивается.\n * Доступ закрыт (401, 403) — повтор через долгую паузу: вход могли восстановить. Вкладка, кроме того, открывает подписку\n * заново после каждого своего хода.\n */\n\nexport type ChatWatchEvent =\n | { type: \"turn-started\"; messageId: string; trigger: string; channel: string }\n | { type: \"turn-finished\"; messageId?: string }\n | { type: \"tasks-changed\" }\n | { type: \"notice-added\"; messageId: string }\n /** Сообщение записано без хода (пачка из мессенджера ждёт тишины, «Стоп» отменил пачку). */\n | { type: \"message-added\"; messageId: string }\n | { type: \"chat-closed\"; closed: { code: string; message: string } }\n | { type: \"chat-reopened\" }\n /** Состояние чата сменилось (ход, смена ветки, правка ответа — в том числе в другой вкладке). */\n | { type: \"state-changed\"; state: unknown; atMessageId: string | null }\n | { type: string; [key: string]: unknown };\n\nexport type WatchChatOptions = {\n url: string;\n fetch: (url: string, init?: RequestInit) => Promise<Response>;\n signal: AbortSignal;\n /** Паузы перед повторами по порядку; последняя повторяется. */\n delaysMs: readonly number[];\n /** Пауза перед повтором после 401 или 403. По умолчанию минута. */\n refusedRetryMs?: number;\n /** Сколько ждать хоть какого-то байта (событие или пинг), прежде чем считать поток мёртвым. */\n silenceTimeoutMs: number;\n onOpen?: () => void;\n onEvent: (event: ChatWatchEvent) => void;\n};\n\nexport type WatchChatEnd = \"unavailable\" | \"stopped\";\n\nexport async function watchChat(options: WatchChatOptions): Promise<WatchChatEnd> {\n const { signal, delaysMs } = options;\n let failures = 0;\n while (!signal.aborted) {\n const outcome = await readOnce(options);\n if (outcome === \"unavailable\") return \"unavailable\";\n if (signal.aborted) break;\n if (outcome === \"refused\") {\n await pause(options.refusedRetryMs ?? 60_000, signal);\n continue;\n }\n if (outcome === \"opened\") failures = 0;\n const delay = delaysMs[Math.min(failures, delaysMs.length - 1)] ?? 1000;\n failures += 1;\n await pause(delay, signal);\n }\n return \"stopped\";\n}\n\n/** Одно подключение: `opened` — поток открывался и потом закрылся; `failed` — не открылся; `refused` — нет доступа. */\nasync function readOnce(options: WatchChatOptions): Promise<\"opened\" | \"failed\" | \"refused\" | \"unavailable\"> {\n const connection = new AbortController();\n const stopConnection = () => connection.abort();\n options.signal.addEventListener(\"abort\", stopConnection, { once: true });\n let silenceTimer: ReturnType<typeof setTimeout> | undefined;\n const restartSilenceTimer = () => {\n clearTimeout(silenceTimer);\n silenceTimer = setTimeout(stopConnection, options.silenceTimeoutMs);\n };\n try {\n const response = await options.fetch(options.url, { headers: { accept: \"text/event-stream\" }, signal: connection.signal });\n if (!response.ok || !response.body) {\n if (response.status === 401 || response.status === 403) return \"refused\";\n if (isPermanentRefusal(response.status)) return \"unavailable\";\n return \"failed\";\n }\n // Сервер без подписки, который на любой адрес отвечает своей страницей или JSON: повторять бессмысленно.\n if (!(response.headers.get(\"content-type\") ?? \"\").includes(\"text/event-stream\")) return \"unavailable\";\n options.onOpen?.();\n restartSilenceTimer();\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n restartSilenceTimer();\n buffer += decoder.decode(value, { stream: true });\n let boundary = buffer.indexOf(\"\\n\\n\");\n while (boundary >= 0) {\n const block = buffer.slice(0, boundary);\n buffer = buffer.slice(boundary + 2);\n const event = eventOf(block);\n if (event && !connection.signal.aborted) options.onEvent(event);\n boundary = buffer.indexOf(\"\\n\\n\");\n }\n }\n return \"opened\";\n } catch {\n return connection.signal.aborted && !options.signal.aborted ? \"opened\" : \"failed\";\n } finally {\n clearTimeout(silenceTimer);\n options.signal.removeEventListener(\"abort\", stopConnection);\n connection.abort();\n }\n}\n\n/** Адреса нет или запрос неверен — повторять бессмысленно. Перегрузку и таймаут сервера повторяем. */\nfunction isPermanentRefusal(status: number): boolean {\n return status >= 400 && status < 500 && status !== 408 && status !== 429;\n}\n\nfunction eventOf(block: string): ChatWatchEvent | undefined {\n const data = block\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).trimStart())\n .join(\"\\n\");\n if (!data || data === \"[DONE]\") return undefined;\n try {\n const parsed: unknown = JSON.parse(data);\n if (parsed && typeof parsed === \"object\" && typeof (parsed as { type?: unknown }).type === \"string\") return parsed as ChatWatchEvent;\n } catch {\n // Битая строка — пропускаем, следующее событие дойдёт.\n }\n return undefined;\n}\n\nfunction pause(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(done, ms);\n signal.addEventListener(\"abort\", done, { once: true });\n function done() {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n }\n });\n}\n","import { ConnectionSilentError } from \"./connection-errors\";\n\nconst CHECK_EVERY_MS = 1000;\n\n/** Действующий срок тишины: не меньше трёх признаков жизни сервера (FR-017). */\nexport function effectiveSilenceTimeoutMs(silenceTimeoutMs: number, heartbeatMs: number): number {\n return Math.max(silenceTimeoutMs, 3 * heartbeatMs);\n}\n\nexport interface SilenceWatchOptions {\n silenceTimeoutMs: number;\n /** Срок до самого первого байта, если он короче: сервер шлёт пинг сразу после заголовков. */\n firstByteTimeoutMs?: number;\n onFirstByte?: () => void;\n /** Тишина затянулась: закрыть исходное соединение и сообщить наверх. */\n onSilence?: () => void;\n /** Во что превратить ошибку чтения тела перед тем, как отдать её читателю. */\n translateError?: (error: unknown) => unknown;\n /** Тело закончилось любым способом: конец, ошибка, тишина или отмена читателем. */\n onEnd?: () => void;\n}\n\n/**\n * Пропускает тело ответа как есть (ошибки чтения — через `translateError`), но следит за временем последнего байта (любого, и пинга тоже).\n * Тишина дольше срока — поток обрывается `ConnectionSilentError`, исходное соединение отменяется.\n * Сравнение времён, а не «таймер на 30 с»: фоновая вкладка и сон телефона не рвут живой поток,\n * а проснувшись — замечают тишину сразу (`visibilitychange`, `online`).\n */\nexport function watchSilence(body: ReadableStream<Uint8Array>, options: SilenceWatchOptions): ReadableStream<Uint8Array> {\n const reader = body.getReader();\n let lastByteAt = Date.now();\n let gotFirstByte = false;\n let finished = false;\n let timer: ReturnType<typeof setInterval> | undefined;\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | undefined;\n\n const stop = () => {\n finished = true;\n if (timer !== undefined) clearInterval(timer);\n timer = undefined;\n document.removeEventListener(\"visibilitychange\", check);\n window.removeEventListener(\"online\", check);\n options.onEnd?.();\n };\n function check() {\n if (finished) return;\n const limit = gotFirstByte ? options.silenceTimeoutMs : (options.firstByteTimeoutMs ?? options.silenceTimeoutMs);\n const silentFor = Date.now() - lastByteAt;\n if (silentFor < limit) return;\n stop();\n void reader.cancel().catch(() => {});\n options.onSilence?.();\n controllerRef?.error(new ConnectionSilentError(silentFor));\n }\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controllerRef = controller;\n timer = setInterval(check, CHECK_EVERY_MS);\n document.addEventListener(\"visibilitychange\", check);\n window.addEventListener(\"online\", check);\n },\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (finished) return;\n if (done) {\n stop();\n controller.close();\n return;\n }\n lastByteAt = Date.now();\n if (!gotFirstByte) {\n gotFirstByte = true;\n options.onFirstByte?.();\n }\n controller.enqueue(value);\n } catch (error) {\n if (finished) return;\n stop();\n throw options.translateError ? options.translateError(error) : error;\n }\n },\n cancel(reason) {\n stop();\n return reader.cancel(reason);\n },\n });\n}\n","import { ConnectionLostError } from \"./connection-errors\";\nimport { watchSilence } from \"./silence-watchdog\";\nimport type { SilenceWatchOptions } from \"./silence-watchdog\";\n\nexport interface DeadlineOptions {\n /** Срок до заголовков ответа. */\n timeoutMs: number;\n /** Своя отмена (серия попыток): для SDK это обрыв связи, а не стоп собеседника. */\n signal?: AbortSignal;\n /** Срок покрывает и тело ответа: для запросов без потока. */\n wholeBody?: boolean;\n /** Следить за тишиной в теле потока; параметры строятся по ответу (например, по заголовку сервера). */\n silence?: (response: Response) => Omit<SilenceWatchOptions, \"translateError\" | \"onEnd\">;\n}\n\n/**\n * `fetch` со сроком. Сетевой `TypeError` и истёкший срок становятся `ConnectionLostError`.\n * Отмена собеседником (`init.signal`) остаётся `AbortError`.\n */\nexport async function fetchWithDeadline(\n fetchFn: (input: string, init?: RequestInit) => Promise<Response>,\n input: string,\n init: RequestInit | undefined,\n options: DeadlineOptions,\n): Promise<Response> {\n const userSignal = init?.signal ?? undefined;\n const own = new AbortController();\n const abortOwn = (reason?: unknown) => {\n if (!own.signal.aborted) own.abort(reason);\n };\n const forwardUserAbort = () => abortOwn(userSignal?.reason);\n const forwardAttemptAbort = () => abortOwn(new ConnectionLostError());\n if (userSignal?.aborted) forwardUserAbort();\n userSignal?.addEventListener(\"abort\", forwardUserAbort);\n if (options.signal?.aborted) forwardAttemptAbort();\n options.signal?.addEventListener(\"abort\", forwardAttemptAbort);\n\n let failDeadline: (error: ConnectionLostError) => void = () => {};\n const deadline = new Promise<never>((_resolve, reject) => {\n failDeadline = reject;\n });\n deadline.catch(() => {});\n const timer = setTimeout(() => {\n const lost = new ConnectionLostError(`Server did not respond within ${options.timeoutMs} ms`);\n failDeadline(lost);\n abortOwn(lost);\n }, options.timeoutMs);\n const cleanup = () => {\n clearTimeout(timer);\n userSignal?.removeEventListener(\"abort\", forwardUserAbort);\n options.signal?.removeEventListener(\"abort\", forwardAttemptAbort);\n };\n /** Сетевой обрыв и своя отмена — потеря связи; отмена собеседником — как есть. */\n const translate = (error: unknown): unknown => {\n if (userSignal?.aborted) return error;\n if (error instanceof ConnectionLostError) return error;\n if (error instanceof TypeError || (error instanceof Error && error.name === \"AbortError\")) {\n return new ConnectionLostError(error.message, { cause: error });\n }\n return error;\n };\n\n try {\n const response = await Promise.race([fetchFn(input, { ...init, signal: own.signal }), deadline]);\n // Тело отказа короткое и обычно никем не дочитывается: читаем его сразу, чтобы слушатели снялись.\n if (options.wholeBody || !response.ok) {\n const buffered = await Promise.race([response.arrayBuffer(), deadline]);\n cleanup();\n return new Response(buffered, { status: response.status, statusText: response.statusText, headers: response.headers });\n }\n clearTimeout(timer);\n if (!response.body) {\n cleanup();\n return response;\n }\n // Одна обёртка тела: тишина, перевод ошибок чтения и снятие слушателей по концу тела.\n const body = watchSilence(response.body, {\n silenceTimeoutMs: Infinity,\n ...options.silence?.(response),\n translateError: translate,\n onEnd: cleanup,\n });\n return new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n } catch (error) {\n cleanup();\n throw translate(error);\n }\n}\n","/** Чей лимит и за какой срок; как в ответе сервера. */\nexport type SpendingScope = \"user\" | \"chat\" | \"service\";\n/** `rolling` — окно на `hours` часов от первого запроса. */\nexport type SpendingPeriod = \"month\" | \"week\" | \"day\" | \"rolling\" | \"none\";\n/** Почему ход не начнётся: лимит исчерпан, человек заблокирован или остаток меньше запаса на новый ответ. */\nexport type SpendingRefusalReason = \"limit\" | \"blocked\" | \"reserve\";\n\n/** Одно окно лимита: доля и срок, без долларов. У окна сообщений — и сами числа. */\nexport interface SpendingWindowInfo {\n id: string;\n scope: SpendingScope;\n period: SpendingPeriod;\n hours?: number;\n state: \"ok\" | \"warning\" | \"exhausted\";\n share: number;\n resetsAt: Date | null;\n /** Окно считает сообщения человека; нет — деньги. */\n unit?: \"messages\";\n /** Окно сообщений: лимит, отправлено и осталось. */\n limit?: number;\n used?: number;\n remaining?: number;\n}\n\n/** Что известно о лимите: из этого слой поведения собирает надпись. Долларов здесь нет. */\nexport interface SpendingInfo {\n scope: SpendingScope;\n period: SpendingPeriod;\n /** Длина скользящего окна в часах. */\n hours?: number;\n /** Доля израсходованного, 0…1 и больше при перерасходе; `null` — сервер её не назвал (отказ хода). */\n share: number | null;\n /** Когда расход обнулится; `null` — никогда. */\n resetsAt: Date | null;\n /** Пояс лимитов (IANA), по которому показывать дату обновления; нет — UTC. */\n timeZone?: string;\n /** Почему ход не начнётся (лимиты с окнами); нет — исчерпан лимит. */\n reason?: SpendingRefusalReason;\n /** Имя самого тесного окна (лимиты с окнами). */\n windowId?: string;\n /** Все окна с лимитом по отдельности (лимиты с окнами): приложение может показать их сами. */\n windows?: SpendingWindowInfo[];\n /** Самое тесное окно считает сообщения человека; нет — деньги. */\n unit?: \"messages\";\n /** Сколько сообщений осталось в этом окне; в отказе 429 числа нет. */\n remaining?: number;\n}\n\n/** Лимит, о котором надо сказать собеседнику: пока всё в пределах, показывать нечего. */\nexport interface SpendingView {\n state: \"warning\" | \"exhausted\";\n info: SpendingInfo;\n}\n\n/** Ответ сервера на `GET …/spending` и тело отказа 429 (в отказе `state` нет). */\ninterface WireSpending {\n state?: string;\n scope?: string;\n period?: string;\n hours?: number;\n share?: number;\n resetsAt?: string | null;\n timeZone?: string;\n reason?: string;\n windowId?: string;\n blocked?: boolean;\n windows?: unknown;\n unit?: string;\n remaining?: number;\n}\n\n/** Не дальше, чем умеет `setTimeout`: месяц с лишним в него не влезает. */\nexport const MAX_TIMER_MS = 2_147_483_647;\n/** Как часто спрашивать сервер, если срок обновления уже прошёл, а лимит ещё стоит (часы вкладки и сервера расходятся). */\nexport const RESET_RECHECK_MS = 5_000;\n\nconst SCOPES: readonly SpendingScope[] = [\"user\", \"chat\", \"service\"];\nconst PERIODS: readonly SpendingPeriod[] = [\"month\", \"week\", \"day\", \"rolling\", \"none\"];\nconst REASONS: readonly SpendingRefusalReason[] = [\"limit\", \"blocked\", \"reserve\"];\n\nconst scopeOf = (scope: unknown): SpendingScope => (SCOPES.includes(scope as SpendingScope) ? (scope as SpendingScope) : \"user\");\nconst periodOf = (period: unknown): SpendingPeriod => (PERIODS.includes(period as SpendingPeriod) ? (period as SpendingPeriod) : \"month\");\nconst hoursOf = (hours: unknown) => (typeof hours === \"number\" && hours > 0 ? { hours } : {});\nconst countOf = (count: unknown) => typeof count === \"number\" && Number.isInteger(count) && count >= 0;\n/** Числа окна сообщений; денежному окну — ничего. */\nfunction messageCountsOf(wire: { unit?: unknown; limit?: unknown; used?: unknown; remaining?: unknown }) {\n if (wire.unit !== \"messages\") return {};\n return {\n unit: \"messages\" as const,\n ...(countOf(wire.limit) && { limit: wire.limit as number }),\n ...(countOf(wire.used) && { used: wire.used as number }),\n ...(countOf(wire.remaining) && { remaining: wire.remaining as number }),\n };\n}\n\nexport function spendingInfoOf(wire: WireSpending): SpendingInfo {\n const reason = wire.blocked === true ? \"blocked\" : REASONS.includes(wire.reason as SpendingRefusalReason) ? (wire.reason as SpendingRefusalReason) : undefined;\n if (reason === \"blocked\") return { scope: \"user\", period: \"none\", share: null, resetsAt: null, reason };\n const windows = Array.isArray(wire.windows) ? wire.windows.flatMap(windowInfoOf) : [];\n return {\n scope: scopeOf(wire.scope),\n period: periodOf(wire.period),\n ...hoursOf(wire.hours),\n share: typeof wire.share === \"number\" ? wire.share : null,\n resetsAt: wire.resetsAt ? new Date(wire.resetsAt) : null,\n ...(typeof wire.timeZone === \"string\" && { timeZone: wire.timeZone }),\n ...(reason && { reason }),\n ...(typeof wire.windowId === \"string\" && { windowId: wire.windowId }),\n ...(windows.length > 0 && { windows }),\n ...messageCountsOf(wire),\n };\n}\n\nfunction windowInfoOf(wire: unknown): SpendingWindowInfo[] {\n if (typeof wire !== \"object\" || wire === null) return [];\n const window = wire as Record<string, unknown>;\n if (typeof window.id !== \"string\" || typeof window.share !== \"number\") return [];\n const state = window.state === \"warning\" || window.state === \"exhausted\" ? window.state : \"ok\";\n return [\n {\n id: window.id,\n scope: scopeOf(window.scope),\n period: periodOf(window.period),\n ...hoursOf(window.hours),\n state,\n share: window.share,\n resetsAt: typeof window.resetsAt === \"string\" ? new Date(window.resetsAt) : null,\n ...messageCountsOf(window),\n },\n ];\n}\n\n/** `null` — лимит в пределах или ответ не разобрать: надписи нет. */\nexport function spendingViewOf(wire: WireSpending): SpendingView | null {\n if (wire.state !== \"warning\" && wire.state !== \"exhausted\") return null;\n return { state: wire.state, info: spendingInfoOf(wire) };\n}\n\n/** Через сколько спросить сервер снова, чтобы снять исчерпание сразу после обновления. */\nexport function untilResetCheckMs(info: SpendingInfo, now: number): number | null {\n if (!info.resetsAt) return null;\n const wait = info.resetsAt.getTime() - now;\n return wait > 0 ? Math.min(wait, MAX_TIMER_MS) : RESET_RECHECK_MS;\n}\n","import type { InferToolInput, ToolSet } from \"ai\";\n\n/** Где сейчас человек в приложении. Форма совпадает с `ScreenPlace` из `@ykurilov/agent-chat-core`: ядро браузеру не нужно. */\nexport interface ScreenPlace {\n description: string;\n data?: Record<string, unknown>;\n}\n\n/** Сообщение «данные приложения изменены»; `id` один на сообщение, повторы с тем же `id` игнорируются. */\nexport interface AppDataChange {\n id: string;\n tag: string;\n details?: unknown;\n}\n\n/** Тип куска потока ответа «данные изменены»; совпадает с `APP_CHANGE_PART_TYPE` ядра. */\nexport const APP_CHANGE_PART_TYPE = \"data-app-change\";\n\n/** Вызов браузерного инструмента, который сервер просит выполнить на этой вкладке. */\nexport interface BrowserCall {\n toolCallId: string;\n toolName: string;\n input: unknown;\n}\n\n/** Итог вызова: результат или текст ошибки для модели. */\nexport type BrowserCallResult = { output: unknown } | { error: string };\n\n/** Функция браузерного инструмента: делает действие на странице. `signal` обрывает её (срок вышел, «стоп»). */\nexport type BrowserToolFunction<Input = any> = (input: Input, context: { signal: AbortSignal }) => unknown;\n\n/**\n * Функции браузерных инструментов по именам. С описаниями из общего модуля (`tool({ description, inputSchema })`)\n * вход каждой функции выводится из схемы: `BrowserToolImplementations<typeof browserToolDescriptions>`.\n */\nexport type BrowserToolImplementations<Descriptions extends ToolSet = ToolSet> = {\n [Name in keyof Descriptions]: BrowserToolFunction<InferToolInput<Descriptions[Name]>>;\n};\n\n/** Связь чата с приложением: место на экране, номер вкладки, сообщения об изменении данных, браузерные вызовы. */\nexport interface AgentAppBridge {\n /** Номер вкладки; сервер по нему отдаёт браузерные вызовы той вкладке, где идёт ответ. */\n tabId: string;\n /** Место на момент запроса; `null` — места нет. */\n currentPlace(): ScreenPlace | null;\n /** Раздаёт сообщение подписчикам; повтор `id` игнорирует. */\n dataChanged(change: AppDataChange): void;\n /** Исполняет функцию приложения по имени с `signal`; нет функции — `{ error: \"Инструмент недоступен на этой странице\" }`, брошенная ошибка — `{ error: её текст }`. */\n browserCall(call: BrowserCall, options?: { signal?: AbortSignal }): Promise<BrowserCallResult>;\n}\n","/** Последние id, которые помнит вкладка: в `sessionStorage` под `key` (переживают перезагрузку), без хранилища — на время жизни страницы. */\nexport interface RememberedIds {\n /** Отмечает id; `false` — он уже отмечен. */\n claim(id: string): boolean;\n}\n\nexport function rememberedIds(key: string, limit: number): RememberedIds {\n /** Отмеченные id, если `sessionStorage` недоступен. */\n let pageIds: string[] = [];\n\n const read = (): string[] => {\n try {\n const stored: unknown = JSON.parse(sessionStorage.getItem(key) ?? \"[]\");\n return Array.isArray(stored) ? stored.filter((id): id is string => typeof id === \"string\") : [];\n } catch {\n return pageIds;\n }\n };\n\n const write = (ids: string[]) => {\n pageIds = ids;\n try {\n sessionStorage.setItem(key, JSON.stringify(ids));\n } catch {\n // Приватный режим: помнит только эта страница.\n }\n };\n\n return {\n claim(id) {\n const ids = read();\n if (ids.includes(id)) return false;\n write([...ids, id].slice(-limit));\n return true;\n },\n };\n}\n","import type { AgentAppBridge, BrowserCall, BrowserCallResult } from \"./app-bridge\";\nimport { rememberedIds } from \"./remembered-ids\";\n\n/** Тип куска потока ответа «выполни браузерный инструмент»; совпадает с `BROWSER_CALL_PART_TYPE` ядра. */\nexport const BROWSER_CALL_PART_TYPE = \"data-browser-call\";\n\n/**\n * Под этим ключом `sessionStorage` хранит исполненные на вкладке вызовы (адрес чата и номер вызова, как ключ ожидания на\n * сервере): повтор кусков после перезагрузки их не повторит, а тот же номер в другом чате исполнится.\n */\nexport const BROWSER_CALLS_DONE_KEY = \"agent-chat:browser-calls-done\";\n\n/** Исполненные на вкладке вызовы; помнятся последние 100. */\nconst doneCalls = rememberedIds(BROWSER_CALLS_DONE_KEY, 100);\n\n/** Данные куска `data-browser-call`: вызов и вкладка, которой он адресован. */\nexport interface BrowserCallRequest extends BrowserCall {\n tabId: string;\n}\n\n/**\n * Исполняет вызов из потока ответа, если он адресован этой вкладке и ещё не исполнялся, и отправляет итог на\n * `{chatUrl}/browser-tools/{toolCallId}`. Сбой связи — тихо, вызов на сервере закроется сам. `409` (срок вышел, ход\n * кончился) — тоже без ошибки, но сервер уже ушёл дальше: `onNotPending` даёт ленте перечитать историю.\n */\nexport async function runBrowserCall(\n call: BrowserCallRequest,\n context: {\n bridge: AgentAppBridge;\n chatUrl: string;\n request: (input: string, init?: RequestInit) => Promise<Response>;\n signal: AbortSignal;\n onNotPending?: () => void;\n },\n): Promise<void> {\n const { bridge } = context;\n if (call.tabId !== bridge.tabId || !doneCalls.claim(JSON.stringify([context.chatUrl, call.toolCallId]))) return;\n const result = await bridge.browserCall({ toolCallId: call.toolCallId, toolName: call.toolName, input: call.input }, { signal: context.signal });\n try {\n const response = await context.request(`${context.chatUrl}/browser-tools/${encodeURIComponent(call.toolCallId)}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: answerBody(bridge.tabId, result),\n });\n if (response.status === 409) context.onNotPending?.();\n } catch {\n // Связь оборвалась: сервер закроет вызов по сроку.\n }\n}\n\n/** Тело ответа: пустой результат — `null` (сервер скажет модели «готово»), результат не в JSON — ошибка с текстом. */\nfunction answerBody(tabId: string, result: BrowserCallResult): string {\n if (\"error\" in result) return JSON.stringify({ tabId, error: result.error });\n try {\n return JSON.stringify({ tabId, output: result.output === undefined ? null : result.output });\n } catch (error) {\n return JSON.stringify({ tabId, error: error instanceof Error ? error.message : String(error) });\n }\n}\n","/** Пауза, которую можно оборвать. Слушатель отмены снимается, когда пауза кончилась сама: на долгом сигнале они не копятся. */\nexport function pause(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const finish = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", finish);\n resolve();\n };\n const timer = setTimeout(finish, ms);\n signal.addEventListener(\"abort\", finish, { once: true });\n });\n}\n","/** Длиннее экран не показывает: текст отказа — короткая причина, а не страница. */\nexport const SERVER_REFUSAL_MAX_LENGTH = 300;\n\n/**\n * Чей отказ: `turn_refused` — приложение не пустило ход (`beforeTurn`), `access_refused` — не пустило человека\n * (`resolveUser`), `chat_closed` — чат закрыт, `action_forbidden` — действие запрещено правами чата (`code` — право),\n * `chat_creation_refused` — приложение не дало создать новый чат («Новый чат» в списке).\n */\nexport type AppRefusalKind = \"turn_refused\" | \"access_refused\" | \"chat_closed\" | \"action_forbidden\" | \"chat_creation_refused\";\n\nconst APP_REFUSAL_KINDS: ReadonlySet<unknown> = new Set<AppRefusalKind>([\"turn_refused\", \"access_refused\", \"chat_closed\", \"action_forbidden\", \"chat_creation_refused\"]);\n\n/** Отказ приложения: код для программы и слова для человека (до 300 знаков). */\nexport interface AppRefusal {\n kind: AppRefusalKind;\n code: string;\n message: string;\n}\n\n/**\n * Отказ приложения из тела ответа: `{ error: { kind, code, message } }`, `kind` — один из `AppRefusalKind`.\n * Статус не важен (403 или 401): вид и слова в теле главнее. `null` — тело другое, без кода или без слов.\n */\nexport async function appRefusalOf(response: Response): Promise<AppRefusal | null> {\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n return null;\n }\n const error = typeof body === \"object\" && body !== null ? (body as { error?: unknown }).error : undefined;\n if (typeof error !== \"object\" || error === null) return null;\n const { kind, code, message } = error as { kind?: unknown; code?: unknown; message?: unknown };\n if (!APP_REFUSAL_KINDS.has(kind)) return null;\n if (typeof code !== \"string\" || typeof message !== \"string\") return null;\n const text = shortenedRefusalText(message);\n return text ? { kind: kind as AppRefusalKind, code, message: text } : null;\n}\n\n/** Слова отказа в одну строку, не длиннее `SERVER_REFUSAL_MAX_LENGTH`. */\nexport function shortenedRefusalText(text: string): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length <= SERVER_REFUSAL_MAX_LENGTH ? flat : `${flat.slice(0, SERVER_REFUSAL_MAX_LENGTH - 1).trimEnd()}…`;\n}\n","import { appRefusalOf, shortenedRefusalText } from \"./app-refusal\";\nimport type { AppRefusalKind } from \"./app-refusal\";\n\n/**\n * Отказ сервера 4xx с понятным человеку текстом. `final` — повтор того же запроса снова получит отказ.\n * `kind` и `code` есть, когда отказало само приложение (`beforeTurn` или `resolveUser`).\n */\nexport interface ServerRefusal {\n text: string;\n final: boolean;\n kind?: AppRefusalKind;\n code?: string;\n}\n\n/** 4xx, после которых повтор имеет смысл: истёк срок запроса, сервер просит подождать. */\nconst RETRYABLE_STATUSES = new Set([408, 425, 429]);\n\n/**\n * Текст отказа из тела 4xx: `{ error: \"…\" }` обёрток приложения или `{ error: { message: \"…\" } }`.\n * `null` — не 4xx, вход истёк (401), тела нет, оно не JSON или текста в нём нет: показывается общая надпись.\n */\nexport async function serverRefusalOf(response: Response): Promise<ServerRefusal | null> {\n if (response.status < 400 || response.status >= 500) return null;\n // Приложение отказало своими словами — окончательно, даже если это 401: вход тут ни при чём.\n const app = await appRefusalOf(response);\n if (app) return { text: app.message, final: true, kind: app.kind, code: app.code };\n if (response.status === 401) return null;\n let body: unknown;\n try {\n body = await response.clone().json();\n } catch {\n return null;\n }\n const text = shortenedRefusalText(refusalTextOf(body));\n return text ? { text, final: !RETRYABLE_STATUSES.has(response.status) } : null;\n}\n\nfunction refusalTextOf(body: unknown): string {\n if (typeof body !== \"object\" || body === null) return \"\";\n const error = (body as { error?: unknown }).error;\n if (typeof error === \"string\") return error;\n if (typeof error === \"object\" && error !== null) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === \"string\") return message;\n }\n return \"\";\n}\n","import { getToolName } from \"ai\";\nimport type { DynamicToolUIPart, ToolUIPart } from \"ai\";\nimport type { ApprovalDecision, ApprovalFailureKind } from \"../chat-types\";\nimport type { HelperCallStatus, HelperCallView, HelperStepView, ToolApprovalView } from \"../skin-contract\";\n\n/*\n * Клиентские копии типов помощника из ядра: ядро браузеру не нужно, форма совпадает с `@ykurilov/agent-chat-core`.\n */\n\n/** Шаг помощника: вызов его инструмента или кусок текста. */\nexport type HelperStep = { type: \"tool\"; toolName: string; input: unknown; output?: unknown; error?: string } | { type: \"text\"; text: string };\n\n/** Ход помощника, как его пишет сервер в вывод вызова `helper_*` (в том числе промежуточный). */\nexport interface HelperProgress {\n task: string;\n steps: HelperStep[];\n text: string;\n status: \"running\" | \"awaiting_approval\" | \"done\" | \"failed\" | \"background\";\n pendingApproval?: { approvalId: string; toolName: string; title?: string; input: unknown };\n taskId?: string;\n error?: string;\n /** Только на экране: решение человека по подтверждению помощника, пока сервер не прислал продолжение. */\n decided?: \"allowed\" | \"always\" | \"denied\";\n}\n\n/** Приставка имени инструмента-помощника; часть протокола, слово не менять. */\nexport const HELPER_TOOL_PREFIX = \"helper_\";\n\ntype ToolPart = ToolUIPart | DynamicToolUIPart;\n\nexport const isHelperToolName = (name: string) => name.startsWith(HELPER_TOOL_PREFIX);\n\nexport const isHelperPart = (part: ToolPart) => isHelperToolName(getToolName(part));\n\n/** Ход помощника из вывода вызова; нет вывода или он чужой формы — `undefined`. */\nexport function helperProgressOf(part: { output?: unknown }): HelperProgress | undefined {\n const output = part.output;\n if (typeof output !== \"object\" || output === null) return undefined;\n const progress = output as Partial<HelperProgress>;\n return typeof progress.status === \"string\" && Array.isArray(progress.steps) ? (output as HelperProgress) : undefined;\n}\n\n/** Id запроса подтверждения внутри помощника, который ждёт человека. */\nexport function pendingHelperApprovalId(part: { output?: unknown }): string | undefined {\n const progress = helperProgressOf(part);\n return progress?.status === \"awaiting_approval\" ? progress.pendingApproval?.approvalId : undefined;\n}\n\nexport interface HelperCallControls {\n onApproval: (approvalId: string, decision: ApprovalDecision) => void;\n approvalPendingId: string | null;\n approvalFailure: { id: string; kind: ApprovalFailureKind; text?: string } | null;\n failureText: (kind: ApprovalFailureKind) => string;\n}\n\n/** Что рисует `skin.HelperCallRow`, кроме раскрытия: имя без приставки, задание, шаги, итог, статус, подтверждение и его действие. */\nexport function helperCallViewOf(part: ToolPart, controls: HelperCallControls): Omit<HelperCallView, \"open\" | \"onOpenChange\"> {\n const progress = helperProgressOf(part as { output?: unknown });\n const input = part.input as { task?: unknown } | undefined;\n const task = progress?.task ?? (typeof input?.task === \"string\" ? input.task : \"\");\n const errorText = progress?.error ?? (part.state === \"output-error\" ? part.errorText : undefined);\n const approval = approvalOf(progress, controls);\n const approvalAction = approval?.state === \"requested\" ? approvalActionOf(progress!.pendingApproval!) : undefined;\n const title = (part as { title?: unknown }).title;\n return {\n name: getToolName(part).slice(HELPER_TOOL_PREFIX.length),\n ...(typeof title === \"string\" && title.length > 0 && { title }),\n task,\n steps: (progress?.steps ?? []).map(stepViewOf),\n text: progress?.text ?? \"\",\n status: statusOf(part, progress),\n ...(approval && { approval }),\n ...(approvalAction && { approvalAction }),\n ...(errorText && { errorText }),\n };\n}\n\nfunction approvalActionOf({ toolName, title, input }: NonNullable<HelperProgress[\"pendingApproval\"]>): NonNullable<HelperCallView[\"approvalAction\"]> {\n return { toolName, ...(title && { title }), ...(input !== undefined && { input }) };\n}\n\nfunction statusOf(part: ToolPart, progress: HelperProgress | undefined): HelperCallStatus {\n if (part.state === \"output-error\" || part.state === \"output-denied\") return \"failed\";\n if (progress) return progress.status;\n return part.state === \"output-available\" && !part.preliminary ? \"done\" : \"running\";\n}\n\nfunction stepViewOf(step: HelperStep, index: number): HelperStepView {\n const id = String(index);\n if (step.type === \"text\") return { kind: \"text\", id, text: step.text };\n return {\n kind: \"tool\",\n id,\n toolName: step.toolName,\n state: step.error !== undefined ? \"error\" : \"output\" in step ? \"done\" : \"running\",\n ...(step.input !== undefined && { input: step.input }),\n ...(step.output !== undefined && { output: step.output }),\n ...(step.error !== undefined && { errorText: step.error }),\n };\n}\n\nfunction approvalOf(progress: HelperProgress | undefined, controls: HelperCallControls): ToolApprovalView | undefined {\n if (progress?.decided) return { state: progress.decided };\n const approvalId = progress?.status === \"awaiting_approval\" ? progress.pendingApproval?.approvalId : undefined;\n if (!approvalId) return undefined;\n const failed = controls.approvalFailure?.id === approvalId ? controls.approvalFailure : undefined;\n const failure = failed?.kind;\n return {\n state: \"requested\",\n onDecide: (decision) => controls.onApproval(approvalId, decision),\n pending: controls.approvalPendingId === approvalId,\n failed: failure !== undefined,\n ...(failure !== undefined && { failureText: failed?.text ?? controls.failureText(failure) }),\n // «Всегда в этом чате» относится к инструментам основного агента; внутри помощника — только «да» или «нет».\n canAlways: false,\n };\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type { AgentChatMessage } from \"../chat-types\";\nimport { fetchWithDeadline } from \"../liveness/request-deadline\";\nimport { serverRefusalOf } from \"../server-refusal\";\nimport type { ServerRefusal } from \"../server-refusal\";\nimport type { SpendingInfo } from \"../spending-status\";\nimport { withWidgetAnswer } from \"./widget-messages\";\n\n/**\n * Почему ответ на виджет не принят: виджет снова активен, причина — словами у виджета. `refused` — приложение не пустило\n * ход, `answer_refused` — приложение отказало в самом ответе (`checkAnswer`); слова — в `text`.\n * `check_timeout` — проверка ответа не уложилась в срок: виджет открыт, текст — подпись `widgets.answerCheckTimeout`.\n */\nexport type WidgetFailureKind = \"connection\" | \"spending_limit\" | \"busy\" | \"too_many_turns\" | \"already_answered\" | \"rejected\" | \"refused\" | \"answer_refused\" | \"check_timeout\";\n\ntype ResumeOutcome = \"streaming\" | \"nothing\" | \"failed\";\n\nexport interface WidgetAnswerDeps {\n chatUrl: string;\n request: (input: string, init?: RequestInit) => Promise<Response>;\n requestTimeoutMs: () => number;\n /** Поля страницы, которые несёт каждый запрос хода (вкладка, место на экране, пояс). */\n pageFields: () => Record<string, unknown>;\n /** Жизнь чата: смена чата обрывает ожидание. */\n chatSignal: () => AbortSignal;\n setMessages: Dispatch<SetStateAction<AgentChatMessage[]>>;\n setResuming: (resuming: boolean) => void;\n resumeWithProbe: (signal: AbortSignal) => Promise<ResumeOutcome>;\n /** Перечитывает историю; сорвавшееся чтение повторяется серией попыток. */\n rereadHistory: (signal: AbortSignal) => Promise<void>;\n /** Новые события ответа с вызовом `toolCallId` придут живыми с перечитанной историей (`AppEventReport.expectLive`). */\n expectLiveEventsOf: (toolCallId: string) => void;\n startSeries: () => void;\n /** Приложение отказало ходу: сообщает странице (`onTurnRefused`). */\n announceAppRefusal: (refusal: ServerRefusal | null) => void;\n /** Отказ 429: лимит расходов (поле ввода блокируется, как при отказе на отправку) или место хода. */\n spendingRefusalOf: (response: Response) => Promise<SpendingInfo | null>;\n showSpendingExhausted: (info: SpendingInfo) => void;\n errorKindOf: (response: Response) => Promise<string | undefined>;\n}\n\nexport interface WidgetAnswerState {\n /** Ответ человека на виджет. `true` — сервер принял; `onAccepted` зовётся сразу, как принял, до продолжения хода. */\n answerWidget: (toolCallId: string, answer: unknown, onAccepted?: () => void) => Promise<boolean>;\n /** Виджет, ответ на который сейчас идёт на сервер. */\n widgetPendingId: string | null;\n widgetFailure: { id: string; kind: WidgetFailureKind; text?: string } | null;\n /** Ответ на виджет в пути или его продолжение ещё подключается: ход, который начнёт сервер, — свой. */\n answeringWidget: () => boolean;\n}\n\n/**\n * Ответ на виджет (contracts/core-and-http.md, «Маршрут ответа»): `POST {api}/:chatId/widgets/:toolCallId`, тело\n * `{ answer }`. Принятый ответ продолжает ход так же, как решение по подтверждению: лента подключается к продолжению,\n * и оно дописывает то же сообщение.\n */\nexport function useWidgetAnswer(deps: WidgetAnswerDeps): WidgetAnswerState {\n const depsRef = useRef(deps);\n depsRef.current = deps;\n const answering = useRef(false);\n const [widgetPendingId, setWidgetPendingId] = useState<string | null>(null);\n const [widgetFailure, setWidgetFailure] = useState<{ id: string; kind: WidgetFailureKind; text?: string } | null>(null);\n\n const answerWidget = useCallback(async (toolCallId: string, answer: unknown, onAccepted?: () => void): Promise<boolean> => {\n if (answering.current) return false;\n answering.current = true;\n const current = depsRef.current;\n const signal = current.chatSignal();\n const fail = (kind: WidgetFailureKind, text?: string) => {\n if (!signal.aborted) setWidgetFailure({ id: toolCallId, kind, ...(text !== undefined && { text }) });\n return false;\n };\n setWidgetFailure(null);\n setWidgetPendingId(toolCallId);\n try {\n let response: Response;\n try {\n response = await fetchWithDeadline(\n current.request,\n `${current.chatUrl}/widgets/${encodeURIComponent(toolCallId)}`,\n { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify({ answer, ...current.pageFields() }) },\n { timeoutMs: current.requestTimeoutMs(), wholeBody: true },\n );\n } catch {\n return fail(\"connection\");\n }\n // Приложение не пустило ход (или человека): его слова у виджета, а не «ответ не отправился».\n const refusedByApp = response.ok ? null : await serverRefusalOf(response);\n if (refusedByApp?.kind) {\n current.announceAppRefusal(refusedByApp);\n return fail(\"refused\", refusedByApp.text);\n }\n // Приложение отказало в самом ответе (`checkAnswer`): виджет открыт, слова — под ним; поле ввода и отказ хода не при чём.\n if (refusedByApp && response.status === 409 && (await current.errorKindOf(response)) === \"widget_answer_refused\") {\n return fail(\"answer_refused\", refusedByApp.text);\n }\n // «Стоп» во время проверки: штатное действие человека. Виджет открыт, ошибки под ним нет.\n if (response.status === 409 && (await current.errorKindOf(response)) === \"widget_answer_cancelled\") return false;\n if (response.status === 504 && (await current.errorKindOf(response)) === \"widget_check_timeout\") return fail(\"check_timeout\");\n if (response.status === 429) {\n const refusal = await current.spendingRefusalOf(response);\n if (refusal && !signal.aborted) current.showSpendingExhausted(refusal);\n if (refusal) return fail(\"spending_limit\");\n return fail((await current.errorKindOf(response)) === \"too_many_turns\" ? \"too_many_turns\" : \"connection\");\n }\n if (response.status === 400) return fail(\"rejected\");\n if (response.status === 409 && (await current.errorKindOf(response)) === \"busy\") return fail(\"busy\");\n // Вопрос уже не ждёт (ответили в другой вкладке, закрыли сообщением) или чата нет: показываем то, что на сервере.\n if (response.status === 409 || response.status === 404) {\n await current.rereadHistory(signal);\n return fail(\"already_answered\");\n }\n if (!response.ok) return fail(\"connection\");\n const resumed = await resumedOf(response);\n current.setMessages((messages) => withWidgetAnswer(messages, toolCallId, answer, resumed));\n if (!signal.aborted) onAccepted?.();\n // Без продолжения события проверки ответа уже лежат в ответе: перечитать историю, они придут живыми. Канал\n // наблюдения может принести то же событие раньше или позже — учёт ленты считает каждое событие один раз.\n if (!resumed) {\n current.expectLiveEventsOf(toolCallId);\n if (!signal.aborted) await current.rereadHistory(signal);\n return true;\n }\n current.setResuming(true);\n try {\n const outcome = await current.resumeWithProbe(signal);\n if (outcome === \"nothing\") await current.rereadHistory(signal);\n else if (outcome === \"failed\" && !signal.aborted) current.startSeries();\n } finally {\n if (!signal.aborted) current.setResuming(false);\n }\n return true;\n } finally {\n answering.current = false;\n if (!signal.aborted) setWidgetPendingId(null);\n }\n }, []);\n\n const answeringWidget = useCallback(() => answering.current, []);\n\n return { answerWidget, widgetPendingId, widgetFailure, answeringWidget };\n}\n\n/** Тело успеха — как у ответа на подтверждение, `{ resumed }`; без него ход считается продолженным. */\nasync function resumedOf(response: Response): Promise<boolean> {\n try {\n const body = (await response.json()) as { resumed?: unknown };\n return body.resumed !== false;\n } catch {\n return true;\n }\n}\n","/*\n * Слова договора виджетов (specs/024-agent-widgets/data-model.md, «Ответы»): так ответ выглядит в результате вызова,\n * который видит модель. Слой поведения пишет код без кириллицы (scripts/check-rules.mjs), поэтому слова записаны\n * кодами символов, а по-русски — в комментарии рядом.\n */\n\n/** «выбрано» — ответ `ask_choice` и `ask_multi_choice`. */\nexport const CHOSEN_KEY = \"\\u0432\\u044b\\u0431\\u0440\\u0430\\u043d\\u043e\";\n/** «оценка» — ответ `ask_rating`. */\nexport const RATING_KEY = \"\\u043e\\u0446\\u0435\\u043d\\u043a\\u0430\";\n/** «поля» — ответ `ask_form`. */\nexport const FIELDS_KEY = \"\\u043f\\u043e\\u043b\\u044f\";\n/** «ответ» — ключ пометки «закрыт сообщением». */\nexport const BY_MESSAGE_KEY = \"\\u043e\\u0442\\u0432\\u0435\\u0442\";\n/** «сообщением» — значение пометки «закрыт сообщением»: `{ \"ответ\": \"сообщением\" }`. */\nexport const BY_MESSAGE_VALUE = \"\\u0441\\u043e\\u043e\\u0431\\u0449\\u0435\\u043d\\u0438\\u0435\\u043c\";\n/** «Задавай один вопрос за раз» — ошибка лишнего ждущего вызова в шаге: такой виджет не показывается. */\nexport const ONE_QUESTION_AT_A_TIME =\n \"\\u0417\\u0430\\u0434\\u0430\\u0432\\u0430\\u0439 \\u043e\\u0434\\u0438\\u043d \\u0432\\u043e\\u043f\\u0440\\u043e\\u0441 \\u0437\\u0430 \\u0440\\u0430\\u0437\";\n\n/** Результат вызова — пометка «человек ответил сообщением, а не виджетом». */\nexport function isAnsweredByMessage(output: unknown): boolean {\n if (typeof output !== \"object\" || output === null || Array.isArray(output)) return false;\n const entries = Object.entries(output);\n return entries.length === 1 && entries[0]![0] === BY_MESSAGE_KEY && entries[0]![1] === BY_MESSAGE_VALUE;\n}\n","import type { AgentChatMessage } from \"../chat-types\";\nimport { pendingHelperApprovalId } from \"../helpers/helper-call\";\nimport { BY_MESSAGE_KEY, BY_MESSAGE_VALUE } from \"./widget-answer-keys\";\n\n/*\n * Ответ на виджет в ленте. Продолжение хода дописывает то же сообщение тем же приёмом, что и после подтверждения\n * (use-agent-chat.ts, «ВНИМАНИЕ: приём с `input-streaming`»): пока ход идёт, отвеченный вызов помечен «в пути» и несёт\n * ответ в `output`. Сервер результат виджета в поток не шлёт, поэтому в конце хода пометка снимается здесь\n * (`withWidgetAnswersSettled`).\n */\n\ninterface ToolPartFields {\n type: string;\n toolCallId?: string;\n state?: string;\n output?: unknown;\n approval?: unknown;\n}\n\nconst fields = (part: AgentChatMessage[\"parts\"][number]) => part as unknown as ToolPartFields;\nconst isToolPart = (part: AgentChatMessage[\"parts\"][number]) => part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\";\n\n/** Ждущий виджет: вызов без результата и без запроса подтверждения. */\nfunction isWaitingWidget(part: AgentChatMessage[\"parts\"][number]): boolean {\n const tool = fields(part);\n return isToolPart(part) && tool.state === \"input-available\" && tool.approval === undefined;\n}\n\n/** Ответ лежит в вызове; `resumed` — ход продолжается, сообщение дописывается потоком. */\nexport function withWidgetAnswer(messages: AgentChatMessage[], toolCallId: string, answer: unknown, resumed: boolean): AgentChatMessage[] {\n return messages.map((message) => {\n if (message.role !== \"assistant\" || !message.parts.some((part) => fields(part).toolCallId === toolCallId)) return message;\n const parts = message.parts.map((part) =>\n fields(part).toolCallId === toolCallId ? ({ ...part, state: resumed ? \"input-streaming\" : \"output-available\", output: answer } as typeof part) : part,\n );\n return { ...message, parts, ...(resumed && { metadata: { ...message.metadata, status: \"streaming\" as const } }) };\n });\n}\n\n/** Ход кончился: отвеченные вызовы, помеченные «в пути», получают свой результат. */\nexport function withWidgetAnswersSettled(messages: AgentChatMessage[]): AgentChatMessage[] {\n const settles = (part: AgentChatMessage[\"parts\"][number]) => fields(part).state === \"input-streaming\" && fields(part).output !== undefined;\n if (!messages.some((message) => message.parts.some(settles))) return messages;\n return messages.map((message) =>\n message.parts.some(settles)\n ? { ...message, parts: message.parts.map((part) => (settles(part) ? ({ ...part, state: \"output-available\" } as typeof part) : part)) }\n : message,\n );\n}\n\n/**\n * История идущего хода сразу после ответа на виджет: последний ответ «идёт», а его последняя часть — отвеченный вызов.\n * Вызов помечается «в пути», чтобы продолжение дописало то же сообщение.\n */\nexport function withWidgetContinuationMarked(messages: AgentChatMessage[]): AgentChatMessage[] {\n const last = messages.at(-1);\n if (last?.role !== \"assistant\" || last.metadata?.status !== \"streaming\") return messages;\n if (last.parts.some((part) => fields(part).state === \"input-streaming\")) return messages;\n const index = last.parts.findLastIndex((part) => part.type !== \"step-start\");\n const part = last.parts[index];\n if (!part || !isToolPart(part) || fields(part).state !== \"output-available\" || fields(part).approval !== undefined) return messages;\n const parts = last.parts.map((item, at) => (at === index ? ({ ...item, state: \"input-streaming\" } as typeof item) : item));\n return [...messages.slice(0, -1), { ...last, parts }];\n}\n\n/** Ждущие виджеты последнего ответа закрыты сообщением человека (сервер делает то же, FR-006). */\nexport function withPendingWidgetsClosed(messages: AgentChatMessage[]): AgentChatMessage[] {\n const last = messages.at(-1);\n if (last?.role !== \"assistant\" || last.metadata?.status !== \"awaiting_approval\" || !last.parts.some(isWaitingWidget)) return messages;\n const parts = last.parts.map((part) =>\n isWaitingWidget(part) ? ({ ...part, state: \"output-available\", output: { [BY_MESSAGE_KEY]: BY_MESSAGE_VALUE } } as typeof part) : part,\n );\n return [...messages.slice(0, -1), { ...last, parts }];\n}\n\n/** В последнем ответе ждёт запрос подтверждения: его решает только кнопка, слова голосом его не закрывают. */\nexport function hasPendingApproval(messages: readonly AgentChatMessage[]): boolean {\n const last = messages.at(-1);\n return (\n last?.role === \"assistant\" &&\n last.metadata?.status === \"awaiting_approval\" &&\n last.parts.some(\n (part) => fields(part).state === \"approval-requested\" || fields(part).state === \"approval-responded\" || pendingHelperApprovalId(part as { output?: unknown }) !== undefined,\n )\n );\n}\n","import { useEffect, useMemo, useRef } from \"react\";\nimport type { ComponentType } from \"react\";\nimport type { AgentChatMessage } from \"./chat-types\";\n\n/** Событие приложения в ответе агента. Форма совпадает с `AppEvent` из `@ykurilov/agent-chat-core`: ядро браузеру не нужно. */\nexport interface AppEvent {\n type: string;\n data?: unknown;\n}\n\n/** Где и когда событие пришло: `live` — живьём (поток ответа или канал наблюдения), а не из истории. */\nexport interface AppEventInfo {\n live: boolean;\n /** Ответ агента с событием; у события вне хода (`publishAppEvent`) его нет. */\n messageId?: string;\n}\n\n/**\n * Свой вид событий приложения внутри ответа: тип события → компонент («урок зачтён» — плашка). Рисуется прямо в теле\n * ответа, там, где лежит часть события, как виджеты: отдельного слота скина нет. Тип без компонента не виден.\n */\nexport type AppEventViews = Record<string, ComponentType<{ event: AppEvent; messageId: string }>>;\n\nexport type AppEventHandler = (event: AppEvent, info: AppEventInfo) => void;\n\nconst APP_EVENT_PART_TYPE = \"data-app-event\";\n/** Начало `id` у события, скопированного из прежнего ответа («Повторить»): оно уже сработало там, живым не считается. */\nconst COPIED_APP_EVENT_ID_PREFIX = \"copied:\";\n\n/** Событие из присланного сервером значения; не событие — `undefined`. */\nexport function appEventOf(value: unknown): AppEvent | undefined {\n if (value === null || typeof value !== \"object\") return undefined;\n const { type } = value as { type?: unknown };\n return typeof type === \"string\" ? (value as AppEvent) : undefined;\n}\n\n/** Событие из части ответа; часть не событие или битая — `undefined`. */\nexport function appEventOfPart(part: AgentChatMessage[\"parts\"][number]): AppEvent | undefined {\n return part.type === APP_EVENT_PART_TYPE ? appEventOf((part as { data?: unknown }).data) : undefined;\n}\n\n/** Учёт событий в ответах ленты. */\nexport interface AppEventReport {\n /** Новые события ответа `messageId` придут живьём, с перечитанной историей (хук итога, доделанный после «Стопа»). */\n expectLive(messageId: string): void;\n}\n\n/**\n * Зовёт `onAppEvent` по разу на каждое событие в ответах ленты. События, что лежали в истории при открытии чата или\n * пришли с перечитанной историей, когда чат свободен (смена ветки), — `live: false`; пришедшие, пока идёт ход, и\n * ожидаемые (`expectLive`) — `live: true`. Скопированные из прежнего ответа («Повторить», `id` вида `copied:…`) — всегда\n * `live: false`: они уже сработали там. Повторная загрузка тех же ответов событие второй раз не даёт; смена чата\n * начинает счёт заново (`AgentChat` к тому же пересоздаёт ленту на каждый чат).\n */\nexport function useAppEventReport(chatId: string, loaded: boolean, busy: boolean, messages: readonly AgentChatMessage[], onAppEvent: AppEventHandler | undefined): AppEventReport {\n const report = useRef(onAppEvent);\n report.current = onAppEvent;\n const seen = useRef({ chatId, keys: new Set<string>(), initialized: false, liveAnswers: new Set<string>() });\n // События, что пришли в той же отрисовке, где чат освободился, — ещё ход: последние куски и конец потока сливаются.\n const wasBusy = useRef(busy);\n\n useEffect(() => {\n if (seen.current.chatId !== chatId) seen.current = { chatId, keys: new Set(), initialized: false, liveAnswers: new Set() };\n if (!loaded) return;\n const state = seen.current;\n const live = state.initialized && (busy || wasBusy.current);\n wasBusy.current = busy;\n state.initialized = true;\n for (const message of messages) {\n if (message.role !== \"assistant\") continue;\n message.parts.forEach((part, index) => {\n const event = appEventOfPart(part);\n if (!event) return;\n const key = `${message.id}:${index}`;\n if (state.keys.has(key)) return;\n state.keys.add(key);\n const partId = (part as { id?: unknown }).id;\n const copied = typeof partId === \"string\" && partId.startsWith(COPIED_APP_EVENT_ID_PREFIX);\n report.current?.(event, { live: !copied && (live || state.liveAnswers.has(message.id)), messageId: message.id });\n });\n }\n }, [busy, chatId, loaded, messages]);\n\n return useMemo(() => ({ expectLive: (messageId: string) => void seen.current.liveAnswers.add(messageId) }), []);\n}\n","/**\n * Права человека в чате и закрытие чата — копия договора ядра (`ChatCapabilities`, `ChatClosure`): пакет экрана от\n * ядра не зависит. Права приходят с историей (`capabilities`), закрытие — в `chat.closed` истории, в отказе 409\n * `chat_closed`, куском потока `data-chat-closed` и событиями наблюдения `chat-closed`/`chat-reopened`.\n */\nexport type ChatCapability =\n | \"edit\"\n | \"regenerate\"\n | \"retry\"\n | \"branches\"\n | \"modelChoice\"\n | \"answerEditing\"\n | \"chatList\"\n | \"compact\"\n | \"attachments\"\n | \"voice\"\n | \"reasoning\"\n | \"toolDetails\"\n | \"steer\"\n | \"modelDetails\";\n\n/**\n * `modelDetails` — модель, провайдер, расход и владелец в истории и потоке; запрет выключает и выбор модели, меню моделей\n * экран тогда не спрашивает. В типе необязательно (нет — `true`): свой литерал прав без него остаётся верным.\n */\nexport type ChatCapabilities = Record<Exclude<ChatCapability, \"modelDetails\">, boolean> & { modelDetails?: boolean };\n\n/** Почему чат закрыт: код для программы, текст для человека. */\nexport interface ChatClosure {\n code: string;\n message: string;\n}\n\n/** Что можно в открытом чате и закрыт ли он. */\nexport interface ChatAccess {\n capabilities: ChatCapabilities;\n /** `null` — чат открыт. */\n closed: ChatClosure | null;\n}\n\n/** Транзитный кусок потока ответа: инструмент закрыл чат. */\nexport const CHAT_CLOSED_PART_TYPE = \"data-chat-closed\";\n\n/** Всё разрешено: сервер до 0.27 прав не присылает. */\nexport const ALL_CAPABILITIES: Required<ChatCapabilities> = {\n edit: true,\n regenerate: true,\n retry: true,\n branches: true,\n modelChoice: true,\n answerEditing: true,\n chatList: true,\n compact: true,\n attachments: true,\n voice: true,\n reasoning: true,\n toolDetails: true,\n steer: true,\n modelDetails: true,\n};\n\n/** Права из ответа сервера: пропущенное и не булево — разрешено. */\nexport function capabilitiesOf(value: unknown): Required<ChatCapabilities> {\n if (typeof value !== \"object\" || value === null) return ALL_CAPABILITIES;\n const given = value as Partial<Record<ChatCapability, unknown>>;\n const resolved = { ...ALL_CAPABILITIES };\n for (const capability of Object.keys(ALL_CAPABILITIES) as ChatCapability[]) {\n if (typeof given[capability] === \"boolean\") resolved[capability] = given[capability] as boolean;\n }\n return resolved;\n}\n\n/** Закрытие из ответа сервера; `null` — нет или неверной формы. */\nexport function closureOf(value: unknown): ChatClosure | null {\n if (typeof value !== \"object\" || value === null) return null;\n const { code, message } = value as { code?: unknown; message?: unknown };\n return typeof code === \"string\" && typeof message === \"string\" ? { code, message } : null;\n}\n\nexport function sameCapabilities(a: ChatCapabilities, b: ChatCapabilities): boolean {\n return (Object.keys(a) as ChatCapability[]).every((capability) => a[capability] === b[capability]);\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { TaskStatusView } from \"../skin-contract\";\n\n/** Задача, как её отдаёт `GET {chat}/tasks`. */\nexport interface BackgroundTask {\n id: string;\n title: string;\n kind: string;\n name: string;\n status: TaskStatusView;\n startedAt: string;\n finishedAt?: string;\n progress?: unknown;\n}\n\n/** Решение человека по задаче, которая ждёт подтверждения. */\nexport type TaskApprovalDecision = \"allow\" | \"deny\";\nexport type TaskDecisionState = { state: \"pending\" } | { state: \"failed\" } | { state: \"decided\"; decision: TaskApprovalDecision };\n\nexport interface UseBackgroundTasksOptions {\n /** Адрес чата: `{api}/{chatId}`. */\n chatUrl: string;\n /** Скин показывает карточки или плашки, история загружена. Нет — задачи не запрашиваются. */\n enabled: boolean;\n /** Растёт, когда список мог измениться (подписка открылась, пришло `tasks-changed`): список перечитывается. */\n version: number;\n fetch?: (url: string, init?: RequestInit) => Promise<Response>;\n}\n\nexport interface BackgroundTasks {\n /** Задачи чата; пусто, пока не загружены или у сервера фона нет. */\n tasks: readonly BackgroundTask[];\n /** Текущее время для «сколько идёт»: тикает раз в секунду, пока есть живые задачи. */\n now: number;\n cancel: (taskId: string) => Promise<void>;\n cancelingIds: ReadonlySet<string>;\n /** Ответ на вопрос `approvalId` задачи: ответ на вопрос, который задача уже не задаёт, сервер не примет. */\n decide: (taskId: string, approvalId: string, decision: TaskApprovalDecision) => Promise<void>;\n /** Решения по номеру вопроса: у одной задачи вопросов бывает несколько. */\n decisions: Readonly<Record<string, TaskDecisionState>>;\n}\n\n// Один пустой список на все вызовы: новый `[]` на каждой отрисовке перерисовывал бы ленту.\nconst NO_TASKS: readonly BackgroundTask[] = [];\nconst LIVE_STATUSES: ReadonlySet<TaskStatusView> = new Set([\"queued\", \"running\", \"awaiting_approval\"]);\nexport const isLiveTask = (task: BackgroundTask) => LIVE_STATUSES.has(task.status);\n\n/** Номер вопроса, ответа на который задача ждёт сейчас (`progress.pendingApproval.approvalId`). */\nexport function pendingApprovalIdOf(task: BackgroundTask): string | undefined {\n const approvalId = (task.progress as { pendingApproval?: { approvalId?: unknown } } | undefined)?.pendingApproval?.approvalId;\n return typeof approvalId === \"string\" ? approvalId : undefined;\n}\n\n/** Сколько идёт (или шла) задача к моменту `now`. */\nexport function taskElapsedMs(task: BackgroundTask, now: number): number {\n const started = Date.parse(task.startedAt);\n const until = task.finishedAt ? Date.parse(task.finishedAt) : now;\n return Number.isFinite(started) && Number.isFinite(until) ? Math.max(0, until - started) : 0;\n}\n\n/**\n * Фоновые задачи чата: список с сервера, отмена и решение по подтверждению. Сервер без фона (404) — задач нет,\n * повторных запросов тоже.\n */\nexport function useBackgroundTasks({ chatUrl, enabled, version, fetch: customFetch }: UseBackgroundTasksOptions): BackgroundTasks {\n const fetchRef = useRef(customFetch);\n fetchRef.current = customFetch;\n const send = useCallback((url: string, init?: RequestInit) => (fetchRef.current ?? globalThis.fetch)(url, init), []);\n const [loaded, setLoaded] = useState<{ chatUrl: string; tasks: readonly BackgroundTask[] } | null>(null);\n const [reread, setReread] = useState(0);\n const unavailableFor = useRef<string | null>(null);\n const [cancelingIds, setCancelingIds] = useState<ReadonlySet<string>>(new Set());\n const [decisions, setDecisions] = useState<Record<string, TaskDecisionState>>({});\n\n useEffect(() => {\n if (!enabled || unavailableFor.current === chatUrl) return;\n const controller = new AbortController();\n void (async () => {\n try {\n const response = await send(`${chatUrl}/tasks`, { signal: controller.signal });\n if (response.status === 404) {\n unavailableFor.current = chatUrl;\n return;\n }\n if (!response.ok) return;\n const body: unknown = await response.json();\n if (!controller.signal.aborted && Array.isArray(body)) setLoaded({ chatUrl, tasks: body as BackgroundTask[] });\n } catch {\n // Нет связи — список остаётся прежним до следующего повода перечитать.\n }\n })();\n return () => controller.abort();\n }, [chatUrl, enabled, reread, send, version]);\n\n useEffect(() => {\n setCancelingIds(new Set());\n setDecisions({});\n }, [chatUrl]);\n\n const tasks = loaded?.chatUrl === chatUrl ? loaded.tasks : NO_TASKS;\n const anyLive = tasks.some(isLiveTask);\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (!anyLive) return;\n setNow(Date.now());\n const timer = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(timer);\n }, [anyLive]);\n\n const cancel = useCallback(\n async (taskId: string) => {\n setCancelingIds((current) => new Set(current).add(taskId));\n try {\n await send(`${chatUrl}/tasks/${encodeURIComponent(taskId)}/cancel`, { method: \"POST\" });\n } catch {\n // Отмена не дошла — кнопка снова доступна.\n }\n setCancelingIds((current) => {\n const next = new Set(current);\n next.delete(taskId);\n return next;\n });\n setReread((count) => count + 1);\n },\n [chatUrl, send],\n );\n\n const decide = useCallback(\n async (taskId: string, approvalId: string, decision: TaskApprovalDecision) => {\n setDecisions((current) => ({ ...current, [approvalId]: { state: \"pending\" } }));\n let accepted = false;\n try {\n const response = await send(`${chatUrl}/tasks/${encodeURIComponent(taskId)}/approval`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ decision, approvalId }),\n });\n accepted = response.ok;\n } catch {\n accepted = false;\n }\n setDecisions((current) => ({ ...current, [approvalId]: accepted ? { state: \"decided\", decision } : { state: \"failed\" } }));\n setReread((count) => count + 1);\n },\n [chatUrl, send],\n );\n\n return { tasks, now, cancel, cancelingIds, decide, decisions };\n}\n","import type { AgentChatMessage } from \"../chat-types\";\nimport type { NoticeLabels } from \"../labels\";\nimport type { AgentNoticeViewKind, AgentNoticeViewProps } from \"../skin-contract\";\nimport { pendingApprovalIdOf } from \"../background-tasks/use-background-tasks\";\nimport type { BackgroundTask, TaskApprovalDecision, TaskDecisionState } from \"../background-tasks/use-background-tasks\";\n\n/** Служебное сообщение, как его пишет сервер в часть `data-agent-notice`. */\nexport interface AgentNotice {\n kind: AgentNoticeViewKind;\n taskId?: string;\n title: string;\n result?: string;\n error?: string;\n approval?: { approvalId: string; toolName: string; input: unknown };\n}\n\nexport const AGENT_NOTICE_PART_TYPE = \"data-agent-notice\";\n\nexport const isAgentNoticePart = (part: { type: string }): boolean => part.type === AGENT_NOTICE_PART_TYPE;\n\n/** Служебные сообщения из сообщения человека, кроме реплик сотрудника и пометок приложения; пусто — это обычное сообщение, реплика или пометка. */\nexport function agentNoticesOf(message: AgentChatMessage): AgentNotice[] {\n return rawNoticesOf(message).filter((notice) => notice.kind !== STAFF_NOTICE_KIND && notice.kind !== APP_NOTICE_KIND) as AgentNotice[];\n}\n\n/** Пометка приложения (`kind: \"app\"`, право чата `appNotices`): имя события и его данные, без текста для модели. */\nexport interface AppNotice {\n name: string;\n data: unknown;\n}\n\nconst APP_NOTICE_KIND = \"app\";\n\n/** Пометки приложения из сообщения; пусто — их нет (или право `appNotices` выключено и сервер их не прислал). */\nexport function appNoticesOf(message: AgentChatMessage): AppNotice[] {\n return rawNoticesOf(message).flatMap((notice) => {\n if (notice.kind !== APP_NOTICE_KIND) return [];\n const { name, data } = notice as { name?: unknown; data?: unknown };\n return typeof name === \"string\" ? [{ name, data }] : [];\n });\n}\n\n/** Реплика живого сотрудника человеку (служебное сообщение вида `staff`, с 0.53). */\nexport interface StaffReply {\n text: string;\n staffName?: string;\n}\n\nconst STAFF_NOTICE_KIND = \"staff\";\n\n/** Реплики сотрудника из сообщения; пусто — их нет. */\nexport function staffRepliesOf(message: AgentChatMessage): StaffReply[] {\n return rawNoticesOf(message)\n .filter((notice) => notice.kind === STAFF_NOTICE_KIND)\n .map((notice) => {\n const { title, staffName } = notice as { title?: unknown; staffName?: unknown };\n return { text: typeof title === \"string\" ? title : \"\", ...(typeof staffName === \"string\" && staffName && { staffName }) };\n });\n}\n\nfunction rawNoticesOf(message: AgentChatMessage): Array<{ kind: string }> {\n if (message.role !== \"user\") return [];\n return message.parts.flatMap((part) => {\n if (part.type !== AGENT_NOTICE_PART_TYPE) return [];\n const notices = (part as { data?: { notices?: unknown } }).data?.notices;\n return Array.isArray(notices) ? (notices.filter(isAgentNotice) as Array<{ kind: string }>) : [];\n });\n}\n\nfunction isAgentNotice(value: unknown): boolean {\n return Boolean(value) && typeof value === \"object\" && typeof (value as { kind?: unknown }).kind === \"string\";\n}\n\nexport interface NoticeTaskControls {\n tasks: readonly BackgroundTask[];\n decisions: Readonly<Record<string, TaskDecisionState>>;\n decide: (taskId: string, approvalId: string, decision: TaskApprovalDecision) => void;\n failureText: string;\n}\n\n/**\n * Вид плашки. У `conversation_started` заголовок пустой: это указание агенту от приложения, человеку его не показывают.\n * Кнопки решения — только пока задача в списке и ждёт ответа на вопрос этой плашки; после решения — отметка.\n */\nexport function noticeViewOf(notice: AgentNotice, controls: NoticeTaskControls): AgentNoticeViewProps {\n const view: AgentNoticeViewProps = { kind: notice.kind, title: notice.kind === \"conversation_started\" ? \"\" : notice.title };\n if (notice.result) view.result = notice.result;\n if (notice.error) view.errorText = notice.error;\n if (notice.kind !== \"task_awaiting_approval\" || !notice.taskId) return view;\n const taskId = notice.taskId;\n const task = controls.tasks.find((item) => item.id === taskId);\n const approvalId = notice.approval?.approvalId ?? (task && pendingApprovalIdOf(task));\n if (!approvalId) return view;\n const decision = controls.decisions[approvalId];\n if (decision?.state === \"decided\") return { ...view, approval: { state: decision.decision === \"allow\" ? \"allowed\" : \"denied\" } };\n // Задача уже задала новый вопрос — у плашки старого кнопок нет.\n const waiting = task?.status === \"awaiting_approval\" && (pendingApprovalIdOf(task) ?? approvalId) === approvalId;\n if (!waiting) return view;\n return {\n ...view,\n approval: {\n state: \"requested\",\n // «Всегда» у задачи нет: решение касается одного вызова фоновой задачи.\n onDecide: (choice) => controls.decide(taskId, approvalId, choice === \"deny\" ? \"deny\" : \"allow\"),\n pending: decision?.state === \"pending\",\n failed: decision?.state === \"failed\",\n ...(decision?.state === \"failed\" && { failureText: controls.failureText }),\n canAlways: false,\n },\n };\n}\n\nconst statusLabel: Record<AgentNoticeViewKind, keyof Omit<NoticeLabels, \"line\" | \"staffSaid\">> = {\n task_done: \"taskDone\",\n task_failed: \"taskFailed\",\n task_canceled: \"taskCanceled\",\n task_interrupted: \"taskInterrupted\",\n task_timed_out: \"taskTimedOut\",\n task_awaiting_approval: \"taskAwaitingApproval\",\n conversation_started: \"conversationStarted\",\n};\n\n/** Служебные сообщения короткими строками — для скина без `AgentNoticeView`. Указание агенту (`conversation_started`) не видно. */\nexport function noticesAsText(notices: readonly AgentNotice[], labels: NoticeLabels): string {\n return notices\n .map((notice) => {\n const status = labels[statusLabel[notice.kind]] ?? notice.kind;\n return labels.line(notice.kind === \"conversation_started\" ? \"\" : notice.title, status);\n })\n .join(\"\\n\");\n}\n","import type { FileUIPart } from \"ai\";\nimport type { AgentChatMessage } from \"../chat-types\";\n\n/** Отметка поправки в частях сообщения: написано во время ответа (как у сервера, core: steering/turn-steering.ts). */\nexport const STEER_PART_TYPE = \"data-agent-steer\";\n\n/** Поправка на экране: текст и файлы человека с отметкой поправки. */\nexport function correctionMessage(id: string, text: string, files: readonly FileUIPart[]): AgentChatMessage {\n const parts = [...files, ...(text.trim().length > 0 ? [{ type: \"text\" as const, text }] : []), { type: STEER_PART_TYPE, data: true }];\n return { id, role: \"user\", parts } as AgentChatMessage;\n}\n\nexport function isCorrection(message: Pick<AgentChatMessage, \"role\" | \"parts\">): boolean {\n return message.role === \"user\" && message.parts.some((part) => part.type === STEER_PART_TYPE);\n}\n\n/**\n * Ответы, за которыми идёт поправка: агент закончил их на границе шага, чтобы взять поправку. Пустой такой ответ —\n * не «модель не дала ответа». Строка номеров через пробел: не меняется, пока не меняется состав.\n */\nexport function steeredAnswerIdsOf(feed: readonly AgentChatMessage[]): string {\n return feed.flatMap((message, index) => (message.role === \"assistant\" && feed[index + 1] && isCorrection(feed[index + 1]!) ? [message.id] : [])).join(\" \");\n}\n\n/** Сообщение для `POST …/steer`: отметку поправки сервер ставит сам. */\nexport function outgoingCorrection(correction: AgentChatMessage): AgentChatMessage {\n return { ...correction, parts: correction.parts.filter((part) => part.type !== STEER_PART_TYPE) };\n}\n\n/**\n * Поправки после чтения истории: ждущие на сервере, затем свои, что ещё идут на сервер. Своя, которая уже в ленте\n * или среди ждущих, второй раз не показывается.\n */\nexport function correctionsAfterHistory(\n waiting: readonly AgentChatMessage[],\n shown: readonly AgentChatMessage[],\n own: readonly AgentChatMessage[],\n sendingIds: ReadonlySet<string>,\n): AgentChatMessage[] {\n const known = new Set([...waiting, ...shown].map((message) => message.id));\n return [...waiting, ...own.filter((message) => sendingIds.has(message.id) && !known.has(message.id))];\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ChatModelChoice } from \"./chat-types\";\nimport { useChatLabels } from \"./labels\";\nimport type { ModelChoiceView } from \"./skin-contract\";\n\n/** Список для меню, как его отдаёт `GET {api}/models`. */\ninterface ModelMenu {\n providers: Array<{\n id: string;\n displayName: string;\n models: Array<{ id: string; displayName: string; acceptsImages?: boolean; group?: string; description?: string; tags?: string[]; price?: { inputPerMillion: number; outputPerMillion: number }; reasoning: { levels: string[]; defaultLevel: string | null } }>;\n }>;\n defaultChoice: {\n modelId: string;\n providerId: string;\n reasoning: string | null;\n /** Есть, когда выбор по умолчанию у скрытого провайдера: его нет в `providers`, но показать текущую модель надо. */\n details?: {\n providerName: string;\n modelName: string;\n acceptsImages?: boolean;\n reasoning: { levels: string[]; defaultLevel: string | null };\n };\n };\n}\n\nexport interface UseModelChoiceOptions {\n /** Адрес обработчика чата, например `/api/chat`. */\n api: string;\n chatId: string;\n /** Выбор, сохранённый в чате (из истории). Пока его нет, показывается выбор по умолчанию. */\n savedChoice?: ChatModelChoice | null;\n /**\n * История чата ещё грузится, и выбор чата не пришёл. Пока это так, выбор по умолчанию не показывается: у нового чата\n * выбор человека уже запомнен на сервере, и показ выбора по умолчанию мигнул бы чужой моделью.\n */\n savedChoicePending?: boolean;\n /**\n * Нужно ли меню. `false` — меню не запрашивается: права ещё не пришли с историей или модель скрыта\n * (`modelDetails: false`). По умолчанию `true`.\n */\n menuWanted?: boolean;\n /** Свой `fetch` — для тестов и особых сред. */\n fetch?: typeof globalThis.fetch;\n /**\n * Человек выбрал модель или уровень в меню, и сервер сохранил выбор: получает его, как он сохранён. Выбор по\n * умолчанию, выбор из истории и отказ сервера его не зовут.\n */\n onModelChosen?: (choice: ChatModelChoice) => void;\n}\n\ntype MenuState = { status: \"loading\" } | { status: \"ready\"; menu: ModelMenu } | { status: \"failed\" };\n\nconst unavailable: ModelChoiceView[\"current\"] = { providerId: \"\", modelId: \"\" };\n\n/**\n * Выбор модели и уровня рассуждений для поля ввода. Живёт отдельно от `useAgentChat`, чтобы смена выбора\n * не перерисовывала ленту. Смена видна сразу; при отказе сервера выбор откатывается к последнему сохранённому.\n */\nexport function useModelChoice({ api, chatId, savedChoice, savedChoicePending = false, menuWanted = true, fetch: customFetch, onModelChosen }: UseModelChoiceOptions): ModelChoiceView {\n const labels = useChatLabels();\n // Новая функция `fetch` при перерисовке страницы не перечитывает меню: запросы берут последнюю.\n const fetchRef = useRef(customFetch);\n fetchRef.current = customFetch;\n const request = useCallback<typeof globalThis.fetch>((input, init) => (fetchRef.current ?? globalThis.fetch)(input, init), []);\n const reportChosen = useRef(onModelChosen);\n reportChosen.current = onModelChosen;\n const [menuState, setMenuState] = useState<MenuState>({ status: \"loading\" });\n /** Выбор, сделанный в этой вкладке и ещё не заменённый историей чата. */\n const [override, setOverride] = useState<ChatModelChoice | null>(null);\n const [failed, setFailed] = useState(false);\n /** Последний выбор, подтверждённый сервером; к нему откатывается неудачная смена. */\n const confirmed = useRef<ChatModelChoice | null>(null);\n const latestRequest = useRef(0);\n\n /** Адрес, меню которого уже получено (или не получено окончательно): смена чата его не перечитывает. */\n const menuOf = useRef<string | null>(null);\n useEffect(() => {\n if (!menuWanted || menuOf.current === api) return;\n let cancelled = false;\n setMenuState({ status: \"loading\" });\n void (async () => {\n try {\n const response = await request(`${api}/models`);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const menu = (await response.json()) as ModelMenu;\n if (cancelled) return;\n menuOf.current = api;\n setMenuState({ status: \"ready\", menu });\n } catch {\n if (cancelled) return;\n menuOf.current = api;\n setMenuState({ status: \"failed\" });\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [api, request, menuWanted]);\n\n useEffect(() => {\n latestRequest.current++;\n confirmed.current = null;\n setOverride(null);\n setFailed(false);\n }, [chatId, savedChoice?.modelId, savedChoice?.providerId, savedChoice?.reasoning]);\n\n const menu = menuState.status === \"ready\" ? menuState.menu : null;\n const waitingForSavedChoice = savedChoicePending && !override && !savedChoice;\n const effective = useMemo(\n () => (menu && !waitingForSavedChoice ? effectiveChoice(menu, override ?? savedChoice ?? null) : null),\n [menu, override, savedChoice, waitingForSavedChoice],\n );\n\n const save = useCallback(\n async (next: ChatModelChoice) => {\n const requestNumber = ++latestRequest.current;\n setFailed(false);\n setOverride(next);\n let saved: ChatModelChoice;\n try {\n const response = await request(`${api}/${encodeURIComponent(chatId)}/model`, {\n method: \"PUT\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(next),\n });\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n saved = (await response.json()) as ChatModelChoice;\n confirmed.current = saved;\n if (requestNumber === latestRequest.current) setOverride(saved);\n } catch {\n if (requestNumber !== latestRequest.current) return;\n setOverride(confirmed.current);\n setFailed(true);\n return;\n }\n // Выбор уже сохранён: сбой колбэка приложения его не откатывает.\n try {\n reportChosen.current?.(saved);\n } catch (error) {\n console.error(error);\n }\n },\n [api, chatId, request],\n );\n\n const onChange = useCallback<ModelChoiceView[\"onChange\"]>(\n ({ providerId, modelId }) => {\n if (!menu || !effective) return;\n const model = findModel(menu, providerId, modelId);\n if (!model) return;\n const { levels, defaultLevel } = model.reasoning;\n const kept = effective.reasoning && levels.includes(effective.reasoning) ? effective.reasoning : defaultLevel;\n void save({ providerId, modelId, ...(kept && { reasoning: kept }) });\n },\n [effective, menu, save],\n );\n\n const onReasoningChange = useCallback(\n (levelId: string) => {\n if (effective) void save({ providerId: effective.providerId, modelId: effective.modelId, reasoning: levelId });\n },\n [effective, save],\n );\n\n return useMemo<ModelChoiceView>(() => {\n const error = failed ? labels.modelChoiceFailed : menuState.status === \"failed\" ? labels.modelMenuFailed : undefined;\n if (!menu || !effective) {\n return { providers: [], current: unavailable, onChange, reasoningLevels: [], reasoning: \"\", onReasoningChange, ...(error && { error }) };\n }\n const levels = knownModel(menu, effective.providerId, effective.modelId)?.reasoning.levels ?? [];\n return {\n providers: [\n ...menu.providers.map((provider) => ({\n id: provider.id,\n name: provider.displayName,\n models: provider.models.map((model) => ({\n id: model.id,\n name: model.displayName,\n ...(model.group !== undefined && { group: model.group }),\n ...(model.description !== undefined && { description: model.description }),\n ...(model.tags !== undefined && { tags: model.tags }),\n ...(model.price !== undefined && { price: model.price }),\n ...(typeof model.acceptsImages === \"boolean\" && { acceptsImages: model.acceptsImages }),\n })),\n })),\n ...hiddenCurrent(menu, effective),\n ],\n current: { providerId: effective.providerId, modelId: effective.modelId },\n onChange,\n reasoningLevels: levels.map((id) => ({ id, name: labels.reasoningLevels[id as keyof typeof labels.reasoningLevels] ?? id })),\n reasoning: effective.reasoning ?? \"\",\n onReasoningChange,\n ...(error && { error }),\n };\n }, [effective, failed, labels, menu, menuState.status, onChange, onReasoningChange]);\n}\n\nfunction findModel(menu: ModelMenu, providerId: string, modelId: string) {\n return menu.providers.find((provider) => provider.id === providerId)?.models.find((model) => model.id === modelId);\n}\n\n/** Модель из меню или, если текущий выбор — выбор по умолчанию скрытого провайдера, модель по его `details`. */\nfunction knownModel(menu: ModelMenu, providerId: string, modelId: string) {\n const listed = findModel(menu, providerId, modelId);\n if (listed) return listed;\n const { defaultChoice } = menu;\n if (defaultChoice.providerId !== providerId || defaultChoice.modelId !== modelId) return undefined;\n const { details } = defaultChoice;\n return details && { id: modelId, displayName: details.modelName, acceptsImages: details.acceptsImages, reasoning: details.reasoning };\n}\n\n/**\n * Текущий выбор, которого нет в меню (выбор по умолчанию скрытого провайдера), показывается в списке своим пунктом, чтобы\n * поле не было пустым. Выбрать его вручную нельзя: `onChange` смотрит только в меню.\n */\nfunction hiddenCurrent(menu: ModelMenu, current: ChatModelChoice): ModelChoiceView[\"providers\"] {\n if (findModel(menu, current.providerId, current.modelId)) return [];\n const model = knownModel(menu, current.providerId, current.modelId);\n const { details } = menu.defaultChoice;\n return [\n {\n id: current.providerId,\n name: details?.providerName ?? current.providerId,\n models: [\n {\n id: current.modelId,\n name: model?.displayName ?? current.modelId,\n ...(typeof model?.acceptsImages === \"boolean\" && { acceptsImages: model.acceptsImages }),\n },\n ],\n },\n ];\n}\n\n/** Выбор для показа: заданный, если он есть в меню, иначе выбор по умолчанию; уровень — из уровней модели. */\nfunction effectiveChoice(menu: ModelMenu, wanted: ChatModelChoice | null): ChatModelChoice {\n const candidate = wanted && findModel(menu, wanted.providerId, wanted.modelId) ? wanted : fromDefault(menu);\n const model = knownModel(menu, candidate.providerId, candidate.modelId);\n const { levels, defaultLevel } = model?.reasoning ?? { levels: [], defaultLevel: null };\n const reasoning = candidate.reasoning && levels.includes(candidate.reasoning) ? candidate.reasoning : defaultLevel;\n return { providerId: candidate.providerId, modelId: candidate.modelId, ...(reasoning && { reasoning }) };\n}\n\nfunction fromDefault(menu: ModelMenu): ChatModelChoice {\n const { modelId, providerId, reasoning } = menu.defaultChoice;\n return { modelId, providerId, ...(reasoning && { reasoning }) };\n}\n","import { createContext, useContext, useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { useChatLabels } from \"../labels\";\n\n/** Как часто сменяется фраза ожидания. Таймер тикает раз в секунду, поэтому фраза держится 3–4 с. */\nexport const THINKING_PHRASE_MS = 3_500;\nconst TICK_MS = 1_000;\n\n/** Номер следующей фразы: любой, кроме предыдущего. */\nexport function nextThinkingPhraseIndex(count: number, previous: number, random: () => number = Math.random): number {\n if (count <= 1) return 0;\n const shift = 1 + Math.min(count - 2, Math.floor(random() * (count - 1)));\n return (previous + shift) % count;\n}\n\n/** Часы одного ответа: когда началось ожидание и в каком порядке идут фразы. */\ninterface ThinkingClock {\n startedAt: number;\n phraseIndexAt: (step: number, count: number) => number;\n}\n\nfunction createThinkingClock(startedAt: number): ThinkingClock {\n let order: number[] = [0];\n let orderCount = 0;\n return {\n startedAt,\n phraseIndexAt(step, count) {\n if (count !== orderCount) {\n order = [0];\n orderCount = count;\n }\n while (order.length <= step) order.push(nextThinkingPhraseIndex(count, order[order.length - 1]!));\n return order[step]!;\n },\n };\n}\n\nconst ThinkingClockContext = createContext<ThinkingClock | null>(null);\nconst ThinkingClocksContext = createContext<Map<string, ThinkingClock> | null>(null);\n\n/** Часы всех ответов чата по id: лента выгружает ответ, ушедший из вида, а его счёт сохраняется. */\nexport function ThinkingClocksProvider({ children }: { children: ReactNode }) {\n const [clocks] = useState(() => new Map<string, ThinkingClock>());\n return <ThinkingClocksContext.Provider value={clocks}>{children}</ThinkingClocksContext.Provider>;\n}\n\n/**\n * Общие часы ожидания одного ответа: строка «думает» без текста и блок рассуждений после неё продолжают тот же счёт\n * и ту же череду фраз. `AgentChat` ставит его вокруг каждого ответа агента. С `id` внутри `ThinkingClocksProvider` часы\n * переживают перемонтирование; ответ дописан (`waiting: false`) — часы забываются.\n */\nexport function ThinkingClockProvider({ id, waiting = true, children }: { id?: string; waiting?: boolean; children: ReactNode }) {\n const clocks = useContext(ThinkingClocksContext);\n const [clock] = useState(() => {\n const kept = id === undefined ? undefined : clocks?.get(id);\n if (kept) return kept;\n const created = createThinkingClock(Date.now());\n if (id !== undefined && waiting) clocks?.set(id, created);\n return created;\n });\n\n useEffect(() => {\n if (id !== undefined && !waiting) clocks?.delete(id);\n }, [clocks, id, waiting]);\n\n return <ThinkingClockContext.Provider value={clock}>{children}</ThinkingClockContext.Provider>;\n}\n\nexport interface ThinkingStatus {\n /** Текущая фраза: из `labels.thinkingPhrases`, пустой набор — `labels.reasoningRunning`. */\n phrase: string;\n /** Секунды с начала ожидания ответа. */\n seconds: number;\n /** Те же секунды словами: «12 с», «1 мин 5 с» (`labels.thinkingElapsed`). */\n elapsed: string;\n}\n\n/**\n * Живая надпись, пока модель думает: фраза сменяется каждые 3–4 с без повтора подряд, рядом — время ожидания.\n * Неактивная (`active: false`) таймер не держит. Скин рисует фразу и время сам; для экранного диктора — постоянная\n * `labels.reasoningRunning`, а меняющиеся фразы от него скрыты, чтобы не зачитывались каждые три секунды.\n */\nexport function useThinkingStatus(active = true): ThinkingStatus {\n const labels = useChatLabels();\n const shared = useContext(ThinkingClockContext);\n const [own] = useState(() => createThinkingClock(Date.now()));\n const clock = shared ?? own;\n const [now, setNow] = useState(() => Date.now());\n\n useEffect(() => {\n if (!active) return;\n setNow(Date.now());\n const timer = setInterval(() => setNow(Date.now()), TICK_MS);\n return () => clearInterval(timer);\n }, [active]);\n\n const elapsedMs = Math.max(0, now - clock.startedAt);\n const phrases = labels.thinkingPhrases.length > 0 ? labels.thinkingPhrases : [labels.reasoningRunning];\n const seconds = Math.floor(elapsedMs / 1000);\n return {\n phrase: phrases[clock.phraseIndexAt(Math.floor(elapsedMs / THINKING_PHRASE_MS), phrases.length)]!,\n seconds,\n elapsed: labels.thinkingElapsed(seconds),\n };\n}\n","import type { StepCall, StepPhrases, StepPhraseText } from \"./step-phrases\";\n\n/**\n * Значок шага в ленте работы (договор скина 28): вид шага, а не картинка — рисует скин. Свой вид приложения — строкой;\n * незнакомый скин рисует общим значком (`tool`).\n */\nexport type StepIcon =\n | \"thinking\"\n | \"read\"\n | \"edit\"\n | \"search\"\n | \"web\"\n | \"skill\"\n | \"mcp\"\n | \"image\"\n | \"video\"\n | \"check\"\n | \"snapshot\"\n | \"run\"\n | \"memory\"\n | \"helper\"\n | \"task\"\n | \"tool\"\n | (string & {});\n\n/** Инструменты пакетов SDK: их значки известны точно. */\nconst SDK_TOOL_ICONS: Record<string, StepIcon> = {\n use_skill: \"skill\",\n web_search: \"web\",\n read_page: \"web\",\n page_screenshot: \"snapshot\",\n manage_memory: \"memory\",\n search_earlier_conversation: \"search\",\n check_tasks: \"task\",\n cancel_task: \"task\",\n generate_images: \"image\",\n show_image: \"image\",\n show_video: \"video\",\n};\n\n/** Слова имени инструмента → значок; порядок важен: «run_tests» — проверка, а не запуск. */\nconst WORD_ICONS: Array<[StepIcon, readonly string[]]> = [\n [\"snapshot\", [\"screenshot\", \"snapshot\", \"capture\"]],\n [\"image\", [\"image\", \"images\", \"picture\", \"photo\", \"draw\"]],\n [\"video\", [\"video\"]],\n [\"check\", [\"test\", \"tests\", \"check\", \"verify\", \"lint\", \"validate\", \"typecheck\", \"review\"]],\n [\"edit\", [\"edit\", \"write\", \"patch\", \"replace\", \"update\", \"create\", \"save\", \"apply\", \"delete\", \"remove\", \"rename\", \"move\", \"insert\"]],\n [\"search\", [\"search\", \"find\", \"grep\", \"lookup\", \"query\"]],\n [\"read\", [\"read\", \"open\", \"view\", \"look\", \"get\", \"load\", \"list\", \"cat\", \"show\", \"inspect\"]],\n [\"web\", [\"web\", \"browse\", \"browser\", \"url\", \"http\", \"page\", \"fetch\"]],\n [\"run\", [\"run\", \"exec\", \"execute\", \"bash\", \"shell\", \"command\", \"terminal\", \"build\", \"install\", \"deploy\"]],\n [\"skill\", [\"skill\", \"skills\"]],\n [\"memory\", [\"memory\", \"remember\", \"note\", \"notes\"]],\n];\n\n/** Слова имени: `edit_file`, `edit-file`, `editFile` → «edit», «file». */\nconst wordsOf = (toolName: string): string[] =>\n toolName\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(Boolean);\n\n/** Значок по имени инструмента: пакеты SDK, помощники `helper_*`, MCP `<префикс>__<имя>`, слова имени; иначе `tool`. */\nexport function guessStepIcon(toolName: string): StepIcon {\n const known = SDK_TOOL_ICONS[toolName];\n if (known) return known;\n if (toolName.startsWith(\"helper_\")) return \"helper\";\n if (toolName.includes(\"__\")) return \"mcp\";\n const words = wordsOf(toolName);\n return WORD_ICONS.find(([, keys]) => words.some((word) => keys.includes(word)))?.[0] ?? \"tool\";\n}\n\nconst iconOfText = (value: unknown): StepIcon | undefined => (typeof value === \"string\" && value.trim() ? value.trim() : undefined);\n\n/**\n * Значок шага: значок приложения рядом с фразой (`stepPhrases`, `icon`), запасной слоя поведения (`fallback`), иначе\n * по имени (`guessStepIcon`). Ошибка функции фраз — угадывание, чат не ломается.\n */\nexport function stepIconOf(call: StepCall, { phrases, fallback }: { phrases?: StepPhrases | undefined; fallback?: Pick<StepPhraseText, \"icon\"> | undefined } = {}): StepIcon {\n const entry = phrases && Object.hasOwn(phrases, call.toolName) ? phrases[call.toolName] : undefined;\n let own: unknown;\n try {\n own = typeof entry === \"function\" ? entry(call)?.icon : entry?.icon;\n } catch {\n own = undefined;\n }\n return iconOfText(own) ?? iconOfText(fallback?.icon) ?? guessStepIcon(call.toolName);\n}\n","import type { ComposerOptions } from \"./composer-options\";\nimport { getToolName, isToolUIPart } from \"ai\";\nimport type { DynamicToolUIPart, ToolUIPart } from \"ai\";\nimport { Fragment, memo, useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentProps, ElementType, ReactNode } from \"react\";\nimport { ALWAYS_IN_CHAT, ALWAYS_OFFERED, messageText } from \"./chat-types\";\nimport { stripVoiceTags } from \"./voice-tags\";\nimport { useVoiceAnswerIds } from \"./voice-turn\";\nimport type { AgentChatMessage, AnswerStatus, ApprovalDecision, ApprovalFailureKind, ChatModelChoice, CompactionMarker } from \"./chat-types\";\nimport { partKey, useExpandedParts, usePartOpen } from \"./expanded-parts\";\nimport type { ExpandedParts } from \"./expanded-parts\";\nimport type { EditErrorKind } from \"./labels\";\nimport { useFeedAnnouncement } from \"./feed-announcement\";\nimport { isLiveTask, taskElapsedMs, useBackgroundTasks } from \"./background-tasks/use-background-tasks\";\nimport { customErrorText, useChatLabels } from \"./labels\";\nimport { agentNoticesOf, appNoticesOf, isAgentNoticePart, noticesAsText, noticeViewOf, staffRepliesOf } from \"./notices/agent-notices\";\nimport type { AppNotice, NoticeTaskControls, StaffReply } from \"./notices/agent-notices\";\nimport type { ChatLabels, ResolvedChatLabels } from \"./labels\";\nimport type { SpendingView } from \"./spending-status\";\nimport { MessageFeed } from \"./message-feed\";\nimport { useMessageEditing } from \"./message-editing/use-message-editing\";\nimport type { MessageFeedHandle } from \"./message-feed\";\nimport type { AgentChatExtensions, NoticeLines } from \"./agent-chat-extensions\";\nimport { answerEdited } from \"./answer-editing/answer-editing\";\nimport type { AnswerEditing, AnswerEditMark } from \"./answer-editing/answer-editing\";\nimport { createDiagramRenders } from \"./diagrams/diagram-renders\";\nimport type { DiagramRenders } from \"./diagrams/diagram-renders\";\nimport { countMermaidBlocks, useDiagramBlocks } from \"./diagrams/use-diagram-blocks\";\nimport { useDiagramColors } from \"./diagrams/use-diagram-colors\";\nimport { useDemoColors, useLiveDemoBlocks } from \"./live-demos/use-live-demo-blocks\";\nimport { collectViewerItems } from \"./media-viewer/collect-viewer-items\";\nimport { answerFileViews } from \"./media-viewer/answer-file-images\";\nimport { SubjectChatLoading } from \"./subject-chat/subject-chat-loading\";\nimport { useSubjectChatId } from \"./subject-chat/use-subject-chat-id\";\nimport { describeWithViewer } from \"./media-viewer/open-attachments\";\nimport { describeGeneratedImages, generatedViewerItems, imageClientsOf } from \"./media-viewer/generated-images\";\nimport type { DescribeGeneratedImages } from \"./media-viewer/generated-images\";\nimport { useMediaViewer } from \"./media-viewer/use-media-viewer\";\nimport type { MediaViewerController } from \"./media-viewer/use-media-viewer\";\nimport { suggestedRepliesOf } from \"./widgets/suggested-replies\";\nimport type { WidgetFailureKind } from \"./widgets/use-widget-answer\";\nimport { SUGGEST_REPLIES, widgetCallOf } from \"./widgets/widget-view\";\nimport { WidgetPart } from \"./widgets/widget-part\";\nimport { helperCallViewOf, isHelperPart } from \"./helpers/helper-call\";\nimport type { AttachmentsPlugin, MessageAttachmentsView } from \"./attachments-contract\";\nimport type { AgentVideoView, ChatHeaderView, ChatSkin, DiagramColors, LiveDemoColors, MessageActionView, ModelChoiceView, ToolApprovalView, ToolCallState } from \"./skin-contract\";\nimport { useAgentChat } from \"./use-agent-chat\";\nimport { useModelChoice } from \"./use-model-choice\";\nimport { useVoiceDictation } from \"./use-voice-dictation\";\nimport type { VoiceInput } from \"./voice-input\";\nimport type { VoiceDialogEngine } from \"./voice-dialog\";\nimport { useVoiceDialog } from \"./use-voice-dialog\";\nimport type { AppRefusal } from \"./app-refusal\";\nimport type { ChatAccess } from \"./chat-access\";\nimport type { ServerRefusal } from \"./server-refusal\";\nimport type { ChatRefusal, ConnectionState, EditRefusalReason, LivenessOptions } from \"./use-agent-chat\";\nimport type { AgentAppBridge } from \"./app-bridge/app-bridge\";\nimport { useMessageActionRuns } from \"./message-actions\";\nimport type { MessageAction, MessageActionTarget } from \"./message-actions\";\nimport { renderComposerActions } from \"./composer-actions\";\nimport type { ComposerActions } from \"./composer-actions\";\nimport { useTurnFinishedReport } from \"./turn-finished\";\nimport { appEventOfPart, useAppEventReport } from \"./app-events\";\nimport type { AppEvent, AppEventHandler } from \"./app-events\";\nimport type { TurnFinishedEvent } from \"./turn-finished\";\nimport { ThinkingClockProvider, ThinkingClocksProvider } from \"./thinking-status/thinking-status\";\nimport { ChatStateContext } from \"./chat-state/chat-state\";\nimport type { ChatStateValues } from \"./chat-state/chat-state\";\nimport { steeredAnswerIdsOf } from \"./steering/corrections\";\nimport { workLayoutOf } from \"./work-view/work-layout\";\nimport type { AnswerEntry, WorkStep } from \"./work-view/work-layout\";\nimport { WorkStepsPart } from \"./work-view/work-steps-part\";\n\ninterface FeedExtensions extends AgentChatExtensions {\n reasoningVisible?: boolean;\n modelNames?: ModelChoiceView[\"providers\"];\n}\n\nexport interface AgentChatProps extends AgentChatExtensions {\n /** Optional application heading and trailing content, rendered by the selected skin. */\n header?: ChatHeaderView;\n /** Application-owned result. Shown after loaded history while idle; never changes server status. Null/false removes it. */\n completion?: ReactNode;\n composerOptions?: ComposerOptions;\n /** Адрес обработчика чата, например `/api/chat`. */\n api: string;\n /** Какой чат открыть. Нужен ровно один из `chatId` и `subject`: оба сразу или ни одного — ошибка разработчика (исключение). */\n chatId?: string;\n /**\n * Ключ объекта приложения (урок, заказ), например `\"lesson:42\"`: при открытии чат спрашивает у сервера\n * `POST {api}/subject` чат человека по этому ключу (нет — сервер создаёт, с приветствием) и дальше работает с ним как\n * с обычным. Отказ сервера показывается текстом. Нужен `subjectChats` в обработчике чата.\n */\n subject?: string;\n skin: ChatSkin;\n /** Сроки живой связи; по умолчанию как в договоре 002. */\n liveness?: LivenessOptions;\n /** Голосовой ввод; без него кнопки микрофона в поле ввода нет. */\n voice?: VoiceInput;\n /** Голосовой режим («разговор»): `createVoiceDialogEngine` из `@ykurilov/agent-chat-voice-dialog`. Без него кнопки режима нет. */\n voiceDialog?: VoiceDialogEngine;\n /** Вложения к сообщениям: `attachmentsClient` из `@ykurilov/agent-chat-attachments/react`. Нет — чат только текстовый. */\n attachments?: AttachmentsPlugin;\n /** У сервера включены лимиты расходов (`limits` в обработчике чата): показывать предупреждение и исчерпание. */\n spendingLimits?: boolean;\n /** Чат занят (идёт ответ) или свободен; вызывается при открытии чата и при каждой смене. Для расширений вокруг чата, например списка чатов. */\n onBusyChange?: (busy: boolean) => void;\n /** Связь с приложением (место на экране, изменения данных, браузерные инструменты); её создаёт `AgentPanelProvider`. Нет — чат как раньше. */\n appBridge?: AgentAppBridge;\n /** Текст в поле ввода при открытии чата. */\n initialDraft?: string;\n /** Правка ответов агента (песочница): кнопка правки у законченных ответов, отметки «изменено», редактор страницы. Нет — ответы не правятся. */\n answerEditing?: AnswerEditing;\n /** Сообщения текущей ветки и занятость чата; вызывается при каждой смене. Для панелей рядом с чатом, например журнала песочницы. */\n onFeedChange?: (feed: { messages: AgentChatMessage[]; busy: boolean }) => void;\n /** Свои кнопки под ответами агента («Нравится», «В заметки»): рисует скин в ряду действий ответа. Массив держите постоянным. */\n messageActions?: MessageAction[];\n /** Ход закончен: у ответа `messageId` итог `status` (включая остановку и ошибку). Один раз на ход; открытие чата хода не даёт. */\n onTurnFinished?: (turn: TurnFinishedEvent) => void;\n /** Свои кнопки у поля ввода («Проверить работу»): узел или функция от помощника (`ComposerHelper`: поле, `send`, `addFiles`). Нет — кнопок нет. */\n composerActions?: ComposerActions;\n /**\n * События приложения (`afterTurn`, `emitAppEvent`, `publishAppEvent` на сервере): по разу на событие. `live: true` —\n * пришло живьём (во время хода или вне хода через канал наблюдения), `false` — лежало в истории при открытии чата.\n * У события вне хода нет `messageId`. Вид события в ответе задаёт `appEvents`.\n */\n onAppEvent?: AppEventHandler;\n /**\n * Приложение отказало ходу (`beforeTurn`) или человеку в доступе (`resolveUser`): `kind` — `turn_refused` или `access_refused`,\n * `code` — для программы, `message` — слова для человека (до 300 знаков; чат их уже показал). Зовётся на каждый такой отказ,\n * в том числе когда сервер не открыл чат объекта (`subject`).\n */\n onTurnRefused?: (refusal: AppRefusal) => void;\n /**\n * Права человека в чате и закрытие (`closed`, `null` — открыт): после загрузки истории и при каждой смене (отказ\n * `chat_closed`, закрытие инструментом, события `chat-closed` и `chat-reopened`). Кнопки чат прячет сам.\n */\n onChatAccessChange?: (access: ChatAccess) => void;\n /**\n * Состояние чата показанной ветки (`model.state` на сервере) после загрузки истории и при каждой смене: запись по\n * ходу ответа, смена ветки, правка. То же значение повторно не приходит. Внутри ленты — `useChatState()`.\n */\n onStateChange?: (state: ChatStateValues) => void;\n /**\n * Человек выбрал модель или уровень рассуждений в меню, и сервер сохранил выбор. Выбор по умолчанию и выбор из истории\n * его не зовут. На сервере то же сообщает хук `onModelChosen`.\n */\n onModelChosen?: (choice: ChatModelChoice) => void;\n /**\n * Служебный, для `ChatWorkspace`: приложению не нужен. Чат не открылся и прав не будет: его нет, приложение не пустило\n * человека или история не загрузилась. Пока права неизвестны, список чатов скрыт, а в этих случаях он нужен,\n * чтобы выбрать другой чат.\n */\n onChatUnavailable?: () => void;\n /**\n * Другое значение — история перечитывается с сервера: права, закрытие и лента. Для случая, когда чат изменили не\n * через этот экран (например, песочница открыла закрытый чат снова). Первое значение ничего не перечитывает.\n */\n reloadKey?: number;\n /**\n * Поправки на лету: пока идёт ответ, поле принимает текст, агент учтёт его на ближайшем шаге. Включено, если скин их\n * рисует (`ChatSkin.steering`, договор 21); `false` — выключить и у такого скина. Скин без `steering` — как до 0.34.\n */\n steering?: boolean;\n}\n\n/** Лента чата с агентом. Всё видимое рисуют компоненты скина. */\nexport function AgentChat(props: AgentChatProps) {\n if (props.chatId !== undefined && props.subject !== undefined) throw new Error(\"AgentChat: pass either chatId or subject, not both.\");\n if (props.subject !== undefined) return <SubjectAgentChat {...props} subject={props.subject} />;\n if (props.chatId === undefined) throw new Error(\"AgentChat: chatId or subject is required.\");\n // Плагин вложений — хук: пока он есть или нет, порядок хуков внутри неизменен, а смена превращается в новый экземпляр.\n // Другой чат — новый экземпляр, как у чата объекта: состояние ленты (в том числе учёт событий приложения) не\n // переходит из чата в чат.\n return <AgentChatBody key={`${props.chatId}:${props.attachments ? \"attachments\" : \"plain\"}`} {...props} chatId={props.chatId} />;\n}\n\n/** Чат объекта: сначала id чата по ключу у сервера, потом обычный чат; отказ — текстом с «Повторить». */\nfunction SubjectAgentChat(props: AgentChatProps & { subject: string }) {\n const found = useSubjectChatId(props.api, props.subject, props.onTurnRefused);\n if (found.state === \"ready\") return <AgentChatBody key={`${found.chatId}:${props.attachments ? \"attachments\" : \"plain\"}`} {...props} chatId={found.chatId} />;\n if (found.state === \"refused\") return <props.skin.ErrorNotice kind=\"other\" text={found.text} onRetry={found.retry} />;\n if (found.state === \"failed\") return <props.skin.ErrorNotice kind=\"other\" onRetry={found.retry} />;\n return <SubjectChatLoading skin={props.skin} />;\n}\n\nfunction AgentChatBody({ header, completion, composerOptions, api, chatId, skin, liveness, voice, voiceDialog, attachments: attachmentsPlugin, spendingLimits, onBusyChange, appBridge, initialDraft, answerEditing, onFeedChange, messageActions, onTurnFinished, onTurnRefused, composerActions, onAppEvent, onChatAccessChange, onStateChange, onModelChosen, onChatUnavailable, reloadKey, steering, widgets, diagrams, liveDemos, images, videos, appEvents, workView, stepPhrases, noticeLines, answerDetails }: AgentChatProps & { chatId: string }) {\n const spendingNoticeId = useId();\n const closedNoticeId = useId();\n const chat = useAgentChat({\n api,\n chatId,\n ...(liveness && { liveness }),\n ...(spendingLimits && { spendingLimits }),\n ...(appBridge && { appBridge }),\n ...(initialDraft !== undefined && { initialDraft }),\n ...(onTurnRefused && { onTurnRefused }),\n ...(reloadKey !== undefined && { reloadKey }),\n // Поправки — только у скина, который их рисует: иначе текст ушёл бы поправкой без «Стопа» рядом и без пометки.\n steering: skin.steering === true && steering !== false,\n // Событие хука после «Стопа» сообщит учёт ленты, когда перечитанная история принесёт его часть.\n onAppEvent: (event: AppEvent, messageId?: string) => (messageId ? appEventReport.expectLive(messageId) : onAppEvent?.(event, { live: true })),\n onExpectLiveAppEvents: (messageId: string) => appEventReport.expectLive(messageId),\n });\n const backgroundTasks = useBackgroundTasks({\n chatUrl: `${api}/${encodeURIComponent(chatId)}`,\n enabled: chat.loaded && chat.backgroundTasksAvailable && Boolean(skin.TaskCard || skin.AgentNoticeView),\n version: chat.tasksVersion,\n });\n // Выбор чата приходит с историей; пока история грузится (и не сорвалась), выбор по умолчанию не показывается.\n const savedChoicePending = !chat.loaded && !chat.loadFailed && !chat.notFound;\n // Меню — после истории: права приходят с ней. Нужно оно для выбора модели; без выбора — только вложениям, если сервер\n // (старый) не прислал, видит ли модель картинки. Скрытая модель меню не запрашивает никогда.\n const menuWanted =\n !savedChoicePending &&\n chat.capabilities.modelDetails !== false &&\n (chat.capabilities.modelChoice || (attachmentsPlugin !== undefined && chat.modelAcceptsImages === undefined));\n const modelChoice = useModelChoice({ api, chatId, savedChoice: chat.savedModelChoice, savedChoicePending, menuWanted, onModelChosen });\n // Меню не нужно — и прежнее в памяти не в счёт: выбор мог смениться без него. Видит ли модель картинки — по истории.\n const chatModelSeesImages = menuWanted ? (modelSeesImages(modelChoice) ?? chat.modelAcceptsImages) : chat.modelAcceptsImages;\n const dictation = useVoiceDictation({ voice, value: chat.value, setValue: chat.setValue, chatId });\n const dialog = useVoiceDialog({ engine: voiceDialog, chatId, chat, ...(videos && { videos }) });\n const attachments = attachmentsPlugin?.useController({ api, chatId, modelSeesImages: chatModelSeesImages });\n const describeFiles = attachments?.describe;\n const imageClients = useSameList(imageClientsOf(images));\n const [localReasoningVisible, setLocalReasoningVisible] = useState(true);\n const showReasoning = composerOptions?.reasoningVisibility?.value ?? localReasoningVisible;\n const effectiveComposerOptions = useMemo(() => ({ ...composerOptions, reasoningVisibility: composerOptions?.reasoningVisibility ?? { value: showReasoning, onChange: setLocalReasoningVisible } }), [composerOptions, showReasoning]);\n const showAnswerDetails = answerDetails ?? composerOptions?.variant === \"extended\";\n // Only catalog names affect past answers; changing the current model/effort keeps this reference stable.\n const answerNamesKey = showAnswerDetails ? JSON.stringify(modelChoice.providers.map(({ id, name, models }) => ({ id, name, models: models.map(({ id, name }) => ({ id, name })) }))) : \"\";\n const answerModelNames = useMemo<ModelChoiceView[\"providers\"] | undefined>(() => answerNamesKey ? JSON.parse(answerNamesKey) as ModelChoiceView[\"providers\"] : undefined, [answerNamesKey]);\n // Свойства страницы несутся вниз одним стабильным объектом: ввод в поле ответы в ленте не перерисовывает.\n const extensions = useMemo<FeedExtensions>(\n () => ({\n reasoningVisible: showReasoning,\n answerDetails: showAnswerDetails,\n ...(answerModelNames && { modelNames: answerModelNames }),\n ...(widgets && { widgets }),\n ...(diagrams && { diagrams }),\n ...(liveDemos && { liveDemos }),\n ...(imageClients.length > 0 && { images: imageClients }),\n ...(videos && { videos }),\n ...(appEvents && { appEvents }),\n ...(workView && { workView }),\n ...(stepPhrases && { stepPhrases }),\n ...(noticeLines && { noticeLines }),\n }),\n [widgets, diagrams, liveDemos, imageClients, videos, appEvents, workView, stepPhrases, noticeLines, showReasoning, showAnswerDetails, answerModelNames],\n );\n const scrollRef = useRef<HTMLDivElement>(null);\n const feedRef = useRef<MessageFeedHandle>(null);\n const labels = useChatLabels();\n const expanded = useExpandedParts(chatId);\n const announcement = useFeedAnnouncement(chat.messages, chat.busy, chatId);\n const { busy, canRetry, retry, canEdit, canRegenerate, regenerate, edit, selectBranch, respondToApproval, approvalPendingId, approvalFailure } = chat;\n const { answerWidget, widgetPendingId, widgetFailure, sendReply, capabilities, closed } = chat;\n const chatClosed = closed !== null;\n const reportAccess = useRef(onChatAccessChange);\n reportAccess.current = onChatAccessChange;\n useEffect(() => {\n if (chat.loaded) reportAccess.current?.({ capabilities, closed });\n }, [capabilities, chat.loaded, closed]);\n const { chatState } = chat;\n const reportState = useRef(onStateChange);\n reportState.current = onStateChange;\n useEffect(() => {\n if (chat.loaded) reportState.current?.(chatState);\n }, [chat.loaded, chatState]);\n const reportUnavailable = useRef(onChatUnavailable);\n reportUnavailable.current = onChatUnavailable;\n const unavailable = chat.notFound || chat.loadFailed || chat.accessRefusal !== null;\n useEffect(() => {\n if (unavailable) reportUnavailable.current?.();\n }, [unavailable]);\n /**\n * «Ответ отправлен» для диктора: звучит, пока лента не скажет своё — итог продолжения хода. `feed` — что лента\n * говорила в миг отправки.\n */\n const [widgetAnnouncement, setWidgetAnnouncement] = useState<{ text: string; feed: string; chatId: string } | null>(null);\n const announced =\n widgetAnnouncement && widgetAnnouncement.chatId === chatId && (announcement === \"\" || announcement === widgetAnnouncement.feed)\n ? widgetAnnouncement.text\n : announcement;\n const announcementRef = useRef(announcement);\n announcementRef.current = announcement;\n const onWidgetAnswer = useCallback(\n (toolCallId: string, answer: unknown) =>\n void answerWidget(toolCallId, answer, () => setWidgetAnnouncement({ text: labels.widgets.answerSent, feed: announcementRef.current, chatId })),\n [answerWidget, chatId, labels],\n );\n const suggestedReplies = useMemo(() => suggestedRepliesOf(chat.messages, busy), [busy, chat.messages]);\n const exhaustedSpending = chat.spending?.state === \"exhausted\";\n const pickReply = useCallback(\n (text: string) => {\n if (sendReply(text)) feedRef.current?.scrollToBottom();\n },\n [sendReply],\n );\n const reportBusy = useRef(onBusyChange);\n reportBusy.current = onBusyChange;\n useEffect(() => {\n reportBusy.current?.(busy);\n }, [busy, chatId]);\n const lastIndex = chat.messages.length - 1;\n // Поправки, которые агент ещё не взял, идут в ленте после сообщений, с пометкой.\n const { corrections } = chat;\n const feedMessages = useMemo(() => (corrections.length > 0 ? [...chat.messages, ...corrections] : chat.messages), [chat.messages, corrections]);\n const correctionIds = useMemo(() => new Set(corrections.map((correction) => correction.id)), [corrections]);\n const steeredAnswerIdsKey = steeredAnswerIdsOf(feedMessages);\n const steeredAnswerIds = useMemo(() => new Set(steeredAnswerIdsKey.split(\" \")), [steeredAnswerIdsKey]);\n const reportFeed = useRef(onFeedChange);\n reportFeed.current = onFeedChange;\n useEffect(() => {\n reportFeed.current?.({ messages: chat.messages, busy });\n }, [busy, chat.messages]);\n useTurnFinishedReport(chatId, busy, chat.messages, onTurnFinished);\n const appEventReport = useAppEventReport(chatId, chat.loaded, busy, chat.messages, onAppEvent);\n const actionRuns = useMessageActionRuns(chatId);\n const dividers = useMemo(() => dividersByMessage(chat.compactions), [chat.compactions]);\n const voiceAnswers = useVoiceAnswerIds(chat.messages);\n const forkByMessage = useMemo(() => new Map(chat.forks.map((fork) => [fork.messageId, fork])), [chat.forks]);\n /** Ответ агента, на месте которого стоит редактор страницы. */\n const [editingAnswerId, setEditingAnswerId] = useState<string | null>(null);\n const saving = useRef(false);\n const composerValue = useRef(chat.value);\n composerValue.current = chat.value;\n const { setValue: setComposerValue } = chat;\n // Сообщения с открытым полем правки больше нет: набранное уходит в поле ввода, а не пропадает.\n const draftLost = useCallback(\n (text: string) => setComposerValue(composerValue.current.trim().length > 0 ? `${composerValue.current}\\n\\n${text}` : text),\n [setComposerValue],\n );\n /** Открытое поле правки; после отказа открывается снова с тем, что печатал собеседник, и ошибкой. */\n const editing = useMessageEditing(chat.messages, { saving, onDraftLost: draftLost });\n const editingId = editing.id;\n /** Лента открывается заново при смене ветки: замеры прежних сообщений новым не подходят. */\n const [feedView, setFeedView] = useState<{ epoch: number; openAtMessageId?: string }>({ epoch: 0 });\n // Окно просмотра: карусель — картинки текущей ветки; другой чат или ветка закрывают окно.\n // Картинки агента — только у скина с `GeneratedImages`: без него вызов остаётся строкой инструмента и в карусель не идёт.\n const imagesShown = imageClients.length > 0 && skin.GeneratedImages ? imageClients : undefined;\n const viewerImages = useMemo(() => imagesShown && generatedViewerItems(describeGeneratedImages(imagesShown, { api, chatId })), [imagesShown, api, chatId]);\n const getViewerItems = useCallback(\n () => collectViewerItems({ messages: chat.messages, describeAttachments: describeFiles, extraItems: viewerImages }),\n [chat.messages, describeFiles, viewerImages],\n );\n const mediaViewer = useMediaViewer({ getItems: getViewerItems, resetKey: `${chatId}:${feedView.epoch}` });\n const describeImages = useMemo(\n () => imagesShown && describeGeneratedImages(imagesShown, { api, chatId, open: skin.MediaViewer && mediaViewer.open }),\n [imagesShown, api, chatId, skin.MediaViewer, mediaViewer.open],\n );\n const describeAttachments = useMemo(\n () => describeWithViewer(describeFiles, skin.MediaViewer && mediaViewer.open),\n [describeFiles, skin.MediaViewer, mediaViewer.open],\n );\n // Схемы: готовые рисунки помнит чат, цвета следуют теме страницы; без `diagrams` ничего этого нет.\n const diagramRenders = useMemo(() => diagrams && createDiagramRenders(diagrams), [diagrams]);\n const diagramColors = useDiagramColors(skin, diagrams !== undefined);\n const diagramSetup = useMemo<DiagramSetup | undefined>(\n () =>\n diagramRenders && diagramColors\n ? { renders: diagramRenders, colors: diagramColors, ...(skin.MediaViewer && { open: mediaViewer.open }) }\n : undefined,\n [diagramColors, diagramRenders, mediaViewer.open, skin.MediaViewer],\n );\n // Живые демонстрации: цвета следуют теме страницы; без `liveDemos` за темой никто не следит.\n const demoColors = useDemoColors(skin, liveDemos === true);\n // В чате чужой текст (сервер: история или кусок потока) — демонстрации показываются кодом, рамки не запускаются.\n const demos = useMemo<LiveDemoSetup | undefined>(() => demoColors && { colors: demoColors, blocked: chat.externalContent }, [demoColors, chat.externalContent]);\n const switchBranch = useCallback(\n async (messageId: string) => {\n if (await selectBranch(messageId)) setFeedView((view) => ({ epoch: view.epoch + 1, openAtMessageId: messageId }));\n },\n [selectBranch],\n );\n // Готовые переключатели по сообщениям: тот же элемент не даёт `memo` сообщений с развилками сбрасываться на каждый кусок ответа.\n const switchers = useMemo(() => new Map<string, ReactNode>(), [busy, capabilities.branches, forkByMessage, skin, switchBranch]);\n const switcherOf = useCallback(\n (messageId: string): ReactNode => {\n const cached = switchers.get(messageId);\n if (cached !== undefined) return cached;\n const fork = forkByMessage.get(messageId);\n // Ветки запрещены правом или чат закрыт: стрелок нет совсем.\n if (!fork || !capabilities.branches) return undefined;\n const position = fork.siblingIds.indexOf(messageId);\n if (position < 0) return undefined;\n const before = fork.siblingIds[position - 1];\n const after = fork.siblingIds[position + 1];\n const element = (\n <skin.BranchSwitcher\n position={position + 1}\n count={fork.siblingIds.length}\n {...(!busy && before !== undefined && { onPrevious: () => void switchBranch(before) })}\n {...(!busy && after !== undefined && { onNext: () => void switchBranch(after) })}\n />\n );\n switchers.set(messageId, element);\n return element;\n },\n [busy, capabilities.branches, forkByMessage, skin, switchBranch, switchers],\n );\n /** Поле правки закрывается, только когда сервер принял правку; иначе остаётся с текстом и ошибкой. */\n const saveEdit = useCallback(\n async (messageId: string, text: string) => {\n if (saving.current) return;\n saving.current = true;\n try {\n const outcome = await edit(messageId, text);\n if (outcome.status === \"started\") {\n editing.close();\n feedRef.current?.scrollToBottom();\n return;\n }\n const kind: EditErrorKind =\n outcome.status === \"refused\"\n ? EDIT_REFUSAL_ERRORS[outcome.reason]\n : outcome.reason === \"busy\" || outcome.reason === \"offline\"\n ? outcome.reason\n : \"rejected\";\n const messageLimit = outcome.status === \"refused\" && outcome.reason === \"spending_limit\" && outcome.unit === \"messages\";\n const error = (outcome.status === \"refused\" && outcome.text) || (messageLimit ? labels.editMessageLimit : labels.editErrors[kind]);\n editing.refuse(messageId, text, error);\n } finally {\n saving.current = false;\n }\n },\n [edit, editing, labels],\n );\n const { tasks: backgroundTaskList, decisions: taskDecisions, decide: decideTask } = backgroundTasks;\n const noticeControls = useMemo<NoticeTaskControls>(\n () => ({ tasks: backgroundTaskList, decisions: taskDecisions, decide: (taskId, approvalId, decision) => void decideTask(taskId, approvalId, decision), failureText: labels.approvalFailed }),\n [backgroundTaskList, decideTask, labels, taskDecisions],\n );\n // Ввод в поле не должен трогать ленту: renderMessage меняется только вместе с тем, что рисует.\n const renderMessage = useCallback(\n (message: AgentChatMessage, index: number) => {\n if (correctionIds.has(message.id)) return <UserMessage skin={skin} message={message} describe={describeAttachments} branchSwitcher={undefined} pending />;\n const branchSwitcher = switcherOf(message.id);\n const NoticeView = skin.AgentNoticeView;\n const notices = agentNoticesOf(message);\n const staffReplies = staffRepliesOf(message);\n const appNotices = appNoticesOf(message);\n // Пометки приложения — серыми строками перед всем остальным в сообщении; только они — больше ничего.\n const serviceLines = appNotices.length > 0 && <ServiceLines skin={skin} notices={appNotices} lines={extensions.noticeLines} />;\n const onlyAppNotices = appNotices.length > 0 && notices.length === 0 && staffReplies.length === 0 && message.parts.every(isAgentNoticePart);\n const content =\n onlyAppNotices ? (\n <></>\n ) : staffReplies.length > 0 ? (\n <StaffReplies skin={skin} replies={staffReplies} label={labels.notices.staffSaid} />\n ) : notices.length > 0 ? (\n NoticeView ? (\n <>\n {notices.map((notice, noticeIndex) => (\n <NoticeView key={noticeIndex} {...noticeViewOf(notice, noticeControls)} />\n ))}\n </>\n ) : (\n // Скин без плашек (v16): короткая строка из словаря, не пустой пузырь; править служебное сообщение нельзя.\n <skin.UserMessage text={noticesAsText(notices, labels.notices)} {...(branchSwitcher && { branchSwitcher })} />\n )\n ) : message.role === \"user\" ? (\n editingId === message.id ? (\n <MessageEditorRow\n key={editing.attempt}\n skin={skin}\n initialText={editing.draftOf(message)}\n describe={describeAttachments}\n message={message}\n {...(editing.error && { error: editing.error })}\n onDraftChange={editing.type}\n onSave={(text) => void saveEdit(message.id, text)}\n onCancel={editing.close}\n />\n ) : (\n <UserMessage\n skin={skin}\n message={message}\n describe={describeAttachments}\n branchSwitcher={branchSwitcher}\n {...(canEdit && { onEdit: () => editing.open(message.id) })}\n />\n )\n ) : editingAnswerId === message.id && answerEditing ? (\n <>\n {answerEditing.renderEditor({\n message,\n onSaved: (newMessageId) => {\n setEditingAnswerId(null);\n void switchBranch(newMessageId);\n },\n onCancel: () => setEditingAnswerId(null),\n })}\n </>\n ) : (\n <AgentAnswer\n skin={skin}\n extensions={extensions}\n {...(diagramSetup && { diagrams: diagramSetup })}\n {...(demos && { demos })}\n describeImages={describeImages}\n describeFiles={describeAttachments}\n message={message}\n voiceAnswer={voiceAnswers.has(message.id)}\n steered={steeredAnswerIds.has(message.id)}\n expanded={expanded}\n status={busy && index === lastIndex ? \"streaming\" : (message.metadata?.status ?? \"complete\")}\n branchSwitcher={branchSwitcher}\n {...(index === lastIndex && canRetry && { onRetry: retry })}\n onApproval={respondToApproval}\n approvalPendingId={approvalPendingId}\n approvalFailure={approvalFailure}\n awaitingHuman={index === lastIndex && !busy && !chatClosed && message.metadata?.status === \"awaiting_approval\"}\n chatClosed={chatClosed}\n chatId={chatId}\n onWidgetAnswer={onWidgetAnswer}\n widgetPendingId={widgetPendingId}\n widgetFailure={widgetFailure}\n {...(index === lastIndex && suggestedReplies && skin.SuggestedReplies && !chatClosed && {\n suggestedReplies: <skin.SuggestedReplies replies={suggestedReplies} onPick={pickReply} disabled={exhaustedSpending} />,\n })}\n {...(index === lastIndex && canRegenerate && { onRegenerate: regenerate })}\n {...(answerEditing && { editMark: answerEditing.edits.get(message.id) })}\n {...(answerEditing && capabilities.answerEditing && !busy && (message.metadata?.status ?? \"complete\") === \"complete\" && {\n onEdit: () => setEditingAnswerId(message.id),\n })}\n {...(messageActions && messageActions.length > 0 && {\n actions: messageActions,\n last: index === lastIndex,\n runningActionIds: actionRuns.running.get(message.id),\n onRunAction: actionRuns.run,\n })}\n />\n );\n const shown = serviceLines ? (\n <>\n {serviceLines}\n {content}\n </>\n ) : (\n content\n );\n const markers = dividers.get(message.id);\n // Разделитель живёт в элементе ленты сообщения, после которого стоит: индексы и замеры ленты не меняются.\n return markers ? (\n <>\n {shown}\n {markers.map((marker) => (\n <CompactionDividerRow key={marker.id} skin={skin} marker={marker} expanded={expanded} />\n ))}\n </>\n ) : (\n shown\n );\n },\n [\n actionRuns,\n answerEditing,\n approvalFailure,\n approvalPendingId,\n busy,\n canEdit,\n canRegenerate,\n canRetry,\n capabilities.answerEditing,\n chatClosed,\n correctionIds,\n describeAttachments,\n diagramSetup,\n demos,\n describeImages,\n dividers,\n editing,\n editingAnswerId,\n labels,\n noticeControls,\n exhaustedSpending,\n expanded,\n extensions,\n lastIndex,\n messageActions,\n onWidgetAnswer,\n pickReply,\n regenerate,\n respondToApproval,\n steeredAnswerIds,\n retry,\n saveEdit,\n skin,\n suggestedReplies,\n switchBranch,\n switcherOf,\n voiceAnswers,\n widgetFailure,\n widgetPendingId,\n ],\n );\n if (chat.notFound) return <skin.ChatNotFound />;\n // Приложение не пустило человека в чат: его слова вместо ленты и поля ввода.\n // «Повторить» перечитывает историю: доступ вернули — удачная загрузка снимает отказ.\n if (chat.accessRefusal) return <skin.Layout feed={<skin.ErrorNotice kind=\"other\" text={chat.accessRefusal.text} onRetry={chat.retry} />} composer={null} scrollRef={scrollRef} announcement=\"\" />;\n\n const feed = (\n <MessageFeed\n key={feedView.epoch}\n ref={feedRef}\n scrollRef={scrollRef}\n messages={feedMessages}\n keepLastMounted={busy}\n {...(editingId !== null && { keepMountedMessageId: editingId })}\n {...(feedView.openAtMessageId && { openAtMessageId: feedView.openAtMessageId })}\n renderMessage={renderMessage}\n />\n );\n\n // С файлами текст не обязателен, но отправка ждёт, пока все файлы загружены. Без файлов — как в чате без вложений.\n // Права прячут скрепку, микрофон, режим разговора и выбор модели; плагины при этом подключены как были.\n const composerAttachments = capabilities.attachments ? attachments : undefined;\n const sendingFiles = composerAttachments?.hasFiles ?? false;\n const exhausted = chat.spending?.state === \"exhausted\";\n const sendingBlocked = exhausted || chatClosed;\n const canSubmit = sendingFiles ? chat.loaded && !chat.busy && !sendingBlocked && attachments!.ready : chat.canSubmit;\n // Поправка во время ответа: с файлами — когда они загружены, без них — когда есть текст.\n const canSteer = chat.canSteer && (sendingFiles ? attachments!.ready : chat.value.trim().length > 0);\n const canSend = chat.loaded && !chat.busy && !sendingBlocked;\n const submitComposer = () => {\n if (sendingFiles && !canSubmit && !canSteer) return;\n if (canSubmit || canSteer) {\n // Отправка заканчивает диктовку: слова, пришедшие позже, не должны начинать новое сообщение.\n dictation.cancel();\n feedRef.current?.scrollToBottom();\n }\n // Поле с файлами очищается, только когда отправка принята; отказ сервера возвращает файлы обратно.\n const accepted = chat.submit(sendingFiles ? attachments!.readFiles() : undefined, attachments && ((kind) => attachments.restoreFiles(kind)));\n if (accepted && sendingFiles) attachments!.markSent();\n };\n const composerActionsNode = renderComposerActions(composerActions, {\n value: chat.value,\n setValue: chat.setValue,\n send: (text) => {\n // Обе формы отказывают, пока отправка невозможна: человек нажал кнопку, а не набрал текст, ждать загрузки истории незачем.\n if (!canSend) return;\n if (text === undefined) return submitComposer();\n if (!chat.sendReply(text)) return;\n dictation.cancel();\n feedRef.current?.scrollToBottom();\n },\n addFiles: (files) => composerAttachments?.composer.onAddFiles(files),\n busy: chat.busy,\n blocked: sendingBlocked,\n canSend,\n });\n const connection = connectionNotice(skin, chat.connection, chat.retry, chat.sendFailed || chat.loadFailed, chat.refusal, chat.serverRefusal);\n // Область для диктора стоит в странице всегда: живой регион, который появился уже с текстом, читают не везде.\n const spendingNotice = spendingLimits && skin.SpendingNotice && (\n <skin.SpendingNotice\n id={spendingNoticeId}\n state={chat.spending?.state ?? \"none\"}\n text={chat.spending ? spendingNoticeText(chat.spending, labels) : \"\"}\n />\n );\n // Закрытие — слова приложения над полем: своя надпись скина или, у скина до версии 20, блок ошибки с текстом.\n const closedNotice =\n closed &&\n (skin.ChatClosedNotice ? (\n <skin.ChatClosedNotice id={closedNoticeId} code={closed.code} message={closed.message} />\n ) : (\n <skin.ErrorNotice kind=\"other\" text={closed.message} />\n ));\n const sendingBlockedReasonId = chatClosed ? (skin.ChatClosedNotice ? closedNoticeId : undefined) : spendingNotice ? spendingNoticeId : undefined;\n const TaskCard = skin.TaskCard;\n const liveTasks = TaskCard ? backgroundTasks.tasks.filter(isLiveTask) : [];\n const taskCards =\n TaskCard && liveTasks.length > 0 ? (\n <>\n {liveTasks.map((task) => (\n <TaskCard\n key={task.id}\n id={task.id}\n title={task.title}\n status={task.status}\n elapsedMs={taskElapsedMs(task, backgroundTasks.now)}\n onCancel={() => void backgroundTasks.cancel(task.id)}\n canceling={backgroundTasks.cancelingIds.has(task.id)}\n />\n ))}\n </>\n ) : undefined;\n const notices = [taskCards, connection, closedNotice, spendingNotice].filter(Boolean);\n const notice =\n notices.length > 1 ? (\n <>\n {notices.map((part, index) => (\n <Fragment key={index}>{part}</Fragment>\n ))}\n </>\n ) : notices[0];\n\n const voiceActive = Boolean(dictation.view && (dictation.view.state !== \"idle\" || dictation.view.polishing))\n || Boolean(dialog && dialog.state !== \"off\" && dialog.state !== \"error\");\n const visibleCompletion = chat.loaded && !busy && !voiceActive && !chat.loadFailed && completion !== false ? completion : undefined;\n const layout = (\n <skin.Layout\n header={header}\n completion={visibleCompletion}\n empty={visibleCompletion == null && chat.loaded && !chat.loadFailed && !chat.sendFailed && !chat.serverRefusal && !chat.refusal && chat.connection === \"online\" && !chatClosed && !busy && feedMessages.length === 0}\n feed={feed}\n scrollRef={scrollRef}\n announcement={announced}\n composer={\n <skin.Composer\n options={effectiveComposerOptions}\n value={chat.value}\n onValueChange={chat.setValue}\n onSubmit={submitComposer}\n canSubmit={canSubmit}\n {...(chat.canSteer && { canSteer })}\n busy={chat.busy}\n onStop={() => void chat.stop()}\n {...(capabilities.modelChoice && { modelChoice })}\n {...(composerAttachments && { attachments: composerAttachments.composer })}\n {...(sendingBlocked && { sendingBlocked: true, ...(sendingBlockedReasonId && { sendingBlockedReasonId }) })}\n {...(capabilities.voice && dictation.view && { voice: dictation.view })}\n {...(capabilities.voice && dialog && { dialog })}\n {...(composerActionsNode != null && { actions: composerActionsNode })}\n />\n }\n {...(notice && { notice })}\n />\n );\n const viewer = skin.MediaViewer && mediaViewer.viewer && <skin.MediaViewer {...mediaViewer.viewer} />;\n return (\n <ChatStateContext.Provider value={chatState}>\n <ThinkingClocksProvider>\n {layout}\n {viewer}\n </ThinkingClocksProvider>\n </ChatStateContext.Provider>\n );\n}\n\n/** Надпись лимита: у окна сообщений — сколько осталось или «лимит сообщений исчерпан», у денег — доля и срок. */\nfunction spendingNoticeText(spending: SpendingView, labels: ResolvedChatLabels): string {\n const { state, info } = spending;\n if (info.unit === \"messages\" && state === \"exhausted\" && info.reason !== \"blocked\") return labels.spendingMessagesExhausted(info);\n if (info.unit === \"messages\" && state === \"warning\" && info.remaining !== undefined) return labels.spendingMessagesLeft(info.remaining, info);\n return (state === \"exhausted\" ? labels.spendingExhausted : labels.spendingWarning)(info);\n}\n\n/** Записи по сообщению, после которого стоит разделитель. Записи, чьего сообщения нет в ленте, ни на что не попадут. */\nfunction dividersByMessage(compactions: readonly CompactionMarker[]): Map<string, CompactionMarker[]> {\n const byMessage = new Map<string, CompactionMarker[]>();\n for (const marker of compactions) byMessage.set(marker.throughMessageId, [...(byMessage.get(marker.throughMessageId) ?? []), marker]);\n return byMessage;\n}\n\nconst CompactionDividerRow = memo(function CompactionDividerRow({\n skin,\n marker,\n expanded,\n}: {\n skin: ChatSkin;\n marker: CompactionMarker;\n expanded: ExpandedParts;\n}) {\n const [open, setOpen] = usePartOpen(expanded, `compaction:${marker.id}`);\n return <skin.CompactionDivider summary={marker.summary} open={open} onOpenChange={setOpen} />;\n});\n\n/**\n * Состояние связи главнее отказа сервера: пока связи нет, «что-то пошло не так» только путает. Понятный отказ\n * («войдите снова», «идут ответы в других чатах») главнее общего «что-то пошло не так».\n */\nfunction connectionNotice(\n skin: ChatSkin,\n connection: ConnectionState,\n onRetry: () => void,\n serverRefused: boolean,\n refusal: ChatRefusal | null,\n serverRefusal: ServerRefusal | null,\n): ReactNode {\n if (connection === \"reconnecting\") return <skin.ConnectionNotice state=\"reconnecting\" />;\n if (connection === \"offline\") return <skin.ConnectionNotice state=\"offline\" onRetry={onRetry} />;\n if (refusal === \"signed_out\") return <skin.ErrorNotice kind=\"signed_out\" onRetry={onRetry} />;\n // Текст уже в поле: отправить снова человек может сам, когда один из ответов закончится.\n if (refusal === \"too_many_turns\") return <skin.ErrorNotice kind=\"too_many_turns\" />;\n // Повтор не поможет, пока сервер не настроят: кнопки нет.\n if (refusal === \"forbidden_origin\") return <skin.ErrorNotice kind=\"forbidden_origin\" />;\n // Сервер назвал причину сам: её и показываем. Окончательный отказ без «Повторить» — повтор получит тот же ответ.\n if (serverRefusal) {\n const retryable = !serverRefusal.final && serverRefused;\n return <skin.ErrorNotice kind=\"other\" text={serverRefusal.text} {...(retryable && { onRetry })} />;\n }\n if (serverRefused) return <skin.ErrorNotice kind=\"other\" onRetry={onRetry} />;\n return null;\n}\n\n/** Надпись в поле правки по причине отказа сервера. */\nconst EDIT_REFUSAL_ERRORS: Record<EditRefusalReason, EditErrorKind> = {\n busy: \"busy\",\n spending_limit: \"spendingLimit\",\n invalid: \"rejected\",\n not_found: \"rejected\",\n signed_out: \"signedOut\",\n too_many_turns: \"tooManyTurns\",\n forbidden_origin: \"forbiddenOrigin\",\n // Текст сервера идёт в редактор как есть; надпись — на случай, если текста почему-то нет.\n server_refused: \"rejected\",\n};\n\n/** Пометки приложения серыми строками (`ServiceLine`, версия 27); нет строки у пометки или компонента у скина — ничего. */\nfunction ServiceLines({ skin, notices, lines }: { skin: ChatSkin; notices: AppNotice[]; lines: NoticeLines | undefined }) {\n const View = skin.ServiceLine;\n if (!View || !lines) return null;\n const texts = notices.flatMap((notice) => {\n const text = Object.hasOwn(lines, notice.name) ? lines[notice.name]!(notice) : undefined;\n return text ? [{ name: notice.name, text }] : [];\n });\n return (\n <>\n {texts.map(({ name, text }, index) => (\n <View key={`${name}-${index}`} text={text} />\n ))}\n </>\n );\n}\n\n/** Реплики живого сотрудника: `StaffMessage` скина; скин без него (до версии 25) — ответом агента с подписью. */\nfunction StaffReplies({ skin, replies, label }: { skin: ChatSkin; replies: StaffReply[]; label: (staffName: string | undefined) => string }) {\n const View = skin.StaffMessage;\n return (\n <>\n {replies.map((reply, index) =>\n View ? (\n <View key={index} text={reply.text} {...(reply.staffName && { staffName: reply.staffName })} />\n ) : (\n <skin.AgentMessage key={index} status=\"complete\" empty={false} onCopy={() => void navigator.clipboard?.writeText(reply.text)}>\n <skin.AgentText markdown={`**${label(reply.staffName)}:** ${reply.text}`} streaming={false} />\n </skin.AgentMessage>\n ),\n )}\n </>\n );\n}\n\n/*\n * Пока дописывается ответ, меняется только он: у остальных сообщений тот же объект.\n * `memo` не даёт длинной ленте перерисовываться на каждый кусок ответа.\n */\nconst UserMessage = memo(function UserMessage({\n skin,\n message,\n describe,\n branchSwitcher,\n onEdit,\n pending,\n}: {\n skin: ChatSkin;\n message: AgentChatMessage;\n describe: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined;\n branchSwitcher: ReactNode;\n onEdit?: () => void;\n pending?: boolean;\n}) {\n const files = useMemo(() => describe?.(message), [describe, message]);\n return (\n <skin.UserMessage\n text={messageText(message)}\n {...(files && { attachments: files })}\n {...(branchSwitcher && { branchSwitcher })}\n {...(onEdit && { onEdit })}\n {...(pending && { pending: true })}\n />\n );\n});\n\n/** Правка сообщения: черновик живёт здесь, набор текста не перерисовывает ленту. Файлы сообщения остаются как были. */\nfunction MessageEditorRow({\n skin,\n message,\n initialText,\n describe,\n error,\n onDraftChange,\n onSave,\n onCancel,\n}: {\n skin: ChatSkin;\n message: AgentChatMessage;\n initialText: string;\n describe: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined;\n error?: string;\n /** Набранный текст хранит чат: поле, пересозданное лентой, открывается с ним. */\n onDraftChange: (text: string) => void;\n onSave: (text: string) => void;\n onCancel: () => void;\n}) {\n const [draft, setDraft] = useState(initialText);\n const [shownError, setShownError] = useState(error);\n const files = useMemo(() => describe?.(message), [describe, message]);\n // С файлами текст может быть пустым: сообщение без слов, только с файлами, уже бывает.\n const canSave = draft.trim().length > 0 || files !== undefined;\n return (\n <skin.MessageEditor\n value={draft}\n onValueChange={(value) => {\n setDraft(value);\n onDraftChange(value);\n setShownError(undefined);\n }}\n onSave={() => canSave && onSave(draft)}\n onCancel={onCancel}\n canSave={canSave}\n {...(shownError && { error: shownError })}\n {...(files && { attachments: files })}\n />\n );\n}\n\n/** Скобки и обратная косая в названии ломали бы ссылку markdown. */\n/** Название — одной строкой и без разметки: переводы строк → пробел, знаки markdown экранированы. */\nconst escapeLinkText = (text: string) =>\n text.replace(/[\\r\\n]+/g, \" \").replace(/[\\\\\\x60*_{}[\\]()#+!<>|~&]/g, (char) => `\\\\${char}`);\n\n/** Видео для скина без карточек: «▶ [Название](страница) · с 3:20». */\nfunction videoLinkMarkdown(video: AgentVideoView, labels: ResolvedChatLabels): string {\n const link = `▶ [${escapeLinkText(video.title)}](${video.pageUrl})`;\n return video.start ? `${link} · ${labels.videos.from(video.start)}` : link;\n}\n\n/** Видит ли выбранная модель картинки; `undefined` — меню ещё не пришло или модели нет в нём. */\nfunction modelSeesImages(choice: ModelChoiceView): boolean | undefined {\n return choice.providers\n .find((provider) => provider.id === choice.current.providerId)\n ?.models.find((model) => model.id === choice.current.modelId)?.acceptsImages;\n}\n\nconst AgentAnswer = memo(function AgentAnswer({\n skin,\n describeImages,\n describeFiles,\n message,\n voiceAnswer,\n steered,\n expanded,\n status,\n onRetry,\n onApproval,\n approvalPendingId,\n approvalFailure,\n awaitingHuman,\n chatClosed,\n chatId,\n onWidgetAnswer,\n widgetPendingId,\n widgetFailure,\n suggestedReplies,\n extensions,\n diagrams,\n demos,\n branchSwitcher,\n onRegenerate,\n editMark,\n onEdit,\n actions,\n last,\n runningActionIds,\n onRunAction,\n}: {\n skin: ChatSkin;\n /** Свои виджеты, схемы и картинки страницы. */\n extensions: FeedExtensions;\n /** Схемы подключены: блоки ```mermaid рисуются (без этого — обычный код). */\n diagrams?: DiagramSetup;\n /** Живые демонстрации подключены: блоки ```html-demo запускаются в этих цветах (без этого — обычный код). */\n demos?: LiveDemoSetup;\n /** Картинки агента (`images`): вызов `generate_images` → вид `GeneratedImages`; нет — строка инструмента. */\n describeImages: DescribeGeneratedImages | undefined;\n /** Вид вложений (плагин `attachments`): картинки-файлы в ответе агента (импорт) — видом `GeneratedImages`. */\n describeFiles: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined;\n message: AgentChatMessage;\n /** Ответ голосового хода: теги голоса слышны, но на экране и в копии их нет. */\n voiceAnswer: boolean;\n /** Агент закончил ответ, чтобы взять поправку: пустой — не «модель не дала ответа». */\n steered: boolean;\n expanded: ExpandedParts;\n status: AnswerStatus;\n onRetry?: () => void;\n onApproval: (approvalId: string, decision: ApprovalDecision) => void;\n approvalPendingId: string | null;\n approvalFailure: { id: string; kind: ApprovalFailureKind; text?: string } | null;\n /** Последний ответ ветки ждёт человека: только в нём виджет активен. */\n awaitingHuman: boolean;\n /** Чат закрыт: ждущие подтверждения без кнопок. */\n chatClosed: boolean;\n /** Чат ленты: его получает свой компонент виджета. */\n chatId: string;\n onWidgetAnswer: (toolCallId: string, answer: unknown) => void;\n widgetPendingId: string | null;\n widgetFailure: { id: string; kind: WidgetFailureKind; text?: string } | null;\n /** Кнопки-подсказки под последним законченным ответом. */\n suggestedReplies?: ReactNode;\n branchSwitcher: ReactNode;\n onRegenerate?: () => void;\n /** Что в ответе изменено человеком (песочница). */\n editMark?: AnswerEditMark | undefined;\n /** Открыть правку ответа (песочница). */\n onEdit?: () => void;\n /** Свои кнопки приложения: какие показать, решает `visible` каждой. */\n actions?: MessageAction[];\n /** Последний ответ ветки — для `visible`. */\n last?: boolean;\n /** Id кнопок, чьи обработчики у этого ответа ещё идут. */\n runningActionIds?: readonly string[] | undefined;\n onRunAction?: (action: MessageAction, message: AgentChatMessage, text: string) => void;\n}) {\n const labels = useChatLabels();\n const durations = message.metadata?.reasoningDurationsMs ?? {};\n const thoughtTitles = message.metadata?.thoughtTitles ?? {};\n let reasoningIndex = 0;\n // Простой вид: размышления и вызовы — шаги работы, их раскладывает `workLayoutOf`; подробный — всё как есть.\n const simple = extensions.workView === \"simple\";\n const entries: AnswerEntry<ReactNode>[] = [];\n const show = (node: ReactNode, blank = false) => void entries.push({ kind: \"shown\", node, ...(blank && { blank }) });\n const step = (index: number, work: WorkStep, node: ReactNode) =>\n void (simple ? entries.push({ kind: \"step\", key: partKey(message.id, index), step: work }) : show(node));\n const customNames = new Set(Object.keys(extensions.widgets ?? {}));\n /** Номер следующей схемы в ответе: схемы всех текстовых частей ответа нумеруются подряд. */\n let nextDiagram = 0;\n const fileViews = answerFileViews(message, describeFiles, Boolean(skin.GeneratedImages));\n\n message.parts.forEach((part, index) => {\n const key = `${message.id}-${index}`;\n if (part.type === \"file\") {\n const file = fileViews.get(index);\n if (file?.kind === \"images\" && skin.GeneratedImages) show(<skin.GeneratedImages key={key} {...file.view} />);\n else if (file?.kind === \"file\") show(<skin.AgentText key={key} markdown={file.markdown} streaming={false} />);\n return;\n }\n const imagesView = isToolUIPart(part) ? describeImages?.(part) : undefined;\n const videoView = isToolUIPart(part) && extensions.videos && getToolName(part) === extensions.videos.toolName ? extensions.videos.describe(part) : undefined;\n if (part.type === \"reasoning\") {\n if (extensions.reasoningVisible === false) return;\n const ordinal = String(reasoningIndex++);\n const streaming = part.state === \"streaming\";\n const durationMs = streaming ? null : (durations[ordinal] ?? null);\n // Заголовок раздумья от сервера (`thoughtTitles`); пока его нет — прежняя подпись.\n const title = streaming ? undefined : thoughtTitles[ordinal];\n // Пустое раздумье — пропуск; без права `reasoning` сервер шлёт пустое место с заголовком — шаг простого вида.\n if (!streaming && part.text.trim().length === 0 && (title === undefined || !simple)) return;\n step(\n index,\n { kind: \"thinking\", durationMs, ...(title !== undefined && { title }) },\n <ExpandablePart\n key={key}\n Component={skin.Reasoning}\n expanded={expanded}\n partId={partKey(message.id, index)}\n text={part.text}\n streaming={streaming}\n durationMs={durationMs}\n {...(title !== undefined && { title })}\n />,\n );\n } else if (part.type === \"text\") {\n const streaming = part.state === \"streaming\";\n // Теги голоса («[laughs]») слышны, но не видны; в потоке недописанный тег тоже прячется, чтобы не мелькал.\n const markdown = voiceAnswer ? stripVoiceTags(part.text, { partialTail: streaming }) : part.text;\n const blank = markdown.trim().length === 0;\n if (diagrams || demos) {\n show(\n <RichAgentText\n key={key}\n AgentText={skin.AgentText}\n markdown={markdown}\n streaming={streaming}\n messageId={message.id}\n firstBlock={nextDiagram}\n {...(diagrams && { diagrams })}\n {...(demos && { demos })}\n />,\n blank,\n );\n if (diagrams) nextDiagram += countMermaidBlocks(markdown);\n } else {\n show(<skin.AgentText key={key} markdown={markdown} streaming={streaming} />, blank);\n }\n } else if (part.type === \"data-app-event\") {\n // Событие приложения — компонентом приложения по типу, прямо в теле ответа; без компонента не видно.\n const event = appEventOfPart(part);\n const View = event && extensions.appEvents?.[event.type];\n if (event && View) show(<View key={key} event={event} messageId={message.id} />);\n } else if (imagesView && skin.GeneratedImages) {\n show(<skin.GeneratedImages key={key} {...imagesView} />);\n } else if (videoView) {\n // Пока вызов идёт — ничего: карточка появится готовой. Скин без `AgentVideo` (до версии 14) — ссылка в тексте.\n if (videoView.state === \"loading\") return;\n show(\n skin.AgentVideo ? (\n <skin.AgentVideo key={key} {...videoView.video} />\n ) : (\n <skin.AgentText key={key} markdown={videoLinkMarkdown(videoView.video, labels)} streaming={false} />\n ),\n );\n } else if (isToolUIPart(part)) {\n // Подсказки рисуются кнопками под ответом, а не строкой вызова.\n if (getToolName(part) === SUGGEST_REPLIES) return;\n // Помощник — своя строка с заданием, шагами и итогом; скин без неё (до версии 18) рисует обычный вызов.\n if (skin.HelperCallRow && isHelperPart(part)) {\n const helper = withoutPendingApproval(helperCallViewOf(part, { onApproval, approvalPendingId, approvalFailure, failureText: (kind) => failureTextOf(kind, labels) }), chatClosed);\n const row = <ExpandablePart key={key} Component={skin.HelperCallRow} expanded={expanded} partId={partKey(message.id, index)} {...helper} />;\n // Помощник, ждущий решения человека, виден и в простом виде: иначе ход встанет без кнопок.\n if (helper.approval?.state === \"requested\") show(row);\n else step(index, helperStepOf(part, helper, labels), row);\n return;\n }\n const call = widgetCallOf(part, { awaitingHere: awaitingHuman, customNames });\n if (call === \"hidden\") return;\n if (call) {\n const failure = widgetFailure?.id === call.toolCallId ? widgetFailureTextOf(widgetFailure, labels) : undefined;\n const Custom = extensions.widgets?.[call.name];\n show(\n skin.Widget ? (\n <WidgetPart\n key={key}\n Widget={skin.Widget}\n {...(Custom && { Custom })}\n call={call}\n chatId={chatId}\n pending={widgetPendingId === call.toolCallId}\n {...(failure !== undefined && { failure })}\n onAnswer={onWidgetAnswer}\n />\n ) : (\n // Скин без виджетов (до версии 13): строка вызова с вопросом и пометкой «ответьте сообщением» (FR-033).\n <ExpandablePart\n key={key}\n Component={skin.ToolCallRow}\n expanded={expanded}\n partId={partKey(message.id, index)}\n toolName={call.name}\n state={toolCallState(part)}\n title={fallbackTitleOf(call.input, labels)}\n {...(part.input !== undefined && { input: part.input })}\n />\n ),\n );\n return;\n }\n const row = (\n <ExpandablePart\n key={key}\n Component={skin.ToolCallRow}\n expanded={expanded}\n partId={partKey(message.id, index)}\n toolName={getToolName(part)}\n state={toolCallState(part)}\n {...(hasTitle(part) && { title: part.title })}\n {...withoutPendingApproval(approvalProps(part, { onApproval, approvalPendingId, approvalFailure, labels }), chatClosed)}\n {...(part.input !== undefined && { input: part.input })}\n {...(part.state === \"output-available\" && { output: part.output })}\n {...(part.state === \"output-error\" && { errorText: part.errorText })}\n {...(editMark?.editedToolCallIds.includes(part.toolCallId) && { outputEdited: true })}\n />\n );\n // Подтверждение опасного действия видно и в простом виде; решённое — уже шаг работы.\n if (part.state === \"approval-requested\") show(row);\n else step(index, toolStepOf(part), row);\n }\n });\n const parts = simple\n ? workLayoutOf(entries, { streaming: status === \"streaming\", phrases: extensions.stepPhrases, labels: labels.workView, thoughts: Object.keys(thoughtTitles).length > 0 }).map((item) =>\n item.kind === \"shown\" ? item.node : <WorkStepsPart key={`${item.key}:work`} skin={skin} expanded={expanded} item={item} />,\n )\n : entries.map((entry) => (entry.kind === \"shown\" ? entry.node : null));\n\n\n const text = messageText(message, { voiceAnswer });\n const actionViews = status === \"streaming\" ? undefined : messageActionViews(actions, { message, status, last: last ?? false }, (action) => ({\n busy: runningActionIds?.includes(action.id) ?? false,\n onRun: () => onRunAction?.(action, message, text),\n }));\n // У ответа с ошибкой «повторить» живёт в блоке ошибки, второй кнопки нет.\n const errorText = status === \"error\" ? customErrorText(labels, message.metadata?.error) : undefined;\n const notice =\n status === \"error\" ? (\n <skin.ErrorNotice kind={message.metadata?.error?.kind ?? \"other\"} {...(errorText !== undefined && { text: errorText })} {...(onRetry && { onRetry })} />\n ) : undefined;\n const answerProvider = extensions.modelNames?.find((provider) => provider.id === message.metadata?.providerId);\n const answerModel = answerProvider?.models.find((model) => model.id === message.metadata?.modelId);\n return (\n <ThinkingClockProvider id={message.id} waiting={status === \"streaming\"}>\n <skin.AgentMessage\n {...(extensions.answerDetails && message.metadata?.modelId && message.metadata?.providerId && { modelDetails: { modelId: message.metadata.modelId, providerId: message.metadata.providerId, ...(answerProvider && { providerName: answerProvider.name }), ...(answerModel && { modelName: answerModel.name }), ...(message.metadata.reasoningLevel !== undefined && { reasoning: message.metadata.reasoningLevel }) } })}\n status={status}\n empty={!steered && status !== \"streaming\" && status !== \"awaiting_approval\" && text.trim().length === 0}\n onCopy={() => void navigator.clipboard?.writeText(text)}\n {...(notice && { notice })}\n {...(onRetry && !notice && { onRetry })}\n {...(branchSwitcher && { branchSwitcher })}\n {...(onRegenerate && { onRegenerate })}\n {...(onEdit && { onEdit })}\n {...(answerEdited(editMark) && { edited: true })}\n {...(message.metadata?.undelivered && { undelivered: true })}\n {...(message.metadata?.voiceCut && { voiceInterrupted: { spokenTail: message.metadata.voiceCut.spokenTail } })}\n {...(message.parts.some((part) => part.type === \"data-length-cut\") && { cutByLength: true })}\n {...(actionViews && { actions: actionViews })}\n >\n {parts}\n {suggestedReplies}\n </skin.AgentMessage>\n </ThinkingClockProvider>\n );\n});\n\n/** Свои кнопки ответа для скина; ни одной видимой — `undefined`. */\nfunction messageActionViews(\n actions: MessageAction[] | undefined,\n target: MessageActionTarget,\n stateOf: (action: MessageAction) => Pick<MessageActionView, \"busy\" | \"onRun\">,\n): MessageActionView[] | undefined {\n const views = (actions ?? [])\n .filter((action) => action.visible?.(target) ?? true)\n .map((action) => ({ id: action.id, label: action.label, ...(action.icon !== undefined && { icon: action.icon }), ...stateOf(action) }));\n return views.length > 0 ? views : undefined;\n}\n\n/** Схемы для ленты: готовые рисунки чата, цвета темы и окно просмотра (если скин его рисует). */\ninterface DiagramSetup {\n renders: DiagramRenders;\n colors: DiagramColors;\n open?: MediaViewerController[\"open\"];\n}\n\ntype AgentTextProps = ComponentProps<ChatSkin[\"AgentText\"]>;\n\n/** Живые демонстрации ленты: цвета темы и запрет запуска (в чате чужой текст). */\ninterface LiveDemoSetup {\n colors: LiveDemoColors;\n blocked: boolean;\n}\n\n/** Текст ответа со схемами и живыми демонстрациями: скин получает `renderDiagram`/`onOpenDiagram` и `renderDemo`. */\nfunction RichAgentText({\n AgentText,\n markdown,\n streaming,\n messageId,\n firstBlock,\n diagrams,\n demos,\n}: {\n AgentText: ChatSkin[\"AgentText\"];\n markdown: string;\n streaming: boolean;\n messageId: string;\n firstBlock: number;\n diagrams?: DiagramSetup;\n demos?: LiveDemoSetup;\n}) {\n const renderDemo = useLiveDemoBlocks({ markdown, streaming, colors: demos?.colors, blocked: demos?.blocked === true });\n const demoProps: Partial<AgentTextProps> = renderDemo ? { renderDemo } : {};\n return diagrams ? (\n <DiagramAgentText AgentText={AgentText} markdown={markdown} streaming={streaming} messageId={messageId} firstBlock={firstBlock} setup={diagrams} extra={demoProps} />\n ) : (\n <AgentText markdown={markdown} streaming={streaming} {...demoProps} />\n );\n}\n\n/** Текст ответа со схемами: скин получает `renderDiagram` и, если есть окно просмотра, `onOpenDiagram`. */\nfunction DiagramAgentText({\n AgentText,\n markdown,\n streaming,\n messageId,\n firstBlock,\n setup,\n extra,\n}: {\n AgentText: ChatSkin[\"AgentText\"];\n markdown: string;\n streaming: boolean;\n messageId: string;\n firstBlock: number;\n setup: DiagramSetup;\n /** Другие свойства текста (демонстрации). */\n extra: Partial<AgentTextProps>;\n}) {\n const diagramProps = useDiagramBlocks({ markdown, streaming, messageId, firstBlock, ...setup });\n return <AgentText markdown={markdown} streaming={streaming} {...extra} {...diagramProps} />;\n}\n\n/** Причина, по которой ответ на виджет не принят, словами. */\nfunction widgetFailureTextOf({ kind, text }: { kind: WidgetFailureKind; text?: string }, labels: ResolvedChatLabels): string {\n if (text !== undefined) return text;\n if (kind === \"already_answered\") return labels.widgets.alreadyAnswered;\n if (kind === \"rejected\" || kind === \"answer_refused\") return labels.widgets.answerRejected;\n if (kind === \"check_timeout\") return labels.widgets.answerCheckTimeout;\n return kind === \"connection\" ? labels.widgets.answerFailed : failureTextOf(kind === \"refused\" ? \"connection\" : kind, labels);\n}\n\n/** Заголовок строки вызова у скина без виджетов: вопрос модели и «ответьте сообщением». */\nfunction fallbackTitleOf(input: unknown, labels: ResolvedChatLabels): string {\n const record = typeof input === \"object\" && input !== null ? (input as { question?: unknown; title?: unknown }) : {};\n const question = typeof record.question === \"string\" ? record.question : typeof record.title === \"string\" ? record.title : \"\";\n return question.trim().length > 0 ? `${question} \\u2014 ${labels.widgets.fallback}` : labels.widgets.fallback;\n}\n\n/** Раскрытие берётся из хранилища чата: переживает уход сообщения за край экрана. */\nfunction ExpandablePart<C extends ChatSkin[\"Reasoning\"] | ChatSkin[\"ToolCallRow\"] | NonNullable<ChatSkin[\"HelperCallRow\"]>>({\n Component,\n expanded,\n partId,\n ...props\n}: { Component: C; expanded: ExpandedParts; partId: string } & Omit<ComponentProps<C>, \"open\" | \"onOpenChange\">) {\n const [open, setOpen] = usePartOpen(expanded, partId);\n const Part = Component as ElementType;\n return <Part {...props} open={open} onOpenChange={setOpen} />;\n}\n\n/** Вызов инструмента как шаг простого вида: имя, состояние, вход и выход, если они пришли. */\nfunction toolStepOf(part: ToolUIPart | DynamicToolUIPart): WorkStep {\n return {\n kind: \"tool\",\n call: {\n toolName: getToolName(part),\n state: toolCallState(part),\n ...(part.input !== undefined && { input: part.input }),\n ...(part.state === \"output-available\" && { output: part.output }),\n },\n ...(hasTitle(part) && { title: part.title }),\n };\n}\n\n/** Помощник как шаг простого вида: без фразы приложения — «Помощник «…» работает» и «… закончил». */\nfunction helperStepOf(part: ToolUIPart | DynamicToolUIPart, helper: { name: string; title?: string; status: string }, labels: ResolvedChatLabels): WorkStep {\n const name = helper.title ?? helper.name;\n const state: ToolCallState = helper.status === \"failed\" ? \"error\" : helper.status === \"running\" || helper.status === \"awaiting_approval\" ? \"running\" : \"done\";\n return {\n kind: \"tool\",\n call: { toolName: getToolName(part), state, ...(part.input !== undefined && { input: part.input }), ...(part.state === \"output-available\" && { output: part.output }) },\n fallback: { running: labels.workView.helperRunning(name), done: labels.workView.helperDone(name) },\n };\n}\n\n/** Ожидание подтверждения — тоже «выполняется», отказ — ошибка; что именно решил человек, показывает `approval`. */\nfunction toolCallState(part: ToolUIPart | DynamicToolUIPart): ToolCallState {\n switch (part.state) {\n case \"output-available\":\n return part.preliminary ? \"running\" : \"done\";\n case \"output-error\":\n case \"output-denied\":\n return \"error\";\n default:\n return \"running\";\n }\n}\n\nconst hasTitle = (part: ToolUIPart | DynamicToolUIPart): part is typeof part & { title: string } =>\n typeof (part as { title?: unknown }).title === \"string\" && (part as { title: string }).title.length > 0;\n\nfunction failureTextOf(kind: ApprovalFailureKind, labels: ChatLabels): string {\n if (kind === \"spending_limit\") return labels.approvalSpendingLimit;\n if (kind === \"too_many_turns\") return labels.approvalTooManyTurns;\n return kind === \"busy\" ? labels.approvalBusy : labels.approvalFailed;\n}\n\n/** В закрытом чате ждущее подтверждение не показывается: ответить на него сервер не даст. Итог прежних — на месте. */\nfunction withoutPendingApproval<View extends { approval?: { state: string }; approvalAction?: unknown }>(view: View, chatClosed: boolean): View {\n if (!chatClosed || view.approval?.state !== \"requested\") return view;\n const { approval: _approval, approvalAction: _action, ...rest } = view;\n return rest as View;\n}\n\n/** Запрос подтверждения и его итог для строки вызова; у вызова без подтверждения свойства нет. */\nfunction approvalProps(\n part: ToolUIPart | DynamicToolUIPart,\n controls: {\n onApproval: (approvalId: string, decision: ApprovalDecision) => void;\n approvalPendingId: string | null;\n approvalFailure: { id: string; kind: ApprovalFailureKind; text?: string } | null;\n labels: ChatLabels;\n },\n): { approval?: ToolApprovalView } {\n const approval = (part as { approval?: { id: string; approved?: boolean; reason?: string; requestReason?: string } }).approval;\n if (!approval) return {};\n if (part.state === \"approval-requested\") {\n return {\n approval: {\n state: \"requested\",\n onDecide: (decision) => controls.onApproval(approval.id, decision),\n pending: controls.approvalPendingId === approval.id,\n failed: controls.approvalFailure?.id === approval.id,\n ...(controls.approvalFailure?.id === approval.id && { failureText: controls.approvalFailure.text ?? failureTextOf(controls.approvalFailure.kind, controls.labels) }),\n canAlways: approval.requestReason === ALWAYS_OFFERED,\n },\n };\n }\n if (part.state === \"output-denied\" || approval.approved === false) return { approval: { state: \"denied\" } };\n return { approval: { state: approval.reason === ALWAYS_IN_CHAT ? \"always\" : \"allowed\" } };\n}\n\n/** Тот же список, пока его элементы те же: `images={[a, b]}` прямо в разметке не перерисовывает ленту на каждый ввод. */\nfunction useSameList<T>(list: readonly T[]): readonly T[] {\n const kept = useRef(list);\n if (kept.current.length !== list.length || kept.current.some((item, index) => item !== list[index])) kept.current = list;\n return kept.current;\n}\n","import { useCallback, useMemo, useSyncExternalStore } from \"react\";\n\n/*\n * Раскрытые рассуждения и строки вызовов. Живут не в самих строках, а в хранилище на чат:\n * лента размонтирует сообщения, ушедшие за край экрана, а раскрытое должно пережить уход.\n */\n\nexport interface ExpandedParts {\n isOpen: (key: string) => boolean;\n setOpen: (key: string, open: boolean) => void;\n subscribe: (listener: () => void) => () => void;\n}\n\n/** Ключ части ответа: id сообщения и номер части. */\nexport function partKey(messageId: string, partIndex: number): string {\n return `${messageId}:${partIndex}`;\n}\n\nfunction createExpandedParts(): ExpandedParts {\n const open = new Set<string>();\n const listeners = new Set<() => void>();\n return {\n isOpen: (key) => open.has(key),\n setOpen: (key, next) => {\n if (open.has(key) === next) return;\n if (next) open.add(key);\n else open.delete(key);\n for (const listener of listeners) listener();\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\n/** Хранилище раскрытых частей одного чата; при смене `chatId` начинается пустое. */\nexport function useExpandedParts(chatId: string): ExpandedParts {\n // eslint-disable-next-line react-hooks/exhaustive-deps -- новый чат — новое хранилище\n return useMemo(createExpandedParts, [chatId]);\n}\n\n/** Раскрыта ли часть и как её переключить; часть перерисовывается только от своего ключа. */\nexport function usePartOpen(store: ExpandedParts, key: string): [boolean, (open: boolean) => void] {\n const open = useSyncExternalStore(\n store.subscribe,\n () => store.isOpen(key),\n () => false,\n );\n const setOpen = useCallback((next: boolean) => store.setOpen(key, next), [store, key]);\n return [open, setOpen];\n}\n","import { useState } from \"react\";\nimport { messageText } from \"./chat-types\";\nimport { voiceAnswerIds } from \"./voice-turn\";\nimport { withoutMarkdown } from \"./without-markdown\";\nimport type { AgentChatMessage } from \"./chat-types\";\nimport { customErrorText, useChatLabels } from \"./labels\";\nimport type { ResolvedChatLabels } from \"./labels\";\n\n/*\n * Что прочитает экранный диктор, когда ответ закончился во вкладке. Сама лента для дикторов\n * молчит (`aria-live=\"off\"`): иначе окно ленты зачитывало бы каждое сообщение, которое оно\n * дорисовывает при прокрутке.\n */\n\n/**\n * Текст для диктора: пустой при открытии чата, итог ответа — когда ответ перешёл из «идёт» в\n * итоговое состояние. Когда начинается новый ответ, текст обнуляется: так одинаковые итоги\n * подряд («Остановлено» дважды) озвучиваются оба раза. `resetKey` (id чата) тоже обнуляет текст.\n */\nexport function useFeedAnnouncement(messages: AgentChatMessage[], busy: boolean, resetKey?: string): string {\n const labels = useChatLabels();\n // Прошлые `busy` и `resetKey` хранятся в состоянии и сравниваются прямо при рендере: итог появляется\n // в том же кадре, в котором ответ закончился, а не в следующем после эффекта.\n const [seen, setSeen] = useState({ busy, resetKey, announcement: \"\" });\n let current = seen;\n if (seen.resetKey !== resetKey) {\n // Чат сменился: итог прежнего чата и его «идёт ответ» к новому чату не относятся.\n current = { busy, resetKey, announcement: \"\" };\n } else if (seen.busy !== busy) {\n // Начался новый ответ: текст прошлого убираем, иначе такой же итог (например «Остановлено») не прозвучит снова.\n current = { busy, resetKey, announcement: busy ? \"\" : announcementFor(messages, labels) };\n }\n if (current !== seen) setSeen(current);\n return current.announcement;\n}\n\nfunction announcementFor(messages: AgentChatMessage[], labels: ResolvedChatLabels): string {\n const answer = messages.findLast((message) => message.role === \"assistant\");\n if (!answer) return \"\";\n const status = answer.metadata?.status ?? \"complete\";\n if (status === \"error\") return customErrorText(labels, answer.metadata?.error) ?? labels.errors[answer.metadata?.error?.kind ?? \"other\"];\n if (status === \"stopped\") return labels.answerStopped;\n if (status === \"interrupted\") return labels.answerInterrupted;\n if (status === \"awaiting_approval\") return widgetQuestionOf(answer, labels) ?? labels.approvalAnnouncement;\n const text = messageText(answer, { voiceAnswer: voiceAnswerIds(messages).has(answer.id) });\n const spoken = withoutMarkdown(text);\n return spoken.trim().length === 0 ? labels.answerEmpty : spoken;\n}\n\n/** Ход остановился на вопросе агента (ждущий виджет): диктор слышит «Агент задал вопрос» и сам вопрос (FR-021). */\nfunction widgetQuestionOf(answer: AgentChatMessage, labels: ResolvedChatLabels): string | null {\n const waiting = answer.parts.find((part) => {\n const tool = part as { type: string; state?: string; approval?: unknown };\n return (tool.type.startsWith(\"tool-\") || tool.type === \"dynamic-tool\") && tool.state === \"input-available\" && tool.approval === undefined;\n });\n if (!waiting) return null;\n const input = (waiting as { input?: { question?: unknown; title?: unknown } }).input;\n const question = typeof input?.question === \"string\" ? input.question : typeof input?.title === \"string\" ? input.title : \"\";\n return question.trim().length > 0 ? `${labels.widgets.asked}: ${question}` : labels.widgets.asked;\n}\n","/*\n * Текст ответа без разметки Markdown — для экранного диктора, которому «**жирный**» зачитали бы со звёздочками.\n * Остаётся то, что видит читатель: заголовки, жирный, курсив, зачёркнутое и код — их слова; ссылка и картинка — текст\n * или подпись; список и цитата — их строки без значков (номера «1.» остаются: диктору они нужны). Обычный текст\n * не трогаем: выделение — только целыми словами (у пробела, скобки, кавычки, знака препинания), поэтому «5*6*7»,\n * «src/*.ts» и имена из кода вроде «__init__» остаются; цитата — «>» с пробелом и не перед числом («> 100 ₽» — это\n * «больше»); решётки в конце снимаются только у заголовка. Скобки, не похожие на ссылку («[1]»), тоже остаются.\n */\n\nconst RULE = /^\\s{0,3}([-*_])(\\s*\\1){2,}\\s*$/;\nconst FENCE = /^\\s*(\\x60{3}|~{3})/;\nconst HEADING = /^\\s{0,3}#{1,6}\\s+(.*?)(?:\\s+#+)?\\s*$/;\nconst QUOTE = /^(\\s{0,3}>(?: (?!\\d)|$))+/;\n/** До открывающего знака выделения — начало, пробел, открывающая скобка или кавычка; после закрывающего — конец,\n * пробел, закрывающая скобка, кавычка или знак препинания. */\nconst OPEN = String.raw`(?<=^|[\\s(\\[«\"'„“])`;\nconst CLOSE = String.raw`(?=$|[\\s)\\]»\"'”.,;:!?…])`;\n/** Выделение знаком `sign` (`count` раз): внутри у краёв не пробел и не тот же знак. */\nconst emphasis = (sign: \"*\" | \"_\" | \"~\", count: number, notBefore = \"\") => {\n const escaped = sign === \"*\" ? \"\\\\*\" : sign;\n const marker = escaped.repeat(count);\n return new RegExp(`${OPEN}${marker}${notBefore}(?=[^\\\\s${escaped}])(.+?)(?<=[^\\\\s${escaped}])${marker}${CLOSE}`, \"gu\");\n};\nconst BOLD = emphasis(\"*\", 2);\n/** Двойные подчёркивания вокруг латинского имени — имя из кода («__init__»), а не жирный. */\nconst BOLD_UNDERSCORE = emphasis(\"_\", 2, \"(?![A-Za-z0-9_]+__)\");\nconst STRIKE = emphasis(\"~\", 2);\nconst ITALIC = emphasis(\"*\", 1);\nconst ITALIC_UNDERSCORE = emphasis(\"_\", 1);\n\nexport function withoutMarkdown(markdown: string): string {\n let inCode = false;\n const lines: string[] = [];\n for (const line of markdown.split(\"\\n\")) {\n if (FENCE.test(line)) {\n inCode = !inCode;\n continue;\n }\n if (inCode) lines.push(line);\n else if (!RULE.test(line)) lines.push(plainLine(line));\n }\n return lines.join(\"\\n\");\n}\n\nfunction plainLine(line: string): string {\n return inline(\n line\n .replace(HEADING, \"$1\")\n .replace(QUOTE, \"\")\n .replace(/^(\\s*)[-*+]\\s+(?:\\[[ xX]\\]\\s+)?/, \"$1\"),\n );\n}\n\nfunction inline(text: string): string {\n return text\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/`+([^`]+)`+/g, \"$1\")\n .replace(BOLD, \"$1\")\n .replace(BOLD_UNDERSCORE, \"$1\")\n .replace(STRIKE, \"$1\")\n .replace(ITALIC, \"$1\")\n .replace(ITALIC_UNDERSCORE, \"$1\");\n}\n","import { memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactElement, Ref, RefObject } from \"react\";\nimport { Virtualizer } from \"virtua\";\nimport type { CustomContainerComponentProps, CustomItemComponentProps, VirtualizerHandle } from \"virtua\";\nimport type { AgentChatMessage } from \"./chat-types\";\n\n/**\n * Подсказка окну ленты, какой высоты сообщение, пока оно не измерено. Без неё библиотека\n * считает каждое сообщение в 40 px и при первом показе рисует 14 сообщений сверху, потом\n * 14 в конце по прикидке: по сравнению с настоящими (ответ — сотни px) это лишние разборы\n * markdown. Ближе к настоящей высоте — меньше сообщений в пробных проходах.\n */\nconst UNMEASURED_MESSAGE_HEIGHT_PX = 200;\n\n/**\n * Запас окна ленты за краем экрана. Большой рывок пальцем уходит дальше стандартных 200 px,\n * и за краем на кадр-другой видна пустота; запас в экран с лишним её убирает.\n */\nconst BUFFER_PX = 800;\n\n/**\n * Цель прокрутки «в самый низ» для окна ленты. Браузер ставит прокрутку не дальше низа, а число\n * не стареет: низ, до которого окно ленты доводит, всегда нынешний, сколько бы сообщений ни добавилось.\n */\nconst BOTTOM_SCROLL_TARGET = Number.MAX_SAFE_INTEGER;\n/**\n * Окно ленты доводит прокрутку первого показа, пока замеры идут чаще чем раз в 150 мс (virtua, `observer.ts`).\n * Лента считает доводку живой, пока перестановки разметки идут чаще этого срока: с запасом.\n */\nconst OPENING_SCROLL_QUIET_MS = 300;\n\n/** Насколько близко к низу надо быть, чтобы лента сама ехала за новым текстом. */\nconst STICK_TO_BOTTOM_PX = 96;\n\n/** Сколько после нажатия клавиши переноса (End, PageDown…) прыжок вниз считается прыжком читателя. */\nconst KEY_JUMP_WINDOW_MS = 1_000;\n/** Сколько после отпускания мыши на скроллере (полоса прокрутки) прыжок вниз считается прыжком читателя. */\nconst POINTER_JUMP_WINDOW_MS = 300;\n/** Как долго зажатая мышь считается зажатой, если отпускания не пришло. */\nconst POINTER_HOLD_LIMIT_MS = 10_000;\nconst JUMP_KEYS = new Set([\"End\", \"PageDown\", \"ArrowDown\", \" \"]);\n/** Клавиши, которыми читатель двигает ленту в любую сторону. */\nconst SCROLL_KEYS = new Set([...JUMP_KEYS, \"PageUp\", \"ArrowUp\", \"Home\"]);\n/** Сколько после колеса, касания или клавиши шаг прокрутки считается делом читателя, а не вёрстки. */\nconst READER_INPUT_WINDOW_MS = 250;\n/** Клавиши, которыми читатель двигает ленту вверх. */\nconst UP_KEYS = new Set([\"PageUp\", \"ArrowUp\", \"Home\"]);\n\nexport interface MessageFeedHandle {\n /** Ставит прилипание и ведёт ленту вниз: после отправки сообщения. */\n scrollToBottom: () => void;\n}\n\nexport interface MessageFeedProps {\n /** Скроллер скина: элемент, который прокручивается. */\n scrollRef: RefObject<HTMLElement | null>;\n messages: AgentChatMessage[];\n renderMessage: (message: AgentChatMessage, index: number) => ReactElement;\n /** Пока идёт ответ, последнее сообщение остаётся в дереве, даже если окно ленты уехало вверх. */\n keepLastMounted: boolean;\n /** Что показать при первом показе: сообщение с этим id встаёт у верха; по умолчанию лента внизу. */\n openAtMessageId?: string;\n /** Сообщение с открытым полем правки остаётся в дереве за краем окна ленты: поле не теряет фокус и место ввода. */\n keepMountedMessageId?: string;\n ref?: Ref<MessageFeedHandle>;\n}\n\n/** Обёртка сообщения: за неё зацепляются замеры окна ленты и проверки. */\nfunction FeedItem({ style, children, ref }: CustomItemComponentProps) {\n // Координаты окна меняются атомарно с компенсацией прокрутки. Даже переход\n // в 0.01 мс (глобальный reduced-motion CSS) оставляет WebKit на старом top\n // до следующего кадра и разрывает ленту между старыми и новыми строками.\n return (\n <div ref={ref} style={{ ...style, transitionProperty: \"none\" }} data-feed-item=\"\">\n {children}\n </div>\n );\n}\n\n/** Высота служебного контейнера тоже меняется без перехода, вместе с позициями строк. */\nfunction FeedWindow({ style, children, ref }: CustomContainerComponentProps) {\n return <div ref={ref} style={{ ...style, transitionProperty: \"none\" }}>{children}</div>;\n}\n\n/** Поле ввода или элемент внутри редактируемого блока: нажатия в нём ленту не листают. */\nfunction isTextField(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false;\n return target.closest(\"textarea, input, select, [contenteditable]:not([contenteditable='false'])\") !== null;\n}\n\n/**\n * Лента сообщений окном: в дереве только сообщения у экрана и небольшой запас. Правило\n * прокрутки живёт здесь, а не в скине: при первом показе истории лента внизу, у низа она едет\n * за растущим ответом, а после шага читателя вверх (колесо, касание, клавиша) стоит на месте.\n * Снова прилипает, когда читатель вернулся к низу ближе чем на 96 px.\n */\nfunction MessageFeedView({ scrollRef, messages, renderMessage, keepLastMounted, openAtMessageId, keepMountedMessageId, ref }: MessageFeedProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const virtualizerRef = useRef<VirtualizerHandle>(null);\n const stuck = useRef(true);\n // То же прилипание для разметки: окно ленты держит запас над экраном, пока лента едет за низом.\n const [followingBottom, setFollowingBottom] = useState(true);\n const stick = useCallback((value: boolean) => {\n stuck.current = value;\n setFollowingBottom(value);\n }, []);\n // Высота списка при прошлой проверке: рост и прокрутка приходят в одном кадре, и отход от низа\n // надо мерить и по новой высоте, и по прежней, иначе рост сам «отодвигает» читателя от низа.\n const lastHeight = useRef(0);\n const lastTop = useRef(0);\n // Рост может прийти до отложенного native scroll: перед доводкой низа\n // обрабатываем уже изменившуюся позицию тем же правилом, что и в scroll.\n const syncScrollState = useRef<(() => void) | null>(null);\n // На сколько px список сжимался с прошлого события прокрутки: сообщения, добавленные с оценкой высоты, измерены ниже.\n const shrunkSinceScroll = useRef(0);\n const openedAtBottom = useRef(false);\n // До какого момента прыжок вниз больше экрана считается действием читателя (клавиша, полоса прокрутки).\n const userJumpUntil = useRef(0);\n // До какого момента шаг вверх считается делом читателя (колесо, касание, клавиша), даже если он мал.\n const readerInputUntil = useRef(0);\n // До какого момента читатель двигает ленту вверх: пока он ведёт, низ не доводится.\n const readerUpUntil = useRef(0);\n // Высота списка и скроллера при последнем доведении низа: низ доводится, только если список вырос или скроллер стал ниже.\n const settledHeight = useRef(0);\n const settledClientHeight = useRef(Infinity);\n const lastTouchY = useRef<number | null>(null);\n const cancelSettle = useRef<(() => void) | null>(null);\n // До какого момента может быть жива доводка первого показа к сообщению: срок продлевает каждая перестановка разметки.\n const openingScrollUntil = useRef(0);\n // Скроллер скина попадает в `scrollRef` только после монтирования; окно ленты строится после него.\n const [scrollerReady, setScrollerReady] = useState(false);\n const [startMargin, setStartMargin] = useState(0);\n // Пока лента не встала на низ, окно рисует только экран без запаса: первый проход разбирает\n // сообщения с самого верха истории, и чем их меньше, тем меньше работа, которая тут же пропадёт.\n // Запас добавляется, когда лента встала на низ.\n const [positioned, setPositioned] = useState(false);\n const lastIndex = messages.length - 1;\n\n useEffect(() => setScrollerReady(true), []);\n\n useLayoutEffect(() => {\n const scroller = scrollRef.current;\n const root = rootRef.current;\n if (!scrollerReady || !scroller || !root) return;\n const measure = () => {\n const offset = root.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;\n setStartMargin(Math.max(0, Math.round(offset)));\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(scroller);\n return () => observer.disconnect();\n }, [scrollRef, scrollerReady]);\n\n useEffect(() => {\n const scroller = scrollRef.current;\n if (!scrollerReady || !scroller) return;\n lastHeight.current = scroller.scrollHeight;\n lastTop.current = scroller.scrollTop;\n // Координата того же сообщения внутри содержимого отличает компенсацию\n // замеров от прокрутки: при компенсации сообщение остаётся на месте экрана,\n // даже если общая высота списка в этот момент выросла.\n let anchor: { element: HTMLElement; offset: number } | undefined;\n const rememberAnchor = () => {\n const view = scroller.getBoundingClientRect();\n for (const element of rootRef.current?.querySelectorAll<HTMLElement>(\"[data-feed-item]\") ?? []) {\n if (element.style.visibility === \"hidden\") continue;\n const box = element.getBoundingClientRect();\n if (box.bottom > view.top && box.top < view.bottom) {\n anchor = { element, offset: box.top - view.top + scroller.scrollTop };\n return;\n }\n }\n anchor = undefined;\n };\n rememberAnchor();\n const onScroll = () => {\n const height = scroller.scrollHeight;\n const top = scroller.scrollTop;\n const near = Math.min(height, lastHeight.current) - top - scroller.clientHeight < STICK_TO_BOTTOM_PX;\n const shrunk = shrunkSinceScroll.current + Math.max(0, lastHeight.current - height);\n shrunkSinceScroll.current = 0;\n // Прилипание снимает движение вверх дальше порога. Вниз ленту двигает и окно ленты, когда доводит\n // положение после замеров: такой прыжок больше экрана, это не читатель, и прилипания он не ставит.\n // Исключение: прыжок, которому предшествовали клавиша End/PageDown или мышь на полосе прокрутки.\n const step = top - lastTop.current;\n // Вверх ленту двигает и сама вёрстка. Сообщения, что окно ленты считало по прикидке, измерены ниже, список\n // сжался: браузер поджал прокрутку на всё сжатие, а окно ленты, сохраняя место на экране, подвинуло ещё\n // на столько же. Шаг вверх не больше двух сжатий — не читатель: прилипание остаётся, низ доводится.\n // Если перед шагом был ввод читателя, вёрстка ни при чём: иначе читатель боролся бы с лентой.\n const now = performance.now();\n const readerActed = now < readerInputUntil.current || now < userJumpUntil.current;\n const anchorShift = anchor && scroller.contains(anchor.element)\n ? anchor.element.getBoundingClientRect().top - scroller.getBoundingClientRect().top + top - anchor.offset\n : 0;\n const compensated = anchorShift < 0 && Math.abs(step - anchorShift) <= 1;\n const movedByLayout = step < 0 && (-step <= 2 * shrunk || compensated) && !readerActed;\n lastTop.current = top;\n lastHeight.current = height;\n // Снимок обновляется после сравнения, а не по MutationObserver: мутация\n // координат приходит раньше соответствующего события прокрутки.\n rememberAnchor();\n if (movedByLayout) {\n if (stuck.current) scroller.scrollTop = height;\n return;\n }\n // Шаг вверх после ввода читателя снимает прилипание сразу, без порога: медленный тачпад шагает мельче 96 px,\n // а каждый шаг окно ленты встречает перестановкой разметки, и прилипание, оставшееся стоять, возвращало бы читателя вниз.\n if (step < 0) stick(readerActed ? false : near);\n else if (step > 0) {\n // Читатель повернул вниз: вверх он больше не ведёт.\n readerUpUntil.current = 0;\n if (near && (step <= scroller.clientHeight || performance.now() < userJumpUntil.current)) stick(true);\n }\n };\n const onKeyDown = (event: KeyboardEvent) => {\n // Клавиши из поля ввода двигают курсор и текст, а не ленту: набор не должен снимать прилипание.\n if (isTextField(event.composedPath()[0] ?? event.target)) return;\n if (JUMP_KEYS.has(event.key)) userJumpUntil.current = performance.now() + KEY_JUMP_WINDOW_MS;\n if (SCROLL_KEYS.has(event.key)) readerInputUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n if (UP_KEYS.has(event.key)) readerUpUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n };\n const onWheel = (event: WheelEvent) => {\n readerInputUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n if (event.deltaY < 0) readerUpUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n };\n const onTouchStart = (event: TouchEvent) => {\n lastTouchY.current = event.touches?.[0]?.clientY ?? null;\n };\n const onTouchMove = (event: TouchEvent) => {\n readerInputUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n // Палец идёт вниз — лента едет вверх. Если направление неизвестно, считаем, что вверх.\n const y = event.touches?.[0]?.clientY;\n const up = y === undefined || lastTouchY.current === null || y >= lastTouchY.current;\n lastTouchY.current = y ?? null;\n if (up) readerUpUntil.current = performance.now() + READER_INPUT_WINDOW_MS;\n };\n const onPointerDown = () => {\n userJumpUntil.current = performance.now() + POINTER_HOLD_LIMIT_MS;\n };\n const onPointerUp = () => {\n if (userJumpUntil.current > performance.now() + POINTER_JUMP_WINDOW_MS) userJumpUntil.current = performance.now() + POINTER_JUMP_WINDOW_MS;\n };\n syncScrollState.current = onScroll;\n scroller.addEventListener(\"scroll\", onScroll, { passive: true });\n document.addEventListener(\"keydown\", onKeyDown);\n scroller.addEventListener(\"pointerdown\", onPointerDown);\n scroller.addEventListener(\"wheel\", onWheel, { passive: true });\n scroller.addEventListener(\"touchstart\", onTouchStart, { passive: true });\n scroller.addEventListener(\"touchmove\", onTouchMove, { passive: true });\n window.addEventListener(\"pointerup\", onPointerUp);\n window.addEventListener(\"pointercancel\", onPointerUp);\n return () => {\n syncScrollState.current = null;\n scroller.removeEventListener(\"scroll\", onScroll);\n document.removeEventListener(\"keydown\", onKeyDown);\n scroller.removeEventListener(\"pointerdown\", onPointerDown);\n scroller.removeEventListener(\"wheel\", onWheel);\n scroller.removeEventListener(\"touchstart\", onTouchStart);\n scroller.removeEventListener(\"touchmove\", onTouchMove);\n window.removeEventListener(\"pointerup\", onPointerUp);\n window.removeEventListener(\"pointercancel\", onPointerUp);\n };\n }, [scrollRef, scrollerReady, stick]);\n\n // Лента выросла или скроллер стал ниже, а читатель у низа: довести низ до края.\n useEffect(() => {\n const scroller = scrollRef.current;\n const root = rootRef.current;\n if (!scrollerReady || !scroller || !root) return;\n const settleAtBottom = () => {\n // scrollTop меняется сразу, а событие может задержаться до следующего\n // цикла. Нельзя затереть ещё не обработанный уход читателя ростом ответа.\n if (scroller.scrollTop < lastTop.current) syncScrollState.current?.();\n const height = scroller.scrollHeight;\n const clientHeight = scroller.clientHeight;\n shrunkSinceScroll.current += Math.max(0, lastHeight.current - height);\n lastHeight.current = height;\n // Любая перестановка разметки окном ленты (шаг прокрутки уводит одни сообщения и приводит другие) сюда доходит,\n // но низ нужен только после роста списка или когда скроллер стал ниже. И не пока читатель ведёт ленту вверх.\n const needed = height > settledHeight.current || clientHeight < settledClientHeight.current;\n settledHeight.current = height;\n settledClientHeight.current = clientHeight;\n if (stuck.current && needed && performance.now() >= readerUpUntil.current) scroller.scrollTop = height;\n };\n // Окно ленты подправляет положение после замеров уже после наблюдателя: в следующем кадре низ доводится ещё раз.\n let followUp = 0;\n const follow = () => {\n if (openingScrollUntil.current > 0) openingScrollUntil.current = performance.now() + OPENING_SCROLL_QUIET_MS;\n settleAtBottom();\n if (followUp === 0)\n followUp = requestAnimationFrame(() => {\n followUp = 0;\n settleAtBottom();\n });\n };\n // Высоту списка меняет окно ленты, когда дописывается ответ: по наблюдателю за разметкой низ\n // доводится в том же кадре. Наблюдатель за размером корня здесь не годится: окно ленты меняет\n // корень изнутри собственной проверки размеров, и браузер переносит доставку на следующий кадр\n // с жалобой «ResizeObserver loop completed with undelivered notifications».\n const listObserver = new MutationObserver(follow);\n listObserver.observe(root, { subtree: true, childList: true, attributes: true, attributeFilter: [\"style\"] });\n // Скроллер стал ниже (экранная клавиатура, выросло поле ввода): низ тоже надо довести до края.\n const scrollerObserver = new ResizeObserver(follow);\n scrollerObserver.observe(scroller);\n return () => {\n listObserver.disconnect();\n scrollerObserver.disconnect();\n cancelAnimationFrame(followUp);\n };\n }, [scrollRef, scrollerReady]);\n\n // Первый показ истории: сразу внизу.\n useLayoutEffect(() => {\n if (messages.length === 0) {\n openedAtBottom.current = false;\n openingScrollUntil.current = 0;\n cancelSettle.current?.();\n setPositioned(false);\n return;\n }\n const scroller = scrollRef.current;\n if (!scrollerReady || !scroller || openedAtBottom.current) return;\n openedAtBottom.current = true;\n const focusIndex = openAtMessageId === undefined ? -1 : messages.findIndex((message) => message.id === openAtMessageId);\n stick(focusIndex < 0 || focusIndex === lastIndex);\n // Доводка окна ленты переживает первый показ, если замеры не затихают: ответ пришёл и сразу отправлен\n // следующий вопрос. Цель «низ» не стареет; цель «сообщение» заменяет отправка (`scrollToBottom`).\n if (focusIndex >= 0 && focusIndex < lastIndex) {\n virtualizerRef.current?.scrollToIndex(focusIndex, { align: \"start\" });\n openingScrollUntil.current = performance.now() + OPENING_SCROLL_QUIET_MS;\n } else {\n // virtua подключает scrollRef в микрозадаче: первый scroll должен\n // прийти после её слушателя. Дальнейшие замеры доводит наш наблюдатель,\n // без асинхронной цели virtua, которая тянула бы читателя обратно.\n queueMicrotask(() => {\n if (rootRef.current?.isConnected && scrollRef.current === scroller && stuck.current) scroller.scrollTop = scroller.scrollHeight;\n });\n openingScrollUntil.current = 0;\n }\n // Прыжок вниз видно по событию прокрутки; в короткой ленте прыжка нет, и хватает пары кадров.\n let frame = 0;\n const cancel = () => {\n scroller.removeEventListener(\"scroll\", settle);\n cancelAnimationFrame(frame);\n cancelSettle.current = null;\n };\n const settle = () => {\n cancel();\n setPositioned(true);\n };\n cancelSettle.current = cancel;\n scroller.addEventListener(\"scroll\", settle, { once: true });\n frame = requestAnimationFrame(() => {\n frame = requestAnimationFrame(settle);\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps -- открытие один раз: id и сообщения читаются в этот момент\n }, [scrollRef, scrollerReady, messages.length, lastIndex]);\n\n // Пары кадров и слушателя первого показа не должны пережить ленту.\n useEffect(() => () => cancelSettle.current?.(), []);\n\n // Без `scrollToIndex` окна ленты: оно держит цель, пока идут замеры, а ответ дописывается сплошным\n // потоком замеров, и читателя, ушедшего вверх, тянуло бы обратно. Лента сама доезжает до низа:\n // высота списка меняется по мере замеров, и наблюдатель за ней (выше) снова опускает низ, пока\n // прилипание стоит.\n // Исключение — ещё живая доводка первого показа к сообщению: она тянула бы ленту обратно к нему.\n // Её можно только заменить новой, и цель новой — низ.\n const scrollToBottom = useCallback(() => {\n stick(true);\n if (performance.now() < openingScrollUntil.current) virtualizerRef.current?.scrollTo(BOTTOM_SCROLL_TARGET);\n openingScrollUntil.current = 0;\n const scroller = scrollRef.current;\n if (scroller) scroller.scrollTop = scroller.scrollHeight;\n }, [scrollRef, stick]);\n useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);\n\n // Пока лента едет вниз, окно ленты не держит запаса над экраном, а через 150 мс тишины возвращает его: готовые\n // сообщения над экраном снимались бы с каждым шагом за растущим ответом и разбирались заново. Пока лента прилипла\n // к низу, запас над экраном держится здесь.\n const virtualizer = virtualizerRef.current;\n const editedIndex = keepMountedMessageId === undefined ? -1 : messages.findIndex((message) => message.id === keepMountedMessageId);\n // Низ DOM и оценки окна могут временно разойтись после замеров (особенно в WebKit).\n // Следование за текущим низом не означает, что в окне последний индекс истории:\n // закрепление «до lastIndex» в таком кадре монтировало сотни сообщений.\n const scroller = scrollRef.current;\n const keepBuffer = positioned && followingBottom && virtualizer && scroller && lastIndex >= 0;\n const bufferStart = keepBuffer ? Math.max(0, virtualizer.findItemIndex(scroller.scrollTop - BUFFER_PX)) : 0;\n const bufferEnd = keepBuffer ? Math.min(lastIndex, virtualizer.findItemIndex(scroller.scrollTop + scroller.clientHeight)) : -1;\n const keepMounted = useMemo(() => {\n const kept = new Set(Array.from({ length: Math.max(0, bufferEnd - bufferStart + 1) }, (_, offset) => bufferStart + offset));\n if (keepLastMounted && lastIndex >= 0) kept.add(lastIndex);\n if (editedIndex >= 0) kept.add(editedIndex);\n return [...kept].sort((a, b) => a - b);\n }, [bufferEnd, bufferStart, editedIndex, keepLastMounted, lastIndex]);\n\n return (\n <div ref={rootRef}>\n {scrollerReady && (\n <Virtualizer\n ref={virtualizerRef}\n scrollRef={scrollRef}\n data={messages}\n itemSize={UNMEASURED_MESSAGE_HEIGHT_PX}\n bufferSize={positioned ? BUFFER_PX : 0}\n item={FeedItem}\n as={FeedWindow}\n keepMounted={keepMounted}\n startMargin={startMargin}\n >\n {(message, index) => renderMessage(message, index)}\n </Virtualizer>\n )}\n </div>\n );\n}\n\n/** `memo`: ввод в поле и другие перерисовки чата, не меняющие ленту, её не трогают. */\nexport const MessageFeed = memo(MessageFeedView);\n","import { useCallback, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { RefObject } from \"react\";\nimport { messageText } from \"../chat-types\";\nimport type { AgentChatMessage } from \"../chat-types\";\n\n/**\n * Открытое поле правки сообщения человека. Набранный текст живёт здесь, а не в самом поле: поле пересоздаётся, когда\n * окно ленты убирает его за краем экрана, когда лента открывается заново (переход ветки) или сообщение приходит с\n * новым id, — и появляется снова с тем, что человек набрал.\n */\nexport interface MessageEditing {\n /** id сообщения с открытым полем правки; `null` — поле закрыто. */\n id: string | null;\n /** Номер попытки сохранения: после отказа поле открывается заново с ошибкой. */\n attempt: number;\n /** Отказ сервера на последнюю попытку. */\n error?: string;\n /** С каким текстом открыть поле: набранное раньше или текст сообщения. */\n draftOf: (message: AgentChatMessage) => string;\n open: (messageId: string) => void;\n /** Человек набирает: перерисовки ленты это не вызывает. */\n type: (text: string) => void;\n close: () => void;\n /** Сервер отказал правке: поле открывается снова с отправленным текстом и ошибкой. */\n refuse: (messageId: string, text: string, error: string) => void;\n}\n\ninterface OpenEditor {\n id: string;\n attempt: number;\n error?: string;\n /** Текст и место сообщения при открытии: по ним находится то же сообщение, пришедшее с новым id. */\n original: string;\n index: number;\n}\n\n/**\n * Состояние правки по ленте `messages`. Сообщение пришло с новым id (перечитанная история) — поле остаётся у него.\n * Сообщения больше нет — поле закрывается, а набранный текст отдаётся `onDraftLost` (чат кладёт его в поле ввода).\n * Пока идёт сохранение (`saving`), лента меняется самой правкой: это не пропажа.\n */\nexport function useMessageEditing(\n messages: readonly AgentChatMessage[],\n { saving, onDraftLost }: { saving: RefObject<boolean>; onDraftLost: (text: string) => void },\n): MessageEditing {\n const [editor, setEditor] = useState<OpenEditor | null>(null);\n const draft = useRef<string | null>(null);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const reportLost = useRef(onDraftLost);\n reportLost.current = onDraftLost;\n\n useLayoutEffect(() => {\n if (!editor || saving.current || messages.some((message) => message.id === editor.id)) return;\n const same = sameMessage(messages, editor);\n if (same) {\n setEditor({ ...editor, id: same.id });\n return;\n }\n const typed = draft.current;\n draft.current = null;\n setEditor(null);\n if (typed !== null && typed.trim().length > 0 && typed !== editor.original) reportLost.current(typed);\n }, [editor, messages, saving]);\n\n const open = useCallback((messageId: string) => {\n const index = messagesRef.current.findIndex((message) => message.id === messageId);\n const message = messagesRef.current[index];\n if (!message) return;\n draft.current = null;\n setEditor({ id: messageId, attempt: 0, original: messageText(message), index });\n }, []);\n const close = useCallback(() => {\n draft.current = null;\n setEditor(null);\n }, []);\n const type = useCallback((text: string) => {\n draft.current = text;\n }, []);\n const refuse = useCallback((messageId: string, text: string, error: string) => {\n draft.current = text;\n setEditor((current) => current && { ...current, id: messageId, attempt: current.attempt + 1, error });\n }, []);\n const draftOf = useCallback((message: AgentChatMessage) => draft.current ?? messageText(message), []);\n\n return useMemo(\n () => ({\n id: editor?.id ?? null,\n attempt: editor?.attempt ?? 0,\n ...(editor?.error !== undefined && { error: editor.error }),\n draftOf,\n open,\n type,\n close,\n refuse,\n }),\n [close, draftOf, editor, open, refuse, type],\n );\n}\n\n/** То же сообщение под новым id: сообщение человека с тем же текстом на прежнем месте, иначе единственное такое. */\nfunction sameMessage(messages: readonly AgentChatMessage[], editor: OpenEditor): AgentChatMessage | undefined {\n const isSame = (message: AgentChatMessage | undefined) => message?.role === \"user\" && messageText(message) === editor.original;\n const atPlace = messages[editor.index];\n if (isSame(atPlace)) return atPlace;\n const matches = messages.filter(isSame);\n return matches.length === 1 ? matches[0] : undefined;\n}\n","import type { ReactNode } from \"react\";\nimport type { AgentChatMessage } from \"../chat-types\";\n\n/** Что в ответе изменено человеком: текст и (или) результаты вызовов инструментов. */\nexport interface AnswerEditMark {\n textEdited: boolean;\n editedToolCallIds: string[];\n}\n\n/** Что получает редактор ответа, который рисует страница. */\nexport interface AnswerEditorSlot {\n message: AgentChatMessage;\n /** Сервер принял правку и создал ответ `newMessageId`: чат переходит на его ветку и закрывает редактор. */\n onSaved: (newMessageId: string) => void;\n onCancel: () => void;\n}\n\n/**\n * Правка ответов агента (песочница). Чат даёт законченным ответам кнопку правки, показывает\n * отметки «изменено» и ставит редактор страницы на место ответа. Без этого свойства ответы не правятся.\n */\nexport interface AnswerEditing {\n /** Отметки правок по id ответа. */\n edits: ReadonlyMap<string, AnswerEditMark>;\n renderEditor: (slot: AnswerEditorSlot) => ReactNode;\n}\n\n/** Изменён ли ответ человеком хоть в чём-то. */\nexport function answerEdited(mark: AnswerEditMark | undefined): boolean {\n return mark !== undefined && (mark.textEdited || mark.editedToolCallIds.length > 0);\n}\n","import type { DiagramsClient } from \"../agent-chat-extensions\";\nimport type { DiagramColors } from \"../skin-contract\";\n\nexport type DiagramRenderResult = { svg: string } | { error: string };\n\n/** Столько готовых рисунков помнит чат; самый старый забывается первым. */\nconst KEPT_RESULTS = 200;\n\n/**\n * Готовые рисунки чата по `source + colors`. Рисунок, уже готовый, отдаётся сразу: сообщение, которое лента убрала\n * и вернула, показывает схему без заглушки, и лента не прыгает.\n */\nexport interface DiagramRenders {\n get(source: string, colors: DiagramColors): DiagramRenderResult | undefined;\n request(source: string, colors: DiagramColors): Promise<DiagramRenderResult>;\n}\n\nexport function createDiagramRenders(client: DiagramsClient): DiagramRenders {\n const results = new Map<string, DiagramRenderResult>();\n const inFlight = new Map<string, Promise<DiagramRenderResult>>();\n const keyOf = (source: string, colors: DiagramColors) => `${JSON.stringify(colors)}\\n${source}`;\n return {\n get: (source, colors) => results.get(keyOf(source, colors)),\n request(source, colors) {\n const key = keyOf(source, colors);\n const ready = results.get(key);\n if (ready) return Promise.resolve(ready);\n const running = inFlight.get(key);\n if (running) return running;\n const result = Promise.resolve()\n .then(() => client.render(source, colors))\n .catch((error: unknown): DiagramRenderResult => ({ error: error instanceof Error ? error.message : String(error) }))\n .then((outcome) => {\n inFlight.delete(key);\n if (results.size >= KEPT_RESULTS) results.delete(results.keys().next().value!);\n results.set(key, outcome);\n return outcome;\n });\n inFlight.set(key, result);\n return result;\n },\n };\n}\n","import { useCallback, useEffect, useId, useMemo, useState } from \"react\";\nimport type { MediaViewerItem } from \"../media-viewer/media-viewer-types\";\nimport type { DiagramColors, DiagramView } from \"../skin-contract\";\nimport type { DiagramRenders } from \"./diagram-renders\";\nimport { splitMermaidBlocks } from \"./mermaid-blocks\";\nimport { withUniqueIds } from \"./unique-svg-ids\";\n\n/** Сколько блоков ```mermaid в тексте: по нему номера схем следующей текстовой части продолжают нумерацию ответа. */\nexport const countMermaidBlocks = (markdown: string) => splitMermaidBlocks(markdown).filter((segment) => segment.kind === \"mermaid\").length;\n\nexport interface UseDiagramBlocksOptions {\n markdown: string;\n /** Текстовая часть ещё печатается. */\n streaming: boolean;\n messageId: string;\n /** Номер первой схемы этой части в ответе: схемы прежних текстовых частей того же ответа. */\n firstBlock: number;\n renders: DiagramRenders;\n colors: DiagramColors;\n /** Открыть окно просмотра своим набором; нет — схема не открывается. */\n open?: (key: string, trigger: HTMLElement, items: readonly MediaViewerItem[]) => void;\n}\n\n/**\n * Схемы одной текстовой части для скина (`AgentText.renderDiagram`/`onOpenDiagram`). Незакрытый блок, пока текст\n * печатается, — `streaming`; закрытый рисуется сразу, не дожидаясь конца ответа; дальше `ready` или `failed`.\n */\nexport function useDiagramBlocks({ markdown, streaming, messageId, firstBlock, renders, colors, open }: UseDiagramBlocksOptions) {\n const blocks = useMemo(\n () => splitMermaidBlocks(markdown).flatMap((segment) => (segment.kind === \"mermaid\" ? [segment] : [])),\n [markdown],\n );\n const [version, setVersion] = useState(0);\n // Свой суффикс id у каждой вставки рисунка: в ленте и в окне просмотра (withUniqueIds).\n const insertion = useId();\n\n // Блок, который можно рисовать: закрыт или текст уже не печатается (незакрытый блок в готовом ответе — тоже схема).\n useEffect(() => {\n let alive = true;\n for (const block of blocks) {\n if ((block.closed || !streaming) && !renders.get(block.source, colors)) {\n void renders.request(block.source, colors).then(() => alive && setVersion((value) => value + 1));\n }\n }\n return () => {\n alive = false;\n };\n }, [blocks, colors, renders, streaming]);\n\n const renderDiagram = useCallback(\n (source: string, blockIndex: number): DiagramView => {\n const block = blocks[blockIndex];\n if (streaming && block && !block.closed) return { state: \"streaming\" };\n const result = renders.get(source, colors);\n if (!result) return { state: \"streaming\" };\n return \"svg\" in result ? { state: \"ready\", svg: withUniqueIds(result.svg, `${insertion}-${blockIndex}`), source } : { state: \"failed\", source };\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `version`: готов новый рисунок, функция новая, скин рисует блок заново\n [blocks, colors, insertion, renders, streaming, version],\n );\n\n const onOpenDiagram = useCallback(\n (blockIndex: number, trigger: HTMLElement) => {\n const block = blocks[blockIndex];\n const result = block && renders.get(block.source, colors);\n if (!open || !block || !result || !(\"svg\" in result)) return;\n const key = `${messageId}:${firstBlock + blockIndex}`;\n const svg = withUniqueIds(result.svg, `${insertion}-${blockIndex}-viewer`);\n open(key, trigger, [{ kind: \"diagram\", key, svg, source: block.source }]);\n },\n [blocks, colors, firstBlock, insertion, messageId, open, renders],\n );\n\n return { renderDiagram, ...(open && { onOpenDiagram }) };\n}\n","const ID_ATTRIBUTE = /\\sid=\"([^\"]+)\"/g;\n/** `id=\"…\"` или ссылка `#…` (в `url(#…)`, `href=\"#…\"` и селекторах стилей). */\nconst ID_OR_REFERENCE = /(\\sid=\")([^\"]+)(\")|#([\\p{L}\\p{N}_-]+)/gu;\n\n/**\n * Разметка схемы со своими id для одной вставки в страницу. Один готовый рисунок из кэша может стоять в ленте\n * дважды и ещё в окне просмотра; при одинаковых id `url(#…)` берёт первый в странице, и стрелки пропадают вместе\n * с ним. Каждый id рисунка получает суффикс вставки, ссылки на него в атрибутах и стилях — тоже.\n */\nexport function withUniqueIds(svg: string, suffix: string): string {\n const ids = new Set([...svg.matchAll(ID_ATTRIBUTE)].map((match) => match[1]!));\n if (ids.size === 0) return svg;\n const tail = `-${suffix.replace(/[^\\w-]/g, \"\")}`;\n return svg.replace(ID_OR_REFERENCE, (whole, open?: string, id?: string, close?: string, reference?: string) => {\n if (open !== undefined) return `${open}${id}${tail}${close}`;\n return ids.has(reference!) ? `#${reference}${tail}` : whole;\n });\n}\n","import { useMemo, useSyncExternalStore } from \"react\";\nimport type { ChatSkin, DiagramColors } from \"../skin-contract\";\n\n/** Цвета, если скин своих не дал: спокойные серые, читаются на любом фоне. */\nconst FALLBACK_COLORS: Record<\"light\" | \"dark\", DiagramColors> = {\n light: { background: \"#ffffff\", text: \"#1f2328\", line: \"#6e7781\", node: \"#f3f4f6\", accent: \"#0969da\", dark: false },\n dark: { background: \"#16181b\", text: \"#e6e8eb\", line: \"#8b949e\", node: \"#24282d\", accent: \"#58a6ff\", dark: true },\n};\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\n/**\n * Тёмная ли сейчас страница: класс `dark` у корня (так включают тему Тандем и Tailwind), иначе `color-scheme` корня,\n * а если он не задан или разрешает обе — тема системы.\n */\nfunction pageIsDark(): boolean {\n const root = document.documentElement;\n if (root.classList.contains(\"dark\")) return true;\n const scheme = getComputedStyle(root).colorScheme?.trim() ?? \"\";\n if (/\\bdark\\b/.test(scheme) && !/\\blight\\b/.test(scheme)) return true;\n if (/\\blight\\b/.test(scheme) && !/\\bdark\\b/.test(scheme)) return false;\n return typeof matchMedia === \"function\" && matchMedia(DARK_QUERY).matches;\n}\n\nfunction subscribe(onChange: () => void): () => void {\n const observer = new MutationObserver(onChange);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\", \"style\", \"data-theme\"] });\n const media = typeof matchMedia === \"function\" ? matchMedia(DARK_QUERY) : null;\n media?.addEventListener(\"change\", onChange);\n return () => {\n observer.disconnect();\n media?.removeEventListener(\"change\", onChange);\n };\n}\n\nconst neverSubscribe = () => () => undefined;\n\n/** Тёмная ли страница, со слежением за сменой темы; `enabled: false` — не следит и отдаёт `false`. */\nexport function usePageDark(enabled: boolean): boolean {\n return useSyncExternalStore(enabled ? subscribe : neverSubscribe, () => enabled && pageIsDark(), () => false);\n}\n\n/**\n * Цвета схем для текущей темы страницы (FR-028): `skin.diagramColors(dark)`, без него — свои. Смена темы даёт новые\n * цвета, и схемы перерисовываются. `enabled: false` (схемы не подключены) — за темой не следит и отдаёт `undefined`.\n */\nexport function useDiagramColors(skin: ChatSkin, enabled: boolean): DiagramColors | undefined {\n const dark = usePageDark(enabled);\n return useMemo(() => {\n if (!enabled) return undefined;\n return skin.diagramColors?.(dark) ?? FALLBACK_COLORS[dark ? \"dark\" : \"light\"];\n }, [dark, enabled, skin]);\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport { usePageDark } from \"../diagrams/use-diagram-colors\";\nimport { useChatLabels } from \"../labels\";\nimport type { ChatSkin, LiveDemoColors, LiveDemoView } from \"../skin-contract\";\nimport { splitAnswerFences } from \"./answer-fences\";\nimport { FALLBACK_DEMO_COLORS, liveDemoFits } from \"./live-demo-document\";\nimport { LiveDemoFrame } from \"./live-demo-frame\";\n\n/**\n * Цвета демонстраций для текущей темы страницы: `skin.demoColors(dark)`, без него — свои. Смена темы даёт новые цвета,\n * и демонстрации перерисовываются. `enabled: false` (демонстрации не подключены) — за темой не следит, `undefined`.\n */\nexport function useDemoColors(skin: ChatSkin, enabled: boolean): LiveDemoColors | undefined {\n const dark = usePageDark(enabled);\n return useMemo(() => {\n if (!enabled) return undefined;\n return skin.demoColors?.(dark) ?? FALLBACK_DEMO_COLORS[dark ? \"dark\" : \"light\"];\n }, [dark, enabled, skin]);\n}\n\n/**\n * Живые демонстрации одной текстовой части для скина (`AgentText.renderDemo`). Незакрытый блок, пока текст печатается,\n * — `streaming`; закрытый запускается сразу, не дожидаясь конца ответа. Код не запускается, если он больше предела, не\n * дописан к концу ответа, уже пытался уйти со своей страницы или в чате чужой текст (`blocked`: сервер сообщил, что в чат\n * попали веб, файл или инструмент с пометкой). Нет цветов (демонстрации не подключены) — `undefined`.\n */\nexport function useLiveDemoBlocks({ markdown, streaming, colors, blocked }: { markdown: string; streaming: boolean; colors: LiveDemoColors | undefined; blocked: boolean }) {\n const labels = useChatLabels();\n const blocks = useMemo(\n () => (colors ? splitAnswerFences(markdown, { demos: true }).flatMap((segment) => (segment.kind === \"demo\" ? [segment] : [])) : []),\n [colors, markdown],\n );\n const [leftPage, setLeftPage] = useState<ReadonlySet<string>>(() => new Set());\n const renderDemo = useCallback(\n (source: string, blockIndex: number): LiveDemoView => {\n if (blocks[blockIndex]?.closed === false) return streaming ? { state: \"streaming\" } : { state: \"code\", source, reason: \"unfinished\" };\n if (!liveDemoFits(source)) return { state: \"code\", source, reason: \"tooLarge\" };\n if (leftPage.has(source)) return { state: \"code\", source, reason: \"leftPage\" };\n if (blocked) return { state: \"code\", source, reason: \"externalContent\" };\n const onLeftPage = () => setLeftPage((sources) => new Set(sources).add(source));\n return { state: \"ready\", source, frame: <LiveDemoFrame source={source} colors={colors!} title={labels.demo.title} onLeftPage={onLeftPage} /> };\n },\n [blocked, blocks, colors, labels, leftPage, streaming],\n );\n return colors ? renderDemo : undefined;\n}\n","import { memo, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { LiveDemoColors } from \"../skin-contract\";\nimport { LIVE_DEMO_SANDBOX, demoHeightOf, demoLeftPage, liveDemoDocument } from \"./live-demo-document\";\n\n/** Высота рамки, пока документ не сообщил свою. */\nconst INITIAL_HEIGHT = 160;\n\n/** Две трети высоты окна: выше рамка не растёт, дальше прокрутка внутри. */\nfunction useTwoThirdsOfScreen(): number {\n const [limit, setLimit] = useState(() => Math.round((window.innerHeight * 2) / 3));\n useEffect(() => {\n const update = () => setLimit(Math.round((window.innerHeight * 2) / 3));\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, []);\n return limit;\n}\n\nexport interface LiveDemoFrameProps {\n source: string;\n colors: LiveDemoColors;\n /** Подпись рамки для диктора. */\n title: string;\n /** Код ушёл со своей страницы (сообщение промежуточного документа или вторая загрузка рамки). */\n onLeftPage: () => void;\n}\n\n/**\n * Рамка живой демонстрации: изолированная песочница, документ через `srcdoc`, ширина во весь ответ, высота — из\n * сообщения своей рамки. Новые цвета — новый документ: демонстрация перерисовывается.\n */\nexport const LiveDemoFrame = memo(function LiveDemoFrame({ source, colors, title, onLeftPage }: LiveDemoFrameProps) {\n const srcDoc = useMemo(() => liveDemoDocument(source, colors), [source, colors]);\n return <Frame key={srcDoc} srcDoc={srcDoc} dark={colors.dark} title={title} onLeftPage={onLeftPage} />;\n});\n\nfunction Frame({ srcDoc, dark, title, onLeftPage }: { srcDoc: string; dark: boolean; title: string; onLeftPage: () => void }) {\n const frame = useRef<HTMLIFrameElement>(null);\n const loads = useRef(0);\n const [height, setHeight] = useState(INITIAL_HEIGHT);\n const limit = useTwoThirdsOfScreen();\n const leftPage = useRef(onLeftPage);\n leftPage.current = onLeftPage;\n\n useEffect(() => {\n const onMessage = (event: MessageEvent) => {\n const frameWindow = frame.current?.contentWindow;\n if (demoLeftPage(event, frameWindow)) return leftPage.current();\n const reported = demoHeightOf(event, frameWindow);\n if (reported !== null) setHeight(reported);\n };\n window.addEventListener(\"message\", onMessage);\n return () => window.removeEventListener(\"message\", onMessage);\n }, []);\n\n return (\n <iframe\n ref={frame}\n title={title}\n sandbox={LIVE_DEMO_SANDBOX}\n srcDoc={srcDoc}\n referrerPolicy=\"no-referrer\"\n data-live-demo-frame=\"\"\n onLoad={() => {\n if (++loads.current > 1) leftPage.current();\n }}\n // Схема цветов рамки та же, что у документа: иначе браузер подложит под прозрачный документ непрозрачный фон.\n style={{ display: \"block\", width: \"100%\", height: `${Math.min(height, limit)}px`, border: 0, colorScheme: dark ? \"dark\" : \"light\" }}\n />\n );\n}\n","import type { MessageAttachmentsView } from \"../attachments-contract\";\nimport type { AgentChatMessage } from \"../chat-types\";\nimport type { MediaViewerItem } from \"./media-viewer-types\";\n\nexport interface CollectViewerItemsInput {\n /** Сообщения текущей ветки по порядку ленты. */\n messages: readonly AgentChatMessage[];\n /** `describe` плагина вложений; нет — у человека картинок нет. */\n describeAttachments?: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined;\n /** Свои элементы сообщения (картинки агента): встают на место сообщения в ленте, после его вложений. */\n extraItems?: ((message: AgentChatMessage) => readonly MediaViewerItem[]) | undefined;\n}\n\n/**\n * Карусель окна просмотра: все картинки текущей ветки по порядку ленты. Одна картинка в нескольких сообщениях\n * (то же вложение) — один элемент на первом месте: `key` по ней возвращает фокус и должен быть единственным.\n */\nexport function collectViewerItems({ messages, describeAttachments, extraItems }: CollectViewerItemsInput): MediaViewerItem[] {\n const items: MediaViewerItem[] = [];\n const seen = new Set<string>();\n const add = (item: MediaViewerItem) => {\n if (seen.has(item.key)) return;\n seen.add(item.key);\n items.push(item);\n };\n for (const message of messages) {\n // Вложения и у человека, и в ответе агента (импорт переписки).\n if (describeAttachments) {\n for (const file of describeAttachments(message)?.items ?? []) {\n if (file.kind === \"image\") add({ kind: \"image\", key: file.id, src: file.url, alt: file.name });\n }\n }\n for (const item of extraItems?.(message) ?? []) add(item);\n }\n return items;\n}\n","import type { MessageAttachmentItem, MessageAttachmentsView } from \"../attachments-contract\";\nimport type { AgentChatMessage } from \"../chat-types\";\nimport type { GeneratedImagesView } from \"../skin-contract\";\n\nconst noOpen = () => {};\n\n/** Размеров картинки вложения клиент не знает: заглушка держит квадрат, готовая картинка — свои пропорции. */\nconst UNKNOWN_ASPECT_RATIO = \"1:1\";\n\n/** Как показать часть `file` ответа агента: группой картинок или строкой со ссылкой на файл (текст ответа). */\nexport type AnswerFileView = { kind: \"images\"; view: GeneratedImagesView } | { kind: \"file\"; markdown: string };\n\n/**\n * Файлы в ответе агента (импорт переписки). Часть узнаётся по адресу (`MessageAttachmentItem.partUrl`), не по порядку.\n * С `withImages` подряд идущие картинки — одна группа `GeneratedImages` на месте первой; ключ — номер части, `null` —\n * часть уже в группе выше. Остальное (не картинка, неизвестная плагину часть, скин без `GeneratedImages`) — строкой\n * «📎 имя» со ссылкой, если адрес известен: файл не пропадает молча.\n */\nexport function answerFileViews(\n message: AgentChatMessage,\n describe: ((message: AgentChatMessage) => MessageAttachmentsView | undefined) | undefined,\n withImages: boolean,\n): Map<number, AnswerFileView | null> {\n const views = new Map<number, AnswerFileView | null>();\n if (!message.parts.some((part) => part.type === \"file\")) return views;\n const byPartUrl = new Map<string, MessageAttachmentItem>();\n for (const item of describe?.(message)?.items ?? []) if (item.partUrl !== undefined) byPartUrl.set(item.partUrl, item);\n let open: GeneratedImagesView | null = null;\n message.parts.forEach((part, index) => {\n if (part.type !== \"file\") {\n open = null;\n return;\n }\n const file = byPartUrl.get(part.url);\n if (!withImages || file?.kind !== \"image\") {\n open = null;\n const name = file?.name ?? part.filename ?? part.mediaType;\n views.set(index, { kind: \"file\", markdown: file ? `📎 [${escapeLinkText(name)}](<${file.url}>)` : `📎 ${name}` });\n return;\n }\n const item = { state: \"ready\" as const, key: file.id, src: file.url, alt: file.name, onOpen: file.onOpen ?? noOpen };\n if (open) {\n open.items.push(item);\n open.ordered++;\n views.set(index, null);\n } else {\n open = { ordered: 1, aspectRatio: UNKNOWN_ASPECT_RATIO, items: [item], stopped: false };\n views.set(index, { kind: \"images\", view: open });\n }\n });\n return views;\n}\n\nfunction escapeLinkText(text: string): string {\n return text.replace(/[\\\\[\\]]/g, (char) => `\\\\${char}`);\n}\n","import { useRef } from \"react\";\nimport type { ChatSkin } from \"../skin-contract\";\n\nconst nothing = () => {};\n\n/**\n * Чат объекта ещё ищется: тот же вид, что у обычного чата, пока грузится история, — пустая лента и поле ввода без\n * отправки. Экран не прыгает, когда чат найден.\n */\nexport function SubjectChatLoading({ skin }: { skin: ChatSkin }) {\n const scrollRef = useRef<HTMLDivElement | null>(null);\n return (\n <skin.Layout\n feed={null}\n scrollRef={scrollRef}\n announcement=\"\"\n composer={<skin.Composer value=\"\" onValueChange={nothing} onSubmit={nothing} canSubmit={false} busy={false} onStop={nothing} />}\n />\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { AppRefusal } from \"../app-refusal\";\nimport { serverRefusalOf } from \"../server-refusal\";\n\n/** Чат объекта: ещё ищется, найден (id), сервер отказал с текстом или запрос не удался (можно повторить). */\nexport type SubjectChatState =\n | { state: \"loading\" }\n | { state: \"ready\"; chatId: string }\n | { state: \"refused\"; text: string }\n | { state: \"failed\" };\n\n/**\n * `POST {api}/subject { key }` при открытии и смене ключа: сервер находит или создаёт чат объекта человека. Ответ\n * помнится вместе с запросом, на который он пришёл: при смене ключа прежний чат не показывается ни одного кадра.\n * Отказ самого приложения (`turn_refused`, `access_refused`) уходит в `onAppRefusal` — по разу на ответ сервера.\n */\nexport function useSubjectChatId(api: string, subject: string, onAppRefusal?: (refusal: AppRefusal) => void): SubjectChatState & { retry: () => void } {\n const [attempt, setAttempt] = useState(0);\n const onAppRefusalRef = useRef(onAppRefusal);\n onAppRefusalRef.current = onAppRefusal;\n const request = `${attempt}\\n${api}\\n${subject}`;\n const [found, setFound] = useState<{ request: string; state: SubjectChatState } | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const settle = (state: SubjectChatState) => {\n if (!controller.signal.aborted) setFound({ request, state });\n };\n void (async () => {\n try {\n const response = await fetch(`${api}/subject`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: subject }),\n signal: controller.signal,\n });\n if (!response.ok) {\n const refusal = await serverRefusalOf(response);\n if (controller.signal.aborted) return;\n settle(refusal?.final ? { state: \"refused\", text: refusal.text } : { state: \"failed\" });\n if (refusal?.kind && refusal.code) onAppRefusalRef.current?.({ kind: refusal.kind, code: refusal.code, message: refusal.text });\n return;\n }\n const { id } = (await response.json()) as { id?: unknown };\n settle(typeof id === \"string\" ? { state: \"ready\", chatId: id } : { state: \"failed\" });\n } catch {\n settle({ state: \"failed\" });\n }\n })();\n return () => controller.abort();\n }, [api, subject, request]);\n\n const retry = useCallback(() => setAttempt((value) => value + 1), []);\n return { ...(found?.request === request ? found.state : { state: \"loading\" }), retry };\n}\n","import type { MessageAttachmentsView } from \"../attachments-contract\";\nimport type { AgentChatMessage } from \"../chat-types\";\n\ntype DescribeAttachments = (message: AgentChatMessage) => MessageAttachmentsView | undefined;\n\n/**\n * `describe` плагина вложений, у картинок которого есть `onOpen` (договор скина, версия 13): открыть в окне просмотра\n * по id вложения. Нет `open` (у скина нет окна) — `describe` как есть, миниатюры как в 0.15.\n */\nexport function describeWithViewer(\n describe: DescribeAttachments | undefined,\n open: ((key: string, trigger: HTMLElement) => void) | undefined,\n): DescribeAttachments | undefined {\n if (!describe || !open) return describe;\n return (message) => {\n const view = describe(message);\n if (!view?.items.some((item) => item.kind === \"image\")) return view;\n return {\n ...view,\n items: view.items.map((item) => (item.kind === \"image\" ? { ...item, onOpen: (trigger: HTMLElement) => open(item.id, trigger) } : item)),\n };\n };\n}\n","import { getToolName, isToolUIPart } from \"ai\";\nimport type { DynamicToolUIPart, ToolUIPart } from \"ai\";\nimport type { ImagesClient } from \"../agent-chat-extensions\";\nimport type { AgentChatMessage } from \"../chat-types\";\nimport type { GeneratedImagesView } from \"../skin-contract\";\nimport type { MediaViewerItem } from \"./media-viewer-types\";\n\n/** Вид картинок агента для части ответа; `undefined` — это не вызов картинок, он остаётся строкой инструмента. */\nexport type DescribeGeneratedImages = (part: ToolUIPart | DynamicToolUIPart) => GeneratedImagesView | undefined;\n\nconst noOpen = () => undefined;\n\n/** `images` страницы — всегда списком: один клиент или несколько. */\nexport const imageClientsOf = (images: ImagesClient | readonly ImagesClient[] | undefined): readonly ImagesClient[] =>\n images === undefined ? [] : Array.isArray(images) ? images : [images as ImagesClient];\n\n/**\n * Картинки агента (`images` страницы): вызов инструмента одного из клиентов → вид для скина; клиент выбирается по имени\n * инструмента. Нет `open` (у скина нет окна просмотра) — клик по картинке ничего не делает.\n */\nexport function describeGeneratedImages(\n images: ImagesClient | readonly ImagesClient[],\n call: { api: string; chatId: string; open?: ((key: string, trigger: HTMLElement) => void) | undefined },\n): DescribeGeneratedImages {\n const openImage = call.open ?? noOpen;\n const byTool = new Map(imageClientsOf(images).map((client) => [client.toolName, client]));\n return (part) => byTool.get(getToolName(part))?.describe({ part, api: call.api, chatId: call.chatId, openImage });\n}\n\n/** Готовые картинки агента в сообщении — элементы карусели окна просмотра, по порядку частей. */\nexport function generatedViewerItems(describe: DescribeGeneratedImages): (message: AgentChatMessage) => MediaViewerItem[] {\n return (message) => {\n if (message.role !== \"assistant\") return [];\n return message.parts.flatMap((part) =>\n isToolUIPart(part)\n ? (describe(part)?.items ?? []).flatMap((item): MediaViewerItem[] =>\n item.state === \"ready\"\n ? [\n {\n kind: \"image\",\n key: item.key,\n src: item.src,\n alt: item.alt,\n ...(item.width !== undefined && { width: item.width }),\n ...(item.height !== undefined && { height: item.height }),\n },\n ]\n : [],\n )\n : [],\n );\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { MediaViewerProps } from \"../skin-contract\";\nimport type { MediaViewerItem } from \"./media-viewer-types\";\n\nexport interface UseMediaViewerOptions {\n /** Карусель чата на сейчас; зовётся, только пока окно открыто. Новая функция — карусель пересобирается. */\n getItems: () => readonly MediaViewerItem[];\n /** Смена значения (другой чат, другая ветка) закрывает окно. */\n resetKey: string;\n}\n\nexport interface MediaViewerController {\n /**\n * Открыть элемент `key`. `items` — свой набор (схема открывается одна); без него — карусель чата.\n * Функция стабильна: строки ленты, которые её держат, не перерисовываются.\n */\n open: (key: string, trigger: HTMLElement, items?: readonly MediaViewerItem[]) => void;\n /** Свойства для `skin.MediaViewer`; `null` — окно закрыто. */\n viewer: MediaViewerProps | null;\n}\n\n/** Отметка своей записи в истории браузера: по ней «назад» узнаёт, что закрыть надо окно, а не уйти со страницы. */\nconst ENTRY_FIELD = \"agentChatViewer\";\nlet entryCounter = 0;\n\ninterface OpenViewer {\n key: string;\n items?: readonly MediaViewerItem[];\n entry: number;\n}\n\nconst currentEntry = (): unknown => (history.state as Record<string, unknown> | null)?.[ENTRY_FIELD];\n\n/**\n * Окно просмотра со стороны поведения: какой элемент открыт, запись в истории браузера (жест «назад» закрывает окно)\n * и возврат фокуса на элемент, с которого открыли. Открытый элемент помнится по `key`: новые картинки в ленте\n * окно не сдвигают, а пропавший элемент окно закрывает.\n */\nexport function useMediaViewer({ getItems, resetKey }: UseMediaViewerOptions): MediaViewerController {\n const [opened, setOpened] = useState<OpenViewer | null>(null);\n const trigger = useRef<HTMLElement | null>(null);\n const openedRef = useRef(opened);\n openedRef.current = opened;\n const getItemsRef = useRef(getItems);\n getItemsRef.current = getItems;\n\n const items = useMemo(() => (opened ? (opened.items ?? getItems()) : []), [opened, getItems]);\n const index = opened ? items.findIndex((item) => item.key === opened.key) : -1;\n\n /** Закрыть; `fromHistory` — запись уже ушла сама («назад»), иначе убрать её. */\n const close = useCallback((fromHistory: boolean, returnFocus: boolean) => {\n const current = openedRef.current;\n if (!current) return;\n openedRef.current = null;\n setOpened(null);\n if (!fromHistory && currentEntry() === current.entry) history.back();\n const source = trigger.current;\n trigger.current = null;\n if (returnFocus && source?.isConnected) source.focus();\n }, []);\n\n const open = useCallback(\n (key: string, source: HTMLElement, own?: readonly MediaViewerItem[]) => {\n if (!(own ?? getItemsRef.current()).some((item) => item.key === key)) return;\n trigger.current = source;\n const current = openedRef.current;\n const entry = current?.entry ?? ++entryCounter;\n if (!current) history.pushState({ ...(history.state as Record<string, unknown> | null), [ENTRY_FIELD]: entry }, \"\");\n const next: OpenViewer = { key, entry, ...(own && { items: own }) };\n openedRef.current = next;\n setOpened(next);\n },\n [],\n );\n\n // «Назад» (кнопка браузера, жест на телефоне) ушёл с записи окна: окно закрывается, страница остаётся.\n const isOpen = opened !== null;\n useEffect(() => {\n if (!isOpen) return;\n const onPopState = () => {\n if (currentEntry() !== openedRef.current?.entry) close(true, true);\n };\n addEventListener(\"popstate\", onPopState);\n return () => removeEventListener(\"popstate\", onPopState);\n }, [isOpen, close]);\n\n // Открытый элемент пропал из ленты.\n const gone = opened !== null && index < 0;\n useEffect(() => {\n if (gone) close(false, true);\n }, [gone, close]);\n\n // Другой чат или ветка: прежняя карусель к ним не относится, а элемента-источника уже нет.\n const shownFor = useRef(resetKey);\n useEffect(() => {\n if (shownFor.current === resetKey) return;\n shownFor.current = resetKey;\n close(false, false);\n }, [resetKey, close]);\n\n // Чат ушёл со страницы с открытым окном: своя запись в истории не остаётся.\n useEffect(() => () => close(false, false), [close]);\n\n const onIndexChange = useCallback(\n (next: number) => {\n const item = items[next];\n const current = openedRef.current;\n if (!item || !current) return;\n const moved = { ...current, key: item.key };\n openedRef.current = moved;\n setOpened(moved);\n },\n [items],\n );\n const onClose = useCallback(() => close(false, true), [close]);\n\n const viewer = useMemo<MediaViewerProps | null>(\n () => (opened && index >= 0 ? { items: items as MediaViewerItem[], index, onIndexChange, onClose } : null),\n [opened, index, items, onIndexChange, onClose],\n );\n return { open, viewer };\n}\n","import { getToolName } from \"ai\";\nimport type { DynamicToolUIPart, ToolUIPart } from \"ai\";\nimport type { ReactNode } from \"react\";\nimport type { CustomWidgetProps, FormFieldView, WidgetView } from \"../skin-contract\";\nimport { checkChoices, checkForm } from \"./form-check\";\nimport type { FormCheckMessages, FormValues } from \"./form-check\";\nimport { BY_MESSAGE_KEY, CHOSEN_KEY, FIELDS_KEY, ONE_QUESTION_AT_A_TIME, RATING_KEY, isAnsweredByMessage } from \"./widget-answer-keys\";\n\n/** Базовые ждущие виджеты: их рисует скин без свойств страницы. */\nconst WAITING_BASIC_WIDGETS = new Set([\"ask_choice\", \"ask_multi_choice\", \"ask_rating\", \"ask_form\"]);\n/** Подсказки: не ждут, рисуются кнопками под ответом (suggested-replies.ts), в ленте их вызова не видно. */\nexport const SUGGEST_REPLIES = \"suggest_replies\";\n\ntype ToolPart = ToolUIPart | DynamicToolUIPart;\n\n/** Вызов виджета в ленте: что спросили, ждёт ли он и чем ответили. */\nexport interface WidgetCall {\n toolCallId: string;\n name: string;\n input: unknown;\n /** `closed` — «Стоп», сбой или ответ, который уже не ждёт (FR-007). */\n state: CustomWidgetProps[\"state\"];\n /** Ответ из результата вызова, у отвеченного. */\n answer?: unknown;\n}\n\n/**\n * Часть вызова → виджет. `null` — не виджет (обычная строка инструмента), `\"hidden\"` — виджет не показывается:\n * модель ещё пишет вход, вход негодный или это лишний вопрос шага («Задавай один вопрос за раз»).\n * `awaitingHere` — часть стоит в последнем ответе текущей ветки, и этот ответ ждёт человека: только тогда виджет активен.\n * Вызов без результата и без подтверждения в ждущем ответе — тоже виджет: свой, которого страница не знает (FR-018).\n */\nexport function widgetCallOf(part: ToolPart, where: { awaitingHere: boolean; customNames: ReadonlySet<string> }): WidgetCall | \"hidden\" | null {\n const name = getToolName(part);\n if (name === SUGGEST_REPLIES) return null;\n const output = (part as { output?: unknown }).output;\n const known = WAITING_BASIC_WIDGETS.has(name) || where.customNames.has(name);\n const hasApproval = (part as { approval?: unknown }).approval !== undefined;\n const unknownWaiting = !known && !hasApproval && part.state === \"input-available\" && where.awaitingHere;\n if (!known && !unknownWaiting && !(part.state === \"output-available\" && isAnsweredByMessage(output))) return null;\n\n const call = { toolCallId: part.toolCallId, name, input: part.input };\n switch (part.state) {\n case \"input-streaming\":\n // Ответ ушёл, ход продолжается: вызов помечен «в пути» с ответом (use-widget-answer.ts). Без ответа — вход пишется.\n return output !== undefined ? { ...call, state: \"answered\", answer: output } : \"hidden\";\n case \"input-available\":\n return { ...call, state: where.awaitingHere ? \"pending\" : \"closed\" };\n case \"output-available\":\n return isAnsweredByMessage(output) ? { ...call, state: \"answered_by_message\" } : { ...call, state: \"answered\", answer: output };\n case \"output-error\":\n if (part.errorText?.startsWith(ONE_QUESTION_AT_A_TIME)) return \"hidden\";\n if (WAITING_BASIC_WIDGETS.has(name) && basicInputOf(name, part.input) === null) return \"hidden\";\n return { ...call, state: \"closed\" };\n default:\n return null;\n }\n}\n\n/** Что даёт слой поведения виду виджета. */\nexport interface WidgetControls {\n /** Ответ уходит на сервер. */\n pending: boolean;\n failure?: string;\n /** Есть только у активного виджета. */\n onAnswer?: (answer: unknown) => void;\n /** Ошибки полей формы после неудачной проверки. */\n fieldErrors?: Readonly<Record<string, string>>;\n /** Проверка формы не прошла (или прошла — тогда ошибок нет): ошибки показать у полей. */\n onInvalid?: (errors: Record<string, string>) => void;\n messages: FormCheckMessages;\n /** Компонент своего виджета со страницы. */\n content?: ReactNode;\n}\n\n/**\n * Виджет → вид для скина (договор v13). Ответ уходит в форме результата вызова (data-model, «Ответы»):\n * `{ \"выбрано\": … }`, `{ \"оценка\": … }`, `{ \"поля\": … }`. Негодный вход базового виджета — `null`.\n */\nexport function widgetViewOf(call: WidgetCall, controls: WidgetControls): WidgetView | null {\n const active = call.state === \"pending\";\n const onAnswer = active ? controls.onAnswer : undefined;\n const common = {\n toolCallId: call.toolCallId,\n active,\n pending: controls.pending,\n ...(controls.failure !== undefined && { failure: controls.failure }),\n answeredByMessage: call.state === \"answered_by_message\",\n };\n const answer = call.state === \"answered\" ? asRecord(call.answer) : undefined;\n\n if (!WAITING_BASIC_WIDGETS.has(call.name)) {\n const question = asRecord(call.input)?.[\"question\"];\n return {\n ...common,\n kind: \"custom\",\n name: call.name,\n ...(typeof question === \"string\" && question.length > 0 && { question }),\n ...(controls.content !== undefined && { content: controls.content }),\n };\n }\n const input = basicInputOf(call.name, call.input);\n if (!input) return null;\n\n switch (input.kind) {\n case \"choice\": {\n const chosen = answer?.[CHOSEN_KEY];\n return {\n ...common,\n ...input,\n ...(typeof chosen === \"string\" && { chosen }),\n ...(onAnswer && { onChoose: (option: string) => input.options.includes(option) && onAnswer({ [CHOSEN_KEY]: option }) }),\n };\n }\n case \"multi_choice\": {\n const chosen = answer?.[CHOSEN_KEY];\n return {\n ...common,\n ...input,\n ...(Array.isArray(chosen) && { chosen: chosen.filter((option): option is string => typeof option === \"string\") }),\n ...(onAnswer && {\n onSubmit: (options: string[]) => {\n const checked = checkChoices(input.options, options, input.min, input.max);\n if (checked) onAnswer({ [CHOSEN_KEY]: checked });\n },\n }),\n };\n }\n case \"rating\": {\n const value = answer?.[RATING_KEY];\n return {\n ...common,\n ...input,\n ...(typeof value === \"number\" && { value }),\n ...(onAnswer && {\n onRate: (rating: number) => Number.isInteger(rating) && rating >= input.from && rating <= input.to && onAnswer({ [RATING_KEY]: rating }),\n }),\n };\n }\n case \"form\": {\n const values = asRecord(answer?.[FIELDS_KEY]);\n const fields = input.fields.map((field) => {\n const error = controls.fieldErrors?.[field.name];\n return error === undefined ? field : { ...field, error };\n });\n return {\n ...common,\n kind: \"form\",\n title: input.title,\n fields,\n ...(values && { values: formValuesOf(values) }),\n ...(onAnswer && {\n onSubmit: (submitted: Record<string, string | number>) => {\n const checked = checkForm(input.fields, submitted, controls.messages);\n controls.onInvalid?.(checked.ok ? {} : checked.errors);\n if (checked.ok) onAnswer({ [FIELDS_KEY]: checked.answer });\n },\n }),\n };\n }\n }\n}\n\ntype BasicInput =\n | { kind: \"choice\"; question: string; options: string[] }\n | { kind: \"multi_choice\"; question: string; options: string[]; min: number; max: number }\n | { kind: \"rating\"; question: string; scale: \"stars\" | \"range\"; from: number; to: number; fromLabel?: string; toLabel?: string }\n | { kind: \"form\"; title: string; fields: FormFieldView[] };\n\n/**\n * Вход базового виджета, прочитанный с опаской: сервер его уже проверил схемой, но история могла прийти от старой\n * версии или от чужой модели. Тексты модели остаются строками — скин показывает их только текстом (FR-009).\n */\nfunction basicInputOf(name: string, raw: unknown): BasicInput | null {\n const input = asRecord(raw);\n if (!input) return null;\n if (name === \"ask_form\") {\n const title = input[\"title\"];\n const fields = Array.isArray(input[\"fields\"]) ? input[\"fields\"].map(formFieldOf) : [];\n if (typeof title !== \"string\" || fields.length === 0 || fields.some((field) => field === null)) return null;\n return { kind: \"form\", title, fields: fields as FormFieldView[] };\n }\n const question = input[\"question\"];\n if (typeof question !== \"string\" || question.length === 0) return null;\n if (name === \"ask_rating\") {\n if (input[\"scale\"] === \"stars\") return { kind: \"rating\", question, scale: \"stars\", from: 1, to: 5 };\n const from = input[\"from\"];\n const to = input[\"to\"];\n if (input[\"scale\"] !== \"range\" || typeof from !== \"number\" || typeof to !== \"number\" || !(from < to)) return null;\n return { kind: \"rating\", question, scale: \"range\", from, to, ...optionalText(input, \"fromLabel\"), ...optionalText(input, \"toLabel\") };\n }\n const options = stringsOf(input[\"options\"]);\n if (!options || options.length < 2) return null;\n if (name === \"ask_choice\") return { kind: \"choice\", question, options };\n const min = typeof input[\"min\"] === \"number\" ? input[\"min\"] : 1;\n const max = typeof input[\"max\"] === \"number\" ? input[\"max\"] : options.length;\n if (!(min >= 1 && min <= max && max <= options.length)) return null;\n return { kind: \"multi_choice\", question, options, min, max };\n}\n\nfunction formFieldOf(raw: unknown): FormFieldView | null {\n const field = asRecord(raw);\n const kind = field?.[\"kind\"];\n if (!field || typeof field[\"name\"] !== \"string\" || typeof field[\"label\"] !== \"string\") return null;\n if (kind !== \"text\" && kind !== \"number\" && kind !== \"date\" && kind !== \"select\") return null;\n const options = stringsOf(field[\"options\"]);\n if (kind === \"select\" && (!options || options.length < 2)) return null;\n const edge = (key: \"min\" | \"max\") => {\n const value = field[key];\n return (kind === \"number\" && typeof value === \"number\") || (kind === \"date\" && typeof value === \"string\") ? { [key]: value } : {};\n };\n return {\n name: field[\"name\"],\n label: field[\"label\"],\n kind,\n required: field[\"required\"] === true,\n ...edge(\"min\"),\n ...edge(\"max\"),\n ...(kind === \"select\" && { options: options! }),\n ...(kind === \"text\" && field[\"multiline\"] === true && { multiline: true }),\n };\n}\n\nfunction formValuesOf(values: Record<string, unknown>): FormValues {\n return Object.fromEntries(Object.entries(values).filter((entry): entry is [string, string | number] => typeof entry[1] === \"string\" || typeof entry[1] === \"number\"));\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\nfunction stringsOf(value: unknown): string[] | null {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\") ? value : null;\n}\n\nfunction optionalText(input: Record<string, unknown>, key: \"fromLabel\" | \"toLabel\"): { fromLabel?: string; toLabel?: string } {\n const value = input[key];\n return typeof value === \"string\" && value.length > 0 ? { [key]: value } : {};\n}\n\n/** Ключ пометки «закрыт сообщением» — для оптимистичной отметки в ленте, когда человек написал вместо ответа. */\nexport { BY_MESSAGE_KEY };\n","import type { WidgetLabels } from \"../labels\";\nimport type { FormFieldView } from \"../skin-contract\";\n\n/** Самый длинный текст поля формы (data-model, «Ответы»). */\nexport const FORM_TEXT_MAX_LENGTH = 2000;\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Тексты ошибок полей: из словаря надписей, чтобы проверку можно было звать вне React. */\nexport interface FormCheckMessages {\n required: string;\n notNumber: string;\n notDate: string;\n outOfRange: (min: string | undefined, max: string | undefined) => string;\n tooLong: (max: number) => string;\n}\n\nexport function formCheckMessages(labels: WidgetLabels): FormCheckMessages {\n return {\n required: labels.formRequired,\n notNumber: labels.formNotNumber,\n notDate: labels.formNotDate,\n outOfRange: labels.formOutOfRange,\n tooLong: labels.formTooLong,\n };\n}\n\nexport type FormValues = Record<string, string | number>;\n\nexport type FormCheck = { ok: true; answer: FormValues } | { ok: false; errors: Record<string, string> };\n\n/**\n * Проверка формы до отправки (FR-014): обязательные поля, числа и даты в пределах, вариант из списка, текст до 2000\n * знаков. Верное — ответ для сервера: числа числами, текст без пробелов по краям, пустые необязательные поля опущены,\n * поля не из формы не уходят.\n */\nexport function checkForm(fields: readonly FormFieldView[], values: Readonly<Record<string, string | number | undefined>>, messages: FormCheckMessages): FormCheck {\n const answer: FormValues = {};\n const errors: Record<string, string> = {};\n for (const field of fields) {\n const raw = values[field.name];\n const text = raw === undefined ? \"\" : String(raw).trim();\n if (text.length === 0) {\n if (field.required) errors[field.name] = messages.required;\n continue;\n }\n const checked = checkValue(field, raw, text, messages);\n if (typeof checked === \"object\") errors[field.name] = checked.error;\n else answer[field.name] = checked;\n }\n return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, answer };\n}\n\nfunction checkValue(field: FormFieldView, raw: string | number | undefined, text: string, messages: FormCheckMessages): string | number | { error: string } {\n const outOfRange = { error: messages.outOfRange(edge(field.min), edge(field.max)) };\n switch (field.kind) {\n case \"number\": {\n const value = typeof raw === \"number\" ? raw : Number(text.replace(\",\", \".\"));\n if (!Number.isFinite(value)) return { error: messages.notNumber };\n if ((typeof field.min === \"number\" && value < field.min) || (typeof field.max === \"number\" && value > field.max)) return outOfRange;\n return value;\n }\n case \"date\": {\n if (!isRealDate(text)) return { error: messages.notDate };\n // Даты `YYYY-MM-DD` сравниваются как строки.\n if ((typeof field.min === \"string\" && text < field.min) || (typeof field.max === \"string\" && text > field.max)) return outOfRange;\n return text;\n }\n case \"select\":\n return field.options?.includes(text) ? text : { error: messages.required };\n default:\n return text.length > FORM_TEXT_MAX_LENGTH ? { error: messages.tooLong(FORM_TEXT_MAX_LENGTH) } : text;\n }\n}\n\nconst edge = (value: number | string | undefined) => (value === undefined ? undefined : String(value));\n\nfunction isRealDate(text: string): boolean {\n if (!DATE_PATTERN.test(text)) return false;\n const date = new Date(`${text}T00:00:00Z`);\n return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === text;\n}\n\n/** Выбор нескольких вариантов: в пределах `min..max` и только из списка — выбор в порядке вариантов; иначе `null`. */\nexport function checkChoices(options: readonly string[], chosen: readonly string[], min: number, max: number): string[] | null {\n const picked = new Set(chosen);\n if ([...picked].some((option) => !options.includes(option))) return null;\n const ordered = options.filter((option) => picked.has(option));\n return ordered.length >= min && ordered.length <= max ? ordered : null;\n}\n","import type { AgentChatMessage } from \"../chat-types\";\nimport { SUGGEST_REPLIES } from \"./widget-view\";\n\n/** Больше подсказок под ответом не показывается (FR-015). */\nconst MAX_SUGGESTED_REPLIES = 4;\n\n/**\n * Подсказки ответа: последний вызов `suggest_replies` в последнем ответе ветки. Пока идёт ход или последнее слово\n * за человеком — подсказок нет. Пустые строки и не строки отбрасываются.\n */\nexport function suggestedRepliesOf(messages: readonly AgentChatMessage[], busy: boolean): string[] | null {\n if (busy) return null;\n const last = messages.at(-1);\n if (last?.role !== \"assistant\") return null;\n const call = last.parts.findLast(\n (part) => part.type === `tool-${SUGGEST_REPLIES}` && (part as { state?: string }).state !== \"input-streaming\",\n );\n const replies = (call as { input?: { replies?: unknown } } | undefined)?.input?.replies;\n if (!Array.isArray(replies)) return null;\n const usable = replies.filter((reply): reply is string => typeof reply === \"string\" && reply.trim().length > 0).slice(0, MAX_SUGGESTED_REPLIES);\n return usable.length > 0 ? usable : null;\n}\n","import { memo, useCallback, useMemo, useState } from \"react\";\nimport type { ComponentType } from \"react\";\nimport { useChatLabels } from \"../labels\";\nimport { CustomWidgetGuard } from \"./custom-widget-guard\";\nimport type { CustomWidgetProps, WidgetView } from \"../skin-contract\";\nimport { formCheckMessages } from \"./form-check\";\nimport { widgetViewOf } from \"./widget-view\";\nimport type { WidgetCall } from \"./widget-view\";\n\n/**\n * Виджет в ленте: вид для скина и свой компонент страницы внутри него. Ошибки полей формы живут здесь, пока человек\n * не отправит форму верно. Упавший свой компонент заменяется запасным видом скина.\n */\nexport const WidgetPart = memo(function WidgetPart({\n Widget,\n Custom,\n call,\n chatId,\n pending,\n failure,\n onAnswer,\n}: {\n Widget: ComponentType<WidgetView>;\n /** Компонент своего виджета из `widgets[name]`; нет — запасной вид скина. */\n Custom?: ComponentType<CustomWidgetProps<unknown, unknown>>;\n call: WidgetCall;\n chatId: string;\n pending: boolean;\n failure?: string;\n onAnswer: (toolCallId: string, answer: unknown) => void;\n}) {\n const labels = useChatLabels();\n const messages = useMemo(() => formCheckMessages(labels.widgets), [labels.widgets]);\n const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});\n const toolCallId = call.toolCallId;\n const answer = useCallback((value: unknown) => onAnswer(toolCallId, value), [onAnswer, toolCallId]);\n const [crashed, setCrashed] = useState(false);\n const onCrash = useCallback(() => setCrashed(true), []);\n const active = call.state === \"pending\";\n const content = Custom && !crashed && (\n <CustomWidgetGuard onCrash={onCrash}>\n <Custom\n input={call.input}\n chatId={chatId}\n toolCallId={toolCallId}\n state={call.state}\n pending={pending}\n {...(call.answer !== undefined && { answer: call.answer })}\n {...(active && { onAnswer: answer })}\n {...(failure !== undefined && { failure })}\n />\n </CustomWidgetGuard>\n );\n const view = widgetViewOf(call, {\n pending,\n ...(failure !== undefined && { failure }),\n ...(active && { onAnswer: answer }),\n fieldErrors,\n onInvalid: setFieldErrors,\n messages,\n ...(content && { content }),\n });\n return view ? <Widget {...view} /> : null;\n});\n","import { Component } from \"react\";\nimport type { ReactNode } from \"react\";\n\n/**\n * Граница вокруг своего виджета страницы: упавший компонент (например, на негодном входе модели) не роняет ленту.\n * После падения виджет показывается запасным видом скина.\n */\nexport class CustomWidgetGuard extends Component<{ onCrash: () => void; children: ReactNode }, { crashed: boolean }> {\n override state = { crashed: false };\n\n static getDerivedStateFromError(): { crashed: boolean } {\n return { crashed: true };\n }\n\n override componentDidCatch(): void {\n this.props.onCrash();\n }\n\n override render(): ReactNode {\n return this.state.crashed ? null : this.props.children;\n }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createDictationField } from \"./dictation-field\";\nimport type { DictationField } from \"./dictation-field\";\nimport { useChatLabels, voiceErrorText } from \"./labels\";\nimport type { VoiceInputView } from \"./skin-contract\";\nimport type { VoiceErrorDetails, VoiceErrorKind, VoiceInput, VoiceSession } from \"./voice-input\";\n\nexport interface UseVoiceDictationOptions {\n /** Нет голоса — нет и кнопки. */\n voice: VoiceInput | undefined;\n value: string;\n setValue: (value: string) => void;\n chatId: string;\n}\n\ntype DictationState = VoiceInputView[\"state\"];\n\nexport interface VoiceDictation {\n /** Что отдать полю ввода скина; `undefined` — голоса нет. */\n view: VoiceInputView | undefined;\n /** Бросить идущую диктовку (при отправке): поздние слова в поле не попадут. Без диктовки ничего не делает. */\n cancel: () => void;\n}\n\n/** Диктовка в поле ввода: состояние кнопки микрофона и запись слов в поле. */\nexport function useVoiceDictation({ voice, value, setValue, chatId }: UseVoiceDictationOptions): VoiceDictation {\n const labels = useChatLabels();\n const [state, setState] = useState<DictationState>(\"idle\");\n const [error, setError] = useState<{ kind: VoiceErrorKind; details?: VoiceErrorDetails }>();\n const [polishing, setPolishing] = useState(false);\n const session = useRef<VoiceSession | null>(null);\n const field = useRef<DictationField | null>(null);\n // Номер диктовки: события брошенной диктовки не должны ничего менять.\n const run = useRef(0);\n const latestValue = useRef(value);\n const knownValue = useRef(value);\n // Что диктовка записала в поле, но React ещё не показал: рендеры приходят по порядку, и отстающий за правку руками не считается.\n const pendingWrites = useRef<string[]>([]);\n latestValue.current = value;\n\n const drop = useCallback(() => {\n run.current += 1;\n session.current?.cancel();\n session.current = null;\n field.current = null;\n setState(\"idle\");\n setPolishing(false);\n }, []);\n\n // Поле изменилось не нашей записью: правка руками или отправка. Сообщение об ошибке больше не нужно.\n useEffect(() => {\n const ownWrite = pendingWrites.current.lastIndexOf(value);\n if (ownWrite >= 0) {\n pendingWrites.current.splice(0, ownWrite + 1);\n return;\n }\n pendingWrites.current = [];\n if (value === knownValue.current) return;\n knownValue.current = value;\n field.current?.observe(value);\n setError(undefined);\n }, [value]);\n\n // Уход с экрана и смена чата обрывают диктовку.\n useEffect(() => drop, [chatId, drop]);\n\n const start = useCallback(() => {\n if (!voice) return;\n const mine = ++run.current;\n const dictation = createDictationField(latestValue.current);\n field.current = dictation;\n knownValue.current = latestValue.current;\n pendingWrites.current = [];\n setError(undefined);\n setState(\"connecting\");\n const alive = () => run.current === mine;\n session.current = voice.start(\n {\n onConnected: () => alive() && setState((current) => (current === \"connecting\" ? \"listening\" : current)),\n onText: (text) => {\n if (!alive()) return;\n const written = dictation.write(text);\n if (written === null) return;\n knownValue.current = written;\n pendingWrites.current.push(written);\n setValue(written);\n },\n onError: (kind, details) => alive() && setError({ kind, ...(details && { details }) }),\n onPolishing: () => {\n if (!alive()) return;\n dictation.freeze();\n setPolishing(true);\n },\n onPolished: (text) => {\n if (!alive()) return;\n // Заменяется только надиктованный кусок; если поле правили внутри него, чистка отбрасывается.\n const written = dictation.applyPolished(text);\n if (written === null) return;\n knownValue.current = written;\n pendingWrites.current.push(written);\n setValue(written);\n },\n onEnd: () => {\n if (!alive()) return;\n session.current = null;\n field.current = null;\n setState(\"idle\");\n setPolishing(false);\n },\n },\n { chatId, ...(labels.locale && { language: labels.locale }) },\n );\n }, [voice, setValue, chatId, labels.locale]);\n\n const onToggle = useCallback(() => {\n if (state === \"idle\") start();\n else if (state === \"connecting\") drop();\n else if (state === \"listening\") {\n setState(\"stopping\");\n session.current?.stop();\n }\n }, [state, start, drop]);\n\n const cancel = useCallback(() => {\n if (session.current) drop();\n }, [drop]);\n\n const view = voice\n ? { state, onToggle, ...(polishing && { polishing }), ...(error && { error: voiceErrorText(labels.voiceErrors, error.kind, error.details) }) }\n : undefined;\n return { view, cancel };\n}\n","import type { DictatedText } from \"./voice-input\";\n\n/**\n * Как диктовка пишет в поле ввода. Продиктованное дописывается к тому, что было набрано;\n * ручная правка остаётся, а сказанное до неё заново не появляется.\n */\nexport interface DictationField {\n /** Значение поля для накопленной диктовки; `null` — поле не трогать (диктовка уже заморожена). */\n write(text: DictatedText): string | null;\n /** Сообщить, что в поле сейчас `value`: если это не наша запись, значит поле правили руками. */\n observe(value: string): void;\n /** Сказано всё и закреплено, дальше только чистка: правки поля запоминаем, но запись больше не двигаем и `write` ничего не пишет. */\n freeze(): void;\n /**\n * Заменить надиктованный кусок исправленным текстом. Возвращает новое значение поля или `null`, если заменять нельзя:\n * поле правили до надиктованного или внутри него (правка не должна пропасть). Дописанное после надиктованного сохраняется.\n */\n applyPolished(polished: string): string | null;\n}\n\nexport function createDictationField(startValue: string): DictationField {\n let base = startValue;\n // Сколько знаков закреплённой речи уже вошло в `base`. Незакреплённая часть ещё может измениться,\n // поэтому правка посреди неё не считается «уже записанной»: слово придёт заново целиком.\n let consumed = 0;\n let spoken = \"\";\n let settled = 0;\n let lastWritten = startValue;\n // Что стоит в поле по последним сведениям, и всё ли надиктованное пока лежит в поле нетронутым.\n let current = startValue;\n let undisturbed = true;\n let frozen = false;\n // Всё, что стоит в поле до надиктованного куска (набранное, пробел-разделитель), и был ли кусок.\n let head = startValue;\n let hasChunk = false;\n\n return {\n write({ finalText, interimText }) {\n // Поздний текст после заморозки не должен перезаписать поле: дописанное руками пропало бы.\n if (frozen) return null;\n spoken = finalText + interimText;\n settled = finalText.length;\n const chunk = spoken.slice(consumed).trimStart();\n const separator = chunk === \"\" || base === \"\" || /\\s$/.test(base) ? \"\" : \" \";\n hasChunk = chunk !== \"\";\n head = base + separator;\n lastWritten = chunk === \"\" ? base : base + separator + chunk;\n current = lastWritten;\n return lastWritten;\n },\n observe(value) {\n current = value;\n if (value === lastWritten || frozen) return;\n undisturbed = false;\n base = value;\n consumed = settled;\n lastWritten = value;\n },\n freeze() {\n frozen = true;\n },\n applyPolished(polished) {\n if (!hasChunk || !undisturbed || !current.startsWith(lastWritten)) return null;\n const tail = current.slice(lastWritten.length);\n lastWritten = head + polished;\n current = lastWritten + tail;\n return current;\n },\n };\n}\n","import { getToolName, isToolUIPart } from \"ai\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { VideosClient } from \"./agent-chat-extensions\";\nimport type { AgentChatMessage } from \"./chat-types\";\nimport { useChatLabels } from \"./labels\";\nimport type { ResolvedChatLabels } from \"./labels\";\nimport type { AgentChatState } from \"./use-agent-chat\";\nimport { widgetCallOf } from \"./widgets/widget-view\";\nimport type { WidgetCall } from \"./widgets/widget-view\";\nimport type { AnswerEnd, AssistantActivity, AssistantTextDelta, DialogChatBridge, VoiceDialogEngine, VoiceDialogView } from \"./voice-dialog\";\n\ntype DialogChat = Pick<AgentChatState, \"messages\" | \"busy\" | \"stop\" | \"sendVoiceMessage\" | \"replaceLastVoiceMessage\" | \"showVoiceCut\">;\n\nexport interface UseVoiceDialogOptions {\n /** Голосовой режим приложения; нет — кнопки нет и `useVoiceDialog` возвращает `undefined`. */\n engine: VoiceDialogEngine | undefined;\n chatId: string;\n chat: DialogChat;\n /** Видео агента: на месте показанного видео звучит `labels.voice.videoOnScreen`. */\n videos?: VideosClient;\n}\n\n/** Режим кончился сам или человеком: звук и слова больше не идут. */\nconst finished = (view: VoiceDialogView) => view.state === \"off\" || view.state === \"error\";\n\n/** Часть ответа, в которой модель сейчас работает: размышление или инструмент без результата. */\ntype ActiveWork = Pick<AssistantActivity, \"kind\" | \"toolName\">;\n/** `quiet` — шло ещё до включения режима: о начале движку не говорили, о конце тоже не скажем. */\ntype AnnouncedWork = ActiveWork & { quiet: boolean };\n\n/**\n * Связывает голосовой режим с чатом: даёт движку мост (отправка слов, остановка, текст ответа, конец ответа, отметка\n * обрыва) и возвращает вид для кнопки в поле ввода. Мост не знает, как режим слушает и говорит; движок не знает, как устроен чат.\n */\nexport function useVoiceDialog({ engine, chatId, chat, videos }: UseVoiceDialogOptions): VoiceDialogView | undefined {\n const labels = useChatLabels();\n const labelsRef = useRef(labels);\n labelsRef.current = labels;\n const [engineView, setEngineView] = useState<VoiceDialogView | null>(null);\n const chatRef = useRef(chat);\n chatRef.current = chat;\n const active = useRef(false);\n /** Номер включения: вид от закрытого режима не должен попасть на экран. */\n const generation = useRef(0);\n const textListeners = useRef(new Set<(delta: AssistantTextDelta) => void>());\n const endListeners = useRef(new Set<(end: AnswerEnd) => void>());\n /** Сколько знаков каждой текстовой части уже отдано движку: сообщение → длины по номерам частей. */\n const delivered = useRef(new Map<string, number[]>());\n /** Ждущие виджеты, о которых движку уже сказано (или что ждали до включения режима). */\n const spokenWidgets = useRef(new Set<string>());\n /** Показанные видео, о которых движку уже сказано (или что были на экране до включения режима). */\n const spokenVideos = useRef(new Set<string>());\n const videosRef = useRef(videos);\n videosRef.current = videos;\n const activityListeners = useRef(new Set<(activity: AssistantActivity) => void>());\n /** Части, о начале которых движку уже сказано: сообщение → номер части → что в ней идёт. */\n const announced = useRef(new Map<string, Map<number, AnnouncedWork>>());\n\n const emitActivity = useCallback((activity: AssistantActivity) => {\n for (const listener of [...activityListeners.current]) listener(activity);\n }, []);\n\n const emitEnd = useCallback((end: AnswerEnd) => {\n for (const listener of [...endListeners.current]) listener(end);\n }, []);\n\n const bridge = useMemo<DialogChatBridge>(\n () => ({\n send: (text, options) => {\n if (!chatRef.current.sendVoiceMessage(text, options)) queueMicrotask(() => emitEnd({ messageId: \"\", status: \"error\" }));\n },\n stop: () => chatRef.current.stop(),\n replaceLastMessage: (text, options) => chatRef.current.replaceLastVoiceMessage(text, options),\n subscribeAssistantText: (listener) => {\n textListeners.current.add(listener);\n return () => void textListeners.current.delete(listener);\n },\n subscribeAnswerEnd: (listener) => {\n endListeners.current.add(listener);\n return () => void endListeners.current.delete(listener);\n },\n subscribeAssistantActivity: (listener) => {\n activityListeners.current.add(listener);\n return () => void activityListeners.current.delete(listener);\n },\n showCut: (messageId, cut) => chatRef.current.showVoiceCut(messageId, cut),\n }),\n [emitEnd],\n );\n\n // Дописанный текст последнего ответа уходит движку приращениями; всё, что было в ленте до включения, не звучит.\n useEffect(() => {\n if (!active.current) return;\n const last = chat.messages.at(-1);\n if (last?.role !== \"assistant\") return;\n const lengths = delivered.current.get(last.id) ?? [];\n last.parts.forEach((part, partIndex) => {\n if (part.type !== \"text\") return;\n const before = lengths[partIndex] ?? 0;\n lengths[partIndex] = part.text.length;\n if (part.text.length > before) {\n for (const listener of [...textListeners.current]) listener({ messageId: last.id, partIndex, text: part.text.slice(before) });\n }\n });\n delivered.current.set(last.id, lengths);\n }, [chat.messages]);\n\n // Ждущий виджет: к озвучке добавляется его вопрос; подсказки не звучат (FR-019), один виджет — один раз.\n useEffect(() => {\n if (!active.current) return;\n const last = chat.messages.at(-1);\n if (last?.role !== \"assistant\") return;\n for (const [partIndex, call] of waitingWidgets(last)) {\n if (spokenWidgets.current.has(call.toolCallId)) continue;\n spokenWidgets.current.add(call.toolCallId);\n const text = `${widgetPhrase(call, labelsRef.current)} `;\n for (const listener of [...textListeners.current]) listener({ messageId: last.id, partIndex, text });\n }\n }, [chat.messages]);\n\n // Показанное видео: к озвучке добавляется короткое «видео на экране» — что за видео, говорит сама модель.\n useEffect(() => {\n if (!active.current) return;\n const last = chat.messages.at(-1);\n if (last?.role !== \"assistant\") return;\n for (const [partIndex, toolCallId] of shownVideos(last, videosRef.current)) {\n if (spokenVideos.current.has(toolCallId)) continue;\n spokenVideos.current.add(toolCallId);\n const text = `${labelsRef.current.voice.videoOnScreen}. `;\n for (const listener of [...textListeners.current]) listener({ messageId: last.id, partIndex, text });\n }\n }, [chat.messages]);\n\n // Размышление и вызовы инструментов последнего ответа: начало и конец каждой части уходят движку один раз.\n useEffect(() => {\n if (!active.current) return;\n const last = chat.messages.at(-1);\n const now = last?.role === \"assistant\" ? activeWork(last) : new Map<number, ActiveWork>();\n const known = announced.current;\n for (const [messageId, parts] of [...known]) {\n for (const [partIndex, work] of [...parts]) {\n if (messageId === last?.id && now.has(partIndex)) continue;\n parts.delete(partIndex);\n if (!work.quiet) emitActivity({ messageId, partIndex, ...endOf(work) });\n }\n if (parts.size === 0) known.delete(messageId);\n }\n if (!last) return;\n const parts = known.get(last.id) ?? new Map<number, AnnouncedWork>();\n for (const [partIndex, work] of now) {\n if (parts.has(partIndex)) continue;\n parts.set(partIndex, { ...work, quiet: false });\n emitActivity({ messageId: last.id, partIndex, kind: work.kind, ...(work.toolName && { toolName: work.toolName }), phase: \"start\" });\n }\n if (parts.size > 0) known.set(last.id, parts);\n }, [chat.messages, emitActivity]);\n\n // Конец ответа — момент, когда чат перестал быть занятым; идёт после последнего приращения текста.\n const wasBusy = useRef(chat.busy);\n useEffect(() => {\n const ended = wasBusy.current && !chat.busy;\n wasBusy.current = chat.busy;\n if (!ended || !active.current) return;\n // Часть, застрявшая «идёт», когда ответ кончился, закрывается: режим не должен ждать её вечно.\n for (const [messageId, parts] of announced.current) {\n for (const [partIndex, work] of parts) if (!work.quiet) emitActivity({ messageId, partIndex, ...endOf(work) });\n }\n announced.current.clear();\n const last = chat.messages.at(-1);\n if (last?.role !== \"assistant\") emitEnd({ messageId: \"\", status: \"error\" });\n else emitEnd({ messageId: last.id, status: endStatus(last) });\n }, [chat.busy, chat.messages, emitActivity, emitEnd]);\n\n // Смена чата или уход со страницы закрывают режим: микрофон и звук не должны остаться без хозяина.\n useEffect(\n () => () => {\n generation.current += 1;\n if (!active.current) return;\n active.current = false;\n announced.current.clear();\n engine?.close();\n setEngineView(null);\n },\n [engine, chatId],\n );\n\n const toggle = useCallback(() => {\n if (!engine) return;\n if (engineView?.state === \"error\") {\n // Нажатие на панели с ошибкой («Выйти»): закрыть режим, чтобы вернулось поле ввода; причина остаётся в виде.\n generation.current += 1;\n engine.close();\n setEngineView({ state: \"off\", speechAvailable: true, error: engineView.error, onToggle: () => {} });\n return;\n }\n if (!active.current) {\n const id = ++generation.current;\n active.current = true;\n delivered.current = snapshotOf(chatRef.current.messages);\n announced.current = quietWork(chatRef.current.messages);\n spokenWidgets.current = new Set([...waitingWidgets(chatRef.current.messages.at(-1))].map(([, call]) => call.toolCallId));\n spokenVideos.current = new Set(shownVideos(chatRef.current.messages.at(-1), videosRef.current).values());\n setEngineView({ state: \"connecting\", speechAvailable: true, onToggle: () => {} });\n engine.open({\n chatId,\n chat: bridge,\n onView: (view) => {\n if (id !== generation.current) return;\n active.current = !finished(view);\n setEngineView(view);\n },\n });\n return;\n }\n generation.current += 1;\n active.current = false;\n engine.close();\n setEngineView(null);\n }, [bridge, chatId, engine, engineView]);\n\n return useMemo(() => {\n if (!engine) return undefined;\n const view: VoiceDialogView = engineView ?? { state: \"off\", speechAvailable: true, onToggle: toggle };\n return { ...view, onToggle: toggle };\n }, [engine, engineView, toggle]);\n}\n\n/** Части ответа, где модель работает сейчас: размышление идёт или инструмент ещё без результата. */\nfunction activeWork(message: AgentChatMessage): Map<number, ActiveWork> {\n const work = new Map<number, ActiveWork>();\n message.parts.forEach((part, partIndex) => {\n if (part.type === \"reasoning\") {\n if (part.state === \"streaming\") work.set(partIndex, { kind: \"reasoning\" });\n } else if (isToolUIPart(part)) {\n // Ждущее подтверждения или отклонённое — не работа модели; «подтвердили» — инструмент вот-вот пойдёт.\n const running = part.state === \"input-streaming\" || part.state === \"input-available\" || part.state === \"approval-responded\" || (part.state === \"output-available\" && part.preliminary === true);\n if (running) work.set(partIndex, { kind: \"tool\", toolName: getToolName(part) });\n }\n });\n return work;\n}\n\nconst endOf = (work: ActiveWork): Pick<AssistantActivity, \"kind\" | \"phase\"> & Pick<Partial<AssistantActivity>, \"toolName\"> => ({\n kind: work.kind,\n ...(work.toolName && { toolName: work.toolName }),\n phase: \"end\",\n});\n\n/** Что шло в последнем ответе в момент включения режима: уже «объявлено», чтобы не считаться началом. */\nfunction quietWork(messages: readonly AgentChatMessage[]): Map<string, Map<number, AnnouncedWork>> {\n const last = messages.at(-1);\n const known = new Map<string, Map<number, AnnouncedWork>>();\n if (last?.role !== \"assistant\") return known;\n const work = activeWork(last);\n if (work.size > 0) known.set(last.id, new Map([...work].map(([index, item]) => [index, { ...item, quiet: true }])));\n return known;\n}\n\n/** Чем кончился ответ; ответ, застрявший в «идёт», когда чат уже не занят, — прерванный. */\nfunction endStatus(message: AgentChatMessage): AnswerEnd[\"status\"] {\n const status = message.metadata?.status ?? \"complete\";\n return status === \"streaming\" ? \"interrupted\" : status;\n}\n\nfunction snapshotOf(messages: readonly AgentChatMessage[]): Map<string, number[]> {\n const snapshot = new Map<string, number[]>();\n for (const message of messages) {\n if (message.role !== \"assistant\") continue;\n snapshot.set(\n message.id,\n message.parts.map((part) => (part.type === \"text\" ? part.text.length : 0)),\n );\n }\n return snapshot;\n}\n\n/** Виджеты последнего ответа, что ждут человека, по номерам частей. */\nfunction waitingWidgets(message: AgentChatMessage | undefined): Map<number, WidgetCall> {\n const waiting = new Map<number, WidgetCall>();\n if (message?.role !== \"assistant\") return waiting;\n const awaitingHere = message.metadata?.status === \"awaiting_approval\";\n message.parts.forEach((part, partIndex) => {\n if (!isToolUIPart(part)) return;\n const call = widgetCallOf(part, { awaitingHere, customNames: new Set() });\n if (call && call !== \"hidden\" && call.state === \"pending\") waiting.set(partIndex, call);\n });\n return waiting;\n}\n\n/** Готовые видео ответа: номер части → id вызова. Отказ и идущий вызов не в счёт. */\nfunction shownVideos(message: AgentChatMessage | undefined, videos: VideosClient | undefined): Map<number, string> {\n const shown = new Map<number, string>();\n if (!videos || message?.role !== \"assistant\") return shown;\n message.parts.forEach((part, partIndex) => {\n if (isToolUIPart(part) && getToolName(part) === videos.toolName && videos.describe(part)?.state === \"ready\") shown.set(partIndex, part.toolCallId);\n });\n return shown;\n}\n\n/** Что сказать вслух: у выбора — вопрос и варианты, у остальных — «ответьте на экране или голосом». */\nfunction widgetPhrase(call: WidgetCall, labels: ResolvedChatLabels): string {\n const input = typeof call.input === \"object\" && call.input !== null ? (call.input as Record<string, unknown>) : {};\n const title = [input[\"question\"], input[\"title\"]].find((value): value is string => typeof value === \"string\" && value.length > 0);\n const options = Array.isArray(input[\"options\"]) ? input[\"options\"].filter((option): option is string => typeof option === \"string\") : [];\n if ((call.name === \"ask_choice\" || call.name === \"ask_multi_choice\") && title && options.length > 0) return labels.voice.widgetChoices(title, options);\n return labels.voice.widgetOnScreen(title);\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { AgentChatMessage, AnswerStatus } from \"./chat-types\";\n\n/** Ответ, под которым решается, показывать ли кнопку. */\nexport interface MessageActionTarget {\n message: AgentChatMessage;\n /** Итог ответа: пока ответ пишется, кнопок нет, и `visible` не зовётся. */\n status: Exclude<AnswerStatus, \"streaming\">;\n /** Последний ответ ветки. */\n last: boolean;\n}\n\n/** Что получает обработчик кнопки. */\nexport interface MessageActionContext {\n chatId: string;\n /** Id ответа — тот же, что в истории на сервере и в `onTurnFinished`. */\n messageId: string;\n message: AgentChatMessage;\n /** Текст ответа, как его копирует «Скопировать». */\n text: string;\n}\n\n/**\n * Своя кнопка приложения под ответом агента. Иконку даёт приложение готовым элементом (`<Heart />`): слой поведения и\n * скин дизайн-систему приложения не знают. Массив кнопок держите постоянным (вне компонента или в `useMemo`):\n * новый массив на каждой отрисовке перерисовывает все ответы ленты.\n */\nexport interface MessageAction {\n /** Постоянный и уникальный среди кнопок. */\n id: string;\n /** Подпись: текст кнопки или подсказка и `aria-label` у кнопки с одной иконкой. */\n label: string;\n icon?: ReactNode;\n /** Показывать ли кнопку у этого ответа. Нет — у каждого законченного ответа. */\n visible?: (target: MessageActionTarget) => boolean;\n /** Пока обещание не выполнено, кнопка «занята». Ошибка уходит в консоль, а не в чат. */\n onRun: (context: MessageActionContext) => void | Promise<void>;\n}\n\n/** Id кнопок, чьи обработчики ещё идут, по id ответа. Список ответа меняется, только когда меняются его кнопки. */\nexport type RunningMessageActions = ReadonlyMap<string, readonly string[]>;\n\n/**\n * Запуск своих кнопок ответа: двойное нажатие не запускает обработчик второй раз, пока первый идёт; сбой обработчика\n * уходит в консоль.\n */\nexport function useMessageActionRuns(chatId: string): {\n running: RunningMessageActions;\n run: (action: MessageAction, message: AgentChatMessage, text: string) => void;\n} {\n const [running, setRunning] = useState<RunningMessageActions>(() => new Map());\n const runningRef = useRef(new Set<string>());\n\n const run = useCallback(\n (action: MessageAction, message: AgentChatMessage, text: string) => {\n const key = `${message.id}\\n${action.id}`;\n if (runningRef.current.has(key)) return;\n let result: void | Promise<void>;\n try {\n result = action.onRun({ chatId, messageId: message.id, message, text });\n } catch (error) {\n console.error(error);\n return;\n }\n if (!isPromiseLike(result)) return;\n runningRef.current.add(key);\n setRunning((current) => withRunning(current, message.id, action.id, true));\n void Promise.resolve(result)\n .catch((error: unknown) => console.error(error))\n .finally(() => {\n runningRef.current.delete(key);\n setRunning((current) => withRunning(current, message.id, action.id, false));\n });\n },\n [chatId],\n );\n\n // Лента перерисовывает ответы, только когда меняется то, что они рисуют.\n return useMemo(() => ({ running, run }), [running, run]);\n}\n\nfunction withRunning(current: RunningMessageActions, messageId: string, actionId: string, on: boolean): RunningMessageActions {\n const ids = current.get(messageId) ?? [];\n const next = new Map(current);\n const changed = on ? [...ids, actionId] : ids.filter((id) => id !== actionId);\n if (changed.length > 0) next.set(messageId, changed);\n else next.delete(messageId);\n return next;\n}\n\nconst isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n typeof value === \"object\" && value !== null && typeof (value as { then?: unknown }).then === \"function\";\n","import type { ReactNode } from \"react\";\n\n/** Что `AgentChat.composerActions` может сделать с полем ввода и отправкой. */\nexport interface ComposerHelper {\n /** Текст в поле ввода сейчас. */\n value: string;\n /** Заменить текст в поле ввода. */\n setValue(text: string): void;\n /**\n * Отправить сообщение по тем же правилам, что кнопка «Отправить». С `text` уходит он, поле не трогается;\n * без `text` — то, что в поле (с файлами). Пока `canSend` ложно (история не загрузилась, идёт ответ, лимит исчерпан),\n * обе формы отказывают сразу и ничего не откладывают: проверяйте `canSend` и гасите кнопку.\n */\n send(text?: string): void;\n /** Положить файлы в поле ввода, как скрепка. Без плагина вложений ничего не делает. */\n addFiles(files: File[]): void;\n /** Агент отвечает: отправка сейчас не пройдёт. */\n busy: boolean;\n /** Отправка остановлена (исчерпан лимит расходов). */\n blocked: boolean;\n /** Отправка сейчас пройдёт: история загружена, ответ не идёт, лимит не исчерпан. */\n canSend: boolean;\n}\n\n/**\n * Свои кнопки у поля ввода: готовый узел или функция от помощника. Функция вызывается при каждом рендере чата:\n * делайте её быстрой и без побочных эффектов; помощника не сохраняйте, каждый раз приходит новый.\n */\nexport type ComposerActions = ReactNode | ((composer: ComposerHelper) => ReactNode);\n\n/** Готовый узел для `Composer.actions` скина; `undefined` — кнопок нет. */\nexport function renderComposerActions(actions: ComposerActions, helper: ComposerHelper): ReactNode {\n return typeof actions === \"function\" ? actions(helper) : actions;\n}\n","import { useEffect, useRef } from \"react\";\nimport type { AgentChatMessage, AnswerStatus } from \"./chat-types\";\nimport { isCorrection } from \"./steering/corrections\";\n\n/** Ход закончен на экране: ответ получил итог. */\nexport interface TurnFinishedEvent {\n chatId: string;\n /** Id ответа агента — тот же, что в истории на сервере. */\n messageId: string;\n status: Exclude<AnswerStatus, \"streaming\">;\n}\n\n/**\n * Зовёт `onTurnFinished` один раз на ход: чат был занят ответом, освободился, и у последнего ответа есть итог\n * (законченный, остановленный, с ошибкой, прерванный, ждущий человека). Открытие чата с готовой историей хода не даёт.\n * Ответ, законченный ради поправки, — свой ход и на сервере: он получает событие первым, перед ответом на поправку.\n */\nexport function useTurnFinishedReport(\n chatId: string,\n busy: boolean,\n messages: readonly AgentChatMessage[],\n onTurnFinished: ((turn: TurnFinishedEvent) => void) | undefined,\n): void {\n const report = useRef(onTurnFinished);\n report.current = onTurnFinished;\n const turnRunning = useRef(false);\n const latest = useRef(messages);\n latest.current = messages;\n /** Ответы с итогом на начало хода: их ход уже прошёл, второй раз о них не сообщается. */\n const finishedBefore = useRef<ReadonlySet<string>>(new Set());\n const last = messages.at(-1);\n const answerId = last?.role === \"assistant\" ? last.id : undefined;\n const answerStatus = last?.role === \"assistant\" ? last.metadata?.status : undefined;\n\n useEffect(() => {\n if (busy) {\n if (!turnRunning.current) finishedBefore.current = finishedAnswerIds(latest.current);\n turnRunning.current = true;\n return;\n }\n if (!turnRunning.current) return;\n const reportSteered = () => {\n for (const steered of steeredAnswersBefore(latest.current)) {\n if (!finishedBefore.current.has(steered.id)) report.current?.({ chatId, messageId: steered.id, status: steered.status });\n }\n };\n // Ответа на последнее сообщение человека нет: обычный ход не начался (отказ сервера), событий нет; продолжение\n // по поправке не стартовало (лимит, чужие ходы, чат закрыт) — события получают ответы цепочки до неё.\n if (answerId === undefined) {\n turnRunning.current = false;\n reportSteered();\n return;\n }\n // Связь оборвалась без итога: ход закончится, когда чат снова получит ответ с итогом.\n if (answerStatus === undefined || answerStatus === \"streaming\") return;\n turnRunning.current = false;\n reportSteered();\n report.current?.({ chatId, messageId: answerId, status: answerStatus });\n }, [answerId, answerStatus, busy, chatId]);\n}\n\ntype FinalStatus = TurnFinishedEvent[\"status\"];\n\nfunction finalStatusOf(message: AgentChatMessage): FinalStatus | undefined {\n const status = message.metadata?.status;\n return status === undefined || status === \"streaming\" ? undefined : status;\n}\n\nfunction finishedAnswerIds(messages: readonly AgentChatMessage[]): ReadonlySet<string> {\n return new Set(messages.flatMap((message) => (message.role === \"assistant\" && finalStatusOf(message) ? [message.id] : [])));\n}\n\n/**\n * Ответы, законченные ради поправок, в конце ленты: цепочка «ответ, поправки, ответ…» перед последним ответом или\n * перед поправками, которые остались без ответа. По порядку.\n */\nfunction steeredAnswersBefore(messages: readonly AgentChatMessage[]): Array<{ id: string; status: FinalStatus }> {\n const steered: Array<{ id: string; status: FinalStatus }> = [];\n let index = messages.at(-1)?.role === \"assistant\" ? messages.length - 2 : messages.length - 1;\n for (;;) {\n if (index < 0 || !isCorrection(messages[index]!)) break;\n while (index >= 0 && isCorrection(messages[index]!)) index--;\n const answer = messages[index];\n const status = answer && answer.role === \"assistant\" ? finalStatusOf(answer) : undefined;\n if (!answer || !status) break;\n steered.unshift({ id: answer.id, status });\n index--;\n }\n return steered;\n}\n","import type { WorkViewLabels } from \"../labels\";\nimport type { ToolCallState } from \"../skin-contract\";\nimport type { StepIcon } from \"./step-icons\";\n\n/**\n * Вид работы агента в ленте. `detailed` — мысли и строки инструментов с входом и выходом, как до 0.45.\n * `simple` — пока ход идёт, одна живая строка с текущим шагом; после хода шаги свёрнуты в «Сделано N шагов».\n */\nexport type WorkView = \"detailed\" | \"simple\";\n\n/** Вызов инструмента для фразы. Вход и выход бывают не всегда: вход ещё пишется или скрыт правами. */\nexport interface StepCall {\n toolName: string;\n state: ToolCallState;\n input?: unknown;\n output?: unknown;\n}\n\n/**\n * Фразы шага: пока идёт («Делаю календарь»), готово («Сделал календарь»), не вышло (необязательно). `icon` — значок\n * шага (договор скина 28, `StepIcon`); нет — угадывается по имени инструмента.\n */\nexport interface StepPhraseText {\n running: string;\n done: string;\n failed?: string;\n icon?: StepIcon;\n}\n\n/** Фразы шага готовые или по вызову. Нет ответа (`undefined`) — общая фраза. */\nexport type StepPhrase = StepPhraseText | ((call: StepCall) => Partial<StepPhraseText> | undefined);\n\n/** Фразы шагов приложения: имя инструмента → фразы. */\nexport type StepPhrases = Record<string, StepPhrase>;\n\n/** Самая длинная фраза шага: длиннее — обрезается с «…», чтобы строка помещалась в пузырь на телефоне. */\nconst PHRASE_MAX_LENGTH = 120;\n\n/** Непустая строка в одну строку, без многоточия на конце, не длиннее `PHRASE_MAX_LENGTH`; иначе `undefined`. */\nfunction cleanPhrase(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const phrase = value.replace(/\\s+/g, \" \").trim().replace(/(…|\\.\\.\\.)$/, \"\").trimEnd();\n if (phrase.length === 0) return undefined;\n return phrase.length > PHRASE_MAX_LENGTH ? `${phrase.slice(0, PHRASE_MAX_LENGTH - 1).trimEnd()}…` : phrase;\n}\n\n/** Фразы приложения для вызова; ошибка функции или странный ответ — `undefined`, чат не ломается. */\nfunction appPhraseOf(call: StepCall, phrases: StepPhrases | undefined): Partial<StepPhraseText> | undefined {\n const entry = phrases && Object.hasOwn(phrases, call.toolName) ? phrases[call.toolName] : undefined;\n if (entry === undefined) return undefined;\n try {\n const phrase = typeof entry === \"function\" ? entry(call) : entry;\n return typeof phrase === \"object\" && phrase !== null ? phrase : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Понятная фраза шага по его состоянию. Порядок: фраза приложения (`stepPhrases`), запасная фраза слоя поведения\n * (`fallback`, например у помощника), название инструмента от приложения (`title`), общая фраза словаря.\n * `unfinished` — ответ закончился, а вызов так и остался «идёт» («Стоп», обрыв): «Делаю календарь — не закончил».\n */\nexport function stepPhraseOf(\n call: StepCall & { title?: string },\n {\n phrases,\n labels,\n fallback,\n unfinished = false,\n }: { phrases?: StepPhrases | undefined; labels: WorkViewLabels; fallback?: StepPhraseText | undefined; unfinished?: boolean },\n): string {\n const { title, ...appCall } = call;\n const own = appPhraseOf(appCall, phrases);\n const named = cleanPhrase(title);\n const running = cleanPhrase(own?.running) ?? cleanPhrase(fallback?.running) ?? named;\n if (call.state === \"running\") {\n if (unfinished) return running ? labels.stepUnfinished(running) : labels.stepUnfinishedUnknown;\n return running ?? labels.stepRunning;\n }\n if (call.state === \"error\") return cleanPhrase(own?.failed) ?? cleanPhrase(fallback?.failed) ?? (running ? labels.stepFailed(running) : labels.stepFailedUnknown);\n return cleanPhrase(own?.done) ?? cleanPhrase(fallback?.done) ?? named ?? labels.stepDone;\n}\n","import type { WorkViewLabels } from \"../labels\";\nimport { stepIconOf } from \"./step-icons\";\nimport type { StepIcon } from \"./step-icons\";\nimport { stepPhraseOf } from \"./step-phrases\";\nimport type { StepCall, StepPhrases, StepPhraseText } from \"./step-phrases\";\n\n/**\n * Шаг работы: размышление модели (`title` — заголовок раздумья от сервера, `durationMs` — сколько думала, `null` — ещё\n * думает) или вызов инструмента (`title` — название от приложения, `fallback` — запасные фразы).\n */\nexport type WorkStep =\n | { kind: \"thinking\"; title?: string; durationMs?: number | null }\n | { kind: \"tool\"; call: StepCall; title?: string; fallback?: StepPhraseText };\n\n/**\n * Часть ответа для раскладки: то, что видно как обычно (`blank` — пустой текст, модель только начала писать), или шаг\n * работы. `key` шага — ключ части ответа: по первому шагу группы хранится раскрытие свёрнутой строки.\n */\nexport type AnswerEntry<Node> = { kind: \"shown\"; node: Node; blank?: boolean } | { kind: \"step\"; key: string; step: WorkStep };\n\n/** Строка списка шагов. */\nexport interface WorkStepLine {\n phrase: string;\n failed: boolean;\n}\n\n/** Строка списка шагов со значком (договор 28): и вызовы, и раздумья. */\nexport interface WorkStepItem extends WorkStepLine {\n icon: StepIcon;\n}\n\n/** Больше значков в свёрнутом ряду не рисуется: остальные — «+N». */\nexport const STEP_ICON_ROW_MAX = 8;\n\n/** Раскладка ответа в простом виде: видимые части, живая строка последней группы, свёрнутые группы. */\nexport type WorkLayoutItem<Node> =\n | { kind: \"shown\"; node: Node }\n | { kind: \"live\"; key: string; phrase: string; icon: StepIcon; completed?: WorkStepItem[] }\n | { kind: \"done\"; key: string; steps: WorkStepLine[]; count: number; items: WorkStepItem[]; icons: StepIcon[]; moreIcons: number };\n\ninterface LayoutOptions {\n /** Ответ ещё пишется: последняя группа шагов — живая строка. */\n streaming: boolean;\n phrases?: StepPhrases | undefined;\n labels: WorkViewLabels;\n /**\n * Раздумья — тоже шаги списка и ряда значков (договор 28). Экран включает, когда у ответа есть заголовки раздумий\n * (`thoughtTitles`): без них в списке только вызовы, как до 0.69.\n */\n thoughts?: boolean;\n}\n\n/**\n * Простой вид: подряд идущие шаги (размышления и вызовы) — одна группа; пустой текст между ними группу не рвёт и\n * ставится после неё. Последняя группа идущего ответа, после которой ничего не видно, — живая строка с текущим шагом.\n * Остальные — свёрнутые «Сделано N шагов» с понятными фразами; одинаковые фразы подряд — одна строка «… ×N», группа из\n * одних размышлений не показывается. Вызов, оставшийся «идёт» в законченном ответе («Стоп», обрыв), — «не закончил».\n */\nexport function workLayoutOf<Node>(entries: AnswerEntry<Node>[], { streaming, phrases, labels, thoughts = false }: LayoutOptions): WorkLayoutItem<Node>[] {\n const lastShown = entries.findLastIndex((entry) => entry.kind === \"shown\" && !entry.blank);\n const layout: WorkLayoutItem<Node>[] = [];\n let index = 0;\n while (index < entries.length) {\n const entry = entries[index]!;\n if (entry.kind === \"shown\") {\n layout.push({ kind: \"shown\", node: entry.node });\n index++;\n continue;\n }\n const group: WorkStep[] = [];\n const blanks: WorkLayoutItem<Node>[] = [];\n const key = entry.key;\n let lastStep = index;\n for (; index < entries.length; index++) {\n const next = entries[index]!;\n if (next.kind === \"step\") {\n group.push(next.step);\n lastStep = index;\n } else if (next.blank) blanks.push({ kind: \"shown\", node: next.node });\n else break;\n }\n if (streaming && lastStep > lastShown) {\n const live = liveStepOf(group, phrases, labels);\n const completed = doneLinesOf((thoughts ? group : group.filter((step) => step.kind === \"tool\")).filter((step) => step.kind === \"tool\" && step.call.state !== \"running\"), phrases, labels, false).items;\n layout.push({ kind: \"live\", key, phrase: labels.live(live.phrase), icon: live.icon, ...(completed.length > 0 && { completed }) });\n } else {\n const done = doneLinesOf(thoughts ? group : group.filter((step) => step.kind === \"tool\"), phrases, labels, !streaming);\n if (done.count > 0) layout.push({ kind: \"done\", key, ...done });\n }\n layout.push(...blanks);\n }\n return layout;\n}\n\n/**\n * Текущий шаг живой строкой: идущий вызов — его фраза и значок (при параллельных — последний из ещё идущих),\n * размышление — его заголовок или «Думаю», все вызовы закончились — общая фраза.\n */\nfunction liveStepOf(group: WorkStep[], phrases: StepPhrases | undefined, labels: WorkViewLabels): { phrase: string; icon: StepIcon } {\n const last = group.at(-1)!;\n const running = group.findLast((step) => step.kind === \"tool\" && step.call.state === \"running\");\n if (last.kind === \"tool\" && running?.kind === \"tool\") return { phrase: phraseOf(running, phrases, labels), icon: iconOf(running, phrases) };\n if (last.kind === \"thinking\") return { phrase: last.title ?? labels.thinking, icon: \"thinking\" };\n return { phrase: labels.stepRunning, icon: iconOf(last, phrases) };\n}\n\nfunction iconOf(step: Extract<WorkStep, { kind: \"tool\" }>, phrases: StepPhrases | undefined): StepIcon {\n return stepIconOf(step.call, { phrases, fallback: step.fallback });\n}\n\nfunction phraseOf(step: Extract<WorkStep, { kind: \"tool\" }>, phrases: StepPhrases | undefined, labels: WorkViewLabels, unfinished = false): string {\n return stepPhraseOf({ ...step.call, ...(step.title !== undefined && { title: step.title }) }, { phrases, labels, fallback: step.fallback, unfinished });\n}\n\n/**\n * Строки списка, число вызовов и ряд значков; `finished` — ответ закончен, и «идёт» значит «не закончил». `steps` —\n * только вызовы (скины 23–27), `items` — вызовы и раздумья со значками, одинаковые подряд схлопнуты; в ряду значков\n * схлопывания нет: по значку на шаг.\n */\nfunction doneLinesOf(\n group: WorkStep[],\n phrases: StepPhrases | undefined,\n labels: WorkViewLabels,\n finished: boolean,\n): { steps: WorkStepLine[]; count: number; items: WorkStepItem[]; icons: StepIcon[]; moreIcons: number } {\n const all = group.map((step) =>\n step.kind === \"tool\"\n ? { ...toolLineOf(step, phrases, labels, finished), tool: true }\n : { icon: \"thinking\", phrase: step.title ?? labels.thought(step.durationMs ?? null), failed: false, tool: false },\n );\n const calls = all.filter((line) => line.tool);\n const icons = all.map((line) => line.icon);\n return {\n steps: collapsed(calls, labels).map(({ phrase, failed }) => ({ phrase, failed })),\n count: calls.length,\n items: collapsed(all, labels),\n icons: icons.slice(0, STEP_ICON_ROW_MAX),\n moreIcons: Math.max(0, icons.length - STEP_ICON_ROW_MAX),\n };\n}\n\n/** Одинаковые строки подряд — одна строка «… ×N». */\nfunction collapsed(lines: WorkStepItem[], labels: WorkViewLabels): WorkStepItem[] {\n const runs: Array<WorkStepItem & { repeats: number }> = [];\n for (const line of lines) {\n const previous = runs.at(-1);\n if (previous?.phrase === line.phrase && previous.failed === line.failed && previous.icon === line.icon) previous.repeats++;\n else runs.push({ icon: line.icon, phrase: line.phrase, failed: line.failed, repeats: 1 });\n }\n return runs.map(({ repeats, ...line }) => (repeats > 1 ? { ...line, phrase: labels.stepRepeated(line.phrase, repeats) } : line));\n}\n\nfunction toolLineOf(step: Extract<WorkStep, { kind: \"tool\" }>, phrases: StepPhrases | undefined, labels: WorkViewLabels, finished: boolean): WorkStepItem {\n const unfinished = finished && step.call.state === \"running\";\n return { icon: iconOf(step, phrases), phrase: phraseOf(step, phrases, labels, unfinished), failed: step.call.state === \"error\" || unfinished };\n}\n","import { usePartOpen } from \"../expanded-parts\";\nimport type { ExpandedParts } from \"../expanded-parts\";\nimport { useChatLabels } from \"../labels\";\nimport type { ChatSkin } from \"../skin-contract\";\nimport type { WorkLayoutItem } from \"./work-layout\";\n\nconst keepClosed = () => {};\n\n/**\n * Живая строка или свёрнутые шаги простого вида; скин 28 получает значки и список со раздумьями. Раскрытие — в хранилище чата под своим ключом (`…:work`), отдельно от\n * раскрытия мыслей и строк подробного вида. Скин без `WorkSteps` (до версии 23) получает строку `ToolCallRow`: фраза\n * в `title`, входа нет, а результат — список понятных фраз текстом, по строке на шаг. Ни кода, ни путей.\n */\nexport function WorkStepsPart({\n skin,\n expanded,\n item,\n}: {\n skin: ChatSkin;\n expanded: ExpandedParts;\n item: Exclude<WorkLayoutItem<unknown>, { kind: \"shown\" }>;\n}) {\n const labels = useChatLabels();\n const [open, setOpen] = usePartOpen(expanded, `${item.key}:work`);\n if (skin.WorkSteps) {\n return item.kind === \"live\" ? (\n <skin.WorkSteps state=\"live\" phrase={item.phrase} icon={item.icon} {...(item.completed && { completed: item.completed })} />\n ) : (\n <skin.WorkSteps\n state=\"done\"\n steps={item.steps}\n count={item.count}\n items={item.items}\n icons={item.icons}\n moreIcons={item.moreIcons}\n open={open}\n onOpenChange={setOpen}\n />\n );\n }\n if (item.kind === \"live\") return <skin.ToolCallRow toolName={item.phrase} title={item.phrase} state=\"running\" open={false} onOpenChange={keepClosed} />;\n const title = labels.workView.stepsDone(item.count);\n const list = item.steps.map((step) => step.phrase).join(\"\\n\");\n return <skin.ToolCallRow toolName={title} title={title} state=\"done\" output={list} open={open} onOpenChange={setOpen} />;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { AppRefusal } from \"../app-refusal\";\nimport { defaultRememberKey, readRememberedChat, rememberChat } from \"./active-chat-memory\";\nimport {\n ChatCreationRefusedError,\n createChatOnServer,\n deleteChatOnServer,\n fetchChatList,\n renameChatOnServer,\n} from \"./chat-list-client\";\nimport type { ChatListEntry } from \"./chat-list-client\";\n\nexport type ChatListStatus = \"loading\" | \"ready\" | \"error\";\n\n/** Какое действие не удалось; сам список не загрузился — `status: \"error\"`. */\nexport type ChatListActionError = \"create\" | \"rename\" | \"delete\";\n\nexport interface UseChatListOptions {\n /** Адрес обработчика чата, например `/api/chat`. */\n api: string;\n /** Какой чат открыть первым (например, из адреса страницы). Главнее запомненного; чата нет в списке — откроется «Чат не найден». */\n initialChatId?: string;\n /** Свой `fetch` — для тестов и особых сред. */\n fetch?: typeof globalThis.fetch;\n /** Ключ запомненного чата. По умолчанию `agent-chat:active-chat:${api}`; у панели агента свой, на человека. Смена ключа — список загружается заново, прежний открытый чат не переносится. */\n rememberKey?: string;\n /** Приложение не дало создать чат (`chat_creation_refused`): та же весть, что `onTurnRefused` у чата. Текст отказа список показывает сам. */\n onCreateRefused?: (refusal: AppRefusal) => void;\n}\n\nexport interface ChatListState {\n status: ChatListStatus;\n items: ChatListEntry[];\n /** Открытый чат; `null`, пока список грузится или не загрузился. */\n activeChatId: string | null;\n error: ChatListActionError | null;\n /** Отказ приложения, из-за которого `error` — `create`: его слова показываются вместо общего «Не удалось создать чат». */\n creationRefusal: AppRefusal | null;\n creating: boolean;\n /** Чат, удаление которого ждёт подтверждения. Удаляет только `confirmDelete`. */\n deleteRequest: { id: string; title: string | null } | null;\n deleting: boolean;\n select(chatId: string): void;\n /** Открывает пустой чат (без названия и без сообщений), если он уже есть, иначе создаёт новый. `false` — не вышло (`error` — `create`). */\n create(): Promise<boolean>;\n /** Пустое или прежнее название — ничего не делает. Не вышло — прежнее название возвращается. */\n rename(chatId: string, title: string): Promise<void>;\n requestDelete(chatId: string): void;\n cancelDelete(): void;\n confirmDelete(): Promise<void>;\n /** Тихо обновляет список (название появилось, порядок сменился); отказ не показывается. */\n refresh(): Promise<void>;\n /** Загружает список заново после отказа. */\n reload(): void;\n}\n\n/**\n * Чат без названия и без сообщений; старый чат с одними файлами тоже без названия, но время его активности позже создания.\n * Чат объекта (урок) пустым не считается: «Новый чат» не должен открывать урок, даже если в нём одно приветствие.\n * Обычный чат с одним приветствием (дата приветствия — дата чата) пустой: его и откроет «Новый чат».\n */\nconst isEmptyChat = (item: ChatListEntry) => item.title === null && item.updatedAt === item.createdAt && item.subjectKey === undefined;\n\n/** Список нового человека, пока он не загружен: ничего от прежнего. */\nconst NOTHING_LOADED = {\n status: \"loading\",\n items: [],\n activeChatId: null,\n error: null,\n creationRefusal: null,\n creating: false,\n deleteRequest: null,\n deleting: false,\n} satisfies Partial<ChatListState>;\n\nconst collapse = (title: string) => title.trim().replace(/\\s+/g, \" \");\n\n/** Состояние списка чатов: загрузка, выбор открытого чата, создание, переименование и удаление. Ничего не рисует. */\nexport function useChatList({ api, initialChatId, fetch: customFetch, rememberKey, onCreateRefused }: UseChatListOptions): ChatListState {\n const memoryKey = rememberKey ?? defaultRememberKey(api);\n const [status, setStatus] = useState<ChatListStatus>(\"loading\");\n const [items, setItems] = useState<ChatListEntry[]>([]);\n const [activeChatId, setActiveChatId] = useState<string | null>(null);\n const [error, setError] = useState<ChatListActionError | null>(null);\n const [creationRefusal, setCreationRefusal] = useState<AppRefusal | null>(null);\n const [creating, setCreating] = useState(false);\n const [deleteRequest, setDeleteRequest] = useState<ChatListState[\"deleteRequest\"]>(null);\n const [deleting, setDeleting] = useState(false);\n\n // Сменился человек: в этом же кадре — ни списка, ни открытого чата, ни действий прежнего.\n const [stateKey, setStateKey] = useState(memoryKey);\n const keyChanged = stateKey !== memoryKey;\n if (keyChanged) {\n setStateKey(memoryKey);\n setStatus(\"loading\");\n setItems([]);\n setActiveChatId(null);\n setError(null);\n setCreationRefusal(null);\n setCreating(false);\n setDeleteRequest(null);\n setDeleting(false);\n }\n /** Ключ, под которым список сейчас на экране: ответ, пришедший после смены человека, к новому не относится. */\n const keyRef = useRef(memoryKey);\n keyRef.current = memoryKey;\n\n const fetchRef = useRef(customFetch);\n fetchRef.current = customFetch;\n const refusedRef = useRef(onCreateRefused);\n refusedRef.current = onCreateRefused;\n const send: typeof globalThis.fetch = useCallback((input, init) => (fetchRef.current ?? globalThis.fetch)(input, init), []);\n const itemsRef = useRef(items);\n itemsRef.current = keyChanged ? [] : items;\n const activeRef = useRef(activeChatId);\n activeRef.current = keyChanged ? null : activeChatId;\n const initialRef = useRef(initialChatId);\n /** Номер последней полной загрузки: ответы более ранних игнорируются. */\n const loadRequest = useRef(0);\n /** Номер последнего запроса списка (загрузка или тихое обновление): устаревшее обновление не затирает свежее. */\n const listRequest = useRef(0);\n /** Ключ, под который загружен список: другой ключ — другой человек, его список и его запомненный чат. */\n const loadedKey = useRef<string | null>(null);\n const alive = useRef(true);\n useEffect(() => {\n alive.current = true;\n return () => {\n alive.current = false;\n };\n }, []);\n /** Ответ на запрос, начатый для человека `key`, ещё нужен: компонент жив и человек тот же. */\n const stillFor = useCallback((key: string) => alive.current && keyRef.current === key, []);\n\n /** Создать чат не вышло: ошибка `create`, со словами приложения, если оно отказало само. */\n const failCreate = useCallback(\n (cause: unknown) => {\n const refusal = cause instanceof ChatCreationRefusedError ? cause.refusal : null;\n setCreationRefusal(refusal);\n setError(\"create\");\n if (refusal) refusedRef.current?.(refusal);\n },\n [],\n );\n\n const activate = useCallback(\n (chatId: string) => {\n activeRef.current = chatId;\n setActiveChatId(chatId);\n rememberChat(memoryKey, chatId);\n },\n [memoryKey],\n );\n\n /** Новый пустой чат на сервере: первым в списке и открытым. `false` — человек сменился, пока чат создавался. */\n const createAndOpen = useCallback(async () => {\n const id = await createChatOnServer(api, send);\n if (!stillFor(memoryKey)) return false;\n const now = new Date().toISOString();\n setItems((current) => [{ id, title: null, createdAt: now, updatedAt: now }, ...current.filter((item) => item.id !== id)]);\n activate(id);\n return true;\n }, [activate, api, memoryKey, send, stillFor]);\n\n const load = useCallback(async () => {\n if (loadedKey.current !== memoryKey) {\n // Сменился человек: начальный чат из адреса к нему не относится (список и открытый чат сброшены при отрисовке).\n if (loadedKey.current !== null) initialRef.current = undefined;\n loadedKey.current = memoryKey;\n }\n const request = ++loadRequest.current;\n listRequest.current += 1;\n setStatus(\"loading\");\n try {\n const loaded = await fetchChatList(api, send);\n if (!alive.current || request !== loadRequest.current) return;\n setItems(loaded);\n setStatus(\"ready\");\n if (activeRef.current !== null) return;\n const remembered = readRememberedChat(memoryKey);\n const chosen =\n initialRef.current ??\n (remembered !== null && loaded.some((item) => item.id === remembered) ? remembered : loaded[0]?.id);\n if (chosen !== undefined) activate(chosen);\n else\n try {\n await createAndOpen();\n } catch (cause) {\n // Приложение отказало в первом чате: список загружен, отказ показывается в нём, а не «список не загрузился».\n if (!(cause instanceof ChatCreationRefusedError)) throw cause;\n if (stillFor(memoryKey) && request === loadRequest.current) failCreate(cause);\n }\n } catch {\n if (!alive.current || request !== loadRequest.current) return;\n setStatus(\"error\");\n // Адрес чата известен: открываем его и без списка, пусть чат сам скажет, что с ним (история не грузится, чата нет).\n if (activeRef.current === null && initialRef.current !== undefined) activate(initialRef.current);\n }\n }, [activate, api, createAndOpen, failCreate, memoryKey, send, stillFor]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const refresh = useCallback(async () => {\n const request = ++listRequest.current;\n try {\n const loaded = await fetchChatList(api, send);\n if (stillFor(memoryKey) && request === listRequest.current) setItems(loaded);\n } catch {\n // Тихое обновление: устаревший список лучше сообщения об ошибке.\n }\n }, [api, memoryKey, send, stillFor]);\n\n const create = useCallback(async () => {\n setError(null);\n setCreating(true);\n try {\n // Пустой чат ищем по свежим данным: в открытом могли написать, а список ещё не знает.\n // Пустой — без названия и без сообщений: у чата с сообщениями время последней активности позже создания.\n const fresh = await fetchChatList(api, send).catch(() => null);\n if (!stillFor(memoryKey)) return false;\n if (fresh) setItems(fresh);\n const empty = (fresh ?? itemsRef.current).find(isEmptyChat);\n if (!empty) return await createAndOpen();\n activate(empty.id);\n return true;\n } catch (cause) {\n if (stillFor(memoryKey)) failCreate(cause);\n return false;\n } finally {\n if (stillFor(memoryKey)) setCreating(false);\n }\n }, [activate, api, createAndOpen, failCreate, memoryKey, send, stillFor]);\n\n const select = useCallback(\n (chatId: string) => {\n setError(null);\n activate(chatId);\n },\n [activate],\n );\n\n const rename = useCallback(\n async (chatId: string, title: string) => {\n const normalized = collapse(title);\n const current = itemsRef.current.find((item) => item.id === chatId);\n if (!current || normalized === \"\" || normalized === current.title) return;\n setError(null);\n const setTitle = (value: string | null) =>\n setItems((list) => list.map((item) => (item.id === chatId ? { ...item, title: value } : item)));\n setTitle(normalized);\n try {\n const saved = await renameChatOnServer(api, send, chatId, normalized);\n if (stillFor(memoryKey)) setTitle(saved);\n } catch {\n if (!stillFor(memoryKey)) return;\n setTitle(current.title);\n setError(\"rename\");\n }\n },\n [api, memoryKey, send, stillFor],\n );\n\n const requestDelete = useCallback((chatId: string) => {\n const item = itemsRef.current.find((entry) => entry.id === chatId);\n if (item) setDeleteRequest({ id: item.id, title: item.title });\n }, []);\n\n const cancelDelete = useCallback(() => setDeleteRequest(null), []);\n\n const confirmDelete = useCallback(async () => {\n if (!deleteRequest) return;\n const { id } = deleteRequest;\n setError(null);\n setDeleting(true);\n try {\n await deleteChatOnServer(api, send, id);\n } catch {\n if (stillFor(memoryKey)) {\n setError(\"delete\");\n setDeleteRequest(null);\n setDeleting(false);\n }\n return;\n }\n if (!stillFor(memoryKey)) return;\n const before = itemsRef.current;\n const index = before.findIndex((item) => item.id === id);\n const rest = before.filter((item) => item.id !== id);\n setItems(rest);\n setDeleteRequest(null);\n setDeleting(false);\n if (activeRef.current !== id) return;\n // Открытый чат исчез: соседний по списку, а если чатов не осталось — новый пустой.\n const next = rest[index] ?? rest[index - 1];\n if (next) {\n activate(next.id);\n return;\n }\n activeRef.current = null;\n setActiveChatId(null);\n try {\n await createAndOpen();\n } catch (cause) {\n if (stillFor(memoryKey)) failCreate(cause);\n }\n }, [activate, api, createAndOpen, deleteRequest, failCreate, memoryKey, send, stillFor]);\n\n const reload = useCallback(() => void load(), [load]);\n\n return {\n ...(keyChanged ? NOTHING_LOADED : { status, items, activeChatId, error, creationRefusal, creating, deleteRequest, deleting }),\n select,\n create,\n rename,\n requestDelete,\n cancelDelete,\n confirmDelete,\n refresh,\n reload,\n };\n}\n","/** Ключ, под которым запоминается открытый чат, если приложение не задало свой. */\nexport const defaultRememberKey = (api: string) => `agent-chat:active-chat:${api}`;\n\n/** Чат, открытый в прошлый раз в этом браузере (под этим ключом). Хранилище недоступно или пусто — `null`. */\nexport function readRememberedChat(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nexport function rememberChat(key: string, chatId: string): void {\n try {\n localStorage.setItem(key, chatId);\n } catch {\n // Приватный режим или заполненное хранилище: в следующий раз откроется самый недавний чат.\n }\n}\n","import type { AppRefusal } from \"../app-refusal\";\nimport { appRefusalOf } from \"../app-refusal\";\nimport type { ChatClosure } from \"../chat-access\";\n\n/** Строка списка чатов, как её отдаёт сервер (`GET {api}`). */\nexport interface ChatListEntry {\n id: string;\n /** `null` — у чата ещё нет названия. */\n title: string | null;\n createdAt: string;\n updatedAt: string;\n /** Мессенджер, из которого создан чат; нет — чат из веба. */\n origin?: string;\n /** Ключ объекта приложения (урок, заказ): есть у чатов объектов, когда сервер их показывает (`chatList.subjectChats`). */\n subjectKey?: string;\n /** Чат закрыт (`closeChat`): причина кодом и словами; у открытого чата поля нет. Форму проверяет слой поведения. */\n closed?: ChatClosure;\n}\n\ntype Fetch = typeof globalThis.fetch;\n\n/** Сервер ответил отказом; `status` — код ответа. */\nexport class ChatListRequestError extends Error {\n constructor(\n readonly status: number,\n action: string,\n ) {\n super(`Chat list request failed (${action}): HTTP ${status}`);\n this.name = \"ChatListRequestError\";\n }\n}\n\n/** Приложение не дало создать чат (`403 { error: { kind: \"chat_creation_refused\", code, message } }`). */\nexport class ChatCreationRefusedError extends Error {\n constructor(readonly refusal: AppRefusal) {\n super(refusal.message);\n this.name = \"ChatCreationRefusedError\";\n }\n}\n\nconst chatUrl = (api: string, chatId: string) => `${api.replace(/\\/+$/, \"\")}/${encodeURIComponent(chatId)}`;\n\nasync function request(fetchImpl: Fetch, action: string, url: string, init?: RequestInit): Promise<Response> {\n const response = await fetchImpl(url, init);\n if (!response.ok) throw new ChatListRequestError(response.status, action);\n return response;\n}\n\nconst jsonBody = (body: unknown): RequestInit => ({\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n});\n\nexport async function fetchChatList(api: string, fetchImpl: Fetch): Promise<ChatListEntry[]> {\n const response = await request(fetchImpl, \"list\", api.replace(/\\/+$/, \"\"));\n return ((await response.json()) as { chats: ChatListEntry[] }).chats;\n}\n\n/** Создаёт пустой чат; возвращает его id. */\nexport async function createChatOnServer(api: string, fetchImpl: Fetch): Promise<string> {\n const response = await fetchImpl(api.replace(/\\/+$/, \"\"), { method: \"POST\" });\n if (!response.ok) {\n const refusal = await appRefusalOf(response);\n if (refusal?.kind === \"chat_creation_refused\") throw new ChatCreationRefusedError(refusal);\n throw new ChatListRequestError(response.status, \"create\");\n }\n return ((await response.json()) as { id: string }).id;\n}\n\n/** Возвращает название, как оно записано на сервере. */\nexport async function renameChatOnServer(api: string, fetchImpl: Fetch, chatId: string, title: string): Promise<string> {\n const response = await request(fetchImpl, \"rename\", chatUrl(api, chatId), { method: \"PATCH\", ...jsonBody({ title }) });\n return ((await response.json()) as { title: string }).title;\n}\n\n/** Удаляет чат. Чата уже нет (404) — это тоже успех: человек хотел, чтобы его не было. */\nexport async function deleteChatOnServer(api: string, fetchImpl: Fetch, chatId: string): Promise<void> {\n const response = await fetchImpl(chatUrl(api, chatId), { method: \"DELETE\" });\n if (!response.ok && response.status !== 404) throw new ChatListRequestError(response.status, \"delete\");\n}\n","import { useCallback } from \"react\";\nimport type { ComponentProps } from \"react\";\nimport { closureOf } from \"../chat-access\";\nimport { useChatLabels } from \"../labels\";\nimport type { ChatLabels } from \"../labels\";\nimport type { ChatOriginView } from \"../skin-contract\";\nimport type { ChatListSkin } from \"./chat-list-skin\";\nimport type { ChatListActionError, ChatListState } from \"./use-chat-list\";\n\nexport type ChatListViewProps = ComponentProps<ChatListSkin[\"ChatList\"]>;\n\n/**\n * Что получает `skin.ChatList`: строки, действия, подтверждение удаления и ошибка готовой надписью.\n * `onChosen` — чат выбран или создан (например, чтобы закрыть список поверх чата). Общее у `ChatWorkspace` и панели агента.\n */\nexport function useChatListView(list: ChatListState, onChosen: () => void): ChatListViewProps {\n const labels = useChatLabels();\n const { select, create: createChat } = list;\n const onSelect = useCallback(\n (chatId: string) => {\n select(chatId);\n onChosen();\n },\n [onChosen, select],\n );\n const onCreate = useCallback(() => {\n void createChat().then((opened) => opened && onChosen());\n }, [createChat, onChosen]);\n\n const deleteRequest = list.deleteRequest && {\n title: list.deleteRequest.title ?? labels.chatListUntitled,\n pending: list.deleting,\n onConfirm: () => void list.confirmDelete(),\n onCancel: list.cancelDelete,\n };\n const error =\n list.status === \"error\"\n ? labels.chatListLoadFailed\n : list.error === \"create\" && list.creationRefusal\n ? list.creationRefusal.message\n : list.error\n ? actionErrorText(labels, list.error)\n : undefined;\n return {\n status: list.status,\n items: list.items.map((item) => {\n const origin = chatOriginOf(item.origin);\n const closed = closureOf(item.closed);\n return { id: item.id, title: item.title, active: item.id === list.activeChatId, ...(origin && { origin }), ...(closed && { closed }) };\n }),\n onSelect,\n onCreate,\n creating: list.creating,\n onRename: (chatId, title) => void list.rename(chatId, title),\n onRequestDelete: list.requestDelete,\n ...(deleteRequest && { deleteRequest }),\n ...(error && { error }),\n ...(list.status === \"error\" && { onRetry: list.reload }),\n };\n}\n\n/** Название открытого чата для шапки; нет названия или чата нет в списке — «Новый чат». */\nexport function activeChatTitle(list: Pick<ChatListState, \"items\" | \"activeChatId\">, labels: ChatLabels): string {\n return list.items.find((item) => item.id === list.activeChatId)?.title ?? labels.chatListUntitled;\n}\n\nfunction actionErrorText(labels: ChatLabels, error: ChatListActionError): string {\n switch (error) {\n case \"create\":\n return labels.chatListCreateFailed;\n case \"rename\":\n return labels.chatListRenameFailed;\n case \"delete\":\n return labels.chatListDeleteFailed;\n }\n}\n\nconst CHAT_ORIGINS: ReadonlySet<string> = new Set<ChatOriginView>([\"telegram\", \"vk\", \"max\"]);\n/** Пометка только для знакомых мессенджеров: незнакомое значение сервера скин не получает. */\nconst chatOriginOf = (origin: string | undefined): ChatOriginView | undefined =>\n origin !== undefined && CHAT_ORIGINS.has(origin) ? (origin as ChatOriginView) : undefined;\n","import { useEffect, useRef } from \"react\";\nimport type { ChatListState } from \"./use-chat-list\";\n\n/** Пока у открытого чата, где идёт ответ, нет названия, список спрашивает сервер так часто (название ставится с первым сообщением). */\nconst TITLE_POLL_MS = 1000;\n\n/**\n * Список чатов следит за открытым чатом: ответ начался или закончился — тихое обновление (порядок и название могли\n * измениться); пока идёт ответ, а названия у чата нет, — опрос раз в секунду. Общее у `ChatWorkspace` и панели агента.\n * `enabled: false` — список не нужен (у чата запрещён `chatList`): ничего не запрашивается.\n */\nexport function useWorkspaceListSync(list: Pick<ChatListState, \"items\" | \"activeChatId\" | \"refresh\">, busy: boolean, enabled = true): void {\n const { items, activeChatId, refresh } = list;\n\n const previousBusy = useRef<boolean | null>(null);\n useEffect(() => {\n const before = previousBusy.current;\n previousBusy.current = busy;\n if (enabled && before !== null && before !== busy) void refresh();\n }, [busy, enabled, refresh]);\n\n const activeItem = items.find((item) => item.id === activeChatId);\n const waitingForTitle = enabled && busy && activeItem !== undefined && activeItem.title === null;\n useEffect(() => {\n if (!waitingForTitle) return;\n const timer = setInterval(() => void refresh(), TITLE_POLL_MS);\n return () => clearInterval(timer);\n }, [refresh, waitingForTitle]);\n}\n"],"mappings":";AAiCA,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,YAAY;AAElB,IAAM,WAAW,oBAAI,IAAI,CAAC,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAExE,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,mBAAmB,oBAAI,IAAI,CAAC,KAAK,GAAI,CAAC;AAE5C,IAAM,WAAW,oBAAI,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC;AASxC,SAAS,UAAU,MAAuB;AACxC,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AACpD,MAAI,KAAK,SAAS,cAAc,SAAS,KAAK,KAAK,KAAK,CAAC,kCAAkC,KAAK,IAAI,EAAG,QAAO;AAC9G,MAAI,KAAK,QAAQ,cAAc,EAAE,EAAE,SAAS,YAAa,QAAO;AAChE,MAAI,KAAK,MAAM,QAAQ,EAAE,SAAS,UAAW,QAAO;AAEpD,MAAI,SAAS,KAAK,YAAY,KAAK,CAAC,iCAAiC,KAAK,IAAI,EAAG,QAAO;AACxF,SAAO,CAAC,SAAS,IAAI,KAAK,YAAY,CAAC;AACzC;AAGA,SAAS,YAAY,MAAc,OAAe,KAAsB;AACtE,QAAM,SAAS,KAAK,QAAQ,CAAC;AAC7B,MAAI,WAAW,UAAa,QAAQ,KAAK,MAAM,EAAG,QAAO;AACzD,QAAM,QAAQ,KAAK,GAAG;AACtB,SAAO,UAAU,UAAa,CAAC,MAAM,SAAS,KAAK;AACrD;AAGA,SAAS,eAAe,MAAc,OAAe,KAAqB;AACxE,MAAI,OAAO;AACX,SAAO,OAAO,KAAK,SAAS,IAAI,KAAK,OAAO,CAAC,CAAE,EAAG;AAElD,MAAI,OAAO,KAAK,gBAAgB,KAAK,KAAK,OAAO,CAAC,CAAE,EAAG,QAAO;AAC9D,MAAI,QAAQ;AACZ,SAAO,QAAQ,KAAK,UAAU,SAAS,IAAI,KAAK,KAAK,CAAE,EAAG;AAC1D,MAAI,SAAS;AACb,SAAO,SAAS,QAAQ,QAAQ,SAAS,QAAQ,OAAO,KAAK,QAAQ,IAAI,MAAM,MAAM,KAAK,MAAM,MAAM,EAAG;AACzG,SAAO;AACT;AAGA,SAAS,SAAS,MAA0B;AAC1C,QAAM,OAAO,IAAI,WAAW,KAAK,MAAM;AACvC,MAAI,QAAiD;AACrD,MAAI,SAAS;AACb,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,OAAO;AACT,WAAK,KAAK,GAAG,QAAQ,OAAO;AAC5B,YAAM,UAAU,4BAA4B,KAAK,IAAI;AACrD,UAAI,WAAW,QAAQ,CAAC,EAAG,CAAC,MAAM,MAAM,QAAQ,QAAQ,CAAC,EAAG,UAAU,MAAM,OAAQ,SAAQ;AAAA,IAC9F,OAAO;AACL,YAAM,UAAU,6BAA6B,KAAK,IAAI;AACtD,UAAI,WAAW,EAAE,QAAQ,CAAC,EAAG,CAAC,MAAM,OAAO,QAAQ,CAAC,EAAG,SAAS,GAAG,IAAI;AACrE,aAAK,KAAK,GAAG,QAAQ,OAAO;AAC5B,gBAAQ,EAAE,MAAM,QAAQ,CAAC,EAAG,CAAC,GAAI,QAAQ,QAAQ,CAAC,EAAG,OAAO;AAAA,MAC9D,OAAO;AACL,uBAAe,MAAM,QAAQ,IAAI;AAAA,MACnC;AAAA,IACF;AACA,aAAS,UAAU;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,eAAe,MAAc,QAAgB,MAAwB;AAC5E,QAAM,SAAS,CAAC,SAAiB;AAC/B,QAAIA,KAAI;AACR,WAAO,KAAKA,EAAC,MAAM,IAAK,CAAAA;AACxB,WAAOA;AAAA,EACT;AACA,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,QAAI,KAAK,CAAC,MAAM,KAAK;AACnB;AACA;AAAA,IACF;AACA,UAAM,UAAU,OAAO,CAAC;AACxB,QAAI,SAAS;AACb,QAAI,WAAW;AACf,WAAO,SAAS,KAAK,QAAQ;AAC3B,UAAI,KAAK,MAAM,MAAM,KAAK;AACxB;AACA;AAAA,MACF;AACA,YAAM,MAAM,OAAO,MAAM;AACzB,UAAI,MAAM,WAAW,UAAU,GAAG;AAChC,mBAAW;AACX;AAAA,MACF;AACA,eAAS;AAAA,IACX;AACA,QAAI,WAAW,GAAG;AAChB,UAAI;AACJ;AAAA,IACF;AACA,SAAK,KAAK,GAAG,SAAS,GAAG,SAAS,QAAQ;AAC1C,QAAI;AAAA,EACN;AACF;AAEA,SAAS,UAAU,MAAc,aAA8B;AAC7D,QAAM,OAAO,SAAS,IAAI;AAC1B,QAAM,QAAgB,CAAC;AACvB,aAAW,SAAS,KAAK,SAAS,SAAS,GAAG;AAC5C,UAAM,QAAQ,MAAM;AACpB,UAAM,MAAM,QAAQ,MAAM,CAAC,EAAE;AAC7B,QAAI,KAAK,KAAK,KAAK,CAAC,UAAU,MAAM,CAAC,CAAE,KAAK,CAAC,YAAY,MAAM,OAAO,GAAG,EAAG;AAC5E,UAAM,OAAO,eAAe,MAAM,OAAO,GAAG;AAC5C,UAAM,KAAK,EAAE,OAAO,QAAQ,MAAM,KAAK,MAAM,MAAM,MAAM,MAAM,CAAC,EAAG,QAAQ,QAAQ,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AAAA,EAChH;AACA,MAAI,aAAa;AACf,UAAM,OAAO,aAAa,KAAK,IAAI;AACnC,QAAI,QAAQ,CAAC,KAAK,KAAK,KAAK,KAAK,YAAY,MAAM,KAAK,OAAO,KAAK,MAAM,KAAK,CAAC,MAAM,KAAK,CAAC,SAAS,KAAK,MAAM,KAAK,KAAK,GAAG;AAC3H,YAAM,KAAK,EAAE,OAAO,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,KAAK,CAAC;AAAA,IAChE;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,gBAAgB,MAAc,IAAoB;AACzD,MAAI,KAAK,EAAE,MAAM,KAAM,QAAO;AAC9B,SAAO,KAAK,EAAE,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AAC1D;AAGA,SAAS,gBAAgB,MAAc,IAAoB;AACzD,SAAO,KAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AACrC;AAGA,SAAS,WAAW,MAAc,OAAyB;AACzD,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,OAAO,GAAG,EAAE;AACzB,UAAM,WAAW,MAAM,GAAG,EAAE;AAC5B,QAAI,QAAQ,YAAY,CAAC,GAAG,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,iBAAiB,IAAI,IAAI,CAAC,EAAG,MAAK,KAAK,IAAI;AAAA,QACxH,QAAO,KAAK,CAAC,IAAI,CAAC;AAAA,EACzB;AACA,SAAO;AACT;AAGO,SAAS,eAAe,MAAc,UAA2B,CAAC,GAAmB;AAC1F,MAAI,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO,EAAE,OAAO,MAAM,MAAM,CAAC,EAAE;AACxD,QAAM,SAAS,WAAW,MAAM,UAAU,MAAM,QAAQ,gBAAgB,IAAI,CAAC;AAC7E,MAAI,OAAO,WAAW,EAAG,QAAO,EAAE,OAAO,MAAM,MAAM,CAAC,EAAE;AACxD,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,QAAM,OAAwB,CAAC;AAC/B,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,CAAC,EAAG;AACxB,UAAM,MAAM,MAAM,GAAG,EAAE,EAAG;AAC1B,QAAI,cAAc;AAClB,WAAO,cAAc,KAAK,iBAAiB,IAAI,KAAK,cAAc,CAAC,CAAE,EAAG;AACxE,QAAI,aAAa;AACjB,WAAO,aAAa,KAAK,UAAU,iBAAiB,IAAI,KAAK,UAAU,CAAE,EAAG;AAC5E,UAAM,iBAAiB,gBAAgB,MAAM,UAAU;AACvD,UAAM,YAAY,gBAAgB,KAAK,KAAK,cAAc,CAAC,MAAM;AACjE,UAAM,UAAU,eAAe,KAAK,UAAU,iBAAiB;AAC/D,UAAM,oBAAoB,aAAa,KAAK,UAAU,iBAAiB,KAAK,KAAK,UAAU,CAAE;AAC7F,UAAM,QAAQ,gBAAgB,SAAS,CAAC,aAAa,gBAAgB,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE;AAE/F,QAAI,UAAU;AACd,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,QAAI,aAAa,SAAS;AAExB,gBAAU;AACV,UAAI,iBAAiB,EAAG,SAAQ,aAAa;AAAA,eACpC,cAAc,EAAG,WAAU,cAAc,gBAAgB,MAAM,WAAW;AAAA,IACrF,WAAW,WAAW,mBAAmB;AACvC,gBAAU;AAAA,IACZ,WAAW,OAAO;AAEhB,UAAI,aAAa,IAAK,SAAQ;AAAA,eACrB,gBAAgB,KAAK,KAAK,GAAG,KAAK,EAAE,EAAG,UAAS;AAAA,IAC3D;AACA,aAAS,KAAK,MAAM,QAAQ,OAAO;AACnC,eAAW,QAAQ,MAAO,KAAI,KAAK,SAAS,KAAM,MAAK,KAAK,EAAE,IAAI,MAAM,QAAQ,MAAM,KAAK,KAAK,CAAC;AACjG,aAAS;AACT,aAAS;AAAA,EACX;AACA,WAAS,KAAK,MAAM,MAAM;AAC1B,SAAO,EAAE,OAAO,KAAK;AACvB;AAGO,SAAS,eAAe,MAAc,UAA2B,CAAC,GAAW;AAClF,SAAO,eAAe,MAAM,OAAO,EAAE;AACvC;;;AC1OA,SAAS,cAAc;AAIhB,IAAM,uBAAuB;AAE7B,SAAS,eAAe,SAAmD;AAChF,SAAO,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,oBAAoB;AACxE;AAMO,SAAS,eAAe,UAAoD;AACjF,QAAM,MAAM,oBAAI,IAAY;AAC5B,MAAI,YAAY;AAChB,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,OAAQ,aAAY,eAAe,OAAO;AAAA,aACtD,QAAQ,SAAS,eAAe,UAAW,KAAI,IAAI,QAAQ,EAAE;AAAA,EACxE;AACA,SAAO;AACT;AAGO,SAAS,kBAAkB,UAA4D;AAC5F,QAAM,WAAW,OAAoB,oBAAI,IAAI,CAAC;AAC9C,QAAM,OAAO,eAAe,QAAQ;AACpC,MAAI,KAAK,SAAS,SAAS,QAAQ,QAAQ,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,QAAQ,IAAI,EAAE,CAAC,EAAG,UAAS,UAAU;AACjH,SAAO,SAAS;AAClB;;;AC9BA,SAAS,eAAe,YAAY,eAAe;AA6zC1C;AAr3BT,IAAM,gBAAgB,EAAE,OAAO,gDAAa,MAAM,sDAAc,KAAK,0CAAY,MAAM,GAAG;AAC1F,SAAS,aAAa,MAA4B;AAChD,MAAI,KAAK,WAAW,UAAW,QAAO,KAAK,QAAQ,iBAAO,KAAK,KAAK,YAAO;AAC3E,SAAO,cAAc,KAAK,MAAM;AAClC;AACA,IAAM,cAAc,CAAC,SAAwB,KAAK,UAAU,SAAS,6DAAgB,KAAK,UAAU,YAAY,gDAAa;AAE7H,SAAS,UAAU,QAAc,WAAW,OAAO,WAAW,OAAe;AAC3E,QAAM,SAAS,CAAC,SAAiB;AAC/B,UAAM,OAAO,IAAI,KAAK,eAAe,SAAS,EAAE,KAAK,WAAW,OAAO,QAAQ,UAAU,KAAK,CAAC,EAAE,OAAO,MAAM;AAC9G,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,GAAG,IAAI,WAAM,IAAI,KAAK,eAAe,SAAS,EAAE,MAAM,WAAW,QAAQ,WAAW,UAAU,KAAK,CAAC,EAAE,OAAO,MAAM,CAAC;AAAA,EAC7H;AACA,MAAI;AACF,WAAO,OAAO,QAAQ;AAAA,EACxB,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAEA,IAAM,cAAc,CAAC,SAAwB,KAAK,WAAW,4DAAe,UAAU,KAAK,UAAU,KAAK,UAAU,KAAK,WAAW,SAAS,CAAC,KAAK;AACnJ,IAAM,YAAY,IAAI,KAAK,eAAe,SAAS,EAAE,KAAK,WAAW,OAAO,QAAQ,MAAM,WAAW,QAAQ,UAAU,CAAC;AAsCxH,IAAM,8BAA8B;AAEpC,IAAI,2BAA2B;AAMxB,SAAS,gBAAgB,QAA0D,OAAkD;AAC1I,MAAI,CAAC,SAAS,CAAC,OAAO,UAAW,QAAO;AACxC,QAAM,QAAQ,OAAO,OAAO,MAAM,IAAI;AACtC,MAAI;AACF,WAAO,OAAO,UAAU,OAAO,KAAK;AAAA,EACtC,SAAS,SAAS;AAChB,QAAI,CAAC,0BAA0B;AAC7B,iCAA2B;AAC3B,cAAQ,MAAM,qNAA0D,OAAO;AAAA,IACjF;AACA,WAAO;AAAA,EACT;AACF;AAGO,SAAS,eAAe,QAA0B,MAAsB,SAAqC;AAClH,MAAI,SAAS,aAAc,QAAO,OAAO,IAAI,KAAK,OAAO;AACzD,QAAM,QAAQ,OAAO;AACrB,SAAO,OAAO,UAAU,WAAW,QAAQ,MAAM,SAAS,qBAAqB,2BAA2B;AAC5G;AAEA,IAAM,gBAAgB,IAAI,KAAK,YAAY,OAAO;AAElD,SAAS,aAAa,OAAe,OAA2D;AAC9F,QAAM,OAAO,cAAc,OAAO,KAAK;AACvC,SAAO,GAAG,KAAK,IAAI,SAAS,QAAQ,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,MAAM,IAAI;AACzF;AAGA,SAAS,gBAAgB,IAAoB;AAC3C,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AACjD,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,SAAO,UAAU,IAAI,GAAG,OAAO,uBAAQ,UAAU,EAAE,wBAAS,GAAG,OAAO;AACxE;AAEA,SAAS,gBAAgB,IAAoB;AAC3C,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AACjD,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,SAAO,UAAU,IAAI,GAAG,OAAO,QAAQ,UAAU,EAAE,OAAO,GAAG,OAAO;AACtE;AAEA,IAAM,iBAA+B;AAAA,EACnC,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ,EAAE,SAAS,0DAAa,mBAAmB,2GAAsB,MAAM,wCAAU,QAAQ,6EAAiB,YAAY,mFAAkB;AAClJ;AAEA,IAAM,eAA2B;AAAA,EAC/B,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,SAAS,CAAC,IAAIC,cAAa,GAAGA,YAAW,yCAAW,0BAAM,IAAI,gBAAgB,EAAE,CAAC;AACnF;AAEA,IAAM,iBAA+B;AAAA,EACnC,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,WAAW,CAAC,cAAe,YAAY,0DAAa,SAAS,KAAK;AAAA,EAClE,MAAM,CAAC,OAAO,WAAY,QAAQ,OAAI,KAAK,SAAM,MAAM,KAAK;AAC9D;AAEA,IAAM,yBAAgD;AAAA,EACpD,UAAU;AAAA,EACV,WAAW,CAAC,UAAU,wHAAyB,KAAK;AAAA,EACpD,eAAe,CAAC,SAAS,wDAAa,IAAI;AAAA,EAC1C,kBAAkB,CAAC,UAAU,4CAAW,KAAK;AAAA,EAC7C,oBAAoB;AACtB;AAGA,IAAM,eAAe,CAAC,WAAoB,cAAc,KAAK,MAAM,IAAI,SAAS,GAAG,MAAM;AAEzF,IAAM,kBAAkC;AAAA,EACtC,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY,CAAC,YAAY,GAAG,OAAO;AAAA,EACnC,mBAAmB;AAAA,EACnB,gBAAgB,CAAC,YAAY,GAAG,OAAO;AAAA,EACvC,uBAAuB;AAAA,EACvB,cAAc,CAAC,QAAQ,UAAU,GAAG,MAAM,QAAK,KAAK;AAAA,EACpD,UAAU;AAAA,EACV,SAAS,CAAC,eAAgB,eAAe,OAAO,+CAAY,kCAAS,KAAK,IAAI,GAAG,KAAK,MAAM,aAAa,GAAI,CAAC,CAAC;AAAA,EAC/G,WAAW,CAAC,UAAU,IAAI,KAAK;AAAA,EAC/B,MAAM;AAAA,EACN,WAAW,CAAC,UAAU,GAAG,cAAc,OAAO,KAAK,MAAM,QAAQ,yCAAW,4CAAS,IAAI,aAAa,OAAO,EAAE,KAAK,sBAAO,KAAK,4BAAQ,MAAM,iCAAQ,CAAC,CAAC;AAAA,EACxJ,eAAe,CAAC,SAAS,wDAAa,IAAI;AAAA,EAC1C,YAAY,CAAC,SAAS,wDAAa,IAAI;AACzC;AAGO,IAAM,wBAAwC;AAAA,EACnD,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY,CAAC,YAAY,GAAG,OAAO;AAAA,EACnC,mBAAmB;AAAA,EACnB,gBAAgB,CAAC,YAAY,GAAG,OAAO;AAAA,EACvC,uBAAuB;AAAA,EACvB,cAAc,CAAC,QAAQ,UAAU,GAAG,MAAM,QAAK,KAAK;AAAA,EACpD,UAAU;AAAA,EACV,SAAS,CAAC,eAAgB,eAAe,OAAO,YAAY,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,aAAa,GAAI,CAAC,CAAC;AAAA,EACrH,WAAW,CAAC,UAAU,IAAI,KAAK;AAAA,EAC/B,MAAM;AAAA,EACN,WAAW,CAAC,UAAU,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,EAChE,eAAe,CAAC,SAAS,gBAAW,IAAI;AAAA,EACxC,YAAY,CAAC,SAAS,gBAAW,IAAI;AACvC;AAGO,IAAM,oBAAuH;AAAA,EAClI,iBAAiB;AAAA,EACjB,SAAS;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ,EAAE,SAAS,iBAAY,mBAAmB,wBAAwB,MAAM,QAAQ,QAAQ,UAAU,YAAY,4BAA4B;AAAA,EACpJ;AAAA,EACA,OAAO;AAAA,IACL,QAAQ;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,aAAa;AAAA,MACb,WAAW;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS,CAAC,IAAIA,cAAa,GAAGA,YAAW,SAAS,SAAS,IAAI,gBAAgB,EAAE,CAAC;AAAA,EACpF;AAAA,EACA,SAAS;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,WAAW,CAAC,cAAe,YAAY,UAAU,SAAS,KAAK;AAAA,IAC/D,MAAM,CAAC,OAAO,WAAY,QAAQ,SAAI,KAAK,WAAM,MAAM,KAAK;AAAA,EAC9D;AAAA,EACA,iBAAiB;AAAA,IACf,UAAU;AAAA,IACV,WAAW,CAAC,UAAU,iBAAiB,KAAK;AAAA,IAC5C,eAAe,CAAC,SAAS,gBAAW,IAAI;AAAA,IACxC,kBAAkB,CAAC,UAAU,cAAS,KAAK;AAAA,IAC3C,oBAAoB;AAAA,EACtB;AACF;AAEO,IAAM,gBAAoC;AAAA,EAC/C,QAAQ;AAAA,EACR,SAAS,EAAE,OAAO,yEAAkB,aAAa,sLAAqC;AAAA,EACtF,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,kBAAkB,EAAE,QAAQ,EAAE,KAAK,4BAAQ,IAAI,sBAAO,SAAS,8CAAW,KAAK,kCAAS,QAAQ,wCAAU,MAAM,wCAAU,KAAK,mDAAW,GAAG,OAAO,wCAAU,WAAW,sEAAe,OAAO,sEAAe,UAAU,+FAAoB,MAAM,wCAAU,KAAK,kCAAS,QAAQ,uEAAgB,eAAe,kHAAwB,SAAS,wCAAU,MAAM,4BAAQ,OAAO,oDAAY,YAAY,+FAAoB,aAAa,4GAAuB,QAAQ,wCAAU,WAAW,gGAAqB,OAAO,0GAA0B,OAAO,4BAAQ,QAAQ,iCAAQ;AAAA,EACnjB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,eAAe,CAAC,YAAY,kCAAS,OAAO;AAAA,EAC5C,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,YAAY;AAC5B,QAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,UAAM,OAAO,UAAU;AACvB,WAAO,SAAS,IAAI,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,wBAAS,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,uBAAQ,IAAI;AAAA,EACjG;AAAA,EACA,aAAa,CAAC,aAAa,GAAG,QAAQ;AAAA,EACtC,UAAU,CAAC,aAAa,GAAG,QAAQ;AAAA,EACnC,WAAW,CAAC,aAAa,GAAG,QAAQ;AAAA,EACpC,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,oBAAoB;AAAA,IACpB,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB;AAAA,EACA,iBAAiB;AAAA,IACf,KAAK;AAAA,IACL,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA,EACA,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,gBAAgB,CAAC,UAAU,UAAU,8CAAW,QAAQ,iBAAO,KAAK;AAAA,EACpE,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,IACV,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,eAAe;AAAA,IACf,WAAW;AAAA,IACX,cAAc;AAAA,IACd,iBAAiB;AAAA,EACnB;AAAA,EACA,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,IACX,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,OAAO;AAAA,IACP,cAAc;AAAA,IACd,YAAY,CAAC,YACX,iJAA8B,UAAU,OAAO,IAAI,aAAa,UAAU,IAAI,EAAE,KAAK,wCAAU,KAAK,wCAAU,MAAM,iCAAQ,CAAC,IAAI,aAAa,SAAS,EAAE,KAAK,8CAAW,KAAK,8CAAW,MAAM,uCAAS,CAAC,CAAC;AAAA,IAC5M,OAAO;AAAA,IACP,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,IACX,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,MACN,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,MACpB,SAAS;AAAA,MACT,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,oBAAoB;AAAA,MACpB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,IACA,iBAAiB,CAAC,eAAe,oIAA6B,UAAU;AAAA,EAC1E;AAAA,EACA,mBAAmB,CAAC,WAAW,kEAAgB,MAAM;AAAA,EACrD,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,iBAAiB,CAAC,SAAS;AACzB,UAAM,UAAU,KAAK,IAAI,IAAI,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,IAAI,CAAC;AACvE,WAAO,4EAAgB,OAAO,yCAAW,YAAY,IAAI,CAAC,GAAG,aAAa,IAAI,CAAC;AAAA,EACjF;AAAA,EACA,mBAAmB,CAAC,SAAS;AAC3B,QAAI,KAAK,WAAW,UAAW,QAAO;AACtC,QAAI,KAAK,WAAW,UAAW,QAAO,uCAAS,YAAY,IAAI,CAAC,GAAG,aAAa,IAAI,CAAC,gIAA4B,YAAY,IAAI,CAAC;AAClI,WAAO,iCAAQ,YAAY,IAAI,CAAC,GAAG,aAAa,IAAI,CAAC,oDAAY,YAAY,IAAI,CAAC;AAAA,EACpF;AAAA,EACA,sBAAsB,CAAC,cAAc,oDAAY,aAAa,WAAW,EAAE,KAAK,0DAAa,KAAK,0DAAa,MAAM,yDAAY,CAAC,CAAC;AAAA,EACnI,2BAA2B,CAAC,SAAS,yIAA2B,YAAY,IAAI,CAAC;AAAA,EACjF,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,gBAAgB,EAAE,UAAU,YAAY,IAAI,0DAAa,KAAK,MAAM;AAAA,EACpE,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,iBAAiB,CAAC,UAAU,8FAAqB,KAAK;AAAA,EACtD,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,oBAAoB,CAAC,UAAU,0BAAQ,KAAK;AAAA,EAC5C,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,aAAa,CAAC,OAAO,UAAU,+CAAY,KAAK,iBAAO,KAAK;AAAA,EAC5D,iBAAiB,CAAC,OAAO,UAAU,0FAAoB,KAAK,iBAAO,KAAK;AAAA,EACxE,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,gBAAgB,CAAC,YAAY,6FAAoB,OAAO;AAAA,EACxD,kBAAkB,CAAC,YAAY,uFAAmB,OAAO;AAAA,EACzD,kBAAkB,CAAC,MAAM,UAAU,GAAG,IAAI,iDAAc,KAAK;AAAA,EAC7D,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,mBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,aAAa;AAAA,IACb,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB;AAAA,EACA,cAAc;AAAA,IACZ,WAAW;AAAA,IACX,eAAe;AAAA,IACf,eAAe;AAAA,IACf,WAAW;AAAA,IACX,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,OAAO;AAAA,EACT;AAAA,EACA,mBAAmB;AAAA,EACnB,kBAAkB,CAAC,YAAY,4CAAW,OAAO;AAAA,EACjD,kBAAkB;AAAA,EAClB,iBAAiB,CAAC,UAAU,4DAAe,KAAK;AAAA,EAChD,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,iBAAiB,CAAC,eAAe,UAAU,OAAO,UAAU;AAAA,EAC5D,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,sBAAsB,CAAC,SAAS,2FAAqB,IAAI;AAAA,EACzD,0BAA0B;AAAA,EAC1B,oBAAoB;AAAA,IAClB,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB,2BAA2B;AAAA,EAC3B,SAAS;AAAA,IACP,mBAAmB;AAAA,IACnB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa,CAAC,OAAO,UAAU,wCAAU,KAAK,iBAAO,KAAK;AAAA,IAC1D,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,cAAc;AAAA,IACd,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa,CAAC,KAAK,QAAS,QAAQ,MAAM,oDAAY,GAAG,KAAK,iEAAe,GAAG,iBAAO,GAAG;AAAA,IAC1F,gBAAgB,CAAC,KAAK,QACpB,QAAQ,UAAa,QAAQ,SAAY,uEAAgB,GAAG,iBAAO,GAAG,KAAK,QAAQ,SAAY,uEAAgB,GAAG,KAAK,uEAAgB,GAAG;AAAA,IAC5I,eAAe;AAAA,IACf,aAAa;AAAA,IACb,aAAa,CAAC,QAAQ,2DAAc,GAAG;AAAA,IACvC,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EACtB;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,MAAM,CAAC,SAAS,UAAK,IAAI;AAAA,IACzB,YAAY;AAAA,EACd;AAAA,EACA,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,IACV,KAAK;AAAA,IACL,SAAS,CAAC,UAAU,UAAU,GAAG,QAAQ,iBAAO,KAAK;AAAA,EACvD;AAAA,EACA,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,eAAe,CAAC,UAAU,YAAY,GAAG,QAAQ,sDAAc,QAAQ,KAAK,IAAI,CAAC;AAAA,IACjF,gBAAgB,CAAC,UAAU,GAAG,QAAQ,GAAG,KAAK,OAAO,EAAE;AAAA,EACzD;AACF;AAEA,IAAM,uBAAuB,EAAE,OAAO,kBAAkB,MAAM,iBAAiB,KAAK,gBAAgB,MAAM,GAAG;AAC7G,SAAS,oBAAoB,MAA4B;AACvD,MAAI,KAAK,WAAW,UAAW,QAAO,KAAK,QAAQ,QAAQ,KAAK,KAAK,OAAO;AAC5E,SAAO,qBAAqB,KAAK,MAAM;AACzC;AACA,IAAM,qBAAqB,CAAC,SAAwB,KAAK,UAAU,SAAS,kBAAkB,KAAK,UAAU,YAAY,oBAAoB;AAC7I,SAAS,iBAAiB,QAAc,WAAW,OAAO,WAAW,OAAe;AAClF,QAAM,SAAS,CAAC,SACd,IAAI,KAAK,eAAe,SAAS,EAAE,KAAK,WAAW,OAAO,QAAQ,UAAU,MAAM,GAAI,YAAY,EAAE,MAAM,WAAW,QAAQ,UAAU,EAAG,CAAC,EAAE,OAAO,MAAM;AAC5J,MAAI;AACF,WAAO,OAAO,QAAQ;AAAA,EACxB,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AACA,IAAM,qBAAqB,CAAC,SAAwB,KAAK,WAAW,YAAY,iBAAiB,KAAK,UAAU,KAAK,UAAU,KAAK,WAAW,SAAS,CAAC,KAAK;AAC9J,IAAM,eAAe,CAAC,OAAe,KAAa,SAAiB,GAAG,KAAK,IAAI,UAAU,IAAI,MAAM,IAAI;AACvG,IAAM,mBAAmB,IAAI,KAAK,eAAe,SAAS,EAAE,KAAK,WAAW,OAAO,QAAQ,MAAM,WAAW,QAAQ,UAAU,CAAC;AAGxH,IAAM,gBAAoC;AAAA,EAC/C,QAAQ;AAAA,EACR,SAAS,EAAE,OAAO,yBAAyB,aAAa,qCAAqC;AAAA,EAC7F,GAAG;AAAA,EACH,UAAU;AAAA,EACV,kBAAkB,EAAE,QAAQ,EAAE,KAAK,OAAO,IAAI,MAAM,SAAS,WAAW,KAAK,OAAO,QAAQ,UAAU,MAAM,QAAQ,KAAK,MAAM,GAAG,OAAO,SAAS,WAAW,aAAa,OAAO,SAAS,UAAU,qBAAqB,MAAM,QAAQ,KAAK,OAAO,QAAQ,cAAc,eAAe,kBAAkB,SAAS,WAAW,MAAM,QAAQ,OAAO,SAAS,YAAY,cAAc,aAAa,mBAAmB,QAAQ,UAAU,WAAW,cAAc,OAAO,0BAA0B,OAAO,SAAS,QAAQ,SAAS;AAAA,EACngB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,eAAe,CAAC,YAAY,eAAe,OAAO;AAAA,EAClD,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,YAAY;AAC5B,QAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,UAAM,OAAO,UAAU;AACvB,WAAO,SAAS,IAAI,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,SAAS,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,QAAQ,IAAI;AAAA,EACjG;AAAA,EACA,aAAa,CAAC,aAAa,GAAG,QAAQ;AAAA,EACtC,UAAU,CAAC,aAAa,GAAG,QAAQ;AAAA,EACnC,WAAW,CAAC,aAAa,GAAG,QAAQ;AAAA,EACpC,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,oBAAoB;AAAA,IACpB,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB;AAAA,EACA,iBAAiB,EAAE,KAAK,OAAO,IAAI,MAAM,SAAS,WAAW,KAAK,OAAO,QAAQ,UAAU,MAAM,QAAQ,KAAK,MAAM;AAAA,EACpH,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,gBAAgB,CAAC,UAAU,UAAU,WAAW,QAAQ,OAAO,KAAK;AAAA,EACpE,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,IACV,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,eAAe;AAAA,IACf,WAAW;AAAA,IACX,cAAc;AAAA,IACd,iBAAiB;AAAA,EACnB;AAAA,EACA,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,IACX,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,OAAO;AAAA,IACP,cAAc;AAAA,IACd,YAAY,CAAC,YACX,mCAAmC,UAAU,OAAO,IAAI,aAAa,UAAU,IAAI,UAAU,SAAS,IAAI,aAAa,SAAS,UAAU,SAAS,CAAC;AAAA,IACtJ,OAAO;AAAA,IACP,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,IACX,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,MACN,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,MACN,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,MACpB,SAAS;AAAA,MACT,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,oBAAoB;AAAA,MACpB,OAAO;AAAA,MACP,cAAc;AAAA,MACd,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,IACA,iBAAiB,CAAC,eAAe,uCAAkC,UAAU;AAAA,EAC/E;AAAA,EACA,mBAAmB,CAAC,WAAW,uBAAuB,MAAM;AAAA,EAC5D,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,iBAAiB,CAAC,SAAS;AACzB,UAAM,UAAU,KAAK,IAAI,IAAI,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,IAAI,CAAC;AACvE,WAAO,GAAG,OAAO,iBAAiB,mBAAmB,IAAI,CAAC,GAAG,oBAAoB,IAAI,CAAC;AAAA,EACxF;AAAA,EACA,mBAAmB,CAAC,SAAS;AAC3B,QAAI,KAAK,WAAW,UAAW,QAAO;AACtC,QAAI,KAAK,WAAW,UAAW,QAAO,+BAA+B,mBAAmB,IAAI,CAAC,GAAG,oBAAoB,IAAI,CAAC,mBAAmB,mBAAmB,IAAI,CAAC;AACpK,WAAO,YAAY,mBAAmB,IAAI,CAAC,GAAG,oBAAoB,IAAI,CAAC,cAAc,mBAAmB,IAAI,CAAC;AAAA,EAC/G;AAAA,EACA,sBAAsB,CAAC,cAAc,GAAG,aAAa,WAAW,WAAW,UAAU,CAAC;AAAA,EACtF,2BAA2B,CAAC,SAAS,+BAA+B,mBAAmB,IAAI,CAAC;AAAA,EAC5F,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,gBAAgB,EAAE,UAAU,YAAY,IAAI,MAAM,KAAK,MAAM;AAAA,EAC7D,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,iBAAiB,CAAC,UAAU,0BAAqB,KAAK;AAAA,EACtD,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,oBAAoB,CAAC,UAAU,cAAS,KAAK;AAAA,EAC7C,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,aAAa,CAAC,OAAO,UAAU,UAAU,KAAK,OAAO,KAAK;AAAA,EAC1D,iBAAiB,CAAC,OAAO,UAAU,uBAAuB,KAAK,OAAO,KAAK;AAAA,EAC3E,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,gBAAgB,CAAC,YAAY,mBAAc,OAAO;AAAA,EAClD,kBAAkB,CAAC,YAAY,qBAAgB,OAAO;AAAA,EACtD,kBAAkB,CAAC,MAAM,UAAU,GAAG,IAAI,YAAY,KAAK;AAAA,EAC3D,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,mBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,aAAa;AAAA,IACb,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,EAClB;AAAA,EACA,cAAc;AAAA,IACZ,WAAW;AAAA,IACX,eAAe;AAAA,IACf,eAAe;AAAA,IACf,WAAW;AAAA,IACX,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,OAAO;AAAA,EACT;AAAA,EACA,mBAAmB;AAAA,EACnB,kBAAkB,CAAC,YAAY,cAAS,OAAO;AAAA,EAC/C,kBAAkB;AAAA,EAClB,iBAAiB,CAAC,UAAU,cAAc,KAAK;AAAA,EAC/C,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,iBAAiB,CAAC,eAAe,iBAAiB,OAAO,UAAU;AAAA,EACnE,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,sBAAsB,CAAC,SAAS,SAAS,IAAI;AAAA,EAC7C,0BAA0B;AAAA,EAC1B,oBAAoB;AAAA,IAClB,eAAe;AAAA,IACf,WAAW;AAAA,EACb;AAAA,EACA,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB,2BAA2B;AAAA,EAC3B,SAAS;AAAA,IACP,mBAAmB;AAAA,IACnB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa,CAAC,OAAO,UAAU,UAAU,KAAK,OAAO,KAAK;AAAA,IAC1D,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,cAAc;AAAA,IACd,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa,CAAC,KAAK,QAAS,QAAQ,MAAM,QAAQ,GAAG,KAAK,QAAQ,GAAG,OAAO,GAAG;AAAA,IAC/E,gBAAgB,CAAC,KAAK,QACpB,QAAQ,UAAa,QAAQ,SAAY,gBAAgB,GAAG,OAAO,GAAG,KAAK,QAAQ,SAAY,gBAAgB,GAAG,KAAK,iBAAiB,GAAG;AAAA,IAC7I,eAAe;AAAA,IACf,aAAa;AAAA,IACb,aAAa,CAAC,QAAQ,SAAS,GAAG;AAAA,IAClC,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EACtB;AAAA,EACA,SAAS,EAAE,SAAS,6BAAwB,QAAQ,oDAA+C,MAAM,eAAe;AAAA,EACxH,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ,EAAE,SAAS,4BAAuB,QAAQ,iCAA4B,SAAS,oBAAoB,MAAM,aAAa;AAAA,EAC9H,QAAQ,EAAE,MAAM,eAAe,MAAM,CAAC,SAAS,QAAQ,IAAI,IAAI,YAAY,cAAc;AAAA,EACzF,QAAQ,EAAE,OAAO,SAAS,MAAM,QAAQ,UAAU,YAAY,KAAK,iBAAiB,SAAS,CAAC,UAAU,UAAU,GAAG,QAAQ,OAAO,KAAK,GAAG;AAAA,EAC5I,OAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,eAAe,CAAC,UAAU,YAAY,GAAG,QAAQ,aAAa,QAAQ,KAAK,IAAI,CAAC;AAAA,IAChF,gBAAgB,CAAC,UAAU,GAAG,QAAQ,GAAG,KAAK,OAAO,EAAE;AAAA,EACzD;AACF;AAOO,IAAM,yBAAyB;AAAA,EACpC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,eAAe;AACjB;AAGA,IAAM,eAAe,CAAC,YAAoB,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,IAAI,OAAO,UAAU,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAE9G,IAAM,gBAAgB,cAAkC,aAAa;AAE9D,SAAS,mBAAmB,EAAE,QAAQ,SAAS,GAAgD;AACpG,QAAM,WAAW;AAAA,IACf,OAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,EAAE,IAAI,OAAO,QAAQ,WAAW,IAAI,IAAI,gBAAgB,eAAe,SAAS,GAAG,OAAO,QAAQ;AAAA,MAC3G,kBAAkB,EAAE,IAAI,OAAO,QAAQ,WAAW,IAAI,IAAI,gBAAgB,eAAe,kBAAkB,GAAG,OAAO,iBAAiB;AAAA,MACtI,mBAAmB,OAAO,qBAAqB,cAAc;AAAA,MAC7D,mBAAmB,OAAO,qBAAqB,cAAc;AAAA,MAC7D,cAAc,OAAO,gBAAgB,cAAc;AAAA,MACnD,mBAAmB,OAAO,qBAAqB,cAAc;AAAA,MAC7D,kBAAkB,OAAO,oBAAoB,cAAc;AAAA,MAC3D,YAAY,OAAO,cAAc,cAAc;AAAA,MAC/C,iBAAiB,OAAO,mBAAmB,cAAc;AAAA,MACzD,sBAAsB,OAAO,wBAAwB,cAAc;AAAA,MACnE,2BAA2B,OAAO,6BAA6B,cAAc;AAAA,MAC7E,kBAAkB,OAAO,oBAAoB,cAAc;AAAA,MAC3D,iBAAiB,OAAO,oBAAoB,OAAO,qBAAqB,cAAc,mBAAmB,cAAc,kBAAkB,CAAC;AAAA,MAC1I,iBAAiB,OAAO,oBAAoB,OAAO,qBAAqB,cAAc,mBAAmB,cAAc,kBAAkB;AAAA,MACzI,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,OAAO,QAAQ;AAAA,MACvD,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,OAAO,QAAQ;AAAA,MACvD,MAAM,EAAE,GAAG,cAAc,MAAM,GAAG,OAAO,KAAK;AAAA,MAC9C,QAAQ,EAAE,GAAG,cAAc,QAAQ,GAAG,OAAO,OAAO;AAAA,MACpD,QAAQ,EAAE,GAAG,cAAc,QAAQ,GAAG,OAAO,OAAO;AAAA,MACpD,QAAQ,EAAE,GAAG,cAAc,QAAQ,GAAG,OAAO,OAAO;AAAA,MACpD,OAAO,EAAE,GAAG,cAAc,OAAO,GAAG,OAAO,MAAM;AAAA;AAAA,MAEjD,aAAa,EAAE,IAAI,OAAO,QAAQ,WAAW,IAAI,IAAI,gBAAgB,eAAe,aAAa,GAAG,OAAO,YAAY;AAAA,MACvH,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,OAAO,QAAQ;AAAA,MACvD,OAAO,EAAE,GAAG,cAAc,OAAO,GAAG,OAAO,MAAM;AAAA,MACjD,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,OAAO,QAAQ;AAAA,MACvD,iBAAiB,EAAE,GAAG,cAAc,iBAAiB,GAAG,OAAO,gBAAgB;AAAA,MAC/E,UAAU,EAAE,GAAG,cAAc,UAAU,GAAG,OAAO,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AACA,SAAO,oBAAC,cAAc,UAAd,EAAuB,OAAO,UAAW,UAAS;AAC5D;AAEO,SAAS,gBAAoC;AAClD,SAAO,WAAW,aAAa;AACjC;;;ACrzCO,IAAM,qBAAqB;AAGlC,IAAM,gBAAgB;AAEtB,SAAS,YAAY,MAAc,OAAwB;AACzD,QAAM,UAAU,KAAK,KAAK;AAC1B,QAAM,SAAS,KAAK,SAAS,KAAK,UAAU,EAAE;AAC9C,SAAO,UAAU,KAAK,QAAQ,UAAU,MAAM,UAAU,YAAY,MAAM,CAAC,EAAG,OAAO,QAAQ,MAAM;AACrG;AAGA,IAAM,oBAAoB,CAAC,SAAiB,KAAK,QAAQ,kBAAkB,EAAE,EAAE,QAAQ,kBAAkB,EAAE;AAE3G,SAAS,YAAY,UAA8B,OAAoD;AACrG,MAAI,aAAa,aAAa,MAAM,SAAU,QAAO;AACrD,MAAI,aAAa,sBAAsB,MAAM,MAAO,QAAO;AAC3D,SAAO;AACT;AAOO,SAAS,kBAAkB,UAAkB,OAA0C;AAC5F,QAAM,WAA4B,CAAC;AACnC,MAAI,QAAkB,CAAC;AACvB,MAAI,aAA4B;AAChC,MAAI,QAA+G;AAEnH,QAAM,aAAa,MAAM;AACvB,UAAM,OAAO,kBAAkB,MAAM,KAAK,IAAI,CAAC;AAC/C,QAAI,KAAK,KAAK,EAAG,UAAS,KAAK,EAAE,MAAM,YAAY,UAAU,KAAK,CAAC;AACnE,YAAQ,CAAC;AAAA,EACX;AACA,QAAM,cAAc,CAAC,WAAoB;AACvC,QAAI,CAAC,MAAO;AACZ,aAAS,KAAK,EAAE,MAAM,MAAM,MAAM,QAAQ,MAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,UAAU,MAAM,MAAM,KAAK,IAAI,EAAE,CAAC;AAC7G,YAAQ;AAAA,EACV;AAEA,aAAW,QAAQ,SAAS,MAAM,IAAI,GAAG;AACvC,QAAI,OAAO;AACT,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,YAAY,MAAM,MAAM,KAAK,EAAG,aAAY,IAAI;AAAA,UAC/C,OAAM,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,QAAQ,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC;AAC5E;AAAA,IACF;AACA,QAAI,YAAY;AACd,YAAM,KAAK,IAAI;AACf,UAAI,YAAY,MAAM,UAAU,EAAG,cAAa;AAChD;AAAA,IACF;AACA,UAAM,UAAU,cAAc,KAAK,IAAI;AACvC,QAAI,CAAC,SAAS;AACZ,YAAM,KAAK,IAAI;AACf;AAAA,IACF;AACA,UAAM,CAAC,EAAE,SAAS,IAAI,QAAQ,IAAI,OAAO,EAAE,IAAI;AAC/C,UAAM,OAAO,YAAY,KAAK,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,GAAG,YAAY,GAAG,KAAK;AAC1E,QAAI,QAAQ,EAAE,MAAM,WAAW,GAAM,KAAK,KAAK,SAAS,GAAM,IAAI;AAChE,iBAAW;AACX,cAAQ,EAAE,MAAM,OAAO,QAAQ,OAAO,QAAQ,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,EAAE;AAAA,IAC1E,OAAO;AACL,mBAAa;AACb,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,cAAY,KAAK;AACjB,aAAW;AACX,SAAO;AACT;;;ACxEO,SAAS,mBAAmB,UAAqC;AACtE,SAAO,kBAAkB,UAAU,EAAE,UAAU,KAAK,CAAC;AACvD;;;ACFO,IAAM,sBAAsB,KAAK;AAEjC,IAAM,uBAAuB;AAE7B,IAAM,oBAAoB;AAE1B,IAAM,2BAA2B;AAEjC,IAAM,yBAAyB;AAE/B,IAAM,6BAA6B;AAGnC,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAKJ,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAGJ,IAAM,uBAAiE;AAAA,EAC5E,OAAO,EAAE,YAAY,WAAW,SAAS,WAAW,MAAM,WAAW,OAAO,WAAW,QAAQ,WAAW,QAAQ,WAAW,UAAU,WAAW,MAAM,MAAM;AAAA,EAC9J,MAAM,EAAE,YAAY,WAAW,SAAS,WAAW,MAAM,WAAW,OAAO,WAAW,QAAQ,WAAW,QAAQ,WAAW,UAAU,WAAW,MAAM,KAAK;AAC9J;AAEA,IAAM,YAAiE;AAAA,EACrE,CAAC,QAAQ,YAAY;AAAA,EACrB,CAAC,aAAa,SAAS;AAAA,EACvB,CAAC,UAAU,MAAM;AAAA,EACjB,CAAC,WAAW,OAAO;AAAA,EACnB,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,YAAY,QAAQ;AAAA,EACrB,CAAC,eAAe,UAAU;AAC5B;AAGA,IAAM,aAAa;AAEnB,SAAS,SAAS,QAAgC;AAChD,QAAM,WAAW,qBAAqB,OAAO,OAAO,SAAS,OAAO;AACpE,QAAM,YAAY,UAAU,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;AAC/C,UAAM,QAAQ,OAAO,GAAG;AACxB,WAAO,GAAG,IAAI,IAAI,WAAW,KAAK,KAAK,KAAK,CAAC,MAAM,SAAS,IAAI,IAAI,QAAQ,SAAS,GAAG,CAAC;AAAA,EAC3F,CAAC,EAAE,KAAK,GAAG;AACX,SACE,SAAS,SAAS,iBAAiB,OAAO,OAAO,SAAS,OAAO;AAKrE;AAOA,IAAM,aAAa,0RAA0R,oBAAoB,iIAAiI,wBAAwB;AAM1d,IAAM,cAAc,CAAC,UAAU,SAAS,UAAU,OAAO;AACzD,IAAM,kBAAkB,CAAC,cAAc,YAAY,gBAAgB,WAAW,iBAAiB,WAAW;AAO1G,IAAM,uBACJ,oBAAoB,KAAK,UAAU,WAAW,CAAC,SAAS,KAAK,UAAU,eAAe,CAAC;AAmBzF,SAAS,qBAAqB,cAA8B;AAC1D,MAAI,OAAO,cAAc,eAAe,CAAC,8CAA8C,KAAK,YAAY,EAAG,QAAO;AAClH,QAAM,SAAS,IAAI,UAAU,EAAE,gBAAgB,cAAc,WAAW;AACxE,MAAI,UAAU;AACd,QAAM,QAAQ,CAAC,SAAqB;AAClC,eAAW,WAAW,KAAK,iBAAiB,YAAY,KAAK,GAAG,CAAC,GAAG;AAClE,cAAQ,OAAO;AACf,gBAAU;AAAA,IACZ;AACA,eAAW,QAAQ,KAAK,iBAAiB,MAAM,GAAG;AAChD,UAAI,EAAE,KAAK,aAAa,KAAK,KAAK,IAAI,YAAY,EAAE,MAAM,KAAK,EAAE,KAAK,CAAC,QAAQ,gBAAgB,SAAS,GAAG,CAAC,EAAG;AAC/G,WAAK,OAAO;AACZ,gBAAU;AAAA,IACZ;AACA,eAAW,YAAY,KAAK,iBAAiB,UAAU,EAAG,OAAM,SAAS,OAAO;AAAA,EAClF;AACA,QAAM,MAAM;AACZ,SAAO,UAAU,kBAAkB,OAAO,gBAAgB,SAAS,KAAK;AAC1E;AAEA,IAAM,kBAAkB;AAMxB,SAAS,YAAY,OAAuB;AAC1C,QAAM,UAAU,CAAC,MAAc,QAAQ,OAAO,6BAA6B,IAAI,IAAI,KAAK;AACxF,SACE,4EAA4E,iBAAiB,qJACM,QAAQ,sBAAsB,CAAC,qIAE1F,wBAAwB,iCAAiC,QAAQ,0BAA0B,kBAAkB,CAAC,eAE1I,KAAK,UAAU,KAAK,EAAE,QAAQ,MAAM,SAAS,CAAC;AAE9D;AAMO,SAAS,iBAAiB,QAAgB,QAAgC;AAC/E,QAAM,SAAS,OAAO,OAAO,SAAS;AACtC,SACE,oDAAoD,eAAe,uDAAuD,mBAAmB,+BAChH,MAAM,qIACX,YAAY,sBAAsB,QAAQ,MAAM,CAAC,CAAC;AAE9E;AAOO,SAAS,sBAAsB,QAAgB,QAAgC;AACpF,SAAO;AAAA,IACL,oDAAoD,eAAe,uDAAuD,aAAa,+EACxD,SAAS,MAAM,CAAC,mBAAmB,UAAU,oBAC/G,oBAAoB,yBAAyB,MAAM;AAAA,EAClE;AACF;AAGO,SAAS,aAAa,QAAyB;AACpD,SAAO,OAAO,UAAU,sBAAsB,KAAK,IAAI,YAAY,EAAE,OAAO,MAAM,EAAE,UAAU;AAChG;AAGO,SAAS,aAAa,OAA8C,aAAiD;AAC1H,MAAI,CAAC,eAAe,MAAM,WAAW,YAAa,QAAO;AACzD,QAAM,OAAgB,MAAM;AAC5B,SAAO,OAAO,SAAS,YAAY,SAAS,QAAS,KAA4B,SAAS;AAC5F;AAMO,SAAS,aAAa,OAA8C,aAAuD;AAChI,MAAI,CAAC,eAAe,MAAM,WAAW,YAAa,QAAO;AACzD,QAAM,OAAgB,MAAM;AAC5B,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,EAAE,MAAM,OAAO,IAAI;AACzB,MAAI,SAAS,4BAA4B,OAAO,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACxG,SAAO,SAAS,KAAK,UAAU,uBAAuB,KAAK,KAAK,MAAM,IAAI;AAC5E;;;ACjNO,IAAM,uBAAuB;AAG7B,SAAS,kBAAiD,OAAgC;AAC/F,MAAI,CAAC,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,oBAAoB,EAAG,QAAO;AACtE,QAAM,OAAe,CAAC;AACtB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,sBAAsB;AACtC,WAAK,KAAK,IAAI;AACd;AAAA,IACF;AACA,UAAM,YAAY,KAAK,cAAc,CAAC,cAAc,UAAU,SAAS,YAAY;AACnF,QAAI,cAAc,GAAI,MAAK,SAAS;AAAA,EACtC;AACA,SAAO;AACT;AAGO,SAAS,qBAAyE,UAAgC;AACvH,MAAI,UAAU;AACd,QAAM,QAAQ,SAAS,IAAI,CAAC,YAAY;AACtC,UAAM,QAAQ,kBAAkB,QAAQ,KAAK;AAC7C,QAAI,UAAU,QAAQ,MAAO,QAAO;AACpC,cAAU;AACV,WAAO,EAAE,GAAG,SAAS,MAAM;AAAA,EAC7B,CAAC;AACD,SAAO,UAAU,QAAQ;AAC3B;;;AC/BA,SAAS,iBAAAC,gBAAe,aAAa,cAAAC,aAAY,4BAA4B;AAKtE,IAAM,sBAAsB;AACnC,IAAM,QAAyB,OAAO,OAAO,CAAC,CAAC;AAG/C,IAAM,iBAAiB,CAAC,SAA0B,KAAK,WAAW,GAAG;AAErE,IAAM,WAAW,CAAC,UAA6C,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAG3H,SAAS,cAAc,UAAsF;AAClH,MAAI;AACJ,aAAW,WAAW,UAAU;AAC9B,eAAW,QAAQ,QAAQ,OAAO;AAChC,UAAI,KAAK,SAAS,oBAAqB;AACvC,YAAM,SAAU,KAA4B;AAC5C,UAAI,CAAC,SAAS,MAAM,EAAG;AACvB,iBAAW,CAAC;AACZ,kBAAY,QAAQ,MAAM;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,QAAyB,QAA+B;AAC3E,QAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC;AAC5D,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,YAAY,eAAe,IAAI,EAAG;AACtD,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,SAAkB;AACtB,eAAW,OAAO,KAAK,MAAM,GAAG,EAAE,EAAG,UAAS,SAAS,MAAM,KAAK,OAAO,OAAO,QAAQ,GAAG,IAAI,OAAO,GAAG,IAAI;AAC7G,QAAI,SAAS,MAAM,EAAG,QAAO,OAAO,KAAK,GAAG,EAAE,CAAE;AAAA,EAClD;AACA,QAAM,MAAM,SAAS,OAAO,GAAG,IAAI,OAAO,MAAM,CAAC;AACjD,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC/C,QAAI,eAAe,IAAI,EAAG;AAC1B,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,SAAS;AACb,eAAW,OAAO,KAAK,MAAM,GAAG,EAAE,GAAG;AACnC,UAAI,CAAC,SAAS,OAAO,GAAG,CAAC,EAAG,QAAO,GAAG,IAAI,CAAC;AAC3C,eAAS,OAAO,GAAG;AAAA,IACrB;AACA,WAAO,KAAK,GAAG,EAAE,CAAE,IAAI,gBAAgB,KAAK;AAAA,EAC9C;AACF;AAGO,SAAS,iBAAiB,OAAwC;AACvE,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,SAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,eAAe,GAAG,CAAC,CAAC;AACzF;AAGO,IAAM,gBAAgB,CAAC,GAAoB,MAAgC,MAAM,KAAK,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;AAE5H,IAAM,mBAAmBD,eAA+B,KAAK;AASpE,IAAM,YAAY,oBAAI,IAAyB;AAC/C,IAAM,YAAY,oBAAI,IAA6B;AAEnD,IAAM,SAAS,CAAC,WAAmB,UAAU,IAAI,MAAM,GAAG,QAAQ,CAAC,aAAa,SAAS,CAAC;AAE1F,IAAM,iBAAiB,CAAC,WAAoC,UAAU,IAAI,MAAM,GAAG,GAAG,EAAE,GAAG,UAAU;AAG9F,SAAS,iBAAiB,QAAgB,QAAoF;AACnI,QAAM,YAAuB,EAAE,OAAO;AACtC,YAAU,IAAI,QAAQ,CAAC,GAAI,UAAU,IAAI,MAAM,KAAK,CAAC,GAAI,SAAS,CAAC;AACnE,SAAO,MAAM;AACb,SAAO;AAAA,IACL,OAAO,MAAM;AACX,UAAI,UAAU,WAAW,KAAM;AAC/B,gBAAU,SAAS;AACnB,aAAO,MAAM;AAAA,IACf;AAAA,IACA,SAAS;AACP,YAAM,QAAQ,UAAU,IAAI,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,cAAc,cAAc,SAAS;AACxF,UAAI,KAAK,SAAS,EAAG,WAAU,IAAI,QAAQ,IAAI;AAAA,UAC1C,WAAU,OAAO,MAAM;AAC5B,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,UAAU,QAAgB,UAAkC;AACnE,QAAM,MAAM,UAAU,IAAI,MAAM,KAAK,oBAAI,IAAI;AAC7C,MAAI,IAAI,QAAQ;AAChB,YAAU,IAAI,QAAQ,GAAG;AACzB,SAAO,MAAM;AACX,QAAI,OAAO,QAAQ;AACnB,QAAI,IAAI,SAAS,EAAG,WAAU,OAAO,MAAM;AAAA,EAC7C;AACF;AASO,SAAS,aAAa,QAAkC;AAC7D,QAAM,SAASC,YAAW,gBAAgB;AAC1C,QAAM,MAAM,UAAU;AACtB,QAAM,kBAAkB,YAAY,CAAC,aAA0B,WAAW,SAAY,MAAM;AAAA,EAAC,IAAI,UAAU,KAAK,QAAQ,GAAI,CAAC,QAAQ,GAAG,CAAC;AACzI,QAAM,cAAc,YAAY,MAAO,WAAW,SAAY,QAAQ,eAAe,GAAG,GAAI,CAAC,QAAQ,GAAG,CAAC;AACzG,QAAM,UAAU,qBAAqB,iBAAiB,aAAa,WAAW;AAC9E,SAAO,WAAW,SAAY,SAAS;AACzC;;;ACvGA,IAAM,aAAa,CAAC,SAAsB,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,SAAS;AAMlF,SAAS,sBAAsB,OAA8C;AAClF,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,UAAM,EAAE,OAAO,aAAa,SAAS,IAAI;AACzC,SAAK,UAAU,wBAAwB,UAAU,yBAAyB,UAAU,IAAI;AACtF,YAAM,SACJ,SAAS,aAAa,SAAS,SAAS,SACpC,SAAS,SACT,SAAS,WACP,uBAAuB,gBACvB,uBAAuB;AAC/B,aAAO,EAAE,GAAG,MAAM,OAAO,iBAAiB,UAAU,EAAE,IAAI,SAAS,IAAI,UAAU,OAAO,OAAO,EAAE;AAAA,IACnG;AACA,QAAI,UAAU,qBAAsB,UAAU,sBAAsB,gBAAgB,MAAO;AACzF,YAAM,EAAE,QAAQ,SAAS,aAAa,cAAc,GAAG,KAAK,IAAI;AAChE,aAAO,EAAE,GAAG,MAAM,OAAO,gBAAgB,WAAW,uBAAuB,QAAQ;AAAA,IACrF;AACA,WAAO;AAAA,EACT,CAAC;AACH;;;ACxCA,SAAS,eAAe;AACxB,SAAS,4BAA4B;AAErC,SAAS,eAAAC,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACyC3D,IAAM,iBAAiB;AAGvB,IAAM,iBAAiB;AA4EvB,SAAS,YAAY,SAA2B,EAAE,cAAc,MAAM,IAA+B,CAAC,GAAW;AACtH,QAAM,QAAQ,QAAQ,MAAM,QAAQ,CAAC,SAAU,KAAK,SAAS,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAE;AACvF,UAAQ,eAAe,QAAQ,SAAS,cAAc,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC,IAAI,OAAO,KAAK,MAAM;AACtH;;;ACzHO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,UAAU,iCAAiC,SAA+B;AACpF,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YAAY,WAAoB;AAC9B,UAAM,cAAc,SAAY,qBAAqB,8BAA8B,SAAS,KAAK;AACjG,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,kBAAkB,OAAsE;AACtG,SAAO,iBAAiB,uBAAuB,iBAAiB;AAClE;;;ACvBO,IAAM,8BAAiD,CAAC,GAAG,KAAM,GAAI;AAgB5E,eAAsB,qBAAqB,SAA8D;AACvG,QAAM,EAAE,UAAU,kBAAkB,SAAS,OAAO,IAAI;AACxD,WAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;AACpD,QAAI,CAAE,MAAM,MAAM,SAAS,KAAK,KAAK,GAAG,MAAM,EAAI,QAAO;AACzD,UAAM,UAAU,MAAM,OAAO,KAAK;AAClC,QAAI,YAAY,YAAa,QAAO;AACpC,QAAI,YAAY,YAAa,QAAO;AAAA,EACtC;AACA,SAAO;AAEP,iBAAe,OAAO,OAA8D;AAClF,UAAM,oBAAoB,IAAI,gBAAgB;AAC9C,QAAI;AACJ,QAAI,gBAA4B,MAAM;AAAA,IAAC;AACvC,UAAM,cAAc,IAAI,QAAgC,CAAC,YAAY;AACnE,cAAQ,WAAW,MAAM;AACvB,0BAAkB,MAAM;AACxB,gBAAQ,QAAQ;AAAA,MAClB,GAAG,gBAAgB;AACnB,sBAAgB,MAAM;AACpB,0BAAkB,MAAM;AACxB,gBAAQ,WAAW;AAAA,MACrB;AACA,aAAO,iBAAiB,SAAS,aAAa;AAAA,IAChD,CAAC;AACD,QAAI;AACF,YAAM,YAAY,QAAQ,kBAAkB,QAAQ,KAAK,EAAE;AAAA,QACzD,CAAC,OAAQ,KAAM,cAAyB;AAAA,QACxC,MAAM;AAAA,MACR;AACA,YAAM,UAAU,MAAM,QAAQ,KAAK,CAAC,WAAW,WAAW,CAAC;AAC3D,aAAO,OAAO,UAAU,cAAc;AAAA,IACxC,UAAE;AACA,UAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,aAAO,oBAAoB,SAAS,aAAa;AAAA,IACnD;AAAA,EACF;AACF;AAGA,SAAS,MAAM,IAAY,QAAuC;AAChE,MAAI,OAAO,QAAS,QAAO,QAAQ,QAAQ,KAAK;AAChD,MAAI,MAAM,EAAG,QAAO,QAAQ,QAAQ,IAAI;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,WAAW,MAAM;AAC7B,aAAO,oBAAoB,SAAS,OAAO;AAC3C,cAAQ,IAAI;AAAA,IACd,GAAG,EAAE;AACL,aAAS,UAAU;AACjB,mBAAa,KAAK;AAClB,cAAQ,KAAK;AAAA,IACf;AACA,WAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC1D,CAAC;AACH;;;AClCA,eAAsB,UAAU,SAAkD;AAChF,QAAM,EAAE,QAAQ,SAAS,IAAI;AAC7B,MAAI,WAAW;AACf,SAAO,CAAC,OAAO,SAAS;AACtB,UAAM,UAAU,MAAM,SAAS,OAAO;AACtC,QAAI,YAAY,cAAe,QAAO;AACtC,QAAI,OAAO,QAAS;AACpB,QAAI,YAAY,WAAW;AACzB,YAAM,MAAM,QAAQ,kBAAkB,KAAQ,MAAM;AACpD;AAAA,IACF;AACA,QAAI,YAAY,SAAU,YAAW;AACrC,UAAM,QAAQ,SAAS,KAAK,IAAI,UAAU,SAAS,SAAS,CAAC,CAAC,KAAK;AACnE,gBAAY;AACZ,UAAM,MAAM,OAAO,MAAM;AAAA,EAC3B;AACA,SAAO;AACT;AAGA,eAAe,SAAS,SAAqF;AAC3G,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,iBAAiB,MAAM,WAAW,MAAM;AAC9C,UAAQ,OAAO,iBAAiB,SAAS,gBAAgB,EAAE,MAAM,KAAK,CAAC;AACvE,MAAI;AACJ,QAAM,sBAAsB,MAAM;AAChC,iBAAa,YAAY;AACzB,mBAAe,WAAW,gBAAgB,QAAQ,gBAAgB;AAAA,EACpE;AACA,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,MAAM,QAAQ,KAAK,EAAE,SAAS,EAAE,QAAQ,oBAAoB,GAAG,QAAQ,WAAW,OAAO,CAAC;AACzH,QAAI,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM;AAClC,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,IAAK,QAAO;AAC/D,UAAI,mBAAmB,SAAS,MAAM,EAAG,QAAO;AAChD,aAAO;AAAA,IACT;AAEA,QAAI,EAAE,SAAS,QAAQ,IAAI,cAAc,KAAK,IAAI,SAAS,mBAAmB,EAAG,QAAO;AACxF,YAAQ,SAAS;AACjB,wBAAoB;AACpB,UAAM,SAAS,SAAS,KAAK,UAAU;AACvC,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI,SAAS;AACb,eAAS;AACP,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,0BAAoB;AACpB,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,UAAI,WAAW,OAAO,QAAQ,MAAM;AACpC,aAAO,YAAY,GAAG;AACpB,cAAM,QAAQ,OAAO,MAAM,GAAG,QAAQ;AACtC,iBAAS,OAAO,MAAM,WAAW,CAAC;AAClC,cAAM,QAAQ,QAAQ,KAAK;AAC3B,YAAI,SAAS,CAAC,WAAW,OAAO,QAAS,SAAQ,QAAQ,KAAK;AAC9D,mBAAW,OAAO,QAAQ,MAAM;AAAA,MAClC;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,WAAW,OAAO,WAAW,CAAC,QAAQ,OAAO,UAAU,WAAW;AAAA,EAC3E,UAAE;AACA,iBAAa,YAAY;AACzB,YAAQ,OAAO,oBAAoB,SAAS,cAAc;AAC1D,eAAW,MAAM;AAAA,EACnB;AACF;AAGA,SAAS,mBAAmB,QAAyB;AACnD,SAAO,UAAU,OAAO,SAAS,OAAO,WAAW,OAAO,WAAW;AACvE;AAEA,SAAS,QAAQ,OAA2C;AAC1D,QAAM,OAAO,MACV,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,CAAC,EACzC,IAAI,CAAC,SAAS,KAAK,MAAM,CAAC,EAAE,UAAU,CAAC,EACvC,KAAK,IAAI;AACZ,MAAI,CAAC,QAAQ,SAAS,SAAU,QAAO;AACvC,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,QAAI,UAAU,OAAO,WAAW,YAAY,OAAQ,OAA8B,SAAS,SAAU,QAAO;AAAA,EAC9G,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,MAAM,IAAY,QAAoC;AAC7D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,WAAW,MAAM,EAAE;AACjC,WAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AACrD,aAAS,OAAO;AACd,mBAAa,KAAK;AAClB,aAAO,oBAAoB,SAAS,IAAI;AACxC,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACH;;;ACpIA,IAAM,iBAAiB;AAGhB,SAAS,0BAA0B,kBAA0B,aAA6B;AAC/F,SAAO,KAAK,IAAI,kBAAkB,IAAI,WAAW;AACnD;AAqBO,SAAS,aAAa,MAAkC,SAA0D;AACvH,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,aAAa,KAAK,IAAI;AAC1B,MAAI,eAAe;AACnB,MAAIC,YAAW;AACf,MAAI;AACJ,MAAI;AAEJ,QAAM,OAAO,MAAM;AACjB,IAAAA,YAAW;AACX,QAAI,UAAU,OAAW,eAAc,KAAK;AAC5C,YAAQ;AACR,aAAS,oBAAoB,oBAAoB,KAAK;AACtD,WAAO,oBAAoB,UAAU,KAAK;AAC1C,YAAQ,QAAQ;AAAA,EAClB;AACA,WAAS,QAAQ;AACf,QAAIA,UAAU;AACd,UAAM,QAAQ,eAAe,QAAQ,mBAAoB,QAAQ,sBAAsB,QAAQ;AAC/F,UAAM,YAAY,KAAK,IAAI,IAAI;AAC/B,QAAI,YAAY,MAAO;AACvB,SAAK;AACL,SAAK,OAAO,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACnC,YAAQ,YAAY;AACpB,mBAAe,MAAM,IAAI,sBAAsB,SAAS,CAAC;AAAA,EAC3D;AAEA,SAAO,IAAI,eAA2B;AAAA,IACpC,MAAM,YAAY;AAChB,sBAAgB;AAChB,cAAQ,YAAY,OAAO,cAAc;AACzC,eAAS,iBAAiB,oBAAoB,KAAK;AACnD,aAAO,iBAAiB,UAAU,KAAK;AAAA,IACzC;AAAA,IACA,MAAM,KAAK,YAAY;AACrB,UAAI;AACF,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAIA,UAAU;AACd,YAAI,MAAM;AACR,eAAK;AACL,qBAAW,MAAM;AACjB;AAAA,QACF;AACA,qBAAa,KAAK,IAAI;AACtB,YAAI,CAAC,cAAc;AACjB,yBAAe;AACf,kBAAQ,cAAc;AAAA,QACxB;AACA,mBAAW,QAAQ,KAAK;AAAA,MAC1B,SAAS,OAAO;AACd,YAAIA,UAAU;AACd,aAAK;AACL,cAAM,QAAQ,iBAAiB,QAAQ,eAAe,KAAK,IAAI;AAAA,MACjE;AAAA,IACF;AAAA,IACA,OAAO,QAAQ;AACb,WAAK;AACL,aAAO,OAAO,OAAO,MAAM;AAAA,IAC7B;AAAA,EACF,CAAC;AACH;;;ACrEA,eAAsB,kBACpB,SACA,OACA,MACA,SACmB;AACnB,QAAM,aAAa,MAAM,UAAU;AACnC,QAAM,MAAM,IAAI,gBAAgB;AAChC,QAAM,WAAW,CAAC,WAAqB;AACrC,QAAI,CAAC,IAAI,OAAO,QAAS,KAAI,MAAM,MAAM;AAAA,EAC3C;AACA,QAAM,mBAAmB,MAAM,SAAS,YAAY,MAAM;AAC1D,QAAM,sBAAsB,MAAM,SAAS,IAAI,oBAAoB,CAAC;AACpE,MAAI,YAAY,QAAS,kBAAiB;AAC1C,cAAY,iBAAiB,SAAS,gBAAgB;AACtD,MAAI,QAAQ,QAAQ,QAAS,qBAAoB;AACjD,UAAQ,QAAQ,iBAAiB,SAAS,mBAAmB;AAE7D,MAAI,eAAqD,MAAM;AAAA,EAAC;AAChE,QAAM,WAAW,IAAI,QAAe,CAAC,UAAU,WAAW;AACxD,mBAAe;AAAA,EACjB,CAAC;AACD,WAAS,MAAM,MAAM;AAAA,EAAC,CAAC;AACvB,QAAM,QAAQ,WAAW,MAAM;AAC7B,UAAM,OAAO,IAAI,oBAAoB,iCAAiC,QAAQ,SAAS,KAAK;AAC5F,iBAAa,IAAI;AACjB,aAAS,IAAI;AAAA,EACf,GAAG,QAAQ,SAAS;AACpB,QAAM,UAAU,MAAM;AACpB,iBAAa,KAAK;AAClB,gBAAY,oBAAoB,SAAS,gBAAgB;AACzD,YAAQ,QAAQ,oBAAoB,SAAS,mBAAmB;AAAA,EAClE;AAEA,QAAM,YAAY,CAAC,UAA4B;AAC7C,QAAI,YAAY,QAAS,QAAO;AAChC,QAAI,iBAAiB,oBAAqB,QAAO;AACjD,QAAI,iBAAiB,aAAc,iBAAiB,SAAS,MAAM,SAAS,cAAe;AACzF,aAAO,IAAI,oBAAoB,MAAM,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,KAAK,CAAC,QAAQ,OAAO,EAAE,GAAG,MAAM,QAAQ,IAAI,OAAO,CAAC,GAAG,QAAQ,CAAC;AAE/F,QAAI,QAAQ,aAAa,CAAC,SAAS,IAAI;AACrC,YAAM,WAAW,MAAM,QAAQ,KAAK,CAAC,SAAS,YAAY,GAAG,QAAQ,CAAC;AACtE,cAAQ;AACR,aAAO,IAAI,SAAS,UAAU,EAAE,QAAQ,SAAS,QAAQ,YAAY,SAAS,YAAY,SAAS,SAAS,QAAQ,CAAC;AAAA,IACvH;AACA,iBAAa,KAAK;AAClB,QAAI,CAAC,SAAS,MAAM;AAClB,cAAQ;AACR,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,aAAa,SAAS,MAAM;AAAA,MACvC,kBAAkB;AAAA,MAClB,GAAG,QAAQ,UAAU,QAAQ;AAAA,MAC7B,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AACD,WAAO,IAAI,SAAS,MAAM;AAAA,MACxB,QAAQ,SAAS;AAAA,MACjB,YAAY,SAAS;AAAA,MACrB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ;AACR,UAAM,UAAU,KAAK;AAAA,EACvB;AACF;;;ACnBO,IAAM,eAAe;AAErB,IAAM,mBAAmB;AAEhC,IAAM,SAAmC,CAAC,QAAQ,QAAQ,SAAS;AACnE,IAAM,UAAqC,CAAC,SAAS,QAAQ,OAAO,WAAW,MAAM;AACrF,IAAM,UAA4C,CAAC,SAAS,WAAW,SAAS;AAEhF,IAAM,UAAU,CAAC,UAAmC,OAAO,SAAS,KAAsB,IAAK,QAA0B;AACzH,IAAM,WAAW,CAAC,WAAqC,QAAQ,SAAS,MAAwB,IAAK,SAA4B;AACjI,IAAM,UAAU,CAAC,UAAoB,OAAO,UAAU,YAAY,QAAQ,IAAI,EAAE,MAAM,IAAI,CAAC;AAC3F,IAAM,UAAU,CAAC,UAAmB,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS;AAErG,SAAS,gBAAgB,MAAgF;AACvG,MAAI,KAAK,SAAS,WAAY,QAAO,CAAC;AACtC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,GAAI,QAAQ,KAAK,KAAK,KAAK,EAAE,OAAO,KAAK,MAAgB;AAAA,IACzD,GAAI,QAAQ,KAAK,IAAI,KAAK,EAAE,MAAM,KAAK,KAAe;AAAA,IACtD,GAAI,QAAQ,KAAK,SAAS,KAAK,EAAE,WAAW,KAAK,UAAoB;AAAA,EACvE;AACF;AAEO,SAAS,eAAe,MAAkC;AAC/D,QAAM,SAAS,KAAK,YAAY,OAAO,YAAY,QAAQ,SAAS,KAAK,MAA+B,IAAK,KAAK,SAAmC;AACrJ,MAAI,WAAW,UAAW,QAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,OAAO,MAAM,UAAU,MAAM,OAAO;AACtG,QAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,QAAQ,YAAY,IAAI,CAAC;AACpF,SAAO;AAAA,IACL,OAAO,QAAQ,KAAK,KAAK;AAAA,IACzB,QAAQ,SAAS,KAAK,MAAM;AAAA,IAC5B,GAAG,QAAQ,KAAK,KAAK;AAAA,IACrB,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,IACrD,UAAU,KAAK,WAAW,IAAI,KAAK,KAAK,QAAQ,IAAI;AAAA,IACpD,GAAI,OAAO,KAAK,aAAa,YAAY,EAAE,UAAU,KAAK,SAAS;AAAA,IACnE,GAAI,UAAU,EAAE,OAAO;AAAA,IACvB,GAAI,OAAO,KAAK,aAAa,YAAY,EAAE,UAAU,KAAK,SAAS;AAAA,IACnE,GAAI,QAAQ,SAAS,KAAK,EAAE,QAAQ;AAAA,IACpC,GAAG,gBAAgB,IAAI;AAAA,EACzB;AACF;AAEA,SAAS,aAAa,MAAqC;AACzD,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO,CAAC;AACvD,QAAMC,UAAS;AACf,MAAI,OAAOA,QAAO,OAAO,YAAY,OAAOA,QAAO,UAAU,SAAU,QAAO,CAAC;AAC/E,QAAM,QAAQA,QAAO,UAAU,aAAaA,QAAO,UAAU,cAAcA,QAAO,QAAQ;AAC1F,SAAO;AAAA,IACL;AAAA,MACE,IAAIA,QAAO;AAAA,MACX,OAAO,QAAQA,QAAO,KAAK;AAAA,MAC3B,QAAQ,SAASA,QAAO,MAAM;AAAA,MAC9B,GAAG,QAAQA,QAAO,KAAK;AAAA,MACvB;AAAA,MACA,OAAOA,QAAO;AAAA,MACd,UAAU,OAAOA,QAAO,aAAa,WAAW,IAAI,KAAKA,QAAO,QAAQ,IAAI;AAAA,MAC5E,GAAG,gBAAgBA,OAAM;AAAA,IAC3B;AAAA,EACF;AACF;AAGO,SAAS,eAAe,MAAyC;AACtE,MAAI,KAAK,UAAU,aAAa,KAAK,UAAU,YAAa,QAAO;AACnE,SAAO,EAAE,OAAO,KAAK,OAAO,MAAM,eAAe,IAAI,EAAE;AACzD;AAGO,SAAS,kBAAkB,MAAoB,KAA4B;AAChF,MAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,QAAM,OAAO,KAAK,SAAS,QAAQ,IAAI;AACvC,SAAO,OAAO,IAAI,KAAK,IAAI,MAAM,YAAY,IAAI;AACnD;;;AC/HO,IAAM,uBAAuB;;;ACV7B,SAAS,cAAc,KAAa,OAA8B;AAEvE,MAAI,UAAoB,CAAC;AAEzB,QAAM,OAAO,MAAgB;AAC3B,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,eAAe,QAAQ,GAAG,KAAK,IAAI;AACtE,aAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,OAAO,CAAC,OAAqB,OAAO,OAAO,QAAQ,IAAI,CAAC;AAAA,IAChG,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,QAAkB;AAC/B,cAAU;AACV,QAAI;AACF,qBAAe,QAAQ,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,IACjD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,IAAI;AACR,YAAM,MAAM,KAAK;AACjB,UAAI,IAAI,SAAS,EAAE,EAAG,QAAO;AAC7B,YAAM,CAAC,GAAG,KAAK,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AChCO,IAAM,yBAAyB;AAM/B,IAAM,yBAAyB;AAGtC,IAAM,YAAY,cAAc,wBAAwB,GAAG;AAY3D,eAAsB,eACpB,MACA,SAOe;AACf,QAAM,EAAE,OAAO,IAAI;AACnB,MAAI,KAAK,UAAU,OAAO,SAAS,CAAC,UAAU,MAAM,KAAK,UAAU,CAAC,QAAQ,SAAS,KAAK,UAAU,CAAC,CAAC,EAAG;AACzG,QAAM,SAAS,MAAM,OAAO,YAAY,EAAE,YAAY,KAAK,YAAY,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,GAAG,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAC/I,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,QAAQ,GAAG,QAAQ,OAAO,kBAAkB,mBAAmB,KAAK,UAAU,CAAC,IAAI;AAAA,MAChH,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,WAAW,OAAO,OAAO,MAAM;AAAA,IACvC,CAAC;AACD,QAAI,SAAS,WAAW,IAAK,SAAQ,eAAe;AAAA,EACtD,QAAQ;AAAA,EAER;AACF;AAGA,SAAS,WAAW,OAAe,QAAmC;AACpE,MAAI,WAAW,OAAQ,QAAO,KAAK,UAAU,EAAE,OAAO,OAAO,OAAO,MAAM,CAAC;AAC3E,MAAI;AACF,WAAO,KAAK,UAAU,EAAE,OAAO,QAAQ,OAAO,WAAW,SAAY,OAAO,OAAO,OAAO,CAAC;AAAA,EAC7F,SAAS,OAAO;AACd,WAAO,KAAK,UAAU,EAAE,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,CAAC;AAAA,EAChG;AACF;;;ACzDO,SAASC,OAAM,IAAY,QAAoC;AACpE,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,OAAO,SAAS;AAClB,cAAQ;AACR;AAAA,IACF;AACA,UAAM,SAAS,MAAM;AACnB,mBAAa,KAAK;AAClB,aAAO,oBAAoB,SAAS,MAAM;AAC1C,cAAQ;AAAA,IACV;AACA,UAAM,QAAQ,WAAW,QAAQ,EAAE;AACnC,WAAO,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAAA,EACzD,CAAC;AACH;;;ACdO,IAAM,4BAA4B;AASzC,IAAM,oBAA0C,oBAAI,IAAoB,CAAC,gBAAgB,kBAAkB,eAAe,oBAAoB,uBAAuB,CAAC;AAatK,eAAsB,aAAa,UAAgD;AACjF,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,SAAS,YAAY,SAAS,OAAQ,KAA6B,QAAQ;AAChG,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,EAAE,MAAM,MAAM,QAAQ,IAAI;AAChC,MAAI,CAAC,kBAAkB,IAAI,IAAI,EAAG,QAAO;AACzC,MAAI,OAAO,SAAS,YAAY,OAAO,YAAY,SAAU,QAAO;AACpE,QAAM,OAAO,qBAAqB,OAAO;AACzC,SAAO,OAAO,EAAE,MAA8B,MAAM,SAAS,KAAK,IAAI;AACxE;AAGO,SAAS,qBAAqB,MAAsB;AACzD,QAAM,OAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC5C,SAAO,KAAK,UAAU,4BAA4B,OAAO,GAAG,KAAK,MAAM,GAAG,4BAA4B,CAAC,EAAE,QAAQ,CAAC;AACpH;;;AC5BA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC;AAMlD,eAAsB,gBAAgB,UAAmD;AACvF,MAAI,SAAS,SAAS,OAAO,SAAS,UAAU,IAAK,QAAO;AAE5D,QAAM,MAAM,MAAM,aAAa,QAAQ;AACvC,MAAI,IAAK,QAAO,EAAE,MAAM,IAAI,SAAS,OAAO,MAAM,MAAM,IAAI,MAAM,MAAM,IAAI,KAAK;AACjF,MAAI,SAAS,WAAW,IAAK,QAAO;AACpC,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,OAAO,qBAAqB,cAAc,IAAI,CAAC;AACrD,SAAO,OAAO,EAAE,MAAM,OAAO,CAAC,mBAAmB,IAAI,SAAS,MAAM,EAAE,IAAI;AAC5E;AAEA,SAAS,cAAc,MAAuB;AAC5C,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,QAAS,KAA6B;AAC5C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,UAAM,UAAW,MAAgC;AACjD,QAAI,OAAO,YAAY,SAAU,QAAO;AAAA,EAC1C;AACA,SAAO;AACT;;;AC9CA,SAAS,mBAAmB;AA0BrB,IAAM,qBAAqB;AAI3B,IAAM,mBAAmB,CAAC,SAAiB,KAAK,WAAW,kBAAkB;AAE7E,IAAM,eAAe,CAAC,SAAmB,iBAAiB,YAAY,IAAI,CAAC;AAG3E,SAAS,iBAAiB,MAAwD;AACvF,QAAM,SAAS,KAAK;AACpB,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,WAAW;AACjB,SAAO,OAAO,SAAS,WAAW,YAAY,MAAM,QAAQ,SAAS,KAAK,IAAK,SAA4B;AAC7G;AAGO,SAAS,wBAAwB,MAAgD;AACtF,QAAM,WAAW,iBAAiB,IAAI;AACtC,SAAO,UAAU,WAAW,sBAAsB,SAAS,iBAAiB,aAAa;AAC3F;AAUO,SAAS,iBAAiB,MAAgB,UAA6E;AAC5H,QAAM,WAAW,iBAAiB,IAA4B;AAC9D,QAAM,QAAQ,KAAK;AACnB,QAAM,OAAO,UAAU,SAAS,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO;AAC/E,QAAM,YAAY,UAAU,UAAU,KAAK,UAAU,iBAAiB,KAAK,YAAY;AACvF,QAAM,WAAW,WAAW,UAAU,QAAQ;AAC9C,QAAM,iBAAiB,UAAU,UAAU,cAAc,iBAAiB,SAAU,eAAgB,IAAI;AACxG,QAAM,QAAS,KAA6B;AAC5C,SAAO;AAAA,IACL,MAAM,YAAY,IAAI,EAAE,MAAM,mBAAmB,MAAM;AAAA,IACvD,GAAI,OAAO,UAAU,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM;AAAA,IAC7D;AAAA,IACA,QAAQ,UAAU,SAAS,CAAC,GAAG,IAAI,UAAU;AAAA,IAC7C,MAAM,UAAU,QAAQ;AAAA,IACxB,QAAQ,SAAS,MAAM,QAAQ;AAAA,IAC/B,GAAI,YAAY,EAAE,SAAS;AAAA,IAC3B,GAAI,kBAAkB,EAAE,eAAe;AAAA,IACvC,GAAI,aAAa,EAAE,UAAU;AAAA,EAC/B;AACF;AAEA,SAAS,iBAAiB,EAAE,UAAU,OAAO,MAAM,GAAkG;AACnJ,SAAO,EAAE,UAAU,GAAI,SAAS,EAAE,MAAM,GAAI,GAAI,UAAU,UAAa,EAAE,MAAM,EAAG;AACpF;AAEA,SAAS,SAAS,MAAgB,UAAwD;AACxF,MAAI,KAAK,UAAU,kBAAkB,KAAK,UAAU,gBAAiB,QAAO;AAC5E,MAAI,SAAU,QAAO,SAAS;AAC9B,SAAO,KAAK,UAAU,sBAAsB,CAAC,KAAK,cAAc,SAAS;AAC3E;AAEA,SAAS,WAAW,MAAkB,OAA+B;AACnE,QAAM,KAAK,OAAO,KAAK;AACvB,MAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,MAAM,QAAQ,IAAI,MAAM,KAAK,KAAK;AACrE,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,UAAU,KAAK;AAAA,IACf,OAAO,KAAK,UAAU,SAAY,UAAU,YAAY,OAAO,SAAS;AAAA,IACxE,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM;AAAA,IACpD,GAAI,KAAK,WAAW,UAAa,EAAE,QAAQ,KAAK,OAAO;AAAA,IACvD,GAAI,KAAK,UAAU,UAAa,EAAE,WAAW,KAAK,MAAM;AAAA,EAC1D;AACF;AAEA,SAAS,WAAW,UAAsC,UAA4D;AACpH,MAAI,UAAU,QAAS,QAAO,EAAE,OAAO,SAAS,QAAQ;AACxD,QAAM,aAAa,UAAU,WAAW,sBAAsB,SAAS,iBAAiB,aAAa;AACrG,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,SAAS,SAAS,iBAAiB,OAAO,aAAa,SAAS,kBAAkB;AACxF,QAAM,UAAU,QAAQ;AACxB,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU,CAAC,aAAa,SAAS,WAAW,YAAY,QAAQ;AAAA,IAChE,SAAS,SAAS,sBAAsB;AAAA,IACxC,QAAQ,YAAY;AAAA,IACpB,GAAI,YAAY,UAAa,EAAE,aAAa,QAAQ,QAAQ,SAAS,YAAY,OAAO,EAAE;AAAA;AAAA,IAE1F,WAAW;AAAA,EACb;AACF;;;ACpHA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,gBAAgB;;;ACOvC,IAAM,aAAa;AAEnB,IAAM,aAAa;AAEnB,IAAM,aAAa;AAEnB,IAAM,iBAAiB;AAEvB,IAAM,mBAAmB;AAEzB,IAAM,yBACX;AAGK,SAAS,oBAAoB,QAA0B;AAC5D,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnF,QAAM,UAAU,OAAO,QAAQ,MAAM;AACrC,SAAO,QAAQ,WAAW,KAAK,QAAQ,CAAC,EAAG,CAAC,MAAM,kBAAkB,QAAQ,CAAC,EAAG,CAAC,MAAM;AACzF;;;ACNA,IAAM,SAAS,CAAC,SAA4C;AAC5D,IAAMC,cAAa,CAAC,SAA4C,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,SAAS;AAG/G,SAAS,gBAAgB,MAAkD;AACzE,QAAM,OAAO,OAAO,IAAI;AACxB,SAAOA,YAAW,IAAI,KAAK,KAAK,UAAU,qBAAqB,KAAK,aAAa;AACnF;AAGO,SAAS,iBAAiB,UAA8B,YAAoB,QAAiB,SAAsC;AACxI,SAAO,SAAS,IAAI,CAAC,YAAY;AAC/B,QAAI,QAAQ,SAAS,eAAe,CAAC,QAAQ,MAAM,KAAK,CAAC,SAAS,OAAO,IAAI,EAAE,eAAe,UAAU,EAAG,QAAO;AAClH,UAAM,QAAQ,QAAQ,MAAM;AAAA,MAAI,CAAC,SAC/B,OAAO,IAAI,EAAE,eAAe,aAAc,EAAE,GAAG,MAAM,OAAO,UAAU,oBAAoB,oBAAoB,QAAQ,OAAO,IAAoB;AAAA,IACnJ;AACA,WAAO,EAAE,GAAG,SAAS,OAAO,GAAI,WAAW,EAAE,UAAU,EAAE,GAAG,QAAQ,UAAU,QAAQ,YAAqB,EAAE,EAAG;AAAA,EAClH,CAAC;AACH;AAGO,SAAS,yBAAyB,UAAkD;AACzF,QAAM,UAAU,CAAC,SAA4C,OAAO,IAAI,EAAE,UAAU,qBAAqB,OAAO,IAAI,EAAE,WAAW;AACjI,MAAI,CAAC,SAAS,KAAK,CAAC,YAAY,QAAQ,MAAM,KAAK,OAAO,CAAC,EAAG,QAAO;AACrE,SAAO,SAAS;AAAA,IAAI,CAAC,YACnB,QAAQ,MAAM,KAAK,OAAO,IACtB,EAAE,GAAG,SAAS,OAAO,QAAQ,MAAM,IAAI,CAAC,SAAU,QAAQ,IAAI,IAAK,EAAE,GAAG,MAAM,OAAO,mBAAmB,IAAoB,IAAK,EAAE,IACnI;AAAA,EACN;AACF;AAMO,SAAS,6BAA6B,UAAkD;AAC7F,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,eAAe,KAAK,UAAU,WAAW,YAAa,QAAO;AAChF,MAAI,KAAK,MAAM,KAAK,CAACC,UAAS,OAAOA,KAAI,EAAE,UAAU,iBAAiB,EAAG,QAAO;AAChF,QAAM,QAAQ,KAAK,MAAM,cAAc,CAACA,UAASA,MAAK,SAAS,YAAY;AAC3E,QAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,MAAI,CAAC,QAAQ,CAACD,YAAW,IAAI,KAAK,OAAO,IAAI,EAAE,UAAU,sBAAsB,OAAO,IAAI,EAAE,aAAa,OAAW,QAAO;AAC3H,QAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,MAAM,OAAQ,OAAO,QAAS,EAAE,GAAG,MAAM,OAAO,kBAAkB,IAAoB,IAAK;AACzH,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC;AACtD;AAGO,SAAS,yBAAyB,UAAkD;AACzF,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,eAAe,KAAK,UAAU,WAAW,uBAAuB,CAAC,KAAK,MAAM,KAAK,eAAe,EAAG,QAAO;AAC7H,QAAM,QAAQ,KAAK,MAAM;AAAA,IAAI,CAAC,SAC5B,gBAAgB,IAAI,IAAK,EAAE,GAAG,MAAM,OAAO,oBAAoB,QAAQ,EAAE,CAAC,cAAc,GAAG,iBAAiB,EAAE,IAAoB;AAAA,EACpI;AACA,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC;AACtD;AAGO,SAAS,mBAAmB,UAAgD;AACjF,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,SACE,MAAM,SAAS,eACf,KAAK,UAAU,WAAW,uBAC1B,KAAK,MAAM;AAAA,IACT,CAAC,SAAS,OAAO,IAAI,EAAE,UAAU,wBAAwB,OAAO,IAAI,EAAE,UAAU,wBAAwB,wBAAwB,IAA4B,MAAM;AAAA,EACpK;AAEJ;;;AF5BO,SAAS,gBAAgB,MAA2C;AACzE,QAAM,UAAUE,QAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,YAAYA,QAAO,KAAK;AAC9B,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAwB,IAAI;AAC1E,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwE,IAAI;AAEtH,QAAM,eAAeC,aAAY,OAAO,YAAoB,QAAiB,eAA8C;AACzH,QAAI,UAAU,QAAS,QAAO;AAC9B,cAAU,UAAU;AACpB,UAAM,UAAU,QAAQ;AACxB,UAAM,SAAS,QAAQ,WAAW;AAClC,UAAM,OAAO,CAAC,MAAyB,SAAkB;AACvD,UAAI,CAAC,OAAO,QAAS,kBAAiB,EAAE,IAAI,YAAY,MAAM,GAAI,SAAS,UAAa,EAAE,KAAK,EAAG,CAAC;AACnG,aAAO;AAAA,IACT;AACA,qBAAiB,IAAI;AACrB,uBAAmB,UAAU;AAC7B,QAAI;AACF,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM;AAAA,UACf,QAAQ;AAAA,UACR,GAAG,QAAQ,OAAO,YAAY,mBAAmB,UAAU,CAAC;AAAA,UAC5D,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,EAAE,QAAQ,GAAG,QAAQ,WAAW,EAAE,CAAC,EAAE;AAAA,UAC7H,EAAE,WAAW,QAAQ,iBAAiB,GAAG,WAAW,KAAK;AAAA,QAC3D;AAAA,MACF,QAAQ;AACN,eAAO,KAAK,YAAY;AAAA,MAC1B;AAEA,YAAM,eAAe,SAAS,KAAK,OAAO,MAAM,gBAAgB,QAAQ;AACxE,UAAI,cAAc,MAAM;AACtB,gBAAQ,mBAAmB,YAAY;AACvC,eAAO,KAAK,WAAW,aAAa,IAAI;AAAA,MAC1C;AAEA,UAAI,gBAAgB,SAAS,WAAW,OAAQ,MAAM,QAAQ,YAAY,QAAQ,MAAO,yBAAyB;AAChH,eAAO,KAAK,kBAAkB,aAAa,IAAI;AAAA,MACjD;AAEA,UAAI,SAAS,WAAW,OAAQ,MAAM,QAAQ,YAAY,QAAQ,MAAO,0BAA2B,QAAO;AAC3G,UAAI,SAAS,WAAW,OAAQ,MAAM,QAAQ,YAAY,QAAQ,MAAO,uBAAwB,QAAO,KAAK,eAAe;AAC5H,UAAI,SAAS,WAAW,KAAK;AAC3B,cAAM,UAAU,MAAM,QAAQ,kBAAkB,QAAQ;AACxD,YAAI,WAAW,CAAC,OAAO,QAAS,SAAQ,sBAAsB,OAAO;AACrE,YAAI,QAAS,QAAO,KAAK,gBAAgB;AACzC,eAAO,KAAM,MAAM,QAAQ,YAAY,QAAQ,MAAO,mBAAmB,mBAAmB,YAAY;AAAA,MAC1G;AACA,UAAI,SAAS,WAAW,IAAK,QAAO,KAAK,UAAU;AACnD,UAAI,SAAS,WAAW,OAAQ,MAAM,QAAQ,YAAY,QAAQ,MAAO,OAAQ,QAAO,KAAK,MAAM;AAEnG,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,QAAQ,cAAc,MAAM;AAClC,eAAO,KAAK,kBAAkB;AAAA,MAChC;AACA,UAAI,CAAC,SAAS,GAAI,QAAO,KAAK,YAAY;AAC1C,YAAM,UAAU,MAAM,UAAU,QAAQ;AACxC,cAAQ,YAAY,CAAC,aAAa,iBAAiB,UAAU,YAAY,QAAQ,OAAO,CAAC;AACzF,UAAI,CAAC,OAAO,QAAS,cAAa;AAGlC,UAAI,CAAC,SAAS;AACZ,gBAAQ,mBAAmB,UAAU;AACrC,YAAI,CAAC,OAAO,QAAS,OAAM,QAAQ,cAAc,MAAM;AACvD,eAAO;AAAA,MACT;AACA,cAAQ,YAAY,IAAI;AACxB,UAAI;AACF,cAAM,UAAU,MAAM,QAAQ,gBAAgB,MAAM;AACpD,YAAI,YAAY,UAAW,OAAM,QAAQ,cAAc,MAAM;AAAA,iBACpD,YAAY,YAAY,CAAC,OAAO,QAAS,SAAQ,YAAY;AAAA,MACxE,UAAE;AACA,YAAI,CAAC,OAAO,QAAS,SAAQ,YAAY,KAAK;AAAA,MAChD;AACA,aAAO;AAAA,IACT,UAAE;AACA,gBAAU,UAAU;AACpB,UAAI,CAAC,OAAO,QAAS,oBAAmB,IAAI;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAkBA,aAAY,MAAM,UAAU,SAAS,CAAC,CAAC;AAE/D,SAAO,EAAE,cAAc,iBAAiB,eAAe,gBAAgB;AACzE;AAGA,eAAe,UAAU,UAAsC;AAC7D,MAAI;AACF,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AGxJA,SAAS,WAAW,WAAAC,UAAS,UAAAC,eAAc;AAyB3C,IAAM,sBAAsB;AAE5B,IAAM,6BAA6B;AAG5B,SAAS,WAAW,OAAsC;AAC/D,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,QAAM,EAAE,KAAK,IAAI;AACjB,SAAO,OAAO,SAAS,WAAY,QAAqB;AAC1D;AAGO,SAAS,eAAe,MAA+D;AAC5F,SAAO,KAAK,SAAS,sBAAsB,WAAY,KAA4B,IAAI,IAAI;AAC7F;AAeO,SAAS,kBAAkB,QAAgB,QAAiB,MAAe,UAAuC,YAAyD;AAChL,QAAM,SAASA,QAAO,UAAU;AAChC,SAAO,UAAU;AACjB,QAAM,OAAOA,QAAO,EAAE,QAAQ,MAAM,oBAAI,IAAY,GAAG,aAAa,OAAO,aAAa,oBAAI,IAAY,EAAE,CAAC;AAE3G,QAAM,UAAUA,QAAO,IAAI;AAE3B,YAAU,MAAM;AACd,QAAI,KAAK,QAAQ,WAAW,OAAQ,MAAK,UAAU,EAAE,QAAQ,MAAM,oBAAI,IAAI,GAAG,aAAa,OAAO,aAAa,oBAAI,IAAI,EAAE;AACzH,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,KAAK;AACnB,UAAM,OAAO,MAAM,gBAAgB,QAAQ,QAAQ;AACnD,YAAQ,UAAU;AAClB,UAAM,cAAc;AACpB,eAAW,WAAW,UAAU;AAC9B,UAAI,QAAQ,SAAS,YAAa;AAClC,cAAQ,MAAM,QAAQ,CAAC,MAAM,UAAU;AACrC,cAAM,QAAQ,eAAe,IAAI;AACjC,YAAI,CAAC,MAAO;AACZ,cAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,KAAK;AAClC,YAAI,MAAM,KAAK,IAAI,GAAG,EAAG;AACzB,cAAM,KAAK,IAAI,GAAG;AAClB,cAAM,SAAU,KAA0B;AAC1C,cAAM,SAAS,OAAO,WAAW,YAAY,OAAO,WAAW,0BAA0B;AACzF,eAAO,UAAU,OAAO,EAAE,MAAM,CAAC,WAAW,QAAQ,MAAM,YAAY,IAAI,QAAQ,EAAE,IAAI,WAAW,QAAQ,GAAG,CAAC;AAAA,MACjH,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,MAAM,QAAQ,QAAQ,QAAQ,CAAC;AAEnC,SAAOD,SAAQ,OAAO,EAAE,YAAY,CAAC,cAAsB,KAAK,KAAK,QAAQ,YAAY,IAAI,SAAS,EAAE,IAAI,CAAC,CAAC;AAChH;;;AC3CO,IAAM,wBAAwB;AAG9B,IAAM,mBAA+C;AAAA,EAC1D,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,eAAe;AAAA,EACf,UAAU;AAAA,EACV,SAAS;AAAA,EACT,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,cAAc;AAChB;AAGO,SAAS,eAAe,OAA4C;AACzE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,QAAQ;AACd,QAAM,WAAW,EAAE,GAAG,iBAAiB;AACvC,aAAW,cAAc,OAAO,KAAK,gBAAgB,GAAuB;AAC1E,QAAI,OAAO,MAAM,UAAU,MAAM,UAAW,UAAS,UAAU,IAAI,MAAM,UAAU;AAAA,EACrF;AACA,SAAO;AACT;AAGO,SAAS,UAAU,OAAoC;AAC5D,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,SAAO,OAAO,SAAS,YAAY,OAAO,YAAY,WAAW,EAAE,MAAM,QAAQ,IAAI;AACvF;AAEO,SAAS,iBAAiB,GAAqB,GAA8B;AAClF,SAAQ,OAAO,KAAK,CAAC,EAAuB,MAAM,CAAC,eAAe,EAAE,UAAU,MAAM,EAAE,UAAU,CAAC;AACnG;;;ACjFA,SAAS,eAAAE,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AA2CzD,IAAM,WAAsC,CAAC;AAC7C,IAAM,gBAA6C,oBAAI,IAAI,CAAC,UAAU,WAAW,mBAAmB,CAAC;AAC9F,IAAM,aAAa,CAAC,SAAyB,cAAc,IAAI,KAAK,MAAM;AAG1E,SAAS,oBAAoB,MAA0C;AAC5E,QAAM,aAAc,KAAK,UAAyE,iBAAiB;AACnH,SAAO,OAAO,eAAe,WAAW,aAAa;AACvD;AAGO,SAAS,cAAc,MAAsB,KAAqB;AACvE,QAAM,UAAU,KAAK,MAAM,KAAK,SAAS;AACzC,QAAM,QAAQ,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAC9D,SAAO,OAAO,SAAS,OAAO,KAAK,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,QAAQ,OAAO,IAAI;AAC7F;AAMO,SAAS,mBAAmB,EAAE,SAAAC,UAAS,SAAS,SAAS,OAAO,YAAY,GAA+C;AAChI,QAAM,WAAWF,QAAO,WAAW;AACnC,WAAS,UAAU;AACnB,QAAM,OAAOF,aAAY,CAAC,KAAa,UAAwB,SAAS,WAAW,WAAW,OAAO,KAAK,IAAI,GAAG,CAAC,CAAC;AACnH,QAAM,CAAC,QAAQ,SAAS,IAAIG,UAAuE,IAAI;AACvG,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,CAAC;AACtC,QAAM,iBAAiBD,QAAsB,IAAI;AACjD,QAAM,CAAC,cAAc,eAAe,IAAIC,UAA8B,oBAAI,IAAI,CAAC;AAC/E,QAAM,CAAC,WAAW,YAAY,IAAIA,UAA4C,CAAC,CAAC;AAEhF,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,WAAW,eAAe,YAAYG,SAAS;AACpD,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,GAAGA,QAAO,UAAU,EAAE,QAAQ,WAAW,OAAO,CAAC;AAC7E,YAAI,SAAS,WAAW,KAAK;AAC3B,yBAAe,UAAUA;AACzB;AAAA,QACF;AACA,YAAI,CAAC,SAAS,GAAI;AAClB,cAAM,OAAgB,MAAM,SAAS,KAAK;AAC1C,YAAI,CAAC,WAAW,OAAO,WAAW,MAAM,QAAQ,IAAI,EAAG,WAAU,EAAE,SAAAA,UAAS,OAAO,KAAyB,CAAC;AAAA,MAC/G,QAAQ;AAAA,MAER;AAAA,IACF,GAAG;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAACA,UAAS,SAAS,QAAQ,MAAM,OAAO,CAAC;AAE5C,EAAAH,WAAU,MAAM;AACd,oBAAgB,oBAAI,IAAI,CAAC;AACzB,iBAAa,CAAC,CAAC;AAAA,EACjB,GAAG,CAACG,QAAO,CAAC;AAEZ,QAAM,QAAQ,QAAQ,YAAYA,WAAU,OAAO,QAAQ;AAC3D,QAAM,UAAU,MAAM,KAAK,UAAU;AACrC,QAAM,CAAC,KAAK,MAAM,IAAID,UAAS,MAAM,KAAK,IAAI,CAAC;AAC/C,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,WAAO,KAAK,IAAI,CAAC;AACjB,UAAM,QAAQ,YAAY,MAAM,OAAO,KAAK,IAAI,CAAC,GAAG,GAAI;AACxD,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,SAASD;AAAA,IACb,OAAO,WAAmB;AACxB,sBAAgB,CAAC,YAAY,IAAI,IAAI,OAAO,EAAE,IAAI,MAAM,CAAC;AACzD,UAAI;AACF,cAAM,KAAK,GAAGI,QAAO,UAAU,mBAAmB,MAAM,CAAC,WAAW,EAAE,QAAQ,OAAO,CAAC;AAAA,MACxF,QAAQ;AAAA,MAER;AACA,sBAAgB,CAAC,YAAY;AAC3B,cAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,aAAK,OAAO,MAAM;AAClB,eAAO;AAAA,MACT,CAAC;AACD,gBAAU,CAAC,UAAU,QAAQ,CAAC;AAAA,IAChC;AAAA,IACA,CAACA,UAAS,IAAI;AAAA,EAChB;AAEA,QAAM,SAASJ;AAAA,IACb,OAAO,QAAgB,YAAoB,aAAmC;AAC5E,mBAAa,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,EAAE,OAAO,UAAU,EAAE,EAAE;AAC9E,UAAI,WAAW;AACf,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,GAAGI,QAAO,UAAU,mBAAmB,MAAM,CAAC,aAAa;AAAA,UACrF,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,WAAW,CAAC;AAAA,QAC/C,CAAC;AACD,mBAAW,SAAS;AAAA,MACtB,QAAQ;AACN,mBAAW;AAAA,MACb;AACA,mBAAa,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,WAAW,EAAE,OAAO,WAAW,SAAS,IAAI,EAAE,OAAO,SAAS,EAAE,EAAE;AACzH,gBAAU,CAAC,UAAU,QAAQ,CAAC;AAAA,IAChC;AAAA,IACA,CAACA,UAAS,IAAI;AAAA,EAChB;AAEA,SAAO,EAAE,OAAO,KAAK,QAAQ,cAAc,QAAQ,UAAU;AAC/D;;;ACpIO,IAAM,yBAAyB;AAE/B,IAAM,oBAAoB,CAAC,SAAoC,KAAK,SAAS;AAG7E,SAAS,eAAe,SAA0C;AACvE,SAAO,aAAa,OAAO,EAAE,OAAO,CAAC,WAAW,OAAO,SAAS,qBAAqB,OAAO,SAAS,eAAe;AACtH;AAQA,IAAM,kBAAkB;AAGjB,SAAS,aAAa,SAAwC;AACnE,SAAO,aAAa,OAAO,EAAE,QAAQ,CAAC,WAAW;AAC/C,QAAI,OAAO,SAAS,gBAAiB,QAAO,CAAC;AAC7C,UAAM,EAAE,MAAM,KAAK,IAAI;AACvB,WAAO,OAAO,SAAS,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,IAAI,CAAC;AAAA,EACxD,CAAC;AACH;AAQA,IAAM,oBAAoB;AAGnB,SAAS,eAAe,SAAyC;AACtE,SAAO,aAAa,OAAO,EACxB,OAAO,CAAC,WAAW,OAAO,SAAS,iBAAiB,EACpD,IAAI,CAAC,WAAW;AACf,UAAM,EAAE,OAAO,UAAU,IAAI;AAC7B,WAAO,EAAE,MAAM,OAAO,UAAU,WAAW,QAAQ,IAAI,GAAI,OAAO,cAAc,YAAY,aAAa,EAAE,UAAU,EAAG;AAAA,EAC1H,CAAC;AACL;AAEA,SAAS,aAAa,SAAoD;AACxE,MAAI,QAAQ,SAAS,OAAQ,QAAO,CAAC;AACrC,SAAO,QAAQ,MAAM,QAAQ,CAAC,SAAS;AACrC,QAAI,KAAK,SAAS,uBAAwB,QAAO,CAAC;AAClD,UAAM,UAAW,KAA0C,MAAM;AACjE,WAAO,MAAM,QAAQ,OAAO,IAAK,QAAQ,OAAO,aAAa,IAAgC,CAAC;AAAA,EAChG,CAAC;AACH;AAEA,SAAS,cAAc,OAAyB;AAC9C,SAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,OAAQ,MAA6B,SAAS;AACtG;AAaO,SAAS,aAAa,QAAqB,UAAoD;AACpG,QAAM,OAA6B,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,SAAS,yBAAyB,KAAK,OAAO,MAAM;AAC1H,MAAI,OAAO,OAAQ,MAAK,SAAS,OAAO;AACxC,MAAI,OAAO,MAAO,MAAK,YAAY,OAAO;AAC1C,MAAI,OAAO,SAAS,4BAA4B,CAAC,OAAO,OAAQ,QAAO;AACvE,QAAM,SAAS,OAAO;AACtB,QAAM,OAAO,SAAS,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,MAAM;AAC7D,QAAM,aAAa,OAAO,UAAU,eAAe,QAAQ,oBAAoB,IAAI;AACnF,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,WAAW,SAAS,UAAU,UAAU;AAC9C,MAAI,UAAU,UAAU,UAAW,QAAO,EAAE,GAAG,MAAM,UAAU,EAAE,OAAO,SAAS,aAAa,UAAU,YAAY,SAAS,EAAE;AAE/H,QAAM,UAAU,MAAM,WAAW,wBAAwB,oBAAoB,IAAI,KAAK,gBAAgB;AACtG,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU;AAAA,MACR,OAAO;AAAA;AAAA,MAEP,UAAU,CAAC,WAAW,SAAS,OAAO,QAAQ,YAAY,WAAW,SAAS,SAAS,OAAO;AAAA,MAC9F,SAAS,UAAU,UAAU;AAAA,MAC7B,QAAQ,UAAU,UAAU;AAAA,MAC5B,GAAI,UAAU,UAAU,YAAY,EAAE,aAAa,SAAS,YAAY;AAAA,MACxE,WAAW;AAAA,IACb;AAAA,EACF;AACF;AAEA,IAAM,cAA2F;AAAA,EAC/F,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,sBAAsB;AACxB;AAGO,SAAS,cAAc,SAAiC,QAA8B;AAC3F,SAAO,QACJ,IAAI,CAAC,WAAW;AACf,UAAM,SAAS,OAAO,YAAY,OAAO,IAAI,CAAC,KAAK,OAAO;AAC1D,WAAO,OAAO,KAAK,OAAO,SAAS,yBAAyB,KAAK,OAAO,OAAO,MAAM;AAAA,EACvF,CAAC,EACA,KAAK,IAAI;AACd;;;AC9HO,IAAM,kBAAkB;AAGxB,SAAS,kBAAkB,IAAY,MAAc,OAAgD;AAC1G,QAAM,QAAQ,CAAC,GAAG,OAAO,GAAI,KAAK,KAAK,EAAE,SAAS,IAAI,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,IAAI,CAAC,GAAI,EAAE,MAAM,iBAAiB,MAAM,KAAK,CAAC;AACpI,SAAO,EAAE,IAAI,MAAM,QAAQ,MAAM;AACnC;AAEO,SAAS,aAAa,SAA4D;AACvF,SAAO,QAAQ,SAAS,UAAU,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,eAAe;AAC9F;AAMO,SAAS,mBAAmB,MAA2C;AAC5E,SAAO,KAAK,QAAQ,CAAC,SAAS,UAAW,QAAQ,SAAS,eAAe,KAAK,QAAQ,CAAC,KAAK,aAAa,KAAK,QAAQ,CAAC,CAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAE,EAAE,KAAK,GAAG;AAC3J;AAGO,SAAS,mBAAmB,YAAgD;AACjF,SAAO,EAAE,GAAG,YAAY,OAAO,WAAW,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,eAAe,EAAE;AAClG;AAMO,SAAS,wBACd,SACA,OACA,KACA,YACoB;AACpB,QAAM,QAAQ,IAAI,IAAI,CAAC,GAAG,SAAS,GAAG,KAAK,EAAE,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACzE,SAAO,CAAC,GAAG,SAAS,GAAG,IAAI,OAAO,CAAC,YAAY,WAAW,IAAI,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;AACtG;;;AtB8PA,IAAM,gBAAmD,EAAE,KAAK,WAAW,KAAK,cAAc,KAAK,aAAa,KAAK,QAAQ,KAAK,iBAAiB;AAGnJ,IAAM,iBAAiB,CAAC,aAAa,UAAU,eAAe,OAAO;AAWrE,IAAM,mBAAmB;AAMzB,IAAM,4BAA4B;AAElC,IAAM,8BAA8B;AAEpC,IAAM,2BAA2B,KAAK;AAGtC,IAAM,iCAAiC;AAEvC,IAAM,6BAA6B;AAGnC,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,IAAM,eAAe;AACrB,IAAM,eAAe;AAMrB,SAAS,WAAW,QAAgG;AAClH,QAAM,OAAO,gBAAgB;AAC7B,QAAM,WAAW,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;AAC9C,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,QAA4B;AAChC,MAAI;AACF,YAAQ,OAAO,aAAa;AAAA,EAC9B,QAAQ;AAAA,EAER;AACA,QAAM,WAAW,SAAS,kBAAkB,KAAK;AACjD,SAAO,EAAE,OAAO,OAAO,OAAO,GAAI,YAAY,EAAE,OAAO,SAAS,GAAI,GAAG,SAAS;AAClF;AAGA,SAAS,kBAAsC;AAC7C,MAAI;AACF,WAAO,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,kBAAkB,OAAwC;AACjE,aAAW,aAAa,CAAC,OAAO,EAAE,aAAa,MAAM,YAAY,CAAC,GAAG;AACnE,QAAI;AACF,WAAK,UAAU,SAAS;AACxB,aAAO;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAwC;AACtC,QAAMC,WAAU,GAAG,GAAG,IAAI,mBAAmB,MAAM,CAAC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAIC,UAA6B,IAAI;AAC/D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAA+B,IAAI;AAC7E,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAA+B,IAAI;AAC7E,QAAM,CAAC,cAAc,eAAe,IAAIA,UAA2B,gBAAgB;AACnF,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAA6B,IAAI;AAE7D,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAyD,IAAI;AACrH,QAAM,2BAA2B,sBAAsB,YAAYD,WAAU,qBAAqB,YAAY;AAC9G,QAAM,CAAC,oBAAoB,qBAAqB,IAAIC,UAA8B,MAAS;AAC3F,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,KAAK;AAC5D,QAAM,cAAcC,aAAY,CAAC,SAA6B;AAC5D,cAAU,CAAC,YAAa,SAAS,SAAS,MAAM,QAAQ,SAAS,YAAY,MAAM,UAAU,UAAU,IAAK;AAAA,EAC9G,GAAG,CAAC,CAAC;AAEL,QAAM,cAAcA;AAAA,IAClB,CAACC,aAA2B;AAC1B,YAAM,OAAO,eAAeA,SAAQ,YAAY;AAChD,sBAAgB,CAAC,YAAa,iBAAiB,SAAS,IAAI,IAAI,UAAU,IAAK;AAC/E,kBAAY,UAAUA,SAAQ,MAAM,MAAM,CAAC;AAE3C,UAAI,OAAOA,SAAQ,oBAAoB,WAAW;AAChD,cAAM,YAAYA,SAAQ;AAC1B,gCAAwB,CAAC,YAAY,SAAS,YAAYH,YAAW,QAAQ,cAAc,YAAY,UAAU,EAAE,SAAAA,UAAS,UAAU,CAAC;AAAA,MACzI;AACA,4BAAsB,OAAOG,SAAQ,uBAAuB,YAAYA,SAAQ,qBAAqB,MAAS;AAE9G,UAAIA,SAAQ,oBAAoB,KAAM,oBAAmB,IAAI;AAAA,IAC/D;AAAA,IACA,CAACH,UAAS,WAAW;AAAA,EACvB;AACA,QAAM,mBAAmBI,QAAO,aAAa;AAC7C,mBAAiB,UAAU;AAE3B,QAAM,qBAAqBF;AAAA,IACzB,CAAC,YAAkC;AACjC,UAAI,CAAC,SAAS,QAAQ,CAAC,QAAQ,KAAM;AACrC,UAAI,QAAQ,SAAS,cAAe,aAAY,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,KAAK,CAAC;AAC7F,uBAAiB,UAAU,EAAE,MAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM,SAAS,QAAQ,KAAK,CAAC;AAAA,IAC9F;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAGA,QAAM,iBAAiBE,QAAO,WAAW;AACzC,iBAAe,UAAU;AACzB,QAAM,wBAAwBA,QAAO,KAAK;AAG1C,QAAMC,WAAUH;AAAA,IACd,OAAO,OAAe,SAAuB;AAC3C,YAAM,WAAW,OAAO,eAAe,WAAW,WAAW,OAAO,OAAO,IAAI;AAC/E,YAAM,UAAU,MAAM,UAAU,OAAO,YAAY,MAAM;AAEzD,UAAI,SAAS,WAAW,OAAO,CAAE,MAAM,aAAa,QAAQ,EAAI,YAAW,YAAY;AAAA,eAC9E,MAAM,kBAAkB,QAAQ,GAAG;AAE1C,YAAI,CAAC,MAAM,SAAS,OAAO,EAAG,YAAW,kBAAkB;AAC3D,YAAI,CAAC,sBAAsB,WAAW,cAAc,GAAG;AACrD,gCAAsB,UAAU;AAChC,kBAAQ,KAAK,oBAAoB,CAAC;AAAA,QACpC;AAAA,MACF,WAAW,SAAS,GAAI,YAAW,CAAC,YAAa,YAAY,gBAAiB,UAAU,YAAY,qBAAsB,OAAO,OAAQ;AACzI,aAAO;AAAA,IACT;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,WAAW;AAAA,IACf,kBAAkB,UAAU,oBAAoB;AAAA,IAChD,mBAAmB,UAAU,qBAAqB;AAAA,IAClD,kBAAkB,UAAU,oBAAoB;AAAA,EAClD;AACA,QAAM,cAAcE,QAAO,QAAQ;AACnC,cAAY,UAAU;AAGtB,QAAM,kBAAkBA,QAAsB,IAAI;AAElD,QAAM,iBAAiBA,QAAgC,IAAI;AAE3D,QAAM,eAAeA,QAA2E,IAAI;AAEpG,QAAM,mBAAmBA,QAA2B,MAAS;AAE7D,QAAM,kBAAkBA,QAAO,oBAAI,IAAY,CAAC;AAEhD,QAAM,kBAAkBA,QAAO,oBAAI,IAAmC,CAAC;AAEvE,QAAM,sBAAsBA,QAAsB,IAAI;AAEtD,QAAM,cAAcA,QAA2B,IAAI;AAEnD,QAAM,iBAAiBA,QAAO,KAAK;AACnC,QAAM,aAAaA,QAAOJ,QAAO;AACjC,aAAW,UAAUA;AAErB,QAAM,YAAYI,QAAO,SAAS;AAClC,YAAU,UAAU;AACpB,QAAM,aAAaA,QAAOC,QAAO;AACjC,aAAW,UAAUA;AAErB,QAAM,mBAAmBD,QAA4B,YAAY;AAAA,EAAC,CAAC;AAEnE,QAAM,qBAAqBA,QAAmB,MAAM;AAAA,EAAC,CAAC;AAEtD,QAAM,uBAAuBA,QAA+B,IAAI;AAChE,QAAM,qBAAqB,OAAO,qBAAqB,YAAY,IAAI,gBAAgB,GAAG;AAC1F,EAAAE;AAAA,IACE,MAAM,MAAM;AACV,2BAAqB,SAAS,MAAM;AACpC,2BAAqB,UAAU;AAAA,IACjC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,kBAAkBF,QAA4B,IAAI;AAExD,QAAM,0BAA0BA,QAAgC,IAAI;AAEpE,QAAM,YAAYA,QAAqB,IAAI;AAE3C,QAAM,cAAcA,QAA2B,IAAI;AAEnD,QAAM,YAAYA,QAA+B,IAAI;AAErD,QAAM,CAAC,eAAe,gBAAgB,IAAIH,UAAS,KAAK;AACxD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAA0B,QAAQ;AAEtE,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,CAAC,CAAC;AACrE,QAAM,iBAAiBG,QAAO,WAAW;AACzC,iBAAe,UAAU;AAEzB,QAAM,qBAAqBA,QAAO,oBAAI,IAAY,CAAC;AAEnD,QAAM,aAAaA,QAAsB,QAAQ,QAAQ,CAAC;AAK1D,QAAM,wBAAwBA,QAA4E,IAAI;AAE9G,QAAM,CAAC,YAAY,aAAa,IAAIH,UAAS,KAAK;AAClD,QAAM,UAAUG,QAA6B,IAAI;AACjD,QAAM,0BAA0BA,QAA4B,YAAY;AAAA,EAAC,CAAC;AAE1E,QAAM,eAAeA,QAAO,KAAK;AAEjC,QAAM,YAAYG;AAAA,IAChB,MACE,IAAI,qBAAuC;AAAA,MACzC,KAAK,GAAGP,QAAO;AAAA,MACf,OAAO,OAAO,OAAO,SAAS;AAC5B,kBAAU,UAAU;AACpB,cAAM,MAAM,iBAAiB,UAAU,MAAM,MAAM,OAAO,KAAK;AAC/D,cAAM,EAAE,kBAAkB,iBAAiB,IAAI,YAAY;AAE3D,cAAM,SAAS,MAAM,UAAU,OAAO,YAAY,MAAM,QAAQ,YAAY,UAAU;AACtF,cAAM,eAAe,MAAM,UAAU,QAAQ,YAAY,MAAM,UAAU,eAAe,KAAK,CAAC,aAAa,IAAI,SAAS,QAAQ,CAAC;AAEjI,YAAI,YAAa,kBAAiB,IAAI;AACtC,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM,kBAAkBK,UAAS,KAAK,MAAM;AAAA,YACrD,WAAW;AAAA,YACX,GAAI,SAAS,EAAE,QAAQ,MAAM,OAAO;AAAA,YACpC,SAAS,CAAC,aAAa;AACrB,oBAAM,cAAc,OAAO,SAAS,QAAQ,IAAI,gBAAgB,CAAC,KAAK;AACtE,qBAAO;AAAA,gBACL,kBAAkB,0BAA0B,kBAAkB,WAAW;AAAA;AAAA,gBAEzE,GAAI,cAAc,KAAK,EAAE,oBAAoB,iBAAiB;AAAA,gBAC9D,aAAa,MAAM;AACjB,yBAAO,OAAO,WAAW;AAEzB,sBAAI,CAAC,UAAU,QAAS,eAAc,QAAQ;AAAA,gBAChD;AAAA,gBACA,WAAW,MAAM,OAAO,OAAO,QAAQ;AAAA,cACzC;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH,SAAS,OAAO;AACd,iBAAO,OAAO,QAAQ;AACtB,gBAAM;AAAA,QACR;AACA,YAAI,SAAS,OAAO,MAAM,UAAU,QAAQ,YAAY,MAAM,UAAU,IAAI,SAAS,WAAW,GAAG;AAEjG,kCAAwB,UAAU;AAAA,QACpC;AACA,cAAM,gBAAgB,SAAS,WAAW,MAAM,MAAM,kBAAkB,QAAQ,IAAI;AACpF,YAAI,SAAS,WAAW,IAAK,iBAAgB,UAAU;AACvD,cAAM,eAAe,eAAe,SAAS,WAAW,OAAQ,MAAM,YAAY,QAAQ,MAAO;AACjG,YAAI,SAAS,MAAM,YAAa,YAAW,CAAC,YAAa,YAAY,mBAAmB,OAAO,OAAQ;AAEvG,YAAI,gBAAgB,CAAC,IAAI,SAAS,OAAO,EAAG,YAAW,gBAAgB;AAEvE,cAAM,kBAAkB,MAAM,kBAAkB,QAAQ;AAExD,cAAM,yBAAyB,SAAS,WAAW,OAAQ,MAAM,aAAa,QAAQ,MAAO;AAC7F,cAAM,eAAgB,SAAS,WAAW,OAAO,CAAC,0BAA2B,kBAAkB,QAAQ,gBAAgB;AACvH,cAAM,kBAAkB,eAAe,CAAC,eAAe,MAAM,gBAAgB,QAAQ,IAAI;AAEzF,YAAI,mBAAmB,CAAC,IAAI,SAAS,OAAO,KAAK,gBAAgB,SAAS,cAAe,kBAAiB,eAAe;AACzH,2BAAmB,eAAe;AAElC,YAAI,iBAAiB,SAAS,mBAAoB,MAAK,iBAAiB,QAAQ;AAChF,cAAMG,QAAO,YAAY;AACzB,YAAIA,SAAQ,IAAI,SAAS,OAAO,GAAG;AACjC,gBAAMC,WAAU,SAAS,KACrB,OACA,eACE,mBACA,kBACE,qBACA,kBACE,mBACC,cAAc,SAAS,MAAM,KAAK;AAC7C,cAAIA,SAAS,CAAAD,MAAK,UAAUC;AAC5B,cAAI,gBAAiB,CAAAD,MAAK,cAAc,gBAAgB;AACxD,cAAI,iBAAiB,KAAM,CAAAA,MAAK,cAAc,gBAAgB;AAC9D,cAAI,eAAe,SAAS,WAAY,CAAAA,MAAK,cAAc;AAE3D,UAAAA,MAAK,SAASC,QAAO;AAAA,QACvB;AAGA,cAAM,cAAc,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,mBAAmB,gBAAgB,iBAAiB,UAAU;AACxI,YAAI,gBAAgB,MAAM,UAAU,QAAQ,YAAY,MAAM,UAAU,IAAI,SAAS,WAAW,GAAG;AACjG,gBAAM,OAAO,MAAM,YAAY,QAAQ;AACvC,uBAAa,UAAU,EAAE,WAAW,iBAAiB,SAAS,MAAM,SAAS,WAAW,OAAO,CAAC,yBAAyB,eAAe,KAAK;AAAA,QAC/I;AACA,YAAI,SAAS,WAAW,IAAK,QAAO,OAAO,SAAS;AAAA,iBAC3C,CAAC,SAAS,MAAM,CAAC,SAAS,KAAM,QAAO,OAAO,QAAQ;AAC/D,eAAO;AAAA,MACT;AAAA;AAAA;AAAA,MAGA,4BAA4B,CAAC,EAAE,UAAAC,WAAU,QAAQ,MAAM;AACrD,cAAM,OAAOA,UAAS,GAAG,EAAE;AAC3B,yBAAiB,UAAU,MAAM;AACjC,cAAMF,QAAO,YAAY;AACzB,YAAIA,SAAQ,MAAM,OAAOA,MAAK,WAAW;AAEvC,gBAAMG,aAAY,gBAAgB,QAAQ,IAAI,KAAK,EAAE;AACrD,gBAAMC,eAAc,gBAAgB,QAAQ,IAAI,KAAK,EAAE,KAAK,CAAC;AAC7D,gBAAMC,YAAW,EAAE,GAAG,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,oBAAoB,EAAE;AACnG,iBAAO;AAAA,YACL,KAAK,GAAGb,QAAO;AAAA,YACf,MAAM,EAAE,iBAAiBQ,MAAK,iBAAiB,SAASK,WAAU,GAAIF,cAAa,EAAE,WAAW,KAAK,GAAI,GAAIA,cAAaC,aAAY,SAAS,KAAK,EAAE,aAAAA,aAAY,GAAI,GAAG,WAAW,UAAU,OAAO,EAAE;AAAA,UACzM;AAAA,QACF;AACA,YAAI,YAAY,wBAAwB,oBAAoB,SAAS;AACnE,iBAAO,EAAE,KAAK,GAAGZ,QAAO,eAAe,MAAM,EAAE,WAAW,oBAAoB,SAAS,GAAG,WAAW,UAAU,OAAO,EAAE,EAAE;AAAA,QAC5H;AACA,YAAI,YAAY,wBAAwB,MAAM,OAAO,gBAAgB,SAAS;AAC5E,iBAAO,EAAE,KAAK,GAAGA,QAAO,UAAU,MAAM,WAAW,UAAU,OAAO,EAAE;AAAA,QACxE;AACA,cAAM,YAAY,SAAS,UAAa,gBAAgB,QAAQ,IAAI,KAAK,EAAE;AAC3E,cAAM,eAAe,QAAQ,gBAAgB,QAAQ,IAAI,KAAK,EAAE,MAAM,CAAC;AAEvE,cAAM,WAAW,QAAQ,EAAE,GAAG,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,oBAAoB,EAAE;AAC3G,eAAO,EAAE,KAAK,GAAGA,QAAO,aAAa,MAAM,EAAE,SAAS,UAAU,GAAI,aAAa,EAAE,WAAW,KAAK,GAAI,GAAI,aAAa,YAAY,SAAS,KAAK,EAAE,YAAY,GAAI,GAAG,WAAW,UAAU,OAAO,EAAE,EAAE;AAAA,MACzM;AAAA;AAAA,MAEA,iCAAiC,OAAO,EAAE,KAAK,GAAGA,QAAO,UAAU;AAAA,IACrE,CAAC;AAAA,IACH,CAACA,UAASK,QAAO;AAAA,EACnB;AACA,QAAM,CAAC,UAAU,WAAW,IAAIJ,UAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,CAAC,CAAC;AACrE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,CAAC,CAAC;AAEnD,QAAM,iBAAiBG,QAA+B,IAAI;AAG1D,QAAM,kBAAkBF;AAAA,IACtB,OAAO,WAAkG;AACvG,UAAI;AACF,cAAM,WAAW,MAAM,kBAAkBG,UAAS,GAAGL,QAAO,gBAAgB,QAAW;AAAA,UACrF,WAAW,YAAY,QAAQ;AAAA,UAC/B,WAAW;AAAA,UACX;AAAA,QACF,CAAC;AACD,eAAO,SAAS,KAAO,MAAM,SAAS,KAAK,IAAkE;AAAA,MAC/G,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAACA,UAASK,QAAO;AAAA,EACnB;AAMA,QAAM,kBAAkBH,aAAY,MAAM;AACxC,mBAAe,SAAS,MAAM;AAC9B,UAAM,aAAa,IAAI,gBAAgB;AACvC,mBAAe,UAAU;AACzB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,YAAY;AAChB,YAAMY,OAAM,2BAA2B,MAAM;AAC7C,eAAS,SAAS,2BAA2B,CAAC,OAAO,WAAW,SAAS,0BAA0B,UAAU,6BAA6B;AACxI,cAAM,QAAQ,MAAM,gBAAgB,MAAM;AAC1C,YAAI,OAAO,QAAS;AACpB,YAAI,OAAO;AACT,yBAAe,MAAM,WAAW;AAChC,cAAI,CAAC,MAAM,WAAY;AAAA,QACzB;AACA,cAAMA,OAAM,6BAA6B,MAAM;AAAA,MACjD;AAAA,IACF,GAAG;AAAA,EACL,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,eAAeZ,aAAY,YAAY;AAC3C,UAAM,QAAQF;AACd,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkBK,UAAS,GAAGL,QAAO,aAAa,QAAW;AAAA,QAClF,WAAW,YAAY,QAAQ;AAAA,QAC/B,WAAW;AAAA,MACb,CAAC;AACD,UAAI,CAAC,SAAS,MAAM,WAAW,YAAY,MAAO;AAClD,gBAAW,MAAM,SAAS,KAAK,GAA+B,KAAK;AAAA,IACrE,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAACA,UAASK,QAAO,CAAC;AAGrB,QAAM,qBAAqB,MAAM;AAC/B,mBAAe,UAAU;AACzB,wBAAoB,UAAU;AAC9B,gBAAY,UAAU;AAAA,EACxB;AAGA,QAAM,cAAcH;AAAA,IAClB,OAAO,WAAiD;AACtD,UAAI;AACF,cAAM,WAAW,MAAM,kBAAkBG,UAASL,UAAS,QAAW;AAAA,UACpE,WAAW,YAAY,QAAQ;AAAA,UAC/B,WAAW;AAAA,UACX,GAAI,UAAU,EAAE,OAAO;AAAA,QACzB,CAAC;AACD,YAAI,SAAS,WAAW,IAAK,QAAO;AAEpC,cAAM,gBAAgB,SAAS,KAAK,OAAO,MAAM,gBAAgB,QAAQ;AACzE,YAAI,eAAe,SAAS,kBAAkB;AAC5C,2BAAiB,aAAa;AAC9B,6BAAmB,aAAa;AAChC,iBAAO;AAAA,QACT;AACA,YAAI,SAAS,WAAW,IAAK,QAAO;AACpC,YAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,yBAAiB,IAAI;AACrB,eAAQ,MAAM,SAAS,KAAK;AAAA,MAC9B,SAAS,OAAO;AACd,eAAO,kBAAkB,KAAK,IAAI,gBAAgB;AAAA,MACpD;AAAA,IACF;AAAA,IACA,CAAC,oBAAoBA,UAASK,QAAO;AAAA,EACvC;AACA,mBAAiB,UAAU,YAAY;AACrC,UAAM,QAAQL;AACd,UAAMG,WAAU,MAAM,YAAY;AAClC,QAAI,OAAOA,aAAY,YAAY,WAAW,YAAY,MAAO,aAAYA,QAAO;AAAA,EACtF;AACA,QAAM,CAAC,YAAY,aAAa,IAAIF,UAAS,KAAK;AAClD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAA8B,IAAI;AAClE,QAAM,YAAY,UAAU,UAAU;AAGtC,QAAM,kBAAkBC,aAAY,YAAY;AAC9C,QAAI,CAAC,eAAgB;AACrB,UAAM,QAAQF;AACd,QAAI;AACF,YAAM,WAAW,MAAM,kBAAkBK,UAAS,GAAGL,QAAO,aAAa,QAAW;AAAA,QAClF,WAAW,YAAY,QAAQ;AAAA,QAC/B,WAAW;AAAA,MACb,CAAC;AACD,UAAI,CAAC,SAAS,MAAM,WAAW,YAAY,MAAO;AAClD,kBAAY,eAAgB,MAAM,SAAS,KAAK,CAA0C,CAAC;AAAA,IAC7F,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAACA,UAASK,UAAS,cAAc,CAAC;AAGrC,QAAM,OAAO,QAA0B;AAAA,IACrC,IAAI;AAAA,IACJ;AAAA,IACA,SAAS,CAAC,UAAU;AAClB,gBAAU,UAAU;AAAA,IACtB;AAAA;AAAA;AAAA,IAGA,QAAQ,CAAC,SAAS;AAChB,UAAI,KAAK,SAAS,wBAAwB;AACxC,cAAM,SAAS,UAAU;AACzB,YAAI,QAAQ;AACV,eAAK,eAAe,KAAK,MAA4B;AAAA,YACnD;AAAA,YACA,SAAS,WAAW;AAAA,YACpB,SAAS,WAAW;AAAA,YACpB,QAAQ,mBAAmB;AAAA,YAC3B,cAAc,MAAM,mBAAmB,QAAQ;AAAA,UACjD,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,UAAI,KAAK,SAAS,4BAA4B;AAC5C,2BAAmB,IAAI;AACvB;AAAA,MACF;AACA,UAAI,KAAK,SAAS,uBAAuB;AACvC,cAAM,UAAU,UAAU,KAAK,IAAI;AACnC,YAAI,QAAS,aAAY,OAAO;AAChC;AAAA,MACF;AACA,UAAI,KAAK,SAAS,qBAAsB;AACxC,YAAM,EAAE,KAAK,QAAQ,IAAI,KAAK;AAC9B,UAAI,KAAK,OAAO,OAAW,WAAU,SAAS,YAAY,EAAE,IAAI,KAAK,IAAI,KAAK,GAAI,YAAY,UAAa,EAAE,QAAQ,EAAG,CAAC;AAAA,IAC3H;AAAA,IACA,UAAU,CAAC,EAAE,UAAU,UAAU,QAAQ,MAAM;AAC7C,mBAAa,UAAU;AAEvB,YAAMK,YAAW,qBAAqB,QAAQ;AAE9C,UAAI,yBAAyBA,SAAQ,MAAMA,UAAU,aAAY,CAAC,YAAY,yBAAyB,OAAO,CAAC;AAG/G,YAAMK,wBAAuB,MAAM;AACjC,YAAI,eAAe,QAAQ,SAAS,EAAG,MAAK,wBAAwB,QAAQ;AAAA,MAC9E;AACA,YAAMN,WAAU,gBAAgB;AAChC,UAAIA,UAAS;AACX,wBAAgB,UAAU;AAE1B,YAAI,YAAY,SAAS,QAAS,aAAY,EAAE,OAAO,aAAa,MAAMA,SAAQ,CAAC;AAAA,YAC9E,MAAK,oBAAoBA,UAASC,SAAQ;AAC/C,QAAAK,sBAAqB;AACrB;AAAA,MACF;AAEA,UAAI,YAAY,SAAS,WAAW,aAAa,SAAS;AACxD,QAAAA,sBAAqB;AACrB;AAAA,MACF;AAGA,UAAI,WAAW,cAAc,iBAAiBL,SAAQ,CAAC,GAAG;AACxD,2BAAmB;AAEnB,YAAI,iBAAiB,WAAW,cAAc,iBAAiBA,SAAQ,CAAC,EAAG,iBAAgB,QAAQ,OAAO,iBAAiB,OAAO;AAElI,YAAI,uBAAuBA,SAAQ,EAAG,MAAK,kBAAkB,aAAa,QAAQ,MAAM;AACxF,aAAK,gBAAgB;AACrB,YAAI,eAAe,SAAS;AAC1B,yBAAe,UAAU;AACzB,eAAK,aAAa;AAAA,QACpB;AAEA,YAAI,CAAC,WAAW,iBAAiBA,SAAQ,MAAM,WAAY,iBAAgB;AAC3E,QAAAK,sBAAqB;AACrB;AAAA,MACF;AACA,YAAM,QAAQ,UAAU;AAExB,kBAAY,EAAE,YAAY,UAAU,QAAQ,CAAC,kBAAkB,KAAK,EAAE,CAAC;AAAA,IACzE;AAAA,EACF,CAAC;AACD,QAAM,EAAE,aAAa,YAAY,cAAc,QAAQ,MAAM,SAAS,IAAI;AAG1E,QAAM,gBAAgBR,SAAQ,MAAM,qBAAqB,KAAK,QAAQ,GAAG,CAAC,KAAK,QAAQ,CAAC;AACxF,QAAM,kBAAkB,KAAK;AAC7B,QAAM,cAAcL;AAAA,IAClB,CAAC,WACC,gBAAgB,OAAO,WAAW,aAAa,CAAC,YAAY,OAAO,qBAAqB,OAAO,CAAC,IAAI,MAAM;AAAA,IAC5G,CAAC,eAAe;AAAA,EAClB;AACA,QAAM,cAAcE,QAAO,aAAa;AACxC,cAAY,UAAU;AACtB,QAAM,CAAC,QAAQ,SAAS,IAAIH,UAAS,KAAK;AAE1C,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAwB,IAAI;AAEpE,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,KAAK;AAE9D,EAAAK,WAAU,MAAM,oBAAoB,KAAK,GAAG,CAAC,MAAM,CAAC;AACpD,QAAM,CAAC,YAAY,aAAa,IAAIL,UAAS,KAAK;AAClD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAiC,IAAI;AAErF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAEhD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAE9C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AAEtE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,CAAC;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,YAAY;AAC/C,QAAM,UAAUG,QAAO,KAAK;AAM5B,QAAM,sBAAsBF;AAAA,IAC1B,OAAOO,UAAuB,UAA8B;AAC1D,yBAAmB;AACnB,sBAAgB,UAAU;AAC1B,kBAAY,EAAE,OAAO,aAAa,MAAMA,SAAQ,CAAC;AACjD,YAAMN,WAAU,MAAM,YAAY;AAClC,UAAI,WAAW,YAAYH,SAAS;AACpC,YAAM,SAAS,OAAOG,aAAY,WAAW,OAAOA,SAAQ;AAC5D,YAAM,OAAO,MAAM,GAAG,EAAE;AACxB,YAAM,YAAY,MAAM,SAAS,UAAU,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,YAAY,QAAQ,OAAO,KAAK,EAAE;AACnG,UAAI,WAAW;AACb,cAAM,OAAO,YAAY,IAAI;AAC7B,iBAAS,CAAC,YAAa,QAAQ,KAAK,EAAE,SAAS,IAAI,UAAU,IAAK;AAAA,MACpE;AACA,UAAI,OAAOA,aAAY,SAAU,aAAYA,QAAO;AACpD,UAAI,OAAQ,aAAY,MAAM;AAAA,eACrB,UAAW,aAAY,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,IACpD;AAAA,IACA,CAAC,aAAaH,UAAS,aAAa,WAAW;AAAA,EACjD;AAEA,EAAAM,WAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,kBAAY,IAAI;AAChB;AAAA,IACF;AACA,SAAK,gBAAgB;AAAA,EACvB,GAAG,CAAC,QAAQ,eAAe,CAAC;AAG5B,EAAAA,WAAU,MAAM;AACd,QAAI,UAAU,UAAU,YAAa;AACrC,UAAM,OAAO,kBAAkB,SAAS,MAAM,KAAK,IAAI,CAAC;AACxD,QAAI,SAAS,KAAM;AACnB,UAAM,QAAQ,WAAW,MAAM,KAAK,gBAAgB,GAAG,IAAI;AAC3D,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,iBAAiB,QAAQ,CAAC;AAG9B,QAAM,kBAAkBJ;AAAA,IACtB,CAAC,WACC,IAAI,QAAuB,CAAC,YAAY;AACtC,YAAM,QAAqB;AAAA,QACzB;AAAA,QACA,QAAQ,CAAC,YAAY;AACnB,cAAI,YAAY,YAAY,MAAO,aAAY,UAAU;AACzD,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AACA,UAAI,OAAO,SAAS;AAClB,gBAAQ,QAAQ;AAChB;AAAA,MACF;AACA,kBAAY,UAAU;AACtB,aAAO,iBAAiB,SAAS,MAAM,MAAM,OAAO,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AAC7E,gBAAU,UAAU;AAEpB,WAAK,aAAa,EAAE;AAAA,QAClB,MAAM,MAAM,OAAO,UAAU,UAAU,WAAW,WAAW;AAAA,QAC7D,MAAM,MAAM,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACF,CAAC;AAAA,IACH,CAAC,YAAY;AAAA,EACf;AAOA,QAAM,iBAAiBA;AAAA,IACrB,CAACC,aAA+C;AAC9C,YAAM,SAAS,YAAY,QAAQ,KAAK,CAAC,YAAY,QAAQ,OAAO,gBAAgB,OAAO;AAC3F,UAAI,CAAC,OAAQ,QAAOA,SAAQ;AAC5B,YAAM,SAASA,SAAQ,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,OAAO,EAAE;AAC1E,UAAI,CAAC,UAAU,CAACA,SAAQ,QAAS,QAAO,CAAC,GAAGA,SAAQ,UAAU,MAAM;AACpE,sBAAgB,UAAU;AAC1B,oBAAc,KAAK;AACnB,UAAI,CAAC,OAAQ,UAAS,CAAC,YAAa,QAAQ,KAAK,EAAE,SAAS,IAAI,UAAU,YAAY,MAAM,CAAE;AAC9F,aAAOA,SAAQ;AAAA,IACjB;AAAA,IACA,CAAC;AAAA,EACH;AAMA,QAAM,qBAAqBD;AAAA,IACzB,OAAOC,UAAwB,QAA4B,WAA0C;AACnG,kBAAYA,QAAO;AACnB,YAAM,QAAQ,eAAeA,QAAO;AACpC,kBAAYA,SAAQ,UAAU,6BAA6B,uBAAuB,KAAK,CAAC,IAAI,KAAK;AACjG,qBAAe,CAAC,YAAY;AAC1B,cAAM,OAAO,wBAAwBA,SAAQ,mBAAmB,CAAC,GAAG,OAAO,SAAS,mBAAmB,OAAO;AAE9G,eAAO,KAAK,WAAW,QAAQ,UAAU,KAAK,MAAM,CAAC,SAAS,UAAU,YAAY,QAAQ,KAAK,CAAC,IAAI,UAAU;AAAA,MAClH,CAAC;AACD,qBAAeA,SAAQ,eAAe,CAAC,CAAC;AACxC,eAASA,SAAQ,SAAS,CAAC,CAAC;AAC5B,uBAAiBA,SAAQ,mBAAmB,OAAO,CAAC,UAAU,SAAS,KAAK,IAAI,IAAI,IAAI;AACxF,UAAI,CAACA,SAAQ,QAAS,QAAO;AAE7B,YAAM,UAAU,MAAM,gBAAgB,MAAM;AAC5C,UAAI,YAAY,YAAa,QAAO;AACpC,UAAI,YAAY,WAAW;AAEzB,cAAMa,YAAW,MAAM,YAAY,MAAM;AACzC,YAAIA,cAAa,aAAa;AAC5B,sBAAY,IAAI;AAChB,iBAAO;AAAA,QACT;AACA,YAAI,OAAOA,cAAa,UAAU;AAChC,sBAAYA,SAAQ;AACpB,sBAAYA,UAAS,QAAQ;AAC7B,yBAAeA,UAAS,eAAe,CAAC,CAAC;AACzC,mBAASA,UAAS,SAAS,CAAC,CAAC;AAC7B,iBAAO;AAAA,QACT;AAAA,MACF;AACA,kBAAY,MAAM;AAClB,aAAO;AAAA,IACT;AAAA,IACA,CAAC,aAAa,aAAa,iBAAiB,aAAa,cAAc;AAAA,EACzE;AAGA,QAAM,wBAAwBd,aAAY,MAAM;AAC9C,UAAM,WAAW,eAAe;AAChC,uBAAmB;AACnB,gBAAY,CAAC,YAAY;AACvB,YAAM,OAAO,QAAQ,GAAG,EAAE;AAC1B,UAAI,MAAM,SAAS,aAAa;AAC9B,eAAO,cAAc,KAAK,UAAU,MAAM,IAAI,UAAU,qBAAqB,SAAS,aAAa;AAAA,MACrG;AACA,aAAO,WAAW,CAAC,GAAG,SAAS,QAAQ,IAAI;AAAA,IAC7C,CAAC;AAAA,EACH,GAAG,CAAC,WAAW,CAAC;AAGhB,QAAM,mBAAmBA;AAAA,IACvB,OAAO,WAA0C;AAC/C,YAAM,SAAS,YAAY;AAC3B,YAAMC,WAAU,MAAM,YAAY,MAAM;AACxC,UAAI,OAAO,QAAS,QAAO;AAC3B,UAAIA,aAAY,aAAa;AAC3B,oBAAY,IAAI;AAChB,eAAO;AAAA,MACT;AACA,UAAIA,aAAY,gBAAgBA,aAAY,kBAAkB;AAE5D,8BAAsB;AACtB,eAAO;AAAA,MACT;AACA,UAAI,OAAOA,aAAY,SAAU,QAAO;AACxC,YAAM,OAAO,OAAO,GAAG,EAAE;AACzB,UAAI,MAAM,SAAS,UAAU,CAACA,SAAQ,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,KAAK,EAAE,GAAG;AAExF,wBAAgB,UAAU,KAAK;AAC/B,sBAAc,IAAI;AAClB,eAAO;AAAA,MACT;AACA,YAAM,YAAY,MAAM,mBAAmBA,UAAS,QAAQ,MAAM;AAClE,UAAI,UAAW,oBAAmB;AAClC,aAAO;AAAA,IACT;AAAA,IACA,CAAC,oBAAoB,aAAa,qBAAqB;AAAA,EACzD;AAGA,QAAM,SAASD,aAAY,MAAM;AAC/B,QAAI,QAAQ,QAAS;AACrB,YAAQ,UAAU;AAClB,UAAM,OAAO,YAAY,QAAQ,GAAG,EAAE;AACtC,mBAAe,UAAU,MAAM,SAAS,cAAc,OAAO;AAC7D,QAAI,MAAM,SAAS,YAAa,gBAAe,UAAU;AACzD,kBAAc,KAAK;AACnB,iBAAa,UAAU;AACvB,SAAK,WAAW,EAAE,QAAQ,MAAM;AAC9B,cAAQ,UAAU;AAClB,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAEhB,mBAAa,UAAU;AACvB,yBAAmB;AACnB,sBAAgB,UAAU;AAC1B,YAAM,WAAW,YAAY,QAAQ,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU,SAAS;AACnF,kBAAY,CAAC,YAAY,QAAQ,OAAO,CAAC,SAAS,KAAK,OAAO,UAAU,SAAS,CAAC;AAClF,UAAI,UAAU,SAAS,QAAQ;AAC7B,cAAM,OAAO,YAAY,QAAQ;AACjC,iBAAS,CAAC,YAAa,QAAQ,WAAW,IAAI,OAAO,OAAQ;AAAA,MAC/D;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,WAAW,CAAC;AAG5B,QAAM,SAASA,aAAY,MAAM;AAC/B,0BAAsB;AACtB,kBAAc,SAAS;AAAA,EACzB,GAAG,CAAC,qBAAqB,CAAC;AAO1B,QAAM,cAAcA;AAAA,IAClB,CAAC,UAA4D,CAAC,MAAM;AAClE,UAAI,UAAU,QAAS;AACvB,YAAM,aAAa,IAAI,gBAAgB;AACvC,gBAAU,UAAU;AACpB,uBAAiB,IAAI;AACrB,UAAI,CAAC,QAAQ,WAAY,eAAc,cAAc;AACrD,WAAK,qBAAqB;AAAA,QACxB,UAAU,YAAY,QAAQ;AAAA,QAC9B,kBAAkB,YAAY,QAAQ;AAAA,QACtC,QAAQ,WAAW;AAAA,QACnB,SAAS,OAAO,QAAQ,UAAU;AAChC,gBAAM,YAAY,MAAM,iBAAiB,MAAM;AAC/C,cAAI,CAAC,aAAa,QAAQ,cAAc,UAAU,KAAK,CAAC,OAAO,QAAS,eAAc,cAAc;AACpG,iBAAO;AAAA,QACT;AAAA,MACF,CAAC,EAAE,KAAK,CAAC,YAAY;AACnB,YAAI,UAAU,YAAY,WAAY;AACtC,kBAAU,UAAU;AACpB,yBAAiB,KAAK;AACtB,YAAI,YAAY,aAAa;AAC3B,iBAAO;AACP;AAAA,QACF;AACA,YAAI,YAAY,YAAa;AAC7B,sBAAc,QAAQ;AACtB,cAAM,OAAO,YAAY,QAAQ,GAAG,EAAE;AACtC,YAAI,QAAQ,gBAAgB,MAAM,SAAS,UAAU,KAAK,OAAO,gBAAgB,QAAS,QAAO;AAAA,MACnG,CAAC;AAAA,IACH;AAAA,IACA,CAAC,QAAQ,kBAAkB,MAAM;AAAA,EACnC;AAGA,QAAM,eAAeA,aAAY,MAAM;AACrC,cAAU,SAAS,MAAM;AACzB,cAAU,UAAU;AACpB,qBAAiB,KAAK;AACtB,kBAAc,QAAQ;AAAA,EACxB,GAAG,CAAC,CAAC;AAGL,QAAM,cAAcE,QAAO,EAAE,QAAQ,aAAa,oBAAoB,aAAa,gBAAgB,CAAC;AACpG,cAAY,UAAU,EAAE,QAAQ,aAAa,oBAAoB,aAAa,gBAAgB;AAC9F,EAAAE,WAAU,MAAM;AACd,UAAM,EAAE,QAAAW,SAAQ,aAAAC,cAAa,oBAAAC,qBAAoB,aAAAC,cAAa,iBAAAC,iBAAgB,IAAI,YAAY;AAC9F,UAAM,aAAa,IAAI,gBAAgB;AACvC,cAAU,KAAK;AACf,kBAAc,KAAK;AAEnB,oBAAgB,UAAU;AAC1B,kBAAc,KAAK;AACnB,gBAAY,KAAK;AACjB,eAAW,IAAI;AACf,qBAAiB,IAAI;AACrB,qBAAiB,IAAI;AACrB,wBAAoB,IAAI;AACxB,0BAAsB,MAAS;AAC/B,uBAAmB,KAAK;AACxB,oBAAgB,gBAAgB;AAChC,cAAU,IAAI;AACd,mBAAe,CAAC,CAAC;AACjB,aAAS,CAAC,CAAC;AACX,mBAAe,CAAC,CAAC;AACjB,mBAAe,SAAS,MAAM;AAC9B,UAAM,YAAY;AAChB,YAAMlB,WAAU,MAAMe,aAAY,WAAW,MAAM;AACnD,UAAI,WAAW,OAAO,QAAS;AAC/B,UAAIf,aAAY,eAAe;AAC7B,sBAAc,SAAS;AACvB;AAAA,MACF;AACA,oBAAc,QAAQ;AACtB,UAAIA,aAAY,aAAa;AAC3B,oBAAY,IAAI;AAChB;AAAA,MACF;AAEA,UAAIA,aAAY,gBAAgBA,aAAY,iBAAkB;AAC9D,UAAIA,aAAY,gBAAgB;AAC9B,sBAAc,IAAI;AAClB;AAAA,MACF;AACA,0BAAoB,cAAcA,QAAO,CAAC;AAC1C,sBAAgBc,OAAM;AACtB,gBAAU,IAAI;AACd,UAAId,SAAQ,WAAY,CAAAkB,iBAAgB;AAExC,UAAIlB,SAAQ,QAAS,aAAY,IAAI;AACrC,UAAI;AACF,cAAM,YAAY,MAAMgB,oBAAmBhB,UAAS,CAAC,GAAG,WAAW,MAAM;AACzE,YAAI,CAAC,aAAa,CAAC,WAAW,OAAO,QAAS,CAAAiB,aAAY;AAAA,MAC5D,UAAE;AACA,YAAI,CAAC,WAAW,OAAO,QAAS,aAAY,KAAK;AAAA,MACnD;AAAA,IACF,GAAG;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAACpB,UAAS,WAAW,CAAC;AAGzB,EAAAM,WAAU,MAAM,cAAc,CAAC,cAAc,MAAM,CAAC;AAEpD,EAAAA,WAAU,MAAM,MAAM,eAAe,SAAS,MAAM,GAAG,CAAC,MAAM,CAAC;AAG/D,QAAM,eAAeJ,aAAY,MAAM;AACrC,QAAI,CAAC,QAAQ;AACX,oBAAc,cAAc;AAC5B,qBAAe,CAAC,YAAY,UAAU,CAAC;AACvC;AAAA,IACF;AACA,gBAAY,EAAE,cAAc,KAAK,CAAC;AAAA,EACpC,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,EAAAI,WAAU,MAAM;AACd,QAAI,eAAe,UAAW;AAC9B,UAAM,SAAS,MAAM;AACnB,UAAI,SAAS,oBAAoB,SAAU,cAAa;AAAA,IAC1D;AACA,WAAO,iBAAiB,UAAU,MAAM;AACxC,aAAS,iBAAiB,oBAAoB,MAAM;AACpD,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,MAAM;AAC3C,eAAS,oBAAoB,oBAAoB,MAAM;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,YAAY,YAAY,CAAC;AAE7B,QAAM,YAAY,WAAW,eAAe,WAAW;AACvD,eAAa,UAAU;AACvB,QAAM,OAAO,YAAY,iBAAiB,cAAc,eAAe,kBAAkB;AAEzF,QAAM,UAAUF,QAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,YAAY,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,MAAM,KAAK,EAAE,SAAS;AAEpF,QAAM,WAAW,YAAY,aAAa,SAAS,UAAU,aAAa,CAAC,iBAAiB,eAAe,YAAY,CAAC,aAAa,CAAC;AAEtI,QAAM,WACJ,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU,aAAa,SAAS,eAAe,YAAY,CAAC,eAAe,SAAS,kBAAkB,cAAc,GAAG,EAAE,CAAC;AAE9J,QAAM,eAAeA,QAAO,IAAI,gBAAgB,CAAC;AACjD,EAAAE,WAAU,MAAM;AACd,iBAAa,UAAU,IAAI,gBAAgB;AAC3C,WAAO,MAAM,aAAa,QAAQ,MAAM;AAAA,EAC1C,GAAG,CAAC,MAAM,CAAC;AACX,QAAM,CAAC,mBAAmB,oBAAoB,IAAIL,UAAwB,IAAI;AAC9E,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAA0E,IAAI;AAC5H,QAAM,YAAYG,QAAO,KAAK;AAG9B,QAAM,eAAeA,QAAO,EAAE,SAAS,GAAG,OAAO,EAAE,CAAC;AAOpD,QAAM,oBAAoBF;AAAA,IACxB,OAAO,QAAqB,UAA2C,CAAC,MAAwB;AAC9F,YAAM,QAAQ,YAAY;AAC1B,YAAM,OAAO,EAAE,aAAa,QAAQ;AACpC,YAAM,cAAc,aAAa,QAAQ;AACzC,YAAMC,WAAU,MAAM,YAAY,MAAM;AACxC,UAAI,OAAO,QAAS,QAAO;AAC3B,UAAI,QAAQ,mBAAmB;AAC7B,YAAI,QAAQ,WAAW,aAAa,QAAQ,QAAQ,KAAM,QAAO;AACjE,YAAI,YAAY,YAAY,SAAS,aAAa,QAAQ,UAAU,YAAa,QAAO;AAAA,MAC1F;AACA,UAAI,OAAOA,aAAY,SAAU,cAAa,QAAQ,QAAQ,KAAK,IAAI,aAAa,QAAQ,OAAO,IAAI;AACvG,UAAIA,aAAY,aAAa;AAC3B,oBAAY,IAAI;AAChB,eAAO;AAAA,MACT;AAEA,UAAIA,aAAY,iBAAiBA,aAAY,eAAgB,QAAO;AACpE,UAAI,OAAOA,aAAY,SAAU,QAAO;AACxC,YAAM,YAAY,MAAM,mBAAmBA,UAAS,YAAY,SAAS,MAAM;AAC/E,UAAI,CAAC,aAAa,CAAC,OAAO,QAAS,aAAY;AAC/C,aAAO;AAAA,IACT;AAAA,IACA,CAAC,oBAAoB,aAAa,WAAW;AAAA,EAC/C;AAMA,QAAM,gBAAgBD;AAAA,IACpB,OAAO,WAAwB;AAC7B,YAAM,OAAO,MAAM,kBAAkB,MAAM;AAC3C,UAAI,CAAC,QAAQ,CAAC,OAAO,QAAS,aAAY,EAAE,YAAY,KAAK,CAAC;AAAA,IAChE;AAAA,IACA,CAAC,mBAAmB,WAAW;AAAA,EACjC;AAEA,qBAAmB,UAAU,MAAM;AACjC,QAAI,CAAC,QAAQ,QAAS,MAAK,cAAc,aAAa,QAAQ,MAAM;AAAA,EACtE;AAEA,QAAM,iBAAiBE,QAAO,SAAS;AACvC,EAAAE,WAAU,MAAM;AACd,QAAI,eAAe,YAAY,UAAW;AAC1C,mBAAe,UAAU;AACzB,SAAK,kBAAkB,aAAa,QAAQ,MAAM;AAAA,EACpD,GAAG,CAAC,WAAW,iBAAiB,CAAC;AAIjC,EAAAA,WAAU,MAAM;AACd,QAAI,kBAAkB,QAAQ,KAAM;AACpC,UAAM,SAAS,aAAa,QAAQ;AACpC,UAAM,WAAW,gBAAgB;AACjC,UAAM,QAAQ;AAAA,MACZ,MAAM;AACJ,YAAI,KAAK,IAAI,KAAK,UAAU;AAC1B,2BAAiB,IAAI;AACrB,sBAAY,CAAC,YAAa,cAAc,iBAAiB,OAAO,CAAC,IAAI,UAAU,qBAAqB,SAAS,aAAa,CAAE;AAC5H;AAAA,QACF;AACA,aAAK,kBAAkB,QAAQ,EAAE,mBAAmB,KAAK,CAAC,EAAE,QAAQ,MAAM;AACxE,cAAI,CAAC,OAAO,QAAS,mBAAkB,CAAC,UAAU,QAAQ,CAAC;AAAA,QAC7D,CAAC;AAAA,MACH;AAAA,MACA,KAAK,IAAI,gCAAgC,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC;AAAA,IAC7E;AACA,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,MAAM,gBAAgB,eAAe,mBAAmB,WAAW,CAAC;AAExE,EAAAA,WAAU,MAAM,iBAAiB,IAAI,GAAG,CAAC,MAAM,CAAC;AAGhD,QAAM,oBAAoBJ;AAAA,IACxB,OAAO,YAAoB,aAA+B;AACxD,UAAI,UAAU,QAAS;AACvB,gBAAU,UAAU;AACpB,YAAM,SAAS,aAAa,QAAQ;AACpC,yBAAmB,IAAI;AACvB,2BAAqB,UAAU;AAC/B,UAAI;AACF,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM;AAAA,YACfG;AAAA,YACA,GAAGL,QAAO,cAAc,mBAAmB,UAAU,CAAC;AAAA,YACtD,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,EAAE,UAAU,GAAG,WAAW,UAAU,OAAO,EAAE,CAAC,EAAE;AAAA,YACxI,EAAE,WAAW,YAAY,QAAQ,kBAAkB,WAAW,KAAK;AAAA,UACrE;AAAA,QACF,QAAQ;AACN,6BAAmB,EAAE,IAAI,YAAY,MAAM,aAAa,CAAC;AACzD;AAAA,QACF;AAEA,cAAM,eAAe,SAAS,KAAK,OAAO,MAAM,gBAAgB,QAAQ;AACxE,YAAI,cAAc,MAAM;AACtB,6BAAmB,YAAY;AAC/B,6BAAmB,EAAE,IAAI,YAAY,MAAM,WAAW,MAAM,aAAa,KAAK,CAAC;AAC/E;AAAA,QACF;AACA,YAAI,SAAS,WAAW,KAAK;AAE3B,gBAAMS,WAAU,MAAM,kBAAkB,QAAQ;AAChD,cAAIA,YAAW,CAAC,OAAO,QAAS,aAAY,EAAE,OAAO,aAAa,MAAMA,SAAQ,CAAC;AACjF,gBAAM,eAAe,CAACA,YAAY,MAAM,YAAY,QAAQ,MAAO;AACnE,6BAAmB,EAAE,IAAI,YAAY,MAAMA,WAAU,mBAAmB,eAAe,mBAAmB,aAAa,CAAC;AACxH;AAAA,QACF;AAEA,YAAI,SAAS,WAAW,OAAQ,MAAM,YAAY,QAAQ,MAAO,QAAQ;AACvE,6BAAmB,EAAE,IAAI,YAAY,MAAM,OAAO,CAAC;AACnD;AAAA,QACF;AAEA,YAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,gBAAM,cAAc,MAAM;AAC1B;AAAA,QACF;AACA,YAAI,CAAC,SAAS,IAAI;AAChB,6BAAmB,EAAE,IAAI,YAAY,MAAM,aAAa,CAAC;AACzD;AAAA,QACF;AACA,cAAM,EAAE,QAAQ,IAAK,MAAM,SAAS,KAAK;AACzC,oBAAY,CAAC,YAAY,qBAAqB,SAAS,YAAY,UAAU,OAAO,CAAC;AACrF,YAAI,CAAC,QAAS;AACd,oBAAY,IAAI;AAChB,YAAI;AACF,gBAAM,UAAU,MAAM,gBAAgB,MAAM;AAC5C,cAAI,YAAY,UAAW,OAAM,cAAc,MAAM;AAAA,mBAC5C,YAAY,YAAY,CAAC,OAAO,QAAS,aAAY;AAAA,QAChE,UAAE;AACA,cAAI,CAAC,OAAO,QAAS,aAAY,KAAK;AAAA,QACxC;AAAA,MACF,UAAE;AACA,kBAAU,UAAU;AACpB,YAAI,CAAC,OAAO,QAAS,sBAAqB,IAAI;AAAA,MAChD;AAAA,IACF;AAAA,IACA,CAAC,oBAAoBT,UAASK,UAAS,eAAe,iBAAiB,aAAa,WAAW;AAAA,EACjG;AAEA,QAAM,EAAE,cAAc,iBAAiB,eAAe,gBAAgB,IAAI,gBAAgB;AAAA,IACxF,SAAAL;AAAA,IACA,SAAAK;AAAA,IACA,kBAAkB,MAAM,YAAY,QAAQ;AAAA,IAC5C,YAAY,MAAM,WAAW,UAAU,OAAO;AAAA,IAC9C,YAAY,MAAM,aAAa,QAAQ;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,CAAC,eAAe;AAClC,YAAM,SAAS,YAAY,QAAQ,KAAK,CAAC,YAAY,QAAQ,MAAM,KAAK,CAAC,SAAU,KAAkC,eAAe,UAAU,CAAC;AAC/I,UAAI,OAAQ,yBAAwB,OAAO,EAAE;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,uBAAuB,CAAC,SAAS,YAAY,EAAE,OAAO,aAAa,KAAK,CAAC;AAAA,IACzE;AAAA,EACF,CAAC;AAED,QAAM,kBAAkBH;AAAA,IACtB,MAAM,QAAQ,WAAW,QAAQ,WAAW,UAAU,WAAW,gBAAgB;AAAA,IACjF,CAAC,eAAe;AAAA,EAClB;AAOA,QAAM,gBAAgBE,QAAO,UAAU;AACvC,gBAAc,UAAU;AACxB,QAAM,CAAC,cAAc,eAAe,IAAIH,UAAS,CAAC;AAElD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAuE,IAAI;AAC3H,QAAM,mBAAmBG,QAAO,aAAa;AAC7C,mBAAiB,UAAU;AAC3B,QAAM,sBAAsBA,QAAsB,IAAI;AACtD,QAAM,iBAAiBA,QAAO,UAAU;AACxC,iBAAe,UAAU;AACzB,EAAAE,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,QAAQ,YAAY,oBAAoB,YAAYN,SAAS;AAC5E,UAAM,aAAa,aAAa,QAAQ;AACxC,UAAM,QAAQ,IAAI,gBAAgB;AAClC,UAAM,YAAY,MAAM,MAAM,MAAM;AACpC,eAAW,iBAAiB,SAAS,WAAW,EAAE,MAAM,KAAK,CAAC;AAC9D,SAAK,UAAU;AAAA,MACb,KAAK,GAAGA,QAAO;AAAA,MACf,OAAOK;AAAA,MACP,QAAQ,MAAM;AAAA,MACd,UAAU,YAAY,QAAQ;AAAA,MAC9B,kBAAkB,YAAY,QAAQ;AAAA,MACtC,QAAQ,MAAM;AACZ,wBAAgB,CAAC,YAAY,UAAU,CAAC;AACxC,YAAI,CAAC,gBAAgB,KAAK,cAAc,YAAY,UAAW,MAAK,kBAAkB,YAAY,EAAE,mBAAmB,KAAK,CAAC;AAAA,MAC/H;AAAA,MACA,SAAS,CAAC,UAAU;AAClB,YAAI,MAAM,SAAS,gBAAiB,iBAAgB,CAAC,YAAY,UAAU,CAAC;AAAA,iBACnE,MAAM,SAAS,cAAe,aAAY,UAAU,MAAM,MAAM,CAAC;AAAA,iBACjE,MAAM,SAAS,gBAAiB,aAAY,IAAI;AAAA,iBAChD,MAAM,SAAS,iBAAiB;AACvC,gBAAM,SAAS,iBAAiB,MAAM,KAAK;AAC3C,cAAI,OAAQ,qBAAoB,EAAE,QAAQ,MAAM,iBAAiB,QAAQ,CAAC;AAAA,QAC5E,WACS,MAAM,SAAS,aAAa;AACnC,gBAAM,WAAW,WAAW,MAAM,KAAK;AACvC,gBAAM,YAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAC1E,cAAI,SAAU,gBAAe,UAAU,UAAU,SAAS;AAE1D,cAAI,YAAY,aAAa,CAAC,gBAAgB,EAAG,MAAK,kBAAkB,YAAY,EAAE,mBAAmB,KAAK,CAAC;AAAA,QACjH,YAAY,MAAM,SAAS,kBAAkB,MAAM,SAAS,kBAAkB,MAAM,SAAS,oBAAoB,CAAC,gBAAgB,GAAG;AACnI,eAAK,kBAAkB,YAAY,EAAE,mBAAmB,KAAK,CAAC;AAAA,QAChE;AAAA,MACF;AAAA,IACF,CAAC,EAAE,KAAK,CAAC,QAAQ;AACf,UAAI,QAAQ,cAAe,qBAAoB,UAAUL;AAAA,IAC3D,CAAC;AACD,WAAO,MAAM;AACX,iBAAW,oBAAoB,SAAS,SAAS;AACjD,YAAM,MAAM;AAAA,IACd;AAAA,EACF,GAAG,CAAC,MAAMA,UAAS,QAAQ,UAAU,iBAAiB,mBAAmBK,UAAS,WAAW,CAAC;AAC9F,EAAAC,WAAU,MAAM,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC;AAG5C,WAAS,sBAAsB;AAC7B,UAAM,WAAW,YAAY,QAAQ,GAAG,EAAE;AAC1C,4BAAwB,UAAU,UAAU,UAAU,WAAW,sBAAsB,WAAW;AAClG,gBAAY,CAAC,YAAY,2BAA2B,yBAAyB,OAAO,CAAC,CAAC;AAAA,EACxF;AAGA,WAAS,2BAA2B;AAClC,UAAMgB,UAAS,wBAAwB;AACvC,QAAI,CAACA,QAAQ;AACb,4BAAwB,UAAU;AAClC,gBAAY,CAAC,YAAY,QAAQ,IAAI,CAAC,SAAU,KAAK,OAAOA,QAAO,KAAKA,UAAS,IAAK,CAAC;AAAA,EACzF;AAMA,QAAM,YAAYpB;AAAA,IAChB,CAAC,SAAkD,MAAc,eAAoD;AACnH,cAAQ,UAAU;AAClB,oBAAc,KAAK;AAEnB,sBAAgB,UAAU;AAC1B,0BAAoB;AACpB,mBAAa,UAAU;AACvB,WAAK,YAAY,OAA4C,EAAE,QAAQ,MAAM;AAC3E,gBAAQ,UAAU;AAClB,iCAAyB;AACzB,cAAM,YAAY,aAAa;AAC/B,YAAI,CAAC,UAAW;AAEhB,qBAAa,UAAU;AACvB,oBAAY,CAAC,YAAY,QAAQ,OAAO,CAAC,SAAS,KAAK,OAAO,UAAU,SAAS,CAAC;AAClF,iBAAS,CAAC,YAAa,QAAQ,WAAW,IAAI,OAAO,OAAQ;AAC7D,qBAAa,UAAU,IAAI;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA;AAAA,IAEA,CAAC,aAAa,WAAW;AAAA,EAC3B;AAMA,QAAM,uBAAuBA,aAAY,MAAM;AAC7C,QAAI,QAAQ,QAAS,QAAO,QAAQ;AACpC,UAAM,SAAS,aAAa,QAAQ;AACpC,kBAAc,IAAI;AAClB,UAAM,OAAO,YAAY;AACvB,YAAM,WAAW;AACjB,YAAM,SAAS;AACf,UAAI,CAAC,OAAO,QAAS,OAAM,kBAAkB,MAAM;AAAA,IACrD,GAAG,EAAE,QAAQ,MAAM;AACjB,cAAQ,UAAU;AAClB,UAAI,CAAC,OAAO,QAAS,eAAc,KAAK;AAAA,IAC1C,CAAC;AACD,YAAQ,UAAU;AAClB,WAAO;AAAA,EACT,GAAG,CAAC,iBAAiB,CAAC;AACtB,0BAAwB,UAAU;AAMlC,QAAM,iBAAiBA;AAAA,IACrB,CAAC,MAAc,OAA8B,eAAoD;AAC/F,YAAM,QAAQF;AACd,YAAM,WAAW,KAAK,UAAU,MAAM,IAAI,CAAC,SAAS,KAAK,GAAG,CAAC;AAC7D,YAAM,cAAc,sBAAsB;AAC1C,YAAM,WAAW,aAAa,YAAY,SAAS,YAAY,SAAS,QAAQ,YAAY,UAAU;AACtG,4BAAsB,UAAU;AAChC,YAAM,aAAa,kBAAkB,WAAW,YAAY,KAAK,OAAO,WAAW,GAAG,MAAM,KAAK;AACjG,YAAM,SAAS,aAAa,QAAQ;AACpC,yBAAmB,QAAQ,IAAI,WAAW,EAAE;AAC5C,qBAAe,CAAC,YAAY,CAAC,GAAG,SAAS,UAAU,CAAC;AACpD,YAAM,UAAU,CAAC,SAA6B;AAC5C,uBAAe,CAAC,YAAY,QAAQ,OAAO,CAAC,SAAS,KAAK,OAAO,WAAW,EAAE,CAAC;AAC/E,iBAAS,CAAC,YAAa,QAAQ,WAAW,IAAI,OAAO,OAAQ;AAC7D,qBAAa,IAAI;AAAA,MACnB;AACA,YAAM,OAAO,WAAW,QAAQ,KAAK,YAAY;AAC/C,YAAI;AACJ,YAAI;AACF,qBAAW,MAAM;AAAA,YACfK;AAAA,YACA,GAAG,KAAK;AAAA,YACR,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,EAAE,SAAS,mBAAmB,UAAU,GAAG,GAAG,WAAW,UAAU,OAAO,EAAE,CAAC,EAAE;AAAA,YACvK,EAAE,WAAW,YAAY,QAAQ,kBAAkB,WAAW,KAAK;AAAA,UACrE;AAAA,QACF,QAAQ;AACN,cAAI,OAAO,QAAS;AACpB,gCAAsB,UAAU,EAAE,SAAS,OAAO,IAAI,WAAW,IAAI,MAAM,OAAO,SAAS;AAC3F,kBAAQ,YAAY;AACpB;AAAA,QACF,UAAE;AACA,6BAAmB,QAAQ,OAAO,WAAW,EAAE;AAAA,QACjD;AACA,YAAI,OAAO,WAAW,WAAW,YAAY,MAAO;AACpD,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,gBAAgB,SAAS,WAAW,MAAM,MAAM,kBAAkB,QAAQ,IAAI;AACpF,cAAI,cAAe,aAAY,EAAE,OAAO,aAAa,MAAM,cAAc,CAAC;AAC1E,gBAAM,kBAAkB,MAAM,gBAAgB,QAAQ;AACtD,6BAAmB,eAAe;AAClC,kBAAQ,gBAAgB,mBAAoB,iBAAiB,QAAS,MAAM,YAAY,QAAQ,CAAG;AACnG;AAAA,QACF;AACA,YAAI,CAAC,aAAa,QAAS,MAAK,wBAAwB,QAAQ;AAAA,MAClE,CAAC;AACD,iBAAW,UAAU,KAAK,MAAM,MAAM,MAAS;AAAA,IACjD;AAAA,IACA,CAAC,oBAAoBL,UAASK,QAAO;AAAA,EACvC;AAEA,QAAM,SAASH,aAAY,CAAC,OAA+B,eAA6D;AACtH,UAAM,WAAW,QAAQ,SAAS,MAAM,SAAS,CAAC;AAClD,QAAI,aAAa,YAAY,MAAM,KAAK,EAAE,SAAS,IAAI;AACrD,YAAMqB,QAAO;AACb,eAAS,EAAE;AACX,qBAAeA,OAAM,SAAS,CAAC,GAAG,UAAU;AAC5C,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,YAAY,CAAC,UAAU,CAAC,QAAQ,MAAM,KAAK,EAAE,SAAS,GAAG;AAC5D,0BAAoB,IAAI;AACxB,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,WAAW,CAAC,UAAU,QAAQ,aAAa,UAAW,CAAC,YAAY,CAAC,UAAY,QAAO;AACnG,UAAM,OAAO;AACb,aAAS,EAAE;AACX,cAAU,EAAE,GAAI,KAAK,KAAK,EAAE,SAAS,KAAK,EAAE,KAAK,GAAI,GAAI,YAAY,EAAE,OAAO,CAAC,GAAG,KAAM,EAAE,EAAG,GAAG,MAAM,UAAU;AAChH,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,WAAW,QAAQ,WAAW,QAAQ,UAAU,gBAAgB,WAAW,KAAK,CAAC;AAE3F,QAAM,YAAYrB;AAAA,IAChB,CAAC,SAA0B;AACzB,UAAI,QAAQ,WAAW,CAAC,UAAU,QAAQ,WAAW,aAAa,UAAU,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAC7G,gBAAU,EAAE,KAAK,GAAG,IAAI;AACxB,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,WAAW,QAAQ,SAAS;AAAA,EACvC;AAEA,EAAAI,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,iBAAkB;AAClC,wBAAoB,KAAK;AACzB,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,QAAQ,gBAAgB,CAAC;AAErC,QAAM,mBAAmBJ;AAAA,IACvB,CAAC,MAAc,YAA0D;AACvE,UAAI,QAAQ,WAAW,CAAC,UAAU,QAAQ,WAAW,aAAa,UAAU,CAAC,aAAa,SAAS,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAGpI,UAAI,mBAAmB,YAAY,OAAO,EAAG,QAAO;AACpD,cAAQ,UAAU;AAClB,oBAAc,KAAK;AAEnB,sBAAgB,UAAU;AAC1B,0BAAoB;AACpB,mBAAa,UAAU;AACvB,YAAM,KAAK,OAAO,WAAW;AAC7B,sBAAgB,QAAQ,IAAI,EAAE;AAC9B,UAAI,SAAS,QAAQ,OAAQ,iBAAgB,QAAQ,IAAI,IAAI,QAAQ,MAAM;AAE3E,WAAK,YAAY,EAAE,IAAI,MAAM,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAK,GAAG,EAAE,MAAM,sBAAsB,MAAM,KAAK,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM;AAChI,gBAAQ,UAAU;AAClB,iCAAyB;AACzB,cAAM,YAAY,aAAa;AAC/B,YAAI,CAAC,UAAW;AAEhB,qBAAa,UAAU;AACvB,wBAAgB,QAAQ,OAAO,UAAU,aAAa,EAAE;AACxD,wBAAgB,QAAQ,OAAO,UAAU,aAAa,EAAE;AACxD,oBAAY,CAAC,YAAY,QAAQ,OAAO,CAAC,SAAS,KAAK,OAAO,UAAU,SAAS,CAAC;AAAA,MACpF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,IACA,CAAC,aAAa,OAAO,QAAQ,WAAW,QAAQ,aAAa,WAAW;AAAA,EAC1E;AAMA,QAAM,CAAC,WAAW,YAAY,IAAID,UAAqE,EAAE,SAAAD,UAAS,aAAa,CAAC,EAAE,CAAC;AACnI,QAAM,eAAeE;AAAA,IACnB,CAAC,WAAmB,QAAkB;AACpC,mBAAa,CAAC,aAAa,EAAE,SAAAF,UAAS,aAAa,EAAE,GAAI,QAAQ,YAAYA,WAAU,QAAQ,cAAc,CAAC,GAAI,CAAC,SAAS,GAAG,IAAI,EAAE,EAAE;AAAA,IACzI;AAAA,IACA,CAACA,QAAO;AAAA,EACV;AACA,QAAM,WAAWO,SAAQ,MAAM;AAC7B,UAAM,OAAO,UAAU,YAAYP,WAAU,UAAU,cAAc,CAAC;AACtE,QAAI,OAAO,KAAK,IAAI,EAAE,WAAW,EAAG,QAAO;AAC3C,WAAO,cAAc,IAAI,CAAC,YAAY;AACpC,YAAM,MAAM,KAAK,QAAQ,EAAE;AAC3B,aAAO,OAAO,QAAQ,SAAS,eAAe,CAAC,QAAQ,UAAU,WAAW,EAAE,GAAG,SAAS,UAAU,EAAE,GAAG,QAAQ,UAAU,UAAU,IAAI,EAAE,IAAI;AAAA,IACjJ,CAAC;AAAA,EACH,GAAG,CAAC,eAAeA,UAAS,SAAS,CAAC;AAEtC,QAAM,gBAAgBI,QAAwB,cAAc,CAAC,CAAC,CAAC;AAC/D,QAAM,YAAYG,SAAQ,MAAM;AAC9B,UAAM,OAAO,kBAAkB,SAAS,gBAAgB,iBAAiB,SAAS,cAAc,aAAa;AAC7G,QAAI,CAAC,cAAc,cAAc,SAAS,IAAI,EAAG,eAAc,UAAU;AACzE,WAAO,cAAc;AAAA,EACvB,GAAG,CAAC,eAAe,gBAAgB,CAAC;AAOpC,QAAMiB,kBAAiBpB,QAAmD,IAAI;AAC9E,QAAM,kBAAkB,UAAU,iBAAiB,SAAS,YAAY;AACxE,QAAM,eAAeA,QAAO,eAAe;AAC3C,eAAa,UAAU;AACvB,QAAMqB,aAAY,oBAAoB;AACtC,EAAAnB,WAAU,MAAM;AACd,QAAI,CAACmB,cAAa,aAAa,YAAY,KAAM;AACjD,UAAM,cAAc,iBAAiB,QAAQ,aAAa,OAAO;AACjE,IAAAD,gBAAe,UAAU;AACzB,WAAO,MAAM;AACX,MAAAA,gBAAe,UAAU;AACzB,kBAAY,OAAO;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQC,UAAS,CAAC;AACtB,EAAAnB,WAAU,MAAM;AACd,QAAI,oBAAoB,KAAM,CAAAkB,gBAAe,SAAS,OAAO,eAAe;AAAA,EAC9E,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,WAAWpB,QAA6B,IAAI;AAClD,QAAM,OAAOF,aAAY,MAAM;AAC7B,QAAI,SAAS,QAAS,QAAO,SAAS;AAEtC,QAAI,UAAU,QAAS,cAAa;AACpC,yBAAqB,SAAS,MAAM;AACpC,yBAAqB,UAAU;AAC/B,UAAM,WAAW,YAAY;AAC3B,YAAM,kBAAkBG,UAAS,GAAGL,QAAO,SAAS,EAAE,QAAQ,OAAO,GAAG;AAAA,QACtE,WAAW,YAAY,QAAQ;AAAA,QAC/B,WAAW;AAAA,MACb,CAAC,EAAE,MAAM,MAAM,MAAS;AACxB,YAAM,SAAS;AACf,kBAAY,CAACU,cAAa,sBAAsBA,SAAQ,CAAC;AAAA,IAC3D,GAAG,EAAE,QAAQ,MAAM;AACjB,eAAS,UAAU;AAAA,IACrB,CAAC;AACD,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,cAAcV,UAASK,UAAS,aAAa,QAAQ,CAAC;AAE1D,QAAM,QAAQH,aAAY,MAAM;AAC9B,QAAI,eAAe,WAAW;AAC5B,mBAAa;AACb;AAAA,IACF;AACA,QAAI,cAAc,iBAAkB,YAAY,gBAAgB,CAAC,QAAS;AACxE,qBAAe,CAAC,YAAY,UAAU,CAAC;AACvC;AAAA,IACF;AACA,QAAI,YAAY,gBAAgB,CAAC,MAAM;AAErC,WAAK,kBAAkB,aAAa,QAAQ,MAAM;AAClD;AAAA,IACF;AACA,QAAI,CAAC,UAAU,QAAQ,EAAE,cAAc,UAAW;AAClD,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,MAAM,UAAU,YAAY,YAAY,QAAQ,cAAc,mBAAmB,SAAS,QAAQ,UAAU,CAAC;AAEhI,QAAM,gBACJ,UAAU,CAAC,QAAQ,CAAC,aAAa,aAAa,cAAc,eAAe,YAAY,CAAC,cAAc,oBAAoB,cAAc,GAAG,EAAE,CAAC,KAAK,cAAc,KAAK,cAAc;AACtL,QAAM,UAAU,UAAU,CAAC,QAAQ,CAAC,aAAa,aAAa,QAAQ,eAAe;AAErF,QAAM,mBAAmBA,aAAY,MAAM;AACzC,UAAM,OAAO,YAAY,QAAQ,GAAG,EAAE;AACtC,QAAI,CAAC,iBAAiB,MAAM,SAAS,eAAe,QAAQ,QAAS;AACrE,wBAAoB,UAAU,KAAK;AACnC,mBAAe,UAAU;AACzB,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,MAAM,CAAC;AAG1B,QAAM,YAAYA;AAAA,IAChB,CAAC,WAAmB,MAAc,OAAgB,WAAyD;AACzG,YAAM,WAAW,YAAY,QAAQ,KAAK,CAAC,YAAY,QAAQ,OAAO,SAAS;AAC/E,YAAM,QAAQ,YAAY,QAAQ,UAAU,CAAC,YAAY,QAAQ,OAAO,SAAS;AAEjF,YAAM,QAAQ,UAAU,MAAM,OAAO,CAAC,SAA6B,KAAK,SAAS,MAAM,KAAK,CAAC;AAC7F,YAAM,UAAU,KAAK,KAAK,EAAE,SAAS;AACrC,cAAQ,UAAU;AAClB,YAAM,SAAS,YAAY;AAC3B,aAAO,IAAI,QAAqB,CAAC,YAAY;AAC3C,cAAM,SAAsB;AAAA,UAC1B,WAAW,OAAO,WAAW;AAAA,UAC7B,iBAAiB;AAAA,UACjB,SAAS;AAAA,UACT,UAAU,CAACO,aAAY;AACrB,gBAAI,CAACA,SAAS,SAAQ,EAAE,QAAQ,UAAU,CAAC;AAAA,UAC7C;AAAA,QACF;AACA,oBAAY,UAAU;AACtB,uBAAe,UAAU;AACzB,YAAI,MAAO,iBAAgB,QAAQ,IAAI,OAAO,SAAS;AACvD,YAAI,SAAS,QAAQ,OAAQ,iBAAgB,QAAQ,IAAI,OAAO,WAAW,MAAM;AACjF,sBAAc,KAAK;AAEnB,oBAAY,OAAO,MAAM,GAAG,KAAK,CAAC;AAClC,cAAM,QAAQ,CAAC,GAAG,OAAO,GAAI,UAAU,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,IAAI,CAAC,GAAI,GAAI,QAAQ,CAAC,EAAE,MAAM,sBAAsB,MAAM,KAAK,CAAU,IAAI,CAAC,CAAE;AACvJ,aAAK,YAAY,EAAE,IAAI,OAAO,WAAW,MAAM,QAAQ,MAAM,CAAC,EAAE,QAAQ,MAAM;AAC5E,kBAAQ,UAAU;AAClB,cAAI,OAAO,SAAS;AAElB,gBAAI,YAAY,YAAY,OAAQ,aAAY,UAAU;AAC1D,2BAAe,UAAU;AACzB,4BAAgB,QAAQ,OAAO,OAAO,SAAS;AAC/C,4BAAgB,QAAQ,OAAO,OAAO,SAAS;AAC/C,wBAAY,MAAM;AAClB,oBAAQ;AAAA,cACN,QAAQ;AAAA,cACR,QAAQ,OAAO;AAAA,cACf,GAAI,OAAO,eAAe,EAAE,MAAM,OAAO,YAAY;AAAA,cACrD,GAAI,OAAO,eAAe,EAAE,MAAM,OAAO,YAAY;AAAA,cACrD,GAAI,OAAO,eAAe,EAAE,MAAM,OAAO,YAAY;AAAA,YACvD,CAAC;AACD;AAAA,UACF;AAEA,kBAAQ,EAAE,QAAQ,UAAU,CAAC;AAAA,QAC/B,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,CAAC,aAAa,WAAW;AAAA,EAC3B;AAEA,QAAM,OAAOP;AAAA,IACX,OAAO,WAAmB,SAAuC;AAC/D,UAAI,QAAQ,WAAW,KAAM,QAAO,EAAE,QAAQ,eAAe,QAAQ,OAAO;AAC5E,UAAI,CAAC,QAAS,QAAO,EAAE,QAAQ,eAAe,QAAQ,UAAU;AAChE,YAAM,WAAW,YAAY,QAAQ,KAAK,CAAC,YAAY,QAAQ,OAAO,SAAS;AAC/E,UAAI,CAAC,YAAY,SAAS,SAAS,OAAQ,QAAO,EAAE,QAAQ,eAAe,QAAQ,kBAAkB;AACrG,YAAM,WAAW,SAAS,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,MAAM;AACnE,UAAI,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC,SAAU,QAAO,EAAE,QAAQ,eAAe,QAAQ,QAAQ;AAC3F,aAAO,UAAU,WAAW,MAAM,KAAK;AAAA,IACzC;AAAA,IACA,CAAC,MAAM,SAAS,SAAS;AAAA,EAC3B;AAGA,QAAM,YAAYE,QAAyB,QAAQ,QAAQ,CAAC;AAE5D,QAAM,0BAA0BF;AAAA,IAC9B,CAAC,MAAc,YAAmE;AAChF,YAAM,MAAM,UAAU,QAAQ,KAAK,YAA8B;AAC/D,cAAM,SAAS,aAAa,QAAQ;AACpC,cAAMQ,YAAW,YAAY;AAC7B,cAAM,QAAQA,UAAS,cAAc,CAAC,YAAY,QAAQ,SAAS,MAAM;AACzE,cAAM,SAASA,UAAS,KAAK;AAC7B,YAAI,CAAC,UAAU,aAAa,CAAC,UAAU,CAAC,gBAAgB,QAAQ,IAAI,OAAO,EAAE,KAAK,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAEnH,YAAI,CAACA,UAAS,MAAM,QAAQ,CAAC,EAAE,MAAM,aAAa,EAAG,QAAO;AAC5D,cAAM,WAAWA;AAEjB,oBAAY,CAAC,GAAGA,UAAS,MAAM,GAAG,KAAK,GAAG,EAAE,GAAG,QAAQ,OAAO,CAAC,EAAE,MAAM,QAAQ,KAAK,GAAG,EAAE,MAAM,sBAAsB,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;AACrI,cAAM,KAAK;AACX,YAAI,CAAE,MAAM,UAAU,MAAM,EAAI,QAAO,oBAAoB,QAAQ;AACnE,iBAAS,UAAU,GAAG,UAAU,kBAAkB,WAAW;AAC3D,gBAAM,UAAU,MAAM,UAAU,OAAO,IAAI,MAAM,MAAM,SAAS,MAAM;AACtE,cAAI,QAAQ,WAAW,UAAW,QAAO;AAEzC,cAAI,EAAE,QAAQ,WAAW,aAAa,QAAQ,WAAW,QAAS;AAClE,gBAAMI,OAAM,kBAAkB,MAAM;AAAA,QACtC;AACA,eAAO,oBAAoB,QAAQ;AAAA,MACrC,CAAC;AACD,gBAAU,UAAU,IAAI,MAAM,MAAM,KAAK;AACzC,aAAO;AAAA,IACT;AAAA,IACA,CAAC,WAAW,QAAQ,mBAAmB,aAAa,WAAW,IAAI;AAAA,EACrE;AAGA,WAAS,oBAAoB,UAAuC;AAClE,gBAAY,QAAQ;AACpB,SAAK,kBAAkB,aAAa,QAAQ,MAAM;AAClD,WAAO;AAAA,EACT;AAGA,iBAAe,UAAU,QAAuC;AAC9D,aAAS,SAAS,GAAG,UAAU,cAAc,UAAU,cAAc;AACnE,UAAI,OAAO,QAAS,QAAO;AAC3B,UAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,QAAS,QAAO;AACjD,YAAMA,OAAM,cAAc,MAAM;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAGA,QAAM,eAAeZ;AAAA,IACnB,OAAO,cAAwC;AAC7C,UAAI,CAAC,UAAU,QAAQ,eAAe,YAAY,CAAC,aAAa,SAAU,QAAO;AACjF,YAAM,eAAe,CAACC,aAA2B;AAC/C,oBAAYA,QAAO;AACnB,oBAAYA,SAAQ,QAAQ;AAC5B,uBAAeA,SAAQ,eAAe,CAAC,CAAC;AACxC,iBAASA,SAAQ,SAAS,CAAC,CAAC;AAAA,MAC9B;AACA,UAAI;AACF,cAAM,WAAW,MAAM;AAAA,UACrBE;AAAA,UACA,GAAGL,QAAO;AAAA,UACV,EAAE,QAAQ,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,GAAG,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC,EAAE;AAAA,UACvG,EAAE,WAAW,YAAY,QAAQ,kBAAkB,WAAW,KAAK;AAAA,QACrE;AACA,YAAI,WAAW,YAAYA,SAAS,QAAO;AAC3C,YAAI,SAAS,IAAI;AACf,uBAAc,MAAM,SAAS,KAAK,CAAmB;AACrD,iBAAO;AAAA,QACT;AAEA,cAAM,QAAQ,MAAM,YAAY;AAChC,YAAI,OAAO,UAAU,YAAY,WAAW,YAAYA,SAAS,cAAa,KAAK;AACnF,eAAO;AAAA,MACT,QAAQ;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,aAAa,MAAM,aAAa,UAAUA,UAAS,YAAY,aAAa,QAAQK,UAAS,WAAW;AAAA,EAC3G;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAmBA,SAAS,qBACP,UACA,YACA,UACA,SACoB;AACpB,QAAM,cAAc,CAAC,SAClB,KAA0B,UAAU,OAAO,cAAc,wBAAwB,IAA4B,MAAM;AACtH,SAAO,SAAS,IAAI,CAAC,YAAY;AAC/B,QAAI,QAAQ,SAAS,eAAe,CAAC,QAAQ,MAAM,KAAK,WAAW,EAAG,QAAO;AAC7E,UAAM,QAAQ,QAAQ,MAAM,IAAI,CAAC,SAAS;AACxC,UAAI,CAAC,YAAY,IAAI,EAAG,QAAO;AAC/B,YAAM,SAAS,iBAAiB,IAA4B;AAC5D,UAAI,QAAQ;AAEV,cAAM,EAAE,iBAAiB,WAAW,GAAG,KAAK,IAAI;AAChD,cAAM,UAAU,aAAa,SAAS,WAAW,aAAa,WAAW,WAAW;AACpF,eAAO,EAAE,GAAG,MAAM,GAAI,WAAW,EAAE,OAAO,kBAAkB,GAAI,QAAQ,EAAE,GAAG,MAAM,QAAQ,WAAW,QAAQ,EAAE;AAAA,MAClH;AACA,aAAO;AAAA,QACL,GAAG;AAAA;AAAA,QAEH,OAAO,UAAU,oBAAoB;AAAA,QACrC,UAAU;AAAA,UACR,IAAI;AAAA,UACJ,UAAU,aAAa;AAAA,UACvB,GAAI,aAAa,YAAY,EAAE,QAAQ,eAAe;AAAA,QACxD;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,EAAE,GAAG,SAAS,OAAO,GAAI,WAAW,EAAE,UAAU,EAAE,GAAG,QAAQ,UAAU,QAAQ,YAAqB,EAAE,EAAG;AAAA,EAClH,CAAC;AACH;AAGA,SAAS,uBAAuB,UAAuC;AACrE,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,SACE,MAAM,SAAS,eACf,KAAK,MAAM;AAAA,IACT,CAAC,SACE,KAA0B,UAAU,sBACpC,QAAS,KAA0B,QAAQ,KAAK,iBAAiB,IAA4B,GAAG,YAAY;AAAA,EACjH;AAEJ;AAMA,SAAS,uBAAuB,UAAkD;AAChF,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,eAAe,CAAC,KAAK,MAAM,KAAK,iBAAiB,EAAG,QAAO;AAC9E,QAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,SAAU,kBAAkB,IAAI,IAAK,EAAE,GAAG,MAAM,OAAO,kBAAkB,IAAoB,IAAK;AAChI,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC;AACtD;AAGA,SAAS,kBAAkB,MAAkD;AAC3E,QAAM,EAAE,MAAM,IAAI;AAClB,MAAI,UAAU,qBAAsB,QAAO;AAC3C,QAAM,SAAS,iBAAiB,IAA4B;AAC5D,SAAO,WAAW,UAAc,KAAmC,gBAAgB,QAAQ,OAAO,WAAW;AAC/G;AAGA,SAAS,2BAA2B,UAAkD;AACpF,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,eAAe,KAAK,UAAU,WAAW,oBAAqB,QAAO;AACxF,QAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,SAAS;AACrC,QAAK,KAA0B,UAAU,wBAAyB,KAA0B,UAAU,sBAAsB;AAC1H,aAAO,EAAE,GAAG,MAAM,OAAO,iBAAiB,UAAU,EAAE,IAAK,KAA0B,SAAU,IAAI,UAAU,MAAM,EAAE;AAAA,IACvH;AACA,UAAM,SAAS,wBAAwB,IAA4B,IAAI,iBAAiB,IAA4B,IAAI;AACxH,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,EAAE,iBAAiB,SAAS,GAAG,KAAK,IAAI;AAC9C,WAAO,EAAE,GAAG,MAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,UAAU,SAAS,SAAS,EAAE;AAAA,EAC7E,CAAC;AACD,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,OAAO,UAAU,EAAE,GAAG,KAAK,UAAU,QAAQ,WAAoB,EAAE,CAAC;AACnH;AAGA,eAAe,kBAAkB,UAAsC;AACrE,SAAO,SAAS,WAAW,OAAQ,MAAM,YAAY,QAAQ,MAAO;AACtE;AAGA,SAAS,gBAAyB;AAChC,MAAI;AACF,WAAO,QAAQ,IAAI,aAAa;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,sBAA8B;AACrC,QAAM,SAAS,OAAO,aAAa,cAAc,kCAAkC,SAAS;AAC5F,SACE,oDAAoD,MAAM,8KAEZ,MAAM;AAGxD;AAGA,eAAe,YAAY,UAAiD;AAC1E,MAAI;AACF,UAAM,OAAQ,MAAM,SAAS,MAAM,EAAE,KAAK;AAC1C,WAAO,OAAO,KAAK,OAAO,SAAS,WAAW,KAAK,MAAM,OAAO;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,kBAAkB,UAAkD;AACjF,MAAI;AACF,UAAM,OAAQ,MAAM,SAAS,MAAM,EAAE,KAAK;AAC1C,WAAO,KAAK,OAAO,SAAS,2BAA2B,eAAe,KAAK,KAA6C,IAAI;AAAA,EAC9H,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,eAAe,SAAoC;AAC1D,SACE,QAAQ,SAAS,UACjB,CAAC,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,sBAAsB,KAClE,EAAE,QAAQ,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,CAAC,SAAS,KAAK,SAAS,mBAAmB;AAEjG;AAOA,SAAS,oBAAoB,SAAgD;AAC3E,MAAI,SAAS,SAAS,YAAa,QAAO;AAC1C,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,WAAW,WAAW,WAAW,aAAa,WAAW,cAAe,QAAO,QAAQ,MAAM,KAAK,mBAAmB;AACzH,SAAO,WAAW,cAAc,CAAC,kBAAkB,OAAO;AAC5D;AAGA,SAAS,oBAAoB,MAAkD;AAC7E,MAAI,CAAC,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,SAAS,eAAgB,QAAO;AAC3E,QAAM,EAAE,MAAM,IAAI;AAClB,SAAO,UAAU,qBAAqB,UAAU,sBAAsB,UAAU,kBAAkB,UAAU;AAC9G;AAEA,SAAS,cAAc,EAAE,KAAK,GAA0C;AACtE,MAAI,OAAO,MAAM,YAAY,YAAY,OAAO,KAAK,eAAe,SAAU,QAAO;AACrF,SAAO,EAAE,SAAS,KAAK,SAAS,YAAY,KAAK,YAAY,GAAI,OAAO,KAAK,cAAc,YAAY,EAAE,WAAW,KAAK,UAAU,EAAG;AACxI;AAGA,IAAM,gBAAgB,CAAC,WAAqC,WAAW,UAAa,WAAW;AAE/F,SAAS,iBAAiB,UAAiE;AACzF,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,SAAO,MAAM,SAAS,cAAc,KAAK,UAAU,SAAS;AAC9D;AAGA,SAAS,kBAAkB,SAAgD;AACzE,MAAI,SAAS,SAAS,YAAa,QAAO;AAC1C,QAAM,SAAS,QAAQ,UAAU,UAAU;AAC3C,MAAI,WAAW,WAAW,WAAW,aAAa,WAAW,cAAe,QAAO;AAEnF,SAAO,WAAW,cAAc,YAAY,OAAO,EAAE,KAAK,EAAE,WAAW;AACzE;AAEA,SAAS,qBAAqB,UAA8B,QAA0C;AACpG,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,YAAa,QAAO;AACvC,SAAO,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,EAAE,GAAG,KAAK,UAAU,OAAO,EAAE,CAAC;AACvF;AAGA,SAAS,cAAc,UAAkD;AACvE,QAAM,UAAU,qBAAqB,UAAU,SAAS;AACxD,QAAM,OAAO,QAAQ,GAAG,EAAE;AAC1B,SAAO,MAAM,SAAS,cAAc,CAAC,GAAG,QAAQ,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,OAAO,sBAAsB,KAAK,KAAK,EAAE,CAAC,IAAI;AACzH;AAGA,SAAS,cAAc,SAAoC;AACzD,SACE,QAAQ,SAAS,eACjB,QAAQ,MAAM,MAAM,CAAC,SAAS,KAAK,SAAS,gBAAgB,KAAK,SAAS,eAAgB,KAAK,SAAS,UAAU,KAAK,KAAK,KAAK,EAAE,WAAW,CAAE;AAEpJ;AAGA,SAAS,sBAAsB,UAAkD;AAC/E,QAAM,SAAS,iBAAiB,QAAQ;AACxC,SAAO,cAAc,MAAM,KAAK,WAAW,YAAY,WAAW,cAAc,QAAQ;AAC1F;;;AuB/oEA,SAAS,eAAAqB,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAqDlE,IAAM,cAA0C,EAAE,YAAY,IAAI,SAAS,GAAG;AAMvE,SAAS,eAAe,EAAE,KAAK,QAAQ,aAAa,qBAAqB,OAAO,aAAa,MAAM,OAAO,aAAa,cAAc,GAA2C;AACrL,QAAM,SAAS,cAAc;AAE7B,QAAM,WAAWC,QAAO,WAAW;AACnC,WAAS,UAAU;AACnB,QAAMC,WAAUC,aAAqC,CAAC,OAAO,UAAU,SAAS,WAAW,WAAW,OAAO,OAAO,IAAI,GAAG,CAAC,CAAC;AAC7H,QAAM,eAAeF,QAAO,aAAa;AACzC,eAAa,UAAU;AACvB,QAAM,CAAC,WAAW,YAAY,IAAIG,UAAoB,EAAE,QAAQ,UAAU,CAAC;AAE3E,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAiC,IAAI;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAE1C,QAAM,YAAYH,QAA+B,IAAI;AACrD,QAAM,gBAAgBA,QAAO,CAAC;AAG9B,QAAM,SAASA,QAAsB,IAAI;AACzC,EAAAI,WAAU,MAAM;AACd,QAAI,CAAC,cAAc,OAAO,YAAY,IAAK;AAC3C,QAAI,YAAY;AAChB,iBAAa,EAAE,QAAQ,UAAU,CAAC;AAClC,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,WAAW,MAAMH,SAAQ,GAAG,GAAG,SAAS;AAC9C,YAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,cAAMI,QAAQ,MAAM,SAAS,KAAK;AAClC,YAAI,UAAW;AACf,eAAO,UAAU;AACjB,qBAAa,EAAE,QAAQ,SAAS,MAAAA,MAAK,CAAC;AAAA,MACxC,QAAQ;AACN,YAAI,UAAW;AACf,eAAO,UAAU;AACjB,qBAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,MACnC;AAAA,IACF,GAAG;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,KAAKJ,UAAS,UAAU,CAAC;AAE7B,EAAAG,WAAU,MAAM;AACd,kBAAc;AACd,cAAU,UAAU;AACpB,gBAAY,IAAI;AAChB,cAAU,KAAK;AAAA,EACjB,GAAG,CAAC,QAAQ,aAAa,SAAS,aAAa,YAAY,aAAa,SAAS,CAAC;AAElF,QAAM,OAAO,UAAU,WAAW,UAAU,UAAU,OAAO;AAC7D,QAAM,wBAAwB,sBAAsB,CAAC,YAAY,CAAC;AAClE,QAAM,YAAYE;AAAA,IAChB,MAAO,QAAQ,CAAC,wBAAwB,gBAAgB,MAAM,YAAY,eAAe,IAAI,IAAI;AAAA,IACjG,CAAC,MAAM,UAAU,aAAa,qBAAqB;AAAA,EACrD;AAEA,QAAM,OAAOJ;AAAA,IACX,OAAO,SAA0B;AAC/B,YAAM,gBAAgB,EAAE,cAAc;AACtC,gBAAU,KAAK;AACf,kBAAY,IAAI;AAChB,UAAI;AACJ,UAAI;AACF,cAAM,WAAW,MAAMD,SAAQ,GAAG,GAAG,IAAI,mBAAmB,MAAM,CAAC,UAAU;AAAA,UAC3E,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QAC3B,CAAC;AACD,YAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,QAAQ,SAAS,MAAM,EAAE;AAC3D,gBAAS,MAAM,SAAS,KAAK;AAC7B,kBAAU,UAAU;AACpB,YAAI,kBAAkB,cAAc,QAAS,aAAY,KAAK;AAAA,MAChE,QAAQ;AACN,YAAI,kBAAkB,cAAc,QAAS;AAC7C,oBAAY,UAAU,OAAO;AAC7B,kBAAU,IAAI;AACd;AAAA,MACF;AAEA,UAAI;AACF,qBAAa,UAAU,KAAK;AAAA,MAC9B,SAAS,OAAO;AACd,gBAAQ,MAAM,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,KAAK,QAAQA,QAAO;AAAA,EACvB;AAEA,QAAM,WAAWC;AAAA,IACf,CAAC,EAAE,YAAY,QAAQ,MAAM;AAC3B,UAAI,CAAC,QAAQ,CAAC,UAAW;AACzB,YAAM,QAAQ,UAAU,MAAM,YAAY,OAAO;AACjD,UAAI,CAAC,MAAO;AACZ,YAAM,EAAE,QAAQ,aAAa,IAAI,MAAM;AACvC,YAAM,OAAO,UAAU,aAAa,OAAO,SAAS,UAAU,SAAS,IAAI,UAAU,YAAY;AACjG,WAAK,KAAK,EAAE,YAAY,SAAS,GAAI,QAAQ,EAAE,WAAW,KAAK,EAAG,CAAC;AAAA,IACrE;AAAA,IACA,CAAC,WAAW,MAAM,IAAI;AAAA,EACxB;AAEA,QAAM,oBAAoBA;AAAA,IACxB,CAAC,YAAoB;AACnB,UAAI,UAAW,MAAK,KAAK,EAAE,YAAY,UAAU,YAAY,SAAS,UAAU,SAAS,WAAW,QAAQ,CAAC;AAAA,IAC/G;AAAA,IACA,CAAC,WAAW,IAAI;AAAA,EAClB;AAEA,SAAOI,SAAyB,MAAM;AACpC,UAAM,QAAQ,SAAS,OAAO,oBAAoB,UAAU,WAAW,WAAW,OAAO,kBAAkB;AAC3G,QAAI,CAAC,QAAQ,CAAC,WAAW;AACvB,aAAO,EAAE,WAAW,CAAC,GAAG,SAAS,aAAa,UAAU,iBAAiB,CAAC,GAAG,WAAW,IAAI,mBAAmB,GAAI,SAAS,EAAE,MAAM,EAAG;AAAA,IACzI;AACA,UAAM,SAAS,WAAW,MAAM,UAAU,YAAY,UAAU,OAAO,GAAG,UAAU,UAAU,CAAC;AAC/F,WAAO;AAAA,MACL,WAAW;AAAA,QACT,GAAG,KAAK,UAAU,IAAI,CAAC,cAAc;AAAA,UACnC,IAAI,SAAS;AAAA,UACb,MAAM,SAAS;AAAA,UACf,QAAQ,SAAS,OAAO,IAAI,CAAC,WAAW;AAAA,YACtC,IAAI,MAAM;AAAA,YACV,MAAM,MAAM;AAAA,YACZ,GAAI,MAAM,UAAU,UAAa,EAAE,OAAO,MAAM,MAAM;AAAA,YACtD,GAAI,MAAM,gBAAgB,UAAa,EAAE,aAAa,MAAM,YAAY;AAAA,YACxE,GAAI,MAAM,SAAS,UAAa,EAAE,MAAM,MAAM,KAAK;AAAA,YACnD,GAAI,MAAM,UAAU,UAAa,EAAE,OAAO,MAAM,MAAM;AAAA,YACtD,GAAI,OAAO,MAAM,kBAAkB,aAAa,EAAE,eAAe,MAAM,cAAc;AAAA,UACvF,EAAE;AAAA,QACJ,EAAE;AAAA,QACF,GAAG,cAAc,MAAM,SAAS;AAAA,MAClC;AAAA,MACA,SAAS,EAAE,YAAY,UAAU,YAAY,SAAS,UAAU,QAAQ;AAAA,MACxE;AAAA,MACA,iBAAiB,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,MAAM,OAAO,gBAAgB,EAAyC,KAAK,GAAG,EAAE;AAAA,MAC3H,WAAW,UAAU,aAAa;AAAA,MAClC;AAAA,MACA,GAAI,SAAS,EAAE,MAAM;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,WAAW,QAAQ,QAAQ,MAAM,UAAU,QAAQ,UAAU,iBAAiB,CAAC;AACrF;AAEA,SAAS,UAAU,MAAiB,YAAoB,SAAiB;AACvE,SAAO,KAAK,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,UAAU,GAAG,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO;AACnH;AAGA,SAAS,WAAW,MAAiB,YAAoB,SAAiB;AACxE,QAAM,SAAS,UAAU,MAAM,YAAY,OAAO;AAClD,MAAI,OAAQ,QAAO;AACnB,QAAM,EAAE,cAAc,IAAI;AAC1B,MAAI,cAAc,eAAe,cAAc,cAAc,YAAY,QAAS,QAAO;AACzF,QAAM,EAAE,QAAQ,IAAI;AACpB,SAAO,WAAW,EAAE,IAAI,SAAS,aAAa,QAAQ,WAAW,eAAe,QAAQ,eAAe,WAAW,QAAQ,UAAU;AACtI;AAMA,SAAS,cAAc,MAAiB,SAAwD;AAC9F,MAAI,UAAU,MAAM,QAAQ,YAAY,QAAQ,OAAO,EAAG,QAAO,CAAC;AAClE,QAAM,QAAQ,WAAW,MAAM,QAAQ,YAAY,QAAQ,OAAO;AAClE,QAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,SAAO;AAAA,IACL;AAAA,MACE,IAAI,QAAQ;AAAA,MACZ,MAAM,SAAS,gBAAgB,QAAQ;AAAA,MACvC,QAAQ;AAAA,QACN;AAAA,UACE,IAAI,QAAQ;AAAA,UACZ,MAAM,OAAO,eAAe,QAAQ;AAAA,UACpC,GAAI,OAAO,OAAO,kBAAkB,aAAa,EAAE,eAAe,MAAM,cAAc;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,SAAS,gBAAgB,MAAiB,QAAiD;AACzF,QAAM,YAAY,UAAU,UAAU,MAAM,OAAO,YAAY,OAAO,OAAO,IAAI,SAAS,YAAY,IAAI;AAC1G,QAAM,QAAQ,WAAW,MAAM,UAAU,YAAY,UAAU,OAAO;AACtE,QAAM,EAAE,QAAQ,aAAa,IAAI,OAAO,aAAa,EAAE,QAAQ,CAAC,GAAG,cAAc,KAAK;AACtF,QAAM,YAAY,UAAU,aAAa,OAAO,SAAS,UAAU,SAAS,IAAI,UAAU,YAAY;AACtG,SAAO,EAAE,YAAY,UAAU,YAAY,SAAS,UAAU,SAAS,GAAI,aAAa,EAAE,UAAU,EAAG;AACzG;AAEA,SAAS,YAAY,MAAkC;AACrD,QAAM,EAAE,SAAS,YAAY,UAAU,IAAI,KAAK;AAChD,SAAO,EAAE,SAAS,YAAY,GAAI,aAAa,EAAE,UAAU,EAAG;AAChE;;;ACvPA,SAAS,iBAAAC,gBAAe,cAAAC,aAAY,aAAAC,YAAW,YAAAC,iBAAgB;AA2CtD,gBAAAC,YAAA;AAtCF,IAAM,qBAAqB;AAClC,IAAM,UAAU;AAGT,SAAS,wBAAwB,OAAe,UAAkB,SAAuB,KAAK,QAAgB;AACnH,MAAI,SAAS,EAAG,QAAO;AACvB,QAAM,QAAQ,IAAI,KAAK,IAAI,QAAQ,GAAG,KAAK,MAAM,OAAO,KAAK,QAAQ,EAAE,CAAC;AACxE,UAAQ,WAAW,SAAS;AAC9B;AAQA,SAAS,oBAAoB,WAAkC;AAC7D,MAAI,QAAkB,CAAC,CAAC;AACxB,MAAI,aAAa;AACjB,SAAO;AAAA,IACL;AAAA,IACA,cAAc,MAAM,OAAO;AACzB,UAAI,UAAU,YAAY;AACxB,gBAAQ,CAAC,CAAC;AACV,qBAAa;AAAA,MACf;AACA,aAAO,MAAM,UAAU,KAAM,OAAM,KAAK,wBAAwB,OAAO,MAAM,MAAM,SAAS,CAAC,CAAE,CAAC;AAChG,aAAO,MAAM,IAAI;AAAA,IACnB;AAAA,EACF;AACF;AAEA,IAAM,uBAAuBC,eAAoC,IAAI;AACrE,IAAM,wBAAwBA,eAAiD,IAAI;AAG5E,SAAS,uBAAuB,EAAE,SAAS,GAA4B;AAC5E,QAAM,CAAC,MAAM,IAAIC,UAAS,MAAM,oBAAI,IAA2B,CAAC;AAChE,SAAO,gBAAAF,KAAC,sBAAsB,UAAtB,EAA+B,OAAO,QAAS,UAAS;AAClE;AAOO,SAAS,sBAAsB,EAAE,IAAI,UAAU,MAAM,SAAS,GAA4D;AAC/H,QAAM,SAASG,YAAW,qBAAqB;AAC/C,QAAM,CAAC,KAAK,IAAID,UAAS,MAAM;AAC7B,UAAM,OAAO,OAAO,SAAY,SAAY,QAAQ,IAAI,EAAE;AAC1D,QAAI,KAAM,QAAO;AACjB,UAAM,UAAU,oBAAoB,KAAK,IAAI,CAAC;AAC9C,QAAI,OAAO,UAAa,QAAS,SAAQ,IAAI,IAAI,OAAO;AACxD,WAAO;AAAA,EACT,CAAC;AAED,EAAAE,WAAU,MAAM;AACd,QAAI,OAAO,UAAa,CAAC,QAAS,SAAQ,OAAO,EAAE;AAAA,EACrD,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC;AAExB,SAAO,gBAAAJ,KAAC,qBAAqB,UAArB,EAA8B,OAAO,OAAQ,UAAS;AAChE;AAgBO,SAAS,kBAAkB,SAAS,MAAsB;AAC/D,QAAM,SAAS,cAAc;AAC7B,QAAM,SAASG,YAAW,oBAAoB;AAC9C,QAAM,CAAC,GAAG,IAAID,UAAS,MAAM,oBAAoB,KAAK,IAAI,CAAC,CAAC;AAC5D,QAAM,QAAQ,UAAU;AACxB,QAAM,CAAC,KAAK,MAAM,IAAIA,UAAS,MAAM,KAAK,IAAI,CAAC;AAE/C,EAAAE,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,WAAO,KAAK,IAAI,CAAC;AACjB,UAAM,QAAQ,YAAY,MAAM,OAAO,KAAK,IAAI,CAAC,GAAG,OAAO;AAC3D,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,YAAY,KAAK,IAAI,GAAG,MAAM,MAAM,SAAS;AACnD,QAAM,UAAU,OAAO,gBAAgB,SAAS,IAAI,OAAO,kBAAkB,CAAC,OAAO,gBAAgB;AACrG,QAAM,UAAU,KAAK,MAAM,YAAY,GAAI;AAC3C,SAAO;AAAA,IACL,QAAQ,QAAQ,MAAM,cAAc,KAAK,MAAM,YAAY,kBAAkB,GAAG,QAAQ,MAAM,CAAC;AAAA,IAC/F;AAAA,IACA,SAAS,OAAO,gBAAgB,OAAO;AAAA,EACzC;AACF;;;AC9EA,IAAM,iBAA2C;AAAA,EAC/C,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,aAAa;AAAA,EACb,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,YAAY;AACd;AAGA,IAAM,aAAmD;AAAA,EACvD,CAAC,YAAY,CAAC,cAAc,YAAY,SAAS,CAAC;AAAA,EAClD,CAAC,SAAS,CAAC,SAAS,UAAU,WAAW,SAAS,MAAM,CAAC;AAAA,EACzD,CAAC,SAAS,CAAC,OAAO,CAAC;AAAA,EACnB,CAAC,SAAS,CAAC,QAAQ,SAAS,SAAS,UAAU,QAAQ,YAAY,aAAa,QAAQ,CAAC;AAAA,EACzF,CAAC,QAAQ,CAAC,QAAQ,SAAS,SAAS,WAAW,UAAU,UAAU,QAAQ,SAAS,UAAU,UAAU,UAAU,QAAQ,QAAQ,CAAC;AAAA,EACnI,CAAC,UAAU,CAAC,UAAU,QAAQ,QAAQ,UAAU,OAAO,CAAC;AAAA,EACxD,CAAC,QAAQ,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,QAAQ,OAAO,QAAQ,SAAS,CAAC;AAAA,EAC1F,CAAC,OAAO,CAAC,OAAO,UAAU,WAAW,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACpE,CAAC,OAAO,CAAC,OAAO,QAAQ,WAAW,QAAQ,SAAS,WAAW,YAAY,SAAS,WAAW,QAAQ,CAAC;AAAA,EACxG,CAAC,SAAS,CAAC,SAAS,QAAQ,CAAC;AAAA,EAC7B,CAAC,UAAU,CAAC,UAAU,YAAY,QAAQ,OAAO,CAAC;AACpD;AAGA,IAAM,UAAU,CAAC,aACf,SACG,QAAQ,sBAAsB,OAAO,EACrC,YAAY,EACZ,MAAM,YAAY,EAClB,OAAO,OAAO;AAGZ,SAAS,cAAc,UAA4B;AACxD,QAAM,QAAQ,eAAe,QAAQ;AACrC,MAAI,MAAO,QAAO;AAClB,MAAI,SAAS,WAAW,SAAS,EAAG,QAAO;AAC3C,MAAI,SAAS,SAAS,IAAI,EAAG,QAAO;AACpC,QAAM,QAAQ,QAAQ,QAAQ;AAC9B,SAAO,WAAW,KAAK,CAAC,CAAC,EAAE,IAAI,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK;AAC1F;AAEA,IAAM,aAAa,CAAC,UAA0C,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI;AAMlH,SAAS,WAAW,MAAgB,EAAE,SAAS,SAAS,IAAgG,CAAC,GAAa;AAC3K,QAAM,QAAQ,WAAW,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI;AAC1F,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,UAAU,aAAa,MAAM,IAAI,GAAG,OAAO,OAAO;AAAA,EACjE,QAAQ;AACN,UAAM;AAAA,EACR;AACA,SAAO,WAAW,GAAG,KAAK,WAAW,UAAU,IAAI,KAAK,cAAc,KAAK,QAAQ;AACrF;;;ACvFA,SAAS,eAAAC,cAAa,gBAAAC,qBAAoB;AAE1C,SAAS,UAAU,QAAAC,OAAM,eAAAC,eAAa,aAAAC,aAAW,SAAAC,QAAO,WAAAC,WAAS,UAAAC,UAAQ,YAAAC,kBAAgB;;;ACHzF,SAAS,eAAAC,cAAa,WAAAC,UAAS,wBAAAC,6BAA4B;AAcpD,SAAS,QAAQ,WAAmB,WAA2B;AACpE,SAAO,GAAG,SAAS,IAAI,SAAS;AAClC;AAEA,SAAS,sBAAqC;AAC5C,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAMC,aAAY,oBAAI,IAAgB;AACtC,SAAO;AAAA,IACL,QAAQ,CAAC,QAAQ,KAAK,IAAI,GAAG;AAAA,IAC7B,SAAS,CAAC,KAAK,SAAS;AACtB,UAAI,KAAK,IAAI,GAAG,MAAM,KAAM;AAC5B,UAAI,KAAM,MAAK,IAAI,GAAG;AAAA,UACjB,MAAK,OAAO,GAAG;AACpB,iBAAW,YAAYA,WAAW,UAAS;AAAA,IAC7C;AAAA,IACA,WAAW,CAAC,aAAa;AACvB,MAAAA,WAAU,IAAI,QAAQ;AACtB,aAAO,MAAMA,WAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,EACF;AACF;AAGO,SAAS,iBAAiB,QAA+B;AAE9D,SAAOF,SAAQ,qBAAqB,CAAC,MAAM,CAAC;AAC9C;AAGO,SAAS,YAAY,OAAsB,KAAiD;AACjG,QAAM,OAAOC;AAAA,IACX,MAAM;AAAA,IACN,MAAM,MAAM,OAAO,GAAG;AAAA,IACtB,MAAM;AAAA,EACR;AACA,QAAM,UAAUF,aAAY,CAAC,SAAkB,MAAM,QAAQ,KAAK,IAAI,GAAG,CAAC,OAAO,GAAG,CAAC;AACrF,SAAO,CAAC,MAAM,OAAO;AACvB;;;ACnDA,SAAS,YAAAI,iBAAgB;;;ACSzB,IAAM,OAAO;AACb,IAAM,QAAQ;AACd,IAAM,UAAU;AAChB,IAAM,QAAQ;AAGd,IAAM,OAAO,OAAO;AACpB,IAAM,QAAQ,OAAO;AAErB,IAAM,WAAW,CAAC,MAAuB,OAAe,YAAY,OAAO;AACzE,QAAM,UAAU,SAAS,MAAM,QAAQ;AACvC,QAAM,SAAS,QAAQ,OAAO,KAAK;AACnC,SAAO,IAAI,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,WAAW,OAAO,mBAAmB,OAAO,KAAK,MAAM,GAAG,KAAK,IAAI,IAAI;AACvH;AACA,IAAM,OAAO,SAAS,KAAK,CAAC;AAE5B,IAAM,kBAAkB,SAAS,KAAK,GAAG,qBAAqB;AAC9D,IAAM,SAAS,SAAS,KAAK,CAAC;AAC9B,IAAM,SAAS,SAAS,KAAK,CAAC;AAC9B,IAAM,oBAAoB,SAAS,KAAK,CAAC;AAElC,SAAS,gBAAgB,UAA0B;AACxD,MAAI,SAAS;AACb,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,SAAS,MAAM,IAAI,GAAG;AACvC,QAAI,MAAM,KAAK,IAAI,GAAG;AACpB,eAAS,CAAC;AACV;AAAA,IACF;AACA,QAAI,OAAQ,OAAM,KAAK,IAAI;AAAA,aAClB,CAAC,KAAK,KAAK,IAAI,EAAG,OAAM,KAAK,UAAU,IAAI,CAAC;AAAA,EACvD;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO;AAAA,IACL,KACG,QAAQ,SAAS,IAAI,EACrB,QAAQ,OAAO,EAAE,EACjB,QAAQ,mCAAmC,IAAI;AAAA,EACpD;AACF;AAEA,SAAS,OAAO,MAAsB;AACpC,SAAO,KACJ,QAAQ,2BAA2B,IAAI,EACvC,QAAQ,0BAA0B,IAAI,EACtC,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,MAAM,IAAI,EAClB,QAAQ,iBAAiB,IAAI,EAC7B,QAAQ,QAAQ,IAAI,EACpB,QAAQ,QAAQ,IAAI,EACpB,QAAQ,mBAAmB,IAAI;AACpC;;;AD5CO,SAAS,oBAAoB,UAA8B,MAAe,UAA2B;AAC1G,QAAM,SAAS,cAAc;AAG7B,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,EAAE,MAAM,UAAU,cAAc,GAAG,CAAC;AACrE,MAAI,UAAU;AACd,MAAI,KAAK,aAAa,UAAU;AAE9B,cAAU,EAAE,MAAM,UAAU,cAAc,GAAG;AAAA,EAC/C,WAAW,KAAK,SAAS,MAAM;AAE7B,cAAU,EAAE,MAAM,UAAU,cAAc,OAAO,KAAK,gBAAgB,UAAU,MAAM,EAAE;AAAA,EAC1F;AACA,MAAI,YAAY,KAAM,SAAQ,OAAO;AACrC,SAAO,QAAQ;AACjB;AAEA,SAAS,gBAAgB,UAA8B,QAAoC;AACzF,QAAM,SAAS,SAAS,SAAS,CAAC,YAAY,QAAQ,SAAS,WAAW;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,OAAO,UAAU,UAAU;AAC1C,MAAI,WAAW,QAAS,QAAO,gBAAgB,QAAQ,OAAO,UAAU,KAAK,KAAK,OAAO,OAAO,OAAO,UAAU,OAAO,QAAQ,OAAO;AACvI,MAAI,WAAW,UAAW,QAAO,OAAO;AACxC,MAAI,WAAW,cAAe,QAAO,OAAO;AAC5C,MAAI,WAAW,oBAAqB,QAAO,iBAAiB,QAAQ,MAAM,KAAK,OAAO;AACtF,QAAM,OAAO,YAAY,QAAQ,EAAE,aAAa,eAAe,QAAQ,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC;AACzF,QAAM,SAAS,gBAAgB,IAAI;AACnC,SAAO,OAAO,KAAK,EAAE,WAAW,IAAI,OAAO,cAAc;AAC3D;AAGA,SAAS,iBAAiB,QAA0B,QAA2C;AAC7F,QAAM,UAAU,OAAO,MAAM,KAAK,CAAC,SAAS;AAC1C,UAAM,OAAO;AACb,YAAQ,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,SAAS,mBAAmB,KAAK,UAAU,qBAAqB,KAAK,aAAa;AAAA,EAClI,CAAC;AACD,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAS,QAAgE;AAC/E,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,MAAM,WAAW,OAAO,OAAO,UAAU,WAAW,MAAM,QAAQ;AACzH,SAAO,SAAS,KAAK,EAAE,SAAS,IAAI,GAAG,OAAO,QAAQ,KAAK,KAAK,QAAQ,KAAK,OAAO,QAAQ;AAC9F;;;AE3DA,SAAS,MAAM,eAAAC,cAAa,aAAAC,YAAW,qBAAqB,iBAAiB,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAE9G,SAAS,mBAAmB;AAuExB,gBAAAC,YAAA;AA7DJ,IAAM,+BAA+B;AAMrC,IAAM,YAAY;AAMlB,IAAM,uBAAuB,OAAO;AAKpC,IAAM,0BAA0B;AAGhC,IAAM,qBAAqB;AAG3B,IAAM,qBAAqB;AAE3B,IAAM,yBAAyB;AAE/B,IAAM,wBAAwB;AAC9B,IAAM,YAAY,oBAAI,IAAI,CAAC,OAAO,YAAY,aAAa,GAAG,CAAC;AAE/D,IAAM,cAAc,oBAAI,IAAI,CAAC,GAAG,WAAW,UAAU,WAAW,MAAM,CAAC;AAEvE,IAAM,yBAAyB;AAE/B,IAAM,UAAU,oBAAI,IAAI,CAAC,UAAU,WAAW,MAAM,CAAC;AAsBrD,SAAS,SAAS,EAAE,OAAO,UAAU,IAAI,GAA6B;AAIpE,SACE,gBAAAA,KAAC,SAAI,KAAU,OAAO,EAAE,GAAG,OAAO,oBAAoB,OAAO,GAAG,kBAAe,IAC5E,UACH;AAEJ;AAGA,SAAS,WAAW,EAAE,OAAO,UAAU,IAAI,GAAkC;AAC3E,SAAO,gBAAAA,KAAC,SAAI,KAAU,OAAO,EAAE,GAAG,OAAO,oBAAoB,OAAO,GAAI,UAAS;AACnF;AAGA,SAAS,YAAY,QAAqC;AACxD,MAAI,EAAE,kBAAkB,SAAU,QAAO;AACzC,SAAO,OAAO,QAAQ,2EAA2E,MAAM;AACzG;AAQA,SAAS,gBAAgB,EAAE,WAAW,UAAU,eAAe,iBAAiB,iBAAiB,sBAAsB,IAAI,GAAqB;AAC9I,QAAM,UAAUF,QAAuB,IAAI;AAC3C,QAAM,iBAAiBA,QAA0B,IAAI;AACrD,QAAM,QAAQA,QAAO,IAAI;AAEzB,QAAM,CAAC,iBAAiB,kBAAkB,IAAIC,UAAS,IAAI;AAC3D,QAAM,QAAQJ,aAAY,CAAC,UAAmB;AAC5C,UAAM,UAAU;AAChB,uBAAmB,KAAK;AAAA,EAC1B,GAAG,CAAC,CAAC;AAGL,QAAM,aAAaG,QAAO,CAAC;AAC3B,QAAM,UAAUA,QAAO,CAAC;AAGxB,QAAM,kBAAkBA,QAA4B,IAAI;AAExD,QAAM,oBAAoBA,QAAO,CAAC;AAClC,QAAM,iBAAiBA,QAAO,KAAK;AAEnC,QAAM,gBAAgBA,QAAO,CAAC;AAE9B,QAAM,mBAAmBA,QAAO,CAAC;AAEjC,QAAM,gBAAgBA,QAAO,CAAC;AAE9B,QAAM,gBAAgBA,QAAO,CAAC;AAC9B,QAAM,sBAAsBA,QAAO,QAAQ;AAC3C,QAAM,aAAaA,QAAsB,IAAI;AAC7C,QAAM,eAAeA,QAA4B,IAAI;AAErD,QAAM,qBAAqBA,QAAO,CAAC;AAEnC,QAAM,CAAC,eAAe,gBAAgB,IAAIC,UAAS,KAAK;AACxD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAIhD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,YAAY,SAAS,SAAS;AAEpC,EAAAH,WAAU,MAAM,iBAAiB,IAAI,GAAG,CAAC,CAAC;AAE1C,kBAAgB,MAAM;AACpB,UAAMK,YAAW,UAAU;AAC3B,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,iBAAiB,CAACA,aAAY,CAAC,KAAM;AAC1C,UAAM,UAAU,MAAM;AACpB,YAAM,SAAS,KAAK,sBAAsB,EAAE,MAAMA,UAAS,sBAAsB,EAAE,MAAMA,UAAS;AAClG,qBAAe,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,IAChD;AACA,YAAQ;AACR,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQA,SAAQ;AACzB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,WAAW,aAAa,CAAC;AAE7B,EAAAL,WAAU,MAAM;AACd,UAAMK,YAAW,UAAU;AAC3B,QAAI,CAAC,iBAAiB,CAACA,UAAU;AACjC,eAAW,UAAUA,UAAS;AAC9B,YAAQ,UAAUA,UAAS;AAI3B,QAAI;AACJ,UAAM,iBAAiB,MAAM;AAC3B,YAAM,OAAOA,UAAS,sBAAsB;AAC5C,iBAAW,WAAW,QAAQ,SAAS,iBAA8B,kBAAkB,KAAK,CAAC,GAAG;AAC9F,YAAI,QAAQ,MAAM,eAAe,SAAU;AAC3C,cAAM,MAAM,QAAQ,sBAAsB;AAC1C,YAAI,IAAI,SAAS,KAAK,OAAO,IAAI,MAAM,KAAK,QAAQ;AAClD,mBAAS,EAAE,SAAS,QAAQ,IAAI,MAAM,KAAK,MAAMA,UAAS,UAAU;AACpE;AAAA,QACF;AAAA,MACF;AACA,eAAS;AAAA,IACX;AACA,mBAAe;AACf,UAAM,WAAW,MAAM;AACrB,YAAM,SAASA,UAAS;AACxB,YAAM,MAAMA,UAAS;AACrB,YAAM,OAAO,KAAK,IAAI,QAAQ,WAAW,OAAO,IAAI,MAAMA,UAAS,eAAe;AAClF,YAAM,SAAS,kBAAkB,UAAU,KAAK,IAAI,GAAG,WAAW,UAAU,MAAM;AAClF,wBAAkB,UAAU;AAI5B,YAAM,OAAO,MAAM,QAAQ;AAK3B,YAAM,MAAM,YAAY,IAAI;AAC5B,YAAM,cAAc,MAAM,iBAAiB,WAAW,MAAM,cAAc;AAC1E,YAAM,cAAc,UAAUA,UAAS,SAAS,OAAO,OAAO,IAC1D,OAAO,QAAQ,sBAAsB,EAAE,MAAMA,UAAS,sBAAsB,EAAE,MAAM,MAAM,OAAO,SACjG;AACJ,YAAM,cAAc,cAAc,KAAK,KAAK,IAAI,OAAO,WAAW,KAAK;AACvE,YAAM,gBAAgB,OAAO,MAAM,CAAC,QAAQ,IAAI,UAAU,gBAAgB,CAAC;AAC3E,cAAQ,UAAU;AAClB,iBAAW,UAAU;AAGrB,qBAAe;AACf,UAAI,eAAe;AACjB,YAAI,MAAM,QAAS,CAAAA,UAAS,YAAY;AACxC;AAAA,MACF;AAGA,UAAI,OAAO,EAAG,OAAM,cAAc,QAAQ,IAAI;AAAA,eACrC,OAAO,GAAG;AAEjB,sBAAc,UAAU;AACxB,YAAI,SAAS,QAAQA,UAAS,gBAAgB,YAAY,IAAI,IAAI,cAAc,SAAU,OAAM,IAAI;AAAA,MACtG;AAAA,IACF;AACA,UAAM,YAAY,CAAC,UAAyB;AAE1C,UAAI,YAAY,MAAM,aAAa,EAAE,CAAC,KAAK,MAAM,MAAM,EAAG;AAC1D,UAAI,UAAU,IAAI,MAAM,GAAG,EAAG,eAAc,UAAU,YAAY,IAAI,IAAI;AAC1E,UAAI,YAAY,IAAI,MAAM,GAAG,EAAG,kBAAiB,UAAU,YAAY,IAAI,IAAI;AAC/E,UAAI,QAAQ,IAAI,MAAM,GAAG,EAAG,eAAc,UAAU,YAAY,IAAI,IAAI;AAAA,IAC1E;AACA,UAAM,UAAU,CAAC,UAAsB;AACrC,uBAAiB,UAAU,YAAY,IAAI,IAAI;AAC/C,UAAI,MAAM,SAAS,EAAG,eAAc,UAAU,YAAY,IAAI,IAAI;AAAA,IACpE;AACA,UAAM,eAAe,CAAC,UAAsB;AAC1C,iBAAW,UAAU,MAAM,UAAU,CAAC,GAAG,WAAW;AAAA,IACtD;AACA,UAAM,cAAc,CAAC,UAAsB;AACzC,uBAAiB,UAAU,YAAY,IAAI,IAAI;AAE/C,YAAM,IAAI,MAAM,UAAU,CAAC,GAAG;AAC9B,YAAM,KAAK,MAAM,UAAa,WAAW,YAAY,QAAQ,KAAK,WAAW;AAC7E,iBAAW,UAAU,KAAK;AAC1B,UAAI,GAAI,eAAc,UAAU,YAAY,IAAI,IAAI;AAAA,IACtD;AACA,UAAM,gBAAgB,MAAM;AAC1B,oBAAc,UAAU,YAAY,IAAI,IAAI;AAAA,IAC9C;AACA,UAAM,cAAc,MAAM;AACxB,UAAI,cAAc,UAAU,YAAY,IAAI,IAAI,uBAAwB,eAAc,UAAU,YAAY,IAAI,IAAI;AAAA,IACtH;AACA,oBAAgB,UAAU;AAC1B,IAAAA,UAAS,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAC/D,aAAS,iBAAiB,WAAW,SAAS;AAC9C,IAAAA,UAAS,iBAAiB,eAAe,aAAa;AACtD,IAAAA,UAAS,iBAAiB,SAAS,SAAS,EAAE,SAAS,KAAK,CAAC;AAC7D,IAAAA,UAAS,iBAAiB,cAAc,cAAc,EAAE,SAAS,KAAK,CAAC;AACvE,IAAAA,UAAS,iBAAiB,aAAa,aAAa,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,iBAAiB,aAAa,WAAW;AAChD,WAAO,iBAAiB,iBAAiB,WAAW;AACpD,WAAO,MAAM;AACX,sBAAgB,UAAU;AAC1B,MAAAA,UAAS,oBAAoB,UAAU,QAAQ;AAC/C,eAAS,oBAAoB,WAAW,SAAS;AACjD,MAAAA,UAAS,oBAAoB,eAAe,aAAa;AACzD,MAAAA,UAAS,oBAAoB,SAAS,OAAO;AAC7C,MAAAA,UAAS,oBAAoB,cAAc,YAAY;AACvD,MAAAA,UAAS,oBAAoB,aAAa,WAAW;AACrD,aAAO,oBAAoB,aAAa,WAAW;AACnD,aAAO,oBAAoB,iBAAiB,WAAW;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,WAAW,eAAe,KAAK,CAAC;AAGpC,EAAAL,WAAU,MAAM;AACd,UAAMK,YAAW,UAAU;AAC3B,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,iBAAiB,CAACA,aAAY,CAAC,KAAM;AAC1C,UAAM,iBAAiB,MAAM;AAG3B,UAAIA,UAAS,YAAY,QAAQ,QAAS,iBAAgB,UAAU;AACpE,YAAM,SAASA,UAAS;AACxB,YAAM,eAAeA,UAAS;AAC9B,wBAAkB,WAAW,KAAK,IAAI,GAAG,WAAW,UAAU,MAAM;AACpE,iBAAW,UAAU;AAGrB,YAAM,SAAS,SAAS,cAAc,WAAW,eAAe,oBAAoB;AACpF,oBAAc,UAAU;AACxB,0BAAoB,UAAU;AAC9B,UAAI,MAAM,WAAW,UAAU,YAAY,IAAI,KAAK,cAAc,QAAS,CAAAA,UAAS,YAAY;AAAA,IAClG;AAEA,QAAI,WAAW;AACf,UAAM,SAAS,MAAM;AACnB,UAAI,mBAAmB,UAAU,EAAG,oBAAmB,UAAU,YAAY,IAAI,IAAI;AACrF,qBAAe;AACf,UAAI,aAAa;AACf,mBAAW,sBAAsB,MAAM;AACrC,qBAAW;AACX,yBAAe;AAAA,QACjB,CAAC;AAAA,IACL;AAKA,UAAM,eAAe,IAAI,iBAAiB,MAAM;AAChD,iBAAa,QAAQ,MAAM,EAAE,SAAS,MAAM,WAAW,MAAM,YAAY,MAAM,iBAAiB,CAAC,OAAO,EAAE,CAAC;AAE3G,UAAM,mBAAmB,IAAI,eAAe,MAAM;AAClD,qBAAiB,QAAQA,SAAQ;AACjC,WAAO,MAAM;AACX,mBAAa,WAAW;AACxB,uBAAiB,WAAW;AAC5B,2BAAqB,QAAQ;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,WAAW,aAAa,CAAC;AAG7B,kBAAgB,MAAM;AACpB,QAAI,SAAS,WAAW,GAAG;AACzB,qBAAe,UAAU;AACzB,yBAAmB,UAAU;AAC7B,mBAAa,UAAU;AACvB,oBAAc,KAAK;AACnB;AAAA,IACF;AACA,UAAMA,YAAW,UAAU;AAC3B,QAAI,CAAC,iBAAiB,CAACA,aAAY,eAAe,QAAS;AAC3D,mBAAe,UAAU;AACzB,UAAM,aAAa,oBAAoB,SAAY,KAAK,SAAS,UAAU,CAAC,YAAY,QAAQ,OAAO,eAAe;AACtH,UAAM,aAAa,KAAK,eAAe,SAAS;AAGhD,QAAI,cAAc,KAAK,aAAa,WAAW;AAC7C,qBAAe,SAAS,cAAc,YAAY,EAAE,OAAO,QAAQ,CAAC;AACpE,yBAAmB,UAAU,YAAY,IAAI,IAAI;AAAA,IACnD,OAAO;AAIL,qBAAe,MAAM;AACnB,YAAI,QAAQ,SAAS,eAAe,UAAU,YAAYA,aAAY,MAAM,QAAS,CAAAA,UAAS,YAAYA,UAAS;AAAA,MACrH,CAAC;AACD,yBAAmB,UAAU;AAAA,IAC/B;AAEA,QAAI,QAAQ;AACZ,UAAM,SAAS,MAAM;AACnB,MAAAA,UAAS,oBAAoB,UAAU,MAAM;AAC7C,2BAAqB,KAAK;AAC1B,mBAAa,UAAU;AAAA,IACzB;AACA,UAAM,SAAS,MAAM;AACnB,aAAO;AACP,oBAAc,IAAI;AAAA,IACpB;AACA,iBAAa,UAAU;AACvB,IAAAA,UAAS,iBAAiB,UAAU,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1D,YAAQ,sBAAsB,MAAM;AAClC,cAAQ,sBAAsB,MAAM;AAAA,IACtC,CAAC;AAAA,EAEH,GAAG,CAAC,WAAW,eAAe,SAAS,QAAQ,SAAS,CAAC;AAGzD,EAAAL,WAAU,MAAM,MAAM,aAAa,UAAU,GAAG,CAAC,CAAC;AAQlD,QAAM,iBAAiBD,aAAY,MAAM;AACvC,UAAM,IAAI;AACV,QAAI,YAAY,IAAI,IAAI,mBAAmB,QAAS,gBAAe,SAAS,SAAS,oBAAoB;AACzG,uBAAmB,UAAU;AAC7B,UAAMM,YAAW,UAAU;AAC3B,QAAIA,UAAU,CAAAA,UAAS,YAAYA,UAAS;AAAA,EAC9C,GAAG,CAAC,WAAW,KAAK,CAAC;AACrB,sBAAoB,KAAK,OAAO,EAAE,eAAe,IAAI,CAAC,cAAc,CAAC;AAKrE,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,yBAAyB,SAAY,KAAK,SAAS,UAAU,CAAC,YAAY,QAAQ,OAAO,oBAAoB;AAIjI,QAAM,WAAW,UAAU;AAC3B,QAAM,aAAa,cAAc,mBAAmB,eAAe,YAAY,aAAa;AAC5F,QAAM,cAAc,aAAa,KAAK,IAAI,GAAG,YAAY,cAAc,SAAS,YAAY,SAAS,CAAC,IAAI;AAC1G,QAAM,YAAY,aAAa,KAAK,IAAI,WAAW,YAAY,cAAc,SAAS,YAAY,SAAS,YAAY,CAAC,IAAI;AAC5H,QAAM,cAAcJ,SAAQ,MAAM;AAChC,UAAM,OAAO,IAAI,IAAI,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,YAAY,cAAc,CAAC,EAAE,GAAG,CAAC,GAAG,WAAW,cAAc,MAAM,CAAC;AAC1H,QAAI,mBAAmB,aAAa,EAAG,MAAK,IAAI,SAAS;AACzD,QAAI,eAAe,EAAG,MAAK,IAAI,WAAW;AAC1C,WAAO,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EACvC,GAAG,CAAC,WAAW,aAAa,aAAa,iBAAiB,SAAS,CAAC;AAEpE,SACE,gBAAAG,KAAC,SAAI,KAAK,SACP,2BACC,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN,UAAU;AAAA,MACV,YAAY,aAAa,YAAY;AAAA,MACrC,MAAM;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MAEC,WAAC,SAAS,UAAU,cAAc,SAAS,KAAK;AAAA;AAAA,EACnD,GAEJ;AAEJ;AAGO,IAAM,cAAc,KAAK,eAAe;;;ACla/C,SAAS,eAAAE,cAAa,mBAAAC,kBAAiB,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAyCjE,SAAS,kBACd,UACA,EAAE,QAAQ,YAAY,GACN;AAChB,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAA4B,IAAI;AAC5D,QAAM,QAAQC,QAAsB,IAAI;AACxC,QAAM,cAAcA,QAAO,QAAQ;AACnC,cAAY,UAAU;AACtB,QAAM,aAAaA,QAAO,WAAW;AACrC,aAAW,UAAU;AAErB,EAAAC,iBAAgB,MAAM;AACpB,QAAI,CAAC,UAAU,OAAO,WAAW,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,OAAO,EAAE,EAAG;AACvF,UAAM,OAAO,YAAY,UAAU,MAAM;AACzC,QAAI,MAAM;AACR,gBAAU,EAAE,GAAG,QAAQ,IAAI,KAAK,GAAG,CAAC;AACpC;AAAA,IACF;AACA,UAAM,QAAQ,MAAM;AACpB,UAAM,UAAU;AAChB,cAAU,IAAI;AACd,QAAI,UAAU,QAAQ,MAAM,KAAK,EAAE,SAAS,KAAK,UAAU,OAAO,SAAU,YAAW,QAAQ,KAAK;AAAA,EACtG,GAAG,CAAC,QAAQ,UAAU,MAAM,CAAC;AAE7B,QAAM,OAAOC,aAAY,CAAC,cAAsB;AAC9C,UAAM,QAAQ,YAAY,QAAQ,UAAU,CAACC,aAAYA,SAAQ,OAAO,SAAS;AACjF,UAAM,UAAU,YAAY,QAAQ,KAAK;AACzC,QAAI,CAAC,QAAS;AACd,UAAM,UAAU;AAChB,cAAU,EAAE,IAAI,WAAW,SAAS,GAAG,UAAU,YAAY,OAAO,GAAG,MAAM,CAAC;AAAA,EAChF,GAAG,CAAC,CAAC;AACL,QAAM,QAAQD,aAAY,MAAM;AAC9B,UAAM,UAAU;AAChB,cAAU,IAAI;AAAA,EAChB,GAAG,CAAC,CAAC;AACL,QAAM,OAAOA,aAAY,CAAC,SAAiB;AACzC,UAAM,UAAU;AAAA,EAClB,GAAG,CAAC,CAAC;AACL,QAAM,SAASA,aAAY,CAAC,WAAmB,MAAc,UAAkB;AAC7E,UAAM,UAAU;AAChB,cAAU,CAAC,YAAY,WAAW,EAAE,GAAG,SAAS,IAAI,WAAW,SAAS,QAAQ,UAAU,GAAG,MAAM,CAAC;AAAA,EACtG,GAAG,CAAC,CAAC;AACL,QAAM,UAAUA,aAAY,CAAC,YAA8B,MAAM,WAAW,YAAY,OAAO,GAAG,CAAC,CAAC;AAEpG,SAAOE;AAAA,IACL,OAAO;AAAA,MACL,IAAI,QAAQ,MAAM;AAAA,MAClB,SAAS,QAAQ,WAAW;AAAA,MAC5B,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,OAAO,MAAM;AAAA,MACzD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,OAAO,SAAS,QAAQ,MAAM,QAAQ,IAAI;AAAA,EAC7C;AACF;AAGA,SAAS,YAAY,UAAuC,QAAkD;AAC5G,QAAM,SAAS,CAAC,YAA0C,SAAS,SAAS,UAAU,YAAY,OAAO,MAAM,OAAO;AACtH,QAAM,UAAU,SAAS,OAAO,KAAK;AACrC,MAAI,OAAO,OAAO,EAAG,QAAO;AAC5B,QAAM,UAAU,SAAS,OAAO,MAAM;AACtC,SAAO,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI;AAC7C;;;AC/EO,SAAS,aAAa,MAA2C;AACtE,SAAO,SAAS,WAAc,KAAK,cAAc,KAAK,kBAAkB,SAAS;AACnF;;;ACxBA,IAAM,eAAe;AAWd,SAAS,qBAAqB,QAAwC;AAC3E,QAAM,UAAU,oBAAI,IAAiC;AACrD,QAAM,WAAW,oBAAI,IAA0C;AAC/D,QAAM,QAAQ,CAAC,QAAgB,WAA0B,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA,EAAK,MAAM;AAC7F,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ,WAAW,QAAQ,IAAI,MAAM,QAAQ,MAAM,CAAC;AAAA,IAC1D,QAAQ,QAAQ,QAAQ;AACtB,YAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,YAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,UAAI,MAAO,QAAO,QAAQ,QAAQ,KAAK;AACvC,YAAM,UAAU,SAAS,IAAI,GAAG;AAChC,UAAI,QAAS,QAAO;AACpB,YAAM,SAAS,QAAQ,QAAQ,EAC5B,KAAK,MAAM,OAAO,OAAO,QAAQ,MAAM,CAAC,EACxC,MAAM,CAAC,WAAyC,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,EAAE,EAClH,KAAK,CAAC,YAAY;AACjB,iBAAS,OAAO,GAAG;AACnB,YAAI,QAAQ,QAAQ,aAAc,SAAQ,OAAO,QAAQ,KAAK,EAAE,KAAK,EAAE,KAAM;AAC7E,gBAAQ,IAAI,KAAK,OAAO;AACxB,eAAO;AAAA,MACT,CAAC;AACH,eAAS,IAAI,KAAK,MAAM;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC1CA,SAAS,eAAAC,cAAa,aAAAC,YAAW,OAAO,WAAAC,UAAS,YAAAC,iBAAgB;;;ACAjE,IAAM,eAAe;AAErB,IAAM,kBAAkB;AAOjB,SAAS,cAAc,KAAa,QAAwB;AACjE,QAAM,MAAM,IAAI,IAAI,CAAC,GAAG,IAAI,SAAS,YAAY,CAAC,EAAE,IAAI,CAAC,UAAU,MAAM,CAAC,CAAE,CAAC;AAC7E,MAAI,IAAI,SAAS,EAAG,QAAO;AAC3B,QAAM,OAAO,IAAI,OAAO,QAAQ,WAAW,EAAE,CAAC;AAC9C,SAAO,IAAI,QAAQ,iBAAiB,CAAC,OAAO,MAAe,IAAa,OAAgB,cAAuB;AAC7G,QAAI,SAAS,OAAW,QAAO,GAAG,IAAI,GAAG,EAAE,GAAG,IAAI,GAAG,KAAK;AAC1D,WAAO,IAAI,IAAI,SAAU,IAAI,IAAI,SAAS,GAAG,IAAI,KAAK;AAAA,EACxD,CAAC;AACH;;;ADTO,IAAM,qBAAqB,CAAC,aAAqB,mBAAmB,QAAQ,EAAE,OAAO,CAAC,YAAY,QAAQ,SAAS,SAAS,EAAE;AAmB9H,SAAS,iBAAiB,EAAE,UAAU,WAAW,WAAW,YAAY,SAAS,QAAQ,KAAK,GAA4B;AAC/H,QAAM,SAASC;AAAA,IACb,MAAM,mBAAmB,QAAQ,EAAE,QAAQ,CAAC,YAAa,QAAQ,SAAS,YAAY,CAAC,OAAO,IAAI,CAAC,CAAE;AAAA,IACrG,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,CAAC;AAExC,QAAM,YAAY,MAAM;AAGxB,EAAAC,WAAU,MAAM;AACd,QAAI,QAAQ;AACZ,eAAW,SAAS,QAAQ;AAC1B,WAAK,MAAM,UAAU,CAAC,cAAc,CAAC,QAAQ,IAAI,MAAM,QAAQ,MAAM,GAAG;AACtE,aAAK,QAAQ,QAAQ,MAAM,QAAQ,MAAM,EAAE,KAAK,MAAM,SAAS,WAAW,CAAC,UAAU,QAAQ,CAAC,CAAC;AAAA,MACjG;AAAA,IACF;AACA,WAAO,MAAM;AACX,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,SAAS,SAAS,CAAC;AAEvC,QAAM,gBAAgBC;AAAA,IACpB,CAAC,QAAgB,eAAoC;AACnD,YAAM,QAAQ,OAAO,UAAU;AAC/B,UAAI,aAAa,SAAS,CAAC,MAAM,OAAQ,QAAO,EAAE,OAAO,YAAY;AACrE,YAAM,SAAS,QAAQ,IAAI,QAAQ,MAAM;AACzC,UAAI,CAAC,OAAQ,QAAO,EAAE,OAAO,YAAY;AACzC,aAAO,SAAS,SAAS,EAAE,OAAO,SAAS,KAAK,cAAc,OAAO,KAAK,GAAG,SAAS,IAAI,UAAU,EAAE,GAAG,OAAO,IAAI,EAAE,OAAO,UAAU,OAAO;AAAA,IAChJ;AAAA;AAAA,IAEA,CAAC,QAAQ,QAAQ,WAAW,SAAS,WAAW,OAAO;AAAA,EACzD;AAEA,QAAM,gBAAgBA;AAAA,IACpB,CAAC,YAAoB,YAAyB;AAC5C,YAAM,QAAQ,OAAO,UAAU;AAC/B,YAAM,SAAS,SAAS,QAAQ,IAAI,MAAM,QAAQ,MAAM;AACxD,UAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,SAAS,QAAS;AACtD,YAAM,MAAM,GAAG,SAAS,IAAI,aAAa,UAAU;AACnD,YAAM,MAAM,cAAc,OAAO,KAAK,GAAG,SAAS,IAAI,UAAU,SAAS;AACzE,WAAK,KAAK,SAAS,CAAC,EAAE,MAAM,WAAW,KAAK,KAAK,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IAC1E;AAAA,IACA,CAAC,QAAQ,QAAQ,YAAY,WAAW,WAAW,MAAM,OAAO;AAAA,EAClE;AAEA,SAAO,EAAE,eAAe,GAAI,QAAQ,EAAE,cAAc,EAAG;AACzD;;;AE1EA,SAAS,WAAAC,UAAS,wBAAAC,6BAA4B;AAI9C,IAAM,kBAA2D;AAAA,EAC/D,OAAO,EAAE,YAAY,WAAW,MAAM,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,WAAW,MAAM,MAAM;AAAA,EAClH,MAAM,EAAE,YAAY,WAAW,MAAM,WAAW,MAAM,WAAW,MAAM,WAAW,QAAQ,WAAW,MAAM,KAAK;AAClH;AAEA,IAAM,aAAa;AAMnB,SAAS,aAAsB;AAC7B,QAAM,OAAO,SAAS;AACtB,MAAI,KAAK,UAAU,SAAS,MAAM,EAAG,QAAO;AAC5C,QAAM,SAAS,iBAAiB,IAAI,EAAE,aAAa,KAAK,KAAK;AAC7D,MAAI,WAAW,KAAK,MAAM,KAAK,CAAC,YAAY,KAAK,MAAM,EAAG,QAAO;AACjE,MAAI,YAAY,KAAK,MAAM,KAAK,CAAC,WAAW,KAAK,MAAM,EAAG,QAAO;AACjE,SAAO,OAAO,eAAe,cAAc,WAAW,UAAU,EAAE;AACpE;AAEA,SAASC,WAAU,UAAkC;AACnD,QAAM,WAAW,IAAI,iBAAiB,QAAQ;AAC9C,WAAS,QAAQ,SAAS,iBAAiB,EAAE,YAAY,MAAM,iBAAiB,CAAC,SAAS,SAAS,YAAY,EAAE,CAAC;AAClH,QAAM,QAAQ,OAAO,eAAe,aAAa,WAAW,UAAU,IAAI;AAC1E,SAAO,iBAAiB,UAAU,QAAQ;AAC1C,SAAO,MAAM;AACX,aAAS,WAAW;AACpB,WAAO,oBAAoB,UAAU,QAAQ;AAAA,EAC/C;AACF;AAEA,IAAM,iBAAiB,MAAM,MAAM;AAG5B,SAAS,YAAY,SAA2B;AACrD,SAAOD,sBAAqB,UAAUC,aAAY,gBAAgB,MAAM,WAAW,WAAW,GAAG,MAAM,KAAK;AAC9G;AAMO,SAAS,iBAAiB,MAAgB,SAA6C;AAC5F,QAAM,OAAO,YAAY,OAAO;AAChC,SAAOF,SAAQ,MAAM;AACnB,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,KAAK,gBAAgB,IAAI,KAAK,gBAAgB,OAAO,SAAS,OAAO;AAAA,EAC9E,GAAG,CAAC,MAAM,SAAS,IAAI,CAAC;AAC1B;;;ACpDA,SAAS,eAAAG,eAAa,WAAAC,WAAS,YAAAC,kBAAgB;;;ACA/C,SAAS,QAAAC,OAAM,aAAAC,YAAW,WAAAC,WAAS,UAAAC,SAAQ,YAAAC,kBAAgB;AAiClD,gBAAAC,YAAA;AA5BT,IAAM,iBAAiB;AAGvB,SAAS,uBAA+B;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAS,MAAM,KAAK,MAAO,OAAO,cAAc,IAAK,CAAC,CAAC;AACjF,EAAAC,WAAU,MAAM;AACd,UAAM,SAAS,MAAM,SAAS,KAAK,MAAO,OAAO,cAAc,IAAK,CAAC,CAAC;AACtE,WAAO,iBAAiB,UAAU,MAAM;AACxC,WAAO,MAAM,OAAO,oBAAoB,UAAU,MAAM;AAAA,EAC1D,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAeO,IAAM,gBAAgBC,MAAK,SAASC,eAAc,EAAE,QAAQ,QAAQ,OAAO,WAAW,GAAuB;AAClH,QAAM,SAASC,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,GAAG,CAAC,QAAQ,MAAM,CAAC;AAC/E,SAAO,gBAAAL,KAAC,SAAmB,QAAgB,MAAM,OAAO,MAAM,OAAc,cAAzD,MAAiF;AACtG,CAAC;AAED,SAAS,MAAM,EAAE,QAAQ,MAAM,OAAO,WAAW,GAA6E;AAC5H,QAAM,QAAQM,QAA0B,IAAI;AAC5C,QAAM,QAAQA,QAAO,CAAC;AACtB,QAAM,CAAC,QAAQ,SAAS,IAAIL,WAAS,cAAc;AACnD,QAAM,QAAQ,qBAAqB;AACnC,QAAM,WAAWK,QAAO,UAAU;AAClC,WAAS,UAAU;AAEnB,EAAAJ,WAAU,MAAM;AACd,UAAM,YAAY,CAAC,UAAwB;AACzC,YAAM,cAAc,MAAM,SAAS;AACnC,UAAI,aAAa,OAAO,WAAW,EAAG,QAAO,SAAS,QAAQ;AAC9D,YAAM,WAAW,aAAa,OAAO,WAAW;AAChD,UAAI,aAAa,KAAM,WAAU,QAAQ;AAAA,IAC3C;AACA,WAAO,iBAAiB,WAAW,SAAS;AAC5C,WAAO,MAAM,OAAO,oBAAoB,WAAW,SAAS;AAAA,EAC9D,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA,gBAAe;AAAA,MACf,wBAAqB;AAAA,MACrB,QAAQ,MAAM;AACZ,YAAI,EAAE,MAAM,UAAU,EAAG,UAAS,QAAQ;AAAA,MAC5C;AAAA,MAEA,OAAO,EAAE,SAAS,SAAS,OAAO,QAAQ,QAAQ,GAAG,KAAK,IAAI,QAAQ,KAAK,CAAC,MAAM,QAAQ,GAAG,aAAa,OAAO,SAAS,QAAQ;AAAA;AAAA,EACpI;AAEJ;;;AD9B8C,gBAAAO,YAAA;AA5BvC,SAAS,cAAc,MAAgB,SAA8C;AAC1F,QAAM,OAAO,YAAY,OAAO;AAChC,SAAOC,UAAQ,MAAM;AACnB,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,KAAK,aAAa,IAAI,KAAK,qBAAqB,OAAO,SAAS,OAAO;AAAA,EAChF,GAAG,CAAC,MAAM,SAAS,IAAI,CAAC;AAC1B;AAQO,SAAS,kBAAkB,EAAE,UAAU,WAAW,QAAQ,QAAQ,GAAmG;AAC1K,QAAM,SAAS,cAAc;AAC7B,QAAM,SAASA;AAAA,IACb,MAAO,SAAS,kBAAkB,UAAU,EAAE,OAAO,KAAK,CAAC,EAAE,QAAQ,CAAC,YAAa,QAAQ,SAAS,SAAS,CAAC,OAAO,IAAI,CAAC,CAAE,IAAI,CAAC;AAAA,IACjI,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACA,QAAM,CAAC,UAAU,WAAW,IAAIC,WAA8B,MAAM,oBAAI,IAAI,CAAC;AAC7E,QAAM,aAAaC;AAAA,IACjB,CAAC,QAAgB,eAAqC;AACpD,UAAI,OAAO,UAAU,GAAG,WAAW,MAAO,QAAO,YAAY,EAAE,OAAO,YAAY,IAAI,EAAE,OAAO,QAAQ,QAAQ,QAAQ,aAAa;AACpI,UAAI,CAAC,aAAa,MAAM,EAAG,QAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,WAAW;AAC9E,UAAI,SAAS,IAAI,MAAM,EAAG,QAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,WAAW;AAC7E,UAAI,QAAS,QAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,kBAAkB;AACvE,YAAM,aAAa,MAAM,YAAY,CAAC,YAAY,IAAI,IAAI,OAAO,EAAE,IAAI,MAAM,CAAC;AAC9E,aAAO,EAAE,OAAO,SAAS,QAAQ,OAAO,gBAAAH,KAAC,iBAAc,QAAgB,QAAiB,OAAO,OAAO,KAAK,OAAO,YAAwB,EAAG;AAAA,IAC/I;AAAA,IACA,CAAC,SAAS,QAAQ,QAAQ,QAAQ,UAAU,SAAS;AAAA,EACvD;AACA,SAAO,SAAS,aAAa;AAC/B;;;AE5BO,SAAS,mBAAmB,EAAE,UAAU,qBAAqB,WAAW,GAA+C;AAC5H,QAAM,QAA2B,CAAC;AAClC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAM,CAAC,SAA0B;AACrC,QAAI,KAAK,IAAI,KAAK,GAAG,EAAG;AACxB,SAAK,IAAI,KAAK,GAAG;AACjB,UAAM,KAAK,IAAI;AAAA,EACjB;AACA,aAAW,WAAW,UAAU;AAE9B,QAAI,qBAAqB;AACvB,iBAAW,QAAQ,oBAAoB,OAAO,GAAG,SAAS,CAAC,GAAG;AAC5D,YAAI,KAAK,SAAS,QAAS,KAAI,EAAE,MAAM,SAAS,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,eAAW,QAAQ,aAAa,OAAO,KAAK,CAAC,EAAG,KAAI,IAAI;AAAA,EAC1D;AACA,SAAO;AACT;;;AC/BA,IAAM,SAAS,MAAM;AAAC;AAGtB,IAAM,uBAAuB;AAWtB,SAAS,gBACd,SACA,UACA,YACoC;AACpC,QAAM,QAAQ,oBAAI,IAAmC;AACrD,MAAI,CAAC,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,MAAM,EAAG,QAAO;AAChE,QAAM,YAAY,oBAAI,IAAmC;AACzD,aAAW,QAAQ,WAAW,OAAO,GAAG,SAAS,CAAC,EAAG,KAAI,KAAK,YAAY,OAAW,WAAU,IAAI,KAAK,SAAS,IAAI;AACrH,MAAI,OAAmC;AACvC,UAAQ,MAAM,QAAQ,CAAC,MAAM,UAAU;AACrC,QAAI,KAAK,SAAS,QAAQ;AACxB,aAAO;AACP;AAAA,IACF;AACA,UAAM,OAAO,UAAU,IAAI,KAAK,GAAG;AACnC,QAAI,CAAC,cAAc,MAAM,SAAS,SAAS;AACzC,aAAO;AACP,YAAM,OAAO,MAAM,QAAQ,KAAK,YAAY,KAAK;AACjD,YAAM,IAAI,OAAO,EAAE,MAAM,QAAQ,UAAU,OAAO,cAAO,eAAe,IAAI,CAAC,MAAM,KAAK,GAAG,OAAO,aAAM,IAAI,GAAG,CAAC;AAChH;AAAA,IACF;AACA,UAAM,OAAO,EAAE,OAAO,SAAkB,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,UAAU,OAAO;AACnH,QAAI,MAAM;AACR,WAAK,MAAM,KAAK,IAAI;AACpB,WAAK;AACL,YAAM,IAAI,OAAO,IAAI;AAAA,IACvB,OAAO;AACL,aAAO,EAAE,SAAS,GAAG,aAAa,sBAAsB,OAAO,CAAC,IAAI,GAAG,SAAS,MAAM;AACtF,YAAM,IAAI,OAAO,EAAE,MAAM,UAAU,MAAM,KAAK,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,eAAe,MAAsB;AAC5C,SAAO,KAAK,QAAQ,YAAY,CAAC,SAAS,KAAK,IAAI,EAAE;AACvD;;;ACvDA,SAAS,UAAAI,gBAAc;AAgBP,gBAAAC,YAAA;AAbhB,IAAM,UAAU,MAAM;AAAC;AAMhB,SAAS,mBAAmB,EAAE,KAAK,GAAuB;AAC/D,QAAM,YAAYD,SAA8B,IAAI;AACpD,SACE,gBAAAC;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACC,MAAM;AAAA,MACN;AAAA,MACA,cAAa;AAAA,MACb,UAAU,gBAAAA,KAAC,KAAK,UAAL,EAAc,OAAM,IAAG,eAAe,SAAS,UAAU,SAAS,WAAW,OAAO,MAAM,OAAO,QAAQ,SAAS;AAAA;AAAA,EAC/H;AAEJ;;;ACnBA,SAAS,eAAAC,eAAa,aAAAC,YAAW,UAAAC,UAAQ,YAAAC,kBAAgB;AAgBlD,SAAS,iBAAiB,KAAa,SAAiB,cAAwF;AACrJ,QAAM,CAAC,SAAS,UAAU,IAAIC,WAAS,CAAC;AACxC,QAAM,kBAAkBC,SAAO,YAAY;AAC3C,kBAAgB,UAAU;AAC1B,QAAMC,WAAU,GAAG,OAAO;AAAA,EAAK,GAAG;AAAA,EAAK,OAAO;AAC9C,QAAM,CAAC,OAAO,QAAQ,IAAIF,WAA8D,IAAI;AAE5F,EAAAG,WAAU,MAAM;AACd,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,SAAS,CAAC,UAA4B;AAC1C,UAAI,CAAC,WAAW,OAAO,QAAS,UAAS,EAAE,SAAAD,UAAS,MAAM,CAAC;AAAA,IAC7D;AACA,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,GAAG,GAAG,YAAY;AAAA,UAC7C,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ,CAAC;AAAA,UACrC,QAAQ,WAAW;AAAA,QACrB,CAAC;AACD,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,UAAU,MAAM,gBAAgB,QAAQ;AAC9C,cAAI,WAAW,OAAO,QAAS;AAC/B,iBAAO,SAAS,QAAQ,EAAE,OAAO,WAAW,MAAM,QAAQ,KAAK,IAAI,EAAE,OAAO,SAAS,CAAC;AACtF,cAAI,SAAS,QAAQ,QAAQ,KAAM,iBAAgB,UAAU,EAAE,MAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM,SAAS,QAAQ,KAAK,CAAC;AAC9H;AAAA,QACF;AACA,cAAM,EAAE,GAAG,IAAK,MAAM,SAAS,KAAK;AACpC,eAAO,OAAO,OAAO,WAAW,EAAE,OAAO,SAAS,QAAQ,GAAG,IAAI,EAAE,OAAO,SAAS,CAAC;AAAA,MACtF,QAAQ;AACN,eAAO,EAAE,OAAO,SAAS,CAAC;AAAA,MAC5B;AAAA,IACF,GAAG;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,KAAK,SAASA,QAAO,CAAC;AAE1B,QAAM,QAAQE,cAAY,MAAM,WAAW,CAAC,UAAU,QAAQ,CAAC,GAAG,CAAC,CAAC;AACpE,SAAO,EAAE,GAAI,OAAO,YAAYF,WAAU,MAAM,QAAQ,EAAE,OAAO,UAAU,GAAI,MAAM;AACvF;;;AC7CO,SAAS,mBACd,UACA,MACiC;AACjC,MAAI,CAAC,YAAY,CAAC,KAAM,QAAO;AAC/B,SAAO,CAAC,YAAY;AAClB,UAAM,OAAO,SAAS,OAAO;AAC7B,QAAI,CAAC,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,OAAO,EAAG,QAAO;AAC/D,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO,KAAK,MAAM,IAAI,CAAC,SAAU,KAAK,SAAS,UAAU,EAAE,GAAG,MAAM,QAAQ,CAAC,YAAyB,KAAK,KAAK,IAAI,OAAO,EAAE,IAAI,IAAK;AAAA,IACxI;AAAA,EACF;AACF;;;ACtBA,SAAS,eAAAG,cAAa,oBAAoB;AAU1C,IAAMC,UAAS,MAAM;AAGd,IAAM,iBAAiB,CAAC,WAC7B,WAAW,SAAY,CAAC,IAAI,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAsB;AAM/E,SAAS,wBACd,QACA,MACyB;AACzB,QAAM,YAAY,KAAK,QAAQA;AAC/B,QAAM,SAAS,IAAI,IAAI,eAAe,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,UAAU,MAAM,CAAC,CAAC;AACxF,SAAO,CAAC,SAAS,OAAO,IAAID,aAAY,IAAI,CAAC,GAAG,SAAS,EAAE,MAAM,KAAK,KAAK,KAAK,QAAQ,KAAK,QAAQ,UAAU,CAAC;AAClH;AAGO,SAAS,qBAAqB,UAAqF;AACxH,SAAO,CAAC,YAAY;AAClB,QAAI,QAAQ,SAAS,YAAa,QAAO,CAAC;AAC1C,WAAO,QAAQ,MAAM;AAAA,MAAQ,CAAC,SAC5B,aAAa,IAAI,KACZ,SAAS,IAAI,GAAG,SAAS,CAAC,GAAG;AAAA,QAAQ,CAAC,SACrC,KAAK,UAAU,UACX;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,KAAK,KAAK;AAAA,YACV,KAAK,KAAK;AAAA,YACV,KAAK,KAAK;AAAA,YACV,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM;AAAA,YACpD,GAAI,KAAK,WAAW,UAAa,EAAE,QAAQ,KAAK,OAAO;AAAA,UACzD;AAAA,QACF,IACA,CAAC;AAAA,MACP,IACA,CAAC;AAAA,IACP;AAAA,EACF;AACF;;;ACpDA,SAAS,eAAAE,eAAa,aAAAC,aAAW,WAAAC,WAAS,UAAAC,UAAQ,YAAAC,kBAAgB;AAsBlE,IAAM,cAAc;AACpB,IAAI,eAAe;AAQnB,IAAM,eAAe,MAAgB,QAAQ,QAA2C,WAAW;AAO5F,SAAS,eAAe,EAAE,UAAU,SAAS,GAAiD;AACnG,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA4B,IAAI;AAC5D,QAAM,UAAUD,SAA2B,IAAI;AAC/C,QAAM,YAAYA,SAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,cAAcA,SAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,QAAM,QAAQD,UAAQ,MAAO,SAAU,OAAO,SAAS,SAAS,IAAK,CAAC,GAAI,CAAC,QAAQ,QAAQ,CAAC;AAC5F,QAAM,QAAQ,SAAS,MAAM,UAAU,CAAC,SAAS,KAAK,QAAQ,OAAO,GAAG,IAAI;AAG5E,QAAM,QAAQF,cAAY,CAAC,aAAsB,gBAAyB;AACxE,UAAM,UAAU,UAAU;AAC1B,QAAI,CAAC,QAAS;AACd,cAAU,UAAU;AACpB,cAAU,IAAI;AACd,QAAI,CAAC,eAAe,aAAa,MAAM,QAAQ,MAAO,SAAQ,KAAK;AACnE,UAAM,SAAS,QAAQ;AACvB,YAAQ,UAAU;AAClB,QAAI,eAAe,QAAQ,YAAa,QAAO,MAAM;AAAA,EACvD,GAAG,CAAC,CAAC;AAEL,QAAM,OAAOA;AAAA,IACX,CAAC,KAAa,QAAqB,QAAqC;AACtE,UAAI,EAAE,OAAO,YAAY,QAAQ,GAAG,KAAK,CAAC,SAAS,KAAK,QAAQ,GAAG,EAAG;AACtE,cAAQ,UAAU;AAClB,YAAM,UAAU,UAAU;AAC1B,YAAM,QAAQ,SAAS,SAAS,EAAE;AAClC,UAAI,CAAC,QAAS,SAAQ,UAAU,EAAE,GAAI,QAAQ,OAA0C,CAAC,WAAW,GAAG,MAAM,GAAG,EAAE;AAClH,YAAM,OAAmB,EAAE,KAAK,OAAO,GAAI,OAAO,EAAE,OAAO,IAAI,EAAG;AAClE,gBAAU,UAAU;AACpB,gBAAU,IAAI;AAAA,IAChB;AAAA,IACA,CAAC;AAAA,EACH;AAGA,QAAM,SAAS,WAAW;AAC1B,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,aAAa,MAAM;AACvB,UAAI,aAAa,MAAM,UAAU,SAAS,MAAO,OAAM,MAAM,IAAI;AAAA,IACnE;AACA,qBAAiB,YAAY,UAAU;AACvC,WAAO,MAAM,oBAAoB,YAAY,UAAU;AAAA,EACzD,GAAG,CAAC,QAAQ,KAAK,CAAC;AAGlB,QAAM,OAAO,WAAW,QAAQ,QAAQ;AACxC,EAAAA,YAAU,MAAM;AACd,QAAI,KAAM,OAAM,OAAO,IAAI;AAAA,EAC7B,GAAG,CAAC,MAAM,KAAK,CAAC;AAGhB,QAAM,WAAWE,SAAO,QAAQ;AAChC,EAAAF,YAAU,MAAM;AACd,QAAI,SAAS,YAAY,SAAU;AACnC,aAAS,UAAU;AACnB,UAAM,OAAO,KAAK;AAAA,EACpB,GAAG,CAAC,UAAU,KAAK,CAAC;AAGpB,EAAAA,YAAU,MAAM,MAAM,MAAM,OAAO,KAAK,GAAG,CAAC,KAAK,CAAC;AAElD,QAAM,gBAAgBD;AAAA,IACpB,CAAC,SAAiB;AAChB,YAAM,OAAO,MAAM,IAAI;AACvB,YAAM,UAAU,UAAU;AAC1B,UAAI,CAAC,QAAQ,CAAC,QAAS;AACvB,YAAM,QAAQ,EAAE,GAAG,SAAS,KAAK,KAAK,IAAI;AAC1C,gBAAU,UAAU;AACpB,gBAAU,KAAK;AAAA,IACjB;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AACA,QAAM,UAAUA,cAAY,MAAM,MAAM,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC;AAE7D,QAAM,SAASE;AAAA,IACb,MAAO,UAAU,SAAS,IAAI,EAAE,OAAmC,OAAO,eAAe,QAAQ,IAAI;AAAA,IACrG,CAAC,QAAQ,OAAO,OAAO,eAAe,OAAO;AAAA,EAC/C;AACA,SAAO,EAAE,MAAM,OAAO;AACxB;;;ACzHA,SAAS,eAAAG,oBAAmB;;;ACIrB,IAAM,uBAAuB;AAEpC,IAAM,eAAe;AAWd,SAAS,kBAAkB,QAAyC;AACzE,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,IACnB,SAAS,OAAO;AAAA,EAClB;AACF;AAWO,SAAS,UAAUC,SAAkC,QAA+D,UAAwC;AACjK,QAAM,SAAqB,CAAC;AAC5B,QAAM,SAAiC,CAAC;AACxC,aAAW,SAASA,SAAQ;AAC1B,UAAM,MAAM,OAAO,MAAM,IAAI;AAC7B,UAAM,OAAO,QAAQ,SAAY,KAAK,OAAO,GAAG,EAAE,KAAK;AACvD,QAAI,KAAK,WAAW,GAAG;AACrB,UAAI,MAAM,SAAU,QAAO,MAAM,IAAI,IAAI,SAAS;AAClD;AAAA,IACF;AACA,UAAM,UAAU,WAAW,OAAO,KAAK,MAAM,QAAQ;AACrD,QAAI,OAAO,YAAY,SAAU,QAAO,MAAM,IAAI,IAAI,QAAQ;AAAA,QACzD,QAAO,MAAM,IAAI,IAAI;AAAA,EAC5B;AACA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,EAAE,IAAI,OAAO,OAAO,IAAI,EAAE,IAAI,MAAM,OAAO;AACrF;AAEA,SAAS,WAAW,OAAsB,KAAkC,MAAc,UAAkE;AAC1J,QAAM,aAAa,EAAE,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,GAAG,KAAK,MAAM,GAAG,CAAC,EAAE;AAClF,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,UAAU;AACb,YAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,OAAO,KAAK,QAAQ,KAAK,GAAG,CAAC;AAC3E,UAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,EAAE,OAAO,SAAS,UAAU;AAChE,UAAK,OAAO,MAAM,QAAQ,YAAY,QAAQ,MAAM,OAAS,OAAO,MAAM,QAAQ,YAAY,QAAQ,MAAM,IAAM,QAAO;AACzH,aAAO;AAAA,IACT;AAAA,IACA,KAAK,QAAQ;AACX,UAAI,CAAC,WAAW,IAAI,EAAG,QAAO,EAAE,OAAO,SAAS,QAAQ;AAExD,UAAK,OAAO,MAAM,QAAQ,YAAY,OAAO,MAAM,OAAS,OAAO,MAAM,QAAQ,YAAY,OAAO,MAAM,IAAM,QAAO;AACvH,aAAO;AAAA,IACT;AAAA,IACA,KAAK;AACH,aAAO,MAAM,SAAS,SAAS,IAAI,IAAI,OAAO,EAAE,OAAO,SAAS,SAAS;AAAA,IAC3E;AACE,aAAO,KAAK,SAAS,uBAAuB,EAAE,OAAO,SAAS,QAAQ,oBAAoB,EAAE,IAAI;AAAA,EACpG;AACF;AAEA,IAAM,OAAO,CAAC,UAAwC,UAAU,SAAY,SAAY,OAAO,KAAK;AAEpG,SAAS,WAAW,MAAuB;AACzC,MAAI,CAAC,aAAa,KAAK,IAAI,EAAG,QAAO;AACrC,QAAM,OAAO,oBAAI,KAAK,GAAG,IAAI,YAAY;AACzC,SAAO,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,MAAM;AAC9E;AAGO,SAAS,aAAa,SAA4B,QAA2B,KAAa,KAA8B;AAC7H,QAAM,SAAS,IAAI,IAAI,MAAM;AAC7B,MAAI,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,QAAQ,SAAS,MAAM,CAAC,EAAG,QAAO;AACpE,QAAM,UAAU,QAAQ,OAAO,CAAC,WAAW,OAAO,IAAI,MAAM,CAAC;AAC7D,SAAO,QAAQ,UAAU,OAAO,QAAQ,UAAU,MAAM,UAAU;AACpE;;;ADhFA,IAAM,wBAAwB,oBAAI,IAAI,CAAC,cAAc,oBAAoB,cAAc,UAAU,CAAC;AAE3F,IAAM,kBAAkB;AAqBxB,SAAS,aAAa,MAAgB,OAAkG;AAC7I,QAAM,OAAOC,aAAY,IAAI;AAC7B,MAAI,SAAS,gBAAiB,QAAO;AACrC,QAAM,SAAU,KAA8B;AAC9C,QAAM,QAAQ,sBAAsB,IAAI,IAAI,KAAK,MAAM,YAAY,IAAI,IAAI;AAC3E,QAAM,cAAe,KAAgC,aAAa;AAClE,QAAM,iBAAiB,CAAC,SAAS,CAAC,eAAe,KAAK,UAAU,qBAAqB,MAAM;AAC3F,MAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,KAAK,UAAU,sBAAsB,oBAAoB,MAAM,GAAI,QAAO;AAE7G,QAAM,OAAO,EAAE,YAAY,KAAK,YAAY,MAAM,OAAO,KAAK,MAAM;AACpE,UAAQ,KAAK,OAAO;AAAA,IAClB,KAAK;AAEH,aAAO,WAAW,SAAY,EAAE,GAAG,MAAM,OAAO,YAAY,QAAQ,OAAO,IAAI;AAAA,IACjF,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,OAAO,MAAM,eAAe,YAAY,SAAS;AAAA,IACrE,KAAK;AACH,aAAO,oBAAoB,MAAM,IAAI,EAAE,GAAG,MAAM,OAAO,sBAAsB,IAAI,EAAE,GAAG,MAAM,OAAO,YAAY,QAAQ,OAAO;AAAA,IAChI,KAAK;AACH,UAAI,KAAK,WAAW,WAAW,sBAAsB,EAAG,QAAO;AAC/D,UAAI,sBAAsB,IAAI,IAAI,KAAK,aAAa,MAAM,KAAK,KAAK,MAAM,KAAM,QAAO;AACvF,aAAO,EAAE,GAAG,MAAM,OAAO,SAAS;AAAA,IACpC;AACE,aAAO;AAAA,EACX;AACF;AAsBO,SAAS,aAAa,MAAkB,UAA6C;AAC1F,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,WAAW,SAAS,SAAS,WAAW;AAC9C,QAAM,SAAS;AAAA,IACb,YAAY,KAAK;AAAA,IACjB;AAAA,IACA,SAAS,SAAS;AAAA,IAClB,GAAI,SAAS,YAAY,UAAa,EAAE,SAAS,SAAS,QAAQ;AAAA,IAClE,mBAAmB,KAAK,UAAU;AAAA,EACpC;AACA,QAAM,SAAS,KAAK,UAAU,aAAa,SAAS,KAAK,MAAM,IAAI;AAEnE,MAAI,CAAC,sBAAsB,IAAI,KAAK,IAAI,GAAG;AACzC,UAAM,WAAW,SAAS,KAAK,KAAK,IAAI,UAAU;AAClD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,GAAI,OAAO,aAAa,YAAY,SAAS,SAAS,KAAK,EAAE,SAAS;AAAA,MACtE,GAAI,SAAS,YAAY,UAAa,EAAE,SAAS,SAAS,QAAQ;AAAA,IACpE;AAAA,EACF;AACA,QAAM,QAAQ,aAAa,KAAK,MAAM,KAAK,KAAK;AAChD,MAAI,CAAC,MAAO,QAAO;AAEnB,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,UAAU;AACb,YAAM,SAAS,SAAS,UAAU;AAClC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAI,OAAO,WAAW,YAAY,EAAE,OAAO;AAAA,QAC3C,GAAI,YAAY,EAAE,UAAU,CAAC,WAAmB,MAAM,QAAQ,SAAS,MAAM,KAAK,SAAS,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,EAAE;AAAA,MACvH;AAAA,IACF;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,SAAS,SAAS,UAAU;AAClC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAI,MAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,OAAO,OAAO,CAAC,WAA6B,OAAO,WAAW,QAAQ,EAAE;AAAA,QAC/G,GAAI,YAAY;AAAA,UACd,UAAU,CAAC,YAAsB;AAC/B,kBAAM,UAAU,aAAa,MAAM,SAAS,SAAS,MAAM,KAAK,MAAM,GAAG;AACzE,gBAAI,QAAS,UAAS,EAAE,CAAC,UAAU,GAAG,QAAQ,CAAC;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,YAAM,QAAQ,SAAS,UAAU;AACjC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAI,OAAO,UAAU,YAAY,EAAE,MAAM;AAAA,QACzC,GAAI,YAAY;AAAA,UACd,QAAQ,CAAC,WAAmB,OAAO,UAAU,MAAM,KAAK,UAAU,MAAM,QAAQ,UAAU,MAAM,MAAM,SAAS,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC;AAAA,QACzI;AAAA,MACF;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAS,SAAS,SAAS,UAAU,CAAC;AAC5C,YAAMC,UAAS,MAAM,OAAO,IAAI,CAAC,UAAU;AACzC,cAAM,QAAQ,SAAS,cAAc,MAAM,IAAI;AAC/C,eAAO,UAAU,SAAY,QAAQ,EAAE,GAAG,OAAO,MAAM;AAAA,MACzD,CAAC;AACD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,OAAO,MAAM;AAAA,QACb,QAAAA;AAAA,QACA,GAAI,UAAU,EAAE,QAAQ,aAAa,MAAM,EAAE;AAAA,QAC7C,GAAI,YAAY;AAAA,UACd,UAAU,CAAC,cAA+C;AACxD,kBAAM,UAAU,UAAU,MAAM,QAAQ,WAAW,SAAS,QAAQ;AACpE,qBAAS,YAAY,QAAQ,KAAK,CAAC,IAAI,QAAQ,MAAM;AACrD,gBAAI,QAAQ,GAAI,UAAS,EAAE,CAAC,UAAU,GAAG,QAAQ,OAAO,CAAC;AAAA,UAC3D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAYA,SAAS,aAAa,MAAc,KAAiC;AACnE,QAAM,QAAQ,SAAS,GAAG;AAC1B,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,SAAS,YAAY;AACvB,UAAM,QAAQ,MAAM,OAAO;AAC3B,UAAMA,UAAS,MAAM,QAAQ,MAAM,QAAQ,CAAC,IAAI,MAAM,QAAQ,EAAE,IAAI,WAAW,IAAI,CAAC;AACpF,QAAI,OAAO,UAAU,YAAYA,QAAO,WAAW,KAAKA,QAAO,KAAK,CAAC,UAAU,UAAU,IAAI,EAAG,QAAO;AACvG,WAAO,EAAE,MAAM,QAAQ,OAAO,QAAQA,QAA0B;AAAA,EAClE;AACA,QAAM,WAAW,MAAM,UAAU;AACjC,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,EAAG,QAAO;AAClE,MAAI,SAAS,cAAc;AACzB,QAAI,MAAM,OAAO,MAAM,QAAS,QAAO,EAAE,MAAM,UAAU,UAAU,OAAO,SAAS,MAAM,GAAG,IAAI,EAAE;AAClG,UAAM,OAAO,MAAM,MAAM;AACzB,UAAM,KAAK,MAAM,IAAI;AACrB,QAAI,MAAM,OAAO,MAAM,WAAW,OAAO,SAAS,YAAY,OAAO,OAAO,YAAY,EAAE,OAAO,IAAK,QAAO;AAC7G,WAAO,EAAE,MAAM,UAAU,UAAU,OAAO,SAAS,MAAM,IAAI,GAAG,aAAa,OAAO,WAAW,GAAG,GAAG,aAAa,OAAO,SAAS,EAAE;AAAA,EACtI;AACA,QAAM,UAAU,UAAU,MAAM,SAAS,CAAC;AAC1C,MAAI,CAAC,WAAW,QAAQ,SAAS,EAAG,QAAO;AAC3C,MAAI,SAAS,aAAc,QAAO,EAAE,MAAM,UAAU,UAAU,QAAQ;AACtE,QAAM,MAAM,OAAO,MAAM,KAAK,MAAM,WAAW,MAAM,KAAK,IAAI;AAC9D,QAAM,MAAM,OAAO,MAAM,KAAK,MAAM,WAAW,MAAM,KAAK,IAAI,QAAQ;AACtE,MAAI,EAAE,OAAO,KAAK,OAAO,OAAO,OAAO,QAAQ,QAAS,QAAO;AAC/D,SAAO,EAAE,MAAM,gBAAgB,UAAU,SAAS,KAAK,IAAI;AAC7D;AAEA,SAAS,YAAY,KAAoC;AACvD,QAAM,QAAQ,SAAS,GAAG;AAC1B,QAAM,OAAO,QAAQ,MAAM;AAC3B,MAAI,CAAC,SAAS,OAAO,MAAM,MAAM,MAAM,YAAY,OAAO,MAAM,OAAO,MAAM,SAAU,QAAO;AAC9F,MAAI,SAAS,UAAU,SAAS,YAAY,SAAS,UAAU,SAAS,SAAU,QAAO;AACzF,QAAM,UAAU,UAAU,MAAM,SAAS,CAAC;AAC1C,MAAI,SAAS,aAAa,CAAC,WAAW,QAAQ,SAAS,GAAI,QAAO;AAClE,QAAMC,QAAO,CAAC,QAAuB;AACnC,UAAM,QAAQ,MAAM,GAAG;AACvB,WAAQ,SAAS,YAAY,OAAO,UAAU,YAAc,SAAS,UAAU,OAAO,UAAU,WAAY,EAAE,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC;AAAA,EAClI;AACA,SAAO;AAAA,IACL,MAAM,MAAM,MAAM;AAAA,IAClB,OAAO,MAAM,OAAO;AAAA,IACpB;AAAA,IACA,UAAU,MAAM,UAAU,MAAM;AAAA,IAChC,GAAGA,MAAK,KAAK;AAAA,IACb,GAAGA,MAAK,KAAK;AAAA,IACb,GAAI,SAAS,YAAY,EAAE,QAAkB;AAAA,IAC7C,GAAI,SAAS,UAAU,MAAM,WAAW,MAAM,QAAQ,EAAE,WAAW,KAAK;AAAA,EAC1E;AACF;AAEA,SAAS,aAAa,QAA6C;AACjE,SAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,UAA8C,OAAO,MAAM,CAAC,MAAM,YAAY,OAAO,MAAM,CAAC,MAAM,QAAQ,CAAC;AACtK;AAEA,SAAS,SAAS,OAAqD;AACrE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAK,QAAoC;AACrH;AAEA,SAAS,UAAU,OAAiC;AAClD,SAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,CAAC,SAAS,OAAO,SAAS,QAAQ,IAAI,QAAQ;AAC3F;AAEA,SAAS,aAAa,OAAgC,KAAwE;AAC5H,QAAM,QAAQ,MAAM,GAAG;AACvB,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,EAAE,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC;AAC7E;;;AE1OA,IAAM,wBAAwB;AAMvB,SAAS,mBAAmB,UAAuC,MAAgC;AACxG,MAAI,KAAM,QAAO;AACjB,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,MAAI,MAAM,SAAS,YAAa,QAAO;AACvC,QAAM,OAAO,KAAK,MAAM;AAAA,IACtB,CAAC,SAAS,KAAK,SAAS,QAAQ,eAAe,MAAO,KAA4B,UAAU;AAAA,EAC9F;AACA,QAAM,UAAW,MAAwD,OAAO;AAChF,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,QAAQ,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,qBAAqB;AAC9I,SAAO,OAAO,SAAS,IAAI,SAAS;AACtC;;;ACrBA,SAAS,QAAAC,OAAM,eAAAC,eAAa,WAAAC,WAAS,YAAAC,kBAAgB;;;ACArD,SAAS,iBAAiB;AAOnB,IAAM,oBAAN,cAAgC,UAA8E;AAAA,EAC1G,QAAQ,EAAE,SAAS,MAAM;AAAA,EAElC,OAAO,2BAAiD;AACtD,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAES,oBAA0B;AACjC,SAAK,MAAM,QAAQ;AAAA,EACrB;AAAA,EAES,SAAoB;AAC3B,WAAO,KAAK,MAAM,UAAU,OAAO,KAAK,MAAM;AAAA,EAChD;AACF;;;ADoBM,gBAAAC,YAAA;AA5BC,IAAM,aAAaC,MAAK,SAASC,YAAW;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,SAAS,cAAc;AAC7B,QAAM,WAAWC,UAAQ,MAAM,kBAAkB,OAAO,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC;AAClF,QAAM,CAAC,aAAa,cAAc,IAAIC,WAAiC,CAAC,CAAC;AACzE,QAAM,aAAa,KAAK;AACxB,QAAM,SAASC,cAAY,CAAC,UAAmB,SAAS,YAAY,KAAK,GAAG,CAAC,UAAU,UAAU,CAAC;AAClG,QAAM,CAAC,SAAS,UAAU,IAAID,WAAS,KAAK;AAC5C,QAAM,UAAUC,cAAY,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AACtD,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,UAAU,CAAC,WACzB,gBAAAL,KAAC,qBAAkB,SACjB,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,KAAK;AAAA,MACZ;AAAA,MACA;AAAA,MACA,OAAO,KAAK;AAAA,MACZ;AAAA,MACC,GAAI,KAAK,WAAW,UAAa,EAAE,QAAQ,KAAK,OAAO;AAAA,MACvD,GAAI,UAAU,EAAE,UAAU,OAAO;AAAA,MACjC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA;AAAA,EAC1C,GACF;AAEF,QAAM,OAAO,aAAa,MAAM;AAAA,IAC9B;AAAA,IACA,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,IACvC,GAAI,UAAU,EAAE,UAAU,OAAO;AAAA,IACjC;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,GAAI,WAAW,EAAE,QAAQ;AAAA,EAC3B,CAAC;AACD,SAAO,OAAO,gBAAAA,KAAC,UAAQ,GAAG,MAAM,IAAK;AACvC,CAAC;;;AE/DD,SAAS,eAAAM,eAAa,aAAAC,aAAW,UAAAC,UAAQ,YAAAC,kBAAgB;;;ACoBlD,SAAS,qBAAqB,YAAoC;AACvE,MAAI,OAAO;AAGX,MAAI,WAAW;AACf,MAAI,SAAS;AACb,MAAI,UAAU;AACd,MAAI,cAAc;AAElB,MAAI,UAAU;AACd,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,MAAI,OAAO;AACX,MAAI,WAAW;AAEf,SAAO;AAAA,IACL,MAAM,EAAE,WAAW,YAAY,GAAG;AAEhC,UAAI,OAAQ,QAAO;AACnB,eAAS,YAAY;AACrB,gBAAU,UAAU;AACpB,YAAM,QAAQ,OAAO,MAAM,QAAQ,EAAE,UAAU;AAC/C,YAAM,YAAY,UAAU,MAAM,SAAS,MAAM,MAAM,KAAK,IAAI,IAAI,KAAK;AACzE,iBAAW,UAAU;AACrB,aAAO,OAAO;AACd,oBAAc,UAAU,KAAK,OAAO,OAAO,YAAY;AACvD,gBAAU;AACV,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,OAAO;AACb,gBAAU;AACV,UAAI,UAAU,eAAe,OAAQ;AACrC,oBAAc;AACd,aAAO;AACP,iBAAW;AACX,oBAAc;AAAA,IAChB;AAAA,IACA,SAAS;AACP,eAAS;AAAA,IACX;AAAA,IACA,cAAc,UAAU;AACtB,UAAI,CAAC,YAAY,CAAC,eAAe,CAAC,QAAQ,WAAW,WAAW,EAAG,QAAO;AAC1E,YAAM,OAAO,QAAQ,MAAM,YAAY,MAAM;AAC7C,oBAAc,OAAO;AACrB,gBAAU,cAAc;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AD5CO,SAAS,kBAAkB,EAAE,OAAO,OAAO,UAAU,OAAO,GAA6C;AAC9G,QAAM,SAAS,cAAc;AAC7B,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAyB,MAAM;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAgE;AAC1F,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAChD,QAAM,UAAUC,SAA4B,IAAI;AAChD,QAAM,QAAQA,SAA8B,IAAI;AAEhD,QAAM,MAAMA,SAAO,CAAC;AACpB,QAAM,cAAcA,SAAO,KAAK;AAChC,QAAM,aAAaA,SAAO,KAAK;AAE/B,QAAM,gBAAgBA,SAAiB,CAAC,CAAC;AACzC,cAAY,UAAU;AAEtB,QAAM,OAAOC,cAAY,MAAM;AAC7B,QAAI,WAAW;AACf,YAAQ,SAAS,OAAO;AACxB,YAAQ,UAAU;AAClB,UAAM,UAAU;AAChB,aAAS,MAAM;AACf,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,CAAC;AAGL,EAAAC,YAAU,MAAM;AACd,UAAM,WAAW,cAAc,QAAQ,YAAY,KAAK;AACxD,QAAI,YAAY,GAAG;AACjB,oBAAc,QAAQ,OAAO,GAAG,WAAW,CAAC;AAC5C;AAAA,IACF;AACA,kBAAc,UAAU,CAAC;AACzB,QAAI,UAAU,WAAW,QAAS;AAClC,eAAW,UAAU;AACrB,UAAM,SAAS,QAAQ,KAAK;AAC5B,aAAS,MAAS;AAAA,EACpB,GAAG,CAAC,KAAK,CAAC;AAGV,EAAAA,YAAU,MAAM,MAAM,CAAC,QAAQ,IAAI,CAAC;AAEpC,QAAM,QAAQD,cAAY,MAAM;AAC9B,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,EAAE,IAAI;AACnB,UAAM,YAAY,qBAAqB,YAAY,OAAO;AAC1D,UAAM,UAAU;AAChB,eAAW,UAAU,YAAY;AACjC,kBAAc,UAAU,CAAC;AACzB,aAAS,MAAS;AAClB,aAAS,YAAY;AACrB,UAAM,QAAQ,MAAM,IAAI,YAAY;AACpC,YAAQ,UAAU,MAAM;AAAA,MACtB;AAAA,QACA,aAAa,MAAM,MAAM,KAAK,SAAS,CAAC,YAAa,YAAY,eAAe,cAAc,OAAQ;AAAA,QACtG,QAAQ,CAAC,SAAS;AAChB,cAAI,CAAC,MAAM,EAAG;AACd,gBAAM,UAAU,UAAU,MAAM,IAAI;AACpC,cAAI,YAAY,KAAM;AACtB,qBAAW,UAAU;AACrB,wBAAc,QAAQ,KAAK,OAAO;AAClC,mBAAS,OAAO;AAAA,QAClB;AAAA,QACA,SAAS,CAAC,MAAM,YAAY,MAAM,KAAK,SAAS,EAAE,MAAM,GAAI,WAAW,EAAE,QAAQ,EAAG,CAAC;AAAA,QACrF,aAAa,MAAM;AACjB,cAAI,CAAC,MAAM,EAAG;AACd,oBAAU,OAAO;AACjB,uBAAa,IAAI;AAAA,QACnB;AAAA,QACA,YAAY,CAAC,SAAS;AACpB,cAAI,CAAC,MAAM,EAAG;AAEd,gBAAM,UAAU,UAAU,cAAc,IAAI;AAC5C,cAAI,YAAY,KAAM;AACtB,qBAAW,UAAU;AACrB,wBAAc,QAAQ,KAAK,OAAO;AAClC,mBAAS,OAAO;AAAA,QAClB;AAAA,QACA,OAAO,MAAM;AACX,cAAI,CAAC,MAAM,EAAG;AACd,kBAAQ,UAAU;AAClB,gBAAM,UAAU;AAChB,mBAAS,MAAM;AACf,uBAAa,KAAK;AAAA,QACpB;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,GAAI,OAAO,UAAU,EAAE,UAAU,OAAO,OAAO,EAAG;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC,OAAO,UAAU,QAAQ,OAAO,MAAM,CAAC;AAE3C,QAAM,WAAWA,cAAY,MAAM;AACjC,QAAI,UAAU,OAAQ,OAAM;AAAA,aACnB,UAAU,aAAc,MAAK;AAAA,aAC7B,UAAU,aAAa;AAC9B,eAAS,UAAU;AACnB,cAAQ,SAAS,KAAK;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,OAAO,OAAO,IAAI,CAAC;AAEvB,QAAM,SAASA,cAAY,MAAM;AAC/B,QAAI,QAAQ,QAAS,MAAK;AAAA,EAC5B,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,OAAO,QACT,EAAE,OAAO,UAAU,GAAI,aAAa,EAAE,UAAU,GAAI,GAAI,SAAS,EAAE,OAAO,eAAe,OAAO,aAAa,MAAM,MAAM,MAAM,OAAO,EAAE,EAAG,IAC3I;AACJ,SAAO,EAAE,MAAM,OAAO;AACxB;;;AEnIA,SAAS,eAAAE,cAAa,gBAAAC,qBAAoB;AAC1C,SAAS,eAAAC,eAAa,aAAAC,aAAW,WAAAC,WAAS,UAAAC,UAAQ,YAAAC,kBAAgB;AAsBlE,IAAM,WAAW,CAAC,SAA0B,KAAK,UAAU,SAAS,KAAK,UAAU;AAW5E,SAAS,eAAe,EAAE,QAAQ,QAAQ,MAAM,OAAO,GAAuD;AACnH,QAAM,SAAS,cAAc;AAC7B,QAAM,YAAYC,SAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,CAAC,YAAY,aAAa,IAAIC,WAAiC,IAAI;AACzE,QAAM,UAAUD,SAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,SAASA,SAAO,KAAK;AAE3B,QAAM,aAAaA,SAAO,CAAC;AAC3B,QAAM,gBAAgBA,SAAO,oBAAI,IAAyC,CAAC;AAC3E,QAAM,eAAeA,SAAO,oBAAI,IAA8B,CAAC;AAE/D,QAAM,YAAYA,SAAO,oBAAI,IAAsB,CAAC;AAEpD,QAAM,gBAAgBA,SAAO,oBAAI,IAAY,CAAC;AAE9C,QAAM,eAAeA,SAAO,oBAAI,IAAY,CAAC;AAC7C,QAAM,YAAYA,SAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,oBAAoBA,SAAO,oBAAI,IAA2C,CAAC;AAEjF,QAAM,YAAYA,SAAO,oBAAI,IAAwC,CAAC;AAEtE,QAAM,eAAeE,cAAY,CAAC,aAAgC;AAChE,eAAW,YAAY,CAAC,GAAG,kBAAkB,OAAO,EAAG,UAAS,QAAQ;AAAA,EAC1E,GAAG,CAAC,CAAC;AAEL,QAAM,UAAUA,cAAY,CAAC,QAAmB;AAC9C,eAAW,YAAY,CAAC,GAAG,aAAa,OAAO,EAAG,UAAS,GAAG;AAAA,EAChE,GAAG,CAAC,CAAC;AAEL,QAAM,SAASC;AAAA,IACb,OAAO;AAAA,MACL,MAAM,CAAC,MAAM,YAAY;AACvB,YAAI,CAAC,QAAQ,QAAQ,iBAAiB,MAAM,OAAO,EAAG,gBAAe,MAAM,QAAQ,EAAE,WAAW,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,MACxH;AAAA,MACA,MAAM,MAAM,QAAQ,QAAQ,KAAK;AAAA,MACjC,oBAAoB,CAAC,MAAM,YAAY,QAAQ,QAAQ,wBAAwB,MAAM,OAAO;AAAA,MAC5F,wBAAwB,CAAC,aAAa;AACpC,sBAAc,QAAQ,IAAI,QAAQ;AAClC,eAAO,MAAM,KAAK,cAAc,QAAQ,OAAO,QAAQ;AAAA,MACzD;AAAA,MACA,oBAAoB,CAAC,aAAa;AAChC,qBAAa,QAAQ,IAAI,QAAQ;AACjC,eAAO,MAAM,KAAK,aAAa,QAAQ,OAAO,QAAQ;AAAA,MACxD;AAAA,MACA,4BAA4B,CAAC,aAAa;AACxC,0BAAkB,QAAQ,IAAI,QAAQ;AACtC,eAAO,MAAM,KAAK,kBAAkB,QAAQ,OAAO,QAAQ;AAAA,MAC7D;AAAA,MACA,SAAS,CAAC,WAAW,QAAQ,QAAQ,QAAQ,aAAa,WAAW,GAAG;AAAA,IAC1E;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAGA,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,OAAO,QAAS;AACrB,UAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAChC,QAAI,MAAM,SAAS,YAAa;AAChC,UAAM,UAAU,UAAU,QAAQ,IAAI,KAAK,EAAE,KAAK,CAAC;AACnD,SAAK,MAAM,QAAQ,CAAC,MAAM,cAAc;AACtC,UAAI,KAAK,SAAS,OAAQ;AAC1B,YAAM,SAAS,QAAQ,SAAS,KAAK;AACrC,cAAQ,SAAS,IAAI,KAAK,KAAK;AAC/B,UAAI,KAAK,KAAK,SAAS,QAAQ;AAC7B,mBAAW,YAAY,CAAC,GAAG,cAAc,OAAO,EAAG,UAAS,EAAE,WAAW,KAAK,IAAI,WAAW,MAAM,KAAK,KAAK,MAAM,MAAM,EAAE,CAAC;AAAA,MAC9H;AAAA,IACF,CAAC;AACD,cAAU,QAAQ,IAAI,KAAK,IAAI,OAAO;AAAA,EACxC,GAAG,CAAC,KAAK,QAAQ,CAAC;AAGlB,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,OAAO,QAAS;AACrB,UAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAChC,QAAI,MAAM,SAAS,YAAa;AAChC,eAAW,CAAC,WAAW,IAAI,KAAK,eAAe,IAAI,GAAG;AACpD,UAAI,cAAc,QAAQ,IAAI,KAAK,UAAU,EAAG;AAChD,oBAAc,QAAQ,IAAI,KAAK,UAAU;AACzC,YAAM,OAAO,GAAG,aAAa,MAAM,UAAU,OAAO,CAAC;AACrD,iBAAW,YAAY,CAAC,GAAG,cAAc,OAAO,EAAG,UAAS,EAAE,WAAW,KAAK,IAAI,WAAW,KAAK,CAAC;AAAA,IACrG;AAAA,EACF,GAAG,CAAC,KAAK,QAAQ,CAAC;AAGlB,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,OAAO,QAAS;AACrB,UAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAChC,QAAI,MAAM,SAAS,YAAa;AAChC,eAAW,CAAC,WAAW,UAAU,KAAK,YAAY,MAAM,UAAU,OAAO,GAAG;AAC1E,UAAI,aAAa,QAAQ,IAAI,UAAU,EAAG;AAC1C,mBAAa,QAAQ,IAAI,UAAU;AACnC,YAAM,OAAO,GAAG,UAAU,QAAQ,MAAM,aAAa;AACrD,iBAAW,YAAY,CAAC,GAAG,cAAc,OAAO,EAAG,UAAS,EAAE,WAAW,KAAK,IAAI,WAAW,KAAK,CAAC;AAAA,IACrG;AAAA,EACF,GAAG,CAAC,KAAK,QAAQ,CAAC;AAGlB,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,OAAO,QAAS;AACrB,UAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAChC,UAAM,MAAM,MAAM,SAAS,cAAc,WAAW,IAAI,IAAI,oBAAI,IAAwB;AACxF,UAAM,QAAQ,UAAU;AACxB,eAAW,CAAC,WAAWC,MAAK,KAAK,CAAC,GAAG,KAAK,GAAG;AAC3C,iBAAW,CAAC,WAAW,IAAI,KAAK,CAAC,GAAGA,MAAK,GAAG;AAC1C,YAAI,cAAc,MAAM,MAAM,IAAI,IAAI,SAAS,EAAG;AAClD,QAAAA,OAAM,OAAO,SAAS;AACtB,YAAI,CAAC,KAAK,MAAO,cAAa,EAAE,WAAW,WAAW,GAAG,MAAM,IAAI,EAAE,CAAC;AAAA,MACxE;AACA,UAAIA,OAAM,SAAS,EAAG,OAAM,OAAO,SAAS;AAAA,IAC9C;AACA,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,MAAM,IAAI,KAAK,EAAE,KAAK,oBAAI,IAA2B;AACnE,eAAW,CAAC,WAAW,IAAI,KAAK,KAAK;AACnC,UAAI,MAAM,IAAI,SAAS,EAAG;AAC1B,YAAM,IAAI,WAAW,EAAE,GAAG,MAAM,OAAO,MAAM,CAAC;AAC9C,mBAAa,EAAE,WAAW,KAAK,IAAI,WAAW,MAAM,KAAK,MAAM,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS,GAAI,OAAO,QAAQ,CAAC;AAAA,IACpI;AACA,QAAI,MAAM,OAAO,EAAG,OAAM,IAAI,KAAK,IAAI,KAAK;AAAA,EAC9C,GAAG,CAAC,KAAK,UAAU,YAAY,CAAC;AAGhC,QAAM,UAAUL,SAAO,KAAK,IAAI;AAChC,EAAAI,YAAU,MAAM;AACd,UAAM,QAAQ,QAAQ,WAAW,CAAC,KAAK;AACvC,YAAQ,UAAU,KAAK;AACvB,QAAI,CAAC,SAAS,CAAC,OAAO,QAAS;AAE/B,eAAW,CAAC,WAAW,KAAK,KAAK,UAAU,SAAS;AAClD,iBAAW,CAAC,WAAW,IAAI,KAAK,MAAO,KAAI,CAAC,KAAK,MAAO,cAAa,EAAE,WAAW,WAAW,GAAG,MAAM,IAAI,EAAE,CAAC;AAAA,IAC/G;AACA,cAAU,QAAQ,MAAM;AACxB,UAAM,OAAO,KAAK,SAAS,GAAG,EAAE;AAChC,QAAI,MAAM,SAAS,YAAa,SAAQ,EAAE,WAAW,IAAI,QAAQ,QAAQ,CAAC;AAAA,QACrE,SAAQ,EAAE,WAAW,KAAK,IAAI,QAAQ,UAAU,IAAI,EAAE,CAAC;AAAA,EAC9D,GAAG,CAAC,KAAK,MAAM,KAAK,UAAU,cAAc,OAAO,CAAC;AAGpD,EAAAA;AAAA,IACE,MAAM,MAAM;AACV,iBAAW,WAAW;AACtB,UAAI,CAAC,OAAO,QAAS;AACrB,aAAO,UAAU;AACjB,gBAAU,QAAQ,MAAM;AACxB,cAAQ,MAAM;AACd,oBAAc,IAAI;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,MAAM;AAAA,EACjB;AAEA,QAAM,SAASF,cAAY,MAAM;AAC/B,QAAI,CAAC,OAAQ;AACb,QAAI,YAAY,UAAU,SAAS;AAEjC,iBAAW,WAAW;AACtB,aAAO,MAAM;AACb,oBAAc,EAAE,OAAO,OAAO,iBAAiB,MAAM,OAAO,WAAW,OAAO,UAAU,MAAM;AAAA,MAAC,EAAE,CAAC;AAClG;AAAA,IACF;AACA,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,KAAK,EAAE,WAAW;AACxB,aAAO,UAAU;AACjB,gBAAU,UAAU,WAAW,QAAQ,QAAQ,QAAQ;AACvD,gBAAU,UAAU,UAAU,QAAQ,QAAQ,QAAQ;AACtD,oBAAc,UAAU,IAAI,IAAI,CAAC,GAAG,eAAe,QAAQ,QAAQ,SAAS,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,MAAM,KAAK,UAAU,CAAC;AACvH,mBAAa,UAAU,IAAI,IAAI,YAAY,QAAQ,QAAQ,SAAS,GAAG,EAAE,GAAG,UAAU,OAAO,EAAE,OAAO,CAAC;AACvG,oBAAc,EAAE,OAAO,cAAc,iBAAiB,MAAM,UAAU,MAAM;AAAA,MAAC,EAAE,CAAC;AAChF,aAAO,KAAK;AAAA,QACV;AAAA,QACA,MAAM;AAAA,QACN,QAAQ,CAAC,SAAS;AAChB,cAAI,OAAO,WAAW,QAAS;AAC/B,iBAAO,UAAU,CAAC,SAAS,IAAI;AAC/B,wBAAc,IAAI;AAAA,QACpB;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,eAAW,WAAW;AACtB,WAAO,UAAU;AACjB,WAAO,MAAM;AACb,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,QAAQ,QAAQ,QAAQ,UAAU,CAAC;AAEvC,SAAOC,UAAQ,MAAM;AACnB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,OAAwB,cAAc,EAAE,OAAO,OAAO,iBAAiB,MAAM,UAAU,OAAO;AACpG,WAAO,EAAE,GAAG,MAAM,UAAU,OAAO;AAAA,EACrC,GAAG,CAAC,QAAQ,YAAY,MAAM,CAAC;AACjC;AAGA,SAAS,WAAW,SAAoD;AACtE,QAAM,OAAO,oBAAI,IAAwB;AACzC,UAAQ,MAAM,QAAQ,CAAC,MAAM,cAAc;AACzC,QAAI,KAAK,SAAS,aAAa;AAC7B,UAAI,KAAK,UAAU,YAAa,MAAK,IAAI,WAAW,EAAE,MAAM,YAAY,CAAC;AAAA,IAC3E,WAAWG,cAAa,IAAI,GAAG;AAE7B,YAAM,UAAU,KAAK,UAAU,qBAAqB,KAAK,UAAU,qBAAqB,KAAK,UAAU,wBAAyB,KAAK,UAAU,sBAAsB,KAAK,gBAAgB;AAC1L,UAAI,QAAS,MAAK,IAAI,WAAW,EAAE,MAAM,QAAQ,UAAUC,aAAY,IAAI,EAAE,CAAC;AAAA,IAChF;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,IAAM,QAAQ,CAAC,UAAgH;AAAA,EAC7H,MAAM,KAAK;AAAA,EACX,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;AAAA,EAC/C,OAAO;AACT;AAGA,SAAS,UAAU,UAAgF;AACjG,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,QAAM,QAAQ,oBAAI,IAAwC;AAC1D,MAAI,MAAM,SAAS,YAAa,QAAO;AACvC,QAAM,OAAO,WAAW,IAAI;AAC5B,MAAI,KAAK,OAAO,EAAG,OAAM,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClH,SAAO;AACT;AAGA,SAAS,UAAU,SAAgD;AACjE,QAAM,SAAS,QAAQ,UAAU,UAAU;AAC3C,SAAO,WAAW,cAAc,gBAAgB;AAClD;AAEA,SAAS,WAAW,UAA8D;AAChF,QAAM,WAAW,oBAAI,IAAsB;AAC3C,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,YAAa;AAClC,aAAS;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ,MAAM,IAAI,CAAC,SAAU,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,CAAE;AAAA,IAC3E;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,eAAe,SAAgE;AACtF,QAAM,UAAU,oBAAI,IAAwB;AAC5C,MAAI,SAAS,SAAS,YAAa,QAAO;AAC1C,QAAM,eAAe,QAAQ,UAAU,WAAW;AAClD,UAAQ,MAAM,QAAQ,CAAC,MAAM,cAAc;AACzC,QAAI,CAACD,cAAa,IAAI,EAAG;AACzB,UAAM,OAAO,aAAa,MAAM,EAAE,cAAc,aAAa,oBAAI,IAAI,EAAE,CAAC;AACxE,QAAI,QAAQ,SAAS,YAAY,KAAK,UAAU,UAAW,SAAQ,IAAI,WAAW,IAAI;AAAA,EACxF,CAAC;AACD,SAAO;AACT;AAGA,SAAS,YAAY,SAAuC,QAAuD;AACjH,QAAM,QAAQ,oBAAI,IAAoB;AACtC,MAAI,CAAC,UAAU,SAAS,SAAS,YAAa,QAAO;AACrD,UAAQ,MAAM,QAAQ,CAAC,MAAM,cAAc;AACzC,QAAIA,cAAa,IAAI,KAAKC,aAAY,IAAI,MAAM,OAAO,YAAY,OAAO,SAAS,IAAI,GAAG,UAAU,QAAS,OAAM,IAAI,WAAW,KAAK,UAAU;AAAA,EACnJ,CAAC;AACD,SAAO;AACT;AAGA,SAAS,aAAa,MAAkB,QAAoC;AAC1E,QAAM,QAAQ,OAAO,KAAK,UAAU,YAAY,KAAK,UAAU,OAAQ,KAAK,QAAoC,CAAC;AACjH,QAAM,QAAQ,CAAC,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAChI,QAAM,UAAU,MAAM,QAAQ,MAAM,SAAS,CAAC,IAAI,MAAM,SAAS,EAAE,OAAO,CAAC,WAA6B,OAAO,WAAW,QAAQ,IAAI,CAAC;AACvI,OAAK,KAAK,SAAS,gBAAgB,KAAK,SAAS,uBAAuB,SAAS,QAAQ,SAAS,EAAG,QAAO,OAAO,MAAM,cAAc,OAAO,OAAO;AACrJ,SAAO,OAAO,MAAM,eAAe,KAAK;AAC1C;;;AClTA,SAAS,eAAAC,eAAa,WAAAC,WAAS,UAAAC,UAAQ,YAAAC,kBAAgB;AA+ChD,SAAS,qBAAqB,QAGnC;AACA,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAgC,MAAM,oBAAI,IAAI,CAAC;AAC7E,QAAM,aAAaD,SAAO,oBAAI,IAAY,CAAC;AAE3C,QAAM,MAAMF;AAAA,IACV,CAAC,QAAuB,SAA2B,SAAiB;AAClE,YAAM,MAAM,GAAG,QAAQ,EAAE;AAAA,EAAK,OAAO,EAAE;AACvC,UAAI,WAAW,QAAQ,IAAI,GAAG,EAAG;AACjC,UAAI;AACJ,UAAI;AACF,iBAAS,OAAO,MAAM,EAAE,QAAQ,WAAW,QAAQ,IAAI,SAAS,KAAK,CAAC;AAAA,MACxE,SAAS,OAAO;AACd,gBAAQ,MAAM,KAAK;AACnB;AAAA,MACF;AACA,UAAI,CAAC,cAAc,MAAM,EAAG;AAC5B,iBAAW,QAAQ,IAAI,GAAG;AAC1B,iBAAW,CAAC,YAAY,YAAY,SAAS,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC;AACzE,WAAK,QAAQ,QAAQ,MAAM,EACxB,MAAM,CAAC,UAAmB,QAAQ,MAAM,KAAK,CAAC,EAC9C,QAAQ,MAAM;AACb,mBAAW,QAAQ,OAAO,GAAG;AAC7B,mBAAW,CAAC,YAAY,YAAY,SAAS,QAAQ,IAAI,OAAO,IAAI,KAAK,CAAC;AAAA,MAC5E,CAAC;AAAA,IACL;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAGA,SAAOC,UAAQ,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC;AACzD;AAEA,SAAS,YAAY,SAAgC,WAAmB,UAAkB,IAAoC;AAC5H,QAAM,MAAM,QAAQ,IAAI,SAAS,KAAK,CAAC;AACvC,QAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,QAAM,UAAU,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,OAAO,CAAC,OAAO,OAAO,QAAQ;AAC5E,MAAI,QAAQ,SAAS,EAAG,MAAK,IAAI,WAAW,OAAO;AAAA,MAC9C,MAAK,OAAO,SAAS;AAC1B,SAAO;AACT;AAEA,IAAM,gBAAgB,CAAC,UACrB,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAQ,MAA6B,SAAS;;;AC7DxF,SAAS,sBAAsB,SAA0B,QAAmC;AACjG,SAAO,OAAO,YAAY,aAAa,QAAQ,MAAM,IAAI;AAC3D;;;ACjCA,SAAS,aAAAG,aAAW,UAAAC,gBAAc;AAiB3B,SAAS,sBACd,QACA,MACA,UACA,gBACM;AACN,QAAM,SAASC,SAAO,cAAc;AACpC,SAAO,UAAU;AACjB,QAAM,cAAcA,SAAO,KAAK;AAChC,QAAM,SAASA,SAAO,QAAQ;AAC9B,SAAO,UAAU;AAEjB,QAAM,iBAAiBA,SAA4B,oBAAI,IAAI,CAAC;AAC5D,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,QAAM,WAAW,MAAM,SAAS,cAAc,KAAK,KAAK;AACxD,QAAM,eAAe,MAAM,SAAS,cAAc,KAAK,UAAU,SAAS;AAE1E,EAAAC,YAAU,MAAM;AACd,QAAI,MAAM;AACR,UAAI,CAAC,YAAY,QAAS,gBAAe,UAAU,kBAAkB,OAAO,OAAO;AACnF,kBAAY,UAAU;AACtB;AAAA,IACF;AACA,QAAI,CAAC,YAAY,QAAS;AAC1B,UAAM,gBAAgB,MAAM;AAC1B,iBAAW,WAAW,qBAAqB,OAAO,OAAO,GAAG;AAC1D,YAAI,CAAC,eAAe,QAAQ,IAAI,QAAQ,EAAE,EAAG,QAAO,UAAU,EAAE,QAAQ,WAAW,QAAQ,IAAI,QAAQ,QAAQ,OAAO,CAAC;AAAA,MACzH;AAAA,IACF;AAGA,QAAI,aAAa,QAAW;AAC1B,kBAAY,UAAU;AACtB,oBAAc;AACd;AAAA,IACF;AAEA,QAAI,iBAAiB,UAAa,iBAAiB,YAAa;AAChE,gBAAY,UAAU;AACtB,kBAAc;AACd,WAAO,UAAU,EAAE,QAAQ,WAAW,UAAU,QAAQ,aAAa,CAAC;AAAA,EACxE,GAAG,CAAC,UAAU,cAAc,MAAM,MAAM,CAAC;AAC3C;AAIA,SAAS,cAAc,SAAoD;AACzE,QAAM,SAAS,QAAQ,UAAU;AACjC,SAAO,WAAW,UAAa,WAAW,cAAc,SAAY;AACtE;AAEA,SAAS,kBAAkB,UAA4D;AACrF,SAAO,IAAI,IAAI,SAAS,QAAQ,CAAC,YAAa,QAAQ,SAAS,eAAe,cAAc,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAE,CAAC;AAC5H;AAMA,SAAS,qBAAqB,UAAmF;AAC/G,QAAM,UAAsD,CAAC;AAC7D,MAAI,QAAQ,SAAS,GAAG,EAAE,GAAG,SAAS,cAAc,SAAS,SAAS,IAAI,SAAS,SAAS;AAC5F,aAAS;AACP,QAAI,QAAQ,KAAK,CAAC,aAAa,SAAS,KAAK,CAAE,EAAG;AAClD,WAAO,SAAS,KAAK,aAAa,SAAS,KAAK,CAAE,EAAG;AACrD,UAAM,SAAS,SAAS,KAAK;AAC7B,UAAM,SAAS,UAAU,OAAO,SAAS,cAAc,cAAc,MAAM,IAAI;AAC/E,QAAI,CAAC,UAAU,CAAC,OAAQ;AACxB,YAAQ,QAAQ,EAAE,IAAI,OAAO,IAAI,OAAO,CAAC;AACzC;AAAA,EACF;AACA,SAAO;AACT;;;ACrDA,IAAM,oBAAoB;AAG1B,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK,EAAE,QAAQ,eAAe,EAAE,EAAE,QAAQ;AACpF,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,OAAO,SAAS,oBAAoB,GAAG,OAAO,MAAM,GAAG,oBAAoB,CAAC,EAAE,QAAQ,CAAC,WAAM;AACtG;AAGA,SAAS,YAAY,MAAgB,SAAuE;AAC1G,QAAM,QAAQ,WAAW,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI;AAC1F,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACF,UAAM,SAAS,OAAO,UAAU,aAAa,MAAM,IAAI,IAAI;AAC3D,WAAO,OAAO,WAAW,YAAY,WAAW,OAAO,SAAS;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,aACd,MACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GACQ;AACR,QAAM,EAAE,OAAO,GAAG,QAAQ,IAAI;AAC9B,QAAM,MAAM,YAAY,SAAS,OAAO;AACxC,QAAM,QAAQ,YAAY,KAAK;AAC/B,QAAM,UAAU,YAAY,KAAK,OAAO,KAAK,YAAY,UAAU,OAAO,KAAK;AAC/E,MAAI,KAAK,UAAU,WAAW;AAC5B,QAAI,WAAY,QAAO,UAAU,OAAO,eAAe,OAAO,IAAI,OAAO;AACzE,WAAO,WAAW,OAAO;AAAA,EAC3B;AACA,MAAI,KAAK,UAAU,QAAS,QAAO,YAAY,KAAK,MAAM,KAAK,YAAY,UAAU,MAAM,MAAM,UAAU,OAAO,WAAW,OAAO,IAAI,OAAO;AAC/I,SAAO,YAAY,KAAK,IAAI,KAAK,YAAY,UAAU,IAAI,KAAK,SAAS,OAAO;AAClF;;;AClDO,IAAM,oBAAoB;AA0B1B,SAAS,aAAmB,SAA8B,EAAE,WAAW,SAAS,QAAQ,WAAW,MAAM,GAA0C;AACxJ,QAAM,YAAY,QAAQ,cAAc,CAAC,UAAU,MAAM,SAAS,WAAW,CAAC,MAAM,KAAK;AACzF,QAAM,SAAiC,CAAC;AACxC,MAAI,QAAQ;AACZ,SAAO,QAAQ,QAAQ,QAAQ;AAC7B,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,MAAM,SAAS,SAAS;AAC1B,aAAO,KAAK,EAAE,MAAM,SAAS,MAAM,MAAM,KAAK,CAAC;AAC/C;AACA;AAAA,IACF;AACA,UAAM,QAAoB,CAAC;AAC3B,UAAM,SAAiC,CAAC;AACxC,UAAM,MAAM,MAAM;AAClB,QAAI,WAAW;AACf,WAAO,QAAQ,QAAQ,QAAQ,SAAS;AACtC,YAAM,OAAO,QAAQ,KAAK;AAC1B,UAAI,KAAK,SAAS,QAAQ;AACxB,cAAM,KAAK,KAAK,IAAI;AACpB,mBAAW;AAAA,MACb,WAAW,KAAK,MAAO,QAAO,KAAK,EAAE,MAAM,SAAS,MAAM,KAAK,KAAK,CAAC;AAAA,UAChE;AAAA,IACP;AACA,QAAI,aAAa,WAAW,WAAW;AACrC,YAAM,OAAO,WAAW,OAAO,SAAS,MAAM;AAC9C,YAAM,YAAY,aAAa,WAAW,QAAQ,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,UAAU,KAAK,KAAK,UAAU,SAAS,GAAG,SAAS,QAAQ,KAAK,EAAE;AACjM,aAAO,KAAK,EAAE,MAAM,QAAQ,KAAK,QAAQ,OAAO,KAAK,KAAK,MAAM,GAAG,MAAM,KAAK,MAAM,GAAI,UAAU,SAAS,KAAK,EAAE,UAAU,EAAG,CAAC;AAAA,IAClI,OAAO;AACL,YAAM,OAAO,YAAY,WAAW,QAAQ,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,GAAG,SAAS,QAAQ,CAAC,SAAS;AACrH,UAAI,KAAK,QAAQ,EAAG,QAAO,KAAK,EAAE,MAAM,QAAQ,KAAK,GAAG,KAAK,CAAC;AAAA,IAChE;AACA,WAAO,KAAK,GAAG,MAAM;AAAA,EACvB;AACA,SAAO;AACT;AAMA,SAAS,WAAW,OAAmB,SAAkC,QAA4D;AACnI,QAAM,OAAO,MAAM,GAAG,EAAE;AACxB,QAAM,UAAU,MAAM,SAAS,CAAC,SAAS,KAAK,SAAS,UAAU,KAAK,KAAK,UAAU,SAAS;AAC9F,MAAI,KAAK,SAAS,UAAU,SAAS,SAAS,OAAQ,QAAO,EAAE,QAAQ,SAAS,SAAS,SAAS,MAAM,GAAG,MAAM,OAAO,SAAS,OAAO,EAAE;AAC1I,MAAI,KAAK,SAAS,WAAY,QAAO,EAAE,QAAQ,KAAK,SAAS,OAAO,UAAU,MAAM,WAAW;AAC/F,SAAO,EAAE,QAAQ,OAAO,aAAa,MAAM,OAAO,MAAM,OAAO,EAAE;AACnE;AAEA,SAAS,OAAO,MAA2C,SAA4C;AACrG,SAAO,WAAW,KAAK,MAAM,EAAE,SAAS,UAAU,KAAK,SAAS,CAAC;AACnE;AAEA,SAAS,SAAS,MAA2C,SAAkC,QAAwB,aAAa,OAAe;AACjJ,SAAO,aAAa,EAAE,GAAG,KAAK,MAAM,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM,EAAG,GAAG,EAAE,SAAS,QAAQ,UAAU,KAAK,UAAU,WAAW,CAAC;AACxJ;AAOA,SAAS,YACP,OACA,SACA,QACAC,WACuG;AACvG,QAAM,MAAM,MAAM;AAAA,IAAI,CAAC,SACrB,KAAK,SAAS,SACV,EAAE,GAAG,WAAW,MAAM,SAAS,QAAQA,SAAQ,GAAG,MAAM,KAAK,IAC7D,EAAE,MAAM,YAAY,QAAQ,KAAK,SAAS,OAAO,QAAQ,KAAK,cAAc,IAAI,GAAG,QAAQ,OAAO,MAAM,MAAM;AAAA,EACpH;AACA,QAAM,QAAQ,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI;AAC5C,QAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;AACzC,SAAO;AAAA,IACL,OAAO,UAAU,OAAO,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,OAAO,OAAO,EAAE,QAAQ,OAAO,EAAE;AAAA,IAChF,OAAO,MAAM;AAAA,IACb,OAAO,UAAU,KAAK,MAAM;AAAA,IAC5B,OAAO,MAAM,MAAM,GAAG,iBAAiB;AAAA,IACvC,WAAW,KAAK,IAAI,GAAG,MAAM,SAAS,iBAAiB;AAAA,EACzD;AACF;AAGA,SAAS,UAAU,OAAuB,QAAwC;AAChF,QAAM,OAAkD,CAAC;AACzD,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,KAAK,GAAG,EAAE;AAC3B,QAAI,UAAU,WAAW,KAAK,UAAU,SAAS,WAAW,KAAK,UAAU,SAAS,SAAS,KAAK,KAAM,UAAS;AAAA,QAC5G,MAAK,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC1F;AACA,SAAO,KAAK,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,MAAO,UAAU,IAAI,EAAE,GAAG,MAAM,QAAQ,OAAO,aAAa,KAAK,QAAQ,OAAO,EAAE,IAAI,IAAK;AACjI;AAEA,SAAS,WAAW,MAA2C,SAAkC,QAAwBA,WAAiC;AACxJ,QAAM,aAAaA,aAAY,KAAK,KAAK,UAAU;AACnD,SAAO,EAAE,MAAM,OAAO,MAAM,OAAO,GAAG,QAAQ,SAAS,MAAM,SAAS,QAAQ,UAAU,GAAG,QAAQ,KAAK,KAAK,UAAU,WAAW,WAAW;AAC/I;;;ACjIM,gBAAAC,YAAA;AApBN,IAAM,aAAa,MAAM;AAAC;AAOnB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,SAAS,cAAc;AAC7B,QAAM,CAAC,MAAM,OAAO,IAAI,YAAY,UAAU,GAAG,KAAK,GAAG,OAAO;AAChE,MAAI,KAAK,WAAW;AAClB,WAAO,KAAK,SAAS,SACnB,gBAAAA,KAAC,KAAK,WAAL,EAAe,OAAM,QAAO,QAAQ,KAAK,QAAQ,MAAM,KAAK,MAAO,GAAI,KAAK,aAAa,EAAE,WAAW,KAAK,UAAU,GAAI,IAE1H,gBAAAA;AAAA,MAAC,KAAK;AAAA,MAAL;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB;AAAA,QACA,cAAc;AAAA;AAAA,IAChB;AAAA,EAEJ;AACA,MAAI,KAAK,SAAS,OAAQ,QAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,UAAU,KAAK,QAAQ,OAAO,KAAK,QAAQ,OAAM,WAAU,MAAM,OAAO,cAAc,YAAY;AACrJ,QAAM,QAAQ,OAAO,SAAS,UAAU,KAAK,KAAK;AAClD,QAAM,OAAO,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,MAAM,EAAE,KAAK,IAAI;AAC5D,SAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,UAAU,OAAO,OAAc,OAAM,QAAO,QAAQ,MAAM,MAAY,cAAc,SAAS;AACxH;;;AjC6H0C,SAsRhC,YAAAC,WAtRgC,OAAAC,MA8WlC,YA9WkC;AAFnC,SAAS,UAAU,OAAuB;AAC/C,MAAI,MAAM,WAAW,UAAa,MAAM,YAAY,OAAW,OAAM,IAAI,MAAM,qDAAqD;AACpI,MAAI,MAAM,YAAY,OAAW,QAAO,gBAAAA,KAAC,oBAAkB,GAAG,OAAO,SAAS,MAAM,SAAS;AAC7F,MAAI,MAAM,WAAW,OAAW,OAAM,IAAI,MAAM,2CAA2C;AAI3F,SAAO,gBAAAA,KAAC,iBAAsF,GAAG,OAAO,QAAQ,MAAM,UAA3F,GAAG,MAAM,MAAM,IAAI,MAAM,cAAc,gBAAgB,OAAO,EAAqC;AAChI;AAGA,SAAS,iBAAiB,OAA6C;AACrE,QAAM,QAAQ,iBAAiB,MAAM,KAAK,MAAM,SAAS,MAAM,aAAa;AAC5E,MAAI,MAAM,UAAU,QAAS,QAAO,gBAAAA,KAAC,iBAAsF,GAAG,OAAO,QAAQ,MAAM,UAA3F,GAAG,MAAM,MAAM,IAAI,MAAM,cAAc,gBAAgB,OAAO,EAAqC;AAC3J,MAAI,MAAM,UAAU,UAAW,QAAO,gBAAAA,KAAC,MAAM,KAAK,aAAX,EAAuB,MAAK,SAAQ,MAAM,MAAM,MAAM,SAAS,MAAM,OAAO;AACnH,MAAI,MAAM,UAAU,SAAU,QAAO,gBAAAA,KAAC,MAAM,KAAK,aAAX,EAAuB,MAAK,SAAQ,SAAS,MAAM,OAAO;AAChG,SAAO,gBAAAA,KAAC,sBAAmB,MAAM,MAAM,MAAM;AAC/C;AAEA,SAAS,cAAc,EAAE,QAAQ,YAAY,iBAAiB,KAAK,QAAQ,MAAM,UAAU,OAAO,aAAa,aAAa,mBAAmB,gBAAgB,cAAc,WAAW,cAAc,eAAe,cAAc,gBAAgB,gBAAgB,eAAe,iBAAiB,YAAY,oBAAoB,eAAe,eAAe,mBAAmB,WAAW,UAAU,SAAS,UAAU,WAAW,QAAQ,QAAQ,WAAW,UAAU,aAAa,aAAa,cAAc,GAAwC;AAC1hB,QAAM,mBAAmBC,OAAM;AAC/B,QAAM,iBAAiBA,OAAM;AAC7B,QAAM,OAAO,aAAa;AAAA,IACxB;AAAA,IACA;AAAA,IACA,GAAI,YAAY,EAAE,SAAS;AAAA,IAC3B,GAAI,kBAAkB,EAAE,eAAe;AAAA,IACvC,GAAI,aAAa,EAAE,UAAU;AAAA,IAC7B,GAAI,iBAAiB,UAAa,EAAE,aAAa;AAAA,IACjD,GAAI,iBAAiB,EAAE,cAAc;AAAA,IACrC,GAAI,cAAc,UAAa,EAAE,UAAU;AAAA;AAAA,IAE3C,UAAU,KAAK,aAAa,QAAQ,aAAa;AAAA;AAAA,IAEjD,YAAY,CAAC,OAAiB,cAAwB,YAAY,eAAe,WAAW,SAAS,IAAI,aAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,IAC3I,uBAAuB,CAAC,cAAsB,eAAe,WAAW,SAAS;AAAA,EACnF,CAAC;AACD,QAAM,kBAAkB,mBAAmB;AAAA,IACzC,SAAS,GAAG,GAAG,IAAI,mBAAmB,MAAM,CAAC;AAAA,IAC7C,SAAS,KAAK,UAAU,KAAK,4BAA4B,QAAQ,KAAK,YAAY,KAAK,eAAe;AAAA,IACtG,SAAS,KAAK;AAAA,EAChB,CAAC;AAED,QAAM,qBAAqB,CAAC,KAAK,UAAU,CAAC,KAAK,cAAc,CAAC,KAAK;AAGrE,QAAM,aACJ,CAAC,sBACD,KAAK,aAAa,iBAAiB,UAClC,KAAK,aAAa,eAAgB,sBAAsB,UAAa,KAAK,uBAAuB;AACpG,QAAM,cAAc,eAAe,EAAE,KAAK,QAAQ,aAAa,KAAK,kBAAkB,oBAAoB,YAAY,cAAc,CAAC;AAErI,QAAM,sBAAsB,aAAc,gBAAgB,WAAW,KAAK,KAAK,qBAAsB,KAAK;AAC1G,QAAM,YAAY,kBAAkB,EAAE,OAAO,OAAO,KAAK,OAAO,UAAU,KAAK,UAAU,OAAO,CAAC;AACjG,QAAM,SAAS,eAAe,EAAE,QAAQ,aAAa,QAAQ,MAAM,GAAI,UAAU,EAAE,OAAO,EAAG,CAAC;AAC9F,QAAM,cAAc,mBAAmB,cAAc,EAAE,KAAK,QAAQ,iBAAiB,oBAAoB,CAAC;AAC1G,QAAM,gBAAgB,aAAa;AACnC,QAAM,eAAe,YAAY,eAAe,MAAM,CAAC;AACvD,QAAM,CAAC,uBAAuB,wBAAwB,IAAIC,WAAS,IAAI;AACvE,QAAM,gBAAgB,iBAAiB,qBAAqB,SAAS;AACrE,QAAM,2BAA2BC,UAAQ,OAAO,EAAE,GAAG,iBAAiB,qBAAqB,iBAAiB,uBAAuB,EAAE,OAAO,eAAe,UAAU,yBAAyB,EAAE,IAAI,CAAC,iBAAiB,aAAa,CAAC;AACpO,QAAM,oBAAoB,iBAAiB,iBAAiB,YAAY;AAExE,QAAM,iBAAiB,oBAAoB,KAAK,UAAU,YAAY,UAAU,IAAI,CAAC,EAAE,IAAI,MAAM,OAAO,OAAO,EAAE,IAAI,MAAM,QAAQ,OAAO,IAAI,CAAC,EAAE,IAAAC,KAAI,MAAAC,MAAK,OAAO,EAAE,IAAAD,KAAI,MAAAC,MAAK,EAAE,EAAE,EAAE,CAAC,IAAI;AACvL,QAAM,mBAAmBF,UAAkD,MAAM,iBAAiB,KAAK,MAAM,cAAc,IAAoC,QAAW,CAAC,cAAc,CAAC;AAE1L,QAAM,aAAaA;AAAA,IACjB,OAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,eAAe;AAAA,MACf,GAAI,oBAAoB,EAAE,YAAY,iBAAiB;AAAA,MACvD,GAAI,WAAW,EAAE,QAAQ;AAAA,MACzB,GAAI,YAAY,EAAE,SAAS;AAAA,MAC3B,GAAI,aAAa,EAAE,UAAU;AAAA,MAC7B,GAAI,aAAa,SAAS,KAAK,EAAE,QAAQ,aAAa;AAAA,MACtD,GAAI,UAAU,EAAE,OAAO;AAAA,MACvB,GAAI,aAAa,EAAE,UAAU;AAAA,MAC7B,GAAI,YAAY,EAAE,SAAS;AAAA,MAC3B,GAAI,eAAe,EAAE,YAAY;AAAA,MACjC,GAAI,eAAe,EAAE,YAAY;AAAA,IACnC;AAAA,IACA,CAAC,SAAS,UAAU,WAAW,cAAc,QAAQ,WAAW,UAAU,aAAa,aAAa,eAAe,mBAAmB,gBAAgB;AAAA,EACxJ;AACA,QAAM,YAAYG,SAAuB,IAAI;AAC7C,QAAM,UAAUA,SAA0B,IAAI;AAC9C,QAAM,SAAS,cAAc;AAC7B,QAAM,WAAW,iBAAiB,MAAM;AACxC,QAAM,eAAe,oBAAoB,KAAK,UAAU,KAAK,MAAM,MAAM;AACzE,QAAM,EAAE,MAAM,UAAU,OAAO,SAAS,eAAe,YAAY,MAAM,cAAc,mBAAmB,mBAAmB,gBAAgB,IAAI;AACjJ,QAAM,EAAE,cAAc,iBAAiB,eAAe,WAAW,cAAc,OAAO,IAAI;AAC1F,QAAM,aAAa,WAAW;AAC9B,QAAM,eAAeA,SAAO,kBAAkB;AAC9C,eAAa,UAAU;AACvB,EAAAC,YAAU,MAAM;AACd,QAAI,KAAK,OAAQ,cAAa,UAAU,EAAE,cAAc,OAAO,CAAC;AAAA,EAClE,GAAG,CAAC,cAAc,KAAK,QAAQ,MAAM,CAAC;AACtC,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,cAAcD,SAAO,aAAa;AACxC,cAAY,UAAU;AACtB,EAAAC,YAAU,MAAM;AACd,QAAI,KAAK,OAAQ,aAAY,UAAU,SAAS;AAAA,EAClD,GAAG,CAAC,KAAK,QAAQ,SAAS,CAAC;AAC3B,QAAM,oBAAoBD,SAAO,iBAAiB;AAClD,oBAAkB,UAAU;AAC5B,QAAME,eAAc,KAAK,YAAY,KAAK,cAAc,KAAK,kBAAkB;AAC/E,EAAAD,YAAU,MAAM;AACd,QAAIC,aAAa,mBAAkB,UAAU;AAAA,EAC/C,GAAG,CAACA,YAAW,CAAC;AAKhB,QAAM,CAAC,oBAAoB,qBAAqB,IAAIN,WAAgE,IAAI;AACxH,QAAM,YACJ,sBAAsB,mBAAmB,WAAW,WAAW,iBAAiB,MAAM,iBAAiB,mBAAmB,QACtH,mBAAmB,OACnB;AACN,QAAM,kBAAkBI,SAAO,YAAY;AAC3C,kBAAgB,UAAU;AAC1B,QAAM,iBAAiBG;AAAA,IACrB,CAAC,YAAoB,WACnB,KAAK,aAAa,YAAY,QAAQ,MAAM,sBAAsB,EAAE,MAAM,OAAO,QAAQ,YAAY,MAAM,gBAAgB,SAAS,OAAO,CAAC,CAAC;AAAA,IAC/I,CAAC,cAAc,QAAQ,MAAM;AAAA,EAC/B;AACA,QAAM,mBAAmBN,UAAQ,MAAM,mBAAmB,KAAK,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC;AACrG,QAAM,oBAAoB,KAAK,UAAU,UAAU;AACnD,QAAM,YAAYM;AAAA,IAChB,CAAC,SAAiB;AAChB,UAAI,UAAU,IAAI,EAAG,SAAQ,SAAS,eAAe;AAAA,IACvD;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AACA,QAAM,aAAaH,SAAO,YAAY;AACtC,aAAW,UAAU;AACrB,EAAAC,YAAU,MAAM;AACd,eAAW,UAAU,IAAI;AAAA,EAC3B,GAAG,CAAC,MAAM,MAAM,CAAC;AACjB,QAAM,YAAY,KAAK,SAAS,SAAS;AAEzC,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,eAAeJ,UAAQ,MAAO,YAAY,SAAS,IAAI,CAAC,GAAG,KAAK,UAAU,GAAG,WAAW,IAAI,KAAK,UAAW,CAAC,KAAK,UAAU,WAAW,CAAC;AAC9I,QAAM,gBAAgBA,UAAQ,MAAM,IAAI,IAAI,YAAY,IAAI,CAAC,eAAe,WAAW,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC;AAC1G,QAAM,sBAAsB,mBAAmB,YAAY;AAC3D,QAAM,mBAAmBA,UAAQ,MAAM,IAAI,IAAI,oBAAoB,MAAM,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC;AACrG,QAAM,aAAaG,SAAO,YAAY;AACtC,aAAW,UAAU;AACrB,EAAAC,YAAU,MAAM;AACd,eAAW,UAAU,EAAE,UAAU,KAAK,UAAU,KAAK,CAAC;AAAA,EACxD,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC;AACxB,wBAAsB,QAAQ,MAAM,KAAK,UAAU,cAAc;AACjE,QAAM,iBAAiB,kBAAkB,QAAQ,KAAK,QAAQ,MAAM,KAAK,UAAU,UAAU;AAC7F,QAAM,aAAa,qBAAqB,MAAM;AAC9C,QAAM,WAAWJ,UAAQ,MAAM,kBAAkB,KAAK,WAAW,GAAG,CAAC,KAAK,WAAW,CAAC;AACtF,QAAM,eAAe,kBAAkB,KAAK,QAAQ;AACpD,QAAM,gBAAgBA,UAAQ,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;AAE3G,QAAM,CAAC,iBAAiB,kBAAkB,IAAID,WAAwB,IAAI;AAC1E,QAAM,SAASI,SAAO,KAAK;AAC3B,QAAM,gBAAgBA,SAAO,KAAK,KAAK;AACvC,gBAAc,UAAU,KAAK;AAC7B,QAAM,EAAE,UAAU,iBAAiB,IAAI;AAEvC,QAAM,YAAYG;AAAA,IAChB,CAAC,SAAiB,iBAAiB,cAAc,QAAQ,KAAK,EAAE,SAAS,IAAI,GAAG,cAAc,OAAO;AAAA;AAAA,EAAO,IAAI,KAAK,IAAI;AAAA,IACzH,CAAC,gBAAgB;AAAA,EACnB;AAEA,QAAM,UAAU,kBAAkB,KAAK,UAAU,EAAE,QAAQ,aAAa,UAAU,CAAC;AACnF,QAAM,YAAY,QAAQ;AAE1B,QAAM,CAAC,UAAU,WAAW,IAAIP,WAAsD,EAAE,OAAO,EAAE,CAAC;AAGlG,QAAM,cAAc,aAAa,SAAS,KAAK,KAAK,kBAAkB,eAAe;AACrF,QAAM,eAAeC,UAAQ,MAAM,eAAe,qBAAqB,wBAAwB,aAAa,EAAE,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,CAAC;AACzJ,QAAM,iBAAiBM;AAAA,IACrB,MAAM,mBAAmB,EAAE,UAAU,KAAK,UAAU,qBAAqB,eAAe,YAAY,aAAa,CAAC;AAAA,IAClH,CAAC,KAAK,UAAU,eAAe,YAAY;AAAA,EAC7C;AACA,QAAM,cAAc,eAAe,EAAE,UAAU,gBAAgB,UAAU,GAAG,MAAM,IAAI,SAAS,KAAK,GAAG,CAAC;AACxG,QAAM,iBAAiBN;AAAA,IACrB,MAAM,eAAe,wBAAwB,aAAa,EAAE,KAAK,QAAQ,MAAM,KAAK,eAAe,YAAY,KAAK,CAAC;AAAA,IACrH,CAAC,aAAa,KAAK,QAAQ,KAAK,aAAa,YAAY,IAAI;AAAA,EAC/D;AACA,QAAM,sBAAsBA;AAAA,IAC1B,MAAM,mBAAmB,eAAe,KAAK,eAAe,YAAY,IAAI;AAAA,IAC5E,CAAC,eAAe,KAAK,aAAa,YAAY,IAAI;AAAA,EACpD;AAEA,QAAM,iBAAiBA,UAAQ,MAAM,YAAY,qBAAqB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAC3F,QAAM,gBAAgB,iBAAiB,MAAM,aAAa,MAAS;AACnE,QAAM,eAAeA;AAAA,IACnB,MACE,kBAAkB,gBACd,EAAE,SAAS,gBAAgB,QAAQ,eAAe,GAAI,KAAK,eAAe,EAAE,MAAM,YAAY,KAAK,EAAG,IACtG;AAAA,IACN,CAAC,eAAe,gBAAgB,YAAY,MAAM,KAAK,WAAW;AAAA,EACpE;AAEA,QAAM,aAAa,cAAc,MAAM,cAAc,IAAI;AAEzD,QAAM,QAAQA,UAAmC,MAAM,cAAc,EAAE,QAAQ,YAAY,SAAS,KAAK,gBAAgB,GAAG,CAAC,YAAY,KAAK,eAAe,CAAC;AAC9J,QAAM,eAAeM;AAAA,IACnB,OAAO,cAAsB;AAC3B,UAAI,MAAM,aAAa,SAAS,EAAG,aAAY,CAAC,UAAU,EAAE,OAAO,KAAK,QAAQ,GAAG,iBAAiB,UAAU,EAAE;AAAA,IAClH;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,YAAYN,UAAQ,MAAM,oBAAI,IAAuB,GAAG,CAAC,MAAM,aAAa,UAAU,eAAe,MAAM,YAAY,CAAC;AAC9H,QAAM,aAAaM;AAAA,IACjB,CAAC,cAAiC;AAChC,YAAM,SAAS,UAAU,IAAI,SAAS;AACtC,UAAI,WAAW,OAAW,QAAO;AACjC,YAAM,OAAO,cAAc,IAAI,SAAS;AAExC,UAAI,CAAC,QAAQ,CAAC,aAAa,SAAU,QAAO;AAC5C,YAAM,WAAW,KAAK,WAAW,QAAQ,SAAS;AAClD,UAAI,WAAW,EAAG,QAAO;AACzB,YAAM,SAAS,KAAK,WAAW,WAAW,CAAC;AAC3C,YAAM,QAAQ,KAAK,WAAW,WAAW,CAAC;AAC1C,YAAM,UACJ,gBAAAT;AAAA,QAAC,KAAK;AAAA,QAAL;AAAA,UACC,UAAU,WAAW;AAAA,UACrB,OAAO,KAAK,WAAW;AAAA,UACtB,GAAI,CAAC,QAAQ,WAAW,UAAa,EAAE,YAAY,MAAM,KAAK,aAAa,MAAM,EAAE;AAAA,UACnF,GAAI,CAAC,QAAQ,UAAU,UAAa,EAAE,QAAQ,MAAM,KAAK,aAAa,KAAK,EAAE;AAAA;AAAA,MAChF;AAEF,gBAAU,IAAI,WAAW,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,IACA,CAAC,MAAM,aAAa,UAAU,eAAe,MAAM,cAAc,SAAS;AAAA,EAC5E;AAEA,QAAM,WAAWS;AAAA,IACf,OAAO,WAAmB,SAAiB;AACzC,UAAI,OAAO,QAAS;AACpB,aAAO,UAAU;AACjB,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,WAAW,IAAI;AAC1C,YAAI,QAAQ,WAAW,WAAW;AAChC,kBAAQ,MAAM;AACd,kBAAQ,SAAS,eAAe;AAChC;AAAA,QACF;AACA,cAAM,OACJ,QAAQ,WAAW,YACf,oBAAoB,QAAQ,MAAM,IAClC,QAAQ,WAAW,UAAU,QAAQ,WAAW,YAC9C,QAAQ,SACR;AACR,cAAM,eAAe,QAAQ,WAAW,aAAa,QAAQ,WAAW,oBAAoB,QAAQ,SAAS;AAC7G,cAAM,QAAS,QAAQ,WAAW,aAAa,QAAQ,SAAU,eAAe,OAAO,mBAAmB,OAAO,WAAW,IAAI;AAChI,gBAAQ,OAAO,WAAW,MAAM,KAAK;AAAA,MACvC,UAAE;AACA,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,MAAM,SAAS,MAAM;AAAA,EACxB;AACA,QAAM,EAAE,OAAO,oBAAoB,WAAW,eAAe,QAAQ,WAAW,IAAI;AACpF,QAAM,iBAAiBN;AAAA,IACrB,OAAO,EAAE,OAAO,oBAAoB,WAAW,eAAe,QAAQ,CAAC,QAAQ,YAAY,aAAa,KAAK,WAAW,QAAQ,YAAY,QAAQ,GAAG,aAAa,OAAO,eAAe;AAAA,IAC1L,CAAC,oBAAoB,YAAY,QAAQ,aAAa;AAAA,EACxD;AAEA,QAAM,gBAAgBM;AAAA,IACpB,CAAC,SAA2B,UAAkB;AAC5C,UAAI,cAAc,IAAI,QAAQ,EAAE,EAAG,QAAO,gBAAAT,KAAC,eAAY,MAAY,SAAkB,UAAU,qBAAqB,gBAAgB,QAAW,SAAO,MAAC;AACvJ,YAAM,iBAAiB,WAAW,QAAQ,EAAE;AAC5C,YAAM,aAAa,KAAK;AACxB,YAAMU,WAAU,eAAe,OAAO;AACtC,YAAM,eAAe,eAAe,OAAO;AAC3C,YAAM,aAAa,aAAa,OAAO;AAEvC,YAAM,eAAe,WAAW,SAAS,KAAK,gBAAAV,KAAC,gBAAa,MAAY,SAAS,YAAY,OAAO,WAAW,aAAa;AAC5H,YAAM,iBAAiB,WAAW,SAAS,KAAKU,SAAQ,WAAW,KAAK,aAAa,WAAW,KAAK,QAAQ,MAAM,MAAM,iBAAiB;AAC1I,YAAM,UACJ,iBACE,gBAAAV,KAAAD,WAAA,EAAE,IACA,aAAa,SAAS,IACxB,gBAAAC,KAAC,gBAAa,MAAY,SAAS,cAAc,OAAO,OAAO,QAAQ,WAAW,IAChFU,SAAQ,SAAS,IACnB,aACE,gBAAAV,KAAAD,WAAA,EACG,UAAAW,SAAQ,IAAI,CAACC,SAAQ,gBACpB,gBAAAX,KAAC,cAA8B,GAAG,aAAaW,SAAQ,cAAc,KAApD,WAAuD,CACzE,GACH;AAAA;AAAA,QAGA,gBAAAX,KAAC,KAAK,aAAL,EAAiB,MAAM,cAAcU,UAAS,OAAO,OAAO,GAAI,GAAI,kBAAkB,EAAE,eAAe,GAAI;AAAA,UAE5G,QAAQ,SAAS,SACnB,cAAc,QAAQ,KACpB,gBAAAV;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,aAAa,QAAQ,QAAQ,OAAO;AAAA,UACpC,UAAU;AAAA,UACV;AAAA,UACC,GAAI,QAAQ,SAAS,EAAE,OAAO,QAAQ,MAAM;AAAA,UAC7C,eAAe,QAAQ;AAAA,UACvB,QAAQ,CAAC,SAAS,KAAK,SAAS,QAAQ,IAAI,IAAI;AAAA,UAChD,UAAU,QAAQ;AAAA;AAAA,QARb,QAAQ;AAAA,MASf,IAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACC,GAAI,WAAW,EAAE,QAAQ,MAAM,QAAQ,KAAK,QAAQ,EAAE,EAAE;AAAA;AAAA,MAC3D,IAEA,oBAAoB,QAAQ,MAAM,gBACpC,gBAAAA,KAAAD,WAAA,EACG,wBAAc,aAAa;AAAA,QAC1B;AAAA,QACA,SAAS,CAAC,iBAAiB;AACzB,6BAAmB,IAAI;AACvB,eAAK,aAAa,YAAY;AAAA,QAChC;AAAA,QACA,UAAU,MAAM,mBAAmB,IAAI;AAAA,MACzC,CAAC,GACH,IAEA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACC,GAAI,gBAAgB,EAAE,UAAU,aAAa;AAAA,UAC7C,GAAI,SAAS,EAAE,MAAM;AAAA,UACtB;AAAA,UACA,eAAe;AAAA,UACf;AAAA,UACA,aAAa,aAAa,IAAI,QAAQ,EAAE;AAAA,UACxC,SAAS,iBAAiB,IAAI,QAAQ,EAAE;AAAA,UACxC;AAAA,UACA,QAAQ,QAAQ,UAAU,YAAY,cAAe,QAAQ,UAAU,UAAU;AAAA,UACjF;AAAA,UACC,GAAI,UAAU,aAAa,YAAY,EAAE,SAAS,MAAM;AAAA,UACzD,YAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA,eAAe,UAAU,aAAa,CAAC,QAAQ,CAAC,cAAc,QAAQ,UAAU,WAAW;AAAA,UAC3F;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACC,GAAI,UAAU,aAAa,oBAAoB,KAAK,oBAAoB,CAAC,cAAc;AAAA,YACtF,kBAAkB,gBAAAA,KAAC,KAAK,kBAAL,EAAsB,SAAS,kBAAkB,QAAQ,WAAW,UAAU,mBAAmB;AAAA,UACtH;AAAA,UACC,GAAI,UAAU,aAAa,iBAAiB,EAAE,cAAc,WAAW;AAAA,UACvE,GAAI,iBAAiB,EAAE,UAAU,cAAc,MAAM,IAAI,QAAQ,EAAE,EAAE;AAAA,UACrE,GAAI,iBAAiB,aAAa,iBAAiB,CAAC,SAAS,QAAQ,UAAU,UAAU,gBAAgB,cAAc;AAAA,YACtH,QAAQ,MAAM,mBAAmB,QAAQ,EAAE;AAAA,UAC7C;AAAA,UACC,GAAI,kBAAkB,eAAe,SAAS,KAAK;AAAA,YAClD,SAAS;AAAA,YACT,MAAM,UAAU;AAAA,YAChB,kBAAkB,WAAW,QAAQ,IAAI,QAAQ,EAAE;AAAA,YACnD,aAAa,WAAW;AAAA,UAC1B;AAAA;AAAA,MACF;AAEJ,YAAM,QAAQ,eACZ,qBAAAD,WAAA,EACG;AAAA;AAAA,QACA;AAAA,SACH,IAEA;AAEF,YAAM,UAAU,SAAS,IAAI,QAAQ,EAAE;AAEvC,aAAO,UACL,qBAAAA,WAAA,EACG;AAAA;AAAA,QACA,QAAQ,IAAI,CAAC,WACZ,gBAAAC,KAAC,wBAAqC,MAAY,QAAgB,YAAvC,OAAO,EAAoD,CACvF;AAAA,SACH,IAEA;AAAA,IAEJ;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,SAAU,QAAO,gBAAAA,KAAC,KAAK,cAAL,EAAkB;AAG7C,MAAI,KAAK,cAAe,QAAO,gBAAAA,KAAC,KAAK,QAAL,EAAY,MAAM,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,SAAQ,MAAM,KAAK,cAAc,MAAM,SAAS,KAAK,OAAO,GAAI,UAAU,MAAM,WAAsB,cAAa,IAAG;AAE/L,QAAM,OACJ,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAEC,KAAK;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,iBAAiB;AAAA,MAChB,GAAI,cAAc,QAAQ,EAAE,sBAAsB,UAAU;AAAA,MAC5D,GAAI,SAAS,mBAAmB,EAAE,iBAAiB,SAAS,gBAAgB;AAAA,MAC7E;AAAA;AAAA,IAPK,SAAS;AAAA,EAQhB;AAKF,QAAM,sBAAsB,aAAa,cAAc,cAAc;AACrE,QAAM,eAAe,qBAAqB,YAAY;AACtD,QAAM,YAAY,KAAK,UAAU,UAAU;AAC3C,QAAM,iBAAiB,aAAa;AACpC,QAAM,YAAY,eAAe,KAAK,UAAU,CAAC,KAAK,QAAQ,CAAC,kBAAkB,YAAa,QAAQ,KAAK;AAE3G,QAAM,WAAW,KAAK,aAAa,eAAe,YAAa,QAAQ,KAAK,MAAM,KAAK,EAAE,SAAS;AAClG,QAAM,UAAU,KAAK,UAAU,CAAC,KAAK,QAAQ,CAAC;AAC9C,QAAM,iBAAiB,MAAM;AAC3B,QAAI,gBAAgB,CAAC,aAAa,CAAC,SAAU;AAC7C,QAAI,aAAa,UAAU;AAEzB,gBAAU,OAAO;AACjB,cAAQ,SAAS,eAAe;AAAA,IAClC;AAEA,UAAM,WAAW,KAAK,OAAO,eAAe,YAAa,UAAU,IAAI,QAAW,gBAAgB,CAAC,SAAS,YAAY,aAAa,IAAI,EAAE;AAC3I,QAAI,YAAY,aAAc,aAAa,SAAS;AAAA,EACtD;AACA,QAAM,sBAAsB,sBAAsB,iBAAiB;AAAA,IACjE,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK;AAAA,IACf,MAAM,CAAC,SAAS;AAEd,UAAI,CAAC,QAAS;AACd,UAAI,SAAS,OAAW,QAAO,eAAe;AAC9C,UAAI,CAAC,KAAK,UAAU,IAAI,EAAG;AAC3B,gBAAU,OAAO;AACjB,cAAQ,SAAS,eAAe;AAAA,IAClC;AAAA,IACA,UAAU,CAAC,UAAU,qBAAqB,SAAS,WAAW,KAAK;AAAA,IACnE,MAAM,KAAK;AAAA,IACX,SAAS;AAAA,IACT;AAAA,EACF,CAAC;AACD,QAAM,aAAa,iBAAiB,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,cAAc,KAAK,YAAY,KAAK,SAAS,KAAK,aAAa;AAE3I,QAAM,iBAAiB,kBAAkB,KAAK,kBAC5C,gBAAAA;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACC,IAAI;AAAA,MACJ,OAAO,KAAK,UAAU,SAAS;AAAA,MAC/B,MAAM,KAAK,WAAW,mBAAmB,KAAK,UAAU,MAAM,IAAI;AAAA;AAAA,EACpE;AAGF,QAAM,eACJ,WACC,KAAK,mBACJ,gBAAAA,KAAC,KAAK,kBAAL,EAAsB,IAAI,gBAAgB,MAAM,OAAO,MAAM,SAAS,OAAO,SAAS,IAEvF,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,SAAQ,MAAM,OAAO,SAAS;AAEzD,QAAM,yBAAyB,aAAc,KAAK,mBAAmB,iBAAiB,SAAa,iBAAiB,mBAAmB;AACvI,QAAM,WAAW,KAAK;AACtB,QAAM,YAAY,WAAW,gBAAgB,MAAM,OAAO,UAAU,IAAI,CAAC;AACzE,QAAM,YACJ,YAAY,UAAU,SAAS,IAC7B,gBAAAA,KAAAD,WAAA,EACG,oBAAU,IAAI,CAAC,SACd,gBAAAC;AAAA,IAAC;AAAA;AAAA,MAEC,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,WAAW,cAAc,MAAM,gBAAgB,GAAG;AAAA,MAClD,UAAU,MAAM,KAAK,gBAAgB,OAAO,KAAK,EAAE;AAAA,MACnD,WAAW,gBAAgB,aAAa,IAAI,KAAK,EAAE;AAAA;AAAA,IAN9C,KAAK;AAAA,EAOZ,CACD,GACH,IACE;AACN,QAAM,UAAU,CAAC,WAAW,YAAY,cAAc,cAAc,EAAE,OAAO,OAAO;AACpF,QAAM,SACJ,QAAQ,SAAS,IACf,gBAAAA,KAAAD,WAAA,EACG,kBAAQ,IAAI,CAAC,MAAM,UAClB,gBAAAC,KAAC,YAAsB,kBAAR,KAAa,CAC7B,GACH,IACE,QAAQ,CAAC;AAEf,QAAM,cAAc,QAAQ,UAAU,SAAS,UAAU,KAAK,UAAU,UAAU,UAAU,KAAK,UAAU,KACtG,QAAQ,UAAU,OAAO,UAAU,SAAS,OAAO,UAAU,OAAO;AACzE,QAAM,oBAAoB,KAAK,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,cAAc,eAAe,QAAQ,aAAa;AAC1H,QAAM,SACJ,gBAAAA;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACC;AAAA,MACA,YAAY;AAAA,MACZ,OAAO,qBAAqB,QAAQ,KAAK,UAAU,CAAC,KAAK,cAAc,CAAC,KAAK,cAAc,CAAC,KAAK,iBAAiB,CAAC,KAAK,WAAW,KAAK,eAAe,YAAY,CAAC,cAAc,CAAC,QAAQ,aAAa,WAAW;AAAA,MACnN;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,UACE,gBAAAA;AAAA,QAAC,KAAK;AAAA,QAAL;AAAA,UACC,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,eAAe,KAAK;AAAA,UACpB,UAAU;AAAA,UACV;AAAA,UACC,GAAI,KAAK,YAAY,EAAE,SAAS;AAAA,UACjC,MAAM,KAAK;AAAA,UACX,QAAQ,MAAM,KAAK,KAAK,KAAK;AAAA,UAC5B,GAAI,aAAa,eAAe,EAAE,YAAY;AAAA,UAC9C,GAAI,uBAAuB,EAAE,aAAa,oBAAoB,SAAS;AAAA,UACvE,GAAI,kBAAkB,EAAE,gBAAgB,MAAM,GAAI,0BAA0B,EAAE,uBAAuB,EAAG;AAAA,UACxG,GAAI,aAAa,SAAS,UAAU,QAAQ,EAAE,OAAO,UAAU,KAAK;AAAA,UACpE,GAAI,aAAa,SAAS,UAAU,EAAE,OAAO;AAAA,UAC7C,GAAI,uBAAuB,QAAQ,EAAE,SAAS,oBAAoB;AAAA;AAAA,MACrE;AAAA,MAED,GAAI,UAAU,EAAE,OAAO;AAAA;AAAA,EAC1B;AAEF,QAAM,SAAS,KAAK,eAAe,YAAY,UAAU,gBAAAA,KAAC,KAAK,aAAL,EAAkB,GAAG,YAAY,QAAQ;AACnG,SACE,gBAAAA,KAAC,iBAAiB,UAAjB,EAA0B,OAAO,WAChC,+BAAC,0BACE;AAAA;AAAA,IACA;AAAA,KACH,GACF;AAEJ;AAGA,SAAS,mBAAmB,UAAwB,QAAoC;AACtF,QAAM,EAAE,OAAO,KAAK,IAAI;AACxB,MAAI,KAAK,SAAS,cAAc,UAAU,eAAe,KAAK,WAAW,UAAW,QAAO,OAAO,0BAA0B,IAAI;AAChI,MAAI,KAAK,SAAS,cAAc,UAAU,aAAa,KAAK,cAAc,OAAW,QAAO,OAAO,qBAAqB,KAAK,WAAW,IAAI;AAC5I,UAAQ,UAAU,cAAc,OAAO,oBAAoB,OAAO,iBAAiB,IAAI;AACzF;AAGA,SAAS,kBAAkB,aAA2E;AACpG,QAAM,YAAY,oBAAI,IAAgC;AACtD,aAAW,UAAU,YAAa,WAAU,IAAI,OAAO,kBAAkB,CAAC,GAAI,UAAU,IAAI,OAAO,gBAAgB,KAAK,CAAC,GAAI,MAAM,CAAC;AACpI,SAAO;AACT;AAEA,IAAM,uBAAuBY,MAAK,SAASC,sBAAqB;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,YAAY,UAAU,cAAc,OAAO,EAAE,EAAE;AACvE,SAAO,gBAAAb,KAAC,KAAK,mBAAL,EAAuB,SAAS,OAAO,SAAS,MAAY,cAAc,SAAS;AAC7F,CAAC;AAMD,SAAS,iBACP,MACA,YACA,SACA,eACA,SACA,eACW;AACX,MAAI,eAAe,eAAgB,QAAO,gBAAAA,KAAC,KAAK,kBAAL,EAAsB,OAAM,gBAAe;AACtF,MAAI,eAAe,UAAW,QAAO,gBAAAA,KAAC,KAAK,kBAAL,EAAsB,OAAM,WAAU,SAAkB;AAC9F,MAAI,YAAY,aAAc,QAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,cAAa,SAAkB;AAE3F,MAAI,YAAY,iBAAkB,QAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,kBAAiB;AAEjF,MAAI,YAAY,mBAAoB,QAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,oBAAmB;AAErF,MAAI,eAAe;AACjB,UAAM,YAAY,CAAC,cAAc,SAAS;AAC1C,WAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,SAAQ,MAAM,cAAc,MAAO,GAAI,aAAa,EAAE,QAAQ,GAAI;AAAA,EAClG;AACA,MAAI,cAAe,QAAO,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAK,SAAQ,SAAkB;AAC3E,SAAO;AACT;AAGA,IAAM,sBAAgE;AAAA,EACpE,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,kBAAkB;AAAA;AAAA,EAElB,gBAAgB;AAClB;AAGA,SAAS,aAAa,EAAE,MAAM,SAAS,MAAM,GAA6E;AACxH,QAAM,OAAO,KAAK;AAClB,MAAI,CAAC,QAAQ,CAAC,MAAO,QAAO;AAC5B,QAAM,QAAQ,QAAQ,QAAQ,CAAC,WAAW;AACxC,UAAM,OAAO,OAAO,OAAO,OAAO,OAAO,IAAI,IAAI,MAAM,OAAO,IAAI,EAAG,MAAM,IAAI;AAC/E,WAAO,OAAO,CAAC,EAAE,MAAM,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC;AAAA,EACjD,CAAC;AACD,SACE,gBAAAA,KAAAD,WAAA,EACG,gBAAM,IAAI,CAAC,EAAE,MAAM,KAAK,GAAG,UAC1B,gBAAAC,KAAC,QAA8B,QAApB,GAAG,IAAI,IAAI,KAAK,EAAgB,CAC5C,GACH;AAEJ;AAGA,SAAS,aAAa,EAAE,MAAM,SAAS,MAAM,GAAgG;AAC3I,QAAM,OAAO,KAAK;AAClB,SACE,gBAAAA,KAAAD,WAAA,EACG,kBAAQ;AAAA,IAAI,CAAC,OAAO,UACnB,OACE,gBAAAC,KAAC,QAAiB,MAAM,MAAM,MAAO,GAAI,MAAM,aAAa,EAAE,WAAW,MAAM,UAAU,KAA9E,KAAkF,IAE7F,gBAAAA,KAAC,KAAK,cAAL,EAA8B,QAAO,YAAW,OAAO,OAAO,QAAQ,MAAM,KAAK,UAAU,WAAW,UAAU,MAAM,IAAI,GACzH,0BAAAA,KAAC,KAAK,WAAL,EAAe,UAAU,KAAK,MAAM,MAAM,SAAS,CAAC,OAAO,MAAM,IAAI,IAAI,WAAW,OAAO,KADtE,KAExB;AAAA,EAEJ,GACF;AAEJ;AAMA,IAAM,cAAcY,MAAK,SAASE,aAAY;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,QAAQX,UAAQ,MAAM,WAAW,OAAO,GAAG,CAAC,UAAU,OAAO,CAAC;AACpE,SACE,gBAAAH;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACC,MAAM,YAAY,OAAO;AAAA,MACxB,GAAI,SAAS,EAAE,aAAa,MAAM;AAAA,MAClC,GAAI,kBAAkB,EAAE,eAAe;AAAA,MACvC,GAAI,UAAU,EAAE,OAAO;AAAA,MACvB,GAAI,WAAW,EAAE,SAAS,KAAK;AAAA;AAAA,EAClC;AAEJ,CAAC;AAGD,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAUG;AACD,QAAM,CAAC,OAAO,QAAQ,IAAIE,WAAS,WAAW;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,KAAK;AAClD,QAAM,QAAQC,UAAQ,MAAM,WAAW,OAAO,GAAG,CAAC,UAAU,OAAO,CAAC;AAEpE,QAAM,UAAU,MAAM,KAAK,EAAE,SAAS,KAAK,UAAU;AACrD,SACE,gBAAAH;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACC,OAAO;AAAA,MACP,eAAe,CAAC,UAAU;AACxB,iBAAS,KAAK;AACd,sBAAc,KAAK;AACnB,sBAAc,MAAS;AAAA,MACzB;AAAA,MACA,QAAQ,MAAM,WAAW,OAAO,KAAK;AAAA,MACrC;AAAA,MACA;AAAA,MACC,GAAI,cAAc,EAAE,OAAO,WAAW;AAAA,MACtC,GAAI,SAAS,EAAE,aAAa,MAAM;AAAA;AAAA,EACrC;AAEJ;AAIA,IAAMe,kBAAiB,CAAC,SACtB,KAAK,QAAQ,YAAY,GAAG,EAAE,QAAQ,8BAA8B,CAAC,SAAS,KAAK,IAAI,EAAE;AAG3F,SAAS,kBAAkB,OAAuB,QAAoC;AACpF,QAAM,OAAO,WAAMA,gBAAe,MAAM,KAAK,CAAC,KAAK,MAAM,OAAO;AAChE,SAAO,MAAM,QAAQ,GAAG,IAAI,SAAM,OAAO,OAAO,KAAK,MAAM,KAAK,CAAC,KAAK;AACxE;AAGA,SAAS,gBAAgB,QAA8C;AACrE,SAAO,OAAO,UACX,KAAK,CAAC,aAAa,SAAS,OAAO,OAAO,QAAQ,UAAU,GAC3D,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,OAAO,QAAQ,OAAO,GAAG;AACnE;AAEA,IAAM,cAAcH,MAAK,SAASI,aAAY;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GA+CG;AACD,QAAM,SAAS,cAAc;AAC7B,QAAM,YAAY,QAAQ,UAAU,wBAAwB,CAAC;AAC7D,QAAM,gBAAgB,QAAQ,UAAU,iBAAiB,CAAC;AAC1D,MAAI,iBAAiB;AAErB,QAAM,SAAS,WAAW,aAAa;AACvC,QAAM,UAAoC,CAAC;AAC3C,QAAM,OAAO,CAAC,MAAiB,QAAQ,UAAU,KAAK,QAAQ,KAAK,EAAE,MAAM,SAAS,MAAM,GAAI,SAAS,EAAE,MAAM,EAAG,CAAC;AACnH,QAAM,OAAO,CAAC,OAAe,MAAgB,SAC3C,MAAM,SAAS,QAAQ,KAAK,EAAE,MAAM,QAAQ,KAAK,QAAQ,QAAQ,IAAI,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,KAAK,IAAI;AACxG,QAAM,cAAc,IAAI,IAAI,OAAO,KAAK,WAAW,WAAW,CAAC,CAAC,CAAC;AAEjE,MAAI,cAAc;AAClB,QAAM,YAAY,gBAAgB,SAAS,eAAe,QAAQ,KAAK,eAAe,CAAC;AAEvF,UAAQ,MAAM,QAAQ,CAAC,MAAM,UAAU;AACrC,UAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,KAAK;AAClC,QAAI,KAAK,SAAS,QAAQ;AACxB,YAAM,OAAO,UAAU,IAAI,KAAK;AAChC,UAAI,MAAM,SAAS,YAAY,KAAK,gBAAiB,MAAK,gBAAAhB,KAAC,KAAK,iBAAL,EAAgC,GAAG,KAAK,QAAd,GAAoB,CAAE;AAAA,eAClG,MAAM,SAAS,OAAQ,MAAK,gBAAAA,KAAC,KAAK,WAAL,EAAyB,UAAU,KAAK,UAAU,WAAW,SAAzC,GAAgD,CAAE;AAC5G;AAAA,IACF;AACA,UAAM,aAAaiB,cAAa,IAAI,IAAI,iBAAiB,IAAI,IAAI;AACjE,UAAM,YAAYA,cAAa,IAAI,KAAK,WAAW,UAAUC,aAAY,IAAI,MAAM,WAAW,OAAO,WAAW,WAAW,OAAO,SAAS,IAAI,IAAI;AACnJ,QAAI,KAAK,SAAS,aAAa;AAC7B,UAAI,WAAW,qBAAqB,MAAO;AAC3C,YAAM,UAAU,OAAO,gBAAgB;AACvC,YAAM,YAAY,KAAK,UAAU;AACjC,YAAM,aAAa,YAAY,OAAQ,UAAU,OAAO,KAAK;AAE7D,YAAM,QAAQ,YAAY,SAAY,cAAc,OAAO;AAE3D,UAAI,CAAC,aAAa,KAAK,KAAK,KAAK,EAAE,WAAW,MAAM,UAAU,UAAa,CAAC,QAAS;AACrF;AAAA,QACE;AAAA,QACA,EAAE,MAAM,YAAY,YAAY,GAAI,UAAU,UAAa,EAAE,MAAM,EAAG;AAAA,QACtE,gBAAAlB;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW,KAAK;AAAA,YAChB;AAAA,YACA,QAAQ,QAAQ,QAAQ,IAAI,KAAK;AAAA,YACjC,MAAM,KAAK;AAAA,YACX;AAAA,YACA;AAAA,YACC,GAAI,UAAU,UAAa,EAAE,MAAM;AAAA;AAAA,UAP/B;AAAA,QAQP;AAAA,MACF;AAAA,IACF,WAAW,KAAK,SAAS,QAAQ;AAC/B,YAAM,YAAY,KAAK,UAAU;AAEjC,YAAM,WAAW,cAAc,eAAe,KAAK,MAAM,EAAE,aAAa,UAAU,CAAC,IAAI,KAAK;AAC5F,YAAM,QAAQ,SAAS,KAAK,EAAE,WAAW;AACzC,UAAI,YAAY,OAAO;AACrB;AAAA,UACE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cAEC,WAAW,KAAK;AAAA,cAChB;AAAA,cACA;AAAA,cACA,WAAW,QAAQ;AAAA,cACnB,YAAY;AAAA,cACX,GAAI,YAAY,EAAE,SAAS;AAAA,cAC3B,GAAI,SAAS,EAAE,MAAM;AAAA;AAAA,YAPjB;AAAA,UAQP;AAAA,UACA;AAAA,QACF;AACA,YAAI,SAAU,gBAAe,mBAAmB,QAAQ;AAAA,MAC1D,OAAO;AACL,aAAK,gBAAAA,KAAC,KAAK,WAAL,EAAyB,UAAoB,aAAzB,GAA+C,GAAI,KAAK;AAAA,MACpF;AAAA,IACF,WAAW,KAAK,SAAS,kBAAkB;AAEzC,YAAM,QAAQ,eAAe,IAAI;AACjC,YAAM,OAAO,SAAS,WAAW,YAAY,MAAM,IAAI;AACvD,UAAI,SAAS,KAAM,MAAK,gBAAAA,KAAC,QAAe,OAAc,WAAW,QAAQ,MAAtC,GAA0C,CAAE;AAAA,IACjF,WAAW,cAAc,KAAK,iBAAiB;AAC7C,WAAK,gBAAAA,KAAC,KAAK,iBAAL,EAAgC,GAAG,cAAT,GAAqB,CAAE;AAAA,IACzD,WAAW,WAAW;AAEpB,UAAI,UAAU,UAAU,UAAW;AACnC;AAAA,QACE,KAAK,aACH,gBAAAA,KAAC,KAAK,YAAL,EAA2B,GAAG,UAAU,SAAnB,GAA0B,IAEhD,gBAAAA,KAAC,KAAK,WAAL,EAAyB,UAAU,kBAAkB,UAAU,OAAO,MAAM,GAAG,WAAW,SAAtE,GAA6E;AAAA,MAEtG;AAAA,IACF,WAAWiB,cAAa,IAAI,GAAG;AAE7B,UAAIC,aAAY,IAAI,MAAM,gBAAiB;AAE3C,UAAI,KAAK,iBAAiB,aAAa,IAAI,GAAG;AAC5C,cAAM,SAAS,uBAAuB,iBAAiB,MAAM,EAAE,YAAY,mBAAmB,iBAAiB,aAAa,CAAC,SAAS,cAAc,MAAM,MAAM,EAAE,CAAC,GAAG,UAAU;AAChL,cAAMC,OAAM,gBAAAnB,KAAC,kBAAyB,WAAW,KAAK,eAAe,UAAoB,QAAQ,QAAQ,QAAQ,IAAI,KAAK,GAAI,GAAG,UAAhG,GAAwG;AAEzI,YAAI,OAAO,UAAU,UAAU,YAAa,MAAKmB,IAAG;AAAA,YAC/C,MAAK,OAAO,aAAa,MAAM,QAAQ,MAAM,GAAGA,IAAG;AACxD;AAAA,MACF;AACA,YAAM,OAAO,aAAa,MAAM,EAAE,cAAc,eAAe,YAAY,CAAC;AAC5E,UAAI,SAAS,SAAU;AACvB,UAAI,MAAM;AACR,cAAM,UAAU,eAAe,OAAO,KAAK,aAAa,oBAAoB,eAAe,MAAM,IAAI;AACrG,cAAM,SAAS,WAAW,UAAU,KAAK,IAAI;AAC7C;AAAA,UACE,KAAK,SACH,gBAAAnB;AAAA,YAAC;AAAA;AAAA,cAEC,QAAQ,KAAK;AAAA,cACZ,GAAI,UAAU,EAAE,OAAO;AAAA,cACxB;AAAA,cACA;AAAA,cACA,SAAS,oBAAoB,KAAK;AAAA,cACjC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,cACxC,UAAU;AAAA;AAAA,YAPL;AAAA,UAQP;AAAA;AAAA,YAGA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAW,KAAK;AAAA,gBAChB;AAAA,gBACA,QAAQ,QAAQ,QAAQ,IAAI,KAAK;AAAA,gBACjC,UAAU,KAAK;AAAA,gBACf,OAAO,cAAc,IAAI;AAAA,gBACzB,OAAO,gBAAgB,KAAK,OAAO,MAAM;AAAA,gBACxC,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM;AAAA;AAAA,cAPhD;AAAA,YAQP;AAAA;AAAA,QAEJ;AACA;AAAA,MACF;AACA,YAAM,MACJ,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,KAAK;AAAA,UAChB;AAAA,UACA,QAAQ,QAAQ,QAAQ,IAAI,KAAK;AAAA,UACjC,UAAUkB,aAAY,IAAI;AAAA,UAC1B,OAAO,cAAc,IAAI;AAAA,UACxB,GAAI,SAAS,IAAI,KAAK,EAAE,OAAO,KAAK,MAAM;AAAA,UAC1C,GAAG,uBAAuB,cAAc,MAAM,EAAE,YAAY,mBAAmB,iBAAiB,OAAO,CAAC,GAAG,UAAU;AAAA,UACrH,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM;AAAA,UACpD,GAAI,KAAK,UAAU,sBAAsB,EAAE,QAAQ,KAAK,OAAO;AAAA,UAC/D,GAAI,KAAK,UAAU,kBAAkB,EAAE,WAAW,KAAK,UAAU;AAAA,UACjE,GAAI,UAAU,kBAAkB,SAAS,KAAK,UAAU,KAAK,EAAE,cAAc,KAAK;AAAA;AAAA,QAX9E;AAAA,MAYP;AAGF,UAAI,KAAK,UAAU,qBAAsB,MAAK,GAAG;AAAA,UAC5C,MAAK,OAAO,WAAW,IAAI,GAAG,GAAG;AAAA,IACxC;AAAA,EACF,CAAC;AACD,QAAM,QAAQ,SACV,aAAa,SAAS,EAAE,WAAW,WAAW,aAAa,SAAS,WAAW,aAAa,QAAQ,OAAO,UAAU,UAAU,OAAO,KAAK,aAAa,EAAE,SAAS,EAAE,CAAC,EAAE;AAAA,IAAI,CAAC,SAC3K,KAAK,SAAS,UAAU,KAAK,OAAO,gBAAAlB,KAAC,iBAAuC,MAAY,UAAoB,QAApD,GAAG,KAAK,GAAG,OAAqD;AAAA,EAC1H,IACA,QAAQ,IAAI,CAAC,UAAW,MAAM,SAAS,UAAU,MAAM,OAAO,IAAK;AAGvE,QAAM,OAAO,YAAY,SAAS,EAAE,YAAY,CAAC;AACjD,QAAM,cAAc,WAAW,cAAc,SAAY,mBAAmB,SAAS,EAAE,SAAS,QAAQ,MAAM,QAAQ,MAAM,GAAG,CAAC,YAAY;AAAA,IAC1I,MAAM,kBAAkB,SAAS,OAAO,EAAE,KAAK;AAAA,IAC/C,OAAO,MAAM,cAAc,QAAQ,SAAS,IAAI;AAAA,EAClD,EAAE;AAEF,QAAM,YAAY,WAAW,UAAU,gBAAgB,QAAQ,QAAQ,UAAU,KAAK,IAAI;AAC1F,QAAM,SACJ,WAAW,UACT,gBAAAA,KAAC,KAAK,aAAL,EAAiB,MAAM,QAAQ,UAAU,OAAO,QAAQ,SAAU,GAAI,cAAc,UAAa,EAAE,MAAM,UAAU,GAAK,GAAI,WAAW,EAAE,QAAQ,GAAI,IACpJ;AACN,QAAM,iBAAiB,WAAW,YAAY,KAAK,CAAC,aAAa,SAAS,OAAO,QAAQ,UAAU,UAAU;AAC7G,QAAM,cAAc,gBAAgB,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,QAAQ,UAAU,OAAO;AACjG,SACE,gBAAAA,KAAC,yBAAsB,IAAI,QAAQ,IAAI,SAAS,WAAW,aACzD;AAAA,IAAC,KAAK;AAAA,IAAL;AAAA,MACE,GAAI,WAAW,iBAAiB,QAAQ,UAAU,WAAW,QAAQ,UAAU,cAAc,EAAE,cAAc,EAAE,SAAS,QAAQ,SAAS,SAAS,YAAY,QAAQ,SAAS,YAAY,GAAI,kBAAkB,EAAE,cAAc,eAAe,KAAK,GAAI,GAAI,eAAe,EAAE,WAAW,YAAY,KAAK,GAAI,GAAI,QAAQ,SAAS,mBAAmB,UAAa,EAAE,WAAW,QAAQ,SAAS,eAAe,EAAG,EAAE;AAAA,MACtZ;AAAA,MACA,OAAO,CAAC,WAAW,WAAW,eAAe,WAAW,uBAAuB,KAAK,KAAK,EAAE,WAAW;AAAA,MACtG,QAAQ,MAAM,KAAK,UAAU,WAAW,UAAU,IAAI;AAAA,MACrD,GAAI,UAAU,EAAE,OAAO;AAAA,MACvB,GAAI,WAAW,CAAC,UAAU,EAAE,QAAQ;AAAA,MACpC,GAAI,kBAAkB,EAAE,eAAe;AAAA,MACvC,GAAI,gBAAgB,EAAE,aAAa;AAAA,MACnC,GAAI,UAAU,EAAE,OAAO;AAAA,MACvB,GAAI,aAAa,QAAQ,KAAK,EAAE,QAAQ,KAAK;AAAA,MAC7C,GAAI,QAAQ,UAAU,eAAe,EAAE,aAAa,KAAK;AAAA,MACzD,GAAI,QAAQ,UAAU,YAAY,EAAE,kBAAkB,EAAE,YAAY,QAAQ,SAAS,SAAS,WAAW,EAAE;AAAA,MAC3G,GAAI,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,iBAAiB,KAAK,EAAE,aAAa,KAAK;AAAA,MACzF,GAAI,eAAe,EAAE,SAAS,YAAY;AAAA,MAE1C;AAAA;AAAA,QACA;AAAA;AAAA;AAAA,EACH,GACF;AAEJ,CAAC;AAGD,SAAS,mBACP,SACA,QACA,SACiC;AACjC,QAAM,SAAS,WAAW,CAAC,GACxB,OAAO,CAAC,WAAW,OAAO,UAAU,MAAM,KAAK,IAAI,EACnD,IAAI,CAAC,YAAY,EAAE,IAAI,OAAO,IAAI,OAAO,OAAO,OAAO,GAAI,OAAO,SAAS,UAAa,EAAE,MAAM,OAAO,KAAK,GAAI,GAAG,QAAQ,MAAM,EAAE,EAAE;AACxI,SAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;AAkBA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,aAAa,kBAAkB,EAAE,UAAU,WAAW,QAAQ,OAAO,QAAQ,SAAS,OAAO,YAAY,KAAK,CAAC;AACrH,QAAM,YAAqC,aAAa,EAAE,WAAW,IAAI,CAAC;AAC1E,SAAO,WACL,gBAAAA,KAAC,oBAAiB,WAAsB,UAAoB,WAAsB,WAAsB,YAAwB,OAAO,UAAU,OAAO,WAAW,IAEnK,gBAAAA,KAAC,aAAU,UAAoB,WAAuB,GAAG,WAAW;AAExE;AAGA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,eAAe,iBAAiB,EAAE,UAAU,WAAW,WAAW,YAAY,GAAG,MAAM,CAAC;AAC9F,SAAO,gBAAAA,KAAC,aAAU,UAAoB,WAAuB,GAAG,OAAQ,GAAG,cAAc;AAC3F;AAGA,SAAS,oBAAoB,EAAE,MAAM,KAAK,GAA+C,QAAoC;AAC3H,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,SAAS,mBAAoB,QAAO,OAAO,QAAQ;AACvD,MAAI,SAAS,cAAc,SAAS,iBAAkB,QAAO,OAAO,QAAQ;AAC5E,MAAI,SAAS,gBAAiB,QAAO,OAAO,QAAQ;AACpD,SAAO,SAAS,eAAe,OAAO,QAAQ,eAAe,cAAc,SAAS,YAAY,eAAe,MAAM,MAAM;AAC7H;AAGA,SAAS,gBAAgB,OAAgB,QAAoC;AAC3E,QAAM,SAAS,OAAO,UAAU,YAAY,UAAU,OAAQ,QAAoD,CAAC;AACnH,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAC3H,SAAO,SAAS,KAAK,EAAE,SAAS,IAAI,GAAG,QAAQ,WAAW,OAAO,QAAQ,QAAQ,KAAK,OAAO,QAAQ;AACvG;AAGA,SAAS,eAAmH;AAAA,EAC1H,WAAAoB;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiH;AAC/G,QAAM,CAAC,MAAM,OAAO,IAAI,YAAY,UAAU,MAAM;AACpD,QAAM,OAAOA;AACb,SAAO,gBAAApB,KAAC,QAAM,GAAG,OAAO,MAAY,cAAc,SAAS;AAC7D;AAGA,SAAS,WAAW,MAAgD;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,UAAUkB,aAAY,IAAI;AAAA,MAC1B,OAAO,cAAc,IAAI;AAAA,MACzB,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM;AAAA,MACpD,GAAI,KAAK,UAAU,sBAAsB,EAAE,QAAQ,KAAK,OAAO;AAAA,IACjE;AAAA,IACA,GAAI,SAAS,IAAI,KAAK,EAAE,OAAO,KAAK,MAAM;AAAA,EAC5C;AACF;AAGA,SAAS,aAAa,MAAsC,QAA0D,QAAsC;AAC1J,QAAM,OAAO,OAAO,SAAS,OAAO;AACpC,QAAM,QAAuB,OAAO,WAAW,WAAW,UAAU,OAAO,WAAW,aAAa,OAAO,WAAW,sBAAsB,YAAY;AACvJ,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,EAAE,UAAUA,aAAY,IAAI,GAAG,OAAO,GAAI,KAAK,UAAU,UAAa,EAAE,OAAO,KAAK,MAAM,GAAI,GAAI,KAAK,UAAU,sBAAsB,EAAE,QAAQ,KAAK,OAAO,EAAG;AAAA,IACtK,UAAU,EAAE,SAAS,OAAO,SAAS,cAAc,IAAI,GAAG,MAAM,OAAO,SAAS,WAAW,IAAI,EAAE;AAAA,EACnG;AACF;AAGA,SAAS,cAAc,MAAqD;AAC1E,UAAQ,KAAK,OAAO;AAAA,IAClB,KAAK;AACH,aAAO,KAAK,cAAc,YAAY;AAAA,IACxC,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,IAAM,WAAW,CAAC,SAChB,OAAQ,KAA6B,UAAU,YAAa,KAA2B,MAAM,SAAS;AAExG,SAAS,cAAc,MAA2B,QAA4B;AAC5E,MAAI,SAAS,iBAAkB,QAAO,OAAO;AAC7C,MAAI,SAAS,iBAAkB,QAAO,OAAO;AAC7C,SAAO,SAAS,SAAS,OAAO,eAAe,OAAO;AACxD;AAGA,SAAS,uBAAgG,MAAY,YAA2B;AAC9I,MAAI,CAAC,cAAc,KAAK,UAAU,UAAU,YAAa,QAAO;AAChE,QAAM,EAAE,UAAU,WAAW,gBAAgB,SAAS,GAAG,KAAK,IAAI;AAClE,SAAO;AACT;AAGA,SAAS,cACP,MACA,UAMiC;AACjC,QAAM,WAAY,KAAoG;AACtH,MAAI,CAAC,SAAU,QAAO,CAAC;AACvB,MAAI,KAAK,UAAU,sBAAsB;AACvC,WAAO;AAAA,MACL,UAAU;AAAA,QACR,OAAO;AAAA,QACP,UAAU,CAAC,aAAa,SAAS,WAAW,SAAS,IAAI,QAAQ;AAAA,QACjE,SAAS,SAAS,sBAAsB,SAAS;AAAA,QACjD,QAAQ,SAAS,iBAAiB,OAAO,SAAS;AAAA,QAClD,GAAI,SAAS,iBAAiB,OAAO,SAAS,MAAM,EAAE,aAAa,SAAS,gBAAgB,QAAQ,cAAc,SAAS,gBAAgB,MAAM,SAAS,MAAM,EAAE;AAAA,QAClK,WAAW,SAAS,kBAAkB;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,UAAU,mBAAmB,SAAS,aAAa,MAAO,QAAO,EAAE,UAAU,EAAE,OAAO,SAAS,EAAE;AAC1G,SAAO,EAAE,UAAU,EAAE,OAAO,SAAS,WAAW,iBAAiB,WAAW,UAAU,EAAE;AAC1F;AAGA,SAAS,YAAe,MAAkC;AACxD,QAAM,OAAOZ,SAAO,IAAI;AACxB,MAAI,KAAK,QAAQ,WAAW,KAAK,UAAU,KAAK,QAAQ,KAAK,CAAC,MAAM,UAAU,SAAS,KAAK,KAAK,CAAC,EAAG,MAAK,UAAU;AACpH,SAAO,KAAK;AACd;;;AkC/3CA,SAAS,eAAAe,eAAa,aAAAC,aAAW,UAAAC,UAAQ,YAAAC,kBAAgB;;;ACClD,IAAM,qBAAqB,CAAC,QAAgB,0BAA0B,GAAG;AAGzE,SAAS,mBAAmB,KAA4B;AAC7D,MAAI;AACF,WAAO,aAAa,QAAQ,GAAG;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aAAa,KAAa,QAAsB;AAC9D,MAAI;AACF,iBAAa,QAAQ,KAAK,MAAM;AAAA,EAClC,QAAQ;AAAA,EAER;AACF;;;ACIO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YACW,QACT,QACA;AACA,UAAM,6BAA6B,MAAM,WAAW,MAAM,EAAE;AAHnD;AAIT,SAAK,OAAO;AAAA,EACd;AAAA,EALW;AAMb;AAGO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,YAAqB,SAAqB;AACxC,UAAM,QAAQ,OAAO;AADF;AAEnB,SAAK,OAAO;AAAA,EACd;AAAA,EAHqB;AAIvB;AAEA,IAAM,UAAU,CAAC,KAAa,WAAmB,GAAG,IAAI,QAAQ,QAAQ,EAAE,CAAC,IAAI,mBAAmB,MAAM,CAAC;AAEzG,eAAe,QAAQ,WAAkB,QAAgB,KAAa,MAAuC;AAC3G,QAAM,WAAW,MAAM,UAAU,KAAK,IAAI;AAC1C,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,qBAAqB,SAAS,QAAQ,MAAM;AACxE,SAAO;AACT;AAEA,IAAM,WAAW,CAAC,UAAgC;AAAA,EAChD,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAC9C,MAAM,KAAK,UAAU,IAAI;AAC3B;AAEA,eAAsB,cAAc,KAAa,WAA4C;AAC3F,QAAM,WAAW,MAAM,QAAQ,WAAW,QAAQ,IAAI,QAAQ,QAAQ,EAAE,CAAC;AACzE,UAAS,MAAM,SAAS,KAAK,GAAkC;AACjE;AAGA,eAAsB,mBAAmB,KAAa,WAAmC;AACvF,QAAM,WAAW,MAAM,UAAU,IAAI,QAAQ,QAAQ,EAAE,GAAG,EAAE,QAAQ,OAAO,CAAC;AAC5E,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAU,MAAM,aAAa,QAAQ;AAC3C,QAAI,SAAS,SAAS,wBAAyB,OAAM,IAAI,yBAAyB,OAAO;AACzF,UAAM,IAAI,qBAAqB,SAAS,QAAQ,QAAQ;AAAA,EAC1D;AACA,UAAS,MAAM,SAAS,KAAK,GAAsB;AACrD;AAGA,eAAsB,mBAAmB,KAAa,WAAkB,QAAgB,OAAgC;AACtH,QAAM,WAAW,MAAM,QAAQ,WAAW,UAAU,QAAQ,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;AACrH,UAAS,MAAM,SAAS,KAAK,GAAyB;AACxD;AAGA,eAAsB,mBAAmB,KAAa,WAAkB,QAA+B;AACrG,QAAM,WAAW,MAAM,UAAU,QAAQ,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,CAAC;AAC3E,MAAI,CAAC,SAAS,MAAM,SAAS,WAAW,IAAK,OAAM,IAAI,qBAAqB,SAAS,QAAQ,QAAQ;AACvG;;;AFlBA,IAAM,cAAc,CAAC,SAAwB,KAAK,UAAU,QAAQ,KAAK,cAAc,KAAK,aAAa,KAAK,eAAe;AAG7H,IAAM,iBAAiB;AAAA,EACrB,QAAQ;AAAA,EACR,OAAO,CAAC;AAAA,EACR,cAAc;AAAA,EACd,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AACZ;AAEA,IAAM,WAAW,CAAC,UAAkB,MAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG;AAG7D,SAAS,YAAY,EAAE,KAAK,eAAe,OAAO,aAAa,aAAa,gBAAgB,GAAsC;AACvI,QAAM,YAAY,eAAe,mBAAmB,GAAG;AACvD,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAAyB,SAAS;AAC9D,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAA0B,CAAC,CAAC;AACtD,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAwB,IAAI;AACpE,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAqC,IAAI;AACnE,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,WAA4B,IAAI;AAC9E,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAyC,IAAI;AACvF,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAG9C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,SAAS;AAClD,QAAM,aAAa,aAAa;AAChC,MAAI,YAAY;AACd,gBAAY,SAAS;AACrB,cAAU,SAAS;AACnB,aAAS,CAAC,CAAC;AACX,oBAAgB,IAAI;AACpB,aAAS,IAAI;AACb,uBAAmB,IAAI;AACvB,gBAAY,KAAK;AACjB,qBAAiB,IAAI;AACrB,gBAAY,KAAK;AAAA,EACnB;AAEA,QAAM,SAASC,SAAO,SAAS;AAC/B,SAAO,UAAU;AAEjB,QAAM,WAAWA,SAAO,WAAW;AACnC,WAAS,UAAU;AACnB,QAAM,aAAaA,SAAO,eAAe;AACzC,aAAW,UAAU;AACrB,QAAM,OAAgCC,cAAY,CAAC,OAAO,UAAU,SAAS,WAAW,WAAW,OAAO,OAAO,IAAI,GAAG,CAAC,CAAC;AAC1H,QAAM,WAAWD,SAAO,KAAK;AAC7B,WAAS,UAAU,aAAa,CAAC,IAAI;AACrC,QAAM,YAAYA,SAAO,YAAY;AACrC,YAAU,UAAU,aAAa,OAAO;AACxC,QAAM,aAAaA,SAAO,aAAa;AAEvC,QAAM,cAAcA,SAAO,CAAC;AAE5B,QAAM,cAAcA,SAAO,CAAC;AAE5B,QAAM,YAAYA,SAAsB,IAAI;AAC5C,QAAM,QAAQA,SAAO,IAAI;AACzB,EAAAE,YAAU,MAAM;AACd,UAAM,UAAU;AAChB,WAAO,MAAM;AACX,YAAM,UAAU;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,WAAWD,cAAY,CAAC,QAAgB,MAAM,WAAW,OAAO,YAAY,KAAK,CAAC,CAAC;AAGzF,QAAM,aAAaA;AAAA,IACjB,CAAC,UAAmB;AAClB,YAAM,UAAU,iBAAiB,2BAA2B,MAAM,UAAU;AAC5E,yBAAmB,OAAO;AAC1B,eAAS,QAAQ;AACjB,UAAI,QAAS,YAAW,UAAU,OAAO;AAAA,IAC3C;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,WAAWA;AAAA,IACf,CAAC,WAAmB;AAClB,gBAAU,UAAU;AACpB,sBAAgB,MAAM;AACtB,mBAAa,WAAW,MAAM;AAAA,IAChC;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAGA,QAAM,gBAAgBA,cAAY,YAAY;AAC5C,UAAM,KAAK,MAAM,mBAAmB,KAAK,IAAI;AAC7C,QAAI,CAAC,SAAS,SAAS,EAAG,QAAO;AACjC,UAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,aAAS,CAAC,YAAY,CAAC,EAAE,IAAI,OAAO,MAAM,WAAW,KAAK,WAAW,IAAI,GAAG,GAAG,QAAQ,OAAO,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC,CAAC;AACxH,aAAS,EAAE;AACX,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,KAAK,WAAW,MAAM,QAAQ,CAAC;AAE7C,QAAM,OAAOA,cAAY,YAAY;AACnC,QAAI,UAAU,YAAY,WAAW;AAEnC,UAAI,UAAU,YAAY,KAAM,YAAW,UAAU;AACrD,gBAAU,UAAU;AAAA,IACtB;AACA,UAAME,WAAU,EAAE,YAAY;AAC9B,gBAAY,WAAW;AACvB,cAAU,SAAS;AACnB,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,KAAK,IAAI;AAC5C,UAAI,CAAC,MAAM,WAAWA,aAAY,YAAY,QAAS;AACvD,eAAS,MAAM;AACf,gBAAU,OAAO;AACjB,UAAI,UAAU,YAAY,KAAM;AAChC,YAAM,aAAa,mBAAmB,SAAS;AAC/C,YAAM,SACJ,WAAW,YACV,eAAe,QAAQ,OAAO,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU,IAAI,aAAa,OAAO,CAAC,GAAG;AAClG,UAAI,WAAW,OAAW,UAAS,MAAM;AAAA;AAEvC,YAAI;AACF,gBAAM,cAAc;AAAA,QACtB,SAAS,OAAO;AAEd,cAAI,EAAE,iBAAiB,0BAA2B,OAAM;AACxD,cAAI,SAAS,SAAS,KAAKA,aAAY,YAAY,QAAS,YAAW,KAAK;AAAA,QAC9E;AAAA,IACJ,QAAQ;AACN,UAAI,CAAC,MAAM,WAAWA,aAAY,YAAY,QAAS;AACvD,gBAAU,OAAO;AAEjB,UAAI,UAAU,YAAY,QAAQ,WAAW,YAAY,OAAW,UAAS,WAAW,OAAO;AAAA,IACjG;AAAA,EACF,GAAG,CAAC,UAAU,KAAK,eAAe,YAAY,WAAW,MAAM,QAAQ,CAAC;AAExE,EAAAD,YAAU,MAAM;AACd,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAUD,cAAY,YAAY;AACtC,UAAME,WAAU,EAAE,YAAY;AAC9B,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,KAAK,IAAI;AAC5C,UAAI,SAAS,SAAS,KAAKA,aAAY,YAAY,QAAS,UAAS,MAAM;AAAA,IAC7E,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,KAAK,WAAW,MAAM,QAAQ,CAAC;AAEnC,QAAM,SAASF,cAAY,YAAY;AACrC,aAAS,IAAI;AACb,gBAAY,IAAI;AAChB,QAAI;AAGF,YAAM,QAAQ,MAAM,cAAc,KAAK,IAAI,EAAE,MAAM,MAAM,IAAI;AAC7D,UAAI,CAAC,SAAS,SAAS,EAAG,QAAO;AACjC,UAAI,MAAO,UAAS,KAAK;AACzB,YAAM,SAAS,SAAS,SAAS,SAAS,KAAK,WAAW;AAC1D,UAAI,CAAC,MAAO,QAAO,MAAM,cAAc;AACvC,eAAS,MAAM,EAAE;AACjB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,SAAS,SAAS,EAAG,YAAW,KAAK;AACzC,aAAO;AAAA,IACT,UAAE;AACA,UAAI,SAAS,SAAS,EAAG,aAAY,KAAK;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,UAAU,KAAK,eAAe,YAAY,WAAW,MAAM,QAAQ,CAAC;AAExE,QAAM,SAASA;AAAA,IACb,CAAC,WAAmB;AAClB,eAAS,IAAI;AACb,eAAS,MAAM;AAAA,IACjB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,QAAgB,UAAkB;AACvC,YAAM,aAAa,SAAS,KAAK;AACjC,YAAM,UAAU,SAAS,QAAQ,KAAK,CAAC,SAAS,KAAK,OAAO,MAAM;AAClE,UAAI,CAAC,WAAW,eAAe,MAAM,eAAe,QAAQ,MAAO;AACnE,eAAS,IAAI;AACb,YAAM,WAAW,CAAC,UAChB,SAAS,CAAC,SAAS,KAAK,IAAI,CAAC,SAAU,KAAK,OAAO,SAAS,EAAE,GAAG,MAAM,OAAO,MAAM,IAAI,IAAK,CAAC;AAChG,eAAS,UAAU;AACnB,UAAI;AACF,cAAM,QAAQ,MAAM,mBAAmB,KAAK,MAAM,QAAQ,UAAU;AACpE,YAAI,SAAS,SAAS,EAAG,UAAS,KAAK;AAAA,MACzC,QAAQ;AACN,YAAI,CAAC,SAAS,SAAS,EAAG;AAC1B,iBAAS,QAAQ,KAAK;AACtB,iBAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,KAAK,WAAW,MAAM,QAAQ;AAAA,EACjC;AAEA,QAAM,gBAAgBA,cAAY,CAAC,WAAmB;AACpD,UAAM,OAAO,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,OAAO,MAAM;AACjE,QAAI,KAAM,kBAAiB,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,EAC/D,GAAG,CAAC,CAAC;AAEL,QAAM,eAAeA,cAAY,MAAM,iBAAiB,IAAI,GAAG,CAAC,CAAC;AAEjE,QAAM,gBAAgBA,cAAY,YAAY;AAC5C,QAAI,CAAC,cAAe;AACpB,UAAM,EAAE,GAAG,IAAI;AACf,aAAS,IAAI;AACb,gBAAY,IAAI;AAChB,QAAI;AACF,YAAM,mBAAmB,KAAK,MAAM,EAAE;AAAA,IACxC,QAAQ;AACN,UAAI,SAAS,SAAS,GAAG;AACvB,iBAAS,QAAQ;AACjB,yBAAiB,IAAI;AACrB,oBAAY,KAAK;AAAA,MACnB;AACA;AAAA,IACF;AACA,QAAI,CAAC,SAAS,SAAS,EAAG;AAC1B,UAAM,SAAS,SAAS;AACxB,UAAM,QAAQ,OAAO,UAAU,CAAC,SAAS,KAAK,OAAO,EAAE;AACvD,UAAM,OAAO,OAAO,OAAO,CAAC,SAAS,KAAK,OAAO,EAAE;AACnD,aAAS,IAAI;AACb,qBAAiB,IAAI;AACrB,gBAAY,KAAK;AACjB,QAAI,UAAU,YAAY,GAAI;AAE9B,UAAM,OAAO,KAAK,KAAK,KAAK,KAAK,QAAQ,CAAC;AAC1C,QAAI,MAAM;AACR,eAAS,KAAK,EAAE;AAChB;AAAA,IACF;AACA,cAAU,UAAU;AACpB,oBAAgB,IAAI;AACpB,QAAI;AACF,YAAM,cAAc;AAAA,IACtB,SAAS,OAAO;AACd,UAAI,SAAS,SAAS,EAAG,YAAW,KAAK;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,UAAU,KAAK,eAAe,eAAe,YAAY,WAAW,MAAM,QAAQ,CAAC;AAEvF,QAAM,SAASA,cAAY,MAAM,KAAK,KAAK,GAAG,CAAC,IAAI,CAAC;AAEpD,SAAO;AAAA,IACL,GAAI,aAAa,iBAAiB,EAAE,QAAQ,OAAO,cAAc,OAAO,iBAAiB,UAAU,eAAe,SAAS;AAAA,IAC3H;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AGjUA,SAAS,eAAAG,qBAAmB;AAerB,SAAS,gBAAgB,MAAqB,UAAyC;AAC5F,QAAM,SAAS,cAAc;AAC7B,QAAM,EAAE,QAAQ,QAAQ,WAAW,IAAI;AACvC,QAAM,WAAWC;AAAA,IACf,CAAC,WAAmB;AAClB,aAAO,MAAM;AACb,eAAS;AAAA,IACX;AAAA,IACA,CAAC,UAAU,MAAM;AAAA,EACnB;AACA,QAAM,WAAWA,cAAY,MAAM;AACjC,SAAK,WAAW,EAAE,KAAK,CAAC,WAAW,UAAU,SAAS,CAAC;AAAA,EACzD,GAAG,CAAC,YAAY,QAAQ,CAAC;AAEzB,QAAM,gBAAgB,KAAK,iBAAiB;AAAA,IAC1C,OAAO,KAAK,cAAc,SAAS,OAAO;AAAA,IAC1C,SAAS,KAAK;AAAA,IACd,WAAW,MAAM,KAAK,KAAK,cAAc;AAAA,IACzC,UAAU,KAAK;AAAA,EACjB;AACA,QAAM,QACJ,KAAK,WAAW,UACZ,OAAO,qBACP,KAAK,UAAU,YAAY,KAAK,kBAC9B,KAAK,gBAAgB,UACrB,KAAK,QACH,gBAAgB,QAAQ,KAAK,KAAK,IAClC;AACV,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb,OAAO,KAAK,MAAM,IAAI,CAAC,SAAS;AAC9B,YAAM,SAAS,aAAa,KAAK,MAAM;AACvC,YAAM,SAAS,UAAU,KAAK,MAAM;AACpC,aAAO,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK,cAAc,GAAI,UAAU,EAAE,OAAO,GAAI,GAAI,UAAU,EAAE,OAAO,EAAG;AAAA,IACvI,CAAC;AAAA,IACD;AAAA,IACA;AAAA,IACA,UAAU,KAAK;AAAA,IACf,UAAU,CAAC,QAAQ,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK;AAAA,IAC3D,iBAAiB,KAAK;AAAA,IACtB,GAAI,iBAAiB,EAAE,cAAc;AAAA,IACrC,GAAI,SAAS,EAAE,MAAM;AAAA,IACrB,GAAI,KAAK,WAAW,WAAW,EAAE,SAAS,KAAK,OAAO;AAAA,EACxD;AACF;AAGO,SAAS,gBAAgB,MAAqD,QAA4B;AAC/G,SAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,KAAK,YAAY,GAAG,SAAS,OAAO;AACnF;AAEA,SAAS,gBAAgB,QAAoB,OAAoC;AAC/E,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,EAClB;AACF;AAEA,IAAM,eAAoC,oBAAI,IAAoB,CAAC,YAAY,MAAM,KAAK,CAAC;AAE3F,IAAM,eAAe,CAAC,WACpB,WAAW,UAAa,aAAa,IAAI,MAAM,IAAK,SAA4B;;;AChFlF,SAAS,aAAAC,aAAW,UAAAC,gBAAc;AAIlC,IAAM,gBAAgB;AAOf,SAAS,qBAAqB,MAAiE,MAAe,UAAU,MAAY;AACzI,QAAM,EAAE,OAAO,cAAc,QAAQ,IAAI;AAEzC,QAAM,eAAeA,SAAuB,IAAI;AAChD,EAAAD,YAAU,MAAM;AACd,UAAM,SAAS,aAAa;AAC5B,iBAAa,UAAU;AACvB,QAAI,WAAW,WAAW,QAAQ,WAAW,KAAM,MAAK,QAAQ;AAAA,EAClE,GAAG,CAAC,MAAM,SAAS,OAAO,CAAC;AAE3B,QAAM,aAAa,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,YAAY;AAChE,QAAM,kBAAkB,WAAW,QAAQ,eAAe,UAAa,WAAW,UAAU;AAC5F,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,gBAAiB;AACtB,UAAM,QAAQ,YAAY,MAAM,KAAK,QAAQ,GAAG,aAAa;AAC7D,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,SAAS,eAAe,CAAC;AAC/B;","names":["i","finished","createContext","useContext","useCallback","useEffect","useMemo","useRef","useState","finished","window","pause","useCallback","useRef","isToolPart","part","useRef","useCallback","useMemo","useRef","useCallback","useEffect","useRef","useState","chatUrl","chatUrl","useState","useCallback","history","useRef","request","useEffect","useMemo","edit","refusal","messages","voiceMode","voiceSounds","outgoing","pause","handOffToCorrections","finished","chatId","loadHistory","applyServerHistory","startSeries","pollCompactions","closed","text","publishedState","published","useCallback","useEffect","useMemo","useRef","useState","useRef","request","useCallback","useState","useEffect","menu","useMemo","createContext","useContext","useEffect","useState","jsx","createContext","useState","useContext","useEffect","getToolName","isToolUIPart","memo","useCallback","useEffect","useId","useMemo","useRef","useState","useCallback","useMemo","useSyncExternalStore","listeners","useState","useState","useCallback","useEffect","useMemo","useRef","useState","jsx","scroller","useCallback","useLayoutEffect","useMemo","useRef","useState","useState","useRef","useLayoutEffect","useCallback","message","useMemo","useCallback","useEffect","useMemo","useState","useMemo","useState","useEffect","useCallback","useMemo","useSyncExternalStore","subscribe","useCallback","useMemo","useState","memo","useEffect","useMemo","useRef","useState","jsx","useState","useEffect","memo","LiveDemoFrame","useMemo","useRef","jsx","useMemo","useState","useCallback","useRef","jsx","useCallback","useEffect","useRef","useState","useState","useRef","request","useEffect","useCallback","getToolName","noOpen","useCallback","useEffect","useMemo","useRef","useState","getToolName","fields","getToolName","fields","edge","memo","useCallback","useMemo","useState","jsx","memo","WidgetPart","useMemo","useState","useCallback","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","getToolName","isToolUIPart","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useCallback","useMemo","useEffect","parts","isToolUIPart","getToolName","useCallback","useMemo","useRef","useState","useEffect","useRef","useRef","useEffect","finished","jsx","Fragment","jsx","useId","useState","useMemo","id","name","useRef","useEffect","unavailable","useCallback","notices","notice","memo","CompactionDividerRow","UserMessage","escapeLinkText","AgentAnswer","isToolUIPart","getToolName","row","Component","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","request","useCallback","useCallback","useEffect","useRef"]}