@wgalleti/primevue-components 0.22.2 → 0.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/charts/WChart.vue.d.ts +12 -19
- package/dist/components/crud/WCrudColumnRenderer.vue.d.ts +2 -1
- package/dist/components/crud/WCrudFormDialog.vue.d.ts +56 -5
- package/dist/components/crud/WCrudSubview.vue.d.ts +30 -6
- package/dist/components/crud/WCrudView.vue.d.ts +89 -95
- package/dist/components/crud/WEditableTable.vue.d.ts +29 -27
- package/dist/components/form/WAutoCompleteFK.vue.d.ts +3 -2
- package/dist/components/form/WDatePicker.vue.d.ts +4 -9
- package/dist/components/form/WDateRange.vue.d.ts +2 -1
- package/dist/components/form/WFileUpload.vue.d.ts +8 -16
- package/dist/components/form/WFormRenderer.vue.d.ts +57 -5
- package/dist/components/form/WImageCropper.vue.d.ts +12 -20
- package/dist/components/form/WMarkdownEditor.vue.d.ts +3 -4
- package/dist/components/form/WMoneyInput.vue.d.ts +2 -1
- package/dist/components/form/WTransferList.vue.d.ts +3 -2
- package/dist/components/form/WTreeSelect.vue.d.ts +19 -21
- package/dist/components/ui/WActionBar.vue.d.ts +14 -15
- package/dist/components/ui/WCheckList.vue.d.ts +8 -12
- package/dist/components/ui/WDetailHeader.vue.d.ts +7 -11
- package/dist/components/ui/WDialog.vue.d.ts +7 -9
- package/dist/components/ui/WEmptyState.vue.d.ts +3 -2
- package/dist/components/ui/WFormSection.vue.d.ts +9 -12
- package/dist/components/ui/WInfoCard.vue.d.ts +3 -2
- package/dist/components/ui/WKanbanBoard.vue.d.ts +23 -46
- package/dist/components/ui/WKpiCard.vue.d.ts +15 -15
- package/dist/components/ui/WKpiGrid.vue.d.ts +12 -16
- package/dist/components/ui/WMapSelect.vue.d.ts +33 -42
- package/dist/components/ui/WMarkdownToc.vue.d.ts +2 -1
- package/dist/components/ui/WMarkdownView.vue.d.ts +3 -4
- package/dist/components/ui/WPageHeader.vue.d.ts +8 -12
- package/dist/components/ui/WProgressFlow.vue.d.ts +12 -16
- package/dist/components/ui/WSectionHeader.vue.d.ts +11 -13
- package/dist/components/ui/WStatusTag.vue.d.ts +2 -1
- package/dist/components/ui/WStepFlow.vue.d.ts +8 -16
- package/dist/components/ui/WStepSection.vue.d.ts +21 -20
- package/dist/components/ui/WTabBar.vue.d.ts +3 -2
- package/dist/components/ui/WTabNav.vue.d.ts +13 -30
- package/dist/components/ui/WTabViewport.vue.d.ts +2 -1
- package/dist/components--YULeZoE.cjs +25 -0
- package/dist/components--YULeZoE.cjs.map +1 -0
- package/dist/components-CYjLRDxm.js +6785 -0
- package/dist/components-CYjLRDxm.js.map +1 -0
- package/dist/components.cjs +1 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +2 -42
- package/dist/composables-B-LVf35N.js +680 -0
- package/dist/composables-B-LVf35N.js.map +1 -0
- package/dist/composables-BqRASiBc.cjs +2 -0
- package/dist/composables-BqRASiBc.cjs.map +1 -0
- package/dist/composables.cjs +1 -2
- package/dist/composables.d.ts +1 -1
- package/dist/composables.js +3 -20
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -234
- package/dist/index.js.map +1 -1
- package/dist/labels-CXBhCk2N.js +20 -0
- package/dist/labels-CXBhCk2N.js.map +1 -0
- package/dist/labels-DiFDhFhQ.cjs +2 -0
- package/dist/labels-DiFDhFhQ.cjs.map +1 -0
- package/dist/primevue-components.css +2 -1
- package/dist/routeTabs-BnVpnSeJ.cjs +2 -0
- package/dist/routeTabs-BnVpnSeJ.cjs.map +1 -0
- package/dist/routeTabs-D6m8PuP6.js +10 -0
- package/dist/routeTabs-D6m8PuP6.js.map +1 -0
- package/dist/types.cjs +1 -2
- package/dist/types.d.ts +1 -1
- package/dist/types.js +3 -11
- package/dist/useFormKeyboardNav-Brgrjxu-.js +550 -0
- package/dist/useFormKeyboardNav-Brgrjxu-.js.map +1 -0
- package/dist/useFormKeyboardNav-HoSmQwuv.cjs +3 -0
- package/dist/useFormKeyboardNav-HoSmQwuv.cjs.map +1 -0
- package/package.json +19 -18
- package/dist/WKanbanBoard.vue_vue_type_script_setup_true_lang-ByBa9LKO.cjs +0 -28
- package/dist/WKanbanBoard.vue_vue_type_script_setup_true_lang-ByBa9LKO.cjs.map +0 -1
- package/dist/WKanbanBoard.vue_vue_type_script_setup_true_lang-rd2SGufX.js +0 -6873
- package/dist/WKanbanBoard.vue_vue_type_script_setup_true_lang-rd2SGufX.js.map +0 -1
- package/dist/components.cjs.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/composables.cjs.map +0 -1
- package/dist/composables.js.map +0 -1
- package/dist/labels-B28P8JNB.js +0 -19
- package/dist/labels-B28P8JNB.js.map +0 -1
- package/dist/labels-pNskxXcl.cjs +0 -2
- package/dist/labels-pNskxXcl.cjs.map +0 -1
- package/dist/plugin-CIBvOgwG.js +0 -14
- package/dist/plugin-CIBvOgwG.js.map +0 -1
- package/dist/plugin-D2IZHyBm.cjs +0 -2
- package/dist/plugin-D2IZHyBm.cjs.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/useFormKeyboardNav-BgBAe0C7.cjs +0 -3
- package/dist/useFormKeyboardNav-BgBAe0C7.cjs.map +0 -1
- package/dist/useFormKeyboardNav-BrKZrHq9.js +0 -463
- package/dist/useFormKeyboardNav-BrKZrHq9.js.map +0 -1
- package/dist/useRouteTabs-CSo1j_ji.js +0 -762
- package/dist/useRouteTabs-CSo1j_ji.js.map +0 -1
- package/dist/useRouteTabs-VGx93R83.cjs +0 -2
- package/dist/useRouteTabs-VGx93R83.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components--YULeZoE.cjs","names":["n","$attrs","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$slots","$attrs","$slots","$slots"],"sources":["../src/components/crud/WCrudColumnRenderer.vue","../src/components/crud/WCrudColumnRenderer.vue","../node_modules/maska/dist/maska.mjs","../node_modules/maska/dist/vue.mjs","../src/utils/fieldMapper.ts","../src/components/form/WAutoCompleteFK.vue","../src/components/form/WAutoCompleteFK.vue","../src/components/form/WDatePicker.vue","../src/components/form/WDatePicker.vue","../src/components/form/WMoneyInput.vue","../src/components/form/WMoneyInput.vue","../src/components/form/WImageCropper.vue","../src/components/form/WImageCropper.vue","../src/components/form/WTransferList.vue","../src/components/form/WTransferList.vue","../src/utils/cep.ts","../src/components/form/WFormRenderer.vue","../src/components/form/WFormRenderer.vue","../src/components/crud/WCrudFormDialog.vue","../src/components/crud/WCrudFormDialog.vue","../src/utils/csv.ts","../src/components/crud/WCrudView.vue","../src/components/crud/WCrudView.vue","../src/components/crud/WEditableTable.vue","../src/components/crud/WEditableTable.vue","../src/components/crud/WCrudSubview.vue","../src/components/crud/WCrudSubview.vue","../src/components/form/WDateRange.vue","../src/components/form/WDateRange.vue","../src/components/form/WFileUpload.vue","../src/components/form/WFileUpload.vue","../src/utils/treeSelect.ts","../src/components/form/WTreeSelect.vue","../src/components/form/WTreeSelect.vue","../src/utils/chart.ts","../src/components/charts/WChart.vue","../src/components/charts/WChart.vue","../src/components/ui/WStatusTag.vue","../src/components/ui/WStatusTag.vue","../src/components/ui/WPageHeader.vue","../src/components/ui/WPageHeader.vue","../src/components/ui/WEmptyState.vue","../src/components/ui/WEmptyState.vue","../src/components/ui/WDetailHeader.vue","../src/components/ui/WDetailHeader.vue","../src/components/ui/WInfoCard.vue","../src/components/ui/WInfoCard.vue","../src/components/ui/WKpiCard.vue","../src/components/ui/WKpiCard.vue","../src/components/ui/WKpiGrid.vue","../src/components/ui/WKpiGrid.vue","../src/components/ui/WSectionHeader.vue","../src/components/ui/WSectionHeader.vue","../src/components/ui/WFormSection.vue","../src/components/ui/WFormSection.vue","../src/components/ui/WActionBar.vue","../src/components/ui/WActionBar.vue","../src/components/ui/WProgressFlow.vue","../src/components/ui/WProgressFlow.vue","../src/utils/stepFlow.ts","../src/components/ui/WStepFlow.vue","../src/components/ui/WStepFlow.vue","../src/components/ui/WStepSection.vue","../src/components/ui/WStepSection.vue","../src/components/ui/WCheckList.vue","../src/components/ui/WCheckList.vue","../src/components/ui/WMapSelect.vue","../src/components/ui/WMapSelect.vue","../src/utils/markdown.ts","../src/utils/markdownEnhance.ts","../src/components/ui/WMarkdownView.vue","../src/components/ui/WMarkdownView.vue","../src/components/form/WMarkdownEditor.vue","../src/components/form/WMarkdownEditor.vue","../src/components/ui/WMarkdownToc.vue","../src/components/ui/WMarkdownToc.vue","../src/components/ui/WDialog.vue","../src/components/ui/WDialog.vue","../src/components/ui/WTabBar.vue","../src/components/ui/WTabBar.vue","../src/components/ui/WTabNav.vue","../src/components/ui/WTabNav.vue","../src/components/ui/WTabPane.ts","../src/components/ui/WTabViewport.vue","../src/components/ui/WTabViewport.vue","../src/components/ui/WKanbanBoard.vue","../src/components/ui/WKanbanBoard.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport type { ColumnDef } from '@/types/column'\nimport { useFormatters } from '@/composables/useFormatters'\n\ndefineProps<{\n column: ColumnDef\n value: unknown\n rowData: Record<string, unknown>\n}>()\n\nconst { formatDate, formatDateTime, formatCurrency, formatNumber } = useFormatters()\n</script>\n\n<template>\n <span v-if=\"value == null\" class=\"text-muted-color text-xs\">—</span>\n\n <template v-else-if=\"column.type === 'image'\">\n <img\n :src=\"String(value)\"\n :alt=\"column.header\"\n class=\"size-9 rounded-lg object-cover ring-1 ring-surface-200 dark:ring-surface-700\"\n />\n </template>\n\n <template v-else-if=\"column.type === 'boolean'\">\n <Tag\n :value=\"column.tagValue ? column.tagValue(value, rowData) : value ? 'Ativo' : 'Inativo'\"\n :severity=\"\n (column.tagSeverity\n ? column.tagSeverity(value, rowData)\n : value\n ? 'success'\n : 'danger') as any\n \"\n class=\"text-xs\"\n />\n </template>\n\n <span v-else-if=\"column.type === 'date'\" class=\"text-muted-color tabular-nums text-[0.8125rem]\">\n {{ formatDate(value as string) }}\n </span>\n\n <span\n v-else-if=\"column.type === 'datetime'\"\n class=\"text-muted-color tabular-nums text-[0.8125rem]\"\n >\n {{ formatDateTime(value as string) }}\n </span>\n\n <span v-else-if=\"column.type === 'currency'\" class=\"font-semibold tabular-nums text-[0.8125rem]\">\n {{ formatCurrency(value as number) }}\n </span>\n\n <span v-else-if=\"column.type === 'number'\" class=\"font-semibold tabular-nums text-[0.8125rem]\">\n {{\n column.format\n ? column.format(value, rowData)\n : formatNumber(value as number, column.decimals ?? 0)\n }}\n </span>\n\n <span v-else class=\"text-[0.8125rem]\">\n {{ column.format ? column.format(value, rowData) : value }}\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport type { ColumnDef } from '@/types/column'\nimport { useFormatters } from '@/composables/useFormatters'\n\ndefineProps<{\n column: ColumnDef\n value: unknown\n rowData: Record<string, unknown>\n}>()\n\nconst { formatDate, formatDateTime, formatCurrency, formatNumber } = useFormatters()\n</script>\n\n<template>\n <span v-if=\"value == null\" class=\"text-muted-color text-xs\">—</span>\n\n <template v-else-if=\"column.type === 'image'\">\n <img\n :src=\"String(value)\"\n :alt=\"column.header\"\n class=\"size-9 rounded-lg object-cover ring-1 ring-surface-200 dark:ring-surface-700\"\n />\n </template>\n\n <template v-else-if=\"column.type === 'boolean'\">\n <Tag\n :value=\"column.tagValue ? column.tagValue(value, rowData) : value ? 'Ativo' : 'Inativo'\"\n :severity=\"\n (column.tagSeverity\n ? column.tagSeverity(value, rowData)\n : value\n ? 'success'\n : 'danger') as any\n \"\n class=\"text-xs\"\n />\n </template>\n\n <span v-else-if=\"column.type === 'date'\" class=\"text-muted-color tabular-nums text-[0.8125rem]\">\n {{ formatDate(value as string) }}\n </span>\n\n <span\n v-else-if=\"column.type === 'datetime'\"\n class=\"text-muted-color tabular-nums text-[0.8125rem]\"\n >\n {{ formatDateTime(value as string) }}\n </span>\n\n <span v-else-if=\"column.type === 'currency'\" class=\"font-semibold tabular-nums text-[0.8125rem]\">\n {{ formatCurrency(value as number) }}\n </span>\n\n <span v-else-if=\"column.type === 'number'\" class=\"font-semibold tabular-nums text-[0.8125rem]\">\n {{\n column.format\n ? column.format(value, rowData)\n : formatNumber(value as number, column.decimals ?? 0)\n }}\n </span>\n\n <span v-else class=\"text-[0.8125rem]\">\n {{ column.format ? column.format(value, rowData) : value }}\n </span>\n</template>\n","var S = Object.defineProperty;\nvar x = (n, t, e) => t in n ? S(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;\nvar A = (n, t, e) => x(n, typeof t != \"symbol\" ? t + \"\" : t, e);\nconst R = {\n \"#\": { pattern: /[0-9]/ },\n \"@\": { pattern: /[a-zA-Z]/ },\n \"*\": { pattern: /[a-zA-Z0-9]/ }\n}, N = (n, t, e) => n.replaceAll(t, \"\").replace(e, \".\").replace(\"..\", \".\").replace(/[^.\\d]/g, \"\"), w = (n, t, e) => {\n var s;\n return new Intl.NumberFormat(((s = e.number) == null ? void 0 : s.locale) ?? \"en\", {\n minimumFractionDigits: n,\n maximumFractionDigits: t,\n roundingMode: \"trunc\"\n });\n}, P = (n, t = !0, e) => {\n var k, g, b, d;\n const s = ((k = e.number) == null ? void 0 : k.unsigned) !== !0 && n.startsWith(\"-\") ? \"-\" : \"\", r = ((g = e.number) == null ? void 0 : g.fraction) ?? 0;\n let a = w(0, r, e);\n const u = a.formatToParts(1000.12), p = ((b = u.find((o) => o.type === \"group\")) == null ? void 0 : b.value) ?? \" \", f = ((d = u.find((o) => o.type === \"decimal\")) == null ? void 0 : d.value) ?? \".\", i = N(n, p, f);\n if (Number.isNaN(parseFloat(i))) return s;\n const h = i.split(\".\");\n if (h[1] != null && h[1].length >= 1) {\n const o = h[1].length <= r ? h[1].length : r;\n a = w(o, r, e);\n }\n let l = a.format(parseFloat(i));\n return t ? r > 0 && i.endsWith(\".\") && !i.slice(0, -1).includes(\".\") && (l += f) : l = N(l, p, f), s + l;\n}, C = (n) => JSON.parse(n.replaceAll(\"'\", '\"')), F = (n, t = {}) => {\n const e = { ...t };\n n.dataset.maska != null && n.dataset.maska !== \"\" && (e.mask = T(n.dataset.maska)), n.dataset.maskaEager != null && (e.eager = E(n.dataset.maskaEager)), n.dataset.maskaReversed != null && (e.reversed = E(n.dataset.maskaReversed)), n.dataset.maskaTokensReplace != null && (e.tokensReplace = E(n.dataset.maskaTokensReplace)), n.dataset.maskaTokens != null && (e.tokens = W(n.dataset.maskaTokens));\n const s = {};\n return n.dataset.maskaNumberLocale != null && (s.locale = n.dataset.maskaNumberLocale), n.dataset.maskaNumberFraction != null && (s.fraction = parseInt(n.dataset.maskaNumberFraction)), n.dataset.maskaNumberUnsigned != null && (s.unsigned = E(n.dataset.maskaNumberUnsigned)), (n.dataset.maskaNumber != null || Object.values(s).length > 0) && (e.number = s), e;\n}, E = (n) => n !== \"\" ? !!JSON.parse(n) : !0, T = (n) => n.startsWith(\"[\") && n.endsWith(\"]\") ? C(n) : n, W = (n) => {\n if (n.startsWith(\"{\") && n.endsWith(\"}\"))\n return C(n);\n const t = {};\n return n.split(\"|\").forEach((e) => {\n const s = e.split(\":\");\n t[s[0]] = {\n pattern: I() ? new RegExp(s[1], \"u\") : new RegExp(s[1]),\n optional: s[2] === \"optional\",\n multiple: s[2] === \"multiple\",\n repeated: s[2] === \"repeated\"\n };\n }), t;\n}, I = () => {\n try {\n return new RegExp(\"\\\\p{L}\", \"u\"), !0;\n } catch {\n return !1;\n }\n};\nclass O {\n constructor(t = {}) {\n A(this, \"opts\", {});\n A(this, \"memo\", /* @__PURE__ */ new Map());\n const e = { ...t };\n if (e.tokens != null) {\n e.tokens = e.tokensReplace ? { ...e.tokens } : { ...R, ...e.tokens };\n for (const s of Object.values(e.tokens))\n typeof s.pattern == \"string\" && (s.pattern = I() ? new RegExp(s.pattern, \"u\") : new RegExp(s.pattern));\n } else\n e.tokens = R;\n Array.isArray(e.mask) && (e.mask.length > 1 ? e.mask = [...e.mask].sort((s, r) => s.length - r.length) : e.mask = e.mask[0] ?? \"\"), e.mask === \"\" && (e.mask = null), this.opts = e;\n }\n masked(t) {\n return this.process(String(t), this.findMask(String(t)));\n }\n unmasked(t) {\n return this.process(String(t), this.findMask(String(t)), !1);\n }\n isEager() {\n return this.opts.eager === !0;\n }\n isReversed() {\n return this.opts.reversed === !0;\n }\n completed(t) {\n const e = this.findMask(String(t));\n if (this.opts.mask == null || e == null) return !1;\n const s = this.process(String(t), e).length;\n return typeof this.opts.mask == \"string\" ? s >= this.opts.mask.length : s >= e.length;\n }\n findMask(t) {\n const e = this.opts.mask;\n if (e == null)\n return null;\n if (typeof e == \"string\")\n return e;\n if (typeof e == \"function\")\n return e(t);\n const s = this.process(t, e.slice(-1).pop() ?? \"\", !1);\n return e.find((r) => this.process(t, r, !1).length >= s.length) ?? \"\";\n }\n escapeMask(t) {\n const e = [], s = [];\n return t.split(\"\").forEach((r, a) => {\n r === \"!\" && t[a - 1] !== \"!\" ? s.push(a - s.length) : e.push(r);\n }), { mask: e.join(\"\"), escaped: s };\n }\n process(t, e, s = !0) {\n if (this.opts.number != null) return P(t, s, this.opts);\n if (e == null) return t;\n const r = `v=${t},mr=${e},m=${s ? 1 : 0}`;\n if (this.memo.has(r)) return this.memo.get(r);\n const { mask: a, escaped: u } = this.escapeMask(e), p = [], f = this.opts.tokens != null ? this.opts.tokens : {}, i = this.isReversed() ? -1 : 1, h = this.isReversed() ? \"unshift\" : \"push\", l = this.isReversed() ? 0 : a.length - 1, k = this.isReversed() ? () => o > -1 && c > -1 : () => o < a.length && c < t.length, g = (v) => !this.isReversed() && v <= l || this.isReversed() && v >= l;\n let b, d = -1, o = this.isReversed() ? a.length - 1 : 0, c = this.isReversed() ? t.length - 1 : 0, M = !1;\n for (; k(); ) {\n const v = a.charAt(o), m = f[v], y = (m == null ? void 0 : m.transform) != null ? m.transform(t.charAt(c)) : t.charAt(c);\n if (!u.includes(o) && m != null ? (y.match(m.pattern) != null ? (p[h](y), m.repeated ? (d === -1 ? d = o : o === l && o !== d && (o = d - i), l === d && (o -= i)) : m.multiple && (M = !0, o -= i), o += i) : m.multiple ? M && (o += i, c -= i, M = !1) : y === b ? b = void 0 : m.optional && (o += i, c -= i), c += i) : (s && !this.isEager() && p[h](v), y === v && !this.isEager() ? c += i : b = v, this.isEager() || (o += i)), this.isEager())\n for (; g(o) && (f[a.charAt(o)] == null || u.includes(o)); ) {\n if (s) {\n if (p[h](a.charAt(o)), t.charAt(c) === a.charAt(o)) {\n o += i, c += i;\n continue;\n }\n } else a.charAt(o) === t.charAt(c) && (c += i);\n o += i;\n }\n }\n return this.memo.set(r, p.join(\"\")), this.memo.get(r);\n }\n}\nclass V {\n constructor(t, e = {}) {\n A(this, \"items\", /* @__PURE__ */ new Map());\n A(this, \"eventAbortController\");\n A(this, \"onInput\", (t) => {\n if (t instanceof CustomEvent && t.type === \"input\" && !t.isTrusted && !t.bubbles)\n return;\n const e = t.target, s = this.items.get(e);\n if (s === void 0) return;\n const r = \"inputType\" in t && t.inputType.startsWith(\"delete\"), a = s.isEager(), u = r && a && s.unmasked(e.value) === \"\" ? \"\" : e.value;\n this.fixCursor(e, r, () => this.setValue(e, u));\n });\n this.options = e, this.eventAbortController = new AbortController(), this.init(this.getInputs(t));\n }\n update(t = {}) {\n this.options = { ...t }, this.init(Array.from(this.items.keys()));\n }\n updateValue(t) {\n var e;\n t.value !== \"\" && t.value !== ((e = this.processInput(t)) == null ? void 0 : e.masked) && this.setValue(t, t.value);\n }\n destroy() {\n this.eventAbortController.abort(), this.items.clear();\n }\n init(t) {\n const e = this.getOptions(this.options);\n for (const s of t) {\n if (!this.items.has(s)) {\n const { signal: a } = this.eventAbortController;\n s.addEventListener(\"input\", this.onInput, { capture: !0, signal: a });\n }\n const r = new O(F(s, e));\n this.items.set(s, r), queueMicrotask(() => this.updateValue(s)), s.selectionStart === null && r.isEager() && console.warn(\"Maska: input of `%s` type is not supported\", s.type);\n }\n }\n getInputs(t) {\n return typeof t == \"string\" ? Array.from(document.querySelectorAll(t)) : \"length\" in t ? Array.from(t) : [t];\n }\n getOptions(t) {\n const { onMaska: e, preProcess: s, postProcess: r, ...a } = t;\n return a;\n }\n fixCursor(t, e, s) {\n var k, g;\n const r = t.selectionStart, a = t.value;\n if (s(), r === null || r === a.length && !e) return;\n const u = t.value, p = a.slice(0, r), f = u.slice(0, r), i = (k = this.processInput(t, p)) == null ? void 0 : k.unmasked, h = (g = this.processInput(t, f)) == null ? void 0 : g.unmasked;\n if (i === void 0 || h === void 0) return;\n let l = r;\n p !== f && (l += e ? u.length - a.length : i.length - h.length), t.setSelectionRange(l, l);\n }\n setValue(t, e) {\n const s = this.processInput(t, e);\n s !== void 0 && (t.value = s.masked, this.options.onMaska != null && (Array.isArray(this.options.onMaska) ? this.options.onMaska.forEach((r) => r(s)) : this.options.onMaska(s)), t.dispatchEvent(new CustomEvent(\"maska\", { detail: s })), t.dispatchEvent(new CustomEvent(\"input\", { detail: s.masked })));\n }\n processInput(t, e) {\n const s = this.items.get(t);\n if (s === void 0) return;\n let r = e ?? t.value;\n this.options.preProcess != null && (r = this.options.preProcess(r));\n let a = s.masked(r);\n return this.options.postProcess != null && (a = this.options.postProcess(a)), {\n masked: a,\n unmasked: s.unmasked(r),\n completed: s.completed(r)\n };\n }\n}\nexport {\n O as Mask,\n V as MaskInput,\n R as tokens\n};\n","import { MaskInput as n } from \"./maska.mjs\";\nconst l = /* @__PURE__ */ new WeakMap(), c = (e, s) => {\n if (e.arg == null || e.instance == null) return;\n const a = \"setup\" in e.instance.$.type;\n e.arg in e.instance ? e.instance[e.arg] = s : a && console.warn(\"Maska: please expose `%s` using defineExpose\", e.arg);\n}, k = (e, s) => {\n var u;\n const a = e instanceof HTMLInputElement ? e : e.querySelector(\"input\");\n if (a == null || (a == null ? void 0 : a.type) === \"file\") return;\n let t = {};\n if (s.value != null && (t = typeof s.value == \"string\" ? { mask: s.value } : { ...s.value }), s.arg != null) {\n const o = (r) => {\n const p = s.modifiers.unmasked ? r.unmasked : s.modifiers.completed ? r.completed : r.masked;\n c(s, p);\n };\n t.onMaska = t.onMaska == null ? o : Array.isArray(t.onMaska) ? [...t.onMaska, o] : [t.onMaska, o];\n }\n l.has(a) ? (u = l.get(a)) == null || u.update(t) : l.set(a, new n(a, t));\n};\nexport {\n k as vMaska\n};\n","import type { FieldDef, SelectOption } from '@/types/field'\nimport type { ColumnDef } from '@/types/column'\n\nexport interface ApiFieldMeta {\n name: string\n label: string\n type: string\n required?: boolean\n max_length?: number\n choices?: { value: string; label: string }[]\n read_only?: boolean\n endpoint?: string\n option_label?: string\n option_value?: string\n}\n\nconst TYPE_MAP: Record<string, FieldDef['type']> = {\n string: 'text',\n integer: 'number',\n decimal: 'number',\n float: 'number',\n boolean: 'switch',\n choice: 'select',\n fk: 'fk',\n date: 'date',\n datetime: 'datetime',\n email: 'email',\n url: 'text',\n slug: 'text',\n text: 'textarea',\n}\n\nexport function mapApiFieldToFieldDef(apiField: ApiFieldMeta): FieldDef {\n const fieldType = TYPE_MAP[apiField.type] ?? 'text'\n\n const def: FieldDef = {\n field: apiField.name,\n label: apiField.label,\n type: fieldType,\n required: apiField.required ?? false,\n }\n\n if (apiField.type === 'decimal' || apiField.type === 'float') {\n def.minFractionDigits = 2\n def.maxFractionDigits = 2\n }\n\n if (apiField.type === 'boolean') {\n def.defaultValue = false\n }\n\n if (apiField.type === 'choice' && apiField.choices?.length) {\n def.options = apiField.choices.map((c) => ({\n label: c.label,\n value: c.value,\n })) as SelectOption[]\n }\n\n if (apiField.type === 'fk') {\n def.endpoint = apiField.endpoint\n if (apiField.option_label) def.optionLabel = apiField.option_label\n if (apiField.option_value) def.optionValue = apiField.option_value\n }\n\n return def\n}\n\nexport function mapApiFieldsToFieldDefs(apiFields: ApiFieldMeta[]): FieldDef[] {\n return apiFields.filter((f) => !f.read_only && f.name !== 'id').map(mapApiFieldToFieldDef)\n}\n\nconst COLUMN_TYPE_MAP: Record<string, ColumnDef['type']> = {\n boolean: 'boolean',\n date: 'date',\n datetime: 'datetime',\n decimal: 'number',\n float: 'number',\n integer: 'number',\n}\n\nexport function mapApiFieldToColumnDef(apiField: ApiFieldMeta): ColumnDef {\n // Para FK, exibe o campo _nome (ex: marca → marca_nome)\n const field = apiField.type === 'fk' ? `${apiField.name}_nome` : apiField.name\n return {\n field,\n header: apiField.label,\n type: COLUMN_TYPE_MAP[apiField.type],\n sortable: true,\n }\n}\n\nexport function mapApiFieldsToColumnDefs(apiFields: ApiFieldMeta[], maxColumns = 6): ColumnDef[] {\n return apiFields\n .filter((f) => !f.read_only && f.name !== 'id')\n .slice(0, maxColumns)\n .map(mapApiFieldToColumnDef)\n}\n","<script setup lang=\"ts\">\nimport { ref, watch, computed, inject, reactive, nextTick, onMounted } from 'vue'\nimport AutoComplete from 'primevue/autocomplete'\nimport Chip from 'primevue/chip'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport DataTable from 'primevue/datatable'\nimport Column from 'primevue/column'\nimport Button from 'primevue/button'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport { W_DATA_PROVIDER_KEY } from '@/types/plugin'\nimport type { DataProvider } from '@/types/dataProvider'\nimport type { FieldDef, FieldSubRowsFetch } from '@/types/field'\nimport type { ColumnDef } from '@/types/column'\nimport type { ApiFieldMeta } from '@/utils/fieldMapper'\nimport { mapApiFieldsToFieldDefs, mapApiFieldsToColumnDefs } from '@/utils/fieldMapper'\nimport { useAppToast } from '@/composables/useAppToast'\nimport { useAppConfirm } from '@/composables/useAppConfirm'\nimport { extractApiError } from '@/composables/useApiError'\nimport WCrudFormDialog from '@/components/crud/WCrudFormDialog.vue'\nimport WCrudColumnRenderer from '@/components/crud/WCrudColumnRenderer.vue'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\ninterface ColumnMeta {\n field: string\n header: string\n}\n\n/** Filtro em cascata já resolvido (nome do parâmetro + valor atual). */\ninterface DrilldownFilter {\n field: string\n value: unknown\n required?: boolean\n}\n\n/** Valor aceito no `v-model`: id, objeto ou — em `multiple` — lista de ids/objetos. */\ntype FKValue = string | number | Record<string, unknown>\n\nconst props = withDefaults(\n defineProps<{\n modelValue: FKValue | FKValue[] | null\n endpoint: string\n endpointParams?: Record<string, string | number | boolean>\n /** Filtro(s) em cascata resolvido(s): aplicado(s) como parâmetro na busca. Com\n * `required` (default true), a busca só ocorre quando o valor estiver preenchido. */\n drilldown?: DrilldownFilter | DrilldownFilter[]\n /** Seleção múltipla: o campo vira chips e o `v-model` passa a ser uma lista\n * de objetos. O modal de pesquisa ganha caixas de marcação. */\n multiple?: boolean\n /** Em `multiple`, quantos chips mostrar antes de resumir o resto em `+N`\n * (0 = mostrar todos). Útil em campo estreito, tipo filtro de painel. */\n maxChips?: number\n optionLabel?: string\n /** Chave de uma segunda linha, em texto de apoio, dentro de cada sugestão — para\n * o dado que **decide** a escolha aparecer antes dela (ex.: saldo em estoque).\n * Vazia ou ausente na linha, a sugestão volta a ter uma linha só. */\n optionDescription?: string\n optionValue?: string\n placeholder?: string\n /** Placeholder exibido enquanto uma cascata obrigatória não estiver preenchida. */\n blockedPlaceholder?: string\n disabled?: boolean\n showClear?: boolean\n forceSelection?: boolean\n columns?: ColumnMeta[]\n minLength?: number\n dialogHeader?: string\n // CRUD no modal\n canCreate?: boolean\n canEdit?: boolean\n canDelete?: boolean\n crudFields?: FieldDef[]\n crudColumns?: ColumnDef[]\n /** Sub-linhas do grid do modal: recebe as linhas da página e devolve o mapa\n * `id → sub-linhas` + as colunas (dinâmicas) do mini-grid. Linha com entrada\n * no mapa abre expandida — ex.: lote (linha) com suas análises (sub-linhas). */\n subRows?: FieldSubRowsFetch\n dialogWidth?: string\n /** Foca o campo ao montar. Marca o input com o atributo nativo `autofocus`\n * para que o `focus()` do PrimeVue Dialog (em onAfterEnter) o encontre e\n * não roube o foco para o botão de fechar. */\n autofocus?: boolean\n }>(),\n {\n multiple: false,\n maxChips: 0,\n optionLabel: 'nome',\n optionValue: 'id',\n placeholder: 'Buscar...',\n disabled: false,\n showClear: true,\n forceSelection: true,\n minLength: 0,\n canCreate: undefined,\n canEdit: undefined,\n canDelete: undefined,\n dialogWidth: '480px',\n autofocus: false,\n },\n)\n\nconst emit = defineEmits<{\n /** Objeto selecionado — ou a lista de objetos quando `multiple`. */\n 'update:modelValue': [value: Record<string, unknown> | Record<string, unknown>[] | null]\n}>()\n\n// O template tem múltiplos nós raiz (o campo + os overlays Dialog/WCrudFormDialog).\n// Sem isto, `class`/`id`/`style` do consumidor não têm onde herdar e o Vue emite\n// warning de \"extraneous non-props attributes\". Aplicamos os attrs no nó visível.\ndefineOptions({ inheritAttrs: false })\n\nconst dataProvider = inject<DataProvider>(W_DATA_PROVIDER_KEY)\nif (!dataProvider) {\n throw new Error('[wPrimeVueComponents] dataProvider não encontrado. Registre o WPrimeVuePlugin.')\n}\nconst provider = dataProvider\n\nconst toast = useAppToast()\nconst { confirmDelete: confirmDeleteDialog } = useAppConfirm()\n\n// ---------------------------------------------------------------------------\n// Inline autocomplete\n// ---------------------------------------------------------------------------\n\nconst selectedItem = ref<Record<string, unknown> | null>(null)\n/** Seleção quando `multiple` — o campo mostra um chip por item. */\nconst selectedItems = ref<Record<string, unknown>[]>([])\nconst suggestions = ref<Record<string, unknown>[]>([])\nconst searching = ref(false)\n\n/** Valor entregue ao AutoComplete: objeto (single) ou lista (multiple). */\nconst acModel = computed(() => (props.multiple ? selectedItems.value : selectedItem.value))\n\n/** Identificador escalar de um valor do v-model (id cru ou objeto já resolvido). */\nfunction keyOf(v: unknown): unknown {\n if (v && typeof v === 'object') return (v as Record<string, unknown>)[props.optionValue]\n return v\n}\n\nfunction dedupeByKey(itens: Record<string, unknown>[]): Record<string, unknown>[] {\n const vistos = new Set<unknown>()\n const out: Record<string, unknown>[] = []\n for (const item of itens) {\n const k = keyOf(item)\n if (vistos.has(k)) continue\n vistos.add(k)\n out.push(item)\n }\n return out\n}\n\nfunction emitSelection() {\n emit('update:modelValue', props.multiple ? [...selectedItems.value] : selectedItem.value)\n}\n\nfunction labelOf(item: unknown): string {\n const v = (item as Record<string, unknown>)?.[props.optionLabel]\n return v == null ? '' : String(v)\n}\n\n/** Texto de apoio da sugestão. Vazio quando a linha não trouxe o campo. */\nfunction descriptionOf(item: unknown): string {\n if (!props.optionDescription) return ''\n const v = (item as Record<string, unknown>)?.[props.optionDescription]\n return v == null ? '' : String(v)\n}\n\n/** Quantos chips ficam escondidos atrás do `+N` (0 quando cabem todos). */\nconst chipsOcultos = computed(() =>\n props.maxChips > 0 ? Math.max(0, selectedItems.value.length - props.maxChips) : 0,\n)\n\n/** Nomes resumidos no `+N` — viram tooltip para não esconder informação. */\nconst chipsOcultosLabel = computed(() =>\n selectedItems.value.slice(props.maxChips).map(labelOf).join(', '),\n)\n\n/** O `x` do chip remove só aquele item — sem deixar o clique subir para o chip\n * (que abre a listagem). */\nfunction onChipRemove(event: Event, remove: (event: Event) => void) {\n event?.stopPropagation?.()\n remove(event)\n}\n\n/** Limpar tudo: em `multiple` a lista inteira; no simples, o valor. */\nfunction limparSelecao() {\n if (props.disabled) return\n if (props.multiple) {\n if (!selectedItems.value.length) return\n selectedItems.value = []\n } else {\n if (!selectedItem.value) return\n selectedItem.value = null\n }\n emitSelection()\n}\n\n/** Com `maxChips`, cada chip fica com uma fração da largura do campo — assim\n * eles dividem a linha (em vez de um por linha) e truncam o que não couber.\n * A reserva cobre o chip `+N` e a área de digitação; o piso de 7rem impede\n * chip ilegível em campo estreito (aí eles quebram para a linha de baixo). */\nconst chipStyle = computed(() =>\n props.multiple && props.maxChips > 0\n ? { '--w-fk-chip-max': `max(6rem, calc((100% - 6rem) / ${props.maxChips}))` }\n : undefined,\n)\n\n/** Mostra o \"limpar\" só quando há o que limpar (e o consumidor permite). */\nconst mostrarLimpar = computed(() => {\n if (!props.showClear || props.disabled) return false\n return props.multiple ? selectedItems.value.length > 0 : selectedItem.value !== null\n})\n\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n// Referência ao AutoComplete para aplicar o autofocus no input nativo.\nconst acRef = ref<{ $el?: HTMLElement } | null>(null)\n\nonMounted(() => {\n if (!props.autofocus) return\n nextTick(() => {\n const input = acRef.value?.$el?.querySelector?.('input') as HTMLInputElement | null\n if (!input) return\n // O atributo nativo faz o Dialog.focus() (onAfterEnter) mirar este input\n // em vez do botão fechar; o focus() cobre o caso fora de Dialog.\n input.setAttribute('autofocus', '')\n input.focus()\n input.select?.()\n })\n})\n\n// ---------------------------------------------------------------------------\n// Filtro em cascata (drill-down)\n// ---------------------------------------------------------------------------\n\nfunction isEmptyValue(v: unknown): boolean {\n return v === null || v === undefined || v === ''\n}\n\n/** Um campo FK guarda o objeto inteiro; extrai o identificador escalar para o filtro. */\nfunction normalizeDrillValue(v: unknown): unknown {\n if (v && typeof v === 'object') {\n const o = v as Record<string, unknown>\n return o.id ?? o.value ?? o\n }\n return v\n}\n\nconst drilldownList = computed<DrilldownFilter[]>(() => {\n const d = props.drilldown\n if (!d) return []\n return Array.isArray(d) ? d : [d]\n})\n\n/** true quando alguma cascata obrigatória ainda não tem valor → não busca. */\nconst blockedByRequired = computed(() =>\n drilldownList.value.some(\n (d) => (d.required ?? true) && isEmptyValue(normalizeDrillValue(d.value)),\n ),\n)\n\n/** Parâmetros de filtro derivados das cascatas com valor preenchido. */\nfunction drilldownParams(): Record<string, unknown> {\n const params: Record<string, unknown> = {}\n for (const d of drilldownList.value) {\n const val = normalizeDrillValue(d.value)\n if (!isEmptyValue(val)) params[d.field] = val\n }\n return params\n}\n\nconst effectivePlaceholder = computed(() =>\n blockedByRequired.value && props.blockedPlaceholder\n ? props.blockedPlaceholder\n : props.placeholder,\n)\n\n/** Em `multiple` o placeholder some quando já há chips — ele descreve o estado\n * \"sem filtro\"; ao lado de uma seleção viraria informação contraditória. */\nconst placeholderVisivel = computed(() =>\n props.multiple && selectedItems.value.length ? undefined : effectivePlaceholder.value,\n)\n\nasync function fetchOne(id: string | number): Promise<Record<string, unknown> | null> {\n try {\n const response = await provider.get(props.endpoint, id)\n return response.data\n } catch {\n return null\n }\n}\n\nasync function fetchById(id: string | number) {\n selectedItem.value = await fetchOne(id)\n}\n\n/** Resolve a lista do v-model: objeto já resolvido passa direto, id vira GET. */\nasync function resolveMany(valores: FKValue[]): Promise<Record<string, unknown>[]> {\n const itens = await Promise.all(\n valores.map(async (v) => {\n if (v && typeof v === 'object') {\n const obj = v as Record<string, unknown>\n if (props.optionLabel in obj) return obj\n const id = obj[props.optionValue]\n return id != null ? await fetchOne(id as string | number) : null\n }\n // Reaproveita o que já está resolvido — evita GET a cada mudança da lista.\n const atual = selectedItems.value.find((i) => i[props.optionValue] === v)\n return atual ?? (await fetchOne(v as string | number))\n }),\n )\n return itens.filter((i): i is Record<string, unknown> => i !== null)\n}\n\nasync function search(query: string) {\n // Cascata obrigatória sem valor → não busca (evita listar tudo).\n if (blockedByRequired.value) {\n suggestions.value = []\n return\n }\n searching.value = true\n try {\n const params: Record<string, unknown> = {\n page_size: 20,\n ...props.endpointParams,\n ...drilldownParams(),\n }\n if (query) params.search = query\n const response = await provider.list(props.endpoint, params)\n suggestions.value = response.data\n } catch {\n suggestions.value = []\n } finally {\n searching.value = false\n }\n}\n\nfunction onSearch(event: { query: string }) {\n const query = event.query || ''\n if (query.length < props.minLength) {\n suggestions.value = []\n return\n }\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => search(query), 300)\n}\n\nfunction onSelect(event: { value: Record<string, unknown> }) {\n // Em `multiple` o AutoComplete já mantém a lista — tratado em `onAcUpdate`.\n if (props.multiple) return\n selectedItem.value = event.value\n emit('update:modelValue', event.value)\n}\n\n/** Em `multiple`, o AutoComplete emite a lista inteira ao marcar/remover chip. */\nfunction onAcUpdate(value: unknown) {\n if (!props.multiple) return\n selectedItems.value = dedupeByKey(\n Array.isArray(value) ? (value as Record<string, unknown>[]) : [],\n )\n emitSelection()\n}\n\nfunction onClear() {\n // Em `multiple`, o AutoComplete emite `clear` também quando só descarta o texto\n // digitado (`forceSelection` sem correspondência) — limpar a lista aqui apagaria\n // a seleção inteira a cada busca sem match. O clear real chega como\n // `update:modelValue` (ícone de limpar), tratado em `onAcUpdate`.\n if (props.multiple) return\n selectedItem.value = null\n emitSelection()\n}\n\n// Resolve initial value\nwatch(\n () => props.modelValue,\n async (newVal) => {\n if (props.multiple) {\n const lista = newVal == null ? [] : Array.isArray(newVal) ? newVal : [newVal]\n const chaves = lista.map(keyOf)\n const atuais = selectedItems.value.map((i) => i[props.optionValue])\n // Mesma lista (na mesma ordem) → nada a resolver; evita loop com o emit.\n if (chaves.length === atuais.length && chaves.every((k, i) => k === atuais[i])) return\n selectedItems.value = await resolveMany(lista)\n return\n }\n if (newVal != null) {\n if (typeof newVal === 'object' && newVal !== null && props.optionLabel in newVal) {\n selectedItem.value = newVal as Record<string, unknown>\n return\n }\n if (!selectedItem.value || selectedItem.value[props.optionValue] !== newVal) {\n await fetchById(newVal as string | number)\n }\n } else {\n selectedItem.value = null\n }\n },\n { immediate: true },\n)\n\n// ---------------------------------------------------------------------------\n// Modal — State\n// ---------------------------------------------------------------------------\n\nconst modalVisible = ref(false)\nconst modalData = ref<Record<string, unknown>[]>([])\nconst modalLoading = ref(false)\nconst modalSearch = ref('')\nconst modalPage = ref(1)\nconst modalPageSize = ref(15)\nconst modalTotalRecords = ref(0)\nconst modalSelection = ref<Record<string, unknown> | Record<string, unknown>[] | null>(null)\n\n/** Itens marcados no modal, normalizados (single ou multiple). */\nconst modalSelecionados = computed<Record<string, unknown>[]>(() => {\n const sel = modalSelection.value\n if (!sel) return []\n return Array.isArray(sel) ? sel : [sel]\n})\nconst modalSortField = ref<string | null>(null)\nconst modalSortOrder = ref<1 | -1 | 0>(0)\nlet modalSearchTimer: ReturnType<typeof setTimeout> | null = null\n\n// --- Sub-linhas (expansão automática por linha com dados) ---\nconst subRowsMap = ref<Record<string, Record<string, unknown>[]>>({})\nconst subRowsColumns = ref<ColumnDef[]>([])\nconst expandedRows = ref<Record<string, boolean>>({})\n\n/** Busca as sub-linhas da página atual e abre as linhas que têm o que mostrar. */\nasync function fetchSubRows() {\n if (!props.subRows) return\n try {\n const { map, columns } = await props.subRows(modalData.value)\n subRowsMap.value = map || {}\n subRowsColumns.value = columns || []\n const abertos: Record<string, boolean> = {}\n for (const row of modalData.value) {\n const id = String(row[props.optionValue])\n if (subRowsMap.value[id]?.length) abertos[id] = true\n }\n expandedRows.value = abertos\n } catch {\n // Sub-linha é enriquecimento: falhou, o grid segue só com as linhas.\n subRowsMap.value = {}\n subRowsColumns.value = []\n expandedRows.value = {}\n }\n}\n\nfunction subRowsDe(row: Record<string, unknown>): Record<string, unknown>[] {\n return subRowsMap.value[String(row[props.optionValue])] || []\n}\n\n// Metadata from extras.fields\nconst apiFields = ref<ApiFieldMeta[]>([])\nconst crudAvailable = computed(() => {\n // Se o usuário forneceu crudFields, CRUD está habilitado\n if (props.crudFields?.length) return true\n // Se a API retornou extras.fields, CRUD está disponível\n return apiFields.value.length > 0\n})\n\n// Respeita props explícitas, senão auto-detecta\nconst showCreate = computed(() => props.canCreate ?? crudAvailable.value)\nconst showEdit = computed(() => props.canEdit ?? crudAvailable.value)\nconst showDelete = computed(() => props.canDelete ?? crudAvailable.value)\nconst hasRowActions = computed(() => showEdit.value || showDelete.value)\n\n// Campos do form — prioridade: props > auto-gerados do extras\nconst formFields = computed<FieldDef[]>(() => {\n if (props.crudFields?.length) return props.crudFields\n return mapApiFieldsToFieldDefs(apiFields.value)\n})\n\n// Colunas do modal — prioridade: crudColumns > props.columns > auto-geradas > fallback\nconst modalColumns = computed<ColumnDef[]>(() => {\n if (props.crudColumns?.length) return props.crudColumns\n if (props.columns?.length) {\n return props.columns.map((c) => ({\n field: c.field,\n header: c.header,\n sortable: true,\n }))\n }\n if (apiFields.value.length) {\n return mapApiFieldsToColumnDefs(apiFields.value)\n }\n return [{ field: props.optionLabel, header: props.optionLabel, sortable: true }]\n})\n\n// ---------------------------------------------------------------------------\n// Modal — Fetch\n// ---------------------------------------------------------------------------\n\nasync function fetchModalData() {\n // Cascata obrigatória sem valor → não lista (o modal mostra estado vazio).\n if (blockedByRequired.value) {\n modalData.value = []\n modalTotalRecords.value = 0\n return\n }\n modalLoading.value = true\n try {\n const params: Record<string, unknown> = {\n page: modalPage.value,\n page_size: modalPageSize.value,\n ...props.endpointParams,\n ...drilldownParams(),\n }\n if (modalSearch.value) params.search = modalSearch.value\n if (modalSortField.value && modalSortOrder.value !== 0) {\n params.ordering =\n modalSortOrder.value === -1 ? `-${modalSortField.value}` : modalSortField.value\n }\n const response = await provider.list(props.endpoint, params)\n modalData.value = response.data\n modalTotalRecords.value = response.rows\n void fetchSubRows()\n\n // Captura metadata de campos na primeira requisição\n if (response.extras?.fields && !props.columns?.length && !props.crudFields?.length) {\n apiFields.value = response.extras.fields as ApiFieldMeta[]\n }\n } catch {\n modalData.value = []\n modalTotalRecords.value = 0\n } finally {\n modalLoading.value = false\n }\n}\n\nfunction onInputKeydown(e: KeyboardEvent) {\n // F2 abre o modal de pesquisa (atalho estilo desktop).\n if (e.key === 'F2' && !props.disabled) {\n e.preventDefault()\n openModal()\n }\n}\n\nfunction openModal() {\n if (props.disabled) return\n modalSearch.value = ''\n modalPage.value = 1\n modalSortField.value = null\n modalSortOrder.value = 0\n // Em `multiple` o modal abre já com o que está selecionado no campo.\n modalSelection.value = props.multiple ? [...selectedItems.value] : null\n modalVisible.value = true\n fetchModalData()\n}\n\nfunction onModalPage(event: { page: number; rows: number }) {\n modalPage.value = event.page + 1\n modalPageSize.value = event.rows\n fetchModalData()\n}\n\nfunction onModalSort(event: { sortField?: string; sortOrder?: 1 | -1 | 0 }) {\n modalSortField.value = (event.sortField as string) ?? null\n modalSortOrder.value = event.sortOrder ?? 0\n modalPage.value = 1\n fetchModalData()\n}\n\nfunction confirmModalSelection() {\n if (props.multiple) {\n selectedItems.value = dedupeByKey(modalSelecionados.value)\n emitSelection()\n modalVisible.value = false\n return\n }\n const sel = modalSelecionados.value[0]\n if (!sel) return\n selectedItem.value = sel\n emit('update:modelValue', sel)\n modalVisible.value = false\n}\n\nfunction onRowDblClick(event: { data: Record<string, unknown> }) {\n // Em `multiple`, o duplo clique acrescenta à marcação e o modal continua\n // aberto — fechar a cada item tornaria a seleção de vários penosa.\n if (props.multiple) {\n modalSelection.value = dedupeByKey([...modalSelecionados.value, event.data])\n return\n }\n selectedItem.value = event.data\n emit('update:modelValue', event.data)\n modalVisible.value = false\n}\n\n// --- Modal: teclado (foco → pesquisa → grid) ---\nconst modalSearchInput = ref<{ $el?: HTMLElement } | null>(null)\nconst modalTableWrap = ref<HTMLElement | null>(null)\nlet lastSearched = ''\n\nfunction onModalShow() {\n lastSearched = ''\n nextTick(() => {\n const el = modalSearchInput.value?.$el as HTMLInputElement | undefined\n el?.focus?.()\n el?.select?.()\n })\n}\n\nfunction focusModalGrid() {\n nextTick(() => {\n const row = modalTableWrap.value?.querySelector<HTMLElement>('.p-datatable-tbody > tr')\n row?.focus()\n })\n}\n\nfunction onModalSearchKeydown(e: KeyboardEvent) {\n if (e.key !== 'Enter' || e.isComposing) return\n e.preventDefault()\n const q = modalSearch.value.trim()\n // Vazio ou já pesquisado → move o foco para o grid.\n if (q === '' || q === lastSearched) {\n focusModalGrid()\n return\n }\n // Texto novo → pesquisa imediata (cancela o debounce); o próximo Enter vai ao grid.\n if (modalSearchTimer) clearTimeout(modalSearchTimer)\n lastSearched = q\n modalPage.value = 1\n fetchModalData()\n}\n\nfunction onModalGridKeydown(e: KeyboardEvent) {\n // Enter no grid = confirmar a seleção (marcada com Espaço). Interceptado (capture)\n // antes do handler nativo do DataTable para não apenas alternar a marca.\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n // Em `multiple` o Enter confirma a marcação atual, inclusive vazia (= limpar).\n if (props.multiple || modalSelecionados.value.length) confirmModalSelection()\n }\n // Espaço e setas: tratados nativamente pelo DataTable (marca/navega).\n}\n\nwatch(modalSearch, () => {\n if (modalSearchTimer) clearTimeout(modalSearchTimer)\n modalSearchTimer = setTimeout(() => {\n modalPage.value = 1\n lastSearched = modalSearch.value.trim()\n fetchModalData()\n }, 300)\n})\n\n// Reage à mudança das cascatas: rebusca (se o modal estiver aberto) e limpa a\n// seleção que ficou obsoleta ao trocar a origem — sem apagar na carga inicial.\nwatch(\n () => drilldownList.value.map((d) => normalizeDrillValue(d.value)),\n (novos, antigos) => {\n if (modalVisible.value) {\n modalPage.value = 1\n fetchModalData()\n }\n if (!antigos) return\n const mudou = novos.some((v, i) => v !== antigos[i])\n const tinhaValorAntes = antigos.some((v) => !isEmptyValue(v))\n if (!mudou || !tinhaValorAntes) return\n if (props.multiple) {\n if (!selectedItems.value.length) return\n selectedItems.value = []\n emitSelection()\n return\n }\n if (selectedItem.value) {\n selectedItem.value = null\n emit('update:modelValue', null)\n }\n },\n)\n\n// ---------------------------------------------------------------------------\n// Modal — CRUD\n// ---------------------------------------------------------------------------\n\nconst formDialogVisible = ref(false)\nconst formSaving = ref(false)\nconst editingItem = ref<Record<string, unknown> | null>(null)\nconst formData = reactive<Record<string, unknown>>({})\n\nconst isEditing = computed(() => editingItem.value !== null)\nconst formDialogTitle = computed(() => (isEditing.value ? 'Editar Registro' : 'Novo Registro'))\n\nfunction getDefaults(): Record<string, unknown> {\n const defaults: Record<string, unknown> = {}\n for (const f of formFields.value) {\n defaults[f.field] =\n f.defaultValue !== undefined\n ? typeof f.defaultValue === 'function'\n ? (f.defaultValue as () => unknown)()\n : f.defaultValue\n : null\n }\n return defaults\n}\n\nfunction resetForm() {\n const defs = getDefaults()\n for (const key of Object.keys(formData)) {\n delete formData[key]\n }\n for (const [key, val] of Object.entries(defs)) {\n formData[key] = val\n }\n}\n\nfunction openCreateForm() {\n editingItem.value = null\n resetForm()\n formDialogVisible.value = true\n}\n\nfunction openEditForm(item: Record<string, unknown>) {\n editingItem.value = item\n for (const f of formFields.value) {\n formData[f.field] = item[f.field] !== undefined ? item[f.field] : null\n }\n formDialogVisible.value = true\n}\n\nfunction setFormField(field: string, value: unknown) {\n formData[field] = value\n}\n\nasync function saveForm() {\n formSaving.value = true\n try {\n const payload = { ...formData }\n\n // FK — extrair ID de objetos selecionados\n for (const f of formFields.value) {\n const val = payload[f.field]\n if (f.type === 'fk' && val !== null && typeof val === 'object') {\n const key = f.optionValue || 'id'\n payload[f.field] = (val as Record<string, unknown>)[key] ?? val\n }\n }\n\n let response\n\n if (isEditing.value && editingItem.value) {\n const pk = editingItem.value[props.optionValue]\n response = await provider.update(props.endpoint, pk as string | number, payload)\n // Atualiza na lista do modal\n const idx = modalData.value.findIndex((i) => i[props.optionValue] === pk)\n if (idx !== -1) {\n modalData.value[idx] = response.data\n }\n toast.success('Registro atualizado com sucesso')\n } else {\n // Cria herdando o(s) pai(s) da cascata (dependsOn) — ex.: um novo lote nasce\n // no produto selecionado. Sem semear o pai o backend rejeitaria o filho órfão.\n // Só completa chaves ausentes: um campo do form com o mesmo nome prevalece.\n const parent = drilldownParams()\n for (const [key, value] of Object.entries(parent)) {\n const current = payload[key]\n if (current === undefined || current === null || current === '') {\n payload[key] = value\n }\n }\n response = await provider.create(props.endpoint, payload)\n // Adiciona na lista e seleciona automaticamente\n modalData.value.unshift(response.data)\n modalTotalRecords.value++\n toast.success('Registro criado com sucesso')\n }\n\n formDialogVisible.value = false\n editingItem.value = null\n\n // Auto-seleciona o registro criado/editado (em `multiple`, acrescenta)\n modalSelection.value = props.multiple\n ? dedupeByKey([...modalSelecionados.value, response.data])\n : response.data\n } catch (err) {\n toast.error(extractApiError(err, 'Erro ao salvar registro'))\n } finally {\n formSaving.value = false\n }\n}\n\nfunction confirmDelete(item: Record<string, unknown>) {\n confirmDeleteDialog(async () => {\n try {\n const pk = item[props.optionValue]\n await provider.delete(props.endpoint, pk as string | number)\n const idx = modalData.value.findIndex((i) => i[props.optionValue] === pk)\n if (idx !== -1) {\n modalData.value.splice(idx, 1)\n modalTotalRecords.value--\n }\n // Se o item deletado estava selecionado, sai da seleção\n if (props.multiple) {\n if (selectedItems.value.some((i) => i[props.optionValue] === pk)) {\n selectedItems.value = selectedItems.value.filter((i) => i[props.optionValue] !== pk)\n emitSelection()\n }\n modalSelection.value = modalSelecionados.value.filter((i) => i[props.optionValue] !== pk)\n } else {\n if (selectedItem.value && selectedItem.value[props.optionValue] === pk) {\n selectedItem.value = null\n emit('update:modelValue', null)\n }\n if (modalSelecionados.value[0]?.[props.optionValue] === pk) {\n modalSelection.value = null\n }\n }\n toast.success('Registro excluído com sucesso')\n } catch (err) {\n toast.error(extractApiError(err, 'Erro ao excluir registro'))\n }\n })\n}\n</script>\n\n<template>\n <div\n class=\"w-autocompletefk\"\n :class=\"{ 'w-autocompletefk-has-clear': mostrarLimpar }\"\n :style=\"chipStyle\"\n v-bind=\"$attrs\"\n >\n <AutoComplete\n ref=\"acRef\"\n :model-value=\"acModel\"\n :suggestions=\"suggestions\"\n :multiple=\"multiple\"\n :option-label=\"optionLabel\"\n :placeholder=\"placeholderVisivel\"\n :disabled=\"disabled\"\n :force-selection=\"forceSelection\"\n :loading=\"searching\"\n :show-clear=\"false\"\n fluid\n @complete=\"onSearch\"\n @item-select=\"onSelect\"\n @update:model-value=\"onAcUpdate\"\n @clear=\"onClear\"\n @keydown=\"onInputKeydown\"\n >\n <!-- Sugestão em duas linhas quando `optionDescription` aponta para algo\n preenchido. Sem ela o slot não é declarado, e o AutoComplete volta ao\n render padrão de uma linha — nada muda para quem não usa. -->\n <template v-if=\"optionDescription\" #option=\"{ option }\">\n <span class=\"w-fk-option\">\n <span class=\"w-fk-option__label\">{{ labelOf(option) }}</span>\n <span v-if=\"descriptionOf(option)\" class=\"w-fk-option__desc\">\n {{ descriptionOf(option) }}\n </span>\n </span>\n </template>\n\n <!-- Chip clicado abre a listagem já marcada com a seleção atual. Com\n `maxChips`, os excedentes viram um chip `+N` (somem do campo, mas\n continuam no tooltip) — chip nenhum pode esticar a caixa. -->\n <template v-if=\"multiple\" #chip=\"{ value, index, removeCallback }\">\n <Chip\n v-if=\"!maxChips || index < maxChips\"\n v-tooltip.top=\"labelOf(value)\"\n class=\"p-autocomplete-chip w-autocompletefk-chip\"\n :label=\"labelOf(value)\"\n removable\n @click=\"openModal\"\n @remove=\"onChipRemove($event, removeCallback)\"\n />\n <Chip\n v-else-if=\"index === maxChips\"\n v-tooltip.top=\"chipsOcultosLabel\"\n class=\"p-autocomplete-chip w-autocompletefk-chip w-autocompletefk-chip-more\"\n :label=\"`+${chipsOcultos}`\"\n @click=\"openModal\"\n />\n <span v-else class=\"w-autocompletefk-chip-hidden\" />\n </template>\n </AutoComplete>\n <button\n v-if=\"mostrarLimpar\"\n v-tooltip.top=\"'Limpar'\"\n type=\"button\"\n data-kbd-skip\n tabindex=\"-1\"\n class=\"w-autocompletefk-clear\"\n @click=\"limparSelecao\"\n >\n <i class=\"pi pi-times\" />\n </button>\n <button\n v-tooltip.top=\"'Pesquisar (F2)'\"\n type=\"button\"\n :disabled=\"disabled\"\n data-kbd-skip\n tabindex=\"-1\"\n class=\"w-autocompletefk-trigger\"\n @click=\"openModal\"\n >\n <i class=\"pi pi-search\" />\n </button>\n </div>\n\n <!-- Modal de Pesquisa + CRUD -->\n <Dialog\n v-model:visible=\"modalVisible\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n :header=\"dialogHeader || 'Pesquisar'\"\n :style=\"{ width: '80vw' }\"\n modal\n :draggable=\"false\"\n class=\"w-autocompletefk-dialog\"\n @show=\"onModalShow\"\n >\n <div class=\"w-autocompletefk-toolbar\">\n <IconField class=\"w-autocompletefk-toolbar-search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n ref=\"modalSearchInput\"\n v-model=\"modalSearch\"\n placeholder=\"Pesquisar...\"\n class=\"w-full\"\n autofocus\n @keydown=\"onModalSearchKeydown\"\n />\n </IconField>\n <div class=\"w-autocompletefk-toolbar-actions\">\n <Button\n v-if=\"showCreate\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n size=\"small\"\n @click=\"openCreateForm\"\n />\n </div>\n </div>\n\n <div ref=\"modalTableWrap\" @keydown.capture=\"onModalGridKeydown\">\n <DataTable\n v-model:selection=\"modalSelection\"\n v-model:expanded-rows=\"expandedRows\"\n :meta-key-selection=\"false\"\n :value=\"modalData\"\n :loading=\"modalLoading\"\n paginator\n lazy\n striped-rows\n removable-sort\n size=\"small\"\n :rows=\"modalPageSize\"\n :total-records=\"modalTotalRecords\"\n :sort-field=\"modalSortField ?? undefined\"\n :sort-order=\"modalSortOrder\"\n :selection-mode=\"multiple ? 'multiple' : 'single'\"\n :data-key=\"optionValue\"\n @page=\"onModalPage\"\n @sort=\"(e: any) => onModalSort({ sortField: e.sortField, sortOrder: e.sortOrder })\"\n @row-dblclick=\"onRowDblClick\"\n >\n <Column :selection-mode=\"multiple ? 'multiple' : 'single'\" header-style=\"width: 3rem\" />\n <Column\n v-for=\"col in modalColumns\"\n :key=\"col.field\"\n :field=\"col.field\"\n :header=\"col.header\"\n :sortable=\"(col as ColumnDef).sortable ?? true\"\n :style=\"(col as ColumnDef).style\"\n >\n <template #body=\"{ data }\">\n <WCrudColumnRenderer\n v-if=\"(col as ColumnDef).type\"\n :column=\"col as ColumnDef\"\n :value=\"data[col.field]\"\n :row-data=\"data\"\n />\n <template v-else>\n {{ data[col.field] }}\n </template>\n </template>\n </Column>\n\n <!-- Coluna de ações CRUD -->\n <Column v-if=\"hasRowActions\" header=\"\" :style=\"{ width: '6rem' }\">\n <template #body=\"{ data }\">\n <div class=\"flex items-center justify-end gap-1\">\n <Button\n v-if=\"showEdit\"\n v-tooltip.top=\"'Editar'\"\n icon=\"pi pi-pencil\"\n text\n rounded\n size=\"small\"\n @click=\"openEditForm(data)\"\n />\n <Button\n v-if=\"showDelete\"\n v-tooltip.top=\"'Excluir'\"\n icon=\"pi pi-trash\"\n text\n rounded\n size=\"small\"\n severity=\"danger\"\n @click=\"confirmDelete(data)\"\n />\n </div>\n </template>\n </Column>\n\n <!-- Sub-linhas: mini-grid por linha expandida (colunas vêm do `subRows`) -->\n <template v-if=\"subRows\" #expansion=\"{ data }\">\n <table class=\"w-fk-subrows\">\n <thead>\n <tr>\n <th v-for=\"col in subRowsColumns\" :key=\"col.field\">{{ col.header }}</th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(sub, i) in subRowsDe(data)\" :key=\"i\">\n <td v-for=\"col in subRowsColumns\" :key=\"col.field\">\n {{ sub[col.field] ?? '—' }}\n </td>\n </tr>\n </tbody>\n </table>\n </template>\n\n <template #empty>\n <div class=\"w-autocompletefk-empty\">Nenhum registro encontrado</div>\n </template>\n </DataTable>\n </div>\n\n <template #footer>\n <div class=\"w-autocompletefk-footer\">\n <Button label=\"Cancelar\" severity=\"secondary\" text @click=\"modalVisible = false\" />\n <Button\n :label=\"\n multiple && modalSelecionados.length\n ? `Selecionar (${modalSelecionados.length})`\n : 'Selecionar'\n \"\n icon=\"pi pi-check\"\n :disabled=\"!multiple && !modalSelecionados.length\"\n @click=\"confirmModalSelection\"\n />\n </div>\n </template>\n </Dialog>\n\n <!-- Form Dialog (CRUD) — nested sobre o modal -->\n <WCrudFormDialog\n v-if=\"crudAvailable\"\n :visible=\"formDialogVisible\"\n :title=\"formDialogTitle\"\n :fields=\"formFields\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :saving=\"formSaving\"\n :width=\"dialogWidth\"\n @update:visible=\"\n (v) => {\n formDialogVisible = v\n if (!v) editingItem = null\n }\n \"\n @update:field=\"setFormField\"\n @save=\"saveForm\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, watch, computed, inject, reactive, nextTick, onMounted } from 'vue'\nimport AutoComplete from 'primevue/autocomplete'\nimport Chip from 'primevue/chip'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport DataTable from 'primevue/datatable'\nimport Column from 'primevue/column'\nimport Button from 'primevue/button'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport { W_DATA_PROVIDER_KEY } from '@/types/plugin'\nimport type { DataProvider } from '@/types/dataProvider'\nimport type { FieldDef, FieldSubRowsFetch } from '@/types/field'\nimport type { ColumnDef } from '@/types/column'\nimport type { ApiFieldMeta } from '@/utils/fieldMapper'\nimport { mapApiFieldsToFieldDefs, mapApiFieldsToColumnDefs } from '@/utils/fieldMapper'\nimport { useAppToast } from '@/composables/useAppToast'\nimport { useAppConfirm } from '@/composables/useAppConfirm'\nimport { extractApiError } from '@/composables/useApiError'\nimport WCrudFormDialog from '@/components/crud/WCrudFormDialog.vue'\nimport WCrudColumnRenderer from '@/components/crud/WCrudColumnRenderer.vue'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\ninterface ColumnMeta {\n field: string\n header: string\n}\n\n/** Filtro em cascata já resolvido (nome do parâmetro + valor atual). */\ninterface DrilldownFilter {\n field: string\n value: unknown\n required?: boolean\n}\n\n/** Valor aceito no `v-model`: id, objeto ou — em `multiple` — lista de ids/objetos. */\ntype FKValue = string | number | Record<string, unknown>\n\nconst props = withDefaults(\n defineProps<{\n modelValue: FKValue | FKValue[] | null\n endpoint: string\n endpointParams?: Record<string, string | number | boolean>\n /** Filtro(s) em cascata resolvido(s): aplicado(s) como parâmetro na busca. Com\n * `required` (default true), a busca só ocorre quando o valor estiver preenchido. */\n drilldown?: DrilldownFilter | DrilldownFilter[]\n /** Seleção múltipla: o campo vira chips e o `v-model` passa a ser uma lista\n * de objetos. O modal de pesquisa ganha caixas de marcação. */\n multiple?: boolean\n /** Em `multiple`, quantos chips mostrar antes de resumir o resto em `+N`\n * (0 = mostrar todos). Útil em campo estreito, tipo filtro de painel. */\n maxChips?: number\n optionLabel?: string\n /** Chave de uma segunda linha, em texto de apoio, dentro de cada sugestão — para\n * o dado que **decide** a escolha aparecer antes dela (ex.: saldo em estoque).\n * Vazia ou ausente na linha, a sugestão volta a ter uma linha só. */\n optionDescription?: string\n optionValue?: string\n placeholder?: string\n /** Placeholder exibido enquanto uma cascata obrigatória não estiver preenchida. */\n blockedPlaceholder?: string\n disabled?: boolean\n showClear?: boolean\n forceSelection?: boolean\n columns?: ColumnMeta[]\n minLength?: number\n dialogHeader?: string\n // CRUD no modal\n canCreate?: boolean\n canEdit?: boolean\n canDelete?: boolean\n crudFields?: FieldDef[]\n crudColumns?: ColumnDef[]\n /** Sub-linhas do grid do modal: recebe as linhas da página e devolve o mapa\n * `id → sub-linhas` + as colunas (dinâmicas) do mini-grid. Linha com entrada\n * no mapa abre expandida — ex.: lote (linha) com suas análises (sub-linhas). */\n subRows?: FieldSubRowsFetch\n dialogWidth?: string\n /** Foca o campo ao montar. Marca o input com o atributo nativo `autofocus`\n * para que o `focus()` do PrimeVue Dialog (em onAfterEnter) o encontre e\n * não roube o foco para o botão de fechar. */\n autofocus?: boolean\n }>(),\n {\n multiple: false,\n maxChips: 0,\n optionLabel: 'nome',\n optionValue: 'id',\n placeholder: 'Buscar...',\n disabled: false,\n showClear: true,\n forceSelection: true,\n minLength: 0,\n canCreate: undefined,\n canEdit: undefined,\n canDelete: undefined,\n dialogWidth: '480px',\n autofocus: false,\n },\n)\n\nconst emit = defineEmits<{\n /** Objeto selecionado — ou a lista de objetos quando `multiple`. */\n 'update:modelValue': [value: Record<string, unknown> | Record<string, unknown>[] | null]\n}>()\n\n// O template tem múltiplos nós raiz (o campo + os overlays Dialog/WCrudFormDialog).\n// Sem isto, `class`/`id`/`style` do consumidor não têm onde herdar e o Vue emite\n// warning de \"extraneous non-props attributes\". Aplicamos os attrs no nó visível.\ndefineOptions({ inheritAttrs: false })\n\nconst dataProvider = inject<DataProvider>(W_DATA_PROVIDER_KEY)\nif (!dataProvider) {\n throw new Error('[wPrimeVueComponents] dataProvider não encontrado. Registre o WPrimeVuePlugin.')\n}\nconst provider = dataProvider\n\nconst toast = useAppToast()\nconst { confirmDelete: confirmDeleteDialog } = useAppConfirm()\n\n// ---------------------------------------------------------------------------\n// Inline autocomplete\n// ---------------------------------------------------------------------------\n\nconst selectedItem = ref<Record<string, unknown> | null>(null)\n/** Seleção quando `multiple` — o campo mostra um chip por item. */\nconst selectedItems = ref<Record<string, unknown>[]>([])\nconst suggestions = ref<Record<string, unknown>[]>([])\nconst searching = ref(false)\n\n/** Valor entregue ao AutoComplete: objeto (single) ou lista (multiple). */\nconst acModel = computed(() => (props.multiple ? selectedItems.value : selectedItem.value))\n\n/** Identificador escalar de um valor do v-model (id cru ou objeto já resolvido). */\nfunction keyOf(v: unknown): unknown {\n if (v && typeof v === 'object') return (v as Record<string, unknown>)[props.optionValue]\n return v\n}\n\nfunction dedupeByKey(itens: Record<string, unknown>[]): Record<string, unknown>[] {\n const vistos = new Set<unknown>()\n const out: Record<string, unknown>[] = []\n for (const item of itens) {\n const k = keyOf(item)\n if (vistos.has(k)) continue\n vistos.add(k)\n out.push(item)\n }\n return out\n}\n\nfunction emitSelection() {\n emit('update:modelValue', props.multiple ? [...selectedItems.value] : selectedItem.value)\n}\n\nfunction labelOf(item: unknown): string {\n const v = (item as Record<string, unknown>)?.[props.optionLabel]\n return v == null ? '' : String(v)\n}\n\n/** Texto de apoio da sugestão. Vazio quando a linha não trouxe o campo. */\nfunction descriptionOf(item: unknown): string {\n if (!props.optionDescription) return ''\n const v = (item as Record<string, unknown>)?.[props.optionDescription]\n return v == null ? '' : String(v)\n}\n\n/** Quantos chips ficam escondidos atrás do `+N` (0 quando cabem todos). */\nconst chipsOcultos = computed(() =>\n props.maxChips > 0 ? Math.max(0, selectedItems.value.length - props.maxChips) : 0,\n)\n\n/** Nomes resumidos no `+N` — viram tooltip para não esconder informação. */\nconst chipsOcultosLabel = computed(() =>\n selectedItems.value.slice(props.maxChips).map(labelOf).join(', '),\n)\n\n/** O `x` do chip remove só aquele item — sem deixar o clique subir para o chip\n * (que abre a listagem). */\nfunction onChipRemove(event: Event, remove: (event: Event) => void) {\n event?.stopPropagation?.()\n remove(event)\n}\n\n/** Limpar tudo: em `multiple` a lista inteira; no simples, o valor. */\nfunction limparSelecao() {\n if (props.disabled) return\n if (props.multiple) {\n if (!selectedItems.value.length) return\n selectedItems.value = []\n } else {\n if (!selectedItem.value) return\n selectedItem.value = null\n }\n emitSelection()\n}\n\n/** Com `maxChips`, cada chip fica com uma fração da largura do campo — assim\n * eles dividem a linha (em vez de um por linha) e truncam o que não couber.\n * A reserva cobre o chip `+N` e a área de digitação; o piso de 7rem impede\n * chip ilegível em campo estreito (aí eles quebram para a linha de baixo). */\nconst chipStyle = computed(() =>\n props.multiple && props.maxChips > 0\n ? { '--w-fk-chip-max': `max(6rem, calc((100% - 6rem) / ${props.maxChips}))` }\n : undefined,\n)\n\n/** Mostra o \"limpar\" só quando há o que limpar (e o consumidor permite). */\nconst mostrarLimpar = computed(() => {\n if (!props.showClear || props.disabled) return false\n return props.multiple ? selectedItems.value.length > 0 : selectedItem.value !== null\n})\n\nlet debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n// Referência ao AutoComplete para aplicar o autofocus no input nativo.\nconst acRef = ref<{ $el?: HTMLElement } | null>(null)\n\nonMounted(() => {\n if (!props.autofocus) return\n nextTick(() => {\n const input = acRef.value?.$el?.querySelector?.('input') as HTMLInputElement | null\n if (!input) return\n // O atributo nativo faz o Dialog.focus() (onAfterEnter) mirar este input\n // em vez do botão fechar; o focus() cobre o caso fora de Dialog.\n input.setAttribute('autofocus', '')\n input.focus()\n input.select?.()\n })\n})\n\n// ---------------------------------------------------------------------------\n// Filtro em cascata (drill-down)\n// ---------------------------------------------------------------------------\n\nfunction isEmptyValue(v: unknown): boolean {\n return v === null || v === undefined || v === ''\n}\n\n/** Um campo FK guarda o objeto inteiro; extrai o identificador escalar para o filtro. */\nfunction normalizeDrillValue(v: unknown): unknown {\n if (v && typeof v === 'object') {\n const o = v as Record<string, unknown>\n return o.id ?? o.value ?? o\n }\n return v\n}\n\nconst drilldownList = computed<DrilldownFilter[]>(() => {\n const d = props.drilldown\n if (!d) return []\n return Array.isArray(d) ? d : [d]\n})\n\n/** true quando alguma cascata obrigatória ainda não tem valor → não busca. */\nconst blockedByRequired = computed(() =>\n drilldownList.value.some(\n (d) => (d.required ?? true) && isEmptyValue(normalizeDrillValue(d.value)),\n ),\n)\n\n/** Parâmetros de filtro derivados das cascatas com valor preenchido. */\nfunction drilldownParams(): Record<string, unknown> {\n const params: Record<string, unknown> = {}\n for (const d of drilldownList.value) {\n const val = normalizeDrillValue(d.value)\n if (!isEmptyValue(val)) params[d.field] = val\n }\n return params\n}\n\nconst effectivePlaceholder = computed(() =>\n blockedByRequired.value && props.blockedPlaceholder\n ? props.blockedPlaceholder\n : props.placeholder,\n)\n\n/** Em `multiple` o placeholder some quando já há chips — ele descreve o estado\n * \"sem filtro\"; ao lado de uma seleção viraria informação contraditória. */\nconst placeholderVisivel = computed(() =>\n props.multiple && selectedItems.value.length ? undefined : effectivePlaceholder.value,\n)\n\nasync function fetchOne(id: string | number): Promise<Record<string, unknown> | null> {\n try {\n const response = await provider.get(props.endpoint, id)\n return response.data\n } catch {\n return null\n }\n}\n\nasync function fetchById(id: string | number) {\n selectedItem.value = await fetchOne(id)\n}\n\n/** Resolve a lista do v-model: objeto já resolvido passa direto, id vira GET. */\nasync function resolveMany(valores: FKValue[]): Promise<Record<string, unknown>[]> {\n const itens = await Promise.all(\n valores.map(async (v) => {\n if (v && typeof v === 'object') {\n const obj = v as Record<string, unknown>\n if (props.optionLabel in obj) return obj\n const id = obj[props.optionValue]\n return id != null ? await fetchOne(id as string | number) : null\n }\n // Reaproveita o que já está resolvido — evita GET a cada mudança da lista.\n const atual = selectedItems.value.find((i) => i[props.optionValue] === v)\n return atual ?? (await fetchOne(v as string | number))\n }),\n )\n return itens.filter((i): i is Record<string, unknown> => i !== null)\n}\n\nasync function search(query: string) {\n // Cascata obrigatória sem valor → não busca (evita listar tudo).\n if (blockedByRequired.value) {\n suggestions.value = []\n return\n }\n searching.value = true\n try {\n const params: Record<string, unknown> = {\n page_size: 20,\n ...props.endpointParams,\n ...drilldownParams(),\n }\n if (query) params.search = query\n const response = await provider.list(props.endpoint, params)\n suggestions.value = response.data\n } catch {\n suggestions.value = []\n } finally {\n searching.value = false\n }\n}\n\nfunction onSearch(event: { query: string }) {\n const query = event.query || ''\n if (query.length < props.minLength) {\n suggestions.value = []\n return\n }\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => search(query), 300)\n}\n\nfunction onSelect(event: { value: Record<string, unknown> }) {\n // Em `multiple` o AutoComplete já mantém a lista — tratado em `onAcUpdate`.\n if (props.multiple) return\n selectedItem.value = event.value\n emit('update:modelValue', event.value)\n}\n\n/** Em `multiple`, o AutoComplete emite a lista inteira ao marcar/remover chip. */\nfunction onAcUpdate(value: unknown) {\n if (!props.multiple) return\n selectedItems.value = dedupeByKey(\n Array.isArray(value) ? (value as Record<string, unknown>[]) : [],\n )\n emitSelection()\n}\n\nfunction onClear() {\n // Em `multiple`, o AutoComplete emite `clear` também quando só descarta o texto\n // digitado (`forceSelection` sem correspondência) — limpar a lista aqui apagaria\n // a seleção inteira a cada busca sem match. O clear real chega como\n // `update:modelValue` (ícone de limpar), tratado em `onAcUpdate`.\n if (props.multiple) return\n selectedItem.value = null\n emitSelection()\n}\n\n// Resolve initial value\nwatch(\n () => props.modelValue,\n async (newVal) => {\n if (props.multiple) {\n const lista = newVal == null ? [] : Array.isArray(newVal) ? newVal : [newVal]\n const chaves = lista.map(keyOf)\n const atuais = selectedItems.value.map((i) => i[props.optionValue])\n // Mesma lista (na mesma ordem) → nada a resolver; evita loop com o emit.\n if (chaves.length === atuais.length && chaves.every((k, i) => k === atuais[i])) return\n selectedItems.value = await resolveMany(lista)\n return\n }\n if (newVal != null) {\n if (typeof newVal === 'object' && newVal !== null && props.optionLabel in newVal) {\n selectedItem.value = newVal as Record<string, unknown>\n return\n }\n if (!selectedItem.value || selectedItem.value[props.optionValue] !== newVal) {\n await fetchById(newVal as string | number)\n }\n } else {\n selectedItem.value = null\n }\n },\n { immediate: true },\n)\n\n// ---------------------------------------------------------------------------\n// Modal — State\n// ---------------------------------------------------------------------------\n\nconst modalVisible = ref(false)\nconst modalData = ref<Record<string, unknown>[]>([])\nconst modalLoading = ref(false)\nconst modalSearch = ref('')\nconst modalPage = ref(1)\nconst modalPageSize = ref(15)\nconst modalTotalRecords = ref(0)\nconst modalSelection = ref<Record<string, unknown> | Record<string, unknown>[] | null>(null)\n\n/** Itens marcados no modal, normalizados (single ou multiple). */\nconst modalSelecionados = computed<Record<string, unknown>[]>(() => {\n const sel = modalSelection.value\n if (!sel) return []\n return Array.isArray(sel) ? sel : [sel]\n})\nconst modalSortField = ref<string | null>(null)\nconst modalSortOrder = ref<1 | -1 | 0>(0)\nlet modalSearchTimer: ReturnType<typeof setTimeout> | null = null\n\n// --- Sub-linhas (expansão automática por linha com dados) ---\nconst subRowsMap = ref<Record<string, Record<string, unknown>[]>>({})\nconst subRowsColumns = ref<ColumnDef[]>([])\nconst expandedRows = ref<Record<string, boolean>>({})\n\n/** Busca as sub-linhas da página atual e abre as linhas que têm o que mostrar. */\nasync function fetchSubRows() {\n if (!props.subRows) return\n try {\n const { map, columns } = await props.subRows(modalData.value)\n subRowsMap.value = map || {}\n subRowsColumns.value = columns || []\n const abertos: Record<string, boolean> = {}\n for (const row of modalData.value) {\n const id = String(row[props.optionValue])\n if (subRowsMap.value[id]?.length) abertos[id] = true\n }\n expandedRows.value = abertos\n } catch {\n // Sub-linha é enriquecimento: falhou, o grid segue só com as linhas.\n subRowsMap.value = {}\n subRowsColumns.value = []\n expandedRows.value = {}\n }\n}\n\nfunction subRowsDe(row: Record<string, unknown>): Record<string, unknown>[] {\n return subRowsMap.value[String(row[props.optionValue])] || []\n}\n\n// Metadata from extras.fields\nconst apiFields = ref<ApiFieldMeta[]>([])\nconst crudAvailable = computed(() => {\n // Se o usuário forneceu crudFields, CRUD está habilitado\n if (props.crudFields?.length) return true\n // Se a API retornou extras.fields, CRUD está disponível\n return apiFields.value.length > 0\n})\n\n// Respeita props explícitas, senão auto-detecta\nconst showCreate = computed(() => props.canCreate ?? crudAvailable.value)\nconst showEdit = computed(() => props.canEdit ?? crudAvailable.value)\nconst showDelete = computed(() => props.canDelete ?? crudAvailable.value)\nconst hasRowActions = computed(() => showEdit.value || showDelete.value)\n\n// Campos do form — prioridade: props > auto-gerados do extras\nconst formFields = computed<FieldDef[]>(() => {\n if (props.crudFields?.length) return props.crudFields\n return mapApiFieldsToFieldDefs(apiFields.value)\n})\n\n// Colunas do modal — prioridade: crudColumns > props.columns > auto-geradas > fallback\nconst modalColumns = computed<ColumnDef[]>(() => {\n if (props.crudColumns?.length) return props.crudColumns\n if (props.columns?.length) {\n return props.columns.map((c) => ({\n field: c.field,\n header: c.header,\n sortable: true,\n }))\n }\n if (apiFields.value.length) {\n return mapApiFieldsToColumnDefs(apiFields.value)\n }\n return [{ field: props.optionLabel, header: props.optionLabel, sortable: true }]\n})\n\n// ---------------------------------------------------------------------------\n// Modal — Fetch\n// ---------------------------------------------------------------------------\n\nasync function fetchModalData() {\n // Cascata obrigatória sem valor → não lista (o modal mostra estado vazio).\n if (blockedByRequired.value) {\n modalData.value = []\n modalTotalRecords.value = 0\n return\n }\n modalLoading.value = true\n try {\n const params: Record<string, unknown> = {\n page: modalPage.value,\n page_size: modalPageSize.value,\n ...props.endpointParams,\n ...drilldownParams(),\n }\n if (modalSearch.value) params.search = modalSearch.value\n if (modalSortField.value && modalSortOrder.value !== 0) {\n params.ordering =\n modalSortOrder.value === -1 ? `-${modalSortField.value}` : modalSortField.value\n }\n const response = await provider.list(props.endpoint, params)\n modalData.value = response.data\n modalTotalRecords.value = response.rows\n void fetchSubRows()\n\n // Captura metadata de campos na primeira requisição\n if (response.extras?.fields && !props.columns?.length && !props.crudFields?.length) {\n apiFields.value = response.extras.fields as ApiFieldMeta[]\n }\n } catch {\n modalData.value = []\n modalTotalRecords.value = 0\n } finally {\n modalLoading.value = false\n }\n}\n\nfunction onInputKeydown(e: KeyboardEvent) {\n // F2 abre o modal de pesquisa (atalho estilo desktop).\n if (e.key === 'F2' && !props.disabled) {\n e.preventDefault()\n openModal()\n }\n}\n\nfunction openModal() {\n if (props.disabled) return\n modalSearch.value = ''\n modalPage.value = 1\n modalSortField.value = null\n modalSortOrder.value = 0\n // Em `multiple` o modal abre já com o que está selecionado no campo.\n modalSelection.value = props.multiple ? [...selectedItems.value] : null\n modalVisible.value = true\n fetchModalData()\n}\n\nfunction onModalPage(event: { page: number; rows: number }) {\n modalPage.value = event.page + 1\n modalPageSize.value = event.rows\n fetchModalData()\n}\n\nfunction onModalSort(event: { sortField?: string; sortOrder?: 1 | -1 | 0 }) {\n modalSortField.value = (event.sortField as string) ?? null\n modalSortOrder.value = event.sortOrder ?? 0\n modalPage.value = 1\n fetchModalData()\n}\n\nfunction confirmModalSelection() {\n if (props.multiple) {\n selectedItems.value = dedupeByKey(modalSelecionados.value)\n emitSelection()\n modalVisible.value = false\n return\n }\n const sel = modalSelecionados.value[0]\n if (!sel) return\n selectedItem.value = sel\n emit('update:modelValue', sel)\n modalVisible.value = false\n}\n\nfunction onRowDblClick(event: { data: Record<string, unknown> }) {\n // Em `multiple`, o duplo clique acrescenta à marcação e o modal continua\n // aberto — fechar a cada item tornaria a seleção de vários penosa.\n if (props.multiple) {\n modalSelection.value = dedupeByKey([...modalSelecionados.value, event.data])\n return\n }\n selectedItem.value = event.data\n emit('update:modelValue', event.data)\n modalVisible.value = false\n}\n\n// --- Modal: teclado (foco → pesquisa → grid) ---\nconst modalSearchInput = ref<{ $el?: HTMLElement } | null>(null)\nconst modalTableWrap = ref<HTMLElement | null>(null)\nlet lastSearched = ''\n\nfunction onModalShow() {\n lastSearched = ''\n nextTick(() => {\n const el = modalSearchInput.value?.$el as HTMLInputElement | undefined\n el?.focus?.()\n el?.select?.()\n })\n}\n\nfunction focusModalGrid() {\n nextTick(() => {\n const row = modalTableWrap.value?.querySelector<HTMLElement>('.p-datatable-tbody > tr')\n row?.focus()\n })\n}\n\nfunction onModalSearchKeydown(e: KeyboardEvent) {\n if (e.key !== 'Enter' || e.isComposing) return\n e.preventDefault()\n const q = modalSearch.value.trim()\n // Vazio ou já pesquisado → move o foco para o grid.\n if (q === '' || q === lastSearched) {\n focusModalGrid()\n return\n }\n // Texto novo → pesquisa imediata (cancela o debounce); o próximo Enter vai ao grid.\n if (modalSearchTimer) clearTimeout(modalSearchTimer)\n lastSearched = q\n modalPage.value = 1\n fetchModalData()\n}\n\nfunction onModalGridKeydown(e: KeyboardEvent) {\n // Enter no grid = confirmar a seleção (marcada com Espaço). Interceptado (capture)\n // antes do handler nativo do DataTable para não apenas alternar a marca.\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n // Em `multiple` o Enter confirma a marcação atual, inclusive vazia (= limpar).\n if (props.multiple || modalSelecionados.value.length) confirmModalSelection()\n }\n // Espaço e setas: tratados nativamente pelo DataTable (marca/navega).\n}\n\nwatch(modalSearch, () => {\n if (modalSearchTimer) clearTimeout(modalSearchTimer)\n modalSearchTimer = setTimeout(() => {\n modalPage.value = 1\n lastSearched = modalSearch.value.trim()\n fetchModalData()\n }, 300)\n})\n\n// Reage à mudança das cascatas: rebusca (se o modal estiver aberto) e limpa a\n// seleção que ficou obsoleta ao trocar a origem — sem apagar na carga inicial.\nwatch(\n () => drilldownList.value.map((d) => normalizeDrillValue(d.value)),\n (novos, antigos) => {\n if (modalVisible.value) {\n modalPage.value = 1\n fetchModalData()\n }\n if (!antigos) return\n const mudou = novos.some((v, i) => v !== antigos[i])\n const tinhaValorAntes = antigos.some((v) => !isEmptyValue(v))\n if (!mudou || !tinhaValorAntes) return\n if (props.multiple) {\n if (!selectedItems.value.length) return\n selectedItems.value = []\n emitSelection()\n return\n }\n if (selectedItem.value) {\n selectedItem.value = null\n emit('update:modelValue', null)\n }\n },\n)\n\n// ---------------------------------------------------------------------------\n// Modal — CRUD\n// ---------------------------------------------------------------------------\n\nconst formDialogVisible = ref(false)\nconst formSaving = ref(false)\nconst editingItem = ref<Record<string, unknown> | null>(null)\nconst formData = reactive<Record<string, unknown>>({})\n\nconst isEditing = computed(() => editingItem.value !== null)\nconst formDialogTitle = computed(() => (isEditing.value ? 'Editar Registro' : 'Novo Registro'))\n\nfunction getDefaults(): Record<string, unknown> {\n const defaults: Record<string, unknown> = {}\n for (const f of formFields.value) {\n defaults[f.field] =\n f.defaultValue !== undefined\n ? typeof f.defaultValue === 'function'\n ? (f.defaultValue as () => unknown)()\n : f.defaultValue\n : null\n }\n return defaults\n}\n\nfunction resetForm() {\n const defs = getDefaults()\n for (const key of Object.keys(formData)) {\n delete formData[key]\n }\n for (const [key, val] of Object.entries(defs)) {\n formData[key] = val\n }\n}\n\nfunction openCreateForm() {\n editingItem.value = null\n resetForm()\n formDialogVisible.value = true\n}\n\nfunction openEditForm(item: Record<string, unknown>) {\n editingItem.value = item\n for (const f of formFields.value) {\n formData[f.field] = item[f.field] !== undefined ? item[f.field] : null\n }\n formDialogVisible.value = true\n}\n\nfunction setFormField(field: string, value: unknown) {\n formData[field] = value\n}\n\nasync function saveForm() {\n formSaving.value = true\n try {\n const payload = { ...formData }\n\n // FK — extrair ID de objetos selecionados\n for (const f of formFields.value) {\n const val = payload[f.field]\n if (f.type === 'fk' && val !== null && typeof val === 'object') {\n const key = f.optionValue || 'id'\n payload[f.field] = (val as Record<string, unknown>)[key] ?? val\n }\n }\n\n let response\n\n if (isEditing.value && editingItem.value) {\n const pk = editingItem.value[props.optionValue]\n response = await provider.update(props.endpoint, pk as string | number, payload)\n // Atualiza na lista do modal\n const idx = modalData.value.findIndex((i) => i[props.optionValue] === pk)\n if (idx !== -1) {\n modalData.value[idx] = response.data\n }\n toast.success('Registro atualizado com sucesso')\n } else {\n // Cria herdando o(s) pai(s) da cascata (dependsOn) — ex.: um novo lote nasce\n // no produto selecionado. Sem semear o pai o backend rejeitaria o filho órfão.\n // Só completa chaves ausentes: um campo do form com o mesmo nome prevalece.\n const parent = drilldownParams()\n for (const [key, value] of Object.entries(parent)) {\n const current = payload[key]\n if (current === undefined || current === null || current === '') {\n payload[key] = value\n }\n }\n response = await provider.create(props.endpoint, payload)\n // Adiciona na lista e seleciona automaticamente\n modalData.value.unshift(response.data)\n modalTotalRecords.value++\n toast.success('Registro criado com sucesso')\n }\n\n formDialogVisible.value = false\n editingItem.value = null\n\n // Auto-seleciona o registro criado/editado (em `multiple`, acrescenta)\n modalSelection.value = props.multiple\n ? dedupeByKey([...modalSelecionados.value, response.data])\n : response.data\n } catch (err) {\n toast.error(extractApiError(err, 'Erro ao salvar registro'))\n } finally {\n formSaving.value = false\n }\n}\n\nfunction confirmDelete(item: Record<string, unknown>) {\n confirmDeleteDialog(async () => {\n try {\n const pk = item[props.optionValue]\n await provider.delete(props.endpoint, pk as string | number)\n const idx = modalData.value.findIndex((i) => i[props.optionValue] === pk)\n if (idx !== -1) {\n modalData.value.splice(idx, 1)\n modalTotalRecords.value--\n }\n // Se o item deletado estava selecionado, sai da seleção\n if (props.multiple) {\n if (selectedItems.value.some((i) => i[props.optionValue] === pk)) {\n selectedItems.value = selectedItems.value.filter((i) => i[props.optionValue] !== pk)\n emitSelection()\n }\n modalSelection.value = modalSelecionados.value.filter((i) => i[props.optionValue] !== pk)\n } else {\n if (selectedItem.value && selectedItem.value[props.optionValue] === pk) {\n selectedItem.value = null\n emit('update:modelValue', null)\n }\n if (modalSelecionados.value[0]?.[props.optionValue] === pk) {\n modalSelection.value = null\n }\n }\n toast.success('Registro excluído com sucesso')\n } catch (err) {\n toast.error(extractApiError(err, 'Erro ao excluir registro'))\n }\n })\n}\n</script>\n\n<template>\n <div\n class=\"w-autocompletefk\"\n :class=\"{ 'w-autocompletefk-has-clear': mostrarLimpar }\"\n :style=\"chipStyle\"\n v-bind=\"$attrs\"\n >\n <AutoComplete\n ref=\"acRef\"\n :model-value=\"acModel\"\n :suggestions=\"suggestions\"\n :multiple=\"multiple\"\n :option-label=\"optionLabel\"\n :placeholder=\"placeholderVisivel\"\n :disabled=\"disabled\"\n :force-selection=\"forceSelection\"\n :loading=\"searching\"\n :show-clear=\"false\"\n fluid\n @complete=\"onSearch\"\n @item-select=\"onSelect\"\n @update:model-value=\"onAcUpdate\"\n @clear=\"onClear\"\n @keydown=\"onInputKeydown\"\n >\n <!-- Sugestão em duas linhas quando `optionDescription` aponta para algo\n preenchido. Sem ela o slot não é declarado, e o AutoComplete volta ao\n render padrão de uma linha — nada muda para quem não usa. -->\n <template v-if=\"optionDescription\" #option=\"{ option }\">\n <span class=\"w-fk-option\">\n <span class=\"w-fk-option__label\">{{ labelOf(option) }}</span>\n <span v-if=\"descriptionOf(option)\" class=\"w-fk-option__desc\">\n {{ descriptionOf(option) }}\n </span>\n </span>\n </template>\n\n <!-- Chip clicado abre a listagem já marcada com a seleção atual. Com\n `maxChips`, os excedentes viram um chip `+N` (somem do campo, mas\n continuam no tooltip) — chip nenhum pode esticar a caixa. -->\n <template v-if=\"multiple\" #chip=\"{ value, index, removeCallback }\">\n <Chip\n v-if=\"!maxChips || index < maxChips\"\n v-tooltip.top=\"labelOf(value)\"\n class=\"p-autocomplete-chip w-autocompletefk-chip\"\n :label=\"labelOf(value)\"\n removable\n @click=\"openModal\"\n @remove=\"onChipRemove($event, removeCallback)\"\n />\n <Chip\n v-else-if=\"index === maxChips\"\n v-tooltip.top=\"chipsOcultosLabel\"\n class=\"p-autocomplete-chip w-autocompletefk-chip w-autocompletefk-chip-more\"\n :label=\"`+${chipsOcultos}`\"\n @click=\"openModal\"\n />\n <span v-else class=\"w-autocompletefk-chip-hidden\" />\n </template>\n </AutoComplete>\n <button\n v-if=\"mostrarLimpar\"\n v-tooltip.top=\"'Limpar'\"\n type=\"button\"\n data-kbd-skip\n tabindex=\"-1\"\n class=\"w-autocompletefk-clear\"\n @click=\"limparSelecao\"\n >\n <i class=\"pi pi-times\" />\n </button>\n <button\n v-tooltip.top=\"'Pesquisar (F2)'\"\n type=\"button\"\n :disabled=\"disabled\"\n data-kbd-skip\n tabindex=\"-1\"\n class=\"w-autocompletefk-trigger\"\n @click=\"openModal\"\n >\n <i class=\"pi pi-search\" />\n </button>\n </div>\n\n <!-- Modal de Pesquisa + CRUD -->\n <Dialog\n v-model:visible=\"modalVisible\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n :header=\"dialogHeader || 'Pesquisar'\"\n :style=\"{ width: '80vw' }\"\n modal\n :draggable=\"false\"\n class=\"w-autocompletefk-dialog\"\n @show=\"onModalShow\"\n >\n <div class=\"w-autocompletefk-toolbar\">\n <IconField class=\"w-autocompletefk-toolbar-search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n ref=\"modalSearchInput\"\n v-model=\"modalSearch\"\n placeholder=\"Pesquisar...\"\n class=\"w-full\"\n autofocus\n @keydown=\"onModalSearchKeydown\"\n />\n </IconField>\n <div class=\"w-autocompletefk-toolbar-actions\">\n <Button\n v-if=\"showCreate\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n size=\"small\"\n @click=\"openCreateForm\"\n />\n </div>\n </div>\n\n <div ref=\"modalTableWrap\" @keydown.capture=\"onModalGridKeydown\">\n <DataTable\n v-model:selection=\"modalSelection\"\n v-model:expanded-rows=\"expandedRows\"\n :meta-key-selection=\"false\"\n :value=\"modalData\"\n :loading=\"modalLoading\"\n paginator\n lazy\n striped-rows\n removable-sort\n size=\"small\"\n :rows=\"modalPageSize\"\n :total-records=\"modalTotalRecords\"\n :sort-field=\"modalSortField ?? undefined\"\n :sort-order=\"modalSortOrder\"\n :selection-mode=\"multiple ? 'multiple' : 'single'\"\n :data-key=\"optionValue\"\n @page=\"onModalPage\"\n @sort=\"(e: any) => onModalSort({ sortField: e.sortField, sortOrder: e.sortOrder })\"\n @row-dblclick=\"onRowDblClick\"\n >\n <Column :selection-mode=\"multiple ? 'multiple' : 'single'\" header-style=\"width: 3rem\" />\n <Column\n v-for=\"col in modalColumns\"\n :key=\"col.field\"\n :field=\"col.field\"\n :header=\"col.header\"\n :sortable=\"(col as ColumnDef).sortable ?? true\"\n :style=\"(col as ColumnDef).style\"\n >\n <template #body=\"{ data }\">\n <WCrudColumnRenderer\n v-if=\"(col as ColumnDef).type\"\n :column=\"col as ColumnDef\"\n :value=\"data[col.field]\"\n :row-data=\"data\"\n />\n <template v-else>\n {{ data[col.field] }}\n </template>\n </template>\n </Column>\n\n <!-- Coluna de ações CRUD -->\n <Column v-if=\"hasRowActions\" header=\"\" :style=\"{ width: '6rem' }\">\n <template #body=\"{ data }\">\n <div class=\"flex items-center justify-end gap-1\">\n <Button\n v-if=\"showEdit\"\n v-tooltip.top=\"'Editar'\"\n icon=\"pi pi-pencil\"\n text\n rounded\n size=\"small\"\n @click=\"openEditForm(data)\"\n />\n <Button\n v-if=\"showDelete\"\n v-tooltip.top=\"'Excluir'\"\n icon=\"pi pi-trash\"\n text\n rounded\n size=\"small\"\n severity=\"danger\"\n @click=\"confirmDelete(data)\"\n />\n </div>\n </template>\n </Column>\n\n <!-- Sub-linhas: mini-grid por linha expandida (colunas vêm do `subRows`) -->\n <template v-if=\"subRows\" #expansion=\"{ data }\">\n <table class=\"w-fk-subrows\">\n <thead>\n <tr>\n <th v-for=\"col in subRowsColumns\" :key=\"col.field\">{{ col.header }}</th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(sub, i) in subRowsDe(data)\" :key=\"i\">\n <td v-for=\"col in subRowsColumns\" :key=\"col.field\">\n {{ sub[col.field] ?? '—' }}\n </td>\n </tr>\n </tbody>\n </table>\n </template>\n\n <template #empty>\n <div class=\"w-autocompletefk-empty\">Nenhum registro encontrado</div>\n </template>\n </DataTable>\n </div>\n\n <template #footer>\n <div class=\"w-autocompletefk-footer\">\n <Button label=\"Cancelar\" severity=\"secondary\" text @click=\"modalVisible = false\" />\n <Button\n :label=\"\n multiple && modalSelecionados.length\n ? `Selecionar (${modalSelecionados.length})`\n : 'Selecionar'\n \"\n icon=\"pi pi-check\"\n :disabled=\"!multiple && !modalSelecionados.length\"\n @click=\"confirmModalSelection\"\n />\n </div>\n </template>\n </Dialog>\n\n <!-- Form Dialog (CRUD) — nested sobre o modal -->\n <WCrudFormDialog\n v-if=\"crudAvailable\"\n :visible=\"formDialogVisible\"\n :title=\"formDialogTitle\"\n :fields=\"formFields\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :saving=\"formSaving\"\n :width=\"dialogWidth\"\n @update:visible=\"\n (v) => {\n formDialogVisible = v\n if (!v) editingItem = null\n }\n \"\n @update:field=\"setFormField\"\n @save=\"saveForm\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted } from 'vue'\nimport DatePicker from 'primevue/datepicker'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Popover from 'primevue/popover'\nimport Button from 'primevue/button'\nimport { vMaska } from 'maska/vue'\nimport { useDateInput, type DateValueFormat } from '@/composables/useDateInput'\n\n/**\n * Campo de data pt-BR padronizado.\n * - Exibe `DD/MM/YYYY` (ou `DD/MM/YYYY HH:mm` com `showTime`) e salva `YYYY-MM-DD`.\n * - Digitação direta com máscara: `30051988` vira `30/05/1988`.\n * - Ícone de calendário abre o seletor (Hoje / Limpar).\n * - `F2` preenche a data de hoje.\n * - `autonow`: no mount, se o valor for `null`, preenche hoje; senão mantém.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: string | Date | null\n /** Formato do valor emitido: `'iso'` (YYYY-MM-DD, default) ou `'date'` (Date). */\n valueFormat?: DateValueFormat\n showTime?: boolean\n /** No mount: se o valor for `null`, preenche a data de hoje. */\n autonow?: boolean\n minDate?: string | Date\n maxDate?: string | Date\n disabled?: boolean\n showClear?: boolean\n placeholder?: string\n invalid?: boolean\n inputId?: string\n }>(),\n {\n modelValue: null,\n valueFormat: 'iso',\n showTime: false,\n autonow: false,\n disabled: false,\n showClear: true,\n invalid: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [value: string | Date | null] }>()\n\nconst di = useDateInput({\n showTime: () => props.showTime,\n valueFormat: () => props.valueFormat,\n})\n\nconst typed = ref('') // texto no input (DD/MM/YYYY[ HH:mm])\nconst panelDate = ref<Date | null>(null) // valor do calendário\nconst op = ref<InstanceType<typeof Popover> | null>(null)\n\nconst maskaOptions = computed(() => ({ mask: di.mask() }))\nconst minDateObj = computed(() => di.toDate(props.minDate) ?? undefined)\nconst maxDateObj = computed(() => di.toDate(props.maxDate) ?? undefined)\n\n// Única sincronização externo → interno (o resto é orientado a eventos, sem watchers).\nwatch(\n () => props.modelValue,\n (v) => {\n const d = di.toDate(v)\n panelDate.value = d\n typed.value = di.toDisplay(d)\n },\n { immediate: true },\n)\n\nfunction commit(date: Date | null) {\n emit('update:modelValue', di.toValue(date))\n}\n\nfunction onTypedInput() {\n if (typed.value === '') {\n commit(null)\n return\n }\n // Só comita quando o texto formar uma data completa e válida.\n const d = di.fromDisplay(typed.value)\n if (d) commit(d)\n}\n\nfunction onBlur() {\n // Ao sair, normaliza o texto para o último valor válido (descarta digitação incompleta).\n typed.value = di.toDisplay(di.toDate(props.modelValue))\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'F2' && !props.disabled) {\n e.preventDefault()\n setToday()\n }\n}\n\nfunction onPick(value: unknown) {\n const d = (value as Date | null) ?? null\n commit(d)\n if (!props.showTime) op.value?.hide()\n}\n\nfunction setToday() {\n commit(di.today())\n}\n\nfunction clearValue() {\n commit(null)\n op.value?.hide()\n}\n\nfunction toggle(e: Event) {\n if (props.disabled) return\n op.value?.toggle(e)\n}\n\nonMounted(() => {\n if (props.autonow && props.modelValue == null) setToday()\n})\n</script>\n\n<template>\n <div class=\"w-datepicker\">\n <IconField>\n <InputText\n :id=\"inputId\"\n v-model=\"typed\"\n v-maska=\"maskaOptions\"\n :placeholder=\"placeholder ?? di.placeholder()\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n fluid\n autocomplete=\"off\"\n inputmode=\"numeric\"\n @input=\"onTypedInput\"\n @blur=\"onBlur\"\n @keydown=\"onKeydown\"\n />\n <InputIcon\n class=\"pi pi-calendar w-datepicker-icon\"\n :class=\"{ 'w-datepicker-icon-disabled': disabled }\"\n role=\"button\"\n tabindex=\"-1\"\n aria-label=\"Abrir calendário\"\n @click=\"toggle\"\n />\n </IconField>\n\n <Popover ref=\"op\">\n <div class=\"w-datepicker-panel\">\n <DatePicker\n :model-value=\"panelDate ?? undefined\"\n inline\n :show-time=\"showTime\"\n hour-format=\"24\"\n :min-date=\"minDateObj\"\n :max-date=\"maxDateObj\"\n @update:model-value=\"onPick\"\n />\n <div class=\"w-datepicker-actions\">\n <Button label=\"Hoje\" text size=\"small\" @click=\"setToday\" />\n <Button\n v-if=\"showClear\"\n label=\"Limpar\"\n text\n size=\"small\"\n severity=\"secondary\"\n @click=\"clearValue\"\n />\n </div>\n </div>\n </Popover>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted } from 'vue'\nimport DatePicker from 'primevue/datepicker'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Popover from 'primevue/popover'\nimport Button from 'primevue/button'\nimport { vMaska } from 'maska/vue'\nimport { useDateInput, type DateValueFormat } from '@/composables/useDateInput'\n\n/**\n * Campo de data pt-BR padronizado.\n * - Exibe `DD/MM/YYYY` (ou `DD/MM/YYYY HH:mm` com `showTime`) e salva `YYYY-MM-DD`.\n * - Digitação direta com máscara: `30051988` vira `30/05/1988`.\n * - Ícone de calendário abre o seletor (Hoje / Limpar).\n * - `F2` preenche a data de hoje.\n * - `autonow`: no mount, se o valor for `null`, preenche hoje; senão mantém.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: string | Date | null\n /** Formato do valor emitido: `'iso'` (YYYY-MM-DD, default) ou `'date'` (Date). */\n valueFormat?: DateValueFormat\n showTime?: boolean\n /** No mount: se o valor for `null`, preenche a data de hoje. */\n autonow?: boolean\n minDate?: string | Date\n maxDate?: string | Date\n disabled?: boolean\n showClear?: boolean\n placeholder?: string\n invalid?: boolean\n inputId?: string\n }>(),\n {\n modelValue: null,\n valueFormat: 'iso',\n showTime: false,\n autonow: false,\n disabled: false,\n showClear: true,\n invalid: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [value: string | Date | null] }>()\n\nconst di = useDateInput({\n showTime: () => props.showTime,\n valueFormat: () => props.valueFormat,\n})\n\nconst typed = ref('') // texto no input (DD/MM/YYYY[ HH:mm])\nconst panelDate = ref<Date | null>(null) // valor do calendário\nconst op = ref<InstanceType<typeof Popover> | null>(null)\n\nconst maskaOptions = computed(() => ({ mask: di.mask() }))\nconst minDateObj = computed(() => di.toDate(props.minDate) ?? undefined)\nconst maxDateObj = computed(() => di.toDate(props.maxDate) ?? undefined)\n\n// Única sincronização externo → interno (o resto é orientado a eventos, sem watchers).\nwatch(\n () => props.modelValue,\n (v) => {\n const d = di.toDate(v)\n panelDate.value = d\n typed.value = di.toDisplay(d)\n },\n { immediate: true },\n)\n\nfunction commit(date: Date | null) {\n emit('update:modelValue', di.toValue(date))\n}\n\nfunction onTypedInput() {\n if (typed.value === '') {\n commit(null)\n return\n }\n // Só comita quando o texto formar uma data completa e válida.\n const d = di.fromDisplay(typed.value)\n if (d) commit(d)\n}\n\nfunction onBlur() {\n // Ao sair, normaliza o texto para o último valor válido (descarta digitação incompleta).\n typed.value = di.toDisplay(di.toDate(props.modelValue))\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'F2' && !props.disabled) {\n e.preventDefault()\n setToday()\n }\n}\n\nfunction onPick(value: unknown) {\n const d = (value as Date | null) ?? null\n commit(d)\n if (!props.showTime) op.value?.hide()\n}\n\nfunction setToday() {\n commit(di.today())\n}\n\nfunction clearValue() {\n commit(null)\n op.value?.hide()\n}\n\nfunction toggle(e: Event) {\n if (props.disabled) return\n op.value?.toggle(e)\n}\n\nonMounted(() => {\n if (props.autonow && props.modelValue == null) setToday()\n})\n</script>\n\n<template>\n <div class=\"w-datepicker\">\n <IconField>\n <InputText\n :id=\"inputId\"\n v-model=\"typed\"\n v-maska=\"maskaOptions\"\n :placeholder=\"placeholder ?? di.placeholder()\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n fluid\n autocomplete=\"off\"\n inputmode=\"numeric\"\n @input=\"onTypedInput\"\n @blur=\"onBlur\"\n @keydown=\"onKeydown\"\n />\n <InputIcon\n class=\"pi pi-calendar w-datepicker-icon\"\n :class=\"{ 'w-datepicker-icon-disabled': disabled }\"\n role=\"button\"\n tabindex=\"-1\"\n aria-label=\"Abrir calendário\"\n @click=\"toggle\"\n />\n </IconField>\n\n <Popover ref=\"op\">\n <div class=\"w-datepicker-panel\">\n <DatePicker\n :model-value=\"panelDate ?? undefined\"\n inline\n :show-time=\"showTime\"\n hour-format=\"24\"\n :min-date=\"minDateObj\"\n :max-date=\"maxDateObj\"\n @update:model-value=\"onPick\"\n />\n <div class=\"w-datepicker-actions\">\n <Button label=\"Hoje\" text size=\"small\" @click=\"setToday\" />\n <Button\n v-if=\"showClear\"\n label=\"Limpar\"\n text\n size=\"small\"\n severity=\"secondary\"\n @click=\"clearValue\"\n />\n </div>\n </div>\n </Popover>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport InputGroup from 'primevue/inputgroup'\nimport InputGroupAddon from 'primevue/inputgroupaddon'\n\nconst props = withDefaults(\n defineProps<{\n /** Numeric model — the real value sent to the API (e.g. 1234.56). */\n modelValue: number | null\n /** Fixed decimal places (default: 2). */\n decimals?: number\n /** Show a \"R$\" prefix. Ignored when `prefix` is set. */\n currency?: boolean\n /** Custom leading addon (overrides `currency`). */\n prefix?: string\n /** Trailing addon. */\n suffix?: string\n /** Display locale for grouping/decimal separators (default: 'pt-BR'). */\n locale?: string\n disabled?: boolean\n placeholder?: string\n invalid?: boolean\n }>(),\n {\n decimals: 2,\n currency: false,\n locale: 'pt-BR',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n}>()\n\nconst resolvedPrefix = computed(() => props.prefix ?? (props.currency ? 'R$' : undefined))\n\nconst hasAddon = computed(() => !!resolvedPrefix.value || !!props.suffix)\n\nconst display = computed(() => {\n const v = props.modelValue\n if (v === null || v === undefined || Number.isNaN(v)) return ''\n return new Intl.NumberFormat(props.locale, {\n minimumFractionDigits: props.decimals,\n maximumFractionDigits: props.decimals,\n }).format(v)\n})\n\nfunction onInput(event: Event) {\n const digits = (event.target as HTMLInputElement).value.replace(/\\D/g, '')\n if (!digits) {\n emit('update:modelValue', null)\n return\n }\n // Digits fill from the right: \"1000\" with decimals=2 -> 10.00\n const num = Number(digits) / Math.pow(10, props.decimals)\n emit('update:modelValue', num)\n}\n</script>\n\n<template>\n <InputGroup v-if=\"hasAddon\" class=\"w-money-input\">\n <InputGroupAddon v-if=\"resolvedPrefix\">{{ resolvedPrefix }}</InputGroupAddon>\n <InputText\n :model-value=\"display\"\n inputmode=\"numeric\"\n class=\"w-money-input__field\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n @input=\"onInput\"\n />\n <InputGroupAddon v-if=\"suffix\">{{ suffix }}</InputGroupAddon>\n </InputGroup>\n <InputText\n v-else\n :model-value=\"display\"\n inputmode=\"numeric\"\n fluid\n class=\"w-money-input__field\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n @input=\"onInput\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport InputGroup from 'primevue/inputgroup'\nimport InputGroupAddon from 'primevue/inputgroupaddon'\n\nconst props = withDefaults(\n defineProps<{\n /** Numeric model — the real value sent to the API (e.g. 1234.56). */\n modelValue: number | null\n /** Fixed decimal places (default: 2). */\n decimals?: number\n /** Show a \"R$\" prefix. Ignored when `prefix` is set. */\n currency?: boolean\n /** Custom leading addon (overrides `currency`). */\n prefix?: string\n /** Trailing addon. */\n suffix?: string\n /** Display locale for grouping/decimal separators (default: 'pt-BR'). */\n locale?: string\n disabled?: boolean\n placeholder?: string\n invalid?: boolean\n }>(),\n {\n decimals: 2,\n currency: false,\n locale: 'pt-BR',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n}>()\n\nconst resolvedPrefix = computed(() => props.prefix ?? (props.currency ? 'R$' : undefined))\n\nconst hasAddon = computed(() => !!resolvedPrefix.value || !!props.suffix)\n\nconst display = computed(() => {\n const v = props.modelValue\n if (v === null || v === undefined || Number.isNaN(v)) return ''\n return new Intl.NumberFormat(props.locale, {\n minimumFractionDigits: props.decimals,\n maximumFractionDigits: props.decimals,\n }).format(v)\n})\n\nfunction onInput(event: Event) {\n const digits = (event.target as HTMLInputElement).value.replace(/\\D/g, '')\n if (!digits) {\n emit('update:modelValue', null)\n return\n }\n // Digits fill from the right: \"1000\" with decimals=2 -> 10.00\n const num = Number(digits) / Math.pow(10, props.decimals)\n emit('update:modelValue', num)\n}\n</script>\n\n<template>\n <InputGroup v-if=\"hasAddon\" class=\"w-money-input\">\n <InputGroupAddon v-if=\"resolvedPrefix\">{{ resolvedPrefix }}</InputGroupAddon>\n <InputText\n :model-value=\"display\"\n inputmode=\"numeric\"\n class=\"w-money-input__field\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n @input=\"onInput\"\n />\n <InputGroupAddon v-if=\"suffix\">{{ suffix }}</InputGroupAddon>\n </InputGroup>\n <InputText\n v-else\n :model-value=\"display\"\n inputmode=\"numeric\"\n fluid\n class=\"w-money-input__field\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n @input=\"onInput\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, reactive, computed, watch, onBeforeUnmount } from 'vue'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport Button from 'primevue/button'\nimport Slider from 'primevue/slider'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\n/**\n * Upload de imagem com preview e crop (pan + zoom) integrado.\n * Sem dependência externa — o recorte é feito via <canvas>.\n * Emite um `File` recortado (ou `null`), consumível direto pelo\n * `useCrudManager`, que detecta campos de imagem e envia como multipart.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: File | string | null\n accept?: string\n /** Tamanho máximo em bytes (default 5MB). */\n maxSize?: number\n /** Proporção do recorte (1 = quadrado). Ignorado quando `circular`. */\n aspectRatio?: number\n /** Stencil circular (avatar). Força proporção 1 e saída PNG com alpha. */\n circular?: boolean\n /** Dimensões do preview em px. */\n width?: number\n height?: number\n }>(),\n {\n modelValue: null,\n accept: 'image/*',\n maxSize: 5_242_880,\n aspectRatio: 1,\n circular: false,\n width: 200,\n height: 200,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: File | null]\n error: [message: string]\n}>()\n\n// ---------------------------------------------------------------------------\n// Preview\n// ---------------------------------------------------------------------------\nconst objectUrl = ref<string | null>(null)\nwatch(\n () => props.modelValue,\n (v) => {\n if (objectUrl.value) {\n URL.revokeObjectURL(objectUrl.value)\n objectUrl.value = null\n }\n if (v instanceof File) objectUrl.value = URL.createObjectURL(v)\n },\n { immediate: true },\n)\nonBeforeUnmount(() => {\n if (objectUrl.value) URL.revokeObjectURL(objectUrl.value)\n})\n\nconst previewSrc = computed<string | null>(() => {\n if (props.modelValue instanceof File) return objectUrl.value\n if (typeof props.modelValue === 'string' && props.modelValue) return props.modelValue\n return null\n})\n\n// ---------------------------------------------------------------------------\n// File picking + validation\n// ---------------------------------------------------------------------------\nconst fileInput = ref<HTMLInputElement | null>(null)\nfunction openPicker() {\n fileInput.value?.click()\n}\n\nfunction acceptsFile(file: File): boolean {\n const acc = (props.accept || '').trim()\n if (!acc || acc === '*' || acc.includes('image/*')) return file.type.startsWith('image/')\n return acc\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .some((t) => {\n if (t.endsWith('/*')) return file.type.startsWith(t.slice(0, -1))\n if (t.startsWith('.')) return file.name.toLowerCase().endsWith(t.toLowerCase())\n return file.type === t\n })\n}\n\nlet originalName = 'image.png'\nlet originalType = 'image/png'\nlet cropImage: HTMLImageElement | null = null\n\nfunction onFileChange(e: Event) {\n const input = e.target as HTMLInputElement\n const file = input.files?.[0]\n input.value = '' // permite re-selecionar o mesmo arquivo\n if (!file) return\n if (!acceptsFile(file)) {\n emit('error', 'Tipo de arquivo não suportado.')\n return\n }\n if (file.size > props.maxSize) {\n emit('error', `Imagem acima do limite de ${(props.maxSize / 1_048_576).toFixed(1)}MB.`)\n return\n }\n originalName = file.name\n originalType = file.type || 'image/png'\n const reader = new FileReader()\n reader.onload = () => {\n const img = new Image()\n img.onload = () => {\n cropImage = img\n naturalW.value = img.naturalWidth\n naturalH.value = img.naturalHeight\n zoom.value = 1\n imgSrc.value = img.src\n offset.x = (vpW.value - dispW.value) / 2\n offset.y = (vpH.value - dispH.value) / 2\n cropOpen.value = true\n }\n img.onerror = () => emit('error', 'Não foi possível carregar a imagem.')\n img.src = String(reader.result)\n }\n reader.onerror = () => emit('error', 'Falha ao ler o arquivo.')\n reader.readAsDataURL(file)\n}\n\nfunction remove() {\n emit('update:modelValue', null)\n}\n\n// ---------------------------------------------------------------------------\n// Crop stage (pan + zoom)\n// ---------------------------------------------------------------------------\nconst VP = 300 // maior lado do viewport de recorte, em px de tela\nconst cropOpen = ref(false)\nconst imgSrc = ref('')\nconst naturalW = ref(0)\nconst naturalH = ref(0)\nconst zoom = ref(1)\nconst offset = reactive({ x: 0, y: 0 })\n\nconst effAspect = computed(() => (props.circular ? 1 : props.aspectRatio || 1))\nconst vpW = computed(() => (effAspect.value >= 1 ? VP : Math.round(VP * effAspect.value)))\nconst vpH = computed(() => (effAspect.value >= 1 ? Math.round(VP / effAspect.value) : VP))\nconst baseScale = computed(() =>\n naturalW.value && naturalH.value\n ? Math.max(vpW.value / naturalW.value, vpH.value / naturalH.value)\n : 1,\n)\nconst dispScale = computed(() => baseScale.value * zoom.value)\nconst dispW = computed(() => naturalW.value * dispScale.value)\nconst dispH = computed(() => naturalH.value * dispScale.value)\n\nfunction clampOffset() {\n offset.x = Math.min(0, Math.max(vpW.value - dispW.value, offset.x))\n offset.y = Math.min(0, Math.max(vpH.value - dispH.value, offset.y))\n}\nwatch(zoom, clampOffset)\n\nconst imgStyle = computed(() => ({\n width: `${dispW.value}px`,\n height: `${dispH.value}px`,\n transform: `translate(${offset.x}px, ${offset.y}px)`,\n}))\n\n// drag to pan\nconst dragging = ref(false)\nlet dragStart = { x: 0, y: 0, ox: 0, oy: 0 }\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId)\n dragStart = { x: e.clientX, y: e.clientY, ox: offset.x, oy: offset.y }\n}\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return\n offset.x = dragStart.ox + (e.clientX - dragStart.x)\n offset.y = dragStart.oy + (e.clientY - dragStart.y)\n clampOffset()\n}\nfunction onPointerUp() {\n dragging.value = false\n}\n\nfunction outputType(): string {\n if (props.circular) return 'image/png'\n return originalType === 'image/jpeg' ||\n originalType === 'image/png' ||\n originalType === 'image/webp'\n ? originalType\n : 'image/png'\n}\nfunction outputName(type: string): string {\n const ext = type === 'image/png' ? 'png' : type === 'image/webp' ? 'webp' : 'jpg'\n return `${originalName.replace(/\\.[^.]+$/, '') || 'image'}.${ext}`\n}\n\nfunction confirmCrop() {\n if (!cropImage) return\n const scale = dispScale.value\n const sx = -offset.x / scale\n const sy = -offset.y / scale\n const sw = vpW.value / scale\n const sh = vpH.value / scale\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(sw))\n canvas.height = Math.max(1, Math.round(sh))\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n emit('error', 'Canvas indisponível neste ambiente.')\n return\n }\n if (props.circular) {\n ctx.beginPath()\n ctx.ellipse(\n canvas.width / 2,\n canvas.height / 2,\n canvas.width / 2,\n canvas.height / 2,\n 0,\n 0,\n Math.PI * 2,\n )\n ctx.clip()\n }\n ctx.drawImage(cropImage, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height)\n const type = outputType()\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n emit('error', 'Falha ao processar a imagem.')\n return\n }\n emit('update:modelValue', new File([blob], outputName(type), { type }))\n cropOpen.value = false\n },\n type,\n 0.92,\n )\n}\n</script>\n\n<template>\n <div class=\"w-imgcropper\">\n <div\n class=\"w-imgcropper-preview\"\n :class=\"{ 'is-circular': circular, 'is-empty': !previewSrc }\"\n :style=\"{ width: `${width}px`, height: `${height}px` }\"\n >\n <template v-if=\"previewSrc\">\n <img :src=\"previewSrc\" alt=\"\" class=\"w-imgcropper-img\" />\n <div class=\"w-imgcropper-actions\">\n <Button\n type=\"button\"\n icon=\"pi pi-pencil\"\n rounded\n text\n aria-label=\"Trocar imagem\"\n @click=\"openPicker\"\n />\n <Button\n type=\"button\"\n icon=\"pi pi-trash\"\n rounded\n text\n severity=\"danger\"\n aria-label=\"Remover imagem\"\n @click=\"remove\"\n />\n </div>\n </template>\n <slot v-else name=\"placeholder\" :open=\"openPicker\">\n <button type=\"button\" class=\"w-imgcropper-empty\" @click=\"openPicker\">\n <i class=\"pi pi-image\" />\n <span>Selecionar imagem</span>\n </button>\n </slot>\n </div>\n\n <input\n ref=\"fileInput\"\n type=\"file\"\n :accept=\"accept\"\n class=\"w-imgcropper-file\"\n @change=\"onFileChange\"\n />\n\n <Dialog\n v-model:visible=\"cropOpen\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n modal\n header=\"Ajustar imagem\"\n :dismissable-mask=\"false\"\n :style=\"{ width: 'auto' }\"\n >\n <div class=\"w-imgcropper-stage\">\n <div\n class=\"w-imgcropper-viewport\"\n :class=\"{ 'is-circular': circular }\"\n :style=\"{ width: `${vpW}px`, height: `${vpH}px` }\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointerleave=\"onPointerUp\"\n >\n <img\n v-if=\"imgSrc\"\n :src=\"imgSrc\"\n alt=\"\"\n class=\"w-imgcropper-cropimg\"\n :style=\"imgStyle\"\n draggable=\"false\"\n />\n <div class=\"w-imgcropper-stencil\" :class=\"{ 'is-circular': circular }\"></div>\n </div>\n <div class=\"w-imgcropper-zoom\">\n <i class=\"pi pi-search-minus\" />\n <Slider v-model=\"zoom\" :min=\"1\" :max=\"4\" :step=\"0.01\" class=\"w-imgcropper-slider\" />\n <i class=\"pi pi-search-plus\" />\n </div>\n </div>\n <template #footer>\n <Button type=\"button\" label=\"Cancelar\" text @click=\"cropOpen = false\" />\n <Button type=\"button\" label=\"Confirmar\" @click=\"confirmCrop\" />\n </template>\n </Dialog>\n </div>\n</template>\n\n<style scoped>\n.w-imgcropper-preview {\n position: relative;\n overflow: hidden;\n border-radius: 8px;\n border: 1px solid var(--p-content-border-color, #e2e8f0);\n background: var(--p-content-background, #f8fafc);\n}\n.w-imgcropper-preview.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.w-imgcropper-actions {\n position: absolute;\n inset: auto 0 0 0;\n display: flex;\n justify-content: center;\n gap: 4px;\n padding: 4px;\n background: color-mix(in srgb, #000 45%, transparent);\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n.w-imgcropper-preview:hover .w-imgcropper-actions,\n.w-imgcropper-preview:focus-within .w-imgcropper-actions {\n opacity: 1;\n}\n.w-imgcropper-empty {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--p-text-muted-color, #64748b);\n font-size: 0.85rem;\n}\n.w-imgcropper-empty i {\n font-size: 1.5rem;\n}\n.w-imgcropper-file {\n display: none;\n}\n.w-imgcropper-stage {\n display: flex;\n flex-direction: column;\n gap: 16px;\n align-items: center;\n}\n.w-imgcropper-viewport {\n position: relative;\n overflow: hidden;\n touch-action: none;\n cursor: grab;\n background: #1e1e1e;\n border-radius: 4px;\n}\n.w-imgcropper-viewport:active {\n cursor: grabbing;\n}\n.w-imgcropper-viewport.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-cropimg {\n position: absolute;\n top: 0;\n left: 0;\n max-width: none;\n user-select: none;\n -webkit-user-drag: none;\n}\n.w-imgcropper-stencil {\n position: absolute;\n inset: 0;\n pointer-events: none;\n box-shadow: 0 0 0 1px color-mix(in srgb, #fff 60%, transparent) inset;\n}\n.w-imgcropper-stencil.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-zoom {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n color: var(--p-text-muted-color, #64748b);\n}\n.w-imgcropper-slider {\n flex: 1;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ref, reactive, computed, watch, onBeforeUnmount } from 'vue'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport Button from 'primevue/button'\nimport Slider from 'primevue/slider'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\n/**\n * Upload de imagem com preview e crop (pan + zoom) integrado.\n * Sem dependência externa — o recorte é feito via <canvas>.\n * Emite um `File` recortado (ou `null`), consumível direto pelo\n * `useCrudManager`, que detecta campos de imagem e envia como multipart.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: File | string | null\n accept?: string\n /** Tamanho máximo em bytes (default 5MB). */\n maxSize?: number\n /** Proporção do recorte (1 = quadrado). Ignorado quando `circular`. */\n aspectRatio?: number\n /** Stencil circular (avatar). Força proporção 1 e saída PNG com alpha. */\n circular?: boolean\n /** Dimensões do preview em px. */\n width?: number\n height?: number\n }>(),\n {\n modelValue: null,\n accept: 'image/*',\n maxSize: 5_242_880,\n aspectRatio: 1,\n circular: false,\n width: 200,\n height: 200,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: File | null]\n error: [message: string]\n}>()\n\n// ---------------------------------------------------------------------------\n// Preview\n// ---------------------------------------------------------------------------\nconst objectUrl = ref<string | null>(null)\nwatch(\n () => props.modelValue,\n (v) => {\n if (objectUrl.value) {\n URL.revokeObjectURL(objectUrl.value)\n objectUrl.value = null\n }\n if (v instanceof File) objectUrl.value = URL.createObjectURL(v)\n },\n { immediate: true },\n)\nonBeforeUnmount(() => {\n if (objectUrl.value) URL.revokeObjectURL(objectUrl.value)\n})\n\nconst previewSrc = computed<string | null>(() => {\n if (props.modelValue instanceof File) return objectUrl.value\n if (typeof props.modelValue === 'string' && props.modelValue) return props.modelValue\n return null\n})\n\n// ---------------------------------------------------------------------------\n// File picking + validation\n// ---------------------------------------------------------------------------\nconst fileInput = ref<HTMLInputElement | null>(null)\nfunction openPicker() {\n fileInput.value?.click()\n}\n\nfunction acceptsFile(file: File): boolean {\n const acc = (props.accept || '').trim()\n if (!acc || acc === '*' || acc.includes('image/*')) return file.type.startsWith('image/')\n return acc\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n .some((t) => {\n if (t.endsWith('/*')) return file.type.startsWith(t.slice(0, -1))\n if (t.startsWith('.')) return file.name.toLowerCase().endsWith(t.toLowerCase())\n return file.type === t\n })\n}\n\nlet originalName = 'image.png'\nlet originalType = 'image/png'\nlet cropImage: HTMLImageElement | null = null\n\nfunction onFileChange(e: Event) {\n const input = e.target as HTMLInputElement\n const file = input.files?.[0]\n input.value = '' // permite re-selecionar o mesmo arquivo\n if (!file) return\n if (!acceptsFile(file)) {\n emit('error', 'Tipo de arquivo não suportado.')\n return\n }\n if (file.size > props.maxSize) {\n emit('error', `Imagem acima do limite de ${(props.maxSize / 1_048_576).toFixed(1)}MB.`)\n return\n }\n originalName = file.name\n originalType = file.type || 'image/png'\n const reader = new FileReader()\n reader.onload = () => {\n const img = new Image()\n img.onload = () => {\n cropImage = img\n naturalW.value = img.naturalWidth\n naturalH.value = img.naturalHeight\n zoom.value = 1\n imgSrc.value = img.src\n offset.x = (vpW.value - dispW.value) / 2\n offset.y = (vpH.value - dispH.value) / 2\n cropOpen.value = true\n }\n img.onerror = () => emit('error', 'Não foi possível carregar a imagem.')\n img.src = String(reader.result)\n }\n reader.onerror = () => emit('error', 'Falha ao ler o arquivo.')\n reader.readAsDataURL(file)\n}\n\nfunction remove() {\n emit('update:modelValue', null)\n}\n\n// ---------------------------------------------------------------------------\n// Crop stage (pan + zoom)\n// ---------------------------------------------------------------------------\nconst VP = 300 // maior lado do viewport de recorte, em px de tela\nconst cropOpen = ref(false)\nconst imgSrc = ref('')\nconst naturalW = ref(0)\nconst naturalH = ref(0)\nconst zoom = ref(1)\nconst offset = reactive({ x: 0, y: 0 })\n\nconst effAspect = computed(() => (props.circular ? 1 : props.aspectRatio || 1))\nconst vpW = computed(() => (effAspect.value >= 1 ? VP : Math.round(VP * effAspect.value)))\nconst vpH = computed(() => (effAspect.value >= 1 ? Math.round(VP / effAspect.value) : VP))\nconst baseScale = computed(() =>\n naturalW.value && naturalH.value\n ? Math.max(vpW.value / naturalW.value, vpH.value / naturalH.value)\n : 1,\n)\nconst dispScale = computed(() => baseScale.value * zoom.value)\nconst dispW = computed(() => naturalW.value * dispScale.value)\nconst dispH = computed(() => naturalH.value * dispScale.value)\n\nfunction clampOffset() {\n offset.x = Math.min(0, Math.max(vpW.value - dispW.value, offset.x))\n offset.y = Math.min(0, Math.max(vpH.value - dispH.value, offset.y))\n}\nwatch(zoom, clampOffset)\n\nconst imgStyle = computed(() => ({\n width: `${dispW.value}px`,\n height: `${dispH.value}px`,\n transform: `translate(${offset.x}px, ${offset.y}px)`,\n}))\n\n// drag to pan\nconst dragging = ref(false)\nlet dragStart = { x: 0, y: 0, ox: 0, oy: 0 }\nfunction onPointerDown(e: PointerEvent) {\n dragging.value = true\n ;(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId)\n dragStart = { x: e.clientX, y: e.clientY, ox: offset.x, oy: offset.y }\n}\nfunction onPointerMove(e: PointerEvent) {\n if (!dragging.value) return\n offset.x = dragStart.ox + (e.clientX - dragStart.x)\n offset.y = dragStart.oy + (e.clientY - dragStart.y)\n clampOffset()\n}\nfunction onPointerUp() {\n dragging.value = false\n}\n\nfunction outputType(): string {\n if (props.circular) return 'image/png'\n return originalType === 'image/jpeg' ||\n originalType === 'image/png' ||\n originalType === 'image/webp'\n ? originalType\n : 'image/png'\n}\nfunction outputName(type: string): string {\n const ext = type === 'image/png' ? 'png' : type === 'image/webp' ? 'webp' : 'jpg'\n return `${originalName.replace(/\\.[^.]+$/, '') || 'image'}.${ext}`\n}\n\nfunction confirmCrop() {\n if (!cropImage) return\n const scale = dispScale.value\n const sx = -offset.x / scale\n const sy = -offset.y / scale\n const sw = vpW.value / scale\n const sh = vpH.value / scale\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(sw))\n canvas.height = Math.max(1, Math.round(sh))\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n emit('error', 'Canvas indisponível neste ambiente.')\n return\n }\n if (props.circular) {\n ctx.beginPath()\n ctx.ellipse(\n canvas.width / 2,\n canvas.height / 2,\n canvas.width / 2,\n canvas.height / 2,\n 0,\n 0,\n Math.PI * 2,\n )\n ctx.clip()\n }\n ctx.drawImage(cropImage, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height)\n const type = outputType()\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n emit('error', 'Falha ao processar a imagem.')\n return\n }\n emit('update:modelValue', new File([blob], outputName(type), { type }))\n cropOpen.value = false\n },\n type,\n 0.92,\n )\n}\n</script>\n\n<template>\n <div class=\"w-imgcropper\">\n <div\n class=\"w-imgcropper-preview\"\n :class=\"{ 'is-circular': circular, 'is-empty': !previewSrc }\"\n :style=\"{ width: `${width}px`, height: `${height}px` }\"\n >\n <template v-if=\"previewSrc\">\n <img :src=\"previewSrc\" alt=\"\" class=\"w-imgcropper-img\" />\n <div class=\"w-imgcropper-actions\">\n <Button\n type=\"button\"\n icon=\"pi pi-pencil\"\n rounded\n text\n aria-label=\"Trocar imagem\"\n @click=\"openPicker\"\n />\n <Button\n type=\"button\"\n icon=\"pi pi-trash\"\n rounded\n text\n severity=\"danger\"\n aria-label=\"Remover imagem\"\n @click=\"remove\"\n />\n </div>\n </template>\n <slot v-else name=\"placeholder\" :open=\"openPicker\">\n <button type=\"button\" class=\"w-imgcropper-empty\" @click=\"openPicker\">\n <i class=\"pi pi-image\" />\n <span>Selecionar imagem</span>\n </button>\n </slot>\n </div>\n\n <input\n ref=\"fileInput\"\n type=\"file\"\n :accept=\"accept\"\n class=\"w-imgcropper-file\"\n @change=\"onFileChange\"\n />\n\n <Dialog\n v-model:visible=\"cropOpen\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n modal\n header=\"Ajustar imagem\"\n :dismissable-mask=\"false\"\n :style=\"{ width: 'auto' }\"\n >\n <div class=\"w-imgcropper-stage\">\n <div\n class=\"w-imgcropper-viewport\"\n :class=\"{ 'is-circular': circular }\"\n :style=\"{ width: `${vpW}px`, height: `${vpH}px` }\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointerleave=\"onPointerUp\"\n >\n <img\n v-if=\"imgSrc\"\n :src=\"imgSrc\"\n alt=\"\"\n class=\"w-imgcropper-cropimg\"\n :style=\"imgStyle\"\n draggable=\"false\"\n />\n <div class=\"w-imgcropper-stencil\" :class=\"{ 'is-circular': circular }\"></div>\n </div>\n <div class=\"w-imgcropper-zoom\">\n <i class=\"pi pi-search-minus\" />\n <Slider v-model=\"zoom\" :min=\"1\" :max=\"4\" :step=\"0.01\" class=\"w-imgcropper-slider\" />\n <i class=\"pi pi-search-plus\" />\n </div>\n </div>\n <template #footer>\n <Button type=\"button\" label=\"Cancelar\" text @click=\"cropOpen = false\" />\n <Button type=\"button\" label=\"Confirmar\" @click=\"confirmCrop\" />\n </template>\n </Dialog>\n </div>\n</template>\n\n<style scoped>\n.w-imgcropper-preview {\n position: relative;\n overflow: hidden;\n border-radius: 8px;\n border: 1px solid var(--p-content-border-color, #e2e8f0);\n background: var(--p-content-background, #f8fafc);\n}\n.w-imgcropper-preview.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.w-imgcropper-actions {\n position: absolute;\n inset: auto 0 0 0;\n display: flex;\n justify-content: center;\n gap: 4px;\n padding: 4px;\n background: color-mix(in srgb, #000 45%, transparent);\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n.w-imgcropper-preview:hover .w-imgcropper-actions,\n.w-imgcropper-preview:focus-within .w-imgcropper-actions {\n opacity: 1;\n}\n.w-imgcropper-empty {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--p-text-muted-color, #64748b);\n font-size: 0.85rem;\n}\n.w-imgcropper-empty i {\n font-size: 1.5rem;\n}\n.w-imgcropper-file {\n display: none;\n}\n.w-imgcropper-stage {\n display: flex;\n flex-direction: column;\n gap: 16px;\n align-items: center;\n}\n.w-imgcropper-viewport {\n position: relative;\n overflow: hidden;\n touch-action: none;\n cursor: grab;\n background: #1e1e1e;\n border-radius: 4px;\n}\n.w-imgcropper-viewport:active {\n cursor: grabbing;\n}\n.w-imgcropper-viewport.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-cropimg {\n position: absolute;\n top: 0;\n left: 0;\n max-width: none;\n user-select: none;\n -webkit-user-drag: none;\n}\n.w-imgcropper-stencil {\n position: absolute;\n inset: 0;\n pointer-events: none;\n box-shadow: 0 0 0 1px color-mix(in srgb, #fff 60%, transparent) inset;\n}\n.w-imgcropper-stencil.is-circular {\n border-radius: 50%;\n}\n.w-imgcropper-zoom {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n color: var(--p-text-muted-color, #64748b);\n}\n.w-imgcropper-slider {\n flex: 1;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Button from 'primevue/button'\n\ntype Id = string | number\ntype Item = Record<string, unknown>\n\nconst props = withDefaults(\n defineProps<{\n /** Full pool of available records. */\n source: Item[]\n /** Currently selected ids (v-model:selected). */\n selected: Id[]\n /** Field used as the record id (default: 'id'). */\n trackBy?: string\n /** Field used for the visible label (default: 'nome'). */\n optionLabel?: string\n /** Fields to match when searching (default: [optionLabel]). */\n searchFields?: string[]\n disabled?: boolean\n }>(),\n {\n trackBy: 'id',\n optionLabel: 'nome',\n selected: () => [],\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [ids: Id[]]\n}>()\n\nconst leftSearch = ref('')\nconst rightSearch = ref('')\n\nconst selectedSet = computed(() => new Set(props.selected))\nconst fields = computed(() => props.searchFields ?? [props.optionLabel])\n\nfunction idOf(item: Item): Id {\n return item[props.trackBy] as Id\n}\n\nfunction labelOf(item: Item): string {\n return String(item[props.optionLabel] ?? '')\n}\n\nfunction matches(item: Item, term: string): boolean {\n if (!term) return true\n const q = term.toLowerCase()\n return fields.value.some((f) =>\n String(item[f] ?? '')\n .toLowerCase()\n .includes(q),\n )\n}\n\nconst available = computed(() =>\n props.source.filter((i) => !selectedSet.value.has(idOf(i)) && matches(i, leftSearch.value)),\n)\n\nconst chosen = computed(() =>\n props.source.filter((i) => selectedSet.value.has(idOf(i)) && matches(i, rightSearch.value)),\n)\n\nfunction add(item: Item) {\n if (props.disabled) return\n emit('update:selected', [...props.selected, idOf(item)])\n}\n\nfunction remove(item: Item) {\n if (props.disabled) return\n const id = idOf(item)\n emit(\n 'update:selected',\n props.selected.filter((s) => s !== id),\n )\n}\n\nfunction addAll() {\n if (props.disabled) return\n emit('update:selected', props.source.map(idOf))\n}\n\nfunction removeAll() {\n if (props.disabled) return\n emit('update:selected', [])\n}\n</script>\n\n<template>\n <div class=\"w-transfer\" :class=\"{ 'w-transfer--disabled': disabled }\">\n <!-- Available -->\n <div class=\"w-transfer__pane\">\n <div class=\"w-transfer__head\">\n <span class=\"w-transfer__title\">Disponíveis</span>\n <span class=\"w-transfer__count\">{{ available.length }}</span>\n </div>\n <IconField class=\"w-transfer__search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText v-model=\"leftSearch\" placeholder=\"Buscar...\" fluid />\n </IconField>\n <ul class=\"w-transfer__list\">\n <li\n v-for=\"item in available\"\n :key=\"`a-${idOf(item)}`\"\n class=\"w-transfer__item\"\n @click=\"add(item)\"\n >\n <span>{{ labelOf(item) }}</span>\n <i class=\"pi pi-angle-right\" />\n </li>\n <li v-if=\"!available.length\" class=\"w-transfer__empty\">Nenhum item</li>\n </ul>\n </div>\n\n <!-- Controls -->\n <div class=\"w-transfer__controls\">\n <Button\n type=\"button\"\n icon=\"pi pi-angle-double-right\"\n text\n rounded\n :disabled=\"disabled || !available.length\"\n @click=\"addAll\"\n />\n <Button\n type=\"button\"\n icon=\"pi pi-angle-double-left\"\n text\n rounded\n :disabled=\"disabled || !selected.length\"\n @click=\"removeAll\"\n />\n </div>\n\n <!-- Selected -->\n <div class=\"w-transfer__pane\">\n <div class=\"w-transfer__head\">\n <span class=\"w-transfer__title\">Selecionados</span>\n <span class=\"w-transfer__count\">{{ chosen.length }}</span>\n </div>\n <IconField class=\"w-transfer__search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText v-model=\"rightSearch\" placeholder=\"Buscar...\" fluid />\n </IconField>\n <ul class=\"w-transfer__list\">\n <li\n v-for=\"item in chosen\"\n :key=\"`s-${idOf(item)}`\"\n class=\"w-transfer__item\"\n @click=\"remove(item)\"\n >\n <i class=\"pi pi-angle-left\" />\n <span>{{ labelOf(item) }}</span>\n </li>\n <li v-if=\"!chosen.length\" class=\"w-transfer__empty\">Nenhum item</li>\n </ul>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Button from 'primevue/button'\n\ntype Id = string | number\ntype Item = Record<string, unknown>\n\nconst props = withDefaults(\n defineProps<{\n /** Full pool of available records. */\n source: Item[]\n /** Currently selected ids (v-model:selected). */\n selected: Id[]\n /** Field used as the record id (default: 'id'). */\n trackBy?: string\n /** Field used for the visible label (default: 'nome'). */\n optionLabel?: string\n /** Fields to match when searching (default: [optionLabel]). */\n searchFields?: string[]\n disabled?: boolean\n }>(),\n {\n trackBy: 'id',\n optionLabel: 'nome',\n selected: () => [],\n },\n)\n\nconst emit = defineEmits<{\n 'update:selected': [ids: Id[]]\n}>()\n\nconst leftSearch = ref('')\nconst rightSearch = ref('')\n\nconst selectedSet = computed(() => new Set(props.selected))\nconst fields = computed(() => props.searchFields ?? [props.optionLabel])\n\nfunction idOf(item: Item): Id {\n return item[props.trackBy] as Id\n}\n\nfunction labelOf(item: Item): string {\n return String(item[props.optionLabel] ?? '')\n}\n\nfunction matches(item: Item, term: string): boolean {\n if (!term) return true\n const q = term.toLowerCase()\n return fields.value.some((f) =>\n String(item[f] ?? '')\n .toLowerCase()\n .includes(q),\n )\n}\n\nconst available = computed(() =>\n props.source.filter((i) => !selectedSet.value.has(idOf(i)) && matches(i, leftSearch.value)),\n)\n\nconst chosen = computed(() =>\n props.source.filter((i) => selectedSet.value.has(idOf(i)) && matches(i, rightSearch.value)),\n)\n\nfunction add(item: Item) {\n if (props.disabled) return\n emit('update:selected', [...props.selected, idOf(item)])\n}\n\nfunction remove(item: Item) {\n if (props.disabled) return\n const id = idOf(item)\n emit(\n 'update:selected',\n props.selected.filter((s) => s !== id),\n )\n}\n\nfunction addAll() {\n if (props.disabled) return\n emit('update:selected', props.source.map(idOf))\n}\n\nfunction removeAll() {\n if (props.disabled) return\n emit('update:selected', [])\n}\n</script>\n\n<template>\n <div class=\"w-transfer\" :class=\"{ 'w-transfer--disabled': disabled }\">\n <!-- Available -->\n <div class=\"w-transfer__pane\">\n <div class=\"w-transfer__head\">\n <span class=\"w-transfer__title\">Disponíveis</span>\n <span class=\"w-transfer__count\">{{ available.length }}</span>\n </div>\n <IconField class=\"w-transfer__search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText v-model=\"leftSearch\" placeholder=\"Buscar...\" fluid />\n </IconField>\n <ul class=\"w-transfer__list\">\n <li\n v-for=\"item in available\"\n :key=\"`a-${idOf(item)}`\"\n class=\"w-transfer__item\"\n @click=\"add(item)\"\n >\n <span>{{ labelOf(item) }}</span>\n <i class=\"pi pi-angle-right\" />\n </li>\n <li v-if=\"!available.length\" class=\"w-transfer__empty\">Nenhum item</li>\n </ul>\n </div>\n\n <!-- Controls -->\n <div class=\"w-transfer__controls\">\n <Button\n type=\"button\"\n icon=\"pi pi-angle-double-right\"\n text\n rounded\n :disabled=\"disabled || !available.length\"\n @click=\"addAll\"\n />\n <Button\n type=\"button\"\n icon=\"pi pi-angle-double-left\"\n text\n rounded\n :disabled=\"disabled || !selected.length\"\n @click=\"removeAll\"\n />\n </div>\n\n <!-- Selected -->\n <div class=\"w-transfer__pane\">\n <div class=\"w-transfer__head\">\n <span class=\"w-transfer__title\">Selecionados</span>\n <span class=\"w-transfer__count\">{{ chosen.length }}</span>\n </div>\n <IconField class=\"w-transfer__search\">\n <InputIcon class=\"pi pi-search\" />\n <InputText v-model=\"rightSearch\" placeholder=\"Buscar...\" fluid />\n </IconField>\n <ul class=\"w-transfer__list\">\n <li\n v-for=\"item in chosen\"\n :key=\"`s-${idOf(item)}`\"\n class=\"w-transfer__item\"\n @click=\"remove(item)\"\n >\n <i class=\"pi pi-angle-left\" />\n <span>{{ labelOf(item) }}</span>\n </li>\n <li v-if=\"!chosen.length\" class=\"w-transfer__empty\">Nenhum item</li>\n </ul>\n </div>\n </div>\n</template>\n","export interface ViaCepResponse {\n cep: string\n logradouro: string\n complemento: string\n bairro: string\n localidade: string\n uf: string\n ibge: string\n gia: string\n ddd: string\n siafi: string\n erro?: true\n}\n\nexport async function lookupCep(cep: string): Promise<ViaCepResponse | null> {\n const digits = cep.replace(/\\D/g, '')\n if (digits.length !== 8) return null\n try {\n const response = await fetch(`https://viacep.com.br/ws/${digits}/json/`)\n if (!response.ok) return null\n const data = (await response.json()) as ViaCepResponse\n if (data.erro) return null\n return data\n } catch {\n return null\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, reactive, isRef, watch } from 'vue'\nimport { vMaska } from 'maska/vue'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Textarea from 'primevue/textarea'\nimport Select from 'primevue/select'\nimport AutoComplete from 'primevue/autocomplete'\nimport ToggleSwitch from 'primevue/toggleswitch'\nimport ColorPicker from 'primevue/colorpicker'\nimport Password from 'primevue/password'\nimport type { FieldDef, FieldSubRowsFetch } from '@/types/field'\nimport WAutoCompleteFK from '@/components/form/WAutoCompleteFK.vue'\nimport WDatePicker from '@/components/form/WDatePicker.vue'\nimport WMoneyInput from '@/components/form/WMoneyInput.vue'\nimport WImageCropper from '@/components/form/WImageCropper.vue'\nimport WTransferList from '@/components/form/WTransferList.vue'\nimport { lookupCep } from '@/utils/cep'\n\nconst props = withDefaults(\n defineProps<{\n fields: FieldDef[]\n formData: Record<string, unknown>\n isEditing: boolean\n disabled?: boolean\n /** Nº de colunas do grid (default 2). Campos ocupam frações via `colSpan`;\n * grupos podem sobrepor via `fieldGroup.columns`. */\n columns?: number\n }>(),\n {\n disabled: false,\n columns: 2,\n },\n)\n\nconst emit = defineEmits<{\n 'update:field': [field: string, value: unknown]\n}>()\n\nconst fieldErrors = reactive<Record<string, string | null>>({})\n\n// --- CEP state ---\nconst cepLoading = reactive<Record<string, boolean>>({})\nconst cepError = reactive<Record<string, string | null>>({})\nconst cepTimers = reactive<Record<string, ReturnType<typeof setTimeout> | null>>({})\n\nfunction onCepInput(field: FieldDef, event: Event) {\n const raw = (event.target as HTMLInputElement).value\n const digits = raw.replace(/\\D/g, '')\n emit('update:field', field.field, raw)\n\n // Clear previous error\n cepError[field.field] = null\n\n // Clear pending timer\n if (cepTimers[field.field]) {\n clearTimeout(cepTimers[field.field]!)\n cepTimers[field.field] = null\n }\n\n if (digits.length === 8) {\n cepTimers[field.field] = setTimeout(async () => {\n cepLoading[field.field] = true\n cepError[field.field] = null\n try {\n const result = await lookupCep(digits)\n if (!result) {\n cepError[field.field] = 'CEP não encontrado. Preencha os campos manualmente.'\n } else {\n // Fill mapped fields, but only when currently empty\n const mapping = field.cepFields || {}\n const keys = Object.keys(mapping) as Array<keyof typeof mapping>\n for (const key of keys) {\n const targetField = mapping[key]\n if (!targetField) continue\n const existing = props.formData[targetField]\n if (existing == null || existing === '') {\n emit('update:field', targetField, result[key] ?? '')\n }\n }\n }\n } finally {\n cepLoading[field.field] = false\n }\n }, 400)\n }\n}\n\n// --- Visibility & Disabled ---\n\nconst visibleFields = computed(() =>\n props.fields.filter((f) => {\n if (f.visible === undefined || f.visible === true) return true\n if (typeof f.visible === 'function') return f.visible(props.formData, props.isEditing)\n return f.visible\n }),\n)\n\nfunction isFieldDisabled(field: FieldDef): boolean {\n if (props.disabled) return true\n // Somente-leitura / calculado → sempre desabilitado (o valor ainda vai no payload).\n if (field.readonly || field.computed) return true\n if (field.disabledOnEdit && props.isEditing) return true\n if (typeof field.disabled === 'function') return field.disabled(props.formData, props.isEditing)\n return !!field.disabled\n}\n\n/** Indireção tipada do `subRows`: devolver a função direto no template fazia o\n * vue-tsc entrar no ciclo WFormRenderer → WAutoCompleteFK → WCrudFormDialog e\n * degradar tudo para `any`. */\nfunction subRowsFetchDe(field: FieldDef): FieldSubRowsFetch | undefined {\n return field.subRows\n}\n\n/** Resolve `endpointParams` estático ou por função (contexto do pai). */\nfunction resolveEndpointParams(field: FieldDef) {\n const ep = field.endpointParams\n return typeof ep === 'function' ? ep() : ep\n}\n\n// --- Campos calculados ---\n// Derivam o valor dos demais campos e o escrevem de volta no formData (para irem no\n// payload). O SOURCE do watch chama `calculate` — assim as dependências reativas exatas\n// (ex.: `formData.produto.unidade_medida`) são coletadas e o watch dispara quando elas\n// mudam. (Um `watch(() => formData, …, { deep })` não reage de forma confiável ao\n// `formData` que chega por prop no fluxo do WCrudFormDialog.) Só emite quando o valor\n// muda de fato (o `!==` evita laço). A função `calculate` deve cair no valor atual quando\n// não puder derivar (ex.: na edição, com a FK já como id).\nconst calculatedFields = computed(() =>\n props.fields.filter((f) => typeof f.calculate === 'function'),\n)\n\nwatch(\n () => calculatedFields.value.map((f) => f.calculate!(props.formData, props.isEditing)),\n (nexts) => {\n calculatedFields.value.forEach((f, i) => {\n if (props.formData[f.field] !== nexts[i]) emit('update:field', f.field, nexts[i])\n })\n },\n { immediate: true },\n)\n\nfunction unwrapRef<V>(val: V): V extends import('vue').Ref<infer U> ? U : V {\n return isRef(val) ? ((val as { value: unknown }).value as never) : (val as never)\n}\n\n// --- Autofocus ---\n\nconst autofocusField = computed(() => {\n const mode = props.isEditing ? 'edit' : 'create'\n const explicit = props.fields.find((f) => f.autofocus === true || f.autofocus === mode)\n if (explicit) return explicit.field\n const first = visibleFields.value.find((f) => {\n // Tipos sem campo de digitação não são candidatos a foco inicial.\n const semInput = ['switch', 'fk', 'select', 'image', 'segmented', 'choice', 'chips', 'transfer']\n if (f.type && semInput.includes(f.type)) return false\n if (f.disabled === true) return false\n if (f.disabledOnEdit && props.isEditing) return false\n return true\n })\n return first?.field ?? null\n})\n\nfunction shouldAutofocus(field: FieldDef): boolean {\n return field.field === autofocusField.value\n}\n\n// --- Mask format conversion (PrimeVue InputMask -> maska) ---\n\nfunction convertMask(mask?: string): string | undefined {\n if (!mask) return undefined\n return mask.replace(/9/g, '#').replace(/a/g, 'S').replace(/\\*/g, 'X')\n}\n\n// --- CPF/CNPJ inline mask ---\n\nfunction displayCpfCnpj(value: unknown): string {\n if (!value) return ''\n const digits = String(value).replace(/\\D/g, '').slice(0, 14)\n if (digits.length <= 11) {\n return digits\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d{1,2})$/, '$1-$2')\n }\n return digits\n .replace(/(\\d{2})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1/$2')\n .replace(/(\\d{4})(\\d{1,2})$/, '$1-$2')\n}\n\nfunction onCpfCnpjInput(fieldName: string, event: Event) {\n const raw = (event.target as HTMLInputElement).value.replace(/\\D/g, '').slice(0, 14)\n emit('update:field', fieldName, raw)\n}\n\n// --- AutoComplete local (para type=autocomplete com options) ---\n\nconst autocompleteSuggestions = reactive<Record<string, unknown[]>>({})\n\nfunction getAutocompleteValue(field: FieldDef) {\n const val = props.formData[field.field]\n if (val == null) return null\n const optionValue = field.optionValue || 'value'\n const options = unwrapRef(field.options) || []\n return (options as Record<string, unknown>[]).find((o) => o[optionValue] === val) ?? null\n}\n\nfunction getFilteredSuggestions(field: FieldDef) {\n return autocompleteSuggestions[field.field] || []\n}\n\nfunction onAutocompleteSearch(field: FieldDef, event: { query: string }) {\n const query = (event.query || '').toLowerCase()\n const options = unwrapRef(field.options) || []\n const optionLabel = field.optionLabel || 'label'\n autocompleteSuggestions[field.field] = (options as Record<string, unknown>[]).filter((o) =>\n String(o[optionLabel] || '')\n .toLowerCase()\n .includes(query),\n )\n}\n\nfunction onAutocompleteSelect(field: FieldDef, event: { value: Record<string, unknown> }) {\n const optionValue = field.optionValue || 'value'\n emit('update:field', field.field, event.value[optionValue])\n}\n\n// --- Options (segmented / choice / chips) ---\n// Os três tipos leem a MESMA tripla `options`/`optionLabel`/`optionValue` do\n// select — quem troca um Select por pílulas não reescreve o FieldDef.\n\nfunction fieldOptions(field: FieldDef): Record<string, unknown>[] {\n return (unwrapRef(field.options) || []) as Record<string, unknown>[]\n}\n\nfunction optionLabelOf(field: FieldDef, option: Record<string, unknown>): string {\n return String(option[field.optionLabel || 'label'] ?? '')\n}\n\nfunction optionValueOf(field: FieldDef, option: Record<string, unknown>): unknown {\n return option[field.optionValue || 'value']\n}\n\nfunction isOptionSelected(field: FieldDef, option: Record<string, unknown>): boolean {\n return props.formData[field.field] === optionValueOf(field, option)\n}\n\n/** Escolha única: clicar na opção ativa mantém a escolha (não desmarca). */\nfunction pickOption(field: FieldDef, option: Record<string, unknown>) {\n if (isFieldDisabled(field)) return\n emit('update:field', field.field, optionValueOf(field, option))\n}\n\n// --- Chips (valor múltiplo, removível) ---\n// O valor é um ARRAY. Cada item pode ser o objeto inteiro (usa `optionLabel`) ou\n// só o id (procura o rótulo em `options`; sem match, mostra o próprio valor).\n\ninterface ChipEntry {\n key: string\n label: string\n}\n\nfunction chipEntries(field: FieldDef): ChipEntry[] {\n const raw = props.formData[field.field]\n if (!Array.isArray(raw)) return []\n const labelKey = field.optionLabel || 'nome'\n const valueKey = field.optionValue || 'id'\n const options = fieldOptions(field)\n\n return raw.map((item, index) => {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>\n return { key: String(obj[valueKey] ?? index), label: String(obj[labelKey] ?? '') }\n }\n const match = options.find((o) => o[valueKey] === item)\n return {\n key: String(item ?? index),\n label: match ? String(match[labelKey] ?? item) : String(item),\n }\n })\n}\n\nfunction removeChip(field: FieldDef, index: number) {\n if (isFieldDisabled(field)) return\n const raw = props.formData[field.field]\n if (!Array.isArray(raw)) return\n emit(\n 'update:field',\n field.field,\n raw.filter((_, i) => i !== index),\n )\n}\n\n// --- Color ---\n\nfunction getColorPickerValue(field: FieldDef): string {\n const val = props.formData[field.field]\n return val ? String(val).replace('#', '') : 'FFFFFF'\n}\n\nfunction onColorChange(field: FieldDef, value: string) {\n emit('update:field', field.field, `#${value}`)\n}\n\n// --- Drill-down (filtro em cascata das FKs) ---\n\n/** Resolve o `dependsOn` do campo em filtros já com o valor atual do formulário. */\nfunction resolveDrilldown(field: FieldDef) {\n const dep = field.dependsOn\n if (!dep) return undefined\n const list = Array.isArray(dep) ? dep : [dep]\n return list.map((d) => ({\n field: d.param || d.field,\n value: props.formData[d.field],\n required: d.required,\n }))\n}\n\n// --- Validation ---\n\nfunction validateField(field: FieldDef) {\n if (typeof field.validate === 'function') {\n const error = field.validate(props.formData[field.field])\n fieldErrors[field.field] = error || null\n }\n}\n\nfunction validateAll(): string[] {\n const errors: string[] = []\n for (const field of props.fields) {\n if (typeof field.validate === 'function') {\n const error = field.validate(props.formData[field.field])\n fieldErrors[field.field] = error || null\n if (error) errors.push(error)\n }\n }\n return errors\n}\n\nfunction clearErrors() {\n Object.keys(fieldErrors).forEach((k) => delete fieldErrors[k])\n}\n\n// --- Field Groups ---\n\ninterface FieldGroup {\n id: string\n title?: string\n description?: string\n columns?: number\n fields: FieldDef[]\n}\n\n// --- Grid columns / span ---\n\nfunction groupColumns(group: FieldGroup): number {\n return Math.max(1, Math.floor(group.columns ?? props.columns))\n}\n\n/** Resolve o colSpan do campo em nº de colunas do grid do grupo.\n * - omitido ou 'full' → linha inteira\n * - 0.5 → metade da linha (independe do nº de colunas)\n * - inteiro ≥ 1 → N colunas (limitado ao total) */\nfunction fieldSpan(field: FieldDef, cols: number): number {\n const span = field.colSpan\n if (span == null || span === 'full') return cols\n if (span === 0.5) return Math.max(1, Math.round(cols / 2))\n return Math.min(Math.max(1, Math.floor(span)), cols)\n}\n\nfunction fieldSpanStyle(field: FieldDef, group: FieldGroup) {\n return { '--w-col-span': fieldSpan(field, groupColumns(group)) }\n}\n\nfunction fieldSpanClass(field: FieldDef, group: FieldGroup): string {\n return fieldSpan(field, groupColumns(group)) === groupColumns(group)\n ? 'w-crud-form-col-full'\n : 'w-crud-form-col-half'\n}\n\nconst groupedFields = computed((): FieldGroup[] => {\n const groupMap = new Map<string, FieldGroup>()\n const groupOrder: string[] = []\n const groupExplicitOrder = new Map<string, number>()\n\n for (const field of visibleFields.value) {\n const groupId = field.fieldGroup?.id ?? '__default__'\n\n if (!groupMap.has(groupId)) {\n groupMap.set(groupId, {\n id: groupId,\n title: field.fieldGroup?.title,\n description: field.fieldGroup?.description,\n columns: field.fieldGroup?.columns,\n fields: [],\n })\n groupOrder.push(groupId)\n if (field.fieldGroup?.order != null) {\n groupExplicitOrder.set(groupId, field.fieldGroup.order)\n }\n }\n\n groupMap.get(groupId)!.fields.push(field)\n }\n\n return groupOrder\n .slice()\n .sort((a, b) => {\n const orderA = groupExplicitOrder.get(a)\n const orderB = groupExplicitOrder.get(b)\n if (orderA != null && orderB != null) return orderA - orderB\n if (orderA != null) return -1\n if (orderB != null) return 1\n return groupOrder.indexOf(a) - groupOrder.indexOf(b)\n })\n .map((id) => groupMap.get(id)!)\n})\n\ndefineExpose({ validateAll, clearErrors })\n</script>\n\n<template>\n <div class=\"w-crud-form\">\n <div v-for=\"group in groupedFields\" :key=\"group.id\" class=\"w-crud-form-group\">\n <div v-if=\"group.title\" class=\"w-crud-form-group-header\">\n <h3 class=\"w-crud-form-group-title\">{{ group.title }}</h3>\n <p v-if=\"group.description\" class=\"w-crud-form-group-desc\">{{ group.description }}</p>\n </div>\n <div class=\"w-crud-form-fields\" :style=\"{ '--w-form-cols': groupColumns(group) }\">\n <template v-for=\"field in group.fields\" :key=\"field.field\">\n <slot\n :name=\"`field-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :set-form-field=\"(f: string, v: unknown) => emit('update:field', f, v)\"\n >\n <!-- Switch -->\n <div\n v-if=\"field.type === 'switch'\"\n class=\"w-crud-form-switch\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <ToggleSwitch\n :model-value=\"formData[field.field] as boolean\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n <label class=\"w-crud-form-switch-label\">{{ field.switchLabel || field.label }}</label>\n </div>\n\n <!-- Color -->\n <div\n v-else-if=\"field.type === 'color'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n </label>\n <div class=\"w-crud-form-color-row\">\n <ColorPicker\n :model-value=\"getColorPickerValue(field)\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"onColorChange(field, $event as string)\"\n />\n <InputText\n :model-value=\"formData[field.field] as string\"\n class=\"w-28\"\n maxlength=\"7\"\n placeholder=\"#000000\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n </div>\n </div>\n\n <!-- Image -->\n <div\n v-else-if=\"field.type === 'image'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n </label>\n <slot :name=\"`image-${field.field}`\" :field=\"field\" :form-data=\"formData\">\n <WImageCropper\n :model-value=\"formData[field.field] as File | string | null\"\n :accept=\"field.accept || 'image/*'\"\n @update:model-value=\"(file) => emit('update:field', field.field, file)\"\n @error=\"\n (msg) => {\n fieldErrors[field.field] = msg\n }\n \"\n />\n </slot>\n <small v-if=\"fieldErrors[field.field]\" class=\"w-crud-form-error\">\n {{ fieldErrors[field.field] }}\n </small>\n </div>\n\n <!-- Transfer (dual list) -->\n <div\n v-else-if=\"field.type === 'transfer'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n </label>\n <WTransferList\n :source=\"(unwrapRef(field.options) as any[]) || []\"\n :selected=\"(formData[field.field] as any[]) || []\"\n :track-by=\"field.optionValue || 'id'\"\n :option-label=\"field.optionLabel || 'nome'\"\n :search-fields=\"field.searchFields\"\n :disabled=\"isFieldDisabled(field)\"\n @update:selected=\"(val) => emit('update:field', field.field, val)\"\n />\n </div>\n\n <!-- All other types -->\n <div v-else :class=\"fieldSpanClass(field, group)\" :style=\"fieldSpanStyle(field, group)\">\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n <i\n v-if=\"cepLoading[field.field]\"\n class=\"pi pi-spin pi-spinner w-crud-form-cep-spinner\"\n />\n </label>\n\n <!-- Text with mask (maska) -->\n <InputText\n v-if=\"(!field.type || field.type === 'text') && field.mask\"\n v-maska=\"{ mask: convertMask(field.mask) }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Text -->\n <InputText\n v-else-if=\"!field.type || field.type === 'text'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Email -->\n <InputText\n v-else-if=\"field.type === 'email'\"\n :model-value=\"formData[field.field] as string\"\n type=\"email\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Password -->\n <Password\n v-else-if=\"field.type === 'password'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n toggle-mask\n :feedback=\"field.feedback !== false\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Number -->\n <InputNumber\n v-else-if=\"field.type === 'number'\"\n :model-value=\"formData[field.field] as number\"\n fluid\n locale=\"pt-BR\"\n :min=\"field.min\"\n :max=\"field.max\"\n :min-fraction-digits=\"field.minFractionDigits\"\n :max-fraction-digits=\"field.maxFractionDigits\"\n :suffix=\"field.suffix\"\n :prefix=\"field.prefix\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Currency (fill-from-right, calculator style) -->\n <WMoneyInput\n v-else-if=\"field.type === 'currency' && field.fillFromRight\"\n :model-value=\"formData[field.field] as number | null\"\n :decimals=\"field.decimals ?? 2\"\n currency\n :prefix=\"field.prefix\"\n :suffix=\"field.suffix\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Currency -->\n <InputNumber\n v-else-if=\"field.type === 'currency'\"\n :model-value=\"formData[field.field] as number\"\n fluid\n mode=\"currency\"\n currency=\"BRL\"\n locale=\"pt-BR\"\n :min=\"field.min\"\n :max=\"field.max\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Select -->\n <Select\n v-else-if=\"field.type === 'select'\"\n :model-value=\"formData[field.field]\"\n fluid\n :options=\"unwrapRef(field.options) as any[]\"\n :option-label=\"field.optionLabel || 'label'\"\n :option-value=\"field.optionValue || 'value'\"\n :show-clear=\"field.showClear !== false\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- AutoComplete (options locais) -->\n <AutoComplete\n v-else-if=\"field.type === 'autocomplete'\"\n :model-value=\"getAutocompleteValue(field)\"\n fluid\n :suggestions=\"getFilteredSuggestions(field) as any[]\"\n :option-label=\"field.optionLabel || 'label'\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @complete=\"onAutocompleteSearch(field, $event)\"\n @item-select=\"onAutocompleteSelect(field, $event)\"\n @clear=\"emit('update:field', field.field, null)\"\n />\n\n <!-- FK (busca na API) -->\n <WAutoCompleteFK\n v-else-if=\"field.type === 'fk'\"\n :model-value=\"formData[field.field] as any\"\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :endpoint=\"field.endpoint!\"\n :endpoint-params=\"resolveEndpointParams(field)\"\n :drilldown=\"resolveDrilldown(field)\"\n :blocked-placeholder=\"field.blockedPlaceholder\"\n :option-label=\"field.optionLabel || 'nome'\"\n :option-description=\"field.optionDescription\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n :show-clear=\"field.showClear !== false\"\n :dialog-header=\"field.label\"\n :crud-fields=\"field.crudFields\"\n :crud-columns=\"field.crudColumns\"\n :sub-rows=\"subRowsFetchDe(field)\"\n :dialog-width=\"field.dialogWidth\"\n :can-create=\"field.canCreate\"\n :can-edit=\"field.canEdit\"\n :can-delete=\"field.canDelete\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Date -->\n <WDatePicker\n v-else-if=\"field.type === 'date'\"\n :model-value=\"formData[field.field] as Date | string | null\"\n value-format=\"date\"\n :autonow=\"field.autonow\"\n :min-date=\"field.minDate\"\n :max-date=\"field.maxDate\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Datetime -->\n <WDatePicker\n v-else-if=\"field.type === 'datetime'\"\n :model-value=\"formData[field.field] as Date | string | null\"\n value-format=\"date\"\n show-time\n :autonow=\"field.autonow\"\n :min-date=\"field.minDate\"\n :max-date=\"field.maxDate\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- CPF/CNPJ -->\n <InputText\n v-else-if=\"field.type === 'cpf_cnpj'\"\n :model-value=\"displayCpfCnpj(formData[field.field])\"\n fluid\n maxlength=\"18\"\n :placeholder=\"field.placeholder || '000.000.000-00'\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!fieldErrors[field.field]\"\n @input=\"onCpfCnpjInput(field.field, $event)\"\n @blur=\"validateField(field)\"\n />\n\n <!-- Mask (maska) -->\n <InputText\n v-else-if=\"field.type === 'mask'\"\n v-maska=\"{ mask: convertMask(field.mask) }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!fieldErrors[field.field]\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n @blur=\"validateField(field)\"\n />\n\n <!-- CEP -->\n <InputText\n v-else-if=\"field.type === 'cep'\"\n v-maska=\"{ mask: '#####-###' }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :placeholder=\"field.placeholder || '00000-000'\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!cepError[field.field]\"\n @input=\"onCepInput(field, $event)\"\n />\n\n <!-- Textarea -->\n <Textarea\n v-else-if=\"field.type === 'textarea'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :rows=\"field.rows || 3\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Segmented (escolha única, 2-3 opções curtas, num trilho) -->\n <div v-else-if=\"field.type === 'segmented'\" class=\"w-segmented\">\n <button\n v-for=\"option in fieldOptions(field)\"\n :key=\"String(optionValueOf(field, option))\"\n type=\"button\"\n class=\"w-segmented__option\"\n :class=\"{ 'w-segmented__option--on': isOptionSelected(field, option) }\"\n :aria-pressed=\"isOptionSelected(field, option)\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"pickOption(field, option)\"\n >\n {{ optionLabelOf(field, option) }}\n </button>\n </div>\n\n <!-- Choice (escolha única em chips, N opções) -->\n <div v-else-if=\"field.type === 'choice'\" class=\"w-choice\">\n <button\n v-for=\"option in fieldOptions(field)\"\n :key=\"String(optionValueOf(field, option))\"\n type=\"button\"\n class=\"w-choice__option\"\n :class=\"{ 'w-choice__option--on': isOptionSelected(field, option) }\"\n :aria-pressed=\"isOptionSelected(field, option)\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"pickOption(field, option)\"\n >\n <i\n v-if=\"isOptionSelected(field, option) && field.choiceIcon !== ''\"\n :class=\"field.choiceIcon || 'pi pi-check-circle'\"\n />\n {{ optionLabelOf(field, option) }}\n </button>\n </div>\n\n <!-- Chips (valor múltiplo removível + gatilho + resumo) -->\n <div v-else-if=\"field.type === 'chips'\" class=\"w-chips\">\n <span\n v-for=\"(chip, chipIndex) in chipEntries(field)\"\n :key=\"chip.key\"\n class=\"w-chips__chip\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"w-chips__remove\"\n :title=\"field.chipsRemoveLabel || 'Remover'\"\n :aria-label=\"field.chipsRemoveLabel || 'Remover'\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"removeChip(field, chipIndex)\"\n >\n <i class=\"pi pi-times\" />\n </button>\n </span>\n\n <span\n v-if=\"!chipEntries(field).length && field.chipsEmptyLabel\"\n class=\"w-chips__empty\"\n >\n {{ field.chipsEmptyLabel }}\n </span>\n\n <slot\n :name=\"`chips-trigger-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :disabled=\"isFieldDisabled(field)\"\n :set-form-field=\"(f: string, v: unknown) => emit('update:field', f, v)\"\n />\n\n <span class=\"w-chips__summary\">\n <slot\n :name=\"`chips-summary-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :items=\"chipEntries(field)\"\n />\n </span>\n </div>\n\n <small v-if=\"cepError[field.field]\" class=\"w-crud-form-cep-error\">\n {{ cepError[field.field] }}\n </small>\n <small v-else-if=\"fieldErrors[field.field]\" class=\"w-crud-form-error\">\n {{ fieldErrors[field.field] }}\n </small>\n </div>\n </slot>\n </template>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, reactive, isRef, watch } from 'vue'\nimport { vMaska } from 'maska/vue'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Textarea from 'primevue/textarea'\nimport Select from 'primevue/select'\nimport AutoComplete from 'primevue/autocomplete'\nimport ToggleSwitch from 'primevue/toggleswitch'\nimport ColorPicker from 'primevue/colorpicker'\nimport Password from 'primevue/password'\nimport type { FieldDef, FieldSubRowsFetch } from '@/types/field'\nimport WAutoCompleteFK from '@/components/form/WAutoCompleteFK.vue'\nimport WDatePicker from '@/components/form/WDatePicker.vue'\nimport WMoneyInput from '@/components/form/WMoneyInput.vue'\nimport WImageCropper from '@/components/form/WImageCropper.vue'\nimport WTransferList from '@/components/form/WTransferList.vue'\nimport { lookupCep } from '@/utils/cep'\n\nconst props = withDefaults(\n defineProps<{\n fields: FieldDef[]\n formData: Record<string, unknown>\n isEditing: boolean\n disabled?: boolean\n /** Nº de colunas do grid (default 2). Campos ocupam frações via `colSpan`;\n * grupos podem sobrepor via `fieldGroup.columns`. */\n columns?: number\n }>(),\n {\n disabled: false,\n columns: 2,\n },\n)\n\nconst emit = defineEmits<{\n 'update:field': [field: string, value: unknown]\n}>()\n\nconst fieldErrors = reactive<Record<string, string | null>>({})\n\n// --- CEP state ---\nconst cepLoading = reactive<Record<string, boolean>>({})\nconst cepError = reactive<Record<string, string | null>>({})\nconst cepTimers = reactive<Record<string, ReturnType<typeof setTimeout> | null>>({})\n\nfunction onCepInput(field: FieldDef, event: Event) {\n const raw = (event.target as HTMLInputElement).value\n const digits = raw.replace(/\\D/g, '')\n emit('update:field', field.field, raw)\n\n // Clear previous error\n cepError[field.field] = null\n\n // Clear pending timer\n if (cepTimers[field.field]) {\n clearTimeout(cepTimers[field.field]!)\n cepTimers[field.field] = null\n }\n\n if (digits.length === 8) {\n cepTimers[field.field] = setTimeout(async () => {\n cepLoading[field.field] = true\n cepError[field.field] = null\n try {\n const result = await lookupCep(digits)\n if (!result) {\n cepError[field.field] = 'CEP não encontrado. Preencha os campos manualmente.'\n } else {\n // Fill mapped fields, but only when currently empty\n const mapping = field.cepFields || {}\n const keys = Object.keys(mapping) as Array<keyof typeof mapping>\n for (const key of keys) {\n const targetField = mapping[key]\n if (!targetField) continue\n const existing = props.formData[targetField]\n if (existing == null || existing === '') {\n emit('update:field', targetField, result[key] ?? '')\n }\n }\n }\n } finally {\n cepLoading[field.field] = false\n }\n }, 400)\n }\n}\n\n// --- Visibility & Disabled ---\n\nconst visibleFields = computed(() =>\n props.fields.filter((f) => {\n if (f.visible === undefined || f.visible === true) return true\n if (typeof f.visible === 'function') return f.visible(props.formData, props.isEditing)\n return f.visible\n }),\n)\n\nfunction isFieldDisabled(field: FieldDef): boolean {\n if (props.disabled) return true\n // Somente-leitura / calculado → sempre desabilitado (o valor ainda vai no payload).\n if (field.readonly || field.computed) return true\n if (field.disabledOnEdit && props.isEditing) return true\n if (typeof field.disabled === 'function') return field.disabled(props.formData, props.isEditing)\n return !!field.disabled\n}\n\n/** Indireção tipada do `subRows`: devolver a função direto no template fazia o\n * vue-tsc entrar no ciclo WFormRenderer → WAutoCompleteFK → WCrudFormDialog e\n * degradar tudo para `any`. */\nfunction subRowsFetchDe(field: FieldDef): FieldSubRowsFetch | undefined {\n return field.subRows\n}\n\n/** Resolve `endpointParams` estático ou por função (contexto do pai). */\nfunction resolveEndpointParams(field: FieldDef) {\n const ep = field.endpointParams\n return typeof ep === 'function' ? ep() : ep\n}\n\n// --- Campos calculados ---\n// Derivam o valor dos demais campos e o escrevem de volta no formData (para irem no\n// payload). O SOURCE do watch chama `calculate` — assim as dependências reativas exatas\n// (ex.: `formData.produto.unidade_medida`) são coletadas e o watch dispara quando elas\n// mudam. (Um `watch(() => formData, …, { deep })` não reage de forma confiável ao\n// `formData` que chega por prop no fluxo do WCrudFormDialog.) Só emite quando o valor\n// muda de fato (o `!==` evita laço). A função `calculate` deve cair no valor atual quando\n// não puder derivar (ex.: na edição, com a FK já como id).\nconst calculatedFields = computed(() =>\n props.fields.filter((f) => typeof f.calculate === 'function'),\n)\n\nwatch(\n () => calculatedFields.value.map((f) => f.calculate!(props.formData, props.isEditing)),\n (nexts) => {\n calculatedFields.value.forEach((f, i) => {\n if (props.formData[f.field] !== nexts[i]) emit('update:field', f.field, nexts[i])\n })\n },\n { immediate: true },\n)\n\nfunction unwrapRef<V>(val: V): V extends import('vue').Ref<infer U> ? U : V {\n return isRef(val) ? ((val as { value: unknown }).value as never) : (val as never)\n}\n\n// --- Autofocus ---\n\nconst autofocusField = computed(() => {\n const mode = props.isEditing ? 'edit' : 'create'\n const explicit = props.fields.find((f) => f.autofocus === true || f.autofocus === mode)\n if (explicit) return explicit.field\n const first = visibleFields.value.find((f) => {\n // Tipos sem campo de digitação não são candidatos a foco inicial.\n const semInput = ['switch', 'fk', 'select', 'image', 'segmented', 'choice', 'chips', 'transfer']\n if (f.type && semInput.includes(f.type)) return false\n if (f.disabled === true) return false\n if (f.disabledOnEdit && props.isEditing) return false\n return true\n })\n return first?.field ?? null\n})\n\nfunction shouldAutofocus(field: FieldDef): boolean {\n return field.field === autofocusField.value\n}\n\n// --- Mask format conversion (PrimeVue InputMask -> maska) ---\n\nfunction convertMask(mask?: string): string | undefined {\n if (!mask) return undefined\n return mask.replace(/9/g, '#').replace(/a/g, 'S').replace(/\\*/g, 'X')\n}\n\n// --- CPF/CNPJ inline mask ---\n\nfunction displayCpfCnpj(value: unknown): string {\n if (!value) return ''\n const digits = String(value).replace(/\\D/g, '').slice(0, 14)\n if (digits.length <= 11) {\n return digits\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d{1,2})$/, '$1-$2')\n }\n return digits\n .replace(/(\\d{2})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1.$2')\n .replace(/(\\d{3})(\\d)/, '$1/$2')\n .replace(/(\\d{4})(\\d{1,2})$/, '$1-$2')\n}\n\nfunction onCpfCnpjInput(fieldName: string, event: Event) {\n const raw = (event.target as HTMLInputElement).value.replace(/\\D/g, '').slice(0, 14)\n emit('update:field', fieldName, raw)\n}\n\n// --- AutoComplete local (para type=autocomplete com options) ---\n\nconst autocompleteSuggestions = reactive<Record<string, unknown[]>>({})\n\nfunction getAutocompleteValue(field: FieldDef) {\n const val = props.formData[field.field]\n if (val == null) return null\n const optionValue = field.optionValue || 'value'\n const options = unwrapRef(field.options) || []\n return (options as Record<string, unknown>[]).find((o) => o[optionValue] === val) ?? null\n}\n\nfunction getFilteredSuggestions(field: FieldDef) {\n return autocompleteSuggestions[field.field] || []\n}\n\nfunction onAutocompleteSearch(field: FieldDef, event: { query: string }) {\n const query = (event.query || '').toLowerCase()\n const options = unwrapRef(field.options) || []\n const optionLabel = field.optionLabel || 'label'\n autocompleteSuggestions[field.field] = (options as Record<string, unknown>[]).filter((o) =>\n String(o[optionLabel] || '')\n .toLowerCase()\n .includes(query),\n )\n}\n\nfunction onAutocompleteSelect(field: FieldDef, event: { value: Record<string, unknown> }) {\n const optionValue = field.optionValue || 'value'\n emit('update:field', field.field, event.value[optionValue])\n}\n\n// --- Options (segmented / choice / chips) ---\n// Os três tipos leem a MESMA tripla `options`/`optionLabel`/`optionValue` do\n// select — quem troca um Select por pílulas não reescreve o FieldDef.\n\nfunction fieldOptions(field: FieldDef): Record<string, unknown>[] {\n return (unwrapRef(field.options) || []) as Record<string, unknown>[]\n}\n\nfunction optionLabelOf(field: FieldDef, option: Record<string, unknown>): string {\n return String(option[field.optionLabel || 'label'] ?? '')\n}\n\nfunction optionValueOf(field: FieldDef, option: Record<string, unknown>): unknown {\n return option[field.optionValue || 'value']\n}\n\nfunction isOptionSelected(field: FieldDef, option: Record<string, unknown>): boolean {\n return props.formData[field.field] === optionValueOf(field, option)\n}\n\n/** Escolha única: clicar na opção ativa mantém a escolha (não desmarca). */\nfunction pickOption(field: FieldDef, option: Record<string, unknown>) {\n if (isFieldDisabled(field)) return\n emit('update:field', field.field, optionValueOf(field, option))\n}\n\n// --- Chips (valor múltiplo, removível) ---\n// O valor é um ARRAY. Cada item pode ser o objeto inteiro (usa `optionLabel`) ou\n// só o id (procura o rótulo em `options`; sem match, mostra o próprio valor).\n\ninterface ChipEntry {\n key: string\n label: string\n}\n\nfunction chipEntries(field: FieldDef): ChipEntry[] {\n const raw = props.formData[field.field]\n if (!Array.isArray(raw)) return []\n const labelKey = field.optionLabel || 'nome'\n const valueKey = field.optionValue || 'id'\n const options = fieldOptions(field)\n\n return raw.map((item, index) => {\n if (item !== null && typeof item === 'object') {\n const obj = item as Record<string, unknown>\n return { key: String(obj[valueKey] ?? index), label: String(obj[labelKey] ?? '') }\n }\n const match = options.find((o) => o[valueKey] === item)\n return {\n key: String(item ?? index),\n label: match ? String(match[labelKey] ?? item) : String(item),\n }\n })\n}\n\nfunction removeChip(field: FieldDef, index: number) {\n if (isFieldDisabled(field)) return\n const raw = props.formData[field.field]\n if (!Array.isArray(raw)) return\n emit(\n 'update:field',\n field.field,\n raw.filter((_, i) => i !== index),\n )\n}\n\n// --- Color ---\n\nfunction getColorPickerValue(field: FieldDef): string {\n const val = props.formData[field.field]\n return val ? String(val).replace('#', '') : 'FFFFFF'\n}\n\nfunction onColorChange(field: FieldDef, value: string) {\n emit('update:field', field.field, `#${value}`)\n}\n\n// --- Drill-down (filtro em cascata das FKs) ---\n\n/** Resolve o `dependsOn` do campo em filtros já com o valor atual do formulário. */\nfunction resolveDrilldown(field: FieldDef) {\n const dep = field.dependsOn\n if (!dep) return undefined\n const list = Array.isArray(dep) ? dep : [dep]\n return list.map((d) => ({\n field: d.param || d.field,\n value: props.formData[d.field],\n required: d.required,\n }))\n}\n\n// --- Validation ---\n\nfunction validateField(field: FieldDef) {\n if (typeof field.validate === 'function') {\n const error = field.validate(props.formData[field.field])\n fieldErrors[field.field] = error || null\n }\n}\n\nfunction validateAll(): string[] {\n const errors: string[] = []\n for (const field of props.fields) {\n if (typeof field.validate === 'function') {\n const error = field.validate(props.formData[field.field])\n fieldErrors[field.field] = error || null\n if (error) errors.push(error)\n }\n }\n return errors\n}\n\nfunction clearErrors() {\n Object.keys(fieldErrors).forEach((k) => delete fieldErrors[k])\n}\n\n// --- Field Groups ---\n\ninterface FieldGroup {\n id: string\n title?: string\n description?: string\n columns?: number\n fields: FieldDef[]\n}\n\n// --- Grid columns / span ---\n\nfunction groupColumns(group: FieldGroup): number {\n return Math.max(1, Math.floor(group.columns ?? props.columns))\n}\n\n/** Resolve o colSpan do campo em nº de colunas do grid do grupo.\n * - omitido ou 'full' → linha inteira\n * - 0.5 → metade da linha (independe do nº de colunas)\n * - inteiro ≥ 1 → N colunas (limitado ao total) */\nfunction fieldSpan(field: FieldDef, cols: number): number {\n const span = field.colSpan\n if (span == null || span === 'full') return cols\n if (span === 0.5) return Math.max(1, Math.round(cols / 2))\n return Math.min(Math.max(1, Math.floor(span)), cols)\n}\n\nfunction fieldSpanStyle(field: FieldDef, group: FieldGroup) {\n return { '--w-col-span': fieldSpan(field, groupColumns(group)) }\n}\n\nfunction fieldSpanClass(field: FieldDef, group: FieldGroup): string {\n return fieldSpan(field, groupColumns(group)) === groupColumns(group)\n ? 'w-crud-form-col-full'\n : 'w-crud-form-col-half'\n}\n\nconst groupedFields = computed((): FieldGroup[] => {\n const groupMap = new Map<string, FieldGroup>()\n const groupOrder: string[] = []\n const groupExplicitOrder = new Map<string, number>()\n\n for (const field of visibleFields.value) {\n const groupId = field.fieldGroup?.id ?? '__default__'\n\n if (!groupMap.has(groupId)) {\n groupMap.set(groupId, {\n id: groupId,\n title: field.fieldGroup?.title,\n description: field.fieldGroup?.description,\n columns: field.fieldGroup?.columns,\n fields: [],\n })\n groupOrder.push(groupId)\n if (field.fieldGroup?.order != null) {\n groupExplicitOrder.set(groupId, field.fieldGroup.order)\n }\n }\n\n groupMap.get(groupId)!.fields.push(field)\n }\n\n return groupOrder\n .slice()\n .sort((a, b) => {\n const orderA = groupExplicitOrder.get(a)\n const orderB = groupExplicitOrder.get(b)\n if (orderA != null && orderB != null) return orderA - orderB\n if (orderA != null) return -1\n if (orderB != null) return 1\n return groupOrder.indexOf(a) - groupOrder.indexOf(b)\n })\n .map((id) => groupMap.get(id)!)\n})\n\ndefineExpose({ validateAll, clearErrors })\n</script>\n\n<template>\n <div class=\"w-crud-form\">\n <div v-for=\"group in groupedFields\" :key=\"group.id\" class=\"w-crud-form-group\">\n <div v-if=\"group.title\" class=\"w-crud-form-group-header\">\n <h3 class=\"w-crud-form-group-title\">{{ group.title }}</h3>\n <p v-if=\"group.description\" class=\"w-crud-form-group-desc\">{{ group.description }}</p>\n </div>\n <div class=\"w-crud-form-fields\" :style=\"{ '--w-form-cols': groupColumns(group) }\">\n <template v-for=\"field in group.fields\" :key=\"field.field\">\n <slot\n :name=\"`field-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :set-form-field=\"(f: string, v: unknown) => emit('update:field', f, v)\"\n >\n <!-- Switch -->\n <div\n v-if=\"field.type === 'switch'\"\n class=\"w-crud-form-switch\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <ToggleSwitch\n :model-value=\"formData[field.field] as boolean\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n <label class=\"w-crud-form-switch-label\">{{ field.switchLabel || field.label }}</label>\n </div>\n\n <!-- Color -->\n <div\n v-else-if=\"field.type === 'color'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n </label>\n <div class=\"w-crud-form-color-row\">\n <ColorPicker\n :model-value=\"getColorPickerValue(field)\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"onColorChange(field, $event as string)\"\n />\n <InputText\n :model-value=\"formData[field.field] as string\"\n class=\"w-28\"\n maxlength=\"7\"\n placeholder=\"#000000\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n </div>\n </div>\n\n <!-- Image -->\n <div\n v-else-if=\"field.type === 'image'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n </label>\n <slot :name=\"`image-${field.field}`\" :field=\"field\" :form-data=\"formData\">\n <WImageCropper\n :model-value=\"formData[field.field] as File | string | null\"\n :accept=\"field.accept || 'image/*'\"\n @update:model-value=\"(file) => emit('update:field', field.field, file)\"\n @error=\"\n (msg) => {\n fieldErrors[field.field] = msg\n }\n \"\n />\n </slot>\n <small v-if=\"fieldErrors[field.field]\" class=\"w-crud-form-error\">\n {{ fieldErrors[field.field] }}\n </small>\n </div>\n\n <!-- Transfer (dual list) -->\n <div\n v-else-if=\"field.type === 'transfer'\"\n :class=\"fieldSpanClass(field, group)\"\n :style=\"fieldSpanStyle(field, group)\"\n >\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n </label>\n <WTransferList\n :source=\"(unwrapRef(field.options) as any[]) || []\"\n :selected=\"(formData[field.field] as any[]) || []\"\n :track-by=\"field.optionValue || 'id'\"\n :option-label=\"field.optionLabel || 'nome'\"\n :search-fields=\"field.searchFields\"\n :disabled=\"isFieldDisabled(field)\"\n @update:selected=\"(val) => emit('update:field', field.field, val)\"\n />\n </div>\n\n <!-- All other types -->\n <div v-else :class=\"fieldSpanClass(field, group)\" :style=\"fieldSpanStyle(field, group)\">\n <label class=\"w-crud-form-label\">\n {{ field.label }}\n <span v-if=\"field.required\" class=\"w-crud-form-required\">*</span>\n <i\n v-if=\"cepLoading[field.field]\"\n class=\"pi pi-spin pi-spinner w-crud-form-cep-spinner\"\n />\n </label>\n\n <!-- Text with mask (maska) -->\n <InputText\n v-if=\"(!field.type || field.type === 'text') && field.mask\"\n v-maska=\"{ mask: convertMask(field.mask) }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Text -->\n <InputText\n v-else-if=\"!field.type || field.type === 'text'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Email -->\n <InputText\n v-else-if=\"field.type === 'email'\"\n :model-value=\"formData[field.field] as string\"\n type=\"email\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Password -->\n <Password\n v-else-if=\"field.type === 'password'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n toggle-mask\n :feedback=\"field.feedback !== false\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Number -->\n <InputNumber\n v-else-if=\"field.type === 'number'\"\n :model-value=\"formData[field.field] as number\"\n fluid\n locale=\"pt-BR\"\n :min=\"field.min\"\n :max=\"field.max\"\n :min-fraction-digits=\"field.minFractionDigits\"\n :max-fraction-digits=\"field.maxFractionDigits\"\n :suffix=\"field.suffix\"\n :prefix=\"field.prefix\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Currency (fill-from-right, calculator style) -->\n <WMoneyInput\n v-else-if=\"field.type === 'currency' && field.fillFromRight\"\n :model-value=\"formData[field.field] as number | null\"\n :decimals=\"field.decimals ?? 2\"\n currency\n :prefix=\"field.prefix\"\n :suffix=\"field.suffix\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Currency -->\n <InputNumber\n v-else-if=\"field.type === 'currency'\"\n :model-value=\"formData[field.field] as number\"\n fluid\n mode=\"currency\"\n currency=\"BRL\"\n locale=\"pt-BR\"\n :min=\"field.min\"\n :max=\"field.max\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Select -->\n <Select\n v-else-if=\"field.type === 'select'\"\n :model-value=\"formData[field.field]\"\n fluid\n :options=\"unwrapRef(field.options) as any[]\"\n :option-label=\"field.optionLabel || 'label'\"\n :option-value=\"field.optionValue || 'value'\"\n :show-clear=\"field.showClear !== false\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- AutoComplete (options locais) -->\n <AutoComplete\n v-else-if=\"field.type === 'autocomplete'\"\n :model-value=\"getAutocompleteValue(field)\"\n fluid\n :suggestions=\"getFilteredSuggestions(field) as any[]\"\n :option-label=\"field.optionLabel || 'label'\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @complete=\"onAutocompleteSearch(field, $event)\"\n @item-select=\"onAutocompleteSelect(field, $event)\"\n @clear=\"emit('update:field', field.field, null)\"\n />\n\n <!-- FK (busca na API) -->\n <WAutoCompleteFK\n v-else-if=\"field.type === 'fk'\"\n :model-value=\"formData[field.field] as any\"\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :endpoint=\"field.endpoint!\"\n :endpoint-params=\"resolveEndpointParams(field)\"\n :drilldown=\"resolveDrilldown(field)\"\n :blocked-placeholder=\"field.blockedPlaceholder\"\n :option-label=\"field.optionLabel || 'nome'\"\n :option-description=\"field.optionDescription\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n :show-clear=\"field.showClear !== false\"\n :dialog-header=\"field.label\"\n :crud-fields=\"field.crudFields\"\n :crud-columns=\"field.crudColumns\"\n :sub-rows=\"subRowsFetchDe(field)\"\n :dialog-width=\"field.dialogWidth\"\n :can-create=\"field.canCreate\"\n :can-edit=\"field.canEdit\"\n :can-delete=\"field.canDelete\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Date -->\n <WDatePicker\n v-else-if=\"field.type === 'date'\"\n :model-value=\"formData[field.field] as Date | string | null\"\n value-format=\"date\"\n :autonow=\"field.autonow\"\n :min-date=\"field.minDate\"\n :max-date=\"field.maxDate\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Datetime -->\n <WDatePicker\n v-else-if=\"field.type === 'datetime'\"\n :model-value=\"formData[field.field] as Date | string | null\"\n value-format=\"date\"\n show-time\n :autonow=\"field.autonow\"\n :min-date=\"field.minDate\"\n :max-date=\"field.maxDate\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- CPF/CNPJ -->\n <InputText\n v-else-if=\"field.type === 'cpf_cnpj'\"\n :model-value=\"displayCpfCnpj(formData[field.field])\"\n fluid\n maxlength=\"18\"\n :placeholder=\"field.placeholder || '000.000.000-00'\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!fieldErrors[field.field]\"\n @input=\"onCpfCnpjInput(field.field, $event)\"\n @blur=\"validateField(field)\"\n />\n\n <!-- Mask (maska) -->\n <InputText\n v-else-if=\"field.type === 'mask'\"\n v-maska=\"{ mask: convertMask(field.mask) }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!fieldErrors[field.field]\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n @blur=\"validateField(field)\"\n />\n\n <!-- CEP -->\n <InputText\n v-else-if=\"field.type === 'cep'\"\n v-maska=\"{ mask: '#####-###' }\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :placeholder=\"field.placeholder || '00000-000'\"\n :disabled=\"isFieldDisabled(field)\"\n :invalid=\"!!cepError[field.field]\"\n @input=\"onCepInput(field, $event)\"\n />\n\n <!-- Textarea -->\n <Textarea\n v-else-if=\"field.type === 'textarea'\"\n :model-value=\"formData[field.field] as string\"\n fluid\n :autofocus=\"shouldAutofocus(field) || undefined\"\n :rows=\"field.rows || 3\"\n :placeholder=\"field.placeholder\"\n :disabled=\"isFieldDisabled(field)\"\n @update:model-value=\"(val) => emit('update:field', field.field, val)\"\n />\n\n <!-- Segmented (escolha única, 2-3 opções curtas, num trilho) -->\n <div v-else-if=\"field.type === 'segmented'\" class=\"w-segmented\">\n <button\n v-for=\"option in fieldOptions(field)\"\n :key=\"String(optionValueOf(field, option))\"\n type=\"button\"\n class=\"w-segmented__option\"\n :class=\"{ 'w-segmented__option--on': isOptionSelected(field, option) }\"\n :aria-pressed=\"isOptionSelected(field, option)\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"pickOption(field, option)\"\n >\n {{ optionLabelOf(field, option) }}\n </button>\n </div>\n\n <!-- Choice (escolha única em chips, N opções) -->\n <div v-else-if=\"field.type === 'choice'\" class=\"w-choice\">\n <button\n v-for=\"option in fieldOptions(field)\"\n :key=\"String(optionValueOf(field, option))\"\n type=\"button\"\n class=\"w-choice__option\"\n :class=\"{ 'w-choice__option--on': isOptionSelected(field, option) }\"\n :aria-pressed=\"isOptionSelected(field, option)\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"pickOption(field, option)\"\n >\n <i\n v-if=\"isOptionSelected(field, option) && field.choiceIcon !== ''\"\n :class=\"field.choiceIcon || 'pi pi-check-circle'\"\n />\n {{ optionLabelOf(field, option) }}\n </button>\n </div>\n\n <!-- Chips (valor múltiplo removível + gatilho + resumo) -->\n <div v-else-if=\"field.type === 'chips'\" class=\"w-chips\">\n <span\n v-for=\"(chip, chipIndex) in chipEntries(field)\"\n :key=\"chip.key\"\n class=\"w-chips__chip\"\n >\n {{ chip.label }}\n <button\n type=\"button\"\n class=\"w-chips__remove\"\n :title=\"field.chipsRemoveLabel || 'Remover'\"\n :aria-label=\"field.chipsRemoveLabel || 'Remover'\"\n :disabled=\"isFieldDisabled(field)\"\n @click=\"removeChip(field, chipIndex)\"\n >\n <i class=\"pi pi-times\" />\n </button>\n </span>\n\n <span\n v-if=\"!chipEntries(field).length && field.chipsEmptyLabel\"\n class=\"w-chips__empty\"\n >\n {{ field.chipsEmptyLabel }}\n </span>\n\n <slot\n :name=\"`chips-trigger-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :disabled=\"isFieldDisabled(field)\"\n :set-form-field=\"(f: string, v: unknown) => emit('update:field', f, v)\"\n />\n\n <span class=\"w-chips__summary\">\n <slot\n :name=\"`chips-summary-${field.field}`\"\n :field=\"field\"\n :form-data=\"formData\"\n :items=\"chipEntries(field)\"\n />\n </span>\n </div>\n\n <small v-if=\"cepError[field.field]\" class=\"w-crud-form-cep-error\">\n {{ cepError[field.field] }}\n </small>\n <small v-else-if=\"fieldErrors[field.field]\" class=\"w-crud-form-error\">\n {{ fieldErrors[field.field] }}\n </small>\n </div>\n </slot>\n </template>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, watch } from 'vue'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport Button from 'primevue/button'\nimport type { FieldDef } from '@/types/field'\nimport WFormRenderer from '@/components/form/WFormRenderer.vue'\nimport { useFormKeyboardNav } from '@/composables/useFormKeyboardNav'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\nconst props = withDefaults(\n defineProps<{\n visible: boolean\n title: string\n fields: FieldDef[]\n formData: Record<string, unknown>\n isEditing: boolean\n saving: boolean\n disabled?: boolean\n width?: string\n /** Nº de colunas do grid do form (repassado ao WFormRenderer; default 2). */\n formColumns?: number\n /** Navegação por teclado estilo desktop: foca o 1º campo ao abrir e o Enter\n * pula para o próximo campo até o botão de salvar. Opt-in. */\n keyboardNav?: boolean\n }>(),\n {\n width: '480px',\n disabled: false,\n formColumns: undefined,\n },\n)\n\nconst emit = defineEmits<{\n 'update:visible': [value: boolean]\n 'update:field': [field: string, value: unknown]\n save: []\n}>()\n\nconst rendererRef = ref<InstanceType<typeof WFormRenderer> | null>(null)\nconst formRef = ref<HTMLFormElement | null>(null)\n\nfunction onSave() {\n if (rendererRef.value) {\n const errors = rendererRef.value.validateAll()\n if (errors.length === 0) {\n emit('save')\n }\n } else {\n emit('save')\n }\n}\n\n// Navegação por teclado (opt-in) — sem watchers: @keydown no <form> + @show no Dialog.\nconst { focusFirst, handleKeydown } = useFormKeyboardNav(formRef, {\n enabled: () => props.keyboardNav === true,\n onSubmit: onSave,\n})\n\n// Clear errors when dialog opens\nwatch(\n () => props.visible,\n (val) => {\n if (val && rendererRef.value) {\n rendererRef.value.clearErrors()\n }\n },\n)\n</script>\n\n<template>\n <Dialog\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n :visible=\"visible\"\n :header=\"title\"\n :style=\"{ width }\"\n modal\n :draggable=\"false\"\n class=\"w-crud-form-dialog\"\n @update:visible=\"emit('update:visible', $event)\"\n @show=\"focusFirst\"\n >\n <form ref=\"formRef\" class=\"w-crud-form\" @submit.prevent=\"onSave\" @keydown=\"handleKeydown\">\n <WFormRenderer\n ref=\"rendererRef\"\n :fields=\"fields\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :disabled=\"disabled\"\n :columns=\"formColumns\"\n @update:field=\"(f, v) => emit('update:field', f, v)\"\n >\n <!-- Forward field-* slots from parent -->\n <template\n v-for=\"field in fields\"\n :key=\"`fwd-${field.field}`\"\n #[`field-${field.field}`]=\"slotData\"\n >\n <slot :name=\"`field-${field.field}`\" v-bind=\"slotData\" />\n </template>\n <!-- Forward image-* slots from parent -->\n <template\n v-for=\"field in fields.filter((f) => f.type === 'image')\"\n :key=\"`img-${field.field}`\"\n #[`image-${field.field}`]=\"slotData\"\n >\n <slot :name=\"`image-${field.field}`\" v-bind=\"slotData\" />\n </template>\n </WFormRenderer>\n\n <!-- Depois dos campos, antes do rodapé: prévia do que vai ser gravado, saldo do\n que foi escolhido, aviso dependente do preenchimento. É conteúdo do form\n (reage ao que está digitado), não ação — por isso não vive no `#footer`. -->\n <slot name=\"after-fields\" :form-data=\"formData\" :is-editing=\"isEditing\" />\n\n <!-- Footer -->\n <div class=\"w-crud-form-footer\">\n <slot name=\"footer\" :saving=\"saving\" :disabled=\"disabled\">\n <Button\n type=\"button\"\n :label=\"disabled ? 'Fechar' : 'Cancelar'\"\n severity=\"secondary\"\n text\n :disabled=\"saving\"\n data-kbd-skip\n @click=\"emit('update:visible', false)\"\n />\n <Button\n v-if=\"!disabled\"\n type=\"submit\"\n :label=\"isEditing ? 'Atualizar' : 'Salvar'\"\n icon=\"pi pi-check\"\n :loading=\"saving\"\n />\n </slot>\n </div>\n </form>\n </Dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, watch } from 'vue'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport Button from 'primevue/button'\nimport type { FieldDef } from '@/types/field'\nimport WFormRenderer from '@/components/form/WFormRenderer.vue'\nimport { useFormKeyboardNav } from '@/composables/useFormKeyboardNav'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\nconst props = withDefaults(\n defineProps<{\n visible: boolean\n title: string\n fields: FieldDef[]\n formData: Record<string, unknown>\n isEditing: boolean\n saving: boolean\n disabled?: boolean\n width?: string\n /** Nº de colunas do grid do form (repassado ao WFormRenderer; default 2). */\n formColumns?: number\n /** Navegação por teclado estilo desktop: foca o 1º campo ao abrir e o Enter\n * pula para o próximo campo até o botão de salvar. Opt-in. */\n keyboardNav?: boolean\n }>(),\n {\n width: '480px',\n disabled: false,\n formColumns: undefined,\n },\n)\n\nconst emit = defineEmits<{\n 'update:visible': [value: boolean]\n 'update:field': [field: string, value: unknown]\n save: []\n}>()\n\nconst rendererRef = ref<InstanceType<typeof WFormRenderer> | null>(null)\nconst formRef = ref<HTMLFormElement | null>(null)\n\nfunction onSave() {\n if (rendererRef.value) {\n const errors = rendererRef.value.validateAll()\n if (errors.length === 0) {\n emit('save')\n }\n } else {\n emit('save')\n }\n}\n\n// Navegação por teclado (opt-in) — sem watchers: @keydown no <form> + @show no Dialog.\nconst { focusFirst, handleKeydown } = useFormKeyboardNav(formRef, {\n enabled: () => props.keyboardNav === true,\n onSubmit: onSave,\n})\n\n// Clear errors when dialog opens\nwatch(\n () => props.visible,\n (val) => {\n if (val && rendererRef.value) {\n rendererRef.value.clearErrors()\n }\n },\n)\n</script>\n\n<template>\n <Dialog\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n :visible=\"visible\"\n :header=\"title\"\n :style=\"{ width }\"\n modal\n :draggable=\"false\"\n class=\"w-crud-form-dialog\"\n @update:visible=\"emit('update:visible', $event)\"\n @show=\"focusFirst\"\n >\n <form ref=\"formRef\" class=\"w-crud-form\" @submit.prevent=\"onSave\" @keydown=\"handleKeydown\">\n <WFormRenderer\n ref=\"rendererRef\"\n :fields=\"fields\"\n :form-data=\"formData\"\n :is-editing=\"isEditing\"\n :disabled=\"disabled\"\n :columns=\"formColumns\"\n @update:field=\"(f, v) => emit('update:field', f, v)\"\n >\n <!-- Forward field-* slots from parent -->\n <template\n v-for=\"field in fields\"\n :key=\"`fwd-${field.field}`\"\n #[`field-${field.field}`]=\"slotData\"\n >\n <slot :name=\"`field-${field.field}`\" v-bind=\"slotData\" />\n </template>\n <!-- Forward image-* slots from parent -->\n <template\n v-for=\"field in fields.filter((f) => f.type === 'image')\"\n :key=\"`img-${field.field}`\"\n #[`image-${field.field}`]=\"slotData\"\n >\n <slot :name=\"`image-${field.field}`\" v-bind=\"slotData\" />\n </template>\n </WFormRenderer>\n\n <!-- Depois dos campos, antes do rodapé: prévia do que vai ser gravado, saldo do\n que foi escolhido, aviso dependente do preenchimento. É conteúdo do form\n (reage ao que está digitado), não ação — por isso não vive no `#footer`. -->\n <slot name=\"after-fields\" :form-data=\"formData\" :is-editing=\"isEditing\" />\n\n <!-- Footer -->\n <div class=\"w-crud-form-footer\">\n <slot name=\"footer\" :saving=\"saving\" :disabled=\"disabled\">\n <Button\n type=\"button\"\n :label=\"disabled ? 'Fechar' : 'Cancelar'\"\n severity=\"secondary\"\n text\n :disabled=\"saving\"\n data-kbd-skip\n @click=\"emit('update:visible', false)\"\n />\n <Button\n v-if=\"!disabled\"\n type=\"submit\"\n :label=\"isEditing ? 'Atualizar' : 'Salvar'\"\n icon=\"pi pi-check\"\n :loading=\"saving\"\n />\n </slot>\n </div>\n </form>\n </Dialog>\n</template>\n","import type { ColumnDef } from '@/types/column'\n\nexport interface ToCsvOptions {\n /** Field separator (default: ';' — friendly to pt-BR Excel). */\n separator?: string\n}\n\nfunction cellValue(col: ColumnDef, row: Record<string, unknown>): string {\n const raw = row[col.field]\n if (col.format) return col.format(raw, row)\n if (raw === null || raw === undefined) return ''\n if (typeof raw === 'boolean') return raw ? 'Sim' : 'Não'\n return String(raw)\n}\n\nfunction escapeCell(value: string, separator: string): string {\n if (\n value.includes('\"') ||\n value.includes(separator) ||\n value.includes('\\n') ||\n value.includes('\\r')\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n }\n return value\n}\n\n/**\n * Serialize rows into a CSV string respecting the given (visible) columns.\n * Uses the column `format` callback when present, prepends a UTF-8 BOM so\n * Excel renders accents correctly, and defaults to `;` as the separator.\n */\nexport function toCsv(\n rows: Record<string, unknown>[],\n columns: ColumnDef[],\n options: ToCsvOptions = {},\n): string {\n const sep = options.separator ?? ';'\n const header = columns.map((c) => escapeCell(c.header, sep)).join(sep)\n const lines = rows.map((row) => columns.map((c) => escapeCell(cellValue(c, row), sep)).join(sep))\n return '' + [header, ...lines].join('\\r\\n')\n}\n\n/** Trigger a browser download of a CSV string. */\nexport function downloadCsv(csv: string, filename = 'export.csv'): void {\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = filename\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n}\n","<script setup lang=\"ts\">\nimport { ref, reactive, computed, onMounted, watch, useSlots } from 'vue'\nimport DataTable from 'primevue/datatable'\nimport Column from 'primevue/column'\nimport Button from 'primevue/button'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Select from 'primevue/select'\nimport Popover from 'primevue/popover'\nimport Checkbox from 'primevue/checkbox'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Paginator from 'primevue/paginator'\nimport ContextMenu from 'primevue/contextmenu'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport WCrudColumnRenderer from './WCrudColumnRenderer.vue'\nimport WCrudFormDialog from './WCrudFormDialog.vue'\nimport type { CrudManagerReturn } from '@/types/manager'\nimport type { RowAction } from '@/types/action'\nimport type { ColumnDef } from '@/types/column'\nimport type { FieldDef } from '@/types/field'\nimport type { KpiItem } from '@/types/kpi'\nimport type { MenuItem } from 'primevue/menuitem'\nimport type { Slots } from 'vue'\nimport { useFormatters } from '@/composables/useFormatters'\nimport { toCsv, downloadCsv } from '@/utils/csv'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\nconst props = withDefaults(\n defineProps<{\n // WCrudView é agnóstico ao tipo do registro (trata linhas como Record<string, unknown>\n // internamente); a prop aceita o CrudManagerReturn de qualquer T do consumidor.\n // TODO(tipagem): tornar o componente genérico <T> e tipar selectedRow/eventos.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n crud: CrudManagerReturn<any>\n title: string\n subtitle?: string\n showSearch?: boolean\n showHeader?: boolean\n dialogWidth?: string\n /** Nº de colunas do grid do form dialog (default: `formColumns` da config\n * do useCrudManager, senão 2). */\n formColumns?: number\n autoInit?: boolean\n showKpi?: boolean\n kpiIcon?: string\n kpiLabel?: string\n extraKpis?: KpiItem[]\n expandable?: boolean\n viewToggle?: boolean\n defaultView?: 'table' | 'cards'\n cardFields?: number\n /** Cards \"sem moldura\": remove o chrome padrão do card (borda/sombra/padding/\n * seleção) para o slot `#card` desenhar o card inteiro (ex.: um card de design\n * próprio). O wrapper vira só a célula do grid. */\n cardBare?: boolean\n actionRail?: boolean\n contextMenu?: boolean\n showPrint?: boolean\n exportCsv?: boolean\n csvFilename?: string\n csvScope?: 'all' | 'page'\n csvPageSize?: number\n /** Chave de persistência do estado de coluna (visibilidade) em localStorage.\n * Opt-in: quando definida, exibe o seletor de colunas e persiste o que foi ocultado. */\n persistState?: string\n }>(),\n {\n showSearch: true,\n showHeader: true,\n dialogWidth: '480px',\n autoInit: true,\n showKpi: false,\n kpiIcon: 'pi pi-list',\n kpiLabel: 'Total de Registros',\n extraKpis: () => [],\n expandable: false,\n viewToggle: true,\n defaultView: 'table',\n cardFields: 4,\n actionRail: true,\n contextMenu: true,\n showPrint: false,\n exportCsv: true,\n csvFilename: 'export.csv',\n csvScope: 'all',\n csvPageSize: 200,\n },\n)\n\nconst emit = defineEmits<{\n 'row-expand': [data: unknown]\n 'row-collapse': [data: unknown]\n print: [data: Record<string, unknown>]\n}>()\n\nconst slots: Slots = useSlots()\nconst { formatNumber } = useFormatters()\n\nconst expandedRows = ref({})\n\n// --- Display mode (table / cards) ---\n\n// Persistência do modo de visualização (opt-in via `persistState`): a escolha do\n// usuário fica em localStorage sob a MESMA chave base do grid (rota + grid, provida\n// pelo consumidor). Leitura síncrona no setup evita flash table→cards. Default =\n// `defaultView` (por padrão 'table' = linha).\nfunction viewKey(): string | null {\n return props.persistState ? `wcrud:view:${props.persistState}` : null\n}\nfunction loadView(): 'table' | 'cards' {\n const key = viewKey()\n if (key) {\n const stored = localStorage.getItem(key)\n if (stored === 'table' || stored === 'cards') return stored\n }\n return props.defaultView\n}\nconst displayMode = ref<'table' | 'cards'>(loadView())\nfunction isView(mode: 'table' | 'cards'): boolean {\n return displayMode.value === mode\n}\nfunction setView(mode: 'table' | 'cards'): void {\n displayMode.value = mode\n const key = viewKey()\n if (key) localStorage.setItem(key, mode)\n}\nconst paginatorFirst = computed(\n () => (props.crud.pagination.page - 1) * props.crud.pagination.pageSize,\n)\n\n// --- Columns ---\n\n// Estado de coluna (opt-in via persistState): visibilidade persistida em localStorage.\nconst columnState = reactive<{ hidden: string[] }>({ hidden: [] })\nconst chooser = ref<InstanceType<typeof Popover> | null>(null)\nfunction colStateKey(): string | null {\n return props.persistState ? `wcrud:cols:${props.persistState}` : null\n}\nfunction loadColumnState() {\n const key = colStateKey()\n if (!key) return\n try {\n const parsed = JSON.parse(localStorage.getItem(key) || '{}')\n if (Array.isArray(parsed.hidden)) columnState.hidden = parsed.hidden\n } catch {\n /* estado inválido — ignora */\n }\n}\nfunction saveColumnState() {\n const key = colStateKey()\n if (key) localStorage.setItem(key, JSON.stringify({ hidden: columnState.hidden }))\n}\n\n// Estado de FILTROS (opt-in via persistState): filtros de coluna + busca ficam em\n// localStorage sob a mesma chave base do grid (rota + grid). Restaurados ANTES do\n// primeiro fetch, então o F5 mantém os últimos filtros aplicados.\nfunction filtersKey(): string | null {\n return props.persistState ? `wcrud:filters:${props.persistState}` : null\n}\nfunction loadFilters() {\n const key = filtersKey()\n if (!key) return\n try {\n const parsed = JSON.parse(localStorage.getItem(key) || '{}')\n if (parsed && typeof parsed.columnFilters === 'object' && parsed.columnFilters) {\n for (const [k, v] of Object.entries(parsed.columnFilters)) {\n if (v !== null && v !== undefined && v !== '') props.crud.columnFilters[k] = v\n }\n }\n if (typeof parsed.search === 'string' && parsed.search) {\n props.crud.search.value = parsed.search\n }\n } catch {\n /* estado inválido — ignora */\n }\n}\nfunction saveFilters() {\n const key = filtersKey()\n if (!key) return\n localStorage.setItem(\n key,\n JSON.stringify({\n columnFilters: { ...props.crud.columnFilters },\n search: props.crud.search.value,\n }),\n )\n}\nwatch(props.crud.columnFilters, saveFilters, { deep: true })\nwatch(props.crud.search, saveFilters)\n// Colunas base (respeita visible:false do consumidor) — fonte do seletor de colunas.\nconst baseColumns = computed(() => props.crud.config.columns.filter((c) => c.visible !== false))\nfunction toggleColumn(field: string, visible: boolean) {\n columnState.hidden = visible\n ? columnState.hidden.filter((f) => f !== field)\n : [...new Set([...columnState.hidden, field])]\n saveColumnState()\n}\n\nconst visibleColumns = computed(() =>\n props.crud.config.columns\n .filter((c) => c.visible !== false && !columnState.hidden.includes(c.field))\n .map((c) => {\n if (c.type === 'number' && !c.align) return { ...c, align: 'right' as const }\n if (c.type === 'currency' && !c.align) return { ...c, align: 'right' as const }\n return c\n }),\n)\n\nfunction colAlignClass(col: { align?: string }) {\n if (col.align === 'right') return 'text-right'\n if (col.align === 'center') return 'text-center'\n return undefined\n}\n\nconst cardColumns = computed(() => visibleColumns.value.slice(0, props.cardFields))\n\n// --- Row Actions ---\n\nconst defaultActions = computed<RowAction[]>(() => {\n const actions: RowAction[] = []\n if (props.crud.config.canEdit !== false) {\n actions.push({ action: 'edit', icon: 'pi pi-pencil', tooltip: 'Editar' })\n }\n if (props.crud.config.canCreate !== false) {\n actions.push({\n action: 'duplicate',\n icon: 'pi pi-copy',\n tooltip: 'Duplicar',\n severity: 'info',\n })\n }\n if (props.crud.config.canDelete !== false) {\n actions.push({\n action: 'delete',\n icon: 'pi pi-trash',\n tooltip: 'Excluir',\n severity: 'danger',\n })\n }\n return actions\n})\n\nconst effectiveRowActions = computed<RowAction[]>(\n () => props.crud.config.rowActions ?? defaultActions.value,\n)\n\nconst hasActions = computed(\n () => effectiveRowActions.value.length > 0 || Boolean(slots['row-actions']),\n)\n\nfunction handleRowAction(action: RowAction, data: Record<string, unknown>) {\n // Custom handler tem prioridade — permite sobrescrever ações nomeadas.\n if (action.handler) {\n action.handler(data)\n return\n }\n if (action.action === 'edit') {\n props.crud.openEditDialog(data)\n } else if (action.action === 'view') {\n props.crud.openViewDialog(data)\n } else if (action.action === 'duplicate') {\n props.crud.openDuplicateDialog(data)\n } else if (action.action === 'delete') {\n // Com slot `delete-message`, usa o diálogo próprio (permite conteúdo rico,\n // ex.: avisos por registro); senão, o confirm de serviço padrão.\n if (slots['delete-message']) {\n deleteTarget.value = data\n deleteDialogVisible.value = true\n } else {\n props.crud.confirmDelete(data)\n }\n }\n}\n\n// --- Diálogo de exclusão próprio (só quando há slot `delete-message`) ---\nconst deleteTarget = ref<Record<string, unknown> | null>(null)\nconst deleteDialogVisible = ref(false)\nconst deleting = ref(false)\n\nasync function onConfirmDelete() {\n if (!deleteTarget.value) return\n deleting.value = true\n try {\n await props.crud.performDelete(deleteTarget.value)\n deleteDialogVisible.value = false\n deleteTarget.value = null\n } finally {\n deleting.value = false\n }\n}\n\nfunction isActionVisible(action: RowAction, data: Record<string, unknown>): boolean {\n if (action.visible) return action.visible(data)\n return true\n}\n\nfunction isActionDisabled(action: RowAction, data: Record<string, unknown>): boolean {\n if (action.disabled) return action.disabled(data)\n return false\n}\n\n// --- KPIs ---\n\nconst allKpis = computed<KpiItem[]>(() => {\n const kpis: KpiItem[] = []\n if (props.showKpi) {\n kpis.push({\n icon: props.kpiIcon,\n label: props.kpiLabel,\n value: formatNumber(props.crud.pagination.rows, 0),\n })\n }\n kpis.push(...props.extraKpis)\n return kpis\n})\n\nconst canCreate = computed(() => props.crud.config.canCreate !== false)\n\n// --- Selection (drives action rail + context menu) ---\n\n// Exposta como `v-model:selectedRow` para o consumidor sincronizar a seleção de\n// linha com outra visão do mesmo dado (ex.: mapa, gráfico). Sem o binding, o\n// defineModel age como ref local e nada muda para quem já usa o componente.\nconst selectedRow = defineModel<Record<string, unknown> | null>('selectedRow', {\n default: null,\n})\n\n// Seleção múltipla — opt-in via config.selectionMode: 'multiple'.\nconst isMultiSelect = computed(() => props.crud.config.selectionMode === 'multiple')\n\n// O tipo do PrimeVue não expõe `$el`; só precisamos do elemento raiz para o scroll.\nconst tableRef = ref<{ $el?: HTMLElement } | null>(null)\n\n/**\n * Rola a tabela até a linha cujo pk é `value` — o par do `v-model:selectedRow`\n * para o sentido \"de fora para dentro\" (selecionou no mapa/gráfico, a linha\n * aparece). Só alcança a página atual: linha em outra página não é buscada — é\n * do consumidor decidir o que fazer nesse caso (a lista é lazy). Com linhas de\n * expansão abertas o índice DOM diverge do índice do item; aí não rola.\n */\nfunction scrollToRow(value: unknown): void {\n const pk = props.crud.config.pk || 'id'\n const index = props.crud.items.value.findIndex(\n (row: Record<string, unknown>) => row[pk] === value,\n )\n if (index < 0) return\n const root = tableRef.value?.$el as HTMLElement | undefined\n const rows = root?.querySelectorAll<HTMLElement>('tbody > tr')\n if (!rows || rows.length !== props.crud.items.value.length) return\n rows[index]?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })\n}\n\ndefineExpose({ scrollToRow })\n\n// Edição inline por célula — opt-in via config.editMode: 'cell'.\nconst isCellEdit = computed(() => props.crud.config.editMode === 'cell')\nconst formFieldMap = computed(() => new Map(props.crud.config.form.map((f) => [f.field, f])))\nconst INLINE_TYPES = ['text', 'email', 'number', 'currency']\nfunction inlineField(field: string): FieldDef | undefined {\n const f = formFieldMap.value.get(field)\n return f && INLINE_TYPES.includes(f.type || 'text') ? f : undefined\n}\nfunction onCellEditComplete(e: {\n data: Record<string, unknown>\n field: string\n value?: unknown\n newValue: unknown\n}) {\n if (e.newValue === (e.value ?? e.data[e.field])) return\n props.crud.updateField(e.data, e.field, e.newValue)\n}\n\n// Filtros declarativos de coluna.\nconst filterableColumns = computed<ColumnDef[]>(() =>\n props.crud.config.columns.filter((c) => c.filter),\n)\nconst booleanFilterOptions = [\n { label: 'Sim', value: true },\n { label: 'Não', value: false },\n]\nfunction fParam(col: ColumnDef): string {\n return col.filter?.param || col.field\n}\nconst filterTimers: Record<string, ReturnType<typeof setTimeout>> = {}\nfunction onTextFilter(col: ColumnDef, value: string) {\n const param = fParam(col)\n if (filterTimers[param]) clearTimeout(filterTimers[param])\n filterTimers[param] = setTimeout(() => props.crud.setColumnFilter(param, value || null), 300)\n}\nconst cm = ref<InstanceType<typeof ContextMenu> | null>(null)\n\nfunction selectRow(row: Record<string, unknown>) {\n selectedRow.value = row\n}\n\nfunction onRowContextMenu(event: { originalEvent: Event; data: Record<string, unknown> }) {\n if (!props.contextMenu) return\n selectedRow.value = event.data\n cm.value?.show(event.originalEvent)\n}\n\nfunction onCardContextMenu(event: MouseEvent, row: Record<string, unknown>) {\n if (!props.contextMenu) return\n event.preventDefault()\n selectedRow.value = row\n cm.value?.show(event)\n}\n\nconst contextMenuItems = computed<MenuItem[]>(() => {\n const row = selectedRow.value\n if (!row) return []\n const items: MenuItem[] = [\n {\n label: 'Ver detalhes',\n icon: 'pi pi-eye',\n command: () => props.crud.openViewDialog(row),\n },\n ]\n // Todas as actions (default + customizadas) entram aqui.\n for (const action of effectiveRowActions.value) {\n if (!isActionVisible(action, row)) continue\n items.push({\n label: action.tooltip ?? action.action,\n icon: action.icon,\n class: action.severity === 'danger' ? 'w-crud-ctx-danger' : undefined,\n disabled: isActionDisabled(action, row),\n command: () => handleRowAction(action, row),\n })\n }\n if (props.showPrint) {\n items.push({\n label: 'Imprimir',\n icon: 'pi pi-print',\n command: () => emit('print', row),\n })\n }\n if (props.exportCsv) {\n items.push({ separator: true })\n items.push({\n label: props.csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)',\n icon: 'pi pi-download',\n command: () => doExportCsv(),\n })\n }\n return items\n})\n\n// --- Action rail handlers ---\n\nfunction railPrint() {\n if (selectedRow.value) emit('print', selectedRow.value)\n}\n\n// --- CSV export (visible columns) ---\n\nconst exporting = ref(false)\n\nasync function doExportCsv() {\n if (exporting.value) return\n exporting.value = true\n try {\n const rows =\n props.csvScope === 'page'\n ? props.crud.items.value\n : await props.crud.fetchAll(props.csvPageSize)\n const csv = toCsv(rows, visibleColumns.value)\n downloadCsv(csv, props.csvFilename)\n } finally {\n exporting.value = false\n }\n}\n\n// --- Init ---\n\nonMounted(() => {\n loadColumnState()\n loadFilters() // restaura filtros/busca ANTES do init para o fetch já sair filtrado\n if (props.autoInit) {\n props.crud.init()\n }\n})\n</script>\n\n<template>\n <div class=\"w-crud\">\n <!-- Header -->\n <div v-if=\"showHeader\" class=\"w-crud-header\">\n <div class=\"w-crud-header-content\">\n <h1 class=\"w-crud-title\">{{ title }}</h1>\n <p v-if=\"subtitle\" class=\"w-crud-subtitle\">{{ subtitle }}</p>\n </div>\n <div class=\"w-crud-header-actions\">\n <slot name=\"header-actions\" />\n <Button\n v-if=\"canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n\n <!-- KPIs -->\n <slot name=\"before-table\">\n <div v-if=\"allKpis.length\" class=\"w-crud-kpis\">\n <div v-for=\"(kpi, idx) in allKpis\" :key=\"idx\" class=\"w-crud-kpi\">\n <div :class=\"['w-crud-kpi-icon', kpi.severity ? `w-crud-kpi-icon--${kpi.severity}` : '']\">\n <i :class=\"[kpi.icon]\" :style=\"kpi.color ? `color: ${kpi.color}` : ''\" />\n </div>\n <div class=\"w-crud-kpi-content\">\n <div class=\"w-crud-kpi-label\">{{ kpi.label }}</div>\n <div class=\"w-crud-kpi-value\">{{ kpi.value }}</div>\n </div>\n </div>\n </div>\n </slot>\n\n <!-- Content (table/cards + optional action rail) -->\n <div class=\"w-crud-content\" :class=\"{ 'w-crud-content--rail': actionRail }\">\n <div class=\"w-crud-content-main\">\n <!-- Barra de filtros de coluna (declarativos, opt-in via ColumnDef.filter) -->\n <div\n v-if=\"filterableColumns.length\"\n class=\"w-crud-filters\"\n style=\"\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n align-items: center;\n margin-bottom: 0.75rem;\n \"\n >\n <template v-for=\"col in filterableColumns\" :key=\"col.field\">\n <InputText\n v-if=\"(col.filter?.type || 'text') === 'text'\"\n :model-value=\"(crud.columnFilters[fParam(col)] as string) ?? ''\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n size=\"small\"\n @input=\"(e: Event) => onTextFilter(col, (e.target as HTMLInputElement).value)\"\n />\n <Select\n v-else-if=\"col.filter?.type === 'select'\"\n :model-value=\"crud.columnFilters[fParam(col)]\"\n :options=\"col.filter?.options\"\n option-label=\"label\"\n option-value=\"value\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n show-clear\n size=\"small\"\n @update:model-value=\"(v: unknown) => crud.setColumnFilter(fParam(col), v)\"\n />\n <Select\n v-else-if=\"col.filter?.type === 'boolean'\"\n :model-value=\"crud.columnFilters[fParam(col)]\"\n :options=\"booleanFilterOptions\"\n option-label=\"label\"\n option-value=\"value\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n show-clear\n size=\"small\"\n @update:model-value=\"(v: unknown) => crud.setColumnFilter(fParam(col), v)\"\n />\n <InputNumber\n v-else-if=\"col.filter?.type === 'numeric'\"\n :model-value=\"(crud.columnFilters[fParam(col)] as number) ?? null\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n size=\"small\"\n @update:model-value=\"(v: number) => crud.setColumnFilter(fParam(col), v)\"\n />\n </template>\n <Button\n v-if=\"Object.keys(crud.columnFilters).length\"\n label=\"Limpar filtros\"\n icon=\"pi pi-filter-slash\"\n text\n size=\"small\"\n @click=\"crud.clearColumnFilters()\"\n />\n </div>\n\n <!-- Table -->\n <div v-if=\"displayMode === 'table'\" class=\"w-crud-table\">\n <!-- Barra de ações em lote (seleção múltipla) -->\n <div\n v-if=\"isMultiSelect && crud.selectedItems.value.length\"\n class=\"w-crud-bulkbar\"\n style=\"\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n border-radius: 8px;\n background: var(--p-primary-50, #eef2ff);\n color: var(--p-primary-700, #3730a3);\n \"\n >\n <span style=\"font-weight: 600; font-size: 0.85rem; margin-right: auto\">\n {{ crud.selectedItems.value.length }} selecionado(s)\n </span>\n <slot name=\"bulk-actions\" :selected=\"crud.selectedItems.value\" :crud=\"crud\">\n <Button\n v-for=\"ba in crud.config.bulkActions || []\"\n :key=\"ba.action\"\n :label=\"ba.label\"\n :icon=\"ba.icon\"\n :severity=\"ba.severity\"\n size=\"small\"\n :disabled=\"ba.disabled ? ba.disabled(crud.selectedItems.value) : false\"\n @click=\"ba.handler(crud.selectedItems.value)\"\n />\n </slot>\n <Button\n v-tooltip.top=\"'Limpar seleção'\"\n icon=\"pi pi-times\"\n text\n rounded\n size=\"small\"\n @click=\"crud.clearSelection()\"\n />\n </div>\n <DataTable\n ref=\"tableRef\"\n v-model:expanded-rows=\"expandedRows\"\n :value=\"crud.items.value\"\n :loading=\"crud.loading.value\"\n paginator\n :rows=\"crud.pagination.pageSize\"\n :total-records=\"crud.pagination.rows\"\n :rows-per-page-options=\"[10, 20, 50]\"\n paginator-template=\"CurrentPageReport PrevPageLink NextPageLink\"\n current-page-report-template=\"Página {currentPage} de {totalPages}\"\n :pt=\"{ pcPaginator: { root: { class: 'w-crud-paginator' } } }\"\n lazy\n striped-rows\n removable-sort\n size=\"small\"\n :sort-field=\"crud.sort.field ?? undefined\"\n :sort-order=\"crud.sort.order\"\n :data-key=\"crud.config.pk || 'id'\"\n :edit-mode=\"isCellEdit ? 'cell' : undefined\"\n :selection=\"\n isMultiSelect\n ? crud.selectedItems.value\n : actionRail || contextMenu\n ? selectedRow\n : undefined\n \"\n :selection-mode=\"\n isMultiSelect ? undefined : actionRail || contextMenu ? 'single' : undefined\n \"\n :context-menu=\"contextMenu\"\n :context-menu-selection=\"contextMenu ? selectedRow : undefined\"\n @update:selection=\"\n (v: any) => (isMultiSelect ? (crud.selectedItems.value = v) : (selectedRow = v))\n \"\n @update:context-menu-selection=\"(v: any) => (selectedRow = v)\"\n @row-contextmenu=\"onRowContextMenu\"\n @page=\"crud.onPage\"\n @sort=\"(e: any) => crud.onSort({ sortField: e.sortField, sortOrder: e.sortOrder })\"\n @row-expand=\"(e) => emit('row-expand', e.data)\"\n @row-collapse=\"(e) => emit('row-collapse', e.data)\"\n @cell-edit-complete=\"onCellEditComplete\"\n >\n <template #header>\n <div class=\"w-crud-toolbar\">\n <div class=\"w-crud-toolbar-start\">\n <IconField v-if=\"showSearch\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n :model-value=\"crud.search.value\"\n placeholder=\"Buscar...\"\n class=\"w-72\"\n @input=\"crud.onSearch\"\n />\n </IconField>\n <slot name=\"toolbar-start\" />\n <slot name=\"toolbar-filters\" />\n </div>\n <div class=\"w-crud-toolbar-end\">\n <slot name=\"toolbar-actions\" />\n <template v-if=\"persistState\">\n <Button\n v-tooltip.top=\"'Colunas'\"\n icon=\"pi pi-sliders-h\"\n text\n size=\"small\"\n @click=\"(e) => chooser?.toggle(e)\"\n />\n <Popover ref=\"chooser\">\n <div\n style=\"\n display: flex;\n flex-direction: column;\n gap: 0.35rem;\n min-width: 180px;\n \"\n >\n <div\n v-for=\"col in baseColumns\"\n :key=\"col.field\"\n style=\"display: flex; align-items: center; gap: 0.5rem\"\n >\n <Checkbox\n :model-value=\"!columnState.hidden.includes(col.field)\"\n :input-id=\"`wcrud-col-${col.field}`\"\n binary\n @update:model-value=\"(v) => toggleColumn(col.field, !!v)\"\n />\n <label\n :for=\"`wcrud-col-${col.field}`\"\n style=\"font-size: 0.85rem; cursor: pointer\"\n >\n {{ col.header }}\n </label>\n </div>\n </div>\n </Popover>\n </template>\n <Button\n v-if=\"exportCsv\"\n v-tooltip.top=\"\n csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\n \"\n icon=\"pi pi-download\"\n text\n size=\"small\"\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n <div v-if=\"viewToggle\" class=\"w-crud-view-toggle\">\n <Button\n icon=\"pi pi-table\"\n size=\"small\"\n :text=\"!isView('table')\"\n :outlined=\"isView('table')\"\n @click=\"setView('table')\"\n />\n <Button\n icon=\"pi pi-th-large\"\n size=\"small\"\n :text=\"!isView('cards')\"\n :outlined=\"isView('cards')\"\n @click=\"setView('cards')\"\n />\n </div>\n <Button\n v-if=\"!showHeader && canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n </template>\n\n <template #empty>\n <slot name=\"empty\">\n <div class=\"w-crud-empty\">\n <div class=\"w-crud-empty-icon\">\n <i class=\"pi pi-inbox\" />\n </div>\n <p class=\"w-crud-empty-title\">Nenhum registro encontrado</p>\n <p class=\"w-crud-empty-text\">Tente ajustar sua busca ou crie um novo registro</p>\n </div>\n </slot>\n </template>\n\n <!-- Seleção múltipla (checkbox) -->\n <Column v-if=\"isMultiSelect\" selection-mode=\"multiple\" style=\"width: 3rem\" />\n\n <!-- Expander -->\n <Column v-if=\"expandable\" expander style=\"width: 3rem\" />\n\n <!-- Data columns -->\n <Column\n v-for=\"col in visibleColumns\"\n :key=\"col.field\"\n :field=\"col.field\"\n :header=\"col.header\"\n :sortable=\"col.sortable\"\n :style=\"col.style\"\n :header-class=\"colAlignClass(col)\"\n :body-class=\"colAlignClass(col)\"\n >\n <template #body=\"{ data }\">\n <slot :name=\"`column-${col.field}`\" :data=\"data\" :value=\"data[col.field]\">\n <WCrudColumnRenderer :column=\"col\" :value=\"data[col.field]\" :row-data=\"data\" />\n </slot>\n </template>\n <template v-if=\"isCellEdit && inlineField(col.field)\" #editor=\"{ data }\">\n <InputNumber\n v-if=\"['number', 'currency'].includes(inlineField(col.field)?.type || '')\"\n :model-value=\"(data[col.field] as number) ?? null\"\n fluid\n @update:model-value=\"\n (v) => {\n data[col.field] = v\n }\n \"\n />\n <InputText\n v-else\n :model-value=\"(data[col.field] as string) ?? ''\"\n fluid\n @update:model-value=\"\n (v) => {\n data[col.field] = v\n }\n \"\n />\n </template>\n </Column>\n\n <!-- Actions column (oculta quando a action rail está ativa) -->\n <Column\n v-if=\"hasActions && !actionRail\"\n header-class=\"w-crud-actions-header\"\n :style=\"{\n width: `${(effectiveRowActions.length + (slots['row-actions'] ? 1 : 0)) * 2.5 + 1}rem`,\n }\"\n >\n <template #body=\"{ data }\">\n <div class=\"w-crud-actions\">\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"isActionVisible(action, data)\"\n v-tooltip.top=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n size=\"small\"\n :severity=\"action.severity as any\"\n :disabled=\"isActionDisabled(action, data)\"\n @click=\"handleRowAction(action, data)\"\n />\n </template>\n <slot name=\"row-actions\" :data=\"data\" :crud=\"crud\" />\n </div>\n </template>\n </Column>\n\n <!-- Expansion -->\n <template v-if=\"expandable\" #expansion=\"slotProps\">\n <slot name=\"expansion\" :data=\"slotProps.data\" />\n </template>\n </DataTable>\n </div>\n\n <!-- Cards view -->\n <div v-else class=\"w-crud-cards-wrap\">\n <div class=\"w-crud-toolbar w-crud-toolbar--standalone\">\n <div class=\"w-crud-toolbar-start\">\n <IconField v-if=\"showSearch\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n :model-value=\"crud.search.value\"\n placeholder=\"Buscar...\"\n class=\"w-72\"\n @input=\"crud.onSearch\"\n />\n </IconField>\n <slot name=\"toolbar-start\" />\n <slot name=\"toolbar-filters\" />\n </div>\n <div class=\"w-crud-toolbar-end\">\n <slot name=\"toolbar-actions\" />\n <Button\n v-if=\"exportCsv\"\n v-tooltip.top=\"csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\"\n icon=\"pi pi-download\"\n text\n size=\"small\"\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n <div v-if=\"viewToggle\" class=\"w-crud-view-toggle\">\n <Button\n icon=\"pi pi-table\"\n size=\"small\"\n :text=\"!isView('table')\"\n :outlined=\"isView('table')\"\n @click=\"setView('table')\"\n />\n <Button\n icon=\"pi pi-th-large\"\n size=\"small\"\n :text=\"!isView('cards')\"\n :outlined=\"isView('cards')\"\n @click=\"setView('cards')\"\n />\n </div>\n <Button\n v-if=\"!showHeader && canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n\n <div v-if=\"crud.loading.value\" class=\"w-crud-cards-loading\">\n <i class=\"pi pi-spin pi-spinner\" />\n </div>\n\n <slot v-else-if=\"!crud.items.value.length\" name=\"empty\">\n <div class=\"w-crud-empty\">\n <div class=\"w-crud-empty-icon\">\n <i class=\"pi pi-inbox\" />\n </div>\n <p class=\"w-crud-empty-title\">Nenhum registro encontrado</p>\n <p class=\"w-crud-empty-text\">Tente ajustar sua busca ou crie um novo registro</p>\n </div>\n </slot>\n\n <div v-else class=\"w-crud-cards\" :class=\"{ 'w-crud-cards--bare': cardBare }\">\n <div\n v-for=\"(row, idx) in crud.items.value\"\n :key=\"(row[crud.config.pk || 'id'] as string | number) ?? idx\"\n :class=\"\n cardBare\n ? 'w-crud-card-bare'\n : ['w-crud-card', { 'w-crud-card--selected': selectedRow === row }]\n \"\n @click=\"!cardBare && selectRow(row)\"\n @dblclick=\"!cardBare && crud.config.canEdit !== false && crud.openEditDialog(row)\"\n @contextmenu=\"!cardBare && onCardContextMenu($event, row)\"\n >\n <!-- Slot `card`: assume o CONTEÚDO do card (corpo + ações), mantendo o\n wrapper do WCrudView (chrome, seleção, dblclick-editar, contextmenu,\n toolbar/loading/empty/paginação). Sem o slot, cai no card padrão\n (título + label:valor das `cardColumns` + ações de linha). -->\n <slot name=\"card\" :data=\"row\" :crud=\"crud\" :columns=\"cardColumns\" :index=\"idx\">\n <div class=\"w-crud-card-body\">\n <div\n v-for=\"(col, ci) in cardColumns\"\n :key=\"col.field\"\n class=\"w-crud-card-row\"\n :class=\"{ 'w-crud-card-row--title': ci === 0 }\"\n >\n <span v-if=\"ci !== 0\" class=\"w-crud-card-label\">{{ col.header }}</span>\n <span class=\"w-crud-card-value\">\n <slot :name=\"`column-${col.field}`\" :data=\"row\" :value=\"row[col.field]\">\n <WCrudColumnRenderer\n :column=\"col\"\n :value=\"row[col.field]\"\n :row-data=\"row\"\n />\n </slot>\n </span>\n </div>\n </div>\n <div v-if=\"hasActions && !actionRail\" class=\"w-crud-card-actions\">\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"isActionVisible(action, row)\"\n v-tooltip.top=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n size=\"small\"\n :severity=\"action.severity as any\"\n :disabled=\"isActionDisabled(action, row)\"\n @click=\"handleRowAction(action, row)\"\n />\n </template>\n <slot name=\"row-actions\" :data=\"row\" :crud=\"crud\" />\n </div>\n </slot>\n </div>\n </div>\n\n <Paginator\n v-if=\"crud.items.value.length\"\n :rows=\"crud.pagination.pageSize\"\n :total-records=\"crud.pagination.rows\"\n :first=\"paginatorFirst\"\n :rows-per-page-options=\"[10, 20, 50]\"\n template=\"CurrentPageReport PrevPageLink NextPageLink\"\n current-page-report-template=\"Página {currentPage} de {totalPages}\"\n class=\"w-crud-paginator\"\n @page=\"crud.onPage\"\n />\n </div>\n </div>\n <!-- /.w-crud-content-main -->\n\n <!-- Floating action rail (todas as actions vivem aqui) -->\n <aside v-if=\"actionRail\" class=\"w-crud-rail\">\n <Button\n v-if=\"canCreate\"\n v-tooltip.left=\"'Novo'\"\n icon=\"pi pi-plus\"\n rounded\n @click=\"crud.openCreateDialog()\"\n />\n <div v-if=\"canCreate && effectiveRowActions.length\" class=\"w-crud-rail-sep\" />\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"!selectedRow || isActionVisible(action, selectedRow)\"\n v-tooltip.left=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n :severity=\"action.severity as any\"\n :disabled=\"!selectedRow || isActionDisabled(action, selectedRow)\"\n @click=\"selectedRow && handleRowAction(action, selectedRow)\"\n />\n </template>\n <slot name=\"rail-actions\" :selected=\"selectedRow\" :crud=\"crud\" />\n <div v-if=\"showPrint || exportCsv\" class=\"w-crud-rail-sep\" />\n <Button\n v-if=\"showPrint\"\n v-tooltip.left=\"'Imprimir'\"\n icon=\"pi pi-print\"\n text\n rounded\n :disabled=\"!selectedRow\"\n @click=\"railPrint\"\n />\n <Button\n v-if=\"exportCsv\"\n v-tooltip.left=\"csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\"\n icon=\"pi pi-download\"\n text\n rounded\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n </aside>\n </div>\n <!-- /.w-crud-content -->\n\n <!-- Context menu -->\n <!-- Transiente (fecha no clique fora): fica no body — o absolutePosition\n do PrimeVue calcula contra o documento e um container posicionado o\n desloca. Só dialog/confirm ancoram no pane. -->\n <ContextMenu v-if=\"contextMenu\" ref=\"cm\" :model=\"contextMenuItems\" />\n\n <!-- Form Dialog -->\n <slot name=\"form-dialog\" :crud=\"crud\" :dialog-width=\"dialogWidth\">\n <WCrudFormDialog\n :visible=\"crud.dialogVisible.value\"\n :title=\"crud.dialogTitle.value\"\n :fields=\"crud.config.form\"\n :form-data=\"crud.formData\"\n :is-editing=\"crud.isEditing.value\"\n :saving=\"crud.saving.value\"\n :disabled=\"crud.viewMode?.value ?? false\"\n :width=\"dialogWidth\"\n :form-columns=\"formColumns ?? crud.config.formColumns\"\n :keyboard-nav=\"crud.config.keyboardNav\"\n @update:visible=\"\n (v) => {\n crud.dialogVisible.value = v\n if (!v) crud.editingItem.value = null\n }\n \"\n @update:field=\"(field, val) => crud.setFormField(field, val)\"\n @save=\"crud.save()\"\n >\n <template\n v-for=\"field in crud.config.form\"\n :key=\"field.field\"\n #[`field-${field.field}`]=\"scope\"\n >\n <slot :name=\"`field-${field.field}`\" v-bind=\"scope\" />\n </template>\n </WCrudFormDialog>\n </slot>\n\n <!-- Diálogo de exclusão próprio: renderizado só quando o consumidor fornece\n o slot `delete-message` (permite conteúdo rico por registro). -->\n <Dialog\n v-if=\"$slots['delete-message']\"\n v-model:visible=\"deleteDialogVisible\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n header=\"Confirmar Exclusão\"\n modal\n :draggable=\"false\"\n class=\"w-crud-delete-dialog\"\n :style=\"{ width: '30rem' }\"\n >\n <slot name=\"delete-message\" :item=\"deleteTarget\" />\n <template #footer>\n <Button\n type=\"button\"\n label=\"Cancelar\"\n severity=\"secondary\"\n text\n :disabled=\"deleting\"\n @click=\"deleteDialogVisible = false\"\n />\n <Button\n type=\"button\"\n label=\"Excluir\"\n icon=\"pi pi-trash\"\n severity=\"danger\"\n :loading=\"deleting\"\n @click=\"onConfirmDelete\"\n />\n </template>\n </Dialog>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, reactive, computed, onMounted, watch, useSlots } from 'vue'\nimport DataTable from 'primevue/datatable'\nimport Column from 'primevue/column'\nimport Button from 'primevue/button'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Select from 'primevue/select'\nimport Popover from 'primevue/popover'\nimport Checkbox from 'primevue/checkbox'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Paginator from 'primevue/paginator'\nimport ContextMenu from 'primevue/contextmenu'\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\nimport WCrudColumnRenderer from './WCrudColumnRenderer.vue'\nimport WCrudFormDialog from './WCrudFormDialog.vue'\nimport type { CrudManagerReturn } from '@/types/manager'\nimport type { RowAction } from '@/types/action'\nimport type { ColumnDef } from '@/types/column'\nimport type { FieldDef } from '@/types/field'\nimport type { KpiItem } from '@/types/kpi'\nimport type { MenuItem } from 'primevue/menuitem'\nimport type { Slots } from 'vue'\nimport { useFormatters } from '@/composables/useFormatters'\nimport { toCsv, downloadCsv } from '@/utils/csv'\n\n// Dentro da navegação por abas, o dialog pendura no pane da própria aba\n// (some e volta com ela, intacto); fora de abas, o body de sempre.\nconst tabHost = useTabHost()\n\nconst props = withDefaults(\n defineProps<{\n // WCrudView é agnóstico ao tipo do registro (trata linhas como Record<string, unknown>\n // internamente); a prop aceita o CrudManagerReturn de qualquer T do consumidor.\n // TODO(tipagem): tornar o componente genérico <T> e tipar selectedRow/eventos.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n crud: CrudManagerReturn<any>\n title: string\n subtitle?: string\n showSearch?: boolean\n showHeader?: boolean\n dialogWidth?: string\n /** Nº de colunas do grid do form dialog (default: `formColumns` da config\n * do useCrudManager, senão 2). */\n formColumns?: number\n autoInit?: boolean\n showKpi?: boolean\n kpiIcon?: string\n kpiLabel?: string\n extraKpis?: KpiItem[]\n expandable?: boolean\n viewToggle?: boolean\n defaultView?: 'table' | 'cards'\n cardFields?: number\n /** Cards \"sem moldura\": remove o chrome padrão do card (borda/sombra/padding/\n * seleção) para o slot `#card` desenhar o card inteiro (ex.: um card de design\n * próprio). O wrapper vira só a célula do grid. */\n cardBare?: boolean\n actionRail?: boolean\n contextMenu?: boolean\n showPrint?: boolean\n exportCsv?: boolean\n csvFilename?: string\n csvScope?: 'all' | 'page'\n csvPageSize?: number\n /** Chave de persistência do estado de coluna (visibilidade) em localStorage.\n * Opt-in: quando definida, exibe o seletor de colunas e persiste o que foi ocultado. */\n persistState?: string\n }>(),\n {\n showSearch: true,\n showHeader: true,\n dialogWidth: '480px',\n autoInit: true,\n showKpi: false,\n kpiIcon: 'pi pi-list',\n kpiLabel: 'Total de Registros',\n extraKpis: () => [],\n expandable: false,\n viewToggle: true,\n defaultView: 'table',\n cardFields: 4,\n actionRail: true,\n contextMenu: true,\n showPrint: false,\n exportCsv: true,\n csvFilename: 'export.csv',\n csvScope: 'all',\n csvPageSize: 200,\n },\n)\n\nconst emit = defineEmits<{\n 'row-expand': [data: unknown]\n 'row-collapse': [data: unknown]\n print: [data: Record<string, unknown>]\n}>()\n\nconst slots: Slots = useSlots()\nconst { formatNumber } = useFormatters()\n\nconst expandedRows = ref({})\n\n// --- Display mode (table / cards) ---\n\n// Persistência do modo de visualização (opt-in via `persistState`): a escolha do\n// usuário fica em localStorage sob a MESMA chave base do grid (rota + grid, provida\n// pelo consumidor). Leitura síncrona no setup evita flash table→cards. Default =\n// `defaultView` (por padrão 'table' = linha).\nfunction viewKey(): string | null {\n return props.persistState ? `wcrud:view:${props.persistState}` : null\n}\nfunction loadView(): 'table' | 'cards' {\n const key = viewKey()\n if (key) {\n const stored = localStorage.getItem(key)\n if (stored === 'table' || stored === 'cards') return stored\n }\n return props.defaultView\n}\nconst displayMode = ref<'table' | 'cards'>(loadView())\nfunction isView(mode: 'table' | 'cards'): boolean {\n return displayMode.value === mode\n}\nfunction setView(mode: 'table' | 'cards'): void {\n displayMode.value = mode\n const key = viewKey()\n if (key) localStorage.setItem(key, mode)\n}\nconst paginatorFirst = computed(\n () => (props.crud.pagination.page - 1) * props.crud.pagination.pageSize,\n)\n\n// --- Columns ---\n\n// Estado de coluna (opt-in via persistState): visibilidade persistida em localStorage.\nconst columnState = reactive<{ hidden: string[] }>({ hidden: [] })\nconst chooser = ref<InstanceType<typeof Popover> | null>(null)\nfunction colStateKey(): string | null {\n return props.persistState ? `wcrud:cols:${props.persistState}` : null\n}\nfunction loadColumnState() {\n const key = colStateKey()\n if (!key) return\n try {\n const parsed = JSON.parse(localStorage.getItem(key) || '{}')\n if (Array.isArray(parsed.hidden)) columnState.hidden = parsed.hidden\n } catch {\n /* estado inválido — ignora */\n }\n}\nfunction saveColumnState() {\n const key = colStateKey()\n if (key) localStorage.setItem(key, JSON.stringify({ hidden: columnState.hidden }))\n}\n\n// Estado de FILTROS (opt-in via persistState): filtros de coluna + busca ficam em\n// localStorage sob a mesma chave base do grid (rota + grid). Restaurados ANTES do\n// primeiro fetch, então o F5 mantém os últimos filtros aplicados.\nfunction filtersKey(): string | null {\n return props.persistState ? `wcrud:filters:${props.persistState}` : null\n}\nfunction loadFilters() {\n const key = filtersKey()\n if (!key) return\n try {\n const parsed = JSON.parse(localStorage.getItem(key) || '{}')\n if (parsed && typeof parsed.columnFilters === 'object' && parsed.columnFilters) {\n for (const [k, v] of Object.entries(parsed.columnFilters)) {\n if (v !== null && v !== undefined && v !== '') props.crud.columnFilters[k] = v\n }\n }\n if (typeof parsed.search === 'string' && parsed.search) {\n props.crud.search.value = parsed.search\n }\n } catch {\n /* estado inválido — ignora */\n }\n}\nfunction saveFilters() {\n const key = filtersKey()\n if (!key) return\n localStorage.setItem(\n key,\n JSON.stringify({\n columnFilters: { ...props.crud.columnFilters },\n search: props.crud.search.value,\n }),\n )\n}\nwatch(props.crud.columnFilters, saveFilters, { deep: true })\nwatch(props.crud.search, saveFilters)\n// Colunas base (respeita visible:false do consumidor) — fonte do seletor de colunas.\nconst baseColumns = computed(() => props.crud.config.columns.filter((c) => c.visible !== false))\nfunction toggleColumn(field: string, visible: boolean) {\n columnState.hidden = visible\n ? columnState.hidden.filter((f) => f !== field)\n : [...new Set([...columnState.hidden, field])]\n saveColumnState()\n}\n\nconst visibleColumns = computed(() =>\n props.crud.config.columns\n .filter((c) => c.visible !== false && !columnState.hidden.includes(c.field))\n .map((c) => {\n if (c.type === 'number' && !c.align) return { ...c, align: 'right' as const }\n if (c.type === 'currency' && !c.align) return { ...c, align: 'right' as const }\n return c\n }),\n)\n\nfunction colAlignClass(col: { align?: string }) {\n if (col.align === 'right') return 'text-right'\n if (col.align === 'center') return 'text-center'\n return undefined\n}\n\nconst cardColumns = computed(() => visibleColumns.value.slice(0, props.cardFields))\n\n// --- Row Actions ---\n\nconst defaultActions = computed<RowAction[]>(() => {\n const actions: RowAction[] = []\n if (props.crud.config.canEdit !== false) {\n actions.push({ action: 'edit', icon: 'pi pi-pencil', tooltip: 'Editar' })\n }\n if (props.crud.config.canCreate !== false) {\n actions.push({\n action: 'duplicate',\n icon: 'pi pi-copy',\n tooltip: 'Duplicar',\n severity: 'info',\n })\n }\n if (props.crud.config.canDelete !== false) {\n actions.push({\n action: 'delete',\n icon: 'pi pi-trash',\n tooltip: 'Excluir',\n severity: 'danger',\n })\n }\n return actions\n})\n\nconst effectiveRowActions = computed<RowAction[]>(\n () => props.crud.config.rowActions ?? defaultActions.value,\n)\n\nconst hasActions = computed(\n () => effectiveRowActions.value.length > 0 || Boolean(slots['row-actions']),\n)\n\nfunction handleRowAction(action: RowAction, data: Record<string, unknown>) {\n // Custom handler tem prioridade — permite sobrescrever ações nomeadas.\n if (action.handler) {\n action.handler(data)\n return\n }\n if (action.action === 'edit') {\n props.crud.openEditDialog(data)\n } else if (action.action === 'view') {\n props.crud.openViewDialog(data)\n } else if (action.action === 'duplicate') {\n props.crud.openDuplicateDialog(data)\n } else if (action.action === 'delete') {\n // Com slot `delete-message`, usa o diálogo próprio (permite conteúdo rico,\n // ex.: avisos por registro); senão, o confirm de serviço padrão.\n if (slots['delete-message']) {\n deleteTarget.value = data\n deleteDialogVisible.value = true\n } else {\n props.crud.confirmDelete(data)\n }\n }\n}\n\n// --- Diálogo de exclusão próprio (só quando há slot `delete-message`) ---\nconst deleteTarget = ref<Record<string, unknown> | null>(null)\nconst deleteDialogVisible = ref(false)\nconst deleting = ref(false)\n\nasync function onConfirmDelete() {\n if (!deleteTarget.value) return\n deleting.value = true\n try {\n await props.crud.performDelete(deleteTarget.value)\n deleteDialogVisible.value = false\n deleteTarget.value = null\n } finally {\n deleting.value = false\n }\n}\n\nfunction isActionVisible(action: RowAction, data: Record<string, unknown>): boolean {\n if (action.visible) return action.visible(data)\n return true\n}\n\nfunction isActionDisabled(action: RowAction, data: Record<string, unknown>): boolean {\n if (action.disabled) return action.disabled(data)\n return false\n}\n\n// --- KPIs ---\n\nconst allKpis = computed<KpiItem[]>(() => {\n const kpis: KpiItem[] = []\n if (props.showKpi) {\n kpis.push({\n icon: props.kpiIcon,\n label: props.kpiLabel,\n value: formatNumber(props.crud.pagination.rows, 0),\n })\n }\n kpis.push(...props.extraKpis)\n return kpis\n})\n\nconst canCreate = computed(() => props.crud.config.canCreate !== false)\n\n// --- Selection (drives action rail + context menu) ---\n\n// Exposta como `v-model:selectedRow` para o consumidor sincronizar a seleção de\n// linha com outra visão do mesmo dado (ex.: mapa, gráfico). Sem o binding, o\n// defineModel age como ref local e nada muda para quem já usa o componente.\nconst selectedRow = defineModel<Record<string, unknown> | null>('selectedRow', {\n default: null,\n})\n\n// Seleção múltipla — opt-in via config.selectionMode: 'multiple'.\nconst isMultiSelect = computed(() => props.crud.config.selectionMode === 'multiple')\n\n// O tipo do PrimeVue não expõe `$el`; só precisamos do elemento raiz para o scroll.\nconst tableRef = ref<{ $el?: HTMLElement } | null>(null)\n\n/**\n * Rola a tabela até a linha cujo pk é `value` — o par do `v-model:selectedRow`\n * para o sentido \"de fora para dentro\" (selecionou no mapa/gráfico, a linha\n * aparece). Só alcança a página atual: linha em outra página não é buscada — é\n * do consumidor decidir o que fazer nesse caso (a lista é lazy). Com linhas de\n * expansão abertas o índice DOM diverge do índice do item; aí não rola.\n */\nfunction scrollToRow(value: unknown): void {\n const pk = props.crud.config.pk || 'id'\n const index = props.crud.items.value.findIndex(\n (row: Record<string, unknown>) => row[pk] === value,\n )\n if (index < 0) return\n const root = tableRef.value?.$el as HTMLElement | undefined\n const rows = root?.querySelectorAll<HTMLElement>('tbody > tr')\n if (!rows || rows.length !== props.crud.items.value.length) return\n rows[index]?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })\n}\n\ndefineExpose({ scrollToRow })\n\n// Edição inline por célula — opt-in via config.editMode: 'cell'.\nconst isCellEdit = computed(() => props.crud.config.editMode === 'cell')\nconst formFieldMap = computed(() => new Map(props.crud.config.form.map((f) => [f.field, f])))\nconst INLINE_TYPES = ['text', 'email', 'number', 'currency']\nfunction inlineField(field: string): FieldDef | undefined {\n const f = formFieldMap.value.get(field)\n return f && INLINE_TYPES.includes(f.type || 'text') ? f : undefined\n}\nfunction onCellEditComplete(e: {\n data: Record<string, unknown>\n field: string\n value?: unknown\n newValue: unknown\n}) {\n if (e.newValue === (e.value ?? e.data[e.field])) return\n props.crud.updateField(e.data, e.field, e.newValue)\n}\n\n// Filtros declarativos de coluna.\nconst filterableColumns = computed<ColumnDef[]>(() =>\n props.crud.config.columns.filter((c) => c.filter),\n)\nconst booleanFilterOptions = [\n { label: 'Sim', value: true },\n { label: 'Não', value: false },\n]\nfunction fParam(col: ColumnDef): string {\n return col.filter?.param || col.field\n}\nconst filterTimers: Record<string, ReturnType<typeof setTimeout>> = {}\nfunction onTextFilter(col: ColumnDef, value: string) {\n const param = fParam(col)\n if (filterTimers[param]) clearTimeout(filterTimers[param])\n filterTimers[param] = setTimeout(() => props.crud.setColumnFilter(param, value || null), 300)\n}\nconst cm = ref<InstanceType<typeof ContextMenu> | null>(null)\n\nfunction selectRow(row: Record<string, unknown>) {\n selectedRow.value = row\n}\n\nfunction onRowContextMenu(event: { originalEvent: Event; data: Record<string, unknown> }) {\n if (!props.contextMenu) return\n selectedRow.value = event.data\n cm.value?.show(event.originalEvent)\n}\n\nfunction onCardContextMenu(event: MouseEvent, row: Record<string, unknown>) {\n if (!props.contextMenu) return\n event.preventDefault()\n selectedRow.value = row\n cm.value?.show(event)\n}\n\nconst contextMenuItems = computed<MenuItem[]>(() => {\n const row = selectedRow.value\n if (!row) return []\n const items: MenuItem[] = [\n {\n label: 'Ver detalhes',\n icon: 'pi pi-eye',\n command: () => props.crud.openViewDialog(row),\n },\n ]\n // Todas as actions (default + customizadas) entram aqui.\n for (const action of effectiveRowActions.value) {\n if (!isActionVisible(action, row)) continue\n items.push({\n label: action.tooltip ?? action.action,\n icon: action.icon,\n class: action.severity === 'danger' ? 'w-crud-ctx-danger' : undefined,\n disabled: isActionDisabled(action, row),\n command: () => handleRowAction(action, row),\n })\n }\n if (props.showPrint) {\n items.push({\n label: 'Imprimir',\n icon: 'pi pi-print',\n command: () => emit('print', row),\n })\n }\n if (props.exportCsv) {\n items.push({ separator: true })\n items.push({\n label: props.csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)',\n icon: 'pi pi-download',\n command: () => doExportCsv(),\n })\n }\n return items\n})\n\n// --- Action rail handlers ---\n\nfunction railPrint() {\n if (selectedRow.value) emit('print', selectedRow.value)\n}\n\n// --- CSV export (visible columns) ---\n\nconst exporting = ref(false)\n\nasync function doExportCsv() {\n if (exporting.value) return\n exporting.value = true\n try {\n const rows =\n props.csvScope === 'page'\n ? props.crud.items.value\n : await props.crud.fetchAll(props.csvPageSize)\n const csv = toCsv(rows, visibleColumns.value)\n downloadCsv(csv, props.csvFilename)\n } finally {\n exporting.value = false\n }\n}\n\n// --- Init ---\n\nonMounted(() => {\n loadColumnState()\n loadFilters() // restaura filtros/busca ANTES do init para o fetch já sair filtrado\n if (props.autoInit) {\n props.crud.init()\n }\n})\n</script>\n\n<template>\n <div class=\"w-crud\">\n <!-- Header -->\n <div v-if=\"showHeader\" class=\"w-crud-header\">\n <div class=\"w-crud-header-content\">\n <h1 class=\"w-crud-title\">{{ title }}</h1>\n <p v-if=\"subtitle\" class=\"w-crud-subtitle\">{{ subtitle }}</p>\n </div>\n <div class=\"w-crud-header-actions\">\n <slot name=\"header-actions\" />\n <Button\n v-if=\"canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n\n <!-- KPIs -->\n <slot name=\"before-table\">\n <div v-if=\"allKpis.length\" class=\"w-crud-kpis\">\n <div v-for=\"(kpi, idx) in allKpis\" :key=\"idx\" class=\"w-crud-kpi\">\n <div :class=\"['w-crud-kpi-icon', kpi.severity ? `w-crud-kpi-icon--${kpi.severity}` : '']\">\n <i :class=\"[kpi.icon]\" :style=\"kpi.color ? `color: ${kpi.color}` : ''\" />\n </div>\n <div class=\"w-crud-kpi-content\">\n <div class=\"w-crud-kpi-label\">{{ kpi.label }}</div>\n <div class=\"w-crud-kpi-value\">{{ kpi.value }}</div>\n </div>\n </div>\n </div>\n </slot>\n\n <!-- Content (table/cards + optional action rail) -->\n <div class=\"w-crud-content\" :class=\"{ 'w-crud-content--rail': actionRail }\">\n <div class=\"w-crud-content-main\">\n <!-- Barra de filtros de coluna (declarativos, opt-in via ColumnDef.filter) -->\n <div\n v-if=\"filterableColumns.length\"\n class=\"w-crud-filters\"\n style=\"\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n align-items: center;\n margin-bottom: 0.75rem;\n \"\n >\n <template v-for=\"col in filterableColumns\" :key=\"col.field\">\n <InputText\n v-if=\"(col.filter?.type || 'text') === 'text'\"\n :model-value=\"(crud.columnFilters[fParam(col)] as string) ?? ''\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n size=\"small\"\n @input=\"(e: Event) => onTextFilter(col, (e.target as HTMLInputElement).value)\"\n />\n <Select\n v-else-if=\"col.filter?.type === 'select'\"\n :model-value=\"crud.columnFilters[fParam(col)]\"\n :options=\"col.filter?.options\"\n option-label=\"label\"\n option-value=\"value\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n show-clear\n size=\"small\"\n @update:model-value=\"(v: unknown) => crud.setColumnFilter(fParam(col), v)\"\n />\n <Select\n v-else-if=\"col.filter?.type === 'boolean'\"\n :model-value=\"crud.columnFilters[fParam(col)]\"\n :options=\"booleanFilterOptions\"\n option-label=\"label\"\n option-value=\"value\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n show-clear\n size=\"small\"\n @update:model-value=\"(v: unknown) => crud.setColumnFilter(fParam(col), v)\"\n />\n <InputNumber\n v-else-if=\"col.filter?.type === 'numeric'\"\n :model-value=\"(crud.columnFilters[fParam(col)] as number) ?? null\"\n :placeholder=\"col.filter?.placeholder || col.header\"\n size=\"small\"\n @update:model-value=\"(v: number) => crud.setColumnFilter(fParam(col), v)\"\n />\n </template>\n <Button\n v-if=\"Object.keys(crud.columnFilters).length\"\n label=\"Limpar filtros\"\n icon=\"pi pi-filter-slash\"\n text\n size=\"small\"\n @click=\"crud.clearColumnFilters()\"\n />\n </div>\n\n <!-- Table -->\n <div v-if=\"displayMode === 'table'\" class=\"w-crud-table\">\n <!-- Barra de ações em lote (seleção múltipla) -->\n <div\n v-if=\"isMultiSelect && crud.selectedItems.value.length\"\n class=\"w-crud-bulkbar\"\n style=\"\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n border-radius: 8px;\n background: var(--p-primary-50, #eef2ff);\n color: var(--p-primary-700, #3730a3);\n \"\n >\n <span style=\"font-weight: 600; font-size: 0.85rem; margin-right: auto\">\n {{ crud.selectedItems.value.length }} selecionado(s)\n </span>\n <slot name=\"bulk-actions\" :selected=\"crud.selectedItems.value\" :crud=\"crud\">\n <Button\n v-for=\"ba in crud.config.bulkActions || []\"\n :key=\"ba.action\"\n :label=\"ba.label\"\n :icon=\"ba.icon\"\n :severity=\"ba.severity\"\n size=\"small\"\n :disabled=\"ba.disabled ? ba.disabled(crud.selectedItems.value) : false\"\n @click=\"ba.handler(crud.selectedItems.value)\"\n />\n </slot>\n <Button\n v-tooltip.top=\"'Limpar seleção'\"\n icon=\"pi pi-times\"\n text\n rounded\n size=\"small\"\n @click=\"crud.clearSelection()\"\n />\n </div>\n <DataTable\n ref=\"tableRef\"\n v-model:expanded-rows=\"expandedRows\"\n :value=\"crud.items.value\"\n :loading=\"crud.loading.value\"\n paginator\n :rows=\"crud.pagination.pageSize\"\n :total-records=\"crud.pagination.rows\"\n :rows-per-page-options=\"[10, 20, 50]\"\n paginator-template=\"CurrentPageReport PrevPageLink NextPageLink\"\n current-page-report-template=\"Página {currentPage} de {totalPages}\"\n :pt=\"{ pcPaginator: { root: { class: 'w-crud-paginator' } } }\"\n lazy\n striped-rows\n removable-sort\n size=\"small\"\n :sort-field=\"crud.sort.field ?? undefined\"\n :sort-order=\"crud.sort.order\"\n :data-key=\"crud.config.pk || 'id'\"\n :edit-mode=\"isCellEdit ? 'cell' : undefined\"\n :selection=\"\n isMultiSelect\n ? crud.selectedItems.value\n : actionRail || contextMenu\n ? selectedRow\n : undefined\n \"\n :selection-mode=\"\n isMultiSelect ? undefined : actionRail || contextMenu ? 'single' : undefined\n \"\n :context-menu=\"contextMenu\"\n :context-menu-selection=\"contextMenu ? selectedRow : undefined\"\n @update:selection=\"\n (v: any) => (isMultiSelect ? (crud.selectedItems.value = v) : (selectedRow = v))\n \"\n @update:context-menu-selection=\"(v: any) => (selectedRow = v)\"\n @row-contextmenu=\"onRowContextMenu\"\n @page=\"crud.onPage\"\n @sort=\"(e: any) => crud.onSort({ sortField: e.sortField, sortOrder: e.sortOrder })\"\n @row-expand=\"(e) => emit('row-expand', e.data)\"\n @row-collapse=\"(e) => emit('row-collapse', e.data)\"\n @cell-edit-complete=\"onCellEditComplete\"\n >\n <template #header>\n <div class=\"w-crud-toolbar\">\n <div class=\"w-crud-toolbar-start\">\n <IconField v-if=\"showSearch\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n :model-value=\"crud.search.value\"\n placeholder=\"Buscar...\"\n class=\"w-72\"\n @input=\"crud.onSearch\"\n />\n </IconField>\n <slot name=\"toolbar-start\" />\n <slot name=\"toolbar-filters\" />\n </div>\n <div class=\"w-crud-toolbar-end\">\n <slot name=\"toolbar-actions\" />\n <template v-if=\"persistState\">\n <Button\n v-tooltip.top=\"'Colunas'\"\n icon=\"pi pi-sliders-h\"\n text\n size=\"small\"\n @click=\"(e) => chooser?.toggle(e)\"\n />\n <Popover ref=\"chooser\">\n <div\n style=\"\n display: flex;\n flex-direction: column;\n gap: 0.35rem;\n min-width: 180px;\n \"\n >\n <div\n v-for=\"col in baseColumns\"\n :key=\"col.field\"\n style=\"display: flex; align-items: center; gap: 0.5rem\"\n >\n <Checkbox\n :model-value=\"!columnState.hidden.includes(col.field)\"\n :input-id=\"`wcrud-col-${col.field}`\"\n binary\n @update:model-value=\"(v) => toggleColumn(col.field, !!v)\"\n />\n <label\n :for=\"`wcrud-col-${col.field}`\"\n style=\"font-size: 0.85rem; cursor: pointer\"\n >\n {{ col.header }}\n </label>\n </div>\n </div>\n </Popover>\n </template>\n <Button\n v-if=\"exportCsv\"\n v-tooltip.top=\"\n csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\n \"\n icon=\"pi pi-download\"\n text\n size=\"small\"\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n <div v-if=\"viewToggle\" class=\"w-crud-view-toggle\">\n <Button\n icon=\"pi pi-table\"\n size=\"small\"\n :text=\"!isView('table')\"\n :outlined=\"isView('table')\"\n @click=\"setView('table')\"\n />\n <Button\n icon=\"pi pi-th-large\"\n size=\"small\"\n :text=\"!isView('cards')\"\n :outlined=\"isView('cards')\"\n @click=\"setView('cards')\"\n />\n </div>\n <Button\n v-if=\"!showHeader && canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n </template>\n\n <template #empty>\n <slot name=\"empty\">\n <div class=\"w-crud-empty\">\n <div class=\"w-crud-empty-icon\">\n <i class=\"pi pi-inbox\" />\n </div>\n <p class=\"w-crud-empty-title\">Nenhum registro encontrado</p>\n <p class=\"w-crud-empty-text\">Tente ajustar sua busca ou crie um novo registro</p>\n </div>\n </slot>\n </template>\n\n <!-- Seleção múltipla (checkbox) -->\n <Column v-if=\"isMultiSelect\" selection-mode=\"multiple\" style=\"width: 3rem\" />\n\n <!-- Expander -->\n <Column v-if=\"expandable\" expander style=\"width: 3rem\" />\n\n <!-- Data columns -->\n <Column\n v-for=\"col in visibleColumns\"\n :key=\"col.field\"\n :field=\"col.field\"\n :header=\"col.header\"\n :sortable=\"col.sortable\"\n :style=\"col.style\"\n :header-class=\"colAlignClass(col)\"\n :body-class=\"colAlignClass(col)\"\n >\n <template #body=\"{ data }\">\n <slot :name=\"`column-${col.field}`\" :data=\"data\" :value=\"data[col.field]\">\n <WCrudColumnRenderer :column=\"col\" :value=\"data[col.field]\" :row-data=\"data\" />\n </slot>\n </template>\n <template v-if=\"isCellEdit && inlineField(col.field)\" #editor=\"{ data }\">\n <InputNumber\n v-if=\"['number', 'currency'].includes(inlineField(col.field)?.type || '')\"\n :model-value=\"(data[col.field] as number) ?? null\"\n fluid\n @update:model-value=\"\n (v) => {\n data[col.field] = v\n }\n \"\n />\n <InputText\n v-else\n :model-value=\"(data[col.field] as string) ?? ''\"\n fluid\n @update:model-value=\"\n (v) => {\n data[col.field] = v\n }\n \"\n />\n </template>\n </Column>\n\n <!-- Actions column (oculta quando a action rail está ativa) -->\n <Column\n v-if=\"hasActions && !actionRail\"\n header-class=\"w-crud-actions-header\"\n :style=\"{\n width: `${(effectiveRowActions.length + (slots['row-actions'] ? 1 : 0)) * 2.5 + 1}rem`,\n }\"\n >\n <template #body=\"{ data }\">\n <div class=\"w-crud-actions\">\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"isActionVisible(action, data)\"\n v-tooltip.top=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n size=\"small\"\n :severity=\"action.severity as any\"\n :disabled=\"isActionDisabled(action, data)\"\n @click=\"handleRowAction(action, data)\"\n />\n </template>\n <slot name=\"row-actions\" :data=\"data\" :crud=\"crud\" />\n </div>\n </template>\n </Column>\n\n <!-- Expansion -->\n <template v-if=\"expandable\" #expansion=\"slotProps\">\n <slot name=\"expansion\" :data=\"slotProps.data\" />\n </template>\n </DataTable>\n </div>\n\n <!-- Cards view -->\n <div v-else class=\"w-crud-cards-wrap\">\n <div class=\"w-crud-toolbar w-crud-toolbar--standalone\">\n <div class=\"w-crud-toolbar-start\">\n <IconField v-if=\"showSearch\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n :model-value=\"crud.search.value\"\n placeholder=\"Buscar...\"\n class=\"w-72\"\n @input=\"crud.onSearch\"\n />\n </IconField>\n <slot name=\"toolbar-start\" />\n <slot name=\"toolbar-filters\" />\n </div>\n <div class=\"w-crud-toolbar-end\">\n <slot name=\"toolbar-actions\" />\n <Button\n v-if=\"exportCsv\"\n v-tooltip.top=\"csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\"\n icon=\"pi pi-download\"\n text\n size=\"small\"\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n <div v-if=\"viewToggle\" class=\"w-crud-view-toggle\">\n <Button\n icon=\"pi pi-table\"\n size=\"small\"\n :text=\"!isView('table')\"\n :outlined=\"isView('table')\"\n @click=\"setView('table')\"\n />\n <Button\n icon=\"pi pi-th-large\"\n size=\"small\"\n :text=\"!isView('cards')\"\n :outlined=\"isView('cards')\"\n @click=\"setView('cards')\"\n />\n </div>\n <Button\n v-if=\"!showHeader && canCreate && !actionRail\"\n label=\"Novo\"\n icon=\"pi pi-plus\"\n @click=\"crud.openCreateDialog()\"\n />\n </div>\n </div>\n\n <div v-if=\"crud.loading.value\" class=\"w-crud-cards-loading\">\n <i class=\"pi pi-spin pi-spinner\" />\n </div>\n\n <slot v-else-if=\"!crud.items.value.length\" name=\"empty\">\n <div class=\"w-crud-empty\">\n <div class=\"w-crud-empty-icon\">\n <i class=\"pi pi-inbox\" />\n </div>\n <p class=\"w-crud-empty-title\">Nenhum registro encontrado</p>\n <p class=\"w-crud-empty-text\">Tente ajustar sua busca ou crie um novo registro</p>\n </div>\n </slot>\n\n <div v-else class=\"w-crud-cards\" :class=\"{ 'w-crud-cards--bare': cardBare }\">\n <div\n v-for=\"(row, idx) in crud.items.value\"\n :key=\"(row[crud.config.pk || 'id'] as string | number) ?? idx\"\n :class=\"\n cardBare\n ? 'w-crud-card-bare'\n : ['w-crud-card', { 'w-crud-card--selected': selectedRow === row }]\n \"\n @click=\"!cardBare && selectRow(row)\"\n @dblclick=\"!cardBare && crud.config.canEdit !== false && crud.openEditDialog(row)\"\n @contextmenu=\"!cardBare && onCardContextMenu($event, row)\"\n >\n <!-- Slot `card`: assume o CONTEÚDO do card (corpo + ações), mantendo o\n wrapper do WCrudView (chrome, seleção, dblclick-editar, contextmenu,\n toolbar/loading/empty/paginação). Sem o slot, cai no card padrão\n (título + label:valor das `cardColumns` + ações de linha). -->\n <slot name=\"card\" :data=\"row\" :crud=\"crud\" :columns=\"cardColumns\" :index=\"idx\">\n <div class=\"w-crud-card-body\">\n <div\n v-for=\"(col, ci) in cardColumns\"\n :key=\"col.field\"\n class=\"w-crud-card-row\"\n :class=\"{ 'w-crud-card-row--title': ci === 0 }\"\n >\n <span v-if=\"ci !== 0\" class=\"w-crud-card-label\">{{ col.header }}</span>\n <span class=\"w-crud-card-value\">\n <slot :name=\"`column-${col.field}`\" :data=\"row\" :value=\"row[col.field]\">\n <WCrudColumnRenderer\n :column=\"col\"\n :value=\"row[col.field]\"\n :row-data=\"row\"\n />\n </slot>\n </span>\n </div>\n </div>\n <div v-if=\"hasActions && !actionRail\" class=\"w-crud-card-actions\">\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"isActionVisible(action, row)\"\n v-tooltip.top=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n size=\"small\"\n :severity=\"action.severity as any\"\n :disabled=\"isActionDisabled(action, row)\"\n @click=\"handleRowAction(action, row)\"\n />\n </template>\n <slot name=\"row-actions\" :data=\"row\" :crud=\"crud\" />\n </div>\n </slot>\n </div>\n </div>\n\n <Paginator\n v-if=\"crud.items.value.length\"\n :rows=\"crud.pagination.pageSize\"\n :total-records=\"crud.pagination.rows\"\n :first=\"paginatorFirst\"\n :rows-per-page-options=\"[10, 20, 50]\"\n template=\"CurrentPageReport PrevPageLink NextPageLink\"\n current-page-report-template=\"Página {currentPage} de {totalPages}\"\n class=\"w-crud-paginator\"\n @page=\"crud.onPage\"\n />\n </div>\n </div>\n <!-- /.w-crud-content-main -->\n\n <!-- Floating action rail (todas as actions vivem aqui) -->\n <aside v-if=\"actionRail\" class=\"w-crud-rail\">\n <Button\n v-if=\"canCreate\"\n v-tooltip.left=\"'Novo'\"\n icon=\"pi pi-plus\"\n rounded\n @click=\"crud.openCreateDialog()\"\n />\n <div v-if=\"canCreate && effectiveRowActions.length\" class=\"w-crud-rail-sep\" />\n <template v-for=\"action in effectiveRowActions\" :key=\"action.action\">\n <Button\n v-if=\"!selectedRow || isActionVisible(action, selectedRow)\"\n v-tooltip.left=\"action.tooltip\"\n :icon=\"action.icon\"\n text\n rounded\n :severity=\"action.severity as any\"\n :disabled=\"!selectedRow || isActionDisabled(action, selectedRow)\"\n @click=\"selectedRow && handleRowAction(action, selectedRow)\"\n />\n </template>\n <slot name=\"rail-actions\" :selected=\"selectedRow\" :crud=\"crud\" />\n <div v-if=\"showPrint || exportCsv\" class=\"w-crud-rail-sep\" />\n <Button\n v-if=\"showPrint\"\n v-tooltip.left=\"'Imprimir'\"\n icon=\"pi pi-print\"\n text\n rounded\n :disabled=\"!selectedRow\"\n @click=\"railPrint\"\n />\n <Button\n v-if=\"exportCsv\"\n v-tooltip.left=\"csvScope === 'all' ? 'Exportar tudo (CSV)' : 'Exportar página (CSV)'\"\n icon=\"pi pi-download\"\n text\n rounded\n :loading=\"exporting\"\n @click=\"doExportCsv\"\n />\n </aside>\n </div>\n <!-- /.w-crud-content -->\n\n <!-- Context menu -->\n <!-- Transiente (fecha no clique fora): fica no body — o absolutePosition\n do PrimeVue calcula contra o documento e um container posicionado o\n desloca. Só dialog/confirm ancoram no pane. -->\n <ContextMenu v-if=\"contextMenu\" ref=\"cm\" :model=\"contextMenuItems\" />\n\n <!-- Form Dialog -->\n <slot name=\"form-dialog\" :crud=\"crud\" :dialog-width=\"dialogWidth\">\n <WCrudFormDialog\n :visible=\"crud.dialogVisible.value\"\n :title=\"crud.dialogTitle.value\"\n :fields=\"crud.config.form\"\n :form-data=\"crud.formData\"\n :is-editing=\"crud.isEditing.value\"\n :saving=\"crud.saving.value\"\n :disabled=\"crud.viewMode?.value ?? false\"\n :width=\"dialogWidth\"\n :form-columns=\"formColumns ?? crud.config.formColumns\"\n :keyboard-nav=\"crud.config.keyboardNav\"\n @update:visible=\"\n (v) => {\n crud.dialogVisible.value = v\n if (!v) crud.editingItem.value = null\n }\n \"\n @update:field=\"(field, val) => crud.setFormField(field, val)\"\n @save=\"crud.save()\"\n >\n <template\n v-for=\"field in crud.config.form\"\n :key=\"field.field\"\n #[`field-${field.field}`]=\"scope\"\n >\n <slot :name=\"`field-${field.field}`\" v-bind=\"scope\" />\n </template>\n </WCrudFormDialog>\n </slot>\n\n <!-- Diálogo de exclusão próprio: renderizado só quando o consumidor fornece\n o slot `delete-message` (permite conteúdo rico por registro). -->\n <Dialog\n v-if=\"$slots['delete-message']\"\n v-model:visible=\"deleteDialogVisible\"\n :append-to=\"tabHost?.hostEl.value ?? 'body'\"\n header=\"Confirmar Exclusão\"\n modal\n :draggable=\"false\"\n class=\"w-crud-delete-dialog\"\n :style=\"{ width: '30rem' }\"\n >\n <slot name=\"delete-message\" :item=\"deleteTarget\" />\n <template #footer>\n <Button\n type=\"button\"\n label=\"Cancelar\"\n severity=\"secondary\"\n text\n :disabled=\"deleting\"\n @click=\"deleteDialogVisible = false\"\n />\n <Button\n type=\"button\"\n label=\"Excluir\"\n icon=\"pi pi-trash\"\n severity=\"danger\"\n :loading=\"deleting\"\n @click=\"onConfirmDelete\"\n />\n </template>\n </Dialog>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, isRef, ref } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Select from 'primevue/select'\nimport { useFormatters } from '@/composables/useFormatters'\nimport type { EditableColumnDef, EditableRow } from '@/types/editableTable'\n\n/**\n * Tabela editável sobre ESTADO LOCAL — o oposto do WCrudView, que conversa com a\n * API a cada operação. Aqui nada é persistido: o consumidor mantém o array e\n * salva em bloco quando quiser (o caso \"documento com filhos\", em que salvar\n * linha a linha criaria registro órfão).\n *\n * As linhas nunca são mutadas no lugar: toda edição emite um array NOVO com a\n * linha trocada (`update:modelValue`), então `v-model` funciona igual em `ref`,\n * `reactive` ou prop vinda de cima.\n *\n * A subtabela de insumos do design é este mesmo componente dentro do slot\n * `#expansion` — não existe componente separado para \"tabela filha\".\n */\nconst props = withDefaults(\n defineProps<{\n /** Linhas (`v-model`). */\n modelValue?: EditableRow[]\n columns?: EditableColumnDef[]\n /** Liga a coluna de expansão e o slot `#expansion`. */\n expandable?: boolean\n /** Rótulo do botão de adicionar. Omitido = sem botão (só o slot `#toolbar`). */\n addLabel?: string\n /** Liga a coluna da lixeira. **Manda sozinha** na remoção: `disabled` trava as\n * células, não a linha (tabela de leitura de onde ainda se remove é caso real —\n * itens cujo CRUD acontece em dialog). Para travar tudo, `:removable=\"false\"`. */\n removable?: boolean\n emptyMessage?: string\n /** Rótulo da primeira célula do rodapé de totais. */\n footerLabel?: string\n /** Trava todos os editores (a tabela vira leitura) e o botão de adicionar. */\n disabled?: boolean\n /** Campo usado como `key` das linhas. Sem ele, a chave é o índice. */\n rowKey?: string\n }>(),\n {\n modelValue: () => [],\n columns: () => [],\n expandable: false,\n removable: false,\n emptyMessage: 'Nenhum item adicionado',\n footerLabel: 'Total',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [rows: EditableRow[]]\n add: []\n remove: [row: EditableRow, index: number]\n}>()\n\nconst { formatNumber } = useFormatters()\n\n// --- Expansão --------------------------------------------------------------\n// Índice das linhas abertas. Várias podem ficar abertas ao mesmo tempo: a\n// expansão é detalhe de leitura, não navegação.\nconst expanded = ref<Set<number>>(new Set())\n\nfunction isExpanded(index: number): boolean {\n return expanded.value.has(index)\n}\n\nfunction toggleRow(index: number) {\n const next = new Set(expanded.value)\n if (next.has(index)) next.delete(index)\n else next.add(index)\n expanded.value = next\n}\n\n// --- Colunas ---------------------------------------------------------------\n\nfunction unwrapOptions(column: EditableColumnDef): Record<string, unknown>[] {\n const options = column.options\n if (!options) return []\n return (isRef(options) ? options.value : options) as Record<string, unknown>[]\n}\n\nfunction columnAlign(column: EditableColumnDef): 'left' | 'center' | 'right' {\n if (column.align) return column.align\n return column.editor === 'number' ? 'right' : 'left'\n}\n\nfunction isNumericColumn(column: EditableColumnDef): boolean {\n return column.editor === 'number' || column.footer === 'sum' || column.decimals != null\n}\n\nfunction columnClass(column: EditableColumnDef): string[] {\n return [\n `w-editable-table__cell--${columnAlign(column)}`,\n ...(isNumericColumn(column) ? ['w-editable-table__cell--num'] : []),\n ]\n}\n\nfunction columnStyle(column: EditableColumnDef) {\n if (column.width == null) return undefined\n return { width: typeof column.width === 'number' ? `${column.width}px` : column.width }\n}\n\nfunction isCellDisabled(column: EditableColumnDef, row: EditableRow, index: number): boolean {\n if (props.disabled) return true\n if (typeof column.disabled === 'function') return column.disabled(row, index)\n return !!column.disabled\n}\n\n// --- Leitura / escrita de célula -------------------------------------------\n\nfunction toNumber(value: unknown): number {\n if (typeof value === 'number') return Number.isFinite(value) ? value : 0\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value.replace(/\\./g, '').replace(',', '.'))\n return Number.isFinite(parsed) ? parsed : 0\n }\n return 0\n}\n\nfunction displayValue(column: EditableColumnDef, row: EditableRow): string {\n const value = row[column.field]\n if (column.format) return column.format(value, row)\n if (value == null || value === '') return '—'\n if (isNumericColumn(column)) {\n return `${formatNumber(toNumber(value), column.decimals ?? 2)}${column.suffix ?? ''}`\n }\n return `${String(value)}${column.suffix ?? ''}`\n}\n\nfunction setCell(index: number, field: string, value: unknown) {\n const next = props.modelValue.map((row, i) => (i === index ? { ...row, [field]: value } : row))\n emit('update:modelValue', next)\n}\n\nfunction removeRow(row: EditableRow, index: number) {\n // Emite os dois: o array já sem a linha (para o v-model continuar sendo a\n // fonte da verdade) e o evento com a linha removida (para quem precisa dela).\n emit(\n 'update:modelValue',\n props.modelValue.filter((_, i) => i !== index),\n )\n expanded.value = new Set()\n emit('remove', row, index)\n}\n\nfunction rowKeyOf(row: EditableRow, index: number): string | number {\n if (props.rowKey) {\n const key = row[props.rowKey]\n if (typeof key === 'string' || typeof key === 'number') return key\n }\n return index\n}\n\n// --- Rodapé de totais ------------------------------------------------------\n\nconst hasFooter = computed(() =>\n props.columns.some((c) => typeof c.footer === 'function' || c.footer === 'sum'),\n)\n\nfunction footerValue(column: EditableColumnDef): string {\n if (typeof column.footer === 'function') return column.footer(props.modelValue)\n if (column.footer !== 'sum') return ''\n const total = props.modelValue.reduce((sum, row) => sum + toNumber(row[column.field]), 0)\n return `${formatNumber(total, column.decimals ?? 2)}${column.suffix ?? ''}`\n}\n\n/** A primeira coluna sem rodapé próprio carrega o rótulo \"Total\". */\nconst footerLabelField = computed(() => {\n const first = props.columns.find((c) => !c.footer || c.footer === 'none')\n return first?.field ?? null\n})\n\nconst columnCount = computed(\n () => props.columns.length + (props.expandable ? 1 : 0) + (props.removable ? 1 : 0),\n)\n\n// --- Grupos de cabeçalho ---------------------------------------------------\n// Colunas vizinhas com o mesmo `group` viram um rótulo único numa linha acima\n// do cabeçalho normal; as demais ficam com a célula de cima vazia.\n\nconst hasHeaderGroups = computed(() => props.columns.some((c) => c.group))\n\nconst headerGroupCells = computed(() => {\n const cells: { label: string; span: number; key: string }[] = []\n for (const column of props.columns) {\n const label = column.group ?? ''\n const last = cells[cells.length - 1]\n if (last && label && last.label === label) last.span += 1\n else cells.push({ label, span: 1, key: column.field })\n }\n return cells\n})\n</script>\n\n<template>\n <div class=\"w-editable-table\">\n <div v-if=\"$slots.toolbar || addLabel\" class=\"w-editable-table__toolbar\">\n <slot name=\"toolbar\">\n <button\n type=\"button\"\n class=\"w-editable-table__add\"\n :disabled=\"disabled\"\n @click=\"emit('add')\"\n >\n <i class=\"pi pi-plus\" />{{ addLabel }}\n </button>\n </slot>\n </div>\n\n <table class=\"w-editable-table__table\">\n <thead>\n <tr v-if=\"hasHeaderGroups\" class=\"w-editable-table__group-row\">\n <th v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <th\n v-for=\"cell in headerGroupCells\"\n :key=\"cell.key\"\n :colspan=\"cell.span\"\n class=\"w-editable-table__group\"\n :class=\"{ 'w-editable-table__group--label': cell.label }\"\n >\n {{ cell.label }}\n </th>\n <th v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n <tr>\n <th v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <th\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n {{ column.header }}\n </th>\n <th v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n </thead>\n\n <tbody v-if=\"!modelValue.length\">\n <tr>\n <td :colspan=\"columnCount\" class=\"w-editable-table__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </td>\n </tr>\n </tbody>\n\n <tbody v-for=\"(row, index) in modelValue\" :key=\"rowKeyOf(row, index)\">\n <!-- Zebra pelo índice, não por `:nth-child`: cada linha é um `<tbody>` próprio\n (por causa da expansão), então a contagem do CSS não enxergaria a\n alternância. -->\n <tr\n class=\"w-editable-table__row\"\n :class=\"{ 'w-editable-table__row--alt': index % 2 === 1 }\"\n >\n <td v-if=\"expandable\" class=\"w-editable-table__toggle-col\">\n <button\n type=\"button\"\n class=\"w-editable-table__toggle\"\n :aria-expanded=\"isExpanded(index)\"\n aria-label=\"Detalhes da linha\"\n @click=\"toggleRow(index)\"\n >\n <i :class=\"isExpanded(index) ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\" />\n </button>\n </td>\n\n <td\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n <slot\n :name=\"`cell-${column.field}`\"\n :row=\"row\"\n :index=\"index\"\n :value=\"row[column.field]\"\n :column=\"column\"\n >\n <InputNumber\n v-if=\"column.editor === 'number'\"\n :model-value=\"row[column.field] as number\"\n fluid\n locale=\"pt-BR\"\n :min=\"column.min\"\n :max=\"column.max\"\n :min-fraction-digits=\"column.decimals != null ? column.decimals : undefined\"\n :max-fraction-digits=\"column.decimals != null ? column.decimals : undefined\"\n :suffix=\"column.suffix\"\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n :input-class=\"'w-editable-table__num'\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <InputText\n v-else-if=\"column.editor === 'text'\"\n :model-value=\"row[column.field] as string\"\n fluid\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <Select\n v-else-if=\"column.editor === 'select'\"\n :model-value=\"row[column.field]\"\n fluid\n :options=\"unwrapOptions(column)\"\n :option-label=\"column.optionLabel || 'label'\"\n :option-value=\"column.optionValue || 'value'\"\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <span v-else class=\"w-editable-table__text\">{{ displayValue(column, row) }}</span>\n </slot>\n </td>\n\n <td v-if=\"removable\" class=\"w-editable-table__action-col\">\n <button\n type=\"button\"\n class=\"w-editable-table__remove\"\n aria-label=\"Remover linha\"\n @click=\"removeRow(row, index)\"\n >\n <i class=\"pi pi-trash\" />\n </button>\n </td>\n </tr>\n\n <tr v-if=\"expandable && isExpanded(index)\" class=\"w-editable-table__expansion\">\n <td :colspan=\"columnCount\">\n <slot name=\"expansion\" :row=\"row\" :index=\"index\" />\n </td>\n </tr>\n </tbody>\n\n <tfoot v-if=\"hasFooter\">\n <tr class=\"w-editable-table__footer\">\n <td v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <td\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n <slot :name=\"`footer-${column.field}`\" :rows=\"modelValue\" :column=\"column\">\n <span v-if=\"column.field === footerLabelField\" class=\"w-editable-table__footer-label\">\n {{ footerLabel }}\n </span>\n <span v-else>{{ footerValue(column) }}</span>\n </slot>\n </td>\n <td v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n </tfoot>\n </table>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, isRef, ref } from 'vue'\nimport InputText from 'primevue/inputtext'\nimport InputNumber from 'primevue/inputnumber'\nimport Select from 'primevue/select'\nimport { useFormatters } from '@/composables/useFormatters'\nimport type { EditableColumnDef, EditableRow } from '@/types/editableTable'\n\n/**\n * Tabela editável sobre ESTADO LOCAL — o oposto do WCrudView, que conversa com a\n * API a cada operação. Aqui nada é persistido: o consumidor mantém o array e\n * salva em bloco quando quiser (o caso \"documento com filhos\", em que salvar\n * linha a linha criaria registro órfão).\n *\n * As linhas nunca são mutadas no lugar: toda edição emite um array NOVO com a\n * linha trocada (`update:modelValue`), então `v-model` funciona igual em `ref`,\n * `reactive` ou prop vinda de cima.\n *\n * A subtabela de insumos do design é este mesmo componente dentro do slot\n * `#expansion` — não existe componente separado para \"tabela filha\".\n */\nconst props = withDefaults(\n defineProps<{\n /** Linhas (`v-model`). */\n modelValue?: EditableRow[]\n columns?: EditableColumnDef[]\n /** Liga a coluna de expansão e o slot `#expansion`. */\n expandable?: boolean\n /** Rótulo do botão de adicionar. Omitido = sem botão (só o slot `#toolbar`). */\n addLabel?: string\n /** Liga a coluna da lixeira. **Manda sozinha** na remoção: `disabled` trava as\n * células, não a linha (tabela de leitura de onde ainda se remove é caso real —\n * itens cujo CRUD acontece em dialog). Para travar tudo, `:removable=\"false\"`. */\n removable?: boolean\n emptyMessage?: string\n /** Rótulo da primeira célula do rodapé de totais. */\n footerLabel?: string\n /** Trava todos os editores (a tabela vira leitura) e o botão de adicionar. */\n disabled?: boolean\n /** Campo usado como `key` das linhas. Sem ele, a chave é o índice. */\n rowKey?: string\n }>(),\n {\n modelValue: () => [],\n columns: () => [],\n expandable: false,\n removable: false,\n emptyMessage: 'Nenhum item adicionado',\n footerLabel: 'Total',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [rows: EditableRow[]]\n add: []\n remove: [row: EditableRow, index: number]\n}>()\n\nconst { formatNumber } = useFormatters()\n\n// --- Expansão --------------------------------------------------------------\n// Índice das linhas abertas. Várias podem ficar abertas ao mesmo tempo: a\n// expansão é detalhe de leitura, não navegação.\nconst expanded = ref<Set<number>>(new Set())\n\nfunction isExpanded(index: number): boolean {\n return expanded.value.has(index)\n}\n\nfunction toggleRow(index: number) {\n const next = new Set(expanded.value)\n if (next.has(index)) next.delete(index)\n else next.add(index)\n expanded.value = next\n}\n\n// --- Colunas ---------------------------------------------------------------\n\nfunction unwrapOptions(column: EditableColumnDef): Record<string, unknown>[] {\n const options = column.options\n if (!options) return []\n return (isRef(options) ? options.value : options) as Record<string, unknown>[]\n}\n\nfunction columnAlign(column: EditableColumnDef): 'left' | 'center' | 'right' {\n if (column.align) return column.align\n return column.editor === 'number' ? 'right' : 'left'\n}\n\nfunction isNumericColumn(column: EditableColumnDef): boolean {\n return column.editor === 'number' || column.footer === 'sum' || column.decimals != null\n}\n\nfunction columnClass(column: EditableColumnDef): string[] {\n return [\n `w-editable-table__cell--${columnAlign(column)}`,\n ...(isNumericColumn(column) ? ['w-editable-table__cell--num'] : []),\n ]\n}\n\nfunction columnStyle(column: EditableColumnDef) {\n if (column.width == null) return undefined\n return { width: typeof column.width === 'number' ? `${column.width}px` : column.width }\n}\n\nfunction isCellDisabled(column: EditableColumnDef, row: EditableRow, index: number): boolean {\n if (props.disabled) return true\n if (typeof column.disabled === 'function') return column.disabled(row, index)\n return !!column.disabled\n}\n\n// --- Leitura / escrita de célula -------------------------------------------\n\nfunction toNumber(value: unknown): number {\n if (typeof value === 'number') return Number.isFinite(value) ? value : 0\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value.replace(/\\./g, '').replace(',', '.'))\n return Number.isFinite(parsed) ? parsed : 0\n }\n return 0\n}\n\nfunction displayValue(column: EditableColumnDef, row: EditableRow): string {\n const value = row[column.field]\n if (column.format) return column.format(value, row)\n if (value == null || value === '') return '—'\n if (isNumericColumn(column)) {\n return `${formatNumber(toNumber(value), column.decimals ?? 2)}${column.suffix ?? ''}`\n }\n return `${String(value)}${column.suffix ?? ''}`\n}\n\nfunction setCell(index: number, field: string, value: unknown) {\n const next = props.modelValue.map((row, i) => (i === index ? { ...row, [field]: value } : row))\n emit('update:modelValue', next)\n}\n\nfunction removeRow(row: EditableRow, index: number) {\n // Emite os dois: o array já sem a linha (para o v-model continuar sendo a\n // fonte da verdade) e o evento com a linha removida (para quem precisa dela).\n emit(\n 'update:modelValue',\n props.modelValue.filter((_, i) => i !== index),\n )\n expanded.value = new Set()\n emit('remove', row, index)\n}\n\nfunction rowKeyOf(row: EditableRow, index: number): string | number {\n if (props.rowKey) {\n const key = row[props.rowKey]\n if (typeof key === 'string' || typeof key === 'number') return key\n }\n return index\n}\n\n// --- Rodapé de totais ------------------------------------------------------\n\nconst hasFooter = computed(() =>\n props.columns.some((c) => typeof c.footer === 'function' || c.footer === 'sum'),\n)\n\nfunction footerValue(column: EditableColumnDef): string {\n if (typeof column.footer === 'function') return column.footer(props.modelValue)\n if (column.footer !== 'sum') return ''\n const total = props.modelValue.reduce((sum, row) => sum + toNumber(row[column.field]), 0)\n return `${formatNumber(total, column.decimals ?? 2)}${column.suffix ?? ''}`\n}\n\n/** A primeira coluna sem rodapé próprio carrega o rótulo \"Total\". */\nconst footerLabelField = computed(() => {\n const first = props.columns.find((c) => !c.footer || c.footer === 'none')\n return first?.field ?? null\n})\n\nconst columnCount = computed(\n () => props.columns.length + (props.expandable ? 1 : 0) + (props.removable ? 1 : 0),\n)\n\n// --- Grupos de cabeçalho ---------------------------------------------------\n// Colunas vizinhas com o mesmo `group` viram um rótulo único numa linha acima\n// do cabeçalho normal; as demais ficam com a célula de cima vazia.\n\nconst hasHeaderGroups = computed(() => props.columns.some((c) => c.group))\n\nconst headerGroupCells = computed(() => {\n const cells: { label: string; span: number; key: string }[] = []\n for (const column of props.columns) {\n const label = column.group ?? ''\n const last = cells[cells.length - 1]\n if (last && label && last.label === label) last.span += 1\n else cells.push({ label, span: 1, key: column.field })\n }\n return cells\n})\n</script>\n\n<template>\n <div class=\"w-editable-table\">\n <div v-if=\"$slots.toolbar || addLabel\" class=\"w-editable-table__toolbar\">\n <slot name=\"toolbar\">\n <button\n type=\"button\"\n class=\"w-editable-table__add\"\n :disabled=\"disabled\"\n @click=\"emit('add')\"\n >\n <i class=\"pi pi-plus\" />{{ addLabel }}\n </button>\n </slot>\n </div>\n\n <table class=\"w-editable-table__table\">\n <thead>\n <tr v-if=\"hasHeaderGroups\" class=\"w-editable-table__group-row\">\n <th v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <th\n v-for=\"cell in headerGroupCells\"\n :key=\"cell.key\"\n :colspan=\"cell.span\"\n class=\"w-editable-table__group\"\n :class=\"{ 'w-editable-table__group--label': cell.label }\"\n >\n {{ cell.label }}\n </th>\n <th v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n <tr>\n <th v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <th\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n {{ column.header }}\n </th>\n <th v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n </thead>\n\n <tbody v-if=\"!modelValue.length\">\n <tr>\n <td :colspan=\"columnCount\" class=\"w-editable-table__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </td>\n </tr>\n </tbody>\n\n <tbody v-for=\"(row, index) in modelValue\" :key=\"rowKeyOf(row, index)\">\n <!-- Zebra pelo índice, não por `:nth-child`: cada linha é um `<tbody>` próprio\n (por causa da expansão), então a contagem do CSS não enxergaria a\n alternância. -->\n <tr\n class=\"w-editable-table__row\"\n :class=\"{ 'w-editable-table__row--alt': index % 2 === 1 }\"\n >\n <td v-if=\"expandable\" class=\"w-editable-table__toggle-col\">\n <button\n type=\"button\"\n class=\"w-editable-table__toggle\"\n :aria-expanded=\"isExpanded(index)\"\n aria-label=\"Detalhes da linha\"\n @click=\"toggleRow(index)\"\n >\n <i :class=\"isExpanded(index) ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\" />\n </button>\n </td>\n\n <td\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n <slot\n :name=\"`cell-${column.field}`\"\n :row=\"row\"\n :index=\"index\"\n :value=\"row[column.field]\"\n :column=\"column\"\n >\n <InputNumber\n v-if=\"column.editor === 'number'\"\n :model-value=\"row[column.field] as number\"\n fluid\n locale=\"pt-BR\"\n :min=\"column.min\"\n :max=\"column.max\"\n :min-fraction-digits=\"column.decimals != null ? column.decimals : undefined\"\n :max-fraction-digits=\"column.decimals != null ? column.decimals : undefined\"\n :suffix=\"column.suffix\"\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n :input-class=\"'w-editable-table__num'\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <InputText\n v-else-if=\"column.editor === 'text'\"\n :model-value=\"row[column.field] as string\"\n fluid\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <Select\n v-else-if=\"column.editor === 'select'\"\n :model-value=\"row[column.field]\"\n fluid\n :options=\"unwrapOptions(column)\"\n :option-label=\"column.optionLabel || 'label'\"\n :option-value=\"column.optionValue || 'value'\"\n :placeholder=\"column.placeholder\"\n :disabled=\"isCellDisabled(column, row, index)\"\n @update:model-value=\"(val) => setCell(index, column.field, val)\"\n />\n\n <span v-else class=\"w-editable-table__text\">{{ displayValue(column, row) }}</span>\n </slot>\n </td>\n\n <td v-if=\"removable\" class=\"w-editable-table__action-col\">\n <button\n type=\"button\"\n class=\"w-editable-table__remove\"\n aria-label=\"Remover linha\"\n @click=\"removeRow(row, index)\"\n >\n <i class=\"pi pi-trash\" />\n </button>\n </td>\n </tr>\n\n <tr v-if=\"expandable && isExpanded(index)\" class=\"w-editable-table__expansion\">\n <td :colspan=\"columnCount\">\n <slot name=\"expansion\" :row=\"row\" :index=\"index\" />\n </td>\n </tr>\n </tbody>\n\n <tfoot v-if=\"hasFooter\">\n <tr class=\"w-editable-table__footer\">\n <td v-if=\"expandable\" class=\"w-editable-table__toggle-col\" />\n <td\n v-for=\"column in columns\"\n :key=\"column.field\"\n :class=\"columnClass(column)\"\n :style=\"columnStyle(column)\"\n >\n <slot :name=\"`footer-${column.field}`\" :rows=\"modelValue\" :column=\"column\">\n <span v-if=\"column.field === footerLabelField\" class=\"w-editable-table__footer-label\">\n {{ footerLabel }}\n </span>\n <span v-else>{{ footerValue(column) }}</span>\n </slot>\n </td>\n <td v-if=\"removable\" class=\"w-editable-table__action-col\" />\n </tr>\n </tfoot>\n </table>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * CRUD de coleção-filha em rascunho — o par do `WCrudView` para editor de documento.\n *\n * O `WCrudView` fala com um recurso REST: cada linha grava sozinha, na hora. Quando os\n * filhos não têm endereço próprio no servidor (o documento inteiro é que é salvo, e o\n * backend reconcilia as coleções), aquele contrato não existe — mas a experiência\n * deveria ser a mesma: dialog de form, ação de linha, confirmação para excluir, estado\n * vazio com texto útil.\n *\n * É essa casca, sobre a `WEditableTable` (que traz rodapé de totais, linha expansível e\n * edição por célula — coisas que a listagem de recurso não tem). O estado vem do\n * `useSubviewCrud`; aqui não mora nenhuma regra.\n *\n * A exclusão é ação de linha, e não a lixeira nativa da tabela, de propósito: a tabela\n * emite `update:modelValue` já sem a linha ANTES do `remove`, e quem casa linha↔item por\n * índice se perde no meio de uma confirmação assíncrona. Pela ação, a linha só sai\n * depois do \"Excluir\".\n */\nimport { computed, useSlots } from 'vue'\nimport WEditableTable from './WEditableTable.vue'\nimport WCrudFormDialog from './WCrudFormDialog.vue'\nimport type { EditableColumnDef, EditableRow } from '@/types/editableTable'\nimport type { SubviewCrudReturn } from '@/types/subview'\n\nconst props = withDefaults(\n defineProps<{\n // Agnóstico ao tipo da linha, como o WCrudView: o componente trata as linhas como\n // `Record<string, unknown>` e aceita o retorno de qualquer T do consumidor.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n crud: SubviewCrudReturn<any>\n columns: EditableColumnDef[]\n title?: string\n subtitle?: string\n /** Rótulo do botão de adicionar (default: `Adicionar`). */\n addLabel?: string\n /** Liga a coluna de expansão e o slot `#expansion`. */\n expandable?: boolean\n /** Documento travado (emitido, aprovado): sem adicionar, editar nem excluir. */\n disabled?: boolean\n emptyMessage?: string\n dialogWidth?: string\n /** Nº de colunas do grid do form dialog (default: o do manager, senão 2). */\n formColumns?: number\n /** Rótulo da primeira célula do rodapé de totais. */\n footerLabel?: string\n rowKey?: string\n }>(),\n {\n addLabel: 'Adicionar',\n expandable: false,\n disabled: false,\n dialogWidth: '520px',\n },\n)\n\nconst slots = useSlots()\n\n// `crud` chega como prop, e prop não sofre unwrap de ref — daí o `.value` explícito\n// (aqui e nos bindings do dialog). Um computed local evita repetir isso no template.\nconst linhas = computed(() => props.crud.items.value)\n\nconst podeCriar = computed(() => props.crud.config.canCreate !== false && !props.disabled)\nconst podeEditar = computed(() => props.crud.config.canEdit !== false && !props.disabled)\nconst podeExcluir = computed(() => props.crud.config.canDelete !== false && !props.disabled)\nconst temAcoes = computed(() => podeEditar.value || podeExcluir.value)\n\n/** Coluna sintética das ações de linha — some quando não há o que fazer com a linha. */\nconst COLUNA_ACOES = '__acoes'\n\nconst colunas = computed<EditableColumnDef[]>(() =>\n temAcoes.value\n ? [...props.columns, { field: COLUNA_ACOES, header: '', width: '5rem', align: 'right' }]\n : props.columns,\n)\n\n/** Só repassa os `cell-*` que o consumidor realmente definiu: repassar um slot vazio\n * faria a tabela achar que existe conteúdo e renderizar a célula em branco. */\nconst slotsDeCelula = computed(() =>\n Object.keys(slots).filter((nome) => nome.startsWith('cell-') && nome !== `cell-${COLUNA_ACOES}`),\n)\n\nconst slotsDeCampo = computed(() =>\n Object.keys(slots).filter((nome) => nome.startsWith('field-') || nome.startsWith('image-')),\n)\n\n/** Edição por célula: a tabela devolve o array inteiro, o dono do estado grava. */\nfunction onLinhas(linhas: EditableRow[]): void {\n props.crud.config.onChange(linhas)\n}\n</script>\n\n<template>\n <div class=\"w-crud-subview\">\n <div v-if=\"title || $slots['toolbar-actions']\" class=\"w-crud-subview__header\">\n <div class=\"w-crud-subview__heading\">\n <h3 v-if=\"title\" class=\"w-crud-subview__title\">\n {{ title }}\n <span v-if=\"linhas.length\" class=\"w-crud-subview__count\">{{ linhas.length }}</span>\n </h3>\n <p v-if=\"subtitle\" class=\"w-crud-subview__subtitle\">{{ subtitle }}</p>\n </div>\n <div v-if=\"$slots['toolbar-actions']\" class=\"w-crud-subview__actions\">\n <slot name=\"toolbar-actions\" />\n </div>\n </div>\n\n <WEditableTable\n :model-value=\"linhas\"\n :columns=\"colunas\"\n :expandable=\"expandable\"\n :disabled=\"disabled\"\n :add-label=\"podeCriar ? addLabel : undefined\"\n :empty-message=\"emptyMessage ?? crud.labels.emptyMessage\"\n :footer-label=\"footerLabel\"\n :row-key=\"rowKey\"\n @update:model-value=\"onLinhas\"\n @add=\"crud.openCreateDialog()\"\n >\n <!-- Ações da linha: editar abre o form, excluir passa pelo confirm do manager. -->\n <template #[`cell-${COLUNA_ACOES}`]=\"{ row, index }\">\n <div class=\"w-crud-subview__row-actions\">\n <button\n v-if=\"podeEditar\"\n type=\"button\"\n class=\"w-crud-subview__action\"\n aria-label=\"Editar\"\n @click=\"crud.openEditDialog(row, index)\"\n >\n <i class=\"pi pi-pencil\" />\n </button>\n <button\n v-if=\"podeExcluir\"\n type=\"button\"\n class=\"w-crud-subview__action w-crud-subview__action--danger\"\n aria-label=\"Excluir\"\n @click=\"crud.confirmDelete(row, index)\"\n >\n <i class=\"pi pi-trash\" />\n </button>\n </div>\n </template>\n\n <!-- Repasse dos slots de célula do consumidor -->\n <template v-for=\"nome in slotsDeCelula\" :key=\"nome\" #[nome]=\"slotData\">\n <slot :name=\"nome\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots.expansion\" #expansion=\"slotData\">\n <slot name=\"expansion\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots.empty\" #empty>\n <slot name=\"empty\" />\n </template>\n </WEditableTable>\n\n <WCrudFormDialog\n :visible=\"crud.dialogVisible.value\"\n :title=\"crud.dialogTitle.value\"\n :fields=\"crud.config.form\"\n :form-data=\"crud.formData\"\n :is-editing=\"crud.isEditing.value\"\n :saving=\"crud.saving.value\"\n :width=\"dialogWidth\"\n :form-columns=\"formColumns ?? crud.config.formColumns\"\n :keyboard-nav=\"crud.config.keyboardNav !== false\"\n @update:visible=\"crud.dialogVisible.value = $event\"\n @update:field=\"crud.setFormField\"\n @save=\"crud.save()\"\n >\n <template v-for=\"nome in slotsDeCampo\" :key=\"nome\" #[nome]=\"slotData\">\n <slot :name=\"nome\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots['after-fields']\" #after-fields=\"slotData\">\n <slot name=\"after-fields\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots['form-footer']\" #footer=\"slotData\">\n <slot name=\"form-footer\" v-bind=\"slotData\" />\n </template>\n </WCrudFormDialog>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * CRUD de coleção-filha em rascunho — o par do `WCrudView` para editor de documento.\n *\n * O `WCrudView` fala com um recurso REST: cada linha grava sozinha, na hora. Quando os\n * filhos não têm endereço próprio no servidor (o documento inteiro é que é salvo, e o\n * backend reconcilia as coleções), aquele contrato não existe — mas a experiência\n * deveria ser a mesma: dialog de form, ação de linha, confirmação para excluir, estado\n * vazio com texto útil.\n *\n * É essa casca, sobre a `WEditableTable` (que traz rodapé de totais, linha expansível e\n * edição por célula — coisas que a listagem de recurso não tem). O estado vem do\n * `useSubviewCrud`; aqui não mora nenhuma regra.\n *\n * A exclusão é ação de linha, e não a lixeira nativa da tabela, de propósito: a tabela\n * emite `update:modelValue` já sem a linha ANTES do `remove`, e quem casa linha↔item por\n * índice se perde no meio de uma confirmação assíncrona. Pela ação, a linha só sai\n * depois do \"Excluir\".\n */\nimport { computed, useSlots } from 'vue'\nimport WEditableTable from './WEditableTable.vue'\nimport WCrudFormDialog from './WCrudFormDialog.vue'\nimport type { EditableColumnDef, EditableRow } from '@/types/editableTable'\nimport type { SubviewCrudReturn } from '@/types/subview'\n\nconst props = withDefaults(\n defineProps<{\n // Agnóstico ao tipo da linha, como o WCrudView: o componente trata as linhas como\n // `Record<string, unknown>` e aceita o retorno de qualquer T do consumidor.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n crud: SubviewCrudReturn<any>\n columns: EditableColumnDef[]\n title?: string\n subtitle?: string\n /** Rótulo do botão de adicionar (default: `Adicionar`). */\n addLabel?: string\n /** Liga a coluna de expansão e o slot `#expansion`. */\n expandable?: boolean\n /** Documento travado (emitido, aprovado): sem adicionar, editar nem excluir. */\n disabled?: boolean\n emptyMessage?: string\n dialogWidth?: string\n /** Nº de colunas do grid do form dialog (default: o do manager, senão 2). */\n formColumns?: number\n /** Rótulo da primeira célula do rodapé de totais. */\n footerLabel?: string\n rowKey?: string\n }>(),\n {\n addLabel: 'Adicionar',\n expandable: false,\n disabled: false,\n dialogWidth: '520px',\n },\n)\n\nconst slots = useSlots()\n\n// `crud` chega como prop, e prop não sofre unwrap de ref — daí o `.value` explícito\n// (aqui e nos bindings do dialog). Um computed local evita repetir isso no template.\nconst linhas = computed(() => props.crud.items.value)\n\nconst podeCriar = computed(() => props.crud.config.canCreate !== false && !props.disabled)\nconst podeEditar = computed(() => props.crud.config.canEdit !== false && !props.disabled)\nconst podeExcluir = computed(() => props.crud.config.canDelete !== false && !props.disabled)\nconst temAcoes = computed(() => podeEditar.value || podeExcluir.value)\n\n/** Coluna sintética das ações de linha — some quando não há o que fazer com a linha. */\nconst COLUNA_ACOES = '__acoes'\n\nconst colunas = computed<EditableColumnDef[]>(() =>\n temAcoes.value\n ? [...props.columns, { field: COLUNA_ACOES, header: '', width: '5rem', align: 'right' }]\n : props.columns,\n)\n\n/** Só repassa os `cell-*` que o consumidor realmente definiu: repassar um slot vazio\n * faria a tabela achar que existe conteúdo e renderizar a célula em branco. */\nconst slotsDeCelula = computed(() =>\n Object.keys(slots).filter((nome) => nome.startsWith('cell-') && nome !== `cell-${COLUNA_ACOES}`),\n)\n\nconst slotsDeCampo = computed(() =>\n Object.keys(slots).filter((nome) => nome.startsWith('field-') || nome.startsWith('image-')),\n)\n\n/** Edição por célula: a tabela devolve o array inteiro, o dono do estado grava. */\nfunction onLinhas(linhas: EditableRow[]): void {\n props.crud.config.onChange(linhas)\n}\n</script>\n\n<template>\n <div class=\"w-crud-subview\">\n <div v-if=\"title || $slots['toolbar-actions']\" class=\"w-crud-subview__header\">\n <div class=\"w-crud-subview__heading\">\n <h3 v-if=\"title\" class=\"w-crud-subview__title\">\n {{ title }}\n <span v-if=\"linhas.length\" class=\"w-crud-subview__count\">{{ linhas.length }}</span>\n </h3>\n <p v-if=\"subtitle\" class=\"w-crud-subview__subtitle\">{{ subtitle }}</p>\n </div>\n <div v-if=\"$slots['toolbar-actions']\" class=\"w-crud-subview__actions\">\n <slot name=\"toolbar-actions\" />\n </div>\n </div>\n\n <WEditableTable\n :model-value=\"linhas\"\n :columns=\"colunas\"\n :expandable=\"expandable\"\n :disabled=\"disabled\"\n :add-label=\"podeCriar ? addLabel : undefined\"\n :empty-message=\"emptyMessage ?? crud.labels.emptyMessage\"\n :footer-label=\"footerLabel\"\n :row-key=\"rowKey\"\n @update:model-value=\"onLinhas\"\n @add=\"crud.openCreateDialog()\"\n >\n <!-- Ações da linha: editar abre o form, excluir passa pelo confirm do manager. -->\n <template #[`cell-${COLUNA_ACOES}`]=\"{ row, index }\">\n <div class=\"w-crud-subview__row-actions\">\n <button\n v-if=\"podeEditar\"\n type=\"button\"\n class=\"w-crud-subview__action\"\n aria-label=\"Editar\"\n @click=\"crud.openEditDialog(row, index)\"\n >\n <i class=\"pi pi-pencil\" />\n </button>\n <button\n v-if=\"podeExcluir\"\n type=\"button\"\n class=\"w-crud-subview__action w-crud-subview__action--danger\"\n aria-label=\"Excluir\"\n @click=\"crud.confirmDelete(row, index)\"\n >\n <i class=\"pi pi-trash\" />\n </button>\n </div>\n </template>\n\n <!-- Repasse dos slots de célula do consumidor -->\n <template v-for=\"nome in slotsDeCelula\" :key=\"nome\" #[nome]=\"slotData\">\n <slot :name=\"nome\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots.expansion\" #expansion=\"slotData\">\n <slot name=\"expansion\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots.empty\" #empty>\n <slot name=\"empty\" />\n </template>\n </WEditableTable>\n\n <WCrudFormDialog\n :visible=\"crud.dialogVisible.value\"\n :title=\"crud.dialogTitle.value\"\n :fields=\"crud.config.form\"\n :form-data=\"crud.formData\"\n :is-editing=\"crud.isEditing.value\"\n :saving=\"crud.saving.value\"\n :width=\"dialogWidth\"\n :form-columns=\"formColumns ?? crud.config.formColumns\"\n :keyboard-nav=\"crud.config.keyboardNav !== false\"\n @update:visible=\"crud.dialogVisible.value = $event\"\n @update:field=\"crud.setFormField\"\n @save=\"crud.save()\"\n >\n <template v-for=\"nome in slotsDeCampo\" :key=\"nome\" #[nome]=\"slotData\">\n <slot :name=\"nome\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots['after-fields']\" #after-fields=\"slotData\">\n <slot name=\"after-fields\" v-bind=\"slotData\" />\n </template>\n\n <template v-if=\"$slots['form-footer']\" #footer=\"slotData\">\n <slot name=\"form-footer\" v-bind=\"slotData\" />\n </template>\n </WCrudFormDialog>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport DatePicker from 'primevue/datepicker'\n\n/**\n * Seletor de intervalo de datas (período). Wrapper fino do DatePicker em modo\n * range com padrões pt-BR. O `v-model` guarda `[início, fim]` como `Date[]`.\n */\nwithDefaults(\n defineProps<{\n modelValue?: Date[] | null\n placeholder?: string\n minDate?: Date\n maxDate?: Date\n showButtonBar?: boolean\n disabled?: boolean\n }>(),\n {\n modelValue: null,\n placeholder: 'Selecione o período',\n showButtonBar: true,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [value: Date[] | null] }>()\n\nfunction onUpdate(v: unknown) {\n emit('update:modelValue', (v as Date[] | null) ?? null)\n}\n</script>\n\n<template>\n <DatePicker\n :model-value=\"modelValue ?? undefined\"\n selection-mode=\"range\"\n :placeholder=\"placeholder\"\n :min-date=\"minDate\"\n :max-date=\"maxDate\"\n :show-button-bar=\"showButtonBar\"\n :disabled=\"disabled\"\n date-format=\"dd/mm/yy\"\n fluid\n @update:model-value=\"onUpdate\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport DatePicker from 'primevue/datepicker'\n\n/**\n * Seletor de intervalo de datas (período). Wrapper fino do DatePicker em modo\n * range com padrões pt-BR. O `v-model` guarda `[início, fim]` como `Date[]`.\n */\nwithDefaults(\n defineProps<{\n modelValue?: Date[] | null\n placeholder?: string\n minDate?: Date\n maxDate?: Date\n showButtonBar?: boolean\n disabled?: boolean\n }>(),\n {\n modelValue: null,\n placeholder: 'Selecione o período',\n showButtonBar: true,\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [value: Date[] | null] }>()\n\nfunction onUpdate(v: unknown) {\n emit('update:modelValue', (v as Date[] | null) ?? null)\n}\n</script>\n\n<template>\n <DatePicker\n :model-value=\"modelValue ?? undefined\"\n selection-mode=\"range\"\n :placeholder=\"placeholder\"\n :min-date=\"minDate\"\n :max-date=\"maxDate\"\n :show-button-bar=\"showButtonBar\"\n :disabled=\"disabled\"\n date-format=\"dd/mm/yy\"\n fluid\n @update:model-value=\"onUpdate\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport Button from 'primevue/button'\n\n/**\n * Upload de arquivo(s) genérico (single ou multiple), sem dependência pesada.\n * O `v-model` guarda `File` (single) ou `File[]` (multiple). Valida tamanho por\n * arquivo e emite `error` para os que excederem.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: File | File[] | null\n multiple?: boolean\n accept?: string\n /** Tamanho máximo por arquivo em bytes (default 10MB). */\n maxSize?: number\n disabled?: boolean\n chooseLabel?: string\n }>(),\n {\n modelValue: null,\n multiple: false,\n accept: undefined,\n maxSize: 10_485_760,\n disabled: false,\n chooseLabel: 'Selecionar arquivo',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: File | File[] | null]\n error: [message: string]\n}>()\n\nconst input = ref<HTMLInputElement | null>(null)\nconst files = computed<File[]>(() => {\n const v = props.modelValue\n if (!v) return []\n return Array.isArray(v) ? v : [v]\n})\n\nfunction open() {\n input.value?.click()\n}\n\nfunction onChange(e: Event) {\n const el = e.target as HTMLInputElement\n const picked = Array.from(el.files || [])\n el.value = ''\n const valid: File[] = []\n for (const f of picked) {\n if (f.size > props.maxSize) {\n emit('error', `\"${f.name}\" acima do limite de ${(props.maxSize / 1_048_576).toFixed(1)}MB.`)\n continue\n }\n valid.push(f)\n }\n if (!valid.length) return\n emit('update:modelValue', props.multiple ? [...files.value, ...valid] : valid[0])\n}\n\nfunction removeAt(i: number) {\n if (!props.multiple) {\n emit('update:modelValue', null)\n return\n }\n const next = files.value.slice()\n next.splice(i, 1)\n emit('update:modelValue', next.length ? next : null)\n}\n\nfunction fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1_048_576) return `${(bytes / 1024).toFixed(0)} KB`\n return `${(bytes / 1_048_576).toFixed(1)} MB`\n}\n</script>\n\n<template>\n <div class=\"w-fileupload\" style=\"display: flex; flex-direction: column; gap: 0.5rem\">\n <div>\n <Button\n type=\"button\"\n :label=\"chooseLabel\"\n icon=\"pi pi-upload\"\n outlined\n size=\"small\"\n :disabled=\"disabled\"\n @click=\"open\"\n />\n </div>\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n style=\"display: none\"\n @change=\"onChange\"\n />\n <ul\n v-if=\"files.length\"\n style=\"\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 0.35rem;\n \"\n >\n <li\n v-for=\"(f, i) in files\"\n :key=\"`${f.name}-${i}`\"\n style=\"display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem\"\n >\n <i class=\"pi pi-file\" style=\"opacity: 0.6\" />\n <span style=\"flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap\">\n {{ f.name }}\n </span>\n <span style=\"opacity: 0.6\">{{ fmtSize(f.size) }}</span>\n <Button\n type=\"button\"\n icon=\"pi pi-times\"\n text\n rounded\n size=\"small\"\n severity=\"danger\"\n :disabled=\"disabled\"\n @click=\"removeAt(i)\"\n />\n </li>\n </ul>\n <slot v-else name=\"empty\">\n <span style=\"font-size: 0.8rem; opacity: 0.6\">Nenhum arquivo selecionado</span>\n </slot>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport Button from 'primevue/button'\n\n/**\n * Upload de arquivo(s) genérico (single ou multiple), sem dependência pesada.\n * O `v-model` guarda `File` (single) ou `File[]` (multiple). Valida tamanho por\n * arquivo e emite `error` para os que excederem.\n */\nconst props = withDefaults(\n defineProps<{\n modelValue?: File | File[] | null\n multiple?: boolean\n accept?: string\n /** Tamanho máximo por arquivo em bytes (default 10MB). */\n maxSize?: number\n disabled?: boolean\n chooseLabel?: string\n }>(),\n {\n modelValue: null,\n multiple: false,\n accept: undefined,\n maxSize: 10_485_760,\n disabled: false,\n chooseLabel: 'Selecionar arquivo',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: File | File[] | null]\n error: [message: string]\n}>()\n\nconst input = ref<HTMLInputElement | null>(null)\nconst files = computed<File[]>(() => {\n const v = props.modelValue\n if (!v) return []\n return Array.isArray(v) ? v : [v]\n})\n\nfunction open() {\n input.value?.click()\n}\n\nfunction onChange(e: Event) {\n const el = e.target as HTMLInputElement\n const picked = Array.from(el.files || [])\n el.value = ''\n const valid: File[] = []\n for (const f of picked) {\n if (f.size > props.maxSize) {\n emit('error', `\"${f.name}\" acima do limite de ${(props.maxSize / 1_048_576).toFixed(1)}MB.`)\n continue\n }\n valid.push(f)\n }\n if (!valid.length) return\n emit('update:modelValue', props.multiple ? [...files.value, ...valid] : valid[0])\n}\n\nfunction removeAt(i: number) {\n if (!props.multiple) {\n emit('update:modelValue', null)\n return\n }\n const next = files.value.slice()\n next.splice(i, 1)\n emit('update:modelValue', next.length ? next : null)\n}\n\nfunction fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1_048_576) return `${(bytes / 1024).toFixed(0)} KB`\n return `${(bytes / 1_048_576).toFixed(1)} MB`\n}\n</script>\n\n<template>\n <div class=\"w-fileupload\" style=\"display: flex; flex-direction: column; gap: 0.5rem\">\n <div>\n <Button\n type=\"button\"\n :label=\"chooseLabel\"\n icon=\"pi pi-upload\"\n outlined\n size=\"small\"\n :disabled=\"disabled\"\n @click=\"open\"\n />\n </div>\n <input\n ref=\"input\"\n type=\"file\"\n :accept=\"accept\"\n :multiple=\"multiple\"\n style=\"display: none\"\n @change=\"onChange\"\n />\n <ul\n v-if=\"files.length\"\n style=\"\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 0.35rem;\n \"\n >\n <li\n v-for=\"(f, i) in files\"\n :key=\"`${f.name}-${i}`\"\n style=\"display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem\"\n >\n <i class=\"pi pi-file\" style=\"opacity: 0.6\" />\n <span style=\"flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap\">\n {{ f.name }}\n </span>\n <span style=\"opacity: 0.6\">{{ fmtSize(f.size) }}</span>\n <Button\n type=\"button\"\n icon=\"pi pi-times\"\n text\n rounded\n size=\"small\"\n severity=\"danger\"\n :disabled=\"disabled\"\n @click=\"removeAt(i)\"\n />\n </li>\n </ul>\n <slot v-else name=\"empty\">\n <span style=\"font-size: 0.8rem; opacity: 0.6\">Nenhum arquivo selecionado</span>\n </slot>\n </div>\n</template>\n","/**\n * Lógica pura por trás do `WTreeSelect` — sem Vue, sem PrimeVue, testável isolada.\n *\n * O componente NÃO delega a seleção ao `Tree` do PrimeVue (ver o comentário longo\n * em `WTreeSelect.vue`). Tudo o que a árvore precisa saber é derivado aqui, sempre\n * a partir da árvore INTEIRA e do array de ids selecionados.\n */\n\nexport type TreeSelectId = string | number\n\nexport type TreeSelectOption = Record<string, unknown>\n\n/** Nó no formato que o `Tree` do PrimeVue consome (`key`/`label`/`children`). */\nexport interface TreeSelectNode {\n /** Chave única e estável. Prefixada para não colidir grupo com folha. */\n key: string\n label: string\n /** Id da folha. Ausente em nó de grupo — grupo é visual, nunca entra no v-model. */\n value?: TreeSelectId\n children?: TreeSelectNode[]\n /** Registro original da lista plana, entregue aos slots. */\n data?: TreeSelectOption\n leaf: boolean\n}\n\n/** Estado de um nó no formato que o `Tree` lê (`checked` / `partialChecked`). */\nexport interface TreeSelectionState {\n checked: boolean\n partialChecked: boolean\n}\n\nexport type TreeSelectionKeys = Record<string, TreeSelectionState>\n\n/** Prefixo da chave de um nó de grupo. */\nexport const TREE_GROUP_PREFIX = 'g:'\n/** Prefixo da chave de um nó folha. */\nexport const TREE_LEAF_PREFIX = 'n:'\n\nexport interface BuildTreeOptions {\n /** Campo do id da folha (default: `id`). */\n optionValue?: string\n /** Campo do rótulo visível (default: `nome`). */\n optionLabel?: string\n /** Campo que agrupa. Sem ele, a árvore é rasa (só folhas na raiz). */\n groupBy?: string\n /** Rótulo do nó-pai a partir do valor bruto do agrupamento. */\n groupLabel?: (chave: string) => string\n}\n\n/** Normaliza para busca: minúsculas e sem acento (rótulos são pt-BR). */\nexport function normalizeTerm(value: unknown): string {\n return String(value ?? '')\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLocaleLowerCase()\n}\n\n/**\n * Converte a lista PLANA em árvore de dois níveis.\n *\n * - Sem `groupBy`, ou item com valor de grupo vazio/nulo → folha na raiz.\n * - Grupos saem na ordem da primeira ocorrência (nada de ordenação implícita,\n * quem manda na ordem é quem monta a lista).\n */\nexport function buildTree(\n options: readonly TreeSelectOption[],\n { optionValue = 'id', optionLabel = 'nome', groupBy, groupLabel }: BuildTreeOptions = {},\n): TreeSelectNode[] {\n const roots: TreeSelectNode[] = []\n const groups = new Map<string, TreeSelectNode>()\n\n for (const option of options ?? []) {\n const id = option[optionValue] as TreeSelectId | undefined\n if (id === undefined || id === null) continue\n\n const leaf: TreeSelectNode = {\n key: `${TREE_LEAF_PREFIX}${id}`,\n label: String(option[optionLabel] ?? id),\n value: id,\n data: option,\n leaf: true,\n }\n\n const rawGroup = groupBy ? option[groupBy] : undefined\n if (rawGroup === undefined || rawGroup === null || rawGroup === '') {\n roots.push(leaf)\n continue\n }\n\n const chave = String(rawGroup)\n let group = groups.get(chave)\n if (!group) {\n group = {\n key: `${TREE_GROUP_PREFIX}${chave}`,\n label: groupLabel ? groupLabel(chave) : chave,\n children: [],\n leaf: false,\n }\n groups.set(chave, group)\n roots.push(group)\n }\n group.children!.push(leaf)\n }\n\n return roots\n}\n\n/** Todos os ids de folha sob um nó (o próprio, se for folha). */\nexport function collectLeafValues(node: TreeSelectNode): TreeSelectId[] {\n if (!node.children?.length) {\n return node.value === undefined ? [] : [node.value]\n }\n return node.children.flatMap(collectLeafValues)\n}\n\n/** Todos os ids de folha da árvore, na ordem em que aparecem. */\nexport function allLeafValues(nodes: readonly TreeSelectNode[]): TreeSelectId[] {\n return nodes.flatMap(collectLeafValues)\n}\n\n/** Índice `key → nó` da árvore INTEIRA — é por aqui que um evento vira ids. */\nexport function indexByKey(nodes: readonly TreeSelectNode[]): Map<string, TreeSelectNode> {\n const index = new Map<string, TreeSelectNode>()\n const visit = (node: TreeSelectNode) => {\n index.set(node.key, node)\n node.children?.forEach(visit)\n }\n nodes.forEach(visit)\n return index\n}\n\n/**\n * Deriva o mapa `selectionKeys` a partir dos ids selecionados.\n *\n * Chame SEMPRE com a árvore inteira — nunca com a visão filtrada. Um grupo é\n * `checked` quando todas as folhas dele estão selecionadas e `partialChecked`\n * quando só algumas estão; como a conta é sobre o conjunto completo, o filtro\n * não tem como corromper o estado.\n */\nexport function deriveSelectionKeys(\n nodes: readonly TreeSelectNode[],\n selected: readonly TreeSelectId[],\n): TreeSelectionKeys {\n const chosen = new Set<TreeSelectId>(selected ?? [])\n const keys: TreeSelectionKeys = {}\n\n const visit = (node: TreeSelectNode): TreeSelectionState => {\n if (!node.children?.length) {\n const checked = node.value !== undefined && chosen.has(node.value)\n if (checked) keys[node.key] = { checked: true, partialChecked: false }\n return { checked, partialChecked: false }\n }\n\n let todas = true\n let alguma = false\n for (const child of node.children) {\n const estado = visit(child)\n if (estado.checked) alguma = true\n else todas = false\n if (estado.partialChecked) {\n alguma = true\n todas = false\n }\n }\n\n if (todas) keys[node.key] = { checked: true, partialChecked: false }\n else if (alguma) keys[node.key] = { checked: false, partialChecked: true }\n return { checked: todas, partialChecked: !todas && alguma }\n }\n\n nodes.forEach(visit)\n return keys\n}\n\n/**\n * Poda a árvore pelo termo de busca, devolvendo CÓPIAS que preservam a `key`.\n *\n * Grupo cujo rótulo casa é mantido inteiro; senão sobram só as folhas que casam.\n * A chave sobrevive à poda de propósito: é ela que reconecta um evento da árvore\n * visível ao nó correspondente da árvore inteira.\n */\nexport function filterTree(\n nodes: readonly TreeSelectNode[],\n term: string,\n extraFields: readonly string[] = [],\n): TreeSelectNode[] {\n const q = normalizeTerm(term).trim()\n if (!q) return [...nodes]\n\n const matches = (node: TreeSelectNode): boolean => {\n if (normalizeTerm(node.label).includes(q)) return true\n return extraFields.some((field) => normalizeTerm(node.data?.[field]).includes(q))\n }\n\n const out: TreeSelectNode[] = []\n for (const node of nodes) {\n if (!node.children?.length) {\n if (matches(node)) out.push(node)\n continue\n }\n if (matches(node)) {\n out.push(node)\n continue\n }\n const children = filterTree(node.children, term, extraFields)\n if (children.length) out.push({ ...node, children })\n }\n return out\n}\n\n/** Adiciona ou remove `ids` do array atual, preservando a ordem e sem duplicar. */\nexport function toggleIds(\n current: readonly TreeSelectId[],\n ids: readonly TreeSelectId[],\n check: boolean,\n): TreeSelectId[] {\n const alvo = new Set<TreeSelectId>(ids)\n if (!check) return (current ?? []).filter((id) => !alvo.has(id))\n\n const out = [...(current ?? [])]\n const existente = new Set<TreeSelectId>(out)\n for (const id of ids) {\n if (!existente.has(id)) {\n existente.add(id)\n out.push(id)\n }\n }\n return out\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport Tree from 'primevue/tree'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Button from 'primevue/button'\nimport {\n allLeafValues,\n buildTree,\n collectLeafValues,\n deriveSelectionKeys,\n filterTree,\n indexByKey,\n toggleIds,\n type TreeSelectId,\n type TreeSelectNode,\n type TreeSelectOption,\n} from '@/utils/treeSelect'\n\n/**\n * Seleção múltipla em árvore com checkbox, alimentada por uma lista PLANA + um\n * campo de agrupamento. O `v-model` é um array de ids de FOLHA — nunca o mapa de\n * chaves do PrimeVue, e nunca com id de grupo dentro (grupo é só visual).\n *\n * ---------------------------------------------------------------------------\n * POR QUE A SELEÇÃO É DERIVADA AQUI E NÃO DELEGADA AO `Tree` — NÃO \"SIMPLIFIQUE\"\n * ---------------------------------------------------------------------------\n * O `Tree` com `selectionMode=\"checkbox\"` sabe manter sozinho o mapa\n * `{ chave: { checked, partialChecked } }`, propagando para pais e filhos. Trocar\n * o que está aqui por um `v-model:selectionKeys` parece uma simplificação óbvia.\n * Não é: existe um bug ABERTO que quebra exatamente a combinação que usamos\n * (checkbox + filtro).\n *\n * https://github.com/primefaces/primevue/issues/6928 (aberta, \"Help Wanted\")\n *\n * Com o filtro ativo, o `partialChecked` dos ancestrais passa a se referir à\n * árvore FILTRADA em vez da árvore inteira. O mecanismo está no código do\n * PrimeVue 4.5: `TreeNode.propagateUp()` conta `this.node.children`, e sob filtro\n * `this.node` é uma CÓPIA que o `findFilteredNodes()` produziu contendo só os\n * filhos que casaram com a busca. Um grupo com 12 telas, 3 marcadas e filtro\n * mostrando só essas 3 aparece como totalmente marcado — e ao salvar o usuário\n * marca o que não queria.\n *\n * O repositório do PrimeVue foi arquivado em junho/2026 e está read-only: não vai\n * ser corrigido upstream.\n *\n * Por isso, aqui:\n * 1. a fonte da verdade é o `modelValue` (ids de folha) — sem estado espelhado;\n * 2. o `selectionKeys` é DERIVADO sobre a árvore INTEIRA (`fullTree`), nunca\n * sobre a visão filtrada, então é correto por construção;\n * 3. o `Tree` recebe `:selection-keys` como prop CONTROLADA (não `v-model:`) —\n * o mapa que ele calcularia é simplesmente descartado.\n *\n * Escolha do evento: `node-select` / `node-unselect`, e não `update:selectionKeys`.\n * Os dois saem do mesmo `onCheckboxChange` do `Tree`, mas carregam coisas\n * diferentes: `update:selectionKeys` entrega o mapa JÁ PROPAGADO (o resultado do\n * `propagateUp` bugado), enquanto `node-select`/`node-unselect` entregam só o nó\n * clicado e a direção do clique — a intenção crua do usuário, que nenhuma conta\n * do PrimeVue tocou. É o único ponto estável da API para este caso.\n *\n * Detalhe que fecha o cerco: sob filtro o nó do evento é uma cópia podada, então\n * ler `event.node.children` alternaria só as folhas VISÍVEIS. Resolvemos o nó\n * pela `key` dentro do índice da árvore inteira (`fullIndex`) e alternamos todas\n * as folhas dele — o mesmo resultado com ou sem filtro na tela.\n */\nconst props = withDefaults(\n defineProps<{\n /** Ids das folhas selecionadas (`v-model`). Nunca contém id de grupo. */\n modelValue?: TreeSelectId[]\n /** Lista PLANA de registros. O agrupamento é feito aqui, por `groupBy`. */\n options?: TreeSelectOption[]\n /** Campo do id da folha (default: `id`). */\n optionValue?: string\n /** Campo do rótulo visível (default: `nome`). */\n optionLabel?: string\n /** Campo que agrupa os registros. Sem ele, a lista sai rasa. */\n groupBy?: string\n /** Rótulo do nó-pai a partir do valor bruto do agrupamento. */\n groupLabel?: (chave: string) => string\n /** Campo de busca interno. */\n filter?: boolean\n filterPlaceholder?: string\n /** Campos extras do registro considerados na busca (além do rótulo). */\n filterFields?: string[]\n loading?: boolean\n disabled?: boolean\n /** Texto quando não há nenhuma opção. */\n emptyMessage?: string\n /** Texto quando a busca não encontrou nada. */\n filterEmptyMessage?: string\n /** Contador `N de M selecionados` acima da árvore. */\n showCount?: boolean\n /** Atalhos \"Marcar todos\" / \"Limpar\". */\n showToggleAll?: boolean\n /** Abre todos os grupos na montagem. */\n defaultExpanded?: boolean\n ariaLabel?: string\n }>(),\n {\n modelValue: () => [],\n options: () => [],\n optionValue: 'id',\n optionLabel: 'nome',\n filter: false,\n filterPlaceholder: 'Buscar...',\n filterFields: () => [],\n loading: false,\n disabled: false,\n emptyMessage: 'Nenhum item disponível',\n filterEmptyMessage: 'Nenhum item encontrado',\n showCount: true,\n showToggleAll: true,\n defaultExpanded: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [ids: TreeSelectId[]] }>()\n\nconst term = ref('')\nconst expandedKeys = ref<Record<string, boolean>>({})\n\n/** Árvore inteira — a única base válida para derivar estado. */\nconst fullTree = computed(() =>\n buildTree(props.options, {\n optionValue: props.optionValue,\n optionLabel: props.optionLabel,\n groupBy: props.groupBy,\n groupLabel: props.groupLabel,\n }),\n)\n\n/** Índice `key → nó` da árvore inteira; traduz evento da árvore visível em ids. */\nconst fullIndex = computed(() => indexByKey(fullTree.value))\n\nconst isFiltering = computed(() => props.filter && term.value.trim().length > 0)\n\n/** O que aparece na tela. Só isto é filtrado — o estado, nunca. */\nconst visibleTree = computed(() =>\n isFiltering.value ? filterTree(fullTree.value, term.value, props.filterFields) : fullTree.value,\n)\n\n/** Derivado sobre `fullTree`, de propósito. Ver o comentário no topo. */\nconst selectionKeys = computed(() => deriveSelectionKeys(fullTree.value, props.modelValue))\n\nconst totalLeaves = computed(() => allLeafValues(fullTree.value).length)\nconst selectedCount = computed(() => {\n const validos = new Set(allLeafValues(fullTree.value))\n return props.modelValue.filter((id) => validos.has(id)).length\n})\n\nconst showHeader = computed(\n () => (props.showCount || props.showToggleAll) && (totalLeaves.value > 0 || props.loading),\n)\nconst isEmpty = computed(() => !props.loading && visibleTree.value.length === 0)\nconst emptyText = computed(() =>\n isFiltering.value ? props.filterEmptyMessage : props.emptyMessage,\n)\n\nfunction expandFrom(nodes: readonly TreeSelectNode[]) {\n const next = { ...expandedKeys.value }\n for (const node of nodes) if (node.children?.length) next[node.key] = true\n expandedKeys.value = next\n}\n\n// `immediate` porque `options` costuma chegar de uma request DEPOIS da montagem:\n// expandir só no setup deixaria a árvore fechada justamente no caso mais comum.\n// Cada grupo é aberto no máximo uma vez, então grupo que o usuário fechou na mão\n// não reabre sozinho se a lista for recarregada.\nconst jaAbertos = new Set<string>()\nwatch(\n fullTree,\n (nodes) => {\n if (!props.defaultExpanded) return\n const novos = nodes.filter((n) => n.children?.length && !jaAbertos.has(n.key))\n if (!novos.length) return\n novos.forEach((n) => jaAbertos.add(n.key))\n expandFrom(novos)\n },\n { immediate: true },\n)\n\n// Buscar sem abrir os grupos que casaram esconderia o resultado atrás de um\n// nó fechado. Ao limpar, a expansão do usuário fica como estava.\nwatch(term, (valor) => {\n if (valor.trim()) expandFrom(visibleTree.value)\n})\n\n/**\n * Traduz o clique num nó (grupo ou folha) para ids de folha.\n * Resolve pela `key` no índice da árvore INTEIRA — `node` pode ser uma cópia\n * podada pelo filtro, com menos filhos do que o grupo realmente tem.\n */\nfunction onNodeToggle(node: TreeSelectNode, check: boolean) {\n if (props.disabled) return\n const completo = fullIndex.value.get(node.key)\n if (!completo) return\n emit('update:modelValue', toggleIds(props.modelValue, collectLeafValues(completo), check))\n}\n\nfunction selectAll() {\n if (props.disabled) return\n emit('update:modelValue', allLeafValues(fullTree.value))\n}\n\nfunction clearAll() {\n if (props.disabled) return\n emit('update:modelValue', [])\n}\n</script>\n\n<template>\n <div class=\"w-tree-select\" :class=\"{ 'w-tree-select--disabled': disabled }\">\n <slot name=\"header\" :selected-count=\"selectedCount\" :total=\"totalLeaves\">\n <div v-if=\"showHeader\" class=\"w-tree-select__head\">\n <span v-if=\"showCount\" class=\"w-tree-select__count\">\n {{ selectedCount }} de {{ totalLeaves }} selecionados\n </span>\n <span v-else />\n <div v-if=\"showToggleAll\" class=\"w-tree-select__actions\">\n <Button\n type=\"button\"\n label=\"Marcar todos\"\n text\n data-kbd-skip\n tabindex=\"-1\"\n :disabled=\"disabled || selectedCount === totalLeaves || !totalLeaves\"\n @click=\"selectAll\"\n />\n <Button\n type=\"button\"\n label=\"Limpar\"\n text\n data-kbd-skip\n tabindex=\"-1\"\n :disabled=\"disabled || !selectedCount\"\n @click=\"clearAll\"\n />\n </div>\n </div>\n </slot>\n\n <IconField v-if=\"filter\" class=\"w-tree-select__filter\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n v-model=\"term\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n :aria-label=\"filterPlaceholder\"\n data-kbd-skip\n fluid\n />\n </IconField>\n\n <Tree\n v-if=\"!isEmpty\"\n :value=\"visibleTree\"\n selection-mode=\"checkbox\"\n :selection-keys=\"selectionKeys\"\n :expanded-keys=\"expandedKeys\"\n :loading=\"loading\"\n :aria-label=\"ariaLabel\"\n class=\"w-tree-select__tree\"\n @update:expanded-keys=\"expandedKeys = $event\"\n @node-select=\"onNodeToggle($event as TreeSelectNode, true)\"\n @node-unselect=\"onNodeToggle($event as TreeSelectNode, false)\"\n >\n <template #default=\"slotProps\">\n <slot name=\"node\" :node=\"slotProps.node as TreeSelectNode\">\n {{ (slotProps.node as TreeSelectNode).label }}\n </slot>\n </template>\n </Tree>\n\n <div v-else class=\"w-tree-select__empty\">\n <slot name=\"empty\" :filtering=\"isFiltering\">{{ emptyText }}</slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport Tree from 'primevue/tree'\nimport InputText from 'primevue/inputtext'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport Button from 'primevue/button'\nimport {\n allLeafValues,\n buildTree,\n collectLeafValues,\n deriveSelectionKeys,\n filterTree,\n indexByKey,\n toggleIds,\n type TreeSelectId,\n type TreeSelectNode,\n type TreeSelectOption,\n} from '@/utils/treeSelect'\n\n/**\n * Seleção múltipla em árvore com checkbox, alimentada por uma lista PLANA + um\n * campo de agrupamento. O `v-model` é um array de ids de FOLHA — nunca o mapa de\n * chaves do PrimeVue, e nunca com id de grupo dentro (grupo é só visual).\n *\n * ---------------------------------------------------------------------------\n * POR QUE A SELEÇÃO É DERIVADA AQUI E NÃO DELEGADA AO `Tree` — NÃO \"SIMPLIFIQUE\"\n * ---------------------------------------------------------------------------\n * O `Tree` com `selectionMode=\"checkbox\"` sabe manter sozinho o mapa\n * `{ chave: { checked, partialChecked } }`, propagando para pais e filhos. Trocar\n * o que está aqui por um `v-model:selectionKeys` parece uma simplificação óbvia.\n * Não é: existe um bug ABERTO que quebra exatamente a combinação que usamos\n * (checkbox + filtro).\n *\n * https://github.com/primefaces/primevue/issues/6928 (aberta, \"Help Wanted\")\n *\n * Com o filtro ativo, o `partialChecked` dos ancestrais passa a se referir à\n * árvore FILTRADA em vez da árvore inteira. O mecanismo está no código do\n * PrimeVue 4.5: `TreeNode.propagateUp()` conta `this.node.children`, e sob filtro\n * `this.node` é uma CÓPIA que o `findFilteredNodes()` produziu contendo só os\n * filhos que casaram com a busca. Um grupo com 12 telas, 3 marcadas e filtro\n * mostrando só essas 3 aparece como totalmente marcado — e ao salvar o usuário\n * marca o que não queria.\n *\n * O repositório do PrimeVue foi arquivado em junho/2026 e está read-only: não vai\n * ser corrigido upstream.\n *\n * Por isso, aqui:\n * 1. a fonte da verdade é o `modelValue` (ids de folha) — sem estado espelhado;\n * 2. o `selectionKeys` é DERIVADO sobre a árvore INTEIRA (`fullTree`), nunca\n * sobre a visão filtrada, então é correto por construção;\n * 3. o `Tree` recebe `:selection-keys` como prop CONTROLADA (não `v-model:`) —\n * o mapa que ele calcularia é simplesmente descartado.\n *\n * Escolha do evento: `node-select` / `node-unselect`, e não `update:selectionKeys`.\n * Os dois saem do mesmo `onCheckboxChange` do `Tree`, mas carregam coisas\n * diferentes: `update:selectionKeys` entrega o mapa JÁ PROPAGADO (o resultado do\n * `propagateUp` bugado), enquanto `node-select`/`node-unselect` entregam só o nó\n * clicado e a direção do clique — a intenção crua do usuário, que nenhuma conta\n * do PrimeVue tocou. É o único ponto estável da API para este caso.\n *\n * Detalhe que fecha o cerco: sob filtro o nó do evento é uma cópia podada, então\n * ler `event.node.children` alternaria só as folhas VISÍVEIS. Resolvemos o nó\n * pela `key` dentro do índice da árvore inteira (`fullIndex`) e alternamos todas\n * as folhas dele — o mesmo resultado com ou sem filtro na tela.\n */\nconst props = withDefaults(\n defineProps<{\n /** Ids das folhas selecionadas (`v-model`). Nunca contém id de grupo. */\n modelValue?: TreeSelectId[]\n /** Lista PLANA de registros. O agrupamento é feito aqui, por `groupBy`. */\n options?: TreeSelectOption[]\n /** Campo do id da folha (default: `id`). */\n optionValue?: string\n /** Campo do rótulo visível (default: `nome`). */\n optionLabel?: string\n /** Campo que agrupa os registros. Sem ele, a lista sai rasa. */\n groupBy?: string\n /** Rótulo do nó-pai a partir do valor bruto do agrupamento. */\n groupLabel?: (chave: string) => string\n /** Campo de busca interno. */\n filter?: boolean\n filterPlaceholder?: string\n /** Campos extras do registro considerados na busca (além do rótulo). */\n filterFields?: string[]\n loading?: boolean\n disabled?: boolean\n /** Texto quando não há nenhuma opção. */\n emptyMessage?: string\n /** Texto quando a busca não encontrou nada. */\n filterEmptyMessage?: string\n /** Contador `N de M selecionados` acima da árvore. */\n showCount?: boolean\n /** Atalhos \"Marcar todos\" / \"Limpar\". */\n showToggleAll?: boolean\n /** Abre todos os grupos na montagem. */\n defaultExpanded?: boolean\n ariaLabel?: string\n }>(),\n {\n modelValue: () => [],\n options: () => [],\n optionValue: 'id',\n optionLabel: 'nome',\n filter: false,\n filterPlaceholder: 'Buscar...',\n filterFields: () => [],\n loading: false,\n disabled: false,\n emptyMessage: 'Nenhum item disponível',\n filterEmptyMessage: 'Nenhum item encontrado',\n showCount: true,\n showToggleAll: true,\n defaultExpanded: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [ids: TreeSelectId[]] }>()\n\nconst term = ref('')\nconst expandedKeys = ref<Record<string, boolean>>({})\n\n/** Árvore inteira — a única base válida para derivar estado. */\nconst fullTree = computed(() =>\n buildTree(props.options, {\n optionValue: props.optionValue,\n optionLabel: props.optionLabel,\n groupBy: props.groupBy,\n groupLabel: props.groupLabel,\n }),\n)\n\n/** Índice `key → nó` da árvore inteira; traduz evento da árvore visível em ids. */\nconst fullIndex = computed(() => indexByKey(fullTree.value))\n\nconst isFiltering = computed(() => props.filter && term.value.trim().length > 0)\n\n/** O que aparece na tela. Só isto é filtrado — o estado, nunca. */\nconst visibleTree = computed(() =>\n isFiltering.value ? filterTree(fullTree.value, term.value, props.filterFields) : fullTree.value,\n)\n\n/** Derivado sobre `fullTree`, de propósito. Ver o comentário no topo. */\nconst selectionKeys = computed(() => deriveSelectionKeys(fullTree.value, props.modelValue))\n\nconst totalLeaves = computed(() => allLeafValues(fullTree.value).length)\nconst selectedCount = computed(() => {\n const validos = new Set(allLeafValues(fullTree.value))\n return props.modelValue.filter((id) => validos.has(id)).length\n})\n\nconst showHeader = computed(\n () => (props.showCount || props.showToggleAll) && (totalLeaves.value > 0 || props.loading),\n)\nconst isEmpty = computed(() => !props.loading && visibleTree.value.length === 0)\nconst emptyText = computed(() =>\n isFiltering.value ? props.filterEmptyMessage : props.emptyMessage,\n)\n\nfunction expandFrom(nodes: readonly TreeSelectNode[]) {\n const next = { ...expandedKeys.value }\n for (const node of nodes) if (node.children?.length) next[node.key] = true\n expandedKeys.value = next\n}\n\n// `immediate` porque `options` costuma chegar de uma request DEPOIS da montagem:\n// expandir só no setup deixaria a árvore fechada justamente no caso mais comum.\n// Cada grupo é aberto no máximo uma vez, então grupo que o usuário fechou na mão\n// não reabre sozinho se a lista for recarregada.\nconst jaAbertos = new Set<string>()\nwatch(\n fullTree,\n (nodes) => {\n if (!props.defaultExpanded) return\n const novos = nodes.filter((n) => n.children?.length && !jaAbertos.has(n.key))\n if (!novos.length) return\n novos.forEach((n) => jaAbertos.add(n.key))\n expandFrom(novos)\n },\n { immediate: true },\n)\n\n// Buscar sem abrir os grupos que casaram esconderia o resultado atrás de um\n// nó fechado. Ao limpar, a expansão do usuário fica como estava.\nwatch(term, (valor) => {\n if (valor.trim()) expandFrom(visibleTree.value)\n})\n\n/**\n * Traduz o clique num nó (grupo ou folha) para ids de folha.\n * Resolve pela `key` no índice da árvore INTEIRA — `node` pode ser uma cópia\n * podada pelo filtro, com menos filhos do que o grupo realmente tem.\n */\nfunction onNodeToggle(node: TreeSelectNode, check: boolean) {\n if (props.disabled) return\n const completo = fullIndex.value.get(node.key)\n if (!completo) return\n emit('update:modelValue', toggleIds(props.modelValue, collectLeafValues(completo), check))\n}\n\nfunction selectAll() {\n if (props.disabled) return\n emit('update:modelValue', allLeafValues(fullTree.value))\n}\n\nfunction clearAll() {\n if (props.disabled) return\n emit('update:modelValue', [])\n}\n</script>\n\n<template>\n <div class=\"w-tree-select\" :class=\"{ 'w-tree-select--disabled': disabled }\">\n <slot name=\"header\" :selected-count=\"selectedCount\" :total=\"totalLeaves\">\n <div v-if=\"showHeader\" class=\"w-tree-select__head\">\n <span v-if=\"showCount\" class=\"w-tree-select__count\">\n {{ selectedCount }} de {{ totalLeaves }} selecionados\n </span>\n <span v-else />\n <div v-if=\"showToggleAll\" class=\"w-tree-select__actions\">\n <Button\n type=\"button\"\n label=\"Marcar todos\"\n text\n data-kbd-skip\n tabindex=\"-1\"\n :disabled=\"disabled || selectedCount === totalLeaves || !totalLeaves\"\n @click=\"selectAll\"\n />\n <Button\n type=\"button\"\n label=\"Limpar\"\n text\n data-kbd-skip\n tabindex=\"-1\"\n :disabled=\"disabled || !selectedCount\"\n @click=\"clearAll\"\n />\n </div>\n </div>\n </slot>\n\n <IconField v-if=\"filter\" class=\"w-tree-select__filter\">\n <InputIcon class=\"pi pi-search\" />\n <InputText\n v-model=\"term\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n :aria-label=\"filterPlaceholder\"\n data-kbd-skip\n fluid\n />\n </IconField>\n\n <Tree\n v-if=\"!isEmpty\"\n :value=\"visibleTree\"\n selection-mode=\"checkbox\"\n :selection-keys=\"selectionKeys\"\n :expanded-keys=\"expandedKeys\"\n :loading=\"loading\"\n :aria-label=\"ariaLabel\"\n class=\"w-tree-select__tree\"\n @update:expanded-keys=\"expandedKeys = $event\"\n @node-select=\"onNodeToggle($event as TreeSelectNode, true)\"\n @node-unselect=\"onNodeToggle($event as TreeSelectNode, false)\"\n >\n <template #default=\"slotProps\">\n <slot name=\"node\" :node=\"slotProps.node as TreeSelectNode\">\n {{ (slotProps.node as TreeSelectNode).label }}\n </slot>\n </template>\n </Tree>\n\n <div v-else class=\"w-tree-select__empty\">\n <slot name=\"empty\" :filtering=\"isFiltering\">{{ emptyText }}</slot>\n </div>\n </div>\n</template>\n","// Suporte do WChart: carregamento preguiçoso do ECharts, temas construídos a\n// partir dos tokens CSS do consumidor e receitas de option compartilhadas.\n//\n// O `echarts` é dependência (o consumidor a recebe pelo npm), mas não entra no\n// bundle da suite: o WChart o carrega por `import()` dinâmico — quem nunca abre\n// um gráfico não paga por ele. O registro é tree-shaken: novo tipo de gráfico\n// entra AQUI, nunca na tela do consumidor.\n\ntype EchartsCore = typeof import('echarts/core')\n\nlet echartsPromise: Promise<EchartsCore | null> | null = null\n\n/** Núcleo do ECharts com barra/linha/pizza + tooltip/legenda/título registrados. */\nexport function loadEcharts(): Promise<EchartsCore | null> {\n if (!echartsPromise) {\n echartsPromise = Promise.all([\n import('echarts/core'),\n import('echarts/charts'),\n import('echarts/components'),\n import('echarts/renderers'),\n ])\n .then(([core, charts, components, renderers]) => {\n core.use([\n charts.BarChart,\n charts.LineChart,\n charts.PieChart,\n components.DatasetComponent,\n components.GridComponent,\n components.LegendComponent,\n components.TitleComponent,\n components.TooltipComponent,\n renderers.CanvasRenderer,\n renderers.SVGRenderer,\n ])\n return core\n })\n .catch(() => {\n // Offline/CSP: o WChart mostra o estado de indisponibilidade no lugar\n // do gráfico. Zera a promise para uma nova tentativa em outro mount.\n echartsPromise = null\n return null\n })\n }\n return echartsPromise\n}\n\nconst token = (name: string, fallback = ''): string => {\n if (typeof window === 'undefined') return fallback\n const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim()\n return value || fallback\n}\n\n/** Objeto de tema do ECharts (o registro formal de tema é dispensável no init). */\nexport type ChartTheme = Record<string, unknown>\n\n/** Option de gráfico — o contrato fica com o ECharts do consumidor. */\nexport type WChartOption = Record<string, unknown>\n\n/**\n * Tema construído a partir dos tokens do design system do consumidor no momento\n * da chamada (`--fg`, `--fg-muted`, `--border`, `--surface`, `--viz-1..6`,\n * `--font-sans`) — lê os valores já resolvidos para light/dark. O WChart\n * reconstrói sozinho quando o atributo de tema do documento muda.\n */\nexport function buildChartTheme(): ChartTheme {\n const fg = token('--fg')\n const fgMuted = token('--fg-muted')\n const border = token('--border')\n const surface = token('--surface')\n const fontFamily = token('--font-sans', 'sans-serif')\n\n const axis = {\n axisLine: { lineStyle: { color: border } },\n axisTick: { show: false },\n axisLabel: { color: fgMuted },\n splitLine: { lineStyle: { color: border } },\n nameTextStyle: { color: fgMuted },\n }\n\n return {\n color: [1, 2, 3, 4, 5, 6].map((i) => token(`--viz-${i}`)),\n backgroundColor: 'transparent',\n textStyle: { color: fg, fontFamily },\n categoryAxis: { ...axis, splitLine: { show: false } },\n valueAxis: axis,\n timeAxis: { ...axis, splitLine: { show: false } },\n legend: {\n textStyle: { color: fgMuted },\n icon: 'circle',\n itemWidth: 8,\n itemHeight: 8,\n },\n title: {\n textStyle: { color: fg },\n subtextStyle: { color: fgMuted },\n },\n tooltip: {\n backgroundColor: surface,\n borderColor: border,\n textStyle: { color: fg },\n // Sombra e raio vêm dos tokens do consumidor; sem token, fica sem sombra\n // (melhor plano B do que inventar uma cor fora do DS de quem consome).\n extraCssText: 'box-shadow: var(--shadow-md, none); border-radius: var(--radius-sm, 0);',\n },\n line: { symbolSize: 6, smooth: false },\n pie: {\n label: { color: fgMuted },\n itemStyle: { borderColor: surface, borderWidth: 2 },\n },\n bar: { itemStyle: { borderRadius: [3, 3, 0, 0] } },\n }\n}\n\n/**\n * Tema monocromático de impressão: escala neutra fixa (papel quase mono),\n * sem animação — o snapshot do PDF é estático. Os valores seguem a paleta de\n * impressão do design system do portal (slate).\n */\nexport function buildPrintTheme(): ChartTheme {\n const shades = ['#141a22', '#5a6573', '#8794a3', '#cdd6e2', '#212d3b', '#335277']\n const axis = {\n axisLine: { lineStyle: { color: '#cdd6e2' } },\n axisTick: { show: false },\n axisLabel: { color: '#5a6573' },\n splitLine: { lineStyle: { color: '#e2e8f0' } },\n nameTextStyle: { color: '#5a6573' },\n }\n\n return {\n animation: false,\n color: shades,\n backgroundColor: 'transparent',\n textStyle: { color: '#141a22' },\n categoryAxis: { ...axis, splitLine: { show: false } },\n valueAxis: axis,\n timeAxis: { ...axis, splitLine: { show: false } },\n legend: { textStyle: { color: '#5a6573' }, icon: 'circle', itemWidth: 8, itemHeight: 8 },\n title: { textStyle: { color: '#141a22' }, subtextStyle: { color: '#5a6573' } },\n line: { symbolSize: 5, smooth: false },\n pie: { label: { color: '#5a6573' }, itemStyle: { borderColor: '#ffffff', borderWidth: 1 } },\n bar: { itemStyle: { borderRadius: 0 } },\n }\n}\n\n/** Opções do overlay de loading alinhadas ao DS (o built-in do ECharts usa azul próprio). */\nexport function buildLoadingOptions(): Record<string, unknown> {\n return {\n text: '',\n color: token('--primary'),\n maskColor: 'transparent',\n spinnerRadius: 8,\n lineWidth: 2,\n }\n}\n\nexport interface DonutOptionInput {\n /** Fatias do donut. */\n data: { name: string; value: number }[]\n /** Formatador dos valores no tooltip. */\n format?: (value: number) => string\n /** Rótulo central (ex.: o total formatado). */\n centerLabel?: string\n /** Linha menor sob o rótulo central. */\n centerSub?: string\n}\n\n/**\n * Donut padrão: total no centro, legenda embaixo, tooltip com o mesmo\n * formatador dos valores.\n */\nexport function donutOption({\n data,\n format = (v) => String(v),\n centerLabel = '',\n centerSub = '',\n}: DonutOptionInput): WChartOption {\n return {\n tooltip: { trigger: 'item', valueFormatter: format },\n legend: { bottom: 0, type: 'scroll' },\n title: centerLabel\n ? {\n text: centerLabel,\n subtext: centerSub,\n left: 'center',\n top: '38%',\n itemGap: 2,\n textStyle: { fontSize: 20, fontWeight: 600 },\n subtextStyle: { fontSize: 11 },\n }\n : undefined,\n series: [\n {\n type: 'pie',\n radius: ['58%', '80%'],\n center: ['50%', '46%'],\n label: { show: false },\n labelLine: { show: false },\n data,\n },\n ],\n }\n}\n","<script setup lang=\"ts\">\n/**\n * Wrapper único de gráficos (Apache ECharts). As telas nunca importam\n * `echarts` direto: tema (claro/escuro via tokens CSS do consumidor),\n * densidade, loading, estado vazio e variante de impressão moram aqui.\n *\n * O ECharts entra por `import()` dinâmico (ver utils/chart.ts): quem nunca\n * renderiza um gráfico não paga pela biblioteca. Se o import falhar\n * (offline, CSP), o gráfico vira a mensagem de indisponibilidade.\n */\nimport { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'\nimport {\n buildChartTheme,\n buildLoadingOptions,\n buildPrintTheme,\n loadEcharts,\n type WChartOption,\n} from '@/utils/chart'\n\nimport type { EChartsType } from 'echarts/core'\n\nconst props = withDefaults(\n defineProps<{\n /** Option do ECharts — série, eixos, tooltip etc. */\n option: WChartOption\n /** Mostra o overlay de carregamento do ECharts (cores do DS). */\n loading?: boolean\n /** true quando a consulta voltou vazia — mostra o estado vazio no lugar do gráfico. */\n empty?: boolean\n emptyMessage?: string\n /** svg para impressão/PDF; canvas no dia a dia. */\n renderer?: 'canvas' | 'svg'\n /** Modo impressão: tema monocromático + SVG + sem animação (PDF Gotenberg). */\n print?: boolean\n /** Altura CSS do gráfico (default: token `--chart-h`, senão 18rem). */\n height?: string\n }>(),\n {\n loading: false,\n empty: false,\n emptyMessage: 'Sem dados para exibir',\n renderer: 'canvas',\n print: false,\n height: '',\n }\n)\n\nconst container = ref<HTMLElement | null>(null)\nconst chart = shallowRef<EChartsType | null>(null)\n/** ECharts indisponível (import falhou) — mostra aviso no lugar do canvas. */\nconst unavailable = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet themeObserver: MutationObserver | null = null\n\nconst applyOption = () => {\n if (!chart.value) return\n chart.value.setOption(props.option as never, true)\n}\n\nconst applyLoading = () => {\n if (!chart.value) return\n if (props.loading) chart.value.showLoading('default', buildLoadingOptions())\n else chart.value.hideLoading()\n}\n\n/** (Re)cria a instância — tema e renderer só mudam com init novo. */\nconst build = async () => {\n const core = await loadEcharts()\n if (!container.value) return\n if (!core) {\n unavailable.value = true\n return\n }\n unavailable.value = false\n chart.value?.dispose()\n chart.value = core.init(\n container.value,\n props.print ? buildPrintTheme() : buildChartTheme(),\n { renderer: props.print ? 'svg' : props.renderer }\n )\n applyOption()\n applyLoading()\n}\n\nwatch(() => props.option, applyOption, { deep: true })\nwatch(() => props.loading, applyLoading)\nwatch([() => props.print, () => props.renderer], () => void build())\n\nonMounted(() => {\n void build()\n\n resizeObserver = new ResizeObserver(() => chart.value?.resize())\n if (container.value) resizeObserver.observe(container.value)\n\n // Troca de tema do app (ex.: data-theme no <html>) muda os tokens CSS —\n // reconstrói o tema sem exigir prop de tema do consumidor.\n themeObserver = new MutationObserver(() => void build())\n themeObserver.observe(document.documentElement, { attributes: true })\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n themeObserver?.disconnect()\n chart.value?.dispose()\n chart.value = null\n})\n</script>\n\n<template>\n <div class=\"w-chart\" :style=\"height ? { '--w-chart-h': height } : undefined\">\n <div v-if=\"empty && !loading\" class=\"w-chart__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n <div v-else-if=\"unavailable\" class=\"w-chart__empty\">\n <slot name=\"unavailable\">Gráfico indisponível.</slot>\n </div>\n <div v-show=\"!empty && !unavailable\" ref=\"container\" class=\"w-chart__canvas\" />\n </div>\n</template>\n\n<style scoped>\n.w-chart {\n height: var(--w-chart-h, var(--chart-h, 18rem));\n min-width: 0;\n}\n\n.w-chart__canvas {\n height: 100%;\n width: 100%;\n}\n\n.w-chart__empty {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--fg-subtle, currentColor);\n font-size: var(--ui-font, inherit);\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * Wrapper único de gráficos (Apache ECharts). As telas nunca importam\n * `echarts` direto: tema (claro/escuro via tokens CSS do consumidor),\n * densidade, loading, estado vazio e variante de impressão moram aqui.\n *\n * O ECharts entra por `import()` dinâmico (ver utils/chart.ts): quem nunca\n * renderiza um gráfico não paga pela biblioteca. Se o import falhar\n * (offline, CSP), o gráfico vira a mensagem de indisponibilidade.\n */\nimport { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'\nimport {\n buildChartTheme,\n buildLoadingOptions,\n buildPrintTheme,\n loadEcharts,\n type WChartOption,\n} from '@/utils/chart'\n\nimport type { EChartsType } from 'echarts/core'\n\nconst props = withDefaults(\n defineProps<{\n /** Option do ECharts — série, eixos, tooltip etc. */\n option: WChartOption\n /** Mostra o overlay de carregamento do ECharts (cores do DS). */\n loading?: boolean\n /** true quando a consulta voltou vazia — mostra o estado vazio no lugar do gráfico. */\n empty?: boolean\n emptyMessage?: string\n /** svg para impressão/PDF; canvas no dia a dia. */\n renderer?: 'canvas' | 'svg'\n /** Modo impressão: tema monocromático + SVG + sem animação (PDF Gotenberg). */\n print?: boolean\n /** Altura CSS do gráfico (default: token `--chart-h`, senão 18rem). */\n height?: string\n }>(),\n {\n loading: false,\n empty: false,\n emptyMessage: 'Sem dados para exibir',\n renderer: 'canvas',\n print: false,\n height: '',\n }\n)\n\nconst container = ref<HTMLElement | null>(null)\nconst chart = shallowRef<EChartsType | null>(null)\n/** ECharts indisponível (import falhou) — mostra aviso no lugar do canvas. */\nconst unavailable = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet themeObserver: MutationObserver | null = null\n\nconst applyOption = () => {\n if (!chart.value) return\n chart.value.setOption(props.option as never, true)\n}\n\nconst applyLoading = () => {\n if (!chart.value) return\n if (props.loading) chart.value.showLoading('default', buildLoadingOptions())\n else chart.value.hideLoading()\n}\n\n/** (Re)cria a instância — tema e renderer só mudam com init novo. */\nconst build = async () => {\n const core = await loadEcharts()\n if (!container.value) return\n if (!core) {\n unavailable.value = true\n return\n }\n unavailable.value = false\n chart.value?.dispose()\n chart.value = core.init(\n container.value,\n props.print ? buildPrintTheme() : buildChartTheme(),\n { renderer: props.print ? 'svg' : props.renderer }\n )\n applyOption()\n applyLoading()\n}\n\nwatch(() => props.option, applyOption, { deep: true })\nwatch(() => props.loading, applyLoading)\nwatch([() => props.print, () => props.renderer], () => void build())\n\nonMounted(() => {\n void build()\n\n resizeObserver = new ResizeObserver(() => chart.value?.resize())\n if (container.value) resizeObserver.observe(container.value)\n\n // Troca de tema do app (ex.: data-theme no <html>) muda os tokens CSS —\n // reconstrói o tema sem exigir prop de tema do consumidor.\n themeObserver = new MutationObserver(() => void build())\n themeObserver.observe(document.documentElement, { attributes: true })\n})\n\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n themeObserver?.disconnect()\n chart.value?.dispose()\n chart.value = null\n})\n</script>\n\n<template>\n <div class=\"w-chart\" :style=\"height ? { '--w-chart-h': height } : undefined\">\n <div v-if=\"empty && !loading\" class=\"w-chart__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n <div v-else-if=\"unavailable\" class=\"w-chart__empty\">\n <slot name=\"unavailable\">Gráfico indisponível.</slot>\n </div>\n <div v-show=\"!empty && !unavailable\" ref=\"container\" class=\"w-chart__canvas\" />\n </div>\n</template>\n\n<style scoped>\n.w-chart {\n height: var(--w-chart-h, var(--chart-h, 18rem));\n min-width: 0;\n}\n\n.w-chart__canvas {\n height: 100%;\n width: 100%;\n}\n\n.w-chart__empty {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--fg-subtle, currentColor);\n font-size: var(--ui-font, inherit);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport Tag from 'primevue/tag'\n\nexport interface StatusMapping {\n label: string\n severity: string\n}\n\nconst props = withDefaults(\n defineProps<{\n value: string\n map?: Record<string, StatusMapping>\n }>(),\n {},\n)\n\nconst defaultMap: Record<string, StatusMapping> = {\n ativo: { label: 'Ativo', severity: 'success' },\n ativa: { label: 'Ativa', severity: 'success' },\n inativo: { label: 'Inativo', severity: 'danger' },\n inativa: { label: 'Inativa', severity: 'danger' },\n suspensa: { label: 'Suspensa', severity: 'warn' },\n suspenso: { label: 'Suspenso', severity: 'warn' },\n cancelada: { label: 'Cancelada', severity: 'danger' },\n cancelado: { label: 'Cancelado', severity: 'danger' },\n pendente: { label: 'Pendente', severity: 'warn' },\n confirmada: { label: 'Confirmada', severity: 'success' },\n confirmado: { label: 'Confirmado', severity: 'success' },\n vencida: { label: 'Vencida', severity: 'danger' },\n vencido: { label: 'Vencido', severity: 'danger' },\n concluido: { label: 'Concluído', severity: 'success' },\n concluida: { label: 'Concluída', severity: 'success' },\n em_progresso: { label: 'Em Progresso', severity: 'info' },\n em_andamento: { label: 'Em Andamento', severity: 'info' },\n expirado: { label: 'Expirado', severity: 'secondary' },\n expirada: { label: 'Expirada', severity: 'secondary' },\n aberto: { label: 'Aberto', severity: 'info' },\n aberta: { label: 'Aberta', severity: 'info' },\n fechado: { label: 'Fechado', severity: 'secondary' },\n fechada: { label: 'Fechada', severity: 'secondary' },\n pago: { label: 'Pago', severity: 'success' },\n paga: { label: 'Paga', severity: 'success' },\n inadimplente: { label: 'Inadimplente', severity: 'danger' },\n}\n\nconst statusInfo = computed(() => {\n const mapping = props.map ?? defaultMap\n return mapping[props.value] ?? { label: props.value, severity: 'secondary' }\n})\n</script>\n\n<template>\n <Tag :value=\"statusInfo.label\" :severity=\"statusInfo.severity as any\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport Tag from 'primevue/tag'\n\nexport interface StatusMapping {\n label: string\n severity: string\n}\n\nconst props = withDefaults(\n defineProps<{\n value: string\n map?: Record<string, StatusMapping>\n }>(),\n {},\n)\n\nconst defaultMap: Record<string, StatusMapping> = {\n ativo: { label: 'Ativo', severity: 'success' },\n ativa: { label: 'Ativa', severity: 'success' },\n inativo: { label: 'Inativo', severity: 'danger' },\n inativa: { label: 'Inativa', severity: 'danger' },\n suspensa: { label: 'Suspensa', severity: 'warn' },\n suspenso: { label: 'Suspenso', severity: 'warn' },\n cancelada: { label: 'Cancelada', severity: 'danger' },\n cancelado: { label: 'Cancelado', severity: 'danger' },\n pendente: { label: 'Pendente', severity: 'warn' },\n confirmada: { label: 'Confirmada', severity: 'success' },\n confirmado: { label: 'Confirmado', severity: 'success' },\n vencida: { label: 'Vencida', severity: 'danger' },\n vencido: { label: 'Vencido', severity: 'danger' },\n concluido: { label: 'Concluído', severity: 'success' },\n concluida: { label: 'Concluída', severity: 'success' },\n em_progresso: { label: 'Em Progresso', severity: 'info' },\n em_andamento: { label: 'Em Andamento', severity: 'info' },\n expirado: { label: 'Expirado', severity: 'secondary' },\n expirada: { label: 'Expirada', severity: 'secondary' },\n aberto: { label: 'Aberto', severity: 'info' },\n aberta: { label: 'Aberta', severity: 'info' },\n fechado: { label: 'Fechado', severity: 'secondary' },\n fechada: { label: 'Fechada', severity: 'secondary' },\n pago: { label: 'Pago', severity: 'success' },\n paga: { label: 'Paga', severity: 'success' },\n inadimplente: { label: 'Inadimplente', severity: 'danger' },\n}\n\nconst statusInfo = computed(() => {\n const mapping = props.map ?? defaultMap\n return mapping[props.value] ?? { label: props.value, severity: 'secondary' }\n})\n</script>\n\n<template>\n <Tag :value=\"statusInfo.label\" :severity=\"statusInfo.severity as any\" />\n</template>\n","<script setup lang=\"ts\">\nimport Button from 'primevue/button'\n\ndefineProps<{\n title: string\n subtitle?: string\n actionLabel?: string\n actionIcon?: string\n}>()\n\nconst emit = defineEmits<{\n action: []\n}>()\n</script>\n\n<template>\n <div class=\"w-page-header\">\n <div class=\"w-page-header-content\">\n <h2 class=\"w-page-header-title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"w-page-header-subtitle\">{{ subtitle }}</p>\n </div>\n <div class=\"w-page-header-actions\">\n <slot name=\"actions\" />\n <Button v-if=\"actionLabel\" :label=\"actionLabel\" :icon=\"actionIcon\" @click=\"emit('action')\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport Button from 'primevue/button'\n\ndefineProps<{\n title: string\n subtitle?: string\n actionLabel?: string\n actionIcon?: string\n}>()\n\nconst emit = defineEmits<{\n action: []\n}>()\n</script>\n\n<template>\n <div class=\"w-page-header\">\n <div class=\"w-page-header-content\">\n <h2 class=\"w-page-header-title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"w-page-header-subtitle\">{{ subtitle }}</p>\n </div>\n <div class=\"w-page-header-actions\">\n <slot name=\"actions\" />\n <Button v-if=\"actionLabel\" :label=\"actionLabel\" :icon=\"actionIcon\" @click=\"emit('action')\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport Button from 'primevue/button'\n\ndefineProps<{\n icon: string\n title: string\n description?: string\n actionLabel?: string\n actionIcon?: string\n}>()\n\nconst emit = defineEmits<{\n action: []\n}>()\n</script>\n\n<template>\n <div class=\"w-empty-state\">\n <div class=\"w-empty-state-icon\">\n <i :class=\"icon\" />\n </div>\n <p class=\"w-empty-state-title\">{{ title }}</p>\n <p v-if=\"description\" class=\"w-empty-state-description\">{{ description }}</p>\n <Button\n v-if=\"actionLabel\"\n :label=\"actionLabel\"\n :icon=\"actionIcon\"\n size=\"small\"\n class=\"mt-3\"\n @click=\"emit('action')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport Button from 'primevue/button'\n\ndefineProps<{\n icon: string\n title: string\n description?: string\n actionLabel?: string\n actionIcon?: string\n}>()\n\nconst emit = defineEmits<{\n action: []\n}>()\n</script>\n\n<template>\n <div class=\"w-empty-state\">\n <div class=\"w-empty-state-icon\">\n <i :class=\"icon\" />\n </div>\n <p class=\"w-empty-state-title\">{{ title }}</p>\n <p v-if=\"description\" class=\"w-empty-state-description\">{{ description }}</p>\n <Button\n v-if=\"actionLabel\"\n :label=\"actionLabel\"\n :icon=\"actionIcon\"\n size=\"small\"\n class=\"mt-3\"\n @click=\"emit('action')\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useRouter } from 'vue-router'\nimport Button from 'primevue/button'\nimport WStatusTag from './WStatusTag.vue'\n\nconst props = defineProps<{\n title: string\n subtitle?: string\n icon?: string\n backRoute?: string\n backTo?: string | Record<string, unknown>\n status?: string\n statusMap?: Record<string, { label: string; severity: string }>\n}>()\n\nconst router = useRouter()\n\nfunction goBack() {\n if (props.backTo) {\n router.push(typeof props.backTo === 'string' ? { name: props.backTo } : props.backTo)\n } else if (props.backRoute) {\n router.push({ name: props.backRoute })\n } else {\n router.back()\n }\n}\n</script>\n\n<template>\n <div class=\"w-detail-header\">\n <div class=\"w-detail-header-left\">\n <Button icon=\"pi pi-arrow-left\" text rounded @click=\"goBack\" />\n <i v-if=\"icon\" :class=\"icon\" class=\"w-detail-header-icon\" />\n <div class=\"w-detail-header-content\">\n <h2 class=\"w-detail-header-title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"w-detail-header-subtitle\">{{ subtitle }}</p>\n </div>\n <WStatusTag v-if=\"status\" :value=\"status\" :map=\"statusMap\" />\n </div>\n <div class=\"w-detail-header-actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useRouter } from 'vue-router'\nimport Button from 'primevue/button'\nimport WStatusTag from './WStatusTag.vue'\n\nconst props = defineProps<{\n title: string\n subtitle?: string\n icon?: string\n backRoute?: string\n backTo?: string | Record<string, unknown>\n status?: string\n statusMap?: Record<string, { label: string; severity: string }>\n}>()\n\nconst router = useRouter()\n\nfunction goBack() {\n if (props.backTo) {\n router.push(typeof props.backTo === 'string' ? { name: props.backTo } : props.backTo)\n } else if (props.backRoute) {\n router.push({ name: props.backRoute })\n } else {\n router.back()\n }\n}\n</script>\n\n<template>\n <div class=\"w-detail-header\">\n <div class=\"w-detail-header-left\">\n <Button icon=\"pi pi-arrow-left\" text rounded @click=\"goBack\" />\n <i v-if=\"icon\" :class=\"icon\" class=\"w-detail-header-icon\" />\n <div class=\"w-detail-header-content\">\n <h2 class=\"w-detail-header-title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"w-detail-header-subtitle\">{{ subtitle }}</p>\n </div>\n <WStatusTag v-if=\"status\" :value=\"status\" :map=\"statusMap\" />\n </div>\n <div class=\"w-detail-header-actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useFormatters } from '@/composables/useFormatters'\n\nexport interface InfoField {\n label: string\n value: string | number | null | undefined\n format?: 'currency' | 'date' | 'datetime' | 'number' | 'text'\n /** Unidade colada ao valor — usada na variante `metric` (ex.: `ha`, `kg`, `L`). */\n suffix?: string\n /** Casas decimais do `format: 'number'` (default 2). */\n decimals?: number\n}\n\nwithDefaults(\n defineProps<{\n title?: string\n fields: InfoField[]\n /** `default`: grade de rótulo em cima e valor embaixo.\n * `metric`: uma linha por campo — rótulo à esquerda, número grande à\n * direita, divisória entre as linhas (o aside \"Resumo\" de um documento). */\n variant?: 'default' | 'metric'\n }>(),\n { variant: 'default' },\n)\n\nconst { formatCurrency, formatDate, formatNumber } = useFormatters()\n\nfunction formatValue(field: InfoField): string {\n const v = field.value\n if (v == null || v === '') return '-'\n if (field.format === 'currency') return formatCurrency(Number(v))\n if (field.format === 'date') return formatDate(String(v))\n if (field.format === 'datetime') return formatDate(String(v), 'DD/MM/YYYY HH:mm')\n if (field.format === 'number') return formatNumber(Number(v), field.decimals ?? 2)\n return String(v)\n}\n</script>\n\n<template>\n <div class=\"w-info-card\" :class=\"`w-info-card--${variant}`\">\n <h3 v-if=\"title\" class=\"w-info-card-title\">{{ title }}</h3>\n\n <div v-if=\"variant === 'metric'\" class=\"w-info-card-metrics\">\n <div v-for=\"field in fields\" :key=\"field.label\" class=\"w-info-card-metric\">\n <span class=\"w-info-card-metric-label\">{{ field.label }}</span>\n <span class=\"w-info-card-metric-value\">{{ formatValue(field) }}</span>\n <span v-if=\"field.suffix\" class=\"w-info-card-metric-suffix\">{{ field.suffix }}</span>\n </div>\n </div>\n\n <div v-else class=\"w-info-card-grid\">\n <div v-for=\"field in fields\" :key=\"field.label\" class=\"w-info-card-field\">\n <span class=\"w-info-card-label\">{{ field.label }}</span>\n <span class=\"w-info-card-value\">{{ formatValue(field) }}</span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useFormatters } from '@/composables/useFormatters'\n\nexport interface InfoField {\n label: string\n value: string | number | null | undefined\n format?: 'currency' | 'date' | 'datetime' | 'number' | 'text'\n /** Unidade colada ao valor — usada na variante `metric` (ex.: `ha`, `kg`, `L`). */\n suffix?: string\n /** Casas decimais do `format: 'number'` (default 2). */\n decimals?: number\n}\n\nwithDefaults(\n defineProps<{\n title?: string\n fields: InfoField[]\n /** `default`: grade de rótulo em cima e valor embaixo.\n * `metric`: uma linha por campo — rótulo à esquerda, número grande à\n * direita, divisória entre as linhas (o aside \"Resumo\" de um documento). */\n variant?: 'default' | 'metric'\n }>(),\n { variant: 'default' },\n)\n\nconst { formatCurrency, formatDate, formatNumber } = useFormatters()\n\nfunction formatValue(field: InfoField): string {\n const v = field.value\n if (v == null || v === '') return '-'\n if (field.format === 'currency') return formatCurrency(Number(v))\n if (field.format === 'date') return formatDate(String(v))\n if (field.format === 'datetime') return formatDate(String(v), 'DD/MM/YYYY HH:mm')\n if (field.format === 'number') return formatNumber(Number(v), field.decimals ?? 2)\n return String(v)\n}\n</script>\n\n<template>\n <div class=\"w-info-card\" :class=\"`w-info-card--${variant}`\">\n <h3 v-if=\"title\" class=\"w-info-card-title\">{{ title }}</h3>\n\n <div v-if=\"variant === 'metric'\" class=\"w-info-card-metrics\">\n <div v-for=\"field in fields\" :key=\"field.label\" class=\"w-info-card-metric\">\n <span class=\"w-info-card-metric-label\">{{ field.label }}</span>\n <span class=\"w-info-card-metric-value\">{{ formatValue(field) }}</span>\n <span v-if=\"field.suffix\" class=\"w-info-card-metric-suffix\">{{ field.suffix }}</span>\n </div>\n </div>\n\n <div v-else class=\"w-info-card-grid\">\n <div v-for=\"field in fields\" :key=\"field.label\" class=\"w-info-card-field\">\n <span class=\"w-info-card-label\">{{ field.label }}</span>\n <span class=\"w-info-card-value\">{{ formatValue(field) }}</span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\n\ndefineProps<{\n label: string\n value: string | number\n icon?: string\n hint?: string\n severity?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral'\n trend?: {\n value: string\n direction?: 'up' | 'down' | 'neutral'\n }\n loading?: boolean\n}>()\n</script>\n\n<template>\n <article class=\"w-kpi-card\" :class=\"severity ? `w-kpi-card--${severity}` : ''\">\n <template v-if=\"loading\">\n <div class=\"w-kpi-card__loading\">\n <Skeleton shape=\"circle\" size=\"2.75rem\" />\n <div class=\"w-kpi-card__loading-content\">\n <Skeleton width=\"6rem\" height=\"0.75rem\" />\n <Skeleton width=\"7.5rem\" height=\"1.5rem\" />\n <Skeleton width=\"5rem\" height=\"0.75rem\" />\n </div>\n </div>\n </template>\n\n <template v-else>\n <div class=\"w-kpi-card__header\">\n <div v-if=\"icon || $slots.icon\" class=\"w-kpi-card__icon\">\n <slot name=\"icon\">\n <i v-if=\"icon\" :class=\"icon\" />\n </slot>\n </div>\n <div v-if=\"trend || $slots.trend\" class=\"w-kpi-card__trend\">\n <slot name=\"trend\">\n <span\n v-if=\"trend\"\n class=\"w-kpi-card__trend-badge\"\n :class=\"trend.direction ? `w-kpi-card__trend-badge--${trend.direction}` : ''\"\n >\n {{ trend.value }}\n </span>\n </slot>\n </div>\n </div>\n\n <div class=\"w-kpi-card__content\">\n <p class=\"w-kpi-card__label\">{{ label }}</p>\n <div class=\"w-kpi-card__value\">\n <slot name=\"value\">{{ value }}</slot>\n </div>\n <p v-if=\"hint || $slots.hint\" class=\"w-kpi-card__hint\">\n <slot name=\"hint\">{{ hint }}</slot>\n </p>\n </div>\n\n <footer v-if=\"$slots.footer\" class=\"w-kpi-card__footer\">\n <slot name=\"footer\" />\n </footer>\n </template>\n </article>\n</template>\n","<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\n\ndefineProps<{\n label: string\n value: string | number\n icon?: string\n hint?: string\n severity?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral'\n trend?: {\n value: string\n direction?: 'up' | 'down' | 'neutral'\n }\n loading?: boolean\n}>()\n</script>\n\n<template>\n <article class=\"w-kpi-card\" :class=\"severity ? `w-kpi-card--${severity}` : ''\">\n <template v-if=\"loading\">\n <div class=\"w-kpi-card__loading\">\n <Skeleton shape=\"circle\" size=\"2.75rem\" />\n <div class=\"w-kpi-card__loading-content\">\n <Skeleton width=\"6rem\" height=\"0.75rem\" />\n <Skeleton width=\"7.5rem\" height=\"1.5rem\" />\n <Skeleton width=\"5rem\" height=\"0.75rem\" />\n </div>\n </div>\n </template>\n\n <template v-else>\n <div class=\"w-kpi-card__header\">\n <div v-if=\"icon || $slots.icon\" class=\"w-kpi-card__icon\">\n <slot name=\"icon\">\n <i v-if=\"icon\" :class=\"icon\" />\n </slot>\n </div>\n <div v-if=\"trend || $slots.trend\" class=\"w-kpi-card__trend\">\n <slot name=\"trend\">\n <span\n v-if=\"trend\"\n class=\"w-kpi-card__trend-badge\"\n :class=\"trend.direction ? `w-kpi-card__trend-badge--${trend.direction}` : ''\"\n >\n {{ trend.value }}\n </span>\n </slot>\n </div>\n </div>\n\n <div class=\"w-kpi-card__content\">\n <p class=\"w-kpi-card__label\">{{ label }}</p>\n <div class=\"w-kpi-card__value\">\n <slot name=\"value\">{{ value }}</slot>\n </div>\n <p v-if=\"hint || $slots.hint\" class=\"w-kpi-card__hint\">\n <slot name=\"hint\">{{ hint }}</slot>\n </p>\n </div>\n\n <footer v-if=\"$slots.footer\" class=\"w-kpi-card__footer\">\n <slot name=\"footer\" />\n </footer>\n </template>\n </article>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { KpiItem } from '@/types/kpi'\nimport WKpiCard from './WKpiCard.vue'\n\nconst props = withDefaults(\n defineProps<{\n items?: KpiItem[]\n /**\n * Número fixo de colunas (2–6) ou 'auto' para preencher a largura com quantos\n * KPIs couberem (responsivo, sem overflow). Use 'auto' em dashboards que devem\n * aproveitar o máximo da tela.\n */\n columns?: 2 | 3 | 4 | 5 | 6 | 'auto'\n dense?: boolean\n }>(),\n {\n items: () => [],\n columns: 4,\n dense: false,\n },\n)\n\nconst gridClass = computed(() => [\n props.columns === 'auto' ? 'w-kpi-grid--auto' : `w-kpi-grid--cols-${props.columns}`,\n { 'w-kpi-grid--dense': props.dense },\n])\n</script>\n\n<template>\n <div class=\"w-kpi-grid\" :class=\"gridClass\">\n <template v-if=\"$slots.item\">\n <slot v-for=\"(item, index) in items\" :key=\"index\" name=\"item\" :item=\"item\" :index=\"index\" />\n </template>\n <template v-else>\n <WKpiCard\n v-for=\"(item, index) in items\"\n :key=\"index\"\n :label=\"item.label\"\n :value=\"item.value\"\n :icon=\"item.icon\"\n :severity=\"item.severity || 'primary'\"\n :hint=\"item.hint\"\n :trend=\"item.trend\"\n :loading=\"item.loading\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { KpiItem } from '@/types/kpi'\nimport WKpiCard from './WKpiCard.vue'\n\nconst props = withDefaults(\n defineProps<{\n items?: KpiItem[]\n /**\n * Número fixo de colunas (2–6) ou 'auto' para preencher a largura com quantos\n * KPIs couberem (responsivo, sem overflow). Use 'auto' em dashboards que devem\n * aproveitar o máximo da tela.\n */\n columns?: 2 | 3 | 4 | 5 | 6 | 'auto'\n dense?: boolean\n }>(),\n {\n items: () => [],\n columns: 4,\n dense: false,\n },\n)\n\nconst gridClass = computed(() => [\n props.columns === 'auto' ? 'w-kpi-grid--auto' : `w-kpi-grid--cols-${props.columns}`,\n { 'w-kpi-grid--dense': props.dense },\n])\n</script>\n\n<template>\n <div class=\"w-kpi-grid\" :class=\"gridClass\">\n <template v-if=\"$slots.item\">\n <slot v-for=\"(item, index) in items\" :key=\"index\" name=\"item\" :item=\"item\" :index=\"index\" />\n </template>\n <template v-else>\n <WKpiCard\n v-for=\"(item, index) in items\"\n :key=\"index\"\n :label=\"item.label\"\n :value=\"item.value\"\n :icon=\"item.icon\"\n :severity=\"item.severity || 'primary'\"\n :hint=\"item.hint\"\n :trend=\"item.trend\"\n :loading=\"item.loading\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n title: string\n subtitle?: string\n icon?: string\n compact?: boolean\n}>()\n</script>\n\n<template>\n <div class=\"w-section-header\" :class=\"{ 'w-section-header--compact': compact }\">\n <div class=\"w-section-header__main\">\n <div v-if=\"icon || $slots.icon\" class=\"w-section-header__icon\">\n <slot name=\"icon\">\n <i v-if=\"icon\" :class=\"icon\" />\n </slot>\n </div>\n <div class=\"w-section-header__content\">\n <div class=\"w-section-header__title-row\">\n <h3 class=\"w-section-header__title\">{{ title }}</h3>\n <slot name=\"meta\" />\n </div>\n <p v-if=\"subtitle\" class=\"w-section-header__subtitle\">{{ subtitle }}</p>\n </div>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-section-header__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n title: string\n subtitle?: string\n icon?: string\n compact?: boolean\n}>()\n</script>\n\n<template>\n <div class=\"w-section-header\" :class=\"{ 'w-section-header--compact': compact }\">\n <div class=\"w-section-header__main\">\n <div v-if=\"icon || $slots.icon\" class=\"w-section-header__icon\">\n <slot name=\"icon\">\n <i v-if=\"icon\" :class=\"icon\" />\n </slot>\n </div>\n <div class=\"w-section-header__content\">\n <div class=\"w-section-header__title-row\">\n <h3 class=\"w-section-header__title\">{{ title }}</h3>\n <slot name=\"meta\" />\n </div>\n <p v-if=\"subtitle\" class=\"w-section-header__subtitle\">{{ subtitle }}</p>\n </div>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-section-header__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n title: string\n description?: string\n variant?: 'default' | 'muted' | 'outlined'\n}>()\n</script>\n\n<template>\n <section class=\"w-form-section\" :class=\"variant ? `w-form-section--${variant}` : ''\">\n <div v-if=\"title || description || $slots.actions\" class=\"w-form-section__header\">\n <div class=\"w-form-section__content\">\n <h3 class=\"w-form-section__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"w-form-section__description\">{{ description }}</p>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-form-section__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n <div class=\"w-form-section__body\">\n <slot />\n </div>\n </section>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n title: string\n description?: string\n variant?: 'default' | 'muted' | 'outlined'\n}>()\n</script>\n\n<template>\n <section class=\"w-form-section\" :class=\"variant ? `w-form-section--${variant}` : ''\">\n <div v-if=\"title || description || $slots.actions\" class=\"w-form-section__header\">\n <div class=\"w-form-section__content\">\n <h3 class=\"w-form-section__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"w-form-section__description\">{{ description }}</p>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-form-section__actions\">\n <slot name=\"actions\" />\n </div>\n </div>\n <div class=\"w-form-section__body\">\n <slot />\n </div>\n </section>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(\n defineProps<{\n align?: 'between' | 'start' | 'end'\n stackOnMobile?: boolean\n }>(),\n {\n align: 'between',\n stackOnMobile: true,\n },\n)\n</script>\n\n<template>\n <div\n class=\"w-action-bar\"\n :class=\"[`w-action-bar--${align}`, { 'w-action-bar--stack': stackOnMobile }]\"\n >\n <div v-if=\"$slots.primary || $slots.default\" class=\"w-action-bar__primary\">\n <slot name=\"primary\">\n <slot />\n </slot>\n </div>\n <div v-if=\"$slots.filters\" class=\"w-action-bar__filters\">\n <slot name=\"filters\" />\n </div>\n <div v-if=\"$slots.secondary\" class=\"w-action-bar__secondary\">\n <slot name=\"secondary\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nwithDefaults(\n defineProps<{\n align?: 'between' | 'start' | 'end'\n stackOnMobile?: boolean\n }>(),\n {\n align: 'between',\n stackOnMobile: true,\n },\n)\n</script>\n\n<template>\n <div\n class=\"w-action-bar\"\n :class=\"[`w-action-bar--${align}`, { 'w-action-bar--stack': stackOnMobile }]\"\n >\n <div v-if=\"$slots.primary || $slots.default\" class=\"w-action-bar__primary\">\n <slot name=\"primary\">\n <slot />\n </slot>\n </div>\n <div v-if=\"$slots.filters\" class=\"w-action-bar__filters\">\n <slot name=\"filters\" />\n </div>\n <div v-if=\"$slots.secondary\" class=\"w-action-bar__secondary\">\n <slot name=\"secondary\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface ProgressStep {\n key: string\n label: string\n description?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n steps: ProgressStep[]\n currentStep: string\n orientation?: 'horizontal' | 'vertical'\n }>(),\n {\n orientation: 'horizontal',\n },\n)\n\nconst currentIndex = computed(() => props.steps.findIndex((step) => step.key === props.currentStep))\n\nfunction stepState(index: number) {\n if (index < currentIndex.value) return 'done'\n if (index === currentIndex.value) return 'current'\n return 'pending'\n}\n</script>\n\n<template>\n <div class=\"w-progress-flow\" :class=\"`w-progress-flow--${orientation}`\">\n <div\n v-for=\"(step, index) in steps\"\n :key=\"step.key\"\n class=\"w-progress-flow__step\"\n :class=\"`w-progress-flow__step--${stepState(index)}`\"\n >\n <slot name=\"step\" :step=\"step\" :index=\"index\" :state=\"stepState(index)\">\n <div class=\"w-progress-flow__marker\">\n <span>{{ index + 1 }}</span>\n </div>\n <div class=\"w-progress-flow__content\">\n <p class=\"w-progress-flow__label\">{{ step.label }}</p>\n <p v-if=\"step.description\" class=\"w-progress-flow__description\">{{ step.description }}</p>\n </div>\n </slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface ProgressStep {\n key: string\n label: string\n description?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n steps: ProgressStep[]\n currentStep: string\n orientation?: 'horizontal' | 'vertical'\n }>(),\n {\n orientation: 'horizontal',\n },\n)\n\nconst currentIndex = computed(() => props.steps.findIndex((step) => step.key === props.currentStep))\n\nfunction stepState(index: number) {\n if (index < currentIndex.value) return 'done'\n if (index === currentIndex.value) return 'current'\n return 'pending'\n}\n</script>\n\n<template>\n <div class=\"w-progress-flow\" :class=\"`w-progress-flow--${orientation}`\">\n <div\n v-for=\"(step, index) in steps\"\n :key=\"step.key\"\n class=\"w-progress-flow__step\"\n :class=\"`w-progress-flow__step--${stepState(index)}`\"\n >\n <slot name=\"step\" :step=\"step\" :index=\"index\" :state=\"stepState(index)\">\n <div class=\"w-progress-flow__marker\">\n <span>{{ index + 1 }}</span>\n </div>\n <div class=\"w-progress-flow__content\">\n <p class=\"w-progress-flow__label\">{{ step.label }}</p>\n <p v-if=\"step.description\" class=\"w-progress-flow__description\">{{ step.description }}</p>\n </div>\n </slot>\n </div>\n </div>\n</template>\n","import type { ComputedRef, InjectionKey } from 'vue'\n\n/**\n * Contrato entre WStepFlow (container) e WStepSection (etapa). Vive num módulo\n * próprio porque `<script setup>` não exporta valores — a chave precisa ser\n * importável pelos dois SFCs.\n *\n * Quem é a última etapa (a que não desenha linha conectora) NÃO passa por aqui:\n * é `:last-child` no CSS. Registrar as etapas em runtime só serviria para\n * descobrir, com um tick de atraso, algo que o seletor já sabe na primeira\n * pintura.\n */\n/** Como o flow arruma as etapas: coluna (padrão) ou régua no topo. */\nexport type StepFlowOrientation = 'vertical' | 'horizontal'\n\nexport interface StepFlowContext {\n /** Etapa aberta. `0` = todas fechadas. */\n active: ComputedRef<number>\n /** Alterna a etapa: abre a informada, ou fecha tudo se já era a aberta. */\n toggle: (step: number) => void\n disabled: ComputedRef<boolean>\n /**\n * Orientação do flow. Vem pelo contexto de propósito: a etapa se desenha\n * conforme o container, sem o consumidor repetir a prop em cada WStepSection.\n * Fora de um flow a etapa assume `vertical` (card colapsável avulso).\n */\n orientation: ComputedRef<StepFlowOrientation>\n}\n\nexport const W_STEP_FLOW_KEY: InjectionKey<StepFlowContext> = Symbol('w-step-flow')\n","<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue'\nimport { W_STEP_FLOW_KEY, type StepFlowOrientation } from '@/utils/stepFlow'\n\n/**\n * Etapas numeradas e colapsáveis (a espinha do editor de documento).\n * Diferente do WProgressFlow, que só ILUSTRA o progresso: aqui cada etapa é um\n * bloco de conteúdo que abre e fecha, e o `v-model` diz qual está aberta.\n *\n * `0` = todas fechadas. É estado do consumidor de propósito: \"continuar para a\n * próxima etapa\" é uma decisão da tela, não do componente.\n */\nconst props = withDefaults(\n defineProps<{\n /** Número da etapa aberta (`v-model`). `0` fecha todas. */\n modelValue?: number\n /**\n * `vertical` empilha as etapas numa coluna (cada uma é um card).\n * `horizontal` põe os cabeçalhos lado a lado numa régua e abre só o corpo da\n * etapa ativa embaixo, na largura inteira — abaixo de 768px cai sozinho\n * para o empilhamento vertical (a régua não cabe).\n */\n orientation?: StepFlowOrientation\n /** Trava o clique no cabeçalho de todas as etapas. */\n disabled?: boolean\n }>(),\n {\n modelValue: 0,\n orientation: 'vertical',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [step: number] }>()\n\nconst active = computed(() => props.modelValue)\nconst disabled = computed(() => props.disabled)\nconst orientation = computed(() => props.orientation)\n\nfunction toggle(step: number) {\n if (props.disabled) return\n emit('update:modelValue', props.modelValue === step ? 0 : step)\n}\n\nprovide(W_STEP_FLOW_KEY, { active, toggle, disabled, orientation })\n\nconst root = ref<HTMLElement | null>(null)\n\n/**\n * Setas / Home / End andam pela régua horizontal. Sem roving tabindex: os\n * cabeçalhos são botões e continuam todos no Tab — a seta é atalho, não a única\n * forma de chegar lá. Na vertical o Tab já resolve, então nem entra.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (props.orientation !== 'horizontal') return\n const passo = { ArrowRight: 1, ArrowLeft: -1, Home: 0, End: 0 }[event.key]\n if (passo === undefined) return\n\n const triggers = Array.from(\n root.value?.querySelectorAll<HTMLButtonElement>(\n '.w-step-section__head .w-step-section__trigger:not(:disabled)',\n ) ?? [],\n )\n const atual = triggers.indexOf(document.activeElement as HTMLButtonElement)\n if (atual < 0 || triggers.length === 0) return\n\n const alvo =\n event.key === 'Home'\n ? 0\n : event.key === 'End'\n ? triggers.length - 1\n : (atual + passo + triggers.length) % triggers.length\n\n event.preventDefault()\n triggers[alvo].focus()\n}\n</script>\n\n<template>\n <div ref=\"root\" class=\"w-step-flow\" :class=\"`w-step-flow--${orientation}`\" @keydown=\"onKeydown\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue'\nimport { W_STEP_FLOW_KEY, type StepFlowOrientation } from '@/utils/stepFlow'\n\n/**\n * Etapas numeradas e colapsáveis (a espinha do editor de documento).\n * Diferente do WProgressFlow, que só ILUSTRA o progresso: aqui cada etapa é um\n * bloco de conteúdo que abre e fecha, e o `v-model` diz qual está aberta.\n *\n * `0` = todas fechadas. É estado do consumidor de propósito: \"continuar para a\n * próxima etapa\" é uma decisão da tela, não do componente.\n */\nconst props = withDefaults(\n defineProps<{\n /** Número da etapa aberta (`v-model`). `0` fecha todas. */\n modelValue?: number\n /**\n * `vertical` empilha as etapas numa coluna (cada uma é um card).\n * `horizontal` põe os cabeçalhos lado a lado numa régua e abre só o corpo da\n * etapa ativa embaixo, na largura inteira — abaixo de 768px cai sozinho\n * para o empilhamento vertical (a régua não cabe).\n */\n orientation?: StepFlowOrientation\n /** Trava o clique no cabeçalho de todas as etapas. */\n disabled?: boolean\n }>(),\n {\n modelValue: 0,\n orientation: 'vertical',\n disabled: false,\n },\n)\n\nconst emit = defineEmits<{ 'update:modelValue': [step: number] }>()\n\nconst active = computed(() => props.modelValue)\nconst disabled = computed(() => props.disabled)\nconst orientation = computed(() => props.orientation)\n\nfunction toggle(step: number) {\n if (props.disabled) return\n emit('update:modelValue', props.modelValue === step ? 0 : step)\n}\n\nprovide(W_STEP_FLOW_KEY, { active, toggle, disabled, orientation })\n\nconst root = ref<HTMLElement | null>(null)\n\n/**\n * Setas / Home / End andam pela régua horizontal. Sem roving tabindex: os\n * cabeçalhos são botões e continuam todos no Tab — a seta é atalho, não a única\n * forma de chegar lá. Na vertical o Tab já resolve, então nem entra.\n */\nfunction onKeydown(event: KeyboardEvent) {\n if (props.orientation !== 'horizontal') return\n const passo = { ArrowRight: 1, ArrowLeft: -1, Home: 0, End: 0 }[event.key]\n if (passo === undefined) return\n\n const triggers = Array.from(\n root.value?.querySelectorAll<HTMLButtonElement>(\n '.w-step-section__head .w-step-section__trigger:not(:disabled)',\n ) ?? [],\n )\n const atual = triggers.indexOf(document.activeElement as HTMLButtonElement)\n if (atual < 0 || triggers.length === 0) return\n\n const alvo =\n event.key === 'Home'\n ? 0\n : event.key === 'End'\n ? triggers.length - 1\n : (atual + passo + triggers.length) % triggers.length\n\n event.preventDefault()\n triggers[alvo].focus()\n}\n</script>\n\n<template>\n <div ref=\"root\" class=\"w-step-flow\" :class=\"`w-step-flow--${orientation}`\" @keydown=\"onKeydown\">\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from 'vue'\nimport { W_STEP_FLOW_KEY } from '@/utils/stepFlow'\n\n/**\n * Uma etapa do WStepFlow: badge numerada + linha conectora, cabeçalho clicável\n * (título, contador e resumo) e corpo colapsável com rodapé de ação.\n *\n * A orientação NÃO é prop daqui: vem do contexto do flow. Na vertical a etapa é\n * um card na coluna; na horizontal o cabeçalho vira um trecho da régua e só o\n * corpo da etapa ativa é desenhado, embaixo, na largura inteira.\n *\n * Fora de um WStepFlow o componente ainda funciona: ele cai num estado local\n * aberto (e em vertical), para poder ser usado como card colapsável avulso.\n */\nconst props = withDefaults(\n defineProps<{\n /** Número da etapa (1-based) — é o valor que o `v-model` do flow carrega. */\n step: number\n title: string\n /** Texto à direita do título (o \"resumo\" da etapa fechada). */\n summary?: string\n /** Badge de contagem entre o título e o resumo. */\n count?: number | string\n disabled?: boolean\n }>(),\n { disabled: false },\n)\n\nconst flow = inject(W_STEP_FLOW_KEY, null)\n\n/** Sem flow o componente é dono do próprio estado (card colapsável avulso). */\nconst localOpen = ref(true)\n\nconst open = computed(() => (flow ? flow.active.value === props.step : localOpen.value))\nconst isDisabled = computed(() => props.disabled || (flow?.disabled.value ?? false))\nconst horizontal = computed(() => flow?.orientation.value === 'horizontal')\n/** Fora do flow não há próxima etapa, então não há linha para ligar. Dentro do\n * flow a linha existe sempre e o CSS a apaga na ponta (`:last-child` na\n * vertical, `:first-child` na horizontal, onde a linha vem antes da badge). */\nconst hasLine = computed(() => flow !== null)\n/**\n * \"Já passei por aqui\" — inferido da posição, não de validação: o componente não\n * conhece regra de negócio. Só a régua horizontal marca isso; a coluna vertical\n * continua exatamente como era.\n */\nconst done = computed(() => horizontal.value && flow != null && props.step < flow.active.value)\n\nfunction toggle() {\n if (isDisabled.value) return\n if (flow) flow.toggle(props.step)\n else localOpen.value = !localOpen.value\n}\n</script>\n\n<template>\n <div v-if=\"!horizontal\" class=\"w-step-section\" :class=\"{ 'w-step-section--open': open }\">\n <div class=\"w-step-section__rail\">\n <span\n class=\"w-step-section__badge\"\n :class=\"{ 'w-step-section__badge--on': open }\"\n aria-hidden=\"true\"\n >\n {{ step }}\n </span>\n <span v-if=\"hasLine\" class=\"w-step-section__line\" />\n </div>\n\n <div class=\"w-step-section__body\">\n <div class=\"w-step-section__card\">\n <div class=\"w-step-section__header\">\n <button\n type=\"button\"\n class=\"w-step-section__trigger\"\n :aria-expanded=\"open\"\n :aria-current=\"open ? 'step' : undefined\"\n :disabled=\"isDisabled\"\n @click=\"toggle\"\n >\n <i\n class=\"w-step-section__chevron\"\n :class=\"open ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\"\n />\n <h2 class=\"w-step-section__title\">{{ title }}</h2>\n <span v-if=\"count != null\" class=\"w-step-section__count\">{{ count }}</span>\n <span v-if=\"summary\" class=\"w-step-section__summary\">{{ summary }}</span>\n </button>\n <div v-if=\"$slots.actions\" class=\"w-step-section__actions\">\n <slot name=\"actions\" :open=\"open\" />\n </div>\n </div>\n\n <template v-if=\"open\">\n <div class=\"w-step-section__content\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"w-step-section__footer\">\n <slot name=\"footer\" />\n </div>\n </template>\n </div>\n </div>\n </div>\n\n <div\n v-else\n class=\"w-step-section w-step-section--horizontal\"\n :class=\"{ 'w-step-section--open': open, 'w-step-section--done': done }\"\n >\n <div class=\"w-step-section__head\">\n <span\n v-if=\"hasLine\"\n class=\"w-step-section__line\"\n :class=\"{ 'w-step-section__line--done': done || open }\"\n />\n <button\n type=\"button\"\n class=\"w-step-section__trigger\"\n :aria-expanded=\"open\"\n :aria-current=\"open ? 'step' : undefined\"\n :disabled=\"isDisabled\"\n @click=\"toggle\"\n >\n <span\n class=\"w-step-section__badge\"\n :class=\"{\n 'w-step-section__badge--on': open,\n 'w-step-section__badge--done': done,\n }\"\n aria-hidden=\"true\"\n >\n <i v-if=\"done\" class=\"pi pi-check\" />\n <template v-else>{{ step }}</template>\n </span>\n <h2 class=\"w-step-section__title\">{{ title }}</h2>\n <span v-if=\"count != null\" class=\"w-step-section__count\">{{ count }}</span>\n <span v-if=\"summary\" class=\"w-step-section__summary\">{{ summary }}</span>\n </button>\n <div v-if=\"$slots.actions\" class=\"w-step-section__actions\">\n <slot name=\"actions\" :open=\"open\" />\n </div>\n </div>\n\n <div v-if=\"open\" class=\"w-step-section__panel\">\n <div class=\"w-step-section__content\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"w-step-section__footer\">\n <slot name=\"footer\" />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from 'vue'\nimport { W_STEP_FLOW_KEY } from '@/utils/stepFlow'\n\n/**\n * Uma etapa do WStepFlow: badge numerada + linha conectora, cabeçalho clicável\n * (título, contador e resumo) e corpo colapsável com rodapé de ação.\n *\n * A orientação NÃO é prop daqui: vem do contexto do flow. Na vertical a etapa é\n * um card na coluna; na horizontal o cabeçalho vira um trecho da régua e só o\n * corpo da etapa ativa é desenhado, embaixo, na largura inteira.\n *\n * Fora de um WStepFlow o componente ainda funciona: ele cai num estado local\n * aberto (e em vertical), para poder ser usado como card colapsável avulso.\n */\nconst props = withDefaults(\n defineProps<{\n /** Número da etapa (1-based) — é o valor que o `v-model` do flow carrega. */\n step: number\n title: string\n /** Texto à direita do título (o \"resumo\" da etapa fechada). */\n summary?: string\n /** Badge de contagem entre o título e o resumo. */\n count?: number | string\n disabled?: boolean\n }>(),\n { disabled: false },\n)\n\nconst flow = inject(W_STEP_FLOW_KEY, null)\n\n/** Sem flow o componente é dono do próprio estado (card colapsável avulso). */\nconst localOpen = ref(true)\n\nconst open = computed(() => (flow ? flow.active.value === props.step : localOpen.value))\nconst isDisabled = computed(() => props.disabled || (flow?.disabled.value ?? false))\nconst horizontal = computed(() => flow?.orientation.value === 'horizontal')\n/** Fora do flow não há próxima etapa, então não há linha para ligar. Dentro do\n * flow a linha existe sempre e o CSS a apaga na ponta (`:last-child` na\n * vertical, `:first-child` na horizontal, onde a linha vem antes da badge). */\nconst hasLine = computed(() => flow !== null)\n/**\n * \"Já passei por aqui\" — inferido da posição, não de validação: o componente não\n * conhece regra de negócio. Só a régua horizontal marca isso; a coluna vertical\n * continua exatamente como era.\n */\nconst done = computed(() => horizontal.value && flow != null && props.step < flow.active.value)\n\nfunction toggle() {\n if (isDisabled.value) return\n if (flow) flow.toggle(props.step)\n else localOpen.value = !localOpen.value\n}\n</script>\n\n<template>\n <div v-if=\"!horizontal\" class=\"w-step-section\" :class=\"{ 'w-step-section--open': open }\">\n <div class=\"w-step-section__rail\">\n <span\n class=\"w-step-section__badge\"\n :class=\"{ 'w-step-section__badge--on': open }\"\n aria-hidden=\"true\"\n >\n {{ step }}\n </span>\n <span v-if=\"hasLine\" class=\"w-step-section__line\" />\n </div>\n\n <div class=\"w-step-section__body\">\n <div class=\"w-step-section__card\">\n <div class=\"w-step-section__header\">\n <button\n type=\"button\"\n class=\"w-step-section__trigger\"\n :aria-expanded=\"open\"\n :aria-current=\"open ? 'step' : undefined\"\n :disabled=\"isDisabled\"\n @click=\"toggle\"\n >\n <i\n class=\"w-step-section__chevron\"\n :class=\"open ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\"\n />\n <h2 class=\"w-step-section__title\">{{ title }}</h2>\n <span v-if=\"count != null\" class=\"w-step-section__count\">{{ count }}</span>\n <span v-if=\"summary\" class=\"w-step-section__summary\">{{ summary }}</span>\n </button>\n <div v-if=\"$slots.actions\" class=\"w-step-section__actions\">\n <slot name=\"actions\" :open=\"open\" />\n </div>\n </div>\n\n <template v-if=\"open\">\n <div class=\"w-step-section__content\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"w-step-section__footer\">\n <slot name=\"footer\" />\n </div>\n </template>\n </div>\n </div>\n </div>\n\n <div\n v-else\n class=\"w-step-section w-step-section--horizontal\"\n :class=\"{ 'w-step-section--open': open, 'w-step-section--done': done }\"\n >\n <div class=\"w-step-section__head\">\n <span\n v-if=\"hasLine\"\n class=\"w-step-section__line\"\n :class=\"{ 'w-step-section__line--done': done || open }\"\n />\n <button\n type=\"button\"\n class=\"w-step-section__trigger\"\n :aria-expanded=\"open\"\n :aria-current=\"open ? 'step' : undefined\"\n :disabled=\"isDisabled\"\n @click=\"toggle\"\n >\n <span\n class=\"w-step-section__badge\"\n :class=\"{\n 'w-step-section__badge--on': open,\n 'w-step-section__badge--done': done,\n }\"\n aria-hidden=\"true\"\n >\n <i v-if=\"done\" class=\"pi pi-check\" />\n <template v-else>{{ step }}</template>\n </span>\n <h2 class=\"w-step-section__title\">{{ title }}</h2>\n <span v-if=\"count != null\" class=\"w-step-section__count\">{{ count }}</span>\n <span v-if=\"summary\" class=\"w-step-section__summary\">{{ summary }}</span>\n </button>\n <div v-if=\"$slots.actions\" class=\"w-step-section__actions\">\n <slot name=\"actions\" :open=\"open\" />\n </div>\n </div>\n\n <div v-if=\"open\" class=\"w-step-section__panel\">\n <div class=\"w-step-section__content\">\n <slot />\n </div>\n <div v-if=\"$slots.footer\" class=\"w-step-section__footer\">\n <slot name=\"footer\" />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { CheckListItem, CheckListLevel } from '@/types/checkList'\n\n/**\n * Lista de verificações de um documento (o aside \"Verificações\"): um ícone por\n * nível e o contador `N/M` de quantas passaram, no cabeçalho.\n *\n * A avaliação é sempre do backend/serviço — o componente só desenha o que\n * recebe. Ele não conhece regra de negócio nenhuma.\n */\nconst props = withDefaults(\n defineProps<{\n title?: string\n items?: CheckListItem[]\n /** Esconde o contador `N/M` do cabeçalho. */\n showCount?: boolean\n emptyMessage?: string\n }>(),\n {\n items: () => [],\n showCount: true,\n emptyMessage: 'Nenhuma verificação',\n },\n)\n\nconst okCount = computed(() => props.items.filter((i) => i.nivel === 'ok').length)\nconst countLabel = computed(() => `${okCount.value}/${props.items.length}`)\n\nconst ICONS: Record<CheckListLevel, string> = {\n ok: 'pi pi-check-circle',\n warn: 'pi pi-exclamation-triangle',\n bad: 'pi pi-times-circle',\n}\n\nfunction iconOf(nivel: CheckListLevel): string {\n return ICONS[nivel] ?? ICONS.warn\n}\n</script>\n\n<template>\n <div class=\"w-check-list\">\n <div v-if=\"title || showCount\" class=\"w-check-list__header\">\n <span class=\"w-check-list__title\">{{ title }}</span>\n <span v-if=\"showCount\" class=\"w-check-list__count\">{{ countLabel }}</span>\n </div>\n\n <ul v-if=\"items.length\" class=\"w-check-list__items\">\n <li v-for=\"(item, index) in items\" :key=\"`${item.label}-${index}`\" class=\"w-check-list__item\">\n <i :class=\"[iconOf(item.nivel), `w-check-list__icon--${item.nivel}`]\" />\n <span class=\"w-check-list__label\">{{ item.label }}</span>\n </li>\n </ul>\n\n <div v-else class=\"w-check-list__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { CheckListItem, CheckListLevel } from '@/types/checkList'\n\n/**\n * Lista de verificações de um documento (o aside \"Verificações\"): um ícone por\n * nível e o contador `N/M` de quantas passaram, no cabeçalho.\n *\n * A avaliação é sempre do backend/serviço — o componente só desenha o que\n * recebe. Ele não conhece regra de negócio nenhuma.\n */\nconst props = withDefaults(\n defineProps<{\n title?: string\n items?: CheckListItem[]\n /** Esconde o contador `N/M` do cabeçalho. */\n showCount?: boolean\n emptyMessage?: string\n }>(),\n {\n items: () => [],\n showCount: true,\n emptyMessage: 'Nenhuma verificação',\n },\n)\n\nconst okCount = computed(() => props.items.filter((i) => i.nivel === 'ok').length)\nconst countLabel = computed(() => `${okCount.value}/${props.items.length}`)\n\nconst ICONS: Record<CheckListLevel, string> = {\n ok: 'pi pi-check-circle',\n warn: 'pi pi-exclamation-triangle',\n bad: 'pi pi-times-circle',\n}\n\nfunction iconOf(nivel: CheckListLevel): string {\n return ICONS[nivel] ?? ICONS.warn\n}\n</script>\n\n<template>\n <div class=\"w-check-list\">\n <div v-if=\"title || showCount\" class=\"w-check-list__header\">\n <span class=\"w-check-list__title\">{{ title }}</span>\n <span v-if=\"showCount\" class=\"w-check-list__count\">{{ countLabel }}</span>\n </div>\n\n <ul v-if=\"items.length\" class=\"w-check-list__items\">\n <li v-for=\"(item, index) in items\" :key=\"`${item.label}-${index}`\" class=\"w-check-list__item\">\n <i :class=\"[iconOf(item.nivel), `w-check-list__icon--${item.nivel}`]\" />\n <span class=\"w-check-list__label\">{{ item.label }}</span>\n </li>\n </ul>\n\n <div v-else class=\"w-check-list__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n// `readonly` do Vue entra com apelido: existe uma PROP chamada `readonly`, e no\n// template o binding do import ganharia dela — a função é sempre truthy, então\n// `v-if=\"!readonly\"` nunca renderizaria painel nem rodapé.\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n readonly as somenteLeitura,\n ref,\n watch,\n} from 'vue'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport InputText from 'primevue/inputtext'\nimport type { Geometry } from 'geojson'\nimport type { GeoJSON as LeafletGeoJSON, LatLng, Map as LeafletMap } from 'leaflet'\nimport 'leaflet/dist/leaflet.css'\nimport { useFormatters } from '@/composables/useFormatters'\nimport type {\n MapSelectDetailPlacement,\n MapSelectFeature,\n MapSelectFeatureDetail,\n MapSelectFeatureLabel,\n MapSelectFeatureStyle,\n MapSelectGeometry,\n MapSelectId,\n MapSelectPolygonStyle,\n MapSelectPosition,\n MapSelectSelectionMode,\n MapSelectTooltips,\n} from '@/types/mapSelect'\n\n/** Aviso de Leaflet fora do ar. Extraído porque o `readonly` precisa reconhecê-lo:\n * ali não existe lista ao lado para onde mandar o usuário. */\nconst ERRO_MAPA_PADRAO = 'O mapa não pôde ser carregado. Selecione os talhões pela lista ao lado.'\nconst ERRO_MAPA_READONLY = 'O mapa não pôde ser carregado.'\n\n/**\n * Seleção múltipla de polígonos num mapa de satélite, com painel lateral de busca.\n *\n * O componente NÃO é dono da seleção: `modelValue` é a fonte da verdade e todo\n * clique (no polígono ou na lista) sai como `update:modelValue`. Isso mantém o\n * mapa e a lista sempre coerentes — os dois leem o mesmo array.\n *\n * O Leaflet entra por `import()` dinâmico: o mapa costuma viver dentro de um\n * Dialog, e carregar ~150 kB de biblioteca de mapa no bundle de quem só abre um\n * CRUD é desperdício. Se o `import()` falhar (offline, CSP), o mapa é substituído\n * por um aviso e o painel lateral continua funcionando — selecionar talhão pela\n * lista nunca depende do mapa ter subido.\n *\n * `layout` só troca POSICIONAMENTO (CSS): a marcação é a mesma nos dois modos.\n * No `'sobreposto'` o mapa ocupa tudo e o painel/rodapé flutuam por cima em\n * vidro. Eles continuam FORA do container do Leaflet (irmãos, não filhos), então\n * o mapa nunca vê os cliques do painel e o painel nunca precisa de\n * `L.DomEvent.disableClickPropagation` — arrastar/zoom por baixo segue intacto.\n *\n * `readonly` é a via de EXIBIÇÃO: sem painel e sem rodapé, sobra o mapa. Diferente\n * de `disabled`, que mantém os controles à vista, só travados.\n */\nconst props = withDefaults(\n defineProps<{\n /** Ids selecionados (`v-model`). */\n modelValue?: MapSelectId[]\n /** Itens selecionáveis. Sem `geometria` o item some do mapa, não da lista. */\n features?: MapSelectFeature[]\n /** Geometria do escopo (ex.: o setor) usada no `fitBounds` inicial.\n * Sem ela, o enquadramento é a união das geometrias das `features`. */\n scopeGeometry?: MapSelectGeometry | null\n /** Tiles. Default: satélite World_Imagery (ArcGIS/Esri). */\n tileUrl?: string\n tileAttribution?: string\n /** Zoom máximo do tile layer (o World_Imagery vai até 19). */\n maxZoom?: number\n searchPlaceholder?: string\n /** Rótulo do rodapé, à esquerda da área somada. */\n areaLabel?: string\n /** Unidade da área, no rodapé e no tooltip do polígono. */\n areaSuffix?: string\n /** Casas decimais da área. */\n areaDecimals?: number\n /** Altura do mapa (qualquer unidade CSS). */\n height?: string\n /** Arranjo do componente.\n * - `'lado-a-lado'`: mapa à esquerda, painel de 300 px à direita, rodapé embaixo.\n * - `'sobreposto'`: o mapa ocupa tudo; painel e rodapé flutuam sobre ele em vidro. */\n layout?: 'lado-a-lado' | 'sobreposto'\n /** Texto do contador. Default em pt-BR: \"N talhões selecionados\". */\n selectionLabel?: (count: number) => string\n clearLabel?: string\n /** `layout=\"sobreposto\"`: rótulo acessível do botão que recolhe o painel. */\n collapseLabel?: string\n /** `layout=\"sobreposto\"`: rótulo acessível do botão que reabre o painel. */\n expandLabel?: string\n /** Selo sobre o mapa (canto superior direito). `''` esconde. */\n badgeLabel?: string\n emptyMessage?: string\n /** Mensagem quando o Leaflet não carregou. */\n mapErrorMessage?: string\n disabled?: boolean\n /** Só o desenho: esconde o painel de busca e a barra de área, e nenhum clique\n * seleciona. Para **exibir** geometrias (uma listagem, a conferência de um\n * cadastro) — quando o mapa não é o controle de escolha, o painel de busca\n * duplica a busca da própria tela e a área somada não tem o que somar.\n * `modelValue` continua valendo: dá para destacar um polígono de fora. */\n readonly?: boolean\n /** Tooltip dos polígonos. `'permanent'` (default) mantém o rótulo sempre\n * visível; `'hover'` só o mostra sob o cursor — com muitos polígonos\n * (100+), o permanente vira um nó DOM por talhão reposicionado a cada\n * pan/zoom, e o `'hover'` devolve a fluidez. */\n tooltips?: MapSelectTooltips\n /** Modo de seleção por clique. Sem valor, o default preserva o comportamento\n * de sempre: interativo → `'multiple'`, `readonly` → `'none'`. Definido\n * explicitamente, vale MESMO com `readonly` (que segue escondendo painel e\n * rodapé): o clique no polígono emite `update:modelValue`. Em `'single'` o\n * clique substitui a seleção (`[id]`) e clicar no selecionado desmarca. */\n selectionMode?: MapSelectSelectionMode\n /** Feature destacada de fora, por cima de tudo (recebe o estilo de\n * selecionado mesclado sobre o estado atual). É o feedback do arrasto:\n * enquanto o dedo passeia com um item \"na mão\", quem chama liga o\n * `hitTest` no `pointermove` e publica o id aqui — o polígono sob o dedo\n * acende sem mexer na seleção. `null` desliga. */\n highlightFeature?: MapSelectId | null\n /** Estilo do polígono não selecionado. */\n polygonStyle?: MapSelectPolygonStyle\n /** Estilo do polígono selecionado. */\n polygonSelectedStyle?: MapSelectPolygonStyle\n /** Destaque por feature: recebe a feature (e se está selecionada) e devolve\n * só o que muda, mesclado sobre o estilo base. Para o mapa mostrar um dado\n * do domínio — ex.: contorno verde no talhão que a recomendação prevê —\n * sem o consumidor ter que reescrever os dois estilos inteiros. */\n featureStyle?: MapSelectFeatureStyle\n /** Texto do rótulo do polígono. Default: `\"<nome> · <área>\"`. Devolver `''`\n * esconde o rótulo daquele polígono — útil quando o mapa é temático e o\n * número do talhão já basta. */\n featureLabel?: MapSelectFeatureLabel\n /** Abre o cartão do slot `#feature-detail` no hover ou no clique. `'none'`\n * (default) não muda nada: sem slot e sem custo. O conteúdo do cartão é\n * inteiro de quem chama — o componente só cuida de QUANDO e ONDE mostrar. */\n featureDetail?: MapSelectFeatureDetail\n /** Onde o cartão de detalhe aparece: junto do cursor ou fixo no canto. */\n detailPlacement?: MapSelectDetailPlacement\n }>(),\n {\n modelValue: () => [],\n features: () => [],\n scopeGeometry: null,\n tileUrl:\n 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n tileAttribution: 'Imagery © Esri, Maxar, Earthstar Geographics',\n maxZoom: 19,\n searchPlaceholder: 'Buscar talhão ou setor',\n areaLabel: 'Área selecionada',\n areaSuffix: 'ha',\n areaDecimals: 0,\n height: '460px',\n layout: 'lado-a-lado',\n selectionLabel: (count: number) =>\n count === 1 ? '1 talhão selecionado' : `${count} talhões selecionados`,\n clearLabel: 'Limpar seleção',\n collapseLabel: 'Recolher painel de seleção',\n expandLabel: 'Mostrar painel de seleção',\n badgeLabel: 'Satélite',\n emptyMessage: 'Nenhum talhão encontrado',\n mapErrorMessage: ERRO_MAPA_PADRAO,\n disabled: false,\n readonly: false,\n tooltips: 'permanent',\n selectionMode: undefined,\n highlightFeature: null,\n // Tinta de mapa, não cor de UI: estes valores são lidos sobre a imagem de\n // satélite, onde os tokens do tema (claro/escuro) não valem. São props para\n // quem tiver outro fundo poder trocar.\n polygonStyle: () => ({\n color: '#ffffff',\n weight: 2,\n opacity: 0.85,\n fillColor: '#ffffff',\n fillOpacity: 0.06,\n }),\n polygonSelectedStyle: () => ({\n color: '#8fb6e4',\n weight: 3,\n opacity: 1,\n fillColor: '#1f5092',\n fillOpacity: 0.48,\n }),\n featureStyle: undefined,\n featureLabel: undefined,\n featureDetail: 'none',\n detailPlacement: 'cursor',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [ids: MapSelectId[]]\n /** Clique num polígono — SEMPRE, inclusive com `selectionMode=\"none\"`. É o\n * gancho para o mapa de leitura abrir um dialog, navegar para o documento ou\n * o que a tela precisar, sem que isso vire regra dentro do componente. */\n 'feature-click': [feature: MapSelectFeature]\n /** O cursor entrou num polígono (só com `featureDetail=\"hover\"`). */\n 'feature-enter': [feature: MapSelectFeature]\n /** O cursor saiu do polígono / o cartão fechou. */\n 'feature-leave': []\n}>()\n\nconst { formatNumber } = useFormatters()\n\n// --- Arranjo e painel flutuante --------------------------------------------\n\nconst isOverlay = computed(() => props.layout === 'sobreposto')\n\n/** Texto customizado sempre vence; só o default é trocado no `readonly`. */\nconst mapErrorText = computed(() =>\n props.readonly && props.mapErrorMessage === ERRO_MAPA_PADRAO\n ? ERRO_MAPA_READONLY\n : props.mapErrorMessage,\n)\n\n/** Estado do painel flutuante — interno de propósito: recolher é gesto de\n * visualização (\"quero ver o mapa\"), não dado do formulário. */\nconst collapsed = ref(false)\n\nfunction setCollapsed(value: boolean) {\n if (collapsed.value === value) return\n collapsed.value = value\n}\n\n/** Recolher devolve o mapa inteiro: o próprio painel vira a pílula (ícone +\n * contador) e cabeçalho/lista saem por `display:none` — somem junto da ordem\n * de tabulação e da árvore de acessibilidade, sem precisar de `inert`. */\nfunction toggleCollapsed() {\n setCollapsed(!collapsed.value)\n}\n\nconst collapseTitle = computed(() => (collapsed.value ? props.expandLabel : props.collapseLabel))\n\n// --- Seleção ---------------------------------------------------------------\n\nconst selected = computed(() => new Set(props.modelValue))\n\n/** `selectionMode` explícito manda — inclusive com `readonly` (que só esconde\n * painel/rodapé). Sem ele, o default de sempre: interativo → múltipla,\n * `readonly` → nada seleciona. */\nconst effectiveSelectionMode = computed<MapSelectSelectionMode>(\n () => props.selectionMode ?? (props.readonly ? 'none' : 'multiple'),\n)\n\nfunction isSelected(id: MapSelectId): boolean {\n return selected.value.has(id)\n}\n\nfunction toggle(id: MapSelectId) {\n if (props.disabled) return\n const mode = effectiveSelectionMode.value\n if (mode === 'none') return\n const next =\n mode === 'single'\n ? isSelected(id)\n ? []\n : [id]\n : isSelected(id)\n ? props.modelValue.filter((x) => x !== id)\n : [...props.modelValue, id]\n emit('update:modelValue', next)\n}\n\nfunction clear() {\n if (props.disabled || effectiveSelectionMode.value === 'none' || !props.modelValue.length) return\n emit('update:modelValue', [])\n}\n\nconst selectedFeatures = computed(() => props.features.filter((f) => isSelected(f.id)))\n\nconst selectedArea = computed(() =>\n selectedFeatures.value.reduce((total, f) => total + (f.area ?? 0), 0),\n)\n\nconst countText = computed(() => props.selectionLabel(selectedFeatures.value.length))\n\n// --- Busca -----------------------------------------------------------------\n\nconst term = ref('')\n\nfunction normalize(value: string): string {\n return value\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n}\n\nconst visibleFeatures = computed(() => {\n const needle = normalize(term.value.trim())\n if (!needle) return props.features\n return props.features.filter((f) => normalize(`${f.nome} ${f.subtitulo ?? ''}`).includes(needle))\n})\n\nfunction areaText(feature: MapSelectFeature): string {\n if (feature.area == null) return ''\n return `${formatNumber(feature.area, props.areaDecimals)} ${props.areaSuffix}`.trim()\n}\n\nconst areaTotalText = computed(() => formatNumber(selectedArea.value, props.areaDecimals))\n\n// --- Mapa ------------------------------------------------------------------\n\ntype Leaflet = typeof import('leaflet')\n\nconst container = ref<HTMLDivElement | null>(null)\nconst root = ref<HTMLDivElement | null>(null)\nconst footer = ref<HTMLDivElement | null>(null)\nconst mapError = ref(false)\n\nlet leaflet: Leaflet | null = null\nlet map: LeafletMap | null = null\nlet observer: ResizeObserver | null = null\nlet footerObserver: ResizeObserver | null = null\nconst layers = new Map<MapSelectId, LeafletGeoJSON>()\n/** Geometria com que cada layer foi desenhada — a comparação é por REFERÊNCIA:\n * consumidor que trocar o contorno manda um objeto novo. */\nconst layerGeoms = new Map<MapSelectId, MapSelectGeometry>()\n\n/** O enquadramento acontece UMA vez — na primeira leva de features (ou quando a\n * `scopeGeometry` chegar). `features` alimentado por páginas não pode refazer o\n * `fitBounds` a cada leva: o mapa pularia na frente do usuário. Reenquadrar de\n * novo é gesto explícito, pelo `fitToScope()` exposto. */\nlet fitted = false\n/** O primeiro fit pode ter sido só pela união das features; quando a\n * `scopeGeometry` chegar depois, ela ainda vale UM reenquadramento. */\nlet fittedToScope = false\n\n/** Índice `id → feature` para o `featureStyle`: o restyle percorre as LAYERS\n * (que só conhecem o id), então sem este mapa o destaque por dado não teria\n * como olhar a feature. */\nconst featuresById = computed(() => {\n const indice = new Map<MapSelectId, MapSelectFeature>()\n for (const feature of props.features) indice.set(feature.id, feature)\n return indice\n})\n\nfunction styleFor(id: MapSelectId): MapSelectPolygonStyle {\n const on = isSelected(id)\n const base = on ? props.polygonSelectedStyle : props.polygonStyle\n let estilo = base\n if (props.featureStyle) {\n const feature = featuresById.value.get(id)\n const ajuste = feature ? props.featureStyle(feature, on) : null\n if (ajuste) estilo = { ...estilo, ...ajuste }\n }\n // O destaque de arrasto vence tudo: é feedback transitório do gesto, não dado.\n if (props.highlightFeature != null && props.highlightFeature === id) {\n estilo = { ...estilo, ...props.polygonSelectedStyle }\n }\n return estilo\n}\n\n// --- Cartão de detalhe (slot `#feature-detail`) -----------------------------\n//\n// O componente decide QUANDO abrir e ONDE posicionar; o CONTEÚDO é do slot. É a\n// divisão que faz o mapa servir a qualquer domínio: a tela evolui o cartão (mais\n// um dado, um botão, outro arranjo) sem que a suite precise de um prop novo.\n\nconst detalhe = ref<MapSelectFeature | null>(null)\nconst detalhePos = ref<{ x: number; y: number }>({ x: 0, y: 0 })\n\n/** O pedaço do MouseEvent do Leaflet que interessa aqui: só o ponto em pixels\n * dentro do container. Evento de layer nem sempre é de mouse (o tipo do\n * Leaflet é o genérico), daí o estreitamento em `comoPonto`. */\ntype PontoDoMapa = { containerPoint?: { x: number; y: number } }\n\nfunction comoPonto(evento: unknown): PontoDoMapa | undefined {\n return (evento ?? undefined) as PontoDoMapa | undefined\n}\n\n/** Guarda o ponto já preso à caixa do mapa: perto da borda, o cartão encosta em\n * vez de vazar para fora do componente. */\nfunction posicionarDetalhe(evento?: PontoDoMapa) {\n const ponto = evento?.containerPoint\n if (!ponto) return\n const largura = container.value?.clientWidth ?? 0\n const altura = container.value?.clientHeight ?? 0\n detalhePos.value = {\n x: largura ? Math.min(Math.max(ponto.x, 12), largura - 12) : ponto.x,\n y: altura ? Math.min(Math.max(ponto.y, 12), altura - 12) : ponto.y,\n }\n}\n\nfunction abrirDetalhe(feature: MapSelectFeature, evento?: PontoDoMapa) {\n posicionarDetalhe(evento)\n // `mouseover` repete enquanto o cursor passeia pelo mesmo polígono: reemitir\n // `feature-enter` faria a tela recarregar dado a cada tremida do mouse.\n if (detalhe.value?.id === feature.id) return\n detalhe.value = feature\n emit('feature-enter', feature)\n}\n\nfunction moverDetalhe(evento?: PontoDoMapa) {\n if (detalhe.value) posicionarDetalhe(evento)\n}\n\nfunction fecharDetalhe() {\n if (!detalhe.value) return\n detalhe.value = null\n emit('feature-leave')\n}\n\n/** Clique no mesmo polígono fecha — é como o usuário desfaz o que abriu. */\nfunction alternarDetalhe(feature: MapSelectFeature, evento?: PontoDoMapa) {\n if (detalhe.value?.id === feature.id) {\n fecharDetalhe()\n return\n }\n detalhe.value = null\n abrirDetalhe(feature, evento)\n}\n\n/** Perto do topo o cartão vira para baixo: acima não haveria espaço. */\nconst detalheAbaixo = computed(() => detalhePos.value.y < 150)\n\nconst detalheStyle = computed(() =>\n props.detailPlacement === 'canto'\n ? undefined\n : { left: `${detalhePos.value.x}px`, top: `${detalhePos.value.y}px` },\n)\n\nfunction onEsc(evento: KeyboardEvent) {\n if (evento.key === 'Escape') fecharDetalhe()\n}\n\nfunction tooltipText(feature: MapSelectFeature): string {\n if (props.featureLabel) return props.featureLabel(feature)\n const area = areaText(feature)\n return area ? `${feature.nome} · ${area}` : feature.nome\n}\n\nfunction bindTip(layer: LeafletGeoJSON, feature: MapSelectFeature) {\n const texto = tooltipText(feature)\n // Rótulo vazio é escolha de quem chama (\"este polígono não tem legenda\"), e\n // um tooltip vazio ainda pintaria uma sombra de texto sobre o satélite.\n if (!texto) return\n layer.bindTooltip(\n texto,\n props.tooltips === 'hover'\n ? {\n permanent: false,\n sticky: true,\n direction: 'center',\n className: 'w-map-select__tip',\n opacity: 1,\n }\n : { permanent: true, direction: 'center', className: 'w-map-select__tip', opacity: 1 },\n )\n}\n\n/** `interactive: false` tira o polígono do hit-test do Leaflet (e o cursor de\n * mão junto). Com tooltip `'hover'`, com cartão de detalhe ou com alguém\n * ouvindo `feature-click`, a layer precisa continuar interativa mesmo sem\n * seleção — o cursor é suprimido via CSS (`--unselectable`). */\nconst layersInteractive = computed(\n () =>\n effectiveSelectionMode.value !== 'none' ||\n props.tooltips === 'hover' ||\n props.featureDetail !== 'none',\n)\n\nfunction createLayer(feature: MapSelectFeature) {\n if (!leaflet || !map || !feature.geometria) return\n const layer = leaflet.geoJSON(feature.geometria as unknown as Geometry, {\n style: () => styleFor(feature.id),\n interactive: layersInteractive.value,\n })\n layer.on('click', (evento) => {\n toggle(feature.id)\n // Clique SEMPRE avisa quem chama — inclusive no mapa de leitura, onde\n // `toggle` não faz nada. Abrir dialog é decisão da tela, não do componente.\n emit('feature-click', feature)\n if (props.featureDetail === 'click') alternarDetalhe(feature, comoPonto(evento))\n })\n if (props.featureDetail === 'hover') {\n layer.on('mouseover', (evento) => abrirDetalhe(feature, comoPonto(evento)))\n layer.on('mousemove', (evento) => moverDetalhe(comoPonto(evento)))\n layer.on('mouseout', () => fecharDetalhe())\n }\n bindTip(layer, feature)\n layer.addTo(map)\n layers.set(feature.id, layer)\n layerGeoms.set(feature.id, feature.geometria)\n}\n\n/** Sincroniza as layers com `features` por DIFF de `id`: remove as que saíram,\n * cria só as novas e recria a que trocou de `geometria` (referência). Páginas\n * chegando incrementalmente não destroem o que já está desenhado. */\nfunction drawFeatures() {\n if (!leaflet || !map) return\n\n const current = new Map<MapSelectId, MapSelectFeature>()\n for (const feature of props.features) {\n if (feature.geometria) current.set(feature.id, feature)\n }\n\n layers.forEach((layer, id) => {\n if (current.has(id)) return\n layer.remove()\n layers.delete(id)\n layerGeoms.delete(id)\n })\n\n current.forEach((feature, id) => {\n const existing = layers.get(id)\n if (existing && layerGeoms.get(id) === feature.geometria) {\n // Mesma geometria: só o texto do rótulo pode ter mudado (nome/área/label).\n const texto = tooltipText(feature)\n const tooltip = existing.getTooltip()\n if (!texto) existing.unbindTooltip()\n else if (tooltip) tooltip.setContent(texto)\n else bindTip(existing, feature)\n return\n }\n if (existing) {\n existing.remove()\n layers.delete(id)\n layerGeoms.delete(id)\n }\n createLayer(feature)\n })\n\n syncStyles()\n\n if (!fitted && (layers.size || props.scopeGeometry)) {\n fitToScope()\n fitted = true\n fittedToScope = Boolean(props.scopeGeometry)\n }\n}\n\n/** Reaplica o estilo (e a classe do tooltip) conforme a seleção atual. */\nfunction syncStyles() {\n layers.forEach((layer, id) => {\n const on = isSelected(id)\n layer.setStyle(styleFor(id))\n const element = layer.getTooltip()?.getElement()\n element?.classList.toggle('w-map-select__tip--on', on)\n })\n}\n\nfunction fitToScope() {\n if (!leaflet || !map) return\n const corners: LatLng[] = []\n\n if (props.scopeGeometry) {\n const scope = leaflet.geoJSON(props.scopeGeometry as unknown as Geometry).getBounds()\n if (scope.isValid()) corners.push(scope.getSouthWest(), scope.getNorthEast())\n }\n\n if (!corners.length) {\n layers.forEach((layer) => {\n const bounds = layer.getBounds()\n if (bounds.isValid()) corners.push(bounds.getSouthWest(), bounds.getNorthEast())\n })\n }\n\n if (corners.length) map.fitBounds(leaflet.latLngBounds(corners), { padding: [20, 20] })\n else map.setView([0, 0], 2)\n}\n\n/** Enquadra a layer de um id, com o mesmo padding do escopo. No-op se o id não\n * tem layer (sem geometria, ainda não chegou, mapa fora do ar). */\nfunction fitToFeature(id: MapSelectId, options?: { padding?: [number, number]; maxZoom?: number }) {\n if (!map) return\n const layer = layers.get(id)\n if (!layer) return\n const bounds = layer.getBounds()\n // `maxZoom` limita a aproximação: enquadrar UM talhão sem teto cola a câmera\n // nele e o usuário perde a vizinhança — quem chama escolhe o quão perto.\n if (bounds.isValid()) map.fitBounds(bounds, { padding: [20, 20], ...options })\n}\n\n// --- Hit-test por coordenada ------------------------------------------------\n//\n// O renderer canvas pinta todos os polígonos num único elemento — não existe\n// \"elemento sob o dedo\" para um arrasto consultar. O teste é geométrico:\n// ray casting sobre as coordenadas GeoJSON das próprias features, independente\n// de renderer e de layer.\n\nfunction pontoNoAnel(lng: number, lat: number, anel: MapSelectPosition[]): boolean {\n let dentro = false\n for (let i = 0, j = anel.length - 1; i < anel.length; j = i++) {\n const [xi, yi] = anel[i]\n const [xj, yj] = anel[j]\n const cruza = yi > lat !== yj > lat && lng < ((xj - xi) * (lat - yi)) / (yj - yi) + xi\n if (cruza) dentro = !dentro\n }\n return dentro\n}\n\nfunction pontoNaGeometria(lng: number, lat: number, geometria: MapSelectGeometry): boolean {\n const poligonos = (\n geometria.type === 'Polygon' ? [geometria.coordinates] : geometria.coordinates\n ) as MapSelectPosition[][][]\n for (const aneis of poligonos) {\n if (!aneis.length || !pontoNoAnel(lng, lat, aneis[0])) continue\n // Dentro do contorno externo: só não conta se caiu num buraco.\n const noBuraco = aneis.slice(1).some((anel) => pontoNoAnel(lng, lat, anel))\n if (!noBuraco) return true\n }\n return false\n}\n\n/** Feature sob um ponto da TELA (`clientX/clientY` de um PointerEvent) — o\n * gancho do arrasto: soltar/passar um item por cima do mapa e descobrir qual\n * polígono está sob o dedo. `null` fora do mapa, fora de qualquer polígono ou\n * com o Leaflet fora do ar. */\nfunction hitTest(clientX: number, clientY: number): MapSelectId | null {\n if (!map || !container.value) return null\n const rect = container.value.getBoundingClientRect()\n const x = clientX - rect.left\n const y = clientY - rect.top\n // Com o container medido, ponto fora dele não é candidato. (Em ambiente sem\n // layout — testes — o rect é 0×0 e a checagem não se aplica.)\n if (rect.width > 0 && rect.height > 0 && (x < 0 || y < 0 || x > rect.width || y > rect.height))\n return null\n const latlng = map.containerPointToLatLng([x, y])\n for (const feature of props.features) {\n if (feature.geometria && pontoNaGeometria(latlng.lng, latlng.lat, feature.geometria))\n return feature.id\n }\n return null\n}\n\n/** Recria todas as layers (opções de criação — tooltip/interatividade —\n * mudaram). Não mexe no enquadramento. */\nfunction rebuildLayers() {\n if (!leaflet || !map) return\n layers.forEach((layer) => layer.remove())\n layers.clear()\n layerGeoms.clear()\n drawFeatures()\n}\n\n/** O mapa costuma nascer escondido (dentro de um Dialog): sem isto o Leaflet\n * calcula 0×0 e desenha só um pedaço quando o container aparece. */\nfunction refreshSize() {\n if (!map || !container.value) return\n if (container.value.clientWidth > 0 && container.value.clientHeight > 0) map.invalidateSize()\n}\n\n/** No `sobreposto` o rodapé é uma barra flutuante: o painel precisa parar acima\n * dela e a atribuição do Leaflet precisa subir. Como a altura depende do que o\n * consumidor põe no `#footer`, ela é MEDIDA e publicada em `--w-map-foot-h` —\n * chutar um valor fixo quebraria com dois botões empilhados. */\nfunction measureFooter() {\n if (!root.value || !footer.value) return\n if (!isOverlay.value) {\n root.value.style.removeProperty('--w-map-foot-h')\n return\n }\n const h = footer.value.offsetHeight\n if (h > 0) root.value.style.setProperty('--w-map-foot-h', `${h}px`)\n}\n\nasync function setupMap() {\n if (!container.value) return\n try {\n const mod = (await import('leaflet')) as unknown as Leaflet & { default?: Leaflet }\n leaflet = mod.default ?? mod\n } catch {\n mapError.value = true\n return\n }\n\n // O componente pode ter sido desmontado durante o import().\n if (!container.value) return\n\n try {\n // `preferCanvas`: com 100+ polígonos o renderer SVG vira uma árvore DOM\n // pesada; no canvas os paths são pintados num único elemento.\n map = leaflet.map(container.value, {\n zoomControl: true,\n attributionControl: true,\n preferCanvas: true,\n })\n leaflet\n .tileLayer(props.tileUrl, {\n maxZoom: props.maxZoom,\n attribution: props.tileAttribution,\n })\n .addTo(map)\n drawFeatures()\n // Sem feature e sem escopo ainda não houve fit: o mapa precisa de UMA view\n // inicial (o Leaflet não opera sem centro/zoom definidos).\n if (!fitted) map.setView([0, 0], 2)\n refreshSize()\n\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(() => refreshSize())\n observer.observe(container.value)\n }\n } catch {\n mapError.value = true\n }\n}\n\nonMounted(() => {\n // `Esc` fecha o cartão — o mesmo gesto de qualquer overlay do sistema.\n if (typeof window !== 'undefined') window.addEventListener('keydown', onEsc)\n void setupMap()\n measureFooter()\n if (typeof ResizeObserver !== 'undefined' && footer.value) {\n footerObserver = new ResizeObserver(() => measureFooter())\n footerObserver.observe(footer.value)\n }\n})\n\nonBeforeUnmount(() => {\n if (typeof window !== 'undefined') window.removeEventListener('keydown', onEsc)\n observer?.disconnect()\n observer = null\n footerObserver?.disconnect()\n footerObserver = null\n layers.clear()\n layerGeoms.clear()\n map?.remove()\n map = null\n})\n\nwatch(() => props.features, drawFeatures)\n// Trocar quando/como o cartão abre recria as layers (os handlers são de\n// criação) e fecha o que estiver aberto — o modo antigo não vale mais.\nwatch([() => props.featureDetail, () => props.detailPlacement], () => {\n fecharDetalhe()\n rebuildLayers()\n})\nwatch(() => props.modelValue, syncStyles, { deep: true })\n// Trocar a regra de destaque repinta o que já está desenhado — sem isto o mapa\n// só mostraria a regra nova nas features que chegassem depois.\nwatch(() => props.featureStyle, syncStyles)\n// O destaque de arrasto muda a cada pointermove — repinta só estilos.\nwatch(() => props.highlightFeature, syncStyles)\n\n// A `scopeGeometry` pode chegar DEPOIS da primeira página de features (duas\n// requests em paralelo): ela ainda vale um único reenquadramento.\nwatch(\n () => props.scopeGeometry,\n (geo) => {\n if (!map || !geo || fittedToScope) return\n fitToScope()\n fitted = true\n fittedToScope = true\n },\n)\n\n// Tooltip e interatividade são opções de CRIAÇÃO da layer: mudou, recria tudo.\nwatch([() => props.tooltips, layersInteractive], () => rebuildLayers())\n\n// Recolher/expandir e trocar de arranjo mexem na caixa do mapa. O\n// ResizeObserver do canvas já pegaria, mas ele dispara no frame seguinte: sem o\n// invalidateSize explícito o Leaflet chega a pintar um quadro com tile cortado.\nwatch([collapsed, isOverlay], () => {\n void nextTick(() => {\n measureFooter()\n refreshSize()\n })\n})\n\ndefineExpose({\n refreshSize,\n /** Painel flutuante recolhido? (só faz sentido no `layout=\"sobreposto\"`.) */\n collapsed: somenteLeitura(collapsed),\n setCollapsed,\n /** Reenquadra no escopo (ou na união das features) — o fit automático só\n * acontece uma vez; páginas novas de `features` não movem o mapa. */\n fitToScope,\n /** Enquadra o polígono de um id (padding igual ao do escopo). No-op sem layer. */\n fitToFeature,\n /** Feature sob um ponto da tela (arrasto por cima do mapa). `null` fora. */\n hitTest,\n /** Feature do cartão de detalhe aberto (`null` fechado). */\n detalhe: somenteLeitura(detalhe),\n /** Fecha o cartão de detalhe — para a tela fechar de fora (navegou, salvou). */\n fecharDetalhe,\n})\n</script>\n\n<template>\n <div\n ref=\"root\"\n class=\"w-map-select\"\n :class=\"{\n 'w-map-select--disabled': disabled,\n 'w-map-select--readonly': readonly,\n 'w-map-select--unselectable': effectiveSelectionMode === 'none',\n 'w-map-select--overlay': isOverlay,\n 'w-map-select--collapsed': isOverlay && collapsed && !readonly,\n }\"\n >\n <div class=\"w-map-select__body\">\n <div class=\"w-map-select__map\" :style=\"{ '--w-map-h': height }\">\n <div ref=\"container\" class=\"w-map-select__canvas\" />\n <div v-if=\"mapError\" class=\"w-map-select__fallback\">\n <i class=\"pi pi-map\" />\n <span>{{ mapErrorText }}</span>\n </div>\n <span v-else-if=\"badgeLabel\" class=\"w-map-select__badge\">\n <i class=\"pi pi-globe\" />{{ badgeLabel }}\n </span>\n\n <!-- Cartão de detalhe: o componente posiciona, o slot preenche. -->\n <div\n v-if=\"detalhe && featureDetail !== 'none'\"\n class=\"w-map-select__detail\"\n :class=\"{\n 'w-map-select__detail--canto': detailPlacement === 'canto',\n 'w-map-select__detail--abaixo': detailPlacement !== 'canto' && detalheAbaixo,\n 'w-map-select__detail--hover': featureDetail === 'hover',\n }\"\n :style=\"detalheStyle\"\n role=\"tooltip\"\n >\n <slot\n name=\"feature-detail\"\n :feature=\"detalhe\"\n :selected=\"isSelected(detalhe.id)\"\n :close=\"fecharDetalhe\"\n >\n <span class=\"w-map-select__detail-nome\">{{ detalhe.nome }}</span>\n <span v-if=\"detalhe.subtitulo\" class=\"w-map-select__detail-sub\">\n {{ detalhe.subtitulo }}\n </span>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!readonly\" class=\"w-map-select__panel\">\n <button\n v-if=\"isOverlay\"\n type=\"button\"\n class=\"w-map-select__collapse\"\n :aria-expanded=\"!collapsed\"\n :aria-label=\"collapseTitle\"\n :title=\"collapseTitle\"\n @click=\"toggleCollapsed\"\n >\n <i class=\"pi\" :class=\"collapsed ? 'pi-list' : 'pi-chevron-right'\" />\n <span v-if=\"modelValue.length\" class=\"w-map-select__collapse-count\">\n {{ modelValue.length }}\n </span>\n </button>\n\n <div class=\"w-map-select__panel-head\">\n <IconField>\n <InputIcon class=\"pi pi-search\" />\n <InputText\n v-model=\"term\"\n fluid\n data-kbd-skip\n :placeholder=\"searchPlaceholder\"\n :aria-label=\"searchPlaceholder\"\n />\n </IconField>\n <div class=\"w-map-select__meta\">\n <span class=\"w-map-select__count\">{{ countText }}</span>\n <button\n type=\"button\"\n class=\"w-map-select__clear\"\n :disabled=\"disabled || !modelValue.length\"\n @click=\"clear\"\n >\n {{ clearLabel }}\n </button>\n </div>\n </div>\n\n <ul class=\"w-map-select__list\">\n <li v-for=\"feature in visibleFeatures\" :key=\"feature.id\">\n <slot name=\"item\" :feature=\"feature\" :selected=\"isSelected(feature.id)\">\n <button\n type=\"button\"\n class=\"w-map-select__item\"\n :class=\"{ 'w-map-select__item--on': isSelected(feature.id) }\"\n :aria-pressed=\"isSelected(feature.id)\"\n :disabled=\"disabled\"\n @click=\"toggle(feature.id)\"\n >\n <span class=\"w-map-select__box\">\n <i v-if=\"isSelected(feature.id)\" class=\"pi pi-check\" />\n </span>\n <span class=\"w-map-select__item-text\">\n <span class=\"w-map-select__item-name\">{{ feature.nome }}</span>\n <span v-if=\"feature.subtitulo\" class=\"w-map-select__item-sub\">\n {{ feature.subtitulo }}\n </span>\n </span>\n <i\n v-if=\"!feature.geometria\"\n class=\"pi pi-exclamation-circle w-map-select__item-nogeo\"\n title=\"Talhão sem contorno cadastrado\"\n />\n <span v-if=\"feature.area != null\" class=\"w-map-select__item-area\">\n {{ areaText(feature) }}\n </span>\n </button>\n </slot>\n </li>\n <li v-if=\"!visibleFeatures.length\" class=\"w-map-select__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </li>\n </ul>\n </div>\n </div>\n\n <div v-if=\"!readonly\" ref=\"footer\" class=\"w-map-select__footer\">\n <slot name=\"footer\" :area=\"selectedArea\" :features=\"selectedFeatures\">\n <span class=\"w-map-select__area\">\n {{ areaLabel }}\n <strong>{{ areaTotalText }}</strong>\n <span class=\"w-map-select__area-unit\">{{ areaSuffix }}</span>\n </span>\n </slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n// `readonly` do Vue entra com apelido: existe uma PROP chamada `readonly`, e no\n// template o binding do import ganharia dela — a função é sempre truthy, então\n// `v-if=\"!readonly\"` nunca renderizaria painel nem rodapé.\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n readonly as somenteLeitura,\n ref,\n watch,\n} from 'vue'\nimport IconField from 'primevue/iconfield'\nimport InputIcon from 'primevue/inputicon'\nimport InputText from 'primevue/inputtext'\nimport type { Geometry } from 'geojson'\nimport type { GeoJSON as LeafletGeoJSON, LatLng, Map as LeafletMap } from 'leaflet'\nimport 'leaflet/dist/leaflet.css'\nimport { useFormatters } from '@/composables/useFormatters'\nimport type {\n MapSelectDetailPlacement,\n MapSelectFeature,\n MapSelectFeatureDetail,\n MapSelectFeatureLabel,\n MapSelectFeatureStyle,\n MapSelectGeometry,\n MapSelectId,\n MapSelectPolygonStyle,\n MapSelectPosition,\n MapSelectSelectionMode,\n MapSelectTooltips,\n} from '@/types/mapSelect'\n\n/** Aviso de Leaflet fora do ar. Extraído porque o `readonly` precisa reconhecê-lo:\n * ali não existe lista ao lado para onde mandar o usuário. */\nconst ERRO_MAPA_PADRAO = 'O mapa não pôde ser carregado. Selecione os talhões pela lista ao lado.'\nconst ERRO_MAPA_READONLY = 'O mapa não pôde ser carregado.'\n\n/**\n * Seleção múltipla de polígonos num mapa de satélite, com painel lateral de busca.\n *\n * O componente NÃO é dono da seleção: `modelValue` é a fonte da verdade e todo\n * clique (no polígono ou na lista) sai como `update:modelValue`. Isso mantém o\n * mapa e a lista sempre coerentes — os dois leem o mesmo array.\n *\n * O Leaflet entra por `import()` dinâmico: o mapa costuma viver dentro de um\n * Dialog, e carregar ~150 kB de biblioteca de mapa no bundle de quem só abre um\n * CRUD é desperdício. Se o `import()` falhar (offline, CSP), o mapa é substituído\n * por um aviso e o painel lateral continua funcionando — selecionar talhão pela\n * lista nunca depende do mapa ter subido.\n *\n * `layout` só troca POSICIONAMENTO (CSS): a marcação é a mesma nos dois modos.\n * No `'sobreposto'` o mapa ocupa tudo e o painel/rodapé flutuam por cima em\n * vidro. Eles continuam FORA do container do Leaflet (irmãos, não filhos), então\n * o mapa nunca vê os cliques do painel e o painel nunca precisa de\n * `L.DomEvent.disableClickPropagation` — arrastar/zoom por baixo segue intacto.\n *\n * `readonly` é a via de EXIBIÇÃO: sem painel e sem rodapé, sobra o mapa. Diferente\n * de `disabled`, que mantém os controles à vista, só travados.\n */\nconst props = withDefaults(\n defineProps<{\n /** Ids selecionados (`v-model`). */\n modelValue?: MapSelectId[]\n /** Itens selecionáveis. Sem `geometria` o item some do mapa, não da lista. */\n features?: MapSelectFeature[]\n /** Geometria do escopo (ex.: o setor) usada no `fitBounds` inicial.\n * Sem ela, o enquadramento é a união das geometrias das `features`. */\n scopeGeometry?: MapSelectGeometry | null\n /** Tiles. Default: satélite World_Imagery (ArcGIS/Esri). */\n tileUrl?: string\n tileAttribution?: string\n /** Zoom máximo do tile layer (o World_Imagery vai até 19). */\n maxZoom?: number\n searchPlaceholder?: string\n /** Rótulo do rodapé, à esquerda da área somada. */\n areaLabel?: string\n /** Unidade da área, no rodapé e no tooltip do polígono. */\n areaSuffix?: string\n /** Casas decimais da área. */\n areaDecimals?: number\n /** Altura do mapa (qualquer unidade CSS). */\n height?: string\n /** Arranjo do componente.\n * - `'lado-a-lado'`: mapa à esquerda, painel de 300 px à direita, rodapé embaixo.\n * - `'sobreposto'`: o mapa ocupa tudo; painel e rodapé flutuam sobre ele em vidro. */\n layout?: 'lado-a-lado' | 'sobreposto'\n /** Texto do contador. Default em pt-BR: \"N talhões selecionados\". */\n selectionLabel?: (count: number) => string\n clearLabel?: string\n /** `layout=\"sobreposto\"`: rótulo acessível do botão que recolhe o painel. */\n collapseLabel?: string\n /** `layout=\"sobreposto\"`: rótulo acessível do botão que reabre o painel. */\n expandLabel?: string\n /** Selo sobre o mapa (canto superior direito). `''` esconde. */\n badgeLabel?: string\n emptyMessage?: string\n /** Mensagem quando o Leaflet não carregou. */\n mapErrorMessage?: string\n disabled?: boolean\n /** Só o desenho: esconde o painel de busca e a barra de área, e nenhum clique\n * seleciona. Para **exibir** geometrias (uma listagem, a conferência de um\n * cadastro) — quando o mapa não é o controle de escolha, o painel de busca\n * duplica a busca da própria tela e a área somada não tem o que somar.\n * `modelValue` continua valendo: dá para destacar um polígono de fora. */\n readonly?: boolean\n /** Tooltip dos polígonos. `'permanent'` (default) mantém o rótulo sempre\n * visível; `'hover'` só o mostra sob o cursor — com muitos polígonos\n * (100+), o permanente vira um nó DOM por talhão reposicionado a cada\n * pan/zoom, e o `'hover'` devolve a fluidez. */\n tooltips?: MapSelectTooltips\n /** Modo de seleção por clique. Sem valor, o default preserva o comportamento\n * de sempre: interativo → `'multiple'`, `readonly` → `'none'`. Definido\n * explicitamente, vale MESMO com `readonly` (que segue escondendo painel e\n * rodapé): o clique no polígono emite `update:modelValue`. Em `'single'` o\n * clique substitui a seleção (`[id]`) e clicar no selecionado desmarca. */\n selectionMode?: MapSelectSelectionMode\n /** Feature destacada de fora, por cima de tudo (recebe o estilo de\n * selecionado mesclado sobre o estado atual). É o feedback do arrasto:\n * enquanto o dedo passeia com um item \"na mão\", quem chama liga o\n * `hitTest` no `pointermove` e publica o id aqui — o polígono sob o dedo\n * acende sem mexer na seleção. `null` desliga. */\n highlightFeature?: MapSelectId | null\n /** Estilo do polígono não selecionado. */\n polygonStyle?: MapSelectPolygonStyle\n /** Estilo do polígono selecionado. */\n polygonSelectedStyle?: MapSelectPolygonStyle\n /** Destaque por feature: recebe a feature (e se está selecionada) e devolve\n * só o que muda, mesclado sobre o estilo base. Para o mapa mostrar um dado\n * do domínio — ex.: contorno verde no talhão que a recomendação prevê —\n * sem o consumidor ter que reescrever os dois estilos inteiros. */\n featureStyle?: MapSelectFeatureStyle\n /** Texto do rótulo do polígono. Default: `\"<nome> · <área>\"`. Devolver `''`\n * esconde o rótulo daquele polígono — útil quando o mapa é temático e o\n * número do talhão já basta. */\n featureLabel?: MapSelectFeatureLabel\n /** Abre o cartão do slot `#feature-detail` no hover ou no clique. `'none'`\n * (default) não muda nada: sem slot e sem custo. O conteúdo do cartão é\n * inteiro de quem chama — o componente só cuida de QUANDO e ONDE mostrar. */\n featureDetail?: MapSelectFeatureDetail\n /** Onde o cartão de detalhe aparece: junto do cursor ou fixo no canto. */\n detailPlacement?: MapSelectDetailPlacement\n }>(),\n {\n modelValue: () => [],\n features: () => [],\n scopeGeometry: null,\n tileUrl:\n 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n tileAttribution: 'Imagery © Esri, Maxar, Earthstar Geographics',\n maxZoom: 19,\n searchPlaceholder: 'Buscar talhão ou setor',\n areaLabel: 'Área selecionada',\n areaSuffix: 'ha',\n areaDecimals: 0,\n height: '460px',\n layout: 'lado-a-lado',\n selectionLabel: (count: number) =>\n count === 1 ? '1 talhão selecionado' : `${count} talhões selecionados`,\n clearLabel: 'Limpar seleção',\n collapseLabel: 'Recolher painel de seleção',\n expandLabel: 'Mostrar painel de seleção',\n badgeLabel: 'Satélite',\n emptyMessage: 'Nenhum talhão encontrado',\n mapErrorMessage: ERRO_MAPA_PADRAO,\n disabled: false,\n readonly: false,\n tooltips: 'permanent',\n selectionMode: undefined,\n highlightFeature: null,\n // Tinta de mapa, não cor de UI: estes valores são lidos sobre a imagem de\n // satélite, onde os tokens do tema (claro/escuro) não valem. São props para\n // quem tiver outro fundo poder trocar.\n polygonStyle: () => ({\n color: '#ffffff',\n weight: 2,\n opacity: 0.85,\n fillColor: '#ffffff',\n fillOpacity: 0.06,\n }),\n polygonSelectedStyle: () => ({\n color: '#8fb6e4',\n weight: 3,\n opacity: 1,\n fillColor: '#1f5092',\n fillOpacity: 0.48,\n }),\n featureStyle: undefined,\n featureLabel: undefined,\n featureDetail: 'none',\n detailPlacement: 'cursor',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [ids: MapSelectId[]]\n /** Clique num polígono — SEMPRE, inclusive com `selectionMode=\"none\"`. É o\n * gancho para o mapa de leitura abrir um dialog, navegar para o documento ou\n * o que a tela precisar, sem que isso vire regra dentro do componente. */\n 'feature-click': [feature: MapSelectFeature]\n /** O cursor entrou num polígono (só com `featureDetail=\"hover\"`). */\n 'feature-enter': [feature: MapSelectFeature]\n /** O cursor saiu do polígono / o cartão fechou. */\n 'feature-leave': []\n}>()\n\nconst { formatNumber } = useFormatters()\n\n// --- Arranjo e painel flutuante --------------------------------------------\n\nconst isOverlay = computed(() => props.layout === 'sobreposto')\n\n/** Texto customizado sempre vence; só o default é trocado no `readonly`. */\nconst mapErrorText = computed(() =>\n props.readonly && props.mapErrorMessage === ERRO_MAPA_PADRAO\n ? ERRO_MAPA_READONLY\n : props.mapErrorMessage,\n)\n\n/** Estado do painel flutuante — interno de propósito: recolher é gesto de\n * visualização (\"quero ver o mapa\"), não dado do formulário. */\nconst collapsed = ref(false)\n\nfunction setCollapsed(value: boolean) {\n if (collapsed.value === value) return\n collapsed.value = value\n}\n\n/** Recolher devolve o mapa inteiro: o próprio painel vira a pílula (ícone +\n * contador) e cabeçalho/lista saem por `display:none` — somem junto da ordem\n * de tabulação e da árvore de acessibilidade, sem precisar de `inert`. */\nfunction toggleCollapsed() {\n setCollapsed(!collapsed.value)\n}\n\nconst collapseTitle = computed(() => (collapsed.value ? props.expandLabel : props.collapseLabel))\n\n// --- Seleção ---------------------------------------------------------------\n\nconst selected = computed(() => new Set(props.modelValue))\n\n/** `selectionMode` explícito manda — inclusive com `readonly` (que só esconde\n * painel/rodapé). Sem ele, o default de sempre: interativo → múltipla,\n * `readonly` → nada seleciona. */\nconst effectiveSelectionMode = computed<MapSelectSelectionMode>(\n () => props.selectionMode ?? (props.readonly ? 'none' : 'multiple'),\n)\n\nfunction isSelected(id: MapSelectId): boolean {\n return selected.value.has(id)\n}\n\nfunction toggle(id: MapSelectId) {\n if (props.disabled) return\n const mode = effectiveSelectionMode.value\n if (mode === 'none') return\n const next =\n mode === 'single'\n ? isSelected(id)\n ? []\n : [id]\n : isSelected(id)\n ? props.modelValue.filter((x) => x !== id)\n : [...props.modelValue, id]\n emit('update:modelValue', next)\n}\n\nfunction clear() {\n if (props.disabled || effectiveSelectionMode.value === 'none' || !props.modelValue.length) return\n emit('update:modelValue', [])\n}\n\nconst selectedFeatures = computed(() => props.features.filter((f) => isSelected(f.id)))\n\nconst selectedArea = computed(() =>\n selectedFeatures.value.reduce((total, f) => total + (f.area ?? 0), 0),\n)\n\nconst countText = computed(() => props.selectionLabel(selectedFeatures.value.length))\n\n// --- Busca -----------------------------------------------------------------\n\nconst term = ref('')\n\nfunction normalize(value: string): string {\n return value\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n}\n\nconst visibleFeatures = computed(() => {\n const needle = normalize(term.value.trim())\n if (!needle) return props.features\n return props.features.filter((f) => normalize(`${f.nome} ${f.subtitulo ?? ''}`).includes(needle))\n})\n\nfunction areaText(feature: MapSelectFeature): string {\n if (feature.area == null) return ''\n return `${formatNumber(feature.area, props.areaDecimals)} ${props.areaSuffix}`.trim()\n}\n\nconst areaTotalText = computed(() => formatNumber(selectedArea.value, props.areaDecimals))\n\n// --- Mapa ------------------------------------------------------------------\n\ntype Leaflet = typeof import('leaflet')\n\nconst container = ref<HTMLDivElement | null>(null)\nconst root = ref<HTMLDivElement | null>(null)\nconst footer = ref<HTMLDivElement | null>(null)\nconst mapError = ref(false)\n\nlet leaflet: Leaflet | null = null\nlet map: LeafletMap | null = null\nlet observer: ResizeObserver | null = null\nlet footerObserver: ResizeObserver | null = null\nconst layers = new Map<MapSelectId, LeafletGeoJSON>()\n/** Geometria com que cada layer foi desenhada — a comparação é por REFERÊNCIA:\n * consumidor que trocar o contorno manda um objeto novo. */\nconst layerGeoms = new Map<MapSelectId, MapSelectGeometry>()\n\n/** O enquadramento acontece UMA vez — na primeira leva de features (ou quando a\n * `scopeGeometry` chegar). `features` alimentado por páginas não pode refazer o\n * `fitBounds` a cada leva: o mapa pularia na frente do usuário. Reenquadrar de\n * novo é gesto explícito, pelo `fitToScope()` exposto. */\nlet fitted = false\n/** O primeiro fit pode ter sido só pela união das features; quando a\n * `scopeGeometry` chegar depois, ela ainda vale UM reenquadramento. */\nlet fittedToScope = false\n\n/** Índice `id → feature` para o `featureStyle`: o restyle percorre as LAYERS\n * (que só conhecem o id), então sem este mapa o destaque por dado não teria\n * como olhar a feature. */\nconst featuresById = computed(() => {\n const indice = new Map<MapSelectId, MapSelectFeature>()\n for (const feature of props.features) indice.set(feature.id, feature)\n return indice\n})\n\nfunction styleFor(id: MapSelectId): MapSelectPolygonStyle {\n const on = isSelected(id)\n const base = on ? props.polygonSelectedStyle : props.polygonStyle\n let estilo = base\n if (props.featureStyle) {\n const feature = featuresById.value.get(id)\n const ajuste = feature ? props.featureStyle(feature, on) : null\n if (ajuste) estilo = { ...estilo, ...ajuste }\n }\n // O destaque de arrasto vence tudo: é feedback transitório do gesto, não dado.\n if (props.highlightFeature != null && props.highlightFeature === id) {\n estilo = { ...estilo, ...props.polygonSelectedStyle }\n }\n return estilo\n}\n\n// --- Cartão de detalhe (slot `#feature-detail`) -----------------------------\n//\n// O componente decide QUANDO abrir e ONDE posicionar; o CONTEÚDO é do slot. É a\n// divisão que faz o mapa servir a qualquer domínio: a tela evolui o cartão (mais\n// um dado, um botão, outro arranjo) sem que a suite precise de um prop novo.\n\nconst detalhe = ref<MapSelectFeature | null>(null)\nconst detalhePos = ref<{ x: number; y: number }>({ x: 0, y: 0 })\n\n/** O pedaço do MouseEvent do Leaflet que interessa aqui: só o ponto em pixels\n * dentro do container. Evento de layer nem sempre é de mouse (o tipo do\n * Leaflet é o genérico), daí o estreitamento em `comoPonto`. */\ntype PontoDoMapa = { containerPoint?: { x: number; y: number } }\n\nfunction comoPonto(evento: unknown): PontoDoMapa | undefined {\n return (evento ?? undefined) as PontoDoMapa | undefined\n}\n\n/** Guarda o ponto já preso à caixa do mapa: perto da borda, o cartão encosta em\n * vez de vazar para fora do componente. */\nfunction posicionarDetalhe(evento?: PontoDoMapa) {\n const ponto = evento?.containerPoint\n if (!ponto) return\n const largura = container.value?.clientWidth ?? 0\n const altura = container.value?.clientHeight ?? 0\n detalhePos.value = {\n x: largura ? Math.min(Math.max(ponto.x, 12), largura - 12) : ponto.x,\n y: altura ? Math.min(Math.max(ponto.y, 12), altura - 12) : ponto.y,\n }\n}\n\nfunction abrirDetalhe(feature: MapSelectFeature, evento?: PontoDoMapa) {\n posicionarDetalhe(evento)\n // `mouseover` repete enquanto o cursor passeia pelo mesmo polígono: reemitir\n // `feature-enter` faria a tela recarregar dado a cada tremida do mouse.\n if (detalhe.value?.id === feature.id) return\n detalhe.value = feature\n emit('feature-enter', feature)\n}\n\nfunction moverDetalhe(evento?: PontoDoMapa) {\n if (detalhe.value) posicionarDetalhe(evento)\n}\n\nfunction fecharDetalhe() {\n if (!detalhe.value) return\n detalhe.value = null\n emit('feature-leave')\n}\n\n/** Clique no mesmo polígono fecha — é como o usuário desfaz o que abriu. */\nfunction alternarDetalhe(feature: MapSelectFeature, evento?: PontoDoMapa) {\n if (detalhe.value?.id === feature.id) {\n fecharDetalhe()\n return\n }\n detalhe.value = null\n abrirDetalhe(feature, evento)\n}\n\n/** Perto do topo o cartão vira para baixo: acima não haveria espaço. */\nconst detalheAbaixo = computed(() => detalhePos.value.y < 150)\n\nconst detalheStyle = computed(() =>\n props.detailPlacement === 'canto'\n ? undefined\n : { left: `${detalhePos.value.x}px`, top: `${detalhePos.value.y}px` },\n)\n\nfunction onEsc(evento: KeyboardEvent) {\n if (evento.key === 'Escape') fecharDetalhe()\n}\n\nfunction tooltipText(feature: MapSelectFeature): string {\n if (props.featureLabel) return props.featureLabel(feature)\n const area = areaText(feature)\n return area ? `${feature.nome} · ${area}` : feature.nome\n}\n\nfunction bindTip(layer: LeafletGeoJSON, feature: MapSelectFeature) {\n const texto = tooltipText(feature)\n // Rótulo vazio é escolha de quem chama (\"este polígono não tem legenda\"), e\n // um tooltip vazio ainda pintaria uma sombra de texto sobre o satélite.\n if (!texto) return\n layer.bindTooltip(\n texto,\n props.tooltips === 'hover'\n ? {\n permanent: false,\n sticky: true,\n direction: 'center',\n className: 'w-map-select__tip',\n opacity: 1,\n }\n : { permanent: true, direction: 'center', className: 'w-map-select__tip', opacity: 1 },\n )\n}\n\n/** `interactive: false` tira o polígono do hit-test do Leaflet (e o cursor de\n * mão junto). Com tooltip `'hover'`, com cartão de detalhe ou com alguém\n * ouvindo `feature-click`, a layer precisa continuar interativa mesmo sem\n * seleção — o cursor é suprimido via CSS (`--unselectable`). */\nconst layersInteractive = computed(\n () =>\n effectiveSelectionMode.value !== 'none' ||\n props.tooltips === 'hover' ||\n props.featureDetail !== 'none',\n)\n\nfunction createLayer(feature: MapSelectFeature) {\n if (!leaflet || !map || !feature.geometria) return\n const layer = leaflet.geoJSON(feature.geometria as unknown as Geometry, {\n style: () => styleFor(feature.id),\n interactive: layersInteractive.value,\n })\n layer.on('click', (evento) => {\n toggle(feature.id)\n // Clique SEMPRE avisa quem chama — inclusive no mapa de leitura, onde\n // `toggle` não faz nada. Abrir dialog é decisão da tela, não do componente.\n emit('feature-click', feature)\n if (props.featureDetail === 'click') alternarDetalhe(feature, comoPonto(evento))\n })\n if (props.featureDetail === 'hover') {\n layer.on('mouseover', (evento) => abrirDetalhe(feature, comoPonto(evento)))\n layer.on('mousemove', (evento) => moverDetalhe(comoPonto(evento)))\n layer.on('mouseout', () => fecharDetalhe())\n }\n bindTip(layer, feature)\n layer.addTo(map)\n layers.set(feature.id, layer)\n layerGeoms.set(feature.id, feature.geometria)\n}\n\n/** Sincroniza as layers com `features` por DIFF de `id`: remove as que saíram,\n * cria só as novas e recria a que trocou de `geometria` (referência). Páginas\n * chegando incrementalmente não destroem o que já está desenhado. */\nfunction drawFeatures() {\n if (!leaflet || !map) return\n\n const current = new Map<MapSelectId, MapSelectFeature>()\n for (const feature of props.features) {\n if (feature.geometria) current.set(feature.id, feature)\n }\n\n layers.forEach((layer, id) => {\n if (current.has(id)) return\n layer.remove()\n layers.delete(id)\n layerGeoms.delete(id)\n })\n\n current.forEach((feature, id) => {\n const existing = layers.get(id)\n if (existing && layerGeoms.get(id) === feature.geometria) {\n // Mesma geometria: só o texto do rótulo pode ter mudado (nome/área/label).\n const texto = tooltipText(feature)\n const tooltip = existing.getTooltip()\n if (!texto) existing.unbindTooltip()\n else if (tooltip) tooltip.setContent(texto)\n else bindTip(existing, feature)\n return\n }\n if (existing) {\n existing.remove()\n layers.delete(id)\n layerGeoms.delete(id)\n }\n createLayer(feature)\n })\n\n syncStyles()\n\n if (!fitted && (layers.size || props.scopeGeometry)) {\n fitToScope()\n fitted = true\n fittedToScope = Boolean(props.scopeGeometry)\n }\n}\n\n/** Reaplica o estilo (e a classe do tooltip) conforme a seleção atual. */\nfunction syncStyles() {\n layers.forEach((layer, id) => {\n const on = isSelected(id)\n layer.setStyle(styleFor(id))\n const element = layer.getTooltip()?.getElement()\n element?.classList.toggle('w-map-select__tip--on', on)\n })\n}\n\nfunction fitToScope() {\n if (!leaflet || !map) return\n const corners: LatLng[] = []\n\n if (props.scopeGeometry) {\n const scope = leaflet.geoJSON(props.scopeGeometry as unknown as Geometry).getBounds()\n if (scope.isValid()) corners.push(scope.getSouthWest(), scope.getNorthEast())\n }\n\n if (!corners.length) {\n layers.forEach((layer) => {\n const bounds = layer.getBounds()\n if (bounds.isValid()) corners.push(bounds.getSouthWest(), bounds.getNorthEast())\n })\n }\n\n if (corners.length) map.fitBounds(leaflet.latLngBounds(corners), { padding: [20, 20] })\n else map.setView([0, 0], 2)\n}\n\n/** Enquadra a layer de um id, com o mesmo padding do escopo. No-op se o id não\n * tem layer (sem geometria, ainda não chegou, mapa fora do ar). */\nfunction fitToFeature(id: MapSelectId, options?: { padding?: [number, number]; maxZoom?: number }) {\n if (!map) return\n const layer = layers.get(id)\n if (!layer) return\n const bounds = layer.getBounds()\n // `maxZoom` limita a aproximação: enquadrar UM talhão sem teto cola a câmera\n // nele e o usuário perde a vizinhança — quem chama escolhe o quão perto.\n if (bounds.isValid()) map.fitBounds(bounds, { padding: [20, 20], ...options })\n}\n\n// --- Hit-test por coordenada ------------------------------------------------\n//\n// O renderer canvas pinta todos os polígonos num único elemento — não existe\n// \"elemento sob o dedo\" para um arrasto consultar. O teste é geométrico:\n// ray casting sobre as coordenadas GeoJSON das próprias features, independente\n// de renderer e de layer.\n\nfunction pontoNoAnel(lng: number, lat: number, anel: MapSelectPosition[]): boolean {\n let dentro = false\n for (let i = 0, j = anel.length - 1; i < anel.length; j = i++) {\n const [xi, yi] = anel[i]\n const [xj, yj] = anel[j]\n const cruza = yi > lat !== yj > lat && lng < ((xj - xi) * (lat - yi)) / (yj - yi) + xi\n if (cruza) dentro = !dentro\n }\n return dentro\n}\n\nfunction pontoNaGeometria(lng: number, lat: number, geometria: MapSelectGeometry): boolean {\n const poligonos = (\n geometria.type === 'Polygon' ? [geometria.coordinates] : geometria.coordinates\n ) as MapSelectPosition[][][]\n for (const aneis of poligonos) {\n if (!aneis.length || !pontoNoAnel(lng, lat, aneis[0])) continue\n // Dentro do contorno externo: só não conta se caiu num buraco.\n const noBuraco = aneis.slice(1).some((anel) => pontoNoAnel(lng, lat, anel))\n if (!noBuraco) return true\n }\n return false\n}\n\n/** Feature sob um ponto da TELA (`clientX/clientY` de um PointerEvent) — o\n * gancho do arrasto: soltar/passar um item por cima do mapa e descobrir qual\n * polígono está sob o dedo. `null` fora do mapa, fora de qualquer polígono ou\n * com o Leaflet fora do ar. */\nfunction hitTest(clientX: number, clientY: number): MapSelectId | null {\n if (!map || !container.value) return null\n const rect = container.value.getBoundingClientRect()\n const x = clientX - rect.left\n const y = clientY - rect.top\n // Com o container medido, ponto fora dele não é candidato. (Em ambiente sem\n // layout — testes — o rect é 0×0 e a checagem não se aplica.)\n if (rect.width > 0 && rect.height > 0 && (x < 0 || y < 0 || x > rect.width || y > rect.height))\n return null\n const latlng = map.containerPointToLatLng([x, y])\n for (const feature of props.features) {\n if (feature.geometria && pontoNaGeometria(latlng.lng, latlng.lat, feature.geometria))\n return feature.id\n }\n return null\n}\n\n/** Recria todas as layers (opções de criação — tooltip/interatividade —\n * mudaram). Não mexe no enquadramento. */\nfunction rebuildLayers() {\n if (!leaflet || !map) return\n layers.forEach((layer) => layer.remove())\n layers.clear()\n layerGeoms.clear()\n drawFeatures()\n}\n\n/** O mapa costuma nascer escondido (dentro de um Dialog): sem isto o Leaflet\n * calcula 0×0 e desenha só um pedaço quando o container aparece. */\nfunction refreshSize() {\n if (!map || !container.value) return\n if (container.value.clientWidth > 0 && container.value.clientHeight > 0) map.invalidateSize()\n}\n\n/** No `sobreposto` o rodapé é uma barra flutuante: o painel precisa parar acima\n * dela e a atribuição do Leaflet precisa subir. Como a altura depende do que o\n * consumidor põe no `#footer`, ela é MEDIDA e publicada em `--w-map-foot-h` —\n * chutar um valor fixo quebraria com dois botões empilhados. */\nfunction measureFooter() {\n if (!root.value || !footer.value) return\n if (!isOverlay.value) {\n root.value.style.removeProperty('--w-map-foot-h')\n return\n }\n const h = footer.value.offsetHeight\n if (h > 0) root.value.style.setProperty('--w-map-foot-h', `${h}px`)\n}\n\nasync function setupMap() {\n if (!container.value) return\n try {\n const mod = (await import('leaflet')) as unknown as Leaflet & { default?: Leaflet }\n leaflet = mod.default ?? mod\n } catch {\n mapError.value = true\n return\n }\n\n // O componente pode ter sido desmontado durante o import().\n if (!container.value) return\n\n try {\n // `preferCanvas`: com 100+ polígonos o renderer SVG vira uma árvore DOM\n // pesada; no canvas os paths são pintados num único elemento.\n map = leaflet.map(container.value, {\n zoomControl: true,\n attributionControl: true,\n preferCanvas: true,\n })\n leaflet\n .tileLayer(props.tileUrl, {\n maxZoom: props.maxZoom,\n attribution: props.tileAttribution,\n })\n .addTo(map)\n drawFeatures()\n // Sem feature e sem escopo ainda não houve fit: o mapa precisa de UMA view\n // inicial (o Leaflet não opera sem centro/zoom definidos).\n if (!fitted) map.setView([0, 0], 2)\n refreshSize()\n\n if (typeof ResizeObserver !== 'undefined') {\n observer = new ResizeObserver(() => refreshSize())\n observer.observe(container.value)\n }\n } catch {\n mapError.value = true\n }\n}\n\nonMounted(() => {\n // `Esc` fecha o cartão — o mesmo gesto de qualquer overlay do sistema.\n if (typeof window !== 'undefined') window.addEventListener('keydown', onEsc)\n void setupMap()\n measureFooter()\n if (typeof ResizeObserver !== 'undefined' && footer.value) {\n footerObserver = new ResizeObserver(() => measureFooter())\n footerObserver.observe(footer.value)\n }\n})\n\nonBeforeUnmount(() => {\n if (typeof window !== 'undefined') window.removeEventListener('keydown', onEsc)\n observer?.disconnect()\n observer = null\n footerObserver?.disconnect()\n footerObserver = null\n layers.clear()\n layerGeoms.clear()\n map?.remove()\n map = null\n})\n\nwatch(() => props.features, drawFeatures)\n// Trocar quando/como o cartão abre recria as layers (os handlers são de\n// criação) e fecha o que estiver aberto — o modo antigo não vale mais.\nwatch([() => props.featureDetail, () => props.detailPlacement], () => {\n fecharDetalhe()\n rebuildLayers()\n})\nwatch(() => props.modelValue, syncStyles, { deep: true })\n// Trocar a regra de destaque repinta o que já está desenhado — sem isto o mapa\n// só mostraria a regra nova nas features que chegassem depois.\nwatch(() => props.featureStyle, syncStyles)\n// O destaque de arrasto muda a cada pointermove — repinta só estilos.\nwatch(() => props.highlightFeature, syncStyles)\n\n// A `scopeGeometry` pode chegar DEPOIS da primeira página de features (duas\n// requests em paralelo): ela ainda vale um único reenquadramento.\nwatch(\n () => props.scopeGeometry,\n (geo) => {\n if (!map || !geo || fittedToScope) return\n fitToScope()\n fitted = true\n fittedToScope = true\n },\n)\n\n// Tooltip e interatividade são opções de CRIAÇÃO da layer: mudou, recria tudo.\nwatch([() => props.tooltips, layersInteractive], () => rebuildLayers())\n\n// Recolher/expandir e trocar de arranjo mexem na caixa do mapa. O\n// ResizeObserver do canvas já pegaria, mas ele dispara no frame seguinte: sem o\n// invalidateSize explícito o Leaflet chega a pintar um quadro com tile cortado.\nwatch([collapsed, isOverlay], () => {\n void nextTick(() => {\n measureFooter()\n refreshSize()\n })\n})\n\ndefineExpose({\n refreshSize,\n /** Painel flutuante recolhido? (só faz sentido no `layout=\"sobreposto\"`.) */\n collapsed: somenteLeitura(collapsed),\n setCollapsed,\n /** Reenquadra no escopo (ou na união das features) — o fit automático só\n * acontece uma vez; páginas novas de `features` não movem o mapa. */\n fitToScope,\n /** Enquadra o polígono de um id (padding igual ao do escopo). No-op sem layer. */\n fitToFeature,\n /** Feature sob um ponto da tela (arrasto por cima do mapa). `null` fora. */\n hitTest,\n /** Feature do cartão de detalhe aberto (`null` fechado). */\n detalhe: somenteLeitura(detalhe),\n /** Fecha o cartão de detalhe — para a tela fechar de fora (navegou, salvou). */\n fecharDetalhe,\n})\n</script>\n\n<template>\n <div\n ref=\"root\"\n class=\"w-map-select\"\n :class=\"{\n 'w-map-select--disabled': disabled,\n 'w-map-select--readonly': readonly,\n 'w-map-select--unselectable': effectiveSelectionMode === 'none',\n 'w-map-select--overlay': isOverlay,\n 'w-map-select--collapsed': isOverlay && collapsed && !readonly,\n }\"\n >\n <div class=\"w-map-select__body\">\n <div class=\"w-map-select__map\" :style=\"{ '--w-map-h': height }\">\n <div ref=\"container\" class=\"w-map-select__canvas\" />\n <div v-if=\"mapError\" class=\"w-map-select__fallback\">\n <i class=\"pi pi-map\" />\n <span>{{ mapErrorText }}</span>\n </div>\n <span v-else-if=\"badgeLabel\" class=\"w-map-select__badge\">\n <i class=\"pi pi-globe\" />{{ badgeLabel }}\n </span>\n\n <!-- Cartão de detalhe: o componente posiciona, o slot preenche. -->\n <div\n v-if=\"detalhe && featureDetail !== 'none'\"\n class=\"w-map-select__detail\"\n :class=\"{\n 'w-map-select__detail--canto': detailPlacement === 'canto',\n 'w-map-select__detail--abaixo': detailPlacement !== 'canto' && detalheAbaixo,\n 'w-map-select__detail--hover': featureDetail === 'hover',\n }\"\n :style=\"detalheStyle\"\n role=\"tooltip\"\n >\n <slot\n name=\"feature-detail\"\n :feature=\"detalhe\"\n :selected=\"isSelected(detalhe.id)\"\n :close=\"fecharDetalhe\"\n >\n <span class=\"w-map-select__detail-nome\">{{ detalhe.nome }}</span>\n <span v-if=\"detalhe.subtitulo\" class=\"w-map-select__detail-sub\">\n {{ detalhe.subtitulo }}\n </span>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!readonly\" class=\"w-map-select__panel\">\n <button\n v-if=\"isOverlay\"\n type=\"button\"\n class=\"w-map-select__collapse\"\n :aria-expanded=\"!collapsed\"\n :aria-label=\"collapseTitle\"\n :title=\"collapseTitle\"\n @click=\"toggleCollapsed\"\n >\n <i class=\"pi\" :class=\"collapsed ? 'pi-list' : 'pi-chevron-right'\" />\n <span v-if=\"modelValue.length\" class=\"w-map-select__collapse-count\">\n {{ modelValue.length }}\n </span>\n </button>\n\n <div class=\"w-map-select__panel-head\">\n <IconField>\n <InputIcon class=\"pi pi-search\" />\n <InputText\n v-model=\"term\"\n fluid\n data-kbd-skip\n :placeholder=\"searchPlaceholder\"\n :aria-label=\"searchPlaceholder\"\n />\n </IconField>\n <div class=\"w-map-select__meta\">\n <span class=\"w-map-select__count\">{{ countText }}</span>\n <button\n type=\"button\"\n class=\"w-map-select__clear\"\n :disabled=\"disabled || !modelValue.length\"\n @click=\"clear\"\n >\n {{ clearLabel }}\n </button>\n </div>\n </div>\n\n <ul class=\"w-map-select__list\">\n <li v-for=\"feature in visibleFeatures\" :key=\"feature.id\">\n <slot name=\"item\" :feature=\"feature\" :selected=\"isSelected(feature.id)\">\n <button\n type=\"button\"\n class=\"w-map-select__item\"\n :class=\"{ 'w-map-select__item--on': isSelected(feature.id) }\"\n :aria-pressed=\"isSelected(feature.id)\"\n :disabled=\"disabled\"\n @click=\"toggle(feature.id)\"\n >\n <span class=\"w-map-select__box\">\n <i v-if=\"isSelected(feature.id)\" class=\"pi pi-check\" />\n </span>\n <span class=\"w-map-select__item-text\">\n <span class=\"w-map-select__item-name\">{{ feature.nome }}</span>\n <span v-if=\"feature.subtitulo\" class=\"w-map-select__item-sub\">\n {{ feature.subtitulo }}\n </span>\n </span>\n <i\n v-if=\"!feature.geometria\"\n class=\"pi pi-exclamation-circle w-map-select__item-nogeo\"\n title=\"Talhão sem contorno cadastrado\"\n />\n <span v-if=\"feature.area != null\" class=\"w-map-select__item-area\">\n {{ areaText(feature) }}\n </span>\n </button>\n </slot>\n </li>\n <li v-if=\"!visibleFeatures.length\" class=\"w-map-select__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </li>\n </ul>\n </div>\n </div>\n\n <div v-if=\"!readonly\" ref=\"footer\" class=\"w-map-select__footer\">\n <slot name=\"footer\" :area=\"selectedArea\" :features=\"selectedFeatures\">\n <span class=\"w-map-select__area\">\n {{ areaLabel }}\n <strong>{{ areaTotalText }}</strong>\n <span class=\"w-map-select__area-unit\">{{ areaSuffix }}</span>\n </span>\n </slot>\n </div>\n </div>\n</template>\n","/**\n * Renderer de markdown do design system — leitura em primeiro lugar.\n *\n * A referência é o Material for MkDocs: o texto puro continua legível, mas o\n * autor tem um vocabulário curto para DESTACAR (alertas), ORGANIZAR (passos,\n * cards, abas, detalhes) e ILUSTRAR (código com realce, diagramas mermaid).\n *\n * Duas sintaxes produzem o MESMO alerta, de propósito:\n * `> [!DICA]` — estilo GitHub, sobrevive em qualquer visualizador de markdown\n * `::: dica` — estilo VitePress/MkDocs, aceita título próprio e conteúdo longo\n *\n * Segurança: `html: false` (markdown cru nunca injeta HTML) e todo o output\n * passa pelo DOMPurify antes do `v-html` — defesa em profundidade, porque o\n * conteúdo é escrito por usuários.\n */\nimport MdIt from 'markdown-it'\nimport type { MarkdownIt, StateCore, Token } from 'markdown-it'\nimport DOMPurify from 'dompurify'\nimport anchor from 'markdown-it-anchor'\nimport container from 'markdown-it-container'\nimport abbr from 'markdown-it-abbr'\nimport deflist from 'markdown-it-deflist'\nimport footnote from 'markdown-it-footnote'\nimport ins from 'markdown-it-ins'\nimport mark from 'markdown-it-mark'\nimport sub from 'markdown-it-sub'\nimport sup from 'markdown-it-sup'\nimport type { MarkdownHeading, MarkdownRenderResult } from '@/types/markdown'\n\n/* ==========================================================\n Alertas — o vocabulário de destaque\n ========================================================== */\n\ninterface AlertSpec {\n /** Sufixo da classe CSS e do tom (`md-alert--dica`). */\n tone: string\n /** Título padrão, usado quando o autor não escreve um. */\n label: string\n icon: string\n}\n\nconst ALERTS: Record<string, AlertSpec> = {\n nota: { tone: 'nota', label: 'Nota', icon: 'pi pi-info-circle' },\n dica: { tone: 'dica', label: 'Dica', icon: 'pi pi-lightbulb' },\n importante: { tone: 'importante', label: 'Importante', icon: 'pi pi-bookmark-fill' },\n atencao: { tone: 'atencao', label: 'Atenção', icon: 'pi pi-exclamation-triangle' },\n cuidado: { tone: 'cuidado', label: 'Cuidado', icon: 'pi pi-times-circle' },\n sucesso: { tone: 'sucesso', label: 'Feito', icon: 'pi pi-check-circle' },\n exemplo: { tone: 'exemplo', label: 'Exemplo', icon: 'pi pi-code' },\n resumo: { tone: 'resumo', label: 'Resumo', icon: 'pi pi-align-left' },\n pergunta: { tone: 'pergunta', label: 'Em aberto', icon: 'pi pi-question-circle' },\n}\n\n/** Apelidos aceitos (GitHub, VitePress e MkDocs) → chave canônica. */\nconst ALERT_ALIASES: Record<string, string> = {\n note: 'nota',\n info: 'nota',\n observacao: 'nota',\n tip: 'dica',\n hint: 'dica',\n sugestao: 'dica',\n important: 'importante',\n warning: 'atencao',\n warn: 'atencao',\n attention: 'atencao',\n aviso: 'atencao',\n caution: 'cuidado',\n danger: 'cuidado',\n error: 'cuidado',\n perigo: 'cuidado',\n success: 'sucesso',\n done: 'sucesso',\n feito: 'sucesso',\n example: 'exemplo',\n summary: 'resumo',\n abstract: 'resumo',\n tldr: 'resumo',\n question: 'pergunta',\n help: 'pergunta',\n duvida: 'pergunta',\n}\n\n/** minúsculas + sem acento — `ATENÇÃO`, `Atencao` e `atenção` são o mesmo. */\nfunction normalizar(nome: string): string {\n return nome\n .trim()\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n}\n\nfunction resolverAlerta(nome: string): AlertSpec | null {\n const chave = normalizar(nome)\n return ALERTS[chave] ?? ALERTS[ALERT_ALIASES[chave]] ?? null\n}\n\nconst escapar = new MdIt().utils.escapeHtml\n\nfunction abrirAlerta(spec: AlertSpec, titulo?: string): string {\n const texto = titulo?.trim() || spec.label\n return (\n `<div class=\"md-alert md-alert--${spec.tone}\">` +\n `<p class=\"md-alert__head\"><i class=\"${spec.icon}\" aria-hidden=\"true\"></i>${escapar(texto)}</p>` +\n `<div class=\"md-alert__body\">`\n )\n}\n\nconst FECHAR_ALERTA = '</div></div>'\n\n/* ==========================================================\n Realce de sintaxe — highlight.js carregado sob demanda\n ========================================================== */\n\ntype FuncaoRealce = (codigo: string, linguagem: string) => string\n\n/** Só os idiomas que aparecem na documentação deste produto. */\nconst CARREGADORES: Record<string, () => Promise<{ default: unknown }>> = {\n bash: () => import('highlight.js/lib/languages/bash'),\n python: () => import('highlight.js/lib/languages/python'),\n javascript: () => import('highlight.js/lib/languages/javascript'),\n typescript: () => import('highlight.js/lib/languages/typescript'),\n json: () => import('highlight.js/lib/languages/json'),\n yaml: () => import('highlight.js/lib/languages/yaml'),\n sql: () => import('highlight.js/lib/languages/sql'),\n xml: () => import('highlight.js/lib/languages/xml'),\n css: () => import('highlight.js/lib/languages/css'),\n diff: () => import('highlight.js/lib/languages/diff'),\n ini: () => import('highlight.js/lib/languages/ini'),\n markdown: () => import('highlight.js/lib/languages/markdown'),\n dockerfile: () => import('highlight.js/lib/languages/dockerfile'),\n}\n\nconst APELIDOS_LINGUAGEM: Record<string, string> = {\n sh: 'bash',\n shell: 'bash',\n zsh: 'bash',\n console: 'bash',\n terminal: 'bash',\n js: 'javascript',\n jsx: 'javascript',\n mjs: 'javascript',\n ts: 'typescript',\n tsx: 'typescript',\n py: 'python',\n jsonc: 'json',\n json5: 'json',\n dotenv: 'ini',\n env: 'ini',\n toml: 'ini',\n cfg: 'ini',\n yml: 'yaml',\n html: 'xml',\n htm: 'xml',\n vue: 'xml',\n svg: 'xml',\n md: 'markdown',\n psql: 'sql',\n postgres: 'sql',\n postgresql: 'sql',\n docker: 'dockerfile',\n}\n\nlet realce: FuncaoRealce | null = null\nlet carregando: Promise<void> | null = null\n\n/** Já dá para realçar? (evita re-render inútil quando o pacote já veio) */\nexport function realceDisponivel(): boolean {\n return realce !== null\n}\n\n/**\n * Carrega o highlight.js e os idiomas — sob demanda, uma única vez.\n * Enquanto não chega, o código aparece sem cores (nunca quebrado).\n */\nexport async function carregarRealce(): Promise<void> {\n if (realce || carregando) return carregando ?? undefined\n carregando = (async () => {\n const nucleo = (await import('highlight.js/lib/core')).default\n await Promise.all(\n Object.entries(CARREGADORES).map(async ([nome, carregar]) => {\n const modulo = await carregar()\n nucleo.registerLanguage(nome, modulo.default as never)\n }),\n )\n realce = (codigo, linguagem) => nucleo.highlight(codigo, { language: linguagem }).value\n })()\n await carregando\n}\n\nfunction normalizarLinguagem(info: string): string {\n const bruta = normalizar(info)\n return APELIDOS_LINGUAGEM[bruta] ?? bruta\n}\n\n/* ==========================================================\n Instância markdown-it\n ========================================================== */\n\n/** Slug estável e sem acento para as âncoras (`## Não conformidade` → `nao-conformidade`). */\nfunction slug(texto: string): string {\n return (\n normalizar(texto)\n .replace(/[^\\w\\s-]/g, '')\n .trim()\n .replace(/\\s+/g, '-') || 'secao'\n )\n}\n\nconst NOMES_CONTAINER = new Set([\n 'detalhes',\n 'details',\n 'passos',\n 'steps',\n 'cards',\n 'grade',\n 'abas',\n 'tabs',\n 'aba',\n 'tab',\n])\n\nconst CONTAINER_RE = /^\\s*([^\\s]+)\\s*(.*)$/\n\nfunction criarRenderer(): MarkdownIt {\n const md = new MdIt({ html: false, linkify: true, typographer: false })\n\n md.use(abbr).use(deflist).use(footnote).use(ins).use(mark).use(sub).use(sup)\n\n md.use(anchor, {\n level: [2, 3, 4],\n slugify: slug,\n // Símbolo vazio de propósito: o \"#\" entra por CSS (`.md-anchor::after`),\n // então copiar o título não traz lixo junto.\n permalink: anchor.permalink.linkInsideHeader({\n symbol: '',\n class: 'md-anchor',\n placement: 'after',\n ariaHidden: true,\n }),\n })\n\n aplicarAlertas(md)\n aplicarContainers(md)\n aplicarTarefas(md)\n aplicarCodigo(md)\n aplicarTabelas(md)\n aplicarLinks(md)\n aplicarFiguras(md)\n\n return md\n}\n\n/** `> [!DICA] Título` — alerta com a sintaxe do GitHub (sobrevive fora do portal). */\nfunction aplicarAlertas(md: MarkdownIt): void {\n md.core.ruler.after('block', 'lz_alerta', (state: StateCore) => {\n const tokens = state.tokens\n for (let i = 0; i < tokens.length; i += 1) {\n const abre = tokens[i]\n if (abre.type !== 'blockquote_open') continue\n\n const paragrafo = tokens[i + 1]\n const inline = tokens[i + 2]\n if (paragrafo?.type !== 'paragraph_open' || inline?.type !== 'inline') continue\n\n const marcador = /^\\[!([^\\]\\n]+)\\][ \\t]*([^\\n]*)\\n?/.exec(inline.content)\n if (!marcador) continue\n\n const spec = resolverAlerta(marcador[1])\n if (!spec) continue\n\n const fecha = encontrarFechamento(tokens, i)\n if (!fecha) continue\n\n inline.content = inline.content.slice(marcador[0].length)\n abre.type = 'lz_alerta_open'\n abre.tag = 'div'\n abre.meta = { spec, titulo: marcador[2] }\n fecha.type = 'lz_alerta_close'\n fecha.tag = 'div'\n\n // `> [!DICA]` sozinho na primeira linha deixa um parágrafo vazio para trás.\n if (!inline.content.trim()) {\n tokens.splice(i + 1, 3)\n }\n }\n return true\n })\n\n md.renderer.rules.lz_alerta_open = (tokens, idx) => {\n const meta = tokens[idx].meta as { spec: AlertSpec; titulo?: string }\n return abrirAlerta(meta.spec, meta.titulo)\n }\n md.renderer.rules.lz_alerta_close = () => FECHAR_ALERTA\n}\n\n/** Fechamento do blockquote aberto em `inicio`, respeitando aninhamento. */\nfunction encontrarFechamento(tokens: Token[], inicio: number): Token | null {\n let profundidade = 0\n for (let i = inicio; i < tokens.length; i += 1) {\n const token = tokens[i]\n if (token.type === 'blockquote_open') profundidade += 1\n else if (token.type === 'blockquote_close') {\n profundidade -= 1\n if (profundidade === 0) return token\n }\n }\n return null\n}\n\n/** `::: dica`, `::: passos`, `::: cards`, `::: abas` + `::: aba`, `::: detalhes`. */\nfunction aplicarContainers(md: MarkdownIt): void {\n // A pilha casa o token de fechamento com o de abertura: o render roda em uma\n // passada síncrona, na ordem do documento.\n const pilha: string[] = []\n\n md.use(container, 'lz', {\n validate: (params: string) => {\n const partes = CONTAINER_RE.exec(params)\n if (!partes) return false\n const chave = normalizar(partes[1])\n return NOMES_CONTAINER.has(chave) || resolverAlerta(chave) !== null\n },\n render: (tokens: Token[], idx: number) => {\n const token = tokens[idx]\n if (token.nesting !== 1) {\n const chave = pilha.pop()\n if (chave === 'detalhes') return '</div></details>\\n'\n if (chave === 'aba') return '</section>\\n'\n if (chave === 'alerta') return `${FECHAR_ALERTA}\\n`\n return '</div>\\n'\n }\n\n const partes = CONTAINER_RE.exec(token.info) ?? ['', '', '']\n const chave = normalizar(partes[1])\n const titulo = partes[2]?.trim()\n\n if (chave === 'detalhes' || chave === 'details') {\n pilha.push('detalhes')\n return `<details class=\"md-details\"><summary>${escapar(titulo || 'Detalhes')}</summary><div class=\"md-details__body\">`\n }\n if (chave === 'passos' || chave === 'steps') {\n pilha.push('bloco')\n return '<div class=\"md-steps\">'\n }\n if (chave === 'cards' || chave === 'grade') {\n pilha.push('bloco')\n return '<div class=\"md-cards\">'\n }\n if (chave === 'abas' || chave === 'tabs') {\n pilha.push('bloco')\n return '<div class=\"md-tabs\" data-md-tabs>'\n }\n if (chave === 'aba' || chave === 'tab') {\n pilha.push('aba')\n return `<section class=\"md-tabs__panel\" data-md-tab=\"${escapar(titulo || 'Aba')}\">`\n }\n\n const spec = resolverAlerta(chave)\n if (spec) {\n pilha.push('alerta')\n return abrirAlerta(spec, titulo)\n }\n\n pilha.push('bloco')\n return '<div>'\n },\n })\n}\n\n/** `- [ ]` / `- [x]` viram checkbox desabilitado (leitura; quem marca é a tarefa do projeto). */\nfunction aplicarTarefas(md: MarkdownIt): void {\n md.core.ruler.after('inline', 'lz_tarefas', (state: StateCore) => {\n const tokens = state.tokens\n for (let i = 2; i < tokens.length; i += 1) {\n const inline = tokens[i]\n if (inline.type !== 'inline' || !inline.children?.length) continue\n if (tokens[i - 1].type !== 'paragraph_open' || tokens[i - 2].type !== 'list_item_open')\n continue\n const primeiro = inline.children[0]\n if (primeiro.type !== 'text') continue\n const marcado = /^\\[([ xX])\\]\\s+/.exec(primeiro.content)\n if (!marcado) continue\n\n primeiro.content = primeiro.content.slice(marcado[0].length)\n const checkbox = new state.Token('html_inline', '', 0)\n checkbox.content = `<input type=\"checkbox\" class=\"md-task__check\" disabled${marcado[1] === ' ' ? '' : ' checked'}> `\n inline.children.unshift(checkbox)\n tokens[i - 2].attrJoin('class', 'md-task')\n }\n return true\n })\n}\n\n/** Bloco de código com barra (título + linguagem) e o gancho do botão copiar. */\nfunction aplicarCodigo(md: MarkdownIt): void {\n md.renderer.rules.fence = (tokens, idx) => {\n const token = tokens[idx]\n const info = token.info.trim()\n const primeira = info.split(/\\s+/)[0] ?? ''\n const linguagem = normalizarLinguagem(primeira)\n const titulo = /title=\"([^\"]*)\"/.exec(info)?.[1]\n\n if (linguagem === 'mermaid') {\n return (\n `<figure class=\"md-mermaid\" data-md-mermaid>` +\n `<pre class=\"md-mermaid__fonte\">${escapar(token.content)}</pre>` +\n (titulo ? `<figcaption>${escapar(titulo)}</figcaption>` : '') +\n `</figure>\\n`\n )\n }\n\n const realcado =\n realce && CARREGADORES[linguagem] ? realce(token.content, linguagem) : escapar(token.content)\n\n const barra =\n titulo || primeira\n ? `<div class=\"md-code__bar\">` +\n `<span class=\"md-code__title\">${escapar(titulo ?? '')}</span>` +\n `<span class=\"md-code__lang\">${escapar(primeira)}</span>` +\n `</div>`\n : ''\n\n return (\n `<div class=\"md-code\" data-md-code>${barra}` +\n `<pre class=\"md-code__pre\"><code class=\"hljs language-${escapar(linguagem)}\">${realcado}</code></pre>` +\n `</div>\\n`\n )\n }\n}\n\n/** Tabela larga rola sozinha, sem empurrar a página. */\nfunction aplicarTabelas(md: MarkdownIt): void {\n const original = md.renderer.rules.table_open\n md.renderer.rules.table_open = (tokens, idx, options, env, self) =>\n `<div class=\"md-table\">${original ? original(tokens, idx, options, env, self) : self.renderToken(tokens, idx, options)}`\n md.renderer.rules.table_close = (tokens, idx, options, env, self) =>\n `${self.renderToken(tokens, idx, options)}</div>`\n}\n\n/** Link externo abre em nova aba e se anuncia como externo. */\nfunction aplicarLinks(md: MarkdownIt): void {\n const original = md.renderer.rules.link_open\n md.renderer.rules.link_open = (tokens, idx, options, env, self) => {\n const href = String(tokens[idx].attrGet('href') ?? '')\n if (/^https?:\\/\\//i.test(href)) {\n tokens[idx].attrSet('target', '_blank')\n tokens[idx].attrSet('rel', 'noopener noreferrer')\n tokens[idx].attrJoin('class', 'md-link-externo')\n }\n return original\n ? original(tokens, idx, options, env, self)\n : self.renderToken(tokens, idx, options)\n }\n}\n\n/** Imagem sozinha no parágrafo vira figura com legenda (o texto alternativo). */\nfunction aplicarFiguras(md: MarkdownIt): void {\n md.core.ruler.after('inline', 'lz_figuras', (state: StateCore) => {\n const tokens = state.tokens\n for (let i = 1; i < tokens.length - 1; i += 1) {\n const inline = tokens[i]\n if (inline.type !== 'inline' || tokens[i - 1].type !== 'paragraph_open') continue\n const filhos = (inline.children ?? []).filter((filho) => filho.type !== 'softbreak')\n if (filhos.length !== 1 || filhos[0].type !== 'image') continue\n\n tokens[i - 1].tag = 'figure'\n tokens[i - 1].attrJoin('class', 'md-figure')\n tokens[i + 1].tag = 'figure'\n filhos[0].meta = { figura: true }\n }\n return true\n })\n\n const original = md.renderer.rules.image\n md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const html = original\n ? original(tokens, idx, options, env, self)\n : self.renderToken(tokens, idx, options)\n const meta = tokens[idx].meta as { figura?: boolean } | undefined\n if (!meta?.figura) return html\n const legenda = tokens[idx].content?.trim()\n return legenda ? `${html}<figcaption>${escapar(legenda)}</figcaption>` : html\n }\n}\n\n/* ==========================================================\n API pública\n ========================================================== */\n\nlet instancia: MarkdownIt | null = null\n\nfunction obterRenderer(): MarkdownIt {\n if (!instancia) instancia = criarRenderer()\n return instancia\n}\n\n/** Texto puro de um heading, para o índice (sem `**`, `` ` `` ou links). */\nfunction textoDoTitulo(inline: Token): string {\n return (inline.children ?? [])\n .filter((filho) => filho.type === 'text' || filho.type === 'code_inline')\n .map((filho) => filho.content)\n .join('')\n .trim()\n}\n\nfunction coletarTitulos(tokens: Token[]): MarkdownHeading[] {\n const titulos: MarkdownHeading[] = []\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]\n if (token.type !== 'heading_open') continue\n const nivel = Number(token.tag.slice(1))\n if (nivel < 2 || nivel > 3) continue\n const id = String(token.attrGet('id') ?? '')\n const inline = tokens[i + 1]\n if (!id || inline?.type !== 'inline') continue\n titulos.push({ id, text: textoDoTitulo(inline), level: nivel })\n }\n return titulos\n}\n\n/** Sanitiza o HTML do markdown (mantém âncoras, notas de rodapé e checkboxes). */\nexport function sanitizarMarkdown(html: string): string {\n return DOMPurify.sanitize(html, {\n ADD_ATTR: ['target', 'open', 'checked', 'disabled'],\n })\n}\n\n/**\n * Renderiza markdown para HTML sanitizado e devolve também o índice de títulos.\n *\n * O realce de sintaxe só aparece depois de `carregarRealce()` — chame antes (ou\n * re-renderize depois) para colorir o código.\n */\nexport function renderMarkdown(source: string): MarkdownRenderResult {\n const md = obterRenderer()\n const env: Record<string, unknown> = {}\n const tokens = md.parse(source ?? '', env)\n return {\n html: sanitizarMarkdown(md.renderer.render(tokens, md.options, env)),\n headings: coletarTitulos(tokens),\n }\n}\n","/**\n * Enriquecimento do markdown JÁ renderizado — o que precisa de DOM/JS.\n *\n * Roda depois do `v-html` (e depois do DOMPurify, por isso os botões nascem\n * aqui e não no HTML sanitizado): botão de copiar nos blocos de código, abas\n * clicáveis e diagramas mermaid (carregados sob demanda, na paleta do tema).\n */\n\n/* ==========================================================\n Copiar código\n ========================================================== */\n\nfunction aplicarCopiar(raiz: HTMLElement): void {\n raiz.querySelectorAll<HTMLElement>('[data-md-code]').forEach((bloco) => {\n if (bloco.dataset.mdPronto) return\n bloco.dataset.mdPronto = '1'\n\n const botao = document.createElement('button')\n botao.type = 'button'\n botao.className = 'md-code__copiar'\n botao.setAttribute('aria-label', 'Copiar código')\n botao.title = 'Copiar código'\n botao.innerHTML = '<i class=\"pi pi-copy\" aria-hidden=\"true\"></i>'\n botao.addEventListener('click', () => {\n const codigo = bloco.querySelector('code')?.textContent ?? ''\n void navigator.clipboard?.writeText(codigo).then(() => {\n botao.classList.add('md-code__copiar--feito')\n botao.innerHTML = '<i class=\"pi pi-check\" aria-hidden=\"true\"></i>'\n window.setTimeout(() => {\n botao.classList.remove('md-code__copiar--feito')\n botao.innerHTML = '<i class=\"pi pi-copy\" aria-hidden=\"true\"></i>'\n }, 1600)\n })\n })\n bloco.appendChild(botao)\n })\n}\n\n/* ==========================================================\n Abas\n ========================================================== */\n\n/**\n * Blocos `::: aba` seguidos viram um grupo de abas sozinhos — o autor não\n * precisa envolver tudo num `:::: abas` (aninhar marcador dentro de marcador é\n * a parte chata da sintaxe). O wrapper explícito continua funcionando.\n */\nfunction agruparAbasSoltas(raiz: HTMLElement): void {\n const soltos = Array.from(raiz.querySelectorAll<HTMLElement>('.md-tabs__panel')).filter(\n (painel) => !painel.parentElement?.hasAttribute('data-md-tabs'),\n )\n const vistos = new Set<HTMLElement>()\n\n soltos.forEach((primeiro) => {\n if (vistos.has(primeiro)) return\n const sequencia = [primeiro]\n let proximo = primeiro.nextElementSibling\n while (proximo instanceof HTMLElement && proximo.classList.contains('md-tabs__panel')) {\n sequencia.push(proximo)\n proximo = proximo.nextElementSibling\n }\n sequencia.forEach((painel) => vistos.add(painel))\n\n const grupo = document.createElement('div')\n grupo.className = 'md-tabs'\n grupo.setAttribute('data-md-tabs', '')\n primeiro.before(grupo)\n sequencia.forEach((painel) => grupo.appendChild(painel))\n })\n}\n\nfunction aplicarAbas(raiz: HTMLElement): void {\n agruparAbasSoltas(raiz)\n raiz.querySelectorAll<HTMLElement>('[data-md-tabs]').forEach((grupo, indiceGrupo) => {\n if (grupo.dataset.mdPronto) return\n const paineis = Array.from(grupo.querySelectorAll<HTMLElement>(':scope > .md-tabs__panel'))\n if (!paineis.length) return\n grupo.dataset.mdPronto = '1'\n\n const barra = document.createElement('div')\n barra.className = 'md-tabs__bar'\n barra.setAttribute('role', 'tablist')\n\n const botoes = paineis.map((painel, indice) => {\n const id = `md-aba-${indiceGrupo}-${indice}`\n painel.id = `${id}-painel`\n painel.setAttribute('role', 'tabpanel')\n painel.setAttribute('aria-labelledby', id)\n\n const botao = document.createElement('button')\n botao.type = 'button'\n botao.id = id\n botao.className = 'md-tabs__botao'\n botao.setAttribute('role', 'tab')\n botao.setAttribute('aria-controls', painel.id)\n botao.textContent = painel.dataset.mdTab ?? `Aba ${indice + 1}`\n botao.addEventListener('click', () => selecionar(indice))\n barra.appendChild(botao)\n return botao\n })\n\n function selecionar(ativo: number): void {\n botoes.forEach((botao, indice) => {\n const selecionado = indice === ativo\n botao.setAttribute('aria-selected', String(selecionado))\n botao.tabIndex = selecionado ? 0 : -1\n paineis[indice].hidden = !selecionado\n })\n }\n\n grupo.prepend(barra)\n selecionar(0)\n })\n}\n\n/* ==========================================================\n Diagramas (mermaid)\n ========================================================== */\n\nlet mermaidPromise: Promise<typeof import('mermaid').default> | null = null\nlet sequencia = 0\n\nasync function carregarMermaid() {\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((modulo) => modulo.default)\n }\n return mermaidPromise\n}\n\n/** Elementos que nunca fazem parte de um diagrama — e que trazem execução junto. */\nconst TAGS_PROIBIDAS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'link',\n 'meta',\n 'base',\n 'form',\n 'input',\n 'button',\n 'audio',\n 'video',\n 'animate',\n 'set',\n])\n\n/** Remove tag executável, handler `on*` e URL `javascript:` — recursivo. */\nfunction limparNo(elemento: Element): void {\n for (const atributo of Array.from(elemento.attributes)) {\n const nome = atributo.name.toLowerCase()\n const valor = atributo.value.trim().toLowerCase()\n const ehLink = nome === 'href' || nome === 'xlink:href' || nome === 'src'\n if (nome.startsWith('on') || (ehLink && /^(javascript|data|vbscript):/.test(valor))) {\n elemento.removeAttribute(atributo.name)\n }\n }\n for (const filho of Array.from(elemento.children)) {\n if (TAGS_PROIBIDAS.has(filho.localName.toLowerCase())) filho.remove()\n else limparNo(filho)\n }\n}\n\n/**\n * Limpa o SVG do mermaid **sem perder os rótulos**.\n *\n * Os rótulos ficam dentro de `<foreignObject>` (HTML embutido no SVG), e o\n * DOMPurify genérico descarta esse conteúdo — o diagrama sairia com as caixas\n * vazias. Então a limpeza aqui é explícita e sobre o DOM já parseado como\n * `image/svg+xml`: fora tag executável, handler inline e URL de script.\n *\n * Isso é a **segunda** camada: o mermaid roda em `securityLevel: 'strict'`,\n * que escapa o texto do autor e já passa o SVG pelo próprio DOMPurify dele\n * (com a configuração que os rótulos exigem).\n *\n * @returns o nó limpo, ou `null` se o SVG vier malformado.\n */\nfunction sanitizarSvg(svg: string): Element | null {\n const documento = new DOMParser().parseFromString(svg, 'image/svg+xml')\n if (documento.querySelector('parsererror')) return null\n const raiz = documento.documentElement\n if (raiz.localName.toLowerCase() !== 'svg') return null\n limparNo(raiz)\n return raiz\n}\n\n/** Lê um token do tema atual do documento (o diagrama fica na paleta do portal). */\nfunction token(estilo: CSSStyleDeclaration, nome: string, padrao: string): string {\n return estilo.getPropertyValue(nome).trim() || padrao\n}\n\nfunction configurarMermaid(mermaid: Awaited<ReturnType<typeof carregarMermaid>>): void {\n const estilo = getComputedStyle(document.documentElement)\n const superficie = token(estilo, '--surface', '#ffffff')\n const texto = token(estilo, '--fg', '#141a22')\n const primaria = token(estilo, '--primary', '#1f5092')\n const suave = token(estilo, '--primary-soft', '#eef4fb')\n const borda = token(estilo, '--border-strong', '#cdd6e2')\n\n mermaid.initialize({\n startOnLoad: false,\n securityLevel: 'strict',\n theme: 'base',\n fontFamily: token(estilo, '--font-sans', 'inherit'),\n flowchart: { useMaxWidth: true },\n themeVariables: {\n background: superficie,\n primaryColor: suave,\n primaryTextColor: texto,\n primaryBorderColor: primaria,\n secondaryColor: token(estilo, '--surface-3', '#eef2f7'),\n tertiaryColor: token(estilo, '--surface-2', '#f8fafc'),\n lineColor: borda,\n textColor: texto,\n mainBkg: suave,\n nodeBorder: primaria,\n clusterBkg: token(estilo, '--surface-2', '#f8fafc'),\n clusterBorder: borda,\n fontSize: '13px',\n },\n })\n}\n\n/**\n * Renderiza os blocos ```mermaid do container.\n * @param forcar redesenha os que já estavam prontos (troca de tema).\n */\nexport async function renderizarDiagramas(raiz: HTMLElement, forcar = false): Promise<void> {\n const alvos = Array.from(raiz.querySelectorAll<HTMLElement>('[data-md-mermaid]')).filter(\n (figura) => forcar || !figura.dataset.mdEstado,\n )\n if (!alvos.length) return\n\n const mermaid = await carregarMermaid()\n configurarMermaid(mermaid)\n\n for (const figura of alvos) {\n const fonte = figura.querySelector<HTMLElement>('.md-mermaid__fonte')?.textContent ?? ''\n if (!fonte.trim()) continue\n figura.querySelector('.md-mermaid__svg')?.remove()\n try {\n sequencia += 1\n const { svg } = await mermaid.render(`md-diagrama-${sequencia}`, fonte)\n const limpo = sanitizarSvg(svg)\n if (!limpo) {\n figura.dataset.mdEstado = 'erro'\n continue\n }\n const caixa = document.createElement('div')\n caixa.className = 'md-mermaid__svg'\n caixa.appendChild(document.importNode(limpo, true))\n figura.prepend(caixa)\n figura.dataset.mdEstado = 'ok'\n } catch {\n // Diagrama inválido: mostra o código-fonte em vez de sumir com o conteúdo.\n figura.dataset.mdEstado = 'erro'\n }\n }\n}\n\n/* ==========================================================\n API\n ========================================================== */\n\n/** Aplica tudo. Idempotente — pode ser chamado a cada re-render. */\nexport async function enhanceMarkdown(raiz: HTMLElement | null): Promise<void> {\n if (!raiz) return\n aplicarCopiar(raiz)\n aplicarAbas(raiz)\n await renderizarDiagramas(raiz)\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { carregarRealce, realceDisponivel, renderMarkdown } from '@/utils/markdown'\nimport { enhanceMarkdown, renderizarDiagramas } from '@/utils/markdownEnhance'\nimport type { MarkdownHeading } from '@/types/markdown'\n\n/**\n * Leitor de markdown rico — a tela de leitura do design system.\n *\n * Além do markdown padrão (títulos, listas, tabelas, citações), entende o\n * vocabulário de documentação do produto: alertas (`> [!DICA]` ou `::: dica`),\n * passos, cards, abas, blocos colapsáveis, notas de rodapé, código com realce e\n * botão de copiar, e diagramas ```mermaid.\n *\n * Sempre sanitizado (DOMPurify) antes do `v-html` — o conteúdo vem de usuários.\n */\nconst props = withDefaults(\n defineProps<{\n /** Markdown cru a renderizar. */\n source: string\n /** Mensagem exibida quando `source` está vazio. */\n emptyText?: string\n }>(),\n {\n emptyText: 'Sem conteúdo',\n },\n)\n\nconst emit = defineEmits<{\n /** Títulos (`##`/`###`) do documento — alimenta um índice lateral. */\n headings: [MarkdownHeading[]]\n}>()\n\nconst raiz = ref<HTMLElement | null>(null)\n/** Vira `true` quando o highlight.js termina de carregar — dispara o re-render. */\nconst realcePronto = ref(realceDisponivel())\n\nconst resultado = computed(() => {\n // Dependência proposital: o realce chega depois e o HTML precisa ser refeito.\n void realcePronto.value\n return renderMarkdown(props.source ?? '')\n})\n\nconst html = computed(() => resultado.value.html)\nconst isEmpty = computed(() => !props.source || !props.source.trim())\n\nwatch(resultado, (valor) => emit('headings', valor.headings), { immediate: true })\nwatch(html, () => void nextTick(() => enhanceMarkdown(raiz.value)))\n\n/** Diagramas usam as cores do tema — redesenha quando o portal troca claro/escuro. */\nlet observador: MutationObserver | null = null\n\nfunction observarTema(): void {\n if (typeof MutationObserver === 'undefined') return\n observador = new MutationObserver(() => void renderizarDiagramas(raiz.value!, true))\n observador.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n}\n\nonMounted(async () => {\n await nextTick()\n await enhanceMarkdown(raiz.value)\n observarTema()\n if (!realcePronto.value) {\n await carregarRealce()\n realcePronto.value = true\n }\n})\n\nonBeforeUnmount(() => observador?.disconnect())\n</script>\n\n<template>\n <div v-if=\"isEmpty\" class=\"w-markdown-view w-markdown-view--empty\">{{ emptyText }}</div>\n <!-- eslint-disable-next-line vue/no-v-html — output sanitizado pelo DOMPurify -->\n <div v-else ref=\"raiz\" class=\"w-markdown-view\" v-html=\"html\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { carregarRealce, realceDisponivel, renderMarkdown } from '@/utils/markdown'\nimport { enhanceMarkdown, renderizarDiagramas } from '@/utils/markdownEnhance'\nimport type { MarkdownHeading } from '@/types/markdown'\n\n/**\n * Leitor de markdown rico — a tela de leitura do design system.\n *\n * Além do markdown padrão (títulos, listas, tabelas, citações), entende o\n * vocabulário de documentação do produto: alertas (`> [!DICA]` ou `::: dica`),\n * passos, cards, abas, blocos colapsáveis, notas de rodapé, código com realce e\n * botão de copiar, e diagramas ```mermaid.\n *\n * Sempre sanitizado (DOMPurify) antes do `v-html` — o conteúdo vem de usuários.\n */\nconst props = withDefaults(\n defineProps<{\n /** Markdown cru a renderizar. */\n source: string\n /** Mensagem exibida quando `source` está vazio. */\n emptyText?: string\n }>(),\n {\n emptyText: 'Sem conteúdo',\n },\n)\n\nconst emit = defineEmits<{\n /** Títulos (`##`/`###`) do documento — alimenta um índice lateral. */\n headings: [MarkdownHeading[]]\n}>()\n\nconst raiz = ref<HTMLElement | null>(null)\n/** Vira `true` quando o highlight.js termina de carregar — dispara o re-render. */\nconst realcePronto = ref(realceDisponivel())\n\nconst resultado = computed(() => {\n // Dependência proposital: o realce chega depois e o HTML precisa ser refeito.\n void realcePronto.value\n return renderMarkdown(props.source ?? '')\n})\n\nconst html = computed(() => resultado.value.html)\nconst isEmpty = computed(() => !props.source || !props.source.trim())\n\nwatch(resultado, (valor) => emit('headings', valor.headings), { immediate: true })\nwatch(html, () => void nextTick(() => enhanceMarkdown(raiz.value)))\n\n/** Diagramas usam as cores do tema — redesenha quando o portal troca claro/escuro. */\nlet observador: MutationObserver | null = null\n\nfunction observarTema(): void {\n if (typeof MutationObserver === 'undefined') return\n observador = new MutationObserver(() => void renderizarDiagramas(raiz.value!, true))\n observador.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n}\n\nonMounted(async () => {\n await nextTick()\n await enhanceMarkdown(raiz.value)\n observarTema()\n if (!realcePronto.value) {\n await carregarRealce()\n realcePronto.value = true\n }\n})\n\nonBeforeUnmount(() => observador?.disconnect())\n</script>\n\n<template>\n <div v-if=\"isEmpty\" class=\"w-markdown-view w-markdown-view--empty\">{{ emptyText }}</div>\n <!-- eslint-disable-next-line vue/no-v-html — output sanitizado pelo DOMPurify -->\n <div v-else ref=\"raiz\" class=\"w-markdown-view\" v-html=\"html\" />\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\nimport WMarkdownView from '../ui/WMarkdownView.vue'\n\n/**\n * Editor de markdown com toolbar mínima e preview (reutiliza o WMarkdownView).\n * A área de edição é um `<textarea>` monospace nativo — Tab mantém o\n * comportamento de foco padrão (a11y). As ações da toolbar inserem a sintaxe\n * na posição do cursor/seleção.\n */\nconst props = withDefaults(\n defineProps<{\n /** Markdown cru (v-model). */\n modelValue: string\n disabled?: boolean\n placeholder?: string\n /** Altura mínima da área de edição/preview. */\n minHeight?: string\n /**\n * Altura máxima da área de edição/preview. Com valor, o editor vira moldura\n * fixa e cada painel rola por dentro — as ações do formulário seguem\n * visíveis por mais longo que fique o documento.\n */\n maxHeight?: string\n /** 'tab' alterna escrever/visualizar; 'split' mostra lado a lado. */\n previewMode?: 'tab' | 'split'\n }>(),\n {\n disabled: false,\n minHeight: '320px',\n maxHeight: 'none',\n previewMode: 'tab',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null)\nconst previewing = ref(false)\n\nconst isSplit = computed(() => props.previewMode === 'split')\nconst showTextarea = computed(() => isSplit.value || !previewing.value)\nconst showPreview = computed(() => isSplit.value || previewing.value)\n\nfunction onInput(event: Event) {\n emit('update:modelValue', (event.target as HTMLTextAreaElement).value)\n}\n\n/** Aplica `next` e restaura foco + seleção no textarea. */\nfunction apply(next: string, selectionStart: number, selectionEnd: number) {\n emit('update:modelValue', next)\n nextTick(() => {\n const el = textareaRef.value\n if (!el) return\n el.focus()\n el.setSelectionRange(selectionStart, selectionEnd)\n })\n}\n\n/** Envolve a seleção com `before`/`after` (ex.: negrito = **seleção**). */\nfunction surround(before: string, after = before, placeholder = 'texto') {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const selected = value.slice(start, end) || placeholder\n const next = value.slice(0, start) + before + selected + after + value.slice(end)\n apply(next, start + before.length, start + before.length + selected.length)\n}\n\n/** Prefixa cada linha da seleção (heading, listas). */\nfunction prefixLines(prefix: string | ((index: number) => string)) {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1\n const lineEndRaw = value.indexOf('\\n', end)\n const lineEnd = lineEndRaw === -1 ? value.length : lineEndRaw\n const block = value.slice(lineStart, lineEnd)\n const prefixed = block\n .split('\\n')\n .map((line, i) => (typeof prefix === 'function' ? prefix(i) : prefix) + line)\n .join('\\n')\n const next = value.slice(0, lineStart) + prefixed + value.slice(lineEnd)\n apply(next, lineStart, lineStart + prefixed.length)\n}\n\nfunction actionBold() {\n surround('**')\n}\n\nfunction actionItalic() {\n surround('*')\n}\n\nfunction actionHeading() {\n prefixLines('## ')\n}\n\nfunction actionList() {\n prefixLines('- ')\n}\n\nfunction actionTaskList() {\n prefixLines('- [ ] ')\n}\n\nfunction actionLink() {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const selected = value.slice(start, end) || 'texto'\n const next = `${value.slice(0, start)}[${selected}](url)${value.slice(end)}`\n // Deixa \"url\" selecionado, pronto para colar o endereço.\n const urlStart = start + selected.length + 3\n apply(next, urlStart, urlStart + 3)\n}\n\nfunction actionCode() {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const selected = (props.modelValue ?? '').slice(el.selectionStart, el.selectionEnd)\n if (selected.includes('\\n')) surround('```\\n', '\\n```', 'código')\n else surround('`', '`', 'código')\n}\n\n/**\n * Insere um bloco em linha própria. No modelo, `$` marca o trecho que fica\n * selecionado depois (ou onde entra o texto que já estava selecionado).\n */\nfunction inserirBloco(modelo: string, exemplo: string) {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const alvo = value.slice(start, end) || exemplo\n const corpo = modelo.replace('$', alvo)\n const antes = value.slice(0, start)\n // Bloco precisa de linha em branco antes para o markdown reconhecer.\n const respiro = !antes || antes.endsWith('\\n\\n') ? '' : antes.endsWith('\\n') ? '\\n' : '\\n\\n'\n const next = `${antes}${respiro}${corpo}\\n${value.slice(end)}`\n const posicao = antes.length + respiro.length + corpo.indexOf(alvo)\n apply(next, posicao, posicao + alvo.length)\n}\n\nfunction actionAlert() {\n inserirBloco('> [!DICA]\\n> $', 'Escreva aqui a sugestão')\n}\n\nfunction actionSteps() {\n inserirBloco('::: passos\\n1. $\\n2. Próximo passo\\n:::', 'Primeiro passo')\n}\n\nfunction actionTable() {\n inserirBloco('| Coluna | Coluna |\\n| --- | --- |\\n| $ | valor |', 'valor')\n}\n\nfunction actionHighlight() {\n surround('==', '==', 'destaque')\n}\n\nfunction togglePreview() {\n previewing.value = !previewing.value\n}\n\nconst ajudaAberta = ref(false)\n\n/** Cola rápida do vocabulário — o autor não precisa decorar a sintaxe. */\nconst ajuda: { sintaxe: string; efeito: string }[] = [\n { sintaxe: '## Título', efeito: 'Seção (entra no índice lateral)' },\n { sintaxe: '> [!DICA] Título', efeito: 'Destaque: dica, nota, importante, atenção, cuidado' },\n { sintaxe: '::: passos … :::', efeito: 'Procedimento numerado' },\n { sintaxe: '::: cards … :::', efeito: 'Lista em cartões' },\n { sintaxe: '::: aba Título', efeito: 'Conteúdo em abas (blocos seguidos)' },\n { sintaxe: '::: detalhes Título', efeito: 'Bloco que abre e fecha' },\n { sintaxe: '==destaque==', efeito: 'Texto marcado' },\n { sintaxe: '- [ ] tarefa', efeito: 'Checklist' },\n { sintaxe: '```python title=\"arquivo.py\"', efeito: 'Código com realce e cópia' },\n { sintaxe: '```mermaid', efeito: 'Diagrama de fluxo' },\n]\n\ninterface ToolDef {\n /** Ícone PrimeIcons OU glyph textual (PrimeIcons não tem bold/itálico). */\n icon?: string\n glyph?: string\n label: string\n run: () => void\n}\n\nconst tools: ToolDef[] = [\n { glyph: 'B', label: 'Negrito', run: actionBold },\n { glyph: 'I', label: 'Itálico', run: actionItalic },\n { icon: 'pi pi-hashtag', label: 'Título (H2)', run: actionHeading },\n { icon: 'pi pi-list', label: 'Lista', run: actionList },\n { icon: 'pi pi-check-square', label: 'Lista de tarefas', run: actionTaskList },\n { icon: 'pi pi-link', label: 'Link', run: actionLink },\n { icon: 'pi pi-code', label: 'Código', run: actionCode },\n { icon: 'pi pi-info-circle', label: 'Destaque', run: actionAlert },\n { icon: 'pi pi-sort-numeric-down', label: 'Passos', run: actionSteps },\n { icon: 'pi pi-table', label: 'Tabela', run: actionTable },\n { icon: 'pi pi-pencil', label: 'Marcar texto', run: actionHighlight },\n]\n</script>\n\n<template>\n <div class=\"w-markdown-editor\" :class=\"{ 'w-markdown-editor--split': isSplit }\">\n <div class=\"w-markdown-editor__toolbar\" role=\"toolbar\" aria-label=\"Formatação markdown\">\n <button\n v-for=\"tool in tools\"\n :key=\"tool.label\"\n type=\"button\"\n class=\"w-markdown-editor__tool\"\n :aria-label=\"tool.label\"\n :title=\"tool.label\"\n :disabled=\"disabled || (!isSplit && previewing)\"\n @click=\"tool.run()\"\n >\n <i v-if=\"tool.icon\" :class=\"tool.icon\" aria-hidden=\"true\" />\n <span\n v-else\n class=\"w-markdown-editor__glyph\"\n :class=\"{\n 'w-markdown-editor__glyph--bold': tool.glyph === 'B',\n 'w-markdown-editor__glyph--italic': tool.glyph === 'I',\n }\"\n aria-hidden=\"true\"\n >\n {{ tool.glyph }}\n </span>\n </button>\n\n <button\n type=\"button\"\n class=\"w-markdown-editor__tool w-markdown-editor__tool--ajuda\"\n :class=\"{ 'w-markdown-editor__tool--active': ajudaAberta }\"\n aria-label=\"Sintaxe disponível\"\n title=\"Sintaxe disponível\"\n :aria-pressed=\"ajudaAberta\"\n @click=\"ajudaAberta = !ajudaAberta\"\n >\n <i class=\"pi pi-question-circle\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"!isSplit\"\n type=\"button\"\n class=\"w-markdown-editor__tool w-markdown-editor__tool--preview\"\n :class=\"{ 'w-markdown-editor__tool--active': previewing }\"\n aria-label=\"Visualizar\"\n title=\"Visualizar\"\n :aria-pressed=\"previewing\"\n @click=\"togglePreview\"\n >\n <i :class=\"previewing ? 'pi pi-pencil' : 'pi pi-eye'\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <dl v-if=\"ajudaAberta\" class=\"w-markdown-editor__ajuda\">\n <template v-for=\"linha in ajuda\" :key=\"linha.sintaxe\">\n <dt>{{ linha.sintaxe }}</dt>\n <dd>{{ linha.efeito }}</dd>\n </template>\n </dl>\n\n <div class=\"w-markdown-editor__body\" :style=\"{ minHeight, maxHeight }\">\n <textarea\n v-if=\"showTextarea\"\n ref=\"textareaRef\"\n class=\"w-markdown-editor__textarea\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n spellcheck=\"false\"\n @input=\"onInput\"\n />\n <WMarkdownView\n v-if=\"showPreview\"\n class=\"w-markdown-editor__preview\"\n :source=\"modelValue\"\n empty-text=\"Nada para visualizar\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\nimport WMarkdownView from '../ui/WMarkdownView.vue'\n\n/**\n * Editor de markdown com toolbar mínima e preview (reutiliza o WMarkdownView).\n * A área de edição é um `<textarea>` monospace nativo — Tab mantém o\n * comportamento de foco padrão (a11y). As ações da toolbar inserem a sintaxe\n * na posição do cursor/seleção.\n */\nconst props = withDefaults(\n defineProps<{\n /** Markdown cru (v-model). */\n modelValue: string\n disabled?: boolean\n placeholder?: string\n /** Altura mínima da área de edição/preview. */\n minHeight?: string\n /**\n * Altura máxima da área de edição/preview. Com valor, o editor vira moldura\n * fixa e cada painel rola por dentro — as ações do formulário seguem\n * visíveis por mais longo que fique o documento.\n */\n maxHeight?: string\n /** 'tab' alterna escrever/visualizar; 'split' mostra lado a lado. */\n previewMode?: 'tab' | 'split'\n }>(),\n {\n disabled: false,\n minHeight: '320px',\n maxHeight: 'none',\n previewMode: 'tab',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null)\nconst previewing = ref(false)\n\nconst isSplit = computed(() => props.previewMode === 'split')\nconst showTextarea = computed(() => isSplit.value || !previewing.value)\nconst showPreview = computed(() => isSplit.value || previewing.value)\n\nfunction onInput(event: Event) {\n emit('update:modelValue', (event.target as HTMLTextAreaElement).value)\n}\n\n/** Aplica `next` e restaura foco + seleção no textarea. */\nfunction apply(next: string, selectionStart: number, selectionEnd: number) {\n emit('update:modelValue', next)\n nextTick(() => {\n const el = textareaRef.value\n if (!el) return\n el.focus()\n el.setSelectionRange(selectionStart, selectionEnd)\n })\n}\n\n/** Envolve a seleção com `before`/`after` (ex.: negrito = **seleção**). */\nfunction surround(before: string, after = before, placeholder = 'texto') {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const selected = value.slice(start, end) || placeholder\n const next = value.slice(0, start) + before + selected + after + value.slice(end)\n apply(next, start + before.length, start + before.length + selected.length)\n}\n\n/** Prefixa cada linha da seleção (heading, listas). */\nfunction prefixLines(prefix: string | ((index: number) => string)) {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1\n const lineEndRaw = value.indexOf('\\n', end)\n const lineEnd = lineEndRaw === -1 ? value.length : lineEndRaw\n const block = value.slice(lineStart, lineEnd)\n const prefixed = block\n .split('\\n')\n .map((line, i) => (typeof prefix === 'function' ? prefix(i) : prefix) + line)\n .join('\\n')\n const next = value.slice(0, lineStart) + prefixed + value.slice(lineEnd)\n apply(next, lineStart, lineStart + prefixed.length)\n}\n\nfunction actionBold() {\n surround('**')\n}\n\nfunction actionItalic() {\n surround('*')\n}\n\nfunction actionHeading() {\n prefixLines('## ')\n}\n\nfunction actionList() {\n prefixLines('- ')\n}\n\nfunction actionTaskList() {\n prefixLines('- [ ] ')\n}\n\nfunction actionLink() {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const selected = value.slice(start, end) || 'texto'\n const next = `${value.slice(0, start)}[${selected}](url)${value.slice(end)}`\n // Deixa \"url\" selecionado, pronto para colar o endereço.\n const urlStart = start + selected.length + 3\n apply(next, urlStart, urlStart + 3)\n}\n\nfunction actionCode() {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const selected = (props.modelValue ?? '').slice(el.selectionStart, el.selectionEnd)\n if (selected.includes('\\n')) surround('```\\n', '\\n```', 'código')\n else surround('`', '`', 'código')\n}\n\n/**\n * Insere um bloco em linha própria. No modelo, `$` marca o trecho que fica\n * selecionado depois (ou onde entra o texto que já estava selecionado).\n */\nfunction inserirBloco(modelo: string, exemplo: string) {\n const el = textareaRef.value\n if (!el || props.disabled) return\n const value = props.modelValue ?? ''\n const start = el.selectionStart\n const end = el.selectionEnd\n const alvo = value.slice(start, end) || exemplo\n const corpo = modelo.replace('$', alvo)\n const antes = value.slice(0, start)\n // Bloco precisa de linha em branco antes para o markdown reconhecer.\n const respiro = !antes || antes.endsWith('\\n\\n') ? '' : antes.endsWith('\\n') ? '\\n' : '\\n\\n'\n const next = `${antes}${respiro}${corpo}\\n${value.slice(end)}`\n const posicao = antes.length + respiro.length + corpo.indexOf(alvo)\n apply(next, posicao, posicao + alvo.length)\n}\n\nfunction actionAlert() {\n inserirBloco('> [!DICA]\\n> $', 'Escreva aqui a sugestão')\n}\n\nfunction actionSteps() {\n inserirBloco('::: passos\\n1. $\\n2. Próximo passo\\n:::', 'Primeiro passo')\n}\n\nfunction actionTable() {\n inserirBloco('| Coluna | Coluna |\\n| --- | --- |\\n| $ | valor |', 'valor')\n}\n\nfunction actionHighlight() {\n surround('==', '==', 'destaque')\n}\n\nfunction togglePreview() {\n previewing.value = !previewing.value\n}\n\nconst ajudaAberta = ref(false)\n\n/** Cola rápida do vocabulário — o autor não precisa decorar a sintaxe. */\nconst ajuda: { sintaxe: string; efeito: string }[] = [\n { sintaxe: '## Título', efeito: 'Seção (entra no índice lateral)' },\n { sintaxe: '> [!DICA] Título', efeito: 'Destaque: dica, nota, importante, atenção, cuidado' },\n { sintaxe: '::: passos … :::', efeito: 'Procedimento numerado' },\n { sintaxe: '::: cards … :::', efeito: 'Lista em cartões' },\n { sintaxe: '::: aba Título', efeito: 'Conteúdo em abas (blocos seguidos)' },\n { sintaxe: '::: detalhes Título', efeito: 'Bloco que abre e fecha' },\n { sintaxe: '==destaque==', efeito: 'Texto marcado' },\n { sintaxe: '- [ ] tarefa', efeito: 'Checklist' },\n { sintaxe: '```python title=\"arquivo.py\"', efeito: 'Código com realce e cópia' },\n { sintaxe: '```mermaid', efeito: 'Diagrama de fluxo' },\n]\n\ninterface ToolDef {\n /** Ícone PrimeIcons OU glyph textual (PrimeIcons não tem bold/itálico). */\n icon?: string\n glyph?: string\n label: string\n run: () => void\n}\n\nconst tools: ToolDef[] = [\n { glyph: 'B', label: 'Negrito', run: actionBold },\n { glyph: 'I', label: 'Itálico', run: actionItalic },\n { icon: 'pi pi-hashtag', label: 'Título (H2)', run: actionHeading },\n { icon: 'pi pi-list', label: 'Lista', run: actionList },\n { icon: 'pi pi-check-square', label: 'Lista de tarefas', run: actionTaskList },\n { icon: 'pi pi-link', label: 'Link', run: actionLink },\n { icon: 'pi pi-code', label: 'Código', run: actionCode },\n { icon: 'pi pi-info-circle', label: 'Destaque', run: actionAlert },\n { icon: 'pi pi-sort-numeric-down', label: 'Passos', run: actionSteps },\n { icon: 'pi pi-table', label: 'Tabela', run: actionTable },\n { icon: 'pi pi-pencil', label: 'Marcar texto', run: actionHighlight },\n]\n</script>\n\n<template>\n <div class=\"w-markdown-editor\" :class=\"{ 'w-markdown-editor--split': isSplit }\">\n <div class=\"w-markdown-editor__toolbar\" role=\"toolbar\" aria-label=\"Formatação markdown\">\n <button\n v-for=\"tool in tools\"\n :key=\"tool.label\"\n type=\"button\"\n class=\"w-markdown-editor__tool\"\n :aria-label=\"tool.label\"\n :title=\"tool.label\"\n :disabled=\"disabled || (!isSplit && previewing)\"\n @click=\"tool.run()\"\n >\n <i v-if=\"tool.icon\" :class=\"tool.icon\" aria-hidden=\"true\" />\n <span\n v-else\n class=\"w-markdown-editor__glyph\"\n :class=\"{\n 'w-markdown-editor__glyph--bold': tool.glyph === 'B',\n 'w-markdown-editor__glyph--italic': tool.glyph === 'I',\n }\"\n aria-hidden=\"true\"\n >\n {{ tool.glyph }}\n </span>\n </button>\n\n <button\n type=\"button\"\n class=\"w-markdown-editor__tool w-markdown-editor__tool--ajuda\"\n :class=\"{ 'w-markdown-editor__tool--active': ajudaAberta }\"\n aria-label=\"Sintaxe disponível\"\n title=\"Sintaxe disponível\"\n :aria-pressed=\"ajudaAberta\"\n @click=\"ajudaAberta = !ajudaAberta\"\n >\n <i class=\"pi pi-question-circle\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"!isSplit\"\n type=\"button\"\n class=\"w-markdown-editor__tool w-markdown-editor__tool--preview\"\n :class=\"{ 'w-markdown-editor__tool--active': previewing }\"\n aria-label=\"Visualizar\"\n title=\"Visualizar\"\n :aria-pressed=\"previewing\"\n @click=\"togglePreview\"\n >\n <i :class=\"previewing ? 'pi pi-pencil' : 'pi pi-eye'\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <dl v-if=\"ajudaAberta\" class=\"w-markdown-editor__ajuda\">\n <template v-for=\"linha in ajuda\" :key=\"linha.sintaxe\">\n <dt>{{ linha.sintaxe }}</dt>\n <dd>{{ linha.efeito }}</dd>\n </template>\n </dl>\n\n <div class=\"w-markdown-editor__body\" :style=\"{ minHeight, maxHeight }\">\n <textarea\n v-if=\"showTextarea\"\n ref=\"textareaRef\"\n class=\"w-markdown-editor__textarea\"\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n spellcheck=\"false\"\n @input=\"onInput\"\n />\n <WMarkdownView\n v-if=\"showPreview\"\n class=\"w-markdown-editor__preview\"\n :source=\"modelValue\"\n empty-text=\"Nada para visualizar\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport type { MarkdownHeading } from '@/types/markdown'\n\n/**\n * Índice do documento (\"Nesta página\") com marcação da seção em leitura.\n *\n * Os títulos vêm do evento `headings` do WMarkdownView. Cada `##` vira um grupo\n * que **recolhe seus `###`** — assim o índice fica curto mesmo em documento\n * longo — e o grupo da seção que está sendo lida abre sozinho.\n *\n * A altura é presa à do contexto e a rolagem, quando sobra conteúdo, acontece\n * sem barra visível: o índice não pode competir com a rolagem da página.\n */\nconst props = withDefaults(\n defineProps<{\n /** Títulos do documento (evento `headings` do WMarkdownView). */\n headings: MarkdownHeading[]\n title?: string\n /** Abaixo disso o índice não aparece (documento curto não precisa). */\n minHeadings?: number\n /** Mostra o campo que filtra os títulos. */\n searchable?: boolean\n /** Teto da lista — o índice se ajusta à altura disponível. */\n maxHeight?: string\n }>(),\n {\n title: 'Nesta página',\n minHeadings: 2,\n searchable: false,\n maxHeight: 'calc(100vh - 14rem)',\n },\n)\n\ninterface Grupo {\n titulo: MarkdownHeading\n filhos: MarkdownHeading[]\n}\n\nconst ativo = ref('')\nconst termo = ref('')\n/**\n * Id do único `##` aberto — acordeão. Tudo começa fechado e nada abre sozinho:\n * abrir seção é decisão do leitor, não efeito colateral da rolagem.\n */\nconst aberto = ref<string | null>(null)\nlet observador: IntersectionObserver | null = null\n\n/** `##` viram grupos; `###` (e abaixo) entram como filhos do grupo anterior. */\nconst grupos = computed<Grupo[]>(() => {\n const saida: Grupo[] = []\n for (const titulo of props.headings) {\n if (titulo.level <= 2 || !saida.length) saida.push({ titulo, filhos: [] })\n else saida[saida.length - 1].filhos.push(titulo)\n }\n return saida\n})\n\nfunction normalizar(texto: string): string {\n return texto\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n}\n\n/** Com filtro, sobram os grupos que casam (ou que têm filho que casa). */\nconst visiveis = computed<Grupo[]>(() => {\n const busca = normalizar(termo.value.trim())\n if (!busca) return grupos.value\n return grupos.value\n .map((grupo) => ({\n titulo: grupo.titulo,\n filhos: grupo.filhos.filter((filho) => normalizar(filho.text).includes(busca)),\n }))\n .filter((grupo) => grupo.filhos.length || normalizar(grupo.titulo.text).includes(busca))\n})\n\nconst filtrando = computed(() => termo.value.trim().length > 0)\n\nfunction expandido(grupo: Grupo): boolean {\n return filtrando.value || aberto.value === grupo.titulo.id\n}\n\nfunction alternar(grupo: Grupo): void {\n aberto.value = aberto.value === grupo.titulo.id ? null : grupo.titulo.id\n}\n\nfunction observar(): void {\n observador?.disconnect()\n if (typeof IntersectionObserver === 'undefined') return\n observador = new IntersectionObserver(\n (entradas) => {\n const visivel = entradas.find((entrada) => entrada.isIntersecting)\n if (visivel) ativo.value = visivel.target.id\n },\n // Faixa de leitura: o título \"ativo\" é o que está no topo da tela.\n { rootMargin: '-88px 0px -70% 0px', threshold: 0 },\n )\n props.headings.forEach((titulo) => {\n const elemento = document.getElementById(titulo.id)\n if (elemento) observador?.observe(elemento)\n })\n}\n\nfunction ir(id: string): void {\n const elemento = document.getElementById(id)\n if (!elemento) return\n ativo.value = id\n elemento.scrollIntoView({ behavior: 'smooth', block: 'start' })\n}\n\nwatch(\n () => props.headings,\n () => void nextTick(observar),\n { immediate: true },\n)\n\nonBeforeUnmount(() => observador?.disconnect())\n</script>\n\n<template>\n <nav v-if=\"headings.length >= minHeadings\" class=\"w-md-toc\" aria-label=\"Índice do documento\">\n <p class=\"w-md-toc__titulo\">{{ title }}</p>\n\n <div v-if=\"searchable\" class=\"w-md-toc__busca\">\n <i class=\"pi pi-search\" aria-hidden=\"true\" />\n <input\n v-model=\"termo\"\n type=\"search\"\n placeholder=\"Filtrar seções\"\n aria-label=\"Filtrar seções\"\n />\n </div>\n\n <div class=\"w-md-toc__corpo\" :style=\"{ maxHeight }\">\n <ul class=\"w-md-toc__lista\">\n <li v-for=\"grupo in visiveis\" :key=\"grupo.titulo.id\" class=\"w-md-toc__grupo\">\n <div\n class=\"w-md-toc__item w-md-toc__item--n2\"\n :class=\"{ 'w-md-toc__item--ativo': ativo === grupo.titulo.id }\"\n >\n <button\n v-if=\"grupo.filhos.length\"\n type=\"button\"\n class=\"w-md-toc__chevron\"\n :aria-expanded=\"expandido(grupo)\"\n :aria-label=\"`Mostrar seções de ${grupo.titulo.text}`\"\n @click=\"alternar(grupo)\"\n >\n <i\n :class=\"expandido(grupo) ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\"\n aria-hidden=\"true\"\n />\n </button>\n <span v-else class=\"w-md-toc__chevron w-md-toc__chevron--vazio\" aria-hidden=\"true\" />\n <a :href=\"`#${grupo.titulo.id}`\" @click.prevent=\"ir(grupo.titulo.id)\">\n {{ grupo.titulo.text }}\n </a>\n </div>\n\n <ul v-if=\"expandido(grupo)\" class=\"w-md-toc__lista\">\n <li\n v-for=\"filho in grupo.filhos\"\n :key=\"filho.id\"\n class=\"w-md-toc__item w-md-toc__item--n3\"\n :class=\"{ 'w-md-toc__item--ativo': ativo === filho.id }\"\n >\n <a :href=\"`#${filho.id}`\" @click.prevent=\"ir(filho.id)\">{{ filho.text }}</a>\n </li>\n </ul>\n </li>\n </ul>\n\n <p v-if=\"filtrando && !visiveis.length\" class=\"w-md-toc__vazio\">Nenhuma seção encontrada</p>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'\nimport type { MarkdownHeading } from '@/types/markdown'\n\n/**\n * Índice do documento (\"Nesta página\") com marcação da seção em leitura.\n *\n * Os títulos vêm do evento `headings` do WMarkdownView. Cada `##` vira um grupo\n * que **recolhe seus `###`** — assim o índice fica curto mesmo em documento\n * longo — e o grupo da seção que está sendo lida abre sozinho.\n *\n * A altura é presa à do contexto e a rolagem, quando sobra conteúdo, acontece\n * sem barra visível: o índice não pode competir com a rolagem da página.\n */\nconst props = withDefaults(\n defineProps<{\n /** Títulos do documento (evento `headings` do WMarkdownView). */\n headings: MarkdownHeading[]\n title?: string\n /** Abaixo disso o índice não aparece (documento curto não precisa). */\n minHeadings?: number\n /** Mostra o campo que filtra os títulos. */\n searchable?: boolean\n /** Teto da lista — o índice se ajusta à altura disponível. */\n maxHeight?: string\n }>(),\n {\n title: 'Nesta página',\n minHeadings: 2,\n searchable: false,\n maxHeight: 'calc(100vh - 14rem)',\n },\n)\n\ninterface Grupo {\n titulo: MarkdownHeading\n filhos: MarkdownHeading[]\n}\n\nconst ativo = ref('')\nconst termo = ref('')\n/**\n * Id do único `##` aberto — acordeão. Tudo começa fechado e nada abre sozinho:\n * abrir seção é decisão do leitor, não efeito colateral da rolagem.\n */\nconst aberto = ref<string | null>(null)\nlet observador: IntersectionObserver | null = null\n\n/** `##` viram grupos; `###` (e abaixo) entram como filhos do grupo anterior. */\nconst grupos = computed<Grupo[]>(() => {\n const saida: Grupo[] = []\n for (const titulo of props.headings) {\n if (titulo.level <= 2 || !saida.length) saida.push({ titulo, filhos: [] })\n else saida[saida.length - 1].filhos.push(titulo)\n }\n return saida\n})\n\nfunction normalizar(texto: string): string {\n return texto\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n}\n\n/** Com filtro, sobram os grupos que casam (ou que têm filho que casa). */\nconst visiveis = computed<Grupo[]>(() => {\n const busca = normalizar(termo.value.trim())\n if (!busca) return grupos.value\n return grupos.value\n .map((grupo) => ({\n titulo: grupo.titulo,\n filhos: grupo.filhos.filter((filho) => normalizar(filho.text).includes(busca)),\n }))\n .filter((grupo) => grupo.filhos.length || normalizar(grupo.titulo.text).includes(busca))\n})\n\nconst filtrando = computed(() => termo.value.trim().length > 0)\n\nfunction expandido(grupo: Grupo): boolean {\n return filtrando.value || aberto.value === grupo.titulo.id\n}\n\nfunction alternar(grupo: Grupo): void {\n aberto.value = aberto.value === grupo.titulo.id ? null : grupo.titulo.id\n}\n\nfunction observar(): void {\n observador?.disconnect()\n if (typeof IntersectionObserver === 'undefined') return\n observador = new IntersectionObserver(\n (entradas) => {\n const visivel = entradas.find((entrada) => entrada.isIntersecting)\n if (visivel) ativo.value = visivel.target.id\n },\n // Faixa de leitura: o título \"ativo\" é o que está no topo da tela.\n { rootMargin: '-88px 0px -70% 0px', threshold: 0 },\n )\n props.headings.forEach((titulo) => {\n const elemento = document.getElementById(titulo.id)\n if (elemento) observador?.observe(elemento)\n })\n}\n\nfunction ir(id: string): void {\n const elemento = document.getElementById(id)\n if (!elemento) return\n ativo.value = id\n elemento.scrollIntoView({ behavior: 'smooth', block: 'start' })\n}\n\nwatch(\n () => props.headings,\n () => void nextTick(observar),\n { immediate: true },\n)\n\nonBeforeUnmount(() => observador?.disconnect())\n</script>\n\n<template>\n <nav v-if=\"headings.length >= minHeadings\" class=\"w-md-toc\" aria-label=\"Índice do documento\">\n <p class=\"w-md-toc__titulo\">{{ title }}</p>\n\n <div v-if=\"searchable\" class=\"w-md-toc__busca\">\n <i class=\"pi pi-search\" aria-hidden=\"true\" />\n <input\n v-model=\"termo\"\n type=\"search\"\n placeholder=\"Filtrar seções\"\n aria-label=\"Filtrar seções\"\n />\n </div>\n\n <div class=\"w-md-toc__corpo\" :style=\"{ maxHeight }\">\n <ul class=\"w-md-toc__lista\">\n <li v-for=\"grupo in visiveis\" :key=\"grupo.titulo.id\" class=\"w-md-toc__grupo\">\n <div\n class=\"w-md-toc__item w-md-toc__item--n2\"\n :class=\"{ 'w-md-toc__item--ativo': ativo === grupo.titulo.id }\"\n >\n <button\n v-if=\"grupo.filhos.length\"\n type=\"button\"\n class=\"w-md-toc__chevron\"\n :aria-expanded=\"expandido(grupo)\"\n :aria-label=\"`Mostrar seções de ${grupo.titulo.text}`\"\n @click=\"alternar(grupo)\"\n >\n <i\n :class=\"expandido(grupo) ? 'pi pi-chevron-down' : 'pi pi-chevron-right'\"\n aria-hidden=\"true\"\n />\n </button>\n <span v-else class=\"w-md-toc__chevron w-md-toc__chevron--vazio\" aria-hidden=\"true\" />\n <a :href=\"`#${grupo.titulo.id}`\" @click.prevent=\"ir(grupo.titulo.id)\">\n {{ grupo.titulo.text }}\n </a>\n </div>\n\n <ul v-if=\"expandido(grupo)\" class=\"w-md-toc__lista\">\n <li\n v-for=\"filho in grupo.filhos\"\n :key=\"filho.id\"\n class=\"w-md-toc__item w-md-toc__item--n3\"\n :class=\"{ 'w-md-toc__item--ativo': ativo === filho.id }\"\n >\n <a :href=\"`#${filho.id}`\" @click.prevent=\"ir(filho.id)\">{{ filho.text }}</a>\n </li>\n </ul>\n </li>\n </ul>\n\n <p v-if=\"filtrando && !visiveis.length\" class=\"w-md-toc__vazio\">Nenhuma seção encontrada</p>\n </div>\n </nav>\n</template>\n","<script setup lang=\"ts\">\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\n\n/**\n * `Dialog` do PrimeVue com a âncora resolvida pela navegação por abas: dentro\n * de uma aba, pendura no pane dela — some e volta com a aba (v-show, sem\n * fechar) e morre junto quando a aba fecha ou a tela troca. Fora de abas, o\n * `body` de sempre. Passthrough total: props/eventos do Dialog via attrs\n * (incluindo `v-model:visible`) e todos os slots repassados; um `append-to`\n * explícito do consumidor vence o default.\n */\ndefineOptions({ name: 'WDialog', inheritAttrs: false })\n\nconst tabHost = useTabHost()\n</script>\n\n<template>\n <Dialog :append-to=\"tabHost?.hostEl.value ?? 'body'\" v-bind=\"$attrs\">\n <template v-for=\"(_, name) in $slots\" :key=\"name\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </Dialog>\n</template>\n","<script setup lang=\"ts\">\nimport Dialog from 'primevue/dialog'\nimport { useTabHost } from '@/types/routeTabs'\n\n/**\n * `Dialog` do PrimeVue com a âncora resolvida pela navegação por abas: dentro\n * de uma aba, pendura no pane dela — some e volta com a aba (v-show, sem\n * fechar) e morre junto quando a aba fecha ou a tela troca. Fora de abas, o\n * `body` de sempre. Passthrough total: props/eventos do Dialog via attrs\n * (incluindo `v-model:visible`) e todos os slots repassados; um `append-to`\n * explícito do consumidor vence o default.\n */\ndefineOptions({ name: 'WDialog', inheritAttrs: false })\n\nconst tabHost = useTabHost()\n</script>\n\n<template>\n <Dialog :append-to=\"tabHost?.hostEl.value ?? 'body'\" v-bind=\"$attrs\">\n <template v-for=\"(_, name) in $slots\" :key=\"name\" #[name]=\"slotProps\">\n <slot :name=\"name\" v-bind=\"slotProps ?? {}\" />\n </template>\n </Dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { TabItem } from '@/types/tabs'\n\n/**\n * Barra de abas de TELA (não confundir com as abas dentro de um markdown).\n *\n * Controlada: só emite `update:modelValue` — quem decide o que renderizar é a\n * página. Navegação por teclado com ←/→ (padrão ARIA de tablist).\n */\ndefineProps<{\n /** `value` da aba ativa. */\n modelValue: string\n items: TabItem[]\n /** Rótulo do grupo para leitores de tela. */\n ariaLabel?: string\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst botoes = ref<HTMLButtonElement[]>([])\n\nfunction registrar(el: unknown, indice: number): void {\n if (el instanceof HTMLButtonElement) botoes.value[indice] = el\n}\n\nfunction selecionar(item: TabItem): void {\n if (!item.disabled) emit('update:modelValue', item.value)\n}\n\n/** ←/→ andam entre as abas habilitadas, circulando nas pontas. */\nfunction navegar(items: TabItem[], indice: number, passo: number): void {\n const total = items.length\n for (let salto = 1; salto <= total; salto += 1) {\n const alvo = (indice + passo * salto + total * total) % total\n if (!items[alvo].disabled) {\n emit('update:modelValue', items[alvo].value)\n botoes.value[alvo]?.focus()\n return\n }\n }\n}\n</script>\n\n<template>\n <div class=\"w-tab-bar\" role=\"tablist\" :aria-label=\"ariaLabel\">\n <button\n v-for=\"(item, indice) in items\"\n :key=\"item.value\"\n :ref=\"(el) => registrar(el, indice)\"\n type=\"button\"\n role=\"tab\"\n class=\"w-tab-bar__item\"\n :class=\"{ 'w-tab-bar__item--ativa': item.value === modelValue }\"\n :aria-selected=\"item.value === modelValue\"\n :tabindex=\"item.value === modelValue ? 0 : -1\"\n :disabled=\"item.disabled\"\n @click=\"selecionar(item)\"\n @keydown.right.prevent=\"navegar(items, indice, 1)\"\n @keydown.left.prevent=\"navegar(items, indice, -1)\"\n >\n <i v-if=\"item.icon\" :class=\"item.icon\" aria-hidden=\"true\" />\n <span>{{ item.label }}</span>\n <span v-if=\"item.badge !== undefined && item.badge !== null\" class=\"w-tab-bar__badge\">\n {{ item.badge }}\n </span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { TabItem } from '@/types/tabs'\n\n/**\n * Barra de abas de TELA (não confundir com as abas dentro de um markdown).\n *\n * Controlada: só emite `update:modelValue` — quem decide o que renderizar é a\n * página. Navegação por teclado com ←/→ (padrão ARIA de tablist).\n */\ndefineProps<{\n /** `value` da aba ativa. */\n modelValue: string\n items: TabItem[]\n /** Rótulo do grupo para leitores de tela. */\n ariaLabel?: string\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst botoes = ref<HTMLButtonElement[]>([])\n\nfunction registrar(el: unknown, indice: number): void {\n if (el instanceof HTMLButtonElement) botoes.value[indice] = el\n}\n\nfunction selecionar(item: TabItem): void {\n if (!item.disabled) emit('update:modelValue', item.value)\n}\n\n/** ←/→ andam entre as abas habilitadas, circulando nas pontas. */\nfunction navegar(items: TabItem[], indice: number, passo: number): void {\n const total = items.length\n for (let salto = 1; salto <= total; salto += 1) {\n const alvo = (indice + passo * salto + total * total) % total\n if (!items[alvo].disabled) {\n emit('update:modelValue', items[alvo].value)\n botoes.value[alvo]?.focus()\n return\n }\n }\n}\n</script>\n\n<template>\n <div class=\"w-tab-bar\" role=\"tablist\" :aria-label=\"ariaLabel\">\n <button\n v-for=\"(item, indice) in items\"\n :key=\"item.value\"\n :ref=\"(el) => registrar(el, indice)\"\n type=\"button\"\n role=\"tab\"\n class=\"w-tab-bar__item\"\n :class=\"{ 'w-tab-bar__item--ativa': item.value === modelValue }\"\n :aria-selected=\"item.value === modelValue\"\n :tabindex=\"item.value === modelValue ? 0 : -1\"\n :disabled=\"item.disabled\"\n @click=\"selecionar(item)\"\n @keydown.right.prevent=\"navegar(items, indice, 1)\"\n @keydown.left.prevent=\"navegar(items, indice, -1)\"\n >\n <i v-if=\"item.icon\" :class=\"item.icon\" aria-hidden=\"true\" />\n <span>{{ item.label }}</span>\n <span v-if=\"item.badge !== undefined && item.badge !== null\" class=\"w-tab-bar__badge\">\n {{ item.badge }}\n </span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\nimport ContextMenu from 'primevue/contextmenu'\nimport type { MenuItem } from 'primevue/menuitem'\nimport type { RouteTab, RouteTabsApi } from '@/types/routeTabs'\n\n/**\n * Barra da navegação por abas (estilo browser/ERP) — o par do WTabViewport.\n * Não confundir com o WTabBar (abas de seção DENTRO de uma tela): esta barra\n * lista rotas abertas, fecha (X, botão do meio) e oferece menu de contexto\n * com recarregar/fechar outras/fechar todas. Overflow rola na horizontal e a\n * aba ativa se mantém à vista. Todo o estado vem do `useRouteTabs` via prop.\n */\nconst props = defineProps<{\n /** Instância retornada por `useRouteTabs` — o dono do estado. */\n tabs: RouteTabsApi\n ariaLabel?: string\n}>()\n\n/** Espelho informativo das ações — o default já chama a api. */\nconst emit = defineEmits<{\n activate: [tab: RouteTab]\n close: [tab: RouteTab]\n reload: [tab: RouteTab]\n}>()\n\nconst items = computed(() => props.tabs.tabs.value)\nconst activeKey = computed(() => props.tabs.activeKey.value)\n\nconst botoes = ref<HTMLElement[]>([])\nconst trilho = ref<HTMLElement | null>(null)\n\nfunction registrar(el: unknown, indice: number): void {\n if (el instanceof HTMLElement) botoes.value[indice] = el\n}\n\n// A aba ativa sempre à vista — inclusive quando ativada por navegação\n// externa (sidebar, palette), não só por clique na barra.\nwatch(activeKey, async () => {\n await nextTick()\n const indice = items.value.findIndex((t) => t.key === activeKey.value)\n botoes.value[indice]?.scrollIntoView?.({ inline: 'nearest', block: 'nearest' })\n})\n\nfunction ativar(tab: RouteTab): void {\n emit('activate', tab)\n void props.tabs.activate(tab.key)\n}\n\nfunction fechar(tab: RouteTab): void {\n if (!tab.closable) return\n emit('close', tab)\n void props.tabs.close(tab.key)\n}\n\n/** ←/→ andam entre as abas, circulando nas pontas (padrão ARIA de tablist). */\nfunction navegar(indice: number, passo: number): void {\n const total = items.value.length\n if (!total) return\n const alvo = (indice + passo + total) % total\n ativar(items.value[alvo])\n botoes.value[alvo]?.focus()\n}\n\n// Menu de contexto: mesmo padrão do WCrudView (um ContextMenu, alvo corrente).\nconst menu = ref<InstanceType<typeof ContextMenu> | null>(null)\nconst alvoMenu = ref<RouteTab | null>(null)\n\nconst itensMenu = computed<MenuItem[]>(() => {\n const alvo = alvoMenu.value\n if (!alvo) return []\n return [\n {\n label: 'Recarregar',\n icon: 'pi pi-refresh',\n disabled: !alvo.hydrated,\n command: () => {\n emit('reload', alvo)\n props.tabs.reload(alvo.key)\n },\n },\n { separator: true },\n {\n label: 'Fechar',\n icon: 'pi pi-times',\n disabled: !alvo.closable,\n command: () => fechar(alvo),\n },\n {\n label: 'Fechar outras',\n icon: 'pi pi-times-circle',\n disabled: items.value.length <= 1,\n command: () => void props.tabs.closeOthers(alvo.key),\n },\n {\n label: 'Fechar todas',\n icon: 'pi pi-ban',\n command: () => void props.tabs.closeAll(),\n },\n ]\n})\n\nfunction abrirMenu(event: MouseEvent, tab: RouteTab): void {\n alvoMenu.value = tab\n menu.value?.show(event)\n}\n\n/** Rótulo do grupo antes do primeiro item de cada bloco (abas já vêm contíguas). */\nfunction inicioDeGrupo(indice: number): boolean {\n const grupo = items.value[indice]?.group\n return Boolean(grupo) && grupo !== items.value[indice - 1]?.group\n}\n</script>\n\n<template>\n <div class=\"w-tab-nav\">\n <div\n ref=\"trilho\"\n class=\"w-tab-nav__list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel ?? 'Abas abertas'\"\n >\n <template v-for=\"(tab, indice) in items\" :key=\"tab.key\">\n <span v-if=\"inicioDeGrupo(indice)\" class=\"w-tab-nav__group\" aria-hidden=\"true\">\n {{ tab.group }}\n </span>\n <div\n :ref=\"(el) => registrar(el, indice)\"\n role=\"tab\"\n :tabindex=\"tab.key === activeKey ? 0 : -1\"\n class=\"w-tab-nav__item\"\n :class=\"{ 'w-tab-nav__item--ativa': tab.key === activeKey }\"\n :aria-selected=\"tab.key === activeKey\"\n @click=\"ativar(tab)\"\n @auxclick.middle.prevent=\"fechar(tab)\"\n @contextmenu.prevent=\"abrirMenu($event, tab)\"\n @keydown.right.prevent=\"navegar(indice, 1)\"\n @keydown.left.prevent=\"navegar(indice, -1)\"\n @keydown.enter.prevent=\"ativar(tab)\"\n >\n <slot name=\"tab\" :tab=\"tab\" :active=\"tab.key === activeKey\">\n <i v-if=\"tab.icon\" :class=\"tab.icon\" aria-hidden=\"true\" />\n <span class=\"w-tab-nav__title\" :title=\"tab.title\">{{ tab.title }}</span>\n </slot>\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"w-tab-nav__close\"\n :aria-label=\"`Fechar ${tab.title}`\"\n tabindex=\"-1\"\n @click.stop=\"fechar(tab)\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\" />\n </button>\n </div>\n </template>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-tab-nav__actions\">\n <slot name=\"actions\" />\n </div>\n <ContextMenu ref=\"menu\" :model=\"itensMenu\" />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\nimport ContextMenu from 'primevue/contextmenu'\nimport type { MenuItem } from 'primevue/menuitem'\nimport type { RouteTab, RouteTabsApi } from '@/types/routeTabs'\n\n/**\n * Barra da navegação por abas (estilo browser/ERP) — o par do WTabViewport.\n * Não confundir com o WTabBar (abas de seção DENTRO de uma tela): esta barra\n * lista rotas abertas, fecha (X, botão do meio) e oferece menu de contexto\n * com recarregar/fechar outras/fechar todas. Overflow rola na horizontal e a\n * aba ativa se mantém à vista. Todo o estado vem do `useRouteTabs` via prop.\n */\nconst props = defineProps<{\n /** Instância retornada por `useRouteTabs` — o dono do estado. */\n tabs: RouteTabsApi\n ariaLabel?: string\n}>()\n\n/** Espelho informativo das ações — o default já chama a api. */\nconst emit = defineEmits<{\n activate: [tab: RouteTab]\n close: [tab: RouteTab]\n reload: [tab: RouteTab]\n}>()\n\nconst items = computed(() => props.tabs.tabs.value)\nconst activeKey = computed(() => props.tabs.activeKey.value)\n\nconst botoes = ref<HTMLElement[]>([])\nconst trilho = ref<HTMLElement | null>(null)\n\nfunction registrar(el: unknown, indice: number): void {\n if (el instanceof HTMLElement) botoes.value[indice] = el\n}\n\n// A aba ativa sempre à vista — inclusive quando ativada por navegação\n// externa (sidebar, palette), não só por clique na barra.\nwatch(activeKey, async () => {\n await nextTick()\n const indice = items.value.findIndex((t) => t.key === activeKey.value)\n botoes.value[indice]?.scrollIntoView?.({ inline: 'nearest', block: 'nearest' })\n})\n\nfunction ativar(tab: RouteTab): void {\n emit('activate', tab)\n void props.tabs.activate(tab.key)\n}\n\nfunction fechar(tab: RouteTab): void {\n if (!tab.closable) return\n emit('close', tab)\n void props.tabs.close(tab.key)\n}\n\n/** ←/→ andam entre as abas, circulando nas pontas (padrão ARIA de tablist). */\nfunction navegar(indice: number, passo: number): void {\n const total = items.value.length\n if (!total) return\n const alvo = (indice + passo + total) % total\n ativar(items.value[alvo])\n botoes.value[alvo]?.focus()\n}\n\n// Menu de contexto: mesmo padrão do WCrudView (um ContextMenu, alvo corrente).\nconst menu = ref<InstanceType<typeof ContextMenu> | null>(null)\nconst alvoMenu = ref<RouteTab | null>(null)\n\nconst itensMenu = computed<MenuItem[]>(() => {\n const alvo = alvoMenu.value\n if (!alvo) return []\n return [\n {\n label: 'Recarregar',\n icon: 'pi pi-refresh',\n disabled: !alvo.hydrated,\n command: () => {\n emit('reload', alvo)\n props.tabs.reload(alvo.key)\n },\n },\n { separator: true },\n {\n label: 'Fechar',\n icon: 'pi pi-times',\n disabled: !alvo.closable,\n command: () => fechar(alvo),\n },\n {\n label: 'Fechar outras',\n icon: 'pi pi-times-circle',\n disabled: items.value.length <= 1,\n command: () => void props.tabs.closeOthers(alvo.key),\n },\n {\n label: 'Fechar todas',\n icon: 'pi pi-ban',\n command: () => void props.tabs.closeAll(),\n },\n ]\n})\n\nfunction abrirMenu(event: MouseEvent, tab: RouteTab): void {\n alvoMenu.value = tab\n menu.value?.show(event)\n}\n\n/** Rótulo do grupo antes do primeiro item de cada bloco (abas já vêm contíguas). */\nfunction inicioDeGrupo(indice: number): boolean {\n const grupo = items.value[indice]?.group\n return Boolean(grupo) && grupo !== items.value[indice - 1]?.group\n}\n</script>\n\n<template>\n <div class=\"w-tab-nav\">\n <div\n ref=\"trilho\"\n class=\"w-tab-nav__list\"\n role=\"tablist\"\n :aria-label=\"ariaLabel ?? 'Abas abertas'\"\n >\n <template v-for=\"(tab, indice) in items\" :key=\"tab.key\">\n <span v-if=\"inicioDeGrupo(indice)\" class=\"w-tab-nav__group\" aria-hidden=\"true\">\n {{ tab.group }}\n </span>\n <div\n :ref=\"(el) => registrar(el, indice)\"\n role=\"tab\"\n :tabindex=\"tab.key === activeKey ? 0 : -1\"\n class=\"w-tab-nav__item\"\n :class=\"{ 'w-tab-nav__item--ativa': tab.key === activeKey }\"\n :aria-selected=\"tab.key === activeKey\"\n @click=\"ativar(tab)\"\n @auxclick.middle.prevent=\"fechar(tab)\"\n @contextmenu.prevent=\"abrirMenu($event, tab)\"\n @keydown.right.prevent=\"navegar(indice, 1)\"\n @keydown.left.prevent=\"navegar(indice, -1)\"\n @keydown.enter.prevent=\"ativar(tab)\"\n >\n <slot name=\"tab\" :tab=\"tab\" :active=\"tab.key === activeKey\">\n <i v-if=\"tab.icon\" :class=\"tab.icon\" aria-hidden=\"true\" />\n <span class=\"w-tab-nav__title\" :title=\"tab.title\">{{ tab.title }}</span>\n </slot>\n <button\n v-if=\"tab.closable\"\n type=\"button\"\n class=\"w-tab-nav__close\"\n :aria-label=\"`Fechar ${tab.title}`\"\n tabindex=\"-1\"\n @click.stop=\"fechar(tab)\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\" />\n </button>\n </div>\n </template>\n </div>\n <div v-if=\"$slots.actions\" class=\"w-tab-nav__actions\">\n <slot name=\"actions\" />\n </div>\n <ContextMenu ref=\"menu\" :model=\"itensMenu\" />\n </div>\n</template>\n","import {\n defineComponent,\n h,\n onScopeDispose,\n provide,\n toRef,\n watch,\n type PropType,\n type Ref,\n} from 'vue'\nimport { routeLocationKey } from 'vue-router'\nimport {\n W_TAB_HOST_KEY,\n type RouteTab,\n type RouteTabsApi,\n type TabHostContext,\n} from '@/types/routeTabs'\n\n/**\n * Pane de uma aba — interno do WTabViewport, não exportado no barrel.\n *\n * Faz os dois provides que dão à tela a ilusão de viver sozinha:\n * - `routeLocationKey` (a MESMA chave que o vue-router usa): o `useRoute()` da\n * tela passa a injetar o snapshot congelado da aba, então um pane oculto\n * nunca reage à navegação global.\n * - `W_TAB_HOST_KEY`: contexto para a tela pendurar dialogs no container da\n * aba (`hostEl`), definir o título dinâmico e registrar hooks/guards.\n *\n * Os registros (guards/hooks) são desfeitos via `onScopeDispose` no escopo de\n * QUEM registrou — assim um `reload()` (remount) não acumula guard morto.\n */\nexport default defineComponent({\n name: 'WTabPane',\n props: {\n tab: { type: Object as PropType<RouteTab>, required: true },\n api: { type: Object as PropType<RouteTabsApi>, required: true },\n active: { type: Boolean, required: true },\n hostEl: { type: Object as PropType<Ref<HTMLElement | null>>, required: true },\n },\n setup(props) {\n const runtime = props.api.runtime(props.tab.key)\n if (!runtime) return () => null\n\n provide(routeLocationKey, runtime.route)\n\n function register(list: Array<() => unknown>, fn: () => unknown): void {\n list.push(fn)\n onScopeDispose(() => {\n const index = list.indexOf(fn)\n if (index !== -1) list.splice(index, 1)\n })\n }\n\n const host: TabHostContext = {\n hostEl: props.hostEl,\n active: toRef(props, 'active'),\n setTitle: (title) => props.api.setTitle(props.tab.key, title),\n onTabActivated: (fn) => register(runtime.activatedHooks, fn),\n onTabDeactivated: (fn) => register(runtime.deactivatedHooks, fn),\n registerCloseGuard: (guard) => register(runtime.closeGuards, guard),\n }\n provide(W_TAB_HOST_KEY, host)\n\n watch(\n () => props.active,\n (active) => {\n const hooks = active ? runtime.activatedHooks : runtime.deactivatedHooks\n for (const fn of [...hooks]) fn()\n },\n )\n\n return () =>\n runtime.component ? h(runtime.component, { ...runtime.props, key: props.tab.remount }) : null\n },\n})\n","<script setup lang=\"ts\">\nimport { computed, shallowRef, watch, type ShallowRef } from 'vue'\nimport type { RouteTab, RouteTabsApi } from '@/types/routeTabs'\nimport WTabPane from './WTabPane'\n\n/**\n * Painéis da navegação por abas: mantém TODA aba hidratada montada num\n * wrapper `v-show` (não `<KeepAlive>` — o DOM nunca sai da árvore, então um\n * dialog com `appendTo` no pane sobrevive intacto à troca de aba, e fechar\n * aba é só desmontar o pane). Substitui o `<RouterView>` do layout: o\n * componente da rota vem já resolvido do `useRouteTabs`.\n */\nconst props = defineProps<{\n /** Instância retornada por `useRouteTabs` — o dono do estado. */\n tabs: RouteTabsApi\n}>()\n\nconst activeKey = computed(() => props.tabs.activeKey.value)\nconst liveTabs = computed(() => props.tabs.tabs.value.filter((t) => t.hydrated))\n\n// Um ref de elemento por aba, entregue ao pane como alvo de appendTo.\nconst hosts = new Map<string, ShallowRef<HTMLElement | null>>()\n\nfunction hostRef(key: string): ShallowRef<HTMLElement | null> {\n let host = hosts.get(key)\n if (!host) {\n host = shallowRef<HTMLElement | null>(null)\n hosts.set(key, host)\n }\n return host\n}\n\nfunction setHost(key: string, el: Element | null): void {\n const host = hostRef(key)\n if (el instanceof HTMLElement) {\n host.value = el\n return\n }\n // `null` vem do unmount do pane. Na troca de tela dentro da aba o pane é\n // SUBSTITUÍDO (chave nova, mesma key de aba) e a ordem mount/unmount não é\n // garantida — só limpa se o elemento atual realmente saiu do documento.\n if (host.value && !host.value.isConnected) host.value = null\n}\n\nwatch(liveTabs, (tabs: RouteTab[]) => {\n const alive = new Set(tabs.map((t) => t.key))\n for (const key of hosts.keys()) {\n if (!alive.has(key)) hosts.delete(key)\n }\n})\n</script>\n\n<template>\n <div class=\"w-tab-viewport\">\n <!-- `remount` na chave: trocar de tela dentro da aba (modo 'module') e\n `reload()` substituem o pane INTEIRO — a tela que sai morre com o\n runtime dela, sem patch de componente dentro de um pane vivo (que\n quebra com dialogs/menus teleportados na âncora). -->\n <div\n v-for=\"tab in liveTabs\"\n v-show=\"tab.key === activeKey\"\n :key=\"`${tab.key}#${tab.remount}`\"\n :ref=\"(el) => setHost(tab.key, el as Element | null)\"\n class=\"w-tab-viewport__pane\"\n >\n <!-- Wrapper de scroll: o pane fica parado (âncora dos overlays com\n appendTo local); só o conteúdo da tela rola. -->\n <div class=\"w-tab-viewport__scroll\">\n <WTabPane\n :tab=\"tab\"\n :api=\"tabs\"\n :active=\"tab.key === activeKey\"\n :host-el=\"hostRef(tab.key)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, shallowRef, watch, type ShallowRef } from 'vue'\nimport type { RouteTab, RouteTabsApi } from '@/types/routeTabs'\nimport WTabPane from './WTabPane'\n\n/**\n * Painéis da navegação por abas: mantém TODA aba hidratada montada num\n * wrapper `v-show` (não `<KeepAlive>` — o DOM nunca sai da árvore, então um\n * dialog com `appendTo` no pane sobrevive intacto à troca de aba, e fechar\n * aba é só desmontar o pane). Substitui o `<RouterView>` do layout: o\n * componente da rota vem já resolvido do `useRouteTabs`.\n */\nconst props = defineProps<{\n /** Instância retornada por `useRouteTabs` — o dono do estado. */\n tabs: RouteTabsApi\n}>()\n\nconst activeKey = computed(() => props.tabs.activeKey.value)\nconst liveTabs = computed(() => props.tabs.tabs.value.filter((t) => t.hydrated))\n\n// Um ref de elemento por aba, entregue ao pane como alvo de appendTo.\nconst hosts = new Map<string, ShallowRef<HTMLElement | null>>()\n\nfunction hostRef(key: string): ShallowRef<HTMLElement | null> {\n let host = hosts.get(key)\n if (!host) {\n host = shallowRef<HTMLElement | null>(null)\n hosts.set(key, host)\n }\n return host\n}\n\nfunction setHost(key: string, el: Element | null): void {\n const host = hostRef(key)\n if (el instanceof HTMLElement) {\n host.value = el\n return\n }\n // `null` vem do unmount do pane. Na troca de tela dentro da aba o pane é\n // SUBSTITUÍDO (chave nova, mesma key de aba) e a ordem mount/unmount não é\n // garantida — só limpa se o elemento atual realmente saiu do documento.\n if (host.value && !host.value.isConnected) host.value = null\n}\n\nwatch(liveTabs, (tabs: RouteTab[]) => {\n const alive = new Set(tabs.map((t) => t.key))\n for (const key of hosts.keys()) {\n if (!alive.has(key)) hosts.delete(key)\n }\n})\n</script>\n\n<template>\n <div class=\"w-tab-viewport\">\n <!-- `remount` na chave: trocar de tela dentro da aba (modo 'module') e\n `reload()` substituem o pane INTEIRO — a tela que sai morre com o\n runtime dela, sem patch de componente dentro de um pane vivo (que\n quebra com dialogs/menus teleportados na âncora). -->\n <div\n v-for=\"tab in liveTabs\"\n v-show=\"tab.key === activeKey\"\n :key=\"`${tab.key}#${tab.remount}`\"\n :ref=\"(el) => setHost(tab.key, el as Element | null)\"\n class=\"w-tab-viewport__pane\"\n >\n <!-- Wrapper de scroll: o pane fica parado (âncora dos overlays com\n appendTo local); só o conteúdo da tela rola. -->\n <div class=\"w-tab-viewport__scroll\">\n <WTabPane\n :tab=\"tab\"\n :api=\"tabs\"\n :active=\"tab.key === activeKey\"\n :host-el=\"hostRef(tab.key)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { KanbanColumn, KanbanMoveEvent } from '@/types/kanban'\n\ntype Item = Record<string, unknown>\n\n/**\n * Board kanban controlado: as colunas e seus itens vêm de fora e o board NUNCA\n * muta os arrays — arrastar um card só emite `move`; o consumidor persiste e\n * atualiza `columns`. Drag and drop com HTML5 nativo (sem dependência).\n *\n * Mover programaticamente (ex.: botão \"mover para...\") é responsabilidade do\n * consumidor — basta aplicar a mesma mudança que faria ao receber um `move`.\n */\nconst props = withDefaults(\n defineProps<{\n /** Colunas na ordem de exibição. Itens pertencem ao consumidor. */\n columns: KanbanColumn[]\n /** Campo usado como key estável dos cards. */\n itemKey?: string\n /** Permite bloquear o arrasto de itens específicos. */\n canDrag?: (item: Item) => boolean\n loading?: boolean\n /** Altura máxima da lista de cards (a coluna rola por dentro). */\n maxHeight?: string\n /** Altura mínima da lista — dá área de drop mesmo com a coluna vazia. */\n minHeight?: string\n }>(),\n {\n itemKey: 'id',\n loading: false,\n maxHeight: '32rem',\n minHeight: '6rem',\n },\n)\n\nconst emit = defineEmits<{\n /** Card solto em outra posição (entre colunas ou reordenação na mesma). */\n move: [event: KanbanMoveEvent]\n /** Clique simples no card (sem drag). */\n 'item-click': [item: Item]\n}>()\n\ndefineSlots<{\n /** Conteúdo do card. Fallback: item.nome ?? item[itemKey]. */\n card?(props: { item: Item; column: KanbanColumn }): unknown\n /** Cabeçalho da coluna. Fallback: label + contador. */\n 'column-header'?(props: { column: KanbanColumn }): unknown\n /** Ação à direita do cabeçalho (ex.: criar nesta coluna). */\n 'column-action'?(props: { column: KanbanColumn }): unknown\n /** Coluna sem itens. */\n 'empty-column'?(props: { column: KanbanColumn }): unknown\n}>()\n\nconst dragging = ref<{ item: Item; from: string; key: unknown } | null>(null)\nconst dropTarget = ref<{ column: string; index: number } | null>(null)\n\nconst listRefs = new Map<string, HTMLElement>()\n\nfunction setListRef(value: string, el: unknown) {\n if (el instanceof HTMLElement) listRefs.set(value, el)\n else listRefs.delete(value)\n}\n\nfunction keyOf(item: Item): unknown {\n return item[props.itemKey]\n}\n\nfunction fallbackLabel(item: Item): string {\n return String(item.nome ?? keyOf(item) ?? '')\n}\n\nfunction isDraggable(item: Item): boolean {\n if (props.loading) return false\n return props.canDrag ? props.canDrag(item) : true\n}\n\nfunction isDragging(item: Item): boolean {\n return !!dragging.value && keyOf(item) === dragging.value.key\n}\n\n/** Índice de inserção na coluna: ponto médio vertical dos cards renderizados. */\nfunction computeIndex(colValue: string, event: DragEvent): number {\n const list = listRefs.get(colValue)\n if (!list) return 0\n const cards = Array.from(list.querySelectorAll(':scope > .w-kanban-board__card'))\n let index = cards.length\n for (let i = 0; i < cards.length; i++) {\n const rect = cards[i].getBoundingClientRect()\n if (event.clientY < rect.top + rect.height / 2) {\n index = i\n break\n }\n }\n return index\n}\n\nfunction onDragStart(item: Item, column: KanbanColumn, event: DragEvent) {\n if (!isDraggable(item)) {\n event.preventDefault()\n return\n }\n dragging.value = { item, from: column.value, key: keyOf(item) }\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', String(keyOf(item)))\n }\n}\n\nfunction onDragOver(column: KanbanColumn, event: DragEvent) {\n if (!dragging.value) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = { column: column.value, index: computeIndex(column.value, event) }\n}\n\nfunction onDragLeave(column: KanbanColumn, event: DragEvent) {\n const list = listRefs.get(column.value)\n const related = event.relatedTarget\n if (list && related instanceof Node && list.contains(related)) return\n if (dropTarget.value?.column === column.value) dropTarget.value = null\n}\n\nfunction onDrop(column: KanbanColumn, event: DragEvent) {\n event.preventDefault()\n const drag = dragging.value\n clearDrag()\n if (!drag) return\n\n const rawIndex = computeIndex(column.value, event)\n let index = rawIndex\n\n if (drag.from === column.value) {\n const source = props.columns.find((c) => c.value === column.value)\n const oldIndex = source ? source.items.findIndex((i) => keyOf(i) === drag.key) : -1\n if (oldIndex !== -1 && rawIndex > oldIndex) index = rawIndex - 1\n // Soltou onde já estava: nada a persistir.\n if (index === oldIndex) return\n }\n\n emit('move', { item: drag.item, from: drag.from, to: column.value, index })\n}\n\nfunction onDragEnd() {\n clearDrag()\n}\n\nfunction clearDrag() {\n dragging.value = null\n dropTarget.value = null\n}\n\nfunction onCardClick(item: Item) {\n if (dragging.value) return\n emit('item-click', item)\n}\n\nfunction showIndicatorAt(column: KanbanColumn, index: number): boolean {\n return (\n !!dragging.value &&\n dropTarget.value?.column === column.value &&\n dropTarget.value.index === index\n )\n}\n</script>\n\n<template>\n <div\n class=\"w-kanban-board\"\n :class=\"{\n 'w-kanban-board--loading': loading,\n 'w-kanban-board--dragging': !!dragging,\n }\"\n :aria-busy=\"loading || undefined\"\n >\n <section\n v-for=\"column in columns\"\n :key=\"column.value\"\n class=\"w-kanban-board__column\"\n :class=\"{\n 'w-kanban-board__column--over': !!dragging && dropTarget?.column === column.value,\n }\"\n :style=\"column.accent ? { '--w-kanban-accent': column.accent } : undefined\"\n >\n <header class=\"w-kanban-board__header\">\n <slot name=\"column-header\" :column=\"column\">\n <span class=\"w-kanban-board__title\">{{ column.label }}</span>\n <span class=\"w-kanban-board__count\">{{ column.items.length }}</span>\n </slot>\n <slot name=\"column-action\" :column=\"column\" />\n </header>\n\n <div\n :ref=\"(el) => setListRef(column.value, el)\"\n class=\"w-kanban-board__list\"\n role=\"list\"\n :aria-label=\"column.label\"\n :style=\"{ maxHeight, minHeight }\"\n @dragover=\"onDragOver(column, $event)\"\n @dragleave=\"onDragLeave(column, $event)\"\n @drop=\"onDrop(column, $event)\"\n >\n <template v-for=\"(item, i) in column.items\" :key=\"String(keyOf(item))\">\n <div v-if=\"showIndicatorAt(column, i)\" class=\"w-kanban-board__indicator\" />\n <div\n class=\"w-kanban-board__card\"\n role=\"listitem\"\n tabindex=\"0\"\n :class=\"{\n 'w-kanban-board__card--dragging': isDragging(item),\n 'w-kanban-board__card--static': !isDraggable(item),\n }\"\n :draggable=\"isDraggable(item)\"\n @dragstart=\"onDragStart(item, column, $event)\"\n @dragend=\"onDragEnd\"\n @click=\"onCardClick(item)\"\n @keydown.enter.prevent=\"onCardClick(item)\"\n @keydown.space.prevent=\"onCardClick(item)\"\n >\n <slot name=\"card\" :item=\"item\" :column=\"column\">\n <span class=\"w-kanban-board__card-fallback\">{{ fallbackLabel(item) }}</span>\n </slot>\n </div>\n </template>\n <div\n v-if=\"showIndicatorAt(column, column.items.length)\"\n class=\"w-kanban-board__indicator\"\n />\n\n <div v-if=\"!column.items.length\" class=\"w-kanban-board__empty\">\n <slot name=\"empty-column\" :column=\"column\">Nenhum item</slot>\n </div>\n </div>\n </section>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { KanbanColumn, KanbanMoveEvent } from '@/types/kanban'\n\ntype Item = Record<string, unknown>\n\n/**\n * Board kanban controlado: as colunas e seus itens vêm de fora e o board NUNCA\n * muta os arrays — arrastar um card só emite `move`; o consumidor persiste e\n * atualiza `columns`. Drag and drop com HTML5 nativo (sem dependência).\n *\n * Mover programaticamente (ex.: botão \"mover para...\") é responsabilidade do\n * consumidor — basta aplicar a mesma mudança que faria ao receber um `move`.\n */\nconst props = withDefaults(\n defineProps<{\n /** Colunas na ordem de exibição. Itens pertencem ao consumidor. */\n columns: KanbanColumn[]\n /** Campo usado como key estável dos cards. */\n itemKey?: string\n /** Permite bloquear o arrasto de itens específicos. */\n canDrag?: (item: Item) => boolean\n loading?: boolean\n /** Altura máxima da lista de cards (a coluna rola por dentro). */\n maxHeight?: string\n /** Altura mínima da lista — dá área de drop mesmo com a coluna vazia. */\n minHeight?: string\n }>(),\n {\n itemKey: 'id',\n loading: false,\n maxHeight: '32rem',\n minHeight: '6rem',\n },\n)\n\nconst emit = defineEmits<{\n /** Card solto em outra posição (entre colunas ou reordenação na mesma). */\n move: [event: KanbanMoveEvent]\n /** Clique simples no card (sem drag). */\n 'item-click': [item: Item]\n}>()\n\ndefineSlots<{\n /** Conteúdo do card. Fallback: item.nome ?? item[itemKey]. */\n card?(props: { item: Item; column: KanbanColumn }): unknown\n /** Cabeçalho da coluna. Fallback: label + contador. */\n 'column-header'?(props: { column: KanbanColumn }): unknown\n /** Ação à direita do cabeçalho (ex.: criar nesta coluna). */\n 'column-action'?(props: { column: KanbanColumn }): unknown\n /** Coluna sem itens. */\n 'empty-column'?(props: { column: KanbanColumn }): unknown\n}>()\n\nconst dragging = ref<{ item: Item; from: string; key: unknown } | null>(null)\nconst dropTarget = ref<{ column: string; index: number } | null>(null)\n\nconst listRefs = new Map<string, HTMLElement>()\n\nfunction setListRef(value: string, el: unknown) {\n if (el instanceof HTMLElement) listRefs.set(value, el)\n else listRefs.delete(value)\n}\n\nfunction keyOf(item: Item): unknown {\n return item[props.itemKey]\n}\n\nfunction fallbackLabel(item: Item): string {\n return String(item.nome ?? keyOf(item) ?? '')\n}\n\nfunction isDraggable(item: Item): boolean {\n if (props.loading) return false\n return props.canDrag ? props.canDrag(item) : true\n}\n\nfunction isDragging(item: Item): boolean {\n return !!dragging.value && keyOf(item) === dragging.value.key\n}\n\n/** Índice de inserção na coluna: ponto médio vertical dos cards renderizados. */\nfunction computeIndex(colValue: string, event: DragEvent): number {\n const list = listRefs.get(colValue)\n if (!list) return 0\n const cards = Array.from(list.querySelectorAll(':scope > .w-kanban-board__card'))\n let index = cards.length\n for (let i = 0; i < cards.length; i++) {\n const rect = cards[i].getBoundingClientRect()\n if (event.clientY < rect.top + rect.height / 2) {\n index = i\n break\n }\n }\n return index\n}\n\nfunction onDragStart(item: Item, column: KanbanColumn, event: DragEvent) {\n if (!isDraggable(item)) {\n event.preventDefault()\n return\n }\n dragging.value = { item, from: column.value, key: keyOf(item) }\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', String(keyOf(item)))\n }\n}\n\nfunction onDragOver(column: KanbanColumn, event: DragEvent) {\n if (!dragging.value) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n dropTarget.value = { column: column.value, index: computeIndex(column.value, event) }\n}\n\nfunction onDragLeave(column: KanbanColumn, event: DragEvent) {\n const list = listRefs.get(column.value)\n const related = event.relatedTarget\n if (list && related instanceof Node && list.contains(related)) return\n if (dropTarget.value?.column === column.value) dropTarget.value = null\n}\n\nfunction onDrop(column: KanbanColumn, event: DragEvent) {\n event.preventDefault()\n const drag = dragging.value\n clearDrag()\n if (!drag) return\n\n const rawIndex = computeIndex(column.value, event)\n let index = rawIndex\n\n if (drag.from === column.value) {\n const source = props.columns.find((c) => c.value === column.value)\n const oldIndex = source ? source.items.findIndex((i) => keyOf(i) === drag.key) : -1\n if (oldIndex !== -1 && rawIndex > oldIndex) index = rawIndex - 1\n // Soltou onde já estava: nada a persistir.\n if (index === oldIndex) return\n }\n\n emit('move', { item: drag.item, from: drag.from, to: column.value, index })\n}\n\nfunction onDragEnd() {\n clearDrag()\n}\n\nfunction clearDrag() {\n dragging.value = null\n dropTarget.value = null\n}\n\nfunction onCardClick(item: Item) {\n if (dragging.value) return\n emit('item-click', item)\n}\n\nfunction showIndicatorAt(column: KanbanColumn, index: number): boolean {\n return (\n !!dragging.value &&\n dropTarget.value?.column === column.value &&\n dropTarget.value.index === index\n )\n}\n</script>\n\n<template>\n <div\n class=\"w-kanban-board\"\n :class=\"{\n 'w-kanban-board--loading': loading,\n 'w-kanban-board--dragging': !!dragging,\n }\"\n :aria-busy=\"loading || undefined\"\n >\n <section\n v-for=\"column in columns\"\n :key=\"column.value\"\n class=\"w-kanban-board__column\"\n :class=\"{\n 'w-kanban-board__column--over': !!dragging && dropTarget?.column === column.value,\n }\"\n :style=\"column.accent ? { '--w-kanban-accent': column.accent } : undefined\"\n >\n <header class=\"w-kanban-board__header\">\n <slot name=\"column-header\" :column=\"column\">\n <span class=\"w-kanban-board__title\">{{ column.label }}</span>\n <span class=\"w-kanban-board__count\">{{ column.items.length }}</span>\n </slot>\n <slot name=\"column-action\" :column=\"column\" />\n </header>\n\n <div\n :ref=\"(el) => setListRef(column.value, el)\"\n class=\"w-kanban-board__list\"\n role=\"list\"\n :aria-label=\"column.label\"\n :style=\"{ maxHeight, minHeight }\"\n @dragover=\"onDragOver(column, $event)\"\n @dragleave=\"onDragLeave(column, $event)\"\n @drop=\"onDrop(column, $event)\"\n >\n <template v-for=\"(item, i) in column.items\" :key=\"String(keyOf(item))\">\n <div v-if=\"showIndicatorAt(column, i)\" class=\"w-kanban-board__indicator\" />\n <div\n class=\"w-kanban-board__card\"\n role=\"listitem\"\n tabindex=\"0\"\n :class=\"{\n 'w-kanban-board__card--dragging': isDragging(item),\n 'w-kanban-board__card--static': !isDraggable(item),\n }\"\n :draggable=\"isDraggable(item)\"\n @dragstart=\"onDragStart(item, column, $event)\"\n @dragend=\"onDragEnd\"\n @click=\"onCardClick(item)\"\n @keydown.enter.prevent=\"onCardClick(item)\"\n @keydown.space.prevent=\"onCardClick(item)\"\n >\n <slot name=\"card\" :item=\"item\" :column=\"column\">\n <span class=\"w-kanban-board__card-fallback\">{{ fallbackLabel(item) }}</span>\n </slot>\n </div>\n </template>\n <div\n v-if=\"showIndicatorAt(column, column.items.length)\"\n class=\"w-kanban-board__indicator\"\n />\n\n <div v-if=\"!column.items.length\" class=\"w-kanban-board__empty\">\n <slot name=\"empty-column\" :column=\"column\">Nenhum item</slot>\n </div>\n </div>\n </section>\n </div>\n</template>\n"],"x_google_ignoreList":[2,3],"mappings":"ovEAWA,GAAM,CAAE,aAAY,iBAAgB,iBAAgB,gBAAiB,EAAA,EAAc,eAIrE,EAAA,OAAK,OAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA0E,OAA1E,EAA4D,GAAO,GAE9C,EAAA,OAAO,OAAI,UAAA,EAC9B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIE,MAAA,OAHC,IAAK,OAAO,EAAA,KAAK,EACjB,IAAK,EAAA,OAAO,OACb,MAAM,8EAIW,EAAA,KAAA,EAAA,EAAA,GAAA,EAAA,OAAO,OAAI,YAAA,EAC9B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OATC,MAAO,EAAA,OAAO,SAAW,EAAA,OAAO,SAAS,EAAA,MAAO,EAAA,OAAO,EAAI,EAAA,MAAK,QAAA,UAChE,SAAoB,EAAA,OAAO,YAAwB,EAAA,OAAO,YAAY,EAAA,MAAO,EAAA,OAAO,EAAc,EAAA,MAAA,UAAA,SAOnG,MAAM,SAIO,EAAA,KAAA,EAAA,CAAA,QAAA,UAAA,CAAA,GAAA,EAAA,OAAO,OAAI,SAAA,EAA5B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,EAAA,EADF,EAAA,MAAA,CAAA,CAAA,CAAU,CAAC,EAAA,KAAK,CAAA,EAAA,CAAA,GAIR,EAAA,OAAO,OAAI,aAAA,EADxB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKO,OALP,GAAA,EAKO,EAAA,gBAAA,EAAA,EADF,EAAA,MAAA,CAAA,CAAA,CAAc,CAAC,EAAA,KAAK,CAAA,EAAA,CAAA,GAGR,EAAA,OAAO,OAAI,aAAA,EAA5B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,GAAA,EAEO,EAAA,gBAAA,EAAA,EADF,EAAA,MAAA,CAAA,CAAA,CAAc,CAAC,EAAA,KAAK,CAAA,EAAA,CAAA,GAGR,EAAA,OAAO,OAAI,WAAA,EAA5B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMO,OANP,IAAA,EAMO,EAAA,gBAAA,CAJH,EAAA,OAAO,OAAiB,EAAA,OAAO,OAAO,EAAA,MAAO,EAAA,OAAO,GAAA,EAAY,EAAA,MAAA,CAAA,CAAA,CAAY,CAAC,EAAA,MAAiB,EAAA,OAAO,UAAQ,CAAA,CAAA,EAAA,CAAA,KAAA,EAMjH,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,OAAO,OAAS,EAAA,OAAO,OAAO,EAAA,MAAO,EAAA,OAAO,EAAI,EAAA,KAAK,EAAA,CAAA,ME/DxD,GAAI,OAAO,eACX,GAAK,EAAG,EAAG,IAAM,KAAK,EAAI,GAAE,EAAG,EAAG,CAAE,WAAY,CAAC,EAAG,aAAc,CAAC,EAAG,SAAU,CAAC,EAAG,MAAO,CAAE,CAAC,EAAI,EAAE,GAAK,EACzG,GAAK,EAAG,EAAG,IAAM,EAAE,EAAG,OAAO,GAAK,SAAoB,EAAT,EAAI,GAAQ,CAAC,EACxD,GAAI,CACR,IAAK,CAAE,QAAS,OAAQ,EACxB,IAAK,CAAE,QAAS,UAAW,EAC3B,IAAK,CAAE,QAAS,aAAc,CAChC,EAAG,IAAK,EAAG,EAAG,IAAM,EAAE,WAAW,EAAG,EAAE,CAAC,CAAC,QAAQ,EAAG,GAAG,CAAC,CAAC,QAAQ,KAAM,GAAG,CAAC,CAAC,QAAQ,UAAW,EAAE,EAAG,IAAK,EAAG,EAAG,IAErG,IAAI,KAAK,aAAmB,EAAE,QAA6B,QAAW,KAAM,CACjF,sBAAuB,EACvB,sBAAuB,EACvB,aAAc,OAChB,CAAC,EACA,GAAK,EAAG,EAAI,CAAC,EAAG,IAAM,CAEvB,IAAM,EAAU,EAAE,QAA6B,WAAc,CAAC,GAAK,EAAE,WAAW,GAAG,EAAI,IAAM,GAAI,EAAU,EAAE,QAA6B,UAAa,EACnJ,EAAI,GAAE,EAAG,EAAG,CAAC,EACX,EAAI,EAAE,cAAc,OAAO,EAAG,EAAU,EAAE,KAAM,GAAM,EAAE,OAAS,OAAO,CAAA,EAAwB,OAAU,IAAK,EAAU,EAAE,KAAM,GAAM,EAAE,OAAS,SAAS,CAAA,EAAwB,OAAU,IAAK,EAAI,GAAE,EAAG,EAAG,CAAC,EACrN,GAAI,OAAO,MAAM,WAAW,CAAC,CAAC,EAAG,OAAO,EACxC,IAAM,EAAI,EAAE,MAAM,GAAG,EACjB,EAAE,IAAM,MAAQ,EAAE,EAAE,CAAC,QAAU,IAEjC,EAAI,GADM,EAAE,EAAE,CAAC,QAAU,EAAI,EAAE,EAAE,CAAC,OAAS,EAClC,EAAG,CAAC,GAEf,IAAI,EAAI,EAAE,OAAO,WAAW,CAAC,CAAC,EAC9B,OAAO,EAAI,EAAI,GAAK,EAAE,SAAS,GAAG,GAAK,CAAC,EAAE,MAAM,EAAG,EAAE,CAAC,CAAC,SAAS,GAAG,IAAM,GAAK,GAAK,EAAI,GAAE,EAAG,EAAG,CAAC,EAAG,EAAI,CACzG,EAAG,EAAK,GAAM,KAAK,MAAM,EAAE,WAAW,IAAK,GAAG,CAAC,EAAG,GAAK,EAAG,EAAI,CAAC,IAAM,CACnE,IAAM,EAAI,CAAE,GAAG,CAAE,EACjB,EAAE,QAAQ,OAAS,MAAQ,EAAE,QAAQ,QAAU,KAAO,EAAE,KAAO,EAAE,EAAE,QAAQ,KAAK,GAAI,EAAE,QAAQ,YAAc,OAAS,EAAE,MAAQ,EAAE,EAAE,QAAQ,UAAU,GAAI,EAAE,QAAQ,eAAiB,OAAS,EAAE,SAAW,EAAE,EAAE,QAAQ,aAAa,GAAI,EAAE,QAAQ,oBAAsB,OAAS,EAAE,cAAgB,EAAE,EAAE,QAAQ,kBAAkB,GAAI,EAAE,QAAQ,aAAe,OAAS,EAAE,OAAS,GAAE,EAAE,QAAQ,WAAW,GACxY,IAAM,EAAI,CAAC,EACX,OAAO,EAAE,QAAQ,mBAAqB,OAAS,EAAE,OAAS,EAAE,QAAQ,mBAAoB,EAAE,QAAQ,qBAAuB,OAAS,EAAE,SAAW,SAAS,EAAE,QAAQ,mBAAmB,GAAI,EAAE,QAAQ,qBAAuB,OAAS,EAAE,SAAW,EAAE,EAAE,QAAQ,mBAAmB,IAAK,EAAE,QAAQ,aAAe,MAAQ,OAAO,OAAO,CAAC,CAAC,CAAC,OAAS,KAAO,EAAE,OAAS,GAAI,CACvW,EAAG,EAAK,GAAM,IAAM,IAAK,CAAC,CAAC,KAAK,MAAM,CAAC,EAAQ,EAAK,GAAM,EAAE,WAAW,GAAG,GAAK,EAAE,SAAS,GAAG,EAAI,EAAE,CAAC,EAAI,EAAG,GAAK,GAAM,CACpH,GAAI,EAAE,WAAW,GAAG,GAAK,EAAE,SAAS,GAAG,EACrC,OAAO,EAAE,CAAC,EACZ,IAAM,EAAI,CAAC,EACX,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,QAAS,GAAM,CACjC,IAAM,EAAI,EAAE,MAAM,GAAG,EACrB,EAAE,EAAE,IAAM,CACR,QAAS,EAAE,EAAI,IAAI,OAAO,EAAE,GAAI,GAAG,EAAI,IAAI,OAAO,EAAE,EAAE,EACtD,SAAU,EAAE,KAAO,WACnB,SAAU,EAAE,KAAO,WACnB,SAAU,EAAE,KAAO,UACrB,CACF,CAAC,EAAG,CACN,EAAG,MAAU,CACX,GAAI,CACF,MAAkC,CAAC,CACrC,MAAQ,CACN,MAAO,CAAC,CACV,CACF,EACM,GAAN,KAAQ,CACN,YAAY,EAAI,CAAC,EAAG,CAClB,EAAE,KAAM,OAAQ,CAAC,CAAC,EAClB,EAAE,KAAM,OAAwB,IAAI,GAAK,EACzC,IAAM,EAAI,CAAE,GAAG,CAAE,EACjB,GAAI,EAAE,QAAU,KAAM,CACpB,EAAE,OAAS,EAAE,cAAgB,CAAE,GAAG,EAAE,MAAO,EAAI,CAAE,GAAG,GAAG,GAAG,EAAE,MAAO,EACnE,IAAK,IAAM,KAAK,OAAO,OAAO,EAAE,MAAM,EACpC,OAAO,EAAE,SAAW,WAAa,EAAE,QAAU,EAAE,EAAI,IAAI,OAAO,EAAE,QAAS,GAAG,EAAI,IAAI,OAAO,EAAE,OAAO,EACxG,KACE,GAAE,OAAS,GACb,MAAM,QAAQ,EAAE,IAAI,IAAM,AAA+E,EAAE,KAAjF,EAAE,KAAK,OAAS,EAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,OAAS,EAAE,MAAM,EAAa,EAAE,KAAK,IAAM,IAAK,EAAE,OAAS,KAAO,EAAE,KAAO,MAAO,KAAK,KAAO,CACpL,CACA,OAAO,EAAG,CACR,OAAO,KAAK,QAAQ,OAAO,CAAC,EAAG,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,CACzD,CACA,SAAS,EAAG,CACV,OAAO,KAAK,QAAQ,OAAO,CAAC,EAAG,KAAK,SAAS,OAAO,CAAC,CAAC,EAAG,CAAC,CAAC,CAC7D,CACA,SAAU,CACR,OAAO,KAAK,KAAK,QAAU,CAAC,CAC9B,CACA,YAAa,CACX,OAAO,KAAK,KAAK,WAAa,CAAC,CACjC,CACA,UAAU,EAAG,CACX,IAAM,EAAI,KAAK,SAAS,OAAO,CAAC,CAAC,EACjC,GAAI,KAAK,KAAK,MAAQ,MAAQ,GAAK,KAAM,MAAO,CAAC,EACjD,IAAM,EAAI,KAAK,QAAQ,OAAO,CAAC,EAAG,CAAC,CAAC,CAAC,OACrC,OAAO,OAAO,KAAK,KAAK,MAAQ,SAAW,GAAK,KAAK,KAAK,KAAK,OAAS,GAAK,EAAE,MACjF,CACA,SAAS,EAAG,CACV,IAAM,EAAI,KAAK,KAAK,KACpB,GAAI,GAAK,KACP,OAAO,KACT,GAAI,OAAO,GAAK,SACd,OAAO,EACT,GAAI,OAAO,GAAK,WACd,OAAO,EAAE,CAAC,EACZ,IAAM,EAAI,KAAK,QAAQ,EAAG,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,GAAK,GAAI,CAAC,CAAC,EACrD,OAAO,EAAE,KAAM,GAAM,KAAK,QAAQ,EAAG,EAAG,CAAC,CAAC,CAAC,CAAC,QAAU,EAAE,MAAM,GAAK,EACrE,CACA,WAAW,EAAG,CACZ,IAAM,EAAI,CAAC,EAAG,EAAI,CAAC,EACnB,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,EAAG,IAAM,CACnC,IAAM,KAAO,EAAE,EAAI,KAAO,IAAM,EAAE,KAAK,EAAI,EAAE,MAAM,EAAI,EAAE,KAAK,CAAC,CACjE,CAAC,EAAG,CAAE,KAAM,EAAE,KAAK,EAAE,EAAG,QAAS,CAAE,CACrC,CACA,QAAQ,EAAG,EAAG,EAAI,CAAC,EAAG,CACpB,GAAI,KAAK,KAAK,QAAU,KAAM,OAAO,EAAE,EAAG,EAAG,KAAK,IAAI,EACtD,GAAI,GAAK,KAAM,OAAO,EACtB,IAAM,EAAI,KAAK,EAAE,MAAM,EAAE,KAAK,OAC9B,GAAI,KAAK,KAAK,IAAI,CAAC,EAAG,OAAO,KAAK,KAAK,IAAI,CAAC,EAC5C,GAAM,CAAE,KAAM,EAAG,QAAS,GAAM,KAAK,WAAW,CAAC,EAAG,EAAI,CAAC,EAAG,EAAI,KAAK,KAAK,QAAU,KAA0B,CAAC,EAApB,KAAK,KAAK,OAAa,EAAI,KAAK,WAAW,EAAI,GAAK,EAAG,EAAI,KAAK,WAAW,EAAI,UAAY,OAAQ,EAAI,KAAK,WAAW,EAAI,EAAI,EAAE,OAAS,EAAG,EAAI,KAAK,WAAW,MAAU,EAAI,IAAM,EAAI,OAAW,EAAI,EAAE,QAAU,EAAI,EAAE,OAAQ,EAAK,GAAM,CAAC,KAAK,WAAW,GAAK,GAAK,GAAK,KAAK,WAAW,GAAK,GAAK,EAC9X,EAAG,EAAI,GAAI,EAAI,KAAK,WAAW,EAAI,EAAE,OAAS,EAAI,EAAG,EAAI,KAAK,WAAW,EAAI,EAAE,OAAS,EAAI,EAAG,EAAI,CAAC,EACxG,KAAO,EAAE,GAAK,CACZ,IAAM,EAAI,EAAE,OAAO,CAAC,EAAG,EAAI,EAAE,GAAI,EAAK,GAAuB,WAAc,KAAkC,EAAE,OAAO,CAAC,EAArC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,EACzG,GAAI,CAAC,EAAE,SAAS,CAAC,GAAK,GAAK,MAAQ,EAAE,MAAM,EAAE,OAAO,GAAK,KAAsJ,EAAE,SAAW,AAAsB,KAAhB,GAAK,EAAG,GAAK,EAAO,CAAC,GAAK,IAAM,EAAI,EAAI,IAAK,GAAI,EAAE,WAAa,GAAK,EAAG,GAAK,IAA9O,EAAE,EAAE,CAAC,CAAC,EAAG,EAAE,UAAY,IAAM,GAAK,EAAI,EAAI,IAAM,GAAK,IAAM,IAAM,EAAI,EAAI,GAAI,IAAM,IAAM,GAAK,IAAM,EAAE,WAAa,EAAI,CAAC,EAAG,GAAK,GAAI,GAAK,GAAyG,GAAK,IAAM,GAAK,CAAC,KAAK,QAAQ,GAAK,EAAE,EAAE,CAAC,CAAC,EAAG,IAAM,GAAK,CAAC,KAAK,QAAQ,EAAI,GAAK,EAAI,EAAI,EAAG,KAAK,QAAQ,IAAM,GAAK,IAAK,KAAK,QAAQ,EACpb,KAAO,EAAE,CAAC,IAAM,EAAE,EAAE,OAAO,CAAC,IAAM,MAAQ,EAAE,SAAS,CAAC,IAAM,CAC1D,GAAI,EACE,IAAA,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,EAAG,EAAE,OAAO,CAAC,IAAM,EAAE,OAAO,CAAC,EAAG,CAClD,GAAK,EAAG,GAAK,EACb,QACF,OACK,EAAE,OAAO,CAAC,IAAM,EAAE,OAAO,CAAC,IAAM,GAAK,GAC5C,GAAK,CACP,CACJ,CACA,OAAO,KAAK,KAAK,IAAI,EAAG,EAAE,KAAK,EAAE,CAAC,EAAG,KAAK,KAAK,IAAI,CAAC,CACtD,CACF,EACM,EAAN,KAAQ,CACN,YAAY,EAAG,EAAI,CAAC,EAAG,CACrB,EAAE,KAAM,QAAyB,IAAI,GAAK,EAC1C,EAAE,KAAM,sBAAsB,EAC9B,EAAE,KAAM,UAAY,GAAM,CACxB,GAAI,aAAa,aAAe,EAAE,OAAS,SAAW,CAAC,EAAE,WAAa,CAAC,EAAE,QACvE,OACF,IAAM,EAAI,EAAE,OAAQ,EAAI,KAAK,MAAM,IAAI,CAAC,EACxC,GAAI,IAAM,IAAK,GAAG,OAClB,IAAM,EAAI,cAAe,GAAK,EAAE,UAAU,WAAW,QAAQ,EAAG,EAAI,EAAE,QAAQ,EAAG,EAAI,GAAK,GAAK,EAAE,SAAS,EAAE,KAAK,IAAM,GAAK,GAAK,EAAE,MACnI,KAAK,UAAU,EAAG,MAAS,KAAK,SAAS,EAAG,CAAC,CAAC,CAChD,CAAC,EACD,KAAK,QAAU,EAAG,KAAK,qBAAuB,IAAI,gBAAmB,KAAK,KAAK,KAAK,UAAU,CAAC,CAAC,CAClG,CACA,OAAO,EAAI,CAAC,EAAG,CACb,KAAK,QAAU,CAAE,GAAG,CAAE,EAAG,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC,CAClE,CACA,YAAY,EAAG,CAEb,EAAE,QAAU,IAAM,EAAE,QAAgB,KAAK,aAAa,CAAC,CAAA,EAAwB,QAAW,KAAK,SAAS,EAAG,EAAE,KAAK,CACpH,CACA,SAAU,CACR,KAAK,qBAAqB,MAAM,EAAG,KAAK,MAAM,MAAM,CACtD,CACA,KAAK,EAAG,CACN,IAAM,EAAI,KAAK,WAAW,KAAK,OAAO,EACtC,IAAK,IAAM,KAAK,EAAG,CACjB,GAAI,CAAC,KAAK,MAAM,IAAI,CAAC,EAAG,CACtB,GAAM,CAAE,OAAQ,GAAM,KAAK,qBAC3B,EAAE,iBAAiB,QAAS,KAAK,QAAS,CAAE,QAAS,CAAC,EAAG,OAAQ,CAAE,CAAC,CACtE,CACA,IAAM,EAAI,IAAI,GAAE,EAAE,EAAG,CAAC,CAAC,EACvB,KAAK,MAAM,IAAI,EAAG,CAAC,EAAG,mBAAqB,KAAK,YAAY,CAAC,CAAC,EAAG,EAAE,iBAAmB,MAAQ,EAAE,QAAQ,GAAK,QAAQ,KAAK,6CAA8C,EAAE,IAAI,CAChL,CACF,CACA,UAAU,EAAG,CACX,OAAO,OAAO,GAAK,SAAW,MAAM,KAAK,SAAS,iBAAiB,CAAC,CAAC,EAAI,WAAY,EAAI,MAAM,KAAK,CAAC,EAAI,CAAC,CAAC,CAC7G,CACA,WAAW,EAAG,CACZ,GAAM,CAAE,QAAS,EAAG,WAAY,EAAG,YAAa,EAAG,GAAG,GAAM,EAC5D,OAAO,CACT,CACA,UAAU,EAAG,EAAG,EAAG,CAEjB,IAAM,EAAI,EAAE,eAAgB,EAAI,EAAE,MAClC,GAAI,EAAE,EAAG,IAAM,MAAQ,IAAM,EAAE,QAAU,CAAC,EAAG,OAC7C,IAAM,EAAI,EAAE,MAAO,EAAI,EAAE,MAAM,EAAG,CAAC,EAAG,EAAI,EAAE,MAAM,EAAG,CAAC,EAAG,EAAS,KAAK,aAAa,EAAG,CAAC,CAAA,EAAwB,SAAU,EAAS,KAAK,aAAa,EAAG,CAAC,CAAA,EAAwB,SACjL,GAAI,IAAM,IAAK,IAAK,IAAM,IAAK,GAAG,OAClC,IAAI,EAAI,EACR,IAAM,IAAM,GAAK,EAAI,EAAE,OAAS,EAAE,OAAS,EAAE,OAAS,EAAE,QAAS,EAAE,kBAAkB,EAAG,CAAC,CAC3F,CACA,SAAS,EAAG,EAAG,CACb,IAAM,EAAI,KAAK,aAAa,EAAG,CAAC,EAChC,IAAM,IAAK,KAAM,EAAE,MAAQ,EAAE,OAAQ,KAAK,QAAQ,SAAW,OAAS,MAAM,QAAQ,KAAK,QAAQ,OAAO,EAAI,KAAK,QAAQ,QAAQ,QAAS,GAAM,EAAE,CAAC,CAAC,EAAI,KAAK,QAAQ,QAAQ,CAAC,GAAI,EAAE,cAAc,IAAI,YAAY,QAAS,CAAE,OAAQ,CAAE,CAAC,CAAC,EAAG,EAAE,cAAc,IAAI,YAAY,QAAS,CAAE,OAAQ,EAAE,MAAO,CAAC,CAAC,EAC5S,CACA,aAAa,EAAG,EAAG,CACjB,IAAM,EAAI,KAAK,MAAM,IAAI,CAAC,EAC1B,GAAI,IAAM,IAAK,GAAG,OAClB,IAAI,EAAI,GAAK,EAAE,MACf,KAAK,QAAQ,YAAc,OAAS,EAAI,KAAK,QAAQ,WAAW,CAAC,GACjE,IAAI,EAAI,EAAE,OAAO,CAAC,EAClB,OAAO,KAAK,QAAQ,aAAe,OAAS,EAAI,KAAK,QAAQ,YAAY,CAAC,GAAI,CAC5E,OAAQ,EACR,SAAU,EAAE,SAAS,CAAC,EACtB,UAAW,EAAE,UAAU,CAAC,CAC1B,CACF,CACF,EC7LM,GAAoB,IAAI,QAAW,IAAK,EAAG,IAAM,CACrD,GAAI,EAAE,KAAO,MAAQ,EAAE,UAAY,KAAM,OACzC,IAAM,EAAI,UAAW,EAAE,SAAS,EAAE,KAClC,EAAE,OAAO,EAAE,SAAW,EAAE,SAAS,EAAE,KAAO,EAAI,GAAK,QAAQ,KAAK,+CAAgD,EAAE,GAAG,CACvH,EAAG,GAAK,EAAG,IAAM,CACf,IAAI,EACJ,IAAM,EAAI,aAAa,iBAAmB,EAAI,EAAE,cAAc,OAAO,EACrE,GAAI,GAAK,MAAS,GAAuB,OAAU,OAAQ,OAC3D,IAAI,EAAI,CAAC,EACT,GAAI,EAAE,OAAS,OAAS,EAAI,OAAO,EAAE,OAAS,SAAW,CAAE,KAAM,EAAE,KAAM,EAAI,CAAE,GAAG,EAAE,KAAM,GAAI,EAAE,KAAO,KAAM,CAC3G,IAAM,EAAK,GAAM,CAEf,GAAE,EADQ,EAAE,UAAU,SAAW,EAAE,SAAW,EAAE,UAAU,UAAY,EAAE,UAAY,EAAE,MAChF,CACR,EACA,EAAE,QAAU,EAAE,SAAW,KAAO,EAAI,MAAM,QAAQ,EAAE,OAAO,EAAI,CAAC,GAAG,EAAE,QAAS,CAAC,EAAI,CAAC,EAAE,QAAS,CAAC,CAClG,CACA,GAAE,IAAI,CAAC,GAAK,EAAI,GAAE,IAAI,CAAC,IAAM,MAAQ,EAAE,OAAO,CAAC,EAAI,GAAE,IAAI,EAAG,IAAIA,EAAE,EAAG,CAAC,CAAC,CACzE,ECFM,GAA6C,CACjD,OAAQ,OACR,QAAS,SACT,QAAS,SACT,MAAO,SACP,QAAS,SACT,OAAQ,SACR,GAAI,KACJ,KAAM,OACN,SAAU,WACV,MAAO,QACP,IAAK,OACL,KAAM,OACN,KAAM,UACR,EAEA,SAAgB,EAAsB,EAAkC,CACtE,IAAM,EAAY,GAAS,EAAS,OAAS,OAEvC,EAAgB,CACpB,MAAO,EAAS,KAChB,MAAO,EAAS,MAChB,KAAM,EACN,SAAU,EAAS,UAAY,EACjC,EAwBA,OAtBI,EAAS,OAAS,WAAa,EAAS,OAAS,WACnD,EAAI,kBAAoB,EACxB,EAAI,kBAAoB,GAGtB,EAAS,OAAS,YACpB,EAAI,aAAe,IAGjB,EAAS,OAAS,UAAY,EAAS,SAAS,SAClD,EAAI,QAAU,EAAS,QAAQ,IAAK,IAAO,CACzC,MAAO,EAAE,MACT,MAAO,EAAE,KACX,EAAE,GAGA,EAAS,OAAS,OACpB,EAAI,SAAW,EAAS,SACpB,EAAS,eAAc,EAAI,YAAc,EAAS,cAClD,EAAS,eAAc,EAAI,YAAc,EAAS,eAGjD,CACT,CAEA,SAAgB,GAAwB,EAAuC,CAC7E,OAAO,EAAU,OAAQ,GAAM,CAAC,EAAE,WAAa,EAAE,OAAS,IAAI,CAAC,CAAC,IAAI,CAAqB,CAC3F,CAEA,IAAM,GAAqD,CACzD,QAAS,UACT,KAAM,OACN,SAAU,WACV,QAAS,SACT,MAAO,SACP,QAAS,QACX,EAEA,SAAgB,GAAuB,EAAmC,CAGxE,MAAO,CACL,MAFY,EAAS,OAAS,KAAO,GAAG,EAAS,KAAK,OAAS,EAAS,KAGxE,OAAQ,EAAS,MACjB,KAAM,GAAgB,EAAS,MAC/B,SAAU,EACZ,CACF,CAEA,SAAgB,GAAyB,EAA2B,EAAa,EAAgB,CAC/F,OAAO,EACJ,OAAQ,GAAM,CAAC,EAAE,WAAa,EAAE,OAAS,IAAI,CAAC,CAC9C,MAAM,EAAG,CAAU,CAAC,CACpB,IAAI,EAAsB,CAC/B,ulCCtEA,IAAM,EAAU,EAAA,EAAW,EAiBrB,EAAQ,EA+DR,EAAO,EAUP,GAAA,EAAe,EAAA,OAAA,CAAqB,EAAA,CAAmB,EAC7D,GAAI,CAAC,EACH,MAAU,MAAM,gFAAgF,EAElG,IAAM,EAAW,EAEX,EAAQ,EAAA,EAAY,EACpB,CAAE,cAAe,GAAwB,EAAA,EAAc,EAMvD,GAAA,EAAe,EAAA,IAAA,CAAoC,IAAI,EAEvD,GAAA,EAAgB,EAAA,IAAA,CAA+B,CAAC,CAAC,EACjD,GAAA,EAAc,EAAA,IAAA,CAA+B,CAAC,CAAC,EAC/C,GAAA,EAAY,EAAA,IAAA,CAAI,EAAK,EAGrB,GAAA,EAAU,EAAA,SAAA,KAAgB,EAAM,SAAW,EAAc,MAAQ,EAAa,KAAM,EAG1F,SAAS,EAAM,EAAqB,CAElC,OADI,GAAK,OAAO,GAAM,SAAkB,EAA8B,EAAM,aACrE,CACT,CAEA,SAAS,EAAY,EAA6D,CAChF,IAAM,EAAS,IAAI,IACb,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAI,EAAM,CAAI,EAChB,EAAO,IAAI,CAAC,IAChB,EAAO,IAAI,CAAC,EACZ,EAAI,KAAK,CAAI,EACf,CACA,OAAO,CACT,CAEA,SAAS,GAAgB,CACvB,EAAK,oBAAqB,EAAM,SAAW,CAAC,GAAG,EAAc,KAAK,EAAI,EAAa,KAAK,CAC1F,CAEA,SAAS,EAAQ,EAAuB,CACtC,IAAM,EAAK,IAAmC,EAAM,aACpD,OAAO,GAAK,KAAO,GAAK,OAAO,CAAC,CAClC,CAGA,SAAS,EAAc,EAAuB,CAC5C,GAAI,CAAC,EAAM,kBAAmB,MAAO,GACrC,IAAM,EAAK,IAAmC,EAAM,mBACpD,OAAO,GAAK,KAAO,GAAK,OAAO,CAAC,CAClC,CAGA,IAAM,GAAA,EAAe,EAAA,SAAA,KACnB,EAAM,SAAW,EAAI,KAAK,IAAI,EAAG,EAAc,MAAM,OAAS,EAAM,QAAQ,EAAI,CAClF,EAGM,GAAA,EAAoB,EAAA,SAAA,KACxB,EAAc,MAAM,MAAM,EAAM,QAAQ,CAAC,CAAC,IAAI,CAAO,CAAC,CAAC,KAAK,IAAI,CAClE,EAIA,SAAS,GAAa,EAAc,EAAgC,CAClE,GAAO,kBAAkB,EACzB,EAAO,CAAK,CACd,CAGA,SAAS,GAAgB,CACnB,MAAM,SACV,IAAI,EAAM,SAAU,CAClB,GAAI,CAAC,EAAc,MAAM,OAAQ,OACjC,EAAc,MAAQ,CAAC,CACzB,KAAO,CACL,GAAI,CAAC,EAAa,MAAO,OACzB,EAAa,MAAQ,IACvB,CACA,EAAc,CADd,CAEF,CAMA,IAAM,GAAA,EAAY,EAAA,SAAA,KAChB,EAAM,UAAY,EAAM,SAAW,EAC/B,CAAE,kBAAmB,kCAAkC,EAAM,SAAS,GAAI,EAC1E,IAAA,EACN,EAGM,GAAA,EAAgB,EAAA,SAAA,KAChB,CAAC,EAAM,WAAa,EAAM,SAAiB,GACxC,EAAM,SAAW,EAAc,MAAM,OAAS,EAAI,EAAa,QAAU,IACjF,EAEG,EAAsD,KAGpD,GAAA,EAAQ,EAAA,IAAA,CAAkC,IAAI,GAEpD,EAAA,EAAA,UAAA,KAAgB,CACT,EAAM,YACX,EAAA,EAAA,SAAA,KAAe,CACb,IAAM,EAAQ,EAAM,OAAO,KAAK,gBAAgB,OAAO,EAClD,IAGL,EAAM,aAAa,YAAa,EAAE,EAClC,EAAM,MAAM,EACZ,EAAM,SAAS,EACjB,CAAC,CACH,CAAC,EAMD,SAAS,EAAa,EAAqB,CACzC,OAAO,GAAM,MAA2B,IAAM,EAChD,CAGA,SAAS,EAAoB,EAAqB,CAChD,GAAI,GAAK,OAAO,GAAM,SAAU,CAC9B,IAAM,EAAI,EACV,OAAO,EAAE,IAAM,EAAE,OAAS,CAC5B,CACA,OAAO,CACT,CAEA,IAAM,GAAA,EAAgB,EAAA,SAAA,KAAkC,CACtD,IAAM,EAAI,EAAM,UAEhB,OADK,EACE,MAAM,QAAQ,CAAC,EAAI,EAAI,CAAC,CAAC,EADjB,CAAC,CAElB,CAAC,EAGK,IAAA,EAAoB,EAAA,SAAA,KACxB,EAAc,MAAM,KACjB,IAAO,EAAE,UAAY,KAAS,EAAa,EAAoB,EAAE,KAAK,CAAC,CAC1E,CACF,EAGA,SAAS,IAA2C,CAClD,IAAM,EAAkC,CAAC,EACzC,IAAK,IAAM,KAAK,EAAc,MAAO,CACnC,IAAM,EAAM,EAAoB,EAAE,KAAK,EAClC,EAAa,CAAG,IAAG,EAAO,EAAE,OAAS,EAC5C,CACA,OAAO,CACT,CAEA,IAAM,GAAA,EAAuB,EAAA,SAAA,KAC3B,GAAkB,OAAS,EAAM,mBAC7B,EAAM,mBACN,EAAM,WACZ,EAIM,GAAA,EAAqB,EAAA,SAAA,KACzB,EAAM,UAAY,EAAc,MAAM,OAAS,IAAA,GAAY,EAAqB,KAClF,EAEA,eAAe,GAAS,EAA8D,CACpF,GAAI,CAEF,OAAO,MADgB,EAAS,IAAI,EAAM,SAAU,CAAE,EAAA,CACtC,IAClB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,eAAe,GAAU,EAAqB,CAC5C,EAAa,MAAQ,MAAM,GAAS,CAAE,CACxC,CAGA,eAAe,GAAY,EAAwD,CAcjF,OAAO,MAba,QAAQ,IAC1B,EAAQ,IAAI,KAAO,IAAM,CACvB,GAAI,GAAK,OAAO,GAAM,SAAU,CAC9B,IAAM,EAAM,EACZ,GAAI,EAAM,eAAe,EAAK,OAAO,EACrC,IAAM,EAAK,EAAI,EAAM,aACrB,OAAO,GAAM,KAA+C,KAAxC,MAAM,GAAS,CAAqB,CAC1D,CAGA,OADc,EAAc,MAAM,KAAM,GAAM,EAAE,EAAM,eAAiB,CAChE,GAAU,MAAM,GAAS,CAAoB,CACtD,CAAC,CACH,EAAA,CACa,OAAQ,GAAoC,IAAM,IAAI,CACrE,CAEA,eAAe,EAAO,EAAe,CAEnC,GAAI,GAAkB,MAAO,CAC3B,EAAY,MAAQ,CAAC,EACrB,MACF,CACA,EAAU,MAAQ,GAClB,GAAI,CACF,IAAM,EAAkC,CACtC,UAAW,GACX,GAAG,EAAM,eACT,GAAG,GAAgB,CACrB,EACI,IAAO,EAAO,OAAS,GAC3B,IAAM,EAAW,MAAM,EAAS,KAAK,EAAM,SAAU,CAAM,EAC3D,EAAY,MAAQ,EAAS,IAC/B,MAAQ,CACN,EAAY,MAAQ,CAAC,CACvB,QAAU,CACR,EAAU,MAAQ,EACpB,CACF,CAEA,SAAS,EAAS,EAA0B,CAC1C,IAAM,EAAQ,EAAM,OAAS,GAC7B,GAAI,EAAM,OAAS,EAAM,UAAW,CAClC,EAAY,MAAQ,CAAC,EACrB,MACF,CACI,GAAe,aAAa,CAAa,EAC7C,EAAgB,eAAiB,EAAO,CAAK,EAAG,GAAG,CACrD,CAEA,SAAS,GAAS,EAA2C,CAEvD,EAAM,WACV,EAAa,MAAQ,EAAM,MAC3B,EAAK,oBAAqB,EAAM,KAAK,EACvC,CAGA,SAAS,GAAW,EAAgB,CAC7B,EAAM,WACX,EAAc,MAAQ,EACpB,MAAM,QAAQ,CAAK,EAAK,EAAsC,CAAC,CACjE,EACA,EAAc,EAChB,CAEA,SAAS,IAAU,CAKb,EAAM,WACV,EAAa,MAAQ,KACrB,EAAc,EAChB,EAGA,EAAA,EAAA,MAAA,KACQ,EAAM,WACZ,KAAO,IAAW,CAChB,GAAI,EAAM,SAAU,CAClB,IAAM,EAAQ,GAAU,KAAO,CAAC,EAAI,MAAM,QAAQ,CAAM,EAAI,EAAS,CAAC,CAAM,EACtE,EAAS,EAAM,IAAI,CAAK,EACxB,EAAS,EAAc,MAAM,IAAK,GAAM,EAAE,EAAM,YAAY,EAElE,GAAI,EAAO,SAAW,EAAO,QAAU,EAAO,OAAO,EAAG,IAAM,IAAM,EAAO,EAAE,EAAG,OAChF,EAAc,MAAQ,MAAM,GAAY,CAAK,EAC7C,MACF,CACA,GAAI,GAAU,KAAM,CAClB,GAAI,OAAO,GAAW,UAAY,GAAmB,EAAM,eAAe,EAAQ,CAChF,EAAa,MAAQ,EACrB,MACF,EACI,CAAC,EAAa,OAAS,EAAa,MAAM,EAAM,eAAiB,IACnE,MAAM,GAAU,CAAyB,CAE7C,KACE,GAAa,MAAQ,IAEzB,EACA,CAAE,UAAW,EAAK,CACpB,EAMA,IAAM,GAAA,EAAe,EAAA,IAAA,CAAI,EAAK,EACxB,GAAA,EAAY,EAAA,IAAA,CAA+B,CAAC,CAAC,EAC7C,GAAA,EAAe,EAAA,IAAA,CAAI,EAAK,EACxB,GAAA,EAAc,EAAA,IAAA,CAAI,EAAE,EACpB,GAAA,EAAY,EAAA,IAAA,CAAI,CAAC,EACjB,IAAA,EAAgB,EAAA,IAAA,CAAI,EAAE,EACtB,GAAA,EAAoB,EAAA,IAAA,CAAI,CAAC,EACzB,IAAA,EAAiB,EAAA,IAAA,CAAgE,IAAI,EAGrF,GAAA,EAAoB,EAAA,SAAA,KAA0C,CAClE,IAAM,EAAM,GAAe,MAE3B,OADK,EACE,MAAM,QAAQ,CAAG,EAAI,EAAM,CAAC,CAAG,EADrB,CAAC,CAEpB,CAAC,EACK,IAAA,EAAiB,EAAA,IAAA,CAAmB,IAAI,EACxC,IAAA,EAAiB,EAAA,IAAA,CAAgB,CAAC,EACpC,EAAyD,KAGvD,IAAA,EAAa,EAAA,IAAA,CAA+C,CAAC,CAAC,EAC9D,GAAA,EAAiB,EAAA,IAAA,CAAiB,CAAC,CAAC,EACpC,IAAA,EAAe,EAAA,IAAA,CAA6B,CAAC,CAAC,EAGpD,eAAe,IAAe,CACvB,KAAM,QACX,GAAI,CACF,GAAM,CAAE,MAAK,WAAY,MAAM,EAAM,QAAQ,EAAU,KAAK,EAC5D,GAAW,MAAQ,GAAO,CAAC,EAC3B,EAAe,MAAQ,GAAW,CAAC,EACnC,IAAM,EAAmC,CAAC,EAC1C,IAAK,IAAM,KAAO,EAAU,MAAO,CACjC,IAAM,EAAK,OAAO,EAAI,EAAM,YAAY,EACpC,GAAW,MAAM,EAAG,EAAE,SAAQ,EAAQ,GAAM,GAClD,CACA,GAAa,MAAQ,CACvB,MAAQ,CAEN,GAAW,MAAQ,CAAC,EACpB,EAAe,MAAQ,CAAC,EACxB,GAAa,MAAQ,CAAC,CACxB,CACF,CAEA,SAAS,GAAU,EAAyD,CAC1E,OAAO,GAAW,MAAM,OAAO,EAAI,EAAM,YAAY,IAAM,CAAC,CAC9D,CAGA,IAAM,IAAA,EAAY,EAAA,IAAA,CAAoB,CAAC,CAAC,EAClC,IAAA,EAAgB,EAAA,SAAA,KAEhB,EAAM,YAAY,OAAe,GAE9B,GAAU,MAAM,OAAS,CACjC,EAGK,IAAA,EAAa,EAAA,SAAA,KAAe,EAAM,WAAa,GAAc,KAAK,EAClE,IAAA,EAAW,EAAA,SAAA,KAAe,EAAM,SAAW,GAAc,KAAK,EAC9D,IAAA,EAAa,EAAA,SAAA,KAAe,EAAM,WAAa,GAAc,KAAK,EAClE,IAAA,EAAgB,EAAA,SAAA,KAAe,GAAS,OAAS,GAAW,KAAK,EAGjE,IAAA,EAAa,EAAA,SAAA,KACb,EAAM,YAAY,OAAe,EAAM,WACpC,GAAwB,GAAU,KAAK,CAC/C,EAGK,IAAA,EAAe,EAAA,SAAA,KACf,EAAM,aAAa,OAAe,EAAM,YACxC,EAAM,SAAS,OACV,EAAM,QAAQ,IAAK,IAAO,CAC/B,MAAO,EAAE,MACT,OAAQ,EAAE,OACV,SAAU,EACZ,EAAE,EAEA,GAAU,MAAM,OACX,GAAyB,GAAU,KAAK,EAE1C,CAAC,CAAE,MAAO,EAAM,YAAa,OAAQ,EAAM,YAAa,SAAU,EAAK,CAAC,CAChF,EAMD,eAAe,IAAiB,CAE9B,GAAI,GAAkB,MAAO,CAC3B,EAAU,MAAQ,CAAC,EACnB,EAAkB,MAAQ,EAC1B,MACF,CACA,EAAa,MAAQ,GACrB,GAAI,CACF,IAAM,EAAkC,CACtC,KAAM,EAAU,MAChB,UAAW,GAAc,MACzB,GAAG,EAAM,eACT,GAAG,GAAgB,CACrB,EACI,EAAY,QAAO,EAAO,OAAS,EAAY,OAC/C,GAAe,OAAS,GAAe,QAAU,IACnD,EAAO,SACL,GAAe,QAAU,GAAK,IAAI,GAAe,QAAU,GAAe,OAE9E,IAAM,EAAW,MAAM,EAAS,KAAK,EAAM,SAAU,CAAM,EAC3D,EAAU,MAAQ,EAAS,KAC3B,EAAkB,MAAQ,EAAS,KACnC,GAAkB,EAGd,EAAS,QAAQ,QAAU,CAAC,EAAM,SAAS,QAAU,CAAC,EAAM,YAAY,SAC1E,GAAU,MAAQ,EAAS,OAAO,OAEtC,MAAQ,CACN,EAAU,MAAQ,CAAC,EACnB,EAAkB,MAAQ,CAC5B,QAAU,CACR,EAAa,MAAQ,EACvB,CACF,CAEA,SAAS,GAAe,EAAkB,CAEpC,EAAE,MAAQ,MAAQ,CAAC,EAAM,WAC3B,EAAE,eAAe,EACjB,GAAU,EAEd,CAEA,SAAS,IAAY,CACf,EAAM,WACV,EAAY,MAAQ,GACpB,EAAU,MAAQ,EAClB,GAAe,MAAQ,KACvB,GAAe,MAAQ,EAEvB,GAAe,MAAQ,EAAM,SAAW,CAAC,GAAG,EAAc,KAAK,EAAI,KACnE,EAAa,MAAQ,GACrB,GAAe,EACjB,CAEA,SAAS,GAAY,EAAuC,CAC1D,EAAU,MAAQ,EAAM,KAAO,EAC/B,GAAc,MAAQ,EAAM,KAC5B,GAAe,CACjB,CAEA,SAAS,GAAY,EAAuD,CAC1E,GAAe,MAAS,EAAM,WAAwB,KACtD,GAAe,MAAQ,EAAM,WAAa,EAC1C,EAAU,MAAQ,EAClB,GAAe,CACjB,CAEA,SAAS,IAAwB,CAC/B,GAAI,EAAM,SAAU,CAClB,EAAc,MAAQ,EAAY,EAAkB,KAAK,EACzD,EAAc,EACd,EAAa,MAAQ,GACrB,MACF,CACA,IAAM,EAAM,EAAkB,MAAM,GAC/B,IACL,EAAa,MAAQ,EACrB,EAAK,oBAAqB,CAAG,EAC7B,EAAa,MAAQ,GACvB,CAEA,SAAS,GAAc,EAA0C,CAG/D,GAAI,EAAM,SAAU,CAClB,GAAe,MAAQ,EAAY,CAAC,GAAG,EAAkB,MAAO,EAAM,IAAI,CAAC,EAC3E,MACF,CACA,EAAa,MAAQ,EAAM,KAC3B,EAAK,oBAAqB,EAAM,IAAI,EACpC,EAAa,MAAQ,EACvB,CAGA,IAAM,IAAA,EAAmB,EAAA,IAAA,CAAkC,IAAI,EACzD,IAAA,EAAiB,EAAA,IAAA,CAAwB,IAAI,EAC/C,GAAe,GAEnB,SAAS,IAAc,CACrB,GAAe,IACf,EAAA,EAAA,SAAA,KAAe,CACb,IAAM,EAAK,GAAiB,OAAO,IACnC,GAAI,QAAQ,EACZ,GAAI,SAAS,CACf,CAAC,CACH,CAEA,SAAS,IAAiB,EACxB,EAAA,EAAA,SAAA,KAAe,EACD,GAAe,OAAO,cAA2B,yBAAyB,EAAA,EACjF,MAAM,CACb,CAAC,CACH,CAEA,SAAS,GAAqB,EAAkB,CAC9C,GAAI,EAAE,MAAQ,SAAW,EAAE,YAAa,OACxC,EAAE,eAAe,EACjB,IAAM,EAAI,EAAY,MAAM,KAAK,EAEjC,GAAI,IAAM,IAAM,IAAM,GAAc,CAClC,GAAe,EACf,MACF,CAEI,GAAkB,aAAa,CAAgB,EACnD,GAAe,EACf,EAAU,MAAQ,EAClB,GAAe,CACjB,CAEA,SAAS,GAAmB,EAAkB,CAGxC,EAAE,MAAQ,UACZ,EAAE,eAAe,EACjB,EAAE,gBAAgB,GAEd,EAAM,UAAY,EAAkB,MAAM,SAAQ,GAAsB,EAGhF,EAEA,EAAA,EAAA,MAAA,CAAM,MAAmB,CACnB,GAAkB,aAAa,CAAgB,EACnD,EAAmB,eAAiB,CAClC,EAAU,MAAQ,EAClB,GAAe,EAAY,MAAM,KAAK,EACtC,GAAe,CACjB,EAAG,GAAG,CACR,CAAC,GAID,EAAA,EAAA,MAAA,KACQ,EAAc,MAAM,IAAK,GAAM,EAAoB,EAAE,KAAK,CAAC,GAChE,EAAO,IAAY,CAKlB,GAJI,EAAa,QACf,EAAU,MAAQ,EAClB,GAAe,GAEb,CAAC,EAAS,OACd,IAAM,EAAQ,EAAM,MAAM,EAAG,IAAM,IAAM,EAAQ,EAAE,EAC7C,EAAkB,EAAQ,KAAM,GAAM,CAAC,EAAa,CAAC,CAAC,EACxD,MAAC,GAAS,CAAC,GACf,IAAI,EAAM,SAAU,CAClB,GAAI,CAAC,EAAc,MAAM,OAAQ,OACjC,EAAc,MAAQ,CAAC,EACvB,EAAc,EACd,MACF,CACI,EAAa,QACf,EAAa,MAAQ,KACrB,EAAK,oBAAqB,IAAI,EAHhC,CAKF,CACF,EAMA,IAAM,IAAA,EAAoB,EAAA,IAAA,CAAI,EAAK,EAC7B,IAAA,EAAa,EAAA,IAAA,CAAI,EAAK,EACtB,IAAA,EAAc,EAAA,IAAA,CAAoC,IAAI,EACtD,IAAA,EAAW,EAAA,SAAA,CAAkC,CAAC,CAAC,EAE/C,IAAA,EAAY,EAAA,SAAA,KAAe,GAAY,QAAU,IAAI,EACrD,IAAA,EAAkB,EAAA,SAAA,KAAgB,GAAU,MAAQ,kBAAoB,eAAgB,EAE9F,SAAS,IAAuC,CAC9C,IAAM,EAAoC,CAAC,EAC3C,IAAK,IAAM,KAAK,GAAW,MACzB,EAAS,EAAE,OACT,EAAE,eAAiB,IAAA,GAIf,KAHA,OAAO,EAAE,cAAiB,WACvB,EAAE,aAA+B,EAClC,EAAE,aAGZ,OAAO,CACT,CAEA,SAAS,IAAY,CACnB,IAAM,EAAO,GAAY,EACzB,IAAK,IAAM,KAAO,OAAO,KAAK,EAAQ,EACpC,OAAO,GAAS,GAElB,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,CAAI,EAC1C,GAAS,GAAO,CAEpB,CAEA,SAAS,IAAiB,CACxB,GAAY,MAAQ,KACpB,GAAU,EACV,GAAkB,MAAQ,EAC5B,CAEA,SAAS,GAAa,EAA+B,CACnD,GAAY,MAAQ,EACpB,IAAK,IAAM,KAAK,GAAW,MACzB,GAAS,EAAE,OAAS,EAAK,EAAE,SAAW,IAAA,GAA4B,KAAhB,EAAK,EAAE,OAE3D,GAAkB,MAAQ,EAC5B,CAEA,SAAS,GAAa,EAAe,EAAgB,CACnD,GAAS,GAAS,CACpB,CAEA,eAAe,IAAW,CACxB,GAAW,MAAQ,GACnB,GAAI,CACF,IAAM,EAAU,CAAE,GAAG,EAAS,EAG9B,IAAK,IAAM,KAAK,GAAW,MAAO,CAChC,IAAM,EAAM,EAAQ,EAAE,OACtB,GAAI,EAAE,OAAS,MAAwB,OAAO,GAAQ,UAA/B,EAAyC,CAC9D,IAAM,EAAM,EAAE,aAAe,KAC7B,EAAQ,EAAE,OAAU,EAAgC,IAAQ,CAC9D,CACF,CAEA,IAAI,EAEJ,GAAI,GAAU,OAAS,GAAY,MAAO,CACxC,IAAM,EAAK,GAAY,MAAM,EAAM,aACnC,EAAW,MAAM,EAAS,OAAO,EAAM,SAAU,EAAuB,CAAO,EAE/E,IAAM,EAAM,EAAU,MAAM,UAAW,GAAM,EAAE,EAAM,eAAiB,CAAE,EACpE,IAAQ,KACV,EAAU,MAAM,GAAO,EAAS,MAElC,EAAM,QAAQ,iCAAiC,CACjD,KAAO,CAIL,IAAM,EAAS,GAAgB,EAC/B,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAM,EAAG,CACjD,IAAM,EAAU,EAAQ,IACpB,GAAqC,MAAQ,IAAY,MAC3D,EAAQ,GAAO,EAEnB,CACA,EAAW,MAAM,EAAS,OAAO,EAAM,SAAU,CAAO,EAExD,EAAU,MAAM,QAAQ,EAAS,IAAI,EACrC,EAAkB,QAClB,EAAM,QAAQ,6BAA6B,CAC7C,CAEA,GAAkB,MAAQ,GAC1B,GAAY,MAAQ,KAGpB,GAAe,MAAQ,EAAM,SACzB,EAAY,CAAC,GAAG,EAAkB,MAAO,EAAS,IAAI,CAAC,EACvD,EAAS,IACf,OAAS,EAAK,CACZ,EAAM,MAAM,EAAA,EAAgB,EAAK,yBAAyB,CAAC,CAC7D,QAAU,CACR,GAAW,MAAQ,EACrB,CACF,CAEA,SAAS,GAAc,EAA+B,CACpD,EAAoB,SAAY,CAC9B,GAAI,CACF,IAAM,EAAK,EAAK,EAAM,aACtB,MAAM,EAAS,OAAO,EAAM,SAAU,CAAqB,EAC3D,IAAM,EAAM,EAAU,MAAM,UAAW,GAAM,EAAE,EAAM,eAAiB,CAAE,EACpE,IAAQ,KACV,EAAU,MAAM,OAAO,EAAK,CAAC,EAC7B,EAAkB,SAGhB,EAAM,UACJ,EAAc,MAAM,KAAM,GAAM,EAAE,EAAM,eAAiB,CAAE,IAC7D,EAAc,MAAQ,EAAc,MAAM,OAAQ,GAAM,EAAE,EAAM,eAAiB,CAAE,EACnF,EAAc,GAEhB,GAAe,MAAQ,EAAkB,MAAM,OAAQ,GAAM,EAAE,EAAM,eAAiB,CAAE,IAEpF,EAAa,OAAS,EAAa,MAAM,EAAM,eAAiB,IAClE,EAAa,MAAQ,KACrB,EAAK,oBAAqB,IAAI,GAE5B,EAAkB,MAAM,EAAE,GAAG,EAAM,eAAiB,IACtD,GAAe,MAAQ,OAG3B,EAAM,QAAQ,+BAA+B,CAC/C,OAAS,EAAK,CACZ,EAAM,MAAM,EAAA,EAAgB,EAAK,0BAA0B,CAAC,CAC9D,CACF,CAAC,CACH,0HAIE,EAAA,EAAA,mBAAA,CAiFM,OAAA,EAjFN,EAAA,WAAA,CAiFM,CAhFJ,MAAK,CAAC,mBAAkB,CAAA,6BACgB,EAAA,KAAa,CAAA,EACpD,MAAO,EAAA,KACAC,EAAAA,EAAAA,MAAM,EAAA,EAEd,EAAA,EAAA,YAAA,EAAA,EAoDe,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAnDT,QAAA,QAAJ,IAAI,EACH,cAAa,EAAA,MACb,YAAa,EAAA,MACb,SAAU,EAAA,SACV,eAAc,EAAA,YACd,YAAa,EAAA,MACb,SAAU,EAAA,SACV,kBAAiB,EAAA,eACjB,QAAS,EAAA,MACT,aAAY,GACb,MAAA,GACC,WAAU,EACV,aAAa,GACb,sBAAoB,GACb,WACP,UAAS,EAKM,GAAA,EAAA,EAAA,YAAA,CAAA,CAAA,EAAA,CAAA,EAAA,CAAA,EAAA,kBAAA,CAAoB,KAAA,SAClC,IAAA,EAAA,EAAA,QAAA,EAKO,CANqC,YAAM,EAAA,EAClD,EAAA,mBAAA,CAKO,OALP,GAKO,EAAA,EAJL,EAAA,mBAAA,CAA6D,OAA7D,IAAA,EAA6D,EAAA,gBAAA,CAAzB,EAAQ,CAAM,CAAA,EAAA,CAAA,EACtC,EAAc,CAAM,IAAA,EAAhC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAc,CAAM,CAAA,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,SAQb,EAAA,IAAA,GAAA,EAAA,SAAA,CAAW,KAAA,OACzB,IAAA,EAAA,EAAA,QAAA,EAQE,CAT+B,QAAO,QAAO,oBAAc,CAEpD,CAAA,EAAA,UAAY,EAAQ,EAAA,UAAA,EAD7B,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,MAAM,4CACL,MAAO,EAAQ,CAAK,EACrB,UAAA,GACC,QAAO,GACP,SAAM,GAAE,GAAa,EAAQ,CAAc,qCAL7B,EAAQ,CAAK,SAAlB,CAAA,IAAV,EAA8B,CAQnB,CAAA,CAAA,EAAA,IAAU,EAAA,UAAA,EADvB,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAHA,MAAM,uEACL,MAAK,IAAM,EAAA,QACX,QAAO,2BAHO,EAAA,aAAL,CAAA,IAAV,EAAiC,CAKnC,CAAA,CAAA,IAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAoD,OAApD,EAAoD,EAAA,CAAA,uIAIhD,EAAA,OAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUS,SAAA,OAPP,KAAK,SACL,gBAAA,GACA,SAAS,KACT,MAAM,yBACL,QAAO,CAER,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAAA,CAAA,GAPP,gBAAL,CAAA,IAAV,EAAwB,sCAS1B,EAAA,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUS,SAAA,CARP,KAAK,SACJ,SAAU,EAAA,SACX,gBAAA,GACA,SAAS,KACT,MAAM,2BACL,QAAO,EAER,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAA0B,IAAA,CAAvB,MAAM,cAAc,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,GAAA,CAAA,GARR,wBAAL,CAAA,IAAV,EAAgC,WAapC,EAAA,EAAA,YAAA,EAAA,EA+IS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CA9IC,QAAS,EAAA,MAAA,mBAAA,AAAA,EAAA,KAAA,GAAA,EAAY,MAAA,EAC5B,aAAA,EAAW,EAAA,MAAA,CAAA,CAAA,CAAO,EAAE,OAAO,OAAK,OAChC,OAAQ,EAAA,cAAY,YACpB,MAAO,CAAA,MAAA,MAAA,EACR,MAAA,GACC,UAAW,GACZ,MAAM,0BACL,OAAM,KAwHI,QAAA,EAAM,EAAA,QAAA,KAaT,EAAA,EAZN,EAAA,mBAAA,CAYM,MAZN,GAYM,EAAA,EAXJ,EAAA,YAAA,EAAA,EAAmF,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAA3E,MAAM,WAAW,SAAS,YAAY,KAAA,GAAM,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,MAAY,EACvE,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CARC,MAAoB,EAAA,UAAY,EAAA,MAAkB,OAAsC,eAAA,EAAA,MAAkB,OAAM,GAAA,aAKjH,KAAK,cACJ,SAAQ,CAAG,EAAA,UAAQ,CAAK,EAAA,MAAkB,OAC1C,QAAO,qCAjId,SAAA,EAAA,EAAA,QAAA,KAqBM,EAAA,EArBN,EAAA,mBAAA,CAqBM,MArBN,GAqBM,EAAA,EApBJ,EAAA,YAAA,EAAA,EAUY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAVD,MAAM,iCAAiC,EAAA,CAChD,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANI,QAAA,mBAAJ,IAAI,GACK,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAW,MAAA,EACpB,YAAY,eACZ,MAAM,SACN,UAAA,GACC,UAAS,gCAGd,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MARN,GAQM,CANI,GAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAJA,MAAM,OACN,KAAK,aACL,KAAK,QACJ,QAAO,EAKd,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA6FM,MAAA,CA7FG,QAAA,iBAAJ,IAAI,GAAmC,iBAAA,EAC1C,EAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EA2FY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CA1FF,UAAW,GAAA,MAAA,qBAAA,AAAA,EAAA,KAAA,GAAA,GAAc,MAAA,EACzB,gBAAe,GAAA,MAAA,wBAAA,AAAA,EAAA,KAAA,GAAA,GAAY,MAAA,EAClC,qBAAoB,GACpB,MAAO,EAAA,MACP,QAAS,EAAA,MACV,UAAA,GACA,KAAA,GACA,eAAA,GACA,iBAAA,GACA,KAAK,QACJ,KAAM,GAAA,MACN,gBAAe,EAAA,MACf,aAAY,GAAA,OAAkB,IAAA,GAC9B,aAAY,GAAA,MACZ,iBAAgB,EAAA,SAAQ,WAAA,SACxB,WAAU,EAAA,YACV,OAAM,GACN,OAAI,AAAA,EAAA,KAAG,GAAW,GAAW,CAAA,UAAc,EAAE,UAAS,UAAa,EAAE,SAAS,CAAA,EAC9E,cAAc,uBAqEJ,OAAA,EAAK,EAAA,QAAA,KACsD,CAApE,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAoE,MAAA,CAA/D,MAAM,wBAAwB,EAAC,6BAA0B,EAAA,CAAA,CAAA,EApEhE,SAAA,EAAA,EAAA,QAAA,KAAwF,EAAxF,EAAA,EAAA,YAAA,EAAA,EAAwF,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAA/E,iBAAgB,EAAA,SAAQ,WAAA,SAA0B,eAAa,4CACxE,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAmBS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAlBO,GAAA,MAAP,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAmBS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAjBN,IAAK,EAAI,MACT,MAAO,EAAI,MACX,OAAQ,EAAI,OACZ,SAAW,EAAkB,UAAQ,GACrC,OAAA,EAAK,EAAA,eAAA,CAAG,EAAkB,KAAK,IAErB,MAAA,EAAI,EAAA,QAAA,EAMX,CANe,UAAI,CAEZ,EAAkB,OAAA,EAD3B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAKE,GAAA,OAHC,OAAQ,EACR,MAAO,EAAK,EAAI,OAChB,WAAU,sCAEb,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEW,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EADN,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAK,EAAI,MAAK,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,2DAMT,GAAA,QAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAwBS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAxBoB,OAAO,GAAI,MAAO,CAAA,MAAA,MAAA,IAClC,MAAA,EAAI,EAAA,QAAA,EAqBP,CArBW,UAAI,EAAA,EACrB,EAAA,mBAAA,CAoBM,MApBN,GAoBM,CAlBI,GAAA,OAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,KAAK,eACL,KAAA,GACA,QAAA,GACA,KAAK,QACJ,QAAK,GAAE,GAAa,CAAI,4BALV,gBAAL,CAAA,IAAV,EAAwB,CAQlB,CAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAAA,GAAA,OAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OANA,KAAK,cACL,KAAA,GACA,QAAA,GACA,KAAK,QACL,SAAS,SACR,QAAK,GAAE,GAAc,CAAI,4BANX,iBAAL,CAAA,IAAV,EAAyB,qFAajB,EAAA,CAAA,EAAA,QAAA,CAAU,KAAA,YACxB,IAAA,EAAA,EAAA,QAAA,EAaQ,CAd6B,UAAI,EAAA,EACzC,EAAA,mBAAA,CAaQ,QAbR,GAaQ,EAAA,EAZN,EAAA,mBAAA,CAIQ,QAAA,KAAA,EAAA,EAHN,EAAA,mBAAA,CAEK,KAAA,KAAA,GAAA,EADH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAAwE,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAAtD,EAAA,MAAP,KAAX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAwE,KAAA,CAArC,IAAK,EAAI,KAAU,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAI,MAAM,EAAA,CAAA,EAGpE,EAAA,GAAA,EAAA,CAAA,CAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMQ,QAAA,KAAA,GAAA,EALN,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAIK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAJkB,GAAU,CAAI,GAAzB,EAAK,MAAjB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIK,KAAA,CAJoC,IAAK,CAAC,EAAA,GAAA,EAC7C,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAEK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAFa,EAAA,MAAP,KAAX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEK,KAAA,CAF8B,IAAK,EAAI,KACvC,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAI,EAAI,QAAK,GAAA,EAAA,CAAA,gOAgCxB,GAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAiBE,GAAA,OAfC,QAAS,GAAA,MACT,MAAO,GAAA,MACP,OAAQ,GAAA,MACR,YAAW,GACX,aAAY,GAAA,MACZ,OAAQ,GAAA,MACR,MAAO,EAAA,YACP,mBAAc,AAAA,EAAA,KAAU,GAAC,CAAe,GAAA,MAAoB,EAAe,IAAG,GAAA,MAAW,OAMzF,iBAAc,GACd,OAAM,klBE5hCX,IAAM,EAAQ,EA2BR,EAAO,EAEP,EAAK,EAAA,EAAa,CACtB,aAAgB,EAAM,SACtB,gBAAmB,EAAM,WAC3B,CAAC,EAEK,GAAA,EAAQ,EAAA,IAAA,CAAI,EAAE,EACd,GAAA,EAAY,EAAA,IAAA,CAAiB,IAAI,EACjC,GAAA,EAAK,EAAA,IAAA,CAAyC,IAAI,EAElD,GAAA,EAAe,EAAA,SAAA,MAAgB,CAAE,KAAM,EAAG,KAAK,CAAE,EAAE,EACnD,GAAA,EAAa,EAAA,SAAA,KAAe,EAAG,OAAO,EAAM,OAAO,GAAK,IAAA,EAAS,EACjE,GAAA,EAAa,EAAA,SAAA,KAAe,EAAG,OAAO,EAAM,OAAO,GAAK,IAAA,EAAS,GAGvE,EAAA,EAAA,MAAA,KACQ,EAAM,WACX,GAAM,CACL,IAAM,EAAI,EAAG,OAAO,CAAC,EACrB,EAAU,MAAQ,EAClB,EAAM,MAAQ,EAAG,UAAU,CAAC,CAC9B,EACA,CAAE,UAAW,EAAK,CACpB,EAEA,SAAS,EAAO,EAAmB,CACjC,EAAK,oBAAqB,EAAG,QAAQ,CAAI,CAAC,CAC5C,CAEA,SAAS,GAAe,CACtB,GAAI,EAAM,QAAU,GAAI,CACtB,EAAO,IAAI,EACX,MACF,CAEA,IAAM,EAAI,EAAG,YAAY,EAAM,KAAK,EAChC,GAAG,EAAO,CAAC,CACjB,CAEA,SAAS,GAAS,CAEhB,EAAM,MAAQ,EAAG,UAAU,EAAG,OAAO,EAAM,UAAU,CAAC,CACxD,CAEA,SAAS,EAAU,EAAkB,CAC/B,EAAE,MAAQ,MAAQ,CAAC,EAAM,WAC3B,EAAE,eAAe,EACjB,EAAS,EAEb,CAEA,SAAS,EAAO,EAAgB,CAE9B,EADW,GAAyB,IAC5B,EACH,EAAM,UAAU,EAAG,OAAO,KAAK,CACtC,CAEA,SAAS,GAAW,CAClB,EAAO,EAAG,MAAM,CAAC,CACnB,CAEA,SAAS,GAAa,CACpB,EAAO,IAAI,EACX,EAAG,OAAO,KAAK,CACjB,CAEA,SAAS,EAAO,EAAU,CACpB,EAAM,UACV,EAAG,OAAO,OAAO,CAAC,CACpB,QAEA,EAAA,EAAA,UAAA,KAAgB,CACV,EAAM,SAAW,EAAM,YAAc,MAAM,EAAS,CAC1D,CAAC,WAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAkDM,MAlDN,GAkDM,EAAA,EAjDJ,EAAA,YAAA,EAAA,EAuBY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,KAAA,CAtBV,SAAA,EAAA,EAAA,QAAA,KAaE,EAAA,EAbF,EAAA,eAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EAaE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAZC,GAAI,EAAA,QACI,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAK,MAAA,EAEb,YAAa,EAAA,cAAA,EAAe,EAAA,MAAA,CAAA,CAAA,CAAE,CAAC,YAAW,EAC1C,SAAU,EAAA,SACV,QAAS,EAAA,QACV,MAAA,GACA,aAAa,MACb,UAAU,UACT,QAAO,EACD,SACG,yEATD,CAAA,EAAA,CAAA,EAAA,EAAA,EAAA,MAAA,CAAA,CAAA,EAAA,EAAA,KAAY,CAAA,CAAA,GAAA,EAWvB,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,mCAAkC,CAAA,6BACA,EAAA,QAAQ,CAAA,CAAA,EAChD,KAAK,SACL,SAAS,KACT,aAAW,mBACV,QAAO,0BAIZ,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAuBU,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAvBG,QAAA,KAAJ,IAAI,IACX,SAAA,EAAA,EAAA,QAAA,KAqBM,EAAA,EArBN,EAAA,mBAAA,CAqBM,MArBN,GAqBM,EAAA,EApBJ,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPC,cAAa,EAAA,OAAa,IAAA,GAC3B,OAAA,GACC,YAAW,EAAA,SACZ,cAAY,KACX,WAAU,EAAA,MACV,WAAU,EAAA,MACV,sBAAoB,0DAEvB,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUM,MAVN,GAUM,EAAA,EATJ,EAAA,YAAA,EAAA,EAA2D,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAnD,MAAM,OAAO,KAAA,GAAK,KAAK,QAAS,QAAO,CAEvC,CAAA,EAAA,EAAA,YAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,MAAM,SACN,KAAA,GACA,KAAK,QACL,SAAS,YACR,QAAO,4VEnKpB,IAAM,EAAQ,EA0BR,EAAO,EAIP,GAAA,EAAiB,EAAA,SAAA,KAAe,EAAM,SAAW,EAAM,SAAW,KAAO,IAAA,GAAU,EAEnF,GAAA,EAAW,EAAA,SAAA,KAAe,CAAC,CAAC,EAAe,OAAS,CAAC,CAAC,EAAM,MAAM,EAElE,GAAA,EAAU,EAAA,SAAA,KAAe,CAC7B,IAAM,EAAI,EAAM,WAEhB,OADI,GAAM,MAA2B,OAAO,MAAM,CAAC,EAAU,GACtD,IAAI,KAAK,aAAa,EAAM,OAAQ,CACzC,sBAAuB,EAAM,SAC7B,sBAAuB,EAAM,QAC/B,CAAC,CAAC,CAAC,OAAO,CAAC,CACb,CAAC,EAED,SAAS,EAAQ,EAAc,CAC7B,IAAM,EAAU,EAAM,OAA4B,MAAM,QAAQ,MAAO,EAAE,EACzE,GAAI,CAAC,EAAQ,CACX,EAAK,oBAAqB,IAAI,EAC9B,MACF,CAEA,IAAM,EAAM,OAAO,CAAM,EAAa,IAAI,EAAM,SAChD,EAAK,oBAAqB,CAAG,CAC/B,cAIoB,EAAA,QAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAYa,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAZe,MAAM,kBAChC,SAAA,EAAA,EAAA,QAAA,KAA6E,CAAtD,EAAA,QAAA,EAAvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAA6E,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,IAAA,CAAA,EAAA,CAAtC,SAAA,EAAA,EAAA,QAAA,KAAoB,EAAA,EAAjB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,KAAc,EAAA,CAAA,CAAA,CAAA,0CACxD,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPC,cAAa,EAAA,MACd,UAAU,UACV,MAAM,uBACL,YAAa,EAAA,YACb,SAAU,EAAA,SACV,QAAS,EAAA,QACF,sEAEa,EAAA,SAAA,EAAvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAA6D,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,IAAA,CAAA,EAAA,CAA9B,SAAA,EAAA,EAAA,QAAA,KAAY,EAAA,EAAT,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,MAAM,EAAA,CAAA,CAAA,CAAA,8CAE1C,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OARC,cAAa,EAAA,MACd,UAAU,UACV,MAAA,GACA,MAAM,uBACL,YAAa,EAAA,YACb,SAAU,EAAA,SACV,QAAS,EAAA,QACF,yOEwDN,GAAK,sSAnIX,IAAM,EAAU,EAAA,EAAW,EAQrB,EAAQ,EAyBR,EAAO,EAQP,GAAA,EAAY,EAAA,IAAA,CAAmB,IAAI,GACzC,EAAA,EAAA,MAAA,KACQ,EAAM,WACX,GAAM,CACL,AAEE,EAAU,SADV,IAAI,gBAAgB,EAAU,KAAK,EACjB,MAEhB,aAAa,OAAM,EAAU,MAAQ,IAAI,gBAAgB,CAAC,EAChE,EACA,CAAE,UAAW,EAAK,CACpB,GACA,EAAA,EAAA,gBAAA,KAAsB,CAChB,EAAU,OAAO,IAAI,gBAAgB,EAAU,KAAK,CAC1D,CAAC,EAED,IAAM,GAAA,EAAa,EAAA,SAAA,KACb,EAAM,sBAAsB,KAAa,EAAU,MACnD,OAAO,EAAM,YAAe,UAAY,EAAM,WAAmB,EAAM,WACpE,IACR,EAKK,GAAA,EAAY,EAAA,IAAA,CAA6B,IAAI,EACnD,SAAS,GAAa,CACpB,EAAU,OAAO,MAAM,CACzB,CAEA,SAAS,EAAY,EAAqB,CACxC,IAAM,GAAO,EAAM,QAAU,GAAA,CAAI,KAAK,EAEtC,MADI,CAAC,GAAO,IAAQ,KAAO,EAAI,SAAS,SAAS,EAAU,EAAK,KAAK,WAAW,QAAQ,EACjF,EACJ,MAAM,GAAG,CAAA,CACT,IAAK,GAAM,EAAE,KAAK,CAAC,CAAA,CACnB,OAAO,OAAO,CAAA,CACd,KAAM,GACD,EAAE,SAAS,IAAI,EAAU,EAAK,KAAK,WAAW,EAAE,MAAM,EAAG,EAAE,CAAC,EAC5D,EAAE,WAAW,GAAG,EAAU,EAAK,KAAK,YAAY,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,EACvE,EAAK,OAAS,CACtB,CACL,CAEA,IAAI,EAAe,YACf,EAAe,YACf,EAAqC,KAEzC,SAAS,EAAa,EAAU,CAC9B,IAAM,EAAQ,EAAE,OACV,EAAO,EAAM,QAAQ,GAE3B,GADA,EAAM,MAAQ,GACV,CAAC,EAAM,OACX,GAAI,CAAC,EAAY,CAAI,EAAG,CACtB,EAAK,QAAS,gCAAgC,EAC9C,MACF,CACA,GAAI,EAAK,KAAO,EAAM,QAAS,CAC7B,EAAK,QAAS,8BAA8B,EAAM,QAAU,QAAA,CAAW,QAAQ,CAAC,EAAE,IAAI,EACtF,MACF,CACA,EAAe,EAAK,KACpB,EAAe,EAAK,MAAQ,YAC5B,IAAM,EAAS,IAAI,WACnB,EAAO,WAAe,CACpB,IAAM,EAAM,IAAI,MAChB,EAAI,WAAe,CACjB,EAAY,EACZ,EAAS,MAAQ,EAAI,aACrB,EAAS,MAAQ,EAAI,cACrB,EAAK,MAAQ,EACb,EAAO,MAAQ,EAAI,IACnB,EAAO,GAAK,EAAI,MAAQ,EAAM,OAAS,EACvC,EAAO,GAAK,EAAI,MAAQ,EAAM,OAAS,EACvC,EAAS,MAAQ,EACnB,EACA,EAAI,YAAgB,EAAK,QAAS,qCAAqC,EACvE,EAAI,IAAM,OAAO,EAAO,MAAM,CAChC,EACA,EAAO,YAAgB,EAAK,QAAS,yBAAyB,EAC9D,EAAO,cAAc,CAAI,CAC3B,CAEA,SAAS,GAAS,CAChB,EAAK,oBAAqB,IAAI,CAChC,CAMA,IAAM,GAAA,EAAW,EAAA,IAAA,CAAI,EAAK,EACpB,GAAA,EAAS,EAAA,IAAA,CAAI,EAAE,EACf,GAAA,EAAW,EAAA,IAAA,CAAI,CAAC,EAChB,GAAA,EAAW,EAAA,IAAA,CAAI,CAAC,EAChB,GAAA,EAAO,EAAA,IAAA,CAAI,CAAC,EACZ,GAAA,EAAS,EAAA,SAAA,CAAS,CAAE,EAAG,EAAG,EAAG,CAAE,CAAC,EAEhC,GAAA,EAAY,EAAA,SAAA,KAAgB,EAAM,SAAW,EAAI,EAAM,aAAe,CAAE,EACxE,GAAA,EAAM,EAAA,SAAA,KAAgB,EAAU,OAAS,EAAI,GAAK,KAAK,MAAM,GAAK,EAAU,KAAK,CAAE,EACnF,GAAA,EAAM,EAAA,SAAA,KAAgB,EAAU,OAAS,EAAI,KAAK,MAAM,GAAK,EAAU,KAAK,EAAI,EAAG,EACnF,GAAA,EAAY,EAAA,SAAA,KAChB,EAAS,OAAS,EAAS,MACvB,KAAK,IAAI,EAAI,MAAQ,EAAS,MAAO,EAAI,MAAQ,EAAS,KAAK,EAC/D,CACN,EACM,GAAA,EAAY,EAAA,SAAA,KAAe,EAAU,MAAQ,EAAK,KAAK,EACvD,GAAA,EAAQ,EAAA,SAAA,KAAe,EAAS,MAAQ,EAAU,KAAK,EACvD,GAAA,EAAQ,EAAA,SAAA,KAAe,EAAS,MAAQ,EAAU,KAAK,EAE7D,SAAS,IAAc,CACrB,EAAO,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAI,MAAQ,EAAM,MAAO,EAAO,CAAC,CAAC,EAClE,EAAO,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAI,MAAQ,EAAM,MAAO,EAAO,CAAC,CAAC,CACpE,EACA,EAAA,EAAA,MAAA,CAAM,EAAM,EAAW,EAEvB,IAAM,GAAA,EAAW,EAAA,SAAA,MAAgB,CAC/B,MAAO,GAAG,EAAM,MAAM,IACtB,OAAQ,GAAG,EAAM,MAAM,IACvB,UAAW,aAAa,EAAO,EAAE,MAAM,EAAO,EAAE,IAClD,EAAE,EAGI,GAAA,EAAW,EAAA,IAAA,CAAI,EAAK,EACtB,EAAY,CAAE,EAAG,EAAG,EAAG,EAAG,GAAI,EAAG,GAAI,CAAE,EAC3C,SAAS,EAAc,EAAiB,CACtC,EAAS,MAAQ,GAChB,EAAG,cAA8B,oBAAoB,EAAE,SAAS,EACjE,EAAY,CAAE,EAAG,EAAE,QAAS,EAAG,EAAE,QAAS,GAAI,EAAO,EAAG,GAAI,EAAO,CAAE,CACvE,CACA,SAAS,EAAc,EAAiB,CACjC,EAAS,QACd,EAAO,EAAI,EAAU,IAAM,EAAE,QAAU,EAAU,GACjD,EAAO,EAAI,EAAU,IAAM,EAAE,QAAU,EAAU,GACjD,GAAY,EACd,CACA,SAAS,GAAc,CACrB,EAAS,MAAQ,EACnB,CAEA,SAAS,GAAqB,CAE5B,OADI,EAAM,SAAiB,YACpB,IAAiB,cACtB,IAAiB,aACjB,IAAiB,aACf,EACA,WACN,CACA,SAAS,EAAW,EAAsB,CACxC,IAAM,EAAM,IAAS,YAAc,MAAQ,IAAS,aAAe,OAAS,MAC5E,MAAO,GAAG,EAAa,QAAQ,WAAY,EAAE,GAAK,QAAQ,GAAG,GAC/D,CAEA,SAAS,IAAc,CACrB,GAAI,CAAC,EAAW,OAChB,IAAM,EAAQ,EAAU,MAClB,EAAK,CAAC,EAAO,EAAI,EACjB,EAAK,CAAC,EAAO,EAAI,EACjB,EAAK,EAAI,MAAQ,EACjB,EAAK,EAAI,MAAQ,EACjB,EAAS,SAAS,cAAc,QAAQ,EAC9C,EAAO,MAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EACzC,EAAO,OAAS,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EAC1C,IAAM,EAAM,EAAO,WAAW,IAAI,EAClC,GAAI,CAAC,EAAK,CACR,EAAK,QAAS,qCAAqC,EACnD,MACF,CACI,EAAM,WACR,EAAI,UAAU,EACd,EAAI,QACF,EAAO,MAAQ,EACf,EAAO,OAAS,EAChB,EAAO,MAAQ,EACf,EAAO,OAAS,EAChB,EACA,EACA,KAAK,GAAK,CACZ,EACA,EAAI,KAAK,GAEX,EAAI,UAAU,EAAW,EAAI,EAAI,EAAI,EAAI,EAAG,EAAG,EAAO,MAAO,EAAO,MAAM,EAC1E,IAAM,EAAO,EAAW,EACxB,EAAO,OACJ,GAAS,CACR,GAAI,CAAC,EAAM,CACT,EAAK,QAAS,8BAA8B,EAC5C,MACF,CACA,EAAK,oBAAqB,IAAI,KAAK,CAAC,CAAI,EAAG,EAAW,CAAI,EAAG,CAAE,MAAK,CAAC,CAAC,EACtE,EAAS,MAAQ,EACnB,EACA,EACA,GACF,CACF,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAmFM,MAnFN,GAmFM,EAlFJ,EAAA,EAAA,mBAAA,CAiCM,MAAA,CAhCJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,uBAAsB,CAAA,cACH,EAAA,SAAQ,WAAA,CAAe,EAAA,KAAU,CAAA,CAAA,EACzD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,MAAA,GAAc,EAAA,MAAK,IAAA,OAAA,GAAiB,EAAA,OAAM,GAAA,CAAA,CAEhC,EAAA,CAAA,EAAA,QAAA,EAAhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqBW,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EApBT,EAAA,mBAAA,CAAyD,MAAA,CAAnD,IAAK,EAAA,MAAY,IAAI,GAAG,MAAM,kBACpC,EAAA,KAAA,EAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAkBM,MAlBN,GAkBM,EAAA,EAjBJ,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,KAAK,SACL,KAAK,eACL,QAAA,GACA,KAAA,GACA,aAAW,gBACV,QAAO,CAEV,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPA,KAAK,SACL,KAAK,cACL,QAAA,GACA,KAAA,GACA,SAAS,SACT,aAAW,iBACV,QAAO,CAId,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA,IAAA,EAAA,EAAA,WAAA,CAKO,EAAA,OAAA,cAAA,CAL0B,KAAM,CAAU,MAK1C,EAAA,EAJL,EAAA,mBAAA,CAGS,SAAA,CAHD,KAAK,SAAS,MAAM,qBAAsB,QAAO,CACvD,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,GAAA,EACtB,EAAA,mBAAA,CAA8B,OAAA,KAAxB,oBAAiB,EAAA,CAAA,CAAA,CAAA,CAAA,EAAA,GAAA,CAAA,CAAA,EAAA,CAAA,GAK7B,EAAA,EAAA,mBAAA,CAME,QAAA,CALI,QAAA,YAAJ,IAAI,EACJ,KAAK,OACJ,OAAQ,EAAA,OACT,MAAM,oBACL,SAAQ,gBAGX,EAAA,EAAA,YAAA,EAAA,EAsCS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CArCC,QAAS,EAAA,MAAA,mBAAA,AAAA,EAAA,KAAA,GAAA,EAAQ,MAAA,EACxB,aAAA,EAAW,EAAA,MAAA,CAAA,CAAA,CAAO,EAAE,OAAO,OAAK,OACjC,MAAA,GACA,OAAO,iBACN,mBAAkB,GAClB,MAAO,CAAA,MAAA,MAAA,IA4BG,QAAA,EAAM,EAAA,QAAA,KACyD,EAAA,EAAxE,EAAA,YAAA,EAAA,EAAwE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAhE,KAAK,SAAS,MAAM,WAAW,KAAA,GAAM,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,MAAQ,EAC5D,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAA+D,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvD,KAAK,SAAS,MAAM,YAAa,QAAO,OA5BlD,SAAA,EAAA,EAAA,QAAA,KAyBM,EAAA,EAzBN,EAAA,mBAAA,CAyBM,MAzBN,GAyBM,EAAA,EAxBJ,EAAA,mBAAA,CAkBM,MAAA,CAjBJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,wBAAuB,CAAA,cACJ,EAAA,QAAQ,CAAA,CAAA,EAChC,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,MAAA,GAAc,EAAA,MAAG,IAAA,OAAA,GAAiB,EAAA,MAAG,GAAA,CAAA,EAC1C,cAAa,EACb,cAAa,EACb,YAAW,EACX,eAAc,CAGP,EAAA,CAAA,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOE,MAAA,OALC,IAAK,EAAA,MACN,IAAI,GACJ,MAAM,uBACL,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,KAAQ,EAChB,UAAU,OAEZ,EAAA,KAAA,GAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAA6E,MAAA,CAAxE,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,uBAAsB,CAAA,cAA0B,EAAA,QAAQ,CAAA,CAAA,CAAA,EAAA,KAAA,CAAA,CAAA,EAAA,EAAA,GAAA,EAErE,EAAA,mBAAA,CAIM,MAJN,GAIM,CAHJ,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAgC,IAAA,CAA7B,MAAM,oBAAoB,EAAA,KAAA,EAAA,GAC7B,EAAA,EAAA,YAAA,EAAA,EAAoF,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAnE,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAI,MAAA,EAAG,IAAK,EAAI,IAAK,EAAI,KAAM,IAAM,MAAM,8CAC5D,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA+B,IAAA,CAA5B,MAAM,mBAAmB,EAAA,KAAA,EAAA,6vBE1TtC,IAAM,EAAQ,EAqBR,EAAO,EAIP,GAAA,EAAa,EAAA,IAAA,CAAI,EAAE,EACnB,GAAA,EAAc,EAAA,IAAA,CAAI,EAAE,EAEpB,GAAA,EAAc,EAAA,SAAA,KAAe,IAAI,IAAI,EAAM,QAAQ,CAAC,EACpD,GAAA,EAAS,EAAA,SAAA,KAAe,EAAM,cAAgB,CAAC,EAAM,WAAW,CAAC,EAEvE,SAAS,EAAK,EAAgB,CAC5B,OAAO,EAAK,EAAM,QACpB,CAEA,SAAS,EAAQ,EAAoB,CACnC,OAAO,OAAO,EAAK,EAAM,cAAgB,EAAE,CAC7C,CAEA,SAAS,EAAQ,EAAY,EAAuB,CAClD,GAAI,CAAC,EAAM,MAAO,GAClB,IAAM,EAAI,EAAK,YAAY,EAC3B,OAAO,EAAO,MAAM,KAAM,GACxB,OAAO,EAAK,IAAM,EAAE,CAAA,CACjB,YAAY,CAAA,CACZ,SAAS,CAAC,CACf,CACF,CAEA,IAAM,GAAA,EAAY,EAAA,SAAA,KAChB,EAAM,OAAO,OAAQ,GAAM,CAAC,EAAY,MAAM,IAAI,EAAK,CAAC,CAAC,GAAK,EAAQ,EAAG,EAAW,KAAK,CAAC,CAC5F,EAEM,GAAA,EAAS,EAAA,SAAA,KACb,EAAM,OAAO,OAAQ,GAAM,EAAY,MAAM,IAAI,EAAK,CAAC,CAAC,GAAK,EAAQ,EAAG,EAAY,KAAK,CAAC,CAC5F,EAEA,SAAS,EAAI,EAAY,CACnB,EAAM,UACV,EAAK,kBAAmB,CAAC,GAAG,EAAM,SAAU,EAAK,CAAI,CAAC,CAAC,CACzD,CAEA,SAAS,EAAO,EAAY,CAC1B,GAAI,EAAM,SAAU,OACpB,IAAM,EAAK,EAAK,CAAI,EACpB,EACE,kBACA,EAAM,SAAS,OAAQ,GAAM,IAAM,CAAE,CACvC,CACF,CAEA,SAAS,GAAS,CACZ,EAAM,UACV,EAAK,kBAAmB,EAAM,OAAO,IAAI,CAAI,CAAC,CAChD,CAEA,SAAS,GAAY,CACf,EAAM,UACV,EAAK,kBAAmB,CAAC,CAAC,CAC5B,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAoEM,MAAA,CApED,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,aAAY,CAAA,uBAAmC,EAAA,QAAQ,CAAA,CAAA,CAAA,EAAA,EAEhE,EAAA,EAAA,mBAAA,CAqBM,MArBN,GAqBM,EApBJ,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,CAFJ,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAkD,OAAA,CAA5C,MAAM,mBAAmB,EAAC,cAAW,EAAA,GAAA,EAC3C,EAAA,mBAAA,CAA6D,OAA7D,IAAA,EAA6D,EAAA,gBAAA,CAA1B,EAAA,MAAU,MAAM,EAAA,CAAA,CAAA,CAAA,GAErD,EAAA,EAAA,YAAA,EAAA,EAGY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAHD,MAAM,oBAAoB,EAAA,CACnC,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAAgE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAA5C,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAU,MAAA,EAAE,YAAY,YAAY,MAAA,oCAE1D,EAAA,EAAA,mBAAA,CAWK,KAXL,GAWK,GAAA,EAVH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAPY,EAAA,MAAR,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,KAAA,CANF,IAAG,KAAO,EAAK,CAAI,IACpB,MAAM,mBACL,QAAK,GAAE,EAAI,CAAI,CAEhB,EAAA,EAAA,EAAA,EAAA,mBAAA,CAAgC,OAAA,MAAA,EAAA,EAAA,gBAAA,CAAvB,EAAQ,CAAI,CAAA,EAAA,CAAA,EACrB,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA+B,IAAA,CAA5B,MAAM,mBAAmB,EAAA,KAAA,EAAA,CAAA,EAAA,EAAA,EAAA,EAEnB,EAAA,GAAA,GAAA,EAAA,MAAU,QAA6C,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAA7C,EAArB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAuE,KAAvE,GAAuD,aAAW,EAAA,CAAA,KAKtE,EAAA,EAAA,mBAAA,CAiBM,MAjBN,GAiBM,EAAA,EAhBJ,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,KAAK,SACL,KAAK,2BACL,KAAA,GACA,QAAA,GACC,SAAU,EAAA,UAAQ,CAAK,EAAA,MAAU,OACjC,QAAO,CAEV,EAAA,KAAA,EAAA,CAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,KAAK,SACL,KAAK,0BACL,KAAA,GACA,QAAA,GACC,SAAU,EAAA,UAAQ,CAAK,EAAA,SAAS,OAChC,QAAO,2BAKZ,EAAA,EAAA,mBAAA,CAqBM,MArBN,GAqBM,EApBJ,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,CAFJ,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAmD,OAAA,CAA7C,MAAM,mBAAmB,EAAC,eAAY,EAAA,GAAA,EAC5C,EAAA,mBAAA,CAA0D,OAA1D,IAAA,EAA0D,EAAA,gBAAA,CAAvB,EAAA,MAAO,MAAM,EAAA,CAAA,CAAA,CAAA,GAElD,EAAA,EAAA,YAAA,EAAA,EAGY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAHD,MAAM,oBAAoB,EAAA,CACnC,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAAiE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAA7C,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAW,MAAA,EAAE,YAAY,YAAY,MAAA,oCAE3D,EAAA,EAAA,mBAAA,CAWK,KAXL,GAWK,GAAA,EAVH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAPY,EAAA,MAAR,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,KAAA,CANF,IAAG,KAAO,EAAK,CAAI,IACpB,MAAM,mBACL,QAAK,GAAE,EAAO,CAAI,CAEnB,EAAA,CAAA,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA8B,IAAA,CAA3B,MAAM,kBAAkB,EAAA,KAAA,EAAA,GAAA,EAC3B,EAAA,mBAAA,CAAgC,OAAA,MAAA,EAAA,EAAA,gBAAA,CAAvB,EAAQ,CAAI,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,EAEZ,EAAA,GAAA,GAAA,EAAA,MAAO,QAA6C,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAA7C,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAoE,KAApE,GAAoD,aAAW,EAAA,CAAA,YEhJvE,eAAsB,GAAU,EAA6C,CAC3E,IAAM,EAAS,EAAI,QAAQ,MAAO,EAAE,EACpC,GAAI,EAAO,SAAW,EAAG,OAAO,KAChC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,4BAA4B,EAAO,OAAO,EACvE,GAAI,CAAC,EAAS,GAAI,OAAO,KACzB,IAAM,EAAQ,MAAM,EAAS,KAAK,EAElC,OADI,EAAK,KAAa,KACf,CACT,MAAQ,CACN,OAAO,IACT,CACF,unCCPA,IAAM,EAAQ,EAgBR,EAAO,EAIP,GAAA,EAAc,EAAA,SAAA,CAAwC,CAAC,CAAC,EAGxD,GAAA,EAAa,EAAA,SAAA,CAAkC,CAAC,CAAC,EACjD,GAAA,EAAW,EAAA,SAAA,CAAwC,CAAC,CAAC,EACrD,GAAA,EAAY,EAAA,SAAA,CAA+D,CAAC,CAAC,EAEnF,SAAS,EAAW,EAAiB,EAAc,CACjD,IAAM,EAAO,EAAM,OAA4B,MACzC,EAAS,EAAI,QAAQ,MAAO,EAAE,EACpC,EAAK,eAAgB,EAAM,MAAO,CAAG,EAGrC,EAAS,EAAM,OAAS,KAGpB,EAAU,EAAM,SAClB,aAAa,EAAU,EAAM,MAAO,EACpC,EAAU,EAAM,OAAS,MAGvB,EAAO,SAAW,IACpB,EAAU,EAAM,OAAS,WAAW,SAAY,CAC9C,EAAW,EAAM,OAAS,GAC1B,EAAS,EAAM,OAAS,KACxB,GAAI,CACF,IAAM,EAAS,MAAM,GAAU,CAAM,EACrC,GAAI,CAAC,EACH,EAAS,EAAM,OAAS,0DACnB,CAEL,IAAM,EAAU,EAAM,WAAa,CAAC,EAC9B,EAAO,OAAO,KAAK,CAAO,EAChC,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAc,EAAQ,GAC5B,GAAI,CAAC,EAAa,SAClB,IAAM,EAAW,EAAM,SAAS,IAC5B,GAAY,MAAQ,IAAa,KACnC,EAAK,eAAgB,EAAa,EAAO,IAAQ,EAAE,CAEvD,CACF,CACF,QAAU,CACR,EAAW,EAAM,OAAS,EAC5B,CACF,EAAG,GAAG,EAEV,CAIA,IAAM,GAAA,EAAgB,EAAA,SAAA,KACpB,EAAM,OAAO,OAAQ,GACf,EAAE,UAAY,IAAA,IAAa,EAAE,UAAY,GAAa,GACtD,OAAO,EAAE,SAAY,WAAmB,EAAE,QAAQ,EAAM,SAAU,EAAM,SAAS,EAC9E,EAAE,OACV,CACH,EAEA,SAAS,EAAgB,EAA0B,CAMjD,OALI,EAAM,UAEN,EAAM,UAAY,EAAM,UACxB,EAAM,gBAAkB,EAAM,UAAkB,GAChD,OAAO,EAAM,UAAa,WAAmB,EAAM,SAAS,EAAM,SAAU,EAAM,SAAS,EACxF,CAAC,CAAC,EAAM,QACjB,CAKA,SAAS,EAAe,EAAgD,CACtE,OAAO,EAAM,OACf,CAGA,SAAS,EAAsB,EAAiB,CAC9C,IAAM,EAAK,EAAM,eACjB,OAAO,OAAO,GAAO,WAAa,EAAG,EAAI,CAC3C,CAUA,IAAM,GAAA,EAAmB,EAAA,SAAA,KACvB,EAAM,OAAO,OAAQ,GAAM,OAAO,EAAE,WAAc,UAAU,CAC9D,GAEA,EAAA,EAAA,MAAA,KACQ,EAAiB,MAAM,IAAK,GAAM,EAAE,UAAW,EAAM,SAAU,EAAM,SAAS,CAAC,EACpF,GAAU,CACT,EAAiB,MAAM,SAAS,EAAG,IAAM,CACnC,EAAM,SAAS,EAAE,SAAW,EAAM,IAAI,EAAK,eAAgB,EAAE,MAAO,EAAM,EAAE,CAClF,CAAC,CACH,EACA,CAAE,UAAW,EAAK,CACpB,EAEA,SAAS,EAAa,EAAsD,CAC1E,OAAA,EAAO,EAAA,MAAA,CAAM,CAAG,EAAM,EAA2B,MAAmB,CACtE,CAIA,IAAM,GAAA,EAAiB,EAAA,SAAA,KAAe,CACpC,IAAM,EAAO,EAAM,UAAY,OAAS,SAClC,EAAW,EAAM,OAAO,KAAM,GAAM,EAAE,YAAc,IAAQ,EAAE,YAAc,CAAI,EAUtF,OATI,EAAiB,EAAS,MAChB,EAAc,MAAM,KAAM,GAKtC,EAFI,EAAE,MAAQ,CADI,SAAU,KAAM,SAAU,QAAS,YAAa,SAAU,QAAS,UACvE,CAAA,CAAS,SAAS,EAAE,IAAI,GAClC,EAAE,WAAa,IACf,EAAE,gBAAkB,EAAM,UAGzB,CAAA,EAAO,OAAS,IACzB,CAAC,EAED,SAAS,EAAgB,EAA0B,CACjD,OAAO,EAAM,QAAU,EAAe,KACxC,CAIA,SAAS,EAAY,EAAmC,CACjD,KACL,OAAO,EAAK,QAAQ,KAAM,GAAG,CAAC,CAAC,QAAQ,KAAM,GAAG,CAAC,CAAC,QAAQ,MAAO,GAAG,CACtE,CAIA,SAAS,EAAe,EAAwB,CAC9C,GAAI,CAAC,EAAO,MAAO,GACnB,IAAM,EAAS,OAAO,CAAK,CAAC,CAAC,QAAQ,MAAO,EAAE,CAAC,CAAC,MAAM,EAAG,EAAE,EAO3D,OANI,EAAO,QAAU,GACZ,EACJ,QAAQ,cAAe,OAAO,CAAA,CAC9B,QAAQ,cAAe,OAAO,CAAA,CAC9B,QAAQ,oBAAqB,OAAO,EAElC,EACJ,QAAQ,cAAe,OAAO,CAAA,CAC9B,QAAQ,cAAe,OAAO,CAAA,CAC9B,QAAQ,cAAe,OAAO,CAAA,CAC9B,QAAQ,oBAAqB,OAAO,CACzC,CAEA,SAAS,EAAe,EAAmB,EAAc,CACvD,IAAM,EAAO,EAAM,OAA4B,MAAM,QAAQ,MAAO,EAAE,CAAC,CAAC,MAAM,EAAG,EAAE,EACnF,EAAK,eAAgB,EAAW,CAAG,CACrC,CAIA,IAAM,GAAA,EAA0B,EAAA,SAAA,CAAoC,CAAC,CAAC,EAEtE,SAAS,EAAqB,EAAiB,CAC7C,IAAM,EAAM,EAAM,SAAS,EAAM,OACjC,GAAI,GAAO,KAAM,OAAO,KACxB,IAAM,EAAc,EAAM,aAAe,QAEzC,OADgB,EAAU,EAAM,OAAO,GAAK,CAAC,EAAA,CACC,KAAM,GAAM,EAAE,KAAiB,CAAG,GAAK,IACvF,CAEA,SAAS,EAAuB,EAAiB,CAC/C,OAAO,EAAwB,EAAM,QAAU,CAAC,CAClD,CAEA,SAAS,GAAqB,EAAiB,EAA0B,CACvE,IAAM,GAAS,EAAM,OAAS,GAAA,CAAI,YAAY,EACxC,EAAU,EAAU,EAAM,OAAO,GAAK,CAAC,EACvC,EAAc,EAAM,aAAe,QACzC,EAAwB,EAAM,OAAU,EAAsC,OAAQ,GACpF,OAAO,EAAE,IAAgB,EAAE,CAAA,CACxB,YAAY,CAAA,CACZ,SAAS,CAAK,CACnB,CACF,CAEA,SAAS,EAAqB,EAAiB,EAA2C,CACxF,IAAM,EAAc,EAAM,aAAe,QACzC,EAAK,eAAgB,EAAM,MAAO,EAAM,MAAM,EAAY,CAC5D,CAMA,SAAS,EAAa,EAA4C,CAChE,OAAQ,EAAU,EAAM,OAAO,GAAK,CAAC,CACvC,CAEA,SAAS,EAAc,EAAiB,EAAyC,CAC/E,OAAO,OAAO,EAAO,EAAM,aAAe,UAAY,EAAE,CAC1D,CAEA,SAAS,EAAc,EAAiB,EAA0C,CAChF,OAAO,EAAO,EAAM,aAAe,QACrC,CAEA,SAAS,EAAiB,EAAiB,EAA0C,CACnF,OAAO,EAAM,SAAS,EAAM,SAAW,EAAc,EAAO,CAAM,CACpE,CAGA,SAAS,EAAW,EAAiB,EAAiC,CAChE,EAAgB,CAAK,GACzB,EAAK,eAAgB,EAAM,MAAO,EAAc,EAAO,CAAM,CAAC,CAChE,CAWA,SAAS,EAAY,EAA8B,CACjD,IAAM,EAAM,EAAM,SAAS,EAAM,OACjC,GAAI,CAAC,MAAM,QAAQ,CAAG,EAAG,MAAO,CAAC,EACjC,IAAM,EAAW,EAAM,aAAe,OAChC,EAAW,EAAM,aAAe,KAChC,EAAU,EAAa,CAAK,EAElC,OAAO,EAAI,KAAK,EAAM,IAAU,CAC9B,GAAqB,OAAO,GAAS,UAAjC,EAA2C,CAC7C,IAAM,EAAM,EACZ,MAAO,CAAE,IAAK,OAAO,EAAI,IAAa,CAAK,EAAG,MAAO,OAAO,EAAI,IAAa,EAAE,CAAE,CACnF,CACA,IAAM,EAAQ,EAAQ,KAAM,GAAM,EAAE,KAAc,CAAI,EACtD,MAAO,CACL,IAAK,OAAO,GAAQ,CAAK,EACzB,MAAe,OAAR,EAAe,EAAM,IAAa,EAAe,CAAI,CAC9D,CACF,CAAC,CACH,CAEA,SAAS,EAAW,EAAiB,EAAe,CAClD,GAAI,EAAgB,CAAK,EAAG,OAC5B,IAAM,EAAM,EAAM,SAAS,EAAM,OAC5B,MAAM,QAAQ,CAAG,GACtB,EACE,eACA,EAAM,MACN,EAAI,QAAQ,EAAG,IAAM,IAAM,CAAK,CAClC,CACF,CAIA,SAAS,GAAoB,EAAyB,CACpD,IAAM,EAAM,EAAM,SAAS,EAAM,OACjC,OAAO,EAAM,OAAO,CAAG,CAAC,CAAC,QAAQ,IAAK,EAAE,EAAI,QAC9C,CAEA,SAAS,GAAc,EAAiB,EAAe,CACrD,EAAK,eAAgB,EAAM,MAAO,IAAI,GAAO,CAC/C,CAKA,SAAS,EAAiB,EAAiB,CACzC,IAAM,EAAM,EAAM,UACb,KAEL,OADa,MAAM,QAAQ,CAAG,EAAI,EAAM,CAAC,CAAG,EAAA,CAChC,IAAK,IAAO,CACtB,MAAO,EAAE,OAAS,EAAE,MACpB,MAAO,EAAM,SAAS,EAAE,OACxB,SAAU,EAAE,QACd,EAAE,CACJ,CAIA,SAAS,EAAc,EAAiB,CACtC,GAAI,OAAO,EAAM,UAAa,WAAY,CACxC,IAAM,EAAQ,EAAM,SAAS,EAAM,SAAS,EAAM,MAAM,EACxD,EAAY,EAAM,OAAS,GAAS,IACtC,CACF,CAEA,SAAS,IAAwB,CAC/B,IAAM,EAAmB,CAAC,EAC1B,IAAK,IAAM,KAAS,EAAM,OACxB,GAAI,OAAO,EAAM,UAAa,WAAY,CACxC,IAAM,EAAQ,EAAM,SAAS,EAAM,SAAS,EAAM,MAAM,EACxD,EAAY,EAAM,OAAS,GAAS,KAChC,GAAO,EAAO,KAAK,CAAK,CAC9B,CAEF,OAAO,CACT,CAEA,SAAS,IAAc,CACrB,OAAO,KAAK,CAAW,CAAC,CAAC,QAAS,GAAM,OAAO,EAAY,EAAE,CAC/D,CAcA,SAAS,GAAa,EAA2B,CAC/C,OAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAM,SAAW,EAAM,OAAO,CAAC,CAC/D,CAMA,SAAS,GAAU,EAAiB,EAAsB,CACxD,IAAM,EAAO,EAAM,QAGnB,OAFI,GAAQ,MAAQ,IAAS,OAAe,EACxC,IAAS,GAAY,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,CAAC,CAAC,EAClD,KAAK,IAAI,KAAK,IAAI,EAAG,KAAK,MAAM,CAAI,CAAC,EAAG,CAAI,CACrD,CAEA,SAAS,EAAe,EAAiB,EAAmB,CAC1D,MAAO,CAAE,eAAgB,GAAU,EAAO,GAAa,CAAK,CAAC,CAAE,CACjE,CAEA,SAAS,EAAe,EAAiB,EAA2B,CAClE,OAAO,GAAU,EAAO,GAAa,CAAK,CAAC,IAAM,GAAa,CAAK,EAC/D,uBACA,sBACN,CAEA,IAAM,IAAA,EAAgB,EAAA,SAAA,KAA6B,CACjD,IAAM,EAAW,IAAI,IACf,EAAuB,CAAC,EACxB,EAAqB,IAAI,IAE/B,IAAK,IAAM,KAAS,EAAc,MAAO,CACvC,IAAM,EAAU,EAAM,YAAY,IAAM,cAEnC,EAAS,IAAI,CAAO,IACvB,EAAS,IAAI,EAAS,CACpB,GAAI,EACJ,MAAO,EAAM,YAAY,MACzB,YAAa,EAAM,YAAY,YAC/B,QAAS,EAAM,YAAY,QAC3B,OAAQ,CAAC,CACX,CAAC,EACD,EAAW,KAAK,CAAO,EACnB,EAAM,YAAY,OAAS,MAC7B,EAAmB,IAAI,EAAS,EAAM,WAAW,KAAK,GAI1D,EAAS,IAAI,CAAO,CAAC,CAAE,OAAO,KAAK,CAAK,CAC1C,CAEA,OAAO,EACJ,MAAM,CAAA,CACN,MAAM,EAAG,IAAM,CACd,IAAM,EAAS,EAAmB,IAAI,CAAC,EACjC,EAAS,EAAmB,IAAI,CAAC,EAIvC,OAHI,GAAU,MAAQ,GAAU,KAAa,EAAS,EAClD,GAAU,KACV,GAAU,KACP,EAAW,QAAQ,CAAC,EAAI,EAAW,QAAQ,CAAC,EADxB,EADA,EAG7B,CAAC,CAAA,CACA,IAAK,GAAO,EAAS,IAAI,CAAE,CAAE,CAClC,CAAC,SAED,EAAa,CAAE,eAAa,cAAY,CAAC,WAIvC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA4aM,MA5aN,GA4aM,GAAA,EA3aJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CA0aM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CA1ae,GAAA,MAAT,KAAZ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA0aM,MAAA,CA1a+B,IAAK,EAAM,GAAI,MAAM,mBAC7C,EAAA,CAAA,EAAM,QAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAA0D,KAA1D,IAAA,EAA0D,EAAA,gBAAA,CAAnB,EAAM,KAAK,EAAA,CAAA,EACzC,EAAM,cAAA,EAAf,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAsF,IAAtF,IAAA,EAAsF,EAAA,gBAAA,CAAxB,EAAM,WAAW,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAEjF,EAAA,mBAAA,CAoaM,MAAA,CApaD,MAAM,qBAAsB,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,gBAAqB,GAAa,CAAK,CAAA,CAAA,CAC3E,EAAA,GAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAkaW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAlae,EAAM,OAAf,IACf,EAAA,EAAA,WAAA,CAgaO,EAAA,OAAA,SA/ZW,EAAM,QAAK,CACnB,QACP,SAAW,EAAA,SACX,UAAY,EAAA,UACZ,cAAiB,EAAW,IAAe,EAAI,eAAiB,EAAG,CAAC,CALvE,MAgaO,CAvZG,EAAM,OAAI,WAAA,EADlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAWM,MAAA,OATJ,MAAM,qBACL,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,CAEnC,EAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EAIE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAHC,cAAa,EAAA,SAAS,EAAM,OAC5B,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,yDAErE,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAsF,QAAtF,IAAA,EAAsF,EAAA,gBAAA,CAA3C,EAAM,aAAe,EAAM,KAAK,EAAA,CAAA,CAAA,EAAA,CAAA,GAKhE,EAAM,OAAI,UAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAwBM,MAAA,OAtBH,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,EAClC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,CAEnC,EAAA,EAAA,EAAA,EAAA,mBAAA,CAGQ,QAHR,GAGQ,EAAA,EAFH,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAM,KAAK,EAAG,IACjB,CAAA,EAAY,EAAM,WAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAiE,OAAjE,GAAyD,GAAC,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAAA,EAE5D,EAAA,mBAAA,CAcM,MAdN,GAcM,EAAA,EAbJ,EAAA,YAAA,EAAA,EAIE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAHC,cAAa,GAAoB,CAAK,EACtC,SAAU,EAAgB,CAAK,EAC/B,sBAAkB,GAAE,GAAc,EAAO,CAAM,yDAElD,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAM,OACN,UAAU,IACV,YAAY,UACX,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,yDAO5D,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,GAAA,EAAM,OAAI,UAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAuBM,MAAA,OArBH,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,EAClC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,KAEnC,EAAA,EAAA,mBAAA,CAEQ,QAFR,IAAA,EAEQ,EAAA,gBAAA,CADH,EAAM,KAAK,EAAA,CAAA,GAEhB,EAAA,EAAA,WAAA,CAWO,EAAA,OAAA,SAXe,EAAM,QAAK,CAAY,QAAQ,SAAW,EAAA,QAAhE,MAWO,EAAA,EAVL,EAAA,YAAA,CASE,GAAA,CARC,cAAa,EAAA,SAAS,EAAM,OAC5B,OAAQ,EAAM,QAAM,UACpB,sBAAqB,GAAS,EAAI,eAAiB,EAAM,MAAO,CAAI,EACpE,QAA6B,GAAG,CAA6B,EAAY,EAAM,OAAS,uEAOhF,EAAY,EAAM,SAAA,EAA/B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEQ,QAFR,IAAA,EAEQ,EAAA,gBAAA,CADH,EAAY,EAAM,MAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAMjB,EAAA,CAAA,GAAA,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAkBM,MAAA,OAhBH,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,EAClC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,CAEnC,EAAA,EAAA,EAAA,EAAA,mBAAA,CAGQ,QAHR,GAGQ,EAAA,EAFH,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAM,KAAK,EAAG,IACjB,CAAA,EAAY,EAAM,WAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAiE,OAAjE,GAAyD,GAAC,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAAA,EAE5D,EAAA,YAAA,CAQE,GAAA,CAPC,OAAS,EAAU,EAAM,OAAO,GAAA,CAAA,EAChC,SAAW,EAAA,SAAS,EAAM,QAAK,CAAA,EAC/B,WAAU,EAAM,aAAW,KAC3B,eAAc,EAAM,aAAW,OAC/B,gBAAe,EAAM,aACrB,SAAU,EAAgB,CAAK,EAC/B,oBAAkB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,uGAKpE,CAAA,CAAA,EAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA+TM,MAAA,OA/TO,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,EAAI,OAAA,EAAK,EAAA,eAAA,CAAE,EAAe,EAAO,CAAK,CAAA,KACnF,EAAA,EAAA,mBAAA,CAOQ,QAPR,GAOQ,EANH,EAAA,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAM,KAAK,EAAG,IACjB,CAAA,EAAY,EAAM,WAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAiE,OAAjE,GAAyD,GAAC,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAElD,EAAW,EAAM,SAAA,EADzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGE,IAHF,EAGE,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,KAKM,CAAA,EAAM,MAAQ,EAAM,OAAI,SAAgB,EAAM,MAAA,EADxD,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OANC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,UAAW,EAAgB,CAAK,GAAK,IAAA,GACrC,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,mFANlD,CAAA,GAAA,CAAA,EAAA,EAAA,EAAA,MAAA,CAAA,CAAA,EAAA,CAAA,KAAA,EAAY,EAAM,IAAI,CAAA,CAAA,CAAA,CAAA,EAW3B,CAAA,EAAM,MAAQ,EAAM,OAAI,SAAA,EADtC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OANC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,UAAW,EAAgB,CAAK,GAAK,IAAA,GACrC,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,mFAKxD,CAAA,GAAA,EAAM,OAAI,UAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,cAAa,EAAA,SAAS,EAAM,OAC7B,KAAK,QACL,MAAA,GACC,UAAW,EAAgB,CAAK,GAAK,IAAA,GACrC,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,mFAKxD,CAAA,GAAA,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACA,cAAA,GACC,SAAU,EAAM,WAAQ,GACxB,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,kFAKxD,CAAA,GAAA,EAAM,OAAI,WAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAcE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAZC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACA,OAAO,QACN,IAAK,EAAM,IACX,IAAK,EAAM,IACX,sBAAqB,EAAM,kBAC3B,sBAAqB,EAAM,kBAC3B,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,iJAKxD,CAAA,GAAA,EAAM,OAAI,YAAmB,EAAM,gBAAA,EADhD,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAUE,GAAA,OARC,cAAa,EAAA,SAAS,EAAM,OAC5B,SAAU,EAAM,UAAQ,EACzB,SAAA,GACC,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,oGAKxD,CAAA,GAAA,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAYE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAVC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACA,KAAK,WACL,SAAS,MACT,OAAO,QACN,IAAK,EAAM,IACX,IAAK,EAAM,IACX,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,mFAKxD,CAAA,GAAA,EAAM,OAAI,WAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAWE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OATC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,QAAS,EAAU,EAAM,OAAO,EAChC,eAAc,EAAM,aAAW,QAC/B,eAAc,EAAM,aAAW,QAC/B,aAAY,EAAM,YAAS,GAC3B,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,4HAKxD,CAAA,GAAA,EAAM,OAAI,iBAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAWE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OATC,cAAa,EAAqB,CAAK,EACxC,MAAA,GACC,YAAa,EAAuB,CAAK,EACzC,eAAc,EAAM,aAAW,QAC/B,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,WAAQ,GAAE,GAAqB,EAAO,CAAM,EAC5C,aAAW,GAAE,EAAqB,EAAO,CAAM,EAC/C,QAAK,GAAE,EAAI,eAAiB,EAAM,MAAK,IAAA,oHAK7B,CAAA,GAAA,EAAM,OAAI,OAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAsBE,GAAA,OApBC,cAAa,EAAA,SAAS,EAAM,OAC5B,UAAW,EAAgB,CAAK,GAAK,IAAA,GACrC,SAAU,EAAM,SAChB,kBAAiB,EAAsB,CAAK,EAC5C,UAAW,EAAiB,CAAK,EACjC,sBAAqB,EAAM,mBAC3B,eAAc,EAAM,aAAW,OAC/B,qBAAoB,EAAM,kBAC1B,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,aAAY,EAAM,YAAS,GAC3B,gBAAe,EAAM,MACrB,cAAa,EAAM,WACnB,eAAc,EAAM,YACpB,WAAU,EAAe,CAAK,EAC9B,eAAc,EAAM,YACpB,aAAY,EAAM,UAClB,WAAU,EAAM,QAChB,aAAY,EAAM,UAClB,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,+SAKxD,CAAA,GAAA,EAAM,OAAI,SAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAUE,GAAA,QARC,cAAa,EAAA,SAAS,EAAM,OAC7B,eAAa,OACZ,QAAS,EAAM,QACf,WAAU,EAAM,QAChB,WAAU,EAAM,QAChB,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,uGAKxD,CAAA,GAAA,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAWE,GAAA,QATC,cAAa,EAAA,SAAS,EAAM,OAC7B,eAAa,OACb,YAAA,GACC,QAAS,EAAM,QACf,WAAU,EAAM,QAChB,WAAU,EAAM,QAChB,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,uGAKxD,CAAA,GAAA,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,QARC,cAAa,EAAe,EAAA,SAAS,EAAM,MAAK,EACjD,MAAA,GACA,UAAU,KACT,YAAa,EAAM,aAAW,iBAC9B,SAAU,EAAgB,CAAK,EAC/B,QAAO,CAAA,CAAI,EAAY,EAAM,OAC7B,QAAK,GAAE,EAAe,EAAM,MAAO,CAAM,EACzC,OAAI,GAAE,EAAc,CAAK,8EAKf,CAAA,GAAA,EAAM,OAAI,QAAA,EADvB,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,QAPC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,QAAO,CAAA,CAAI,EAAY,EAAM,OAC7B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,EAClE,OAAI,GAAE,EAAc,CAAK,0FAPT,CAAA,GAAA,CAAA,EAAA,EAAA,EAAA,MAAA,CAAA,CAAA,EAAA,CAAA,KAAA,EAAY,EAAM,IAAI,CAAA,CAAA,CAAA,CAAA,EAY5B,EAAM,OAAI,OAAA,EADvB,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,QANC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,YAAa,EAAM,aAAW,YAC9B,SAAU,EAAgB,CAAK,EAC/B,QAAO,CAAA,CAAI,EAAS,EAAM,OAC1B,QAAK,GAAE,EAAW,EAAO,CAAM,qEANvB,CAAA,GAAA,CAAA,EAAA,EAAA,EAAA,MAAA,CAAA,CAAA,EAAA,CAAA,KAAA,WAAA,CAAqB,CAAA,CAAA,EAWnB,EAAM,OAAI,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,QAPC,cAAa,EAAA,SAAS,EAAM,OAC7B,MAAA,GACC,UAAW,EAAgB,CAAK,GAAK,IAAA,GACrC,KAAM,EAAM,MAAI,EAChB,YAAa,EAAM,YACnB,SAAU,EAAgB,CAAK,EAC/B,sBAAqB,GAAQ,EAAI,eAAiB,EAAM,MAAO,CAAG,0FAIrD,CAAA,GAAA,EAAM,OAAI,cAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAaM,MAbN,GAaM,GAAA,EAZJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAWS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAVU,EAAa,CAAK,EAA5B,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAWS,SAAA,CATN,IAAK,OAAO,EAAc,EAAO,CAAM,CAAA,EACxC,KAAK,SACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,sBAAqB,CAAA,0BACU,EAAiB,EAAO,CAAM,CAAA,CAAA,CAAA,EAClE,eAAc,EAAiB,EAAO,CAAM,EAC5C,SAAU,EAAgB,CAAK,EAC/B,QAAK,GAAE,EAAW,EAAO,CAAM,CAE7B,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAc,EAAO,CAAM,CAAA,EAAA,GAAA,EAAA,EAKlB,EAAA,GAAA,EAAA,CAAA,GAAA,EAAM,OAAI,WAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAjBN,GAiBM,GAAA,EAhBJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAeS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAdU,EAAa,CAAK,EAA5B,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeS,SAAA,CAbN,IAAK,OAAO,EAAc,EAAO,CAAM,CAAA,EACxC,KAAK,SACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,mBAAkB,CAAA,uBACU,EAAiB,EAAO,CAAM,CAAA,CAAA,CAAA,EAC/D,eAAc,EAAiB,EAAO,CAAM,EAC5C,SAAU,EAAgB,CAAK,EAC/B,QAAK,GAAE,EAAW,EAAO,CAAM,CAGxB,EAAA,CAAA,EAAiB,EAAO,CAAM,GAAK,EAAM,aAAU,KAAA,EAD3D,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGE,IAAA,OADC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAM,YAAU,oBAAA,CACxB,EAAA,KAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,gBAAA,CAAA,KAAA,EACF,EAAA,gBAAA,CAAG,EAAc,EAAO,CAAM,CAAA,EAAA,CAAA,CAAA,EAAA,GAAA,EAAA,EAKlB,EAAA,GAAA,EAAA,CAAA,GAAA,EAAM,OAAI,UAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA0CM,MA1CN,GA0CM,GAzCJ,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAgBO,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAfuB,EAAY,CAAK,GAArC,EAAM,MADhB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAgBO,OAAA,CAdJ,IAAK,EAAK,IACX,MAAM,eAEH,EAAA,EAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAK,KAAK,EAAG,IAChB,CAAA,GAAA,EAAA,EAAA,mBAAA,CASS,SAAA,CARP,KAAK,SACL,MAAM,kBACL,MAAO,EAAM,kBAAgB,UAC7B,aAAY,EAAM,kBAAgB,UAClC,SAAU,EAAgB,CAAK,EAC/B,QAAK,GAAE,EAAW,EAAO,CAAS,CAEnC,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,UAKjB,CAAA,EAAY,CAAK,CAAA,CAAE,QAAU,EAAM,kBAAA,EAD5C,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKO,OALP,IAAA,EAKO,EAAA,gBAAA,CADF,EAAM,eAAe,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAG1B,EAAA,EAAA,WAAA,CAME,EAAA,OAAA,iBALwB,EAAM,QAAK,CAC3B,QACP,SAAW,EAAA,SACX,SAAU,EAAgB,CAAK,EAC/B,cAAiB,EAAW,IAAe,EAAI,eAAiB,EAAG,CAAC,KAGvE,EAAA,EAAA,mBAAA,CAOO,OAPP,GAOO,EAAA,EANL,EAAA,WAAA,CAKE,EAAA,OAAA,iBAJwB,EAAM,QAAK,CAC3B,QACP,SAAW,EAAA,SACX,MAAO,EAAY,CAAK,yCAKlB,EAAS,EAAM,SAAA,EAA5B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEQ,QAFR,IAAA,EAEQ,EAAA,gBAAA,CADH,EAAS,EAAM,MAAK,EAAA,CAAA,GAEP,EAAY,EAAM,SAAA,EAApC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEQ,QAFR,IAAA,EAEQ,EAAA,gBAAA,CADH,EAAY,EAAM,MAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CA9ZY,EAAA,CAAA,EAAA,EAAA,IAAA,GAAA,EAAM,KAAK,iZEpajE,IAAM,EAAU,EAAA,EAAW,EAErB,EAAQ,EAuBR,EAAO,EAMP,GAAA,EAAc,EAAA,IAAA,CAA+C,IAAI,EACjE,GAAA,EAAU,EAAA,IAAA,CAA4B,IAAI,EAEhD,SAAS,GAAS,CACZ,EAAY,MACC,EAAY,MAAM,YAC7B,CAAA,CAAO,SAAW,GACpB,EAAK,MAAM,EAGb,EAAK,MAAM,CAEf,CAGA,GAAM,CAAE,aAAY,iBAAkB,EAAA,EAAmB,EAAS,CAChE,YAAe,EAAM,cAAgB,GACrC,SAAU,CACZ,CAAC,SAGD,EAAA,EAAA,MAAA,KACQ,EAAM,QACX,GAAQ,CACH,GAAO,EAAY,OACrB,EAAY,MAAM,YAAY,CAElC,CACF,WAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAkES,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAjEN,aAAA,EAAW,EAAA,MAAA,CAAA,CAAA,CAAO,EAAE,OAAO,OAAK,OAChC,QAAS,EAAA,QACT,OAAQ,EAAA,MACR,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,MAAI,EAAA,KAAK,CAAA,EACf,MAAA,GACC,UAAW,GACZ,MAAM,qBACL,mBAAc,AAAA,EAAA,KAAA,GAAE,EAAI,iBAAmB,CAAM,EAC7C,QAAA,EAAM,EAAA,MAAA,CAAA,CAAA,IAEP,SAAA,EAAA,EAAA,QAAA,KAsDO,EAAA,EAtDP,EAAA,mBAAA,CAsDO,OAAA,CAtDG,QAAA,UAAJ,IAAI,EAAU,MAAM,cAAe,UAAA,EAAM,EAAA,cAAA,CAAU,EAAM,CAAA,SAAA,CAAA,EAAG,UAAO,AAAA,EAAA,MAAE,GAAA,KAAA,EAAA,EAAA,MAAA,CAAA,CAAA,IAAA,EAAA,EAAA,MAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KACzE,EAAA,EAAA,YAAA,CAyBgB,GAAA,CAxBV,QAAA,cAAJ,IAAI,EACH,OAAQ,EAAA,OACR,YAAW,EAAA,SACX,aAAY,EAAA,UACZ,SAAU,EAAA,SACV,QAAS,EAAA,YACT,iBAAY,AAAA,EAAA,MAAG,EAAG,IAAM,EAAI,eAAiB,EAAG,CAAC,CAIhC,GAAA,EAAA,EAAA,YAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA,EAAA,EAAA,WAAA,CAAA,EAAA,OAAT,KAEG,KAAA,SAAA,EAAM,QAEhB,IAAA,EAAA,EAAA,QAAA,CAF2B,GAAQ,EAAA,EAEnC,EAAA,WAAA,CAAyD,EAAA,OAAA,SAAnC,EAAM,SAAA,EAAK,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAY,CAAQ,CAAA,CAAA,CAAA,CAAA,GAIrC,GAAA,EAAA,EAAA,WAAA,CAAA,EAAA,OAAO,OAAQ,GAAM,EAAE,OAAI,OAAA,EAApC,KAEG,KAAA,SAAA,EAAM,QAEhB,IAAA,EAAA,EAAA,QAAA,CAF2B,GAAQ,EAAA,EAEnC,EAAA,WAAA,CAAyD,EAAA,OAAA,SAAnC,EAAM,SAAA,EAAK,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAY,CAAQ,CAAA,CAAA,CAAA,CAAA,uEAOzD,EAAA,EAAA,WAAA,CAA0E,EAAA,OAAA,eAAA,CAA/C,SAAW,EAAA,SAAW,UAAY,EAAA,aAG7D,EAAA,EAAA,mBAAA,CAmBM,MAnBN,GAmBM,EAAA,EAlBJ,EAAA,WAAA,CAiBO,EAAA,OAAA,SAAA,CAjBc,OAAQ,EAAA,OAAS,SAAU,EAAA,QAAhD,MAiBO,EAAA,EAhBL,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPA,KAAK,SACJ,MAAO,EAAA,SAAQ,SAAA,WAChB,SAAS,YACT,KAAA,GACC,SAAU,EAAA,OACX,gBAAA,GACC,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,iBAAA,EAAA,CAGL,EAAA,KAAA,EAAA,CAAA,QAAA,UAAA,CAAA,EAAA,EAAA,2CAAA,EADT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAJA,KAAK,SACJ,MAAO,EAAA,UAAS,YAAA,SACjB,KAAK,cACJ,QAAS,EAAA,+GEhItB,SAAS,GAAU,EAAgB,EAAsC,CACvE,IAAM,EAAM,EAAI,EAAI,OAIpB,OAHI,EAAI,OAAe,EAAI,OAAO,EAAK,CAAG,EACtC,GAAQ,KAAkC,GAC1C,OAAO,GAAQ,UAAkB,EAAM,MAAQ,MAC5C,OAAO,CAAG,CACnB,CAEA,SAAS,GAAW,EAAe,EAA2B,CAS5D,OAPE,EAAM,SAAS,GAAG,GAClB,EAAM,SAAS,CAAS,GACxB,EAAM,SAAS;CAAI,GACnB,EAAM,SAAS,IAAI,EAEZ,IAAI,EAAM,QAAQ,KAAM,IAAI,EAAE,GAEhC,CACT,CAOA,SAAgB,GACd,EACA,EACA,EAAwB,CAAC,EACjB,CACR,IAAM,EAAM,EAAQ,WAAa,IAGjC,MAAO,IAAM,CAFE,EAAQ,IAAK,GAAM,GAAW,EAAE,OAAQ,CAAG,CAAC,CAAC,CAAC,KAAK,CAEpD,EAAQ,GADR,EAAK,IAAK,GAAQ,EAAQ,IAAK,GAAM,GAAW,GAAU,EAAG,CAAG,EAAG,CAAG,CAAC,CAAC,CAAC,KAAK,CAAG,CACtE,CAAK,CAAC,CAAC,KAAK;CAAM,CAC7C,CAGA,SAAgB,GAAY,EAAa,EAAW,aAAoB,CACtE,IAAM,EAAO,IAAI,KAAK,CAAC,CAAG,EAAG,CAAE,KAAM,yBAA0B,CAAC,EAC1D,EAAM,IAAI,gBAAgB,CAAI,EAC9B,EAAO,SAAS,cAAc,GAAG,EACvC,EAAK,KAAO,EACZ,EAAK,SAAW,EAChB,SAAS,KAAK,YAAY,CAAI,EAC9B,EAAK,MAAM,EACX,SAAS,KAAK,YAAY,CAAI,EAC9B,IAAI,gBAAgB,CAAG,CACzB,ugFCxBA,IAAM,EAAU,EAAA,EAAW,EAErB,EAAQ,EA8DR,EAAO,EAMP,GAAA,EAAe,EAAA,SAAA,CAAS,EACxB,CAAE,gBAAiB,EAAA,EAAc,EAEjC,GAAA,EAAe,EAAA,IAAA,CAAI,CAAC,CAAC,EAQ3B,SAAS,GAAyB,CAChC,OAAO,EAAM,aAAe,cAAc,EAAM,eAAiB,IACnE,CACA,SAAS,GAA8B,CACrC,IAAM,EAAM,EAAQ,EACpB,GAAI,EAAK,CACP,IAAM,EAAS,aAAa,QAAQ,CAAG,EACvC,GAAI,IAAW,SAAW,IAAW,QAAS,OAAO,CACvD,CACA,OAAO,EAAM,WACf,CACA,IAAM,GAAA,EAAc,EAAA,IAAA,CAAuB,EAAS,CAAC,EACrD,SAAS,EAAO,EAAkC,CAChD,OAAO,EAAY,QAAU,CAC/B,CACA,SAAS,EAAQ,EAA+B,CAC9C,EAAY,MAAQ,EACpB,IAAM,EAAM,EAAQ,EAChB,GAAK,aAAa,QAAQ,EAAK,CAAI,CACzC,CACA,IAAM,GAAA,EAAiB,EAAA,SAAA,MACd,EAAM,KAAK,WAAW,KAAO,GAAK,EAAM,KAAK,WAAW,QACjE,EAKM,GAAA,EAAc,EAAA,SAAA,CAA+B,CAAE,OAAQ,CAAC,CAAE,CAAC,EAC3D,GAAA,EAAU,EAAA,IAAA,CAAyC,IAAI,EAC7D,SAAS,IAA6B,CACpC,OAAO,EAAM,aAAe,cAAc,EAAM,eAAiB,IACnE,CACA,SAAS,GAAkB,CACzB,IAAM,EAAM,GAAY,EACnB,KACL,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,aAAa,QAAQ,CAAG,GAAK,IAAI,EACvD,MAAM,QAAQ,EAAO,MAAM,IAAG,EAAY,OAAS,EAAO,OAChE,MAAQ,CAER,CACF,CACA,SAAS,GAAkB,CACzB,IAAM,EAAM,GAAY,EACpB,GAAK,aAAa,QAAQ,EAAK,KAAK,UAAU,CAAE,OAAQ,EAAY,MAAO,CAAC,CAAC,CACnF,CAKA,SAAS,GAA4B,CACnC,OAAO,EAAM,aAAe,iBAAiB,EAAM,eAAiB,IACtE,CACA,SAAS,GAAc,CACrB,IAAM,EAAM,EAAW,EAClB,KACL,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,aAAa,QAAQ,CAAG,GAAK,IAAI,EAC3D,GAAI,GAAU,OAAO,EAAO,eAAkB,UAAY,EAAO,cAC1D,IAAA,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAO,aAAa,EAClD,GAAM,MAA2B,IAAM,KAAI,EAAM,KAAK,cAAc,GAAK,GAG7E,OAAO,EAAO,QAAW,UAAY,EAAO,SAC9C,EAAM,KAAK,OAAO,MAAQ,EAAO,OAErC,MAAQ,CAER,CACF,CACA,SAAS,GAAc,CACrB,IAAM,EAAM,EAAW,EAClB,GACL,aAAa,QACX,EACA,KAAK,UAAU,CACb,cAAe,CAAE,GAAG,EAAM,KAAK,aAAc,EAC7C,OAAQ,EAAM,KAAK,OAAO,KAC5B,CAAC,CACH,CACF,EACA,EAAA,EAAA,MAAA,CAAM,EAAM,KAAK,cAAe,EAAa,CAAE,KAAM,EAAK,CAAC,GAC3D,EAAA,EAAA,MAAA,CAAM,EAAM,KAAK,OAAQ,CAAW,EAEpC,IAAM,GAAA,EAAc,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,QAAQ,OAAQ,GAAM,EAAE,UAAY,EAAK,CAAC,EAC/F,SAAS,EAAa,EAAe,EAAkB,CACrD,EAAY,OAAS,EACjB,EAAY,OAAO,OAAQ,GAAM,IAAM,CAAK,EAC5C,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAY,OAAQ,CAAK,CAAC,CAAC,EAC/C,EAAgB,CAClB,CAEA,IAAM,GAAA,EAAiB,EAAA,SAAA,KACrB,EAAM,KAAK,OAAO,QACf,OAAQ,GAAM,EAAE,UAAY,IAAS,CAAC,EAAY,OAAO,SAAS,EAAE,KAAK,CAAC,CAAA,CAC1E,IAAK,GACA,EAAE,OAAS,UAAY,CAAC,EAAE,OAC1B,EAAE,OAAS,YAAc,CAAC,EAAE,MAAc,CAAE,GAAG,EAAG,MAAO,OAAiB,EACvE,CACR,CACL,EAEA,SAAS,GAAc,EAAyB,CAC9C,GAAI,EAAI,QAAU,QAAS,MAAO,aAClC,GAAI,EAAI,QAAU,SAAU,MAAO,aAErC,CAEA,IAAM,IAAA,EAAc,EAAA,SAAA,KAAe,EAAe,MAAM,MAAM,EAAG,EAAM,UAAU,CAAC,EAI5E,GAAA,EAAiB,EAAA,SAAA,KAA4B,CACjD,IAAM,EAAuB,CAAC,EAoB9B,OAnBI,EAAM,KAAK,OAAO,UAAY,IAChC,EAAQ,KAAK,CAAE,OAAQ,OAAQ,KAAM,eAAgB,QAAS,QAAS,CAAC,EAEtE,EAAM,KAAK,OAAO,YAAc,IAClC,EAAQ,KAAK,CACX,OAAQ,YACR,KAAM,aACN,QAAS,WACT,SAAU,MACZ,CAAC,EAEC,EAAM,KAAK,OAAO,YAAc,IAClC,EAAQ,KAAK,CACX,OAAQ,SACR,KAAM,cACN,QAAS,UACT,SAAU,QACZ,CAAC,EAEI,CACT,CAAC,EAEK,GAAA,EAAsB,EAAA,SAAA,KACpB,EAAM,KAAK,OAAO,YAAc,EAAe,KACvD,EAEM,IAAA,EAAa,EAAA,SAAA,KACX,EAAoB,MAAM,OAAS,GAAK,EAAQ,EAAM,cAC9D,EAEA,SAAS,GAAgB,EAAmB,EAA+B,CAEzE,GAAI,EAAO,QAAS,CAClB,EAAO,QAAQ,CAAI,EACnB,MACF,CACI,EAAO,SAAW,OACpB,EAAM,KAAK,eAAe,CAAI,EACrB,EAAO,SAAW,OAC3B,EAAM,KAAK,eAAe,CAAI,EACrB,EAAO,SAAW,YAC3B,EAAM,KAAK,oBAAoB,CAAI,EAC1B,EAAO,SAAW,WAGvB,EAAM,mBACR,GAAa,MAAQ,EACrB,EAAoB,MAAQ,IAE5B,EAAM,KAAK,cAAc,CAAI,EAGnC,CAGA,IAAM,IAAA,EAAe,EAAA,IAAA,CAAoC,IAAI,EACvD,GAAA,EAAsB,EAAA,IAAA,CAAI,EAAK,EAC/B,GAAA,EAAW,EAAA,IAAA,CAAI,EAAK,EAE1B,eAAe,IAAkB,CAC1B,MAAa,MAClB,GAAS,MAAQ,GACjB,GAAI,CACF,MAAM,EAAM,KAAK,cAAc,GAAa,KAAK,EACjD,EAAoB,MAAQ,GAC5B,GAAa,MAAQ,IACvB,QAAU,CACR,EAAS,MAAQ,EACnB,CAPiB,CAQnB,CAEA,SAAS,GAAgB,EAAmB,EAAwC,CAElF,MADA,CAAI,EAAO,SAAgB,EAAO,QAAQ,CAAI,CAEhD,CAEA,SAAS,GAAiB,EAAmB,EAAwC,CAEnF,OADI,EAAO,SAAiB,EAAO,SAAS,CAAI,EACzC,EACT,CAIA,IAAM,GAAA,EAAU,EAAA,SAAA,KAA0B,CACxC,IAAM,EAAkB,CAAC,EASzB,OARI,EAAM,SACR,EAAK,KAAK,CACR,KAAM,EAAM,QACZ,MAAO,EAAM,SACb,MAAO,EAAa,EAAM,KAAK,WAAW,KAAM,CAAC,CACnD,CAAC,EAEH,EAAK,KAAK,GAAG,EAAM,SAAS,EACrB,CACT,CAAC,EAEK,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,YAAc,EAAK,EAOhE,GAAA,EAAc,EAAA,SAAA,CAA2C,EAAC,aAE/D,EAGK,GAAA,EAAgB,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,gBAAkB,UAAU,EAG7E,GAAA,EAAW,EAAA,IAAA,CAAkC,IAAI,EASvD,SAAS,GAAY,EAAsB,CACzC,IAAM,EAAK,EAAM,KAAK,OAAO,IAAM,KAC7B,EAAQ,EAAM,KAAK,MAAM,MAAM,UAClC,GAAiC,EAAI,KAAQ,CAChD,EACA,GAAI,EAAQ,EAAG,OAEf,IAAM,GADO,EAAS,OAAO,IAAA,EACV,iBAA8B,YAAY,EACzD,CAAC,GAAQ,EAAK,SAAW,EAAM,KAAK,MAAM,MAAM,QACpD,EAAK,EAAM,EAAE,eAAe,CAAE,MAAO,UAAW,SAAU,QAAS,CAAC,CACtE,CAEA,EAAa,CAAE,cAAY,CAAC,EAG5B,IAAM,GAAA,EAAa,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,WAAa,MAAM,EACjE,IAAA,EAAe,EAAA,SAAA,KAAe,IAAI,IAAI,EAAM,KAAK,OAAO,KAAK,IAAK,GAAM,CAAC,EAAE,MAAO,CAAC,CAAC,CAAC,CAAC,EACtF,EAAe,CAAC,OAAQ,QAAS,SAAU,UAAU,EAC3D,SAAS,GAAY,EAAqC,CACxD,IAAM,EAAI,GAAa,MAAM,IAAI,CAAK,EACtC,OAAO,GAAK,EAAa,SAAS,EAAE,MAAQ,MAAM,EAAI,EAAI,IAAA,EAC5D,CACA,SAAS,GAAmB,EAKzB,CACG,EAAE,YAAc,EAAE,OAAS,EAAE,KAAK,EAAE,SACxC,EAAM,KAAK,YAAY,EAAE,KAAM,EAAE,MAAO,EAAE,QAAQ,CACpD,CAGA,IAAM,GAAA,EAAoB,EAAA,SAAA,KACxB,EAAM,KAAK,OAAO,QAAQ,OAAQ,GAAM,EAAE,MAAM,CAClD,EACM,GAAuB,CAC3B,CAAE,MAAO,MAAO,MAAO,EAAK,EAC5B,CAAE,MAAO,MAAO,MAAO,EAAM,CAC/B,EACA,SAAS,EAAO,EAAwB,CACtC,OAAO,EAAI,QAAQ,OAAS,EAAI,KAClC,CACA,IAAM,GAA8D,CAAC,EACrE,SAAS,GAAa,EAAgB,EAAe,CACnD,IAAM,EAAQ,EAAO,CAAG,EACpB,GAAa,IAAQ,aAAa,GAAa,EAAM,EACzD,GAAa,GAAS,eAAiB,EAAM,KAAK,gBAAgB,EAAO,GAAS,IAAI,EAAG,GAAG,CAC9F,CACA,IAAM,IAAA,EAAK,EAAA,IAAA,CAA6C,IAAI,EAE5D,SAAS,GAAU,EAA8B,CAC/C,EAAY,MAAQ,CACtB,CAEA,SAAS,GAAiB,EAAgE,CACnF,EAAM,cACX,EAAY,MAAQ,EAAM,KAC1B,GAAG,OAAO,KAAK,EAAM,aAAa,EACpC,CAEA,SAAS,GAAkB,EAAmB,EAA8B,CACrE,EAAM,cACX,EAAM,eAAe,EACrB,EAAY,MAAQ,EACpB,GAAG,OAAO,KAAK,CAAK,EACtB,CAEA,IAAM,IAAA,EAAmB,EAAA,SAAA,KAA2B,CAClD,IAAM,EAAM,EAAY,MACxB,GAAI,CAAC,EAAK,MAAO,CAAC,EAClB,IAAM,EAAoB,CACxB,CACE,MAAO,eACP,KAAM,YACN,YAAe,EAAM,KAAK,eAAe,CAAG,CAC9C,CACF,EAEA,IAAK,IAAM,KAAU,EAAoB,MAClC,GAAgB,EAAQ,CAAG,GAChC,EAAM,KAAK,CACT,MAAO,EAAO,SAAW,EAAO,OAChC,KAAM,EAAO,KACb,MAAO,EAAO,WAAa,SAAW,oBAAsB,IAAA,GAC5D,SAAU,GAAiB,EAAQ,CAAG,EACtC,YAAe,GAAgB,EAAQ,CAAG,CAC5C,CAAC,EAiBH,OAfI,EAAM,WACR,EAAM,KAAK,CACT,MAAO,WACP,KAAM,cACN,YAAe,EAAK,QAAS,CAAG,CAClC,CAAC,EAEC,EAAM,YACR,EAAM,KAAK,CAAE,UAAW,EAAK,CAAC,EAC9B,EAAM,KAAK,CACT,MAAO,EAAM,WAAa,MAAQ,sBAAwB,wBAC1D,KAAM,iBACN,YAAe,GAAY,CAC7B,CAAC,GAEI,CACT,CAAC,EAID,SAAS,IAAY,CACf,EAAY,OAAO,EAAK,QAAS,EAAY,KAAK,CACxD,CAIA,IAAM,IAAA,EAAY,EAAA,IAAA,CAAI,EAAK,EAE3B,eAAe,IAAc,CACvB,OAAU,MACd,IAAU,MAAQ,GAClB,GAAI,CAMF,GADY,GAHV,EAAM,WAAa,OACf,EAAM,KAAK,MAAM,MACjB,MAAM,EAAM,KAAK,SAAS,EAAM,WAAW,EACzB,EAAe,KAC3B,EAAK,EAAM,WAAW,CACpC,QAAU,CACR,GAAU,MAAQ,EACpB,CAVkB,CAWpB,QAIA,EAAA,EAAA,UAAA,KAAgB,CACd,EAAgB,EAChB,EAAY,EACR,EAAM,UACR,EAAM,KAAK,KAAK,CAEpB,CAAC,kDAIC,OAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA2mBM,MA3mBN,GA2mBM,CAzmBO,EAAA,aAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAcM,MAdN,GAcM,EAAA,EAbJ,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAyC,KAAzC,IAAA,EAAyC,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EACxB,EAAA,WAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6D,IAA7D,IAAA,EAA6D,EAAA,gBAAA,CAAf,EAAA,QAAQ,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAAA,EAExD,EAAA,mBAAA,CAQM,MARN,GAQM,EAAA,EAPJ,EAAA,WAAA,CAA8B,EAAA,OAAA,gBAAA,EAEtB,EAAA,OAAS,CAAK,EAAA,aAAA,EADtB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAHA,MAAM,OACN,KAAK,aACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,iBAAgB,2EAMnC,EAAA,EAAA,WAAA,CAYO,EAAA,OAAA,eAAA,CAAA,MAAA,CAXM,EAAA,MAAQ,SAAA,EAAnB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUM,MAVN,GAUM,GAAA,EATJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CARoB,EAAA,OAAb,EAAK,MAAlB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MAAA,CAR8B,IAAK,EAAK,MAAM,YAClD,EAAA,EAAA,EAAA,EAAA,mBAAA,CAEM,MAAA,CAFA,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,kBAAsB,EAAI,SAAQ,oBAAuB,EAAI,WAAQ,EAAA,CAAA,CAAA,EAAA,EAAA,EAC9E,EAAA,mBAAA,CAAyE,IAAA,CAArE,OAAA,EAAK,EAAA,eAAA,CAAA,CAAG,EAAI,IAAI,CAAA,EAAI,OAAA,EAAK,EAAA,eAAA,CAAE,EAAI,MAAK,UAAa,EAAI,QAAK,EAAA,CAEhE,EAAA,KAAA,CAAA,CAAA,EAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAmD,MAAnD,IAAA,EAAmD,EAAA,gBAAA,CAAlB,EAAI,KAAK,EAAA,CAAA,GAAA,EAC1C,EAAA,mBAAA,CAAmD,MAAnD,IAAA,EAAmD,EAAA,gBAAA,CAAlB,EAAI,KAAK,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,gDAOlD,EAAA,EAAA,mBAAA,CAggBM,MAAA,CAhgBD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,iBAAgB,CAAA,uBAAmC,EAAA,UAAU,CAAA,CAAA,CAAA,EAAA,EAAA,EACtE,EAAA,mBAAA,CAidM,MAjdN,GAidM,CA9cI,EAAA,MAAkB,SAAA,EAD1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAyDM,MAzDN,GAyDM,GAAA,EA9CJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAqCW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CArCa,EAAA,MAAP,KAAgC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAI,KAAA,EAAA,EAE1C,EAAI,QAAQ,MAAI,UAAA,SAAA,EADzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAJC,cAAc,EAAA,KAAK,cAAc,EAAO,CAAG,IAAA,GAC3C,YAAa,EAAI,QAAQ,aAAe,EAAI,OAC7C,KAAK,QACJ,QAAQ,GAAa,GAAa,EAAM,EAAE,OAA4B,KAAK,gDAGjE,CAAA,GAAA,EAAI,QAAQ,OAAI,WAAA,EAD7B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OARC,cAAa,EAAA,KAAK,cAAc,EAAO,CAAG,GAC1C,QAAS,EAAI,QAAQ,QACtB,eAAa,QACb,eAAa,QACZ,YAAa,EAAI,QAAQ,aAAe,EAAI,OAC7C,aAAA,GACA,KAAK,QACJ,sBAAqB,GAAe,EAAA,KAAK,gBAAgB,EAAO,CAAG,EAAG,CAAC,sEAG7D,CAAA,GAAA,EAAI,QAAQ,OAAI,YAAA,EAD7B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OARC,cAAa,EAAA,KAAK,cAAc,EAAO,CAAG,GAC1C,QAAS,GACV,eAAa,QACb,eAAa,QACZ,YAAa,EAAI,QAAQ,aAAe,EAAI,OAC7C,aAAA,GACA,KAAK,QACJ,sBAAqB,GAAe,EAAA,KAAK,gBAAgB,EAAO,CAAG,EAAG,CAAC,4DAG7D,CAAA,GAAA,EAAI,QAAQ,OAAI,YAAA,EAD7B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAJC,cAAc,EAAA,KAAK,cAAc,EAAO,CAAG,IAAA,KAC3C,YAAa,EAAI,QAAQ,aAAe,EAAI,OAC7C,KAAK,QACJ,sBAAqB,GAAc,EAAA,KAAK,gBAAgB,EAAO,CAAG,EAAG,CAAC,qGAInE,EAAA,GAAA,GAAA,OAAO,KAAK,EAAA,KAAK,aAAa,CAAA,CAAE,SAAA,EADxC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,MAAM,iBACN,KAAK,qBACL,KAAA,GACA,KAAK,QACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,mBAAkB,CAKxB,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAAA,EAAA,QAAW,UAAA,EAAtB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA0QM,MA1QN,GA0QM,CAvQI,EAAA,OAAiB,EAAA,KAAK,cAAc,MAAM,SAAA,EADlD,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqCM,MArCN,GAqCM,EAvBJ,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,KAAK,cAAc,MAAM,MAAM,EAAG,mBACvC,CAAA,GACA,EAAA,EAAA,WAAA,CAWO,EAAA,OAAA,eAAA,CAXoB,SAAU,EAAA,KAAK,cAAc,MAAQ,KAAM,EAAA,IAAtE,MAWO,GAAA,EAVL,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CASE,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CARa,EAAA,KAAK,OAAO,aAAW,CAAA,EAA7B,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPC,IAAK,EAAG,OACR,MAAO,EAAG,MACV,KAAM,EAAG,KACT,SAAU,EAAG,SACd,KAAK,QACJ,SAAU,EAAG,SAAW,EAAG,SAAS,EAAA,KAAK,cAAc,KAAK,EAAA,GAC5D,QAAK,GAAE,EAAG,QAAQ,EAAA,KAAK,cAAc,KAAK,uEAG/C,EAAA,EAAA,eAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALA,KAAK,cACL,KAAA,GACA,QAAA,GACA,KAAK,QACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,eAAc,iBALZ,wBAAL,CAAA,IAAV,EAAgC,IAQpC,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAiOY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAhON,QAAA,WAAJ,IAAI,EACI,gBAAe,EAAA,MAAA,wBAAA,AAAA,EAAA,KAAA,GAAA,EAAY,MAAA,EAClC,MAAO,EAAA,KAAK,MAAM,MAClB,QAAS,EAAA,KAAK,QAAQ,MACvB,UAAA,GACC,KAAM,EAAA,KAAK,WAAW,SACtB,gBAAe,EAAA,KAAK,WAAW,KAC/B,wBAAuB,CAAA,GAAA,GAAA,EAAA,EACxB,qBAAmB,8CACnB,+BAA6B,uCAC5B,GAAI,CAAA,YAAA,CAAA,KAAA,CAAA,MAAA,kBAAA,CAAA,CAAA,EACL,KAAA,GACA,eAAA,GACA,iBAAA,GACA,KAAK,QACJ,aAAY,EAAA,KAAK,KAAK,OAAS,IAAA,GAC/B,aAAY,EAAA,KAAK,KAAK,MACtB,WAAU,EAAA,KAAK,OAAO,IAAE,KACxB,YAAW,EAAA,MAAU,OAAY,IAAA,GACjC,UAA0B,EAAA,MAAgC,EAAA,KAAK,cAAc,MAAwB,EAAA,YAAc,EAAA,YAAgC,EAAA,MAAgC,IAAA,GAOnL,iBAA+B,EAAA,MAAgB,IAAA,GAAY,EAAA,YAAc,EAAA,YAAW,SAAc,IAAA,GAGlG,eAAc,EAAA,YACd,yBAAwB,EAAA,YAAc,EAAA,MAAc,IAAA,GACpD,qBAAgB,AAAA,EAAA,KAAkB,GAAY,EAAA,MAAiB,EAAA,KAAK,cAAc,MAAQ,EAAM,EAAA,MAAc,EAG9G,gCAA6B,AAAA,EAAA,KAAG,GAAY,EAAA,MAAc,EAC1D,iBAAiB,GACjB,OAAM,EAAA,KAAK,OACX,OAAI,AAAA,EAAA,MAAG,GAAW,EAAA,KAAK,OAAM,CAAA,UAAc,EAAE,UAAS,UAAa,EAAE,SAAS,CAAA,EAC9E,YAAU,AAAA,EAAA,MAAG,GAAM,EAAI,aAAe,EAAE,IAAI,EAC5C,cAAY,AAAA,EAAA,MAAG,GAAM,EAAI,eAAiB,EAAE,IAAI,EAC5B,0CAEV,QAAA,EAAM,EAAA,QAAA,KAyFT,EAAA,EAxFN,EAAA,mBAAA,CAwFM,MAxFN,GAwFM,EAAA,EAvFJ,EAAA,mBAAA,CAYM,MAZN,GAYM,CAXa,EAAA,aAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,IAAA,CAAA,EAAA,CAPV,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAJC,cAAa,EAAA,KAAK,OAAO,MAC1B,YAAY,YACZ,MAAM,OACL,QAAO,EAAA,KAAK,sFAGjB,EAAA,EAAA,WAAA,CAA6B,EAAA,OAAA,eAAA,GAC7B,EAAA,EAAA,WAAA,CAA+B,EAAA,OAAA,iBAAA,CAEjC,CAAA,GAAA,EAAA,EAAA,mBAAA,CAyEM,MAzEN,GAyEM,EAxEJ,EAAA,EAAA,WAAA,CAA+B,EAAA,OAAA,iBAAA,EACf,EAAA,eAAA,EAAhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqCW,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EApCT,EAAA,eAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAJA,KAAK,kBACL,KAAA,GACA,KAAK,QACJ,QAAK,AAAA,EAAA,KAAG,GAAM,EAAA,OAAS,OAAO,CAAC,iBAJjB,iBAAL,CAAA,IAAV,EAAyB,CAM3B,CAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EA4BU,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CA5BG,QAAA,UAAJ,IAAI,IACX,SAAA,EAAA,EAAA,QAAA,KA0BM,EAAA,EA1BN,EAAA,mBAAA,CA0BM,MA1BN,GA0BM,GAAA,EAlBJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAhBU,EAAA,MAAP,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAfH,IAAK,EAAI,MACV,MAAA,CAAA,QAAA,OAAA,cAAA,SAAA,IAAA,QAAA,CAEA,EAAA,EAAA,EAAA,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAJC,cAAW,CAAG,EAAY,OAAO,SAAS,EAAI,KAAK,EACnD,WAAQ,aAAe,EAAI,QAC5B,OAAA,GACC,sBAAqB,GAAM,EAAa,EAAI,MAAK,CAAA,CAAI,CAAC,yDAEzD,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKQ,QAAA,CAJL,IAAG,aAAe,EAAI,QACvB,MAAA,CAAA,YAAA,UAAA,OAAA,SAAA,CAEG,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAI,MAAM,EAAA,EAAA,EAAA,CAAA,CAAA,8DAOf,EAAA,WAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,KAAK,iBACL,KAAA,GACA,KAAK,QACJ,QAAS,GAAA,MACT,QAAO,6BAP8B,EAAA,WAAQ,MAAA,sBAAA,+BAApC,CAAA,IAAV,EAEC,qCAOQ,EAAA,aAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,MAfN,GAeM,EAAA,EAdJ,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALA,KAAK,cACL,KAAK,QACJ,KAAI,CAAG,EAAM,OAAA,EACb,SAAU,EAAM,OAAA,EAChB,QAAK,AAAA,EAAA,KAAA,GAAE,EAAO,OAAA,CAEjB,EAAA,KAAA,EAAA,CAAA,OAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALA,KAAK,iBACL,KAAK,QACJ,KAAI,CAAG,EAAM,OAAA,EACb,SAAU,EAAM,OAAA,EAChB,QAAK,AAAA,EAAA,KAAA,GAAE,EAAO,OAAA,kEAIV,CAAA,EAAA,YAAc,EAAA,OAAS,CAAK,EAAA,aAAA,EADrC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAHA,MAAM,OACN,KAAK,aACJ,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,iBAAgB,2CAM1B,OAAA,EAAK,EAAA,QAAA,KASP,EAAA,EARP,EAAA,WAAA,CAQO,EAAA,OAAA,QAAA,CAAA,MAAA,CAPL,AAAA,EAAA,OAAA,EAAA,EAAA,mBAAA,CAMM,MAAA,CAND,MAAM,cAAc,EAAA,EACvB,EAAA,EAAA,mBAAA,CAEM,MAAA,CAFD,MAAM,mBAAmB,EAAA,EAAA,EAC5B,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,CAAA,CAAA,CAAA,GAExB,EAAA,EAAA,mBAAA,CAA4D,IAAA,CAAzD,MAAM,oBAAoB,EAAC,4BAA0B,GACxD,EAAA,EAAA,mBAAA,CAAiF,IAAA,CAA9E,MAAM,mBAAmB,EAAC,kDAAgD,WAMnF,SAAA,EAAA,EAAA,QAAA,KAA6E,CAA/D,EAAA,QAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAA6E,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAAhD,iBAAe,WAAW,MAAA,CAAA,MAAA,MAAA,qCAGzC,EAAA,aAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAAyD,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAA/B,SAAA,GAAS,MAAA,CAAA,MAAA,MAAA,uCAGnC,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAqCS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CApCO,EAAA,MAAP,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAqCS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAnCN,IAAK,EAAI,MACT,MAAO,EAAI,MACX,OAAQ,EAAI,OACZ,SAAU,EAAI,SACd,OAAA,EAAK,EAAA,eAAA,CAAE,EAAI,KAAK,EAChB,eAAc,GAAc,CAAG,EAC/B,aAAY,GAAc,CAAG,sBAEnB,MAAA,EAAI,EAAA,QAAA,EAGN,CAHU,UAAI,EAAA,EACrB,EAAA,WAAA,CAEO,EAAA,OAAA,UAFgB,EAAI,QAAK,CAAW,OAAO,MAAO,EAAK,EAAI,MAAlE,MAEO,EAAA,EADL,EAAA,YAAA,CAA+E,GAAA,CAAzD,OAAQ,EAAM,MAAO,EAAK,EAAI,OAAS,WAAU,gDAG3D,EAAA,CAAA,EAAA,OAAc,GAAY,EAAI,KAAK,EAAA,CAAI,KAAA,SACrD,IAAA,EAAA,EAAA,QAAA,EASE,CAV6D,UAAI,CAEpC,CAAA,SAAA,UAAA,CAAA,CAAA,SAAS,GAAY,EAAI,KAAK,CAAA,EAAG,MAAI,EAAA,IAAA,EADpE,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,cAAc,EAAK,EAAI,QAAK,KAC7B,MAAA,GACC,sBAA0C,GAAC,CAA6B,EAAK,EAAI,OAAS,EAM7F,EAAA,KAAA,EAAA,CAAA,cAAA,qBAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,cAAc,EAAK,EAAI,QAAK,GAC7B,MAAA,GACC,sBAA0C,GAAC,CAA6B,EAAK,EAAI,OAAS,uJAWzF,GAAA,OAAU,CAAK,EAAA,aAAA,EADvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAyBS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAvBP,eAAa,wBACZ,OAAA,EAAK,EAAA,eAAA,CAAA,CAA+B,MAAA,IAAA,EAAA,MAAoB,QAAA,IAAA,EAAU,EAAA,MAAA,CAAA,CAAA,CAAK,CAAA,gBAAA,IAAA,EAAA,IAAA,CAAA,IAI7D,MAAA,EAAI,EAAA,QAAA,EAgBP,CAhBW,UAAI,EAAA,EACrB,EAAA,mBAAA,CAeM,MAfN,GAeM,GAAA,EAdJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAYW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAZgB,EAAA,MAAV,KAAqC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAO,MAAA,EAAA,CAEnD,GAAgB,EAAQ,CAAI,GAAA,EADpC,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,KAAM,EAAO,KACd,KAAA,GACA,QAAA,GACA,KAAK,QACJ,SAAU,EAAO,SACjB,SAAU,GAAiB,EAAQ,CAAI,EACvC,QAAK,GAAE,GAAgB,EAAQ,CAAI,yDAPrB,EAAO,eAAZ,CAAA,IAAV,EAA8B,0CAUlC,EAAA,GAAA,IAAA,EAAA,EAAA,WAAA,CAAqD,EAAA,OAAA,cAAA,CAArB,OAAO,KAAM,EAAA,mEAMnC,EAAA,CAAA,EAAA,WAAA,CAAa,KAAA,YAC3B,IAAA,EAAA,EAAA,QAAA,CADsC,GAAS,EAAA,EAC/C,EAAA,WAAA,CAAgD,EAAA,OAAA,YAAA,CAAxB,KAAM,EAAU,IAAA,CAAA,CAAA,CAAA,iNAM9C,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqIM,MArIN,GAqIM,EApIJ,EAAA,EAAA,mBAAA,CAgDM,MAhDN,GAgDM,EAAA,EA/CJ,EAAA,mBAAA,CAYM,MAZN,GAYM,CAXa,EAAA,aAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,IAAA,CAAA,EAAA,CAPV,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAJC,cAAa,EAAA,KAAK,OAAO,MAC1B,YAAY,YACZ,MAAM,OACL,QAAO,EAAA,KAAK,sFAGjB,EAAA,EAAA,WAAA,CAA6B,EAAA,OAAA,eAAA,GAC7B,EAAA,EAAA,WAAA,CAA+B,EAAA,OAAA,iBAAA,CAEjC,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiCM,MAjCN,GAiCM,EAhCJ,EAAA,EAAA,WAAA,CAA+B,EAAA,OAAA,iBAAA,EAEvB,EAAA,WAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,KAAK,iBACL,KAAA,GACA,KAAK,QACJ,QAAS,GAAA,MACT,QAAO,6BALO,EAAA,WAAQ,MAAA,sBAAA,+BAAb,CAAA,IAAV,EAAoF,qCAO3E,EAAA,aAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,MAfN,GAeM,EAAA,EAdJ,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALA,KAAK,cACL,KAAK,QACJ,KAAI,CAAG,EAAM,OAAA,EACb,SAAU,EAAM,OAAA,EAChB,QAAK,AAAA,EAAA,MAAA,GAAE,EAAO,OAAA,CAEjB,EAAA,KAAA,EAAA,CAAA,OAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALA,KAAK,iBACL,KAAK,QACJ,KAAI,CAAG,EAAM,OAAA,EACb,SAAU,EAAM,OAAA,EAChB,QAAK,AAAA,EAAA,MAAA,GAAE,EAAO,OAAA,kEAIV,CAAA,EAAA,YAAc,EAAA,OAAS,CAAK,EAAA,aAAA,EADrC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAHA,MAAM,OACN,KAAK,aACJ,QAAK,AAAA,EAAA,MAAA,GAAE,EAAA,KAAK,iBAAgB,yCAKxB,EAAA,KAAK,QAAQ,QAAA,EAAxB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,CAAA,GAAA,AAAA,EAAA,MAAA,EAAA,EADJ,EAAA,mBAAA,CAAmC,IAAA,CAAhC,MAAM,uBAAuB,EAAA,KAAA,EAAA,CAAA,CAAA,CAAA,GAGhB,EAAA,KAAK,MAAM,MAAM,SAQ5B,EAEP,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAuDM,MAAA,OAvDM,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,eAAc,CAAA,qBAAiC,EAAA,QAAQ,CAAA,CAAA,CACvE,EAAA,GAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAqDM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CApDiB,EAAA,KAAK,MAAM,OAAxB,EAAK,MADf,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAqDM,MAAA,CAnDH,IAAM,EAAI,EAAA,KAAK,OAAO,IAAE,OAAiC,EACzD,OAAA,EAAK,EAAA,eAAA,CAAmB,EAAA,SAAA,mBAA+G,CAAA,cAAA,CAAA,wBAAA,EAAA,QAAgB,CAAG,CAAA,CAAA,EAK1J,QAAK,GAAA,CAAG,EAAA,UAAY,GAAU,CAAG,EACjC,WAAQ,GAAA,CAAG,EAAA,UAAY,EAAA,KAAK,OAAO,UAAO,IAAc,EAAA,KAAK,eAAe,CAAG,EAC/E,cAAW,GAAA,CAAG,EAAA,UAAY,GAAkB,EAAQ,CAAG,CAMxD,EAAA,EAAA,EAAA,EAAA,WAAA,CAoCO,EAAA,OAAA,OAAA,CApCY,KAAM,EAAM,KAAM,EAAA,KAAO,QAAS,GAAA,MAAc,MAAO,CAA1E,MAoCO,EAAA,EAnCL,EAAA,mBAAA,CAkBM,MAlBN,GAkBM,GAAA,EAjBJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAgBM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAfgB,GAAA,OAAZ,EAAK,MADf,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAgBM,MAAA,CAdH,IAAK,EAAI,MACV,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,kBAAiB,CAAA,yBACa,IAAE,CAAA,CAAA,CAAA,CAE1B,EAAA,CAAA,IAAE,GAA+C,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAA/C,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAuE,OAAvE,IAAA,EAAuE,EAAA,gBAAA,CAApB,EAAI,MAAM,EAAA,CAAA,IAAA,EAC7D,EAAA,mBAAA,CAQO,OARP,GAQO,EAAA,EAPL,EAAA,WAAA,CAMO,EAAA,OAAA,UANgB,EAAI,QAAK,CAAK,KAAM,EAAM,MAAO,EAAI,EAAI,MAAhE,MAMO,EAAA,EALL,EAAA,YAAA,CAIE,GAAA,CAHC,OAAQ,EACR,MAAO,EAAI,EAAI,OACf,WAAU,iDAMV,EAAA,GAAA,EAAA,CAAA,EAAA,GAAA,OAAU,CAAK,EAAA,aAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,MAfN,GAeM,GAAA,EAdJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAYW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAZgB,EAAA,MAAV,KAAqC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAO,MAAA,EAAA,CAEnD,GAAgB,EAAQ,CAAG,GAAA,EADnC,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAPC,KAAM,EAAO,KACd,KAAA,GACA,QAAA,GACA,KAAK,QACJ,SAAU,EAAO,SACjB,SAAU,GAAiB,EAAQ,CAAG,EACtC,QAAK,GAAE,GAAgB,EAAQ,CAAG,yDAPpB,EAAO,eAAZ,CAAA,IAAV,EAA8B,0CAUlC,EAAA,GAAA,IAAA,EAAA,EAAA,WAAA,CAAoD,EAAA,OAAA,cAAA,CAA1B,KAAM,EAAM,KAAM,EAAA,kEA7DjB,EAAnC,EAAA,WAAA,CAQO,EAAA,OAAA,QAAA,CAAA,MAAA,CAAA,AAAA,EAAA,OAAA,EAAA,EAAA,kBAAA,CAAA,gPAAA,CAAA,CAAA,EAAA,IAAA,GAAA,CAAA,EA4DC,EAAA,KAAK,MAAM,MAAM,SAAA,EADzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OARC,KAAM,EAAA,KAAK,WAAW,SACtB,gBAAe,EAAA,KAAK,WAAW,KAC/B,MAAO,EAAA,MACP,wBAAuB,CAAA,GAAA,GAAA,EAAA,EACxB,SAAS,8CACT,+BAA6B,uCAC7B,MAAM,mBACL,OAAM,EAAA,KAAK,0FAOL,CAAA,EAAA,CAAA,EAAA,EAAA,aAAA,EAAb,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAyCQ,QAzCR,GAyCQ,CAvCE,EAAA,OAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAHA,KAAK,aACL,QAAA,GACC,QAAK,AAAA,EAAA,MAAA,GAAE,EAAA,KAAK,iBAAgB,kBAHb,cAAN,CAAA,KAAV,EAAuB,qCAKd,EAAA,OAAa,EAAA,MAAoB,SAAA,EAA5C,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA8E,MAA9E,EAA8E,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAC9E,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAWW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAXgB,EAAA,MAAV,KAAqC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAO,MAAA,EAAA,CAElD,CAAA,EAAA,OAAe,GAAgB,EAAQ,EAAA,KAAW,GAAA,EAD3D,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OANC,KAAM,EAAO,KACd,KAAA,GACA,QAAA,GACC,SAAU,EAAO,SACjB,SAAQ,CAAG,EAAA,OAAe,GAAiB,EAAQ,EAAA,KAAW,EAC9D,QAAK,GAAE,EAAA,OAAe,GAAgB,EAAQ,EAAA,KAAW,yDAN1C,EAAO,eAAb,CAAA,KAAV,EAA+B,mDASnC,EAAA,EAAA,WAAA,CAAiE,EAAA,OAAA,eAAA,CAAtC,SAAU,EAAA,MAAc,KAAM,EAAA,OAC9C,EAAA,WAAa,EAAA,YAAA,EAAxB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6D,MAA7D,EAA6D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAErD,EAAA,WAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,KAAK,cACL,KAAA,GACA,QAAA,GACC,SAAQ,CAAG,EAAA,MACX,QAAO,8BALQ,kBAAN,CAAA,KAAV,EAA2B,qCAQrB,EAAA,WAAA,EADR,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALA,KAAK,iBACL,KAAA,GACA,QAAA,GACC,QAAS,GAAA,MACT,QAAO,6BALQ,EAAA,WAAQ,MAAA,sBAAA,+BAAd,CAAA,KAAV,EAAqF,4EAexE,EAAA,cAAA,EAAnB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAAqE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAAjC,QAAA,KAAJ,IAAI,GAAM,MAAO,GAAA,2DAGjD,EAAA,EAAA,WAAA,CA6BO,EAAA,OAAA,cAAA,CA7BmB,KAAM,EAAA,KAAO,YAAc,EAAA,WAArD,MA6BO,EAAA,EA5BL,EAAA,YAAA,CA2BkB,GAAA,CA1Bf,QAAS,EAAA,KAAK,cAAc,MAC5B,MAAO,EAAA,KAAK,YAAY,MACxB,OAAQ,EAAA,KAAK,OAAO,KACpB,YAAW,EAAA,KAAK,SAChB,aAAY,EAAA,KAAK,UAAU,MAC3B,OAAQ,EAAA,KAAK,OAAO,MACpB,SAAU,EAAA,KAAK,UAAU,OAAK,GAC9B,MAAO,EAAA,YACP,eAAc,EAAA,aAAe,EAAA,KAAK,OAAO,YACzC,eAAc,EAAA,KAAK,OAAO,YAC1B,mBAAc,AAAA,EAAA,MAAc,GAAC,CAAmB,EAAA,KAAK,cAAc,MAAQ,EAAmB,IAAG,EAAA,KAAK,YAAY,MAAK,OAMvH,iBAAY,AAAA,EAAA,OAAG,EAAO,IAAQ,EAAA,KAAK,aAAa,EAAO,CAAG,EAC1D,OAAI,AAAA,EAAA,MAAA,GAAE,EAAA,KAAK,KAAI,CAGE,GAAA,EAAA,EAAA,YAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA,EAAA,EAAA,WAAA,CAAA,EAAA,KAAK,OAAO,KAArB,KAEG,KAAA,SAAA,EAAM,QAEhB,IAAA,EAAA,EAAA,QAAA,CAF2B,GAAK,EAAA,EAEhC,EAAA,WAAA,CAAsD,EAAA,OAAA,SAAhC,EAAM,SAAA,EAAK,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAY,CAAK,CAAA,CAAA,CAAA,CAAA,+HAQhDC,EAAAA,OAAM,oBAAA,EADd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EA6BS,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OA3BC,QAAS,EAAA,MAAA,mBAAA,AAAA,EAAA,MAAA,GAAA,EAAmB,MAAA,EACnC,aAAA,EAAW,EAAA,MAAA,CAAA,CAAA,CAAO,EAAE,OAAO,OAAK,OACjC,OAAO,qBACP,MAAA,GACC,UAAW,GACZ,MAAM,uBACL,MAAO,CAAA,MAAA,OAAA,IAGG,QAAA,EAAM,EAAA,QAAA,KAQb,EAAA,EAPF,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,KAAK,SACL,MAAM,WACN,SAAS,YACT,KAAA,GACC,SAAU,EAAA,MACV,QAAK,AAAA,EAAA,MAAA,GAAE,EAAA,MAAmB,EAE7B,EAAA,KAAA,EAAA,CAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANA,KAAK,SACL,MAAM,UACN,KAAK,cACL,SAAS,SACR,QAAS,EAAA,MACT,QAAO,0BAhBZ,SAAA,EAAA,EAAA,QAAA,KAAmD,EAAA,EAAnD,EAAA,WAAA,CAAmD,EAAA,OAAA,iBAAA,CAAtB,KAAM,GAAA,KAAY,CAAA,CAAA,CAAA,iyCE3iCrD,IAAM,EAAQ,EAgCR,EAAO,EAMP,CAAE,gBAAiB,EAAA,EAAc,EAKjC,GAAA,EAAW,EAAA,IAAA,CAAiB,IAAI,GAAK,EAE3C,SAAS,EAAW,EAAwB,CAC1C,OAAO,EAAS,MAAM,IAAI,CAAK,CACjC,CAEA,SAAS,EAAU,EAAe,CAChC,IAAM,EAAO,IAAI,IAAI,EAAS,KAAK,EAC/B,EAAK,IAAI,CAAK,EAAG,EAAK,OAAO,CAAK,EACjC,EAAK,IAAI,CAAK,EACnB,EAAS,MAAQ,CACnB,CAIA,SAAS,EAAc,EAAsD,CAC3E,IAAM,EAAU,EAAO,QAEvB,OADK,GACL,EAAQ,EAAA,MAAA,CAAM,CAAO,EAAI,EAAQ,MAAQ,EADpB,CAAC,CAExB,CAEA,SAAS,EAAY,EAAwD,CAE3E,OADI,EAAO,MAAc,EAAO,MACzB,EAAO,SAAW,SAAW,QAAU,MAChD,CAEA,SAAS,EAAgB,EAAoC,CAC3D,OAAO,EAAO,SAAW,UAAY,EAAO,SAAW,OAAS,EAAO,UAAY,IACrF,CAEA,SAAS,EAAY,EAAqC,CACxD,MAAO,CACL,2BAA2B,EAAY,CAAM,IAC7C,GAAI,EAAgB,CAAM,EAAI,CAAC,6BAA6B,EAAI,CAAC,CACnE,CACF,CAEA,SAAS,EAAY,EAA2B,CAC1C,KAAO,OAAS,KACpB,MAAO,CAAE,MAAO,OAAO,EAAO,OAAU,SAAW,GAAG,EAAO,MAAM,IAAM,EAAO,KAAM,CACxF,CAEA,SAAS,EAAe,EAA2B,EAAkB,EAAwB,CAG3F,OAFI,EAAM,SAAiB,GACvB,OAAO,EAAO,UAAa,WAAmB,EAAO,SAAS,EAAK,CAAK,EACrE,CAAC,CAAC,EAAO,QAClB,CAIA,SAAS,EAAS,EAAwB,CACxC,GAAI,OAAO,GAAU,SAAU,OAAO,OAAO,SAAS,CAAK,EAAI,EAAQ,EACvE,GAAI,OAAO,GAAU,UAAY,EAAM,KAAK,EAAG,CAC7C,IAAM,EAAS,OAAO,EAAM,QAAQ,MAAO,EAAE,CAAC,CAAC,QAAQ,IAAK,GAAG,CAAC,EAChE,OAAO,OAAO,SAAS,CAAM,EAAI,EAAS,CAC5C,CACA,MAAO,EACT,CAEA,SAAS,EAAa,EAA2B,EAA0B,CACzE,IAAM,EAAQ,EAAI,EAAO,OAMzB,OALI,EAAO,OAAe,EAAO,OAAO,EAAO,CAAG,EAC9C,GAAS,MAAQ,IAAU,GAAW,IACtC,EAAgB,CAAM,EACjB,GAAG,EAAa,EAAS,CAAK,EAAG,EAAO,UAAY,CAAC,IAAI,EAAO,QAAU,KAE5E,GAAG,OAAO,CAAK,IAAI,EAAO,QAAU,IAC7C,CAEA,SAAS,EAAQ,EAAe,EAAe,EAAgB,CAC7D,IAAM,EAAO,EAAM,WAAW,KAAK,EAAK,IAAO,IAAM,EAAQ,CAAE,GAAG,GAAM,GAAQ,CAAM,EAAI,CAAI,EAC9F,EAAK,oBAAqB,CAAI,CAChC,CAEA,SAAS,EAAU,EAAkB,EAAe,CAGlD,EACE,oBACA,EAAM,WAAW,QAAQ,EAAG,IAAM,IAAM,CAAK,CAC/C,EACA,EAAS,MAAQ,IAAI,IACrB,EAAK,SAAU,EAAK,CAAK,CAC3B,CAEA,SAAS,EAAS,EAAkB,EAAgC,CAClE,GAAI,EAAM,OAAQ,CAChB,IAAM,EAAM,EAAI,EAAM,QACtB,GAAI,OAAO,GAAQ,UAAY,OAAO,GAAQ,SAAU,OAAO,CACjE,CACA,OAAO,CACT,CAIA,IAAM,GAAA,EAAY,EAAA,SAAA,KAChB,EAAM,QAAQ,KAAM,GAAM,OAAO,EAAE,QAAW,YAAc,EAAE,SAAW,KAAK,CAChF,EAEA,SAAS,EAAY,EAAmC,CACtD,GAAI,OAAO,EAAO,QAAW,WAAY,OAAO,EAAO,OAAO,EAAM,UAAU,EAC9E,GAAI,EAAO,SAAW,MAAO,MAAO,GACpC,IAAM,EAAQ,EAAM,WAAW,QAAQ,EAAK,IAAQ,EAAM,EAAS,EAAI,EAAO,MAAM,EAAG,CAAC,EACxF,MAAO,GAAG,EAAa,EAAO,EAAO,UAAY,CAAC,IAAI,EAAO,QAAU,IACzE,CAGA,IAAM,GAAA,EAAmB,EAAA,SAAA,KACT,EAAM,QAAQ,KAAM,GAAM,CAAC,EAAE,QAAU,EAAE,SAAW,MAC3D,CAAA,EAAO,OAAS,IACxB,EAEK,GAAA,EAAc,EAAA,SAAA,KACZ,EAAM,QAAQ,QAAU,KAAM,YAAuB,KAAM,SACnE,EAMM,GAAA,EAAkB,EAAA,SAAA,KAAe,EAAM,QAAQ,KAAM,GAAM,EAAE,KAAK,CAAC,EAEnE,GAAA,EAAmB,EAAA,SAAA,KAAe,CACtC,IAAM,EAAwD,CAAC,EAC/D,IAAK,IAAM,KAAU,EAAM,QAAS,CAClC,IAAM,EAAQ,EAAO,OAAS,GACxB,EAAO,EAAM,EAAM,OAAS,GAC9B,GAAQ,GAAS,EAAK,QAAU,EAAO,EAAK,MAAQ,EACnD,EAAM,KAAK,CAAE,QAAO,KAAM,EAAG,IAAK,EAAO,KAAM,CAAC,CACvD,CACA,OAAO,CACT,CAAC,iBAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAoKM,MApKN,GAoKM,CAnKOC,EAAAA,OAAO,SAAW,EAAA,WAAA,EAA7B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAWM,MAXN,GAWM,EAAA,EAVJ,EAAA,WAAA,CASO,EAAA,OAAA,UAAA,CAAA,MAAA,EAAA,EARL,EAAA,mBAAA,CAOS,SAAA,CANP,KAAK,SACL,MAAM,wBACL,SAAU,EAAA,SACV,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,KAAA,CAEZ,EAAA,CAAA,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAwB,IAAA,CAArB,MAAM,YAAY,EAAA,KAAA,EAAA,GAAA,EAAM,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,QAAQ,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAKzC,EAAA,mBAAA,CAqJQ,QArJR,GAqJQ,EApJN,EAAA,EAAA,mBAAA,CA0BQ,QAAA,KAAA,CAzBI,EAAA,QAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAYK,KAZL,GAYK,CAXO,EAAA,aAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6D,KAA7D,EAA6D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAC7D,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAPY,EAAA,MAAR,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQK,KAAA,CANF,IAAK,EAAK,IACV,QAAS,EAAK,KACf,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,0BAAyB,CAAA,iCACa,EAAK,KAAK,CAAA,CAAA,CAEnD,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAK,KAAK,EAAA,GAAA,EAAA,UAEL,EAAA,YAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,KAA5D,EAA4D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAE9D,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAWK,KAAA,KAAA,CAVO,EAAA,aAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6D,KAA7D,EAA6D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAC7D,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAOK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CANc,EAAA,QAAV,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOK,KAAA,CALF,IAAK,EAAO,MACZ,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,EACzB,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,CAEvB,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAO,MAAM,EAAA,CAAA,UAER,EAAA,YAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,KAA5D,EAA4D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,MAIlD,EAAA,WAAW,QAGe,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAHf,EAAzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMQ,QAAA,GAAA,EAAA,EALN,EAAA,mBAAA,CAIK,KAAA,KAAA,EAAA,EAHH,EAAA,mBAAA,CAEK,KAAA,CAFA,QAAS,EAAA,MAAa,MAAM,yBAC/B,EAAA,EAAA,EAAA,EAAA,WAAA,CAA4C,EAAA,OAAA,QAAA,CAAA,MAAA,EAAA,EAAtB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,YAAY,EAAA,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,KAKxC,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CA2FQ,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CA3FsB,EAAA,YAAf,EAAK,MAApB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA2FQ,QAAA,CA3FmC,IAAK,EAAS,EAAK,CAAK,CAAA,EAAA,EAAA,EAIjE,EAAA,mBAAA,CAgFK,KAAA,CA/EH,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,wBAAuB,CAAA,6BACW,EAAK,GAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAEnC,EAAA,aAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUK,KAVL,GAUK,EAAA,EATH,EAAA,mBAAA,CAQS,SAAA,CAPP,KAAK,SACL,MAAM,2BACL,gBAAe,EAAW,CAAK,EAChC,aAAW,oBACV,QAAK,GAAE,EAAU,CAAK,CAEvB,EAAA,EAAA,EAAA,EAAA,mBAAA,CAA+E,IAAA,CAA3E,OAAA,EAAK,EAAA,eAAA,CAAE,EAAW,CAAK,EAAA,qBAAA,qBAAA,CAAA,EAAA,KAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAI/B,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAoDK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAnDc,EAAA,QAAV,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAoDK,KAAA,CAlDF,IAAK,EAAO,MACZ,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,EACzB,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,CAE1B,EAAA,EAAA,EAAA,EAAA,WAAA,CA6CO,EAAA,OAAA,QA5CU,EAAO,QAAK,CACrB,MACE,QACP,MAAO,EAAI,EAAO,OACV,QALX,MA6CO,CArCG,EAAO,SAAM,WAAA,EADrB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAcE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAZC,cAAa,EAAI,EAAO,OACzB,MAAA,GACA,OAAO,QACN,IAAK,EAAO,IACZ,IAAK,EAAO,IACZ,sBAAqB,EAAO,UAAQ,KAA6B,IAAA,GAAlB,EAAO,SACtD,sBAAqB,EAAO,UAAQ,KAA6B,IAAA,GAAlB,EAAO,SACtD,OAAQ,EAAO,OACf,YAAa,EAAO,YACpB,SAAU,EAAe,EAAQ,EAAK,CAAK,EAC3C,cAAa,wBACb,sBAAqB,GAAQ,EAAQ,EAAO,EAAO,MAAO,CAAG,wIAInD,CAAA,GAAA,EAAO,SAAM,SAAA,EAD1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALC,cAAa,EAAI,EAAO,OACzB,MAAA,GACC,YAAa,EAAO,YACpB,SAAU,EAAe,EAAQ,EAAK,CAAK,EAC3C,sBAAqB,GAAQ,EAAQ,EAAO,EAAO,MAAO,CAAG,uEAInD,CAAA,GAAA,EAAO,SAAM,WAAA,EAD1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OARC,cAAa,EAAI,EAAO,OACzB,MAAA,GACC,QAAS,EAAc,CAAM,EAC7B,eAAc,EAAO,aAAW,QAChC,eAAc,EAAO,aAAW,QAChC,YAAa,EAAO,YACpB,SAAU,EAAe,EAAQ,EAAK,CAAK,EAC3C,sBAAqB,GAAQ,EAAQ,EAAO,EAAO,MAAO,CAAG,+GAGhE,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAkF,OAAlF,IAAA,EAAkF,EAAA,gBAAA,CAAnC,EAAa,EAAQ,CAAG,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA,EAAA,CAAA,UAIjE,EAAA,YAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CASK,KATL,GASK,EAAA,EARH,EAAA,mBAAA,CAOS,SAAA,CANP,KAAK,SACL,MAAM,2BACN,aAAW,gBACV,QAAK,GAAE,EAAU,EAAK,CAAK,CAE5B,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAKlB,EAAA,CAAA,EAAA,EAAA,YAAc,EAAW,CAAK,IAAA,EAAxC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIK,KAJL,GAIK,EAAA,EAHH,EAAA,mBAAA,CAEK,KAAA,CAFA,QAAS,EAAA,KAAW,EAAA,EAAA,EACvB,EAAA,WAAA,CAAmD,EAAA,OAAA,YAAA,CAAtB,MAAa,8DAKnC,EAAA,QAAA,EAAb,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAkBQ,QAAA,GAAA,EAAA,EAjBN,EAAA,mBAAA,CAgBK,KAhBL,GAgBK,CAfO,EAAA,aAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6D,KAA7D,EAA6D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAC7D,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAYK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAXc,EAAA,QAAV,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAYK,KAAA,CAVF,IAAK,EAAO,MACZ,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,EACzB,OAAA,EAAK,EAAA,eAAA,CAAE,EAAY,CAAM,CAAA,CAE1B,EAAA,EAAA,EAAA,EAAA,WAAA,CAKO,EAAA,OAAA,UALgB,EAAO,QAAK,CAAK,KAAM,EAAA,WAAqB,QAAnE,MAKO,CAJO,EAAO,QAAU,EAAA,QAAA,EAA7B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,WAAW,EAAA,CAAA,KAAA,EAEhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6C,OAAA,IAAA,EAAA,EAAA,gBAAA,CAA7B,EAAY,CAAM,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA,EAAA,CAAA,UAG5B,EAAA,YAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,KAA5D,EAA4D,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,0YEnShE,GAAe,gTA3CrB,IAAM,EAAQ,EA+BR,GAAA,EAAQ,EAAA,SAAA,CAAS,EAIjB,GAAA,EAAS,EAAA,SAAA,KAAe,EAAM,KAAK,MAAM,KAAK,EAE9C,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,YAAc,IAAS,CAAC,EAAM,QAAQ,EACnF,GAAA,EAAa,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,UAAY,IAAS,CAAC,EAAM,QAAQ,EAClF,GAAA,EAAc,EAAA,SAAA,KAAe,EAAM,KAAK,OAAO,YAAc,IAAS,CAAC,EAAM,QAAQ,EACrF,GAAA,EAAW,EAAA,SAAA,KAAe,EAAW,OAAS,EAAY,KAAK,EAK/D,GAAA,EAAU,EAAA,SAAA,KACd,EAAS,MACL,CAAC,GAAG,EAAM,QAAS,CAAE,MAAO,GAAc,OAAQ,GAAI,MAAO,OAAQ,MAAO,OAAQ,CAAC,EACrF,EAAM,OACZ,EAIM,GAAA,EAAgB,EAAA,SAAA,KACpB,OAAO,KAAK,CAAK,CAAC,CAAC,OAAQ,GAAS,EAAK,WAAW,OAAO,GAAK,IAAS,QAAQ,IAAc,CACjG,EAEM,GAAA,EAAe,EAAA,SAAA,KACnB,OAAO,KAAK,CAAK,CAAC,CAAC,OAAQ,GAAS,EAAK,WAAW,QAAQ,GAAK,EAAK,WAAW,QAAQ,CAAC,CAC5F,EAGA,SAAS,EAAS,EAA6B,CAC7C,EAAM,KAAK,OAAO,SAAS,CAAM,CACnC,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA0FM,MA1FN,GA0FM,CAzFO,EAAA,OAASC,EAAAA,OAAM,qBAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAWM,MAXN,GAWM,EAAA,EAVJ,EAAA,mBAAA,CAMM,MANN,GAMM,CALM,EAAA,QAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGK,KAHL,GAGK,EAAA,EAFA,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,KAAK,EAAG,IACX,CAAA,EAAY,EAAA,MAAO,SAAA,EAAnB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAmF,OAAnF,IAAA,EAAmF,EAAA,gBAAA,CAAvB,EAAA,MAAO,MAAM,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAElE,EAAA,WAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAsE,IAAtE,IAAA,EAAsE,EAAA,gBAAA,CAAf,EAAA,QAAQ,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,EAEtDA,EAAAA,OAAM,qBAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAA+B,EAAA,OAAA,iBAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAInC,EAAA,EAAA,YAAA,CAgDiB,GAAA,CA/Cd,cAAa,EAAA,MACb,QAAS,EAAA,MACT,WAAY,EAAA,WACZ,SAAU,EAAA,SACV,YAAW,EAAA,MAAY,EAAA,SAAW,IAAA,GAClC,gBAAe,EAAA,cAAgB,EAAA,KAAK,OAAO,aAC3C,eAAc,EAAA,YACd,UAAS,EAAA,OACT,sBAAoB,EACpB,MAAG,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,iBAAgB,uBAGR,QAAA,OAAA,EAAY,EAAA,QAAA,EAoBvB,CApB+B,MAAK,WAAK,EAAA,EAC/C,EAAA,mBAAA,CAmBM,MAnBN,GAmBM,CAjBI,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQS,SAAA,OANP,KAAK,SACL,MAAM,yBACN,aAAW,SACV,QAAK,GAAE,EAAA,KAAK,eAAe,EAAK,CAAK,CAEtC,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAA0B,IAAA,CAAvB,MAAM,cAAc,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAGjB,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQS,SAAA,OANP,KAAK,SACL,MAAM,wDACN,aAAW,UACV,QAAK,GAAE,EAAA,KAAK,cAAc,EAAK,CAAK,CAErC,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,SAMH,EAAA,EAAA,WAAA,CAAA,EAAA,MAAR,KAAoC,KAAA,EACnD,IAAA,EAAA,EAAA,QAAA,CAD2D,GAAQ,EAAA,EACnE,EAAA,WAAA,CAAuC,EAAA,OAA1B,GAAA,EAAI,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAU,CAAQ,CAAA,CAAA,CAAA,CAAA,KAGrBA,EAAAA,OAAO,UAAA,CAAY,KAAA,YACjC,IAAA,EAAA,EAAA,QAAA,CAD4C,GAAQ,EAAA,EACpD,EAAA,WAAA,CAA2C,EAAA,OAAA,aAAA,EAAA,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAZ,CAAQ,CAAA,CAAA,CAAA,CAAA,kBAGzBA,EAAAA,OAAO,MAAA,CAAQ,KAAA,QAC7B,IAAA,EAAA,EAAA,QAAA,KAAqB,EAAA,EAArB,EAAA,WAAA,CAAqB,EAAA,OAAA,OAAA,CAAA,CAAA,kIAIzB,EAAA,EAAA,YAAA,CAyBkB,GAAA,CAxBf,QAAS,EAAA,KAAK,cAAc,MAC5B,MAAO,EAAA,KAAK,YAAY,MACxB,OAAQ,EAAA,KAAK,OAAO,KACpB,YAAW,EAAA,KAAK,SAChB,aAAY,EAAA,KAAK,UAAU,MAC3B,OAAQ,EAAA,KAAK,OAAO,MACpB,MAAO,EAAA,YACP,eAAc,EAAA,aAAe,EAAA,KAAK,OAAO,YACzC,eAAc,EAAA,KAAK,OAAO,cAAW,GACrC,mBAAc,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,cAAc,MAAQ,EAC3C,iBAAc,EAAA,KAAK,aACnB,OAAI,AAAA,EAAA,KAAA,GAAE,EAAA,KAAK,KAAI,6BAES,EAAA,EAAA,WAAA,CAAA,EAAA,MAAR,KAAmC,KAAA,EAClD,IAAA,EAAA,EAAA,QAAA,CAD0D,GAAQ,EAAA,EAClE,EAAA,WAAA,CAAuC,EAAA,OAA1B,GAAA,EAAI,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAU,CAAQ,CAAA,CAAA,CAAA,CAAA,KAGrBA,EAAAA,OAAM,gBAAA,CAAmB,KAAA,eACvC,IAAA,EAAA,EAAA,QAAA,CADqD,GAAQ,EAAA,EAC7D,EAAA,WAAA,CAA8C,EAAA,OAAA,gBAAA,EAAA,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAZ,CAAQ,CAAA,CAAA,CAAA,CAAA,kBAG5BA,EAAAA,OAAM,eAAA,CAAkB,KAAA,SACtC,IAAA,EAAA,EAAA,QAAA,CAD8C,GAAQ,EAAA,EACtD,EAAA,WAAA,CAA6C,EAAA,OAAA,eAAA,EAAA,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAZ,CAAQ,CAAA,CAAA,CAAA,CAAA,iaE5JjD,IAAM,EAAO,EAEb,SAAS,EAAS,EAAY,CAC5B,EAAK,oBAAsB,GAAuB,IAAI,CACxD,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAWE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAVC,cAAa,EAAA,YAAc,IAAA,GAC5B,iBAAe,QACd,YAAa,EAAA,YACb,WAAU,EAAA,QACV,WAAU,EAAA,QACV,kBAAiB,EAAA,cACjB,SAAU,EAAA,SACX,cAAY,WACZ,MAAA,GACC,sBAAoB,suBEjCzB,IAAM,EAAQ,EAoBR,EAAO,EAKP,GAAA,EAAQ,EAAA,IAAA,CAA6B,IAAI,EACzC,GAAA,EAAQ,EAAA,SAAA,KAAuB,CACnC,IAAM,EAAI,EAAM,WAEhB,OADK,EACE,MAAM,QAAQ,CAAC,EAAI,EAAI,CAAC,CAAC,EADjB,CAAC,CAElB,CAAC,EAED,SAAS,GAAO,CACd,EAAM,OAAO,MAAM,CACrB,CAEA,SAAS,EAAS,EAAU,CAC1B,IAAM,EAAK,EAAE,OACP,EAAS,MAAM,KAAK,EAAG,OAAS,CAAC,CAAC,EACxC,EAAG,MAAQ,GACX,IAAM,EAAgB,CAAC,EACvB,IAAK,IAAM,KAAK,EAAQ,CACtB,GAAI,EAAE,KAAO,EAAM,QAAS,CAC1B,EAAK,QAAS,IAAI,EAAE,KAAK,wBAAwB,EAAM,QAAU,QAAA,CAAW,QAAQ,CAAC,EAAE,IAAI,EAC3F,QACF,CACA,EAAM,KAAK,CAAC,CACd,CACK,EAAM,QACX,EAAK,oBAAqB,EAAM,SAAW,CAAC,GAAG,EAAM,MAAO,GAAG,CAAK,EAAI,EAAM,EAAE,CAClF,CAEA,SAAS,EAAS,EAAW,CAC3B,GAAI,CAAC,EAAM,SAAU,CACnB,EAAK,oBAAqB,IAAI,EAC9B,MACF,CACA,IAAM,EAAO,EAAM,MAAM,MAAM,EAC/B,EAAK,OAAO,EAAG,CAAC,EAChB,EAAK,oBAAqB,EAAK,OAAS,EAAO,IAAI,CACrD,CAEA,SAAS,EAAQ,EAAuB,CAGtC,OAFI,EAAQ,KAAa,GAAG,EAAM,IAC9B,EAAQ,QAAkB,IAAI,EAAQ,KAAA,CAAM,QAAQ,CAAC,EAAE,KACpD,IAAI,EAAQ,QAAA,CAAW,QAAQ,CAAC,EAAE,IAC3C,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAwDM,MAxDN,GAwDM,EAvDJ,EAAA,EAAA,mBAAA,CAUM,MAAA,KAAA,EAAA,EATJ,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPA,KAAK,SACJ,MAAO,EAAA,YACR,KAAK,eACL,SAAA,GACA,KAAK,QACJ,SAAU,EAAA,SACV,QAAO,mCAGZ,EAAA,EAAA,mBAAA,CAOE,QAAA,CANI,QAAA,QAAJ,IAAI,EACJ,KAAK,OACJ,OAAQ,EAAA,OACR,SAAU,EAAA,SACX,MAAA,CAAA,QAAA,MAAA,EACS,wBAGH,EAAA,MAAM,SAAA,EADd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAgCK,KAhCL,GAgCK,GAAA,EArBH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAoBK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAnBc,EAAA,OAAT,EAAG,MADb,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAoBK,KAAA,CAlBF,IAAG,GAAK,EAAE,KAAI,GAAI,IACnB,MAAA,CAAA,QAAA,OAAA,cAAA,SAAA,IAAA,SAAA,YAAA,SAAA,IAEA,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA6C,IAAA,CAA1C,MAAM,aAAa,MAAA,CAAA,QAAA,KAAA,aACtB,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAE,IAAI,EAAA,CAAA,GAEX,EAAA,EAAA,mBAAA,CAAuD,OAAvD,IAAA,EAAuD,EAAA,gBAAA,CAAzB,EAAQ,EAAE,IAAI,CAAA,EAAA,CAAA,GAC5C,EAAA,EAAA,YAAA,EAAA,EASE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CARA,KAAK,SACL,KAAK,cACL,KAAA,GACA,QAAA,GACA,KAAK,QACL,SAAS,SACR,SAAU,EAAA,SACV,QAAK,GAAE,EAAS,CAAC,oCAIxB,EAAA,GAAA,EAAA,CAAA,IAAA,EAAA,EAAA,WAAA,CAEO,EAAA,OAAA,QAAA,CAAA,MAAA,CADL,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA+E,OAAA,CAAzE,MAAA,CAAA,YAAA,SAAA,QAAA,KAAA,CAAuC,EAAC,6BAA0B,EAAA,CAAA,EAAA,IAAA,GAAA,CAAA,QEnF9E,SAAgB,GAAc,EAAwB,CACpD,OAAO,OAAO,GAAS,EAAE,CAAC,CACvB,UAAU,KAAK,CAAC,CAChB,QAAQ,kBAAmB,EAAE,CAAC,CAC9B,kBAAkB,CACvB,CASA,SAAgB,GACd,EACA,CAAE,cAAc,KAAM,cAAc,OAAQ,UAAS,cAAiC,CAAC,EACrE,CAClB,IAAM,EAA0B,CAAC,EAC3B,EAAS,IAAI,IAEnB,IAAK,IAAM,KAAU,GAAW,CAAC,EAAG,CAClC,IAAM,EAAK,EAAO,GAClB,GAAI,GAA2B,KAAM,SAErC,IAAM,EAAuB,CAC3B,IAAK,KAAsB,IAC3B,MAAO,OAAO,EAAO,IAAgB,CAAE,EACvC,MAAO,EACP,KAAM,EACN,KAAM,EACR,EAEM,EAAW,EAAU,EAAO,GAAW,IAAA,GAC7C,GAAI,GAAuC,MAAQ,IAAa,GAAI,CAClE,EAAM,KAAK,CAAI,EACf,QACF,CAEA,IAAM,EAAQ,OAAO,CAAQ,EACzB,EAAQ,EAAO,IAAI,CAAK,EACvB,IACH,EAAQ,CACN,IAAK,KAAuB,IAC5B,MAAO,EAAa,EAAW,CAAK,EAAI,EACxC,SAAU,CAAC,EACX,KAAM,EACR,EACA,EAAO,IAAI,EAAO,CAAK,EACvB,EAAM,KAAK,CAAK,GAElB,EAAM,SAAU,KAAK,CAAI,CAC3B,CAEA,OAAO,CACT,CAGA,SAAgB,GAAkB,EAAsC,CAItE,OAHK,EAAK,UAAU,OAGb,EAAK,SAAS,QAAQ,EAAiB,EAFrC,EAAK,QAAU,IAAA,GAAY,CAAC,EAAI,CAAC,EAAK,KAAK,CAGtD,CAGA,SAAgB,GAAc,EAAkD,CAC9E,OAAO,EAAM,QAAQ,EAAiB,CACxC,CAGA,SAAgB,GAAW,EAA+D,CACxF,IAAM,EAAQ,IAAI,IACZ,EAAS,GAAyB,CACtC,EAAM,IAAI,EAAK,IAAK,CAAI,EACxB,EAAK,UAAU,QAAQ,CAAK,CAC9B,EAEA,OADA,EAAM,QAAQ,CAAK,EACZ,CACT,CAUA,SAAgB,GACd,EACA,EACmB,CACnB,IAAM,EAAS,IAAI,IAAkB,GAAY,CAAC,CAAC,EAC7C,EAA0B,CAAC,EAE3B,EAAS,GAA6C,CAC1D,GAAI,CAAC,EAAK,UAAU,OAAQ,CAC1B,IAAM,EAAU,EAAK,QAAU,IAAA,IAAa,EAAO,IAAI,EAAK,KAAK,EAEjE,OADI,IAAS,EAAK,EAAK,KAAO,CAAE,QAAS,GAAM,eAAgB,EAAM,GAC9D,CAAE,UAAS,eAAgB,EAAM,CAC1C,CAEA,IAAI,EAAQ,GACR,EAAS,GACb,IAAK,IAAM,KAAS,EAAK,SAAU,CACjC,IAAM,EAAS,EAAM,CAAK,EACtB,EAAO,QAAS,EAAS,GACxB,EAAQ,GACT,EAAO,iBACT,EAAS,GACT,EAAQ,GAEZ,CAIA,OAFI,EAAO,EAAK,EAAK,KAAO,CAAE,QAAS,GAAM,eAAgB,EAAM,EAC1D,IAAQ,EAAK,EAAK,KAAO,CAAE,QAAS,GAAO,eAAgB,EAAK,GAClE,CAAE,QAAS,EAAO,eAAgB,CAAC,GAAS,CAAO,CAC5D,EAGA,OADA,EAAM,QAAQ,CAAK,EACZ,CACT,CASA,SAAgB,GACd,EACA,EACA,EAAiC,CAAC,EAChB,CAClB,IAAM,EAAI,GAAc,CAAI,CAAC,CAAC,KAAK,EACnC,GAAI,CAAC,EAAG,MAAO,CAAC,GAAG,CAAK,EAExB,IAAM,EAAW,GACX,GAAc,EAAK,KAAK,CAAC,CAAC,SAAS,CAAC,EAAU,GAC3C,EAAY,KAAM,GAAU,GAAc,EAAK,OAAO,EAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAG5E,EAAwB,CAAC,EAC/B,IAAK,IAAM,KAAQ,EAAO,CACxB,GAAI,CAAC,EAAK,UAAU,OAAQ,CACtB,EAAQ,CAAI,GAAG,EAAI,KAAK,CAAI,EAChC,QACF,CACA,GAAI,EAAQ,CAAI,EAAG,CACjB,EAAI,KAAK,CAAI,EACb,QACF,CACA,IAAM,EAAW,GAAW,EAAK,SAAU,EAAM,CAAW,EACxD,EAAS,QAAQ,EAAI,KAAK,CAAE,GAAG,EAAM,UAAS,CAAC,CACrD,CACA,OAAO,CACT,CAGA,SAAgB,GACd,EACA,EACA,EACgB,CAChB,IAAM,EAAO,IAAI,IAAkB,CAAG,EACtC,GAAI,CAAC,EAAO,OAAQ,GAAW,CAAC,EAAA,CAAG,OAAQ,GAAO,CAAC,EAAK,IAAI,CAAE,CAAC,EAE/D,IAAM,EAAM,CAAC,GAAI,GAAW,CAAC,CAAE,EACzB,EAAY,IAAI,IAAkB,CAAG,EAC3C,IAAK,IAAM,KAAM,EACV,EAAU,IAAI,CAAE,IACnB,EAAU,IAAI,CAAE,EAChB,EAAI,KAAK,CAAE,GAGf,OAAO,CACT,izBClKA,IAAM,EAAQ,EAmDR,EAAO,EAEP,GAAA,EAAO,EAAA,IAAA,CAAI,EAAE,EACb,GAAA,EAAe,EAAA,IAAA,CAA6B,CAAC,CAAC,EAG9C,GAAA,EAAW,EAAA,SAAA,KACf,GAAU,EAAM,QAAS,CACvB,YAAa,EAAM,YACnB,YAAa,EAAM,YACnB,QAAS,EAAM,QACf,WAAY,EAAM,UACpB,CAAC,CACH,EAGM,GAAA,EAAY,EAAA,SAAA,KAAe,GAAW,EAAS,KAAK,CAAC,EAErD,GAAA,EAAc,EAAA,SAAA,KAAe,EAAM,QAAU,EAAK,MAAM,KAAK,CAAC,CAAC,OAAS,CAAC,EAGzE,GAAA,EAAc,EAAA,SAAA,KAClB,EAAY,MAAQ,GAAW,EAAS,MAAO,EAAK,MAAO,EAAM,YAAY,EAAI,EAAS,KAC5F,EAGM,GAAA,EAAgB,EAAA,SAAA,KAAe,GAAoB,EAAS,MAAO,EAAM,UAAU,CAAC,EAEpF,GAAA,EAAc,EAAA,SAAA,KAAe,GAAc,EAAS,KAAK,CAAC,CAAC,MAAM,EACjE,GAAA,EAAgB,EAAA,SAAA,KAAe,CACnC,IAAM,EAAU,IAAI,IAAI,GAAc,EAAS,KAAK,CAAC,EACrD,OAAO,EAAM,WAAW,OAAQ,GAAO,EAAQ,IAAI,CAAE,CAAC,CAAC,CAAC,MAC1D,CAAC,EAEK,GAAA,EAAa,EAAA,SAAA,MACV,EAAM,WAAa,EAAM,iBAAmB,EAAY,MAAQ,GAAK,EAAM,QACpF,EACM,GAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAM,SAAW,EAAY,MAAM,SAAW,CAAC,EACzE,GAAA,EAAY,EAAA,SAAA,KAChB,EAAY,MAAQ,EAAM,mBAAqB,EAAM,YACvD,EAEA,SAAS,EAAW,EAAkC,CACpD,IAAM,EAAO,CAAE,GAAG,EAAa,KAAM,EACrC,IAAK,IAAM,KAAQ,EAAW,EAAK,UAAU,SAAQ,EAAK,EAAK,KAAO,IACtE,EAAa,MAAQ,CACvB,CAMA,IAAM,EAAY,IAAI,KACtB,EAAA,EAAA,MAAA,CACE,EACC,GAAU,CACT,GAAI,CAAC,EAAM,gBAAiB,OAC5B,IAAM,EAAQ,EAAM,OAAQ,GAAM,EAAE,UAAU,QAAU,CAAC,EAAU,IAAI,EAAE,GAAG,CAAC,EACxE,EAAM,SACX,EAAM,QAAS,GAAM,EAAU,IAAI,EAAE,GAAG,CAAC,EACzC,EAAW,CAAK,EAClB,EACA,CAAE,UAAW,EAAK,CACpB,GAIA,EAAA,EAAA,MAAA,CAAM,EAAO,GAAU,CACjB,EAAM,KAAK,GAAG,EAAW,EAAY,KAAK,CAChD,CAAC,EAOD,SAAS,EAAa,EAAsB,EAAgB,CAC1D,GAAI,EAAM,SAAU,OACpB,IAAM,EAAW,EAAU,MAAM,IAAI,EAAK,GAAG,EACxC,GACL,EAAK,oBAAqB,GAAU,EAAM,WAAY,GAAkB,CAAQ,EAAG,CAAK,CAAC,CAC3F,CAEA,SAAS,GAAY,CACf,EAAM,UACV,EAAK,oBAAqB,GAAc,EAAS,KAAK,CAAC,CACzD,CAEA,SAAS,GAAW,CACd,EAAM,UACV,EAAK,oBAAqB,CAAC,CAAC,CAC9B,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiEM,MAAA,CAjED,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,gBAAe,CAAA,0BAAsC,EAAA,QAAQ,CAAA,CAAA,CAAA,EAAA,EACtE,EAAA,EAAA,WAAA,CA2BO,EAAA,OAAA,SAAA,CA3Bc,cAAgB,EAAA,MAAgB,MAAO,EAAA,KAA5D,MA2BO,CA1BM,EAAA,QAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAyBM,MAzBN,GAyBM,CAxBQ,EAAA,YAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,KAAa,EAAG,QAAA,EAAI,EAAA,gBAAA,CAAG,EAAA,KAAW,EAAG,iBAC1C,CAAA,KAAA,EACA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAe,OAAA,EAAA,GACJ,EAAA,gBAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAmBM,MAnBN,GAmBM,EAAA,EAlBJ,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPA,KAAK,SACL,MAAM,eACN,KAAA,GACA,gBAAA,GACA,SAAS,KACR,SAAU,EAAA,UAAY,EAAA,QAAkB,EAAA,OAAW,CAAK,EAAA,MACxD,QAAO,CAEV,EAAA,KAAA,EAAA,CAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAQE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAPA,KAAK,SACL,MAAM,SACN,KAAA,GACA,gBAAA,GACA,SAAS,KACR,SAAU,EAAA,UAAQ,CAAK,EAAA,MACvB,QAAO,gGAMC,EAAA,SAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAUY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAVa,MAAM,0BAC7B,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CANS,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAI,MAAA,EACZ,YAAa,EAAA,kBACb,SAAU,EAAA,SACV,aAAY,EAAA,kBACb,gBAAA,GACA,MAAA,0GAKK,EAAA,QAmBT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAkE,EAAA,OAAA,QAAA,CAA9C,UAAW,EAAA,KAAW,MAAwB,EAAA,EAAnB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,KAAS,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KApBjD,EADT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAkBO,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAhBJ,MAAO,EAAA,MACR,iBAAe,WACd,iBAAgB,EAAA,MAChB,gBAAe,EAAA,MACf,QAAS,EAAA,QACT,aAAY,EAAA,UACb,MAAM,sBACL,wBAAoB,AAAA,EAAA,KAAA,GAAE,EAAA,MAAe,EACrC,aAAW,AAAA,EAAA,KAAA,GAAE,EAAa,EAAM,EAAA,EAChC,eAAa,AAAA,EAAA,KAAA,GAAE,EAAa,EAAM,EAAA,IAExB,SAAA,EAAO,EAAA,QAAA,CAAE,GAAS,EAAA,EAC3B,EAAA,WAAA,CAEO,EAAA,OAAA,OAAA,CAFY,KAAM,EAAU,IAAnC,MAEO,EAAA,EADD,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAU,KAAwB,KAAK,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,yEAKjD,CAAA,WExQA,GAAqD,KAGzD,SAAgB,IAA2C,CA8BzD,MA7BA,CACE,KAAiB,QAAQ,IAAI,CAC3B,OAAO,gBACP,OAAO,kBACP,OAAO,sBACP,OAAO,oBACT,CAAC,CAAC,CACC,MAAM,CAAC,EAAM,EAAQ,EAAY,MAChC,EAAK,IAAI,CACP,EAAO,SACP,EAAO,UACP,EAAO,SACP,EAAW,iBACX,EAAW,cACX,EAAW,gBACX,EAAW,eACX,EAAW,iBACX,EAAU,eACV,EAAU,WACZ,CAAC,EACM,EACR,CAAC,CACD,WAGC,GAAiB,KACV,KACR,EAEE,EACT,CAEA,IAAM,IAAS,EAAc,EAAW,KAClC,OAAO,OAAW,IAAoB,EAC5B,iBAAiB,SAAS,eAAe,CAAC,CAAC,iBAAiB,CAAI,CAAC,CAAC,KACzE,GAAS,EAelB,SAAgB,IAA8B,CAC5C,IAAM,EAAK,GAAM,MAAM,EACjB,EAAU,GAAM,YAAY,EAC5B,EAAS,GAAM,UAAU,EACzB,EAAU,GAAM,WAAW,EAC3B,EAAa,GAAM,cAAe,YAAY,EAE9C,EAAO,CACX,SAAU,CAAE,UAAW,CAAE,MAAO,CAAO,CAAE,EACzC,SAAU,CAAE,KAAM,EAAM,EACxB,UAAW,CAAE,MAAO,CAAQ,EAC5B,UAAW,CAAE,UAAW,CAAE,MAAO,CAAO,CAAE,EAC1C,cAAe,CAAE,MAAO,CAAQ,CAClC,EAEA,MAAO,CACL,MAAO,CAAC,EAAG,EAAG,EAAG,EAAG,EAAG,CAAC,CAAC,CAAC,IAAK,GAAM,GAAM,SAAS,GAAG,CAAC,EACxD,gBAAiB,cACjB,UAAW,CAAE,MAAO,EAAI,YAAW,EACnC,aAAc,CAAE,GAAG,EAAM,UAAW,CAAE,KAAM,EAAM,CAAE,EACpD,UAAW,EACX,SAAU,CAAE,GAAG,EAAM,UAAW,CAAE,KAAM,EAAM,CAAE,EAChD,OAAQ,CACN,UAAW,CAAE,MAAO,CAAQ,EAC5B,KAAM,SACN,UAAW,EACX,WAAY,CACd,EACA,MAAO,CACL,UAAW,CAAE,MAAO,CAAG,EACvB,aAAc,CAAE,MAAO,CAAQ,CACjC,EACA,QAAS,CACP,gBAAiB,EACjB,YAAa,EACb,UAAW,CAAE,MAAO,CAAG,EAGvB,aAAc,yEAChB,EACA,KAAM,CAAE,WAAY,EAAG,OAAQ,EAAM,EACrC,IAAK,CACH,MAAO,CAAE,MAAO,CAAQ,EACxB,UAAW,CAAE,YAAa,EAAS,YAAa,CAAE,CACpD,EACA,IAAK,CAAE,UAAW,CAAE,aAAc,CAAC,EAAG,EAAG,EAAG,CAAC,CAAE,CAAE,CACnD,CACF,CAOA,SAAgB,IAA8B,CAC5C,IAAM,EAAS,CAAC,UAAW,UAAW,UAAW,UAAW,UAAW,SAAS,EAC1E,EAAO,CACX,SAAU,CAAE,UAAW,CAAE,MAAO,SAAU,CAAE,EAC5C,SAAU,CAAE,KAAM,EAAM,EACxB,UAAW,CAAE,MAAO,SAAU,EAC9B,UAAW,CAAE,UAAW,CAAE,MAAO,SAAU,CAAE,EAC7C,cAAe,CAAE,MAAO,SAAU,CACpC,EAEA,MAAO,CACL,UAAW,GACX,MAAO,EACP,gBAAiB,cACjB,UAAW,CAAE,MAAO,SAAU,EAC9B,aAAc,CAAE,GAAG,EAAM,UAAW,CAAE,KAAM,EAAM,CAAE,EACpD,UAAW,EACX,SAAU,CAAE,GAAG,EAAM,UAAW,CAAE,KAAM,EAAM,CAAE,EAChD,OAAQ,CAAE,UAAW,CAAE,MAAO,SAAU,EAAG,KAAM,SAAU,UAAW,EAAG,WAAY,CAAE,EACvF,MAAO,CAAE,UAAW,CAAE,MAAO,SAAU,EAAG,aAAc,CAAE,MAAO,SAAU,CAAE,EAC7E,KAAM,CAAE,WAAY,EAAG,OAAQ,EAAM,EACrC,IAAK,CAAE,MAAO,CAAE,MAAO,SAAU,EAAG,UAAW,CAAE,YAAa,UAAW,YAAa,CAAE,CAAE,EAC1F,IAAK,CAAE,UAAW,CAAE,aAAc,CAAE,CAAE,CACxC,CACF,CAGA,SAAgB,IAA+C,CAC7D,MAAO,CACL,KAAM,GACN,MAAO,GAAM,WAAW,EACxB,UAAW,cACX,cAAe,EACf,UAAW,CACb,CACF,CAiBA,SAAgB,GAAY,CAC1B,OACA,SAAU,GAAM,OAAO,CAAC,EACxB,cAAc,GACd,YAAY,IACqB,CACjC,MAAO,CACL,QAAS,CAAE,QAAS,OAAQ,eAAgB,CAAO,EACnD,OAAQ,CAAE,OAAQ,EAAG,KAAM,QAAS,EACpC,MAAO,EACH,CACE,KAAM,EACN,QAAS,EACT,KAAM,SACN,IAAK,MACL,QAAS,EACT,UAAW,CAAE,SAAU,GAAI,WAAY,GAAI,EAC3C,aAAc,CAAE,SAAU,EAAG,CAC/B,EACA,IAAA,GACJ,OAAQ,CACN,CACE,KAAM,MACN,OAAQ,CAAC,MAAO,KAAK,EACrB,OAAQ,CAAC,MAAO,KAAK,EACrB,MAAO,CAAE,KAAM,EAAM,EACrB,UAAW,CAAE,KAAM,EAAM,EACzB,MACF,CACF,CACF,CACF,kVCpLA,IAAM,EAAQ,EA0BR,GAAA,EAAY,EAAA,IAAA,CAAwB,IAAI,EACxC,GAAA,EAAQ,EAAA,WAAA,CAA+B,IAAI,EAE3C,GAAA,EAAc,EAAA,IAAA,CAAI,EAAK,EAEzB,EAAwC,KACxC,EAAyC,KAEvC,MAAoB,CACnB,EAAM,OACX,EAAM,MAAM,UAAU,EAAM,OAAiB,EAAI,CACnD,EAEM,MAAqB,CACpB,EAAM,QACP,EAAM,QAAS,EAAM,MAAM,YAAY,UAAW,GAAoB,CAAC,EACtE,EAAM,MAAM,YAAY,EAC/B,EAGM,EAAQ,SAAY,CACxB,IAAM,EAAO,MAAM,GAAY,EAC1B,KAAU,MACf,IAAI,CAAC,EAAM,CACT,EAAY,MAAQ,GACpB,MACF,CACA,EAAY,MAAQ,GACpB,EAAM,OAAO,QAAQ,EACrB,EAAM,MAAQ,EAAK,KACjB,EAAU,MACV,EAAM,MAAQ,GAAgB,EAAI,GAAgB,EAClD,CAAE,SAAU,EAAM,MAAQ,MAAQ,EAAM,QAAS,CACnD,EACA,EAAY,EACZ,EAAa,CATb,CAUF,SAEA,EAAA,EAAA,MAAA,KAAY,EAAM,OAAQ,EAAa,CAAE,KAAM,EAAK,CAAC,GACrD,EAAA,EAAA,MAAA,KAAY,EAAM,QAAS,CAAY,GACvC,EAAA,EAAA,MAAA,CAAM,KAAO,EAAM,UAAa,EAAM,QAAQ,MAAS,KAAK,EAAM,CAAC,GAEnE,EAAA,EAAA,UAAA,KAAgB,CACd,EAAW,EAEX,EAAiB,IAAI,mBAAqB,EAAM,OAAO,OAAO,CAAC,EAC3D,EAAU,OAAO,EAAe,QAAQ,EAAU,KAAK,EAI3D,EAAgB,IAAI,qBAAuB,KAAK,EAAM,CAAC,EACvD,EAAc,QAAQ,SAAS,gBAAiB,CAAE,WAAY,EAAK,CAAC,CACtE,CAAC,GAED,EAAA,EAAA,gBAAA,KAAsB,CACpB,GAAgB,WAAW,EAC3B,GAAe,WAAW,EAC1B,EAAM,OAAO,QAAQ,EACrB,EAAM,MAAQ,IAChB,CAAC,WAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MAAA,CARD,MAAM,UAAW,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,OAAM,CAAA,cAAoB,EAAA,MAAM,EAAK,IAAA,EAAS,CAC9D,EAAA,CAAA,EAAA,OAAK,CAAK,EAAA,UAAA,EAArB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAA4C,EAAA,OAAA,QAAA,CAAA,MAAA,EAAA,EAAtB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,YAAY,EAAA,CAAA,CAAA,EAAA,EAAA,CAAA,CAAA,GAEpB,EAAA,QAAA,EAAhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAqD,EAAA,OAAA,cAAA,CAAA,MAAA,CAA5B,AAAA,EAAA,MAAA,EAAA,EAAA,gBAAA,CAAA,wBAAqB,EAAA,CAAA,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAEhD,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAA+E,MAAA,CAAtC,QAAA,YAAJ,IAAI,EAAY,MAAM,iBAA7C,EAAA,KAAA,GAAA,EAAA,CAAA,CAAA,EAAA,MAAA,CAAA,EAAA,OAAK,CAAK,EAAA,KAAW,CAAA,CAAA,CAAA,EAAA,CAAA,wHE5GvC,IAAM,EAAQ,EAQR,EAA4C,CAChD,MAAO,CAAE,MAAO,QAAS,SAAU,SAAU,EAC7C,MAAO,CAAE,MAAO,QAAS,SAAU,SAAU,EAC7C,QAAS,CAAE,MAAO,UAAW,SAAU,QAAS,EAChD,QAAS,CAAE,MAAO,UAAW,SAAU,QAAS,EAChD,SAAU,CAAE,MAAO,WAAY,SAAU,MAAO,EAChD,SAAU,CAAE,MAAO,WAAY,SAAU,MAAO,EAChD,UAAW,CAAE,MAAO,YAAa,SAAU,QAAS,EACpD,UAAW,CAAE,MAAO,YAAa,SAAU,QAAS,EACpD,SAAU,CAAE,MAAO,WAAY,SAAU,MAAO,EAChD,WAAY,CAAE,MAAO,aAAc,SAAU,SAAU,EACvD,WAAY,CAAE,MAAO,aAAc,SAAU,SAAU,EACvD,QAAS,CAAE,MAAO,UAAW,SAAU,QAAS,EAChD,QAAS,CAAE,MAAO,UAAW,SAAU,QAAS,EAChD,UAAW,CAAE,MAAO,YAAa,SAAU,SAAU,EACrD,UAAW,CAAE,MAAO,YAAa,SAAU,SAAU,EACrD,aAAc,CAAE,MAAO,eAAgB,SAAU,MAAO,EACxD,aAAc,CAAE,MAAO,eAAgB,SAAU,MAAO,EACxD,SAAU,CAAE,MAAO,WAAY,SAAU,WAAY,EACrD,SAAU,CAAE,MAAO,WAAY,SAAU,WAAY,EACrD,OAAQ,CAAE,MAAO,SAAU,SAAU,MAAO,EAC5C,OAAQ,CAAE,MAAO,SAAU,SAAU,MAAO,EAC5C,QAAS,CAAE,MAAO,UAAW,SAAU,WAAY,EACnD,QAAS,CAAE,MAAO,UAAW,SAAU,WAAY,EACnD,KAAM,CAAE,MAAO,OAAQ,SAAU,SAAU,EAC3C,KAAM,CAAE,MAAO,OAAQ,SAAU,SAAU,EAC3C,aAAc,CAAE,MAAO,eAAgB,SAAU,QAAS,CAC5D,EAEM,GAAA,EAAa,EAAA,SAAA,MACD,EAAM,KAAO,EAAA,CACd,EAAM,QAAU,CAAE,MAAO,EAAM,MAAO,SAAU,WAAY,CAC5E,iBAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAAwE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAlE,MAAO,EAAA,MAAW,MAAQ,SAAU,EAAA,MAAW,mWE3CvD,IAAM,EAAO,iBAMX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CASM,MATN,GASM,EAAA,EARJ,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAgD,KAAhD,IAAA,EAAgD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EAC/B,EAAA,WAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAoE,IAApE,IAAA,EAAoE,EAAA,gBAAA,CAAf,EAAA,QAAQ,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAAA,EAE/D,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,EACT,EAAA,cAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAA6F,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OAAjE,MAAO,EAAA,YAAc,KAAM,EAAA,WAAa,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,QAAA,qWEZrF,IAAM,EAAO,iBAMX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAcM,MAdN,GAcM,EAbJ,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,mBAAA,CAAmB,IAAA,CAAf,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,IAAI,CAAA,EAAA,KAAA,CAAA,CAAA,CAAA,GAEjB,EAAA,EAAA,mBAAA,CAA8C,IAA9C,IAAA,EAA8C,EAAA,gBAAA,CAAZ,EAAA,KAAK,EAAA,CAAA,EAC9B,EAAA,cAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA6E,IAA7E,IAAA,EAA6E,EAAA,gBAAA,CAAlB,EAAA,WAAW,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAE9D,EAAA,cAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAOE,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,OALC,MAAO,EAAA,YACP,KAAM,EAAA,WACP,KAAK,QACL,MAAM,OACL,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,QAAA,waExBlB,IAAM,EAAQ,EAUR,GAAA,EAAS,EAAA,UAAA,CAAU,EAEzB,SAAS,GAAS,CACZ,EAAM,OACR,EAAO,KAAK,OAAO,EAAM,QAAW,SAAW,CAAE,KAAM,EAAM,MAAO,EAAI,EAAM,MAAM,EAC3E,EAAM,UACf,EAAO,KAAK,CAAE,KAAM,EAAM,SAAU,CAAC,EAErC,EAAO,KAAK,CAEhB,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAaM,MAbN,GAaM,EAAA,EAZJ,EAAA,mBAAA,CAQM,MARN,GAQM,EAPJ,EAAA,EAAA,YAAA,EAAA,EAA+D,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvD,KAAK,mBAAmB,KAAA,GAAK,QAAA,GAAS,QAAO,IAC5C,EAAA,OAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,IAAA,OAA5C,OAAA,EAAK,EAAA,eAAA,CAAA,CAAE,EAAA,KAAY,sBAAsB,CAAA,6CACzD,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAkD,KAAlD,IAAA,EAAkD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EACjC,EAAA,WAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAsE,IAAtE,IAAA,EAAsE,EAAA,gBAAA,CAAf,EAAA,QAAQ,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,EAE/C,EAAA,SAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAA6D,GAAA,OAAlC,MAAO,EAAA,OAAS,IAAK,EAAA,mEAElD,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,CAAA,CAAA,CAAA,CAAA,0ZEf7B,GAAM,CAAE,iBAAgB,aAAY,gBAAiB,EAAA,EAAc,EAEnE,SAAS,EAAY,EAA0B,CAC7C,IAAM,EAAI,EAAM,MAMhB,OALI,GAAK,MAAQ,IAAM,GAAW,IAC9B,EAAM,SAAW,WAAmB,EAAe,OAAO,CAAC,CAAC,EAC5D,EAAM,SAAW,OAAe,EAAW,OAAO,CAAC,CAAC,EACpD,EAAM,SAAW,WAAmB,EAAW,OAAO,CAAC,EAAG,kBAAkB,EAC5E,EAAM,SAAW,SAAiB,EAAa,OAAO,CAAC,EAAG,EAAM,UAAY,CAAC,EAC1E,OAAO,CAAC,CACjB,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAjBD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,cAAa,gBAAyB,EAAA,SAAO,CAAA,CAAA,EAAA,CAC5C,EAAA,QAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA2D,KAA3D,IAAA,EAA2D,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAExC,EAAA,UAAO,WAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMM,MANN,GAMM,GAAA,EALJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAJe,EAAA,OAAT,KAAZ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAAA,CAJwB,IAAK,EAAM,MAAO,MAAM,wBACpD,EAAA,EAAA,mBAAA,CAA+D,OAA/D,IAAA,EAA+D,EAAA,gBAAA,CAArB,EAAM,KAAK,EAAA,CAAA,GACrD,EAAA,EAAA,mBAAA,CAAsE,OAAtE,IAAA,EAAsE,EAAA,gBAAA,CAA5B,EAAY,CAAK,CAAA,EAAA,CAAA,EAC/C,EAAM,SAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAqF,OAArF,IAAA,EAAqF,EAAA,gBAAA,CAAtB,EAAM,MAAM,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAI/E,EAAA,GAAA,EAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKM,MALN,GAKM,GAAA,EAJJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAHe,EAAA,OAAT,KAAZ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,MAAA,CAHwB,IAAK,EAAM,MAAO,MAAM,mBACpD,EAAA,EAAA,EAAA,EAAA,mBAAA,CAAwD,OAAxD,IAAA,EAAwD,EAAA,gBAAA,CAArB,EAAM,KAAK,EAAA,CAAA,GAAA,EAC9C,EAAA,mBAAA,CAA+D,OAA/D,IAAA,EAA+D,EAAA,gBAAA,CAA5B,EAAY,CAAK,CAAA,EAAA,CAAA,CAAA,CAAA,ihBEnC1D,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA8CU,UAAA,CA9CD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,aAAqB,EAAA,SAAQ,eAAkB,EAAA,WAAQ,EAAA,CAAA,CAAA,EAAA,CACpD,EAAA,UAAA,EACd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOM,MAPN,GAOM,EAAA,EANJ,EAAA,YAAA,EAAA,EAA0C,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAhC,MAAM,SAAS,KAAK,SAC9B,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAJN,GAIM,EAHJ,EAAA,EAAA,YAAA,EAAA,EAA0C,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAhC,MAAM,OAAO,OAAO,aAC9B,EAAA,EAAA,YAAA,EAAA,EAA2C,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAjC,MAAM,SAAS,OAAO,YAChC,EAAA,EAAA,YAAA,EAAA,EAA0C,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAhC,MAAM,OAAO,OAAO,WAKpC,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiCW,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAhCT,EAAA,EAAA,mBAAA,CAiBM,MAjBN,GAiBM,CAhBO,EAAA,MAAQC,EAAAA,OAAO,OAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAJN,GAIM,EAAA,EAHJ,EAAA,WAAA,CAEO,EAAA,OAAA,OAAA,CAAA,MAAA,CADI,EAAA,OAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA+B,IAAA,OAAf,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,IAAI,CAGpB,EAAA,KAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAAA,EAAA,OAASA,EAAAA,OAAO,QAAA,EAA3B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUM,MAVN,GAUM,EAAA,EATJ,EAAA,WAAA,CAQO,EAAA,OAAA,QAAA,CAAA,MAAA,CANG,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMO,OAAA,OAJL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,0BACE,EAAA,MAAM,UAAS,4BAA+B,EAAA,MAAM,YAAS,EAAA,CAAA,CAElE,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,MAAM,KAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAMtB,EAAA,EAAA,mBAAA,CAQM,MARN,GAQM,EAPJ,EAAA,EAAA,mBAAA,CAA4C,IAA5C,IAAA,EAA4C,EAAA,gBAAA,CAAZ,EAAA,KAAK,EAAA,CAAA,GACrC,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAqC,EAAA,OAAA,QAAA,CAAA,MAAA,EAAA,EAAf,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,KAAK,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAEpB,EAAA,MAAQA,EAAAA,OAAO,OAAA,EAAxB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEI,IAFJ,GAEI,EAAA,EADF,EAAA,WAAA,CAAmC,EAAA,OAAA,OAAA,CAAA,MAAA,EAAA,EAAd,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,IAAI,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAIfA,EAAAA,OAAO,SAAA,EAArB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAES,SAFT,GAES,EAAA,EADP,EAAA,WAAA,CAAsB,EAAA,OAAA,QAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,wJEvD9B,IAAM,EAAQ,EAkBR,GAAA,EAAY,EAAA,SAAA,KAAe,CAC/B,EAAM,UAAY,OAAS,mBAAqB,oBAAoB,EAAM,UAC1E,CAAE,oBAAqB,EAAM,KAAM,CACrC,CAAC,iBAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAjBD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,aAAqB,EAAA,KAAS,CAAA,CAAA,EAAA,CACvBC,EAAAA,OAAO,OAAA,EACrB,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAA4F,EAAA,SAAA,CAAA,IAAA,CAAA,GAAA,EAAA,EAAA,WAAA,CAA9D,EAAA,OAAhB,EAAM,KAApB,EAAA,EAAA,WAAA,CAA4F,EAAA,OAAA,OAAA,CAAtD,IAAK,EAA0B,OAAc,SAGnF,EAAA,GAAA,KAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAUE,EAAA,SAAA,CAAA,IAAA,CAAA,GAAA,EAAA,EAAA,WAAA,CATwB,EAAA,OAAhB,EAAM,MADhB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAUE,GAAA,CARC,IAAK,EACL,MAAO,EAAK,MACZ,MAAO,EAAK,MACZ,KAAM,EAAK,KACX,SAAU,EAAK,UAAQ,UACvB,KAAM,EAAK,KACX,MAAO,EAAK,MACZ,QAAS,EAAK,kgBEnCrB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAkBM,MAAA,CAlBD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,mBAAkB,CAAA,4BAAwC,EAAA,OAAO,CAAA,CAAA,CAAA,EAAA,EAAA,EAC1E,EAAA,mBAAA,CAaM,MAbN,GAaM,CAZO,EAAA,MAAQC,EAAAA,OAAO,OAAA,EAA1B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAJN,GAIM,EAAA,EAHJ,EAAA,WAAA,CAEO,EAAA,OAAA,OAAA,CAAA,MAAA,CADI,EAAA,OAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA+B,IAAA,OAAf,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,IAAI,CAG/B,EAAA,KAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAMM,MANN,GAMM,EAAA,EALJ,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAoD,KAApD,IAAA,EAAoD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,GAAA,EAC5C,EAAA,WAAA,CAAoB,EAAA,OAAA,MAAA,CAAA,CAAA,EAEb,EAAA,WAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAwE,IAAxE,IAAA,EAAwE,EAAA,gBAAA,CAAf,EAAA,QAAQ,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAG1DA,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,EAAA,CAAA,uWEjB3B,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAaU,UAAA,CAbD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,iBAAyB,EAAA,QAAO,mBAAsB,EAAA,UAAO,EAAA,CAAA,CAAA,EAAA,CAC/D,EAAA,OAAS,EAAA,aAAeC,EAAAA,OAAO,UAAA,EAA1C,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MARN,GAQM,EAAA,EAPJ,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAkD,KAAlD,IAAA,EAAkD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EACjC,EAAA,cAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA+E,IAA/E,IAAA,EAA+E,EAAA,gBAAA,CAAlB,EAAA,WAAW,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,EAE/DA,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAG3B,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAQ,EAAA,OAAA,SAAA,CAAA,CAAA,CAAA,EAAA,CAAA,mRENZ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,MAAA,CAdJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,eAAc,CAAA,iBACM,EAAA,QAAK,CAAA,sBAA6B,EAAA,aAAa,CAAA,CAAA,CAAA,CAAA,EAAA,CAE9DC,EAAAA,OAAO,SAAWA,EAAAA,OAAO,UAAA,EAApC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIM,MAJN,GAIM,EAAA,EAHJ,EAAA,WAAA,CAEO,EAAA,OAAA,UAAA,CAAA,MAAA,EAAA,EADL,EAAA,WAAA,CAAQ,EAAA,OAAA,SAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAGDA,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAEdA,EAAAA,OAAO,YAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAyB,EAAA,OAAA,WAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,sSElB/B,IAAM,EAAQ,EAWR,GAAA,EAAe,EAAA,SAAA,KAAe,EAAM,MAAM,UAAW,GAAS,EAAK,MAAQ,EAAM,WAAW,CAAC,EAEnG,SAAS,EAAU,EAAe,CAGhC,OAFI,EAAQ,EAAa,MAAc,OACnC,IAAU,EAAa,MAAc,UAClC,SACT,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAjBD,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,kBAAiB,oBAA6B,EAAA,aAAW,CAAA,CAAA,EAAA,GAAA,EAClE,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAdoB,EAAA,OAAhB,EAAM,MADhB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAeM,MAAA,CAbH,IAAK,EAAK,IACX,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,wBAAuB,0BACK,EAAU,CAAK,GAAA,CAAA,CAEjD,EAAA,EAAA,EAAA,EAAA,WAAA,CAQO,EAAA,OAAA,OAAA,CARkB,OAAc,QAAQ,MAAO,EAAU,CAAK,CAArE,MAQO,EAAA,EAPL,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,mBAAA,CAA4B,OAAA,MAAA,EAAA,EAAA,gBAAA,CAAnB,EAAK,CAAA,EAAA,CAAA,CAAA,CAAA,GAAA,EAEhB,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAsD,IAAtD,IAAA,EAAsD,EAAA,gBAAA,CAAjB,EAAK,KAAK,EAAA,CAAA,EACtC,EAAK,cAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA0F,IAA1F,IAAA,EAA0F,EAAA,gBAAA,CAAvB,EAAK,WAAW,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,kBEdhF,GAAiD,OAAO,aAAa,gMCjBlF,IAAM,EAAQ,EAqBR,EAAO,EAEP,GAAA,EAAS,EAAA,SAAA,KAAe,EAAM,UAAU,EACxC,GAAA,EAAW,EAAA,SAAA,KAAe,EAAM,QAAQ,EACxC,GAAA,EAAc,EAAA,SAAA,KAAe,EAAM,WAAW,EAEpD,SAAS,EAAO,EAAc,CACxB,EAAM,UACV,EAAK,oBAAqB,EAAM,aAAe,EAAO,EAAI,CAAI,CAChE,EAEA,EAAA,EAAA,QAAA,CAAQ,GAAiB,CAAE,SAAQ,SAAQ,WAAU,aAAY,CAAC,EAElE,IAAM,GAAA,EAAO,EAAA,IAAA,CAAwB,IAAI,EAOzC,SAAS,EAAU,EAAsB,CACvC,GAAI,EAAM,cAAgB,aAAc,OACxC,IAAM,EAAQ,CAAE,WAAY,EAAG,UAAW,GAAI,KAAM,EAAG,IAAK,CAAE,EAAE,EAAM,KACtE,GAAI,IAAU,IAAA,GAAW,OAEzB,IAAM,EAAW,MAAM,KACrB,EAAK,OAAO,iBACV,+DACF,GAAK,CAAC,CACR,EACM,EAAQ,EAAS,QAAQ,SAAS,aAAkC,EAC1E,GAAI,EAAQ,GAAK,EAAS,SAAW,EAAG,OAExC,IAAM,EACJ,EAAM,MAAQ,OACV,EACA,EAAM,MAAQ,MACZ,EAAS,OAAS,GACjB,EAAQ,EAAQ,EAAS,QAAU,EAAS,OAErD,EAAM,eAAe,EACrB,EAAS,EAAK,CAAC,MAAM,CACvB,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAAA,CAFG,QAAA,OAAJ,IAAI,EAAO,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,cAAa,gBAAyB,EAAA,OAAW,CAAA,EAAc,WACnF,EAAA,EAAA,EAAA,EAAA,WAAA,CAAQ,EAAA,OAAA,SAAA,CAAA,EAAA,EAAA,2+BEjEZ,IAAM,EAAQ,EAcR,GAAA,EAAO,EAAA,OAAA,CAAO,GAAiB,IAAI,EAGnC,GAAA,EAAY,EAAA,IAAA,CAAI,EAAI,EAEpB,GAAA,EAAO,EAAA,SAAA,KAAgB,EAAO,EAAK,OAAO,QAAU,EAAM,KAAO,EAAU,KAAM,EACjF,GAAA,EAAa,EAAA,SAAA,KAAe,EAAM,WAAa,GAAM,SAAS,OAAS,GAAM,EAC7E,GAAA,EAAa,EAAA,SAAA,KAAe,GAAM,YAAY,QAAU,YAAY,EAIpE,GAAA,EAAU,EAAA,SAAA,KAAe,IAAS,IAAI,EAMtC,GAAA,EAAO,EAAA,SAAA,KAAe,EAAW,OAAS,GAAQ,MAAQ,EAAM,KAAO,EAAK,OAAO,KAAK,EAE9F,SAAS,GAAS,CACZ,EAAW,QACX,EAAM,EAAK,OAAO,EAAM,IAAI,EAC3B,EAAU,MAAQ,CAAC,EAAU,MACpC,cAIc,EAAA,QAyCoB,EAOhC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA+CM,MAAA,OA7CJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,4CAA2C,CAAA,uBACf,EAAA,MAAI,uBAA0B,EAAA,KAAI,CAAA,CAAA,CAEpE,EAAA,EAAA,EAAA,EAAA,mBAAA,CAgCM,MAhCN,GAgCM,CA9BI,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIE,OAAA,OAFA,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,uBAAsB,CAAA,6BACY,EAAA,OAAQ,EAAA,KAAI,CAAA,CAAA,6CAEtD,EAAA,EAAA,mBAAA,CAsBS,SAAA,CArBP,KAAK,SACL,MAAM,0BACL,gBAAe,EAAA,MACf,eAAc,EAAA,MAAI,OAAY,IAAA,GAC9B,SAAU,EAAA,MACV,QAAO,KAER,EAAA,EAAA,mBAAA,CAUO,OAAA,CATL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,wBAAuB,CACsB,4BAAA,EAAA,MAAiD,8BAAA,EAAA,SAIpG,cAAY,MAEH,EAAA,CAAA,EAAA,QAAA,EAAT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAqC,IAArC,EAAqC,KAAA,EACrC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAsC,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EAAlB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,IAAI,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,EAAA,CAAA,GAE1B,EAAA,EAAA,mBAAA,CAAkD,KAAlD,IAAA,EAAkD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EAC9B,EAAA,OAAK,MAAgD,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAAhD,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA2E,OAA3E,IAAA,EAA2E,EAAA,gBAAA,CAAf,EAAA,KAAK,EAAA,CAAA,GACrD,EAAA,UAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAyE,OAAzE,IAAA,EAAyE,EAAA,gBAAA,CAAjB,EAAA,OAAO,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,SAEtDC,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAoC,EAAA,OAAA,UAAA,CAAd,KAAM,EAAA,KAAI,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAIzB,CAAA,EAAA,EAAA,QAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOM,MAPN,GAOM,EAAA,EANJ,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAQ,EAAA,OAAA,SAAA,CAAA,CAAA,EAECA,EAAAA,OAAO,SAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAsB,EAAA,OAAA,QAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,EAAA,CAAA,KA5FhB,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA8CM,MAAA,OA9CkB,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,iBAAgB,CAAA,uBAAmC,EAAA,KAAI,CAAA,CAAA,CACnF,EAAA,EAAA,EAAA,EAAA,mBAAA,CASM,MATN,GASM,EAAA,EARJ,EAAA,mBAAA,CAMO,OAAA,CALL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,wBAAuB,CAAA,4BACU,EAAA,KAAI,CAAA,CAAA,EAC3C,cAAY,MAET,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,IAAI,EAAA,CAAA,EAEG,EAAA,QAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAoD,OAApD,EAAoD,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAAA,EAGtD,EAAA,mBAAA,CAiCM,MAjCN,GAiCM,EAAA,EAhCJ,EAAA,mBAAA,CA+BM,MA/BN,GA+BM,EAAA,EA9BJ,EAAA,mBAAA,CAoBM,MApBN,GAoBM,EAAA,EAnBJ,EAAA,mBAAA,CAeS,SAAA,CAdP,KAAK,SACL,MAAM,0BACL,gBAAe,EAAA,MACf,eAAc,EAAA,MAAI,OAAY,IAAA,GAC9B,SAAU,EAAA,MACV,QAAO,KAER,EAAA,EAAA,mBAAA,CAGE,IAAA,CAFA,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,0BACE,EAAA,MAAI,qBAAA,qBAAA,CAAA,CAAA,EAAA,KAAA,CAAA,GAEd,EAAA,EAAA,mBAAA,CAAkD,KAAlD,IAAA,EAAkD,EAAA,gBAAA,CAAb,EAAA,KAAK,EAAA,CAAA,EAC9B,EAAA,OAAK,MAAgD,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAAhD,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA2E,OAA3E,IAAA,EAA2E,EAAA,gBAAA,CAAf,EAAA,KAAK,EAAA,CAAA,GACrD,EAAA,UAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAyE,OAAzE,IAAA,EAAyE,EAAA,gBAAA,CAAjB,EAAA,OAAO,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAEtDA,EAAAA,EAAAA,EAAAA,EAAAA,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAoC,EAAA,OAAA,UAAA,CAAd,KAAM,EAAA,KAAI,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,EAIpB,EAAA,QAAA,EAAhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOW,EAAA,SAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EANT,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAQ,EAAA,OAAA,SAAA,CAAA,CAAA,EAECA,EAAAA,OAAO,SAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAsB,EAAA,OAAA,QAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,EAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,+aEtFlC,IAAM,EAAQ,EAeR,GAAA,EAAU,EAAA,SAAA,KAAe,EAAM,MAAM,OAAQ,GAAM,EAAE,QAAU,IAAI,CAAC,CAAC,MAAM,EAC3E,GAAA,EAAa,EAAA,SAAA,KAAe,GAAG,EAAQ,MAAM,GAAG,EAAM,MAAM,QAAQ,EAEpE,EAAwC,CAC5C,GAAI,qBACJ,KAAM,6BACN,IAAK,oBACP,EAEA,SAAS,EAAO,EAA+B,CAC7C,OAAO,EAAM,IAAU,EAAM,IAC/B,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAgBM,MAhBN,GAgBM,CAfO,EAAA,OAAS,EAAA,YAAA,EAApB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,EAAA,EAFJ,EAAA,mBAAA,CAAoD,OAApD,IAAA,EAAoD,EAAA,gBAAA,CAAf,EAAA,KAAK,EAAA,CAAA,EAC9B,EAAA,YAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA0E,OAA1E,IAAA,EAA0E,EAAA,gBAAA,CAApB,EAAA,KAAU,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAGxD,EAAA,MAAM,SAAA,EAAhB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKK,KALL,GAKK,GAAA,EAJH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAGK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAHuB,EAAA,OAAhB,EAAM,MAAlB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGK,KAAA,CAH+B,IAAG,GAAK,EAAK,MAAK,GAAI,IAAS,MAAM,oBACvE,EAAA,EAAA,EAAA,EAAA,mBAAA,CAAwE,IAAA,CAApE,OAAA,EAAK,EAAA,eAAA,CAAA,CAAG,EAAO,EAAK,KAAK,EAAA,uBAA0B,EAAK,OAAK,CAAA,CAAA,EAAA,KAAA,CAAA,GAAA,EACjE,EAAA,mBAAA,CAAyD,OAAzD,IAAA,EAAyD,EAAA,gBAAA,CAApB,EAAK,KAAK,EAAA,CAAA,CAAA,CAAA,EAInD,EAAA,GAAA,EAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAA4C,EAAA,OAAA,QAAA,CAAA,MAAA,EAAA,EAAtB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,YAAY,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,g8BEnBlC,GAAmB,0EACnB,GAAqB,2hDAwB3B,IAAM,EAAQ,EAsIR,EAAO,EAYP,CAAE,gBAAiB,EAAA,EAAc,EAIjC,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,SAAW,YAAY,EAGxD,GAAA,EAAe,EAAA,SAAA,KACnB,EAAM,UAAY,EAAM,kBAAoB,GACxC,GACA,EAAM,eACZ,EAIM,GAAA,EAAY,EAAA,IAAA,CAAI,EAAK,EAE3B,SAAS,EAAa,EAAgB,CAChC,EAAU,QAAU,IACxB,EAAU,MAAQ,EACpB,CAKA,SAAS,GAAkB,CACzB,EAAa,CAAC,EAAU,KAAK,CAC/B,CAEA,IAAM,GAAA,EAAgB,EAAA,SAAA,KAAgB,EAAU,MAAQ,EAAM,YAAc,EAAM,aAAc,EAI1F,GAAA,EAAW,EAAA,SAAA,KAAe,IAAI,IAAI,EAAM,UAAU,CAAC,EAKnD,GAAA,EAAyB,EAAA,SAAA,KACvB,EAAM,gBAAkB,EAAM,SAAW,OAAS,WAC1D,EAEA,SAAS,EAAW,EAA0B,CAC5C,OAAO,EAAS,MAAM,IAAI,CAAE,CAC9B,CAEA,SAAS,EAAO,EAAiB,CAC/B,GAAI,EAAM,SAAU,OACpB,IAAM,EAAO,EAAuB,MACpC,GAAI,IAAS,OAAQ,OACrB,IAAM,EACJ,IAAS,SACL,EAAW,CAAE,EACX,CAAC,EACD,CAAC,CAAE,EACL,EAAW,CAAE,EACX,EAAM,WAAW,OAAQ,GAAM,IAAM,CAAE,EACvC,CAAC,GAAG,EAAM,WAAY,CAAE,EAChC,EAAK,oBAAqB,CAAI,CAChC,CAEA,SAAS,GAAQ,CACX,EAAM,UAAY,EAAuB,QAAU,QAAU,CAAC,EAAM,WAAW,QACnF,EAAK,oBAAqB,CAAC,CAAC,CAC9B,CAEA,IAAM,GAAA,EAAmB,EAAA,SAAA,KAAe,EAAM,SAAS,OAAQ,GAAM,EAAW,EAAE,EAAE,CAAC,CAAC,EAEhF,GAAA,EAAe,EAAA,SAAA,KACnB,EAAiB,MAAM,QAAQ,EAAO,IAAM,GAAS,EAAE,MAAQ,GAAI,CAAC,CACtE,EAEM,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,eAAe,EAAiB,MAAM,MAAM,CAAC,EAI9E,GAAA,EAAO,EAAA,IAAA,CAAI,EAAE,EAEnB,SAAS,EAAU,EAAuB,CACxC,OAAO,EACJ,UAAU,KAAK,CAAA,CACf,QAAQ,mBAAoB,EAAE,CAAA,CAC9B,YAAY,CACjB,CAEA,IAAM,GAAA,EAAkB,EAAA,SAAA,KAAe,CACrC,IAAM,EAAS,EAAU,EAAK,MAAM,KAAK,CAAC,EAE1C,OADK,EACE,EAAM,SAAS,OAAQ,GAAM,EAAU,GAAG,EAAE,KAAK,GAAG,EAAE,WAAa,IAAI,CAAC,CAAC,SAAS,CAAM,CAAC,EAD5E,EAAM,QAE5B,CAAC,EAED,SAAS,EAAS,EAAmC,CAEnD,OADI,EAAQ,MAAQ,KAAa,GAC1B,GAAG,EAAa,EAAQ,KAAM,EAAM,YAAY,EAAE,GAAG,EAAM,aAAa,KAAK,CACtF,CAEA,IAAM,GAAA,EAAgB,EAAA,SAAA,KAAe,EAAa,EAAa,MAAO,EAAM,YAAY,CAAC,EAMnF,GAAA,EAAY,EAAA,IAAA,CAA2B,IAAI,EAC3C,GAAA,EAAO,EAAA,IAAA,CAA2B,IAAI,EACtC,GAAA,EAAS,EAAA,IAAA,CAA2B,IAAI,EACxC,IAAA,EAAW,EAAA,IAAA,CAAI,EAAK,EAEtB,EAA0B,KAC1B,EAAyB,KACzB,EAAkC,KAClC,EAAwC,KACtC,EAAS,IAAI,IAGb,EAAa,IAAI,IAMnB,EAAS,GAGT,EAAgB,GAKd,IAAA,EAAe,EAAA,SAAA,KAAe,CAClC,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAW,EAAM,SAAU,EAAO,IAAI,EAAQ,GAAI,CAAO,EACpE,OAAO,CACT,CAAC,EAED,SAAS,GAAS,EAAwC,CACxD,IAAM,EAAK,EAAW,CAAE,EAEpB,EADS,EAAK,EAAM,qBAAuB,EAAM,aAErD,GAAI,EAAM,aAAc,CACtB,IAAM,EAAU,GAAa,MAAM,IAAI,CAAE,EACnC,EAAS,EAAU,EAAM,aAAa,EAAS,CAAE,EAAI,KACvD,IAAQ,EAAS,CAAE,GAAG,EAAQ,GAAG,CAAO,EAC9C,CAKA,OAHI,EAAM,kBAAoB,MAAQ,EAAM,mBAAqB,IAC/D,EAAS,CAAE,GAAG,EAAQ,GAAG,EAAM,oBAAqB,GAE/C,CACT,CAQA,IAAM,GAAA,EAAU,EAAA,IAAA,CAA6B,IAAI,EAC3C,GAAA,EAAa,EAAA,IAAA,CAA8B,CAAE,EAAG,EAAG,EAAG,CAAE,CAAC,EAO/D,SAAS,GAAU,EAA0C,CAC3D,OAAQ,GAAU,IAAA,EACpB,CAIA,SAAS,GAAkB,EAAsB,CAC/C,IAAM,EAAQ,GAAQ,eACtB,GAAI,CAAC,EAAO,OACZ,IAAM,EAAU,EAAU,OAAO,aAAe,EAC1C,EAAS,EAAU,OAAO,cAAgB,EAChD,EAAW,MAAQ,CACjB,EAAG,EAAU,KAAK,IAAI,KAAK,IAAI,EAAM,EAAG,EAAE,EAAG,EAAU,EAAE,EAAI,EAAM,EACnE,EAAG,EAAS,KAAK,IAAI,KAAK,IAAI,EAAM,EAAG,EAAE,EAAG,EAAS,EAAE,EAAI,EAAM,CACnE,CACF,CAEA,SAAS,GAAa,EAA2B,EAAsB,CACrE,GAAkB,CAAM,EAGpB,EAAQ,OAAO,KAAO,EAAQ,KAClC,EAAQ,MAAQ,EAChB,EAAK,gBAAiB,CAAO,EAC/B,CAEA,SAAS,GAAa,EAAsB,CACtC,EAAQ,OAAO,GAAkB,CAAM,CAC7C,CAEA,SAAS,GAAgB,CAClB,EAAQ,QACb,EAAQ,MAAQ,KAChB,EAAK,eAAe,EACtB,CAGA,SAAS,EAAgB,EAA2B,EAAsB,CACxE,GAAI,EAAQ,OAAO,KAAO,EAAQ,GAAI,CACpC,EAAc,EACd,MACF,CACA,EAAQ,MAAQ,KAChB,GAAa,EAAS,CAAM,CAC9B,CAGA,IAAM,IAAA,EAAgB,EAAA,SAAA,KAAe,EAAW,MAAM,EAAI,GAAG,EAEvD,IAAA,EAAe,EAAA,SAAA,KACnB,EAAM,kBAAoB,QACtB,IAAA,GACA,CAAE,KAAM,GAAG,EAAW,MAAM,EAAE,IAAK,IAAK,GAAG,EAAW,MAAM,EAAE,GAAI,CACxE,EAEA,SAAS,GAAM,EAAuB,CAChC,EAAO,MAAQ,UAAU,EAAc,CAC7C,CAEA,SAAS,EAAY,EAAmC,CACtD,GAAI,EAAM,aAAc,OAAO,EAAM,aAAa,CAAO,EACzD,IAAM,EAAO,EAAS,CAAO,EAC7B,OAAO,EAAO,GAAG,EAAQ,KAAK,KAAK,IAAS,EAAQ,IACtD,CAEA,SAAS,EAAQ,EAAuB,EAA2B,CACjE,IAAM,EAAQ,EAAY,CAAO,EAG5B,GACL,EAAM,YACJ,EACA,EAAM,WAAa,QACf,CACE,UAAW,GACX,OAAQ,GACR,UAAW,SACX,UAAW,oBACX,QAAS,CACX,EACA,CAAE,UAAW,GAAM,UAAW,SAAU,UAAW,oBAAqB,QAAS,CAAE,CACzF,CACF,CAMA,IAAM,GAAA,EAAoB,EAAA,SAAA,KAEtB,EAAuB,QAAU,QACjC,EAAM,WAAa,SACnB,EAAM,gBAAkB,MAC5B,EAEA,SAAS,EAAY,EAA2B,CAC9C,GAAI,CAAC,GAAW,CAAC,GAAO,CAAC,EAAQ,UAAW,OAC5C,IAAM,EAAQ,EAAQ,QAAQ,EAAQ,UAAkC,CACtE,UAAa,GAAS,EAAQ,EAAE,EAChC,YAAa,EAAkB,KACjC,CAAC,EACD,EAAM,GAAG,QAAU,GAAW,CAC5B,EAAO,EAAQ,EAAE,EAGjB,EAAK,gBAAiB,CAAO,EACzB,EAAM,gBAAkB,SAAS,EAAgB,EAAS,GAAU,CAAM,CAAC,CACjF,CAAC,EACG,EAAM,gBAAkB,UAC1B,EAAM,GAAG,YAAc,GAAW,GAAa,EAAS,GAAU,CAAM,CAAC,CAAC,EAC1E,EAAM,GAAG,YAAc,GAAW,GAAa,GAAU,CAAM,CAAC,CAAC,EACjE,EAAM,GAAG,eAAkB,EAAc,CAAC,GAE5C,EAAQ,EAAO,CAAO,EACtB,EAAM,MAAM,CAAG,EACf,EAAO,IAAI,EAAQ,GAAI,CAAK,EAC5B,EAAW,IAAI,EAAQ,GAAI,EAAQ,SAAS,CAC9C,CAKA,SAAS,GAAe,CACtB,GAAI,CAAC,GAAW,CAAC,EAAK,OAEtB,IAAM,EAAU,IAAI,IACpB,IAAK,IAAM,KAAW,EAAM,SACtB,EAAQ,WAAW,EAAQ,IAAI,EAAQ,GAAI,CAAO,EAGxD,EAAO,SAAS,EAAO,IAAO,CACxB,EAAQ,IAAI,CAAE,IAClB,EAAM,OAAO,EACb,EAAO,OAAO,CAAE,EAChB,EAAW,OAAO,CAAE,EACtB,CAAC,EAED,EAAQ,SAAS,EAAS,IAAO,CAC/B,IAAM,EAAW,EAAO,IAAI,CAAE,EAC9B,GAAI,GAAY,EAAW,IAAI,CAAE,IAAM,EAAQ,UAAW,CAExD,IAAM,EAAQ,EAAY,CAAO,EAC3B,EAAU,EAAS,WAAW,EAC/B,EACI,EAAS,EAAQ,WAAW,CAAK,EACrC,EAAQ,EAAU,CAAO,EAFlB,EAAS,cAAc,EAGnC,MACF,CACI,IACF,EAAS,OAAO,EAChB,EAAO,OAAO,CAAE,EAChB,EAAW,OAAO,CAAE,GAEtB,EAAY,CAAO,CACrB,CAAC,EAED,GAAW,EAEP,CAAC,IAAW,EAAO,MAAQ,EAAM,iBACnC,EAAW,EACX,EAAS,GACT,EAAgB,EAAQ,EAAM,cAElC,CAGA,SAAS,IAAa,CACpB,EAAO,SAAS,EAAO,IAAO,CAC5B,IAAM,EAAK,EAAW,CAAE,EACxB,EAAM,SAAS,GAAS,CAAE,CAAC,GACX,EAAM,WAAW,CAAC,EAAE,WAAW,EAAA,EACtC,UAAU,OAAO,wBAAyB,CAAE,CACvD,CAAC,CACH,CAEA,SAAS,GAAa,CACpB,GAAI,CAAC,GAAW,CAAC,EAAK,OACtB,IAAM,EAAoB,CAAC,EAE3B,GAAI,EAAM,cAAe,CACvB,IAAM,EAAQ,EAAQ,QAAQ,EAAM,aAAoC,CAAC,CAAC,UAAU,EAChF,EAAM,QAAQ,GAAG,EAAQ,KAAK,EAAM,aAAa,EAAG,EAAM,aAAa,CAAC,CAC9E,CAEK,EAAQ,QACX,EAAO,QAAS,GAAU,CACxB,IAAM,EAAS,EAAM,UAAU,EAC3B,EAAO,QAAQ,GAAG,EAAQ,KAAK,EAAO,aAAa,EAAG,EAAO,aAAa,CAAC,CACjF,CAAC,EAGC,EAAQ,OAAQ,EAAI,UAAU,EAAQ,aAAa,CAAO,EAAG,CAAE,QAAS,CAAC,GAAI,EAAE,CAAE,CAAC,EACjF,EAAI,QAAQ,CAAC,EAAG,CAAC,EAAG,CAAC,CAC5B,CAIA,SAAS,GAAa,EAAiB,EAA4D,CACjG,GAAI,CAAC,EAAK,OACV,IAAM,EAAQ,EAAO,IAAI,CAAE,EAC3B,GAAI,CAAC,EAAO,OACZ,IAAM,EAAS,EAAM,UAAU,EAG3B,EAAO,QAAQ,GAAG,EAAI,UAAU,EAAQ,CAAE,QAAS,CAAC,GAAI,EAAE,EAAG,GAAG,CAAQ,CAAC,CAC/E,CASA,SAAS,EAAY,EAAa,EAAa,EAAoC,CACjF,IAAI,EAAS,GACb,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAS,EAAG,EAAI,EAAK,OAAQ,EAAI,IAAK,CAC7D,GAAM,CAAC,EAAI,GAAM,EAAK,GAChB,CAAC,EAAI,GAAM,EAAK,GACR,EAAK,GAAQ,EAAK,GAAO,GAAQ,EAAK,IAAO,EAAM,IAAQ,EAAK,GAAM,IACzE,EAAS,CAAC,EACvB,CACA,OAAO,CACT,CAEA,SAAS,GAAiB,EAAa,EAAa,EAAuC,CACzF,IAAM,EACJ,EAAU,OAAS,UAAY,CAAC,EAAU,WAAW,EAAI,EAAU,YAErE,IAAK,IAAM,KAAS,EACd,MAAC,EAAM,QAAU,CAAC,EAAY,EAAK,EAAK,EAAM,EAAE,IAGhD,CADa,EAAM,MAAM,CAAC,CAAC,CAAC,KAAM,GAAS,EAAY,EAAK,EAAK,CAAI,CACpE,EAAU,MAAO,GAExB,MAAO,EACT,CAMA,SAAS,GAAQ,EAAiB,EAAqC,CACrE,GAAI,CAAC,GAAO,CAAC,EAAU,MAAO,OAAO,KACrC,IAAM,EAAO,EAAU,MAAM,sBAAsB,EAC7C,EAAI,EAAU,EAAK,KACnB,EAAI,EAAU,EAAK,IAGzB,GAAI,EAAK,MAAQ,GAAK,EAAK,OAAS,IAAM,EAAI,GAAK,EAAI,GAAK,EAAI,EAAK,OAAS,EAAI,EAAK,QACrF,OAAO,KACT,IAAM,EAAS,EAAI,uBAAuB,CAAC,EAAG,CAAC,CAAC,EAChD,IAAK,IAAM,KAAW,EAAM,SAC1B,GAAI,EAAQ,WAAa,GAAiB,EAAO,IAAK,EAAO,IAAK,EAAQ,SAAS,EACjF,OAAO,EAAQ,GAEnB,OAAO,IACT,CAIA,SAAS,GAAgB,CACnB,CAAC,GAAW,CAAC,IACjB,EAAO,QAAS,GAAU,EAAM,OAAO,CAAC,EACxC,EAAO,MAAM,EACb,EAAW,MAAM,EACjB,EAAa,EACf,CAIA,SAAS,IAAc,CACjB,CAAC,GAAO,CAAC,EAAU,OACnB,EAAU,MAAM,YAAc,GAAK,EAAU,MAAM,aAAe,GAAG,EAAI,eAAe,CAC9F,CAMA,SAAS,GAAgB,CACvB,GAAI,CAAC,EAAK,OAAS,CAAC,EAAO,MAAO,OAClC,GAAI,CAAC,EAAU,MAAO,CACpB,EAAK,MAAM,MAAM,eAAe,gBAAgB,EAChD,MACF,CACA,IAAM,EAAI,EAAO,MAAM,aACnB,EAAI,GAAG,EAAK,MAAM,MAAM,YAAY,iBAAkB,GAAG,EAAE,GAAG,CACpE,CAEA,eAAe,IAAW,CACnB,KAAU,MACf,IAAI,CACF,IAAM,EAAO,MAAM,OAAO,WAC1B,EAAU,EAAI,SAAW,CAC3B,MAAQ,CACN,GAAS,MAAQ,GACjB,MACF,CAGK,KAAU,MAEf,GAAI,CAGF,EAAM,EAAQ,IAAI,EAAU,MAAO,CACjC,YAAa,GACb,mBAAoB,GACpB,aAAc,EAChB,CAAC,EACD,EACG,UAAU,EAAM,QAAS,CACxB,QAAS,EAAM,QACf,YAAa,EAAM,eACrB,CAAC,CAAA,CACA,MAAM,CAAG,EACZ,EAAa,EAGR,GAAQ,EAAI,QAAQ,CAAC,EAAG,CAAC,EAAG,CAAC,EAClC,GAAY,EAER,OAAO,eAAmB,MAC5B,EAAW,IAAI,mBAAqB,GAAY,CAAC,EACjD,EAAS,QAAQ,EAAU,KAAK,EAEpC,MAAQ,CACN,GAAS,MAAQ,EACnB,CA/BA,CAgCF,QAEA,EAAA,EAAA,UAAA,KAAgB,CAEV,OAAO,OAAW,KAAa,OAAO,iBAAiB,UAAW,EAAK,EAC3E,GAAc,EACd,EAAc,EACV,OAAO,eAAmB,KAAe,EAAO,QAClD,EAAiB,IAAI,mBAAqB,EAAc,CAAC,EACzD,EAAe,QAAQ,EAAO,KAAK,EAEvC,CAAC,GAED,EAAA,EAAA,gBAAA,KAAsB,CAChB,OAAO,OAAW,KAAa,OAAO,oBAAoB,UAAW,EAAK,EAC9E,GAAU,WAAW,EACrB,EAAW,KACX,GAAgB,WAAW,EAC3B,EAAiB,KACjB,EAAO,MAAM,EACb,EAAW,MAAM,EACjB,GAAK,OAAO,EACZ,EAAM,IACR,CAAC,GAED,EAAA,EAAA,MAAA,KAAY,EAAM,SAAU,CAAY,GAGxC,EAAA,EAAA,MAAA,CAAM,KAAO,EAAM,kBAAqB,EAAM,eAAe,MAAS,CACpE,EAAc,EACd,EAAc,CAChB,CAAC,GACD,EAAA,EAAA,MAAA,KAAY,EAAM,WAAY,GAAY,CAAE,KAAM,EAAK,CAAC,GAGxD,EAAA,EAAA,MAAA,KAAY,EAAM,aAAc,EAAU,GAE1C,EAAA,EAAA,MAAA,KAAY,EAAM,iBAAkB,EAAU,GAI9C,EAAA,EAAA,MAAA,KACQ,EAAM,cACX,GAAQ,CACH,CAAC,GAAO,CAAC,GAAO,IACpB,EAAW,EACX,EAAS,GACT,EAAgB,GAClB,CACF,GAGA,EAAA,EAAA,MAAA,CAAM,KAAO,EAAM,SAAU,CAAiB,MAAS,EAAc,CAAC,GAKtE,EAAA,EAAA,MAAA,CAAM,CAAC,EAAW,CAAS,MAAS,EAClC,EAAK,EAAA,SAAA,KAAe,CAClB,EAAc,EACd,GAAY,CACd,CAAC,CACH,CAAC,EAED,EAAa,CACX,eAEA,WAAA,EAAW,EAAA,SAAA,CAAe,CAAS,EACnC,eAGA,aAEA,gBAEA,WAEA,SAAA,EAAS,EAAA,SAAA,CAAe,CAAO,EAE/B,eACF,CAAC,WAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAuIM,MAAA,CAtIA,QAAA,OAAJ,IAAI,EACJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,eAAc,CACsB,yBAAA,EAAA,SAA0C,yBAAA,EAAA,SAA8C,6BAAA,EAAA,QAAsB,OAA4C,wBAAA,EAAA,MAA4C,0BAAA,EAAA,OAAa,EAAA,OAAS,CAAK,EAAA,WAQ3Q,EAAA,EAAA,EAAA,EAAA,mBAAA,CAiHM,MAjHN,GAiHM,EAAA,EAhHJ,EAAA,mBAAA,CAkCM,MAAA,CAlCD,MAAM,oBAAqB,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,YAAiB,EAAA,MAAM,CAAA,KAC1D,EAAA,EAAA,mBAAA,CAAoD,MAAA,CAA3C,QAAA,YAAJ,IAAI,EAAY,MAAM,kCAChB,GAAA,QAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGM,MAHN,GAGM,CAFJ,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAuB,IAAA,CAApB,MAAM,WAAW,EAAA,KAAA,EAAA,GAAA,EACpB,EAAA,mBAAA,CAA+B,OAAA,MAAA,EAAA,EAAA,gBAAA,CAAtB,EAAA,KAAY,EAAA,CAAA,CAAA,CAAA,GAEN,EAAA,aAAA,EAAjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,GAEO,CADL,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAAyB,IAAA,CAAtB,MAAM,aAAa,EAAA,KAAA,EAAA,GAAA,EAAM,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,UAAU,EAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAKhC,EAAA,OAAW,EAAA,gBAAa,SAAA,EADhC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAsBM,MAAA,OApBJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,uBAAsB,CACyB,8BAAA,EAAA,kBAAe,QAA0D,+BAAA,EAAA,kBAAe,SAAgB,GAAA,MAA0D,8BAAA,EAAA,gBAAa,WAKnO,OAAA,EAAK,EAAA,eAAA,CAAE,GAAA,KAAY,EACpB,KAAK,SAEL,EAAA,EAAA,EAAA,EAAA,WAAA,CAUO,EAAA,OAAA,iBAAA,CARJ,QAAS,EAAA,MACT,SAAU,EAAW,EAAA,MAAQ,EAAE,EAC/B,MAAO,CAJV,MAUO,EAAA,EAJL,EAAA,mBAAA,CAAiE,OAAjE,IAAA,EAAiE,EAAA,gBAAA,CAAtB,EAAA,MAAQ,IAAI,EAAA,CAAA,EAC3C,EAAA,MAAQ,YAAA,EAApB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,MAAQ,SAAS,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAMhB,EAAA,CAAA,EAAA,EAAA,UA8EF,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IA9EE,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA2EM,MA3EN,GA2EM,CAzEI,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAaS,SAAA,OAXP,KAAK,SACL,MAAM,yBACL,gBAAa,CAAG,EAAA,MAChB,aAAY,EAAA,MACZ,MAAO,EAAA,MACP,QAAO,CAER,EAAA,EAAA,EAAA,EAAA,mBAAA,CAAoE,IAAA,CAAjE,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,KAAa,EAAA,MAAS,UAAA,kBAAA,CAAA,CAAA,EAAA,KAAA,CAAA,EACnB,EAAA,WAAW,SAAA,EAAvB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAA,WAAW,MAAM,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAIxB,EAAA,EAAA,mBAAA,CAsBM,MAtBN,GAsBM,EAAA,EArBJ,EAAA,YAAA,EAAA,EASY,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,KAAA,CARV,SAAA,EAAA,EAAA,QAAA,KAAkC,EAAA,EAAlC,EAAA,YAAA,EAAA,EAAkC,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAvB,MAAM,cAAc,CAAA,GAAA,EAC/B,EAAA,YAAA,EAAA,EAME,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CALS,WAAA,EAAA,MAAA,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAI,MAAA,EACb,MAAA,GACA,gBAAA,GACC,YAAa,EAAA,kBACb,aAAY,EAAA,0EAGjB,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUM,MAVN,GAUM,EAAA,EATJ,EAAA,mBAAA,CAAwD,OAAxD,IAAA,EAAwD,EAAA,gBAAA,CAAnB,EAAA,KAAS,EAAA,CAAA,GAAA,EAC9C,EAAA,mBAAA,CAOS,SAAA,CANP,KAAK,SACL,MAAM,sBACL,SAAU,EAAA,UAAQ,CAAK,EAAA,WAAW,OAClC,QAAO,CAEL,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,UAAU,EAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,GAKnB,EAAA,EAAA,mBAAA,CAkCK,KAlCL,GAkCK,GAAA,EAjCH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CA6BK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CA7BiB,EAAA,MAAX,KAAX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA6BK,KAAA,CA7BmC,IAAK,EAAQ,EAAA,EAAA,EAAA,EACnD,EAAA,WAAA,CA2BO,EAAA,OAAA,OAAA,CA3BqB,UAAU,SAAU,EAAW,EAAQ,EAAE,CAArE,MA2BO,EAAA,EA1BL,EAAA,mBAAA,CAyBS,SAAA,CAxBP,KAAK,SACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,qBAAoB,CAAA,yBACU,EAAW,EAAQ,EAAE,CAAA,CAAA,CAAA,EACxD,eAAc,EAAW,EAAQ,EAAE,EACnC,SAAU,EAAA,SACV,QAAK,GAAE,EAAO,EAAQ,EAAE,KAEzB,EAAA,EAAA,mBAAA,CAEO,OAFP,GAEO,CADI,EAAW,EAAQ,EAAE,IAAA,EAA9B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAuD,IAAvD,EAAuD,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,GAEzD,EAAA,EAAA,mBAAA,CAKO,OALP,GAKO,EAAA,EAJL,EAAA,mBAAA,CAA+D,OAA/D,IAAA,EAA+D,EAAA,gBAAA,CAAtB,EAAQ,IAAI,EAAA,CAAA,EACzC,EAAQ,YAAA,EAApB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAQ,SAAS,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,CAAA,EAIf,EAAQ,WAGf,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAHe,EADjB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAIE,IAJF,EAIE,GACU,EAAQ,MAAI,MACH,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IADG,EAAxB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAS,CAAO,CAAA,EAAA,CAAA,eAKhB,EAAA,GAAA,GAAA,EAAA,MAAgB,QACS,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IADT,EAA3B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEK,KAFL,GAEK,EAAA,EADH,EAAA,WAAA,CAA4C,EAAA,OAAA,QAAA,CAAA,MAAA,EAAA,EAAtB,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,YAAY,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,CAM9B,CAAA,EAAA,CAAA,EAAA,EAAA,2CAAA,EAAZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MAAA,OARoB,QAAA,SAAJ,IAAI,EAAS,MAAM,sBACvC,EAAA,EAAA,EAAA,EAAA,WAAA,CAMO,EAAA,OAAA,SAAA,CANc,KAAM,EAAA,MAAe,SAAU,EAAA,KAApD,MAMO,EAAA,EALL,EAAA,mBAAA,CAIO,OAJP,GAIO,EAHF,EAAA,EAAA,gBAAA,EAAA,EAAA,EAAA,gBAAA,CAAA,EAAA,SAAS,EAAG,IACf,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAoC,SAAA,MAAA,EAAA,EAAA,gBAAA,CAAzB,EAAA,KAAa,EAAA,CAAA,GACxB,EAAA,EAAA,mBAAA,CAA6D,OAA7D,IAAA,EAA6D,EAAA,gBAAA,CAApB,EAAA,UAAU,EAAA,CAAA,qBE32BvD,GAAoC,CACxC,KAAM,CAAE,KAAM,OAAQ,MAAO,OAAQ,KAAM,mBAAoB,EAC/D,KAAM,CAAE,KAAM,OAAQ,MAAO,OAAQ,KAAM,iBAAkB,EAC7D,WAAY,CAAE,KAAM,aAAc,MAAO,aAAc,KAAM,qBAAsB,EACnF,QAAS,CAAE,KAAM,UAAW,MAAO,UAAW,KAAM,4BAA6B,EACjF,QAAS,CAAE,KAAM,UAAW,MAAO,UAAW,KAAM,oBAAqB,EACzE,QAAS,CAAE,KAAM,UAAW,MAAO,QAAS,KAAM,oBAAqB,EACvE,QAAS,CAAE,KAAM,UAAW,MAAO,UAAW,KAAM,YAAa,EACjE,OAAQ,CAAE,KAAM,SAAU,MAAO,SAAU,KAAM,kBAAmB,EACpE,SAAU,CAAE,KAAM,WAAY,MAAO,YAAa,KAAM,uBAAwB,CAClF,EAGM,GAAwC,CAC5C,KAAM,OACN,KAAM,OACN,WAAY,OACZ,IAAK,OACL,KAAM,OACN,SAAU,OACV,UAAW,aACX,QAAS,UACT,KAAM,UACN,UAAW,UACX,MAAO,UACP,QAAS,UACT,OAAQ,UACR,MAAO,UACP,OAAQ,UACR,QAAS,UACT,KAAM,UACN,MAAO,UACP,QAAS,UACT,QAAS,SACT,SAAU,SACV,KAAM,SACN,SAAU,WACV,KAAM,WACN,OAAQ,UACV,EAGA,SAAS,GAAW,EAAsB,CACxC,OAAO,EACJ,KAAK,CAAC,CACN,YAAY,CAAC,CACb,UAAU,KAAK,CAAC,CAChB,QAAQ,mBAAoB,EAAE,CACnC,CAEA,SAAS,GAAe,EAAgC,CACtD,IAAM,EAAQ,GAAW,CAAI,EAC7B,OAAO,GAAO,IAAU,GAAO,GAAc,KAAW,IAC1D,CAEA,IAAM,GAAU,IAAI,EAAA,QAAK,CAAC,CAAC,MAAM,WAEjC,SAAS,GAAY,EAAiB,EAAyB,CAC7D,IAAM,EAAQ,GAAQ,KAAK,GAAK,EAAK,MACrC,MACE,kCAAkC,EAAK,KAAK,wCACL,EAAK,KAAK,2BAA2B,GAAQ,CAAK,EAAE,iCAG/F,CAEA,IAAM,GAAgB,eAShB,GAAoE,CACxE,SAAY,OAAO,mCACnB,WAAc,OAAO,qCACrB,eAAkB,OAAO,yCACzB,eAAkB,OAAO,yCACzB,SAAY,OAAO,mCACnB,SAAY,OAAO,mCACnB,QAAW,OAAO,kCAClB,QAAW,OAAO,kCAClB,QAAW,OAAO,kCAClB,SAAY,OAAO,mCACnB,QAAW,OAAO,kCAClB,aAAgB,OAAO,uCACvB,eAAkB,OAAO,wCAC3B,EAEM,GAA6C,CACjD,GAAI,OACJ,MAAO,OACP,IAAK,OACL,QAAS,OACT,SAAU,OACV,GAAI,aACJ,IAAK,aACL,IAAK,aACL,GAAI,aACJ,IAAK,aACL,GAAI,SACJ,MAAO,OACP,MAAO,OACP,OAAQ,MACR,IAAK,MACL,KAAM,MACN,IAAK,MACL,IAAK,OACL,KAAM,MACN,IAAK,MACL,IAAK,MACL,IAAK,MACL,GAAI,WACJ,KAAM,MACN,SAAU,MACV,WAAY,MACZ,OAAQ,YACV,EAEI,GAA8B,KAC9B,GAAmC,KAGvC,SAAgB,IAA4B,CAC1C,OAAO,KAAW,IACpB,CAMA,eAAsB,IAAgC,CACpD,GAAI,IAAU,GAAY,OAAO,IAAc,IAAA,GAC/C,IAAc,SAAY,CACxB,IAAM,GAAU,MAAM,OAAO,yBAAA,CAA0B,QACvD,MAAM,QAAQ,IACZ,OAAO,QAAQ,EAAY,CAAC,CAAC,IAAI,MAAO,CAAC,EAAM,KAAc,CAC3D,IAAM,EAAS,MAAM,EAAS,EAC9B,EAAO,iBAAiB,EAAM,EAAO,OAAgB,CACvD,CAAC,CACH,EACA,IAAU,EAAQ,IAAc,EAAO,UAAU,EAAQ,CAAE,SAAU,CAAU,CAAC,CAAC,CAAC,KACpF,EAAA,CAAG,EACH,MAAM,EACR,CAEA,SAAS,GAAoB,EAAsB,CACjD,IAAM,EAAQ,GAAW,CAAI,EAC7B,OAAO,GAAmB,IAAU,CACtC,CAOA,SAAS,GAAK,EAAuB,CACnC,OACE,GAAW,CAAK,CAAC,CACd,QAAQ,YAAa,EAAE,CAAC,CACxB,KAAK,CAAC,CACN,QAAQ,OAAQ,GAAG,GAAK,OAE/B,CAEA,IAAM,GAAkB,IAAI,IAAI,CAC9B,WACA,UACA,SACA,QACA,QACA,QACA,OACA,OACA,MACA,KACF,CAAC,EAEK,GAAe,uBAErB,SAAS,IAA4B,CACnC,IAAM,EAAK,IAAI,EAAA,QAAK,CAAE,KAAM,GAAO,QAAS,GAAM,YAAa,EAAM,CAAC,EAyBtE,OAvBA,EAAG,IAAI,EAAA,OAAI,CAAC,CAAC,IAAI,EAAA,OAAO,CAAC,CAAC,IAAI,EAAA,OAAQ,CAAC,CAAC,IAAI,EAAA,OAAG,CAAC,CAAC,IAAI,EAAA,OAAI,CAAC,CAAC,IAAI,EAAA,OAAG,CAAC,CAAC,IAAI,EAAA,OAAG,EAE3E,EAAG,IAAI,EAAA,QAAQ,CACb,MAAO,CAAC,EAAG,EAAG,CAAC,EACf,QAAS,GAGT,UAAW,EAAA,QAAO,UAAU,iBAAiB,CAC3C,OAAQ,GACR,MAAO,YACP,UAAW,QACX,WAAY,EACd,CAAC,CACH,CAAC,EAED,GAAe,CAAE,EACjB,GAAkB,CAAE,EACpB,GAAe,CAAE,EACjB,GAAc,CAAE,EAChB,GAAe,CAAE,EACjB,GAAa,CAAE,EACf,GAAe,CAAE,EAEV,CACT,CAGA,SAAS,GAAe,EAAsB,CAC5C,EAAG,KAAK,MAAM,MAAM,QAAS,YAAc,GAAqB,CAC9D,IAAM,EAAS,EAAM,OACrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAO,EAAO,GACpB,GAAI,EAAK,OAAS,kBAAmB,SAErC,IAAM,EAAY,EAAO,EAAI,GACvB,EAAS,EAAO,EAAI,GAC1B,GAAI,GAAW,OAAS,kBAAoB,GAAQ,OAAS,SAAU,SAEvE,IAAM,EAAW,oCAAoC,KAAK,EAAO,OAAO,EACxE,GAAI,CAAC,EAAU,SAEf,IAAM,EAAO,GAAe,EAAS,EAAE,EACvC,GAAI,CAAC,EAAM,SAEX,IAAM,EAAQ,GAAoB,EAAQ,CAAC,EACtC,IAEL,EAAO,QAAU,EAAO,QAAQ,MAAM,EAAS,EAAE,CAAC,MAAM,EACxD,EAAK,KAAO,iBACZ,EAAK,IAAM,MACX,EAAK,KAAO,CAAE,OAAM,OAAQ,EAAS,EAAG,EACxC,EAAM,KAAO,kBACb,EAAM,IAAM,MAGP,EAAO,QAAQ,KAAK,GACvB,EAAO,OAAO,EAAI,EAAG,CAAC,EAE1B,CACA,MAAO,EACT,CAAC,EAED,EAAG,SAAS,MAAM,gBAAkB,EAAQ,IAAQ,CAClD,IAAM,EAAO,EAAO,EAAI,CAAC,KACzB,OAAO,GAAY,EAAK,KAAM,EAAK,MAAM,CAC3C,EACA,EAAG,SAAS,MAAM,oBAAwB,EAC5C,CAGA,SAAS,GAAoB,EAAiB,EAA8B,CAC1E,IAAI,EAAe,EACnB,IAAK,IAAI,EAAI,EAAQ,EAAI,EAAO,OAAQ,GAAK,EAAG,CAC9C,IAAM,EAAQ,EAAO,GACrB,GAAI,EAAM,OAAS,kBAAmB,GAAgB,OACjD,GAAI,EAAM,OAAS,qBACtB,IACI,IAAiB,GAAG,OAAO,CAEnC,CACA,OAAO,IACT,CAGA,SAAS,GAAkB,EAAsB,CAG/C,IAAM,EAAkB,CAAC,EAEzB,EAAG,IAAI,GAAA,QAAW,KAAM,CACtB,SAAW,GAAmB,CAC5B,IAAM,EAAS,GAAa,KAAK,CAAM,EACvC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAQ,GAAW,EAAO,EAAE,EAClC,OAAO,GAAgB,IAAI,CAAK,GAAK,GAAe,CAAK,IAAM,IACjE,EACA,QAAS,EAAiB,IAAgB,CACxC,IAAM,EAAQ,EAAO,GACrB,GAAI,EAAM,UAAY,EAAG,CACvB,IAAM,EAAQ,EAAM,IAAI,EAIxB,OAHI,IAAU,WAAmB;EAC7B,IAAU,MAAc;EACxB,IAAU,SAAiB,GAAG,GAAc,IACzC;CACT,CAEA,IAAM,EAAS,GAAa,KAAK,EAAM,IAAI,GAAK,CAAC,GAAI,GAAI,EAAE,EACrD,EAAQ,GAAW,EAAO,EAAE,EAC5B,EAAS,EAAO,EAAE,EAAE,KAAK,EAE/B,GAAI,IAAU,YAAc,IAAU,UAEpC,OADA,EAAM,KAAK,UAAU,EACd,wCAAwC,GAAQ,GAAU,UAAU,EAAE,0CAE/E,GAAI,IAAU,UAAY,IAAU,QAElC,OADA,EAAM,KAAK,OAAO,EACX,yBAET,GAAI,IAAU,SAAW,IAAU,QAEjC,OADA,EAAM,KAAK,OAAO,EACX,yBAET,GAAI,IAAU,QAAU,IAAU,OAEhC,OADA,EAAM,KAAK,OAAO,EACX,qCAET,GAAI,IAAU,OAAS,IAAU,MAE/B,OADA,EAAM,KAAK,KAAK,EACT,gDAAgD,GAAQ,GAAU,KAAK,EAAE,IAGlF,IAAM,EAAO,GAAe,CAAK,EAOjC,OANI,GACF,EAAM,KAAK,QAAQ,EACZ,GAAY,EAAM,CAAM,IAGjC,EAAM,KAAK,OAAO,EACX,QACT,CACF,CAAC,CACH,CAGA,SAAS,GAAe,EAAsB,CAC5C,EAAG,KAAK,MAAM,MAAM,SAAU,aAAe,GAAqB,CAChE,IAAM,EAAS,EAAM,OACrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAS,EAAO,GAEtB,GADI,EAAO,OAAS,UAAY,CAAC,EAAO,UAAU,QAC9C,EAAO,EAAI,EAAE,CAAC,OAAS,kBAAoB,EAAO,EAAI,EAAE,CAAC,OAAS,iBACpE,SACF,IAAM,EAAW,EAAO,SAAS,GACjC,GAAI,EAAS,OAAS,OAAQ,SAC9B,IAAM,EAAU,kBAAkB,KAAK,EAAS,OAAO,EACvD,GAAI,CAAC,EAAS,SAEd,EAAS,QAAU,EAAS,QAAQ,MAAM,EAAQ,EAAE,CAAC,MAAM,EAC3D,IAAM,EAAW,IAAI,EAAM,MAAM,cAAe,GAAI,CAAC,EACrD,EAAS,QAAU,yDAAyD,EAAQ,KAAO,IAAM,GAAK,WAAW,IACjH,EAAO,SAAS,QAAQ,CAAQ,EAChC,EAAO,EAAI,EAAE,CAAC,SAAS,QAAS,SAAS,CAC3C,CACA,MAAO,EACT,CAAC,CACH,CAGA,SAAS,GAAc,EAAsB,CAC3C,EAAG,SAAS,MAAM,OAAS,EAAQ,IAAQ,CACzC,IAAM,EAAQ,EAAO,GACf,EAAO,EAAM,KAAK,KAAK,EACvB,EAAW,EAAK,MAAM,KAAK,CAAC,CAAC,IAAM,GACnC,EAAY,GAAoB,CAAQ,EACxC,EAAS,kBAAkB,KAAK,CAAI,CAAC,GAAG,GAE9C,GAAI,IAAc,UAChB,MACE,6EACkC,GAAQ,EAAM,OAAO,EAAE,SACxD,EAAS,eAAe,GAAQ,CAAM,EAAE,eAAiB,IAC1D;EAIJ,IAAM,EACJ,IAAU,GAAa,GAAa,GAAO,EAAM,QAAS,CAAS,EAAI,GAAQ,EAAM,OAAO,EAU9F,MACE,qCARA,GAAU,EACN,0DACgC,GAAQ,GAAU,EAAE,EAAE,qCACvB,GAAQ,CAAQ,EAAE,eAEjD,GAAA,uDAIoD,GAAQ,CAAS,EAAE,IAAI,EAAS,sBAG5F,CACF,CAGA,SAAS,GAAe,EAAsB,CAC5C,IAAM,EAAW,EAAG,SAAS,MAAM,WACnC,EAAG,SAAS,MAAM,YAAc,EAAQ,EAAK,EAAS,EAAK,IACzD,yBAAyB,EAAW,EAAS,EAAQ,EAAK,EAAS,EAAK,CAAI,EAAI,EAAK,YAAY,EAAQ,EAAK,CAAO,IACvH,EAAG,SAAS,MAAM,aAAe,EAAQ,EAAK,EAAS,EAAK,IAC1D,GAAG,EAAK,YAAY,EAAQ,EAAK,CAAO,EAAE,OAC9C,CAGA,SAAS,GAAa,EAAsB,CAC1C,IAAM,EAAW,EAAG,SAAS,MAAM,UACnC,EAAG,SAAS,MAAM,WAAa,EAAQ,EAAK,EAAS,EAAK,IAAS,CACjE,IAAM,EAAO,OAAO,EAAO,EAAI,CAAC,QAAQ,MAAM,GAAK,EAAE,EAMrD,MALI,gBAAgB,KAAK,CAAI,IAC3B,EAAO,EAAI,CAAC,QAAQ,SAAU,QAAQ,EACtC,EAAO,EAAI,CAAC,QAAQ,MAAO,qBAAqB,EAChD,EAAO,EAAI,CAAC,SAAS,QAAS,iBAAiB,GAE1C,EACH,EAAS,EAAQ,EAAK,EAAS,EAAK,CAAI,EACxC,EAAK,YAAY,EAAQ,EAAK,CAAO,CAC3C,CACF,CAGA,SAAS,GAAe,EAAsB,CAC5C,EAAG,KAAK,MAAM,MAAM,SAAU,aAAe,GAAqB,CAChE,IAAM,EAAS,EAAM,OACrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAS,EAAG,GAAK,EAAG,CAC7C,IAAM,EAAS,EAAO,GACtB,GAAI,EAAO,OAAS,UAAY,EAAO,EAAI,EAAE,CAAC,OAAS,iBAAkB,SACzE,IAAM,GAAU,EAAO,UAAY,CAAC,EAAA,CAAG,OAAQ,GAAU,EAAM,OAAS,WAAW,EAC/E,EAAO,SAAW,GAAK,EAAO,EAAE,CAAC,OAAS,UAE9C,EAAO,EAAI,EAAE,CAAC,IAAM,SACpB,EAAO,EAAI,EAAE,CAAC,SAAS,QAAS,WAAW,EAC3C,EAAO,EAAI,EAAE,CAAC,IAAM,SACpB,EAAO,EAAE,CAAC,KAAO,CAAE,OAAQ,EAAK,EAClC,CACA,MAAO,EACT,CAAC,EAED,IAAM,EAAW,EAAG,SAAS,MAAM,MACnC,EAAG,SAAS,MAAM,OAAS,EAAQ,EAAK,EAAS,EAAK,IAAS,CAC7D,IAAM,EAAO,EACT,EAAS,EAAQ,EAAK,EAAS,EAAK,CAAI,EACxC,EAAK,YAAY,EAAQ,EAAK,CAAO,EAEzC,GAAI,CADS,EAAO,EAAI,CAAC,MACd,OAAQ,OAAO,EAC1B,IAAM,EAAU,EAAO,EAAI,CAAC,SAAS,KAAK,EAC1C,OAAO,EAAU,GAAG,EAAK,cAAc,GAAQ,CAAO,EAAE,eAAiB,CAC3E,CACF,CAMA,IAAI,GAA+B,KAEnC,SAAS,IAA4B,CAEnC,MADA,CAAgB,KAAY,GAAc,EACnC,EACT,CAGA,SAAS,GAAc,EAAuB,CAC5C,OAAQ,EAAO,UAAY,CAAC,EAAA,CACzB,OAAQ,GAAU,EAAM,OAAS,QAAU,EAAM,OAAS,aAAa,CAAC,CACxE,IAAK,GAAU,EAAM,OAAO,CAAC,CAC7B,KAAK,EAAE,CAAC,CACR,KAAK,CACV,CAEA,SAAS,GAAe,EAAoC,CAC1D,IAAM,EAA6B,CAAC,EACpC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAQ,EAAO,GACrB,GAAI,EAAM,OAAS,eAAgB,SACnC,IAAM,EAAQ,OAAO,EAAM,IAAI,MAAM,CAAC,CAAC,EACvC,GAAI,EAAQ,GAAK,EAAQ,EAAG,SAC5B,IAAM,EAAK,OAAO,EAAM,QAAQ,IAAI,GAAK,EAAE,EACrC,EAAS,EAAO,EAAI,GACtB,CAAC,GAAM,GAAQ,OAAS,UAC5B,EAAQ,KAAK,CAAE,KAAI,KAAM,GAAc,CAAM,EAAG,MAAO,CAAM,CAAC,CAChE,CACA,OAAO,CACT,CAGA,SAAgB,GAAkB,EAAsB,CACtD,OAAO,EAAA,QAAU,SAAS,EAAM,CAC9B,SAAU,CAAC,SAAU,OAAQ,UAAW,UAAU,CACpD,CAAC,CACH,CAQA,SAAgB,GAAe,EAAsC,CACnE,IAAM,EAAK,GAAc,EACnB,EAA+B,CAAC,EAChC,EAAS,EAAG,MAAM,GAAU,GAAI,CAAG,EACzC,MAAO,CACL,KAAM,GAAkB,EAAG,SAAS,OAAO,EAAQ,EAAG,QAAS,CAAG,CAAC,EACnE,SAAU,GAAe,CAAM,CACjC,CACF,CCjhBA,SAAS,GAAc,EAAyB,CAC9C,EAAK,iBAA8B,gBAAgB,CAAC,CAAC,QAAS,GAAU,CACtE,GAAI,EAAM,QAAQ,SAAU,OAC5B,EAAM,QAAQ,SAAW,IAEzB,IAAM,EAAQ,SAAS,cAAc,QAAQ,EAC7C,EAAM,KAAO,SACb,EAAM,UAAY,kBAClB,EAAM,aAAa,aAAc,eAAe,EAChD,EAAM,MAAQ,gBACd,EAAM,UAAY,gDAClB,EAAM,iBAAiB,YAAe,CACpC,IAAM,EAAS,EAAM,cAAc,MAAM,CAAC,EAAE,aAAe,GAC3D,UAAe,WAAW,UAAU,CAAM,CAAC,CAAC,SAAW,CACrD,EAAM,UAAU,IAAI,wBAAwB,EAC5C,EAAM,UAAY,iDAClB,OAAO,eAAiB,CACtB,EAAM,UAAU,OAAO,wBAAwB,EAC/C,EAAM,UAAY,+CACpB,EAAG,IAAI,CACT,CAAC,CACH,CAAC,EACD,EAAM,YAAY,CAAK,CACzB,CAAC,CACH,CAWA,SAAS,GAAkB,EAAyB,CAClD,IAAM,EAAS,MAAM,KAAK,EAAK,iBAA8B,iBAAiB,CAAC,CAAC,CAAC,OAC9E,GAAW,CAAC,EAAO,eAAe,aAAa,cAAc,CAChE,EACM,EAAS,IAAI,IAEnB,EAAO,QAAS,GAAa,CAC3B,GAAI,EAAO,IAAI,CAAQ,EAAG,OAC1B,IAAM,EAAY,CAAC,CAAQ,EACvB,EAAU,EAAS,mBACvB,KAAO,aAAmB,aAAe,EAAQ,UAAU,SAAS,gBAAgB,GAClF,EAAU,KAAK,CAAO,EACtB,EAAU,EAAQ,mBAEpB,EAAU,QAAS,GAAW,EAAO,IAAI,CAAM,CAAC,EAEhD,IAAM,EAAQ,SAAS,cAAc,KAAK,EAC1C,EAAM,UAAY,UAClB,EAAM,aAAa,eAAgB,EAAE,EACrC,EAAS,OAAO,CAAK,EACrB,EAAU,QAAS,GAAW,EAAM,YAAY,CAAM,CAAC,CACzD,CAAC,CACH,CAEA,SAAS,GAAY,EAAyB,CAC5C,GAAkB,CAAI,EACtB,EAAK,iBAA8B,gBAAgB,CAAC,CAAC,SAAS,EAAO,IAAgB,CACnF,GAAI,EAAM,QAAQ,SAAU,OAC5B,IAAM,EAAU,MAAM,KAAK,EAAM,iBAA8B,0BAA0B,CAAC,EAC1F,GAAI,CAAC,EAAQ,OAAQ,OACrB,EAAM,QAAQ,SAAW,IAEzB,IAAM,EAAQ,SAAS,cAAc,KAAK,EAC1C,EAAM,UAAY,eAClB,EAAM,aAAa,OAAQ,SAAS,EAEpC,IAAM,EAAS,EAAQ,KAAK,EAAQ,IAAW,CAC7C,IAAM,EAAK,UAAU,EAAY,GAAG,IACpC,EAAO,GAAK,GAAG,EAAG,SAClB,EAAO,aAAa,OAAQ,UAAU,EACtC,EAAO,aAAa,kBAAmB,CAAE,EAEzC,IAAM,EAAQ,SAAS,cAAc,QAAQ,EAS7C,MARA,GAAM,KAAO,SACb,EAAM,GAAK,EACX,EAAM,UAAY,iBAClB,EAAM,aAAa,OAAQ,KAAK,EAChC,EAAM,aAAa,gBAAiB,EAAO,EAAE,EAC7C,EAAM,YAAc,EAAO,QAAQ,OAAS,OAAO,EAAS,IAC5D,EAAM,iBAAiB,YAAe,EAAW,CAAM,CAAC,EACxD,EAAM,YAAY,CAAK,EAChB,CACT,CAAC,EAED,SAAS,EAAW,EAAqB,CACvC,EAAO,SAAS,EAAO,IAAW,CAChC,IAAM,EAAc,IAAW,EAC/B,EAAM,aAAa,gBAAiB,OAAO,CAAW,CAAC,EACvD,EAAM,SAAW,EAAc,EAAI,GACnC,EAAQ,EAAO,CAAC,OAAS,CAAC,CAC5B,CAAC,CACH,CAEA,EAAM,QAAQ,CAAK,EACnB,EAAW,CAAC,CACd,CAAC,CACH,CAMA,IAAI,GAAmE,KACnE,GAAY,EAEhB,eAAe,IAAkB,CAI/B,MAHA,CACE,KAAiB,OAAO,UAAU,CAAC,KAAM,GAAW,EAAO,OAAO,EAE7D,EACT,CAGA,IAAM,GAAiB,IAAI,IAAI,CAC7B,SACA,SACA,SACA,QACA,OACA,OACA,OACA,OACA,QACA,SACA,QACA,QACA,UACA,KACF,CAAC,EAGD,SAAS,GAAS,EAAyB,CACzC,IAAK,IAAM,KAAY,MAAM,KAAK,EAAS,UAAU,EAAG,CACtD,IAAM,EAAO,EAAS,KAAK,YAAY,EACjC,EAAQ,EAAS,MAAM,KAAK,CAAC,CAAC,YAAY,EAC1C,EAAS,IAAS,QAAU,IAAS,cAAgB,IAAS,OAChE,EAAK,WAAW,IAAI,GAAM,GAAU,+BAA+B,KAAK,CAAK,IAC/E,EAAS,gBAAgB,EAAS,IAAI,CAE1C,CACA,IAAK,IAAM,KAAS,MAAM,KAAK,EAAS,QAAQ,EAC1C,GAAe,IAAI,EAAM,UAAU,YAAY,CAAC,EAAG,EAAM,OAAO,EAC/D,GAAS,CAAK,CAEvB,CAgBA,SAAS,GAAa,EAA6B,CACjD,IAAM,EAAY,IAAI,UAAU,CAAC,CAAC,gBAAgB,EAAK,eAAe,EACtE,GAAI,EAAU,cAAc,aAAa,EAAG,OAAO,KACnD,IAAM,EAAO,EAAU,gBAGvB,OAFI,EAAK,UAAU,YAAY,IAAM,OACrC,GAAS,CAAI,EACN,GAF4C,IAGrD,CAGA,SAAS,GAAM,EAA6B,EAAc,EAAwB,CAChF,OAAO,EAAO,iBAAiB,CAAI,CAAC,CAAC,KAAK,GAAK,CACjD,CAEA,SAAS,GAAkB,EAA4D,CACrF,IAAM,EAAS,iBAAiB,SAAS,eAAe,EAClD,EAAa,GAAM,EAAQ,YAAa,SAAS,EACjD,EAAQ,GAAM,EAAQ,OAAQ,SAAS,EACvC,EAAW,GAAM,EAAQ,YAAa,SAAS,EAC/C,EAAQ,GAAM,EAAQ,iBAAkB,SAAS,EACjD,EAAQ,GAAM,EAAQ,kBAAmB,SAAS,EAExD,EAAQ,WAAW,CACjB,YAAa,GACb,cAAe,SACf,MAAO,OACP,WAAY,GAAM,EAAQ,cAAe,SAAS,EAClD,UAAW,CAAE,YAAa,EAAK,EAC/B,eAAgB,CACd,WAAY,EACZ,aAAc,EACd,iBAAkB,EAClB,mBAAoB,EACpB,eAAgB,GAAM,EAAQ,cAAe,SAAS,EACtD,cAAe,GAAM,EAAQ,cAAe,SAAS,EACrD,UAAW,EACX,UAAW,EACX,QAAS,EACT,WAAY,EACZ,WAAY,GAAM,EAAQ,cAAe,SAAS,EAClD,cAAe,EACf,SAAU,MACZ,CACF,CAAC,CACH,CAMA,eAAsB,GAAoB,EAAmB,EAAS,GAAsB,CAC1F,IAAM,EAAQ,MAAM,KAAK,EAAK,iBAA8B,mBAAmB,CAAC,CAAC,CAAC,OAC/E,GAAW,GAAU,CAAC,EAAO,QAAQ,QACxC,EACA,GAAI,CAAC,EAAM,OAAQ,OAEnB,IAAM,EAAU,MAAM,GAAgB,EACtC,GAAkB,CAAO,EAEzB,IAAK,IAAM,KAAU,EAAO,CAC1B,IAAM,EAAQ,EAAO,cAA2B,oBAAoB,CAAC,EAAE,aAAe,GACjF,KAAM,KAAK,EAChB,GAAO,cAAc,kBAAkB,CAAC,EAAE,OAAO,EACjD,GAAI,CACF,IAAa,EACb,GAAM,CAAE,OAAQ,MAAM,EAAQ,OAAO,eAAe,KAAa,CAAK,EAChE,EAAQ,GAAa,CAAG,EAC9B,GAAI,CAAC,EAAO,CACV,EAAO,QAAQ,SAAW,OAC1B,QACF,CACA,IAAM,EAAQ,SAAS,cAAc,KAAK,EAC1C,EAAM,UAAY,kBAClB,EAAM,YAAY,SAAS,WAAW,EAAO,EAAI,CAAC,EAClD,EAAO,QAAQ,CAAK,EACpB,EAAO,QAAQ,SAAW,IAC5B,MAAQ,CAEN,EAAO,QAAQ,SAAW,MAC5B,CAjBiD,CAkBnD,CACF,CAOA,eAAsB,GAAgB,EAAyC,CACxE,IACL,GAAc,CAAI,EAClB,GAAY,CAAI,EAChB,MAAM,GAAoB,CAAI,EAChC,2NC9PA,IAAM,EAAQ,EAYR,EAAO,EAKP,GAAA,EAAO,EAAA,IAAA,CAAwB,IAAI,EAEnC,GAAA,EAAe,EAAA,IAAA,CAAI,GAAiB,CAAC,EAErC,GAAA,EAAY,EAAA,SAAA,MAEhB,EAAkB,MACX,GAAe,EAAM,QAAU,EAAE,EACzC,EAEK,GAAA,EAAO,EAAA,SAAA,KAAe,EAAU,MAAM,IAAI,EAC1C,GAAA,EAAU,EAAA,SAAA,KAAe,CAAC,EAAM,QAAU,CAAC,EAAM,OAAO,KAAK,CAAC,GAEpE,EAAA,EAAA,MAAA,CAAM,EAAY,GAAU,EAAK,WAAY,EAAM,QAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,GACjF,EAAA,EAAA,MAAA,CAAM,MAAY,KAAA,EAAK,EAAA,SAAA,KAAe,GAAgB,EAAK,KAAK,CAAC,CAAC,EAGlE,IAAI,EAAsC,KAE1C,SAAS,GAAqB,CACxB,OAAO,iBAAqB,MAChC,EAAa,IAAI,qBAAuB,KAAK,GAAoB,EAAK,MAAQ,EAAI,CAAC,EACnF,EAAW,QAAQ,SAAS,gBAAiB,CAAE,WAAY,GAAM,gBAAiB,CAAC,OAAO,CAAE,CAAC,EAC/F,QAEA,EAAA,EAAA,UAAA,CAAU,SAAY,CACpB,MAAA,EAAM,EAAA,SAAA,CAAS,EACf,MAAM,GAAgB,EAAK,KAAK,EAChC,EAAa,EACb,AAEE,EAAa,SADb,MAAM,GAAe,EACA,GAEzB,CAAC,GAED,EAAA,EAAA,gBAAA,KAAsB,GAAY,WAAW,CAAC,SAIjC,EAAA,QAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAwF,MAAxF,IAAA,EAAwF,EAAA,gBAAA,CAAlB,EAAA,SAAS,EAAA,CAAA,KAAA,EAE/E,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA+D,MAAA,OAA/C,QAAA,OAAJ,IAAI,EAAO,MAAM,kBAAkB,UAAQ,EAAA,qhBEhEzD,IAAM,EAAQ,EAyBR,EAAO,EAIP,GAAA,EAAc,EAAA,IAAA,CAAgC,IAAI,EAClD,GAAA,EAAa,EAAA,IAAA,CAAI,EAAK,EAEtB,GAAA,EAAU,EAAA,SAAA,KAAe,EAAM,cAAgB,OAAO,EACtD,GAAA,EAAe,EAAA,SAAA,KAAe,EAAQ,OAAS,CAAC,EAAW,KAAK,EAChE,GAAA,EAAc,EAAA,SAAA,KAAe,EAAQ,OAAS,EAAW,KAAK,EAEpE,SAAS,EAAQ,EAAc,CAC7B,EAAK,oBAAsB,EAAM,OAA+B,KAAK,CACvE,CAGA,SAAS,EAAM,EAAc,EAAwB,EAAsB,CACzE,EAAK,oBAAqB,CAAI,GAC9B,EAAA,EAAA,SAAA,KAAe,CACb,IAAM,EAAK,EAAY,MAClB,IACL,EAAG,MAAM,EACT,EAAG,kBAAkB,EAAgB,CAAY,EACnD,CAAC,CACH,CAGA,SAAS,EAAS,EAAgB,EAAQ,EAAQ,EAAc,QAAS,CACvE,IAAM,EAAK,EAAY,MACvB,GAAI,CAAC,GAAM,EAAM,SAAU,OAC3B,IAAM,EAAQ,EAAM,YAAc,GAC5B,EAAQ,EAAG,eACX,EAAM,EAAG,aACT,EAAW,EAAM,MAAM,EAAO,CAAG,GAAK,EAE5C,EADa,EAAM,MAAM,EAAG,CAAK,EAAI,EAAS,EAAW,EAAQ,EAAM,MAAM,CAAG,EACpE,EAAQ,EAAO,OAAQ,EAAQ,EAAO,OAAS,EAAS,MAAM,CAC5E,CAGA,SAAS,EAAY,EAA8C,CACjE,IAAM,EAAK,EAAY,MACvB,GAAI,CAAC,GAAM,EAAM,SAAU,OAC3B,IAAM,EAAQ,EAAM,YAAc,GAC5B,EAAQ,EAAG,eACX,EAAM,EAAG,aACT,EAAY,EAAM,YAAY;EAAM,EAAQ,CAAC,EAAI,EACjD,EAAa,EAAM,QAAQ;EAAM,CAAG,EACpC,EAAU,IAAe,GAAK,EAAM,OAAS,EAE7C,EADQ,EAAM,MAAM,EAAW,CACpB,CAAA,CACd,MAAM;CAAI,CAAA,CACV,KAAK,EAAM,KAAO,OAAO,GAAW,WAAa,EAAO,CAAC,EAAI,GAAU,CAAI,CAAA,CAC3E,KAAK;CAAI,EAEZ,EADa,EAAM,MAAM,EAAG,CAAS,EAAI,EAAW,EAAM,MAAM,CAAO,EAC3D,EAAW,EAAY,EAAS,MAAM,CACpD,CAEA,SAAS,GAAa,CACpB,EAAS,IAAI,CACf,CAEA,SAAS,GAAe,CACtB,EAAS,GAAG,CACd,CAEA,SAAS,GAAgB,CACvB,EAAY,KAAK,CACnB,CAEA,SAAS,GAAa,CACpB,EAAY,IAAI,CAClB,CAEA,SAAS,GAAiB,CACxB,EAAY,QAAQ,CACtB,CAEA,SAAS,GAAa,CACpB,IAAM,EAAK,EAAY,MACvB,GAAI,CAAC,GAAM,EAAM,SAAU,OAC3B,IAAM,EAAQ,EAAM,YAAc,GAC5B,EAAQ,EAAG,eACX,EAAM,EAAG,aACT,EAAW,EAAM,MAAM,EAAO,CAAG,GAAK,QACtC,EAAO,GAAG,EAAM,MAAM,EAAG,CAAK,EAAE,GAAG,EAAS,QAAQ,EAAM,MAAM,CAAG,IAEnE,EAAW,EAAQ,EAAS,OAAS,EAC3C,EAAM,EAAM,EAAU,EAAW,CAAC,CACpC,CAEA,SAAS,GAAa,CACpB,IAAM,EAAK,EAAY,MACnB,CAAC,GAAM,EAAM,YACC,EAAM,YAAc,GAAA,CAAI,MAAM,EAAG,eAAgB,EAAG,YAClE,CAAA,CAAS,SAAS;CAAI,EAAG,EAAS,QAAS,QAAS,QAAQ,EAC3D,EAAS,IAAK,IAAK,QAAQ,EAClC,CAMA,SAAS,EAAa,EAAgB,EAAiB,CACrD,IAAM,EAAK,EAAY,MACvB,GAAI,CAAC,GAAM,EAAM,SAAU,OAC3B,IAAM,EAAQ,EAAM,YAAc,GAC5B,EAAQ,EAAG,eACX,EAAM,EAAG,aACT,EAAO,EAAM,MAAM,EAAO,CAAG,GAAK,EAClC,EAAQ,EAAO,QAAQ,IAAK,CAAI,EAChC,EAAQ,EAAM,MAAM,EAAG,CAAK,EAE5B,EAAU,CAAC,GAAS,EAAM,SAAS;;CAAM,EAAI,GAAK,EAAM,SAAS;CAAI,EAAI;EAAO;;EAChF,EAAO,GAAG,IAAQ,IAAU,EAAM,IAAI,EAAM,MAAM,CAAG,IACrD,EAAU,EAAM,OAAS,EAAQ,OAAS,EAAM,QAAQ,CAAI,EAClE,EAAM,EAAM,EAAS,EAAU,EAAK,MAAM,CAC5C,CAEA,SAAS,GAAc,CACrB,EAAa;KAAkB,yBAAyB,CAC1D,CAEA,SAAS,GAAc,CACrB,EAAa;;;KAA2C,gBAAgB,CAC1E,CAEA,SAAS,GAAc,CACrB,EAAa;;eAAqD,OAAO,CAC3E,CAEA,SAAS,GAAkB,CACzB,EAAS,KAAM,KAAM,UAAU,CACjC,CAEA,SAAS,GAAgB,CACvB,EAAW,MAAQ,CAAC,EAAW,KACjC,CAEA,IAAM,GAAA,EAAc,EAAA,IAAA,CAAI,EAAK,EAGvB,EAA+C,CACnD,CAAE,QAAS,YAAa,OAAQ,iCAAkC,EAClE,CAAE,QAAS,mBAAoB,OAAQ,oDAAqD,EAC5F,CAAE,QAAS,mBAAoB,OAAQ,uBAAwB,EAC/D,CAAE,QAAS,kBAAmB,OAAQ,kBAAmB,EACzD,CAAE,QAAS,iBAAkB,OAAQ,oCAAqC,EAC1E,CAAE,QAAS,sBAAuB,OAAQ,wBAAyB,EACnE,CAAE,QAAS,eAAgB,OAAQ,eAAgB,EACnD,CAAE,QAAS,eAAgB,OAAQ,WAAY,EAC/C,CAAE,QAAS,+BAAgC,OAAQ,2BAA4B,EAC/E,CAAE,QAAS,aAAc,OAAQ,mBAAoB,CACvD,EAUM,EAAmB,CACvB,CAAE,MAAO,IAAK,MAAO,UAAW,IAAK,CAAW,EAChD,CAAE,MAAO,IAAK,MAAO,UAAW,IAAK,CAAa,EAClD,CAAE,KAAM,gBAAiB,MAAO,cAAe,IAAK,CAAc,EAClE,CAAE,KAAM,aAAc,MAAO,QAAS,IAAK,CAAW,EACtD,CAAE,KAAM,qBAAsB,MAAO,mBAAoB,IAAK,CAAe,EAC7E,CAAE,KAAM,aAAc,MAAO,OAAQ,IAAK,CAAW,EACrD,CAAE,KAAM,aAAc,MAAO,SAAU,IAAK,CAAW,EACvD,CAAE,KAAM,oBAAqB,MAAO,WAAY,IAAK,CAAY,EACjE,CAAE,KAAM,0BAA2B,MAAO,SAAU,IAAK,CAAY,EACrE,CAAE,KAAM,cAAe,MAAO,SAAU,IAAK,CAAY,EACzD,CAAE,KAAM,eAAgB,MAAO,eAAgB,IAAK,CAAgB,CACtE,iBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA6EM,MAAA,CA7ED,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,oBAAmB,CAAA,2BAAuC,EAAA,KAAO,CAAA,CAAA,CAAA,EAAA,EAC1E,EAAA,EAAA,mBAAA,CAiDM,MAjDN,GAiDM,GAhDJ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAsBS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CArBQ,EAAR,IADT,EAAA,EAAA,mBAAA,CAsBS,SAAA,CApBN,IAAK,EAAK,MACX,KAAK,SACL,MAAM,0BACL,aAAY,EAAK,MACjB,MAAO,EAAK,MACZ,SAAU,EAAA,UAAQ,CAAM,EAAA,OAAW,EAAA,MACnC,QAAK,GAAE,EAAK,IAAG,CAEP,EAAA,CAAA,EAAK,OAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,IAAA,OAAvC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAK,IAAI,EAAE,cAAY,MACnD,EAAA,KAAA,CAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAUO,OAAA,OARL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,2BAA0B,CACwB,iCAAA,EAAK,QAAK,IAA0D,mCAAA,EAAK,QAAK,OAItI,cAAY,MAET,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAK,KAAK,EAAA,CAAA,EAAA,EAAA,EAAA,EAAA,SAIjB,EAAA,EAAA,mBAAA,CAUS,SAAA,CATP,KAAK,SACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,yDAAwD,CAAA,kCACjB,EAAA,KAAW,CAAA,CAAA,EACxD,aAAW,qBACX,MAAM,qBACL,eAAc,EAAA,MACd,QAAK,AAAA,EAAA,KAAA,GAAE,EAAA,MAAW,CAAI,EAAA,KAEvB,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAAsD,IAAA,CAAnD,MAAM,wBAAwB,cAAY,0BAItC,EAAA,wCAAA,EADT,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAWS,SAAA,OATP,KAAK,SACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,2DAA0D,CAAA,kCACnB,EAAA,KAAU,CAAA,CAAA,EACvD,aAAW,aACX,MAAM,aACL,eAAc,EAAA,MACd,QAAO,CAER,EAAA,EAAA,EAAA,EAAA,mBAAA,CAA2E,IAAA,CAAvE,OAAA,EAAK,EAAA,eAAA,CAAE,EAAA,MAAU,eAAA,WAAA,EAAiC,cAAY,2BAI5D,EAAA,QAAA,EAAV,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAKK,KALL,GAKK,GAAA,EAJH,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAHe,EAAT,KAAsB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAM,OAAA,EAAA,EAAA,EAC3C,EAAA,mBAAA,CAA4B,KAAA,MAAA,EAAA,EAAA,gBAAA,CAArB,EAAM,OAAO,EAAA,CAAA,GAAA,EACpB,EAAA,mBAAA,CAA2B,KAAA,MAAA,EAAA,EAAA,gBAAA,CAApB,EAAM,MAAM,EAAA,CAAA,CAAA,EAAA,EAAA,6CAIvB,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAjBD,MAAM,0BAA2B,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,UAAI,EAAA,UAAS,UAAE,EAAA,SAAS,CAAA,CAEzD,EAAA,CAAA,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CASE,WAAA,OAPI,QAAA,cAAJ,IAAI,EACJ,MAAM,8BACL,MAAO,EAAA,WACP,SAAU,EAAA,SACV,YAAa,EAAA,YACd,WAAW,QACH,SAGF,EAAA,KAAA,GAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAAA,EAAA,QAAA,EADR,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,CAKE,GAAA,OAHA,MAAM,6BACL,OAAQ,EAAA,WACT,aAAW,yrBEjRnB,IAAM,EAAQ,EAyBR,GAAA,EAAQ,EAAA,IAAA,CAAI,EAAE,EACd,GAAA,EAAQ,EAAA,IAAA,CAAI,EAAE,EAKd,GAAA,EAAS,EAAA,IAAA,CAAmB,IAAI,EAClC,EAA0C,KAGxC,GAAA,EAAS,EAAA,SAAA,KAAwB,CACrC,IAAM,EAAiB,CAAC,EACxB,IAAK,IAAM,KAAU,EAAM,SACrB,EAAO,OAAS,GAAK,CAAC,EAAM,OAAQ,EAAM,KAAK,CAAE,SAAQ,OAAQ,CAAC,CAAE,CAAC,EACpE,EAAM,EAAM,OAAS,EAAE,CAAC,OAAO,KAAK,CAAM,EAEjD,OAAO,CACT,CAAC,EAED,SAAS,EAAW,EAAuB,CACzC,OAAO,EACJ,YAAY,CAAA,CACZ,UAAU,KAAK,CAAA,CACf,QAAQ,mBAAoB,EAAE,CACnC,CAGA,IAAM,GAAA,EAAW,EAAA,SAAA,KAAwB,CACvC,IAAM,EAAQ,EAAW,EAAM,MAAM,KAAK,CAAC,EAE3C,OADK,EACE,EAAO,MACX,IAAK,IAAW,CACf,OAAQ,EAAM,OACd,OAAQ,EAAM,OAAO,OAAQ,GAAU,EAAW,EAAM,IAAI,CAAC,CAAC,SAAS,CAAK,CAAC,CAC/E,EAAE,CAAA,CACD,OAAQ,GAAU,EAAM,OAAO,QAAU,EAAW,EAAM,OAAO,IAAI,CAAC,CAAC,SAAS,CAAK,CAAC,EANtE,EAAO,KAO5B,CAAC,EAEK,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,MAAM,KAAK,CAAC,CAAC,OAAS,CAAC,EAE9D,SAAS,EAAU,EAAuB,CACxC,OAAO,EAAU,OAAS,EAAO,QAAU,EAAM,OAAO,EAC1D,CAEA,SAAS,EAAS,EAAoB,CACpC,EAAO,MAAQ,EAAO,QAAU,EAAM,OAAO,GAAK,KAAO,EAAM,OAAO,EACxE,CAEA,SAAS,GAAiB,CACxB,GAAY,WAAW,EACnB,SAAO,qBAAyB,OACpC,EAAa,IAAI,qBACd,GAAa,CACZ,IAAM,EAAU,EAAS,KAAM,GAAY,EAAQ,cAAc,EAC7D,IAAS,EAAM,MAAQ,EAAQ,OAAO,GAC5C,EAEA,CAAE,WAAY,qBAAsB,UAAW,CAAE,CACnD,EACA,EAAM,SAAS,QAAS,GAAW,CACjC,IAAM,EAAW,SAAS,eAAe,EAAO,EAAE,EAC9C,GAAU,GAAY,QAAQ,CAAQ,CAC5C,CAAC,EACH,CAEA,SAAS,EAAG,EAAkB,CAC5B,IAAM,EAAW,SAAS,eAAe,CAAE,EACtC,IACL,EAAM,MAAQ,EACd,EAAS,eAAe,CAAE,SAAU,SAAU,MAAO,OAAQ,CAAC,EAChE,QAEA,EAAA,EAAA,MAAA,KACQ,EAAM,aACN,KAAA,EAAK,EAAA,SAAA,CAAS,CAAQ,EAC5B,CAAE,UAAW,EAAK,CACpB,GAEA,EAAA,EAAA,gBAAA,KAAsB,GAAY,WAAW,CAAC,SAIjC,EAAA,SAAS,QAAU,EAAA,cAAA,EAA9B,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAsDM,MAtDN,GAsDM,EArDJ,EAAA,EAAA,mBAAA,CAA2C,IAA3C,IAAA,EAA2C,EAAA,gBAAA,CAAZ,EAAA,KAAK,EAAA,CAAA,EAEzB,EAAA,aAAA,EAAX,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAQM,MARN,GAQM,CAPJ,AAAA,EAAA,MAAA,EAAA,EAAA,mBAAA,CAA6C,IAAA,CAA1C,MAAM,eAAe,cAAY,MACpC,EAAA,KAAA,EAAA,GAAA,EAAA,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAKE,QAAA,CAJS,sBAAA,AAAA,EAAA,KAAA,GAAA,EAAK,MAAA,EACd,KAAK,SACL,YAAY,iBACZ,aAAW,gBAHF,EAAA,KAAA,GAAA,EAAA,CAAA,CAAA,EAAA,WAAA,EAAA,KAAK,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAOlB,EAAA,EAAA,mBAAA,CAwCM,MAAA,CAxCD,MAAM,kBAAmB,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,UAAI,EAAA,SAAS,CAAA,CAC9C,EAAA,EAAA,EAAA,EAAA,mBAAA,CAoCK,KApCL,GAoCK,GAAA,EAnCH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAkCK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAlCe,EAAA,MAAT,KAAX,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAkCK,KAAA,CAlC0B,IAAK,EAAM,OAAO,GAAI,MAAM,iBACzD,EAAA,EAAA,EAAA,EAAA,mBAAA,CAqBM,MAAA,CApBJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,oCAAmC,CAAA,wBACN,EAAA,QAAU,EAAM,OAAO,EAAE,CAAA,CAAA,CAAA,EAAA,CAGpD,EAAM,OAAO,SAAA,EADrB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAYS,SAAA,OAVP,KAAK,SACL,MAAM,oBACL,gBAAe,EAAU,CAAK,EAC9B,aAAU,qBAAuB,EAAM,OAAO,OAC9C,QAAK,GAAE,EAAS,CAAK,CAEtB,EAAA,EAAA,EAAA,EAAA,mBAAA,CAGE,IAAA,CAFC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAU,CAAK,EAAA,qBAAA,qBAAA,EACvB,cAAY,MAGhB,EAAA,KAAA,CAAA,CAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAqF,OAArF,EAAqF,IAAA,EACrF,EAAA,mBAAA,CAEI,IAAA,CAFA,KAAI,IAAM,EAAM,OAAO,KAAO,SAAA,EAAK,EAAA,cAAA,CAAA,GAAU,EAAG,EAAM,OAAO,EAAE,EAAA,CAAA,SAAA,CAAA,CAC9D,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAM,OAAO,IAAI,EAAA,EAAA,EAAA,CAAA,EAAA,CAAA,EAId,EAAU,CAAK,IAAA,EAAzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CASK,KATL,GASK,GAAA,EARH,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAOK,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CANa,EAAM,OAAf,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAOK,KAAA,CALF,IAAK,EAAM,GACZ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,oCAAmC,CAAA,wBACN,EAAA,QAAU,EAAM,EAAE,CAAA,CAAA,CAErD,EAAA,EAAA,EAAA,EAAA,mBAAA,CAA4E,IAAA,CAAxE,KAAI,IAAM,EAAM,KAAO,SAAA,EAAK,EAAA,cAAA,CAAA,GAAU,EAAG,EAAM,EAAE,EAAA,CAAA,SAAA,CAAA,CAAM,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAM,IAAI,EAAA,EAAA,EAAA,CAAA,EAAA,CAAA,+CAMpE,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA,OAAS,CAAK,EAAA,MAAS,SAAA,EAAhC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4F,IAA5F,GAAgE,0BAAwB,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,CAAA,EAAA,CAAA,2HE/J9F,IAAM,EAAU,EAAA,EAAW,iBAIzB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,YAAA,EAAA,EAIS,EAAA,MAAA,CAAA,EAAA,OAAA,GAAA,EAJT,EAAA,WAAA,CAIS,CAJA,aAAA,EAAW,EAAA,MAAA,CAAA,CAAA,CAAO,EAAE,OAAO,OAAK,MAAoBC,EAAAA,EAAAA,MAAM,GAAA,EAAA,EAAA,YAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA,EACnCC,EAAAA,WAAAA,CAAAA,EAAAA,QAAZ,EAAG,MAA8B,OACjD,IAAA,EAAA,EAAA,QAAA,CADyD,GAAS,EAAA,EAClE,EAAA,WAAA,CAA8C,EAAA,OAAjC,GAAA,EAAI,EAAA,eAAA,EAAA,EAAA,EAAA,mBAAA,CAAU,GAAS,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,2REF1C,IAAM,EAAO,EAIP,GAAA,EAAS,EAAA,IAAA,CAAyB,CAAC,CAAC,EAE1C,SAAS,EAAU,EAAa,EAAsB,CAChD,aAAc,oBAAmB,EAAO,MAAM,GAAU,EAC9D,CAEA,SAAS,EAAW,EAAqB,CAClC,EAAK,UAAU,EAAK,oBAAqB,EAAK,KAAK,CAC1D,CAGA,SAAS,EAAQ,EAAkB,EAAgB,EAAqB,CACtE,IAAM,EAAQ,EAAM,OACpB,IAAK,IAAI,EAAQ,EAAG,GAAS,EAAO,GAAS,EAAG,CAC9C,IAAM,GAAQ,EAAS,EAAQ,EAAQ,EAAQ,GAAS,EACxD,GAAI,CAAC,EAAM,EAAK,CAAC,SAAU,CACzB,EAAK,oBAAqB,EAAM,EAAK,CAAC,KAAK,EAC3C,EAAO,MAAM,EAAK,EAAE,MAAM,EAC1B,MACF,CACF,CACF,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAsBM,MAAA,CAtBD,MAAM,YAAY,KAAK,UAAW,aAAY,EAAA,SACjD,EAAA,GAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAoBS,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAnBkB,EAAA,OAAjB,EAAM,MADhB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAoBS,SAAA,CAlBN,IAAK,EAAK,iBACV,IAAM,GAAO,EAAU,EAAI,CAAM,EAClC,KAAK,SACL,KAAK,MACL,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,kBAAiB,CAAA,yBACa,EAAK,QAAU,EAAA,UAAU,CAAA,CAAA,EAC5D,gBAAe,EAAK,QAAU,EAAA,WAC9B,SAAU,EAAK,QAAU,EAAA,WAAU,EAAA,GACnC,SAAU,EAAK,SACf,QAAK,GAAE,EAAW,CAAI,EACtB,UAAO,EAAA,EAAgB,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAQ,EAAA,MAAO,EAAM,CAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,OAAA,CAAA,GAAA,EACtB,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAQ,EAAA,MAAO,EAAM,EAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,MAAA,CAAA,CAAA,IAEnC,EAAK,OAAA,EAAd,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA4D,IAAA,OAAvC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAK,IAAI,EAAE,cAAY,kDACnD,EAAA,EAAA,mBAAA,CAA6B,OAAA,MAAA,EAAA,EAAA,gBAAA,CAApB,EAAK,KAAK,EAAA,CAAA,EACP,EAAK,QAAU,IAAA,IAAa,EAAK,QAAK,OAAA,EAAlD,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAK,KAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,+ZErDrB,IAAM,EAAQ,EAOR,EAAO,EAMP,GAAA,EAAQ,EAAA,SAAA,KAAe,EAAM,KAAK,KAAK,KAAK,EAC5C,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,KAAK,UAAU,KAAK,EAErD,GAAA,EAAS,EAAA,IAAA,CAAmB,CAAC,CAAC,EAC9B,GAAA,EAAS,EAAA,IAAA,CAAwB,IAAI,EAE3C,SAAS,EAAU,EAAa,EAAsB,CAChD,aAAc,cAAa,EAAO,MAAM,GAAU,EACxD,EAIA,EAAA,EAAA,MAAA,CAAM,EAAW,SAAY,CAC3B,MAAA,EAAM,EAAA,SAAA,CAAS,EACf,IAAM,EAAS,EAAM,MAAM,UAAW,GAAM,EAAE,MAAQ,EAAU,KAAK,EACrE,EAAO,MAAM,EAAO,EAAE,iBAAiB,CAAE,OAAQ,UAAW,MAAO,SAAU,CAAC,CAChF,CAAC,EAED,SAAS,EAAO,EAAqB,CACnC,EAAK,WAAY,CAAG,EACpB,EAAW,KAAK,SAAS,EAAI,GAAG,CAClC,CAEA,SAAS,EAAO,EAAqB,CAC9B,EAAI,WACT,EAAK,QAAS,CAAG,EACjB,EAAW,KAAK,MAAM,EAAI,GAAG,EAC/B,CAGA,SAAS,EAAQ,EAAgB,EAAqB,CACpD,IAAM,EAAQ,EAAM,MAAM,OAC1B,GAAI,CAAC,EAAO,OACZ,IAAM,GAAQ,EAAS,EAAQ,GAAS,EACxC,EAAO,EAAM,MAAM,EAAK,EACxB,EAAO,MAAM,EAAK,EAAE,MAAM,CAC5B,CAGA,IAAM,GAAA,EAAO,EAAA,IAAA,CAA6C,IAAI,EACxD,GAAA,EAAW,EAAA,IAAA,CAAqB,IAAI,EAEpC,GAAA,EAAY,EAAA,SAAA,KAA2B,CAC3C,IAAM,EAAO,EAAS,MAEtB,OADK,EACE,CACL,CACE,MAAO,aACP,KAAM,gBACN,SAAU,CAAC,EAAK,SAChB,YAAe,CACb,EAAK,SAAU,CAAI,EACnB,EAAM,KAAK,OAAO,EAAK,GAAG,CAC5B,CACF,EACA,CAAE,UAAW,EAAK,EAClB,CACE,MAAO,SACP,KAAM,cACN,SAAU,CAAC,EAAK,SAChB,YAAe,EAAO,CAAI,CAC5B,EACA,CACE,MAAO,gBACP,KAAM,qBACN,SAAU,EAAM,MAAM,QAAU,EAChC,YAAe,KAAK,EAAM,KAAK,YAAY,EAAK,GAAG,CACrD,EACA,CACE,MAAO,eACP,KAAM,YACN,YAAe,KAAK,EAAM,KAAK,SAAS,CAC1C,CACF,EA7BkB,CAAC,CA8BrB,CAAC,EAED,SAAS,EAAU,EAAmB,EAAqB,CACzD,EAAS,MAAQ,EACjB,EAAK,OAAO,KAAK,CAAK,CACxB,CAGA,SAAS,EAAc,EAAyB,CAC9C,IAAM,EAAQ,EAAM,MAAM,EAAO,EAAE,MACnC,MAAO,EAAQ,GAAU,IAAU,EAAM,MAAM,EAAS,EAAE,EAAE,KAC9D,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA8CM,MA9CN,GA8CM,EA7CJ,EAAA,EAAA,mBAAA,CAwCM,MAAA,CAvCA,QAAA,SAAJ,IAAI,EACJ,MAAM,kBACN,KAAK,UACJ,aAAY,EAAA,WAAS,cAEtB,EAAA,GAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAiCW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAjCuB,EAAA,OAAhB,EAAK,MAAwB,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,EAAI,GAAA,EAAA,CACrC,EAAc,CAAM,IAAA,EAAhC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEO,OAFP,IAAA,EAEO,EAAA,gBAAA,CADF,EAAI,KAAK,EAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAEd,EAAA,mBAAA,CA4BM,MAAA,YA3BH,IAAM,GAAO,EAAU,EAAI,CAAM,EAClC,KAAK,MACJ,SAAU,EAAI,MAAQ,EAAA,MAAS,EAAA,GAChC,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,kBAAiB,CAAA,yBACa,EAAI,MAAQ,EAAA,KAAS,CAAA,CAAA,EACxD,gBAAe,EAAI,MAAQ,EAAA,MAC3B,QAAK,GAAE,EAAO,CAAG,EACjB,YAAA,EAAQ,EAAA,cAAA,CAAA,GAAiB,EAAO,CAAG,EAAA,CAAA,SAAA,SAAA,CAAA,EACnC,eAAA,EAAW,EAAA,cAAA,CAAA,GAAU,EAAU,EAAQ,CAAG,EAAA,CAAA,SAAA,CAAA,EAC1C,UAAO,EAAgB,EAAA,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAQ,EAAM,CAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,OAAA,CAAA,GACf,EAAA,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAQ,EAAM,EAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,MAAA,CAAA,GACb,EAAA,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAO,CAAG,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,OAAA,CAAA,EAElC,EAAA,EAAA,EAAA,EAAA,WAAA,CAGO,EAAA,OAAA,MAAA,CAHgB,MAAM,OAAQ,EAAI,MAAQ,EAAA,KAAjD,MAGO,CAFI,EAAI,OAAA,EAAb,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA0D,IAAA,OAAtC,OAAA,EAAK,EAAA,eAAA,CAAE,EAAI,IAAI,EAAE,cAAY,MACjD,EAAA,KAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAAwE,OAAA,CAAlE,MAAM,mBAAoB,MAAO,EAAI,KAAU,GAAA,EAAA,EAAA,gBAAA,CAAA,EAAI,KAAK,EAAA,EAAA,EAAA,CAAA,CAAA,EAGxD,EAAI,WAAA,EADZ,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CASS,SAAA,OAPP,KAAK,SACL,MAAM,mBACL,aAAU,UAAY,EAAI,QAC3B,SAAS,KACR,SAAA,EAAK,EAAA,cAAA,CAAA,GAAO,EAAO,CAAG,EAAA,CAAA,MAAA,CAAA,CAEvB,EAAA,CAAA,GAAA,AAAA,EAAA,KAAA,EAAA,EAAA,EAAA,mBAAA,CAA4C,IAAA,CAAzC,MAAM,cAAc,cAAY,sFAKhCC,EAAAA,OAAO,UAAA,EAAlB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAAuB,EAAA,OAAA,SAAA,CAAA,CAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAEzB,EAAA,EAAA,YAAA,EAAA,EAA6C,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAA5B,QAAA,OAAJ,IAAI,EAAQ,MAAO,EAAA,+BEjIpC,IAAA,EAAe,EAAA,gBAAA,CAAgB,CAC7B,KAAM,WACN,MAAO,CACL,IAAK,CAAE,KAAM,OAA8B,SAAU,EAAK,EAC1D,IAAK,CAAE,KAAM,OAAkC,SAAU,EAAK,EAC9D,OAAQ,CAAE,KAAM,QAAS,SAAU,EAAK,EACxC,OAAQ,CAAE,KAAM,OAA6C,SAAU,EAAK,CAC9E,EACA,MAAM,EAAO,CACX,IAAM,EAAU,EAAM,IAAI,QAAQ,EAAM,IAAI,GAAG,EAC/C,GAAI,CAAC,EAAS,UAAa,MAE3B,EAAA,EAAA,QAAA,CAAQ,EAAA,iBAAkB,EAAQ,KAAK,EAEvC,SAAS,EAAS,EAA4B,EAAyB,CACrE,EAAK,KAAK,CAAE,GACZ,EAAA,EAAA,eAAA,KAAqB,CACnB,IAAM,EAAQ,EAAK,QAAQ,CAAE,EACzB,IAAU,IAAI,EAAK,OAAO,EAAO,CAAC,CACxC,CAAC,CACH,CAEA,IAAM,EAAuB,CAC3B,OAAQ,EAAM,OACd,QAAA,EAAQ,EAAA,MAAA,CAAM,EAAO,QAAQ,EAC7B,SAAW,GAAU,EAAM,IAAI,SAAS,EAAM,IAAI,IAAK,CAAK,EAC5D,eAAiB,GAAO,EAAS,EAAQ,eAAgB,CAAE,EAC3D,iBAAmB,GAAO,EAAS,EAAQ,iBAAkB,CAAE,EAC/D,mBAAqB,GAAU,EAAS,EAAQ,YAAa,CAAK,CACpE,EAWA,OAVA,EAAA,EAAA,QAAA,CAAQ,EAAA,EAAgB,CAAI,GAE5B,EAAA,EAAA,MAAA,KACQ,EAAM,OACX,GAAW,CACV,IAAM,EAAQ,EAAS,EAAQ,eAAiB,EAAQ,iBACxD,IAAK,IAAM,IAAM,CAAC,GAAG,CAAK,EAAG,EAAG,CAClC,CACF,MAGE,EAAQ,WAAA,EAAY,EAAA,EAAA,CAAE,EAAQ,UAAW,CAAE,GAAG,EAAQ,MAAO,IAAK,EAAM,IAAI,OAAQ,CAAC,EAAI,IAC7F,CACF,CAAC,2IC9DD,IAAM,EAAQ,EAKR,GAAA,EAAY,EAAA,SAAA,KAAe,EAAM,KAAK,UAAU,KAAK,EACrD,GAAA,EAAW,EAAA,SAAA,KAAe,EAAM,KAAK,KAAK,MAAM,OAAQ,GAAM,EAAE,QAAQ,CAAC,EAGzE,EAAQ,IAAI,IAElB,SAAS,EAAQ,EAA6C,CAC5D,IAAI,EAAO,EAAM,IAAI,CAAG,EAKxB,OAJK,IACH,GAAA,EAAO,EAAA,WAAA,CAA+B,IAAI,EAC1C,EAAM,IAAI,EAAK,CAAI,GAEd,CACT,CAEA,SAAS,EAAQ,EAAa,EAA0B,CACtD,IAAM,EAAO,EAAQ,CAAG,EACxB,GAAI,aAAc,YAAa,CAC7B,EAAK,MAAQ,EACb,MACF,CAII,EAAK,OAAS,CAAC,EAAK,MAAM,cAAa,EAAK,MAAQ,KAC1D,QAEA,EAAA,EAAA,MAAA,CAAM,EAAW,GAAqB,CACpC,IAAM,EAAQ,IAAI,IAAI,EAAK,IAAK,GAAM,EAAE,GAAG,CAAC,EAC5C,IAAK,IAAM,KAAO,EAAM,KAAK,EACtB,EAAM,IAAI,CAAG,GAAG,EAAM,OAAO,CAAG,CAEzC,CAAC,WAIC,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAuBM,MAvBN,GAuBM,GAAA,EAlBJ,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAhBU,EAAA,MAAP,IADT,EAAA,EAAA,eAAA,GAAA,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAiBM,MAAA,CAdH,IAAG,GAAK,EAAI,IAAG,GAAI,EAAI,qBACvB,IAAM,GAAO,EAAQ,EAAI,IAAK,CAAE,EACjC,MAAM,sBAIN,EAAA,EAAA,EAAA,EAAA,mBAAA,CAOM,MAPN,GAOM,EAAA,EANJ,EAAA,YAAA,EAAA,EAKE,EAAA,MAAA,CAAA,EAAA,EAAA,CAJM,MACL,IAAK,EAAA,KACL,OAAQ,EAAI,MAAQ,EAAA,MACpB,UAAS,EAAQ,EAAI,GAAG,yCAZrB,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,CAAA,EAAA,MAAA,EAAI,MAAQ,EAAA,KAAS,CAAA,CAAA,+oBE9CnC,IAAM,EAAQ,EAsBR,EAAO,EAkBP,GAAA,EAAW,EAAA,IAAA,CAAuD,IAAI,EACtE,GAAA,EAAa,EAAA,IAAA,CAA8C,IAAI,EAE/D,EAAW,IAAI,IAErB,SAAS,EAAW,EAAe,EAAa,CAC1C,aAAc,YAAa,EAAS,IAAI,EAAO,CAAE,EAChD,EAAS,OAAO,CAAK,CAC5B,CAEA,SAAS,EAAM,EAAqB,CAClC,OAAO,EAAK,EAAM,QACpB,CAEA,SAAS,EAAc,EAAoB,CACzC,OAAO,OAAO,EAAK,MAAQ,EAAM,CAAI,GAAK,EAAE,CAC9C,CAEA,SAAS,EAAY,EAAqB,CAExC,OADI,EAAM,QAAgB,GACnB,GAAM,SAAU,EAAM,QAAQ,CAAI,CAC3C,CAEA,SAAS,EAAW,EAAqB,CACvC,MAAO,CAAC,CAAC,EAAS,OAAS,EAAM,CAAI,IAAM,EAAS,MAAM,GAC5D,CAGA,SAAS,EAAa,EAAkB,EAA0B,CAChE,IAAM,EAAO,EAAS,IAAI,CAAQ,EAClC,GAAI,CAAC,EAAM,MAAO,GAClB,IAAM,EAAQ,MAAM,KAAK,EAAK,iBAAiB,gCAAgC,CAAC,EAC5E,EAAQ,EAAM,OAClB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAO,EAAM,EAAE,CAAC,sBAAsB,EAC5C,GAAI,EAAM,QAAU,EAAK,IAAM,EAAK,OAAS,EAAG,CAC9C,EAAQ,EACR,KACF,CACF,CACA,OAAO,CACT,CAEA,SAAS,EAAY,EAAY,EAAsB,EAAkB,CACvE,GAAI,CAAC,EAAY,CAAI,EAAG,CACtB,EAAM,eAAe,EACrB,MACF,CACA,EAAS,MAAQ,CAAE,OAAM,KAAM,EAAO,MAAO,IAAK,EAAM,CAAI,CAAE,EAC1D,EAAM,eACR,EAAM,aAAa,cAAgB,OACnC,EAAM,aAAa,QAAQ,aAAc,OAAO,EAAM,CAAI,CAAC,CAAC,EAEhE,CAEA,SAAS,EAAW,EAAsB,EAAkB,CACrD,EAAS,QACd,EAAM,eAAe,EACjB,EAAM,eAAc,EAAM,aAAa,WAAa,QACxD,EAAW,MAAQ,CAAE,OAAQ,EAAO,MAAO,MAAO,EAAa,EAAO,MAAO,CAAK,CAAE,EACtF,CAEA,SAAS,EAAY,EAAsB,EAAkB,CAC3D,IAAM,EAAO,EAAS,IAAI,EAAO,KAAK,EAChC,EAAU,EAAM,cAClB,GAAQ,aAAmB,MAAQ,EAAK,SAAS,CAAO,GACxD,EAAW,OAAO,SAAW,EAAO,QAAO,EAAW,MAAQ,KACpE,CAEA,SAAS,EAAO,EAAsB,EAAkB,CACtD,EAAM,eAAe,EACrB,IAAM,EAAO,EAAS,MAEtB,GADA,EAAU,EACN,CAAC,EAAM,OAEX,IAAM,EAAW,EAAa,EAAO,MAAO,CAAK,EAC7C,EAAQ,EAEZ,GAAI,EAAK,OAAS,EAAO,MAAO,CAC9B,IAAM,EAAS,EAAM,QAAQ,KAAM,GAAM,EAAE,QAAU,EAAO,KAAK,EAC3D,EAAW,EAAS,EAAO,MAAM,UAAW,GAAM,EAAM,CAAC,IAAM,EAAK,GAAG,EAAI,GAGjF,GAFI,IAAa,IAAM,EAAW,IAAU,EAAQ,EAAW,GAE3D,IAAU,EAAU,MAC1B,CAEA,EAAK,OAAQ,CAAE,KAAM,EAAK,KAAM,KAAM,EAAK,KAAM,GAAI,EAAO,MAAO,OAAM,CAAC,CAC5E,CAEA,SAAS,GAAY,CACnB,EAAU,CACZ,CAEA,SAAS,GAAY,CACnB,EAAS,MAAQ,KACjB,EAAW,MAAQ,IACrB,CAEA,SAAS,EAAY,EAAY,CAC3B,EAAS,OACb,EAAK,aAAc,CAAI,CACzB,CAEA,SAAS,EAAgB,EAAsB,EAAwB,CACrE,MACE,CAAC,CAAC,EAAS,OACX,EAAW,OAAO,SAAW,EAAO,OACpC,EAAW,MAAM,QAAU,CAE/B,gBAIE,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAmEM,MAAA,CAlEJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,iBAAgB,CACqB,0BAAA,EAAA,QAA6C,2BAAA,CAAA,CAAA,EAAA,SAIvF,YAAW,EAAA,SAAW,IAAA,EAEvB,EAAA,GAAA,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CA0DU,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CAzDS,EAAA,QAAV,KADT,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CA0DU,UAAA,CAxDP,IAAK,EAAO,MACb,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,yBAAwB,CACsB,+BAAA,CAAA,CAAA,EAAA,OAAY,EAAA,OAAY,SAAW,EAAO,KAAA,CAAA,CAAA,EAG7F,OAAA,EAAK,EAAA,eAAA,CAAE,EAAO,OAAM,CAAA,oBAA0B,EAAO,MAAM,EAAK,IAAA,EAAS,CAE1E,EAAA,EAAA,EAAA,EAAA,mBAAA,CAMS,SANT,GAMS,EAAA,EALP,EAAA,WAAA,CAGO,EAAA,OAAA,gBAAA,CAH6B,QAAM,MAGnC,EAAA,EAFL,EAAA,mBAAA,CAA6D,OAA7D,IAAA,EAA6D,EAAA,gBAAA,CAAtB,EAAO,KAAK,EAAA,CAAA,GAAA,EACnD,EAAA,mBAAA,CAAoE,OAApE,IAAA,EAAoE,EAAA,gBAAA,CAA7B,EAAO,MAAM,MAAM,EAAA,CAAA,CAAA,CAAA,GAAA,EAE5D,EAAA,WAAA,CAA8C,EAAA,OAAA,gBAAA,CAAV,QAAM,CAAA,CAAA,CAAA,GAAA,EAG5C,EAAA,mBAAA,CAwCM,MAAA,YAvCH,IAAM,GAAO,EAAW,EAAO,MAAO,CAAE,EACzC,MAAM,uBACN,KAAK,OACJ,aAAY,EAAO,MACnB,OAAA,EAAK,EAAA,eAAA,CAAA,CAAA,UAAI,EAAA,UAAS,UAAE,EAAA,SAAS,CAAA,EAC7B,WAAQ,GAAE,EAAW,EAAQ,CAAM,EACnC,YAAS,GAAE,EAAY,EAAQ,CAAM,EACrC,OAAI,GAAE,EAAO,EAAQ,CAAM,MAE5B,EAAA,EAAA,UAAA,CAAA,EAAA,GAAA,EAAA,EAAA,mBAAA,CAqBW,EAAA,SAAA,MAAA,EAAA,EAAA,WAAA,CArBmB,EAAO,OAAnB,EAAM,MAA0B,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAAA,EAAA,SAAA,CAAA,IAAA,OAAO,EAAM,CAAI,CAAA,CAAA,EAAA,CACtD,EAAgB,EAAQ,CAAC,IAAA,EAApC,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAA2E,MAA3E,EAA2E,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,GAAA,EAC3E,EAAA,mBAAA,CAkBM,MAAA,CAjBJ,OAAA,EAAK,EAAA,eAAA,CAAA,CAAC,uBAAsB,CAG8B,iCAAA,EAAW,CAAI,EAAkD,+BAAA,CAAA,EAAY,CAAI,KAF3I,KAAK,WACL,SAAS,IAKR,UAAW,EAAY,CAAI,EAC3B,YAAS,GAAE,EAAY,EAAM,EAAQ,CAAM,EAC3C,UAAS,EACT,QAAK,GAAE,EAAY,CAAI,EACvB,UAAO,EAAA,EAAgB,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAY,CAAI,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,OAAA,CAAA,GAAA,EAChB,EAAA,SAAA,EAAA,EAAA,EAAA,cAAA,CAAA,GAAA,EAAY,CAAI,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,OAAA,CAAA,CAAA,CAExC,EAAA,EAAA,EAAA,EAAA,WAAA,CAEO,EAAA,OAAA,OAAA,CAFkB,OAAe,QAAxC,MAEO,EAAA,EADL,EAAA,mBAAA,CAA4E,OAA5E,IAAA,EAA4E,EAAA,gBAAA,CAA7B,EAAc,CAAI,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA,UAK/D,EAAgB,EAAQ,EAAO,MAAM,MAAM,IAAA,EADnD,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAGE,MAHF,EAGE,IAAA,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,EAEU,EAAO,MAAM,QAC+B,EAAA,EAAA,mBAAA,CAAA,GAAA,EAAA,IAD/B,EAAzB,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAEM,MAFN,GAEM,EAAA,EADJ,EAAA,WAAA,CAA6D,EAAA,OAAA,eAAA,CAA1B,QAAM,MAAoB,CAAlB,AAAA,EAAA,MAAA,EAAA,EAAA,gBAAA,CAAA,cAAW,EAAA,CAAA,CAAA,CAAA,CAAA"}
|