@declarion/react 0.6.5 → 0.6.7
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-lib/DraggableChildrenTable-CdzxxoTe.js +259 -0
- package/dist-lib/Grid-Cwxca9aO.js +0 -2
- package/dist-lib/JsonEditor-KelPV-OQ.js +0 -2
- package/dist-lib/MarkdownRenderer-Dbl3AFDQ.js +0 -2
- package/dist-lib/api/data.d.ts +1 -1
- package/dist-lib/components/detail-layout/DetailAuditStrip.d.ts +2 -2
- package/dist-lib/components/detail-layout/DetailHighlights.d.ts +2 -2
- package/dist-lib/components/detail-layout/DraggableChildrenTable.d.ts +2 -2
- package/dist-lib/components/detail-layout/EditableChildrenTable.d.ts +2 -2
- package/dist-lib/components/detail-layout/LayoutRenderer.d.ts +2 -2
- package/dist-lib/components/fields/cells/registry.d.ts +4 -4
- package/dist-lib/components/fields/index.d.ts +3 -3
- package/dist-lib/components/file-widgets/host.d.ts +2 -2
- package/dist-lib/components/filters/FilterDrawer.d.ts +1 -1
- package/dist-lib/components/list/PeekFieldCell.d.ts +2 -2
- package/dist-lib/components/list/QuickPeek.d.ts +2 -2
- package/dist-lib/components/mass-edit/PropertyGridEditor.d.ts +1 -1
- package/dist-lib/components/pages/SmartListPage.d.ts +1 -25
- package/dist-lib/components/pages/list/DataCell.d.ts +1 -2
- package/dist-lib/components/pages/list/columns.d.ts +3 -3
- package/dist-lib/components/primitives/index.d.ts +0 -1
- package/dist-lib/components/shared/ActionRunner.d.ts +3 -2
- package/dist-lib/components/shared/Breadcrumbs.d.ts +1 -1
- package/dist-lib/{dataTableStyles-BwnbXviH.js → dataTableStyles-zH3M7xdm.js} +765 -793
- package/dist-lib/index.d.ts +1 -6
- package/dist-lib/index.js +2888 -3311
- package/dist-lib/lib/display-value.d.ts +1 -0
- package/dist-lib/lib/filters.d.ts +0 -4
- package/dist-lib/lib/timestamp.d.ts +16 -3
- package/dist-lib/rich-text-CJpzFOG5.js +0 -2
- package/dist-lib/types/api.d.ts +10 -3
- package/dist-lib/value-CrpaObP_.js +0 -2
- package/dist-lib/vite/index.js +0 -2
- package/package.json +4 -2
- package/dist-lib/DraggableChildrenTable-DhcSXQfV.js +0 -261
- package/dist-lib/DraggableChildrenTable-DhcSXQfV.js.map +0 -1
- package/dist-lib/Grid-Cwxca9aO.js.map +0 -1
- package/dist-lib/JsonEditor-KelPV-OQ.js.map +0 -1
- package/dist-lib/MarkdownRenderer-Dbl3AFDQ.js.map +0 -1
- package/dist-lib/components/list/BulkBar.d.ts +0 -18
- package/dist-lib/components/list/ListPage.d.ts +0 -34
- package/dist-lib/components/list/resolveActions.d.ts +0 -53
- package/dist-lib/components/primitives/cx.d.ts +0 -1
- package/dist-lib/dataTableStyles-BwnbXviH.js.map +0 -1
- package/dist-lib/index.js.map +0 -1
- package/dist-lib/rich-text-CJpzFOG5.js.map +0 -1
- package/dist-lib/value-CrpaObP_.js.map +0 -1
- package/dist-lib/vite/index.js.map +0 -1
package/dist-lib/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/lib/branding.ts","../src/components/layout/Rail.tsx","../src/components/primitives/BrandLogo.tsx","../src/stores/tabs.ts","../src/embed/params.ts","../src/embed/theme.ts","../src/embed/EmbedContext.tsx","../src/embed/handshake.ts","../src/embed/EmbedAuthBootstrap.tsx","../src/embed/EmbedResizeReporter.tsx","../src/embed/embed-dirty.ts","../src/embed/EmbedDirtyReporter.tsx","../src/embed/screen-location.ts","../src/embed/embed-nav-guard.ts","../src/embed/useEmbedNavigate.ts","../src/lib/utils.ts","../src/embed/useGlobalLinkInterceptor.ts","../src/embed/EmbedLocationReporter.tsx","../src/components/shared/ConfirmDialog.tsx","../src/embed/EmbedUnsavedGuard.tsx","../src/embed/index.ts","../src/hooks/useTabNavigate.ts","../src/components/layout/Sidebar.tsx","../src/components/shared/toast/DeclarionToast.tsx","../src/lib/toast.tsx","../src/hooks/useAuth.ts","../src/hooks/useImpersonation.ts","../src/components/layout/TenantChip.tsx","../src/components/shared/Breadcrumbs.tsx","../src/components/layout/TopBar.tsx","../src/components/layout/TabStrip.tsx","../src/components/layout/DirtyTabsCloseDialog.tsx","../src/components/layout/ImpersonationBanner.tsx","../src/api/params.ts","../src/hooks/useParams.ts","../src/components/layout/ImpersonationFrame.tsx","../src/components/layout/tab-close.ts","../src/components/shell/CommandPalette.tsx","../src/api/agents.ts","../src/api/events.ts","../src/hooks/useAgentConversation.ts","../src/components/agents/AgentElicitForm.tsx","../src/components/agents/AgentChatPanel.tsx","../src/lib/field-regions.ts","../src/lib/ui-actions.ts","../src/lib/mass-edit.ts","../src/components/mass-edit/PropertyGridEditor.tsx","../src/components/shared/ActionDialog.tsx","../src/components/shared/ActionResultDialog.tsx","../src/components/shared/ActionRunner.tsx","../src/hooks/useChildrenData.ts","../src/stores/realtime.ts","../src/hooks/useSSE.ts","../src/components/layout/Layout.tsx","../src/components/pages/auth/AuthShell.tsx","../src/components/pages/auth/icons.tsx","../src/lib/schema-routing.ts","../src/lib/messages.ts","../src/components/pages/auth/SignIn.tsx","../src/components/pages/auth/SignUp.tsx","../src/components/pages/auth/Forgot.tsx","../src/components/pages/auth/Reset.tsx","../src/components/pages/auth/TwoFA.tsx","../src/components/pages/SSOCallbackPage.tsx","../src/components/pages/NotFoundPage.tsx","../src/components/shared/ChangePasswordDialog.tsx","../src/hooks/useAssociatedData.ts","../src/components/shared/ActionConfirmDialog.tsx","../src/hooks/useMutations.ts","../src/lib/actions.ts","../src/components/shared/ActionButton.tsx","../src/components/shared/RowActionsKebab.tsx","../src/components/detail-layout/AssociatedToolbar.tsx","../src/components/shared/ResponsiveActionBar.tsx","../src/lib/action-dispatch.ts","../src/components/detail-layout/AssociatedBlock.tsx","../src/components/pages/profile/Profile.tsx","../src/components/pages/profile/Preferences.tsx","../src/components/pages/AdminCatalogPage.tsx","../src/components/pages/exportRequest.ts","../src/components/pages/ExportMenu.tsx","../src/hooks/useDebounce.ts","../src/components/shared/EmptyState.tsx","../src/lib/safe-url.ts","../src/components/shared/LoadingSkeleton.tsx","../src/lib/filters.ts","../src/hooks/useFilters.ts","../src/components/list/PeekFieldCell.tsx","../src/components/list/QuickPeek.tsx","../src/components/list/peekDraftStore.ts","../src/lib/sort.ts","../src/lib/format-count.ts","../src/components/filters/FilterChips.tsx","../src/components/filters/FilterDrawer.tsx","../src/components/pages/list/filter-meta.ts","../src/components/pages/list/SaveViewDialog.tsx","../src/components/pages/list/ListHeader.tsx","../src/components/pages/list/ListBulkBar.tsx","../src/components/pages/list/DataCell.tsx","../src/lib/field-classification.ts","../src/components/pages/list/RowActionsCell.tsx","../src/components/pages/list/columns.ts","../src/components/columns/ColumnPicker.tsx","../src/components/views/ViewSwitcher.tsx","../src/hooks/useScreen.ts","../src/hooks/useViews.ts","../src/components/pages/SmartListPage.tsx","../src/hooks/useEntityItem.ts","../src/components/shared/AssociationTabs.tsx","../src/lib/size-value.ts","../src/components/detail-layout/LayoutRow.tsx","../src/components/detail-layout/LayoutColumn.tsx","../src/components/detail-layout/blockMeta.ts","../src/components/detail-layout/LayoutTabs.tsx","../src/components/detail-layout/SectionBlock.tsx","../src/lib/child-config.ts","../src/lib/quick-add.ts","../src/components/detail-layout/useQuickAdd.ts","../src/components/detail-layout/QuickAddForm.tsx","../src/components/detail-layout/ChildrenTable.tsx","../src/components/detail-layout/ChildrenList.tsx","../src/components/detail-layout/ChildrenCards.tsx","../src/hooks/useTimelineData.ts","../src/components/detail-layout/TimelineComposer.tsx","../src/components/detail-layout/ChildrenTimeline.tsx","../src/components/detail-layout/EditableChildrenTable.tsx","../src/components/detail-layout/ChildrenBlock.tsx","../src/components/detail-layout/FieldRow.tsx","../src/components/detail-layout/FieldsBlock.tsx","../src/components/detail-layout/PropertiesBlock.tsx","../src/components/shared/StatusStepper.tsx","../src/components/shared/StatusGroupsDisplay.tsx","../src/components/detail-layout/StatusesBlock.tsx","../src/components/detail-layout/LayoutRenderer.tsx","../src/components/detail-layout/ErrorSummary.tsx","../src/components/shared/HighlightsStrip.tsx","../src/components/detail-layout/DetailHighlights.tsx","../src/components/shared/AuditMetaStrip.tsx","../src/components/detail-layout/DetailAuditStrip.tsx","../src/components/shared/RecordHeader.tsx","../src/lib/children-validation.ts","../src/components/pages/SmartDetailPage.tsx","../src/api/status.ts","../src/hooks/useObjectStatus.ts","../src/api/properties.ts","../src/hooks/useProperties.ts","../src/components/pages/SmartRecordListPage.tsx","../src/components/shared/ErrorBoundary.tsx","../src/hooks/useVersionWatcher.ts","../src/lib/capabilityPrefetch.ts","../src/stores/screen-registry.ts","../src/app.tsx","../src/api/entities.ts","../src/api/batch.ts","../src/hooks/useAllEntityData.ts","../src/hooks/useColumns.ts","../src/components/shared/PageHeader.tsx","../src/components/list/BulkBar.tsx","../src/components/list/RowMenu.tsx","../src/components/list/resolveActions.ts","../src/components/list/ListPage.tsx","../src/components/list/InlineEditCell.tsx","../src/components/pages/home/HomeStub.tsx","../src/components/pages/pipeline/Pipeline.tsx","../src/components/pages/jobs/Jobs.tsx","../src/components/layout/ImpersonationStartModal.tsx","../src/components/agents/AgentVisualHints.tsx","../src/components/agents/AgentConversationDrawer.tsx"],"sourcesContent":["// Branding helpers. Single source of truth for how consumer-YAML branding\n// values are interpreted across every chrome surface (rail, sidebar,\n// favicon, auth shell).\n\n/**\n * Returns true when `value` is a renderable logo URL. Accepts:\n * - root-relative paths (\"/logo.png\"), served from the UI bundle root;\n * - absolute http(s) URLs;\n * - data:image/... URIs.\n *\n * Rejects inline SVG markup and any other string so callers can't\n * accidentally inject operator-controlled YAML into a dangerouslySet\n * surface. Returns false for null/undefined/empty input.\n */\nexport function isValidLogoUrl(value: string | undefined | null): boolean {\n if (!value) return false;\n const t = value.trim();\n if (!t) return false;\n return (\n t.startsWith(\"/\") ||\n t.startsWith(\"http://\") ||\n t.startsWith(\"https://\") ||\n t.startsWith(\"data:image/\")\n );\n}\n","import { useState, type CSSProperties } from \"react\";\nimport { Icon, type IconName } from \"@/components/primitives\";\nimport type { AreaDef } from \"@/types/schema\";\nimport type { PublicBranding } from \"@/types/api\";\nimport { loc } from \"@/types/schema\";\nimport { isValidLogoUrl } from \"@/lib/branding\";\n\n// Size of the brand tile at the top of the rail. Must match the tile's\n// width/height so the logo fills the tile edge-to-edge without distortion.\nconst RAIL_BRAND_SIZE = 32;\n\nexport interface RailAreaItem {\n id: string;\n icon: IconName | undefined;\n label: string;\n}\n\nexport interface RailProps {\n activeArea: string;\n onPick: (id: string) => void;\n onToggleAI: () => void;\n aiOpen: boolean;\n onCmdK: () => void;\n areas?: AreaDef[];\n /**\n * Consumer branding from /api/params/public. When `app_logo` is present\n * the rail's brand tile renders the consumer image; otherwise it falls\n * back to the Declarion wordmark SVG. `app_name` drives the tooltip.\n */\n branding?: PublicBranding;\n}\n\n// Empty/absent areas registry → no area buttons. The platform-home brand tile\n// and the bottom chrome (command palette, AI) still render.\nfunction toItems(areas?: AreaDef[]): RailAreaItem[] {\n if (!areas || areas.length === 0) return [];\n return areas.map((a) => ({\n id: a.code,\n icon: a.icon ? (a.icon as IconName) : undefined,\n label: loc(a.name) || a.code,\n }));\n}\n\nexport function Rail({ activeArea, onPick, onToggleAI, aiOpen, onCmdK, areas, branding }: RailProps) {\n const items = toItems(areas);\n const brandName = branding?.app_name ?? \"\";\n const brandLogo = isValidLogoUrl(branding?.app_logo) ? branding?.app_logo : undefined;\n return (\n <div\n style={{\n gridArea: \"rail\",\n background: \"var(--rail)\",\n color: \"var(--rail-fg)\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n padding: \"8px 0\",\n gap: 2,\n }}\n >\n <div\n style={{\n width: RAIL_BRAND_SIZE,\n height: RAIL_BRAND_SIZE,\n borderRadius: 8,\n // Solid background only when we have no consumer logo; when a\n // logo image renders on top, the gradient muddies it.\n background: brandLogo\n ? \"transparent\"\n : \"linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n marginBottom: 8,\n boxShadow: brandLogo\n ? \"none\"\n : \"0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.14)\",\n overflow: \"hidden\",\n }}\n title={brandName ? `${brandName} — home` : \"home\"}\n >\n {brandLogo ? (\n <img\n src={brandLogo}\n alt={brandName}\n width={RAIL_BRAND_SIZE}\n height={RAIL_BRAND_SIZE}\n style={{ display: \"block\", objectFit: \"contain\" }}\n />\n ) : brandName ? (\n <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: \"0.04em\" }}>\n {brandName.charAt(0).toUpperCase()}\n </span>\n ) : null}\n </div>\n {items.map((it) => (\n <RailButton key={it.id} item={it} active={activeArea === it.id} onPick={onPick} />\n ))}\n <div style={{ flex: 1 }} />\n <button\n onClick={onCmdK}\n title=\"Command Palette (⌘K)\"\n style={railIconButton(false)}\n >\n {Icon.command && <Icon.command size={16} />}\n </button>\n <button\n onClick={onToggleAI}\n title=\"AI Assistant\"\n style={{\n ...railIconButton(false),\n background: aiOpen ? \"var(--accent)\" : \"transparent\",\n color: aiOpen ? \"var(--rail-fg)\" : \"var(--rail-muted)\",\n }}\n >\n {Icon.sparkle && <Icon.sparkle size={16} />}\n </button>\n </div>\n );\n}\n\nfunction RailButton({ item, active, onPick }: { item: RailAreaItem; active: boolean; onPick: (id: string) => void }) {\n const [hover, setHover] = useState(false);\n const IconComp = item.icon ? Icon[item.icon] : undefined;\n if (item.icon && !IconComp) {\n // Surface the misconfiguration instead of silently degrading. Consumer\n // YAML referenced an icon name that is not in the registry; the button\n // still renders (so the area is clickable) but the caller should see\n // the warning and fix the schema.\n console.error(`[declarion] area \"${item.id}\" uses unknown icon \"${item.icon}\"`);\n }\n const bg = active ? \"var(--rail-active)\" : hover ? \"var(--rail-hover)\" : \"transparent\";\n const color = active || hover ? \"var(--rail-fg)\" : \"var(--rail-muted)\";\n return (\n <button\n onClick={() => onPick(item.id)}\n title={item.label}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={{\n width: 36,\n height: 36,\n borderRadius: 8,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: bg,\n color,\n border: \"none\",\n cursor: \"pointer\",\n position: \"relative\",\n }}\n >\n {IconComp && <IconComp size={16} />}\n {active && (\n <span\n style={{\n position: \"absolute\",\n left: -1,\n top: 8,\n bottom: 8,\n width: 2,\n background: \"var(--accent)\",\n borderRadius: 2,\n }}\n />\n )}\n </button>\n );\n}\n\nfunction railIconButton(_active: boolean): CSSProperties {\n return {\n width: 36,\n height: 36,\n borderRadius: 8,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"transparent\",\n color: \"var(--rail-muted)\",\n border: 0,\n cursor: \"pointer\",\n };\n}\n","// Shared brand-logo primitive. Single renderer for every surface that\n// shows the consumer's branding.app_logo (Sidebar header, AuthShell\n// topbar, AuthShell brand panel). Reads the URL from consumer YAML; the\n// consumer's asset lives in their app/public/ dir, served from the UI\n// bundle root at runtime.\n//\n// When `appLogo` is missing or invalid the component renders the\n// caller-supplied `fallback` if any, otherwise nothing. The SDK does\n// NOT supply a default mark — same rule as every other Declarion-flavored\n// fallback we removed: consumers override every brand surface, including\n// the logo, through `branding:` YAML. A null return when no logo is\n// configured is a feature, not a bug.\n\nimport type { ReactNode } from \"react\";\nimport { isValidLogoUrl } from \"@/lib/branding\";\n\nexport interface BrandLogoProps {\n /** URL from branding.app_logo in consumer YAML. */\n appLogo?: string;\n /** Pixel edge of the rendered image. */\n size: number;\n /** Border radius for the image frame. Defaults to 8 at size>=30 else 6. */\n radius?: number;\n /** Rendered when appLogo is missing or not a valid URL. */\n fallback?: ReactNode;\n}\n\nexport function BrandLogo({ appLogo, size, radius, fallback }: BrandLogoProps) {\n if (!isValidLogoUrl(appLogo)) return fallback ? <>{fallback}</> : null;\n const r = radius ?? (size >= 30 ? 8 : 6);\n return (\n <img\n src={appLogo!.trim()}\n alt=\"\"\n width={size}\n height={size}\n style={{ display: \"block\", borderRadius: r, objectFit: \"contain\" }}\n />\n );\n}\n","import { create } from \"zustand\";\nimport { useAppearanceStore } from \"./appearance\";\n\nexport type TabMode = \"view\" | \"edit\" | \"create\";\nexport type TabCloseScope = \"current\" | \"others\" | \"right\" | \"all\";\n\nexport interface TabItem {\n id: string;\n pathname: string;\n search: string;\n title: string;\n icon?: string;\n closable: boolean;\n // New fields from design bundle (app.jsx tab model).\n area?: string;\n screen?: string;\n navId?: string;\n entity?: string;\n record?: Record<string, unknown>;\n mode?: TabMode;\n dirty?: boolean;\n}\n\ninterface TabState {\n tabs: TabItem[];\n activeTabId: string | null;\n\n openTab: (tab: Omit<TabItem, \"id\">) => string;\n closeTab: (id: string) => void;\n closeTabs: (ids: string[]) => TabItem | null;\n switchTab: (id: string) => void;\n reorderTabs: (activeId: string, overId: string) => void;\n findTabByPathname: (pathname: string) => TabItem | undefined;\n updateTabTitle: (id: string, title: string) => void;\n updateTabSearch: (id: string, search: string) => void;\n setDirty: (id: string, dirty: boolean) => void;\n setMode: (id: string, mode: TabMode) => void;\n updateTab: (id: string, patch: Partial<TabItem>) => void;\n setScope: (tenantId: string | null, userId: string | null) => void;\n hydrate: () => void;\n}\n\n// Routes that should not create tabs.\nconst EXCLUDED_ROUTES = [\"/login\", \"/signup\"];\n\nfunction isClosable(tab: TabItem): boolean {\n return tab.closable !== false;\n}\n\nexport function tabIdsForCloseScope(\n tabs: TabItem[],\n activeTabId: string | null,\n scope: TabCloseScope,\n targetId?: string,\n): string[] {\n if (scope === \"all\") return tabs.filter(isClosable).map((t) => t.id);\n\n const id = targetId ?? activeTabId;\n if (!id) return [];\n const idx = tabs.findIndex((t) => t.id === id);\n if (idx === -1) return [];\n\n if (scope === \"current\") {\n return isClosable(tabs[idx]) ? [tabs[idx].id] : [];\n }\n if (scope === \"others\") {\n return tabs.filter((t, i) => i !== idx && isClosable(t)).map((t) => t.id);\n }\n return tabs.slice(idx + 1).filter(isClosable).map((t) => t.id);\n}\n\nlet currentTenantId: string | null = null;\nlet currentUserId: string | null = null;\n\nfunction tabsStorageKey(tenantId: string, userId: string): string {\n return `declarion::${tenantId}::${userId}::tabs`;\n}\n\nfunction storageKey(): string | null {\n if (!currentTenantId || !currentUserId) return null;\n return tabsStorageKey(currentTenantId, currentUserId);\n}\n\n// peekTenantLandingPath reads a tenant's persisted tab set WITHOUT changing\n// the active scope, returning the pathname+search of that tenant's\n// last-active tab. Tenant switching uses it to reload straight onto the\n// tenant's own last-active tab (per-tenant tab sets); it returns null when\n// the tenant has no persisted set, so the caller can fall back to a default.\nexport function peekTenantLandingPath(tenantId: string, userId: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(tabsStorageKey(tenantId, userId));\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (!parsed || !Array.isArray(parsed.tabs)) return null;\n const active = (parsed.tabs as TabItem[]).find((t) => t.id === parsed.activeTabId);\n const pathname = active?.pathname;\n // Only same-origin in-app routes - guard against a tampered snapshot.\n if (typeof pathname === \"string\" && pathname.startsWith(\"/\") && !pathname.startsWith(\"//\")) {\n return `${pathname}${typeof active?.search === \"string\" ? active.search : \"\"}`;\n }\n } catch {\n // ignore - a malformed snapshot falls back to the default route\n }\n return null;\n}\n\nfunction persist(tabs: TabItem[], activeTabId: string | null) {\n if (typeof window === \"undefined\") return;\n // Honor ui_tabs_persist (Stage 3 plan T4): when disabled, also clear any\n // stale snapshot so a future reload starts clean.\n const enabled = useAppearanceStore.getState().tabsPersist;\n const key = storageKey();\n if (!key) return;\n if (!enabled) {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n return;\n }\n try {\n localStorage.setItem(key, JSON.stringify({ tabs, activeTabId }));\n } catch {\n // ignore\n }\n}\n\nfunction loadPersisted(): { tabs: TabItem[]; activeTabId: string | null } | null {\n if (typeof window === \"undefined\") return null;\n if (!useAppearanceStore.getState().tabsPersist) return null;\n const key = storageKey();\n if (!key) return null;\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n if (parsed && Array.isArray(parsed.tabs)) return parsed;\n } catch {\n // ignore\n }\n return null;\n}\n\nexport const useTabStore = create<TabState>((set, get) => ({\n tabs: [],\n activeTabId: null,\n\n openTab: (tab) => {\n if (EXCLUDED_ROUTES.includes(tab.pathname)) {\n return \"\";\n }\n\n const state = get();\n // If a tab with the same pathname already exists, switch to it.\n const existing = state.tabs.find((t) => t.pathname === tab.pathname);\n if (existing) {\n set({ activeTabId: existing.id });\n persist(state.tabs, existing.id);\n return existing.id;\n }\n\n const id = crypto.randomUUID();\n const newTab: TabItem = { ...tab, id };\n let tabs = [...state.tabs, newTab];\n\n const cap = useAppearanceStore.getState().tabsMaxOpen;\n if (cap > 0 && tabs.length > cap) {\n // Evict oldest non-dirty, non-active tab. New tab is at end, so scan from front.\n const evictIdx = tabs.findIndex(\n (t) => t.id !== id && t.closable !== false && !t.dirty,\n );\n if (evictIdx !== -1) tabs = tabs.filter((_, i) => i !== evictIdx);\n }\n\n set({ tabs, activeTabId: id });\n persist(tabs, id);\n return id;\n },\n\n closeTab: (id) => {\n const state = get();\n const idx = state.tabs.findIndex((t) => t.id === id);\n if (idx === -1) return;\n\n const tab = state.tabs[idx];\n if (!tab.closable) return;\n\n // Prevent closing the very last tab — always keep at least one.\n if (state.tabs.length === 1) return;\n\n get().closeTabs([id]);\n },\n\n closeTabs: (ids) => {\n const state = get();\n const requested = new Set(ids);\n if (requested.size === 0) {\n return state.tabs.find((t) => t.id === state.activeTabId) ?? null;\n }\n\n const activeIdx = state.tabs.findIndex((t) => t.id === state.activeTabId);\n const remaining = state.tabs.filter((t) => !(requested.has(t.id) && isClosable(t)));\n let nextActive = state.activeTabId;\n\n if (nextActive && !remaining.some((t) => t.id === nextActive)) {\n if (remaining.length === 0) {\n nextActive = null;\n } else {\n const nextIdx = Math.min(Math.max(activeIdx, 0), remaining.length - 1);\n nextActive = remaining[nextIdx].id;\n }\n } else if (remaining.length === 0) {\n nextActive = null;\n }\n\n set({ tabs: remaining, activeTabId: nextActive });\n persist(remaining, nextActive);\n return remaining.find((t) => t.id === nextActive) ?? null;\n },\n\n switchTab: (id) => {\n set({ activeTabId: id });\n persist(get().tabs, id);\n },\n\n reorderTabs: (activeId, overId) => {\n set((s) => {\n const oldIdx = s.tabs.findIndex((t) => t.id === activeId);\n const newIdx = s.tabs.findIndex((t) => t.id === overId);\n if (oldIdx === -1 || newIdx === -1 || oldIdx === newIdx) return s;\n const tabs = [...s.tabs];\n const [moved] = tabs.splice(oldIdx, 1);\n tabs.splice(newIdx, 0, moved);\n return { tabs };\n });\n },\n\n findTabByPathname: (pathname) => {\n return get().tabs.find((t) => t.pathname === pathname);\n },\n\n updateTabTitle: (id, title) => {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, title } : t)),\n }));\n },\n\n updateTabSearch: (id, search) => {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, search } : t)),\n }));\n },\n\n setDirty: (id, dirty) => {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, dirty } : t)),\n }));\n persist(get().tabs, get().activeTabId);\n },\n\n setMode: (id, mode) => {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, mode } : t)),\n }));\n persist(get().tabs, get().activeTabId);\n },\n\n updateTab: (id, patch) => {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, ...patch } : t)),\n }));\n persist(get().tabs, get().activeTabId);\n },\n\n setScope: (tenantId, userId) => {\n currentTenantId = tenantId;\n currentUserId = userId;\n },\n\n hydrate: () => {\n const data = loadPersisted();\n if (data) set({ tabs: data.tabs, activeTabId: data.activeTabId });\n },\n}));\n","// Embed URL-parameter contract.\n//\n// `?embed=1` renders a Declarion screen route without shell chrome, for\n// white-label iframe embedding. The query string is the single source of\n// truth for embed mode; it is parsed ONCE at bootstrap (see EmbedProvider)\n// and never re-read.\n//\n// This module is the pure, React-free parser. Keeping it separate makes the\n// param grammar unit-testable without rendering a component and lets the\n// theme bootstrap (see applyEmbedThemeFromLocation) reuse the exact same\n// parser the provider uses, so they can never diverge.\n\n/** Theme hint carried on the `theme` query param. */\nexport type EmbedTheme = \"light\" | \"dark\";\n\n/**\n * Navigation mode carried on the `nav` query param.\n *\n * - `self`: the iframe navigates internally between Declarion screen routes.\n * - `delegated`: the host owns navigation; the iframe stays put and only\n * emits intent. Defaults to `self` (see DEFAULT_NAVIGATION_MODE).\n */\nexport type EmbedNavigationMode = \"self\" | \"delegated\";\n\n/** Default navigation mode when `nav` is absent or invalid. */\nexport const DEFAULT_NAVIGATION_MODE: EmbedNavigationMode = \"self\";\n\n/** Query-param names that make up the embed contract. */\nexport const EMBED_PARAM_EMBED = \"embed\";\nexport const EMBED_PARAM_PARENT_ORIGIN = \"parent_origin\";\nexport const EMBED_PARAM_THEME = \"theme\";\nexport const EMBED_PARAM_NAV = \"nav\";\n\n/**\n * The four embed query params, in URL-stable order. `appendEmbedParams`\n * iterates this list so preserved params always appear in the same order.\n */\nexport const EMBED_PARAM_KEYS = [\n EMBED_PARAM_EMBED,\n EMBED_PARAM_PARENT_ORIGIN,\n EMBED_PARAM_THEME,\n EMBED_PARAM_NAV,\n] as const;\n\n/**\n * Parsed, validated embed context.\n *\n * `enabled` is true ONLY when `embed=1` is explicitly present. Being inside\n * an iframe (frame detection) MUST NOT enable embed mode - a Declarion app\n * framed without `embed=1` still renders the full shell.\n */\nexport interface EmbedParams {\n /** True only when `embed=1` is explicitly present in the query string. */\n readonly enabled: boolean;\n /**\n * Exact parent origin from `parent_origin`, validated as a well-formed\n * origin (scheme + host + optional port, no path/query/fragment). `null`\n * when absent, malformed, or carrying anything beyond an origin.\n */\n readonly parentOrigin: string | null;\n /** Theme hint from `theme`; any value other than light/dark drops to null. */\n readonly theme: EmbedTheme | null;\n /** Navigation mode from `nav`; invalid or absent values fall back to `self`. */\n readonly navigationMode: EmbedNavigationMode;\n}\n\n/**\n * Validate that `raw` is exactly a well-formed origin: `scheme://host[:port]`\n * with no path, query, fragment, or credentials. `new URL(raw).origin`\n * normalizes the input; requiring `raw === url.origin` rejects any extra\n * structure (`https://h.com/`, `https://h.com/path`, `https://h.com?x=1`).\n *\n * Returns the origin on success, `null` otherwise.\n */\nexport function parseExactOrigin(raw: string | null | undefined): string | null {\n if (!raw) return null;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return null;\n }\n // `url.origin` is `\"null\"` (the string) for opaque origins such as\n // `data:` / `blob:` URLs; reject those explicitly.\n if (url.origin === \"null\") return null;\n // The input must BE the origin, with nothing appended. A trailing slash,\n // path, query, fragment, username, or password all fail this equality.\n return raw === url.origin ? url.origin : null;\n}\n\n/** Normalize the `theme` param to a known value or null. */\nfunction parseTheme(raw: string | null): EmbedTheme | null {\n return raw === \"light\" || raw === \"dark\" ? raw : null;\n}\n\n/** Normalize the `nav` param to a known mode, defaulting to `self`. */\nfunction parseNavigationMode(raw: string | null): EmbedNavigationMode {\n return raw === \"delegated\" ? \"delegated\" : DEFAULT_NAVIGATION_MODE;\n}\n\n/**\n * Parse the embed contract out of a query string.\n *\n * Accepts a raw query string (`window.location.search`) or a\n * `URLSearchParams`. Pure and deterministic - the single parser used by\n * both the React provider and the pre-paint theme bootstrap.\n */\nexport function parseEmbedParams(\n query: string | URLSearchParams,\n): EmbedParams {\n const params =\n typeof query === \"string\" ? new URLSearchParams(query) : query;\n const enabled = params.get(EMBED_PARAM_EMBED) === \"1\";\n return {\n enabled,\n parentOrigin: parseExactOrigin(params.get(EMBED_PARAM_PARENT_ORIGIN)),\n theme: parseTheme(params.get(EMBED_PARAM_THEME)),\n navigationMode: parseNavigationMode(params.get(EMBED_PARAM_NAV)),\n };\n}\n\n/**\n * Given an internal Declarion URL or path, return it with the currently-set\n * embed params (`embed`, `parent_origin`, `theme`, `nav`) preserved on its\n * query string. Only params present in `current` are appended; absent ones\n * are not invented.\n *\n * Used by internal navigation so a full iframe reload of the navigated URL\n * still resolves to shellless embed mode. When embed mode is off, the input\n * is returned unchanged.\n *\n * The input may be an absolute URL or a root-relative path. A relative path\n * is resolved against a placeholder base purely so `URLSearchParams` can be\n * manipulated; the returned string keeps the input's relative shape.\n */\nexport function appendEmbedParams(url: string, current: EmbedParams): string {\n if (!current.enabled) return url;\n\n // Resolve against a placeholder base so relative paths parse. The base is\n // only used to manipulate the query string; it never appears in output\n // for relative inputs.\n const PLACEHOLDER_BASE = \"http://embed.invalid\";\n const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(url);\n let parsed: URL;\n try {\n parsed = new URL(url, PLACEHOLDER_BASE);\n } catch {\n // Unparseable input: return it untouched rather than corrupt it.\n return url;\n }\n\n const search = parsed.searchParams;\n // Preserve only params that are actually set on the current context.\n if (current.enabled) search.set(EMBED_PARAM_EMBED, \"1\");\n if (current.parentOrigin) {\n search.set(EMBED_PARAM_PARENT_ORIGIN, current.parentOrigin);\n }\n if (current.theme) search.set(EMBED_PARAM_THEME, current.theme);\n // `nav` always has a concrete value (default `self`); preserve it so a\n // reload keeps the host-selected navigation mode.\n search.set(EMBED_PARAM_NAV, current.navigationMode);\n\n if (isAbsolute) return parsed.toString();\n // Relative input: strip the placeholder base, keep path + query + hash.\n return `${parsed.pathname}${parsed.search}${parsed.hash}`;\n}\n","// Embed theme bootstrap.\n//\n// When an embed iframe carries `?embed=1&theme=dark`, the requested theme\n// MUST land on <html> BEFORE the first visible paint, so the white-label\n// host never sees a light flash before React boots.\n//\n// This mirrors the existing pre-paint appearance bootstrap in\n// `stores/appearance.ts` (the module-load `applyAppearance(initial)` call):\n// the theme is applied at module init, not in a React effect.\n//\n// Crucially, the embed theme MUST NOT be persisted to localStorage. It is a\n// per-iframe host hint, not a user choice. This reuses the precedent set by\n// `appearance.hydrateFromParams`, which applies a theme via `applyAppearance`\n// WITHOUT calling `writeStorage`.\n\nimport { applyAppearance, useAppearanceStore, type Appearance } from \"@/stores/appearance\";\nimport { parseEmbedParams, type EmbedTheme } from \"./params\";\n\n/**\n * Apply an embed theme to <html> immediately, WITHOUT persisting it.\n *\n * The single theme-apply path for embed mode: the pre-paint URL bootstrap\n * (`applyEmbedThemeFromQuery`) and the runtime `theme` postMessage handler\n * both route through here, so they can never diverge.\n *\n * `applyAppearance` is the single writer of <html> theme attributes; it\n * keeps accent / density / etc. intact. The appearance store slice is then\n * set directly (not via `setMany`) so later reads stay consistent WITHOUT\n * writing localStorage - the embed theme is a host-supplied, ephemeral hint,\n * not a user choice.\n */\nexport function applyEmbedTheme(theme: EmbedTheme): void {\n const current = useAppearanceStore.getState();\n const next: Appearance = { ...current, theme };\n applyAppearance(next);\n useAppearanceStore.setState({ theme });\n}\n\n/**\n * Read the embed `theme` param from the given query string and, when it is a\n * valid `light`/`dark` value under an active `embed=1`, apply it to\n * <html> immediately.\n *\n * Returns true when an embed theme was applied, false otherwise.\n *\n * Apply path: routes through `applyEmbedTheme`, which writes `data-theme` and\n * updates the appearance store but deliberately does NOT touch localStorage.\n */\nexport function applyEmbedThemeFromQuery(query: string | URLSearchParams): boolean {\n const params = parseEmbedParams(query);\n if (!params.enabled || params.theme === null) return false;\n\n applyEmbedTheme(params.theme);\n return true;\n}\n\n/**\n * Apply the embed theme from `window.location.search` at module load.\n *\n * Called once from the embed barrel so importing the embed entry point is\n * enough to get pre-paint theming, exactly like the appearance module's\n * own module-load `applyAppearance(initial)`.\n */\nexport function applyEmbedThemeFromLocation(): boolean {\n if (typeof window === \"undefined\") return false;\n return applyEmbedThemeFromQuery(window.location.search);\n}\n","// Embed React context.\n//\n// `EmbedProvider` wraps the entire app (above BrowserRouter) and exposes the\n// parsed embed contract through `useEmbed()`. The URL query string is parsed\n// ONCE, at provider construction, and never re-read - embed mode is a boot\n// property of the document, not a reactive value.\n//\n// `Layout`, `app.tsx`, and `stores/auth` consumers read this context to\n// branch into shellless rendering and to suppress the cookie auth probe and\n// the login redirect while an embed token is pending.\n\nimport {\n createContext,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport {\n appendEmbedParams as appendEmbedParamsPure,\n parseEmbedParams,\n type EmbedNavigationMode,\n type EmbedParams,\n type EmbedTheme,\n} from \"./params\";\n\n/**\n * The embed context value exposed by `useEmbed()`.\n *\n * Mirrors `EmbedParams` plus a bound `appendEmbedParams` helper that closes\n * over the parsed context, so callers do not pass the params back in.\n */\nexport interface EmbedContextValue {\n /** True only when `embed=1` is explicitly present. */\n readonly enabled: boolean;\n /** Exact, validated parent origin, or null. */\n readonly parentOrigin: string | null;\n /** Theme hint, or null when absent/invalid. */\n readonly theme: EmbedTheme | null;\n /** Navigation mode; defaults to `self`. */\n readonly navigationMode: EmbedNavigationMode;\n /**\n * Given an internal URL/path, return it with the active embed params\n * preserved. A no-op (returns the input unchanged) when embed mode is off.\n */\n appendEmbedParams: (url: string) => string;\n}\n\n// Default value for callers rendered outside an EmbedProvider: embed mode is\n// off, so every consumer behaves exactly as the pre-embed app did.\nconst DISABLED_EMBED: EmbedContextValue = {\n enabled: false,\n parentOrigin: null,\n theme: null,\n navigationMode: \"self\",\n appendEmbedParams: (url) => url,\n};\n\nconst EmbedContext = createContext<EmbedContextValue>(DISABLED_EMBED);\n\n/** Build an `EmbedContextValue` from parsed params. Exported for tests. */\nexport function toEmbedContextValue(params: EmbedParams): EmbedContextValue {\n return {\n enabled: params.enabled,\n parentOrigin: params.parentOrigin,\n theme: params.theme,\n navigationMode: params.navigationMode,\n appendEmbedParams: (url) => appendEmbedParamsPure(url, params),\n };\n}\n\ninterface EmbedProviderProps {\n children: ReactNode;\n /**\n * Test seam: an explicit query string overrides `window.location.search`.\n * Production code never passes this - the provider reads the live URL.\n */\n query?: string;\n}\n\n/**\n * Provides the embed context to the whole app. Mount above `BrowserRouter`\n * so routes and `Layout` can call `useEmbed()`.\n *\n * The query string is parsed once and memoized on `query` identity. In\n * production `query` is undefined and the live `window.location.search` is\n * read a single time at construction.\n */\nexport function EmbedProvider({ children, query }: EmbedProviderProps) {\n const value = useMemo<EmbedContextValue>(() => {\n const search =\n query ??\n (typeof window !== \"undefined\" ? window.location.search : \"\");\n return toEmbedContextValue(parseEmbedParams(search));\n }, [query]);\n\n return (\n <EmbedContext.Provider value={value}>{children}</EmbedContext.Provider>\n );\n}\n\n/**\n * Read the parsed embed context.\n *\n * Outside an `EmbedProvider` this returns the disabled context, so a screen\n * component rendered in isolation (tests, Storybook) behaves as non-embed.\n */\nexport function useEmbed(): EmbedContextValue {\n return useContext(EmbedContext);\n}\n","// Iframe-side embed postMessage protocol - the INBOUND half.\n//\n// `embed/protocol.ts` owns the wire envelope, the type constants, and the\n// OUTBOUND sender `postToParent`. This module owns the INBOUND side: a\n// validated handler for `message` events from the host page.\n//\n// Security model (Section 6 of the embed plan):\n// - The listener is enabled ONLY when a concrete `parentOrigin` is known.\n// - EVERY inbound message is validated on TWO axes before it is acted on:\n// 1. `event.origin === parentOrigin` - exact string match, no prefix,\n// no wildcard. A frame from any other origin is dropped.\n// 2. the envelope shape - `source === \"declarion-embed\"` and a\n// `protocol` field that is a number.\n// - A message failing either check is dropped SILENTLY. An untrusted frame\n// that re-framed the iframe must learn nothing from the iframe's logs,\n// so there is deliberately no `console.warn` on a rejected message.\n// - A message that passes origin + source but carries a DIFFERENT\n// `protocol` version produces a single clear `console.warn` naming both\n// versions - that frame IS the trusted host, just on a mismatched SDK,\n// and a silent drop there is a developer-hostile blank iframe.\n//\n// This module is React-free so the validation can be unit tested directly.\n\nimport {\n EMBED_MESSAGE_SOURCE,\n EMBED_MESSAGE_TYPES,\n EMBED_PROTOCOL_VERSION,\n type EmbedSetTokenPayload,\n type EmbedMessage,\n type EmbedMessageType,\n type EmbedNavigatePayload,\n type EmbedSetThemePayload,\n} from \"./protocol\";\n\n/**\n * The classification of an inbound `message` event after validation.\n *\n * - `valid`: a trusted, shape-correct, version-matched embed frame. `message`\n * is the typed envelope.\n * - `protocol-mismatch`: origin + source are trusted, but the `protocol`\n * version differs. The caller logs a warning; the frame is not acted on.\n * - `rejected`: the frame is not a trusted embed frame (wrong origin,\n * missing/foreign `source`, malformed envelope). The caller drops it\n * silently.\n */\nexport type InboundClassification =\n | { kind: \"valid\"; message: EmbedMessage }\n | { kind: \"protocol-mismatch\"; received: unknown }\n | { kind: \"rejected\" };\n\n/**\n * Validate and classify an inbound `message` event against the trusted\n * `parentOrigin`.\n *\n * Returns `rejected` for anything that is not a trusted embed frame - the\n * caller drops those with no logging. Returns `protocol-mismatch` when the\n * frame is trusted but on a different protocol version. Returns `valid` with\n * the typed envelope otherwise.\n *\n * `parentOrigin` MUST be a concrete origin; the inbound listener is not\n * installed at all when it is null, so this function assumes a real value.\n */\nexport function classifyInboundMessage(\n event: MessageEvent,\n parentOrigin: string,\n): InboundClassification {\n // Axis 1: exact origin match. A trailing slash, a subdomain, a different\n // port - all fail this equality and the frame is dropped.\n if (event.origin !== parentOrigin) {\n return { kind: \"rejected\" };\n }\n\n // Axis 2: envelope shape. The data must be an object carrying our\n // `source` discriminator and a numeric `protocol`.\n const data = event.data;\n if (typeof data !== \"object\" || data === null) {\n return { kind: \"rejected\" };\n }\n const envelope = data as Partial<EmbedMessage>;\n if (envelope.source !== EMBED_MESSAGE_SOURCE) {\n return { kind: \"rejected\" };\n }\n if (typeof envelope.protocol !== \"number\") {\n return { kind: \"rejected\" };\n }\n if (typeof envelope.type !== \"string\") {\n return { kind: \"rejected\" };\n }\n\n // The frame is from the trusted host and carries our envelope, but the\n // protocol version differs. Surface it loudly - this is the host SDK on a\n // mismatched version, not an attacker.\n if (envelope.protocol !== EMBED_PROTOCOL_VERSION) {\n return { kind: \"protocol-mismatch\", received: envelope.protocol };\n }\n\n return { kind: \"valid\", message: envelope as EmbedMessage };\n}\n\n/**\n * The set of inbound-message callbacks the embed bootstrap supplies. Each is\n * invoked only for a `valid`, version-matched frame of the matching type.\n */\nexport interface InboundHandlers {\n /** A host `auth` frame: store/refresh the embed token. */\n onAuth: (payload: EmbedSetTokenPayload) => void;\n /** A host `navigate` frame: drive the iframe to a screen route. */\n onNavigate: (payload: EmbedNavigatePayload) => void;\n /** A host `theme` frame: switch the iframe theme at runtime. */\n onTheme: (payload: EmbedSetThemePayload) => void;\n}\n\n/**\n * Build the `message` event listener for the inbound embed protocol.\n *\n * The returned listener validates every event via `classifyInboundMessage`,\n * drops untrusted frames silently, warns once on a protocol mismatch, and\n * dispatches a valid frame to the matching handler.\n *\n * Returned as a standalone function (not auto-attached) so the React\n * bootstrap owns `addEventListener` / `removeEventListener` lifecycle.\n */\nexport function createInboundMessageListener(\n parentOrigin: string,\n handlers: InboundHandlers,\n): (event: MessageEvent) => void {\n return (event: MessageEvent): void => {\n const classified = classifyInboundMessage(event, parentOrigin);\n\n if (classified.kind === \"rejected\") {\n // Untrusted or malformed frame. Drop silently - no logging.\n return;\n }\n\n if (classified.kind === \"protocol-mismatch\") {\n console.warn(\n `[declarion-embed] protocol version mismatch: this iframe speaks ` +\n `protocol ${EMBED_PROTOCOL_VERSION}, the host sent protocol ` +\n `${String(classified.received)}. Update the host @declarion/embed ` +\n `SDK and the Declarion deployment to matching versions.`,\n );\n return;\n }\n\n const { message } = classified;\n switch (message.type as EmbedMessageType) {\n case EMBED_MESSAGE_TYPES.setToken:\n handlers.onAuth(message.payload as EmbedSetTokenPayload);\n break;\n case EMBED_MESSAGE_TYPES.navigate:\n handlers.onNavigate(message.payload as EmbedNavigatePayload);\n break;\n case EMBED_MESSAGE_TYPES.setTheme:\n handlers.onTheme(message.payload as EmbedSetThemePayload);\n break;\n default:\n // Inbound frame of a type the iframe does not consume (e.g. an\n // echo of an outbound-only type). Ignore it - not an error.\n break;\n }\n };\n}\n","// Embed auth handshake bootstrap.\n//\n// In cross-site embed mode the iframe boots with NO credentials. It cannot\n// run the cookie probe (the cookie would carry the viewer's full-authority\n// session - see app.tsx), so it authenticates through the postMessage\n// handshake instead:\n//\n// 1. On mount the iframe emits `ready` to the host.\n// 2. The host replies with an `auth { token, expires_at }` frame.\n// 3. The iframe stores the token, hydrates the user via `auth.me` over the\n// Bearer transport, and sets the auth store authenticated.\n// 4. With the auth store authenticated, app.tsx releases the neutral blank\n// and the screen renders.\n// 5. Every later `auth` frame just refreshes the in-memory token.\n//\n// It also installs the inbound protocol listener (`auth` / `navigate` /\n// `theme`), replaces the persistent-auth-failure handler so a terminal auth\n// failure notifies the host instead of navigating the iframe to the\n// Declarion login page, and surfaces an error state if no `auth` frame\n// arrives within a bounded window after `ready`.\n//\n// Mounted ABOVE the app.tsx auth-pending gate (in AppShell), so it runs the\n// handshake during the blank-pending window. Inert outside embed mode.\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useEmbed } from \"./EmbedContext\";\nimport { initEmbedTokenMode, setEmbedToken } from \"./token\";\nimport { createInboundMessageListener } from \"./handshake\";\nimport { applyEmbedTheme } from \"./theme\";\nimport { EMBED_MESSAGE_TYPES, postToParent } from \"./protocol\";\nimport type { EmbedSetTokenPayload } from \"./protocol\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { getMe } from \"@/api/auth\";\nimport { setPersistentAuthFailureHandler } from \"@/api/client\";\n\n/**\n * How long the iframe waits for the first `auth` frame after emitting\n * `ready`. The host's `getToken` callback plus a backend mint round-trip is\n * normally fast; a bound is required so a misconfigured host (wrong origin,\n * `getToken` never resolving) surfaces a visible error instead of an\n * indefinite blank pending screen.\n */\nconst FIRST_AUTH_TIMEOUT_MS = 20_000;\n\n/** The handshake lifecycle state surfaced to the embed render path. */\ntype HandshakeState = \"pending\" | \"authenticated\" | \"error\";\n\n/**\n * Runs the embed auth handshake. Renders a minimal error panel if the host\n * never delivers an `auth` frame; otherwise renders nothing (the app shell\n * renders the screen once the auth store is authenticated).\n *\n * Returns immediately (renders nothing) outside embed mode.\n */\nexport function EmbedAuthBootstrap() {\n const embed = useEmbed();\n const navigate = useNavigate();\n const setUser = useAuthStore((s) => s.setUser);\n const [state, setState] = useState<HandshakeState>(\"pending\");\n\n // The handshake runs exactly once per iframe load. A ref guards the\n // first-`auth` hydration so React StrictMode's double effect invocation\n // and any later `auth` frame do not re-run `auth.me`.\n const hydratedRef = useRef(false);\n\n useEffect(() => {\n if (!embed.enabled) return undefined;\n\n // Activate embed token mode for every transport (fetch, SSE, upload,\n // version probe). `parentOrigin` is where `token-expired` and\n // `reload-required` frames are emitted.\n initEmbedTokenMode(embed.parentOrigin);\n\n // Replace the persistent-auth-failure handler. The default navigates\n // the iframe to the Declarion login page; in embed mode a terminal\n // failure must instead ask the host to re-mint (`token-expired`) or\n // reload the iframe (`reload-required`). The host owns recovery.\n const prevAuthFailureHandler = setPersistentAuthFailureHandler(() => {\n postToParent(\n embed.parentOrigin,\n EMBED_MESSAGE_TYPES.reloadRequired,\n { reason: \"embed session terminated\" },\n );\n });\n\n // hydrateFromFirstAuth runs once: it stores the token, fetches the user\n // over the Bearer transport, and sets the auth store authenticated so\n // app.tsx releases the blank pending screen.\n const hydrateFromFirstAuth = async () => {\n if (hydratedRef.current) return;\n hydratedRef.current = true;\n try {\n const user = await getMe();\n setUser(user, user.tenant ?? undefined);\n setState(\"authenticated\");\n } catch {\n // The first authenticated call failed even though a token was\n // delivered (revoked session, scope mismatch, network). Surface\n // the error state rather than hang on the blank.\n setState(\"error\");\n }\n };\n\n const onAuth = (payload: EmbedSetTokenPayload) => {\n // Store/refresh the token. This also resolves any in-flight\n // `awaitEmbedToken()` wait (a transport blocked on a fresh token).\n setEmbedToken(payload);\n if (!hydratedRef.current) {\n void hydrateFromFirstAuth();\n }\n };\n\n const listener = embed.parentOrigin\n ? createInboundMessageListener(embed.parentOrigin, {\n onAuth,\n onNavigate: (payload) => {\n // The host drives the iframe to a screen. Works in both nav\n // modes for deep-linking; the embed params are preserved so a\n // full reload of the target URL stays shellless.\n navigate(embed.appendEmbedParams(payload.route));\n },\n onTheme: (payload) => {\n // Runtime theme switch, applied without persisting (the embed\n // theme is a host hint, not a user choice).\n applyEmbedTheme(payload.theme);\n },\n })\n : null;\n\n if (listener) {\n window.addEventListener(\"message\", listener);\n }\n\n // Emit `ready` so the host delivers the first token. Posted after the\n // listener is attached so an instant `auth` reply is never missed.\n postToParent(embed.parentOrigin, EMBED_MESSAGE_TYPES.ready, {});\n\n // Bound the wait for the first `auth` frame.\n const timeoutHandle = window.setTimeout(() => {\n if (!hydratedRef.current) {\n setState(\"error\");\n }\n }, FIRST_AUTH_TIMEOUT_MS);\n\n return () => {\n if (listener) {\n window.removeEventListener(\"message\", listener);\n }\n window.clearTimeout(timeoutHandle);\n setPersistentAuthFailureHandler(prevAuthFailureHandler);\n };\n // The embed contract is parsed once at bootstrap and never changes, so\n // the dependency list is stable; `navigate` and `setUser` are stable\n // references from their respective libraries.\n }, [embed, navigate, setUser]);\n\n if (!embed.enabled || state !== \"error\") return null;\n\n // The host never completed the handshake. Surface a minimal, neutral\n // error panel so the iframe is not a silent blank. The host's own\n // diagnostics (the @declarion/embed SDK `onError`) carry the actionable\n // detail; this panel is the in-iframe fallback.\n //\n // Rendered as a fixed full-viewport overlay so it sits ON TOP of the\n // neutral blank that AppRoutes still holds while `!isAuthenticated`\n // (the bootstrap mounts as a sibling of AppRoutes, not inside it).\n return (\n <div\n role=\"alert\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-background p-6 text-center text-sm text-muted-foreground\"\n >\n <p>\n This embedded view could not authenticate. The host application did\n not provide a session.\n </p>\n </div>\n );\n}\n","// Embed content-size mirror.\n//\n// A white-label host sizing its iframe to the embedded screen needs the\n// current content height. This component observes the embed content root\n// with a `ResizeObserver` and emits a `resize { height }` frame on every\n// change, debounced through `requestAnimationFrame` so a burst of layout\n// changes collapses to one frame per paint.\n//\n// Mounted only inside `Layout`'s embed branch. Renders nothing.\n\nimport { useEffect } from \"react\";\nimport { useEmbed } from \"./EmbedContext\";\nimport { EMBED_MESSAGE_TYPES, postToParent } from \"./protocol\";\n\n/**\n * The DOM id of the embed content root. `Layout`'s embed branch renders the\n * single screen content under `<div id=\"app-embed\">`; the observer measures\n * that element so the reported height is exactly the embedded screen.\n */\nconst EMBED_CONTENT_ROOT_ID = \"app-embed\";\n\n/**\n * Observes the embed content root and emits `resize` to the host on every\n * height change. Inert outside embed mode and when no `parentOrigin` is set\n * (nothing to emit to).\n */\nexport function EmbedResizeReporter() {\n const embed = useEmbed();\n const active = embed.enabled && embed.parentOrigin !== null;\n\n useEffect(() => {\n if (!active) return undefined;\n if (typeof window === \"undefined\") return undefined;\n if (typeof ResizeObserver === \"undefined\") return undefined;\n\n const root = document.getElementById(EMBED_CONTENT_ROOT_ID);\n if (!root) return undefined;\n\n let rafHandle: number | null = null;\n let lastHeight = -1;\n\n const emit = () => {\n rafHandle = null;\n // `scrollHeight` is the full content height including overflow, which\n // is what the host must size the iframe to. `getBoundingClientRect`\n // would clip to the visible box.\n const height = root.scrollHeight;\n if (height === lastHeight) return;\n lastHeight = height;\n postToParent(embed.parentOrigin, EMBED_MESSAGE_TYPES.resized, { height });\n };\n\n const observer = new ResizeObserver(() => {\n // Debounce to one emission per animation frame: a single layout pass\n // can fire many ResizeObserver callbacks.\n if (rafHandle !== null) return;\n rafHandle = window.requestAnimationFrame(emit);\n });\n observer.observe(root);\n\n return () => {\n observer.disconnect();\n if (rafHandle !== null) window.cancelAnimationFrame(rafHandle);\n };\n }, [active, embed.parentOrigin]);\n\n return null;\n}\n","// Embed-mode unsaved-changes channel.\n//\n// The platform guards dirty edits only at tab-close (`DirtyTabsCloseDialog`,\n// `tab-close.ts`, `TabItem.dirty`). Embed mode removes the tab UI, so that\n// guard never fires. This module is the embed-mode replacement: a focused\n// store the editing screen writes its dirty state into, and the route-level\n// guard (`EmbedUnsavedGuard`) reads.\n//\n// It is deliberately a tiny standalone store, NOT the tabs store: embed mode\n// has no tabs, and overloading `TabItem.dirty` with no tab to attach it to\n// would be a camouflaged hack. The store is keyed by nothing - embed mode\n// renders exactly one screen at a time, so a single boolean is the whole\n// state.\n\nimport { useEffect } from \"react\";\nimport { create } from \"zustand\";\n\ninterface EmbedDirtyState {\n /** True when the embedded screen has unsaved edits. */\n readonly isDirty: boolean;\n /** Set the dirty flag. Called by the editing screen. */\n setDirty: (dirty: boolean) => void;\n}\n\n/**\n * The embed-mode dirty store. The editing screen reports its dirty state\n * here; `EmbedUnsavedGuard` reads `isDirty` to decide whether to block a\n * route change.\n */\nexport const useEmbedDirtyStore = create<EmbedDirtyState>((set) => ({\n isDirty: false,\n setDirty: (dirty) =>\n set((s) => (s.isDirty === dirty ? s : { isDirty: dirty })),\n}));\n\n/**\n * Report the embedded screen's current dirty state into the embed-mode dirty\n * channel for the lifetime of the calling component.\n *\n * The flag is reset to clean on unmount, so a screen that navigates away\n * (and unmounts) never leaves a stale dirty flag behind for the next screen.\n *\n * `active` lets a caller cheaply opt out (e.g. only report when embed mode is\n * on); when false the hook is fully inert.\n */\nexport function useReportEmbedDirty(dirty: boolean, active: boolean): void {\n const setDirty = useEmbedDirtyStore((s) => s.setDirty);\n useEffect(() => {\n if (!active) return;\n setDirty(dirty);\n }, [active, dirty, setDirty]);\n useEffect(() => {\n if (!active) return undefined;\n return () => setDirty(false);\n }, [active, setDirty]);\n}\n","// Embed unsaved-changes mirror.\n//\n// A white-label host guards its OWN navigation - its menu, a host-initiated\n// `navigate` frame - before moving the iframe away from a screen with unsaved\n// edits. To do that the host must know whether the embedded screen is dirty.\n// This component watches the embed-mode dirty store and emits a `dirty-changed`\n// frame to the host whenever that state flips; the host tracks the boolean and\n// shows its own confirm before it navigates.\n//\n// Division of responsibility: the iframe-side guard (`EmbedUnsavedGuard`)\n// covers navigation that happens INSIDE the iframe (a row click, a Back\n// button), where popping a dialog in the iframe is correct because the host\n// chrome did not move. This reporter covers the other half - navigation the\n// HOST initiates - which the iframe cannot intercept and must not pop a dialog\n// for. See docs/ui-sdk.md \"Embed navigation and unsaved changes\".\n//\n// Mounted only inside `Layout`'s embed branch. Renders nothing.\n\nimport { useEffect } from \"react\";\nimport { useEmbed } from \"./EmbedContext\";\nimport { useEmbedDirtyStore } from \"./embed-dirty\";\nimport { EMBED_MESSAGE_TYPES, postToParent } from \"./protocol\";\n\n/**\n * Emits `dirty-changed` to the host whenever the embedded screen's\n * unsaved-edits state flips. Inert outside embed mode and when no\n * `parentOrigin` is set (nothing to emit to).\n *\n * `useEmbedDirtyStore` only updates `isDirty` on a real transition, so the\n * effect fires once per flip - plus once on mount, reporting the initial\n * clean state so the host starts from a known value rather than an assumption.\n */\nexport function EmbedDirtyReporter() {\n const embed = useEmbed();\n const isDirty = useEmbedDirtyStore((s) => s.isDirty);\n const active = embed.enabled && embed.parentOrigin !== null;\n\n useEffect(() => {\n if (!active) return;\n postToParent(embed.parentOrigin, EMBED_MESSAGE_TYPES.dirtyChanged, {\n dirty: isDirty,\n });\n }, [active, isDirty, embed.parentOrigin]);\n\n return null;\n}\n","// Embed navigation-payload resolver.\n//\n// The `navigated` and `navigation-requested` protocol messages carry\n// `{ route, entity, recordId }` so the host can mirror its URL, breadcrumb,\n// and back button. This module turns a router pathname plus the screen\n// schema into that payload.\n//\n// Pure and React-free: it takes the screen map explicitly so it can be unit\n// tested without rendering, and so both the `self`-mode location reporter and\n// the `delegated`-mode navigate helper resolve payloads identically.\n\nimport type { EmbedNavigationPayload } from \"./protocol\";\nimport type { Screen } from \"@/types/schema\";\n\n/**\n * Turn a route pattern (`/cases/:id`) into an anchored matcher. Literal\n * segments are regex-escaped; a `:param` segment matches any single\n * non-slash path segment. A trailing slash on the pattern is ignored.\n *\n * Kept local rather than imported from `types/schema` because that module's\n * `routeToRegex` is not exported; duplicating six lines is cheaper than\n * widening that module's public surface for one internal caller.\n */\nfunction routeToRegex(route: string): RegExp {\n const segs = route\n .replace(/\\/+$/, \"\")\n .split(\"/\")\n .map((s) =>\n s.startsWith(\":\") ? \"[^/]+\" : s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n );\n return new RegExp(\"^\" + segs.join(\"/\") + \"$\");\n}\n\n/**\n * The result of resolving a router pathname against the screen schema.\n *\n * `matched` is the load-bearing flag for the navigation chokepoint: when a\n * registered screen owns the path it is `true` and `screenCode` names that\n * screen; when no screen owns the path it is `false` (the chokepoint then\n * refuses an embed navigation and emits `navigation-failed`). `payload` is\n * always present - it carries the matched screen route (or the raw pathname\n * when unmatched) for the `navigated` / `navigation-requested` messages.\n */\nexport interface EmbedNavigationResolution {\n /** True when a registered screen route owns this pathname. */\n readonly matched: boolean;\n /** The matched screen's code (its key in `screens`), when matched. */\n readonly screenCode?: string;\n /** The matched screen's `type` (`list` / `detail` / `custom` / ...), when\n * matched. Matched-screen metadata the host may use (e.g. to label or\n * classify a navigation it observes). */\n readonly screenType?: string;\n /** The navigation payload to emit (route always set; ids when resolvable). */\n readonly payload: EmbedNavigationPayload;\n}\n\n/**\n * Resolve a router pathname against the screen schema, with match status.\n *\n * Matches the pathname against every registered screen route. An exact\n * (wildcard-free) route wins over a `:param` route, mirroring\n * `screenTitleForPath`, so `/cases` resolves to the list screen even though\n * the detail route `/cases/:id` also shares the prefix.\n *\n * - `matched` is true only when a registered screen owns the path.\n * - `screenCode` is the matched screen's map key (so the host can mint a\n * scoped token for it).\n * - `payload.route` is the matched screen's own route pattern, or the raw\n * pathname when no screen owns it (a route always exists in the payload).\n * - `payload.entity` is the matched screen's bound entity code, when it has one.\n * - `payload.recordId` is the last path segment for a detail (`:param`) route,\n * when that segment is present and non-empty.\n *\n * `screens` may be undefined (schema not yet loaded); the result is then\n * `matched: false` with the raw pathname as `payload.route`.\n */\nexport function resolveEmbedNavigation(\n pathname: string,\n screens: Record<string, Screen> | undefined,\n): EmbedNavigationResolution {\n const norm = pathname.replace(/\\/+$/, \"\") || \"/\";\n\n if (!screens) {\n return { matched: false, payload: { route: pathname } };\n }\n\n let exact: Screen | undefined;\n let exactCode: string | undefined;\n let wildcard: Screen | undefined;\n let wildcardCode: string | undefined;\n for (const [code, screen] of Object.entries(screens)) {\n if (!screen.route) continue;\n if (!routeToRegex(screen.route).test(norm)) continue;\n if (screen.route.includes(\"/:\")) {\n if (!wildcard) {\n wildcard = screen;\n wildcardCode = code;\n }\n } else {\n exact = screen;\n exactCode = code;\n break; // exact match wins; stop scanning\n }\n }\n\n const matched = exact ?? wildcard;\n const matchedCode = exact ? exactCode : wildcardCode;\n if (!matched || !matchedCode) {\n // No screen owns this path - report the raw pathname, no screen/entity/id.\n return { matched: false, payload: { route: pathname } };\n }\n\n const payload: {\n route: string;\n screenCode?: string;\n entity?: string;\n recordId?: string;\n } = {\n route: matched.route,\n screenCode: matchedCode,\n };\n if (matched.entity) {\n payload.entity = matched.entity;\n }\n // A detail route (`:param`) carries a record id as its last path segment.\n if (matched.route.includes(\"/:\")) {\n const lastSegment = norm.slice(norm.lastIndexOf(\"/\") + 1);\n if (lastSegment) {\n payload.recordId = lastSegment;\n }\n }\n return { matched: true, screenCode: matchedCode, screenType: matched.type, payload };\n}\n\n/**\n * Convenience wrapper returning just the payload, for the `navigated` and\n * `navigation-requested` emitters that do not need the match status.\n */\nexport function resolveEmbedNavigationPayload(\n pathname: string,\n screens: Record<string, Screen> | undefined,\n): EmbedNavigationPayload {\n return resolveEmbedNavigation(pathname, screens).payload;\n}\n","// Embed-mode pending-navigation channel for the unsaved-changes guard.\n//\n// React Router's `useBlocker` only works inside a DATA router\n// (`createBrowserRouter` / `RouterProvider`). The Declarion app mounts a\n// plain `<BrowserRouter>` (app.tsx), where `useBlocker` throws\n// \"useBlocker must be used within a data router\" - which crashed the entire\n// embed render path. The guard is therefore router-agnostic: instead of\n// asking the router to block, the embed navigation primitive\n// (`useEmbedScreenNavigate`) consults this channel and DEFERS a dirty\n// navigation here as a thunk; `EmbedUnsavedGuard` renders the confirm dialog\n// and either runs the thunk (discard + leave) or drops it (stay).\n//\n// This is a tiny standalone store, like `embed-dirty.ts`. Embed mode renders\n// exactly one screen at a time, so a single pending thunk is the whole state.\n\nimport { create } from \"zustand\";\n\ninterface EmbedNavGuardState {\n /**\n * The deferred navigation, or null when nothing is pending. Set by\n * `useEmbedScreenNavigate` when it intercepts a dirty navigation; consumed\n * by `EmbedUnsavedGuard`.\n */\n readonly pendingNavigation: (() => void) | null;\n /** Defer a navigation behind the unsaved-changes confirm dialog. */\n requestNavigation: (run: () => void) => void;\n /** Run the deferred navigation (the user chose discard + leave). */\n confirmNavigation: () => void;\n /** Drop the deferred navigation (the user chose to stay). */\n cancelNavigation: () => void;\n}\n\n/**\n * The embed-mode pending-navigation store. A non-null `pendingNavigation`\n * means a dirty navigation is waiting on the confirm dialog.\n */\nexport const useEmbedNavGuardStore = create<EmbedNavGuardState>((set, get) => ({\n pendingNavigation: null,\n requestNavigation: (run) => set({ pendingNavigation: run }),\n confirmNavigation: () => {\n const run = get().pendingNavigation;\n // Clear BEFORE running so the navigation cannot re-enter the guard.\n set({ pendingNavigation: null });\n run?.();\n },\n cancelNavigation: () => set({ pendingNavigation: null }),\n}));\n","// Embed-aware screen navigation.\n//\n// `useEmbedNavigate` is the single navigation primitive every internal\n// screen-route transition must use, so embed mode is preserved consistently.\n// It wraps React Router's `useNavigate` and branches on the embed context:\n//\n// - Outside embed mode: identical to `navigate` - the input path and\n// options pass straight through, byte-for-byte the pre-embed behavior.\n// - Embed mode, `self`: navigate to the same path with the embed query\n// params (`embed`, `parent_origin`, `theme`, `nav`) preserved, so a full\n// iframe reload of the navigated URL still resolves shellless.\n// - Embed mode, `delegated`: do NOT navigate. Emit a `navigation-requested`\n// message to the host and leave the current screen mounted; the host\n// decides what to open.\n//\n// `useTabNavigate` reuses the same param-preservation / delegated-interception\n// logic (see useEmbedScreenNavigate) so the tab-aware and raw navigation\n// surfaces behave identically in embed mode.\n\nimport { useCallback } from \"react\";\nimport { useLocation, useNavigate, type NavigateOptions } from \"react-router-dom\";\nimport { useEmbed } from \"./EmbedContext\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { resolveEmbedNavigation } from \"./screen-location\";\nimport {\n EMBED_MESSAGE_TYPES,\n postToParent,\n type EmbedNavigationFailedPayload,\n} from \"./protocol\";\nimport { useEmbedDirtyStore } from \"./embed-dirty\";\nimport { useEmbedNavGuardStore } from \"./embed-nav-guard\";\n\n/** Strip the query/hash so a path-only comparison ignores embed params. */\nfunction pathOf(to: string): string {\n const q = to.search(/[?#]/);\n return q === -1 ? to : to.slice(0, q);\n}\n\n/**\n * The shared embed navigation policy, decoupled from React Router's hook.\n *\n * `rawNavigate` is the underlying navigator (`useNavigate()`'s return value\n * for `useEmbedNavigate`, the same for `useTabNavigate`). The returned\n * `screenNavigate` applies the embed branch and reports whether it actually\n * navigated:\n *\n * - returns `false` in `delegated` embed mode (intercepted; iframe stayed),\n * - returns `true` otherwise (a real navigation happened).\n *\n * Callers in embed mode use the boolean to skip side effects (opening a tab)\n * that only make sense for a navigation that actually moved.\n */\nexport function useEmbedScreenNavigate(): (\n to: string,\n opts?: NavigateOptions,\n) => boolean {\n const navigate = useNavigate();\n const location = useLocation();\n const embed = useEmbed();\n const { data: schema } = useSchema();\n\n return useCallback(\n (to: string, opts?: NavigateOptions): boolean => {\n // Non-embed: identity behavior. No params appended, plain navigate.\n if (!embed.enabled) {\n navigate(to, opts);\n return true;\n }\n\n const screens = schema?.screens;\n const target = resolveEmbedNavigation(pathOf(to), screens);\n const isDirtyPathChange =\n useEmbedDirtyStore.getState().isDirty && pathOf(to) !== location.pathname;\n\n // Guard: a target that matches no registered screen would 404 or escape\n // the embed (the leak this whole boundary closes). Refuse it - stay put,\n // emit `navigation-failed` so the host can recover - rather than letting\n // the iframe load an unscoped route. Only enforce once the schema is\n // loaded; before that the embed holds a blank and does not navigate.\n if (screens && !target.matched) {\n postToParent(embed.parentOrigin, EMBED_MESSAGE_TYPES.navigationFailed, {\n route: pathOf(to),\n reason: \"unresolved\",\n });\n return false;\n }\n\n // Decide self vs delegate. `self` mode self-routes EVERY in-frame link -\n // ref-links and list/detail drill-downs alike - because the embed token is\n // user-scoped (the user's full permissions), so the iframe is already\n // authorized to open anywhere the user can go. A link is a link; there is\n // no same-surface/cross-surface split. The central `navigated` message\n // (EmbedLocationReporter) lets the host mirror its own sidebar if it wants.\n // `delegated` mode never moves: it emits `navigation-requested` and lets\n // the host drive.\n const delegate = embed.navigationMode === \"delegated\";\n\n // Delegated: the host owns navigation. Emit intent (carrying the resolved\n // screenCode so the host can mint a scoped token), do not move.\n if (delegate) {\n const performNavigateIntent = (): void => {\n postToParent(\n embed.parentOrigin,\n EMBED_MESSAGE_TYPES.navigationRequested,\n target.payload,\n );\n };\n if (isDirtyPathChange) {\n useEmbedNavGuardStore.getState().requestNavigation(performNavigateIntent);\n return false;\n }\n performNavigateIntent();\n return false;\n }\n\n // Self mode: navigate internally, preserving the embed\n // params so a full reload of the destination URL returns shellless. The\n // `navigated` message is emitted centrally by EmbedLocationReporter\n // watching the router location, so it fires for every navigation\n // regardless of which call site triggered it.\n const performNavigate = (): void => {\n navigate(embed.appendEmbedParams(to), opts);\n };\n\n // Unsaved-changes guard: when the embedded screen is dirty and the\n // navigation changes the path, defer it behind the confirm dialog\n // (EmbedUnsavedGuard). The platform otherwise guards dirty edits only\n // at tab-close, which embed mode removes. This is the router-agnostic\n // replacement for `useBlocker` (which requires a data router; the app\n // mounts a plain BrowserRouter).\n if (isDirtyPathChange) {\n useEmbedNavGuardStore.getState().requestNavigation(performNavigate);\n return false;\n }\n\n performNavigate();\n return true;\n },\n [navigate, location.pathname, embed, schema?.screens],\n );\n}\n\n/**\n * Embed-aware replacement for `useNavigate` at internal screen-route call\n * sites. Returns a `navigate(to, opts)` function with the embed branch\n * applied. Outside embed mode it is `useNavigate()` unchanged.\n *\n * Use this for every raw screen transition (cancel-edit, save success,\n * delete success, Back). Do NOT use it for non-route navigation (anchors,\n * external URLs); those are not screen routes and embed mode does not touch\n * them.\n */\nexport function useEmbedNavigate(): (to: string, opts?: NavigateOptions) => void {\n const screenNavigate = useEmbedScreenNavigate();\n return useCallback(\n (to: string, opts?: NavigateOptions) => {\n screenNavigate(to, opts);\n },\n [screenNavigate],\n );\n}\n\n/**\n * Emit a `navigation-failed` event to the host. Used where an in-frame\n * navigation cannot be handled OUTSIDE the chokepoint - chiefly the embedded\n * 404 page, which the iframe reaches via a host-driven `navigate` command, a\n * bad initial route, or a hard reload (none of which pass through the\n * chokepoint's own guard). A no-op outside embed mode.\n */\nexport function useEmbedNavigationFailed(): (\n route: string,\n reason: EmbedNavigationFailedPayload[\"reason\"],\n) => void {\n const embed = useEmbed();\n return useCallback(\n (route, reason) => {\n if (!embed.enabled) return;\n postToParent(embed.parentOrigin, EMBED_MESSAGE_TYPES.navigationFailed, {\n route,\n reason,\n });\n },\n [embed.enabled, embed.parentOrigin],\n );\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n// isModifiedClick reports whether a click should be left to the browser\n// rather than intercepted for in-app (SPA / tab) navigation. True when any\n// modifier key is held (cmd/ctrl/shift/alt) or the click is not the primary\n// mouse button - those are the browser-native \"open in new tab/window\"\n// gestures. Any element rendered as <a href> that intercepts onClick for\n// SPA navigation MUST consult this so cmd-click / middle-click still open a\n// new tab.\n//\n// Single home for the check: the ref-link cell factory and the sidebar both\n// route through here. Keeping it inline in each call site let the two copies\n// drift - the sidebar copy also checked altKey, the ref-link copy did not.\nexport function isModifiedClick(e: {\n metaKey: boolean;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n button: number;\n}): boolean {\n return e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0;\n}\n","// Global link-click interceptor - the single entry point for in-app anchor\n// navigation.\n//\n// One capture-phase `click` listener on `document` catches EVERY anchor\n// activation - table cells, detail chips, ref links, identity links, and\n// crucially the anchors WE do not author (markdown / rich-text render arbitrary\n// `<a href>` from user content) - and routes the internal ones through\n// `useTabNavigate`. That gives ONE uniform behavior for every internal link:\n// - standalone: open (or reuse) an in-APP tab, exactly like a row click. No\n// browser tab is ever opened for an internal link.\n// - embed: `useTabNavigate` collapses to the embed navigation chokepoint\n// (`self` navigates in place with embed params; `delegated` emits\n// `navigation-requested` and stays put; an unresolved route is refused) -\n// no app tabs exist in embed.\n// External links (cross-origin, `target=_blank`, `rel=external`, mailto:/tel:)\n// are NOT intercepted - the browser opens them in a new window as usual. This\n// is what makes the embed boundary total: in embed mode a plain left-click can\n// never trigger a full-document load that escapes the iframe, whatever rendered\n// the anchor.\n//\n// Design notes:\n// - ONE listener, zero per-cell handlers, zero extra DOM: the virtualized\n// list is untouched. This is event delegation, the standard for large /\n// dynamic DOMs.\n// - `document`, not the React root: portaled content (Dropdown, HoverCard)\n// mounts to `document.body`, outside a root-scoped listener; a ref link in\n// a dropdown would otherwise escape.\n// - capture phase so an inner `stopPropagation` cannot suppress us. We still\n// bail on `event.defaultPrevented` to honor a PRIOR native capture\n// listener. We do NOT treat `defaultPrevented` as a React opt-out - React's\n// bubble handlers run AFTER this capture listener, so their `preventDefault`\n// has not happened yet. A React-router `<Link>` that must keep its native\n// semantics opts out with `data-declarion-native-link` (see predicate).\n// - real `<a href>` is preserved everywhere, so middle-click, ctrl/cmd-click,\n// right-click \"Copy link address\" / \"Open in new tab\", and screen-reader\n// link semantics all keep working natively - we only intercept the plain\n// left-click.\n// - the predicate is pure and exported for unit testing.\n\nimport { useEffect, useRef } from \"react\";\nimport { isModifiedClick } from \"@/lib/utils\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\n\n/**\n * Attribute that opts an anchor out of interception. A react-router `<Link>`\n * (or any anchor) that must keep native / router-managed click semantics\n * (relative resolution, `replace`, navigation `state`) carries this and the\n * interceptor leaves it to the browser / the router's own onClick.\n */\nexport const NATIVE_LINK_ATTR = \"data-declarion-native-link\";\n\n/** A clicked anchor, HTML or SVG. */\ntype AnyAnchor = HTMLAnchorElement | SVGAElement;\n\nfunction isAnchor(node: EventTarget): node is AnyAnchor {\n return (\n node instanceof HTMLAnchorElement ||\n (typeof SVGAElement !== \"undefined\" && node instanceof SVGAElement)\n );\n}\n\n/**\n * The nearest anchor on the event's composed path. `composedPath()` is used\n * (not `closest()` from `event.target`) so anchors inside open shadow trees\n * resolve - `event.target` is retargeted to the shadow host and would miss\n * them. Closed shadow roots stay opaque, which is acceptable.\n */\nfunction findAnchor(event: MouseEvent): AnyAnchor | null {\n for (const node of event.composedPath()) {\n if (isAnchor(node)) return node;\n }\n return null;\n}\n\n/** The anchor's raw href attribute (SVG `href` is `xlink:href`-compatible). */\nfunction anchorHref(anchor: AnyAnchor): string | null {\n if (anchor instanceof HTMLAnchorElement) {\n // Empty `href=\"\"` resolves to the current document - not a navigation.\n return anchor.getAttribute(\"href\");\n }\n // SVGAElement: `href` is an SVGAnimatedString; fall back to the attribute.\n return (\n anchor.href?.baseVal ??\n anchor.getAttribute(\"href\") ??\n anchor.getAttribute(\"xlink:href\")\n );\n}\n\n/**\n * Decide whether a click should be intercepted for in-app navigation, and to\n * where. Returns the in-app path (`pathname + search + hash`) to navigate to,\n * or `null` to leave the click to the browser.\n *\n * Pure (no side effects, no `preventDefault`): the caller prevents default and\n * routes. The guard order is cheap-first so the common \"not for us\" click\n * exits immediately. Mirrors React Router `shouldProcessLinkClick`, Turbo\n * `LinkClickObserver`, and the SvelteKit client router.\n */\nexport function interceptedNavigationTarget(\n event: MouseEvent,\n loc: { origin: string; pathname: string; search: string } = window.location,\n): string | null {\n // 1. A prior (native capture) handler opted out.\n if (event.defaultPrevented) return null;\n // 2-3. Primary button only, no modifier keys. Middle/right fire `auxclick`,\n // not `click`, so they never reach here; guard anyway for old engines.\n if (isModifiedClick(event)) return null;\n // 4. Resolve the anchor (shadow-DOM-safe).\n const anchor = findAnchor(event);\n if (!anchor) return null;\n // 5. Explicit native / router opt-out.\n if (anchor.hasAttribute(NATIVE_LINK_ATTR)) return null;\n // 6. A usable href.\n const rawHref = anchorHref(anchor);\n if (!rawHref) return null;\n // 7. New browsing context (`_blank`, `_parent`, `_top`, named frame).\n const target = (anchor.getAttribute(\"target\") ?? \"\").trim();\n if (target && target !== \"_self\") return null;\n // 8. A file save, not a navigation.\n if (anchor.hasAttribute(\"download\")) return null;\n // 9. Author asked for a full navigation.\n const rel = anchor.getAttribute(\"rel\") ?? \"\";\n if (/(^|\\s)external(\\s|$)/.test(rel)) return null;\n\n // Resolve a relative href against the CURRENT location (so origin and path\n // comparisons below are consistent with `loc`).\n let url: URL;\n try {\n url = new URL(rawHref, `${loc.origin}${loc.pathname}${loc.search}`);\n } catch {\n return null;\n }\n // 10. Routable scheme only (skip mailto:/tel:/javascript:/blob:/data:).\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n // 11. Same origin - a client router cannot handle cross-origin (the sandbox\n // contains those). NOTE: the same-origin check IS our app-ownership boundary.\n // The plan floated a separate DOM-containment \"app-root\" filter, but that was\n // deliberately dropped: our own portaled UI (Dropdown, HoverCard) mounts to\n // document.body OUTSIDE any app-root, so a containment filter would wrongly\n // stop intercepting the very ref-links we must catch. In the embed iframe the\n // document is wholly the app, so same-origin is exact. In standalone, an\n // over-broad catch only routes a stray same-origin anchor through the\n // chokepoint (which refuses unresolved routes) - the safe direction; it can\n // never widen authority or enable an escape.\n if (url.origin !== loc.origin) return null;\n // 12. Same-page hash-only change - let the browser scroll natively.\n if (\n url.pathname === loc.pathname &&\n url.search === loc.search &&\n url.hash !== \"\"\n ) {\n return null;\n }\n\n return url.pathname + url.search + url.hash;\n}\n\n/**\n * Minimal structural shape of the Navigation API `navigate` event - typed\n * locally because `window.navigation` / `NavigateEvent` are not yet in the\n * ambient DOM lib across our toolchain.\n */\nexport interface NavigateEventLike {\n readonly canIntercept: boolean;\n readonly hashChange: boolean;\n readonly downloadRequest: string | null;\n readonly formData: unknown;\n readonly cancelable: boolean;\n readonly destination: { readonly url: string; readonly sameDocument: boolean };\n preventDefault(): void;\n}\n\n/**\n * Decide whether a Navigation API `navigate` event is a programmatic, same-\n * origin, FULL-document escape that must be kept inside the SPA / embed\n * boundary, and to where. Returns the in-app path, or null to leave it alone.\n *\n * This is the layer the click interceptor cannot cover: `location.assign`,\n * `location.href = ...`, `<meta http-equiv=refresh>`, etc. - navigations not\n * triggered by an anchor click. Same-document navigations (react-router's own\n * `pushState` SPA transitions) are skipped via `destination.sameDocument`, so\n * we never re-process or loop on our own router. Cross-origin and non-http\n * destinations are left to the browser (the iframe sandbox contains them);\n * downloads and hash changes are left alone. A POST form submission carries a\n * body (`formData` non-null) and must reach the server, so it is left alone\n * too; a GET form navigating to a same-origin route has no body and is routed\n * like any internal navigation (kept in-frame, not full-loaded out of embed).\n */\nexport function interceptedProgrammaticTarget(\n event: NavigateEventLike,\n loc: { origin: string } = window.location,\n): string | null {\n // Same-document = our own router (pushState). Never touch it.\n if (event.destination.sameDocument) return null;\n if (!event.canIntercept) return null;\n // Skip downloads, hash-only changes, and POST form submissions (formData is\n // non-null only for POST; a GET form has no body and routes like any nav).\n if (event.hashChange || event.downloadRequest != null || event.formData != null) {\n return null;\n }\n let url: URL;\n try {\n url = new URL(event.destination.url);\n } catch {\n return null;\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n if (url.origin !== loc.origin) return null;\n return url.pathname + url.search + url.hash;\n}\n\n/** The shape of `window.navigation` we use (feature-detected at runtime). */\ninterface NavigationLike {\n addEventListener(type: \"navigate\", listener: (e: NavigateEventLike) => void): void;\n removeEventListener(type: \"navigate\", listener: (e: NavigateEventLike) => void): void;\n}\n\n/**\n * Mount the single global link interceptor. Call once, high in the tree that\n * renders screens (the app `Layout`), so it covers every embedded and\n * standalone screen. It is intentionally NOT mounted on the auth routes\n * (`/login`, `/signup`, ...), which live outside `Layout`, so those pages keep\n * their native react-router `<Link>` semantics.\n */\nexport function useGlobalLinkInterceptor(): void {\n const tabNavigate = useTabNavigate();\n // Hold the latest navigate in a ref so the listener attaches exactly once.\n const navigateRef = useRef(tabNavigate);\n navigateRef.current = tabNavigate;\n\n useEffect(() => {\n // Layer 1 - anchor clicks (every browser): catches the dominant vector,\n // including markdown / injected anchors.\n const clickHandler = (event: MouseEvent): void => {\n const target = interceptedNavigationTarget(event);\n if (target == null) return;\n event.preventDefault();\n navigateRef.current(target);\n };\n document.addEventListener(\"click\", clickHandler, { capture: true });\n\n // Layer 2 - the Navigation API (where available): catches programmatic\n // navigations the click listener cannot see (location.assign / href=,\n // meta-refresh). Feature-detected; unsupported browsers rely on layer 1\n // plus the reverse-framing boot guard, which contains any full-document\n // escape. We only act when the event is cancelable - otherwise the full\n // load proceeds and the boot guard contains it (no double navigation).\n const navigation = (window as { navigation?: NavigationLike }).navigation;\n const navHandler = (event: NavigateEventLike): void => {\n const target = interceptedProgrammaticTarget(event);\n if (target == null) return;\n if (!event.cancelable) return;\n event.preventDefault();\n navigateRef.current(target);\n };\n navigation?.addEventListener(\"navigate\", navHandler);\n\n return () => {\n document.removeEventListener(\"click\", clickHandler, { capture: true });\n navigation?.removeEventListener(\"navigate\", navHandler);\n };\n }, []);\n}\n","// Embed `self`-mode location mirror.\n//\n// In `self` navigation mode the iframe navigates internally between Declarion\n// screen routes, and the host must mirror its own URL, breadcrumb, and back\n// button. This component is a single effect that watches the router\n// `location` and emits a `navigated` message on every change.\n//\n// Watching `location` (rather than emitting from each navigation call site)\n// guarantees every navigation is reported - `useTabNavigate`, the raw\n// `SmartDetailPage` transitions, a host-driven `navigate`, browser\n// back/forward, all land here. It renders nothing.\n\nimport { useEffect, useRef } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { useEmbed } from \"./EmbedContext\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { resolveEmbedNavigationPayload } from \"./screen-location\";\nimport { EMBED_MESSAGE_TYPES, postToParent } from \"./protocol\";\n\n/**\n * Emits `navigated` to the host on every internal navigation while\n * embed mode is `self`. Mounted inside `Layout`'s embed branch.\n *\n * Inert in `delegated` mode (the iframe does not move there, so there is no\n * location to mirror) and inert outside embed mode.\n */\nexport function EmbedLocationReporter() {\n const embed = useEmbed();\n const location = useLocation();\n const { data: schema } = useSchema();\n // Track the last reported pathname+search so React StrictMode's double\n // effect invocation and unrelated re-renders do not emit duplicate frames.\n const lastReported = useRef<string | null>(null);\n\n const active = embed.enabled && embed.navigationMode === \"self\";\n\n useEffect(() => {\n if (!active) return;\n const key = `${location.pathname}${location.search}`;\n if (lastReported.current === key) return;\n lastReported.current = key;\n\n const payload = resolveEmbedNavigationPayload(\n location.pathname,\n schema?.screens,\n );\n postToParent(\n embed.parentOrigin,\n EMBED_MESSAGE_TYPES.navigated,\n payload,\n );\n }, [active, location.pathname, location.search, embed.parentOrigin, schema?.screens]);\n\n return null;\n}\n","import { Btn, Icon, IconBtn } from \"@/components/primitives\";\n\ninterface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description: string;\n confirmLabel?: string;\n onConfirm: () => void;\n destructive?: boolean;\n}\n\n// Ported from the design's ActionModal pattern (decl/detail.jsx 434-488).\n// Fixed 460-wide centered panel, accent-tile header icon, divider, footer with\n// Cancel + primary (danger when destructive) buttons.\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel = \"Confirm\",\n onConfirm,\n destructive = false,\n}: ConfirmDialogProps) {\n if (!open) return null;\n return (\n <>\n <div\n onClick={() => onOpenChange(false)}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 460,\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"14px 18px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: destructive ? \"var(--danger-soft)\" : \"var(--accent-soft)\",\n color: destructive ? \"var(--danger)\" : \"var(--accent)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n {destructive ? <Icon.trash size={15} /> : <Icon.info size={15} />}\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>{title}</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", marginTop: 2 }}>\n {description}\n </div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={() => onOpenChange(false)} />\n </div>\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n <div style={{ flex: 1 }} />\n <Btn onClick={() => onOpenChange(false)}>Cancel</Btn>\n <Btn\n kind=\"primary\"\n danger={destructive}\n onClick={() => {\n onConfirm();\n onOpenChange(false);\n }}\n >\n {confirmLabel}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","// Embed-mode route-level unsaved-changes guard.\n//\n// The platform warns about unsaved edits only at tab-close. Embed mode has no\n// tabs, so navigating away from a dirty detail screen would silently discard\n// edits. This guard restores that protection for embed mode only.\n//\n// Router-agnostic by design. React Router's `useBlocker` works ONLY inside a\n// data router (`createBrowserRouter`); the Declarion app mounts a plain\n// `<BrowserRouter>` (app.tsx), where `useBlocker` throws and crashed the\n// whole embed render path. Instead, the embed navigation primitive\n// (`useEmbedScreenNavigate`) defers a dirty navigation into\n// `useEmbedNavGuardStore`; this component renders the confirm dialog and\n// either runs the deferred navigation (discard + leave) or drops it (stay).\n//\n// Mounted only inside `Layout`'s embed branch, so non-embed behavior is\n// completely unchanged - the guard does not exist outside embed mode.\n\nimport { ConfirmDialog } from \"@/components/shared/ConfirmDialog\";\nimport { useEmbedNavGuardStore } from \"./embed-nav-guard\";\n\n/**\n * Renders the discard-confirm dialog whenever a navigation is deferred in the\n * embed navigation guard store. Confirm runs the deferred navigation; cancel\n * (button / overlay / Escape) drops it and stays on the screen. Renders\n * nothing while no navigation is pending.\n */\nexport function EmbedUnsavedGuard() {\n const pendingNavigation = useEmbedNavGuardStore((s) => s.pendingNavigation);\n const confirmNavigation = useEmbedNavGuardStore((s) => s.confirmNavigation);\n const cancelNavigation = useEmbedNavGuardStore((s) => s.cancelNavigation);\n\n if (pendingNavigation === null) return null;\n\n return (\n <ConfirmDialog\n open\n onOpenChange={(open) => {\n // The dialog can only close via Cancel / overlay / X / Escape - the\n // Confirm path runs `confirmNavigation()` in `onConfirm` and clears\n // the pending state itself, so by the time a close fires after a\n // confirm there is nothing pending and `cancelNavigation` is a\n // no-op. A close while still pending means the user chose to stay.\n if (!open) cancelNavigation();\n }}\n title=\"Discard unsaved changes?\"\n description=\"This screen has unsaved changes. Leaving now discards them.\"\n confirmLabel=\"Discard and leave\"\n destructive\n onConfirm={() => {\n confirmNavigation();\n }}\n />\n );\n}\n","// Embed mode public surface.\n//\n// `?embed=1` renders a Declarion screen route without shell chrome, for\n// white-label iframe embedding. This barrel is the single import point for\n// embed-mode primitives used by `app.tsx`, `Layout`, and later tasks.\n//\n// Importing this module also runs the pre-paint theme bootstrap: when the\n// URL carries `?embed=1&theme=dark`, the theme lands on <html> before React\n// renders. This side effect mirrors the appearance store's own module-load\n// `applyAppearance(initial)` and must run before first paint, so the import\n// must stay eager (not lazy) wherever the app entry point pulls it in.\n\nimport { applyEmbedThemeFromLocation } from \"./theme\";\n\n// Side effect: apply the embed theme from the live URL at module load,\n// before any component renders. No-op outside embed mode or without a valid\n// `theme` param.\napplyEmbedThemeFromLocation();\n\nexport {\n EmbedProvider,\n useEmbed,\n toEmbedContextValue,\n type EmbedContextValue,\n} from \"./EmbedContext\";\n\nexport {\n parseEmbedParams,\n parseExactOrigin,\n appendEmbedParams,\n DEFAULT_NAVIGATION_MODE,\n EMBED_PARAM_EMBED,\n EMBED_PARAM_PARENT_ORIGIN,\n EMBED_PARAM_THEME,\n EMBED_PARAM_NAV,\n EMBED_PARAM_KEYS,\n type EmbedParams,\n type EmbedTheme,\n type EmbedNavigationMode,\n} from \"./params\";\n\nexport {\n applyEmbedTheme,\n applyEmbedThemeFromQuery,\n applyEmbedThemeFromLocation,\n} from \"./theme\";\n\nexport {\n initEmbedTokenMode,\n isEmbedTokenMode,\n getEmbedToken,\n getEmbedParentOrigin,\n setEmbedToken,\n clearEmbedToken,\n awaitEmbedToken,\n} from \"./token\";\n\nexport {\n resolveEmbedFileSrc,\n DerivationPendingError,\n type EmbedFileSrc,\n type EmbedFileSrcKind,\n type EmbedFileVariant,\n} from \"./file-src\";\n\nexport {\n classifyInboundMessage,\n createInboundMessageListener,\n type InboundClassification,\n type InboundHandlers,\n} from \"./handshake\";\n\nexport { EmbedAuthBootstrap } from \"./EmbedAuthBootstrap\";\n\nexport { EmbedResizeReporter } from \"./EmbedResizeReporter\";\n\nexport { EmbedDirtyReporter } from \"./EmbedDirtyReporter\";\n\nexport {\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EMBED_MESSAGE_TYPES,\n postToParent,\n type EmbedMessageType,\n type EmbedMessage,\n type EmbedMessagePayloadMap,\n type EmbedReadyPayload,\n type EmbedSetTokenPayload,\n type EmbedTokenExpiredPayload,\n type EmbedReloadRequiredPayload,\n type EmbedResizedPayload,\n type EmbedNavigationPayload,\n type EmbedDirtyChangedPayload,\n type EmbedNavigationFailedPayload,\n type EmbedNavigatePayload,\n type EmbedSetThemePayload,\n} from \"./protocol\";\n\nexport {\n resolveEmbedNavigation,\n resolveEmbedNavigationPayload,\n} from \"./screen-location\";\nexport type { EmbedNavigationResolution } from \"./screen-location\";\n\nexport {\n useEmbedNavigate,\n useEmbedScreenNavigate,\n useEmbedNavigationFailed,\n} from \"./useEmbedNavigate\";\n\nexport {\n useGlobalLinkInterceptor,\n interceptedNavigationTarget,\n interceptedProgrammaticTarget,\n NATIVE_LINK_ATTR,\n} from \"./useGlobalLinkInterceptor\";\n\nexport { EmbedLocationReporter } from \"./EmbedLocationReporter\";\n\nexport { useEmbedDirtyStore, useReportEmbedDirty } from \"./embed-dirty\";\n\nexport { EmbedUnsavedGuard } from \"./EmbedUnsavedGuard\";\n","import { useCallback } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useTabStore } from \"@/stores/tabs\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { screenTitleForPath } from \"@/types/schema\";\nimport { useEmbed, useEmbedScreenNavigate } from \"@/embed\";\n\ninterface TabNavigateOptions {\n title?: string;\n icon?: string;\n search?: string;\n replace?: boolean;\n}\n\nexport function useTabNavigate() {\n const navigate = useNavigate();\n const { data: schema } = useSchema();\n const { openTab, switchTab, findTabByPathname } = useTabStore();\n const embed = useEmbed();\n const embedScreenNavigate = useEmbedScreenNavigate();\n\n const tabNavigate = useCallback(\n (pathname: string, opts?: TabNavigateOptions) => {\n // Embed mode has no tab UI: the (hidden) TabStrip is the only consumer\n // of the tab store, and every screen read of it is null-checked. So in\n // embed mode `useTabNavigate` collapses to a plain navigation - it\n // MUST NOT call openTab/switchTab. `useEmbedScreenNavigate` preserves\n // the embed query params (`self` mode) or emits a `navigation-requested`\n // and stays put (`delegated` mode). Reason: Decision 15.\n if (embed.enabled) {\n const target = opts?.search\n ? `${pathname}${opts.search.startsWith(\"?\") ? \"\" : \"?\"}${opts.search}`\n : pathname;\n embedScreenNavigate(target, { replace: opts?.replace });\n return;\n }\n\n // Check if tab exists for this pathname.\n const existing = findTabByPathname(pathname);\n if (existing) {\n switchTab(existing.id);\n navigate(\n { pathname, search: opts?.search },\n { replace: opts?.replace },\n );\n return;\n }\n\n // Title resolution: caller-supplied title wins; otherwise resolve the\n // destination screen's display name from the route table. For a detail\n // route whose screen has no name, screenTitleForPath falls back to the\n // bound entity name plus a short id, so the tab never shows a raw UUID\n // (Finding 4). The raw pathname remains the absolute last resort only\n // for paths that match no screen route at all. Detail tabs get this\n // resolved title now and SmartDetailPage refines it to the record label\n // once the row loads.\n const resolved = opts?.title\n ? undefined\n : screenTitleForPath(pathname, schema?.screens, schema?.entities);\n const title = opts?.title ?? resolved?.title ?? pathname;\n const icon = opts?.icon ?? resolved?.icon;\n\n // Open new tab.\n openTab({\n pathname,\n search: opts?.search ?? \"\",\n title,\n icon,\n closable: true,\n });\n navigate(\n { pathname, search: opts?.search },\n { replace: opts?.replace },\n );\n },\n [\n navigate,\n openTab,\n switchTab,\n findTabByPathname,\n schema?.screens,\n schema?.entities,\n embed.enabled,\n embedScreenNavigate,\n ],\n );\n\n return tabNavigate;\n}\n","import { useMemo, useState, type ReactElement } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { Icon, Kbd } from \"@/components/primitives\";\nimport { BrandLogo } from \"@/components/primitives/BrandLogo\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { isModifiedClick } from \"@/lib/utils\";\nimport { loc, type NavNode, type Screen } from \"@/types/schema\";\n\nexport interface SidebarProps {\n activeArea: string;\n}\n\nexport function Sidebar({ activeArea }: SidebarProps) {\n const { data: schema } = useSchema();\n const location = useLocation();\n const tabNavigate = useTabNavigate();\n const sidebarMode = useAppearanceStore((s) => s.sidebar);\n const setAppearance = useAppearanceStore((s) => s.set);\n const collapsed = sidebarMode === \"collapsed\";\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n\n const branding = schema?.branding;\n const appName = branding?.app_name ?? \"\";\n const appLogo = branding?.app_logo;\n const versionLabel = branding?.version_label || \"v4.2\";\n\n const sections = useMemo(() => buildSections(schema?.navigation ?? [], activeArea), [schema?.navigation, activeArea]);\n\n const isActive = (route?: string) => {\n if (!route) return false;\n return location.pathname === route || location.pathname.startsWith(route + \"/\");\n };\n\n const pickItem = (item: NavNode, opts?: { newTab?: boolean }) => {\n if (!item.screen) return;\n const screen = schema?.screens[item.screen];\n if (!screen) return;\n tabNavigate(screen.route, {\n title: loc(item.name),\n icon: item.icon,\n });\n // newTab is currently delegated to the browser-default behaviour on\n // <a href> + modifier/middle-click; this option is here for callers\n // that synthesise nav from a programmatic source. Reason: with real\n // anchors, the SPA-intercept onClick yields control to the browser\n // when modifiers are pressed, so we don't need an explicit code path.\n void opts;\n };\n\n return (\n <aside\n style={{\n gridArea: \"sidebar\",\n background: \"var(--sidebar)\",\n borderRight: \"1px solid var(--border)\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n height: 44,\n display: \"flex\",\n alignItems: \"center\",\n padding: collapsed ? 0 : \"0 12px\",\n justifyContent: collapsed ? \"center\" : \"flex-start\",\n borderBottom: \"1px solid var(--divider)\",\n gap: 8,\n }}\n >\n {!collapsed ? (\n <>\n <BrandLogo appLogo={appLogo} size={20} />\n <div\n style={{\n fontSize: 13,\n fontWeight: 600,\n letterSpacing: \"-0.01em\",\n color: \"var(--text)\",\n }}\n >\n {appName}\n </div>\n <span\n style={{\n padding: \"1px 6px\",\n background: \"var(--accent-soft)\",\n color: \"var(--accent)\",\n fontSize: 9.5,\n fontWeight: 600,\n borderRadius: 3,\n letterSpacing: \"0.04em\",\n textTransform: \"uppercase\",\n }}\n >\n {versionLabel}\n </span>\n </>\n ) : appLogo ? (\n <BrandLogo appLogo={appLogo} size={18} />\n ) : appName ? (\n <span style={{ fontSize: 11, fontWeight: 700, color: \"var(--accent)\" }}>\n {appName.charAt(0)}\n </span>\n ) : null}\n </div>\n\n <nav style={{ flex: 1, overflowY: \"auto\", padding: collapsed ? \"8px 4px\" : \"8px 6px\" }}>\n {sections.map((sec, i) => (\n <div key={i} style={{ marginBottom: 12 }}>\n {!collapsed && sec.title && (\n <div\n style={{\n fontSize: 9.5,\n fontWeight: 700,\n color: \"var(--text-3)\",\n letterSpacing: \"0.08em\",\n padding: \"4px 10px 4px\",\n textTransform: \"uppercase\",\n }}\n >\n {sec.title}\n </div>\n )}\n {sec.items.map((it, j) => {\n const key = `${i}-${j}-${it.screen || loc(it.name)}`;\n const screen = it.screen ? schema?.screens[it.screen] : undefined;\n const active = isActive(screen?.route);\n const hasKids = !!(it.children && it.children.length > 0);\n const exp = !!expanded[key];\n return (\n <SidebarItem\n key={key}\n item={it}\n href={screen?.route}\n collapsed={collapsed}\n active={active}\n hasKids={hasKids}\n expanded={exp}\n onToggleExpand={() => setExpanded((prev) => ({ ...prev, [key]: !prev[key] }))}\n onPick={(opts) => pickItem(it, opts)}\n resolveChild={(child) => {\n const cScreen = child.screen ? schema?.screens[child.screen] : undefined;\n return cScreen;\n }}\n onPickChild={(child) => {\n const cScreen = child.screen ? schema?.screens[child.screen] : undefined;\n if (!cScreen) return;\n tabNavigate(cScreen.route, {\n title: loc(child.name),\n icon: child.icon,\n });\n }}\n />\n );\n })}\n </div>\n ))}\n </nav>\n\n <div\n style={{\n padding: \"6px 8px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n justifyContent: collapsed ? \"center\" : \"space-between\",\n }}\n >\n <button\n onClick={() => setAppearance(\"sidebar\", collapsed ? \"wide\" : \"collapsed\")}\n title={collapsed ? \"Expand sidebar (⌘B)\" : \"Collapse sidebar (⌘B)\"}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 26,\n height: 26,\n borderRadius: 5,\n background: \"transparent\",\n border: 0,\n color: \"var(--text-3)\",\n cursor: \"pointer\",\n }}\n >\n {collapsed ? <Icon.right size={14} /> : <Icon.left size={14} />}\n </button>\n {!collapsed && (\n <>\n <span style={{ flex: 1, fontSize: 11, color: \"var(--text-4)\" }}>\n Docs · API · Help\n </span>\n <Kbd>?</Kbd>\n </>\n )}\n </div>\n </aside>\n );\n}\n\ninterface SidebarSection {\n title: string;\n items: NavNode[];\n}\n\nexport function buildSections(nodes: NavNode[], activeArea: string): SidebarSection[] {\n const sections: SidebarSection[] = [];\n for (const node of nodes) {\n if (!matchesArea(node, activeArea)) continue;\n if (node.items && node.items.length > 0) {\n sections.push({ title: loc(node.name) || \"\", items: node.items });\n continue;\n }\n if (node.screen) {\n // Merge loose top-level leaves under an implicit untitled section.\n let sec = sections.find((s) => s.title === \"\");\n if (!sec) {\n sec = { title: \"\", items: [] };\n sections.push(sec);\n }\n sec.items.push(node);\n }\n }\n return sections;\n}\n\nfunction matchesArea(node: NavNode, activeArea: string): boolean {\n const nodeArea = node.area || \"default\";\n return nodeArea === activeArea;\n}\n\nfunction SidebarItem({\n item,\n href,\n collapsed,\n active,\n hasKids,\n expanded,\n onToggleExpand,\n onPick,\n onPickChild,\n resolveChild,\n}: {\n item: NavNode;\n href: string | undefined;\n collapsed: boolean;\n active: boolean;\n hasKids: boolean;\n expanded: boolean;\n onToggleExpand: () => void;\n onPick: (opts?: { newTab?: boolean }) => void;\n onPickChild: (child: NavNode) => void;\n resolveChild: (child: NavNode) => Screen | undefined;\n}) {\n const [hover, setHover] = useState(false);\n const iconMap = Icon as unknown as Record<string, (p: { size?: number }) => ReactElement>;\n const IconComp = item.icon && iconMap[item.icon as string] ? iconMap[item.icon as string] : undefined;\n if (item.icon && !IconComp) {\n console.error(`[declarion] nav item \"${item.screen ?? loc(item.name)}\" uses unknown icon \"${item.icon}\"`);\n }\n const label = loc(item.name) || item.screen || \"\";\n\n // Shared styles for the collapsed icon-only variant. Element type is\n // <a href> when the item has a route (so middle-click / cmd-click open\n // a new tab natively); falls back to <button> for navless branches.\n if (collapsed) {\n const collapsedStyle: React.CSSProperties = {\n width: 40,\n height: 32,\n margin: \"2px auto\",\n borderRadius: 6,\n border: 0,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: active\n ? \"var(--sidebar-active)\"\n : hover\n ? \"var(--sidebar-hover)\"\n : \"transparent\",\n color: active ? \"var(--accent)\" : \"var(--text-3)\",\n textDecoration: \"none\",\n };\n if (href) {\n return (\n <a\n href={href}\n // Owns its own navigation via onPick (which also sets the active\n // area), so it opts out of the global link interceptor to avoid\n // double-handling. (The sidebar is not rendered in embed mode.)\n data-declarion-native-link=\"\"\n onClick={(e) => {\n if (isModifiedClick(e)) return; // browser handles new-tab\n e.preventDefault();\n onPick();\n }}\n title={label}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={collapsedStyle}\n >\n {IconComp && <IconComp size={14} />}\n </a>\n );\n }\n return (\n <button\n onClick={() => onPick()}\n title={label}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={collapsedStyle}\n >\n {IconComp && <IconComp size={14} />}\n </button>\n );\n }\n\n // Shared styles for the expanded row. Element is <a href> when the\n // item has a route (enables browser-native middle-click / cmd-click new\n // tab + screen-reader \"link\" semantics); falls back to <button> for\n // navless branches that only toggle expand.\n const rowStyle: React.CSSProperties = {\n width: \"calc(100% - 4px)\",\n margin: \"0 2px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"5px 8px\",\n borderRadius: 5,\n border: 0,\n cursor: \"pointer\",\n textAlign: \"left\",\n background: active\n ? \"var(--sidebar-active)\"\n : hover\n ? \"var(--sidebar-hover)\"\n : \"transparent\",\n color: active ? \"var(--accent)\" : \"var(--text-2)\",\n fontSize: 12.5,\n fontWeight: active ? 600 : 500,\n textDecoration: \"none\",\n };\n const rowBody = (\n <>\n {IconComp && <IconComp size={14} />}\n <span style={{ flex: 1 }}>{label}</span>\n {item.badge && (\n <span\n style={{\n padding: \"1px 5px\",\n background: \"var(--surface-3)\",\n color: \"var(--text-2)\",\n borderRadius: 3,\n fontSize: 10,\n fontWeight: 600,\n }}\n >\n {item.badge}\n </span>\n )}\n {hasKids && (\n <span\n role=\"button\"\n aria-label={expanded ? \"Collapse\" : \"Expand\"}\n onClick={(e) => {\n // Chevron always toggles expand, regardless of whether the\n // parent has a screen. Stop propagation so we don't also\n // navigate when the parent is a leaf-with-kids.\n e.stopPropagation();\n e.preventDefault();\n onToggleExpand();\n }}\n style={{ display: \"inline-flex\", padding: 2, cursor: \"pointer\" }}\n >\n <Icon.down\n size={11}\n style={{\n transform: expanded ? \"rotate(0)\" : \"rotate(-90deg)\",\n transition: \"transform .15s\",\n color: \"var(--text-3)\",\n }}\n />\n </span>\n )}\n </>\n );\n return (\n <div>\n {href ? (\n <a\n href={href}\n // Owns its own navigation via onPick; opts out of the global link\n // interceptor to avoid double-handling. (Sidebar is hidden in embed.)\n data-declarion-native-link=\"\"\n onClick={(e) => {\n // Yield to browser for new-tab/window intents.\n if (isModifiedClick(e)) return;\n e.preventDefault();\n onPick();\n }}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={rowStyle}\n >\n {rowBody}\n </a>\n ) : (\n <button\n onClick={() => {\n // Plan: leaf-with-kids — handled by the <a> branch above. A\n // bare branch (no screen) only toggles the children list.\n if (hasKids) onToggleExpand();\n }}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={rowStyle}\n >\n {rowBody}\n </button>\n )}\n {hasKids && expanded && (\n <div\n style={{\n marginLeft: 22,\n borderLeft: \"1px solid var(--divider)\",\n paddingLeft: 6,\n }}\n >\n {(item.children || []).map((child, idx) => {\n const cScreen = resolveChild(child);\n const childKey = child.screen || `${idx}`;\n return (\n <ChildItem\n key={childKey}\n label={loc(child.name) || child.screen || \"\"}\n href={cScreen?.route}\n enabled={!!cScreen}\n onPick={() => onPickChild(child)}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nfunction ChildItem({\n label,\n href,\n enabled,\n onPick,\n}: {\n label: string;\n href: string | undefined;\n enabled: boolean;\n onPick: () => void;\n}) {\n const [hover, setHover] = useState(false);\n const childStyle: React.CSSProperties = {\n width: \"100%\",\n display: \"block\",\n padding: \"4px 8px\",\n borderRadius: 5,\n background: hover ? \"var(--sidebar-hover)\" : \"transparent\",\n border: 0,\n textAlign: \"left\",\n cursor: enabled ? \"pointer\" : \"default\",\n fontSize: 12,\n color: \"var(--text-3)\",\n textDecoration: \"none\",\n };\n if (enabled && href) {\n return (\n <a\n href={href}\n // Owns its own navigation via onPick; opts out of the global link\n // interceptor to avoid double-handling. (Sidebar is hidden in embed.)\n data-declarion-native-link=\"\"\n onClick={(e) => {\n if (isModifiedClick(e)) return;\n e.preventDefault();\n onPick();\n }}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={childStyle}\n >\n {label}\n </a>\n );\n }\n return (\n <button\n onClick={() => enabled && onPick()}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={childStyle}\n >\n {label}\n </button>\n );\n}\n\n","// DeclarionToast - the toast card.\n//\n// Recreates the \"Toasts UI Kit\" design handoff: a calm card coloured by a\n// trailing-edge accent border + a soft icon ring, not a heavy background.\n// Five variants (success / error / warning / info / loading), selectable\n// text, an optional action button, a Copy button on errors, an expandable\n// `details` block, a `requestId` badge, a ×N repeat-rollup badge and a\n// remaining-time progress bar.\n//\n// Sonner owns the portal, stack geometry, swipe and positioning. This card\n// owns its own timer, pause-on-hover, progress bar and enter/exit animation:\n// a single owned clock keeps the progress bar and the auto-dismiss exact,\n// which a wrapped library timer cannot expose. Mounted via `toast.custom`\n// from `lib/toast`.\n\nimport { useCallback, useEffect, useRef, useState, type CSSProperties } from \"react\";\nimport { Icon } from \"@/components/primitives\";\n\n/** The five toast variants. `loading` transforms in place via `toast.update`. */\nexport type ToastType = \"success\" | \"error\" | \"warning\" | \"info\" | \"loading\";\n\n/** A single trailing action button rendered in the toast's action row. */\nexport interface ToastAction {\n label: string;\n onClick: () => void;\n}\n\n/** Everything needed to render one toast. Built by the `lib/toast` facade. */\nexport interface ToastModel {\n id: string | number;\n type: ToastType;\n title: string;\n /** Optional body line - selectable so users can quote it in a ticket. */\n message?: string;\n /** Auto-dismiss after this many ms. 0 = sticky (never auto-dismisses). */\n duration: number;\n sticky: boolean;\n action?: ToastAction;\n /** Correlation id, shown top-right in mono, selectable, included in Copy. */\n requestId?: string;\n /** Expandable detail block (stack trace, server payload) - selectable. */\n details?: string;\n /** Repeat-rollup count. >1 renders a ×N badge instead of a new toast. */\n count?: number;\n /** Re-stamped on every `toast.update` so the dismiss timer re-arms. */\n createdAt: number;\n}\n\n/** Visual tone token prefix + icon per variant. error → danger, warning → warn. */\nconst TYPE_META: Record<ToastType, { tone: string; defaultTitle: string }> = {\n success: { tone: \"success\", defaultTitle: \"Success\" },\n error: { tone: \"danger\", defaultTitle: \"Error\" },\n warning: { tone: \"warn\", defaultTitle: \"Warning\" },\n info: { tone: \"info\", defaultTitle: \"Heads up\" },\n loading: { tone: \"info\", defaultTitle: \"Working…\" },\n};\n\n/** Time the exit animation runs before the card is unmounted (ms). */\nconst EXIT_ANIMATION_MS = 200;\n\n/** The variant glyph. `loading` spins; the rest are static. Colour is\n * inherited from the icon-ring wrapper, so this only picks the shape. */\nfunction ToastIcon({ type }: { type: ToastType }) {\n const style: CSSProperties = { display: \"flex\" };\n if (type === \"loading\") {\n return (\n <span style={{ ...style, animation: \"dz-toast-spin 0.9s linear infinite\" }}>\n <Icon.loader size={11} />\n </span>\n );\n }\n const Glyph =\n type === \"success\" ? Icon.check : type === \"error\" ? Icon.x : type === \"warning\" ? Icon.warning : Icon.info;\n return (\n <span style={style}>\n <Glyph size={11} />\n </span>\n );\n}\n\nexport function DeclarionToast({ model, onClose }: { model: ToastModel; onClose: () => void }) {\n const meta = TYPE_META[model.type] ?? TYPE_META.info;\n const tone = meta.tone;\n\n const [paused, setPaused] = useState(false);\n const [copied, setCopied] = useState(false);\n const [expanded, setExpanded] = useState(false);\n const [exiting, setExiting] = useState(false);\n const [progress, setProgress] = useState(1);\n\n // Owned countdown clock. `remainingRef` is the ms left to run; each\n // running stretch decrements it on cleanup, so pause-on-hover genuinely\n // preserves time (the design prototype's pause did not - it kept\n // counting wall-clock through the pause).\n const remainingRef = useRef(model.duration);\n const closingRef = useRef(false);\n\n const autoDismiss = !model.sticky && model.duration > 0;\n\n const beginExit = useCallback(() => {\n if (closingRef.current) return;\n closingRef.current = true;\n setExiting(true);\n window.setTimeout(onClose, EXIT_ANIMATION_MS);\n }, [onClose]);\n\n // toast.update() re-stamps createdAt + may change duration: reset the clock.\n useEffect(() => {\n remainingRef.current = model.duration;\n setProgress(1);\n }, [model.createdAt, model.duration]);\n\n // Run the clock while not paused. Cleanup banks the elapsed time so the\n // next run resumes from where it stopped.\n useEffect(() => {\n if (!autoDismiss || paused || exiting) return undefined;\n const runStart = Date.now();\n const timer = window.setTimeout(beginExit, remainingRef.current);\n let raf = 0;\n const tick = () => {\n const left = remainingRef.current - (Date.now() - runStart);\n setProgress(Math.max(0, left) / model.duration);\n if (left > 0) raf = requestAnimationFrame(tick);\n };\n raf = requestAnimationFrame(tick);\n return () => {\n window.clearTimeout(timer);\n cancelAnimationFrame(raf);\n remainingRef.current = Math.max(0, remainingRef.current - (Date.now() - runStart));\n };\n }, [autoDismiss, paused, exiting, model.duration, model.createdAt, beginExit]);\n\n const doCopy = useCallback(() => {\n const text = [\n model.title,\n model.message,\n model.requestId ? `request_id: ${model.requestId}` : null,\n model.details,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n void navigator.clipboard?.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n }, [model.title, model.message, model.requestId, model.details]);\n\n const toneVar = (suffix: string) => `var(--${tone}${suffix})`;\n const showCopy = model.type === \"error\";\n\n return (\n <div\n role={model.type === \"error\" ? \"alert\" : \"status\"}\n aria-live={model.type === \"error\" ? \"assertive\" : \"polite\"}\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n style={{\n width: \"100%\",\n background: \"var(--surface)\",\n border: `1px solid ${toneVar(\"-border\")}`,\n borderLeft: `3px solid ${toneVar(\"\")}`,\n borderRadius: 8,\n boxShadow: \"var(--shadow-toast)\",\n overflow: \"hidden\",\n position: \"relative\",\n animation: exiting\n ? `dz-toast-out ${EXIT_ANIMATION_MS}ms ease-in forwards`\n : \"dz-toast-in 0.24s cubic-bezier(0.16, 1, 0.3, 1)\",\n }}\n >\n <div style={{ display: \"flex\", padding: \"10px 12px 11px 12px\", gap: 10 }}>\n {/* Icon ring */}\n <div\n style={{\n width: 18,\n height: 18,\n marginTop: 1,\n flexShrink: 0,\n background: toneVar(\"-soft\"),\n borderRadius: 99,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: toneVar(\"\"),\n }}\n >\n <ToastIcon type={model.type} />\n </div>\n\n {/* Body */}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 6 }}>\n <div\n style={{\n fontSize: 12.5,\n fontWeight: 600,\n color: \"var(--text)\",\n letterSpacing: \"-0.005em\",\n userSelect: \"text\",\n }}\n >\n {model.title || meta.defaultTitle}\n </div>\n {(model.count ?? 0) > 1 && (\n <span\n style={{\n background: toneVar(\"-soft\"),\n color: toneVar(\"\"),\n fontSize: 10,\n padding: \"1px 6px\",\n borderRadius: 99,\n fontWeight: 600,\n flexShrink: 0,\n }}\n >\n ×{model.count}\n </span>\n )}\n {model.requestId && (\n <span\n className=\"mono\"\n style={{\n fontSize: 10,\n color: \"var(--text-4)\",\n marginLeft: \"auto\",\n userSelect: \"text\",\n }}\n >\n {model.requestId}\n </span>\n )}\n </div>\n\n {model.message && (\n <div\n style={{\n fontSize: 12,\n color: \"var(--text-2)\",\n marginTop: 3,\n lineHeight: 1.5,\n userSelect: \"text\",\n cursor: \"text\",\n wordBreak: \"break-word\",\n }}\n >\n {model.message}\n </div>\n )}\n\n {model.details && (\n <div style={{ marginTop: 6 }}>\n <button\n type=\"button\"\n onClick={() => setExpanded((e) => !e)}\n style={{\n background: \"transparent\",\n border: 0,\n padding: 0,\n cursor: \"pointer\",\n color: \"var(--text-3)\",\n fontSize: 11,\n fontWeight: 500,\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 4,\n }}\n >\n <Icon.down\n size={10}\n style={{ transform: expanded ? \"rotate(180deg)\" : \"none\", transition: \"transform .15s\" }}\n />\n {expanded ? \"Hide details\" : \"Show details\"}\n </button>\n {expanded && (\n <pre\n className=\"mono\"\n style={{\n margin: \"6px 0 0 0\",\n padding: \"8px 10px\",\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 5,\n fontSize: 11,\n lineHeight: 1.5,\n color: \"var(--text-2)\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n userSelect: \"text\",\n maxHeight: 200,\n overflow: \"auto\",\n }}\n >\n {model.details}\n </pre>\n )}\n </div>\n )}\n\n {(model.action || showCopy) && (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, marginTop: 8 }}>\n {model.action && (\n <button\n type=\"button\"\n onClick={() => {\n model.action?.onClick();\n beginExit();\n }}\n style={{\n background: toneVar(\"-soft\"),\n color: toneVar(\"\"),\n border: 0,\n padding: \"4px 10px\",\n borderRadius: 5,\n fontSize: 11.5,\n fontWeight: 600,\n cursor: \"pointer\",\n }}\n >\n {model.action.label}\n </button>\n )}\n {showCopy && (\n <button\n type=\"button\"\n onClick={doCopy}\n title=\"Copy error message\"\n aria-label=\"Copy error message\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 4,\n background: \"transparent\",\n border: \"1px solid var(--border)\",\n padding: \"3px 7px\",\n borderRadius: 5,\n fontSize: 11,\n color: copied ? \"var(--success)\" : \"var(--text-3)\",\n cursor: \"pointer\",\n fontWeight: 500,\n }}\n >\n {copied ? <Icon.check size={11} /> : <Icon.copy size={11} />}\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n )}\n </div>\n )}\n </div>\n\n {/* Close */}\n <button\n type=\"button\"\n onClick={beginExit}\n aria-label=\"Dismiss\"\n style={{\n width: 20,\n height: 20,\n padding: 0,\n marginTop: -1,\n flexShrink: 0,\n background: \"transparent\",\n border: 0,\n borderRadius: 4,\n cursor: \"pointer\",\n color: \"var(--text-4)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Icon.x size={12} />\n </button>\n </div>\n\n {/* Remaining-time progress bar */}\n {autoDismiss && (\n <div style={{ position: \"absolute\", left: 0, right: 0, bottom: 0, height: 2 }}>\n <div\n style={{\n height: \"100%\",\n width: `${progress * 100}%`,\n background: toneVar(\"\"),\n opacity: 0.55,\n transition: paused ? \"none\" : \"width 0.1s linear\",\n }}\n />\n </div>\n )}\n </div>\n );\n}\n","// Toast facade - the single toast API for the whole app.\n//\n// Import `toast` from here (never from `sonner` directly): every callsite,\n// component or not, goes through this facade so the toast UX is declared in\n// exactly one place. The facade keeps Sonner as the headless engine (portal,\n// stack geometry, swipe, the aria-live region) and renders every toast with\n// `<DeclarionToast>` via `toast.custom` - the \"Toasts UI Kit\" design.\n//\n// API (title-first, message optional - severity drives the rest):\n// toast.success(title, message?, opts?) // auto-dismiss 4s\n// toast.info(title, message?, opts?) // auto-dismiss 6s\n// toast.warning(title, message?, opts?) // auto-dismiss 8s\n// toast.error(title, message?, opts?) // sticky, has a Copy button\n// const id = toast.loading(title, message?, opts?) // sticky, spinner\n// toast.update(id, { type, title, message, duration, sticky, action })\n// toast.dismiss(id) toast.dismissAll()\n//\n// opts: { duration, sticky, action:{label,onClick}, requestId, details,\n// group, id }. A `group` key rolls repeats up into one ×N toast.\n//\n// Position is declared, not hard-coded: `<Toaster>` in app.tsx reads\n// `ui_toast_position` (appearance store). Default bottom-right.\n\nimport { toast as sonner } from \"sonner\";\nimport { DeclarionToast, type ToastModel, type ToastType } from \"@/components/shared/toast/DeclarionToast\";\n\nexport type { ToastType, ToastAction } from \"@/components/shared/toast/DeclarionToast\";\n\n/** Per-call toast options. Severity sets the defaults; these override them. */\nexport interface ToastOptions {\n /** Auto-dismiss after this many ms. 0 = sticky. Defaults to the variant's. */\n duration?: number;\n /** Force sticky (never auto-dismiss) regardless of duration. */\n sticky?: boolean;\n /** Trailing action button - e.g. View / Retry / Undo / Reload. */\n action?: { label: string; onClick: () => void };\n /** Correlation id, shown top-right in mono, included when the user copies. */\n requestId?: string;\n /** Expandable detail block (stack trace, server payload). */\n details?: string;\n /** Repeats sharing a `group` roll up into one toast with a ×N badge. */\n group?: string;\n /** Explicit id - reuse to replace an existing toast in place (idempotent). */\n id?: string;\n}\n\n/** A live toast also remembers its `group` so rollup can find it. */\ntype StoredModel = ToastModel & { group?: string };\n\n/** Default lifetime per variant (ms). 0 = sticky: errors and loading wait. */\nconst DURATIONS: Record<ToastType, number> = {\n success: 4000,\n info: 6000,\n warning: 8000,\n error: 0,\n loading: 0,\n};\n\n/** Toasts currently on screen, by id - the source of truth for update(). */\nconst live = new Map<string, StoredModel>();\n/** Active `group` key -> the toast id currently absorbing that group's repeats. */\nconst groups = new Map<string, string>();\n\nlet seq = 0;\nconst nextId = () => `dz-${++seq}`;\n\n/** (Re)render a toast through Sonner. Same id => Sonner updates in place. */\nfunction present(model: StoredModel): void {\n sonner.custom(() => <DeclarionToast model={model} onClose={() => sonner.dismiss(model.id)} />, {\n id: model.id,\n // Sonner never times the toast out: the card owns its own clock so the\n // progress bar and the dismiss stay exact and pause together on hover.\n duration: Infinity,\n onDismiss: () => {\n live.delete(String(model.id));\n if (model.group && groups.get(model.group) === model.id) groups.delete(model.group);\n },\n });\n}\n\n/** Build, store and show a toast. Returns its id. */\nfunction emit(type: ToastType, title: string, message?: string, opts: ToastOptions = {}): string {\n // Group rollup: an existing, still-visible toast for this group absorbs\n // the repeat as a ×N count instead of stacking a duplicate.\n if (opts.group) {\n const prevId = groups.get(opts.group);\n if (prevId && live.has(prevId)) {\n const prev = live.get(prevId)!;\n const merged: StoredModel = {\n ...prev,\n title,\n message,\n count: (prev.count ?? 1) + 1,\n createdAt: Date.now(),\n };\n live.set(prevId, merged);\n present(merged);\n return prevId;\n }\n }\n\n const id = opts.id ?? nextId();\n const duration = opts.duration ?? DURATIONS[type];\n const model: StoredModel = {\n id,\n type,\n title,\n message,\n duration,\n sticky: opts.sticky ?? duration === 0,\n action: opts.action,\n requestId: opts.requestId,\n details: opts.details,\n count: 1,\n createdAt: Date.now(),\n group: opts.group,\n };\n live.set(id, model);\n if (opts.group) groups.set(opts.group, id);\n present(model);\n return id;\n}\n\n/** Fields a loading toast can morph into when its async work resolves. */\nexport interface ToastUpdate {\n type?: ToastType;\n title?: string;\n message?: string;\n duration?: number;\n sticky?: boolean;\n action?: { label: string; onClick: () => void };\n requestId?: string;\n details?: string;\n}\n\n/** Morph an existing toast in place - the loading -> success/error path. */\nfunction update(id: string, patch: ToastUpdate): void {\n const current = live.get(id);\n if (!current) return;\n const type = patch.type ?? current.type;\n // A type change with no explicit lifetime adopts the new variant's default\n // (loading -> success should auto-dismiss, not stay sticky).\n const duration = patch.duration ?? (patch.type ? DURATIONS[type] : current.duration);\n const next: StoredModel = {\n ...current,\n ...patch,\n type,\n duration,\n sticky: patch.sticky ?? duration === 0,\n createdAt: Date.now(),\n };\n live.set(id, next);\n present(next);\n}\n\n/** The app-wide toast API. Import this - not `sonner` - everywhere. */\nexport const toast = {\n success: (title: string, message?: string, opts?: ToastOptions) => emit(\"success\", title, message, opts),\n info: (title: string, message?: string, opts?: ToastOptions) => emit(\"info\", title, message, opts),\n warning: (title: string, message?: string, opts?: ToastOptions) => emit(\"warning\", title, message, opts),\n error: (title: string, message?: string, opts?: ToastOptions) => emit(\"error\", title, message, opts),\n loading: (title: string, message?: string, opts?: ToastOptions) => emit(\"loading\", title, message, opts),\n update,\n dismiss: (id: string) => sonner.dismiss(id),\n dismissAll: () => sonner.dismiss(),\n};\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useNavigate } from \"react-router-dom\";\nimport { toast } from \"@/lib/toast\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { peekTenantLandingPath } from \"@/stores/tabs\";\nimport * as authApi from \"@/api/auth\";\n\nexport function useAuth() {\n const {\n user,\n isAuthenticated,\n isLoading,\n activeTenant,\n tenants,\n setUser,\n setTenants,\n logout: storeLogout,\n } = useAuthStore();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n\n const loginMutation = useMutation({\n mutationFn: ({ email, password, remember }: { email: string; password: string; remember?: boolean }) =>\n authApi.login(email, password, remember ?? true),\n onSuccess: (result) => {\n setUser(result.user, result.tenant);\n // Refetch schema so permission-gated areas (admin, ops, system) appear\n // for users who had an unauthenticated schema cached before login.\n void queryClient.invalidateQueries({ queryKey: [\"schema\"] });\n // Fetch tenants in background after login.\n authApi.listTenants().then(setTenants).catch(() => {});\n },\n });\n\n const signupMutation = useMutation({\n mutationFn: ({\n email,\n password,\n displayName,\n }: {\n email: string;\n password: string;\n displayName: string;\n }) => authApi.signup(email, password, displayName),\n onSuccess: (result) => {\n setUser(result.user, result.tenant);\n void queryClient.invalidateQueries({ queryKey: [\"schema\"] });\n authApi.listTenants().then(setTenants).catch(() => {});\n },\n onError: (err: Error) => {\n toast.error(err.message);\n },\n });\n\n const switchTenantMutation = useMutation({\n mutationFn: (tenantId: string) => authApi.switchTenant(tenantId),\n onSuccess: (result) => {\n // Tenant context changed. Hard-reload the app so every in-memory cache\n // (open tabs, react-query data, detail-screen state) is rebuilt under\n // the new tenant - this is what guarantees no record loaded under the\n // previous tenant can linger on screen. The backend has set the new\n // access cookie; the reload picks it up. Land on the new tenant's own\n // last-active tab when it has a persisted tab set (per-tenant tabs),\n // otherwise the default route.\n const tid = result.tenant?.id;\n const uid = result.user?.id;\n const landing = (tid && uid && peekTenantLandingPath(tid, uid)) || \"/\";\n window.location.assign(landing);\n },\n onError: (err: Error) => {\n toast.error(err.message);\n },\n });\n\n const logout = async () => {\n await storeLogout();\n queryClient.clear();\n navigate(\"/login\");\n };\n\n return {\n user,\n isAuthenticated,\n isLoading,\n activeTenant,\n tenants,\n login: loginMutation.mutate,\n signup: signupMutation.mutate,\n switchTenant: switchTenantMutation.mutate,\n logout,\n isLoginPending: loginMutation.isPending,\n isSignupPending: signupMutation.isPending,\n isSwitchingTenant: switchTenantMutation.isPending,\n };\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport {\n impersonationStart,\n impersonationStop,\n impersonationRevoke,\n} from \"@/api/auth\";\n\n/**\n * useImpersonation centralizes the three lifecycle calls (start, stop,\n * revoke) and re-hydrates auth state on success so the topbar / banner /\n * viewport frame react in lockstep.\n *\n * isImpersonating is derived from auth.me's impersonation field which the\n * server populates only when the request was issued under an active\n * impersonation session. Direct human action and agent flows (without an\n * impersonation channel) leave it null.\n */\nexport function useImpersonation() {\n const user = useAuthStore((s) => s.user);\n const initialize = useAuthStore((s) => s.initialize);\n const queryClient = useQueryClient();\n\n const info = user?.impersonation ?? null;\n const isImpersonating = !!info;\n\n const startMutation = useMutation({\n mutationFn: ({\n targetUserId,\n reason,\n durationMinutes,\n }: {\n targetUserId: string;\n reason: string;\n durationMinutes?: number;\n }) => impersonationStart(targetUserId, reason, durationMinutes),\n onSuccess: async () => {\n // The server swapped __Host- cookies; re-hydrate from auth.me so\n // the entire shell flips to the target's identity in one place.\n await initialize();\n queryClient.invalidateQueries();\n toast.success(\"Impersonation session started.\");\n },\n onError: (err: Error) => toast.error(err.message),\n });\n\n const stopMutation = useMutation({\n mutationFn: () => impersonationStop(),\n onSuccess: async () => {\n await initialize();\n queryClient.invalidateQueries();\n toast.success(\"Returned to your own session.\");\n },\n onError: (err: Error) => toast.error(err.message),\n });\n\n const revokeMutation = useMutation({\n mutationFn: (sessionId: string) => impersonationRevoke(sessionId),\n onSuccess: () => {\n toast.success(\"Session revoked.\");\n queryClient.invalidateQueries({ queryKey: [\"impersonation_session\"] });\n },\n onError: (err: Error) => toast.error(err.message),\n });\n\n return {\n info,\n isImpersonating,\n start: startMutation.mutate,\n stop: stopMutation.mutate,\n revoke: revokeMutation.mutate,\n isStarting: startMutation.isPending,\n isStopping: stopMutation.isPending,\n };\n}\n","import { useMemo, useRef, useState } from \"react\";\nimport { Icon, Dropdown } from \"@/components/primitives\";\nimport type { Tenant, TenantEnv } from \"@/types/api\";\n\n// Threshold above which the switcher renders a search input. Below this\n// count the list is small enough to scan visually without filtering;\n// the input would just steal a click without saving any.\nconst SEARCH_THRESHOLD = 6;\n\n// Sentinel tenants whose codes start with `_` are platform-internal\n// surfaces (currently `_global` for cross-tenant identity admin). Carry\n// a small system badge so superadmins recognise them as plumbing rather\n// than regular workspaces.\nfunction isSentinelCode(code: string): boolean {\n return code.startsWith(\"_\");\n}\n\nexport interface TenantChipProps {\n tenant: Tenant | null;\n tenants: Tenant[];\n onSwitch: (tenant: Tenant) => void;\n busy?: boolean;\n /**\n * When true, the chip is disabled with a tooltip explaining why.\n * Used during impersonation: cross-tenant targeting at start is\n * supported, but mid-session tenant switching is undefined and\n * locked (plan §\"Cross-tenant impersonation\").\n */\n locked?: boolean;\n lockedReason?: string;\n}\n\ninterface EnvTone {\n fg: string;\n bg: string;\n border?: string;\n darkFg?: string;\n darkBg?: string;\n}\n\nconst ENV_TONES: Record<string, EnvTone> = {\n production: { fg: \"#C2362C\", bg: \"#FAE8E6\", border: \"#F4C7C2\", darkFg: \"#EF6A60\", darkBg: \"rgba(239,106,96,0.14)\" },\n staging: { fg: \"#B56B00\", bg: \"#FBF1E0\", border: \"#F2DFB8\", darkFg: \"#E0A44A\", darkBg: \"rgba(224,164,74,0.14)\" },\n dev: { fg: \"#0B6FB0\", bg: \"#E4F1FA\", border: \"#C9DFEE\", darkFg: \"#5AB3EA\", darkBg: \"rgba(90,179,234,0.14)\" },\n};\n\nconst FALLBACK_TONE: EnvTone = { fg: \"#5B6270\", bg: \"#F3F4F7\", border: \"#E6E8EE\" };\n\nexport function envTone(env?: TenantEnv): EnvTone {\n if (!env) return ENV_TONES.production;\n return ENV_TONES[env] ?? FALLBACK_TONE;\n}\n\nexport function TenantChip({ tenant, tenants, onSwitch, busy, locked, lockedReason }: TenantChipProps) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const btnRef = useRef<HTMLButtonElement>(null);\n const env = tenant?.env || \"production\";\n const tone = envTone(env);\n const name = tenant?.name || \"—\";\n const role = tenant?.role || \"—\";\n\n // Split member vs non-member. is_member is true for everyone the\n // caller has a tenant_users row for; superadmins additionally see\n // tenants they have authority over but no membership in. Treat\n // missing flag as `true` for older API responses (defensive — pre-\n // is_member servers always returned membership-only rows).\n const memberTenants = useMemo(\n () => tenants.filter((t) => t.is_member !== false),\n [tenants],\n );\n const nonMemberTenants = useMemo(\n () => tenants.filter((t) => t.is_member === false),\n [tenants],\n );\n\n const showSearch = tenants.length >= SEARCH_THRESHOLD;\n const matches = (t: Tenant): boolean => {\n if (!query) return true;\n const needle = query.toLowerCase();\n return t.name.toLowerCase().includes(needle) || t.code.toLowerCase().includes(needle);\n };\n const visibleMembers = memberTenants.filter(matches);\n const visibleOthers = nonMemberTenants.filter(matches);\n const noMatches = query !== \"\" && visibleMembers.length === 0 && visibleOthers.length === 0;\n\n return (\n <div style={{ position: \"relative\" }}>\n <button\n ref={btnRef}\n onClick={() => !locked && setOpen((v) => !v)}\n data-testid=\"tenant-chip\"\n data-locked={locked ? \"true\" : undefined}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n height: 28,\n padding: \"0 8px 0 8px\",\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n cursor: locked ? \"not-allowed\" : \"pointer\",\n color: \"var(--text)\",\n fontSize: 12.5,\n fontWeight: 500,\n opacity: locked ? 0.6 : 1,\n }}\n title={locked ? (lockedReason || \"Tenant switching is disabled\") : \"Current tenant / environment\"}\n disabled={busy || locked}\n >\n <Icon.tenant size={13} style={{ color: \"var(--text-3)\" }} />\n <span style={{ fontWeight: 600 }}>{name}</span>\n <span\n className=\"mono\"\n style={{\n padding: \"2px 6px\",\n borderRadius: 3,\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: \"0.06em\",\n textTransform: \"uppercase\",\n background: tone.bg,\n color: tone.fg,\n border: `1px solid ${tone.border || \"transparent\"}`,\n }}\n >\n {env}\n </span>\n <Icon.down size={11} style={{ color: \"var(--text-3)\" }} />\n </button>\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n width={300}\n align=\"start\"\n >\n <div style={{ padding: \"10px 10px 6px\", borderBottom: \"1px solid var(--divider)\" }}>\n <div\n style={{\n fontSize: 10.5,\n color: \"var(--text-3)\",\n letterSpacing: \"0.08em\",\n fontWeight: 700,\n textTransform: \"uppercase\",\n }}\n >\n Active scope\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, marginTop: 4 }}>\n <Icon.tenant size={13} style={{ color: \"var(--accent)\" }} />\n <span style={{ fontSize: 13, fontWeight: 600 }}>{name}</span>\n <span\n className=\"mono\"\n style={{\n marginLeft: \"auto\",\n padding: \"2px 6px\",\n borderRadius: 3,\n fontSize: 10,\n fontWeight: 700,\n background: tone.bg,\n color: tone.fg,\n }}\n >\n {env}\n </span>\n </div>\n <div style={{ marginTop: 4, fontSize: 11, color: \"var(--text-3)\" }}>\n Role:{\" \"}\n <span style={{ color: \"var(--text-2)\", fontWeight: 500 }}>{role}</span>\n </div>\n </div>\n {showSearch && (\n <div style={{ padding: \"8px 10px 4px\", borderBottom: \"1px solid var(--divider)\" }}>\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search tenants…\"\n autoFocus\n data-testid=\"tenant-switch-search\"\n style={{\n width: \"100%\",\n height: 28,\n padding: \"0 8px\",\n fontSize: 12.5,\n background: \"var(--surface-1)\",\n color: \"var(--text)\",\n border: \"1px solid var(--border)\",\n borderRadius: 4,\n outline: \"none\",\n }}\n />\n </div>\n )}\n <div\n style={{\n maxHeight: 320,\n overflowY: \"auto\",\n }}\n >\n <TenantSection\n label={nonMemberTenants.length > 0 ? \"Your tenants\" : \"Switch tenant\"}\n tenants={visibleMembers}\n activeID={tenant?.id}\n onPick={(t) => {\n setOpen(false);\n onSwitch(t);\n }}\n />\n {visibleOthers.length > 0 && (\n <TenantSection\n label=\"All tenants (admin)\"\n tenants={visibleOthers}\n activeID={tenant?.id}\n onPick={(t) => {\n setOpen(false);\n onSwitch(t);\n }}\n />\n )}\n {noMatches && (\n <div\n style={{\n padding: \"12px 12px\",\n fontSize: 12,\n color: \"var(--text-3)\",\n textAlign: \"center\",\n }}\n >\n No tenants match “{query}”.\n </div>\n )}\n </div>\n <div\n style={{\n padding: \"8px 10px\",\n borderTop: \"1px solid var(--divider)\",\n fontSize: 10.5,\n color: \"var(--text-4)\",\n display: \"flex\",\n alignItems: \"flex-start\",\n gap: 6,\n }}\n >\n <Icon.info size={11} style={{ marginTop: 2, flexShrink: 0 }} />\n <span>Switching tenant reloads all data. Unsaved changes in open tabs will prompt.</span>\n </div>\n </Dropdown>\n </div>\n );\n}\n\nfunction TenantSection({\n label,\n tenants,\n activeID,\n onPick,\n}: {\n label: string;\n tenants: Tenant[];\n activeID: string | undefined;\n onPick: (t: Tenant) => void;\n}) {\n if (tenants.length === 0) return null;\n return (\n <>\n <div\n style={{\n padding: \"6px 10px 4px\",\n fontSize: 10,\n color: \"var(--text-4)\",\n letterSpacing: \"0.08em\",\n fontWeight: 700,\n textTransform: \"uppercase\",\n }}\n >\n {label}\n </div>\n {tenants.map((t) => (\n <TenantRow\n key={t.id}\n tenant={t}\n tone={envTone(t.env)}\n active={t.id === activeID}\n onPick={() => onPick(t)}\n />\n ))}\n </>\n );\n}\n\nfunction TenantRow({\n tenant,\n tone,\n active,\n onPick,\n}: {\n tenant: Tenant;\n tone: EnvTone;\n active: boolean;\n onPick: () => void;\n}) {\n const [hover, setHover] = useState(false);\n const bg = active ? \"var(--surface-3)\" : hover ? \"var(--surface-3)\" : \"transparent\";\n const sentinel = isSentinelCode(tenant.code);\n return (\n <button\n onClick={onPick}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n width: \"100%\",\n padding: \"7px 10px\",\n background: bg,\n border: 0,\n cursor: \"pointer\",\n textAlign: \"left\",\n }}\n >\n <Icon.tenant size={12} style={{ color: \"var(--text-3)\" }} />\n <span style={{ fontSize: 12.5, color: \"var(--text)\", fontWeight: 500 }}>{tenant.name}</span>\n {sentinel ? (\n <span\n className=\"mono\"\n title=\"Platform-internal workspace\"\n style={{\n padding: \"1px 5px\",\n borderRadius: 3,\n fontSize: 9.5,\n fontWeight: 700,\n background: \"var(--surface-3)\",\n color: \"var(--text-2)\",\n border: \"1px solid var(--border)\",\n }}\n >\n system\n </span>\n ) : (\n <span\n className=\"mono\"\n style={{\n padding: \"1px 5px\",\n borderRadius: 3,\n fontSize: 9.5,\n fontWeight: 700,\n background: tone.bg,\n color: tone.fg,\n }}\n >\n {tenant.env || \"production\"}\n </span>\n )}\n <span style={{ flex: 1 }} />\n {tenant.role && (\n <span style={{ fontSize: 10.5, color: \"var(--text-3)\" }}>{tenant.role}</span>\n )}\n {active && <Icon.check size={12} style={{ color: \"var(--accent)\", marginLeft: 4 }} />}\n </button>\n );\n}\n","import { Fragment, type ReactNode, useState } from \"react\";\nimport { Icon, type IconName } from \"@/components/primitives\";\n\nexport interface Crumb {\n label: string;\n icon?: IconName | string;\n onClick?: () => void;\n target?: unknown;\n /** True for the final segment (rendered bold per design spec). */\n isLast?: boolean;\n}\n\nexport interface BreadcrumbsProps {\n items: Crumb[];\n onCrumbClick?: (crumb: Crumb) => void;\n}\n\nexport function Breadcrumbs({ items, onCrumbClick }: BreadcrumbsProps) {\n if (!items || items.length === 0) return null;\n return (\n <nav\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 2,\n fontSize: 12,\n minWidth: 0,\n overflow: \"hidden\",\n flex: \"0 1 auto\",\n }}\n >\n {items.map((c, i) => {\n const last = i === items.length - 1;\n const clickable = !!c.onClick || !!c.target;\n return (\n <Fragment key={i}>\n {i > 0 && (\n <span style={{ color: \"var(--text-4)\", padding: \"0 4px\" }}>\n <Icon.right size={11} />\n </span>\n )}\n <CrumbButton crumb={c} last={last} clickable={clickable} onClick={onCrumbClick} />\n </Fragment>\n );\n })}\n </nav>\n );\n}\n\nfunction CrumbButton({\n crumb,\n last,\n clickable,\n onClick,\n}: {\n crumb: Crumb;\n last: boolean;\n clickable: boolean;\n onClick?: (crumb: Crumb) => void;\n}) {\n const [hover, setHover] = useState(false);\n const IconComp = crumb.icon && (Icon as Record<string, unknown>)[crumb.icon as string]\n ? (Icon as unknown as Record<string, (props: { size?: number; style?: object }) => ReactNode>)[crumb.icon as string]\n : Icon.folder;\n return (\n <button\n onClick={() => {\n if (clickable) {\n if (onClick) onClick(crumb);\n else if (crumb.onClick) crumb.onClick();\n }\n }}\n disabled={!clickable}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => setHover(false)}\n style={{\n background: hover && clickable ? \"var(--surface-3)\" : \"transparent\",\n border: 0,\n padding: \"4px 6px\",\n borderRadius: 4,\n color: last ? \"var(--text)\" : hover && clickable ? \"var(--text)\" : \"var(--text-3)\",\n fontWeight: last ? 600 : 500,\n cursor: clickable ? \"pointer\" : \"default\",\n whiteSpace: \"nowrap\",\n maxWidth: 200,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n fontSize: 12.5,\n }}\n >\n {crumb.icon && IconComp && (\n <span style={{ marginRight: 4, display: \"inline-block\", verticalAlign: \"-2px\" }}>\n <IconComp size={12} />\n </span>\n )}\n {crumb.label}\n </button>\n );\n}\n","import { useRef, useState } from \"react\";\nimport {\n Icon,\n IconBtn,\n Avatar,\n Dropdown,\n MenuItem,\n Pill,\n Kbd,\n} from \"@/components/primitives\";\nimport type { Tenant } from \"@/types/api\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useAuth } from \"@/hooks/useAuth\";\nimport { useImpersonation } from \"@/hooks/useImpersonation\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { useTabStore } from \"@/stores/tabs\";\nimport { TenantChip, envTone } from \"./TenantChip\";\nimport { Breadcrumbs, type Crumb } from \"@/components/shared/Breadcrumbs\";\n\nexport interface TopBarProps {\n breadcrumbs?: Crumb[];\n onCrumbClick?: (c: Crumb) => void;\n onCmdK?: () => void;\n onToggleAI?: () => void;\n aiOpen?: boolean;\n onOpenProfile?: () => void;\n onOpenPrefs?: () => void;\n}\n\nexport function TopBar({\n breadcrumbs,\n onCrumbClick,\n onCmdK,\n onToggleAI,\n aiOpen,\n onOpenProfile,\n onOpenPrefs,\n}: TopBarProps = {}) {\n const user = useAuthStore((s) => s.user);\n const { activeTenant, tenants, switchTenant, isSwitchingTenant, logout } = useAuth();\n const { isImpersonating, stop: stopImpersonation } = useImpersonation();\n const theme = useAppearanceStore((s) => s.theme);\n const setAppearance = useAppearanceStore((s) => s.set);\n\n const [userMenu, setUserMenu] = useState(false);\n const userBtnRef = useRef<HTMLButtonElement>(null);\n\n const env = activeTenant?.env || \"production\";\n const tone = envTone(env);\n\n const email = user?.email ?? \"\";\n const displayName = user?.display_name || email || \"User\";\n\n const toggleTheme = () => {\n setAppearance(\"theme\", theme === \"dark\" ? \"light\" : \"dark\");\n };\n\n const handleSwitchTenant = (t: Tenant) => {\n if (activeTenant?.id === t.id) return;\n // Plan T4: prompt before switching tenants when any tab has unsaved\n // edits. Switch reloads all queries, so dirty in-memory drafts would\n // be lost without warning.\n const dirty = useTabStore.getState().tabs.some((tab) => tab.dirty);\n if (dirty) {\n const proceed = window.confirm(\n \"You have unsaved changes in one or more tabs. Switching tenant will discard them. Continue?\",\n );\n if (!proceed) return;\n }\n switchTenant(t.id);\n };\n\n return (\n <header\n style={{\n gridArea: \"topbar\",\n background: \"var(--surface)\",\n borderBottom: \"1px solid var(--border)\",\n display: \"flex\",\n alignItems: \"center\",\n padding: \"0 12px 0 12px\",\n gap: 10,\n zIndex: 5,\n position: \"relative\",\n }}\n >\n {env === \"production\" && (\n <span\n style={{\n position: \"absolute\",\n left: 0,\n right: 0,\n top: 0,\n height: 2,\n background: `linear-gradient(90deg, transparent, ${tone.fg}, transparent)`,\n opacity: 0.35,\n pointerEvents: \"none\",\n }}\n />\n )}\n\n <TenantChip\n tenant={activeTenant}\n tenants={tenants}\n onSwitch={handleSwitchTenant}\n busy={isSwitchingTenant}\n locked={isImpersonating}\n lockedReason={isImpersonating ? \"Exit impersonation to switch tenants\" : undefined}\n />\n {isImpersonating && (\n <span\n data-testid=\"impersonation-chip\"\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 4,\n height: 22,\n padding: \"0 8px\",\n borderRadius: 999,\n background: \"var(--impersonation-accent-soft)\",\n color: \"var(--impersonation-accent-fg)\",\n fontSize: 11,\n fontWeight: 600,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n <Icon.users size={11} /> Impersonating\n </span>\n )}\n\n <span style={{ color: \"var(--text-4)\", fontSize: 11 }}>\n <Icon.right size={10} />\n </span>\n\n <Breadcrumbs items={breadcrumbs ?? []} onCrumbClick={onCrumbClick} />\n\n <div style={{ flex: 1 }} />\n\n <button\n onClick={onCmdK}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"0 8px 0 10px\",\n height: 28,\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n cursor: \"pointer\",\n color: \"var(--text-3)\",\n fontSize: 12,\n minWidth: 240,\n }}\n >\n <Icon.search size={13} />\n <span style={{ flex: 1, textAlign: \"left\" }}>Search or run command…</span>\n <Kbd>⌘K</Kbd>\n </button>\n\n <IconBtn icon={<Icon.bell size={15} />} title=\"Notifications\" />\n <IconBtn\n icon={theme === \"dark\" ? <Icon.sun size={15} /> : <Icon.moon size={15} />}\n title=\"Theme\"\n onClick={toggleTheme}\n />\n <IconBtn\n icon={<Icon.sparkle size={15} />}\n title=\"AI Assistant\"\n onClick={onToggleAI}\n active={aiOpen}\n />\n <div style={{ width: 1, height: 18, background: \"var(--divider)\", margin: \"0 2px\" }} />\n <div style={{ position: \"relative\" }}>\n <button\n ref={userBtnRef}\n onClick={() => setUserMenu((v) => !v)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"0 4px 0 2px\",\n height: 28,\n background: \"transparent\",\n border: 0,\n cursor: \"pointer\",\n }}\n >\n <Avatar name={displayName} size={22} />\n <span style={{ fontSize: 12, color: \"var(--text-2)\" }}>{email}</span>\n <Icon.down size={11} style={{ color: \"var(--text-3)\" }} />\n </button>\n <Dropdown\n open={userMenu}\n anchor={userBtnRef.current}\n onClose={() => setUserMenu(false)}\n width={220}\n align=\"end\"\n >\n <div style={{ padding: \"10px 10px 8px\", borderBottom: \"1px solid var(--divider)\" }}>\n <div style={{ display: \"flex\", gap: 8, alignItems: \"center\" }}>\n <Avatar name={displayName} size={28} />\n <div style={{ minWidth: 0 }}>\n <div style={{ fontSize: 12.5, fontWeight: 600, color: \"var(--text)\" }}>\n {displayName}\n </div>\n <div\n style={{\n fontSize: 11,\n color: \"var(--text-3)\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {email}\n </div>\n </div>\n </div>\n {activeTenant && (\n <div style={{ marginTop: 8, display: \"flex\", gap: 4, flexWrap: \"wrap\" }}>\n <Pill tone=\"violet\" size=\"xs\" dot>\n {activeTenant.role}\n </Pill>\n <Pill tone=\"slate\" size=\"xs\">\n {activeTenant.name} · {env}\n </Pill>\n </div>\n )}\n </div>\n {isImpersonating && (\n <MenuItem\n icon={<Icon.x size={13} />}\n onClick={() => {\n setUserMenu(false);\n stopImpersonation();\n }}\n data-testid=\"exit-impersonation-menu-item\"\n >\n Exit impersonation\n </MenuItem>\n )}\n <MenuItem\n icon={<Icon.users size={13} />}\n onClick={() => {\n setUserMenu(false);\n onOpenProfile?.();\n }}\n >\n My profile\n </MenuItem>\n <MenuItem\n icon={<Icon.settings size={13} />}\n onClick={() => {\n setUserMenu(false);\n onOpenPrefs?.();\n }}\n >\n Preferences\n </MenuItem>\n {/* \"API keys\" and \"Security & sessions\" live as sections INSIDE\n /profile (custom Profile component) - one self-service entry\n point. \"Switch tenant…\" removed because the top-left tenant\n chip already owns that flow; two switchers = duplicated UX. */}\n <MenuItem icon={<Icon.moon size={13} />} onClick={toggleTheme}>\n Toggle theme\n </MenuItem>\n <MenuItem divider />\n <MenuItem\n icon={<Icon.x size={13} />}\n danger\n onClick={() => {\n setUserMenu(false);\n logout();\n }}\n >\n Sign out\n </MenuItem>\n </Dropdown>\n </div>\n </header>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { Dropdown, Icon, MenuItem } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { tabIdsForCloseScope, useTabStore, type TabCloseScope, type TabItem } from \"@/stores/tabs\";\n\nexport interface TabStripProps {\n onNew?: () => void;\n onCloseRequest?: (scope: TabCloseScope, targetId?: string) => void;\n}\n\nexport function TabStrip({ onNew: onNewProp, onCloseRequest }: TabStripProps = {}) {\n const navigate = useNavigate();\n const kebabRef = useRef<HTMLButtonElement | null>(null);\n const activeTabRef = useRef<HTMLDivElement | null>(null);\n const newTabButtonRef = useRef<HTMLButtonElement | null>(null);\n const hasAutoScrolledRef = useRef(false);\n const [kebabOpen, setKebabOpen] = useState(false);\n const tabs = useTabStore((s) => s.tabs);\n const activeTabId = useTabStore((s) => s.activeTabId);\n const tabsMaxOpen = useAppearanceStore((s) => s.tabsMaxOpen);\n const switchTab = useTabStore((s) => s.switchTab);\n const closeTab = useTabStore((s) => s.closeTab);\n\n const active = tabs.findIndex((t) => t.id === activeTabId);\n\n // Keep the active tab inside the scrollable strip. A newly opened tab is\n // appended at the end, so without this it lands off-screen to the right.\n // When the active tab is the last one, scroll the trailing \"New tab\"\n // button into view instead - that reveals the last tab and keeps the\n // button reachable. The first run (tab state restored on load) snaps\n // without animation; every later open/switch animates.\n useEffect(() => {\n const target =\n active === tabs.length - 1 ? newTabButtonRef.current : activeTabRef.current;\n if (!target) return;\n target.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n behavior: hasAutoScrolledRef.current ? \"smooth\" : \"auto\",\n });\n hasAutoScrolledRef.current = true;\n }, [activeTabId, active, tabs.length]);\n\n const onPick = useCallback(\n (i: number, _opts: { newTab: boolean }) => {\n const t = tabs[i];\n if (!t) return;\n switchTab(t.id);\n navigate({ pathname: t.pathname, search: t.search || undefined });\n },\n [tabs, switchTab, navigate],\n );\n\n const onClose = useCallback(\n (i: number) => {\n const t = tabs[i];\n if (!t) return;\n if (onCloseRequest) {\n onCloseRequest(\"current\", t.id);\n return;\n }\n const isActive = t.id === activeTabId;\n closeTab(t.id);\n if (isActive) {\n const remaining = tabs.filter((x) => x.id !== t.id);\n if (remaining.length > 0) {\n const nextIdx = i < remaining.length ? i : remaining.length - 1;\n const next = remaining[nextIdx];\n navigate({ pathname: next.pathname, search: next.search || undefined });\n }\n }\n },\n [tabs, activeTabId, closeTab, navigate, onCloseRequest],\n );\n\n const onMenuClose = useCallback(\n (scope: TabCloseScope) => {\n setKebabOpen(false);\n if (onCloseRequest) {\n onCloseRequest(scope);\n return;\n }\n if (scope === \"current\" && active >= 0) onClose(active);\n },\n [active, onClose, onCloseRequest],\n );\n\n const onNew = useCallback(() => {\n if (onNewProp) onNewProp();\n else navigate(\"/\");\n }, [onNewProp, navigate]);\n\n if (tabs.length === 0) return null;\n\n const canCloseCurrent = active >= 0 && tabs.length > 1 && tabs[active]?.closable !== false;\n const canCloseOthers = tabIdsForCloseScope(tabs, activeTabId, \"others\").length > 0;\n const canCloseRight = tabIdsForCloseScope(tabs, activeTabId, \"right\").length > 0;\n const countTone = tabs.length >= tabsMaxOpen\n ? \"danger\"\n : tabs.length >= Math.ceil(tabsMaxOpen * 0.8)\n ? \"warning\"\n : \"normal\";\n const countColor = countTone === \"danger\"\n ? \"var(--danger)\"\n : countTone === \"warning\"\n ? \"var(--warn)\"\n : \"var(--text-3)\";\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"stretch\",\n height: 34,\n background: \"var(--surface-2)\",\n borderBottom: \"1px solid var(--border)\",\n overflow: \"hidden\",\n flexShrink: 0,\n }}\n >\n <div\n className=\"scrollbar-none\"\n style={{ display: \"flex\", alignItems: \"stretch\", overflowX: \"auto\", flex: 1, gap: 0 }}\n >\n {tabs.map((t: TabItem, i: number) => {\n const isActive = i === active;\n const canCloseTab = tabs.length > 1 && t.closable !== false;\n return (\n <div\n key={t.id}\n ref={isActive ? activeTabRef : undefined}\n onClick={(e) => onPick(i, { newTab: e.metaKey || e.ctrlKey })}\n onAuxClick={(e) => {\n if (e.button === 1) {\n e.preventDefault();\n onClose(i);\n }\n }}\n title={t.title}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n height: \"100%\",\n padding: \"0 10px 0 12px\",\n background: isActive ? \"var(--surface)\" : \"transparent\",\n borderRight: \"1px solid var(--border)\",\n borderTop: isActive ? \"2px solid var(--accent)\" : \"2px solid transparent\",\n borderBottom: isActive ? \"1px solid var(--surface)\" : \"1px solid transparent\",\n marginBottom: isActive ? -1 : 0,\n cursor: \"pointer\",\n position: \"relative\",\n maxWidth: 220,\n minWidth: 120,\n color: isActive ? \"var(--text)\" : \"var(--text-3)\",\n fontSize: 12,\n fontWeight: isActive ? 500 : 400,\n }}\n >\n {t.icon && (\n <span\n style={{\n color: isActive ? \"var(--accent)\" : \"var(--text-4)\",\n display: \"flex\",\n }}\n >\n <DynamicIcon name={t.icon} size={12} />\n </span>\n )}\n <span\n style={{\n flex: 1,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n }}\n >\n {t.title}\n </span>\n {t.dirty && (\n <span\n style={{\n width: 6,\n height: 6,\n borderRadius: 999,\n background: \"var(--warn)\",\n flexShrink: 0,\n }}\n title=\"Unsaved changes\"\n />\n )}\n {canCloseTab && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onClose(i);\n }}\n aria-label={`Close ${t.title}`}\n title=\"Close tab\"\n style={{\n display: \"flex\",\n background: \"transparent\",\n border: 0,\n padding: 2,\n borderRadius: 3,\n cursor: \"pointer\",\n color: \"var(--text-4)\",\n opacity: isActive ? 1 : 0.5,\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = \"var(--surface-3)\";\n e.currentTarget.style.color = \"var(--text)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = \"transparent\";\n e.currentTarget.style.color = \"var(--text-4)\";\n }}\n >\n <Icon.x size={11} />\n </button>\n )}\n </div>\n );\n })}\n <button\n ref={newTabButtonRef}\n onClick={onNew}\n title=\"New tab (⌘T)\"\n style={{\n width: 32,\n background: \"transparent\",\n border: 0,\n borderRight: \"1px solid var(--border)\",\n cursor: \"pointer\",\n color: \"var(--text-3)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Icon.plus size={13} />\n </button>\n </div>\n <div\n className=\"tabstrip-utility\"\n style={{\n padding: \"0 8px 0 12px\",\n fontSize: 11,\n color: \"var(--text-4)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n borderLeft: \"1px solid var(--border)\",\n flexShrink: 0,\n }}\n >\n <span\n data-testid=\"tabstrip-tab-count\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 3,\n color: countColor,\n fontWeight: countTone === \"normal\" ? 400 : 600,\n }}\n >\n <span className=\"mono num\">{tabs.length}/{tabsMaxOpen}</span>\n <span className=\"tabstrip-tabs-label\" style={{ color: \"var(--text-4)\" }}>tabs</span>\n </span>\n <span\n className=\"tabstrip-utility-divider\"\n style={{ width: 1, height: 14, background: \"var(--border)\" }}\n />\n <button\n ref={kebabRef}\n onClick={() => setKebabOpen(true)}\n title=\"Tab actions\"\n aria-label=\"Tab actions\"\n data-testid=\"tabstrip-actions-button\"\n style={{\n width: 24,\n height: 24,\n border: 0,\n background: \"transparent\",\n borderRadius: 4,\n cursor: \"pointer\",\n color: \"var(--text-3)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.background = \"var(--surface-3)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = \"transparent\";\n }}\n >\n <Icon.more size={13} />\n </button>\n <TabActionsMenu\n open={kebabOpen}\n anchor={kebabRef.current}\n onClose={() => setKebabOpen(false)}\n canCloseCurrent={canCloseCurrent}\n canCloseOthers={canCloseOthers}\n canCloseRight={canCloseRight}\n onAction={onMenuClose}\n />\n </div>\n </div>\n );\n}\n\ninterface TabActionsMenuProps {\n open: boolean;\n anchor: HTMLElement | null;\n onClose: () => void;\n canCloseCurrent: boolean;\n canCloseOthers: boolean;\n canCloseRight: boolean;\n onAction: (scope: TabCloseScope) => void;\n}\n\nfunction TabActionsMenu({\n open,\n anchor,\n onClose,\n canCloseCurrent,\n canCloseOthers,\n canCloseRight,\n onAction,\n}: TabActionsMenuProps) {\n return (\n <Dropdown open={open} onClose={onClose} anchor={anchor} align=\"end\" width={230}>\n <MenuItem\n icon={<Icon.x size={13} />}\n disabled={!canCloseCurrent}\n title={canCloseCurrent ? undefined : \"No current tab can be closed\"}\n onClick={() => onAction(\"current\")}\n >\n Close current tab\n </MenuItem>\n <MenuItem\n icon={<Icon.x size={13} />}\n disabled={!canCloseOthers}\n title={canCloseOthers ? undefined : \"No other tabs\"}\n onClick={() => onAction(\"others\")}\n >\n Close others\n </MenuItem>\n <MenuItem\n icon={<Icon.x size={13} />}\n disabled={!canCloseRight}\n title={canCloseRight ? undefined : \"No tabs to the right\"}\n onClick={() => onAction(\"right\")}\n >\n Close tabs to the right\n </MenuItem>\n <MenuItem divider />\n <MenuItem icon={<Icon.trash size={13} />} danger onClick={() => onAction(\"all\")}>\n Close all tabs\n </MenuItem>\n </Dropdown>\n );\n}\n","import { useEffect } from \"react\";\nimport { Btn, Icon, IconBtn } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport type { PendingTabClose } from \"./tab-close\";\n\ninterface DirtyTabsCloseDialogProps {\n pending: PendingTabClose | null;\n onCancel: () => void;\n onCloseClean: () => void;\n onDiscardAll: () => void;\n}\n\nexport function DirtyTabsCloseDialog({\n pending,\n onCancel,\n onCloseClean,\n onDiscardAll,\n}: DirtyTabsCloseDialogProps) {\n useEffect(() => {\n if (!pending) return undefined;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onCancel();\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [pending, onCancel]);\n\n if (!pending) return null;\n\n const totalCount = pending.ids.length;\n const dirtyCount = pending.dirtyTabs.length;\n const cleanCount = pending.cleanIds.length;\n const title = `Close ${totalCount} ${totalCount === 1 ? \"tab\" : \"tabs\"}?`;\n\n return (\n <>\n <div\n onClick={onCancel}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 460,\n maxWidth: \"calc(100vw - 32px)\",\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"14px 18px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: \"var(--warn-soft)\",\n color: \"var(--warn)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n <Icon.warning size={15} />\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>{title}</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", marginTop: 2 }}>\n {dirtyCount} {dirtyCount === 1 ? \"tab has\" : \"tabs have\"} unsaved changes.\n </div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={onCancel} title=\"Cancel\" />\n </div>\n <div style={{ padding: \"0 18px 14px\" }}>\n <div\n style={{\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: \"8px 10px\",\n maxHeight: 190,\n overflow: \"auto\",\n }}\n >\n {pending.dirtyTabs.map((tab) => (\n <div\n key={tab.id}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"4px 0\",\n fontSize: 12,\n color: \"var(--text-2)\",\n minWidth: 0,\n }}\n >\n <span\n style={{\n width: 6,\n height: 6,\n borderRadius: 999,\n background: \"var(--warn)\",\n flexShrink: 0,\n }}\n />\n {tab.icon && <DynamicIcon name={tab.icon} size={12} style={{ color: \"var(--text-4)\" }} />}\n <span style={{ whiteSpace: \"nowrap\", overflow: \"hidden\", textOverflow: \"ellipsis\" }}>\n {tab.title}\n </span>\n </div>\n ))}\n </div>\n {cleanCount > 0 && (\n <div style={{ fontSize: 11.5, color: \"var(--text-4)\", marginTop: 10 }}>\n Other {cleanCount} {cleanCount === 1 ? \"tab has\" : \"tabs have\"} no unsaved changes.\n </div>\n )}\n </div>\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n <div style={{ flex: 1 }} />\n <Btn onClick={onCancel}>Cancel</Btn>\n {cleanCount > 0 && (\n <Btn autoFocus onClick={onCloseClean}>\n Close clean only\n </Btn>\n )}\n <Btn kind=\"primary\" danger onClick={onDiscardAll}>\n {cleanCount > 0 ? \"Discard all\" : \"Discard\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { useImpersonation } from \"@/hooks/useImpersonation\";\n\n/**\n * Sticky top-of-viewport banner shown for the entire duration of an\n * impersonation session (principle 8: \"UI signals impersonation loudly\").\n * Never dismissable. Real user, target identity, expiry countdown, and\n * an explicit \"End session\" button are always visible.\n *\n * Color is driven by the `--impersonation-accent` CSS variable in\n * globals.css. The body[data-impersonation-destructive] flip is wired\n * separately by ImpersonationFrame so the banner + frame stay in lockstep.\n */\nexport function ImpersonationBanner() {\n const { info, stop, isStopping } = useImpersonation();\n const [now, setNow] = useState(() => Date.now());\n\n useEffect(() => {\n if (!info) return;\n const id = window.setInterval(() => setNow(Date.now()), 1000);\n return () => window.clearInterval(id);\n }, [info]);\n\n if (!info) return null;\n\n const expiresAt = new Date(info.expires_at).getTime();\n const remainingMs = Math.max(0, expiresAt - now);\n const minutes = Math.floor(remainingMs / 60000);\n const seconds = Math.floor((remainingMs % 60000) / 1000);\n const countdown = `${String(minutes).padStart(2, \"0\")}:${String(seconds).padStart(2, \"0\")}`;\n\n return (\n <div\n role=\"alert\"\n aria-live=\"polite\"\n data-testid=\"impersonation-banner\"\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n right: 0,\n background: \"var(--impersonation-accent)\",\n color: \"var(--impersonation-accent-fg)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 16,\n padding: \"8px 16px\",\n fontSize: 13,\n fontWeight: 600,\n borderBottom: \"2px solid var(--impersonation-accent-fg)\",\n zIndex: 50,\n }}\n >\n <span aria-hidden=\"true\">⚠</span>\n <span>\n You are acting as <strong data-testid=\"impersonation-target\">{info.target_user_email || info.target_user_id}</strong>\n </span>\n <span style={{ opacity: 0.85 }}>\n · Real user: <strong>{info.real_user_email || info.real_user_id}</strong>\n </span>\n <span style={{ opacity: 0.85 }}>· Reason: {info.reason}</span>\n <span style={{ flex: 1 }} />\n <span data-testid=\"impersonation-countdown\" aria-label=\"Time remaining\">\n Ends in {countdown}\n </span>\n <button\n onClick={() => stop()}\n disabled={isStopping}\n data-testid=\"impersonation-end-btn\"\n style={{\n background: \"var(--impersonation-accent-fg)\",\n color: \"var(--impersonation-accent)\",\n border: 0,\n padding: \"4px 12px\",\n borderRadius: 4,\n fontSize: 12,\n fontWeight: 700,\n cursor: isStopping ? \"wait\" : \"pointer\",\n }}\n >\n {isStopping ? \"Ending…\" : \"End session\"}\n </button>\n </div>\n );\n}\n","import { apiFetch } from \"./client\";\nimport { executeAction } from \"./data\";\nimport type {\n DataResponse,\n ParamLookup,\n PublicParam,\n PublicParamsResponse,\n} from \"@/types/api\";\n\n/**\n * Fetches the full pre-login envelope from /api/params/public:\n * {parameters, accents, branding, meta}. Used by AuthShell to theme\n * unauthenticated screens before any user context exists.\n */\nexport async function fetchPublicParams(): Promise<PublicParamsResponse> {\n const res = await apiFetch<DataResponse<PublicParamsResponse>>(\"/api/params/public\");\n // Tolerate older servers that returned a bare array of PublicParam[].\n const data = res.data as unknown;\n if (Array.isArray(data)) {\n return { parameters: data as PublicParam[] };\n }\n const env = (data ?? {}) as Partial<PublicParamsResponse>;\n return {\n parameters: env.parameters ?? [],\n accents: env.accents,\n messages: env.messages,\n branding: env.branding,\n meta: env.meta,\n };\n}\n\n/**\n * Looks up a single parameter from the platform. Always returns a\n * `ParamLookup`; `found:false` signals \"no value, use your default\".\n * Network/auth failures still throw - \"missing\" is normal, \"broken\"\n * is an error.\n */\nexport async function fetchParam(code: string): Promise<ParamLookup> {\n const res = await apiFetch<DataResponse<ParamLookup>>(\n `/api/params/${encodeURIComponent(code)}`,\n );\n return res.data;\n}\n\n/**\n * Persist a parameter override at user scope. Writes via the platform-default\n * user.__update action endpoint (POST /api/actions/user.__update) with the\n * $params subresource block inside `fields`. Per plan 2026-06-01-write-api-final\n * §1.2 the wire envelope is FLAT: `object_ids` is the only reserved key, and\n * `entity` + `fields` sit at the top level (decoded into DataUpdateParams via\n * JSON tags). subresource.ExtractSubResources routes the $params array to\n * declarion.object_params at write time.\n *\n * Plan G5/T23 contract: ParameterDef.scopes gates the write server-side;\n * a 400 will surface back to the caller if the scope is not allowed.\n */\nexport async function setUserParam(userId: string, code: string, value: unknown): Promise<void> {\n await executeAction(\"user.__update\", {\n object_ids: [userId],\n entity: \"user\",\n fields: { $params: [{ param_code: code, value }] },\n });\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { fetchPublicParams, fetchParam } from \"@/api/params\";\nimport type { ParamLookup } from \"@/types/api\";\n\nexport function usePublicParams() {\n return useQuery({\n queryKey: [\"params-public\"],\n queryFn: fetchPublicParams,\n staleTime: 5 * 60 * 1000,\n });\n}\n\n/**\n * Looks up the platform's record for a parameter without a default.\n * Returns the full ParamLookup envelope (`found`, `value`, `source`).\n * Prefer `useParam` for typical reads where the call site has a default.\n */\nexport function useResolvedParam(code: string) {\n return useQuery({\n queryKey: [\"params\", code],\n queryFn: () => fetchParam(code),\n enabled: !!code,\n staleTime: 60 * 1000,\n });\n}\n\n/**\n * Reads a parameter and substitutes a caller-supplied default when the\n * platform has no value for it. The default never crosses the wire; the\n * server only answers \"do you have a value for code X?\".\n *\n * Behaviour:\n * - Platform has a value -> `value` is the resolved value.\n * - Platform reports no value -> `value` is `defaultValue`. Not an error.\n * - Network / auth failure -> `error` is set; `value` is `defaultValue`\n * so the UI has something to render during a partial outage.\n *\n * Important: this React hook intentionally diverges from the Go-side\n * contract. In Go (`params.Get[T]`), a transport error propagates and\n * the default is NEVER used for broken infrastructure. In React, the\n * hook returns the default AND surfaces the error so a component can\n * still mount during a brownout. Callers whose parameter controls\n * security-sensitive behaviour (a feature gate that hides admin UI,\n * a permission flag, etc.) MUST check `error` and refuse to render\n * the protected branch when set - do not rely on `value` alone.\n *\n * Devs may call useParam with a code that is not yet declared in YAML;\n * the platform reports not-found and the default applies. That is by\n * design: knobs become declarable later as ops asks for tunability.\n *\n * Type T is enforced by the caller. The hook does not coerce - if the\n * stored value's shape is incompatible with T, that is a programming\n * bug at the call site to be fixed.\n */\nexport function useParam<T>(code: string, defaultValue: T): {\n value: T;\n found: boolean;\n source: ParamLookup[\"source\"] | undefined;\n loading: boolean;\n error: Error | null;\n} {\n const q = useQuery({\n queryKey: [\"params\", code],\n queryFn: () => fetchParam(code),\n enabled: !!code,\n staleTime: 60 * 1000,\n });\n const lookup = q.data;\n const found = !!lookup?.found;\n const value = found ? (lookup!.value as T) : defaultValue;\n return {\n value,\n found,\n source: lookup?.source,\n loading: q.isLoading,\n error: (q.error as Error | null) ?? null,\n };\n}\n","import { useEffect } from \"react\";\nimport { useImpersonation } from \"@/hooks/useImpersonation\";\nimport { useParam } from \"@/hooks/useParams\";\n\n/**\n * Fixed 4px viewport overlay frame that surrounds every route. Visible\n * for the duration of an impersonation session (principle 8). The\n * pointer-events:none guarantees the frame never blocks clicks.\n *\n * Color comes from --impersonation-accent (amber by default). When\n * impersonation.allow_destructive=true, this component sets a body\n * attribute that flips the variable to red across the banner, chip,\n * and frame in lockstep (one CSS variable, no parallel state).\n */\nexport function ImpersonationFrame() {\n const { info } = useImpersonation();\n const { value: allowDestructive } = useParam<boolean>(\n info ? \"impersonation.allow_destructive\" : \"\",\n false,\n );\n\n useEffect(() => {\n if (!info) {\n document.body.removeAttribute(\"data-impersonation-destructive\");\n return;\n }\n if (allowDestructive) {\n document.body.setAttribute(\"data-impersonation-destructive\", \"true\");\n } else {\n document.body.removeAttribute(\"data-impersonation-destructive\");\n }\n return () => {\n document.body.removeAttribute(\"data-impersonation-destructive\");\n };\n }, [info, allowDestructive]);\n\n if (!info) return null;\n\n return (\n <div\n data-testid=\"impersonation-frame\"\n aria-hidden=\"true\"\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n border: \"4px solid var(--impersonation-accent)\",\n pointerEvents: \"none\",\n zIndex: 9999,\n }}\n />\n );\n}\n","import type { TabCloseScope, TabItem } from \"@/stores/tabs\";\nimport { tabIdsForCloseScope } from \"@/stores/tabs\";\n\nexport interface PendingTabClose {\n ids: string[];\n dirtyTabs: TabItem[];\n cleanIds: string[];\n}\n\nexport interface TabCloseRoute {\n pathname: string;\n search?: string;\n replace?: boolean;\n}\n\nexport function buildPendingTabClose(\n tabs: TabItem[],\n activeTabId: string | null,\n scope: TabCloseScope,\n targetId?: string,\n): PendingTabClose | null {\n if (scope === \"current\" && tabs.length <= 1) return null;\n const ids = tabIdsForCloseScope(tabs, activeTabId, scope, targetId);\n if (ids.length === 0) return null;\n\n const idSet = new Set(ids);\n const dirtyTabs = tabs.filter((t) => idSet.has(t.id) && t.dirty);\n const dirtyIds = new Set(dirtyTabs.map((t) => t.id));\n return {\n ids,\n dirtyTabs,\n cleanIds: ids.filter((id) => !dirtyIds.has(id)),\n };\n}\n\nexport function tabCloseRoute(nextTab: TabItem | null): TabCloseRoute {\n if (!nextTab) return { pathname: \"/\", replace: true };\n return {\n pathname: nextTab.pathname,\n search: nextTab.search || undefined,\n };\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/ai.jsx\n// (CommandPalette). Wired to real schema navigation instead of APPDATA.\nimport { useEffect, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport { Icon, Kbd, type IconName } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport type { TabCloseScope } from \"@/stores/tabs\";\nimport { loc, type NavNode, type Screen } from \"@/types/schema\";\n\nexport type CommandActionType = \"nav\" | \"new\" | \"open\" | \"ai\" | \"ai-panel\" | \"theme\" | \"tab-close\";\n\nexport interface CommandAction {\n type: CommandActionType;\n id?: string;\n route?: string;\n q?: string;\n tabCloseScope?: TabCloseScope;\n}\n\nexport interface CommandPaletteProps {\n open: boolean;\n onClose: () => void;\n onAction: (a: CommandAction) => void;\n mode?: \"default\" | \"new\";\n navigation?: NavNode[];\n screens?: Record<string, Screen>;\n includeAI?: boolean;\n aiName?: string;\n}\n\ninterface Cmd {\n group: string;\n icon: ReactNode;\n label: string;\n hint?: string;\n act: () => void;\n actionType: CommandActionType;\n}\n\nfunction builtinIcon(name: string | undefined, size = 14) {\n if (!name) return null;\n const K = Icon[name as IconName];\n if (K) return <K size={size} />;\n return <DynamicIcon name={name} size={size} />;\n}\n\nfunction buildNavCommands(\n navigation: NavNode[] | undefined,\n screens: Record<string, Screen> | undefined,\n onAction: (a: CommandAction) => void,\n): Cmd[] {\n if (!navigation || !screens) return [];\n const out: Cmd[] = [];\n const visit = (n: NavNode) => {\n if (n.screen) {\n const s = screens[n.screen];\n if (s) {\n const label = loc(n.name) || n.screen;\n out.push({\n group: \"Navigate\",\n icon: builtinIcon(n.icon || s.display?.icon || \"folder\"),\n label: `Go to ${label}`,\n act: () => onAction({ type: \"nav\", id: n.screen, route: s.route }),\n actionType: \"nav\",\n });\n if (s.type === \"list\" && s.entity) {\n out.push({\n group: \"Create\",\n icon: <Icon.plus size={14} />,\n label: `New ${label}`,\n act: () => onAction({ type: \"new\", id: n.screen, route: s.route }),\n actionType: \"new\",\n });\n }\n }\n }\n for (const c of n.items || []) visit(c);\n for (const c of n.children || []) visit(c);\n };\n for (const n of navigation) visit(n);\n return out;\n}\n\nexport function CommandPalette({\n open,\n onClose,\n onAction,\n mode = \"default\",\n navigation,\n screens,\n includeAI = true,\n aiName = \"\",\n}: CommandPaletteProps) {\n const [q, setQ] = useState(\"\");\n const [idx, setIdx] = useState(0);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (open) {\n setTimeout(() => inputRef.current?.focus(), 50);\n setQ(mode === \"new\" ? \"New \" : \"\");\n setIdx(0);\n }\n }, [open, mode]);\n\n const commands = useMemo<Cmd[]>(() => {\n const list: Cmd[] = [];\n list.push(...buildNavCommands(navigation, screens, onAction));\n if (includeAI) {\n list.push(\n { group: \"AI\", icon: <Icon.sparkle size={14} />, label: \"Draft YAML for new entity…\", hint: \"✨\", act: () => onAction({ type: \"ai\", q: \"draft entity\" }), actionType: \"ai\" },\n { group: \"AI\", icon: <Icon.sparkle size={14} />, label: \"Explain this record with AI\", hint: \"✨\", act: () => onAction({ type: \"ai-panel\" }), actionType: \"ai-panel\" },\n { group: \"AI\", icon: <Icon.sparkle size={14} />, label: \"Summarize activity on selected rows\", hint: \"✨\", act: () => onAction({ type: \"ai-panel\" }), actionType: \"ai-panel\" },\n );\n }\n list.push(\n { group: \"Tabs\", icon: <Icon.x size={14} />, label: \"Close all tabs\", act: () => onAction({ type: \"tab-close\", tabCloseScope: \"all\" }), actionType: \"tab-close\" },\n { group: \"Tabs\", icon: <Icon.x size={14} />, label: \"Close other tabs\", act: () => onAction({ type: \"tab-close\", tabCloseScope: \"others\" }), actionType: \"tab-close\" },\n { group: \"Tabs\", icon: <Icon.x size={14} />, label: \"Close tabs to the right\", act: () => onAction({ type: \"tab-close\", tabCloseScope: \"right\" }), actionType: \"tab-close\" },\n { group: \"Settings\", icon: <Icon.settings size={14} />, label: \"Open Preferences\", act: () => onAction({ type: \"nav\", id: \"preferences\", route: \"/preferences\" }), actionType: \"nav\" },\n { group: \"Settings\", icon: <Icon.moon size={14} />, label: \"Toggle dark mode\", act: () => onAction({ type: \"theme\" }), actionType: \"theme\" },\n );\n if (mode === \"new\") return list.filter((c) => c.actionType === \"new\");\n return list;\n }, [navigation, screens, includeAI, onAction, mode]);\n\n const filtered = !q\n ? commands\n : commands.filter(\n (c) =>\n c.label.toLowerCase().includes(q.toLowerCase()) ||\n c.group.toLowerCase().includes(q.toLowerCase()),\n );\n const groups: Record<string, Array<Cmd & { _i: number }>> = {};\n filtered.forEach((c, i) => {\n groups[c.group] = groups[c.group] || [];\n groups[c.group].push({ ...c, _i: i });\n });\n\n if (!open) return null;\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setIdx(Math.min(idx + 1, filtered.length - 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setIdx(Math.max(idx - 1, 0));\n } else if (e.key === \"Enter\" && filtered[idx]) {\n filtered[idx].act();\n onClose();\n } else if (e.key === \"Escape\") {\n onClose();\n }\n };\n\n return (\n <>\n <div\n onClick={onClose}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim-strong)\",\n zIndex: 70,\n backdropFilter: \"blur(3px)\",\n }}\n />\n <div\n role=\"dialog\"\n aria-label=\"Command palette\"\n style={{\n position: \"fixed\",\n top: \"12vh\",\n left: \"50%\",\n transform: \"translateX(-50%)\",\n width: 640,\n zIndex: 71,\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"70vh\",\n }}\n onKeyDown={onKeyDown}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"14px 16px\",\n gap: 10,\n borderBottom: \"1px solid var(--divider)\",\n }}\n >\n <Icon.command size={15} style={{ color: \"var(--text-3)\" }} />\n <input\n ref={inputRef}\n value={q}\n onChange={(e) => {\n setQ(e.target.value);\n setIdx(0);\n }}\n placeholder={includeAI ? \"Search or run command — type / for AI\" : \"Search or run command\"}\n style={{\n flex: 1,\n border: 0,\n outline: 0,\n background: \"transparent\",\n fontSize: 14,\n color: \"var(--text)\",\n }}\n />\n <Kbd>Esc</Kbd>\n </div>\n <div style={{ flex: 1, overflowY: \"auto\", padding: 6 }}>\n {Object.entries(groups).map(([g, items]) => (\n <div key={g} style={{ marginBottom: 6 }}>\n <div\n style={{\n padding: \"8px 10px 4px\",\n fontSize: 10,\n fontWeight: 700,\n color: \"var(--text-3)\",\n letterSpacing: \"0.08em\",\n textTransform: \"uppercase\",\n }}\n >\n {g}\n </div>\n {items.map((c) => (\n <div\n key={`${c.group}:${c.label}`}\n onClick={() => {\n c.act();\n onClose();\n }}\n onMouseEnter={() => setIdx(c._i)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n padding: \"7px 10px\",\n borderRadius: 6,\n cursor: \"pointer\",\n background: c._i === idx ? \"var(--accent-soft)\" : \"transparent\",\n color: c._i === idx ? \"var(--accent)\" : \"var(--text-2)\",\n }}\n >\n <span\n style={{\n color: c._i === idx ? \"var(--accent)\" : \"var(--text-3)\",\n display: \"flex\",\n }}\n >\n {c.icon}\n </span>\n <span style={{ flex: 1, fontSize: 13 }}>{c.label}</span>\n {c.hint && (\n <span className=\"mono\" style={{ fontSize: 10.5, color: \"var(--text-4)\" }}>\n {c.hint}\n </span>\n )}\n </div>\n ))}\n </div>\n ))}\n {filtered.length === 0 && (\n <div\n style={{\n padding: \"24px 16px\",\n textAlign: \"center\",\n color: \"var(--text-3)\",\n fontSize: 12.5,\n }}\n >\n <Icon.sparkle size={20} style={{ color: \"var(--accent)\", marginBottom: 8 }} />\n <div>{includeAI ? `Press Enter to ask ${aiName}:` : \"No matches\"}</div>\n {includeAI && (\n <div style={{ fontWeight: 600, color: \"var(--text)\", marginTop: 4 }}>\n “{q}”\n </div>\n )}\n </div>\n )}\n </div>\n <div\n style={{\n padding: \"8px 12px\",\n borderTop: \"1px solid var(--divider)\",\n background: \"var(--surface-2)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n fontSize: 11,\n color: \"var(--text-3)\",\n }}\n >\n <span style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n <Kbd>↑↓</Kbd> navigate\n </span>\n <span style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n <Kbd>↵</Kbd> select\n </span>\n {includeAI && (\n <span style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n <Kbd>/</Kbd> ask AI\n </span>\n )}\n <div style={{ flex: 1 }} />\n <span className=\"mono\">Declarion</span>\n </div>\n </div>\n </>\n );\n}\n","// API helpers for the Clara agents subsystem. The chat protocol\n// is plain CRUD over /api/data/{agent_*} per the Task 8 contract;\n// this file just types the rows + provides convenience wrappers\n// the chat panel consumes.\n\nimport { createEntity, executeAction, listEntities, updateEntity } from \"./data\";\n\nexport type AgentRole = \"user\" | \"assistant\" | \"tool\" | \"system\";\n\nexport type AgentContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: Record<string, unknown> }\n | { type: \"tool_result\"; id: string; name?: string; output: unknown; is_error?: boolean }\n | { type: \"ui_hint\"; kind: string; target?: unknown; ttl_ms?: number; label?: string }\n | { type: \"elicit\"; id: string; schema: Record<string, unknown>; message: string }\n | { type: \"elicit_response\"; id: string; data: Record<string, unknown> }\n | { type: \"attachment\"; kind: string; content: string }\n | { type: \"summary\"; covers: string[]; text: string }\n | { type: \"system\"; reason: string }\n | {\n type: \"decision\";\n tool_use_id?: string;\n tool_code: string;\n decision: \"approved\" | \"rejected\";\n decided_by?: string;\n decided_at?: string;\n note?: string;\n };\n\nexport interface AgentMessage {\n id: string;\n conversation_id: string;\n seq: number;\n role: AgentRole;\n content: AgentContentBlock[];\n created_at: string;\n streaming?: boolean;\n}\n\nexport type AgentInvocationStatus =\n | \"proposed\"\n | \"pending_approval\"\n | \"approved\"\n | \"rejected\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"canceled\"\n | \"interrupted\";\n\nexport interface AgentToolInvocation {\n id: string;\n conversation_id: string;\n message_id: string | null;\n tool_code: string;\n params: Record<string, unknown>;\n status: AgentInvocationStatus;\n approval?: { decided_by?: string; decided_at?: string; reason?: string; edits?: Record<string, unknown> };\n result?: unknown;\n audit_operation_id?: string;\n // LLM-supplied sidecar metadata. reason: per-call rationale in\n // the user's language (doubles as approval-card summary + audit\n // trail). confidence: 0-1 self-estimate. Both optional.\n reason?: string;\n confidence?: number;\n}\n\nexport type AgentConversationStatus = \"active\" | \"canceling\" | \"interrupted\" | \"closed\";\n\nexport interface AgentConversation {\n id: string;\n tenant_id: string;\n // The owning user is `created_by` (auto-filled by the platform\n // from the JWT on insert). No separate user_id column - that was\n // a redundant denormalization that confused who-owns-the-row.\n created_by?: string;\n agent_code: string;\n agent_version?: string;\n title?: string;\n context?: Record<string, unknown>;\n model?: string;\n status: AgentConversationStatus;\n token_usage?: { in?: number; out?: number; cached_in?: number; cost_cents?: number };\n catalog_snapshot?: unknown;\n active_instance?: string;\n}\n\n// createConversation creates an agent_conversation row. Frontend\n// calls this on first send so every agent_message can be POSTed\n// with a conversation_id already set (the server-side handler no\n// longer patches it). Subsequent sends in the same chat reuse the\n// returned id.\nexport async function createConversation(input: {\n agent_code?: string;\n context?: Record<string, unknown>;\n}): Promise<AgentConversation> {\n const res = await createEntity(\"agent_conversation\", {\n status: \"active\",\n ...(input.agent_code ? { agent_code: input.agent_code } : {}),\n ...(input.context ? { context: input.context } : {}),\n });\n return res.data as unknown as AgentConversation;\n}\n\n// sendMessage POSTs a new role=user message. The server's\n// $on_created handler runs the LLM turn ASYNCHRONOUSLY (mode=async\n// step → platform job queue), so this returns in ~50ms and the\n// optimistic UI render doesn't wait on the LLM.\n//\n// `conversation_id` is required - the frontend creates the\n// conversation explicitly via createConversation() first. `seq` is\n// allocated server-side by the MessageWriter for assistant + tool\n// rows; for user rows the data engine assigns it via the seq\n// allocator (next-seq-per-conversation lock).\nexport async function sendMessage(\n text: string,\n conversationId: string,\n // seq parameter retained for backward-compat at the call site;\n // ignored. Server allocates seq atomically via the migration-0005\n // trigger so the client + server can't race. Drop the arg in a\n // follow-up cleanup once all callers stop passing it.\n _seq?: number,\n): Promise<AgentMessage> {\n const row: Record<string, unknown> = {\n role: \"user\",\n content: [{ type: \"text\", text }],\n conversation_id: conversationId,\n // seq=0 is the trigger's \"auto-allocate\" sentinel.\n seq: 0,\n };\n const res = await createEntity(\"agent_message\", row);\n return res.data as unknown as AgentMessage;\n}\n\n// sendElicitResponse posts the user's elicit form answers as a\n// role=user agent_message with a single elicit_response block.\n// History projection on the server-side maps this to a tool_result\n// block paired with the LLM's elicit.ask tool_use id, so the LLM\n// gets the answers on its next turn.\nexport async function sendElicitResponse(\n conversationId: string,\n // seq parameter retained for backward-compat; ignored. Server\n // allocates atomically via the trigger.\n _seq: number | undefined,\n toolUseId: string,\n data: Record<string, unknown>,\n): Promise<AgentMessage> {\n const row: Record<string, unknown> = {\n role: \"user\",\n content: [{ type: \"elicit_response\", id: toolUseId, data }],\n conversation_id: conversationId,\n seq: 0,\n };\n const res = await createEntity(\"agent_message\", row);\n return res.data as unknown as AgentMessage;\n}\n\nexport async function approveInvocation(\n id: string,\n edits?: Record<string, unknown>,\n): Promise<void> {\n const patch: Record<string, unknown> = { status: \"approved\" };\n if (edits) patch.params = edits;\n await updateEntity(\"agent_tool_invocation\", id, patch);\n}\n\nexport async function rejectInvocation(id: string, note?: string): Promise<void> {\n await updateEntity(\"agent_tool_invocation\", id, {\n status: \"rejected\",\n ...(note ? { approval: { reason: note } } : {}),\n });\n}\n\nexport async function cancelConversation(id: string): Promise<void> {\n await updateEntity(\"agent_conversation\", id, { status: \"canceling\" });\n}\n\nexport async function listConversations(): Promise<AgentConversation[]> {\n const res = await listEntities(\"agent_conversation\", {\n sort: \"updated_at:desc\",\n page: 1,\n per_page: 50,\n });\n return (res.data ?? []) as unknown as AgentConversation[];\n}\n\n// rerunMessageTurn idempotently re-fires the LLM turn against the\n// conversation's existing last user message. Use when the previous\n// turn failed mid-stream (context-canceled, LLM 5xx, network drop).\n// No new agent_message row created — the transcript stays clean.\nexport async function rerunMessageTurn(conversationId: string): Promise<void> {\n await executeAction(\"agents.rerun_message_turn\", {\n args: { conversation_id: conversationId },\n });\n}\n\n// listPendingInvocations returns every agent_tool_invocation row in\n// status=pending_approval for the conversation. Used on hook mount\n// to rehydrate the approval card after a page reload or server\n// restart — without this the row keeps waiting in the DB but the UI\n// never shows it again until a new turn fires another tool.pending\n// SSE frame.\nexport async function listPendingInvocations(conversationId: string): Promise<AgentToolInvocation[]> {\n const res = await listEntities(\"agent_tool_invocation\", {\n filters: [\n { field: \"conversation_id\", op: \"eq\", value: conversationId },\n { field: \"status\", op: \"eq\", value: \"pending_approval\" },\n ],\n sort: \"created_at:asc\",\n per_page: 200,\n });\n return (res.data ?? []) as unknown as AgentToolInvocation[];\n}\n\nexport async function loadHistory(conversationId: string): Promise<AgentMessage[]> {\n // Sort by created_at, NOT seq. seq is now advisory (the UNIQUE\n // constraint was dropped in agents migration 0003); created_at\n // is the canonical ordering. Postgres timestamptz has\n // microsecond resolution so ties are vanishingly rare.\n const res = await listEntities(\"agent_message\", {\n filters: [{ field: \"conversation_id\", op: \"eq\", value: conversationId }],\n sort: \"created_at:asc\",\n per_page: 1000,\n });\n return (res.data ?? []) as unknown as AgentMessage[];\n}\n","export type SSEEvent =\n | {\n type: \"entity_changed\";\n eventID?: string;\n entity: string;\n ids: string[];\n objects?: Array<{ object_id?: string; pk?: Record<string, string> }>;\n action: \"created\" | \"updated\" | \"deleted\" | \"restored\";\n }\n | { type: \"checkpoint\"; eventID: string }\n | {\n // realtime_degraded: the CDC relay's A3 high-volume monitor\n // auto-degraded one entity (its change rate sustained above the\n // configured threshold). The relay stops fanning that entity's row\n // changes out; the UI must fall back to the manual refresh control\n // for it, the same as a `realtime: false` entity.\n type: \"realtime_degraded\";\n eventID?: string;\n entity: string;\n degradedSeq?: number;\n }\n | {\n type: \"cache_reset\";\n eventID?: string;\n reason: string;\n requestedSeq?: number;\n oldestSeq?: number;\n resetSeq?: number;\n }\n | { type: \"session_refresh\" };\n\n// agentBus is the in-process fanout for agent_stream SSE frames.\n// We have a single /api/events fetch (owned by useSSE) and the\n// chat hook subscribes here instead of opening a parallel stream.\n// Browsers throttle / starve concurrent body-streaming fetches to\n// the same origin (Chrome HTTP/1.1 limit is 6, but in practice\n// streaming bodies hold connections so the second SSE often\n// receives nothing). One stream + JS fanout is the right shape.\n//\n// Custom event detail: {convId, kind, turnId?, payload}.\nexport const agentBus = new EventTarget();\nexport const AGENT_FRAME_EVENT = \"agent-frame\";\n\n// Fired whenever entity_changed arrives for `agent_message`. The\n// chat hook listens and refetches history for the active\n// conversation; this is the safety net that surfaces the assistant\n// reply even when the agent_stream frames were lost (proxy idle\n// timeout, brief disconnect window, etc). The MessageWriter has\n// already persisted the durable row by the time this fires, so\n// loadHistory returns the answer.\nexport const AGENT_MESSAGE_CHANGED_EVENT = \"agent-message-changed\";\n\nexport interface AgentFrameDetail {\n convId: string;\n kind: string;\n turnId?: string;\n payload?: Record<string, unknown>;\n}\n\nexport function emitAgentFrame(detail: AgentFrameDetail): void {\n agentBus.dispatchEvent(new CustomEvent(AGENT_FRAME_EVENT, { detail }));\n}\n\n// SSEAuthError signals that /api/events returned 401 — the access\n// token is expired or invalid. The reconnect loop in useSSE catches\n// this and runs ensureRefresh() before retrying, instead of looping\n// forever with the dead cookie.\nexport class SSEAuthError extends Error {\n constructor() {\n super(\"SSE auth expired\");\n this.name = \"SSEAuthError\";\n }\n}\n\nexport interface StreamEventsOptions {\n signal?: AbortSignal;\n lastEventID?: string;\n /**\n * Embed token mode: the scoped embed JWT. When set, it is sent as\n * `Authorization: Bearer <token>` and the request omits cookies (see\n * `credentials` below). Outside embed mode this is undefined and the\n * cookie path runs unchanged.\n */\n bearerToken?: string;\n /**\n * Cookie policy for the SSE fetch. Defaults to `\"include\"` (the cookie\n * path). Embed mode passes `\"omit\"`: the iframe is same-origin to\n * Declarion and the auth middleware reads the cookie before the Bearer\n * header, so a sent cookie would bypass the scoped embed token.\n */\n credentials?: RequestCredentials;\n}\n\nexport async function* streamEvents(\n options: StreamEventsOptions = {},\n): AsyncGenerator<SSEEvent> {\n const headers = new Headers();\n if (options.lastEventID) {\n headers.set(\"Last-Event-ID\", options.lastEventID);\n }\n if (options.bearerToken) {\n headers.set(\"Authorization\", `Bearer ${options.bearerToken}`);\n }\n\n const res = await fetch(\"/api/events\", {\n credentials: options.credentials ?? \"include\",\n signal: options.signal,\n headers,\n });\n\n if (res.status === 401) {\n throw new SSEAuthError();\n }\n if (!res.ok || !res.body) {\n throw new Error(`SSE connection failed: ${res.status}`);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent = \"\";\n let currentData = \"\";\n let currentID = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event: \")) {\n currentEvent = line.slice(7);\n } else if (line.startsWith(\"id: \")) {\n currentID = line.slice(4);\n } else if (line === \"id:\") {\n currentID = \"\";\n } else if (line.startsWith(\"data: \")) {\n currentData = line.slice(6);\n } else if (line === \"\" && currentEvent) {\n try {\n if (currentEvent === \"session_refresh\") {\n yield { type: \"session_refresh\" };\n } else if (currentEvent === \"checkpoint\" && currentID) {\n yield { type: \"checkpoint\", eventID: currentID };\n } else if (currentEvent === \"realtime_degraded\" && currentData) {\n const parsed = JSON.parse(currentData) as {\n entity?: string;\n degraded_seq?: number;\n };\n if (parsed.entity) {\n yield {\n type: \"realtime_degraded\",\n eventID: currentID || undefined,\n entity: parsed.entity,\n degradedSeq: parsed.degraded_seq,\n };\n }\n } else if (currentEvent === \"cache_reset\" && currentData) {\n const parsed = JSON.parse(currentData) as {\n reason?: string;\n requested_seq?: number;\n oldest_seq?: number;\n reset_seq?: number;\n };\n yield {\n type: \"cache_reset\",\n eventID: currentID || undefined,\n reason: parsed.reason || \"replay_gap\",\n requestedSeq: parsed.requested_seq,\n oldestSeq: parsed.oldest_seq,\n resetSeq: parsed.reset_seq,\n };\n } else if (currentEvent === \"entity_changed\" && currentData) {\n const parsed = JSON.parse(currentData) as {\n entity: string;\n ids: string[];\n objects?: Array<{ object_id?: string; pk?: Record<string, string> }>;\n action: \"created\" | \"updated\" | \"deleted\" | \"restored\";\n };\n // agent_stream frames piggyback on the\n // entity_changed envelope. Dispatch them through\n // the agent bus instead of yielding (the regular\n // entity_changed consumer doesn't care about\n // agents). ids[0]=conv_id, ids[1]=kind,\n // ids[2]=turn_id, ids[3]=json payload.\n if (parsed.entity === \"agent_stream\") {\n let payload: Record<string, unknown> | undefined;\n try {\n payload = parsed.ids[3] ? JSON.parse(parsed.ids[3]) : undefined;\n } catch {\n payload = undefined;\n }\n emitAgentFrame({\n convId: parsed.ids[0],\n kind: parsed.action,\n turnId: parsed.ids[2],\n payload,\n });\n // Don't yield agent_stream events to the regular\n // consumer; they're not entity rows and would\n // trigger spurious cache invalidations.\n } else {\n if (parsed.entity === \"agent_message\") {\n // Notify the chat hook to refetch history so the\n // persisted assistant row appears even when the\n // agent_stream stream missed frames.\n agentBus.dispatchEvent(\n new CustomEvent(AGENT_MESSAGE_CHANGED_EVENT, {\n detail: { rowId: parsed.ids[0], action: parsed.action },\n }),\n );\n }\n yield {\n type: \"entity_changed\",\n eventID: currentID || undefined,\n entity: parsed.entity,\n ids: parsed.ids,\n objects: parsed.objects,\n action: parsed.action,\n };\n }\n }\n } catch {\n // Ignore parse errors\n }\n currentEvent = \"\";\n currentData = \"\";\n currentID = \"\";\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","// useAgentConversation drives the chat-panel state machine: sends\n// messages, subscribes to the agents SSE stream, accumulates text\n// deltas, and surfaces tool invocations + elicit forms.\n//\n// SSE wire shape: the platform's events.Hub publishes our agent\n// frames as Entity=\"agent_stream\", IDs=[conversation_id, kind,\n// turn_id, json_payload], Action=kind. We piggyback on the same\n// /api/events transport the rest of the UI uses.\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n approveInvocation,\n cancelConversation,\n createConversation,\n listConversations,\n listPendingInvocations,\n loadHistory,\n rejectInvocation,\n rerunMessageTurn,\n sendElicitResponse,\n sendMessage,\n type AgentContentBlock,\n type AgentConversation,\n type AgentMessage,\n} from \"@/api/agents\";\nimport {\n agentBus,\n AGENT_FRAME_EVENT,\n AGENT_MESSAGE_CHANGED_EVENT,\n type AgentFrameDetail,\n} from \"@/api/events\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { ApiError } from \"@/types/api\";\n\nexport interface AgentTurnState {\n // Pending tool proposals waiting for user approval. reason is\n // the LLM's per-call rationale (in the user's language); doubles\n // as the approval-card summary and audit trail. confidence is\n // the LLM's self-estimated 0-1 score. Both optional.\n pendingTools: Array<{\n id: string;\n name: string;\n params: Record<string, unknown>;\n reason?: string;\n confidence?: number;\n }>;\n // Pending elicit forms waiting for user input (path 2: LLM-asked\n // structured questions). id is the original elicit.ask tool_use\n // id; the user's answers post back as a tool_result paired with\n // this id. schema is JSON Schema (object with `properties`).\n elicits: Array<{\n id: string;\n message: string;\n schema: Record<string, unknown>;\n }>;\n // Visual hints emitted during the turn.\n hints: Array<{ kind: string; target?: unknown; ttlMs?: number }>;\n // True while the LLM is generating; flips back to false on\n // turn.finished or error.\n streaming: boolean;\n // Last error message if the turn failed.\n error?: string;\n}\n\nconst initialTurn: AgentTurnState = {\n pendingTools: [],\n elicits: [],\n hints: [],\n streaming: false,\n};\n\n// Browser-storage prefix for the active conversation id. Survives page\n// refresh so the chat panel reattaches to history instead of looking like\n// the messages got lost. The full key is suffixed with the active\n// tenant id (`declarion.agents.conversation_id:<tenant_id>`) — without\n// scoping, a browser carrying a stored id from tenant A into tenant B\n// (signup, tenant switch, dev DB rebuild) makes the next message POST\n// reference a conversation row that doesn't exist in tenant B's data,\n// failing 422 FOREIGN_KEY_VIOLATION. Tenant scoping makes this class of\n// staleness structurally impossible.\nconst CONVERSATION_STORAGE_KEY_PREFIX = \"declarion.agents.conversation_id\";\n\nfunction conversationStorageKey(tenantId: string | undefined): string | undefined {\n if (!tenantId) return undefined;\n return `${CONVERSATION_STORAGE_KEY_PREFIX}:${tenantId}`;\n}\n\nfunction readStoredConversationId(tenantId: string | undefined): string | undefined {\n if (typeof window === \"undefined\") return undefined;\n const key = conversationStorageKey(tenantId);\n if (!key) return undefined;\n try {\n return window.localStorage.getItem(key) || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeStoredConversationId(tenantId: string | undefined, id: string | undefined): void {\n if (typeof window === \"undefined\") return;\n const key = conversationStorageKey(tenantId);\n if (!key) return;\n try {\n if (id) window.localStorage.setItem(key, id);\n else window.localStorage.removeItem(key);\n } catch {\n // Storage unavailable (private mode, quota): silently degrade\n // to ephemeral conversation - panel still works, just won't\n // survive refresh. Not worth surfacing to UI.\n }\n}\n\n// isStaleConversationFKError reports whether an error from sendMessage\n// indicates the conversation row referenced by the request no longer\n// exists. This is the recoverable failure mode — tenant scoping handles\n// the common cross-tenant case, but data manipulation outside the UI\n// (admin deleting a conversation, partial backups) plus the brief mount\n// → load → send race window can still produce a stale id within a\n// single tenant. The send path treats this as \"create a fresh\n// conversation and retry once\" rather than surfacing as an error.\nfunction isStaleConversationFKError(err: unknown): boolean {\n if (!(err instanceof ApiError)) return false;\n if (err.code !== \"FOREIGN_KEY_VIOLATION\") return false;\n // The platform's 23503 mapper inlines the constraint name in the\n // message; the agent_messages → agent_conversations FK is the only\n // one we can recover from on this code path. Other FK violations\n // (tenant_id, user_id) indicate a deeper bug and should surface.\n return err.message.includes(\"agent_messages_tenant_conv_fk\");\n}\n\nfunction createStreamingAssistantMessage(messageId: string, conversationId: string): AgentMessage {\n return {\n id: messageId,\n conversation_id: conversationId,\n seq: 0,\n role: \"assistant\",\n content: [],\n created_at: new Date().toISOString(),\n streaming: true,\n };\n}\n\nfunction stopStreamingAssistantMessages(messages: AgentMessage[], exceptId?: string): AgentMessage[] {\n let changed = false;\n const next = messages.map((message) => {\n if (message.role !== \"assistant\" || !message.streaming || message.id === exceptId) return message;\n changed = true;\n return { ...message, streaming: false };\n });\n return changed ? next : messages;\n}\n\nfunction finalizeAssistantMessage(messages: AgentMessage[], messageId: string): AgentMessage[] {\n let changed = false;\n const next: AgentMessage[] = [];\n for (const message of messages) {\n if (message.id !== messageId || message.role !== \"assistant\") {\n next.push(message);\n continue;\n }\n changed = true;\n if (message.content.length === 0) {\n continue;\n }\n if (message.streaming) {\n next.push({ ...message, streaming: false });\n continue;\n }\n next.push(message);\n }\n return changed ? next : messages;\n}\n\nfunction finalizeAllStreamingAssistantMessages(messages: AgentMessage[]): AgentMessage[] {\n let next = messages;\n for (const message of messages) {\n if (message.role !== \"assistant\" || !message.streaming) continue;\n next = finalizeAssistantMessage(next, message.id);\n }\n return next;\n}\n\nfunction upsertAssistantMessage(\n messages: AgentMessage[],\n conversationId: string,\n messageId: string,\n updater: (message: AgentMessage) => AgentMessage,\n): AgentMessage[] {\n let found = false;\n const next = messages.map((message) => {\n if (message.id !== messageId) return message;\n found = true;\n return updater(message);\n });\n if (found) return next;\n return [...messages, updater(createStreamingAssistantMessage(messageId, conversationId))];\n}\n\nfunction appendDeltaToAssistantMessage(\n messages: AgentMessage[],\n conversationId: string,\n messageId: string,\n text: string,\n): AgentMessage[] {\n if (!text) return messages;\n // Late deltas after turn.finished must not resurrect a finalized\n // row; once streaming flips to false, the content is authoritative.\n const existing = messages.find((m) => m.id === messageId);\n if (existing && !existing.streaming) return messages;\n // Lazy-create path (delta arrives before assistant.message.created)\n // must also stop streaming on prior iterations, same as the\n // created handler does.\n const base = stopStreamingAssistantMessages(messages, messageId);\n return upsertAssistantMessage(base, conversationId, messageId, (message) => {\n const content = [...message.content];\n const last = content[content.length - 1];\n if (last?.type === \"text\") {\n content[content.length - 1] = { ...last, text: last.text + text };\n } else {\n content.push({ type: \"text\", text });\n }\n return { ...message, streaming: true, content };\n });\n}\n\nfunction appendToolProposalToAssistantMessage(\n messages: AgentMessage[],\n conversationId: string,\n messageId: string,\n toolUseId: string,\n name: string,\n input: Record<string, unknown>,\n): AgentMessage[] {\n const existing = messages.find((m) => m.id === messageId);\n if (existing && !existing.streaming) return messages;\n const base = stopStreamingAssistantMessages(messages, messageId);\n return upsertAssistantMessage(base, conversationId, messageId, (message) => {\n if (toolUseId && message.content.some((block) => block.type === \"tool_use\" && block.id === toolUseId)) {\n return message.streaming ? message : { ...message, streaming: true };\n }\n return {\n ...message,\n streaming: true,\n content: [\n ...message.content,\n {\n type: \"tool_use\",\n id: toolUseId || `pending-${messageId}-${message.content.length}`,\n name,\n input,\n },\n ],\n };\n });\n}\n\n// mergeHistoryRows: durable rows from loadHistory are authoritative.\n// Local in-flight streaming rows (no durable counterpart yet) are\n// appended so the user keeps seeing partial assistant output while\n// the writer commits. Once the durable row lands for the same id,\n// it replaces the local placeholder verbatim — durable is always\n// more complete because the writer flushes the full iteration's\n// content blocks at end-of-iteration, after the last delta.\nfunction mergeHistoryRows(rows: AgentMessage[], localMessages: AgentMessage[]): AgentMessage[] {\n const output = rows.map((row) => (row.streaming ? { ...row, streaming: false } : row));\n const durableIds = new Set(output.map((row) => row.id));\n const durableToolResultIds = new Set<string>();\n for (const row of output) {\n if (row.role !== \"tool\") continue;\n for (const block of row.content) {\n if (block.type === \"tool_result\" && block.id) durableToolResultIds.add(block.id);\n }\n }\n for (const message of localMessages) {\n if (message.role === \"assistant\") {\n if (!message.streaming) continue;\n if (durableIds.has(message.id)) continue;\n output.push(message);\n continue;\n }\n if (message.role === \"tool\") {\n if (durableIds.has(message.id)) continue;\n const toolResult = message.content.find((block) => block.type === \"tool_result\");\n if (toolResult && durableToolResultIds.has(toolResult.id)) continue;\n output.push(message);\n }\n }\n return output;\n}\n\nfunction appendToolResultMessage(\n messages: AgentMessage[],\n conversationId: string,\n localMessageId: string,\n toolUseId: string,\n name: string,\n output: unknown,\n isError: boolean,\n): AgentMessage[] {\n const content: AgentContentBlock[] = [{\n type: \"tool_result\",\n id: toolUseId || localMessageId,\n name,\n output,\n is_error: isError,\n }];\n const existing = messages.findIndex((message) => message.id === localMessageId);\n if (existing >= 0) {\n const next = messages.slice();\n next[existing] = { ...next[existing], role: \"tool\", content, streaming: false };\n return next;\n }\n return [...messages, {\n id: localMessageId,\n conversation_id: conversationId,\n seq: 0,\n role: \"tool\",\n content,\n created_at: new Date().toISOString(),\n }];\n}\n\n// unansweredElicitsFromHistory returns the elicit.ask tool_uses in the\n// most recent assistant message that have NOT been answered by a later\n// elicit_response. Used on history load to rebuild the inline forms\n// queue so a refresh while the user has open forms keeps them visible.\n// Mirrors the backend gating in handlers.go's\n// unansweredElicitsInLatestTurn — same semantics, different surface.\nfunction unansweredElicitsFromHistory(\n messages: AgentMessage[],\n): Array<{ id: string; message: string; schema: Record<string, unknown> }> {\n let latestAssistant: AgentMessage | undefined;\n let latestAssistantIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"assistant\") {\n latestAssistant = messages[i];\n latestAssistantIdx = i;\n break;\n }\n }\n if (!latestAssistant) return [];\n const asks = latestAssistant.content.flatMap((b) =>\n b.type === \"tool_use\" && b.name === \"elicit.ask\" && b.id\n ? [{\n id: b.id,\n message: typeof (b.input as Record<string, unknown>)?.message === \"string\"\n ? ((b.input as Record<string, unknown>).message as string)\n : \"\",\n schema: (((b.input as Record<string, unknown>)?.schema as Record<string, unknown>) ?? {}),\n }]\n : [],\n );\n if (asks.length === 0) return [];\n const answered = new Set<string>();\n for (let i = latestAssistantIdx + 1; i < messages.length; i++) {\n for (const block of messages[i].content) {\n if (block.type === \"elicit_response\" && block.id) answered.add(block.id);\n }\n }\n return asks.filter((a) => !answered.has(a.id));\n}\n\nfunction readMessageId(frame: AgentStreamFrame): string | undefined {\n const candidate = frame.payload?.message_id;\n return typeof candidate === \"string\" && candidate ? candidate : undefined;\n}\n\nexport function useAgentConversation(initialConversationId?: string) {\n // Tenant scope is read once at hook init so the storage key is stable\n // across renders — switching tenant tears down/remounts Layout, which\n // remounts this hook with the new tenant id. Subscribing to the auth\n // store inside this hook would be over-engineered: an in-flight\n // tenant switch never delivers messages to the previous tenant's\n // panel because the POST carries the new tenant via cookie/JWT.\n const tenantId = useAuthStore.getState().activeTenant?.id;\n const [conversationId, setConversationIdInternal] = useState<string | undefined>(\n initialConversationId ?? readStoredConversationId(tenantId),\n );\n const setConversationId = useCallback(\n (id: string | undefined) => {\n writeStoredConversationId(tenantId, id);\n setConversationIdInternal(id);\n },\n [tenantId],\n );\n const [messages, setMessages] = useState<AgentMessage[]>([]);\n const [turn, setTurn] = useState<AgentTurnState>(initialTurn);\n const [conversations, setConversations] = useState<AgentConversation[]>([]);\n\n // Tracks conversation ids this hook instance just created via\n // send(). loadHistory must NOT fire for those - the optimistic\n // user-message row is already in `messages` and a fetch would\n // overwrite it with the empty-history server response during the\n // brief createConversation -> first sendMessage window. The id is\n // removed the first time the conversationId-driven load effect\n // observes it, so only that one initial effect run is skipped.\n const freshConvIds = useRef<Set<string>>(new Set());\n\n // historyGen monotonically increments on every refetchOnMessageChange\n // dispatch; the resolver compares against current value and discards\n // stale results. Without this, two entity_changed events (e.g., user\n // message + assistant message in quick succession) can race: the\n // SECOND load returns first with the assistant row, then the FIRST\n // load returns with the older snapshot and overwrites — wiping the\n // assistant text from the visible history. The DB still has it\n // (refresh recovers), but the live view shows the streamed answer\n // disappearing on turn.finished.\n const historyGen = useRef(0);\n\n // Initial history load when the conversation id is set/changed.\n // 404 from the platform = stored id no longer valid (purged,\n // wrong tenant after re-login, etc.); clear it so the next send\n // starts a fresh conversation instead of repeatedly 404-ing.\n //\n // Also rehydrate any pending_approval invocations into pendingTools.\n // The DB persists these across page reloads + server restarts (per\n // the original plan §15(3): \"Pending approvals DO survive\"), but\n // pendingTools is in-memory React state that doesn't. Without this\n // load step, the user sees the conversation history but no\n // approval card after F5 — even though the gate is still waiting.\n useEffect(() => {\n if (!conversationId) {\n setMessages([]);\n setTurn(initialTurn);\n return;\n }\n if (freshConvIds.current.has(conversationId)) {\n freshConvIds.current.delete(conversationId);\n // Just-created in this session: optimistic state IS the\n // truth right now. Skip ONLY this initial load; later\n // entity_changed / refresh cycles can reconcile normally.\n return;\n }\n let cancelled = false;\n loadHistory(conversationId).then(\n (rows) => {\n if (cancelled) return;\n const cleaned = rows.map((row) => (row.streaming ? { ...row, streaming: false } : row));\n setMessages(cleaned);\n // Rehydrate any unanswered elicit forms from the most recent\n // assistant message. Previously we relied on the persisted\n // pending_response placeholder as the rehydration marker; now\n // that the placeholder is no longer written to the transcript,\n // we reconstruct the open forms by walking the actual messages.\n const open = unansweredElicitsFromHistory(cleaned);\n if (open.length > 0) {\n setTurn((t) => ({ ...t, elicits: open }));\n }\n },\n (err) => {\n if (cancelled) return;\n const status = (err as { status?: number })?.status;\n if (status === 404) {\n setConversationId(undefined);\n setMessages([]);\n }\n },\n );\n listPendingInvocations(conversationId).then(\n (rows) => {\n if (cancelled) return;\n if (rows.length === 0) return;\n setTurn((t) => ({\n ...t,\n pendingTools: rows.map((r) => ({\n id: r.id,\n name: r.tool_code,\n params: r.params ?? {},\n reason: r.reason ?? undefined,\n confidence: typeof r.confidence === \"number\" ? r.confidence : undefined,\n })),\n }));\n },\n () => {/* best-effort: a failed list keeps the user on history-only */},\n );\n return () => {\n cancelled = true;\n };\n }, [conversationId, setConversationId]);\n\n // SSE subscription is opened on hook mount, NOT on conversationId\n // change. Subscribing per-conversation creates a race: the hook\n // POSTs createConversation + agent_message in the same tick that\n // setConversationId fires; by the time the useEffect runs, the\n // server-side `turn.started` SSE frame has already been broadcast\n // and dropped on the floor (no subscriber yet). Subscribing on\n // mount means the connection is open BEFORE any send, so early\n // frames land cleanly.\n //\n // We use a ref to read the current conversationId inside the\n // long-lived stream callback (closing over the value would freeze\n // it at \"undefined\" forever).\n const activeConvIdRef = useRef<string | undefined>(conversationId);\n useEffect(() => {\n activeConvIdRef.current = conversationId;\n }, [conversationId]);\n\n useEffect(() => {\n // Subscribe to the platform-wide /api/events stream via the\n // shared agentBus instead of opening a parallel fetch.\n // Browsers throttle concurrent body-streaming fetches to the\n // same origin; the second SSE often gets starved and never\n // delivers frames. useSSE owns the single fetch and dispatches\n // agent_stream frames here via the bus.\n const handler = (e: Event) => {\n const ce = e as CustomEvent<AgentFrameDetail>;\n const { convId, kind, turnId, payload } = ce.detail;\n // Drop frames for other conversations the same user has\n // open in another tab. The stream is per-tenant on the\n // server; per-conversation filter lives here.\n if (convId !== activeConvIdRef.current) return;\n const frame: AgentStreamFrame = { kind, turnId, payload };\n // Dispatch identical to the old onFrame body below.\n onAgentFrame(convId, frame);\n };\n agentBus.addEventListener(AGENT_FRAME_EVENT, handler);\n\n const onAgentFrame = (convID: string, frame: AgentStreamFrame) => {\n switch (frame.kind) {\n case \"turn.started\":\n // Preserve pendingTools (path 1) and elicits (path 2)\n // across turn boundaries. A previous turn that emitted\n // tool.pending or elicit.pending finishes immediately\n // (engine breaks the loop on pending). The follow-up\n // turn (after the user submits one form / approves one\n // tool) MUST NOT wipe the still-open cards from the\n // earlier turn, otherwise multi-question wizards lose\n // their unanswered forms when the first answer fires\n // a new turn. The cards remove themselves on their own\n // resolution events (tool.result/failed/canceled,\n // submitElicit/cancelElicit dismissal).\n setTurn((t) => ({\n ...initialTurn,\n streaming: true,\n pendingTools: t.pendingTools,\n elicits: t.elicits,\n }));\n setMessages((m) => stopStreamingAssistantMessages(m));\n break;\n case \"assistant.message.created\": {\n const messageId = typeof frame.payload?.message_id === \"string\" ? frame.payload.message_id : \"\";\n if (!messageId) break;\n setMessages((m) =>\n upsertAssistantMessage(stopStreamingAssistantMessages(m, messageId), convID, messageId, (message) => ({\n ...message,\n streaming: true,\n })),\n );\n break;\n }\n case \"text.delta\": {\n const text = String(frame.payload?.text ?? \"\");\n const messageId = readMessageId(frame);\n if (!messageId || !text) break;\n setMessages((m) => appendDeltaToAssistantMessage(m, convID, messageId, text));\n break;\n }\n case \"elicit.pending\": {\n // The LLM asked the user a structured question. Update the\n // `elicits` queue (drives the inline form) but do NOT add a\n // synthetic tool_result chip to messages — that would be a\n // ghost \"✓ elicit.ask\" placeholder between the call and the\n // user's answer. The form rehydrates on refresh from the\n // assistant tool_use block (see initial history-load effect).\n const id = String(frame.payload?.tool_use_id ?? \"\");\n const message = String(frame.payload?.message ?? \"\");\n const schema = (frame.payload?.schema as Record<string, unknown>) ?? {};\n if (!id) break;\n setTurn((t) => ({\n ...t,\n elicits: [...t.elicits, { id, message, schema }],\n }));\n break;\n }\n case \"tool.pending\": {\n // Approval card data. invocation_id is the\n // agent_tool_invocation row id; approve/reject PATCH hits\n // this exact id. We do NOT add a synthetic tool_result chip\n // — the approval card itself renders the request, and the\n // decision chip + real result chip land after the click.\n // Rehydrates on refresh from listPendingInvocations.\n const reason = typeof frame.payload?.reason === \"string\" ? frame.payload.reason : undefined;\n const confidence = typeof frame.payload?.confidence === \"number\" ? frame.payload.confidence : undefined;\n setTurn((t) => ({\n ...t,\n pendingTools: [\n ...t.pendingTools,\n {\n id: String(frame.payload?.invocation_id ?? \"\"),\n name: String(frame.payload?.tool ?? \"\"),\n params: (frame.payload?.params as Record<string, unknown>) ?? {},\n reason,\n confidence,\n },\n ],\n }));\n break;\n }\n case \"tool.proposed\": {\n const messageId = readMessageId(frame);\n const name = typeof frame.payload?.tool === \"string\" ? frame.payload.tool : \"\";\n const params = (frame.payload?.params as Record<string, unknown>) ?? {};\n const toolUseId = typeof frame.payload?.tool_use_id === \"string\" ? frame.payload.tool_use_id : \"\";\n if (!messageId || !name) break;\n setMessages((m) => appendToolProposalToAssistantMessage(m, convID, messageId, toolUseId, name, params));\n break;\n }\n case \"tool.result\":\n case \"tool.failed\":\n case \"tool.canceled\": {\n const toolUseId = typeof frame.payload?.tool_use_id === \"string\" ? frame.payload.tool_use_id : \"\";\n const name = typeof frame.payload?.tool === \"string\" ? frame.payload.tool : \"result\";\n const output = frame.payload?.output ?? (\n typeof frame.payload?.error === \"string\" ? { error: frame.payload.error } : {}\n );\n const isError = frame.kind === \"tool.failed\" || frame.payload?.is_error === true;\n if (toolUseId) {\n const localMessageId = `local-tool-${frame.turnId || \"turn\"}-${toolUseId}`;\n setMessages((m) => appendToolResultMessage(m, convID, localMessageId, toolUseId, name, output, isError));\n }\n setTurn((t) => ({\n ...t,\n pendingTools: t.pendingTools.filter((x) => x.id !== frame.payload?.invocation_id),\n }));\n break;\n }\n case \"ui.hint\":\n setTurn((t) => ({\n ...t,\n hints: [\n ...t.hints,\n {\n kind: String(frame.payload?.kind ?? \"\"),\n target: frame.payload?.target,\n ttlMs: typeof frame.payload?.ttl_ms === \"number\" ? Number(frame.payload.ttl_ms) : undefined,\n },\n ],\n }));\n break;\n case \"turn.finished\": {\n // Preserve pendingTools across the reset. A turn that\n // proposed an approval (tool.pending fired earlier in this\n // same turn) finishes immediately because the engine\n // breaks the loop on pending_approval — wiping the array\n // here would erase the approval card before the user\n // could see it.\n setTurn((t) => {\n // Preserve both pendingTools (path 1) AND elicits\n // (path 2) — both produce a turn.finished immediately\n // after their pending event because the engine breaks\n // the loop. Wiping them here would erase the card the\n // user is supposed to act on.\n return { ...initialTurn, pendingTools: t.pendingTools, elicits: t.elicits };\n });\n const messageId = readMessageId(frame);\n if (messageId) {\n setMessages((m) => finalizeAssistantMessage(m, messageId));\n }\n break;\n }\n case \"error\":\n setMessages((m) => finalizeAllStreamingAssistantMessages(m));\n setTurn((t) => ({ ...t, streaming: false, error: String(frame.payload?.message ?? \"stream error\") }));\n break;\n }\n };\n\n // Safety net: when the platform's entity_changed wire delivers\n // an agent_message create/update for the active conversation,\n // refetch history. Catches the case where the live agent_stream\n // frames were lost (Vite dev proxy 5min idle close, brief auth\n // refresh window, etc) — the durable row IS in the DB, and\n // loadHistory will surface it.\n const refetchOnMessageChange = () => {\n const id = activeConvIdRef.current;\n if (!id) return;\n const gen = ++historyGen.current;\n loadHistory(id).then(\n (rows) => {\n // Only swap if (a) the conv hasn't changed under us AND\n // (b) we're the latest in-flight load. A user-message\n // entity_changed and the follow-up assistant-message\n // entity_changed both trigger refetches; without the gen\n // gate, an older fetch resolving last would clobber the\n // newer rows and the answer would visibly disappear.\n if (id !== activeConvIdRef.current) return;\n if (gen !== historyGen.current) return;\n setMessages((cur) => mergeHistoryRows(rows, cur));\n },\n () => {/* ignore — periodic refetch, errors not fatal */},\n );\n };\n agentBus.addEventListener(AGENT_MESSAGE_CHANGED_EVENT, refetchOnMessageChange);\n\n return () => {\n agentBus.removeEventListener(AGENT_FRAME_EVENT, handler);\n agentBus.removeEventListener(AGENT_MESSAGE_CHANGED_EVENT, refetchOnMessageChange);\n };\n // Empty deps: the listener is bound once for the hook's\n // lifetime. Per-conversation filtering happens via\n // activeConvIdRef inside the handler.\n }, []);\n\n const send = useCallback(\n async (text: string, context?: Record<string, unknown>) => {\n // First send: create the conversation row explicitly. The\n // server-side handler used to do this as a side effect of\n // $on_created on agent_message, which forced conversation_id\n // to be nullable + ran a sync handler that blocked the POST\n // for ~5s on the LLM. The new design has the frontend own\n // conversation creation, so the schema can NOT NULL the FK\n // and the message POST is a clean async-only flow.\n let convId = conversationId;\n if (!convId) {\n const conv = await createConversation({\n context: context,\n });\n convId = conv.id;\n // Mark the conv id as fresh BEFORE setting state so the\n // loadHistory effect, which runs synchronously in response\n // to the conversationId change, sees it and skips.\n freshConvIds.current.add(convId);\n // Sync the ref immediately so the SSE frame filter accepts\n // frames for this conversation before React re-renders. The\n // fake LLM (and fast real responses) can publish tool.pending\n // or text.delta before the useEffect fires — without this the\n // frame is dropped and the approval card never appears.\n activeConvIdRef.current = convId;\n setConversationId(convId);\n }\n const nextSeq = messages.length + 1;\n // Stale-conversation recovery: tenant-scoped storage prevents the\n // common cross-tenant case, but a stored id can still go stale\n // within a tenant (admin-deleted conversation, partial restore,\n // brief mount→loadHistory→send race). On FK_VIOLATION for the\n // conversation FK, drop the stale id, mint a fresh conversation,\n // and retry once. One round-trip cost vs. surfacing the user's\n // first message as a hard failure.\n let created: AgentMessage;\n try {\n created = await sendMessage(text, convId, nextSeq);\n } catch (err) {\n if (!isStaleConversationFKError(err)) throw err;\n const conv = await createConversation({\n context: context,\n });\n convId = conv.id;\n freshConvIds.current.add(convId);\n activeConvIdRef.current = convId;\n setConversationId(convId);\n // Recompute seq against the (now empty) new conversation so\n // the trigger's \"auto-allocate\" path stays consistent for any\n // caller that DOES pass a non-zero seq in the future.\n created = await sendMessage(text, convId, 1);\n }\n // Optimistic append: the bubble must render instantly. The\n // POST itself returns in ~50ms now (async LLM step), so the\n // perceived latency = network round-trip, like any normal\n // save. SSE turn.started arrives shortly after as the job\n // queue picks up agents.run_message_turn.\n //\n // Dedupe by id: the AGENT_MESSAGE_CHANGED_EVENT for this same\n // user row can land in the same tick as the POST response and\n // trigger refetchOnMessageChange, which would already have\n // appended the row. Without dedupe the user bubble renders\n // twice. Match by id so the swap is idempotent.\n setMessages((m) => {\n if (m.some((x) => x.id === created.id)) return m;\n return [...m, { ...created, content: [{ type: \"text\", text }] as AgentContentBlock[] }];\n });\n },\n [conversationId, messages.length, setConversationId],\n );\n\n // Optimistically drop the approval card from local state on\n // approve/reject. The PATCH triggers on_tool_invocation_status_change\n // server-side which runs the action and writes a tool_result\n // agent_message; the next $on_created fires a follow-up turn.\n // If the PATCH itself fails, the card stays gone but the LLM\n // never resumes; surface that case via the next conv.turn.error.\n // Re-adding the card on error is also possible — left as a\n // follow-up since the failure path is rare in practice.\n const approve = useCallback(async (invocationId: string, edits?: Record<string, unknown>) => {\n setTurn((t) => ({ ...t, pendingTools: t.pendingTools.filter((x) => x.id !== invocationId) }));\n await approveInvocation(invocationId, edits);\n }, []);\n\n const reject = useCallback(async (invocationId: string, note?: string) => {\n setTurn((t) => ({ ...t, pendingTools: t.pendingTools.filter((x) => x.id !== invocationId) }));\n await rejectInvocation(invocationId, note);\n }, []);\n\n // submitElicit posts the user's form answers as an elicit_response\n // agent_message paired with the original elicit.ask tool_use id.\n // The post triggers $on_created → run_message_turn, the LLM gets\n // the answers via history projection, continues. Optimistically\n // dismisses the form AND inserts a local elicit_response message\n // so the user sees their submitted answer chip immediately, even\n // when the LLM is still waiting for sibling forms (multi-elicit\n // turn). The next history reload reconciles by id.\n const submitElicit = useCallback(async (toolUseId: string, data: Record<string, unknown>) => {\n if (!conversationId) return;\n setTurn((t) => ({ ...t, elicits: t.elicits.filter((e) => e.id !== toolUseId) }));\n const nextSeq = messages.length + 1;\n const created = await sendElicitResponse(conversationId, nextSeq, toolUseId, data);\n setMessages((m) => {\n if (m.some((x) => x.id === created.id)) return m;\n return [...m, {\n ...created,\n content: [{ type: \"elicit_response\", id: toolUseId, data }] as AgentContentBlock[],\n }];\n });\n }, [conversationId, messages.length]);\n\n // cancelElicit sends a sentinel response so the LLM unblocks and\n // can acknowledge the cancel. Without this, the form sits in the\n // UI forever and the LLM is silent. Sentinel shape is documented\n // in docs/plans/2026-04-25-clara-elicitation.md.\n const cancelElicit = useCallback(async (toolUseId: string) => {\n if (!conversationId) return;\n setTurn((t) => ({ ...t, elicits: t.elicits.filter((e) => e.id !== toolUseId) }));\n const nextSeq = messages.length + 1;\n await sendElicitResponse(conversationId, nextSeq, toolUseId, { __canceled: true });\n }, [conversationId, messages.length]);\n\n const cancel = useCallback(async () => {\n if (conversationId) await cancelConversation(conversationId);\n }, [conversationId]);\n\n const refreshConversations = useCallback(async () => {\n setConversations(await listConversations());\n }, []);\n\n const startNew = useCallback(() => {\n setConversationId(undefined);\n setMessages([]);\n setTurn(initialTurn);\n }, [setConversationId]);\n\n // retry idempotently re-fires the LLM turn against the existing\n // last user message. The platform's `agents.rerun_message_turn`\n // action finds the row and runs run_message_turn directly — no\n // new agent_message inserted, no duplicate user bubble in the\n // chat, no malformed history (two consecutive role=user rows\n // with nothing between them is invalid LLM input). Audit visibility\n // for \"two attempts\" lives on the agent_tool_invocation rows\n // generated within each turn — those ARE distinct, the user\n // message is not.\n const retry = useCallback(async () => {\n if (!conversationId) return;\n setTurn((t) => ({ ...t, error: undefined }));\n await rerunMessageTurn(conversationId);\n }, [conversationId]);\n\n return {\n conversationId,\n messages,\n turn,\n conversations,\n send,\n approve,\n reject,\n submitElicit,\n cancelElicit,\n cancel,\n retry,\n refreshConversations,\n startNew,\n setConversationId,\n };\n}\n\ninterface AgentStreamFrame {\n kind: string;\n turnId?: string;\n payload?: Record<string, unknown>;\n}\n","// AgentElicitForm renders an inline JSON-Schema → form widget for\n// the agents elicit channel (§7). Handles the subset of JSON\n// Schema the LLM actually produces: object with primitive\n// properties (string + enum, integer/number + min/max, boolean,\n// array of enum), required[], format hints (multiline, email,\n// url, date-time), title + description per property.\n//\n// Submission posts an elicit_response message back to the\n// conversation, which resumes the LLM turn server-side.\nimport { useState, type ChangeEvent } from \"react\";\nimport { Btn } from \"@/components/primitives\";\n\ntype JSONSchemaProp = {\n type?: string | string[];\n enum?: unknown[];\n format?: string;\n minimum?: number;\n maximum?: number;\n title?: string;\n description?: string;\n default?: unknown;\n items?: { enum?: unknown[]; type?: string };\n};\n\ninterface JSONSchema {\n type?: string;\n properties?: Record<string, JSONSchemaProp>;\n required?: string[];\n}\n\nexport interface AgentElicitFormProps {\n message: string;\n schema: JSONSchema;\n onSubmit: (data: Record<string, unknown>) => void;\n onCancel?: () => void;\n}\n\nexport function AgentElicitForm({ message, schema, onSubmit, onCancel }: AgentElicitFormProps) {\n const [data, setData] = useState<Record<string, unknown>>(() => initialFromSchema(schema));\n const [error, setError] = useState<string | null>(null);\n\n const submit = () => {\n const missing = (schema.required ?? []).filter((k) => isEmpty(data[k]));\n if (missing.length > 0) {\n setError(`Required: ${missing.join(\", \")}`);\n return;\n }\n onSubmit(data);\n };\n\n const props = Object.entries(schema.properties ?? {});\n\n return (\n <div className=\"rounded-md border border-divider bg-surface p-3 text-sm\">\n <div className=\"mb-3 font-medium\">{message}</div>\n <div className=\"space-y-2\">\n {props.map(([name, prop]) => (\n <FieldRenderer\n key={name}\n name={name}\n prop={prop}\n required={(schema.required ?? []).includes(name)}\n value={data[name]}\n onChange={(v) => setData((d) => ({ ...d, [name]: v }))}\n />\n ))}\n </div>\n {error && <div className=\"mt-2 text-xs text-destructive\">{error}</div>}\n <div className=\"mt-3 flex justify-end gap-2\">\n {onCancel && (\n <Btn kind=\"ghost\" onClick={onCancel}>\n Cancel\n </Btn>\n )}\n <Btn onClick={submit}>Submit</Btn>\n </div>\n </div>\n );\n}\n\ninterface FieldProps {\n name: string;\n prop: JSONSchemaProp;\n required: boolean;\n value: unknown;\n onChange: (v: unknown) => void;\n}\n\nfunction FieldRenderer({ name, prop, required, value, onChange }: FieldProps) {\n const label = (prop.title ?? humanize(name)) + (required ? \" *\" : \"\");\n const help = prop.description;\n\n // string with enum -> select\n if (prop.enum) {\n return (\n <Field label={label} help={help}>\n <select\n value={String(value ?? \"\")}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border border-border bg-card p-2 text-sm\"\n >\n <option value=\"\">—</option>\n {prop.enum.map((opt) => (\n <option key={String(opt)} value={String(opt)}>\n {String(opt)}\n </option>\n ))}\n </select>\n </Field>\n );\n }\n\n // boolean -> checkbox\n if (prop.type === \"boolean\") {\n return (\n <Field label={label} help={help}>\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => onChange(e.target.checked)}\n />\n </Field>\n );\n }\n\n // array with items.enum -> multi-select tags\n if (prop.type === \"array\" && prop.items?.enum) {\n return (\n <Field label={label} help={help}>\n <select\n multiple\n value={Array.isArray(value) ? value.map(String) : []}\n onChange={(e) => {\n const arr = Array.from(e.target.selectedOptions).map((o) => o.value);\n onChange(arr);\n }}\n className=\"w-full rounded-md border border-border bg-card p-2 text-sm\"\n >\n {prop.items.enum.map((opt) => (\n <option key={String(opt)} value={String(opt)}>\n {String(opt)}\n </option>\n ))}\n </select>\n </Field>\n );\n }\n\n // integer / number\n if (prop.type === \"integer\" || prop.type === \"number\") {\n const handle = (e: ChangeEvent<HTMLInputElement>) => {\n const v = e.target.value === \"\" ? undefined : Number(e.target.value);\n onChange(v);\n };\n return (\n <Field label={label} help={help}>\n <input\n type=\"number\"\n step={prop.type === \"integer\" ? 1 : \"any\"}\n min={prop.minimum}\n max={prop.maximum}\n value={value === undefined || value === null ? \"\" : String(value)}\n onChange={handle}\n className=\"w-full rounded-md border border-border bg-card p-2 text-sm\"\n />\n </Field>\n );\n }\n\n // string with multiline format -> textarea\n if (prop.format === \"multiline\") {\n return (\n <Field label={label} help={help}>\n <textarea\n rows={3}\n value={String(value ?? \"\")}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border border-border bg-card p-2 text-sm\"\n />\n </Field>\n );\n }\n\n // string default\n return (\n <Field label={label} help={help}>\n <input\n type={prop.format === \"email\" ? \"email\" : prop.format === \"url\" ? \"url\" : prop.format === \"date-time\" ? \"datetime-local\" : \"text\"}\n value={String(value ?? \"\")}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border border-border bg-card p-2 text-sm\"\n />\n </Field>\n );\n}\n\nfunction Field({ label, help, children }: { label: string; help?: string; children: React.ReactNode }) {\n return (\n <label className=\"block\">\n <span className=\"block text-xs font-medium text-muted-foreground\">{label}</span>\n {children}\n {help && <span className=\"mt-0.5 block text-xs text-muted-foreground\">{help}</span>}\n </label>\n );\n}\n\nfunction humanize(s: string): string {\n return s.replace(/[_-]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction isEmpty(v: unknown): boolean {\n if (v === undefined || v === null || v === \"\") return true;\n if (Array.isArray(v) && v.length === 0) return true;\n return false;\n}\n\nfunction initialFromSchema(schema: JSONSchema): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, p] of Object.entries(schema.properties ?? {})) {\n if (p.default !== undefined) out[k] = p.default;\n }\n return out;\n}\n","// AgentChatPanel: real-backend port of the canonical design at\n// docs/plans/assets/2026-04-19-shell-design/project/decl/ai.jsx.\n//\n// UX patterns ported from `assistant-ui` (3rd-party reference):\n// - assistant rows keep one stable React identity from first delta\n// through persistence; the same MessageRow instance gains/loses\n// its streaming cursor instead of swapping render paths.\n// - auto-scroll to bottom only when user is already at bottom; if\n// user scrolled up to read history, the scroll is paused. New\n// content while paused triggers a \"Jump to latest\" pill.\n// - send button transitions to stop button while streaming, so the\n// same affordance is available without a layout shift.\n// - retry button on turn.error re-sends the last user message.\n// - textarea auto-grows up to a cap via `react-textarea-autosize`.\n// - markdown renderer uses react-markdown + remark-gfm for tables,\n// task lists, etc. Code blocks render as <pre><code> for now -\n// syntax highlighting is a future enhancement, not a blocker.\nimport { useEffect, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport TextareaAutosize from \"react-textarea-autosize\";\nimport { Icon, IconBtn, Btn, Spinner } from \"@/components/primitives\";\nimport { Markdown } from \"@/components/shared/Markdown\";\nimport { useAgentConversation } from \"@/hooks/useAgentConversation\";\nimport { AgentElicitForm } from \"@/components/agents/AgentElicitForm\";\nimport type { AgentContentBlock, AgentMessage } from \"@/api/agents\";\n\nexport interface AgentChatPanelProps {\n open: boolean;\n onClose: () => void;\n context?: string;\n title?: string;\n}\n\n// AT_BOTTOM_THRESHOLD_PX: distance from the scroll bottom that still\n// counts as \"user is at the bottom\" (auto-scroll engages). Matches\n// the assistant-ui heuristic - tight enough that any meaningful\n// scroll-up disengages auto-scroll, loose enough that natural\n// scroll dynamics don't constantly toggle the flag.\nconst AT_BOTTOM_THRESHOLD_PX = 80;\n\nexport function AgentChatPanel({ open, onClose, context, title = \"\" }: AgentChatPanelProps) {\n const conv = useAgentConversation();\n const [input, setInput] = useState(\"\");\n const [sendError, setSendError] = useState<string | null>(null);\n\n // Viewport ref + isAtBottom flag drive the auto-scroll behaviour.\n // We track isAtBottom on every scroll event; message changes call\n // scrollToBottom() ONLY when the flag is true. When false (user\n // scrolled up), we surface a \"Jump to latest\" pill so they can\n // resume.\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const isAtBottomRef = useRef(true);\n const [showJumpToLatest, setShowJumpToLatest] = useState(false);\n\n const handleScroll = () => {\n const el = scrollRef.current;\n if (!el) return;\n const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = distanceFromBottom <= AT_BOTTOM_THRESHOLD_PX;\n isAtBottomRef.current = atBottom;\n if (atBottom && showJumpToLatest) setShowJumpToLatest(false);\n };\n\n const scrollToBottom = (behavior: ScrollBehavior = \"smooth\") => {\n const el = scrollRef.current;\n if (!el) return;\n el.scrollTo({ top: el.scrollHeight, behavior });\n isAtBottomRef.current = true;\n setShowJumpToLatest(false);\n };\n\n // Auto-scroll trigger: any time messages change, scroll if the\n // user was already at bottom. If they weren't, surface the pill\n // so they know new content arrived.\n useEffect(() => {\n if (isAtBottomRef.current) {\n scrollToBottom(\"smooth\");\n } else {\n setShowJumpToLatest(true);\n }\n }, [conv.messages]);\n\n // On panel open, jump to bottom (instant) so the user sees the\n // most recent state. Smooth would scroll past content as it\n // hydrates from loadHistory.\n useEffect(() => {\n if (open) scrollToBottom(\"instant\");\n }, [open]);\n\n if (!open) return null;\n\n const send = async (text: string) => {\n if (!text.trim()) return;\n setSendError(null);\n setInput(\"\");\n try {\n await conv.send(text);\n } catch (err) {\n console.error(\"agent send failed\", err);\n setSendError(err instanceof Error ? err.message : String(err));\n setInput(text);\n }\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n void send(input);\n }\n };\n\n const introVisible = conv.messages.length === 0 && !conv.turn.streaming;\n const isStreaming = conv.turn.streaming;\n // An assistant placeholder with empty content is not yet visually\n // contributing anything (it just got created via\n // assistant.message.created and the first delta / tool_use hasn't\n // arrived). We HIDE that empty row in MessageRow and show the global\n // \"Thinking…\" line at the bottom of the chat instead, so the\n // indicator always lands under the last visible item — not as an\n // orphaned sparkle on the opposite side from the latest content.\n const hasContentfulStreamingAssistant = conv.messages.some(\n (message) => message.role === \"assistant\" && message.streaming && message.content.length > 0,\n );\n const canSend = input.trim().length > 0 && !isStreaming;\n\n return (\n <aside style={{\n position: \"fixed\", top: \"var(--topbar-h)\", right: 0, bottom: \"var(--tabbar-h, 0px)\", width: 400, zIndex: 30,\n background: \"var(--surface)\", borderLeft: \"1px solid var(--border)\",\n display: \"flex\", flexDirection: \"column\", boxShadow: \"var(--shadow-md)\",\n }}>\n <div style={{ display: \"flex\", alignItems: \"center\", padding: \"10px 14px\", gap: 8, borderBottom: \"1px solid var(--divider)\" }}>\n <span style={{\n width: 22, height: 22, borderRadius: 6,\n background: \"linear-gradient(135deg, var(--accent), var(--accent-2))\",\n display: \"flex\", alignItems: \"center\", justifyContent: \"center\",\n }}><Icon.sparkle size={13} style={{ color: \"var(--accent-foreground)\" }} /></span>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: 12.5, fontWeight: 600, color: \"var(--text)\" }}>{title}</div>\n <div style={{ fontSize: 10.5, color: \"var(--text-3)\", display: \"flex\", alignItems: \"center\", gap: 4 }}>\n <span style={{ width: 5, height: 5, background: \"var(--success)\", borderRadius: 999 }} />\n context: <span className=\"mono\">{context || \"Platform\"}</span>\n </div>\n </div>\n {conv.messages.length > 0 && (\n <IconBtn\n size={24}\n icon={<Icon.copy size={12} />}\n onClick={() => void copyTranscript(conv.messages)}\n title=\"Copy entire conversation\"\n />\n )}\n {conv.conversationId && (\n <IconBtn size={24} icon={<Icon.plus size={12} />} onClick={conv.startNew} title=\"New conversation\" />\n )}\n <IconBtn size={24} icon={<Icon.settings size={12} />} title=\"Preferences\" />\n <IconBtn size={24} icon={<Icon.x size={13} />} onClick={onClose} title=\"Close\" />\n </div>\n\n <div\n ref={scrollRef}\n onScroll={handleScroll}\n style={{ flex: 1, overflowY: \"auto\", padding: \"12px 14px\", display: \"flex\", flexDirection: \"column\", gap: 12, position: \"relative\" }}\n >\n {introVisible && (\n <AIRow>\n Hi — I can answer questions about your data, suggest actions, draft YAML definitions, and run allowed tools within your permissions. What would you like to do?\n </AIRow>\n )}\n {conv.messages.map((m) => <MessageRow key={m.id} message={m} />)}\n {conv.turn.pendingTools.map((t) => (\n <AgentApprovalCard key={t.id} tool={t}\n onApprove={(edits) => void conv.approve(t.id, edits)}\n onReject={(note) => void conv.reject(t.id, note)} />\n ))}\n {/* Path 2: LLM-asked structured questions (elicit.ask).\n Distinct primitive from approval cards above — these are\n the model asking the user for missing intent, not the\n platform asking for authorization. */}\n {conv.turn.elicits.map((e) => (\n <AgentElicitForm key={e.id} message={e.message} schema={e.schema}\n onSubmit={(data) => void conv.submitElicit(e.id, data)}\n onCancel={() => void conv.cancelElicit(e.id)} />\n ))}\n {/* \"Thinking…\" placeholder before the stream has yielded an\n assistant message row at all. Once the row exists, the\n row itself owns its spinner/cursor. */}\n {isStreaming && !hasContentfulStreamingAssistant && (\n <div style={{ display: \"flex\", gap: 6, alignItems: \"center\", color: \"var(--text-3)\", fontSize: 11.5 }}>\n <Spinner size={12} />Thinking…\n </div>\n )}\n {/* Turn-level error (LLM failure, network, etc.) with Retry. */}\n {conv.turn.error && (\n <div style={{\n padding: \"10px 12px\", background: \"var(--surface-2)\",\n border: \"1px solid var(--border-strong, var(--border))\", borderRadius: 8,\n fontSize: 11.5, color: \"var(--text-2)\",\n display: \"flex\", flexDirection: \"column\", gap: 8,\n }}>\n <div><strong>The AI couldn't finish:</strong> {conv.turn.error}</div>\n <div style={{ display: \"flex\", gap: 6 }}>\n <Btn size=\"xs\" kind=\"primary\" icon={<Icon.refresh size={11} />} onClick={() => void conv.retry()}>\n Retry\n </Btn>\n </div>\n </div>\n )}\n {/* Send-error (POST failed). Distinct from turn.error: the\n request never made it to the LLM. */}\n {sendError && (\n <div style={{\n padding: \"8px 10px\", background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\", borderRadius: 6,\n fontSize: 11.5, color: \"var(--text-2)\",\n }}>\n <strong>Send failed:</strong> {sendError}\n </div>\n )}\n </div>\n\n {/* \"Jump to latest\" pill - shown when user scrolled up and new\n content arrived. Click resumes auto-scroll. */}\n {showJumpToLatest && (\n <button\n onClick={() => scrollToBottom(\"smooth\")}\n style={{\n position: \"absolute\", right: 16, bottom: 110,\n padding: \"6px 12px\", borderRadius: 999,\n background: \"var(--accent)\", color: \"var(--accent-foreground)\", border: 0,\n fontSize: 11, fontWeight: 600, cursor: \"pointer\",\n boxShadow: \"var(--shadow-md)\",\n display: \"inline-flex\", alignItems: \"center\", gap: 4, zIndex: 1,\n }}\n >\n <Icon.arrowDown size={11} />Jump to latest\n </button>\n )}\n\n <div style={{ padding: 10, borderTop: \"1px solid var(--divider)\", background: \"var(--surface-2)\" }}>\n <div style={{\n display: \"flex\", alignItems: \"flex-end\", gap: 6, padding: 6, background: \"var(--surface)\",\n border: \"1px solid var(--border)\", borderRadius: 8,\n }}>\n <TextareaAutosize\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Ask or describe what you want…\"\n minRows={1}\n maxRows={8}\n style={{\n flex: 1, border: 0, outline: 0, background: \"transparent\", resize: \"none\",\n fontSize: 12.5, lineHeight: 1.5, color: \"var(--text)\", padding: \"4px 6px\",\n fontFamily: \"inherit\",\n }}\n />\n <IconBtn size={26} icon={<Icon.paperclip size={12} />} title=\"Attach context\" />\n {/* Send-or-stop button: while streaming, the same slot\n renders a stop affordance so the user can cancel\n without hunting for a separate button. Cancels the\n conversation (server-side: status → canceling →\n registered turn aborts in-flight). */}\n {isStreaming ? (\n <button\n onClick={() => void conv.cancel()}\n aria-label=\"Stop generating\"\n title=\"Stop generating\"\n style={{\n width: 26, height: 26, borderRadius: 6, border: 0, cursor: \"pointer\",\n background: \"var(--destructive)\", color: \"var(--destructive-foreground)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n }}\n ><Icon.x size={13} /></button>\n ) : (\n <button\n onClick={() => void send(input)}\n disabled={!canSend}\n aria-label=\"Send\"\n style={{\n width: 26, height: 26, borderRadius: 6, border: 0,\n cursor: canSend ? \"pointer\" : \"default\",\n background: canSend ? \"var(--accent)\" : \"var(--surface-3)\",\n color: canSend ? \"var(--accent-foreground)\" : \"var(--text-4)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n }}\n ><Icon.arrowUp size={13} /></button>\n )}\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, marginTop: 6, fontSize: 10.5, color: \"var(--text-4)\" }}>\n <Icon.shield size={10} />\n <span>Runs within your permissions. All actions audit-logged.</span>\n </div>\n </div>\n </aside>\n );\n}\n\n// BlinkingCursor: tiny animated caret used at the tail of streaming\n// text. CSS-animated via the existing `agent-blink` keyframes (added\n// to globals.css). Pure presentation; no state.\nfunction BlinkingCursor() {\n return (\n <span\n aria-hidden\n style={{\n display: \"inline-block\",\n width: 2, height: 13, marginLeft: 2,\n verticalAlign: \"text-bottom\",\n background: \"var(--text-3)\",\n animation: \"agent-blink 1s steps(2, end) infinite\",\n }}\n />\n );\n}\n\n// AIRow renders a sparkle-avatar prefix + content.\nfunction AIRow({\n children,\n dataMessageId,\n dataStreaming,\n}: {\n children: ReactNode;\n dataMessageId?: string;\n dataStreaming?: boolean;\n}) {\n return (\n <div\n data-message-id={dataMessageId}\n data-streaming={dataStreaming ? \"true\" : undefined}\n style={{ display: \"flex\", gap: 8, alignItems: \"flex-start\" }}\n >\n <span style={{\n width: 20, height: 20, borderRadius: 5, background: \"var(--accent-soft)\", color: \"var(--accent)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\", flexShrink: 0, marginTop: 2,\n }}>\n <Icon.sparkle size={11} />\n </span>\n <div style={{ flex: 1, minWidth: 0, fontSize: 12.5, lineHeight: 1.55, color: \"var(--text)\" }}>\n {children}\n </div>\n </div>\n );\n}\n\nfunction MessageRow({ message }: { message: AgentMessage }) {\n // Side allocation:\n // left = AI (sparkle row): assistant text + tool_use chips\n // right = user typed bubble, user form answers (elicit_response),\n // tool results coming back from the platform, and the\n // user's approve/reject decision chip\n // The chat reads as a dialogue: AI on the left does a thing, the\n // right side answers (you or the platform). Pairing happens\n // chronologically — a tool_use on the left is followed by the\n // matching tool_result on the right in the next row.\n if (message.role === \"user\") {\n const text = flattenText(message.content);\n const hasNonText = message.content.some((b) => b.type !== \"text\");\n if (text && !hasNonText) {\n return (\n <div style={{\n alignSelf: \"flex-end\", maxWidth: \"80%\", padding: \"8px 12px\",\n background: \"var(--primary)\", color: \"var(--primary-fg)\", borderRadius: 10,\n fontSize: 12.5, lineHeight: 1.5,\n }} data-message-id={message.id} data-streaming={message.streaming ? \"true\" : undefined}>\n {text}\n </div>\n );\n }\n return <RightColumn message={message} />;\n }\n if (message.role === \"tool\") {\n // tool_result blocks (the platform answering an AI tool call) and\n // any synthetic user-side blocks the engine writes for HITL pending\n // / elicit pending land here. All right-aligned.\n return <RightColumn message={message} />;\n }\n if (message.role === \"system\") {\n // Decision blocks (Approved / Rejected) get a prominent right-side\n // chip. Other system blocks (legacy interrupt notices, etc.) keep\n // the compact dim italic line under the AI row.\n if (message.content.some((b) => b.type === \"decision\")) {\n return <RightColumn message={message} />;\n }\n return (\n <AIRow dataMessageId={message.id}>\n {message.content.map((b, i) => <BlockView key={i} block={b} />)}\n </AIRow>\n );\n }\n // Empty streaming assistant placeholder: skip rendering. The global\n // \"Thinking…\" line at the bottom of the chat carries the indicator\n // so it always appears under the last visible item rather than as\n // an orphaned sparkle on the opposite side from the latest content.\n if (message.content.length === 0) return null;\n return (\n <AIRow dataMessageId={message.id} dataStreaming={message.streaming}>\n {message.content.map((b, i) => <BlockView key={i} block={b} />)}\n {message.streaming && <BlinkingCursor />}\n </AIRow>\n );\n}\n\nfunction RightColumn({ message }: { message: AgentMessage }) {\n return (\n <div\n data-message-id={message.id}\n data-streaming={message.streaming ? \"true\" : undefined}\n style={{ alignSelf: \"flex-end\", maxWidth: \"80%\", display: \"flex\", flexDirection: \"column\", alignItems: \"flex-end\" }}\n >\n {message.content.map((b, i) => <BlockView key={i} block={b} />)}\n </div>\n );\n}\n\n// markdownComponents lets us style inline / block code consistently\n// with the rest of the chat without pulling a heavy syntax-highlight\n// library. Per-language highlighting can land later as a swap-in.\n//\n// react-markdown invokes these overrides with loosely-typed props (the AST\n// `node` plus the intrinsic element props); typing each precisely buys nothing\n// here, so this one adapter boundary opts out of no-explicit-any as a reviewed\n// block. `node` is destructured out so it is never spread onto the DOM element.\n/* eslint-disable @typescript-eslint/no-explicit-any -- react-markdown override props are an untyped adapter boundary */\nconst markdownComponents = {\n code({ node: _node, inline, className, children, ...props }: any) {\n if (inline) {\n return (\n <code\n {...props}\n style={{\n background: \"var(--surface-2)\",\n padding: \"1px 4px\",\n borderRadius: 4,\n fontFamily: \"var(--font-mono, ui-monospace)\",\n fontSize: \"0.92em\",\n }}\n >\n {children}\n </code>\n );\n }\n return (\n <pre\n style={{\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n padding: \"8px 10px\",\n margin: \"6px 0\",\n overflowX: \"auto\",\n fontSize: 11.5,\n lineHeight: 1.5,\n }}\n >\n <code className={className} {...props}>{children}</code>\n </pre>\n );\n },\n a({ children, ...props }: any) {\n return (\n <a\n {...props}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: \"var(--accent)\", textDecoration: \"underline\" }}\n >\n {children}\n </a>\n );\n },\n ul({ children, ...props }: any) {\n return <ul {...props} style={{ paddingLeft: 18, margin: \"4px 0\" }}>{children}</ul>;\n },\n ol({ children, ...props }: any) {\n return <ol {...props} style={{ paddingLeft: 20, margin: \"4px 0\" }}>{children}</ol>;\n },\n p({ children, ...props }: any) {\n return <p {...props} style={{ margin: \"4px 0\" }}>{children}</p>;\n },\n};\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nfunction BlockView({ block }: { block: AgentContentBlock }) {\n switch (block.type) {\n case \"text\":\n return (\n <div className=\"agent-md\">\n <Markdown\n content={block.text}\n plugins=\"gfm\"\n prose={false}\n skipHtml={false}\n components={markdownComponents}\n />\n </div>\n );\n case \"tool_use\": {\n // Chip header reads the user-meaningful name. The action.invoke\n // wrapper is implementation noise: when an LLM calls\n // action.invoke({code: \"X\", params: {...}}) the user cares\n // about X, not the wrapper. We surface X as the label and keep\n // a small dim \"via action.invoke\" tag for transparency.\n const display = deriveToolDisplay(block.name, block.input as Record<string, unknown> | undefined);\n return <ToolChip kind=\"call\" label={display.label} sub={display.sub} body={block.input} />;\n }\n case \"tool_result\": {\n // tool_result.name is rewritten on the engine side to the inner\n // action code for action.invoke calls (engine.go:333), so the\n // label here is already user-meaningful in the common case.\n const label = block.name || (block.is_error ? \"error\" : \"result\");\n return <ToolChip kind=\"result\" label={label} body={block.output} isError={!!block.is_error} />;\n }\n case \"elicit_response\":\n return <ToolChip kind=\"result\" label=\"answer\" body={block.data} />;\n case \"decision\":\n return <DecisionChip block={block} />;\n case \"ui_hint\":\n return <div style={{ fontSize: 10.5, color: \"var(--text-3)\" }}>→ {block.kind}</div>;\n case \"system\":\n return <div style={{ fontSize: 10.5, fontStyle: \"italic\", color: \"var(--text-3)\" }}>— {block.reason}</div>;\n case \"summary\":\n return <div style={{ fontSize: 10.5, fontStyle: \"italic\", color: \"var(--text-3)\" }}>{block.text}</div>;\n case \"attachment\":\n return (\n <pre className=\"mono\" style={{\n marginTop: 8, padding: \"8px 10px\", background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\", borderRadius: 6, fontSize: 10.5, overflowX: \"auto\",\n }}>{block.content}</pre>\n );\n default:\n return null;\n }\n}\n\n// ToolChip renders a tool_use or tool_result as a tiny one-line\n// chip (collapsed by default) that the user can expand to see the\n// full params/output JSON. Mirrors how ChatGPT / Claude / Cursor\n// present tool calls: don't drown the conversation in JSON, but\n// keep it one click away.\n//\n// Implementation note: native <details>/<summary> instead of React\n// state. The browser tracks open/closed per element, no re-renders,\n// no state plumbing. Custom marker hides the default ▶/▼ disclosure\n// triangle; we draw our own arrow that rotates when [open].\nfunction ToolChip({\n kind,\n label,\n sub,\n body,\n isError,\n}: {\n kind: \"call\" | \"result\";\n label: string;\n // sub: dim transparency tag rendered next to the label, e.g.\n // \"via action.invoke\" on tool_use chips that were dispatched\n // through the action.invoke wrapper. Optional.\n sub?: string;\n body: unknown;\n isError?: boolean;\n}) {\n const arrow = kind === \"call\" ? \"→\" : (isError ? \"✗\" : \"✓\");\n const accentColor = isError ? \"var(--destructive)\" : \"var(--text-3)\";\n return (\n <details\n style={{\n marginTop: 6,\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n fontSize: 11,\n // width:100% + minWidth:0 + boxSizing prevent the expanded\n // <pre> body from blowing out the parent flex column when a\n // long JSON line lands inside the chip. Without these, the\n // chip sizes to its widest descendant and pushes the entire\n // chat panel to overflow horizontally.\n width: \"100%\",\n minWidth: 0,\n maxWidth: \"100%\",\n boxSizing: \"border-box\",\n overflow: \"hidden\",\n }}\n >\n <summary\n style={{\n padding: \"5px 8px\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n listStyle: \"none\",\n userSelect: \"none\",\n color: \"var(--text-2)\",\n }}\n >\n <span aria-hidden style={{ color: accentColor, fontFamily: \"var(--font-mono, ui-monospace)\", width: 10 }}>{arrow}</span>\n <span className=\"mono\" style={{ color: \"var(--text-2)\", flex: \"0 1 auto\", minWidth: 0, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{label}</span>\n {sub && (\n <span style={{ fontSize: 10, color: \"var(--text-4)\", fontStyle: \"italic\", flex: \"0 1 auto\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{sub}</span>\n )}\n <span style={{ flex: 1 }} />\n <span aria-hidden style={{ fontSize: 9, color: \"var(--text-4)\" }}>▾</span>\n </summary>\n <pre\n className=\"mono\"\n style={{\n margin: 0,\n padding: \"4px 8px 6px\",\n fontSize: 10.5,\n lineHeight: 1.45,\n color: \"var(--text-2)\",\n overflowX: \"auto\",\n background: \"var(--surface)\",\n borderTop: \"1px solid var(--border)\",\n }}\n >\n {JSON.stringify(body, null, 2)}\n </pre>\n </details>\n );\n}\n\nfunction flattenText(blocks: AgentContentBlock[]): string {\n return blocks.map((b) => (b.type === \"text\" ? b.text : \"\")).join(\"\");\n}\n\n// deriveToolDisplay returns the user-facing chip header for a tool\n// call. action.invoke is a generic wrapper over registered actions —\n// the meaningful name lives in input.code, not in the tool name. We\n// surface input.code as the primary label and keep \"via action.invoke\"\n// as a small dim tag so the wrapper is still discoverable for power\n// users without crowding the label on every chip. For other tools the\n// chip label is the tool name itself; no tag.\nfunction deriveToolDisplay(name: string, input: Record<string, unknown> | undefined): { label: string; sub?: string } {\n if (name === \"action.invoke\") {\n const code = typeof input?.code === \"string\" ? (input.code as string) : \"\";\n if (code) return { label: code, sub: \"via action.invoke\" };\n }\n return { label: name };\n}\n\n// DecisionChip renders the user's approve/reject decision as a\n// prominent right-side badge. The structured `decision` block carries\n// tool_code, decision, decided_at, and an optional rejection note —\n// rich enough that the chat history reads \"Approved api_key.create_own\"\n// even after a refresh, instead of an ephemeral approval card that\n// vanishes the moment it's clicked.\nfunction DecisionChip({\n block,\n}: {\n block: { tool_code: string; decision: \"approved\" | \"rejected\"; note?: string; decided_at?: string };\n}) {\n const approved = block.decision === \"approved\";\n const accent = approved ? \"var(--success)\" : \"var(--destructive)\";\n return (\n <div\n style={{\n marginTop: 6,\n padding: \"5px 10px\",\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderLeft: `3px solid ${accent}`,\n borderRadius: 6,\n fontSize: 11.5,\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n color: \"var(--text)\",\n }}\n title={block.decided_at ? `decided ${block.decided_at}` : undefined}\n >\n <span aria-hidden style={{ color: accent, fontWeight: 700 }}>{approved ? \"✓\" : \"✗\"}</span>\n <span style={{ fontWeight: 600 }}>{approved ? \"Approved\" : \"Rejected\"}</span>\n <span className=\"mono\" style={{ color: \"var(--text-3)\", fontSize: 11 }}>{block.tool_code}</span>\n {block.note && (\n <span style={{ color: \"var(--text-3)\", fontStyle: \"italic\" }}>— {block.note}</span>\n )}\n </div>\n );\n}\n\n// copyTranscript serialises every message + every block in the panel\n// to a plain-text transcript and writes it to the clipboard. Reads\n// from `messages` directly so collapsed <details> chips, never-opened\n// approval cards, and any other UI-state-dependent content all land\n// faithfully — selection-based copy can't see the bodies of closed\n// chips because the browser excludes them from the selection range.\nasync function copyTranscript(messages: AgentMessage[]): Promise<void> {\n const lines: string[] = [];\n for (const m of messages) {\n const header = `[${m.role}]`;\n const body = m.content.map(serializeBlock).filter((s) => s.length > 0).join(\"\\n\");\n if (!body) continue;\n lines.push(`${header}\\n${body}`);\n }\n const text = lines.join(\"\\n\\n\");\n try {\n await navigator.clipboard.writeText(text);\n } catch (err) {\n console.error(\"agent: copyTranscript failed\", err);\n }\n}\n\nfunction serializeBlock(b: AgentContentBlock): string {\n switch (b.type) {\n case \"text\":\n return b.text;\n case \"tool_use\": {\n const display = deriveToolDisplay(b.name, b.input as Record<string, unknown> | undefined);\n const head = display.sub ? `→ ${display.label} (${display.sub})` : `→ ${display.label}`;\n return `${head}\\n${JSON.stringify(b.input, null, 2)}`;\n }\n case \"tool_result\": {\n const head = `${b.is_error ? \"✗\" : \"✓\"} ${b.name || (b.is_error ? \"error\" : \"result\")}`;\n return `${head}\\n${JSON.stringify(b.output, null, 2)}`;\n }\n case \"elicit_response\":\n return `answer\\n${JSON.stringify(b.data, null, 2)}`;\n case \"decision\":\n return `${b.decision === \"approved\" ? \"Approved\" : \"Rejected\"} ${b.tool_code}${b.note ? ` — ${b.note}` : \"\"}`;\n case \"system\":\n return `— ${b.reason}`;\n case \"summary\":\n return b.text;\n case \"attachment\":\n return b.content;\n case \"ui_hint\":\n return \"\";\n default:\n return \"\";\n }\n}\n\ninterface ApprovalProps {\n tool: {\n id: string;\n name: string;\n params: Record<string, unknown>;\n reason?: string;\n confidence?: number;\n };\n onApprove: (edits?: Record<string, unknown>) => void;\n onReject: (note?: string) => void;\n}\n\n// AgentApprovalCard: single-question security card. The platform\n// asks the user to authorize one mutating tool call. UI mirrors\n// Claude Code's permission prompt (one yes/no), with two affordances\n// for power users: a disclosure to view/edit the params JSON, and\n// optional Reject note to send back to the LLM.\n//\n// Reason (LLM-supplied, in user's language) is the prose summary\n// shown prominently. Confidence (LLM-supplied 0-1) renders as a\n// small pip — purely informational.\nfunction AgentApprovalCard({ tool, onApprove, onReject }: ApprovalProps) {\n const initialJSON = JSON.stringify(tool.params ?? {}, null, 2);\n const [paramsText, setParamsText] = useState(initialJSON);\n const [showParams, setShowParams] = useState(false);\n const [showReject, setShowReject] = useState(false);\n const [rejectNote, setRejectNote] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const submit = () => {\n if (submitting) return;\n let edits: Record<string, unknown> | undefined;\n if (paramsText.trim() && paramsText !== initialJSON) {\n try {\n edits = JSON.parse(paramsText);\n } catch (e) {\n setError(`invalid JSON: ${(e as Error).message}`);\n return;\n }\n }\n setSubmitting(true);\n onApprove(edits);\n };\n\n const reject = () => {\n if (submitting) return;\n setSubmitting(true);\n onReject(rejectNote.trim() || undefined);\n };\n\n const confidencePct = typeof tool.confidence === \"number\"\n ? Math.round(Math.max(0, Math.min(1, tool.confidence)) * 100)\n : null;\n\n return (\n <div style={{\n marginTop: 4, padding: \"10px 12px\", background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\", borderRadius: 7,\n }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, marginBottom: 6 }}>\n <Icon.bolt size={11} style={{ color: \"var(--accent)\" }} />\n <span style={{ fontSize: 11.5, fontWeight: 600, color: \"var(--text)\" }}>AI proposed</span>\n <span className=\"mono\" style={{ fontSize: 10.5, color: \"var(--text-3)\" }}>{tool.name}</span>\n {confidencePct !== null && (\n <span title={`LLM confidence ${confidencePct}%`} style={{\n marginLeft: \"auto\", fontSize: 10, color: \"var(--text-3)\",\n padding: \"1px 6px\", border: \"1px solid var(--border)\", borderRadius: 999,\n }}>\n {confidencePct}% confident\n </span>\n )}\n </div>\n\n {tool.reason && (\n <div style={{ fontSize: 12, lineHeight: 1.45, color: \"var(--text)\", marginBottom: 8 }}>\n {tool.reason}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={() => setShowParams((v) => !v)}\n style={{\n background: \"transparent\", border: 0, padding: 0, marginBottom: showParams ? 6 : 0,\n fontSize: 10.5, color: \"var(--text-3)\", cursor: \"pointer\",\n display: \"inline-flex\", alignItems: \"center\", gap: 4,\n }}\n >\n <span style={{\n display: \"inline-block\", transform: showParams ? \"rotate(90deg)\" : \"none\",\n transition: \"transform 0.15s\", fontSize: 9, lineHeight: 1,\n }}>▶</span>\n {showParams ? \"Hide\" : \"View / edit\"} parameters\n </button>\n\n {showParams && (\n <textarea\n value={paramsText}\n onChange={(e) => setParamsText(e.target.value)}\n rows={4}\n className=\"mono\"\n style={{\n width: \"100%\", padding: 6, fontSize: 10.5, lineHeight: 1.45,\n background: \"var(--surface)\", border: \"1px solid var(--border)\", borderRadius: 6,\n color: \"var(--text)\", resize: \"vertical\",\n }}\n />\n )}\n {error && <div style={{ marginTop: 4, fontSize: 10.5, color: \"var(--text-2)\" }}>{error}</div>}\n\n {showReject && (\n <input\n type=\"text\"\n value={rejectNote}\n onChange={(e) => setRejectNote(e.target.value)}\n placeholder=\"Optional note to the AI…\"\n autoFocus\n style={{\n marginTop: 6, width: \"100%\", padding: \"5px 7px\", fontSize: 11,\n background: \"var(--surface)\", border: \"1px solid var(--border)\",\n borderRadius: 6, color: \"var(--text)\",\n }}\n />\n )}\n\n <div style={{ display: \"flex\", gap: 6, marginTop: 8, justifyContent: \"flex-end\" }}>\n {!showReject ? (\n <Btn size=\"xs\" kind=\"ghost\" onClick={() => setShowReject(true)} disabled={submitting}>Reject</Btn>\n ) : (\n <Btn size=\"xs\" kind=\"ghost\" onClick={reject} disabled={submitting}>Send rejection</Btn>\n )}\n <Btn size=\"xs\" kind=\"primary\" icon={<Icon.check size={11} />} onClick={submit} disabled={submitting}>Approve</Btn>\n </div>\n </div>\n );\n}\n","import { useEffect } from \"react\";\n\n// FieldRegion is an imperative handle for a named region of form fields.\n// Regions register themselves on mount and unregister on unmount. ui_steps\n// reference fields as \"{region}.{field}\" (e.g. \"quick_entry.body\").\nexport interface FieldRegion {\n getField: (fieldName: string) => unknown;\n setField: (fieldName: string, value: unknown) => void;\n focusField: (fieldName: string) => void;\n}\n\n// Module-scope registry. Declarion is a single-app SPA; a module-scope map is\n// simpler than threading context through every tree and matches how toast\n// (sonner) is already consumed.\nconst regions = new Map<string, FieldRegion>();\n\nexport function registerFieldRegion(name: string, region: FieldRegion): () => void {\n regions.set(name, region);\n return () => {\n // Only unregister if still the same instance (remount races).\n if (regions.get(name) === region) regions.delete(name);\n };\n}\n\nexport function getFieldRegion(name: string): FieldRegion | undefined {\n return regions.get(name);\n}\n\n// useFieldRegion is a convenience hook for components to register themselves\n// for the lifetime of the mount. The handle MUST be stable or wrapped in useMemo\n// to avoid re-registration on every render.\nexport function useFieldRegion(name: string, region: FieldRegion): void {\n useEffect(() => registerFieldRegion(name, region), [name, region]);\n}\n","import { toast } from \"@/lib/toast\";\nimport { getFieldRegion } from \"./field-regions\";\nimport { formatWireDateTime } from \"./timestamp\";\nimport type { UIStep } from \"@/types/schema\";\n\n// UIActionContext is everything a ui_step sequence may read from.\n// row: current record (populated for row_actions). May be undefined for global actions.\n// params: current action-modal form state, used to resolve ${params.X} references\n// in HandlerParam.filter templates.\nexport interface UIActionContext {\n row?: Record<string, unknown>;\n params?: Record<string, unknown>;\n}\n\n// Supported tokens (match Go-side dispatch semantics where applicable):\n// ${now} - current time as local wall-clock + offset\n// (YYYY-MM-DDTHH:mm:ss±HH:MM). Matches the\n// wire contract: the server strips the offset\n// and re-zones for configured-timezone fields;\n// for passthrough fields the offset carries the\n// unambiguous instant.\n// ${row.FIELD} - read from ctx.row[FIELD]\n// ${field.REGION.FIELD} - read from a registered field region\n// ${params.NAME} - read from ctx.params[NAME]\n// Unknown tokens are left untouched in the output, matching the Go resolver's\n// fall-through — broken templates are visible instead of silently blank.\nconst TOKEN_RE = /\\$\\{\\s*([^}]+?)\\s*\\}/g;\nconst SINGLE_TOKEN_RE = /^\\s*\\$\\{\\s*([^}]+?)\\s*\\}\\s*$/;\n\nconst UNRESOLVED = Symbol(\"unresolved\");\n\n// interpolate resolves template tokens in a string. When the entire input is a\n// single bare token (e.g. \"${now}\" or \"${row.touch}\"), returns the raw typed\n// value — callers like set_field can then propagate booleans, dates, etc.\n// without stringifying. Mixed templates (\"Hi ${row.name}\") stringify as usual.\nexport function interpolate(template: string, ctx: UIActionContext): unknown {\n const single = template.match(SINGLE_TOKEN_RE);\n if (single) {\n const resolved = resolvePath(single[1], ctx);\n if (resolved === UNRESOLVED) return template;\n return resolved;\n }\n return template.replace(TOKEN_RE, (match, path: string) => {\n const resolved = resolvePath(path, ctx);\n if (resolved === UNRESOLVED) return match;\n return resolved == null ? \"\" : String(resolved);\n });\n}\n\nfunction resolvePath(path: string, ctx: UIActionContext): unknown | typeof UNRESOLVED {\n if (path === \"now\") return formatWireDateTime();\n const parts = path.split(\".\");\n const [root, ...rest] = parts;\n if (root === \"row\") {\n if (!ctx.row || rest.length === 0) return UNRESOLVED;\n return walk(ctx.row, rest);\n }\n if (root === \"field\" && rest.length >= 2) {\n const [regionName, fieldName] = rest;\n const region = getFieldRegion(regionName);\n if (!region) return UNRESOLVED;\n return region.getField(fieldName);\n }\n if (root === \"params\") {\n if (!ctx.params || rest.length === 0) return UNRESOLVED;\n return walk(ctx.params, rest);\n }\n return UNRESOLVED;\n}\n\nfunction walk(base: Record<string, unknown>, keys: string[]): unknown {\n return keys.reduce<unknown>((acc, key) => {\n if (acc && typeof acc === \"object\") return (acc as Record<string, unknown>)[key];\n return undefined;\n }, base);\n}\n\n// executeUISteps runs the step sequence in order. Errors surface as toasts and\n// stop the sequence - a partial execution is preferable to silent failure\n// because the steps mutate client state.\nexport async function executeUISteps(steps: UIStep[], ctx: UIActionContext): Promise<void> {\n for (const step of steps) {\n try {\n await runStep(step, ctx);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n toast.error(`ui_step failed: ${msg}`);\n return;\n }\n }\n}\n\nasync function runStep(step: UIStep, ctx: UIActionContext): Promise<void> {\n if (step.copy_to_clipboard != null) {\n const value = resolveToString(step.copy_to_clipboard, ctx);\n await navigator.clipboard.writeText(value);\n return;\n }\n if (step.set_field) {\n const { target, value } = step.set_field;\n const { region, field } = splitPath(target);\n const r = getFieldRegion(region);\n if (!r) throw new Error(`region \"${region}\" not registered`);\n r.setField(field, resolveValue(value, ctx));\n return;\n }\n if (step.focus_field) {\n const { region, field } = splitPath(step.focus_field);\n const r = getFieldRegion(region);\n if (!r) throw new Error(`region \"${region}\" not registered`);\n r.focusField(field);\n return;\n }\n if (step.show_toast) {\n const msg = resolveToString(step.show_toast.message, ctx);\n const kind = step.show_toast.kind ?? \"info\";\n if (kind === \"success\") toast.success(msg);\n else if (kind === \"error\") toast.error(msg);\n else toast.info(msg);\n return;\n }\n}\n\n// resolveValue: for set_field, pass through the raw typed value.\nfunction resolveValue(raw: unknown, ctx: UIActionContext): unknown {\n if (typeof raw !== \"string\") return raw;\n return interpolate(raw, ctx);\n}\n\n// resolveToString: for text-only steps (clipboard, toast message).\nfunction resolveToString(raw: string, ctx: UIActionContext): string {\n const v = interpolate(raw, ctx);\n return v == null ? \"\" : String(v);\n}\n\nfunction splitPath(path: string): { region: string; field: string } {\n const dot = path.indexOf(\".\");\n if (dot <= 0) throw new Error(`invalid path \"${path}\" (expected \"region.field\")`);\n return { region: path.slice(0, dot), field: path.slice(dot + 1) };\n}\n\n// isUIAction returns true when the action is a client-only ui_steps action.\nexport function isUIAction(action: { ui_steps?: UIStep[]; handler?: string }): boolean {\n return !action.handler && !!action.ui_steps && action.ui_steps.length > 0;\n}\n","// Target resolution for the mass-edit property grid. Mirrors the\n// buildFilterFieldMeta shape used by FilterDrawer so the picker UX and\n// value renderers stay aligned; the key difference is that mass-edit\n// sets values (no operator column) and excludes parameters entirely\n// (scope-resolution semantics incompatible with ops-level bulk editing;\n// see docs/plans/2026-04-22-mass-inline-edit.md §3).\nimport type { Entity, EntityField, LocalizedString, PropertyDef, StatusGroupDef } from \"@/types/schema\";\nimport { entityAllowsMutation, getPrimaryKeyFields, isCompositePk, loc } from \"@/types/schema\";\n\n/** Target kinds mirror engine.MassEditTargetKind in Go. */\nexport type MassEditTargetKind = \"field\" | \"status\" | \"property\";\n\n/** Metadata a property-grid row needs to render a target + editable value cell. */\nexport interface MassEditTarget {\n /** Picker key — bare field code, or `$status.<group>` / `$property.<code>`. */\n code: string;\n /** Bare identifier (without prefix) — \"priority\", \"approval\", \"color\". */\n bare: string;\n kind: MassEditTargetKind;\n label: string;\n /** For field targets, the underlying EntityField drives the value renderer. */\n field?: EntityField;\n /** For status targets, the group definition drives the status-picker options. */\n statusGroup?: StatusGroupDef;\n /** For property targets, the property definition drives type/widget/ref. */\n property?: PropertyDef;\n}\n\n/** Parse `HandlerParam.Filter[\"targets\"]` into a list of allowed keys. */\nexport function parseTargetFilter(raw: string | undefined): string[] {\n if (!raw) return [];\n const seen = new Set<string>();\n const out: string[] = [];\n for (const part of raw.split(\",\")) {\n const p = part.trim();\n if (!p || seen.has(p)) continue;\n seen.add(p);\n out.push(p);\n }\n return out;\n}\n\n/**\n * Build the list of writable mass-edit targets for an entity.\n * - When `whitelist` is set, narrows to those keys only (preserves the\n * order in the whitelist so action authors can curate a picker order).\n * - Without a whitelist, emits every non-PK, non-auto field plus all\n * status groups and properties (when not composite PK). Matches the\n * engine-side BuildMassEditTargets contract.\n * - entities without generic update globally hide every target (UI mirrors\n * the server gate; the dispatcher still enforces permissions).\n */\nexport function buildMassEditTargets(\n entity: Entity | undefined,\n whitelistRaw?: string | string[],\n): MassEditTarget[] {\n if (!entity || !entityAllowsMutation(entity, \"update\")) return [];\n const whitelist = Array.isArray(whitelistRaw) ? whitelistRaw : parseTargetFilter(whitelistRaw);\n const allow = whitelist.length > 0 ? new Set(whitelist) : null;\n const composite = isCompositePk(entity);\n const pkSet = new Set(getPrimaryKeyFields(entity));\n\n const keyOrder = allow ? whitelist : defaultTargetOrder(entity, composite, pkSet);\n const out: MassEditTarget[] = [];\n for (const key of keyOrder) {\n if (allow && !allow.has(key)) continue;\n const meta = resolveTarget(entity, key, composite, pkSet);\n if (meta) out.push(meta);\n }\n return out;\n}\n\nfunction defaultTargetOrder(entity: Entity, composite: boolean, pkSet: Set<string>): string[] {\n const order: string[] = [];\n for (const [name, f] of entity.fields) {\n if (pkSet.has(name) || f.auto) continue;\n order.push(name);\n }\n if (!composite) {\n for (const code of Object.keys(entity.status_groups ?? {})) {\n order.push(`$status.${code}`);\n }\n for (const code of Object.keys(entity.properties ?? {})) {\n order.push(`$property.${code}`);\n }\n }\n return order;\n}\n\nfunction resolveTarget(\n entity: Entity,\n key: string,\n composite: boolean,\n pkSet: Set<string>,\n): MassEditTarget | null {\n if (key.startsWith(\"$status.\")) {\n if (composite) return null;\n const bare = key.slice(\"$status.\".length);\n const group = entity.status_groups?.[bare];\n if (!group) return null;\n return {\n code: key,\n bare,\n kind: \"status\",\n label: localeOr(group.name, bare),\n statusGroup: group,\n };\n }\n if (key.startsWith(\"$property.\")) {\n if (composite) return null;\n const bare = key.slice(\"$property.\".length);\n const prop = entity.properties?.[bare];\n if (!prop) return null;\n return {\n code: key,\n bare,\n kind: \"property\",\n label: localeOr(prop.name, bare),\n property: prop,\n };\n }\n if (pkSet.has(key)) return null;\n const field = entity.fields.get(key);\n if (!field || field.auto) return null;\n return {\n code: key,\n bare: key,\n kind: \"field\",\n label: localeOr(field.display?.name, key),\n field,\n };\n}\n\nfunction localeOr(name: LocalizedString | undefined, fallback: string): string {\n if (!name) return fallback;\n return loc(name) || fallback;\n}\n","// PropertyGridEditor renders the `entity_patch` handler-param type: an\n// add-target-then-set-value grid for mass edit. Layout mirrors\n// FilterDrawer's FilterRow (one row per target, label + value cell +\n// remove button) but drops the operator column since mass edit sets\n// values rather than narrowing queries.\n//\n// Same-value pre-fill: when `selectedRows` is non-empty and every row\n// shares the same value for a target, that value seeds the editor cell.\n// Differing values render a neutral `<multiple values>` placeholder;\n// the user explicitly typing into the cell replaces the common value\n// across the whole bulk apply (documented in the plan §6).\nimport { useCallback, useMemo, useState } from \"react\";\nimport { Btn, Icon, IconBtn } from \"@/components/primitives\";\nimport { Select } from \"@/components/primitives/Fields\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { loc } from \"@/types/schema\";\nimport type { Entity, EntityField } from \"@/types/schema\";\nimport type { MassEditTarget } from \"@/lib/mass-edit\";\nimport { buildMassEditTargets } from \"@/lib/mass-edit\";\n\n/** Raw patch shape: field codes + optional $statuses / $properties subresource arrays. */\nexport type PatchMap = Record<string, unknown>;\n\ninterface PropertyGridEditorProps {\n entity: Entity | undefined;\n /** Optional YAML-declared target whitelist (HandlerParam.Filter[\"targets\"]). */\n filterTargets?: string;\n /** The current bulk selection. Used for same-value pre-fill per target. */\n selectedRows?: ReadonlyArray<Record<string, unknown>>;\n /** Current patch value (controlled). */\n value: PatchMap;\n /** Emitted whenever the user adds, changes, or removes a target. */\n onChange: (next: PatchMap) => void;\n}\n\n/** UI-only marker for cells whose selected rows disagree on the current value. */\nconst MULTIPLE_VALUES = Symbol.for(\"declarion.mass-edit.multiple\");\n\ninterface RowState {\n target: MassEditTarget;\n /**\n * Current editor value. Null = user hasn't touched the cell yet.\n * MULTIPLE_VALUES = rows disagreed; shows placeholder. Any other\n * value = user-selected common or explicit value.\n */\n value: unknown | typeof MULTIPLE_VALUES;\n}\n\nexport function PropertyGridEditor({\n entity,\n filterTargets,\n selectedRows,\n value,\n onChange,\n}: PropertyGridEditorProps) {\n const availableTargets = useMemo(\n () => buildMassEditTargets(entity, filterTargets),\n [entity, filterTargets],\n );\n\n // Derive row state from the controlled `value` map — keeps parents as\n // the source of truth while letting us attach the `MULTIPLE_VALUES`\n // marker on cells that started with differing values.\n const rows = useMemo<RowState[]>(() => {\n return Object.keys(value)\n .map((key) => {\n const target = availableTargets.find((t) => t.code === key);\n if (!target) return null;\n return { target, value: value[key] };\n })\n .filter((r): r is RowState => r !== null);\n }, [value, availableTargets]);\n\n const remainingTargets = useMemo(() => {\n const used = new Set(rows.map((r) => r.target.code));\n return availableTargets.filter((t) => !used.has(t.code));\n }, [rows, availableTargets]);\n\n const addTarget = useCallback(\n (target: MassEditTarget) => {\n const common = detectCommonValue(target, selectedRows);\n const next = { ...value };\n // MULTIPLE_VALUES is an in-memory marker; persist as undefined so\n // the payload shape stays JSON-friendly until the user types.\n next[target.code] = common === MULTIPLE_VALUES ? undefined : common;\n onChange(next);\n },\n [value, selectedRows, onChange],\n );\n\n const setValue = useCallback(\n (code: string, next: unknown) => {\n onChange({ ...value, [code]: next });\n },\n [value, onChange],\n );\n\n const removeTarget = useCallback(\n (code: string) => {\n const next = { ...value };\n delete next[code];\n onChange(next);\n },\n [value, onChange],\n );\n\n if (!entity) {\n return (\n <div style={{ fontSize: 12, color: \"var(--text-3)\" }}>\n Entity schema unavailable. Mass edit requires an entity-bound action.\n </div>\n );\n }\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n {rows.length === 0 && (\n <div style={{ fontSize: 12, color: \"var(--text-3)\" }}>\n No targets selected. Add a field, status, or property to edit across all selected rows.\n </div>\n )}\n {rows.map((row) => (\n <MassEditRow\n key={row.target.code}\n row={row}\n onChange={(v) => setValue(row.target.code, v)}\n onRemove={() => removeTarget(row.target.code)}\n />\n ))}\n\n <TargetPicker targets={remainingTargets} onPick={addTarget} />\n </div>\n );\n}\n\n// MassEditRow renders one target + value pair. The label is static; the\n// value cell dispatches on target.kind so each type gets the right\n// editor (field -> renderField, status -> Select of the group's\n// statuses, property -> renderField against a synthesized EntityField).\ninterface MassEditRowProps {\n row: RowState;\n onChange: (value: unknown) => void;\n onRemove: () => void;\n}\n\nfunction MassEditRow({ row, onChange, onRemove }: MassEditRowProps) {\n const { target } = row;\n return (\n <div\n data-testid=\"mass-edit-row\"\n style={{\n display: \"grid\",\n gridTemplateColumns: \"160px 1fr 28px\",\n gap: 8,\n alignItems: \"center\",\n }}\n >\n <div style={{ fontSize: 12.5, color: \"var(--text-2)\" }} title={target.code}>\n {target.label}\n </div>\n <ValueCell row={row} onChange={onChange} />\n <IconBtn icon={<Icon.x size={12} />} onClick={onRemove} title=\"Remove target\" />\n </div>\n );\n}\n\ninterface ValueCellProps {\n row: RowState;\n onChange: (value: unknown) => void;\n}\n\nfunction ValueCell({ row, onChange }: ValueCellProps) {\n const { target } = row;\n const userLocale = useUserLocale();\n const isMultiple = row.value === MULTIPLE_VALUES;\n const placeholder = isMultiple ? \"<multiple values>\" : undefined;\n const currentValue = isMultiple ? \"\" : row.value;\n\n if (target.kind === \"status\" && target.statusGroup) {\n const options = target.statusGroup.statuses.map((s) => ({\n value: s.code,\n label: loc(s.name) || s.code,\n }));\n return (\n <div data-testid={`value-${target.code}`}>\n <Select\n value={typeof currentValue === \"string\" ? currentValue : \"\"}\n options={options}\n onChange={onChange}\n placeholder={placeholder ?? \"(pick status)\"}\n />\n </div>\n );\n }\n\n // Fields and properties go through renderField. Property targets\n // synthesize a lightweight EntityField from the PropertyDef so the\n // same renderer registry handles string/enum/ref/date/etc. uniformly.\n const field: EntityField = fieldForTarget(target);\n return (\n <div data-testid={`value-${target.code}`} style={{ position: \"relative\" }}>\n {renderField({\n field,\n fieldName: target.bare,\n value: currentValue ?? \"\",\n mode: \"edit\",\n onChange,\n locale: userLocale,\n })}\n {placeholder && (\n <div\n data-testid={`multiple-values-${target.code}`}\n style={{ fontSize: 11, color: \"var(--text-3)\", marginTop: 4, fontStyle: \"italic\" }}\n >\n {placeholder}\n </div>\n )}\n </div>\n );\n}\n\nfunction fieldForTarget(target: MassEditTarget): EntityField {\n if (target.kind === \"field\" && target.field) return target.field;\n if (target.kind === \"property\" && target.property) {\n const p = target.property;\n return {\n name: target.bare,\n type: p.type,\n display: p.display ?? { name: p.name },\n values: p.values,\n ref: p.ref,\n structure: p.structure,\n } satisfies EntityField;\n }\n // Fallback — renderField picks StringField for unknown types.\n return { type: \"string\" } satisfies EntityField;\n}\n\n// TargetPicker is the `+ Add target` dropdown. Mirrors FilterDrawer's\n// FieldPicker affordance: pick from the remaining-not-yet-added list.\ninterface TargetPickerProps {\n targets: MassEditTarget[];\n onPick: (t: MassEditTarget) => void;\n}\n\nfunction TargetPicker({ targets, onPick }: TargetPickerProps) {\n const [picking, setPicking] = useState(false);\n\n if (targets.length === 0) {\n return null;\n }\n\n if (!picking) {\n return (\n <Btn kind=\"ghost\" onClick={() => setPicking(true)} style={{ alignSelf: \"flex-start\" }}>\n <Icon.plus size={12} /> Add target\n </Btn>\n );\n }\n\n return (\n <div data-testid=\"mass-edit-target-picker\" style={{ alignSelf: \"flex-start\", minWidth: 220 }}>\n <Select\n value=\"\"\n placeholder=\"Select target...\"\n options={targets.map((t) => ({ value: t.code, label: t.label }))}\n onChange={(code) => {\n const t = targets.find((x) => x.code === code);\n if (t) onPick(t);\n setPicking(false);\n }}\n />\n </div>\n );\n}\n\n// detectCommonValue scans selected rows for the target's current value.\n// Returns:\n// - undefined when selectedRows is empty (no prefill)\n// - the common value when every row agrees\n// - MULTIPLE_VALUES when rows disagree\n// Field targets read directly; status/property targets are not\n// prefilled because the frontend row data is the flat entity row — it\n// does not carry nested status/property state except when the list\n// query joined them (out of scope for MVP; Phase G may revisit).\nexport function detectCommonValue(\n target: MassEditTarget,\n selectedRows: ReadonlyArray<Record<string, unknown>> | undefined,\n): unknown | typeof MULTIPLE_VALUES {\n if (!selectedRows || selectedRows.length === 0) return undefined;\n if (target.kind !== \"field\") return undefined;\n let seen: unknown = undefined;\n let initialized = false;\n for (const row of selectedRows) {\n const v = row[target.bare];\n if (!initialized) {\n seen = v;\n initialized = true;\n continue;\n }\n if (seen !== v) return MULTIPLE_VALUES;\n }\n return seen;\n}\n\nexport { MULTIPLE_VALUES };\n","import { useState, useCallback, useMemo } from \"react\";\nimport { Btn, FieldLabel, Icon, IconBtn, Select, Spinner } from \"@/components/primitives\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { loc } from \"@/types/schema\";\nimport type {\n Action,\n HandlerParam,\n EntityField,\n StatusDef,\n StatusGroupDef,\n} from \"@/types/schema\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { interpolate } from \"@/lib/ui-actions\";\nimport { PropertyGridEditor, type PatchMap } from \"@/components/mass-edit/PropertyGridEditor\";\n\ninterface ActionDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n action: Action;\n actionName: string;\n onSubmit: (params: Record<string, unknown>) => void;\n isPending: boolean;\n row?: Record<string, unknown>;\n /**\n * Bulk selection when invoked from a list bulk bar. Used by two\n * param renderings: (1) `id_array` typed params are auto-filled with\n * the selection's PKs and hidden from the form, matching the\n * BulkBar auto-fill contract; (2) `entity_patch` params feed the\n * selection into PropertyGridEditor for same-value pre-fill.\n */\n bulkSelection?: ReadonlyArray<Record<string, unknown>>;\n}\n\nconst ROW_PREFILL_RE = /^\\$row\\.([a-zA-Z_][a-zA-Z0-9_]*)$/;\n\nfunction resolveParamDefault(raw: unknown, row: Record<string, unknown> | undefined): unknown {\n if (typeof raw !== \"string\") return raw ?? \"\";\n const match = raw.match(ROW_PREFILL_RE);\n if (!match) return raw;\n const field = match[1];\n if (row && field in row) return row[field] ?? \"\";\n if (typeof console !== \"undefined\" && console.warn) {\n console.warn(\n `ActionDialog: prefill template ${raw} could not be resolved; row missing or has no field '${field}'`,\n );\n }\n return \"\";\n}\n\nfunction paramToEntityField(param: HandlerParam): EntityField {\n return {\n type: param.type ?? \"string\",\n required: param.required,\n display: param.display\n ? { name: param.display.name ?? {}, widget: param.display.widget }\n : undefined,\n enum: param.enum,\n values: param.values,\n ref: param.ref ? { entity: param.ref } : undefined,\n };\n}\n\nfunction resolveFilterValue(raw: string, formData: Record<string, unknown>): string {\n if (!raw.includes(\"${\")) return raw;\n const v = interpolate(raw, { params: formData });\n return v == null ? \"\" : String(v);\n}\n\nconst TEMPLATE_TOKEN_RE = /\\$\\{\\s*([^}]+?)\\s*\\}/g;\n\nfunction referencesParam(template: string, paramName: string): boolean {\n const target = `params.${paramName}`;\n for (const match of template.matchAll(TEMPLATE_TOKEN_RE)) {\n if (match[1] === target) return true;\n }\n return false;\n}\n\ninterface StatusPickerProps {\n param: HandlerParam;\n value: unknown;\n onChange: (v: unknown) => void;\n statusGroups: Record<string, StatusGroupDef> | undefined;\n formData: Record<string, unknown>;\n}\n\nfunction StatusGroupPicker({ param, value, onChange, statusGroups }: StatusPickerProps) {\n const options = useMemo(() => {\n const entries = statusGroups ? Object.entries(statusGroups) : [];\n return [\n { value: \"\", label: param.required ? \"Select status group…\" : \"—\" },\n ...entries.map(([code, group]) => ({ value: code, label: loc(group.name) || code })),\n ];\n }, [statusGroups, param.required]);\n\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n options={options}\n onChange={(v) => onChange(v)}\n placeholder=\"Select status group…\"\n />\n );\n}\n\nfunction StatusPicker({ param, value, onChange, statusGroups, formData }: StatusPickerProps) {\n const statuses: StatusDef[] = useMemo(() => {\n if (!statusGroups) return [];\n const groupKey = param.filter?.[\"status_group\"];\n if (groupKey) {\n const resolved = resolveFilterValue(groupKey, formData);\n if (resolved && statusGroups[resolved]) return statusGroups[resolved].statuses;\n return [];\n }\n return Object.values(statusGroups).flatMap((g) => g.statuses);\n }, [statusGroups, param.filter, formData]);\n\n const options = useMemo(() => {\n return [\n {\n value: \"\",\n label: statuses.length === 0 ? \"Select status group first\" : \"Select status…\",\n },\n ...statuses.map((s) => ({ value: s.code, label: loc(s.name) || s.code })),\n ];\n }, [statuses]);\n\n return (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n options={options}\n onChange={(v) => onChange(v)}\n placeholder=\"Select status…\"\n />\n );\n}\n\nexport function ActionDialog({\n open,\n onOpenChange,\n action,\n actionName,\n onSubmit,\n isPending,\n row,\n bulkSelection,\n}: ActionDialogProps) {\n const { data: schema } = useSchema();\n const userLocale = useUserLocale();\n const params = action.resolved_params;\n const hiddenDefaults = action.resolved_hidden_defaults ?? {};\n const paramEntries = useMemo(() => {\n if (!params) return [] as Array<[string, HandlerParam]>;\n return Array.from(params);\n }, [params]);\n\n const entityStatusGroups = useMemo(() => {\n if (!action.entity || !schema) return undefined;\n return schema.entities[action.entity]?.status_groups;\n }, [action.entity, schema]);\n\n // Entity schema for the action's bound entity drives entity_patch\n // target resolution, same-value prefill, and picker narrowing. Nil for\n // global actions — ActionDialog still renders, just without a patch\n // editor (the loader-level guard in ValidateActions rejects that config\n // at startup, so in practice this is defensive).\n const entity = useMemo(() => {\n if (!action.entity || !schema) return undefined;\n return schema.entities[action.entity];\n }, [action.entity, schema]);\n\n // PK field name for auto-filling id_array params from the bulk\n // selection. Single-PK entities only; bulk dispatch routes through\n // data.update with object_ids carrying PK values.\n const pkField = useMemo(() => {\n if (!entity) return undefined;\n for (const [name, f] of entity.fields) {\n if (f.primary) return name;\n }\n return entity.primary_key?.[0];\n }, [entity]);\n\n const buildInitialFormData = useCallback(() => {\n const initial: Record<string, unknown> = { ...hiddenDefaults };\n for (const [key, param] of paramEntries) {\n if (param.type === \"id_array\") {\n // Auto-fill with the bulk selection's PKs. Hidden in the\n // render loop below; server-side caps enforced by the underlying\n // data.update bulk handler.\n if (bulkSelection && pkField) {\n initial[key] = bulkSelection.map((r) => r[pkField]).filter((v) => v != null);\n } else {\n initial[key] = [];\n }\n continue;\n }\n if (param.type === \"entity_patch\") {\n // Start empty; PropertyGridEditor prompts the user to add targets.\n initial[key] = {};\n continue;\n }\n if (param.type === \"bool\") {\n // A bool param must start as a real boolean. resolveParamDefault's\n // generic \"\" fallback (for an absent default) would submit the\n // toggle's untouched value as the string \"\", and the server rejects\n // it with `expected bool, got string`. Match the string form\n // explicitly - Boolean(\"false\") is true.\n initial[key] = param.default === true || param.default === \"true\";\n continue;\n }\n initial[key] = resolveParamDefault(param.default, row);\n }\n return initial;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paramEntries, row, bulkSelection, pkField]);\n\n const [formData, setFormData] = useState<Record<string, unknown>>(buildInitialFormData);\n\n const handleFieldChange = useCallback(\n (fieldName: string, value: unknown) => {\n setFormData((prev) => {\n const next = { ...prev, [fieldName]: value };\n if (!params) return next;\n for (const [key, param] of params) {\n if (param.type !== \"status\") continue;\n const filterBy = param.filter?.[\"status_group\"];\n if (!filterBy) continue;\n if (referencesParam(filterBy, fieldName)) next[key] = \"\";\n }\n return next;\n });\n },\n [params],\n );\n\n const handleSubmit = useCallback(() => {\n const payload: Record<string, unknown> = { ...formData };\n for (const [key, param] of paramEntries) {\n const val = formData[key];\n const isEmpty = val == null || val === \"\";\n // Block submit on a missing required param.\n if (param.required && isEmpty) return;\n // Omit an untouched optional param. Its value is the empty-string\n // sentinel a text input leaves behind; the server rejects \"\" for a\n // typed param (number, bool, ref, enum, ...) with `expected <type>,\n // got string`. Dropping it lets the handler apply its own declared\n // default. Bool params never reach here - they initialise to a real\n // boolean (see buildInitialFormData).\n if (isEmpty) delete payload[key];\n }\n onSubmit(payload);\n }, [formData, paramEntries, onSubmit]);\n\n if (!open) return null;\n\n return (\n <>\n <div\n onClick={() => onOpenChange(false)}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 480,\n maxHeight: \"80vh\",\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <div style={{ padding: \"14px 18px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: \"var(--accent-soft)\",\n color: \"var(--accent)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n <Icon.bolt size={15} />\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>{actionName}</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", marginTop: 2 }}>\n Fill in the parameters below to execute this action.\n </div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={() => onOpenChange(false)} />\n </div>\n\n <div\n style={{\n padding: \"12px 18px 16px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n overflowY: \"auto\",\n }}\n >\n {paramEntries.map(([key, param]) => {\n const paramName = loc(param.display?.name) || key;\n\n // id_array params are BulkBar auto-fill sinks — hidden from\n // the form so users don't edit the raw ID list. The value\n // already sits in formData from buildInitialFormData.\n if (param.type === \"id_array\") {\n return null;\n }\n\n let control: React.ReactNode;\n if (param.type === \"entity_patch\") {\n const whitelist = typeof param.filter?.[\"targets\"] === \"string\"\n ? param.filter[\"targets\"]\n : undefined;\n control = (\n <PropertyGridEditor\n entity={entity}\n filterTargets={whitelist}\n selectedRows={bulkSelection}\n value={(formData[key] as PatchMap) ?? {}}\n onChange={(next) => handleFieldChange(key, next)}\n />\n );\n } else if (param.type === \"status_group\") {\n control = (\n <StatusGroupPicker\n param={param}\n value={formData[key]}\n onChange={(v) => handleFieldChange(key, v)}\n statusGroups={entityStatusGroups}\n formData={formData}\n />\n );\n } else if (param.type === \"status\") {\n control = (\n <StatusPicker\n param={param}\n value={formData[key]}\n onChange={(v) => handleFieldChange(key, v)}\n statusGroups={entityStatusGroups}\n formData={formData}\n />\n );\n } else {\n const field = paramToEntityField(param);\n control = renderField({\n field,\n fieldName: key,\n value: formData[key],\n mode: \"edit\",\n onChange: (v) => handleFieldChange(key, v),\n locale: userLocale,\n });\n }\n\n return (\n <div key={key}>\n <FieldLabel required={param.required}>{paramName}</FieldLabel>\n {control}\n {param.display?.description && (\n <p style={{ fontSize: 11, color: \"var(--text-3)\", marginTop: 4, marginBottom: 0 }}>\n {loc(param.display.description)}\n </p>\n )}\n </div>\n );\n })}\n </div>\n\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n <div style={{ flex: 1 }} />\n <Btn onClick={() => onOpenChange(false)} disabled={isPending}>\n Cancel\n </Btn>\n <Btn\n kind=\"primary\"\n icon={isPending ? <Spinner size={13} /> : <Icon.bolt size={13} />}\n onClick={handleSubmit}\n disabled={isPending}\n >\n {isPending ? \"Executing…\" : \"Execute\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { Btn, Icon, IconBtn } from \"@/components/primitives\";\nimport { CopyButton } from \"./CopyButton\";\n\nexport interface DisplayField {\n label: string;\n value: string;\n copyable?: boolean;\n monospace?: boolean;\n}\n\nexport interface ActionDisplayResult {\n title?: string;\n message?: string;\n fields?: DisplayField[];\n}\n\ninterface ActionResultDialogProps {\n open: boolean;\n onClose: () => void;\n display: ActionDisplayResult;\n}\n\nexport function ActionResultDialog({ open, onClose, display }: ActionResultDialogProps) {\n if (!open) return null;\n return (\n <>\n <div\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 480,\n maxHeight: \"80vh\",\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <div style={{ padding: \"14px 18px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: \"var(--success-soft)\",\n color: \"var(--success)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n <Icon.check size={15} />\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>\n {display.title ?? \"Action Result\"}\n </div>\n {display.message && (\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", marginTop: 2 }}>\n {display.message}\n </div>\n )}\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={onClose} />\n </div>\n\n {display.fields && display.fields.length > 0 && (\n <div\n style={{\n padding: \"12px 18px 16px\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n overflowY: \"auto\",\n }}\n >\n {display.fields.map((field, i) => (\n <DisplayFieldRow key={i} field={field} />\n ))}\n </div>\n )}\n\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n <div style={{ flex: 1 }} />\n <Btn kind=\"primary\" onClick={onClose}>\n Close\n </Btn>\n </div>\n </div>\n </>\n );\n}\n\nfunction DisplayFieldRow({ field }: { field: DisplayField }) {\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n <div style={{ fontSize: 11.5, fontWeight: 500, color: \"var(--text-3)\" }}>{field.label}</div>\n <div style={{ position: \"relative\", width: \"100%\" }}>\n <div\n className={field.monospace ? \"mono\" : undefined}\n style={{\n borderRadius: 6,\n border: \"1px solid var(--border)\",\n background: \"var(--surface-2)\",\n padding: field.copyable ? \"8px 40px 8px 10px\" : \"8px 10px\",\n fontSize: 12.5,\n wordBreak: \"break-all\",\n color: \"var(--text)\",\n }}\n >\n {field.value}\n </div>\n {field.copyable && (\n <div style={{ position: \"absolute\", top: 4, right: 4 }}>\n <CopyButton value={field.value} />\n </div>\n )}\n </div>\n </div>\n );\n}\n","import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from \"react\";\nimport { useEmbedNavigate } from \"@/embed\";\nimport { toast } from \"@/lib/toast\";\nimport { Btn, Icon, IconBtn, Spinner } from \"@/components/primitives\";\nimport { loc, type Action } from \"@/types/schema\";\nimport { ActionDialog } from \"@/components/shared/ActionDialog\";\nimport { ActionResultDialog, type ActionDisplayResult } from \"@/components/shared/ActionResultDialog\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { ActionBatchError, type ActionExecuteResponse } from \"@/api/data\";\nimport { ApiError } from \"@/types/api\";\n\ntype OutcomeKind = \"success\" | \"accepted\" | \"skipped\" | \"partial\" | \"error\";\n\ninterface ActionOutcome {\n kind: OutcomeKind;\n /** Short one-line description suitable for a toast description slot. */\n detail?: string;\n}\n\n/**\n * Classify a successful (2xx/207) action result into a toast-ready outcome.\n * Falls back to `\"success\"` whenever the response doesn't carry the\n * invoke:each batch shape (e.g. invoke:all returning `{status:\"success\",result}`).\n */\nfunction interpretResult(result: unknown): ActionOutcome {\n if (!result || typeof result !== \"object\") return { kind: \"success\" };\n const r = result as Partial<ActionExecuteResponse>;\n const status = r.status;\n const successCount = r.success?.length ?? 0;\n const errorCount = r.errors?.length ?? 0;\n const skippedCount = r.skipped?.length ?? 0;\n const firstErr = r.errors?.[0]?.error;\n\n if (status === \"partial\") {\n const total = successCount + errorCount + skippedCount;\n const base = `${successCount} of ${total} succeeded`;\n return {\n kind: \"partial\",\n detail: firstErr ? `${base} · ${firstErr}` : base,\n };\n }\n if (status === \"skipped\") {\n return { kind: \"skipped\", detail: \"No items matched\" };\n }\n if (status === \"accepted\") {\n return { kind: \"accepted\" };\n }\n return { kind: \"success\" };\n}\n\n/**\n * Trigger a full page reload after a successful impersonation.start. The\n * handler returns an `ImpersonationStartResult` (embeds `LoginResult` whose\n * `CustomizeHTTPResponse` swaps the __Host- access + refresh cookies). To\n * adopt the new identity end-to-end (auth context, impersonation banner,\n * sidebar nav, SSE reconnect, React Query cache reset) the SPA must reload.\n *\n * The action envelope is `ActionExecuteResponse {status, result, ...}`. The\n * impersonation payload (`impersonation_session_id`, `expires_at`, ...) lives\n * in `result.result`, NOT at top level. We descend exactly one level.\n *\n * Called from every success branch (direct dispatch, params dialog, confirm\n * modal) because the impersonation action carries `resolved_params` (reason,\n * target_user_id, duration) and so always goes through the params-dialog\n * path; the direct-dispatch branch only ever fires for parameterless actions.\n */\n/**\n * Extract the handler's `$display` payload from a successful action\n * envelope. The envelope is `{status, result, audit_operation_id}` and\n * the handler return lives in `result`. Symmetric with\n * `maybeReloadAfterImpersonation`: same envelope shape, same descent\n * by one level. Returns null when the action did not return a $display\n * (the common case - most actions just emit a toast).\n */\nfunction extractDisplay(envelope: unknown): ActionDisplayResult | null {\n if (!envelope || typeof envelope !== \"object\") return null;\n const payload = (envelope as { result?: unknown }).result;\n if (!payload || typeof payload !== \"object\") return null;\n const display = (payload as { $display?: unknown }).$display;\n if (!display || typeof display !== \"object\") return null;\n return display as ActionDisplayResult;\n}\n\nfunction maybeReloadAfterImpersonation(envelope: unknown): void {\n if (!envelope || typeof envelope !== \"object\") return;\n const payload = (envelope as { result?: unknown }).result;\n if (!payload || typeof payload !== \"object\") return;\n if (\"impersonation_session_id\" in (payload as Record<string, unknown>)) {\n window.location.assign(\"/\");\n }\n}\n\n/** Classify a thrown error into a toast-ready outcome. */\nfunction interpretError(err: unknown): ActionOutcome {\n if (err instanceof ActionBatchError) {\n const resp = err.response;\n const errors = resp.errors ?? [];\n if (errors.length === 1) {\n return { kind: \"error\", detail: errors[0].error };\n }\n const first = errors[0]?.error;\n return {\n kind: \"error\",\n detail: first ? `${errors.length} failed · ${first}` : `${errors.length} failed`,\n };\n }\n if (err instanceof ApiError) {\n return { kind: \"error\", detail: err.message };\n }\n return { kind: \"error\", detail: err instanceof Error ? err.message : String(err) };\n}\n\n// Toasts go through the `lib/toast` facade: the title-first API carries the\n// detail line as `message` and the card renders it selectable, with the Copy\n// button built in on errors. Errors are sticky by design; success/info/warning\n// auto-dismiss on the variant's own timer.\n\nexport interface RunActionOptions {\n /** Full YAML action when available; drives destructive/long_running/resolved_params. */\n action?: Action;\n /** Presentation title for the modal/toast. */\n title?: string;\n /** When `action` is absent, individual flags still steer dispatch. */\n destructive?: boolean;\n longRunning?: boolean;\n /** Invoked after the user confirms / fills params. */\n execute: (params: Record<string, unknown>) => Promise<unknown> | unknown;\n /**\n * Caller-side hook fired after `execute` resolves with a `success`\n * outcome. Reason: navigation and cache-invalidation decisions that\n * follow an action depend on the caller's screen context, not on\n * the action declaration. Detail pages use it to navigate back to\n * the entity list after `__delete`; list pages can use it to clear\n * row selection; SSE-driven pages may rely on the realtime stream\n * instead and omit it entirely.\n */\n onSuccess?: (result: unknown) => void;\n /**\n * The current bulk selection (full row objects). Forwarded to\n * ActionDialog so entity_patch params can pre-fill same-value\n * editors from the selected rows. Omit for single-row or global\n * action invocations.\n */\n bulkSelection?: ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface ActionRunnerContextValue {\n /**\n * Dispatch an action. Resolves when the action settles: for a direct-run\n * action (no params, non-destructive) that is when `execute` finishes, so\n * callers can await it to keep a trigger button disabled for the whole\n * operation. For modal paths (params dialog / destructive confirm) it\n * resolves immediately - the dialog owns its own pending UI.\n */\n run: (opts: RunActionOptions) => Promise<void>;\n}\n\nconst Ctx = createContext<ActionRunnerContextValue | null>(null);\n\nexport function useActionRunner(): ActionRunnerContextValue {\n const v = useContext(Ctx);\n if (!v) throw new Error(\"useActionRunner must be used within <ActionRunnerProvider>\");\n return v;\n}\n\ninterface ModalState {\n id: symbol; // stable identity; survives { ...m, pending: true } spreads\n opts: RunActionOptions;\n title: string;\n kind: \"params\" | \"confirm\";\n pending: boolean;\n}\n\nfunction hiddenDefaults(opts: RunActionOptions): Record<string, unknown> {\n const seed: Record<string, unknown> = {};\n const hidden = opts.action?.resolved_hidden_defaults;\n if (hidden) {\n for (const [k, v] of Object.entries(hidden)) {\n if (v !== undefined) seed[k] = v;\n }\n }\n return seed;\n}\n\nexport function ActionRunnerProvider({ children }: { children: ReactNode }) {\n // Embed-aware: a post-action \"View\" navigation must stay inside the embed\n // boundary (self/delegated), not full-load the app inside the iframe.\n const navigate = useEmbedNavigate();\n const { data: schema } = useSchema();\n const [modal, setModal] = useState<ModalState | null>(null);\n // $display rendering is the platform-standard declarative path for\n // an action handler to return user-facing content that must NOT be\n // a toast (one-time-only secrets like API key plaintext, reveal-and-\n // copy artifacts, large summaries). Handler returns\n // `{$display: {title, message, fields}}` and the central runner pops\n // ActionResultDialog. Lives at the provider so EVERY dispatch path\n // (direct, params dialog, confirm modal) renders the same UX -\n // previously only ActionButton handled it, so any flow that went\n // through ResponsiveActionBar (SmartListPage toolbar / AssociatedBlock\n // toolbar / SmartDetailPage actions) lost the plaintext silently.\n const [displayResult, setDisplayResult] = useState<ActionDisplayResult | null>(null);\n\n const resolveProgressRoute = useCallback(\n (a: Action | undefined) => {\n if (!a?.progress_screen) return undefined;\n return schema?.screens[a.progress_screen]?.route;\n },\n [schema?.screens],\n );\n\n const emitToast = useCallback(\n (title: string, longRunning: boolean, action: Action | undefined, outcome: ActionOutcome, result?: unknown) => {\n // \"Started in background\" toast - shared by the long_running success and\n // the accepted (job-queued) outcomes.\n const emitStarted = () => {\n const jobId = extractJobId(result);\n const progressRoute = resolveProgressRoute(action);\n const detail = jobId ? `Job ${jobId} queued` : \"Running in background\";\n toast.success(\n `${title} started`,\n detail,\n progressRoute ? { action: { label: \"View\", onClick: () => navigate(progressRoute) } } : undefined,\n );\n };\n\n switch (outcome.kind) {\n case \"success\": {\n if (longRunning) emitStarted();\n else toast.success(`${title} complete`);\n return;\n }\n case \"accepted\":\n emitStarted();\n return;\n case \"skipped\":\n toast.info(title, outcome.detail);\n return;\n case \"partial\":\n toast.warning(`${title} partially completed`, outcome.detail);\n return;\n case \"error\":\n // Errors are sticky (the toast facade): they stay until the user\n // reads, copies and dismisses them. The Toaster stack cap keeps a\n // noisy session from piling overlays.\n toast.error(`${title} failed`, outcome.detail);\n return;\n }\n },\n [navigate, resolveProgressRoute],\n );\n\n const run = useCallback(\n (opts: RunActionOptions): Promise<void> => {\n const a = opts.action;\n const destructive = opts.destructive ?? a?.destructive ?? false;\n const longRunning = opts.longRunning ?? a?.long_running ?? false;\n const title = opts.title || loc(a?.display?.name) || \"Action\";\n\n const runToast = async (params: Record<string, unknown>) => {\n try {\n const result = await opts.execute(params);\n const outcome = interpretResult(result);\n emitToast(title, longRunning, a, outcome, result);\n if (outcome.kind === \"success\" || outcome.kind === \"accepted\") {\n opts.onSuccess?.(result);\n const display = extractDisplay(result);\n if (display) setDisplayResult(display);\n maybeReloadAfterImpersonation(result);\n }\n } catch (err) {\n emitToast(title, longRunning, a, interpretError(err));\n }\n };\n\n if (a?.resolved_params && a.resolved_params.length > 0) {\n setModal({ id: Symbol(), opts, title, kind: \"params\", pending: false });\n return Promise.resolve();\n }\n if (destructive) {\n setModal({ id: Symbol(), opts, title, kind: \"confirm\", pending: false });\n return Promise.resolve();\n }\n return runToast(hiddenDefaults(opts));\n },\n [emitToast],\n );\n\n const value = useMemo<ActionRunnerContextValue>(() => ({ run }), [run]);\n\n const modalAction = modal?.opts.action;\n\n const handleDialogSubmit = useCallback(\n async (params: Record<string, unknown>) => {\n if (!modal) return;\n const thisId = modal.id;\n const snap = modal;\n const a = snap.opts.action;\n const longRunning = snap.opts.longRunning ?? a?.long_running ?? false;\n setModal((m) => (m?.id === thisId ? { ...m, pending: true } : m));\n try {\n const result = await snap.opts.execute(params);\n const outcome = interpretResult(result);\n emitToast(snap.title, longRunning, a, outcome, result);\n if (outcome.kind === \"success\" || outcome.kind === \"accepted\") {\n snap.opts.onSuccess?.(result);\n const display = extractDisplay(result);\n if (display) setDisplayResult(display);\n maybeReloadAfterImpersonation(result);\n }\n // Close on clean outcomes; keep dialog open for partial so the user\n // reads the warning before dismissing.\n if (outcome.kind !== \"partial\") {\n setModal((m) => (m?.id === thisId ? null : m));\n }\n } catch (err) {\n emitToast(snap.title, longRunning, a, interpretError(err));\n } finally {\n setModal((m) => (m?.id === thisId ? { ...m!, pending: false } : m));\n }\n },\n [modal, emitToast],\n );\n\n return (\n <Ctx.Provider value={value}>\n {children}\n {/* $display dialog: rendered universally for ANY action whose\n handler returns `{$display: ...}` (api_key.create, password\n reset reveal, file-export download links, etc.). The dialog\n uses ActionResultDialog's standard layout - title, message,\n per-field copyable rows - so a handler author never writes\n dialog code by hand. */}\n {displayResult && (\n <ActionResultDialog\n open\n onClose={() => setDisplayResult(null)}\n display={displayResult}\n />\n )}\n {modal?.kind === \"params\" && modalAction && (\n <ActionDialog\n open\n onOpenChange={(o) => {\n if (!o) setModal(null);\n }}\n action={modalAction}\n actionName={modal.title}\n onSubmit={handleDialogSubmit}\n isPending={modal.pending}\n bulkSelection={modal.opts.bulkSelection}\n />\n )}\n {modal?.kind === \"confirm\" && (\n <ActionModal\n state={modal}\n onClose={() => setModal(null)}\n onExecute={async (params) => {\n const thisId = modal.id;\n const snap = modal;\n const a = snap.opts.action;\n const longRunning = snap.opts.longRunning ?? a?.long_running ?? false;\n let keepOpen = false;\n try {\n const result = await snap.opts.execute(params);\n const outcome = interpretResult(result);\n emitToast(snap.title, longRunning, a, outcome, result);\n if (outcome.kind === \"success\" || outcome.kind === \"accepted\") {\n snap.opts.onSuccess?.(result);\n const display = extractDisplay(result);\n if (display) setDisplayResult(display);\n maybeReloadAfterImpersonation(result);\n }\n // Partial: keep the destructive confirm modal open so the user\n // sees the warning before dismissing.\n keepOpen = outcome.kind === \"partial\";\n } catch (err) {\n emitToast(snap.title, longRunning, a, interpretError(err));\n // All-fail: close the modal (toast carries the error).\n } finally {\n if (!keepOpen) {\n setModal((m) => (m?.id === thisId ? null : m));\n }\n }\n }}\n />\n )}\n </Ctx.Provider>\n );\n}\n\nfunction extractJobId(v: unknown): string | undefined {\n if (!v || typeof v !== \"object\") return undefined;\n const rec = v as Record<string, unknown>;\n const id = rec.job_id ?? rec.id;\n return typeof id === \"string\" ? id : undefined;\n}\n\ninterface ActionModalProps {\n state: ModalState;\n onClose: () => void;\n onExecute: (params: Record<string, unknown>) => Promise<void>;\n}\n\nfunction ActionModal({ state, onClose, onExecute }: ActionModalProps) {\n const { title, opts } = state;\n const [running, setRunning] = useState(false);\n\n const action = opts.action;\n const destructive = opts.destructive ?? action?.destructive ?? false;\n\n const go = async () => {\n setRunning(true);\n try {\n await onExecute(hiddenDefaults(opts));\n } finally {\n setRunning(false);\n }\n };\n\n return (\n <>\n <div\n onClick={onClose}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 460,\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"14px 18px 0\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: destructive ? \"var(--danger-soft)\" : \"var(--accent-soft)\",\n color: destructive ? \"var(--danger)\" : \"var(--accent)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {destructive ? <Icon.trash size={15} /> : <Icon.bolt size={15} />}\n </span>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>{title}</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\" }}>\n This cannot be undone. Confirm to proceed.\n </div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={onClose} />\n </div>\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n {/* Action code (handler symbol) is a developer-facing identifier\n and MUST NOT appear in user-facing dialogs. The mono badge\n that used to render `${actionCode}` here was pure dev leak\n (e.g. an end user clicking Delete saw \"__platform.delete\")\n and offers no decision-grade context the title + body don't\n already give. Removed. */}\n <div style={{ flex: 1 }} />\n <Btn onClick={onClose}>Cancel</Btn>\n <Btn\n kind=\"primary\"\n danger={destructive}\n icon={running ? <Spinner size={13} /> : <Icon.bolt size={13} />}\n onClick={go}\n >\n {running ? \"Executing…\" : \"Confirm\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { apiFetch } from \"@/api/client\";\nimport type { ListResponse } from \"@/types/api\";\nimport { pkValuesFromId } from \"@/types/schema\";\n\n// ENTITY_CHILDREN_KEY is the single source of truth for the query key prefix\n// used by all child entity queries. useSSE, QuickAddForm, TimelineComposer,\n// and SmartDetailPage must all use this constant to stay in sync.\nexport const ENTITY_CHILDREN_KEY = \"entity-children\" as const;\n\n// useChildrenData fetches child entity records for a parent record.\n// Extracted as a shared hook so all child display components can reuse it.\n// Uses the standard list endpoint with a filter on the foreign key field(s).\nexport function useChildrenData(\n parentEntity: string,\n parentId: string,\n childCode: string,\n foreignKey: string | string[],\n sort?: string,\n limit?: number,\n) {\n const fkArr = Array.isArray(foreignKey) ? foreignKey : [foreignKey];\n const idArr = Array.isArray(foreignKey) ? pkValuesFromId(parentId, fkArr.length) : [parentId];\n\n return useQuery({\n queryKey: [ENTITY_CHILDREN_KEY, parentEntity, parentId, childCode, sort, limit],\n queryFn: () => {\n const params = new URLSearchParams();\n if (!idArr) throw new Error(\"invalid composite parent id\");\n const filters = fkArr.map((fk, i) => ({ field: fk, op: \"eq\", value: idArr[i] }));\n params.set(\"filters\", JSON.stringify(filters));\n params.set(\"preview\", \"true\");\n if (sort) params.set(\"sort\", sort);\n if (limit) params.set(\"limit\", String(limit));\n return apiFetch<ListResponse>(\n `/api/data/${childCode}?${params.toString()}`,\n );\n },\n enabled: !!parentEntity && !!parentId && !!childCode && !!foreignKey && idArr !== null,\n });\n}\n","import { create } from \"zustand\";\n\n// useRealtimeStore tracks which entities the CDC relay has auto-degraded\n// during this browser session. The A3 high-volume monitor degrades an\n// over-threshold `realtime: true` entity at runtime and the server emits a\n// `realtime_degraded` SSE frame for it (see useSSE). A degraded entity stops\n// receiving live row updates, so its list views must fall back to the manual\n// refresh control - the same treatment a `realtime: false` entity gets.\n//\n// The set is session-scoped and intentionally not persisted: degrade state is\n// a property of the live relay, and a fresh page load re-derives it from the\n// delivery-log replay (the server re-emits the degraded row on reconnect).\ninterface RealtimeState {\n /** Entity codes the relay auto-degraded this session. */\n degradedEntities: ReadonlySet<string>;\n /** Mark one entity as auto-degraded. Idempotent. */\n markDegraded: (entityCode: string) => void;\n}\n\nexport const useRealtimeStore = create<RealtimeState>((set, get) => ({\n degradedEntities: new Set<string>(),\n markDegraded: (entityCode) => {\n if (!entityCode || get().degradedEntities.has(entityCode)) return;\n const next = new Set(get().degradedEntities);\n next.add(entityCode);\n set({ degradedEntities: next });\n },\n}));\n\n// isEntityLive reports whether an entity is expected to auto-update its list\n// views. An entity is live only when its schema flag `realtime` is true AND\n// the relay has not auto-degraded it this session. Every table view uses this\n// single helper to decide whether to surface the manual-refresh staleness\n// affordance, so the rule stays in one place.\nexport function isEntityLive(\n realtimeFlag: boolean | undefined,\n degradedEntities: ReadonlySet<string>,\n entityCode: string,\n): boolean {\n return realtimeFlag === true && !degradedEntities.has(entityCode);\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport { useQueryClient, type QueryClient } from \"@tanstack/react-query\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { streamEvents, SSEAuthError } from \"@/api/events\";\nimport { ensureRefresh, RefreshDeadError, getPersistentAuthFailureHandler } from \"@/api/client\";\nimport { ENTITY_CHILDREN_KEY } from \"@/hooks/useChildrenData\";\nimport { useRealtimeStore } from \"@/stores/realtime\";\nimport { awaitEmbedToken, clearEmbedToken, getEmbedToken, isEmbedTokenMode } from \"@/embed/token\";\n\nconst MAX_RECONNECT_DELAY = 30000;\nconst BASE_DELAY = 1000;\n\n// CACHE_RESET_JITTER_MS bounds the random delay before a cache_reset\n// refetch. A slot invalidation broadcasts cache_reset to every connected\n// client at once; refetching immediately would fire one synchronized\n// /api/data burst from the whole fleet (a thundering herd) right after the\n// CDC relay already recovered from a slot loss. Each client waits a uniform\n// random delay in [0, CACHE_RESET_JITTER_MS) so the refetches spread evenly.\n// 3s is long enough to flatten the herd across a realistic client count and\n// short enough that the UI still refreshes promptly.\nconst CACHE_RESET_JITTER_MS = 3000;\n\n// COALESCE_WINDOW_MS groups entity_changed events into a single invalidation\n// per entity-per-window. A bulk upsert of 1000 rows produces ~2000 SSE events\n// server-side (audit_change + audit_operation per write); without coalescing,\n// React Query refires one HTTP fetch per event, saturating the UI and the\n// server's rate limiter. 50ms is short enough to feel real-time and long\n// enough to absorb a typical bulk-write burst.\nconst COALESCE_WINDOW_MS = 50;\n\ntype EntityAction = \"created\" | \"updated\" | \"deleted\" | \"restored\";\n\ninterface EntityChangedEvent {\n entity: string;\n action: EntityAction;\n eventID?: string;\n}\n\nfunction documentIsVisible(): boolean {\n if (typeof document === \"undefined\") return true;\n return document.visibilityState !== \"hidden\";\n}\n\nfunction useDocumentVisible(): boolean {\n const [visible, setVisible] = useState(documentIsVisible);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return undefined;\n const onVisibilityChange = () => setVisible(documentIsVisible());\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n return () => document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n }, []);\n\n return visible;\n}\n\n// invalidateForEntityChanges deduplicates a batch of entity_changed events\n// and fires the minimum set of React Query invalidations. Per entity:\n// - always invalidate the list-window query, the list-count query, and\n// matching child queries;\n// - additionally invalidate cached detail views if any event for that\n// entity is a mutation (update/delete/restore) — create events don't\n// need detail invalidation because a new row has no cached detail yet.\n// The list-count query (`entity-count`, see useEntityData) is a separate\n// request from the list window, so it must be invalidated explicitly here\n// or list totals and page counts go stale after a realtime change.\n// Exported so useSSE can call it and tests can exercise it directly.\nexport function invalidateForEntityChanges(\n queryClient: QueryClient,\n events: EntityChangedEvent[],\n): void {\n if (events.length === 0) return;\n\n const entities = new Map<string, { mutated: boolean }>();\n for (const ev of events) {\n const prev = entities.get(ev.entity);\n const mutated =\n ev.action === \"updated\" ||\n ev.action === \"deleted\" ||\n ev.action === \"restored\";\n entities.set(ev.entity, { mutated: (prev?.mutated ?? false) || mutated });\n }\n\n for (const [entity, state] of entities) {\n queryClient.invalidateQueries({ queryKey: [\"entity-data\", entity] });\n queryClient.invalidateQueries({ queryKey: [\"entity-count\", entity] });\n\n queryClient.invalidateQueries({\n predicate: (query) => {\n const key = query.queryKey;\n return key[0] === ENTITY_CHILDREN_KEY && key[3] === entity;\n },\n });\n\n if (state.mutated) {\n queryClient.invalidateQueries({ queryKey: [\"entity-item\", entity] });\n }\n }\n}\n\nfunction isSSEBackedQueryKey(key: readonly unknown[]): boolean {\n return (\n key[0] === \"entity-data\" ||\n key[0] === \"entity-count\" ||\n key[0] === \"entity-item\" ||\n key[0] === ENTITY_CHILDREN_KEY\n );\n}\n\nexport function refetchActiveSSEQueries(queryClient: QueryClient): void {\n queryClient.invalidateQueries({\n predicate: (query: { queryKey: readonly unknown[] }) => isSSEBackedQueryKey(query.queryKey),\n refetchType: \"active\",\n });\n}\n\nexport function useSSE() {\n const isAuthenticated = useAuthStore((s) => s.isAuthenticated);\n const activeTenantID = useAuthStore((s) => s.activeTenant?.id ?? \"\");\n const queryClient = useQueryClient();\n const abortRef = useRef<AbortController | null>(null);\n const lastEventIDRef = useRef(\"\");\n const cursorScopeRef = useRef(\"\");\n const documentVisible = useDocumentVisible();\n const wasHiddenRef = useRef(!documentVisible);\n\n useEffect(() => {\n if (!isAuthenticated) {\n lastEventIDRef.current = \"\";\n cursorScopeRef.current = \"\";\n wasHiddenRef.current = !documentVisible;\n return;\n }\n if (!documentVisible) {\n wasHiddenRef.current = true;\n abortRef.current?.abort();\n abortRef.current = null;\n return;\n }\n\n if (wasHiddenRef.current) {\n wasHiddenRef.current = false;\n refetchActiveSSEQueries(queryClient);\n }\n\n if (cursorScopeRef.current !== activeTenantID) {\n lastEventIDRef.current = \"\";\n cursorScopeRef.current = activeTenantID;\n }\n\n let mounted = true;\n let attempt = 0;\n\n const pending: EntityChangedEvent[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let cacheResetTimer: ReturnType<typeof setTimeout> | null = null;\n\n // scheduleCacheResetRefetch staggers the post-cache_reset refetch by a\n // uniform random delay so a fleet-wide slot invalidation does not\n // produce a synchronized /api/data burst. Only one refetch is pending\n // at a time: a second cache_reset arriving inside the window is folded\n // into the already-scheduled refetch (the refetch reloads current DB\n // state regardless, so coalescing loses nothing).\n const scheduleCacheResetRefetch = () => {\n if (cacheResetTimer !== null) return;\n const delay = Math.random() * CACHE_RESET_JITTER_MS;\n cacheResetTimer = setTimeout(() => {\n cacheResetTimer = null;\n if (!mounted) return;\n refetchActiveSSEQueries(queryClient);\n }, delay);\n };\n\n const flush = () => {\n flushTimer = null;\n if (pending.length === 0) return;\n const batch = pending.splice(0, pending.length);\n invalidateForEntityChanges(queryClient, batch);\n for (const ev of batch) {\n if (ev.eventID) lastEventIDRef.current = ev.eventID;\n }\n };\n\n const scheduleFlush = () => {\n if (flushTimer !== null) return;\n flushTimer = setTimeout(flush, COALESCE_WINDOW_MS);\n };\n\n async function connect() {\n while (mounted) {\n const controller = new AbortController();\n abortRef.current = controller;\n\n // Reset backoff on every successful CONNECTION attempt\n // (not just on first event received). Without this, after\n // the Vite dev proxy / server idle-timeout closes a stream\n // cleanly (`done: true` exits the for-await with no error),\n // the next reconnect carries forward `attempt` from the\n // previous cycle. The user can sit through 30s of silence\n // before the next attempt, during which any agent_stream\n // frame from a freshly submitted turn is dropped at the\n // Hub (no subscribers; ring buffer holds it but nothing\n // replays). Resetting here keeps the gap to a base 1s.\n attempt = 0;\n\n // Embed token mode: stream over the scoped Bearer token with\n // cookies omitted. `getEmbedToken()` is read fresh on every\n // reconnect so a token refreshed mid-session (after `session_refresh`\n // or `SSEAuthError`) is picked up on the next connection attempt.\n // `lastEventIDRef` carries the resume cursor across the refresh, so\n // no realtime events are lost. Outside embed mode both fields are\n // undefined and `streamEvents` runs the cookie path unchanged.\n const embedMode = isEmbedTokenMode();\n const embedToken = embedMode ? getEmbedToken() : null;\n\n try {\n for await (const event of streamEvents({\n signal: controller.signal,\n lastEventID: lastEventIDRef.current,\n bearerToken: embedToken ?? undefined,\n credentials: embedMode ? \"omit\" : undefined,\n })) {\n if (!mounted) break;\n\n if (event.type === \"session_refresh\") {\n // Permissions, roles, or tenant authority changed. Refresh the\n // token (the new authority is baked into the JWT), then drop the\n // authority-shaped caches so the UI re-renders against the new\n // authority without a full page reload:\n // - [\"schema\"] is fetched once with staleTime: Infinity and is\n // authority-shaped server-side (schema-cache ETags encode\n // caller authority - see docs/gotchas.md \"Schema, Engine &\n // Validation\"). Without invalidation the nav, screens, and\n // actions stay stale until the tab is reloaded.\n // - the auth-store user carries `permissions`, which drives\n // client-side action and field gating.\n //\n // Embed token mode: NEVER POST `auth.refresh` - the embed\n // token is refresh-less. Ask the host to re-mint via\n // `awaitEmbedToken()` (emits `token-expired`); the host's\n // fresh token carries the changed permissions. The reconnect\n // below reads the new token from `getEmbedToken()`.\n try {\n if (embedMode) {\n // The held token is clock-valid but its baked-in authority\n // is now stale. Clear it so awaitEmbedToken() emits\n // `token-expired` and waits for the host to re-mint,\n // instead of fast-returning the stale token (which would\n // reconnect, draw another `session_refresh`, and loop).\n clearEmbedToken();\n await awaitEmbedToken();\n } else {\n await ensureRefresh();\n }\n queryClient.invalidateQueries({ queryKey: [\"schema\"] });\n void useAuthStore.getState().refreshMe();\n } catch {\n // Refresh failed - reconnect will use new token or fail.\n }\n break; // Break to reconnect with new token.\n }\n\n if (event.type === \"checkpoint\") {\n lastEventIDRef.current = event.eventID;\n continue;\n }\n\n if (event.type === \"cache_reset\") {\n if (event.eventID) lastEventIDRef.current = event.eventID;\n scheduleCacheResetRefetch();\n continue;\n }\n\n if (event.type === \"realtime_degraded\") {\n // The relay auto-degraded this entity: it no longer fans row\n // changes out, so its list views will not auto-update for the\n // rest of the session. Record it so every table view falls\n // back to the manual refresh control (same as `realtime:\n // false`). Refetch once now so the view is current at the\n // moment live updates stop.\n if (event.eventID) lastEventIDRef.current = event.eventID;\n useRealtimeStore.getState().markDegraded(event.entity);\n invalidateForEntityChanges(queryClient, [\n { entity: event.entity, action: \"updated\" },\n ]);\n continue;\n }\n\n if (event.type === \"entity_changed\") {\n pending.push({ entity: event.entity, action: event.action, eventID: event.eventID });\n scheduleFlush();\n }\n }\n } catch (err) {\n if (!mounted) break;\n if (err instanceof DOMException && err.name === \"AbortError\") break;\n\n // Cookie expired (5-min access token). Refresh it BEFORE\n // the backoff so the next reconnect uses the new token.\n // Without this, the loop retries forever with a dead\n // cookie, delivering nothing for hours.\n //\n // Embed token mode: the embed JWT is refresh-less. Ask the host\n // to re-mint via `awaitEmbedToken()` (emits `token-expired`)\n // instead of POSTing `auth.refresh`. On success the reconnect\n // reads the fresh token; the `lastEventIDRef` resume cursor is\n // preserved so no events are lost.\n if (err instanceof SSEAuthError) {\n if (embedMode) {\n try {\n // The SSE stream 401'd: the token was rejected even if it is\n // still clock-valid. Clear it so awaitEmbedToken() emits\n // `token-expired` and waits for a fresh token, instead of\n // fast-returning the dead token and reconnect-looping.\n clearEmbedToken();\n await awaitEmbedToken();\n attempt = 0; // fresh token in hand; reconnect immediately\n continue;\n } catch {\n // The host never re-minted in time. Escalate to the\n // persistent-auth-failure handler (in embed mode this is\n // the host-notifying emitter installed by the bootstrap).\n getPersistentAuthFailureHandler()();\n return;\n }\n }\n try {\n await ensureRefresh();\n attempt = 0; // refresh succeeded; reconnect immediately\n continue;\n } catch (refreshErr) {\n // Terminal refresh failure (refresh cookie missing, token\n // reused/expired, session revoked). Stop the reconnect loop\n // and route to /login — without this break, useSSE keeps\n // hitting /api/events → 401 → /api/actions/auth.refresh →\n // 401, hammering the rate-limited refresh endpoint until\n // the user reloads the tab. apiFetch's interceptor would\n // also redirect, but useSSE owns its own fetch and must\n // escalate independently.\n if (refreshErr instanceof RefreshDeadError) {\n getPersistentAuthFailureHandler()();\n return;\n }\n // Generic network/transient error — fall through to backoff.\n }\n }\n }\n\n // Exponential backoff with 50% jitter.\n if (mounted) {\n const delay = Math.min(BASE_DELAY * Math.pow(2, attempt), MAX_RECONNECT_DELAY);\n const jitter = delay * 0.5 * Math.random();\n await new Promise((r) => setTimeout(r, delay + jitter));\n attempt++;\n }\n }\n }\n\n connect();\n\n return () => {\n mounted = false;\n abortRef.current?.abort();\n abortRef.current = null;\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (cacheResetTimer !== null) {\n clearTimeout(cacheResetTimer);\n cacheResetTimer = null;\n }\n if (pending.length > 0) {\n flush();\n }\n };\n }, [isAuthenticated, activeTenantID, queryClient, documentVisible]);\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Outlet, useNavigate, useLocation } from \"react-router-dom\";\nimport { Rail } from \"./Rail\";\nimport { Sidebar } from \"./Sidebar\";\nimport { TopBar } from \"./TopBar\";\nimport { TabStrip } from \"./TabStrip\";\nimport { DirtyTabsCloseDialog } from \"./DirtyTabsCloseDialog\";\nimport { ImpersonationBanner } from \"./ImpersonationBanner\";\nimport { ImpersonationFrame } from \"./ImpersonationFrame\";\nimport { buildPendingTabClose, tabCloseRoute, type PendingTabClose } from \"./tab-close\";\nimport { CommandPalette, type CommandAction } from \"@/components/shell/CommandPalette\";\nimport { AgentChatPanel } from \"@/components/agents/AgentChatPanel\";\nimport { ActionRunnerProvider } from \"@/components/shared/ActionRunner\";\nimport {\n useEmbed,\n useGlobalLinkInterceptor,\n EmbedLocationReporter,\n EmbedUnsavedGuard,\n EmbedResizeReporter,\n EmbedDirtyReporter,\n} from \"@/embed\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useTabStore, type TabCloseScope } from \"@/stores/tabs\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useSSE } from \"@/hooks/useSSE\";\nimport { usePublicParams } from \"@/hooks/useParams\";\nimport { loginPathForRedirectPath } from \"@/api/auth-redirect\";\nimport { loc, type NavNode, type Screen } from \"@/types/schema\";\nimport type { Crumb } from \"@/components/shared/Breadcrumbs\";\n\nconst DEFAULT_AREA = \"default\";\n\nexport function Layout() {\n // The single global link interceptor: one capture-phase document click\n // listener that routes every internal anchor (cells, detail chips, AND\n // markdown / rich-text anchors we do not author) through the embed\n // navigation chokepoint. Mounted here so it covers both the embed (bare\n // Outlet) and standalone (full chrome) branches below, but NOT the auth\n // routes, which live outside Layout and keep native react-router links.\n useGlobalLinkInterceptor();\n\n const { isAuthenticated, isLoading, user, activeTenant } = useAuthStore();\n const embed = useEmbed();\n const navigate = useNavigate();\n const location = useLocation();\n const { data: schema } = useSchema();\n const sidebarMode = useAppearanceStore((s) => s.sidebar);\n const tabsPosition = useAppearanceStore((s) => s.tabsPosition);\n const aiSurface = useAppearanceStore((s) => s.aiSurface);\n const theme = useAppearanceStore((s) => s.theme);\n const setAppearance = useAppearanceStore((s) => s.set);\n const hydrateAppearance = useAppearanceStore((s) => s.hydrateFromParams);\n const { data: publicParams } = usePublicParams();\n\n const tabs = useTabStore((s) => s.tabs);\n const activeTabId = useTabStore((s) => s.activeTabId);\n const closeTabs = useTabStore((s) => s.closeTabs);\n const setScope = useTabStore((s) => s.setScope);\n const hydrate = useTabStore((s) => s.hydrate);\n\n const [activeArea, setActiveArea] = useState<string>(DEFAULT_AREA);\n const [cmdOpen, setCmdOpen] = useState(false);\n const [cmdMode, setCmdMode] = useState<\"default\" | \"new\">(\"default\");\n const [aiOpen, setAiOpen] = useState(false);\n const [pendingTabClose, setPendingTabClose] = useState<PendingTabClose | null>(null);\n\n const paletteEnabled = aiSurface !== \"off\";\n const panelEnabled = aiSurface === \"panel\" || aiSurface === \"both\";\n const paletteAI = aiSurface === \"command\" || aiSurface === \"both\";\n const aiName = schema?.branding?.ai_name?.trim() ?? \"\";\n\n const openPalette = useCallback((mode: \"default\" | \"new\" = \"default\") => {\n if (!paletteEnabled) return;\n setCmdMode(mode);\n setCmdOpen(true);\n }, [paletteEnabled]);\n\n const performTabClose = useCallback(\n (ids: string[]) => {\n if (ids.length === 0) return;\n const before = useTabStore.getState();\n const shouldNavigate = !before.activeTabId || ids.includes(before.activeTabId);\n const next = closeTabs(ids);\n if (!shouldNavigate) return;\n const target = tabCloseRoute(next);\n navigate(\n { pathname: target.pathname, search: target.search },\n { replace: target.replace },\n );\n },\n [closeTabs, navigate],\n );\n\n const requestTabClose = useCallback(\n (scope: TabCloseScope, targetId?: string) => {\n const state = useTabStore.getState();\n const pending = buildPendingTabClose(state.tabs, state.activeTabId, scope, targetId);\n if (!pending) return;\n if (pending.dirtyTabs.length > 0) {\n setPendingTabClose(pending);\n return;\n }\n performTabClose(pending.ids);\n },\n [performTabClose],\n );\n\n const cancelPendingTabClose = useCallback(() => {\n setPendingTabClose(null);\n }, []);\n\n const closePendingCleanTabs = useCallback(() => {\n if (!pendingTabClose) return;\n setPendingTabClose(null);\n performTabClose(pendingTabClose.cleanIds);\n }, [pendingTabClose, performTabClose]);\n\n const discardPendingTabs = useCallback(() => {\n if (!pendingTabClose) return;\n setPendingTabClose(null);\n performTabClose(pendingTabClose.ids);\n }, [pendingTabClose, performTabClose]);\n\n useSSE();\n\n useEffect(() => {\n // In embed mode the iframe boots with no token; the postMessage\n // handshake (a later task) delivers it. Redirecting to /login here\n // would flash the Declarion login page inside the white-label iframe.\n // Embed mode never resolves to the login page - app.tsx holds a\n // neutral blank until the embed session authenticates.\n if (embed.enabled) return;\n if (!isLoading && !isAuthenticated) {\n navigate(\n loginPathForRedirectPath(`${location.pathname}${location.search}${location.hash}`),\n { replace: true },\n );\n }\n }, [embed.enabled, isLoading, isAuthenticated, navigate, location.hash, location.pathname, location.search]);\n\n // Scope tabs store by tenant + user; hydrate persisted state once scope set.\n useEffect(() => {\n const uid = user?.id || null;\n const tid = activeTenant?.id || null;\n setScope(tid, uid);\n if (uid && tid) hydrate();\n }, [user?.id, activeTenant?.id, setScope, hydrate]);\n\n // Hydrate appearance store from public params (ui_theme, ui_accent, etc).\n // Public params expose tenant-default UI settings before any user override.\n useEffect(() => {\n if (!publicParams?.parameters) return;\n const map: Record<string, unknown> = {};\n for (const p of publicParams.parameters) map[p.code] = p.value;\n hydrateAppearance(map);\n }, [publicParams, hydrateAppearance]);\n\n // Derive area from current route when schema is ready.\n //\n // When the route does NOT map to any navigation area (e.g. /profile,\n // which lives in the top-right user menu and has no sidebar entry),\n // keep the previous activeArea so the sidebar doesn't go blank as\n // the user navigates off the nav graph. On first load, if the default\n // area has no nav entries at all, fall back to the first area that\n // does - this prevents an empty sidebar when the consumer app's\n // navigation YAML doesn't define a \"default\" area.\n useEffect(() => {\n if (!schema) return;\n const area = findAreaForRoute(schema.navigation, schema.screens, location.pathname);\n if (area) {\n setActiveArea(area);\n return;\n }\n // No area matches - keep current if it produces any nav nodes.\n const current = activeArea;\n const currentHasNodes = (schema.navigation ?? []).some(\n (n) => (n.area || DEFAULT_AREA) === current,\n );\n if (currentHasNodes) return;\n // Otherwise fall back to the first area that has nodes.\n const firstArea = (schema.navigation ?? [])[0]?.area || DEFAULT_AREA;\n if (firstArea !== current) setActiveArea(firstArea);\n }, [schema, location.pathname, activeArea]);\n\n // Keyboard shortcuts (T26).\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n const target = document.activeElement as HTMLElement | null;\n const typing =\n !!target &&\n (target.tagName === \"INPUT\" ||\n target.tagName === \"TEXTAREA\" ||\n target.isContentEditable);\n const mod = e.metaKey || e.ctrlKey;\n\n if (mod && e.key.toLowerCase() === \"k\") {\n e.preventDefault();\n openPalette(\"default\");\n } else if (mod && e.key.toLowerCase() === \"t\") {\n e.preventDefault();\n openPalette(\"new\");\n } else if (mod && e.key.toLowerCase() === \"b\") {\n e.preventDefault();\n setAppearance(\"sidebar\", sidebarMode === \"collapsed\" ? \"wide\" : \"collapsed\");\n } else if (mod && e.key.toLowerCase() === \"w\") {\n e.preventDefault();\n requestTabClose(\"current\");\n } else if (mod && e.key === \"/\") {\n e.preventDefault();\n if (panelEnabled) setAiOpen((v) => !v);\n } else if (e.key === \"/\" && !typing) {\n e.preventDefault();\n openPalette(\"default\");\n } else if (e.key === \"Escape\") {\n setCmdOpen(false);\n }\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [openPalette, requestTabClose, setAppearance, sidebarMode, panelEnabled]);\n\n const breadcrumbs = useMemo<Crumb[]>(() => {\n if (!schema) return [];\n return buildCrumbs(schema.navigation, schema.screens, location.pathname, tabs, activeTabId, navigate);\n }, [schema, location.pathname, tabs, activeTabId, navigate]);\n\n if (isLoading) {\n return (\n <div\n style={{\n display: \"flex\",\n height: \"100vh\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"var(--bg)\",\n color: \"var(--text-3)\",\n }}\n >\n Loading…\n </div>\n );\n }\n\n if (!isAuthenticated) return null;\n\n const pickArea = (id: string) => {\n setActiveArea(id);\n // The rail is a workspace switcher: picking an area MUST navigate, else\n // the useEffect above immediately derives activeArea from the unchanged\n // URL and reverts our selection (the \"top rail icons do nothing\" bug).\n // Destination priority: (1) area.home_screen, (2) first resolvable nav\n // leaf in the area. filterAreas on the server already drops areas that\n // have neither, so reaching this function without a target means the\n // schema state changed mid-render — we no-op silently.\n const target = resolveAreaTarget(id, schema);\n if (target) navigate(target);\n };\n\n const handleCommand = (a: CommandAction) => {\n switch (a.type) {\n case \"nav\":\n if (a.route) navigate(a.route);\n break;\n case \"new\":\n if (a.route) navigate(a.route + \"/new\");\n break;\n case \"open\":\n if (a.route) navigate(a.route);\n break;\n case \"ai\":\n case \"ai-panel\":\n if (panelEnabled) setAiOpen(true);\n break;\n case \"theme\":\n setAppearance(\"theme\", theme === \"dark\" ? \"light\" : \"dark\");\n break;\n case \"tab-close\":\n if (a.tabCloseScope) requestTabClose(a.tabCloseScope);\n break;\n }\n };\n\n // Embed mode: shellless render. `?embed=1` shows the same screen route\n // WITHOUT Declarion shell chrome, for white-label iframe embedding. The\n // branch lives INSIDE Layout (not in app.tsx) because Layout owns the\n // screen-runtime providers - chiefly ActionRunnerProvider. Embed mode\n // renders ONLY ActionRunnerProvider + a single screen content root with\n // <Outlet/>, and NONE of: TopBar, Rail, Sidebar, TabStrip, CommandPalette,\n // AgentChatPanel, ImpersonationBanner, ImpersonationFrame. useSSE() above\n // keeps running. Toaster is action feedback and stays in AppShell.\n //\n // Four embed-only aids mount here:\n // - EmbedLocationReporter mirrors every internal navigation to the host\n // via a `navigated` message (`self` mode only).\n // - EmbedUnsavedGuard restores the dirty-edit confirmation the platform\n // otherwise provides only at tab-close, which embed mode removes. It\n // covers navigation INSIDE the iframe.\n // - EmbedDirtyReporter emits `dirty-changed` to the host so the host can\n // guard navigation IT initiates (its menu, a host `navigate`).\n // - EmbedResizeReporter mirrors the embed content height to the host so\n // it can size the iframe to the rendered screen.\n if (embed.enabled) {\n return (\n <ActionRunnerProvider>\n <EmbedLocationReporter />\n <EmbedUnsavedGuard />\n <EmbedDirtyReporter />\n <EmbedResizeReporter />\n <div id=\"app-embed\" className=\"flex h-screen min-h-0 flex-col overflow-auto\">\n <Outlet />\n </div>\n </ActionRunnerProvider>\n );\n }\n\n return (\n <ActionRunnerProvider>\n <div id=\"app\" data-sidebar={sidebarMode} style={{ height: \"100vh\" }}>\n <ImpersonationBanner />\n <ImpersonationFrame />\n <TopBar\n breadcrumbs={breadcrumbs}\n onCrumbClick={(c) => c.onClick && c.onClick()}\n onCmdK={() => openPalette(\"default\")}\n onToggleAI={() => panelEnabled && setAiOpen((v) => !v)}\n aiOpen={aiOpen}\n onOpenProfile={() => navigate(\"/profile\")}\n onOpenPrefs={() => navigate(\"/preferences\")}\n />\n <Rail\n activeArea={activeArea}\n onPick={pickArea}\n onToggleAI={() => panelEnabled && setAiOpen((v) => !v)}\n aiOpen={aiOpen}\n onCmdK={() => openPalette(\"default\")}\n areas={schema?.areas}\n branding={publicParams?.branding}\n />\n <Sidebar activeArea={activeArea} />\n <main\n style={{\n gridArea: \"main\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n minWidth: 0,\n }}\n >\n {tabsPosition === \"top\" && <TabStrip onNew={() => openPalette(\"new\")} onCloseRequest={requestTabClose} />}\n <div\n style={{\n flex: 1,\n overflow: \"auto\",\n minHeight: 0,\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <Outlet />\n </div>\n {tabsPosition === \"bottom\" && <TabStrip onNew={() => openPalette(\"new\")} onCloseRequest={requestTabClose} />}\n </main>\n {paletteEnabled && (\n <CommandPalette\n open={cmdOpen}\n mode={cmdMode}\n onClose={() => setCmdOpen(false)}\n onAction={(a) => handleCommand(a)}\n navigation={schema?.navigation}\n screens={schema?.screens}\n includeAI={paletteAI}\n aiName={aiName}\n />\n )}\n {panelEnabled && aiOpen && <AgentChatPanel open={aiOpen} onClose={() => setAiOpen(false)} title={aiName} />}\n <DirtyTabsCloseDialog\n pending={pendingTabClose}\n onCancel={cancelPendingTabClose}\n onCloseClean={closePendingCleanTabs}\n onDiscardAll={discardPendingTabs}\n />\n </div>\n </ActionRunnerProvider>\n );\n}\n\n// resolveAreaTarget returns the route to navigate to when the rail picks an\n// area. Checks home_screen first, then walks the area's navigation subtree\n// for the first nav leaf whose screen is present in the (permission-filtered)\n// schema. Returns undefined when no target is resolvable.\nfunction resolveAreaTarget(areaCode: string, schema: ReturnType<typeof useSchema>[\"data\"]): string | undefined {\n if (!schema) return undefined;\n const area = schema.areas?.find((a) => a.code === areaCode);\n if (area?.home_screen) {\n const home = schema.screens[area.home_screen];\n if (home?.route) return home.route;\n }\n const walk = (node: NavNode): string | undefined => {\n if (node.screen) {\n const s = schema.screens[node.screen];\n if (s?.route) return s.route;\n }\n for (const c of node.items || []) {\n const r = walk(c);\n if (r) return r;\n }\n for (const c of node.children || []) {\n const r = walk(c);\n if (r) return r;\n }\n return undefined;\n };\n for (const top of schema.navigation ?? []) {\n if ((top.area || DEFAULT_AREA) !== areaCode) continue;\n const r = walk(top);\n if (r) return r;\n }\n return undefined;\n}\n\nfunction findAreaForRoute(\n nodes: NavNode[],\n screens: Record<string, Screen>,\n pathname: string,\n): string | undefined {\n // Recursive walk over both `items` (sections) and `children`\n // (leaf-with-kids per Stage 3 plan T10). Tracks the inherited top-level\n // area so nested matches return the correct rail destination.\n const matches = (n: NavNode): boolean => {\n if (!n.screen) return false;\n const s = screens[n.screen];\n return !!s && (s.route === pathname || pathname.startsWith(s.route + \"/\"));\n };\n const walk = (n: NavNode, area: string): string | undefined => {\n if (matches(n)) return area;\n for (const c of n.items || []) {\n const got = walk(c, area);\n if (got) return got;\n }\n for (const c of n.children || []) {\n const got = walk(c, area);\n if (got) return got;\n }\n return undefined;\n };\n for (const top of nodes ?? []) {\n const area = top.area || DEFAULT_AREA;\n const got = walk(top, area);\n if (got) return got;\n }\n return undefined;\n}\n\nfunction buildCrumbs(\n nodes: NavNode[],\n screens: Record<string, Screen>,\n pathname: string,\n tabs: ReturnType<typeof useTabStore.getState>[\"tabs\"],\n activeTabId: string | null,\n navigate: ReturnType<typeof useNavigate>,\n): Crumb[] {\n // Walk both `items` and `children`; return the first ancestor chain that\n // contains a screen matching the pathname.\n const search = (n: NavNode, ancestors: NavNode[]): NavNode[] | null => {\n if (n.screen) {\n const s = screens[n.screen];\n if (s && (s.route === pathname || pathname.startsWith(s.route + \"/\"))) {\n return [...ancestors, n];\n }\n }\n for (const c of n.items || []) {\n const got = search(c, [...ancestors, n]);\n if (got) return got;\n }\n for (const c of n.children || []) {\n const got = search(c, [...ancestors, n]);\n if (got) return got;\n }\n return null;\n };\n\n let chain: NavNode[] | null = null;\n for (const top of nodes ?? []) {\n chain = search(top, []);\n if (chain) break;\n }\n if (!chain || chain.length === 0) return [];\n\n const crumbs: Crumb[] = [];\n for (let i = 0; i < chain.length; i++) {\n const n = chain[i];\n const isLast = i === chain.length - 1;\n const s = n.screen ? screens[n.screen] : undefined;\n crumbs.push({\n label: loc(n.name) || n.screen || \"\",\n icon: n.icon,\n onClick: s ? () => navigate(s.route) : undefined,\n isLast: isLast && (!s || s.route === pathname),\n });\n }\n // Detail subroute under a list (e.g. /leads/<id>) — append the active\n // tab title as the leaf crumb.\n const last = chain[chain.length - 1];\n const lastScreen = last.screen ? screens[last.screen] : undefined;\n if (lastScreen && pathname !== lastScreen.route && pathname.startsWith(lastScreen.route + \"/\")) {\n const active = tabs.find((t) => t.id === activeTabId);\n crumbs.push({\n label: active?.title || \"Record\",\n icon: \"file\",\n isLast: true,\n });\n }\n return crumbs;\n}\n","// AuthShell: pre-login surface (split-panel: brand on the left, form on the\n// right; topbar with logo + workspace pill + theme toggle; footer with\n// copyright and consumer-supplied legal links).\n//\n// Branding contract: every visible surface is sourced from the consumer's\n// `branding:` YAML via /api/params/public, runtime-computed (e.g. current\n// year), or HIDDEN. The SDK ships no Declarion-flavored fallbacks:\n// missing wordmark → no name; missing tagline → no tagline section;\n// missing legal_links → no footer anchors; etc. Consumer apps (CRM,\n// Swiftward) and the platform's own deploy both supply their own brand\n// identity through the same channel.\n//\n// Pre-login theming: usePublicParams hydrates ui_theme/ui_accent before\n// the auth surface renders so the consumer's theme is already applied.\nimport { useEffect, useMemo, type ReactNode } from \"react\";\nimport { BrandLogo } from \"@/components/primitives/BrandLogo\";\nimport { IconBtn } from \"@/components/primitives/IconBtn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { usePublicParams } from \"@/hooks/useParams\";\nimport type { LocalizedString } from \"@/types/schema\";\nimport type { PublicParam } from \"@/types/api\";\n\nfunction pickLocalized(s: LocalizedString | undefined): string | undefined {\n if (!s) return undefined;\n if (s.en) return s.en;\n for (const k of Object.keys(s)) {\n const v = s[k];\n if (v) return v;\n }\n return undefined;\n}\n\nfunction paramsToRecord(list: PublicParam[] | undefined): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (!list) return out;\n for (const p of list) out[p.code] = p.value;\n return out;\n}\n\n// hostFromUrl returns the bare host portion of a URL (\"status.example.com\"\n// from \"https://status.example.com/path\"). Falls back to the raw input on\n// parse failure so a misconfigured non-URL string still renders something\n// recognizable rather than blowing up the auth shell.\nfunction hostFromUrl(s: string): string {\n try {\n return new URL(s).host;\n } catch {\n return s;\n }\n}\n\nexport type AuthShellProps = {\n children: ReactNode;\n /**\n * Tenant slug used to compose the workspace pill. The pill renders\n * `<tenantSlug>.<branding.workspace_domain>` only when both pieces\n * are set; otherwise it is hidden. Callers are not required to\n * provide a slug — the auth shell fails closed (no pill) rather\n * than display a placeholder host.\n *\n * Currently unwired in v1: no SDK caller passes this prop because\n * frontend tenant-from-host resolution does not exist yet (see\n * `docs/gotchas.md` \"Tenant Resolution\" — the deferred multi-tenant-\n * via-host work). When that lands, callers will source the slug from\n * `window.location.hostname` (or an equivalent middleware-supplied\n * context) and thread it through. Until then the pill stays hidden;\n * the prop is kept so the contract is in place for the follow-up.\n */\n tenantSlug?: string;\n tagline?: string;\n subTagline?: string;\n version?: string;\n badges?: Array<[string, keyof typeof Icon]>;\n /** Optional override for the wordmark / app name shown in topbar + brand panel. */\n brandName?: string;\n};\n\nexport function AuthShell({\n children,\n tenantSlug,\n tagline,\n subTagline,\n version,\n badges,\n brandName,\n}: AuthShellProps) {\n const theme = useAppearanceStore((s) => s.theme);\n const setAppearance = useAppearanceStore((s) => s.set);\n const hydrateFromParams = useAppearanceStore((s) => s.hydrateFromParams);\n const toggleTheme = () => setAppearance(\"theme\", theme === \"dark\" ? \"light\" : \"dark\");\n\n // Pre-login theming: fetch public params and apply ui_theme / ui_accent\n // before render. usePublicParams is React-Query backed; the appearance\n // store's hydrateFromParams is idempotent and only writes deltas.\n const { data: publicParams } = usePublicParams();\n useEffect(() => {\n if (!publicParams) return;\n hydrateFromParams(paramsToRecord(publicParams.parameters));\n }, [publicParams, hydrateFromParams]);\n\n const branding = publicParams?.branding;\n // Every surface either resolves from branding or stays empty. No SDK\n // fallbacks: consumer apps must opt in via YAML or the surface hides.\n const resolvedBrandName = brandName ?? branding?.wordmark ?? branding?.app_name ?? \"\";\n const resolvedTagline = tagline ?? pickLocalized(branding?.auth_tagline) ?? \"\";\n const resolvedSubTagline = subTagline ?? pickLocalized(branding?.auth_subtagline) ?? \"\";\n const resolvedVersion = version ?? branding?.version_label ?? \"\";\n const resolvedBadges = useMemo<Array<[string, keyof typeof Icon]>>(() => {\n if (badges && badges.length) return badges;\n const fromBranding = (branding?.auth_badges ?? [])\n .map((b) => pickLocalized(b))\n .filter((s): s is string => Boolean(s));\n if (!fromBranding.length) return [];\n // Auth badges from schema have no icon hint — fall back to shield.\n // Cap at 8 so a 2-column grid stays readable (4 rows max). YAML\n // can supply more; surplus is dropped by design rather than\n // overflowing the brand panel.\n return fromBranding.slice(0, 8).map((label) => [label, \"shield\" as keyof typeof Icon]);\n }, [badges, branding?.auth_badges]);\n\n const workspaceDomain = branding?.workspace_domain ?? \"\";\n const showWorkspacePill = Boolean(tenantSlug && workspaceDomain);\n\n const statusUrl = branding?.status_url ?? \"\";\n const showStatusRow = Boolean(statusUrl);\n\n const legalLinks = branding?.legal_links;\n const footerLinks = useMemo<Array<[string, string]>>(() => {\n const candidates: Array<[string, string]> = [\n [\"Security\", legalLinks?.security_url ?? \"\"],\n [\"Privacy\", legalLinks?.privacy_url ?? \"\"],\n [\"Terms\", legalLinks?.terms_url ?? \"\"],\n [\"Docs\", legalLinks?.docs_url ?? \"\"],\n ];\n return candidates.filter(([, url]) => Boolean(url));\n }, [legalLinks?.security_url, legalLinks?.privacy_url, legalLinks?.terms_url, legalLinks?.docs_url]);\n\n const legalEntity = branding?.legal_entity ?? \"\";\n const copyrightLine = `© ${new Date().getFullYear()}${legalEntity ? ` ${legalEntity}` : \"\"}`;\n\n // Deploy-time identity pill (region · build). Each piece is opt-in:\n // region present only when DECLARION_REGION is set; version/revision\n // only when the binary was ldflags-stamped (production build). The\n // pill itself hides when neither piece resolves.\n //\n // Build label format:\n // - release build: `<tag>+<short-revision>` e.g. \"1.4.2+a9b2c0d\"\n // - dev build: VERSION is already the short SHA (Makefile uses\n // `git rev-parse --short HEAD` when no tag), so version === short-rev\n // → render only `<short-rev>` (no `+`-suffix duplication).\n const meta = publicParams?.meta;\n const shortRev = meta?.revision ? meta.revision.slice(0, 7) : \"\";\n const buildLabel = meta?.version\n ? shortRev && shortRev !== meta.version\n ? `${meta.version}+${shortRev}`\n : meta.version\n : \"\";\n const metaPillSegments = [\n meta?.region ? `Region: ${meta.region}` : \"\",\n buildLabel ? `Build ${buildLabel}` : \"\",\n ].filter(Boolean);\n const metaPill = metaPillSegments.join(\" · \");\n\n return (\n <div style={{ minHeight: \"100vh\", background: \"var(--bg)\", display: \"flex\", flexDirection: \"column\" }}>\n <div style={{ padding: \"16px 20px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <BrandLogo appLogo={branding?.app_logo} size={26} />\n\n {resolvedBrandName && (\n <span style={{ fontSize: 14, fontWeight: 600, letterSpacing: \"-0.01em\" }}>{resolvedBrandName}</span>\n )}\n {showWorkspacePill && (\n <span className=\"mono\" style={{ marginLeft: 6, fontSize: 10.5, color: \"var(--text-4)\", padding: \"2px 6px\", background: \"var(--surface-2)\", border: \"1px solid var(--border)\", borderRadius: 4 }}>{tenantSlug}.{workspaceDomain}</span>\n )}\n <div style={{ flex: 1 }} />\n <IconBtn\n icon={theme === \"dark\" ? <Icon.sun size={14} /> : <Icon.moon size={14} />}\n onClick={toggleTheme}\n title=\"Toggle theme\"\n />\n </div>\n\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", padding: 20 }}>\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 0, width: 980, maxWidth: \"100%\", minHeight: 560, background: \"var(--surface)\", border: \"1px solid var(--border)\", borderRadius: 14, overflow: \"hidden\", boxShadow: \"var(--shadow-lg)\" }}>\n\n {/* Left — brand panel. Holds its own colorway across light + dark\n themes; the dark gradient + light foreground is the design\n intent, not a theme leak. All colors lifted to --auth-*\n CSS variables in globals.css so a future re-brand or\n contrast-token requirement edits ONE place. */}\n <div style={{ padding: \"36px 36px 28px\", background: \"linear-gradient(155deg, var(--auth-panel-bg-start) 0%, var(--auth-panel-bg-mid) 40%, var(--auth-panel-bg-end) 100%)\", color: \"var(--auth-panel-fg)\", display: \"flex\", flexDirection: \"column\", position: \"relative\", overflow: \"hidden\" }}>\n <div style={{ position: \"absolute\", top: -80, right: -80, width: 280, height: 280, borderRadius: \"50%\", background: \"radial-gradient(circle, var(--auth-panel-glow-1) 0%, transparent 70%)\" }} />\n <div style={{ position: \"absolute\", bottom: -60, left: -40, width: 220, height: 220, borderRadius: \"50%\", background: \"radial-gradient(circle, var(--auth-panel-glow-2) 0%, transparent 70%)\" }} />\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, position: \"relative\" }}>\n <BrandLogo appLogo={branding?.app_logo} size={34} />\n\n {resolvedBrandName && (\n <span style={{ fontSize: 18, fontWeight: 600, letterSpacing: \"-0.012em\" }}>{resolvedBrandName}</span>\n )}\n {resolvedVersion && (\n <span className=\"mono\" style={{ fontSize: 10, padding: \"2px 6px\", border: \"1px solid var(--auth-panel-border-faint)\", borderRadius: 4, color: \"var(--auth-panel-fg-muted)\" }}>{resolvedVersion}</span>\n )}\n </div>\n {/* Hero block: renders the consumer's logo at hero size plus\n the wordmark text. Hidden entirely when neither piece is\n configured — same no-fallback rule as the rest of the\n shell. Consumers override `app_logo` and/or `wordmark`\n in their branding YAML. */}\n {(branding?.app_logo || resolvedBrandName) && (\n <div style={{ marginTop: 48, position: \"relative\", display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap: 16 }}>\n {branding?.app_logo && (\n <BrandLogo appLogo={branding.app_logo} size={96} radius={0} />\n )}\n {resolvedBrandName && (\n <span style={{ fontSize: 28, fontWeight: 600, letterSpacing: \"-0.018em\", color: \"var(--auth-panel-fg)\" }}>{resolvedBrandName}</span>\n )}\n </div>\n )}\n <div style={{ marginTop: \"auto\", position: \"relative\" }}>\n {resolvedTagline && (\n <h1 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: \"-0.022em\", lineHeight: 1.2 }}>{resolvedTagline}</h1>\n )}\n {resolvedSubTagline && (\n <p style={{ margin: \"10px 0 0\", fontSize: 13, color: \"var(--auth-panel-fg-muted)\", lineHeight: 1.65 }}>{resolvedSubTagline}</p>\n )}\n {resolvedBadges.length > 0 && (\n <div style={{ marginTop: 22, display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 10, fontSize: 11.5, color: \"var(--auth-panel-fg-dim)\" }}>\n {resolvedBadges.map(([label, iconName]) => {\n const I = Icon[iconName];\n return (\n <div key={label} style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n {I && <I size={12} style={{ color: \"var(--accent-2)\" }} />}\n <span>{label}</span>\n </div>\n );\n })}\n </div>\n )}\n {showStatusRow && (\n <a\n href={statusUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mono\"\n style={{ marginTop: 20, padding: \"10px 12px\", background: \"var(--auth-panel-surface-soft)\", border: \"1px solid var(--auth-panel-border-soft)\", borderRadius: 8, display: \"flex\", alignItems: \"center\", gap: 8, fontSize: 11.5, color: \"var(--auth-panel-fg-muted)\", textDecoration: \"none\" }}\n >\n {hostFromUrl(statusUrl)}\n </a>\n )}\n </div>\n </div>\n\n {/* Right — form */}\n <div style={{ padding: \"36px 40px\", display: \"flex\", flexDirection: \"column\", justifyContent: \"center\" }}>\n {children}\n </div>\n </div>\n </div>\n\n <div style={{ padding: \"14px 20px\", display: \"flex\", alignItems: \"center\", gap: 16, fontSize: 11, color: \"var(--text-4)\" }}>\n <span>{copyrightLine}</span>\n {footerLinks.map(([label, url]) => (\n <a key={label} href={url} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: \"inherit\" }}>{label}</a>\n ))}\n {metaPill && (\n <>\n <div style={{ flex: 1 }} />\n <span className=\"mono\">{metaPill}</span>\n </>\n )}\n </div>\n </div>\n );\n}\n","// Vendor SSO logos inlined for the auth shell. Ported from\n// docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx (SignInForm).\n\nexport function GoogleIcon({ size = 14 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" aria-hidden>\n <path fill=\"#4285F4\" d=\"M22.5 12.27c0-.79-.07-1.55-.2-2.27h-9.8v4.5h5.6c-.24 1.28-.97 2.37-2.07 3.1v2.57h3.35c1.96-1.8 3.12-4.47 3.12-7.9z\"/>\n <path fill=\"#34A853\" d=\"M12.5 22c2.8 0 5.15-.92 6.87-2.5l-3.35-2.57c-.93.62-2.12.99-3.52.99-2.7 0-4.99-1.82-5.81-4.27h-3.46v2.66C4.95 19.6 8.42 22 12.5 22z\"/>\n <path fill=\"#FBBC05\" d=\"M6.69 13.65a6 6 0 010-3.8V7.18H3.23a10 10 0 000 9.13z\"/>\n <path fill=\"#EA4335\" d=\"M12.5 6.02c1.52 0 2.88.52 3.95 1.54l2.97-2.97C17.65 2.98 15.3 2 12.5 2c-4.08 0-7.55 2.4-9.27 5.82l3.46 2.66C7.51 7.84 9.8 6.02 12.5 6.02z\"/>\n </svg>\n );\n}\n\nexport function MicrosoftIcon({ size = 14 }: { size?: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" aria-hidden>\n <path fill=\"#F25022\" d=\"M1 1h10v10H1z\"/>\n <path fill=\"#7FBA00\" d=\"M13 1h10v10H13z\"/>\n <path fill=\"#00A4EF\" d=\"M1 13h10v10H1z\"/>\n <path fill=\"#FFB900\" d=\"M13 13h10v10H13z\"/>\n </svg>\n );\n}\n","import type { NavNode, Schema, Screen } from \"@/types/schema\";\n\nconst STATIC_APP_ROUTES = new Set([\"/preferences\", \"/admin/catalog\"]);\n\nexport function schemaDefaultRoute(schema: Schema | undefined): string | null {\n\tif (!schema?.navigation) return null;\n\tconst areas = schema.areas ?? [];\n\tif (areas.length > 0) {\n\t\tfor (const area of areas) {\n\t\t\tfor (const node of schema.navigation) {\n\t\t\t\tconst nodeArea = node.area || \"default\";\n\t\t\t\tif (nodeArea !== area.code) continue;\n\t\t\t\tconst route = firstRenderableRoute(schema, node);\n\t\t\t\tif (route) return route;\n\t\t\t}\n\t\t}\n\t}\n\tfor (const node of schema.navigation) {\n\t\tconst route = firstRenderableRoute(schema, node);\n\t\tif (route) return route;\n\t}\n\treturn null;\n}\n\nexport function accessibleRedirectPath(\n\trequestedPath: string,\n\tschema: Schema | undefined,\n): string {\n\tconst fallback = schemaDefaultRoute(schema) || \"/\";\n\tif (!schema) return fallback;\n\tif (requestedPath === \"/\" || requestedPath === \"/admin\") return fallback;\n\tconst pathname = pathOnly(requestedPath);\n\tif (STATIC_APP_ROUTES.has(pathname)) return requestedPath;\n\tfor (const screen of Object.values(schema.screens ?? {})) {\n\t\tif (!screen.route || !isRenderableRoute(schema, screen)) continue;\n\t\tif (routeToRegex(screen.route).test(pathname)) return requestedPath;\n\t}\n\treturn fallback;\n}\n\nfunction pathOnly(path: string): string {\n\tconst hashStart = path.indexOf(\"#\");\n\tconst withoutHash = hashStart >= 0 ? path.slice(0, hashStart) : path;\n\tconst queryStart = withoutHash.indexOf(\"?\");\n\treturn (\n\t\t(queryStart >= 0 ? withoutHash.slice(0, queryStart) : withoutHash).replace(\n\t\t\t/\\/+$/,\n\t\t\t\"\",\n\t\t) || \"/\"\n\t);\n}\n\nfunction isRenderableScreen(schema: Schema, screenCode: string): boolean {\n\tconst screen = schema.screens[screenCode];\n\treturn !!screen && isRenderableRoute(schema, screen);\n}\n\nfunction firstRenderableRoute(schema: Schema, node: NavNode): string | null {\n\tif (node.screen && isRenderableScreen(schema, node.screen)) {\n\t\treturn schema.screens[node.screen].route;\n\t}\n\tfor (const child of [...(node.items ?? []), ...(node.children ?? [])]) {\n\t\tconst route = firstRenderableRoute(schema, child);\n\t\tif (route) return route;\n\t}\n\treturn null;\n}\n\nfunction isRenderableRoute(schema: Schema, screen: Screen): boolean {\n\tif (screen.type !== \"list\") return true;\n\tconst entityCode = (screen as { entity?: string }).entity;\n\tconst entity = entityCode ? schema.entities?.[entityCode] : undefined;\n\treturn !entity?.singleton;\n}\n\nfunction routeToRegex(route: string): RegExp {\n\tconst segs = route\n\t\t.replace(/\\/+$/, \"\")\n\t\t.split(\"/\")\n\t\t.map((segment) =>\n\t\t\tsegment.startsWith(\":\")\n\t\t\t\t? \"[^/]+\"\n\t\t\t\t: segment.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n\t\t);\n\treturn new RegExp(\"^\" + segs.join(\"/\") + \"$\");\n}\n","import { loc, type LocalizedString } from \"@/types/schema\";\nimport type { PublicParam } from \"@/types/api\";\n\nconst DEFAULT_LOCALE = \"en\";\n\nexport type MessageCatalog = Record<string, LocalizedString> | undefined;\n\nexport function localizedMessage(\n messages: MessageCatalog,\n code: string,\n locale: string,\n fallback = \"\",\n): string {\n return loc(messages?.[code], locale) || fallback;\n}\n\nexport function publicLocale(parameters: PublicParam[] | undefined): string {\n const configured = parameters?.find((param) => param.code === \"ui_language\")?.value;\n if (typeof configured === \"string\" && configured.trim()) {\n return configured.trim();\n }\n const browser = navigatorPrimarySubtag();\n return browser || DEFAULT_LOCALE;\n}\n\nfunction navigatorPrimarySubtag(): string | undefined {\n if (typeof navigator === \"undefined\") return undefined;\n const raw = navigator.language;\n if (!raw) return undefined;\n return raw.split(\"-\")[0] || undefined;\n}\n","// Ported from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx SignInForm.\n// Wired to the real auth API: SSO providers (dynamic), email+password via useAuth.\nimport { useEffect, useState, type FormEvent, type ReactNode } from \"react\";\nimport { Link, useNavigate, useSearchParams } from \"react-router-dom\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\nimport { AuthShell } from \"./AuthShell\";\nimport { GoogleIcon, MicrosoftIcon } from \"./icons\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { FieldLabel, TextInput, PasswordInput } from \"@/components/primitives/Fields\";\nimport { useAuth } from \"@/hooks/useAuth\";\nimport { usePublicParams } from \"@/hooks/useParams\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { checkSetup, listSSOProviders, ssoAuthorizeUrl } from \"@/api/auth\";\nimport { sanitizeRedirectPath } from \"@/api/auth-redirect\";\nimport { accessibleRedirectPath } from \"@/lib/schema-routing\";\nimport { localizedMessage, publicLocale, type MessageCatalog } from \"@/lib/messages\";\nimport { ApiError, type SSOProvider } from \"@/types/api\";\n\nconst INVALID_CREDENTIALS_FALLBACK = \"Invalid email or password.\";\nconst INACCESSIBLE_REDIRECT_FALLBACK = \"You do not have access to that page. We opened your default page instead.\";\n\nconst VENDOR: Record<string, { label: string; icon: ReactNode }> = {\n google: { label: \"Continue with Google\", icon: <GoogleIcon size={14} /> },\n microsoft: { label: \"Continue with Microsoft\", icon: <MicrosoftIcon size={14} /> },\n azure: { label: \"Continue with Microsoft\", icon: <MicrosoftIcon size={14} /> },\n};\n\nexport function SignIn() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { login, isLoginPending, isAuthenticated } = useAuth();\n const { data: publicParams } = usePublicParams();\n const { data: schema } = useSchema();\n const [email, setEmail] = useState(\"\");\n const [pass, setPass] = useState(\"\");\n // Remember-me selects the session longevity policy on auth.login. Default\n // true matches the legacy behavior users have been relying on (refresh\n // cookie persists across browser close). Unchecking turns the refresh\n // cookie into a browser-session cookie that dies on close (the\n // shared / public computer path; Threat T4 in the auth-longevity plan).\n const [remember, setRemember] = useState(true);\n\n const nextPath = sanitizeRedirectPath(searchParams.get(\"next\") || searchParams.get(\"redirect\"));\n const ssoError = searchParams.get(\"sso_error\");\n const locale = publicLocale(publicParams?.parameters);\n\n const { data: setupStatus } = useQuery({ queryKey: [\"check-setup\"], queryFn: checkSetup });\n const { data: ssoProviders } = useQuery<SSOProvider[]>({\n queryKey: [\"sso-providers\"],\n queryFn: () => listSSOProviders(),\n staleTime: 60_000,\n });\n\n useEffect(() => { if (setupStatus?.needs_setup) navigate(\"/signup\", { replace: true }); }, [setupStatus, navigate]);\n useEffect(() => {\n if (!isAuthenticated || !schema) return;\n const target = accessibleRedirectPath(nextPath, schema);\n if (shouldWarnRedirectFallback(nextPath, target)) {\n toast.warning(localizedMessage(\n schema.messages,\n \"auth.inaccessible_redirect\",\n locale,\n INACCESSIBLE_REDIRECT_FALLBACK,\n ));\n }\n navigate(target, { replace: true });\n }, [isAuthenticated, locale, navigate, nextPath, schema]);\n useEffect(() => { if (ssoError) toast.error(ssoError); }, [ssoError]);\n\n function onSubmit(e: FormEvent) {\n e.preventDefault();\n login(\n { email, password: pass, remember },\n {\n onError: (err) => {\n toast.error(loginErrorMessage(err, publicParams?.messages, locale));\n },\n },\n );\n }\n\n return (\n <AuthShell>\n <form onSubmit={onSubmit}>\n <div style={{ fontSize: 10.5, fontWeight: 700, color: \"var(--accent)\", letterSpacing: \"0.1em\", textTransform: \"uppercase\" }}>Welcome back</div>\n <h2 style={{ margin: \"6px 0 4px\", fontSize: 24, fontWeight: 600, letterSpacing: \"-0.018em\" }}>Sign in</h2>\n <p style={{ margin: \"0 0 20px\", fontSize: 13, color: \"var(--text-3)\" }}>Use your corporate SSO or email & password.</p>\n\n {ssoProviders && ssoProviders.length > 0 && (\n <>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, marginBottom: 12 }}>\n {ssoProviders.map((p) => {\n const v = VENDOR[p.slug.toLowerCase()] ?? { label: `Continue with ${p.name}`, icon: <Icon.shield size={13} /> };\n return (\n <Btn\n key={p.slug}\n size=\"md\"\n style={{ justifyContent: \"center\", width: \"100%\" }}\n icon={v.icon}\n onClick={(e) => { e.preventDefault(); window.location.href = ssoAuthorizeUrl(p.slug, undefined, nextPath); }}\n >\n {v.label}\n </Btn>\n );\n })}\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, margin: \"16px 0\" }}>\n <span style={{ flex: 1, height: 1, background: \"var(--divider)\" }} />\n <span style={{ fontSize: 10.5, color: \"var(--text-4)\", letterSpacing: \"0.08em\", textTransform: \"uppercase\" }}>or with email</span>\n <span style={{ flex: 1, height: 1, background: \"var(--divider)\" }} />\n </div>\n </>\n )}\n\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div>\n <FieldLabel>Email</FieldLabel>\n <TextInput value={email} onChange={setEmail} prefix={<Icon.email size={13} />} placeholder=\"you@company.com\" autoFocus required />\n </div>\n <div>\n <div style={{ display: \"flex\", alignItems: \"center\" }}>\n <FieldLabel>Password</FieldLabel>\n <div style={{ flex: 1 }} />\n <Link to=\"/forgot\" style={{ fontSize: 11, color: \"var(--accent)\", marginBottom: 4, textDecoration: \"none\" }}>Forgot?</Link>\n </div>\n <PasswordInput value={pass} onChange={setPass} placeholder=\"Your password\" />\n </div>\n <label style={{ display: \"flex\", alignItems: \"center\", gap: 6, fontSize: 12, color: \"var(--text-2)\" }}>\n <input\n type=\"checkbox\"\n name=\"remember\"\n checked={remember}\n onChange={(e) => setRemember(e.target.checked)}\n /> Keep me signed in on this device\n </label>\n <Btn\n type=\"submit\"\n size=\"md\"\n kind=\"primary\"\n style={{ justifyContent: \"center\", width: \"100%\" }}\n disabled={isLoginPending}\n >\n {isLoginPending ? \"Signing in…\" : \"Continue\"}\n </Btn>\n <div style={{ fontSize: 12, color: \"var(--text-3)\", textAlign: \"center\", marginTop: 4 }}>\n New here? <Link to=\"/signup\" style={{ color: \"var(--accent)\", fontWeight: 500, textDecoration: \"none\" }}>Request access</Link>\n </div>\n </div>\n </form>\n </AuthShell>\n );\n}\n\nfunction loginErrorMessage(err: Error, messages: MessageCatalog, locale: string): string {\n if (err instanceof ApiError && err.code === \"INVALID_CREDENTIALS\") {\n return localizedMessage(messages, \"auth.invalid_credentials\", locale, INVALID_CREDENTIALS_FALLBACK);\n }\n return err.message;\n}\n\nfunction shouldWarnRedirectFallback(requestedPath: string, targetPath: string): boolean {\n if (requestedPath === targetPath) return false;\n const pathname = requestedPath.split(/[?#]/, 1)[0].replace(/\\/+$/, \"\") || \"/\";\n return pathname !== \"/\" && pathname !== \"/admin\";\n}\n","// SignUp form. Wired to useAuth.signup; only used when the platform\n// reports needs_setup. The workspace-URL suffix (`.<workspace_domain>`)\n// and the agreement links (Terms / Privacy) come from public branding;\n// the surface fails closed when a key is unset rather than render a\n// placeholder host or `href=\"#\"`.\nimport { useEffect, useState, type FormEvent } from \"react\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { AuthShell } from \"./AuthShell\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { FieldLabel, TextInput, PasswordInput } from \"@/components/primitives/Fields\";\nimport { useAuth } from \"@/hooks/useAuth\";\nimport { usePublicParams } from \"@/hooks/useParams\";\nimport { checkSetup } from \"@/api/auth\";\n\nexport function SignUp() {\n const navigate = useNavigate();\n const { signup, isSignupPending, isAuthenticated } = useAuth();\n const [first, setFirst] = useState(\"\");\n const [last, setLast] = useState(\"\");\n const [email, setEmail] = useState(\"\");\n const [company, setCompany] = useState(\"\");\n const [workspace, setWorkspace] = useState(\"\");\n const [pass, setPass] = useState(\"\");\n const [agree, setAgree] = useState(false);\n\n const { data: publicParams } = usePublicParams();\n const branding = publicParams?.branding;\n const workspaceDomain = branding?.workspace_domain ?? \"\";\n const termsUrl = branding?.legal_links?.terms_url ?? \"\";\n const privacyUrl = branding?.legal_links?.privacy_url ?? \"\";\n\n const { data: setupStatus } = useQuery({ queryKey: [\"check-setup\"], queryFn: checkSetup });\n\n useEffect(() => { if (setupStatus && !setupStatus.needs_setup) navigate(\"/login\", { replace: true }); }, [setupStatus, navigate]);\n useEffect(() => { if (isAuthenticated) navigate(\"/\", { replace: true }); }, [isAuthenticated, navigate]);\n\n function onSubmit(e: FormEvent) {\n e.preventDefault();\n if (!agree) return;\n const displayName = [first, last].filter(Boolean).join(\" \") || email;\n signup({ email, password: pass, displayName });\n }\n\n return (\n <AuthShell>\n <form onSubmit={onSubmit}>\n <div style={{ fontSize: 10.5, fontWeight: 700, color: \"var(--accent)\", letterSpacing: \"0.1em\", textTransform: \"uppercase\" }}>Request access</div>\n <h2 style={{ margin: \"6px 0 4px\", fontSize: 24, fontWeight: 600, letterSpacing: \"-0.018em\" }}>Create your workspace</h2>\n <p style={{ margin: \"0 0 20px\", fontSize: 13, color: \"var(--text-3)\" }}>We'll provision a tenant and invite your team. Takes under a minute.</p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 10 }}>\n <div><FieldLabel>First name</FieldLabel><TextInput value={first} onChange={setFirst} /></div>\n <div><FieldLabel>Last name</FieldLabel><TextInput value={last} onChange={setLast} /></div>\n </div>\n <div>\n <FieldLabel>Work email</FieldLabel>\n <TextInput value={email} onChange={setEmail} prefix={<Icon.email size={13} />} placeholder=\"name@company.com\" required />\n </div>\n <div>\n <FieldLabel>Company</FieldLabel>\n <TextInput value={company} onChange={setCompany} prefix={<Icon.building size={13} />} />\n </div>\n <div>\n <FieldLabel>Workspace URL</FieldLabel>\n <div style={{ display: \"flex\", alignItems: \"stretch\", border: \"1px solid var(--border)\", borderRadius: 6, overflow: \"hidden\" }}>\n <TextInput value={workspace} onChange={setWorkspace} style={{ border: 0, borderRadius: 0 }} />\n {workspaceDomain && (\n <span className=\"mono\" style={{ display: \"flex\", alignItems: \"center\", padding: \"0 10px\", background: \"var(--surface-2)\", borderLeft: \"1px solid var(--border)\", fontSize: 12, color: \"var(--text-3)\" }}>.{workspaceDomain}</span>\n )}\n </div>\n </div>\n <div>\n <FieldLabel>Password</FieldLabel>\n <PasswordInput value={pass} onChange={setPass} placeholder=\"At least 12 characters\" showStrength />\n </div>\n <label style={{ display: \"flex\", alignItems: \"flex-start\", gap: 6, fontSize: 11.5, color: \"var(--text-3)\", lineHeight: 1.5 }}>\n <input type=\"checkbox\" style={{ marginTop: 2 }} checked={agree} onChange={(e) => setAgree(e.target.checked)} />\n <AgreementCopy termsUrl={termsUrl} privacyUrl={privacyUrl} />\n </label>\n <Btn\n type=\"submit\"\n size=\"md\"\n kind=\"primary\"\n style={{ justifyContent: \"center\", width: \"100%\" }}\n disabled={isSignupPending || !agree}\n >\n {isSignupPending ? \"Creating…\" : \"Create workspace\"}\n </Btn>\n <div style={{ fontSize: 12, color: \"var(--text-3)\", textAlign: \"center\" }}>\n Already have an account? <Link to=\"/login\" style={{ color: \"var(--accent)\", fontWeight: 500, textDecoration: \"none\" }}>Sign in</Link>\n </div>\n </div>\n </form>\n </AuthShell>\n );\n}\n\n// AgreementCopy renders the signup agreement text. Each policy link\n// renders only when its URL is configured in branding.legal_links;\n// when no URLs are set, the copy reads as a plain \"I agree to the\n// terms\" sentence with no anchors. Avoids `href=\"#\"` placeholders.\nfunction AgreementCopy({ termsUrl, privacyUrl }: { termsUrl: string; privacyUrl: string }) {\n const termsAnchor = termsUrl ? (\n <a href={termsUrl} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: \"var(--accent)\" }}>Terms</a>\n ) : <span>Terms</span>;\n const privacyAnchor = privacyUrl ? (\n <a href={privacyUrl} target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: \"var(--accent)\" }}>Privacy</a>\n ) : <span>Privacy</span>;\n return (\n <span>I agree to the {termsAnchor} and {privacyAnchor}.</span>\n );\n}\n","// Forgot password request page. Posts to auth.request_password_reset with the\n// email only — tenant is resolved server-side from the request, the same way\n// auth.login does (frontend stays tenant-agnostic). The success panel renders\n// unconditionally after submit, including on network / server errors, so the\n// UI cannot be used to enumerate registered emails.\nimport { useState, type FormEvent } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport { AuthShell } from \"./AuthShell\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { FieldLabel, TextInput } from \"@/components/primitives/Fields\";\nimport { callHandler } from \"@/api/client\";\n\nexport function Forgot() {\n const [email, setEmail] = useState(\"\");\n const [submitted, setSubmitted] = useState(false);\n const [pending, setPending] = useState(false);\n\n async function onSubmit(e: FormEvent) {\n e.preventDefault();\n if (pending) return;\n setPending(true);\n try {\n // Generic success panel renders no matter what the server says — never\n // surface 4xx/5xx text to the user, never branch on the response.\n await callHandler(\"auth.request_password_reset\", { email });\n } catch {\n // Swallow on purpose: enumeration resistance per plan §1.1.\n } finally {\n setSubmitted(true);\n setPending(false);\n }\n }\n\n return (\n <AuthShell>\n <form onSubmit={onSubmit} data-testid=\"forgot-form\">\n <div className=\"text-accent uppercase\" style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: \"0.1em\" }}>Reset password</div>\n <h2 style={{ margin: \"6px 0 4px\", fontSize: 24, fontWeight: 600, letterSpacing: \"-0.018em\" }}>Forgot your password?</h2>\n <p className=\"text-muted-foreground\" style={{ margin: \"0 0 20px\", fontSize: 13 }}>Enter your email and we'll send a secure reset link. It expires in 30 minutes.</p>\n {!submitted ? (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div>\n <FieldLabel>Email</FieldLabel>\n <TextInput\n value={email}\n onChange={setEmail}\n prefix={<Icon.email size={13} />}\n placeholder=\"you@company.com\"\n autoFocus\n required\n type=\"email\"\n aria-label=\"Email\"\n />\n </div>\n <Btn\n type=\"submit\"\n size=\"md\"\n kind=\"primary\"\n style={{ justifyContent: \"center\", width: \"100%\" }}\n disabled={pending}\n >\n {pending ? \"Sending…\" : \"Send reset link\"}\n </Btn>\n <Link to=\"/login\" className=\"text-accent text-center no-underline\" style={{ fontSize: 12 }}>← Back to sign in</Link>\n </div>\n ) : (\n <div\n data-testid=\"forgot-sent-panel\"\n role=\"status\"\n className=\"bg-success-soft text-success\"\n style={{ padding: 14, borderRadius: 8, fontSize: 12.5, display: \"flex\", gap: 8, alignItems: \"flex-start\" }}\n >\n <Icon.check size={14} />\n <div>If that email is in our system, we've sent a reset link. Check your inbox — it expires in 30 minutes.</div>\n </div>\n )}\n </form>\n </AuthShell>\n );\n}\n","// Reset password consumer page. Reads the reset token from the URL fragment\n// (#t=<token>) — fragments never travel in the Referer header or server\n// access logs, defeating the SafeLinks/Mimecast/Proofpoint pre-fetch class\n// of token-consumption attacks (paired with the email-on-form requirement).\n//\n// This component MUST NOT call any API on mount. Backend GET-side validation\n// would defeat the pre-fetch protection. The token only enters the wire on\n// explicit user submission; if the URL is missing the fragment we render a\n// generic error state and never touch the network.\n//\n// On mount we also (1) scrub the fragment from the URL bar via\n// history.replaceState so a screen-share / shoulder-surf cannot lift the\n// token, and (2) inject a meta name=\"referrer\" content=\"no-referrer\" tag so\n// any outbound link click during the session does not leak the path.\nimport { useEffect, useMemo, useState, type FormEvent } from \"react\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport { AuthShell } from \"./AuthShell\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { FieldLabel, TextInput, PasswordInput } from \"@/components/primitives/Fields\";\nimport { callHandler } from \"@/api/client\";\nimport { ApiError } from \"@/types/api\";\n\n// Mirrors the server-side constant in\n// golang/internal/auth/password_handlers.go (`minPasswordLength = 8`). The\n// SPA bootstrap payload (`/api/info`, `/api/params/public`) does not expose\n// this value today; keeping the constant inline (with the cross-reference)\n// avoids a new round-trip on Reset.tsx mount, which the plan explicitly\n// forbids. Bump in lockstep with the Go constant.\nconst MIN_PASSWORD_LENGTH = 8;\n\nconst FLASH_KEY = \"declarion.auth.flash\";\n\nconst NO_REFERRER_META_ID = \"declarion-reset-no-referrer\";\n\nfunction readTokenFromHash(): string {\n if (typeof window === \"undefined\") return \"\";\n const hash = window.location.hash.startsWith(\"#\")\n ? window.location.hash.slice(1)\n : window.location.hash;\n if (!hash) return \"\";\n const params = new URLSearchParams(hash);\n return params.get(\"t\") ?? \"\";\n}\n\nfunction ensureNoReferrerMeta() {\n if (typeof document === \"undefined\") return;\n let meta = document.getElementById(NO_REFERRER_META_ID) as HTMLMetaElement | null;\n if (meta) return;\n meta = document.createElement(\"meta\");\n meta.id = NO_REFERRER_META_ID;\n meta.name = \"referrer\";\n meta.content = \"no-referrer\";\n document.head.appendChild(meta);\n}\n\nfunction removeNoReferrerMeta() {\n if (typeof document === \"undefined\") return;\n const meta = document.getElementById(NO_REFERRER_META_ID);\n if (meta) meta.remove();\n}\n\nexport function Reset() {\n const navigate = useNavigate();\n // Capture the token synchronously on first render, BEFORE the scrub\n // effect runs. useState's lazy initializer guarantees the read happens\n // exactly once, before paint.\n const [token] = useState<string>(() => readTokenFromHash());\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Token-bearing path was hit. Scrub the fragment from the URL bar and\n // pin the no-referrer policy. Both side-effects MUST run only when a\n // token is present so the missing-fragment branch stays inert (no DOM\n // mutation, no history rewrite — easier to reason about in tests).\n useEffect(() => {\n if (!token) return;\n if (typeof window !== \"undefined\" && typeof window.history?.replaceState === \"function\") {\n window.history.replaceState(null, \"\", \"/reset\");\n }\n ensureNoReferrerMeta();\n // Cleanup on unmount: the no-referrer policy was injected for the\n // sole benefit of this page (token-bearing fragment scrub). Other\n // SPA pages should serve under the platform's default referrer\n // policy, not inherit the reset-page hardening.\n return removeNoReferrerMeta;\n }, [token]);\n\n const tokenMissing = useMemo(() => !token, [token]);\n\n async function onSubmit(e: FormEvent) {\n e.preventDefault();\n if (pending) return;\n if (password !== confirm) {\n setError(\"Passwords do not match.\");\n return;\n }\n if (password.length < MIN_PASSWORD_LENGTH) {\n setError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters.`);\n return;\n }\n setPending(true);\n setError(null);\n try {\n await callHandler(\"auth.reset_password\", { email, token, password });\n // Stash a one-shot flash for /login. Plain sessionStorage is fine —\n // the message is benign UX copy, never the password or token.\n try {\n window.sessionStorage.setItem(\n FLASH_KEY,\n JSON.stringify({ kind: \"success\", text: \"Password updated. Sign in with your new password.\" }),\n );\n } catch {\n // Storage may be unavailable (private mode, quota). Skip silently.\n }\n navigate(\"/login\", { replace: true });\n } catch (err) {\n // Two branches surface distinctly so the user gets actionable\n // copy instead of being pushed back through a fresh-request cycle:\n // - PASSWORD_REUSED: token is still valid; pick a different password.\n // - PASSWORD_TOO_SHORT: defense-in-depth path (the client-side\n // length guard above normally prevents this); show the length\n // hint so the user fixes the input rather than the link.\n // Every other failure (invalid/expired/used token, email mismatch,\n // network, 5xx) collapses to the single generic refusal per plan §1.2.\n if (err instanceof ApiError && err.code === \"PASSWORD_REUSED\") {\n setError(\"Your new password must differ from your current password.\");\n } else if (err instanceof ApiError && err.code === \"PASSWORD_TOO_SHORT\") {\n setError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters.`);\n } else {\n setError(\"Invalid or expired link. Please request a new one.\");\n }\n } finally {\n setPending(false);\n }\n }\n\n return (\n <AuthShell>\n <div data-testid=\"reset-page\">\n <div className=\"text-accent uppercase\" style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: \"0.1em\" }}>Reset password</div>\n <h2 style={{ margin: \"6px 0 4px\", fontSize: 24, fontWeight: 600, letterSpacing: \"-0.018em\" }}>Choose a new password</h2>\n {tokenMissing ? (\n <div data-testid=\"reset-invalid-link\">\n <p className=\"text-muted-foreground\" style={{ margin: \"0 0 16px\", fontSize: 13 }}>\n Invalid or expired link. Please request a new one.\n </p>\n <Link\n to=\"/forgot\"\n className=\"text-accent no-underline\"\n style={{ fontSize: 12 }}\n >\n Request a new reset link\n </Link>\n </div>\n ) : (\n <form onSubmit={onSubmit} data-testid=\"reset-form\">\n <p className=\"text-muted-foreground\" style={{ margin: \"0 0 20px\", fontSize: 13 }}>\n Enter the email associated with your account and choose a new password.\n </p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div>\n <FieldLabel>Email</FieldLabel>\n <TextInput\n value={email}\n onChange={setEmail}\n prefix={<Icon.email size={13} />}\n placeholder=\"you@company.com\"\n autoFocus\n required\n type=\"email\"\n aria-label=\"Email\"\n />\n </div>\n <div>\n <FieldLabel>New password</FieldLabel>\n <PasswordInput\n value={password}\n onChange={setPassword}\n placeholder=\"At least 8 characters\"\n showStrength\n />\n </div>\n <div>\n <FieldLabel>Confirm new password</FieldLabel>\n <PasswordInput\n value={confirm}\n onChange={setConfirm}\n placeholder=\"Re-enter new password\"\n />\n </div>\n {error && (\n <div\n data-testid=\"reset-error\"\n role=\"alert\"\n className=\"bg-destructive-soft text-destructive\"\n style={{\n padding: 12,\n borderRadius: 8,\n fontSize: 12.5,\n }}\n >\n {error}\n </div>\n )}\n <Btn\n type=\"submit\"\n size=\"md\"\n kind=\"primary\"\n style={{ justifyContent: \"center\", width: \"100%\" }}\n disabled={pending}\n >\n {pending ? \"Updating…\" : \"Update password\"}\n </Btn>\n <Link\n to=\"/login\"\n className=\"text-accent text-center no-underline\"\n style={{ fontSize: 12 }}\n >\n ← Back to sign in\n </Link>\n </div>\n </form>\n )}\n </div>\n </AuthShell>\n );\n}\n","// Ported from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx TwoFAForm.\n// Visual-only: TOTP verification is not wired through the SDK yet.\nimport { useRef, type ChangeEvent, type KeyboardEvent } from \"react\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport { AuthShell } from \"./AuthShell\";\nimport { Btn } from \"@/components/primitives/Btn\";\n\nexport function TwoFA() {\n const navigate = useNavigate();\n const refs = useRef<Array<HTMLInputElement | null>>([]);\n\n function focusAt(i: number) {\n refs.current[i]?.focus();\n refs.current[i]?.select();\n }\n\n function onChange(i: number, e: ChangeEvent<HTMLInputElement>) {\n const v = e.target.value.replace(/\\D/g, \"\").slice(0, 1);\n e.target.value = v;\n if (v && i < 5) focusAt(i + 1);\n }\n\n function onKeyDown(i: number, e: KeyboardEvent<HTMLInputElement>) {\n if (e.key === \"Backspace\" && !(e.target as HTMLInputElement).value && i > 0) focusAt(i - 1);\n }\n\n return (\n <AuthShell>\n <div>\n <div style={{ fontSize: 10.5, fontWeight: 700, color: \"var(--accent)\", letterSpacing: \"0.1em\", textTransform: \"uppercase\" }}>Two-factor</div>\n <h2 style={{ margin: \"6px 0 4px\", fontSize: 24, fontWeight: 600, letterSpacing: \"-0.018em\" }}>Enter your code</h2>\n <p style={{ margin: \"0 0 20px\", fontSize: 13, color: \"var(--text-3)\" }}>Open your authenticator app and enter the 6-digit code.</p>\n <div style={{ display: \"flex\", gap: 8, justifyContent: \"center\", margin: \"20px 0\" }}>\n {[0, 1, 2, 3, 4, 5].map((i) => (\n <input\n key={i}\n ref={(el) => { refs.current[i] = el; }}\n maxLength={1}\n inputMode=\"numeric\"\n onChange={(e) => onChange(i, e)}\n onKeyDown={(e) => onKeyDown(i, e)}\n style={{\n width: 44, height: 52, textAlign: \"center\", fontSize: 22, fontWeight: 600,\n background: \"var(--surface-2)\", border: \"1px solid var(--border)\", borderRadius: 8,\n outline: \"none\", color: \"var(--text)\", fontFamily: \"var(--font-mono)\",\n }}\n />\n ))}\n </div>\n <Btn size=\"md\" kind=\"primary\" style={{ justifyContent: \"center\", width: \"100%\" }} onClick={() => navigate(\"/\")}>\n Verify\n </Btn>\n <div style={{ marginTop: 12, display: \"flex\", justifyContent: \"space-between\", fontSize: 12 }}>\n <a href=\"#\" style={{ color: \"var(--accent)\", textDecoration: \"none\" }}>Use backup code</a>\n <Link to=\"/login\" style={{ color: \"var(--text-3)\", textDecoration: \"none\" }}>Cancel</Link>\n </div>\n </div>\n </AuthShell>\n );\n}\n","import { useEffect } from \"react\";\nimport { useNavigate, useSearchParams } from \"react-router-dom\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { getMe, listTenants } from \"@/api/auth\";\nimport { sanitizeRedirectPath } from \"@/api/auth-redirect\";\n\nexport function SSOCallbackPage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { setUser, setTenants } = useAuthStore();\n const queryClient = useQueryClient();\n\n useEffect(() => {\n // The backend has already set the __Host- session cookies on the SSO\n // callback response. All we need to do is hydrate the store by\n // dispatching auth.me (the browser sends the cookie automatically).\n const redirect = sanitizeRedirectPath(searchParams.get(\"redirect\"));\n\n getMe()\n .then((user) => {\n setUser(user, user.tenant);\n void queryClient.invalidateQueries({ queryKey: [\"schema\"] });\n listTenants().then(setTenants).catch(() => {});\n navigate(redirect, { replace: true });\n })\n .catch(() => {\n navigate(\"/login?sso_error=authentication+failed\", { replace: true });\n });\n }, [navigate, queryClient, searchParams, setUser, setTenants]);\n\n return (\n <div className=\"flex min-h-screen items-center justify-center\">\n <p className=\"text-muted-foreground\">Completing sign in...</p>\n </div>\n );\n}\n","import { useEffect } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport { Btn } from \"@/components/primitives\";\nimport { useEmbed, useEmbedNavigate, useEmbedNavigationFailed } from \"@/embed\";\n\nexport function NotFoundPage() {\n const embed = useEmbed();\n const navigate = useEmbedNavigate();\n const location = useLocation();\n const emitNavigationFailed = useEmbedNavigationFailed();\n\n // In embed mode the iframe must own NO navigation surface of its own (a\n // \"Go home\" button would change the iframe to a screen the host did not\n // select). Instead, signal the host once so it can recover in its own chrome\n // (show its own not-found, reset its sidebar selection), and render a neutral\n // contained state with no navigation control. The host owns recovery; the\n // iframe stays put.\n useEffect(() => {\n if (embed.enabled) {\n emitNavigationFailed(location.pathname, \"not_found\");\n }\n // Fire once per distinct unresolved path.\n }, [embed.enabled, location.pathname, emitNavigationFailed]);\n\n if (embed.enabled) {\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <p style={{ fontSize: 15, color: \"var(--text-3)\" }}>This view is unavailable.</p>\n </div>\n );\n }\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <div style={{ textAlign: \"center\" }}>\n <h1 style={{ fontSize: 64, fontWeight: 700, color: \"var(--text-3)\", margin: 0 }}>404</h1>\n <p style={{ marginTop: 8, fontSize: 16, color: \"var(--text-3)\" }}>Page not found</p>\n <div style={{ marginTop: 24 }}>\n <Btn onClick={() => navigate(\"/\")}>Go home</Btn>\n </div>\n </div>\n </div>\n );\n}\n","// Ported from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx\n// ProfilePage onChangePassword action-modal config (current/new/confirm).\n// The new-password field uses PasswordInput showStrength from Fields.tsx.\nimport { useState } from \"react\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { IconBtn } from \"@/components/primitives/IconBtn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { FieldLabel, PasswordInput } from \"@/components/primitives/Fields\";\nimport { Spinner } from \"@/components/primitives/Spinner\";\n\nexport type ChangePasswordDialogProps = {\n open: boolean;\n onClose: () => void;\n onSubmit: (input: { current: string; next: string }) => Promise<void> | void;\n};\n\nexport function ChangePasswordDialog({ open, onClose, onSubmit }: ChangePasswordDialogProps) {\n const [current, setCurrent] = useState(\"\");\n const [next, setNext] = useState(\"\");\n const [confirm, setConfirm] = useState(\"\");\n const [running, setRunning] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (!open) return null;\n\n const reset = () => { setCurrent(\"\"); setNext(\"\"); setConfirm(\"\"); setError(null); setRunning(false); };\n const close = () => { reset(); onClose(); };\n\n const canSubmit = current.length > 0 && next.length >= 8 && confirm === next;\n\n const go = async () => {\n if (!canSubmit) {\n if (next.length < 8) setError(\"New password must be at least 8 characters.\");\n else if (confirm !== next) setError(\"Passwords do not match.\");\n else setError(\"Fill in all fields.\");\n return;\n }\n setError(null);\n setRunning(true);\n try {\n await onSubmit({ current, next });\n reset();\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : \"Failed to change password.\");\n } finally {\n setRunning(false);\n }\n };\n\n return (\n <>\n <div\n onClick={close}\n style={{ position: \"fixed\", inset: 0, background: \"var(--scrim)\", zIndex: 60, backdropFilter: \"blur(2px)\" }}\n />\n <div\n style={{\n position: \"fixed\", top: \"50%\", left: \"50%\", transform: \"translate(-50%, -50%)\",\n zIndex: 61, width: 460, background: \"var(--surface)\", borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\", border: \"1px solid var(--border)\", overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"14px 18px 0\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span style={{\n width: 28, height: 28, borderRadius: 7, background: \"var(--accent-soft)\", color: \"var(--accent)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n }}>\n <Icon.key size={15} />\n </span>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>Change password</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\" }}>Pick something strong. You'll stay signed in on this device.</div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={close} />\n </div>\n\n <div style={{ padding: \"12px 18px 16px\", display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div>\n <FieldLabel required>Current password</FieldLabel>\n <PasswordInput value={current} onChange={setCurrent} placeholder=\"Enter your current password\" />\n </div>\n <div>\n <FieldLabel required>New password</FieldLabel>\n <PasswordInput value={next} onChange={setNext} placeholder=\"At least 8 characters\" showStrength />\n </div>\n <div>\n <FieldLabel required>Confirm new password</FieldLabel>\n <PasswordInput value={confirm} onChange={setConfirm} placeholder=\"Re-enter new password\" />\n </div>\n {error && (\n <div style={{\n padding: \"8px 10px\", background: \"var(--danger-soft)\", borderRadius: 6,\n fontSize: 11.5, color: \"var(--danger)\", display: \"flex\", gap: 6,\n }}>\n <Icon.info size={13} />\n <span>{error}</span>\n </div>\n )}\n </div>\n\n <div style={{\n padding: \"10px 16px\", borderTop: \"1px solid var(--divider)\",\n display: \"flex\", alignItems: \"center\", gap: 8, background: \"var(--surface-2)\",\n }}>\n <span className=\"mono\" style={{ fontSize: 10.5, color: \"var(--text-4)\" }}>auth.change_password@v1</span>\n <div style={{ flex: 1 }} />\n <Btn onClick={close}>Cancel</Btn>\n <Btn\n kind=\"primary\"\n icon={running ? <Spinner size={13} /> : <Icon.key size={13} />}\n onClick={go}\n disabled={!canSubmit || running}\n >\n {running ? \"Updating…\" : \"Update password\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { apiFetch } from \"@/api/client\";\nimport type { V2ListResponse } from \"@/types/api\";\nimport { ENTITY_CHILDREN_KEY } from \"./useChildrenData\";\n\n/**\n * useAssociatedData fetches associated (non-aggregate) child entity records\n * with cursor-based pagination, search, and filter support.\n *\n * Uses useInfiniteQuery so that \"Load more\" accumulates pages rather than\n * replacing data. The query key is compatible with the SSE invalidation\n * predicate (index 0 = ENTITY_CHILDREN_KEY, index 3 = child entity code).\n */\nexport function useAssociatedData(\n parentEntity: string,\n parentId: string,\n entityCode: string,\n foreignKey: string,\n sort?: string,\n limit?: number,\n search?: string,\n filters?: Record<string, string>,\n) {\n // Stable serialization of filters for query key identity.\n const stableFilters = filters\n ? JSON.stringify(Object.entries(filters).sort())\n : undefined;\n\n return useInfiniteQuery({\n queryKey: [\n ENTITY_CHILDREN_KEY,\n parentEntity,\n parentId,\n entityCode,\n sort,\n limit,\n search,\n stableFilters,\n ],\n queryFn: ({ pageParam }) => {\n const params = new URLSearchParams();\n // Build JSON filter array: FK constraint + any additional filters.\n const filterNodes: Array<{ field: string; op: string; value: unknown }> = [\n { field: foreignKey, op: \"eq\", value: parentId },\n ];\n if (filters) {\n for (const [field, value] of Object.entries(filters)) {\n if (value && value !== \"__all__\") {\n filterNodes.push({ field, op: \"eq\", value });\n }\n }\n }\n params.set(\"filters\", JSON.stringify(filterNodes));\n if (sort) params.set(\"sort\", sort);\n if (limit) params.set(\"limit\", String(limit));\n if (search) params.set(\"search\", search);\n if (pageParam) params.set(\"after\", pageParam);\n return apiFetch<V2ListResponse>(\n `/api/data/${entityCode}?${params.toString()}`,\n );\n },\n getNextPageParam: (lastPage) =>\n lastPage.meta?.has_more ? lastPage.meta.cursor : undefined,\n initialPageParam: undefined as string | undefined,\n enabled:\n !!parentEntity && !!parentId && !!entityCode && !!foreignKey,\n });\n}\n","import { Btn, Icon, IconBtn, Spinner } from \"@/components/primitives\";\n\ninterface ActionConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n actionName: string;\n message: string;\n onConfirm: () => void;\n isPending: boolean;\n}\n\nexport function ActionConfirmDialog({\n open,\n onOpenChange,\n actionName,\n message,\n onConfirm,\n isPending,\n}: ActionConfirmDialogProps) {\n if (!open) return null;\n return (\n <>\n <div\n onClick={() => !isPending && onOpenChange(false)}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 60,\n backdropFilter: \"blur(2px)\",\n }}\n />\n <div\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n zIndex: 61,\n width: 460,\n background: \"var(--surface)\",\n borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\",\n border: \"1px solid var(--border)\",\n overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"14px 18px\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: \"var(--accent-soft)\",\n color: \"var(--accent)\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n <Icon.bolt size={15} />\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>{actionName}</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", marginTop: 2 }}>{message}</div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={() => !isPending && onOpenChange(false)} />\n </div>\n <div\n style={{\n padding: \"10px 16px\",\n borderTop: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n background: \"var(--surface-2)\",\n }}\n >\n <div style={{ flex: 1 }} />\n <Btn onClick={() => onOpenChange(false)} disabled={isPending}>\n Cancel\n </Btn>\n <Btn\n kind=\"primary\"\n icon={isPending ? <Spinner size={13} /> : <Icon.bolt size={13} />}\n onClick={onConfirm}\n disabled={isPending}\n >\n {isPending ? \"Executing…\" : \"Confirm\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { useMutation, useQueryClient, type QueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\nimport { createEntity, updateEntity, deleteEntity, restoreEntity, executeAction } from \"@/api/data\";\nimport type { PkValues } from \"@/types/schema\";\nimport { ENTITY_CHILDREN_KEY } from \"@/hooks/useChildrenData\";\nimport type { ActionExecuteRequest } from \"@/api/data\";\n\n// persistentErrorToast surfaces a mutation failure as an error toast.\n//\n// Server-rejection errors (unique-constraint, FK, validation) need the user\n// to read and act on them. Error toasts are sticky by design (see lib/toast):\n// they stay until dismissed and carry a built-in Copy button, so a\n// duplicate-email rejection cannot flash past while the user reads the field.\nfunction persistentErrorToast(err: Error): void {\n toast.error(err.message);\n}\n\n// invalidateEntityCaches drops the React Query caches that a write against\n// `entityCode` can stale. Invalidation is by entity-level key prefix: we avoid\n// setQueryData because cache keys use different ID formats across consumers\n// (pkCacheKey \"k=v&...\" vs raw URL id vs \"|\"-joined composite). Prefix\n// invalidation always matches regardless of format.\n//\n// `entity-data` (list views) and `entity-count` (the decoupled list-total\n// query, see useEntityData) are always invalidated together: a create /\n// delete / restore changes the total, and an update can move a row in or\n// out of an active filter set. The remaining caches are opt-in so each hook\n// invalidates exactly what its write can affect:\n// - item: `entity-item` (single-record detail views)\n// - children: nested child collections\n// - objectMeta: per-object status and properties\nfunction invalidateEntityCaches(\n queryClient: QueryClient,\n entityCode: string,\n opts: { item?: boolean; children?: boolean; objectMeta?: boolean } = {},\n): void {\n queryClient.invalidateQueries({ queryKey: [\"entity-data\", entityCode] });\n queryClient.invalidateQueries({ queryKey: [\"entity-count\", entityCode] });\n if (opts.item) {\n queryClient.invalidateQueries({ queryKey: [\"entity-item\", entityCode] });\n }\n if (opts.children) {\n queryClient.invalidateQueries({ queryKey: [ENTITY_CHILDREN_KEY, entityCode] });\n }\n if (opts.objectMeta) {\n queryClient.invalidateQueries({ queryKey: [\"object-status\", entityCode] });\n queryClient.invalidateQueries({ queryKey: [\"object-properties\", entityCode] });\n }\n}\n\nexport function useCreateEntity(entityCode: string) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (data: Record<string, unknown>) =>\n createEntity(entityCode, data),\n onSuccess: () => {\n invalidateEntityCaches(queryClient, entityCode);\n toast.success(\"Record created\");\n },\n onError: persistentErrorToast,\n });\n}\n\nexport function useUpdateEntity(entityCode: string) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ pkValues, data }: { pkValues: PkValues | string; data: Record<string, unknown> }) =>\n updateEntity(entityCode, pkValues, data),\n onSuccess: () => {\n invalidateEntityCaches(queryClient, entityCode, { item: true, children: true, objectMeta: true });\n toast.success(\"Record updated\");\n },\n onError: persistentErrorToast,\n });\n}\n\nexport function useDeleteEntity(entityCode: string) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (pkValues: PkValues | string) => deleteEntity(entityCode, pkValues),\n onSuccess: () => {\n invalidateEntityCaches(queryClient, entityCode, { item: true });\n toast.success(\"Record deleted\");\n },\n onError: persistentErrorToast,\n });\n}\n\nexport function useRestoreEntity(entityCode: string) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (pkValues: PkValues | string) => restoreEntity(entityCode, pkValues),\n onSuccess: () => {\n invalidateEntityCaches(queryClient, entityCode, { item: true, objectMeta: true });\n toast.success(\"Record restored\");\n },\n onError: persistentErrorToast,\n });\n}\n\nexport function useExecuteAction(entityCode?: string) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async ({ actionCode, body }: { actionCode: string; body: ActionExecuteRequest }) => {\n const result = await executeAction(actionCode, body);\n if (result.status === \"error\") {\n throw new Error(\"Action failed for all items\");\n }\n return result;\n },\n onSuccess: (result) => {\n if (entityCode) {\n invalidateEntityCaches(queryClient, entityCode, { item: true, children: true, objectMeta: true });\n }\n if (result.status === \"accepted\") {\n toast.success(\"Action started - check audit for progress\");\n } else if (result.status === \"partial\") {\n toast.warning(\"Action partially completed - some items failed or were skipped\");\n } else {\n toast.success(\"Action executed\");\n }\n },\n onError: persistentErrorToast,\n });\n}\n","import type { Action, ActionCondition, ActionConditionList, LocalizedString, Schema } from \"@/types/schema\";\n\n/**\n * Evaluates action conditions against row data.\n * Returns true if the action is available (all conditions pass).\n * An action with no conditions is always available.\n */\nexport function evaluateConditions(\n conditions: ActionCondition[] | undefined,\n row: Record<string, unknown>,\n): boolean {\n if (!conditions || conditions.length === 0) return true;\n\n for (const cond of conditions) {\n // Status-based conditions.\n if (cond.status && cond.status.length > 0) {\n const rowStatus = row.status;\n if (typeof rowStatus !== \"string\") return false;\n if (!cond.status.includes(rowStatus)) return false;\n }\n // Field-based conditions with operators.\n if (cond.field) {\n if (!evaluateFieldCondition(cond, row)) return false;\n }\n }\n return true;\n}\n\n/**\n * Evaluates a single field-based condition against row data.\n * Supports operators: eq, ne, gt, gte, lt, lte.\n * Defaults to \"eq\" when op is not specified.\n */\nfunction evaluateFieldCondition(\n cond: ActionCondition,\n row: Record<string, unknown>,\n): boolean {\n const fieldVal = row[cond.field!];\n const op = cond.op || \"eq\";\n\n // Set-membership operators: value is an array; row's field is matched against\n // the array elements (plan §3.3: op MUST be one of eq|ne|in|not_in|gt|lt|ge|le).\n // HITL visible_if uses `op: in, value: [open, assigned]` to scope approve/reject\n // to undecided cases; missing this branch caused all HITL row actions to drop\n // silently (caught by e2e/tests/action-toolbar.spec.ts).\n if (op === \"in\" || op === \"not_in\") {\n const arr = Array.isArray(cond.value) ? cond.value : [cond.value];\n const fieldStr = fieldVal == null ? \"\" : String(fieldVal);\n const present = arr.some((v) => (v == null ? \"\" : String(v)) === fieldStr);\n return op === \"in\" ? present : !present;\n }\n\n // Boolean truthiness check: {field: \"is_active\", value: true/false}\n if (typeof cond.value === \"boolean\" && op === \"eq\") {\n return isTruthy(fieldVal) === cond.value;\n }\n\n // Numeric comparison when both values are numbers.\n if (typeof fieldVal === \"number\" && typeof cond.value === \"number\") {\n return compareValues(fieldVal, cond.value, op);\n }\n\n // String comparison (coerce to string).\n const fieldStr = fieldVal == null ? \"\" : String(fieldVal);\n const condStr = cond.value == null ? \"\" : String(cond.value);\n return compareValues(fieldStr, condStr, op);\n}\n\nfunction isTruthy(val: unknown): boolean {\n if (val == null) return false;\n if (typeof val === \"boolean\") return val;\n if (typeof val === \"string\") return val !== \"\";\n if (typeof val === \"number\") return val !== 0;\n return true;\n}\n\nfunction compareValues<T extends number | string>(a: T, b: T, op: string): boolean {\n switch (op) {\n case \"eq\": return a === b;\n case \"ne\": return a !== b;\n case \"gt\": return a > b;\n case \"gte\":\n case \"ge\": return a >= b; // \"ge\" alias from plan §3.3 op grammar\n case \"lt\": return a < b;\n case \"lte\":\n case \"le\": return a <= b; // \"le\" alias from plan §3.3 op grammar\n default: return false;\n }\n}\n\n/**\n * Returns actions from an entity whose visibility array contains the given\n * placement. Canonical values: detail | list_row | list_toolbar | app_menu |\n * nav_create | command_palette. Schema validator rejects unknown values at\n * load time so we don't need runtime alias handling here.\n */\nexport function getActionsByVisibility(\n actions: Record<string, Action> | undefined,\n visibility: string,\n): Array<{ code: string; action: Action }> {\n if (!actions) return [];\n return Object.entries(actions)\n .filter(([, action]) => action.visibility?.includes(visibility))\n .map(([code, action]) => ({ code, action }));\n}\n\n/**\n * Returns actions bound to a screen. When the screen defines explicit action/row_action\n * codes, only those are returned (resolved from entity.actions first, then global actions).\n * When no screen binding exists, falls back to visibility-based filtering.\n */\nexport function getScreenActions(\n entityActions: Record<string, Action> | undefined,\n screenActionCodes: string[] | undefined,\n fallbackVisibility: string,\n globalActions?: Record<string, Action>,\n): Array<{ code: string; action: Action }> {\n if (!entityActions && !globalActions) return [];\n if (screenActionCodes !== undefined) {\n return screenActionCodes\n .map((code) => {\n const action = entityActions?.[code] ?? globalActions?.[code];\n return action ? { code, action } : null;\n })\n .filter((item): item is { code: string; action: Action } => item !== null);\n }\n return getActionsByVisibility(entityActions, fallbackVisibility);\n}\n\n/**\n * Checks if an action has parameters that need user input.\n */\nexport function actionHasParams(action: Action): boolean {\n const params = action.resolved_params;\n if (!params) return false;\n return params.length > 0;\n}\n\n/**\n * Builds the full dot-qualified action code for API calls.\n */\nexport function fullActionCode(entityCode: string | undefined, actionCode: string): string {\n if (entityCode) return `${entityCode}.${actionCode}`;\n return actionCode;\n}\n\n/**\n * Checks if an action is an association navigation action.\n * Reason: per new wire format (unified-toolbar plan §1-§2), dispatch mode is\n * encoded by which dispatch field is set; the `association` field is the mode.\n */\nexport function isAssociationAction(action: Action): boolean {\n return !!action.association;\n}\n\n/**\n * Finds the list screen route for a given entity code.\n * Returns the screen route and display info, or undefined if no list screen exists.\n */\nexport function findListScreenForEntity(\n schema: Schema | undefined,\n entityCode: string,\n): { route: string; icon: string } | undefined {\n if (!schema) return undefined;\n for (const screen of Object.values(schema.screens)) {\n if (screen.entity === entityCode && screen.type === \"list\") {\n const entity = schema.entities[entityCode];\n return {\n route: screen.route,\n icon: screen.display?.icon || entity?.display?.icon || \"\",\n };\n }\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Resolved action layout - post-RBAC-prune wire format and per-row evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Resolved leaf node. Kind discriminator \"action\".\n * Enabled defaults true from server; client sets false when enabled_if fails.\n * Reason carries the localized tooltip text when Enabled is false.\n *\n * Wire format (plan §8): {\"kind\":\"action\",\"code\":\"approve\",\"enabled\":true,\"reason\":null}\n */\nexport interface ResolvedActionLeaf {\n kind: \"action\";\n code: string;\n enabled: boolean;\n reason: string | null;\n}\n\n/**\n * Resolved group node. Kind discriminator \"group\".\n * Items is always non-empty after pruning (empty groups are dropped).\n *\n * Wire format (plan §8): {\"kind\":\"group\",\"name\":{...},\"icon\":\"...\",\"items\":[...]}\n */\nexport interface ResolvedActionGroup {\n kind: \"group\";\n name?: LocalizedString;\n icon?: string;\n items: ResolvedActionLayoutNode[];\n}\n\n/**\n * Discriminated union for a resolved action layout node.\n * Narrow via node.kind === \"action\" or node.kind === \"group\".\n */\nexport type ResolvedActionLayoutNode = ResolvedActionLeaf | ResolvedActionGroup;\n\n/**\n * applyRowState walks an RBAC-pruned ResolvedActionLayoutNode[] and applies\n * per-row visible_if / enabled_if evaluation (plan §4, §5).\n *\n * Drop conditions:\n * - visible_if conditions not satisfied by row -> leaf dropped\n * - Empty groups after all children are dropped -> group dropped\n *\n * Disable conditions:\n * - enabled_if conditions not satisfied by row -> leaf enabled=false + reason set\n * - For bulk (rowSelection.length > 1): disabled if ANY selected row fails enabled_if\n * (plan §5: \"disabled if ANY row fails\"). Reason is from the first failing row.\n *\n * @param layout RBAC-pruned tree from the server (or resolveActionLayout client-side).\n * @param entityActions Entity-level actions map (entity wins on code collision).\n * @param globalActions Global schema actions (fallback when entity doesn't declare).\n * @param row The current row data for single-row context (detail / list_row).\n * @param rowSelection All selected rows for bulk context (list_toolbar bulk mode).\n * Pass empty array for non-bulk contexts.\n */\nexport function applyRowState(\n layout: ResolvedActionLayoutNode[],\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n row: Record<string, unknown>,\n rowSelection: Record<string, unknown>[],\n): ResolvedActionLayoutNode[] {\n const out: ResolvedActionLayoutNode[] = [];\n for (const node of layout) {\n const result = applyRowStateNode(node, entityActions, globalActions, row, rowSelection);\n if (result !== null) {\n out.push(result);\n }\n }\n return out;\n}\n\n/** Apply row state to a single node. Returns null if the node should be dropped. */\nfunction applyRowStateNode(\n node: ResolvedActionLayoutNode,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n row: Record<string, unknown>,\n rowSelection: Record<string, unknown>[],\n): ResolvedActionLayoutNode | null {\n if (node.kind === \"group\") {\n return applyRowStateGroup(node, entityActions, globalActions, row, rowSelection);\n }\n return applyRowStateLeaf(node, entityActions, globalActions, row, rowSelection);\n}\n\n/** Apply row state to a group. Drops the group if all items are pruned. */\nfunction applyRowStateGroup(\n group: ResolvedActionGroup,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n row: Record<string, unknown>,\n rowSelection: Record<string, unknown>[],\n): ResolvedActionGroup | null {\n const items: ResolvedActionLayoutNode[] = [];\n for (const child of group.items) {\n const result = applyRowStateNode(child, entityActions, globalActions, row, rowSelection);\n if (result !== null) {\n items.push(result);\n }\n }\n if (items.length === 0) {\n return null; // empty group -> dropped (plan §5)\n }\n return { ...group, items };\n}\n\n/** Apply row state to a leaf. Returns null if visible_if fails; sets enabled/reason if enabled_if fails. */\nfunction applyRowStateLeaf(\n leaf: ResolvedActionLeaf,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n row: Record<string, unknown>,\n rowSelection: Record<string, unknown>[],\n): ResolvedActionLeaf | null {\n const action = lookupActionTS(leaf.code, entityActions, globalActions);\n if (!action) {\n // Action code references an entry not present in the current actions map.\n // Drop the leaf rather than render a dead button. The plan §4 walk algorithm\n // calls for dropping unresolvable references; keeping them would surface a\n // button whose onRun receives undefined action metadata and whose label/icon\n // cannot be rendered. The empty-group auto-hide logic upstream still applies.\n return null;\n }\n\n // Tier 2: visible_if - hide entirely when condition fails for current row (plan §5).\n if (!evaluateConditions(asActionConditions(action.visible_if), row)) {\n return null;\n }\n\n // Tier 3: enabled_if - disable with tooltip when condition fails (plan §5).\n // For bulk: disabled if ANY selected row fails (plan §5 multi-row semantics).\n const { enabled, reason } = evaluateEnabledIf(action, row, rowSelection);\n\n return { ...leaf, enabled, reason };\n}\n\n/**\n * Evaluates enabled_if for single-row and bulk-row contexts.\n * Single row: condition evaluated against `row`.\n * Bulk (rowSelection.length > 0): condition evaluated against ALL rows in selection;\n * disabled if ANY row fails; reason from first failing row (plan §5).\n * Returns { enabled: true, reason: null } when enabled_if is absent.\n */\nfunction evaluateEnabledIf(\n action: Action,\n row: Record<string, unknown>,\n rowSelection: Record<string, unknown>[],\n): { enabled: boolean; reason: string | null } {\n const conditions = asActionConditions(action.enabled_if);\n if (!conditions || conditions.length === 0) {\n return { enabled: true, reason: null };\n }\n\n const reasonText = locFirst(action.enabled_if_reason);\n\n // Bulk mode: check all rows in selection. Disabled if ANY row fails.\n // Tooltip includes failing count so the user knows which rows block the action (plan §5).\n if (rowSelection.length > 0) {\n let failingCount = 0;\n for (const selectedRow of rowSelection) {\n if (!evaluateConditions(conditions, selectedRow)) {\n failingCount++;\n }\n }\n if (failingCount > 0) {\n const countNote = `(${failingCount} of ${rowSelection.length} rows fail)`;\n const fullReason = reasonText ? `${reasonText} ${countNote}` : countNote;\n return { enabled: false, reason: fullReason };\n }\n return { enabled: true, reason: null };\n }\n\n // Single-row mode: check current row.\n if (!evaluateConditions(conditions, row)) {\n return { enabled: false, reason: reasonText };\n }\n return { enabled: true, reason: null };\n}\n\n/**\n * Looks up an action by code. Entity-level map wins over global actions\n * on code collision (plan §4: \"entity wins\"). Mirrors Go lookupAction.\n */\nfunction lookupActionTS(\n code: string,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): Action | undefined {\n if (entityActions && code in entityActions) {\n return entityActions[code];\n }\n if (globalActions && code in globalActions) {\n return globalActions[code];\n }\n return undefined;\n}\n\n/**\n * Coerces ActionConditionList (which uses ActionConditionEntry) to ActionCondition[]\n * for use with evaluateConditions. Both types have the same shape; this cast avoids\n * duplication until the types are unified in a future cleanup pass.\n */\nfunction asActionConditions(\n list: ActionConditionList | undefined,\n): ActionCondition[] | undefined {\n return list as ActionCondition[] | undefined;\n}\n\n/**\n * Returns the first non-empty language value from a LocalizedString, preferring \"en\".\n * Returns null when the string is absent or has no values.\n */\nfunction locFirst(ls: LocalizedString | undefined): string | null {\n if (!ls) return null;\n if (ls[\"en\"]) return ls[\"en\"];\n const vals = Object.values(ls);\n return vals.length > 0 ? vals[0] : null;\n}\n\n// ---------------------------------------------------------------------------\n// Default flat layout: fallback when entity declares no action_layout\n// ---------------------------------------------------------------------------\n\n/**\n * Composes a flat ResolvedActionLayoutNode[] from an entity's actions map,\n * filtered to those that include the given slot in their visibility array,\n * sorted by position ascending (ties broken by declaration order).\n *\n * This is the fallback used when neither screen.action_layout nor\n * entity.action_layout is present (plan §3: \"runtime MUST compose a flat\n * layout from all actions matching the slot's visibility[], sorted by\n * position ascending\").\n *\n * All leaves are emitted with enabled=true; client applies visible_if /\n * enabled_if via applyRowState.\n *\n * @param actions Entity-level actions map from the (already RBAC-pruned) schema.\n * @param slot Placement slot code (e.g. \"detail\", \"list_row\", \"list_toolbar\").\n */\n/**\n * Filters a server-emitted ResolvedActionLayoutNode tree to a single slot,\n * preserving group structure. Drops leaves whose action's visibility[] does\n * not include the slot. Drops groups whose entire item list pruned out\n * (plan §5 empty-group auto-hide). Additional filters via `opts`:\n *\n * - opts.bulkRequired: when set, gates list_toolbar leaves whose effective\n * \"bulk-required\" semantic matches the request. The old wire-format flag\n * `bulk_selection_required` was removed in unified-toolbar Phase 1 (the\n * server now derives this from `handler.invoke`); until Task 38 replaces\n * this code path the caller passes a predicate that decides per action.\n * Reason: keep the slot+bulk filtering surface intact while removing the\n * field; the call sites supply the legacy predicate.\n * - opts.excludeIf: drop leaves where the predicate returns true. Common\n * use: hide \"create\" / \"edit\" / \"delete\" style actions that the screen\n * renders inline elsewhere. Reason: the old `excludeTypes` keyed on\n * `action.type`, which is removed; callers now express the same intent\n * as a field-presence predicate.\n *\n * Leaves whose code cannot be looked up are dropped (server should have\n * pruned them; defensive).\n */\nexport function filterLayoutForSlot(\n layout: ResolvedActionLayoutNode[],\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n slot: string,\n opts?: {\n bulkRequired?: boolean;\n bulkRequiredPredicate?: (action: Action) => boolean;\n excludeIf?: (action: Action) => boolean;\n },\n): ResolvedActionLayoutNode[] {\n const out: ResolvedActionLayoutNode[] = [];\n for (const node of layout) {\n if (node.kind === \"group\") {\n const items = filterLayoutForSlot(node.items, entityActions, globalActions, slot, opts);\n if (items.length > 0) {\n out.push({ ...node, items });\n }\n continue;\n }\n const action = lookupActionTS(node.code, entityActions, globalActions);\n if (!action) continue;\n if (!action.visibility?.includes(slot)) continue;\n if (opts?.bulkRequired !== undefined) {\n const isBulk = opts.bulkRequiredPredicate ? opts.bulkRequiredPredicate(action) : false;\n if (isBulk !== opts.bulkRequired) continue;\n }\n if (opts?.excludeIf && opts.excludeIf(action)) continue;\n out.push(node);\n }\n return out;\n}\n\n/**\n * True when an action should be treated as requiring a row selection.\n *\n * THIS IS A PROXY, not the canonical rule. The canonical \"requires\n * selection\" answer is derived per dispatch mode (see docs/dsl.md §16.6.2:\n * `handler.invoke`, association semantics, `href ${id}`). The SDK cannot\n * evaluate that rule - the resolved-action wire format carries no `invoke`\n * value, and two of the five modes (`ui_steps`, `widget`) have no\n * derivation at all. So the SDK stands in slot membership: an action is\n * selection-mode iff its `visibility` includes `list_floatbar`.\n *\n * This matches the platform's authoring guidance - the loader's\n * `bulk_selection_required`-removal error tells authors to \"use\n * visibility: [list_floatbar] for selection-mode actions\" - so the proxy\n * agrees with the canonical rule for every guideline-following schema\n * (a mis-declared schema can diverge). Full rationale, the divergence\n * case, and the planned hybrid replacement: docs/architecture.md\n * \"List Toolbar Selection Model\".\n *\n * Drives list-toolbar state: a selection-mode action renders disabled\n * while no rows are selected, and moves to the floatbar (not the static\n * toolbar) once a selection exists.\n */\nexport function actionRequiresSelection(action: Action): boolean {\n return action.visibility?.includes(\"list_floatbar\") ?? false;\n}\n\n/**\n * Walks a ResolvedActionLayoutNode[] and sets `enabled: false` plus the\n * given `reason` on every leaf whose action satisfies `predicate`. Groups\n * are recursed into; tree structure is preserved (no nodes dropped) so the\n * user still sees what is available. Leaves whose code cannot be resolved\n * are left untouched (defensive; the server should have pruned them).\n *\n * Use: grey out selection-mode actions on the list toolbar while no rows\n * are selected.\n */\nexport function disableLeavesWhere(\n layout: ResolvedActionLayoutNode[],\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n predicate: (action: Action) => boolean,\n reason: string,\n): ResolvedActionLayoutNode[] {\n return layout.map((node) => {\n if (node.kind === \"group\") {\n return {\n ...node,\n items: disableLeavesWhere(node.items, entityActions, globalActions, predicate, reason),\n };\n }\n const action = lookupActionTS(node.code, entityActions, globalActions);\n if (action && predicate(action)) {\n return { ...node, enabled: false, reason };\n }\n return node;\n });\n}\n\nexport function defaultFlatForSlot(\n actions: Record<string, Action> | undefined,\n slot: string,\n): ResolvedActionLayoutNode[] {\n if (!actions) return [];\n // Sort by (position ascending, code ascending) for deterministic ordering\n // that matches the Go defaultFlatLayout in engine/action_layout_resolve.go.\n // JS Object.entries preserves insertion order, but Array.sort with a single\n // numeric key is unstable across engines for ties - we need code as the\n // explicit secondary key for cross-engine and Go-TS parity.\n const matching = Object.entries(actions)\n .filter(([, action]) => action.visibility?.includes(slot))\n .sort(([codeA, a], [codeB, b]) => {\n const posA = a.position ?? 0;\n const posB = b.position ?? 0;\n if (posA !== posB) return posA - posB;\n return codeA < codeB ? -1 : codeA > codeB ? 1 : 0;\n });\n return matching.map(([code]) => ({\n kind: \"action\" as const,\n code,\n enabled: true,\n reason: null,\n }));\n}\n","import { useState, useCallback } from \"react\";\nimport { Btn, Icon, IconBtn, type IconName } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { ActionDialog } from \"@/components/shared/ActionDialog\";\nimport { ActionConfirmDialog } from \"@/components/shared/ActionConfirmDialog\";\nimport { ActionResultDialog, type ActionDisplayResult } from \"@/components/shared/ActionResultDialog\";\nimport { useExecuteAction } from \"@/hooks/useMutations\";\nimport { buildActionBody } from \"@/api/data\";\nimport { evaluateConditions, actionHasParams, isAssociationAction, findListScreenForEntity } from \"@/lib/actions\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { resolveAssociation } from \"@/api/data\";\nimport { toast } from \"@/lib/toast\";\nimport { loc } from \"@/types/schema\";\nimport type { Action } from \"@/types/schema\";\nimport { executeUISteps, isUIAction } from \"@/lib/ui-actions\";\n\ninterface ActionButtonProps {\n actionCode: string;\n action: Action;\n entityCode?: string;\n /** Selected object IDs (for single/batch scope). */\n objectIds?: string[];\n /** Current row data for condition evaluation (single row context). */\n row?: Record<string, unknown>;\n /** Variant for the button. */\n variant?: \"default\" | \"outline\" | \"ghost\" | \"secondary\";\n /** Size for the button. */\n size?: \"default\" | \"sm\" | \"icon\";\n /** Called after action execution succeeds. */\n onSuccess?: () => void;\n /** Called when a batch action starts executing. */\n onBatchStart?: (count: number) => void;\n /** Called when a batch action fails. */\n onBatchError?: (count: number) => void;\n /** Called when a batch action partially succeeds (some items failed/skipped). */\n onBatchPartial?: (count: number, successIds?: string[]) => void;\n}\n\nexport function ActionButton({\n actionCode,\n action,\n entityCode,\n objectIds = [],\n row,\n variant = \"outline\",\n size = \"default\",\n onSuccess,\n onBatchStart,\n onBatchError,\n onBatchPartial,\n}: ActionButtonProps) {\n const [showDialog, setShowDialog] = useState(false);\n const [showConfirm, setShowConfirm] = useState(false);\n const [navigating, setNavigating] = useState(false);\n const [displayResult, setDisplayResult] = useState<ActionDisplayResult | null>(null);\n const executeMutation = useExecuteAction(entityCode);\n const { data: schema } = useSchema();\n const tabNavigate = useTabNavigate();\n\n const isAssociation = isAssociationAction(action);\n const hasParams = actionHasParams(action);\n const hasConfirmation = !!action.confirmation && Object.keys(action.confirmation).length > 0;\n\n // Evaluate conditions - if row data is provided and conditions fail, disable button.\n const conditionsMet = row ? evaluateConditions(action.conditions, row) : true;\n\n // Scope-based disable: batch requires >= 1, single requires exactly 1.\n const isBatchEmpty = false;\n const isSingleInvalid = false;\n\n const handleAssociationNavigate = useCallback(async () => {\n if (!action.association || objectIds.length === 0) return;\n setNavigating(true);\n try {\n const assocCode = entityCode ? `${entityCode}.${action.association}` : action.association;\n const result = await resolveAssociation(assocCode, objectIds);\n const targetScreen = findListScreenForEntity(schema, result.target_entity);\n if (!targetScreen) {\n toast.error(`No list screen found for ${result.target_entity}`);\n return;\n }\n const targetEntity = schema?.entities[result.target_entity];\n const displayName = loc(action.display?.name) || result.target_entity;\n if (result.ids.length === 0) {\n toast.info(`No related ${displayName} found`);\n return;\n }\n // Navigate to target entity's list with ID filter.\n const pk = targetEntity?.fields.names().find((f) => targetEntity.fields.get(f)?.primary) ?? \"id\";\n const search = new URLSearchParams();\n search.set(`filter_${pk}`, result.ids.join(\",\"));\n tabNavigate(targetScreen.route, {\n title: displayName,\n icon: action.display?.icon || targetScreen.icon,\n search: search.toString(),\n });\n onSuccess?.();\n } catch (err) {\n toast.error(err instanceof Error ? err.message : \"Failed to resolve association\");\n } finally {\n setNavigating(false);\n }\n }, [action, entityCode, objectIds, schema, tabNavigate, onSuccess]);\n\n // Fully-qualified URL code: \"entity.action\" for entity-scoped, bare\n // \"action\" for globals. The entityCode prop is only used for cache\n // invalidation, not URL composition.\n const qualifiedActionCode = action.entity ? `${action.entity}.${actionCode}` : actionCode;\n // Only pass object_ids when the scope is non-global.\n const actionIds = objectIds.length > 0 ? objectIds : undefined;\n\n // Hidden defaults (server-resolved, e.g. hidden YAML params or values\n // injected by a parent container like AssociatedBlock) must reach the\n // server on every dispatch path — no-dialog, confirm, and dialog. The\n // dialog path merges them into initial form state; the other two paths\n // pass them here as the args body. See docs/gotchas.md \"Hidden params\"\n // and apikey_handlers.go:95 (UserID prefill for admin-on-behalf create).\n const hiddenArgs =\n action.resolved_hidden_defaults && Object.keys(action.resolved_hidden_defaults).length > 0\n ? action.resolved_hidden_defaults\n : undefined;\n\n // Shared success handler: checks for $display in the result and opens\n // ActionResultDialog instead of firing the regular onSuccess callback.\n const handleActionSuccess = useCallback(\n (result: { status: string; result?: unknown; success?: string[]; audit_operation_id?: string }, closeDialog?: () => void) => {\n closeDialog?.();\n // Async handler: enqueued for background execution.\n if (result.status === \"accepted\") {\n onSuccess?.();\n return;\n }\n // Check if the handler returned a $display result.\n const display = (result.result as Record<string, unknown> | undefined)?.$display as ActionDisplayResult | undefined;\n if (display) {\n setDisplayResult(display);\n return;\n }\n if (result.status === \"partial\") {\n onBatchPartial?.(objectIds.length, result.success);\n } else {\n onSuccess?.();\n }\n },\n [objectIds.length, onSuccess, onBatchPartial],\n );\n\n const handleClick = useCallback(() => {\n if (isUIAction(action)) {\n // Client-only action: run ui_steps sequentially. No server round-trip.\n void executeUISteps(action.ui_steps!, { row }).then(() => onSuccess?.());\n return;\n }\n if (isAssociation) {\n handleAssociationNavigate();\n return;\n }\n // Href-mode action: navigate, do not POST to the dispatcher. The\n // platform-default `__create` action is href-mode (server-expanded to\n // `<list_route>/new` at registry-build time, see\n // `golang/internal/engine/platform_defaults.go:255`). Without this\n // branch a click would fall through to `executeMutation.mutate` and\n // surface `BAD_ACTION_MODE / \"action has no server-callable handler\"`\n // because the dispatcher rejects actions with no handler / association.\n //\n // `${entity}` and `${id}` are interpolated for parity with\n // `ResponsiveActionBar.dispatchAction` (the toolbar/row dispatcher\n // used when the entity declares an action_layout); `${listRoute}`\n // is already substituted server-side so it does not appear here.\n if (action.href && !action.handler && !action.association) {\n const rowId = row && typeof row[\"id\"] === \"string\" ? (row[\"id\"] as string) : \"\";\n let url = action.href;\n if (entityCode) url = url.replaceAll(\"${entity}\", entityCode);\n if (rowId) url = url.replaceAll(\"${id}\", encodeURIComponent(rowId));\n // tabNavigate expects pathname and search as separate args; split on\n // the first `?` so platform __edit (href = \"<list>/<id>?edit=true\")\n // lands on the detail screen with the edit flag intact rather than\n // leaking the query into the router's pathname matcher.\n const qIdx = url.indexOf(\"?\");\n const meta = {\n title: loc(action.display?.name) || actionCode,\n icon: action.display?.icon,\n };\n if (qIdx === -1) {\n tabNavigate(url, meta);\n } else {\n tabNavigate(url.slice(0, qIdx), { ...meta, search: url.slice(qIdx + 1) });\n }\n onSuccess?.();\n return;\n }\n if (hasParams) {\n setShowDialog(true);\n } else if (hasConfirmation) {\n setShowConfirm(true);\n } else {\n // Execute immediately.\n onBatchStart?.(objectIds.length);\n executeMutation.mutate(\n { actionCode: qualifiedActionCode, body: buildActionBody(hiddenArgs, actionIds) },\n {\n onSuccess: (result) => handleActionSuccess(result),\n onError: () => onBatchError?.(objectIds.length),\n },\n );\n }\n }, [action, row, entityCode, actionCode, tabNavigate, isAssociation, hasParams, hasConfirmation, qualifiedActionCode, actionIds, objectIds, hiddenArgs, executeMutation, handleActionSuccess, handleAssociationNavigate, onBatchStart, onBatchError, onSuccess]);\n\n const handleConfirm = useCallback(() => {\n onBatchStart?.(objectIds.length);\n executeMutation.mutate(\n { actionCode: qualifiedActionCode, body: buildActionBody(hiddenArgs, actionIds) },\n {\n onSuccess: (result) => handleActionSuccess(result, () => setShowConfirm(false)),\n onError: () => onBatchError?.(objectIds.length),\n },\n );\n }, [qualifiedActionCode, actionIds, objectIds, hiddenArgs, executeMutation, handleActionSuccess, onBatchStart, onBatchError]);\n\n const handleDialogSubmit = useCallback(\n (params: Record<string, unknown>) => {\n onBatchStart?.(objectIds.length);\n executeMutation.mutate(\n { actionCode: qualifiedActionCode, body: buildActionBody(params, actionIds) },\n {\n onSuccess: (result) => handleActionSuccess(result, () => setShowDialog(false)),\n onError: () => onBatchError?.(objectIds.length),\n },\n );\n },\n [qualifiedActionCode, actionIds, objectIds, executeMutation, handleActionSuccess, onBatchStart, onBatchError],\n );\n\n const displayName = loc(action.display?.name);\n const iconName = action.display?.icon;\n const IconC = iconName && (Icon[iconName as IconName] as typeof Icon.plus | undefined);\n const iconNode = IconC ? <IconC size={13} /> : iconName ? <DynamicIcon name={iconName} size={13} /> : null;\n const isDisabled =\n !conditionsMet || isBatchEmpty || isSingleInvalid || executeMutation.isPending || navigating;\n const kind = variant === \"default\" ? \"primary\" : variant === \"ghost\" ? \"ghost\" : \"default\";\n\n return (\n <>\n {size === \"icon\" ? (\n <IconBtn\n icon={iconNode ?? <Icon.bolt size={13} />}\n onClick={handleClick}\n title={displayName}\n />\n ) : (\n <Btn\n kind={kind}\n size={size === \"sm\" ? \"sm\" : \"sm\"}\n icon={iconNode}\n danger={action.destructive}\n onClick={handleClick}\n disabled={isDisabled}\n title={displayName}\n aria-label={displayName}\n >\n {displayName}\n </Btn>\n )}\n\n {hasParams && (\n <ActionDialog\n open={showDialog}\n onOpenChange={setShowDialog}\n action={action}\n actionName={displayName}\n onSubmit={handleDialogSubmit}\n isPending={executeMutation.isPending}\n row={row}\n />\n )}\n\n {hasConfirmation && (\n <ActionConfirmDialog\n open={showConfirm}\n onOpenChange={setShowConfirm}\n actionName={displayName}\n message={loc(action.confirmation)}\n onConfirm={handleConfirm}\n isPending={executeMutation.isPending}\n />\n )}\n\n {displayResult && (\n <ActionResultDialog\n open={!!displayResult}\n onClose={() => {\n setDisplayResult(null);\n onSuccess?.();\n }}\n display={displayResult}\n />\n )}\n </>\n );\n}\n","import { useRef, useState } from \"react\";\nimport { Dropdown, Icon, IconBtn, MenuItem, type IconName } from \"@/components/primitives\";\nimport { loc, type Action } from \"@/types/schema\";\nimport { evaluateConditions } from \"@/lib/actions\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { executeAction, buildActionBody } from \"@/api/data\";\n\n// RowActionsKebab renders the \"...\" menu on a table row, identical on\n// standalone list pages and embedded associated-section tables. The\n// caller passes already-filtered `list_row` actions (see\n// resolveAssociatedRowActions / equivalent upstream filters).\n//\n// The menu dispatches each action via the same ActionRunner flow that\n// SmartListPage uses, so confirmation dialogs, parameter prompts,\n// $display result modals, and toast outcomes are identical everywhere.\n//\n// Note: this component intentionally does NOT render the default\n// Edit/Delete menu items that SmartListPage's RowActionsCell adds for\n// the standard entity page. Those defaults belong to the list host,\n// not the kebab primitive. Callers wrap this with their own Edit/\n// Delete MenuItems before the action list when needed.\ninterface RowActionsKebabProps {\n actions: Array<{ code: string; action: Action }>;\n objectIds: string[];\n row?: Record<string, unknown>;\n onSuccess?: () => void;\n /** Pre-menu slot for host-specific items (Edit, Delete, etc). */\n preMenu?: React.ReactNode;\n /** Emit a divider between preMenu and the action list when both are present. */\n divider?: boolean;\n /** Narrow hit-target button size. Defaults match SmartListPage (22px). */\n size?: number;\n align?: \"start\" | \"end\";\n}\n\nexport function RowActionsKebab({\n actions,\n objectIds,\n row,\n onSuccess,\n preMenu,\n divider,\n size = 22,\n align = \"end\",\n}: RowActionsKebabProps) {\n const btnRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n const { run } = useActionRunner();\n\n // Don't render the button at all when there's nothing to show.\n if (!preMenu && actions.length === 0) return null;\n\n const go = (fn: () => void) => () => {\n setOpen(false);\n fn();\n };\n\n return (\n <div\n className=\"sl-row-actions\"\n data-open={open ? \"true\" : undefined}\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"flex-end\" }}\n >\n <IconBtn\n ref={btnRef}\n size={size}\n icon={<Icon.more size={Math.round(size * 0.55)} />}\n onClick={() => setOpen((prev) => !prev)}\n title=\"Actions\"\n />\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n width={200}\n align={align}\n >\n {preMenu}\n {preMenu && divider && actions.length > 0 && <MenuItem divider />}\n {actions.map(({ code, action }) => {\n const conditionsMet = row ? evaluateConditions(action.conditions, row) : true;\n const name = loc(action.display?.name) || code;\n const iconName = action.display?.icon as IconName | undefined;\n const IconC = iconName && Icon[iconName] ? (Icon[iconName] as typeof Icon.plus) : undefined;\n const icon = IconC ? <IconC size={12} /> : null;\n return (\n <MenuItem\n key={code}\n icon={icon}\n danger={action.destructive}\n onClick={go(() => {\n if (!conditionsMet) return;\n const fullCode = action.entity ? `${action.entity}.${code}` : code;\n run({\n action,\n title: name,\n execute: async (params) => {\n const result = await executeAction(fullCode, buildActionBody(params, objectIds));\n onSuccess?.();\n return result;\n },\n });\n })}\n >\n {name}\n </MenuItem>\n );\n })}\n </Dropdown>\n </div>\n );\n}\n","import { useMemo } from \"react\";\nimport { Icon, Select, TextInput } from \"@/components/primitives\";\nimport { useEntity } from \"@/hooks/useEntity\";\nimport { useEntityData } from \"@/hooks/useEntityData\";\nimport { DISPLAY_KEY, fieldDisplayName, loc } from \"@/types/schema\";\nimport type { Entity, EntityField } from \"@/types/schema\";\n\ninterface AssociatedToolbarProps {\n entity: Entity;\n searchable?: boolean;\n filterFields?: string[];\n searchValue: string;\n onSearchChange: (value: string) => void;\n filters: Record<string, string>;\n onFilterChange: (field: string, value: string) => void;\n allEntities?: Record<string, Entity>;\n}\n\nexport function AssociatedToolbar({\n entity,\n searchable,\n filterFields,\n searchValue,\n onSearchChange,\n filters,\n onFilterChange,\n allEntities,\n}: AssociatedToolbarProps) {\n const hasSearch = searchable === true;\n const hasFilters = filterFields && filterFields.length > 0;\n\n if (!hasSearch && !hasFilters) return null;\n\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, marginBottom: 8 }}>\n {hasSearch && (\n <div style={{ flex: 1, maxWidth: 220 }}>\n <TextInput\n value={searchValue}\n onChange={onSearchChange}\n placeholder=\"Search…\"\n prefix={<Icon.search size={12} />}\n />\n </div>\n )}\n {hasFilters &&\n filterFields.map((filterField) => {\n const field = entity.fields.get(filterField);\n if (!field) return null;\n const currentVal = filters[filterField] ?? \"__all__\";\n const label = fieldDisplayName(field, filterField, allEntities);\n\n if (field.type === \"ref\" && field.ref?.entity) {\n return (\n <RefFilterDropdown\n key={filterField}\n field={field}\n label={label}\n value={currentVal}\n onChange={(v) => onFilterChange(filterField, v)}\n />\n );\n }\n\n if (!field.values) return null;\n const options = [\n { value: \"__all__\", label: `All ${label}` },\n ...field.values.map((v) => ({ value: v.code, label: loc(v.name) || v.code })),\n ];\n\n return (\n <div key={filterField} style={{ width: 160 }}>\n <Select\n value={currentVal}\n options={options}\n onChange={(v) => onFilterChange(filterField, v)}\n placeholder={label}\n />\n </div>\n );\n })}\n </div>\n );\n}\n\nfunction RefFilterDropdown({\n field,\n label,\n value,\n onChange,\n}: {\n field: EntityField;\n label: string;\n value: string;\n onChange: (value: string) => void;\n}) {\n const refEntity = useEntity(field.ref?.entity);\n const displayField = field.display?.ref_display_field ?? refEntity?.display?.display_field;\n const pk = useMemo(\n () =>\n refEntity\n ? ([...refEntity.fields].find(([, f]) => f.primary)?.[0] ?? \"id\")\n : \"id\",\n [refEntity],\n );\n\n const { data: response } = useEntityData(field.ref?.entity, {\n per_page: 200,\n sort: displayField ? `${displayField}:asc` : undefined,\n });\n const options = response?.data ?? [];\n\n const resolveDisplay = (val: unknown): string => {\n if (val == null) return \"—\";\n if (typeof val === \"object\" && !Array.isArray(val)) {\n const first = Object.values(val as Record<string, unknown>)[0];\n return first != null ? String(first) : \"—\";\n }\n return String(val);\n };\n\n const selectOptions = [\n { value: \"__all__\", label: `All ${label}` },\n ...options.map((r) => {\n const id = String(r[pk]);\n const rendered = r[DISPLAY_KEY];\n const optLabel =\n typeof rendered === \"string\" && rendered !== \"\"\n ? rendered\n : displayField\n ? resolveDisplay(r[displayField] ?? id)\n : id;\n return { value: id, label: optLabel };\n }),\n ];\n\n return (\n <div style={{ width: 160 }}>\n <Select value={value} options={selectOptions} onChange={onChange} placeholder={label} />\n </div>\n );\n}\n","/**\n * ResponsiveActionBar - placement-agnostic action toolbar.\n *\n * Consumes a resolved + RBAC-pruned ResolvedActionLayoutNode[] from the server\n * (plan §8) and renders it as an inline bar with overflow \"More\" dropdown\n * (kind=\"detail\"/\"toolbar\") or as a single kebab \"⋮\" (kind=\"row\").\n *\n * Measurement algorithm (plan §9):\n * 1. Off-screen layer renders every leaf + group trigger invisibly.\n * 2. ResizeObserver tracks container width.\n * 3. On layout or container-width change, greedy-fill in position-ascending\n * order. Destructive leaves always trail; non-destructive overflow goes\n * into \"More\" dropdown before the destructive slot.\n * 4. visibility:hidden until first measurement completes (avoids FOUC).\n * 16ms timeout fallback in case ResizeObserver fires late.\n *\n * Selection-state performance (plan §9 \"Selection-state performance\"):\n * kind=\"toolbar\" pre-resolves and pre-measures BOTH static (no selection)\n * and bulk (selection > 0) variants at initial mount. The rowSelection toggle\n * swaps which variant renders WITHOUT re-measuring.\n *\n * Row context (kind=\"row\"):\n * Renders as a single kebab \"⋮\" dropdown. No inline measurement.\n * Destructive items land at the bottom of the dropdown with a separator.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Btn, Dropdown, Icon, IconBtn, MenuItem, Spinner } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { loc, extractPkValues, pkValuesToDetailId } from \"@/types/schema\";\nimport type { Action, Entity, Screen } from \"@/types/schema\";\nimport type { ResolvedActionLayoutNode, ResolvedActionLeaf, ResolvedActionGroup } from \"@/lib/actions\";\n\n/**\n * Action-trigger callback. Returns a promise when the caller dispatches an\n * awaitable action (direct-run via ActionRunner); the bar awaits it so the\n * button keeps its spinner/disabled state for the whole operation. Returns\n * void for fire-and-forget caller paths.\n */\nexport type ActionRunHandler = (code: string, action: Action) => void | Promise<void>;\n\n// ---------------------------------------------------------------------------\n// WidgetContext (plan §8 Task 10)\n// ---------------------------------------------------------------------------\n\n/**\n * Tab navigation options accepted by SDK tabNavigate. Kept loose here so the\n * caller can pass through the full TabMeta from useTabNavigate without forcing\n * a re-export.\n *\n * Reason: ResponsiveActionBar only forwards the value - tighter typing belongs\n * with the navigation API, not with the dispatcher that hands the value off.\n */\nexport interface TabMeta {\n title?: string;\n icon?: string;\n search?: string;\n replace?: boolean;\n}\n\n/**\n * Minimal dispatch surface assembled by SmartListPage/DetailLayout and passed\n * to ResponsiveActionBar. Phase 1 only consumes what `href` dispatch needs\n * plus the seat for the Phase 2 widget registry mount.\n *\n * Reason: defining the full WidgetContext now would freeze a surface before\n * the widget registry has a real consumer. Phase 1 ships exactly the fields\n * the href dispatch reads; Phase 2 will extend without breaking callers.\n */\nexport interface WidgetContext {\n /** Resolved entity for the current page; widget code reads schema metadata. */\n entity: Entity;\n /** Wire-format entity code used to interpolate `${entity}` in href templates. */\n entityCode: string;\n /** Active screen, if any. Optional because some pages predate screens. */\n screen?: Screen;\n /** Pathname of the current screen, used by widgets that deep-link back. */\n screenRoute: string;\n /** Currently selected rows (full row objects). */\n selectedRows: Record<string, unknown>[];\n /** PK values of the currently selected rows, in selection order. */\n selectedIds: string[];\n /** Active UI locale (e.g. \"en\", \"ru\"). */\n locale: string;\n /** Opens or switches to a tab in the SDK shell. */\n tabNavigate: (url: string, meta?: TabMeta) => void;\n /** Invokes a server action and returns its response envelope. */\n runActionApi: (code: string, params: unknown) => Promise<unknown>;\n}\n\n/**\n * Telemetry payload emitted on every action click. Stays scalar by design -\n * no Action reference or i18n strings (plan §8 last paragraph).\n *\n * Reason: display.name may carry tenant-customized strings with PII; the\n * payload feeds analytics pipelines and MUST NOT leak them.\n */\nexport interface ActionClickPayload {\n code: string;\n entity: string;\n /** Dispatch mode chosen by ResponsiveActionBar based on which field was set. */\n mode: \"handler\" | \"association\" | \"widget\" | \"href\" | \"ui_steps\" | \"unknown\";\n /** PK of the row the click was bound to, when row-scoped. Empty otherwise. */\n rowId: string;\n}\n\n// ---------------------------------------------------------------------------\n// interpolate helper (plan §8)\n// ---------------------------------------------------------------------------\n\n/**\n * Replaces `${key}` occurrences in `template` with `vars[key]`. Unknown keys\n * are left in place so they show up in error reports rather than being\n * silently dropped.\n *\n * Reason: href templates may reference placeholders the dispatcher does not\n * own (e.g. tenant-defined `${region}`); leaving them intact lets a wrapping\n * layer interpolate next instead of swallowing the error.\n */\nexport function interpolate(template: string, vars: Record<string, string>): string {\n return template.replace(/\\$\\{([^}]+)\\}/g, (match, key) => {\n if (Object.prototype.hasOwnProperty.call(vars, key)) {\n return vars[key];\n }\n return match;\n });\n}\n\n// ---------------------------------------------------------------------------\n// Action dispatch (plan §8 Task 10)\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the dispatch mode for an action based on which field is set.\n * Server validation guarantees exactly one of `handler`/`association`/\n * `widget`/`href` is set, so this picks the first match in priority order\n * (widget > href > handler > association > ui_steps).\n *\n * Reason: keeps the click handler one switch instead of a chain of ifs.\n */\nfunction actionMode(action: Action): \"handler\" | \"association\" | \"widget\" | \"href\" | \"ui_steps\" | \"unknown\" {\n if (action.widget) return \"widget\";\n if (action.href) return \"href\";\n if (action.handler) return \"handler\";\n if (action.association) return \"association\";\n if (action.ui_steps && action.ui_steps.length > 0) return \"ui_steps\";\n return \"unknown\";\n}\n\n/**\n * Resolves the row-scope PK id for `${id}` href interpolation.\n *\n * Uses the entity's declared primary key (single, natural, or composite) via\n * extractPkValues + pkValuesToDetailId, so non-`id` and composite-PK entities\n * navigate correctly. The platform-default `__edit` action is an `${id}`-based\n * href; reading a literal `id` field would interpolate to an empty string for\n * any entity whose PK is not named `id`. Falls back to a raw `id` lookup only\n * when no entity is in scope (defensive - href dispatch always has one).\n */\nfunction rowIdOf(\n row: Record<string, unknown> | undefined,\n entity: Entity | undefined,\n): string {\n if (!row) return \"\";\n if (entity) {\n try {\n return pkValuesToDetailId(entity, extractPkValues(entity, row));\n } catch {\n // Row missing PK fields - fall through to the raw lookup below.\n }\n }\n const id = row[\"id\"];\n return typeof id === \"string\" ? id : id == null ? \"\" : String(id);\n}\n\n/**\n * Click dispatch core. Returns true if the dispatcher handled the click\n * (widget mount in Phase 2, href navigation), false to fall through to\n * the legacy `onRun(code, action)` path (handler/association/ui_steps and\n * any unknown mode for forward compat).\n *\n * Reason: keeps the branch one place; LeafBtn and DropdownLeafItem both\n * call this so behavior cannot drift between inline and overflow buttons.\n */\nfunction dispatchAction(\n code: string,\n action: Action,\n row: Record<string, unknown> | undefined,\n widgetContext: WidgetContext | undefined,\n onActionClick: ((payload: ActionClickPayload) => void) | undefined,\n): boolean {\n const mode = actionMode(action);\n const rowId = rowIdOf(row, widgetContext?.entity);\n const entity = widgetContext?.entityCode ?? \"\";\n\n onActionClick?.({ code, entity, mode, rowId });\n\n // Widget mode: Phase 2 mounts via registry. Phase 1 warns and falls through\n // so href/handler dispatch keeps working on actions that accidentally set\n // widget while the registry is still being built.\n // TODO(phase-2): mount widget from registry instead of falling through.\n if (mode === \"widget\") {\n console.warn(\n `[ResponsiveActionBar] widget \"${action.widget}\" requested for action \"${code}\" but the widget registry ships in Phase 2; falling back to onRun.`,\n );\n return false;\n }\n\n // Href mode: client-side navigation. Requires widgetContext for tabNavigate.\n // tabNavigate expects pathname and search separately; passing a URL with\n // an embedded query string as pathname leaks \"?edit=true\" into the\n // router's pathname matcher and falls back to the list page. Split\n // the interpolated href on the first '?' so platform-default actions\n // like __edit (href = \"<list_route>/<id>?edit=true\") land on the\n // detail screen with the edit flag intact.\n if (mode === \"href\" && widgetContext) {\n // URL-encode the PK before interpolation: natural / composite PK values\n // can carry `/`, spaces, quotes, or brackets (a composite id serializes\n // to a JSON array string) that would otherwise break the path or query.\n // This matches the canonical detail-href builder (buildDetailHrefFromId\n // in types/schema.ts, which encodeURIComponent's the id). `entity` is a\n // slug-safe entity code and needs no encoding.\n const url = interpolate(action.href ?? \"\", {\n entity,\n id: encodeURIComponent(rowId),\n });\n const qIdx = url.indexOf(\"?\");\n if (qIdx === -1) {\n widgetContext.tabNavigate(url);\n } else {\n widgetContext.tabNavigate(url.slice(0, qIdx), { search: url.slice(qIdx + 1) });\n }\n return true;\n }\n\n // Handler / association / ui_steps modes: caller-owned onRun path.\n if (mode === \"handler\" || mode === \"association\" || mode === \"ui_steps\") {\n return false;\n }\n\n // No dispatch mode at all. Server validation should reject this at load\n // time; warn defensively so a misconfigured schema does not silently no-op.\n if (mode === \"unknown\") {\n console.warn(\n `[ResponsiveActionBar] no dispatch mode set on action \"${code}\" (none of handler/association/widget/href/ui_steps).`,\n );\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Public contract\n// ---------------------------------------------------------------------------\n\nexport interface ResponsiveActionBarProps {\n /** RBAC-pruned, post-Task-7 tree from server. */\n layout: ResolvedActionLayoutNode[];\n /** Single-row context (detail / row / quick_peek). */\n row?: Record<string, unknown>;\n /** Bulk context (list_toolbar with selection). */\n rowSelection?: unknown[];\n /** Called when the user triggers an action. */\n onRun: ActionRunHandler;\n /**\n * Width in px already consumed by caller-rendered edit/delete slots on the\n * right side of the container. The bar subtracts this before filling inline.\n */\n reservedRightWidth?: number;\n /** Rendering density / measurement strategy. */\n kind?: \"detail\" | \"toolbar\" | \"row\";\n /**\n * Test seam: when provided, overrides the measurement decision for each\n * node. Return true = node fits inline; false = overflows to More.\n * jsdom always reports 0 widths so tests rely on this predicate.\n */\n measurementOverride?: (node: ResolvedActionLayoutNode) => boolean;\n /**\n * Action catalog map used to resolve display metadata (name, icon, etc.)\n * for each leaf referenced by code. Entity-level map. May be undefined\n * when the caller has no entity context (global actions).\n */\n entityActions?: Record<string, Action>;\n /** Global action catalog fallback. */\n globalActions?: Record<string, Action>;\n /** aria-label for the toolbar root. Defaults to \"Actions\". */\n ariaLabel?: string;\n /**\n * Platform-default kebab affordances appended to row mode after schema items.\n * Used by SmartListPage's row kebab to surface Edit (navigation) and Delete\n * (mutation) without rendering a second kebab next to the schema-defined one.\n * Plan §11: each row exposes ONE kebab \"⋮\"; schema actions own the rendering,\n * platform defaults integrate as trailing items.\n *\n * - normal: rendered after schema-normal items, before any separator. Used\n * for Edit (or schema-declared type=edit actions).\n * - destructive: rendered in the destructive trailing section, after any\n * schema-destructive items. Used for Delete (or schema-declared\n * type=delete actions). Triggers the destructive separator just like\n * schema-destructive leaves do.\n *\n * Only honored when kind=\"row\". Other kinds ignore these (detail/toolbar\n * place edit/delete in their own dedicated UI slots).\n */\n trailingNormalItems?: ReactNode;\n trailingDestructiveItems?: ReactNode;\n /**\n * Dispatch surface for client-side action modes (`href` today, `widget` in\n * Phase 2). When omitted, every action falls through to `onRun`.\n *\n * Reason: keeps the bar usable from contexts that have no router or schema\n * runtime (test fixtures, storybook) while letting production callers wire\n * the full dispatch path.\n */\n widgetContext?: WidgetContext;\n /**\n * Telemetry sink fired on every action click (plan §8). Payload stays\n * scalar - no Action reference or i18n strings.\n *\n * Reason: feeds analytics without leaking tenant-customized labels.\n */\n onActionClick?: (payload: ActionClickPayload) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Counts leaf nodes (recursively) in a resolved tree. */\nfunction countLeaves(nodes: ResolvedActionLayoutNode[]): number {\n let n = 0;\n for (const node of nodes) {\n if (node.kind === \"action\") n++;\n else n += countLeaves(node.items);\n }\n return n;\n}\n\n/** Partitions a flat node list into non-destructive and destructive buckets. */\nfunction partitionDestructive(\n nodes: ResolvedActionLayoutNode[],\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): { normal: ResolvedActionLayoutNode[]; destructive: ResolvedActionLeaf[] } {\n const normal: ResolvedActionLayoutNode[] = [];\n const destructive: ResolvedActionLeaf[] = [];\n for (const node of nodes) {\n if (node.kind === \"action\") {\n const action = lookupAction(node.code, entityActions, globalActions);\n if (action?.destructive) {\n destructive.push(node);\n } else {\n normal.push(node);\n }\n } else {\n // Groups are never destructive at the group level.\n normal.push(node);\n }\n }\n return { normal, destructive };\n}\n\n/**\n * Partitions a flat node list into three groups per plan §3:\n * - primary: `display.primary === true`, leading-anchor pinned.\n * - regular: neither primary nor destructive; this is the only group\n * that overflows to the More dropdown.\n * - destructive: `destructive === true`, trailing-anchor pinned.\n *\n * Groups (kind=\"group\") are never primary or destructive themselves and\n * always land in `regular`.\n *\n * Reason: primary CTAs and destructive actions MUST stay visible regardless\n * of toolbar width; the three-group partition is the carve-out that keeps\n * the overflow algorithm operating only on the middle group.\n */\nfunction partitionThreeGroup(\n nodes: ResolvedActionLayoutNode[],\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): {\n primary: ResolvedActionLeaf[];\n regular: ResolvedActionLayoutNode[];\n destructive: ResolvedActionLeaf[];\n} {\n const primary: ResolvedActionLeaf[] = [];\n const regular: ResolvedActionLayoutNode[] = [];\n const destructive: ResolvedActionLeaf[] = [];\n for (const node of nodes) {\n if (node.kind !== \"action\") {\n // Groups (dropdowns) carry the regular treatment - they can overflow.\n regular.push(node);\n continue;\n }\n const action = lookupAction(node.code, entityActions, globalActions);\n const isDestructive = action?.destructive === true;\n const isPrimary = action?.display?.primary === true;\n if (isDestructive && isPrimary) {\n // Defensive: server validation should reject this combo. Treat as\n // destructive (safer trailing-anchor placement) and warn.\n console.warn(\n `[ResponsiveActionBar] action \"${node.code}\" declares both primary and destructive; treating as destructive.`,\n );\n destructive.push(node);\n continue;\n }\n if (isDestructive) {\n destructive.push(node);\n continue;\n }\n if (isPrimary) {\n primary.push(node);\n continue;\n }\n regular.push(node);\n }\n return { primary, regular, destructive };\n}\n\n/** Entity-wins action lookup. */\nfunction lookupAction(\n code: string,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): Action | undefined {\n if (entityActions && code in entityActions) return entityActions[code];\n if (globalActions && code in globalActions) return globalActions[code];\n return undefined;\n}\n\n/** Resolve display name for an action leaf or group. */\nfunction resolveLeafName(\n code: string,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): string {\n const action = lookupAction(code, entityActions, globalActions);\n return loc(action?.display?.name) || code;\n}\n\n/** Resolve display icon name for an action leaf. */\nfunction resolveLeafIcon(\n code: string,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n): string | undefined {\n const action = lookupAction(code, entityActions, globalActions);\n return action?.display?.icon || undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Disabled tooltip ID helper (stable across renders for same action code)\n// ---------------------------------------------------------------------------\n\nfunction tooltipId(code: string): string {\n return `action-tooltip-${code}`;\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components: inline leaf button\n// ---------------------------------------------------------------------------\n\ninterface LeafBtnProps {\n leaf: ResolvedActionLeaf;\n entityActions: Record<string, Action> | undefined;\n globalActions: Record<string, Action> | undefined;\n onRun: ActionRunHandler;\n loading: boolean;\n onLoadingChange: (loading: boolean) => void;\n measureRef?: (el: HTMLButtonElement | null) => void;\n style?: React.CSSProperties;\n row?: Record<string, unknown>;\n widgetContext?: WidgetContext;\n onActionClick?: (payload: ActionClickPayload) => void;\n}\n\nfunction LeafBtn({\n leaf,\n entityActions,\n globalActions,\n onRun,\n loading,\n onLoadingChange,\n measureRef,\n style,\n row,\n widgetContext,\n onActionClick,\n}: LeafBtnProps) {\n const action = lookupAction(leaf.code, entityActions, globalActions);\n const name = resolveLeafName(leaf.code, entityActions, globalActions);\n const iconName = resolveLeafIcon(leaf.code, entityActions, globalActions);\n const isDestructive = action?.destructive ?? false;\n // display.primary marks the leading-anchor \"call to action\" leaf\n // (plan §3 partition); the Btn primitive renders kind=\"primary\" as\n // the accent-coloured pill. Destructive wins over primary because a\n // dangerous CTA should NEVER be the accent style.\n const isPrimary = !isDestructive && action?.display?.primary === true;\n const isDisabled = !leaf.enabled;\n const tipId = isDisabled ? tooltipId(leaf.code) : undefined;\n\n const handleClick = useCallback(async () => {\n if (!action || isDisabled || loading) return;\n onLoadingChange(true);\n try {\n const handled = dispatchAction(leaf.code, action, row, widgetContext, onActionClick);\n if (!handled) {\n await Promise.resolve(onRun(leaf.code, action));\n }\n } finally {\n onLoadingChange(false);\n }\n }, [action, isDisabled, loading, onRun, leaf.code, onLoadingChange, row, widgetContext, onActionClick]);\n\n const iconEl: ReactNode = loading\n ? <Spinner size={12} />\n : iconName\n ? <DynamicIcon name={iconName} size={12} />\n : null;\n\n return (\n <>\n {tipId && leaf.reason && (\n <span\n id={tipId}\n role=\"tooltip\"\n style={{ position: \"absolute\", width: 1, height: 1, overflow: \"hidden\", clip: \"rect(0,0,0,0)\" }}\n >\n {leaf.reason}\n </span>\n )}\n <Btn\n ref={measureRef}\n kind={isPrimary ? \"primary\" : \"default\"}\n danger={isDestructive}\n icon={iconEl}\n disabled={isDisabled || loading}\n aria-disabled={isDisabled ? \"true\" : undefined}\n aria-describedby={tipId}\n title={isDisabled && leaf.reason ? leaf.reason : name}\n onClick={handleClick}\n style={style}\n >\n {name}\n </Btn>\n </>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components: group trigger button + dropdown\n// ---------------------------------------------------------------------------\n\ninterface GroupBtnProps {\n group: ResolvedActionGroup;\n entityActions: Record<string, Action> | undefined;\n globalActions: Record<string, Action> | undefined;\n onRun: ActionRunHandler;\n loading: string | null;\n onLoadingChange: (code: string | null) => void;\n measureRef?: (el: HTMLButtonElement | null) => void;\n style?: React.CSSProperties;\n}\n\nfunction GroupBtn({\n group,\n entityActions,\n globalActions,\n onRun,\n loading,\n onLoadingChange,\n measureRef,\n style,\n}: GroupBtnProps) {\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n const name = group.name ? loc(group.name) : \"Actions\";\n\n // Merge the measureRef + our triggerRef\n const setRef = useCallback(\n (el: HTMLButtonElement | null) => {\n (triggerRef as React.MutableRefObject<HTMLButtonElement | null>).current = el;\n measureRef?.(el);\n },\n [measureRef],\n );\n\n // Close the menu, then run; return the promise so the dropdown item can\n // await it and hold its loading state for the whole operation.\n const runAndClose = useCallback<ActionRunHandler>(\n (code, action) => {\n setOpen(false);\n return onRun(code, action);\n },\n [onRun],\n );\n\n return (\n <>\n <Btn\n ref={setRef}\n kind=\"default\"\n icon={\n loading != null\n ? <Spinner size={12} />\n : group.icon\n ? <DynamicIcon name={group.icon} size={12} />\n : undefined\n }\n iconRight={<Icon.down size={10} />}\n aria-haspopup=\"menu\"\n aria-expanded={open ? \"true\" : \"false\"}\n title={name}\n onClick={() => setOpen((o) => !o)}\n style={style}\n >\n {name}\n </Btn>\n <Dropdown\n open={open}\n anchor={triggerRef.current}\n onClose={() => setOpen(false)}\n align=\"start\"\n width={200}\n >\n <div role=\"menu\" aria-label={name}>\n {group.items.map((item) =>\n item.kind === \"action\" ? (\n <DropdownLeafItem\n key={item.code}\n leaf={item}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndClose}\n loading={loading === item.code}\n onLoadingChange={(l) => onLoadingChange(l ? item.code : null)}\n />\n ) : (\n <DropdownGroupSection\n key={item.items[0]?.kind === \"action\" ? item.items[0].code : item.icon ?? \"group\"}\n group={item}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndClose}\n loading={loading}\n onLoadingChange={onLoadingChange}\n />\n ),\n )}\n </div>\n </Dropdown>\n </>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components: dropdown items (leaf + nested group section)\n// ---------------------------------------------------------------------------\n\ninterface DropdownLeafItemProps {\n leaf: ResolvedActionLeaf;\n entityActions: Record<string, Action> | undefined;\n globalActions: Record<string, Action> | undefined;\n onRun: ActionRunHandler;\n loading: boolean;\n onLoadingChange: (loading: boolean) => void;\n row?: Record<string, unknown>;\n widgetContext?: WidgetContext;\n onActionClick?: (payload: ActionClickPayload) => void;\n}\n\nfunction DropdownLeafItem({\n leaf,\n entityActions,\n globalActions,\n onRun,\n loading,\n onLoadingChange,\n row,\n widgetContext,\n onActionClick,\n}: DropdownLeafItemProps) {\n const action = lookupAction(leaf.code, entityActions, globalActions);\n const name = resolveLeafName(leaf.code, entityActions, globalActions);\n const iconName = resolveLeafIcon(leaf.code, entityActions, globalActions);\n const isDestructive = action?.destructive ?? false;\n const isDisabled = !leaf.enabled;\n const tipId = isDisabled ? tooltipId(leaf.code) : undefined;\n\n const iconEl: ReactNode = loading\n ? <Spinner size={12} />\n : iconName\n ? <DynamicIcon name={iconName} size={12} />\n : null;\n\n return (\n <>\n {tipId && leaf.reason && (\n <span\n id={tipId}\n role=\"tooltip\"\n style={{ position: \"absolute\", width: 1, height: 1, overflow: \"hidden\", clip: \"rect(0,0,0,0)\" }}\n >\n {leaf.reason}\n </span>\n )}\n <div\n role=\"menuitem\"\n aria-disabled={isDisabled ? \"true\" : undefined}\n aria-describedby={tipId}\n title={isDisabled && leaf.reason ? leaf.reason : name}\n onClick={async () => {\n if (!action || isDisabled || loading) return;\n onLoadingChange(true);\n try {\n const handled = dispatchAction(leaf.code, action, row, widgetContext, onActionClick);\n if (!handled) {\n // Await so the per-item loading state holds for the whole\n // operation; a re-opened menu shows the spinner and the\n // `loading` guard above blocks a second dispatch.\n await Promise.resolve(onRun(leaf.code, action));\n }\n } finally {\n onLoadingChange(false);\n }\n }}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"6px 10px\",\n borderRadius: 5,\n fontSize: 12.5,\n color: isDestructive ? \"var(--danger)\" : isDisabled ? \"var(--text-3)\" : \"var(--text-2)\",\n cursor: isDisabled ? \"not-allowed\" : \"pointer\",\n opacity: isDisabled ? 0.6 : 1,\n }}\n onMouseEnter={(e) => {\n if (!isDisabled) e.currentTarget.style.background = \"var(--surface-3)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = \"transparent\";\n }}\n >\n {iconEl && (\n <span style={{ color: isDestructive ? \"var(--danger)\" : \"var(--text-3)\", display: \"flex\" }}>\n {iconEl}\n </span>\n )}\n <span style={{ flex: 1 }}>{name}</span>\n </div>\n </>\n );\n}\n\ninterface DropdownGroupSectionProps {\n group: ResolvedActionGroup;\n entityActions: Record<string, Action> | undefined;\n globalActions: Record<string, Action> | undefined;\n onRun: ActionRunHandler;\n loading: string | null;\n onLoadingChange: (code: string | null) => void;\n row?: Record<string, unknown>;\n widgetContext?: WidgetContext;\n onActionClick?: (payload: ActionClickPayload) => void;\n}\n\n/**\n * Renders nested groups inside a dropdown as sectioned headers (Jira pattern).\n * Reason: section headers are simpler to implement correctly in a portal-based\n * Dropdown and avoid the focus-trap complexity of nested flyout menus.\n */\nfunction DropdownGroupSection({\n group,\n entityActions,\n globalActions,\n onRun,\n loading,\n onLoadingChange,\n row,\n widgetContext,\n onActionClick,\n}: DropdownGroupSectionProps) {\n const sectionName = group.name ? loc(group.name) : undefined;\n return (\n <>\n {sectionName && (\n <div\n style={{\n padding: \"4px 10px 2px\",\n fontSize: 10.5,\n fontWeight: 600,\n color: \"var(--text-3)\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n {sectionName}\n </div>\n )}\n {group.items.map((item) =>\n item.kind === \"action\" ? (\n <DropdownLeafItem\n key={item.code}\n leaf={item}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loading === item.code}\n onLoadingChange={(l) => onLoadingChange(l ? item.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ) : (\n <DropdownGroupSection\n key={item.icon ?? \"nested\"}\n group={item}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loading}\n onLoadingChange={onLoadingChange}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ),\n )}\n </>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Row-context (kebab) rendering\n// ---------------------------------------------------------------------------\n\ninterface KebabDropdownProps {\n layout: ResolvedActionLayoutNode[];\n entityActions: Record<string, Action> | undefined;\n globalActions: Record<string, Action> | undefined;\n onRun: ActionRunHandler;\n trailingNormalItems?: ReactNode;\n trailingDestructiveItems?: ReactNode;\n row?: Record<string, unknown>;\n widgetContext?: WidgetContext;\n onActionClick?: (payload: ActionClickPayload) => void;\n}\n\nfunction KebabDropdown({\n layout,\n entityActions,\n globalActions,\n onRun,\n trailingNormalItems,\n trailingDestructiveItems,\n row,\n widgetContext,\n onActionClick,\n}: KebabDropdownProps) {\n const btnRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n const [loadingCode, setLoadingCode] = useState<string | null>(null);\n\n // Close the menu, then run; return the promise so the dropdown item can\n // await it and hold its loading state for the whole operation.\n const runAndClose = useCallback<ActionRunHandler>(\n (code, action) => {\n setOpen(false);\n return onRun(code, action);\n },\n [onRun],\n );\n\n const hasTrailingNormal = trailingNormalItems !== undefined && trailingNormalItems !== null && trailingNormalItems !== false;\n const hasTrailingDestructive = trailingDestructiveItems !== undefined && trailingDestructiveItems !== null && trailingDestructiveItems !== false;\n\n if (countLeaves(layout) === 0 && !hasTrailingNormal && !hasTrailingDestructive) return null;\n\n // Partition normal vs destructive for separator placement. Row kebab does\n // not separate primary - everything but destructive lives in the normal bin.\n // Reason: a kebab dropdown has no visual \"leading anchor\" concept; pin\n // semantics only matter for inline bars.\n const { normal, destructive } = partitionDestructive(layout, entityActions, globalActions);\n const hasDestructive = destructive.length > 0 || hasTrailingDestructive;\n\n return (\n <div style={{ display: \"inline-flex\", alignItems: \"center\" }}>\n <IconBtn\n ref={btnRef}\n icon={loadingCode != null ? <Spinner size={14} /> : <Icon.more size={14} />}\n title=\"Actions\"\n size={28}\n onClick={() => setOpen((o) => !o)}\n />\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n align=\"end\"\n width={220}\n >\n <div role=\"menu\" aria-label=\"Row actions\">\n {normal.map((node) =>\n node.kind === \"action\" ? (\n <DropdownLeafItem\n key={node.code}\n leaf={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndClose}\n loading={loadingCode === node.code}\n onLoadingChange={(l) => setLoadingCode(l ? node.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ) : (\n <DropdownGroupSection\n key={node.icon ?? \"group\"}\n group={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndClose}\n loading={loadingCode}\n onLoadingChange={setLoadingCode}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ),\n )}\n {hasTrailingNormal && trailingNormalItems}\n {hasDestructive && <MenuItem divider />}\n {destructive.map((leaf) => (\n <DropdownLeafItem\n key={leaf.code}\n leaf={leaf}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndClose}\n loading={loadingCode === leaf.code}\n onLoadingChange={(l) => setLoadingCode(l ? leaf.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ))}\n {hasTrailingDestructive && trailingDestructiveItems}\n </div>\n </Dropdown>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Measurement types\n// ---------------------------------------------------------------------------\n\n/**\n * Layout decision computed by the measurement pass. Three pinned groups\n * (primary / inline / destructive) plus the overflow bucket that lives\n * inside the \"More\" dropdown.\n *\n * Reason: primary and destructive are anchored - they never feed the\n * overflow algorithm. Only the regular group can spill into More.\n */\ninterface LayoutDecision {\n /** Pinned leading-anchor group; never overflows. Plan §3 partition. */\n primaryInline: ResolvedActionLeaf[];\n /** Regular group rendered inline (the part that fit). */\n inline: ResolvedActionLayoutNode[];\n /** Regular group rendered inside the \"More\" dropdown. */\n overflow: ResolvedActionLayoutNode[];\n /** Pinned trailing-anchor group; never overflows. Plan §3 partition. */\n destructiveInline: ResolvedActionLeaf[];\n}\n\n// ---------------------------------------------------------------------------\n// Inline bar measurement + layout engine\n// ---------------------------------------------------------------------------\n\nconst MORE_BTN_WIDTH = 90; // estimated width of the \"More ▾\" button\nconst DESTRUCTIVE_MIN_WIDTH = 80; // minimum reserved width for one destructive button\n\nfunction computeLayout(\n nodes: ResolvedActionLayoutNode[],\n containerWidth: number,\n reservedRightWidth: number,\n measuredWidths: Map<string, number>,\n entityActions: Record<string, Action> | undefined,\n globalActions: Record<string, Action> | undefined,\n measurementOverride?: (node: ResolvedActionLayoutNode) => boolean,\n): LayoutDecision {\n const { primary, regular, destructive } = partitionThreeGroup(nodes, entityActions, globalActions);\n\n // Width consumed by the pinned anchor groups - the regular fill cannot\n // claim space the anchors will keep.\n const primarySlotWidth =\n primary.length > 0\n ? primary.reduce((sum, p) => sum + (measuredWidths.get(nodeKey(p)) ?? 0), 0)\n : 0;\n const destructiveSlotWidth =\n destructive.length > 0\n ? destructive.reduce((sum, d) => sum + (measuredWidths.get(nodeKey(d)) ?? DESTRUCTIVE_MIN_WIDTH), 0)\n : 0;\n\n // Available width for the regular (overflow-eligible) buttons.\n const available = containerWidth - reservedRightWidth - primarySlotWidth - destructiveSlotWidth;\n\n let used = 0;\n const inline: ResolvedActionLayoutNode[] = [];\n const overflow: ResolvedActionLayoutNode[] = [];\n let needsMoreBtn = false;\n\n for (const node of regular) {\n // nodeKey() generates the same key used by the off-screen measurement layer.\n const w = measuredWidths.get(nodeKey(node)) ?? 0;\n\n if (measurementOverride) {\n if (measurementOverride(node)) {\n inline.push(node);\n used += w;\n } else {\n overflow.push(node);\n needsMoreBtn = true;\n }\n continue;\n }\n\n // Check if this node fits. Reserve space for More button if there are\n // remaining nodes that might overflow.\n const remainingNodes = regular.length - inline.length - overflow.length - 1;\n const moreBtnSpace = remainingNodes > 0 || overflow.length > 0 ? MORE_BTN_WIDTH : 0;\n\n if (used + w + moreBtnSpace <= available) {\n inline.push(node);\n used += w;\n } else {\n overflow.push(node);\n needsMoreBtn = true;\n }\n }\n\n return {\n primaryInline: primary,\n inline,\n overflow: needsMoreBtn ? overflow : [],\n destructiveInline: destructive,\n };\n}\n\n/** Stable key for a node used in the width map. */\nfunction nodeKey(\n node: ResolvedActionLayoutNode,\n): string {\n if (node.kind === \"action\") return `action:${node.code}`;\n const name = node.name ? loc(node.name) : \"\";\n return `group:${name}`;\n}\n\n// ---------------------------------------------------------------------------\n// Main component: inline bar (kind=\"detail\" or kind=\"toolbar\")\n// ---------------------------------------------------------------------------\n\nfunction InlineBar({\n layout,\n entityActions,\n globalActions,\n onRun,\n reservedRightWidth = 0,\n measurementOverride,\n ariaLabel = \"Actions\",\n row,\n widgetContext,\n onActionClick,\n}: Omit<ResponsiveActionBarProps, \"kind\"> & { ariaLabel?: string }) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const measureLayerRef = useRef<HTMLDivElement | null>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const [measuredWidths, setMeasuredWidths] = useState<Map<string, number>>(new Map());\n const [measured, setMeasured] = useState(false);\n const [loadingCode, setLoadingCode] = useState<string | null>(null);\n\n // Track document dir for RTL flip. Updated via MutationObserver so the bar\n // re-renders when the caller switches document.documentElement.dir at runtime\n // (e.g. locale switcher). Reason: plan §9 - trailing-edge must mean trailing\n // in reading order; destructive at visual left in RTL is a mis-click hazard.\n const [isRtl, setIsRtl] = useState(\n () => typeof document !== \"undefined\" && document.documentElement.dir === \"rtl\",\n );\n\n // MutationObserver on <html> for dir and lang attribute changes.\n // - dir change → update isRtl (triggers re-render for layout flip).\n // - lang change → mark as not-measured so the off-screen layer re-measures\n // with the new locale's label widths (Russian/Arabic labels can be 1.3-1.7x\n // wider than English, shifting the overflow boundary).\n useEffect(() => {\n if (typeof MutationObserver === \"undefined\") return;\n const observer = new MutationObserver((mutations) => {\n for (const m of mutations) {\n if (m.attributeName === \"dir\") {\n setIsRtl(document.documentElement.dir === \"rtl\");\n }\n if (m.attributeName === \"lang\") {\n // Locale changed: force re-measure so button widths are re-read with\n // the new locale's translated labels.\n setMeasured(false);\n }\n }\n });\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"dir\", \"lang\"] });\n return () => observer.disconnect();\n }, []);\n\n // More dropdown state.\n const moreBtnRef = useRef<HTMLButtonElement | null>(null);\n const [moreOpen, setMoreOpen] = useState(false);\n\n // Close the More menu, then run; return the promise so DropdownLeafItem can\n // await it and hold the per-item loading guard for the whole operation.\n const runAndCloseMore = useCallback<ActionRunHandler>(\n (code, action) => {\n setMoreOpen(false);\n return onRun(code, action);\n },\n [onRun],\n );\n\n const leafCount = countLeaves(layout);\n\n // When measurementOverride is provided (test seam), skip the off-screen\n // measurement and ResizeObserver entirely - decisions are fully deterministic.\n const hasOverride = !!measurementOverride;\n\n // Measure node widths from the off-screen layer.\n const doMeasure = useCallback(() => {\n if (hasOverride) return; // override bypasses measurement\n if (!measureLayerRef.current) return;\n const children = Array.from(measureLayerRef.current.children) as HTMLElement[];\n const newWidths = new Map<string, number>();\n children.forEach((el) => {\n const key = el.dataset[\"nodeKey\"];\n if (key) newWidths.set(key, el.getBoundingClientRect().width || el.offsetWidth);\n });\n setMeasuredWidths(newWidths);\n setMeasured(true);\n }, [hasOverride]);\n\n // Trigger re-measure when layout changes (content change, not just resize).\n // We stringify the layout keys as a fast structural fingerprint.\n const layoutFingerprint = useMemo(\n () => JSON.stringify(layout.map((n) => nodeKey(n))),\n [layout],\n );\n\n useLayoutEffect(() => {\n if (hasOverride) {\n // Test seam: mark measured immediately, skip off-screen work.\n setMeasured(true);\n return;\n }\n // Reset measured state when layout changes so we re-measure.\n setMeasured(false);\n // Schedule measure after the off-screen layer renders.\n const id = requestAnimationFrame(() => {\n doMeasure();\n });\n return () => cancelAnimationFrame(id);\n }, [layoutFingerprint, doMeasure, hasOverride]);\n\n // 16ms fallback: in case ResizeObserver fires after our rAF.\n useEffect(() => {\n if (hasOverride || measured) return;\n const id = setTimeout(() => {\n doMeasure();\n setMeasured(true);\n }, 16);\n return () => clearTimeout(id);\n }, [measured, doMeasure, hasOverride]);\n\n // ResizeObserver on container for width tracking.\n // Guard: ResizeObserver is not available in jsdom; skip gracefully in test env.\n useEffect(() => {\n if (hasOverride) return; // not needed when override is active\n const el = containerRef.current;\n if (!el) return;\n if (typeof ResizeObserver === \"undefined\") {\n // jsdom: fall back to container's current width (will be 0, which is\n // fine because the override bypasses the container-width check).\n setContainerWidth(el.getBoundingClientRect().width);\n return;\n }\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (entry) {\n setContainerWidth(entry.contentRect.width);\n }\n });\n observer.observe(el);\n // Read initial width.\n setContainerWidth(el.getBoundingClientRect().width);\n return () => observer.disconnect();\n }, [hasOverride]);\n\n const decision = useMemo<LayoutDecision>(() => {\n // When override is active, treat container as infinitely wide and skip\n // the measured gate. The override predicate drives all layout decisions.\n if (hasOverride) {\n return computeLayout(\n layout,\n Number.MAX_SAFE_INTEGER,\n reservedRightWidth,\n measuredWidths,\n entityActions,\n globalActions,\n measurementOverride,\n );\n }\n if (!measured) {\n // Before off-screen widths are known: show nothing inline. The\n // wrapper's visibility:hidden also gates this so callers never see\n // a flash of an empty bar.\n return { primaryInline: [], inline: [], overflow: [], destructiveInline: [] };\n }\n if (containerWidth === 0) {\n // Catch-22 escape: parent flex sized us to 0 because we have no\n // in-flow content yet (the measurement layer is position:absolute).\n // Render every leaf inline at intrinsic width so the bar contributes\n // its natural size to the parent. The ResizeObserver fires on the\n // next paint with the real container width and computeLayout\n // re-runs to collapse items into overflow if space is tight.\n // Reason: without this fallback, an entity that lacks a manually-\n // declared list_toolbar action and relies on the __create platform\n // default would render an empty toolbar (regression after the\n // cleanup commit that removed the bespoke \"New {entityName}\" Btn\n // fallback). The off-screen measurement layer keeps node-key sizes\n // available; treating the container as effectively unbounded here\n // matches the override-mode semantics already used by tests.\n return computeLayout(\n layout,\n Number.MAX_SAFE_INTEGER,\n reservedRightWidth,\n measuredWidths,\n entityActions,\n globalActions,\n measurementOverride,\n );\n }\n return computeLayout(\n layout,\n containerWidth,\n reservedRightWidth,\n measuredWidths,\n entityActions,\n globalActions,\n measurementOverride,\n );\n }, [hasOverride, measured, containerWidth, layout, reservedRightWidth, measuredWidths, entityActions, globalActions, measurementOverride]);\n\n // Empty bar: render nothing (plan §9 \"MUST render nothing when 0 leaves\").\n if (leafCount === 0) return null;\n\n const hasOverflow = decision.overflow.length > 0;\n\n return (\n <div\n ref={containerRef}\n role=\"toolbar\"\n aria-label={ariaLabel}\n style={{\n display: \"flex\",\n flexDirection: isRtl ? \"row-reverse\" : \"row\",\n alignItems: \"center\",\n gap: 4,\n // Hide until first measurement to avoid FOUC (plan §9).\n // Override active = test seam = always visible.\n visibility: (measured || hasOverride) ? \"visible\" : \"hidden\",\n position: \"relative\",\n }}\n >\n {/* Off-screen measurement layer. */}\n <div\n ref={measureLayerRef}\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n display: \"flex\",\n gap: 4,\n top: 0,\n left: 0,\n }}\n >\n {layout.map((node) => {\n const key = nodeKey(node);\n if (node.kind === \"action\") {\n const action = lookupAction(node.code, entityActions, globalActions);\n const name = resolveLeafName(node.code, entityActions, globalActions);\n const iconName = resolveLeafIcon(node.code, entityActions, globalActions);\n const iconEl = iconName ? <DynamicIcon name={iconName} size={12} /> : null;\n return (\n <Btn\n key={key}\n data-node-key={key}\n icon={iconEl}\n danger={action?.destructive}\n tabIndex={-1}\n >\n {name}\n </Btn>\n );\n } else {\n const groupName = node.name ? loc(node.name) : \"Actions\";\n return (\n <Btn\n key={key}\n data-node-key={key}\n iconRight={<Icon.down size={10} />}\n tabIndex={-1}\n >\n {groupName}\n </Btn>\n );\n }\n })}\n </div>\n\n {/* Pinned leading-anchor group: display.primary === true. Never overflows. */}\n {decision.primaryInline.map((leaf) => (\n <LeafBtn\n key={nodeKey(leaf)}\n leaf={leaf}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loadingCode === leaf.code}\n onLoadingChange={(l) => setLoadingCode(l ? leaf.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ))}\n\n {/* Inline buttons (regular group: neither primary nor destructive). */}\n {decision.inline.map((node) => {\n const key = nodeKey(node);\n if (node.kind === \"action\") {\n return (\n <LeafBtn\n key={key}\n leaf={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loadingCode === node.code}\n onLoadingChange={(l) => setLoadingCode(l ? node.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n );\n } else {\n return (\n <GroupBtn\n key={key}\n group={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loadingCode}\n onLoadingChange={setLoadingCode}\n />\n );\n }\n })}\n\n {/* More overflow dropdown. */}\n {hasOverflow && (\n <>\n <Btn\n ref={moreBtnRef}\n kind=\"default\"\n iconRight={<Icon.down size={10} />}\n aria-haspopup=\"menu\"\n aria-expanded={moreOpen ? \"true\" : \"false\"}\n onClick={() => setMoreOpen((o) => !o)}\n >\n Actions\n </Btn>\n <Dropdown\n open={moreOpen}\n anchor={moreBtnRef.current}\n onClose={() => setMoreOpen(false)}\n align={isRtl ? \"start\" : \"end\"}\n width={220}\n >\n <div role=\"menu\" aria-label=\"More actions\">\n {decision.overflow.map((node) =>\n node.kind === \"action\" ? (\n <DropdownLeafItem\n key={node.code}\n leaf={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndCloseMore}\n loading={loadingCode === node.code}\n onLoadingChange={(l) => setLoadingCode(l ? node.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ) : (\n <DropdownGroupSection\n key={nodeKey(node)}\n group={node}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={runAndCloseMore}\n loading={loadingCode}\n onLoadingChange={setLoadingCode}\n />\n ),\n )}\n </div>\n </Dropdown>\n </>\n )}\n\n {/* Destructive leaves always trail (plan §8 D10, §9). */}\n {decision.destructiveInline.map((leaf) => (\n <LeafBtn\n key={nodeKey(leaf)}\n leaf={leaf}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n loading={loadingCode === leaf.code}\n onLoadingChange={(l) => setLoadingCode(l ? leaf.code : null)}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n ))}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Toolbar variant: pre-measures static + bulk and swaps without re-measure\n// ---------------------------------------------------------------------------\n\ninterface ToolbarBarProps extends Omit<ResponsiveActionBarProps, \"kind\"> {\n ariaLabel?: string;\n}\n\nfunction ToolbarBar({ layout, rowSelection = [], ...rest }: ToolbarBarProps) {\n // Static variant: layout filtered for bulk_selection_required=false context.\n // Bulk variant: layout filtered for bulk_selection_required=true context.\n // The server already pre-filtered RBAC. The layout tree here is already\n // filtered per-slot by the caller. We only switch which variant shows.\n //\n // For selection-state performance (plan §9), we keep BOTH variants mounted\n // but toggle visibility. This avoids re-measure on selection toggle.\n\n const isBulk = rowSelection.length > 0;\n\n return (\n <div style={{ position: \"relative\" }}>\n {/* Static variant (no selection). */}\n <div\n aria-hidden={isBulk ? \"true\" : undefined}\n style={{ display: isBulk ? \"none\" : undefined }}\n >\n <InlineBar layout={layout} rowSelection={[]} {...rest} />\n </div>\n {/* Bulk variant (selection > 0). */}\n <div\n aria-hidden={!isBulk ? \"true\" : undefined}\n style={{ display: !isBulk ? \"none\" : undefined }}\n >\n <InlineBar layout={layout} rowSelection={rowSelection} {...rest} />\n </div>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Main export\n// ---------------------------------------------------------------------------\n\n/**\n * ResponsiveActionBar renders a resolved action layout tree.\n *\n * kind=\"detail\" or \"toolbar\": inline bar with overflow \"More\" dropdown.\n * kind=\"row\": single kebab \"⋮\" with all actions in dropdown.\n * kind default: \"toolbar\".\n */\nexport function ResponsiveActionBar({\n layout,\n row,\n rowSelection,\n onRun,\n reservedRightWidth = 0,\n kind = \"toolbar\",\n measurementOverride,\n entityActions,\n globalActions,\n ariaLabel = \"Actions\",\n trailingNormalItems,\n trailingDestructiveItems,\n widgetContext,\n onActionClick,\n}: ResponsiveActionBarProps) {\n const leafCount = countLeaves(layout);\n\n // Row mode: ONE kebab combining schema actions + caller-provided platform\n // defaults (Edit/Delete). The schema-only check below is for kind !== \"row\".\n if (kind === \"row\") {\n // Empty bar guard moves into KebabDropdown so trailing items can still\n // surface when schema layout is empty but Edit/Delete affordances exist.\n return (\n <KebabDropdown\n layout={layout}\n entityActions={entityActions}\n globalActions={globalActions}\n onRun={onRun}\n trailingNormalItems={trailingNormalItems}\n trailingDestructiveItems={trailingDestructiveItems}\n row={row}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n );\n }\n\n // Empty bar: no DOM element (plan §9 \"render nothing when 0 leaves\").\n // Only applies to non-row kinds; row mode handled above.\n if (leafCount === 0) return null;\n\n if (kind === \"toolbar\") {\n return (\n <ToolbarBar\n layout={layout}\n row={row}\n rowSelection={rowSelection}\n onRun={onRun}\n reservedRightWidth={reservedRightWidth}\n measurementOverride={measurementOverride}\n entityActions={entityActions}\n globalActions={globalActions}\n ariaLabel={ariaLabel}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n );\n }\n\n // kind=\"detail\"\n return (\n <InlineBar\n layout={layout}\n row={row}\n rowSelection={rowSelection}\n onRun={onRun}\n reservedRightWidth={reservedRightWidth}\n measurementOverride={measurementOverride}\n entityActions={entityActions}\n globalActions={globalActions}\n ariaLabel={ariaLabel}\n widgetContext={widgetContext}\n onActionClick={onActionClick}\n />\n );\n}\n","import { isUIAction, executeUISteps, type UIActionContext } from \"@/lib/ui-actions\";\nimport type { UIStep } from \"@/types/schema\";\n\n// dispatchAction owns the action-dispatch preamble shared by every\n// ResponsiveActionBar onRun callback (SmartListPage row/toolbar/bulk,\n// SmartDetailPage header, AssociatedBlock child):\n//\n// 1. UI actions (ui_steps, no handler) run entirely client-side - no server\n// round-trip (plan §7 / §3.10). The step sequence runs against `uiContext`\n// (the current row, if any) and the function returns null.\n// 2. Server actions resolve their fully-qualified dispatch code: an explicit\n// `action.entity` prefix wins; otherwise the caller's `entityFallback`\n// prefixes the bare code (SmartListPage/SmartDetailPage pass the bare\n// `code` through unprefixed; AssociatedBlock prefixes with the association\n// code). The function returns that `fullCode` for the caller to dispatch.\n//\n// Returns null when the action was handled client-side (caller must return\n// early), or the fully-qualified `fullCode` string for server dispatch.\nexport function dispatchAction(\n code: string,\n action: { ui_steps?: UIStep[]; handler?: string; entity?: string },\n uiContext: UIActionContext,\n entityFallback?: string,\n): string | null {\n if (isUIAction(action)) {\n void executeUISteps(action.ui_steps!, uiContext);\n return null;\n }\n if (action.entity) {\n return `${action.entity}.${code}`;\n }\n return entityFallback ? `${entityFallback}.${code}` : code;\n}\n","import { useState, useCallback, useRef, useMemo, useEffect, type CSSProperties, type KeyboardEvent } from \"react\";\nimport { Btn, Icon, IconBtn, SectionCard, Spinner } from \"@/components/primitives\";\nimport {\n dataTableStyle,\n dataTableHeadCellStyle,\n dataTableCellStyle,\n} from \"@/components/primitives/dataTableStyles\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { entityAllowsMutation, extractPkValues, fieldDisplayName, getDisplayValue, getPrimaryKeyFields, loc, resolveDetailRoute } from \"@/types/schema\";\nimport type { LayoutNode, Entity, Action, HandlerParam, PkValues } from \"@/types/schema\";\nimport { OrderedMap } from \"@/types/schema\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { useChildrenData } from \"@/hooks/useChildrenData\";\nimport { useAssociatedData } from \"@/hooks/useAssociatedData\";\nimport { updateEntity, createEntity, deleteEntity, encodeObjectId, executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { ENTITY_CHILDREN_KEY } from \"@/hooks/useChildrenData\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { ConfirmDialog } from \"@/components/shared/ConfirmDialog\";\nimport { ActionButton } from \"@/components/shared/ActionButton\";\nimport { RowActionsKebab } from \"@/components/shared/RowActionsKebab\";\nimport { AssociatedToolbar } from \"./AssociatedToolbar\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\nimport type { Refs } from \"@/types/api\";\nimport { applyRowState, defaultFlatForSlot, type ResolvedActionLayoutNode } from \"@/lib/actions\";\nimport { ResponsiveActionBar, type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { dispatchAction } from \"@/lib/action-dispatch\";\n\n// Per-row state for merge-debounce saving.\ninterface AssociatedRowState {\n buffer: Record<string, unknown>; // accumulated dirty fields\n ver: number; // monotonic counter, incremented on every cell change\n saving: boolean; // true while PATCH is in flight\n error?: string; // last save error (cleared on next successful save)\n}\n\ninterface AssociatedBlockProps {\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\n// resolveAssociatedRowActions picks the child-entity actions that render\n// per-row in an associated-section's table. Rules:\n// - visibility must include `list_row`.\n// - drop `association` nav-only actions (no server dispatch).\n//\n// Note: bulk_selection_required is NOT filtered here. That flag gates\n// toolbar-context rendering (action disabled until rows are selected);\n// in row-context the row IS the implicit selection, so the action\n// should run fine. Actions that want to appear ONLY in the toolbar\n// should simply omit `list_row` from their visibility list.\n// Exported for unit testing. See AssociatedRow for invocation shape.\nexport function resolveAssociatedRowActions(\n actions: Record<string, Action> | undefined,\n): Array<{ code: string; action: Action }> {\n if (!actions) return [];\n const out: Array<{ code: string; action: Action }> = [];\n for (const [code, action] of Object.entries(actions)) {\n const vis = action.visibility ?? [];\n if (!vis.includes(\"list_row\")) continue;\n if (action.association) continue;\n out.push({ code, action });\n }\n return out;\n}\n\n// resolveAssociatedToolbarActions picks the child-entity actions that render\n// in an associated-section's header. Rules:\n// - visibility must include `list_toolbar` (reused as the \"toolbar slot\"\n// token across list screens AND associated sections; see docs/dsl.md).\n// - drop `association` nav-only actions (no server dispatch here).\n// - drop platform-default actions (`__`-prefixed: __create / __edit /\n// __delete). Reason: these are screen-navigation defaults whose href is\n// `${listRoute}/...` - they assume the entity has a standalone list/detail\n// screen and navigate away from the parent record. An associated section\n// is a parent-scoped inline surface: create/edit/delete happen inline\n// (\"Add row\", cell edit, \"Delete row\"). Rendering `__create` here produced\n// a duplicate \"New X\" button that 404s for screenless sub-entities. The\n// `__` namespace is reserved for platform defaults (tenant actions cannot\n// use it), so the prefix check is exact.\n// - inject the FK to the parent as a hidden default so handlers like\n// create_api_key scope the new row under the parent user without the\n// admin typing user_id by hand. The FK is simultaneously removed from\n// visible `resolved_params` so the params dialog doesn't render the\n// field (and can't clobber the hidden default in `buildInitialFormData`,\n// which iterates visible params and overwrites).\n// Standalone list screens still use the unmodified action, so\n// /admin/api-keys continues to let admins target arbitrary users.\n// NOTE: the old `bulk_selection_required` filter was dropped in\n// unified-toolbar Phase 1 (plan §1-§2). The flag is removed from the wire\n// format; Task 38 wires the replacement multi-selection semantics through\n// the unified toolbar pipeline. Reason: keep the data path compiling without\n// inventing a fake field-presence check that would mis-categorize actions.\n// TODO(task-38): reinstate the proper bulk-mode filter.\n// Exported for unit testing; the component memoizes on (actions, fk, parentId).\nexport function resolveAssociatedToolbarActions(\n actions: Record<string, Action> | undefined,\n fkField: string,\n parentId: string,\n): Array<{ code: string; action: Action }> {\n if (!actions) return [];\n const out: Array<{ code: string; action: Action }> = [];\n for (const [code, action] of Object.entries(actions)) {\n const vis = action.visibility ?? [];\n if (!vis.includes(\"list_toolbar\")) continue;\n if (action.association) continue;\n // Platform-default screen-navigation actions never belong in a\n // parent-scoped associated section - see the rules above.\n if (code.startsWith(\"__\")) continue;\n\n let cloned: Action = {\n ...action,\n resolved_hidden_defaults: {\n ...(action.resolved_hidden_defaults ?? {}),\n [fkField]: parentId,\n },\n };\n\n // If the FK field is a visible param on this action, strip it out — we\n // supply it via hidden defaults. Leaving it in resolved_params would\n // cause ActionDialog to render an editable input AND overwrite the\n // hidden default with \"\" during buildInitialFormData.\n if (action.resolved_params?.has(fkField)) {\n const filtered = action.resolved_params\n .items()\n .filter((p: HandlerParam) => p.name !== fkField);\n cloned = { ...cloned, resolved_params: new OrderedMap<HandlerParam>(filtered) };\n }\n\n out.push({ code, action: cloned });\n }\n return out;\n}\n\n// AssociatedBlock renders a filtered list of an associated entity (independent entity\n// with a FK ref field pointing back to the parent entity). Unlike ChildrenBlock,\n// associated entities are not aggregate children - they have independent lifecycles.\n// Supports inline editing: click-to-edit cells, add/delete rows with immediate saves.\n// When searchable or filters are configured, renders a toolbar and uses cursor-based pagination.\n// Server-side ordering only: set `sort:` on the layout node (e.g. `sort: created_at:desc`).\n// Manual drag-reorder via `position_field` is not supported on associated blocks - it is\n// semantically broken under pagination and prohibitively expensive under per-row save.\n// Use `children:` if you need manual ordering.\n// AssociatedBlock is a thin wrapper around AssociatedBlockBody that\n// short-circuits on cheap, render-free conditions BEFORE any hook\n// runs. Splitting this way keeps the body component's hook count\n// stable across renders — moving the early returns inside the body\n// would make the hook count differ between \"render with assoc\" and\n// \"render without assoc\" and trigger React #310. This is the\n// canonical Rules-of-Hooks workaround when an early return depends\n// on data the hooks downstream consume.\n// resolveAssociatedForeignKey resolves the column on `assocCode` that points\n// back to `parentEntityCode` for an `associated:` layout node. Two paths, in\n// priority order, mirroring engine/registry.go validateAssociated so the SDK\n// accepts every schema the Go loader accepts:\n//\n// 1. An explicit `children:` relation on the parent entity. When the parent\n// declares this code as a child with a single-field foreign_key, that wins.\n// Reason: it pins the FK for child entities whose join column is a plain\n// column (not a `ref`), and disambiguates junction tables that carry\n// multiple refs back to the parent.\n// 2. An auto-discovered `ref` field on the associated entity pointing at the\n// parent. Also covers shared-table aliases (profile / users) by comparing\n// physical table names.\n//\n// Returns null when neither path resolves a FK.\nexport function resolveAssociatedForeignKey(\n entities: Record<string, Entity>,\n parentEntityCode: string,\n assocCode: string,\n): string | null {\n const assocEntity = entities[assocCode];\n if (!assocEntity) return null;\n const parentEntity = entities[parentEntityCode];\n\n const childRel = parentEntity?.children?.find((c) => c.entity === assocCode);\n if (childRel) {\n const fk = childRel.foreign_key;\n if (typeof fk === \"string\") return fk;\n if (Array.isArray(fk) && fk.length === 1) return fk[0];\n }\n\n const parentTable = (parentEntity as { table?: string } | undefined)?.table;\n for (const [name, field] of assocEntity.fields) {\n const refEntityCode = field.ref?.entity;\n if (!refEntityCode) continue;\n if (refEntityCode === parentEntityCode) return name;\n if (parentTable) {\n const refEntity = entities[refEntityCode];\n const refTable = (refEntity as { table?: string } | undefined)?.table;\n if (refTable && refTable === parentTable) return name;\n }\n }\n return null;\n}\n\n// associatedEditableColumns returns the columns inline-editable on an EXISTING\n// associated row: every column except the FK, auto fields, and primary-key\n// fields. PK fields are row identity, immutable on update - the store keys the\n// UPDATE SET on non-PK columns and drops writes to them (data_update_where.go),\n// so offering them as editable cells only yields a confusing revert-on-refresh.\n// getPrimaryKeyFields covers both single-field `primary: true` and composite\n// `primary_key:` declarations; the bare `field.primary` flag misses composite-\n// PK members such as a natural key column `code`.\nexport function associatedEditableColumns(\n entity: Entity,\n columns: string[],\n foreignKey: string,\n): Set<string> {\n const pkFields = new Set(getPrimaryKeyFields(entity));\n return new Set(\n columns.filter((col) => {\n if (col === foreignKey) return false;\n if (pkFields.has(col)) return false;\n const field = entity.fields.get(col);\n if (!field) return false;\n if (field.auto || field.primary) return false;\n return true;\n }),\n );\n}\n\n// associatedDraftEditableColumns returns the columns editable in the NEW-row\n// draft: every column except the FK and auto fields. Unlike\n// associatedEditableColumns it keeps primary-key fields, because a new row\n// legitimately supplies its own primary key on create - including a natural or\n// composite key like `code`. Without this, composite-PK entities whose key is\n// not auto-generated could never be created from the UI.\nexport function associatedDraftEditableColumns(\n entity: Entity,\n columns: string[],\n foreignKey: string,\n): Set<string> {\n return new Set(\n columns.filter((col) => {\n if (col === foreignKey) return false;\n const field = entity.fields.get(col);\n if (!field) return false;\n if (field.auto) return false;\n return true;\n }),\n );\n}\n\n// findMissingRequiredAssociatedFields returns the column codes that are\n// required but missing on a candidate associated row (a new-row draft or an\n// existing row merged with its edit buffer). A field is \"missing\" when its\n// value is null/undefined or the empty string; 0 and false are valid.\n//\n// Skipped from the check, matching the server's validateRequired:\n// - auto fields and primary-key fields (server-supplied / immutable)\n// - the foreign key (supplied automatically from the parent record)\n// - the tenant_key field (server-injected from caller context)\n//\n// Shared by the create flow (handleConfirmDraft) and the row_edit flow\n// (handleConfirmEdit) so both apply byte-identical validation rules.\n// Exported for unit testing.\nexport function findMissingRequiredAssociatedFields(\n values: Record<string, unknown>,\n columns: string[],\n entity: Entity,\n foreignKey: string,\n): string[] {\n const errors: string[] = [];\n for (const col of columns) {\n const field = entity.fields.get(col);\n if (!field || !field.required) continue;\n if (field.auto || field.primary) continue;\n if (col === foreignKey) continue;\n if (entity.tenant_key && col === entity.tenant_key) continue;\n const val = values[col];\n if (val == null || val === \"\") {\n errors.push(col);\n }\n }\n return errors;\n}\n\nexport function AssociatedBlock({ node, ctx }: AssociatedBlockProps) {\n const assocCode = node.associated;\n if (!assocCode) return null;\n if (ctx.isNew) return null;\n const assocEntity = ctx.schema.entities[assocCode];\n if (!assocEntity) {\n return (\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>\n Entity not found: {assocCode}\n </div>\n );\n }\n // FK is resolved in this outer wrapper so AssociatedBlockBody keeps a stable\n // hook count: the FK-missing branch returns null here, never inside the\n // hook-laden body.\n const fkField = resolveAssociatedForeignKey(ctx.schema.entities, ctx.entityCode, assocCode);\n if (!fkField) {\n return (\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>\n No FK found for {assocCode} → {ctx.entityCode}\n </div>\n );\n }\n return (\n <AssociatedBlockBody\n node={node}\n ctx={ctx}\n assocCode={assocCode}\n assocEntity={assocEntity}\n fkField={fkField}\n />\n );\n}\n\ninterface AssociatedBlockBodyProps extends AssociatedBlockProps {\n assocCode: string;\n assocEntity: LayoutContext[\"schema\"][\"entities\"][string];\n fkField: string;\n}\n\nfunction AssociatedBlockBody({ node, ctx, assocCode, assocEntity, fkField }: AssociatedBlockBodyProps) {\n // Determine columns: use node.columns override or infer from field_order.\n const columns = node.columns && node.columns.length > 0\n ? node.columns.filter((col) => {\n if (col === fkField) return false;\n const field = assocEntity.fields.get(col);\n return !!field;\n })\n : assocEntity.fields.names().filter(\n (col) => {\n if (col === fkField) return false;\n const field = assocEntity.fields.get(col);\n if (!field) return false;\n if (field.primary) return false;\n if (field.display?.is_listable === false) return false;\n return true;\n },\n );\n\n // Resolve interaction: explicit node.interaction wins; otherwise default to\n // \"navigate\" for associated records (they are first-class entities with\n // their own detail screens). \"inline_edit\" only when opted in.\n const canUpdate = entityAllowsMutation(assocEntity, \"update\");\n const interaction: \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\" =\n node.interaction ?? (canUpdate ? \"navigate\" : \"readonly\");\n // Resolve editable: node.editable overrides; navigate/readonly force off.\n // Reason: both inline_edit and row_edit support add/edit/delete of rows;\n // they differ only in HOW an existing row enters edit mode (click-to-edit\n // cell vs. a per-row Edit button), so `editable` is true for either.\n const editable =\n (interaction === \"inline_edit\" || interaction === \"row_edit\") &&\n canUpdate &&\n node.editable !== false;\n\n const label = loc(assocEntity.display?.name_plural) || loc(assocEntity.display?.name) || assocCode;\n\n // Determine if toolbar features are enabled.\n const hasToolbar = node.searchable === true || (node.filters && node.filters.length > 0);\n\n // Child entity actions eligible for the associated-section header. See\n // resolveAssociatedToolbarActions for the selection rules and the FK\n // prefill rationale. These are also used as the action catalog for\n // ResponsiveActionBar so the FK defaults are carried through the onRun path.\n const associatedToolbarActions = useMemo(\n () => resolveAssociatedToolbarActions(assocEntity.actions, fkField, ctx.recordId),\n [assocEntity.actions, fkField, ctx.recordId],\n );\n\n // Build a map of FK-injected cloned actions for use as ResponsiveActionBar's\n // entity catalog. Entity wins: list_toolbar-visible clones with FK defaults.\n const childActionsMap = useMemo<Record<string, Action>>(() => {\n const map: Record<string, Action> = {};\n for (const { code, action } of associatedToolbarActions) {\n map[code] = action;\n }\n return map;\n }, [associatedToolbarActions]);\n\n // Resolve the toolbar layout. If the child entity declares action_layout,\n // use it (server has already RBAC-pruned it). Otherwise fall back to\n // defaultFlatForSlot over the cloned (FK-injected) actions map (plan §2.1\n // row #5/#6: child-entity action_layout must surface in associated toolbar).\n const childLayout = useMemo<ResolvedActionLayoutNode[]>(() => {\n const serverLayout = (assocEntity.action_layout ?? null) as ResolvedActionLayoutNode[] | null;\n // When using the server layout, apply applyRowState over the full\n // assocEntity.actions (not just list_toolbar subset) so visible_if /\n // enabled_if on any referenced code resolves correctly.\n return applyRowState(\n serverLayout ?? defaultFlatForSlot(childActionsMap, \"list_toolbar\"),\n // Use the FK-injected map so the catalog resolver in applyRowState gets\n // the same cloned actions that will be passed to ResponsiveActionBar.\n childActionsMap,\n undefined,\n {},\n [],\n );\n }, [assocEntity.action_layout, childActionsMap]);\n\n const { run } = useActionRunner();\n const tabNavigate = useTabNavigate();\n const userLocale = useUserLocale();\n // WidgetContext lets ResponsiveActionBar's internal dispatcher route\n // href-mode actions (e.g. platform-default `__create` whose Href is\n // server-expanded to `/admin/<entity>/new`) through `tabNavigate` instead\n // of falling through to `onRun = runChildAction`, which would POST the\n // href-only action to /api/actions and surface BAD_ACTION_MODE\n // (\"action has no server-callable handler\"). Toolbar context, so the row\n // selection is intentionally empty - href dispatch reads only `entity`,\n // `entityCode`, and `tabNavigate`. `screenRoute` carries the current SPA\n // path purely as a fallback for any widget that deep-links back; the\n // server has already expanded `${listRoute}` in platform defaults, so\n // its value does not feed __create / __edit dispatch.\n const widgetContext = useMemo<WidgetContext>(() => ({\n entity: assocEntity as Entity,\n entityCode: assocCode,\n screenRoute: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n selectedRows: [],\n selectedIds: [],\n locale: userLocale,\n tabNavigate: (url, meta) => tabNavigate(url, { title: meta?.title, icon: meta?.icon, search: meta?.search }),\n runActionApi: (code, params) => runActionApi(code, params as Parameters<typeof runActionApi>[1]),\n }), [assocEntity, assocCode, userLocale, tabNavigate]);\n const queryClient = useQueryClient();\n const invalidateAssociated = useCallback(() => {\n queryClient.invalidateQueries({\n predicate: (query) => {\n const key = query.queryKey;\n return key[0] === ENTITY_CHILDREN_KEY && key[3] === assocCode;\n },\n });\n }, [queryClient, assocCode]);\n\n const runChildAction = useCallback(\n (code: string, action: Action) => {\n const fullCode = dispatchAction(code, action, {}, assocCode);\n if (fullCode === null) return;\n // Return the run promise so ResponsiveActionBar awaits it and keeps the\n // associated-block button disabled with a spinner for the whole operation.\n return run({\n action,\n title: loc(action.display?.name) || code,\n execute: async (params) => {\n const result = await runActionApi(fullCode, buildActionBody(params, undefined));\n invalidateAssociated();\n return result;\n },\n });\n },\n [run, assocCode, invalidateAssociated],\n );\n\n // Signal to ask the child table to start an empty draft row. Incrementing\n // the counter re-runs the child's effect and triggers handleStartDraft.\n // Decouples \"button lives in section header\" (here) from \"state lives in\n // the table component\" (child) without lifting all draft state upward.\n const [startDraftSignal, setStartDraftSignal] = useState(0);\n\n // Toolbar: child-entity actions + the \"Add row\" affordance. Lives in the\n // SectionCard header (right slot), or above the table when already carded.\n const toolbar = (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n {/* Prefer ResponsiveActionBar when action_layout is declared on the\n child entity (plan §2.1 row #5/#6). Legacy ActionButton fallback\n keeps backward compat for entities without action_layout. */}\n {assocEntity.action_layout ? (\n childLayout.length > 0 && (\n <ResponsiveActionBar\n kind=\"toolbar\"\n layout={childLayout}\n entityActions={childActionsMap}\n onRun={runChildAction}\n widgetContext={widgetContext}\n ariaLabel={`${label} toolbar actions`}\n />\n )\n ) : (\n associatedToolbarActions.map(({ code, action }) => (\n <ActionButton\n key={code}\n actionCode={code}\n action={action}\n entityCode={assocCode}\n variant=\"ghost\"\n size=\"sm\"\n onSuccess={invalidateAssociated}\n />\n ))\n )}\n {editable && (\n <Btn\n kind=\"ghost\"\n size=\"xs\"\n icon={<Icon.plus size={12} />}\n onClick={() => setStartDraftSignal((n) => n + 1)}\n >\n Add row\n </Btn>\n )}\n </div>\n );\n\n const table = hasToolbar ? (\n <PaginatedAssociatedTable\n entityCode={assocCode}\n entity={assocEntity}\n parentEntity={ctx.entityCode}\n parentId={ctx.recordId}\n foreignKey={fkField}\n columns={columns}\n editable={editable}\n interaction={interaction}\n ctx={ctx}\n searchable={node.searchable}\n filterFields={node.filters}\n sort={node.sort}\n startDraftSignal={startDraftSignal}\n />\n ) : (\n <SimpleAssociatedTable\n entityCode={assocCode}\n entity={assocEntity}\n parentEntity={ctx.entityCode}\n parentId={ctx.recordId}\n foreignKey={fkField}\n columns={columns}\n editable={editable}\n interaction={interaction}\n ctx={ctx}\n sort={node.sort}\n startDraftSignal={startDraftSignal}\n />\n );\n\n // Already inside a section/tab card: render the toolbar + table directly.\n if (ctx.withinCard) {\n return (\n <div>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", marginBottom: 8 }}>\n {toolbar}\n </div>\n {table}\n </div>\n );\n }\n\n // Top-level: a first-class titled card, consistent with `section` nodes.\n return (\n <SectionCard\n title={label}\n icon={\n assocEntity.display?.icon ? (\n <DynamicIcon name={assocEntity.display.icon} size={14} />\n ) : undefined\n }\n right={toolbar}\n >\n {table}\n </SectionCard>\n );\n}\n\n// SimpleAssociatedTable uses the original useChildrenData hook (no search/filter/pagination).\ninterface SimpleTableProps {\n entityCode: string;\n entity: Entity;\n parentEntity: string;\n parentId: string;\n foreignKey: string;\n columns: string[];\n editable: boolean;\n interaction: \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\";\n ctx: LayoutContext;\n sort?: string;\n}\n\nfunction SimpleAssociatedTable({\n entityCode,\n entity,\n parentEntity,\n parentId,\n foreignKey,\n columns,\n editable,\n interaction,\n ctx,\n sort,\n startDraftSignal,\n}: SimpleTableProps & { startDraftSignal?: number }) {\n const queryClient = useQueryClient();\n const { data: response, isLoading, isError } = useChildrenData(\n parentEntity,\n parentId,\n entityCode,\n foreignKey,\n sort,\n );\n const rows = response?.data ?? [];\n const refs = response?.$refs;\n\n const invalidateQuery = useCallback(() => {\n queryClient.invalidateQueries({\n queryKey: [ENTITY_CHILDREN_KEY, parentEntity, parentId, entityCode],\n });\n }, [queryClient, parentEntity, parentId, entityCode]);\n\n return (\n <AssociatedTableInner\n entityCode={entityCode}\n entity={entity}\n parentId={parentId}\n foreignKey={foreignKey}\n columns={columns}\n editable={editable}\n interaction={interaction}\n ctx={ctx}\n rows={rows}\n refs={refs}\n isLoading={isLoading}\n isError={isError}\n invalidateQuery={invalidateQuery}\n startDraftSignal={startDraftSignal}\n />\n );\n}\n\n// PaginatedAssociatedTable uses useAssociatedData with search/filter/pagination support.\ninterface PaginatedTableProps extends SimpleTableProps {\n searchable?: boolean;\n filterFields?: string[];\n}\n\nfunction PaginatedAssociatedTable({\n entityCode,\n entity,\n parentEntity,\n parentId,\n foreignKey,\n columns,\n editable,\n interaction,\n ctx,\n searchable,\n filterFields,\n sort,\n startDraftSignal,\n}: PaginatedTableProps & { startDraftSignal?: number }) {\n const queryClient = useQueryClient();\n\n // Search and filter state.\n const [searchValue, setSearchValue] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [filters, setFilters] = useState<Record<string, string>>({});\n const searchTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n // Debounce search input.\n useEffect(() => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n searchTimerRef.current = setTimeout(() => {\n setDebouncedSearch(searchValue);\n }, 300);\n return () => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n };\n }, [searchValue]);\n\n const handleFilterChange = useCallback((field: string, value: string) => {\n setFilters((prev) => {\n if (value === \"__all__\") {\n const { [field]: _, ...rest } = prev;\n return rest;\n }\n return { ...prev, [field]: value };\n });\n }, []);\n\n // Active filters for the API call.\n const activeFilters = useMemo(() => {\n return Object.keys(filters).length > 0 ? filters : undefined;\n }, [filters]);\n\n const {\n data: infiniteData,\n isLoading,\n isError,\n hasNextPage,\n fetchNextPage,\n isFetchingNextPage,\n } = useAssociatedData(\n parentEntity,\n parentId,\n entityCode,\n foreignKey,\n sort,\n undefined, // limit\n debouncedSearch || undefined,\n activeFilters,\n );\n\n // Flatten pages into a single rows array and merge refs.\n const { rows, refs } = useMemo(() => {\n if (!infiniteData?.pages) return { rows: [] as Record<string, unknown>[], refs: undefined as Refs | undefined };\n const allRows: Record<string, unknown>[] = [];\n let mergedRefs: Refs | undefined;\n for (const page of infiniteData.pages) {\n allRows.push(...page.data);\n if (page.$refs) {\n if (!mergedRefs) {\n mergedRefs = { ...page.$refs };\n } else {\n for (const [entityKey, records] of Object.entries(page.$refs)) {\n mergedRefs[entityKey] = { ...mergedRefs[entityKey], ...records };\n }\n }\n }\n }\n return { rows: allRows, refs: mergedRefs };\n }, [infiniteData]);\n\n const invalidateQuery = useCallback(() => {\n queryClient.invalidateQueries({\n predicate: (query) => {\n const key = query.queryKey;\n return key[0] === ENTITY_CHILDREN_KEY && key[3] === entityCode;\n },\n });\n }, [queryClient, entityCode]);\n\n return (\n <div>\n <AssociatedToolbar\n entity={entity}\n searchable={searchable}\n filterFields={filterFields}\n searchValue={searchValue}\n onSearchChange={setSearchValue}\n filters={filters}\n onFilterChange={handleFilterChange}\n allEntities={ctx.schema.entities}\n />\n <AssociatedTableInner\n entityCode={entityCode}\n entity={entity}\n parentId={parentId}\n foreignKey={foreignKey}\n columns={columns}\n editable={editable}\n interaction={interaction}\n ctx={ctx}\n rows={rows}\n refs={refs}\n isLoading={isLoading}\n isError={isError}\n invalidateQuery={invalidateQuery}\n startDraftSignal={startDraftSignal}\n />\n {hasNextPage && (\n <div style={{ marginTop: 4 }}>\n <Btn\n kind=\"ghost\"\n size=\"xs\"\n icon={isFetchingNextPage ? <Spinner size={12} /> : undefined}\n onClick={() => fetchNextPage()}\n disabled={isFetchingNextPage}\n >\n Load more\n </Btn>\n </div>\n )}\n </div>\n );\n}\n\n// AssociatedTableInner is the shared table rendering logic used by both simple and paginated modes.\ninterface TableInnerProps {\n entityCode: string;\n entity: Entity;\n parentId: string;\n foreignKey: string;\n columns: string[];\n editable: boolean;\n interaction: \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\";\n ctx: LayoutContext;\n rows: Record<string, unknown>[];\n refs?: Refs;\n isLoading: boolean;\n isError: boolean;\n invalidateQuery: () => void;\n // Monotonic counter from the parent section header's \"+ Add row\" button.\n // Each increment asks the table to start an empty draft row.\n startDraftSignal?: number;\n}\n\nfunction AssociatedTableInner({\n entityCode,\n entity,\n parentId,\n foreignKey,\n columns,\n editable,\n interaction,\n ctx,\n rows,\n refs,\n isLoading,\n isError,\n invalidateQuery,\n startDraftSignal,\n}: TableInnerProps) {\n // When interaction === \"navigate\", identity column (first non-FK column) is\n // a link to the child's detail screen. Resolution honors `entity.detail_screen`\n // (canonical pointer in the schema), so any screen type — including `custom` —\n // works as long as the entity declares it.\n const identityCol = columns.find((c) => c !== foreignKey);\n const detailRoute = useMemo(\n () =>\n interaction === \"navigate\"\n ? resolveDetailRoute(undefined, entity, ctx.schema.screens)\n : null,\n [interaction, entity, ctx.schema.screens],\n );\n // Editable columns for inline edit of an EXISTING row (empty when the table\n // is not editable) and for the NEW-row draft. See the two helpers below for\n // the rules and why they differ on primary-key columns.\n const editableColumns = useMemo(\n () => (editable ? associatedEditableColumns(entity, columns, foreignKey) : new Set<string>()),\n [columns, foreignKey, entity, editable],\n );\n const draftEditableColumns = useMemo(\n () => associatedDraftEditableColumns(entity, columns, foreignKey),\n [columns, foreignKey, entity],\n );\n\n // Map each row's encoded object-id to its decomposed PK values. The encoded\n // id (entity.PrimaryKeyFields joined) is the stable string used for React\n // keys, per-row edit state, and row-action objectIds; the PkValues object is\n // what updateEntity / deleteEntity need to address the row. A composite- or\n // natural-PK entity has no single `id` column, so neither can be read off\n // `row.id` - both are derived here from the entity's declared primary key.\n const rowPkByKey = useMemo(() => {\n const m = new Map<string, PkValues>();\n for (const row of rows) {\n const pk = extractPkValues(entity, row);\n m.set(encodeObjectId(pk), pk);\n }\n return m;\n }, [rows, entity]);\n\n // Per-row actions: list_row-visible entity actions that render in each\n // row's trailing cell. AssociatedBlock dispatches via ActionButton which\n // invokes via /api/actions/<entity>.<code> with ObjectIDs=[rowId], so the\n // handler must accept the row id via ctx.ObjectIDs (not params).\n const rowActions = useMemo(\n () => resolveAssociatedRowActions(entity.actions),\n [entity.actions],\n );\n\n // Per-row edit state: which cell is active (click-to-edit, inline_edit only).\n const [activeCell, setActiveCell] = useState<{ rowId: string; col: string } | null>(null);\n // row_edit state: which row is in whole-row edit mode, and the\n // required-validation errors for it. inline_edit never sets these.\n const [editingRowId, setEditingRowId] = useState<string | null>(null);\n const [editErrors, setEditErrors] = useState<string[]>([]);\n // Per-row merge-debounce state.\n const [rowStates, setRowStates] = useState<Record<string, AssociatedRowState>>({});\n const rowStatesRef = useRef(rowStates);\n rowStatesRef.current = rowStates;\n const debounceTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>({});\n\n // Clean up debounce timers on unmount.\n useEffect(() => {\n const timers = debounceTimers.current;\n return () => {\n for (const t of Object.values(timers)) clearTimeout(t);\n };\n }, []);\n\n // Draft row state for \"Add row\" flow.\n const [draftRow, setDraftRow] = useState<Record<string, unknown> | null>(null);\n const [draftErrors, setDraftErrors] = useState<string[]>([]);\n const [draftSaving, setDraftSaving] = useState(false);\n\n // Delete confirmation state.\n const [deleteConfirm, setDeleteConfirm] = useState<{ show: boolean; rowId: string; label: string } | null>(null);\n const [deletingIds, setDeletingIds] = useState<Set<string>>(new Set());\n\n // Flush a row's buffer as a single PATCH. Returns true on success, false on\n // error. Reason: row_edit awaits the result to decide whether to leave edit\n // mode; relying on a post-call ref read (the old inline_edit pattern) would\n // race the React state update.\n const flushRow = useCallback(\n async (rowId: string, buffer: Record<string, unknown>, sentVer: number): Promise<boolean> => {\n setRowStates((prev) => ({\n ...prev,\n [rowId]: { ...prev[rowId], saving: true, error: undefined },\n }));\n try {\n // updateEntity needs the decomposed PK to address a composite-PK row;\n // rowId is only the encoded string key. Fall back to rowId for the\n // single-column-PK case (encoded id == the id value verbatim).\n await updateEntity(entityCode, rowPkByKey.get(rowId) ?? rowId, buffer);\n setRowStates((prev) => {\n const current = prev[rowId];\n if (!current) return prev;\n // Drop stale response: if user made more edits, don't clear buffer.\n if (current.ver > sentVer) {\n return { ...prev, [rowId]: { ...current, saving: false } };\n }\n // Response is current: clear buffer.\n const { [rowId]: _, ...rest } = prev;\n return rest;\n });\n invalidateQuery();\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Save failed\";\n setRowStates((prev) => ({\n ...prev,\n [rowId]: { ...prev[rowId], saving: false, error: msg },\n }));\n return false;\n }\n },\n [entityCode, invalidateQuery, rowPkByKey],\n );\n\n // Handle cell value change. Always writes to the row buffer. The 500ms\n // debounced auto-flush runs ONLY for inline_edit; row_edit buffers without\n // saving and commits explicitly via handleConfirmEdit.\n const handleCellChange = useCallback(\n (rowId: string, fieldName: string, value: unknown) => {\n setRowStates((prev) => {\n const current = prev[rowId] ?? { buffer: {}, ver: 0, saving: false };\n const nextVer = current.ver + 1;\n return {\n ...prev,\n [rowId]: {\n ...current,\n buffer: { ...current.buffer, [fieldName]: value },\n ver: nextVer,\n },\n };\n });\n if (interaction !== \"inline_edit\") return;\n // Reset debounce timer.\n if (debounceTimers.current[rowId]) {\n clearTimeout(debounceTimers.current[rowId]);\n }\n debounceTimers.current[rowId] = setTimeout(() => {\n const current = rowStatesRef.current[rowId];\n if (current && Object.keys(current.buffer).length > 0) {\n flushRow(rowId, { ...current.buffer }, current.ver);\n }\n }, 500);\n },\n [flushRow, interaction],\n );\n\n // Handle adding a new draft row.\n const handleStartDraft = useCallback(() => {\n const draft: Record<string, unknown> = {};\n // Pre-fill FK to parent.\n draft[foreignKey] = parentId;\n // Pre-fill field-level defaults, then bool fallback.\n for (const col of columns) {\n const field = entity.fields.get(col);\n if (!field) continue;\n if (field.default !== undefined) {\n draft[col] = field.default;\n } else if (field.type === \"bool\") {\n draft[col] = false;\n }\n }\n setDraftRow(draft);\n setDraftErrors([]);\n }, [foreignKey, parentId, columns, entity.fields]);\n\n // Parent section header's \"+ Add row\" button increments startDraftSignal.\n // Track the last-seen value in a ref so we only fire on the rising edge,\n // not when draftRow or handleStartDraft identity changes. Single-draft\n // rule: silently no-op if a draft is already in flight.\n const lastSignalRef = useRef(startDraftSignal);\n useEffect(() => {\n if (startDraftSignal === undefined) return;\n if (startDraftSignal === lastSignalRef.current) return;\n lastSignalRef.current = startDraftSignal;\n if (draftRow) return;\n handleStartDraft();\n }, [startDraftSignal, draftRow, handleStartDraft]);\n\n // Validate and confirm the draft row.\n const handleConfirmDraft = useCallback(async () => {\n if (!draftRow) return;\n // Client-side required field validation.\n const errors = findMissingRequiredAssociatedFields(draftRow, columns, entity, foreignKey);\n if (errors.length > 0) {\n setDraftErrors(errors);\n return;\n }\n setDraftSaving(true);\n try {\n await createEntity(entityCode, draftRow);\n setDraftRow(null);\n setDraftErrors([]);\n invalidateQuery();\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Create failed\";\n setDraftErrors([`_error:${msg}`]);\n } finally {\n setDraftSaving(false);\n }\n }, [draftRow, columns, entity, foreignKey, entityCode, invalidateQuery]);\n\n // row_edit: open a row in whole-row edit mode. Clears any prior validation\n // errors. Single-row rule: starting another row's edit replaces the target.\n // Reason: dropping every row buffer is required, not cosmetic - if the user\n // edited row A, typed a value, then clicked row B's Edit without confirming\n // or cancelling, row A's abandoned buffer would otherwise linger in\n // rowStates and render as A's value via displayRow's {...row, ...buffer}\n // merge, showing an uncommitted edit as if it were saved data.\n const handleStartEdit = useCallback((rowId: string) => {\n setRowStates({});\n setEditErrors([]);\n setEditingRowId(rowId);\n }, []);\n\n // row_edit: commit the row currently in edit mode. Validates the merged\n // {server row + buffer} with the SAME rule as handleConfirmDraft, then\n // PATCHes the buffered changes. Leaves edit mode only on a successful save.\n const handleConfirmEdit = useCallback(\n async (rowId: string) => {\n const state = rowStatesRef.current[rowId];\n const buffer = state?.buffer ?? {};\n // No edits: nothing to save, just exit edit mode.\n if (Object.keys(buffer).length === 0) {\n setEditingRowId(null);\n setEditErrors([]);\n return;\n }\n const row = rows.find(\n (r) => encodeObjectId(extractPkValues(entity, r)) === rowId,\n );\n const merged = { ...(row ?? {}), ...buffer };\n const errors = findMissingRequiredAssociatedFields(\n merged,\n columns,\n entity,\n foreignKey,\n );\n if (errors.length > 0) {\n setEditErrors(errors);\n return;\n }\n const ok = await flushRow(rowId, { ...buffer }, state?.ver ?? 0);\n if (ok) {\n setEditingRowId(null);\n setEditErrors([]);\n }\n },\n [rows, entity, columns, foreignKey, flushRow],\n );\n\n // row_edit: discard the in-flight edit - drop the row's buffer and exit\n // edit mode without saving.\n const handleCancelEdit = useCallback((rowId: string) => {\n setRowStates((prev) => {\n if (!prev[rowId]) return prev;\n const { [rowId]: _, ...rest } = prev;\n return rest;\n });\n setEditErrors([]);\n setEditingRowId(null);\n }, []);\n\n // Handle delete row with confirmation.\n const handleDeleteRow = useCallback(\n async (rowId: string) => {\n setDeleteConfirm(null);\n setDeletingIds((prev) => new Set(prev).add(rowId));\n try {\n // deleteEntity needs the decomposed PK for a composite-PK row; rowId\n // is only the encoded string key. Fall back to rowId for single PKs.\n await deleteEntity(entityCode, rowPkByKey.get(rowId) ?? rowId);\n invalidateQuery();\n } catch {\n // On error, just remove from deleting set - row stays visible.\n } finally {\n setDeletingIds((prev) => {\n const next = new Set(prev);\n next.delete(rowId);\n return next;\n });\n }\n },\n [entityCode, invalidateQuery, rowPkByKey],\n );\n\n if (isError) {\n return (\n <div style={{ fontSize: 12.5, color: \"var(--danger)\", padding: \"8px 0\" }}>Failed to load.</div>\n );\n }\n\n if (isLoading) {\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8, padding: \"8px 0\" }}>\n <div style={{ height: 32, background: \"var(--surface-3)\", borderRadius: 6 }} />\n <div style={{ height: 32, background: \"var(--surface-3)\", borderRadius: 6 }} />\n </div>\n );\n }\n\n const showEmpty = rows.length === 0 && !draftRow;\n\n return (\n <div>\n {showEmpty && !editable && (\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"12px 0\" }}>\n No records yet.\n </div>\n )}\n {(!showEmpty || editable) && (\n <div style={{ overflowX: \"auto\" }}>\n <table style={dataTableStyle}>\n <thead>\n <tr>\n {columns.map((col) => {\n const field = entity.fields.get(col);\n return (\n <th key={col} style={dataTableHeadCellStyle}>\n {field ? fieldDisplayName(field, col, ctx.schema.entities) : col}\n </th>\n );\n })}\n {/* Widened to 72 so the row_edit Edit + Delete icon-button\n pair fits; inline_edit shows only Delete but shares the\n column width. */}\n {editable && <th style={{ height: 30, width: 72 }} />}\n {!editable && rowActions.length > 0 && (\n <th style={{ height: 30, width: 40 }} />\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n // Stable per-row key: the encoded primary key, not row.id -\n // composite / natural-PK entities have no `id` column.\n const rowId = encodeObjectId(extractPkValues(entity, row));\n const isDeleting = deletingIds.has(rowId);\n const rowState = rowStates[rowId];\n const href = detailRoute && rowId\n ? detailRoute.replace(/:\\w+/, rowId)\n : undefined;\n return (\n <AssociatedRow\n key={rowId}\n rowId={rowId}\n row={row}\n entity={entity}\n entities={ctx.schema.entities}\n screens={ctx.schema.screens}\n columns={columns}\n editableColumns={editableColumns}\n editable={editable}\n interaction={interaction}\n isEditing={editingRowId === rowId}\n editErrors={editErrors}\n isDeleting={isDeleting}\n activeCell={activeCell}\n setActiveCell={setActiveCell}\n rowState={rowState}\n onCellChange={handleCellChange}\n onDeleteClick={(id) => {\n const label = getDisplayValue(row as Record<string, unknown>, entity) ?? \"\";\n setDeleteConfirm({ show: true, rowId: id, label });\n }}\n onStartEdit={handleStartEdit}\n onConfirmEdit={handleConfirmEdit}\n onCancelEdit={handleCancelEdit}\n refs={refs}\n identityCol={identityCol}\n href={href}\n rowActions={rowActions}\n entityCode={entityCode}\n onRowActionSuccess={invalidateQuery}\n />\n );\n })}\n {draftRow && (\n <DraftRow\n draftRow={draftRow}\n entity={entity}\n columns={columns}\n editableColumns={draftEditableColumns}\n foreignKey={foreignKey}\n draftErrors={draftErrors}\n draftSaving={draftSaving}\n onChange={(field, value) =>\n setDraftRow((prev) => (prev ? { ...prev, [field]: value } : prev))\n }\n onConfirm={handleConfirmDraft}\n onCancel={() => {\n setDraftRow(null);\n setDraftErrors([]);\n }}\n refs={refs}\n />\n )}\n </tbody>\n </table>\n </div>\n )}\n {/* The \"+ Add row\" button now lives in the section header (AssociatedBlock,\n top of this file). It drives handleStartDraft via the startDraftSignal\n prop threaded through AssociatedTableInner. No separate bottom button. */}\n {deleteConfirm && (\n <ConfirmDialog\n open={deleteConfirm.show}\n onOpenChange={(open) => !open && setDeleteConfirm(null)}\n title=\"Delete record\"\n description={\n deleteConfirm.label\n ? `Are you sure you want to delete \"${deleteConfirm.label}\"?`\n : \"Are you sure you want to delete this record?\"\n }\n confirmLabel=\"Delete\"\n onConfirm={() => handleDeleteRow(deleteConfirm.rowId)}\n destructive\n />\n )}\n </div>\n );\n}\n\n// AssociatedRow renders a single data row with click-to-edit support.\ninterface AssociatedRowProps {\n rowId: string;\n row: Record<string, unknown>;\n entity: Entity;\n // entities + screens are required for display-mode ref rendering: the\n // cell registry routes ref fields through refLinkFactory, which needs\n // the target entity (to resolve `display.display_field`) and the screens\n // map (to resolve the target's detail route). Without them, ref cells\n // fall through to the raw FK value.\n entities: LayoutContext[\"schema\"][\"entities\"];\n screens: LayoutContext[\"schema\"][\"screens\"];\n columns: string[];\n editableColumns: Set<string>;\n editable: boolean;\n // Interaction mode of the parent table. Drives how an existing row enters\n // edit mode: click-to-edit cell (inline_edit) vs. a per-row Edit button\n // (row_edit). navigate/readonly never edit.\n interaction: \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\";\n // row_edit only: true when this row is the one currently in edit mode.\n isEditing: boolean;\n // row_edit only: required-validation field codes (and a `_error:` API error)\n // for the row currently in edit mode.\n editErrors: string[];\n isDeleting: boolean;\n activeCell: { rowId: string; col: string } | null;\n setActiveCell: (cell: { rowId: string; col: string } | null) => void;\n rowState?: AssociatedRowState;\n onCellChange: (rowId: string, fieldName: string, value: unknown) => void;\n onDeleteClick: (rowId: string) => void;\n // row_edit only: start/commit/discard editing this row.\n onStartEdit: (rowId: string) => void;\n onConfirmEdit: (rowId: string) => void;\n onCancelEdit: (rowId: string) => void;\n refs?: Refs;\n identityCol?: string;\n href?: string;\n rowActions?: Array<{ code: string; action: Action }>;\n entityCode?: string;\n onRowActionSuccess?: () => void;\n}\n\nfunction AssociatedRow({\n rowId,\n row,\n entity,\n entities,\n screens,\n columns,\n editableColumns,\n editable,\n interaction,\n isEditing,\n editErrors,\n isDeleting,\n activeCell,\n setActiveCell,\n rowState,\n onCellChange,\n onDeleteClick,\n onStartEdit,\n onConfirmEdit,\n onCancelEdit,\n refs,\n identityCol,\n href,\n rowActions,\n onRowActionSuccess,\n}: AssociatedRowProps) {\n const userLocale = useUserLocale();\n // Merge buffer values over server data for display while editing.\n const displayRow = useMemo(() => {\n if (!rowState || Object.keys(rowState.buffer).length === 0) return row;\n return { ...row, ...rowState.buffer };\n }, [row, rowState]);\n\n // row_edit, this row in edit mode: render the shared explicit-commit editor.\n // The buffer is already merged into displayRow above. A pending save error\n // surfaces as the EditableRow `_error:` channel.\n if (interaction === \"row_edit\" && isEditing) {\n const errors = rowState?.error\n ? [...editErrors, `_error:${rowState.error}`]\n : editErrors;\n return (\n <EditableRow\n values={displayRow}\n entity={entity}\n columns={columns}\n editableColumns={editableColumns}\n errors={errors}\n saving={rowState?.saving ?? false}\n onChange={(f, v) => onCellChange(rowId, f, v)}\n onConfirm={() => onConfirmEdit(rowId)}\n onCancel={() => onCancelEdit(rowId)}\n refs={refs}\n />\n );\n }\n\n const cellStyle = dataTableCellStyle;\n const editingCellStyle: React.CSSProperties = {\n padding: \"4px 4px\",\n borderBottom: \"1px solid var(--divider)\",\n verticalAlign: \"middle\",\n };\n\n // Click-to-edit only applies to inline_edit. row_edit enters edit mode via\n // the per-row Edit button (handled in the editing branch above), so its\n // display cells must NOT respond to clicks.\n const cellClickable = interaction === \"inline_edit\";\n\n return (\n <tr style={{ opacity: isDeleting ? 0.5 : 1 }}>\n {columns.map((col) => {\n const field = entity.fields.get(col);\n const isEditable = editableColumns.has(col);\n const isActive =\n cellClickable && activeCell?.rowId === rowId && activeCell?.col === col;\n\n if (isActive && isEditable) {\n return (\n <td key={col} style={editingCellStyle}>\n {field\n ? renderField({\n field,\n fieldName: col,\n value: displayRow[col],\n mode: \"edit\",\n onChange: (val) => onCellChange(rowId, col, val),\n record: displayRow,\n refs,\n compact: true,\n locale: userLocale,\n })\n : String(displayRow[col] ?? \"\")}\n </td>\n );\n }\n\n const isIdentityLink = href && col === identityCol;\n const rendered = field\n ? renderField({\n field,\n fieldName: col,\n value: displayRow[col],\n mode: \"display\",\n record: displayRow,\n refs,\n // Pass schema context so display-mode dispatches in the cell\n // registry (notably refLinkFactory) can resolve ref labels via\n // the target entity's display_field. ChildrenTable threads the\n // same trio (entity + entities + screens); AssociatedBlock\n // historically omitted them, which is why ref cells used to\n // render the raw FK UUID.\n entity,\n entities,\n // Omit `screens` for the identity column to avoid nesting a\n // refLink <a> inside the outer identity link wrapper -\n // refLinkFactory falls back to plain text when no detail route\n // is available.\n screens: isIdentityLink ? undefined : screens,\n locale: userLocale,\n })\n : String(displayRow[col] ?? \"\");\n const canClickCell = cellClickable && isEditable && !isDeleting;\n return (\n <td\n key={col}\n style={{ ...cellStyle, cursor: canClickCell ? \"pointer\" : \"default\" }}\n onClick={canClickCell ? () => setActiveCell({ rowId, col }) : undefined}\n >\n {isIdentityLink ? (\n // Plain real <a href>: the global link interceptor (Layout)\n // catches the plain click and routes it through useTabNavigate\n // (open/reuse an in-app tab standalone, or the embed chokepoint in\n // embed). Modifier / middle clicks stay native.\n <a\n href={href}\n style={{ color: \"var(--accent)\", textDecoration: \"none\", fontWeight: 500 }}\n onMouseEnter={(e) => (e.currentTarget.style.textDecoration = \"underline\")}\n onMouseLeave={(e) => (e.currentTarget.style.textDecoration = \"none\")}\n >\n {rendered}\n </a>\n ) : (\n rendered\n )}\n </td>\n );\n })}\n {editable && (\n <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid var(--divider)\", verticalAlign: \"middle\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n {rowState?.saving && <Spinner size={12} />}\n {rowState?.error && (\n <span style={{ fontSize: 11, color: \"var(--danger)\" }} title={rowState.error}>\n !\n </span>\n )}\n {/* row_edit: a per-row Edit button opens the whole row in the\n shared explicit-commit editor. inline_edit keeps click-to-edit\n cells and so needs no Edit button here. */}\n {interaction === \"row_edit\" && (\n <IconBtn\n size={24}\n icon={<Icon.edit size={12} />}\n onClick={() => onStartEdit(rowId)}\n title=\"Edit row\"\n />\n )}\n <IconBtn\n size={24}\n icon={<Icon.trash size={12} />}\n onClick={() => onDeleteClick(rowId)}\n title=\"Delete row\"\n />\n </div>\n </td>\n )}\n {!editable && rowActions && rowActions.length > 0 && (\n <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid var(--divider)\", verticalAlign: \"middle\" }}>\n <RowActionsKebab\n actions={rowActions}\n objectIds={[rowId]}\n row={displayRow}\n onSuccess={onRowActionSuccess}\n />\n </td>\n )}\n </tr>\n );\n}\n\n// EditableRow renders one <tr> where every column in `editableColumns` is an\n// input and a trailing cell holds Confirm/Cancel. It is the single source of\n// truth for the explicit-commit row editor, shared by both the \"Add row\" draft\n// (DraftRow) and the row_edit interaction (an existing row in edit mode).\n// Reason: the create flow and the row_edit flow must look and behave\n// identically; extracting one component guarantees that by construction\n// instead of by two copies kept in sync by hand.\ninterface EditableRowProps {\n // Field values to render. For a draft this is the draft object; for row_edit\n // it is the server row merged with its edit buffer.\n values: Record<string, unknown>;\n entity: Entity;\n columns: string[];\n // Columns rendered as inputs. Every other column renders read-only.\n editableColumns: Set<string>;\n // A `_error:<msg>` entry is an API error shown as a `!`; any other entry is\n // a field code that failed required-validation and gets a red outline.\n errors: string[];\n saving: boolean;\n onChange: (field: string, value: unknown) => void;\n onConfirm: () => void;\n onCancel: () => void;\n refs?: Refs;\n // When true, draw the left accent bar marking a \"pending insertion\" (draft\n // row). Existing rows in edit mode do not get the bar.\n accent?: boolean;\n}\n\nfunction EditableRow({\n values,\n entity,\n columns,\n editableColumns,\n errors,\n saving,\n onChange,\n onConfirm,\n onCancel,\n refs,\n accent,\n}: EditableRowProps) {\n const userLocale = useUserLocale();\n const apiError = errors.find((e) => e.startsWith(\"_error:\"));\n const fieldErrors = new Set(errors.filter((e) => !e.startsWith(\"_error:\")));\n\n // The editable row visually continues the table:\n // - same padding/height/divider as data rows (no dominant tint, no row\n // height jumps that make display rows wrap datetimes into 3 lines)\n // - optional left accent bar signals \"pending insertion\" for a new draft\n // - inputs constrained via min-width: 0 + width: 100% so native\n // datetime-local inputs (which have a non-zero intrinsic min-width)\n // cannot force their column wider than the display cells above\n // - Enter → confirm, Escape → cancel (keyboard affordance)\n const cellBase: CSSProperties = {\n padding: \"6px 12px\",\n borderBottom: \"1px solid var(--divider)\",\n verticalAlign: \"middle\",\n height: 40,\n minWidth: 0,\n };\n const editorWrapperBase: CSSProperties = {\n display: \"block\",\n width: \"100%\",\n maxWidth: \"100%\",\n minWidth: 0,\n };\n const onKeyDown = (e: KeyboardEvent<HTMLTableRowElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n onConfirm();\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n onCancel();\n }\n };\n\n return (\n <tr\n style={accent ? { boxShadow: \"inset 3px 0 0 var(--accent)\" } : undefined}\n onKeyDown={onKeyDown}\n >\n {columns.map((col) => {\n const field = entity.fields.get(col);\n const isEditable = editableColumns.has(col);\n const hasError = fieldErrors.has(col);\n\n if (!isEditable) {\n return (\n <td key={col} style={{ ...cellBase, color: \"var(--text-3)\" }}>\n {field?.auto ? \"—\" : String(values[col] ?? \"\")}\n </td>\n );\n }\n\n return (\n <td\n key={col}\n style={{\n ...cellBase,\n outline: hasError ? \"1px solid var(--danger)\" : \"none\",\n outlineOffset: -1,\n }}\n title={hasError ? `${col} is required` : undefined}\n >\n <div style={editorWrapperBase}>\n {field\n ? renderField({\n field,\n fieldName: col,\n value: values[col],\n mode: \"edit\",\n onChange: (val) => onChange(col, val),\n record: values,\n refs,\n compact: true,\n locale: userLocale,\n })\n : String(values[col] ?? \"\")}\n </div>\n </td>\n );\n })}\n <td style={cellBase}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4, justifyContent: \"flex-end\" }}>\n {apiError && (\n <span style={{ fontSize: 11, color: \"var(--danger)\" }} title={apiError.slice(7)}>\n !\n </span>\n )}\n <IconBtn\n size={24}\n icon={saving ? <Spinner size={12} /> : <Icon.check size={12} />}\n onClick={onConfirm}\n title=\"Confirm (Enter)\"\n />\n <IconBtn size={24} icon={<Icon.x size={12} />} onClick={onCancel} title=\"Cancel (Esc)\" />\n </div>\n </td>\n </tr>\n );\n}\n\n// DraftRow renders the inline draft for new row creation. It is a thin wrapper\n// over EditableRow: the create flow keeps its existing external prop shape and\n// all call sites unchanged while delegating rendering to the shared component.\ninterface DraftRowProps {\n draftRow: Record<string, unknown>;\n entity: Entity;\n columns: string[];\n editableColumns: Set<string>;\n foreignKey: string;\n draftErrors: string[];\n draftSaving: boolean;\n onChange: (field: string, value: unknown) => void;\n onConfirm: () => void;\n onCancel: () => void;\n refs?: Refs;\n}\n\nfunction DraftRow({\n draftRow,\n entity,\n columns,\n editableColumns,\n foreignKey,\n draftErrors,\n draftSaving,\n onChange,\n onConfirm,\n onCancel,\n refs,\n}: DraftRowProps) {\n // The FK column is supplied automatically and must never become an input,\n // even if draftEditableColumns happens to include it. Filter it out here so\n // EditableRow renders it read-only - byte-identical to the old behavior,\n // which gated each cell on `editableColumns.has(col) && col !== foreignKey`.\n const editableNoFk = useMemo(() => {\n const next = new Set(editableColumns);\n next.delete(foreignKey);\n return next;\n }, [editableColumns, foreignKey]);\n\n return (\n <EditableRow\n values={draftRow}\n entity={entity}\n columns={columns}\n editableColumns={editableNoFk}\n errors={draftErrors}\n saving={draftSaving}\n onChange={onChange}\n onConfirm={onConfirm}\n onCancel={onCancel}\n refs={refs}\n accent\n />\n );\n}\n","import { useState, useRef, type ReactNode } from \"react\";\nimport { useEmbedNavigate } from \"@/embed\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Avatar } from \"@/components/primitives/Avatar\";\nimport { Btn } from \"@/components/primitives/Btn\";\nimport { Chip } from \"@/components/primitives/Chip\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { IconBtn } from \"@/components/primitives/IconBtn\";\nimport { Pill } from \"@/components/primitives/Pill\";\nimport { RowField } from \"@/components/primitives/RowField\";\nimport { SectionCard } from \"@/components/primitives/SectionCard\";\nimport { BoolToggle } from \"@/components/primitives/BoolToggle\";\nimport { FieldLabel, TextInput } from \"@/components/primitives/Fields\";\nimport { Spinner } from \"@/components/primitives/Spinner\";\nimport { ChangePasswordDialog } from \"@/components/shared/ChangePasswordDialog\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { changePassword } from \"@/api/auth\";\nimport { getEntity, updateEntity } from \"@/api/data\";\nimport { uploadFile, validateClientSide } from \"@/components/file-widgets\";\nimport type { HydratedFile } from \"@/types/files\";\nimport { toast } from \"@/lib/toast\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { AssociatedBlock } from \"@/components/detail-layout/AssociatedBlock\";\nimport type { LayoutContext } from \"@/components/detail-layout/LayoutRenderer\";\n\n// Accepted MIME types for avatar upload — matches profile entity files.avatar.accepts.\nconst AVATAR_ACCEPTS = [\"image/png\", \"image/jpeg\", \"image/webp\"];\nconst AVATAR_MAX_BYTES = 5 * 1024 * 1024; // 5 MB\n\nexport function Profile() {\n const navigate = useEmbedNavigate();\n const user = useAuthStore((s) => s.user);\n const tenant = useAuthStore((s) => s.activeTenant);\n const refreshMe = useAuthStore((s) => s.refreshMe);\n\n const [changeOpen, setChangeOpen] = useState(false);\n const [editOpen, setEditOpen] = useState(false);\n const [uploading, setUploading] = useState(false);\n const [notifs, setNotifs] = useState<Record<string, boolean>>({\n email: true, inapp: true, slack: false, desktop: false,\n });\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const displayName = user?.display_name || user?.email || \"User\";\n const email = user?.email ?? \"\";\n const role = (user?.roles && user.roles[0]) || \"Member\";\n const envLabel = tenant?.env ? `${tenant.name} · ${tenant.env}` : (tenant?.name ?? \"—\");\n const createdAt = user?.created_at ? new Date(user.created_at).toLocaleDateString() : \"—\";\n\n // Fetch full profile record (including avatar file attachment).\n const profileKey = [\"profile-files\", user?.id];\n const { data: profileRecord, refetch: refetchProfile } = useQuery({\n queryKey: profileKey,\n queryFn: () => getEntity(\"profile\", { id: user!.id }),\n enabled: !!user?.id,\n staleTime: 5 * 60 * 1000,\n });\n\n const avatarFile = (profileRecord?.data as Record<string, unknown> | undefined)?.[\"avatar\"] as\n HydratedFile | null | undefined;\n const avatarUrl =\n avatarFile?.derivations?.[\"thumb_200\"] ??\n avatarFile?.url ??\n null;\n\n const handleAvatarClick = () => {\n if (uploading) return;\n fileInputRef.current?.click();\n };\n\n const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file || !user) return;\n // Reset so the same file can be re-selected.\n e.target.value = \"\";\n\n const validationError = validateClientSide(file, AVATAR_ACCEPTS, AVATAR_MAX_BYTES);\n if (validationError) {\n toast.error(validationError.message);\n return;\n }\n\n setUploading(true);\n try {\n const uploaded = await uploadFile({\n file,\n entityCode: \"profile\",\n fieldCode: \"avatar\",\n });\n await updateEntity(\"profile\", { id: user.id }, {\n \"$files\": {\n avatar: { set: [{ file_id: uploaded.id, filename: uploaded.filename }] },\n },\n });\n await refetchProfile();\n toast.success(\"Avatar updated.\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Upload failed.\";\n toast.error(msg);\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div style={{ flex: 1, overflowY: \"auto\", padding: \"14px 20px 28px\", background: \"var(--bg)\" }}>\n <div style={{ maxWidth: 1000, margin: \"0 auto\" }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 14, padding: \"16px 16px\", background: \"var(--surface)\", border: \"1px solid var(--border)\", borderRadius: 10, marginBottom: 14 }}>\n {/* Avatar - clickable to upload */}\n <div\n onClick={handleAvatarClick}\n title=\"Click to change avatar\"\n style={{ position: \"relative\", cursor: uploading ? \"default\" : \"pointer\", flexShrink: 0 }}\n >\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt=\"Avatar\"\n style={{ width: 56, height: 56, borderRadius: \"50%\", objectFit: \"cover\", display: \"block\" }}\n />\n ) : (\n <Avatar name={displayName} size={56} />\n )}\n <div style={{\n position: \"absolute\", inset: 0, borderRadius: \"50%\",\n background: \"var(--profile-avatar-scrim)\",\n display: \"flex\", alignItems: \"center\", justifyContent: \"center\",\n opacity: uploading ? 1 : 0,\n transition: \"opacity 0.15s\",\n }}\n className=\"avatar-upload-overlay\"\n >\n {uploading ? <Spinner size={18} /> : <Icon.upload size={16} style={{ color: \"var(--profile-avatar-upload-fg)\" }} />}\n </div>\n <style>{`.avatar-upload-overlay:hover { opacity: 1 !important; }`}</style>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={AVATAR_ACCEPTS.join(\",\")}\n style={{ display: \"none\" }}\n onChange={handleFileChange}\n />\n\n <div style={{ flex: 1, minWidth: 0 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600, letterSpacing: \"-0.012em\" }}>{displayName}</h1>\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", marginTop: 2 }} className=\"mono\">{email}</div>\n <div style={{ marginTop: 6, display: \"flex\", gap: 4, flexWrap: \"wrap\" }}>\n <Pill tone=\"violet\" size=\"xs\" dot>{role}</Pill>\n <Pill tone=\"slate\" size=\"xs\">{envLabel}</Pill>\n {user?.is_active && <Pill tone=\"emerald\" size=\"xs\" dot>Active</Pill>}\n </div>\n </div>\n <Btn icon={<Icon.edit size={13} />} onClick={() => setEditOpen(true)}>Edit profile</Btn>\n <Btn icon={<Icon.settings size={13} />} onClick={() => navigate(\"/preferences\")}>Preferences</Btn>\n </div>\n\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 14 }}>\n <SectionCard title=\"Personal info\" icon={<Icon.users size={14} />}>\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", columnGap: 20, rowGap: 6 }}>\n <RowField label=\"Display name\">{displayName}</RowField>\n <RowField label=\"Email\"><span className=\"mono\">{email}</span></RowField>\n <RowField label=\"Phone\"><span style={{ color: \"var(--text-4)\" }}>—</span></RowField>\n <RowField label=\"Timezone\">{resolveTZ()}</RowField>\n <RowField label=\"Language\">English (US)</RowField>\n <RowField label=\"Joined\"><span className=\"mono\">{createdAt}</span></RowField>\n </div>\n </SectionCard>\n\n <SectionCard title=\"Security\" icon={<Icon.shield size={14} />}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 10 }}>\n <SecurityRow\n icon={<Icon.key size={13} />}\n label=\"Password\"\n hint=\"Update at any time\"\n action={<Btn size=\"xs\" onClick={() => setChangeOpen(true)}>Change</Btn>}\n />\n <SecurityRow\n icon={<Icon.shield size={13} />}\n label=\"Two-factor authentication\"\n hint=\"Authenticator app recommended\"\n action={<Pill tone=\"slate\" size=\"xs\">Not set up</Pill>}\n />\n <SecurityRow\n icon={<Icon.device size={13} />}\n label=\"Trusted devices\"\n hint=\"Sign-in sessions by device\"\n action={<Btn size=\"xs\" kind=\"ghost\">Manage</Btn>}\n />\n <SecurityRow\n icon={<Icon.activity size={13} />}\n label=\"Sign-in history\"\n hint=\"Audit trail of recent logins\"\n action={<Btn size=\"xs\" kind=\"ghost\">View</Btn>}\n />\n </div>\n </SectionCard>\n\n <SectionCard title=\"Access\" icon={<Icon.key size={14} />}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6 }}>\n <RowField label=\"Role\"><Pill tone=\"violet\" size=\"xs\" dot>{role}</Pill></RowField>\n <RowField label=\"Groups\">\n {user?.roles && user.roles.length > 0 ? (\n <div style={{ display: \"flex\", gap: 4, flexWrap: \"wrap\" }}>\n {user.roles.map((r) => <Chip key={r}>{r}</Chip>)}\n </div>\n ) : <span style={{ color: \"var(--text-4)\" }}>—</span>}\n </RowField>\n <RowField label=\"Permissions\">\n <span className=\"mono\" style={{ color: \"var(--text-3)\", fontSize: 11.5 }}>\n {user?.permissions?.length ?? 0} explicit\n </span>\n </RowField>\n <RowField label=\"API keys\"><span className=\"mono\">—</span></RowField>\n </div>\n </SectionCard>\n\n <SectionCard title=\"Notifications\" icon={<Icon.bell size={14} />}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 10 }}>\n {([\n [\"email\", \"Email\", \"Daily digest · important only\"],\n [\"inapp\", \"In-app\", \"All mentions and assignments\"],\n [\"slack\", \"Slack\", \"Connect via OAuth\"],\n [\"desktop\", \"Desktop\", \"Browser notifications\"],\n ] as const).map(([k, title, hint]) => (\n <div key={k} style={{ display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: 12.5, fontWeight: 500 }}>{title}</div>\n <div style={{ fontSize: 11, color: \"var(--text-3)\" }}>{hint}</div>\n </div>\n <BoolToggle value={!!notifs[k]} onChange={(v) => setNotifs({ ...notifs, [k]: v })} />\n </div>\n ))}\n </div>\n </SectionCard>\n </div>\n\n {/* API Keys self-service block. Profile is `type: custom` so it\n is allowed to compose generic layout blocks - this is one\n <AssociatedBlock> usage referencing the api_key children\n relation declared on the profile entity in core_entities.yaml.\n Renders the standard list + \"+ New API Key\" toolbar + revoke\n row actions, all through the same dispatch path used on\n user-detail and the platform /admin/api-keys list. No per-\n screen mint code lives here. */}\n <ProfileApiKeysBlock userId={user?.id} />\n\n {user && (\n <div style={{ fontSize: 11, color: \"var(--text-4)\", marginTop: 12, display: \"flex\", gap: 12 }}>\n <span>User ID: <span className=\"mono\">{user.id}</span></span>\n <span>·</span>\n <span>Created <span className=\"mono\">{createdAt}</span></span>\n </div>\n )}\n </div>\n\n <EditProfileDialog\n open={editOpen}\n initialName={user?.display_name ?? \"\"}\n userId={user?.id ?? \"\"}\n onClose={() => setEditOpen(false)}\n onSaved={async () => {\n await refreshMe();\n setEditOpen(false);\n }}\n />\n\n <ChangePasswordDialog\n open={changeOpen}\n onClose={() => setChangeOpen(false)}\n onSubmit={async ({ current, next }) => {\n await changePassword(current, next);\n toast.success(\"Password changed. Sign in again to continue.\");\n }}\n />\n </div>\n );\n}\n\n// EditProfileDialog lets the user rename their display_name.\nfunction EditProfileDialog({\n open, initialName, userId, onClose, onSaved,\n}: {\n open: boolean;\n initialName: string;\n userId: string;\n onClose: () => void;\n onSaved: () => Promise<void>;\n}) {\n const [name, setName] = useState(initialName);\n const [running, setRunning] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (!open) return null;\n\n const close = () => { setName(initialName); setError(null); onClose(); };\n\n const go = async () => {\n if (!name.trim()) { setError(\"Display name is required.\"); return; }\n setError(null);\n setRunning(true);\n try {\n await updateEntity(\"profile\", { id: userId }, { display_name: name.trim() });\n await onSaved();\n } catch (e) {\n setError(e instanceof Error ? e.message : \"Failed to save.\");\n } finally {\n setRunning(false);\n }\n };\n\n return (\n <>\n <div onClick={close} style={{ position: \"fixed\", inset: 0, background: \"var(--scrim)\", zIndex: 60, backdropFilter: \"blur(2px)\" }} />\n <div style={{\n position: \"fixed\", top: \"50%\", left: \"50%\", transform: \"translate(-50%, -50%)\",\n zIndex: 61, width: 420, background: \"var(--surface)\", borderRadius: 12,\n boxShadow: \"var(--shadow-lg)\", border: \"1px solid var(--border)\", overflow: \"hidden\",\n }}>\n <div style={{ padding: \"14px 18px 0\", display: \"flex\", alignItems: \"center\", gap: 10 }}>\n <span style={{\n width: 28, height: 28, borderRadius: 7, background: \"var(--accent-soft)\", color: \"var(--accent)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n }}>\n <Icon.edit size={15} />\n </span>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: 14, fontWeight: 600, color: \"var(--text)\" }}>Edit profile</div>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\" }}>Update your display name.</div>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={close} />\n </div>\n <div style={{ padding: \"12px 18px 16px\", display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <div>\n <FieldLabel required>Display name</FieldLabel>\n <TextInput value={name} onChange={setName} placeholder=\"Your name\" autoFocus />\n </div>\n {error && (\n <div style={{\n padding: \"8px 10px\", background: \"var(--danger-soft)\", borderRadius: 6,\n fontSize: 11.5, color: \"var(--danger)\", display: \"flex\", gap: 6,\n }}>\n <Icon.info size={13} />\n <span>{error}</span>\n </div>\n )}\n </div>\n <div style={{\n padding: \"10px 16px\", borderTop: \"1px solid var(--divider)\",\n display: \"flex\", alignItems: \"center\", gap: 8, background: \"var(--surface-2)\",\n }}>\n <div style={{ flex: 1 }} />\n <Btn onClick={close}>Cancel</Btn>\n <Btn\n kind=\"primary\"\n icon={running ? <Spinner size={13} /> : <Icon.check size={13} />}\n onClick={go}\n disabled={!name.trim() || running}\n >\n {running ? \"Saving…\" : \"Save\"}\n </Btn>\n </div>\n </div>\n </>\n );\n}\n\nfunction SecurityRow({ icon, label, hint, action }: { icon: ReactNode; label: string; hint: string; action: ReactNode }) {\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, padding: \"6px 0\" }}>\n <span style={{\n width: 26, height: 26, borderRadius: 6, background: \"var(--surface-3)\", color: \"var(--text-2)\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\", flexShrink: 0,\n }}>{icon}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 12.5, fontWeight: 500 }}>{label}</div>\n <div style={{ fontSize: 11, color: \"var(--text-3)\" }}>{hint}</div>\n </div>\n {action}\n </div>\n );\n}\n\nfunction resolveTZ(): string {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || \"—\";\n } catch {\n return \"—\";\n }\n}\n\n// ProfileApiKeysBlock is the thinnest possible wrapper that lets the\n// custom Profile screen reuse the generic AssociatedBlock. It builds the\n// LayoutContext that AssociatedBlock expects (schema + parent entity +\n// parent record id) and renders the standard layout node `{associated:\n// \"api_key\"}`. The FK + tenant scoping come from the `profile.children`\n// declaration in core_entities.yaml plus api_key's `tenant_key`, so\n// nothing about WHAT we render or HOW we mint lives in this file - only\n// the wiring to the user's own profile row.\nfunction ProfileApiKeysBlock({ userId }: { userId: string | undefined }) {\n const { data: schema } = useSchema();\n if (!userId || !schema) return null;\n const entity = schema.entities[\"profile\"];\n if (!entity) return null;\n const ctx: LayoutContext = {\n schema,\n entity,\n entityCode: \"profile\",\n recordId: userId,\n record: { id: userId },\n isNew: false,\n mode: \"display\",\n };\n return <AssociatedBlock node={{ associated: \"api_key\" }} ctx={ctx} />;\n}\n","// Ported from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx\n// PreferencesPage + PrefRow. Appearance + Keyboard sections.\n//\n// Controls write through the existing useAppearanceStore (Stage 3). That\n// mirrors the design's \"parameter API at user scope\" contract: the store\n// persists to localStorage today and will hydrate from /api/params/{code}\n// (user-scope) once the SDK parameter-write hook lands.\nimport type { ReactNode } from \"react\";\nimport { toast } from \"@/lib/toast\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { Kbd } from \"@/components/primitives/Kbd\";\nimport { SectionCard } from \"@/components/primitives/SectionCard\";\nimport { BoolToggle } from \"@/components/primitives/BoolToggle\";\nimport {\n useAppearanceStore,\n type AccentId,\n type Density,\n type FaviconSource,\n type ThemeId,\n} from \"@/stores/appearance\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { setUserParam } from \"@/api/params\";\n\n/**\n * Param-code → Appearance-key map (Plan T23). Each control writes to the\n * matching server parameter at user scope through the subresource $params\n * channel; the appearance store mirrors the value optimistically so the\n * UI updates without waiting for the round trip.\n */\nconst PARAM_BY_KEY: Record<string, string> = {\n theme: \"ui_theme\",\n accent: \"ui_accent\",\n density: \"ui_density\",\n radius: \"ui_radius\",\n zebra: \"ui_zebra_rows\",\n faviconSource: \"ui_favicon_source\",\n};\n\nconst ACCENTS: Array<{ id: AccentId; c: string }> = [\n { id: \"violet\", c: \"#5B5BD6\" },\n { id: \"blue\", c: \"#2563EB\" },\n { id: \"teal\", c: \"#0D9488\" },\n { id: \"amber\", c: \"#B45309\" },\n { id: \"rose\", c: \"#BE123C\" },\n];\n\nconst DENSITIES: Density[] = [\"compact\", \"comfortable\", \"cozy\"];\n\nconst FAVICON_SOURCES: Array<[FaviconSource, string, string]> = [\n [\"google\", \"Google\", \"Sharp icons via Google's favicon service. Each render leaks the host name to Google.\"],\n [\"direct\", \"Direct\", \"Load /favicon.ico from each origin. No third party, but reveals your IP to each host and misses sites that only use <link rel=\\\"icon\\\">.\"],\n [\"none\", \"None\", \"Do not fetch external favicons. Show a generic globe.\"],\n];\n\nconst SHORTCUTS: Array<[string, string]> = [\n [\"Command palette\", \"⌘ K\"],\n [\"Toggle AI assistant\", \"⌘ /\"],\n [\"Collapse sidebar\", \"⌘ B\"],\n [\"Close current tab\", \"⌘ W\"],\n [\"Quick search\", \"/\"],\n [\"Escape / close\", \"Esc\"],\n];\n\nexport function Preferences() {\n const theme = useAppearanceStore((s) => s.theme);\n const accent = useAppearanceStore((s) => s.accent);\n const density = useAppearanceStore((s) => s.density);\n const radius = useAppearanceStore((s) => s.radius);\n const zebra = useAppearanceStore((s) => s.zebra);\n const faviconSource = useAppearanceStore((s) => s.faviconSource);\n const setStorePref = useAppearanceStore((s) => s.set);\n const userId = useAuthStore((s) => s.user?.id);\n\n // Optimistic write: update the local store first (so the UI reacts\n // immediately) then persist to the user-scoped parameter. On error we\n // surface a toast — the local change stays so the user can retry.\n const setPref = <K extends keyof typeof PARAM_BY_KEY>(\n key: K,\n value: Parameters<typeof setStorePref>[1],\n ) => {\n setStorePref(key as Parameters<typeof setStorePref>[0], value);\n if (!userId) return;\n const code = PARAM_BY_KEY[key as string];\n if (!code) return;\n setUserParam(userId, code, value).catch((err: Error) => {\n toast.error(`Could not save ${key}`, err.message);\n });\n };\n\n return (\n <div style={{ flex: 1, overflowY: \"auto\", padding: \"14px 20px 28px\", background: \"var(--bg)\" }}>\n <div style={{ maxWidth: 900, margin: \"0 auto\" }}>\n <div style={{ marginBottom: 14 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600, letterSpacing: \"-0.012em\" }}>Preferences</h1>\n <p style={{ margin: \"4px 0 0\", fontSize: 12.5, color: \"var(--text-3)\" }}>\n These settings apply to your account. Tenant admins can set defaults in the{\" \"}\n <span className=\"mono\">workspace.yaml</span>.\n </p>\n </div>\n\n <SectionCard title=\"Appearance\" icon={<Icon.moon size={14} />}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <PrefRow label=\"Theme\" hint=\"Pick light or dark (system sync coming soon)\">\n <div style={{ display: \"flex\", gap: 6 }}>\n {([[\"light\", \"Light\"], [\"dark\", \"Dark\"]] as Array<[ThemeId, string]>).map(([k, l]) => (\n <SegBtn key={k} active={theme === k} onClick={() => setPref(\"theme\", k)}>{l}</SegBtn>\n ))}\n </div>\n </PrefRow>\n <PrefRow label=\"Accent color\" hint=\"Your personal accent across the app\">\n <div style={{ display: \"flex\", gap: 8 }}>\n {ACCENTS.map((a) => (\n <button\n key={a.id}\n onClick={() => setPref(\"accent\", a.id)}\n aria-label={`Accent ${a.id}`}\n style={{\n width: 26, height: 26, borderRadius: 7, background: a.c,\n border: accent === a.id ? \"2px solid var(--text)\" : \"2px solid transparent\",\n cursor: \"pointer\",\n }}\n />\n ))}\n </div>\n </PrefRow>\n <PrefRow label=\"Density\" hint=\"Compact saves space; cozy is easier on the eyes\">\n <div style={{ display: \"flex\", gap: 6 }}>\n {DENSITIES.map((d) => (\n <SegBtn key={d} active={density === d} onClick={() => setPref(\"density\", d)} capitalize>\n {d}\n </SegBtn>\n ))}\n </div>\n </PrefRow>\n <PrefRow label=\"Border radius\">\n <input\n type=\"range\" min={0} max={14} value={radius}\n onChange={(e) => setPref(\"radius\", Number(e.target.value))}\n style={{ width: 180 }}\n />\n <span className=\"mono num\" style={{ fontSize: 12, color: \"var(--text-3)\", marginLeft: 8 }}>{radius}px</span>\n </PrefRow>\n <PrefRow label=\"Zebra rows\" hint=\"Alternating row backgrounds on tables\">\n <BoolToggle value={zebra} onChange={(v) => setPref(\"zebra\", v)} />\n </PrefRow>\n <PrefRow\n label=\"URL favicons\"\n hint={FAVICON_SOURCES.find(([id]) => id === faviconSource)?.[2]}\n >\n <div style={{ display: \"flex\", gap: 6 }}>\n {FAVICON_SOURCES.map(([id, label]) => (\n <SegBtn\n key={id}\n active={faviconSource === id}\n onClick={() => setPref(\"faviconSource\", id)}\n >\n {label}\n </SegBtn>\n ))}\n </div>\n </PrefRow>\n </div>\n </SectionCard>\n\n <div style={{ height: 12 }} />\n <SectionCard title=\"Keyboard\" icon={<Icon.command size={14} />}>\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 8 }}>\n {SHORTCUTS.map(([l, k]) => (\n <div key={l} style={{ display: \"flex\", alignItems: \"center\", padding: \"5px 0\" }}>\n <span style={{ flex: 1, fontSize: 12.5, color: \"var(--text-2)\" }}>{l}</span>\n <Kbd>{k}</Kbd>\n </div>\n ))}\n </div>\n </SectionCard>\n </div>\n </div>\n );\n}\n\nfunction PrefRow({ label, hint, children }: { label: string; hint?: string; children: ReactNode }) {\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 14, padding: \"6px 0\" }}>\n <div style={{ width: 200, flexShrink: 0 }}>\n <div style={{ fontSize: 12.5, fontWeight: 500, color: \"var(--text)\" }}>{label}</div>\n {hint && <div style={{ fontSize: 11, color: \"var(--text-3)\" }}>{hint}</div>}\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", flex: 1 }}>{children}</div>\n </div>\n );\n}\n\nfunction SegBtn({ active, onClick, children, capitalize }: { active: boolean; onClick: () => void; children: ReactNode; capitalize?: boolean }) {\n return (\n <button\n onClick={onClick}\n style={{\n padding: \"6px 12px\", fontSize: 12, borderRadius: 6, cursor: \"pointer\",\n textTransform: capitalize ? \"capitalize\" : \"none\",\n background: active ? \"var(--accent)\" : \"var(--surface-2)\",\n color: active ? \"var(--accent-foreground)\" : \"var(--text-2)\",\n border: `1px solid ${active ? \"var(--accent)\" : \"var(--border)\"}`,\n }}\n >\n {children}\n </button>\n );\n}\n","import { useState, useMemo } from \"react\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { Btn, Pill } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { loc } from \"@/types/schema\";\nimport type { Action, HandlerDef, HandlerParam } from \"@/types/schema\";\n\ntype Tab = \"handlers\" | \"actions\";\n\nexport function AdminCatalogPage() {\n const { data: schema } = useSchema();\n const [activeTab, setActiveTab] = useState<Tab>(\"handlers\");\n\n if (!schema) return null;\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <div style={{ padding: \"20px 20px 0\" }}>\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 10, marginBottom: 12 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600, letterSpacing: \"-0.012em\", color: \"var(--text)\" }}>\n Handler & Action Catalog\n </h1>\n </div>\n <div style={{ display: \"flex\", gap: 6, marginBottom: 14 }}>\n <Btn\n kind={activeTab === \"handlers\" ? \"primary\" : \"default\"}\n active={activeTab === \"handlers\"}\n onClick={() => setActiveTab(\"handlers\")}\n >\n Handlers\n </Btn>\n <Btn\n kind={activeTab === \"actions\" ? \"primary\" : \"default\"}\n active={activeTab === \"actions\"}\n onClick={() => setActiveTab(\"actions\")}\n >\n Actions\n </Btn>\n </div>\n </div>\n <div style={{ flex: 1, overflowY: \"auto\", padding: \"0 20px 20px\" }}>\n {activeTab === \"handlers\" && <HandlersTab schema={schema} />}\n {activeTab === \"actions\" && <ActionsTab schema={schema} />}\n </div>\n </div>\n );\n}\n\nfunction HandlersTab({ schema }: { schema: NonNullable<ReturnType<typeof useSchema>[\"data\"]> }) {\n const allHandlers = useMemo(() => {\n const result: Array<{ code: string; handler: HandlerDef; entity?: string }> = [];\n if (schema.handlers) {\n for (const [code, h] of Object.entries(schema.handlers)) {\n result.push({ code, handler: h });\n }\n }\n for (const [entityCode, entity] of Object.entries(schema.entities)) {\n if (entity.handlers) {\n for (const [code, h] of Object.entries(entity.handlers as Record<string, HandlerDef>)) {\n result.push({ code: `${entityCode}.${code}`, handler: h, entity: entityCode });\n }\n }\n }\n return result.sort((a, b) => a.code.localeCompare(b.code));\n }, [schema]);\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 10 }}>\n <p style={{ fontSize: 12.5, color: \"var(--text-3)\", margin: 0 }}>\n {allHandlers.length} handlers registered (view-only — source of truth is code/YAML)\n </p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n {allHandlers.map(({ code, handler, entity }) => (\n <HandlerCard key={code} code={code} handler={handler} entity={entity} />\n ))}\n </div>\n </div>\n );\n}\n\nfunction HandlerCard({ code, handler, entity }: { code: string; handler: HandlerDef; entity?: string }) {\n const [expanded, setExpanded] = useState(false);\n const displayName = handler.display?.name ? loc(handler.display.name) : code;\n\n return (\n <div\n style={{\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: 14,\n background: \"var(--surface)\",\n }}\n >\n <div\n style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", cursor: \"pointer\" }}\n onClick={() => setExpanded((prev) => !prev)}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10 }}>\n {handler.display?.icon && (\n <span style={{ color: \"var(--text-3)\", display: \"flex\" }}>\n <DynamicIcon name={handler.display.icon} size={14} />\n </span>\n )}\n <div>\n <span className=\"mono\" style={{ fontSize: 12.5, fontWeight: 500 }}>\n {code}\n </span>\n {displayName !== code && (\n <span style={{ marginLeft: 8, fontSize: 12.5, color: \"var(--text-3)\" }}>\n {displayName}\n </span>\n )}\n </div>\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n <Pill tone=\"slate\" size=\"xs\">\n {handler.type}\n </Pill>\n {entity && (\n <Pill tone=\"violet\" size=\"xs\">\n {entity}\n </Pill>\n )}\n {handler.success_event && (\n <Pill tone=\"emerald\" size=\"xs\">\n event: {handler.success_event}\n </Pill>\n )}\n </div>\n </div>\n {expanded && handler.params && handler.params.length > 0 && (\n <div style={{ marginTop: 12, borderTop: \"1px solid var(--divider)\", paddingTop: 12 }}>\n <p\n style={{\n fontSize: 10.5,\n fontWeight: 600,\n color: \"var(--text-3)\",\n marginBottom: 8,\n marginTop: 0,\n textTransform: \"uppercase\",\n letterSpacing: \"0.04em\",\n }}\n >\n Parameters\n </p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n {(handler.params?.names() ?? []).map((paramCode) => {\n const param = handler.params!.get(paramCode);\n if (!param) return null;\n return <ParamRow key={paramCode} code={paramCode} param={param} />;\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction ParamRow({ code, param }: { code: string; param: HandlerParam }) {\n const displayName = param.display?.name ? loc(param.display.name) : undefined;\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, fontSize: 12, padding: \"2px 0\" }}>\n <span className=\"mono\" style={{ fontSize: 11, width: 128, flexShrink: 0 }}>\n {code}\n </span>\n <Pill tone=\"slate\" size=\"xs\">\n {param.type || \"string\"}\n </Pill>\n {param.required && (\n <Pill tone=\"rose\" size=\"xs\">\n required\n </Pill>\n )}\n {param.hidden && (\n <Pill tone=\"slate\" size=\"xs\">\n hidden\n </Pill>\n )}\n {param.default != null && (\n <span style={{ fontSize: 11, color: \"var(--text-3)\" }}>default: {String(param.default)}</span>\n )}\n {displayName && <span style={{ fontSize: 11, color: \"var(--text-3)\" }}>{displayName}</span>}\n </div>\n );\n}\n\nfunction ActionsTab({ schema }: { schema: NonNullable<ReturnType<typeof useSchema>[\"data\"]> }) {\n const allActions = useMemo(() => {\n const result: Array<{ code: string; action: Action; entity?: string }> = [];\n if (schema.actions) {\n for (const [code, action] of Object.entries(schema.actions)) {\n result.push({ code, action });\n }\n }\n for (const [entityCode, entity] of Object.entries(schema.entities)) {\n if (entity.actions) {\n for (const [code, action] of Object.entries(entity.actions)) {\n result.push({ code: `${entityCode}.${code}`, action, entity: entityCode });\n }\n }\n }\n return result.sort((a, b) => a.code.localeCompare(b.code));\n }, [schema]);\n\n const grouped = useMemo(() => {\n const groups: Record<string, typeof allActions> = {};\n for (const item of allActions) {\n const key = item.entity || \"(global)\";\n if (!groups[key]) groups[key] = [];\n groups[key].push(item);\n }\n return Object.entries(groups).sort(([a], [b]) => a.localeCompare(b));\n }, [allActions]);\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 14 }}>\n <p style={{ fontSize: 12.5, color: \"var(--text-3)\", margin: 0 }}>\n {allActions.length} actions defined (view-only — source of truth is code/YAML)\n </p>\n {grouped.map(([group, actions]) => (\n <div key={group}>\n <h3 style={{ fontSize: 12.5, fontWeight: 600, margin: \"0 0 8px 0\", color: \"var(--text)\" }}>\n {group}\n </h3>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6 }}>\n {actions.map(({ code, action }) => (\n <ActionCard key={code} code={code} action={action} />\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction ActionCard({ code, action }: { code: string; action: Action }) {\n const displayName = loc(action.display?.name);\n return (\n <div\n style={{\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: 12,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n background: \"var(--surface)\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10 }}>\n {action.display?.icon && (\n <span style={{ color: \"var(--text-3)\", display: \"flex\" }}>\n <DynamicIcon name={action.display.icon} size={14} />\n </span>\n )}\n <div>\n <span className=\"mono\" style={{ fontSize: 12.5 }}>\n {code}\n </span>\n {displayName && displayName !== code && (\n <span style={{ marginLeft: 8, fontSize: 12.5, color: \"var(--text-3)\" }}>{displayName}</span>\n )}\n </div>\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n {/*\n The legacy `action.type` enum was removed in unified-toolbar Phase 1\n (plan §1-§2). Dispatch mode now derives from which field is set:\n handler / association / widget / href. Show the first one present\n as a slate pill - same visual affordance, sourced from the new wire\n format. Reason: keeps the catalog explorable without inventing UI.\n */}\n {(() => {\n const mode = action.handler\n ? \"handler\"\n : action.association\n ? \"association\"\n : action.widget\n ? \"widget\"\n : action.href\n ? \"href\"\n : null;\n return mode ? (\n <Pill tone=\"slate\" size=\"xs\">\n {mode}\n </Pill>\n ) : null;\n })()}\n {action.handler && (\n <span className=\"mono\" style={{ fontSize: 11, color: \"var(--text-3)\" }}>\n handler: {action.handler}\n </span>\n )}\n {action.visibility && action.visibility.length > 0 && (\n <div style={{ display: \"flex\", gap: 4 }}>\n {action.visibility.map((v) => (\n <Pill key={v} tone=\"slate\" size=\"xs\">\n {v}\n </Pill>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","// exportRequest.ts: shared builders for the list-query JSON body.\n//\n// One source of truth for what \"the current list view\" means on the wire.\n// Both the URL list path (useEntityData) and the export endpoint\n// (POST /api/data/{entity}/export) consume the SAME query shape. A single\n// builder keeps them in lock-step so a filter that's active on screen is\n// also active in the CSV — the central UX promise of the export feature.\n//\n// Two layers:\n//\n// - buildListQuery(state) → ExportQuery\n// The canonical list-query shape. Identical fields and semantics to the\n// URL list params parsed by parseListParams on the server. Use for\n// anything that needs to describe the active view (export, future\n// server-side print, future schedule-this-view).\n//\n// - buildExportRequest(state) → ExportRequest\n// Wraps buildListQuery with export-specific intent (scope, columns,\n// selection, locale, filename). Used only by ExportMenu.\n\nimport type { FilterNodeParam } from \"@/types/api\";\n\n/** ExportQuery mirrors the Go ExportQuery struct (server/handlers/data_export.go).\n * Field order matches; semantics match parseListParams 1:1. */\nexport interface ExportQuery {\n screen?: string;\n filters?: FilterNodeParam[];\n search?: string;\n sort?: string;\n include_deleted?: boolean;\n page?: number;\n per_page?: number;\n}\n\n/** ExportScope values accepted by the server. v1 supports `current_view`,\n * `selected_rows`, and `current_page`; `template` is reserved for v2. */\nexport type ExportScope = \"current_view\" | \"selected_rows\" | \"current_page\";\n\n/** ExportRequest mirrors the Go ExportRequest struct. */\nexport interface ExportRequest {\n format: \"csv\";\n scope: ExportScope;\n query: ExportQuery;\n columns: string[];\n selection?: Array<Record<string, string>>;\n locale?: string;\n /** v1 wire defaults: `header_style=\"label\"`, `ref_mode=\"display\"`. v2 will\n * add `code` / `stable_key`. Omitted = server defaults. */\n header_style?: \"label\";\n ref_mode?: \"display\";\n filename?: string;\n}\n\n/** ListViewState captures the SmartListPage hook state needed to describe\n * the current view. Field names mirror useEntityData state so the call\n * site can spread it as-is. */\nexport interface ListViewState {\n /** Active screen code, when the list is rendered through a screen. */\n screenCode?: string;\n /** JSON filter tree (the same shape sent in the `filters` URL param). */\n jsonFilters?: FilterNodeParam[];\n /** Debounced search value from the toolbar. */\n search?: string;\n /** \"field:asc\" / \"field:desc\" sort spec. */\n sort?: string;\n /** Show soft-deleted rows. */\n includeDeleted?: boolean;\n /** Current page (1-based). Used only for `current_page` scope. */\n page?: number;\n perPage?: number;\n}\n\n/** Shape of the visible columns in the table, before _select / _actions\n * pseudo-columns are stripped. */\nconst RESERVED_COLUMN_KEYS = new Set([\"_select\", \"_actions\"]);\n\n/** Locale resolver hook input — typically a string from the SDK ctx\n * (e.g. `useLocale()`); empty/undefined defers to the server default. */\nexport interface ExportIntent {\n scope: ExportScope;\n /** All visible columns in display order. _select / _actions are stripped\n * by buildExportRequest. */\n visibleColumns: string[];\n /** PK objects of the currently-selected rows. Non-empty only for\n * `selected_rows` scope. */\n selectedRowPKs?: Array<Record<string, string>>;\n locale?: string;\n filename?: string;\n}\n\n/** buildListQuery returns the canonical list-query JSON shape from the\n * hook state the table renders against. Output is plain-object so it\n * serializes deterministically through JSON.stringify (key order is\n * controlled by spread order below). */\nexport function buildListQuery(state: ListViewState): ExportQuery {\n const out: ExportQuery = {};\n if (state.screenCode) out.screen = state.screenCode;\n if (state.jsonFilters && state.jsonFilters.length > 0) {\n out.filters = state.jsonFilters;\n }\n if (state.search) out.search = state.search;\n if (state.sort) out.sort = state.sort;\n if (state.includeDeleted) out.include_deleted = true;\n if (state.page) out.page = state.page;\n if (state.perPage) out.per_page = state.perPage;\n return out;\n}\n\n/** buildExportRequest assembles the POST body for the export endpoint.\n * The query block is always present (even for selected_rows; the server\n * ignores filters/search/sort under that scope but keeps screen for\n * per-screen export gating). Reserved column keys (_select, _actions)\n * are stripped here, never on the server. */\nexport function buildExportRequest(\n state: ListViewState,\n intent: ExportIntent,\n): ExportRequest {\n const columns = intent.visibleColumns.filter((c) => !RESERVED_COLUMN_KEYS.has(c));\n const req: ExportRequest = {\n format: \"csv\",\n scope: intent.scope,\n query: buildListQuery(state),\n columns,\n };\n if (intent.scope === \"selected_rows\" && intent.selectedRowPKs && intent.selectedRowPKs.length > 0) {\n req.selection = intent.selectedRowPKs;\n }\n if (intent.locale) req.locale = intent.locale;\n if (intent.filename) req.filename = intent.filename;\n return req;\n}\n","// ExportMenu.tsx: toolbar dropdown that triggers POST /api/data/{entity}/export.\n//\n// Replaces the \"Export coming soon\" placeholders at SmartListPage.tsx:882-889\n// (toolbar button) and :2007-2015 (selection menu item). The component is\n// schema-aware: renders nothing when entity.export.enabled === false or\n// screen.export.enabled === false.\n//\n// Wire contract is buildExportRequest from ./exportRequest. UX contract is\n// docs/plans/2026-05-01-standard-table-export.md §4 — three scope items\n// (Current view / Selected rows / Current page), CSV-only in v1, button\n// shows pending state until response headers arrive, server errors keep the\n// dropdown open and show the {code, message} envelope inline.\n\nimport { useRef, useState } from \"react\";\nimport { Btn, Dropdown, Icon, MenuItem } from \"@/components/primitives\";\nimport type { Entity, Screen } from \"@/types/schema\";\nimport { exportEntities } from \"@/api/data\";\nimport { ApiError } from \"@/types/api\";\nimport {\n buildExportRequest,\n type ExportScope,\n type ListViewState,\n} from \"./exportRequest\";\n\nexport interface ExportMenuProps {\n entityCode: string;\n entity: Entity;\n screen?: Screen | undefined;\n /** Live list-view state (filters/search/sort/page/per_page/etc). */\n viewState: ListViewState;\n /** Visible columns in display order. _select / _actions stripped by buildExportRequest. */\n visibleColumns: string[];\n /** Currently-selected row PKs. Empty array = nothing selected; the\n * Selected scope item is hidden. Caller is responsible for translating\n * whatever row-selection state shape into PK objects. */\n selectedRowPKs: Array<Record<string, string>>;\n /** Total row count for the current view, when known (from list meta.total).\n * When undefined, the menu shows \"all matching rows\" instead of a number. */\n totalRows?: number;\n /** Per-page size of the current view (for the Current page label). */\n pageSize?: number;\n /** Caller's UI locale (e.g. \"en\"). Server falls back to ui_language when omitted. */\n locale?: string;\n /** Optional placement override for the popover. */\n align?: \"start\" | \"end\";\n /** When true, render only as a single MenuItem suitable for embedding inside\n * the existing selection menu (replaces the placeholder at :2007-2015).\n * Defaults to a full Btn + Dropdown toolbar item. */\n variant?: \"toolbar\" | \"selection-menu-item\";\n /** Optional click-handler ran before scope dispatch — e.g. to close a\n * parent menu so the export popover doesn't stack underneath. */\n onBeforeOpen?: () => void;\n}\n\n// pickEnabled returns false ONLY when an explicit `enabled: false` is set.\n// Any other shape (block absent, block present without `enabled`, `enabled: true`)\n// → enabled. Mirrors the Go ExportEnabled() helpers.\nfunction pickEnabled(block: { enabled?: boolean } | undefined): boolean {\n return !(block && block.enabled === false);\n}\n\n// formatRowCount turns the total into a label fragment. Undefined means\n// \"we don't know yet\" — fall back to a friendly \"all matching rows\".\nfunction formatRowCount(total: number | undefined): string {\n if (total == null) return \"all matching rows\";\n return `${total.toLocaleString()} ${total === 1 ? \"row\" : \"rows\"}`;\n}\n\nexport function ExportMenu(props: ExportMenuProps) {\n const {\n entityCode,\n entity,\n screen,\n viewState,\n visibleColumns,\n selectedRowPKs,\n totalRows,\n pageSize,\n locale,\n align = \"end\",\n variant = \"toolbar\",\n onBeforeOpen,\n } = props;\n\n // Hooks MUST be called unconditionally on every render, before any\n // early-return branch — React's rules-of-hooks. Compute the visibility\n // gate after the hooks.\n const [open, setOpen] = useState(false);\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n const anchorRef = useRef<HTMLButtonElement | null>(null);\n\n // Hidden when either gate says no. Server enforces the same rule, so\n // hiding here is purely UX — refusing to show a button that would 403.\n const entityEnabled = pickEnabled(entity.export);\n const screenEnabled = pickEnabled(screen?.export);\n if (!entityEnabled || !screenEnabled) {\n return null;\n }\n\n async function runExport(scope: ExportScope) {\n setError(null);\n setPending(true);\n try {\n const body = buildExportRequest(viewState, {\n scope,\n visibleColumns,\n selectedRowPKs: scope === \"selected_rows\" ? selectedRowPKs : undefined,\n locale,\n });\n const { blob, filename } = await exportEntities(entityCode, body);\n const downloadName = filename || `${entityCode}-${timestampedSuffix()}.csv`;\n triggerDownload(blob, downloadName);\n setOpen(false);\n } catch (err) {\n const e = err instanceof ApiError ? { code: err.code, message: err.message } : { code: \"UNKNOWN\", message: String(err) };\n setError(e);\n // Keep dropdown open (UX contract) so the user can re-pick a scope.\n } finally {\n setPending(false);\n }\n }\n\n if (variant === \"selection-menu-item\") {\n const blocked = pending || selectedRowPKs.length === 0;\n return (\n <MenuItem\n icon={<Icon.download size={12} />}\n data-testid=\"export-selection-menu-item\"\n onClick={blocked ? undefined : () => {\n onBeforeOpen?.();\n void runExport(\"selected_rows\");\n }}\n >\n {pending ? \"Exporting…\" : \"Export selection (CSV)\"}\n </MenuItem>\n );\n }\n\n const hasSelection = selectedRowPKs.length > 0;\n\n return (\n <>\n <Btn\n ref={anchorRef as never}\n kind=\"ghost\"\n size=\"sm\"\n icon={<Icon.download size={12} />}\n onClick={() => {\n onBeforeOpen?.();\n setOpen((v) => !v);\n }}\n disabled={pending}\n >\n {pending ? \"Exporting…\" : \"Export\"}\n </Btn>\n <Dropdown open={open} anchor={anchorRef.current} onClose={() => setOpen(false)} align={align} width={260}>\n <MenuItem\n icon={<Icon.download size={12} />}\n onClick={pending ? undefined : () => void runExport(\"current_view\")}\n >\n Current view (CSV) · {formatRowCount(totalRows)}\n </MenuItem>\n {hasSelection && (\n <MenuItem\n icon={<Icon.check size={12} />}\n onClick={pending ? undefined : () => void runExport(\"selected_rows\")}\n >\n Selected rows (CSV) · {selectedRowPKs.length}\n </MenuItem>\n )}\n <MenuItem\n icon={<Icon.columns size={12} />}\n onClick={pending ? undefined : () => void runExport(\"current_page\")}\n >\n Current page (CSV) · {pageSize ? `${pageSize} rows` : \"page window\"}\n </MenuItem>\n <MenuItem icon={<Icon.grid size={12} />}>\n XLSX (coming soon)\n </MenuItem>\n {error && (\n <div\n role=\"alert\"\n style={{\n padding: \"6px 10px\",\n marginTop: 4,\n fontSize: 12,\n color: \"var(--destructive)\",\n borderTop: \"1px solid var(--border)\",\n }}\n >\n <strong>{error.code}</strong>: {error.message}\n </div>\n )}\n </Dropdown>\n </>\n );\n}\n\n// timestampedSuffix returns a YYYYMMDD-HHMM stem matching the server's\n// default filename shape (server/handlers/data_export.go::exportFilename).\n// Used only when Content-Disposition is missing (degraded clients,\n// legacy proxies stripping headers). Identical algorithm so client + server\n// produce visually consistent download names.\nfunction timestampedSuffix(): string {\n const d = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}-${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}`;\n}\n\n// triggerDownload turns a Blob into a save-as. Allocates an object URL,\n// dispatches a click on a synthetic anchor, then revokes the URL on the\n// next tick. Compatible with Chrome / Firefox / Safari / Edge / mobile\n// Safari (which requires the anchor to be in the DOM at click time).\nfunction triggerDownload(blob: Blob, filename: string) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.style.display = \"none\";\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Free the URL on the next tick so the browser has time to start the\n // download — revoking synchronously cancels the request in some browsers.\n setTimeout(() => URL.revokeObjectURL(url), 0);\n}\n","import { useState, useEffect } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState(value);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n\n return debounced;\n}\n","import type { ReactNode } from \"react\";\nimport { DynamicIcon } from \"./DynamicIcon\";\n\ninterface EmptyStateProps {\n icon?: string;\n title: string;\n description?: ReactNode;\n action?: ReactNode;\n}\n\nexport function EmptyState({ icon, title, description, action }: EmptyStateProps) {\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"64px 16px\",\n textAlign: \"center\",\n }}\n >\n {icon && (\n <div\n style={{\n marginBottom: 16,\n borderRadius: 999,\n background: \"var(--surface-3)\",\n padding: 16,\n color: \"var(--text-3)\",\n display: \"inline-flex\",\n }}\n >\n <DynamicIcon name={icon} size={32} />\n </div>\n )}\n <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600, color: \"var(--text)\" }}>{title}</h3>\n {description && (\n <p style={{ marginTop: 4, fontSize: 12.5, color: \"var(--text-3)\", maxWidth: 420 }}>\n {description}\n </p>\n )}\n {action && <div style={{ marginTop: 16 }}>{action}</div>}\n </div>\n );\n}\n","/**\n * Returns the URL when its scheme is safe to use as an `<a href>` value, or\n * undefined otherwise. Prevents `javascript:`, `data:`, `vbscript:`, etc. from\n * sneaking through user-supplied (manifest- or DB-supplied) link strings into\n * a clickable anchor.\n *\n * Allowlist: http, https, mailto. Relative URLs (no scheme, no leading `//`)\n * pass through unchanged so SDK-internal routing keeps working.\n */\nconst ALLOWED_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n// Matches ASCII control characters (U+0000-U+001F and U+007F) - this range\n// covers the whitespace browsers ignore when normalizing an href (\"\\t\", \"\\n\",\n// \"\\r\"). The WHATWG URL parser strips these before scheme detection, so\n// without this filter `java\\nscript:alert(1)` slips through the allowlist\n// below and executes as `javascript:` once the browser parses the href.\n//\n// The range is written with `\\u` escapes (not literal control bytes) so this\n// security helper stays plain text and visible to `grep` / `rg` audits.\nconst CONTROL_OR_WHITESPACE_RE = /[\\u0000-\\u001f\\u007f]/g;\n\nexport function safeExternalUrl(input: string | undefined | null): string | undefined {\n if (!input) return undefined;\n const trimmed = input.trim();\n if (!trimmed) return undefined;\n\n // Strip browser-ignored control / whitespace characters BEFORE allow-listing\n // the scheme so the check evaluates what the browser would actually execute.\n const sanitized = trimmed.replace(CONTROL_OR_WHITESPACE_RE, \"\");\n if (!sanitized) return undefined;\n\n // Protocol-relative URLs (`//host/path`) inherit the page scheme — treat as\n // external and require an explicit allowlisted scheme on the manifest side.\n if (sanitized.startsWith(\"//\")) return undefined;\n\n // Anything starting with a scheme must be in the allowlist. We test for\n // `<scheme>:` rather than parsing to URL to keep relative paths working\n // without throwing.\n const schemeMatch = sanitized.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);\n if (schemeMatch) {\n const scheme = schemeMatch[1].toLowerCase() + \":\";\n if (!ALLOWED_SCHEMES.includes(scheme as (typeof ALLOWED_SCHEMES)[number])) {\n return undefined;\n }\n }\n return sanitized;\n}\n","const shimmer: React.CSSProperties = {\n display: \"block\",\n background:\n \"linear-gradient(90deg, var(--surface-3) 0%, var(--surface-2) 40%, var(--surface-3) 80%)\",\n backgroundSize: \"200% 100%\",\n borderRadius: 6,\n animation: \"declarion-shimmer 1.2s ease-in-out infinite\",\n};\n\nfunction Skel({ height, width }: { height: number; width?: string | number }) {\n return (\n <>\n <style>{`@keyframes declarion-shimmer{from{background-position:200% 0}to{background-position:-200% 0}}`}</style>\n <span\n style={{\n ...shimmer,\n height,\n width: width ?? \"100%\",\n }}\n />\n </>\n );\n}\n\nexport function TableSkeleton({ rows = 8, cols = 4 }: { rows?: number; cols?: number }) {\n return (\n <div style={{ padding: 16, display: \"flex\", flexDirection: \"column\", gap: 10 }}>\n <div style={{ display: \"flex\", gap: 16 }}>\n {Array.from({ length: cols }).map((_, i) => (\n <div key={i} style={{ flex: 1 }}>\n <Skel height={14} />\n </div>\n ))}\n </div>\n {Array.from({ length: rows }).map((_, i) => (\n <div key={i} style={{ display: \"flex\", gap: 16 }}>\n {Array.from({ length: cols }).map((_, j) => (\n <div key={j} style={{ flex: 1 }}>\n <Skel height={28} />\n </div>\n ))}\n </div>\n ))}\n </div>\n );\n}\n\n// DetailSkeleton mirrors the real detail-page shape - a record-identity\n// header (breadcrumb, glyph, title) followed by section cards - so the\n// content swaps in with minimal layout jump.\nexport function DetailSkeleton() {\n return (\n <div style={{ padding: 20, display: \"flex\", flexDirection: \"column\", gap: 16 }}>\n {/* Record header */}\n <Skel height={12} width={160} />\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <span style={{ ...shimmer, width: 32, height: 32, borderRadius: 8 }} />\n <Skel height={22} width={240} />\n </div>\n {/* Section cards */}\n {Array.from({ length: 2 }).map((_, s) => (\n <div\n key={s}\n style={{\n border: \"1px solid var(--border)\",\n borderRadius: 10,\n background: \"var(--surface)\",\n overflow: \"hidden\",\n }}\n >\n <div style={{ padding: \"11px 14px\", borderBottom: \"1px solid var(--divider)\" }}>\n <Skel height={13} width={150} />\n </div>\n <div\n style={{\n padding: 16,\n display: \"grid\",\n gridTemplateColumns: \"repeat(2, minmax(0, 1fr))\",\n gap: \"12px 24px\",\n }}\n >\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} style={{ display: \"flex\", gap: 16 }}>\n <Skel height={13} width={110} />\n <div style={{ flex: 1 }}>\n <Skel height={13} />\n </div>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","/**\n * Filter system types and utilities.\n * One JSON format everywhere: API, URL, DB, frontend state.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single node in the recursive filter tree. */\nexport interface FilterNode {\n /** Field condition */\n field?: string;\n op?: FilterOp;\n value?: unknown; // string | number | string[] | number[] | null\n\n /** OR group: each inner array is AND-ed, groups are OR-ed */\n or?: FilterNode[][];\n /** Explicit AND group (rarely needed, top-level is implicit AND) */\n and?: FilterNode[];\n}\n\n/** All supported filter operator codes. */\nexport type FilterOp =\n | \"eq\" | \"not_eq\"\n | \"gt\" | \"gte\" | \"lt\" | \"lte\"\n | \"in\" | \"not_in\" | \"between\"\n | \"contains\" | \"starts_with\"\n | \"is_empty\" | \"is_not_empty\"\n | \"today\" | \"this_week\" | \"this_month\"\n | \"last_7_days\" | \"last_30_days\"\n | \"last_hour\" | \"last_24_hours\";\n\n/** Operators that don't take a value. */\nexport const NO_VALUE_OPS = new Set<FilterOp>([\n \"is_empty\", \"is_not_empty\",\n \"today\", \"this_week\", \"this_month\",\n \"last_7_days\", \"last_30_days\",\n \"last_hour\", \"last_24_hours\",\n]);\n\n/** Human-friendly display names for operators. */\nexport const OP_LABELS: Record<FilterOp, string> = {\n eq: \"is\",\n not_eq: \"is not\",\n gt: \">\",\n gte: \">=\",\n lt: \"<\",\n lte: \"<=\",\n in: \"is any of\",\n not_in: \"is none of\",\n between: \"between\",\n contains: \"contains\",\n starts_with: \"starts with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n today: \"today\",\n this_week: \"this week\",\n this_month: \"this month\",\n last_7_days: \"last 7 days\",\n last_30_days: \"last 30 days\",\n last_hour: \"last hour\",\n last_24_hours: \"last 24 hours\",\n};\n\n// ---------------------------------------------------------------------------\n// URL serialization\n// ---------------------------------------------------------------------------\n\n/** Parse filters from URL search params (JSON `filters` param). */\nexport function parseFiltersFromURL(searchParams: URLSearchParams): FilterNode[] {\n const raw = searchParams.get(\"filters\");\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n}\n\n/** Write filters to URL search params. */\nexport function setFiltersInURL(\n filters: FilterNode[],\n searchParams: URLSearchParams,\n): void {\n if (filters.length === 0) {\n searchParams.delete(\"filters\");\n } else {\n searchParams.set(\"filters\", JSON.stringify(filters));\n }\n}\n\n// ---------------------------------------------------------------------------\n// API conversion\n// ---------------------------------------------------------------------------\n\n/**\n * Convert FilterNode array to API query params.\n * Uses the JSON `filters` param for the API request.\n */\nexport function filtersToApiParams(filters: FilterNode[]): URLSearchParams {\n const params = new URLSearchParams();\n if (filters.length > 0) {\n params.set(\"filters\", JSON.stringify(filters));\n }\n return params;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Check if the filter tree has any OR groups (needs Level 2 UI). */\nexport function hasOrGroups(nodes: FilterNode[]): boolean {\n return nodes.some((n) => n.or != null);\n}\n\n/** Extract flat field conditions from a FilterNode array (ignoring OR/AND structure). */\nexport function flatFieldConditions(nodes: FilterNode[]): FilterNode[] {\n const result: FilterNode[] = [];\n for (const n of nodes) {\n if (n.field && n.op) {\n result.push(n);\n }\n if (n.or) {\n for (const group of n.or) {\n result.push(...flatFieldConditions(group));\n }\n }\n if (n.and) {\n result.push(...flatFieldConditions(n.and));\n }\n }\n return result;\n}\n\n/** Count active field conditions in the filter tree. */\nexport function countActiveFilters(nodes: FilterNode[]): number {\n return flatFieldConditions(nodes).length;\n}\n\n/** Format a filter node for chip display. */\nexport function formatFilterChip(node: FilterNode): string {\n if (!node.field || !node.op) return \"\";\n const label = OP_LABELS[node.op] || node.op;\n if (NO_VALUE_OPS.has(node.op)) {\n return `${label}`;\n }\n const val = Array.isArray(node.value)\n ? node.value.join(\", \")\n : String(node.value ?? \"\");\n return `${label} ${val}`;\n}\n\n/** Create a simple equality filter node. */\nexport function eqFilter(field: string, value: string): FilterNode {\n return { field, op: \"eq\", value };\n}\n\n/** Remove a filter node by field name, recursing into OR/AND groups. */\nexport function removeFilter(nodes: FilterNode[], field: string): FilterNode[] {\n const result: FilterNode[] = [];\n for (const n of nodes) {\n if (n.field === field) continue;\n if (n.or) {\n const cleaned = n.or\n .map((group) => removeFilter(group, field))\n .filter((group) => group.length > 0);\n if (cleaned.length > 0) result.push({ ...n, or: cleaned });\n continue;\n }\n if (n.and) {\n const cleaned = removeFilter(n.and, field);\n if (cleaned.length > 0) result.push({ ...n, and: cleaned });\n continue;\n }\n result.push(n);\n }\n return result;\n}\n","import { useMemo, useCallback } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n type FilterNode,\n parseFiltersFromURL,\n setFiltersInURL,\n countActiveFilters,\n} from \"@/lib/filters\";\n\n/**\n * useFilters reads/writes the JSON `filters` URL param.\n * URL is the source of truth.\n */\nexport function useFilters() {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const filters = useMemo<FilterNode[]>(\n () => parseFiltersFromURL(searchParams),\n [searchParams],\n );\n\n const activeCount = useMemo(() => countActiveFilters(filters), [filters]);\n\n const setFilters = useCallback(\n (newFilters: FilterNode[]) => {\n const next = new URLSearchParams(searchParams);\n setFiltersInURL(newFilters, next);\n next.set(\"page\", \"1\");\n setSearchParams(next);\n },\n [searchParams, setSearchParams],\n );\n\n const clearFilters = useCallback(() => {\n const next = new URLSearchParams(searchParams);\n next.delete(\"filters\");\n next.set(\"page\", \"1\");\n setSearchParams(next);\n }, [searchParams, setSearchParams]);\n\n return { filters, activeCount, setFilters, clearFilters };\n}\n","// PeekFieldCell — the editable cell used inside QuickPeek. Ports the\n// prototype's QuickEditCell semantics (display-by-default, click-to-edit,\n// ✓/✗ commit/cancel) on top of the typed renderField registry so each field\n// type keeps its existing display + edit renderers (e.g. UrlField's anchor,\n// EnumField's Pill, BoolField's dot).\n//\n// Commit-only invariant: typing into an editor does NOT propagate to\n// `onCommit` until the user explicitly confirms (✓ / Enter), or picks a\n// value in a picker-style type (enum, bool, ref). Blur does NOT commit —\n// matches fields.jsx:195-231 prototype exactly.\nimport {\n useCallback,\n useEffect,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n} from \"react\";\nimport { Icon, IconBtn } from \"@/components/primitives\";\nimport { renderField, type RefsMap } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport type { EntityField } from \"@/types/schema\";\n\n// Picker types auto-commit on user selection — the choice itself IS the\n// commit. Text-like types need explicit ✓/Enter confirmation.\nconst PICKER_TYPES = new Set([\"enum\", \"bool\", \"ref\"]);\n\nfunction isPickerField(field: EntityField): boolean {\n const widget = field.display?.widget;\n if (widget && PICKER_TYPES.has(widget)) return true;\n return PICKER_TYPES.has(field.type);\n}\n\nexport interface PeekFieldCellProps {\n field: EntityField;\n fieldName: string;\n /** User-facing localized label — drives the `aria-label` so screen\n * readers speak the display name, not the raw schema key. */\n label: string;\n value: unknown;\n record?: Record<string, unknown>;\n refs?: RefsMap;\n readOnly?: boolean;\n /** Fired exactly once per explicit user commit. */\n onCommit: (next: unknown) => void;\n}\n\nexport function PeekFieldCell({\n field,\n fieldName,\n label,\n value,\n record,\n refs,\n readOnly,\n onCommit,\n}: PeekFieldCellProps) {\n const userLocale = useUserLocale();\n const [editing, setEditing] = useState(false);\n const [tmp, setTmp] = useState<unknown>(value);\n const picker = isPickerField(field);\n\n useEffect(() => {\n if (!editing) setTmp(value);\n }, [value, editing]);\n\n const enterEdit = useCallback(() => {\n if (readOnly) return;\n setTmp(value);\n setEditing(true);\n }, [readOnly, value]);\n\n const commit = useCallback(\n (next?: unknown) => {\n const v = next === undefined ? tmp : next;\n onCommit(v);\n setEditing(false);\n },\n [onCommit, tmp],\n );\n\n const cancel = useCallback(() => {\n setTmp(value);\n setEditing(false);\n }, [value]);\n\n // Click-to-edit on the cell. Skip when the click originated inside an\n // anchor — the anchor's native navigation wins (clickable URLs, emails,\n // refs remain clickable). Skip when the click is on the pencil itself\n // (the pencil has its own handler) or when already editing / read-only.\n const onCellClick = useCallback(\n (e: ReactMouseEvent<HTMLDivElement>) => {\n if (readOnly || editing) return;\n const target = e.target as HTMLElement;\n if (target.closest(\"a\")) return;\n if (target.closest('[data-peek-pencil=\"true\"]')) return;\n enterEdit();\n },\n [readOnly, editing, enterEdit],\n );\n\n const onKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (!editing) {\n // role=\"button\" requires Enter AND Space activation (WAI-ARIA).\n if (!readOnly && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n enterEdit();\n }\n return;\n }\n if (picker) return; // pickers commit on pick; nothing to confirm here\n if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n commit();\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n cancel();\n }\n },\n [editing, picker, readOnly, enterEdit, commit, cancel],\n );\n\n // Pickers interpret their inner onChange as a pick → auto-commit.\n // Non-pickers keep changes in local tmp state until explicit confirm.\n const innerOnChange = useCallback(\n (next: unknown) => {\n if (picker) {\n setTmp(next);\n commit(next);\n } else {\n setTmp(next);\n }\n },\n [picker, commit],\n );\n\n // Display renderer is always built. Edit renderer is only built while\n // actively editing — avoids synchronously resolving editor components\n // (which may mount useEntityData / pickers) for every peek cell.\n const displayNode = renderField({\n field,\n fieldName,\n value,\n mode: \"display\",\n record,\n refs,\n locale: userLocale,\n });\n\n const editNode = editing\n ? renderField({\n field,\n fieldName,\n value: tmp,\n mode: \"edit\",\n onChange: innerOnChange,\n record,\n refs,\n locale: userLocale,\n })\n : null;\n\n const cellStyle: CSSProperties = {\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"4px 6px\",\n margin: \"-4px -6px\",\n borderRadius: 4,\n minHeight: 28,\n cursor: readOnly || editing ? \"default\" : \"text\",\n border: \"1px dashed transparent\",\n };\n\n // Pencil visibility is driven by `.peek-pencil` + `.peek-field-cell`\n // rules in globals.css — fades in on hover / focus-within; persistent\n // on `@media (hover: none)` (touch). Keep position here (pairs with the\n // cell geometry); opacity lives in CSS.\n const pencilStyle: CSSProperties = {\n position: \"absolute\",\n right: 4,\n top: \"50%\",\n transform: \"translateY(-50%)\",\n };\n return (\n <div\n className=\"peek-field-cell\"\n data-editing={editing ? \"true\" : \"false\"}\n onClick={onCellClick}\n onKeyDown={onKeyDown}\n onMouseEnter={(e) => {\n if (readOnly || editing) return;\n e.currentTarget.style.borderColor = \"var(--border)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = \"transparent\";\n }}\n style={cellStyle}\n // Display-mode cell behaves as a button (click / Enter / Space opens\n // the editor). While editing, the div is just a layout container\n // around the real input — dropping the button role prevents screen\n // readers from re-reading the \"Edit …\" label over active input focus.\n tabIndex={readOnly || editing ? -1 : 0}\n role={readOnly || editing ? undefined : \"button\"}\n aria-label={readOnly || editing ? undefined : `Edit ${label}`}\n >\n {editing ? (\n <div\n style={{ display: \"flex\", alignItems: \"center\", gap: 4, flex: 1, minWidth: 0 }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>{editNode}</div>\n {!picker && (\n <>\n <IconBtn\n size={24}\n icon={<Icon.check size={13} />}\n onClick={() => commit()}\n title=\"Save\"\n />\n <IconBtn\n size={24}\n icon={<Icon.x size={13} />}\n onClick={cancel}\n title=\"Cancel\"\n />\n </>\n )}\n </div>\n ) : (\n <>\n <div style={{ flex: 1, minWidth: 0 }}>{displayNode}</div>\n {!readOnly && (\n <span data-peek-pencil=\"true\" style={pencilStyle} className=\"peek-pencil\">\n <IconBtn\n size={20}\n icon={<Icon.edit size={12} />}\n onClick={(e) => {\n e.stopPropagation();\n enterEdit();\n }}\n title=\"Edit\"\n />\n </span>\n )}\n </>\n )}\n </div>\n );\n}\n","// Right-side drawer over a dim scrim. Shows a read-only preview of a record\n// and lets the user commit per-field edits one at a time — see PeekFieldCell\n// for the commit/cancel state machine. Key invariant: typing never triggers\n// a write; `onSave` fires only on explicit user commit.\n//\n// Dirty-state (any committed change since the peek opened) promotes to a\n// tabbed edit session via `onOpen(draft)` — caller wires ShellDetailPage to\n// consume the draft and enter edit mode pre-filled.\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport {\n Avatar,\n Btn,\n Dropdown,\n Icon,\n IconBtn,\n Kbd,\n MenuItem,\n type IconName,\n} from \"@/components/primitives\";\nimport { renderSchemaField } from \"@/components/file-widgets\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { loc, type Action, type Entity, type EntityField, type Screen } from \"@/types/schema\";\nimport { PeekFieldCell } from \"./PeekFieldCell\";\nimport type { RefsMap } from \"@/components/fields\";\n\nexport interface QuickPeekRegularFieldSpec {\n key: string;\n label: string;\n kind: \"field\";\n field: EntityField;\n readOnly?: boolean;\n}\n\nexport interface QuickPeekFileFieldSpec {\n key: string;\n label: string;\n kind: \"file\";\n readOnly?: boolean;\n}\n\nexport type QuickPeekFieldSpec = QuickPeekRegularFieldSpec | QuickPeekFileFieldSpec;\n\nexport interface QuickPeekProps<R extends Record<string, unknown> = Record<string, unknown>> {\n entity: Pick<Entity, \"fields\" | \"files\">;\n entityCode?: string;\n record: R;\n title: string;\n recordId?: string;\n avatarName?: string;\n fields: QuickPeekFieldSpec[];\n /** $refs payload from the list/detail API; used by RefField-style renderers. */\n refs?: RefsMap;\n /** Full screen YAML — honors `screen.quick_peek.width` and `quick_peek.primary_action`. */\n screen?: Screen;\n /** All actions available on this record. */\n actions: Record<string, Action>;\n executeAction: (code: string, action: Action, params: Record<string, unknown>) => Promise<unknown>;\n /**\n * Close the peek.\n */\n onClose: () => void;\n /**\n * Open full detail. Receives the draft when dirty, else null — caller seeds\n * ShellDetailPage.initialDirty + initial values from this draft.\n */\n onOpen: (draft: R | null) => void;\n /** Fires once per explicit user commit — never on keystroke. */\n onSave?: (next: R) => void;\n}\n\nfunction iconFor(a: Action, size = 12): ReactNode {\n const name = a.display?.icon;\n if (!name) return null;\n const K = Icon[name as IconName];\n if (K) return <K size={size} />;\n return <DynamicIcon name={name} size={size} />;\n}\n\nexport function QuickPeek<R extends Record<string, unknown>>({\n entity,\n entityCode,\n record,\n title,\n recordId,\n avatarName,\n fields,\n refs,\n screen,\n actions,\n executeAction,\n onClose,\n onOpen,\n onSave,\n}: QuickPeekProps<R>) {\n const userLocale = useUserLocale();\n const [draft, setDraft] = useState<R>({ ...record });\n const [dirty, setDirty] = useState(false);\n const [actionsOpen, setActionsOpen] = useState(false);\n const actionsBtnRef = useRef<HTMLButtonElement | null>(null);\n const { run } = useActionRunner();\n\n useEffect(() => {\n setDraft({ ...record });\n setDirty(false);\n }, [record]);\n\n const peekActs = useMemo<Array<{ code: string; action: Action }>>(() => {\n return Object.entries(actions)\n .filter(([, a]) => (a.visibility ?? []).includes(\"quick_peek\"))\n .map(([code, action]) => ({ code, action }));\n }, [actions]);\n\n const primary = useMemo(() => {\n const listed = screen?.quick_peek?.primary_action;\n if (listed) {\n const hit = peekActs.find((x) => x.code === listed);\n if (hit) return hit;\n }\n // Action.primary removed in Task 5. Promote the action with the lowest\n // position value (ties broken by declaration order). No position = 0.\n if (peekActs.length === 0) return null;\n return peekActs.reduce((best, cur) =>\n (cur.action.position ?? 0) < (best.action.position ?? 0) ? cur : best,\n );\n }, [peekActs, screen?.quick_peek?.primary_action]);\n\n const overflow = useMemo(\n () => peekActs.filter((x) => x.code !== primary?.code),\n [peekActs, primary],\n );\n\n const runAction = ({ code, action }: { code: string; action: Action }) => {\n run({\n action,\n title: loc(action.display?.name) || code,\n execute: (params) => executeAction(code, action, params),\n });\n };\n\n // Only called from PeekFieldCell.onCommit — never per-keystroke.\n const commit = (k: string, v: unknown) => {\n const next = { ...draft, [k]: v } as R;\n setDraft(next);\n setDirty(true);\n onSave?.(next);\n };\n\n const width = screen?.quick_peek?.width ?? 480;\n\n return (\n <>\n <div\n onClick={onClose}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim-lite)\",\n zIndex: 40,\n }}\n />\n <aside\n role=\"dialog\"\n aria-label=\"Quick peek\"\n onKeyDown={(e) => {\n // Esc closes the drawer — but only if no field editor swallowed it\n // first (PeekFieldCell calls stopPropagation when cancelling edit).\n if (e.key === \"Escape\") onClose();\n }}\n style={{\n position: \"fixed\",\n top: \"var(--topbar-h)\",\n right: 0,\n bottom: \"var(--tabbar-h)\",\n width,\n background: \"var(--surface)\",\n borderLeft: \"1px solid var(--border)\",\n zIndex: 41,\n display: \"flex\",\n flexDirection: \"column\",\n boxShadow: \"var(--shadow-lg)\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"12px 16px\",\n borderBottom: \"1px solid var(--divider)\",\n gap: 10,\n }}\n >\n <Avatar name={avatarName || title} size={28} />\n <div style={{ flex: 1, minWidth: 0 }}>\n <div\n style={{\n fontSize: 14,\n fontWeight: 600,\n color: \"var(--text)\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {title}\n </div>\n {recordId && (\n <div className=\"mono\" style={{ fontSize: 11, color: \"var(--text-4)\" }}>\n {recordId}\n </div>\n )}\n </div>\n <IconBtn\n icon={<Icon.external size={13} />}\n title={dirty ? \"Open in tab (keeps unsaved changes)\" : \"Open in tab\"}\n onClick={() => onOpen(dirty ? draft : null)}\n />\n <IconBtn icon={<Icon.x size={14} />} onClick={onClose} />\n </div>\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: 14,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 10,\n }}\n >\n <div\n style={{\n padding: \"8px 10px\",\n background: \"var(--accent-soft)\",\n borderRadius: 6,\n fontSize: 11.5,\n color: \"var(--accent)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n }}\n >\n <Icon.sparkle size={12} /> Inline quick-edit — click any field to change.\n <Kbd>Tab</Kbd> to move, <Kbd>Esc</Kbd> to close.\n </div>\n {fields.map((f) => (\n <QEField key={f.key} label={f.label}>\n {f.kind === \"file\"\n ? renderSchemaField({\n entity,\n entityCode,\n fieldName: f.key,\n record: draft,\n mode: f.readOnly ? \"display\" : \"edit\",\n onChange: (value) => commit(f.key, value),\n refs,\n disabled: f.readOnly,\n locale: userLocale,\n })\n : (\n <PeekFieldCell\n field={f.field}\n fieldName={f.key}\n label={f.label}\n value={draft[f.key]}\n record={draft}\n refs={refs}\n readOnly={f.readOnly}\n onCommit={(v) => commit(f.key, v)}\n />\n )}\n </QEField>\n ))}\n </div>\n <div\n style={{\n display: \"flex\",\n padding: \"10px 14px\",\n borderTop: \"1px solid var(--divider)\",\n gap: 6,\n alignItems: \"center\",\n }}\n >\n {dirty && (\n <span\n style={{\n fontSize: 11,\n color: \"var(--warn)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n }}\n >\n <span\n style={{\n width: 6,\n height: 6,\n borderRadius: 999,\n background: \"var(--warn)\",\n }}\n />\n Unsaved\n </span>\n )}\n <div style={{ flex: 1 }} />\n {overflow.length > 0 && (\n <div style={{ position: \"relative\" }}>\n <Btn\n ref={actionsBtnRef}\n kind=\"default\"\n icon={<Icon.bolt size={13} />}\n iconRight={<Icon.down size={10} style={{ opacity: 0.6 }} />}\n onClick={() => setActionsOpen((prev) => !prev)}\n >\n Actions\n </Btn>\n <Dropdown\n open={actionsOpen}\n anchor={actionsBtnRef.current}\n onClose={() => setActionsOpen(false)}\n width={220}\n align=\"end\"\n >\n {overflow.map(({ code, action }) => (\n <MenuItem\n key={code}\n icon={iconFor(action)}\n danger={action.destructive}\n onClick={() => {\n setActionsOpen(false);\n runAction({ code, action });\n }}\n >\n {loc(action.display?.name) || code}\n </MenuItem>\n ))}\n </Dropdown>\n </div>\n )}\n {primary && (\n <Btn\n kind=\"default\"\n icon={iconFor(primary.action, 13)}\n onClick={() => runAction(primary)}\n >\n {loc(primary.action.display?.name) || primary.code}\n </Btn>\n )}\n <Btn\n kind=\"primary\"\n icon={<Icon.edit size={13} />}\n onClick={() => onOpen(dirty ? draft : null)}\n >\n Open full\n </Btn>\n </div>\n </aside>\n </>\n );\n}\n\nexport interface QEFieldProps {\n label: string;\n children: ReactNode;\n}\n\nexport function QEField({ label, children }: QEFieldProps) {\n const style: CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"120px 1fr\",\n alignItems: \"center\",\n padding: \"4px 0\",\n borderBottom: \"1px solid var(--divider)\",\n };\n return (\n <div style={style}>\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\", fontWeight: 500 }}>{label}</div>\n <div>{children}</div>\n </div>\n );\n}\n","// Ephemeral in-memory handoff from QuickPeek → SmartDetailPage. When the\n// user clicks \"Open full\" with unsaved edits in the peek, SmartListPage\n// stashes the draft here keyed by record id, then navigates. The detail\n// page consumes and clears the entry on mount. Lives for the JS runtime\n// only — no persistence, no URL payload, no PII in browser history.\n//\n// Key choice: string id of the record's primary key. Composite PKs use\n// the same serialization SmartListPage uses for navigation.\nconst store = new Map<string, Record<string, unknown>>();\n\nexport function setPeekDraft(id: string, draft: Record<string, unknown>): void {\n store.set(id, draft);\n}\n\nexport function takePeekDraft(id: string): Record<string, unknown> | null {\n const v = store.get(id);\n if (v === undefined) return null;\n store.delete(id);\n return v;\n}\n\n/** Drop any pending draft for a record — called when the peek closes\n * without promoting to detail, so a stale draft cannot be picked up by a\n * later URL-driven navigation to the same record. */\nexport function clearPeekDraft(id: string): void {\n store.delete(id);\n}\n","// Sort grammar helpers shared by SmartListPage and any other list surface.\n//\n// Canonical wire format (see docs/plans/2026-05-25-multi-sort-and-nulls-ordering.md §2):\n//\n// sort_expr = sort_col (\",\" sort_col){0,4}\n// sort_col = field \":\" direction (\":\" nulls)?\n// direction = \"asc\" | \"desc\"\n// nulls = \"nulls_first\" | \"nulls_last\"\n//\n// The string IS the API. There is no JSON form. The server-side parser is\n// strict and returns ValidationError on grammar violations; the client-side\n// parser here is intentionally tolerant so we can roundtrip URLs and saved\n// views without throwing on a stray segment we don't recognize (the request\n// to the server will still fail loudly if the value is genuinely invalid).\n//\n// Phase 1 of the plan only exposes single-column sort to the UI, but the\n// helpers handle the full multi-column grammar so Phase 2 has no extra work.\n\nexport type SortDir = \"asc\" | \"desc\";\nexport type SortNulls = \"first\" | \"last\";\n\nexport interface SortCol {\n field: string;\n dir: SortDir;\n // When undefined the system default (NULLS LAST in both directions) applies.\n nulls?: SortNulls;\n}\n\nconst VALID_NULLS_LITERALS: Record<string, SortNulls> = {\n nulls_first: \"first\",\n nulls_last: \"last\",\n};\n\n/**\n * Parse a sort expression string into a list of SortCol entries.\n *\n * Tolerant per plan §5.2:\n * - empty/null/undefined → []\n * - missing direction defaults to \"asc\"\n * - missing nulls segment is left undefined (system default applies server-side)\n * - unknown nulls literal is dropped (server is the strict authority)\n * - empty fields between commas are skipped\n */\nexport function parseSort(s: string | null | undefined): SortCol[] {\n if (!s) return [];\n const out: SortCol[] = [];\n for (const raw of s.split(\",\")) {\n const piece = raw.trim();\n if (!piece) continue;\n const segs = piece.split(\":\");\n const field = segs[0]?.trim();\n if (!field) continue;\n // $property.* sort is deferred (plan §2). Drop silently so stale URLs /\n // saved views bookmarked before that contract landed don't hard-fail the\n // backend's strict parser. The frontend is tolerant for display, but\n // the backend is the strict authority on every OTHER grammar element\n // (see stripUnsupportedSortSegments for the request-side filter that\n // preserves malformed grammar so a 400 surfaces to the user).\n if (field.startsWith(\"$property.\")) continue;\n const dirRaw = segs[1]?.trim().toLowerCase();\n const dir: SortDir = dirRaw === \"desc\" ? \"desc\" : \"asc\";\n const col: SortCol = { field, dir };\n const nullsRaw = segs[2]?.trim().toLowerCase();\n if (nullsRaw) {\n const mapped = VALID_NULLS_LITERALS[nullsRaw];\n if (mapped) col.nulls = mapped;\n // Unknown nulls literal: drop from the parsed SortCol so menu/badges\n // don't render garbage state. The raw segment is preserved verbatim\n // by stripUnsupportedSortSegments for the outbound request.\n }\n out.push(col);\n }\n return out;\n}\n\n/**\n * Filter a raw sort URL string so $property.* segments (deferred per plan §2)\n * don't reach the backend's strict parser. EVERY OTHER token passes through\n * unchanged - including malformed grammar like `:nulls_middle` or extra\n * colons - so the backend can return a typed ValidationError and the SDK\n * surfaces the error to the user. Plan §14 strict-parse contract requires\n * that grammar violations are NOT silently rewritten on the client.\n */\nexport function stripUnsupportedSortSegments(s: string | null | undefined): string | undefined {\n if (!s) return undefined;\n const kept: string[] = [];\n for (const raw of s.split(\",\")) {\n const piece = raw.trim();\n if (!piece) continue;\n const field = piece.split(\":\")[0]?.trim();\n if (field && field.startsWith(\"$property.\")) continue;\n // Preserve the original piece verbatim - whitespace and all - so the\n // backend sees exactly what the user/saved-view authored for anything\n // outside the documented deferral.\n kept.push(piece);\n }\n return kept.length > 0 ? kept.join(\",\") : undefined;\n}\n\n/**\n * Serialize a list of SortCol entries back to the canonical string form.\n * Round-trips losslessly with parseSort for any valid input.\n */\nexport function serializeSort(cols: SortCol[]): string {\n return cols\n .map((c) => {\n const head = `${c.field}:${c.dir}`;\n if (c.nulls === \"first\") return `${head}:nulls_first`;\n if (c.nulls === \"last\") return `${head}:nulls_last`;\n return head;\n })\n .join(\",\");\n}\n\n/**\n * Server-enforced cap (plan §3 Decision 4, §4.2). Mirrored on the client so\n * we never POST a sort the server will reject with HTTP 400.\n */\nexport const MAX_SORT_COLS = 5;\n\n/**\n * Shift-click on a column header (plan §7.1).\n *\n * - field already in `prev` → remove it (toggle off).\n * - field not in `prev` → append { field, dir: \"asc\" } to the tail.\n *\n * When the list is already at the {@link MAX_SORT_COLS} cap AND `field` is\n * not already in it, the helper refuses and returns `prev` unchanged. The\n * UI surfaces this implicitly: the \"Add to multi-sort\" menu item is hidden\n * once the cap is reached, and a shift-click on a brand-new column at the\n * cap is a no-op (consistent UX, matches the brief's \"refuse\" call).\n *\n * Nulls placement of every retained column is preserved verbatim - shift-\n * click never silently rewrites another column's NULL setting.\n *\n * Returned arrays are always new references; never mutates `prev`.\n */\nexport function applyShiftClickSort(prev: SortCol[], field: string): SortCol[] {\n const existingIdx = prev.findIndex((c) => c.field === field);\n if (existingIdx >= 0) {\n return [...prev.slice(0, existingIdx), ...prev.slice(existingIdx + 1)];\n }\n if (prev.length >= MAX_SORT_COLS) return prev;\n return [...prev, { field, dir: \"asc\" }];\n}\n\n/**\n * \"Add to multi-sort\" menu action (plan §7.2). Adds `col` to the sort tail\n * with the caller-chosen `dir` + `nulls`. Dedupes: if `col.field` is already\n * present in `prev`, the earlier occurrence is removed and the new one\n * appended at the tail (server rejects duplicate fields, plan §4.2).\n *\n * Refuses to grow past {@link MAX_SORT_COLS}: when appending would exceed\n * the cap AND the field is not already present, returns `prev` unchanged.\n * The menu item that drives this is hidden at the cap, so this guard exists\n * to keep the helper safe under any caller.\n */\nexport function applyAppendSort(prev: SortCol[], col: SortCol): SortCol[] {\n const without = prev.filter((c) => c.field !== col.field);\n if (without.length >= MAX_SORT_COLS) return prev;\n return [...without, col];\n}\n\n/**\n * \"Clear sort on this column\" menu action (plan §7.2). Removes the one\n * entry matching `field` from `prev`; returns `prev` unchanged if absent.\n */\nexport function removeSortField(prev: SortCol[], field: string): SortCol[] {\n const idx = prev.findIndex((c) => c.field === field);\n if (idx < 0) return prev;\n return [...prev.slice(0, idx), ...prev.slice(idx + 1)];\n}\n","/**\n * Row-count formatting for list views. Display precision MUST match data\n * accuracy (docs/plans/2026-05-18-declarative-row-count.md, Principle 3):\n *\n * - an exact count renders to the unit with thousands separators\n * (3,613,711) - it is known precisely;\n * - an estimate renders coarse (~3.6M) - showing an estimate to the unit\n * is false precision and is wrong.\n *\n * This is the single shared count formatter; the SDK has no other. Field\n * cell factories that call `toLocaleString` for per-cell numbers are a\n * separate concern and are not routed through here.\n */\n\n/** Magnitude suffixes for the estimated form, largest first. */\nconst MAGNITUDES: ReadonlyArray<{ value: number; suffix: string }> = [\n { value: 1_000_000_000, suffix: \"B\" },\n { value: 1_000_000, suffix: \"M\" },\n { value: 1_000, suffix: \"K\" },\n];\n\n/**\n * formatEstimatedCount renders a coarse estimate: a `~` prefix, ~2\n * significant figures, and a magnitude suffix (K/M/B).\n *\n * 3_613_711 -> \"~3.6M\"\n * 850_000 -> \"~850K\"\n * 12_000 -> \"~12K\"\n * 940 -> \"~940\" (below 1000: no suffix, the ~ still signals an estimate)\n *\n * Two significant figures: a value's first 2 digits are kept and the rest\n * collapse to the magnitude suffix. 3_613_711 -> 3.6M (one decimal because\n * the leading digit is single); 12_000 -> 12K (no decimal because 2 leading\n * digits already give 2 sig figs).\n */\nexport function formatEstimatedCount(total: number): string {\n if (!Number.isFinite(total) || total < 0) return \"~0\";\n const n = Math.round(total);\n if (n < 1000) return `~${n}`;\n\n for (const { value, suffix } of MAGNITUDES) {\n if (n < value) continue;\n const scaled = n / value;\n // One decimal when the scaled value is < 10 (a single leading digit\n // needs the decimal for 2 sig figs); none at >= 10 (already 2+ digits).\n const text = scaled < 10 ? scaled.toFixed(1) : Math.round(scaled).toString();\n // Drop a trailing \".0\" - \"~3.0M\" reads as false precision; \"~3M\" is right.\n return `~${text.replace(/\\.0$/, \"\")}${suffix}`;\n }\n // Unreachable: n >= 1000 always matches the K bucket. Defensive only.\n return `~${n.toLocaleString()}`;\n}\n\n/**\n * formatExactCount renders an exact count to the unit with locale thousands\n * separators: 3613711 -> \"3,613,711\".\n */\nexport function formatExactCount(total: number): string {\n if (!Number.isFinite(total) || total < 0) return \"0\";\n return Math.round(total).toLocaleString();\n}\n\n/**\n * formatCount picks the rendering by the count strategy actually used to\n * produce `total` (the server's `meta.count_mode`), NOT the entity schema:\n * a forced exact count must render exact even on a `count: estimated`\n * entity.\n */\nexport function formatCount(total: number, mode: \"exact\" | \"estimated\"): string {\n return mode === \"estimated\" ? formatEstimatedCount(total) : formatExactCount(total);\n}\n","import { Btn, Chip, Icon } from \"@/components/primitives\";\nimport type { ReactNode } from \"react\";\nimport {\n type FilterNode,\n hasOrGroups,\n flatFieldConditions,\n OP_LABELS,\n NO_VALUE_OPS,\n} from \"@/lib/filters\";\n\ninterface FilterChipsProps {\n filters: FilterNode[];\n onRemove: (field: string) => void;\n onClearAll: () => void;\n onChipClick?: () => void;\n fieldLabel?: (field: string) => string;\n}\n\nexport function FilterChips({\n filters,\n onRemove,\n onClearAll,\n onChipClick,\n fieldLabel = (f) => f,\n}: FilterChipsProps) {\n if (filters.length === 0) return null;\n\n const isOrMode = hasOrGroups(filters);\n\n if (isOrMode) {\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, flexWrap: \"wrap\", paddingBottom: 12 }}>\n {filters.map((node, i) => {\n if (node.or) {\n return (\n <div key={i} style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n {node.or.map((group, gi) => (\n <span key={gi} style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n {gi > 0 && (\n <span\n style={{\n fontSize: 11,\n color: \"var(--text-3)\",\n fontWeight: 500,\n padding: \"0 4px\",\n }}\n >\n OR\n </span>\n )}\n <Chip tone=\"slate\" onClick={onChipClick}>\n {group\n .filter((n) => n.field && n.op)\n .map((n) => `${fieldLabel(n.field!)} ${formatValue(n)}`)\n .join(\" AND \")}\n </Chip>\n </span>\n ))}\n </div>\n );\n }\n if (node.field && node.op) {\n return (\n <FilterChip\n key={`${node.field}-${i}`}\n node={node}\n fieldLabel={fieldLabel}\n onRemove={() => onRemove(node.field!)}\n onClick={onChipClick}\n />\n );\n }\n return null;\n })}\n <Btn kind=\"ghost\" size=\"xs\" icon={<Icon.x size={11} />} onClick={onClearAll}>\n Clear all\n </Btn>\n </div>\n );\n }\n\n const fieldNodes = flatFieldConditions(filters);\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, flexWrap: \"wrap\", paddingBottom: 12 }}>\n {fieldNodes.map((node, i) => (\n <FilterChip\n key={`${node.field}-${i}`}\n node={node}\n fieldLabel={fieldLabel}\n onRemove={() => onRemove(node.field!)}\n onClick={onChipClick}\n />\n ))}\n <Btn kind=\"ghost\" size=\"xs\" icon={<Icon.x size={11} />} onClick={onClearAll}>\n Clear all\n </Btn>\n </div>\n );\n}\n\nfunction FilterChip({\n node,\n fieldLabel,\n onRemove,\n onClick,\n}: {\n node: FilterNode;\n fieldLabel: (f: string) => string;\n onRemove: () => void;\n onClick?: () => void;\n}) {\n const icon: ReactNode = <Icon.filter size={11} />;\n return (\n <Chip tone=\"slate\" icon={icon} onClick={onClick} onRemove={onRemove}>\n <span>\n <span style={{ color: \"var(--text-3)\" }}>{fieldLabel(node.field!)}</span>{\" \"}\n <span style={{ fontWeight: 600 }}>{formatValue(node)}</span>\n </span>\n </Chip>\n );\n}\n\nfunction formatValue(node: FilterNode): string {\n if (!node.op) return \"\";\n const label = OP_LABELS[node.op] || node.op;\n if (NO_VALUE_OPS.has(node.op)) return label;\n const val = Array.isArray(node.value) ? node.value.join(\", \") : String(node.value ?? \"\");\n return `${label} ${val}`;\n}\n","import { useState, useEffect } from \"react\";\nimport { Btn, Icon, IconBtn, Select, TextInput } from \"@/components/primitives\";\nimport {\n type FilterNode,\n type FilterOp,\n OP_LABELS,\n NO_VALUE_OPS,\n hasOrGroups,\n} from \"@/lib/filters\";\n\nexport interface FilterFieldMeta {\n code: string;\n label: string;\n type:\n | \"string\"\n | \"enum\"\n | \"int\"\n | \"float\"\n | \"date\"\n | \"timestamp\"\n | \"bool\"\n | \"ref\"\n | \"status\"\n | \"property\"\n | string;\n operators: FilterOp[];\n options?: { value: string; label: string; color?: string }[];\n}\n\ninterface FilterDrawerProps {\n open: boolean;\n onClose: () => void;\n filters: FilterNode[];\n onApply: (filters: FilterNode[]) => void;\n availableFields: FilterFieldMeta[];\n}\n\nexport function FilterDrawer({\n open,\n onClose,\n filters,\n onApply,\n availableFields,\n}: FilterDrawerProps) {\n const [draft, setDraft] = useState<FilterNode[]>(filters);\n const [showFieldPicker, setShowFieldPicker] = useState(false);\n\n useEffect(() => {\n if (open) setDraft(filters);\n }, [open, filters]);\n\n if (!open) return null;\n\n const isOrMode = hasOrGroups(draft);\n const activeFields = new Set(draft.filter((n) => n.field).map((n) => n.field!));\n\n const addCondition = (fieldCode: string) => {\n const meta = availableFields.find((f) => f.code === fieldCode);\n if (!meta) return;\n const defaultOp = meta.operators[0] || \"eq\";\n setDraft([...draft, { field: fieldCode, op: defaultOp }]);\n setShowFieldPicker(false);\n };\n\n const updateCondition = (index: number, updates: Partial<FilterNode>) => {\n setDraft(draft.map((n, i) => (i === index ? { ...n, ...updates } : n)));\n };\n\n const removeCondition = (index: number) => {\n setDraft(draft.filter((_, i) => i !== index));\n };\n\n const addOrGroup = () => {\n const currentConditions = draft.filter((n) => n.field && n.op);\n const otherNodes = draft.filter((n) => !n.field || !n.op);\n setDraft([\n ...otherNodes,\n {\n or: [currentConditions.length > 0 ? currentConditions : [], []],\n },\n ]);\n };\n\n const handleApply = () => {\n const cleaned = draft\n .map((n) => {\n if (n.or) {\n const filteredOr = n.or.filter((g) => g.length > 0);\n return filteredOr.length > 0 ? { ...n, or: filteredOr } : null;\n }\n return n.field && n.op ? n : null;\n })\n .filter((n): n is FilterNode => n !== null);\n onApply(cleaned);\n onClose();\n };\n\n const handleReset = () => {\n setDraft([]);\n };\n\n return (\n <>\n <div\n onClick={onClose}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim-lite)\",\n zIndex: 40,\n }}\n />\n <div\n role=\"dialog\"\n aria-label=\"Filters\"\n aria-modal=\"true\"\n data-testid=\"filter-drawer\"\n style={{\n position: \"fixed\",\n top: \"var(--topbar-h)\",\n right: 0,\n bottom: \"var(--tabbar-h)\",\n width: 400,\n maxWidth: \"100%\",\n background: \"var(--surface)\",\n borderLeft: \"1px solid var(--border)\",\n boxShadow: \"var(--shadow-lg)\",\n zIndex: 50,\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 14px\",\n borderBottom: \"1px solid var(--divider)\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <Icon.filter size={14} />\n <h3 style={{ margin: 0, fontSize: 13, fontWeight: 600, color: \"var(--text)\" }}>Filters</h3>\n </div>\n <IconBtn icon={<Icon.x size={14} />} onClick={onClose} title=\"Close\" />\n </div>\n\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: 14,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 10,\n }}\n >\n {!isOrMode\n ? draft.map((node, i) => {\n if (!node.field || !node.op) return null;\n const meta = availableFields.find((f) => f.code === node.field);\n if (!meta) return null;\n return (\n <FilterRow\n key={`${node.field}-${i}`}\n node={node}\n meta={meta}\n onUpdate={(updates) => updateCondition(i, updates)}\n onRemove={() => removeCondition(i)}\n />\n );\n })\n : draft.map((node, i) => {\n if (!node.or) {\n if (!node.field || !node.op) return null;\n const meta = availableFields.find((f) => f.code === node.field);\n if (!meta) return null;\n return (\n <FilterRow\n key={`${node.field}-${i}`}\n node={node}\n meta={meta}\n onUpdate={(updates) => updateCondition(i, updates)}\n onRemove={() => removeCondition(i)}\n />\n );\n }\n return (\n <OrGroupEditor\n key={`or-${i}`}\n groups={node.or}\n availableFields={availableFields}\n onChange={(newGroups) => {\n const newDraft = [...draft];\n newDraft[i] = { or: newGroups };\n setDraft(newDraft);\n }}\n />\n );\n })}\n\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, paddingTop: 6 }}>\n {showFieldPicker ? (\n <FieldPicker\n fields={availableFields}\n activeFields={activeFields}\n onSelect={addCondition}\n onCancel={() => setShowFieldPicker(false)}\n />\n ) : (\n <>\n <Btn icon={<Icon.plus size={12} />} onClick={() => setShowFieldPicker(true)}>\n Add filter\n </Btn>\n {!isOrMode && draft.some((n) => n.field) && (\n <Btn kind=\"ghost\" icon={<Icon.plus size={12} />} onClick={addOrGroup}>\n Add OR group\n </Btn>\n )}\n </>\n )}\n </div>\n </div>\n\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"10px 14px\",\n borderTop: \"1px solid var(--divider)\",\n background: \"var(--surface-2)\",\n gap: 6,\n }}\n >\n <Btn kind=\"ghost\" onClick={handleReset}>\n Reset\n </Btn>\n <div style={{ flex: 1 }} />\n <Btn onClick={onClose}>Cancel</Btn>\n <Btn kind=\"primary\" onClick={handleApply}>\n Apply\n </Btn>\n </div>\n </div>\n </>\n );\n}\n\nfunction FilterRow({\n node,\n meta,\n onUpdate,\n onRemove,\n}: {\n node: FilterNode;\n meta: FilterFieldMeta;\n onUpdate: (updates: Partial<FilterNode>) => void;\n onRemove: () => void;\n}) {\n const isNoValue = NO_VALUE_OPS.has(node.op as FilterOp);\n const opOptions = meta.operators.map((op) => ({ value: op, label: OP_LABELS[op] || op }));\n\n return (\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: \"110px 80px 1fr 24px\",\n gap: 6,\n alignItems: \"center\",\n }}\n >\n <span\n style={{\n fontSize: 12,\n fontWeight: 500,\n color: \"var(--text-2)\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n title={meta.label}\n >\n {meta.label}\n </span>\n <Select\n aria-label={`${meta.label} filter operator`}\n value={node.op ?? opOptions[0]?.value}\n options={opOptions}\n onChange={(op) => onUpdate({ op: op as FilterOp })}\n />\n {isNoValue ? (\n <span />\n ) : meta.options ? (\n <Select\n aria-label={`${meta.label} filter value`}\n value={String(node.value ?? \"\")}\n options={meta.options.map((o) => ({ value: o.value, label: o.label }))}\n onChange={(v) => onUpdate({ value: v })}\n placeholder=\"Select…\"\n />\n ) : (\n <TextInput\n aria-label={`${meta.label} filter value`}\n value={String(node.value ?? \"\")}\n onChange={(v) => onUpdate({ value: v })}\n placeholder=\"Value…\"\n />\n )}\n <IconBtn size={24} icon={<Icon.x size={12} />} onClick={onRemove} title=\"Remove\" />\n </div>\n );\n}\n\nfunction OrGroupEditor({\n groups,\n availableFields,\n onChange,\n}: {\n groups: FilterNode[][];\n availableFields: FilterFieldMeta[];\n onChange: (groups: FilterNode[][]) => void;\n}) {\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n {groups.map((group, gi) => (\n <div key={gi}>\n {gi > 0 && (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, padding: \"4px 0\" }}>\n <div style={{ flex: 1, borderTop: \"1px solid var(--divider)\" }} />\n <span style={{ fontSize: 11, fontWeight: 600, color: \"var(--text-3)\", padding: \"0 6px\" }}>OR</span>\n <div style={{ flex: 1, borderTop: \"1px solid var(--divider)\" }} />\n </div>\n )}\n <div style={{ border: \"1px solid var(--border)\", borderRadius: 8, padding: 10, display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <div style={{ fontSize: 10.5, fontWeight: 600, color: \"var(--text-3)\", textTransform: \"uppercase\", letterSpacing: \"0.08em\" }}>\n Match ALL\n </div>\n {group.map((node, ni) => {\n const meta = availableFields.find((f) => f.code === node.field);\n if (!meta) return null;\n return (\n <FilterRow\n key={`${node.field}-${ni}`}\n node={node}\n meta={meta}\n onUpdate={(updates) => {\n const newGroups = groups.map((g, i) =>\n i === gi ? g.map((n, j) => (j === ni ? { ...n, ...updates } : n)) : g,\n );\n onChange(newGroups);\n }}\n onRemove={() => {\n const newGroups = groups.map((g, i) =>\n i === gi ? g.filter((_, j) => j !== ni) : g,\n );\n onChange(newGroups.filter((g) => g.length > 0));\n }}\n />\n );\n })}\n <Btn\n kind=\"ghost\"\n size=\"xs\"\n icon={<Icon.plus size={11} />}\n onClick={() => {\n // Set membership once instead of group.some() per field -\n // avoids an O(fields x conditions) scan.\n const usedInGroup = new Set(group.map((n) => n.field));\n const firstAvailable = availableFields.find(\n (f) => !usedInGroup.has(f.code),\n );\n if (firstAvailable) {\n const newGroups = groups.map((g, i) =>\n i === gi\n ? [\n ...g,\n {\n field: firstAvailable.code,\n op: firstAvailable.operators[0] || (\"eq\" as FilterOp),\n },\n ]\n : g,\n );\n onChange(newGroups);\n }\n }}\n >\n Add condition\n </Btn>\n </div>\n </div>\n ))}\n <Btn\n kind=\"ghost\"\n size=\"xs\"\n icon={<Icon.plus size={11} />}\n onClick={() => onChange([...groups, []])}\n >\n Add OR group\n </Btn>\n </div>\n );\n}\n\nfunction FieldPicker({\n fields,\n activeFields,\n onSelect,\n onCancel,\n}: {\n fields: FilterFieldMeta[];\n activeFields: Set<string>;\n onSelect: (code: string) => void;\n onCancel: () => void;\n}) {\n const [search, setSearch] = useState(\"\");\n const filtered = fields.filter(\n (f) =>\n !activeFields.has(f.code) &&\n (search === \"\" ||\n f.label.toLowerCase().includes(search.toLowerCase()) ||\n f.code.toLowerCase().includes(search.toLowerCase())),\n );\n\n return (\n <div\n style={{\n width: \"100%\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: 8,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 6,\n }}\n >\n <TextInput\n value={search}\n onChange={setSearch}\n placeholder=\"Search fields…\"\n prefix={<Icon.search size={12} />}\n autoFocus\n />\n <div style={{ maxHeight: 200, overflow: \"auto\", display: \"flex\", flexDirection: \"column\" }}>\n {filtered.map((f) => (\n <button\n key={f.code}\n data-testid={`filter-field-${f.code}`}\n onClick={() => onSelect(f.code)}\n style={{\n textAlign: \"left\",\n padding: \"6px 8px\",\n fontSize: 12,\n background: \"transparent\",\n border: 0,\n color: \"var(--text-2)\",\n cursor: \"pointer\",\n borderRadius: 5,\n display: \"flex\",\n justifyContent: \"space-between\",\n gap: 8,\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = \"var(--surface-3)\")}\n onMouseLeave={(e) => (e.currentTarget.style.background = \"transparent\")}\n >\n <span>{f.label}</span>\n <span className=\"mono\" style={{ color: \"var(--text-4)\", fontSize: 10.5 }}>\n {f.code}\n </span>\n </button>\n ))}\n {filtered.length === 0 && (\n <div style={{ fontSize: 12, color: \"var(--text-3)\", padding: \"6px 8px\" }}>\n No fields available\n </div>\n )}\n </div>\n <Btn kind=\"ghost\" size=\"xs\" onClick={onCancel} style={{ justifyContent: \"center\" }}>\n Cancel\n </Btn>\n </div>\n );\n}\n","import { loc, fieldDisplayName } from \"@/types/schema\";\nimport type { Entity } from \"@/types/schema\";\nimport type { FilterFieldMeta } from \"@/components/filters/FilterDrawer\";\nimport type { FilterOp } from \"@/lib/filters\";\n\n/** Build FilterFieldMeta array from screen + entity for the FilterDrawer. */\nexport function buildFilterFieldMeta(\n screen: { filters?: string[] } | undefined,\n entity: Pick<Entity, \"fields\" | \"status_groups\" | \"properties\"> | undefined,\n entities?: Record<string, Entity>,\n): FilterFieldMeta[] {\n if (!screen || !entity) return [];\n const filterFields = screen.filters ?? entity.fields.names();\n const meta: FilterFieldMeta[] = [];\n\n for (const f of filterFields) {\n if (f.startsWith(\"$status.\")) {\n const groupCode = f.slice(8);\n const group = entity.status_groups?.[groupCode];\n if (!group) continue;\n meta.push({\n code: f,\n label: loc(group.name) || groupCode,\n type: \"status\",\n operators: [\"eq\", \"not_eq\", \"in\", \"not_in\"],\n options: group.statuses.map((s) => ({\n value: s.code,\n label: loc(s.name) || s.code,\n color: s.color,\n })),\n });\n continue;\n }\n if (f.startsWith(\"$property.\")) {\n const propCode = f.slice(10);\n const prop = entity.properties?.[propCode];\n if (!prop) continue;\n meta.push({\n code: f,\n label: loc(prop.name) || propCode,\n type: \"property\",\n operators: [\"eq\", \"not_eq\", \"contains\", \"is_empty\", \"is_not_empty\"],\n });\n continue;\n }\n const field = entity.fields.get(f);\n if (!field) continue;\n const label = fieldDisplayName(field, f, entities);\n const ops = fieldTypeToOperators(field.type);\n const options = field.values?.map((v) => ({\n value: v.code,\n label: loc(v.name) || v.code,\n color: v.color,\n }));\n meta.push({ code: f, label, type: field.type, operators: ops, options });\n }\n\n return meta;\n}\n\n/** Map field type to available filter operators. */\nexport function fieldTypeToOperators(type: string): FilterOp[] {\n switch (type) {\n case \"string\": case \"text\": case \"rich_text\": case \"email\": case \"url\": case \"phone\":\n return [\"eq\", \"not_eq\", \"contains\", \"starts_with\", \"is_empty\", \"is_not_empty\"];\n case \"enum\":\n return [\"eq\", \"not_eq\", \"in\", \"not_in\", \"is_empty\", \"is_not_empty\"];\n case \"int\": case \"float\": case \"decimal\":\n return [\"eq\", \"not_eq\", \"gt\", \"gte\", \"lt\", \"lte\", \"between\", \"is_empty\", \"is_not_empty\"];\n case \"date\":\n return [\"eq\", \"not_eq\", \"gt\", \"gte\", \"lt\", \"lte\", \"between\", \"today\", \"this_week\", \"this_month\", \"last_7_days\", \"last_30_days\", \"is_empty\", \"is_not_empty\"];\n case \"timestamp\":\n return [\"eq\", \"not_eq\", \"gt\", \"gte\", \"lt\", \"lte\", \"between\", \"today\", \"this_week\", \"this_month\", \"last_7_days\", \"last_30_days\", \"last_hour\", \"last_24_hours\", \"is_empty\", \"is_not_empty\"];\n case \"bool\":\n return [\"eq\", \"is_empty\"];\n case \"ref\": case \"uuid\":\n return [\"eq\", \"not_eq\", \"is_empty\", \"is_not_empty\"];\n default:\n return [\"eq\", \"not_eq\", \"is_empty\", \"is_not_empty\"];\n }\n}\n","import { useState, useEffect } from \"react\";\nimport { Btn, TextInput } from \"@/components/primitives\";\n\n// SaveViewDialog is the modal that names and persists the current list state\n// (filters, columns, sort) as a personal view. Lives in its own file so\n// SmartListPage keeps only orchestration.\nexport function SaveViewDialog({\n open,\n pending,\n onClose,\n onSave,\n}: {\n open: boolean;\n pending: boolean;\n onClose: () => void;\n onSave: (name: string) => Promise<void>;\n}) {\n const [name, setName] = useState(\"\");\n\n useEffect(() => {\n if (open) setName(\"\");\n }, [open]);\n\n if (!open) return null;\n\n const trimmed = name.trim();\n\n return (\n <>\n <div\n onClick={onClose}\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim)\",\n zIndex: 70,\n }}\n />\n <div\n role=\"dialog\"\n aria-label=\"Save view\"\n aria-modal=\"true\"\n style={{\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n width: 360,\n maxWidth: \"calc(100vw - 32px)\",\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 10,\n boxShadow: \"var(--shadow-lg)\",\n zIndex: 80,\n padding: 16,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n }}\n >\n <div>\n <h3 style={{ margin: 0, fontSize: 14, color: \"var(--text)\" }}>Save view</h3>\n <p style={{ margin: \"4px 0 0\", fontSize: 12, color: \"var(--text-3)\" }}>\n Save the current filters, columns, and sort as a personal view.\n </p>\n </div>\n <label style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n <span style={{ fontSize: 11.5, color: \"var(--text-3)\", fontWeight: 500 }}>Name</span>\n <TextInput\n autoFocus\n aria-label=\"View name\"\n value={name}\n onChange={setName}\n placeholder=\"My filtered view\"\n onKeyDown={(event) => {\n if (event.key === \"Escape\") onClose();\n if (event.key === \"Enter\" && trimmed && !pending) {\n void onSave(trimmed);\n }\n }}\n />\n </label>\n <div style={{ display: \"flex\", justifyContent: \"flex-end\", gap: 8 }}>\n <Btn onClick={onClose} disabled={pending}>Cancel</Btn>\n <Btn kind=\"primary\" onClick={() => void onSave(trimmed)} disabled={!trimmed || pending}>\n Save view\n </Btn>\n </div>\n </div>\n </>\n );\n}\n","import { useMemo } from \"react\";\nimport {\n defaultFlatForSlot,\n filterLayoutForSlot,\n actionRequiresSelection,\n disableLeavesWhere,\n type ResolvedActionLayoutNode,\n} from \"@/lib/actions\";\nimport { dispatchAction } from \"@/lib/action-dispatch\";\nimport { ResponsiveActionBar, type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport { loc } from \"@/types/schema\";\nimport type { Action } from \"@/types/schema\";\nimport { executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { formatCount } from \"@/lib/format-count\";\nimport { Spinner } from \"@/components/primitives\";\n\nexport interface ListHeaderProps {\n title: string;\n /** Total matching rows, from the separate count query. Undefined while\n * the count is in flight or after it failed. */\n total: number | undefined;\n /** Strategy used to produce `total` (\"exact\" | \"estimated\"). Drives\n * display precision. Undefined when no count has resolved. */\n countMode?: \"exact\" | \"estimated\";\n /** True while the count query is in flight - render a loading affordance\n * in the count slot instead of a number. */\n countPending?: boolean;\n loaded: number;\n toolbarActions: { code: string; action: Action }[];\n /** Server-emitted action_layout from screen (highest precedence per plan §3.9). */\n screenActionLayout?: ResolvedActionLayoutNode[];\n /** Server-emitted action_layout from entity. Used when screen has none. */\n entityActionLayout?: ResolvedActionLayoutNode[];\n selectedIds: string[];\n data: Record<string, unknown>[];\n rowKey: (row: Record<string, unknown>) => string;\n onActionSuccess: () => void;\n /** Dispatch surface threaded into ResponsiveActionBar (plan §8 Task 10). */\n widgetContext: WidgetContext;\n}\n\nexport function ListHeader({\n title,\n total,\n countMode,\n countPending,\n loaded,\n toolbarActions,\n screenActionLayout,\n entityActionLayout,\n selectedIds,\n data,\n rowKey,\n onActionSuccess,\n widgetContext,\n}: ListHeaderProps) {\n const { run } = useActionRunner();\n\n // Build entity actions map from toolbarActions for use with ResponsiveActionBar.\n const entityActionsMap = useMemo<Record<string, Action>>(() => {\n const out: Record<string, Action> = {};\n for (const { code, action } of toolbarActions) out[code] = action;\n return out;\n }, [toolbarActions]);\n\n // List-toolbar action layout. Plan §3.9: screen-level action_layout\n // overrides entity's. Server is already RBAC-pruned; client filters per\n // slot (server emits the full tree, not per-slot). Default-flat fallback\n // when neither layout declared. visible_if/enabled_if are evaluated\n // separately - the toolbar has no single row, so applyRowState is not\n // invoked here.\n const toolbarBase = useMemo<ResolvedActionLayoutNode[]>(() => {\n const source = screenActionLayout ?? entityActionLayout;\n if (source) {\n return filterLayoutForSlot(source, entityActionsMap, undefined, \"list_toolbar\");\n }\n return filterLayoutForSlot(\n defaultFlatForSlot(entityActionsMap, \"list_toolbar\"),\n entityActionsMap,\n undefined,\n \"list_toolbar\",\n );\n }, [screenActionLayout, entityActionLayout, entityActionsMap]);\n\n // No selection: the full toolbar is shown. Selection-mode actions (those\n // in the list_floatbar slot) render disabled, so the user sees what is\n // available and what a selection would unlock. Page-level actions stay\n // enabled. The selection-mode signal is a slot-membership proxy - see\n // actionRequiresSelection and docs/architecture.md \"List Toolbar\n // Selection Model\".\n const noSelectionToolbar = useMemo<ResolvedActionLayoutNode[]>(\n () =>\n disableLeavesWhere(\n toolbarBase,\n entityActionsMap,\n undefined,\n actionRequiresSelection,\n \"Select one or more rows first\",\n ),\n [toolbarBase, entityActionsMap],\n );\n\n // Selection active: selection-mode actions move to the floatbar\n // (ListBulkBar); the toolbar keeps only page-level actions so no action\n // appears in two bars at once. The toolbar itself stays mounted.\n const selectionToolbar = useMemo<ResolvedActionLayoutNode[]>(\n () =>\n filterLayoutForSlot(toolbarBase, entityActionsMap, undefined, \"list_toolbar\", {\n excludeIf: actionRequiresSelection,\n }),\n [toolbarBase, entityActionsMap],\n );\n\n const toolbarLayout = selectedIds.length > 0 ? selectionToolbar : noSelectionToolbar;\n\n const runToolbarAction = (code: string, action: Action) => {\n const fullCode = dispatchAction(code, action, {});\n if (fullCode === null) return;\n const name = loc(action.display?.name) || code;\n // Pass the current selection through so multi-row actions invoked from\n // the static toolbar can see what is selected. Action confirmation +\n // execution run inside useActionRunner.\n // Reason: the previous bulk-required client predicate has been removed;\n // the action itself declares whether it needs a selection.\n let selectedRows: Record<string, unknown>[] | undefined;\n if (selectedIds.length > 0) {\n // Index rows by key once - avoids an O(rows x selection) scan. Built\n // here (handler runs on action click), not per render.\n const rowByKey = new Map<string, Record<string, unknown>>();\n for (const r of data) rowByKey.set(rowKey(r), r);\n selectedRows = selectedIds\n .map((id) => rowByKey.get(id))\n .filter((r): r is Record<string, unknown> => r !== undefined);\n }\n // Return the run promise so ResponsiveActionBar awaits it and keeps the\n // toolbar button disabled with a spinner for the whole operation.\n return run({\n action,\n title: name,\n bulkSelection: selectedRows,\n execute: async (params) => {\n const ids = selectedIds.length > 0 ? selectedIds : undefined;\n const result = await runActionApi(fullCode, buildActionBody(params, ids));\n if (ids && ids.length > 0) onActionSuccess();\n return result;\n },\n });\n };\n\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, padding: \"16px 20px 10px\" }}>\n <div style={{ flex: 1 }}>\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 10 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600, letterSpacing: \"-0.012em\", color: \"var(--text)\" }}>\n {title}\n </h1>\n {/* Count slot. The total comes from a separate count request\n that resolves on its own clock; while it is in flight a\n small spinner shows, then the resolved count - rendered with\n precision matching the strategy (exact \"3,613,711\" vs\n estimated \"~3.6M\"). If the count never resolves, \"loaded\"\n (the rows actually fetched) is the quiet fallback. */}\n <span\n className=\"mono num\"\n style={{ fontSize: 12, color: \"var(--text-3)\", display: \"inline-flex\", alignItems: \"center\", gap: 5 }}\n data-testid=\"list-count\"\n >\n {loaded} of{\" \"}\n {total !== undefined ? (\n formatCount(total, countMode ?? \"exact\")\n ) : countPending ? (\n <span data-testid=\"list-count-pending\" style={{ display: \"inline-flex\" }}>\n <Spinner size={10} />\n </span>\n ) : (\n loaded\n )}\n </span>\n </div>\n </div>\n\n {/* List toolbar. Stays mounted whether or not rows are selected.\n No selection: full list_toolbar set, selection-mode actions render\n disabled. Selection active: only page-level actions remain here -\n selection-mode actions move to the floatbar (ListBulkBar). All\n render via ResponsiveActionBar's unified dispatch (handler/\n association/widget/href/ui_steps). */}\n {toolbarLayout.length > 0 && (\n <ResponsiveActionBar\n layout={toolbarLayout}\n kind=\"toolbar\"\n entityActions={entityActionsMap}\n onRun={runToolbarAction}\n widgetContext={widgetContext}\n ariaLabel=\"List toolbar actions\"\n />\n )}\n </div>\n );\n}\n","import { useMemo } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Icon } from \"@/components/primitives\";\nimport {\n defaultFlatForSlot,\n filterLayoutForSlot,\n applyRowState,\n type ResolvedActionLayoutNode,\n} from \"@/lib/actions\";\nimport { dispatchAction } from \"@/lib/action-dispatch\";\nimport { ResponsiveActionBar, type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport { loc } from \"@/types/schema\";\nimport type { Action } from \"@/types/schema\";\nimport { executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\n\nexport interface ListBulkBarProps {\n selectedIds: string[];\n /** Actions whose visibility includes `list_floatbar` - the bulk pill slot.\n * Reason: the parent owns slot filtering via `getScreenActions(...)`;\n * passing `toolbarActions` (list_toolbar slice) here silently dropped\n * __delete (which is list_floatbar/list_row/detail, NOT list_toolbar)\n * and rendered an empty pill. */\n floatbarActions: { code: string; action: Action }[];\n /** Server-emitted action_layout from screen (highest precedence per plan §3.9). */\n screenActionLayout?: ResolvedActionLayoutNode[];\n /** Server-emitted action_layout from entity. Used when screen has none. */\n entityActionLayout?: ResolvedActionLayoutNode[];\n data: Record<string, unknown>[];\n rowKey: (row: Record<string, unknown>) => string;\n entityCode?: string;\n onClear: () => void;\n onActionSuccess: () => void;\n /** Dispatch surface threaded into ResponsiveActionBar (plan §8 Task 10). */\n widgetContext: WidgetContext;\n}\n\nexport function ListBulkBar({\n selectedIds,\n floatbarActions,\n screenActionLayout,\n entityActionLayout,\n data,\n rowKey,\n entityCode,\n onClear,\n onActionSuccess,\n widgetContext,\n}: ListBulkBarProps) {\n const { run } = useActionRunner();\n const queryClient = useQueryClient();\n\n // Build entity actions map for use with ResponsiveActionBar.\n const entityActionsMap = useMemo<Record<string, Action>>(() => {\n const out: Record<string, Action> = {};\n for (const { code, action } of floatbarActions) out[code] = action;\n return out;\n }, [floatbarActions]);\n\n // Bulk pill layout. Plan §3.9: screen-level action_layout overrides entity's.\n // Server is RBAC-pruned (Task 7). Client filters per slot.\n // Multi-row enabled_if applied via applyRowState below over the selected-rows set.\n //\n // The bulk pill is the selection-driven `list_floatbar` slot, NOT\n // `list_toolbar`. Filtering for `list_toolbar` here was a real\n // regression caught by admin-qa-senior.spec.ts: the bulk bar\n // surfaced \"New User\" (__create, visibility=[list_toolbar]) and\n // hid \"Delete\" (__delete, visibility includes list_floatbar but\n // NOT list_toolbar). Per the unified-toolbar Phase 1 contract:\n // list_toolbar - always-visible static toolbar actions\n // list_floatbar - selection-driven bulk pill actions\n // Actions that should appear in BOTH must list both slots in their\n // visibility array; the filter at this site no longer pretends\n // they're equivalent.\n const bulkLayout = useMemo<ResolvedActionLayoutNode[]>(() => {\n const source = screenActionLayout ?? entityActionLayout;\n if (source) {\n return filterLayoutForSlot(source, entityActionsMap, undefined, \"list_floatbar\");\n }\n return filterLayoutForSlot(\n defaultFlatForSlot(entityActionsMap, \"list_floatbar\"),\n entityActionsMap,\n undefined,\n \"list_floatbar\",\n );\n }, [screenActionLayout, entityActionLayout, entityActionsMap]);\n\n // Apply row state for bulk: evaluate enabled_if against all selected rows.\n // selectedIds.map(data.find(...)) is O(rows x selection) - quadratic on\n // large tables. Index into a Map instead, and skip the build entirely\n // when nothing is selected (the common case).\n const selectedRows = useMemo(() => {\n if (selectedIds.length === 0) return [];\n const rowByKey = new Map<string, Record<string, unknown>>();\n for (const r of data) rowByKey.set(rowKey(r), r);\n return selectedIds\n .map((id) => rowByKey.get(id))\n .filter((r): r is Record<string, unknown> => r !== undefined);\n }, [selectedIds, data, rowKey]);\n\n const bulkLayoutWithState = useMemo(\n () => applyRowState(bulkLayout, entityActionsMap, undefined, {}, selectedRows),\n [bulkLayout, entityActionsMap, selectedRows],\n );\n\n const runBulkAction = (code: string, action: Action) => {\n // Bulk-mode UI action: pass empty row context - toolbar actions have no\n // single record to interpolate against. Multi-row state-aware UI actions\n // would need a future contract extension (out of scope today).\n const fullCode = dispatchAction(code, action, {});\n if (fullCode === null) return;\n // Return the run promise so ResponsiveActionBar awaits it and keeps the\n // bulk-bar button disabled with a spinner for the whole operation.\n return run({\n action,\n title: loc(action.display?.name) || code,\n bulkSelection: selectedRows,\n execute: async (params) => {\n const result = await runActionApi(fullCode, buildActionBody(params, selectedIds));\n onActionSuccess();\n if (entityCode) {\n void queryClient.invalidateQueries({ queryKey: [\"entity-data\", entityCode] });\n // entity-count is a separate query from the list window; without\n // this the list total and page count stay stale after a bulk action.\n void queryClient.invalidateQueries({ queryKey: [\"entity-count\", entityCode] });\n void queryClient.invalidateQueries({ queryKey: [\"entity-item\", entityCode] });\n }\n return result;\n },\n });\n };\n\n // Render nothing when there are no bulk-eligible actions; the bespoke\n // \"Delete selected\" button that used to ride alongside this pill was\n // removed in Task 38. __delete will flow through ResponsiveActionBar as\n // a regular handler action once Task 34 (platform-default injection) lands.\n if (bulkLayoutWithState.length === 0) {\n return null;\n }\n\n return (\n <div\n style={{\n position: \"fixed\",\n left: \"50%\",\n bottom: \"calc(var(--tabbar-h) + 18px)\",\n transform: \"translateX(-50%)\",\n zIndex: 45,\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"6px 10px 6px 14px\",\n background: \"var(--text)\",\n color: \"var(--bg)\",\n borderRadius: 10,\n boxShadow: \"var(--shadow-lg)\",\n animation: \"bulkBarIn .15s ease-out\",\n }}\n >\n <style>{`@keyframes bulkBarIn{from{transform:translateX(-50%) translateY(8px);opacity:0}to{transform:translateX(-50%) translateY(0);opacity:1}}`}</style>\n <span style={{ fontSize: 12.5, fontWeight: 600 }}>\n <span\n style={{\n padding: \"1px 7px\",\n borderRadius: 4,\n background: \"var(--accent)\",\n color: \"var(--primary-fg)\",\n marginRight: 6,\n fontSize: 11.5,\n }}\n >\n {selectedIds.length}\n </span>\n selected\n </span>\n <ResponsiveActionBar\n layout={bulkLayoutWithState}\n kind=\"toolbar\"\n entityActions={entityActionsMap}\n rowSelection={selectedRows}\n onRun={runBulkAction}\n widgetContext={widgetContext}\n ariaLabel=\"Bulk action bar\"\n />\n <button\n onClick={onClear}\n style={{\n background: \"none\",\n border: \"none\",\n color: \"var(--bg)\",\n cursor: \"pointer\",\n opacity: 0.6,\n padding: \"4px 6px\",\n borderRadius: 6,\n }}\n title=\"Clear selection\"\n >\n <Icon.x size={14} />\n </button>\n </div>\n );\n}\n","import { type ReactNode } from \"react\";\nimport type { Cell } from \"@tanstack/react-table\";\n\ntype RowData = Record<string, unknown>;\n\n// DataCell renders one cell in the virtualized list table.\n//\n// Calls the column's cell function directly (instead of going through\n// flexRender) so we can inspect the renderer's return value. When it is a\n// primitive string, we set `title=` on the `<td>` so the user sees the full\n// value on hover when overflow:hidden truncates it. When the renderer\n// returns a JSX element (markdown HoverCard, badge, link, image), we leave\n// `title` unset and the element owns its own tooltip behaviour.\n//\n// The truncation CSS lives on this `<td>`; placing the tooltip here keeps\n// concern and effect on the same element. See\n// docs/plans/2026-05-09-virtual-list-100k.md §2.\nexport function DataCell({\n cell,\n isRowSelected,\n}: {\n cell: Cell<RowData, unknown>;\n isRowSelected: boolean;\n}) {\n const isSelect = cell.column.id === \"_select\";\n const isActions = cell.column.id === \"_actions\";\n const cellDef = cell.column.columnDef.cell;\n const out =\n typeof cellDef === \"function\" ? cellDef(cell.getContext()) : cellDef;\n const title = typeof out === \"string\" ? out : undefined;\n return (\n <td\n title={title}\n style={{\n padding: isSelect ? \"0 6px 0 12px\" : isActions ? \"0 6px\" : \"0 10px\",\n verticalAlign: \"middle\",\n borderBottom: \"1px solid var(--divider)\",\n width: cell.column.getSize(),\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n maxWidth: cell.column.getSize(),\n color: \"var(--text)\",\n // Sticky select column needs a SOLID background (not inherit) so\n // horizontally-scrolled data cells stay behind it instead of\n // bleeding through. Use a surface-toned background that follows\n // the row selection state.\n background: isSelect\n ? isRowSelected\n ? \"var(--row-selected-bg)\"\n : \"var(--bg)\"\n : \"inherit\",\n position: isSelect ? \"sticky\" : \"static\",\n left: isSelect ? 0 : \"auto\",\n zIndex: isSelect ? 1 : 0,\n }}\n onClick={\n isSelect || isActions ? (e) => e.stopPropagation() : undefined\n }\n >\n {out as ReactNode}\n </td>\n );\n}\n","// Plan 2026-05-26 §B13. Frontend helpers for the field-level\n// classification surface. Pure functions that components consume to\n// render the locked-widget badge, gate write inputs, and surface the\n// operator-facing required_permission hint.\n//\n// The platform delivers per-caller `field_access` on Entity (B9) and a\n// top-level `classifications:` map of localized tier names. This\n// module wraps the cross-reference so callers don't have to thread\n// both objects through every component.\n\nimport type { Entity, FieldAccessInfo, ClassificationTierDef, Schema } from \"../types/schema\";\n\n/**\n * isFieldReadable returns true when the caller can see the field's\n * value. Non-classified fields are always readable (no entry in\n * field_access). Classified fields are readable when field_access\n * marks read=\"granted\". Plan §B9.\n */\nexport function isFieldReadable(entity: Entity | undefined, fieldName: string): boolean {\n const access = entity?.field_access?.[fieldName];\n if (!access) return true; // not classified — always readable\n return access.read === \"granted\";\n}\n\n/**\n * isFieldWritable returns true when the caller can submit a new value\n * for the field. Form components disable input + show a lock badge\n * when this returns false. Non-classified fields are always writable\n * (gating happens via entity-level RBAC at the HTTP layer). Plan §B9.\n */\nexport function isFieldWritable(entity: Entity | undefined, fieldName: string): boolean {\n const access = entity?.field_access?.[fieldName];\n if (!access) return true;\n return access.write === \"granted\";\n}\n\n/**\n * fieldClassification returns the classification tier code gating\n * the field, or undefined when the field is non-classified. Lets\n * UI code render a tier badge without re-implementing the field_access\n * lookup.\n */\nexport function fieldClassification(entity: Entity | undefined, fieldName: string): string | undefined {\n return entity?.field_access?.[fieldName]?.classification;\n}\n\n/**\n * fieldRequiredPermission returns the exact grant string an\n * administrator must add to a role to unlock the field for the\n * current caller. Returns undefined when the field is readable +\n * writable (no hint needed). Plan §B9 — operator hand-off surface.\n */\nexport function fieldRequiredPermission(entity: Entity | undefined, fieldName: string): string | undefined {\n return entity?.field_access?.[fieldName]?.required_permission;\n}\n\n/**\n * classificationTier returns the localized tier metadata for a field\n * (display name, description, audit_on_read flag, icon). Resolves\n * via the top-level schema.classifications map. Returns undefined when\n * the field is non-classified or the tier code is unknown in the\n * catalog (defense — points at a misconfigured registry).\n */\nexport function classificationTier(\n schema: Schema | undefined,\n entity: Entity | undefined,\n fieldName: string,\n): ClassificationTierDef | undefined {\n const tierCode = fieldClassification(entity, fieldName);\n if (!tierCode) return undefined;\n return schema?.classifications?.[tierCode];\n}\n\n/**\n * isFieldSoDProtected reports whether the field's tier carries\n * sod_protected:true in the catalog. Used by UI to surface a stronger\n * visual treatment (e.g. red lock vs. grey lock) for tiers that\n * trigger the stricter SoD controls (superadmin-bypass-disabled\n * tenants, role self-assignment refusal, etc).\n */\nexport function isFieldSoDProtected(\n schema: Schema | undefined,\n entity: Entity | undefined,\n fieldName: string,\n): boolean {\n const tier = classificationTier(schema, entity, fieldName);\n return !!tier?.sod_protected;\n}\n\n/**\n * fieldAccessInfo returns the raw FieldAccessInfo entry for a field,\n * or undefined when the field is non-classified. Convenience for\n * components that need the full structure (e.g. a tooltip rendering\n * both classification name + required_permission hint).\n */\nexport function fieldAccessInfo(entity: Entity | undefined, fieldName: string): FieldAccessInfo | undefined {\n return entity?.field_access?.[fieldName];\n}\n","import { useMemo, useCallback } from \"react\";\nimport { Icon, IconBtn } from \"@/components/primitives\";\nimport {\n defaultFlatForSlot,\n filterLayoutForSlot,\n applyRowState,\n type ResolvedActionLayoutNode,\n} from \"@/lib/actions\";\nimport { dispatchAction } from \"@/lib/action-dispatch\";\nimport { ResponsiveActionBar, type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport { loc } from \"@/types/schema\";\nimport type { Action, Entity, Screen } from \"@/types/schema\";\nimport { executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\n\nexport interface RowActionsCellProps {\n row: Record<string, unknown>;\n rowId: string;\n /** detail_screen for row-click navigation. The component reads\n * screen.action_layout via cast to ResolvedActionLayoutNode[] (server emits\n * the resolved wire shape per plan §8 even though the raw Screen TS type\n * declares ActionLayoutNode[] for authoring). */\n screen: { detail_screen?: string };\n /** Full Screen object used to build the row-scoped WidgetContext that the\n * ResponsiveActionBar dispatcher passes to href/widget action modes. */\n fullScreen: Screen;\n entity: Entity;\n entityCode?: string;\n /** Gate flags that determine whether the kebab column renders at all.\n * After the unified-toolbar Phase 1 cleanup the kebab's Edit/Delete\n * affordances come from the __edit / __delete platform-default actions\n * (injected server-side at load), so these flags now only control\n * column visibility - the legacy trailing-item content they once\n * feed has been removed. */\n hasDefaultEditAction: boolean;\n hasDefaultDeleteAction: boolean;\n rowActions: { code: string; action: Action }[];\n // title is optional - when omitted, useTabNavigate resolves the\n // destination screen's name from the route table (never the raw URL).\n tabNavigate: (href: string, opts?: { title?: string; icon?: string; search?: string }) => void;\n onPeek?: () => void;\n /** Active UI locale, forwarded into the row-scoped WidgetContext. */\n locale: string;\n}\n\nexport function RowActionsCell({\n row,\n rowId,\n screen,\n fullScreen,\n entity,\n entityCode,\n hasDefaultEditAction,\n hasDefaultDeleteAction,\n rowActions,\n tabNavigate,\n onPeek,\n locale,\n}: RowActionsCellProps) {\n const { run } = useActionRunner();\n\n // Row-scoped WidgetContext. selectedRows/selectedIds are empty because the\n // per-row kebab does not own a bulk selection; bulk dispatch lives on the\n // bulk pill. Reason: plan §8 keeps WidgetContext placement-agnostic, so the\n // row kebab gets the same dispatch surface as the toolbar without leaking\n // toolbar-level state into a per-row leaf.\n const widgetContext = useMemo<WidgetContext>(\n () => ({\n entity,\n entityCode: entityCode ?? \"\",\n screen: fullScreen,\n screenRoute: fullScreen.route,\n selectedRows: [],\n selectedIds: [],\n locale,\n tabNavigate: (url, meta) => tabNavigate(url, { title: meta?.title, icon: meta?.icon, search: meta?.search }),\n runActionApi: (code, params) => runActionApi(code, params as Parameters<typeof runActionApi>[1]),\n }),\n [entity, entityCode, fullScreen, locale, tabNavigate],\n );\n\n // Placement 3: per-row actions via ResponsiveActionBar kind=\"row\".\n // Build entity actions map from the resolved rowActions array.\n const rowEntityActionsMap = useMemo<Record<string, Action>>(() => {\n const out: Record<string, Action> = {};\n for (const { code, action } of rowActions) out[code] = action;\n return out;\n }, [rowActions]);\n\n // Row layout. Plan §3.9: screen-level action_layout overrides entity's.\n // Server has already RBAC-pruned the source tree (Task 7). Client filters per\n // slot (server emits the full tree, not per-slot). visible_if/enabled_if\n // applied by applyRowState below over the current row.\n // Casts: raw TS Entity/Screen.action_layout is typed as ActionLayoutNode[]\n // (authoring shape); server emits ResolvedActionLayoutNode[] per plan §8.\n //\n // Schema-declared edit/delete-shaped actions now flow through the same\n // layout as any other list_row action; their click dispatches via\n // ResponsiveActionBar (href -> tabNavigate, handler -> onRun). The\n // platform-default Edit/Delete affordances stay as trailing items below.\n const rowLayout = useMemo<ResolvedActionLayoutNode[]>(() => {\n const screenLayoutRaw = (screen as unknown as { action_layout?: unknown }).action_layout;\n const screenLayout = (screenLayoutRaw as ResolvedActionLayoutNode[] | undefined) ?? null;\n const entityLayout = (entity.action_layout as unknown as ResolvedActionLayoutNode[] | undefined) ?? null;\n const source = screenLayout ?? entityLayout;\n if (source) {\n return filterLayoutForSlot(source, rowEntityActionsMap, undefined, \"list_row\");\n }\n return filterLayoutForSlot(\n defaultFlatForSlot(rowEntityActionsMap, \"list_row\"),\n rowEntityActionsMap,\n undefined,\n \"list_row\",\n );\n }, [screen, entity, rowEntityActionsMap]);\n\n // Apply row-state (visible_if / enabled_if) for the current row.\n const rowLayoutWithState = useMemo(\n () => applyRowState(rowLayout, rowEntityActionsMap, undefined, row, []),\n [rowLayout, rowEntityActionsMap, row],\n );\n\n\n const runRowAction = useCallback(\n (code: string, action: Action) => {\n const fullCode = dispatchAction(code, action, { row });\n if (fullCode === null) return;\n // Return the run promise so ResponsiveActionBar awaits it and keeps the\n // row action disabled with a spinner for the whole operation.\n return run({\n action,\n title: loc(action.display?.name) || code,\n execute: (params) => runActionApi(fullCode, buildActionBody(params, [rowId])),\n });\n },\n [run, rowId, row],\n );\n\n const showDefaultDropdown = hasDefaultEditAction || hasDefaultDeleteAction;\n\n return (\n <div\n className=\"sl-row-actions\"\n style={{ display: \"flex\", alignItems: \"center\", gap: 2, justifyContent: \"flex-end\" }}\n >\n {onPeek && (\n <IconBtn\n size={22}\n icon={<Icon.eye size={12} />}\n onClick={(e) => {\n e.stopPropagation();\n onPeek();\n }}\n title=\"Peek\"\n />\n )}\n {/*\n ONE kebab per row (plan §11). ResponsiveActionBar kind=\"row\" owns the\n single kebab \"⋮\". Schema actions render as leaves inside that dropdown;\n platform-default Edit (navigation) and Delete (mutation) thread in as\n trailing items so they share the same dropdown surface instead of\n rendering a sibling \"More\" kebab.\n */}\n {(rowLayoutWithState.length > 0 || showDefaultDropdown) && (\n <ResponsiveActionBar\n layout={rowLayoutWithState}\n row={row}\n kind=\"row\"\n entityActions={rowEntityActionsMap}\n onRun={runRowAction}\n widgetContext={widgetContext}\n ariaLabel=\"Row actions\"\n />\n )}\n </div>\n );\n}\n","import { createElement, type RefObject, type ReactElement } from \"react\";\nimport type { ColumnDef } from \"@tanstack/react-table\";\nimport { Check, Icon, Pill, type PillTone } from \"@/components/primitives\";\nimport { resolveCellRenderer, type RefsMap } from \"@/components/fields/cells/registry\";\nimport { loc, fieldDisplayName } from \"@/types/schema\";\nimport type { Action, Entity, Screen } from \"@/types/schema\";\nimport type { V2StatusInstance, V2PropertyInstance } from \"@/types/api\";\nimport { fieldAccessInfo } from \"@/lib/field-classification\";\n\n// classifiedColumnHeader decorates a column header label with a lock\n// glyph when the caller is classification-denied on read. Plan §B13.\n// Returns the bare label when the field is non-classified or the\n// caller holds the grant. Used by buildListColumns to mark columns\n// whose values were stripped from the row payload upstream — gives\n// users a visual cue (\"you don't have access to this column\") instead\n// of just empty cells.\nfunction classifiedColumnHeader(label: string, entity: Entity, fieldCode: string): string | ReactElement {\n const access = fieldAccessInfo(entity, fieldCode);\n if (!access || access.read !== \"denied\") return label;\n const title = access.required_permission\n ? `Locked column: requires \"${access.required_permission}\"`\n : `Locked column: ${access.classification} classification`;\n return createElement(\n \"span\",\n {\n title,\n \"aria-label\": title,\n className: \"inline-flex items-center gap-1\",\n },\n createElement(Icon.lock, { size: 12 }),\n label,\n );\n}\nimport {\n renderSchemaField,\n resolveSchemaFieldLabel,\n} from \"@/components/file-widgets\";\nimport { RowActionsCell } from \"./RowActionsCell\";\n\ntype RowData = Record<string, unknown>;\n\n// SchemaEntities/SchemaScreens mirror the optional maps the orchestrator reads\n// off the loaded schema. Kept loose so the builder does not depend on the\n// full Schema type surface.\ntype SchemaMaps = {\n entities?: Record<string, Entity>;\n screens?: Record<string, Screen>;\n};\n\n// BuildListColumnsArgs is the full input the list table column builder needs.\n// All values are derived in SmartListPageInner's render and passed through;\n// the builder itself is pure (no hooks, no closures over component state) so\n// the orchestrator can wrap the call in a single useMemo.\nexport interface BuildListColumnsArgs {\n /** Visible column keys from URL state (not the full screen.columns pool). */\n visibleCols: string[];\n entity: Entity | undefined;\n screen: Screen | undefined;\n schema: SchemaMaps | undefined;\n entityCode: string | undefined;\n /** list_row-slot actions resolved by getScreenActions. */\n rowActions: { code: string; action: Action }[];\n hasBatchActions: boolean;\n /** True when the screen explicitly binds row_actions (controls full menu). */\n hasExplicitRowActions: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n peekEnabled: boolean;\n userLocale: string;\n tabNavigate: (href: string, opts?: { title?: string; icon?: string; search?: string }) => void;\n /** Latest $refs payload, read through a ref so column identity is stable\n * across data fetches (see SmartListPageInner refsRef comment). */\n refsRef: RefObject<RefsMap | undefined>;\n /** Row-selection key, used as the stable rowId for per-row action dispatch. */\n rowSelectionKey: (entity: Entity, row: RowData) => string;\n setPeekRow: (row: RowData) => void;\n}\n\n// buildListColumns assembles the TanStack ColumnDef array for the list table:\n// an optional selection checkbox, one column per visible field (status group,\n// property, file field, or regular field), and an optional trailing actions\n// column. Extracted from SmartListPageInner so the god-file keeps only\n// orchestration; behavior is byte-identical to the inlined useMemo body.\nexport function buildListColumns(args: BuildListColumnsArgs): ColumnDef<RowData>[] {\n const {\n visibleCols,\n entity,\n screen,\n schema,\n entityCode,\n rowActions,\n hasBatchActions,\n hasExplicitRowActions,\n canUpdate,\n canDelete,\n peekEnabled,\n userLocale,\n tabNavigate,\n refsRef,\n rowSelectionKey,\n setPeekRow,\n } = args;\n\n if (visibleCols.length === 0 || !entity) return [];\n\n const cols: ColumnDef<RowData>[] = [];\n\n // Checkbox column for batch selection.\n if (hasBatchActions) {\n cols.push({\n id: \"_select\",\n header: ({ table: tbl }) =>\n createElement(Check, {\n checked: tbl.getIsAllRowsSelected(),\n indeterminate: tbl.getIsSomeRowsSelected(),\n onChange: () => tbl.toggleAllRowsSelected(),\n }),\n size: 34,\n // Wrapper carries data-testid for e2e row-selection targeting.\n // Plain <Check> is a span with no accessible name; the wrapper\n // gives Playwright a stable hook scoped per row via the row's\n // visible content.\n cell: ({ row }) =>\n createElement(\n \"span\",\n { \"data-testid\": \"row-select-checkbox\" },\n createElement(Check, {\n checked: row.getIsSelected(),\n onChange: () => row.toggleSelected(),\n }),\n ),\n });\n }\n\n // Data columns (use visible columns from URL state, not full screen.columns pool).\n for (const colKey of visibleCols) {\n // Status group column: $status.pipeline\n if (colKey.startsWith(\"$status.\")) {\n const groupCode = colKey.slice(8);\n const group = entity.status_groups?.[groupCode];\n if (!group) continue;\n const statusMap = new Map(group.statuses.map((s) => [s.code, s]));\n cols.push({\n id: colKey,\n header: loc(group.name) || groupCode,\n size: 140,\n cell: ({ row }) => {\n const statuses = (row.original as Record<string, unknown>)[\"$statuses\"] as V2StatusInstance[] | undefined;\n const match = statuses?.find((s) => s.group_code === groupCode);\n if (!match) {\n return createElement(\"span\", { className: \"text-sm text-muted-foreground\" }, \"-\");\n }\n const def = statusMap.get(match.status_code);\n return createElement(\n Pill,\n { tone: (def?.color as PillTone) ?? \"slate\", size: \"xs\" },\n loc(def?.name) || match.status_code,\n );\n },\n });\n continue;\n }\n\n // Property column: $property.clickup_task_id\n if (colKey.startsWith(\"$property.\")) {\n const propCode = colKey.slice(10);\n const propDef = entity.properties?.[propCode];\n if (!propDef) continue;\n cols.push({\n id: colKey,\n header: () => classifiedColumnHeader(loc(propDef.name) || propCode, entity, propCode),\n size: 150,\n enableSorting: false,\n cell: ({ row }) => {\n const props = (row.original as Record<string, unknown>)[\"$properties\"] as V2PropertyInstance[] | undefined;\n const match = props?.find((p) => p.property_code === propCode);\n if (!match || match.value == null) {\n return createElement(\"span\", { className: \"text-sm text-muted-foreground\" }, \"-\");\n }\n return createElement(\"span\", { className: \"text-sm\" }, String(match.value));\n },\n });\n continue;\n }\n\n const fileField = entity.files?.get(colKey);\n if (fileField) {\n cols.push({\n id: colKey,\n accessorFn: (row) => (row.$files as Record<string, unknown> | undefined)?.[colKey] ?? null,\n header: () => classifiedColumnHeader(resolveSchemaFieldLabel(entity, colKey, schema?.entities), entity, colKey),\n size: 150,\n cell: ({ row }) =>\n renderSchemaField({\n entity,\n entityCode,\n fieldName: colKey,\n record: row.original,\n mode: \"display\",\n surface: \"list\",\n refs: refsRef.current,\n locale: userLocale,\n }),\n });\n continue;\n }\n\n // Regular field column.\n //\n // Hot path: resolve the cell renderer ONCE per column. The factory\n // closes over schema lookups (entities/screens/status_groups/value_map);\n // the per-cell function is then a single bound call (`renderer(value,\n // row)`), with no per-row type/widget dispatch.\n //\n // Per plan §3 the display dispatcher is TOTAL: the engine stamps a\n // smart default at YAML-load time, and the cell registry covers every\n // allow-listed widget (CI sync gate enforces this). No fallback path.\n const field = entity.fields.get(colKey);\n if (!field) continue;\n const displayName = fieldDisplayName(field, colKey, schema?.entities);\n const width = field.display?.width;\n const renderer = resolveCellRenderer({\n field,\n fieldName: colKey,\n surface: \"list\",\n entity,\n entities: schema?.entities,\n screens: schema?.screens,\n refs: () => refsRef.current,\n locale: userLocale,\n });\n cols.push({\n id: colKey,\n accessorKey: colKey,\n header: () => classifiedColumnHeader(displayName, entity, colKey),\n enableSorting: field.type !== \"json\",\n size: width ?? 150,\n cell: ({ getValue, row }) => renderer(getValue(), row.original as Record<string, unknown>),\n });\n }\n\n // Actions column (dropdown) - only show when there are items to display.\n const hasDefaultEditAction = !hasExplicitRowActions && canUpdate;\n const hasDefaultDeleteAction = !hasExplicitRowActions && canDelete;\n if (hasDefaultEditAction || hasDefaultDeleteAction || rowActions.length > 0) {\n cols.push({\n id: \"_actions\",\n header: \"\",\n size: 40,\n cell: ({ row }) =>\n createElement(RowActionsCell, {\n row: row.original,\n rowId: rowSelectionKey(entity, row.original as RowData),\n screen: screen!,\n fullScreen: screen!,\n entity: entity!,\n entityCode,\n hasDefaultEditAction,\n hasDefaultDeleteAction,\n rowActions,\n tabNavigate,\n onPeek: peekEnabled ? () => setPeekRow(row.original) : undefined,\n locale: userLocale,\n }),\n });\n }\n\n return cols;\n}\n","import { useRef, useState } from \"react\";\nimport { Btn, Dropdown, Icon, MenuItem } from \"@/components/primitives\";\n\ninterface ColumnPickerProps {\n availableColumns: string[];\n visibleColumns: string[];\n onToggle: (col: string) => void;\n onReset: () => void;\n onShowAll: () => void;\n columnLabel?: (code: string) => string;\n}\n\nexport function ColumnPicker({\n availableColumns,\n visibleColumns,\n onToggle,\n onReset,\n onShowAll,\n columnLabel = (c) => c,\n}: ColumnPickerProps) {\n const btnRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n\n return (\n <>\n <Btn\n ref={btnRef}\n icon={<Icon.columns size={13} />}\n onClick={() => setOpen((prev) => !prev)}\n active={open}\n >\n Columns\n </Btn>\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n align=\"end\"\n width={200}\n >\n <div\n style={{\n padding: \"6px 8px 8px\",\n fontSize: 10.5,\n color: \"var(--text-3)\",\n fontWeight: 600,\n letterSpacing: \"0.06em\",\n textTransform: \"uppercase\",\n }}\n >\n Show columns\n </div>\n {availableColumns.map((col) => {\n const isVisible = visibleColumns.includes(col);\n return (\n <MenuItem\n key={col}\n icon={isVisible ? <Icon.check size={12} /> : <span style={{ width: 12, display: \"inline-block\" }} />}\n onClick={() => onToggle(col)}\n >\n {columnLabel(col)}\n </MenuItem>\n );\n })}\n <MenuItem divider />\n <MenuItem icon={<Icon.refresh size={12} />} onClick={onReset}>\n Reset\n </MenuItem>\n <MenuItem icon={<Icon.eye size={12} />} onClick={onShowAll}>\n Show all\n </MenuItem>\n </Dropdown>\n </>\n );\n}\n","import { useRef, useState } from \"react\";\nimport { Btn, Dropdown, Icon, MenuItem } from \"@/components/primitives\";\nimport { loc } from \"@/types/schema\";\n\nconst PLAIN_BTN_STYLE: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n height: 28,\n padding: \"0 10px\",\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n cursor: \"pointer\",\n fontSize: 12,\n color: \"var(--text-2)\",\n fontFamily: \"inherit\",\n};\n\nexport interface ViewItem {\n id: string;\n name: Record<string, string>;\n icon?: string;\n source: \"system\" | \"personal\" | \"shared\";\n is_default?: boolean;\n filters?: unknown[];\n columns?: string[];\n sort?: string;\n}\n\ninterface ViewSwitcherProps {\n views: ViewItem[];\n activeViewId: string | null;\n isDirty: boolean;\n onSelect: (view: ViewItem) => void;\n onSave?: () => void;\n onSaveAs?: () => void;\n onSetDefault?: (viewId: string) => void;\n}\n\nexport function ViewSwitcher({\n views,\n activeViewId,\n isDirty,\n onSelect,\n onSave,\n onSaveAs,\n}: ViewSwitcherProps) {\n const btnRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n\n const activeView = views.find((v) => v.id === activeViewId);\n const systemViews = views.filter((v) => v.source === \"system\");\n const personalViews = views.filter((v) => v.source === \"personal\");\n const sharedViews = views.filter((v) => v.source === \"shared\");\n\n const displayName = activeView ? loc(activeView.name) : \"Default view\";\n\n const sectionLabel = (text: string) => (\n <div\n style={{\n padding: \"6px 8px 4px\",\n fontSize: 10,\n color: \"var(--text-3)\",\n fontWeight: 700,\n letterSpacing: \"0.08em\",\n textTransform: \"uppercase\",\n }}\n >\n {text}\n </div>\n );\n\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n <button\n ref={btnRef}\n type=\"button\"\n onClick={() => setOpen((prev) => !prev)}\n style={PLAIN_BTN_STYLE}\n >\n <Icon.star size={12} />\n <span>{displayName}</span>\n {isDirty && (\n <span\n style={{\n display: \"inline-block\",\n width: 6,\n height: 6,\n borderRadius: 999,\n background: \"var(--warn)\",\n marginLeft: 2,\n }}\n />\n )}\n <Icon.down size={11} />\n </button>\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n width={280}\n >\n <MenuItem\n icon={!activeViewId ? <Icon.check size={12} /> : undefined}\n active={!activeViewId}\n onClick={() => {\n onSelect({ id: \"\", name: { en: \"Default\" }, source: \"system\" });\n setOpen(false);\n }}\n >\n Default view\n </MenuItem>\n\n {systemViews.length > 0 && sectionLabel(\"Presets\")}\n {systemViews.map((v) => (\n <MenuItem\n key={v.id}\n icon={activeViewId === v.id ? <Icon.check size={12} /> : undefined}\n active={activeViewId === v.id}\n onClick={() => {\n onSelect(v);\n setOpen(false);\n }}\n >\n {loc(v.name)}\n </MenuItem>\n ))}\n\n {personalViews.length > 0 && sectionLabel(\"My Views\")}\n {personalViews.map((v) => (\n <MenuItem\n key={v.id}\n icon={activeViewId === v.id ? <Icon.check size={12} /> : v.is_default ? <Icon.star size={12} /> : undefined}\n active={activeViewId === v.id}\n onClick={() => {\n onSelect(v);\n setOpen(false);\n }}\n >\n {loc(v.name)}\n </MenuItem>\n ))}\n\n {sharedViews.length > 0 && sectionLabel(\"Shared\")}\n {sharedViews.map((v) => (\n <MenuItem\n key={v.id}\n icon={activeViewId === v.id ? <Icon.check size={12} /> : <Icon.globe size={12} />}\n active={activeViewId === v.id}\n onClick={() => {\n onSelect(v);\n setOpen(false);\n }}\n >\n {loc(v.name)}\n </MenuItem>\n ))}\n\n {onSaveAs && (\n <>\n <MenuItem divider />\n <MenuItem\n icon={<Icon.plus size={12} />}\n onClick={() => {\n onSaveAs();\n setOpen(false);\n }}\n >\n Save as new view…\n </MenuItem>\n </>\n )}\n </Dropdown>\n\n {isDirty && activeViewId && onSave && (\n <Btn icon={<Icon.save size={12} />} onClick={onSave}>\n Save\n </Btn>\n )}\n </div>\n );\n}\n","import { useSchema } from \"./useSchema\";\nimport type { Screen } from \"@/types/schema\";\n\nexport function useScreen(screenCode: string | undefined): Screen | undefined {\n const { data: schema } = useSchema();\n if (!schema || !screenCode) return undefined;\n return schema.screens[screenCode];\n}\n","import { useMemo } from \"react\";\nimport { useEntityData } from \"./useEntityData\";\nimport { useScreen } from \"./useScreen\";\nimport type { ViewItem } from \"@/components/views/ViewSwitcher\";\n\n/**\n * useViews fetches DB views for a screen and merges with system views (presets) from DSL.\n * Returns a unified list with source tagging.\n */\nexport function useViews(screenCode: string, userId?: string) {\n const screen = useScreen(screenCode);\n\n // Fetch DB views for this screen (personal + shared).\n const { data: dbResponse } = useEntityData(\"view\", {\n filters: [\n { field: \"screen_code\", op: \"eq\", value: screenCode },\n // Return own views + shared views. The OR ensures visibility.\n ...(userId ? [{ or: [\n [{ field: \"user_id\", op: \"eq\", value: userId }],\n [{ field: \"is_shared\", op: \"eq\", value: \"true\" }],\n ] }] : []),\n ],\n per_page: 100,\n });\n\n const views = useMemo<ViewItem[]>(() => {\n const items: ViewItem[] = [];\n\n // System views (presets from DSL) - always first.\n if (screen?.views) {\n for (const v of screen.views) {\n items.push({\n id: v.code,\n name: v.name,\n icon: v.icon,\n source: \"system\",\n filters: v.filters as unknown[],\n columns: v.columns,\n sort: v.sort,\n });\n }\n }\n\n // DB views (personal + shared).\n if (dbResponse?.data) {\n for (const row of dbResponse.data) {\n const isOwn = userId && String(row.user_id) === userId;\n items.push({\n id: String(row.id),\n name: typeof row.name === \"object\" && row.name !== null ? row.name as Record<string, string> : { en: String(row.name ?? \"\") },\n icon: row.icon as string | undefined,\n source: isOwn ? \"personal\" : \"shared\",\n is_default: row.is_default === true,\n filters: row.filters as unknown[] | undefined,\n columns: row.columns as string[] | undefined,\n sort: row.sort as string | undefined,\n });\n }\n }\n\n return items;\n }, [screen?.views, dbResponse?.data, userId]);\n\n return views;\n}\n","import {\n useMemo,\n useRef,\n useState,\n useCallback,\n useEffect,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n useReactTable,\n getCoreRowModel,\n flexRender,\n type ColumnDef,\n type SortingState,\n type RowSelectionState,\n} from \"@tanstack/react-table\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport {\n Btn,\n Dropdown,\n Icon,\n IconBtn,\n Kbd,\n MenuItem,\n Pill,\n Spinner,\n TextInput,\n} from \"@/components/primitives\";\nimport { ExportMenu } from \"./ExportMenu\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useEntityData, useEntityCount } from \"@/hooks/useEntityData\";\nimport { useCreateEntity, useDeleteEntity, useUpdateEntity } from \"@/hooks/useMutations\";\nimport { useDebounce } from \"@/hooks/useDebounce\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { EmptyState } from \"@/components/shared/EmptyState\";\nimport { safeExternalUrl } from \"@/lib/safe-url\";\nimport { TableSkeleton } from \"@/components/shared/LoadingSkeleton\";\nimport { ConfirmDialog } from \"@/components/shared/ConfirmDialog\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { EMBED_PARAM_KEYS, useEmbed } from \"@/embed\";\nimport { useTabStore } from \"@/stores/tabs\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { useRealtimeStore, isEntityLive } from \"@/stores/realtime\";\nimport { toast } from \"@/lib/toast\";\nimport { ApiError } from \"@/types/api\";\nimport { getScreenActions, type ResolvedActionLayoutNode } from \"@/lib/actions\";\nimport { type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport {\n loc,\n fieldDisplayName,\n buildDetailHrefFromId,\n buildEntityDetailHref,\n entityAllowsMutation,\n extractPkValues,\n getDisplayValue,\n pkValuesToDetailId,\n} from \"@/types/schema\";\nimport type { Entity, PkValues } from \"@/types/schema\";\nimport { useFilters } from \"@/hooks/useFilters\";\nimport { QuickPeek, type QuickPeekFieldSpec } from \"@/components/list/QuickPeek\";\nimport { setPeekDraft, clearPeekDraft } from \"@/components/list/peekDraftStore\";\nimport { executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport type { Action } from \"@/types/schema\";\nimport { removeFilter } from \"@/lib/filters\";\nimport {\n applyAppendSort,\n applyShiftClickSort,\n MAX_SORT_COLS,\n parseSort,\n removeSortField,\n serializeSort,\n stripUnsupportedSortSegments,\n type SortCol,\n type SortNulls,\n} from \"@/lib/sort\";\nimport { formatCount } from \"@/lib/format-count\";\nimport { FilterChips } from \"@/components/filters/FilterChips\";\nimport { FilterDrawer } from \"@/components/filters/FilterDrawer\";\nimport { buildFilterFieldMeta } from \"./list/filter-meta\";\nimport { SaveViewDialog } from \"./list/SaveViewDialog\";\nimport { ListHeader } from \"./list/ListHeader\";\nimport { ListBulkBar } from \"./list/ListBulkBar\";\nimport { DataCell } from \"./list/DataCell\";\nimport { buildListColumns } from \"./list/columns\";\nimport { ColumnPicker } from \"@/components/columns/ColumnPicker\";\nimport { ViewSwitcher } from \"@/components/views/ViewSwitcher\";\nimport { useViews } from \"@/hooks/useViews\";\nimport {\n FileDraftScope,\n buildFilesPayload,\n collectCommittedFileIds,\n getSchemaFieldValue,\n resolveSchemaFieldLabel,\n serializeSchemaFieldValue,\n useFileDraftScope,\n} from \"@/components/file-widgets\";\n\ninterface SmartListPageProps {\n screenCode: string;\n}\n\n/**\n * Default quick-peek field selection when YAML doesn't specify.\n * All simple scalars in declared order, capped at 8. Excludes primary keys,\n * auto fields, relations, json/structure/array, and non-listable fields.\n */\nconst PEEK_SCALAR_TYPES = new Set([\n \"string\", \"text\", \"email\", \"url\", \"phone\",\n \"int\", \"integer\", \"bigint\", \"smallint\",\n \"float\", \"double\", \"decimal\", \"numeric\", \"number\", \"money\", \"percent\",\n \"bool\", \"boolean\",\n \"date\", \"datetime\", \"timestamp\", \"timestamptz\",\n \"enum\", \"status\",\n]);\n\nfunction defaultPeekFieldNames(entity: Pick<Entity, \"fields\">): string[] {\n const picked: string[] = [];\n for (const [code, f] of entity.fields) {\n if (f.primary || f.auto) continue;\n if (f.ref) continue;\n if (f.array || f.structure) continue;\n if (f.display?.is_listable === false) continue;\n const t = (f.type || \"\").toLowerCase();\n const isEnum = Boolean(f.enum || f.values);\n if (!PEEK_SCALAR_TYPES.has(t) && !isEnum) continue;\n picked.push(code);\n if (picked.length >= 8) break;\n }\n return picked;\n}\n\ntype RowData = Record<string, unknown>;\n\n/**\n * SortHeaderMenu renders the chevron-triggered dropdown next to a sortable\n * column header. The menu surfaces all 4 (dir, nulls) combinations plus\n * \"Add to multi-sort\", \"Clear sort on this column\", and \"Clear all sorts\".\n * The currently active option is marked with a check icon.\n *\n * The chevron MUST stop click propagation so it doesn't trigger the\n * header cell's own toggle-sort handler.\n *\n * Multi-sort behavior (plan §7.2):\n * - \"Add to multi-sort\" appends this column to the sort tail with the\n * chosen (dir, nulls). Hidden when the column is ALREADY in the sort\n * (no-op) or when the sort already has {@link MAX_SORT_COLS} cols.\n * - \"Clear sort on this column\" splices this column out of the multi-\n * sort list; the rest of the sort is preserved.\n */\nfunction SortHeaderMenu({\n field,\n activeDir,\n activeNulls,\n inMultiSort,\n canAppend,\n onApply,\n onAppend,\n onClearColumn,\n onClearAll,\n}: {\n field: string;\n activeDir: \"asc\" | \"desc\" | false;\n activeNulls: SortNulls | undefined;\n /** True if this column is already part of the current sort list. */\n inMultiSort: boolean;\n /** True if the sort has room to add another column (length < cap). */\n canAppend: boolean;\n onApply: (dir: \"asc\" | \"desc\", nulls: SortNulls | undefined) => void;\n onAppend: (dir: \"asc\" | \"desc\", nulls: SortNulls | undefined) => void;\n onClearColumn: () => void;\n onClearAll: () => void;\n}) {\n const btnRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(false);\n const options: Array<{\n label: string;\n dir: \"asc\" | \"desc\";\n nulls: SortNulls | undefined;\n icon: typeof Icon.arrowUp;\n }> = [\n { label: \"Sort ascending, empty last\", dir: \"asc\", nulls: \"last\", icon: Icon.arrowUp },\n { label: \"Sort ascending, empty first\", dir: \"asc\", nulls: \"first\", icon: Icon.arrowUp },\n { label: \"Sort descending, empty last\", dir: \"desc\", nulls: \"last\", icon: Icon.arrowDown },\n { label: \"Sort descending, empty first\", dir: \"desc\", nulls: \"first\", icon: Icon.arrowDown },\n ];\n // \"Effective\" nulls for highlight matching: when no override is set on\n // the URL, the system default is NULLS LAST (plan §3). The menu shows\n // a check on that row so the user sees the live state, not a blank.\n const effectiveNulls: SortNulls = activeNulls ?? \"last\";\n // Hide \"Add to multi-sort\" when it would be a no-op (column already in\n // the sort) or when the server-enforced cap is reached.\n const showAppendItem = !inMultiSort && canAppend;\n return (\n <>\n <button\n ref={btnRef}\n type=\"button\"\n aria-label={`Sort options for ${field}`}\n title=\"Sort options. Shift-click the header to add this column to multi-sort.\"\n onClick={(e) => {\n e.stopPropagation();\n setOpen((v) => !v);\n }}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 16,\n height: 16,\n padding: 0,\n background: \"transparent\",\n border: \"none\",\n color: \"var(--text-3)\",\n cursor: \"pointer\",\n opacity: open ? 1 : 0.55,\n }}\n >\n <Icon.cheveron size={12} />\n </button>\n <Dropdown\n open={open}\n anchor={btnRef.current}\n onClose={() => setOpen(false)}\n align=\"start\"\n width={240}\n >\n {options.map((opt) => {\n const active = activeDir === opt.dir && effectiveNulls === opt.nulls;\n const IconC = opt.icon;\n return (\n <MenuItem\n key={`${opt.dir}:${opt.nulls}`}\n icon={\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 2 }}>\n <IconC size={11} />\n {active ? <Icon.check size={11} /> : null}\n </span>\n }\n onClick={() => {\n setOpen(false);\n onApply(opt.dir, opt.nulls);\n }}\n >\n {opt.label}\n </MenuItem>\n );\n })}\n {showAppendItem ? (\n <>\n <MenuItem divider />\n <MenuItem\n icon={<Icon.plus size={11} />}\n onClick={() => {\n setOpen(false);\n // Append with the column's effective NULLs (system default\n // unless the user has set an override on this column). Dir\n // defaults to asc - matches single-column first-click cycle.\n onAppend(\"asc\", activeNulls);\n }}\n >\n Add to multi-sort (Shift-click shortcut)\n </MenuItem>\n </>\n ) : null}\n <MenuItem divider />\n <MenuItem\n disabled={activeDir === false}\n onClick={() => {\n setOpen(false);\n if (activeDir !== false) onClearColumn();\n }}\n >\n Clear sort on this column\n </MenuItem>\n <MenuItem divider />\n <MenuItem\n disabled={activeDir === false}\n onClick={() => {\n setOpen(false);\n onClearAll();\n }}\n >\n Clear all sorts\n </MenuItem>\n </Dropdown>\n </>\n );\n}\n\nconst TABLE_STALE_LOADING_MS = 10_000;\n\n// Pagination knobs. Server hard cap is 1000 (data_store.go,\n// data_clickhouse.go); higher would be clamped server-side. Default 200\n// trades a slightly heavier first render for far fewer page clicks on\n// the admin and analytics surfaces. Options are exposed in the rows-\n// per-page dropdown at the table footer.\nexport const PER_PAGE_MAX = 1000;\nexport const PER_PAGE_DEFAULT = 200;\nexport const PER_PAGE_OPTIONS = [25, 50, 100, 200, 500, 1000] as const;\n\nfunction useStaleLoading(active: boolean, resetKey: string): boolean {\n const [stale, setStale] = useState(false);\n\n useEffect(() => {\n if (!active) {\n setStale(false);\n return;\n }\n\n setStale(false);\n const timer = window.setTimeout(() => setStale(true), TABLE_STALE_LOADING_MS);\n return () => window.clearTimeout(timer);\n }, [active, resetKey]);\n\n return stale;\n}\n\nfunction listLoadErrorDescription(error: unknown): string {\n if (error instanceof ApiError) {\n return `${error.code}: ${error.message}`;\n }\n if (error instanceof Error && error.message) {\n return error.message;\n }\n return \"Something went wrong. Please try again.\";\n}\n\n// UNSORTABLE_FIELD_TYPES mirrors data_cursor.go parseSortParam's reject list\n// (secret, password, all *_array, json). Header clicks on these would produce\n// a `?sort=<field>:asc` URL the backend's strict parser rejects with HTTP 400\n// per plan §14 - we must not let the UI generate that state.\nconst UNSORTABLE_FIELD_TYPES = new Set<string>([\n \"secret\",\n \"password\",\n \"json\",\n \"string_array\",\n \"int_array\",\n \"uuid_array\",\n]);\n\n// isHeaderSortable matches the backend's sortability contract so the cycle\n// handler never produces an invalid sort URL. Codex P2R1 H2 found that the\n// previous check only excluded action / select / $property.* and let users\n// click into a hard 400 on $refs.*, JSON columns, etc.\nfunction isHeaderSortable(headerId: string, entity: Entity | undefined): boolean {\n if (headerId === \"_actions\" || headerId === \"_select\") return false;\n // $property.* sort is deferred (plan §2). $refs.* is virtual join expansion\n // data, never a sortable column on the entity table.\n if (headerId.startsWith(\"$property.\") || headerId.startsWith(\"$refs.\")) {\n return false;\n }\n // $status.* sort IS supported (joined to object_statuses with VALUES-mapped\n // YAML position per plan §8.1) so it stays clickable.\n if (headerId.startsWith(\"$status.\")) return true;\n // Base-table field: must exist on the entity and not be a type the backend\n // refuses to sort.\n const field = entity?.fields.get(headerId);\n if (!field) return false;\n return !UNSORTABLE_FIELD_TYPES.has(field.type);\n}\n\nfunction baseViewSearchParams(current: URLSearchParams): URLSearchParams {\n const next = new URLSearchParams();\n const search = current.get(\"search\");\n if (search) next.set(\"search\", search);\n for (const key of EMBED_PARAM_KEYS) {\n const value = current.get(key);\n if (value !== null) next.set(key, value);\n }\n return next;\n}\n\nexport function rowSelectionKey(entity: Entity, row: RowData): string {\n return pkValuesToDetailId(entity, extractPkValues(entity, row));\n}\n\nexport function selectedRowPkValues(\n entity: Entity | undefined,\n rows: RowData[],\n selectedKeys: string[],\n): PkValues[] {\n if (!entity || selectedKeys.length === 0) return [];\n const selected = new Set(selectedKeys);\n return rows\n .filter((row) => selected.has(rowSelectionKey(entity, row)))\n .map((row) => extractPkValues(entity, row));\n}\n\n/**\n * Resolve the target href for a row click on a list screen per the\n * entity-canonical DSL. `list.detail_screen` is tri-valued:\n *\n * absent -> inherit entity.detail_screen\n * DETAIL_SCREEN_NONE (\"none\") -> explicit opt-out (disabled)\n * any other code -> override with that detail screen\n *\n * Resolution:\n * if list.detail_screen === \"none\" -> null (disabled)\n * elif list.detail_screen is set -> use screens[list.detail_screen]\n * elif entity.detail_screen is set -> use screens[entity.detail_screen]\n * else -> null (disabled)\n *\n * Returns null when no navigation target exists; callers MUST render\n * non-interactive rows in that case (no cursor, no click handler).\n */\nexport function resolveDetailHref(\n list: { detail_screen?: string },\n id: string,\n entity: { detail_screen?: string } | undefined,\n allScreens: Record<string, { route?: string }> | undefined,\n): string | null {\n return buildDetailHrefFromId(list, id, entity, allScreens);\n}\n\nexport function SmartListPage({ screenCode }: SmartListPageProps) {\n return (\n <FileDraftScope>\n <SmartListPageInner screenCode={screenCode} />\n </FileDraftScope>\n );\n}\n\nfunction SmartListPageInner({ screenCode }: SmartListPageProps) {\n const tabNavigate = useTabNavigate();\n const queryClient = useQueryClient();\n const [searchParams, setSearchParams] = useSearchParams();\n const { data: schema } = useSchema();\n // Plan §3 contract: every direct caller of resolveCellRenderer / renderField\n // MUST pass the user-resolved locale. Hot-path cell factories (time_ago,\n // multilang_inline, money) read it from CellResolveCtx.locale.\n const userLocale = useUserLocale();\n const [deleteState, setDeleteState] = useState<{ ids: string[]; confirmation?: string } | null>(null);\n\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({});\n const [filterDrawerOpen, setFilterDrawerOpen] = useState(false);\n const [saveViewOpen, setSaveViewOpen] = useState(false);\n const [contextMenu, setContextMenu] = useState<{ x: number; y: number; row: RowData } | null>(null);\n const { filters: jsonFilters, activeCount: filterCount, setFilters: setJsonFilters, clearFilters } = useFilters();\n const user = useAuthStore((s) => s.user);\n const activeTenant = useAuthStore((s) => s.activeTenant);\n const allViews = useViews(screenCode, user?.id);\n const fileDraftScope = useFileDraftScope();\n\n // Plan §8.2: if a user has marked a personal view as default for this\n // screen and the URL carries no explicit ?view= selection, apply that\n // view on initial mount. We do NOT auto-apply for system or shared views\n // and we never re-apply after the user explicitly cleared the view this\n // session - that intent is persisted in sessionStorage keyed by screen\n // code so subsequent navigations within the session respect the clear.\n const defaultViewSessionKey = `dcl:smartlist:default-view-cleared:${screenCode}`;\n const defaultViewApplied = useRef(false);\n useEffect(() => {\n if (defaultViewApplied.current) return;\n if (!allViews || allViews.length === 0) return;\n if (searchParams.get(\"view\")) {\n // User already arrived with an explicit selection (URL bookmark,\n // view-switcher click, link from elsewhere). Respect it; never\n // override.\n defaultViewApplied.current = true;\n return;\n }\n if (typeof window !== \"undefined\" && window.sessionStorage?.getItem(defaultViewSessionKey)) {\n // User explicitly cleared the view this session. Don't fight them.\n defaultViewApplied.current = true;\n return;\n }\n const def = allViews.find((v) => v.source === \"personal\" && v.is_default);\n if (!def || !def.id) return;\n defaultViewApplied.current = true;\n // Strip view-owned params (filters / columns / sort) before overlaying\n // the default view's own values - identical to the user-explicit-select\n // path. Without this, a stale `?filters=...` bookmark survives and the\n // user sees a filtered list that doesn't belong to the view they\n // auto-loaded (Codex P2R1 H4). baseViewSearchParams preserves only the\n // truly orthogonal keys (search, embed) per its own contract.\n const next = baseViewSearchParams(searchParams);\n next.set(\"view\", def.id);\n if (def.filters && Array.isArray(def.filters) && def.filters.length > 0) {\n next.set(\"filters\", JSON.stringify(def.filters));\n }\n if (def.columns && def.columns.length > 0) {\n next.set(\"columns\", def.columns.join(\",\"));\n }\n if (def.sort) {\n next.set(\"sort\", def.sort);\n }\n // replace:true so Back doesn't bounce to the pre-applied bare URL.\n setSearchParams(next, { replace: true });\n }, [allViews, searchParams, setSearchParams, defaultViewSessionKey]);\n\n const screen = schema?.screens[screenCode];\n const entity = screen?.entity ? schema?.entities[screen.entity] : undefined;\n const entityCode = screen?.entity;\n const canCreate = entityAllowsMutation(entity, \"create\");\n const canUpdate = entityAllowsMutation(entity, \"update\");\n const canDelete = entityAllowsMutation(entity, \"delete\");\n\n // Actions: use screen-bound actions when defined, otherwise fall back to visibility.\n const globalActions = schema?.actions;\n const toolbarActions = useMemo(\n () => getScreenActions(entity?.actions, screen?.actions, \"list_toolbar\", globalActions),\n [entity?.actions, screen?.actions, globalActions],\n );\n // Bulk pill slice: actions visible in `list_floatbar`. The bulk pill is the\n // selection-driven counterpart of the static toolbar; mixing the two slices\n // (which we did pre-fix) hid __delete and surfaced __create. Reason: __delete\n // is list_floatbar/list_row/detail and __create is list_toolbar - distinct\n // slots with distinct action sets.\n const floatbarActions = useMemo(\n () => getScreenActions(entity?.actions, undefined, \"list_floatbar\", globalActions),\n [entity?.actions, globalActions],\n );\n const rowActions = useMemo(\n () => getScreenActions(entity?.actions, screen?.row_actions, \"list_row\", globalActions),\n [entity?.actions, screen?.row_actions, globalActions],\n );\n // When the screen explicitly binds row_actions, it controls the full row menu.\n const hasExplicitRowActions = screen?.row_actions !== undefined;\n // Checkboxes appear when rows can be acted on as a selection: either a\n // bulk-pill action exists (list_floatbar) or standard export can export\n // selected rows. Reason: list_toolbar is the static slot; selection-driven\n // actions live in list_floatbar (e.g. __delete platform default).\n const hasBatchActions = useMemo(\n () => floatbarActions.length > 0 || (!(entity?.export?.enabled === false) && !(screen?.export?.enabled === false)),\n [floatbarActions, entity?.export?.enabled, screen?.export?.enabled],\n );\n // Soft-delete visibility toggle (admin-only).\n const hasSoftDelete = !!entity?.soft_delete;\n const canViewDeleted = hasSoftDelete && (user?.permissions ?? []).some((p) => p === \"feature:view_deleted\" || p === \"feature:*\" || p === \"*\");\n const includeDeleted = canViewDeleted && searchParams.get(\"include_deleted\") === \"true\";\n\n // Ensure this page is tracked as a tab (preserves current search params).\n // Embed mode has no tab UI (plan Decision 15): the tab store is persisted\n // per tenant+user, so writing to it from an embedded screen would leak\n // spurious tabs into the user's normal Declarion session. Skip it.\n const embed = useEmbed();\n const { openTab, updateTabSearch, findTabByPathname } = useTabStore();\n useEffect(() => {\n if (screen?.route && entity && !embed.enabled) {\n const tabName = loc(screen.display?.name) || loc(entity.display.name_plural);\n const tabIcon = screen.display?.icon || entity.display.icon;\n openTab({\n pathname: screen.route,\n search: searchParams.toString(),\n title: tabName,\n icon: tabIcon,\n closable: true,\n });\n }\n }, [screen?.route, screen?.display?.name?.en, entity?.display.name_plural?.en, screen?.display?.icon, entity?.display.icon, embed.enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Sync URL search params back to tab store so switching away and back restores the URL.\n useEffect(() => {\n if (!screen?.route || embed.enabled) return;\n const tab = findTabByPathname(screen.route);\n if (tab) {\n updateTabSearch(tab.id, searchParams.toString());\n }\n }, [searchParams, screen?.route, embed.enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // URL state\n const page = parseInt(searchParams.get(\"page\") ?? \"1\", 10);\n // per_page URL param drives both the list query and the current_page\n // export window. Defaults to PER_PAGE_DEFAULT when absent or out-of-\n // range. The 1000 ceiling matches DataStore.List's clamp (data_store.go,\n // data_clickhouse.go) so the export always mirrors what the user sees\n // on screen. Reason for the 200 default vs the legacy 50: the platform\n // serves admins + ML/analytics teams who scan large data; 50 forced\n // them through many page clicks. 200 quadruples per-request density\n // without blowing past a ~100ms client render (200 rows * ~2KB JSON =\n // ~400KB payload). Power users pick 500/1000 explicitly via the\n // dropdown.\n const perPage = (() => {\n const raw = parseInt(searchParams.get(\"per_page\") ?? \"\", 10);\n if (!Number.isFinite(raw) || raw <= 0 || raw > PER_PAGE_MAX) return PER_PAGE_DEFAULT;\n return raw;\n })();\n const searchValue = searchParams.get(\"search\") ?? \"\";\n const sortParam = searchParams.get(\"sort\") ?? screen?.sort_default ?? \"\";\n // sortCols / sortNullsMap are derived from sortParam early because the\n // request payload (listParams below) and the saved-view persistence path\n // both need the parser-filtered form, not the raw URL string. parseSort\n // drops $property.* segments (deferred per plan §2) so a stale bookmark\n // never reaches the backend's strict parser as an HTTP 400.\n const sortCols = useMemo(() => parseSort(sortParam), [sortParam]);\n const sortNullsMap = useMemo(() => {\n const m = new Map<string, SortNulls>();\n for (const c of sortCols) if (c.nulls) m.set(c.field, c.nulls);\n return m;\n }, [sortCols]);\n // sortForRequest preserves the raw URL grammar verbatim (so malformed\n // input still triggers the backend's strict-parse 400 per plan §14) and\n // only filters the documented $property.* deferral via the surgical helper.\n // serialize(parseSort(...)) would silently rewrite unknown nulls literals\n // and extra segments, masking grammar violations - that's the bug Codex\n // P2R1 finding H1 caught.\n const sortForRequest = useMemo(() => stripUnsupportedSortSegments(sortParam), [sortParam]);\n const debouncedSearch = useDebounce(searchValue, 300);\n\n // Visible columns from URL (or screen defaults).\n const visibleCols = useMemo<string[]>(() => {\n const raw = searchParams.get(\"columns\");\n if (raw) return raw.split(\",\").filter(Boolean);\n return screen?.default_columns ?? screen?.columns ?? [];\n }, [searchParams, screen?.default_columns, screen?.columns]);\n\n // Detect which expand params are needed based on VISIBLE column prefixes.\n // When any expand is requested, $refs must be explicitly included too\n // (the backend disables auto-refs when expand mode is active).\n const expand = useMemo(() => {\n const e: string[] = [];\n // Check both the screen pool and visible columns for $status/$property.\n const allCols = [...(screen?.columns ?? []), ...visibleCols];\n if (allCols.some((c) => c.startsWith(\"$status.\"))) e.push(\"statuses\");\n if (allCols.some((c) => c.startsWith(\"$property.\"))) e.push(\"properties\");\n if (e.length > 0) e.unshift(\"refs\");\n return e.length > 0 ? e : undefined;\n }, [screen?.columns, visibleCols]);\n\n // Narrow the server-side column fetch to exactly what the UI needs. The\n // backend auto-adds PK fields AND every sort col so a narrowed select can\n // never break cursor pagination (data_list.go does the merge), which means\n // we can pass only visible base-table columns here. Virtual columns\n // ($status.*, $property.*, $refs.*) are served by the `expand` param,\n // not by a direct SELECT on the entity table, so they are stripped.\n const select = useMemo<string[] | undefined>(() => {\n const real = visibleCols.filter((c) => c && !c.startsWith(\"$\"));\n return real.length > 0 ? real : undefined;\n }, [visibleCols]);\n\n // Data fetching - prefer JSON filters over legacy format.\n //\n // Decoupled count (docs/plans/2026-05-18-declarative-row-count.md): the\n // window query (rows for the current page) and the count query (the\n // total) are SEPARATE requests. The table renders rows the moment the\n // window resolves and never blocks on the count; the count fills in when\n // its own request - which may take seconds on a firehose table - resolves.\n const listParams = {\n page,\n per_page: perPage,\n sort: sortForRequest,\n search: debouncedSearch || undefined,\n filters: jsonFilters.length > 0 ? jsonFilters : undefined,\n include_deleted: includeDeleted || undefined,\n expand,\n select,\n preview: true,\n screen: screen?.fixed_filters ? screenCode : undefined,\n };\n const { data: response, isLoading, isFetching, isError, error, refetch } = useEntityData(entityCode, listParams);\n // Count query: page/sort-independent, resolves on its own clock.\n const { data: countResponse, isLoading: countLoading, isError: countError } = useEntityCount(entityCode, listParams);\n // The total + the strategy used to produce it (exact vs estimated).\n // Undefined while the count request is in flight or after it failed -\n // callers degrade to the loaded row count, never an error.\n const countTotal = countResponse?.meta?.total;\n const countStrategy = countResponse?.meta?.count_mode;\n const tableLoadingStale = useStaleLoading(isLoading, `${entityCode ?? \"\"}|${searchParams.toString()}`);\n const retryListLoad = useCallback(() => {\n if (entityCode) {\n void queryClient.cancelQueries({ queryKey: [\"entity-data\", entityCode] });\n }\n void refetch();\n }, [entityCode, queryClient, refetch]);\n\n // Realtime liveness. An entity auto-updates this list view only when its\n // schema `realtime` flag is true AND the CDC relay has not auto-degraded it\n // this session. When it is live, the toolbar surfaces a green \"Live\"\n // affordance; otherwise the manual refresh control is the only freshness\n // mechanism.\n const degradedEntities = useRealtimeStore((s) => s.degradedEntities);\n const isLive = isEntityLive(entity?.realtime, degradedEntities, entityCode ?? \"\");\n\n const deleteMutation = useDeleteEntity(entityCode ?? \"\");\n const updateMutation = useUpdateEntity(entityCode ?? \"\");\n const createViewMutation = useCreateEntity(\"view\");\n\n // Parse sort state from URL via the shared `sort.ts` helpers. TanStack\n // models only `{id, desc}` per column; the per-column NULLs placement\n // lives in a parallel Map keyed by column id so the header dropdown can\n // read/write it without losing the grammar's third segment on flip.\n // (sortCols and sortNullsMap are declared earlier alongside sortParam.)\n const sorting: SortingState = useMemo(\n () => sortCols.map((c) => ({ id: c.field, desc: c.dir === \"desc\" })),\n [sortCols],\n );\n\n // Primary key field name.\n const pkField = useMemo(\n () => entity?.fields.names().find((f) => entity.fields.get(f)?.primary) ?? \"id\",\n [entity],\n );\n const getSelectedRowKey = useCallback(\n (row: Record<string, unknown>) => entity ? rowSelectionKey(entity, row) : String(row[pkField] ?? \"\"),\n [entity, pkField],\n );\n\n const refs = response?.$refs;\n // Use a ref so the cell render function always reads the latest $refs\n // without adding it to the useMemo deps (which would re-create columns on every fetch).\n const refsRef = useRef(refs);\n refsRef.current = refs;\n\n // QuickPeek state declared early so columns useMemo can reference it.\n const [peekRow, setPeekRow] = useState<RowData | null>(null);\n const peekEnabled = screen?.on_row_click === \"peek\" && screen.quick_peek?.enabled !== false;\n\n // Build columns from visible columns (URL state or screen defaults).\n const columns = useMemo<ColumnDef<RowData>[]>(\n () =>\n buildListColumns({\n visibleCols,\n entity,\n screen,\n schema,\n entityCode,\n rowActions,\n hasBatchActions,\n hasExplicitRowActions,\n canUpdate,\n canDelete,\n peekEnabled,\n userLocale,\n tabNavigate,\n refsRef,\n rowSelectionKey,\n setPeekRow,\n }),\n [screen, entity, visibleCols, tabNavigate, hasBatchActions, rowActions, hasExplicitRowActions, entityCode, peekEnabled, schema, userLocale, canUpdate, canDelete, refsRef],\n );\n\n const data = useMemo(() => response?.data ?? [], [response?.data]);\n // Window meta: pagination shape (page, per_page, has_more). Carries NO\n // total - the count comes from the separate count query below.\n const meta = response?.meta;\n // Total pages, derived from the count query's total once it resolves.\n // Until then it is undefined and paging falls back to the window's\n // has_more probe (Next enabled, Prev gated on page > 1).\n const totalPages = useMemo(() => {\n if (countTotal === undefined || perPage <= 0) return undefined;\n return Math.max(1, Math.ceil(countTotal / perPage));\n }, [countTotal, perPage]);\n const hasMorePages = meta?.has_more ?? (totalPages !== undefined && page < totalPages);\n\n const table = useReactTable({\n data,\n columns,\n getCoreRowModel: getCoreRowModel(),\n manualSorting: true,\n manualPagination: true,\n state: { sorting, rowSelection },\n onSortingChange: (updater) => {\n const next = typeof updater === \"function\" ? updater(sorting) : updater;\n // Cap to the server-enforced max so a programmatic caller can't\n // produce a list the server will reject with HTTP 400. Shift-click\n // goes through a dedicated path (applyShiftClickSort) that enforces\n // the cap by refusal; this slice is defense-in-depth.\n const capped = next.slice(0, MAX_SORT_COLS);\n const params = new URLSearchParams(searchParams);\n if (capped.length === 0) {\n params.delete(\"sort\");\n } else {\n // Preserve every retained column's NULLs placement across dir\n // flips and re-orderings so the dropdown choice survives a\n // header click (multi-col safe).\n const cols: SortCol[] = capped.map((s) => {\n const nulls = sortNullsMap.get(s.id);\n return { field: s.id, dir: s.desc ? \"desc\" : \"asc\", ...(nulls ? { nulls } : {}) };\n });\n params.set(\"sort\", serializeSort(cols));\n }\n params.set(\"page\", \"1\");\n setSearchParams(params);\n },\n onRowSelectionChange: setRowSelection,\n getRowId: getSelectedRowKey,\n // rowCount comes from the count query; until it resolves, fall back to\n // the rows actually loaded so the table is never blocked on the count.\n rowCount: countTotal ?? data.length,\n });\n\n // Selected IDs for batch actions.\n const selectedIds = useMemo(\n () => Object.keys(rowSelection).filter((k) => rowSelection[k]),\n [rowSelection],\n );\n const selectedRowPKs = useMemo(\n () => selectedRowPkValues(entity, data, selectedIds),\n [entity, data, selectedIds],\n );\n\n // Centralized writer for the URL `sort` param. All header-driven sort\n // mutations route through here so URL encoding lives in one place.\n // Empty list deletes the param; cap is enforced as defense-in-depth.\n const writeSort = useCallback(\n (cols: SortCol[]) => {\n const capped = cols.slice(0, MAX_SORT_COLS);\n const params = new URLSearchParams(searchParams);\n if (capped.length === 0) {\n params.delete(\"sort\");\n } else {\n params.set(\"sort\", serializeSort(capped));\n }\n params.set(\"page\", \"1\");\n setSearchParams(params);\n },\n [searchParams, setSearchParams],\n );\n\n // Single-column REPLACE from the dropdown's 4 (dir, nulls) options.\n // Discards any pre-existing multi-sort - mirrors a plain header click.\n const applyColumnSort = useCallback(\n (field: string, dir: \"asc\" | \"desc\", nulls: SortNulls | undefined) => {\n writeSort([{ field, dir, ...(nulls ? { nulls } : {}) }]);\n },\n [writeSort],\n );\n\n // Shift-click on a header cell: toggle this column in/out of the multi-\n // sort tail. New cols default to dir=asc + system-default nulls. Existing\n // cols retain their NULL placement on other rows. Refuses past the cap.\n const shiftClickColumn = useCallback(\n (field: string) => {\n writeSort(applyShiftClickSort(sortCols, field));\n },\n [sortCols, writeSort],\n );\n\n // \"Add to multi-sort\" menu action: append `field` with chosen (dir, nulls)\n // to the multi-sort tail. Dedupes on `field` (server rejects duplicates).\n const appendColumnSort = useCallback(\n (field: string, dir: \"asc\" | \"desc\", nulls: SortNulls | undefined) => {\n writeSort(applyAppendSort(sortCols, { field, dir, ...(nulls ? { nulls } : {}) }));\n },\n [sortCols, writeSort],\n );\n\n // \"Clear sort on this column\" menu action: splice just this column out;\n // the rest of the multi-sort is preserved.\n const clearColumnSort = useCallback(\n (field: string) => {\n writeSort(removeSortField(sortCols, field));\n },\n [sortCols, writeSort],\n );\n\n const clearAllSorts = useCallback(() => {\n writeSort([]);\n }, [writeSort]);\n\n // Clear selection after data changes (page, search, sort, or filter change).\n useEffect(() => {\n setRowSelection({});\n }, [page, debouncedSearch, sortParam, jsonFilters]);\n\n // Keyboard shortcuts: / = focus search, f = open filter drawer, Escape = close drawer.\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Skip when typing in an input/textarea/select.\n const tag = (e.target as HTMLElement)?.tagName;\n if (tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\") {\n if (e.key === \"Escape\") {\n (e.target as HTMLElement).blur();\n setFilterDrawerOpen(false);\n }\n return;\n }\n if (e.key === \"/\" && !e.metaKey && !e.ctrlKey) {\n e.preventDefault();\n const input = document.querySelector<HTMLInputElement>('[placeholder*=\"Search\"]');\n input?.focus();\n }\n if (e.key === \"f\" && !e.metaKey && !e.ctrlKey) {\n e.preventDefault();\n setFilterDrawerOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") {\n setFilterDrawerOpen(false);\n }\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, []);\n\n // Virtual scroll\n const parentRef = useRef<HTMLDivElement>(null);\n const { rows } = table.getRowModel();\n\n const density = useAppearanceStore((s) => s.density);\n const zebraEnabled = useAppearanceStore((s) => s.zebra);\n\n const rowVirtualizer = useVirtualizer({\n // Embed mode renders every row in document flow (see the <tbody>\n // branch). The embed iframe auto-resizes to content height, so there\n // is no bounded viewport to virtualize against - a virtualized table\n // collapses to zero rows. `count: 0` makes the virtualizer inert there.\n count: embed.enabled ? 0 : rows.length,\n getScrollElement: () => parentRef.current,\n estimateSize: () => 48,\n overscan: 10,\n });\n\n // Live remeasure on density change so row heights update without scroll jump.\n // CSS rules at globals.css:157-158 swap --row-h via [data-density] on <html>;\n // TanStack Virtual caches measurements and needs an explicit invalidation.\n useEffect(() => {\n rowVirtualizer.measure();\n }, [density, rowVirtualizer]);\n\n // URL state helpers\n const updateSearch = useCallback(\n (value: string) => {\n setSearchParams((prev) => {\n const next = new URLSearchParams(prev);\n if (value) {\n next.set(\"search\", value);\n } else {\n next.delete(\"search\");\n }\n next.set(\"page\", \"1\");\n return next;\n });\n },\n [setSearchParams],\n );\n\n const toggleIncludeDeleted = useCallback(() => {\n setSearchParams((prev) => {\n const next = new URLSearchParams(prev);\n if (next.get(\"include_deleted\")) {\n next.delete(\"include_deleted\");\n } else {\n next.set(\"include_deleted\", \"true\");\n }\n next.set(\"page\", \"1\");\n return next;\n });\n }, [setSearchParams]);\n\n const goToPage = useCallback(\n (p: number) => {\n setSearchParams((prev) => {\n const next = new URLSearchParams(prev);\n next.set(\"page\", String(p));\n return next;\n });\n },\n [setSearchParams],\n );\n\n // Clamp an out-of-range page. A deep link like ?page=2 on a list that\n // only fills one page (or a stale link after rows were deleted) would\n // otherwise render the \"No <entity> yet\" empty state - which both lies\n // (the list is not empty) and hides the pagination footer, stranding the\n // user with no control to get back. Once the count resolves, snap to the\n // last real page.\n useEffect(() => {\n if (totalPages !== undefined && page > totalPages) {\n goToPage(totalPages);\n }\n }, [totalPages, page, goToPage]);\n\n // Rows-per-page change. Reason for reset-to-page-1: a user on page 8 of\n // 10 (at 50/page) who switches to 200/page would land at page 8 of a\n // shorter pagination and most likely see \"no results\" because the\n // backend total_pages collapsed; reset is the user-friendly default.\n const setPerPage = useCallback(\n (n: number) => {\n setSearchParams((prev) => {\n const next = new URLSearchParams(prev);\n next.set(\"per_page\", String(n));\n next.set(\"page\", \"1\");\n return next;\n });\n },\n [setSearchParams],\n );\n\n const hasRowDetailTarget = useMemo(() => {\n if (!screen || !entity) return false;\n return resolveDetailHref(screen, \"_\", entity, schema?.screens) !== null;\n }, [screen, entity, schema?.screens]);\n\n // QuickPeek state — Plan T17. When `screen.on_row_click === \"peek\"` a\n // row click opens the right drawer instead of navigating; the drawer's\n // Open-in-tab promotes to a tab. (Declarations moved earlier; used here.)\n const navigateToDetail = useCallback(\n (row: RowData, options?: { draft?: RowData | null }) => {\n if (!entity || !screen) return;\n const pkValues = extractPkValues(entity, row);\n const id = pkValuesToDetailId(entity, pkValues);\n let href = buildEntityDetailHref(screen, entity, schema?.screens, pkValues);\n if (!href) return;\n // Dirty-promote: stash the peek draft so SmartDetailPage can seed\n // formData with the user's uncommitted edits, and set ?edit=true so\n // the detail page opens in edit mode. Parse the existing query so\n // we never emit duplicate `edit` params if href already has one.\n if (options?.draft) {\n setPeekDraft(id, options.draft as Record<string, unknown>);\n const [path, queryStr] = href.split(\"?\", 2);\n const params = new URLSearchParams(queryStr ?? \"\");\n params.set(\"edit\", \"true\");\n href = `${path}?${params.toString()}`;\n }\n // Tab title = the entity's canonical record label (display.field /\n // display.template-rendered $display). The previous heuristic used\n // the first table column's raw value, which on lists whose first\n // column is a timestamp (e.g. Audit Trail, ordered by created_at)\n // produced a raw ISO string as the tab title. getDisplayValue is the\n // platform's single label resolver - use it.\n const tabTitle = getDisplayValue(row, entity) ?? loc(entity.display.name);\n const icon = screen.display?.icon || entity.display.icon;\n tabNavigate(href, { title: tabTitle, icon });\n },\n [entity, screen, schema, tabNavigate],\n );\n\n const handleRowClick = useCallback(\n (e: React.MouseEvent, row: RowData) => {\n // Ignore clicks that originated on an interactive child - a ref-link\n // cell, the row kebab, the select checkbox. Those own their own\n // navigation/action; the row-level handler must not also fire, or\n // a single click on a user_id ref-link both navigates to the user\n // AND opens a stray tab for the row's own entity. A target-closest\n // guard is more reliable than relying on every child to\n // stopPropagation (synthetic-event propagation across the\n // TanStack-virtualized row + cell-factory subtree proved leaky).\n if ((e.target as HTMLElement).closest(\"a, button, input, select, [role='button'], [role='menuitem']\")) {\n return;\n }\n if (peekEnabled) {\n setPeekRow(row);\n return;\n }\n navigateToDetail(row);\n },\n [peekEnabled, navigateToDetail],\n );\n\n // QuickPeek field projection: emit a structured spec per field. The peek\n // renders each via PeekFieldCell (commit-gated) — no per-keystroke writes.\n const peekFields = useMemo<QuickPeekFieldSpec[]>(() => {\n if (!peekEnabled || !entity || !peekRow) return [];\n const explicit = screen?.quick_peek?.fields;\n const sections = screen?.quick_peek?.sections;\n let fieldNames: string[];\n if (explicit && explicit.length > 0) {\n fieldNames = explicit.filter((c) => entity.fields.get(c) || entity.files?.get(c));\n } else if (sections && sections.length > 0) {\n // Resolve section names against the matching detail screen's layout.\n const detailScreen = Object.values(schema?.screens ?? {}).find(\n (s) => s.type === \"detail\" && s.entity === screen?.entity,\n );\n const layoutItems = detailScreen?.items ?? [];\n const collected: string[] = [];\n const walk = (items: unknown): void => {\n if (!Array.isArray(items)) return;\n for (const node of items) {\n if (node && typeof node === \"object\" && \"section\" in node) {\n const sec = (node as { section?: { name?: unknown; items?: unknown } }).section;\n const secName = typeof sec?.name === \"string\"\n ? sec.name\n : sec?.name && typeof sec.name === \"object\"\n ? Object.values(sec.name as Record<string, string>)[0]\n : \"\";\n if (sections.includes(String(secName))) {\n walk(sec?.items);\n } else {\n walk(sec?.items);\n }\n } else if (node && typeof node === \"object\" && \"fields\" in node) {\n const fs = (node as { fields?: unknown }).fields;\n if (Array.isArray(fs)) for (const f of fs) if (typeof f === \"string\") collected.push(f);\n }\n }\n };\n walk(layoutItems);\n fieldNames = collected.filter((c) => entity.fields.get(c) || entity.files?.get(c));\n if (fieldNames.length === 0) {\n fieldNames = defaultPeekFieldNames(entity);\n }\n } else {\n fieldNames = defaultPeekFieldNames(entity);\n }\n return fieldNames.map((name) => {\n const fileField = entity.files?.get(name);\n if (fileField) {\n return {\n key: name,\n label: resolveSchemaFieldLabel(entity, name, schema?.entities),\n kind: \"file\",\n readOnly: !canUpdate,\n } satisfies QuickPeekFieldSpec;\n }\n const f = entity.fields.get(name)!;\n const editable = canUpdate && !f.primary && !f.auto && !f.ref && f.files?.mode !== \"rich_text_inline\";\n return {\n key: name,\n label: fieldDisplayName(f, name, schema?.entities),\n kind: \"field\",\n field: f,\n readOnly: !editable,\n } satisfies QuickPeekFieldSpec;\n });\n }, [peekEnabled, peekRow, entity, screen, schema?.entities, schema?.screens, canUpdate]);\n\n const peekActions = useMemo<Record<string, Action>>(() => {\n if (!entity?.actions) return {};\n return entity.actions;\n }, [entity?.actions]);\n\n const peekExecute = useCallback(\n async (code: string, action: Action, params: Record<string, unknown>) => {\n const pk = entity?.fields.names().find((f) => entity.fields.get(f)?.primary);\n const ids = pk && peekRow ? [String(peekRow[pk] ?? \"\")] : undefined;\n const fullCode = action.entity ? `${action.entity}.${code}` : code;\n return runActionApi(fullCode, buildActionBody(params, ids));\n },\n [entity, peekRow],\n );\n\n const handleDelete = useCallback(async () => {\n if (!deleteState || deleteState.ids.length === 0) return;\n setDeleteState(null);\n const results = await Promise.allSettled(\n deleteState.ids.map((id) => deleteMutation.mutateAsync(id)),\n );\n const failed = results.filter((r) => r.status === \"rejected\").length;\n if (failed === 0) {\n setRowSelection({});\n } else if (failed < deleteState.ids.length) {\n // Remove successfully deleted IDs from selection to prevent stale retries.\n const succeededIds = new Set(\n deleteState.ids.filter((_, i) => results[i].status === \"fulfilled\"),\n );\n setRowSelection((prev) => {\n const next: RowSelectionState = {};\n for (const [key, val] of Object.entries(prev)) {\n if (!succeededIds.has(key)) next[key] = val;\n }\n return next;\n });\n toast.warning(`${failed} of ${deleteState.ids.length} deletes failed`);\n }\n }, [deleteState, deleteMutation]);\n\n if (!screen || !entity) return null;\n\n const entityDisplay = entity.display;\n // Screen display name takes priority over entity display name (supports multiple screens per entity).\n const screenName = loc(screen.display?.name) || loc(entityDisplay.name);\n const screenIcon = screen.display?.icon || entityDisplay.icon;\n\n // Dispatch context handed to ResponsiveActionBar at every mount site\n // (toolbar, row kebab, bulk pill). Built once from page-level locals so\n // `href`-mode actions reach `tabNavigate` and Phase 2 widget mounts get\n // the same shape regardless of where the action click originates.\n // Reason: keeps the bar usable without coupling it to SmartListPage\n // internals (plan §8 Task 10).\n const selectedIdStrings = selectedRowPKs.map((pk) => pkValuesToDetailId(entity, pk));\n // Resolve selected rows by key. selectedIds.map(data.find(...)) is\n // O(rows x selection) - quadratic on large tables. Index into a Map\n // instead, but only when there is a selection: the no-selection case\n // (the common one) must stay O(0), not pay an O(rows) Map build.\n let selectedRowObjects: Record<string, unknown>[] = [];\n if (selectedIds.length > 0) {\n const rowByKey = new Map<string, Record<string, unknown>>();\n for (const r of data) rowByKey.set(getSelectedRowKey(r), r);\n selectedRowObjects = selectedIds\n .map((id) => rowByKey.get(id))\n .filter((r): r is Record<string, unknown> => r !== undefined);\n }\n const widgetContext: WidgetContext = {\n entity,\n entityCode: entityCode ?? \"\",\n screen,\n screenRoute: screen.route,\n selectedRows: selectedRowObjects,\n selectedIds: selectedIdStrings,\n locale: userLocale,\n tabNavigate: (url, meta) => tabNavigate(url, { title: meta?.title, icon: meta?.icon, search: meta?.search }),\n runActionApi: (code, params) => runActionApi(code, params as Parameters<typeof runActionApi>[1]),\n };\n\n // One data row. Shared by the virtualized list (default) and the\n // document-flow list used in embed mode. `virtualized` adds the measure\n // ref + data-index that TanStack Virtual needs; the embed path omits\n // both because it renders every row.\n const renderDataRow = (row: (typeof rows)[number], rowIndex: number, virtualized: boolean) => {\n const isDeleted = hasSoftDelete && row.original[entity!.soft_delete!] != null;\n const checked = row.getIsSelected();\n const zebra = zebraEnabled && rowIndex % 2 === 1;\n const baseBg = checked\n ? \"var(--row-selected-bg)\"\n : zebra\n ? \"var(--surface-2)\"\n : \"transparent\";\n return (\n <tr\n key={row.id}\n data-index={virtualized ? rowIndex : undefined}\n ref={virtualized ? (node) => rowVirtualizer.measureElement(node) : undefined}\n style={{\n height: \"var(--row-h)\",\n // Pointer when click does anything — either navigate to detail\n // (`hasRowDetailTarget`) OR open the peek drawer (`peekEnabled`).\n // Composite-PK entities have no detail route, so without the peek\n // branch their rows looked dead.\n cursor: hasRowDetailTarget || peekEnabled ? \"pointer\" : \"default\",\n opacity: isDeleted ? 0.5 : 1,\n fontStyle: isDeleted ? \"italic\" : \"normal\",\n background: baseBg,\n boxShadow: checked ? \"inset 2px 0 0 var(--accent)\" : \"none\",\n transition: \"background-color 120ms\",\n }}\n onMouseEnter={(e) => {\n if (!checked) e.currentTarget.style.background = \"var(--surface-3)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.background = baseBg;\n }}\n onClick={\n hasRowDetailTarget || peekEnabled ? (e) => handleRowClick(e, row.original) : undefined\n }\n onDoubleClick={\n hasRowDetailTarget\n ? () => {\n // Double-click always goes to detail, bypassing the peek.\n setPeekRow(null);\n navigateToDetail(row.original);\n }\n : undefined\n }\n onContextMenu={(e) => {\n e.preventDefault();\n setContextMenu({ x: e.clientX, y: e.clientY, row: row.original });\n }}\n >\n {row.getVisibleCells().map((cell) => (\n <DataCell key={cell.id} cell={cell} isRowSelected={row.getIsSelected()} />\n ))}\n </tr>\n );\n };\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n // Embed mode flows in document height so the auto-resizing iframe\n // can size itself; the standalone app fills its 100vh viewport.\n height: embed.enabled ? \"auto\" : \"100%\",\n background: \"var(--bg)\",\n }}\n >\n <>\n <ListHeader\n title={screenName}\n total={countTotal}\n countMode={countStrategy}\n countPending={countLoading && !countError}\n loaded={data.length}\n toolbarActions={toolbarActions}\n screenActionLayout={screen.action_layout as ResolvedActionLayoutNode[] | undefined}\n entityActionLayout={entity.action_layout as ResolvedActionLayoutNode[] | undefined}\n selectedIds={selectedIds}\n data={data}\n rowKey={getSelectedRowKey}\n onActionSuccess={() => setRowSelection({})}\n widgetContext={widgetContext}\n />\n\n {/* Toolbar: view switcher + search + filter + columns + density + soft-delete + export */}\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, padding: \"0 20px 10px\", flexWrap: \"wrap\" }}>\n <ViewSwitcher\n views={allViews}\n activeViewId={searchParams.get(\"view\")}\n isDirty={false}\n onSelect={(view) => {\n if (!view.id) {\n // Explicit clear from the view-switcher. Remember the intent\n // for the rest of this browser session so the auto-apply\n // effect (plan §8.2) doesn't re-apply the user's default\n // view mid-session. Cleared on a fresh session/tab.\n if (typeof window !== \"undefined\") {\n window.sessionStorage?.setItem(defaultViewSessionKey, \"1\");\n }\n setSearchParams(baseViewSearchParams(searchParams));\n return;\n }\n const next = new URLSearchParams(searchParams);\n next.set(\"view\", view.id);\n if (view.filters && Array.isArray(view.filters) && view.filters.length > 0) {\n next.set(\"filters\", JSON.stringify(view.filters));\n }\n if (view.columns && view.columns.length > 0) {\n next.set(\"columns\", view.columns.join(\",\"));\n }\n if (view.sort) {\n next.set(\"sort\", view.sort);\n }\n next.set(\"page\", \"1\");\n setSearchParams(next);\n }}\n onSaveAs={() => setSaveViewOpen(true)}\n />\n\n <div style={{ position: \"relative\", width: 260 }}>\n <TextInput\n value={searchValue}\n onChange={updateSearch}\n placeholder={`Search ${screenName.toLowerCase()}…`}\n prefix={<Icon.search size={13} />}\n />\n <span style={{ position: \"absolute\", right: 6, top: \"50%\", transform: \"translateY(-50%)\", pointerEvents: \"none\" }}>\n <Kbd>/</Kbd>\n </span>\n </div>\n\n <Btn\n icon={<Icon.filter size={13} />}\n onClick={() => setFilterDrawerOpen(true)}\n active={filterCount > 0}\n >\n Filters\n {filterCount > 0 && (\n <Pill tone=\"violet\" size=\"xs\" style={{ marginLeft: 4 }}>\n {filterCount}\n </Pill>\n )}\n </Btn>\n\n <ColumnPicker\n availableColumns={screen.columns ?? []}\n visibleColumns={visibleCols}\n onToggle={(col) => {\n const next = new URLSearchParams(searchParams);\n const current = visibleCols.includes(col)\n ? visibleCols.filter((c) => c !== col)\n : [...visibleCols, col];\n if (current.length === 0) return;\n next.set(\"columns\", current.join(\",\"));\n setSearchParams(next);\n }}\n onReset={() => {\n const next = new URLSearchParams(searchParams);\n next.delete(\"columns\");\n setSearchParams(next);\n }}\n onShowAll={() => {\n const next = new URLSearchParams(searchParams);\n next.set(\"columns\", (screen.columns ?? []).join(\",\"));\n setSearchParams(next);\n }}\n columnLabel={(c) => {\n if (c.startsWith(\"$status.\")) {\n const gc = c.slice(8);\n const g = entity.status_groups?.[gc];\n return g ? (loc(g.name) || gc) : c;\n }\n if (c.startsWith(\"$property.\")) {\n const pc = c.slice(10);\n const p = entity.properties?.[pc];\n return p ? (loc(p.name) || pc) : c;\n }\n return resolveSchemaFieldLabel(entity, c, schema?.entities);\n }}\n />\n\n <span style={{ width: 1, height: 20, background: \"var(--divider)\" }} />\n <IconBtn\n icon={<Icon.grid size={14} />}\n title=\"Density\"\n active={density !== \"compact\"}\n onClick={() => {\n const next = density === \"compact\" ? \"comfortable\" : density === \"comfortable\" ? \"cozy\" : \"compact\";\n useAppearanceStore.getState().setMany({ density: next });\n }}\n />\n {/* Refresh control. Always visible on every table view: reuses\n retryListLoad (cancels in-flight queries, then refetches). For a\n non-live entity (realtime: false, or auto-degraded by the relay)\n it is the only freshness mechanism. While a fetch is in flight -\n the cold load OR a manual refetch of an already-populated table -\n the icon swaps to a Spinner so the user sees the refresh is\n running, and the button is disabled so it cannot be re-triggered\n mid-flight. isFetching (not isLoading) is the gate: react-query's\n isLoading is true only on the cold first load and would leave a\n manual refresh with no feedback. */}\n <IconBtn\n icon={\n isFetching ? (\n <span data-testid=\"refresh-spinner\" style={{ display: \"inline-flex\" }}>\n <Spinner size={14} />\n </span>\n ) : (\n <Icon.refresh size={14} />\n )\n }\n title=\"Refresh\"\n disabled={isLoading || isFetching}\n onClick={retryListLoad}\n />\n {isLive && (\n <span\n data-testid=\"live-indicator\"\n title=\"This view updates automatically in realtime.\"\n >\n <Pill tone=\"emerald\" size=\"xs\" dot>\n Live\n </Pill>\n </span>\n )}\n\n <div style={{ flex: 1 }} />\n\n {canViewDeleted && (\n <Btn\n kind=\"ghost\"\n size=\"sm\"\n icon={includeDeleted ? <Icon.eyeOff size={12} /> : <Icon.eye size={12} />}\n onClick={toggleIncludeDeleted}\n active={includeDeleted}\n >\n {includeDeleted ? \"Hide deleted\" : \"Show deleted\"}\n </Btn>\n )}\n {entityCode && entity && (\n <ExportMenu\n entityCode={entityCode}\n entity={entity}\n screen={screen}\n viewState={{\n screenCode,\n jsonFilters,\n search: debouncedSearch,\n sort: sortForRequest,\n includeDeleted: includeDeleted || undefined,\n page,\n perPage: perPage,\n }}\n visibleColumns={visibleCols}\n selectedRowPKs={selectedRowPKs}\n totalRows={countTotal}\n pageSize={perPage}\n locale={userLocale}\n />\n )}\n </div>\n\n {/* Filter chips (collapsed filter summary) — only wrap when present so no extra space. */}\n {jsonFilters.length > 0 && (\n <div style={{ padding: \"0 20px 10px\" }}>\n <FilterChips\n filters={jsonFilters}\n onRemove={(field) => setJsonFilters(removeFilter(jsonFilters, field))}\n onClearAll={clearFilters}\n onChipClick={() => setFilterDrawerOpen(true)}\n fieldLabel={(f) => {\n if (f.startsWith(\"$status.\")) {\n const groupCode = f.slice(8);\n const group = entity.status_groups?.[groupCode];\n return group ? (loc(group.name) || groupCode) : f;\n }\n if (f.startsWith(\"$property.\")) {\n const propCode = f.slice(10);\n const prop = entity.properties?.[propCode];\n return prop ? (loc(prop.name) || propCode) : f;\n }\n const field = entity.fields.get(f);\n return field ? fieldDisplayName(field, f, schema?.entities) : f;\n }}\n />\n </div>\n )}\n </>\n\n {/* Table */}\n {isLoading && tableLoadingStale ? (\n <div style={{ padding: \"0 20px\" }}>\n <EmptyState\n icon=\"alert-circle\"\n title=\"Still loading data\"\n description=\"This table is taking longer than expected. Retry the request or check network and server health.\"\n action={(\n <Btn kind=\"primary\" size=\"sm\" onClick={retryListLoad}>\n Retry\n </Btn>\n )}\n />\n </div>\n ) : isLoading ? (\n <TableSkeleton cols={screen.columns?.length ?? 4} />\n ) : isError ? (\n <div style={{ padding: \"0 20px\" }}>\n <EmptyState\n icon=\"alert-circle\"\n title=\"Failed to load data\"\n description={listLoadErrorDescription(error)}\n action={(\n <Btn kind=\"primary\" size=\"sm\" onClick={retryListLoad}>\n Retry\n </Btn>\n )}\n />\n </div>\n ) : data.length === 0 ? (\n <div style={{ padding: \"0 20px\" }}>\n {(() => {\n const isFiltered = !!debouncedSearch || filterCount > 0;\n const customEmpty = entityDisplay.empty_state;\n const customTitle = loc(customEmpty?.title);\n const customBody = loc(customEmpty?.body);\n const learnMoreUrl = safeExternalUrl(customEmpty?.learn_more_url);\n\n const title = isFiltered\n ? \"No results found\"\n : customTitle || `No ${loc(entityDisplay.name_plural).toLowerCase() || screenName.toLowerCase()} yet`;\n\n // Description precedence:\n // - filtered list -> generic \"adjust search/filters\" copy\n // - custom empty_state.body -> use as-is (with optional learn-more link)\n // - read-only entity, no custom body -> render no body\n // - mutable entity, no custom body -> legacy \"Create your first...\" copy\n let description: ReactNode | undefined;\n if (isFiltered) {\n description = \"Try adjusting your search or filters\";\n } else if (customBody) {\n description = (\n <>\n {customBody}\n {learnMoreUrl && (\n <>\n {\" \"}\n <a href={learnMoreUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n Learn more\n </a>\n </>\n )}\n </>\n );\n } else if (!canCreate) {\n description = undefined;\n } else {\n description = `Create your first ${loc(entityDisplay.name).toLowerCase()} to get started`;\n }\n\n // EmptyState no longer carries its own \"New X\" CTA. The\n // __create platform-default action lives in the list_toolbar\n // slot at the top of the page; rendering a second\n // visually-identical button below the toolbar produced two\n // primary CTAs side by side on empty lists (see admin-deep-\n // explore.spec.ts \"Group: list-page chrome\" regression). The\n // description text is enough to point the user at the\n // canonical button. Entities that deliberately opt out of\n // __create (DisableDefaults: [__create]) get no CTA in\n // either place, which is the documented \"no create from UI\"\n // contract per plan §3.\n return (\n <EmptyState\n icon={screenIcon}\n title={title}\n description={description}\n />\n );\n })()}\n </div>\n ) : (\n <div\n style={{\n // Embed mode flows in document height (no inner scroll\n // viewport) so the iframe can auto-size; the standalone app\n // gives the table a bounded flex viewport to virtualize in.\n flex: embed.enabled ? \"none\" : 1,\n minHeight: 0,\n padding: \"0 20px 16px\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <div\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n overflow: \"hidden\",\n boxShadow: \"var(--shadow-xs)\",\n display: \"flex\",\n flexDirection: \"column\",\n flex: embed.enabled ? \"none\" : 1,\n minHeight: 0,\n }}\n >\n <div\n ref={parentRef}\n style={{\n overflow: embed.enabled ? \"visible\" : \"auto\",\n flex: embed.enabled ? \"none\" : 1,\n minHeight: 0,\n }}\n >\n <table\n style={{\n width: \"100%\",\n borderCollapse: \"separate\",\n borderSpacing: 0,\n fontSize: 12.5,\n color: \"var(--text)\",\n }}\n >\n <thead>\n {table.getHeaderGroups().map((headerGroup) => (\n <tr key={headerGroup.id} style={{ height: 32, background: \"var(--surface-2)\" }}>\n {headerGroup.headers.map((header) => {\n // Sortability rules: action and selection columns are\n // chrome, not data. $property.* columns are filter-only\n // today (plan §2: \"property sort not supported\") -\n // the backend hard-rejects `?sort=$property.foo:asc`\n // with HTTP 400, so we must NOT let the header cycle\n // produce that URL. $status.* sorting IS supported\n // (joined to object_statuses) so it stays clickable.\n // For base-table fields we mirror the backend's\n // sortability rule (data_cursor.go parseSortParam):\n // secret, password, json, and array-typed fields\n // also surface as 400 under strict parse, so they\n // must be unclickable here too. $refs.* is virtual\n // join expansion data, never sortable.\n const canSort = isHeaderSortable(header.id, entity);\n const sorted = header.column.getIsSorted();\n const isSelect = header.id === \"_select\";\n const isActions = header.id === \"_actions\";\n // Sort rank for the multi-sort badge: 1-based index in\n // sortCols, only surfaced when more than one column is\n // sorted (plan §7.1).\n const sortRank = sorted\n ? sortCols.findIndex((c) => c.field === header.id) + 1\n : 0;\n const showRankBadge = sortCols.length > 1 && sortRank > 0;\n const inMultiSort = sortRank > 0;\n const canAppendMore = sortCols.length < MAX_SORT_COLS;\n // Plain click → TanStack's single-col cycle (replace).\n // Shift-click → toggle this column in/out of the multi-\n // sort tail (plan §7.1). Routed through our own helper\n // so the \"remove if already in sort\" semantics differ\n // from TanStack's default (flip dir on shift-click).\n const toggleHandler = canSort\n ? header.column.getToggleSortingHandler()\n : undefined;\n const onHeaderClick = canSort\n ? (e: ReactMouseEvent<HTMLTableCellElement>) => {\n if (e.shiftKey) {\n e.preventDefault();\n shiftClickColumn(header.id);\n return;\n }\n toggleHandler?.(e);\n }\n : undefined;\n return (\n <th\n key={header.id}\n onClick={onHeaderClick}\n style={{\n padding: isSelect ? \"0 6px 0 12px\" : isActions ? \"0 6px\" : \"0 10px\",\n textAlign: \"left\",\n fontWeight: 600,\n fontSize: 11,\n letterSpacing: \"0.01em\",\n color: \"var(--text-3)\",\n whiteSpace: \"nowrap\",\n cursor: canSort ? \"pointer\" : \"default\",\n width: header.getSize(),\n minWidth: header.getSize(),\n borderBottom: \"1px solid var(--border)\",\n background: \"var(--surface-2)\",\n position: \"sticky\",\n top: 0,\n left: isSelect ? 0 : \"auto\",\n zIndex: isSelect ? 3 : 2,\n }}\n >\n {header.isPlaceholder ? null : canSort ? (\n <span\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 4,\n textTransform: \"uppercase\",\n fontSize: 10.5,\n letterSpacing: \"0.04em\",\n }}\n >\n {flexRender(header.column.columnDef.header, header.getContext())}\n <span\n style={{\n display: \"inline-flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n lineHeight: 0,\n }}\n >\n {sorted === \"asc\" ? (\n <Icon.arrowUp size={11} />\n ) : sorted === \"desc\" ? (\n <Icon.arrowDown size={11} />\n ) : (\n <Icon.sort size={11} style={{ opacity: 0.35 }} />\n )}\n {/* Dot indicator: this column has a NULLs\n placement override that DIFFERS from the\n system default. Plan §3 Decision 1 says\n NULLS LAST is the system default in both\n directions, so only \"nulls_first\" is the\n non-default state the dot signals. An\n explicit \"nulls_last\" matches the default\n and stays silent. Plan §7.1 - \"tiny dot\n under the arrow ... when NULLs placement\n is NOT the system default\". */}\n {sorted && sortNullsMap.get(header.id) === \"first\" ? (\n <span\n aria-hidden=\"true\"\n style={{\n width: 4,\n height: 4,\n marginTop: 1,\n borderRadius: 9999,\n background: \"var(--text-3)\",\n }}\n />\n ) : null}\n </span>\n {/* Multi-sort rank badge (plan §7.1): small\n superscript number on each sorted header\n showing its 1-based rank. Hidden when only\n one column is sorted to avoid noise. */}\n {showRankBadge ? (\n <span\n aria-label={`Sort rank ${sortRank}`}\n style={{\n fontSize: 10,\n lineHeight: 1,\n color: \"var(--text-3)\",\n verticalAlign: \"super\",\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {sortRank}\n </span>\n ) : null}\n <SortHeaderMenu\n field={header.id}\n activeDir={sorted}\n activeNulls={sortNullsMap.get(header.id)}\n inMultiSort={inMultiSort}\n canAppend={canAppendMore}\n onApply={(dir, nulls) => applyColumnSort(header.id, dir, nulls)}\n onAppend={(dir, nulls) => appendColumnSort(header.id, dir, nulls)}\n onClearColumn={() => clearColumnSort(header.id)}\n onClearAll={clearAllSorts}\n />\n </span>\n ) : (\n flexRender(header.column.columnDef.header, header.getContext())\n )}\n </th>\n );\n })}\n </tr>\n ))}\n </thead>\n <tbody>\n {embed.enabled ? (\n // Embed mode: every row in document flow, no virtualization.\n // See the useVirtualizer note above - the auto-resizing\n // iframe has no bounded viewport, so a virtualized table\n // renders zero rows. One page of rows is cheap to render\n // whole and lets the iframe size itself correctly.\n rows.map((row, i) => renderDataRow(row, i, false))\n ) : (\n <>\n {rowVirtualizer.getVirtualItems().length > 0 && (\n <tr>\n <td\n style={{ height: rowVirtualizer.getVirtualItems()[0]?.start ?? 0 }}\n colSpan={columns.length}\n />\n </tr>\n )}\n {rowVirtualizer.getVirtualItems().map((virtualRow) =>\n renderDataRow(rows[virtualRow.index], virtualRow.index, true),\n )}\n {rowVirtualizer.getVirtualItems().length > 0 && (\n <tr>\n <td\n style={{\n height:\n rowVirtualizer.getTotalSize() -\n (rowVirtualizer.getVirtualItems().at(-1)?.end ?? 0),\n }}\n colSpan={columns.length}\n />\n </tr>\n )}\n </>\n )}\n </tbody>\n </table>\n </div>\n\n {/* Footer strip — inside the card per design */}\n {meta && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n padding: \"8px 14px\",\n background: \"var(--surface-2)\",\n borderTop: \"1px solid var(--border)\",\n fontSize: 11.5,\n color: \"var(--text-3)\",\n gap: 12,\n }}\n >\n {/* Record count. Resolves from the separate count query;\n a spinner shows while it is in flight, then the count\n rendered to a precision matching its strategy (exact\n \"3,613,711\" vs estimated \"~3.6M\"). A failed count\n degrades quietly to the loaded row count. */}\n <span\n className=\"num\"\n data-testid=\"list-footer-count\"\n style={{ display: \"inline-flex\", alignItems: \"center\", gap: 5 }}\n >\n {countTotal !== undefined ? (\n <>\n {formatCount(countTotal, countStrategy ?? \"exact\")}{\" \"}\n {countTotal === 1 ? \"record\" : \"records\"}\n </>\n ) : countLoading && !countError ? (\n <span data-testid=\"list-footer-count-pending\" style={{ display: \"inline-flex\" }}>\n <Spinner size={11} />\n </span>\n ) : (\n <>\n {data.length} {data.length === 1 ? \"record\" : \"records\"}\n </>\n )}\n </span>\n <span>·</span>\n <span>\n Selected:{\" \"}\n <span className=\"num\" style={{ color: \"var(--text-2)\", fontWeight: 500 }}>\n {selectedIds.length}\n </span>\n </span>\n <div style={{ flex: 1 }} />\n <span>Rows per page</span>\n <select\n aria-label=\"Rows per page\"\n value={perPage}\n onChange={(e) => setPerPage(parseInt(e.target.value, 10))}\n style={{\n fontSize: 11.5,\n padding: \"2px 4px\",\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 4,\n color: \"var(--text-2)\",\n cursor: \"pointer\",\n }}\n >\n {PER_PAGE_OPTIONS.map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n {/* Pager. Works without the count: Next is gated on the\n window's has_more probe, Prev on page > 1. Once the\n count resolves, the total page span fills in (\"Page 2\n of 14\"); until then it shows just \"Page 2\". The pager\n is hidden only when there is demonstrably one page\n (no has_more and on page 1). */}\n {(hasMorePages || page > 1) && (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n <IconBtn\n size={22}\n icon={<Icon.left size={12} />}\n onClick={() => goToPage(page - 1)}\n disabled={page <= 1}\n />\n <span className=\"num\" style={{ padding: \"0 6px\" }}>\n Page {page}\n {totalPages !== undefined ? ` of ${totalPages}` : \"\"}\n </span>\n <IconBtn\n size={22}\n icon={<Icon.right size={12} />}\n onClick={() => goToPage(page + 1)}\n disabled={totalPages !== undefined ? page >= totalPages : !hasMorePages}\n />\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Filter drawer */}\n <FilterDrawer\n open={filterDrawerOpen}\n onClose={() => setFilterDrawerOpen(false)}\n filters={jsonFilters}\n onApply={setJsonFilters}\n availableFields={buildFilterFieldMeta(screen, entity, schema?.entities)}\n />\n\n <SaveViewDialog\n open={saveViewOpen}\n pending={createViewMutation.isPending}\n onClose={() => setSaveViewOpen(false)}\n onSave={async (name) => {\n if (!user?.id || !activeTenant?.id) return;\n const response = await createViewMutation.mutateAsync({\n tenant_id: activeTenant.id,\n screen_code: screenCode,\n user_id: user.id,\n name: { en: name },\n filters: jsonFilters,\n columns: visibleCols,\n ...(sortForRequest ? { sort: sortForRequest } : {}),\n is_shared: false,\n is_default: false,\n });\n const createdId = String(response.data.id ?? \"\");\n if (createdId) {\n const next = new URLSearchParams(searchParams);\n next.set(\"view\", createdId);\n setSearchParams(next);\n }\n setSaveViewOpen(false);\n }}\n />\n\n {/* Delete confirmation */}\n <ConfirmDialog\n open={deleteState !== null}\n onOpenChange={(open) => !open && setDeleteState(null)}\n title={`Delete ${loc(entityDisplay.name)}`}\n description={\n deleteState?.confirmation\n ? deleteState.confirmation\n : hasSoftDelete\n ? `Are you sure you want to delete this ${loc(entityDisplay.name).toLowerCase()}? It can be restored later by an admin.`\n : `Are you sure you want to delete this ${loc(entityDisplay.name).toLowerCase()}? This action cannot be undone.`\n }\n confirmLabel=\"Delete\"\n onConfirm={handleDelete}\n destructive\n />\n\n {selectedIds.length > 0 && (\n <ListBulkBar\n selectedIds={selectedIds}\n floatbarActions={floatbarActions}\n screenActionLayout={screen.action_layout as ResolvedActionLayoutNode[] | undefined}\n entityActionLayout={entity.action_layout as ResolvedActionLayoutNode[] | undefined}\n data={data}\n rowKey={getSelectedRowKey}\n entityCode={entityCode}\n onClear={() => setRowSelection({})}\n onActionSuccess={() => setRowSelection({})}\n widgetContext={widgetContext}\n />\n )}\n\n {contextMenu && entity && (\n <div\n style={{ position: \"fixed\", top: contextMenu.y, left: contextMenu.x, zIndex: 100 }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: 4,\n boxShadow: \"var(--shadow-lg)\",\n width: 200,\n }}\n >\n {peekEnabled && (\n <MenuItem\n icon={<Icon.eye size={13} />}\n onClick={() => {\n setPeekRow(contextMenu.row);\n setContextMenu(null);\n }}\n >\n Quick peek\n </MenuItem>\n )}\n <MenuItem\n icon={<Icon.external size={13} />}\n onClick={() => {\n navigateToDetail(contextMenu.row);\n setContextMenu(null);\n }}\n >\n Open\n </MenuItem>\n {canDelete && (\n <>\n <MenuItem divider />\n <MenuItem\n icon={<Icon.trash size={13} />}\n danger\n onClick={() => {\n const pk = entity.fields.names()?.find((f) => entity.fields.get(f)?.primary);\n const id = pk ? String(contextMenu.row[pk] ?? \"\") : \"\";\n setContextMenu(null);\n if (id) setDeleteState({ ids: [id] });\n }}\n >\n Delete\n </MenuItem>\n </>\n )}\n </div>\n <div\n onClick={() => setContextMenu(null)}\n onContextMenu={(e) => {\n e.preventDefault();\n setContextMenu(null);\n }}\n style={{ position: \"fixed\", inset: 0, zIndex: -1 }}\n />\n </div>\n )}\n\n {peekEnabled && peekRow && entity && (\n <QuickPeek\n entity={entity}\n entityCode={entityCode}\n record={peekRow}\n title={(() => {\n const firstCol = screen.columns?.find(\n (c) => !c.startsWith(\"$status.\") && !c.startsWith(\"$property.\"),\n );\n return firstCol ? String(peekRow[firstCol] ?? loc(entity.display.name)) : loc(entity.display.name);\n })()}\n recordId={(() => {\n const pk = entity.fields.names()?.find((f) => entity.fields.get(f)?.primary);\n return pk ? String(peekRow[pk] ?? \"\") : undefined;\n })()}\n fields={peekFields}\n screen={screen}\n actions={peekActions}\n executeAction={peekExecute}\n onClose={() => {\n // Drop any draft we may have staged for this row in a prior\n // \"Open full\" path — keeps the store from accumulating entries\n // that were never consumed by SmartDetailPage.\n if (entity && peekRow) {\n const pk = entity.fields.names()?.find((f) => entity.fields.get(f)?.primary);\n if (pk) clearPeekDraft(String(peekRow[pk] ?? \"\"));\n }\n setPeekRow(null);\n }}\n onOpen={(draft) => {\n const row = peekRow;\n setPeekRow(null);\n navigateToDetail(row, { draft: draft as RowData | null });\n }}\n onSave={(next) => {\n // Optimistic local update so the panel reflects the edit\n // immediately, then PATCH to server and invalidate caches.\n setPeekRow(next as RowData);\n const pk = entity.fields.names()?.find((f) => entity.fields.get(f)?.primary);\n if (!pk) return;\n const id = String((next as Record<string, unknown>)[pk] ?? \"\");\n const payload: Record<string, unknown> = {};\n for (const fieldName of entity.fields.names()) {\n const nextValue = getSchemaFieldValue(entity, next as Record<string, unknown>, fieldName);\n const prevValue = getSchemaFieldValue(entity, peekRow as Record<string, unknown>, fieldName);\n const serializedNext = serializeSchemaFieldValue(entity, fieldName, nextValue);\n const serializedPrev = serializeSchemaFieldValue(entity, fieldName, prevValue);\n const normalizedNext = serializedNext === \"\" ? null : serializedNext;\n const normalizedPrev = serializedPrev === \"\" || serializedPrev === undefined ? null : serializedPrev;\n if (normalizedNext !== normalizedPrev) {\n payload[fieldName] = serializedNext;\n }\n }\n const filesPayload = buildFilesPayload({\n entity,\n formData: next as Record<string, unknown>,\n record: peekRow as Record<string, unknown>,\n isNew: false,\n });\n if (Object.keys(filesPayload).length > 0) {\n payload.$files = filesPayload;\n }\n if (Object.keys(payload).length === 0) return;\n const committedFileIds = collectCommittedFileIds(filesPayload);\n updateMutation.mutate(\n { pkValues: id, data: payload },\n {\n onSuccess: () => {\n committedFileIds.forEach((fileId) => fileDraftScope.markCommitted(fileId));\n },\n },\n );\n }}\n />\n )}\n </div>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { getEntity } from \"@/api/data\";\nimport type { PkValues } from \"@/types/schema\";\n\n// useEntityItem fetches a single record by primary key.\n// pkValues can be:\n// - a string (single PK convenience)\n// - a PkValues object (composite PK)\n// - undefined (disabled)\nexport function useEntityItem(entity: string | undefined, pkValues: PkValues | string | undefined) {\n // Stable cache key: for string IDs use them directly; for PkValues sort keys\n // so that { a: \"1\", b: \"2\" } and { b: \"2\", a: \"1\" } hit the same cache entry.\n const cacheKey = pkValues === undefined || pkValues === \"new\"\n ? undefined\n : typeof pkValues === \"string\"\n ? pkValues\n : Object.keys(pkValues).sort().map((k) => [k, pkValues[k]] as const);\n\n return useQuery({\n queryKey: [\"entity-item\", entity, cacheKey],\n queryFn: () => getEntity(entity!, pkValues!),\n enabled: !!entity && !!pkValues && pkValues !== \"new\" && cacheKey !== undefined,\n });\n}\n","import { useState, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Btn, Icon, SectionCard } from \"@/components/primitives\";\nimport {\n dataTableStyle,\n dataTableHeadCellStyle,\n dataTableCellStyle,\n} from \"@/components/primitives/dataTableStyles\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { resolveAssociation, listEntities } from \"@/api/data\";\nimport { findListScreenForEntity } from \"@/lib/actions\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { loc, fieldDisplayName } from \"@/types/schema\";\nimport type { AssociationDef, Schema } from \"@/types/schema\";\n\ninterface AssociationTabsProps {\n entityCode: string;\n recordId: string;\n associations: Record<string, AssociationDef>;\n schema: Schema;\n}\n\nexport function AssociationTabs({\n entityCode,\n recordId,\n associations,\n schema,\n}: AssociationTabsProps) {\n const assocEntries = useMemo(() => Object.entries(associations), [associations]);\n const [activeTab, setActiveTab] = useState(0);\n\n if (assocEntries.length === 0) return null;\n\n const [activeKey, activeAssoc] = assocEntries[activeTab];\n\n return (\n <SectionCard\n title={\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4, borderBottom: \"1px solid var(--divider)\", margin: \"-10px -14px 0 -14px\", padding: \"0 14px\" }}>\n {assocEntries.map(([key, assoc], idx) => {\n const active = idx === activeTab;\n return (\n <button\n key={key}\n onClick={(e) => {\n e.stopPropagation();\n setActiveTab(idx);\n }}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"10px 12px\",\n fontSize: 12.5,\n background: \"transparent\",\n border: 0,\n borderBottom: `2px solid ${active ? \"var(--accent)\" : \"transparent\"}`,\n marginBottom: -1,\n color: active ? \"var(--text)\" : \"var(--text-3)\",\n cursor: \"pointer\",\n fontWeight: active ? 600 : 500,\n }}\n >\n {assoc.display?.icon && <DynamicIcon name={assoc.display.icon} size={13} />}\n {loc(assoc.display?.name) || key}\n </button>\n );\n })}\n </div>\n }\n collapsible={false}\n padding=\"0\"\n >\n <AssociationContent\n entityCode={entityCode}\n recordId={recordId}\n assocKey={activeKey}\n association={activeAssoc}\n schema={schema}\n />\n </SectionCard>\n );\n}\n\nfunction AssociationContent({\n entityCode,\n recordId,\n assocKey,\n association,\n schema,\n}: {\n entityCode: string;\n recordId: string;\n assocKey: string;\n association: AssociationDef;\n schema: Schema;\n}) {\n const tabNavigate = useTabNavigate();\n const userLocale = useUserLocale();\n const assocCode = `${entityCode}.${assocKey}`;\n\n const { data: assocResult, isLoading: resolving } = useQuery({\n queryKey: [\"association\", assocCode, recordId],\n queryFn: () => resolveAssociation(assocCode, [recordId]),\n });\n\n const targetEntityCode = assocResult?.target_entity ?? association.target;\n const targetEntity = schema.entities[targetEntityCode];\n const targetIds = useMemo(() => assocResult?.ids ?? [], [assocResult?.ids]);\n\n const pk = useMemo(\n () => targetEntity?.fields.names().find((f) => targetEntity.fields.get(f)?.primary) ?? \"id\",\n [targetEntity],\n );\n\n const filterIds = useMemo(() => targetIds.slice(0, 50), [targetIds]);\n\n const { data: listResponse, isLoading: loadingRecords } = useQuery({\n queryKey: [\"association-records\", targetEntityCode, filterIds],\n queryFn: () =>\n listEntities(targetEntityCode, {\n per_page: 50,\n filters: [{ field: pk, op: \"in\", value: filterIds }],\n }),\n enabled: filterIds.length > 0 && !!targetEntity,\n });\n\n const rows = listResponse?.data ?? [];\n const refs = listResponse?.$refs;\n\n const columns = useMemo(() => {\n if (!targetEntity) return [];\n return targetEntity.fields.names()\n .filter((name) => {\n const f = targetEntity.fields.get(name);\n return f && !f.primary && !f.auto && f.display?.is_listable !== false;\n })\n .slice(0, 6);\n }, [targetEntity]);\n\n const handleViewAll = () => {\n const targetScreen = findListScreenForEntity(schema, targetEntityCode);\n if (!targetScreen) return;\n const displayName = loc(association.display?.name) || assocKey;\n const search = new URLSearchParams();\n search.set(\"filters\", JSON.stringify([{ field: pk, op: \"in\", value: targetIds }]));\n tabNavigate(targetScreen.route, {\n title: displayName,\n icon: association.display?.icon || targetScreen.icon,\n search: search.toString(),\n });\n };\n\n const muted = { fontSize: 12.5, color: \"var(--text-3)\", padding: \"14px 16px\" } as const;\n\n if (resolving || loadingRecords) return <div style={muted}>Loading…</div>;\n if (!targetEntity) return <div style={muted}>Entity not found.</div>;\n if (targetIds.length === 0) return <div style={muted}>No related records.</div>;\n\n const targetScreen = findListScreenForEntity(schema, targetEntityCode);\n\n return (\n <div style={{ padding: \"8px 0\" }}>\n <table style={dataTableStyle}>\n <thead>\n <tr>\n {columns.map((col) => (\n <th key={col} style={dataTableHeadCellStyle}>\n {fieldDisplayName(targetEntity.fields.get(col)!, col, schema.entities)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr key={i}>\n {columns.map((col) => {\n const field = targetEntity.fields.get(col);\n if (!field) return <td key={col} />;\n return (\n <td key={col} style={dataTableCellStyle}>\n {renderField({\n field,\n fieldName: col,\n value: row[col],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n {targetScreen && assocResult && assocResult.count > rows.length && (\n <div style={{ padding: \"10px 16px\", textAlign: \"center\" }}>\n <Btn icon={<Icon.external size={12} />} onClick={handleViewAll}>\n View all {assocResult.count} records\n </Btn>\n </div>\n )}\n {targetScreen && rows.length > 0 && assocResult && assocResult.count <= rows.length && (\n <div style={{ padding: \"10px 16px\", textAlign: \"center\" }}>\n <Btn kind=\"ghost\" icon={<Icon.external size={12} />} onClick={handleViewAll}>\n Open in list view\n </Btn>\n </div>\n )}\n </div>\n );\n}\n","import type { SizeValue } from \"@/types/schema\";\n\n// ParsedSizeValue represents a parsed and normalized SizeValue.\nexport type ParsedSizeValue =\n | { kind: \"weight\"; value: number }\n | { kind: \"px\"; value: number }\n | { kind: \"auto\" }\n | { kind: \"fill\" };\n\nconst PX_PATTERN = /^(\\d+)px$/;\n\n// parseSizeValue parses a SizeValue string into a structured representation.\n// Returns null if the value is invalid.\nexport function parseSizeValue(raw: SizeValue | undefined): ParsedSizeValue | null {\n if (raw == null || raw === \"\") return null;\n\n if (raw === \"auto\") return { kind: \"auto\" };\n if (raw === \"fill\") return { kind: \"fill\" };\n\n // Check for \"<n>px\" format.\n const pxMatch = PX_PATTERN.exec(raw);\n if (pxMatch) {\n const n = parseInt(pxMatch[1], 10);\n if (n > 0) return { kind: \"px\", value: n };\n return null; // 0px or negative is invalid\n }\n\n // Check for integer weight (plain number string or actual number coerced to string).\n const intVal = parseInt(raw, 10);\n if (!isNaN(intVal) && String(intVal) === raw && intVal > 0) {\n return { kind: \"weight\", value: intVal };\n }\n\n return null; // unknown token\n}\n\n// isValidSizeValue returns true if the value can be parsed as a valid SizeValue.\nexport function isValidSizeValue(raw: SizeValue | undefined): boolean {\n if (raw == null || raw === \"\") return true; // omitted is valid\n return parseSizeValue(raw) !== null;\n}\n\n// sizeValueToCSS converts a parsed SizeValue into a CSS value string suitable\n// for flex or grid sizing.\n// weight -> flex: <n> (caller should apply to flex container)\n// px -> \"<n>px\"\n// auto -> \"auto\"\n// fill -> \"1fr\" (for grid) or \"flex: 1\" (for flex)\nexport function sizeValueToCSS(parsed: ParsedSizeValue): string {\n switch (parsed.kind) {\n case \"weight\":\n return String(parsed.value);\n case \"px\":\n return parsed.value + \"px\";\n case \"auto\":\n return \"auto\";\n case \"fill\":\n return \"1fr\";\n }\n}\n\n// normalizeSizeValue parses and returns a CSS-ready string, or the default\n// if the input is empty/undefined.\nexport function normalizeSizeValue(\n raw: SizeValue | undefined,\n defaultValue: ParsedSizeValue,\n): ParsedSizeValue {\n if (raw == null || raw === \"\") return defaultValue;\n return parseSizeValue(raw) ?? defaultValue;\n}\n","import { useState, useCallback, useRef, useEffect } from \"react\";\nimport type { RowConfig, SizeValue } from \"@/types/schema\";\nimport { cn } from \"@/lib/utils\";\nimport { parseSizeValue } from \"@/lib/size-value\";\nimport { LayoutNodeRenderer, type LayoutContext } from \"./LayoutRenderer\";\n\ninterface LayoutRowProps {\n config: RowConfig;\n ctx: LayoutContext;\n}\n\n// LayoutRow renders children horizontally with flex layout. Supports:\n// - gap between items\n// - resizable drag handles between items\n// - responsive stacking below a breakpoint\nexport function LayoutRow({ config, ctx }: LayoutRowProps) {\n const { items, gap = 0, resizable = false, stack_below } = config;\n const stackPx = stack_below === \"never\" ? 0 : (stack_below ?? 768);\n const [stacked, setStacked] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Track container width for responsive stacking.\n useEffect(() => {\n if (stackPx === 0) return;\n const el = containerRef.current;\n if (!el) return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n setStacked(entry.contentRect.width < stackPx);\n }\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, [stackPx]);\n\n // Track flex sizes when resizable.\n const [flexSizes, setFlexSizes] = useState<number[] | null>(null);\n const dragging = useRef<{ index: number; startX: number; startSizes: number[] } | null>(null);\n\n const getInitialSizes = useCallback((): number[] => {\n return items.map((item) => {\n // Width can live at the LayoutNode level OR inside the container config\n // (e.g. `column: { width: 3 }` vs `width: 3` beside `column:`). Check all.\n const w = item.width ?? item.column?.width ?? item.row?.width ?? config.width;\n const parsed = parseSizeValue(w);\n if (parsed && parsed.kind === \"weight\") return parsed.value;\n return 1;\n });\n }, [items, config.width]);\n\n const onDragStart = useCallback(\n (index: number, e: React.MouseEvent) => {\n e.preventDefault();\n const sizes = flexSizes ?? getInitialSizes();\n dragging.current = { index, startX: e.clientX, startSizes: [...sizes] };\n\n const onMove = (ev: MouseEvent) => {\n const d = dragging.current;\n if (!d || !containerRef.current) return;\n const totalWidth = containerRef.current.getBoundingClientRect().width;\n const delta = ev.clientX - d.startX;\n const totalFlex = d.startSizes.reduce((a, b) => a + b, 0);\n const flexDelta = (delta / totalWidth) * totalFlex;\n const newSizes = [...d.startSizes];\n newSizes[d.index] = Math.max(0.1, d.startSizes[d.index] + flexDelta);\n newSizes[d.index + 1] = Math.max(0.1, d.startSizes[d.index + 1] - flexDelta);\n setFlexSizes(newSizes);\n };\n\n const onUp = () => {\n dragging.current = null;\n document.removeEventListener(\"mousemove\", onMove);\n document.removeEventListener(\"mouseup\", onUp);\n };\n\n document.addEventListener(\"mousemove\", onMove);\n document.addEventListener(\"mouseup\", onUp);\n },\n [flexSizes, getInitialSizes],\n );\n\n const sizes = flexSizes ?? getInitialSizes();\n\n // Parse height for the row container (enables scrollable children).\n const heightStyle = config.height ? parseHeight(config.height) : undefined;\n\n return (\n <div\n ref={containerRef}\n className={cn(\"flex\", stacked && \"flex-col\")}\n style={{ gap: gap ? `${gap}px` : undefined, ...heightStyle }}\n >\n {items.map((item, i) => (\n <div\n key={i}\n className={cn(\"min-w-0 flex items-stretch\", stacked && \"w-full\")}\n style={stacked ? undefined : itemStyle(item.width ?? item.column?.width ?? item.row?.width ?? config.width, sizes[i], item.min_width, item.max_width)}\n >\n <div className={cn(\"flex-1 min-w-0 flex flex-col\", heightStyle && \"overflow-y-auto overflow-x-hidden\")}>\n <LayoutNodeRenderer node={item} ctx={ctx} />\n </div>\n {resizable && !stacked && i < items.length - 1 && (\n <div\n className=\"detail-resize-handle\"\n onMouseDown={(e) => onDragStart(i, e)}\n />\n )}\n </div>\n ))}\n </div>\n );\n}\n\nfunction itemStyle(\n width: SizeValue | undefined,\n flexValue: number,\n minWidth?: string,\n maxWidth?: string,\n): React.CSSProperties {\n const parsed = parseSizeValue(width);\n let flex: string;\n if (!parsed || parsed.kind === \"weight\") {\n flex = `${flexValue} 1 0%`;\n } else if (parsed.kind === \"px\") {\n flex = `0 0 ${parsed.value}px`;\n } else if (parsed.kind === \"fill\") {\n flex = \"1 1 0%\";\n } else {\n flex = \"0 0 auto\";\n }\n return { flex, minWidth, maxWidth };\n}\n\nfunction parseHeight(height: SizeValue): React.CSSProperties | undefined {\n if (!height) return undefined;\n const parsed = parseSizeValue(height);\n if (parsed?.kind === \"px\") return { height: `${parsed.value}px`, overflow: \"hidden\" };\n // Support raw CSS values like \"calc(100vh - 200px)\" or \"80vh\" passed as strings.\n // parseSizeValue returns null for these, so we handle them directly.\n if (typeof height === \"string\" && (height.includes(\"vh\") || height.includes(\"calc\") || height.includes(\"%\"))) {\n return { height, overflow: \"hidden\" };\n }\n return undefined;\n}\n","import type { ColumnConfig, LayoutNode } from \"@/types/schema\";\nimport { LayoutNodeRenderer, type LayoutContext } from \"./LayoutRenderer\";\n\ninterface LayoutColumnProps {\n config: ColumnConfig;\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\n// LayoutColumn renders children vertically with flex layout and optional gap.\nexport function LayoutColumn({ config, ctx }: LayoutColumnProps) {\n const { items, gap = 12 } = config;\n\n return (\n <div\n className=\"flex flex-col h-full\"\n style={{ gap: gap ? `${gap}px` : undefined }}\n >\n {items.map((item, i) => (\n <LayoutNodeRenderer key={i} node={item} ctx={ctx} />\n ))}\n </div>\n );\n}\n","// Block metadata resolution - the display label and icon for a layout block.\n//\n// A `children` / `associated` / `section` node needs a title and icon\n// wherever it gets chrome: a tab strip (LayoutTabs) and the section-card\n// wrapper around a top-level children/associated block (ChildrenBlock).\n// One source of truth so the two never drift.\n\nimport type { LayoutNode } from \"@/types/schema\";\nimport { loc } from \"@/types/schema\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\n\n// The related entity a children/associated block points at; undefined for a\n// section node.\nfunction blockRelationEntity(node: LayoutNode): string | undefined {\n return node.children ?? node.associated;\n}\n\n// resolveBlockLabel returns the display title for a layout block. Priority:\n// explicit `label` override -> the parent's child-relation display name ->\n// the related entity's own plural/singular name -> the raw entity code.\n// For a `section` node it returns the section name.\nexport function resolveBlockLabel(node: LayoutNode, ctx: LayoutContext): string {\n const relEntity = blockRelationEntity(node);\n if (relEntity) {\n if (node.label) return loc(node.label);\n const relation = ctx.entity.children?.find((c) => c.entity === relEntity);\n if (relation?.display?.name) return loc(relation.display.name);\n const entity = ctx.schema.entities[relEntity];\n if (entity) return loc(entity.display.name_plural) || loc(entity.display.name) || relEntity;\n return relEntity;\n }\n if (node.section) return loc(node.section.name);\n return \"Tab\";\n}\n\n// resolveBlockIcon returns the Icon-registry name for a layout block, or\n// undefined when none is declared. Priority: explicit `icon` -> the parent's\n// child-relation icon -> the related entity's icon.\nexport function resolveBlockIcon(node: LayoutNode, ctx: LayoutContext): string | undefined {\n if (node.icon) return node.icon;\n const relEntity = blockRelationEntity(node);\n if (relEntity) {\n const relation = ctx.entity.children?.find((c) => c.entity === relEntity);\n if (relation?.display?.icon) return relation.display.icon;\n const entity = ctx.schema.entities[relEntity];\n if (entity) return entity.display.icon;\n }\n return undefined;\n}\n","import { useState } from \"react\";\nimport type { TabsConfig } from \"@/types/schema\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { LayoutNodeRenderer, type LayoutContext } from \"./LayoutRenderer\";\nimport { resolveBlockLabel, resolveBlockIcon } from \"./blockMeta\";\n\ninterface LayoutTabsProps {\n config: TabsConfig;\n ctx: LayoutContext;\n}\n\n// LayoutTabs renders the `tabs:` node as an in-card tab strip: a clean\n// underline indicator, distinct from the app's browser-style top tab strip\n// (which is not touched - see the design brief §14).\nexport function LayoutTabs({ config, ctx }: LayoutTabsProps) {\n const { items } = config;\n const [activeTab, setActiveTab] = useState(0);\n\n if (items.length === 0) return null;\n\n return (\n <div\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 10,\n overflow: \"hidden\",\n boxShadow: \"var(--shadow-xs)\",\n flexShrink: 0,\n }}\n >\n <div\n role=\"tablist\"\n style={{\n display: \"flex\",\n alignItems: \"stretch\",\n gap: 4,\n borderBottom: \"1px solid var(--divider)\",\n padding: \"6px 10px 0 10px\",\n overflowX: \"auto\",\n }}\n >\n {items.map((item, idx) => {\n const label = resolveBlockLabel(item, ctx);\n const icon = resolveBlockIcon(item, ctx);\n const active = idx === activeTab;\n return (\n <button\n key={idx}\n role=\"tab\"\n aria-selected={active}\n onClick={(e) => {\n e.stopPropagation();\n setActiveTab(idx);\n }}\n style={{\n position: \"relative\",\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"8px 12px\",\n background: \"transparent\",\n border: 0,\n cursor: \"pointer\",\n fontSize: 12.5,\n fontWeight: 500,\n fontFamily: \"inherit\",\n color: active ? \"var(--text)\" : \"var(--text-3)\",\n whiteSpace: \"nowrap\",\n transition: \"color .12s\",\n }}\n onMouseEnter={(e) => {\n if (!active) e.currentTarget.style.color = \"var(--text-2)\";\n }}\n onMouseLeave={(e) => {\n if (!active) e.currentTarget.style.color = \"var(--text-3)\";\n }}\n >\n {icon && <DynamicIcon name={icon} size={13} />}\n {label}\n {active && (\n <span\n style={{\n position: \"absolute\",\n left: 4,\n right: 4,\n bottom: -1,\n height: 2,\n background: \"var(--accent)\",\n borderRadius: 2,\n }}\n />\n )}\n </button>\n );\n })}\n </div>\n <div role=\"tabpanel\" style={{ padding: 14 }}>\n {/* The tab panel is itself a card, so a `children`/`associated` tab\n must not draw its own. */}\n <LayoutNodeRenderer\n key={activeTab}\n node={items[activeTab]}\n ctx={{ ...ctx, withinCard: true }}\n />\n </div>\n </div>\n );\n}\n","import type { SectionNode } from \"@/types/schema\";\nimport { loc } from \"@/types/schema\";\nimport type { ComponentType } from \"react\";\nimport { Icon, SectionCard, type SectionBadge } from \"@/components/primitives\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\nimport { LayoutRenderer } from \"./LayoutRenderer\";\n\ninterface SectionBlockProps {\n config: SectionNode;\n ctx: LayoutContext;\n}\n\nexport function SectionBlock({ config, ctx }: SectionBlockProps) {\n const { name, items, collapsed: defaultCollapsed = false, columns, icon, badge, emphasis } = config;\n // Children rendered inside this section are already inside a card, so a\n // nested `children`/`associated` block must not draw its own.\n const childCtx = { ...ctx, withinCard: true, ...(columns ? { columns } : null) };\n\n const IconC = icon\n ? (Icon as Record<string, ComponentType<{ size?: number }> | undefined>)[icon]\n : undefined;\n const iconNode = IconC ? <IconC size={14} /> : undefined;\n\n const badgeProp: SectionBadge | undefined = badge\n ? { text: loc(badge.text), tone: badge.tone, dot: badge.dot }\n : undefined;\n\n return (\n <SectionCard\n title={loc(name)}\n defaultOpen={!defaultCollapsed}\n icon={iconNode}\n badge={badgeProp}\n emphasis={emphasis}\n // A section nested inside another section or a tab (ctx.withinCard) is\n // one heading level deeper than a top-level section.\n headingLevel={ctx.withinCard ? 3 : 2}\n >\n <LayoutRenderer nodes={items} ctx={childCtx} />\n </SectionCard>\n );\n}\n","import type {\n ChildDisplayMode,\n ChildQuickAdd,\n ChildRelation,\n CardsConfig,\n Entity,\n LayoutNode,\n LocalizedString,\n TimelineConfig,\n} from \"@/types/schema\";\nimport { entityAllowsMutation, getPrimaryKeyFields } from \"@/types/schema\";\n\n// ResolvedChildConfig is the fully-resolved rendering configuration for a\n// child entity on a detail screen. All optional fields are filled by the\n// three-layer precedence algorithm or auto-derivation.\nexport interface ResolvedChildConfig {\n entity: string;\n foreign_key: string | string[];\n name: LocalizedString;\n icon: string;\n mode: ChildDisplayMode;\n columns: string[];\n sort: string;\n limit: number;\n timeline?: TimelineConfig;\n cards?: CardsConfig;\n quick_add?: ChildQuickAdd;\n editable: boolean;\n position_field?: string;\n // Mirrors the schema `interaction` union. `row_edit` is honored by\n // AssociatedBlock; ChildrenTable treats any non-navigate/non-readonly value\n // as inline-editable, so a `row_edit` override resolves to inline editing\n // there until ChildrenTable grows explicit row_edit support.\n interaction: \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\";\n}\n\n// resolveChildConfig applies the three-layer precedence algorithm:\n// 1. Screen node override (highest priority)\n// 2. Entity-level child defaults\n// 3. Auto-derived defaults (lowest priority)\n//\n// Structural fields (entity, foreign_key) come from the relation and are\n// never overridable from screen layout.\nexport function resolveChildConfig(\n relation: ChildRelation,\n childEntity: Entity,\n screenNode?: LayoutNode,\n): ResolvedChildConfig {\n const display = relation.display;\n\n // Start with structural fields from the relation.\n const mode = resolveField(screenNode?.mode, display?.mode, \"table\") as ChildDisplayMode;\n // For timeline mode the user expects the LATEST N rows on open with a\n // \"load older\" affordance for history (chat semantics). 20 is too tight\n // for any record with even modest activity, and would silently hide new\n // entries once total > 20 under asc display sort. 50 is one screen of\n // chat. Other modes keep the table default of 20.\n const defaultLimit = mode === \"timeline\" ? 50 : 20;\n const result: ResolvedChildConfig = {\n entity: relation.entity,\n foreign_key: relation.foreign_key,\n name: resolveField(screenNode?.label, display?.name, inferName(childEntity)),\n icon: resolveField(screenNode?.icon, display?.icon, childEntity.display.icon) ?? \"\",\n mode,\n columns: resolveField(screenNode?.columns, display?.columns, inferColumns(childEntity, relation.foreign_key)),\n // For mode: timeline, schema `sort:` is the DISPLAY order only. The\n // timeline hook (useTimelineData) always fetches `time_field:desc`\n // regardless of this value, then ChildrenTimeline reverses in memory\n // when asc is requested.\n sort: resolveField(screenNode?.sort, display?.sort, inferSort(childEntity)),\n limit: resolveField(screenNode?.limit, display?.limit, defaultLimit),\n editable: resolveField(screenNode?.editable, display?.editable, true),\n interaction: resolveField(\n screenNode?.interaction,\n display?.interaction,\n // Default: inline_edit — entity-level `children:` declarations are\n // aggregate members of the parent by contract. They save/cancel with\n // the parent (DDD aggregate). If the child entity cannot be updated,\n // degrade to readonly. Independent-lifecycle relations are declared\n // via screen-level `associated:` layout nodes (AssociatedBlock), not\n // here. Override per-relation with `display.interaction` or per-screen\n // with the LayoutNode's `interaction`.\n inferDefaultInteraction(childEntity),\n ),\n };\n\n // Resolve position_field: screen override > entity-level > undefined.\n const posField = resolveField(screenNode?.position_field, display?.position_field, undefined as string | undefined);\n if (posField) {\n result.position_field = posField;\n // When position_field is active, sort is always position_field:asc.\n result.sort = posField + \":asc\";\n }\n\n // Resolve timeline config.\n const timelineSource = screenNode?.timeline ?? display?.timeline;\n if (result.mode === \"timeline\") {\n result.timeline = inferTimelineFields(timelineSource ?? {}, childEntity);\n } else if (timelineSource) {\n result.timeline = timelineSource;\n }\n\n // Resolve cards config.\n const cardsSource = screenNode?.cards ?? display?.cards;\n if (cardsSource) {\n result.cards = cardsSource;\n }\n\n // Resolve quick_add.\n const quickAdd = screenNode?.quick_add ?? relation.quick_add;\n if (quickAdd) {\n result.quick_add = quickAdd;\n }\n\n return result;\n}\n\n// resolveField returns the first non-nullish value from the precedence chain.\nfunction resolveField<T>(screenOverride: T | undefined, entityDefault: T | undefined, autoDefault: T): T {\n if (screenOverride != null) return screenOverride;\n if (entityDefault != null) return entityDefault;\n return autoDefault;\n}\n\nfunction inferDefaultInteraction(\n childEntity: Entity,\n): \"navigate\" | \"inline_edit\" | \"row_edit\" | \"readonly\" {\n // Entity-level `children:` is the aggregate-membership signal. Aggregate\n // children edit inline with the parent. Non-updatable child entities stay\n // readonly regardless of lifecycle.\n if (!entityAllowsMutation(childEntity, \"update\")) return \"readonly\";\n return \"inline_edit\";\n}\n\nfunction inferName(entity: Entity): LocalizedString {\n if (entity.display.name_plural && Object.keys(entity.display.name_plural).length > 0) {\n return entity.display.name_plural;\n }\n return entity.display.name;\n}\n\n// inferSort returns a default sort string using the fallback chain:\n// created_at:desc -> updated_at:desc -> first timestamp :desc ->\n// first date :desc -> primary_key:asc\nexport function inferSort(entity: Entity): string {\n if (entity.fields.get(\"created_at\")?.type === \"timestamp\") {\n return \"created_at:desc\";\n }\n if (entity.fields.get(\"updated_at\")?.type === \"timestamp\") {\n return \"updated_at:desc\";\n }\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (f && f.type === \"timestamp\") {\n return name + \":desc\";\n }\n }\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (f && f.type === \"date\") {\n return name + \":desc\";\n }\n }\n // Fall back to first primary key field.\n const pk = findPrimaryKey(entity);\n if (pk) return pk + \":asc\";\n return \"\";\n}\n\n// inferColumns returns up to 6 fields from field_order, excluding primary,\n// auto, is_listable, and the parent FK field(s).\nexport function inferColumns(entity: Entity, fk: string | string[]): string[] {\n const fkSet = new Set(Array.isArray(fk) ? fk : [fk]);\n const cols: string[] = [];\n for (const name of entity.fields.names()) {\n if (cols.length >= 6) break;\n const f = entity.fields.get(name);\n if (!f) continue;\n if (f.primary || f.auto) continue;\n if (f.display?.is_listable === false) continue;\n if (fkSet.has(name)) continue;\n cols.push(name);\n }\n return cols;\n}\n\n// inferTimelineFields fills missing timeline config fields from entity fields.\n// Missing optional fields are left empty (they are simply not rendered).\nexport function inferTimelineFields(tc: TimelineConfig, entity: Entity): TimelineConfig {\n const result: TimelineConfig = { ...tc };\n\n if (!result.time_field) {\n result.time_field = inferTimeField(entity);\n }\n if (!result.type_field) {\n result.type_field = inferFirstFieldOfType(entity, \"enum\");\n }\n if (!result.summary_field) {\n result.summary_field = inferFirstFieldOfTypes(entity, \"string\", \"text\", \"rich_text\");\n }\n if (!result.content_field) {\n result.content_field = inferFirstFieldOfTypes(entity, \"rich_text\", \"text\");\n }\n // The summary slot is a short heading rendered with fontWeight:500; the\n // content slot is the full body rendered with fontWeight:400. When they\n // collapse to the same field (entities with a single text column like\n // `activity.content`), rendering both would either duplicate the body or,\n // with the \"contentField !== summaryField\" guard in ChildrenTimeline,\n // suppress the content entry so the body is always bold. Clear the\n // summary slot so such entities render their body with normal weight.\n // Does not affect entities that declare distinct summary_field /\n // content_field in YAML, nor entities with both a short string field\n // (subject / title) and a long text field (body / description).\n if (\n result.summary_field &&\n result.content_field &&\n result.summary_field === result.content_field\n ) {\n result.summary_field = undefined;\n }\n if (!result.author_field) {\n if (entity.fields.get(\"created_by\")) {\n result.author_field = \"created_by\";\n }\n }\n if (!result.direction_field) {\n result.direction_field = inferDirectionField(entity);\n }\n if (!result.intent_field) {\n result.intent_field = inferIntentField(entity);\n }\n\n return result;\n}\n\nfunction inferDirectionField(entity: Entity): string | undefined {\n const direction = entity.fields.get(\"direction\");\n if (direction && direction.type === \"enum\") return \"direction\";\n for (const name of [\"is_outbound\", \"outbound\", \"is_inbound\", \"inbound\"]) {\n const f = entity.fields.get(name);\n if (f && f.type === \"bool\") return name;\n }\n return undefined;\n}\n\nfunction inferIntentField(entity: Entity): string | undefined {\n const intent = entity.fields.get(\"intent\");\n if (intent && intent.type === \"enum\") return \"intent\";\n return undefined;\n}\n\nfunction inferTimeField(entity: Entity): string | undefined {\n // Prefer explicit (non-auto) timestamps first.\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (f && f.type === \"timestamp\" && !f.auto) {\n return name;\n }\n }\n // Fall back to auto timestamps: created_at/updated_at first, then any.\n if (entity.fields.get(\"created_at\")?.type === \"timestamp\") return \"created_at\";\n if (entity.fields.get(\"updated_at\")?.type === \"timestamp\") return \"updated_at\";\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (f && f.type === \"timestamp\") return name;\n }\n return undefined;\n}\n\nfunction inferFirstFieldOfType(entity: Entity, type: string): string | undefined {\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (f && f.type === type) {\n return name;\n }\n }\n return undefined;\n}\n\nfunction inferFirstFieldOfTypes(entity: Entity, ...types: string[]): string | undefined {\n for (const name of entity.fields.names()) {\n const f = entity.fields.get(name);\n if (!f) continue;\n for (const t of types) {\n if (f.type === t) return name;\n }\n }\n return undefined;\n}\n\n// findPrimaryKey returns the first primary key field name.\n// Supports composite PK via entity.primary_key (returns first element).\n// Used for backward-compat scenarios where a single field name is needed\n// (e.g. sort fallback). For full composite PK support, use getPrimaryKeyFields().\nexport function findPrimaryKey(entity: Entity): string | undefined {\n const pks = getPrimaryKeyFields(entity);\n return pks.length > 0 ? pks[0] : undefined;\n}\n\n// findPrimaryKeys returns all primary key field names as an array.\n// Delegates to getPrimaryKeyFields from schema types.\nexport function findPrimaryKeys(entity: Entity): string[] {\n return getPrimaryKeyFields(entity);\n}\n\n// findChildRelation looks up a child relation by entity code on the parent entity.\nexport function findChildRelation(parentEntity: Entity, childCode: string): ChildRelation | undefined {\n return parentEntity.children?.find((c) => c.entity === childCode);\n}\n","import { pkValuesFromId } from \"@/types/schema\";\n\n// buildQuickAddPayload builds the POST body for a quick-add submission.\n//\n// Precedence (highest first):\n// 1. FK auto-injection - always set, overwrites user input and defaults\n// 2. User field values - user input wins over defaults\n// 3. Configured defaults - from quick_add.defaults\n//\n// The FK is intentionally never shown as an input field and must always\n// reflect the current parent record's ID.\nexport function buildQuickAddPayload(\n fieldValues: Record<string, unknown>,\n defaults: Record<string, unknown> | undefined,\n foreignKey: string | string[],\n parentId: string | string[],\n): Record<string, unknown> {\n const result: Record<string, unknown> = {\n ...(defaults ?? {}),\n ...fieldValues,\n };\n if (Array.isArray(foreignKey)) {\n const ids = Array.isArray(parentId)\n ? parentId.map(String)\n : pkValuesFromId(String(parentId), foreignKey.length);\n if (!ids || ids.length !== foreignKey.length) {\n throw new Error(\"invalid composite parent id\");\n }\n for (let i = 0; i < foreignKey.length; i++) {\n result[foreignKey[i]] = ids[i];\n }\n } else {\n result[foreignKey] = Array.isArray(parentId) ? parentId[0] : parentId;\n }\n return result;\n}\n\n// isQuickAddReady returns true when all quick_add.fields with required entity\n// fields have non-empty values. Fields that are not marked required in the\n// entity definition are always considered satisfied.\nexport function isQuickAddReady(\n fieldValues: Record<string, unknown>,\n requiredFields: string[],\n): boolean {\n for (const name of requiredFields) {\n const val = fieldValues[name];\n if (val == null || val === \"\") return false;\n }\n return true;\n}\n","import { useState, type FormEvent } from \"react\";\nimport { useMutation, useQueryClient, type QueryKey } from \"@tanstack/react-query\";\nimport { createEntity } from \"@/api/data\";\nimport { buildQuickAddPayload, isQuickAddReady } from \"@/lib/quick-add\";\nimport { ENTITY_CHILDREN_KEY } from \"@/hooks/useChildrenData\";\nimport { pkValuePartsToId } from \"@/types/schema\";\nimport type { ResolvedChildConfig } from \"@/lib/child-config\";\nimport type { Schema } from \"@/types/schema\";\nimport type { V2ListResponse } from \"@/types/api\";\nimport { formatWireDateTime } from \"@/lib/timestamp\";\n\nexport interface UseQuickAddResult {\n values: Record<string, unknown>;\n quickAdd: ResolvedChildConfig[\"quick_add\"];\n childEntity: ChildEntity;\n isPending: boolean;\n error: Error | null;\n isReady: boolean;\n handleSubmit: (e: FormEvent) => void;\n handleFieldChange: (fieldName: string, val: unknown) => void;\n}\n\ntype ChildEntity = Schema[\"entities\"][string] | undefined;\n\n// useQuickAdd encapsulates all shared state and mutation logic for quick-add\n// forms. It is used by QuickAddForm (table/list/cards) and TimelineComposer\n// (timeline). All hooks are called unconditionally before any early returns\n// in the consuming component.\nexport function useQuickAdd(\n config: ResolvedChildConfig,\n parentEntity: string,\n parentId: string | string[],\n schema: Schema,\n): UseQuickAddResult {\n const [values, setValues] = useState<Record<string, unknown>>(() => {\n const qa0 = config.quick_add;\n const ce = schema.entities[config.entity];\n const initialValues: Record<string, unknown> = {};\n if (qa0?.fields) {\n for (const f of qa0.fields) {\n if (qa0.defaults?.[f] != null) {\n initialValues[f] = qa0.defaults[f];\n } else if (ce?.fields.get(f)?.type === \"timestamp\") {\n // Default timestamp fields to now as bare local datetime.\n // The server interprets this in the field's configured timezone.\n initialValues[f] = formatWireDateTime();\n }\n }\n }\n return initialValues;\n });\n const queryClient = useQueryClient();\n const { mutate, isPending, error, reset } = useMutation({\n mutationFn: (payload: Record<string, unknown>) => createEntity(config.entity, payload),\n onMutate: async (payload: Record<string, unknown>) => {\n // Optimistic insert: prepend a temp row to every cached infinite-\n // query under this prefix so the timeline shows the user's entry\n // before the network round-trip resolves. Real row replaces the\n // temp on the trailing invalidate (onSettled). The temp row carries\n // `__optimistic: true` and a synthetic id so ChildrenTimeline can\n // dim it and suppress edit/delete (those mutations would 404).\n const normalizedParentId = Array.isArray(parentId)\n ? pkValuePartsToId(parentId.map(String))\n : parentId;\n const keyPrefix: QueryKey = [\n ENTITY_CHILDREN_KEY,\n parentEntity,\n normalizedParentId,\n config.entity,\n ];\n\n await queryClient.cancelQueries({ queryKey: keyPrefix });\n const snapshots = queryClient.getQueriesData<unknown>({ queryKey: keyPrefix });\n\n const optimisticRow: Record<string, unknown> = {\n ...payload,\n id: `__optimistic_${crypto.randomUUID()}`,\n __optimistic: true,\n };\n\n queryClient.setQueriesData<unknown>({ queryKey: keyPrefix }, (old: unknown) => {\n if (!old || typeof old !== \"object\") return old;\n // useInfiniteQuery cache shape: { pages: V2ListResponse[], pageParams: unknown[] }.\n // Page 0 is the newest window; prepend the optimistic row there.\n if (\n \"pages\" in old &&\n Array.isArray((old as { pages?: unknown[] }).pages) &&\n (old as { pages: unknown[] }).pages.length > 0\n ) {\n const infinite = old as { pages: V2ListResponse[]; pageParams: unknown[] };\n const [first, ...rest] = infinite.pages;\n return {\n ...infinite,\n pages: [\n { ...first, data: [optimisticRow, ...first.data] },\n ...rest,\n ],\n };\n }\n // Other cache shapes (v1 ListResponse used by useChildrenData):\n // leave untouched. The trailing invalidate will reconcile them.\n return old;\n });\n\n return { snapshots };\n },\n onError: (_err, _payload, ctx) => {\n if (!ctx?.snapshots) return;\n for (const [key, data] of ctx.snapshots) {\n queryClient.setQueryData(key, data);\n }\n },\n onSuccess: () => {\n // Reset all visible fields to their defaults (or empty), clearing any\n // user-typed values for fields that have no configured default.\n const qa = config.quick_add;\n const ce = schema.entities[config.entity];\n const resetValues: Record<string, unknown> = {};\n if (qa?.fields) {\n for (const f of qa.fields) {\n if (qa?.defaults?.[f] != null) {\n resetValues[f] = qa.defaults[f];\n } else if (ce?.fields.get(f)?.type === \"timestamp\") {\n resetValues[f] = formatWireDateTime();\n } else {\n resetValues[f] = \"\";\n }\n }\n }\n setValues(resetValues);\n },\n onSettled: () => {\n const normalizedParentId = Array.isArray(parentId)\n ? pkValuePartsToId(parentId.map(String))\n : parentId;\n queryClient.invalidateQueries({\n queryKey: [ENTITY_CHILDREN_KEY, parentEntity, normalizedParentId, config.entity],\n });\n },\n });\n\n const quickAdd = config.quick_add;\n const childEntity = schema.entities[config.entity];\n\n // Derive required fields from the quick_add field list.\n // Only fields marked required on the entity definition are checked.\n const requiredFields = quickAdd?.fields.filter(\n (f) => childEntity?.fields.get(f)?.required,\n ) ?? [];\n const isReady = isQuickAddReady(values, requiredFields);\n\n function handleSubmit(e: FormEvent) {\n e.preventDefault();\n if (!quickAdd) return;\n const payload = buildQuickAddPayload(values, quickAdd.defaults, config.foreign_key, parentId);\n mutate(payload);\n }\n\n function handleFieldChange(fieldName: string, val: unknown) {\n if (error) reset();\n setValues((prev: Record<string, unknown>) => ({ ...prev, [fieldName]: val }));\n }\n\n return {\n values,\n quickAdd,\n childEntity,\n isPending,\n error,\n isReady,\n handleSubmit,\n handleFieldChange,\n };\n}\n","import { Btn, Icon } from \"@/components/primitives\";\nimport { fieldDisplayName } from \"@/types/schema\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { useQuickAdd } from \"./useQuickAdd\";\nimport type { ResolvedChildConfig } from \"@/lib/child-config\";\nimport type { Schema } from \"@/types/schema\";\n\ninterface QuickAddFormProps {\n config: ResolvedChildConfig;\n parentEntity: string;\n parentId: string | string[];\n schema: Schema;\n}\n\nexport function QuickAddForm({ config, parentEntity, parentId, schema }: QuickAddFormProps) {\n const { values, quickAdd, childEntity, isPending, error, isReady, handleSubmit, handleFieldChange } =\n useQuickAdd(config, parentEntity, parentId, schema);\n const userLocale = useUserLocale();\n\n if (!quickAdd || quickAdd.fields.length === 0) return null;\n if (!childEntity) return null;\n\n return (\n <form\n onSubmit={handleSubmit}\n style={{\n display: \"flex\",\n alignItems: \"flex-end\",\n gap: 8,\n padding: \"8px 0\",\n borderBottom: \"1px solid var(--divider)\",\n }}\n >\n {quickAdd.fields.map((fieldName) => {\n const field = childEntity.fields.get(fieldName);\n if (!field) return null;\n return (\n <div key={fieldName} style={{ flex: 1, minWidth: 0 }}>\n <label\n style={{\n fontSize: 11,\n color: \"var(--text-3)\",\n marginBottom: 4,\n display: \"block\",\n fontWeight: 500,\n }}\n >\n {fieldDisplayName(field, fieldName, schema.entities)}\n </label>\n {renderField({\n field,\n fieldName,\n value: values[fieldName] ?? \"\",\n mode: \"edit\",\n onChange: (val) => handleFieldChange(fieldName, val),\n record: values,\n locale: userLocale,\n })}\n </div>\n );\n })}\n <Btn\n type=\"submit\"\n kind=\"primary\"\n icon={<Icon.plus size={12} />}\n disabled={isPending || !isReady}\n >\n Add\n </Btn>\n {error != null && (\n <span style={{ fontSize: 11, color: \"var(--danger)\", alignSelf: \"flex-end\", marginBottom: 4 }}>\n {error.message}\n </span>\n )}\n </form>\n );\n}\n","import { useMemo, useRef } from \"react\";\nimport { fieldDisplayName, resolveDetailRoute } from \"@/types/schema\";\nimport {\n resolveCellRenderer,\n type CellRenderer,\n} from \"@/components/fields/cells/registry\";\nimport { useChildrenData } from \"@/hooks/useChildrenData\";\nimport { findPrimaryKey } from \"@/lib/child-config\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { Icon, IconBtn, Pill, Spinner } from \"@/components/primitives\";\nimport {\n dataTableStyle,\n dataTableHeadCellStyle,\n dataTableCellStyle,\n} from \"@/components/primitives/dataTableStyles\";\nimport { useRealtimeStore, isEntityLive } from \"@/stores/realtime\";\nimport { QuickAddForm } from \"./QuickAddForm\";\nimport type { ChildDisplayProps } from \"./ChildrenBlock\";\n\nexport function ChildrenTable({ config, parentEntity, parentId, schema }: ChildDisplayProps) {\n const childEntity = schema.entities[config.entity];\n // Plan §3 contract: locale flows from useUserLocale through every direct\n // resolveCellRenderer / renderField call site so cell factories format\n // consistently across the surface area.\n const userLocale = useUserLocale();\n const degradedEntities = useRealtimeStore((s) => s.degradedEntities);\n const { data: response, isLoading, isError, isFetching, refetch } = useChildrenData(\n parentEntity,\n parentId,\n config.entity,\n config.foreign_key,\n config.sort,\n config.limit,\n );\n const rows = response?.data ?? [];\n const refs = response?.$refs;\n // 1a.5: refs change per fetch but we cache cell renderers per column. Pass\n // a getter so the cached factory always reads the latest refs.\n const refsRef = useRef(refs);\n refsRef.current = refs;\n const columns = config.columns;\n const interaction = config.interaction;\n const navigate = interaction === \"navigate\";\n // Identity column (first configured column) acts as the link target.\n const identityCol = columns[0];\n // Resolution honors `entity.detail_screen` (canonical pointer in the schema),\n // so any screen type — including `custom` — works as long as the entity\n // declares it.\n const detailRoute = navigate && childEntity\n ? resolveDetailRoute(undefined, childEntity, schema.screens)\n : null;\n const pk = childEntity ? findPrimaryKey(childEntity) : undefined;\n\n // Resolve cell renderers ONCE per column so the per-row map is a single\n // bound-function call (no widget/type dispatch in the row loop). Per plan\n // §3 the dispatcher is total: the engine stamps a smart default at\n // YAML-load time and the registry covers every allow-listed widget, so\n // resolveCellRenderer always returns a valid renderer. No fallback path.\n const cellRenderers = useMemo<Map<string, CellRenderer>>(() => {\n const map = new Map<string, CellRenderer>();\n if (!childEntity) return map;\n for (const col of columns) {\n const field = childEntity.fields.get(col);\n if (!field) continue;\n map.set(\n col,\n resolveCellRenderer({\n field,\n fieldName: col,\n surface: \"list\",\n entity: childEntity,\n entities: schema.entities,\n screens: schema.screens,\n refs: () => refsRef.current,\n locale: userLocale,\n }),\n );\n }\n return map;\n }, [childEntity, columns, schema.entities, schema.screens, userLocale]);\n\n if (!childEntity) {\n return <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>Entity not found.</div>;\n }\n\n const quickAddForm = (\n <QuickAddForm config={config} parentEntity={parentEntity} parentId={parentId} schema={schema} />\n );\n\n // Embedded child tables have no view-controls row of their own, so the\n // refresh control is the table's only toolbar. It is always present: it\n // reloads the child query, and for a non-live child entity (realtime:\n // false, or auto-degraded by the relay) it is the only freshness\n // mechanism. The \"Live\" pill mirrors the SmartListPage affordance.\n const childIsLive = isEntityLive(childEntity.realtime, degradedEntities, config.entity);\n const tableToolbar = (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, padding: \"4px 0\", justifyContent: \"flex-end\" }}>\n {childIsLive && (\n <span\n data-testid=\"children-live-indicator\"\n title=\"This table updates automatically in realtime.\"\n >\n <Pill tone=\"emerald\" size=\"xs\" dot>\n Live\n </Pill>\n </span>\n )}\n {/* While a fetch is in flight (cold load or manual refetch) the icon\n swaps to a Spinner so the user sees the refresh is running; the\n button is already disabled on isFetching so it cannot re-trigger. */}\n <IconBtn\n icon={\n isFetching ? (\n <span data-testid=\"children-refresh-spinner\" style={{ display: \"inline-flex\" }}>\n <Spinner size={13} />\n </span>\n ) : (\n <Icon.refresh size={13} />\n )\n }\n title=\"Refresh\"\n size={24}\n disabled={isFetching}\n onClick={() => { void refetch(); }}\n />\n </div>\n );\n\n if (isError) {\n return (\n <div>\n {quickAddForm}\n {tableToolbar}\n <div style={{ fontSize: 12.5, color: \"var(--danger)\", padding: \"8px 0\" }}>\n Failed to load. Try again.\n </div>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div>\n {quickAddForm}\n {tableToolbar}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, padding: \"8px 0\" }}>\n {[0, 1, 2].map((i) => (\n <div key={i} style={{ height: 32, background: \"var(--surface-3)\", borderRadius: 4 }} />\n ))}\n </div>\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div>\n {quickAddForm}\n {tableToolbar}\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"12px 0\" }}>No records yet.</div>\n </div>\n );\n }\n\n return (\n <div>\n {quickAddForm}\n {tableToolbar}\n <div style={{ overflowX: \"auto\" }}>\n <table style={dataTableStyle}>\n <thead>\n <tr>\n {columns.map((col) => {\n const field = childEntity.fields.get(col);\n return (\n <th key={col} style={dataTableHeadCellStyle}>\n {field ? fieldDisplayName(field, col, schema.entities) : col}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, rowIdx) => {\n const rowPk = pk ? String((row as Record<string, unknown>)[pk] ?? \"\") : \"\";\n const href = navigate && detailRoute && rowPk\n ? detailRoute.replace(/:\\w+/, rowPk)\n : undefined;\n return (\n <tr key={String((row as Record<string, unknown>)[\"id\"] ?? rowIdx)}>\n {columns.map((col) => {\n const isIdentity = col === identityCol;\n // Per plan §3 the dispatcher is total: every column\n // configured here is an entity field (filtered above) so\n // the renderer is always present. Missing renderer signals\n // a misconfigured column code and falls through as raw\n // text rather than throwing during a render.\n const cellRenderer = cellRenderers.get(col);\n const rendered = cellRenderer\n ? cellRenderer(row[col], row as Record<string, unknown>)\n : String(row[col] ?? \"\");\n // When the renderer returns a primitive string the table\n // applies overflow:hidden + textOverflow:ellipsis at the\n // `<td>` layer, so a `title=` here surfaces the full value\n // on hover. JSX returns own their own tooltips.\n // See docs/plans/2026-05-09-virtual-list-100k.md §2.\n const title = typeof rendered === \"string\" ? rendered : undefined;\n return (\n <td key={col} title={title} style={dataTableCellStyle}>\n {isIdentity && href ? (\n // Plain real <a href>: the global link interceptor\n // (Layout) catches the plain click and routes it through\n // useTabNavigate - opening / reusing an in-app tab in\n // standalone, or the embed chokepoint in embed. Modifier\n // / middle clicks stay native. One uniform mechanism.\n <a\n href={href}\n style={{ color: \"var(--accent)\", textDecoration: \"none\", fontWeight: 500 }}\n onMouseEnter={(e) => (e.currentTarget.style.textDecoration = \"underline\")}\n onMouseLeave={(e) => (e.currentTarget.style.textDecoration = \"none\")}\n >\n {rendered}\n </a>\n ) : (\n rendered\n )}\n </td>\n );\n })}\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n","import { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { useChildrenData } from \"@/hooks/useChildrenData\";\nimport { DISPLAY_KEY } from \"@/types/schema\";\nimport { QuickAddForm } from \"./QuickAddForm\";\nimport type { ChildDisplayProps } from \"./ChildrenBlock\";\n\nexport function ChildrenList({ config, parentEntity, parentId, schema }: ChildDisplayProps) {\n const childEntity = schema.entities[config.entity];\n const userLocale = useUserLocale();\n const { data: response, isLoading, isError } = useChildrenData(\n parentEntity,\n parentId,\n config.entity,\n config.foreign_key,\n config.sort,\n config.limit,\n );\n const rows = response?.data ?? [];\n const refs = response?.$refs;\n const columns = config.columns;\n\n if (!childEntity) {\n return <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>Entity not found.</div>;\n }\n\n const quickAddForm = (\n <QuickAddForm config={config} parentEntity={parentEntity} parentId={parentId} schema={schema} />\n );\n\n if (isError) {\n return (\n <div>\n {quickAddForm}\n <div style={{ fontSize: 12.5, color: \"var(--danger)\", padding: \"8px 0\" }}>\n Failed to load. Try again.\n </div>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div>\n {quickAddForm}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, padding: \"8px 0\" }}>\n <div style={{ height: 22, background: \"var(--surface-3)\", borderRadius: 4 }} />\n <div style={{ height: 22, background: \"var(--surface-3)\", borderRadius: 4 }} />\n <div style={{ height: 22, background: \"var(--surface-3)\", borderRadius: 4, width: \"75%\" }} />\n </div>\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div>\n {quickAddForm}\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"12px 0\" }}>No records yet.</div>\n </div>\n );\n }\n\n const displayField =\n columns.length > 0 ? childEntity.display.display_field ?? columns[0] : childEntity.display.display_field;\n\n return (\n <div>\n {quickAddForm}\n <div style={{ display: \"flex\", flexDirection: \"column\" }}>\n {rows.map((row, idx) => {\n // Server-rendered display template wins over raw display_field.\n // When $display is present we ship the literal string and skip\n // renderField — templates already encode the desired stringification.\n const rendered = typeof row[DISPLAY_KEY] === \"string\" ? (row[DISPLAY_KEY] as string) : \"\";\n return (\n <div\n key={String((row as Record<string, unknown>)[\"id\"] ?? idx)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 12,\n padding: \"8px 4px\",\n borderBottom: \"1px solid var(--divider)\",\n }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 12.5, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>\n {rendered\n ? rendered\n : displayField && childEntity.fields.get(displayField)\n ? renderField({\n field: childEntity.fields.get(displayField)!,\n fieldName: displayField,\n value: row[displayField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })\n : displayField && row[displayField] != null\n ? String(row[displayField])\n : null}\n </div>\n {columns.length > 1 &&\n (() => {\n const subCols = columns.slice(1, 3).filter((col) => row[col] != null);\n if (subCols.length === 0) return null;\n return (\n <div\n style={{\n fontSize: 11.5,\n color: \"var(--text-3)\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {subCols.map((col, i) => (\n <span key={col}>\n {i > 0 && \" · \"}\n {childEntity.fields.get(col)\n ? renderField({\n field: childEntity.fields.get(col)!,\n fieldName: col,\n value: row[col],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })\n : String(row[col])}\n </span>\n ))}\n </div>\n );\n })()}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","import { Pill } from \"@/components/primitives\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { useChildrenData } from \"@/hooks/useChildrenData\";\nimport { QuickAddForm } from \"./QuickAddForm\";\nimport type { ChildDisplayProps } from \"./ChildrenBlock\";\n\nexport function ChildrenCards({ config, parentEntity, parentId, schema }: ChildDisplayProps) {\n const childEntity = schema.entities[config.entity];\n const userLocale = useUserLocale();\n const { data: response, isLoading, isError } = useChildrenData(\n parentEntity,\n parentId,\n config.entity,\n config.foreign_key,\n config.sort,\n config.limit,\n );\n const rows = response?.data ?? [];\n const refs = response?.$refs;\n const columns = config.columns;\n const cardsConfig = config.cards;\n const headerField = cardsConfig?.header_field ?? columns[0];\n const badgeField = cardsConfig?.badge_field;\n const colCount = cardsConfig?.columns_count ?? 3;\n\n if (!childEntity) {\n return <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>Entity not found.</div>;\n }\n\n const gridStyle: React.CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: `repeat(auto-fill, minmax(${colCount >= 4 ? 180 : 240}px, 1fr))`,\n gap: 12,\n };\n\n const quickAddForm = (\n <QuickAddForm config={config} parentEntity={parentEntity} parentId={parentId} schema={schema} />\n );\n\n if (isError) {\n return (\n <div>\n {quickAddForm}\n <div style={{ fontSize: 12.5, color: \"var(--danger)\", padding: \"8px 0\" }}>\n Failed to load. Try again.\n </div>\n </div>\n );\n }\n\n if (isLoading) {\n return (\n <div>\n {quickAddForm}\n <div style={gridStyle}>\n {[0, 1, 2].map((i) => (\n <div key={i} style={{ height: 96, background: \"var(--surface-3)\", borderRadius: 10 }} />\n ))}\n </div>\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div>\n {quickAddForm}\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"12px 0\" }}>No records yet.</div>\n </div>\n );\n }\n\n const detailFields = columns.filter((c) => c !== headerField && c !== badgeField);\n\n return (\n <div>\n {quickAddForm}\n <div style={gridStyle}>\n {rows.map((row, idx) => (\n <div\n key={String((row as Record<string, unknown>)[\"id\"] ?? idx)}\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 10,\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n padding: \"10px 14px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 8,\n borderBottom: detailFields.length > 0 ? \"1px solid var(--divider)\" : \"none\",\n }}\n >\n <div style={{ fontSize: 12.5, fontWeight: 600, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>\n {headerField && childEntity.fields.get(headerField)\n ? renderField({\n field: childEntity.fields.get(headerField)!,\n fieldName: headerField,\n value: row[headerField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })\n : `Record ${idx + 1}`}\n </div>\n {badgeField && row[badgeField] != null && (\n <Pill tone=\"slate\" size=\"xs\">\n {childEntity.fields.get(badgeField)\n ? renderField({\n field: childEntity.fields.get(badgeField)!,\n fieldName: badgeField,\n value: row[badgeField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })\n : String(row[badgeField])}\n </Pill>\n )}\n </div>\n {detailFields.length > 0 && (\n <div style={{ padding: \"10px 14px\", display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n {detailFields.slice(0, 4).map((col) => {\n const field = childEntity.fields.get(col);\n if (!field) return null;\n return (\n <div\n key={col}\n style={{\n display: \"flex\",\n alignItems: \"baseline\",\n gap: 8,\n fontSize: 11.5,\n }}\n >\n <span style={{ color: \"var(--text-3)\", flexShrink: 0 }}>\n {schema.entities[config.entity]?.fields.get(col)?.display?.name\n ? Object.values(schema.entities[config.entity].fields.get(col)!.display!.name)[0]\n : col}\n :\n </span>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>\n {renderField({\n field,\n fieldName: col,\n value: row[col],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </span>\n </div>\n );\n })}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n );\n}\n","import { useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { apiFetch } from \"@/api/client\";\nimport type { V2ListResponse } from \"@/types/api\";\nimport { pkValuesFromId } from \"@/types/schema\";\nimport { ENTITY_CHILDREN_KEY } from \"./useChildrenData\";\n\nconst DEFAULT_TIMELINE_LIMIT = 50;\n\n// useTimelineData fetches child entity records for a parent record using\n// cursor-based pagination, always anchored to the newest entries.\n//\n// Fetch order is always `${timeField}:desc, limit=N` regardless of the\n// caller's display sort. The consumer (ChildrenTimeline) reverses the rows\n// in memory when the schema requests asc display order. This split is the\n// whole point of the hook: the API window is \"the latest N\", and \"load\n// older\" appends older pages, regardless of how the rail renders them\n// (chat-style asc or news-feed desc).\n//\n// The query key preserves the 4-element prefix\n// `[ENTITY_CHILDREN_KEY, parentEntity, parentId, childCode]` so existing\n// prefix-match invalidations (useMutations, useSSE, useQuickAdd,\n// TimelineEntry edit/delete) keep working unchanged. The literal\n// \"timeline\" at index 4 disambiguates from the v1 useChildrenData cache\n// (which uses sort string at index 4) for the same (parent, child) pair.\nexport function useTimelineData(\n parentEntity: string,\n parentId: string,\n childCode: string,\n foreignKey: string | string[],\n timeField: string | undefined,\n limit?: number,\n) {\n const fkArr = Array.isArray(foreignKey) ? foreignKey : [foreignKey];\n const idArr = Array.isArray(foreignKey) ? pkValuesFromId(parentId, fkArr.length) : [parentId];\n const effectiveLimit = limit ?? DEFAULT_TIMELINE_LIMIT;\n\n const query = useInfiniteQuery({\n queryKey: [\n ENTITY_CHILDREN_KEY,\n parentEntity,\n parentId,\n childCode,\n \"timeline\",\n timeField,\n effectiveLimit,\n ],\n queryFn: ({ pageParam }) => {\n const params = new URLSearchParams();\n if (!idArr) throw new Error(\"invalid composite parent id\");\n const filterNodes = fkArr.map((fk, i) => ({ field: fk, op: \"eq\", value: idArr[i] }));\n params.set(\"filters\", JSON.stringify(filterNodes));\n params.set(\"preview\", \"true\");\n if (timeField) {\n params.set(\"sort\", `${timeField}:desc`);\n } else if (typeof console !== \"undefined\") {\n // Without a time field we can't anchor to \"newest\"; downstream\n // sorts by primary key. Keep a warning so the YAML author can fix\n // the timeline declaration rather than silently see arbitrary\n // ordering.\n console.warn(\n `[useTimelineData] no time_field for ${childCode}; fetch order falls back to API default`,\n );\n }\n params.set(\"limit\", String(effectiveLimit));\n if (pageParam) params.set(\"after\", pageParam);\n return apiFetch<V2ListResponse>(`/api/data/${childCode}?${params.toString()}`);\n },\n getNextPageParam: (lastPage) =>\n lastPage.meta?.has_more ? lastPage.meta.cursor : undefined,\n initialPageParam: undefined as string | undefined,\n enabled:\n !!parentEntity && !!parentId && !!childCode && !!foreignKey && idArr !== null,\n });\n\n const rows = useMemo(\n () => query.data?.pages.flatMap((p) => p.data) ?? [],\n [query.data?.pages],\n );\n\n // Refs from page 1 are sufficient for the common case (refs are entity-\n // keyed lookup tables, not per-row). If older pages introduce new ref\n // targets and the displayed enums/links go blank, switch to a shallow\n // merge across pages here.\n const refs = query.data?.pages[0]?.$refs;\n\n return {\n rows,\n refs,\n fetchNextPage: query.fetchNextPage,\n hasOlder: !!query.hasNextPage,\n isFetchingOlder: query.isFetchingNextPage,\n isLoading: query.isLoading,\n isError: query.isError,\n };\n}\n","import { useState, useCallback, useRef, useEffect, useMemo } from \"react\";\nimport { Btn, Icon } from \"@/components/primitives\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { useQuickAdd } from \"./useQuickAdd\";\nimport { registerFieldRegion } from \"@/lib/field-regions\";\nimport type { ResolvedChildConfig } from \"@/lib/child-config\";\nimport type { Schema } from \"@/types/schema\";\n\n// QUICK_ENTRY_REGION is the registered field-region name for the activity\n// composer. ui_steps reference it as \"quick_entry.FIELD\" (e.g. quick_entry.body).\nconst QUICK_ENTRY_REGION = \"quick_entry\";\n\ninterface TimelineComposerProps {\n config: ResolvedChildConfig;\n parentEntity: string;\n parentId: string | string[];\n schema: Schema;\n}\n\nconst MIN_HEIGHT = 100;\nconst DEFAULT_HEIGHT = 240;\n// Absolute ceiling for extreme cases. In practice the dynamic cap below\n// (parent panel height minus reserve) will hit first.\nconst MAX_HEIGHT_ABSOLUTE = 800;\n// Reserve above the composer so timeline entries remain visible when the\n// composer is dragged to its max size.\nconst PARENT_RESERVE_PX = 80;\n\n// TimelineComposer renders a resizable bottom composer for timeline mode.\n// Sits at the bottom of the Activity panel. Drag the top edge to resize upward.\nexport function TimelineComposer({ config, parentEntity, parentId, schema }: TimelineComposerProps) {\n const { values, quickAdd, childEntity, isPending, error, isReady, handleSubmit, handleFieldChange } =\n useQuickAdd(config, parentEntity, parentId, schema);\n const userLocale = useUserLocale();\n\n const [height, setHeight] = useState(DEFAULT_HEIGHT);\n const dragging = useRef<{ startY: number; startHeight: number } | null>(null);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [maxHeight, setMaxHeight] = useState(MAX_HEIGHT_ABSOLUTE);\n\n // Track parent panel size so the composer never grows past the available\n // timeline area. The composer is mounted as the last child of a flex-column\n // parent; we cap drag-expansion at parent.height - reserve.\n useEffect(() => {\n const root = rootRef.current;\n const parent = root?.parentElement;\n if (!parent) return;\n const update = () => {\n const cap = Math.max(MIN_HEIGHT, parent.clientHeight - PARENT_RESERVE_PX);\n setMaxHeight(Math.min(MAX_HEIGHT_ABSOLUTE, cap));\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(parent);\n return () => ro.disconnect();\n }, []);\n\n // Clamp current height whenever the cap shrinks (e.g. window resize).\n useEffect(() => {\n setHeight((h) => Math.min(h, maxHeight));\n }, [maxHeight]);\n\n // Stable refs for the field region. React state closures would go stale\n // inside the region handle; refs always read the current value.\n const valuesRef = useRef(values);\n valuesRef.current = values;\n const handleFieldChangeRef = useRef(handleFieldChange);\n handleFieldChangeRef.current = handleFieldChange;\n const fieldNodesRef = useRef<Record<string, HTMLElement | null>>({});\n\n const region = useMemo(() => ({\n getField: (fieldName: string) => valuesRef.current[fieldName],\n setField: (fieldName: string, value: unknown) => {\n handleFieldChangeRef.current(fieldName, value);\n },\n focusField: (fieldName: string) => {\n const node = fieldNodesRef.current[fieldName];\n if (!node) return;\n const focusable = node.querySelector<HTMLElement>(\"textarea, input, select, [contenteditable]\");\n (focusable ?? node).focus();\n },\n }), []);\n\n useEffect(() => registerFieldRegion(QUICK_ENTRY_REGION, region), [region]);\n\n const onDragStart = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n dragging.current = { startY: e.clientY, startHeight: height };\n\n const onMove = (ev: MouseEvent) => {\n if (!dragging.current) return;\n const delta = dragging.current.startY - ev.clientY;\n const next = Math.min(maxHeight, Math.max(MIN_HEIGHT, dragging.current.startHeight + delta));\n setHeight(next);\n };\n\n const onUp = () => {\n dragging.current = null;\n document.removeEventListener(\"mousemove\", onMove);\n document.removeEventListener(\"mouseup\", onUp);\n };\n\n document.addEventListener(\"mousemove\", onMove);\n document.addEventListener(\"mouseup\", onUp);\n },\n [height, maxHeight],\n );\n\n if (!quickAdd || quickAdd.fields.length === 0) return null;\n if (!childEntity) return null;\n\n // Partition the configured quick_add.fields into three zones so the\n // composer reads top-to-bottom as \"classify → write → timestamp+send\":\n // topFields — enum comboboxes (channel, intent, direction, …)\n // contentField — the one text/string field that gets the textarea\n // bottomFields — everything else (typically just the timestamp)\n const contentField = quickAdd.fields.find((f) => {\n const field = childEntity.fields.get(f);\n return field && (field.type === \"text\" || field.type === \"string\");\n });\n const topFields: string[] = [];\n const bottomFields: string[] = [];\n for (const f of quickAdd.fields) {\n if (f === contentField) continue;\n const field = childEntity.fields.get(f);\n if (!field) continue;\n if (field.type === \"enum\") {\n topFields.push(f);\n } else {\n bottomFields.push(f);\n }\n }\n\n const renderMetaField = (fieldName: string) => {\n const field = childEntity.fields.get(fieldName);\n if (!field) return null;\n return (\n <div key={fieldName} ref={(el) => { fieldNodesRef.current[fieldName] = el; }}>\n {renderField({\n field,\n fieldName,\n value: values[fieldName] ?? \"\",\n mode: \"edit\",\n onChange: (val) => handleFieldChange(fieldName, val),\n record: values,\n locale: userLocale,\n })}\n </div>\n );\n };\n\n return (\n <div\n ref={rootRef}\n style={{\n flexShrink: 0,\n borderTop: \"1px solid var(--divider)\",\n background: \"var(--surface)\",\n display: \"flex\",\n flexDirection: \"column\",\n height: `${height}px`,\n }}\n >\n <div\n onMouseDown={onDragStart}\n title=\"Drag to resize\"\n style={{\n height: 12,\n cursor: \"row-resize\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n }}\n >\n <div style={{ width: 56, height: 3, borderRadius: 999, background: \"var(--border)\" }} />\n </div>\n\n <form\n onSubmit={handleSubmit}\n onKeyDown={(e) => {\n // Keyboard-first: Cmd/Ctrl+Enter submits from anywhere in the\n // composer (including the multi-line content textarea).\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault();\n e.currentTarget.requestSubmit();\n }\n }}\n style={{\n flex: 1,\n minHeight: 0,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 6,\n padding: \"0 12px 8px\",\n }}\n >\n {topFields.length > 0 && (\n <div style={{ display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: 6, flexShrink: 0 }}>\n {topFields.map(renderMetaField)}\n </div>\n )}\n\n {contentField && childEntity.fields.get(contentField) && (\n <div\n style={{ flex: 1, minHeight: 0, display: \"flex\" }}\n ref={(el) => {\n fieldNodesRef.current[contentField] = el;\n }}\n >\n {renderField({\n field: childEntity.fields.get(contentField)!,\n fieldName: contentField,\n value: values[contentField] ?? \"\",\n mode: \"edit\",\n onChange: (val) => handleFieldChange(contentField, val),\n record: values,\n fill: true,\n locale: userLocale,\n })}\n </div>\n )}\n\n <div style={{ display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: 6, flexShrink: 0 }}>\n {bottomFields.map(renderMetaField)}\n <div\n style={{\n marginLeft: \"auto\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n }}\n >\n <span style={{ fontSize: 10.5, color: \"var(--text-4)\" }}>\n <span className=\"mono\">⌘</span>\n <span className=\"mono\">↵</span> to add\n </span>\n <Btn\n type=\"submit\"\n kind=\"primary\"\n icon={<Icon.plus size={13} />}\n disabled={isPending || !isReady}\n >\n Add\n </Btn>\n </div>\n </div>\n\n {error != null && (\n <p style={{ fontSize: 11, color: \"var(--danger)\", margin: 0, flexShrink: 0 }}>{error.message}</p>\n )}\n </form>\n </div>\n );\n}\n","import { useState, useCallback, useRef, useEffect, useMemo, useLayoutEffect } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Btn, Icon, IconBtn, Pill, Spinner } from \"@/components/primitives\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { ENTITY_CHILDREN_KEY } from \"@/hooks/useChildrenData\";\nimport { useTimelineData } from \"@/hooks/useTimelineData\";\nimport { updateEntity, deleteEntity } from \"@/api/data\";\nimport { toast } from \"@/lib/toast\";\nimport { loc, fieldDisplayName } from \"@/types/schema\";\nimport type { EntityField } from \"@/types/schema\";\nimport { TimelineComposer } from \"./TimelineComposer\";\nimport type { ChildDisplayProps } from \"./ChildrenBlock\";\n\ntype RowData = Record<string, unknown>;\n\nconst SCROLL_BOTTOM_TOLERANCE_PX = 80;\nconst IO_PREFETCH_MARGIN_PX = 200;\n\nfunction rowId(row: RowData): string {\n const id = row[\"id\"];\n return id != null ? String(id) : \"\";\n}\n\nfunction isOptimisticRow(row: RowData): boolean {\n return row[\"__optimistic\"] === true;\n}\n\nexport function ChildrenTimeline({ config, parentEntity, parentId, schema }: ChildDisplayProps) {\n const childEntity = schema.entities[config.entity];\n const {\n rows: rawRows,\n refs,\n fetchNextPage,\n hasOlder,\n isFetchingOlder,\n isLoading,\n isError,\n } = useTimelineData(\n parentEntity,\n parentId,\n config.entity,\n config.foreign_key,\n config.timeline?.time_field,\n config.limit,\n );\n const timeline = config.timeline;\n const scroll = timeline?.scroll ?? false;\n\n // Schema `sort:` is the DISPLAY order (asc = chat: oldest top, newest\n // bottom; desc = news feed: newest top). The hook always returns rows\n // newest-first, so reverse here when asc is requested. slice() avoids\n // mutating react-query's cached array.\n const displayAsc = (config.sort ?? \"\").endsWith(\":asc\");\n const rows = useMemo(\n () => (displayAsc ? [...rawRows].reverse() : rawRows),\n [rawRows, displayAsc],\n );\n\n const autoScroll = timeline?.auto_scroll ?? \"none\";\n const scrollRef = useRef<HTMLDivElement>(null);\n const topSentinelRef = useRef<HTMLDivElement>(null);\n const hasAutoScrolled = useRef(false);\n const prevRowCount = useRef(0);\n const prevFirstRowId = useRef<string | null>(null);\n const prevLastRowId = useRef<string | null>(null);\n const isNearBottomRef = useRef(true);\n // Captured before older-page prepend so we can restore the visual top\n // row's position after rows insert above it.\n const prependAnchorRef = useRef<{ scrollHeight: number; scrollTop: number } | null>(null);\n\n useEffect(() => {\n hasAutoScrolled.current = false;\n prevRowCount.current = 0;\n prevFirstRowId.current = null;\n prevLastRowId.current = null;\n isNearBottomRef.current = true;\n prependAnchorRef.current = null;\n }, [parentId]);\n\n // Capture scroll anchor BEFORE older rows render. Fires when an older\n // page is in-flight; the actual prepend lands on the next data update.\n useLayoutEffect(() => {\n if (!isFetchingOlder || !scrollRef.current) return;\n prependAnchorRef.current = {\n scrollHeight: scrollRef.current.scrollHeight,\n scrollTop: scrollRef.current.scrollTop,\n };\n }, [isFetchingOlder]);\n\n useLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el || rows.length === 0) {\n prevRowCount.current = rows.length;\n return;\n }\n\n const firstId = rowId(rows[0]);\n const lastId = rowId(rows[rows.length - 1]);\n const isInitialLoad = !hasAutoScrolled.current;\n const grew = rows.length > prevRowCount.current && prevRowCount.current > 0;\n // For asc display, \"older\" rows arrive at the top of the list. For\n // desc display, \"older\" rows arrive at the bottom. We detect the\n // boundary row that has not changed identity (the formerly-top or\n // formerly-bottom row) to decide which kind of growth happened.\n const olderPrepended =\n grew &&\n ((displayAsc && firstId !== prevFirstRowId.current && lastId === prevLastRowId.current) ||\n (!displayAsc && lastId !== prevLastRowId.current && firstId === prevFirstRowId.current));\n const newRowAppended = grew && !olderPrepended;\n\n if (isInitialLoad) {\n // Chat-style auto-scroll on first render: jump to the bottom so the\n // newest visible row is in view. Non-chat (desc) keeps natural top.\n if (displayAsc && autoScroll === \"bottom\") {\n el.scrollTop = el.scrollHeight;\n }\n hasAutoScrolled.current = true;\n } else if (olderPrepended && prependAnchorRef.current) {\n // Preserve visual position when older rows insert. For asc the new\n // rows are above the viewport (push content down -> bump scrollTop\n // by the height delta). For desc they're below the viewport, so\n // scrollTop stays put.\n const anchor = prependAnchorRef.current;\n if (displayAsc) {\n el.scrollTop = anchor.scrollTop + (el.scrollHeight - anchor.scrollHeight);\n }\n prependAnchorRef.current = null;\n } else if (newRowAppended) {\n // A new row arrived at the chat bottom (composer add, SSE, etc.).\n // Only auto-scroll if the user was already near the bottom; if\n // they were scrolled up reading history, respect that position.\n if (displayAsc && isNearBottomRef.current) {\n el.scrollTop = el.scrollHeight;\n }\n }\n\n prevRowCount.current = rows.length;\n prevFirstRowId.current = firstId;\n prevLastRowId.current = lastId;\n }, [rows, autoScroll, displayAsc]);\n\n const onScroll = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n isNearBottomRef.current =\n el.scrollHeight - el.scrollTop - el.clientHeight < SCROLL_BOTTOM_TOLERANCE_PX;\n }, []);\n\n // IntersectionObserver triggers fetchNextPage when the top sentinel\n // becomes visible. Button below is the keyboard/jsdom/a11y fallback.\n // When the timeline has its own scroll container (sidebar variant),\n // observe against it; otherwise fall back to the page viewport.\n useEffect(() => {\n const sentinel = topSentinelRef.current;\n if (!sentinel) return;\n if (typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(\n (entries) => {\n for (const e of entries) {\n if (e.isIntersecting && hasOlder && !isFetchingOlder) {\n fetchNextPage();\n }\n }\n },\n {\n root: scrollRef.current ?? null,\n rootMargin: `${IO_PREFETCH_MARGIN_PX}px 0px 0px 0px`,\n threshold: 0,\n },\n );\n io.observe(sentinel);\n return () => io.disconnect();\n }, [hasOlder, isFetchingOlder, fetchNextPage]);\n\n const [editingId, setEditingId] = useState<string | null>(null);\n\n if (!childEntity) {\n return <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: \"8px 0\" }}>Entity not found.</div>;\n }\n\n const editableFields = config.quick_add?.fields\n ?? ([timeline?.content_field].filter(Boolean) as string[]);\n\n const title =\n loc(childEntity.display?.name_plural) || loc(childEntity.display?.name) || config.entity;\n\n // The load-older affordance is rendered at the TOP of the scroll\n // container regardless of display sort, because in both asc and desc\n // modes the newest rows are anchored to the API page-0 window and\n // older history accretes upward in time space. Visually that means:\n // - asc display (chat): top of list = oldest visible, \"Load older\"\n // reveals even older history.\n // - desc display (news feed): top of list = newest visible, but the\n // pagination cursor still walks backward in time, so the button\n // labelled \"Load older\" loads MORE rows below. We surface it at the\n // top in both cases for consistency.\n const olderButton = hasOlder ? (\n <div style={{ display: \"flex\", justifyContent: \"center\", padding: \"6px 0\" }}>\n <Btn\n size=\"xs\"\n onClick={() => fetchNextPage()}\n disabled={isFetchingOlder}\n icon={isFetchingOlder ? <Spinner size={11} /> : undefined}\n >\n {isFetchingOlder ? \"Loading…\" : \"Load older\"}\n </Btn>\n </div>\n ) : null;\n const topSentinel = <div ref={topSentinelRef} aria-hidden=\"true\" style={{ height: 1 }} />;\n\n let entries: React.ReactNode;\n if (isError) {\n entries = <div style={{ fontSize: 12.5, color: \"var(--danger)\", padding: 12 }}>Failed to load.</div>;\n } else if (isLoading) {\n entries = (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8, padding: 12 }}>\n <div style={{ height: 64, background: \"var(--surface-3)\", borderRadius: 8 }} />\n <div style={{ height: 64, background: \"var(--surface-3)\", borderRadius: 8 }} />\n </div>\n );\n } else if (rows.length === 0) {\n entries = (\n <>\n {topSentinel}\n <div style={{ fontSize: 12.5, color: \"var(--text-3)\", padding: 12 }}>No activity yet.</div>\n </>\n );\n } else {\n const renderEntry = (row: RowData, idx: number) => {\n const id = rowId(row) || String(idx);\n return (\n <TimelineEntry\n key={id}\n row={row}\n rowId={id}\n childEntity={childEntity}\n entityCode={config.entity}\n parentEntity={parentEntity}\n parentId={parentId}\n timeline={timeline}\n editableFields={editableFields}\n refs={refs}\n isEditing={editingId === id}\n onStartEdit={() => setEditingId(id)}\n onCancelEdit={() => setEditingId(null)}\n onSaved={() => setEditingId(null)}\n schema={schema}\n optimistic={isOptimisticRow(row)}\n />\n );\n };\n // Group consecutive entries by calendar day so the feed gets a clear\n // chronological rhythm (design brief P2: \"grouping by day\"). Only when a\n // time field is configured; otherwise render a flat list.\n const timeField = timeline?.time_field;\n let rowNodes: React.ReactNode;\n if (timeField) {\n const groups: { key: string; label: string; rows: { row: RowData; idx: number }[] }[] = [];\n rows.forEach((row, idx) => {\n const ts = (row as RowData)[timeField];\n const key = dayKey(ts);\n const last = groups[groups.length - 1];\n if (!last || last.key !== key) {\n groups.push({ key, label: dayLabel(ts), rows: [{ row: row as RowData, idx }] });\n } else {\n last.rows.push({ row: row as RowData, idx });\n }\n });\n rowNodes = groups.map((g) => (\n <div key={`day-${g.key}`} style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <DayHeader label={g.label} count={g.rows.length} />\n {g.rows.map(({ row, idx }) => renderEntry(row, idx))}\n </div>\n ));\n } else {\n rowNodes = rows.map((row, idx) => renderEntry(row as RowData, idx));\n }\n entries = (\n <>\n {topSentinel}\n {olderButton}\n {rowNodes}\n </>\n );\n }\n\n if (scroll) {\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100%\",\n borderLeft: \"1px solid var(--border)\",\n background: \"var(--surface-2)\",\n color: \"var(--text)\",\n }}\n >\n <div style={{ padding: \"10px 16px\", borderBottom: \"1px solid var(--divider)\", background: \"var(--surface)\" }}>\n <h3 style={{ margin: 0, fontSize: 12.5, fontWeight: 600 }}>{title}</h3>\n </div>\n <div\n ref={scrollRef}\n onScroll={onScroll}\n style={{ flex: 1, overflowY: \"auto\", padding: 12, display: \"flex\", flexDirection: \"column\", gap: 8 }}\n >\n {entries}\n </div>\n <TimelineComposer config={config} parentEntity={parentEntity} parentId={parentId} schema={schema} />\n </div>\n );\n }\n\n return (\n <div>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>{entries}</div>\n <TimelineComposer config={config} parentEntity={parentEntity} parentId={parentId} schema={schema} />\n </div>\n );\n}\n\ninterface TimelineEntryProps {\n row: RowData;\n rowId: string;\n childEntity: NonNullable<ChildDisplayProps[\"schema\"][\"entities\"][string]>;\n entityCode: string;\n parentEntity: string;\n parentId: string;\n timeline: ChildDisplayProps[\"config\"][\"timeline\"];\n editableFields: string[];\n refs?: Record<string, Record<string, Record<string, unknown>>>;\n isEditing: boolean;\n onStartEdit: () => void;\n onCancelEdit: () => void;\n onSaved: () => void;\n schema: ChildDisplayProps[\"schema\"];\n /**\n * Optimistic row inserted before the server confirms the create. Renders\n * dimmed without edit/delete controls — those mutations would 404 on the\n * client-generated temp id. Replaced by the real server row on refetch.\n */\n optimistic?: boolean;\n}\n\nfunction TimelineEntry({\n row,\n rowId,\n childEntity,\n entityCode,\n parentEntity,\n parentId,\n timeline,\n editableFields,\n refs,\n isEditing,\n onStartEdit,\n onCancelEdit,\n onSaved,\n schema,\n optimistic = false,\n}: TimelineEntryProps) {\n const queryClient = useQueryClient();\n const userLocale = useUserLocale();\n const [formData, setFormData] = useState<Record<string, unknown>>({});\n\n const invalidate = useCallback(() => {\n queryClient.invalidateQueries({ queryKey: [ENTITY_CHILDREN_KEY, parentEntity, parentId, entityCode] });\n }, [queryClient, parentEntity, parentId, entityCode]);\n\n const updateMut = useMutation({\n mutationFn: (data: Record<string, unknown>) => updateEntity(entityCode, rowId, data),\n onSuccess: () => {\n invalidate();\n toast.success(\"Updated\");\n onSaved();\n },\n onError: (err: Error) => toast.error(err.message),\n });\n\n const deleteMut = useMutation({\n mutationFn: () => deleteEntity(entityCode, rowId),\n onSuccess: () => {\n invalidate();\n toast.success(\"Deleted\");\n },\n onError: (err: Error) => toast.error(err.message),\n });\n\n const startEdit = useCallback(() => {\n const data: Record<string, unknown> = {};\n for (const f of editableFields) data[f] = row[f] ?? \"\";\n setFormData(data);\n onStartEdit();\n }, [row, editableFields, onStartEdit]);\n\n const handleFieldChange = useCallback((fieldName: string, val: unknown) => {\n setFormData((prev) => ({ ...prev, [fieldName]: val }));\n }, []);\n\n const timeField = timeline?.time_field;\n const typeField = timeline?.type_field;\n const summaryField = timeline?.summary_field;\n const contentField = timeline?.content_field;\n const authorField = timeline?.author_field;\n const directionField = timeline?.direction_field;\n const intentField = timeline?.intent_field;\n\n if (isEditing) {\n return (\n <div\n style={{\n borderRadius: 10,\n border: \"1px solid var(--accent)\",\n background: \"var(--surface)\",\n padding: 12,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 8,\n }}\n >\n {editableFields.map((fieldName) => {\n const field = childEntity.fields.get(fieldName);\n if (!field) return null;\n return (\n <div key={fieldName}>\n <label style={{ fontSize: 11, color: \"var(--text-3)\", marginBottom: 4, display: \"block\" }}>\n {fieldDisplayName(field, fieldName, schema.entities)}\n </label>\n {renderField({\n field,\n fieldName,\n value: formData[fieldName] ?? \"\",\n mode: \"edit\",\n onChange: (val) => handleFieldChange(fieldName, val),\n record: formData,\n locale: userLocale,\n })}\n </div>\n );\n })}\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, paddingTop: 4 }}>\n <Btn\n kind=\"primary\"\n size=\"xs\"\n icon={updateMut.isPending ? <Spinner size={12} /> : <Icon.check size={12} />}\n onClick={() => updateMut.mutate(formData)}\n disabled={updateMut.isPending}\n >\n {updateMut.isPending ? \"Saving…\" : \"Save\"}\n </Btn>\n <Btn\n size=\"xs\"\n icon={<Icon.x size={12} />}\n onClick={() => {\n setFormData({});\n onCancelEdit();\n }}\n disabled={updateMut.isPending}\n >\n Cancel\n </Btn>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className=\"group\"\n data-optimistic={optimistic || undefined}\n style={{\n borderRadius: 10,\n border: \"1px solid var(--border)\",\n background: \"var(--surface)\",\n padding: 12,\n transition: \"border-color .12s\",\n opacity: optimistic ? 0.6 : 1,\n }}\n onMouseEnter={(e) => (e.currentTarget.style.borderColor = \"var(--border-strong)\")}\n onMouseLeave={(e) => (e.currentTarget.style.borderColor = \"var(--border)\")}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n flexWrap: \"wrap\",\n fontSize: 11.5,\n color: \"var(--text-3)\",\n }}\n >\n {timeField && row[timeField] != null && childEntity.fields.get(timeField) && (\n <span style={{ fontWeight: 500 }}>\n {renderField({\n field: childEntity.fields.get(timeField)!,\n fieldName: timeField,\n value: row[timeField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </span>\n )}\n {typeField && row[typeField] != null && childEntity.fields.get(typeField) && (\n <Pill tone=\"slate\" size=\"xs\">\n {renderField({\n field: childEntity.fields.get(typeField)!,\n fieldName: typeField,\n value: row[typeField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </Pill>\n )}\n {directionField && row[directionField] != null && childEntity.fields.get(directionField) && (\n <DirectionChip value={row[directionField]} field={childEntity.fields.get(directionField)!} />\n )}\n {intentField && row[intentField] != null && childEntity.fields.get(intentField) && (\n <Pill tone=\"slate\" size=\"xs\">\n {renderField({\n field: childEntity.fields.get(intentField)!,\n fieldName: intentField,\n value: row[intentField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </Pill>\n )}\n {authorField && row[authorField] != null && childEntity.fields.get(authorField) && (\n <span>\n by{\" \"}\n {renderField({\n field: childEntity.fields.get(authorField)!,\n fieldName: authorField,\n value: row[authorField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </span>\n )}\n {!optimistic && (\n <div style={{ marginLeft: \"auto\", display: \"flex\", alignItems: \"center\", gap: 2 }}>\n <IconBtn size={22} icon={<Icon.edit size={11} />} onClick={startEdit} title=\"Edit\" />\n <IconBtn\n size={22}\n icon={<Icon.trash size={11} />}\n onClick={() => deleteMut.mutate()}\n title=\"Delete\"\n />\n </div>\n )}\n </div>\n {summaryField && row[summaryField] != null && childEntity.fields.get(summaryField) && (\n <div style={{ fontSize: 12.5, fontWeight: 500, marginTop: 6 }}>\n {renderField({\n field: childEntity.fields.get(summaryField)!,\n fieldName: summaryField,\n value: row[summaryField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </div>\n )}\n {contentField &&\n row[contentField] != null &&\n contentField !== summaryField &&\n childEntity.fields.get(contentField) && (\n <div style={{ fontSize: 12.5, fontWeight: 400, marginTop: 6 }}>\n {renderField({\n field: childEntity.fields.get(contentField)!,\n fieldName: contentField,\n value: row[contentField],\n mode: \"display\",\n record: row,\n refs,\n locale: userLocale,\n })}\n </div>\n )}\n </div>\n );\n}\n\n// dayKey returns a stable per-calendar-day key for grouping; \"\" for an\n// unparseable timestamp (those entries fall into a single trailing group).\nfunction dayKey(ts: unknown): string {\n if (ts == null) return \"\";\n const d = new Date(String(ts));\n if (Number.isNaN(d.getTime())) return \"\";\n return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;\n}\n\n// dayLabel renders a human day heading: \"Today\" / \"Yesterday\" / \"Mon Jan 5\".\nfunction dayLabel(ts: unknown): string {\n if (ts == null) return \"—\";\n const d = new Date(String(ts));\n if (Number.isNaN(d.getTime())) return \"—\";\n const now = new Date();\n const yesterday = new Date();\n yesterday.setDate(now.getDate() - 1);\n const sameDay = (a: Date, b: Date) => a.toDateString() === b.toDateString();\n if (sameDay(d, now)) return \"Today\";\n if (sameDay(d, yesterday)) return \"Yesterday\";\n return d.toLocaleDateString(undefined, { weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\n// DayHeader is the sticky chronological divider between day groups.\nfunction DayHeader({ label, count }: { label: string; count: number }) {\n return (\n <div\n style={{\n position: \"sticky\",\n top: -4,\n zIndex: 1,\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"4px 2px\",\n background:\n \"linear-gradient(180deg, var(--surface-2) 0%, var(--surface-2) 65%, transparent 100%)\",\n }}\n >\n <span\n style={{\n fontSize: 11,\n fontWeight: 600,\n color: \"var(--text-3)\",\n }}\n >\n {label}\n </span>\n <span style={{ flex: 1, height: 1, background: \"var(--divider)\" }} />\n <span className=\"num\" style={{ fontSize: 10.5, color: \"var(--text-4)\" }}>\n {count}\n </span>\n </div>\n );\n}\n\n// DirectionChip marks an inbound vs outbound activity. Outbound carries the\n// accent tint, inbound the info tint, so the feed reads directionally at a\n// glance (design brief P2: \"inbound vs outbound\").\nfunction DirectionChip({ value, field }: { value: unknown; field: EntityField }) {\n const outbound = isOutbound(value, field);\n if (outbound === null) return null;\n return (\n <span\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 3,\n padding: \"1px 6px 1px 4px\",\n borderRadius: 999,\n fontSize: 10.5,\n fontWeight: 600,\n background: outbound ? \"var(--accent-soft)\" : \"var(--info-soft)\",\n color: outbound ? \"var(--accent)\" : \"var(--info)\",\n }}\n >\n {outbound ? <Icon.arrowUp size={10} /> : <Icon.arrowDown size={10} />}\n {outbound ? \"Out\" : \"In\"}\n </span>\n );\n}\n\nfunction isOutbound(value: unknown, field: EntityField): boolean | null {\n if (field.type === \"bool\") return value === true;\n if (typeof value === \"string\") {\n const v = value.toLowerCase();\n if (v === \"outbound\" || v === \"out\") return true;\n if (v === \"inbound\" || v === \"in\") return false;\n }\n return null;\n}\n","import { useCallback, useRef } from \"react\";\nimport { Btn, Icon, IconBtn } from \"@/components/primitives\";\nimport { dataTableStyle, dataTableHeadCellStyle } from \"@/components/primitives/dataTableStyles\";\nimport { fieldDisplayName } from \"@/types/schema\";\nimport type { Entity, Schema } from \"@/types/schema\";\nimport { renderField } from \"@/components/fields\";\nimport type { RefsMap } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport type { PendingChildRow } from \"./LayoutRenderer\";\nimport type { ResolvedChildConfig } from \"@/lib/child-config\";\n\ninterface EditableChildrenTableProps {\n config: ResolvedChildConfig;\n childEntity: Entity;\n schema: Schema;\n rows: PendingChildRow[];\n onChange: (rows: PendingChildRow[]) => void;\n validationErrors?: Record<string, string[]>; // rowKey -> fieldNames\n refs?: RefsMap;\n}\n\n// EditableChildrenTable renders child rows with all cells in edit mode.\n// Used for direct (aggregate) children when the parent detail page is in edit mode.\nexport function EditableChildrenTable({\n config,\n childEntity,\n schema,\n rows,\n onChange,\n validationErrors,\n refs,\n}: EditableChildrenTableProps) {\n const userLocale = useUserLocale();\n const tableRef = useRef<HTMLTableElement>(null);\n const columns = config.columns;\n\n // Editable columns: exclude FK, auto, primary, and position_field.\n const fkSet = new Set(Array.isArray(config.foreign_key) ? config.foreign_key : [config.foreign_key]);\n const editableColumns = columns.filter((col) => {\n if (fkSet.has(col)) return false;\n if (config.position_field && col === config.position_field) return false;\n const field = childEntity.fields.get(col);\n if (!field) return false;\n if (field.auto || field.primary) return false;\n return true;\n });\n\n // Display-only columns (auto/primary but visible in column list, or position_field).\n const displayOnlyColumns = new Set(\n columns.filter((col) => {\n if (config.position_field && col === config.position_field) return true;\n const field = childEntity.fields.get(col);\n return field && (field.auto || field.primary);\n }),\n );\n\n const handleCellChange = useCallback(\n (rowKey: string, fieldName: string, value: unknown) => {\n const updated = rows.map((row) =>\n row._key === rowKey\n ? { ...row, [fieldName]: value, _dirty: true }\n : row,\n );\n onChange(updated);\n },\n [rows, onChange],\n );\n\n const handleAddRow = useCallback(() => {\n const newRow: PendingChildRow = {\n _key: crypto.randomUUID(),\n _dirty: true,\n };\n // Pre-fill from quick_add.defaults first, then field-level defaults.\n const quickDefaults = config.quick_add?.defaults ?? {};\n for (const col of editableColumns) {\n const field = childEntity.fields.get(col);\n if (!field) continue;\n if (quickDefaults[col] !== undefined) {\n newRow[col] = quickDefaults[col];\n } else if (field.default !== undefined) {\n newRow[col] = field.default;\n } else if (field.type === \"bool\") {\n newRow[col] = false;\n }\n }\n onChange([...rows, newRow]);\n }, [rows, onChange, editableColumns, childEntity, config.quick_add?.defaults]);\n\n const handleDeleteRow = useCallback(\n (rowKey: string) => {\n const updated = rows.map((row) =>\n row._key === rowKey ? { ...row, _deleted: true } : row,\n );\n onChange(updated);\n },\n [rows, onChange],\n );\n\n const handleUndoDelete = useCallback(\n (rowKey: string) => {\n const updated = rows.map((row) =>\n row._key === rowKey ? { ...row, _deleted: false } : row,\n );\n onChange(updated);\n },\n [rows, onChange],\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTableElement>) => {\n const target = e.target as HTMLElement;\n const cell = target.closest(\"td\");\n if (!cell) return;\n const row = cell.closest(\"tr\");\n if (!row) return;\n const tbody = row.closest(\"tbody\");\n if (!tbody) return;\n\n const cellIndex = Array.from(row.cells).indexOf(cell as HTMLTableCellElement);\n const rowIndex = Array.from(tbody.rows).indexOf(row as HTMLTableRowElement);\n\n if (e.key === \"Tab\") {\n // Tab/Shift+Tab navigate between editable cells.\n const direction = e.shiftKey ? -1 : 1;\n const allRows = Array.from(tbody.rows);\n let nextRow = rowIndex;\n let nextCell = cellIndex + direction;\n\n // Walk across rows to find next editable cell.\n while (nextRow >= 0 && nextRow < allRows.length) {\n if (nextCell >= 0 && nextCell < allRows[nextRow].cells.length) {\n const nextTd = allRows[nextRow].cells[nextCell];\n const input = nextTd.querySelector(\"input, select, textarea, [tabindex]\") as HTMLElement | null;\n if (input) {\n e.preventDefault();\n input.focus();\n return;\n }\n nextCell += direction;\n } else {\n // Move to next/previous row.\n nextRow += direction;\n nextCell = direction > 0 ? 0 : (allRows[nextRow]?.cells.length ?? 1) - 1;\n }\n }\n // Let the browser handle focus leaving the table.\n } else if (e.key === \"Enter\") {\n // Enter: move to same column in next row, or add row if at last.\n e.preventDefault();\n const allRows = Array.from(tbody.rows);\n if (rowIndex < allRows.length - 1) {\n const nextTd = allRows[rowIndex + 1].cells[cellIndex];\n const input = nextTd?.querySelector(\"input, select, textarea, [tabindex]\") as HTMLElement | null;\n if (input) input.focus();\n } else {\n // At last row: add a new row.\n handleAddRow();\n // Focus will happen on next render via autoFocus-like mechanism.\n requestAnimationFrame(() => {\n const newRows = tbody.querySelectorAll(\"tr\");\n const lastRow = newRows[newRows.length - 1];\n if (lastRow) {\n const firstInput = lastRow.querySelector(\"input, select, textarea, [tabindex]\") as HTMLElement | null;\n if (firstInput) firstInput.focus();\n }\n });\n }\n } else if (e.key === \"Escape\") {\n // Escape: blur the current cell.\n (target as HTMLElement).blur?.();\n }\n },\n [handleAddRow],\n );\n\n // Shared table chrome - see primitives/dataTableStyles.ts.\n const thStyle = dataTableHeadCellStyle;\n\n return (\n <div>\n <div style={{ overflowX: \"auto\" }}>\n <table\n ref={tableRef}\n style={dataTableStyle}\n onKeyDown={handleKeyDown}\n >\n <thead>\n <tr>\n {columns.map((col) => {\n const field = childEntity.fields.get(col);\n return (\n <th key={col} style={thStyle}>\n {field ? fieldDisplayName(field, col, schema.entities) : col}\n </th>\n );\n })}\n <th style={{ ...thStyle, width: 40 }} />\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const isDeleted = !!row._deleted;\n const rowErrors = validationErrors?.[row._key];\n return (\n <tr key={row._key} style={{ opacity: isDeleted ? 0.5 : 1 }}>\n {columns.map((col) => {\n const field = childEntity.fields.get(col);\n const hasError = rowErrors?.includes(col);\n const isDisplayOnly = displayOnlyColumns.has(col);\n\n if (isDeleted || isDisplayOnly) {\n return (\n <td\n key={col}\n style={{\n padding: \"6px 12px\",\n borderBottom: \"1px solid var(--divider)\",\n verticalAlign: \"middle\",\n textDecoration: isDeleted ? \"line-through\" : \"none\",\n color: \"var(--text-2)\",\n }}\n >\n {field\n ? renderField({\n field,\n fieldName: col,\n value: row[col],\n mode: \"display\",\n record: row as Record<string, unknown>,\n refs,\n locale: userLocale,\n })\n : String(row[col] ?? \"\")}\n </td>\n );\n }\n\n return (\n <td\n key={col}\n style={{\n padding: \"4px 4px\",\n borderBottom: \"1px solid var(--divider)\",\n verticalAlign: \"middle\",\n outline: hasError ? \"1px solid var(--danger)\" : \"none\",\n outlineOffset: -1,\n borderRadius: hasError ? 4 : 0,\n }}\n title={hasError ? `${col} is required` : undefined}\n >\n {field\n ? renderField({\n field,\n fieldName: col,\n value: row[col],\n mode: \"edit\",\n onChange: (val) => handleCellChange(row._key, col, val),\n record: row as Record<string, unknown>,\n refs,\n compact: true,\n locale: userLocale,\n })\n : String(row[col] ?? \"\")}\n </td>\n );\n })}\n <td style={{ padding: \"4px 8px\", borderBottom: \"1px solid var(--divider)\", verticalAlign: \"middle\" }}>\n {isDeleted ? (\n <IconBtn\n size={24}\n icon={<Icon.refresh size={12} />}\n onClick={() => handleUndoDelete(row._key)}\n title=\"Undo delete\"\n />\n ) : (\n <IconBtn\n size={24}\n icon={<Icon.trash size={12} />}\n onClick={() => handleDeleteRow(row._key)}\n title=\"Delete row\"\n />\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n <div style={{ marginTop: 8 }}>\n <Btn kind=\"ghost\" size=\"xs\" icon={<Icon.plus size={12} />} onClick={handleAddRow}>\n Add row\n </Btn>\n </div>\n </div>\n );\n}\n","import { lazy, Suspense } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { LayoutNode, Schema } from \"@/types/schema\";\nimport { entityAllowsMutation } from \"@/types/schema\";\nimport { resolveChildConfig, findChildRelation, type ResolvedChildConfig } from \"@/lib/child-config\";\nimport { TableSkeleton } from \"@/components/shared/LoadingSkeleton\";\nimport { SectionCard } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\nimport { resolveBlockLabel, resolveBlockIcon } from \"./blockMeta\";\nimport { ChildrenTable } from \"./ChildrenTable\";\nimport { ChildrenList } from \"./ChildrenList\";\nimport { ChildrenCards } from \"./ChildrenCards\";\nimport { ChildrenTimeline } from \"./ChildrenTimeline\";\nimport { EditableChildrenTable } from \"./EditableChildrenTable\";\n\n// DraggableChildrenTable pulls @dnd-kit (drag-to-reorder). It is the only\n// table variant that needs it, and only on edit screens with a position\n// field, so it loads lazily - @dnd-kit stays in its own chunk.\nconst DraggableChildrenTable = lazy(() =>\n import(\"./DraggableChildrenTable\").then((m) => ({\n default: m.DraggableChildrenTable,\n })),\n);\n\ninterface ChildrenBlockProps {\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\nexport interface ChildDisplayProps {\n config: ResolvedChildConfig;\n parentEntity: string;\n parentId: string;\n schema: Schema;\n}\n\n// ChildrenBlock resolves the child config using the three-layer precedence\n// algorithm and dispatches to the appropriate display mode component.\n//\n// Every non-timeline mode is wrapped in a SectionCard so a `children:` block\n// reads as a first-class titled block, consistent with `section` nodes -\n// resolving the design-brief gap where child tables rendered with no card\n// and no header. Timeline mode owns its own presentation (the activity rail\n// / inline feed). A block already inside a section or tab (`ctx.withinCard`)\n// skips its own card to avoid a card within a card.\nexport function ChildrenBlock({ node, ctx }: ChildrenBlockProps) {\n const childCode = node.children;\n if (!childCode) return null;\n\n // For non-new records, children cannot be loaded until the parent record\n // exists. For new records in edit mode, we show the editable table.\n if (ctx.isNew && ctx.mode !== \"edit\") return null;\n\n const relation = findChildRelation(ctx.entity, childCode);\n if (!relation) {\n return (\n <div className=\"text-sm text-muted-foreground py-2\">\n Unknown child relation: {childCode}\n </div>\n );\n }\n\n const childEntity = ctx.schema.entities[relation.entity];\n if (!childEntity) {\n return (\n <div className=\"text-sm text-muted-foreground py-2\">\n Entity not found: {relation.entity}\n </div>\n );\n }\n\n const config = resolveChildConfig(relation, childEntity, node);\n\n // Block title + icon for the SectionCard chrome - shared with the tab strip\n // (LayoutTabs) via blockMeta so a children block reads identically whether\n // it carries its own card or sits as a tab.\n const title = resolveBlockLabel(node, ctx);\n const iconName = resolveBlockIcon(node, ctx);\n\n // wrap gives the block its section card. Timeline keeps its own chrome;\n // a block nested inside a section/tab is already carded.\n const wrap = (body: ReactNode): ReactNode => {\n if (config.mode === \"timeline\" || ctx.withinCard) return body;\n return (\n <SectionCard\n title={title}\n icon={iconName ? <DynamicIcon name={iconName} size={14} /> : undefined}\n >\n {body}\n </SectionCard>\n );\n };\n\n // In edit mode, dispatch to EditableChildrenTable for table-mode children\n // when editable is true and the child entity allows generic update.\n if (\n ctx.mode === \"edit\" &&\n config.editable &&\n config.interaction !== \"navigate\" &&\n config.interaction !== \"readonly\" &&\n entityAllowsMutation(childEntity, \"update\") &&\n config.mode === \"table\" &&\n ctx.pendingChildren !== undefined &&\n ctx.onChildrenChange\n ) {\n const pendingRows = ctx.pendingChildren[childCode] ?? [];\n const validationErrors = ctx.childValidationErrors?.[childCode];\n const TableComponent = config.position_field\n ? DraggableChildrenTable\n : EditableChildrenTable;\n return wrap(\n <Suspense fallback={<TableSkeleton />}>\n <TableComponent\n config={config}\n childEntity={childEntity}\n schema={ctx.schema}\n rows={pendingRows}\n onChange={(rows) => ctx.onChildrenChange!(childCode, rows)}\n validationErrors={validationErrors}\n refs={ctx.refs}\n />\n </Suspense>,\n );\n }\n\n // For new records not in editable table mode, nothing to show.\n if (ctx.isNew) return null;\n\n const displayProps: ChildDisplayProps = {\n config,\n parentEntity: ctx.entityCode,\n parentId: ctx.recordId,\n schema: ctx.schema,\n };\n\n switch (config.mode) {\n case \"list\":\n return wrap(<ChildrenList {...displayProps} />);\n case \"cards\":\n return wrap(<ChildrenCards {...displayProps} />);\n case \"timeline\":\n return wrap(<ChildrenTimeline {...displayProps} />);\n case \"table\":\n default:\n return wrap(<ChildrenTable {...displayProps} />);\n }\n}\n","// FieldRow / FieldsGrid - the ONE canonical detail-page field row.\n//\n// Resolves the design-brief P1 \"two competing field rows\": FieldsBlock used a\n// Tailwind grid with a 160px / 14px label; the orphan RowField primitive used\n// inline styles with a 140px / 11.5px label. A page that mixed them looked\n// misaligned. This is the merged contract, used by FieldsBlock and\n// PropertiesBlock so every label+value pair on every detail page is identical.\n//\n// Layout: label beside value (2-column grid) by default; label above value\n// when the grid is a single column, when the grid stacks at narrow widths, or\n// when the row spans full width. The label column is fixed-width but the label\n// text wraps (never clipped) so other languages stay readable - see the design\n// brief's multilingual constraint.\n//\n// Ported from docs/plans/assets/2026-05-22-detail-kit/kits/FieldRowKit.jsx.\n\nimport { createContext, useContext, useEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Icon } from \"@/components/primitives\";\nimport { CopyButton } from \"@/components/shared/CopyButton\";\n\n// Fixed label-column width. The label text wraps inside it, so a longer\n// translation never clips - it just grows the row height.\nconst LABEL_WIDTH = 150;\n// Below this content width the 2-column grid collapses to one column and rows\n// stack label-above-value (see the design brief's narrow-width screenshot).\nconst STACK_BELOW = 560;\n\n// First-line box shared by the label (12px) and the display-mode value (13px)\n// when they sit side by side. A line box's vertical centre is independent of\n// font size, so an identical line-height + top padding on both columns lands\n// their text on the same line despite the 1px size difference. These MUST\n// move together - splitting them reintroduces the ~4px label/value drift this\n// pair was added to remove. Edit mode opts out: the value holds an input that\n// owns its own height.\nconst ROW_LINE_HEIGHT = \"20px\";\nconst ROW_TEXT_PADDING_TOP = 4;\n\n// Long-form field types render full-width and stacked (label above value) -\n// a paragraph, a JSON tree or a nested structure needs the room. Defined\n// once here; FieldsBlock and PropertiesBlock both consume it.\nexport const SPAN_FIELD_TYPES: ReadonlySet<string> = new Set([\n \"text\",\n \"rich_text\",\n \"json\",\n \"structure\",\n \"multilang_text\",\n]);\n\ninterface FieldsGridState {\n // Effective column count after narrow-width collapse: 1 or 2.\n columns: number;\n}\n\nconst FieldsGridContext = createContext<FieldsGridState>({ columns: 2 });\n\nexport interface FieldsGridProps {\n // Declared column count (1 or 2) from the section's `columns:` option.\n columns?: number;\n children: ReactNode;\n}\n\n// FieldsGrid wraps a `fields:` node. It owns the responsive collapse: a\n// 2-column grid becomes 1 column below STACK_BELOW px, and publishes the\n// effective count to its FieldRow children via context.\nexport function FieldsGrid({ columns = 2, children }: FieldsGridProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [narrow, setNarrow] = useState(false);\n\n useEffect(() => {\n const el = ref.current;\n if (!el || columns === 1 || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(([entry]) => {\n setNarrow(entry.contentRect.width < STACK_BELOW);\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, [columns]);\n\n const effective = columns === 1 || narrow ? 1 : columns;\n\n return (\n <FieldsGridContext.Provider value={{ columns: effective }}>\n <div\n ref={ref}\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${effective}, minmax(0, 1fr))`,\n columnGap: 24,\n rowGap: 2,\n }}\n >\n {children}\n </div>\n </FieldsGridContext.Provider>\n );\n}\n\nexport interface FieldRowProps {\n label: ReactNode;\n // The rendered value (display mode) or edit control (edit mode).\n children: ReactNode;\n // Spans the full grid width and stacks label-above-value. Used for\n // long-form field types (text, rich_text, json, structure) that need room.\n span?: boolean;\n // Marks the field required; the asterisk shows in edit mode only.\n required?: boolean;\n // Inline validation error - red message + warning glyph under the value.\n error?: string;\n // Neutral helper text under the value (e.g. \"0-100\"). Hidden while `error`\n // is set so the row never shows both at once.\n hint?: ReactNode;\n // Already-rendered inline action buttons (a field's `actions:`). They fade\n // in on row hover so they never compete with the value at rest.\n actions?: ReactNode;\n // Raw value behind a hover-revealed click-to-copy button. Set by FieldsBlock\n // for copyable fields (uuid types, or `copyable: true`). Empty = no button.\n copyValue?: string;\n mode?: \"display\" | \"edit\";\n // Pairs the <label> with the input the value renderer owns (edit mode).\n htmlFor?: string;\n // Stable id for the label element - the target of aria-labelledby for\n // button-based renderers (enum/ref) that cannot be a <label htmlFor> target.\n labelId?: string;\n \"data-testid\"?: string;\n}\n\n// FieldRow renders one label+value pair. It is purely presentational: the\n// caller passes the rendered value/control as children.\nexport function FieldRow({\n label,\n children,\n span = false,\n required,\n error,\n hint,\n actions,\n copyValue,\n mode = \"display\",\n htmlFor,\n labelId,\n \"data-testid\": testId,\n}: FieldRowProps) {\n const grid = useContext(FieldsGridContext);\n const stacked = span || grid.columns === 1;\n const hasError = !!error;\n\n return (\n <div\n className=\"detail-row\"\n data-error={hasError || undefined}\n data-testid={testId}\n style={{\n display: \"flex\",\n flexDirection: stacked ? \"column\" : \"row\",\n gap: stacked ? 4 : 16,\n alignItems: stacked ? \"stretch\" : \"flex-start\",\n padding: \"6px 0\",\n ...(span ? { gridColumn: \"1 / -1\" } : null),\n }}\n >\n {/* Label column */}\n <div\n style={{\n width: stacked ? \"auto\" : LABEL_WIDTH,\n flexShrink: 0,\n paddingTop: stacked ? 0 : ROW_TEXT_PADDING_TOP,\n fontSize: 12,\n lineHeight: ROW_LINE_HEIGHT,\n fontWeight: 500,\n color: \"var(--text-3)\",\n display: \"flex\",\n alignItems: \"flex-start\",\n gap: 2,\n }}\n >\n {htmlFor ? (\n <label id={labelId} htmlFor={htmlFor}>\n {label}\n </label>\n ) : (\n <span id={labelId}>{label}</span>\n )}\n {required && mode === \"edit\" && (\n <span aria-hidden=\"true\" style={{ color: \"var(--danger)\", marginLeft: 2 }}>\n *\n </span>\n )}\n </div>\n\n {/* Value column */}\n <div style={{ flex: 1, minWidth: 0, display: \"flex\", flexDirection: \"column\", gap: 3 }}>\n <div style={{ display: \"flex\", alignItems: \"flex-start\", gap: 6, minHeight: 24 }}>\n <div\n style={{\n flex: 1,\n minWidth: 0,\n fontSize: 13,\n lineHeight: ROW_LINE_HEIGHT,\n color: \"var(--text)\",\n overflowWrap: \"anywhere\",\n // Side-by-side display rows share the label's first-line box so\n // the two columns align; stacked rows and edit-mode inputs do\n // not (the input owns its height).\n paddingTop: mode === \"display\" && !stacked ? ROW_TEXT_PADDING_TOP : 0,\n }}\n >\n {hasError && mode === \"edit\" ? (\n // A danger ring hugs the input so an invalid field reads as an\n // error without each of the 24 field renderers needing an\n // `invalid` prop.\n <div style={{ borderRadius: 6, boxShadow: \"0 0 0 1.5px var(--danger)\" }}>\n {children}\n </div>\n ) : (\n children\n )}\n </div>\n {(copyValue || actions) && (\n <div\n className=\"detail-row-action\"\n style={{ display: \"flex\", gap: 2, flexShrink: 0, marginTop: -2 }}\n >\n {copyValue && <CopyButton value={copyValue} size=\"sm\" title=\"Copy value\" />}\n {actions}\n </div>\n )}\n </div>\n {(hasError || hint != null) && (\n <div\n style={{\n fontSize: 11.5,\n lineHeight: 1.4,\n color: hasError ? \"var(--danger)\" : \"var(--text-3)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n }}\n >\n {hasError && <Icon.warning size={11} />}\n <span>{hasError ? error : hint}</span>\n </div>\n )}\n </div>\n </div>\n );\n}\n","import type { LayoutField } from \"@/types/schema\";\nimport { ActionButton } from \"@/components/shared/ActionButton\";\nimport { renderSchemaField, resolveSchemaFieldLabel } from \"@/components/file-widgets\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { ClassifiedFieldLocked } from \"@/components/primitives/ClassifiedFieldLocked\";\nimport { fieldAccessInfo, classificationTier } from \"@/lib/field-classification\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\nimport { FieldRow, FieldsGrid, SPAN_FIELD_TYPES } from \"./FieldRow\";\n\ninterface FieldsBlockProps {\n fields: LayoutField[];\n ctx: LayoutContext;\n}\n\n// FieldsBlock renders a `fields:` node as a grid of canonical FieldRows.\n// One field row everywhere - FieldsBlock and PropertiesBlock share FieldRow,\n// so every label+value pair on every detail page aligns identically.\n// When ctx.columns is 1, rows stack label-above-value; default is 2 columns.\n// Per-field screen-scoped action buttons (LayoutField.actions) render as\n// hover-revealed icon buttons next to the value, in display mode only.\nexport function FieldsBlock({ fields, ctx }: FieldsBlockProps) {\n const { entity, entityCode, record, mode, onFieldChange, schema } = ctx;\n const userLocale = useUserLocale();\n const columns = ctx.columns ?? 2;\n // When columns=1 and there's only one field, the section header already\n // serves as label - skip the redundant field label.\n const hideLabels = columns === 1 && fields.length === 1;\n\n return (\n <FieldsGrid columns={columns}>\n {fields.map((layoutField) => {\n const fieldName = layoutField.name;\n // Accessibility: a stable id pairs the <label> with the input the\n // renderer owns. entityCode scopes it so two field blocks on one page\n // (e.g. master + nested) never collide.\n const fieldId = `${entityCode ?? \"field\"}-${fieldName}`;\n const labelId = `${fieldId}-label`;\n // Auto / readonly fields (id, created_at, updated_at, created_by,\n // last_login_*) are server-owned: the user never types them. Render\n // them read-only even when the surrounding form is in edit mode.\n const fieldDef = entity.fields.get(fieldName);\n const fieldReadOnly = !!(fieldDef?.auto || fieldDef?.readonly);\n // Classification visual treatment (plan §B13). field_access entry\n // from /api/schema reports the per-caller decision. Display-mode\n // read-denied → render the locked badge instead of the value;\n // edit-mode write-denied → force display so the input is never\n // shown locked-but-tryable (the data layer would 422 it anyway,\n // but the UX is to make the lock obvious BEFORE submit).\n const access = fieldAccessInfo(entity, fieldName);\n const readDenied = access?.read === \"denied\";\n const writeDenied = access?.write === \"denied\";\n const classificationLocked =\n mode === \"display\"\n ? readDenied\n : writeDenied || readDenied; // edit mode: lock if can't write OR can't read\n const fieldMode =\n fieldReadOnly || (mode === \"edit\" && writeDenied) ? \"display\" : mode;\n const rendered = classificationLocked ? (\n <ClassifiedFieldLocked\n tier={access!.classification}\n tierDef={classificationTier(schema, entity, fieldName)}\n requiredPermission={access?.required_permission}\n locale={userLocale}\n variant={fieldMode === \"edit\" ? \"input\" : \"value\"}\n />\n ) : (\n renderSchemaField({\n entity,\n entityCode,\n fieldName,\n record,\n mode: fieldMode,\n onChange:\n onFieldChange && !fieldReadOnly\n ? (v: unknown) => onFieldChange(fieldName, v)\n : undefined,\n refs: ctx.refs,\n entities: schema?.entities,\n screens: schema?.screens,\n locale: userLocale,\n fieldId,\n labelId,\n })\n );\n if (!rendered) return null;\n\n const hasActions =\n mode === \"display\" && layoutField.actions && layoutField.actions.length > 0;\n const actionsNode = hasActions ? (\n <>\n {layoutField.actions!.map((action, i) => (\n <ActionButton\n key={i}\n actionCode={`${fieldName}.action[${i}]`}\n action={action}\n entityCode={entityCode}\n row={record}\n variant=\"ghost\"\n size=\"icon\"\n />\n ))}\n </>\n ) : undefined;\n\n // Long-form types stack and span the full width so they breathe.\n const span = !!fieldDef && SPAN_FIELD_TYPES.has(fieldDef.type);\n\n // Copy affordance: `uuid` fields (opaque IDs you copy, never read) and\n // any field declaring `copyable: true` get a hover-revealed copy\n // button next to the value, display mode only.\n const rawValue = record?.[fieldName];\n const copyValue =\n fieldMode === \"display\" &&\n (fieldDef?.copyable || fieldDef?.type === \"uuid\") &&\n rawValue != null &&\n rawValue !== \"\"\n ? String(rawValue)\n : undefined;\n\n // hideLabels: render the value alone, full width - the section header\n // is the label.\n if (hideLabels) {\n return (\n <div key={fieldName} style={{ gridColumn: \"1 / -1\" }}>\n {rendered}\n </div>\n );\n }\n\n return (\n <FieldRow\n key={fieldName}\n label={resolveSchemaFieldLabel(entity, fieldName, schema.entities)}\n // Edit mode points <label htmlFor> at the input; display mode has\n // no input, so the label stays a plain <span> (no dangling for).\n htmlFor={fieldMode === \"edit\" ? fieldId : undefined}\n labelId={labelId}\n required={fieldDef?.required ?? false}\n mode={fieldMode}\n span={span}\n error={ctx.fieldErrors?.[fieldName]}\n actions={actionsNode}\n copyValue={copyValue}\n >\n {rendered}\n </FieldRow>\n );\n })}\n </FieldsGrid>\n );\n}\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { renderField } from \"@/components/fields\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport { loc, propertyToField } from \"@/types/schema\";\nimport type { Entity, LayoutNode, Schema } from \"@/types/schema\";\nimport { ClassifiedFieldLocked } from \"@/components/primitives/ClassifiedFieldLocked\";\nimport { fieldAccessInfo, classificationTier } from \"@/lib/field-classification\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\nimport { FieldRow, FieldsGrid, SPAN_FIELD_TYPES } from \"./FieldRow\";\n\ninterface PropertiesBlockProps {\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\n// Types where onChange fires on every keystroke and needs debouncing.\nconst DEBOUNCE_TYPES = new Set([\"string\", \"text\", \"int\", \"float\", \"decimal\", \"email\", \"url\"]);\nconst DEBOUNCE_MS = 500;\n\n// PropertiesBlock renders object properties through the same canonical\n// FieldRow / FieldsGrid as FieldsBlock, so a `properties` node and a `fields`\n// node on the same page align identically. Values render via the shared rich\n// field renderers.\nexport function PropertiesBlock({ node, ctx }: PropertiesBlockProps) {\n const { entity, objectProperties, onSetProperty, isNew, mode, refs } = ctx;\n const propertyDefs = entity.properties ?? {};\n\n // A brand-new record (pre-create) has no properties to render - the row\n // doesn't exist yet in the DB.\n if (isNew) return null;\n\n // node.properties is true (all) or an array of property codes (subset).\n const filter: string[] | true = node.properties === true ? true : (node.properties as string[]);\n\n // When filter is an array, preserve its explicit order; otherwise sort alphabetically.\n const entries: [string, import(\"@/types/schema\").PropertyDef][] =\n filter === true\n ? Object.entries(propertyDefs).sort(([a], [b]) => a.localeCompare(b))\n : (filter as string[])\n .filter((code) => propertyDefs[code] != null)\n .map((code) => [code, propertyDefs[code]]);\n\n if (entries.length === 0) return null;\n\n return (\n <FieldsGrid columns={2}>\n {entries.map(([propCode, prop]) => {\n const propVal = objectProperties?.find((p) => p.property_code === propCode);\n const field = propertyToField(prop);\n // Force display mode when the outer page has no write callback\n // (readonly entity, or view mode on a detail page).\n const effectiveMode = prop.readonly || !onSetProperty ? \"display\" : mode;\n\n return (\n <DebouncedProperty\n key={propCode}\n propCode={propCode}\n prop={prop}\n field={field}\n value={propVal?.value}\n mode={effectiveMode}\n onSetProperty={onSetProperty}\n refs={refs}\n entity={entity}\n schema={ctx.schema}\n />\n );\n })}\n </FieldsGrid>\n );\n}\n\n// DebouncedProperty wraps a single property field with optional debounce for\n// text-like types. onSetProperty is optional - omit it (display-mode /\n// read-only containers) and the field renders without an onChange handler.\nfunction DebouncedProperty({\n propCode,\n prop,\n field,\n value,\n mode,\n onSetProperty,\n refs,\n entity,\n schema,\n}: {\n propCode: string;\n prop: {\n name: import(\"@/types/schema\").LocalizedString;\n display?: { name: import(\"@/types/schema\").LocalizedString };\n type: string;\n };\n field: import(\"@/types/schema\").EntityField;\n value: unknown;\n mode: \"display\" | \"edit\";\n onSetProperty?: (code: string, value: unknown) => void;\n refs?: import(\"@/components/fields\").RefsMap;\n entity: Entity;\n schema?: Schema;\n}) {\n const userLocale = useUserLocale();\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, []);\n\n const onChange = useCallback(\n (v: unknown) => {\n if (!onSetProperty) return;\n if (DEBOUNCE_TYPES.has(prop.type)) {\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSetProperty(propCode, v), DEBOUNCE_MS);\n } else {\n onSetProperty(propCode, v);\n }\n },\n [propCode, prop.type, onSetProperty],\n );\n\n // Classification visual treatment (plan §B13). The schema endpoint\n // (B9 extended) populates field_access for classified properties too,\n // keyed by property_code. Same locked rendering as FieldsBlock.\n const access = fieldAccessInfo(entity, propCode);\n const readDenied = access?.read === \"denied\";\n const writeDenied = access?.write === \"denied\";\n const classificationLocked =\n mode === \"display\" ? readDenied : writeDenied || readDenied;\n const effectiveMode = mode === \"edit\" && writeDenied ? \"display\" : mode;\n\n return (\n <FieldRow label={loc(prop.display?.name ?? prop.name)} mode={effectiveMode} span={SPAN_FIELD_TYPES.has(prop.type)}>\n {classificationLocked ? (\n <ClassifiedFieldLocked\n tier={access!.classification}\n tierDef={classificationTier(schema, entity, propCode)}\n requiredPermission={access?.required_permission}\n locale={userLocale}\n variant={effectiveMode === \"edit\" ? \"input\" : \"value\"}\n />\n ) : (\n renderField({\n field,\n fieldName: propCode,\n value,\n mode: effectiveMode,\n onChange: effectiveMode === \"edit\" ? onChange : undefined,\n refs,\n locale: userLocale,\n })\n )}\n </FieldRow>\n );\n}\n","// StatusStepper - an ordered-stage pipeline for a status group.\n//\n// Resolves the design-brief P2 \"status deserves a real treatment\": an ordered\n// status group declared `display_mode: stepper` renders as a pipeline (stage\n// 1 -> 2 -> 3 -> done) instead of a loose pill. Past stages get a check,\n// the current stage is accent-highlighted, future stages are muted.\n//\n// Ported from docs/plans/assets/2026-05-22-detail-kit/kits/RecordHeaderKit.jsx.\n\nimport { Icon } from \"@/components/primitives\";\n\nexport interface StatusStage {\n code: string;\n label: string;\n}\n\ninterface StatusStepperProps {\n stages: StatusStage[];\n // The record's current status code in this group.\n current: string | undefined;\n // Codes the user may move to (current + allowed transitions). A stage not\n // in this set is rendered but not clickable. Omit for a read-only stepper.\n selectable?: Set<string>;\n onSelect?: (code: string) => void;\n compact?: boolean;\n}\n\nexport function StatusStepper({\n stages,\n current,\n selectable,\n onSelect,\n compact = false,\n}: StatusStepperProps) {\n const curIdx = stages.findIndex((s) => s.code === current);\n\n return (\n <div\n role=\"list\"\n style={{\n display: \"flex\",\n alignItems: \"stretch\",\n borderRadius: 6,\n overflow: \"hidden\",\n border: \"1px solid var(--border)\",\n background: \"var(--surface)\",\n }}\n >\n {stages.map((s, i) => {\n const isPast = curIdx >= 0 && i < curIdx;\n const isCurrent = i === curIdx;\n const isFuture = curIdx < 0 || i > curIdx;\n const clickable = !!onSelect && (selectable?.has(s.code) ?? false);\n const first = i === 0;\n const last = i === stages.length - 1;\n\n const bg = isCurrent\n ? \"var(--accent-soft)\"\n : isPast\n ? \"var(--success-soft)\"\n : \"transparent\";\n const fg = isCurrent ? \"var(--accent)\" : isPast ? \"var(--success)\" : \"var(--text-3)\";\n\n // Chevron clip-path: a notch on the right (points into the next\n // stage) and on the left (receives the previous). The first stage\n // keeps a flat left edge, the last a flat right edge.\n const clip =\n first && last\n ? undefined\n : first\n ? \"polygon(0 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 0 100%)\"\n : last\n ? \"polygon(0 0, 100% 0, 100% 100%, 0 100%, 10px 50%)\"\n : \"polygon(0 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 0 100%, 10px 50%)\";\n\n return (\n <button\n key={s.code}\n role=\"listitem\"\n type=\"button\"\n disabled={!clickable}\n onClick={clickable ? () => onSelect!(s.code) : undefined}\n title={s.label}\n style={{\n flex: 1,\n minWidth: 0,\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n padding: compact\n ? `6px 8px 6px ${first ? 10 : 20}px`\n : `8px 10px 8px ${first ? 12 : 24}px`,\n border: 0,\n borderLeft: i > 0 ? \"1px solid var(--divider)\" : \"none\",\n background: bg,\n color: fg,\n fontSize: compact ? 11.5 : 12.5,\n fontFamily: \"inherit\",\n fontWeight: isCurrent ? 600 : 500,\n cursor: clickable ? \"pointer\" : \"default\",\n clipPath: clip,\n whiteSpace: \"nowrap\",\n }}\n >\n <span\n style={{\n width: 16,\n height: 16,\n borderRadius: 999,\n flexShrink: 0,\n background: isFuture ? \"transparent\" : fg,\n border: isFuture ? \"1px solid var(--border-strong)\" : \"none\",\n color: isFuture\n ? \"var(--text-4)\"\n : isCurrent\n ? \"var(--accent-soft)\"\n : \"var(--success-soft)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: 9.5,\n fontWeight: 700,\n }}\n >\n {isPast ? <Icon.check size={10} /> : i + 1}\n </span>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\" }}>{s.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n","import { Pill, Select, type PillTone } from \"@/components/primitives\";\nimport { loc } from \"@/types/schema\";\nimport type { StatusGroupDef } from \"@/types/schema\";\nimport type { StatusInfo } from \"@/types/api\";\nimport { StatusStepper } from \"./StatusStepper\";\n\ninterface StatusGroupsDisplayProps {\n statusGroups: Record<string, StatusGroupDef>;\n objectStatuses: StatusInfo[] | undefined;\n onSetStatus: (groupCode: string, statusCode: string) => void;\n isPending: boolean;\n mode?: \"display\" | \"edit\";\n}\n\n// StatusGroupsDisplay renders each of an entity's status groups. A group\n// declared `display_mode: stepper` renders as an ordered pipeline\n// (StatusStepper); otherwise it renders as a read-only pill in display mode\n// and an editable dropdown in edit mode.\nexport function StatusGroupsDisplay({\n statusGroups,\n objectStatuses,\n onSetStatus,\n isPending,\n mode = \"edit\",\n}: StatusGroupsDisplayProps) {\n return (\n <div style={{ display: \"flex\", flexWrap: \"wrap\", alignItems: \"center\", gap: 16 }}>\n {Object.entries(statusGroups).map(([sgCode, sg]) => {\n const current = objectStatuses?.find((s) => s.group_code === sgCode);\n const currentCode = current?.status_code;\n const currentStatus = currentCode\n ? sg.statuses.find((s) => s.code === currentCode)\n : undefined;\n\n // Stepper rendering for ordered pipelines.\n if (sg.display_mode === \"stepper\") {\n const editable = mode === \"edit\";\n // Allowed moves: the current status plus its transition targets.\n // No `transitions` map = any status is reachable.\n const selectable = editable\n ? sg.transitions && currentCode\n ? new Set<string>([currentCode, ...(sg.transitions[currentCode] ?? [])])\n : new Set<string>(sg.statuses.map((s) => s.code))\n : undefined;\n return (\n <div key={sgCode} style={{ display: \"flex\", alignItems: \"center\", gap: 8, minWidth: 0 }}>\n <span style={{ fontSize: 12, color: \"var(--text-3)\", flexShrink: 0 }}>\n {loc(sg.name)}:\n </span>\n <div\n style={{\n minWidth: 240,\n opacity: isPending ? 0.6 : 1,\n pointerEvents: isPending ? \"none\" : \"auto\",\n }}\n >\n <StatusStepper\n stages={sg.statuses.map((s) => ({ code: s.code, label: loc(s.name) }))}\n current={currentCode}\n selectable={selectable}\n onSelect={editable ? (code) => onSetStatus(sgCode, code) : undefined}\n compact\n />\n </div>\n </div>\n );\n }\n\n if (mode === \"display\") {\n return (\n <div key={sgCode} style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <span style={{ fontSize: 12, color: \"var(--text-3)\" }}>{loc(sg.name)}:</span>\n {currentStatus ? (\n <Pill tone={(currentStatus.color as PillTone) ?? \"slate\"} size=\"xs\">\n {loc(currentStatus.name)}\n </Pill>\n ) : (\n <span style={{ fontSize: 12.5, color: \"var(--text-4)\" }}>—</span>\n )}\n </div>\n );\n }\n\n const available =\n sg.transitions && currentCode\n ? sg.statuses.filter(\n (s) =>\n s.code === currentCode ||\n (sg.transitions![currentCode]?.includes(s.code) ?? false),\n )\n : sg.statuses;\n\n const options = available.map((s) => ({\n value: s.code,\n label: (\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n {s.color && (\n <span\n style={{\n width: 8,\n height: 8,\n borderRadius: 999,\n background: `var(--pill-${s.color}, ${s.color})`,\n display: \"inline-block\",\n }}\n />\n )}\n {loc(s.name)}\n </span>\n ),\n }));\n\n return (\n <div key={sgCode} style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <span style={{ fontSize: 12, color: \"var(--text-3)\" }}>{loc(sg.name)}:</span>\n <div\n style={{\n minWidth: 180,\n opacity: isPending ? 0.6 : 1,\n pointerEvents: isPending ? \"none\" : \"auto\",\n }}\n >\n <Select\n value={currentCode ?? \"\"}\n options={options}\n onChange={(statusCode) => onSetStatus(sgCode, statusCode)}\n placeholder=\"Select…\"\n />\n </div>\n </div>\n );\n })}\n </div>\n );\n}\n","import { StatusGroupsDisplay } from \"@/components/shared/StatusGroupsDisplay\";\nimport type { LayoutNode } from \"@/types/schema\";\nimport type { LayoutContext } from \"./LayoutRenderer\";\n\nconst noop = () => {};\n\ninterface StatusesBlockProps {\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\n// StatusesBlock renders status group selectors for a `statuses: true` layout node.\n// Filters to a subset of status groups if the node value is an array of group codes.\nexport function StatusesBlock({ node, ctx }: StatusesBlockProps) {\n const { entity, objectStatuses, onSetStatus, isStatusPending, isNew } = ctx;\n const statusGroups = entity.status_groups ?? {};\n\n if (isNew) return null;\n\n // node.statuses is true (all groups) or an array of group codes (subset).\n const filter: string[] | true = node.statuses === true ? true : (node.statuses as string[]);\n\n const filtered = Object.fromEntries(\n Object.entries(statusGroups).filter(\n ([code]) => filter === true || (filter as string[]).includes(code),\n ),\n );\n\n if (Object.keys(filtered).length === 0) return null;\n\n return (\n <StatusGroupsDisplay\n statusGroups={filtered}\n objectStatuses={objectStatuses}\n onSetStatus={onSetStatus ?? noop}\n isPending={isStatusPending ?? false}\n mode={ctx.mode}\n />\n );\n}\n","import type { Entity, LayoutNode, Schema } from \"@/types/schema\";\nimport type { StatusInfo, PropertyValue } from \"@/types/api\";\nimport { LayoutRow } from \"./LayoutRow\";\nimport { LayoutColumn } from \"./LayoutColumn\";\nimport { LayoutTabs } from \"./LayoutTabs\";\nimport { SectionBlock } from \"./SectionBlock\";\nimport { ChildrenBlock } from \"./ChildrenBlock\";\nimport { AssociatedBlock } from \"./AssociatedBlock\";\nimport { FieldsBlock } from \"./FieldsBlock\";\nimport { PropertiesBlock } from \"./PropertiesBlock\";\nimport { StatusesBlock } from \"./StatusesBlock\";\n\nimport type { RefsMap } from \"@/components/fields\";\n\n// PendingChildRow represents a single child row in the pending edit state.\n// Used for direct (aggregate) children during parent edit mode.\nexport interface PendingChildRow {\n _key: string; // stable React key (id for existing, nanoid() for new)\n _deleted?: boolean; // marked for deletion (shown with strikethrough, excluded from save)\n _dirty?: boolean; // has any field been modified since edit mode started\n [field: string]: unknown;\n}\n\n// LayoutContext carries the parent record context needed by all layout nodes.\nexport interface LayoutContext {\n schema: Schema;\n entity: Entity;\n entityCode: string;\n recordId: string;\n record: Record<string, unknown>;\n isNew: boolean;\n mode: \"display\" | \"edit\";\n onFieldChange?: (field: string, value: unknown) => void;\n // $refs from the detail response (entity -> id -> record).\n refs?: RefsMap;\n // Section-level column count override for FieldsBlock (1 = stacked, 2 = side-by-side).\n columns?: number;\n // Properties and statuses state - required for `properties` and `statuses` leaf nodes.\n objectProperties?: PropertyValue[];\n onSetProperty?: (propertyCode: string, value: unknown) => void;\n isPropertyPending?: boolean;\n objectStatuses?: StatusInfo[];\n onSetStatus?: (groupCode: string, statusCode: string) => void;\n isStatusPending?: boolean;\n // Children editing (direct/aggregate children only).\n // undefined = no edits to any children yet; skip $children in payload.\n pendingChildren?: Record<string, PendingChildRow[]>;\n onChildrenChange?: (childCode: string, rows: PendingChildRow[]) => void;\n // Validation errors keyed by childCode -> rowKey -> fieldNames with errors.\n childValidationErrors?: Record<string, Record<string, string[]>>;\n // Parent-record field-level validation errors keyed by field name: a\n // present key is a one-line message shown inline under that field.\n // Populated by SmartDetailPage once a save has been attempted; mirrors the\n // childValidationErrors threading. undefined = nothing to show.\n fieldErrors?: Record<string, string>;\n // True when the current node tree is already rendered inside a card\n // (a `section` or a `tabs` panel). `children` / `associated` blocks use it\n // to decide whether to draw their own SectionCard chrome - top-level they\n // do, nested they don't (avoids a card inside a card).\n withinCard?: boolean;\n}\n\ninterface LayoutRendererProps {\n nodes: LayoutNode[];\n ctx: LayoutContext;\n}\n\n// LayoutRenderer renders an array of layout nodes, dispatching each to the\n// appropriate component based on which kind field is set.\nexport function LayoutRenderer({ nodes, ctx }: LayoutRendererProps) {\n return (\n <>\n {nodes.map((node, i) => (\n <LayoutNodeRenderer key={i} node={node} ctx={ctx} />\n ))}\n </>\n );\n}\n\ninterface LayoutNodeRendererProps {\n node: LayoutNode;\n ctx: LayoutContext;\n}\n\n// LayoutNodeRenderer dispatches a single node to the right component.\nexport function LayoutNodeRenderer({ node, ctx }: LayoutNodeRendererProps) {\n // Containers\n if (node.row) {\n return <LayoutRow config={node.row} ctx={ctx} />;\n }\n if (node.column) {\n return <LayoutColumn config={node.column} node={node} ctx={ctx} />;\n }\n if (node.tabs) {\n return <LayoutTabs config={node.tabs} ctx={ctx} />;\n }\n if (node.section) {\n return <SectionBlock config={node.section} ctx={ctx} />;\n }\n if (node.children) {\n return <ChildrenBlock node={node} ctx={ctx} />;\n }\n if (node.associated) {\n return <AssociatedBlock node={node} ctx={ctx} />;\n }\n if (node.fields) {\n return <FieldsBlock fields={node.fields} ctx={ctx} />;\n }\n if (node.properties != null) {\n return <PropertiesBlock node={node} ctx={ctx} />;\n }\n if (node.statuses != null) {\n return <StatusesBlock node={node} ctx={ctx} />;\n }\n\n return null;\n}\n\n// getNodeKind returns the kind string for a layout node, used for testing and debugging.\nexport function getNodeKind(node: LayoutNode): string | null {\n if (node.row) return \"row\";\n if (node.column) return \"column\";\n if (node.tabs) return \"tabs\";\n if (node.section) return \"section\";\n if (node.children) return \"children\";\n if (node.associated) return \"associated\";\n if (node.fields) return \"fields\";\n if (node.properties != null) return \"properties\";\n if (node.statuses != null) return \"statuses\";\n return null;\n}\n","// ErrorSummary - the top-of-form validation digest.\n//\n// Resolves the design-brief P1 \"validation is invisible\": required and email\n// errors used to surface only as a transient toast. This banner lists every\n// field error at the top of the edit form; clicking an entry scrolls to and\n// focuses that field. Pairs with the per-field inline error on FieldRow.\n//\n// Ported from docs/plans/assets/2026-05-22-detail-kit/kits/FieldRowKit.jsx.\n\nimport { Icon } from \"@/components/primitives\";\n\nexport interface FieldErrorEntry {\n // Field code - used to locate the input to scroll to.\n name: string;\n // Human label shown in the list.\n label: string;\n // One-line reason (\"Required\", \"Invalid email address\").\n message: string;\n}\n\ninterface ErrorSummaryProps {\n errors: FieldErrorEntry[];\n // Scrolls to / focuses the field with this code. Omit for a non-interactive\n // summary.\n onJump?: (name: string) => void;\n}\n\nexport function ErrorSummary({ errors, onJump }: ErrorSummaryProps) {\n if (errors.length === 0) return null;\n const plural = errors.length !== 1;\n\n return (\n <div\n role=\"alert\"\n style={{\n display: \"flex\",\n gap: 10,\n padding: \"10px 12px\",\n marginBottom: 12,\n borderRadius: 8,\n background: \"var(--danger-soft)\",\n border: \"1px solid var(--danger-border)\",\n color: \"var(--text)\",\n }}\n >\n <span style={{ color: \"var(--danger)\", marginTop: 1, display: \"flex\" }}>\n <Icon.warning size={14} />\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 12.5, fontWeight: 600, marginBottom: 4 }}>\n {errors.length} {plural ? \"fields need\" : \"field needs\"} attention\n </div>\n <ul\n style={{\n margin: 0,\n padding: 0,\n listStyle: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n }}\n >\n {errors.map((e) => (\n <li key={e.name}>\n <button\n type=\"button\"\n onClick={onJump ? () => onJump(e.name) : undefined}\n style={{\n background: \"transparent\",\n border: 0,\n padding: 0,\n cursor: onJump ? \"pointer\" : \"default\",\n fontSize: 12,\n textAlign: \"left\",\n fontFamily: \"inherit\",\n }}\n >\n <span style={{ color: \"var(--danger)\", marginRight: 6 }}>•</span>\n <span style={{ fontWeight: 500, color: \"var(--text-2)\" }}>{e.label}</span>\n <span style={{ color: \"var(--text-3)\" }}> - {e.message}</span>\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n );\n}\n","// HighlightsStrip - a compact summary of a record's key fields, shown under\n// the title in the record header.\n//\n// Resolves part of the design-brief P2 \"the record header is thin\": enterprise\n// record pages lead with a few at-a-glance values. Which fields appear is\n// declared per detail screen via `highlights: [code, ...]`.\n//\n// Pure presentational - the caller resolves each code to a {label, value}.\n// Ported from docs/plans/assets/2026-05-22-detail-kit/kits/RecordHeaderKit.jsx.\n\nimport { Fragment, type ReactNode } from \"react\";\n\nexport interface HighlightItem {\n label: string;\n value: ReactNode;\n}\n\ninterface HighlightsStripProps {\n items: HighlightItem[];\n}\n\nexport function HighlightsStrip({ items }: HighlightsStripProps) {\n if (items.length === 0) return null;\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n flexWrap: \"wrap\",\n gap: \"6px 0\",\n fontSize: 12,\n }}\n >\n {items.map((it, i) => (\n <Fragment key={i}>\n {i > 0 && (\n <span\n style={{\n width: 1,\n height: 12,\n background: \"var(--divider)\",\n marginInline: 12,\n }}\n />\n )}\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, lineHeight: 1.3 }}>\n <span style={{ color: \"var(--text-3)\", fontSize: 11.5 }}>{it.label}</span>\n <span style={{ color: \"var(--text)\" }}>{it.value}</span>\n </span>\n </Fragment>\n ))}\n </div>\n );\n}\n","// DetailHighlights - resolves a detail screen's `highlights: [code…]` list\n// into a HighlightsStrip. Each code is a field name or a status group code.\n//\n// Kept separate from RecordHeader so the header stays pure presentational and\n// SmartDetailPage stays lean.\n\nimport { renderSchemaField, resolveSchemaFieldLabel } from \"@/components/file-widgets\";\nimport { Pill, type PillTone } from \"@/components/primitives\";\nimport { loc } from \"@/types/schema\";\nimport type { Entity, Schema, Screen } from \"@/types/schema\";\nimport type { StatusInfo } from \"@/types/api\";\nimport type { RefsMap } from \"@/components/fields\";\nimport { HighlightsStrip, type HighlightItem } from \"@/components/shared/HighlightsStrip\";\n\ninterface DetailHighlightsProps {\n screen: Screen;\n entity: Entity;\n entityCode: string;\n record: Record<string, unknown>;\n refs?: RefsMap;\n objectStatuses?: StatusInfo[];\n schema: Schema;\n locale: string;\n}\n\nexport function DetailHighlights({\n screen,\n entity,\n entityCode,\n record,\n refs,\n objectStatuses,\n schema,\n locale,\n}: DetailHighlightsProps) {\n const codes = screen.highlights;\n if (!codes || codes.length === 0) return null;\n\n const items: HighlightItem[] = [];\n for (const code of codes) {\n // A status group code -> the current status as a pill.\n const group = entity.status_groups?.[code];\n if (group) {\n const current = objectStatuses?.find((s) => s.group_code === code);\n const def = current\n ? group.statuses.find((s) => s.code === current.status_code)\n : undefined;\n items.push({\n label: loc(group.name),\n value: def ? (\n <Pill tone={(def.color as PillTone) ?? \"slate\"} size=\"xs\">\n {loc(def.name)}\n </Pill>\n ) : (\n <span style={{ color: \"var(--text-4)\" }}>—</span>\n ),\n });\n continue;\n }\n\n // A field (or file field) -> its compact display value.\n if (entity.fields.get(code) || entity.files?.get(code)) {\n const rendered = renderSchemaField({\n entity,\n entityCode,\n fieldName: code,\n record,\n mode: \"display\",\n refs,\n entities: schema.entities,\n screens: schema.screens,\n surface: \"list\",\n locale,\n });\n items.push({\n label: resolveSchemaFieldLabel(entity, code, schema.entities),\n value: rendered,\n });\n continue;\n }\n // Unknown code: silently skipped.\n }\n\n if (items.length === 0) return null;\n return <HighlightsStrip items={items} />;\n}\n","// AuditMetaStrip - the record's provenance strip.\n//\n// Resolves the design-brief P3 \"audit footer is an afterthought\": a designed\n// strip for who created / last updated the record and when.\n//\n// Pure presentational. The caller (DetailAuditStrip) resolves the schema's\n// `audit:` config into a list of lines, with each line's label and values\n// already rendered - timestamps through the field pipeline (relative time),\n// actor refs resolved to user names. This component is only the layout.\n// It renders nothing when every line is empty.\n\nimport type { ReactNode } from \"react\";\n\n// AuditStripLine is one resolved provenance line: a label and the rendered\n// actor / timestamp values. `by` and `at` are omitted when the backing field\n// is absent on the entity or null on the record.\nexport interface AuditStripLine {\n label: ReactNode;\n by?: ReactNode;\n at?: ReactNode;\n}\n\ninterface AuditMetaStripProps {\n lines: AuditStripLine[];\n}\n\nfunction hasContent(line: AuditStripLine): boolean {\n return line.by != null || line.at != null;\n}\n\nexport function AuditMetaStrip({ lines }: AuditMetaStripProps) {\n const visible = lines.filter(hasContent);\n if (visible.length === 0) return null;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"8px 24px\",\n padding: \"10px 14px\",\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n fontSize: 11.5,\n color: \"var(--text-3)\",\n }}\n >\n {visible.map((line, i) => (\n <AuditItem key={i} line={line} />\n ))}\n </div>\n );\n}\n\nfunction AuditItem({ line }: { line: AuditStripLine }) {\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, minWidth: 0 }}>\n <span\n style={{\n fontSize: 11,\n fontWeight: 600,\n color: \"var(--text-4)\",\n flexShrink: 0,\n }}\n >\n {line.label}\n </span>\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, minWidth: 0 }}>\n {line.by != null && <span style={{ color: \"var(--text-2)\" }}>{line.by}</span>}\n {line.by != null && line.at != null && (\n <span style={{ color: \"var(--text-4)\" }}>·</span>\n )}\n {line.at != null && <span className=\"num\">{line.at}</span>}\n </span>\n </div>\n );\n}\n","// DetailAuditStrip - renders a record's provenance (who created / updated it\n// and when) into an AuditMetaStrip.\n//\n// Audit metadata is universal: every entity carries it, and it is the same\n// few fields. So the platform renders it automatically at the foot of every\n// detail page rather than making each schema author re-declare a \"System\"\n// section.\n//\n// The screen may override the default via `audit:` (see docs/dsl.md):\n// - omitted -> the automatic Created / Updated lines below\n// - enabled: false -> no strip\n// - lines: [...] -> those explicit lines instead of the convention\n//\n// Each audit field renders through the normal `renderSchemaField` pipeline -\n// the same path every field uses everywhere. So a timestamp shows relative\n// time, and `created_by` (a ref to `user`) shows the user's display name,\n// resolved from the entity's own `display` config. Nothing is hand-resolved.\n//\n// Robust to entities that lack some audit fields: a field absent from the\n// entity, or null on the record, is simply skipped. A line with no resolved\n// value is dropped; a strip with no lines renders nothing.\n\nimport type { ReactNode } from \"react\";\nimport { renderSchemaField } from \"@/components/file-widgets\";\nimport { loc } from \"@/types/schema\";\nimport type { Entity, Schema, Screen } from \"@/types/schema\";\nimport type { RefsMap } from \"@/components/fields\";\nimport { AuditMetaStrip, type AuditStripLine } from \"@/components/shared/AuditMetaStrip\";\n\ninterface DetailAuditStripProps {\n screen: Screen;\n entity: Entity;\n entityCode: string;\n record: Record<string, unknown>;\n refs?: RefsMap;\n schema: Schema;\n locale: string;\n}\n\nexport function DetailAuditStrip({\n screen,\n entity,\n entityCode,\n record,\n refs,\n schema,\n locale,\n}: DetailAuditStripProps) {\n const audit = screen.audit;\n // Explicit opt-out.\n if (audit?.enabled === false) return null;\n\n // Render one audit field through the standard field pipeline, or undefined\n // when the entity has no such field / the record value is empty.\n const renderAudit = (fieldName: string | undefined): ReactNode => {\n if (!fieldName || !entity.fields.get(fieldName)) return undefined;\n const value = record[fieldName];\n if (value == null || value === \"\") return undefined;\n return renderSchemaField({\n entity,\n entityCode,\n fieldName,\n record,\n mode: \"display\",\n refs,\n entities: schema.entities,\n screens: schema.screens,\n surface: \"list\",\n locale,\n });\n };\n\n // Explicit `lines:` replace the convention; otherwise the platform default\n // is the Created / Updated pair over the convention fields.\n const lines: AuditStripLine[] =\n audit?.lines && audit.lines.length > 0\n ? audit.lines.map((line) => ({\n label: loc(line.label),\n at: renderAudit(line.at),\n by: renderAudit(line.by),\n }))\n : [\n { label: \"Created\", at: renderAudit(\"created_at\"), by: renderAudit(\"created_by\") },\n { label: \"Updated\", at: renderAudit(\"updated_at\"), by: renderAudit(\"updated_by\") },\n ];\n\n return <AuditMetaStrip lines={lines} />;\n}\n","// RecordHeader - the detail-page identity zone.\n//\n// Resolves the design-brief P2 \"the record header is thin\": a real record\n// identity zone with an entity glyph, the title, the record id, and a\n// highlights strip - the pattern enterprise record pages (Salesforce, Linear)\n// lead with.\n//\n// No breadcrumb: the app shell already renders a navigation breadcrumb in the\n// top bar, so a second one here is a duplicate. A Back button covers the\n// \"return to the list\" affordance.\n//\n// Pure presentational. The caller (SmartDetailPage's DetailHeader) keeps all\n// action dispatch and passes the resolved action bar / save-cancel chrome via\n// the `right` slot, so this component stays trivially testable.\n\nimport type { ReactNode } from \"react\";\nimport { Avatar, Icon, IconBtn, Pill, RecordIdBadge } from \"@/components/primitives\";\n\ninterface RecordHeaderProps {\n // Entity glyph - a rendered icon node. Shown in an accent-tinted tile.\n icon?: ReactNode;\n // Alternative to `icon`: render a generated avatar from this name.\n avatarName?: string;\n title: string;\n recordId?: string;\n // Pre-rendered highlights strip (a HighlightsStrip element). Shown under\n // the title in display mode only.\n highlights?: ReactNode;\n mode: \"display\" | \"edit\" | \"create\";\n // True when an existing record has unsaved edits.\n dirty?: boolean;\n showBack?: boolean;\n onBack?: () => void;\n // Action bar (display) or save/cancel/restore chrome (edit) - built by the\n // caller because it needs action dispatch.\n right?: ReactNode;\n}\n\nexport function RecordHeader({\n icon,\n avatarName,\n title,\n recordId,\n highlights,\n mode,\n dirty = false,\n showBack = false,\n onBack,\n right,\n}: RecordHeaderProps) {\n return (\n <div style={{ display: \"flex\", alignItems: \"flex-start\", gap: 10 }}>\n {showBack && (\n <div style={{ paddingTop: 4, flexShrink: 0 }}>\n <IconBtn size={26} icon={<Icon.back size={14} />} title=\"Back\" onClick={onBack} />\n </div>\n )}\n\n {(avatarName || icon) && (\n <div style={{ flexShrink: 0, paddingTop: 2 }}>\n {avatarName ? (\n <Avatar name={avatarName} size={32} />\n ) : (\n <span\n style={{\n width: 32,\n height: 32,\n borderRadius: 8,\n background: \"var(--accent-soft)\",\n color: \"var(--accent)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {icon}\n </span>\n )}\n </div>\n )}\n\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, flexWrap: \"wrap\" }}>\n <h1\n style={{\n margin: 0,\n fontSize: 19,\n fontWeight: 600,\n letterSpacing: \"-0.011em\",\n lineHeight: 1.2,\n color: \"var(--text)\",\n minWidth: 0,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n maxWidth: \"100%\",\n }}\n >\n {title}\n </h1>\n {recordId && <RecordIdBadge value={recordId} />}\n {mode === \"edit\" && dirty && (\n <span\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 5,\n fontSize: 11,\n color: \"var(--warn)\",\n }}\n >\n <span\n style={{ width: 6, height: 6, borderRadius: 999, background: \"var(--warn)\" }}\n />\n Unsaved\n </span>\n )}\n {mode === \"create\" && (\n <Pill tone=\"violet\" size=\"xs\" dot>\n New record\n </Pill>\n )}\n </div>\n {highlights && mode === \"display\" && <div style={{ marginTop: 8 }}>{highlights}</div>}\n </div>\n\n {right && (\n <div\n role=\"toolbar\"\n aria-label=\"Page actions\"\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n flexShrink: 0,\n flexWrap: \"wrap\",\n justifyContent: \"flex-end\",\n }}\n >\n {right}\n </div>\n )}\n </div>\n );\n}\n","import type { Entity, Schema } from \"@/types/schema\";\nimport type { PendingChildRow } from \"@/components/detail-layout/LayoutRenderer\";\n\n// findMissingRequiredFields returns the names of fields on `entity` that are\n// marked `required: true` on the schema, are not server-generated (auto), are\n// not the primary key during a new-record save, and are missing from\n// `formData` (null, undefined, or empty string).\n//\n// Reason: the AssociatedBlock child-row table already validates required\n// fields client-side before POST (see AssociatedBlock.tsx:809). The top-level\n// parent form (SmartDetailPage) did NOT, so clicking Save on an empty\n// /<entity>/new form would happily POST to /api/data/<entity> and create a\n// row with all-null required fields. The server's Data.Create currently\n// trusts the prefilter + entity-level constraints and does not block \"all\n// nulls submitted by an authenticated admin\" - that's a defense gap\n// tracked separately. Client-side validation is the user-visible fix here.\n//\n// Naming: `findMissing` (not `validate`) because the function answers\n// \"which required fields are blank?\" rather than \"is this whole record\n// valid?\" - the caller decides what to do with the list (toast, inline,\n// block submit).\nexport function findMissingRequiredFields(\n entity: Entity,\n formData: Record<string, unknown>,\n isNew: boolean,\n): string[] {\n const missing: string[] = [];\n for (const [name, field] of entity.fields) {\n if (!field.required) continue;\n if (field.auto) continue;\n // Skip the primary key on new records - the server generates it.\n if (isNew && field.primary) continue;\n // Skip the tenant_key field. It is `required` in the schema but the\n // frontend is tenant-agnostic and never carries it in the payload - the\n // server injects it from caller context (data_create.go auto-inject).\n // Flagging it here blocks every legitimate UI create of a tenant-scoped\n // entity (member, role, group, ...). Mirrors the server-side skip in\n // store/data_validation.go validateRequired.\n if (entity.tenant_key && name === entity.tenant_key) continue;\n // Skip required fields that have a schema-level default. Reason: the\n // form initialises new records with these defaults, and even if the\n // payload doesn't carry them explicitly, the server's prefilter +\n // default injection (data.go injectPrefilterDefaults) will apply the\n // YAML default. Flagging them as missing would block a Save the user\n // intended to make with all defaults accepted - false positive UX.\n if (field.default !== undefined && field.default !== null) continue;\n const v = formData[name];\n if (v === null || v === undefined || v === \"\") {\n missing.push(name);\n }\n }\n return missing;\n}\n\n// emailFormatRe mirrors the server check (store/data_validation.go\n// emailFormatRe): non-empty local part, '@', domain with a dot, no\n// whitespace. The server is authoritative; this is UX - it lets the form\n// reject a junk email before the round-trip.\nconst emailFormatRe = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/;\n\n// findInvalidEmailFields returns the names of `type: email` fields on\n// `entity` whose value in `formData` is a non-empty, non-email-shaped\n// string. Empty values are not flagged - required-ness is a separate check.\nexport function findInvalidEmailFields(\n entity: Entity,\n formData: Record<string, unknown>,\n): string[] {\n const invalid: string[] = [];\n for (const [name, field] of entity.fields) {\n if (field.type !== \"email\") continue;\n const v = formData[name];\n if (typeof v === \"string\" && v !== \"\" && !emailFormatRe.test(v)) {\n invalid.push(name);\n }\n }\n return invalid;\n}\n\n// collectFKFields returns the set of FK field names for a given child entity\n// by scanning all parent entities' children relations.\nfunction collectFKFields(childCode: string, schema: Schema): Set<string> {\n const fks = new Set<string>();\n for (const entity of Object.values(schema.entities)) {\n if (!entity.children) continue;\n for (const rel of entity.children) {\n if (rel.entity === childCode && rel.foreign_key) {\n const fkArr = Array.isArray(rel.foreign_key) ? rel.foreign_key : [rel.foreign_key];\n for (const fk of fkArr) {\n fks.add(fk);\n }\n }\n }\n }\n return fks;\n}\n\n// validatePendingChildren checks all non-deleted rows across all child codes\n// against their entity's required fields. Returns a map of validation errors:\n// { childCode: { rowKey: [fieldName, ...] } }\n// Empty object = no errors.\nexport function validatePendingChildren(\n pendingChildren: Record<string, PendingChildRow[]> | undefined,\n schema: Schema | undefined,\n): Record<string, Record<string, string[]>> {\n if (!pendingChildren || !schema) return {};\n\n const errors: Record<string, Record<string, string[]>> = {};\n\n for (const [childCode, rows] of Object.entries(pendingChildren)) {\n const childEntity = schema.entities[childCode];\n if (!childEntity) continue;\n\n // FK fields are auto-injected server-side, skip them.\n const fkFields = collectFKFields(childCode, schema);\n\n // Find required fields (non-auto, non-primary, non-FK, non-tenant_key).\n // The tenant_key is required in the schema but server-injected from\n // caller context - the frontend never carries it.\n const requiredFields: string[] = [];\n for (const [fieldName, field] of childEntity.fields) {\n if (childEntity.tenant_key && fieldName === childEntity.tenant_key) continue;\n if (field.required && !field.auto && !field.primary && !fkFields.has(fieldName)) {\n requiredFields.push(fieldName);\n }\n }\n if (requiredFields.length === 0) continue;\n\n for (const row of rows) {\n if (row._deleted) continue;\n\n const missingFields: string[] = [];\n for (const fieldName of requiredFields) {\n const val = row[fieldName];\n if (val == null || val === \"\") {\n missingFields.push(fieldName);\n }\n }\n if (missingFields.length > 0) {\n if (!errors[childCode]) errors[childCode] = {};\n errors[childCode][row._key] = missingFields;\n }\n }\n }\n\n return errors;\n}\n","import { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport { useParams, useLocation, useSearchParams } from \"react-router-dom\";\nimport { formatOffsetTimestamp, formatWireDateTime } from \"@/lib/timestamp\";\nimport { Btn, Icon, IconBtn, Spinner } from \"@/components/primitives\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useEntityItem } from \"@/hooks/useEntityItem\";\nimport { useCreateEntity, useUpdateEntity, useDeleteEntity, useRestoreEntity } from \"@/hooks/useMutations\";\nimport { ApiError, type StatusInfo, type PropertyValue } from \"@/types/api\";\nimport { DetailSkeleton } from \"@/components/shared/LoadingSkeleton\";\nimport { ConfirmDialog } from \"@/components/shared/ConfirmDialog\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { executeAction as runActionApi, buildActionBody } from \"@/api/data\";\nimport { AssociationTabs } from \"@/components/shared/AssociationTabs\";\nimport { useTabStore } from \"@/stores/tabs\";\nimport { applyRowState, defaultFlatForSlot, filterLayoutForSlot, type ResolvedActionLayoutNode } from \"@/lib/actions\";\nimport { dispatchAction } from \"@/lib/action-dispatch\";\nimport { ResponsiveActionBar, type WidgetContext } from \"@/components/shared/ResponsiveActionBar\";\nimport { useTabNavigate } from \"@/hooks/useTabNavigate\";\nimport { useEmbed, useEmbedNavigate, useReportEmbedDirty } from \"@/embed\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport {\n loc,\n getDisplayValue,\n getPrimaryKeyFields,\n extractPkValues,\n pkValuesToId,\n pkValuesFromId,\n entityAllowsMutation,\n} from \"@/types/schema\";\nimport type { PkValues } from \"@/types/schema\";\nimport {\n LayoutRenderer,\n ErrorSummary,\n DetailHighlights,\n DetailAuditStrip,\n type LayoutContext,\n type PendingChildRow,\n type FieldErrorEntry,\n} from \"@/components/detail-layout\";\nimport { StatusGroupsDisplay } from \"@/components/shared/StatusGroupsDisplay\";\nimport { RecordHeader } from \"@/components/shared/RecordHeader\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { findMissingRequiredFields, findInvalidEmailFields, validatePendingChildren } from \"@/lib/children-validation\";\nimport { fieldDisplayName } from \"@/types/schema\";\nimport { takePeekDraft } from \"@/components/list/peekDraftStore\";\nimport {\n FileDraftScope,\n discardFile,\n useFileDraftScope,\n buildFilesPayload,\n collectCommittedFileIds,\n getEntityFileField,\n getSchemaFieldValue,\n serializeSchemaFieldValue,\n} from \"@/components/file-widgets\";\n\nconst noop = () => {};\n\n// Deep comparison of two arrays of plain objects for detecting children changes.\nfunction arraysEqual(a: Record<string, unknown>[], b: Record<string, unknown>[]): boolean {\n if (a.length !== b.length) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n// childRowKey derives a stable React/PendingChildRow `_key` for a server-\n// returned child row. Stable across re-renders and round-trips because it's\n// derived from the child entity's primary key (supports composite PK via\n// pkValuesToId). Falls back to row.id, then to a fresh UUID, so unknown\n// schemas or PK-less rows still render with unique keys instead of colliding.\n// Reason: extracted from the URL edit-mode hydration so that the effect body\n// stays focused on \"what to do\" while this owns the \"how to key a row\"\n// invariant; matches what the legacy startEditing helper did before refactor.\nfunction childRowKey(\n childEntity: import(\"@/types/schema\").Entity | undefined,\n row: Record<string, unknown>,\n): string {\n if (childEntity) {\n const pkValues = extractPkValues(childEntity, row);\n const joined = pkValuesToId(pkValues);\n if (joined.replace(/^\\|+|\\|+$/g, \"\")) return joined;\n }\n if (row.id != null) return String(row.id);\n return crypto.randomUUID();\n}\n\ninterface SmartDetailPageProps {\n screenCode: string;\n}\n\nexport function SmartDetailPage({ screenCode }: SmartDetailPageProps) {\n return (\n <FileDraftScope>\n <SmartDetailPageInner screenCode={screenCode} />\n </FileDraftScope>\n );\n}\n\nfunction SmartDetailPageInner({ screenCode }: SmartDetailPageProps) {\n const { id: urlId } = useParams<{ id: string }>();\n // Embed-aware navigation: in embed mode every internal screen transition\n // here (cancel-edit, create/delete success, not-found Back) preserves the\n // embed query params (`self` mode) or emits a `navigation-requested` and stays\n // put (`delegated` mode). Outside embed mode it is plain `useNavigate`.\n const navigate = useEmbedNavigate();\n const [searchParams, setSearchParams] = useSearchParams();\n const { data: schema } = useSchema();\n const selfUser = useAuthStore((s) => s.user);\n const draftScope = useFileDraftScope();\n\n const screen = schema?.screens[screenCode];\n const entity = screen?.entity ? schema?.entities[screen.entity] : undefined;\n const entityCode = screen?.entity;\n const canCreate = entityAllowsMutation(entity, \"create\");\n const canUpdate = entityAllowsMutation(entity, \"update\");\n const canDelete = entityAllowsMutation(entity, \"delete\");\n const canRestore = entityAllowsMutation(entity, \"restore\");\n\n // Singleton entities whose PK is bound by the entity's prefilter to a\n // caller-scoped value (`$caller.user_id` or `$caller.tenant_id`) resolve\n // their PK from the authenticated user instead of a URL `:id` segment.\n // The route registered for the detail screen is just the screen's\n // declared `route:` (e.g. `/profile`, `/branding`) without a parameter.\n //\n // Bindings the frontend resolves today:\n // $caller.user_id -> selfUser.id\n // $caller.tenant_id -> selfUser.tenant?.id\n //\n // `$caller.attributes.<key>` is valid on the backend prefilter but not\n // resolved here yet (AuthUser doesn't carry attributes). A singleton\n // entity using that form falls through to URL `:id` mode; the page\n // shows a clean 404 from the server's prefilter mismatch rather than\n // crashing.\n //\n // See docs/plans/2026-05-01-profile-singleton-fix.md.\n const singletonCallerPk = (() => {\n if (!entity || !entity.singleton || !entity.prefilter) return undefined;\n const pkFields = getPrimaryKeyFields(entity);\n if (pkFields.length !== 1) return undefined;\n const binding = entity.prefilter[pkFields[0]];\n if (binding === \"$caller.user_id\") return selfUser?.id;\n if (binding === \"$caller.tenant_id\") return selfUser?.tenant?.id;\n return undefined;\n })();\n const isSingletonByCaller = singletonCallerPk !== undefined;\n const id = isSingletonByCaller ? singletonCallerPk : urlId;\n\n const isNew = !isSingletonByCaller && id === \"new\";\n\n // Compute PK values for the API call.\n const pkValues: PkValues | string | undefined = useMemo(() => {\n if (!id || isNew) return undefined;\n if (!entity) return id;\n const pkFields = getPrimaryKeyFields(entity);\n if (pkFields.length === 1) {\n return { [pkFields[0]]: id };\n }\n const parts = pkValuesFromId(id, pkFields.length);\n if (!parts) return undefined;\n const values: PkValues = {};\n for (let i = 0; i < pkFields.length; i++) {\n values[pkFields[i]] = parts[i] ?? \"\";\n }\n return values;\n }, [id, isNew, entity]);\n\n const pkCacheKey = useMemo(() => {\n if (!pkValues) return undefined;\n if (typeof pkValues === \"string\") return pkValues;\n return pkValuesToId(pkValues);\n }, [pkValues]);\n\n const { data: itemResponse, isLoading } = useEntityItem(entityCode, pkValues);\n\n const createMutation = useCreateEntity(entityCode ?? \"\");\n const updateMutation = useUpdateEntity(entityCode ?? \"\");\n const deleteMutation = useDeleteEntity(entityCode ?? \"\");\n const restoreMutation = useRestoreEntity(entityCode ?? \"\");\n\n // $statuses and $properties are enriched inline on the record by\n // handlers/data.go:getByPKValues. Reading them directly from the\n // entity-item query result avoids a second round-trip AND an old bug\n // where the detail page fired GET /api/data/{entity}/{id} - a path\n // that does not exist and silently 404'd, leaving status/property\n // dropdowns blank even though the data was already in hand. See\n // TestGetByPKEnrichment for the backend contract.\n const hasStatusGroups = Object.keys(entity?.status_groups ?? {}).length > 0;\n const itemData = itemResponse?.data as Record<string, unknown> | undefined;\n const statuses = (itemData?.[\"$statuses\"] as StatusInfo[] | undefined);\n const objectProperties = (itemData?.[\"$properties\"] as PropertyValue[] | undefined);\n const openInEdit = searchParams.get(\"edit\") === \"true\";\n const [editing, setEditing] = useState(isNew || openInEdit);\n const [formData, setFormData] = useState<Record<string, unknown>>({});\n const [pendingStatuses, setPendingStatuses] = useState<Record<string, string>>({});\n const [pendingProperties, setPendingProperties] = useState<Record<string, unknown>>({});\n const [pendingChildren, setPendingChildren] = useState<Record<string, PendingChildRow[]> | undefined>(undefined);\n const [staleRecord, setStaleRecord] = useState(false);\n // Snapshot of $children at the time edit mode was entered, used to detect actual changes on save.\n const [childrenSnapshot, setChildrenSnapshot] = useState<Record<string, Record<string, unknown>[]> | undefined>(undefined);\n const [deleteConfirm, setDeleteConfirm] = useState<{ show: boolean; confirmation?: string }>({ show: false });\n // validationVisible gates inline field errors: false on a fresh form so an\n // untouched create page is not painted red, flipped true on the first Save\n // attempt. The parentValidationErrors memo then recomputes live as the user\n // fixes fields.\n const [validationVisible, setValidationVisible] = useState(false);\n const editAllowed = isNew ? canCreate : canUpdate;\n const effectiveEditing = editing && editAllowed;\n\n // Embed-mode unsaved-changes channel. The platform guards dirty edits only\n // at tab-close; embed mode has no tabs, so the route-level guard\n // (EmbedUnsavedGuard) needs a dirty signal. Report the same condition the\n // header uses to render its visible \"unsaved\" dot (`editing && !isNew`):\n // an existing record being edited. A fresh-create form is intentionally\n // NOT reported - it has no prior state to lose and starts in edit mode.\n // The hook is fully inert outside embed mode and resets to clean on\n // unmount, so the next embedded screen never inherits a stale flag.\n const embed = useEmbed();\n useReportEmbedDirty(effectiveEditing && !isNew, embed.enabled);\n\n // Sidebar resize: use a ref for live DOM updates during drag to avoid\n // re-rendering the entire component tree on every mousemove. Commit to\n // React state only on mouseup.\n const [sidebarW, setSidebarW] = useState(() => {\n const w = parseInt(screen?.sidebar_width || \"380\", 10);\n return isNaN(w) ? 380 : w;\n });\n const sidebarRef = useRef<HTMLDivElement>(null);\n const sidebarDrag = useRef<{ startX: number; startW: number } | null>(null);\n const onSidebarDragStart = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n const el = sidebarRef.current;\n if (!el) return;\n const startW = el.offsetWidth;\n sidebarDrag.current = { startX: e.clientX, startW };\n const onMove = (ev: MouseEvent) => {\n if (!sidebarDrag.current) return;\n const delta = sidebarDrag.current.startX - ev.clientX;\n const next = Math.max(280, Math.min(800, sidebarDrag.current.startW + delta));\n el.style.width = `${next}px`;\n };\n const onUp = () => {\n if (sidebarDrag.current && el) {\n setSidebarW(parseInt(el.style.width, 10) || sidebarDrag.current.startW);\n }\n sidebarDrag.current = null;\n document.removeEventListener(\"mousemove\", onMove);\n document.removeEventListener(\"mouseup\", onUp);\n };\n document.addEventListener(\"mousemove\", onMove);\n document.addEventListener(\"mouseup\", onUp);\n },\n [],\n );\n\n // Record data from API\n const record = itemResponse?.data as Record<string, unknown> | undefined;\n const rowVersion = typeof record?.$row_version === \"string\" ? record.$row_version : undefined;\n // Extract $refs from the record (detail responses embed refs inside the data object).\n const recordRefs = record?.[\"$refs\"] as Record<string, Record<string, Record<string, unknown>>> | undefined;\n const previousRowVersion = useRef<string | undefined>(rowVersion);\n\n // Stale-record detection: when the record's row_version changes\n // beneath an active edit, flag it so the user knows their copy is\n // outdated. Must live with the other hooks (above the early\n // returns), not deeper in render flow — otherwise the hook count\n // changes between \"loading\" and \"loaded\" renders and React throws\n // #310 \"Rendered more hooks than during the previous render\".\n useEffect(() => {\n if (!rowVersion) return;\n const previous = previousRowVersion.current;\n previousRowVersion.current = rowVersion;\n if (previous && previous !== rowVersion && editing) {\n setStaleRecord(true);\n }\n }, [editing, rowVersion]);\n\n // Soft-delete detection: if entity has soft_delete and the column is non-null, the record is deleted.\n const hasSoftDelete = !!entity?.soft_delete;\n const isRecordDeleted = hasSoftDelete && record != null && record[entity!.soft_delete!] != null;\n\n // Tab tracking: ensure this page has a tab and update title when record loads.\n const location = useLocation();\n const { openTab, updateTabTitle, findTabByPathname } = useTabStore();\n useEffect(() => {\n // Embed mode has no tab UI (plan Decision 15): the tab store is\n // persisted per tenant+user, so writing to it from an embedded screen\n // would leak spurious tabs into the user's normal Declarion session.\n if (!entity || isSingletonByCaller || embed.enabled) return;\n const pathname = location.pathname;\n const tabName = loc(screen?.display?.name) || loc(entity.display.name);\n const tabIcon = screen?.display?.icon || entity.display.icon;\n const existing = findTabByPathname(pathname);\n if (!existing) {\n openTab({\n pathname,\n search: \"\",\n title: isNew ? `New ${tabName}` : tabName,\n icon: tabIcon,\n closable: true,\n });\n }\n }, [location.pathname, screen?.display?.name?.en, entity?.display.name?.en, screen?.display?.icon, entity?.display.icon, isNew, isSingletonByCaller, embed.enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n if (!record || !entity || isSingletonByCaller || embed.enabled) return;\n const tab = findTabByPathname(location.pathname);\n if (tab) {\n // getDisplayValue prefers $display, falls back to display_field, and\n // unwraps localized JSONB via loc() — it covers every shape the row\n // value can take. The entity-name floor (screen name or entity display\n // name) covers the case where getDisplayValue is still undefined.\n // Reason: Finding 4 - the tab must never show a raw entity UUID/path.\n const entityFloor = loc(screen?.display?.name) || loc(entity.display.name);\n const resolved = getDisplayValue(record as Record<string, unknown>, entity);\n // Reject any pathname-shaped resolved title: a leading slash means it is\n // a route path, never a record label. Fall back to the entity floor.\n const displayTitle =\n resolved && !String(resolved).startsWith(\"/\") ? String(resolved) : entityFloor;\n updateTabTitle(tab.id, displayTitle);\n }\n }, [record, entity?.display.name?.en, entity?.display.display_field, location.pathname, isSingletonByCaller, embed.enabled]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Editable fields (non-auto, non-primary)\n const editableFields = useMemo(() => {\n if (!entity || !editAllowed) return new Set<string>();\n const editable = new Set<string>();\n for (const [name, field] of entity.fields) {\n if (!field.auto && !field.primary) {\n editable.add(name);\n }\n }\n for (const [name] of entity.files ?? []) {\n editable.add(name);\n }\n return editable;\n }, [entity, editAllowed]);\n\n // Handle ?edit=true URL param: enter edit mode, populate form data, and\n // seed child editing state. Handles both initial mount and already-mounted\n // tab receiving the param. When the navigation was a QuickPeek \"Open full\"\n // with uncommitted edits, peekDraftStore holds the draft keyed by record id\n // — merge it on top of the server record so the detail page opens\n // pre-filled with those edits.\n //\n // Reason: this effect is the single entry point for edit-mode initialization\n // (the legacy `startEditing` helper was removed). It MUST be the parity\n // replacement — that includes seeding pendingChildren for \"aggregate-only\"\n // entities (no editable parent fields, children: declared), otherwise the\n // Edit action becomes a no-op for those entities.\n useEffect(() => {\n if (!openInEdit || !record || !entity || !canUpdate) return;\n // Enter edit mode if not already editing\n if (!editing) setEditing(true);\n // Populate form data if empty (avoid clobbering in-progress edits)\n if (Object.keys(formData).length === 0) {\n const peekDraft = id ? takePeekDraft(id) : null;\n if (peekDraft) {\n const promotedFiles = buildFilesPayload({\n entity,\n formData: peekDraft,\n record: undefined,\n isNew: true,\n });\n collectCommittedFileIds(promotedFiles).forEach((fileId) => {\n draftScope.registerDraft({ fileId, discard: () => discardFile(fileId) });\n });\n }\n const data: Record<string, unknown> = {};\n editableFields.forEach((name) => {\n const fromDraft = peekDraft ? peekDraft[name] : undefined;\n if (fromDraft !== undefined) {\n data[name] = fromDraft;\n return;\n }\n const value = getSchemaFieldValue(entity, record, name);\n data[name] = getEntityFileField(entity, name) ? (value ?? null) : (value ?? \"\");\n });\n setFormData(data);\n }\n // Seed pendingChildren / childrenSnapshot from the detail GET response's\n // `$children` (populated by handlers/enrich.go for entities that declare\n // aggregate children). EditableChildrenTable refuses to mount until\n // pendingChildren is non-undefined; without this branch, aggregate-only\n // entities (e.g. an Agent entity whose only data lives in label/counter/\n // bucket/metadata child tables) silently render their child tables as\n // read-only even in edit mode.\n if (\n entity.children !== undefined &&\n entity.children.length > 0 &&\n pendingChildren === undefined\n ) {\n const serverChildren = record[\"$children\"] as\n | Record<string, Record<string, unknown>[]>\n | undefined;\n if (serverChildren && Object.keys(serverChildren).length > 0) {\n const pending: Record<string, PendingChildRow[]> = {};\n for (const [childCode, rows] of Object.entries(serverChildren)) {\n const childEntity = schema?.entities[childCode];\n pending[childCode] = rows.map((row) => ({\n ...row,\n _key: childRowKey(childEntity, row),\n }));\n }\n setPendingChildren(pending);\n setChildrenSnapshot(serverChildren);\n } else {\n // Entity declares children but the server returned none yet. Seed\n // empty so EditableChildrenTable renders the \"add row\" affordance\n // instead of falling back to the read-only ChildrenTable.\n setPendingChildren({});\n setChildrenSnapshot({});\n }\n }\n // Always clean the param from URL\n setSearchParams((prev) => {\n const next = new URLSearchParams(prev);\n next.delete(\"edit\");\n return next;\n }, { replace: true });\n }, [openInEdit, record, editableFields, canUpdate, entity, pendingChildren, schema]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Initialize pendingChildren for new records (isNew starts in edit mode).\n useEffect(() => {\n if (!isNew || !canCreate || !entity?.children || entity.children.length === 0) return;\n if (pendingChildren !== undefined) return; // already initialized\n setPendingChildren({});\n setChildrenSnapshot({});\n }, [isNew, canCreate, entity?.children]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Edit-mode entry is owned entirely by the ?edit=true URL effect at\n // line 284 (`useEffect openInEdit`). The legacy `startEditing` helper\n // that the old in-place Edit button called duplicated that effect's\n // record-to-formData hydration; it was removed alongside the bespoke\n // Edit button when the __edit platform-default action took over. The\n // platform default's href targets \"<list_route>/<id>?edit=true\",\n // which re-renders this page with `openInEdit=true` and runs the\n // URL effect with the same hydration code.\n\n const cancelEditing = useCallback(async () => {\n await draftScope.flushAllDrafts();\n if (isNew) {\n const listRoute = screen?.route?.replace(\"/:id\", \"\") ?? \"/\";\n navigate(listRoute);\n } else {\n setEditing(false);\n setFormData({});\n setPendingStatuses({});\n setPendingProperties({});\n setPendingChildren(undefined);\n setChildrenSnapshot(undefined);\n setValidationVisible(false);\n }\n }, [draftScope, isNew, screen, navigate]);\n\n const handleFieldChange = useCallback((fieldName: string, value: unknown) => {\n setFormData((prev) => ({ ...prev, [fieldName]: value }));\n }, []);\n\n // Find the list screen route for back navigation\n const listRoute = useMemo(() => {\n if (!screen?.route) return \"/\";\n // /leads/:id -> /leads\n const parts = screen.route.split(\"/\");\n parts.pop(); // Remove :id\n return parts.join(\"/\") || \"/\";\n }, [screen]);\n\n const listRouteExists = useMemo(() => {\n if (!schema?.screens || !listRoute || listRoute === \"/\") return false;\n return Object.values(schema.screens).some((s) => s.route === listRoute);\n }, [schema, listRoute]);\n\n const userLocale = useUserLocale();\n\n // Parent-record field errors, keyed by field name. Computed from formData\n // (raw form state, not the diffed payload - a payload skips unchanged\n // fields and would miss an existing required field the user cleared). Only\n // fields the user can actually edit are flagged: a required-but-auto or\n // required PK field has no input to fix, so flagging it is a dead end.\n const parentValidationErrors = useMemo<Record<string, string>>(() => {\n if (!entity) return {};\n const out: Record<string, string> = {};\n for (const name of findMissingRequiredFields(entity, formData, isNew)) {\n if (editableFields.has(name)) out[name] = \"Required\";\n }\n for (const name of findInvalidEmailFields(entity, formData)) {\n if (editableFields.has(name)) out[name] = \"Invalid email address\";\n }\n return out;\n }, [entity, formData, isNew, editableFields]);\n const hasFieldErrors = Object.keys(parentValidationErrors).length > 0;\n\n const handleSave = useCallback(() => {\n if (!entityCode || !entity) return;\n if ((isNew && !canCreate) || (!isNew && !canUpdate)) return;\n\n // Inline validation gate. The first Save attempt reveals field-level\n // errors (red ring + inline message + ErrorSummary) instead of a\n // transient toast; a form with errors does not submit. The\n // parentValidationErrors memo recomputes live as the user fixes fields.\n setValidationVisible(true);\n if (hasFieldErrors) return;\n\n // Build payload with only changed fields (lighter payloads, less churn).\n const payload: Record<string, unknown> = {};\n const filesPayload = buildFilesPayload({ entity, formData, record, isNew });\n const committedFileIds = collectCommittedFileIds(filesPayload);\n if (isNew) {\n // Only include non-empty fields to preserve schema defaults for unset fields.\n for (const [key, val] of Object.entries(formData)) {\n if (entity.files?.has(key)) continue;\n const serialized = serializeSchemaFieldValue(entity, key, val);\n if (serialized !== \"\" && serialized !== null && serialized !== undefined) {\n payload[key] = serialized;\n }\n }\n } else {\n // Only send fields that actually changed.\n for (const [key, val] of Object.entries(formData)) {\n if (entity.files?.has(key)) continue;\n const original = record ? getSchemaFieldValue(entity, record, key) : undefined;\n const serialized = serializeSchemaFieldValue(entity, key, val);\n const serializedOriginal = serializeSchemaFieldValue(entity, key, original);\n // Treat null/undefined/empty-string as equivalent to avoid spurious writes.\n const normalizedVal = serialized === \"\" ? null : serialized;\n const normalizedOrig = serializedOriginal === \"\" || serializedOriginal === undefined ? null : serializedOriginal;\n if (normalizedVal !== normalizedOrig) {\n payload[key] = serialized;\n }\n }\n if (rowVersion) {\n payload.$row_version = rowVersion;\n }\n }\n if (Object.keys(filesPayload).length > 0) {\n payload.$files = filesPayload;\n }\n // Include $statuses only if any status was changed.\n if (Object.keys(pendingStatuses).length > 0) {\n payload[\"$statuses\"] = Object.entries(pendingStatuses).map(\n ([groupCode, statusCode]) => ({ status_group_code: groupCode, status_code: statusCode }),\n );\n }\n // Include $properties only if any property was changed.\n if (Object.keys(pendingProperties).length > 0) {\n payload[\"$properties\"] = Object.entries(pendingProperties).map(\n ([propertyCode, value]) => ({ property_code: propertyCode, value }),\n );\n }\n // Include $children only for child codes with actual changes.\n if (pendingChildren) {\n const childrenPayload: Record<string, Record<string, unknown>[]> = {};\n for (const [childCode, rows] of Object.entries(pendingChildren)) {\n // Filter out deleted rows, strip meta fields.\n const cleaned = rows\n .filter((r) => !r._deleted)\n .map((r) => {\n const { _key, _deleted, _dirty, ...fields } = r;\n return fields;\n });\n // Compare against snapshot to detect actual changes.\n const original = childrenSnapshot?.[childCode] ?? [];\n if (isNew || !arraysEqual(cleaned, original)) {\n childrenPayload[childCode] = cleaned;\n }\n }\n if (Object.keys(childrenPayload).length > 0) {\n payload[\"$children\"] = childrenPayload;\n }\n }\n\n // Skip save if nothing changed.\n if (!isNew && Object.keys(payload).length === 0) {\n setEditing(false);\n setPendingStatuses({});\n setPendingProperties({});\n setPendingChildren(undefined);\n setChildrenSnapshot(undefined);\n setValidationVisible(false);\n return;\n }\n\n const clearPending = () => {\n setEditing(false);\n setStaleRecord(false);\n setValidationVisible(false);\n setPendingStatuses({});\n setPendingProperties({});\n setPendingChildren(undefined);\n setChildrenSnapshot(undefined);\n // No need to invalidate - useUpdateEntity.onSuccess uses the PATCH response\n // to update item, status, and property caches directly.\n };\n\n if (isNew) {\n createMutation.mutate(payload, {\n onSuccess: (response) => {\n committedFileIds.forEach((fileId) => draftScope.markCommitted(fileId));\n const newRecord = response.data as Record<string, unknown>;\n const newPkValues = entity ? extractPkValues(entity, newRecord) : {};\n const newId = pkValuesToId(newPkValues);\n if (newId) {\n navigate(`${listRoute}/${encodeURIComponent(newId)}`, { replace: true });\n } else {\n navigate(listRoute, { replace: true });\n }\n clearPending();\n },\n });\n } else if (pkValues) {\n updateMutation.mutate(\n { pkValues, data: payload },\n {\n onSuccess: () => {\n committedFileIds.forEach((fileId) => draftScope.markCommitted(fileId));\n clearPending();\n },\n onError: (err) => {\n if (err instanceof ApiError && err.code === \"STALE_OBJECT\") {\n setStaleRecord(true);\n }\n },\n },\n );\n }\n }, [draftScope, isNew, canCreate, canUpdate, entityCode, formData, hasFieldErrors, pendingStatuses, pendingProperties, pendingChildren, childrenSnapshot, createMutation, updateMutation, entity, navigate, listRoute, record, rowVersion, pkValues]);\n\n const handleChildrenChange = useCallback((childCode: string, rows: PendingChildRow[]) => {\n setPendingChildren((prev) => ({ ...(prev ?? {}), [childCode]: rows }));\n }, []);\n\n const handleDelete = useCallback(() => {\n if (!pkValues || isNew || !canDelete) return;\n deleteMutation.mutate(pkValues, {\n onSuccess: () => navigate(listRoute, { replace: true }),\n });\n }, [pkValues, isNew, canDelete, deleteMutation, navigate, listRoute]);\n\n const handleRestore = useCallback(() => {\n if (!pkValues || isNew || !canRestore) return;\n restoreMutation.mutate(pkValues);\n }, [pkValues, isNew, canRestore, restoreMutation]);\n\n // Compute validation errors for direct children (required field checks).\n const childValidationErrors = useMemo(\n () => validatePendingChildren(pendingChildren, schema),\n [pendingChildren, schema],\n );\n const hasChildErrors = Object.keys(childValidationErrors).length > 0;\n\n if (!screen || !entity) return null;\n if (!isNew && isLoading) return <DetailSkeleton />;\n if (!isNew && !record) {\n return (\n <div style={{ padding: 24 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, marginBottom: 8 }}>\n {listRouteExists && (\n <IconBtn\n icon={<Icon.back size={14} />}\n onClick={() => navigate(listRoute)}\n title=\"Back\"\n />\n )}\n <h1 style={{ margin: 0, fontSize: 18, fontWeight: 600, color: \"var(--text)\" }}>Not Found</h1>\n </div>\n <p style={{ color: \"var(--text-3)\" }}>Record not found.</p>\n </div>\n );\n }\n\n const entityDisplay = entity.display;\n // Screen display name overrides entity name (e.g. custom detail screens).\n const detailName = loc(screen.display?.name) || loc(entityDisplay.name);\n // getDisplayValue covers $display, display_field, and localized-JSONB\n // unwrap. Returns undefined when neither is set; render-time templates\n // handle that with the entity-name fallback.\n const displayValue = getDisplayValue(record as Record<string, unknown> | null | undefined, entity);\n const title = isNew\n ? `New ${detailName}`\n : displayValue ? String(displayValue) : detailName;\n\n const isSaving = createMutation.isPending || updateMutation.isPending;\n\n // When screen.actions is defined, use only those; otherwise fall back to visibility.\n const layoutCtx: LayoutContext = {\n schema: schema!,\n entity,\n entityCode: entityCode!,\n recordId: pkCacheKey ?? id ?? \"\",\n record: effectiveEditing ? { ...(record ?? {}), ...formData } : (record ?? {}),\n isNew,\n mode: effectiveEditing ? \"edit\" : \"display\",\n onFieldChange: effectiveEditing ? handleFieldChange : undefined,\n refs: recordRefs,\n objectProperties: (() => {\n const base = objectProperties ?? [];\n if (!effectiveEditing || Object.keys(pendingProperties).length === 0) return base.length > 0 ? base : undefined;\n const merged = base.map((p) =>\n pendingProperties[p.property_code] !== undefined\n ? { ...p, value: pendingProperties[p.property_code] }\n : p,\n );\n for (const [code, value] of Object.entries(pendingProperties)) {\n if (!base.some((p) => p.property_code === code)) {\n merged.push({ property_code: code, name: {}, type: \"string\", value });\n }\n }\n return merged;\n })(),\n onSetProperty: (isNew || !canUpdate)\n ? undefined\n : effectiveEditing\n ? (propertyCode, value) => setPendingProperties((prev) => ({ ...prev, [propertyCode]: value }))\n : undefined,\n isPropertyPending: false,\n // In edit mode, merge pending status changes into displayed statuses and\n // store changes locally (saved atomically on Save, discarded on Cancel).\n objectStatuses: (() => {\n const base = statuses ?? [];\n if (!effectiveEditing || Object.keys(pendingStatuses).length === 0) return base.length > 0 ? base : undefined;\n // Merge: replace matching groups, keep others.\n const merged = base.map((s) =>\n pendingStatuses[s.group_code] !== undefined\n ? { ...s, status_code: pendingStatuses[s.group_code] }\n : s,\n );\n // Add groups that aren't in base yet (new status set for first time).\n for (const [gc, sc] of Object.entries(pendingStatuses)) {\n if (!base.some((s) => s.group_code === gc)) {\n merged.push({ group_code: gc, status_code: sc, status_name: {}, set_at: formatWireDateTime() });\n }\n }\n return merged;\n })(),\n onSetStatus: (isNew || !canUpdate)\n ? undefined\n : effectiveEditing\n ? (groupCode, statusCode) => setPendingStatuses((prev) => ({ ...prev, [groupCode]: statusCode }))\n : undefined,\n isStatusPending: false,\n // Children editing context for direct (aggregate) children.\n pendingChildren: effectiveEditing ? pendingChildren : undefined,\n onChildrenChange: effectiveEditing ? handleChildrenChange : undefined,\n childValidationErrors: effectiveEditing ? childValidationErrors : undefined,\n // Parent-record field errors, surfaced inline only once a Save has been\n // attempted (validationVisible).\n fieldErrors: effectiveEditing && validationVisible ? parentValidationErrors : undefined,\n };\n\n const hasSidebar = screen.sidebar && screen.sidebar.length > 0;\n\n // Highlights strip - the screen's declared `highlights:` codes. Display\n // mode, existing record only.\n const highlightsEl =\n !isNew && record ? (\n <DetailHighlights\n screen={screen}\n entity={entity}\n entityCode={entityCode!}\n record={record}\n refs={recordRefs}\n objectStatuses={statuses}\n schema={schema!}\n locale={userLocale}\n />\n ) : undefined;\n\n const headerEl = (\n <DetailHeader\n title={title}\n recordId={id}\n editing={effectiveEditing}\n isNew={isNew}\n isSingletonByCaller={!!isSingletonByCaller}\n isRecordDeleted={isRecordDeleted}\n dirty={editing}\n isSaving={isSaving}\n hasChildErrors={hasChildErrors}\n hasFieldErrors={validationVisible && hasFieldErrors}\n canRestore={canRestore}\n entity={entity}\n screen={screen}\n globalActions={schema?.actions}\n entityCode={entityCode}\n record={record}\n id={id}\n listRoute={listRoute}\n listRouteExists={!!listRouteExists}\n highlights={highlightsEl}\n onBack={() => navigate(listRouteExists ? listRoute : \"/\")}\n onCancelEdit={cancelEditing}\n onSave={handleSave}\n onRestore={handleRestore}\n isRestoring={restoreMutation.isPending}\n />\n );\n\n const deletedBanner = isRecordDeleted && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"10px 14px\",\n marginBottom: 12,\n borderRadius: 8,\n border: \"1px solid var(--danger)\",\n background: \"var(--danger-soft)\",\n color: \"var(--danger)\",\n fontSize: 12.5,\n }}\n >\n <Icon.warning size={14} />\n <span>\n This record was deleted on {formatOffsetTimestamp(String(record![entity.soft_delete!]))}\n </span>\n </div>\n );\n\n const staleBanner = staleRecord && (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"10px 14px\",\n marginBottom: 12,\n borderRadius: 8,\n border: \"1px solid var(--warn)\",\n background: \"var(--warn-soft)\",\n color: \"var(--warn)\",\n fontSize: 12.5,\n }}\n >\n <Icon.warning size={14} />\n <span>This record changed on the server while you were viewing or editing it.</span>\n </div>\n );\n\n const errorSummaryEl = effectiveEditing && validationVisible && hasFieldErrors && (\n <ErrorSummary\n errors={Object.entries(parentValidationErrors).map(([name, message]): FieldErrorEntry => {\n const field = entity.fields.get(name);\n return {\n name,\n label: field ? fieldDisplayName(field, name, schema?.entities) : name,\n message,\n };\n })}\n onJump={(name) => {\n const el = document.getElementById(`${entityCode}-${name}`);\n if (el) {\n el.scrollIntoView({ block: \"center\", behavior: \"smooth\" });\n (el as HTMLElement).focus?.();\n }\n }}\n />\n );\n\n const statusesEl = hasStatusGroups && !isNew && !(screen.items && screen.items.length > 0) && (\n <div className=\"mb-4\">\n <StatusGroupsDisplay\n statusGroups={entity.status_groups ?? {}}\n objectStatuses={layoutCtx.objectStatuses}\n onSetStatus={!canUpdate ? noop : effectiveEditing\n ? (groupCode, statusCode) => setPendingStatuses((prev) => ({ ...prev, [groupCode]: statusCode }))\n : noop\n }\n isPending={false}\n mode={effectiveEditing ? \"edit\" : \"display\"}\n />\n </div>\n );\n\n const mainContent = (\n <>\n {screen.items && screen.items.length > 0 && (record || isNew) && schema && (\n <LayoutRenderer nodes={screen.items} ctx={layoutCtx} />\n )}\n {!isNew && id && entityCode && entity.associations && Object.keys(entity.associations).length > 0 && schema && (\n <AssociationTabs entityCode={entityCode} recordId={pkCacheKey ?? id ?? \"\"} associations={entity.associations} schema={schema} />\n )}\n {!isNew && record && schema && (\n <DetailAuditStrip\n screen={screen}\n entity={entity}\n entityCode={entityCode!}\n record={record}\n refs={recordRefs}\n schema={schema}\n locale={userLocale}\n />\n )}\n </>\n );\n\n const deleteDialog = (\n <ConfirmDialog\n open={deleteConfirm.show}\n onOpenChange={(open) => !open && setDeleteConfirm({ show: false })}\n title={`Delete ${detailName}`}\n description={\n deleteConfirm.confirmation\n ? deleteConfirm.confirmation\n : hasSoftDelete\n ? `Are you sure you want to delete this ${detailName.toLowerCase()}? It can be restored later by an admin.`\n : `Are you sure you want to delete this ${detailName.toLowerCase()}? This action cannot be undone.`\n }\n confirmLabel=\"Delete\"\n onConfirm={handleDelete}\n destructive\n />\n );\n\n // Sidebar layout: two fully independent panes separated by resize handle.\n // Left pane: header + statuses + scrollable sections (entity details)\n // Right pane: sidebar content (e.g. activities) - completely separate, own header\n if (hasSidebar) {\n return (\n <div style={{ height: \"100%\", display: \"flex\", overflow: \"hidden\", background: \"var(--bg)\" }}>\n {/* Left pane - entity details */}\n <div style={{ flex: 1, minWidth: 0, display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n {/* Sticky header (title + Edit/Delete) */}\n <div style={{ flexShrink: 0, padding: \"14px 20px 8px\" }}>\n {headerEl}\n {staleBanner}\n {deletedBanner}\n {errorSummaryEl}\n {statusesEl}\n </div>\n {/* Scrollable content */}\n <div style={{ flex: 1, minHeight: 0, overflowY: \"auto\", padding: \"4px 20px 24px\", display: \"flex\", flexDirection: \"column\", gap: 16 }}>\n {mainContent}\n </div>\n </div>\n {/* Resize handle - a wide hit zone with a thin divider line that\n picks up the accent on hover. */}\n <div\n className=\"detail-resize-handle\"\n onMouseDown={onSidebarDragStart}\n title=\"Drag to resize\"\n >\n <div\n style={{\n position: \"absolute\",\n top: 0,\n bottom: 0,\n left: \"50%\",\n transform: \"translateX(-50%)\",\n width: 1,\n background: \"var(--border)\",\n }}\n />\n </div>\n {/* Right pane - sidebar (activities etc.) */}\n <div ref={sidebarRef} style={{ flexShrink: 0, height: \"100%\", width: `${sidebarW}px` }}>\n {(record || isNew) && schema && (\n <LayoutRenderer nodes={screen.sidebar!} ctx={layoutCtx} />\n )}\n </div>\n {deleteDialog}\n </div>\n );\n }\n\n // Standard layout (no sidebar): sticky header + scrollable content\n return (\n <div style={{ height: \"100%\", display: \"flex\", flexDirection: \"column\", overflow: \"hidden\", background: \"var(--bg)\" }}>\n <div className=\"shrink-0 px-6 pt-6 pb-2\">\n {headerEl}\n {staleBanner}\n {deletedBanner}\n {errorSummaryEl}\n {statusesEl}\n </div>\n <div className=\"flex-1 min-h-0 overflow-y-auto px-6 pb-6 space-y-4\">\n {mainContent}\n </div>\n {deleteDialog}\n </div>\n );\n}\n\n\ninterface DetailHeaderProps {\n title: string;\n recordId?: string;\n editing: boolean;\n isNew: boolean;\n isSingletonByCaller: boolean;\n isRecordDeleted: boolean;\n dirty: boolean;\n isSaving: boolean;\n hasChildErrors: boolean;\n // Field-level validation errors block Save once a save has been attempted.\n hasFieldErrors: boolean;\n canRestore: boolean;\n entity?: import(\"@/types/schema\").Entity;\n /** Active screen so per-screen action_layout overrides take precedence over entity's. */\n screen?: import(\"@/types/schema\").Screen;\n globalActions?: Record<string, import(\"@/types/schema\").Action>;\n entityCode?: string;\n record: Record<string, unknown> | undefined;\n id: string | undefined;\n /** List-screen route for the entity, used by runHeaderAction to redirect\n * after a successful __delete. */\n listRoute: string;\n listRouteExists: boolean;\n /** Pre-rendered highlights strip element. */\n highlights?: React.ReactNode;\n onBack: () => void;\n onCancelEdit: () => void;\n onSave: () => void;\n onRestore: () => void;\n isRestoring: boolean;\n}\n\nfunction DetailHeader({\n title,\n recordId,\n editing,\n isNew,\n isSingletonByCaller,\n isRecordDeleted,\n dirty,\n isSaving,\n hasChildErrors,\n hasFieldErrors,\n canRestore,\n entity,\n screen,\n globalActions,\n entityCode,\n record,\n id,\n listRoute,\n listRouteExists,\n highlights,\n onBack,\n onCancelEdit,\n onSave,\n onRestore,\n isRestoring,\n}: DetailHeaderProps) {\n const { run } = useActionRunner();\n const tabNavigate = useTabNavigate();\n // Embed-aware: the post-`__delete` transition back to the list route\n // preserves embed params in `self` mode and emits a `navigation-requested` in\n // `delegated` mode. Outside embed mode it is plain `useNavigate`.\n const navigate = useEmbedNavigate();\n const locale = useUserLocale();\n\n // Layout resolution order (plan §3.9): screen-level override wins over entity default.\n // Server-emitted action_layout is RBAC-pruned (Task 7); client filters by slot\n // (server emits the full tree, not per-slot) and applies row-state (visible_if /\n // enabled_if) via applyRowState. Default-flat fallback when neither tree declared.\n // Cast: server emits ResolvedActionLayoutNode[] per §8 wire format.\n const serverScreenLayout = (screen?.action_layout ?? null) as ResolvedActionLayoutNode[] | null;\n const serverEntityLayout = (entity?.action_layout ?? null) as ResolvedActionLayoutNode[] | null;\n const sourceLayout = serverScreenLayout ?? serverEntityLayout;\n const layoutForDetail = sourceLayout\n ? filterLayoutForSlot(sourceLayout, entity?.actions, globalActions, \"detail\")\n : defaultFlatForSlot(entity?.actions, \"detail\");\n const resolvedLayout = applyRowState(\n layoutForDetail,\n entity?.actions,\n globalActions,\n record ?? {},\n [],\n );\n\n // Dispatch surface for ResponsiveActionBar (plan §8 Task 10). selectedRows /\n // selectedIds collapse to the single detail record so href-templated\n // ${id} resolves correctly for any header action that uses href mode.\n // Reason: detail pages have no bulk selection; widgets reading this\n // surface still see a consistent shape.\n const widgetContext = useMemo<WidgetContext | undefined>(() => {\n if (!entity) return undefined;\n const rowIds = id ? [id] : [];\n return {\n entity,\n entityCode: entityCode ?? \"\",\n screen,\n screenRoute: screen?.route ?? \"\",\n selectedRows: record ? [record] : [],\n selectedIds: rowIds,\n locale,\n tabNavigate: (url, meta) => tabNavigate(url, { title: meta?.title, icon: meta?.icon, search: meta?.search }),\n runActionApi: (code, params) => runActionApi(code, params as Parameters<typeof runActionApi>[1]),\n };\n }, [entity, entityCode, screen, id, record, locale, tabNavigate]);\n\n const runHeaderAction = (code: string, action: import(\"@/types/schema\").Action) => {\n const fullCode = dispatchAction(code, action, { row: record ?? undefined });\n if (fullCode === null) return;\n const ids = id ? [id] : [];\n // Detail-page semantic: when the platform-default __delete succeeds\n // against the record this page is viewing, the record is gone and\n // sitting on its detail URL is dead-end UX. Navigate back to the\n // entity's list screen so the user lands on a meaningful page with\n // the success toast still visible. Other actions stay on the detail\n // page because they MUTATE the record (no navigation needed) or\n // are read-only / no-op against the routing context.\n const navigateAfter = code === \"__delete\" && listRouteExists ? listRoute : null;\n // Return the run promise so ResponsiveActionBar can await it and keep the\n // trigger button disabled with a spinner for the whole operation.\n return run({\n action,\n title: loc(action.display?.name) || code,\n execute: (params) => runActionApi(fullCode, buildActionBody(params, ids)),\n onSuccess: navigateAfter\n ? () => navigate(navigateAfter, { replace: true })\n : undefined,\n });\n };\n\n // The header's right slot: restore (deleted record) / cancel + save\n // (editing) / the resolved action bar (display). All action dispatch stays\n // in this component; RecordHeader below is purely presentational.\n const rightSlot = isRecordDeleted ? (\n canRestore ? (\n <Btn\n kind=\"primary\"\n icon={isRestoring ? <Spinner size={13} /> : <Icon.refresh size={13} />}\n onClick={onRestore}\n disabled={isRestoring}\n >\n {isRestoring ? \"Restoring…\" : \"Restore\"}\n </Btn>\n ) : null\n ) : editing ? (\n <>\n <Btn icon={<Icon.x size={13} />} onClick={onCancelEdit} disabled={isSaving}>\n Cancel\n </Btn>\n <Btn\n kind=\"primary\"\n icon={isSaving ? <Spinner size={13} /> : <Icon.save size={13} />}\n onClick={onSave}\n disabled={isSaving || hasChildErrors || hasFieldErrors}\n title={\n hasChildErrors\n ? \"Fix errors in child rows\"\n : hasFieldErrors\n ? \"Fix the highlighted fields\"\n : undefined\n }\n >\n {isSaving ? \"Saving…\" : \"Save\"}\n </Btn>\n </>\n ) : !isNew && id && resolvedLayout.length > 0 ? (\n <ResponsiveActionBar\n layout={resolvedLayout}\n row={record}\n onRun={runHeaderAction}\n kind=\"detail\"\n entityActions={entity?.actions}\n globalActions={globalActions}\n reservedRightWidth={0}\n widgetContext={widgetContext}\n />\n ) : null;\n\n return (\n <RecordHeader\n icon={entity?.display.icon ? <DynamicIcon name={entity.display.icon} size={16} /> : undefined}\n title={title}\n recordId={recordId}\n highlights={highlights}\n mode={isNew ? \"create\" : editing ? \"edit\" : \"display\"}\n dirty={dirty}\n showBack={!isSingletonByCaller && listRouteExists}\n onBack={onBack}\n right={rightSlot}\n />\n );\n}\n","// NOTE: there is no separate \"get statuses\" API call. $statuses is enriched\n// inline on GET /api/data/{entity}?<pk>=<value> by handlers/data.go:getByPKValues.\n// Callers read record.$statuses directly from the useEntityItem query result.\n// See TestGetByPKEnrichment in the Go integration tests for the contract.\n//\n// Writes route through the platform-default __update action endpoint\n// (POST /api/actions/<entity>.__update). Per plan 2026-06-01-write-api-final\n// §1.2, the wire envelope is FLAT: `object_ids` is the only reserved key, and\n// `entity` + `fields` sit at the top level (decoded into DataUpdateParams via\n// JSON tags). The $statuses subresource lives inside `fields` as an array of\n// {status_group_code, status_code, note?} entries; subresource.ExtractSubResources\n// routes it to declarion.object_statuses at write time.\nimport { executeAction } from \"./data\";\nimport type { StatusInfo } from \"@/types/api\";\n\nexport async function setObjectStatus(\n entity: string,\n id: string,\n groupCode: string,\n statusCode: string,\n note?: string,\n): Promise<StatusInfo> {\n const statusEntry: Record<string, unknown> = {\n status_group_code: groupCode,\n status_code: statusCode,\n };\n if (note != null) statusEntry.note = note;\n\n const response = await executeAction(`${entity}.__update`, {\n object_ids: [id],\n entity,\n fields: { $statuses: [statusEntry] },\n });\n const result = response.result as { rows?: Record<string, unknown>[] } | undefined;\n const row = result?.rows?.[0];\n const statuses = (row?.[\"$statuses\"] ?? []) as StatusInfo[];\n return statuses.find((s) => s.group_code === groupCode) ?? statuses[0];\n}\n","// NOTE: there is no useObjectStatuses hook. $statuses is enriched inline\n// on the row returned by useEntityItem - read `record.$statuses` directly.\n// See api/status.ts and TestGetByPKEnrichment for the contract.\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\nimport * as statusApi from \"@/api/status\";\n\nexport function useSetStatus(entity: string | undefined, id: string | undefined) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ groupCode, statusCode, note }: { groupCode: string; statusCode: string; note?: string }) =>\n statusApi.setObjectStatus(entity!, id!, groupCode, statusCode, note),\n onSuccess: () => {\n // $statuses rides the entity-item query response - invalidate it\n // (not a separate \"object-status\" cache key, which no longer exists).\n queryClient.invalidateQueries({\n queryKey: [\"entity-item\", entity],\n });\n },\n onError: (err: Error) => {\n toast.error(err.message);\n },\n });\n}\n","// NOTE: there is no separate \"get properties\" API call. $properties is enriched\n// inline on GET /api/data/{entity}?<pk>=<value> by handlers/data.go:getByPKValues.\n// Callers read record.$properties directly from the useEntityItem query result.\n// See TestGetByPKEnrichment in the Go integration tests for the contract.\n//\n// Writes route through the platform-default __update action endpoint\n// (POST /api/actions/<entity>.__update). Per plan 2026-06-01-write-api-final\n// §1.2, the wire envelope is FLAT: `object_ids` is the only reserved key, and\n// `entity` + `fields` sit at the top level (decoded into DataUpdateParams via\n// JSON tags). The $properties subresource lives inside `fields` as an array of\n// {property_code, value} entries; subresource.ExtractSubResources routes it to\n// declarion.object_properties at write time.\nimport { executeAction } from \"./data\";\nimport type { PropertyValue } from \"@/types/api\";\n\nexport async function setObjectProperty(\n entity: string,\n id: string,\n propertyCode: string,\n value: unknown,\n): Promise<PropertyValue> {\n const response = await executeAction(`${entity}.__update`, {\n object_ids: [id],\n entity,\n fields: { $properties: [{ property_code: propertyCode, value }] },\n });\n const result = response.result as { rows?: Record<string, unknown>[] } | undefined;\n const row = result?.rows?.[0];\n const props = (row?.[\"$properties\"] ?? []) as PropertyValue[];\n return props.find((p) => p.property_code === propertyCode) ?? props[0];\n}\n\nexport async function deleteObjectProperty(\n entity: string,\n id: string,\n propertyCode: string,\n): Promise<void> {\n await executeAction(`${entity}.__update`, {\n object_ids: [id],\n entity,\n fields: { $properties: [{ property_code: propertyCode, value: null }] },\n });\n}\n","// NOTE: there is no useObjectProperties hook. $properties is enriched inline\n// on the row returned by useEntityItem - read `record.$properties` directly.\n// See api/properties.ts and TestGetByPKEnrichment for the contract.\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\nimport * as propsApi from \"@/api/properties\";\n\nexport function useSetProperty(entity: string | undefined, id: string | undefined) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ propertyCode, value }: { propertyCode: string; value: unknown }) =>\n propsApi.setObjectProperty(entity!, id!, propertyCode, value),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: [\"entity-item\", entity],\n });\n },\n onError: (err: Error) => {\n toast.error(err.message);\n },\n });\n}\n\nexport function useDeleteProperty(entity: string | undefined, id: string | undefined) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (propertyCode: string) =>\n propsApi.deleteObjectProperty(entity!, id!, propertyCode),\n onSuccess: () => {\n queryClient.invalidateQueries({\n queryKey: [\"entity-item\", entity],\n });\n },\n onError: (err: Error) => {\n toast.error(err.message);\n },\n });\n}\n","import { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport { Btn, Icon, Select, Spinner, TextInput } from \"@/components/primitives\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { useEntityData } from \"@/hooks/useEntityData\";\nimport { useEntityItem } from \"@/hooks/useEntityItem\";\nimport { useUpdateEntity } from \"@/hooks/useMutations\";\nimport { useSetStatus } from \"@/hooks/useObjectStatus\";\nimport { useSetProperty } from \"@/hooks/useProperties\";\nimport type { StatusInfo, PropertyValue } from \"@/types/api\";\nimport { useDebounce } from \"@/hooks/useDebounce\";\nimport { useUserLocale } from \"@/hooks/useUserLocale\";\nimport {\n FileDraftScope,\n useFileDraftScope,\n buildFilesPayload,\n collectCommittedFileIds,\n getEntityFileField,\n getSchemaFieldValue,\n renderSchemaField,\n serializeSchemaFieldValue,\n} from \"@/components/file-widgets\";\nimport {\n type FilterNode,\n eqFilter,\n removeFilter,\n setFiltersInURL,\n parseFiltersFromURL,\n} from \"@/lib/filters\";\nimport { useTabStore } from \"@/stores/tabs\";\nimport { DetailSkeleton } from \"@/components/shared/LoadingSkeleton\";\nimport { LayoutRenderer, type LayoutContext } from \"@/components/detail-layout\";\nimport { toast } from \"@/lib/toast\";\nimport { loc, fieldDisplayName, getDisplayValue, entityAllowsMutation } from \"@/types/schema\";\nimport type { Entity, Schema, LayoutNode } from \"@/types/schema\";\n\ninterface SmartRecordListPageProps {\n screenCode: string;\n}\n\ntype RowData = Record<string, unknown>;\n\nexport function SmartRecordListPage({ screenCode }: SmartRecordListPageProps) {\n const [searchParams, setSearchParams] = useSearchParams();\n const { data: schema } = useSchema();\n\n const screen = schema?.screens[screenCode];\n const entity = screen?.entity ? schema?.entities[screen.entity] : undefined;\n const entityCode = screen?.entity;\n\n const { openTab } = useTabStore();\n useEffect(() => {\n if (screen?.route && entity) {\n const tabName = loc(screen.display?.name) || loc(entity.display.name_plural);\n const tabIcon = screen.display?.icon || entity.display.icon;\n openTab({\n pathname: screen.route,\n search: searchParams.toString(),\n title: tabName,\n icon: tabIcon,\n closable: true,\n });\n }\n }, [screen?.route, screen?.display?.name?.en, entity?.display.name_plural?.en]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const selectedId = searchParams.get(\"selected\") ?? null;\n const searchValue = searchParams.get(\"search\") ?? \"\";\n const debouncedSearch = useDebounce(searchValue, 300);\n const sortParam = searchParams.get(\"sort\") ?? screen?.sort_default ?? \"\";\n\n const filterNodes = useMemo(() => {\n const raw = searchParams.get(\"filters\");\n if (!raw) return undefined;\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) && parsed.length > 0 ? parsed : undefined;\n } catch {\n return undefined;\n }\n }, [searchParams]);\n\n const { data: listResponse, isLoading: listLoading } = useEntityData(entityCode, {\n page: 1,\n per_page: 200,\n sort: sortParam || undefined,\n search: debouncedSearch || undefined,\n filters: filterNodes,\n });\n\n const records = listResponse?.data ?? [];\n const listRefs = listResponse?.$refs;\n\n const pkField = useMemo(\n () => entity?.fields.names().find((f) => entity.fields.get(f)?.primary) ?? \"id\",\n [entity],\n );\n\n const selectRecord = useCallback(\n (id: string | null) => {\n setSearchParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n if (id) next.set(\"selected\", id);\n else next.delete(\"selected\");\n return next;\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n const updateSearch = useCallback(\n (value: string) => {\n setSearchParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n if (value) next.set(\"search\", value);\n else next.delete(\"search\");\n return next;\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n const updateFilter = useCallback(\n (field: string, value: string) => {\n setSearchParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n const current = parseFiltersFromURL(prev);\n let updated: FilterNode[];\n if (value && value !== \"__all__\") {\n updated = [...removeFilter(current, field), eqFilter(field, value)];\n } else {\n updated = removeFilter(current, field);\n }\n setFiltersInURL(updated, next);\n return next;\n },\n { replace: true },\n );\n },\n [setSearchParams],\n );\n\n const listResizable = screen?.list_resizable ?? false;\n const containerRef = useRef<HTMLDivElement>(null);\n const [panelWidth, setPanelWidth] = useState<number | null>(null);\n const isDragging = useRef(false);\n\n const listWidthRaw = screen?.list_width;\n useEffect(() => {\n if (!listResizable || panelWidth !== null) return;\n const container = containerRef.current;\n if (!container) return;\n if (listWidthRaw && listWidthRaw.endsWith(\"%\")) {\n const pct = parseFloat(listWidthRaw) / 100;\n setPanelWidth(Math.round(container.getBoundingClientRect().width * pct));\n } else if (listWidthRaw) {\n setPanelWidth(parseInt(listWidthRaw, 10) || 320);\n } else {\n setPanelWidth(320);\n }\n }, [listResizable, listWidthRaw, panelWidth, entity]);\n\n const dragCleanup = useRef<(() => void) | null>(null);\n useEffect(() => {\n return () => {\n dragCleanup.current?.();\n };\n }, []);\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n isDragging.current = true;\n const startX = e.clientX;\n const startWidth = panelWidth ?? 320;\n\n const onMouseMove = (ev: MouseEvent) => {\n if (!isDragging.current) return;\n const containerWidth = containerRef.current?.getBoundingClientRect().width ?? window.innerWidth;\n const minWidth = 200;\n const maxWidth = containerWidth - 300;\n const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth + ev.clientX - startX));\n setPanelWidth(newWidth);\n };\n\n const cleanup = () => {\n isDragging.current = false;\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"mouseup\", cleanup);\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n dragCleanup.current = null;\n };\n\n document.body.style.cursor = \"col-resize\";\n document.body.style.userSelect = \"none\";\n document.addEventListener(\"mousemove\", onMouseMove);\n document.addEventListener(\"mouseup\", cleanup);\n dragCleanup.current = cleanup;\n },\n [panelWidth],\n );\n\n if (!schema || !screen || !entity) return null;\n\n const listColumns =\n screen.columns ??\n entity.fields.names()\n .filter(\n (f) =>\n !entity.fields.get(f)?.primary &&\n !entity.fields.get(f)?.auto &&\n entity.fields.get(f)?.display?.is_listable !== false,\n )\n .slice(0, 4);\n\n const activeFilterCount = filterNodes?.length ?? 0;\n\n return (\n <div ref={containerRef} style={{ display: \"flex\", height: \"100%\", overflow: \"hidden\" }}>\n <div\n style={{\n flex: \"none\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRight: listResizable ? \"none\" : \"1px solid var(--divider)\",\n width: listResizable && panelWidth !== null ? panelWidth : screen.list_width ?? \"320px\",\n }}\n >\n <div\n style={{\n padding: 12,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 8,\n borderBottom: \"1px solid var(--divider)\",\n }}\n >\n <TextInput\n value={searchValue}\n onChange={updateSearch}\n placeholder={`Search ${loc(entity.display.name_plural)}…`}\n prefix={<Icon.search size={13} />}\n />\n {screen.filters && screen.filters.length > 0 && (\n <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 6 }}>\n {screen.filters.map((filterField) => {\n const fieldDef = entity.fields.get(filterField);\n if (!fieldDef?.values) return null;\n const activeNode = filterNodes?.find((n) => n.field === filterField && n.op === \"eq\");\n const currentValue = (activeNode?.value as string) ?? \"__all__\";\n const label = fieldDisplayName(fieldDef, filterField, schema.entities);\n const options = [\n { value: \"__all__\", label: `All ${label}` },\n ...fieldDef.values.map((v) => ({ value: v.code, label: loc(v.name) || v.code })),\n ];\n return (\n <div key={filterField} style={{ minWidth: 120 }}>\n <Select\n value={currentValue}\n options={options}\n onChange={(v) => updateFilter(filterField, v)}\n placeholder={label}\n />\n </div>\n );\n })}\n </div>\n )}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n fontSize: 11,\n color: \"var(--text-3)\",\n }}\n >\n <span>\n {listLoading ? \"Loading…\" : `${records.length} ${loc(entity.display.name_plural)}`}\n </span>\n {activeFilterCount > 0 && (\n <button\n onClick={() =>\n setSearchParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n next.delete(\"filters\");\n return next;\n },\n { replace: true },\n )\n }\n style={{\n background: \"transparent\",\n border: 0,\n color: \"var(--accent)\",\n cursor: \"pointer\",\n fontSize: 11,\n textDecoration: \"underline\",\n padding: 0,\n }}\n >\n Clear filters\n </button>\n )}\n </div>\n </div>\n\n <div style={{ flex: 1, overflowY: \"auto\" }}>\n {listLoading ? (\n <div style={{ padding: 12, display: \"flex\", flexDirection: \"column\", gap: 6 }}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div\n key={i}\n style={{ height: 48, borderRadius: 6, background: \"var(--surface-3)\" }}\n />\n ))}\n </div>\n ) : records.length === 0 ? (\n <div style={{ padding: 28, textAlign: \"center\", fontSize: 12.5, color: \"var(--text-3)\" }}>\n No records found\n </div>\n ) : (\n records.map((record) => (\n <RecordListRow\n key={String(record[pkField] ?? \"\")}\n record={record}\n entity={entity}\n columns={listColumns}\n pkField={pkField}\n refs={listRefs}\n isSelected={record[pkField] === selectedId}\n onClick={() => selectRecord(String(record[pkField] ?? \"\"))}\n />\n ))\n )}\n </div>\n </div>\n\n {listResizable && (\n <div\n onMouseDown={handleMouseDown}\n style={{\n flex: \"none\",\n width: 1,\n cursor: \"col-resize\",\n background: \"var(--divider)\",\n transition: \"background-color .12s\",\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = \"var(--border-strong)\")}\n onMouseLeave={(e) => (e.currentTarget.style.background = \"var(--divider)\")}\n />\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {selectedId ? (\n <RecordDetailPanel\n key={selectedId}\n entityCode={entityCode!}\n recordId={selectedId}\n items={screen.items}\n schema={schema}\n entity={entity}\n />\n ) : (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n color: \"var(--text-3)\",\n }}\n >\n <p style={{ fontSize: 12.5 }}>Select a {loc(entity.display.name)} to view details</p>\n </div>\n )}\n </div>\n </div>\n );\n}\n\ninterface RecordListRowProps {\n record: RowData;\n entity: Entity;\n columns: string[];\n pkField: string;\n refs?: Record<string, Record<string, Record<string, unknown>>>;\n isSelected: boolean;\n onClick: () => void;\n}\n\nfunction RecordListRow({\n record,\n entity,\n columns,\n refs,\n isSelected,\n onClick,\n}: RecordListRowProps) {\n const userLocale = useUserLocale();\n const primaryCol = columns[0];\n const secondaryCol = columns[1];\n const badgeCols = columns.slice(2, 4);\n\n return (\n <button\n onClick={onClick}\n style={{\n width: \"100%\",\n textAlign: \"left\",\n padding: \"10px 12px\",\n borderBottom: \"1px solid var(--divider)\",\n background: isSelected ? \"var(--row-selected-bg)\" : \"transparent\",\n borderLeft: isSelected ? \"2px solid var(--accent)\" : \"2px solid transparent\",\n cursor: \"pointer\",\n transition: \"background-color .12s\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n border: \"1px solid transparent\",\n borderBottomColor: \"var(--divider)\",\n }}\n onMouseEnter={(e) => {\n if (!isSelected) e.currentTarget.style.background = \"var(--surface-3)\";\n }}\n onMouseLeave={(e) => {\n if (!isSelected) e.currentTarget.style.background = \"transparent\";\n }}\n >\n {primaryCol && (\n <div\n style={{\n fontSize: 12.5,\n fontWeight: 500,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n color: \"var(--text)\",\n }}\n >\n {renderSchemaField({\n entity,\n fieldName: primaryCol,\n record,\n mode: \"display\",\n refs,\n locale: userLocale,\n })}\n </div>\n )}\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n {secondaryCol && (\n <div\n style={{\n fontSize: 11,\n color: \"var(--text-3)\",\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {renderSchemaField({\n entity,\n fieldName: secondaryCol,\n record,\n mode: \"display\",\n refs,\n locale: userLocale,\n })}\n </div>\n )}\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4, flexShrink: 0 }}>\n {badgeCols.map((col) => {\n const value = getSchemaFieldValue(entity, record, col);\n if (value == null) return null;\n return (\n <div key={col} style={{ fontSize: 11 }}>\n {renderSchemaField({\n entity,\n fieldName: col,\n record,\n mode: \"display\",\n refs,\n locale: userLocale,\n })}\n </div>\n );\n })}\n </div>\n </div>\n </button>\n );\n}\n\ninterface RecordDetailPanelProps {\n entityCode: string;\n recordId: string;\n items?: LayoutNode[];\n schema: Schema;\n entity: Entity;\n}\n\nfunction RecordDetailPanel({\n entityCode,\n recordId,\n items,\n schema,\n entity,\n}: RecordDetailPanelProps) {\n return (\n <FileDraftScope>\n <RecordDetailPanelInner\n entityCode={entityCode}\n recordId={recordId}\n items={items}\n schema={schema}\n entity={entity}\n />\n </FileDraftScope>\n );\n}\n\nfunction RecordDetailPanelInner({\n entityCode,\n recordId,\n items,\n schema,\n entity,\n}: RecordDetailPanelProps) {\n const canUpdate = entityAllowsMutation(entity, \"update\");\n const { data: itemResponse, isLoading } = useEntityItem(entityCode, recordId);\n const updateMutation = useUpdateEntity(entityCode);\n const draftScope = useFileDraftScope();\n\n const [editing, setEditing] = useState(false);\n const [formData, setFormData] = useState<Record<string, unknown>>({});\n\n const record = itemResponse?.data as Record<string, unknown> | undefined;\n const statuses = record?.[\"$statuses\"] as StatusInfo[] | undefined;\n const objectProperties = record?.[\"$properties\"] as PropertyValue[] | undefined;\n const setStatusMutation = useSetStatus(entityCode, recordId);\n const setPropertyMutation = useSetProperty(entityCode, recordId);\n\n const editableFields = useMemo(() => {\n const editable = new Set<string>();\n for (const [name, field] of entity.fields) {\n if (!field.auto && !field.primary) editable.add(name);\n }\n for (const [name] of entity.files ?? []) {\n editable.add(name);\n }\n return editable;\n }, [entity]);\n\n const startEditing = useCallback(() => {\n if (record) {\n const data: Record<string, unknown> = {};\n editableFields.forEach((name) => {\n const value = getSchemaFieldValue(entity, record, name);\n data[name] = getEntityFileField(entity, name) ? (value ?? null) : (value ?? \"\");\n });\n setFormData(data);\n }\n setEditing(true);\n }, [record, editableFields, entity]);\n\n const cancelEditing = useCallback(async () => {\n await draftScope.flushAllDrafts();\n setEditing(false);\n setFormData({});\n }, [draftScope]);\n\n const handleSave = useCallback(() => {\n const payload: Record<string, unknown> = {};\n const filesPayload = buildFilesPayload({ entity, formData, record, isNew: false });\n const committedFileIds = collectCommittedFileIds(filesPayload);\n for (const [key, val] of Object.entries(formData)) {\n if (entity.files?.has(key)) continue;\n payload[key] = serializeSchemaFieldValue(entity, key, val);\n }\n if (Object.keys(filesPayload).length > 0) {\n payload.$files = filesPayload;\n }\n if (Object.keys(payload).length === 0) {\n setEditing(false);\n setFormData({});\n return;\n }\n updateMutation.mutate(\n { pkValues: recordId, data: payload },\n {\n onSuccess: () => {\n committedFileIds.forEach((fileId) => draftScope.markCommitted(fileId));\n setEditing(false);\n setFormData({});\n toast.success(\"Saved\");\n },\n onError: () => toast.error(\"Failed to save\"),\n },\n );\n }, [draftScope, updateMutation, recordId, entity, formData, record]);\n\n const handleFieldChange = useCallback((field: string, value: unknown) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n }, []);\n\n if (isLoading) return <DetailSkeleton />;\n if (!record) return null;\n\n // Server-rendered display.template wins over display_field. Falls back\n // to the entity's first field if neither is set, then to the entity name.\n const renderedTitle = getDisplayValue(record as Record<string, unknown>, entity);\n const dfKey = entity.display?.display_field;\n const rawTitle = renderedTitle\n ? undefined\n : dfKey\n ? record[dfKey]\n : record[entity.fields.names()[0]];\n const displayTitle =\n renderedTitle ||\n (rawTitle != null && typeof rawTitle !== \"object\" ? String(rawTitle) : loc(entity.display.name));\n\n const recordRefs = record?.[\"$refs\"] as Record<string, Record<string, Record<string, unknown>>> | undefined;\n\n const ctx: LayoutContext = {\n schema,\n entity,\n entityCode,\n recordId,\n record: editing ? { ...record, ...formData } : record,\n isNew: false,\n mode: editing ? \"edit\" : \"display\",\n onFieldChange: editing ? handleFieldChange : undefined,\n refs: recordRefs,\n objectProperties: objectProperties ?? undefined,\n onSetProperty: !canUpdate\n ? undefined\n : (code, val) => setPropertyMutation.mutate({ propertyCode: code, value: val }),\n isPropertyPending: setPropertyMutation.isPending,\n objectStatuses: statuses ?? undefined,\n onSetStatus: !canUpdate\n ? undefined\n : (groupCode, statusCode) => setStatusMutation.mutate({ groupCode, statusCode }),\n isStatusPending: setStatusMutation.isPending,\n };\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 16px\",\n borderBottom: \"1px solid var(--divider)\",\n position: \"sticky\",\n top: 0,\n background: \"var(--surface)\",\n zIndex: 10,\n }}\n >\n <h2\n style={{\n margin: 0,\n fontSize: 13,\n fontWeight: 600,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n color: \"var(--text)\",\n }}\n >\n {displayTitle}\n </h2>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n {canUpdate &&\n (editing ? (\n <>\n <Btn size=\"xs\" icon={<Icon.x size={12} />} onClick={cancelEditing}>\n Cancel\n </Btn>\n <Btn\n kind=\"primary\"\n size=\"xs\"\n icon={updateMutation.isPending ? <Spinner size={12} /> : <Icon.save size={12} />}\n onClick={handleSave}\n disabled={updateMutation.isPending}\n >\n {updateMutation.isPending ? \"Saving…\" : \"Save\"}\n </Btn>\n </>\n ) : (\n <Btn size=\"xs\" icon={<Icon.edit size={12} />} onClick={startEditing}>\n Edit\n </Btn>\n ))}\n </div>\n </div>\n\n <div style={{ flex: 1, overflowY: \"auto\", padding: 16, display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n {items && items.length > 0 ? (\n <LayoutRenderer nodes={items} ctx={ctx} />\n ) : (\n <p style={{ fontSize: 12.5, color: \"var(--text-3)\" }}>\n No layout configured. Add <code>items:</code> to the screen definition.\n </p>\n )}\n </div>\n </div>\n );\n}\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { Btn } from \"@/components/primitives\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n console.error(\"[ErrorBoundary]\", error, info.componentStack);\n }\n\n handleRetry = () => {\n this.setState({ hasError: false, error: null });\n };\n\n render() {\n if (this.state.hasError) {\n if (this.props.fallback) return this.props.fallback;\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: 200,\n padding: 32,\n }}\n >\n <div style={{ textAlign: \"center\", maxWidth: 420, display: \"flex\", flexDirection: \"column\", gap: 10 }}>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: 0, color: \"var(--text)\" }}>\n Something went wrong\n </h2>\n <p style={{ fontSize: 12.5, color: \"var(--text-3)\", margin: 0 }}>\n {this.state.error?.message || \"An unexpected error occurred.\"}\n </p>\n <div>\n <Btn onClick={this.handleRetry}>Try again</Btn>\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","import { useEffect } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { toast } from \"@/lib/toast\";\n\nimport {\n fetchProbe,\n subscribe,\n type VersionDelta,\n} from \"@/lib/versionTracker\";\nimport { isEmbedTokenMode } from \"@/embed/token\";\n\n/**\n * useVersionWatcher — staleness watchdog for the SPA.\n *\n * Wires three layers of detection (in defense-in-depth order):\n *\n * 1. Header-based detection (in apiFetch via versionTracker.observe)\n * — instant, runs on every API response, no extra round-trip.\n *\n * 2. Periodic /api/version probe — caps drift latency at 60s for\n * idle tabs. Fires on:\n * • window focus (debounced to ≥5s)\n * • online reconnect\n * • document.visibilitychange → visible\n * • 60s interval while visible\n * Backs off exponentially on failure (1s → 5min cap) so a 5xx\n * doesn't error-loop.\n *\n * 3. (future) SSE `version-changed` from the realtime role —\n * handled by useSSE; kicks fetchProbe() on receipt.\n *\n * On drift the tracker classifies the delta:\n * • schema-only → silent React Query invalidation of [\"schema\"]\n * • asset-or-binary → persistent toast: \"Reload now\" / \"Later\"\n * • unknown → same as asset/binary (conservative reload)\n *\n * Safe to call exactly once at the App root. Subsequent calls are\n * harmless (subscribe is idempotent through the tracker's drift-\n * debounce flag), but the probes would compound — keep it to one.\n */\nexport function useVersionWatcher(): void {\n const queryClient = useQueryClient();\n\n useEffect(() => {\n let cancelled = false;\n let scheduledTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // 60s steady-state cadence; failures double until 5min cap.\n const STEADY_INTERVAL_MS = 60_000;\n const BACKOFF_CAP_MS = 5 * 60 * 1_000;\n let nextDelayMs = STEADY_INTERVAL_MS;\n let consecutiveFailures = 0;\n\n let lastFocusProbeAt = 0;\n const FOCUS_DEBOUNCE_MS = 5_000;\n\n function scheduleNext(delayMs: number): void {\n if (cancelled) return;\n if (scheduledTimeout !== null) clearTimeout(scheduledTimeout);\n scheduledTimeout = setTimeout(() => {\n if (cancelled) return;\n // Skip cleanly when the tab is hidden; the visibilitychange\n // listener will trigger a fresh probe when it returns to\n // visible. We still schedule the next tick so a tab that\n // stays hidden indefinitely doesn't silently stop checking.\n if (document.visibilityState !== \"visible\") {\n scheduleNext(STEADY_INTERVAL_MS);\n return;\n }\n void runProbe();\n }, delayMs);\n }\n\n async function runProbe(): Promise<void> {\n if (cancelled) return;\n try {\n await fetchProbe();\n consecutiveFailures = 0;\n nextDelayMs = STEADY_INTERVAL_MS;\n } catch (err) {\n // Only log on the first failure of a streak — repeat 5xx\n // would otherwise spam the console at the backoff cadence.\n if (consecutiveFailures === 0) {\n console.warn(\"[declarion] version probe failed; backing off\", err);\n }\n consecutiveFailures += 1;\n // Exponential ramp anchored at 1s, capped at 5min. The\n // 60s steady-state floor only applies on success — once\n // we're failing, we want to retry sooner-than-60s for a\n // few attempts before the cap kicks in.\n nextDelayMs = Math.min(1_000 * 2 ** Math.min(consecutiveFailures - 1, 9), BACKOFF_CAP_MS);\n }\n scheduleNext(nextDelayMs);\n }\n\n function maybeProbeOnFocus(): void {\n const now = Date.now();\n if (now - lastFocusProbeAt < FOCUS_DEBOUNCE_MS) return;\n lastFocusProbeAt = now;\n // Focus-triggered probe pre-empts the scheduled timeout —\n // user is back, check immediately, then resume cadence\n // from a fresh 60s tick.\n void runProbe();\n }\n\n function handleVisibilityChange(): void {\n if (document.visibilityState === \"visible\") {\n maybeProbeOnFocus();\n }\n }\n\n // Subscribe to drift events first so a probe-driven detection is\n // not lost between fetchProbe resolving and the listener wiring.\n const unsubscribe = subscribe((delta: VersionDelta) => {\n if (delta.kind === \"schema-only\") {\n // Silent path — refetch the schema; UI re-renders against\n // the new shape without disrupting the user.\n void queryClient.invalidateQueries({ queryKey: [\"schema\"] });\n return;\n }\n\n if (isEmbedTokenMode()) {\n // versionTracker already posted `reload-required` to the host. The\n // iframe must not show Declarion-owned reload chrome in white-label mode.\n return;\n }\n\n // Asset / binary / unknown → the running JS bundle is stale,\n // a soft refetch can't repair it. A sticky toast prompts a\n // reload; idempotent (the fixed `id` replaces any previous\n // instance, so repeated drift events don't stack).\n //\n // No \"clear cache before reload\" dance — the tracker holds\n // baseline state in memory only, so window.location.reload()\n // boots a fresh JS context with a clean slate that captures\n // its baseline from the post-reload server's first response.\n //\n // This is an actionable upgrade prompt, not an error: `info`\n // variant, `sticky` so it cannot disappear before the user\n // notices, the `action:` slot is the explicit Reload path.\n toast.info(\"A new version is available\", \"Reload to pick up the latest UI and schema.\", {\n id: \"declarion-update-available\",\n sticky: true,\n action: {\n label: \"Reload now\",\n onClick: () => {\n window.location.reload();\n },\n },\n });\n });\n\n // Layer-2 wiring: focus / online / visibility / scheduled probes.\n window.addEventListener(\"focus\", maybeProbeOnFocus);\n window.addEventListener(\"online\", maybeProbeOnFocus);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n // First probe shortly after mount so a tab opened against an\n // already-redeployed server detects drift right away rather\n // than waiting 60s. After it resolves the chain takes over.\n void runProbe();\n\n return () => {\n cancelled = true;\n unsubscribe();\n window.removeEventListener(\"focus\", maybeProbeOnFocus);\n window.removeEventListener(\"online\", maybeProbeOnFocus);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n if (scheduledTimeout !== null) clearTimeout(scheduledTimeout);\n };\n }, [queryClient]);\n}\n","// capabilityPrefetch: after the app mounts, warm the code-split \"capability\"\n// chunks during browser idle time, so the first navigation to a screen that\n// needs one does not wait on a network round trip.\n//\n// Every Declarion app uses all of these capabilities, so they are warmed\n// unconditionally - there is no schema scan. The chunks are static assets\n// served from the same image; prefetch is a same-origin dynamic import, never\n// a fetch from the internet.\n//\n// Each loader MUST use the exact module the matching lazy() boundary imports,\n// so prefetch and on-demand load resolve to one shared chunk.\n\nconst CAPABILITY_LOADERS: ReadonlyArray<() => Promise<unknown>> = [\n // markdown renderer + remark/micromark parser - lazy in components/shared/Markdown.tsx\n () => import(\"@/components/shared/MarkdownRenderer\"),\n // JSON editor (CodeMirror) - lazy in components/fields/JsonField.tsx\n () => import(\"@/components/fields/JsonEditor\"),\n // file grid (@dnd-kit) - lazy in components/file-widgets/GridLazy.tsx\n () => import(\"@/components/file-widgets/Grid\"),\n // draggable children table (@dnd-kit) - lazy in components/detail-layout/ChildrenBlock.tsx\n () => import(\"@/components/detail-layout/DraggableChildrenTable\"),\n];\n\n// warmAll fires every loader. A rejected prefetch is swallowed: the lazy\n// boundary retries the import on real use, so a transient failure here must\n// never surface to the user.\nfunction warmAll(): void {\n for (const load of CAPABILITY_LOADERS) {\n void load().catch(() => {});\n }\n}\n\nlet scheduled = false;\n\n/**\n * Warm every code-split capability chunk during browser idle time.\n * Idempotent - call once, after the app has mounted.\n */\nexport function prefetchHeavyCapabilities(): void {\n if (scheduled) return;\n scheduled = true;\n const idle = (\n globalThis as { requestIdleCallback?: (cb: () => void) => void }\n ).requestIdleCallback;\n if (typeof idle === \"function\") {\n idle(warmAll);\n } else {\n // Safari < 17 has no requestIdleCallback; a macrotask is close enough.\n setTimeout(warmAll, 0);\n }\n}\n\n// Test-only: clear the once-guard so a test can schedule again.\nexport function __resetCapabilityPrefetchForTests(): void {\n scheduled = false;\n}\n","import type { ComponentType } from \"react\";\n\nconst registry = new Map<string, ComponentType>();\n\nexport function registerScreen(code: string, component: ComponentType): void {\n registry.set(code, component);\n}\n\nexport function getRegisteredScreen(code: string): ComponentType | undefined {\n return registry.get(code);\n}\n","import { useEffect, useMemo, type ReactNode } from \"react\";\nimport { BrowserRouter, Routes, Route, Navigate } from \"react-router-dom\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { Toaster } from \"sonner\";\nimport { Layout } from \"@/components/layout/Layout\";\nimport { SignIn } from \"@/components/pages/auth/SignIn\";\nimport { SignUp } from \"@/components/pages/auth/SignUp\";\nimport { Forgot } from \"@/components/pages/auth/Forgot\";\nimport { Reset } from \"@/components/pages/auth/Reset\";\nimport { TwoFA } from \"@/components/pages/auth/TwoFA\";\nimport { SSOCallbackPage } from \"@/components/pages/SSOCallbackPage\";\nimport { NotFoundPage } from \"@/components/pages/NotFoundPage\";\nimport { Profile } from \"@/components/pages/profile/Profile\";\nimport { Preferences } from \"@/components/pages/profile/Preferences\";\nimport { AdminCatalogPage } from \"@/components/pages/AdminCatalogPage\";\nimport { SmartListPage } from \"@/components/pages/SmartListPage\";\nimport { SmartDetailPage } from \"@/components/pages/SmartDetailPage\";\nimport { SmartRecordListPage } from \"@/components/pages/SmartRecordListPage\";\nimport { ErrorBoundary } from \"@/components/shared/ErrorBoundary\";\nimport { useAuthStore } from \"@/stores/auth\";\nimport { useAppearanceStore } from \"@/stores/appearance\";\nimport { useSchema } from \"@/hooks/useSchema\";\nimport { usePublicParams } from \"@/hooks/useParams\";\nimport { useVersionWatcher } from \"@/hooks/useVersionWatcher\";\nimport { isValidLogoUrl } from \"@/lib/branding\";\nimport { prefetchHeavyCapabilities } from \"@/lib/capabilityPrefetch\";\nimport { schemaDefaultRoute } from \"@/lib/schema-routing\";\nimport { getRegisteredScreen, registerScreen } from \"@/stores/screen-registry\";\nimport { EmbedProvider, useEmbed, EmbedAuthBootstrap } from \"@/embed\";\n\n// Platform-shipped custom screens. YAML schemas reference these by the\n// registered code via `screens.<x>: { type: custom, component: <Code> }`.\n// Consumer apps may register additional custom screens via the SDK's\n// re-exported `registerScreen` (see @declarion/react index).\nregisterScreen(\"Profile\", Profile);\n\n// DOM id for the SDK-managed favicon link. Using a stable id lets the\n// effect idempotently update the href or remove the element when the\n// consumer clears branding.app_logo.\nconst MANAGED_FAVICON_ID = \"declarion-managed-favicon\";\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000,\n retry: 1,\n },\n },\n});\n\n// safeSameOriginHref validates `branding.css_url` before injection.\n// Only same-origin URLs are permitted; anything resolving to a different\n// origin (http://attacker.example/…, //evil/…, data:, blob:, etc.) is\n// rejected. Returns the resolved absolute URL on success, null otherwise.\nfunction safeSameOriginHref(raw: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const url = new URL(raw, window.location.origin);\n if (url.protocol !== window.location.protocol) return null;\n if (url.host !== window.location.host) return null;\n return url.href;\n } catch {\n return null;\n }\n}\n\nfunction useDynamicRoutes(): { routes: ReactNode[]; defaultRoute: string | null } {\n const { data: schema } = useSchema();\n\n const routes = useMemo<ReactNode[]>(() => {\n if (!schema) return [];\n\n const out: ReactNode[] = [];\n // schema.screens is absent when an anonymous caller hits /api/schema —\n // the server returns only accents/languages/parameters pre-login, so\n // guard the iteration or Object.entries crashes on undefined.\n for (const [code, screen] of Object.entries(schema.screens ?? {})) {\n if (screen.type === \"list\") {\n // Singleton entities have at most one visible row per caller - a\n // list view is meaningless. Skip the list route; detail-bound\n // screens on the same entity still register normally below.\n const entityCode = (screen as { entity?: string }).entity;\n const entity = entityCode ? schema.entities?.[entityCode] : undefined;\n if (entity?.singleton) {\n continue;\n }\n out.push(\n <Route\n key={code}\n path={screen.route}\n element={<ErrorBoundary><SmartListPage screenCode={code} /></ErrorBoundary>}\n />,\n );\n continue;\n }\n if (screen.type === \"detail\") {\n out.push(\n <Route\n key={code}\n path={screen.route}\n element={<ErrorBoundary><SmartDetailPage screenCode={code} /></ErrorBoundary>}\n />,\n );\n continue;\n }\n if (screen.type === \"record_list\") {\n out.push(\n <Route\n key={code}\n path={screen.route}\n element={<ErrorBoundary><SmartRecordListPage screenCode={code} /></ErrorBoundary>}\n />,\n );\n continue;\n }\n if (screen.type === \"custom\") {\n const CustomComponent = screen.component\n ? getRegisteredScreen(screen.component)\n : undefined;\n if (CustomComponent) {\n out.push(\n <Route\n key={code}\n path={screen.route}\n element={<ErrorBoundary><CustomComponent /></ErrorBoundary>}\n />,\n );\n }\n }\n }\n return out;\n }, [schema]);\n\n const defaultRoute = useMemo(() => schemaDefaultRoute(schema), [schema]);\n\n return { routes, defaultRoute };\n}\n\nfunction AppRoutes() {\n const initialize = useAuthStore((s) => s.initialize);\n const authLoading = useAuthStore((s) => s.isLoading);\n const isAuthenticated = useAuthStore((s) => s.isAuthenticated);\n const { data: schema, isLoading: schemaLoading } = useSchema();\n const { data: publicParams, isLoading: publicParamsLoading } = usePublicParams();\n const { routes, defaultRoute } = useDynamicRoutes();\n const embed = useEmbed();\n\n // Reverse-framing guard: the document is in an iframe but NOT in an approved\n // embed session (no `?embed=1`). Rendering the standalone shell here would\n // expose the full app - chrome, and on a hard route load the public landing\n // + login - inside someone else's frame. We render a neutral guard instead\n // (see the terminal return below) and suppress the cookie probe so a hostile\n // frame triggers no auth side effects. CSP `frame-ancestors` separately\n // controls WHO may frame; this controls WHAT renders when framed-but-not-\n // embed, and is shell-leak containment, not a frame-bust. `window`-guarded\n // for SSR. Note: a same-origin standalone-in-a-frame (allowed by\n // `frame-ancestors 'self'`) also hits this guard - accepted, there is no such\n // use case today; revisit if one appears.\n const framedWithoutEmbed =\n typeof window !== \"undefined\" && window.self !== window.top && !embed.enabled;\n\n // The cookie-based unauthenticated probe (initialize -> POST auth.me with\n // the __Host- cookie) MUST NOT run in embed mode. The embed iframe is\n // same-origin to Declarion, so a sent cookie would silently authenticate\n // the viewer's full-authority browser session instead of the scoped embed\n // token. Embed auth arrives only via the postMessage handshake; until then\n // there is intentionally no session. It also must not run under the\n // reverse-framing guard. Outside both, the probe runs exactly as before.\n useEffect(() => {\n if (embed.enabled || framedWithoutEmbed) return;\n initialize();\n }, [embed.enabled, framedWithoutEmbed, initialize]);\n\n // After mount, warm the code-split capability chunks (markdown renderer,\n // JSON editor, drag-and-drop) during browser idle time. requestIdleCallback\n // defers this until the schema fetch and first real paint have settled, so\n // it never competes with first-paint work.\n useEffect(() => {\n prefetchHeavyCapabilities();\n }, []);\n\n // Drive document.title and the tab favicon from consumer-YAML branding\n // (branding.app_name + branding.app_logo on /api/params/public). The HTML\n // shell is kept brand-neutral so no default name or icon flashes before\n // JS boots; the first paint after public params resolve is already in\n // the consumer's brand.\n const branding = publicParams?.branding;\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const title = branding?.app_name?.trim();\n if (title) document.title = title;\n }, [branding?.app_name]);\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const rawLogo = branding?.app_logo;\n const resolved =\n isValidLogoUrl(rawLogo) ? safeSameOriginHref(rawLogo!.trim()) : null;\n const existing = document.getElementById(MANAGED_FAVICON_ID) as HTMLLinkElement | null;\n if (!resolved) {\n // No consumer favicon (or rejected URL): tear down any link we\n // previously installed so stale icons don't linger after YAML\n // removes app_logo.\n if (existing) existing.remove();\n return;\n }\n // First-time install: strip any static <link rel=\"icon\"> the HTML\n // shell might still carry so browsers never race between two icons.\n if (!existing) {\n document\n .querySelectorAll('link[rel=\"icon\"], link[rel=\"shortcut icon\"]')\n .forEach((el) => el.remove());\n const link = document.createElement(\"link\");\n link.id = MANAGED_FAVICON_ID;\n link.rel = \"icon\";\n link.href = resolved;\n document.head.appendChild(link);\n return;\n }\n if (existing.href !== resolved) existing.href = resolved;\n }, [branding?.app_logo]);\n\n // Consumer-app brand override: if the active module declares\n // `branding.css_url` in its schema, inject a <link> into <head>. The CSS\n // file contains narrow token overrides only (e.g. :root { --accent: … }),\n // layered over globals.css. Deduped by element id so hot reloads don't\n // accumulate links. Removing the field removes the <link> on next render.\n //\n // Same-origin only: the platform serves consumer CSS as a root static\n // asset from the UI bundle (e.g. \"/crm-brand.css\"). Off-origin URLs are\n // rejected even though `branding.css_url` is operator-controlled YAML —\n // enterprise deployments treat any third-party stylesheet inclusion as a\n // supply-chain and exfiltration risk. To ship a CSS override, drop the\n // file in the consumer module's `declarion/app/public/` directory; the\n // URL path is relative to the UI bundle root.\n const cssUrl = schema?.branding?.css_url ?? null;\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const id = \"consumer-brand-css\";\n const existing = document.getElementById(id) as HTMLLinkElement | null;\n if (!cssUrl) {\n if (existing) existing.remove();\n return;\n }\n const resolved = safeSameOriginHref(cssUrl);\n if (!resolved) {\n if (existing) existing.remove();\n console.error(\n `[declarion] branding.css_url rejected: ${cssUrl}. ` +\n \"Only same-origin, relative URLs are allowed (e.g. /crm-brand.css).\",\n );\n return;\n }\n if (existing && existing.href === resolved) return;\n if (existing) existing.remove();\n const link = document.createElement(\"link\");\n link.id = id;\n link.rel = \"stylesheet\";\n link.href = resolved;\n document.head.appendChild(link);\n }, [cssUrl]);\n\n // Block the first paint until consumer branding AND schema are\n // available. Without the schema gate, dynamic routes\n // (useDynamicRoutes) is an empty array on the first render and\n // React Router falls through to the catch-all <Route path=\"*\">,\n // briefly rendering NotFoundPage on every refresh until the\n // schema fetch lands (~100-500ms). The user sees a \"404 Page\n // not found\" flash that disappears once schema resolves —\n // confusing, especially on slow connections. publicParams +\n // schema both staleTime: Infinity so this gate fires only on the\n // VERY first load; cached visits go straight through.\n // Boolean-shaped check (loading && !data) lets SSR-style \"no\n // fetch yet\" + transient-error states fall through to the\n // router so the user gets a real error UI, not a hung blank\n // screen.\n //\n // Embed auth-pending gate: in embed mode the iframe boots with no token\n // (the postMessage handshake delivers it in a later task). Until an embed\n // session is authenticated, app.tsx MUST NOT resolve to the <Routes> tree -\n // doing so would render the Declarion login page inside the white-label\n // iframe. While `embed.enabled && !isAuthenticated`, hold the neutral blank.\n const embedAuthPending = embed.enabled && !isAuthenticated;\n\n // Reverse-framing guard (TERMINAL): a framed-but-not-embed document never\n // resolves to <Routes>. Return the neutral surface and stop - never the\n // standalone shell / login inside someone else's frame.\n if (framedWithoutEmbed) {\n return (\n <div aria-hidden style={{ minHeight: \"100vh\", background: \"var(--bg)\" }} />\n );\n }\n\n if (\n embedAuthPending ||\n authLoading ||\n (publicParamsLoading && !publicParams) ||\n (schemaLoading && !schema)\n ) {\n return (\n <div\n aria-hidden\n style={{ minHeight: \"100vh\", background: \"var(--bg)\" }}\n />\n );\n }\n\n return (\n <Routes>\n <Route path=\"/login\" element={<SignIn />} />\n <Route path=\"/login/sso-callback\" element={<SSOCallbackPage />} />\n <Route path=\"/signup\" element={<SignUp />} />\n <Route path=\"/forgot\" element={<Forgot />} />\n <Route path=\"/reset\" element={<Reset />} />\n <Route path=\"/2fa\" element={<TwoFA />} />\n <Route element={<Layout />}>\n {defaultRoute && (\n <>\n <Route index element={<Navigate to={defaultRoute} replace />} />\n {/* Bare /admin has no screen of its own (every real screen lives at\n /admin/<slug>). Without this redirect, users who bookmark or\n share /admin land on NotFoundPage even though the side nav is\n fully populated. Mirror the index-route behaviour: redirect to\n the same defaultRoute the navigation tree picked. */}\n <Route path=\"/admin\" element={<Navigate to={defaultRoute} replace />} />\n </>\n )}\n {routes}\n <Route path=\"/preferences\" element={<Preferences />} />\n <Route path=\"/admin/catalog\" element={<AdminCatalogPage />} />\n <Route path=\"*\" element={<NotFoundPage />} />\n </Route>\n </Routes>\n );\n}\n\n// AppShell hosts hooks that need to live inside QueryClientProvider — most\n// notably useVersionWatcher, which uses the React Query client to invalidate\n// the cached schema on schema-only drift. Splitting App in two avoids\n// re-introducing a \"useQueryClient outside its Provider\" runtime error.\nfunction AppShell() {\n // Single staleness watchdog — header observation runs in apiFetch\n // independently; this hook adds the focus/online/60s probe layer\n // and the React Query / toast reactions.\n useVersionWatcher();\n // Toast placement is declared, not hard-coded - `ui_toast_position`\n // (default bottom-right) flows through the appearance store.\n const toastPosition = useAppearanceStore((s) => s.toastPosition);\n return (\n <BrowserRouter>\n {/* EmbedAuthBootstrap runs the postMessage auth handshake. It mounts\n ABOVE the AppRoutes auth-pending gate so it can receive the first\n `auth` frame while AppRoutes still holds the neutral blank, and\n inside BrowserRouter so it can drive an inbound `navigate` frame.\n Inert (renders nothing) outside embed mode. */}\n <EmbedAuthBootstrap />\n <AppRoutes />\n {/* Toaster: Sonner is the headless engine only (portal, stack\n geometry, swipe, the aria-live region). Every toast renders as\n a <DeclarionToast> card via `toast.custom` in lib/toast - hence\n `toastOptions.unstyled` to strip Sonner's own chrome.\n - position: declared via `ui_toast_position` (default\n bottom-right) so moving the toaster is a parameter change,\n not a code edit. bottom-right also keeps the close × on the\n trailing edge and clears the list-page \"New X\" CTA.\n - expand: the stack stays fanned out (the design is a flat\n gap-8 column), not collapsed-until-hover.\n - visibleToasts 4: the design caps the stack at 4. */}\n <Toaster\n position={toastPosition}\n toastOptions={{ unstyled: true }}\n expand\n visibleToasts={4}\n gap={8}\n offset={16}\n />\n </BrowserRouter>\n );\n}\n\nexport function App() {\n return (\n <ErrorBoundary>\n <QueryClientProvider client={queryClient}>\n {/* EmbedProvider wraps AppShell so BrowserRouter, every route, and\n Layout can call useEmbed(). The embed contract is parsed once\n from the URL at provider construction. */}\n <EmbedProvider>\n <AppShell />\n </EmbedProvider>\n </QueryClientProvider>\n </ErrorBoundary>\n );\n}\n","// TODO(T1): EntityClient is the v2 convenience layer built alongside the existing v1\n// hooks (useEntityData, useEntityItem, useMutations). The v1 hooks will be migrated to\n// use EntityClient internally over time. Until then, both layers coexist.\n//\n// TODO(T6): No tests exist for the v2 SDK yet. Unit tests are needed for EntityClient\n// methods, attachHelpers immutability, RefCache merge semantics, and URL building.\nimport { apiFetch, callHandler } from \"./client\";\nimport { executeAction } from \"./data\";\nimport type { BatchOpResult, BatchResponse } from \"./batch\";\nimport type {\n Refs,\n V2ListParams,\n V2ListResponse,\n V2Meta,\n V2StatusInstance,\n V2PropertyInstance,\n EnrichedRow,\n} from \"@/types/api\";\nimport { isCursorStaleError } from \"@/types/api\";\n\n// ---------------------------------------------------------------------------\n// Helpers attached to returned rows\n// ---------------------------------------------------------------------------\n\n/** Find a status by group code on an enriched row. */\nfunction findStatus(\n row: EnrichedRow,\n groupCode: string,\n): V2StatusInstance | undefined {\n return row.$statuses?.find((s) => s.group_code === groupCode);\n}\n\n/** Find a property by code on an enriched row. */\nfunction findProperty(\n row: EnrichedRow,\n propertyCode: string,\n): V2PropertyInstance | undefined {\n return row.$properties?.find((p) => p.property_code === propertyCode);\n}\n\n/**\n * Attach $status() and $property() convenience helpers to a row.\n * Returns a shallow copy - never mutates the input (which may be a\n * TanStack Query cache entry where mutation breaks change detection).\n */\nfunction attachHelpers<T extends Record<string, unknown>>(\n row: T & EnrichedRow,\n): T & EnrichedRow & RowHelpers {\n const enriched = { ...row } as T & EnrichedRow & RowHelpers;\n enriched.$status = (groupCode: string) => findStatus(enriched, groupCode);\n enriched.$property = (propertyCode: string) => findProperty(enriched, propertyCode);\n return enriched;\n}\n\n/** Helper methods injected onto enriched rows. */\nexport interface RowHelpers {\n $status(groupCode: string): V2StatusInstance | undefined;\n $property(propertyCode: string): V2PropertyInstance | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Ref cache - merges $refs across pages\n// ---------------------------------------------------------------------------\n\n/** Normalized ref cache that merges $refs across multiple responses. */\nexport class RefCache {\n private cache: Refs = {};\n\n /** Merge incoming refs into the cache. */\n merge(refs: Refs | undefined): void {\n if (!refs) return;\n for (const entityCode of Object.keys(refs)) {\n if (!this.cache[entityCode]) {\n this.cache[entityCode] = {};\n }\n const incoming = refs[entityCode];\n for (const id of Object.keys(incoming)) {\n // Newer data wins - shallow merge per ref object.\n this.cache[entityCode][id] = {\n ...this.cache[entityCode][id],\n ...incoming[id],\n };\n }\n }\n }\n\n /** Get a snapshot of all cached refs. */\n all(): Refs {\n return this.cache;\n }\n\n /** Look up a single ref by entity code and id. */\n get(entityCode: string, id: string): Record<string, unknown> | undefined {\n return this.cache[entityCode]?.[id];\n }\n\n /** Clear the cache. */\n clear(): void {\n this.cache = {};\n }\n}\n\n// ---------------------------------------------------------------------------\n// URL builder\n// ---------------------------------------------------------------------------\n\nfunction buildV2ListUrl(entity: string, params: V2ListParams): string {\n const url = new URL(`/api/data/${entity}`, window.location.origin);\n\n if (params.select && params.select.length > 0) {\n url.searchParams.set(\"select\", params.select.join(\",\"));\n }\n if (params.expand && params.expand.length > 0) {\n url.searchParams.set(\"expand\", params.expand.join(\",\"));\n }\n if (params.sort) url.searchParams.set(\"sort\", params.sort);\n if (params.search) url.searchParams.set(\"search\", params.search);\n if (params.limit) url.searchParams.set(\"limit\", String(params.limit));\n if (params.after) url.searchParams.set(\"after\", params.after);\n if (params.include_deleted) url.searchParams.set(\"include_deleted\", \"true\");\n\n if (params.filters && params.filters.length > 0) {\n url.searchParams.set(\"filters\", JSON.stringify(params.filters));\n }\n\n return url.pathname + url.search;\n}\n\n// ---------------------------------------------------------------------------\n// EntityClient<T>\n// ---------------------------------------------------------------------------\n\n/**\n * Generic, type-safe client for a single entity's v2 API.\n *\n * Single-item methods wrap arrays and unwrap the first result.\n * Bulk methods pass arrays through directly.\n *\n * All write responses return enriched rows with $refs, $statuses, etc.\n */\nexport class EntityClient<T extends Record<string, unknown> = Record<string, unknown>> {\n readonly entity: string;\n readonly refs: RefCache;\n\n constructor(entity: string, refCache?: RefCache) {\n this.entity = entity;\n this.refs = refCache ?? new RefCache();\n }\n\n // ---- Read ----------------------------------------------------------------\n\n /** Get a single-id record through the query-PK detail contract. */\n async get(id: string): Promise<T & EnrichedRow & RowHelpers> {\n const params = new URLSearchParams({ id });\n const res = await apiFetch<{ data: T & EnrichedRow }>(\n `/api/data/${this.entity}?${params.toString()}`,\n );\n return attachHelpers(res.data);\n }\n\n /** List records with cursor-based pagination, field selection, and expansion.\n *\n * Transparent recovery on cursor-stale signals: when the server returns\n * `cursor_semantics_mismatch` (sort grammar version bump) or\n * `cursor_locale_changed` (active locale switched mid-pagination), the\n * `after` token is dropped and the request is retried once. The caller\n * receives page 1 of the fresh result set. Both signals indicate that\n * resuming the cursor would mis-order rows; refetching from the top is\n * the correct contract. See docs/plans/2026-06-02-multilang-locale-aware-sort.md.\n */\n async list(\n params: V2ListParams = {},\n ): Promise<{ data: (T & RowHelpers)[]; meta: V2Meta; $refs: Refs }> {\n let res: V2ListResponse<T & EnrichedRow>;\n try {\n res = await apiFetch<V2ListResponse<T & EnrichedRow>>(\n buildV2ListUrl(this.entity, params),\n );\n } catch (err) {\n if (params.after && isCursorStaleError(err)) {\n // Drop the stale cursor and refetch page 1 in current conditions.\n const { after: _drop, ...freshParams } = params;\n res = await apiFetch<V2ListResponse<T & EnrichedRow>>(\n buildV2ListUrl(this.entity, freshParams),\n );\n } else {\n throw err;\n }\n }\n\n // Merge response-level $refs into the cache.\n this.refs.merge(res.$refs);\n\n const data = res.data.map((row) => attachHelpers(row));\n return { data, meta: res.meta, $refs: this.refs.all() };\n }\n\n // ---- Create --------------------------------------------------------------\n\n /** Create a single record. Wraps in array, returns unwrapped result. */\n async create(\n item: Partial<T> & Record<string, unknown>,\n ): Promise<T & EnrichedRow & RowHelpers> {\n const results = await this.createMany([item]);\n return results[0];\n }\n\n /**\n * Create multiple records in one transactional batch.\n *\n * Plan 2026-06-01-write-api-final §1.2 + Non-Obvious Knowledge:\n * `data.create` is one-row-per-dispatch on the new envelope; N independent\n * inserts compose through the platform batch primitive (`system.batch`,\n * atomic READ COMMITTED tx). Each child op targets `<entity>.__create`\n * with a FLAT `{entity, fields}` body inside `BatchOp.params`.\n *\n * Returns rows in input order. The batch fails atomically: any per-op\n * error surfaces as a thrown Error with the first failing op's code +\n * message; `committed: false` (4xx/200-soft-fail) means no row was\n * written. The HTTP transport itself failing (5xx, network) keeps\n * propagating as ApiError unchanged.\n */\n async createMany(\n items: (Partial<T> & Record<string, unknown>)[],\n ): Promise<(T & EnrichedRow & RowHelpers)[]> {\n if (items.length === 0) return [];\n const actions = items.map((fields) => ({\n action: `${this.entity}.__create`,\n params: { entity: this.entity, fields },\n }));\n const res = await callHandler<BatchResponse>(\"system.batch\", {\n actions,\n atomic: true,\n });\n assertBatchCommitted(res, `${this.entity}.__create`);\n return res.results.map((op) => attachHelpers(extractFirstRow<T>(op)));\n }\n\n // ---- Update --------------------------------------------------------------\n\n /** Update a single record. Wraps in array, returns unwrapped result. */\n async update(\n id: string,\n data: Partial<T> & Record<string, unknown>,\n ): Promise<T & EnrichedRow & RowHelpers> {\n const results = await this.updateMany([{ id, ...data }]);\n return results[0];\n }\n\n /**\n * Update multiple records in one request. Each item must include its id.\n *\n * Plan 2026-06-01-write-api-final §1.2 + Non-Obvious Knowledge:\n * `data.update` is one-fields-map-per-dispatch on the new envelope\n * ({object_ids, entity, fields}); per-row variation composes through\n * `system.batch` (atomic READ COMMITTED tx) — one __update child op per\n * input item, mirroring createMany / upsertMany. The batch is atomic:\n * any per-op failure rolls back all rows. The first per-op error\n * surfaces as a thrown Error.\n *\n * Returns rows in input order.\n */\n async updateMany(\n items: (Partial<T> & Record<string, unknown> & { id: string })[],\n ): Promise<(T & EnrichedRow & RowHelpers)[]> {\n if (items.length === 0) return [];\n const actions = items.map(({ id, ...fields }) => ({\n action: `${this.entity}.__update`,\n params: {\n object_ids: [id],\n entity: this.entity,\n fields,\n },\n }));\n const res = await callHandler<BatchResponse>(\"system.batch\", {\n actions,\n atomic: true,\n });\n assertBatchCommitted(res, `${this.entity}.__update`);\n return res.results.map((op) => attachHelpers(extractFirstRow<T>(op)));\n }\n\n // ---- Delete --------------------------------------------------------------\n\n /** Delete a single record by ID. */\n async delete(id: string): Promise<void> {\n await this.deleteMany([id]);\n }\n\n /**\n * Delete multiple records in one request. Returns the count of deleted\n * records.\n *\n * Plan 2026-06-01-write-api-final §1.2: dispatches\n * /api/actions/<entity>.__delete with a FLAT envelope\n * ({object_ids, entity}). The reserved `object_ids` key lands directly\n * in ctx.ObjectIDs; the dispatcher derives items from those + the\n * entity's PK fields. NO legacy `_ids` on the wire (rejected 400 by\n * parseActionBody as of Task 5).\n */\n async deleteMany(ids: string[]): Promise<{ count: number }> {\n if (ids.length === 0) return { count: 0 };\n const res = await executeAction(`${this.entity}.__delete`, {\n object_ids: ids,\n entity: this.entity,\n });\n return { count: res.object_count ?? ids.length };\n }\n\n // ---- Restore -------------------------------------------------------------\n\n /** Restore a single soft-deleted record by ID. */\n async restore(id: string): Promise<void> {\n await this.restoreMany([id]);\n }\n\n /**\n * Restore multiple soft-deleted records. Returns the count of restored\n * records.\n *\n * Plan 2026-06-01-write-api-final §1.2: routes through the platform-\n * default `__restore` action. The legacy CRUD route\n * POST /api/data/{entity}/restore is retired (returns 410). The FLAT\n * envelope ({object_ids, entity}) lands directly in DataRestoreParams\n * via JSON tags; no `params:` wrapper on the wire.\n */\n async restoreMany(ids: string[]): Promise<{ count: number }> {\n if (ids.length === 0) return { count: 0 };\n const res = await executeAction(`${this.entity}.__restore`, {\n object_ids: ids,\n entity: this.entity,\n });\n return { count: res.object_count ?? ids.length };\n }\n\n // ---- Upsert --------------------------------------------------------------\n\n /** Upsert a single record. Wraps in array, returns unwrapped result. */\n async upsert(\n item: Partial<T> & Record<string, unknown>,\n uniqueFields: string[],\n ): Promise<T & EnrichedRow & RowHelpers> {\n const results = await this.upsertMany([item], uniqueFields);\n return results[0];\n }\n\n /**\n * Upsert multiple records. Existing matches (by uniqueFields) are\n * updated; absent rows are inserted.\n *\n * Plan 2026-06-01-write-api-final §1.2 + OQ-1: `data.upsert` is one-row-\n * per-dispatch on the new envelope (per-row uniqueness matching), so N\n * rows compose through `system.batch`. Each child op targets\n * `<entity>.__upsert` with FLAT `{entity, fields, unique_by}` params.\n * The handler returns `DataUpsertResult.rows[]` where each\n * DataUpsertRowResult carries `{pk, action, row}`; the row payload is\n * extracted to preserve the prior API shape (array of enriched rows).\n *\n * The batch is atomic: any per-op failure rolls back all rows. The\n * first per-op error surfaces as a thrown Error.\n */\n async upsertMany(\n items: (Partial<T> & Record<string, unknown>)[],\n uniqueFields: string[],\n ): Promise<(T & EnrichedRow & RowHelpers)[]> {\n if (items.length === 0) return [];\n const actions = items.map((fields) => ({\n action: `${this.entity}.__upsert`,\n params: {\n entity: this.entity,\n fields,\n unique_by: uniqueFields,\n },\n }));\n const res = await callHandler<BatchResponse>(\"system.batch\", {\n actions,\n atomic: true,\n });\n assertBatchCommitted(res, `${this.entity}.__upsert`);\n return res.results.map((op) =>\n attachHelpers(extractUpsertRow<T>(op)),\n );\n }\n}\n\n/**\n * Throws if a system.batch dispatch did not commit. Each `system.batch`\n * call returns HTTP 200 + `committed:false` on logical failure (per its\n * design — see batch.ts header); transport failures already throw via\n * apiFetch. Surface the first failing op so the caller sees a meaningful\n * message instead of an empty/partial result silently.\n */\nfunction assertBatchCommitted(res: BatchResponse, action: string): void {\n if (res.committed) return;\n const failed = res.results.find((op) => !op.ok);\n const detail = failed\n ? `${failed.error_code ?? \"ERROR\"}: ${failed.error ?? \"unknown\"}`\n : \"batch did not commit\";\n throw new Error(`${action} batch failed: ${detail}`);\n}\n\n/**\n * Pull the first row from a DataCreateResult (or compatible\n * {rows: [...]}-shaped) inner-op result. data.create returns exactly one\n * row per dispatch; the slot is always rows[0].\n */\nfunction extractFirstRow<T extends Record<string, unknown>>(\n op: BatchOpResult,\n): T & EnrichedRow {\n const r = (op.result ?? {}) as { rows?: unknown[] };\n const row = Array.isArray(r.rows) ? r.rows[0] : undefined;\n return (row ?? {}) as T & EnrichedRow;\n}\n\n/**\n * Pull the row payload out of a DataUpsertResult.Rows[0] entry. The\n * upsert handler returns `{pk, action, row}` per row; the prior SDK\n * surface returned the row map directly.\n */\nfunction extractUpsertRow<T extends Record<string, unknown>>(\n op: BatchOpResult,\n): T & EnrichedRow {\n const r = (op.result ?? {}) as { rows?: Array<{ row?: unknown }> };\n const entry = Array.isArray(r.rows) ? r.rows[0] : undefined;\n return ((entry?.row ?? {}) as T & EnrichedRow);\n}\n\n/**\n * Create an EntityClient for the given entity code.\n * Optionally share a RefCache across multiple clients.\n *\n * NOTE(T5): Each call creates a new EntityClient (and a new RefCache if none is\n * provided). For cross-page ref accumulation, callers should create a persistent\n * RefCache instance and pass it to every entityClient() call, e.g. via React\n * context or a module-level singleton.\n */\nexport function entityClient<T extends Record<string, unknown> = Record<string, unknown>>(\n entity: string,\n refCache?: RefCache,\n): EntityClient<T> {\n return new EntityClient<T>(entity, refCache);\n}\n","// system.batch primitive client. Mirrors the Go SDK builder shape:\n//\n// const resp = await new Batch()\n// .call(\"data.upsert\", { entity: \"state_counters\", records: [...], unique_by: [\"code\"] })\n// .upsert(\"analytics_events\", [...], \"insert\", [\"code\"])\n// .call(\"swiftward.actions.http_request\", { url: \"...\" })\n// .execute();\n//\n// system.batch is sync + atomic by definition: the wire roundtrip resolves\n// only after the platform commits (or rolls back) the entire transaction.\n//\n// HTTP 200 with { committed: false } is the wire-level signal for a logical\n// op failure. The frontend MUST branch on `committed`, not on a thrown\n// error; thrown errors here reflect transport/infra failures only.\n\nimport { callHandler } from \"./client\";\n\nexport interface BatchOp {\n action: string;\n params?: Record<string, unknown> | null;\n}\n\nexport interface BatchOpResult {\n index: number;\n ok: boolean;\n result?: unknown;\n error?: string;\n error_code?: string;\n}\n\nexport interface BatchResponse {\n results: BatchOpResult[];\n committed: boolean;\n}\n\nexport class Batch {\n private ops: BatchOp[] = [];\n\n call(action: string, params?: unknown): this {\n this.ops.push({\n action,\n params: toParamsRecord(params),\n });\n return this;\n }\n\n // upsert is sugar for call(\"data.upsert\", ...). Mirrors the platform\n // handler's params exactly; mode is one of \"upsert\" | \"insert_if_missing\".\n upsert(\n entity: string,\n records: Array<Record<string, unknown>>,\n mode: \"upsert\" | \"insert_if_missing\" = \"upsert\",\n uniqueBy: string[] = [],\n ): this {\n const params: Record<string, unknown> = {\n entity,\n records,\n mode,\n };\n if (uniqueBy.length > 0) {\n params.unique_by = uniqueBy;\n }\n return this.call(\"data.upsert\", params);\n }\n\n async execute(): Promise<BatchResponse> {\n if (this.ops.length === 0) {\n throw new Error(\n \"declarion: batch.execute() called with no ops; add ops via .call() or .upsert()\",\n );\n }\n return await callHandler<BatchResponse>(\"system.batch\", {\n actions: this.ops,\n atomic: true,\n });\n }\n\n // Internal accessor for tests / debugging only.\n _ops(): readonly BatchOp[] {\n return this.ops;\n }\n}\n\nfunction toParamsRecord(v: unknown): Record<string, unknown> | null {\n if (v == null) {\n return null;\n }\n if (typeof v === \"object\" && !Array.isArray(v)) {\n return v as Record<string, unknown>;\n }\n // Non-object params (e.g. a string) are not what the wire expects.\n // Wrap into a single-key object so the runtime error from the platform\n // points at the offending op rather than a JSON unmarshal failure.\n return { value: v };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { listEntities } from \"@/api/data\";\nimport { RefCache } from \"@/api/entities\";\nimport type { ListParams, ListResponse } from \"@/types/api\";\n\n// Page size for internal pagination. Matches the server's per_page hard cap\n// (declarion-core internal/store/data_list.go): a request above this cap is\n// silently reset to the default page size, so 1000 is the largest useful\n// page and the fewest round-trips.\nconst ALL_PAGE_SIZE = 1000;\n\n// Hard ceiling on the number of pages this hook will fetch. This hook holds\n// the entire result set in memory and is contracted for BOUNDED entities\n// (board / kanban screens) only. Pointed at a firehose table it would fan out\n// thousands of parallel requests and freeze the tab. 20 pages * 1000 rows =\n// 20,000 rows is already well past any workable client-side-grouped board, so\n// crossing it means the caller picked the wrong hook - fail loudly instead.\nconst MAX_PAGES = 20;\n\n/**\n * Fetches every page of an entity list and concatenates the result into a\n * single ListResponse whose `meta.total` is the true row count.\n *\n * For board / kanban screens that group the entire result set client-side\n * and therefore cannot use one server-capped page. It fetches page 1 to\n * learn `total_pages`, then fetches the remaining pages in parallel and\n * merges their `$refs`.\n *\n * Prefer `useEntityData` for paginated list screens. This hook holds the\n * full result set in memory and is only appropriate for bounded entities.\n * The caller's `page` / `per_page` in `params` are ignored - this hook owns\n * pagination - while `filters`, `search`, `sort`, and `expand` are honored.\n */\nexport function useAllEntityData(entity: string | undefined, params: ListParams = {}) {\n return useQuery({\n queryKey: [\"entity-data-all\", entity, params],\n enabled: !!entity,\n queryFn: async ({ signal }): Promise<ListResponse> => {\n // count: \"with\" so page 1 carries total_pages - this hook owns full\n // pagination and must know how many pages to fetch. The default\n // window mode omits the count.\n const pageParams: ListParams = { ...params, per_page: ALL_PAGE_SIZE, count: \"with\" };\n const first = await listEntities(entity!, { ...pageParams, page: 1 }, { signal });\n\n const totalPages = first.meta.total_pages ?? 1;\n if (totalPages <= 1) return first;\n\n // Refuse firehose entities: this hook is bounded-entity-only (see\n // MAX_PAGES). Surfacing this as a query error is far better than\n // silently issuing thousands of parallel requests and freezing the tab.\n if (totalPages > MAX_PAGES) {\n throw new Error(\n `useAllEntityData: entity \"${entity}\" spans ${totalPages} pages ` +\n `(limit ${MAX_PAGES}); it is not a bounded entity. Use ` +\n `useEntityData with server-side pagination instead.`,\n );\n }\n\n const rest = await Promise.all(\n Array.from({ length: totalPages - 1 }, (_, i) =>\n listEntities(entity!, { ...pageParams, page: i + 2 }, { signal }),\n ),\n );\n\n const refs = new RefCache();\n refs.merge(first.$refs);\n const data = [...first.data];\n for (const page of rest) {\n data.push(...page.data);\n refs.merge(page.$refs);\n }\n\n // One combined page: total stays the real count; per_page reflects the\n // concatenated length so `data.length === meta.per_page` still holds.\n return {\n data,\n meta: { ...first.meta, page: 1, per_page: data.length, total_pages: 1 },\n $refs: refs.all(),\n };\n },\n });\n}\n","import { useMemo, useCallback } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\n\n/**\n * useColumns manages visible columns via URL `columns` param.\n * Falls back to screen defaults when no URL param is set.\n */\nexport function useColumns(\n availableColumns: string[],\n defaultColumns: string[],\n) {\n const [searchParams, setSearchParams] = useSearchParams();\n\n const visibleColumns = useMemo<string[]>(() => {\n const raw = searchParams.get(\"columns\");\n if (raw) {\n const fromURL = raw.split(\",\").filter(Boolean);\n // Intersect with available pool (drop stale column refs).\n return fromURL.filter((c) => availableColumns.includes(c));\n }\n return defaultColumns.length > 0 ? defaultColumns : availableColumns;\n }, [searchParams, availableColumns, defaultColumns]);\n\n const setColumns = useCallback(\n (cols: string[]) => {\n const next = new URLSearchParams(searchParams);\n if (cols.length === 0 || arraysEqual(cols, defaultColumns)) {\n next.delete(\"columns\"); // reset to defaults\n } else {\n next.set(\"columns\", cols.join(\",\"));\n }\n setSearchParams(next);\n },\n [searchParams, setSearchParams, defaultColumns],\n );\n\n const resetToDefaults = useCallback(() => {\n const next = new URLSearchParams(searchParams);\n next.delete(\"columns\");\n setSearchParams(next);\n }, [searchParams, setSearchParams]);\n\n const showAll = useCallback(() => {\n setColumns(availableColumns);\n }, [availableColumns, setColumns]);\n\n const isColumnVisible = useCallback(\n (col: string) => visibleColumns.includes(col),\n [visibleColumns],\n );\n\n const toggleColumn = useCallback(\n (col: string) => {\n if (visibleColumns.includes(col)) {\n // Don't allow hiding last column.\n if (visibleColumns.length <= 1) return;\n setColumns(visibleColumns.filter((c) => c !== col));\n } else {\n setColumns([...visibleColumns, col]);\n }\n },\n [visibleColumns, setColumns],\n );\n\n return {\n visibleColumns,\n availableColumns,\n setColumns,\n resetToDefaults,\n showAll,\n isColumnVisible,\n toggleColumn,\n };\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((v, i) => v === b[i]);\n}\n","import type { ReactNode } from \"react\";\nimport { useEmbedNavigate } from \"@/embed\";\nimport { Icon, IconBtn } from \"@/components/primitives\";\nimport { useTabStore } from \"@/stores/tabs\";\n\ninterface PageHeaderProps {\n title: string;\n backTo?: string;\n actions?: ReactNode;\n}\n\nexport function PageHeader({ title, backTo, actions }: PageHeaderProps) {\n // Embed-aware back navigation: stays inside the embed boundary.\n const navigate = useEmbedNavigate();\n const { switchTab, findTabByPathname } = useTabStore();\n\n function handleBack() {\n if (!backTo) return;\n const tab = findTabByPathname(backTo);\n if (tab) switchTab(tab.id);\n navigate(backTo);\n }\n\n return (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n marginBottom: 16,\n gap: 12,\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 10, minWidth: 0 }}>\n {backTo && <IconBtn icon={<Icon.back size={14} />} onClick={handleBack} title=\"Back\" />}\n <h1\n style={{\n margin: 0,\n fontSize: 18,\n fontWeight: 600,\n letterSpacing: \"-0.01em\",\n color: \"var(--text)\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {title}\n </h1>\n </div>\n {actions && <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>{actions}</div>}\n </div>\n );\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/list.jsx\n// (BulkBar, lines 404-466). Floating dark pill, count badge in accent, primary\n// inline + ⋯ More overflow that always ends with destructive Delete.\nimport { useRef, useState, type ReactNode } from \"react\";\nimport { Dropdown, Icon, MenuItem } from \"@/components/primitives\";\n\nexport interface BulkActionItem {\n id: string;\n label: string;\n icon?: ReactNode;\n primary?: boolean;\n destructive?: boolean;\n onRun: () => void;\n}\n\nexport interface BulkBarProps {\n count: number;\n actions: BulkActionItem[];\n onClear: () => void;\n /** Optional extra destructive action tail (e.g. delete). */\n onDelete?: () => void;\n onExport?: () => void;\n}\n\nexport function BulkBar({ count, actions, onClear, onDelete, onExport }: BulkBarProps) {\n const [moreOpen, setMoreOpen] = useState(false);\n const moreRef = useRef<HTMLButtonElement | null>(null);\n const primary = actions.filter((a) => a.primary);\n const overflow = actions.filter((a) => !a.primary);\n\n return (\n <div\n style={{\n position: \"fixed\",\n left: \"50%\",\n bottom: \"calc(var(--tabbar-h) + 18px)\",\n transform: \"translateX(-50%)\",\n zIndex: 45,\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n padding: \"6px 6px 6px 14px\",\n background: \"var(--text)\",\n color: \"var(--bg)\",\n borderRadius: 10,\n boxShadow: \"var(--shadow-lg)\",\n animation: \"bulkBarIn .15s ease-out\",\n }}\n >\n <style>{`@keyframes bulkBarIn{from{transform:translateX(-50%) translateY(8px);opacity:0}to{transform:translateX(-50%) translateY(0);opacity:1}}`}</style>\n <span style={{ fontSize: 12.5, fontWeight: 600 }}>\n <span\n className=\"num\"\n style={{\n padding: \"1px 7px\",\n borderRadius: 4,\n background: \"var(--accent)\",\n color: \"var(--primary-fg)\",\n marginRight: 6,\n fontSize: 11.5,\n }}\n >\n {count}\n </span>\n selected\n </span>\n <button\n onClick={onClear}\n title=\"Clear (Esc)\"\n style={{\n background: \"transparent\",\n border: 0,\n color: \"var(--bg)\",\n opacity: 0.6,\n cursor: \"pointer\",\n padding: 4,\n display: \"flex\",\n borderRadius: 4,\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = \"1\";\n e.currentTarget.style.background = \"var(--inverted-overlay)\";\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = \"0.6\";\n e.currentTarget.style.background = \"transparent\";\n }}\n >\n <Icon.x size={12} />\n </button>\n <span\n style={{\n width: 1,\n height: 20,\n background: \"var(--inverted-overlay)\",\n margin: \"0 4px\",\n }}\n />\n {primary.map((a) => (\n <PillBtn key={a.id} icon={a.icon} label={a.label} onClick={a.onRun} />\n ))}\n {(overflow.length > 0 || onDelete || onExport) && (\n <>\n <span style={{ width: 1, height: 20, background: \"var(--inverted-overlay)\" }} />\n <div style={{ position: \"relative\" }}>\n <button\n ref={moreRef}\n onClick={() => setMoreOpen((prev) => !prev)}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n height: 28,\n padding: \"0 10px\",\n borderRadius: 6,\n background: \"transparent\",\n border: 0,\n color: \"var(--bg)\",\n fontSize: 12,\n fontWeight: 500,\n cursor: \"pointer\",\n }}\n onMouseEnter={(e) =>\n (e.currentTarget.style.background = \"var(--inverted-overlay)\")\n }\n onMouseLeave={(e) => (e.currentTarget.style.background = \"transparent\")}\n >\n <Icon.more size={13} /> More\n </button>\n <Dropdown\n open={moreOpen}\n anchor={moreRef.current}\n onClose={() => setMoreOpen(false)}\n width={220}\n align=\"end\"\n >\n {overflow.map((a) => (\n <MenuItem\n key={a.id}\n icon={a.icon}\n danger={a.destructive}\n onClick={() => {\n setMoreOpen(false);\n a.onRun();\n }}\n >\n {a.label}\n </MenuItem>\n ))}\n {(onExport || onDelete) && overflow.length > 0 && <MenuItem divider />}\n {onExport && (\n <MenuItem icon={<Icon.download size={12} />} onClick={onExport}>\n Export selection\n </MenuItem>\n )}\n {onDelete && (\n <MenuItem icon={<Icon.trash size={12} />} danger onClick={onDelete}>\n Delete selected\n </MenuItem>\n )}\n </Dropdown>\n </div>\n </>\n )}\n </div>\n );\n}\n\nfunction PillBtn({\n icon,\n label,\n onClick,\n}: {\n icon?: ReactNode;\n label: string;\n onClick: () => void;\n}) {\n return (\n <button\n onClick={onClick}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n height: 28,\n padding: \"0 10px\",\n borderRadius: 6,\n background: \"transparent\",\n border: 0,\n color: \"var(--bg)\",\n fontSize: 12,\n fontWeight: 500,\n cursor: \"pointer\",\n }}\n onMouseEnter={(e) => (e.currentTarget.style.background = \"var(--inverted-overlay)\")}\n onMouseLeave={(e) => (e.currentTarget.style.background = \"transparent\")}\n >\n {icon}\n {label}\n </button>\n );\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/list.jsx\n// row context menu block (lines 321-336). Floating menu with peek/open/edit/\n// duplicate/copy-link, divider, run-action/change-status, divider, destructive\n// Delete. Hover-action icon arrays are intentionally removed from callers.\nimport type { ReactNode } from \"react\";\nimport { Icon, MenuItem } from \"@/components/primitives\";\n\nexport interface RowMenuEntry {\n id: string;\n label: string;\n icon?: ReactNode;\n destructive?: boolean;\n kbd?: ReactNode;\n onRun: () => void;\n}\n\nexport interface RowMenuProps {\n x: number;\n y: number;\n onClose: () => void;\n onPeek?: () => void;\n onOpen?: () => void;\n onEditInline?: () => void;\n onDuplicate?: () => void;\n onCopyLink?: () => void;\n onDelete?: () => void;\n /** Extra schema-driven entries placed between copy-link and Delete. */\n actions?: RowMenuEntry[];\n}\n\nexport function RowMenu({\n x,\n y,\n onClose,\n onPeek,\n onOpen,\n onEditInline,\n onDuplicate,\n onCopyLink,\n onDelete,\n actions,\n}: RowMenuProps) {\n const wrap = (fn?: () => void) => () => {\n onClose();\n fn && fn();\n };\n const hasBuiltin = onPeek || onOpen || onEditInline || onDuplicate || onCopyLink;\n const hasActions = actions && actions.length > 0;\n return (\n <div\n style={{ position: \"fixed\", top: y, left: x, zIndex: 100 }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n padding: 4,\n boxShadow: \"var(--shadow-lg)\",\n width: 200,\n }}\n >\n {onPeek && (\n <MenuItem icon={<Icon.eye size={13} />} kbd=\"Space\" onClick={wrap(onPeek)}>\n Quick peek\n </MenuItem>\n )}\n {onOpen && (\n <MenuItem icon={<Icon.external size={13} />} kbd=\"⏎\" onClick={wrap(onOpen)}>\n Open\n </MenuItem>\n )}\n {onEditInline && (\n <MenuItem icon={<Icon.edit size={13} />} kbd=\"E\" onClick={wrap(onEditInline)}>\n Edit inline\n </MenuItem>\n )}\n {onDuplicate && (\n <MenuItem icon={<Icon.copy size={13} />} kbd=\"⌘D\" onClick={wrap(onDuplicate)}>\n Duplicate\n </MenuItem>\n )}\n {onCopyLink && (\n <MenuItem icon={<Icon.link size={13} />} onClick={wrap(onCopyLink)}>\n Copy link\n </MenuItem>\n )}\n {hasBuiltin && hasActions && <MenuItem divider />}\n {actions?.map((a) => (\n <MenuItem\n key={a.id}\n icon={a.icon}\n danger={a.destructive}\n kbd={a.kbd}\n onClick={wrap(a.onRun)}\n >\n {a.label}\n </MenuItem>\n ))}\n {onDelete && (\n <>\n {(hasBuiltin || hasActions) && <MenuItem divider />}\n <MenuItem icon={<Icon.trash size={13} />} danger onClick={wrap(onDelete)}>\n Delete\n </MenuItem>\n </>\n )}\n </div>\n <div onClick={onClose} style={{ position: \"fixed\", inset: 0, zIndex: -1 }} />\n </div>\n );\n}\n","// Action slot resolution per docs/plans/2026-04-19-platform-shell-redesign.md\n// section C (\"Action slots — extend Action and Screen\").\n//\n// Inputs: a flat actions map (entity-scoped already merged with globals by the\n// caller), the current Screen DSL, the user's permission predicate, and the\n// current selection size. Output: structured buckets the ListPage and BulkBar\n// render directly. Resolver is permission- and visibility-aware so the UI\n// never has to re-filter; it never reaches into schema, callers stay thin.\n\nimport type { Action, Screen } from \"@/types/schema\";\n\nexport const VISIBILITY_LIST_TOOLBAR = \"list_toolbar\";\nexport const VISIBILITY_LIST_BULK_BAR = \"list_floatbar\";\n\n/** Headed entry: action plus its registry code (entity- or globally-scoped). */\nexport interface ActionEntry {\n code: string;\n action: Action;\n}\n\nexport interface ResolveInput {\n actions: Record<string, Action>;\n screen?: Screen;\n /**\n * Permission gate. Receives the action code (without entity prefix as stored\n * in the actions map). Caller wires this to the existing RBAC helper.\n * Default: allow all (useful in tests; real callers MUST pass this).\n */\n hasPermission?: (code: string) => boolean;\n selectionCount: number;\n /** Maximum primary buttons promoted per slot. Spec locks to 2. */\n maxPrimary?: number;\n}\n\nexport interface ToolbarResolution {\n /** Promoted to header buttons (max `maxPrimary`, default 2). */\n primary: ActionEntry[];\n /** Global (non-bulk) actions to render in Actions ▾ \"Global\" group. */\n global: ActionEntry[];\n /**\n * Bulk-required actions to render in Actions ▾ \"Needs selection\" group.\n * Always present in the menu; UI greys them out when selectionCount === 0.\n */\n needsSelection: ActionEntry[];\n /** Live count badge value next to \"Needs selection\" label. */\n selectionCount: number;\n}\n\nexport interface BulkBarResolution {\n /** All actions for the floating bulk bar (primary inline + overflow). */\n entries: ActionEntry[];\n /** Subset promoted as inline buttons (max `maxPrimary`, default 2). */\n primaryCodes: Set<string>;\n}\n\n/**\n * Resolve actions for the list_toolbar slot.\n *\n * Slot rules (plan section C):\n * - Take actions where Visibility includes \"list_toolbar\" AND user has perm.\n * - If `screen.primary_actions` is set, it OVERRIDES the per-action `primary`\n * flag for promotion (explicit beats implicit). Order preserved, capped at\n * maxPrimary, and bulk-required actions are NEVER promoted to header\n * (they belong in BulkBar / \"Needs selection\" group only).\n * - Else: first maxPrimary actions with `primary: true` AND not\n * bulk-required become header buttons.\n * - Remaining toolbar actions split into Global / Needs selection. The wire\n * format flag that drove this split (`bulk_selection_required`) was removed\n * in unified-toolbar Phase 1; until Task 38 lands the replacement, no\n * action is treated as bulk-required here.\n */\n// TODO(task-38): rewrite with full toolbar pipeline (drop this legacy path).\nfunction isBulkRequired(_action: Action): boolean {\n // Reason: the bulk-required wire flag is gone (plan §1-§2). Task 38 wires\n // the unified toolbar pipeline that derives bulk semantics from handler\n // invoke metadata. For now, return false so toolbar resolution stays\n // behavior-stable for the common (non-bulk) case.\n return false;\n}\n\nexport function resolveToolbarActions(input: ResolveInput): ToolbarResolution {\n const { actions, screen, selectionCount } = input;\n const hasPermission = input.hasPermission ?? (() => true);\n const maxPrimary = input.maxPrimary ?? 2;\n\n const visible: ActionEntry[] = [];\n for (const [code, action] of Object.entries(actions)) {\n const vis = action.visibility ?? [];\n if (!vis.includes(VISIBILITY_LIST_TOOLBAR)) continue;\n if (!hasPermission(code)) continue;\n visible.push({ code, action });\n }\n\n const byCode = new Map(visible.map((e) => [e.code, e]));\n\n // Promotion candidates exclude bulk-required actions: those live in the\n // \"Needs selection\" group / BulkBar, never as global header buttons.\n const promotable = visible.filter((e) => !isBulkRequired(e.action));\n\n let primary: ActionEntry[];\n const explicit = screen?.primary_actions;\n if (explicit && explicit.length > 0) {\n const promotableCodes = new Set(promotable.map((e) => e.code));\n primary = explicit\n .filter((c) => byCode.has(c) && promotableCodes.has(c))\n .slice(0, maxPrimary)\n .map((c) => byCode.get(c)!);\n } else {\n // Action.primary was removed in plan Task 5. Promotion is now driven\n // by action.position (lower = earlier = higher priority). Actions at\n // position <= 10 are promoted as header buttons (step-10 convention).\n primary = promotable\n .filter((e) => (e.action.position ?? 0) <= 10)\n .slice(0, maxPrimary);\n }\n\n // Promoted actions still appear in the dropdown's GLOBAL group (design\n // shows them in both places — toolbar button for quick access, dropdown for\n // discoverability).\n const global: ActionEntry[] = [];\n const needsSelection: ActionEntry[] = [];\n for (const entry of visible) {\n if (isBulkRequired(entry.action)) needsSelection.push(entry);\n else global.push(entry);\n }\n\n return { primary, global, needsSelection, selectionCount };\n}\n\n/**\n * Resolve actions for the list_floatbar slot (floating pill on selection).\n *\n * Slot rules (plan section C):\n * - Take actions where Visibility includes \"list_floatbar\" AND user has perm.\n * - If `screen.primary_bulk_actions` is set, it overrides `primary:` for\n * promotion. Order preserved, capped at maxPrimary.\n * - Else: actions with `primary: true` are promoted (capped).\n * - All resolved actions returned in `entries`; UI splits inline vs ⋯ More\n * using `primaryCodes`.\n */\nexport function resolveBulkBarActions(input: ResolveInput): BulkBarResolution {\n const { actions, screen } = input;\n const hasPermission = input.hasPermission ?? (() => true);\n const maxPrimary = input.maxPrimary ?? 2;\n\n const entries: ActionEntry[] = [];\n for (const [code, action] of Object.entries(actions)) {\n const vis = action.visibility ?? [];\n if (!vis.includes(VISIBILITY_LIST_BULK_BAR)) continue;\n if (!hasPermission(code)) continue;\n entries.push({ code, action });\n }\n\n const byCode = new Map(entries.map((e) => [e.code, e]));\n const explicit = screen?.primary_bulk_actions;\n let primaryCodes: Set<string>;\n if (explicit && explicit.length > 0) {\n primaryCodes = new Set(\n explicit.filter((c) => byCode.has(c)).slice(0, maxPrimary),\n );\n } else {\n // Action.primary removed in Task 5; promote by position <= 10.\n primaryCodes = new Set(\n entries\n .filter((e) => (e.action.position ?? 0) <= 10)\n .slice(0, maxPrimary)\n .map((e) => e.code),\n );\n }\n\n return { entries, primaryCodes };\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/list.jsx\n// v2 (ListPage page-header: 2 primary actions + Actions ▾ + New). The local\n// ACTIONS literal is replaced with schema-resolved data per the Stage-6 plan\n// (T14): screen.primary_actions + action `primary` flag + visibility +\n// bulk_selection_required. Resolution lives in ./resolveActions so it stays\n// unit-testable without mounting the page. Menu groupings (`Global` / `Needs\n// selection` with count badge) kept identical. Table body rendering is\n// intentionally left to the caller so this ports only the scope the plan\n// nailed down; BulkBar/RowMenu/ActionRunner are wired so consumers get\n// end-to-end action flow out of the box.\nimport { useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { Btn, Dropdown, Icon, type IconName, MenuItem } from \"@/components/primitives\";\nimport { DynamicIcon } from \"@/components/shared/DynamicIcon\";\nimport { useActionRunner } from \"@/components/shared/ActionRunner\";\nimport { BulkBar, type BulkActionItem } from \"./BulkBar\";\nimport {\n type ActionEntry,\n resolveBulkBarActions,\n resolveToolbarActions,\n} from \"./resolveActions\";\nimport { loc, type Action, type Screen } from \"@/types/schema\";\n\nexport interface ListPageProps {\n title: string;\n singular: string;\n total: number;\n shown: number;\n /** Selected row count — when >0 BulkBar is rendered. */\n selectionCount: number;\n clearSelection: () => void;\n onNewRecord: () => void;\n /** Screen YAML, used to honor `screen.primary_actions[*]`. */\n screen?: Screen;\n /** All actions available in this list scope (toolbar + bulk). */\n actions: Record<string, Action>;\n /**\n * Permission gate. Receives the action's registry code (without entity\n * prefix). When omitted, all visible actions are shown — callers in\n * production wire this to the existing RBAC helper.\n */\n hasPermission?: (code: string) => boolean;\n /** Executes an action against current selection (or globally). */\n executeAction: (code: string, action: Action, params: Record<string, unknown>) => Promise<unknown>;\n /** Rendered between header and BulkBar (table, footer, drawers). */\n children?: ReactNode;\n /** Optional extra destructive delete shortcut for the bulk bar. */\n onBulkDelete?: () => void;\n onBulkExport?: () => void;\n /** Invoked when the dropdown's built-in \"Export selection (CSV)\" is clicked. */\n onToolbarExport?: () => void;\n /** Invoked when the dropdown's built-in \"Manage actions…\" is clicked. */\n onManageActions?: () => void;\n}\n\nfunction iconFor(a: Action, size = 12): ReactNode {\n const name = a.display?.icon;\n if (!name) return null;\n const K = Icon[name as IconName];\n if (K) return <K size={size} />;\n return <DynamicIcon name={name} size={size} />;\n}\n\n\nexport function ListPage({\n title,\n singular,\n total,\n shown,\n selectionCount,\n clearSelection,\n onNewRecord,\n screen,\n actions,\n hasPermission,\n executeAction,\n children,\n onBulkDelete,\n onBulkExport,\n onToolbarExport,\n onManageActions,\n}: ListPageProps) {\n const actionsBtnRef = useRef<HTMLButtonElement | null>(null);\n const [actionsOpen, setActionsOpen] = useState(false);\n const { run } = useActionRunner();\n\n const toolbar = useMemo(\n () => resolveToolbarActions({ actions, screen, selectionCount, hasPermission }),\n [actions, screen, selectionCount, hasPermission],\n );\n\n const bulk = useMemo(\n () => resolveBulkBarActions({ actions, screen, selectionCount, hasPermission }),\n [actions, screen, selectionCount, hasPermission],\n );\n\n const runAction = (entry: ActionEntry, requiresSelection: boolean) => {\n if (requiresSelection && selectionCount === 0) return;\n const { code, action } = entry;\n run({\n action,\n title: loc(action.display?.name) || code,\n execute: (params) => executeAction(code, action, params),\n });\n };\n\n const primaryBulk = useMemo<BulkActionItem[]>(\n () =>\n bulk.entries.map(({ code, action }) => ({\n id: code,\n label: loc(action.display?.name) || code,\n icon: iconFor(action),\n primary: bulk.primaryCodes.has(code),\n destructive: action.destructive,\n onRun: () => runAction({ code, action }, true),\n })),\n // runAction closes over selectionCount/run; bulk recomputes when those move.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [bulk, selectionCount],\n );\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100%\",\n background: \"var(--bg)\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", padding: \"16px 20px 10px\", gap: 8 }}>\n <div style={{ flex: 1 }}>\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 10 }}>\n <h1\n style={{\n margin: 0,\n fontSize: 20,\n fontWeight: 600,\n letterSpacing: \"-0.012em\",\n color: \"var(--text)\",\n }}\n >\n {title}\n </h1>\n <span className=\"mono num\" style={{ fontSize: 12, color: \"var(--text-3)\" }}>\n {shown} of {total}\n </span>\n </div>\n </div>\n {toolbar.primary.map((entry) => (\n <Btn\n key={entry.code}\n kind=\"default\"\n icon={iconFor(entry.action, 13)}\n onClick={() => runAction(entry, false)}\n >\n {loc(entry.action.display?.name) || entry.code}\n </Btn>\n ))}\n <div style={{ position: \"relative\" }}>\n <Btn\n ref={actionsBtnRef}\n kind=\"default\"\n icon={<Icon.bolt size={13} />}\n iconRight={<Icon.down size={10} style={{ opacity: 0.6 }} />}\n onClick={() => setActionsOpen((prev) => !prev)}\n >\n Actions\n </Btn>\n <Dropdown\n open={actionsOpen}\n anchor={actionsBtnRef.current}\n onClose={() => setActionsOpen(false)}\n width={240}\n align=\"end\"\n >\n {toolbar.global.length > 0 && <SectionLabel>Global</SectionLabel>}\n {toolbar.global.map((entry) => (\n <MenuItem\n key={entry.code}\n icon={iconFor(entry.action)}\n danger={entry.action.destructive}\n onClick={() => {\n setActionsOpen(false);\n runAction(entry, false);\n }}\n >\n {loc(entry.action.display?.name) || entry.code}\n </MenuItem>\n ))}\n {toolbar.needsSelection.length > 0 && (\n <>\n <MenuItem divider />\n <div\n style={{\n padding: \"6px 8px 4px\",\n fontSize: 10,\n color: \"var(--text-3)\",\n fontWeight: 700,\n letterSpacing: \"0.08em\",\n textTransform: \"uppercase\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n }}\n >\n <span>Needs selection</span>\n <span\n className=\"mono\"\n style={{\n padding: \"1px 5px\",\n borderRadius: 3,\n background: \"var(--surface-3)\",\n color: \"var(--text-3)\",\n fontSize: 9.5,\n fontWeight: 700,\n }}\n >\n {selectionCount || 0}\n </span>\n </div>\n {toolbar.needsSelection.map((entry) => (\n <div\n key={entry.code}\n style={{\n opacity: selectionCount === 0 ? 0.5 : 1,\n pointerEvents: selectionCount === 0 ? \"none\" : \"auto\",\n }}\n >\n <MenuItem\n icon={iconFor(entry.action)}\n danger={entry.action.destructive}\n onClick={() => {\n setActionsOpen(false);\n runAction(entry, true);\n }}\n >\n {loc(entry.action.display?.name) || entry.code}\n </MenuItem>\n </div>\n ))}\n </>\n )}\n {(onToolbarExport || onManageActions) && (\n <>\n <MenuItem divider />\n {onToolbarExport && (\n <MenuItem\n icon={<Icon.download size={12} />}\n onClick={() => {\n setActionsOpen(false);\n onToolbarExport();\n }}\n >\n Export selection (CSV)\n </MenuItem>\n )}\n {onManageActions && (\n <MenuItem\n icon={<Icon.settings size={12} />}\n onClick={() => {\n setActionsOpen(false);\n onManageActions();\n }}\n >\n Manage actions…\n </MenuItem>\n )}\n </>\n )}\n </Dropdown>\n </div>\n <div\n style={{ width: 1, height: 20, background: \"var(--divider)\", margin: \"0 2px\" }}\n />\n <Btn kind=\"primary\" icon={<Icon.plus size={13} />} onClick={onNewRecord}>\n New {singular}\n </Btn>\n </div>\n\n {children}\n\n {selectionCount > 0 && (\n <BulkBar\n count={selectionCount}\n actions={primaryBulk}\n onClear={clearSelection}\n onDelete={onBulkDelete}\n onExport={onBulkExport}\n />\n )}\n </div>\n );\n}\n\nfunction SectionLabel({ children }: { children: ReactNode }) {\n return (\n <div\n style={{\n padding: \"6px 8px 4px\",\n fontSize: 10,\n color: \"var(--text-3)\",\n fontWeight: 700,\n letterSpacing: \"0.08em\",\n textTransform: \"uppercase\",\n }}\n >\n {children}\n </div>\n );\n}\n","// Ported from docs/plans/assets/2026-04-19-shell-design/project/decl/list.jsx\n// (inline-edit trigger convention on columns with `inline_edit: true`) and the\n// display-to-input switch from detail.jsx QuickEditCell (lines 460-506, already\n// ported as `QuickEditCell` in components/primitives/Fields.tsx).\n//\n// Responsibilities:\n// - gate entering edit mode via `trigger: dblclick | enter | always`\n// - on Enter → call onSave(v) (async). Optimistic: caller sees value change\n// immediately; on rejection we roll back and show a sonner toast mirroring\n// ActionRunner's error shape.\n// - Esc reverts.\nimport { useEffect, useRef, useState, type CSSProperties, type KeyboardEvent } from \"react\";\nimport { toast } from \"@/lib/toast\";\nimport { BoolToggle, IconBtn, Icon, Select, TextInput, type SelectOption } from \"@/components/primitives\";\n\nexport type InlineEditTrigger = \"dblclick\" | \"enter\" | \"always\";\nexport type InlineEditKind = \"text\" | \"select\" | \"bool\";\n\nexport interface InlineEditCellProps {\n value?: string;\n onSave: (v: string) => Promise<void> | void;\n type?: InlineEditKind;\n options?: SelectOption[];\n /** Drives when a display cell flips to edit. Default `always` (matches QuickEditCell). */\n trigger?: InlineEditTrigger;\n /** Field label — surfaces in the error toast so the user knows which cell rolled back. */\n label?: string;\n placeholder?: string;\n}\n\nconst display: CSSProperties = {\n padding: \"4px 6px\",\n margin: \"-4px -6px\",\n borderRadius: 4,\n cursor: \"text\",\n border: \"1px dashed transparent\",\n minHeight: 24,\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n};\n\nexport function InlineEditCell({\n value,\n onSave,\n type = \"text\",\n options,\n trigger = \"always\",\n label,\n placeholder,\n}: InlineEditCellProps) {\n // Optimistic local copy. `shown` is what the cell displays, `committed`\n // is the last known-good value we roll back to on failure.\n const [committed, setCommitted] = useState<string | undefined>(value);\n const [shown, setShown] = useState<string | undefined>(value);\n const [editing, setEditing] = useState(false);\n const [tmp, setTmp] = useState<string | undefined>(value);\n const cellRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n setCommitted(value);\n setShown(value);\n setTmp(value);\n }, [value]);\n\n const openEdit = () => {\n setTmp(shown);\n setEditing(true);\n };\n\n const commit = async (next: string) => {\n const prior = committed;\n setShown(next);\n setCommitted(next);\n setEditing(false);\n try {\n await onSave(next);\n } catch (err) {\n setShown(prior);\n setCommitted(prior);\n toast.error(`${label ? label + \" \" : \"\"}save failed`, err instanceof Error ? err.message : String(err));\n }\n };\n\n const cancel = () => {\n setTmp(shown);\n setEditing(false);\n };\n\n const saveText = () => {\n if (tmp === undefined) return cancel();\n void commit(tmp);\n };\n\n if (editing) {\n return (\n <div\n style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}\n onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === \"Enter\") saveText();\n if (e.key === \"Escape\") cancel();\n }}\n >\n {type === \"select\" ? (\n <Select\n value={tmp}\n options={options || []}\n onChange={(v) => {\n setTmp(v);\n void commit(v);\n }}\n />\n ) : type === \"bool\" ? (\n <BoolToggle\n value={tmp === \"true\"}\n onChange={(v) => {\n const next = String(v);\n setTmp(next);\n void commit(next);\n }}\n />\n ) : (\n <TextInput value={tmp ?? \"\"} onChange={setTmp} autoFocus placeholder={placeholder} />\n )}\n {type !== \"select\" && type !== \"bool\" && (\n <>\n <IconBtn size={24} icon={<Icon.check size={13} />} onClick={saveText} title=\"Save\" />\n <IconBtn size={24} icon={<Icon.x size={13} />} onClick={cancel} title=\"Cancel\" />\n </>\n )}\n </div>\n );\n }\n\n const onClick = trigger === \"always\" ? openEdit : undefined;\n const onDoubleClick = trigger === \"dblclick\" ? openEdit : undefined;\n const onKeyDown =\n trigger === \"enter\"\n ? (e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key === \"Enter\") openEdit();\n }\n : undefined;\n\n return (\n <div\n ref={cellRef}\n tabIndex={trigger === \"enter\" ? 0 : undefined}\n onClick={onClick}\n onDoubleClick={onDoubleClick}\n onKeyDown={onKeyDown}\n onMouseEnter={(e) => { e.currentTarget.style.borderColor = \"var(--border)\"; }}\n onMouseLeave={(e) => { e.currentTarget.style.borderColor = \"transparent\"; }}\n style={display}\n >\n {shown ?? <span style={{ color: \"var(--text-4)\" }}>Click to edit</span>}\n </div>\n );\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx HomeStub.\n// KPI tiles + Quick access + Recent activity. Mock data inline (matches source's APPDATA usage).\nimport { createElement } from \"react\";\nimport { Icon, type IconName } from \"@/components/primitives/Icons\";\nimport { Pill, type PillTone } from \"@/components/primitives/Pill\";\nimport { SectionCard } from \"@/components/primitives/SectionCard\";\n\nconst ACTIVITIES = [\n { id: \"a1\", title: \"James Gallagher moved to Discovery\", when: \"11:04 AM\" },\n { id: \"a2\", title: \"1Password marked as customer\", when: \"10:42 AM\" },\n { id: \"a3\", title: \"Refill enrichment job completed (32 leads)\", when: \"10:00 AM\" },\n];\n\nexport type HomeStubProps = {\n onOpenNav?: (id: string) => void;\n greeting?: string;\n tenantLabel?: string;\n};\n\nconst KPIS: ReadonlyArray<{ label: string; val: string; delta: string; tone: PillTone }> = [\n { label: \"Open leads\", val: \"127\", delta: \"+12 this week\", tone: \"emerald\" },\n { label: \"Touched today\", val: \"34\", delta: \"+5 vs yday\", tone: \"sky\" },\n { label: \"Pipeline value\", val: \"$2.41M\", delta: \"+$140K\", tone: \"emerald\" },\n { label: \"Jobs running\", val: \"12\", delta: \"2 failed\", tone: \"amber\" },\n];\n\nconst QUICK: ReadonlyArray<[string, string, IconName]> = [\n [\"leads\", \"Leads\", \"leads\"],\n [\"companies\", \"Companies\", \"company\"],\n [\"pipeline\", \"Pipeline\", \"pipeline\"],\n [\"jobs\", \"Jobs\", \"jobs\"],\n [\"activities\", \"Activities\", \"activity\"],\n [\"entities\", \"Entities\", \"entity\"],\n];\n\nexport function HomeStub({\n onOpenNav,\n greeting = \"Good afternoon, Konstantin\",\n tenantLabel = \"Acme · production\",\n}: HomeStubProps) {\n return (\n <div style={{ padding: \"22px 24px\", height: \"100%\", overflowY: \"auto\" }}>\n <div style={{ maxWidth: 1100, margin: \"0 auto\" }}>\n <h1 style={{ fontSize: 22, fontWeight: 600, letterSpacing: \"-0.015em\", margin: 0 }}>{greeting}</h1>\n <p style={{ color: \"var(--text-3)\", marginTop: 4, fontSize: 13 }}>\n Here's what's happening across <strong style={{ color: \"var(--text-2)\" }}>{tenantLabel}</strong> today.\n </p>\n <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(4,1fr)\", gap: 10, margin: \"16px 0\" }}>\n {KPIS.map((k) => (\n <div\n key={k.label}\n style={{\n padding: 14,\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n }}\n >\n <div style={{ fontSize: 11.5, color: \"var(--text-3)\" }}>{k.label}</div>\n <div\n className=\"num\"\n style={{ fontSize: 26, fontWeight: 600, letterSpacing: \"-0.02em\", margin: \"4px 0\" }}\n >\n {k.val}\n </div>\n <Pill tone={k.tone} size=\"xs\">\n {k.delta}\n </Pill>\n </div>\n ))}\n </div>\n <div style={{ display: \"grid\", gridTemplateColumns: \"2fr 1fr\", gap: 14 }}>\n <SectionCard title=\"Quick access\" icon={<Icon.bolt size={14} />}>\n <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(3,1fr)\", gap: 8 }}>\n {QUICK.map(([id, label, iconName]) => {\n const IconComp = Icon[iconName];\n return (\n <button\n key={id}\n onClick={() => onOpenNav?.(id)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"10px 12px\",\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 7,\n cursor: \"pointer\",\n textAlign: \"left\",\n }}\n >\n {IconComp && createElement(IconComp, { size: 14, style: { color: \"var(--accent)\" } })}\n <span style={{ fontSize: 12.5, fontWeight: 500 }}>{label}</span>\n <span style={{ flex: 1 }} />\n <Icon.right size={12} style={{ color: \"var(--text-4)\" }} />\n </button>\n );\n })}\n </div>\n </SectionCard>\n <SectionCard title=\"Recent activity\" icon={<Icon.activity size={14} />}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n {ACTIVITIES.slice(0, 3).map((a) => (\n <div key={a.id} style={{ fontSize: 12, color: \"var(--text-2)\" }}>\n <div style={{ fontWeight: 500, color: \"var(--text)\" }}>{a.title}</div>\n <div className=\"mono\" style={{ fontSize: 10.5, color: \"var(--text-4)\" }}>\n {a.when}\n </div>\n </div>\n ))}\n </div>\n </SectionCard>\n </div>\n </div>\n </div>\n );\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx PipelineStub.\n// Kanban columns by stage. Mock data inline (matches source's APPDATA usage).\nimport { Pill, type PillTone } from \"@/components/primitives/Pill\";\n\ntype Lead = {\n id: string;\n contact: string;\n company: string;\n pipeline: string;\n priority?: string;\n score?: number;\n};\n\nconst PIPE: Record<string, { color: PillTone }> = {\n \"1-New\": { color: \"slate\" },\n \"2-Contacted\": { color: \"sky\" },\n \"3-Qualified\": { color: \"violet\" },\n \"4-Proposal\": { color: \"amber\" },\n \"5-Won\": { color: \"emerald\" },\n \"6-Lost\": { color: \"rose\" },\n};\n\nconst PRIO: Record<string, PillTone> = { P0: \"rose\", P1: \"amber\", P2: \"sky\", P3: \"slate\" };\n\nconst LEADS: Lead[] = [\n { id: \"l1\", contact: \"James Gallagher\", company: \"Northwind\", pipeline: \"3-Qualified\", priority: \"P1\", score: 82 },\n { id: \"l2\", contact: \"Priya Shah\", company: \"Acme\", pipeline: \"1-New\", priority: \"P2\", score: 41 },\n { id: \"l3\", contact: \"Hugo Bernal\", company: \"Globex\", pipeline: \"2-Contacted\", priority: \"P1\", score: 67 },\n { id: \"l4\", contact: \"Mei Tanaka\", company: \"Initech\", pipeline: \"4-Proposal\", priority: \"P0\", score: 91 },\n { id: \"l5\", contact: \"Owen Reilly\", company: \"Umbrella\", pipeline: \"5-Won\", priority: \"P1\", score: 88 },\n { id: \"l6\", contact: \"Sara Jensen\", company: \"Hooli\", pipeline: \"1-New\", priority: \"P3\", score: 22 },\n { id: \"l7\", contact: \"Diego Rivas\", company: \"Pied Piper\", pipeline: \"6-Lost\", priority: \"P2\", score: 35 },\n { id: \"l8\", contact: \"Ana Costa\", company: \"Soylent\", pipeline: \"3-Qualified\", priority: \"P2\", score: 58 },\n];\n\nexport function PipelineStub() {\n const stages = Object.entries(PIPE).map(([k, v]) => ({\n key: k,\n color: v.color,\n leads: LEADS.filter((l) => l.pipeline === k),\n }));\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100%\",\n padding: \"14px 20px\",\n background: \"var(--bg)\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 10, marginBottom: 12 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600, letterSpacing: \"-0.012em\" }}>Pipeline</h1>\n <span className=\"mono num\" style={{ fontSize: 12, color: \"var(--text-3)\" }}>\n {LEADS.length} leads · $2.4M pipeline\n </span>\n </div>\n <div style={{ flex: 1, overflow: \"auto\", display: \"flex\", gap: 10, paddingBottom: 10 }}>\n {stages.map((s) => (\n <div\n key={s.key}\n style={{\n minWidth: 240,\n width: 240,\n background: \"var(--surface-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <div\n style={{\n padding: \"8px 10px\",\n borderBottom: \"1px solid var(--divider)\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n }}\n >\n <Pill tone={s.color} size=\"xs\">\n {s.key}\n </Pill>\n <div style={{ flex: 1 }} />\n <span className=\"mono num\" style={{ fontSize: 11, color: \"var(--text-3)\" }}>\n {s.leads.length}\n </span>\n </div>\n <div\n style={{\n padding: 6,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 6,\n flex: 1,\n overflowY: \"auto\",\n }}\n >\n {s.leads.map((l) => (\n <div\n key={l.id}\n style={{\n padding: \"8px 10px\",\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 6,\n fontSize: 12,\n cursor: \"pointer\",\n }}\n >\n <div style={{ fontWeight: 500, color: \"var(--text)\", marginBottom: 2 }}>{l.contact}</div>\n <div style={{ fontSize: 11, color: \"var(--text-3)\" }}>{l.company}</div>\n <div style={{ display: \"flex\", gap: 4, marginTop: 6, flexWrap: \"wrap\" }}>\n {l.priority && l.priority !== \"-\" && (\n <Pill tone={PRIO[l.priority] ?? \"slate\"} size=\"xs\" dot>\n {l.priority}\n </Pill>\n )}\n {l.score !== undefined && (\n <Pill tone=\"slate\" size=\"xs\">\n Score {l.score}\n </Pill>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n","// Ported 1:1 from docs/plans/assets/2026-04-19-shell-design/project/decl/app.jsx JobsStub.\n// Jobs table with progress bars + tone-coded status pills. Mock data inline (matches source).\nimport { IconBtn } from \"@/components/primitives/IconBtn\";\nimport { Icon } from \"@/components/primitives/Icons\";\nimport { Pill, type PillTone } from \"@/components/primitives/Pill\";\n\ntype JobStatus = \"running\" | \"success\" | \"queued\" | \"failed\";\n\ntype Job = {\n id: string;\n name: string;\n status: JobStatus;\n started: string;\n progress: number;\n worker: string;\n took?: string;\n};\n\nconst JOBS: Job[] = [\n { id: \"job_91a4\", name: \"reparse_leads_with_llm\", status: \"running\", started: \"11:24:03\", progress: 68, worker: \"wk-eu-03\" },\n { id: \"job_91a3\", name: \"daily_enrichment_sync\", status: \"success\", started: \"10:00:00\", progress: 100, worker: \"wk-us-01\", took: \"4m 12s\" },\n { id: \"job_91a2\", name: \"export_leads_csv\", status: \"queued\", started: \"—\", progress: 0, worker: \"—\" },\n { id: \"job_91a1\", name: \"webhook_retry_batch\", status: \"failed\", started: \"09:12:14\", progress: 44, worker: \"wk-eu-02\", took: \"55s\" },\n { id: \"job_91a0\", name: \"scheduled_followups\", status: \"success\", started: \"08:00:01\", progress: 100, worker: \"wk-us-01\", took: \"2m 02s\" },\n];\n\nconst TONE: Record<JobStatus, PillTone> = {\n running: \"sky\",\n success: \"emerald\",\n queued: \"slate\",\n failed: \"rose\",\n};\n\nconst HEADERS = [\"ID\", \"Name\", \"Status\", \"Progress\", \"Started\", \"Duration\", \"Worker\", \"\"] as const;\n\nexport function JobsStub() {\n return (\n <div style={{ padding: \"14px 20px\", height: \"100%\", overflowY: \"auto\" }}>\n <div style={{ display: \"flex\", alignItems: \"baseline\", gap: 10, marginBottom: 12 }}>\n <h1 style={{ margin: 0, fontSize: 20, fontWeight: 600 }}>Jobs</h1>\n <span className=\"mono num\" style={{ fontSize: 12, color: \"var(--text-3)\" }}>\n Live · 12 in flight\n </span>\n </div>\n <div\n style={{\n background: \"var(--surface)\",\n border: \"1px solid var(--border)\",\n borderRadius: 8,\n overflow: \"hidden\",\n }}\n >\n <table style={{ width: \"100%\", fontSize: 12.5, borderCollapse: \"separate\", borderSpacing: 0 }}>\n <thead>\n <tr style={{ height: 32, background: \"var(--surface-2)\", color: \"var(--text-3)\" }}>\n {HEADERS.map((h, i) => (\n <th\n key={h || `_h${i}`}\n style={{\n padding: \"0 10px\",\n textAlign: \"left\",\n fontSize: 10.5,\n fontWeight: 600,\n letterSpacing: \"0.04em\",\n textTransform: \"uppercase\",\n borderBottom: \"1px solid var(--border)\",\n }}\n >\n {h}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {JOBS.map((j) => (\n <tr key={j.id} style={{ height: 32 }}>\n <td\n style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}\n className=\"mono\"\n >\n <a href=\"#\" style={{ color: \"var(--accent)\", textDecoration: \"none\" }}>\n {j.id}\n </a>\n </td>\n <td\n style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}\n className=\"mono\"\n >\n {j.name}\n </td>\n <td style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}>\n <Pill tone={TONE[j.status]} size=\"xs\" dot>\n {j.status}\n </Pill>\n </td>\n <td\n style={{\n padding: \"0 10px\",\n borderBottom: \"1px solid var(--divider)\",\n width: 160,\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n <span\n style={{\n flex: 1,\n height: 4,\n background: \"var(--surface-3)\",\n borderRadius: 999,\n overflow: \"hidden\",\n }}\n >\n <span\n style={{\n display: \"block\",\n height: \"100%\",\n width: `${j.progress}%`,\n background: `var(--pill-${TONE[j.status]})`,\n }}\n />\n </span>\n <span className=\"mono num\" style={{ fontSize: 11, color: \"var(--text-2)\" }}>\n {j.progress}%\n </span>\n </div>\n </td>\n <td\n style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}\n className=\"mono num\"\n >\n {j.started}\n </td>\n <td\n style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}\n className=\"mono\"\n >\n {j.took || \"—\"}\n </td>\n <td\n style={{ padding: \"0 10px\", borderBottom: \"1px solid var(--divider)\" }}\n className=\"mono\"\n >\n {j.worker}\n </td>\n <td\n style={{\n padding: \"0 10px\",\n borderBottom: \"1px solid var(--divider)\",\n textAlign: \"right\",\n }}\n >\n <IconBtn size={22} icon={<Icon.more size={12} />} />\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n","import { useState } from \"react\";\nimport { useImpersonation } from \"@/hooks/useImpersonation\";\nimport { useParam } from \"@/hooks/useParams\";\n\ninterface Props {\n /** Open state controlled by the caller. */\n open: boolean;\n /** Target user the admin is about to impersonate. */\n targetUserId: string;\n targetUserLabel?: string;\n /** Closes the modal without dispatching. Caller resets state. */\n onClose: () => void;\n /**\n * Called after the start mutation succeeds. Useful for closing the\n * modal and navigating; auth state is rehydrated by the hook itself.\n */\n onStarted?: () => void;\n}\n\n/**\n * Reason modal for impersonation.start. Enforces a minimum reason\n * length (server-side param impersonation.require_reason_min_length,\n * default 10) before submitting. The \"Duration\" input is optional and\n * is capped server-side by impersonation.max_duration.\n */\nexport function ImpersonationStartModal({ open, targetUserId, targetUserLabel, onClose, onStarted }: Props) {\n const { start, isStarting } = useImpersonation();\n // Live tenant param so a non-default minimum (e.g. 25) is reflected in\n // both the hint label and the submit gate. Falls back to the platform\n // default (10) while the param query is loading or on resolver error;\n // the server still enforces the actual minimum, so a stale UX value\n // can never let a too-short reason through.\n const { value: minLen } = useParam<number>(\"impersonation.require_reason_min_length\", 10);\n const [reason, setReason] = useState(\"\");\n const [duration, setDuration] = useState<string>(\"\");\n\n if (!open) return null;\n\n const trimmedReason = reason.trim();\n const reasonOk = trimmedReason.length >= minLen;\n\n const submit = () => {\n if (!reasonOk) return;\n start(\n {\n targetUserId,\n reason: trimmedReason,\n durationMinutes: duration ? Number(duration) : undefined,\n },\n {\n onSuccess: () => {\n setReason(\"\");\n setDuration(\"\");\n onStarted?.();\n onClose();\n },\n },\n );\n };\n\n return (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"impersonation-modal-title\"\n data-testid=\"impersonation-start-modal\"\n style={{\n position: \"fixed\",\n inset: 0,\n background: \"var(--scrim-strong)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 100,\n }}\n onClick={onClose}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n style={{\n background: \"var(--surface)\",\n color: \"var(--text)\",\n minWidth: 420,\n maxWidth: 520,\n padding: 20,\n borderRadius: 8,\n boxShadow: \"0 12px 40px rgba(0,0,0,0.4)\",\n }}\n >\n <h2 id=\"impersonation-modal-title\" style={{ marginTop: 0, fontSize: 16 }}>\n Impersonate {targetUserLabel || targetUserId}\n </h2>\n <p style={{ fontSize: 13, color: \"var(--text-3)\" }}>\n You will see exactly what {targetUserLabel || \"this user\"} sees and act on their behalf.\n Destructive actions are blocked by default. Sessions are time-boxed and the target is\n notified when they end. State a clear reason - it is recorded on the audit trail.\n </p>\n <label htmlFor=\"imp-reason\" style={{ display: \"block\", fontSize: 12, marginTop: 12 }}>\n Reason (min {minLen} characters)\n </label>\n <textarea\n id=\"imp-reason\"\n data-testid=\"impersonation-reason-input\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n style={{\n width: \"100%\",\n marginTop: 4,\n padding: 8,\n background: \"var(--surface-2)\",\n color: \"var(--text)\",\n border: \"1px solid var(--border)\",\n borderRadius: 4,\n fontSize: 13,\n fontFamily: \"inherit\",\n boxSizing: \"border-box\",\n }}\n />\n <label htmlFor=\"imp-duration\" style={{ display: \"block\", fontSize: 12, marginTop: 12 }}>\n Duration in minutes (optional, capped server-side)\n </label>\n <input\n id=\"imp-duration\"\n data-testid=\"impersonation-duration-input\"\n type=\"number\"\n min={1}\n value={duration}\n onChange={(e) => setDuration(e.target.value)}\n style={{\n marginTop: 4,\n padding: \"6px 8px\",\n width: 120,\n background: \"var(--surface-2)\",\n color: \"var(--text)\",\n border: \"1px solid var(--border)\",\n borderRadius: 4,\n fontSize: 13,\n }}\n />\n <div style={{ display: \"flex\", gap: 8, justifyContent: \"flex-end\", marginTop: 20 }}>\n <button\n onClick={onClose}\n data-testid=\"impersonation-cancel-btn\"\n style={{\n padding: \"6px 14px\",\n background: \"transparent\",\n color: \"var(--text-2)\",\n border: \"1px solid var(--border)\",\n borderRadius: 4,\n cursor: \"pointer\",\n fontSize: 13,\n }}\n >\n Cancel\n </button>\n <button\n onClick={submit}\n disabled={!reasonOk || isStarting}\n data-testid=\"impersonation-submit-btn\"\n style={{\n padding: \"6px 14px\",\n background: \"var(--accent)\",\n color: \"white\",\n border: 0,\n borderRadius: 4,\n cursor: !reasonOk || isStarting ? \"not-allowed\" : \"pointer\",\n fontSize: 13,\n opacity: !reasonOk || isStarting ? 0.5 : 1,\n }}\n >\n {isStarting ? \"Starting…\" : \"Start session\"}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","// AgentVisualHints subscribes to ui_hint frames from the\n// active conversation and applies the §8 visual effects:\n// navigate, highlight (4s row glow), open_tab, attention (field\n// pulse). All hints are advisory - the underlying state change\n// already happened via the API; the hint just nudges the user's\n// eye.\n//\n// Gate via agents.allow_visual_hints (default true). When the\n// param is false the bootstrap omits ui_hint frames at the wire\n// level so this component sees nothing.\nimport { useEffect } from \"react\";\nimport type { AgentTurnState } from \"@/hooks/useAgentConversation\";\n\nexport interface AgentVisualHintsProps {\n turn: AgentTurnState;\n // navigate is the platform's existing routing primitive; the\n // host app passes its router's push.\n navigate?: (path: string) => void;\n}\n\nexport function AgentVisualHints({ turn, navigate }: AgentVisualHintsProps) {\n useEffect(() => {\n for (const hint of turn.hints) {\n switch (hint.kind) {\n case \"navigate\":\n if (navigate && hint.target && typeof hint.target === \"object\") {\n const t = hint.target as { entity?: string; view?: string; pk?: string };\n const path = t.entity\n ? `/data/${t.entity}${t.view === \"list\" ? \"\" : t.pk ? `/${t.pk}` : \"\"}`\n : null;\n if (path) navigate(path);\n }\n break;\n case \"highlight\":\n if (hint.target && typeof hint.target === \"object\") {\n const t = hint.target as { entity?: string; ids?: string[] };\n const ids = t.ids ?? [];\n for (const id of ids) {\n const el = document.querySelector<HTMLElement>(\n `[data-row-id=\"${id}\"]`,\n );\n if (!el) continue;\n el.classList.add(\"agent-highlight\");\n const ttl = hint.ttlMs ?? 4000;\n window.setTimeout(() => el.classList.remove(\"agent-highlight\"), ttl);\n }\n }\n break;\n case \"attention\":\n if (hint.target && typeof hint.target === \"object\") {\n const t = hint.target as { field?: string };\n if (t.field) {\n const el = document.querySelector<HTMLElement>(\n `[data-field=\"${t.field}\"]`,\n );\n if (el) {\n el.classList.add(\"agent-attention\");\n const ttl = hint.ttlMs ?? 2000;\n window.setTimeout(() => el.classList.remove(\"agent-attention\"), ttl);\n }\n }\n }\n break;\n // open_tab requires browser-tab control; v1 leaves it to\n // the consumer app to wire (host knows its routing model).\n }\n }\n }, [turn.hints, navigate]);\n\n return null;\n}\n","// AgentConversationDrawer is the left-edge listing of recent\n// conversations (§\"Task 15\"). Click restores; the chat panel\n// loads history + subscribes to SSE.\nimport { useEffect } from \"react\";\nimport { Btn, Icon } from \"@/components/primitives\";\nimport type { AgentConversation } from \"@/api/agents\";\n\nexport interface AgentConversationDrawerProps {\n open: boolean;\n onClose: () => void;\n conversations: AgentConversation[];\n activeId?: string;\n onSelect: (id: string) => void;\n onNew: () => void;\n onRefresh: () => Promise<void>;\n}\n\nexport function AgentConversationDrawer({\n open,\n onClose,\n conversations,\n activeId,\n onSelect,\n onNew,\n onRefresh,\n}: AgentConversationDrawerProps) {\n useEffect(() => {\n if (open) void onRefresh();\n }, [open, onRefresh]);\n\n if (!open) return null;\n return (\n <aside className=\"fixed left-0 top-0 z-40 flex h-full w-[320px] flex-col border-r border-border bg-card text-foreground shadow-2xl\">\n <header className=\"flex items-center justify-between border-b border-border px-4 py-3\">\n <span className=\"font-semibold\">Conversations</span>\n <Btn kind=\"ghost\" onClick={onNew}>\n <Icon.plus /> New\n </Btn>\n </header>\n <div className=\"flex-1 overflow-y-auto\">\n {conversations.length === 0 && (\n <div className=\"p-4 text-sm text-muted-foreground\">No conversations yet.</div>\n )}\n {conversations.map((c) => (\n <button\n key={c.id}\n onClick={() => onSelect(c.id)}\n className={`block w-full px-4 py-3 text-left border-b border-divider hover:bg-surface ${\n c.id === activeId ? \"bg-surface\" : \"\"\n }`}\n >\n <div className=\"text-sm font-medium truncate\">\n {c.title || c.context ? formatContext(c.context) : \"Untitled\"}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n {c.agent_code} · {c.status}\n {c.token_usage?.cost_cents ? ` · $${(c.token_usage.cost_cents / 100).toFixed(2)}` : \"\"}\n </div>\n </button>\n ))}\n </div>\n <footer className=\"border-t border-border p-2\">\n <Btn kind=\"ghost\" onClick={onClose}>\n Close\n </Btn>\n </footer>\n </aside>\n );\n}\n\nfunction formatContext(ctx?: Record<string, unknown>): string {\n if (!ctx) return \"\";\n const e = ctx.entity ? String(ctx.entity) : \"\";\n const id = ctx.pk ? `:${String(ctx.pk).slice(0, 6)}` : \"\";\n return e ? `${e}${id}` : \"\";\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,GAAe,GAA2C;CACxE,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAI,EAAM,KAAK;CAErB,OADK,IAEH,EAAE,WAAW,GAAG,KAChB,EAAE,WAAW,SAAS,KACtB,EAAE,WAAW,UAAU,KACvB,EAAE,WAAW,aAAa,IALb;AAOjB;;;ACfA,IAAM,KAAkB;AAyBxB,SAAS,GAAQ,GAAmC;CAElD,OADI,CAAC,KAAS,EAAM,WAAW,IAAU,CAAC,IACnC,EAAM,KAAK,OAAO;EACvB,IAAI,EAAE;EACN,MAAM,EAAE,OAAQ,EAAE,OAAoB,KAAA;EACtC,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;CAC1B,EAAE;AACJ;AAEA,SAAgB,GAAK,EAAE,eAAY,WAAQ,eAAY,WAAQ,WAAQ,UAAO,eAAuB;CACnG,IAAM,IAAQ,GAAQ,CAAK,GACrB,IAAY,GAAU,YAAY,IAClC,IAAY,GAAe,GAAU,QAAQ,IAAI,GAAU,WAAW,KAAA;CAC5E,OACE,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,YAAY;GACZ,OAAO;GACP,SAAS;GACT,eAAe;GACf,YAAY;GACZ,SAAS;GACT,KAAK;EACP;YAVF;GAYE,kBAAC,OAAD;IACE,OAAO;KACL,OAAO;KACP,QAAQ;KACR,cAAc;KAGd,YAAY,IACR,gBACA;KACJ,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,cAAc;KACd,WAAW,IACP,SACA;KACJ,UAAU;IACZ;IACA,OAAO,IAAY,GAAG,EAAU,WAAW;cAE1C,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,OAAO;KACP,QAAQ;KACR,OAAO;MAAE,SAAS;MAAS,WAAW;KAAU;IACjD,CAAA,IACC,IACF,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,YAAY;MAAK,eAAe;KAAS;eACnE,EAAU,OAAO,CAAC,EAAE,YAAY;IAC7B,CAAA,IACJ;GACD,CAAA;GACJ,EAAM,KAAK,MACV,kBAAC,IAAD;IAAwB,MAAM;IAAI,QAAQ,MAAe,EAAG;IAAY;GAAS,GAAhE,EAAG,EAA6D,CAClF;GACD,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;GAC1B,kBAAC,UAAD;IACE,SAAS;IACT,OAAM;IACN,OAAO,GAAe,EAAK;cAE1B,EAAK,WAAW,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GACpC,CAAA;GACR,kBAAC,UAAD;IACE,SAAS;IACT,OAAM;IACN,OAAO;KACL,GAAG,GAAe,EAAK;KACvB,YAAY,IAAS,kBAAkB;KACvC,OAAO,IAAS,mBAAmB;IACrC;cAEC,EAAK,WAAW,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GACpC,CAAA;EACL;;AAET;AAEA,SAAS,GAAW,EAAE,SAAM,WAAQ,aAAiF;CACnH,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,IAAW,EAAK,OAAO,EAAK,EAAK,QAAQ,KAAA;CAC/C,AAAI,EAAK,QAAQ,CAAC,KAKhB,QAAQ,MAAM,qBAAqB,EAAK,GAAG,uBAAuB,EAAK,KAAK,EAAE;CAEhF,IAAM,IAAK,IAAS,uBAAuB,IAAQ,sBAAsB,eACnE,IAAQ,KAAU,IAAQ,mBAAmB;CACnD,OACE,kBAAC,UAAD;EACE,eAAe,EAAO,EAAK,EAAE;EAC7B,OAAO,EAAK;EACZ,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;GACL,OAAO;GACP,QAAQ;GACR,cAAc;GACd,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,YAAY;GACZ;GACA,QAAQ;GACR,QAAQ;GACR,UAAU;EACZ;YAjBF,CAmBG,KAAY,kBAAC,GAAD,EAAU,MAAM,GAAK,CAAA,GACjC,KACC,kBAAC,QAAD,EACE,OAAO;GACL,UAAU;GACV,MAAM;GACN,KAAK;GACL,QAAQ;GACR,OAAO;GACP,YAAY;GACZ,cAAc;EAChB,EACD,CAAA,CAEG;;AAEZ;AAEA,SAAS,GAAe,GAAiC;CACvD,OAAO;EACL,OAAO;EACP,QAAQ;EACR,cAAc;EACd,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACZ,OAAO;EACP,QAAQ;EACR,QAAQ;CACV;AACF;;;AC7JA,SAAgB,GAAU,EAAE,YAAS,SAAM,WAAQ,eAA4B;CAC7E,IAAI,CAAC,GAAe,CAAO,GAAG,OAAO,IAAW,kBAAA,GAAA,EAAA,UAAG,EAAW,CAAA,IAAI;CAClE,IAAM,IAAI,MAAW,KAAQ,KAAK,IAAI;CACtC,OACE,kBAAC,OAAD;EACE,KAAK,EAAS,KAAK;EACnB,KAAI;EACJ,OAAO;EACP,QAAQ;EACR,OAAO;GAAE,SAAS;GAAS,cAAc;GAAG,WAAW;EAAU;CAClE,CAAA;AAEL;;;ACIA,IAAM,KAAkB,CAAC,UAAU,SAAS;AAE5C,SAAS,GAAW,GAAuB;CACzC,OAAO,EAAI,aAAa;AAC1B;AAEA,SAAgB,GACd,GACA,GACA,GACA,GACU;CACV,IAAI,MAAU,OAAO,OAAO,EAAK,OAAO,EAAU,EAAE,KAAK,MAAM,EAAE,EAAE;CAEnE,IAAM,IAAK,KAAY;CACvB,IAAI,CAAC,GAAI,OAAO,CAAC;CACjB,IAAM,IAAM,EAAK,WAAW,MAAM,EAAE,OAAO,CAAE;CAS7C,OARI,MAAQ,KAAW,CAAC,IAEpB,MAAU,YACL,GAAW,EAAK,EAAI,IAAI,CAAC,EAAK,GAAK,EAAE,IAAI,CAAC,IAE/C,MAAU,WACL,EAAK,QAAQ,GAAG,MAAM,MAAM,KAAO,GAAW,CAAC,CAAC,EAAE,KAAK,MAAM,EAAE,EAAE,IAEnE,EAAK,MAAM,IAAM,CAAC,EAAE,OAAO,EAAU,EAAE,KAAK,MAAM,EAAE,EAAE;AAC/D;AAEA,IAAI,KAAiC,MACjC,KAA+B;AAEnC,SAAS,GAAe,GAAkB,GAAwB;CAChE,OAAO,cAAc,EAAS,IAAI,EAAO;AAC3C;AAEA,SAAS,KAA4B;CAEnC,OADI,CAAC,MAAmB,CAAC,KAAsB,OACxC,GAAe,IAAiB,EAAa;AACtD;AAOA,SAAgB,GAAsB,GAAkB,GAA+B;CACrF,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAI;EACF,IAAM,IAAM,aAAa,QAAQ,GAAe,GAAU,CAAM,CAAC;EACjE,IAAI,CAAC,GAAK,OAAO;EACjB,IAAM,IAAS,KAAK,MAAM,CAAG;EAC7B,IAAI,CAAC,KAAU,CAAC,MAAM,QAAQ,EAAO,IAAI,GAAG,OAAO;EACnD,IAAM,IAAU,EAAO,KAAmB,MAAM,MAAM,EAAE,OAAO,EAAO,WAAW,GAC3E,IAAW,GAAQ;EAEzB,IAAI,OAAO,KAAa,YAAY,EAAS,WAAW,GAAG,KAAK,CAAC,EAAS,WAAW,IAAI,GACvF,OAAO,GAAG,IAAW,OAAO,GAAQ,UAAW,WAAW,EAAO,SAAS;CAE9E,QAAQ,CAER;CACA,OAAO;AACT;AAEA,SAAS,GAAQ,GAAiB,GAA4B;CAC5D,IAAI,OAAO,SAAW,KAAa;CAGnC,IAAM,IAAU,EAAmB,SAAS,EAAE,aACxC,IAAM,GAAW;CAClB,OACL;MAAI,CAAC,GAAS;GACZ,IAAI;IACF,aAAa,WAAW,CAAG;GAC7B,QAAQ,CAER;GACA;EACF;EACA,IAAI;GACF,aAAa,QAAQ,GAAK,KAAK,UAAU;IAAE;IAAM;GAAY,CAAC,CAAC;EACjE,QAAQ,CAER;CALA;AAMF;AAEA,SAAS,KAAwE;CAE/E,IADI,OAAO,SAAW,OAClB,CAAC,EAAmB,SAAS,EAAE,aAAa,OAAO;CACvD,IAAM,IAAM,GAAW;CACvB,IAAI,CAAC,GAAK,OAAO;CACjB,IAAI;EACF,IAAM,IAAM,aAAa,QAAQ,CAAG;EACpC,IAAI,CAAC,GAAK,OAAO;EACjB,IAAM,IAAS,KAAK,MAAM,CAAG;EAC7B,IAAI,KAAU,MAAM,QAAQ,EAAO,IAAI,GAAG,OAAO;CACnD,QAAQ,CAER;CACA,OAAO;AACT;AAEA,IAAa,KAAc,IAAkB,GAAK,OAAS;CACzD,MAAM,CAAC;CACP,aAAa;CAEb,UAAU,MAAQ;EAChB,IAAI,GAAgB,SAAS,EAAI,QAAQ,GACvC,OAAO;EAGT,IAAM,IAAQ,EAAI,GAEZ,IAAW,EAAM,KAAK,MAAM,MAAM,EAAE,aAAa,EAAI,QAAQ;EACnE,IAAI,GAGF,OAFA,EAAI,EAAE,aAAa,EAAS,GAAG,CAAC,GAChC,GAAQ,EAAM,MAAM,EAAS,EAAE,GACxB,EAAS;EAGlB,IAAM,IAAK,OAAO,WAAW,GACvB,IAAkB;GAAE,GAAG;GAAK;EAAG,GACjC,IAAO,CAAC,GAAG,EAAM,MAAM,CAAM,GAE3B,IAAM,EAAmB,SAAS,EAAE;EAC1C,IAAI,IAAM,KAAK,EAAK,SAAS,GAAK;GAEhC,IAAM,IAAW,EAAK,WACnB,MAAM,EAAE,OAAO,KAAM,EAAE,aAAa,MAAS,CAAC,EAAE,KACnD;GACA,AAAI,MAAa,OAAI,IAAO,EAAK,QAAQ,GAAG,MAAM,MAAM,CAAQ;EAClE;EAIA,OAFA,EAAI;GAAE;GAAM,aAAa;EAAG,CAAC,GAC7B,GAAQ,GAAM,CAAE,GACT;CACT;CAEA,WAAW,MAAO;EAChB,IAAM,IAAQ,EAAI,GACZ,IAAM,EAAM,KAAK,WAAW,MAAM,EAAE,OAAO,CAAE;EAC/C,MAAQ,MAEA,EAAM,KAAK,GACd,YAGL,EAAM,KAAK,WAAW,KAE1B,EAAI,EAAE,UAAU,CAAC,CAAE,CAAC;CACtB;CAEA,YAAY,MAAQ;EAClB,IAAM,IAAQ,EAAI,GACZ,IAAY,IAAI,IAAI,CAAG;EAC7B,IAAI,EAAU,SAAS,GACrB,OAAO,EAAM,KAAK,MAAM,MAAM,EAAE,OAAO,EAAM,WAAW,KAAK;EAG/D,IAAM,IAAY,EAAM,KAAK,WAAW,MAAM,EAAE,OAAO,EAAM,WAAW,GAClE,IAAY,EAAM,KAAK,QAAQ,MAAM,EAAE,EAAU,IAAI,EAAE,EAAE,KAAK,GAAW,CAAC,EAAE,GAC9E,IAAa,EAAM;EAevB,OAbI,KAAc,CAAC,EAAU,MAAM,MAAM,EAAE,OAAO,CAAU,IAC1D,AAIE,IAJE,EAAU,WAAW,IACV,OAGA,EADG,KAAK,IAAI,KAAK,IAAI,GAAW,CAAC,GAAG,EAAU,SAAS,CAC7C,GAAS,KAEzB,EAAU,WAAW,MAC9B,IAAa,OAGf,EAAI;GAAE,MAAM;GAAW,aAAa;EAAW,CAAC,GAChD,GAAQ,GAAW,CAAU,GACtB,EAAU,MAAM,MAAM,EAAE,OAAO,CAAU,KAAK;CACvD;CAEA,YAAY,MAAO;EAEjB,AADA,EAAI,EAAE,aAAa,EAAG,CAAC,GACvB,GAAQ,EAAI,EAAE,MAAM,CAAE;CACxB;CAEA,cAAc,GAAU,MAAW;EACjC,GAAK,MAAM;GACT,IAAM,IAAS,EAAE,KAAK,WAAW,MAAM,EAAE,OAAO,CAAQ,GAClD,IAAS,EAAE,KAAK,WAAW,MAAM,EAAE,OAAO,CAAM;GACtD,IAAI,MAAW,MAAM,MAAW,MAAM,MAAW,GAAQ,OAAO;GAChE,IAAM,IAAO,CAAC,GAAG,EAAE,IAAI,GACjB,CAAC,KAAS,EAAK,OAAO,GAAQ,CAAC;GAErC,OADA,EAAK,OAAO,GAAQ,GAAG,CAAK,GACrB,EAAE,QAAK;EAChB,CAAC;CACH;CAEA,oBAAoB,MACX,EAAI,EAAE,KAAK,MAAM,MAAM,EAAE,aAAa,CAAQ;CAGvD,iBAAiB,GAAI,MAAU;EAC7B,GAAK,OAAO,EACV,MAAM,EAAE,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK;GAAE,GAAG;GAAG;EAAM,IAAI,CAAE,EAC7D,EAAE;CACJ;CAEA,kBAAkB,GAAI,MAAW;EAC/B,GAAK,OAAO,EACV,MAAM,EAAE,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK;GAAE,GAAG;GAAG;EAAO,IAAI,CAAE,EAC9D,EAAE;CACJ;CAEA,WAAW,GAAI,MAAU;EAIvB,AAHA,GAAK,OAAO,EACV,MAAM,EAAE,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK;GAAE,GAAG;GAAG;EAAM,IAAI,CAAE,EAC7D,EAAE,GACF,GAAQ,EAAI,EAAE,MAAM,EAAI,EAAE,WAAW;CACvC;CAEA,UAAU,GAAI,MAAS;EAIrB,AAHA,GAAK,OAAO,EACV,MAAM,EAAE,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK;GAAE,GAAG;GAAG;EAAK,IAAI,CAAE,EAC5D,EAAE,GACF,GAAQ,EAAI,EAAE,MAAM,EAAI,EAAE,WAAW;CACvC;CAEA,YAAY,GAAI,MAAU;EAIxB,AAHA,GAAK,OAAO,EACV,MAAM,EAAE,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK;GAAE,GAAG;GAAG,GAAG;EAAM,IAAI,CAAE,EAChE,EAAE,GACF,GAAQ,EAAI,EAAE,MAAM,EAAI,EAAE,WAAW;CACvC;CAEA,WAAW,GAAU,MAAW;EAE9B,AADA,KAAkB,GAClB,KAAgB;CAClB;CAEA,eAAe;EACb,IAAM,IAAO,GAAc;EAC3B,AAAI,KAAM,EAAI;GAAE,MAAM,EAAK;GAAM,aAAa,EAAK;EAAY,CAAC;CAClE;AACF,EAAE,GCpQW,KAA+C,QAG/C,KAAoB,SACpB,KAA4B,iBAC5B,KAAoB,SAOpB,KAAmB;CAC9B;CACA;CACA;;AAEF;AAgCA,SAAgB,GAAiB,GAA+C;CAC9E,IAAI,CAAC,GAAK,OAAO;CACjB,IAAI;CACJ,IAAI;EACF,IAAM,IAAI,IAAI,CAAG;CACnB,QAAQ;EACN,OAAO;CACT;CAMA,OAHI,EAAI,WAAW,SAAe,OAG3B,MAAQ,EAAI,SAAS,EAAI,SAAS;AAC3C;AAGA,SAAS,GAAW,GAAuC;CACzD,OAAO,MAAQ,WAAW,MAAQ,SAAS,IAAM;AACnD;AAGA,SAAS,GAAoB,GAAyC;CACpE,OAAO,MAAQ,cAAc,cAAc;AAC7C;AASA,SAAgB,GACd,GACa;CACb,IAAM,IACJ,OAAO,KAAU,WAAW,IAAI,gBAAgB,CAAK,IAAI;CAE3D,OAAO;EACL,SAFc,EAAO,IAAI,EAAiB,MAAM;EAGhD,cAAc,GAAiB,EAAO,IAAI,EAAyB,CAAC;EACpE,OAAO,GAAW,EAAO,IAAI,EAAiB,CAAC;EAC/C,gBAAgB,GAAoB,EAAO,IAAA,KAAmB,CAAC;CACjE;AACF;AAgBA,SAAgB,GAAkB,GAAa,GAA8B;CAC3E,IAAI,CAAC,EAAQ,SAAS,OAAO;CAK7B,IACM,IAAa,uBAAuB,KAAK,CAAG,GAC9C;CACJ,IAAI;EACF,IAAS,IAAI,IAAI,GAAK,sBAAgB;CACxC,QAAQ;EAEN,OAAO;CACT;CAEA,IAAM,IAAS,EAAO;CAatB,OAXI,EAAQ,WAAS,EAAO,IAAI,IAAmB,GAAG,GAClD,EAAQ,gBACV,EAAO,IAAI,IAA2B,EAAQ,YAAY,GAExD,EAAQ,SAAO,EAAO,IAAI,IAAmB,EAAQ,KAAK,GAG9D,EAAO,IAAA,OAAqB,EAAQ,cAAc,GAE9C,IAAmB,EAAO,SAAS,IAEhC,GAAG,EAAO,WAAW,EAAO,SAAS,EAAO;AACrD;;;ACtIA,SAAgB,GAAgB,GAAyB;CAIvD,AADA,EAAgB;EADW,GADX,EAAmB,SACL;EAAS;CACvB,CAAI,GACpB,EAAmB,SAAS,EAAE,SAAM,CAAC;AACvC;AAYA,SAAgB,GAAyB,GAA0C;CACjF,IAAM,IAAS,GAAiB,CAAK;CAIrC,OAHI,CAAC,EAAO,WAAW,EAAO,UAAU,OAAa,MAErD,GAAgB,EAAO,KAAK,GACrB;AACT;AASA,SAAgB,KAAuC;CAErD,OADI,OAAO,SAAW,MAAoB,KACnC,GAAyB,OAAO,SAAS,MAAM;AACxD;;;ACTA,IAAM,KAAe,GAAiC;CAPpD,SAAS;CACT,cAAc;CACd,OAAO;CACP,gBAAgB;CAChB,oBAAoB,MAAQ;AAGwB,CAAc;AAGpE,SAAgB,GAAoB,GAAwC;CAC1E,OAAO;EACL,SAAS,EAAO;EAChB,cAAc,EAAO;EACrB,OAAO,EAAO;EACd,gBAAgB,EAAO;EACvB,oBAAoB,MAAQ,GAAsB,GAAK,CAAM;CAC/D;AACF;AAmBA,SAAgB,GAAc,EAAE,aAAU,YAA6B;CACrE,IAAM,IAAQ,QAIL,GAAoB,GAFzB,MACC,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS,GACV,CAAC,GAClD,CAAC,CAAK,CAAC;CAEV,OACE,kBAAC,GAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AAE1E;AAQA,SAAgB,KAA8B;CAC5C,OAAO,GAAW,EAAY;AAChC;;;AC9CA,SAAgB,GACd,GACA,GACuB;CAGvB,IAAI,EAAM,WAAW,GACnB,OAAO,EAAE,MAAM,WAAW;CAK5B,IAAM,IAAO,EAAM;CACnB,IAAI,OAAO,KAAS,aAAY,GAC9B,OAAO,EAAE,MAAM,WAAW;CAE5B,IAAM,IAAW;CAkBjB,OAjBI,EAAS,WAAA,qBAGT,OAAO,EAAS,YAAa,YAG7B,OAAO,EAAS,QAAS,WACpB,EAAE,MAAM,WAAW,IAMxB,EAAS,aAAA,IAIN;EAAE,MAAM;EAAS,SAAS;CAAyB,IAHjD;EAAE,MAAM;EAAqB,UAAU,EAAS;CAAS;AAIpE;AAyBA,SAAgB,GACd,GACA,GAC+B;CAC/B,QAAQ,MAA8B;EACpC,IAAM,IAAa,GAAuB,GAAO,CAAY;EAE7D,IAAI,EAAW,SAAS,YAEtB;EAGF,IAAI,EAAW,SAAS,qBAAqB;GAC3C,QAAQ,KACN,sGAEK,OAAO,EAAW,QAAQ,EAAE,0FAEnC;GACA;EACF;EAEA,IAAM,EAAE,eAAY;EACpB,QAAQ,EAAQ,MAAhB;GACE,KAAK,GAAoB;IACvB,EAAS,OAAO,EAAQ,OAA+B;IACvD;GACF,KAAK,GAAoB;IACvB,EAAS,WAAW,EAAQ,OAA+B;IAC3D;GACF,KAAK,GAAoB;IACvB,EAAS,QAAQ,EAAQ,OAA+B;IACxD;GACF,SAGE;EACJ;CACF;AACF;;;ACtHA,IAAM,KAAwB;AAY9B,SAAgB,KAAqB;CACnC,IAAM,IAAQ,GAAS,GACjB,IAAW,GAAY,GACvB,IAAU,GAAc,MAAM,EAAE,OAAO,GACvC,CAAC,GAAO,KAAY,EAAyB,SAAS,GAKtD,IAAc,EAAO,EAAK;CAuGhC,OArGA,QAAgB;EACd,IAAI,CAAC,EAAM,SAAS;EAKpB,GAAmB,EAAM,YAAY;EAMrC,IAAM,IAAyB,SAAsC;GACnE,GACE,EAAM,cACN,GAAoB,gBACpB,EAAE,QAAQ,2BAA2B,CACvC;EACF,CAAC,GAKK,IAAuB,YAAY;GACnC,OAAY,SAChB;MAAY,UAAU;IACtB,IAAI;KACF,IAAM,IAAO,MAAM,EAAM;KAEzB,AADA,EAAQ,GAAM,EAAK,UAAU,KAAA,CAAS,GACtC,EAAS,eAAe;IAC1B,QAAQ;KAIN,EAAS,OAAO;IAClB;GAVsB;EAWxB,GAWM,IAAW,EAAM,eACnB,GAA6B,EAAM,cAAc;GAC/C,SAXU,MAAkC;IAIhD,AADA,GAAc,CAAO,GAChB,EAAY,WACf,EAA0B;GAE9B;GAKM,aAAa,MAAY;IAIvB,EAAS,EAAM,kBAAkB,EAAQ,KAAK,CAAC;GACjD;GACA,UAAU,MAAY;IAGpB,GAAgB,EAAQ,KAAK;GAC/B;EACF,CAAC,IACD;EAQJ,AANI,KACF,OAAO,iBAAiB,WAAW,CAAQ,GAK7C,GAAa,EAAM,cAAc,GAAoB,OAAO,CAAC,CAAC;EAG9D,IAAM,IAAgB,OAAO,iBAAiB;GAC5C,AAAK,EAAY,WACf,EAAS,OAAO;EAEpB,GAAG,EAAqB;EAExB,aAAa;GAKX,AAJI,KACF,OAAO,oBAAoB,WAAW,CAAQ,GAEhD,OAAO,aAAa,CAAa,GACjC,GAAgC,CAAsB;EACxD;CAIF,GAAG;EAAC;EAAO;EAAU;CAAO,CAAC,GAEzB,CAAC,EAAM,WAAW,MAAU,UAAgB,OAW9C,kBAAC,OAAD;EACE,MAAK;EACL,WAAU;YAEV,kBAAC,KAAD,EAAA,UAAG,6FAGA,CAAA;CACA,CAAA;AAET;;;AC/JA,IAAM,KAAwB;AAO9B,SAAgB,KAAsB;CACpC,IAAM,IAAQ,GAAS,GACjB,IAAS,EAAM,WAAW,EAAM,iBAAiB;CAsCvD,OApCA,QAAgB;EAGd,IAFI,CAAC,KACD,OAAO,SAAW,OAClB,OAAO,iBAAmB,KAAa;EAE3C,IAAM,IAAO,SAAS,eAAe,EAAqB;EAC1D,IAAI,CAAC,GAAM;EAEX,IAAI,IAA2B,MAC3B,IAAa,IAEX,UAAa;GACjB,IAAY;GAIZ,IAAM,IAAS,EAAK;GAChB,MAAW,MACf,IAAa,GACb,GAAa,EAAM,cAAc,GAAoB,SAAS,EAAE,UAAO,CAAC;EAC1E,GAEM,IAAW,IAAI,qBAAqB;GAGpC,MAAc,SAClB,IAAY,OAAO,sBAAsB,CAAI;EAC/C,CAAC;EAGD,OAFA,EAAS,QAAQ,CAAI,SAER;GAEX,AADA,EAAS,WAAW,GAChB,MAAc,QAAM,OAAO,qBAAqB,CAAS;EAC/D;CACF,GAAG,CAAC,GAAQ,EAAM,YAAY,CAAC,GAExB;AACT;;;ACtCA,IAAa,KAAqB,IAAyB,OAAS;CAClE,SAAS;CACT,WAAW,MACT,GAAK,MAAO,EAAE,YAAY,IAAQ,IAAI,EAAE,SAAS,EAAM,CAAE;AAC7D,EAAE;AAYF,SAAgB,GAAoB,GAAgB,GAAuB;CACzE,IAAM,IAAW,IAAoB,MAAM,EAAE,QAAQ;CAKrD,AAJA,QAAgB;EACT,KACL,EAAS,CAAK;CAChB,GAAG;EAAC;EAAQ;EAAO;CAAQ,CAAC,GAC5B,QAAgB;EACT,OACL,aAAa,EAAS,EAAK;CAC7B,GAAG,CAAC,GAAQ,CAAQ,CAAC;AACvB;;;ACvBA,SAAgB,KAAqB;CACnC,IAAM,IAAQ,GAAS,GACjB,IAAU,IAAoB,MAAM,EAAE,OAAO,GAC7C,IAAS,EAAM,WAAW,EAAM,iBAAiB;CASvD,OAPA,QAAgB;EACT,KACL,GAAa,EAAM,cAAc,GAAoB,cAAc,EACjE,OAAO,EACT,CAAC;CACH,GAAG;EAAC;EAAQ;EAAS,EAAM;CAAY,CAAC,GAEjC;AACT;;;ACtBA,SAAS,GAAa,GAAuB;CAC3C,IAAM,IAAO,EACV,QAAQ,QAAQ,EAAE,EAClB,MAAM,GAAG,EACT,KAAK,MACJ,EAAE,WAAW,GAAG,IAAI,UAAU,EAAE,QAAQ,uBAAuB,MAAM,CACvE;CACF,OAAW,OAAO,MAAM,EAAK,KAAK,GAAG,IAAI,GAAG;AAC9C;AA6CA,SAAgB,GACd,GACA,GAC2B;CAC3B,IAAM,IAAO,EAAS,QAAQ,QAAQ,EAAE,KAAK;CAE7C,IAAI,CAAC,GACH,OAAO;EAAE,SAAS;EAAO,SAAS,EAAE,OAAO,EAAS;CAAE;CAGxD,IAAI,GACA,GACA,GACA;CACJ,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAO,GAC5C,MAAO,SACP,GAAa,EAAO,KAAK,EAAE,KAAK,CAAI,GACzC,IAAI,EAAO,MAAM,SAAS,IAAI,GACvB,MACH,IAAW,GACX,IAAe;MAEZ;EAEL,AADA,IAAQ,GACR,IAAY;EACZ;CACF;CAGF,IAAM,IAAU,KAAS,GACnB,IAAc,IAAQ,IAAY;CACxC,IAAI,CAAC,KAAW,CAAC,GAEf,OAAO;EAAE,SAAS;EAAO,SAAS,EAAE,OAAO,EAAS;CAAE;CAGxD,IAAM,IAKF;EACF,OAAO,EAAQ;EACf,YAAY;CACd;CAKA,IAJI,EAAQ,WACV,EAAQ,SAAS,EAAQ,SAGvB,EAAQ,MAAM,SAAS,IAAI,GAAG;EAChC,IAAM,IAAc,EAAK,MAAM,EAAK,YAAY,GAAG,IAAI,CAAC;EACxD,AAAI,MACF,EAAQ,WAAW;CAEvB;CACA,OAAO;EAAE,SAAS;EAAM,YAAY;EAAa,YAAY,EAAQ;EAAM;CAAQ;AACrF;AAMA,SAAgB,GACd,GACA,GACwB;CACxB,OAAO,GAAuB,GAAU,CAAO,EAAE;AACnD;;;AC3GA,IAAa,KAAwB,IAA4B,GAAK,OAAS;CAC7E,mBAAmB;CACnB,oBAAoB,MAAQ,EAAI,EAAE,mBAAmB,EAAI,CAAC;CAC1D,yBAAyB;EACvB,IAAM,IAAM,EAAI,EAAE;EAGlB,AADA,EAAI,EAAE,mBAAmB,KAAK,CAAC,GAC/B,IAAM;CACR;CACA,wBAAwB,EAAI,EAAE,mBAAmB,KAAK,CAAC;AACzD,EAAE;;;ACbF,SAAS,GAAO,GAAoB;CAClC,IAAM,IAAI,EAAG,OAAO,MAAM;CAC1B,OAAO,MAAM,KAAK,IAAK,EAAG,MAAM,GAAG,CAAC;AACtC;AAgBA,SAAgB,KAGH;CACX,IAAM,IAAW,GAAY,GACvB,IAAW,GAAY,GACvB,IAAQ,GAAS,GACjB,EAAE,MAAM,MAAW,GAAU;CAEnC,OAAO,GACJ,GAAY,MAAoC;EAE/C,IAAI,CAAC,EAAM,SAET,OADA,EAAS,GAAI,CAAI,GACV;EAGT,IAAM,IAAU,GAAQ,SAClB,IAAS,GAAuB,GAAO,CAAE,GAAG,CAAO,GACnD,IACJ,GAAmB,SAAS,EAAE,WAAW,GAAO,CAAE,MAAM,EAAS;EAOnE,IAAI,KAAW,CAAC,EAAO,SAKrB,OAJA,GAAa,EAAM,cAAc,GAAoB,kBAAkB;GACrE,OAAO,GAAO,CAAE;GAChB,QAAQ;EACV,CAAC,GACM;EAeT,IAJiB,EAAM,mBAAmB,aAI5B;GACZ,IAAM,UAAoC;IACxC,GACE,EAAM,cACN,GAAoB,qBACpB,EAAO,OACT;GACF;GAMA,OALI,KACF,GAAsB,SAAS,EAAE,kBAAkB,CAAqB,GACjE,OAET,EAAsB,GACf;EACT;EAOA,IAAM,UAA8B;GAClC,EAAS,EAAM,kBAAkB,CAAE,GAAG,CAAI;EAC5C;EAcA,OANI,KACF,GAAsB,SAAS,EAAE,kBAAkB,CAAe,GAC3D,OAGT,EAAgB,GACT;CACT,GACA;EAAC;EAAU,EAAS;EAAU;EAAO,GAAQ;CAAO,CACtD;AACF;AAYA,SAAgB,KAAiE;CAC/E,IAAM,IAAiB,GAAuB;CAC9C,OAAO,GACJ,GAAY,MAA2B;EACtC,EAAe,GAAI,CAAI;CACzB,GACA,CAAC,CAAc,CACjB;AACF;AASA,SAAgB,KAGN;CACR,IAAM,IAAQ,GAAS;CACvB,OAAO,GACJ,GAAO,MAAW;EACZ,EAAM,WACX,GAAa,EAAM,cAAc,GAAoB,kBAAkB;GACrE;GACA;EACF,CAAC;CACH,GACA,CAAC,EAAM,SAAS,EAAM,YAAY,CACpC;AACF;;;ACrLA,SAAgB,GAAG,GAAG,GAAsB;CAC1C,OAAO,GAAQ,GAAK,CAAM,CAAC;AAC7B;AAaA,SAAgB,GAAgB,GAMpB;CACV,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW;AAC1E;AC4BA,SAAS,GAAS,GAAsC;CACtD,OACE,aAAgB,qBACf,OAAO,cAAgB,OAAe,aAAgB;AAE3D;AAQA,SAAS,GAAW,GAAqC;CACvD,KAAK,IAAM,KAAQ,EAAM,aAAa,GACpC,IAAI,GAAS,CAAI,GAAG,OAAO;CAE7B,OAAO;AACT;AAGA,SAAS,GAAW,GAAkC;CAMpD,OALI,aAAkB,oBAEb,EAAO,aAAa,MAAM,IAIjC,EAAO,MAAM,WACb,EAAO,aAAa,MAAM,KAC1B,EAAO,aAAa,YAAY;AAEpC;AAYA,SAAgB,GACd,GACA,IAA4D,OAAO,UACpD;CAKf,IAHI,EAAM,oBAGN,GAAgB,CAAK,GAAG,OAAO;CAEnC,IAAM,IAAS,GAAW,CAAK;CAG/B,IAFI,CAAC,KAED,EAAO,aAAA,4BAA6B,GAAG,OAAO;CAElD,IAAM,IAAU,GAAW,CAAM;CACjC,IAAI,CAAC,GAAS,OAAO;CAErB,IAAM,KAAU,EAAO,aAAa,QAAQ,KAAK,IAAI,KAAK;CAG1D,IAFI,KAAU,MAAW,WAErB,EAAO,aAAa,UAAU,GAAG,OAAO;CAE5C,IAAM,IAAM,EAAO,aAAa,KAAK,KAAK;CAC1C,IAAI,uBAAuB,KAAK,CAAG,GAAG,OAAO;CAI7C,IAAI;CACJ,IAAI;EACF,IAAM,IAAI,IAAI,GAAS,GAAG,EAAI,SAAS,EAAI,WAAW,EAAI,QAAQ;CACpE,QAAQ;EACN,OAAO;CACT;CAuBA,OArBI,EAAI,aAAa,WAAW,EAAI,aAAa,YAW7C,EAAI,WAAW,EAAI,UAGrB,EAAI,aAAa,EAAI,YACrB,EAAI,WAAW,EAAI,UACnB,EAAI,SAAS,KAEN,OAGF,EAAI,WAAW,EAAI,SAAS,EAAI;AACzC;AAiCA,SAAgB,GACd,GACA,IAA0B,OAAO,UAClB;CAMf,IAJI,EAAM,YAAY,gBAClB,CAAC,EAAM,gBAGP,EAAM,cAAc,EAAM,mBAAmB,QAAQ,EAAM,YAAY,MACzE,OAAO;CAET,IAAI;CACJ,IAAI;EACF,IAAM,IAAI,IAAI,EAAM,YAAY,GAAG;CACrC,QAAQ;EACN,OAAO;CACT;CAGA,OAFI,EAAI,aAAa,WAAW,EAAI,aAAa,YAC7C,EAAI,WAAW,EAAI,SAAe,OAC/B,EAAI,WAAW,EAAI,SAAS,EAAI;AACzC;AAeA,SAAgB,KAAiC;CAC/C,IAAM,IAAc,GAAe,GAE7B,IAAc,EAAO,CAAW;CAGtC,AAFA,EAAY,UAAU,GAEtB,QAAgB;EAGd,IAAM,KAAgB,MAA4B;GAChD,IAAM,IAAS,GAA4B,CAAK;GAC5C,KAAU,SACd,EAAM,eAAe,GACrB,EAAY,QAAQ,CAAM;EAC5B;EACA,SAAS,iBAAiB,SAAS,GAAc,EAAE,SAAS,GAAK,CAAC;EAQlE,IAAM,IAAc,OAA2C,YACzD,KAAc,MAAmC;GACrD,IAAM,IAAS,GAA8B,CAAK;GAC9C,KAAU,QACT,EAAM,eACX,EAAM,eAAe,GACrB,EAAY,QAAQ,CAAM;EAC5B;EAGA,OAFA,GAAY,iBAAiB,YAAY,CAAU,SAEtC;GAEX,AADA,SAAS,oBAAoB,SAAS,GAAc,EAAE,SAAS,GAAK,CAAC,GACrE,GAAY,oBAAoB,YAAY,CAAU;EACxD;CACF,GAAG,CAAC,CAAC;AACP;;;AC5OA,SAAgB,KAAwB;CACtC,IAAM,IAAQ,GAAS,GACjB,IAAW,GAAY,GACvB,EAAE,MAAM,MAAW,GAAU,GAG7B,IAAe,EAAsB,IAAI,GAEzC,IAAS,EAAM,WAAW,EAAM,mBAAmB;CAmBzD,OAjBA,QAAgB;EACd,IAAI,CAAC,GAAQ;EACb,IAAM,IAAM,GAAG,EAAS,WAAW,EAAS;EAC5C,IAAI,EAAa,YAAY,GAAK;EAClC,EAAa,UAAU;EAEvB,IAAM,IAAU,GACd,EAAS,UACT,GAAQ,OACV;EACA,GACE,EAAM,cACN,GAAoB,WACpB,CACF;CACF,GAAG;EAAC;EAAQ,EAAS;EAAU,EAAS;EAAQ,EAAM;EAAc,GAAQ;CAAO,CAAC,GAE7E;AACT;;;ACvCA,SAAgB,GAAc,EAC5B,SACA,iBACA,UACA,gBACA,kBAAe,WACf,cACA,iBAAc,MACO;CAErB,OADK,IAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,eAAe,EAAa,EAAK;EACjC,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;EACZ;YAbF,CAeE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAa,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAG;aAAnF;IACE,kBAAC,QAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY,IAAc,uBAAuB;MACjD,OAAO,IAAc,kBAAkB;MACvC,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,YAAY;KACd;eAEe,EAAd,IAAe,EAAK,QAAsB,EAAK,MAAjC,EAAY,MAAM,GAAK,CAA0B;IAC5D,CAAA;IACN,kBAAC,OAAD;KAAK,OAAO;MAAE,MAAM;MAAG,UAAU;KAAE;eAAnC,CACE,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,OAAO;MAAc;gBAAI;KAAW,CAAA,GACjF,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAM,OAAO;OAAiB,WAAW;MAAE;gBAChE;KACE,CAAA,CACF;;IACL,kBAAC,GAAD;KAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;KAAG,eAAe,EAAa,EAAK;IAAI,CAAA;GACvE;MACL,kBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,WAAW;IACX,SAAS;IACT,YAAY;IACZ,KAAK;IACL,YAAY;GACd;aARF;IAUE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;IAC1B,kBAAC,GAAD;KAAK,eAAe,EAAa,EAAK;eAAG;IAAW,CAAA;IACpD,kBAAC,GAAD;KACE,MAAK;KACL,QAAQ;KACR,eAAe;MAEb,AADA,EAAU,GACV,EAAa,EAAK;KACpB;eAEC;IACE,CAAA;GACF;IACF;GACL,EAAA,CAAA,IA5Ec;AA8EpB;;;AC5EA,SAAgB,KAAoB;CAClC,IAAM,IAAoB,IAAuB,MAAM,EAAE,iBAAiB,GACpE,IAAoB,IAAuB,MAAM,EAAE,iBAAiB,GACpE,IAAmB,IAAuB,MAAM,EAAE,gBAAgB;CAIxE,OAFI,MAAsB,OAAa,OAGrC,kBAAC,IAAD;EACE,MAAA;EACA,eAAe,MAAS;GAMtB,AAAK,KAAM,EAAiB;EAC9B;EACA,OAAM;EACN,aAAY;EACZ,cAAa;EACb,aAAA;EACA,iBAAiB;GACf,EAAkB;EACpB;CACD,CAAA;AAEL;;;ACpCA,GAA4B;;;ACH5B,SAAgB,KAAiB;CAC/B,IAAM,IAAW,GAAY,GACvB,EAAE,MAAM,MAAW,GAAU,GAC7B,EAAE,YAAS,cAAW,yBAAsB,GAAY,GACxD,IAAQ,GAAS,GACjB,IAAsB,GAAuB;CAoEnD,OAlEoB,GACjB,GAAkB,MAA8B;EAO/C,IAAI,EAAM,SAAS;GAIjB,EAHe,GAAM,SACjB,GAAG,IAAW,EAAK,OAAO,WAAW,GAAG,IAAI,KAAK,MAAM,EAAK,WAC5D,GACwB,EAAE,SAAS,GAAM,QAAQ,CAAC;GACtD;EACF;EAGA,IAAM,IAAW,EAAkB,CAAQ;EAC3C,IAAI,GAAU;GAEZ,AADA,EAAU,EAAS,EAAE,GACrB,EACE;IAAE;IAAU,QAAQ,GAAM;GAAO,GACjC,EAAE,SAAS,GAAM,QAAQ,CAC3B;GACA;EACF;EAUA,IAAM,IAAW,GAAM,QACnB,KAAA,IACA,GAAmB,GAAU,GAAQ,SAAS,GAAQ,QAAQ,GAC5D,IAAQ,GAAM,SAAS,GAAU,SAAS,GAC1C,IAAO,GAAM,QAAQ,GAAU;EAUrC,AAPA,EAAQ;GACN;GACA,QAAQ,GAAM,UAAU;GACxB;GACA;GACA,UAAU;EACZ,CAAC,GACD,EACE;GAAE;GAAU,QAAQ,GAAM;EAAO,GACjC,EAAE,SAAS,GAAM,QAAQ,CAC3B;CACF,GACA;EACE;EACA;EACA;EACA;EACA,GAAQ;EACR,GAAQ;EACR,EAAM;EACN;CACF,CAGK;AACT;;;AC1EA,SAAgB,GAAQ,EAAE,iBAA4B;CACpD,IAAM,EAAE,MAAM,MAAW,GAAU,GAC7B,IAAW,GAAY,GACvB,IAAc,GAAe,GAC7B,IAAc,GAAoB,MAAM,EAAE,OAAO,GACjD,IAAgB,GAAoB,MAAM,EAAE,GAAG,GAC/C,IAAY,MAAgB,aAC5B,CAAC,GAAU,KAAe,EAAkC,CAAC,CAAC,GAE9D,IAAW,GAAQ,UACnB,IAAU,GAAU,YAAY,IAChC,IAAU,GAAU,UACpB,IAAe,GAAU,iBAAiB,QAE1C,IAAW,QAAc,GAAc,GAAQ,cAAc,CAAC,GAAG,CAAU,GAAG,CAAC,GAAQ,YAAY,CAAU,CAAC,GAE9G,KAAY,MACX,IACE,EAAS,aAAa,KAAS,EAAS,SAAS,WAAW,IAAQ,GAAG,IAD3D,IAIf,KAAY,GAAe,MAAgC;EAC/D,IAAI,CAAC,EAAK,QAAQ;EAClB,IAAM,IAAS,GAAQ,QAAQ,EAAK;EAC/B,KACL,EAAY,EAAO,OAAO;GACxB,OAAO,EAAI,EAAK,IAAI;GACpB,MAAM,EAAK;EACb,CAAC;CAOH;CAEA,OACE,kBAAC,SAAD;EACE,OAAO;GACL,UAAU;GACV,YAAY;GACZ,aAAa;GACb,SAAS;GACT,eAAe;GACf,UAAU;EACZ;YARF;GAUE,kBAAC,OAAD;IACE,OAAO;KACL,QAAQ;KACR,SAAS;KACT,YAAY;KACZ,SAAS,IAAY,IAAI;KACzB,gBAAgB,IAAY,WAAW;KACvC,cAAc;KACd,KAAK;IACP;cAEE,IA4BE,IACF,kBAAC,IAAD;KAAoB;KAAS,MAAM;IAAK,CAAA,IACtC,IACF,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,YAAY;MAAK,OAAO;KAAgB;eAClE,EAAQ,OAAO,CAAC;IACb,CAAA,IACJ,OAjCF,kBAAA,GAAA,EAAA,UAAA;KACE,kBAAC,IAAD;MAAoB;MAAS,MAAM;KAAK,CAAA;KACxC,kBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,eAAe;OACf,OAAO;MACT;gBAEC;KACE,CAAA;KACL,kBAAC,QAAD;MACE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,OAAO;OACP,UAAU;OACV,YAAY;OACZ,cAAc;OACd,eAAe;OACf,eAAe;MACjB;gBAEC;KACG,CAAA;IACN,EAAA,CAAA;GAQD,CAAA;GAEL,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,WAAW;KAAQ,SAAS,IAAY,YAAY;IAAU;cAClF,EAAS,KAAK,GAAK,MAClB,kBAAC,OAAD;KAAa,OAAO,EAAE,cAAc,GAAG;eAAvC,CACG,CAAC,KAAa,EAAI,SACjB,kBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACP,eAAe;OACf,SAAS;OACT,eAAe;MACjB;gBAEC,EAAI;KACF,CAAA,GAEN,EAAI,MAAM,KAAK,GAAI,MAAM;MACxB,IAAM,IAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAG,UAAU,EAAI,EAAG,IAAI,KAC3C,IAAS,EAAG,SAAS,GAAQ,QAAQ,EAAG,UAAU,KAAA,GAClD,IAAS,EAAS,GAAQ,KAAK,GAC/B,IAAU,CAAC,EAAE,EAAG,YAAY,EAAG,SAAS,SAAS,IACjD,IAAM,CAAC,CAAC,EAAS;MACvB,OACE,kBAAC,IAAD;OAEE,MAAM;OACN,MAAM,GAAQ;OACH;OACH;OACC;OACT,UAAU;OACV,sBAAsB,GAAa,OAAU;QAAE,GAAG;SAAO,IAAM,CAAC,EAAK;OAAK,EAAE;OAC5E,SAAS,MAAS,EAAS,GAAI,CAAI;OACnC,eAAe,MACG,EAAM,SAAS,GAAQ,QAAQ,EAAM,UAAU,KAAA;OAGjE,cAAc,MAAU;QACtB,IAAM,IAAU,EAAM,SAAS,GAAQ,QAAQ,EAAM,UAAU,KAAA;QAC1D,KACL,EAAY,EAAQ,OAAO;SACzB,OAAO,EAAI,EAAM,IAAI;SACrB,MAAM,EAAM;QACd,CAAC;OACH;MACD,GArBM,CAqBN;KAEL,CAAC,CACE;OA/CK,CA+CL,CACN;GACE,CAAA;GAEL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,WAAW;KACX,SAAS;KACT,YAAY;KACZ,KAAK;KACL,gBAAgB,IAAY,WAAW;IACzC;cARF,CAUE,kBAAC,UAAD;KACE,eAAe,EAAc,WAAW,IAAY,SAAS,WAAW;KACxE,OAAO,IAAY,wBAAwB;KAC3C,OAAO;MACL,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,QAAQ;MACR,OAAO;MACP,QAAQ;KACV;eAEa,EAAZ,IAAa,EAAK,QAAsB,EAAK,MAAjC,EAAY,MAAM,GAAK,CAA0B;IACxD,CAAA,GACP,CAAC,KACA,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,MAAM;MAAG,UAAU;MAAI,OAAO;KAAgB;eAAG;IAE1D,CAAA,GACN,kBAAC,IAAD,EAAA,UAAK,IAAM,CAAA,CACX,EAAA,CAAA,CAED;;EACA;;AAEX;AAOA,SAAgB,GAAc,GAAkB,GAAsC;CACpF,IAAM,IAA6B,CAAC;CACpC,KAAK,IAAM,KAAQ,GACZ,OAAY,GAAM,CAAU,GACjC;MAAI,EAAK,SAAS,EAAK,MAAM,SAAS,GAAG;GACvC,EAAS,KAAK;IAAE,OAAO,EAAI,EAAK,IAAI,KAAK;IAAI,OAAO,EAAK;GAAM,CAAC;GAChE;EACF;EACA,IAAI,EAAK,QAAQ;GAEf,IAAI,IAAM,EAAS,MAAM,MAAM,EAAE,UAAU,EAAE;GAK7C,AAJK,MACH,IAAM;IAAE,OAAO;IAAI,OAAO,CAAC;GAAE,GAC7B,EAAS,KAAK,CAAG,IAEnB,EAAI,MAAM,KAAK,CAAI;EACrB;CATA;CAWF,OAAO;AACT;AAEA,SAAS,GAAY,GAAe,GAA6B;CAE/D,QADiB,EAAK,QAAQ,eACV;AACtB;AAEA,SAAS,GAAY,EACnB,SACA,SACA,cACA,WACA,YACA,aACA,mBACA,WACA,gBACA,mBAYC;CACD,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,IAAU,GACV,IAAW,EAAK,QAAQ,EAAQ,EAAK,QAAkB,EAAQ,EAAK,QAAkB,KAAA;CAC5F,AAAI,EAAK,QAAQ,CAAC,KAChB,QAAQ,MAAM,yBAAyB,EAAK,UAAU,EAAI,EAAK,IAAI,EAAE,uBAAuB,EAAK,KAAK,EAAE;CAE1G,IAAM,IAAQ,EAAI,EAAK,IAAI,KAAK,EAAK,UAAU;CAK/C,IAAI,GAAW;EACb,IAAM,IAAsC;GAC1C,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,cAAc;GACd,QAAQ;GACR,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,YAAY,IACR,0BACA,IACE,yBACA;GACN,OAAO,IAAS,kBAAkB;GAClC,gBAAgB;EAClB;EAuBA,OAtBI,IAEA,kBAAC,KAAD;GACQ;GAIN,8BAA2B;GAC3B,UAAU,MAAM;IACV,GAAgB,CAAC,MACrB,EAAE,eAAe,GACjB,EAAO;GACT;GACA,OAAO;GACP,oBAAoB,EAAS,EAAI;GACjC,oBAAoB,EAAS,EAAK;GAClC,OAAO;aAEN,KAAY,kBAAC,GAAD,EAAU,MAAM,GAAK,CAAA;EACjC,CAAA,IAIL,kBAAC,UAAD;GACE,eAAe,EAAO;GACtB,OAAO;GACP,oBAAoB,EAAS,EAAI;GACjC,oBAAoB,EAAS,EAAK;GAClC,OAAO;aAEN,KAAY,kBAAC,GAAD,EAAU,MAAM,GAAK,CAAA;EAC5B,CAAA;CAEZ;CAMA,IAAM,IAAgC;EACpC,OAAO;EACP,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,KAAK;EACL,SAAS;EACT,cAAc;EACd,QAAQ;EACR,QAAQ;EACR,WAAW;EACX,YAAY,IACR,0BACA,IACE,yBACA;EACN,OAAO,IAAS,kBAAkB;EAClC,UAAU;EACV,YAAY,IAAS,MAAM;EAC3B,gBAAgB;CAClB,GACM,IACJ,kBAAA,GAAA,EAAA,UAAA;EACG,KAAY,kBAAC,GAAD,EAAU,MAAM,GAAK,CAAA;EAClC,kBAAC,QAAD;GAAM,OAAO,EAAE,MAAM,EAAE;aAAI;EAAY,CAAA;EACtC,EAAK,SACJ,kBAAC,QAAD;GACE,OAAO;IACL,SAAS;IACT,YAAY;IACZ,OAAO;IACP,cAAc;IACd,UAAU;IACV,YAAY;GACd;aAEC,EAAK;EACF,CAAA;EAEP,KACC,kBAAC,QAAD;GACE,MAAK;GACL,cAAY,IAAW,aAAa;GACpC,UAAU,MAAM;IAMd,AAFA,EAAE,gBAAgB,GAClB,EAAE,eAAe,GACjB,EAAe;GACjB;GACA,OAAO;IAAE,SAAS;IAAe,SAAS;IAAG,QAAQ;GAAU;aAE/D,kBAAC,EAAK,MAAN;IACE,MAAM;IACN,OAAO;KACL,WAAW,IAAW,cAAc;KACpC,YAAY;KACZ,OAAO;IACT;GACD,CAAA;EACG,CAAA;CAER,EAAA,CAAA;CAEJ,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,IACC,kBAAC,KAAD;EACQ;EAGN,8BAA2B;EAC3B,UAAU,MAAM;GAEV,GAAgB,CAAC,MACrB,EAAE,eAAe,GACjB,EAAO;EACT;EACA,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;YAEN;CACA,CAAA,IAEH,kBAAC,UAAD;EACE,eAAe;GAGb,AAAI,KAAS,EAAe;EAC9B;EACA,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;YAEN;CACK,CAAA,GAET,KAAW,KACV,kBAAC,OAAD;EACE,OAAO;GACL,YAAY;GACZ,YAAY;GACZ,aAAa;EACf;aAEE,EAAK,YAAY,CAAC,GAAG,KAAK,GAAO,MAAQ;GACzC,IAAM,IAAU,EAAa,CAAK,GAC5B,IAAW,EAAM,UAAU,GAAG;GACpC,OACE,kBAAC,IAAD;IAEE,OAAO,EAAI,EAAM,IAAI,KAAK,EAAM,UAAU;IAC1C,MAAM,GAAS;IACf,SAAS,CAAC,CAAC;IACX,cAAc,EAAY,CAAK;GAChC,GALM,CAKN;EAEL,CAAC;CACE,CAAA,CAEJ,EAAA,CAAA;AAET;AAEA,SAAS,GAAU,EACjB,UACA,SACA,YACA,aAMC;CACD,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,IAAkC;EACtC,OAAO;EACP,SAAS;EACT,SAAS;EACT,cAAc;EACd,YAAY,IAAQ,yBAAyB;EAC7C,QAAQ;EACR,WAAW;EACX,QAAQ,IAAU,YAAY;EAC9B,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CAqBA,OApBI,KAAW,IAEX,kBAAC,KAAD;EACQ;EAGN,8BAA2B;EAC3B,UAAU,MAAM;GACV,GAAgB,CAAC,MACrB,EAAE,eAAe,GACjB,EAAO;EACT;EACA,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;YAEN;CACA,CAAA,IAIL,kBAAC,UAAD;EACE,eAAe,KAAW,EAAO;EACjC,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;YAEN;CACK,CAAA;AAEZ;;;AC3cA,IAAM,KAAuE;CAC3E,SAAS;EAAE,MAAM;EAAW,cAAc;CAAU;CACpD,OAAO;EAAE,MAAM;EAAU,cAAc;CAAQ;CAC/C,SAAS;EAAE,MAAM;EAAQ,cAAc;CAAU;CACjD,MAAM;EAAE,MAAM;EAAQ,cAAc;CAAW;CAC/C,SAAS;EAAE,MAAM;EAAQ,cAAc;CAAW;AACpD,GAGM,KAAoB;AAI1B,SAAS,GAAU,EAAE,WAA6B;CAChD,IAAM,IAAuB,EAAE,SAAS,OAAO;CAU/C,OATI,MAAS,YAET,kBAAC,QAAD;EAAM,OAAO;GAAE,GAAG;GAAO,WAAW;EAAqC;YACvE,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;CACpB,CAAA,IAMR,kBAAC,QAAD;EAAa;YACX,kBAHF,MAAS,YAAY,EAAK,QAAQ,MAAS,UAAU,EAAK,IAAI,MAAS,YAAY,EAAK,UAAU,EAAK,MAGrG,EAAO,MAAM,GAAK,CAAA;CACd,CAAA;AAEV;AAEA,SAAgB,GAAe,EAAE,UAAO,cAAuD;CAC7F,IAAM,IAAO,GAAU,EAAM,SAAS,GAAU,MAC1C,IAAO,EAAK,MAEZ,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAU,KAAe,EAAS,CAAC,GAMpC,IAAe,EAAO,EAAM,QAAQ,GACpC,IAAa,EAAO,EAAK,GAEzB,IAAc,CAAC,EAAM,UAAU,EAAM,WAAW,GAEhD,IAAY,QAAkB;EAC9B,EAAW,YACf,EAAW,UAAU,IACrB,EAAW,EAAI,GACf,OAAO,WAAW,GAAS,EAAiB;CAC9C,GAAG,CAAC,CAAO,CAAC;CAUZ,AAPA,QAAgB;EAEd,AADA,EAAa,UAAU,EAAM,UAC7B,EAAY,CAAC;CACf,GAAG,CAAC,EAAM,WAAW,EAAM,QAAQ,CAAC,GAIpC,QAAgB;EACd,IAAI,CAAC,KAAe,KAAU,GAAS;EACvC,IAAM,IAAW,KAAK,IAAI,GACpB,IAAQ,OAAO,WAAW,GAAW,EAAa,OAAO,GAC3D,IAAM,GACJ,UAAa;GACjB,IAAM,IAAO,EAAa,WAAW,KAAK,IAAI,IAAI;GAElD,AADA,EAAY,KAAK,IAAI,GAAG,CAAI,IAAI,EAAM,QAAQ,GAC1C,IAAO,MAAG,IAAM,sBAAsB,CAAI;EAChD;EAEA,OADA,IAAM,sBAAsB,CAAI,SACnB;GAGX,AAFA,OAAO,aAAa,CAAK,GACzB,qBAAqB,CAAG,GACxB,EAAa,UAAU,KAAK,IAAI,GAAG,EAAa,WAAW,KAAK,IAAI,IAAI,EAAS;EACnF;CACF,GAAG;EAAC;EAAa;EAAQ;EAAS,EAAM;EAAU,EAAM;EAAW;CAAS,CAAC;CAE7E,IAAM,IAAS,QAAkB;EAC/B,IAAM,IAAO;GACX,EAAM;GACN,EAAM;GACN,EAAM,YAAY,eAAe,EAAM,cAAc;GACrD,EAAM;EACR,EACG,OAAO,OAAO,EACd,KAAK,IAAI;EAGZ,AAFA,UAAe,WAAW,UAAU,CAAI,GACxC,EAAU,EAAI,GACd,OAAO,iBAAiB,EAAU,EAAK,GAAG,IAAI;CAChD,GAAG;EAAC,EAAM;EAAO,EAAM;EAAS,EAAM;EAAW,EAAM;CAAO,CAAC,GAEzD,KAAW,MAAmB,SAAS,IAAO,EAAO,IACrD,IAAW,EAAM,SAAS;CAEhC,OACE,kBAAC,OAAD;EACE,MAAM,EAAM,SAAS,UAAU,UAAU;EACzC,aAAW,EAAM,SAAS,UAAU,cAAc;EAClD,oBAAoB,EAAU,EAAI;EAClC,oBAAoB,EAAU,EAAK;EACnC,OAAO;GACL,OAAO;GACP,YAAY;GACZ,QAAQ,aAAa,EAAQ,SAAS;GACtC,YAAY,aAAa,EAAQ,EAAE;GACnC,cAAc;GACd,WAAW;GACX,UAAU;GACV,UAAU;GACV,WAAW,IACP,gBAAgB,GAAkB,uBAClC;EACN;YAjBF,CAmBE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,SAAS;IAAuB,KAAK;GAAG;aAAvE;IAEE,kBAAC,OAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,WAAW;MACX,YAAY;MACZ,YAAY,EAAQ,OAAO;MAC3B,cAAc;MACd,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,OAAO,EAAQ,EAAE;KACnB;eAEA,kBAAC,IAAD,EAAW,MAAM,EAAM,KAAO,CAAA;IAC3B,CAAA;IAGL,kBAAC,OAAD;KAAK,OAAO;MAAE,MAAM;MAAG,UAAU;KAAE;eAAnC;MACE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAY,KAAK;OAAE;iBAA9D;QACE,kBAAC,OAAD;SACE,OAAO;UACL,UAAU;UACV,YAAY;UACZ,OAAO;UACP,eAAe;UACf,YAAY;SACd;mBAEC,EAAM,SAAS,EAAK;QAClB,CAAA;SACH,EAAM,SAAS,KAAK,KACpB,kBAAC,QAAD;SACE,OAAO;UACL,YAAY,EAAQ,OAAO;UAC3B,OAAO,EAAQ,EAAE;UACjB,UAAU;UACV,SAAS;UACT,cAAc;UACd,YAAY;UACZ,YAAY;SACd;mBATF,CAUC,KACG,EAAM,KACJ;;QAEP,EAAM,aACL,kBAAC,QAAD;SACE,WAAU;SACV,OAAO;UACL,UAAU;UACV,OAAO;UACP,YAAY;UACZ,YAAY;SACd;mBAEC,EAAM;QACH,CAAA;OAEL;;MAEJ,EAAM,WACL,kBAAC,OAAD;OACE,OAAO;QACL,UAAU;QACV,OAAO;QACP,WAAW;QACX,YAAY;QACZ,YAAY;QACZ,QAAQ;QACR,WAAW;OACb;iBAEC,EAAM;MACJ,CAAA;MAGN,EAAM,WACL,kBAAC,OAAD;OAAK,OAAO,EAAE,WAAW,EAAE;iBAA3B,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,GAAa,MAAM,CAAC,CAAC;QACpC,OAAO;SACL,YAAY;SACZ,QAAQ;SACR,SAAS;SACT,QAAQ;SACR,OAAO;SACP,UAAU;SACV,YAAY;SACZ,SAAS;SACT,YAAY;SACZ,KAAK;QACP;kBAdF,CAgBE,kBAAC,EAAK,MAAN;SACE,MAAM;SACN,OAAO;UAAE,WAAW,IAAW,mBAAmB;UAAQ,YAAY;SAAiB;QACxF,CAAA,GACA,IAAW,iBAAiB,cACvB;WACP,KACC,kBAAC,OAAD;QACE,WAAU;QACV,OAAO;SACL,QAAQ;SACR,SAAS;SACT,YAAY;SACZ,QAAQ;SACR,cAAc;SACd,UAAU;SACV,YAAY;SACZ,OAAO;SACP,YAAY;SACZ,WAAW;SACX,YAAY;SACZ,WAAW;SACX,UAAU;QACZ;kBAEC,EAAM;OACJ,CAAA,CAEJ;;OAGL,EAAM,UAAU,MAChB,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAG,WAAW;OAAE;iBAA1E,CACG,EAAM,UACL,kBAAC,UAAD;QACE,MAAK;QACL,eAAe;SAEb,AADA,EAAM,QAAQ,QAAQ,GACtB,EAAU;QACZ;QACA,OAAO;SACL,YAAY,EAAQ,OAAO;SAC3B,OAAO,EAAQ,EAAE;SACjB,QAAQ;SACR,SAAS;SACT,cAAc;SACd,UAAU;SACV,YAAY;SACZ,QAAQ;QACV;kBAEC,EAAM,OAAO;OACR,CAAA,GAET,KACC,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,OAAM;QACN,cAAW;QACX,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,YAAY;SACZ,QAAQ;SACR,SAAS;SACT,cAAc;SACd,UAAU;SACV,OAAO,IAAS,mBAAmB;SACnC,QAAQ;SACR,YAAY;QACd;kBAjBF,CAmBY,EAAT,IAAU,EAAK,QAAsB,EAAK,MAAjC,EAAY,MAAM,GAAK,CAA0B,GAC1D,IAAS,WAAW,MACf;SAEP;;KAEJ;;IAGL,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,cAAW;KACX,OAAO;MACL,OAAO;MACP,QAAQ;MACR,SAAS;MACT,WAAW;MACX,YAAY;MACZ,YAAY;MACZ,QAAQ;MACR,cAAc;MACd,QAAQ;MACR,OAAO;MACP,SAAS;MACT,YAAY;MACZ,gBAAgB;KAClB;eAEA,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACb,CAAA;GACL;MAGJ,KACC,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAY,MAAM;IAAG,OAAO;IAAG,QAAQ;IAAG,QAAQ;GAAE;aAC1E,kBAAC,OAAD,EACE,OAAO;IACL,QAAQ;IACR,OAAO,GAAG,IAAW,IAAI;IACzB,YAAY,EAAQ,EAAE;IACtB,SAAS;IACT,YAAY,IAAS,SAAS;GAChC,EACD,CAAA;EACE,CAAA,CAEJ;;AAET;;;ACnVA,IAAM,KAAuC;CAC3C,SAAS;CACT,MAAM;CACN,SAAS;CACT,OAAO;CACP,SAAS;AACX,GAGM,qBAAO,IAAI,IAAyB,GAEpC,qBAAS,IAAI,IAAoB,GAEnC,KAAM,GACJ,WAAe,MAAM,EAAE;AAG7B,SAAS,GAAQ,GAA0B;CACzC,GAAO,aAAa,kBAAC,IAAD;EAAuB;EAAO,eAAe,GAAO,QAAQ,EAAM,EAAE;CAAI,CAAA,GAAG;EAC7F,IAAI,EAAM;EAGV,UAAU;EACV,iBAAiB;GAEf,AADA,GAAK,OAAO,OAAO,EAAM,EAAE,CAAC,GACxB,EAAM,SAAS,GAAO,IAAI,EAAM,KAAK,MAAM,EAAM,MAAI,GAAO,OAAO,EAAM,KAAK;EACpF;CACF,CAAC;AACH;AAGA,SAAS,GAAK,GAAiB,GAAe,GAAkB,IAAqB,CAAC,GAAW;CAG/F,IAAI,EAAK,OAAO;EACd,IAAM,IAAS,GAAO,IAAI,EAAK,KAAK;EACpC,IAAI,KAAU,GAAK,IAAI,CAAM,GAAG;GAC9B,IAAM,IAAO,GAAK,IAAI,CAAM,GACtB,IAAsB;IAC1B,GAAG;IACH;IACA;IACA,QAAQ,EAAK,SAAS,KAAK;IAC3B,WAAW,KAAK,IAAI;GACtB;GAGA,OAFA,GAAK,IAAI,GAAQ,CAAM,GACvB,GAAQ,CAAM,GACP;EACT;CACF;CAEA,IAAM,IAAK,EAAK,MAAM,GAAO,GACvB,IAAW,EAAK,YAAY,GAAU,IACtC,IAAqB;EACzB;EACA;EACA;EACA;EACA;EACA,QAAQ,EAAK,UAAU,MAAa;EACpC,QAAQ,EAAK;EACb,WAAW,EAAK;EAChB,SAAS,EAAK;EACd,OAAO;EACP,WAAW,KAAK,IAAI;EACpB,OAAO,EAAK;CACd;CAIA,OAHA,GAAK,IAAI,GAAI,CAAK,GACd,EAAK,SAAO,GAAO,IAAI,EAAK,OAAO,CAAE,GACzC,GAAQ,CAAK,GACN;AACT;AAeA,SAAS,GAAO,GAAY,GAA0B;CACpD,IAAM,IAAU,GAAK,IAAI,CAAE;CAC3B,IAAI,CAAC,GAAS;CACd,IAAM,IAAO,EAAM,QAAQ,EAAQ,MAG7B,IAAW,EAAM,aAAa,EAAM,OAAO,GAAU,KAAQ,EAAQ,WACrE,IAAoB;EACxB,GAAG;EACH,GAAG;EACH;EACA;EACA,QAAQ,EAAM,UAAU,MAAa;EACrC,WAAW,KAAK,IAAI;CACtB;CAEA,AADA,GAAK,IAAI,GAAI,CAAI,GACjB,GAAQ,CAAI;AACd;AAGA,IAAa,IAAQ;CACnB,UAAU,GAAe,GAAkB,MAAwB,GAAK,WAAW,GAAO,GAAS,CAAI;CACvG,OAAO,GAAe,GAAkB,MAAwB,GAAK,QAAQ,GAAO,GAAS,CAAI;CACjG,UAAU,GAAe,GAAkB,MAAwB,GAAK,WAAW,GAAO,GAAS,CAAI;CACvG,QAAQ,GAAe,GAAkB,MAAwB,GAAK,SAAS,GAAO,GAAS,CAAI;CACnG,UAAU,GAAe,GAAkB,MAAwB,GAAK,WAAW,GAAO,GAAS,CAAI;CACvG;CACA,UAAU,MAAe,GAAO,QAAQ,CAAE;CAC1C,kBAAkB,GAAO,QAAQ;AACnC;;;AC9JA,SAAgB,KAAU;CACxB,IAAM,EACJ,SACA,oBACA,cACA,iBACA,YACA,YACA,eACA,QAAQ,MACN,EAAa,GACX,IAAW,GAAY,GACvB,IAAc,GAAe,GAE7B,IAAgB,GAAY;EAChC,aAAa,EAAE,UAAO,aAAU,kBAC9B,GAAc,GAAO,GAAU,KAAY,EAAI;EACjD,YAAY,MAAW;GAMrB,AALA,EAAQ,EAAO,MAAM,EAAO,MAAM,GAGlC,EAAiB,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,GAE3D,EAAoB,EAAE,KAAK,CAAU,EAAE,YAAY,CAAC,CAAC;EACvD;CACF,CAAC,GAEK,IAAiB,GAAY;EACjC,aAAa,EACX,UACA,aACA,qBAKI,GAAe,GAAO,GAAU,CAAW;EACjD,YAAY,MAAW;GAGrB,AAFA,EAAQ,EAAO,MAAM,EAAO,MAAM,GAClC,EAAiB,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,GAC3D,EAAoB,EAAE,KAAK,CAAU,EAAE,YAAY,CAAC,CAAC;EACvD;EACA,UAAU,MAAe;GACvB,EAAM,MAAM,EAAI,OAAO;EACzB;CACF,CAAC,GAEK,IAAuB,GAAY;EACvC,aAAa,MAAqB,GAAqB,CAAQ;EAC/D,YAAY,MAAW;GAQrB,IAAM,IAAM,EAAO,QAAQ,IACrB,IAAM,EAAO,MAAM,IACnB,IAAW,KAAO,KAAO,GAAsB,GAAK,CAAG,KAAM;GACnE,OAAO,SAAS,OAAO,CAAO;EAChC;EACA,UAAU,MAAe;GACvB,EAAM,MAAM,EAAI,OAAO;EACzB;CACF,CAAC;CAQD,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,OAAO,EAAc;EACrB,QAAQ,EAAe;EACvB,cAAc,EAAqB;EACnC,oBAfyB;GAGzB,AAFA,MAAM,EAAY,GAClB,EAAY,MAAM,GAClB,EAAS,QAAQ;EACnB;EAYE,gBAAgB,EAAc;EAC9B,iBAAiB,EAAe;EAChC,mBAAmB,EAAqB;CAC1C;AACF;;;AC3EA,SAAgB,KAAmB;CACjC,IAAM,IAAO,GAAc,MAAM,EAAE,IAAI,GACjC,IAAa,GAAc,MAAM,EAAE,UAAU,GAC7C,IAAc,GAAe,GAE7B,IAAO,GAAM,iBAAiB,MAC9B,IAAkB,CAAC,CAAC,GAEpB,IAAgB,GAAY;EAChC,aAAa,EACX,iBACA,WACA,yBAKI,GAAmB,GAAc,GAAQ,CAAe;EAC9D,WAAW,YAAY;GAKrB,AAFA,MAAM,EAAW,GACjB,EAAY,kBAAkB,GAC9B,EAAM,QAAQ,gCAAgC;EAChD;EACA,UAAU,MAAe,EAAM,MAAM,EAAI,OAAO;CAClD,CAAC,GAEK,IAAe,GAAY;EAC/B,kBAAkB,GAAkB;EACpC,WAAW,YAAY;GAGrB,AAFA,MAAM,EAAW,GACjB,EAAY,kBAAkB,GAC9B,EAAM,QAAQ,+BAA+B;EAC/C;EACA,UAAU,MAAe,EAAM,MAAM,EAAI,OAAO;CAClD,CAAC,GAEK,IAAiB,GAAY;EACjC,aAAa,MAAsB,GAAoB,CAAS;EAChE,iBAAiB;GAEf,AADA,EAAM,QAAQ,kBAAkB,GAChC,EAAY,kBAAkB,EAAE,UAAU,CAAC,uBAAuB,EAAE,CAAC;EACvE;EACA,UAAU,MAAe,EAAM,MAAM,EAAI,OAAO;CAClD,CAAC;CAED,OAAO;EACL;EACA;EACA,OAAO,EAAc;EACrB,MAAM,EAAa;EACnB,QAAQ,EAAe;EACvB,YAAY,EAAc;EAC1B,YAAY,EAAa;CAC3B;AACF;;;ACpEA,IAAM,KAAmB;AAMzB,SAAS,GAAe,GAAuB;CAC7C,OAAO,EAAK,WAAW,GAAG;AAC5B;AAyBA,IAAM,KAAqC;CACzC,YAAY;EAAE,IAAI;EAAW,IAAI;EAAW,QAAQ;EAAW,QAAQ;EAAW,QAAQ;CAAwB;CAClH,SAAY;EAAE,IAAI;EAAW,IAAI;EAAW,QAAQ;EAAW,QAAQ;EAAW,QAAQ;CAAwB;CAClH,KAAY;EAAE,IAAI;EAAW,IAAI;EAAW,QAAQ;EAAW,QAAQ;EAAW,QAAQ;CAAwB;AACpH,GAEM,KAAyB;CAAE,IAAI;CAAW,IAAI;CAAW,QAAQ;AAAU;AAEjF,SAAgB,GAAQ,GAA0B;CAEhD,OADK,IACE,GAAU,MAAQ,KADR,GAAU;AAE7B;AAEA,SAAgB,GAAW,EAAE,WAAQ,YAAS,aAAU,SAAM,WAAQ,mBAAiC;CACrG,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,IAAS,EAA0B,IAAI,GACvC,IAAM,GAAQ,OAAO,cACrB,IAAO,GAAQ,CAAG,GAClB,IAAO,GAAQ,QAAQ,KACvB,IAAO,GAAQ,QAAQ,KAOvB,IAAgB,QACd,EAAQ,QAAQ,MAAM,EAAE,cAAc,EAAK,GACjD,CAAC,CAAO,CACV,GACM,IAAmB,QACjB,EAAQ,QAAQ,MAAM,EAAE,cAAc,EAAK,GACjD,CAAC,CAAO,CACV,GAEM,IAAa,EAAQ,UAAU,IAC/B,KAAW,MAAuB;EACtC,IAAI,CAAC,GAAO,OAAO;EACnB,IAAM,IAAS,EAAM,YAAY;EACjC,OAAO,EAAE,KAAK,YAAY,EAAE,SAAS,CAAM,KAAK,EAAE,KAAK,YAAY,EAAE,SAAS,CAAM;CACtF,GACM,IAAiB,EAAc,OAAO,CAAO,GAC7C,IAAgB,EAAiB,OAAO,CAAO,GAC/C,IAAY,MAAU,MAAM,EAAe,WAAW,KAAK,EAAc,WAAW;CAE1F,OACE,kBAAC,OAAD;EAAK,OAAO,EAAE,UAAU,WAAW;YAAnC,CACE,kBAAC,UAAD;GACE,KAAK;GACL,eAAe,CAAC,KAAU,GAAS,MAAM,CAAC,CAAC;GAC3C,eAAY;GACZ,eAAa,IAAS,SAAS,KAAA;GAC/B,OAAO;IACL,SAAS;IACT,YAAY;IACZ,KAAK;IACL,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,QAAQ,IAAS,gBAAgB;IACjC,OAAO;IACP,UAAU;IACV,YAAY;IACZ,SAAS,IAAS,KAAM;GAC1B;GACA,OAAO,IAAU,KAAgB,iCAAkC;GACnE,UAAU,KAAQ;aArBpB;IAuBE,kBAAC,EAAK,QAAN;KAAa,MAAM;KAAI,OAAO,EAAE,OAAO,gBAAgB;IAAI,CAAA;IAC3D,kBAAC,QAAD;KAAM,OAAO,EAAE,YAAY,IAAI;eAAI;IAAW,CAAA;IAC9C,kBAAC,QAAD;KACE,WAAU;KACV,OAAO;MACL,SAAS;MACT,cAAc;MACd,UAAU;MACV,YAAY;MACZ,eAAe;MACf,eAAe;MACf,YAAY,EAAK;MACjB,OAAO,EAAK;MACZ,QAAQ,aAAa,EAAK,UAAU;KACtC;eAEC;IACG,CAAA;IACN,kBAAC,EAAK,MAAN;KAAW,MAAM;KAAI,OAAO,EAAE,OAAO,gBAAgB;IAAI,CAAA;GACnD;MACR,kBAAC,IAAD;GACQ;GACN,QAAQ,EAAO;GACf,eAAe,EAAQ,EAAK;GAC5B,OAAO;GACP,OAAM;aALR;IAOE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAiB,cAAc;KAA2B;eAAjF;MACE,kBAAC,OAAD;OACE,OAAO;QACL,UAAU;QACV,OAAO;QACP,eAAe;QACf,YAAY;QACZ,eAAe;OACjB;iBACD;MAEI,CAAA;MACL,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAG,WAAW;OAAE;iBAA1E;QACE,kBAAC,EAAK,QAAN;SAAa,MAAM;SAAI,OAAO,EAAE,OAAO,gBAAgB;QAAI,CAAA;QAC3D,kBAAC,QAAD;SAAM,OAAO;UAAE,UAAU;UAAI,YAAY;SAAI;mBAAI;QAAW,CAAA;QAC5D,kBAAC,QAAD;SACE,WAAU;SACV,OAAO;UACL,YAAY;UACZ,SAAS;UACT,cAAc;UACd,UAAU;UACV,YAAY;UACZ,YAAY,EAAK;UACjB,OAAO,EAAK;SACd;mBAEC;QACG,CAAA;OACH;;MACL,kBAAC,OAAD;OAAK,OAAO;QAAE,WAAW;QAAG,UAAU;QAAI,OAAO;OAAgB;iBAAjE;QAAoE;QAC5D;QACN,kBAAC,QAAD;SAAM,OAAO;UAAE,OAAO;UAAiB,YAAY;SAAI;mBAAI;QAAW,CAAA;OACnE;;KACF;;IACJ,KACC,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAgB,cAAc;KAA2B;eAC9E,kBAAC,SAAD;MACE,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;MACxC,aAAY;MACZ,WAAA;MACA,eAAY;MACZ,OAAO;OACL,OAAO;OACP,QAAQ;OACR,SAAS;OACT,UAAU;OACV,YAAY;OACZ,OAAO;OACP,QAAQ;OACR,cAAc;OACd,SAAS;MACX;KACD,CAAA;IACE,CAAA;IAEP,kBAAC,OAAD;KACE,OAAO;MACL,WAAW;MACX,WAAW;KACb;eAJF;MAME,kBAAC,IAAD;OACE,OAAO,EAAiB,SAAS,IAAI,iBAAiB;OACtD,SAAS;OACT,UAAU,GAAQ;OAClB,SAAS,MAAM;QAEb,AADA,EAAQ,EAAK,GACb,EAAS,CAAC;OACZ;MACD,CAAA;MACA,EAAc,SAAS,KACtB,kBAAC,IAAD;OACE,OAAM;OACN,SAAS;OACT,UAAU,GAAQ;OAClB,SAAS,MAAM;QAEb,AADA,EAAQ,EAAK,GACb,EAAS,CAAC;OACZ;MACD,CAAA;MAEF,KACC,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,UAAU;QACV,OAAO;QACP,WAAW;OACb;iBANF;QAOC;QACoB;QAAM;OACtB;;KAEJ;;IACL,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,WAAW;MACX,UAAU;MACV,OAAO;MACP,SAAS;MACT,YAAY;MACZ,KAAK;KACP;eATF,CAWE,kBAAC,EAAK,MAAN;MAAW,MAAM;MAAI,OAAO;OAAE,WAAW;OAAG,YAAY;MAAE;KAAI,CAAA,GAC9D,kBAAC,QAAD,EAAA,UAAM,+EAAkF,CAAA,CACrF;;GACG;IACP;;AAET;AAEA,SAAS,GAAc,EACrB,UACA,YACA,aACA,aAMC;CAED,OADI,EAAQ,WAAW,IAAU,OAE/B,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,UAAU;GACV,OAAO;GACP,eAAe;GACf,YAAY;GACZ,eAAe;EACjB;YAEC;CACE,CAAA,GACJ,EAAQ,KAAK,MACZ,kBAAC,IAAD;EAEE,QAAQ;EACR,MAAM,GAAQ,EAAE,GAAG;EACnB,QAAQ,EAAE,OAAO;EACjB,cAAc,EAAO,CAAC;CACvB,GALM,EAAE,EAKR,CACF,CACD,EAAA,CAAA;AAEN;AAEA,SAAS,GAAU,EACjB,WACA,SACA,WACA,aAMC;CACD,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,IAAK,KAA8B,IAArB,qBAAkD,eAChE,IAAW,GAAe,EAAO,IAAI;CAC3C,OACE,kBAAC,UAAD;EACE,SAAS;EACT,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,OAAO;GACP,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,QAAQ;GACR,WAAW;EACb;YAdF;GAgBE,kBAAC,EAAK,QAAN;IAAa,MAAM;IAAI,OAAO,EAAE,OAAO,gBAAgB;GAAI,CAAA;GAC3D,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAM,OAAO;KAAe,YAAY;IAAI;cAAI,EAAO;GAAW,CAAA;GAC1F,IACC,kBAAC,QAAD;IACE,WAAU;IACV,OAAM;IACN,OAAO;KACL,SAAS;KACT,cAAc;KACd,UAAU;KACV,YAAY;KACZ,YAAY;KACZ,OAAO;KACP,QAAQ;IACV;cACD;GAEK,CAAA,IAEN,kBAAC,QAAD;IACE,WAAU;IACV,OAAO;KACL,SAAS;KACT,cAAc;KACd,UAAU;KACV,YAAY;KACZ,YAAY,EAAK;KACjB,OAAO,EAAK;IACd;cAEC,EAAO,OAAO;GACX,CAAA;GAER,kBAAC,QAAD,EAAM,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;GAC1B,EAAO,QACN,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAM,OAAO;IAAgB;cAAI,EAAO;GAAW,CAAA;GAE7E,KAAU,kBAAC,EAAK,OAAN;IAAY,MAAM;IAAI,OAAO;KAAE,OAAO;KAAiB,YAAY;IAAE;GAAI,CAAA;EAC9E;;AAEZ;;;AC3VA,SAAgB,GAAY,EAAE,UAAO,mBAAkC;CAErE,OADI,CAAC,KAAS,EAAM,WAAW,IAAU,OAEvC,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,UAAU;GACV,UAAU;GACV,UAAU;GACV,MAAM;EACR;YAEC,EAAM,KAAK,GAAG,MAAM;GACnB,IAAM,IAAO,MAAM,EAAM,SAAS,GAC5B,IAAY,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE;GACrC,OACE,kBAAC,IAAD,EAAA,UAAA,CACG,IAAI,KACH,kBAAC,QAAD;IAAM,OAAO;KAAE,OAAO;KAAiB,SAAS;IAAQ;cACtD,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;GACnB,CAAA,GAER,kBAAC,IAAD;IAAa,OAAO;IAAS;IAAiB;IAAW,SAAS;GAAe,CAAA,CACzE,EAAA,GAPK,CAOL;EAEd,CAAC;CACE,CAAA;AAET;AAEA,SAAS,GAAY,EACnB,UACA,SACA,cACA,cAMC;CACD,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,IAAW,EAAM,QAAS,EAAiC,EAAM,QAClE,EAA4F,EAAM,QACnG,EAAK;CACT,OACE,kBAAC,UAAD;EACE,eAAe;GACb,AAAI,MACE,IAAS,EAAQ,CAAK,IACjB,EAAM,WAAS,EAAM,QAAQ;EAE1C;EACA,UAAU,CAAC;EACX,oBAAoB,EAAS,EAAI;EACjC,oBAAoB,EAAS,EAAK;EAClC,OAAO;GACL,YAAY,KAAS,IAAY,qBAAqB;GACtD,QAAQ;GACR,SAAS;GACT,cAAc;GACd,OAAO,KAAuB,KAAS,IAAzB,gBAAqD;GACnE,YAAY,IAAO,MAAM;GACzB,QAAQ,IAAY,YAAY;GAChC,YAAY;GACZ,UAAU;GACV,UAAU;GACV,cAAc;GACd,UAAU;EACZ;YAvBF,CAyBG,EAAM,QAAQ,KACb,kBAAC,QAAD;GAAM,OAAO;IAAE,aAAa;IAAG,SAAS;IAAgB,eAAe;GAAO;aAC5E,kBAAC,GAAD,EAAU,MAAM,GAAK,CAAA;EACjB,CAAA,GAEP,EAAM,KACD;;AAEZ;;;ACrEA,SAAgB,GAAO,EACrB,gBACA,iBACA,WACA,eACA,WACA,kBACA,mBACe,CAAC,GAAG;CACnB,IAAM,IAAO,GAAc,MAAM,EAAE,IAAI,GACjC,EAAE,iBAAc,YAAS,iBAAc,sBAAmB,cAAW,GAAQ,GAC7E,EAAE,oBAAiB,MAAM,MAAsB,GAAiB,GAChE,IAAQ,GAAoB,MAAM,EAAE,KAAK,GACzC,IAAgB,GAAoB,MAAM,EAAE,GAAG,GAE/C,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,IAAa,EAA0B,IAAI,GAE3C,IAAM,GAAc,OAAO,cAC3B,IAAO,GAAQ,CAAG,GAElB,IAAQ,GAAM,SAAS,IACvB,IAAc,GAAM,gBAAgB,KAAS,QAE7C,UAAoB;EACxB,EAAc,SAAS,MAAU,SAAS,UAAU,MAAM;CAC5D;CAiBA,OACE,kBAAC,UAAD;EACE,OAAO;GACL,UAAU;GACV,YAAY;GACZ,cAAc;GACd,SAAS;GACT,YAAY;GACZ,SAAS;GACT,KAAK;GACL,QAAQ;GACR,UAAU;EACZ;YAXF;GAaG,MAAQ,gBACP,kBAAC,QAAD,EACE,OAAO;IACL,UAAU;IACV,MAAM;IACN,OAAO;IACP,KAAK;IACL,QAAQ;IACR,YAAY,uCAAuC,EAAK,GAAG;IAC3D,SAAS;IACT,eAAe;GACjB,EACD,CAAA;GAGH,kBAAC,IAAD;IACE,QAAQ;IACC;IACT,WA/CsB,MAAc;KACpC,GAAc,OAAO,EAAE,OAIb,GAAY,SAAS,EAAE,KAAK,MAAM,MAAQ,EAAI,KACxD,KAIE,CAHY,OAAO,QACrB,6FAEG,KAEP,EAAa,EAAE,EAAE;IACnB;IAmCM,MAAM;IACN,QAAQ;IACR,cAAc,IAAkB,yCAAyC,KAAA;GAC1E,CAAA;GACA,KACC,kBAAC,QAAD;IACE,eAAY;IACZ,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK;KACL,QAAQ;KACR,SAAS;KACT,cAAc;KACd,YAAY;KACZ,OAAO;KACP,UAAU;KACV,YAAY;KACZ,eAAe;KACf,eAAe;IACjB;cAfF,CAiBE,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,GAAC,gBACpB;;GAGR,kBAAC,QAAD;IAAM,OAAO;KAAE,OAAO;KAAiB,UAAU;IAAG;cAClD,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;GACnB,CAAA;GAEN,kBAAC,IAAD;IAAa,OAAO,KAAe,CAAC;IAAiB;GAAe,CAAA;GAEpE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;GAE1B,kBAAC,UAAD;IACE,SAAS;IACT,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK;KACL,SAAS;KACT,QAAQ;KACR,YAAY;KACZ,QAAQ;KACR,cAAc;KACd,QAAQ;KACR,OAAO;KACP,UAAU;KACV,UAAU;IACZ;cAfF;KAiBE,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;KACxB,kBAAC,QAAD;MAAM,OAAO;OAAE,MAAM;OAAG,WAAW;MAAO;gBAAG;KAA4B,CAAA;KACzE,kBAAC,IAAD,EAAA,UAAK,KAAO,CAAA;IACN;;GAER,kBAAC,GAAD;IAAS,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAAG,OAAM;GAAiB,CAAA;GAC/D,kBAAC,GAAD;IACE,MAAyB,EAAnB,MAAU,SAAU,EAAK,MAAoB,EAAK,MAA/B,EAAU,MAAM,GAAK,CAA0B;IACxE,OAAM;IACN,SAAS;GACV,CAAA;GACD,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;IAC/B,OAAM;IACN,SAAS;IACT,QAAQ;GACT,CAAA;GACD,kBAAC,OAAD,EAAK,OAAO;IAAE,OAAO;IAAG,QAAQ;IAAI,YAAY;IAAkB,QAAQ;GAAQ,EAAI,CAAA;GACtF,kBAAC,OAAD;IAAK,OAAO,EAAE,UAAU,WAAW;cAAnC,CACE,kBAAC,UAAD;KACE,KAAK;KACL,eAAe,GAAa,MAAM,CAAC,CAAC;KACpC,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,SAAS;MACT,QAAQ;MACR,YAAY;MACZ,QAAQ;MACR,QAAQ;KACV;eAZF;MAcE,kBAAC,GAAD;OAAQ,MAAM;OAAa,MAAM;MAAK,CAAA;MACtC,kBAAC,QAAD;OAAM,OAAO;QAAE,UAAU;QAAI,OAAO;OAAgB;iBAAI;MAAY,CAAA;MACpE,kBAAC,EAAK,MAAN;OAAW,MAAM;OAAI,OAAO,EAAE,OAAO,gBAAgB;MAAI,CAAA;KACnD;QACR,kBAAC,IAAD;KACE,MAAM;KACN,QAAQ,EAAW;KACnB,eAAe,EAAY,EAAK;KAChC,OAAO;KACP,OAAM;eALR;MAOE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAiB,cAAc;OAA2B;iBAAjF,CACE,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,KAAK;SAAG,YAAY;QAAS;kBAA5D,CACE,kBAAC,GAAD;SAAQ,MAAM;SAAa,MAAM;QAAK,CAAA,GACtC,kBAAC,OAAD;SAAK,OAAO,EAAE,UAAU,EAAE;mBAA1B,CACE,kBAAC,OAAD;UAAK,OAAO;WAAE,UAAU;WAAM,YAAY;WAAK,OAAO;UAAc;oBACjE;SACE,CAAA,GACL,kBAAC,OAAD;UACE,OAAO;WACL,UAAU;WACV,OAAO;WACP,UAAU;WACV,cAAc;WACd,YAAY;UACd;oBAEC;SACE,CAAA,CACF;UACF;WACJ,KACC,kBAAC,OAAD;QAAK,OAAO;SAAE,WAAW;SAAG,SAAS;SAAQ,KAAK;SAAG,UAAU;QAAO;kBAAtE,CACE,kBAAC,GAAD;SAAM,MAAK;SAAS,MAAK;SAAK,KAAA;mBAC3B,EAAa;QACV,CAAA,GACN,kBAAC,GAAD;SAAM,MAAK;SAAQ,MAAK;mBAAxB;UACG,EAAa;UAAK;UAAI;SACnB;UACH;SAEJ;;MACJ,KACC,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;OACzB,eAAe;QAEb,AADA,EAAY,EAAK,GACjB,EAAkB;OACpB;OACA,eAAY;iBACb;MAES,CAAA;MAEZ,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC7B,eAAe;QAEb,AADA,EAAY,EAAK,GACjB,IAAgB;OAClB;iBACD;MAES,CAAA;MACV,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;OAChC,eAAe;QAEb,AADA,EAAY,EAAK,GACjB,IAAc;OAChB;iBACD;MAES,CAAA;MAKV,kBAAC,GAAD;OAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAAG,SAAS;iBAAa;MAErD,CAAA;MACV,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;MACnB,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;OACzB,QAAA;OACA,eAAe;QAEb,AADA,EAAY,EAAK,GACjB,EAAO;OACT;iBACD;MAES,CAAA;KACF;MACP;;EACC;;AAEZ;;;AC/QA,SAAgB,GAAS,EAAE,OAAO,GAAW,sBAAkC,CAAC,GAAG;CACjF,IAAM,IAAW,GAAY,GACvB,IAAW,EAAiC,IAAI,GAChD,IAAe,EAA8B,IAAI,GACjD,IAAkB,EAAiC,IAAI,GACvD,IAAqB,EAAO,EAAK,GACjC,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,IAAO,IAAa,MAAM,EAAE,IAAI,GAChC,IAAc,IAAa,MAAM,EAAE,WAAW,GAC9C,IAAc,GAAoB,MAAM,EAAE,WAAW,GACrD,IAAY,IAAa,MAAM,EAAE,SAAS,GAC1C,IAAW,IAAa,MAAM,EAAE,QAAQ,GAExC,IAAS,EAAK,WAAW,MAAM,EAAE,OAAO,CAAW;CAQzD,QAAgB;EACd,IAAM,IACJ,MAAW,EAAK,SAAS,IAAI,EAAgB,UAAU,EAAa;EACjE,MACL,EAAO,eAAe;GACpB,OAAO;GACP,QAAQ;GACR,UAAU,EAAmB,UAAU,WAAW;EACpD,CAAC,GACD,EAAmB,UAAU;CAC/B,GAAG;EAAC;EAAa;EAAQ,EAAK;CAAM,CAAC;CAErC,IAAM,IAAS,GACZ,GAAW,MAA+B;EACzC,IAAM,IAAI,EAAK;EACV,MACL,EAAU,EAAE,EAAE,GACd,EAAS;GAAE,UAAU,EAAE;GAAU,QAAQ,EAAE,UAAU,KAAA;EAAU,CAAC;CAClE,GACA;EAAC;EAAM;EAAW;CAAQ,CAC5B,GAEM,IAAU,GACb,MAAc;EACb,IAAM,IAAI,EAAK;EACf,IAAI,CAAC,GAAG;EACR,IAAI,GAAgB;GAClB,EAAe,WAAW,EAAE,EAAE;GAC9B;EACF;EACA,IAAM,IAAW,EAAE,OAAO;EAE1B,IADA,EAAS,EAAE,EAAE,GACT,GAAU;GACZ,IAAM,IAAY,EAAK,QAAQ,MAAM,EAAE,OAAO,EAAE,EAAE;GAClD,IAAI,EAAU,SAAS,GAAG;IAExB,IAAM,IAAO,EADG,IAAI,EAAU,SAAS,IAAI,EAAU,SAAS;IAE9D,EAAS;KAAE,UAAU,EAAK;KAAU,QAAQ,EAAK,UAAU,KAAA;IAAU,CAAC;GACxE;EACF;CACF,GACA;EAAC;EAAM;EAAa;EAAU;EAAU;CAAc,CACxD,GAEM,IAAc,GACjB,MAAyB;EAExB,IADA,EAAa,EAAK,GACd,GAAgB;GAClB,EAAe,CAAK;GACpB;EACF;EACA,AAAI,MAAU,aAAa,KAAU,KAAG,EAAQ,CAAM;CACxD,GACA;EAAC;EAAQ;EAAS;CAAc,CAClC,GAEM,IAAQ,QAAkB;EAC9B,AAAI,IAAW,EAAU,IACpB,EAAS,GAAG;CACnB,GAAG,CAAC,GAAW,CAAQ,CAAC;CAExB,IAAI,EAAK,WAAW,GAAG,OAAO;CAE9B,IAAM,IAAkB,KAAU,KAAK,EAAK,SAAS,KAAK,EAAK,IAAS,aAAa,IAC/E,IAAiB,GAAoB,GAAM,GAAa,QAAQ,EAAE,SAAS,GAC3E,IAAgB,GAAoB,GAAM,GAAa,OAAO,EAAE,SAAS,GACzE,IAAY,EAAK,UAAU,IAC7B,WACA,EAAK,UAAU,KAAK,KAAK,IAAc,EAAG,IACxC,YACA,UACA,IAAa,MAAc,WAC7B,kBACA,MAAc,YACZ,gBACA;CAEN,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,UAAU;GACV,YAAY;EACd;YATF,CAWE,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAW,WAAW;IAAQ,MAAM;IAAG,KAAK;GAAE;aAFtF,CAIG,EAAK,KAAK,GAAY,MAAc;IACnC,IAAM,IAAW,MAAM,GACjB,IAAc,EAAK,SAAS,KAAK,EAAE,aAAa;IACtD,OACE,kBAAC,OAAD;KAEE,KAAK,IAAW,IAAe,KAAA;KAC/B,UAAU,MAAM,EAAO,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC;KAC5D,aAAa,MAAM;MACjB,AAAI,EAAE,WAAW,MACf,EAAE,eAAe,GACjB,EAAQ,CAAC;KAEb;KACA,OAAO,EAAE;KACT,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,QAAQ;MACR,SAAS;MACT,YAAY,IAAW,mBAAmB;MAC1C,aAAa;MACb,WAAW,IAAW,4BAA4B;MAClD,cAAc,IAAW,6BAA6B;MACtD,cAAc,IAAW,KAAK;MAC9B,QAAQ;MACR,UAAU;MACV,UAAU;MACV,UAAU;MACV,OAAO,IAAW,gBAAgB;MAClC,UAAU;MACV,YAAY,IAAW,MAAM;KAC/B;eA7BF;MA+BG,EAAE,QACD,kBAAC,QAAD;OACE,OAAO;QACL,OAAO,IAAW,kBAAkB;QACpC,SAAS;OACX;iBAEA,kBAAC,GAAD;QAAa,MAAM,EAAE;QAAM,MAAM;OAAK,CAAA;MAClC,CAAA;MAER,kBAAC,QAAD;OACE,OAAO;QACL,MAAM;QACN,YAAY;QACZ,UAAU;QACV,cAAc;OAChB;iBAEC,EAAE;MACC,CAAA;MACL,EAAE,SACD,kBAAC,QAAD;OACE,OAAO;QACL,OAAO;QACP,QAAQ;QACR,cAAc;QACd,YAAY;QACZ,YAAY;OACd;OACA,OAAM;MACP,CAAA;MAEF,KACC,kBAAC,UAAD;OACE,UAAU,MAAM;QAEd,AADA,EAAE,gBAAgB,GAClB,EAAQ,CAAC;OACX;OACA,cAAY,SAAS,EAAE;OACvB,OAAM;OACN,OAAO;QACL,SAAS;QACT,YAAY;QACZ,QAAQ;QACR,SAAS;QACT,cAAc;QACd,QAAQ;QACR,OAAO;QACP,SAAS,IAAW,IAAI;OAC1B;OACA,eAAe,MAAM;QAEnB,AADA,EAAE,cAAc,MAAM,aAAa,oBACnC,EAAE,cAAc,MAAM,QAAQ;OAChC;OACA,eAAe,MAAM;QAEnB,AADA,EAAE,cAAc,MAAM,aAAa,eACnC,EAAE,cAAc,MAAM,QAAQ;OAChC;iBAEA,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MACb,CAAA;KAEP;OA5FE,EAAE,EA4FJ;GAET,CAAC,GACD,kBAAC,UAAD;IACE,KAAK;IACL,SAAS;IACT,OAAM;IACN,OAAO;KACL,OAAO;KACP,YAAY;KACZ,QAAQ;KACR,aAAa;KACb,QAAQ;KACR,OAAO;KACP,SAAS;KACT,YAAY;KACZ,gBAAgB;IAClB;cAEA,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GAChB,CAAA,CACL;MACL,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IACL,SAAS;IACT,UAAU;IACV,OAAO;IACP,SAAS;IACT,YAAY;IACZ,KAAK;IACL,YAAY;IACZ,YAAY;GACd;aAXF;IAaE,kBAAC,QAAD;KACE,eAAY;KACZ,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,OAAO;MACP,YAAY,MAAc,WAAW,MAAM;KAC7C;eARF,CAUE,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAA4B,EAAK;OAAO;OAAE;MAAkB;SAC5D,kBAAC,QAAD;MAAM,WAAU;MAAsB,OAAO,EAAE,OAAO,gBAAgB;gBAAG;KAAU,CAAA,CAC/E;;IACN,kBAAC,QAAD;KACE,WAAU;KACV,OAAO;MAAE,OAAO;MAAG,QAAQ;MAAI,YAAY;KAAgB;IAC5D,CAAA;IACD,kBAAC,UAAD;KACE,KAAK;KACL,eAAe,EAAa,EAAI;KAChC,OAAM;KACN,cAAW;KACX,eAAY;KACZ,OAAO;MACL,OAAO;MACP,QAAQ;MACR,QAAQ;MACR,YAAY;MACZ,cAAc;MACd,QAAQ;MACR,OAAO;MACP,SAAS;MACT,YAAY;MACZ,gBAAgB;KAClB;KACA,eAAe,MAAM;MACnB,EAAE,cAAc,MAAM,aAAa;KACrC;KACA,eAAe,MAAM;MACnB,EAAE,cAAc,MAAM,aAAa;KACrC;eAEA,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAChB,CAAA;IACR,kBAAC,IAAD;KACE,MAAM;KACN,QAAQ,EAAS;KACjB,eAAe,EAAa,EAAK;KAChB;KACD;KACD;KACf,UAAU;IACX,CAAA;GACE;IACF;;AAET;AAYA,SAAS,GAAe,EACtB,SACA,WACA,YACA,oBACA,mBACA,kBACA,eACsB;CACtB,OACE,kBAAC,IAAD;EAAgB;EAAe;EAAiB;EAAQ,OAAM;EAAM,OAAO;YAA3E;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACzB,UAAU,CAAC;IACX,OAAO,IAAkB,KAAA,IAAY;IACrC,eAAe,EAAS,SAAS;cAClC;GAES,CAAA;GACV,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACzB,UAAU,CAAC;IACX,OAAO,IAAiB,KAAA,IAAY;IACpC,eAAe,EAAS,QAAQ;cACjC;GAES,CAAA;GACV,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACzB,UAAU,CAAC;IACX,OAAO,IAAgB,KAAA,IAAY;IACnC,eAAe,EAAS,OAAO;cAChC;GAES,CAAA;GACV,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;GACnB,kBAAC,GAAD;IAAU,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;IAAG,QAAA;IAAO,eAAe,EAAS,KAAK;cAAG;GAEvE,CAAA;EACF;;AAEd;;;ACnWA,SAAgB,GAAqB,EACnC,YACA,aACA,iBACA,mBAC4B;CAU5B,IATA,QAAgB;EACd,IAAI,CAAC,GAAS;EACd,IAAM,KAAa,MAAyB;GAC1C,AAAI,EAAM,QAAQ,YAAU,EAAS;EACvC;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B,OAAO,oBAAoB,WAAW,CAAS;CAC9D,GAAG,CAAC,GAAS,CAAQ,CAAC,GAElB,CAAC,GAAS,OAAO;CAErB,IAAM,IAAa,EAAQ,IAAI,QACzB,IAAa,EAAQ,UAAU,QAC/B,IAAa,EAAQ,SAAS,QAC9B,IAAQ,SAAS,EAAW,GAAG,MAAe,IAAI,QAAQ,OAAO;CAEvE,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,cAAY;EACZ,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,UAAU;GACV,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;EACZ;YAjBF;GAmBE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAAnF;KACE,kBAAC,QAAD;MACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc;OACd,YAAY;OACZ,OAAO;OACP,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,YAAY;MACd;gBAEA,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;KACrB,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO;OAAE,MAAM;OAAG,UAAU;MAAE;gBAAnC,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO;OAAc;iBAAI;MAAW,CAAA,GACjF,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;QAAiB,WAAW;OAAE;iBAAnE;QACG;QAAW;QAAE,MAAe,IAAI,YAAY;QAAY;OACtD;QACF;;KACL,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;MAAU,OAAM;KAAU,CAAA;IACrE;;GACL,kBAAC,OAAD;IAAK,OAAO,EAAE,SAAS,cAAc;cAArC,CACE,kBAAC,OAAD;KACE,OAAO;MACL,YAAY;MACZ,QAAQ;MACR,cAAc;MACd,SAAS;MACT,WAAW;MACX,UAAU;KACZ;eAEC,EAAQ,UAAU,KAAK,MACtB,kBAAC,OAAD;MAEE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,KAAK;OACL,SAAS;OACT,UAAU;OACV,OAAO;OACP,UAAU;MACZ;gBAVF;OAYE,kBAAC,QAAD,EACE,OAAO;QACL,OAAO;QACP,QAAQ;QACR,cAAc;QACd,YAAY;QACZ,YAAY;OACd,EACD,CAAA;OACA,EAAI,QAAQ,kBAAC,GAAD;QAAa,MAAM,EAAI;QAAM,MAAM;QAAI,OAAO,EAAE,OAAO,gBAAgB;OAAI,CAAA;OACxF,kBAAC,QAAD;QAAM,OAAO;SAAE,YAAY;SAAU,UAAU;SAAU,cAAc;QAAW;kBAC/E,EAAI;OACD,CAAA;MACH;QAxBE,EAAI,EAwBN,CACN;IACE,CAAA,GACJ,IAAa,KACZ,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAM,OAAO;MAAiB,WAAW;KAAG;eAApE;MAAuE;MAC9D;MAAW;MAAE,MAAe,IAAI,YAAY;MAAY;KAC5D;MAEJ;;GACL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,WAAW;KACX,SAAS;KACT,YAAY;KACZ,KAAK;KACL,YAAY;IACd;cARF;KAUE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MAAK,SAAS;gBAAU;KAAW,CAAA;KAClC,IAAa,KACZ,kBAAC,GAAD;MAAK,WAAA;MAAU,SAAS;gBAAc;KAEjC,CAAA;KAEP,kBAAC,GAAD;MAAK,MAAK;MAAU,QAAA;MAAO,SAAS;gBACjC,IAAa,IAAI,gBAAgB;KAC/B,CAAA;IACF;;EACF;GACL,EAAA,CAAA;AAEN;;;AClJA,SAAgB,KAAsB;CACpC,IAAM,EAAE,SAAM,SAAM,kBAAe,GAAiB,GAC9C,CAAC,GAAK,KAAU,QAAe,KAAK,IAAI,CAAC;CAQ/C,IANA,QAAgB;EACd,IAAI,CAAC,GAAM;EACX,IAAM,IAAK,OAAO,kBAAkB,EAAO,KAAK,IAAI,CAAC,GAAG,GAAI;EAC5D,aAAa,OAAO,cAAc,CAAE;CACtC,GAAG,CAAC,CAAI,CAAC,GAEL,CAAC,GAAM,OAAO;CAElB,IAAM,IAAY,IAAI,KAAK,EAAK,UAAU,EAAE,QAAQ,GAC9C,IAAc,KAAK,IAAI,GAAG,IAAY,CAAG,GACzC,IAAU,KAAK,MAAM,IAAc,GAAK,GACxC,IAAU,KAAK,MAAO,IAAc,MAAS,GAAI,GACjD,IAAY,GAAG,OAAO,CAAO,EAAE,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,CAAO,EAAE,SAAS,GAAG,GAAG;CAExF,OACE,kBAAC,OAAD;EACE,MAAK;EACL,aAAU;EACV,eAAY;EACZ,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,OAAO;GACP,YAAY;GACZ,OAAO;GACP,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,UAAU;GACV,YAAY;GACZ,cAAc;GACd,QAAQ;EACV;YAnBF;GAqBE,kBAAC,QAAD;IAAM,eAAY;cAAO;GAAO,CAAA;GAChC,kBAAC,QAAD,EAAA,UAAA,CAAM,sBACc,kBAAC,UAAD;IAAQ,eAAY;cAAwB,EAAK,qBAAqB,EAAK;GAAuB,CAAA,CAChH,EAAA,CAAA;GACN,kBAAC,QAAD;IAAM,OAAO,EAAE,SAAS,IAAK;cAA7B,CAAgC,iBACjB,kBAAC,UAAD,EAAA,UAAS,EAAK,mBAAmB,EAAK,aAAqB,CAAA,CACpE;;GACN,kBAAC,QAAD;IAAM,OAAO,EAAE,SAAS,IAAK;cAA7B,CAAgC,cAAW,EAAK,MAAa;;GAC7D,kBAAC,QAAD,EAAM,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;GAC3B,kBAAC,QAAD;IAAM,eAAY;IAA0B,cAAW;cAAvD,CAAwE,YAC7D,CACL;;GACN,kBAAC,UAAD;IACE,eAAe,EAAK;IACpB,UAAU;IACV,eAAY;IACZ,OAAO;KACL,YAAY;KACZ,OAAO;KACP,QAAQ;KACR,SAAS;KACT,cAAc;KACd,UAAU;KACV,YAAY;KACZ,QAAQ,IAAa,SAAS;IAChC;cAEC,IAAa,YAAY;GACpB,CAAA;EACL;;AAET;;;ACtEA,eAAsB,KAAmD;CAGvE,IAAM,KAAO,MAFK,GAA6C,oBAAoB,GAElE;CACjB,IAAI,MAAM,QAAQ,CAAI,GACpB,OAAO,EAAE,YAAY,EAAsB;CAE7C,IAAM,IAAO,KAAQ,CAAC;CACtB,OAAO;EACL,YAAY,EAAI,cAAc,CAAC;EAC/B,SAAS,EAAI;EACb,UAAU,EAAI;EACd,UAAU,EAAI;EACd,MAAM,EAAI;CACZ;AACF;AAQA,eAAsB,GAAW,GAAoC;CAInE,QAAO,MAHW,GAChB,eAAe,mBAAmB,CAAI,GACxC,GACW;AACb;AAcA,eAAsB,GAAa,GAAgB,GAAc,GAA+B;CAC9F,MAAM,EAAc,iBAAiB;EACnC,YAAY,CAAC,CAAM;EACnB,QAAQ;EACR,QAAQ,EAAE,SAAS,CAAC;GAAE,YAAY;GAAM;EAAM,CAAC,EAAE;CACnD,CAAC;AACH;;;AC1DA,SAAgB,KAAkB;CAChC,OAAO,GAAS;EACd,UAAU,CAAC,eAAe;EAC1B,SAAS;EACT,WAAW,MAAS;CACtB,CAAC;AACH;AAOA,SAAgB,GAAiB,GAAc;CAC7C,OAAO,GAAS;EACd,UAAU,CAAC,UAAU,CAAI;EACzB,eAAe,GAAW,CAAI;EAC9B,SAAS,CAAC,CAAC;EACX,WAAW,KAAK;CAClB,CAAC;AACH;AA8BA,SAAgB,GAAY,GAAc,GAMxC;CACA,IAAM,IAAI,GAAS;EACjB,UAAU,CAAC,UAAU,CAAI;EACzB,eAAe,GAAW,CAAI;EAC9B,SAAS,CAAC,CAAC;EACX,WAAW,KAAK;CAClB,CAAC,GACK,IAAS,EAAE,MACX,IAAQ,CAAC,CAAC,GAAQ;CAExB,OAAO;EACL,OAFY,IAAS,EAAQ,QAAc;EAG3C;EACA,QAAQ,GAAQ;EAChB,SAAS,EAAE;EACX,OAAQ,EAAE,SAA0B;CACtC;AACF;;;AC/DA,SAAgB,KAAqB;CACnC,IAAM,EAAE,YAAS,GAAiB,GAC5B,EAAE,OAAO,MAAqB,GAClC,IAAO,oCAAoC,IAC3C,EACF;CAmBA,OAjBA,QAAgB;EACd,IAAI,CAAC,GAAM;GACT,SAAS,KAAK,gBAAgB,gCAAgC;GAC9D;EACF;EAMA,OALI,IACF,SAAS,KAAK,aAAa,kCAAkC,MAAM,IAEnE,SAAS,KAAK,gBAAgB,gCAAgC,SAEnD;GACX,SAAS,KAAK,gBAAgB,gCAAgC;EAChE;CACF,GAAG,CAAC,GAAM,CAAgB,CAAC,GAEtB,IAGH,kBAAC,OAAD;EACE,eAAY;EACZ,eAAY;EACZ,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,eAAe;GACf,QAAQ;EACV;CACD,CAAA,IAhBe;AAkBpB;;;ACvCA,SAAgB,GACd,GACA,GACA,GACA,GACwB;CACxB,IAAI,MAAU,aAAa,EAAK,UAAU,GAAG,OAAO;CACpD,IAAM,IAAM,GAAoB,GAAM,GAAa,GAAO,CAAQ;CAClE,IAAI,EAAI,WAAW,GAAG,OAAO;CAE7B,IAAM,IAAQ,IAAI,IAAI,CAAG,GACnB,IAAY,EAAK,QAAQ,MAAM,EAAM,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,GACzD,IAAW,IAAI,IAAI,EAAU,KAAK,MAAM,EAAE,EAAE,CAAC;CACnD,OAAO;EACL;EACA;EACA,UAAU,EAAI,QAAQ,MAAO,CAAC,EAAS,IAAI,CAAE,CAAC;CAChD;AACF;AAEA,SAAgB,GAAc,GAAwC;CAEpE,OADK,IACE;EACL,UAAU,EAAQ;EAClB,QAAQ,EAAQ,UAAU,KAAA;CAC5B,IAJqB;EAAE,UAAU;EAAK,SAAS;CAAK;AAKtD;;;ACHA,SAAS,GAAY,GAA0B,IAAO,IAAI;CACxD,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAI,EAAK;CAEf,OADI,IAAU,kBAAC,GAAD,EAAS,QAAO,CAAA,IACvB,kBAAC,GAAD;EAAmB;EAAY;CAAO,CAAA;AAC/C;AAEA,SAAS,GACP,GACA,GACA,GACO;CACP,IAAI,CAAC,KAAc,CAAC,GAAS,OAAO,CAAC;CACrC,IAAM,IAAa,CAAC,GACd,KAAS,MAAe;EAC5B,IAAI,EAAE,QAAQ;GACZ,IAAM,IAAI,EAAQ,EAAE;GACpB,IAAI,GAAG;IACL,IAAM,IAAQ,EAAI,EAAE,IAAI,KAAK,EAAE;IAQ/B,AAPA,EAAI,KAAK;KACP,OAAO;KACP,MAAM,GAAY,EAAE,QAAQ,EAAE,SAAS,QAAQ,QAAQ;KACvD,OAAO,SAAS;KAChB,WAAW,EAAS;MAAE,MAAM;MAAO,IAAI,EAAE;MAAQ,OAAO,EAAE;KAAM,CAAC;KACjE,YAAY;IACd,CAAC,GACG,EAAE,SAAS,UAAU,EAAE,UACzB,EAAI,KAAK;KACP,OAAO;KACP,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAC5B,OAAO,OAAO;KACd,WAAW,EAAS;MAAE,MAAM;MAAO,IAAI,EAAE;MAAQ,OAAO,EAAE;KAAM,CAAC;KACjE,YAAY;IACd,CAAC;GAEL;EACF;EACA,KAAK,IAAM,KAAK,EAAE,SAAS,CAAC,GAAG,EAAM,CAAC;EACtC,KAAK,IAAM,KAAK,EAAE,YAAY,CAAC,GAAG,EAAM,CAAC;CAC3C;CACA,KAAK,IAAM,KAAK,GAAY,EAAM,CAAC;CACnC,OAAO;AACT;AAEA,SAAgB,GAAe,EAC7B,SACA,YACA,aACA,UAAO,WACP,eACA,YACA,eAAY,IACZ,YAAS,MACa;CACtB,IAAM,CAAC,GAAG,KAAQ,EAAS,EAAE,GACvB,CAAC,GAAK,KAAU,EAAS,CAAC,GAC1B,IAAW,EAAyB,IAAI;CAE9C,QAAgB;EACd,AAAI,MACF,iBAAiB,EAAS,SAAS,MAAM,GAAG,EAAE,GAC9C,EAAK,MAAS,QAAQ,SAAS,EAAE,GACjC,EAAO,CAAC;CAEZ,GAAG,CAAC,GAAM,CAAI,CAAC;CAEf,IAAM,IAAW,QAAqB;EACpC,IAAM,IAAc,CAAC;EAiBrB,OAhBA,EAAK,KAAK,GAAG,GAAiB,GAAY,GAAS,CAAQ,CAAC,GACxD,KACF,EAAK,KACH;GAAE,OAAO;GAAM,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GAAG,OAAO;GAA8B,MAAM;GAAK,WAAW,EAAS;IAAE,MAAM;IAAM,GAAG;GAAe,CAAC;GAAG,YAAY;EAAK,GAC1K;GAAE,OAAO;GAAM,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GAAG,OAAO;GAA+B,MAAM;GAAK,WAAW,EAAS,EAAE,MAAM,WAAW,CAAC;GAAG,YAAY;EAAW,GACpK;GAAE,OAAO;GAAM,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GAAG,OAAO;GAAuC,MAAM;GAAK,WAAW,EAAS,EAAE,MAAM,WAAW,CAAC;GAAG,YAAY;EAAW,CAC9K,GAEF,EAAK,KACH;GAAE,OAAO;GAAQ,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,OAAO;GAAkB,WAAW,EAAS;IAAE,MAAM;IAAa,eAAe;GAAM,CAAC;GAAG,YAAY;EAAY,GAChK;GAAE,OAAO;GAAQ,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,OAAO;GAAoB,WAAW,EAAS;IAAE,MAAM;IAAa,eAAe;GAAS,CAAC;GAAG,YAAY;EAAY,GACrK;GAAE,OAAO;GAAQ,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,OAAO;GAA2B,WAAW,EAAS;IAAE,MAAM;IAAa,eAAe;GAAQ,CAAC;GAAG,YAAY;EAAY,GAC3K;GAAE,OAAO;GAAY,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;GAAG,OAAO;GAAoB,WAAW,EAAS;IAAE,MAAM;IAAO,IAAI;IAAe,OAAO;GAAe,CAAC;GAAG,YAAY;EAAM,GACrL;GAAE,OAAO;GAAY,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GAAG,OAAO;GAAoB,WAAW,EAAS,EAAE,MAAM,QAAQ,CAAC;GAAG,YAAY;EAAQ,CAC7I,GACI,MAAS,QAAc,EAAK,QAAQ,MAAM,EAAE,eAAe,KAAK,IAC7D;CACT,GAAG;EAAC;EAAY;EAAS;EAAW;EAAU;CAAI,CAAC,GAE7C,IAAY,IAEd,EAAS,QACN,MACC,EAAE,MAAM,YAAY,EAAE,SAAS,EAAE,YAAY,CAAC,KAC9C,EAAE,MAAM,YAAY,EAAE,SAAS,EAAE,YAAY,CAAC,CAClD,IALA,GAME,IAAsD,CAAC;CAuB7D,OAtBA,EAAS,SAAS,GAAG,MAAM;EAEzB,AADA,EAAO,EAAE,SAAS,EAAO,EAAE,UAAU,CAAC,GACtC,EAAO,EAAE,OAAO,KAAK;GAAE,GAAG;GAAG,IAAI;EAAE,CAAC;CACtC,CAAC,GAEI,IAkBH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO;GACP,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;GACV,SAAS;GACT,eAAe;GACf,WAAW;EACb;EACW,YA9CE,MAAqC;GACtD,AAAI,EAAE,QAAQ,eACZ,EAAE,eAAe,GACjB,EAAO,KAAK,IAAI,IAAM,GAAG,EAAS,SAAS,CAAC,CAAC,KACpC,EAAE,QAAQ,aACnB,EAAE,eAAe,GACjB,EAAO,KAAK,IAAI,IAAM,GAAG,CAAC,CAAC,KAClB,EAAE,QAAQ,WAAW,EAAS,MACvC,EAAS,GAAK,IAAI,GAClB,EAAQ,KACC,EAAE,QAAQ,YACnB,EAAQ;EAEZ;YAcI;GAqBE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,SAAS;KACT,KAAK;KACL,cAAc;IAChB;cAPF;KASE,kBAAC,EAAK,SAAN;MAAc,MAAM;MAAI,OAAO,EAAE,OAAO,gBAAgB;KAAI,CAAA;KAC5D,kBAAC,SAAD;MACE,KAAK;MACL,OAAO;MACP,WAAW,MAAM;OAEf,AADA,EAAK,EAAE,OAAO,KAAK,GACnB,EAAO,CAAC;MACV;MACA,aAAa,IAAY,0CAA0C;MACnE,OAAO;OACL,MAAM;OACN,QAAQ;OACR,SAAS;OACT,YAAY;OACZ,UAAU;OACV,OAAO;MACT;KACD,CAAA;KACD,kBAAC,IAAD,EAAA,UAAK,MAAQ,CAAA;IACV;;GACL,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,WAAW;KAAQ,SAAS;IAAE;cAArD,CACG,OAAO,QAAQ,CAAM,EAAE,KAAK,CAAC,GAAG,OAC/B,kBAAC,OAAD;KAAa,OAAO,EAAE,cAAc,EAAE;eAAtC,CACE,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,UAAU;OACV,YAAY;OACZ,OAAO;OACP,eAAe;OACf,eAAe;MACjB;gBAEC;KACE,CAAA,GACJ,EAAM,KAAK,MACV,kBAAC,OAAD;MAEE,eAAe;OAEb,AADA,EAAE,IAAI,GACN,EAAQ;MACV;MACA,oBAAoB,EAAO,EAAE,EAAE;MAC/B,OAAO;OACL,SAAS;OACT,YAAY;OACZ,KAAK;OACL,SAAS;OACT,cAAc;OACd,QAAQ;OACR,YAAY,EAAE,OAAO,IAAM,uBAAuB;OAClD,OAAO,EAAE,OAAO,IAAM,kBAAkB;MAC1C;gBAhBF;OAkBE,kBAAC,QAAD;QACE,OAAO;SACL,OAAO,EAAE,OAAO,IAAM,kBAAkB;SACxC,SAAS;QACX;kBAEC,EAAE;OACC,CAAA;OACN,kBAAC,QAAD;QAAM,OAAO;SAAE,MAAM;SAAG,UAAU;QAAG;kBAAI,EAAE;OAAY,CAAA;OACtD,EAAE,QACD,kBAAC,QAAD;QAAM,WAAU;QAAO,OAAO;SAAE,UAAU;SAAM,OAAO;QAAgB;kBACpE,EAAE;OACC,CAAA;MAEL;QA/BE,GAAG,EAAE,MAAM,GAAG,EAAE,OA+BlB,CACN,CACE;OAhDK,CAgDL,CACN,GACA,EAAS,WAAW,KACnB,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,WAAW;MACX,OAAO;MACP,UAAU;KACZ;eANF;MAQE,kBAAC,EAAK,SAAN;OAAc,MAAM;OAAI,OAAO;QAAE,OAAO;QAAiB,cAAc;OAAE;MAAI,CAAA;MAC7E,kBAAC,OAAD,EAAA,UAAM,IAAY,sBAAsB,EAAO,KAAK,aAAkB,CAAA;MACrE,KACC,kBAAC,OAAD;OAAK,OAAO;QAAE,YAAY;QAAK,OAAO;QAAe,WAAW;OAAE;iBAAlE;QAAqE;QAC3D;QAAE;OACP;;KAEJ;MAEJ;;GACL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,WAAW;KACX,YAAY;KACZ,SAAS;KACT,YAAY;KACZ,KAAK;KACL,UAAU;KACV,OAAO;IACT;cAVF;KAYE,kBAAC,QAAD;MAAM,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;gBAA7D,CACE,kBAAC,IAAD,EAAA,UAAK,KAAO,CAAA,GAAC,WACT;;KACN,kBAAC,QAAD;MAAM,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;gBAA7D,CACE,kBAAC,IAAD,EAAA,UAAK,IAAM,CAAA,GAAC,SACR;;KACL,KACC,kBAAC,QAAD;MAAM,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;gBAA7D,CACE,kBAAC,IAAD,EAAA,UAAK,IAAM,CAAA,GAAC,SACR;;KAER,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,QAAD;MAAM,WAAU;gBAAO;KAAe,CAAA;IACnC;;EACF;GACL,EAAA,CAAA,IAjLc;AAmLpB;;;ACjOA,eAAsB,GAAmB,GAGV;CAM7B,QAAO,MALW,GAAa,sBAAsB;EACnD,QAAQ;EACR,GAAI,EAAM,aAAa,EAAE,YAAY,EAAM,WAAW,IAAI,CAAC;EAC3D,GAAI,EAAM,UAAU,EAAE,SAAS,EAAM,QAAQ,IAAI,CAAC;CACpD,CAAC,GACU;AACb;AAYA,eAAsB,GACpB,GACA,GAKA,GACuB;CASvB,QAAO,MADW,GAAa,iBAAiB;EAN9C,MAAM;EACN,SAAS,CAAC;GAAE,MAAM;GAAQ;EAAK,CAAC;EAChC,iBAAiB;EAEjB,KAAK;CAEyC,CAAG,GACxC;AACb;AAOA,eAAsB,GACpB,GAGA,GACA,GACA,GACuB;CAQvB,QAAO,MADW,GAAa,iBAAiB;EAL9C,MAAM;EACN,SAAS,CAAC;GAAE,MAAM;GAAmB,IAAI;GAAW;EAAK,CAAC;EAC1D,iBAAiB;EACjB,KAAK;CAEyC,CAAG,GACxC;AACb;AAEA,eAAsB,GACpB,GACA,GACe;CACf,IAAM,IAAiC,EAAE,QAAQ,WAAW;CAE5D,AADI,MAAO,EAAM,SAAS,IAC1B,MAAM,EAAa,yBAAyB,GAAI,CAAK;AACvD;AAEA,eAAsB,GAAiB,GAAY,GAA8B;CAC/E,MAAM,EAAa,yBAAyB,GAAI;EAC9C,QAAQ;EACR,GAAI,IAAO,EAAE,UAAU,EAAE,QAAQ,EAAK,EAAE,IAAI,CAAC;CAC/C,CAAC;AACH;AAEA,eAAsB,GAAmB,GAA2B;CAClE,MAAM,EAAa,sBAAsB,GAAI,EAAE,QAAQ,YAAY,CAAC;AACtE;AAEA,eAAsB,KAAkD;CAMtE,QAAQ,MALU,EAAa,sBAAsB;EACnD,MAAM;EACN,MAAM;EACN,UAAU;CACZ,CAAC,GACW,QAAQ,CAAC;AACvB;AAMA,eAAsB,GAAiB,GAAuC;CAC5E,MAAM,EAAc,6BAA6B,EAC/C,MAAM,EAAE,iBAAiB,EAAe,EAC1C,CAAC;AACH;AAQA,eAAsB,GAAuB,GAAwD;CASnG,QAAQ,MARU,EAAa,yBAAyB;EACtD,SAAS,CACP;GAAE,OAAO;GAAmB,IAAI;GAAM,OAAO;EAAe,GAC5D;GAAE,OAAO;GAAU,IAAI;GAAM,OAAO;EAAmB,CACzD;EACA,MAAM;EACN,UAAU;CACZ,CAAC,GACW,QAAQ,CAAC;AACvB;AAEA,eAAsB,GAAY,GAAiD;CAUjF,QAAQ,MALU,EAAa,iBAAiB;EAC9C,SAAS,CAAC;GAAE,OAAO;GAAmB,IAAI;GAAM,OAAO;EAAe,CAAC;EACvE,MAAM;EACN,UAAU;CACZ,CAAC,GACW,QAAQ,CAAC;AACvB;;;ACzLA,IAAa,KAAW,IAAI,YAAY,GAC3B,KAAoB,eASpB,KAA8B;AAS3C,SAAgB,GAAe,GAAgC;CAC7D,GAAS,cAAc,IAAI,YAAY,IAAmB,EAAE,UAAO,CAAC,CAAC;AACvE;AAMA,IAAa,KAAb,cAAkC,MAAM;CACtC,cAAc;EAEZ,AADA,MAAM,kBAAkB,GACxB,KAAK,OAAO;CACd;AACF;AAqBA,gBAAuB,GACrB,IAA+B,CAAC,GACN;CAC1B,IAAM,IAAU,IAAI,QAAQ;CAI5B,AAHI,EAAQ,eACV,EAAQ,IAAI,iBAAiB,EAAQ,WAAW,GAE9C,EAAQ,eACV,EAAQ,IAAI,iBAAiB,UAAU,EAAQ,aAAa;CAG9D,IAAM,IAAM,MAAM,MAAM,eAAe;EACrC,aAAa,EAAQ,eAAe;EACpC,QAAQ,EAAQ;EAChB;CACF,CAAC;CAED,IAAI,EAAI,WAAW,KACjB,MAAM,IAAI,GAAa;CAEzB,IAAI,CAAC,EAAI,MAAM,CAAC,EAAI,MAClB,MAAU,MAAM,0BAA0B,EAAI,QAAQ;CAGxD,IAAM,IAAS,EAAI,KAAK,UAAU,GAC5B,IAAU,IAAI,YAAY,GAC5B,IAAS,IACT,IAAe,IACf,IAAc,IACd,IAAY;CAEhB,IAAI;EACF,SAAa;GACX,IAAM,EAAE,SAAM,aAAU,MAAM,EAAO,KAAK;GAC1C,IAAI,GAAM;GAEV,KAAU,EAAQ,OAAO,GAAO,EAAE,QAAQ,GAAK,CAAC;GAChD,IAAM,IAAQ,EAAO,MAAM,IAAI;GAC/B,IAAS,EAAM,IAAI,KAAK;GAExB,KAAK,IAAM,KAAQ,GACjB,IAAI,EAAK,WAAW,SAAS,GAC3B,IAAe,EAAK,MAAM,CAAC;QACtB,IAAI,EAAK,WAAW,MAAM,GAC/B,IAAY,EAAK,MAAM,CAAC;QACnB,IAAI,MAAS,OAClB,IAAY;QACP,IAAI,EAAK,WAAW,QAAQ,GACjC,IAAc,EAAK,MAAM,CAAC;QACrB,IAAI,MAAS,MAAM,GAAc;IACtC,IAAI;KACF,IAAI,MAAiB,mBACnB,MAAM,EAAE,MAAM,kBAAkB;UAC3B,IAAI,MAAiB,gBAAgB,GAC1C,MAAM;MAAE,MAAM;MAAc,SAAS;KAAU;UAC1C,IAAI,MAAiB,uBAAuB,GAAa;MAC9D,IAAM,IAAS,KAAK,MAAM,CAAW;MAIrC,AAAI,EAAO,WACT,MAAM;OACJ,MAAM;OACN,SAAS,KAAa,KAAA;OACtB,QAAQ,EAAO;OACf,aAAa,EAAO;MACtB;KAEJ,OAAO,IAAI,MAAiB,iBAAiB,GAAa;MACxD,IAAM,IAAS,KAAK,MAAM,CAAW;MAMrC,MAAM;OACJ,MAAM;OACN,SAAS,KAAa,KAAA;OACtB,QAAQ,EAAO,UAAU;OACzB,cAAc,EAAO;OACrB,WAAW,EAAO;OAClB,UAAU,EAAO;MACnB;KACF,OAAO,IAAI,MAAiB,oBAAoB,GAAa;MAC3D,IAAM,IAAS,KAAK,MAAM,CAAW;MAYrC,IAAI,EAAO,WAAW,gBAAgB;OACpC,IAAI;OACJ,IAAI;QACF,IAAU,EAAO,IAAI,KAAK,KAAK,MAAM,EAAO,IAAI,EAAE,IAAI,KAAA;OACxD,QAAQ;QACN,IAAU,KAAA;OACZ;OACA,GAAe;QACb,QAAQ,EAAO,IAAI;QACnB,MAAM,EAAO;QACb,QAAQ,EAAO,IAAI;QACnB;OACF,CAAC;MAIH,OAWE,AAVI,EAAO,WAAW,mBAIpB,GAAS,cACP,IAAI,YAAY,IAA6B,EAC3C,QAAQ;OAAE,OAAO,EAAO,IAAI;OAAI,QAAQ,EAAO;MAAO,EACxD,CAAC,CACH,GAEF,MAAM;OACJ,MAAM;OACN,SAAS,KAAa,KAAA;OACtB,QAAQ,EAAO;OACf,KAAK,EAAO;OACZ,SAAS,EAAO;OAChB,QAAQ,EAAO;MACjB;KAEJ;IACF,QAAQ,CAER;IAGA,AAFA,IAAe,IACf,IAAc,IACd,IAAY;GACd;EAEJ;CACF,UAAU;EACR,EAAO,YAAY;CACrB;AACF;;;AC/KA,IAAM,KAA8B;CAClC,cAAc,CAAC;CACf,SAAS,CAAC;CACV,OAAO,CAAC;CACR,WAAW;AACb,GAWM,KAAkC;AAExC,SAAS,GAAuB,GAAkD;CAC3E,OACL,OAAO,GAAG,GAAgC,GAAG;AAC/C;AAEA,SAAS,GAAyB,GAAkD;CAClF,IAAI,OAAO,SAAW,KAAa;CACnC,IAAM,IAAM,GAAuB,CAAQ;CACtC,OACL,IAAI;EACF,OAAO,OAAO,aAAa,QAAQ,CAAG,KAAK,KAAA;CAC7C,QAAQ;EACN;CACF;AACF;AAEA,SAAS,GAA0B,GAA8B,GAA8B;CAC7F,IAAI,OAAO,SAAW,KAAa;CACnC,IAAM,IAAM,GAAuB,CAAQ;CACtC,OACL,IAAI;EACF,AAAI,IAAI,OAAO,aAAa,QAAQ,GAAK,CAAE,IACtC,OAAO,aAAa,WAAW,CAAG;CACzC,QAAQ,CAIR;AACF;AAUA,SAAS,GAA2B,GAAuB;CAOzD,OANI,EAAE,aAAe,OACjB,EAAI,SAAS,0BAAgC,KAK1C,EAAI,QAAQ,SAAS,+BAA+B;AAC7D;AAEA,SAAS,GAAgC,GAAmB,GAAsC;CAChG,OAAO;EACL,IAAI;EACJ,iBAAiB;EACjB,KAAK;EACL,MAAM;EACN,SAAS,CAAC;EACV,6BAAY,IAAI,KAAK,GAAE,YAAY;EACnC,WAAW;CACb;AACF;AAEA,SAAS,GAA+B,GAA0B,GAAmC;CACnG,IAAI,IAAU,IACR,IAAO,EAAS,KAAK,MACrB,EAAQ,SAAS,eAAe,CAAC,EAAQ,aAAa,EAAQ,OAAO,IAAiB,KAC1F,IAAU,IACH;EAAE,GAAG;EAAS,WAAW;CAAM,EACvC;CACD,OAAO,IAAU,IAAO;AAC1B;AAEA,SAAS,GAAyB,GAA0B,GAAmC;CAC7F,IAAI,IAAU,IACR,IAAuB,CAAC;CAC9B,KAAK,IAAM,KAAW,GAAU;EAC9B,IAAI,EAAQ,OAAO,KAAa,EAAQ,SAAS,aAAa;GAC5D,EAAK,KAAK,CAAO;GACjB;EACF;EACA,QAAU,IACN,EAAQ,QAAQ,WAAW,GAG/B;OAAI,EAAQ,WAAW;IACrB,EAAK,KAAK;KAAE,GAAG;KAAS,WAAW;IAAM,CAAC;IAC1C;GACF;GACA,EAAK,KAAK,CAAO;EADjB;CAEF;CACA,OAAO,IAAU,IAAO;AAC1B;AAEA,SAAS,GAAsC,GAA0C;CACvF,IAAI,IAAO;CACX,KAAK,IAAM,KAAW,GAChB,EAAQ,SAAS,eAAe,CAAC,EAAQ,cAC7C,IAAO,GAAyB,GAAM,EAAQ,EAAE;CAElD,OAAO;AACT;AAEA,SAAS,GACP,GACA,GACA,GACA,GACgB;CAChB,IAAI,IAAQ,IACN,IAAO,EAAS,KAAK,MACrB,EAAQ,OAAO,KACnB,IAAQ,IACD,EAAQ,CAAO,KAFe,CAGtC;CAED,OADI,IAAc,IACX,CAAC,GAAG,GAAU,EAAQ,GAAgC,GAAW,CAAc,CAAC,CAAC;AAC1F;AAEA,SAAS,GACP,GACA,GACA,GACA,GACgB;CAChB,IAAI,CAAC,GAAM,OAAO;CAGlB,IAAM,IAAW,EAAS,MAAM,MAAM,EAAE,OAAO,CAAS;CAMxD,OALI,KAAY,CAAC,EAAS,YAAkB,IAKrC,GADM,GAA+B,GAAU,CACxB,GAAM,GAAgB,IAAY,MAAY;EAC1E,IAAM,IAAU,CAAC,GAAG,EAAQ,OAAO,GAC7B,IAAO,EAAQ,EAAQ,SAAS;EAMtC,OALI,GAAM,SAAS,SACjB,EAAQ,EAAQ,SAAS,KAAK;GAAE,GAAG;GAAM,MAAM,EAAK,OAAO;EAAK,IAEhE,EAAQ,KAAK;GAAE,MAAM;GAAQ;EAAK,CAAC,GAE9B;GAAE,GAAG;GAAS,WAAW;GAAM;EAAQ;CAChD,CAAC;AACH;AAEA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACgB;CAChB,IAAM,IAAW,EAAS,MAAM,MAAM,EAAE,OAAO,CAAS;CAGxD,OAFI,KAAY,CAAC,EAAS,YAAkB,IAErC,GADM,GAA+B,GAAU,CACxB,GAAM,GAAgB,IAAY,MAC1D,KAAa,EAAQ,QAAQ,MAAM,MAAU,EAAM,SAAS,cAAc,EAAM,OAAO,CAAS,IAC3F,EAAQ,YAAY,IAAU;EAAE,GAAG;EAAS,WAAW;CAAK,IAE9D;EACL,GAAG;EACH,WAAW;EACX,SAAS,CACP,GAAG,EAAQ,SACX;GACE,MAAM;GACN,IAAI,KAAa,WAAW,EAAU,GAAG,EAAQ,QAAQ;GACzD;GACA;EACF,CACF;CACF,CACD;AACH;AASA,SAAS,GAAiB,GAAsB,GAA+C;CAC7F,IAAM,IAAS,EAAK,KAAK,MAAS,EAAI,YAAY;EAAE,GAAG;EAAK,WAAW;CAAM,IAAI,CAAI,GAC/E,IAAa,IAAI,IAAI,EAAO,KAAK,MAAQ,EAAI,EAAE,CAAC,GAChD,oBAAuB,IAAI,IAAY;CAC7C,KAAK,IAAM,KAAO,GACZ,MAAI,SAAS,QACjB,KAAK,IAAM,KAAS,EAAI,SACtB,AAAI,EAAM,SAAS,iBAAiB,EAAM,MAAI,EAAqB,IAAI,EAAM,EAAE;CAGnF,KAAK,IAAM,KAAW,GAAe;EACnC,IAAI,EAAQ,SAAS,aAAa;GAEhC,IADI,CAAC,EAAQ,aACT,EAAW,IAAI,EAAQ,EAAE,GAAG;GAChC,EAAO,KAAK,CAAO;GACnB;EACF;EACA,IAAI,EAAQ,SAAS,QAAQ;GAC3B,IAAI,EAAW,IAAI,EAAQ,EAAE,GAAG;GAChC,IAAM,IAAa,EAAQ,QAAQ,MAAM,MAAU,EAAM,SAAS,aAAa;GAC/E,IAAI,KAAc,EAAqB,IAAI,EAAW,EAAE,GAAG;GAC3D,EAAO,KAAK,CAAO;EACrB;CACF;CACA,OAAO;AACT;AAEA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACA,GACgB;CAChB,IAAM,IAA+B,CAAC;EACpC,MAAM;EACN,IAAI,KAAa;EACjB;EACA;EACA,UAAU;CACZ,CAAC,GACK,IAAW,EAAS,WAAW,MAAY,EAAQ,OAAO,CAAc;CAC9E,IAAI,KAAY,GAAG;EACjB,IAAM,IAAO,EAAS,MAAM;EAE5B,OADA,EAAK,KAAY;GAAE,GAAG,EAAK;GAAW,MAAM;GAAQ;GAAS,WAAW;EAAM,GACvE;CACT;CACA,OAAO,CAAC,GAAG,GAAU;EACnB,IAAI;EACJ,iBAAiB;EACjB,KAAK;EACL,MAAM;EACN;EACA,6BAAY,IAAI,KAAK,GAAE,YAAY;CACrC,CAAC;AACH;AAQA,SAAS,GACP,GACyE;CACzE,IAAI,GACA,IAAqB;CACzB,KAAK,IAAI,IAAI,EAAS,SAAS,GAAG,KAAK,GAAG,KACxC,IAAI,EAAS,GAAG,SAAS,aAAa;EAEpC,AADA,IAAkB,EAAS,IAC3B,IAAqB;EACrB;CACF;CAEF,IAAI,CAAC,GAAiB,OAAO,CAAC;CAC9B,IAAM,IAAO,EAAgB,QAAQ,SAAS,MAC5C,EAAE,SAAS,cAAc,EAAE,SAAS,gBAAgB,EAAE,KAClD,CAAC;EACC,IAAI,EAAE;EACN,SAAS,OAAQ,EAAE,OAAmC,WAAY,WAC5D,EAAE,MAAkC,UACtC;EACJ,QAAW,EAAE,OAAmC,UAAsC,CAAC;CACzF,CAAC,IACD,CAAC,CACP;CACA,IAAI,EAAK,WAAW,GAAG,OAAO,CAAC;CAC/B,IAAM,oBAAW,IAAI,IAAY;CACjC,KAAK,IAAI,IAAI,IAAqB,GAAG,IAAI,EAAS,QAAQ,KACxD,KAAK,IAAM,KAAS,EAAS,GAAG,SAC9B,AAAI,EAAM,SAAS,qBAAqB,EAAM,MAAI,EAAS,IAAI,EAAM,EAAE;CAG3E,OAAO,EAAK,QAAQ,MAAM,CAAC,EAAS,IAAI,EAAE,EAAE,CAAC;AAC/C;AAEA,SAAS,GAAc,GAA6C;CAClE,IAAM,IAAY,EAAM,SAAS;CACjC,OAAO,OAAO,KAAc,YAAY,IAAY,IAAY,KAAA;AAClE;AAEA,SAAgB,GAAqB,GAAgC;CAOnE,IAAM,IAAW,EAAa,SAAS,EAAE,cAAc,IACjD,CAAC,GAAgB,KAA6B,EAClD,KAAyB,GAAyB,CAAQ,CAC5D,GACM,IAAoB,GACvB,MAA2B;EAE1B,AADA,GAA0B,GAAU,CAAE,GACtC,EAA0B,CAAE;CAC9B,GACA,CAAC,CAAQ,CACX,GACM,CAAC,GAAU,KAAe,EAAyB,CAAC,CAAC,GACrD,CAAC,GAAM,KAAW,EAAyB,EAAW,GACtD,CAAC,GAAe,KAAoB,EAA8B,CAAC,CAAC,GASpE,IAAe,kBAAoB,IAAI,IAAI,CAAC,GAW5C,IAAa,EAAO,CAAC;CAa3B,QAAgB;EACd,IAAI,CAAC,GAAgB;GAEnB,AADA,EAAY,CAAC,CAAC,GACd,EAAQ,EAAW;GACnB;EACF;EACA,IAAI,EAAa,QAAQ,IAAI,CAAc,GAAG;GAC5C,EAAa,QAAQ,OAAO,CAAc;GAI1C;EACF;EACA,IAAI,IAAY;EA0ChB,OAzCA,GAAY,CAAc,EAAE,MACzB,MAAS;GACR,IAAI,GAAW;GACf,IAAM,IAAU,EAAK,KAAK,MAAS,EAAI,YAAY;IAAE,GAAG;IAAK,WAAW;GAAM,IAAI,CAAI;GACtF,EAAY,CAAO;GAMnB,IAAM,IAAO,GAA6B,CAAO;GACjD,AAAI,EAAK,SAAS,KAChB,GAAS,OAAO;IAAE,GAAG;IAAG,SAAS;GAAK,EAAE;EAE5C,IACC,MAAQ;GACH,KACY,GAA6B,WAC9B,QACb,EAAkB,KAAA,CAAS,GAC3B,EAAY,CAAC,CAAC;EAElB,CACF,GACA,GAAuB,CAAc,EAAE,MACpC,MAAS;GACJ,KACA,EAAK,WAAW,KACpB,GAAS,OAAO;IACd,GAAG;IACH,cAAc,EAAK,KAAK,OAAO;KAC7B,IAAI,EAAE;KACN,MAAM,EAAE;KACR,QAAQ,EAAE,UAAU,CAAC;KACrB,QAAQ,EAAE,UAAU,KAAA;KACpB,YAAY,OAAO,EAAE,cAAe,WAAW,EAAE,aAAa,KAAA;IAChE,EAAE;GACJ,EAAE;EACJ,SACM,CAAgE,CACxE,SACa;GACX,IAAY;EACd;CACF,GAAG,CAAC,GAAgB,CAAiB,CAAC;CActC,IAAM,IAAkB,EAA2B,CAAc;CAKjE,AAJA,QAAgB;EACd,EAAgB,UAAU;CAC5B,GAAG,CAAC,CAAc,CAAC,GAEnB,QAAgB;EAOd,IAAM,KAAW,MAAa;GAE5B,IAAM,EAAE,WAAQ,SAAM,WAAQ,eAAY,EAAG;GAIzC,MAAW,EAAgB,WAG/B,EAAa,GAAQ;IAFa;IAAM;IAAQ;GAE3B,CAAK;EAC5B;EACA,GAAS,iBAAiB,IAAmB,CAAO;EAEpD,IAAM,KAAgB,GAAgB,MAA4B;GAChE,QAAQ,EAAM,MAAd;IACE,KAAK;KAkBH,AANA,GAAS,OAAO;MACd,GAAG;MACH,WAAW;MACX,cAAc,EAAE;MAChB,SAAS,EAAE;KACb,EAAE,GACF,GAAa,MAAM,GAA+B,CAAC,CAAC;KACpD;IACF,KAAK,6BAA6B;KAChC,IAAM,IAAY,OAAO,EAAM,SAAS,cAAe,WAAW,EAAM,QAAQ,aAAa;KAC7F,IAAI,CAAC,GAAW;KAChB,GAAa,MACX,GAAuB,GAA+B,GAAG,CAAS,GAAG,GAAQ,IAAY,OAAa;MACpG,GAAG;MACH,WAAW;KACb,EAAE,CACJ;KACA;IACF;IACA,KAAK,cAAc;KACjB,IAAM,IAAO,OAAO,EAAM,SAAS,QAAQ,EAAE,GACvC,IAAY,GAAc,CAAK;KACrC,IAAI,CAAC,KAAa,CAAC,GAAM;KACzB,GAAa,MAAM,GAA8B,GAAG,GAAQ,GAAW,CAAI,CAAC;KAC5E;IACF;IACA,KAAK,kBAAkB;KAOrB,IAAM,IAAK,OAAO,EAAM,SAAS,eAAe,EAAE,GAC5C,IAAU,OAAO,EAAM,SAAS,WAAW,EAAE,GAC7C,IAAU,EAAM,SAAS,UAAsC,CAAC;KACtE,IAAI,CAAC,GAAI;KACT,GAAS,OAAO;MACd,GAAG;MACH,SAAS,CAAC,GAAG,EAAE,SAAS;OAAE;OAAI;OAAS;MAAO,CAAC;KACjD,EAAE;KACF;IACF;IACA,KAAK,gBAAgB;KAOnB,IAAM,IAAS,OAAO,EAAM,SAAS,UAAW,WAAW,EAAM,QAAQ,SAAS,KAAA,GAC5E,IAAa,OAAO,EAAM,SAAS,cAAe,WAAW,EAAM,QAAQ,aAAa,KAAA;KAC9F,GAAS,OAAO;MACd,GAAG;MACH,cAAc,CACZ,GAAG,EAAE,cACL;OACE,IAAI,OAAO,EAAM,SAAS,iBAAiB,EAAE;OAC7C,MAAM,OAAO,EAAM,SAAS,QAAQ,EAAE;OACtC,QAAS,EAAM,SAAS,UAAsC,CAAC;OAC/D;OACA;MACF,CACF;KACF,EAAE;KACF;IACF;IACA,KAAK,iBAAiB;KACpB,IAAM,IAAY,GAAc,CAAK,GAC/B,IAAO,OAAO,EAAM,SAAS,QAAS,WAAW,EAAM,QAAQ,OAAO,IACtE,IAAU,EAAM,SAAS,UAAsC,CAAC,GAChE,IAAY,OAAO,EAAM,SAAS,eAAgB,WAAW,EAAM,QAAQ,cAAc;KAC/F,IAAI,CAAC,KAAa,CAAC,GAAM;KACzB,GAAa,MAAM,GAAqC,GAAG,GAAQ,GAAW,GAAW,GAAM,CAAM,CAAC;KACtG;IACF;IACA,KAAK;IACL,KAAK;IACL,KAAK,iBAAiB;KACpB,IAAM,IAAY,OAAO,EAAM,SAAS,eAAgB,WAAW,EAAM,QAAQ,cAAc,IACzF,IAAO,OAAO,EAAM,SAAS,QAAS,WAAW,EAAM,QAAQ,OAAO,UACtE,IAAS,EAAM,SAAS,WAC5B,OAAO,EAAM,SAAS,SAAU,WAAW,EAAE,OAAO,EAAM,QAAQ,MAAM,IAAI,CAAC,IAEzE,IAAU,EAAM,SAAS,iBAAiB,EAAM,SAAS,aAAa;KAC5E,IAAI,GAAW;MACb,IAAM,IAAiB,cAAc,EAAM,UAAU,OAAO,GAAG;MAC/D,GAAa,MAAM,GAAwB,GAAG,GAAQ,GAAgB,GAAW,GAAM,GAAQ,CAAO,CAAC;KACzG;KACA,GAAS,OAAO;MACd,GAAG;MACH,cAAc,EAAE,aAAa,QAAQ,MAAM,EAAE,OAAO,EAAM,SAAS,aAAa;KAClF,EAAE;KACF;IACF;IACA,KAAK;KACH,GAAS,OAAO;MACd,GAAG;MACH,OAAO,CACL,GAAG,EAAE,OACL;OACE,MAAM,OAAO,EAAM,SAAS,QAAQ,EAAE;OACtC,QAAQ,EAAM,SAAS;OACvB,OAAO,OAAO,EAAM,SAAS,UAAW,WAAW,OAAO,EAAM,QAAQ,MAAM,IAAI,KAAA;MACpF,CACF;KACF,EAAE;KACF;IACF,KAAK,iBAAiB;KAOpB,GAAS,OAMA;MAAE,GAAG;MAAa,cAAc,EAAE;MAAc,SAAS,EAAE;KAAQ,EAC3E;KACD,IAAM,IAAY,GAAc,CAAK;KACrC,AAAI,KACF,GAAa,MAAM,GAAyB,GAAG,CAAS,CAAC;KAE3D;IACF;IACA,KAAK;KAEH,AADA,GAAa,MAAM,GAAsC,CAAC,CAAC,GAC3D,GAAS,OAAO;MAAE,GAAG;MAAG,WAAW;MAAO,OAAO,OAAO,EAAM,SAAS,WAAW,cAAc;KAAE,EAAE;KACpG;GACJ;EACF,GAQM,UAA+B;GACnC,IAAM,IAAK,EAAgB;GAC3B,IAAI,CAAC,GAAI;GACT,IAAM,IAAM,EAAE,EAAW;GACzB,GAAY,CAAE,EAAE,MACb,MAAS;IAOJ,MAAO,EAAgB,WACvB,MAAQ,EAAW,WACvB,GAAa,MAAQ,GAAiB,GAAM,CAAG,CAAC;GAClD,SACM,CAAkD,CAC1D;EACF;EAGA,OAFA,GAAS,iBAAiB,IAA6B,CAAsB,SAEhE;GAEX,AADA,GAAS,oBAAoB,IAAmB,CAAO,GACvD,GAAS,oBAAoB,IAA6B,CAAsB;EAClF;CAIF,GAAG,CAAC,CAAC;CAEL,IAAM,IAAO,EACX,OAAO,GAAc,MAAsC;EAQzD,IAAI,IAAS;EACb,AAAK,MAIH,KAAS,MAHU,GAAmB,EAC3B,WACX,CAAC,GACa,IAId,EAAa,QAAQ,IAAI,CAAM,GAM/B,EAAgB,UAAU,GAC1B,EAAkB,CAAM;EAE1B,IAAM,IAAU,EAAS,SAAS,GAQ9B;EACJ,IAAI;GACF,IAAU,MAAM,GAAY,GAAM,GAAQ,CAAO;EACnD,SAAS,GAAK;GACZ,IAAI,CAAC,GAA2B,CAAG,GAAG,MAAM;GAW5C,AAPA,KAAS,MAHU,GAAmB,EAC3B,WACX,CAAC,GACa,IACd,EAAa,QAAQ,IAAI,CAAM,GAC/B,EAAgB,UAAU,GAC1B,EAAkB,CAAM,GAIxB,IAAU,MAAM,GAAY,GAAM,GAAQ,CAAC;EAC7C;EAYA,GAAa,MACP,EAAE,MAAM,MAAM,EAAE,OAAO,EAAQ,EAAE,IAAU,IACxC,CAAC,GAAG,GAAG;GAAE,GAAG;GAAS,SAAS,CAAC;IAAE,MAAM;IAAQ;GAAK,CAAC;EAAyB,CAAC,CACvF;CACH,GACA;EAAC;EAAgB,EAAS;EAAQ;CAAiB,CACrD,GAUM,IAAU,EAAY,OAAO,GAAsB,MAAoC;EAE3F,AADA,GAAS,OAAO;GAAE,GAAG;GAAG,cAAc,EAAE,aAAa,QAAQ,MAAM,EAAE,OAAO,CAAY;EAAE,EAAE,GAC5F,MAAM,GAAkB,GAAc,CAAK;CAC7C,GAAG,CAAC,CAAC,GAEC,IAAS,EAAY,OAAO,GAAsB,MAAkB;EAExE,AADA,GAAS,OAAO;GAAE,GAAG;GAAG,cAAc,EAAE,aAAa,QAAQ,MAAM,EAAE,OAAO,CAAY;EAAE,EAAE,GAC5F,MAAM,GAAiB,GAAc,CAAI;CAC3C,GAAG,CAAC,CAAC,GAUC,IAAe,EAAY,OAAO,GAAmB,MAAkC;EAC3F,IAAI,CAAC,GAAgB;EACrB,GAAS,OAAO;GAAE,GAAG;GAAG,SAAS,EAAE,QAAQ,QAAQ,MAAM,EAAE,OAAO,CAAS;EAAE,EAAE;EAE/E,IAAM,IAAU,MAAM,GAAmB,GADzB,EAAS,SAAS,GACgC,GAAW,CAAI;EACjF,GAAa,MACP,EAAE,MAAM,MAAM,EAAE,OAAO,EAAQ,EAAE,IAAU,IACxC,CAAC,GAAG,GAAG;GACZ,GAAG;GACH,SAAS,CAAC;IAAE,MAAM;IAAmB,IAAI;IAAW;GAAK,CAAC;EAC5D,CAAC,CACF;CACH,GAAG,CAAC,GAAgB,EAAS,MAAM,CAAC,GAM9B,IAAe,EAAY,OAAO,MAAsB;EACvD,MACL,GAAS,OAAO;GAAE,GAAG;GAAG,SAAS,EAAE,QAAQ,QAAQ,MAAM,EAAE,OAAO,CAAS;EAAE,EAAE,GAE/E,MAAM,GAAmB,GADT,EAAS,SAAS,GACgB,GAAW,EAAE,YAAY,GAAK,CAAC;CACnF,GAAG,CAAC,GAAgB,EAAS,MAAM,CAAC,GAE9B,IAAS,EAAY,YAAY;EACrC,AAAI,KAAgB,MAAM,GAAmB,CAAc;CAC7D,GAAG,CAAC,CAAc,CAAC,GAEb,IAAuB,EAAY,YAAY;EACnD,EAAiB,MAAM,GAAkB,CAAC;CAC5C,GAAG,CAAC,CAAC,GAEC,IAAW,QAAkB;EAGjC,AAFA,EAAkB,KAAA,CAAS,GAC3B,EAAY,CAAC,CAAC,GACd,EAAQ,EAAW;CACrB,GAAG,CAAC,CAAiB,CAAC;CAiBtB,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAjBY,EAAY,YAAY;GAC/B,MACL,GAAS,OAAO;IAAE,GAAG;IAAG,OAAO,KAAA;GAAU,EAAE,GAC3C,MAAM,GAAiB,CAAc;EACvC,GAAG,CAAC,CAAc,CAahB;EACA;EACA;EACA;CACF;AACF;;;ACh0BA,SAAgB,GAAgB,EAAE,YAAS,WAAQ,aAAU,eAAkC;CAC7F,IAAM,CAAC,GAAM,KAAW,QAAwC,GAAkB,CAAM,CAAC,GACnF,CAAC,GAAO,KAAY,EAAwB,IAAI,GAEhD,UAAe;EACnB,IAAM,KAAW,EAAO,YAAY,CAAC,GAAG,QAAQ,MAAM,GAAQ,EAAK,EAAE,CAAC;EACtE,IAAI,EAAQ,SAAS,GAAG;GACtB,EAAS,aAAa,EAAQ,KAAK,IAAI,GAAG;GAC1C;EACF;EACA,EAAS,CAAI;CACf,GAEM,IAAQ,OAAO,QAAQ,EAAO,cAAc,CAAC,CAAC;CAEpD,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAoB;GAAa,CAAA;GAChD,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,CAAC,GAAM,OACjB,kBAAC,IAAD;KAEQ;KACA;KACN,WAAW,EAAO,YAAY,CAAC,GAAG,SAAS,CAAI;KAC/C,OAAO,EAAK;KACZ,WAAW,MAAM,GAAS,OAAO;MAAE,GAAG;OAAI,IAAO;KAAE,EAAE;IACtD,GANM,CAMN,CACF;GACE,CAAA;GACJ,KAAS,kBAAC,OAAD;IAAK,WAAU;cAAiC;GAAW,CAAA;GACrE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,GAAD;KAAK,MAAK;KAAQ,SAAS;eAAU;IAEhC,CAAA,GAEP,kBAAC,GAAD;KAAK,SAAS;eAAQ;IAAW,CAAA,CAC9B;;EACF;;AAET;AAUA,SAAS,GAAc,EAAE,SAAM,SAAM,aAAU,UAAO,eAAwB;CAC5E,IAAM,KAAS,EAAK,SAAS,GAAS,CAAI,MAAM,IAAW,OAAO,KAC5D,IAAO,EAAK;CA8FlB,OA3FI,EAAK,OAEL,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,UAAD;GACE,OAAO,OAAO,KAAS,EAAE;GACzB,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;GACxC,WAAU;aAHZ,CAKE,kBAAC,UAAD;IAAQ,OAAM;cAAG;GAAS,CAAA,GACzB,EAAK,KAAK,KAAK,MACd,kBAAC,UAAD;IAA0B,OAAO,OAAO,CAAG;cACxC,OAAO,CAAG;GACL,GAFK,OAAO,CAAG,CAEf,CACT,CACK;;CACH,CAAA,IAKP,EAAK,SAAS,YAEd,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,SAAD;GACE,MAAK;GACL,SAAS,EAAQ;GACjB,WAAW,MAAM,EAAS,EAAE,OAAO,OAAO;EAC3C,CAAA;CACI,CAAA,IAKP,EAAK,SAAS,WAAW,EAAK,OAAO,OAErC,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,UAAD;GACE,UAAA;GACA,OAAO,MAAM,QAAQ,CAAK,IAAI,EAAM,IAAI,MAAM,IAAI,CAAC;GACnD,WAAW,MAAM;IAEf,EADY,MAAM,KAAK,EAAE,OAAO,eAAe,EAAE,KAAK,MAAM,EAAE,KACrD,CAAG;GACd;GACA,WAAU;aAET,EAAK,MAAM,KAAK,KAAK,MACpB,kBAAC,UAAD;IAA0B,OAAO,OAAO,CAAG;cACxC,OAAO,CAAG;GACL,GAFK,OAAO,CAAG,CAEf,CACT;EACK,CAAA;CACH,CAAA,IAKP,EAAK,SAAS,aAAa,EAAK,SAAS,WAMzC,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,SAAD;GACE,MAAK;GACL,MAAM,EAAK,SAAS,YAAY,IAAI;GACpC,KAAK,EAAK;GACV,KAAK,EAAK;GACV,OAAO,KAAiC,OAAO,KAAK,OAAO,CAAK;GAChE,WAZU,MAAqC;IAEnD,EADU,EAAE,OAAO,UAAU,KAAK,KAAA,IAAY,OAAO,EAAE,OAAO,KAAK,CACzD;GACZ;GAUM,WAAU;EACX,CAAA;CACI,CAAA,IAKP,EAAK,WAAW,cAEhB,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,YAAD;GACE,MAAM;GACN,OAAO,OAAO,KAAS,EAAE;GACzB,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;GACxC,WAAU;EACX,CAAA;CACI,CAAA,IAMT,kBAAC,IAAD;EAAc;EAAa;YACzB,kBAAC,SAAD;GACE,MAAM,EAAK,WAAW,UAAU,UAAU,EAAK,WAAW,QAAQ,QAAQ,EAAK,WAAW,cAAc,mBAAmB;GAC3H,OAAO,OAAO,KAAS,EAAE;GACzB,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;GACxC,WAAU;EACX,CAAA;CACI,CAAA;AAEX;AAEA,SAAS,GAAM,EAAE,UAAO,SAAM,eAAyE;CACrG,OACE,kBAAC,SAAD;EAAO,WAAU;YAAjB;GACE,kBAAC,QAAD;IAAM,WAAU;cAAmD;GAAY,CAAA;GAC9E;GACA,KAAQ,kBAAC,QAAD;IAAM,WAAU;cAA8C;GAAW,CAAA;EAC7E;;AAEX;AAEA,SAAS,GAAS,GAAmB;CACnC,OAAO,EAAE,QAAQ,SAAS,GAAG,EAAE,QAAQ,UAAU,MAAM,EAAE,YAAY,CAAC;AACxE;AAEA,SAAS,GAAQ,GAAqB;CAGpC,OADA,GADI,KAAyB,QAAQ,MAAM,MACvC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW;AAEvC;AAEA,SAAS,GAAkB,GAA6C;CACtE,IAAM,IAA+B,CAAC;CACtC,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAO,cAAc,CAAC,CAAC,GACzD,AAAI,EAAE,YAAY,KAAA,MAAW,EAAI,KAAK,EAAE;CAE1C,OAAO;AACT;;;ACzLA,IAAM,KAAyB;AAE/B,SAAgB,GAAe,EAAE,SAAM,YAAS,YAAS,WAAQ,MAA2B;CAC1F,IAAM,IAAO,GAAqB,GAC5B,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAW,KAAgB,EAAwB,IAAI,GAOxD,IAAY,EAA8B,IAAI,GAC9C,IAAgB,EAAO,EAAI,GAC3B,CAAC,GAAkB,KAAuB,EAAS,EAAK,GAExD,UAAqB;EACzB,IAAM,IAAK,EAAU;EACrB,IAAI,CAAC,GAAI;EAET,IAAM,IADqB,EAAG,eAAe,EAAG,YAAY,EAAG,gBACxB;EAEvC,AADA,EAAc,UAAU,GACpB,KAAY,KAAkB,EAAoB,EAAK;CAC7D,GAEM,KAAkB,IAA2B,aAAa;EAC9D,IAAM,IAAK,EAAU;EAChB,MACL,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc;EAAS,CAAC,GAC9C,EAAc,UAAU,IACxB,EAAoB,EAAK;CAC3B;CAoBA,IAfA,QAAgB;EACd,AAAI,EAAc,UAChB,EAAe,QAAQ,IAEvB,EAAoB,EAAI;CAE5B,GAAG,CAAC,EAAK,QAAQ,CAAC,GAKlB,QAAgB;EACd,AAAI,KAAM,EAAe,SAAS;CACpC,GAAG,CAAC,CAAI,CAAC,GAEL,CAAC,GAAM,OAAO;CAElB,IAAM,IAAO,OAAO,MAAiB;EAC9B,MAAK,KAAK,GAEf;GADA,EAAa,IAAI,GACjB,EAAS,EAAE;GACX,IAAI;IACF,MAAM,EAAK,KAAK,CAAI;GACtB,SAAS,GAAK;IAGZ,AAFA,QAAQ,MAAM,qBAAqB,CAAG,GACtC,EAAa,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG,CAAC,GAC7D,EAAS,CAAI;GACf;EAPW;CAQb,GAEM,KAAa,MAA0C;EAC3D,AAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,aAC1B,EAAE,eAAe,GACjB,EAAU,CAAK;CAEnB,GAEM,IAAe,EAAK,SAAS,WAAW,KAAK,CAAC,EAAK,KAAK,WACxD,IAAc,EAAK,KAAK,WAQxB,IAAkC,EAAK,SAAS,MACnD,MAAY,EAAQ,SAAS,eAAe,EAAQ,aAAa,EAAQ,QAAQ,SAAS,CAC7F,GACM,IAAU,EAAM,KAAK,EAAE,SAAS,KAAK,CAAC;CAE5C,OACE,kBAAC,SAAD;EAAO,OAAO;GACZ,UAAU;GAAS,KAAK;GAAmB,OAAO;GAAG,QAAQ;GAAwB,OAAO;GAAK,QAAQ;GACzG,YAAY;GAAkB,YAAY;GAC1C,SAAS;GAAQ,eAAe;GAAU,WAAW;EACvD;YAJA;GAKE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,SAAS;KAAa,KAAK;KAAG,cAAc;IAA2B;cAA5H;KACE,kBAAC,QAAD;MAAM,OAAO;OACX,OAAO;OAAI,QAAQ;OAAI,cAAc;OACrC,YAAY;OACZ,SAAS;OAAQ,YAAY;OAAU,gBAAgB;MACzD;gBAAG,kBAAC,EAAK,SAAN;OAAc,MAAM;OAAI,OAAO,EAAE,OAAO,2BAA2B;MAAI,CAAA;KAAO,CAAA;KACjF,kBAAC,OAAD;MAAK,OAAO,EAAE,MAAM,EAAE;gBAAtB,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,YAAY;QAAK,OAAO;OAAc;iBAAI;MAAW,CAAA,GACnF,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;QAAiB,SAAS;QAAQ,YAAY;QAAU,KAAK;OAAE;iBAApG;QACE,kBAAC,QAAD,EAAM,OAAO;SAAE,OAAO;SAAG,QAAQ;SAAG,YAAY;SAAkB,cAAc;QAAI,EAAI,CAAA;QAAC;QAChF,kBAAC,QAAD;SAAM,WAAU;mBAAQ,KAAW;QAAiB,CAAA;OAC1D;QACF;;KACJ,EAAK,SAAS,SAAS,KACtB,kBAAC,GAAD;MACE,MAAM;MACN,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAC5B,eAAe,KAAK,GAAe,EAAK,QAAQ;MAChD,OAAM;KACP,CAAA;KAEF,EAAK,kBACJ,kBAAC,GAAD;MAAS,MAAM;MAAI,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAAG,SAAS,EAAK;MAAU,OAAM;KAAoB,CAAA;KAEtG,kBAAC,GAAD;MAAS,MAAM;MAAI,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;MAAG,OAAM;KAAe,CAAA;KAC3E,kBAAC,GAAD;MAAS,MAAM;MAAI,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;MAAS,OAAM;KAAS,CAAA;IAC7E;;GAEL,kBAAC,OAAD;IACE,KAAK;IACL,UAAU;IACV,OAAO;KAAE,MAAM;KAAG,WAAW;KAAQ,SAAS;KAAa,SAAS;KAAQ,eAAe;KAAU,KAAK;KAAI,UAAU;IAAW;cAHrI;KAKG,KACC,kBAAC,IAAD,EAAA,UAAO,kKAEA,CAAA;KAER,EAAK,SAAS,KAAK,MAAM,kBAAC,IAAD,EAAuB,SAAS,EAAI,GAAnB,EAAE,EAAiB,CAAC;KAC9D,EAAK,KAAK,aAAa,KAAK,MAC3B,kBAAC,IAAD;MAA8B,MAAM;MAClC,YAAY,MAAU,KAAK,EAAK,QAAQ,EAAE,IAAI,CAAK;MACnD,WAAW,MAAS,KAAK,EAAK,OAAO,EAAE,IAAI,CAAI;KAAI,GAF7B,EAAE,EAE2B,CACtD;KAKA,EAAK,KAAK,QAAQ,KAAK,MACtB,kBAAC,IAAD;MAA4B,SAAS,EAAE;MAAS,QAAQ,EAAE;MACxD,WAAW,MAAS,KAAK,EAAK,aAAa,EAAE,IAAI,CAAI;MACrD,gBAAgB,KAAK,EAAK,aAAa,EAAE,EAAE;KAAI,GAF3B,EAAE,EAEyB,CAClD;KAIA,KAAe,CAAC,KACf,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAG,YAAY;OAAU,OAAO;OAAiB,UAAU;MAAK;gBAApG,CACE,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,GAAC,WAClB;;KAGN,EAAK,KAAK,SACT,kBAAC,OAAD;MAAK,OAAO;OACV,SAAS;OAAa,YAAY;OAClC,QAAQ;OAAiD,cAAc;OACvE,UAAU;OAAM,OAAO;OACvB,SAAS;OAAQ,eAAe;OAAU,KAAK;MACjD;gBALA,CAME,kBAAC,OAAD,EAAA,UAAA;OAAK,kBAAC,UAAD,EAAA,UAAQ,0BAA+B,CAAA;OAAC;OAAE,EAAK,KAAK;MAAW,EAAA,CAAA,GACpE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,KAAK;OAAE;iBACpC,kBAAC,GAAD;QAAK,MAAK;QAAK,MAAK;QAAU,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;QAAG,eAAe,KAAK,EAAK,MAAM;kBAAG;OAE7F,CAAA;MACF,CAAA,CACF;;KAIN,KACC,kBAAC,OAAD;MAAK,OAAO;OACV,SAAS;OAAY,YAAY;OACjC,QAAQ;OAA2B,cAAc;OACjD,UAAU;OAAM,OAAO;MACzB;gBAJA;OAKE,kBAAC,UAAD,EAAA,UAAQ,eAAoB,CAAA;OAAC;OAAE;MAC5B;;IAEJ;;GAIJ,KACC,kBAAC,UAAD;IACE,eAAe,EAAe,QAAQ;IACtC,OAAO;KACL,UAAU;KAAY,OAAO;KAAI,QAAQ;KACzC,SAAS;KAAY,cAAc;KACnC,YAAY;KAAiB,OAAO;KAA4B,QAAQ;KACxE,UAAU;KAAI,YAAY;KAAK,QAAQ;KACvC,WAAW;KACX,SAAS;KAAe,YAAY;KAAU,KAAK;KAAG,QAAQ;IAChE;cATF,CAWE,kBAAC,EAAK,WAAN,EAAgB,MAAM,GAAK,CAAA,GAAC,gBACtB;;GAGV,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAI,WAAW;KAA4B,YAAY;IAAmB;cAAjG,CACE,kBAAC,OAAD;KAAK,OAAO;MACV,SAAS;MAAQ,YAAY;MAAY,KAAK;MAAG,SAAS;MAAG,YAAY;MACzE,QAAQ;MAA2B,cAAc;KACnD;eAHA;MAIE,kBAAC,IAAD;OACE,OAAO;OACP,WAAW,MAAM,EAAS,EAAE,OAAO,KAAK;OAC7B;OACX,aAAY;OACZ,SAAS;OACT,SAAS;OACT,OAAO;QACL,MAAM;QAAG,QAAQ;QAAG,SAAS;QAAG,YAAY;QAAe,QAAQ;QACnE,UAAU;QAAM,YAAY;QAAK,OAAO;QAAe,SAAS;QAChE,YAAY;OACd;MACD,CAAA;MACD,kBAAC,GAAD;OAAS,MAAM;OAAI,MAAM,kBAAC,EAAK,WAAN,EAAgB,MAAM,GAAK,CAAA;OAAG,OAAM;MAAkB,CAAA;MAM9E,IACC,kBAAC,UAAD;OACE,eAAe,KAAK,EAAK,OAAO;OAChC,cAAW;OACX,OAAM;OACN,OAAO;QACL,OAAO;QAAI,QAAQ;QAAI,cAAc;QAAG,QAAQ;QAAG,QAAQ;QAC3D,YAAY;QAAsB,OAAO;QACzC,SAAS;QAAe,YAAY;QAAU,gBAAgB;OAChE;iBACD,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAS,CAAA,IAE7B,kBAAC,UAAD;OACE,eAAe,KAAK,EAAK,CAAK;OAC9B,UAAU,CAAC;OACX,cAAW;OACX,OAAO;QACL,OAAO;QAAI,QAAQ;QAAI,cAAc;QAAG,QAAQ;QAChD,QAAQ,IAAU,YAAY;QAC9B,YAAY,IAAU,kBAAkB;QACxC,OAAO,IAAU,6BAA6B;QAC9C,SAAS;QAAe,YAAY;QAAU,gBAAgB;OAChE;iBACD,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;MAAS,CAAA;KAElC;QACL,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAG,WAAW;MAAG,UAAU;MAAM,OAAO;KAAgB;eAAlH,CACE,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA,GACxB,kBAAC,QAAD,EAAA,UAAM,0DAA6D,CAAA,CAChE;MACF;;EACA;;AAEX;AAKA,SAAS,KAAiB;CACxB,OACE,kBAAC,QAAD;EACE,eAAA;EACA,OAAO;GACL,SAAS;GACT,OAAO;GAAG,QAAQ;GAAI,YAAY;GAClC,eAAe;GACf,YAAY;GACZ,WAAW;EACb;CACD,CAAA;AAEL;AAGA,SAAS,GAAM,EACb,aACA,kBACA,oBAKC;CACD,OACE,kBAAC,OAAD;EACE,mBAAiB;EACjB,kBAAgB,IAAgB,SAAS,KAAA;EACzC,OAAO;GAAE,SAAS;GAAQ,KAAK;GAAG,YAAY;EAAa;YAH7D,CAKE,kBAAC,QAAD;GAAM,OAAO;IACX,OAAO;IAAI,QAAQ;IAAI,cAAc;IAAG,YAAY;IAAsB,OAAO;IACjF,SAAS;IAAe,YAAY;IAAU,gBAAgB;IAAU,YAAY;IAAG,WAAW;GACpG;aACE,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;EACrB,CAAA,GACN,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;IAAG,UAAU;IAAM,YAAY;IAAM,OAAO;GAAc;GACxF;EACE,CAAA,CACF;;AAET;AAEA,SAAS,GAAW,EAAE,cAAsC;CAU1D,IAAI,EAAQ,SAAS,QAAQ;EAC3B,IAAM,IAAO,GAAY,EAAQ,OAAO,GAClC,IAAa,EAAQ,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM;EAYhE,OAXI,KAAQ,CAAC,IAET,kBAAC,OAAD;GAAK,OAAO;IACV,WAAW;IAAY,UAAU;IAAO,SAAS;IACjD,YAAY;IAAkB,OAAO;IAAqB,cAAc;IACxE,UAAU;IAAM,YAAY;GAC9B;GAAG,mBAAiB,EAAQ;GAAI,kBAAgB,EAAQ,YAAY,SAAS,KAAA;aAC1E;EACE,CAAA,IAGF,kBAAC,IAAD,EAAsB,WAAU,CAAA;CACzC;CAyBA,OAxBI,EAAQ,SAAS,SAIZ,kBAAC,IAAD,EAAsB,WAAU,CAAA,IAErC,EAAQ,SAAS,WAIf,EAAQ,QAAQ,MAAM,MAAM,EAAE,SAAS,UAAU,IAC5C,kBAAC,IAAD,EAAsB,WAAU,CAAA,IAGvC,kBAAC,IAAD;EAAO,eAAe,EAAQ;YAC3B,EAAQ,QAAQ,KAAK,GAAG,MAAM,kBAAC,IAAD,EAAmB,OAAO,EAAI,GAAd,CAAc,CAAC;CACzD,CAAA,IAOP,EAAQ,QAAQ,WAAW,IAAU,OAEvC,kBAAC,IAAD;EAAO,eAAe,EAAQ;EAAI,eAAe,EAAQ;YAAzD,CACG,EAAQ,QAAQ,KAAK,GAAG,MAAM,kBAAC,IAAD,EAAmB,OAAO,EAAI,GAAd,CAAc,CAAC,GAC7D,EAAQ,aAAa,kBAAC,IAAD,CAAiB,CAAA,CAClC;;AAEX;AAEA,SAAS,GAAY,EAAE,cAAsC;CAC3D,OACE,kBAAC,OAAD;EACE,mBAAiB,EAAQ;EACzB,kBAAgB,EAAQ,YAAY,SAAS,KAAA;EAC7C,OAAO;GAAE,WAAW;GAAY,UAAU;GAAO,SAAS;GAAQ,eAAe;GAAU,YAAY;EAAW;YAEjH,EAAQ,QAAQ,KAAK,GAAG,MAAM,kBAAC,IAAD,EAAmB,OAAO,EAAI,GAAd,CAAc,CAAC;CAC3D,CAAA;AAET;AAWA,IAAM,KAAqB;CACzB,KAAK,EAAE,MAAM,GAAO,WAAQ,cAAW,aAAU,GAAG,KAAc;EAiBhE,OAhBI,IAEA,kBAAC,QAAD;GACE,GAAI;GACJ,OAAO;IACL,YAAY;IACZ,SAAS;IACT,cAAc;IACd,YAAY;IACZ,UAAU;GACZ;GAEC;EACG,CAAA,IAIR,kBAAC,OAAD;GACE,OAAO;IACL,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,SAAS;IACT,QAAQ;IACR,WAAW;IACX,UAAU;IACV,YAAY;GACd;aAEA,kBAAC,QAAD;IAAiB;IAAW,GAAI;IAAQ;GAAe,CAAA;EACpD,CAAA;CAET;CACA,EAAE,EAAE,aAAU,GAAG,KAAc;EAC7B,OACE,kBAAC,KAAD;GACE,GAAI;GACJ,QAAO;GACP,KAAI;GACJ,OAAO;IAAE,OAAO;IAAiB,gBAAgB;GAAY;GAE5D;EACA,CAAA;CAEP;CACA,GAAG,EAAE,aAAU,GAAG,KAAc;EAC9B,OAAO,kBAAC,MAAD;GAAI,GAAI;GAAO,OAAO;IAAE,aAAa;IAAI,QAAQ;GAAQ;GAAI;EAAa,CAAA;CACnF;CACA,GAAG,EAAE,aAAU,GAAG,KAAc;EAC9B,OAAO,kBAAC,MAAD;GAAI,GAAI;GAAO,OAAO;IAAE,aAAa;IAAI,QAAQ;GAAQ;GAAI;EAAa,CAAA;CACnF;CACA,EAAE,EAAE,aAAU,GAAG,KAAc;EAC7B,OAAO,kBAAC,KAAD;GAAG,GAAI;GAAO,OAAO,EAAE,QAAQ,QAAQ;GAAI;EAAY,CAAA;CAChE;AACF;AAGA,SAAS,GAAU,EAAE,YAAuC;CAC1D,QAAQ,EAAM,MAAd;EACE,KAAK,QACH,OACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD;IACE,SAAS,EAAM;IACf,SAAQ;IACR,OAAO;IACP,UAAU;IACV,YAAY;GACb,CAAA;EACE,CAAA;EAET,KAAK,YAAY;GAMf,IAAM,IAAU,GAAkB,EAAM,MAAM,EAAM,KAA4C;GAChG,OAAO,kBAAC,IAAD;IAAU,MAAK;IAAO,OAAO,EAAQ;IAAO,KAAK,EAAQ;IAAK,MAAM,EAAM;GAAQ,CAAA;EAC3F;EACA,KAAK,eAKH,OAAO,kBAAC,IAAD;GAAU,MAAK;GAAgB,OADxB,EAAM,SAAS,EAAM,WAAW,UAAU;GACX,MAAM,EAAM;GAAQ,SAAS,CAAC,CAAC,EAAM;EAAW,CAAA;EAE/F,KAAK,mBACH,OAAO,kBAAC,IAAD;GAAU,MAAK;GAAS,OAAM;GAAS,MAAM,EAAM;EAAO,CAAA;EACnE,KAAK,YACH,OAAO,kBAAC,IAAD,EAAqB,SAAQ,CAAA;EACtC,KAAK,WACH,OAAO,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,OAAO;GAAgB;aAArD,CAAwD,MAAG,EAAM,IAAU;;EACpF,KAAK,UACH,OAAO,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,WAAW;IAAU,OAAO;GAAgB;aAA1E,CAA6E,MAAG,EAAM,MAAY;;EAC3G,KAAK,WACH,OAAO,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,WAAW;IAAU,OAAO;GAAgB;aAAI,EAAM;EAAU,CAAA;EACvG,KAAK,cACH,OACE,kBAAC,OAAD;GAAK,WAAU;GAAO,OAAO;IAC3B,WAAW;IAAG,SAAS;IAAY,YAAY;IAC/C,QAAQ;IAA2B,cAAc;IAAG,UAAU;IAAM,WAAW;GACjF;aAAI,EAAM;EAAa,CAAA;EAE3B,SACE,OAAO;CACX;AACF;AAYA,SAAS,GAAS,EAChB,SACA,UACA,QACA,SACA,cAUC;CAGD,OACE,kBAAC,WAAD;EACE,OAAO;GACL,WAAW;GACX,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,UAAU;GAMV,OAAO;GACP,UAAU;GACV,UAAU;GACV,WAAW;GACX,UAAU;EACZ;YAjBF,CAmBE,kBAAC,WAAD;GACE,OAAO;IACL,SAAS;IACT,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,KAAK;IACL,WAAW;IACX,YAAY;IACZ,OAAO;GACT;aAVF;IAYE,kBAAC,QAAD;KAAM,eAAA;KAAY,OAAO;MAAE,OAjCb,IAAU,uBAAuB;MAiCA,YAAY;MAAkC,OAAO;KAAG;eAlC/F,MAAS,SAAS,MAAO,IAAU,MAAM;IAkCsE,CAAA;IACvH,kBAAC,QAAD;KAAM,WAAU;KAAO,OAAO;MAAE,OAAO;MAAiB,MAAM;MAAY,UAAU;MAAG,UAAU;MAAU,cAAc;MAAY,YAAY;KAAS;eAAI;IAAY,CAAA;IACzK,KACC,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,OAAO;MAAiB,WAAW;MAAU,MAAM;MAAY,UAAU;MAAU,cAAc;MAAY,YAAY;KAAS;eAAI;IAAU,CAAA;IAE/K,kBAAC,QAAD,EAAM,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;IAC3B,kBAAC,QAAD;KAAM,eAAA;KAAY,OAAO;MAAE,UAAU;MAAG,OAAO;KAAgB;eAAG;IAAO,CAAA;GAClE;MACT,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IACL,QAAQ;IACR,SAAS;IACT,UAAU;IACV,YAAY;IACZ,OAAO;IACP,WAAW;IACX,YAAY;IACZ,WAAW;GACb;aAEC,KAAK,UAAU,GAAM,MAAM,CAAC;EAC1B,CAAA,CACE;;AAEb;AAEA,SAAS,GAAY,GAAqC;CACxD,OAAO,EAAO,KAAK,MAAO,EAAE,SAAS,SAAS,EAAE,OAAO,EAAG,EAAE,KAAK,EAAE;AACrE;AASA,SAAS,GAAkB,GAAc,GAA6E;CACpH,IAAI,MAAS,iBAAiB;EAC5B,IAAM,IAAO,OAAO,GAAO,QAAS,WAAY,EAAM,OAAkB;EACxE,IAAI,GAAM,OAAO;GAAE,OAAO;GAAM,KAAK;EAAoB;CAC3D;CACA,OAAO,EAAE,OAAO,EAAK;AACvB;AAQA,SAAS,GAAa,EACpB,YAGC;CACD,IAAM,IAAW,EAAM,aAAa,YAC9B,IAAS,IAAW,mBAAmB;CAC7C,OACE,kBAAC,OAAD;EACE,OAAO;GACL,WAAW;GACX,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,YAAY,aAAa;GACzB,cAAc;GACd,UAAU;GACV,SAAS;GACT,YAAY;GACZ,KAAK;GACL,OAAO;EACT;EACA,OAAO,EAAM,aAAa,WAAW,EAAM,eAAe,KAAA;YAd5D;GAgBE,kBAAC,QAAD;IAAM,eAAA;IAAY,OAAO;KAAE,OAAO;KAAQ,YAAY;IAAI;cAAI,IAAW,MAAM;GAAU,CAAA;GACzF,kBAAC,QAAD;IAAM,OAAO,EAAE,YAAY,IAAI;cAAI,IAAW,aAAa;GAAiB,CAAA;GAC5E,kBAAC,QAAD;IAAM,WAAU;IAAO,OAAO;KAAE,OAAO;KAAiB,UAAU;IAAG;cAAI,EAAM;GAAgB,CAAA;GAC9F,EAAM,QACL,kBAAC,QAAD;IAAM,OAAO;KAAE,OAAO;KAAiB,WAAW;IAAS;cAA3D,CAA8D,MAAG,EAAM,IAAW;;EAEjF;;AAET;AAQA,eAAe,GAAe,GAAyC;CACrE,IAAM,IAAkB,CAAC;CACzB,KAAK,IAAM,KAAK,GAAU;EACxB,IAAM,IAAS,IAAI,EAAE,KAAK,IACpB,IAAO,EAAE,QAAQ,IAAI,EAAc,EAAE,QAAQ,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,IAAI;EAC3E,KACL,EAAM,KAAK,GAAG,EAAO,IAAI,GAAM;CACjC;CACA,IAAM,IAAO,EAAM,KAAK,MAAM;CAC9B,IAAI;EACF,MAAM,UAAU,UAAU,UAAU,CAAI;CAC1C,SAAS,GAAK;EACZ,QAAQ,MAAM,gCAAgC,CAAG;CACnD;AACF;AAEA,SAAS,GAAe,GAA8B;CACpD,QAAQ,EAAE,MAAV;EACE,KAAK,QACH,OAAO,EAAE;EACX,KAAK,YAAY;GACf,IAAM,IAAU,GAAkB,EAAE,MAAM,EAAE,KAA4C;GAExF,OAAO,GADM,EAAQ,MAAM,KAAK,EAAQ,MAAM,IAAI,EAAQ,IAAI,KAAK,KAAK,EAAQ,QACjE,IAAI,KAAK,UAAU,EAAE,OAAO,MAAM,CAAC;EACpD;EACA,KAAK,eAEH,OAAO,GAAG,GADM,EAAE,WAAW,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,WAAW,UAAU,YAC7D,IAAI,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;EAErD,KAAK,mBACH,OAAO,WAAW,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;EAClD,KAAK,YACH,OAAO,GAAG,EAAE,aAAa,aAAa,aAAa,WAAW,GAAG,EAAE,YAAY,EAAE,OAAO,MAAM,EAAE,SAAS;EAC3G,KAAK,UACH,OAAO,KAAK,EAAE;EAChB,KAAK,WACH,OAAO,EAAE;EACX,KAAK,cACH,OAAO,EAAE;EACX,KAAK,WACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAuBA,SAAS,GAAkB,EAAE,SAAM,cAAW,eAA2B;CACvE,IAAM,IAAc,KAAK,UAAU,EAAK,UAAU,CAAC,GAAG,MAAM,CAAC,GACvD,CAAC,GAAY,KAAiB,EAAS,CAAW,GAClD,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAY,KAAiB,EAAS,EAAE,GACzC,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,UAAe;EACnB,IAAI,GAAY;EAChB,IAAI;EACJ,IAAI,EAAW,KAAK,KAAK,MAAe,GACtC,IAAI;GACF,IAAQ,KAAK,MAAM,CAAU;EAC/B,SAAS,GAAG;GACV,EAAS,iBAAkB,EAAY,SAAS;GAChD;EACF;EAGF,AADA,EAAc,EAAI,GAClB,EAAU,CAAK;CACjB,GAEM,UAAe;EACf,MACJ,EAAc,EAAI,GAClB,EAAS,EAAW,KAAK,KAAK,KAAA,CAAS;CACzC,GAEM,IAAgB,OAAO,EAAK,cAAe,WAC7C,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAK,UAAU,CAAC,IAAI,GAAG,IAC1D;CAEJ,OACE,kBAAC,OAAD;EAAK,OAAO;GACV,WAAW;GAAG,SAAS;GAAa,YAAY;GAChD,QAAQ;GAA2B,cAAc;EACnD;YAHA;GAIE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,cAAc;IAAE;cAA7E;KACE,kBAAC,EAAK,MAAN;MAAW,MAAM;MAAI,OAAO,EAAE,OAAO,gBAAgB;KAAI,CAAA;KACzD,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAM,YAAY;OAAK,OAAO;MAAc;gBAAG;KAAiB,CAAA;KACzF,kBAAC,QAAD;MAAM,WAAU;MAAO,OAAO;OAAE,UAAU;OAAM,OAAO;MAAgB;gBAAI,EAAK;KAAW,CAAA;KAC1F,MAAkB,QACjB,kBAAC,QAAD;MAAM,OAAO,kBAAkB,EAAc;MAAI,OAAO;OACtD,YAAY;OAAQ,UAAU;OAAI,OAAO;OACzC,SAAS;OAAW,QAAQ;OAA2B,cAAc;MACvE;gBAHA,CAIG,GAAc,aACX;;IAEL;;GAEJ,EAAK,UACJ,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAI,YAAY;KAAM,OAAO;KAAe,cAAc;IAAE;cACjF,EAAK;GACH,CAAA;GAGP,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,GAAe,MAAM,CAAC,CAAC;IACtC,OAAO;KACL,YAAY;KAAe,QAAQ;KAAG,SAAS;KAAG,cAAc,IAAa,IAAI;KACjF,UAAU;KAAM,OAAO;KAAiB,QAAQ;KAChD,SAAS;KAAe,YAAY;KAAU,KAAK;IACrD;cAPF;KASE,kBAAC,QAAD;MAAM,OAAO;OACX,SAAS;OAAgB,WAAW,IAAa,kBAAkB;OACnE,YAAY;OAAmB,UAAU;OAAG,YAAY;MAC1D;gBAAG;KAAO,CAAA;KACT,IAAa,SAAS;KAAc;IAC/B;;GAEP,KACC,kBAAC,YAAD;IACE,OAAO;IACP,WAAW,MAAM,EAAc,EAAE,OAAO,KAAK;IAC7C,MAAM;IACN,WAAU;IACV,OAAO;KACL,OAAO;KAAQ,SAAS;KAAG,UAAU;KAAM,YAAY;KACvD,YAAY;KAAkB,QAAQ;KAA2B,cAAc;KAC/E,OAAO;KAAe,QAAQ;IAChC;GACD,CAAA;GAEF,KAAS,kBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAG,UAAU;KAAM,OAAO;IAAgB;cAAI;GAAW,CAAA;GAE3F,KACC,kBAAC,SAAD;IACE,MAAK;IACL,OAAO;IACP,WAAW,MAAM,EAAc,EAAE,OAAO,KAAK;IAC7C,aAAY;IACZ,WAAA;IACA,OAAO;KACL,WAAW;KAAG,OAAO;KAAQ,SAAS;KAAW,UAAU;KAC3D,YAAY;KAAkB,QAAQ;KACtC,cAAc;KAAG,OAAO;IAC1B;GACD,CAAA;GAGH,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,KAAK;KAAG,WAAW;KAAG,gBAAgB;IAAW;cAAhF,CACI,IAGA,kBAAC,GAAD;KAAK,MAAK;KAAK,MAAK;KAAQ,SAAS;KAAQ,UAAU;eAAY;IAAmB,CAAA,IAFtF,kBAAC,GAAD;KAAK,MAAK;KAAK,MAAK;KAAQ,eAAe,EAAc,EAAI;KAAG,UAAU;eAAY;IAAW,CAAA,GAInG,kBAAC,GAAD;KAAK,MAAK;KAAK,MAAK;KAAU,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;KAAG,SAAS;KAAQ,UAAU;eAAY;IAAY,CAAA,CAC9G;;EACF;;AAET;;;ACt1BA,IAAM,qBAAU,IAAI,IAAyB;AAE7C,SAAgB,GAAoB,GAAc,GAAiC;CAEjF,OADA,GAAQ,IAAI,GAAM,CAAM,SACX;EAEX,AAAI,GAAQ,IAAI,CAAI,MAAM,KAAQ,GAAQ,OAAO,CAAI;CACvD;AACF;AAEA,SAAgB,GAAe,GAAuC;CACpE,OAAO,GAAQ,IAAI,CAAI;AACzB;;;ACAA,IAAM,KAAW,yBACX,KAAkB,gCAElB,KAAa,OAAO,YAAY;AAMtC,SAAgB,GAAY,GAAkB,GAA+B;CAC3E,IAAM,IAAS,EAAS,MAAM,EAAe;CAC7C,IAAI,GAAQ;EACV,IAAM,IAAW,GAAY,EAAO,IAAI,CAAG;EAE3C,OADI,MAAa,KAAmB,IAC7B;CACT;CACA,OAAO,EAAS,QAAQ,KAAW,GAAO,MAAiB;EACzD,IAAM,IAAW,GAAY,GAAM,CAAG;EAEtC,OADI,MAAa,KAAmB,IAC7B,KAAY,OAAO,KAAK,OAAO,CAAQ;CAChD,CAAC;AACH;AAEA,SAAS,GAAY,GAAc,GAAmD;CACpF,IAAI,MAAS,OAAO,OAAO,EAAmB;CAE9C,IAAM,CAAC,GAAM,GAAG,KADF,EAAK,MAAM,GACD;CACxB,IAAI,MAAS,OAEX,OADI,CAAC,EAAI,OAAO,EAAK,WAAW,IAAU,KACnC,GAAK,EAAI,KAAK,CAAI;CAE3B,IAAI,MAAS,WAAW,EAAK,UAAU,GAAG;EACxC,IAAM,CAAC,GAAY,KAAa,GAC1B,IAAS,GAAe,CAAU;EAExC,OADK,IACE,EAAO,SAAS,CAAS,IADZ;CAEtB;CAKA,OAJI,MAAS,WACP,CAAC,EAAI,UAAU,EAAK,WAAW,IAAU,KACtC,GAAK,EAAI,QAAQ,CAAI,IAEvB;AACT;AAEA,SAAS,GAAK,GAA+B,GAAyB;CACpE,OAAO,EAAK,QAAiB,GAAK,MAAQ;EACxC,IAAI,KAAO,OAAO,KAAQ,UAAU,OAAQ,EAAgC;CAE9E,GAAG,CAAI;AACT;AAKA,eAAsB,GAAe,GAAiB,GAAqC;CACzF,KAAK,IAAM,KAAQ,GACjB,IAAI;EACF,MAAM,GAAQ,GAAM,CAAG;CACzB,SAAS,GAAK;EACZ,IAAM,IAAM,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG;EAC3D,EAAM,MAAM,mBAAmB,GAAK;EACpC;CACF;AAEJ;AAEA,eAAe,GAAQ,GAAc,GAAqC;CACxE,IAAI,EAAK,qBAAqB,MAAM;EAClC,IAAM,IAAQ,GAAgB,EAAK,mBAAmB,CAAG;EACzD,MAAM,UAAU,UAAU,UAAU,CAAK;EACzC;CACF;CACA,IAAI,EAAK,WAAW;EAClB,IAAM,EAAE,WAAQ,aAAU,EAAK,WACzB,EAAE,WAAQ,aAAU,GAAU,CAAM,GACpC,IAAI,GAAe,CAAM;EAC/B,IAAI,CAAC,GAAG,MAAU,MAAM,WAAW,EAAO,iBAAiB;EAC3D,EAAE,SAAS,GAAO,GAAa,GAAO,CAAG,CAAC;EAC1C;CACF;CACA,IAAI,EAAK,aAAa;EACpB,IAAM,EAAE,WAAQ,aAAU,GAAU,EAAK,WAAW,GAC9C,IAAI,GAAe,CAAM;EAC/B,IAAI,CAAC,GAAG,MAAU,MAAM,WAAW,EAAO,iBAAiB;EAC3D,EAAE,WAAW,CAAK;EAClB;CACF;CACA,IAAI,EAAK,YAAY;EACnB,IAAM,IAAM,GAAgB,EAAK,WAAW,SAAS,CAAG,GAClD,IAAO,EAAK,WAAW,QAAQ;EACrC,AAAI,MAAS,YAAW,EAAM,QAAQ,CAAG,IAChC,MAAS,UAAS,EAAM,MAAM,CAAG,IACrC,EAAM,KAAK,CAAG;EACnB;CACF;AACF;AAGA,SAAS,GAAa,GAAc,GAA+B;CAEjE,OADI,OAAO,KAAQ,WACZ,GAAY,GAAK,CAAG,IADS;AAEtC;AAGA,SAAS,GAAgB,GAAa,GAA8B;CAClE,IAAM,IAAI,GAAY,GAAK,CAAG;CAC9B,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC;AAClC;AAEA,SAAS,GAAU,GAAiD;CAClE,IAAM,IAAM,EAAK,QAAQ,GAAG;CAC5B,IAAI,KAAO,GAAG,MAAU,MAAM,iBAAiB,EAAK,4BAA4B;CAChF,OAAO;EAAE,QAAQ,EAAK,MAAM,GAAG,CAAG;EAAG,OAAO,EAAK,MAAM,IAAM,CAAC;CAAE;AAClE;AAGA,SAAgB,GAAW,GAA4D;CACrF,OAAO,CAAC,EAAO,WAAW,CAAC,CAAC,EAAO,YAAY,EAAO,SAAS,SAAS;AAC1E;;;ACnHA,SAAgB,GAAkB,GAAmC;CACnE,IAAI,CAAC,GAAK,OAAO,CAAC;CAClB,IAAM,oBAAO,IAAI,IAAY,GACvB,IAAgB,CAAC;CACvB,KAAK,IAAM,KAAQ,EAAI,MAAM,GAAG,GAAG;EACjC,IAAM,IAAI,EAAK,KAAK;EAChB,CAAC,KAAK,EAAK,IAAI,CAAC,MACpB,EAAK,IAAI,CAAC,GACV,EAAI,KAAK,CAAC;CACZ;CACA,OAAO;AACT;AAYA,SAAgB,GACd,GACA,GACkB;CAClB,IAAI,CAAC,KAAU,CAAC,GAAqB,GAAQ,QAAQ,GAAG,OAAO,CAAC;CAChE,IAAM,IAAY,MAAM,QAAQ,CAAY,IAAI,IAAe,GAAkB,CAAY,GACvF,IAAQ,EAAU,SAAS,IAAI,IAAI,IAAI,CAAS,IAAI,MACpD,IAAY,GAAc,CAAM,GAChC,IAAQ,IAAI,IAAI,GAAoB,CAAM,CAAC,GAE3C,IAAW,IAAQ,IAAY,GAAmB,GAAQ,GAAW,CAAK,GAC1E,IAAwB,CAAC;CAC/B,KAAK,IAAM,KAAO,GAAU;EAC1B,IAAI,KAAS,CAAC,EAAM,IAAI,CAAG,GAAG;EAC9B,IAAM,IAAO,GAAc,GAAQ,GAAK,GAAW,CAAK;EACxD,AAAI,KAAM,EAAI,KAAK,CAAI;CACzB;CACA,OAAO;AACT;AAEA,SAAS,GAAmB,GAAgB,GAAoB,GAA8B;CAC5F,IAAM,IAAkB,CAAC;CACzB,KAAK,IAAM,CAAC,GAAM,MAAM,EAAO,QACzB,EAAM,IAAI,CAAI,KAAK,EAAE,QACzB,EAAM,KAAK,CAAI;CAEjB,IAAI,CAAC,GAAW;EACd,KAAK,IAAM,KAAQ,OAAO,KAAK,EAAO,iBAAiB,CAAC,CAAC,GACvD,EAAM,KAAK,WAAW,GAAM;EAE9B,KAAK,IAAM,KAAQ,OAAO,KAAK,EAAO,cAAc,CAAC,CAAC,GACpD,EAAM,KAAK,aAAa,GAAM;CAElC;CACA,OAAO;AACT;AAEA,SAAS,GACP,GACA,GACA,GACA,GACuB;CACvB,IAAI,EAAI,WAAW,UAAU,GAAG;EAC9B,IAAI,GAAW,OAAO;EACtB,IAAM,IAAO,EAAI,MAAM,CAAiB,GAClC,IAAQ,EAAO,gBAAgB;EAErC,OADK,IACE;GACL,MAAM;GACN;GACA,MAAM;GACN,OAAO,GAAS,EAAM,MAAM,CAAI;GAChC,aAAa;EACf,IAPmB;CAQrB;CACA,IAAI,EAAI,WAAW,YAAY,GAAG;EAChC,IAAI,GAAW,OAAO;EACtB,IAAM,IAAO,EAAI,MAAM,EAAmB,GACpC,IAAO,EAAO,aAAa;EAEjC,OADK,IACE;GACL,MAAM;GACN;GACA,MAAM;GACN,OAAO,GAAS,EAAK,MAAM,CAAI;GAC/B,UAAU;EACZ,IAPkB;CAQpB;CACA,IAAI,EAAM,IAAI,CAAG,GAAG,OAAO;CAC3B,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;CAEnC,OADI,CAAC,KAAS,EAAM,OAAa,OAC1B;EACL,MAAM;EACN,MAAM;EACN,MAAM;EACN,OAAO,GAAS,EAAM,SAAS,MAAM,CAAG;EACxC;CACF;AACF;AAEA,SAAS,GAAS,GAAmC,GAA0B;CAE7E,OADK,KACE,EAAI,CAAI,KADG;AAEpB;;;ACnGA,IAAM,KAAkB,OAAO,IAAI,8BAA8B;AAYjE,SAAgB,GAAmB,EACjC,WACA,kBACA,iBACA,UACA,eAC0B;CAC1B,IAAM,IAAmB,QACjB,GAAqB,GAAQ,CAAa,GAChD,CAAC,GAAQ,CAAa,CACxB,GAKM,IAAO,QACJ,OAAO,KAAK,CAAK,EACrB,KAAK,MAAQ;EACZ,IAAM,IAAS,EAAiB,MAAM,MAAM,EAAE,SAAS,CAAG;EAE1D,OADK,IACE;GAAE;GAAQ,OAAO,EAAM;EAAK,IADf;CAEtB,CAAC,EACA,QAAQ,MAAqB,MAAM,IAAI,GACzC,CAAC,GAAO,CAAgB,CAAC,GAEtB,IAAmB,QAAc;EACrC,IAAM,IAAO,IAAI,IAAI,EAAK,KAAK,MAAM,EAAE,OAAO,IAAI,CAAC;EACnD,OAAO,EAAiB,QAAQ,MAAM,CAAC,EAAK,IAAI,EAAE,IAAI,CAAC;CACzD,GAAG,CAAC,GAAM,CAAgB,CAAC,GAErB,IAAY,GACf,MAA2B;EAC1B,IAAM,IAAS,GAAkB,GAAQ,CAAY,GAC/C,IAAO,EAAE,GAAG,EAAM;EAIxB,AADA,EAAK,EAAO,QAAQ,MAAW,KAAkB,KAAA,IAAY,GAC7D,EAAS,CAAI;CACf,GACA;EAAC;EAAO;EAAc;CAAQ,CAChC,GAEM,IAAW,GACd,GAAc,MAAkB;EAC/B,EAAS;GAAE,GAAG;IAAQ,IAAO;EAAK,CAAC;CACrC,GACA,CAAC,GAAO,CAAQ,CAClB,GAEM,IAAe,GAClB,MAAiB;EAChB,IAAM,IAAO,EAAE,GAAG,EAAM;EAExB,AADA,OAAO,EAAK,IACZ,EAAS,CAAI;CACf,GACA,CAAC,GAAO,CAAQ,CAClB;CAUA,OARK,IASH,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;YAA/D;GACG,EAAK,WAAW,KACf,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAAG;GAEjD,CAAA;GAEN,EAAK,KAAK,MACT,kBAAC,IAAD;IAEO;IACL,WAAW,MAAM,EAAS,EAAI,OAAO,MAAM,CAAC;IAC5C,gBAAgB,EAAa,EAAI,OAAO,IAAI;GAC7C,GAJM,EAAI,OAAO,IAIjB,CACF;GAED,kBAAC,IAAD;IAAc,SAAS;IAAkB,QAAQ;GAAY,CAAA;EAC1D;MAvBH,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAI,OAAO;EAAgB;YAAG;CAEjD,CAAA;AAuBX;AAYA,SAAS,GAAY,EAAE,QAAK,aAAU,eAA8B;CAClE,IAAM,EAAE,cAAW;CACnB,OACE,kBAAC,OAAD;EACE,eAAY;EACZ,OAAO;GACL,SAAS;GACT,qBAAqB;GACrB,KAAK;GACL,YAAY;EACd;YAPF;GASE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,OAAO;IAAgB;IAAG,OAAO,EAAO;cACnE,EAAO;GACL,CAAA;GACL,kBAAC,IAAD;IAAgB;IAAe;GAAW,CAAA;GAC1C,kBAAC,GAAD;IAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IAAG,SAAS;IAAU,OAAM;GAAiB,CAAA;EAC5E;;AAET;AAOA,SAAS,GAAU,EAAE,QAAK,eAA4B;CACpD,IAAM,EAAE,cAAW,GACb,IAAa,GAAc,GAC3B,IAAa,EAAI,UAAU,IAC3B,IAAc,IAAa,sBAAsB,KAAA,GACjD,IAAe,IAAa,KAAK,EAAI;CAE3C,IAAI,EAAO,SAAS,YAAY,EAAO,aAAa;EAClD,IAAM,IAAU,EAAO,YAAY,SAAS,KAAK,OAAO;GACtD,OAAO,EAAE;GACT,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;EAC1B,EAAE;EACF,OACE,kBAAC,OAAD;GAAK,eAAa,SAAS,EAAO;aAChC,kBAAC,IAAD;IACE,OAAO,OAAO,KAAiB,WAAW,IAAe;IAChD;IACC;IACV,aAAa,KAAe;GAC7B,CAAA;EACE,CAAA;CAET;CAKA,IAAM,IAAqB,GAAe,CAAM;CAChD,OACE,kBAAC,OAAD;EAAK,eAAa,SAAS,EAAO;EAAQ,OAAO,EAAE,UAAU,WAAW;YAAxE,CACG,EAAY;GACX;GACA,WAAW,EAAO;GAClB,OAAO,KAAgB;GACvB,MAAM;GACN;GACA,QAAQ;EACV,CAAC,GACA,KACC,kBAAC,OAAD;GACE,eAAa,mBAAmB,EAAO;GACvC,OAAO;IAAE,UAAU;IAAI,OAAO;IAAiB,WAAW;IAAG,WAAW;GAAS;aAEhF;EACE,CAAA,CAEJ;;AAET;AAEA,SAAS,GAAe,GAAqC;CAC3D,IAAI,EAAO,SAAS,WAAW,EAAO,OAAO,OAAO,EAAO;CAC3D,IAAI,EAAO,SAAS,cAAc,EAAO,UAAU;EACjD,IAAM,IAAI,EAAO;EACjB,OAAO;GACL,MAAM,EAAO;GACb,MAAM,EAAE;GACR,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK;GACrC,QAAQ,EAAE;GACV,KAAK,EAAE;GACP,WAAW,EAAE;EACf;CACF;CAEA,OAAO,EAAE,MAAM,SAAS;AAC1B;AASA,SAAS,GAAa,EAAE,YAAS,aAA6B;CAC5D,IAAM,CAAC,GAAS,KAAc,EAAS,EAAK;CAc5C,OAZI,EAAQ,WAAW,IACd,OAGJ,IASH,kBAAC,OAAD;EAAK,eAAY;EAA0B,OAAO;GAAE,WAAW;GAAc,UAAU;EAAI;YACzF,kBAAC,IAAD;GACE,OAAM;GACN,aAAY;GACZ,SAAS,EAAQ,KAAK,OAAO;IAAE,OAAO,EAAE;IAAM,OAAO,EAAE;GAAM,EAAE;GAC/D,WAAW,MAAS;IAClB,IAAM,IAAI,EAAQ,MAAM,MAAM,EAAE,SAAS,CAAI;IAE7C,AADI,KAAG,EAAO,CAAC,GACf,EAAW,EAAK;GAClB;EACD,CAAA;CACE,CAAA,IAlBH,kBAAC,GAAD;EAAK,MAAK;EAAQ,eAAe,EAAW,EAAI;EAAG,OAAO,EAAE,WAAW,aAAa;YAApF,CACE,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA,GAAC,aACpB;;AAkBX;AAWA,SAAgB,GACd,GACA,GACkC;CAElC,IADI,CAAC,KAAgB,EAAa,WAAW,KACzC,EAAO,SAAS,SAAS;CAC7B,IAAI,GACA,IAAc;CAClB,KAAK,IAAM,KAAO,GAAc;EAC9B,IAAM,IAAI,EAAI,EAAO;EACrB,IAAI,CAAC,GAAa;GAEhB,AADA,IAAO,GACP,IAAc;GACd;EACF;EACA,IAAI,MAAS,GAAG,OAAO;CACzB;CACA,OAAO;AACT;;;AC9QA,IAAM,KAAiB;AAEvB,SAAS,GAAoB,GAAc,GAAmD;CAC5F,IAAI,OAAO,KAAQ,UAAU,OAAO,KAAO;CAC3C,IAAM,IAAQ,EAAI,MAAM,EAAc;CACtC,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAQ,EAAM;CAOpB,OANI,KAAO,KAAS,IAAY,EAAI,MAAU,MAC1C,OAAO,UAAY,OAAe,QAAQ,QAC5C,QAAQ,KACN,kCAAkC,EAAI,uDAAuD,EAAM,EACrG,GAEK;AACT;AAEA,SAAS,GAAmB,GAAkC;CAC5D,OAAO;EACL,MAAM,EAAM,QAAQ;EACpB,UAAU,EAAM;EAChB,SAAS,EAAM,UACX;GAAE,MAAM,EAAM,QAAQ,QAAQ,CAAC;GAAG,QAAQ,EAAM,QAAQ;EAAO,IAC/D,KAAA;EACJ,MAAM,EAAM;EACZ,QAAQ,EAAM;EACd,KAAK,EAAM,MAAM,EAAE,QAAQ,EAAM,IAAI,IAAI,KAAA;CAC3C;AACF;AAEA,SAAS,GAAmB,GAAa,GAA2C;CAClF,IAAI,CAAC,EAAI,SAAS,IAAI,GAAG,OAAO;CAChC,IAAM,IAAI,GAAY,GAAK,EAAE,QAAQ,EAAS,CAAC;CAC/C,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC;AAClC;AAEA,IAAM,KAAoB;AAE1B,SAAS,GAAgB,GAAkB,GAA4B;CACrE,IAAM,IAAS,UAAU;CACzB,KAAK,IAAM,KAAS,EAAS,SAAS,EAAiB,GACrD,IAAI,EAAM,OAAO,GAAQ,OAAO;CAElC,OAAO;AACT;AAUA,SAAS,GAAkB,EAAE,UAAO,UAAO,aAAU,mBAAmC;CACtF,IAAM,IAAU,QAAc;EAC5B,IAAM,IAAU,IAAe,OAAO,QAAQ,CAAY,IAAI,CAAC;EAC/D,OAAO,CACL;GAAE,OAAO;GAAI,OAAO,EAAM,WAAW,yBAAyB;EAAI,GAClE,GAAG,EAAQ,KAAK,CAAC,GAAM,QAAY;GAAE,OAAO;GAAM,OAAO,EAAI,EAAM,IAAI,KAAK;EAAK,EAAE,CACrF;CACF,GAAG,CAAC,GAAc,EAAM,QAAQ,CAAC;CAEjC,OACE,kBAAC,IAAD;EACE,OAAO,OAAO,KAAU,WAAW,IAAQ;EAClC;EACT,WAAW,MAAM,EAAS,CAAC;EAC3B,aAAY;CACb,CAAA;AAEL;AAEA,SAAS,GAAa,EAAE,UAAO,UAAO,aAAU,iBAAc,eAA+B;CAC3F,IAAM,IAAwB,QAAc;EAC1C,IAAI,CAAC,GAAc,OAAO,CAAC;EAC3B,IAAM,IAAW,EAAM,QAAS;EAChC,IAAI,GAAU;GACZ,IAAM,IAAW,GAAmB,GAAU,CAAQ;GAEtD,OADI,KAAY,EAAa,KAAkB,EAAa,GAAU,WAC/D,CAAC;EACV;EACA,OAAO,OAAO,OAAO,CAAY,EAAE,SAAS,MAAM,EAAE,QAAQ;CAC9D,GAAG;EAAC;EAAc,EAAM;EAAQ;CAAQ,CAAC,GAEnC,IAAU,QACP,CACL;EACE,OAAO;EACP,OAAO,EAAS,WAAW,IAAI,8BAA8B;CAC/D,GACA,GAAG,EAAS,KAAK,OAAO;EAAE,OAAO,EAAE;EAAM,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;CAAK,EAAE,CAC1E,GACC,CAAC,CAAQ,CAAC;CAEb,OACE,kBAAC,IAAD;EACE,OAAO,OAAO,KAAU,WAAW,IAAQ;EAClC;EACT,WAAW,MAAM,EAAS,CAAC;EAC3B,aAAY;CACb,CAAA;AAEL;AAEA,SAAgB,GAAa,EAC3B,SACA,iBACA,WACA,eACA,aACA,cACA,QACA,oBACoB;CACpB,IAAM,EAAE,MAAM,MAAW,GAAU,GAC7B,IAAa,GAAc,GAC3B,IAAS,EAAO,iBAChB,IAAiB,EAAO,4BAA4B,CAAC,GACrD,IAAe,QACd,IACE,MAAM,KAAK,CAAM,IADJ,CAAC,GAEpB,CAAC,CAAM,CAAC,GAEL,IAAqB,QAAc;EACnC,OAAC,EAAO,UAAU,CAAC,IACvB,OAAO,EAAO,SAAS,EAAO,SAAS;CACzC,GAAG,CAAC,EAAO,QAAQ,CAAM,CAAC,GAOpB,IAAS,QAAc;EACvB,OAAC,EAAO,UAAU,CAAC,IACvB,OAAO,EAAO,SAAS,EAAO;CAChC,GAAG,CAAC,EAAO,QAAQ,CAAM,CAAC,GAKpB,IAAU,QAAc;EACvB,OACL;QAAK,IAAM,CAAC,GAAM,MAAM,EAAO,QAC7B,IAAI,EAAE,SAAS,OAAO;GAExB,OAAO,EAAO,cAAc;EAFJ;CAG1B,GAAG,CAAC,CAAM,CAAC,GAoCL,CAAC,GAAU,KAAe,EAlCH,QAAkB;EAC7C,IAAM,IAAmC,EAAE,GAAG,EAAe;EAC7D,KAAK,IAAM,CAAC,GAAK,MAAU,GAAc;GACvC,IAAI,EAAM,SAAS,YAAY;IAI7B,AAAI,KAAiB,IACnB,EAAQ,KAAO,EAAc,KAAK,MAAM,EAAE,EAAQ,EAAE,QAAQ,MAAM,KAAK,IAAI,IAE3E,EAAQ,KAAO,CAAC;IAElB;GACF;GACA,IAAI,EAAM,SAAS,gBAAgB;IAEjC,EAAQ,KAAO,CAAC;IAChB;GACF;GACA,IAAI,EAAM,SAAS,QAAQ;IAMzB,EAAQ,KAAO,EAAM,YAAY,MAAQ,EAAM,YAAY;IAC3D;GACF;GACA,EAAQ,KAAO,GAAoB,EAAM,SAAS,CAAG;EACvD;EACA,OAAO;CAET,GAAG;EAAC;EAAc;EAAK;EAAe;CAAO,CAEqB,CAAoB,GAEhF,IAAoB,GACvB,GAAmB,MAAmB;EACrC,GAAa,MAAS;GACpB,IAAM,IAAO;IAAE,GAAG;KAAO,IAAY;GAAM;GAC3C,IAAI,CAAC,GAAQ,OAAO;GACpB,KAAK,IAAM,CAAC,GAAK,MAAU,GAAQ;IACjC,IAAI,EAAM,SAAS,UAAU;IAC7B,IAAM,IAAW,EAAM,QAAS;IAC3B,KACD,GAAgB,GAAU,CAAS,MAAG,EAAK,KAAO;GACxD;GACA,OAAO;EACT,CAAC;CACH,GACA,CAAC,CAAM,CACT,GAEM,IAAe,QAAkB;EACrC,IAAM,IAAmC,EAAE,GAAG,EAAS;EACvD,KAAK,IAAM,CAAC,GAAK,MAAU,GAAc;GACvC,IAAM,IAAM,EAAS,IACf,IAAU,KAAO,QAAQ,MAAQ;GAEvC,IAAI,EAAM,YAAY,GAAS;GAO/B,AAAI,KAAS,OAAO,EAAQ;EAC9B;EACA,EAAS,CAAO;CAClB,GAAG;EAAC;EAAU;EAAc;CAAQ,CAAC;CAIrC,OAFK,IAGH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,eAAe,EAAa,EAAK;EACjC,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,WAAW;GACX,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;GACV,SAAS;GACT,eAAe;EACjB;YAhBF;GAkBE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAAnF;KACE,kBAAC,QAAD;MACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc;OACd,YAAY;OACZ,OAAO;OACP,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,YAAY;MACd;gBAEA,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAClB,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO;OAAE,MAAM;OAAG,UAAU;MAAE;gBAAnC,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO;OAAc;iBAAI;MAAgB,CAAA,GACtF,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;QAAiB,WAAW;OAAE;iBAAG;MAEjE,CAAA,CACF;;KACL,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,eAAe,EAAa,EAAK;KAAI,CAAA;IACvE;;GAEL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,SAAS;KACT,eAAe;KACf,KAAK;KACL,WAAW;IACb;cAEC,EAAa,KAAK,CAAC,GAAK,OAAW;KAClC,IAAM,IAAY,EAAI,EAAM,SAAS,IAAI,KAAK;KAK9C,IAAI,EAAM,SAAS,YACjB,OAAO;KAGT,IAAI;KA8CJ,OA7CA,AAmCE,IAnCE,EAAM,SAAS,iBAKf,kBAAC,IAAD;MACU;MACR,eANc,OAAO,EAAM,QAAS,WAAe,WACnD,EAAM,OAAO,UACb,KAAA;MAKA,cAAc;MACd,OAAQ,EAAS,MAAqB,CAAC;MACvC,WAAW,MAAS,EAAkB,GAAK,CAAI;KAChD,CAAA,IAEM,EAAM,SAAS,iBAEtB,kBAAC,IAAD;MACS;MACP,OAAO,EAAS;MAChB,WAAW,MAAM,EAAkB,GAAK,CAAC;MACzC,cAAc;MACJ;KACX,CAAA,IAEM,EAAM,SAAS,WAEtB,kBAAC,IAAD;MACS;MACP,OAAO,EAAS;MAChB,WAAW,MAAM,EAAkB,GAAK,CAAC;MACzC,cAAc;MACJ;KACX,CAAA,IAIO,EAAY;MACpB,OAFY,GAAmB,CAE/B;MACA,WAAW;MACX,OAAO,EAAS;MAChB,MAAM;MACN,WAAW,MAAM,EAAkB,GAAK,CAAC;MACzC,QAAQ;KACV,CAAC,GAID,kBAAC,OAAD,EAAA,UAAA;MACE,kBAAC,IAAD;OAAY,UAAU,EAAM;iBAAW;MAAsB,CAAA;MAC5D;MACA,EAAM,SAAS,eACd,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAAI,OAAO;QAAiB,WAAW;QAAG,cAAc;OAAE;iBAC7E,EAAI,EAAM,QAAQ,WAAW;MAC7B,CAAA;KAEF,EAAA,GARK,CAQL;IAET,CAAC;GACE,CAAA;GAEL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,WAAW;KACX,SAAS;KACT,YAAY;KACZ,KAAK;KACL,YAAY;IACd;cARF;KAUE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MAAK,eAAe,EAAa,EAAK;MAAG,UAAU;gBAAW;KAEzD,CAAA;KACL,kBAAC,GAAD;MACE,MAAK;MACL,MAAkB,EAAZ,IAAa,IAAwB,EAAK,MAA9B,EAAS,MAAM,GAAK,CAA0B;MAChE,SAAS;MACT,UAAU;gBAET,IAAY,eAAe;KACzB,CAAA;IACF;;EACF;GACL,EAAA,CAAA,IAhKc;AAkKpB;;;AC1YA,SAAgB,GAAmB,EAAE,SAAM,YAAS,cAAoC;CAEtF,OADK,IAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD,EACE,OAAO;EACL,UAAU;EACV,OAAO;EACP,YAAY;EACZ,QAAQ;EACR,gBAAgB;CAClB,EACD,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,WAAW;GACX,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;GACV,SAAS;GACT,eAAe;EACjB;YAhBF;GAkBE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAAnF;KACE,kBAAC,QAAD;MACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc;OACd,YAAY;OACZ,OAAO;OACP,SAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,YAAY;MACd;gBAEA,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;KACnB,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO;OAAE,MAAM;OAAG,UAAU;MAAE;gBAAnC,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO;OAAc;iBAC/D,EAAQ,SAAS;MACf,CAAA,GACJ,EAAQ,WACP,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;QAAiB,WAAW;OAAE;iBAChE,EAAQ;MACN,CAAA,CAEJ;;KACL,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;KAAU,CAAA;IACrD;;GAEJ,EAAQ,UAAU,EAAQ,OAAO,SAAS,KACzC,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,SAAS;KACT,eAAe;KACf,KAAK;KACL,WAAW;IACb;cAEC,EAAQ,OAAO,KAAK,GAAO,MAC1B,kBAAC,IAAD,EAAgC,SAAQ,GAAlB,CAAkB,CACzC;GACE,CAAA;GAGP,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,WAAW;KACX,SAAS;KACT,YAAY;KACZ,KAAK;KACL,YAAY;IACd;cARF,CAUE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA,GAC1B,kBAAC,GAAD;KAAK,MAAK;KAAU,SAAS;eAAS;IAEjC,CAAA,CACF;;EACF;GACL,EAAA,CAAA,IA3Fc;AA6FpB;AAEA,SAAS,GAAgB,EAAE,YAAkC;CAC3D,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;YAA/D,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,YAAY;IAAK,OAAO;GAAgB;aAAI,EAAM;EAAW,CAAA,GAC3F,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAY,OAAO;GAAO;aAAlD,CACE,kBAAC,OAAD;IACE,WAAW,EAAM,YAAY,SAAS,KAAA;IACtC,OAAO;KACL,cAAc;KACd,QAAQ;KACR,YAAY;KACZ,SAAS,EAAM,WAAW,sBAAsB;KAChD,UAAU;KACV,WAAW;KACX,OAAO;IACT;cAEC,EAAM;GACJ,CAAA,GACJ,EAAM,YACL,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAY,KAAK;KAAG,OAAO;IAAE;cACnD,kBAAC,GAAD,EAAY,OAAO,EAAM,MAAQ,CAAA;GAC9B,CAAA,CAEJ;IACF;;AAET;;;ACzHA,SAAS,GAAgB,GAAgC;CACvD,IAAI,CAAC,KAAU,OAAO,KAAW,UAAU,OAAO,EAAE,MAAM,UAAU;CACpE,IAAM,IAAI,GACJ,IAAS,EAAE,QACX,IAAe,EAAE,SAAS,UAAU,GACpC,IAAa,EAAE,QAAQ,UAAU,GACjC,IAAe,EAAE,SAAS,UAAU,GACpC,IAAW,EAAE,SAAS,IAAI;CAEhC,IAAI,MAAW,WAAW;EAExB,IAAM,IAAO,GAAG,EAAa,MADf,IAAe,IAAa,EACD;EACzC,OAAO;GACL,MAAM;GACN,QAAQ,IAAW,GAAG,EAAK,KAAK,MAAa;EAC/C;CACF;CAOA,OANI,MAAW,YACN;EAAE,MAAM;EAAW,QAAQ;CAAmB,IAEnD,MAAW,aACN,EAAE,MAAM,WAAW,IAErB,EAAE,MAAM,UAAU;AAC3B;AA0BA,SAAS,GAAe,GAA+C;CACrE,IAAI,CAAC,KAAY,OAAO,KAAa,UAAU,OAAO;CACtD,IAAM,IAAW,EAAkC;CACnD,IAAI,CAAC,KAAW,OAAO,KAAY,UAAU,OAAO;CACpD,IAAM,IAAW,EAAmC;CAEpD,OADI,CAAC,KAAW,OAAO,KAAY,WAAiB,OAC7C;AACT;AAEA,SAAS,GAA8B,GAAyB;CAC9D,IAAI,CAAC,KAAY,OAAO,KAAa,UAAU;CAC/C,IAAM,IAAW,EAAkC;CAC/C,CAAC,KAAW,OAAO,KAAY,YAC/B,8BAA+B,KACjC,OAAO,SAAS,OAAO,GAAG;AAE9B;AAGA,SAAS,GAAe,GAA6B;CACnD,IAAI,aAAe,IAAkB;EAEnC,IAAM,IADO,EAAI,SACG,UAAU,CAAC;EAC/B,IAAI,EAAO,WAAW,GACpB,OAAO;GAAE,MAAM;GAAS,QAAQ,EAAO,GAAG;EAAM;EAElD,IAAM,IAAQ,EAAO,IAAI;EACzB,OAAO;GACL,MAAM;GACN,QAAQ,IAAQ,GAAG,EAAO,OAAO,YAAY,MAAU,GAAG,EAAO,OAAO;EAC1E;CACF;CAIA,OAHI,aAAe,KACV;EAAE,MAAM;EAAS,QAAQ,EAAI;CAAQ,IAEvC;EAAE,MAAM;EAAS,QAAQ,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG;CAAE;AACnF;AA+CA,IAAM,KAAM,GAA+C,IAAI;AAE/D,SAAgB,KAA4C;CAC1D,IAAM,IAAI,GAAW,EAAG;CACxB,IAAI,CAAC,GAAG,MAAU,MAAM,4DAA4D;CACpF,OAAO;AACT;AAUA,SAAS,GAAe,GAAiD;CACvE,IAAM,IAAgC,CAAC,GACjC,IAAS,EAAK,QAAQ;CAC5B,IAAI,QACG,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAM,GACxC,AAAI,MAAM,KAAA,MAAW,EAAK,KAAK;CAGnC,OAAO;AACT;AAEA,SAAgB,GAAqB,EAAE,eAAqC;CAG1E,IAAM,IAAW,GAAiB,GAC5B,EAAE,MAAM,MAAW,GAAU,GAC7B,CAAC,GAAO,KAAY,EAA4B,IAAI,GAWpD,CAAC,GAAe,KAAoB,EAAqC,IAAI,GAE7E,IAAuB,GAC1B,MAA0B;EACpB,OAAG,iBACR,OAAO,GAAQ,QAAQ,EAAE,kBAAkB;CAC7C,GACA,CAAC,GAAQ,OAAO,CAClB,GAEM,IAAY,GACf,GAAe,GAAsB,GAA4B,GAAwB,MAAqB;EAG7G,IAAM,UAAoB;GACxB,IAAM,IAAQ,GAAa,CAAM,GAC3B,IAAgB,EAAqB,CAAM,GAC3C,IAAS,IAAQ,OAAO,EAAM,WAAW;GAC/C,EAAM,QACJ,GAAG,EAAM,WACT,GACA,IAAgB,EAAE,QAAQ;IAAE,OAAO;IAAQ,eAAe,EAAS,CAAa;GAAE,EAAE,IAAI,KAAA,CAC1F;EACF;EAEA,QAAQ,EAAQ,MAAhB;GACE,KAAK;IACH,AAAI,IAAa,EAAY,IACxB,EAAM,QAAQ,GAAG,EAAM,UAAU;IACtC;GAEF,KAAK;IACH,EAAY;IACZ;GACF,KAAK;IACH,EAAM,KAAK,GAAO,EAAQ,MAAM;IAChC;GACF,KAAK;IACH,EAAM,QAAQ,GAAG,EAAM,uBAAuB,EAAQ,MAAM;IAC5D;GACF,KAAK;IAIH,EAAM,MAAM,GAAG,EAAM,UAAU,EAAQ,MAAM;IAC7C;EACJ;CACF,GACA,CAAC,GAAU,CAAoB,CACjC,GAEM,IAAM,GACT,MAA0C;EACzC,IAAM,IAAI,EAAK,QACT,IAAc,EAAK,eAAe,GAAG,eAAe,IACpD,IAAc,EAAK,eAAe,GAAG,gBAAgB,IACrD,IAAQ,EAAK,SAAS,EAAI,GAAG,SAAS,IAAI,KAAK;EA0BrD,OARI,GAAG,mBAAmB,EAAE,gBAAgB,SAAS,KACnD,EAAS;GAAE,IAAI,OAAO;GAAG;GAAM;GAAO,MAAM;GAAU,SAAS;EAAM,CAAC,GAC/D,QAAQ,QAAQ,KAErB,KACF,EAAS;GAAE,IAAI,OAAO;GAAG;GAAM;GAAO,MAAM;GAAW,SAAS;EAAM,CAAC,GAChE,QAAQ,QAAQ,MAElB,OAxBiB,MAAoC;GAC1D,IAAI;IACF,IAAM,IAAS,MAAM,EAAK,QAAQ,CAAM,GAClC,IAAU,GAAgB,CAAM;IAEtC,IADA,EAAU,GAAO,GAAa,GAAG,GAAS,CAAM,GAC5C,EAAQ,SAAS,aAAa,EAAQ,SAAS,YAAY;KAC7D,EAAK,YAAY,CAAM;KACvB,IAAM,IAAU,GAAe,CAAM;KAErC,AADI,KAAS,EAAiB,CAAO,GACrC,GAA8B,CAAM;IACtC;GACF,SAAS,GAAK;IACZ,EAAU,GAAO,GAAa,GAAG,GAAe,CAAG,CAAC;GACtD;EACF,GAUgB,GAAe,CAAI,CAAC;CACtC,GACA,CAAC,CAAS,CACZ,GAEM,IAAQ,SAAyC,EAAE,OAAI,IAAI,CAAC,CAAG,CAAC,GAEhE,IAAc,GAAO,KAAK,QAE1B,IAAqB,EACzB,OAAO,MAAoC;EACzC,IAAI,CAAC,GAAO;EACZ,IAAM,IAAS,EAAM,IACf,IAAO,GACP,IAAI,EAAK,KAAK,QACd,IAAc,EAAK,KAAK,eAAe,GAAG,gBAAgB;EAChE,GAAU,MAAO,GAAG,OAAO,IAAS;GAAE,GAAG;GAAG,SAAS;EAAK,IAAI,CAAE;EAChE,IAAI;GACF,IAAM,IAAS,MAAM,EAAK,KAAK,QAAQ,CAAM,GACvC,IAAU,GAAgB,CAAM;GAEtC,IADA,EAAU,EAAK,OAAO,GAAa,GAAG,GAAS,CAAM,GACjD,EAAQ,SAAS,aAAa,EAAQ,SAAS,YAAY;IAC7D,EAAK,KAAK,YAAY,CAAM;IAC5B,IAAM,IAAU,GAAe,CAAM;IAErC,AADI,KAAS,EAAiB,CAAO,GACrC,GAA8B,CAAM;GACtC;GAGA,AAAI,EAAQ,SAAS,aACnB,GAAU,MAAO,GAAG,OAAO,IAAS,OAAO,CAAE;EAEjD,SAAS,GAAK;GACZ,EAAU,EAAK,OAAO,GAAa,GAAG,GAAe,CAAG,CAAC;EAC3D,UAAU;GACR,GAAU,MAAO,GAAG,OAAO,IAAS;IAAE,GAAG;IAAI,SAAS;GAAM,IAAI,CAAE;EACpE;CACF,GACA,CAAC,GAAO,CAAS,CACnB;CAEA,OACE,kBAAC,GAAI,UAAL;EAAqB;YAArB;GACG;GAOA,KACC,kBAAC,IAAD;IACE,MAAA;IACA,eAAe,EAAiB,IAAI;IACpC,SAAS;GACV,CAAA;GAEF,GAAO,SAAS,YAAY,KAC3B,kBAAC,IAAD;IACE,MAAA;IACA,eAAe,MAAM;KACnB,AAAK,KAAG,EAAS,IAAI;IACvB;IACA,QAAQ;IACR,YAAY,EAAM;IAClB,UAAU;IACV,WAAW,EAAM;IACjB,eAAe,EAAM,KAAK;GAC3B,CAAA;GAEF,GAAO,SAAS,aACf,kBAAC,IAAD;IACE,OAAO;IACP,eAAe,EAAS,IAAI;IAC5B,WAAW,OAAO,MAAW;KAC3B,IAAM,IAAS,EAAM,IACf,IAAO,GACP,IAAI,EAAK,KAAK,QACd,IAAc,EAAK,KAAK,eAAe,GAAG,gBAAgB,IAC5D,IAAW;KACf,IAAI;MACF,IAAM,IAAS,MAAM,EAAK,KAAK,QAAQ,CAAM,GACvC,IAAU,GAAgB,CAAM;MAEtC,IADA,EAAU,EAAK,OAAO,GAAa,GAAG,GAAS,CAAM,GACjD,EAAQ,SAAS,aAAa,EAAQ,SAAS,YAAY;OAC7D,EAAK,KAAK,YAAY,CAAM;OAC5B,IAAM,IAAU,GAAe,CAAM;OAErC,AADI,KAAS,EAAiB,CAAO,GACrC,GAA8B,CAAM;MACtC;MAGA,IAAW,EAAQ,SAAS;KAC9B,SAAS,GAAK;MACZ,EAAU,EAAK,OAAO,GAAa,GAAG,GAAe,CAAG,CAAC;KAE3D,UAAU;MACR,AAAK,KACH,GAAU,MAAO,GAAG,OAAO,IAAS,OAAO,CAAE;KAEjD;IACF;GACD,CAAA;EAES;;AAElB;AAEA,SAAS,GAAa,GAAgC;CACpD,IAAI,CAAC,KAAK,OAAO,KAAM,UAAU;CACjC,IAAM,IAAM,GACN,IAAK,EAAI,UAAU,EAAI;CAC7B,OAAO,OAAO,KAAO,WAAW,IAAK,KAAA;AACvC;AAQA,SAAS,GAAY,EAAE,UAAO,YAAS,gBAA+B;CACpE,IAAM,EAAE,UAAO,YAAS,GAClB,CAAC,GAAS,KAAc,EAAS,EAAK,GAEtC,IAAS,EAAK,QACd,IAAc,EAAK,eAAe,GAAQ,eAAe;CAW/D,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;EACZ;YAbF,CAeE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAe,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAG;aAArF;IACE,kBAAC,QAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY,IAAc,uBAAuB;MACjD,OAAO,IAAc,kBAAkB;MACvC,SAAS;MACT,YAAY;MACZ,gBAAgB;KAClB;eAEe,EAAd,IAAe,EAAK,QAAsB,EAAK,MAAjC,EAAY,MAAM,GAAK,CAA0B;IAC5D,CAAA;IACN,kBAAC,OAAD;KAAK,OAAO,EAAE,MAAM,EAAE;eAAtB,CACE,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,OAAO;MAAc;gBAAI;KAAW,CAAA,GACjF,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAM,OAAO;MAAgB;gBAAG;KAEnD,CAAA,CACF;;IACL,kBAAC,GAAD;KAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;KAAG,SAAS;IAAU,CAAA;GACrD;MACL,kBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,WAAW;IACX,SAAS;IACT,YAAY;IACZ,KAAK;IACL,YAAY;GACd;aARF;IAgBE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;IAC1B,kBAAC,GAAD;KAAK,SAAS;eAAS;IAAW,CAAA;IAClC,kBAAC,GAAD;KACE,MAAK;KACL,QAAQ;KACR,MAAgB,EAAV,IAAW,IAAwB,EAAK,MAA9B,EAAS,MAAM,GAAK,CAA0B;KAC9D,SAAS,YAjFI;MACrB,EAAW,EAAI;MACf,IAAI;OACF,MAAM,EAAU,GAAe,CAAI,CAAC;MACtC,UAAU;OACR,EAAW,EAAK;MAClB;KACF;eA4EW,IAAU,eAAe;IACvB,CAAA;GACF;IACF;GACL,EAAA,CAAA;AAEN;;;AC3eA,IAAa,KAAsB;AAKnC,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA;CACA,IAAM,IAAQ,MAAM,QAAQ,CAAU,IAAI,IAAa,CAAC,CAAU,GAC5D,IAAQ,MAAM,QAAQ,CAAU,IAAI,GAAe,GAAU,EAAM,MAAM,IAAI,CAAC,CAAQ;CAE5F,OAAO,GAAS;EACd,UAAU;GAAC;GAAqB;GAAc;GAAU;GAAW;GAAM;EAAK;EAC9E,eAAe;GACb,IAAM,IAAS,IAAI,gBAAgB;GACnC,IAAI,CAAC,GAAO,MAAU,MAAM,6BAA6B;GACzD,IAAM,IAAU,EAAM,KAAK,GAAI,OAAO;IAAE,OAAO;IAAI,IAAI;IAAM,OAAO,EAAM;GAAG,EAAE;GAK/E,OAJA,EAAO,IAAI,WAAW,KAAK,UAAU,CAAO,CAAC,GAC7C,EAAO,IAAI,WAAW,MAAM,GACxB,KAAM,EAAO,IAAI,QAAQ,CAAI,GAC7B,KAAO,EAAO,IAAI,SAAS,OAAO,CAAK,CAAC,GACrC,GACL,aAAa,EAAU,GAAG,EAAO,SAAS,GAC5C;EACF;EACA,SAAS,CAAC,CAAC,KAAgB,CAAC,CAAC,KAAY,CAAC,CAAC,KAAa,CAAC,CAAC,KAAc,MAAU;CACpF,CAAC;AACH;;;ACrBA,IAAa,KAAmB,IAAuB,GAAK,OAAS;CACnE,kCAAkB,IAAI,IAAY;CAClC,eAAe,MAAe;EAC5B,IAAI,CAAC,KAAc,EAAI,EAAE,iBAAiB,IAAI,CAAU,GAAG;EAC3D,IAAM,IAAO,IAAI,IAAI,EAAI,EAAE,gBAAgB;EAE3C,AADA,EAAK,IAAI,CAAU,GACnB,EAAI,EAAE,kBAAkB,EAAK,CAAC;CAChC;AACF,EAAE;AAOF,SAAgB,GACd,GACA,GACA,GACS;CACT,OAAO,MAAiB,MAAQ,CAAC,EAAiB,IAAI,CAAU;AAClE;;;AC/BA,IAAM,KAAsB,KACtB,KAAa,KAUb,KAAwB,KAQxB,KAAqB;AAU3B,SAAS,KAA6B;CAEpC,OADI,OAAO,WAAa,MAAoB,KACrC,SAAS,oBAAoB;AACtC;AAEA,SAAS,KAA8B;CACrC,IAAM,CAAC,GAAS,KAAc,EAAS,EAAiB;CASxD,OAPA,QAAgB;EACd,IAAI,OAAO,WAAa,KAAa;EACrC,IAAM,UAA2B,EAAW,GAAkB,CAAC;EAE/D,OADA,SAAS,iBAAiB,oBAAoB,CAAkB,SACnD,SAAS,oBAAoB,oBAAoB,CAAkB;CAClF,GAAG,CAAC,CAAC,GAEE;AACT;AAaA,SAAgB,GACd,GACA,GACM;CACN,IAAI,EAAO,WAAW,GAAG;CAEzB,IAAM,oBAAW,IAAI,IAAkC;CACvD,KAAK,IAAM,KAAM,GAAQ;EACvB,IAAM,IAAO,EAAS,IAAI,EAAG,MAAM,GAC7B,IACJ,EAAG,WAAW,aACd,EAAG,WAAW,aACd,EAAG,WAAW;EAChB,EAAS,IAAI,EAAG,QAAQ,EAAE,UAAU,GAAM,WAAW,OAAU,EAAQ,CAAC;CAC1E;CAEA,KAAK,IAAM,CAAC,GAAQ,MAAU,GAW5B,AAVA,EAAY,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAM,EAAE,CAAC,GACnE,EAAY,kBAAkB,EAAE,UAAU,CAAC,gBAAgB,CAAM,EAAE,CAAC,GAEpE,EAAY,kBAAkB,EAC5B,YAAY,MAAU;EACpB,IAAM,IAAM,EAAM;EAClB,OAAO,EAAI,OAAA,qBAA8B,EAAI,OAAO;CACtD,EACF,CAAC,GAEG,EAAM,WACR,EAAY,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAM,EAAE,CAAC;AAGzE;AAEA,SAAS,GAAoB,GAAkC;CAC7D,OACE,EAAI,OAAO,iBACX,EAAI,OAAO,kBACX,EAAI,OAAO,iBACX,EAAI,OAAA;AAER;AAEA,SAAgB,GAAwB,GAAgC;CACtE,EAAY,kBAAkB;EAC5B,YAAY,MAA4C,GAAoB,EAAM,QAAQ;EAC1F,aAAa;CACf,CAAC;AACH;AAEA,SAAgB,KAAS;CACvB,IAAM,IAAkB,GAAc,MAAM,EAAE,eAAe,GACvD,IAAiB,GAAc,MAAM,EAAE,cAAc,MAAM,EAAE,GAC7D,IAAc,GAAe,GAC7B,IAAW,EAA+B,IAAI,GAC9C,IAAiB,EAAO,EAAE,GAC1B,IAAiB,EAAO,EAAE,GAC1B,IAAkB,GAAmB,GACrC,IAAe,EAAO,CAAC,CAAe;CAE5C,QAAgB;EACd,IAAI,CAAC,GAAiB;GAGpB,AAFA,EAAe,UAAU,IACzB,EAAe,UAAU,IACzB,EAAa,UAAU,CAAC;GACxB;EACF;EACA,IAAI,CAAC,GAAiB;GAGpB,AAFA,EAAa,UAAU,IACvB,EAAS,SAAS,MAAM,GACxB,EAAS,UAAU;GACnB;EACF;EAOA,AALI,EAAa,YACf,EAAa,UAAU,IACvB,GAAwB,CAAW,IAGjC,EAAe,YAAY,MAC7B,EAAe,UAAU,IACzB,EAAe,UAAU;EAG3B,IAAI,IAAU,IACV,IAAU,GAER,IAAgC,CAAC,GACnC,IAAmD,MACnD,IAAwD,MAQtD,UAAkC;GACtC,IAAI,MAAoB,MAAM;GAC9B,IAAM,IAAQ,KAAK,OAAO,IAAI;GAC9B,IAAkB,iBAAiB;IACjC,IAAkB,MACb,KACL,GAAwB,CAAW;GACrC,GAAG,CAAK;EACV,GAEM,UAAc;GAElB,IADA,IAAa,MACT,EAAQ,WAAW,GAAG;GAC1B,IAAM,IAAQ,EAAQ,OAAO,GAAG,EAAQ,MAAM;GAC9C,GAA2B,GAAa,CAAK;GAC7C,KAAK,IAAM,KAAM,GACf,AAAI,EAAG,YAAS,EAAe,UAAU,EAAG;EAEhD,GAEM,UAAsB;GACtB,MAAe,SACnB,IAAa,WAAW,GAAO,EAAkB;EACnD;EAEA,eAAe,IAAU;GACvB,OAAO,IAAS;IACd,IAAM,IAAa,IAAI,gBAAgB;IAavC,AAZA,EAAS,UAAU,GAYnB,IAAU;IASV,IAAM,IAAY,GAAiB,GAC7B,IAAa,IAAY,GAAc,IAAI;IAEjD,IAAI;KACF,WAAW,IAAM,KAAS,GAAa;MACrC,QAAQ,EAAW;MACnB,aAAa,EAAe;MAC5B,aAAa,KAAc,KAAA;MAC3B,aAAa,IAAY,SAAS,KAAA;KACpC,CAAC,GAAG;MACF,IAAI,CAAC,GAAS;MAEd,IAAI,EAAM,SAAS,mBAAmB;OAkBpC,IAAI;QAaF,AAZI,KAMF,GAAgB,GAChB,MAAM,GAAgB,KAEtB,MAAM,GAAc,GAEtB,EAAY,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,GACtD,EAAkB,SAAS,EAAE,UAAU;OACzC,QAAQ,CAER;OACA;MACF;MAEA,IAAI,EAAM,SAAS,cAAc;OAC/B,EAAe,UAAU,EAAM;OAC/B;MACF;MAEA,IAAI,EAAM,SAAS,eAAe;OAEhC,AADI,EAAM,YAAS,EAAe,UAAU,EAAM,UAClD,EAA0B;OAC1B;MACF;MAEA,IAAI,EAAM,SAAS,qBAAqB;OAStC,AAFI,EAAM,YAAS,EAAe,UAAU,EAAM,UAClD,GAAiB,SAAS,EAAE,aAAa,EAAM,MAAM,GACrD,GAA2B,GAAa,CACtC;QAAE,QAAQ,EAAM;QAAQ,QAAQ;OAAU,CAC5C,CAAC;OACD;MACF;MAEA,AAAI,EAAM,SAAS,qBACjB,EAAQ,KAAK;OAAE,QAAQ,EAAM;OAAQ,QAAQ,EAAM;OAAQ,SAAS,EAAM;MAAQ,CAAC,GACnF,EAAc;KAElB;IACF,SAAS,GAAK;KAEZ,IADI,CAAC,KACD,aAAe,gBAAgB,EAAI,SAAS,cAAc;KAY9D,IAAI,aAAe,IAAc;MAC/B,IAAI,GACF,IAAI;OAOF,AAFA,GAAgB,GAChB,MAAM,GAAgB,GACtB,IAAU;OACV;MACF,QAAQ;OAIN,GAAgC,EAAE;OAClC;MACF;MAEF,IAAI;OAEF,AADA,MAAM,GAAc,GACpB,IAAU;OACV;MACF,SAAS,GAAY;OASnB,IAAI,aAAsB,IAAkB;QAC1C,GAAgC,EAAE;QAClC;OACF;MAEF;KACF;IACF;IAGA,IAAI,GAAS;KACX,IAAM,IAAQ,KAAK,IAAI,KAAsB,KAAG,GAAU,EAAmB,GACvE,IAAS,IAAQ,KAAM,KAAK,OAAO;KAEzC,AADA,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAQ,CAAM,CAAC,GACtD;IACF;GACF;EACF;EAIA,OAFA,EAAQ,SAEK;GAYX,AAXA,IAAU,IACV,EAAS,SAAS,MAAM,GACxB,EAAS,UAAU,MACf,MAAe,SACjB,aAAa,CAAU,GACvB,IAAa,OAEX,MAAoB,SACtB,aAAa,CAAe,GAC5B,IAAkB,OAEhB,EAAQ,SAAS,KACnB,EAAM;EAEV;CACF,GAAG;EAAC;EAAiB;EAAgB;EAAa;CAAe,CAAC;AACpE;;;AC1VA,IAAM,KAAe;AAErB,SAAgB,KAAS;CAOvB,GAAyB;CAEzB,IAAM,EAAE,oBAAiB,cAAW,SAAM,oBAAiB,EAAa,GAClE,IAAQ,GAAS,GACjB,IAAW,GAAY,GACvB,IAAW,GAAY,GACvB,EAAE,MAAM,MAAW,GAAU,GAC7B,IAAc,GAAoB,MAAM,EAAE,OAAO,GACjD,IAAe,GAAoB,MAAM,EAAE,YAAY,GACvD,IAAY,GAAoB,MAAM,EAAE,SAAS,GACjD,IAAQ,GAAoB,MAAM,EAAE,KAAK,GACzC,IAAgB,GAAoB,MAAM,EAAE,GAAG,GAC/C,IAAoB,GAAoB,MAAM,EAAE,iBAAiB,GACjE,EAAE,MAAM,MAAiB,GAAgB,GAEzC,IAAO,IAAa,MAAM,EAAE,IAAI,GAChC,IAAc,IAAa,MAAM,EAAE,WAAW,GAC9C,IAAY,IAAa,MAAM,EAAE,SAAS,GAC1C,IAAW,IAAa,MAAM,EAAE,QAAQ,GACxC,IAAU,IAAa,MAAM,EAAE,OAAO,GAEtC,CAAC,GAAY,KAAiB,EAAiB,EAAY,GAC3D,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAS,KAAc,EAA4B,SAAS,GAC7D,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,CAAC,GAAiB,KAAsB,EAAiC,IAAI,GAE7E,IAAiB,MAAc,OAC/B,IAAe,MAAc,WAAW,MAAc,QACtD,IAAY,MAAc,aAAa,MAAc,QACrD,KAAS,GAAQ,UAAU,SAAS,KAAK,KAAK,IAE9C,IAAc,GAAa,IAA0B,cAAc;EAClE,MACL,EAAW,CAAI,GACf,EAAW,EAAI;CACjB,GAAG,CAAC,CAAc,CAAC,GAEb,IAAkB,GACrB,MAAkB;EACjB,IAAI,EAAI,WAAW,GAAG;EACtB,IAAM,IAAS,GAAY,SAAS,GAC9B,IAAiB,CAAC,EAAO,eAAe,EAAI,SAAS,EAAO,WAAW,GACvE,IAAO,EAAU,CAAG;EAC1B,IAAI,CAAC,GAAgB;EACrB,IAAM,IAAS,GAAc,CAAI;EACjC,EACE;GAAE,UAAU,EAAO;GAAU,QAAQ,EAAO;EAAO,GACnD,EAAE,SAAS,EAAO,QAAQ,CAC5B;CACF,GACA,CAAC,GAAW,CAAQ,CACtB,GAEM,IAAkB,GACrB,GAAsB,MAAsB;EAC3C,IAAM,IAAQ,GAAY,SAAS,GAC7B,IAAU,GAAqB,EAAM,MAAM,EAAM,aAAa,GAAO,CAAQ;EAC9E,OACL;OAAI,EAAQ,UAAU,SAAS,GAAG;IAChC,EAAmB,CAAO;IAC1B;GACF;GACA,EAAgB,EAAQ,GAAG;EAD3B;CAEF,GACA,CAAC,CAAe,CAClB,GAEM,KAAwB,QAAkB;EAC9C,EAAmB,IAAI;CACzB,GAAG,CAAC,CAAC,GAEC,KAAwB,QAAkB;EACzC,MACL,EAAmB,IAAI,GACvB,EAAgB,EAAgB,QAAQ;CAC1C,GAAG,CAAC,GAAiB,CAAe,CAAC,GAE/B,KAAqB,QAAkB;EACtC,MACL,EAAmB,IAAI,GACvB,EAAgB,EAAgB,GAAG;CACrC,GAAG,CAAC,GAAiB,CAAe,CAAC;CAgErC,AA9DA,GAAO,GAEP,QAAgB;EAMV,EAAM,WACN,CAAC,KAAa,CAAC,KACjB,EACE,GAAyB,GAAG,EAAS,WAAW,EAAS,SAAS,EAAS,MAAM,GACjF,EAAE,SAAS,GAAK,CAClB;CAEJ,GAAG;EAAC,EAAM;EAAS;EAAW;EAAiB;EAAU,EAAS;EAAM,EAAS;EAAU,EAAS;CAAM,CAAC,GAG3G,QAAgB;EACd,IAAM,IAAM,GAAM,MAAM,MAClB,IAAM,GAAc,MAAM;EAEhC,AADA,EAAS,GAAK,CAAG,GACb,KAAO,KAAK,EAAQ;CAC1B,GAAG;EAAC,GAAM;EAAI,GAAc;EAAI;EAAU;CAAO,CAAC,GAIlD,QAAgB;EACd,IAAI,CAAC,GAAc,YAAY;EAC/B,IAAM,IAA+B,CAAC;EACtC,KAAK,IAAM,KAAK,EAAa,YAAY,EAAI,EAAE,QAAQ,EAAE;EACzD,EAAkB,CAAG;CACvB,GAAG,CAAC,GAAc,CAAiB,CAAC,GAWpC,QAAgB;EACd,IAAI,CAAC,GAAQ;EACb,IAAM,IAAO,GAAiB,EAAO,YAAY,EAAO,SAAS,EAAS,QAAQ;EAClF,IAAI,GAAM;GACR,EAAc,CAAI;GAClB;EACF;EAEA,IAAM,IAAU;EAIhB,KAHyB,EAAO,cAAc,CAAC,GAAG,MAC/C,OAAO,EAAE,QAAQ,QAAkB,CAElC,GAAiB;EAErB,IAAM,KAAa,EAAO,cAAc,CAAC,GAAG,IAAI,QAAQ;EACxD,AAAI,MAAc,KAAS,EAAc,CAAS;CACpD,GAAG;EAAC;EAAQ,EAAS;EAAU;CAAU,CAAC,GAG1C,QAAgB;EACd,IAAM,KAAW,MAAqB;GACpC,IAAM,IAAS,SAAS,eAClB,IACJ,CAAC,CAAC,MACD,EAAO,YAAY,WAClB,EAAO,YAAY,cACnB,EAAO,oBACL,IAAM,EAAE,WAAW,EAAE;GAE3B,AAAI,KAAO,EAAE,IAAI,YAAY,MAAM,OACjC,EAAE,eAAe,GACjB,EAAY,SAAS,KACZ,KAAO,EAAE,IAAI,YAAY,MAAM,OACxC,EAAE,eAAe,GACjB,EAAY,KAAK,KACR,KAAO,EAAE,IAAI,YAAY,MAAM,OACxC,EAAE,eAAe,GACjB,EAAc,WAAW,MAAgB,cAAc,SAAS,WAAW,KAClE,KAAO,EAAE,IAAI,YAAY,MAAM,OACxC,EAAE,eAAe,GACjB,EAAgB,SAAS,KAChB,KAAO,EAAE,QAAQ,OAC1B,EAAE,eAAe,GACb,KAAc,GAAW,MAAM,CAAC,CAAC,KAC5B,EAAE,QAAQ,OAAO,CAAC,KAC3B,EAAE,eAAe,GACjB,EAAY,SAAS,KACZ,EAAE,QAAQ,YACnB,EAAW,EAAK;EAEpB;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAO,SAC7B,OAAO,oBAAoB,WAAW,CAAO;CAC5D,GAAG;EAAC;EAAa;EAAiB;EAAe;EAAa;CAAY,CAAC;CAE3E,IAAM,KAAc,QACb,IACE,GAAY,EAAO,YAAY,EAAO,SAAS,EAAS,UAAU,GAAM,GAAa,CAAQ,IADhF,CAAC,GAEpB;EAAC;EAAQ,EAAS;EAAU;EAAM;EAAa;CAAQ,CAAC;CAE3D,IAAI,GACF,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,QAAQ;GACR,YAAY;GACZ,gBAAgB;GAChB,YAAY;GACZ,OAAO;EACT;YACD;CAEI,CAAA;CAIT,IAAI,CAAC,GAAiB,OAAO;CAE7B,IAAM,KAAY,MAAe;EAC/B,EAAc,CAAE;EAQhB,IAAM,IAAS,GAAkB,GAAI,CAAM;EAC3C,AAAI,KAAQ,EAAS,CAAM;CAC7B,GAEM,MAAiB,MAAqB;EAC1C,QAAQ,EAAE,MAAV;GACE,KAAK;IACH,AAAI,EAAE,SAAO,EAAS,EAAE,KAAK;IAC7B;GACF,KAAK;IACH,AAAI,EAAE,SAAO,EAAS,EAAE,QAAQ,MAAM;IACtC;GACF,KAAK;IACH,AAAI,EAAE,SAAO,EAAS,EAAE,KAAK;IAC7B;GACF,KAAK;GACL,KAAK;IACH,AAAI,KAAc,EAAU,EAAI;IAChC;GACF,KAAK;IACH,EAAc,SAAS,MAAU,SAAS,UAAU,MAAM;IAC1D;GACF,KAAK;IACH,AAAI,EAAE,iBAAe,EAAgB,EAAE,aAAa;IACpD;EACJ;CACF;CAmCA,OAdI,EAAM,UAEN,kBAAC,IAAD,EAAA,UAAA;EACE,kBAAC,IAAD,CAAwB,CAAA;EACxB,kBAAC,IAAD,CAAoB,CAAA;EACpB,kBAAC,IAAD,CAAqB,CAAA;EACrB,kBAAC,IAAD,CAAsB,CAAA;EACtB,kBAAC,OAAD;GAAK,IAAG;GAAY,WAAU;aAC5B,kBAAC,IAAD,CAAS,CAAA;EACN,CAAA;CACe,EAAA,CAAA,IAKxB,kBAAC,IAAD,EAAA,UACA,kBAAC,OAAD;EAAK,IAAG;EAAM,gBAAc;EAAa,OAAO,EAAE,QAAQ,QAAQ;YAAlE;GACE,kBAAC,IAAD,CAAsB,CAAA;GACtB,kBAAC,IAAD,CAAqB,CAAA;GACrB,kBAAC,IAAD;IACe;IACb,eAAe,MAAM,EAAE,WAAW,EAAE,QAAQ;IAC5C,cAAc,EAAY,SAAS;IACnC,kBAAkB,KAAgB,GAAW,MAAM,CAAC,CAAC;IAC7C;IACR,qBAAqB,EAAS,UAAU;IACxC,mBAAmB,EAAS,cAAc;GAC3C,CAAA;GACD,kBAAC,IAAD;IACc;IACZ,QAAQ;IACR,kBAAkB,KAAgB,GAAW,MAAM,CAAC,CAAC;IAC7C;IACR,cAAc,EAAY,SAAS;IACnC,OAAO,GAAQ;IACf,UAAU,GAAc;GACzB,CAAA;GACD,kBAAC,IAAD,EAAqB,cAAa,CAAA;GAClC,kBAAC,QAAD;IACE,OAAO;KACL,UAAU;KACV,UAAU;KACV,SAAS;KACT,eAAe;KACf,UAAU;IACZ;cAPF;KASG,MAAiB,SAAS,kBAAC,IAAD;MAAU,aAAa,EAAY,KAAK;MAAG,gBAAgB;KAAkB,CAAA;KACxG,kBAAC,OAAD;MACE,OAAO;OACL,MAAM;OACN,UAAU;OACV,WAAW;OACX,SAAS;OACT,eAAe;MACjB;gBAEA,kBAAC,IAAD,CAAS,CAAA;KACN,CAAA;KACJ,MAAiB,YAAY,kBAAC,IAAD;MAAU,aAAa,EAAY,KAAK;MAAG,gBAAgB;KAAkB,CAAA;IACvG;;GACL,KACC,kBAAC,IAAD;IACE,MAAM;IACN,MAAM;IACN,eAAe,EAAW,EAAK;IAC/B,WAAW,MAAM,GAAc,CAAC;IAChC,YAAY,GAAQ;IACpB,SAAS,GAAQ;IACjB,WAAW;IACH;GACT,CAAA;GAEF,KAAgB,KAAU,kBAAC,IAAD;IAAgB,MAAM;IAAQ,eAAe,EAAU,EAAK;IAAG,OAAO;GAAS,CAAA;GAC1G,kBAAC,IAAD;IACE,SAAS;IACT,UAAU;IACV,cAAc;IACd,cAAc;GACf,CAAA;EACE;IACiB,CAAA;AAE1B;AAMA,SAAS,GAAkB,GAAkB,GAAkE;CAC7G,IAAI,CAAC,GAAQ;CACb,IAAM,IAAO,EAAO,OAAO,MAAM,MAAM,EAAE,SAAS,CAAQ;CAC1D,IAAI,GAAM,aAAa;EACrB,IAAM,IAAO,EAAO,QAAQ,EAAK;EACjC,IAAI,GAAM,OAAO,OAAO,EAAK;CAC/B;CACA,IAAM,KAAQ,MAAsC;EAClD,IAAI,EAAK,QAAQ;GACf,IAAM,IAAI,EAAO,QAAQ,EAAK;GAC9B,IAAI,GAAG,OAAO,OAAO,EAAE;EACzB;EACA,KAAK,IAAM,KAAK,EAAK,SAAS,CAAC,GAAG;GAChC,IAAM,IAAI,EAAK,CAAC;GAChB,IAAI,GAAG,OAAO;EAChB;EACA,KAAK,IAAM,KAAK,EAAK,YAAY,CAAC,GAAG;GACnC,IAAM,IAAI,EAAK,CAAC;GAChB,IAAI,GAAG,OAAO;EAChB;CAEF;CACA,KAAK,IAAM,KAAO,EAAO,cAAc,CAAC,GAAG;EACzC,KAAK,EAAI,QAAQ,QAAkB,GAAU;EAC7C,IAAM,IAAI,EAAK,CAAG;EAClB,IAAI,GAAG,OAAO;CAChB;AAEF;AAEA,SAAS,GACP,GACA,GACA,GACoB;CAIpB,IAAM,KAAW,MAAwB;EACvC,IAAI,CAAC,EAAE,QAAQ,OAAO;EACtB,IAAM,IAAI,EAAQ,EAAE;EACpB,OAAO,CAAC,CAAC,MAAM,EAAE,UAAU,KAAY,EAAS,WAAW,EAAE,QAAQ,GAAG;CAC1E,GACM,KAAQ,GAAY,MAAqC;EAC7D,IAAI,EAAQ,CAAC,GAAG,OAAO;EACvB,KAAK,IAAM,KAAK,EAAE,SAAS,CAAC,GAAG;GAC7B,IAAM,IAAM,EAAK,GAAG,CAAI;GACxB,IAAI,GAAK,OAAO;EAClB;EACA,KAAK,IAAM,KAAK,EAAE,YAAY,CAAC,GAAG;GAChC,IAAM,IAAM,EAAK,GAAG,CAAI;GACxB,IAAI,GAAK,OAAO;EAClB;CAEF;CACA,KAAK,IAAM,KAAO,KAAS,CAAC,GAAG;EAE7B,IAAM,IAAM,EAAK,GADJ,EAAI,QAAQ,EACC;EAC1B,IAAI,GAAK,OAAO;CAClB;AAEF;AAEA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACS;CAGT,IAAM,KAAU,GAAY,MAA2C;EACrE,IAAI,EAAE,QAAQ;GACZ,IAAM,IAAI,EAAQ,EAAE;GACpB,IAAI,MAAM,EAAE,UAAU,KAAY,EAAS,WAAW,EAAE,QAAQ,GAAG,IACjE,OAAO,CAAC,GAAG,GAAW,CAAC;EAE3B;EACA,KAAK,IAAM,KAAK,EAAE,SAAS,CAAC,GAAG;GAC7B,IAAM,IAAM,EAAO,GAAG,CAAC,GAAG,GAAW,CAAC,CAAC;GACvC,IAAI,GAAK,OAAO;EAClB;EACA,KAAK,IAAM,KAAK,EAAE,YAAY,CAAC,GAAG;GAChC,IAAM,IAAM,EAAO,GAAG,CAAC,GAAG,GAAW,CAAC,CAAC;GACvC,IAAI,GAAK,OAAO;EAClB;EACA,OAAO;CACT,GAEI,IAA0B;CAC9B,KAAK,IAAM,KAAO,KAAS,CAAC,GAE1B,IADA,IAAQ,EAAO,GAAK,CAAC,CAAC,GAClB,GAAO;CAEb,IAAI,CAAC,KAAS,EAAM,WAAW,GAAG,OAAO,CAAC;CAE1C,IAAM,IAAkB,CAAC;CACzB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK;EACrC,IAAM,IAAI,EAAM,IACV,IAAS,MAAM,EAAM,SAAS,GAC9B,IAAI,EAAE,SAAS,EAAQ,EAAE,UAAU,KAAA;EACzC,EAAO,KAAK;GACV,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE,UAAU;GAClC,MAAM,EAAE;GACR,SAAS,UAAU,EAAS,EAAE,KAAK,IAAI,KAAA;GACvC,QAAQ,MAAW,CAAC,KAAK,EAAE,UAAU;EACvC,CAAC;CACH;CAGA,IAAM,IAAO,EAAM,EAAM,SAAS,IAC5B,IAAa,EAAK,SAAS,EAAQ,EAAK,UAAU,KAAA;CACxD,IAAI,KAAc,MAAa,EAAW,SAAS,EAAS,WAAW,EAAW,QAAQ,GAAG,GAAG;EAC9F,IAAM,IAAS,EAAK,MAAM,MAAM,EAAE,OAAO,CAAW;EACpD,EAAO,KAAK;GACV,OAAO,GAAQ,SAAS;GACxB,MAAM;GACN,QAAQ;EACV,CAAC;CACH;CACA,OAAO;AACT;;;AC3eA,SAAS,GAAc,GAAoD;CACpE,OACL;MAAI,EAAE,IAAI,OAAO,EAAE;EACnB,KAAK,IAAM,KAAK,OAAO,KAAK,CAAC,GAAG;GAC9B,IAAM,IAAI,EAAE;GACZ,IAAI,GAAG,OAAO;EAChB;CAJmB;AAMrB;AAEA,SAAS,GAAe,GAA0D;CAChF,IAAM,IAA+B,CAAC;CACtC,IAAI,CAAC,GAAM,OAAO;CAClB,KAAK,IAAM,KAAK,GAAM,EAAI,EAAE,QAAQ,EAAE;CACtC,OAAO;AACT;AAMA,SAAS,GAAY,GAAmB;CACtC,IAAI;EACF,OAAO,IAAI,IAAI,CAAC,EAAE;CACpB,QAAQ;EACN,OAAO;CACT;AACF;AA4BA,SAAgB,GAAU,EACxB,aACA,eACA,YACA,eACA,YACA,WACA,gBACiB;CACjB,IAAM,IAAQ,GAAoB,MAAM,EAAE,KAAK,GACzC,IAAgB,GAAoB,MAAM,EAAE,GAAG,GAC/C,IAAoB,GAAoB,MAAM,EAAE,iBAAiB,GACjE,UAAoB,EAAc,SAAS,MAAU,SAAS,UAAU,MAAM,GAK9E,EAAE,MAAM,MAAiB,GAAgB;CAC/C,QAAgB;EACT,KACL,EAAkB,GAAe,EAAa,UAAU,CAAC;CAC3D,GAAG,CAAC,GAAc,CAAiB,CAAC;CAEpC,IAAM,IAAW,GAAc,UAGzB,IAAoB,KAAa,GAAU,YAAY,GAAU,YAAY,IAC7E,IAAkB,KAAW,GAAc,GAAU,YAAY,KAAK,IACtE,IAAqB,KAAc,GAAc,GAAU,eAAe,KAAK,IAC/E,IAAkB,KAAW,GAAU,iBAAiB,IACxD,IAAiB,QAAkD;EACvE,IAAI,KAAU,EAAO,QAAQ,OAAO;EACpC,IAAM,KAAgB,GAAU,eAAe,CAAC,GAC7C,KAAK,MAAM,GAAc,CAAC,CAAC,EAC3B,QAAQ,MAAmB,EAAQ,CAAE;EAMxC,OALK,EAAa,SAKX,EAAa,MAAM,GAAG,CAAC,EAAE,KAAK,MAAU,CAAC,GAAO,QAA6B,CAAC,IALpD,CAAC;CAMpC,GAAG,CAAC,GAAQ,GAAU,WAAW,CAAC,GAE5B,IAAkB,GAAU,oBAAoB,IAChD,IAAoB,GAAQ,KAAc,IAE1C,IAAY,GAAU,cAAc,IACpC,IAAgB,EAAQ,GAExB,IAAa,GAAU,aACvB,IAAc,QAOX;EALL,CAAC,YAAY,GAAY,gBAAgB,EAAE;EAC3C,CAAC,WAAW,GAAY,eAAe,EAAE;EACzC,CAAC,SAAS,GAAY,aAAa,EAAE;EACrC,CAAC,QAAQ,GAAY,YAAY,EAAE;CAE9B,EAAW,QAAQ,GAAG,OAAS,EAAQ,CAAI,GACjD;EAAC,GAAY;EAAc,GAAY;EAAa,GAAY;EAAW,GAAY;CAAQ,CAAC,GAE7F,IAAc,GAAU,gBAAgB,IACxC,IAAgB,sBAAK,IAAI,KAAK,GAAE,YAAY,IAAI,IAAc,IAAI,MAAgB,MAYlF,IAAO,GAAc,MACrB,IAAW,GAAM,WAAW,EAAK,SAAS,MAAM,GAAG,CAAC,IAAI,IACxD,IAAa,GAAM,UACrB,KAAY,MAAa,EAAK,UAC5B,GAAG,EAAK,QAAQ,GAAG,MACnB,EAAK,UACP,IAKE,IAJmB,CACvB,GAAM,SAAS,WAAW,EAAK,WAAW,IAC1C,IAAa,SAAS,MAAe,EACvC,EAAE,OAAO,OACQ,EAAiB,KAAK,KAAK;CAE5C,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,WAAW;GAAS,YAAY;GAAa,SAAS;GAAQ,eAAe;EAAS;YAApG;GACE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAAnF;KACE,kBAAC,IAAD;MAAW,SAAS,GAAU;MAAU,MAAM;KAAK,CAAA;KAElD,KACC,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,eAAe;MAAU;gBAAI;KAAwB,CAAA;KAEpG,KACC,kBAAC,QAAD;MAAM,WAAU;MAAO,OAAO;OAAE,YAAY;OAAG,UAAU;OAAM,OAAO;OAAiB,SAAS;OAAW,YAAY;OAAoB,QAAQ;OAA2B,cAAc;MAAE;gBAA9L;OAAkM;OAAW;OAAE;MAAsB;;KAEvO,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MACE,MAAyB,EAAnB,MAAU,SAAU,EAAK,MAAoB,EAAK,MAA/B,EAAU,MAAM,GAAK,CAA0B;MACxE,SAAS;MACT,OAAM;KACP,CAAA;IACE;;GAEL,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,SAAS;KAAQ,YAAY;KAAU,gBAAgB;KAAU,SAAS;IAAG;cAClG,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,qBAAqB;MAAW,KAAK;MAAG,OAAO;MAAK,UAAU;MAAQ,WAAW;MAAK,YAAY;MAAkB,QAAQ;MAA2B,cAAc;MAAI,UAAU;MAAU,WAAW;KAAmB;eAA1P,CAOE,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAkB,YAAY;OAAuH,OAAO;OAAwB,SAAS;OAAQ,eAAe;OAAU,UAAU;OAAY,UAAU;MAAS;gBAA9R;OACE,kBAAC,OAAD,EAAK,OAAO;QAAE,UAAU;QAAY,KAAK;QAAK,OAAO;QAAK,OAAO;QAAK,QAAQ;QAAK,cAAc;QAAO,YAAY;OAAwE,EAAI,CAAA;OAChM,kBAAC,OAAD,EAAK,OAAO;QAAE,UAAU;QAAY,QAAQ;QAAK,MAAM;QAAK,OAAO;QAAK,QAAQ;QAAK,cAAc;QAAO,YAAY;OAAwE,EAAI,CAAA;OAClM,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,YAAY;SAAU,KAAK;SAAG,UAAU;QAAW;kBAAlF;SACE,kBAAC,IAAD;UAAW,SAAS,GAAU;UAAU,MAAM;SAAK,CAAA;SAElD,KACC,kBAAC,QAAD;UAAM,OAAO;WAAE,UAAU;WAAI,YAAY;WAAK,eAAe;UAAW;oBAAI;SAAwB,CAAA;SAErG,KACC,kBAAC,QAAD;UAAM,WAAU;UAAO,OAAO;WAAE,UAAU;WAAI,SAAS;WAAW,QAAQ;WAA4C,cAAc;WAAG,OAAO;UAA6B;oBAAI;SAAsB,CAAA;QAEpM;;QAMH,GAAU,YAAY,MACtB,kBAAC,OAAD;QAAK,OAAO;SAAE,WAAW;SAAI,UAAU;SAAY,SAAS;SAAQ,eAAe;SAAU,YAAY;SAAU,KAAK;QAAG;kBAA3H,CACG,GAAU,YACT,kBAAC,IAAD;SAAW,SAAS,EAAS;SAAU,MAAM;SAAI,QAAQ;QAAI,CAAA,GAE9D,KACC,kBAAC,QAAD;SAAM,OAAO;UAAE,UAAU;UAAI,YAAY;UAAK,eAAe;UAAY,OAAO;SAAuB;mBAAI;QAAwB,CAAA,CAElI;;OAEP,kBAAC,OAAD;QAAK,OAAO;SAAE,WAAW;SAAQ,UAAU;QAAW;kBAAtD;SACG,KACC,kBAAC,MAAD;UAAI,OAAO;WAAE,QAAQ;WAAG,UAAU;WAAI,YAAY;WAAK,eAAe;WAAY,YAAY;UAAI;oBAAI;SAAoB,CAAA;SAE3H,KACC,kBAAC,KAAD;UAAG,OAAO;WAAE,QAAQ;WAAY,UAAU;WAAI,OAAO;WAA8B,YAAY;UAAK;oBAAI;SAAsB,CAAA;SAE/H,EAAe,SAAS,KACvB,kBAAC,OAAD;UAAK,OAAO;WAAE,WAAW;WAAI,SAAS;WAAQ,qBAAqB;WAAW,KAAK;WAAI,UAAU;WAAM,OAAO;UAA2B;oBACtI,EAAe,KAAK,CAAC,GAAO,OAAc;WACzC,IAAM,IAAI,EAAK;WACf,OACE,kBAAC,OAAD;YAAiB,OAAO;aAAE,SAAS;aAAQ,YAAY;aAAU,KAAK;YAAE;sBAAxE,CACG,KAAK,kBAAC,GAAD;aAAG,MAAM;aAAI,OAAO,EAAE,OAAO,kBAAkB;YAAI,CAAA,GACzD,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA,CAChB;cAHK,CAGL;UAET,CAAC;SACE,CAAA;SAEN,KACC,kBAAC,KAAD;UACE,MAAM;UACN,QAAO;UACP,KAAI;UACJ,WAAU;UACV,OAAO;WAAE,WAAW;WAAI,SAAS;WAAa,YAAY;WAAkC,QAAQ;WAA2C,cAAc;WAAG,SAAS;WAAQ,YAAY;WAAU,KAAK;WAAG,UAAU;WAAM,OAAO;WAA8B,gBAAgB;UAAO;oBAE1R,GAAY,CAAS;SACrB,CAAA;QAEF;;MACF;SAGL,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAa,SAAS;OAAQ,eAAe;OAAU,gBAAgB;MAAS;MACpG;KACE,CAAA,CACF;;GACF,CAAA;GAEL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAI,UAAU;KAAI,OAAO;IAAgB;cAAzH;KACE,kBAAC,QAAD,EAAA,UAAO,EAAoB,CAAA;KAC1B,EAAY,KAAK,CAAC,GAAO,OACxB,kBAAC,KAAD;MAAe,MAAM;MAAK,QAAO;MAAS,KAAI;MAAsB,OAAO,EAAE,OAAO,UAAU;gBAAI;KAAS,GAAnG,CAAmG,CAC5G;KACA,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA,GAC1B,kBAAC,QAAD;MAAM,WAAU;gBAAQ;KAAe,CAAA,CACvC,EAAA,CAAA;IAED;;EACF;;AAET;;;AC/QA,SAAgB,GAAW,EAAE,UAAO,MAAyB;CAC3D,OACE,kBAAC,OAAD;EAAK,OAAO;EAAM,QAAQ;EAAM,SAAQ;EAAY,eAAA;YAApD;GACE,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAqH,CAAA;GAC5I,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAsI,CAAA;GAC7J,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAwD,CAAA;GAC/E,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAA4I,CAAA;EAChK;;AAET;AAEA,SAAgB,GAAc,EAAE,UAAO,MAAyB;CAC9D,OACE,kBAAC,OAAD;EAAK,OAAO;EAAM,QAAQ;EAAM,SAAQ;EAAY,eAAA;YAApD;GACE,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAgB,CAAA;GACvC,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAkB,CAAA;GACzC,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAiB,CAAA;GACxC,kBAAC,QAAD;IAAM,MAAK;IAAU,GAAE;GAAmB,CAAA;EACvC;;AAET;;;ACrBA,IAAM,KAAoB,IAAI,IAAI,CAAC,gBAAgB,gBAAgB,CAAC;AAEpE,SAAgB,GAAmB,GAA2C;CAC7E,IAAI,CAAC,GAAQ,YAAY,OAAO;CAChC,IAAM,IAAQ,EAAO,SAAS,CAAC;CAC/B,IAAI,EAAM,SAAS,GAClB,KAAK,IAAM,KAAQ,GAClB,KAAK,IAAM,KAAQ,EAAO,YAAY;EAErC,KADiB,EAAK,QAAQ,eACb,EAAK,MAAM;EAC5B,IAAM,IAAQ,GAAqB,GAAQ,CAAI;EAC/C,IAAI,GAAO,OAAO;CACnB;CAGF,KAAK,IAAM,KAAQ,EAAO,YAAY;EACrC,IAAM,IAAQ,GAAqB,GAAQ,CAAI;EAC/C,IAAI,GAAO,OAAO;CACnB;CACA,OAAO;AACR;AAEA,SAAgB,GACf,GACA,GACS;CACT,IAAM,IAAW,GAAmB,CAAM,KAAK;CAE/C,IADI,CAAC,KACD,MAAkB,OAAO,MAAkB,UAAU,OAAO;CAChE,IAAM,IAAW,GAAS,CAAa;CACvC,IAAI,GAAkB,IAAI,CAAQ,GAAG,OAAO;CAC5C,KAAK,IAAM,KAAU,OAAO,OAAO,EAAO,WAAW,CAAC,CAAC,GAClD,OAAC,EAAO,SAAS,CAAC,GAAkB,GAAQ,CAAM,MAClD,GAAa,EAAO,KAAK,EAAE,KAAK,CAAQ,GAAG,OAAO;CAEvD,OAAO;AACR;AAEA,SAAS,GAAS,GAAsB;CACvC,IAAM,IAAY,EAAK,QAAQ,GAAG,GAC5B,IAAc,KAAa,IAAI,EAAK,MAAM,GAAG,CAAS,IAAI,GAC1D,IAAa,EAAY,QAAQ,GAAG;CAC1C,QACE,KAAc,IAAI,EAAY,MAAM,GAAG,CAAU,IAAI,GAAa,QAClE,QACA,EACD,KAAK;AAEP;AAEA,SAAS,GAAmB,GAAgB,GAA6B;CACxE,IAAM,IAAS,EAAO,QAAQ;CAC9B,OAAO,CAAC,CAAC,KAAU,GAAkB,GAAQ,CAAM;AACpD;AAEA,SAAS,GAAqB,GAAgB,GAA8B;CAC3E,IAAI,EAAK,UAAU,GAAmB,GAAQ,EAAK,MAAM,GACxD,OAAO,EAAO,QAAQ,EAAK,QAAQ;CAEpC,KAAK,IAAM,KAAS,CAAC,GAAI,EAAK,SAAS,CAAC,GAAI,GAAI,EAAK,YAAY,CAAC,CAAE,GAAG;EACtE,IAAM,IAAQ,GAAqB,GAAQ,CAAK;EAChD,IAAI,GAAO,OAAO;CACnB;CACA,OAAO;AACR;AAEA,SAAS,GAAkB,GAAgB,GAAyB;CACnE,IAAI,EAAO,SAAS,QAAQ,OAAO;CACnC,IAAM,IAAc,EAA+B;CAEnD,OAAO,EADQ,IAAa,EAAO,WAAW,KAAc,KAAA,IAC5C;AACjB;AAEA,SAAS,GAAa,GAAuB;CAC5C,IAAM,IAAO,EACX,QAAQ,QAAQ,EAAE,EAClB,MAAM,GAAG,EACT,KAAK,MACL,EAAQ,WAAW,GAAG,IACnB,UACA,EAAQ,QAAQ,uBAAuB,MAAM,CACjD;CACD,OAAW,OAAO,MAAM,EAAK,KAAK,GAAG,IAAI,GAAG;AAC7C;;;AClFA,IAAM,KAAiB;AAIvB,SAAgB,GACd,GACA,GACA,GACA,IAAW,IACH;CACR,OAAO,EAAI,IAAW,IAAO,CAAM,KAAK;AAC1C;AAEA,SAAgB,GAAa,GAA+C;CAC1E,IAAM,IAAa,GAAY,MAAM,MAAU,EAAM,SAAS,aAAa,GAAG;CAK9E,OAJI,OAAO,KAAe,YAAY,EAAW,KAAK,IAC7C,EAAW,KAAK,IAET,GACT,KAAW;AACpB;AAEA,SAAS,KAA6C;CACpD,IAAI,OAAO,YAAc,KAAa;CACtC,IAAM,IAAM,UAAU;CACjB,OACL,OAAO,EAAI,MAAM,GAAG,EAAE,MAAM,KAAA;AAC9B;;;ACVA,IAAM,KAA+B,8BAC/B,KAAiC,6EAEjC,KAA6D;CACjE,QAAW;EAAE,OAAO;EAA2B,MAAM,kBAAC,IAAD,EAAY,MAAM,GAAK,CAAA;CAAE;CAC9E,WAAW;EAAE,OAAO;EAA2B,MAAM,kBAAC,IAAD,EAAe,MAAM,GAAK,CAAA;CAAE;CACjF,OAAW;EAAE,OAAO;EAA2B,MAAM,kBAAC,IAAD,EAAe,MAAM,GAAK,CAAA;CAAE;AACnF;AAEA,SAAgB,KAAS;CACvB,IAAM,IAAW,GAAY,GACvB,CAAC,KAAgB,GAAgB,GACjC,EAAE,UAAO,mBAAgB,uBAAoB,GAAQ,GACrD,EAAE,MAAM,MAAiB,GAAgB,GACzC,EAAE,MAAM,MAAW,GAAU,GAC7B,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAM,KAAW,EAAS,EAAE,GAM7B,CAAC,GAAU,KAAe,EAAS,EAAI,GAEvC,IAAW,GAAqB,EAAa,IAAI,MAAM,KAAK,EAAa,IAAI,UAAU,CAAC,GACxF,IAAW,EAAa,IAAI,WAAW,GACvC,IAAS,GAAa,GAAc,UAAU,GAE9C,EAAE,MAAM,MAAgB,GAAS;EAAE,UAAU,CAAC,aAAa;EAAG,SAAS;CAAW,CAAC,GACnF,EAAE,MAAM,MAAiB,GAAwB;EACrD,UAAU,CAAC,eAAe;EAC1B,eAAe,EAAiB;EAChC,WAAW;CACb,CAAC;CAgBD,AAdA,QAAgB;EAAE,AAAI,GAAa,eAAa,EAAS,WAAW,EAAE,SAAS,GAAK,CAAC;CAAG,GAAG,CAAC,GAAa,CAAQ,CAAC,GAClH,QAAgB;EACd,IAAI,CAAC,KAAmB,CAAC,GAAQ;EACjC,IAAM,IAAS,GAAuB,GAAU,CAAM;EAStD,AARI,GAA2B,GAAU,CAAM,KAC7C,EAAM,QAAQ,GACZ,EAAO,UACP,8BACA,GACA,EACF,CAAC,GAEH,EAAS,GAAQ,EAAE,SAAS,GAAK,CAAC;CACpC,GAAG;EAAC;EAAiB;EAAQ;EAAU;EAAU;CAAM,CAAC,GACxD,QAAgB;EAAE,AAAI,KAAU,EAAM,MAAM,CAAQ;CAAG,GAAG,CAAC,CAAQ,CAAC;CAEpE,SAAS,EAAS,GAAc;EAE9B,AADA,EAAE,eAAe,GACjB,EACE;GAAE;GAAO,UAAU;GAAM;EAAS,GAClC,EACE,UAAU,MAAQ;GAChB,EAAM,MAAM,GAAkB,GAAK,GAAc,UAAU,CAAM,CAAC;EACpE,EACF,CACF;CACF;CAEA,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,QAAD;EAAgB;YAAhB;GACE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,OAAO;KAAiB,eAAe;KAAS,eAAe;IAAY;cAAG;GAAiB,CAAA;GAC9I,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAa,UAAU;KAAI,YAAY;KAAK,eAAe;IAAW;cAAG;GAAW,CAAA;GACzG,kBAAC,KAAD;IAAG,OAAO;KAAE,QAAQ;KAAY,UAAU;KAAI,OAAO;IAAgB;cAAG;GAA8C,CAAA;GAErH,KAAgB,EAAa,SAAS,KACrC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;KAAG,cAAc;IAAG;cAC9E,EAAa,KAAK,MAAM;KACvB,IAAM,IAAI,GAAO,EAAE,KAAK,YAAY,MAAM;MAAE,OAAO,iBAAiB,EAAE;MAAQ,MAAM,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;KAAE;KAC9G,OACE,kBAAC,GAAD;MAEE,MAAK;MACL,OAAO;OAAE,gBAAgB;OAAU,OAAO;MAAO;MACjD,MAAM,EAAE;MACR,UAAU,MAAM;OAAsB,AAApB,EAAE,eAAe,GAAG,OAAO,SAAS,OAAO,GAAgB,EAAE,MAAM,KAAA,GAAW,CAAQ;MAAG;gBAE1G,EAAE;KACA,GAPE,EAAE,IAOJ;IAET,CAAC;GACE,CAAA,GACL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAI,QAAQ;IAAS;cAA/E;KACE,kBAAC,QAAD,EAAM,OAAO;MAAE,MAAM;MAAG,QAAQ;MAAG,YAAY;KAAiB,EAAI,CAAA;KACpE,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAM,OAAO;OAAiB,eAAe;OAAU,eAAe;MAAY;gBAAG;KAAmB,CAAA;KACjI,kBAAC,QAAD,EAAM,OAAO;MAAE,MAAM;MAAG,QAAQ;MAAG,YAAY;KAAiB,EAAI,CAAA;IACjE;KACL,EAAA,CAAA;GAGJ,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAAhE;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,QAAiB,CAAA,GAC7B,kBAAC,GAAD;MAAW,OAAO;MAAO,UAAU;MAAU,QAAQ,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;MAAG,aAAY;MAAkB,WAAA;MAAU,UAAA;KAAU,CAAA,CAC9H,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;MAAS;gBAApD;OACE,kBAAC,IAAD,EAAA,UAAY,WAAoB,CAAA;OAChC,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;OAC1B,kBAAC,IAAD;QAAM,IAAG;QAAU,OAAO;SAAE,UAAU;SAAI,OAAO;SAAiB,cAAc;SAAG,gBAAgB;QAAO;kBAAG;OAAa,CAAA;MACvH;SACL,kBAAC,GAAD;MAAe,OAAO;MAAM,UAAU;MAAS,aAAY;KAAiB,CAAA,CACzE,EAAA,CAAA;KACL,kBAAC,SAAD;MAAO,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;OAAG,UAAU;OAAI,OAAO;MAAgB;gBAApG,CACE,kBAAC,SAAD;OACE,MAAK;OACL,MAAK;OACL,SAAS;OACT,WAAW,MAAM,EAAY,EAAE,OAAO,OAAO;MAC9C,CAAA,GAAC,mCACG;;KACP,kBAAC,GAAD;MACE,MAAK;MACL,MAAK;MACL,MAAK;MACL,OAAO;OAAE,gBAAgB;OAAU,OAAO;MAAO;MACjD,UAAU;gBAET,IAAiB,gBAAgB;KAC/B,CAAA;KACL,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,OAAO;OAAiB,WAAW;OAAU,WAAW;MAAE;gBAAtF,CAAyF,cAC7E,kBAAC,IAAD;OAAM,IAAG;OAAU,OAAO;QAAE,OAAO;QAAiB,YAAY;QAAK,gBAAgB;OAAO;iBAAG;MAAoB,CAAA,CAC1H;;IACF;;EACD;IACG,CAAA;AAEf;AAEA,SAAS,GAAkB,GAAY,GAA0B,GAAwB;CAIvF,OAHI,aAAe,MAAY,EAAI,SAAS,wBACnC,GAAiB,GAAU,4BAA4B,GAAQ,EAA4B,IAE7F,EAAI;AACb;AAEA,SAAS,GAA2B,GAAuB,GAA6B;CACtF,IAAI,MAAkB,GAAY,OAAO;CACzC,IAAM,IAAW,EAAc,MAAM,QAAQ,CAAC,EAAE,GAAG,QAAQ,QAAQ,EAAE,KAAK;CAC1E,OAAO,MAAa,OAAO,MAAa;AAC1C;;;ACtJA,SAAgB,KAAS;CACvB,IAAM,IAAW,GAAY,GACvB,EAAE,WAAQ,oBAAiB,uBAAoB,GAAQ,GACvD,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAW,KAAgB,EAAS,EAAE,GACvC,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAO,KAAY,EAAS,EAAK,GAElC,EAAE,MAAM,MAAiB,GAAgB,GACzC,IAAW,GAAc,UACzB,IAAkB,GAAU,oBAAoB,IAChD,IAAW,GAAU,aAAa,aAAa,IAC/C,IAAa,GAAU,aAAa,eAAe,IAEnD,EAAE,MAAM,MAAgB,GAAS;EAAE,UAAU,CAAC,aAAa;EAAG,SAAS;CAAW,CAAC;CAGzF,AADA,QAAgB;EAAE,AAAI,KAAe,CAAC,EAAY,eAAa,EAAS,UAAU,EAAE,SAAS,GAAK,CAAC;CAAG,GAAG,CAAC,GAAa,CAAQ,CAAC,GAChI,QAAgB;EAAE,AAAI,KAAiB,EAAS,KAAK,EAAE,SAAS,GAAK,CAAC;CAAG,GAAG,CAAC,GAAiB,CAAQ,CAAC;CAEvG,SAAS,EAAS,GAAc;EAC9B,EAAE,eAAe,GACZ,KAEL,EAAO;GAAE;GAAO,UAAU;GAAM,aADZ,CAAC,GAAO,CAAI,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;EACnB,CAAC;CAC/C;CAEA,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,QAAD;EAAgB;YAAhB;GACE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,OAAO;KAAiB,eAAe;KAAS,eAAe;IAAY;cAAG;GAAmB,CAAA;GAChJ,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAa,UAAU;KAAI,YAAY;KAAK,eAAe;IAAW;cAAG;GAAyB,CAAA;GACvH,kBAAC,KAAD;IAAG,OAAO;KAAE,QAAQ;KAAY,UAAU;KAAI,OAAO;IAAgB;cAAG;GAAuE,CAAA;GAC/I,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAAhE;KACE,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,qBAAqB;OAAW,KAAK;MAAG;gBAAvE,CACE,kBAAC,OAAD,EAAA,UAAA,CAAK,kBAAC,IAAD,EAAA,UAAY,aAAsB,CAAA,GAAC,kBAAC,GAAD;OAAW,OAAO;OAAO,UAAU;MAAW,CAAA,CAAM,EAAA,CAAA,GAC5F,kBAAC,OAAD,EAAA,UAAA,CAAK,kBAAC,IAAD,EAAA,UAAY,YAAqB,CAAA,GAAC,kBAAC,GAAD;OAAW,OAAO;OAAM,UAAU;MAAU,CAAA,CAAM,EAAA,CAAA,CACtF;;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,aAAsB,CAAA,GAClC,kBAAC,GAAD;MAAW,OAAO;MAAO,UAAU;MAAU,QAAQ,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;MAAG,aAAY;MAAmB,UAAA;KAAU,CAAA,CACrH,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,UAAmB,CAAA,GAC/B,kBAAC,GAAD;MAAW,OAAO;MAAS,UAAU;MAAY,QAAQ,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;KAAI,CAAA,CACpF,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,gBAAyB,CAAA,GACrC,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAW,QAAQ;OAA2B,cAAc;OAAG,UAAU;MAAS;gBAA7H,CACE,kBAAC,GAAD;OAAW,OAAO;OAAW,UAAU;OAAc,OAAO;QAAE,QAAQ;QAAG,cAAc;OAAE;MAAI,CAAA,GAC5F,KACC,kBAAC,QAAD;OAAM,WAAU;OAAO,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,SAAS;QAAU,YAAY;QAAoB,YAAY;QAA2B,UAAU;QAAI,OAAO;OAAgB;iBAAtM,CAAyM,KAAE,CAAsB;QAEhO;OACF,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,WAAoB,CAAA,GAChC,kBAAC,GAAD;MAAe,OAAO;MAAM,UAAU;MAAS,aAAY;MAAyB,cAAA;KAAc,CAAA,CAC/F,EAAA,CAAA;KACL,kBAAC,SAAD;MAAO,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAc,KAAK;OAAG,UAAU;OAAM,OAAO;OAAiB,YAAY;MAAI;gBAA3H,CACE,kBAAC,SAAD;OAAO,MAAK;OAAW,OAAO,EAAE,WAAW,EAAE;OAAG,SAAS;OAAO,WAAW,MAAM,EAAS,EAAE,OAAO,OAAO;MAAI,CAAA,GAC9G,kBAAC,IAAD;OAAyB;OAAsB;MAAa,CAAA,CACvD;;KACP,kBAAC,GAAD;MACE,MAAK;MACL,MAAK;MACL,MAAK;MACL,OAAO;OAAE,gBAAgB;OAAU,OAAO;MAAO;MACjD,UAAU,KAAmB,CAAC;gBAE7B,IAAkB,cAAc;KAC9B,CAAA;KACL,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,OAAO;OAAiB,WAAW;MAAS;gBAAxE,CAA2E,6BAChD,kBAAC,IAAD;OAAM,IAAG;OAAS,OAAO;QAAE,OAAO;QAAiB,YAAY;QAAK,gBAAgB;OAAO;iBAAG;MAAa,CAAA,CACjI;;IACF;;EACD;IACG,CAAA;AAEf;AAMA,SAAS,GAAc,EAAE,aAAU,iBAAwD;CAOzF,OACE,kBAAC,QAAD,EAAA,UAAA;EAAM;EAPY,IAClB,kBAAC,KAAD;GAAG,MAAM;GAAU,QAAO;GAAS,KAAI;GAAsB,OAAO,EAAE,OAAO,gBAAgB;aAAG;EAAQ,CAAA,IACtG,kBAAC,QAAD,EAAA,UAAM,QAAW,CAAA;EAKe;EAJd,IACpB,kBAAC,KAAD;GAAG,MAAM;GAAY,QAAO;GAAS,KAAI;GAAsB,OAAO,EAAE,OAAO,gBAAgB;aAAG;EAAU,CAAA,IAC1G,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;EAEiC;CAAO,EAAA,CAAA;AAEjE;;;ACpGA,SAAgB,KAAS;CACvB,IAAM,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,CAAC,GAAS,KAAc,EAAS,EAAK;CAE5C,eAAe,EAAS,GAAc;EACpC,MAAE,eAAe,GACb,IACJ;KAAW,EAAI;GACf,IAAI;IAGF,MAAM,GAAY,+BAA+B,EAAE,SAAM,CAAC;GAC5D,QAAQ,CAER,UAAU;IAER,AADA,EAAa,EAAI,GACjB,EAAW,EAAK;GAClB;EAVe;CAWjB;CAEA,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,QAAD;EAAgB;EAAU,eAAY;YAAtC;GACE,kBAAC,OAAD;IAAK,WAAU;IAAwB,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,eAAe;IAAQ;cAAG;GAAmB,CAAA;GAC9H,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAa,UAAU;KAAI,YAAY;KAAK,eAAe;IAAW;cAAG;GAAyB,CAAA;GACvH,kBAAC,KAAD;IAAG,WAAU;IAAwB,OAAO;KAAE,QAAQ;KAAY,UAAU;IAAG;cAAG;GAAiF,CAAA;GACjK,IA2BA,kBAAC,OAAD;IACE,eAAY;IACZ,MAAK;IACL,WAAU;IACV,OAAO;KAAE,SAAS;KAAI,cAAc;KAAG,UAAU;KAAM,SAAS;KAAQ,KAAK;KAAG,YAAY;IAAa;cAJ3G,CAME,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,GACvB,kBAAC,OAAD,EAAA,UAAK,wGAA0G,CAAA,CAC5G;QAlCL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAAhE;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,QAAiB,CAAA,GAC7B,kBAAC,GAAD;MACE,OAAO;MACP,UAAU;MACV,QAAQ,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;MAC/B,aAAY;MACZ,WAAA;MACA,UAAA;MACA,MAAK;MACL,cAAW;KACZ,CAAA,CACE,EAAA,CAAA;KACL,kBAAC,GAAD;MACE,MAAK;MACL,MAAK;MACL,MAAK;MACL,OAAO;OAAE,gBAAgB;OAAU,OAAO;MAAO;MACjD,UAAU;gBAET,IAAU,aAAa;KACrB,CAAA;KACL,kBAAC,IAAD;MAAM,IAAG;MAAS,WAAU;MAAuC,OAAO,EAAE,UAAU,GAAG;gBAAG;KAAuB,CAAA;IAChH;;EAYH;IACG,CAAA;AAEf;;;ACnDA,IAAM,KAAsB,GAEtB,KAAY,wBAEZ,KAAsB;AAE5B,SAAS,KAA4B;CACnC,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAM,IAAO,OAAO,SAAS,KAAK,WAAW,GAAG,IAC5C,OAAO,SAAS,KAAK,MAAM,CAAC,IAC5B,OAAO,SAAS;CAGpB,OAFK,IAEE,IADY,gBAAgB,CAC5B,EAAO,IAAI,GAAG,KAAK,KAFR;AAGpB;AAEA,SAAS,KAAuB;CAC9B,IAAI,OAAO,WAAa,KAAa;CACrC,IAAI,IAAO,SAAS,eAAe,EAAmB;CAClD,MACJ,IAAO,SAAS,cAAc,MAAM,GACpC,EAAK,KAAK,IACV,EAAK,OAAO,YACZ,EAAK,UAAU,eACf,SAAS,KAAK,YAAY,CAAI;AAChC;AAEA,SAAS,KAAuB;CAC9B,IAAI,OAAO,WAAa,KAAa;CACrC,IAAM,IAAO,SAAS,eAAe,EAAmB;CACxD,AAAI,KAAM,EAAK,OAAO;AACxB;AAEA,SAAgB,KAAQ;CACtB,IAAM,IAAW,GAAY,GAIvB,CAAC,KAAS,QAAuB,GAAkB,CAAC,GACpD,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAMtD,QAAgB;EACT,OASL,OARI,OAAO,SAAW,OAAe,OAAO,OAAO,SAAS,gBAAiB,cAC3E,OAAO,QAAQ,aAAa,MAAM,IAAI,QAAQ,GAEhD,GAAqB,GAKd;CACT,GAAG,CAAC,CAAK,CAAC;CAEV,IAAM,IAAe,QAAc,CAAC,GAAO,CAAC,CAAK,CAAC;CAElD,eAAe,EAAS,GAAc;EACpC,MAAE,eAAe,GACb,IACJ;OAAI,MAAa,GAAS;IACxB,EAAS,yBAAyB;IAClC;GACF;GACA,IAAI,EAAS,SAAS,IAAqB;IACzC,EAAS,6BAA6B,GAAoB,aAAa;IACvE;GACF;GAEA,AADA,EAAW,EAAI,GACf,EAAS,IAAI;GACb,IAAI;IACF,MAAM,GAAY,uBAAuB;KAAE;KAAO;KAAO;IAAS,CAAC;IAGnE,IAAI;KACF,OAAO,eAAe,QACpB,IACA,KAAK,UAAU;MAAE,MAAM;MAAW,MAAM;KAAoD,CAAC,CAC/F;IACF,QAAQ,CAER;IACA,EAAS,UAAU,EAAE,SAAS,GAAK,CAAC;GACtC,SAAS,GAAK;IASZ,AAAI,aAAe,MAAY,EAAI,SAAS,oBAC1C,EAAS,2DAA2D,IAC3D,aAAe,MAAY,EAAI,SAAS,uBACjD,EAAS,6BAA6B,GAAoB,aAAa,IAEvE,EAAS,oDAAoD;GAEjE,UAAU;IACR,EAAW,EAAK;GAClB;EAtCA;CAuCF;CAEA,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,OAAD;EAAK,eAAY;YAAjB;GACE,kBAAC,OAAD;IAAK,WAAU;IAAwB,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,eAAe;IAAQ;cAAG;GAAmB,CAAA;GAC9H,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAa,UAAU;KAAI,YAAY;KAAK,eAAe;IAAW;cAAG;GAAyB,CAAA;GACtH,IACC,kBAAC,OAAD;IAAK,eAAY;cAAjB,CACE,kBAAC,KAAD;KAAG,WAAU;KAAwB,OAAO;MAAE,QAAQ;MAAY,UAAU;KAAG;eAAG;IAE/E,CAAA,GACH,kBAAC,IAAD;KACE,IAAG;KACH,WAAU;KACV,OAAO,EAAE,UAAU,GAAG;eACvB;IAEK,CAAA,CACH;QAEL,kBAAC,QAAD;IAAgB;IAAU,eAAY;cAAtC,CACE,kBAAC,KAAD;KAAG,WAAU;KAAwB,OAAO;MAAE,QAAQ;MAAY,UAAU;KAAG;eAAG;IAE/E,CAAA,GACH,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,eAAe;MAAU,KAAK;KAAG;eAAhE;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,QAAiB,CAAA,GAC7B,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,QAAQ,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC/B,aAAY;OACZ,WAAA;OACA,UAAA;OACA,MAAK;OACL,cAAW;MACZ,CAAA,CACE,EAAA,CAAA;MACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,eAAwB,CAAA,GACpC,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,aAAY;OACZ,cAAA;MACD,CAAA,CACE,EAAA,CAAA;MACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAY,uBAAgC,CAAA,GAC5C,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,aAAY;MACb,CAAA,CACE,EAAA,CAAA;MACJ,KACC,kBAAC,OAAD;OACE,eAAY;OACZ,MAAK;OACL,WAAU;OACV,OAAO;QACL,SAAS;QACT,cAAc;QACd,UAAU;OACZ;iBAEC;MACE,CAAA;MAEP,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,MAAK;OACL,OAAO;QAAE,gBAAgB;QAAU,OAAO;OAAO;OACjD,UAAU;iBAET,IAAU,cAAc;MACtB,CAAA;MACL,kBAAC,IAAD;OACE,IAAG;OACH,WAAU;OACV,OAAO,EAAE,UAAU,GAAG;iBACvB;MAEK,CAAA;KACH;MACD;;EAEL;IACI,CAAA;AAEf;;;AC/NA,SAAgB,KAAQ;CACtB,IAAM,IAAW,GAAY,GACvB,IAAO,EAAuC,CAAC,CAAC;CAEtD,SAAS,EAAQ,GAAW;EAE1B,AADA,EAAK,QAAQ,IAAI,MAAM,GACvB,EAAK,QAAQ,IAAI,OAAO;CAC1B;CAEA,SAAS,EAAS,GAAW,GAAkC;EAC7D,IAAM,IAAI,EAAE,OAAO,MAAM,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG,CAAC;EAEtD,AADA,EAAE,OAAO,QAAQ,GACb,KAAK,IAAI,KAAG,EAAQ,IAAI,CAAC;CAC/B;CAEA,SAAS,EAAU,GAAW,GAAoC;EAChE,AAAI,EAAE,QAAQ,eAAe,CAAE,EAAE,OAA4B,SAAS,IAAI,KAAG,EAAQ,IAAI,CAAC;CAC5F;CAEA,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,YAAY;IAAK,OAAO;IAAiB,eAAe;IAAS,eAAe;GAAY;aAAG;EAAe,CAAA;EAC5I,kBAAC,MAAD;GAAI,OAAO;IAAE,QAAQ;IAAa,UAAU;IAAI,YAAY;IAAK,eAAe;GAAW;aAAG;EAAmB,CAAA;EACjH,kBAAC,KAAD;GAAG,OAAO;IAAE,QAAQ;IAAY,UAAU;IAAI,OAAO;GAAgB;aAAG;EAA0D,CAAA;EAClI,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,KAAK;IAAG,gBAAgB;IAAU,QAAQ;GAAS;aAC/E;IAAC;IAAG;IAAG;IAAG;IAAG;IAAG;GAAC,EAAE,KAAK,MACvB,kBAAC,SAAD;IAEE,MAAM,MAAO;KAAE,EAAK,QAAQ,KAAK;IAAI;IACrC,WAAW;IACX,WAAU;IACV,WAAW,MAAM,EAAS,GAAG,CAAC;IAC9B,YAAY,MAAM,EAAU,GAAG,CAAC;IAChC,OAAO;KACL,OAAO;KAAI,QAAQ;KAAI,WAAW;KAAU,UAAU;KAAI,YAAY;KACtE,YAAY;KAAoB,QAAQ;KAA2B,cAAc;KACjF,SAAS;KAAQ,OAAO;KAAe,YAAY;IACrD;GACD,GAXM,CAWN,CACF;EACE,CAAA;EACL,kBAAC,GAAD;GAAK,MAAK;GAAK,MAAK;GAAU,OAAO;IAAE,gBAAgB;IAAU,OAAO;GAAO;GAAG,eAAe,EAAS,GAAG;aAAG;EAE3G,CAAA;EACL,kBAAC,OAAD;GAAK,OAAO;IAAE,WAAW;IAAI,SAAS;IAAQ,gBAAgB;IAAiB,UAAU;GAAG;aAA5F,CACE,kBAAC,KAAD;IAAG,MAAK;IAAI,OAAO;KAAE,OAAO;KAAiB,gBAAgB;IAAO;cAAG;GAAkB,CAAA,GACzF,kBAAC,IAAD;IAAM,IAAG;IAAS,OAAO;KAAE,OAAO;KAAiB,gBAAgB;IAAO;cAAG;GAAY,CAAA,CACtF;;CACF,EAAA,CAAA,EACI,CAAA;AAEf;;;ACpDA,SAAgB,KAAkB;CAChC,IAAM,IAAW,GAAY,GACvB,CAAC,KAAgB,GAAgB,GACjC,EAAE,YAAS,kBAAe,EAAa,GACvC,IAAc,GAAe;CAoBnC,OAlBA,QAAgB;EAId,IAAM,IAAW,GAAqB,EAAa,IAAI,UAAU,CAAC;EAElE,EAAM,EACH,MAAM,MAAS;GAId,AAHA,EAAQ,GAAM,EAAK,MAAM,GACzB,EAAiB,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,GAC3D,EAAY,EAAE,KAAK,CAAU,EAAE,YAAY,CAAC,CAAC,GAC7C,EAAS,GAAU,EAAE,SAAS,GAAK,CAAC;EACtC,CAAC,EACA,YAAY;GACX,EAAS,0CAA0C,EAAE,SAAS,GAAK,CAAC;EACtE,CAAC;CACL,GAAG;EAAC;EAAU;EAAa;EAAc;EAAS;CAAU,CAAC,GAG3D,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAwB;EAAwB,CAAA;CAC1D,CAAA;AAET;;;AC/BA,SAAgB,KAAe;CAC7B,IAAM,IAAQ,GAAS,GACjB,IAAW,GAAiB,GAC5B,IAAW,GAAY,GACvB,IAAuB,GAAyB;CA8BtD,OAtBA,QAAgB;EACd,AAAI,EAAM,WACR,EAAqB,EAAS,UAAU,WAAW;CAGvD,GAAG;EAAC,EAAM;EAAS,EAAS;EAAU;CAAoB,CAAC,GAEvD,EAAM,UAEN,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;EACV;YAEA,kBAAC,KAAD;GAAG,OAAO;IAAE,UAAU;IAAI,OAAO;GAAgB;aAAG;EAA4B,CAAA;CAC7E,CAAA,IAKP,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;EACV;YAEA,kBAAC,OAAD;GAAK,OAAO,EAAE,WAAW,SAAS;aAAlC;IACE,kBAAC,MAAD;KAAI,OAAO;MAAE,UAAU;MAAI,YAAY;MAAK,OAAO;MAAiB,QAAQ;KAAE;eAAG;IAAO,CAAA;IACxF,kBAAC,KAAD;KAAG,OAAO;MAAE,WAAW;MAAG,UAAU;MAAI,OAAO;KAAgB;eAAG;IAAiB,CAAA;IACnF,kBAAC,OAAD;KAAK,OAAO,EAAE,WAAW,GAAG;eAC1B,kBAAC,GAAD;MAAK,eAAe,EAAS,GAAG;gBAAG;KAAY,CAAA;IAC5C,CAAA;GACF;;CACF,CAAA;AAET;;;ACzCA,SAAgB,GAAqB,EAAE,SAAM,YAAS,eAAuC;CAC3F,IAAM,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAEtD,IAAI,CAAC,GAAM,OAAO;CAElB,IAAM,UAAc;EAA+D,AAA7D,EAAW,EAAE,GAAG,EAAQ,EAAE,GAAG,EAAW,EAAE,GAAG,EAAS,IAAI,GAAG,EAAW,EAAK;CAAG,GAChG,UAAc;EAAW,AAAT,EAAM,GAAG,EAAQ;CAAG,GAEpC,IAAY,EAAQ,SAAS,KAAK,EAAK,UAAU,KAAK,MAAY;CAsBxE,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GAAE,UAAU;GAAS,OAAO;GAAG,YAAY;GAAgB,QAAQ;GAAI,gBAAgB;EAAY;CAC3G,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GAAS,KAAK;GAAO,MAAM;GAAO,WAAW;GACvD,QAAQ;GAAI,OAAO;GAAK,YAAY;GAAkB,cAAc;GACpE,WAAW;GAAoB,QAAQ;GAA2B,UAAU;EAC9E;YALF;GAOE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAe,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAArF;KACE,kBAAC,QAAD;MAAM,OAAO;OACX,OAAO;OAAI,QAAQ;OAAI,cAAc;OAAG,YAAY;OAAsB,OAAO;OACjF,SAAS;OAAe,YAAY;OAAU,gBAAgB;MAChE;gBACE,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;KACjB,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO,EAAE,MAAM,EAAE;gBAAtB,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO;OAAc;iBAAG;MAAoB,CAAA,GACzF,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;OAAgB;iBAAG;MAAiE,CAAA,CACtH;;KACL,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;KAAQ,CAAA;IACnD;;GAEL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAkB,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAA3F;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;MAAY,UAAA;gBAAS;KAA4B,CAAA,GACjD,kBAAC,GAAD;MAAe,OAAO;MAAS,UAAU;MAAY,aAAY;KAA+B,CAAA,CAC7F,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;MAAY,UAAA;gBAAS;KAAwB,CAAA,GAC7C,kBAAC,GAAD;MAAe,OAAO;MAAM,UAAU;MAAS,aAAY;MAAwB,cAAA;KAAc,CAAA,CAC9F,EAAA,CAAA;KACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;MAAY,UAAA;gBAAS;KAAgC,CAAA,GACrD,kBAAC,GAAD;MAAe,OAAO;MAAS,UAAU;MAAY,aAAY;KAAyB,CAAA,CACvF,EAAA,CAAA;KACJ,KACC,kBAAC,OAAD;MAAK,OAAO;OACV,SAAS;OAAY,YAAY;OAAsB,cAAc;OACrE,UAAU;OAAM,OAAO;OAAiB,SAAS;OAAQ,KAAK;MAChE;gBAHA,CAIE,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA,GACtB,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA,CAChB;;IAEJ;;GAEL,kBAAC,OAAD;IAAK,OAAO;KACV,SAAS;KAAa,WAAW;KACjC,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,YAAY;IAC7D;cAHA;KAIE,kBAAC,QAAD;MAAM,WAAU;MAAO,OAAO;OAAE,UAAU;OAAM,OAAO;MAAgB;gBAAG;KAA6B,CAAA;KACvG,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MAAK,SAAS;gBAAO;KAAW,CAAA;KAChC,kBAAC,GAAD;MACE,MAAK;MACL,MAAgB,EAAV,IAAW,IAAwB,EAAK,KAA9B,EAAS,MAAM,GAAK,CAAyB;MAC7D,SAAS,YAjFI;OACrB,IAAI,CAAC,GAAW;QACd,AAAI,EAAK,SAAS,IAAG,EAAS,6CAA6C,IAEtE,EADI,MAAY,IACP,wBADsB,yBACD;QACnC;OACF;OAEA,AADA,EAAS,IAAI,GACb,EAAW,EAAI;OACf,IAAI;QAGF,AAFA,MAAM,EAAS;SAAE;SAAS;QAAK,CAAC,GAChC,EAAM,GACN,EAAQ;OACV,SAAS,GAAG;QACV,EAAS,aAAa,QAAQ,EAAE,UAAU,4BAA4B;OACxE,UAAU;QACR,EAAW,EAAK;OAClB;MACF;MAgEU,UAAU,CAAC,KAAa;gBAEvB,IAAU,cAAc;KACtB,CAAA;IACF;;EACF;GACL,EAAA,CAAA;AAEN;;;AC3GA,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA;CAMA,OAAO,GAAiB;EACtB,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GAZkB,IAClB,KAAK,UAAU,OAAO,QAAQ,CAAO,EAAE,KAAK,CAAC,IAC7C,KAAA;EAYF;EACA,UAAU,EAAE,mBAAgB;GAC1B,IAAM,IAAS,IAAI,gBAAgB,GAE7B,IAAoE,CACxE;IAAE,OAAO;IAAY,IAAI;IAAM,OAAO;GAAS,CACjD;GACA,IAAI,QACG,IAAM,CAAC,GAAO,MAAU,OAAO,QAAQ,CAAO,GACjD,AAAI,KAAS,MAAU,aACrB,EAAY,KAAK;IAAE;IAAO,IAAI;IAAM;GAAM,CAAC;GASjD,OALA,EAAO,IAAI,WAAW,KAAK,UAAU,CAAW,CAAC,GAC7C,KAAM,EAAO,IAAI,QAAQ,CAAI,GAC7B,KAAO,EAAO,IAAI,SAAS,OAAO,CAAK,CAAC,GACxC,KAAQ,EAAO,IAAI,UAAU,CAAM,GACnC,KAAW,EAAO,IAAI,SAAS,CAAS,GACrC,GACL,aAAa,EAAW,GAAG,EAAO,SAAS,GAC7C;EACF;EACA,mBAAmB,MACjB,EAAS,MAAM,WAAW,EAAS,KAAK,SAAS,KAAA;EACnD,kBAAkB,KAAA;EAClB,SACE,CAAC,CAAC,KAAgB,CAAC,CAAC,KAAY,CAAC,CAAC,KAAc,CAAC,CAAC;CACtD,CAAC;AACH;;;ACxDA,SAAgB,GAAoB,EAClC,SACA,iBACA,eACA,YACA,cACA,gBAC2B;CAE3B,OADK,IAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,eAAe,CAAC,KAAa,EAAa,EAAK;EAC/C,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB;CACD,CAAA,GACD,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,QAAQ;GACR,OAAO;GACP,YAAY;GACZ,cAAc;GACd,WAAW;GACX,QAAQ;GACR,UAAU;EACZ;YAbF,CAeE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAa,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAG;aAAnF;IACE,kBAAC,QAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,OAAO;MACP,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,YAAY;KACd;eAEA,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAClB,CAAA;IACN,kBAAC,OAAD;KAAK,OAAO;MAAE,MAAM;MAAG,UAAU;KAAE;eAAnC,CACE,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,OAAO;MAAc;gBAAI;KAAgB,CAAA,GACtF,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAM,OAAO;OAAiB,WAAW;MAAE;gBAAI;KAAa,CAAA,CACjF;;IACL,kBAAC,GAAD;KAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;KAAG,eAAe,CAAC,KAAa,EAAa,EAAK;IAAI,CAAA;GACrF;MACL,kBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,WAAW;IACX,SAAS;IACT,YAAY;IACZ,KAAK;IACL,YAAY;GACd;aARF;IAUE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;IAC1B,kBAAC,GAAD;KAAK,eAAe,EAAa,EAAK;KAAG,UAAU;eAAW;IAEzD,CAAA;IACL,kBAAC,GAAD;KACE,MAAK;KACL,MAAkB,EAAZ,IAAa,IAAwB,EAAK,MAA9B,EAAS,MAAM,GAAK,CAA0B;KAChE,SAAS;KACT,UAAU;eAET,IAAY,eAAe;IACzB,CAAA;GACF;IACF;GACL,EAAA,CAAA,IA1Ec;AA4EpB;;;AClFA,SAAS,GAAqB,GAAkB;CAC9C,EAAM,MAAM,EAAI,OAAO;AACzB;AAgBA,SAAS,GACP,GACA,GACA,IAAqE,CAAC,GAChE;CASN,AARA,EAAY,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAU,EAAE,CAAC,GACvE,EAAY,kBAAkB,EAAE,UAAU,CAAC,gBAAgB,CAAU,EAAE,CAAC,GACpE,EAAK,QACP,EAAY,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAU,EAAE,CAAC,GAErE,EAAK,YACP,EAAY,kBAAkB,EAAE,UAAU,CAAC,IAAqB,CAAU,EAAE,CAAC,GAE3E,EAAK,eACP,EAAY,kBAAkB,EAAE,UAAU,CAAC,iBAAiB,CAAU,EAAE,CAAC,GACzE,EAAY,kBAAkB,EAAE,UAAU,CAAC,qBAAqB,CAAU,EAAE,CAAC;AAEjF;AAEA,SAAgB,GAAgB,GAAoB;CAClD,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,MACX,GAAa,GAAY,CAAI;EAC/B,iBAAiB;GAEf,AADA,GAAuB,GAAa,CAAU,GAC9C,EAAM,QAAQ,gBAAgB;EAChC;EACA,SAAS;CACX,CAAC;AACH;AAEA,SAAgB,GAAgB,GAAoB;CAClD,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,EAAE,aAAU,cACvB,EAAa,GAAY,GAAU,CAAI;EACzC,iBAAiB;GAEf,AADA,GAAuB,GAAa,GAAY;IAAE,MAAM;IAAM,UAAU;IAAM,YAAY;GAAK,CAAC,GAChG,EAAM,QAAQ,gBAAgB;EAChC;EACA,SAAS;CACX,CAAC;AACH;AAEA,SAAgB,GAAgB,GAAoB;CAClD,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,MAAgC,EAAa,GAAY,CAAQ;EAC9E,iBAAiB;GAEf,AADA,GAAuB,GAAa,GAAY,EAAE,MAAM,GAAK,CAAC,GAC9D,EAAM,QAAQ,gBAAgB;EAChC;EACA,SAAS;CACX,CAAC;AACH;AAEA,SAAgB,GAAiB,GAAoB;CACnD,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,MAAgC,GAAc,GAAY,CAAQ;EAC/E,iBAAiB;GAEf,AADA,GAAuB,GAAa,GAAY;IAAE,MAAM;IAAM,YAAY;GAAK,CAAC,GAChF,EAAM,QAAQ,iBAAiB;EACjC;EACA,SAAS;CACX,CAAC;AACH;AAEA,SAAgB,GAAiB,GAAqB;CACpD,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,YAAY,OAAO,EAAE,eAAY,cAA+D;GAC9F,IAAM,IAAS,MAAM,EAAc,GAAY,CAAI;GACnD,IAAI,EAAO,WAAW,SACpB,MAAU,MAAM,6BAA6B;GAE/C,OAAO;EACT;EACA,YAAY,MAAW;GAIrB,AAHI,KACF,GAAuB,GAAa,GAAY;IAAE,MAAM;IAAM,UAAU;IAAM,YAAY;GAAK,CAAC,GAE9F,EAAO,WAAW,aACpB,EAAM,QAAQ,2CAA2C,IAChD,EAAO,WAAW,YAC3B,EAAM,QAAQ,gEAAgE,IAE9E,EAAM,QAAQ,iBAAiB;EAEnC;EACA,SAAS;CACX,CAAC;AACH;;;AC1HA,SAAgB,GACd,GACA,GACS;CACT,IAAI,CAAC,KAAc,EAAW,WAAW,GAAG,OAAO;CAEnD,KAAK,IAAM,KAAQ,GAAY;EAE7B,IAAI,EAAK,UAAU,EAAK,OAAO,SAAS,GAAG;GACzC,IAAM,IAAY,EAAI;GAEtB,IADI,OAAO,KAAc,YACrB,CAAC,EAAK,OAAO,SAAS,CAAS,GAAG,OAAO;EAC/C;EAEA,IAAI,EAAK,SACH,CAAC,GAAuB,GAAM,CAAG,GAAG,OAAO;CAEnD;CACA,OAAO;AACT;AAOA,SAAS,GACP,GACA,GACS;CACT,IAAM,IAAW,EAAI,EAAK,QACpB,IAAK,EAAK,MAAM;CAOtB,IAAI,MAAO,QAAQ,MAAO,UAAU;EAClC,IAAM,IAAM,MAAM,QAAQ,EAAK,KAAK,IAAI,EAAK,QAAQ,CAAC,EAAK,KAAK,GAC1D,IAAW,KAAY,OAAO,KAAK,OAAO,CAAQ,GAClD,IAAU,EAAI,MAAM,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC,OAAO,CAAQ;EACzE,OAAO,MAAO,OAAO,IAAU,CAAC;CAClC;CAeA,OAZI,OAAO,EAAK,SAAU,aAAa,MAAO,OACrC,GAAS,CAAQ,MAAM,EAAK,QAIjC,OAAO,KAAa,YAAY,OAAO,EAAK,SAAU,WACjD,GAAc,GAAU,EAAK,OAAO,CAAE,IAMxC,GAFU,KAAY,OAAO,KAAK,OAAO,CAAQ,GACxC,EAAK,SAAS,OAAO,KAAK,OAAO,EAAK,KAAK,GACnB,CAAE;AAC5C;AAEA,SAAS,GAAS,GAAuB;CAKvC,OAJI,KAAO,OAAa,KACpB,OAAO,KAAQ,YAAkB,IACjC,OAAO,KAAQ,WAAiB,MAAQ,KACxC,OAAO,KAAQ,WAAiB,MAAQ,IACrC;AACT;AAEA,SAAS,GAAyC,GAAM,GAAM,GAAqB;CACjF,QAAQ,GAAR;EACE,KAAK,MAAM,OAAO,MAAM;EACxB,KAAK,MAAM,OAAO,MAAM;EACxB,KAAK,MAAM,OAAO,IAAI;EACtB,KAAK;EACL,KAAK,MAAM,OAAO,KAAK;EACvB,KAAK,MAAM,OAAO,IAAI;EACtB,KAAK;EACL,KAAK,MAAM,OAAO,KAAK;EACvB,SAAS,OAAO;CAClB;AACF;AAQA,SAAgB,GACd,GACA,GACyC;CAEzC,OADK,IACE,OAAO,QAAQ,CAAO,EAC1B,QAAQ,GAAG,OAAY,EAAO,YAAY,SAAS,CAAU,CAAC,EAC9D,KAAK,CAAC,GAAM,QAAa;EAAE;EAAM;CAAO,EAAE,IAHxB,CAAC;AAIxB;AAOA,SAAgB,GACd,GACA,GACA,GACA,GACyC;CAUzC,OATI,CAAC,KAAiB,CAAC,IAAsB,CAAC,IAC1C,MAAsB,KAAA,IAQnB,GAAuB,GAAe,CAAkB,IAPtD,EACJ,KAAK,MAAS;EACb,IAAM,IAAS,IAAgB,MAAS,IAAgB;EACxD,OAAO,IAAS;GAAE;GAAM;EAAO,IAAI;CACrC,CAAC,EACA,QAAQ,MAAmD,MAAS,IAAI;AAG/E;AAKA,SAAgB,GAAgB,GAAyB;CACvD,IAAM,IAAS,EAAO;CAEtB,OADK,IACE,EAAO,SAAS,IADH;AAEtB;AAeA,SAAgB,GAAoB,GAAyB;CAC3D,OAAO,CAAC,CAAC,EAAO;AAClB;AAMA,SAAgB,GACd,GACA,GAC6C;CACxC,OACL;OAAK,IAAM,KAAU,OAAO,OAAO,EAAO,OAAO,GAC/C,IAAI,EAAO,WAAW,KAAc,EAAO,SAAS,QAAQ;GAC1D,IAAM,IAAS,EAAO,SAAS;GAC/B,OAAO;IACL,OAAO,EAAO;IACd,MAAM,EAAO,SAAS,QAAQ,GAAQ,SAAS,QAAQ;GACzD;EACF;;AAGJ;AA2DA,SAAgB,GACd,GACA,GACA,GACA,GACA,GAC4B;CAC5B,IAAM,IAAkC,CAAC;CACzC,KAAK,IAAM,KAAQ,GAAQ;EACzB,IAAM,IAAS,GAAkB,GAAM,GAAe,GAAe,GAAK,CAAY;EACtF,AAAI,MAAW,QACb,EAAI,KAAK,CAAM;CAEnB;CACA,OAAO;AACT;AAGA,SAAS,GACP,GACA,GACA,GACA,GACA,GACiC;CAIjC,OAHI,EAAK,SAAS,UACT,GAAmB,GAAM,GAAe,GAAe,GAAK,CAAY,IAE1E,GAAkB,GAAM,GAAe,GAAe,GAAK,CAAY;AAChF;AAGA,SAAS,GACP,GACA,GACA,GACA,GACA,GAC4B;CAC5B,IAAM,IAAoC,CAAC;CAC3C,KAAK,IAAM,KAAS,EAAM,OAAO;EAC/B,IAAM,IAAS,GAAkB,GAAO,GAAe,GAAe,GAAK,CAAY;EACvF,AAAI,MAAW,QACb,EAAM,KAAK,CAAM;CAErB;CAIA,OAHI,EAAM,WAAW,IACZ,OAEF;EAAE,GAAG;EAAO;CAAM;AAC3B;AAGA,SAAS,GACP,GACA,GACA,GACA,GACA,GAC2B;CAC3B,IAAM,IAAS,GAAe,EAAK,MAAM,GAAe,CAAa;CAWrE,IAVI,CAAC,KAUD,CAAC,GAAmB,GAAmB,EAAO,UAAU,GAAG,CAAG,GAChE,OAAO;CAKT,IAAM,EAAE,YAAS,cAAW,GAAkB,GAAQ,GAAK,CAAY;CAEvE,OAAO;EAAE,GAAG;EAAM;EAAS;CAAO;AACpC;AASA,SAAS,GACP,GACA,GACA,GAC6C;CAC7C,IAAM,IAAa,GAAmB,EAAO,UAAU;CACvD,IAAI,CAAC,KAAc,EAAW,WAAW,GACvC,OAAO;EAAE,SAAS;EAAM,QAAQ;CAAK;CAGvC,IAAM,IAAa,GAAS,EAAO,iBAAiB;CAIpD,IAAI,EAAa,SAAS,GAAG;EAC3B,IAAI,IAAe;EACnB,KAAK,IAAM,KAAe,GACxB,AAAK,GAAmB,GAAY,CAAW,KAC7C;EAGJ,IAAI,IAAe,GAAG;GACpB,IAAM,IAAY,IAAI,EAAa,MAAM,EAAa,OAAO;GAE7D,OAAO;IAAE,SAAS;IAAO,QADN,IAAa,GAAG,EAAW,GAAG,MAAc;GACnB;EAC9C;EACA,OAAO;GAAE,SAAS;GAAM,QAAQ;EAAK;CACvC;CAMA,OAHK,GAAmB,GAAY,CAAG,IAGhC;EAAE,SAAS;EAAM,QAAQ;CAAK,IAF5B;EAAE,SAAS;EAAO,QAAQ;CAAW;AAGhD;AAMA,SAAS,GACP,GACA,GACA,GACoB;CACpB,IAAI,KAAiB,KAAQ,GAC3B,OAAO,EAAc;CAEvB,IAAI,KAAiB,KAAQ,GAC3B,OAAO,EAAc;AAGzB;AAOA,SAAS,GACP,GAC+B;CAC/B,OAAO;AACT;AAMA,SAAS,GAAS,GAAgD;CAChE,IAAI,CAAC,GAAI,OAAO;CAChB,IAAI,EAAG,IAAO,OAAO,EAAG;CACxB,IAAM,IAAO,OAAO,OAAO,CAAE;CAC7B,OAAO,EAAK,SAAS,IAAI,EAAK,KAAK;AACrC;AA4CA,SAAgB,GACd,GACA,GACA,GACA,GACA,GAK4B;CAC5B,IAAM,IAAkC,CAAC;CACzC,KAAK,IAAM,KAAQ,GAAQ;EACzB,IAAI,EAAK,SAAS,SAAS;GACzB,IAAM,IAAQ,GAAoB,EAAK,OAAO,GAAe,GAAe,GAAM,CAAI;GACtF,AAAI,EAAM,SAAS,KACjB,EAAI,KAAK;IAAE,GAAG;IAAM;GAAM,CAAC;GAE7B;EACF;EACA,IAAM,IAAS,GAAe,EAAK,MAAM,GAAe,CAAa;EAChE,KACA,EAAO,YAAY,SAAS,CAAI,MACjC,GAAM,iBAAiB,KAAA,MACV,EAAK,wBAAwB,EAAK,sBAAsB,CAAM,IAAI,QAClE,EAAK,gBAElB,GAAM,aAAa,EAAK,UAAU,CAAM,KAC5C,EAAI,KAAK,CAAI;CACf;CACA,OAAO;AACT;AAyBA,SAAgB,GAAwB,GAAyB;CAC/D,OAAO,EAAO,YAAY,SAAS,eAAe,KAAK;AACzD;AAYA,SAAgB,GACd,GACA,GACA,GACA,GACA,GAC4B;CAC5B,OAAO,EAAO,KAAK,MAAS;EAC1B,IAAI,EAAK,SAAS,SAChB,OAAO;GACL,GAAG;GACH,OAAO,GAAmB,EAAK,OAAO,GAAe,GAAe,GAAW,CAAM;EACvF;EAEF,IAAM,IAAS,GAAe,EAAK,MAAM,GAAe,CAAa;EAIrE,OAHI,KAAU,EAAU,CAAM,IACrB;GAAE,GAAG;GAAM,SAAS;GAAO;EAAO,IAEpC;CACT,CAAC;AACH;AAEA,SAAgB,GACd,GACA,GAC4B;CAe5B,OAdK,IAMY,OAAO,QAAQ,CAAO,EACpC,QAAQ,GAAG,OAAY,EAAO,YAAY,SAAS,CAAI,CAAC,EACxD,MAAM,CAAC,GAAO,IAAI,CAAC,GAAO,OAAO;EAChC,IAAM,IAAO,EAAE,YAAY,GACrB,IAAO,EAAE,YAAY;EAE3B,OADI,MAAS,IACN,IAAQ,IAAQ,KAAK,MAAQ,KADV,IAAO;CAEnC,CACK,EAAS,KAAK,CAAC,QAAW;EAC/B,MAAM;EACN;EACA,SAAS;EACT,QAAQ;CACV,EAAE,IAnBmB,CAAC;AAoBxB;;;ACrgBA,SAAgB,GAAa,EAC3B,eACA,WACA,eACA,eAAY,CAAC,GACb,QACA,aAAU,WACV,UAAO,WACP,cACA,iBACA,iBACA,qBACoB;CACpB,IAAM,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAa,KAAkB,EAAS,EAAK,GAC9C,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAe,KAAoB,EAAqC,IAAI,GAC7E,IAAkB,GAAiB,CAAU,GAC7C,EAAE,MAAM,MAAW,GAAU,GAC7B,IAAc,GAAe,GAE7B,IAAgB,GAAoB,CAAM,GAC1C,IAAY,GAAgB,CAAM,GAClC,IAAkB,CAAC,CAAC,EAAO,gBAAgB,OAAO,KAAK,EAAO,YAAY,EAAE,SAAS,GAGrF,IAAgB,IAAM,GAAmB,EAAO,YAAY,CAAG,IAAI,IAMnE,IAA4B,EAAY,YAAY;EACpD,OAAC,EAAO,eAAe,EAAU,WAAW,IAChD;KAAc,EAAI;GAClB,IAAI;IAEF,IAAM,IAAS,MAAM,EADH,IAAa,GAAG,EAAW,GAAG,EAAO,gBAAgB,EAAO,aAC3B,CAAS,GACtD,IAAe,GAAwB,GAAQ,EAAO,aAAa;IACzE,IAAI,CAAC,GAAc;KACjB,EAAM,MAAM,4BAA4B,EAAO,eAAe;KAC9D;IACF;IACA,IAAM,IAAe,GAAQ,SAAS,EAAO,gBACvC,IAAc,EAAI,EAAO,SAAS,IAAI,KAAK,EAAO;IACxD,IAAI,EAAO,IAAI,WAAW,GAAG;KAC3B,EAAM,KAAK,cAAc,EAAY,OAAO;KAC5C;IACF;IAEA,IAAM,IAAK,GAAc,OAAO,MAAM,EAAE,MAAM,MAAM,EAAa,OAAO,IAAI,CAAC,GAAG,OAAO,KAAK,MACtF,IAAS,IAAI,gBAAgB;IAOnC,AANA,EAAO,IAAI,UAAU,KAAM,EAAO,IAAI,KAAK,GAAG,CAAC,GAC/C,EAAY,EAAa,OAAO;KAC9B,OAAO;KACP,MAAM,EAAO,SAAS,QAAQ,EAAa;KAC3C,QAAQ,EAAO,SAAS;IAC1B,CAAC,GACD,IAAY;GACd,SAAS,GAAK;IACZ,EAAM,MAAM,aAAe,QAAQ,EAAI,UAAU,+BAA+B;GAClF,UAAU;IACR,EAAc,EAAK;GACrB;EA7BkB;CA8BpB,GAAG;EAAC;EAAQ;EAAY;EAAW;EAAQ;EAAa;CAAS,CAAC,GAK5D,IAAsB,EAAO,SAAS,GAAG,EAAO,OAAO,GAAG,MAAe,GAEzE,IAAY,EAAU,SAAS,IAAI,IAAY,KAAA,GAQ/C,IACJ,EAAO,4BAA4B,OAAO,KAAK,EAAO,wBAAwB,EAAE,SAAS,IACrF,EAAO,2BACP,KAAA,GAIA,IAAsB,GACzB,GAA+F,MAA6B;EAG3H,IAFA,IAAc,GAEV,EAAO,WAAW,YAAY;GAChC,IAAY;GACZ;EACF;EAEA,IAAM,IAAW,EAAO,QAAgD;EACxE,IAAI,GAAS;GACX,EAAiB,CAAO;GACxB;EACF;EACA,AAAI,EAAO,WAAW,YACpB,IAAiB,EAAU,QAAQ,EAAO,OAAO,IAEjD,IAAY;CAEhB,GACA;EAAC,EAAU;EAAQ;EAAW;CAAc,CAC9C,GAEM,IAAc,QAAkB;EACpC,IAAI,GAAW,CAAM,GAAG;GAEtB,GAAoB,EAAO,UAAW,EAAE,OAAI,CAAC,EAAE,WAAW,IAAY,CAAC;GACvE;EACF;EACA,IAAI,GAAe;GACjB,EAA0B;GAC1B;EACF;EAaA,IAAI,EAAO,QAAQ,CAAC,EAAO,WAAW,CAAC,EAAO,aAAa;GACzD,IAAM,IAAQ,KAAO,OAAO,EAAI,MAAU,WAAY,EAAI,KAAmB,IACzE,IAAM,EAAO;GAEjB,AADI,MAAY,IAAM,EAAI,WAAW,aAAa,CAAU,IACxD,MAAO,IAAM,EAAI,WAAW,SAAS,mBAAmB,CAAK,CAAC;GAKlE,IAAM,IAAO,EAAI,QAAQ,GAAG,GACtB,IAAO;IACX,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;IACpC,MAAM,EAAO,SAAS;GACxB;GAMA,AALI,MAAS,KACX,EAAY,GAAK,CAAI,IAErB,EAAY,EAAI,MAAM,GAAG,CAAI,GAAG;IAAE,GAAG;IAAM,QAAQ,EAAI,MAAM,IAAO,CAAC;GAAE,CAAC,GAE1E,IAAY;GACZ;EACF;EACA,AAAI,IACF,EAAc,EAAI,IACT,IACT,EAAe,EAAI,KAGnB,IAAe,EAAU,MAAM,GAC/B,EAAgB,OACd;GAAE,YAAY;GAAqB,MAAM,EAAgB,GAAY,CAAS;EAAE,GAChF;GACE,YAAY,MAAW,EAAoB,CAAM;GACjD,eAAe,IAAe,EAAU,MAAM;EAChD,CACF;CAEJ,GAAG;EAAC;EAAQ;EAAK;EAAY;EAAY;EAAa;EAAe;EAAW;EAAiB;EAAqB;EAAW;EAAW;EAAY;EAAiB;EAAqB;EAA2B;EAAc;EAAc;CAAS,CAAC,GAEzP,IAAgB,QAAkB;EAEtC,AADA,IAAe,EAAU,MAAM,GAC/B,EAAgB,OACd;GAAE,YAAY;GAAqB,MAAM,EAAgB,GAAY,CAAS;EAAE,GAChF;GACE,YAAY,MAAW,EAAoB,SAAc,EAAe,EAAK,CAAC;GAC9E,eAAe,IAAe,EAAU,MAAM;EAChD,CACF;CACF,GAAG;EAAC;EAAqB;EAAW;EAAW;EAAY;EAAiB;EAAqB;EAAc;CAAY,CAAC,GAEtH,KAAqB,GACxB,MAAoC;EAEnC,AADA,IAAe,EAAU,MAAM,GAC/B,EAAgB,OACd;GAAE,YAAY;GAAqB,MAAM,EAAgB,GAAQ,CAAS;EAAE,GAC5E;GACE,YAAY,MAAW,EAAoB,SAAc,EAAc,EAAK,CAAC;GAC7E,eAAe,IAAe,EAAU,MAAM;EAChD,CACF;CACF,GACA;EAAC;EAAqB;EAAW;EAAW;EAAiB;EAAqB;EAAc;CAAY,CAC9G,GAEM,IAAc,EAAI,EAAO,SAAS,IAAI,GACtC,IAAW,EAAO,SAAS,MAC3B,KAAQ,KAAa,EAAK,IAC1B,KAAW,KAAQ,kBAAC,IAAD,EAAO,MAAM,GAAK,CAAA,IAAI,IAAW,kBAAC,GAAD;EAAa,MAAM;EAAU,MAAM;CAAK,CAAA,IAAI,MAChG,KACJ,CAAC,KAAoD,EAAgB,aAAa;CAGpF,OACE,kBAAA,GAAA,EAAA,UAAA;EACG,MAAS,SACR,kBAAC,GAAD;GACE,MAAM,MAAY,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GACxC,SAAS;GACT,OAAO;EACR,CAAA,IAED,kBAAC,GAAD;GACQ,MAZD,MAAY,YAAY,YAAY,MAAY,UAAU,UAAU;GAazE,MAAsB;GACtB,MAAM;GACN,QAAQ,EAAO;GACf,SAAS;GACT,UAAU;GACV,OAAO;GACP,cAAY;aAEX;EACE,CAAA;EAGN,KACC,kBAAC,IAAD;GACE,MAAM;GACN,cAAc;GACN;GACR,YAAY;GACZ,UAAU;GACV,WAAW,EAAgB;GACtB;EACN,CAAA;EAGF,KACC,kBAAC,IAAD;GACE,MAAM;GACN,cAAc;GACd,YAAY;GACZ,SAAS,EAAI,EAAO,YAAY;GAChC,WAAW;GACX,WAAW,EAAgB;EAC5B,CAAA;EAGF,KACC,kBAAC,IAAD;GACE,MAAM,CAAC,CAAC;GACR,eAAe;IAEb,AADA,EAAiB,IAAI,GACrB,IAAY;GACd;GACA,SAAS;EACV,CAAA;CAEH,EAAA,CAAA;AAEN;;;ACzQA,SAAgB,GAAgB,EAC9B,YACA,cACA,QACA,cACA,YACA,YACA,UAAO,IACP,WAAQ,SACe;CACvB,IAAM,IAAS,EAAiC,IAAI,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,EAAE,WAAQ,GAAgB;CAGhC,IAAI,CAAC,KAAW,EAAQ,WAAW,GAAG,OAAO;CAE7C,IAAM,KAAM,YAAyB;EAEnC,AADA,EAAQ,EAAK,GACb,EAAG;CACL;CAEA,OACE,kBAAC,OAAD;EACE,WAAU;EACV,aAAW,IAAO,SAAS,KAAA;EAC3B,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,gBAAgB;EAAW;YAH7E,CAKE,kBAAC,GAAD;GACE,KAAK;GACC;GACN,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,KAAK,MAAM,IAAO,GAAI,EAAI,CAAA;GACjD,eAAe,GAAS,MAAS,CAAC,CAAI;GACtC,OAAM;EACP,CAAA,GACD,kBAAC,IAAD;GACQ;GACN,QAAQ,EAAO;GACf,eAAe,EAAQ,EAAK;GAC5B,OAAO;GACA;aALT;IAOG;IACA,KAAW,KAAW,EAAQ,SAAS,KAAK,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;IAC/D,EAAQ,KAAK,EAAE,SAAM,gBAAa;KACjC,IAAM,IAAgB,IAAM,GAAmB,EAAO,YAAY,CAAG,IAAI,IACnE,IAAO,EAAI,EAAO,SAAS,IAAI,KAAK,GACpC,IAAW,EAAO,SAAS,MAC3B,IAAQ,KAAY,EAAK,KAAa,EAAK,KAAiC,KAAA;KAElF,OACE,kBAAC,GAAD;MAEQ,MAJG,IAAQ,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,IAAI;MAKvC,QAAQ,EAAO;MACf,SAAS,QAAS;OAChB,IAAI,CAAC,GAAe;OACpB,IAAM,IAAW,EAAO,SAAS,GAAG,EAAO,OAAO,GAAG,MAAS;OAC9D,EAAI;QACF;QACA,OAAO;QACP,SAAS,OAAO,MAAW;SACzB,IAAM,IAAS,MAAM,EAAc,GAAU,EAAgB,GAAQ,CAAS,CAAC;SAE/E,OADA,IAAY,GACL;QACT;OACF,CAAC;MACH,CAAC;gBAEA;KACO,GAlBH,CAkBG;IAEd,CAAC;GACO;IACP;;AAET;;;AC7FA,SAAgB,GAAkB,EAChC,WACA,eACA,iBACA,gBACA,mBACA,YACA,mBACA,kBACyB;CACzB,IAAM,IAAY,MAAe,IAC3B,IAAa,KAAgB,EAAa,SAAS;CAIzD,OAFI,CAAC,KAAa,CAAC,IAAmB,OAGpC,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,cAAc;EAAE;YAA7E,CACG,KACC,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;GAAI;aACnC,kBAAC,GAAD;IACE,OAAO;IACP,UAAU;IACV,aAAY;IACZ,QAAQ,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;GACjC,CAAA;EACE,CAAA,GAEN,KACC,EAAa,KAAK,MAAgB;GAChC,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAW;GAC3C,IAAI,CAAC,GAAO,OAAO;GACnB,IAAM,IAAa,EAAQ,MAAgB,WACrC,IAAQ,GAAiB,GAAO,GAAa,CAAW;GAoB9D,OAlBI,EAAM,SAAS,SAAS,EAAM,KAAK,SAEnC,kBAAC,IAAD;IAES;IACA;IACP,OAAO;IACP,WAAW,MAAM,EAAe,GAAa,CAAC;GAC/C,GALM,CAKN,IAIA,EAAM,SAOT,kBAAC,OAAD;IAAuB,OAAO,EAAE,OAAO,IAAI;cACzC,kBAAC,IAAD;KACE,OAAO;KACE,SAAA,CARb;MAAE,OAAO;MAAW,OAAO,OAAO;KAAQ,GAC1C,GAAG,EAAM,OAAO,KAAK,OAAO;MAAE,OAAO,EAAE;MAAM,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;KAAK,EAAE,CAO/D;KACT,WAAW,MAAM,EAAe,GAAa,CAAC;KAC9C,aAAa;IACd,CAAA;GACE,GAPK,CAOL,IAdmB;EAgB5B,CAAC,CACA;;AAET;AAEA,SAAS,GAAkB,EACzB,UACA,UACA,UACA,eAMC;CACD,IAAM,IAAY,EAAU,EAAM,KAAK,MAAM,GACvC,IAAe,EAAM,SAAS,qBAAqB,GAAW,SAAS,eACvE,IAAK,QAEP,IACK,CAAC,GAAG,EAAU,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,OAAO,IAAI,MAAM,OAC1D,MACN,CAAC,CAAS,CACZ,GAEM,EAAE,MAAM,MAAa,GAAc,EAAM,KAAK,QAAQ;EAC1D,UAAU;EACV,MAAM,IAAe,GAAG,EAAa,QAAQ,KAAA;CAC/C,CAAC,GACK,IAAU,GAAU,QAAQ,CAAC,GAE7B,KAAkB,MAAyB;EAC/C,IAAI,KAAO,MAAM,OAAO;EACxB,IAAI,OAAO,KAAQ,YAAY,CAAC,MAAM,QAAQ,CAAG,GAAG;GAClD,IAAM,IAAQ,OAAO,OAAO,CAA8B,EAAE;GAC5D,OAAO,KAAS,OAAuB,MAAhB,OAAO,CAAK;EACrC;EACA,OAAO,OAAO,CAAG;CACnB;CAiBA,OACE,kBAAC,OAAD;EAAK,OAAO,EAAE,OAAO,IAAI;YACvB,kBAAC,IAAD;GAAe;GAAO,SAAS,CAhBjC;IAAE,OAAO;IAAW,OAAO,OAAO;GAAQ,GAC1C,GAAG,EAAQ,KAAK,MAAM;IACpB,IAAM,IAAK,OAAO,EAAE,EAAG,GACjB,IAAW,EAAE;IAOnB,OAAO;KAAE,OAAO;KAAI,OALlB,OAAO,KAAa,YAAY,MAAa,KACzC,IACA,IACE,EAAe,EAAE,MAAiB,CAAE,IACpC;IAC4B;GACtC,CAAC,CAKgC;GAAyB;GAAU,aAAa;EAAQ,CAAA;CACpF,CAAA;AAET;;;ACdA,SAAgB,GAAY,GAAkB,GAAsC;CAClF,OAAO,EAAS,QAAQ,mBAAmB,GAAO,MAC5C,OAAO,UAAU,eAAe,KAAK,GAAM,CAAG,IACzC,EAAK,KAEP,CACR;AACH;AAcA,SAAS,GAAW,GAAwF;CAM1G,OALI,EAAO,SAAe,WACtB,EAAO,OAAa,SACpB,EAAO,UAAgB,YACvB,EAAO,cAAoB,gBAC3B,EAAO,YAAY,EAAO,SAAS,SAAS,IAAU,aACnD;AACT;AAYA,SAAS,GACP,GACA,GACQ;CACR,IAAI,CAAC,GAAK,OAAO;CACjB,IAAI,GACF,IAAI;EACF,OAAO,GAAmB,GAAQ,GAAgB,GAAQ,CAAG,CAAC;CAChE,QAAQ,CAER;CAEF,IAAM,IAAK,EAAI;CACf,OAAO,OAAO,KAAO,WAAW,IAAK,KAAM,OAAO,KAAK,OAAO,CAAE;AAClE;AAWA,SAAS,GACP,GACA,GACA,GACA,GACA,GACS;CACT,IAAM,IAAO,GAAW,CAAM,GACxB,IAAQ,GAAQ,GAAK,GAAe,MAAM,GAC1C,IAAS,GAAe,cAAc;CAQ5C,IANA,IAAgB;EAAE;EAAM;EAAQ;EAAM;CAAM,CAAC,GAMzC,MAAS,UAIX,OAHA,QAAQ,KACN,iCAAiC,EAAO,OAAO,0BAA0B,EAAK,mEAChF,GACO;CAUT,IAAI,MAAS,UAAU,GAAe;EAOpC,IAAM,IAAM,GAAY,EAAO,QAAQ,IAAI;GACzC;GACA,IAAI,mBAAmB,CAAK;EAC9B,CAAC,GACK,IAAO,EAAI,QAAQ,GAAG;EAM5B,OALI,MAAS,KACX,EAAc,YAAY,CAAG,IAE7B,EAAc,YAAY,EAAI,MAAM,GAAG,CAAI,GAAG,EAAE,QAAQ,EAAI,MAAM,IAAO,CAAC,EAAE,CAAC,GAExE;CACT;CAcA,OAXI,MAAS,aAAa,MAAS,iBAAiB,MAAS,cAMzD,MAAS,aACX,QAAQ,KACN,yDAAyD,EAAK,sDAChE,GARO;AAWX;AAgFA,SAAS,GAAY,GAA2C;CAC9D,IAAI,IAAI;CACR,KAAK,IAAM,KAAQ,GACjB,AAAI,EAAK,SAAS,WAAU,MACvB,KAAK,GAAY,EAAK,KAAK;CAElC,OAAO;AACT;AAGA,SAAS,GACP,GACA,GACA,GAC2E;CAC3E,IAAM,IAAqC,CAAC,GACtC,IAAoC,CAAC;CAC3C,KAAK,IAAM,KAAQ,GACjB,AAAI,EAAK,SAAS,YACD,GAAa,EAAK,MAAM,GAAe,CAClD,GAAQ,cACV,EAAY,KAAK,CAAI,IAErB,EAAO,KAAK,CAAI;CAOtB,OAAO;EAAE;EAAQ;CAAY;AAC/B;AAgBA,SAAS,GACP,GACA,GACA,GAKA;CACA,IAAM,IAAgC,CAAC,GACjC,IAAsC,CAAC,GACvC,IAAoC,CAAC;CAC3C,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAI,EAAK,SAAS,UAAU;GAE1B,EAAQ,KAAK,CAAI;GACjB;EACF;EACA,IAAM,IAAS,GAAa,EAAK,MAAM,GAAe,CAAa,GAC7D,IAAgB,GAAQ,gBAAgB,IACxC,IAAY,GAAQ,SAAS,YAAY;EAC/C,IAAI,KAAiB,GAAW;GAM9B,AAHA,QAAQ,KACN,iCAAiC,EAAK,KAAK,kEAC7C,GACA,EAAY,KAAK,CAAI;GACrB;EACF;EACA,IAAI,GAAe;GACjB,EAAY,KAAK,CAAI;GACrB;EACF;EACA,IAAI,GAAW;GACb,EAAQ,KAAK,CAAI;GACjB;EACF;EACA,EAAQ,KAAK,CAAI;CACnB;CACA,OAAO;EAAE;EAAS;EAAS;CAAY;AACzC;AAGA,SAAS,GACP,GACA,GACA,GACoB;CACpB,IAAI,KAAiB,KAAQ,GAAe,OAAO,EAAc;CACjE,IAAI,KAAiB,KAAQ,GAAe,OAAO,EAAc;AAEnE;AAGA,SAAS,GACP,GACA,GACA,GACQ;CAER,OAAO,EADQ,GAAa,GAAM,GAAe,CACtC,GAAQ,SAAS,IAAI,KAAK;AACvC;AAGA,SAAS,GACP,GACA,GACA,GACoB;CAEpB,OADe,GAAa,GAAM,GAAe,CAC1C,GAAQ,SAAS,QAAQ,KAAA;AAClC;AAMA,SAAS,GAAU,GAAsB;CACvC,OAAO,kBAAkB;AAC3B;AAoBA,SAAS,GAAQ,EACf,SACA,kBACA,kBACA,UACA,YACA,oBACA,eACA,UACA,QACA,kBACA,oBACe;CACf,IAAM,IAAS,GAAa,EAAK,MAAM,GAAe,CAAa,GAC7D,IAAO,GAAgB,EAAK,MAAM,GAAe,CAAa,GAC9D,IAAW,GAAgB,EAAK,MAAM,GAAe,CAAa,GAClE,IAAgB,GAAQ,eAAe,IAKvC,IAAY,CAAC,KAAiB,GAAQ,SAAS,YAAY,IAC3D,IAAa,CAAC,EAAK,SACnB,IAAQ,IAAa,GAAU,EAAK,IAAI,IAAI,KAAA,GAE5C,IAAc,EAAY,YAAY;EACtC,OAAC,KAAU,KAAc,IAC7B;KAAgB,EAAI;GACpB,IAAI;IAEF,AADgB,GAAe,EAAK,MAAM,GAAQ,GAAK,GAAe,CACjE,KACH,MAAM,QAAQ,QAAQ,EAAM,EAAK,MAAM,CAAM,CAAC;GAElD,UAAU;IACR,EAAgB,EAAK;GACvB;EARoB;CAStB,GAAG;EAAC;EAAQ;EAAY;EAAS;EAAO,EAAK;EAAM;EAAiB;EAAK;EAAe;CAAa,CAAC,GAEhG,IAAoB,IACtB,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IACpB,IACE,kBAAC,GAAD;EAAa,MAAM;EAAU,MAAM;CAAK,CAAA,IACxC;CAEN,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,KAAS,EAAK,UACb,kBAAC,QAAD;EACE,IAAI;EACJ,MAAK;EACL,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,QAAQ;GAAG,UAAU;GAAU,MAAM;EAAgB;YAE7F,EAAK;CACF,CAAA,GAER,kBAAC,GAAD;EACE,KAAK;EACL,MAAM,IAAY,YAAY;EAC9B,QAAQ;EACR,MAAM;EACN,UAAU,KAAc;EACxB,iBAAe,IAAa,SAAS,KAAA;EACrC,oBAAkB;EAClB,OAAO,KAAc,EAAK,SAAS,EAAK,SAAS;EACjD,SAAS;EACF;YAEN;CACE,CAAA,CACL,EAAA,CAAA;AAEN;AAiBA,SAAS,GAAS,EAChB,UACA,kBACA,kBACA,UACA,YACA,oBACA,eACA,YACgB;CAChB,IAAM,IAAa,EAAiC,IAAI,GAClD,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,IAAO,EAAM,OAAO,EAAI,EAAM,IAAI,IAAI,WAGtC,IAAS,GACZ,MAAiC;EAEhC,AADA,EAAiE,UAAU,GAC3E,IAAa,CAAE;CACjB,GACA,CAAC,CAAU,CACb,GAIM,IAAc,GACjB,GAAM,OACL,EAAQ,EAAK,GACN,EAAM,GAAM,CAAM,IAE3B,CAAC,CAAK,CACR;CAEA,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,KAAK;EACL,MAAK;EACL,MACE,KAAW,OAEP,EAAM,OACJ,kBAAC,GAAD;GAAa,MAAM,EAAM;GAAM,MAAM;EAAK,CAAA,IAC1C,KAAA,IAHF,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;EAK1B,WAAW,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;EACjC,iBAAc;EACd,iBAAe,IAAO,SAAS;EAC/B,OAAO;EACP,eAAe,GAAS,MAAM,CAAC,CAAC;EACzB;YAEN;CACE,CAAA,GACL,kBAAC,IAAD;EACQ;EACN,QAAQ,EAAW;EACnB,eAAe,EAAQ,EAAK;EAC5B,OAAM;EACN,OAAO;YAEP,kBAAC,OAAD;GAAK,MAAK;GAAO,cAAY;aAC1B,EAAM,MAAM,KAAK,MAChB,EAAK,SAAS,WACZ,kBAAC,IAAD;IAEE,MAAM;IACS;IACA;IACf,OAAO;IACP,SAAS,MAAY,EAAK;IAC1B,kBAAkB,MAAM,EAAgB,IAAI,EAAK,OAAO,IAAI;GAC7D,GAPM,EAAK,IAOX,IAED,kBAAC,IAAD;IAEE,OAAO;IACQ;IACA;IACf,OAAO;IACE;IACQ;GAClB,GAPM,EAAK,MAAM,IAAI,SAAS,WAAW,EAAK,MAAM,GAAG,OAAO,EAAK,QAAQ,OAO3E,CAEL;EACG,CAAA;CACG,CAAA,CACV,EAAA,CAAA;AAEN;AAkBA,SAAS,GAAiB,EACxB,SACA,kBACA,kBACA,UACA,YACA,oBACA,QACA,kBACA,oBACwB;CACxB,IAAM,IAAS,GAAa,EAAK,MAAM,GAAe,CAAa,GAC7D,IAAO,GAAgB,EAAK,MAAM,GAAe,CAAa,GAC9D,IAAW,GAAgB,EAAK,MAAM,GAAe,CAAa,GAClE,IAAgB,GAAQ,eAAe,IACvC,IAAa,CAAC,EAAK,SACnB,IAAQ,IAAa,GAAU,EAAK,IAAI,IAAI,KAAA,GAE5C,IAAoB,IACtB,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IACpB,IACE,kBAAC,GAAD;EAAa,MAAM;EAAU,MAAM;CAAK,CAAA,IACxC;CAEN,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,KAAS,EAAK,UACb,kBAAC,QAAD;EACE,IAAI;EACJ,MAAK;EACL,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,QAAQ;GAAG,UAAU;GAAU,MAAM;EAAgB;YAE7F,EAAK;CACF,CAAA,GAER,kBAAC,OAAD;EACE,MAAK;EACL,iBAAe,IAAa,SAAS,KAAA;EACrC,oBAAkB;EAClB,OAAO,KAAc,EAAK,SAAS,EAAK,SAAS;EACjD,SAAS,YAAY;GACf,OAAC,KAAU,KAAc,IAC7B;MAAgB,EAAI;IACpB,IAAI;KAEF,AADgB,GAAe,EAAK,MAAM,GAAQ,GAAK,GAAe,CACjE,KAIH,MAAM,QAAQ,QAAQ,EAAM,EAAK,MAAM,CAAM,CAAC;IAElD,UAAU;KACR,EAAgB,EAAK;IACvB;GAXoB;EAYtB;EACA,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,cAAc;GACd,UAAU;GACV,OAAO,IAAgB,kBAAkB,IAAa,kBAAkB;GACxE,QAAQ,IAAa,gBAAgB;GACrC,SAAS,IAAa,KAAM;EAC9B;EACA,eAAe,MAAM;GACnB,AAAK,MAAY,EAAE,cAAc,MAAM,aAAa;EACtD;EACA,eAAe,MAAM;GACnB,EAAE,cAAc,MAAM,aAAa;EACrC;YApCF,CAsCG,KACC,kBAAC,QAAD;GAAM,OAAO;IAAE,OAAO,IAAgB,kBAAkB;IAAiB,SAAS;GAAO;aACtF;EACG,CAAA,GAER,kBAAC,QAAD;GAAM,OAAO,EAAE,MAAM,EAAE;aAAI;EAAW,CAAA,CACnC;GACL,EAAA,CAAA;AAEN;AAmBA,SAAS,GAAqB,EAC5B,UACA,kBACA,kBACA,UACA,YACA,oBACA,QACA,kBACA,oBAC4B;CAC5B,IAAM,IAAc,EAAM,OAAO,EAAI,EAAM,IAAI,IAAI,KAAA;CACnD,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,KACC,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,UAAU;GACV,YAAY;GACZ,OAAO;GACP,eAAe;GACf,eAAe;EACjB;YAEC;CACE,CAAA,GAEN,EAAM,MAAM,KAAK,MAChB,EAAK,SAAS,WACZ,kBAAC,IAAD;EAEE,MAAM;EACS;EACA;EACR;EACP,SAAS,MAAY,EAAK;EAC1B,kBAAkB,MAAM,EAAgB,IAAI,EAAK,OAAO,IAAI;EACvD;EACU;EACA;CAChB,GAVM,EAAK,IAUX,IAED,kBAAC,IAAD;EAEE,OAAO;EACQ;EACA;EACR;EACE;EACQ;EACZ;EACU;EACA;CAChB,GAVM,EAAK,QAAQ,QAUnB,CAEL,CACA,EAAA,CAAA;AAEN;AAkBA,SAAS,GAAc,EACrB,WACA,kBACA,kBACA,UACA,wBACA,6BACA,QACA,kBACA,oBACqB;CACrB,IAAM,IAAS,EAAiC,IAAI,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAa,KAAkB,EAAwB,IAAI,GAI5D,IAAc,GACjB,GAAM,OACL,EAAQ,EAAK,GACN,EAAM,GAAM,CAAM,IAE3B,CAAC,CAAK,CACR,GAEM,IAAoB,KAA6D,QAAQ,MAAwB,IACjH,IAAyB,KAAuE,QAAQ,MAA6B;CAE3I,IAAI,GAAY,CAAM,MAAM,KAAK,CAAC,KAAqB,CAAC,GAAwB,OAAO;CAMvF,IAAM,EAAE,WAAQ,mBAAgB,GAAqB,GAAQ,GAAe,CAAa,GACnF,IAAiB,EAAY,SAAS,KAAK;CAEjD,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAe,YAAY;EAAS;YAA3D,CACE,kBAAC,GAAD;GACE,KAAK;GACL,MAAoD,EAA9C,KAAe,OAAgC,EAAK,OAA7B,GAAuB,EAAW,MAAM,GAAK,CAAA;GAC1E,OAAM;GACN,MAAM;GACN,eAAe,GAAS,MAAM,CAAC,CAAC;EACjC,CAAA,GACD,kBAAC,IAAD;GACQ;GACN,QAAQ,EAAO;GACf,eAAe,EAAQ,EAAK;GAC5B,OAAM;GACN,OAAO;aAEP,kBAAC,OAAD;IAAK,MAAK;IAAO,cAAW;cAA5B;KACG,EAAO,KAAK,MACX,EAAK,SAAS,WACZ,kBAAC,IAAD;MAEE,MAAM;MACS;MACA;MACf,OAAO;MACP,SAAS,MAAgB,EAAK;MAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;MACtD;MACU;MACA;KAChB,GAVM,EAAK,IAUX,IAED,kBAAC,IAAD;MAEE,OAAO;MACQ;MACA;MACf,OAAO;MACP,SAAS;MACT,iBAAiB;MACZ;MACU;MACA;KAChB,GAVM,EAAK,QAAQ,OAUnB,CAEL;KACC,KAAqB;KACrB,KAAkB,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;KACrC,EAAY,KAAK,MAChB,kBAAC,IAAD;MAEQ;MACS;MACA;MACf,OAAO;MACP,SAAS,MAAgB,EAAK;MAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;MACtD;MACU;MACA;KAChB,GAVM,EAAK,IAUX,CACF;KACA,KAA0B;IACxB;;EACG,CAAA,CACP;;AAET;AA6BA,IAAM,KAAiB,IACjB,KAAwB;AAE9B,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACA,GACgB;CAChB,IAAM,EAAE,YAAS,YAAS,mBAAgB,GAAoB,GAAO,GAAe,CAAa,GAI3F,IACJ,EAAQ,SAAS,IACb,EAAQ,QAAQ,GAAK,MAAM,KAAO,EAAe,IAAI,GAAQ,CAAC,CAAC,KAAK,IAAI,CAAC,IACzE,GACA,IACJ,EAAY,SAAS,IACjB,EAAY,QAAQ,GAAK,MAAM,KAAO,EAAe,IAAI,GAAQ,CAAC,CAAC,KAAK,KAAwB,CAAC,IACjG,GAGA,IAAY,IAAiB,IAAqB,IAAmB,GAEvE,IAAO,GACL,IAAqC,CAAC,GACtC,IAAuC,CAAC,GAC1C,IAAe;CAEnB,KAAK,IAAM,KAAQ,GAAS;EAE1B,IAAM,IAAI,EAAe,IAAI,GAAQ,CAAI,CAAC,KAAK;EAE/C,IAAI,GAAqB;GACvB,AAAI,EAAoB,CAAI,KAC1B,EAAO,KAAK,CAAI,GAChB,KAAQ,MAER,EAAS,KAAK,CAAI,GAClB,IAAe;GAEjB;EACF;EAKA,IAAM,IADiB,EAAQ,SAAS,EAAO,SAAS,EAAS,SAAS,IACpC,KAAK,EAAS,SAAS,IAAI,KAAiB;EAElF,AAAI,IAAO,IAAI,KAAgB,KAC7B,EAAO,KAAK,CAAI,GAChB,KAAQ,MAER,EAAS,KAAK,CAAI,GAClB,IAAe;CAEnB;CAEA,OAAO;EACL,eAAe;EACf;EACA,UAAU,IAAe,IAAW,CAAC;EACrC,mBAAmB;CACrB;AACF;AAGA,SAAS,GACP,GACQ;CAGR,OAFI,EAAK,SAAS,WAAiB,UAAU,EAAK,SAE3C,SADM,EAAK,OAAO,EAAI,EAAK,IAAI,IAAI;AAE5C;AAMA,SAAS,GAAU,EACjB,WACA,kBACA,kBACA,UACA,wBAAqB,GACrB,wBACA,eAAY,WACZ,QACA,kBACA,oBACkE;CAClE,IAAM,IAAe,EAA8B,IAAI,GACjD,IAAkB,EAA8B,IAAI,GACpD,CAAC,GAAgB,KAAqB,EAAS,CAAC,GAChD,CAAC,GAAgB,KAAqB,kBAA8B,IAAI,IAAI,CAAC,GAC7E,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,CAAC,GAAa,KAAkB,EAAwB,IAAI,GAM5D,CAAC,GAAO,KAAY,QAClB,OAAO,WAAa,OAAe,SAAS,gBAAgB,QAAQ,KAC5E;CAOA,QAAgB;EACd,IAAI,OAAO,mBAAqB,KAAa;EAC7C,IAAM,IAAW,IAAI,kBAAkB,MAAc;GACnD,KAAK,IAAM,KAAK,GAId,AAHI,EAAE,kBAAkB,SACtB,EAAS,SAAS,gBAAgB,QAAQ,KAAK,GAE7C,EAAE,kBAAkB,UAGtB,EAAY,EAAK;EAGvB,CAAC;EAED,OADA,EAAS,QAAQ,SAAS,iBAAiB;GAAE,YAAY;GAAM,iBAAiB,CAAC,OAAO,MAAM;EAAE,CAAC,SACpF,EAAS,WAAW;CACnC,GAAG,CAAC,CAAC;CAGL,IAAM,IAAa,EAAiC,IAAI,GAClD,CAAC,GAAU,KAAe,EAAS,EAAK,GAIxC,IAAkB,GACrB,GAAM,OACL,EAAY,EAAK,GACV,EAAM,GAAM,CAAM,IAE3B,CAAC,CAAK,CACR,GAEM,IAAY,GAAY,CAAM,GAI9B,IAAc,CAAC,CAAC,GAGhB,IAAY,QAAkB;EAElC,IADI,KACA,CAAC,EAAgB,SAAS;EAC9B,IAAM,IAAW,MAAM,KAAK,EAAgB,QAAQ,QAAQ,GACtD,oBAAY,IAAI,IAAoB;EAM1C,AALA,EAAS,SAAS,MAAO;GACvB,IAAM,IAAM,EAAG,QAAQ;GACvB,AAAI,KAAK,EAAU,IAAI,GAAK,EAAG,sBAAsB,EAAE,SAAS,EAAG,WAAW;EAChF,CAAC,GACD,EAAkB,CAAS,GAC3B,EAAY,EAAI;CAClB,GAAG,CAAC,CAAW,CAAC;CAoChB,AA3BA,SAAsB;EACpB,IAAI,GAAa;GAEf,EAAY,EAAI;GAChB;EACF;EAEA,EAAY,EAAK;EAEjB,IAAM,IAAK,4BAA4B;GACrC,EAAU;EACZ,CAAC;EACD,aAAa,qBAAqB,CAAE;CACtC,GAAG;EAlBuB,QAClB,KAAK,UAAU,EAAO,KAAK,MAAM,GAAQ,CAAC,CAAC,CAAC,GAClD,CAAC,CAAM,CAgBL;EAAmB;EAAW;CAAW,CAAC,GAG9C,QAAgB;EACd,IAAI,KAAe,GAAU;EAC7B,IAAM,IAAK,iBAAiB;GAE1B,AADA,EAAU,GACV,EAAY,EAAI;EAClB,GAAG,EAAE;EACL,aAAa,aAAa,CAAE;CAC9B,GAAG;EAAC;EAAU;EAAW;CAAW,CAAC,GAIrC,QAAgB;EACd,IAAI,GAAa;EACjB,IAAM,IAAK,EAAa;EACxB,IAAI,CAAC,GAAI;EACT,IAAI,OAAO,iBAAmB,KAAa;GAGzC,EAAkB,EAAG,sBAAsB,EAAE,KAAK;GAClD;EACF;EACA,IAAM,IAAW,IAAI,gBAAgB,MAAY;GAC/C,IAAM,IAAQ,EAAQ;GACtB,AAAI,KACF,EAAkB,EAAM,YAAY,KAAK;EAE7C,CAAC;EAID,OAHA,EAAS,QAAQ,CAAE,GAEnB,EAAkB,EAAG,sBAAsB,EAAE,KAAK,SACrC,EAAS,WAAW;CACnC,GAAG,CAAC,CAAW,CAAC;CAEhB,IAAM,IAAW,QAGX,IACK,GACL,gBAEA,GACA,GACA,GACA,GACA,CACF,IAEG,IAMD,MAAmB,IAcd,GACL,gBAEA,GACA,GACA,GACA,GACA,CACF,IAEK,GACL,GACA,GACA,GACA,GACA,GACA,GACA,CACF,IAlCS;EAAE,eAAe,CAAC;EAAG,QAAQ,CAAC;EAAG,UAAU,CAAC;EAAG,mBAAmB,CAAC;CAAE,GAmC7E;EAAC;EAAa;EAAU;EAAgB;EAAQ;EAAoB;EAAgB;EAAe;EAAe;CAAmB,CAAC;CAGzI,IAAI,MAAc,GAAG,OAAO;CAE5B,IAAM,IAAc,EAAS,SAAS,SAAS;CAE/C,OACE,kBAAC,OAAD;EACE,KAAK;EACL,MAAK;EACL,cAAY;EACZ,OAAO;GACL,SAAS;GACT,eAAe,IAAQ,gBAAgB;GACvC,YAAY;GACZ,KAAK;GAGL,YAAa,KAAY,IAAe,YAAY;GACpD,UAAU;EACZ;YAbF;GAgBE,kBAAC,OAAD;IACE,KAAK;IACL,eAAY;IACZ,OAAO;KACL,UAAU;KACV,YAAY;KACZ,eAAe;KACf,SAAS;KACT,KAAK;KACL,KAAK;KACL,MAAM;IACR;cAEC,EAAO,KAAK,MAAS;KACpB,IAAM,IAAM,GAAQ,CAAI;KACxB,IAAI,EAAK,SAAS,UAAU;MAC1B,IAAM,IAAS,GAAa,EAAK,MAAM,GAAe,CAAa,GAC7D,IAAO,GAAgB,EAAK,MAAM,GAAe,CAAa,GAC9D,IAAW,GAAgB,EAAK,MAAM,GAAe,CAAa;MAExE,OACE,kBAAC,GAAD;OAEE,iBAAe;OACf,MALW,IAAW,kBAAC,GAAD;QAAa,MAAM;QAAU,MAAM;OAAK,CAAA,IAAI;OAMlE,QAAQ,GAAQ;OAChB,UAAU;iBAET;MACE,GAPE,CAOF;KAET,OAAO;MACL,IAAM,IAAY,EAAK,OAAO,EAAI,EAAK,IAAI,IAAI;MAC/C,OACE,kBAAC,GAAD;OAEE,iBAAe;OACf,WAAW,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OACjC,UAAU;iBAET;MACE,GANE,CAMF;KAET;IACF,CAAC;GACE,CAAA;GAGJ,EAAS,cAAc,KAAK,MAC3B,kBAAC,IAAD;IAEQ;IACS;IACA;IACR;IACP,SAAS,MAAgB,EAAK;IAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;IACtD;IACU;IACA;GAChB,GAVM,GAAQ,CAAI,CAUlB,CACF;GAGA,EAAS,OAAO,KAAK,MAAS;IAC7B,IAAM,IAAM,GAAQ,CAAI;IAiBtB,OAhBE,EAAK,SAAS,WAEd,kBAAC,IAAD;KAEE,MAAM;KACS;KACA;KACR;KACP,SAAS,MAAgB,EAAK;KAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;KACtD;KACU;KACA;IAChB,GAVM,CAUN,IAID,kBAAC,IAAD;KAEE,OAAO;KACQ;KACA;KACR;KACP,SAAS;KACT,iBAAiB;IAClB,GAPM,CAON;GAGP,CAAC;GAGA,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IACE,KAAK;IACL,MAAK;IACL,WAAW,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IACjC,iBAAc;IACd,iBAAe,IAAW,SAAS;IACnC,eAAe,GAAa,MAAM,CAAC,CAAC;cACrC;GAEI,CAAA,GACL,kBAAC,IAAD;IACE,MAAM;IACN,QAAQ,EAAW;IACnB,eAAe,EAAY,EAAK;IAChC,OAAO,IAAQ,UAAU;IACzB,OAAO;cAEP,kBAAC,OAAD;KAAK,MAAK;KAAO,cAAW;eACzB,EAAS,SAAS,KAAK,MACtB,EAAK,SAAS,WACZ,kBAAC,IAAD;MAEE,MAAM;MACS;MACA;MACf,OAAO;MACP,SAAS,MAAgB,EAAK;MAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;MACtD;MACU;MACA;KAChB,GAVM,EAAK,IAUX,IAED,kBAAC,IAAD;MAEE,OAAO;MACQ;MACA;MACf,OAAO;MACP,SAAS;MACT,iBAAiB;KAClB,GAPM,GAAQ,CAAI,CAOlB,CAEL;IACG,CAAA;GACG,CAAA,CACV,EAAA,CAAA;GAIH,EAAS,kBAAkB,KAAK,MAC/B,kBAAC,IAAD;IAEQ;IACS;IACA;IACR;IACP,SAAS,MAAgB,EAAK;IAC9B,kBAAkB,MAAM,EAAe,IAAI,EAAK,OAAO,IAAI;IACtD;IACU;IACA;GAChB,GAVM,GAAQ,CAAI,CAUlB,CACF;EACE;;AAET;AAUA,SAAS,GAAW,EAAE,WAAQ,kBAAe,CAAC,GAAG,GAAG,KAAyB;CAS3E,IAAM,IAAS,EAAa,SAAS;CAErC,OACE,kBAAC,OAAD;EAAK,OAAO,EAAE,UAAU,WAAW;YAAnC,CAEE,kBAAC,OAAD;GACE,eAAa,IAAS,SAAS,KAAA;GAC/B,OAAO,EAAE,SAAS,IAAS,SAAS,KAAA,EAAU;aAE9C,kBAAC,IAAD;IAAmB;IAAQ,cAAc,CAAC;IAAG,GAAI;GAAO,CAAA;EACrD,CAAA,GAEL,kBAAC,OAAD;GACE,eAAc,IAAkB,KAAA,IAAT;GACvB,OAAO,EAAE,SAAU,IAAkB,KAAA,IAAT,OAAmB;aAE/C,kBAAC,IAAD;IAAmB;IAAsB;IAAc,GAAI;GAAO,CAAA;EAC/D,CAAA,CACF;;AAET;AAaA,SAAgB,GAAoB,EAClC,WACA,QACA,iBACA,UACA,wBAAqB,GACrB,UAAO,WACP,wBACA,kBACA,kBACA,eAAY,WACZ,wBACA,6BACA,kBACA,oBAC2B;CAC3B,IAAM,IAAY,GAAY,CAAM;CA6CpC,OAzCI,MAAS,QAIT,kBAAC,IAAD;EACU;EACO;EACA;EACR;EACc;EACK;EACrB;EACU;EACA;CAChB,CAAA,IAMD,MAAc,IAAU,OAIxB,EAFA,MAAS,YAER,KAkBF,IAlBC;EACU;EACH;EACS;EACP;EACa;EACC;EACN;EACA;EACJ;EACI;EACA;CAChB,CAAA;AAoBP;;;AC5hDA,SAAgB,GACd,GACA,GACA,GACA,GACe;CAQf,OAPI,GAAW,CAAM,KACnB,GAAoB,EAAO,UAAW,CAAS,GACxC,QAEL,EAAO,SACF,GAAG,EAAO,OAAO,GAAG,MAEtB,IAAiB,GAAG,EAAe,GAAG,MAAS;AACxD;;;ACsBA,SAAgB,GACd,GACyC;CACzC,IAAI,CAAC,GAAS,OAAO,CAAC;CACtB,IAAM,IAA+C,CAAC;CACtD,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAO,GAE7C,CADQ,EAAO,cAAc,CAAC,GACzB,SAAS,UAAU,MACxB,EAAO,eACX,EAAI,KAAK;EAAE;EAAM;CAAO,CAAC;CAE3B,OAAO;AACT;AA+BA,SAAgB,GACd,GACA,GACA,GACyC;CACzC,IAAI,CAAC,GAAS,OAAO,CAAC;CACtB,IAAM,IAA+C,CAAC;CACtD,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAO,GAAG;EAMpD,IAJI,EADQ,EAAO,cAAc,CAAC,GACzB,SAAS,cAAc,KAC5B,EAAO,eAGP,EAAK,WAAW,IAAI,GAAG;EAE3B,IAAI,IAAiB;GACnB,GAAG;GACH,0BAA0B;IACxB,GAAI,EAAO,4BAA4B,CAAC;KACvC,IAAU;GACb;EACF;EAMA,IAAI,EAAO,iBAAiB,IAAI,CAAO,GAAG;GACxC,IAAM,IAAW,EAAO,gBACrB,MAAM,EACN,QAAQ,MAAoB,EAAE,SAAS,CAAO;GACjD,IAAS;IAAE,GAAG;IAAQ,iBAAiB,IAAI,GAAyB,CAAQ;GAAE;EAChF;EAEA,EAAI,KAAK;GAAE;GAAM,QAAQ;EAAO,CAAC;CACnC;CACA,OAAO;AACT;AAkCA,SAAgB,GACd,GACA,GACA,GACe;CACf,IAAM,IAAc,EAAS;CAC7B,IAAI,CAAC,GAAa,OAAO;CACzB,IAAM,IAAe,EAAS,IAExB,IAAW,GAAc,UAAU,MAAM,MAAM,EAAE,WAAW,CAAS;CAC3E,IAAI,GAAU;EACZ,IAAM,IAAK,EAAS;EACpB,IAAI,OAAO,KAAO,UAAU,OAAO;EACnC,IAAI,MAAM,QAAQ,CAAE,KAAK,EAAG,WAAW,GAAG,OAAO,EAAG;CACtD;CAEA,IAAM,IAAe,GAAiD;CACtE,KAAK,IAAM,CAAC,GAAM,MAAU,EAAY,QAAQ;EAC9C,IAAM,IAAgB,EAAM,KAAK;EAC5B,OACL;OAAI,MAAkB,GAAkB,OAAO;GAC/C,IAAI,GAAa;IAEf,IAAM,IADY,EAAS,IACqC;IAChE,IAAI,KAAY,MAAa,GAAa,OAAO;GACnD;EAL+C;CAMjD;CACA,OAAO;AACT;AAUA,SAAgB,GACd,GACA,GACA,GACa;CACb,IAAM,IAAW,IAAI,IAAI,GAAoB,CAAM,CAAC;CACpD,OAAO,IAAI,IACT,EAAQ,QAAQ,MAAQ;EAEtB,IADI,MAAQ,KACR,EAAS,IAAI,CAAG,GAAG,OAAO;EAC9B,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;EAGnC,OADA,EADI,CAAC,KACD,EAAM,QAAQ,EAAM;CAE1B,CAAC,CACH;AACF;AAQA,SAAgB,GACd,GACA,GACA,GACa;CACb,OAAO,IAAI,IACT,EAAQ,QAAQ,MAAQ;EACtB,IAAI,MAAQ,GAAY,OAAO;EAC/B,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;EAGnC,OADA,EADI,CAAC,KACD,EAAM;CAEZ,CAAC,CACH;AACF;AAeA,SAAgB,GACd,GACA,GACA,GACA,GACU;CACV,IAAM,IAAmB,CAAC;CAC1B,KAAK,IAAM,KAAO,GAAS;EACzB,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;EAInC,IAHI,CAAC,KAAS,CAAC,EAAM,YACjB,EAAM,QAAQ,EAAM,WACpB,MAAQ,KACR,EAAO,cAAc,MAAQ,EAAO,YAAY;EACpD,IAAM,IAAM,EAAO;EACnB,CAAI,KAAO,QAAQ,MAAQ,OACzB,EAAO,KAAK,CAAG;CAEnB;CACA,OAAO;AACT;AAEA,SAAgB,GAAgB,EAAE,SAAM,UAA6B;CACnE,IAAM,IAAY,EAAK;CAEvB,IADI,CAAC,KACD,EAAI,OAAO,OAAO;CACtB,IAAM,IAAc,EAAI,OAAO,SAAS;CACxC,IAAI,CAAC,GACH,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAvE,CAA0E,sBACrD,CAChB;;CAMT,IAAM,IAAU,GAA4B,EAAI,OAAO,UAAU,EAAI,YAAY,CAAS;CAQ1F,OAPK,IAQH,kBAAC,IAAD;EACQ;EACD;EACM;EACE;EACJ;CACV,CAAA,IAZC,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAvE;GAA0E;GACvD;GAAU;GAAI,EAAI;EAChC;;AAYX;AAQA,SAAS,GAAoB,EAAE,SAAM,QAAK,cAAW,gBAAa,cAAqC;CAErG,IAAM,IAAU,EAAK,WAAW,EAAK,QAAQ,SAAS,IAClD,EAAK,QAAQ,QAAQ,MACf,MAAQ,IAAgB,KAErB,CAAC,CADM,EAAY,OAAO,IAAI,CAC5B,CACV,IACD,EAAY,OAAO,MAAM,EAAE,QACxB,MAAQ;EACP,IAAI,MAAQ,GAAS,OAAO;EAC5B,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;EAIxC,OADA,EAFI,CAAC,KACD,EAAM,WACN,EAAM,SAAS,gBAAgB;CAErC,CACF,GAKE,IAAY,GAAqB,GAAa,QAAQ,GACtD,IACJ,EAAK,gBAAgB,IAAY,aAAa,aAK1C,KACH,MAAgB,iBAAiB,MAAgB,eAClD,KACA,EAAK,aAAa,IAEd,IAAQ,EAAI,EAAY,SAAS,WAAW,KAAK,EAAI,EAAY,SAAS,IAAI,KAAK,GAGnF,IAAa,EAAK,eAAe,MAAS,EAAK,WAAW,EAAK,QAAQ,SAAS,GAMhF,IAA2B,QACzB,GAAgC,EAAY,SAAS,GAAS,EAAI,QAAQ,GAChF;EAAC,EAAY;EAAS;EAAS,EAAI;CAAQ,CAC7C,GAIM,IAAkB,QAAsC;EAC5D,IAAM,IAA8B,CAAC;EACrC,KAAK,IAAM,EAAE,SAAM,eAAY,GAC7B,EAAI,KAAQ;EAEd,OAAO;CACT,GAAG,CAAC,CAAwB,CAAC,GAMvB,IAAc,QAKX,GAJe,EAAY,iBAAiB,QAKjC,GAAmB,GAAiB,cAAc,GAGlE,GACA,KAAA,GACA,CAAC,GACD,CAAC,CACH,GACC,CAAC,EAAY,eAAe,CAAe,CAAC,GAEzC,EAAE,WAAQ,GAAgB,GAC1B,IAAc,GAAe,GAC7B,IAAa,GAAc,GAY3B,IAAgB,SAA8B;EAClD,QAAQ;EACR,YAAY;EACZ,aAAa,OAAO,SAAW,MAAc,OAAO,SAAS,WAAW;EACxE,cAAc,CAAC;EACf,aAAa,CAAC;EACd,QAAQ;EACR,cAAc,GAAK,MAAS,EAAY,GAAK;GAAE,OAAO,GAAM;GAAO,MAAM,GAAM;GAAM,QAAQ,GAAM;EAAO,CAAC;EAC3G,eAAe,GAAM,MAAW,EAAa,GAAM,CAA4C;CACjG,IAAI;EAAC;EAAa;EAAW;EAAY;CAAW,CAAC,GAC/C,IAAc,GAAe,GAC7B,IAAuB,QAAkB;EAC7C,EAAY,kBAAkB,EAC5B,YAAY,MAAU;GACpB,IAAM,IAAM,EAAM;GAClB,OAAO,EAAI,OAAA,qBAA8B,EAAI,OAAO;EACtD,EACF,CAAC;CACH,GAAG,CAAC,GAAa,CAAS,CAAC,GAErB,IAAiB,GACpB,GAAc,MAAmB;EAChC,IAAM,IAAW,GAAe,GAAM,GAAQ,CAAC,GAAG,CAAS;EACvD,UAAa,MAGjB,OAAO,EAAI;GACT;GACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;GACpC,SAAS,OAAO,MAAW;IACzB,IAAM,IAAS,MAAM,EAAa,GAAU,EAAgB,GAAQ,KAAA,CAAS,CAAC;IAE9E,OADA,EAAqB,GACd;GACT;EACF,CAAC;CACH,GACA;EAAC;EAAK;EAAW;CAAoB,CACvC,GAMM,CAAC,GAAkB,KAAuB,EAAS,CAAC,GAIpD,IACJ,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;EAAE;YAA5D,CAIG,EAAY,gBACX,EAAY,SAAS,KACnB,kBAAC,IAAD;GACE,MAAK;GACL,QAAQ;GACR,eAAe;GACf,OAAO;GACQ;GACf,WAAW,GAAG,EAAM;EACrB,CAAA,IAGH,EAAyB,KAAK,EAAE,SAAM,gBACpC,kBAAC,IAAD;GAEE,YAAY;GACJ;GACR,YAAY;GACZ,SAAQ;GACR,MAAK;GACL,WAAW;EACZ,GAPM,CAON,CACF,GAEF,KACC,kBAAC,GAAD;GACE,MAAK;GACL,MAAK;GACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GAC5B,eAAe,GAAqB,MAAM,IAAI,CAAC;aAChD;EAEI,CAAA,CAEJ;KAGD,IAAQ,IACZ,kBAAC,IAAD;EACE,YAAY;EACZ,QAAQ;EACR,cAAc,EAAI;EAClB,UAAU,EAAI;EACd,YAAY;EACH;EACC;EACG;EACR;EACL,YAAY,EAAK;EACjB,cAAc,EAAK;EACnB,MAAM,EAAK;EACO;CACnB,CAAA,IAED,kBAAC,IAAD;EACE,YAAY;EACZ,QAAQ;EACR,cAAc,EAAI;EAClB,UAAU,EAAI;EACd,YAAY;EACH;EACC;EACG;EACR;EACL,MAAM,EAAK;EACO;CACnB,CAAA;CAgBH,OAZI,EAAI,aAEJ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,gBAAgB;GAAY,cAAc;EAAE;YACxE;CACE,CAAA,GACJ,CACE,EAAA,CAAA,IAMP,kBAAC,IAAD;EACE,OAAO;EACP,MACE,EAAY,SAAS,OACnB,kBAAC,GAAD;GAAa,MAAM,EAAY,QAAQ;GAAM,MAAM;EAAK,CAAA,IACtD,KAAA;EAEN,OAAO;YAEN;CACU,CAAA;AAEjB;AAgBA,SAAS,GAAsB,EAC7B,eACA,WACA,iBACA,aACA,eACA,YACA,aACA,gBACA,QACA,SACA,uBACmD;CACnD,IAAM,IAAc,GAAe,GAC7B,EAAE,MAAM,GAAU,cAAW,eAAY,GAC7C,GACA,GACA,GACA,GACA,CACF,GACM,IAAO,GAAU,QAAQ,CAAC,GAC1B,IAAO,GAAU;CAQvB,OACE,kBAAC,IAAD;EACc;EACJ;EACE;EACE;EACH;EACC;EACG;EACR;EACC;EACA;EACK;EACF;EACQ,iBApBG,QAAkB;GACxC,EAAY,kBAAkB,EAC5B,UAAU;IAAC;IAAqB;IAAc;IAAU;GAAU,EACpE,CAAC;EACH,GAAG;GAAC;GAAa;GAAc;GAAU;EAAU,CAgB9B;EACC;CACnB,CAAA;AAEL;AAQA,SAAS,GAAyB,EAChC,eACA,WACA,iBACA,aACA,eACA,YACA,aACA,gBACA,QACA,eACA,iBACA,SACA,uBACsD;CACtD,IAAM,IAAc,GAAe,GAG7B,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,CAAC,GAAiB,KAAsB,EAAS,EAAE,GACnD,CAAC,GAAS,KAAc,EAAiC,CAAC,CAAC,GAC3D,IAAiB,EAAsC,KAAA,CAAS;CAGtE,SACM,EAAe,WAAS,aAAa,EAAe,OAAO,GAC/D,EAAe,UAAU,iBAAiB;EACxC,EAAmB,CAAW;CAChC,GAAG,GAAG,SACO;EACX,AAAI,EAAe,WAAS,aAAa,EAAe,OAAO;CACjE,IACC,CAAC,CAAW,CAAC;CAEhB,IAAM,IAAqB,GAAa,GAAe,MAAkB;EACvE,GAAY,MAAS;GACnB,IAAI,MAAU,WAAW;IACvB,IAAM,GAAG,IAAQ,GAAG,GAAG,MAAS;IAChC,OAAO;GACT;GACA,OAAO;IAAE,GAAG;KAAO,IAAQ;GAAM;EACnC,CAAC;CACH,GAAG,CAAC,CAAC,GAGC,IAAgB,QACb,OAAO,KAAK,CAAO,EAAE,SAAS,IAAI,IAAU,KAAA,GAClD,CAAC,CAAO,CAAC,GAEN,EACJ,MAAM,GACN,cACA,YACA,gBACA,kBACA,0BACE,GACF,GACA,GACA,GACA,GACA,GACA,KAAA,GACA,KAAmB,KAAA,GACnB,CACF,GAGM,EAAE,SAAM,YAAS,QAAc;EACnC,IAAI,CAAC,GAAc,OAAO,OAAO;GAAE,MAAM,CAAC;GAAgC,MAAM,KAAA;EAA8B;EAC9G,IAAM,IAAqC,CAAC,GACxC;EACJ,KAAK,IAAM,KAAQ,EAAa,OAE9B,IADA,EAAQ,KAAK,GAAG,EAAK,IAAI,GACrB,EAAK,OACP,IAAI,CAAC,GACH,IAAa,EAAE,GAAG,EAAK,MAAM;OAE7B,KAAK,IAAM,CAAC,GAAW,MAAY,OAAO,QAAQ,EAAK,KAAK,GAC1D,EAAW,KAAa;GAAE,GAAG,EAAW;GAAY,GAAG;EAAQ;EAKvE,OAAO;GAAE,MAAM;GAAS,MAAM;EAAW;CAC3C,GAAG,CAAC,CAAY,CAAC,GAEX,IAAkB,QAAkB;EACxC,EAAY,kBAAkB,EAC5B,YAAY,MAAU;GACpB,IAAM,IAAM,EAAM;GAClB,OAAO,EAAI,OAAA,qBAA8B,EAAI,OAAO;EACtD,EACF,CAAC;CACH,GAAG,CAAC,GAAa,CAAU,CAAC;CAE5B,OACE,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,IAAD;GACU;GACI;GACE;GACD;GACb,gBAAgB;GACP;GACT,gBAAgB;GAChB,aAAa,EAAI,OAAO;EACzB,CAAA;EACD,kBAAC,IAAD;GACc;GACJ;GACE;GACE;GACH;GACC;GACG;GACR;GACC;GACA;GACK;GACF;GACQ;GACC;EACnB,CAAA;EACA,KACC,kBAAC,OAAD;GAAK,OAAO,EAAE,WAAW,EAAE;aACzB,kBAAC,GAAD;IACE,MAAK;IACL,MAAK;IACL,MAAM,IAAqB,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IAAI,KAAA;IACnD,eAAe,EAAc;IAC7B,UAAU;cACX;GAEI,CAAA;EACF,CAAA;CAEJ,EAAA,CAAA;AAET;AAsBA,SAAS,GAAqB,EAC5B,eACA,WACA,aACA,eACA,YACA,aACA,gBACA,QACA,SACA,SACA,cACA,YACA,oBACA,uBACkB;CAKlB,IAAM,IAAc,EAAQ,MAAM,MAAM,MAAM,CAAU,GAClD,IAAc,QAEhB,MAAgB,aACZ,GAAmB,KAAA,GAAW,GAAQ,EAAI,OAAO,OAAO,IACxD,MACN;EAAC;EAAa;EAAQ,EAAI,OAAO;CAAO,CAC1C,GAIM,IAAkB,QACf,IAAW,GAA0B,GAAQ,GAAS,CAAU,oBAAI,IAAI,IAAY,GAC3F;EAAC;EAAS;EAAY;EAAQ;CAAQ,CACxC,GACM,IAAuB,QACrB,GAA+B,GAAQ,GAAS,CAAU,GAChE;EAAC;EAAS;EAAY;CAAM,CAC9B,GAQM,IAAa,QAAc;EAC/B,IAAM,oBAAI,IAAI,IAAsB;EACpC,KAAK,IAAM,KAAO,GAAM;GACtB,IAAM,IAAK,GAAgB,GAAQ,CAAG;GACtC,EAAE,IAAI,EAAe,CAAE,GAAG,CAAE;EAC9B;EACA,OAAO;CACT,GAAG,CAAC,GAAM,CAAM,CAAC,GAMX,IAAa,QACX,GAA4B,EAAO,OAAO,GAChD,CAAC,EAAO,OAAO,CACjB,GAGM,CAAC,GAAY,KAAiB,EAAgD,IAAI,GAGlF,CAAC,GAAc,KAAmB,EAAwB,IAAI,GAC9D,CAAC,GAAY,KAAiB,EAAmB,CAAC,CAAC,GAEnD,CAAC,GAAW,KAAgB,EAA6C,CAAC,CAAC,GAC3E,IAAe,EAAO,CAAS;CACrC,EAAa,UAAU;CACvB,IAAM,IAAiB,EAAsD,CAAC,CAAC;CAG/E,QAAgB;EACd,IAAM,IAAS,EAAe;EAC9B,aAAa;GACX,KAAK,IAAM,KAAK,OAAO,OAAO,CAAM,GAAG,aAAa,CAAC;EACvD;CACF,GAAG,CAAC,CAAC;CAGL,IAAM,CAAC,GAAU,MAAe,EAAyC,IAAI,GACvE,CAAC,GAAa,KAAkB,EAAmB,CAAC,CAAC,GACrD,CAAC,GAAa,MAAkB,EAAS,EAAK,GAG9C,CAAC,IAAe,MAAoB,EAAiE,IAAI,GACzG,CAAC,GAAa,MAAkB,kBAAsB,IAAI,IAAI,CAAC,GAM/D,IAAW,EACf,OAAO,GAAe,GAAiC,MAAsC;EAC3F,GAAc,OAAU;GACtB,GAAG;IACF,IAAQ;IAAE,GAAG,EAAK;IAAQ,QAAQ;IAAM,OAAO,KAAA;GAAU;EAC5D,EAAE;EACF,IAAI;GAiBF,OAbA,MAAM,EAAa,GAAY,EAAW,IAAI,CAAK,KAAK,GAAO,CAAM,GACrE,GAAc,MAAS;IACrB,IAAM,IAAU,EAAK;IACrB,IAAI,CAAC,GAAS,OAAO;IAErB,IAAI,EAAQ,MAAM,GAChB,OAAO;KAAE,GAAG;MAAO,IAAQ;MAAE,GAAG;MAAS,QAAQ;KAAM;IAAE;IAG3D,IAAM,GAAG,IAAQ,GAAG,GAAG,MAAS;IAChC,OAAO;GACT,CAAC,GACD,EAAgB,GACT;EACT,SAAS,GAAK;GACZ,IAAM,IAAM,aAAe,QAAQ,EAAI,UAAU;GAKjD,OAJA,GAAc,OAAU;IACtB,GAAG;KACF,IAAQ;KAAE,GAAG,EAAK;KAAQ,QAAQ;KAAO,OAAO;IAAI;GACvD,EAAE,GACK;EACT;CACF,GACA;EAAC;EAAY;EAAiB;CAAU,CAC1C,GAKM,KAAmB,GACtB,GAAe,GAAmB,MAAmB;EACpD,GAAc,MAAS;GACrB,IAAM,IAAU,EAAK,MAAU;IAAE,QAAQ,CAAC;IAAG,KAAK;IAAG,QAAQ;GAAM,GAC7D,IAAU,EAAQ,MAAM;GAC9B,OAAO;IACL,GAAG;KACF,IAAQ;KACP,GAAG;KACH,QAAQ;MAAE,GAAG,EAAQ;OAAS,IAAY;KAAM;KAChD,KAAK;IACP;GACF;EACF,CAAC,GACG,MAAgB,kBAEhB,EAAe,QAAQ,MACzB,aAAa,EAAe,QAAQ,EAAM,GAE5C,EAAe,QAAQ,KAAS,iBAAiB;GAC/C,IAAM,IAAU,EAAa,QAAQ;GACrC,AAAI,KAAW,OAAO,KAAK,EAAQ,MAAM,EAAE,SAAS,KAClD,EAAS,GAAO,EAAE,GAAG,EAAQ,OAAO,GAAG,EAAQ,GAAG;EAEtD,GAAG,GAAG;CACR,GACA,CAAC,GAAU,CAAW,CACxB,GAGM,KAAmB,QAAkB;EACzC,IAAM,IAAiC,CAAC;EAExC,EAAM,KAAc;EAEpB,KAAK,IAAM,KAAO,GAAS;GACzB,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;GAC9B,MACD,EAAM,YAAY,KAAA,IAEX,EAAM,SAAS,WACxB,EAAM,KAAO,MAFb,EAAM,KAAO,EAAM;EAIvB;EAEA,AADA,GAAY,CAAK,GACjB,EAAe,CAAC,CAAC;CACnB,GAAG;EAAC;EAAY;EAAU;EAAS,EAAO;CAAM,CAAC,GAM3C,KAAgB,EAAO,CAAgB;CAC7C,QAAgB;EACV,MAAqB,KAAA,KACrB,MAAqB,GAAc,YACvC,GAAc,UAAU,GACpB,MACJ,GAAiB;CACnB,GAAG;EAAC;EAAkB;EAAU;CAAgB,CAAC;CAGjD,IAAM,KAAqB,EAAY,YAAY;EACjD,IAAI,CAAC,GAAU;EAEf,IAAM,IAAS,GAAoC,GAAU,GAAS,GAAQ,CAAU;EACxF,IAAI,EAAO,SAAS,GAAG;GACrB,EAAe,CAAM;GACrB;EACF;EACA,GAAe,EAAI;EACnB,IAAI;GAIF,AAHA,MAAM,GAAa,GAAY,CAAQ,GACvC,GAAY,IAAI,GAChB,EAAe,CAAC,CAAC,GACjB,EAAgB;EAClB,SAAS,GAAK;GAEZ,EAAe,CAAC,UADJ,aAAe,QAAQ,EAAI,UAAU,iBAClB,CAAC;EAClC,UAAU;GACR,GAAe,EAAK;EACtB;CACF,GAAG;EAAC;EAAU;EAAS;EAAQ;EAAY;EAAY;CAAe,CAAC,GASjE,IAAkB,GAAa,MAAkB;EAGrD,AAFA,EAAa,CAAC,CAAC,GACf,EAAc,CAAC,CAAC,GAChB,EAAgB,CAAK;CACvB,GAAG,CAAC,CAAC,GAKC,KAAoB,EACxB,OAAO,MAAkB;EACvB,IAAM,IAAQ,EAAa,QAAQ,IAC7B,IAAS,GAAO,UAAU,CAAC;EAEjC,IAAI,OAAO,KAAK,CAAM,EAAE,WAAW,GAAG;GAEpC,AADA,EAAgB,IAAI,GACpB,EAAc,CAAC,CAAC;GAChB;EACF;EAKA,IAAM,IAAS,GACb;GAFe,GAHL,EAAK,MACd,MAAM,EAAe,GAAgB,GAAQ,CAAC,CAAC,MAAM,CAEnC,KAAO,CAAC;GAAI,GAAG;EAElC,GACA,GACA,GACA,CACF;EACA,IAAI,EAAO,SAAS,GAAG;GACrB,EAAc,CAAM;GACpB;EACF;EAEA,AAAI,MADa,EAAS,GAAO,EAAE,GAAG,EAAO,GAAG,GAAO,OAAO,CAAC,MAE7D,EAAgB,IAAI,GACpB,EAAc,CAAC,CAAC;CAEpB,GACA;EAAC;EAAM;EAAQ;EAAS;EAAY;CAAQ,CAC9C,GAIM,IAAmB,GAAa,MAAkB;EAOtD,AANA,GAAc,MAAS;GACrB,IAAI,CAAC,EAAK,IAAQ,OAAO;GACzB,IAAM,GAAG,IAAQ,GAAG,GAAG,MAAS;GAChC,OAAO;EACT,CAAC,GACD,EAAc,CAAC,CAAC,GAChB,EAAgB,IAAI;CACtB,GAAG,CAAC,CAAC,GAGC,KAAkB,EACtB,OAAO,MAAkB;EAEvB,AADA,GAAiB,IAAI,GACrB,IAAgB,MAAS,IAAI,IAAI,CAAI,EAAE,IAAI,CAAK,CAAC;EACjD,IAAI;GAIF,AADA,MAAM,EAAa,GAAY,EAAW,IAAI,CAAK,KAAK,CAAK,GAC7D,EAAgB;EAClB,QAAQ,CAER,UAAU;GACR,IAAgB,MAAS;IACvB,IAAM,IAAO,IAAI,IAAI,CAAI;IAEzB,OADA,EAAK,OAAO,CAAK,GACV;GACT,CAAC;EACH;CACF,GACA;EAAC;EAAY;EAAiB;CAAU,CAC1C;CAEA,IAAI,GACF,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAAoB,CAAA;CAIlG,IAAI,GACF,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;GAAG,SAAS;EAAQ;YAAjF,CACE,kBAAC,OAAD,EAAK,OAAO;GAAE,QAAQ;GAAI,YAAY;GAAoB,cAAc;EAAE,EAAI,CAAA,GAC9E,kBAAC,OAAD,EAAK,OAAO;GAAE,QAAQ;GAAI,YAAY;GAAoB,cAAc;EAAE,EAAI,CAAA,CAC3E;;CAIT,IAAM,IAAY,EAAK,WAAW,KAAK,CAAC;CAExC,OACE,kBAAC,OAAD,EAAA,UAAA;EACG,KAAa,CAAC,KACb,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,SAAS;GAAS;aAAG;EAEtE,CAAA;GAEL,CAAC,KAAa,MACd,kBAAC,OAAD;GAAK,OAAO,EAAE,WAAW,OAAO;aAC9B,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UAAA;KACG,EAAQ,KAAK,MAAQ;MACpB,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG;MACnC,OACE,kBAAC,MAAD;OAAc,OAAO;iBAClB,IAAQ,GAAiB,GAAO,GAAK,EAAI,OAAO,QAAQ,IAAI;MAC3D,GAFK,CAEL;KAER,CAAC;KAIA,KAAY,kBAAC,MAAD,EAAI,OAAO;MAAE,QAAQ;MAAI,OAAO;KAAG,EAAI,CAAA;KACnD,CAAC,KAAY,EAAW,SAAS,KAChC,kBAAC,MAAD,EAAI,OAAO;MAAE,QAAQ;MAAI,OAAO;KAAG,EAAI,CAAA;IAEvC,EAAA,CAAA,EACC,CAAA,GACP,kBAAC,SAAD,EAAA,UAAA,CACG,EAAK,KAAK,MAAQ;KAGjB,IAAM,IAAQ,EAAe,GAAgB,GAAQ,CAAG,CAAC,GACnD,IAAa,EAAY,IAAI,CAAK,GAClC,IAAW,EAAU,IACrB,IAAO,KAAe,IACxB,EAAY,QAAQ,QAAQ,CAAK,IACjC,KAAA;KACJ,OACE,kBAAC,IAAD;MAES;MACF;MACG;MACR,UAAU,EAAI,OAAO;MACrB,SAAS,EAAI,OAAO;MACX;MACQ;MACP;MACG;MACb,WAAW,MAAiB;MAChB;MACA;MACA;MACG;MACL;MACV,cAAc;MACd,gBAAgB,MAAO;OAErB,GAAiB;QAAE,MAAM;QAAM,OAAO;QAAI,OAD5B,GAAgB,GAAgC,CAAM,KAAK;OACzB,CAAC;MACnD;MACA,aAAa;MACb,eAAe;MACf,cAAc;MACR;MACO;MACP;MACM;MACA;MACZ,oBAAoB;KACrB,GA9BM,CA8BN;IAEL,CAAC,GACA,KACC,kBAAC,IAAD;KACY;KACF;KACC;KACT,iBAAiB;KACL;KACC;KACA;KACb,WAAW,GAAO,MAChB,IAAa,MAAU,KAAO;MAAE,GAAG;OAAO,IAAQ;KAAM,CAAS;KAEnE,WAAW;KACX,gBAAgB;MAEd,AADA,GAAY,IAAI,GAChB,EAAe,CAAC,CAAC;KACnB;KACM;IACP,CAAA,CAEE,EAAA,CAAA,CACF;;EACJ,CAAA;EAKN,MACC,kBAAC,IAAD;GACE,MAAM,GAAc;GACpB,eAAe,MAAS,CAAC,KAAQ,GAAiB,IAAI;GACtD,OAAM;GACN,aACE,GAAc,QACV,oCAAoC,GAAc,MAAM,MACxD;GAEN,cAAa;GACb,iBAAiB,GAAgB,GAAc,KAAK;GACpD,aAAA;EACD,CAAA;CAEA,EAAA,CAAA;AAET;AA4CA,SAAS,GAAc,EACrB,UACA,QACA,WACA,aACA,YACA,YACA,oBACA,aACA,gBACA,cACA,eACA,eACA,eACA,kBACA,aACA,iBACA,kBACA,gBACA,kBACA,iBACA,SACA,gBACA,SACA,eACA,yBACqB;CACrB,IAAM,IAAa,GAAc,GAE3B,IAAa,QACb,CAAC,KAAY,OAAO,KAAK,EAAS,MAAM,EAAE,WAAW,IAAU,IAC5D;EAAE,GAAG;EAAK,GAAG,EAAS;CAAO,GACnC,CAAC,GAAK,CAAQ,CAAC;CAKlB,IAAI,MAAgB,cAAc,GAIhC,OACE,kBAAC,IAAD;EACE,QAAQ;EACA;EACC;EACQ;EACT,QATG,GAAU,QACrB,CAAC,GAAG,GAAY,UAAU,EAAS,OAAO,IAC1C;EAQA,QAAQ,GAAU,UAAU;EAC5B,WAAW,GAAG,MAAM,EAAa,GAAO,GAAG,CAAC;EAC5C,iBAAiB,EAAc,CAAK;EACpC,gBAAgB,EAAa,CAAK;EAC5B;CACP,CAAA;CAIL,IAAM,IAAY,IACZ,IAAwC;EAC5C,SAAS;EACT,cAAc;EACd,eAAe;CACjB,GAKM,IAAgB,MAAgB;CAEtC,OACE,kBAAC,MAAD;EAAI,OAAO,EAAE,SAAS,IAAa,KAAM,EAAE;YAA3C;GACG,EAAQ,KAAK,MAAQ;IACpB,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG,GAC7B,IAAa,EAAgB,IAAI,CAAG;IAI1C,IAFE,KAAiB,GAAY,UAAU,KAAS,GAAY,QAAQ,KAEtD,GACd,OACE,kBAAC,MAAD;KAAc,OAAO;eAClB,IACG,EAAY;MACV;MACA,WAAW;MACX,OAAO,EAAW;MAClB,MAAM;MACN,WAAW,MAAQ,EAAa,GAAO,GAAK,CAAG;MAC/C,QAAQ;MACR;MACA,SAAS;MACT,QAAQ;KACV,CAAC,IACD,OAAO,EAAW,MAAQ,EAAE;IAC9B,GAdK,CAcL;IAIR,IAAM,IAAiB,KAAQ,MAAQ,GACjC,IAAW,IACb,EAAY;KACV;KACA,WAAW;KACX,OAAO,EAAW;KAClB,MAAM;KACN,QAAQ;KACR;KAOA;KACA;KAKA,SAAS,IAAiB,KAAA,IAAY;KACtC,QAAQ;IACV,CAAC,IACD,OAAO,EAAW,MAAQ,EAAE,GAC1B,IAAe,KAAiB,KAAc,CAAC;IACrD,OACE,kBAAC,MAAD;KAEE,OAAO;MAAE,GAAG;MAAW,QAAQ,IAAe,YAAY;KAAU;KACpE,SAAS,UAAqB,EAAc;MAAE;MAAO;KAAI,CAAC,IAAI,KAAA;eAE7D,IAKC,kBAAC,KAAD;MACQ;MACN,OAAO;OAAE,OAAO;OAAiB,gBAAgB;OAAQ,YAAY;MAAI;MACzE,eAAe,MAAO,EAAE,cAAc,MAAM,iBAAiB;MAC7D,eAAe,MAAO,EAAE,cAAc,MAAM,iBAAiB;gBAE5D;KACA,CAAA,IAEH;IAEA,GApBG,CAoBH;GAER,CAAC;GACA,KACC,kBAAC,MAAD;IAAI,OAAO;KAAE,SAAS;KAAW,cAAc;KAA4B,eAAe;IAAS;cACjG,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;KAAE;eAA5D;MACG,GAAU,UAAU,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;MACxC,GAAU,SACT,kBAAC,QAAD;OAAM,OAAO;QAAE,UAAU;QAAI,OAAO;OAAgB;OAAG,OAAO,EAAS;iBAAO;MAExE,CAAA;MAKP,MAAgB,cACf,kBAAC,GAAD;OACE,MAAM;OACN,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAC5B,eAAe,EAAY,CAAK;OAChC,OAAM;MACP,CAAA;MAEH,kBAAC,GAAD;OACE,MAAM;OACN,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC7B,eAAe,EAAc,CAAK;OAClC,OAAM;MACP,CAAA;KACE;;GACH,CAAA;GAEL,CAAC,KAAY,KAAc,EAAW,SAAS,KAC9C,kBAAC,MAAD;IAAI,OAAO;KAAE,SAAS;KAAW,cAAc;KAA4B,eAAe;IAAS;cACjG,kBAAC,IAAD;KACE,SAAS;KACT,WAAW,CAAC,CAAK;KACjB,KAAK;KACL,WAAW;IACZ,CAAA;GACC,CAAA;EAEJ;;AAER;AA8BA,SAAS,GAAY,EACnB,WACA,WACA,YACA,oBACA,WACA,WACA,aACA,cACA,aACA,SACA,aACmB;CACnB,IAAM,IAAa,GAAc,GAC3B,IAAW,EAAO,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC,GACrD,IAAc,IAAI,IAAI,EAAO,QAAQ,MAAM,CAAC,EAAE,WAAW,SAAS,CAAC,CAAC,GAUpE,IAA0B;EAC9B,SAAS;EACT,cAAc;EACd,eAAe;EACf,QAAQ;EACR,UAAU;CACZ,GACM,IAAmC;EACvC,SAAS;EACT,OAAO;EACP,UAAU;EACV,UAAU;CACZ;CAWA,OACE,kBAAC,MAAD;EACE,OAAO,IAAS,EAAE,WAAW,8BAA8B,IAAI,KAAA;EACpD,YAbI,MAA0C;GAC3D,AAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,YAC1B,EAAE,eAAe,GACjB,EAAU,KACD,EAAE,QAAQ,aACnB,EAAE,eAAe,GACjB,EAAS;EAEb;YAGE,CAIG,EAAQ,KAAK,MAAQ;GACpB,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAG,GAC7B,IAAa,EAAgB,IAAI,CAAG,GACpC,IAAW,EAAY,IAAI,CAAG;GAUpC,OARK,IASH,kBAAC,MAAD;IAEE,OAAO;KACL,GAAG;KACH,SAAS,IAAW,4BAA4B;KAChD,eAAe;IACjB;IACA,OAAO,IAAW,GAAG,EAAI,gBAAgB,KAAA;cAEzC,kBAAC,OAAD;KAAK,OAAO;eACT,IACG,EAAY;MACV;MACA,WAAW;MACX,OAAO,EAAO;MACd,MAAM;MACN,WAAW,MAAQ,EAAS,GAAK,CAAG;MACpC,QAAQ;MACR;MACA,SAAS;MACT,QAAQ;KACV,CAAC,IACD,OAAO,EAAO,MAAQ,EAAE;IACzB,CAAA;GACH,GAvBG,CAuBH,IA/BF,kBAAC,MAAD;IAAc,OAAO;KAAE,GAAG;KAAU,OAAO;IAAgB;cACxD,GAAO,OAAO,MAAM,OAAO,EAAO,MAAQ,EAAE;GAC3C,GAFK,CAEL;EA+BV,CAAC,GACD,kBAAC,MAAD;GAAI,OAAO;aACT,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,gBAAgB;IAAW;cAAxF;KACG,KACC,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAI,OAAO;MAAgB;MAAG,OAAO,EAAS,MAAM,CAAC;gBAAG;KAE3E,CAAA;KAER,kBAAC,GAAD;MACE,MAAM;MACN,MAAe,EAAT,IAAU,IAAwB,EAAK,OAA9B,EAAS,MAAM,GAAK,CAA2B;MAC9D,SAAS;MACT,OAAM;KACP,CAAA;KACD,kBAAC,GAAD;MAAS,MAAM;MAAI,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;MAAU,OAAM;KAAgB,CAAA;IACrF;;EACH,CAAA,CACF;;AAER;AAmBA,SAAS,GAAS,EAChB,aACA,WACA,YACA,oBACA,eACA,gBACA,gBACA,aACA,cACA,aACA,WACgB;CAWhB,OACE,kBAAC,IAAD;EACE,QAAQ;EACA;EACC;EACT,iBAXiB,QAAc;GACjC,IAAM,IAAO,IAAI,IAAI,CAAe;GAEpC,OADA,EAAK,OAAO,CAAU,GACf;EACT,GAAG,CAAC,GAAiB,CAAU,CAOV;EACjB,QAAQ;EACR,QAAQ;EACE;EACC;EACD;EACJ;EACN,QAAA;CACD,CAAA;AAEL;;;ACrmDA,IAAM,KAAiB;CAAC;CAAa;CAAc;AAAY,GACzD,KAAmB,IAAI,OAAO;AAEpC,SAAgB,KAAU;CACxB,IAAM,IAAW,GAAiB,GAC5B,IAAO,GAAc,MAAM,EAAE,IAAI,GACjC,IAAS,GAAc,MAAM,EAAE,YAAY,GAC3C,IAAY,GAAc,MAAM,EAAE,SAAS,GAE3C,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,CAAC,GAAQ,KAAa,EAAkC;EAC5D,OAAO;EAAM,OAAO;EAAM,OAAO;EAAO,SAAS;CACnD,CAAC,GAEK,IAAe,EAAyB,IAAI,GAE5C,IAAc,GAAM,gBAAgB,GAAM,SAAS,QACnD,IAAQ,GAAM,SAAS,IACvB,IAAQ,GAAM,SAAS,EAAK,MAAM,MAAO,UACzC,IAAW,GAAQ,MAAM,GAAG,EAAO,KAAK,KAAK,EAAO,QAAS,GAAQ,QAAQ,KAC7E,IAAY,GAAM,aAAa,IAAI,KAAK,EAAK,UAAU,EAAE,mBAAmB,IAAI,KAIhF,EAAE,MAAM,GAAe,SAAS,MAAmB,GAAS;EAChE,UAAU,CAFQ,iBAAiB,GAAM,EAE/B;EACV,eAAe,EAAU,WAAW,EAAE,IAAI,EAAM,GAAG,CAAC;EACpD,SAAS,CAAC,CAAC,GAAM;EACjB,WAAW,MAAS;CACtB,CAAC,GAEK,IAAc,GAAe,MAA+C,QAE5E,IACJ,GAAY,aAAc,aAC1B,GAAY,OACZ;CAyCF,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,MAAM;GAAG,WAAW;GAAQ,SAAS;GAAkB,YAAY;EAAY;YAA7F;GACE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,QAAQ;IAAS;cAA/C;KACE,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;OAAI,SAAS;OAAa,YAAY;OAAkB,QAAQ;OAA2B,cAAc;OAAI,cAAc;MAAG;gBAAxL;OAEE,kBAAC,OAAD;QACE,eA7CsB;SAC1B,KACJ,EAAa,SAAS,MAAM;QAC9B;QA2CU,OAAM;QACN,OAAO;SAAE,UAAU;SAAY,QAAQ,IAAY,YAAY;SAAW,YAAY;QAAE;kBAH1F;SAKG,IACC,kBAAC,OAAD;UACE,KAAK;UACL,KAAI;UACJ,OAAO;WAAE,OAAO;WAAI,QAAQ;WAAI,cAAc;WAAO,WAAW;WAAS,SAAS;UAAQ;SAC3F,CAAA,IAED,kBAAC,GAAD;UAAQ,MAAM;UAAa,MAAM;SAAK,CAAA;SAExC,kBAAC,OAAD;UAAK,OAAO;WACV,UAAU;WAAY,OAAO;WAAG,cAAc;WAC9C,YAAY;WACZ,SAAS;WAAQ,YAAY;WAAU,gBAAgB;WACvD,SAAS;WACT,YAAY;UACd;UACE,WAAU;oBAET,IAAY,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IAAI,kBAAC,EAAK,QAAN;WAAa,MAAM;WAAI,OAAO,EAAE,OAAO,kCAAkC;UAAI,CAAA;SAC/G,CAAA;SACL,kBAAC,SAAD,EAAA,UAAQ,0DAAiE,CAAA;QACtE;;OACL,kBAAC,SAAD;QACE,KAAK;QACL,MAAK;QACL,QAAQ,GAAe,KAAK,GAAG;QAC/B,OAAO,EAAE,SAAS,OAAO;QACzB,UAAU,OAvEY,MAA2C;SACzE,IAAM,IAAO,EAAE,OAAO,QAAQ;SAC9B,IAAI,CAAC,KAAQ,CAAC,GAAM;SAEpB,EAAE,OAAO,QAAQ;SAEjB,IAAM,IAAkB,GAAmB,GAAM,IAAgB,EAAgB;SACjF,IAAI,GAAiB;UACnB,EAAM,MAAM,EAAgB,OAAO;UACnC;SACF;SAEA,EAAa,EAAI;SACjB,IAAI;UACF,IAAM,IAAW,MAAM,GAAW;WAChC;WACA,YAAY;WACZ,WAAW;UACb,CAAC;UAOD,AANA,MAAM,EAAa,WAAW,EAAE,IAAI,EAAK,GAAG,GAAG,EAC7C,QAAU,EACR,QAAQ,EAAE,KAAK,CAAC;WAAE,SAAS,EAAS;WAAI,UAAU,EAAS;UAAS,CAAC,EAAE,EACzE,EACF,CAAC,GACD,MAAM,EAAe,GACrB,EAAM,QAAQ,iBAAiB;SACjC,SAAS,GAAK;UACZ,IAAM,IAAM,aAAe,QAAQ,EAAI,UAAU;UACjD,EAAM,MAAM,CAAG;SACjB,UAAU;UACR,EAAa,EAAK;SACpB;QACF;OAwCS,CAAA;OAED,kBAAC,OAAD;QAAK,OAAO;SAAE,MAAM;SAAG,UAAU;QAAE;kBAAnC;SACE,kBAAC,MAAD;UAAI,OAAO;WAAE,QAAQ;WAAG,UAAU;WAAI,YAAY;WAAK,eAAe;UAAW;oBAAI;SAAgB,CAAA;SACrG,kBAAC,OAAD;UAAK,OAAO;WAAE,UAAU;WAAM,OAAO;WAAiB,WAAW;UAAE;UAAG,WAAU;oBAAQ;SAAW,CAAA;SACnG,kBAAC,OAAD;UAAK,OAAO;WAAE,WAAW;WAAG,SAAS;WAAQ,KAAK;WAAG,UAAU;UAAO;oBAAtE;WACE,kBAAC,GAAD;YAAM,MAAK;YAAS,MAAK;YAAK,KAAA;sBAAK;WAAW,CAAA;WAC9C,kBAAC,GAAD;YAAM,MAAK;YAAQ,MAAK;sBAAM;WAAe,CAAA;WAC5C,GAAM,aAAa,kBAAC,GAAD;YAAM,MAAK;YAAU,MAAK;YAAK,KAAA;sBAAI;WAAY,CAAA;UAChE;;QACF;;OACL,kBAAC,GAAD;QAAK,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;QAAG,eAAe,EAAY,EAAI;kBAAG;OAAiB,CAAA;OACvF,kBAAC,GAAD;QAAK,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;QAAG,eAAe,EAAS,cAAc;kBAAG;OAAgB,CAAA;MAC9F;;KAEL,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,qBAAqB;OAAW,KAAK;MAAG;gBAAvE;OACE,kBAAC,IAAD;QAAa,OAAM;QAAgB,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;kBAC9D,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,qBAAqB;UAAW,WAAW;UAAI,QAAQ;SAAE;mBAAxF;UACE,kBAAC,IAAD;WAAU,OAAM;qBAAgB;UAAsB,CAAA;UACtD,kBAAC,IAAD;WAAU,OAAM;qBAAQ,kBAAC,QAAD;YAAM,WAAU;sBAAQ;WAAY,CAAA;UAAW,CAAA;UACvE,kBAAC,IAAD;WAAU,OAAM;qBAAQ,kBAAC,QAAD;YAAM,OAAO,EAAE,OAAO,gBAAgB;sBAAG;WAAO,CAAA;UAAW,CAAA;UACnF,kBAAC,IAAD;WAAU,OAAM;qBAAY,GAAU;UAAY,CAAA;UAClD,kBAAC,IAAD;WAAU,OAAM;qBAAW;UAAsB,CAAA;UACjD,kBAAC,IAAD;WAAU,OAAM;qBAAS,kBAAC,QAAD;YAAM,WAAU;sBAAQ;WAAgB,CAAA;UAAW,CAAA;SACzE;;OACM,CAAA;OAEb,kBAAC,IAAD;QAAa,OAAM;QAAW,MAAM,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;kBAC1D,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,eAAe;UAAU,KAAK;SAAG;mBAAhE;UACE,kBAAC,IAAD;WACE,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;WAC3B,OAAM;WACN,MAAK;WACL,QAAQ,kBAAC,GAAD;YAAK,MAAK;YAAK,eAAe,EAAc,EAAI;sBAAG;WAAW,CAAA;UACvE,CAAA;UACD,kBAAC,IAAD;WACE,MAAM,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;WAC9B,OAAM;WACN,MAAK;WACL,QAAQ,kBAAC,GAAD;YAAM,MAAK;YAAQ,MAAK;sBAAK;WAAgB,CAAA;UACtD,CAAA;UACD,kBAAC,IAAD;WACE,MAAM,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;WAC9B,OAAM;WACN,MAAK;WACL,QAAQ,kBAAC,GAAD;YAAK,MAAK;YAAK,MAAK;sBAAQ;WAAW,CAAA;UAChD,CAAA;UACD,kBAAC,IAAD;WACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;WAChC,OAAM;WACN,MAAK;WACL,QAAQ,kBAAC,GAAD;YAAK,MAAK;YAAK,MAAK;sBAAQ;WAAS,CAAA;UAC9C,CAAA;SACE;;OACM,CAAA;OAEb,kBAAC,IAAD;QAAa,OAAM;QAAS,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;kBACrD,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,eAAe;UAAU,KAAK;SAAE;mBAA/D;UACE,kBAAC,IAAD;WAAU,OAAM;qBAAO,kBAAC,GAAD;YAAM,MAAK;YAAS,MAAK;YAAK,KAAA;sBAAK;WAAW,CAAA;UAAW,CAAA;UAChF,kBAAC,IAAD;WAAU,OAAM;qBACb,GAAM,SAAS,EAAK,MAAM,SAAS,IAClC,kBAAC,OAAD;YAAK,OAAO;aAAE,SAAS;aAAQ,KAAK;aAAG,UAAU;YAAO;sBACrD,EAAK,MAAM,KAAK,MAAM,kBAAC,GAAD,EAAA,UAAe,EAAQ,GAAZ,CAAY,CAAC;WAC5C,CAAA,IACH,kBAAC,QAAD;YAAM,OAAO,EAAE,OAAO,gBAAgB;sBAAG;WAAO,CAAA;UAC5C,CAAA;UACV,kBAAC,IAAD;WAAU,OAAM;qBACd,kBAAC,QAAD;YAAM,WAAU;YAAO,OAAO;aAAE,OAAO;aAAiB,UAAU;YAAK;sBAAvE,CACG,GAAM,aAAa,UAAU,GAAE,WAC5B;;UACE,CAAA;UACV,kBAAC,IAAD;WAAU,OAAM;qBAAW,kBAAC,QAAD;YAAM,WAAU;sBAAO;WAAO,CAAA;UAAW,CAAA;SACjE;;OACM,CAAA;OAEb,kBAAC,IAAD;QAAa,OAAM;QAAgB,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;kBAC7D,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,eAAe;UAAU,KAAK;SAAG;mBAC5D;UACA;WAAC;WAAS;WAAS;UAA+B;UAClD;WAAC;WAAS;WAAU;UAA8B;UAClD;WAAC;WAAS;WAAS;UAAmB;UACtC;WAAC;WAAW;WAAW;UAAuB;SAChD,EAAY,KAAK,CAAC,GAAG,GAAO,OAC1B,kBAAC,OAAD;UAAa,OAAO;WAAE,SAAS;WAAQ,YAAY;WAAU,KAAK;UAAG;oBAArE,CACE,kBAAC,OAAD;WAAK,OAAO,EAAE,MAAM,EAAE;qBAAtB,CACE,kBAAC,OAAD;YAAK,OAAO;aAAE,UAAU;aAAM,YAAY;YAAI;sBAAI;WAAW,CAAA,GAC7D,kBAAC,OAAD;YAAK,OAAO;aAAE,UAAU;aAAI,OAAO;YAAgB;sBAAI;WAAU,CAAA,CAC9D;cACL,kBAAC,GAAD;WAAY,OAAO,CAAC,CAAC,EAAO;WAAI,WAAW,MAAM,EAAU;YAAE,GAAG;aAAS,IAAI;WAAE,CAAC;UAAI,CAAA,CACjF;YANK,CAML,CACN;QACE,CAAA;OACM,CAAA;MACV;;KAUL,kBAAC,IAAD,EAAqB,QAAQ,GAAM,GAAK,CAAA;KAEvC,KACC,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,OAAO;OAAiB,WAAW;OAAI,SAAS;OAAQ,KAAK;MAAG;gBAA5F;OACE,kBAAC,QAAD,EAAA,UAAA,CAAM,aAAS,kBAAC,QAAD;QAAM,WAAU;kBAAQ,EAAK;OAAS,CAAA,CAAO,EAAA,CAAA;OAC5D,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA;OACb,kBAAC,QAAD,EAAA,UAAA,CAAM,YAAQ,kBAAC,QAAD;QAAM,WAAU;kBAAQ;OAAgB,CAAA,CAAO,EAAA,CAAA;MAC1D;;IAEJ;;GAEL,kBAAC,IAAD;IACE,MAAM;IACN,aAAa,GAAM,gBAAgB;IACnC,QAAQ,GAAM,MAAM;IACpB,eAAe,EAAY,EAAK;IAChC,SAAS,YAAY;KAEnB,AADA,MAAM,EAAU,GAChB,EAAY,EAAK;IACnB;GACD,CAAA;GAED,kBAAC,IAAD;IACE,MAAM;IACN,eAAe,EAAc,EAAK;IAClC,UAAU,OAAO,EAAE,YAAS,cAAW;KAErC,AADA,MAAM,EAAe,GAAS,CAAI,GAClC,EAAM,QAAQ,8CAA8C;IAC9D;GACD,CAAA;EACE;;AAET;AAGA,SAAS,GAAkB,EACzB,SAAM,gBAAa,WAAQ,YAAS,cAOnC;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,CAAW,GACtC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI;CAEtD,IAAI,CAAC,GAAM,OAAO;CAElB,IAAM,UAAc;EAAwC,AAAtC,EAAQ,CAAW,GAAG,EAAS,IAAI,GAAG,EAAQ;CAAG;CAgBvE,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EAAK,SAAS;EAAO,OAAO;GAAE,UAAU;GAAS,OAAO;GAAG,YAAY;GAAgB,QAAQ;GAAI,gBAAgB;EAAY;CAAI,CAAA,GACnI,kBAAC,OAAD;EAAK,OAAO;GACV,UAAU;GAAS,KAAK;GAAO,MAAM;GAAO,WAAW;GACvD,QAAQ;GAAI,OAAO;GAAK,YAAY;GAAkB,cAAc;GACpE,WAAW;GAAoB,QAAQ;GAA2B,UAAU;EAC9E;YAJA;GAKE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAe,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAArF;KACE,kBAAC,QAAD;MAAM,OAAO;OACX,OAAO;OAAI,QAAQ;OAAI,cAAc;OAAG,YAAY;OAAsB,OAAO;OACjF,SAAS;OAAe,YAAY;OAAU,gBAAgB;MAChE;gBACE,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAClB,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO,EAAE,MAAM,EAAE;gBAAtB,CACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO;OAAc;iBAAG;MAAiB,CAAA,GACtF,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAM,OAAO;OAAgB;iBAAG;MAA8B,CAAA,CACnF;;KACL,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;KAAQ,CAAA;IACnD;;GACL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAkB,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAA3F,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;KAAY,UAAA;eAAS;IAAwB,CAAA,GAC7C,kBAAC,GAAD;KAAW,OAAO;KAAM,UAAU;KAAS,aAAY;KAAY,WAAA;IAAW,CAAA,CAC3E,EAAA,CAAA,GACJ,KACC,kBAAC,OAAD;KAAK,OAAO;MACV,SAAS;MAAY,YAAY;MAAsB,cAAc;MACrE,UAAU;MAAM,OAAO;MAAiB,SAAS;MAAQ,KAAK;KAChE;eAHA,CAIE,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA,GACtB,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA,CAChB;MAEJ;;GACL,kBAAC,OAAD;IAAK,OAAO;KACV,SAAS;KAAa,WAAW;KACjC,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,YAAY;IAC7D;cAHA;KAIE,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MAAK,SAAS;gBAAO;KAAW,CAAA;KAChC,kBAAC,GAAD;MACE,MAAK;MACL,MAAgB,EAAV,IAAW,IAAwB,EAAK,OAA9B,EAAS,MAAM,GAAK,CAA2B;MAC/D,SAAS,YA3DI;OACrB,IAAI,CAAC,EAAK,KAAK,GAAG;QAAE,EAAS,2BAA2B;QAAG;OAAQ;OAEnE,AADA,EAAS,IAAI,GACb,EAAW,EAAI;OACf,IAAI;QAEF,AADA,MAAM,EAAa,WAAW,EAAE,IAAI,EAAO,GAAG,EAAE,cAAc,EAAK,KAAK,EAAE,CAAC,GAC3E,MAAM,EAAQ;OAChB,SAAS,GAAG;QACV,EAAS,aAAa,QAAQ,EAAE,UAAU,iBAAiB;OAC7D,UAAU;QACR,EAAW,EAAK;OAClB;MACF;MAgDU,UAAU,CAAC,EAAK,KAAK,KAAK;gBAEzB,IAAU,YAAY;KACpB,CAAA;IACF;;EACF;GACL,EAAA,CAAA;AAEN;AAEA,SAAS,GAAY,EAAE,SAAM,UAAO,SAAM,aAA+E;CACvH,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAI,SAAS;EAAQ;YAA/E;GACE,kBAAC,QAAD;IAAM,OAAO;KACX,OAAO;KAAI,QAAQ;KAAI,cAAc;KAAG,YAAY;KAAoB,OAAO;KAC/E,SAAS;KAAe,YAAY;KAAU,gBAAgB;KAAU,YAAY;IACtF;cAAI;GAAW,CAAA;GACf,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;IAAE;cAAnC,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAM,YAAY;KAAI;eAAI;IAAW,CAAA,GAC7D,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAI,OAAO;KAAgB;eAAI;IAAU,CAAA,CAC9D;;GACJ;EACE;;AAET;AAEA,SAAS,KAAoB;CAC3B,IAAI;EACF,OAAO,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;CAC7D,QAAQ;EACN,OAAO;CACT;AACF;AAUA,SAAS,GAAoB,EAAE,aAA0C;CACvE,IAAM,EAAE,MAAM,MAAW,GAAU;CACnC,IAAI,CAAC,KAAU,CAAC,GAAQ,OAAO;CAC/B,IAAM,IAAS,EAAO,SAAS;CAW/B,OAVK,IAUE,kBAAC,IAAD;EAAiB,MAAM,EAAE,YAAY,UAAU;EAAQ,KAAA;GAR5D;GACA;GACA,YAAY;GACZ,UAAU;GACV,QAAQ,EAAE,IAAI,EAAO;GACrB,OAAO;GACP,MAAM;EAEsD;CAAM,CAAA,IAVhD;AAWtB;;;AClYA,IAAM,KAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,OAAO;CACP,eAAe;AACjB,GAEM,KAA8C;CAClD;EAAE,IAAI;EAAU,GAAG;CAAU;CAC7B;EAAE,IAAI;EAAU,GAAG;CAAU;CAC7B;EAAE,IAAI;EAAU,GAAG;CAAU;CAC7B;EAAE,IAAI;EAAU,GAAG;CAAU;CAC7B;EAAE,IAAI;EAAU,GAAG;CAAU;AAC/B,GAEM,KAAuB;CAAC;CAAW;CAAe;AAAM,GAExD,KAA0D;CAC9D;EAAC;EAAU;EAAU;CAAsF;CAC3G;EAAC;EAAU;EAAU;CAA0I;CAC/J;EAAC;EAAQ;EAAQ;CAAuD;AAC1E,GAEM,KAAqC;CACzC,CAAC,mBAAmB,KAAK;CACzB,CAAC,uBAAuB,KAAK;CAC7B,CAAC,oBAAoB,KAAK;CAC1B,CAAC,qBAAqB,KAAK;CAC3B,CAAC,gBAAgB,GAAG;CACpB,CAAC,kBAAkB,KAAK;AAC1B;AAEA,SAAgB,KAAc;CAC5B,IAAM,IAAQ,GAAoB,MAAM,EAAE,KAAK,GACzC,IAAS,GAAoB,MAAM,EAAE,MAAM,GAC3C,IAAU,GAAoB,MAAM,EAAE,OAAO,GAC7C,IAAS,GAAoB,MAAM,EAAE,MAAM,GAC3C,IAAQ,GAAoB,MAAM,EAAE,KAAK,GACzC,IAAgB,GAAoB,MAAM,EAAE,aAAa,GACzD,IAAe,GAAoB,MAAM,EAAE,GAAG,GAC9C,IAAS,GAAc,MAAM,EAAE,MAAM,EAAE,GAKvC,KACJ,GACA,MACG;EAEH,IADA,EAAa,GAA2C,CAAK,GACzD,CAAC,GAAQ;EACb,IAAM,IAAO,GAAa;EACrB,KACL,GAAa,GAAQ,GAAM,CAAK,EAAE,OAAO,MAAe;GACtD,EAAM,MAAM,kBAAkB,KAAO,EAAI,OAAO;EAClD,CAAC;CACH;CAEA,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,MAAM;GAAG,WAAW;GAAQ,SAAS;GAAkB,YAAY;EAAY;YAC3F,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAK,QAAQ;GAAS;aAA9C;IACE,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,GAAG;eAA/B,CACE,kBAAC,MAAD;MAAI,OAAO;OAAE,QAAQ;OAAG,UAAU;OAAI,YAAY;OAAK,eAAe;MAAW;gBAAG;KAAe,CAAA,GACnG,kBAAC,KAAD;MAAG,OAAO;OAAE,QAAQ;OAAW,UAAU;OAAM,OAAO;MAAgB;gBAAtE;OAAyE;OACK;OAC5E,kBAAC,QAAD;QAAM,WAAU;kBAAO;OAAoB,CAAA;OAAC;MAC3C;OACA;;IAEL,kBAAC,IAAD;KAAa,OAAM;KAAa,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;eAC1D,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,eAAe;OAAU,KAAK;MAAG;gBAAhE;OACE,kBAAC,IAAD;QAAS,OAAM;QAAQ,MAAK;kBAC1B,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,KAAK;SAAE;mBAClC,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,QAAQ,MAAM,CAAC,EAA+B,KAAK,CAAC,GAAG,OAC7E,kBAAC,IAAD;UAAgB,QAAQ,MAAU;UAAG,eAAe,EAAQ,SAAS,CAAC;oBAAI;SAAU,GAAvE,CAAuE,CACrF;QACE,CAAA;OACE,CAAA;OACT,kBAAC,IAAD;QAAS,OAAM;QAAe,MAAK;kBACjC,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,KAAK;SAAE;mBACnC,GAAQ,KAAK,MACZ,kBAAC,UAAD;UAEE,eAAe,EAAQ,UAAU,EAAE,EAAE;UACrC,cAAY,UAAU,EAAE;UACxB,OAAO;WACL,OAAO;WAAI,QAAQ;WAAI,cAAc;WAAG,YAAY,EAAE;WACtD,QAAQ,MAAW,EAAE,KAAK,0BAA0B;WACpD,QAAQ;UACV;SACD,GARM,EAAE,EAQR,CACF;QACE,CAAA;OACE,CAAA;OACT,kBAAC,IAAD;QAAS,OAAM;QAAU,MAAK;kBAC5B,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,KAAK;SAAE;mBACnC,GAAU,KAAK,MACd,kBAAC,IAAD;UAAgB,QAAQ,MAAY;UAAG,eAAe,EAAQ,WAAW,CAAC;UAAG,YAAA;oBAC1E;SACK,GAFK,CAEL,CACT;QACE,CAAA;OACE,CAAA;OACT,kBAAC,IAAD;QAAS,OAAM;kBAAf,CACE,kBAAC,SAAD;SACE,MAAK;SAAQ,KAAK;SAAG,KAAK;SAAI,OAAO;SACrC,WAAW,MAAM,EAAQ,UAAU,OAAO,EAAE,OAAO,KAAK,CAAC;SACzD,OAAO,EAAE,OAAO,IAAI;QACrB,CAAA,GACD,kBAAC,QAAD;SAAM,WAAU;SAAW,OAAO;UAAE,UAAU;UAAI,OAAO;UAAiB,YAAY;SAAE;mBAAxF,CAA4F,GAAO,IAAQ;UACpG;;OACT,kBAAC,IAAD;QAAS,OAAM;QAAa,MAAK;kBAC/B,kBAAC,GAAD;SAAY,OAAO;SAAO,WAAW,MAAM,EAAQ,SAAS,CAAC;QAAI,CAAA;OAC1D,CAAA;OACT,kBAAC,IAAD;QACE,OAAM;QACN,MAAM,GAAgB,MAAM,CAAC,OAAQ,MAAO,CAAa,IAAI;kBAE7D,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,KAAK;SAAE;mBACnC,GAAgB,KAAK,CAAC,GAAI,OACzB,kBAAC,IAAD;UAEE,QAAQ,MAAkB;UAC1B,eAAe,EAAQ,iBAAiB,CAAE;oBAEzC;SACK,GALD,CAKC,CACT;QACE,CAAA;OACE,CAAA;MACN;;IACM,CAAA;IAEb,kBAAC,OAAD,EAAK,OAAO,EAAE,QAAQ,GAAG,EAAI,CAAA;IAC7B,kBAAC,IAAD;KAAa,OAAM;KAAW,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;eAC3D,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,qBAAqB;OAAW,KAAK;MAAE;gBACnE,GAAU,KAAK,CAAC,GAAG,OAClB,kBAAC,OAAD;OAAa,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,SAAS;OAAQ;iBAA9E,CACE,kBAAC,QAAD;QAAM,OAAO;SAAE,MAAM;SAAG,UAAU;SAAM,OAAO;QAAgB;kBAAI;OAAQ,CAAA,GAC3E,kBAAC,IAAD,EAAA,UAAM,EAAO,CAAA,CACV;SAHK,CAGL,CACN;KACE,CAAA;IACM,CAAA;GACV;;CACF,CAAA;AAET;AAEA,SAAS,GAAQ,EAAE,UAAO,SAAM,eAAmE;CACjG,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAI,SAAS;EAAQ;YAA/E,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,OAAO;IAAK,YAAY;GAAE;aAAxC,CACE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,OAAO;IAAc;cAAI;GAAW,CAAA,GAClF,KAAQ,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAAI;GAAU,CAAA,CACvE;MACL,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,MAAM;GAAE;GAAI;EAAc,CAAA,CAC5E;;AAET;AAEA,SAAS,GAAO,EAAE,WAAQ,YAAS,aAAU,iBAAmG;CAC9I,OACE,kBAAC,UAAD;EACW;EACT,OAAO;GACL,SAAS;GAAY,UAAU;GAAI,cAAc;GAAG,QAAQ;GAC5D,eAAe,IAAa,eAAe;GAC3C,YAAY,IAAS,kBAAkB;GACvC,OAAO,IAAS,6BAA6B;GAC7C,QAAQ,aAAa,IAAS,kBAAkB;EAClD;EAEC;CACK,CAAA;AAEZ;;;ACtMA,SAAgB,KAAmB;CACjC,IAAM,EAAE,MAAM,MAAW,GAAU,GAC7B,CAAC,GAAW,KAAgB,EAAc,UAAU;CAI1D,OAFK,IAGH,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,QAAQ;EAAO;YAAvE,CACE,kBAAC,OAAD;GAAK,OAAO,EAAE,SAAS,cAAc;aAArC,CACE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAY,KAAK;KAAI,cAAc;IAAG;cAC/E,kBAAC,MAAD;KAAI,OAAO;MAAE,QAAQ;MAAG,UAAU;MAAI,YAAY;MAAK,eAAe;MAAY,OAAO;KAAc;eAAG;IAEtG,CAAA;GACD,CAAA,GACL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,KAAK;KAAG,cAAc;IAAG;cAAxD,CACE,kBAAC,GAAD;KACE,MAAM,MAAc,aAAa,YAAY;KAC7C,QAAQ,MAAc;KACtB,eAAe,EAAa,UAAU;eACvC;IAEI,CAAA,GACL,kBAAC,GAAD;KACE,MAAM,MAAc,YAAY,YAAY;KAC5C,QAAQ,MAAc;KACtB,eAAe,EAAa,SAAS;eACtC;IAEI,CAAA,CACF;KACF;MACL,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,WAAW;IAAQ,SAAS;GAAc;aAAjE,CACG,MAAc,cAAc,kBAAC,IAAD,EAAqB,UAAS,CAAA,GAC1D,MAAc,aAAa,kBAAC,IAAD,EAAoB,UAAS,CAAA,CACtD;IACF;MA/Ba;AAiCtB;AAEA,SAAS,GAAY,EAAE,aAAyE;CAC9F,IAAM,IAAc,QAAc;EAChC,IAAM,IAAwE,CAAC;EAC/E,IAAI,EAAO,UACT,KAAK,IAAM,CAAC,GAAM,MAAM,OAAO,QAAQ,EAAO,QAAQ,GACpD,EAAO,KAAK;GAAE;GAAM,SAAS;EAAE,CAAC;EAGpC,KAAK,IAAM,CAAC,GAAY,MAAW,OAAO,QAAQ,EAAO,QAAQ,GAC/D,IAAI,EAAO,UACT,KAAK,IAAM,CAAC,GAAM,MAAM,OAAO,QAAQ,EAAO,QAAsC,GAClF,EAAO,KAAK;GAAE,MAAM,GAAG,EAAW,GAAG;GAAQ,SAAS;GAAG,QAAQ;EAAW,CAAC;EAInF,OAAO,EAAO,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CAC3D,GAAG,CAAC,CAAM,CAAC;CAEX,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAG;YAAhE,CACE,kBAAC,KAAD;GAAG,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,QAAQ;GAAE;aAA9D,CACG,EAAY,QAAO,iEACnB;MACH,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,eAAe;IAAU,KAAK;GAAE;aAC5D,EAAY,KAAK,EAAE,SAAM,YAAS,gBACjC,kBAAC,IAAD;IAA8B;IAAe;IAAiB;GAAS,GAArD,CAAqD,CACxE;EACE,CAAA,CACF;;AAET;AAEA,SAAS,GAAY,EAAE,SAAM,YAAS,aAAkE;CACtG,IAAM,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,IAAc,EAAQ,SAAS,OAAO,EAAI,EAAQ,QAAQ,IAAI,IAAI;CAExE,OACE,kBAAC,OAAD;EACE,OAAO;GACL,QAAQ;GACR,cAAc;GACd,SAAS;GACT,YAAY;EACd;YANF,CAQE,kBAAC,OAAD;GACE,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,gBAAgB;IAAiB,QAAQ;GAAU;GACnG,eAAe,GAAa,MAAS,CAAC,CAAI;aAF5C,CAIE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAA7D,CACG,EAAQ,SAAS,QAChB,kBAAC,QAAD;KAAM,OAAO;MAAE,OAAO;MAAiB,SAAS;KAAO;eACrD,kBAAC,GAAD;MAAa,MAAM,EAAQ,QAAQ;MAAM,MAAM;KAAK,CAAA;IAChD,CAAA,GAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;KAAM,WAAU;KAAO,OAAO;MAAE,UAAU;MAAM,YAAY;KAAI;eAC7D;IACG,CAAA,GACL,MAAgB,KACf,kBAAC,QAAD;KAAM,OAAO;MAAE,YAAY;MAAG,UAAU;MAAM,OAAO;KAAgB;eAClE;IACG,CAAA,CAEL,EAAA,CAAA,CACF;OACL,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAE;cAA5D;KACE,kBAAC,GAAD;MAAM,MAAK;MAAQ,MAAK;gBACrB,EAAQ;KACL,CAAA;KACL,KACC,kBAAC,GAAD;MAAM,MAAK;MAAS,MAAK;gBACtB;KACG,CAAA;KAEP,EAAQ,iBACP,kBAAC,GAAD;MAAM,MAAK;MAAU,MAAK;gBAA1B,CAA+B,WACrB,EAAQ,aACZ;;IAEL;KACF;MACJ,KAAY,EAAQ,UAAU,EAAQ,OAAO,SAAS,KACrD,kBAAC,OAAD;GAAK,OAAO;IAAE,WAAW;IAAI,WAAW;IAA4B,YAAY;GAAG;aAAnF,CACE,kBAAC,KAAD;IACE,OAAO;KACL,UAAU;KACV,YAAY;KACZ,OAAO;KACP,cAAc;KACd,WAAW;KACX,eAAe;KACf,eAAe;IACjB;cACD;GAEE,CAAA,GACH,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAE;eAC3D,EAAQ,QAAQ,MAAM,KAAK,CAAC,GAAG,KAAK,MAAc;KAClD,IAAM,IAAQ,EAAQ,OAAQ,IAAI,CAAS;KAE3C,OADK,IACE,kBAAC,IAAD;MAA0B,MAAM;MAAkB;KAAQ,GAA3C,CAA2C,IAD9C;IAErB,CAAC;GACE,CAAA,CACF;IAEJ;;AAET;AAEA,SAAS,GAAS,EAAE,SAAM,YAAgD;CACxE,IAAM,IAAc,EAAM,SAAS,OAAO,EAAI,EAAM,QAAQ,IAAI,IAAI,KAAA;CACpE,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAI,UAAU;GAAI,SAAS;EAAQ;YAA7F;GACE,kBAAC,QAAD;IAAM,WAAU;IAAO,OAAO;KAAE,UAAU;KAAI,OAAO;KAAK,YAAY;IAAE;cACrE;GACG,CAAA;GACN,kBAAC,GAAD;IAAM,MAAK;IAAQ,MAAK;cACrB,EAAM,QAAQ;GACX,CAAA;GACL,EAAM,YACL,kBAAC,GAAD;IAAM,MAAK;IAAO,MAAK;cAAK;GAEtB,CAAA;GAEP,EAAM,UACL,kBAAC,GAAD;IAAM,MAAK;IAAQ,MAAK;cAAK;GAEvB,CAAA;GAEP,EAAM,WAAW,QAChB,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAApD,CAAuD,aAAU,OAAO,EAAM,OAAO,CAAQ;;GAE9F,KAAe,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAAI;GAAkB,CAAA;EACvF;;AAET;AAEA,SAAS,GAAW,EAAE,aAAyE;CAC7F,IAAM,IAAa,QAAc;EAC/B,IAAM,IAAmE,CAAC;EAC1E,IAAI,EAAO,SACT,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,EAAO,OAAO,GACxD,EAAO,KAAK;GAAE;GAAM;EAAO,CAAC;EAGhC,KAAK,IAAM,CAAC,GAAY,MAAW,OAAO,QAAQ,EAAO,QAAQ,GAC/D,IAAI,EAAO,SACT,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,EAAO,OAAO,GACxD,EAAO,KAAK;GAAE,MAAM,GAAG,EAAW,GAAG;GAAQ;GAAQ,QAAQ;EAAW,CAAC;EAI/E,OAAO,EAAO,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CAC3D,GAAG,CAAC,CAAM,CAAC,GAEL,IAAU,QAAc;EAC5B,IAAM,IAA4C,CAAC;EACnD,KAAK,IAAM,KAAQ,GAAY;GAC7B,IAAM,IAAM,EAAK,UAAU;GAE3B,AADK,EAAO,OAAM,EAAO,KAAO,CAAC,IACjC,EAAO,GAAK,KAAK,CAAI;EACvB;EACA,OAAO,OAAO,QAAQ,CAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;CACrE,GAAG,CAAC,CAAU,CAAC;CAEf,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAG;YAAhE,CACE,kBAAC,KAAD;GAAG,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,QAAQ;GAAE;aAA9D,CACG,EAAW,QAAO,6DAClB;MACF,EAAQ,KAAK,CAAC,GAAO,OACpB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,OAAO;IAAE,UAAU;IAAM,YAAY;IAAK,QAAQ;IAAa,OAAO;GAAc;aACrF;EACC,CAAA,GACJ,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,eAAe;IAAU,KAAK;GAAE;aAC5D,EAAQ,KAAK,EAAE,SAAM,gBACpB,kBAAC,IAAD;IAA6B;IAAc;GAAS,GAAnC,CAAmC,CACrD;EACE,CAAA,CACF,EAAA,GATK,CASL,CACN,CACE;;AAET;AAEA,SAAS,GAAW,EAAE,SAAM,aAA4C;CACtE,IAAM,IAAc,EAAI,EAAO,SAAS,IAAI;CAC5C,OACE,kBAAC,OAAD;EACE,OAAO;GACL,QAAQ;GACR,cAAc;GACd,SAAS;GACT,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,YAAY;EACd;YATF,CAWE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAG;aAA7D,CACG,EAAO,SAAS,QACf,kBAAC,QAAD;IAAM,OAAO;KAAE,OAAO;KAAiB,SAAS;IAAO;cACrD,kBAAC,GAAD;KAAa,MAAM,EAAO,QAAQ;KAAM,MAAM;IAAK,CAAA;GAC/C,CAAA,GAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;IAAM,WAAU;IAAO,OAAO,EAAE,UAAU,KAAK;cAC5C;GACG,CAAA,GACL,KAAe,MAAgB,KAC9B,kBAAC,QAAD;IAAM,OAAO;KAAE,YAAY;KAAG,UAAU;KAAM,OAAO;IAAgB;cAAI;GAAkB,CAAA,CAE1F,EAAA,CAAA,CACF;MACL,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAE;aAA5D;WAQU;KACN,IAAM,IAAO,EAAO,UAChB,YACA,EAAO,cACL,gBACA,EAAO,SACL,WACA,EAAO,OACL,SACA;KACV,OAAO,IACL,kBAAC,GAAD;MAAM,MAAK;MAAQ,MAAK;gBACrB;KACG,CAAA,IACJ;IACN,GAAG;IACF,EAAO,WACN,kBAAC,QAAD;KAAM,WAAU;KAAO,OAAO;MAAE,UAAU;MAAI,OAAO;KAAgB;eAArE,CAAwE,aAC5D,EAAO,OACb;;IAEP,EAAO,cAAc,EAAO,WAAW,SAAS,KAC/C,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK;KAAE;eACnC,EAAO,WAAW,KAAK,MACtB,kBAAC,GAAD;MAAc,MAAK;MAAQ,MAAK;gBAC7B;KACG,GAFK,CAEL,CACP;IACE,CAAA;GAEJ;IACF;;AAET;;;ACvOA,IAAM,KAAuB,IAAI,IAAI,CAAC,WAAW,UAAU,CAAC;AAoB5D,SAAgB,GAAe,GAAmC;CAChE,IAAM,IAAmB,CAAC;CAU1B,OATI,EAAM,eAAY,EAAI,SAAS,EAAM,aACrC,EAAM,eAAe,EAAM,YAAY,SAAS,MAClD,EAAI,UAAU,EAAM,cAElB,EAAM,WAAQ,EAAI,SAAS,EAAM,SACjC,EAAM,SAAM,EAAI,OAAO,EAAM,OAC7B,EAAM,mBAAgB,EAAI,kBAAkB,KAC5C,EAAM,SAAM,EAAI,OAAO,EAAM,OAC7B,EAAM,YAAS,EAAI,WAAW,EAAM,UACjC;AACT;AAOA,SAAgB,GACd,GACA,GACe;CACf,IAAM,IAAU,EAAO,eAAe,QAAQ,MAAM,CAAC,GAAqB,IAAI,CAAC,CAAC,GAC1E,IAAqB;EACzB,QAAQ;EACR,OAAO,EAAO;EACd,OAAO,GAAe,CAAK;EAC3B;CACF;CAMA,OALI,EAAO,UAAU,mBAAmB,EAAO,kBAAkB,EAAO,eAAe,SAAS,MAC9F,EAAI,YAAY,EAAO,iBAErB,EAAO,WAAQ,EAAI,SAAS,EAAO,SACnC,EAAO,aAAU,EAAI,WAAW,EAAO,WACpC;AACT;;;ACzEA,SAAS,GAAY,GAAmD;CACtE,OAAO,EAAE,KAAS,EAAM,YAAY;AACtC;AAIA,SAAS,GAAe,GAAmC;CAEzD,OADI,KAAS,OAAa,sBACnB,GAAG,EAAM,eAAe,EAAE,GAAG,MAAU,IAAI,QAAQ;AAC5D;AAEA,SAAgB,GAAW,GAAwB;CACjD,IAAM,EACJ,eACA,WACA,WACA,cACA,mBACA,mBACA,cACA,aACA,WACA,WAAQ,OACR,aAAU,WACV,oBACE,GAKE,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAmD,IAAI,GAC3E,IAAY,EAAiC,IAAI,GAIjD,IAAgB,GAAY,EAAO,MAAM,GACzC,IAAgB,GAAY,GAAQ,MAAM;CAChD,IAAI,CAAC,KAAiB,CAAC,GACrB,OAAO;CAGT,eAAe,EAAU,GAAoB;EAE3C,AADA,EAAS,IAAI,GACb,EAAW,EAAI;EACf,IAAI;GAOF,IAAM,EAAE,SAAM,gBAAa,MAAM,EAAe,GANnC,GAAmB,GAAW;IACzC;IACA;IACA,gBAAgB,MAAU,kBAAkB,IAAiB,KAAA;IAC7D;GACF,CAC4D,CAAI;GAGhE,AADA,GAAgB,GADK,KAAY,GAAG,EAAW,GAAG,GAAkB,EAAE,KACpC,GAClC,EAAQ,EAAK;EACf,SAAS,GAAK;GAEZ,EADU,aAAe,KAAW;IAAE,MAAM,EAAI;IAAM,SAAS,EAAI;GAAQ,IAAI;IAAE,MAAM;IAAW,SAAS,OAAO,CAAG;GAAE,CAC7G;EAEZ,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,IAAI,MAAY,uBAAuB;EACrC,IAAM,IAAU,KAAW,EAAe,WAAW;EACrD,OACE,kBAAC,GAAD;GACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;GAChC,eAAY;GACZ,SAAS,IAAU,KAAA,UAAkB;IAEnC,AADA,IAAe,GACf,EAAe,eAAe;GAChC;aAEC,IAAU,eAAe;EAClB,CAAA;CAEd;CAEA,IAAM,IAAe,EAAe,SAAS;CAE7C,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,KAAK;EACL,MAAK;EACL,MAAK;EACL,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;EAChC,eAAe;GAEb,AADA,IAAe,GACf,GAAS,MAAM,CAAC,CAAC;EACnB;EACA,UAAU;YAET,IAAU,eAAe;CACvB,CAAA,GACL,kBAAC,IAAD;EAAgB;EAAM,QAAQ,EAAU;EAAS,eAAe,EAAQ,EAAK;EAAU;EAAO,OAAO;YAArG;GACE,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;IAChC,SAAS,IAAU,KAAA,UAAkB,KAAK,EAAU,cAAc;cAFpE,CAGC,yBACuB,GAAe,CAAS,CACtC;;GACT,KACC,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;IAC7B,SAAS,IAAU,KAAA,UAAkB,KAAK,EAAU,eAAe;cAFrE,CAGC,0BACwB,EAAe,MAC9B;;GAEZ,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;IAC/B,SAAS,IAAU,KAAA,UAAkB,KAAK,EAAU,cAAc;cAFpE,CAGC,yBACuB,IAAW,GAAG,EAAS,SAAS,aAC9C;;GACV,kBAAC,GAAD;IAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;cAAG;GAE/B,CAAA;GACT,KACC,kBAAC,OAAD;IACE,MAAK;IACL,OAAO;KACL,SAAS;KACT,WAAW;KACX,UAAU;KACV,OAAO;KACP,WAAW;IACb;cARF;KAUE,kBAAC,UAAD,EAAA,UAAS,EAAM,KAAa,CAAA;KAAC;KAAG,EAAM;IACnC;;EAEC;GACV,EAAA,CAAA;AAEN;AAOA,SAAS,KAA4B;CACnC,IAAM,oBAAI,IAAI,KAAK,GACb,KAAO,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;CACpD,OAAO,GAAG,EAAE,eAAe,IAAI,EAAI,EAAE,YAAY,IAAI,CAAC,IAAI,EAAI,EAAE,WAAW,CAAC,EAAE,GAAG,EAAI,EAAE,YAAY,CAAC,IAAI,EAAI,EAAE,cAAc,CAAC;AAC/H;AAMA,SAAS,GAAgB,GAAY,GAAkB;CACrD,IAAM,IAAM,IAAI,gBAAgB,CAAI,GAC9B,IAAI,SAAS,cAAc,GAAG;CASpC,AARA,EAAE,OAAO,GACT,EAAE,WAAW,GACb,EAAE,MAAM,UAAU,QAClB,SAAS,KAAK,YAAY,CAAC,GAC3B,EAAE,MAAM,GACR,SAAS,KAAK,YAAY,CAAC,GAG3B,iBAAiB,IAAI,gBAAgB,CAAG,GAAG,CAAC;AAC9C;;;AChOA,SAAgB,GAAe,GAAU,GAAkB;CACzD,IAAM,CAAC,GAAW,KAAgB,EAAS,CAAK;CAOhD,OALA,QAAgB;EACd,IAAM,IAAQ,iBAAiB,EAAa,CAAK,GAAG,CAAK;EACzD,aAAa,aAAa,CAAK;CACjC,GAAG,CAAC,GAAO,CAAK,CAAC,GAEV;AACT;;;ACDA,SAAgB,GAAW,EAAE,SAAM,UAAO,gBAAa,aAA2B;CAChF,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,SAAS;GACT,WAAW;EACb;YARF;GAUG,KACC,kBAAC,OAAD;IACE,OAAO;KACL,cAAc;KACd,cAAc;KACd,YAAY;KACZ,SAAS;KACT,OAAO;KACP,SAAS;IACX;cAEA,kBAAC,GAAD;KAAa,MAAM;KAAM,MAAM;IAAK,CAAA;GACjC,CAAA;GAEP,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAG,UAAU;KAAI,YAAY;KAAK,OAAO;IAAc;cAAI;GAAU,CAAA;GACzF,KACC,kBAAC,KAAD;IAAG,OAAO;KAAE,WAAW;KAAG,UAAU;KAAM,OAAO;KAAiB,UAAU;IAAI;cAC7E;GACA,CAAA;GAEJ,KAAU,kBAAC,OAAD;IAAK,OAAO,EAAE,WAAW,GAAG;cAAI;GAAY,CAAA;EACpD;;AAET;;;ACpCA,IAAM,KAAkB;CAAC;CAAS;CAAU;AAAS,GAU/C,KAA2B;AAEjC,SAAgB,GAAgB,GAAsD;CACpF,IAAI,CAAC,GAAO;CACZ,IAAM,IAAU,EAAM,KAAK;CAC3B,IAAI,CAAC,GAAS;CAId,IAAM,IAAY,EAAQ,QAAQ,IAA0B,EAAE;CAK9D,IAJI,CAAC,KAID,EAAU,WAAW,IAAI,GAAG;CAKhC,IAAM,IAAc,EAAU,MAAM,6BAA6B;CACjE,IAAI,GAAa;EACf,IAAM,IAAS,EAAY,GAAG,YAAY,IAAI;EAC9C,IAAI,CAAC,GAAgB,SAAS,CAA0C,GACtE;CAEJ;CACA,OAAO;AACT;;;AC9CA,IAAM,KAA+B;CACnC,SAAS;CACT,YACE;CACF,gBAAgB;CAChB,cAAc;CACd,WAAW;AACb;AAEA,SAAS,GAAK,EAAE,WAAQ,YAAsD;CAC5E,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,SAAD,EAAA,UAAQ,gGAAuG,CAAA,GAC/G,kBAAC,QAAD,EACE,OAAO;EACL,GAAG;EACH;EACA,OAAO,KAAS;CAClB,EACD,CAAA,CACD,EAAA,CAAA;AAEN;AAEA,SAAgB,GAAc,EAAE,UAAO,GAAG,UAAO,KAAuC;CACtF,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAI,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAG;YAA7E,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,KAAK;GAAG;aACpC,MAAM,KAAK,EAAE,QAAQ,EAAK,CAAC,EAAE,KAAK,GAAG,MACpC,kBAAC,OAAD;IAAa,OAAO,EAAE,MAAM,EAAE;cAC5B,kBAAC,IAAD,EAAM,QAAQ,GAAK,CAAA;GAChB,GAFK,CAEL,CACN;EACE,CAAA,GACJ,MAAM,KAAK,EAAE,QAAQ,EAAK,CAAC,EAAE,KAAK,GAAG,MACpC,kBAAC,OAAD;GAAa,OAAO;IAAE,SAAS;IAAQ,KAAK;GAAG;aAC5C,MAAM,KAAK,EAAE,QAAQ,EAAK,CAAC,EAAE,KAAK,GAAG,MACpC,kBAAC,OAAD;IAAa,OAAO,EAAE,MAAM,EAAE;cAC5B,kBAAC,IAAD,EAAM,QAAQ,GAAK,CAAA;GAChB,GAFK,CAEL,CACN;EACE,GANK,CAML,CACN,CACE;;AAET;AAKA,SAAgB,KAAiB;CAC/B,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAI,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAG;YAA7E;GAEE,kBAAC,IAAD;IAAM,QAAQ;IAAI,OAAO;GAAM,CAAA;GAC/B,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAG;cAA7D,CACE,kBAAC,QAAD,EAAM,OAAO;KAAE,GAAG;KAAS,OAAO;KAAI,QAAQ;KAAI,cAAc;IAAE,EAAI,CAAA,GACtE,kBAAC,IAAD;KAAM,QAAQ;KAAI,OAAO;IAAM,CAAA,CAC5B;;GAEJ,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MACjC,kBAAC,OAAD;IAEE,OAAO;KACL,QAAQ;KACR,cAAc;KACd,YAAY;KACZ,UAAU;IACZ;cAPF,CASE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAa,cAAc;KAA2B;eAC3E,kBAAC,IAAD;MAAM,QAAQ;MAAI,OAAO;KAAM,CAAA;IAC5B,CAAA,GACL,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,SAAS;MACT,qBAAqB;MACrB,KAAK;KACP;eAEC,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MACjC,kBAAC,OAAD;MAAa,OAAO;OAAE,SAAS;OAAQ,KAAK;MAAG;gBAA/C,CACE,kBAAC,IAAD;OAAM,QAAQ;OAAI,OAAO;MAAM,CAAA,GAC/B,kBAAC,OAAD;OAAK,OAAO,EAAE,MAAM,EAAE;iBACpB,kBAAC,IAAD,EAAM,QAAQ,GAAK,CAAA;MAChB,CAAA,CACF;QALK,CAKL,CACN;IACE,CAAA,CACF;MA5BE,CA4BF,CACN;EACE;;AAET;;;AC5DA,IAAa,KAAe,IAAI,IAAc;CAC5C;CAAY;CACZ;CAAS;CAAa;CACtB;CAAe;CACf;CAAa;AACf,CAAC,GAGY,KAAsC;CACjD,IAAI;CACJ,QAAQ;CACR,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,QAAQ;CACR,SAAS;CACT,UAAU;CACV,aAAa;CACb,UAAU;CACV,cAAc;CACd,OAAO;CACP,WAAW;CACX,YAAY;CACZ,aAAa;CACb,cAAc;CACd,WAAW;CACX,eAAe;AACjB;AAOA,SAAgB,GAAoB,GAA6C;CAC/E,IAAM,IAAM,EAAa,IAAI,SAAS;CACtC,IAAI,CAAC,GAAK,OAAO,CAAC;CAClB,IAAI;EACF,IAAM,IAAS,KAAK,MAAM,CAAG;EAC7B,OAAO,MAAM,QAAQ,CAAM,IAAI,IAAS,CAAC;CAC3C,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAGA,SAAgB,GACd,GACA,GACM;CACN,AAAI,EAAQ,WAAW,IACrB,EAAa,OAAO,SAAS,IAE7B,EAAa,IAAI,WAAW,KAAK,UAAU,CAAO,CAAC;AAEvD;AAuBA,SAAgB,GAAY,GAA8B;CACxD,OAAO,EAAM,MAAM,MAAM,EAAE,MAAM,IAAI;AACvC;AAGA,SAAgB,GAAoB,GAAmC;CACrE,IAAM,IAAuB,CAAC;CAC9B,KAAK,IAAM,KAAK,GAAO;EAIrB,IAHI,EAAE,SAAS,EAAE,MACf,EAAO,KAAK,CAAC,GAEX,EAAE,IACJ,KAAK,IAAM,KAAS,EAAE,IACpB,EAAO,KAAK,GAAG,GAAoB,CAAK,CAAC;EAG7C,AAAI,EAAE,OACJ,EAAO,KAAK,GAAG,GAAoB,EAAE,GAAG,CAAC;CAE7C;CACA,OAAO;AACT;AAGA,SAAgB,GAAmB,GAA6B;CAC9D,OAAO,GAAoB,CAAK,EAAE;AACpC;AAgBA,SAAgB,GAAS,GAAe,GAA2B;CACjE,OAAO;EAAE;EAAO,IAAI;EAAM;CAAM;AAClC;AAGA,SAAgB,GAAa,GAAqB,GAA6B;CAC7E,IAAM,IAAuB,CAAC;CAC9B,KAAK,IAAM,KAAK,GACV,MAAE,UAAU,GAChB;MAAI,EAAE,IAAI;GACR,IAAM,IAAU,EAAE,GACf,KAAK,MAAU,GAAa,GAAO,CAAK,CAAC,EACzC,QAAQ,MAAU,EAAM,SAAS,CAAC;GACrC,AAAI,EAAQ,SAAS,KAAG,EAAO,KAAK;IAAE,GAAG;IAAG,IAAI;GAAQ,CAAC;GACzD;EACF;EACA,IAAI,EAAE,KAAK;GACT,IAAM,IAAU,GAAa,EAAE,KAAK,CAAK;GACzC,AAAI,EAAQ,SAAS,KAAG,EAAO,KAAK;IAAE,GAAG;IAAG,KAAK;GAAQ,CAAC;GAC1D;EACF;EACA,EAAO,KAAK,CAAC;CANb;CAQF,OAAO;AACT;;;ACvKA,SAAgB,KAAa;CAC3B,IAAM,CAAC,GAAc,KAAmB,GAAgB,GAElD,IAAU,QACR,GAAoB,CAAY,GACtC,CAAC,CAAY,CACf;CAqBA,OAAO;EAAE;EAAS,aAnBE,QAAc,GAAmB,CAAO,GAAG,CAAC,CAAO,CAmBrD;EAAa,YAjBZ,GAChB,MAA6B;GAC5B,IAAM,IAAO,IAAI,gBAAgB,CAAY;GAG7C,AAFA,GAAgB,GAAY,CAAI,GAChC,EAAK,IAAI,QAAQ,GAAG,GACpB,EAAgB,CAAI;EACtB,GACA,CAAC,GAAc,CAAe,CAUD;EAAY,cAPtB,QAAkB;GACrC,IAAM,IAAO,IAAI,gBAAgB,CAAY;GAG7C,AAFA,EAAK,OAAO,SAAS,GACrB,EAAK,IAAI,QAAQ,GAAG,GACpB,EAAgB,CAAI;EACtB,GAAG,CAAC,GAAc,CAAe,CAEU;CAAa;AAC1D;;;AChBA,IAAM,KAAe,IAAI,IAAI;CAAC;CAAQ;CAAQ;AAAK,CAAC;AAEpD,SAAS,GAAc,GAA6B;CAClD,IAAM,IAAS,EAAM,SAAS;CAE9B,OADI,KAAU,GAAa,IAAI,CAAM,IAAU,KACxC,GAAa,IAAI,EAAM,IAAI;AACpC;AAgBA,SAAgB,GAAc,EAC5B,UACA,cACA,UACA,UACA,WACA,SACA,aACA,eACqB;CACrB,IAAM,IAAa,GAAc,GAC3B,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAK,KAAU,EAAkB,CAAK,GACvC,IAAS,GAAc,CAAK;CAElC,QAAgB;EACd,AAAK,KAAS,EAAO,CAAK;CAC5B,GAAG,CAAC,GAAO,CAAO,CAAC;CAEnB,IAAM,IAAY,QAAkB;EAC9B,MACJ,EAAO,CAAK,GACZ,EAAW,EAAI;CACjB,GAAG,CAAC,GAAU,CAAK,CAAC,GAEd,IAAS,GACZ,MAAmB;EAGlB,AADA,EADU,MAAS,KAAA,IAAY,IAAM,CAC3B,GACV,EAAW,EAAK;CAClB,GACA,CAAC,GAAU,CAAG,CAChB,GAEM,IAAS,QAAkB;EAE/B,AADA,EAAO,CAAK,GACZ,EAAW,EAAK;CAClB,GAAG,CAAC,CAAK,CAAC,GAMJ,IAAc,GACjB,MAAuC;EACtC,IAAI,KAAY,GAAS;EACzB,IAAM,IAAS,EAAE;EACb,EAAO,QAAQ,GAAG,KAClB,EAAO,QAAQ,6BAA2B,KAC9C,EAAU;CACZ,GACA;EAAC;EAAU;EAAS;CAAS,CAC/B,GAEM,IAAY,GACf,MAA0C;EACzC,IAAI,CAAC,GAAS;GAEZ,AAAI,CAAC,MAAa,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC/C,EAAE,eAAe,GACjB,EAAU;GAEZ;EACF;EACI,MACA,EAAE,QAAQ,WACZ,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,EAAO,KACE,EAAE,QAAQ,aACnB,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,EAAO;CAEX,GACA;EAAC;EAAS;EAAQ;EAAU;EAAW;EAAQ;CAAM,CACvD,GAIM,IAAgB,GACnB,MAAkB;EACjB,AAAI,KACF,EAAO,CAAI,GACX,EAAO,CAAI,KAEX,EAAO,CAAI;CAEf,GACA,CAAC,GAAQ,CAAM,CACjB,GAKM,IAAc,EAAY;EAC9B;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,QAAQ;CACV,CAAC,GAEK,IAAW,IACb,EAAY;EACV;EACA;EACA,OAAO;EACP,MAAM;EACN,UAAU;EACV;EACA;EACA,QAAQ;CACV,CAAC,IACD;CAyBJ,OACE,kBAAC,OAAD;EACE,WAAU;EACV,gBAAc,IAAU,SAAS;EACjC,SAAS;EACE;EACX,eAAe,MAAM;GACf,KAAY,MAChB,EAAE,cAAc,MAAM,cAAc;EACtC;EACA,eAAe,MAAM;GACnB,EAAE,cAAc,MAAM,cAAc;EACtC;EACA,OAAO;GAnCT,UAAU;GACV,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,QAAQ;GACR,cAAc;GACd,WAAW;GACX,QAAQ,KAAY,IAAU,YAAY;GAC1C,QAAQ;EA0BC;EAKP,UAAU,KAAY,IAAU,KAAK;EACrC,MAAM,KAAY,IAAU,KAAA,IAAY;EACxC,cAAY,KAAY,IAAU,KAAA,IAAY,QAAQ;YAErD,IACC,kBAAC,OAAD;GACE,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAG,MAAM;IAAG,UAAU;GAAE;aAD/E,CAGE,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;IAAE;cAAI;GAAc,CAAA,GACpD,CAAC,KACA,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IACE,MAAM;IACN,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;IAC7B,eAAe,EAAO;IACtB,OAAM;GACP,CAAA,GACD,kBAAC,GAAD;IACE,MAAM;IACN,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACzB,SAAS;IACT,OAAM;GACP,CAAA,CACD,EAAA,CAAA,CAED;OAEL,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;GAAE;aAAI;EAAiB,CAAA,GACvD,CAAC,KACA,kBAAC,QAAD;GAAM,oBAAiB;GAAO,OAAO;IArD7C,UAAU;IACV,OAAO;IACP,KAAK;IACL,WAAW;GAkDkC;GAAa,WAAU;aAC1D,kBAAC,GAAD;IACE,MAAM;IACN,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAC5B,UAAU,MAAM;KAEd,AADA,EAAE,gBAAgB,GAClB,EAAU;IACZ;IACA,OAAM;GACP,CAAA;EACG,CAAA,CAER,EAAA,CAAA;CAED,CAAA;AAET;;;AC7KA,SAAS,GAAQ,GAAW,IAAO,IAAe;CAChD,IAAM,IAAO,EAAE,SAAS;CACxB,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAI,EAAK;CAEf,OADI,IAAU,kBAAC,GAAD,EAAS,QAAO,CAAA,IACvB,kBAAC,GAAD;EAAmB;EAAY;CAAO,CAAA;AAC/C;AAEA,SAAgB,GAA6C,EAC3D,WACA,eACA,WACA,UACA,aACA,eACA,WACA,SACA,WACA,YACA,kBACA,YACA,WACA,aACoB;CACpB,IAAM,IAAa,GAAc,GAC3B,CAAC,GAAO,KAAY,EAAY,EAAE,GAAG,EAAO,CAAC,GAC7C,CAAC,GAAO,KAAY,EAAS,EAAK,GAClC,CAAC,GAAa,KAAkB,EAAS,EAAK,GAC9C,IAAgB,EAAiC,IAAI,GACrD,EAAE,WAAQ,GAAgB;CAEhC,QAAgB;EAEd,AADA,EAAS,EAAE,GAAG,EAAO,CAAC,GACtB,EAAS,EAAK;CAChB,GAAG,CAAC,CAAM,CAAC;CAEX,IAAM,IAAW,QACR,OAAO,QAAQ,CAAO,EAC1B,QAAQ,GAAG,QAAQ,EAAE,cAAc,CAAC,GAAG,SAAS,YAAY,CAAC,EAC7D,KAAK,CAAC,GAAM,QAAa;EAAE;EAAM;CAAO,EAAE,GAC5C,CAAC,CAAO,CAAC,GAEN,IAAU,QAAc;EAC5B,IAAM,IAAS,GAAQ,YAAY;EACnC,IAAI,GAAQ;GACV,IAAM,IAAM,EAAS,MAAM,MAAM,EAAE,SAAS,CAAM;GAClD,IAAI,GAAK,OAAO;EAClB;EAIA,OADI,EAAS,WAAW,IAAU,OAC3B,EAAS,QAAQ,GAAM,OAC3B,EAAI,OAAO,YAAY,MAAM,EAAK,OAAO,YAAY,KAAK,IAAM,CACnE;CACF,GAAG,CAAC,GAAU,GAAQ,YAAY,cAAc,CAAC,GAE3C,IAAW,QACT,EAAS,QAAQ,MAAM,EAAE,SAAS,GAAS,IAAI,GACrD,CAAC,GAAU,CAAO,CACpB,GAEM,KAAa,EAAE,SAAM,gBAA+C;EACxE,EAAI;GACF;GACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;GACpC,UAAU,MAAW,EAAc,GAAM,GAAQ,CAAM;EACzD,CAAC;CACH,GAGM,KAAU,GAAW,MAAe;EACxC,IAAM,IAAO;GAAE,GAAG;IAAQ,IAAI;EAAE;EAGhC,AAFA,EAAS,CAAI,GACb,EAAS,EAAI,GACb,IAAS,CAAI;CACf,GAEM,IAAQ,GAAQ,YAAY,SAAS;CAE3C,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;EACV;CACD,CAAA,GACD,kBAAC,SAAD;EACE,MAAK;EACL,cAAW;EACX,YAAY,MAAM;GAGhB,AAAI,EAAE,QAAQ,YAAU,EAAQ;EAClC;EACA,OAAO;GACL,UAAU;GACV,KAAK;GACL,OAAO;GACP,QAAQ;GACR;GACA,YAAY;GACZ,YAAY;GACZ,QAAQ;GACR,SAAS;GACT,eAAe;GACf,WAAW;EACb;YApBF;GAsBE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,SAAS;KACT,cAAc;KACd,KAAK;IACP;cAPF;KASE,kBAAC,GAAD;MAAQ,MAAM,KAAc;MAAO,MAAM;KAAK,CAAA;KAC9C,kBAAC,OAAD;MAAK,OAAO;OAAE,MAAM;OAAG,UAAU;MAAE;gBAAnC,CACE,kBAAC,OAAD;OACE,OAAO;QACL,UAAU;QACV,YAAY;QACZ,OAAO;QACP,UAAU;QACV,cAAc;QACd,YAAY;OACd;iBAEC;MACE,CAAA,GACJ,KACC,kBAAC,OAAD;OAAK,WAAU;OAAO,OAAO;QAAE,UAAU;QAAI,OAAO;OAAgB;iBACjE;MACE,CAAA,CAEJ;;KACL,kBAAC,GAAD;MACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;MAChC,OAAO,IAAQ,wCAAwC;MACvD,eAAe,EAAO,IAAQ,IAAQ,IAAI;KAC3C,CAAA;KACD,kBAAC,GAAD;MAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;MAAG,SAAS;KAAU,CAAA;IACrD;;GACL,kBAAC,OAAD;IACE,OAAO;KACL,MAAM;KACN,WAAW;KACX,SAAS;KACT,SAAS;KACT,eAAe;KACf,KAAK;IACP;cARF,CAUE,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,cAAc;MACd,UAAU;MACV,OAAO;MACP,SAAS;MACT,YAAY;MACZ,KAAK;KACP;eAVF;MAYE,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;MAAC;MAC1B,kBAAC,IAAD,EAAA,UAAK,MAAQ,CAAA;MAAC;MAAU,kBAAC,IAAD,EAAA,UAAK,MAAQ,CAAA;MAAC;KACnC;QACJ,EAAO,KAAK,MACX,kBAAC,IAAD;KAAqB,OAAO,EAAE;eAC3B,EAAE,SAAS,SACR,GAAkB;MAChB;MACA;MACA,WAAW,EAAE;MACb,QAAQ;MACR,MAAM,EAAE,WAAW,YAAY;MAC/B,WAAW,MAAU,EAAO,EAAE,KAAK,CAAK;MACxC;MACA,UAAU,EAAE;MACZ,QAAQ;KACV,CAAC,IAEC,kBAAC,IAAD;MACE,OAAO,EAAE;MACT,WAAW,EAAE;MACb,OAAO,EAAE;MACT,OAAO,EAAM,EAAE;MACf,QAAQ;MACF;MACN,UAAU,EAAE;MACZ,WAAW,MAAM,EAAO,EAAE,KAAK,CAAC;KACjC,CAAA;IAEA,GAzBK,EAAE,GAyBP,CACV,CACE;;GACL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,SAAS;KACT,WAAW;KACX,KAAK;KACL,YAAY;IACd;cAPF;KASG,KACC,kBAAC,QAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO;OACP,SAAS;OACT,YAAY;OACZ,KAAK;MACP;gBAPF,CASE,kBAAC,QAAD,EACE,OAAO;OACL,OAAO;OACP,QAAQ;OACR,cAAc;OACd,YAAY;MACd,EACD,CAAA,GAAC,SAEE;;KAER,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KACzB,EAAS,SAAS,KACjB,kBAAC,OAAD;MAAK,OAAO,EAAE,UAAU,WAAW;gBAAnC,CACE,kBAAC,GAAD;OACE,KAAK;OACL,MAAK;OACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAC5B,WAAW,kBAAC,EAAK,MAAN;QAAW,MAAM;QAAI,OAAO,EAAE,SAAS,GAAI;OAAI,CAAA;OAC1D,eAAe,GAAgB,MAAS,CAAC,CAAI;iBAC9C;MAEI,CAAA,GACL,kBAAC,IAAD;OACE,MAAM;OACN,QAAQ,EAAc;OACtB,eAAe,EAAe,EAAK;OACnC,OAAO;OACP,OAAM;iBAEL,EAAS,KAAK,EAAE,SAAM,gBACrB,kBAAC,GAAD;QAEE,MAAM,GAAQ,CAAM;QACpB,QAAQ,EAAO;QACf,eAAe;SAEb,AADA,EAAe,EAAK,GACpB,EAAU;UAAE;UAAM;SAAO,CAAC;QAC5B;kBAEC,EAAI,EAAO,SAAS,IAAI,KAAK;OACtB,GATH,CASG,CACX;MACO,CAAA,CACP;;KAEN,KACC,kBAAC,GAAD;MACE,MAAK;MACL,MAAM,GAAQ,EAAQ,QAAQ,EAAE;MAChC,eAAe,EAAU,CAAO;gBAE/B,EAAI,EAAQ,OAAO,SAAS,IAAI,KAAK,EAAQ;KAC3C,CAAA;KAEP,kBAAC,GAAD;MACE,MAAK;MACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAC5B,eAAe,EAAO,IAAQ,IAAQ,IAAI;gBAC3C;KAEI,CAAA;IACF;;EACA;GACP,EAAA,CAAA;AAEN;AAOA,SAAgB,GAAQ,EAAE,UAAO,eAA0B;CAQzD,OACE,kBAAC,OAAD;EAAY,OAAA;GAPZ,SAAS;GACT,qBAAqB;GACrB,YAAY;GACZ,SAAS;GACT,cAAc;EAGF;YAAZ,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,YAAY;GAAI;aAAI;EAAW,CAAA,GACrF,kBAAC,OAAD,EAAM,YAAc,CAAA,CACjB;;AAET;;;AC3XA,IAAM,qBAAQ,IAAI,IAAqC;AAEvD,SAAgB,GAAa,GAAY,GAAsC;CAC7E,GAAM,IAAI,GAAI,CAAK;AACrB;AAEA,SAAgB,GAAc,GAA4C;CACxE,IAAM,IAAI,GAAM,IAAI,CAAE;CAGtB,OAFI,MAAM,KAAA,IAAkB,QAC5B,GAAM,OAAO,CAAE,GACR;AACT;AAKA,SAAgB,GAAe,GAAkB;CAC/C,GAAM,OAAO,CAAE;AACjB;;;ACEA,IAAM,KAAkD;CACtD,aAAa;CACb,YAAY;AACd;AAYA,SAAgB,GAAU,GAAyC;CACjE,IAAI,CAAC,GAAG,OAAO,CAAC;CAChB,IAAM,IAAiB,CAAC;CACxB,KAAK,IAAM,KAAO,EAAE,MAAM,GAAG,GAAG;EAC9B,IAAM,IAAQ,EAAI,KAAK;EACvB,IAAI,CAAC,GAAO;EACZ,IAAM,IAAO,EAAM,MAAM,GAAG,GACtB,IAAQ,EAAK,IAAI,KAAK;EAQ5B,IAPI,CAAC,KAOD,EAAM,WAAW,YAAY,GAAG;EAGpC,IAAM,IAAe;GAAE;GAAO,KAFf,EAAK,IAAI,KAAK,EAAE,YAAY,MACX,SAAS,SAAS;EAChB,GAC5B,IAAW,EAAK,IAAI,KAAK,EAAE,YAAY;EAC7C,IAAI,GAAU;GACZ,IAAM,IAAS,GAAqB;GACpC,AAAI,MAAQ,EAAI,QAAQ;EAI1B;EACA,EAAI,KAAK,CAAG;CACd;CACA,OAAO;AACT;AAUA,SAAgB,GAA6B,GAAkD;CAC7F,IAAI,CAAC,GAAG;CACR,IAAM,IAAiB,CAAC;CACxB,KAAK,IAAM,KAAO,EAAE,MAAM,GAAG,GAAG;EAC9B,IAAM,IAAQ,EAAI,KAAK;EACvB,IAAI,CAAC,GAAO;EACZ,IAAM,IAAQ,EAAM,MAAM,GAAG,EAAE,IAAI,KAAK;EACpC,KAAS,EAAM,WAAW,YAAY,KAI1C,EAAK,KAAK,CAAK;CACjB;CACA,OAAO,EAAK,SAAS,IAAI,EAAK,KAAK,GAAG,IAAI,KAAA;AAC5C;AAMA,SAAgB,GAAc,GAAyB;CACrD,OAAO,EACJ,KAAK,MAAM;EACV,IAAM,IAAO,GAAG,EAAE,MAAM,GAAG,EAAE;EAG7B,OAFI,EAAE,UAAU,UAAgB,GAAG,EAAK,gBACpC,EAAE,UAAU,SAAe,GAAG,EAAK,eAChC;CACT,CAAC,EACA,KAAK,GAAG;AACb;AAyBA,SAAgB,GAAoB,GAAiB,GAA0B;CAC7E,IAAM,IAAc,EAAK,WAAW,MAAM,EAAE,UAAU,CAAK;CAK3D,OAJI,KAAe,IACV,CAAC,GAAG,EAAK,MAAM,GAAG,CAAW,GAAG,GAAG,EAAK,MAAM,IAAc,CAAC,CAAC,IAEnE,EAAK,UAAA,IAAgC,IAClC,CAAC,GAAG,GAAM;EAAE;EAAO,KAAK;CAAM,CAAC;AACxC;AAaA,SAAgB,GAAgB,GAAiB,GAAyB;CACxE,IAAM,IAAU,EAAK,QAAQ,MAAM,EAAE,UAAU,EAAI,KAAK;CAExD,OADI,EAAQ,UAAA,IAAgC,IACrC,CAAC,GAAG,GAAS,CAAG;AACzB;AAMA,SAAgB,GAAgB,GAAiB,GAA0B;CACzE,IAAM,IAAM,EAAK,WAAW,MAAM,EAAE,UAAU,CAAK;CAEnD,OADI,IAAM,IAAU,IACb,CAAC,GAAG,EAAK,MAAM,GAAG,CAAG,GAAG,GAAG,EAAK,MAAM,IAAM,CAAC,CAAC;AACvD;;;AC5JA,IAAM,KAA+D;CACnE;EAAE,OAAO;EAAe,QAAQ;CAAI;CACpC;EAAE,OAAO;EAAW,QAAQ;CAAI;CAChC;EAAE,OAAO;EAAO,QAAQ;CAAI;AAC9B;AAgBA,SAAgB,GAAqB,GAAuB;CAC1D,IAAI,CAAC,OAAO,SAAS,CAAK,KAAK,IAAQ,GAAG,OAAO;CACjD,IAAM,IAAI,KAAK,MAAM,CAAK;CAC1B,IAAI,IAAI,KAAM,OAAO,IAAI;CAEzB,KAAK,IAAM,EAAE,UAAO,eAAY,IAAY;EAC1C,IAAI,IAAI,GAAO;EACf,IAAM,IAAS,IAAI;EAKnB,OAAO,KAFM,IAAS,KAAK,EAAO,QAAQ,CAAC,IAAI,KAAK,MAAM,CAAM,EAAE,SAAS,GAE3D,QAAQ,QAAQ,EAAE,IAAI;CACxC;CAEA,OAAO,IAAI,EAAE,eAAe;AAC9B;AAMA,SAAgB,GAAiB,GAAuB;CAEtD,OADI,CAAC,OAAO,SAAS,CAAK,KAAK,IAAQ,IAAU,MAC1C,KAAK,MAAM,CAAK,EAAE,eAAe;AAC1C;AAQA,SAAgB,GAAY,GAAe,GAAqC;CAC9E,OAAO,MAAS,cAAc,GAAqB,CAAK,IAAI,GAAiB,CAAK;AACpF;;;ACpDA,SAAgB,GAAY,EAC1B,YACA,aACA,eACA,gBACA,iBAAc,MAAM,KACD;CA0DnB,OAzDI,EAAQ,WAAW,IAAU,OAEhB,GAAY,CAEzB,IAEA,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,UAAU;GAAQ,eAAe;EAAG;YAAjG,CACG,EAAQ,KAAK,GAAM,MACd,EAAK,KAEL,kBAAC,OAAD;GAAa,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAE;aACjE,EAAK,GAAG,KAAK,GAAO,MACnB,kBAAC,QAAD;IAAe,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAE;cAAtE,CACG,IAAK,KACJ,kBAAC,QAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO;MACP,YAAY;MACZ,SAAS;KACX;eACD;IAEK,CAAA,GAER,kBAAC,GAAD;KAAM,MAAK;KAAQ,SAAS;eACzB,EACE,QAAQ,MAAM,EAAE,SAAS,EAAE,EAAE,EAC7B,KAAK,MAAM,GAAG,EAAW,EAAE,KAAM,EAAE,GAAG,GAAY,CAAC,GAAG,EACtD,KAAK,OAAO;IACX,CAAA,CACF;MAnBK,CAmBL,CACP;EACE,GAvBK,CAuBL,IAGL,EAAK,SAAS,EAAK,KAEnB,kBAAC,IAAD;GAEQ;GACM;GACZ,gBAAgB,EAAS,EAAK,KAAM;GACpC,SAAS;EACV,GALM,GAAG,EAAK,MAAM,GAAG,GAKvB,IAGE,IACR,GACD,kBAAC,GAAD;GAAK,MAAK;GAAQ,MAAK;GAAK,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,SAAS;aAAY;EAExE,CAAA,CACF;MAMP,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,UAAU;GAAQ,eAAe;EAAG;YAAjG,CAFiB,GAAoB,CAGlC,EAAW,KAAK,GAAM,MACrB,kBAAC,IAAD;GAEQ;GACM;GACZ,gBAAgB,EAAS,EAAK,KAAM;GACpC,SAAS;EACV,GALM,GAAG,EAAK,MAAM,GAAG,GAKvB,CACF,GACD,kBAAC,GAAD;GAAK,MAAK;GAAQ,MAAK;GAAK,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,SAAS;aAAY;EAExE,CAAA,CACF;;AAET;AAEA,SAAS,GAAW,EAClB,SACA,eACA,aACA,cAMC;CAED,OACE,kBAAC,GAAD;EAAM,MAAK;EAAc,MAFH,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAErB;EAAe;EAAmB;YACzD,kBAAC,QAAD,EAAA,UAAA;GACE,kBAAC,QAAD;IAAM,OAAO,EAAE,OAAO,gBAAgB;cAAI,EAAW,EAAK,KAAM;GAAQ,CAAA;GAAE;GAC1E,kBAAC,QAAD;IAAM,OAAO,EAAE,YAAY,IAAI;cAAI,GAAY,CAAI;GAAQ,CAAA;EACvD,EAAA,CAAA;CACF,CAAA;AAEV;AAEA,SAAS,GAAY,GAA0B;CAC7C,IAAI,CAAC,EAAK,IAAI,OAAO;CACrB,IAAM,IAAQ,GAAU,EAAK,OAAO,EAAK;CAGzC,OAFI,GAAa,IAAI,EAAK,EAAE,IAAU,IAE/B,GAAG,EAAM,GADJ,MAAM,QAAQ,EAAK,KAAK,IAAI,EAAK,MAAM,KAAK,IAAI,IAAI,OAAO,EAAK,SAAS,EAAE;AAEzF;;;AC3FA,SAAgB,GAAa,EAC3B,SACA,YACA,YACA,YACA,sBACoB;CACpB,IAAM,CAAC,GAAO,KAAY,EAAuB,CAAO,GAClD,CAAC,GAAiB,KAAsB,EAAS,EAAK;CAM5D,IAJA,QAAgB;EACd,AAAI,KAAM,EAAS,CAAO;CAC5B,GAAG,CAAC,GAAM,CAAO,CAAC,GAEd,CAAC,GAAM,OAAO;CAElB,IAAM,IAAW,GAAY,CAAK,GAC5B,IAAe,IAAI,IAAI,EAAM,QAAQ,MAAM,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,KAAM,CAAC,GAExE,KAAgB,MAAsB;EAC1C,IAAM,IAAO,EAAgB,MAAM,MAAM,EAAE,SAAS,CAAS;EAC7D,IAAI,CAAC,GAAM;EACX,IAAM,IAAY,EAAK,UAAU,MAAM;EAEvC,AADA,EAAS,CAAC,GAAG,GAAO;GAAE,OAAO;GAAW,IAAI;EAAU,CAAC,CAAC,GACxD,EAAmB,EAAK;CAC1B,GAEM,KAAmB,GAAe,MAAiC;EACvE,EAAS,EAAM,KAAK,GAAG,MAAO,MAAM,IAAQ;GAAE,GAAG;GAAG,GAAG;EAAQ,IAAI,CAAE,CAAC;CACxE,GAEM,KAAmB,MAAkB;EACzC,EAAS,EAAM,QAAQ,GAAG,MAAM,MAAM,CAAK,CAAC;CAC9C;CA+BA,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;EACV;CACD,CAAA,GACD,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,cAAW;EACX,eAAY;EACZ,OAAO;GACL,UAAU;GACV,KAAK;GACL,OAAO;GACP,QAAQ;GACR,OAAO;GACP,UAAU;GACV,YAAY;GACZ,YAAY;GACZ,WAAW;GACX,QAAQ;GACR,SAAS;GACT,eAAe;EACjB;YAlBF;GAoBE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,SAAS;KACT,cAAc;IAChB;cAPF,CASE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;KAAE;eAA5D,CACE,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA,GACxB,kBAAC,MAAD;MAAI,OAAO;OAAE,QAAQ;OAAG,UAAU;OAAI,YAAY;OAAK,OAAO;MAAc;gBAAG;KAAW,CAAA,CACvF;QACL,kBAAC,GAAD;KAAS,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;KAAG,SAAS;KAAS,OAAM;IAAS,CAAA,CACnE;;GAEL,kBAAC,OAAD;IACE,OAAO;KACL,MAAM;KACN,WAAW;KACX,SAAS;KACT,SAAS;KACT,eAAe;KACf,KAAK;IACP;cARF,CAUI,IAeE,EAAM,KAAK,GAAM,MAAM;KACrB,IAAI,CAAC,EAAK,IAAI;MACZ,IAAI,CAAC,EAAK,SAAS,CAAC,EAAK,IAAI,OAAO;MACpC,IAAM,IAAO,EAAgB,MAAM,MAAM,EAAE,SAAS,EAAK,KAAK;MAE9D,OADK,IAEH,kBAAC,IAAD;OAEQ;OACA;OACN,WAAW,MAAY,EAAgB,GAAG,CAAO;OACjD,gBAAgB,EAAgB,CAAC;MAClC,GALM,GAAG,EAAK,MAAM,GAAG,GAKvB,IARe;KAUpB;KACA,OACE,kBAAC,IAAD;MAEE,QAAQ,EAAK;MACI;MACjB,WAAW,MAAc;OACvB,IAAM,IAAW,CAAC,GAAG,CAAK;OAE1B,AADA,EAAS,KAAK,EAAE,IAAI,EAAU,GAC9B,EAAS,CAAQ;MACnB;KACD,GARM,MAAM,GAQZ;IAEL,CAAC,IAzCD,EAAM,KAAK,GAAM,MAAM;KACrB,IAAI,CAAC,EAAK,SAAS,CAAC,EAAK,IAAI,OAAO;KACpC,IAAM,IAAO,EAAgB,MAAM,MAAM,EAAE,SAAS,EAAK,KAAK;KAE9D,OADK,IAEH,kBAAC,IAAD;MAEQ;MACA;MACN,WAAW,MAAY,EAAgB,GAAG,CAAO;MACjD,gBAAgB,EAAgB,CAAC;KAClC,GALM,GAAG,EAAK,MAAM,GAAG,GAKvB,IARe;IAUpB,CAAC,GA8BL,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAG,YAAY;KAAE;eACxE,IACC,kBAAC,IAAD;MACE,QAAQ;MACM;MACd,UAAU;MACV,gBAAgB,EAAmB,EAAK;KACzC,CAAA,IAED,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;MAAK,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAAG,eAAe,EAAmB,EAAI;gBAAG;KAExE,CAAA,GACJ,CAAC,KAAY,EAAM,MAAM,MAAM,EAAE,KAAK,KACrC,kBAAC,GAAD;MAAK,MAAK;MAAQ,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAAG,eAhJxC;OACvB,IAAM,IAAoB,EAAM,QAAQ,MAAM,EAAE,SAAS,EAAE,EAAE;OAE7D,EAAS,CACP,GAFiB,EAAM,QAAQ,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,EAEjD,GACH,EACE,IAAI,CAAC,EAAkB,SAAS,IAAI,IAAoB,CAAC,GAAG,CAAC,CAAC,EAChE,CACF,CAAC;MACH;gBAuIsF;KAEjE,CAAA,CAEP,EAAA,CAAA;IAED,CAAA,CACF;;GAEL,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,SAAS;KACT,WAAW;KACX,YAAY;KACZ,KAAK;IACP;cARF;KAUE,kBAAC,GAAD;MAAK,MAAK;MAAQ,eA1IA;OACxB,EAAS,CAAC,CAAC;MACb;gBAwIgD;KAEnC,CAAA;KACL,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;KAC1B,kBAAC,GAAD;MAAK,SAAS;gBAAS;KAAW,CAAA;KAClC,kBAAC,GAAD;MAAK,MAAK;MAAU,eA7JF;OAWxB,AADA,EATgB,EACb,KAAK,MAAM;QACV,IAAI,EAAE,IAAI;SACR,IAAM,IAAa,EAAE,GAAG,QAAQ,MAAM,EAAE,SAAS,CAAC;SAClD,OAAO,EAAW,SAAS,IAAI;UAAE,GAAG;UAAG,IAAI;SAAW,IAAI;QAC5D;QACA,OAAO,EAAE,SAAS,EAAE,KAAK,IAAI;OAC/B,CAAC,EACA,QAAQ,MAAuB,MAAM,IAChC,CAAO,GACf,EAAQ;MACV;gBAiJkD;KAErC,CAAA;IACF;;EACF;GACL,EAAA,CAAA;AAEN;AAEA,SAAS,GAAU,EACjB,SACA,SACA,aACA,eAMC;CACD,IAAM,IAAY,GAAa,IAAI,EAAK,EAAc,GAChD,IAAY,EAAK,UAAU,KAAK,OAAQ;EAAE,OAAO;EAAI,OAAO,GAAU,MAAO;CAAG,EAAE;CAExF,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,qBAAqB;GACrB,KAAK;GACL,YAAY;EACd;YANF;GAQE,kBAAC,QAAD;IACE,OAAO;KACL,UAAU;KACV,YAAY;KACZ,OAAO;KACP,UAAU;KACV,cAAc;KACd,YAAY;IACd;IACA,OAAO,EAAK;cAEX,EAAK;GACF,CAAA;GACN,kBAAC,IAAD;IACE,cAAY,GAAG,EAAK,MAAM;IAC1B,OAAO,EAAK,MAAM,EAAU,IAAI;IAChC,SAAS;IACT,WAAW,MAAO,EAAS,EAAM,MAAe,CAAC;GAClD,CAAA;GACA,IACC,kBAAC,QAAD,CAAO,CAAA,IACL,EAAK,UACP,kBAAC,IAAD;IACE,cAAY,GAAG,EAAK,MAAM;IAC1B,OAAO,OAAO,EAAK,SAAS,EAAE;IAC9B,SAAS,EAAK,QAAQ,KAAK,OAAO;KAAE,OAAO,EAAE;KAAO,OAAO,EAAE;IAAM,EAAE;IACrE,WAAW,MAAM,EAAS,EAAE,OAAO,EAAE,CAAC;IACtC,aAAY;GACb,CAAA,IAED,kBAAC,GAAD;IACE,cAAY,GAAG,EAAK,MAAM;IAC1B,OAAO,OAAO,EAAK,SAAS,EAAE;IAC9B,WAAW,MAAM,EAAS,EAAE,OAAO,EAAE,CAAC;IACtC,aAAY;GACb,CAAA;GAEH,kBAAC,GAAD;IAAS,MAAM;IAAI,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IAAG,SAAS;IAAU,OAAM;GAAU,CAAA;EAC/E;;AAET;AAEA,SAAS,GAAc,EACrB,WACA,oBACA,eAKC;CACD,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;YAA/D,CACG,EAAO,KAAK,GAAO,MAClB,kBAAC,OAAD,EAAA,UAAA,CACG,IAAK,KACJ,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAG,SAAS;GAAQ;aAA9E;IACE,kBAAC,OAAD,EAAK,OAAO;KAAE,MAAM;KAAG,WAAW;IAA2B,EAAI,CAAA;IACjE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,YAAY;MAAK,OAAO;MAAiB,SAAS;KAAQ;eAAG;IAAQ,CAAA;IAClG,kBAAC,OAAD,EAAK,OAAO;KAAE,MAAM;KAAG,WAAW;IAA2B,EAAI,CAAA;GAC9D;MAEP,kBAAC,OAAD;GAAK,OAAO;IAAE,QAAQ;IAA2B,cAAc;IAAG,SAAS;IAAI,SAAS;IAAQ,eAAe;IAAU,KAAK;GAAE;aAAhI;IACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAM,YAAY;MAAK,OAAO;MAAiB,eAAe;MAAa,eAAe;KAAS;eAAG;IAEzH,CAAA;IACJ,EAAM,KAAK,GAAM,MAAO;KACvB,IAAM,IAAO,EAAgB,MAAM,MAAM,EAAE,SAAS,EAAK,KAAK;KAE9D,OADK,IAEH,kBAAC,IAAD;MAEQ;MACA;MACN,WAAW,MAAY;OAIrB,EAHkB,EAAO,KAAK,GAAG,MAC/B,MAAM,IAAK,EAAE,KAAK,GAAG,MAAO,MAAM,IAAK;QAAE,GAAG;QAAG,GAAG;OAAQ,IAAI,CAAE,IAAI,CAE7D,CAAS;MACpB;MACA,gBAAgB;OAId,EAHkB,EAAO,KAAK,GAAG,MAC/B,MAAM,IAAK,EAAE,QAAQ,GAAG,MAAM,MAAM,CAAE,IAAI,CAEnC,EAAU,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;MAChD;KACD,GAfM,GAAG,EAAK,MAAM,GAAG,GAevB,IAlBe;IAoBpB,CAAC;IACD,kBAAC,GAAD;KACE,MAAK;KACL,MAAK;KACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAC5B,eAAe;MAGb,IAAM,IAAc,IAAI,IAAI,EAAM,KAAK,MAAM,EAAE,KAAK,CAAC,GAC/C,IAAiB,EAAgB,MACpC,MAAM,CAAC,EAAY,IAAI,EAAE,IAAI,CAChC;MACA,AAAI,KAYF,EAXkB,EAAO,KAAK,GAAG,MAC/B,MAAM,IACF,CACE,GAAG,GACH;OACE,OAAO,EAAe;OACtB,IAAI,EAAe,UAAU,MAAO;MACtC,CACF,IACA,CAEG,CAAS;KAEtB;eACD;IAEI,CAAA;GACF;IACF,EAAA,GAjEK,CAiEL,CACN,GACD,kBAAC,GAAD;GACE,MAAK;GACL,MAAK;GACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GAC5B,eAAe,EAAS,CAAC,GAAG,GAAQ,CAAC,CAAC,CAAC;aACxC;EAEI,CAAA,CACF;;AAET;AAEA,SAAS,GAAY,EACnB,WACA,iBACA,aACA,eAMC;CACD,IAAM,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,IAAW,EAAO,QACrB,MACC,CAAC,EAAa,IAAI,EAAE,IAAI,MACvB,MAAW,MACV,EAAE,MAAM,YAAY,EAAE,SAAS,EAAO,YAAY,CAAC,KACnD,EAAE,KAAK,YAAY,EAAE,SAAS,EAAO,YAAY,CAAC,EACxD;CAEA,OACE,kBAAC,OAAD;EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,cAAc;GACd,SAAS;GACT,SAAS;GACT,eAAe;GACf,KAAK;EACP;YATF;GAWE,kBAAC,GAAD;IACE,OAAO;IACP,UAAU;IACV,aAAY;IACZ,QAAQ,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;IAChC,WAAA;GACD,CAAA;GACD,kBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAK,UAAU;KAAQ,SAAS;KAAQ,eAAe;IAAS;cAAzF,CACG,EAAS,KAAK,MACX,kBAAC,UAAD;KAEE,eAAa,gBAAgB,EAAE;KAC/B,eAAe,EAAS,EAAE,IAAI;KAChC,OAAO;MACL,WAAW;MACX,SAAS;MACT,UAAU;MACV,YAAY;MACZ,QAAQ;MACR,OAAO;MACP,QAAQ;MACR,cAAc;MACd,SAAS;MACT,gBAAgB;MAChB,KAAK;KACP;KACA,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;KACzD,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;eAlBzD,CAoBA,kBAAC,QAAD,EAAA,UAAO,EAAE,MAAY,CAAA,GACrB,kBAAC,QAAD;MAAM,WAAU;MAAO,OAAO;OAAE,OAAO;OAAiB,UAAU;MAAK;gBACpE,EAAE;KACC,CAAA,CACA;OAvBC,EAAE,IAuBH,CACT,GACA,EAAS,WAAW,KACnB,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAI,OAAO;MAAiB,SAAS;KAAU;eAAG;IAErE,CAAA,CAEJ;;GACL,kBAAC,GAAD;IAAK,MAAK;IAAQ,MAAK;IAAK,SAAS;IAAU,OAAO,EAAE,gBAAgB,SAAS;cAAG;GAE/E,CAAA;EACF;;AAET;;;AC7dA,SAAgB,GACd,GACA,GACA,GACmB;CACnB,IAAI,CAAC,KAAU,CAAC,GAAQ,OAAO,CAAC;CAChC,IAAM,IAAe,EAAO,WAAW,EAAO,OAAO,MAAM,GACrD,IAA0B,CAAC;CAEjC,KAAK,IAAM,KAAK,GAAc;EAC5B,IAAI,EAAE,WAAW,UAAU,GAAG;GAC5B,IAAM,IAAY,EAAE,MAAM,CAAC,GACrB,IAAQ,EAAO,gBAAgB;GACrC,IAAI,CAAC,GAAO;GACZ,EAAK,KAAK;IACR,MAAM;IACN,OAAO,EAAI,EAAM,IAAI,KAAK;IAC1B,MAAM;IACN,WAAW;KAAC;KAAM;KAAU;KAAM;IAAQ;IAC1C,SAAS,EAAM,SAAS,KAAK,OAAO;KAClC,OAAO,EAAE;KACT,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;KACxB,OAAO,EAAE;IACX,EAAE;GACJ,CAAC;GACD;EACF;EACA,IAAI,EAAE,WAAW,YAAY,GAAG;GAC9B,IAAM,IAAW,EAAE,MAAM,EAAE,GACrB,IAAO,EAAO,aAAa;GACjC,IAAI,CAAC,GAAM;GACX,EAAK,KAAK;IACR,MAAM;IACN,OAAO,EAAI,EAAK,IAAI,KAAK;IACzB,MAAM;IACN,WAAW;KAAC;KAAM;KAAU;KAAY;KAAY;IAAc;GACpE,CAAC;GACD;EACF;EACA,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAC;EACjC,IAAI,CAAC,GAAO;EACZ,IAAM,IAAQ,GAAiB,GAAO,GAAG,CAAQ,GAC3C,IAAM,GAAqB,EAAM,IAAI,GACrC,IAAU,EAAM,QAAQ,KAAK,OAAO;GACxC,OAAO,EAAE;GACT,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;GACxB,OAAO,EAAE;EACX,EAAE;EACF,EAAK,KAAK;GAAE,MAAM;GAAG;GAAO,MAAM,EAAM;GAAM,WAAW;GAAK;EAAQ,CAAC;CACzE;CAEA,OAAO;AACT;AAGA,SAAgB,GAAqB,GAA0B;CAC7D,QAAQ,GAAR;EACE,KAAK;EAAU,KAAK;EAAQ,KAAK;EAAa,KAAK;EAAS,KAAK;EAAO,KAAK,SAC3E,OAAO;GAAC;GAAM;GAAU;GAAY;GAAe;GAAY;EAAc;EAC/E,KAAK,QACH,OAAO;GAAC;GAAM;GAAU;GAAM;GAAU;GAAY;EAAc;EACpE,KAAK;EAAO,KAAK;EAAS,KAAK,WAC7B,OAAO;GAAC;GAAM;GAAU;GAAM;GAAO;GAAM;GAAO;GAAW;GAAY;EAAc;EACzF,KAAK,QACH,OAAO;GAAC;GAAM;GAAU;GAAM;GAAO;GAAM;GAAO;GAAW;GAAS;GAAa;GAAc;GAAe;GAAgB;GAAY;EAAc;EAC5J,KAAK,aACH,OAAO;GAAC;GAAM;GAAU;GAAM;GAAO;GAAM;GAAO;GAAW;GAAS;GAAa;GAAc;GAAe;GAAgB;GAAa;GAAiB;GAAY;EAAc;EAC1L,KAAK,QACH,OAAO,CAAC,MAAM,UAAU;EAC1B,KAAK;EAAO,KAAK,QACf,OAAO;GAAC;GAAM;GAAU;GAAY;EAAc;EACpD,SACE,OAAO;GAAC;GAAM;GAAU;GAAY;EAAc;CACtD;AACF;;;AC1EA,SAAgB,GAAe,EAC7B,SACA,YACA,YACA,aAMC;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,EAAE;CAMnC,IAJA,QAAgB;EACd,AAAI,KAAM,EAAQ,EAAE;CACtB,GAAG,CAAC,CAAI,CAAC,GAEL,CAAC,GAAM,OAAO;CAElB,IAAM,IAAU,EAAK,KAAK;CAE1B,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;EACE,SAAS;EACT,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;EACV;CACD,CAAA,GACD,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,cAAW;EACX,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,WAAW;GACX,OAAO;GACP,UAAU;GACV,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,WAAW;GACX,QAAQ;GACR,SAAS;GACT,SAAS;GACT,eAAe;GACf,KAAK;EACP;YApBF;GAsBE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAG,UAAU;KAAI,OAAO;IAAc;cAAG;GAAa,CAAA,GAC3E,kBAAC,KAAD;IAAG,OAAO;KAAE,QAAQ;KAAW,UAAU;KAAI,OAAO;IAAgB;cAAG;GAEpE,CAAA,CACA,EAAA,CAAA;GACL,kBAAC,SAAD;IAAO,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAE;cAAjE,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAM,OAAO;MAAiB,YAAY;KAAI;eAAG;IAAU,CAAA,GACpF,kBAAC,GAAD;KACE,WAAA;KACA,cAAW;KACX,OAAO;KACP,UAAU;KACV,aAAY;KACZ,YAAY,MAAU;MAEpB,AADI,EAAM,QAAQ,YAAU,EAAQ,GAChC,EAAM,QAAQ,WAAW,KAAW,CAAC,KACvC,EAAY,CAAO;KAEvB;IACD,CAAA,CACI;;GACP,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,gBAAgB;KAAY,KAAK;IAAE;cAAlE,CACE,kBAAC,GAAD;KAAK,SAAS;KAAS,UAAU;eAAS;IAAW,CAAA,GACrD,kBAAC,GAAD;KAAK,MAAK;KAAU,eAAe,KAAK,EAAO,CAAO;KAAG,UAAU,CAAC,KAAW;eAAS;IAEnF,CAAA,CACF;;EACF;GACL,EAAA,CAAA;AAEN;;;ACjDA,SAAgB,GAAW,EACzB,UACA,UACA,cACA,iBACA,WACA,mBACA,uBACA,uBACA,gBACA,SACA,WACA,oBACA,oBACkB;CAClB,IAAM,EAAE,WAAQ,GAAgB,GAG1B,IAAmB,QAAsC;EAC7D,IAAM,IAA8B,CAAC;EACrC,KAAK,IAAM,EAAE,SAAM,eAAY,GAAgB,EAAI,KAAQ;EAC3D,OAAO;CACT,GAAG,CAAC,CAAc,CAAC,GAQb,IAAc,QAGT,IAFM,KAAsB,MAKnC,GAAmB,GAAkB,cAAc,GAHhB,GAAkB,KAAA,GAAW,cAAc,GAQ/E;EAAC;EAAoB;EAAoB;CAAgB,CAAC,GAQvD,IAAqB,QAEvB,GACE,GACA,GACA,KAAA,GACA,IACA,+BACF,GACF,CAAC,GAAa,CAAgB,CAChC,GAKM,IAAmB,QAErB,GAAoB,GAAa,GAAkB,KAAA,GAAW,gBAAgB,EAC5E,WAAW,GACb,CAAC,GACH,CAAC,GAAa,CAAgB,CAChC,GAEM,IAAgB,EAAY,SAAS,IAAI,IAAmB;CAoClE,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,SAAS;EAAiB;YAAvF,CACE,kBAAC,OAAD;GAAK,OAAO,EAAE,MAAM,EAAE;aACpB,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAY,KAAK;IAAG;cAA/D,CACE,kBAAC,MAAD;KAAI,OAAO;MAAE,QAAQ;MAAG,UAAU;MAAI,YAAY;MAAK,eAAe;MAAY,OAAO;KAAc;eACpG;IACC,CAAA,GAOJ,kBAAC,QAAD;KACE,WAAU;KACV,OAAO;MAAE,UAAU;MAAI,OAAO;MAAiB,SAAS;MAAe,YAAY;MAAU,KAAK;KAAE;KACpG,eAAY;eAHd;MAKG;MAAO;MAAI;MACX,MAAU,KAAA,IAEP,IACF,kBAAC,QAAD;OAAM,eAAY;OAAqB,OAAO,EAAE,SAAS,cAAc;iBACrE,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;MAChB,CAAA,IAEN,IANA,GAAY,GAAO,KAAa,OAAO;KAQrC;MACH;;EACF,CAAA,GAQJ,EAAc,SAAS,KACtB,kBAAC,IAAD;GACE,QAAQ;GACR,MAAK;GACL,eAAe;GACf,QA7EkB,GAAc,MAAmB;IACzD,IAAM,IAAW,GAAe,GAAM,GAAQ,CAAC,CAAC;IAChD,IAAI,MAAa,MAAM;IACvB,IAAM,IAAO,EAAI,EAAO,SAAS,IAAI,KAAK,GAMtC;IACJ,IAAI,EAAY,SAAS,GAAG;KAG1B,IAAM,oBAAW,IAAI,IAAqC;KAC1D,KAAK,IAAM,KAAK,GAAM,EAAS,IAAI,EAAO,CAAC,GAAG,CAAC;KAC/C,IAAe,EACZ,KAAK,MAAO,EAAS,IAAI,CAAE,CAAC,EAC5B,QAAQ,MAAoC,MAAM,KAAA,CAAS;IAChE;IAGA,OAAO,EAAI;KACT;KACA,OAAO;KACP,eAAe;KACf,SAAS,OAAO,MAAW;MACzB,IAAM,IAAM,EAAY,SAAS,IAAI,IAAc,KAAA,GAC7C,IAAS,MAAM,EAAa,GAAU,EAAgB,GAAQ,CAAG,CAAC;MAExE,OADI,KAAO,EAAI,SAAS,KAAG,EAAgB,GACpC;KACT;IACF,CAAC;GACH;GA8CuB;GACf,WAAU;EACX,CAAA,CAEA;;AAET;;;ACnKA,SAAgB,GAAY,EAC1B,gBACA,oBACA,uBACA,uBACA,SACA,WACA,eACA,YACA,oBACA,oBACmB;CACnB,IAAM,EAAE,WAAQ,GAAgB,GAC1B,IAAc,GAAe,GAG7B,IAAmB,QAAsC;EAC7D,IAAM,IAA8B,CAAC;EACrC,KAAK,IAAM,EAAE,SAAM,eAAY,GAAiB,EAAI,KAAQ;EAC5D,OAAO;CACT,GAAG,CAAC,CAAe,CAAC,GAiBd,IAAa,QAGR,IAFM,KAAsB,MAKnC,GAAmB,GAAkB,eAAe,GAHjB,GAAkB,KAAA,GAAW,eAAe,GAQhF;EAAC;EAAoB;EAAoB;CAAgB,CAAC,GAMvD,IAAe,QAAc;EACjC,IAAI,EAAY,WAAW,GAAG,OAAO,CAAC;EACtC,IAAM,oBAAW,IAAI,IAAqC;EAC1D,KAAK,IAAM,KAAK,GAAM,EAAS,IAAI,EAAO,CAAC,GAAG,CAAC;EAC/C,OAAO,EACJ,KAAK,MAAO,EAAS,IAAI,CAAE,CAAC,EAC5B,QAAQ,MAAoC,MAAM,KAAA,CAAS;CAChE,GAAG;EAAC;EAAa;EAAM;CAAM,CAAC,GAExB,IAAsB,QACpB,GAAc,GAAY,GAAkB,KAAA,GAAW,CAAC,GAAG,CAAY,GAC7E;EAAC;EAAY;EAAkB;CAAY,CAC7C;CAqCA,OAJI,EAAoB,WAAW,IAC1B,OAIP,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,MAAM;GACN,QAAQ;GACR,WAAW;GACX,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,YAAY;GACZ,OAAO;GACP,cAAc;GACd,WAAW;GACX,WAAW;EACb;YAhBF;GAkBE,kBAAC,SAAD,EAAA,UAAQ,yIAAgJ,CAAA;GACxJ,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAM,YAAY;IAAI;cAA/C,CACE,kBAAC,QAAD;KACE,OAAO;MACL,SAAS;MACT,cAAc;MACd,YAAY;MACZ,OAAO;MACP,aAAa;MACb,UAAU;KACZ;eAEC,EAAY;IACT,CAAA,GAAC,UAEH;;GACN,kBAAC,IAAD;IACE,QAAQ;IACR,MAAK;IACL,eAAe;IACf,cAAc;IACd,QA3EiB,GAAc,MAAmB;KAItD,IAAM,IAAW,GAAe,GAAM,GAAQ,CAAC,CAAC;KAC5C,UAAa,MAGjB,OAAO,EAAI;MACT;MACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;MACpC,eAAe;MACf,SAAS,OAAO,MAAW;OACzB,IAAM,IAAS,MAAM,EAAa,GAAU,EAAgB,GAAQ,CAAW,CAAC;OAShF,OARA,EAAgB,GACZ,MACF,EAAiB,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAU,EAAE,CAAC,GAG5E,EAAiB,kBAAkB,EAAE,UAAU,CAAC,gBAAgB,CAAU,EAAE,CAAC,GAC7E,EAAiB,kBAAkB,EAAE,UAAU,CAAC,eAAe,CAAU,EAAE,CAAC,IAEvE;MACT;KACF,CAAC;IACH;IAmDqB;IACf,WAAU;GACX,CAAA;GACD,kBAAC,UAAD;IACE,SAAS;IACT,OAAO;KACL,YAAY;KACZ,QAAQ;KACR,OAAO;KACP,QAAQ;KACR,SAAS;KACT,SAAS;KACT,cAAc;IAChB;IACA,OAAM;cAEN,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GACb,CAAA;EACL;;AAET;;;ACxLA,SAAgB,GAAS,EACvB,SACA,oBAIC;CACD,IAAM,IAAW,EAAK,OAAO,OAAO,WAC9B,IAAY,EAAK,OAAO,OAAO,YAC/B,IAAU,EAAK,OAAO,UAAU,MAChC,IACJ,OAAO,KAAY,aAAa,EAAQ,EAAK,WAAW,CAAC,IAAI;CAE/D,OACE,kBAAC,MAAD;EACS,OAHG,OAAO,KAAQ,WAAW,IAAM,KAAA;EAI1C,OAAO;GACL,SAAS,IAAW,iBAAiB,IAAY,UAAU;GAC3D,eAAe;GACf,cAAc;GACd,OAAO,EAAK,OAAO,QAAQ;GAC3B,YAAY;GACZ,UAAU;GACV,cAAc;GACd,UAAU,EAAK,OAAO,QAAQ;GAC9B,OAAO;GAKP,YAAY,IACR,IACE,2BACA,cACF;GACJ,UAAU,IAAW,WAAW;GAChC,MAAM,IAAW,IAAI;GACrB,QAAQ;EACV;EACA,SACE,KAAY,KAAa,MAAM,EAAE,gBAAgB,IAAI,KAAA;YAGtD;CACC,CAAA;AAER;;;AC7CA,SAAgB,GAAgB,GAA4B,GAA4B;CACtF,IAAM,IAAS,GAAQ,eAAe;CAEtC,OADK,IACE,EAAO,SAAS,YADH;AAEtB;AAQA,SAAgB,GAAgB,GAA4B,GAA4B;CACtF,IAAM,IAAS,GAAQ,eAAe;CAEtC,OADK,IACE,EAAO,UAAU,YADJ;AAEtB;AAQA,SAAgB,GAAoB,GAA4B,GAAuC;CACrG,OAAO,GAAQ,eAAe,IAAY;AAC5C;AAQA,SAAgB,GAAwB,GAA4B,GAAuC;CACzG,OAAO,GAAQ,eAAe,IAAY;AAC5C;AASA,SAAgB,GACd,GACA,GACA,GACmC;CACnC,IAAM,IAAW,GAAoB,GAAQ,CAAS;CACjD,OACL,OAAO,GAAQ,kBAAkB;AACnC;AASA,SAAgB,GACd,GACA,GACA,GACS;CAET,OAAO,CAAC,CADK,GAAmB,GAAQ,GAAQ,CACvC,GAAM;AACjB;AAQA,SAAgB,GAAgB,GAA4B,GAAgD;CAC1G,OAAO,GAAQ,eAAe;AAChC;;;ACpDA,SAAgB,GAAe,EAC7B,QACA,UACA,WACA,eACA,WACA,eACA,yBACA,2BACA,eACA,gBACA,WACA,aACsB;CACtB,IAAM,EAAE,WAAQ,GAAgB,GAO1B,IAAgB,SACb;EACL;EACA,YAAY,KAAc;EAC1B,QAAQ;EACR,aAAa,EAAW;EACxB,cAAc,CAAC;EACf,aAAa,CAAC;EACd;EACA,cAAc,GAAK,MAAS,EAAY,GAAK;GAAE,OAAO,GAAM;GAAO,MAAM,GAAM;GAAM,QAAQ,GAAM;EAAO,CAAC;EAC3G,eAAe,GAAM,MAAW,EAAa,GAAM,CAA4C;CACjG,IACA;EAAC;EAAQ;EAAY;EAAY;EAAQ;CAAW,CACtD,GAIM,IAAsB,QAAsC;EAChE,IAAM,IAA8B,CAAC;EACrC,KAAK,IAAM,EAAE,SAAM,eAAY,GAAY,EAAI,KAAQ;EACvD,OAAO;CACT,GAAG,CAAC,CAAU,CAAC,GAaT,IAAY,QAA0C;EAE1D,IAAM,IADmB,EAAkD,iBACS,MAC9E,IAAgB,EAAO,iBAAuE;EAKpG,OAFS,IAFM,KAAgB,MAK7B,GAAmB,GAAqB,UAAU,GAHf,GAAqB,KAAA,GAAW,UAAU;CAQjF,GAAG;EAAC;EAAQ;EAAQ;CAAmB,CAAC,GAGlC,IAAqB,QACnB,GAAc,GAAW,GAAqB,KAAA,GAAW,GAAK,CAAC,CAAC,GACtE;EAAC;EAAW;EAAqB;CAAG,CACtC,GAGM,IAAe,GAClB,GAAc,MAAmB;EAChC,IAAM,IAAW,GAAe,GAAM,GAAQ,EAAE,OAAI,CAAC;EACjD,UAAa,MAGjB,OAAO,EAAI;GACT;GACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;GACpC,UAAU,MAAW,EAAa,GAAU,EAAgB,GAAQ,CAAC,CAAK,CAAC,CAAC;EAC9E,CAAC;CACH,GACA;EAAC;EAAK;EAAO;CAAG,CAClB,GAEM,IAAsB,KAAwB;CAEpD,OACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,gBAAgB;EAAW;YAFrF,CAIG,KACC,kBAAC,GAAD;GACE,MAAM;GACN,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;GAC3B,UAAU,MAAM;IAEd,AADA,EAAE,gBAAgB,GAClB,EAAO;GACT;GACA,OAAM;EACP,CAAA,IASD,EAAmB,SAAS,KAAK,MACjC,kBAAC,IAAD;GACE,QAAQ;GACH;GACL,MAAK;GACL,eAAe;GACf,OAAO;GACQ;GACf,WAAU;EACX,CAAA,CAEA;;AAET;;;AChKA,SAAS,GAAuB,GAAe,GAAgB,GAA0C;CACvG,IAAM,IAAS,GAAgB,GAAQ,CAAS;CAChD,IAAI,CAAC,KAAU,EAAO,SAAS,UAAU,OAAO;CAChD,IAAM,IAAQ,EAAO,sBACjB,4BAA4B,EAAO,oBAAoB,KACvD,kBAAkB,EAAO,eAAe;CAC5C,OAAO,GACL,QACA;EACE;EACA,cAAc;EACd,WAAW;CACb,GACA,GAAc,EAAK,MAAM,EAAE,MAAM,GAAG,CAAC,GACrC,CACF;AACF;AAmDA,SAAgB,GAAiB,GAAkD;CACjF,IAAM,EACJ,gBACA,WACA,WACA,WACA,eACA,eACA,oBACA,0BACA,cACA,cACA,gBACA,eACA,gBACA,YACA,oBACA,kBACE;CAEJ,IAAI,EAAY,WAAW,KAAK,CAAC,GAAQ,OAAO,CAAC;CAEjD,IAAM,IAA6B,CAAC;CAGpC,AAAI,KACF,EAAK,KAAK;EACR,IAAI;EACJ,SAAS,EAAE,OAAO,QAChB,GAAc,GAAO;GACnB,SAAS,EAAI,qBAAqB;GAClC,eAAe,EAAI,sBAAsB;GACzC,gBAAgB,EAAI,sBAAsB;EAC5C,CAAC;EACH,MAAM;EAKN,OAAO,EAAE,aACP,GACE,QACA,EAAE,eAAe,sBAAsB,GACvC,GAAc,GAAO;GACnB,SAAS,EAAI,cAAc;GAC3B,gBAAgB,EAAI,eAAe;EACrC,CAAC,CACH;CACJ,CAAC;CAIH,KAAK,IAAM,KAAU,GAAa;EAEhC,IAAI,EAAO,WAAW,UAAU,GAAG;GACjC,IAAM,IAAY,EAAO,MAAM,CAAC,GAC1B,IAAQ,EAAO,gBAAgB;GACrC,IAAI,CAAC,GAAO;GACZ,IAAM,IAAY,IAAI,IAAI,EAAM,SAAS,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;GAChE,EAAK,KAAK;IACR,IAAI;IACJ,QAAQ,EAAI,EAAM,IAAI,KAAK;IAC3B,MAAM;IACN,OAAO,EAAE,aAAU;KAEjB,IAAM,IADY,EAAI,SAAqC,WACnC,MAAM,MAAM,EAAE,eAAe,CAAS;KAC9D,IAAI,CAAC,GACH,OAAO,GAAc,QAAQ,EAAE,WAAW,gCAAgC,GAAG,GAAG;KAElF,IAAM,IAAM,EAAU,IAAI,EAAM,WAAW;KAC3C,OAAO,GACL,GACA;MAAE,MAAO,GAAK,SAAsB;MAAS,MAAM;KAAK,GACxD,EAAI,GAAK,IAAI,KAAK,EAAM,WAC1B;IACF;GACF,CAAC;GACD;EACF;EAGA,IAAI,EAAO,WAAW,YAAY,GAAG;GACnC,IAAM,IAAW,EAAO,MAAM,EAAE,GAC1B,IAAU,EAAO,aAAa;GACpC,IAAI,CAAC,GAAS;GACd,EAAK,KAAK;IACR,IAAI;IACJ,cAAc,GAAuB,EAAI,EAAQ,IAAI,KAAK,GAAU,GAAQ,CAAQ;IACpF,MAAM;IACN,eAAe;IACf,OAAO,EAAE,aAAU;KAEjB,IAAM,IADS,EAAI,SAAqC,aACnC,MAAM,MAAM,EAAE,kBAAkB,CAAQ;KAI7D,OAHI,CAAC,KAAS,EAAM,SAAS,OACpB,GAAc,QAAQ,EAAE,WAAW,gCAAgC,GAAG,GAAG,IAE3E,GAAc,QAAQ,EAAE,WAAW,UAAU,GAAG,OAAO,EAAM,KAAK,CAAC;IAC5E;GACF,CAAC;GACD;EACF;EAGA,IADkB,EAAO,OAAO,IAAI,CAAM,GAC3B;GACb,EAAK,KAAK;IACR,IAAI;IACJ,aAAa,MAAS,EAAI,SAAiD,MAAW;IACtF,cAAc,GAAuB,GAAwB,GAAQ,GAAQ,GAAQ,QAAQ,GAAG,GAAQ,CAAM;IAC9G,MAAM;IACN,OAAO,EAAE,aACP,GAAkB;KAChB;KACA;KACA,WAAW;KACX,QAAQ,EAAI;KACZ,MAAM;KACN,SAAS;KACT,MAAM,EAAQ;KACd,QAAQ;IACV,CAAC;GACL,CAAC;GACD;EACF;EAYA,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAM;EACtC,IAAI,CAAC,GAAO;EACZ,IAAM,IAAc,GAAiB,GAAO,GAAQ,GAAQ,QAAQ,GAC9D,IAAQ,EAAM,SAAS,OACvB,IAAW,GAAoB;GACnC;GACA,WAAW;GACX,SAAS;GACT;GACA,UAAU,GAAQ;GAClB,SAAS,GAAQ;GACjB,YAAY,EAAQ;GACpB,QAAQ;EACV,CAAC;EACD,EAAK,KAAK;GACR,IAAI;GACJ,aAAa;GACb,cAAc,GAAuB,GAAa,GAAQ,CAAM;GAChE,eAAe,EAAM,SAAS;GAC9B,MAAM,KAAS;GACf,OAAO,EAAE,aAAU,aAAU,EAAS,EAAS,GAAG,EAAI,QAAmC;EAC3F,CAAC;CACH;CAGA,IAAM,IAAuB,CAAC,KAAyB,GACjD,IAAyB,CAAC,KAAyB;CAwBzD,QAvBI,KAAwB,KAA0B,EAAW,SAAS,MACxE,EAAK,KAAK;EACR,IAAI;EACJ,QAAQ;EACR,MAAM;EACN,OAAO,EAAE,aACP,GAAc,IAAgB;GAC5B,KAAK,EAAI;GACT,OAAO,EAAgB,GAAQ,EAAI,QAAmB;GAC9C;GACR,YAAY;GACJ;GACR;GACA;GACA;GACA;GACA;GACA,QAAQ,UAAoB,EAAW,EAAI,QAAQ,IAAI,KAAA;GACvD,QAAQ;EACV,CAAC;CACL,CAAC,GAGI;AACT;;;AChQA,SAAgB,GAAa,EAC3B,qBACA,mBACA,aACA,YACA,cACA,kBAAe,MAAM,KACD;CACpB,IAAM,IAAS,EAAiC,IAAI,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAK;CAEtC,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,KAAK;EACL,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;EAC/B,eAAe,GAAS,MAAS,CAAC,CAAI;EACtC,QAAQ;YACT;CAEI,CAAA,GACL,kBAAC,IAAD;EACQ;EACN,QAAQ,EAAO;EACf,eAAe,EAAQ,EAAK;EAC5B,OAAM;EACN,OAAO;YALT;GAOE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,UAAU;KACV,OAAO;KACP,YAAY;KACZ,eAAe;KACf,eAAe;IACjB;cACD;GAEI,CAAA;GACJ,EAAiB,KAAK,MAGnB,kBAAC,GAAD;IAEE,MAJc,EAAe,SAAS,CAIhC,IAAY,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,kBAAC,QAAD,EAAM,OAAO;KAAE,OAAO;KAAI,SAAS;IAAe,EAAI,CAAA;IACnG,eAAe,EAAS,CAAG;cAE1B,EAAY,CAAG;GACR,GALH,CAKG,CAEb;GACD,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;GACnB,kBAAC,GAAD;IAAU,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;IAAG,SAAS;cAAS;GAEpD,CAAA;GACV,kBAAC,GAAD;IAAU,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;IAAG,SAAS;cAAW;GAElD,CAAA;EACF;GACV,EAAA,CAAA;AAEN;;;ACtEA,IAAM,KAAuC;CAC3C,SAAS;CACT,YAAY;CACZ,KAAK;CACL,QAAQ;CACR,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,cAAc;CACd,QAAQ;CACR,UAAU;CACV,OAAO;CACP,YAAY;AACd;AAuBA,SAAgB,GAAa,EAC3B,UACA,iBACA,YACA,aACA,WACA,eACoB;CACpB,IAAM,IAAS,EAAiC,IAAI,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAK,GAEhC,IAAa,EAAM,MAAM,MAAM,EAAE,OAAO,CAAY,GACpD,IAAc,EAAM,QAAQ,MAAM,EAAE,WAAW,QAAQ,GACvD,IAAgB,EAAM,QAAQ,MAAM,EAAE,WAAW,UAAU,GAC3D,IAAc,EAAM,QAAQ,MAAM,EAAE,WAAW,QAAQ,GAEvD,IAAc,IAAa,EAAI,EAAW,IAAI,IAAI,gBAElD,KAAgB,MACpB,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,UAAU;GACV,OAAO;GACP,YAAY;GACZ,eAAe;GACf,eAAe;EACjB;YAEC;CACE,CAAA;CAGP,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;EAAE;YAA5D;GACE,kBAAC,UAAD;IACE,KAAK;IACL,MAAK;IACL,eAAe,GAAS,MAAS,CAAC,CAAI;IACtC,OAAO;cAJT;KAME,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KACtB,kBAAC,QAAD,EAAA,UAAO,EAAkB,CAAA;KACxB,KACC,kBAAC,QAAD,EACE,OAAO;MACL,SAAS;MACT,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,YAAY;KACd,EACD,CAAA;KAEH,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAChB;;GACR,kBAAC,IAAD;IACQ;IACN,QAAQ,EAAO;IACf,eAAe,EAAQ,EAAK;IAC5B,OAAO;cAJT;KAME,kBAAC,GAAD;MACE,MAAO,IAA0C,KAAA,IAA3B,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;MAC7C,QAAQ,CAAC;MACT,eAAe;OAEb,AADA,EAAS;QAAE,IAAI;QAAI,MAAM,EAAE,IAAI,UAAU;QAAG,QAAQ;OAAS,CAAC,GAC9D,EAAQ,EAAK;MACf;gBACD;KAES,CAAA;KAET,EAAY,SAAS,KAAK,EAAa,SAAS;KAChD,EAAY,KAAK,MAChB,kBAAC,GAAD;MAEE,MAAM,MAAiB,EAAE,KAAK,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,KAAA;MACzD,QAAQ,MAAiB,EAAE;MAC3B,eAAe;OAEb,AADA,EAAS,CAAC,GACV,EAAQ,EAAK;MACf;gBAEC,EAAI,EAAE,IAAI;KACH,GATH,EAAE,EASC,CACX;KAEA,EAAc,SAAS,KAAK,EAAa,UAAU;KACnD,EAAc,KAAK,MAClB,kBAAC,GAAD;MAEE,MAAM,MAAiB,EAAE,KAAK,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,EAAE,aAAa,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA,IAAI,KAAA;MAClG,QAAQ,MAAiB,EAAE;MAC3B,eAAe;OAEb,AADA,EAAS,CAAC,GACV,EAAQ,EAAK;MACf;gBAEC,EAAI,EAAE,IAAI;KACH,GATH,EAAE,EASC,CACX;KAEA,EAAY,SAAS,KAAK,EAAa,QAAQ;KAC/C,EAAY,KAAK,MAChB,kBAAC,GAAD;MAEE,MAAM,MAAiB,EAAE,KAAK,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;MAChF,QAAQ,MAAiB,EAAE;MAC3B,eAAe;OAEb,AADA,EAAS,CAAC,GACV,EAAQ,EAAK;MACf;gBAEC,EAAI,EAAE,IAAI;KACH,GATH,EAAE,EASC,CACX;KAEA,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA,GACnB,kBAAC,GAAD;MACE,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAC5B,eAAe;OAEb,AADA,EAAS,GACT,EAAQ,EAAK;MACf;gBACD;KAES,CAAA,CACV,EAAA,CAAA;IAEI;;GAET,KAAW,KAAgB,KAC1B,kBAAC,GAAD;IAAK,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAAG,SAAS;cAAQ;GAEhD,CAAA;EAEJ;;AAET;;;ACnLA,SAAgB,GAAU,GAAoD;CAC5E,IAAM,EAAE,MAAM,MAAW,GAAU;CAC/B,OAAC,KAAU,CAAC,IAChB,OAAO,EAAO,QAAQ;AACxB;;;ACEA,SAAgB,GAAS,GAAoB,GAAiB;CAC5D,IAAM,IAAS,GAAU,CAAU,GAG7B,EAAE,MAAM,MAAe,GAAc,QAAQ;EACjD,SAAS,CACP;GAAE,OAAO;GAAe,IAAI;GAAM,OAAO;EAAW,GAEpD,GAAI,IAAS,CAAC,EAAE,IAAI,CAClB,CAAC;GAAE,OAAO;GAAW,IAAI;GAAM,OAAO;EAAO,CAAC,GAC9C,CAAC;GAAE,OAAO;GAAa,IAAI;GAAM,OAAO;EAAO,CAAC,CAClD,EAAE,CAAC,IAAI,CAAC,CACV;EACA,UAAU;CACZ,CAAC;CAwCD,OAtCc,QAA0B;EACtC,IAAM,IAAoB,CAAC;EAG3B,IAAI,GAAQ,OACV,KAAK,IAAM,KAAK,EAAO,OACrB,EAAM,KAAK;GACT,IAAI,EAAE;GACN,MAAM,EAAE;GACR,MAAM,EAAE;GACR,QAAQ;GACR,SAAS,EAAE;GACX,SAAS,EAAE;GACX,MAAM,EAAE;EACV,CAAC;EAKL,IAAI,GAAY,MACd,KAAK,IAAM,KAAO,EAAW,MAAM;GACjC,IAAM,IAAQ,KAAU,OAAO,EAAI,OAAO,MAAM;GAChD,EAAM,KAAK;IACT,IAAI,OAAO,EAAI,EAAE;IACjB,MAAM,OAAO,EAAI,QAAS,YAAY,EAAI,SAAS,OAAO,EAAI,OAAiC,EAAE,IAAI,OAAO,EAAI,QAAQ,EAAE,EAAE;IAC5H,MAAM,EAAI;IACV,QAAQ,IAAQ,aAAa;IAC7B,YAAY,EAAI,eAAe;IAC/B,SAAS,EAAI;IACb,SAAS,EAAI;IACb,MAAM,EAAI;GACZ,CAAC;EACH;EAGF,OAAO;CACT,GAAG;EAAC,GAAQ;EAAO,GAAY;EAAM;CAAM,CAEpC;AACT;;;AC8CA,IAAM,KAAoB,IAAI,IAAI;CAChC;CAAU;CAAQ;CAAS;CAAO;CAClC;CAAO;CAAW;CAAU;CAC5B;CAAS;CAAU;CAAW;CAAW;CAAU;CAAS;CAC5D;CAAQ;CACR;CAAQ;CAAY;CAAa;CACjC;CAAQ;AACV,CAAC;AAED,SAAS,GAAsB,GAA0C;CACvE,IAAM,IAAmB,CAAC;CAC1B,KAAK,IAAM,CAAC,GAAM,MAAM,EAAO,QAAQ;EAIrC,IAHI,EAAE,WAAW,EAAE,QACf,EAAE,OACF,EAAE,SAAS,EAAE,aACb,EAAE,SAAS,gBAAgB,IAAO;EACtC,IAAM,KAAK,EAAE,QAAQ,IAAI,YAAY,GAC/B,IAAS,GAAQ,EAAE,QAAQ,EAAE;EAC/B,OAAC,GAAkB,IAAI,CAAC,KAAK,CAAC,OAClC,EAAO,KAAK,CAAI,GACZ,EAAO,UAAU,IAAG;CAC1B;CACA,OAAO;AACT;AAoBA,SAAS,GAAe,EACtB,UACA,cACA,gBACA,gBACA,cACA,YACA,aACA,kBACA,iBAaC;CACD,IAAM,IAAS,EAAiC,IAAI,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,IAKD;EACH;GAAE,OAAO;GAA8B,KAAK;GAAO,OAAO;GAAQ,MAAM,EAAK;EAAQ;EACrF;GAAE,OAAO;GAA+B,KAAK;GAAO,OAAO;GAAS,MAAM,EAAK;EAAQ;EACvF;GAAE,OAAO;GAA+B,KAAK;GAAQ,OAAO;GAAQ,MAAM,EAAK;EAAU;EACzF;GAAE,OAAO;GAAgC,KAAK;GAAQ,OAAO;GAAS,MAAM,EAAK;EAAU;CAC7F,GAIM,IAA4B,KAAe,QAG3C,IAAiB,CAAC,KAAe;CACvC,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;EACE,KAAK;EACL,MAAK;EACL,cAAY,oBAAoB;EAChC,OAAM;EACN,UAAU,MAAM;GAEd,AADA,EAAE,gBAAgB,GAClB,GAAS,MAAM,CAAC,CAAC;EACnB;EACA,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,OAAO;GACP,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,OAAO;GACP,QAAQ;GACR,SAAS,IAAO,IAAI;EACtB;YAEA,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;CACpB,CAAA,GACR,kBAAC,IAAD;EACQ;EACN,QAAQ,EAAO;EACf,eAAe,EAAQ,EAAK;EAC5B,OAAM;EACN,OAAO;YALT;GAOG,EAAQ,KAAK,MAAQ;IACpB,IAAM,IAAS,MAAc,EAAI,OAAO,MAAmB,EAAI,OACzD,IAAQ,EAAI;IAClB,OACE,kBAAC,GAAD;KAEE,MACE,kBAAC,QAAD;MAAM,OAAO;OAAE,SAAS;OAAe,YAAY;OAAU,KAAK;MAAE;gBAApE,CACE,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,GACjB,IAAS,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,IACjC;;KAER,eAAe;MAEb,AADA,EAAQ,EAAK,GACb,EAAQ,EAAI,KAAK,EAAI,KAAK;KAC5B;eAEC,EAAI;IACG,GAbH,GAAG,EAAI,IAAI,GAAG,EAAI,OAaf;GAEd,CAAC;GACA,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA,GACnB,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAC5B,eAAe;KAKb,AAJA,EAAQ,EAAK,GAIb,EAAS,OAAO,CAAW;IAC7B;cACD;GAES,CAAA,CACV,EAAA,CAAA,IACA;GACJ,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;GACnB,kBAAC,GAAD;IACE,UAAU,MAAc;IACxB,eAAe;KAEb,AADA,EAAQ,EAAK,GACT,MAAc,MAAO,EAAc;IACzC;cACD;GAES,CAAA;GACV,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;GACnB,kBAAC,GAAD;IACE,UAAU,MAAc;IACxB,eAAe;KAEb,AADA,EAAQ,EAAK,GACb,EAAW;IACb;cACD;GAES,CAAA;EACF;GACV,EAAA,CAAA;AAEN;AAEA,IAAM,KAAyB,KASlB,KAAmB;CAAC;CAAI;CAAI;CAAK;CAAK;CAAK;AAAI;AAE5D,SAAS,GAAgB,GAAiB,GAA2B;CACnE,IAAM,CAAC,GAAO,KAAY,EAAS,EAAK;CAaxC,OAXA,QAAgB;EACd,IAAI,CAAC,GAAQ;GACX,EAAS,EAAK;GACd;EACF;EAEA,EAAS,EAAK;EACd,IAAM,IAAQ,OAAO,iBAAiB,EAAS,EAAI,GAAG,EAAsB;EAC5E,aAAa,OAAO,aAAa,CAAK;CACxC,GAAG,CAAC,GAAQ,CAAQ,CAAC,GAEd;AACT;AAEA,SAAS,GAAyB,GAAwB;CAOxD,OANI,aAAiB,KACZ,GAAG,EAAM,KAAK,IAAI,EAAM,YAE7B,aAAiB,SAAS,EAAM,UAC3B,EAAM,UAER;AACT;AAMA,IAAM,KAAyB,IAAI,IAAY;CAC7C;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAMD,SAAS,GAAiB,GAAkB,GAAqC;CAI/E,IAHI,MAAa,cAAc,MAAa,aAGxC,EAAS,WAAW,YAAY,KAAK,EAAS,WAAW,QAAQ,GACnE,OAAO;CAIT,IAAI,EAAS,WAAW,UAAU,GAAG,OAAO;CAG5C,IAAM,IAAQ,GAAQ,OAAO,IAAI,CAAQ;CAEzC,OADK,IACE,CAAC,GAAuB,IAAI,EAAM,IAAI,IAD1B;AAErB;AAEA,SAAS,GAAqB,GAA2C;CACvE,IAAM,IAAO,IAAI,gBAAgB,GAC3B,IAAS,EAAQ,IAAI,QAAQ;CACnC,AAAI,KAAQ,EAAK,IAAI,UAAU,CAAM;CACrC,KAAK,IAAM,KAAO,IAAkB;EAClC,IAAM,IAAQ,EAAQ,IAAI,CAAG;EAC7B,AAAI,MAAU,QAAM,EAAK,IAAI,GAAK,CAAK;CACzC;CACA,OAAO;AACT;AAEA,SAAgB,GAAgB,GAAgB,GAAsB;CACpE,OAAO,GAAmB,GAAQ,GAAgB,GAAQ,CAAG,CAAC;AAChE;AAEA,SAAgB,GACd,GACA,GACA,GACY;CACZ,IAAI,CAAC,KAAU,EAAa,WAAW,GAAG,OAAO,CAAC;CAClD,IAAM,IAAW,IAAI,IAAI,CAAY;CACrC,OAAO,EACJ,QAAQ,MAAQ,EAAS,IAAI,GAAgB,GAAQ,CAAG,CAAC,CAAC,EAC1D,KAAK,MAAQ,GAAgB,GAAQ,CAAG,CAAC;AAC9C;AAmBA,SAAgB,GACd,GACA,GACA,GACA,GACe;CACf,OAAO,GAAsB,GAAM,GAAI,GAAQ,CAAU;AAC3D;AAEA,SAAgB,GAAc,EAAE,iBAAkC;CAChE,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD,EAAgC,cAAa,CAAA,EAC/B,CAAA;AAEpB;AAEA,SAAS,GAAmB,EAAE,iBAAkC;CAC9D,IAAM,IAAc,GAAe,GAC7B,IAAc,GAAe,GAC7B,CAAC,GAAc,KAAmB,GAAgB,GAClD,EAAE,MAAM,MAAW,GAAU,GAI7B,IAAa,GAAc,GAC3B,CAAC,GAAa,KAAkB,EAA0D,IAAI,GAE9F,CAAC,GAAc,KAAmB,EAA4B,CAAC,CAAC,GAChE,CAAC,GAAkB,KAAuB,EAAS,EAAK,GACxD,CAAC,GAAc,KAAmB,EAAS,EAAK,GAChD,CAAC,GAAa,KAAkB,EAAwD,IAAI,GAC5F,EAAE,SAAS,GAAa,aAAa,GAAa,YAAY,GAAgB,oBAAiB,GAAW,GAC1G,IAAO,GAAc,MAAM,EAAE,IAAI,GACjC,IAAe,GAAc,MAAM,EAAE,YAAY,GACjD,IAAW,GAAS,GAAY,GAAM,EAAE,GACxC,IAAiB,GAAkB,GAQnC,IAAwB,sCAAsC,KAC9D,KAAqB,EAAO,EAAK;CACvC,QAAgB;EAEd,IADI,GAAmB,WACnB,CAAC,KAAY,EAAS,WAAW,GAAG;EACxC,IAAI,EAAa,IAAI,MAAM,GAAG;GAI5B,GAAmB,UAAU;GAC7B;EACF;EACA,IAAI,OAAO,SAAW,OAAe,OAAO,gBAAgB,QAAQ,CAAqB,GAAG;GAE1F,GAAmB,UAAU;GAC7B;EACF;EACA,IAAM,IAAM,EAAS,MAAM,MAAM,EAAE,WAAW,cAAc,EAAE,UAAU;EACxE,IAAI,CAAC,KAAO,CAAC,EAAI,IAAI;EACrB,GAAmB,UAAU;EAO7B,IAAM,IAAO,GAAqB,CAAY;EAY9C,AAXA,EAAK,IAAI,QAAQ,EAAI,EAAE,GACnB,EAAI,WAAW,MAAM,QAAQ,EAAI,OAAO,KAAK,EAAI,QAAQ,SAAS,KACpE,EAAK,IAAI,WAAW,KAAK,UAAU,EAAI,OAAO,CAAC,GAE7C,EAAI,WAAW,EAAI,QAAQ,SAAS,KACtC,EAAK,IAAI,WAAW,EAAI,QAAQ,KAAK,GAAG,CAAC,GAEvC,EAAI,QACN,EAAK,IAAI,QAAQ,EAAI,IAAI,GAG3B,EAAgB,GAAM,EAAE,SAAS,GAAK,CAAC;CACzC,GAAG;EAAC;EAAU;EAAc;EAAiB;CAAqB,CAAC;CAEnE,IAAM,IAAS,GAAQ,QAAQ,IACzB,IAAS,GAAQ,SAAS,GAAQ,SAAS,EAAO,UAAU,KAAA,GAC5D,IAAa,GAAQ,QACrB,KAAY,GAAqB,GAAQ,QAAQ,GACjD,KAAY,GAAqB,GAAQ,QAAQ,GACjD,KAAY,GAAqB,GAAQ,QAAQ,GAGjD,KAAgB,GAAQ,SACxB,IAAiB,QACf,GAAiB,GAAQ,SAAS,GAAQ,SAAS,gBAAgB,EAAa,GACtF;EAAC,GAAQ;EAAS,GAAQ;EAAS;CAAa,CAClD,GAMM,KAAkB,QAChB,GAAiB,GAAQ,SAAS,KAAA,GAAW,iBAAiB,EAAa,GACjF,CAAC,GAAQ,SAAS,EAAa,CACjC,GACM,KAAa,QACX,GAAiB,GAAQ,SAAS,GAAQ,aAAa,YAAY,EAAa,GACtF;EAAC,GAAQ;EAAS,GAAQ;EAAa;CAAa,CACtD,GAEM,KAAwB,GAAQ,gBAAgB,KAAA,GAKhD,KAAkB,QAChB,GAAgB,SAAS,KAAQ,GAAQ,QAAQ,YAAY,MAAY,GAAQ,QAAQ,YAAY,IAC3G;EAAC;EAAiB,GAAQ,QAAQ;EAAS,GAAQ,QAAQ;CAAO,CACpE,GAEM,KAAgB,CAAC,CAAC,GAAQ,aAC1B,KAAiB,OAAkB,GAAM,eAAe,CAAC,GAAG,MAAM,MAAM,MAAM,0BAA0B,MAAM,eAAe,MAAM,GAAG,GACtI,IAAiB,MAAkB,EAAa,IAAI,iBAAiB,MAAM,QAM3E,KAAQ,GAAS,GACjB,EAAE,aAAS,qBAAiB,0BAAsB,GAAY;CAgBpE,AAfA,QAAgB;EACd,IAAI,GAAQ,SAAS,KAAU,CAAC,GAAM,SAAS;GAC7C,IAAM,IAAU,EAAI,EAAO,SAAS,IAAI,KAAK,EAAI,EAAO,QAAQ,WAAW,GACrE,IAAU,EAAO,SAAS,QAAQ,EAAO,QAAQ;GACvD,GAAQ;IACN,UAAU,EAAO;IACjB,QAAQ,EAAa,SAAS;IAC9B,OAAO;IACP,MAAM;IACN,UAAU;GACZ,CAAC;EACH;CACF,GAAG;EAAC,GAAQ;EAAO,GAAQ,SAAS,MAAM;EAAI,GAAQ,QAAQ,aAAa;EAAI,GAAQ,SAAS;EAAM,GAAQ,QAAQ;EAAM,GAAM;CAAO,CAAC,GAG1I,QAAgB;EACd,IAAI,CAAC,GAAQ,SAAS,GAAM,SAAS;EACrC,IAAM,IAAM,GAAkB,EAAO,KAAK;EAC1C,AAAI,KACF,GAAgB,EAAI,IAAI,EAAa,SAAS,CAAC;CAEnD,GAAG;EAAC;EAAc,GAAQ;EAAO,GAAM;CAAO,CAAC;CAG/C,IAAM,KAAO,SAAS,EAAa,IAAI,MAAM,KAAK,KAAK,EAAE,GAWnD,YAAiB;EACrB,IAAM,IAAM,SAAS,EAAa,IAAI,UAAU,KAAK,IAAI,EAAE;EAE3D,OADI,CAAC,OAAO,SAAS,CAAG,KAAK,KAAO,KAAK,IAAA,MAAoB,MACtD;CACT,GAAG,GACG,KAAc,EAAa,IAAI,QAAQ,KAAK,IAC5C,KAAY,EAAa,IAAI,MAAM,KAAK,GAAQ,gBAAgB,IAMhE,KAAW,QAAc,GAAU,EAAS,GAAG,CAAC,EAAS,CAAC,GAC1D,IAAe,QAAc;EACjC,IAAM,oBAAI,IAAI,IAAuB;EACrC,KAAK,IAAM,KAAK,IAAU,AAAI,EAAE,SAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK;EAC7D,OAAO;CACT,GAAG,CAAC,EAAQ,CAAC,GAOP,KAAiB,QAAc,GAA6B,EAAS,GAAG,CAAC,EAAS,CAAC,GACnF,KAAkB,GAAY,IAAa,GAAG,GAG9C,KAAc,QAAwB;EAC1C,IAAM,IAAM,EAAa,IAAI,SAAS;EAEtC,OADI,IAAY,EAAI,MAAM,GAAG,EAAE,OAAO,OAAO,IACtC,GAAQ,mBAAmB,GAAQ,WAAW,CAAC;CACxD,GAAG;EAAC;EAAc,GAAQ;EAAiB,GAAQ;CAAO,CAAC,GAKrD,KAAS,QAAc;EAC3B,IAAM,IAAc,CAAC,GAEf,IAAU,CAAC,GAAI,GAAQ,WAAW,CAAC,GAAI,GAAG,EAAW;EAI3D,OAHI,EAAQ,MAAM,MAAM,EAAE,WAAW,UAAU,CAAC,KAAG,EAAE,KAAK,UAAU,GAChE,EAAQ,MAAM,MAAM,EAAE,WAAW,YAAY,CAAC,KAAG,EAAE,KAAK,YAAY,GACpE,EAAE,SAAS,KAAG,EAAE,QAAQ,MAAM,GAC3B,EAAE,SAAS,IAAI,IAAI,KAAA;CAC5B,GAAG,CAAC,GAAQ,SAAS,EAAW,CAAC,GAQ3B,KAAS,QAAoC;EACjD,IAAM,IAAO,GAAY,QAAQ,MAAM,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC;EAC9D,OAAO,EAAK,SAAS,IAAI,IAAO,KAAA;CAClC,GAAG,CAAC,EAAW,CAAC,GASV,KAAa;EACjB;EACA,UAAU;EACV,MAAM;EACN,QAAQ,MAAmB,KAAA;EAC3B,SAAS,EAAY,SAAS,IAAI,IAAc,KAAA;EAChD,iBAAiB,KAAkB,KAAA;EACnC;EACA;EACA,SAAS;EACT,QAAQ,GAAQ,gBAAgB,IAAa,KAAA;CAC/C,GACM,EAAE,MAAM,IAAU,eAAW,gBAAY,aAAS,WAAO,gBAAY,GAAc,GAAY,EAAU,GAEzG,EAAE,MAAM,IAAe,WAAW,IAAc,SAAS,OAAe,EAAe,GAAY,EAAU,GAI7G,KAAa,IAAe,MAAM,OAClC,KAAgB,IAAe,MAAM,YACrC,KAAoB,GAAgB,IAAW,GAAG,KAAc,GAAG,GAAG,EAAa,SAAS,GAAG,GAC/F,KAAgB,QAAkB;EAItC,AAHI,KACF,EAAiB,cAAc,EAAE,UAAU,CAAC,eAAe,CAAU,EAAE,CAAC,GAE1E,GAAa;CACf,GAAG;EAAC;EAAY;EAAa;CAAO,CAAC,GAO/B,KAAmB,IAAkB,MAAM,EAAE,gBAAgB,GAC7D,KAAS,GAAa,GAAQ,UAAU,IAAkB,KAAc,EAAE,GAE1E,KAAiB,GAAgB,KAAc,EAAE,GACjD,KAAiB,GAAgB,KAAc,EAAE,GACjD,KAAqB,GAAgB,MAAM,GAO3C,KAAwB,QACtB,GAAS,KAAK,OAAO;EAAE,IAAI,EAAE;EAAO,MAAM,EAAE,QAAQ;CAAO,EAAE,GACnE,CAAC,EAAQ,CACX,GAGM,KAAU,QACR,GAAQ,OAAO,MAAM,EAAE,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO,KAAK,MAC3E,CAAC,CAAM,CACT,GACM,KAAoB,GACvB,MAAiC,IAAS,GAAgB,GAAQ,CAAG,IAAI,OAAO,EAAI,OAAY,EAAE,GACnG,CAAC,GAAQ,EAAO,CAClB,GAEM,KAAO,IAAU,OAGjB,KAAU,EAAO,EAAI;CAC3B,GAAQ,UAAU;CAGlB,IAAM,CAAC,IAAS,MAAc,EAAyB,IAAI,GACrD,KAAc,GAAQ,iBAAiB,UAAU,EAAO,YAAY,YAAY,IAGhF,KAAU,QAEZ,GAAiB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GACH;EAAC;EAAQ;EAAQ;EAAa;EAAa;EAAiB;EAAY;EAAuB;EAAY;EAAa;EAAQ;EAAY;EAAW;EAAW;CAAO,CAC3K,GAEM,KAAO,QAAc,IAAU,QAAQ,CAAC,GAAG,CAAC,IAAU,IAAI,CAAC,GAG3D,KAAO,IAAU,MAIjB,KAAa,QAAc;EAC3B,aAAe,KAAA,KAAa,MAAW,IAC3C,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,KAAa,EAAO,CAAC;CACpD,GAAG,CAAC,IAAY,EAAO,CAAC,GAClB,KAAe,IAAM,aAAa,OAAe,KAAA,KAAa,KAAO,KAErE,KAAQ,GAAc;EAC1B;EACA;EACA,iBAAiB,GAAgB;EACjC,eAAe;EACf,kBAAkB;EAClB,OAAO;GAAE;GAAS;EAAa;EAC/B,kBAAkB,MAAY;GAM5B,IAAM,KALO,OAAO,KAAY,aAAa,EAAQ,EAAO,IAAI,GAK5C,MAAM,GAAA,CAAgB,GACpC,IAAS,IAAI,gBAAgB,CAAY;GAC/C,IAAI,EAAO,WAAW,GACpB,EAAO,OAAO,MAAM;QACf;IAIL,IAAM,IAAkB,EAAO,KAAK,MAAM;KACxC,IAAM,IAAQ,EAAa,IAAI,EAAE,EAAE;KACnC,OAAO;MAAE,OAAO,EAAE;MAAI,KAAK,EAAE,OAAO,SAAS;MAAO,GAAI,IAAQ,EAAE,SAAM,IAAI,CAAC;KAAG;IAClF,CAAC;IACD,EAAO,IAAI,QAAQ,GAAc,CAAI,CAAC;GACxC;GAEA,AADA,EAAO,IAAI,QAAQ,GAAG,GACtB,EAAgB,CAAM;EACxB;EACA,sBAAsB;EACtB,UAAU;EAGV,UAAU,MAAc,GAAK;CAC/B,CAAC,GAGK,KAAc,QACZ,OAAO,KAAK,CAAY,EAAE,QAAQ,MAAM,EAAa,EAAE,GAC7D,CAAC,CAAY,CACf,GACM,KAAiB,QACf,GAAoB,GAAQ,IAAM,EAAW,GACnD;EAAC;EAAQ;EAAM;CAAW,CAC5B,GAKM,KAAY,GACf,MAAoB;EACnB,IAAM,IAAS,EAAK,MAAM,GAAA,CAAgB,GACpC,IAAS,IAAI,gBAAgB,CAAY;EAO/C,AANI,EAAO,WAAW,IACpB,EAAO,OAAO,MAAM,IAEpB,EAAO,IAAI,QAAQ,GAAc,CAAM,CAAC,GAE1C,EAAO,IAAI,QAAQ,GAAG,GACtB,EAAgB,CAAM;CACxB,GACA,CAAC,GAAc,CAAe,CAChC,GAIM,KAAkB,GACrB,GAAe,GAAqB,MAAiC;EACpE,GAAU,CAAC;GAAE;GAAO;GAAK,GAAI,IAAQ,EAAE,SAAM,IAAI,CAAC;EAAG,CAAC,CAAC;CACzD,GACA,CAAC,EAAS,CACZ,GAKM,KAAmB,GACtB,MAAkB;EACjB,GAAU,GAAoB,IAAU,CAAK,CAAC;CAChD,GACA,CAAC,IAAU,EAAS,CACtB,GAIM,KAAmB,GACtB,GAAe,GAAqB,MAAiC;EACpE,GAAU,GAAgB,IAAU;GAAE;GAAO;GAAK,GAAI,IAAQ,EAAE,SAAM,IAAI,CAAC;EAAG,CAAC,CAAC;CAClF,GACA,CAAC,IAAU,EAAS,CACtB,GAIM,KAAkB,GACrB,MAAkB;EACjB,GAAU,GAAgB,IAAU,CAAK,CAAC;CAC5C,GACA,CAAC,IAAU,EAAS,CACtB,GAEM,KAAgB,QAAkB;EACtC,GAAU,CAAC,CAAC;CACd,GAAG,CAAC,EAAS,CAAC;CAQd,AALA,QAAgB;EACd,EAAgB,CAAC,CAAC;CACpB,GAAG;EAAC;EAAM;EAAiB;EAAW;CAAW,CAAC,GAGlD,QAAgB;EACd,IAAM,KAAW,MAAqB;GAEpC,IAAM,IAAO,EAAE,QAAwB;GACvC,IAAI,MAAQ,WAAW,MAAQ,cAAc,MAAQ,UAAU;IAC7D,AAAI,EAAE,QAAQ,aACZ,EAAG,OAAuB,KAAK,GAC/B,EAAoB,EAAK;IAE3B;GACF;GAUA,AATI,EAAE,QAAQ,OAAO,CAAC,EAAE,WAAW,CAAC,EAAE,YACpC,EAAE,eAAe,GAEjB,SADuB,cAAgC,2BACvD,GAAO,MAAM,IAEX,EAAE,QAAQ,OAAO,CAAC,EAAE,WAAW,CAAC,EAAE,YACpC,EAAE,eAAe,GACjB,GAAqB,MAAS,CAAC,CAAI,IAEjC,EAAE,QAAQ,YACZ,EAAoB,EAAK;EAE7B;EAEA,OADA,SAAS,iBAAiB,WAAW,CAAO,SAC/B,SAAS,oBAAoB,WAAW,CAAO;CAC9D,GAAG,CAAC,CAAC;CAGL,IAAM,KAAY,EAAuB,IAAI,GACvC,EAAE,aAAS,GAAM,YAAY,GAE7B,KAAU,GAAoB,MAAM,EAAE,OAAO,GAC7C,KAAe,GAAoB,MAAM,EAAE,KAAK,GAEhD,KAAiB,GAAe;EAKpC,OAAO,GAAM,UAAU,IAAI,GAAK;EAChC,wBAAwB,GAAU;EAClC,oBAAoB;EACpB,UAAU;CACZ,CAAC;CAKD,QAAgB;EACd,GAAe,QAAQ;CACzB,GAAG,CAAC,IAAS,EAAc,CAAC;CAG5B,IAAM,KAAe,GAClB,MAAkB;EACjB,GAAiB,MAAS;GACxB,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAOrC,OANI,IACF,EAAK,IAAI,UAAU,CAAK,IAExB,EAAK,OAAO,QAAQ,GAEtB,EAAK,IAAI,QAAQ,GAAG,GACb;EACT,CAAC;CACH,GACA,CAAC,CAAe,CAClB,GAEM,KAAuB,QAAkB;EAC7C,GAAiB,MAAS;GACxB,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAOrC,OANI,EAAK,IAAI,iBAAiB,IAC5B,EAAK,OAAO,iBAAiB,IAE7B,EAAK,IAAI,mBAAmB,MAAM,GAEpC,EAAK,IAAI,QAAQ,GAAG,GACb;EACT,CAAC;CACH,GAAG,CAAC,CAAe,CAAC,GAEd,KAAW,GACd,MAAc;EACb,GAAiB,MAAS;GACxB,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAErC,OADA,EAAK,IAAI,QAAQ,OAAO,CAAC,CAAC,GACnB;EACT,CAAC;CACH,GACA,CAAC,CAAe,CAClB;CAQA,QAAgB;EACd,AAAI,OAAe,KAAA,KAAa,KAAO,MACrC,GAAS,EAAU;CAEvB,GAAG;EAAC;EAAY;EAAM;CAAQ,CAAC;CAM/B,IAAM,KAAa,GAChB,MAAc;EACb,GAAiB,MAAS;GACxB,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAGrC,OAFA,EAAK,IAAI,YAAY,OAAO,CAAC,CAAC,GAC9B,EAAK,IAAI,QAAQ,GAAG,GACb;EACT,CAAC;CACH,GACA,CAAC,CAAe,CAClB,GAEM,KAAqB,QACrB,CAAC,KAAU,CAAC,IAAe,KACxB,GAAkB,GAAQ,KAAK,GAAQ,GAAQ,OAAO,MAAM,MAClE;EAAC;EAAQ;EAAQ,GAAQ;CAAO,CAAC,GAK9B,KAAmB,GACtB,GAAc,MAAyC;EACtD,IAAI,CAAC,KAAU,CAAC,GAAQ;EACxB,IAAM,IAAW,GAAgB,GAAQ,CAAG,GACtC,IAAK,GAAmB,GAAQ,CAAQ,GAC1C,IAAO,GAAsB,GAAQ,GAAQ,GAAQ,SAAS,CAAQ;EAC1E,IAAI,CAAC,GAAM;EAKX,IAAI,GAAS,OAAO;GAClB,GAAa,GAAI,EAAQ,KAAgC;GACzD,IAAM,CAAC,GAAM,KAAY,EAAK,MAAM,KAAK,CAAC,GACpC,IAAS,IAAI,gBAAgB,KAAY,EAAE;GAEjD,AADA,EAAO,IAAI,QAAQ,MAAM,GACzB,IAAO,GAAG,EAAK,GAAG,EAAO,SAAS;EACpC;EAOA,IAAM,IAAW,GAAgB,GAAK,CAAM,KAAK,EAAI,EAAO,QAAQ,IAAI,GAClE,IAAO,EAAO,SAAS,QAAQ,EAAO,QAAQ;EACpD,EAAY,GAAM;GAAE,OAAO;GAAU;EAAK,CAAC;CAC7C,GACA;EAAC;EAAQ;EAAQ;EAAQ;CAAW,CACtC,GAEM,KAAiB,GACpB,GAAqB,MAAiB;EAShC,OAAE,OAAuB,QAAQ,8DAA8D,GAGpG;OAAI,IAAa;IACf,GAAW,CAAG;IACd;GACF;GACA,GAAiB,CAAG;EADpB;CAEF,GACA,CAAC,IAAa,EAAgB,CAChC,GAIM,KAAa,QAAoC;EACrD,IAAI,CAAC,MAAe,CAAC,KAAU,CAAC,IAAS,OAAO,CAAC;EACjD,IAAM,IAAW,GAAQ,YAAY,QAC/B,IAAW,GAAQ,YAAY,UACjC;EACJ,IAAI,KAAY,EAAS,SAAS,GAChC,IAAa,EAAS,QAAQ,MAAM,EAAO,OAAO,IAAI,CAAC,KAAK,EAAO,OAAO,IAAI,CAAC,CAAC;OAC3E,IAAI,KAAY,EAAS,SAAS,GAAG;GAK1C,IAAM,IAHe,OAAO,OAAO,GAAQ,WAAW,CAAC,CAAC,EAAE,MACvD,MAAM,EAAE,SAAS,YAAY,EAAE,WAAW,GAAQ,MAEjC,GAAc,SAAS,CAAC,GACtC,IAAsB,CAAC,GACvB,KAAQ,MAAyB;IAChC,UAAM,QAAQ,CAAK,GACxB;UAAK,IAAM,KAAQ,GACjB,IAAI,KAAQ,OAAO,KAAS,YAAY,aAAa,GAAM;MACzD,IAAM,IAAO,EAA2D,SAClE,IAAU,OAAO,GAAK,QAAS,WACjC,EAAI,OACJ,GAAK,QAAQ,OAAO,EAAI,QAAS,WAC/B,OAAO,OAAO,EAAI,IAA8B,EAAE,KAClD;MACN,AAAI,EAAS,SAAS,OAAO,CAAO,CAAC,GACnC,EAAK,GAAK,KAAK;KAInB,OAAO,IAAI,KAAQ,OAAO,KAAS,YAAY,YAAY,GAAM;MAC/D,IAAM,IAAM,EAA8B;MAC1C,IAAI,MAAM,QAAQ,CAAE,QAAQ,IAAM,KAAK,GAAI,AAAI,OAAO,KAAM,YAAU,EAAU,KAAK,CAAC;KACxF;;GAEJ;GAGA,AAFA,EAAK,CAAW,GAChB,IAAa,EAAU,QAAQ,MAAM,EAAO,OAAO,IAAI,CAAC,KAAK,EAAO,OAAO,IAAI,CAAC,CAAC,GAC7E,EAAW,WAAW,MACxB,IAAa,GAAsB,CAAM;EAE7C,OACE,IAAa,GAAsB,CAAM;EAE3C,OAAO,EAAW,KAAK,MAAS;GAE9B,IADkB,EAAO,OAAO,IAAI,CAAI,GAEtC,OAAO;IACL,KAAK;IACL,OAAO,GAAwB,GAAQ,GAAM,GAAQ,QAAQ;IAC7D,MAAM;IACN,UAAU,CAAC;GACb;GAEF,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI,GAC1B,IAAW,MAAa,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,SAAS;GACnF,OAAO;IACL,KAAK;IACL,OAAO,GAAiB,GAAG,GAAM,GAAQ,QAAQ;IACjD,MAAM;IACN,OAAO;IACP,UAAU,CAAC;GACb;EACF,CAAC;CACH,GAAG;EAAC;EAAa;EAAS;EAAQ;EAAQ,GAAQ;EAAU,GAAQ;EAAS;CAAS,CAAC,GAEjF,KAAc,QACb,GAAQ,UACN,EAAO,UADe,CAAC,GAE7B,CAAC,GAAQ,OAAO,CAAC,GAEd,KAAc,EAClB,OAAO,GAAc,GAAgB,MAAoC;EACvE,IAAM,IAAK,GAAQ,OAAO,MAAM,EAAE,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO,GACrE,IAAM,KAAM,KAAU,CAAC,OAAO,GAAQ,MAAO,EAAE,CAAC,IAAI,KAAA;EAE1D,OAAO,EADU,EAAO,SAAS,GAAG,EAAO,OAAO,GAAG,MAAS,GAChC,EAAgB,GAAQ,CAAG,CAAC;CAC5D,GACA,CAAC,GAAQ,EAAO,CAClB,GAEM,KAAe,EAAY,YAAY;EAC3C,IAAI,CAAC,KAAe,EAAY,IAAI,WAAW,GAAG;EAClD,EAAe,IAAI;EACnB,IAAM,IAAU,MAAM,QAAQ,WAC5B,EAAY,IAAI,KAAK,MAAO,GAAe,YAAY,CAAE,CAAC,CAC5D,GACM,IAAS,EAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,EAAE;EAC9D,IAAI,MAAW,GACb,EAAgB,CAAC,CAAC;OACb,IAAI,IAAS,EAAY,IAAI,QAAQ;GAE1C,IAAM,IAAe,IAAI,IACvB,EAAY,IAAI,QAAQ,GAAG,MAAM,EAAQ,GAAG,WAAW,WAAW,CACpE;GAQA,AAPA,GAAiB,MAAS;IACxB,IAAM,IAA0B,CAAC;IACjC,KAAK,IAAM,CAAC,GAAK,MAAQ,OAAO,QAAQ,CAAI,GAC1C,AAAK,EAAa,IAAI,CAAG,MAAG,EAAK,KAAO;IAE1C,OAAO;GACT,CAAC,GACD,EAAM,QAAQ,GAAG,EAAO,MAAM,EAAY,IAAI,OAAO,gBAAgB;EACvE;CACF,GAAG,CAAC,GAAa,EAAc,CAAC;CAEhC,IAAI,CAAC,KAAU,CAAC,GAAQ,OAAO;CAE/B,IAAM,KAAgB,EAAO,SAEvB,KAAa,EAAI,EAAO,SAAS,IAAI,KAAK,EAAI,GAAc,IAAI,GAChE,KAAa,EAAO,SAAS,QAAQ,GAAc,MAQnD,KAAoB,GAAe,KAAK,MAAO,GAAmB,GAAQ,CAAE,CAAC,GAK/E,KAAgD,CAAC;CACrD,IAAI,GAAY,SAAS,GAAG;EAC1B,IAAM,oBAAW,IAAI,IAAqC;EAC1D,KAAK,IAAM,KAAK,IAAM,EAAS,IAAI,GAAkB,CAAC,GAAG,CAAC;EAC1D,KAAqB,GAClB,KAAK,MAAO,EAAS,IAAI,CAAE,CAAC,EAC5B,QAAQ,MAAoC,MAAM,KAAA,CAAS;CAChE;CACA,IAAM,KAA+B;EACnC;EACA,YAAY,KAAc;EAC1B;EACA,aAAa,EAAO;EACpB,cAAc;EACd,aAAa;EACb,QAAQ;EACR,cAAc,GAAK,MAAS,EAAY,GAAK;GAAE,OAAO,GAAM;GAAO,MAAM,GAAM;GAAM,QAAQ,GAAM;EAAO,CAAC;EAC3G,eAAe,GAAM,MAAW,EAAa,GAAM,CAA4C;CACjG,GAMM,MAAiB,GAA4B,GAAkB,MAAyB;EAC5F,IAAM,IAAY,MAAiB,EAAI,SAAS,EAAQ,gBAAiB,MACnE,IAAU,EAAI,cAAc,GAC5B,IAAQ,MAAgB,IAAW,KAAM,GACzC,IAAS,IACX,2BACA,IACE,qBACA;EACN,OACE,kBAAC,MAAD;GAEE,cAAY,IAAc,IAAW,KAAA;GACrC,KAAK,KAAe,MAAS,GAAe,eAAe,CAAI,IAAI,KAAA;GACnE,OAAO;IACL,QAAQ;IAKR,QAAQ,MAAsB,KAAc,YAAY;IACxD,SAAS,IAAY,KAAM;IAC3B,WAAW,IAAY,WAAW;IAClC,YAAY;IACZ,WAAW,IAAU,gCAAgC;IACrD,YAAY;GACd;GACA,eAAe,MAAM;IACnB,AAAK,MAAS,EAAE,cAAc,MAAM,aAAa;GACnD;GACA,eAAe,MAAM;IACnB,EAAE,cAAc,MAAM,aAAa;GACrC;GACA,SACE,MAAsB,MAAe,MAAM,GAAe,GAAG,EAAI,QAAQ,IAAI,KAAA;GAE/E,eACE,WACU;IAGJ,AADA,GAAW,IAAI,GACf,GAAiB,EAAI,QAAQ;GAC/B,IACA,KAAA;GAEN,gBAAgB,MAAM;IAEpB,AADA,EAAE,eAAe,GACjB,EAAe;KAAE,GAAG,EAAE;KAAS,GAAG,EAAE;KAAS,KAAK,EAAI;IAAS,CAAC;GAClE;aAEC,EAAI,gBAAgB,EAAE,KAAK,MAC1B,kBAAC,IAAD;IAA8B;IAAM,eAAe,EAAI,cAAc;GAAI,GAA1D,EAAK,EAAqD,CAC1E;EACC,GA1CG,EAAI,EA0CP;CAER;CAEA,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GAGf,QAAQ,GAAM,UAAU,SAAS;GACjC,YAAY;EACd;YARF;GAUE,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,IAAD;KACE,OAAO;KACP,OAAO;KACP,WAAW;KACX,cAAc,MAAgB,CAAC;KAC/B,QAAQ,GAAK;KACG;KAChB,oBAAoB,EAAO;KAC3B,oBAAoB,EAAO;KACd;KACP;KACN,QAAQ;KACR,uBAAuB,EAAgB,CAAC,CAAC;KAC1B;IAChB,CAAA;IAGD,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAG,SAAS;MAAe,UAAU;KAAO;eAAtG;MACE,kBAAC,IAAD;OACE,OAAO;OACP,cAAc,EAAa,IAAI,MAAM;OACrC,SAAS;OACT,WAAW,MAAS;QAClB,IAAI,CAAC,EAAK,IAAI;SAQZ,AAHI,OAAO,SAAW,OACpB,OAAO,gBAAgB,QAAQ,GAAuB,GAAG,GAE3D,EAAgB,GAAqB,CAAY,CAAC;SAClD;QACF;QACA,IAAM,IAAO,IAAI,gBAAgB,CAAY;QAY7C,AAXA,EAAK,IAAI,QAAQ,EAAK,EAAE,GACpB,EAAK,WAAW,MAAM,QAAQ,EAAK,OAAO,KAAK,EAAK,QAAQ,SAAS,KACvE,EAAK,IAAI,WAAW,KAAK,UAAU,EAAK,OAAO,CAAC,GAE9C,EAAK,WAAW,EAAK,QAAQ,SAAS,KACxC,EAAK,IAAI,WAAW,EAAK,QAAQ,KAAK,GAAG,CAAC,GAExC,EAAK,QACP,EAAK,IAAI,QAAQ,EAAK,IAAI,GAE5B,EAAK,IAAI,QAAQ,GAAG,GACpB,EAAgB,CAAI;OACtB;OACA,gBAAgB,EAAgB,EAAI;MACrC,CAAA;MAED,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAY,OAAO;OAAI;iBAA/C,CACE,kBAAC,GAAD;QACE,OAAO;QACP,UAAU;QACV,aAAa,UAAU,GAAW,YAAY,EAAE;QAChD,QAAQ,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;OACjC,CAAA,GACD,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAY,OAAO;SAAG,KAAK;SAAO,WAAW;SAAoB,eAAe;QAAO;kBAC9G,kBAAC,IAAD,EAAA,UAAK,IAAM,CAAA;OACP,CAAA,CACH;;MAEL,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;OAC9B,eAAe,EAAoB,EAAI;OACvC,QAAQ,IAAc;iBAHxB,CAIC,WAEE,IAAc,KACb,kBAAC,GAAD;QAAM,MAAK;QAAS,MAAK;QAAK,OAAO,EAAE,YAAY,EAAE;kBAClD;OACG,CAAA,CAEL;;MAEL,kBAAC,IAAD;OACE,kBAAkB,EAAO,WAAW,CAAC;OACrC,gBAAgB;OAChB,WAAW,MAAQ;QACjB,IAAM,IAAO,IAAI,gBAAgB,CAAY,GACvC,IAAU,GAAY,SAAS,CAAG,IACpC,GAAY,QAAQ,MAAM,MAAM,CAAG,IACnC,CAAC,GAAG,IAAa,CAAG;QACpB,EAAQ,WAAW,MACvB,EAAK,IAAI,WAAW,EAAQ,KAAK,GAAG,CAAC,GACrC,EAAgB,CAAI;OACtB;OACA,eAAe;QACb,IAAM,IAAO,IAAI,gBAAgB,CAAY;QAE7C,AADA,EAAK,OAAO,SAAS,GACrB,EAAgB,CAAI;OACtB;OACA,iBAAiB;QACf,IAAM,IAAO,IAAI,gBAAgB,CAAY;QAE7C,AADA,EAAK,IAAI,YAAY,EAAO,WAAW,CAAC,GAAG,KAAK,GAAG,CAAC,GACpD,EAAgB,CAAI;OACtB;OACA,cAAc,MAAM;QAClB,IAAI,EAAE,WAAW,UAAU,GAAG;SAC5B,IAAM,IAAK,EAAE,MAAM,CAAC,GACd,IAAI,EAAO,gBAAgB;SACjC,OAAO,IAAK,EAAI,EAAE,IAAI,KAAK,IAAM;QACnC;QACA,IAAI,EAAE,WAAW,YAAY,GAAG;SAC9B,IAAM,IAAK,EAAE,MAAM,EAAE,GACf,IAAI,EAAO,aAAa;SAC9B,OAAO,IAAK,EAAI,EAAE,IAAI,KAAK,IAAM;QACnC;QACA,OAAO,GAAwB,GAAQ,GAAG,GAAQ,QAAQ;OAC5D;MACD,CAAA;MAED,kBAAC,QAAD,EAAM,OAAO;OAAE,OAAO;OAAG,QAAQ;OAAI,YAAY;MAAiB,EAAI,CAAA;MACtE,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAC5B,OAAM;OACN,QAAQ,OAAY;OACpB,eAAe;QACb,IAAM,IAAO,OAAY,YAAY,gBAAgB,OAAY,gBAAgB,SAAS;QAC1F,EAAmB,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAK,CAAC;OACzD;MACD,CAAA;MAWD,kBAAC,GAAD;OACE,MACE,KACE,kBAAC,QAAD;QAAM,eAAY;QAAkB,OAAO,EAAE,SAAS,cAAc;kBAClE,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;OAChB,CAAA,IAEN,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;OAG7B,OAAM;OACN,UAAU,MAAa;OACvB,SAAS;MACV,CAAA;MACA,MACC,kBAAC,QAAD;OACE,eAAY;OACZ,OAAM;iBAEN,kBAAC,GAAD;QAAM,MAAK;QAAU,MAAK;QAAK,KAAA;kBAAI;OAE7B,CAAA;MACF,CAAA;MAGR,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;MAEzB,MACC,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,MAAuB,EAAjB,IAAkB,EAAK,SAAuB,EAAK,KAAlC,EAAa,MAAM,GAAK,CAAyB;OACxE,SAAS;OACT,QAAQ;iBAEP,IAAiB,iBAAiB;MAChC,CAAA;MAEN,KAAc,KACb,kBAAC,IAAD;OACc;OACJ;OACA;OACR,WAAW;QACT;QACA;QACA,QAAQ;QACR,MAAM;QACN,gBAAgB,KAAkB,KAAA;QAClC;QACS;OACX;OACA,gBAAgB;OACA;OAChB,WAAW;OACX,UAAU;OACV,QAAQ;MACT,CAAA;KAEA;;IAGJ,EAAY,SAAS,KACtB,kBAAC,OAAD;KAAK,OAAO,EAAE,SAAS,cAAc;eACrC,kBAAC,IAAD;MACE,SAAS;MACT,WAAW,MAAU,EAAe,GAAa,GAAa,CAAK,CAAC;MACpE,YAAY;MACZ,mBAAmB,EAAoB,EAAI;MAC3C,aAAa,MAAM;OACjB,IAAI,EAAE,WAAW,UAAU,GAAG;QAC5B,IAAM,IAAY,EAAE,MAAM,CAAC,GACrB,IAAQ,EAAO,gBAAgB;QACrC,OAAO,IAAS,EAAI,EAAM,IAAI,KAAK,IAAa;OAClD;OACA,IAAI,EAAE,WAAW,YAAY,GAAG;QAC9B,IAAM,IAAW,EAAE,MAAM,EAAE,GACrB,IAAO,EAAO,aAAa;QACjC,OAAO,IAAQ,EAAI,EAAK,IAAI,KAAK,IAAY;OAC/C;OACA,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAC;OACjC,OAAO,IAAQ,GAAiB,GAAO,GAAG,GAAQ,QAAQ,IAAI;MAChE;KACD,CAAA;IACI,CAAA;GAEL,EAAA,CAAA;GAGD,MAAa,KACZ,kBAAC,OAAD;IAAK,OAAO,EAAE,SAAS,SAAS;cAC9B,kBAAC,IAAD;KACE,MAAK;KACL,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAK,MAAK;MAAU,MAAK;MAAK,SAAS;gBAAe;KAEjD,CAAA;IAER,CAAA;GACE,CAAA,IACH,KACF,kBAAC,IAAD,EAAe,MAAM,EAAO,SAAS,UAAU,EAAI,CAAA,IACjD,KACF,kBAAC,OAAD;IAAK,OAAO,EAAE,SAAS,SAAS;cAC9B,kBAAC,IAAD;KACE,MAAK;KACL,OAAM;KACN,aAAa,GAAyB,EAAK;KAC3C,QACE,kBAAC,GAAD;MAAK,MAAK;MAAU,MAAK;MAAK,SAAS;gBAAe;KAEjD,CAAA;IAER,CAAA;GACE,CAAA,IACH,GAAK,WAAW,IAClB,kBAAC,OAAD;IAAK,OAAO,EAAE,SAAS,SAAS;qBACtB;KACN,IAAM,IAAa,CAAC,CAAC,MAAmB,IAAc,GAChD,IAAc,GAAc,aAC5B,IAAc,EAAI,GAAa,KAAK,GACpC,IAAa,EAAI,GAAa,IAAI,GAClC,IAAe,GAAgB,GAAa,cAAc,GAE1D,IAAQ,IACV,qBACA,KAAe,MAAM,EAAI,GAAc,WAAW,EAAE,YAAY,KAAK,GAAW,YAAY,EAAE,OAO9F;KAkCJ,OAjCA,AAiBE,IAjBE,IACY,yCACL,IAEP,kBAAA,GAAA,EAAA,UAAA,CACG,GACA,KACC,kBAAA,GAAA,EAAA,UAAA,CACG,KACD,kBAAC,KAAD;MAAG,MAAM;MAAc,QAAO;MAAS,KAAI;gBAAsB;KAE9D,CAAA,CACH,EAAA,CAAA,CAEJ,EAAA,CAAA,IAEM,KAGI,qBAAqB,EAAI,GAAc,IAAI,EAAE,YAAY,EAAE,mBAF3D,KAAA,GAiBd,kBAAC,IAAD;MACE,MAAM;MACC;MACM;KACd,CAAA;IAEL,GAAG;GACA,CAAA,IAEL,kBAAC,OAAD;IACE,OAAO;KAIL,MAAM,GAAM,UAAU,SAAS;KAC/B,WAAW;KACX,SAAS;KACT,SAAS;KACT,eAAe;IACjB;cAEA,kBAAC,OAAD;KACE,OAAO;MACL,YAAY;MACZ,QAAQ;MACR,cAAc;MACd,UAAU;MACV,WAAW;MACX,SAAS;MACT,eAAe;MACf,MAAM,GAAM,UAAU,SAAS;MAC/B,WAAW;KACb;eAXF,CAaE,kBAAC,OAAD;MACE,KAAK;MACL,OAAO;OACL,UAAU,GAAM,UAAU,YAAY;OACtC,MAAM,GAAM,UAAU,SAAS;OAC/B,WAAW;MACb;gBAEA,kBAAC,SAAD;OACE,OAAO;QACL,OAAO;QACP,gBAAgB;QAChB,eAAe;QACf,UAAU;QACV,OAAO;OACT;iBAPF,CASE,kBAAC,SAAD,EAAA,UACG,GAAM,gBAAgB,EAAE,KAAK,MAC5B,kBAAC,MAAD;QAAyB,OAAO;SAAE,QAAQ;SAAI,YAAY;QAAmB;kBAC1E,EAAY,QAAQ,KAAK,MAAW;SAcnC,IAAM,IAAU,GAAiB,EAAO,IAAI,CAAM,GAC5C,IAAS,EAAO,OAAO,YAAY,GACnC,IAAW,EAAO,OAAO,WACzB,IAAY,EAAO,OAAO,YAI1B,IAAW,IACb,GAAS,WAAW,MAAM,EAAE,UAAU,EAAO,EAAE,IAAI,IACnD,GACE,IAAgB,GAAS,SAAS,KAAK,IAAW,GAClD,IAAc,IAAW,GACzB,IAAgB,GAAS,SAAA,GAMzB,IAAgB,IAClB,EAAO,OAAO,wBAAwB,IACtC,KAAA;SAWJ,OACE,kBAAC,MAAD;UAEE,SAbkB,KACjB,MAA6C;WAC5C,IAAI,EAAE,UAAU;YAEd,AADA,EAAE,eAAe,GACjB,GAAiB,EAAO,EAAE;YAC1B;WACF;WACA,IAAgB,CAAC;UACnB,IACA,KAAA;UAKA,OAAO;WACL,SAAS,IAAW,iBAAiB,IAAY,UAAU;WAC3D,WAAW;WACX,YAAY;WACZ,UAAU;WACV,eAAe;WACf,OAAO;WACP,YAAY;WACZ,QAAQ,IAAU,YAAY;WAC9B,OAAO,EAAO,QAAQ;WACtB,UAAU,EAAO,QAAQ;WACzB,cAAc;WACd,YAAY;WACZ,UAAU;WACV,KAAK;WACL,MAAM,IAAW,IAAI;WACrB,QAAQ,IAAW,IAAI;UACzB;oBAEC,EAAO,gBAAgB,OAAO,IAC7B,kBAAC,QAAD;WACE,OAAO;YACL,SAAS;YACT,YAAY;YACZ,KAAK;YACL,eAAe;YACf,UAAU;YACV,eAAe;WACjB;qBARF;YAUG,GAAW,EAAO,OAAO,UAAU,QAAQ,EAAO,WAAW,CAAC;YAC/D,kBAAC,QAAD;aACE,OAAO;cACL,SAAS;cACT,eAAe;cACf,YAAY;cACZ,YAAY;aACd;uBANF,CAQG,MAAW,QACV,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA,IACvB,MAAW,SACb,kBAAC,EAAK,WAAN,EAAgB,MAAM,GAAK,CAAA,IAE3B,kBAAC,EAAK,MAAN;cAAW,MAAM;cAAI,OAAO,EAAE,SAAS,IAAK;aAAI,CAAA,GAYjD,KAAU,EAAa,IAAI,EAAO,EAAE,MAAM,UACzC,kBAAC,QAAD;cACE,eAAY;cACZ,OAAO;eACL,OAAO;eACP,QAAQ;eACR,WAAW;eACX,cAAc;eACd,YAAY;cACd;aACD,CAAA,IACC,IACA;;YAKL,IACC,kBAAC,QAAD;aACE,cAAY,aAAa;aACzB,OAAO;cACL,UAAU;cACV,YAAY;cACZ,OAAO;cACP,eAAe;cACf,oBAAoB;aACtB;uBAEC;YACG,CAAA,IACJ;YACJ,kBAAC,IAAD;aACE,OAAO,EAAO;aACd,WAAW;aACX,aAAa,EAAa,IAAI,EAAO,EAAE;aAC1B;aACb,WAAW;aACX,UAAU,GAAK,MAAU,GAAgB,EAAO,IAAI,GAAK,CAAK;aAC9D,WAAW,GAAK,MAAU,GAAiB,EAAO,IAAI,GAAK,CAAK;aAChE,qBAAqB,GAAgB,EAAO,EAAE;aAC9C,YAAY;YACb,CAAA;WACG;eAEN,GAAW,EAAO,OAAO,UAAU,QAAQ,EAAO,WAAW,CAAC;SAE9D,GAxGG,EAAO,EAwGV;QAER,CAAC;OACC,GA3JK,EAAY,EA2JjB,CACL,EACI,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,GAAM,UAML,GAAK,KAAK,GAAK,MAAM,GAAc,GAAK,GAAG,EAAK,CAAC,IAEjD,kBAAA,GAAA,EAAA,UAAA;QACG,GAAe,gBAAgB,EAAE,SAAS,KACzC,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;SACE,OAAO,EAAE,QAAQ,GAAe,gBAAgB,EAAE,IAAI,SAAS,EAAE;SACjE,SAAS,GAAQ;QAClB,CAAA,EACC,CAAA;QAEL,GAAe,gBAAgB,EAAE,KAAK,MACrC,GAAc,GAAK,EAAW,QAAQ,EAAW,OAAO,EAAI,CAC9D;QACC,GAAe,gBAAgB,EAAE,SAAS,KACzC,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;SACE,OAAO,EACL,QACE,GAAe,aAAa,KAC3B,GAAe,gBAAgB,EAAE,GAAG,EAAE,GAAG,OAAO,GACrD;SACA,SAAS,GAAQ;QAClB,CAAA,EACC,CAAA;OAEN,EAAA,CAAA,EAEC,CAAA,CACF;;KACJ,CAAA,GAGJ,MACC,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,SAAS;OACT,YAAY;OACZ,WAAW;OACX,UAAU;OACV,OAAO;OACP,KAAK;MACP;gBAVF;OAiBE,kBAAC,QAAD;QACE,WAAU;QACV,eAAY;QACZ,OAAO;SAAE,SAAS;SAAe,YAAY;SAAU,KAAK;QAAE;kBAE7D,OAAe,KAAA,IAKZ,MAAgB,CAAC,KACnB,kBAAC,QAAD;SAAM,eAAY;SAA4B,OAAO,EAAE,SAAS,cAAc;mBAC5E,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;QAChB,CAAA,IAEN,kBAAA,GAAA,EAAA,UAAA;SACG,GAAK;SAAO;SAAE,GAAK,WAAW,IAAI,WAAW;QAC9C,EAAA,CAAA,IAXF,kBAAA,GAAA,EAAA,UAAA;SACG,GAAY,IAAY,MAAiB,OAAO;SAAG;SACnD,OAAe,IAAI,WAAW;QAC/B,EAAA,CAAA;OAUA,CAAA;OACN,kBAAC,QAAD,EAAA,UAAM,IAAO,CAAA;OACb,kBAAC,QAAD,EAAA,UAAA;QAAM;QACM;QACV,kBAAC,QAAD;SAAM,WAAU;SAAM,OAAO;UAAE,OAAO;UAAiB,YAAY;SAAI;mBACpE,GAAY;QACT,CAAA;OACF,EAAA,CAAA;OACN,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;OAC1B,kBAAC,QAAD,EAAA,UAAM,gBAAmB,CAAA;OACzB,kBAAC,UAAD;QACE,cAAW;QACX,OAAO;QACP,WAAW,MAAM,GAAW,SAAS,EAAE,OAAO,OAAO,EAAE,CAAC;QACxD,OAAO;SACL,UAAU;SACV,SAAS;SACT,YAAY;SACZ,QAAQ;SACR,cAAc;SACd,OAAO;SACP,QAAQ;QACV;kBAEC,GAAiB,KAAK,MACrB,kBAAC,UAAD;SAAgB,OAAO;mBACpB;QACK,GAFK,CAEL,CACT;OACK,CAAA;QAON,MAAgB,KAAO,MACvB,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,YAAY;SAAU,KAAK;QAAE;kBAA5D;SACE,kBAAC,GAAD;UACE,MAAM;UACN,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;UAC5B,eAAe,GAAS,KAAO,CAAC;UAChC,UAAU,MAAQ;SACnB,CAAA;SACD,kBAAC,QAAD;UAAM,WAAU;UAAM,OAAO,EAAE,SAAS,QAAQ;oBAAhD;WAAmD;WAC3C;WACL,OAAe,KAAA,IAAkC,KAAtB,OAAO;UAC/B;;SACN,kBAAC,GAAD;UACE,MAAM;UACN,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;UAC7B,eAAe,GAAS,KAAO,CAAC;UAChC,UAAU,OAAe,KAAA,IAAiC,CAAC,KAAtB,MAAQ;SAC9C,CAAA;QACE;;MAEJ;OAEJ;;GACF,CAAA;GAIP,kBAAC,IAAD;IACE,MAAM;IACN,eAAe,EAAoB,EAAK;IACxC,SAAS;IACT,SAAS;IACT,iBAAiB,GAAqB,GAAQ,GAAQ,GAAQ,QAAQ;GACvE,CAAA;GAED,kBAAC,IAAD;IACE,MAAM;IACN,SAAS,GAAmB;IAC5B,eAAe,EAAgB,EAAK;IACpC,QAAQ,OAAO,MAAS;KACtB,IAAI,CAAC,GAAM,MAAM,CAAC,GAAc,IAAI;KACpC,IAAM,IAAW,MAAM,GAAmB,YAAY;MACpD,WAAW,EAAa;MACxB,aAAa;MACb,SAAS,EAAK;MACd,MAAM,EAAE,IAAI,EAAK;MACjB,SAAS;MACT,SAAS;MACT,GAAI,KAAiB,EAAE,MAAM,GAAe,IAAI,CAAC;MACjD,WAAW;MACX,YAAY;KACd,CAAC,GACK,IAAY,OAAO,EAAS,KAAK,MAAM,EAAE;KAC/C,IAAI,GAAW;MACb,IAAM,IAAO,IAAI,gBAAgB,CAAY;MAE7C,AADA,EAAK,IAAI,QAAQ,CAAS,GAC1B,EAAgB,CAAI;KACtB;KACA,EAAgB,EAAK;IACvB;GACD,CAAA;GAGD,kBAAC,IAAD;IACE,MAAM,MAAgB;IACtB,eAAe,MAAS,CAAC,KAAQ,EAAe,IAAI;IACpD,OAAO,UAAU,EAAI,GAAc,IAAI;IACvC,aACE,GAAa,eACT,EAAY,eACZ,KACE,wCAAwC,EAAI,GAAc,IAAI,EAAE,YAAY,EAAE,2CAC9E,wCAAwC,EAAI,GAAc,IAAI,EAAE,YAAY,EAAE;IAEtF,cAAa;IACb,WAAW;IACX,aAAA;GACD,CAAA;GAEA,GAAY,SAAS,KACpB,kBAAC,IAAD;IACe;IACI;IACjB,oBAAoB,EAAO;IAC3B,oBAAoB,EAAO;IACrB;IACN,QAAQ;IACI;IACZ,eAAe,EAAgB,CAAC,CAAC;IACjC,uBAAuB,EAAgB,CAAC,CAAC;IAC1B;GAChB,CAAA;GAGF,KAAe,KACd,kBAAC,OAAD;IACE,OAAO;KAAE,UAAU;KAAS,KAAK,EAAY;KAAG,MAAM,EAAY;KAAG,QAAQ;IAAI;IACjF,UAAU,MAAM,EAAE,gBAAgB;cAFpC,CAIE,kBAAC,OAAD;KACE,OAAO;MACL,YAAY;MACZ,QAAQ;MACR,cAAc;MACd,SAAS;MACT,WAAW;MACX,OAAO;KACT;eARF;MAUG,MACC,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;OAC3B,eAAe;QAEb,AADA,GAAW,EAAY,GAAG,GAC1B,EAAe,IAAI;OACrB;iBACD;MAES,CAAA;MAEZ,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;OAChC,eAAe;QAEb,AADA,GAAiB,EAAY,GAAG,GAChC,EAAe,IAAI;OACrB;iBACD;MAES,CAAA;MACT,MACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA,GACnB,kBAAC,GAAD;OACE,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC7B,QAAA;OACA,eAAe;QACb,IAAM,IAAK,EAAO,OAAO,MAAM,GAAG,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO,GACrE,IAAK,IAAK,OAAO,EAAY,IAAI,MAAO,EAAE,IAAI;QAEpD,AADA,EAAe,IAAI,GACf,KAAI,EAAe,EAAE,KAAK,CAAC,CAAE,EAAE,CAAC;OACtC;iBACD;MAES,CAAA,CACV,EAAA,CAAA;KAED;QACL,kBAAC,OAAD;KACE,eAAe,EAAe,IAAI;KAClC,gBAAgB,MAAM;MAEpB,AADA,EAAE,eAAe,GACjB,EAAe,IAAI;KACrB;KACA,OAAO;MAAE,UAAU;MAAS,OAAO;MAAG,QAAQ;KAAG;IAClD,CAAA,CACE;;GAGN,MAAe,MAAW,KACzB,kBAAC,IAAD;IACU;IACI;IACZ,QAAQ;IACR,cAAc;KACZ,IAAM,IAAW,EAAO,SAAS,MAC9B,MAAM,CAAC,EAAE,WAAW,UAAU,KAAK,CAAC,EAAE,WAAW,YAAY,CAChE;KACA,OAAO,IAAW,OAAO,GAAQ,MAAa,EAAI,EAAO,QAAQ,IAAI,CAAC,IAAI,EAAI,EAAO,QAAQ,IAAI;IACnG,GAAG;IACH,iBAAiB;KACf,IAAM,IAAK,EAAO,OAAO,MAAM,GAAG,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO;KAC3E,OAAO,IAAK,OAAO,GAAQ,MAAO,EAAE,IAAI,KAAA;IAC1C,GAAG;IACH,QAAQ;IACA;IACR,SAAS;IACT,eAAe;IACf,eAAe;KAIb,IAAI,KAAU,IAAS;MACrB,IAAM,IAAK,EAAO,OAAO,MAAM,GAAG,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO;MAC3E,AAAI,KAAI,GAAe,OAAO,GAAQ,MAAO,EAAE,CAAC;KAClD;KACA,GAAW,IAAI;IACjB;IACA,SAAS,MAAU;KACjB,IAAM,IAAM;KAEZ,AADA,GAAW,IAAI,GACf,GAAiB,GAAK,EAAS,SAAwB,CAAC;IAC1D;IACA,SAAS,MAAS;KAGhB,GAAW,CAAe;KAC1B,IAAM,IAAK,EAAO,OAAO,MAAM,GAAG,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO;KAC3E,IAAI,CAAC,GAAI;KACT,IAAM,IAAK,OAAQ,EAAiC,MAAO,EAAE,GACvD,IAAmC,CAAC;KAC1C,KAAK,IAAM,KAAa,EAAO,OAAO,MAAM,GAAG;MAC7C,IAAM,IAAY,GAAoB,GAAQ,GAAiC,CAAS,GAClF,IAAY,GAAoB,GAAQ,IAAoC,CAAS,GACrF,IAAiB,GAA0B,GAAQ,GAAW,CAAS,GACvE,IAAiB,GAA0B,GAAQ,GAAW,CAAS;MAG7E,CAFuB,MAAmB,KAAK,OAAO,QAC/B,MAAmB,MAAM,MAAmB,KAAA,IAAY,OAAO,OAEpF,EAAQ,KAAa;KAEzB;KACA,IAAM,IAAe,GAAkB;MACrC;MACA,UAAU;MACV,QAAQ;MACR,OAAO;KACT,CAAC;KAID,IAHI,OAAO,KAAK,CAAY,EAAE,SAAS,MACrC,EAAQ,SAAS,IAEf,OAAO,KAAK,CAAO,EAAE,WAAW,GAAG;KACvC,IAAM,IAAmB,GAAwB,CAAY;KAC7D,GAAe,OACb;MAAE,UAAU;MAAI,MAAM;KAAQ,GAC9B,EACE,iBAAiB;MACf,EAAiB,SAAS,MAAW,EAAe,cAAc,CAAM,CAAC;KAC3E,EACF,CACF;IACF;GACD,CAAA;EAEA;;AAET;;;AClkEA,SAAgB,GAAc,GAA4B,GAAyC;CAGjG,IAAM,IAAW,MAAa,KAAA,KAAa,MAAa,QACpD,KAAA,IACA,OAAO,KAAa,WAClB,IACA,OAAO,KAAK,CAAQ,EAAE,KAAK,EAAE,KAAK,MAAM,CAAC,GAAG,EAAS,EAAE,CAAU;CAEvE,OAAO,GAAS;EACd,UAAU;GAAC;GAAe;GAAQ;EAAQ;EAC1C,eAAe,EAAU,GAAS,CAAS;EAC3C,SAAS,CAAC,CAAC,KAAU,CAAC,CAAC,KAAY,MAAa,SAAS,MAAa,KAAA;CACxE,CAAC;AACH;;;ACCA,SAAgB,GAAgB,EAC9B,eACA,aACA,iBACA,aACuB;CACvB,IAAM,IAAe,QAAc,OAAO,QAAQ,CAAY,GAAG,CAAC,CAAY,CAAC,GACzE,CAAC,GAAW,KAAgB,EAAS,CAAC;CAE5C,IAAI,EAAa,WAAW,GAAG,OAAO;CAEtC,IAAM,CAAC,GAAW,KAAe,EAAa;CAE9C,OACE,kBAAC,IAAD;EACE,OACE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAG,cAAc;IAA4B,QAAQ;IAAuB,SAAS;GAAS;aACrJ,EAAa,KAAK,CAAC,GAAK,IAAQ,MAAQ;IACvC,IAAM,IAAS,MAAQ;IACvB,OACE,kBAAC,UAAD;KAEE,UAAU,MAAM;MAEd,AADA,EAAE,gBAAgB,GAClB,EAAa,CAAG;KAClB;KACA,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,SAAS;MACT,UAAU;MACV,YAAY;MACZ,QAAQ;MACR,cAAc,aAAa,IAAS,kBAAkB;MACtD,cAAc;MACd,OAAO,IAAS,gBAAgB;MAChC,QAAQ;MACR,YAAY,IAAS,MAAM;KAC7B;eAnBF,CAqBG,EAAM,SAAS,QAAQ,kBAAC,GAAD;MAAa,MAAM,EAAM,QAAQ;MAAM,MAAM;KAAK,CAAA,GACzE,EAAI,EAAM,SAAS,IAAI,KAAK,CACvB;OAtBD,CAsBC;GAEZ,CAAC;EACE,CAAA;EAEP,aAAa;EACb,SAAQ;YAER,kBAAC,IAAD;GACc;GACF;GACV,UAAU;GACV,aAAa;GACL;EACT,CAAA;CACU,CAAA;AAEjB;AAEA,SAAS,GAAmB,EAC1B,eACA,aACA,aACA,gBACA,aAOC;CACD,IAAM,IAAc,GAAe,GAC7B,IAAa,GAAc,GAC3B,IAAY,GAAG,EAAW,GAAG,KAE7B,EAAE,MAAM,GAAa,WAAW,MAAc,GAAS;EAC3D,UAAU;GAAC;GAAe;GAAW;EAAQ;EAC7C,eAAe,EAAmB,GAAW,CAAC,CAAQ,CAAC;CACzD,CAAC,GAEK,IAAmB,GAAa,iBAAiB,EAAY,QAC7D,IAAe,EAAO,SAAS,IAC/B,IAAY,QAAc,GAAa,OAAO,CAAC,GAAG,CAAC,GAAa,GAAG,CAAC,GAEpE,IAAK,QACH,GAAc,OAAO,MAAM,EAAE,MAAM,MAAM,EAAa,OAAO,IAAI,CAAC,GAAG,OAAO,KAAK,MACvF,CAAC,CAAY,CACf,GAEM,IAAY,QAAc,EAAU,MAAM,GAAG,EAAE,GAAG,CAAC,CAAS,CAAC,GAE7D,EAAE,MAAM,GAAc,WAAW,MAAmB,GAAS;EACjE,UAAU;GAAC;GAAuB;GAAkB;EAAS;EAC7D,eACE,EAAa,GAAkB;GAC7B,UAAU;GACV,SAAS,CAAC;IAAE,OAAO;IAAI,IAAI;IAAM,OAAO;GAAU,CAAC;EACrD,CAAC;EACH,SAAS,EAAU,SAAS,KAAK,CAAC,CAAC;CACrC,CAAC,GAEK,IAAO,GAAc,QAAQ,CAAC,GAC9B,IAAO,GAAc,OAErB,IAAU,QACT,IACE,EAAa,OAAO,MAAM,EAC9B,QAAQ,MAAS;EAChB,IAAM,IAAI,EAAa,OAAO,IAAI,CAAI;EACtC,OAAO,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,gBAAgB;CAClE,CAAC,EACA,MAAM,GAAG,CAAC,IANa,CAAC,GAO1B,CAAC,CAAY,CAAC,GAEX,UAAsB;EAC1B,IAAM,IAAe,GAAwB,GAAQ,CAAgB;EACrE,IAAI,CAAC,GAAc;EACnB,IAAM,IAAc,EAAI,EAAY,SAAS,IAAI,KAAK,GAChD,IAAS,IAAI,gBAAgB;EAEnC,AADA,EAAO,IAAI,WAAW,KAAK,UAAU,CAAC;GAAE,OAAO;GAAI,IAAI;GAAM,OAAO;EAAU,CAAC,CAAC,CAAC,GACjF,EAAY,EAAa,OAAO;GAC9B,OAAO;GACP,MAAM,EAAY,SAAS,QAAQ,EAAa;GAChD,QAAQ,EAAO,SAAS;EAC1B,CAAC;CACH,GAEM,IAAQ;EAAE,UAAU;EAAM,OAAO;EAAiB,SAAS;CAAY;CAE7E,IAAI,KAAa,GAAgB,OAAO,kBAAC,OAAD;EAAK,OAAO;YAAO;CAAa,CAAA;CACxE,IAAI,CAAC,GAAc,OAAO,kBAAC,OAAD;EAAK,OAAO;YAAO;CAAsB,CAAA;CACnE,IAAI,EAAU,WAAW,GAAG,OAAO,kBAAC,OAAD;EAAK,OAAO;YAAO;CAAwB,CAAA;CAE9E,IAAM,IAAe,GAAwB,GAAQ,CAAgB;CAErE,OACE,kBAAC,OAAD;EAAK,OAAO,EAAE,SAAS,QAAQ;YAA/B;GACE,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MACZ,kBAAC,MAAD;KAAc,OAAO;eAClB,GAAiB,EAAa,OAAO,IAAI,CAAG,GAAI,GAAK,EAAO,QAAQ;IACnE,GAFK,CAEL,CACL,EACC,CAAA,EACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAK,KAAK,GAAK,MACd,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MAAQ;KACpB,IAAM,IAAQ,EAAa,OAAO,IAAI,CAAG;KAEzC,OADK,IAEH,kBAAC,MAAD;MAAc,OAAO;gBAClB,EAAY;OACX;OACA,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC;KACC,GAVK,CAUL,IAZa,kBAAC,MAAD,CAAe,GAAN,CAAM;IAcpC,CAAC,EACC,GAlBK,CAkBL,CACL,EACI,CAAA,CACF;;GACN,KAAgB,KAAe,EAAY,QAAQ,EAAK,UACvD,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,WAAW;IAAS;cACtD,kBAAC,GAAD;KAAK,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;KAAG,SAAS;eAAjD;MAAgE;MACpD,EAAY;MAAM;KACzB;;GACF,CAAA;GAEN,KAAgB,EAAK,SAAS,KAAK,KAAe,EAAY,SAAS,EAAK,UAC3E,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,WAAW;IAAS;cACtD,kBAAC,GAAD;KAAK,MAAK;KAAQ,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;KAAG,SAAS;eAAe;IAExE,CAAA;GACF,CAAA;EAEJ;;AAET;;;AC9MA,IAAM,KAAa;AAInB,SAAgB,GAAe,GAAoD;CACjF,IAAI,KAAO,QAAQ,MAAQ,IAAI,OAAO;CAEtC,IAAI,MAAQ,QAAQ,OAAO,EAAE,MAAM,OAAO;CAC1C,IAAI,MAAQ,QAAQ,OAAO,EAAE,MAAM,OAAO;CAG1C,IAAM,IAAU,GAAW,KAAK,CAAG;CACnC,IAAI,GAAS;EACX,IAAM,IAAI,SAAS,EAAQ,IAAI,EAAE;EAEjC,OADI,IAAI,IAAU;GAAE,MAAM;GAAM,OAAO;EAAE,IAClC;CACT;CAGA,IAAM,IAAS,SAAS,GAAK,EAAE;CAK/B,OAJI,CAAC,MAAM,CAAM,KAAK,OAAO,CAAM,MAAM,KAAO,IAAS,IAChD;EAAE,MAAM;EAAU,OAAO;CAAO,IAGlC;AACT;;;ACnBA,SAAgB,GAAU,EAAE,WAAQ,UAAuB;CACzD,IAAM,EAAE,UAAO,SAAM,GAAG,eAAY,IAAO,mBAAgB,GACrD,IAAU,MAAgB,UAAU,IAAK,KAAe,KACxD,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,IAAe,EAAuB,IAAI;CAGhD,QAAgB;EACd,IAAI,MAAY,GAAG;EACnB,IAAM,IAAK,EAAa;EACxB,IAAI,CAAC,GAAI;EAET,IAAM,IAAW,IAAI,gBAAgB,MAAY;GAC/C,KAAK,IAAM,KAAS,GAClB,EAAW,EAAM,YAAY,QAAQ,CAAO;EAEhD,CAAC;EAED,OADA,EAAS,QAAQ,CAAE,SACN,EAAS,WAAW;CACnC,GAAG,CAAC,CAAO,CAAC;CAGZ,IAAM,CAAC,GAAW,KAAgB,EAA0B,IAAI,GAC1D,IAAW,EAAuE,IAAI,GAEtF,IAAkB,QACf,EAAM,KAAK,MAAS;EAIzB,IAAM,IAAS,GADL,EAAK,SAAS,EAAK,QAAQ,SAAS,EAAK,KAAK,SAAS,EAAO,KACzC;EAE/B,OADI,KAAU,EAAO,SAAS,WAAiB,EAAO,QAC/C;CACT,CAAC,GACA,CAAC,GAAO,EAAO,KAAK,CAAC,GAElB,IAAc,GACjB,GAAe,MAAwB;EACtC,EAAE,eAAe;EACjB,IAAM,IAAQ,KAAa,EAAgB;EAC3C,EAAS,UAAU;GAAE;GAAO,QAAQ,EAAE;GAAS,YAAY,CAAC,GAAG,CAAK;EAAE;EAEtE,IAAM,KAAU,MAAmB;GACjC,IAAM,IAAI,EAAS;GACnB,IAAI,CAAC,KAAK,CAAC,EAAa,SAAS;GACjC,IAAM,IAAa,EAAa,QAAQ,sBAAsB,EAAE,OAC1D,IAAQ,EAAG,UAAU,EAAE,QACvB,IAAY,EAAE,WAAW,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC,GAClD,IAAa,IAAQ,IAAc,GACnC,IAAW,CAAC,GAAG,EAAE,UAAU;GAGjC,AAFA,EAAS,EAAE,SAAS,KAAK,IAAI,IAAK,EAAE,WAAW,EAAE,SAAS,CAAS,GACnE,EAAS,EAAE,QAAQ,KAAK,KAAK,IAAI,IAAK,EAAE,WAAW,EAAE,QAAQ,KAAK,CAAS,GAC3E,EAAa,CAAQ;EACvB,GAEM,UAAa;GAGjB,AAFA,EAAS,UAAU,MACnB,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,WAAW,CAAI;EAC9C;EAGA,AADA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,WAAW,CAAI;CAC3C,GACA,CAAC,GAAW,CAAe,CAC7B,GAEM,IAAQ,KAAa,EAAgB,GAGrC,IAAc,EAAO,SAAS,GAAY,EAAO,MAAM,IAAI,KAAA;CAEjE,OACE,kBAAC,OAAD;EACE,KAAK;EACL,WAAW,GAAG,QAAQ,KAAW,UAAU;EAC3C,OAAO;GAAE,KAAK,IAAM,GAAG,EAAI,MAAM,KAAA;GAAW,GAAG;EAAY;YAE1D,EAAM,KAAK,GAAM,MAChB,kBAAC,OAAD;GAEE,WAAW,GAAG,8BAA8B,KAAW,QAAQ;GAC/D,OAAO,IAAU,KAAA,IAAY,GAAU,EAAK,SAAS,EAAK,QAAQ,SAAS,EAAK,KAAK,SAAS,EAAO,OAAO,EAAM,IAAI,EAAK,WAAW,EAAK,SAAS;aAHtJ,CAKE,kBAAC,OAAD;IAAK,WAAW,GAAG,gCAAgC,KAAe,mCAAmC;cACnG,kBAAC,IAAD;KAAoB,MAAM;KAAW;IAAM,CAAA;GACxC,CAAA,GACJ,KAAa,CAAC,KAAW,IAAI,EAAM,SAAS,KAC3C,kBAAC,OAAD;IACE,WAAU;IACV,cAAc,MAAM,EAAY,GAAG,CAAC;GACrC,CAAA,CAEA;KAbE,CAaF,CACN;CACE,CAAA;AAET;AAEA,SAAS,GACP,GACA,GACA,GACA,GACqB;CACrB,IAAM,IAAS,GAAe,CAAK,GAC/B;CAUJ,OATA,AAOE,IAPE,CAAC,KAAU,EAAO,SAAS,WACtB,GAAG,EAAU,SACX,EAAO,SAAS,OAClB,OAAO,EAAO,MAAM,MAClB,EAAO,SAAS,SAClB,WAEA,YAEF;EAAE;EAAM;EAAU;CAAS;AACpC;AAEA,SAAS,GAAY,GAAoD;CACvE,IAAI,CAAC,GAAQ;CACb,IAAM,IAAS,GAAe,CAAM;CACpC,IAAI,GAAQ,SAAS,MAAM,OAAO;EAAE,QAAQ,GAAG,EAAO,MAAM;EAAK,UAAU;CAAS;CAGpF,IAAI,OAAO,KAAW,aAAa,EAAO,SAAS,IAAI,KAAK,EAAO,SAAS,MAAM,KAAK,EAAO,SAAS,GAAG,IACxG,OAAO;EAAE;EAAQ,UAAU;CAAS;AAGxC;;;ACtIA,SAAgB,GAAa,EAAE,WAAQ,UAA0B;CAC/D,IAAM,EAAE,UAAO,SAAM,OAAO;CAE5B,OACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO,EAAE,KAAK,IAAM,GAAG,EAAI,MAAM,KAAA,EAAU;YAE1C,EAAM,KAAK,GAAM,MAChB,kBAAC,IAAD;GAA4B,MAAM;GAAW;EAAM,GAA1B,CAA0B,CACpD;CACE,CAAA;AAET;;;ACVA,SAAS,GAAoB,GAAsC;CACjE,OAAO,EAAK,YAAY,EAAK;AAC/B;AAMA,SAAgB,GAAkB,GAAkB,GAA4B;CAC9E,IAAM,IAAY,GAAoB,CAAI;CAC1C,IAAI,GAAW;EACb,IAAI,EAAK,OAAO,OAAO,EAAI,EAAK,KAAK;EACrC,IAAM,IAAW,EAAI,OAAO,UAAU,MAAM,MAAM,EAAE,WAAW,CAAS;EACxE,IAAI,GAAU,SAAS,MAAM,OAAO,EAAI,EAAS,QAAQ,IAAI;EAC7D,IAAM,IAAS,EAAI,OAAO,SAAS;EAEnC,OADI,MAAe,EAAI,EAAO,QAAQ,WAAW,KAAK,EAAI,EAAO,QAAQ,IAAI,MACtE;CACT;CAEA,OADI,EAAK,UAAgB,EAAI,EAAK,QAAQ,IAAI,IACvC;AACT;AAKA,SAAgB,GAAiB,GAAkB,GAAwC;CACzF,IAAI,EAAK,MAAM,OAAO,EAAK;CAC3B,IAAM,IAAY,GAAoB,CAAI;CAC1C,IAAI,GAAW;EACb,IAAM,IAAW,EAAI,OAAO,UAAU,MAAM,MAAM,EAAE,WAAW,CAAS;EACxE,IAAI,GAAU,SAAS,MAAM,OAAO,EAAS,QAAQ;EACrD,IAAM,IAAS,EAAI,OAAO,SAAS;EACnC,IAAI,GAAQ,OAAO,EAAO,QAAQ;CACpC;AAEF;;;AClCA,SAAgB,GAAW,EAAE,WAAQ,UAAwB;CAC3D,IAAM,EAAE,aAAU,GACZ,CAAC,GAAW,KAAgB,EAAS,CAAC;CAI5C,OAFI,EAAM,WAAW,IAAU,OAG7B,kBAAC,OAAD;EACE,OAAO;GACL,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,UAAU;GACV,WAAW;GACX,YAAY;EACd;YARF,CAUE,kBAAC,OAAD;GACE,MAAK;GACL,OAAO;IACL,SAAS;IACT,YAAY;IACZ,KAAK;IACL,cAAc;IACd,SAAS;IACT,WAAW;GACb;aAEC,EAAM,KAAK,GAAM,MAAQ;IACxB,IAAM,IAAQ,GAAkB,GAAM,CAAG,GACnC,IAAO,GAAiB,GAAM,CAAG,GACjC,IAAS,MAAQ;IACvB,OACE,kBAAC,UAAD;KAEE,MAAK;KACL,iBAAe;KACf,UAAU,MAAM;MAEd,AADA,EAAE,gBAAgB,GAClB,EAAa,CAAG;KAClB;KACA,OAAO;MACL,UAAU;MACV,SAAS;MACT,YAAY;MACZ,KAAK;MACL,SAAS;MACT,YAAY;MACZ,QAAQ;MACR,QAAQ;MACR,UAAU;MACV,YAAY;MACZ,YAAY;MACZ,OAAO,IAAS,gBAAgB;MAChC,YAAY;MACZ,YAAY;KACd;KACA,eAAe,MAAM;MACnB,AAAK,MAAQ,EAAE,cAAc,MAAM,QAAQ;KAC7C;KACA,eAAe,MAAM;MACnB,AAAK,MAAQ,EAAE,cAAc,MAAM,QAAQ;KAC7C;eA7BF;MA+BG,KAAQ,kBAAC,GAAD;OAAa,MAAM;OAAM,MAAM;MAAK,CAAA;MAC5C;MACA,KACC,kBAAC,QAAD,EACE,OAAO;OACL,UAAU;OACV,MAAM;OACN,OAAO;OACP,QAAQ;OACR,QAAQ;OACR,YAAY;OACZ,cAAc;MAChB,EACD,CAAA;KAEG;OA7CD,CA6CC;GAEZ,CAAC;EACE,CAAA,GACL,kBAAC,OAAD;GAAK,MAAK;GAAW,OAAO,EAAE,SAAS,GAAG;aAGxC,kBAAC,IAAD;IAEE,MAAM,EAAM;IACZ,KAAK;KAAE,GAAG;KAAK,YAAY;IAAK;GACjC,GAHM,CAGN;EACE,CAAA,CACF;;AAET;;;AChGA,SAAgB,GAAa,EAAE,WAAQ,UAA0B;CAC/D,IAAM,EAAE,SAAM,UAAO,WAAW,IAAmB,IAAO,YAAS,SAAM,UAAO,gBAAa,GAGvF,IAAW;EAAE,GAAG;EAAK,YAAY;EAAM,GAAI,IAAU,EAAE,WAAQ,IAAI;CAAM,GAEzE,IAAQ,IACT,EAAsE,KACvE,KAAA,GACE,IAAW,IAAQ,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,IAAI,KAAA,GAEzC,IAAsC,IACxC;EAAE,MAAM,EAAI,EAAM,IAAI;EAAG,MAAM,EAAM;EAAM,KAAK,EAAM;CAAI,IAC1D,KAAA;CAEJ,OACE,kBAAC,IAAD;EACE,OAAO,EAAI,CAAI;EACf,aAAa,CAAC;EACd,MAAM;EACN,OAAO;EACG;EAGV,cAAc,EAAI,aAAa,IAAI;YAEnC,kBAAC,IAAD;GAAgB,OAAO;GAAO,KAAK;EAAW,CAAA;CACnC,CAAA;AAEjB;;;ACEA,SAAgB,GACd,GACA,GACA,GACqB;CACrB,IAAM,IAAU,EAAS,SAGnB,IAAO,GAAa,GAAY,MAAM,GAAS,MAAM,OAAO,GAM5D,IAAe,MAAS,aAAa,KAAK,IAC1C,IAA8B;EAClC,QAAQ,EAAS;EACjB,aAAa,EAAS;EACtB,MAAM,GAAa,GAAY,OAAO,GAAS,MAAM,GAAU,CAAW,CAAC;EAC3E,MAAM,GAAa,GAAY,MAAM,GAAS,MAAM,EAAY,QAAQ,IAAI,KAAK;EACjF;EACA,SAAS,GAAa,GAAY,SAAS,GAAS,SAAS,GAAa,GAAa,EAAS,WAAW,CAAC;EAK5G,MAAM,GAAa,GAAY,MAAM,GAAS,MAAM,GAAU,CAAW,CAAC;EAC1E,OAAO,GAAa,GAAY,OAAO,GAAS,OAAO,CAAY;EACnE,UAAU,GAAa,GAAY,UAAU,GAAS,UAAU,EAAI;EACpE,aAAa,GACX,GAAY,aACZ,GAAS,aAQT,GAAwB,CAAW,CACrC;CACF,GAGM,IAAW,GAAa,GAAY,gBAAgB,GAAS,gBAAgB,KAAA,CAA+B;CAClH,AAAI,MACF,EAAO,iBAAiB,GAExB,EAAO,OAAO,IAAW;CAI3B,IAAM,IAAiB,GAAY,YAAY,GAAS;CACxD,AAAI,EAAO,SAAS,aAClB,EAAO,WAAW,GAAoB,KAAkB,CAAC,GAAG,CAAW,IAC9D,MACT,EAAO,WAAW;CAIpB,IAAM,IAAc,GAAY,SAAS,GAAS;CAClD,AAAI,MACF,EAAO,QAAQ;CAIjB,IAAM,IAAW,GAAY,aAAa,EAAS;CAKnD,OAJI,MACF,EAAO,YAAY,IAGd;AACT;AAGA,SAAS,GAAgB,GAA+B,GAA8B,GAAmB;CAGvG,OAFI,KACA,KACG;AACT;AAEA,SAAS,GACP,GACsD;CAKtD,OADK,GAAqB,GAAa,QAAQ,IACxC,gBADkD;AAE3D;AAEA,SAAS,GAAU,GAAiC;CAIlD,OAHI,EAAO,QAAQ,eAAe,OAAO,KAAK,EAAO,QAAQ,WAAW,EAAE,SAAS,IAC1E,EAAO,QAAQ,cAEjB,EAAO,QAAQ;AACxB;AAKA,SAAgB,GAAU,GAAwB;CAChD,IAAI,EAAO,OAAO,IAAI,YAAY,GAAG,SAAS,aAC5C,OAAO;CAET,IAAI,EAAO,OAAO,IAAI,YAAY,GAAG,SAAS,aAC5C,OAAO;CAET,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,aAClB,OAAO,IAAO;CAElB;CACA,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,QAClB,OAAO,IAAO;CAElB;CAEA,IAAM,IAAK,GAAe,CAAM;CAEhC,OADI,IAAW,IAAK,SACb;AACT;AAIA,SAAgB,GAAa,GAAgB,GAAiC;CAC5E,IAAM,IAAQ,IAAI,IAAI,MAAM,QAAQ,CAAE,IAAI,IAAK,CAAC,CAAE,CAAC,GAC7C,IAAiB,CAAC;CACxB,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAI,EAAK,UAAU,GAAG;EACtB,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAC3B,MACD,EAAE,WAAW,EAAE,QACf,EAAE,SAAS,gBAAgB,OAC3B,EAAM,IAAI,CAAI,KAClB,EAAK,KAAK,CAAI;CAChB;CACA,OAAO;AACT;AAIA,SAAgB,GAAoB,GAAoB,GAAgC;CACtF,IAAM,IAAyB,EAAE,GAAG,EAAG;CA2CvC,OAzCA,AACE,EAAO,eAAa,GAAe,CAAM,GAE3C,AACE,EAAO,eAAa,GAAsB,GAAQ,MAAM,GAE1D,AACE,EAAO,kBAAgB,GAAuB,GAAQ,UAAU,QAAQ,WAAW,GAErF,AACE,EAAO,kBAAgB,GAAuB,GAAQ,aAAa,MAAM,GAazE,EAAO,iBACP,EAAO,iBACP,EAAO,kBAAkB,EAAO,kBAEhC,EAAO,gBAAgB,KAAA,IAEpB,EAAO,gBACN,EAAO,OAAO,IAAI,YAAY,MAChC,EAAO,eAAe,eAG1B,AACE,EAAO,oBAAkB,GAAoB,CAAM,GAErD,AACE,EAAO,iBAAe,GAAiB,CAAM,GAGxC;AACT;AAEA,SAAS,GAAoB,GAAoC;CAC/D,IAAM,IAAY,EAAO,OAAO,IAAI,WAAW;CAC/C,IAAI,KAAa,EAAU,SAAS,QAAQ,OAAO;CACnD,KAAK,IAAM,KAAQ;EAAC;EAAe;EAAY;EAAc;CAAS,GAAG;EACvE,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,QAAQ,OAAO;CACrC;AAEF;AAEA,SAAS,GAAiB,GAAoC;CAC5D,IAAM,IAAS,EAAO,OAAO,IAAI,QAAQ;CACzC,IAAI,KAAU,EAAO,SAAS,QAAQ,OAAO;AAE/C;AAEA,SAAS,GAAe,GAAoC;CAE1D,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,eAAe,CAAC,EAAE,MACpC,OAAO;CAEX;CAEA,IAAI,EAAO,OAAO,IAAI,YAAY,GAAG,SAAS,aAAa,OAAO;CAClE,IAAI,EAAO,OAAO,IAAI,YAAY,GAAG,SAAS,aAAa,OAAO;CAClE,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,aAAa,OAAO;CAC1C;AAEF;AAEA,SAAS,GAAsB,GAAgB,GAAkC;CAC/E,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAChC,IAAI,KAAK,EAAE,SAAS,GAClB,OAAO;CAEX;AAEF;AAEA,SAAS,GAAuB,GAAgB,GAAG,GAAqC;CACtF,KAAK,IAAM,KAAQ,EAAO,OAAO,MAAM,GAAG;EACxC,IAAM,IAAI,EAAO,OAAO,IAAI,CAAI;EAC3B,OACL;QAAK,IAAM,KAAK,GACd,IAAI,EAAE,SAAS,GAAG,OAAO;EAAA;CAE7B;AAEF;AAMA,SAAgB,GAAe,GAAoC;CACjE,IAAM,IAAM,GAAoB,CAAM;CACtC,OAAO,EAAI,SAAS,IAAI,EAAI,KAAK,KAAA;AACnC;AASA,SAAgB,GAAkB,GAAsB,GAA8C;CACpG,OAAO,EAAa,UAAU,MAAM,MAAM,EAAE,WAAW,CAAS;AAClE;;;ACzSA,SAAgB,GACd,GACA,GACA,GACA,GACyB;CACzB,IAAM,IAAkC;EACtC,GAAI,KAAY,CAAC;EACjB,GAAG;CACL;CACA,IAAI,MAAM,QAAQ,CAAU,GAAG;EAC7B,IAAM,IAAM,MAAM,QAAQ,CAAQ,IAC9B,EAAS,IAAI,MAAM,IACnB,GAAe,OAAO,CAAQ,GAAG,EAAW,MAAM;EACtD,IAAI,CAAC,KAAO,EAAI,WAAW,EAAW,QACpC,MAAU,MAAM,6BAA6B;EAE/C,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KACrC,EAAO,EAAW,MAAM,EAAI;CAEhC,OACE,EAAO,KAAc,MAAM,QAAQ,CAAQ,IAAI,EAAS,KAAK;CAE/D,OAAO;AACT;AAKA,SAAgB,GACd,GACA,GACS;CACT,KAAK,IAAM,KAAQ,GAAgB;EACjC,IAAM,IAAM,EAAY;EACxB,IAAI,KAAO,QAAQ,MAAQ,IAAI,OAAO;CACxC;CACA,OAAO;AACT;;;ACrBA,SAAgB,GACd,GACA,GACA,GACA,GACmB;CACnB,IAAM,CAAC,GAAQ,KAAa,QAAwC;EAClE,IAAM,IAAM,EAAO,WACb,IAAK,EAAO,SAAS,EAAO,SAC5B,IAAyC,CAAC;EAChD,IAAI,GAAK,aACF,IAAM,KAAK,EAAI,QAClB,AAAI,EAAI,WAAW,MAAM,OAEd,GAAI,OAAO,IAAI,CAAC,GAAG,SAAS,gBAGrC,EAAc,KAAK,EAAmB,KAJtC,EAAc,KAAK,EAAI,SAAS;EAQtC,OAAO;CACT,CAAC,GACK,IAAc,GAAe,GAC7B,EAAE,WAAQ,cAAW,UAAO,aAAU,GAAY;EACtD,aAAa,MAAqC,GAAa,EAAO,QAAQ,CAAO;EACrF,UAAU,OAAO,MAAqC;GAUpD,IAAM,IAAsB;IAC1B;IACA;IALyB,MAAM,QAAQ,CAAQ,IAC7C,EAAiB,EAAS,IAAI,MAAM,CAAC,IACrC;IAKF,EAAO;GACT;GAEA,MAAM,EAAY,cAAc,EAAE,UAAU,EAAU,CAAC;GACvD,IAAM,IAAY,EAAY,eAAwB,EAAE,UAAU,EAAU,CAAC,GAEvE,IAAyC;IAC7C,GAAG;IACH,IAAI,gBAAgB,OAAO,WAAW;IACtC,cAAc;GAChB;GA0BA,OAxBA,EAAY,eAAwB,EAAE,UAAU,EAAU,IAAI,MAAiB;IAC7E,IAAI,CAAC,KAAO,OAAO,KAAQ,UAAU,OAAO;IAG5C,IACE,WAAW,KACX,MAAM,QAAS,EAA8B,KAAK,KACjD,EAA6B,MAAM,SAAS,GAC7C;KACA,IAAM,IAAW,GACX,CAAC,GAAO,GAAG,KAAQ,EAAS;KAClC,OAAO;MACL,GAAG;MACH,OAAO,CACL;OAAE,GAAG;OAAO,MAAM,CAAC,GAAe,GAAG,EAAM,IAAI;MAAE,GACjD,GAAG,CACL;KACF;IACF;IAGA,OAAO;GACT,CAAC,GAEM,EAAE,aAAU;EACrB;EACA,UAAU,GAAM,GAAU,MAAQ;GAC3B,OAAK,WACV,KAAK,IAAM,CAAC,GAAK,MAAS,EAAI,WAC5B,EAAY,aAAa,GAAK,CAAI;EAEtC;EACA,iBAAiB;GAGf,IAAM,IAAK,EAAO,WACZ,IAAK,EAAO,SAAS,EAAO,SAC5B,IAAuC,CAAC;GAC9C,IAAI,GAAI,QACN,KAAK,IAAM,KAAK,EAAG,QACjB,AAAI,GAAI,WAAW,MAAM,OAEd,GAAI,OAAO,IAAI,CAAC,GAAG,SAAS,cACrC,EAAY,KAAK,EAAmB,IAEpC,EAAY,KAAK,KAJjB,EAAY,KAAK,EAAG,SAAS;GAQnC,EAAU,CAAW;EACvB;EACA,iBAAiB;GACf,IAAM,IAAqB,MAAM,QAAQ,CAAQ,IAC7C,EAAiB,EAAS,IAAI,MAAM,CAAC,IACrC;GACJ,EAAY,kBAAkB,EAC5B,UAAU;IAAC;IAAqB;IAAc;IAAoB,EAAO;GAAM,EACjF,CAAC;EACH;CACF,CAAC,GAEK,IAAW,EAAO,WAClB,IAAc,EAAO,SAAS,EAAO,SAOrC,IAAU,GAAgB,GAHT,GAAU,OAAO,QACrC,MAAM,GAAa,OAAO,IAAI,CAAC,GAAG,QACrC,KAAK,CAAC,CACgD;CAEtD,SAAS,EAAa,GAAc;EAClC,EAAE,eAAe,GACZ,KAEL,EADgB,GAAqB,GAAQ,EAAS,UAAU,EAAO,aAAa,CAC7E,CAAO;CAChB;CAEA,SAAS,EAAkB,GAAmB,GAAc;EAE1D,AADI,KAAO,EAAM,GACjB,GAAW,OAAmC;GAAE,GAAG;IAAO,IAAY;EAAI,EAAE;CAC9E;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;AC9JA,SAAgB,GAAa,EAAE,WAAQ,iBAAc,aAAU,aAA6B;CAC1F,IAAM,EAAE,WAAQ,aAAU,gBAAa,cAAW,UAAO,YAAS,iBAAc,yBAC9E,GAAY,GAAQ,GAAc,GAAU,CAAM,GAC9C,IAAa,GAAc;CAKjC,OAHI,CAAC,KAAY,EAAS,OAAO,WAAW,KACxC,CAAC,IAAoB,OAGvB,kBAAC,QAAD;EACE,UAAU;EACV,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,cAAc;EAChB;YARF;GAUG,EAAS,OAAO,KAAK,MAAc;IAClC,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAS;IAE9C,OADK,IAEH,kBAAC,OAAD;KAAqB,OAAO;MAAE,MAAM;MAAG,UAAU;KAAE;eAAnD,CACE,kBAAC,SAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO;OACP,cAAc;OACd,SAAS;OACT,YAAY;MACd;gBAEC,GAAiB,GAAO,GAAW,EAAO,QAAQ;KAC9C,CAAA,GACN,EAAY;MACX;MACA;MACA,OAAO,EAAO,MAAc;MAC5B,MAAM;MACN,WAAW,MAAQ,EAAkB,GAAW,CAAG;MACnD,QAAQ;MACR,QAAQ;KACV,CAAC,CACE;OArBK,CAqBL,IAvBY;GAyBrB,CAAC;GACD,kBAAC,GAAD;IACE,MAAK;IACL,MAAK;IACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAC5B,UAAU,KAAa,CAAC;cACzB;GAEI,CAAA;GACJ,KAAS,QACR,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAI,OAAO;KAAiB,WAAW;KAAY,cAAc;IAAE;cACzF,EAAM;GACH,CAAA;EAEJ;;AAEV;;;AC1DA,SAAgB,GAAc,EAAE,WAAQ,iBAAc,aAAU,aAA6B;CAC3F,IAAM,IAAc,EAAO,SAAS,EAAO,SAIrC,IAAa,GAAc,GAC3B,IAAmB,IAAkB,MAAM,EAAE,gBAAgB,GAC7D,EAAE,MAAM,GAAU,cAAW,YAAS,eAAY,eAAY,GAClE,GACA,GACA,EAAO,QACP,EAAO,aACP,EAAO,MACP,EAAO,KACT,GACM,IAAO,GAAU,QAAQ,CAAC,GAC1B,IAAO,GAAU,OAGjB,IAAU,EAAO,CAAI;CAC3B,EAAQ,UAAU;CAClB,IAAM,IAAU,EAAO,SAEjB,IADc,EAAO,gBACM,YAE3B,IAAc,EAAQ,IAItB,IAAc,KAAY,IAC5B,GAAmB,KAAA,GAAW,GAAa,EAAO,OAAO,IACzD,MACE,IAAK,IAAc,GAAe,CAAW,IAAI,KAAA,GAOjD,IAAgB,QAAyC;EAC7D,IAAM,oBAAM,IAAI,IAA0B;EAC1C,IAAI,CAAC,GAAa,OAAO;EACzB,KAAK,IAAM,KAAO,GAAS;GACzB,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;GACnC,KACL,EAAI,IACF,GACA,GAAoB;IAClB;IACA,WAAW;IACX,SAAS;IACT,QAAQ;IACR,UAAU,EAAO;IACjB,SAAS,EAAO;IAChB,YAAY,EAAQ;IACpB,QAAQ;GACV,CAAC,CACH;EACF;EACA,OAAO;CACT,GAAG;EAAC;EAAa;EAAS,EAAO;EAAU,EAAO;EAAS;CAAU,CAAC;CAEtE,IAAI,CAAC,GACH,OAAO,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAAsB,CAAA;CAGzG,IAAM,IACJ,kBAAC,IAAD;EAAsB;EAAsB;EAAwB;EAAkB;CAAS,CAAA,GAS3F,IACJ,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,SAAS;GAAS,gBAAgB;EAAW;YAA1G,CAFkB,GAAa,EAAY,UAAU,GAAkB,EAAO,MAG3E,KACC,kBAAC,QAAD;GACE,eAAY;GACZ,OAAM;aAEN,kBAAC,GAAD;IAAM,MAAK;IAAU,MAAK;IAAK,KAAA;cAAI;GAE7B,CAAA;EACF,CAAA,GAKR,kBAAC,GAAD;GACE,MACE,IACE,kBAAC,QAAD;IAAM,eAAY;IAA2B,OAAO,EAAE,SAAS,cAAc;cAC3E,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;GAChB,CAAA,IAEN,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;GAG7B,OAAM;GACN,MAAM;GACN,UAAU;GACV,eAAe;IAAE,EAAa;GAAG;EAClC,CAAA,CACE;;CAuCP,OApCI,IAEA,kBAAC,OAAD,EAAA,UAAA;EACG;EACA;EACD,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,SAAS;GAAQ;aAAG;EAErE,CAAA;CACF,EAAA,CAAA,IAIL,IAEA,kBAAC,OAAD,EAAA,UAAA;EACG;EACA;EACD,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,eAAe;IAAU,KAAK;IAAG,SAAS;GAAQ;aAC9E;IAAC;IAAG;IAAG;GAAC,EAAE,KAAK,MACd,kBAAC,OAAD,EAAa,OAAO;IAAE,QAAQ;IAAI,YAAY;IAAoB,cAAc;GAAE,EAAI,GAA5E,CAA4E,CACvF;EACE,CAAA;CACF,EAAA,CAAA,IAIL,EAAK,WAAW,IAEhB,kBAAC,OAAD,EAAA,UAAA;EACG;EACA;EACD,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,OAAO;IAAiB,SAAS;GAAS;aAAG;EAAoB,CAAA;CAC5F,EAAA,CAAA,IAKP,kBAAC,OAAD,EAAA,UAAA;EACG;EACA;EACD,kBAAC,OAAD;GAAK,OAAO,EAAE,WAAW,OAAO;aAC9B,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MAAQ;KACpB,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;KACxC,OACE,kBAAC,MAAD;MAAc,OAAO;gBAClB,IAAQ,GAAiB,GAAO,GAAK,EAAO,QAAQ,IAAI;KACvD,GAFK,CAEL;IAER,CAAC,EACC,CAAA,EACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAK,KAAK,GAAK,MAAW;KACzB,IAAM,IAAQ,IAAK,OAAQ,EAAgC,MAAO,EAAE,IAAI,IAClE,IAAO,KAAY,KAAe,IACpC,EAAY,QAAQ,QAAQ,CAAK,IACjC,KAAA;KACJ,OACA,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MAAQ;MACpB,IAAM,IAAa,MAAQ,GAMrB,IAAe,EAAc,IAAI,CAAG,GACpC,IAAW,IACb,EAAa,EAAI,IAAM,CAA8B,IACrD,OAAO,EAAI,MAAQ,EAAE;MAOzB,OACE,kBAAC,MAAD;OAAqB,OAFT,OAAO,KAAa,WAAW,IAAW,KAAA;OAE1B,OAAO;iBAChC,KAAc,IAMb,kBAAC,KAAD;QACQ;QACN,OAAO;SAAE,OAAO;SAAiB,gBAAgB;SAAQ,YAAY;QAAI;QACzE,eAAe,MAAO,EAAE,cAAc,MAAM,iBAAiB;QAC7D,eAAe,MAAO,EAAE,cAAc,MAAM,iBAAiB;kBAE5D;OACA,CAAA,IAEH;MAEA,GAlBK,CAkBL;KAER,CAAC,EACC,GAxCK,OAAQ,EAAgC,MAAS,CAAM,CAwC5D;IAEN,CAAC,EACI,CAAA,CACF;;EACJ,CAAA;CACF,EAAA,CAAA;AAET;;;ACtOA,SAAgB,GAAa,EAAE,WAAQ,iBAAc,aAAU,aAA6B;CAC1F,IAAM,IAAc,EAAO,SAAS,EAAO,SACrC,IAAa,GAAc,GAC3B,EAAE,MAAM,GAAU,cAAW,eAAY,GAC7C,GACA,GACA,EAAO,QACP,EAAO,aACP,EAAO,MACP,EAAO,KACT,GACM,IAAO,GAAU,QAAQ,CAAC,GAC1B,IAAO,GAAU,OACjB,IAAU,EAAO;CAEvB,IAAI,CAAC,GACH,OAAO,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAAsB,CAAA;CAGzG,IAAM,IACJ,kBAAC,IAAD;EAAsB;EAAsB;EAAwB;EAAkB;CAAS,CAAA;CAGjG,IAAI,GACF,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAErE,CAAA,CACF,EAAA,CAAA;CAIT,IAAI,GACF,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;GAAG,SAAS;EAAQ;YAAjF;GACE,kBAAC,OAAD,EAAK,OAAO;IAAE,QAAQ;IAAI,YAAY;IAAoB,cAAc;GAAE,EAAI,CAAA;GAC9E,kBAAC,OAAD,EAAK,OAAO;IAAE,QAAQ;IAAI,YAAY;IAAoB,cAAc;GAAE,EAAI,CAAA;GAC9E,kBAAC,OAAD,EAAK,OAAO;IAAE,QAAQ;IAAI,YAAY;IAAoB,cAAc;IAAG,OAAO;GAAM,EAAI,CAAA;EACzF;GACF,EAAA,CAAA;CAIT,IAAI,EAAK,WAAW,GAClB,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAS;YAAG;CAAoB,CAAA,CAC5F,EAAA,CAAA;CAIT,IAAM,IACJ,EAAQ,SAAS,IAAI,EAAY,QAAQ,iBAAiB,EAAQ,KAAK,EAAY,QAAQ;CAE7F,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;EAAS;YACpD,EAAK,KAAK,GAAK,MAMd,kBAAC,OAAD;GAEE,OAAO;IACL,SAAS;IACT,YAAY;IACZ,KAAK;IACL,SAAS;IACT,cAAc;GAChB;aAEA,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;IAAE;cAAnC,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAM,UAAU;MAAU,cAAc;MAAY,YAAY;KAAS;gBAbpF,OAAO,EAAA,YAAqB,WAAY,EAAI,MAA0B,QAgB7E,KAAgB,EAAY,OAAO,IAAI,CAAY,IACnD,EAAY;MACV,OAAO,EAAY,OAAO,IAAI,CAAY;MAC1C,WAAW;MACX,OAAO,EAAI;MACX,MAAM;MACN,QAAQ;MACR;MACF,QAAQ;KACR,CAAC,IACD,KAAgB,EAAI,MAAiB,OACnC,OAAO,EAAI,EAAa,IACxB;IACH,CAAA,GACJ,EAAQ,SAAS,YACT;KACL,IAAM,IAAU,EAAQ,MAAM,GAAG,CAAC,EAAE,QAAQ,MAAQ,EAAI,MAAQ,IAAI;KAEpE,OADI,EAAQ,WAAW,IAAU,OAE/B,kBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO;OACP,UAAU;OACV,cAAc;OACd,YAAY;MACd;gBAEC,EAAQ,KAAK,GAAK,MACjB,kBAAC,QAAD,EAAA,UAAA,CACG,IAAI,KAAK,OACT,EAAY,OAAO,IAAI,CAAG,IACvB,EAAY;OACV,OAAO,EAAY,OAAO,IAAI,CAAG;OACjC,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC,IACD,OAAO,EAAI,EAAI,CACf,EAAA,GAbK,CAaL,CACP;KACE,CAAA;IAET,GAAG,CACF;;EACF,GA7DE,OAAQ,EAAgC,MAAS,CAAG,CA6DtD,CAEN;CACE,CAAA,CACF,EAAA,CAAA;AAET;;;ACzIA,SAAgB,GAAc,EAAE,WAAQ,iBAAc,aAAU,aAA6B;CAC3F,IAAM,IAAc,EAAO,SAAS,EAAO,SACrC,IAAa,GAAc,GAC3B,EAAE,MAAM,GAAU,cAAW,eAAY,GAC7C,GACA,GACA,EAAO,QACP,EAAO,aACP,EAAO,MACP,EAAO,KACT,GACM,IAAO,GAAU,QAAQ,CAAC,GAC1B,IAAO,GAAU,OACjB,IAAU,EAAO,SACjB,IAAc,EAAO,OACrB,IAAc,GAAa,gBAAgB,EAAQ,IACnD,IAAa,GAAa,aAC1B,IAAW,GAAa,iBAAiB;CAE/C,IAAI,CAAC,GACH,OAAO,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAAsB,CAAA;CAGzG,IAAM,IAAiC;EACrC,SAAS;EACT,qBAAqB,4BAA4B,KAAY,IAAI,MAAM,IAAI;EAC3E,KAAK;CACP,GAEM,IACJ,kBAAC,IAAD;EAAsB;EAAsB;EAAwB;EAAkB;CAAS,CAAA;CAGjG,IAAI,GACF,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAErE,CAAA,CACF,EAAA,CAAA;CAIT,IAAI,GACF,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;YACT;GAAC;GAAG;GAAG;EAAC,EAAE,KAAK,MACd,kBAAC,OAAD,EAAa,OAAO;GAAE,QAAQ;GAAI,YAAY;GAAoB,cAAc;EAAG,EAAI,GAA7E,CAA6E,CACxF;CACE,CAAA,CACF,EAAA,CAAA;CAIT,IAAI,EAAK,WAAW,GAClB,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAS;YAAG;CAAoB,CAAA,CAC5F,EAAA,CAAA;CAIT,IAAM,IAAe,EAAQ,QAAQ,MAAM,MAAM,KAAe,MAAM,CAAU;CAEhF,OACE,kBAAC,OAAD,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;YACT,EAAK,KAAK,GAAK,MACd,kBAAC,OAAD;GAEE,OAAO;IACL,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,UAAU;GACZ;aAPF,CASE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,KAAK;KACL,cAAc,EAAa,SAAS,IAAI,6BAA6B;IACvE;cARF,CAUE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAM,YAAY;MAAK,UAAU;MAAU,cAAc;MAAY,YAAY;KAAS;eAC/G,KAAe,EAAY,OAAO,IAAI,CAAW,IAC9C,EAAY;MACV,OAAO,EAAY,OAAO,IAAI,CAAW;MACzC,WAAW;MACX,OAAO,EAAI;MACX,MAAM;MACN,QAAQ;MACR;MACF,QAAQ;KACR,CAAC,IACD,UAAU,IAAM;IACjB,CAAA,GACJ,KAAc,EAAI,MAAe,QAChC,kBAAC,GAAD;KAAM,MAAK;KAAQ,MAAK;eACrB,EAAY,OAAO,IAAI,CAAU,IAC9B,EAAY;MACV,OAAO,EAAY,OAAO,IAAI,CAAU;MACxC,WAAW;MACX,OAAO,EAAI;MACX,MAAM;MACN,QAAQ;MACR;MACF,QAAQ;KACR,CAAC,IACD,OAAO,EAAI,EAAW;IACtB,CAAA,CAEL;OACJ,EAAa,SAAS,KACrB,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAE;cAClF,EAAa,MAAM,GAAG,CAAC,EAAE,KAAK,MAAQ;KACrC,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;KAExC,OADK,IAEH,kBAAC,OAAD;MAEE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,KAAK;OACL,UAAU;MACZ;gBAPF,CASE,kBAAC,QAAD;OAAM,OAAO;QAAE,OAAO;QAAiB,YAAY;OAAE;iBAArD,CACG,EAAO,SAAS,EAAO,SAAS,OAAO,IAAI,CAAG,GAAG,SAAS,OACvD,OAAO,OAAO,EAAO,SAAS,EAAO,QAAQ,OAAO,IAAI,CAAG,EAAG,QAAS,IAAI,EAAE,KAC7E,GAAI,GAEJ;UACN,kBAAC,QAAD;OAAM,OAAO;QAAE,UAAU;QAAU,cAAc;QAAY,YAAY;OAAS;iBAC/E,EAAY;QACX;QACA,WAAW;QACX,OAAO,EAAI;QACX,MAAM;QACN,QAAQ;QACR;QACF,QAAQ;OACR,CAAC;MACG,CAAA,CACH;QAzBE,CAyBF,IA5BY;IA8BrB,CAAC;GACE,CAAA,CAEJ;KApFE,OAAQ,EAAgC,MAAS,CAAG,CAoFtD,CACN;CACE,CAAA,CACF,EAAA,CAAA;AAET;;;ACnKA,IAAM,KAAyB;AAkB/B,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA;CACA,IAAM,IAAQ,MAAM,QAAQ,CAAU,IAAI,IAAa,CAAC,CAAU,GAC5D,IAAQ,MAAM,QAAQ,CAAU,IAAI,GAAe,GAAU,EAAM,MAAM,IAAI,CAAC,CAAQ,GACtF,IAAiB,KAAS,IAE1B,IAAQ,GAAiB;EAC7B,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU,EAAE,mBAAgB;GAC1B,IAAM,IAAS,IAAI,gBAAgB;GACnC,IAAI,CAAC,GAAO,MAAU,MAAM,6BAA6B;GACzD,IAAM,IAAc,EAAM,KAAK,GAAI,OAAO;IAAE,OAAO;IAAI,IAAI;IAAM,OAAO,EAAM;GAAG,EAAE;GAgBnF,OAfA,EAAO,IAAI,WAAW,KAAK,UAAU,CAAW,CAAC,GACjD,EAAO,IAAI,WAAW,MAAM,GACxB,IACF,EAAO,IAAI,QAAQ,GAAG,EAAU,MAAM,IAC7B,OAAO,UAAY,OAK5B,QAAQ,KACN,uCAAuC,EAAU,wCACnD,GAEF,EAAO,IAAI,SAAS,OAAO,CAAc,CAAC,GACtC,KAAW,EAAO,IAAI,SAAS,CAAS,GACrC,GAAyB,aAAa,EAAU,GAAG,EAAO,SAAS,GAAG;EAC/E;EACA,mBAAmB,MACjB,EAAS,MAAM,WAAW,EAAS,KAAK,SAAS,KAAA;EACnD,kBAAkB,KAAA;EAClB,SACE,CAAC,CAAC,KAAgB,CAAC,CAAC,KAAY,CAAC,CAAC,KAAa,CAAC,CAAC,KAAc,MAAU;CAC7E,CAAC;CAaD,OAAO;EACL,MAZW,QACL,EAAM,MAAM,MAAM,SAAS,MAAM,EAAE,IAAI,KAAK,CAAC,GACnD,CAAC,EAAM,MAAM,KAAK,CAUlB;EACA,MAJW,EAAM,MAAM,MAAM,IAAI;EAKjC,eAAe,EAAM;EACrB,UAAU,CAAC,CAAC,EAAM;EAClB,iBAAiB,EAAM;EACvB,WAAW,EAAM;EACjB,SAAS,EAAM;CACjB;AACF;;;ACpFA,IAAM,KAAqB,eASrB,KAAa,KACb,KAAiB,KAGjB,KAAsB,KAGtB,KAAoB;AAI1B,SAAgB,GAAiB,EAAE,WAAQ,iBAAc,aAAU,aAAiC;CAClG,IAAM,EAAE,WAAQ,aAAU,gBAAa,cAAW,UAAO,YAAS,iBAAc,yBAC9E,GAAY,GAAQ,GAAc,GAAU,CAAM,GAC9C,IAAa,GAAc,GAE3B,CAAC,GAAQ,KAAa,EAAS,EAAc,GAC7C,IAAW,EAAuD,IAAI,GACtE,IAAU,EAA8B,IAAI,GAC5C,CAAC,GAAW,KAAgB,EAAS,EAAmB;CAoB9D,AAfA,QAAgB;EAEd,IAAM,IADO,EAAQ,SACA;EACrB,IAAI,CAAC,GAAQ;EACb,IAAM,UAAe;GACnB,IAAM,IAAM,KAAK,IAAI,IAAY,EAAO,eAAe,EAAiB;GACxE,EAAa,KAAK,IAAI,IAAqB,CAAG,CAAC;EACjD;EACA,EAAO;EACP,IAAM,IAAK,IAAI,eAAe,CAAM;EAEpC,OADA,EAAG,QAAQ,CAAM,SACJ,EAAG,WAAW;CAC7B,GAAG,CAAC,CAAC,GAGL,QAAgB;EACd,GAAW,MAAM,KAAK,IAAI,GAAG,CAAS,CAAC;CACzC,GAAG,CAAC,CAAS,CAAC;CAId,IAAM,IAAY,EAAO,CAAM;CAC/B,EAAU,UAAU;CACpB,IAAM,IAAuB,EAAO,CAAiB;CACrD,EAAqB,UAAU;CAC/B,IAAM,IAAgB,EAA2C,CAAC,CAAC,GAE7D,IAAS,SAAe;EAC5B,WAAW,MAAsB,EAAU,QAAQ;EACnD,WAAW,GAAmB,MAAmB;GAC/C,EAAqB,QAAQ,GAAW,CAAK;EAC/C;EACA,aAAa,MAAsB;GACjC,IAAM,IAAO,EAAc,QAAQ;GAC9B,MACa,EAAK,cAA2B,4CACjD,KAAa,GAAM,MAAM;EAC5B;CACF,IAAI,CAAC,CAAC;CAEN,QAAgB,GAAoB,IAAoB,CAAM,GAAG,CAAC,CAAM,CAAC;CAEzE,IAAM,IAAc,GACjB,MAAwB;EAEvB,AADA,EAAE,eAAe,GACjB,EAAS,UAAU;GAAE,QAAQ,EAAE;GAAS,aAAa;EAAO;EAE5D,IAAM,KAAU,MAAmB;GACjC,IAAI,CAAC,EAAS,SAAS;GACvB,IAAM,IAAQ,EAAS,QAAQ,SAAS,EAAG;GAE3C,EADa,KAAK,IAAI,GAAW,KAAK,IAAI,IAAY,EAAS,QAAQ,cAAc,CAAK,CAChF,CAAI;EAChB,GAEM,UAAa;GAGjB,AAFA,EAAS,UAAU,MACnB,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,WAAW,CAAI;EAC9C;EAGA,AADA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,WAAW,CAAI;CAC3C,GACA,CAAC,GAAQ,CAAS,CACpB;CAGA,IADI,CAAC,KAAY,EAAS,OAAO,WAAW,KACxC,CAAC,GAAa,OAAO;CAOzB,IAAM,IAAe,EAAS,OAAO,MAAM,MAAM;EAC/C,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAC;EACtC,OAAO,MAAU,EAAM,SAAS,UAAU,EAAM,SAAS;CAC3D,CAAC,GACK,IAAsB,CAAC,GACvB,IAAyB,CAAC;CAChC,KAAK,IAAM,KAAK,EAAS,QAAQ;EAC/B,IAAI,MAAM,GAAc;EACxB,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAC;EACjC,MACD,EAAM,SAAS,SACjB,EAAU,KAAK,CAAC,IAEhB,EAAa,KAAK,CAAC;CAEvB;CAEA,IAAM,KAAmB,MAAsB;EAC7C,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAS;EAE9C,OADK,IAEH,kBAAC,OAAD;GAAqB,MAAM,MAAO;IAAE,EAAc,QAAQ,KAAa;GAAI;aACxE,EAAY;IACX;IACA;IACA,OAAO,EAAO,MAAc;IAC5B,MAAM;IACN,WAAW,MAAQ,EAAkB,GAAW,CAAG;IACnD,QAAQ;IACR,QAAQ;GACV,CAAC;EACE,GAVK,CAUL,IAZY;CAcrB;CAEA,OACE,kBAAC,OAAD;EACE,KAAK;EACL,OAAO;GACL,YAAY;GACZ,WAAW;GACX,YAAY;GACZ,SAAS;GACT,eAAe;GACf,QAAQ,GAAG,EAAO;EACpB;YATF,CAWE,kBAAC,OAAD;GACE,aAAa;GACb,OAAM;GACN,OAAO;IACL,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,YAAY;GACd;aAEA,kBAAC,OAAD,EAAK,OAAO;IAAE,OAAO;IAAI,QAAQ;IAAG,cAAc;IAAK,YAAY;GAAgB,EAAI,CAAA;EACpF,CAAA,GAEL,kBAAC,QAAD;GACE,UAAU;GACV,YAAY,MAAM;IAGhB,CAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,YACxC,EAAE,eAAe,GACjB,EAAE,cAAc,cAAc;GAElC;GACA,OAAO;IACL,MAAM;IACN,WAAW;IACX,SAAS;IACT,eAAe;IACf,KAAK;IACL,SAAS;GACX;aAjBF;IAmBG,EAAU,SAAS,KAClB,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,UAAU;MAAQ,YAAY;MAAU,KAAK;MAAG,YAAY;KAAE;eAC1F,EAAU,IAAI,CAAe;IAC3B,CAAA;IAGN,KAAgB,EAAY,OAAO,IAAI,CAAY,KAClD,kBAAC,OAAD;KACE,OAAO;MAAE,MAAM;MAAG,WAAW;MAAG,SAAS;KAAO;KAChD,MAAM,MAAO;MACX,EAAc,QAAQ,KAAgB;KACxC;eAEC,EAAY;MACX,OAAO,EAAY,OAAO,IAAI,CAAY;MAC1C,WAAW;MACX,OAAO,EAAO,MAAiB;MAC/B,MAAM;MACN,WAAW,MAAQ,EAAkB,GAAc,CAAG;MACtD,QAAQ;MACR,MAAM;MACN,QAAQ;KACV,CAAC;IACE,CAAA;IAGP,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,UAAU;MAAQ,YAAY;MAAU,KAAK;MAAG,YAAY;KAAE;eAA7F,CACG,EAAa,IAAI,CAAe,GACjC,kBAAC,OAAD;MACE,OAAO;OACL,YAAY;OACZ,SAAS;OACT,YAAY;OACZ,KAAK;MACP;gBANF,CAQE,kBAAC,QAAD;OAAM,OAAO;QAAE,UAAU;QAAM,OAAO;OAAgB;iBAAtD;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAO;QAAO,CAAA;QAC9B,kBAAC,QAAD;SAAM,WAAU;mBAAO;QAAO,CAAA;QAAC;OAC3B;UACN,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAC5B,UAAU,KAAa,CAAC;iBACzB;MAEI,CAAA,CACF;OACF;;IAEJ,KAAS,QACR,kBAAC,KAAD;KAAG,OAAO;MAAE,UAAU;MAAI,OAAO;MAAiB,QAAQ;MAAG,YAAY;KAAE;eAAI,EAAM;IAAW,CAAA;GAE9F;IACH;;AAET;;;AChPA,IAAM,KAA6B,IAC7B,KAAwB;AAE9B,SAAS,GAAM,GAAsB;CACnC,IAAM,IAAK,EAAI;CACf,OAAO,KAAM,OAAoB,KAAb,OAAO,CAAE;AAC/B;AAEA,SAAS,GAAgB,GAAuB;CAC9C,OAAO,EAAI,iBAAoB;AACjC;AAEA,SAAgB,GAAiB,EAAE,WAAQ,iBAAc,aAAU,aAA6B;CAC9F,IAAM,IAAc,EAAO,SAAS,EAAO,SACrC,EACJ,MAAM,GACN,SACA,kBACA,aACA,oBACA,cACA,eACE,GACF,GACA,GACA,EAAO,QACP,EAAO,aACP,EAAO,UAAU,YACjB,EAAO,KACT,GACM,IAAW,EAAO,UAClB,IAAS,GAAU,UAAU,IAM7B,KAAc,EAAO,QAAQ,IAAI,SAAS,MAAM,GAChD,IAAO,QACJ,IAAa,CAAC,GAAG,CAAO,EAAE,QAAQ,IAAI,GAC7C,CAAC,GAAS,CAAU,CACtB,GAEM,IAAa,GAAU,eAAe,QACtC,IAAY,EAAuB,IAAI,GACvC,IAAiB,EAAuB,IAAI,GAC5C,IAAkB,EAAO,EAAK,GAC9B,IAAe,EAAO,CAAC,GACvB,IAAiB,EAAsB,IAAI,GAC3C,IAAgB,EAAsB,IAAI,GAC1C,IAAkB,EAAO,EAAI,GAG7B,IAAmB,EAA2D,IAAI;CAqBxF,AAnBA,QAAgB;EAMd,AALA,EAAgB,UAAU,IAC1B,EAAa,UAAU,GACvB,EAAe,UAAU,MACzB,EAAc,UAAU,MACxB,EAAgB,UAAU,IAC1B,EAAiB,UAAU;CAC7B,GAAG,CAAC,CAAQ,CAAC,GAIb,SAAsB;EAChB,CAAC,KAAmB,CAAC,EAAU,YACnC,EAAiB,UAAU;GACzB,cAAc,EAAU,QAAQ;GAChC,WAAW,EAAU,QAAQ;EAC/B;CACF,GAAG,CAAC,CAAe,CAAC,GAEpB,SAAsB;EACpB,IAAM,IAAK,EAAU;EACrB,IAAI,CAAC,KAAM,EAAK,WAAW,GAAG;GAC5B,EAAa,UAAU,EAAK;GAC5B;EACF;EAEA,IAAM,IAAU,GAAM,EAAK,EAAE,GACvB,IAAS,GAAM,EAAK,EAAK,SAAS,EAAE,GACpC,IAAgB,CAAC,EAAgB,SACjC,IAAO,EAAK,SAAS,EAAa,WAAW,EAAa,UAAU,GAKpE,IACJ,MACE,KAAc,MAAY,EAAe,WAAW,MAAW,EAAc,WAC5E,CAAC,KAAc,MAAW,EAAc,WAAW,MAAY,EAAe,UAC7E,IAAiB,KAAQ,CAAC;EAEhC,IAAI,GAMF,AAHI,KAAc,MAAe,aAC/B,EAAG,YAAY,EAAG,eAEpB,EAAgB,UAAU;OACrB,IAAI,KAAkB,EAAiB,SAAS;GAKrD,IAAM,IAAS,EAAiB;GAIhC,AAHI,MACF,EAAG,YAAY,EAAO,aAAa,EAAG,eAAe,EAAO,gBAE9D,EAAiB,UAAU;EAC7B,OAAO,AAAI,KAIL,KAAc,EAAgB,YAChC,EAAG,YAAY,EAAG;EAMtB,AAFA,EAAa,UAAU,EAAK,QAC5B,EAAe,UAAU,GACzB,EAAc,UAAU;CAC1B,GAAG;EAAC;EAAM;EAAY;CAAU,CAAC;CAEjC,IAAM,IAAW,QAAkB;EACjC,IAAM,IAAK,EAAU;EAChB,MACL,EAAgB,UACd,EAAG,eAAe,EAAG,YAAY,EAAG,eAAe;CACvD,GAAG,CAAC,CAAC;CAML,QAAgB;EACd,IAAM,IAAW,EAAe;EAEhC,IADI,CAAC,KACD,OAAO,uBAAyB,KAAa;EACjD,IAAM,IAAK,IAAI,sBACZ,MAAY;GACX,KAAK,IAAM,KAAK,GACd,AAAI,EAAE,kBAAkB,KAAY,CAAC,KACnC,EAAc;EAGpB,GACA;GACE,MAAM,EAAU,WAAW;GAC3B,YAAY,GAAG,GAAsB;GACrC,WAAW;EACb,CACF;EAEA,OADA,EAAG,QAAQ,CAAQ,SACN,EAAG,WAAW;CAC7B,GAAG;EAAC;EAAU;EAAiB;CAAa,CAAC;CAE7C,IAAM,CAAC,GAAW,KAAgB,EAAwB,IAAI;CAE9D,IAAI,CAAC,GACH,OAAO,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAQ;YAAG;CAAsB,CAAA;CAGzG,IAAM,IAAiB,EAAO,WAAW,UACnC,CAAC,GAAU,aAAa,EAAE,OAAO,OAAO,GAExC,IACJ,EAAI,EAAY,SAAS,WAAW,KAAK,EAAI,EAAY,SAAS,IAAI,KAAK,EAAO,QAY9E,IAAc,IAClB,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,gBAAgB;GAAU,SAAS;EAAQ;YACxE,kBAAC,GAAD;GACE,MAAK;GACL,eAAe,EAAc;GAC7B,UAAU;GACV,MAAM,IAAkB,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IAAI,KAAA;aAE/C,IAAkB,aAAa;EAC7B,CAAA;CACF,CAAA,IACH,MACE,IAAc,kBAAC,OAAD;EAAK,KAAK;EAAgB,eAAY;EAAO,OAAO,EAAE,QAAQ,EAAE;CAAI,CAAA,GAEpF;CACJ,IAAI,GACF,IAAU,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAG;YAAG;CAAoB,CAAA;MAC9F,IAAI,GACT,IACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;GAAG,SAAS;EAAG;YAA5E,CACE,kBAAC,OAAD,EAAK,OAAO;GAAE,QAAQ;GAAI,YAAY;GAAoB,cAAc;EAAE,EAAI,CAAA,GAC9E,kBAAC,OAAD,EAAK,OAAO;GAAE,QAAQ;GAAI,YAAY;GAAoB,cAAc;EAAE,EAAI,CAAA,CAC3E;;MAEF,IAAI,EAAK,WAAW,GACzB,IACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAM,OAAO;GAAiB,SAAS;EAAG;YAAG;CAAqB,CAAA,CAC1F,EAAA,CAAA;MAEC;EACL,IAAM,KAAe,GAAc,MAAgB;GACjD,IAAM,IAAK,GAAM,CAAG,KAAK,OAAO,CAAG;GACnC,OACE,kBAAC,IAAD;IAEO;IACL,OAAO;IACM;IACb,YAAY,EAAO;IACL;IACJ;IACA;IACM;IACV;IACN,WAAW,MAAc;IACzB,mBAAmB,EAAa,CAAE;IAClC,oBAAoB,EAAa,IAAI;IACrC,eAAe,EAAa,IAAI;IACxB;IACR,YAAY,GAAgB,CAAG;GAChC,GAhBM,CAgBN;EAEL,GAIM,IAAY,GAAU,YACxB;EACJ,IAAI,GAAW;GACb,IAAM,IAAkF,CAAC;GAWzF,AAVA,EAAK,SAAS,GAAK,MAAQ;IACzB,IAAM,IAAM,EAAgB,IACtB,IAAM,GAAO,CAAE,GACf,IAAO,EAAO,EAAO,SAAS;IACpC,AAAI,CAAC,KAAQ,EAAK,QAAQ,IACxB,EAAO,KAAK;KAAE;KAAK,OAAO,GAAS,CAAE;KAAG,MAAM,CAAC;MAAO;MAAgB;KAAI,CAAC;IAAE,CAAC,IAE9E,EAAK,KAAK,KAAK;KAAO;KAAgB;IAAI,CAAC;GAE/C,CAAC,GACD,IAAW,EAAO,KAAK,MACrB,kBAAC,OAAD;IAA0B,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAE;cAApF,CACE,kBAAC,IAAD;KAAW,OAAO,EAAE;KAAO,OAAO,EAAE,KAAK;IAAS,CAAA,GACjD,EAAE,KAAK,KAAK,EAAE,QAAK,aAAU,EAAY,GAAK,CAAG,CAAC,CAChD;MAHK,OAAO,EAAE,KAGd,CACN;EACH,OACE,IAAW,EAAK,KAAK,GAAK,MAAQ,EAAY,GAAgB,CAAG,CAAC;EAEpE,IACE,kBAAA,GAAA,EAAA,UAAA;GACG;GACA;GACA;EACD,EAAA,CAAA;CAEN;CA6BA,OA3BI,IAEA,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO;EACT;YARF;GAUE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAa,cAAc;KAA4B,YAAY;IAAiB;cACzG,kBAAC,MAAD;KAAI,OAAO;MAAE,QAAQ;MAAG,UAAU;MAAM,YAAY;KAAI;eAAI;IAAU,CAAA;GACnE,CAAA;GACL,kBAAC,OAAD;IACE,KAAK;IACK;IACV,OAAO;KAAE,MAAM;KAAG,WAAW;KAAQ,SAAS;KAAI,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAE;cAElG;GACE,CAAA;GACL,kBAAC,IAAD;IAA0B;IAAsB;IAAwB;IAAkB;GAAS,CAAA;EAChG;MAKP,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;YAAI;CAAa,CAAA,GAChF,kBAAC,IAAD;EAA0B;EAAsB;EAAwB;EAAkB;CAAS,CAAA,CAChG,EAAA,CAAA;AAET;AAyBA,SAAS,GAAc,EACrB,QACA,UACA,gBACA,eACA,iBACA,aACA,aACA,mBACA,SACA,cACA,gBACA,iBACA,YACA,WACA,gBAAa,MACQ;CACrB,IAAM,IAAc,GAAe,GAC7B,IAAa,GAAc,GAC3B,CAAC,GAAU,KAAe,EAAkC,CAAC,CAAC,GAE9D,IAAa,QAAkB;EACnC,EAAY,kBAAkB,EAAE,UAAU;GAAC;GAAqB;GAAc;GAAU;EAAU,EAAE,CAAC;CACvG,GAAG;EAAC;EAAa;EAAc;EAAU;CAAU,CAAC,GAE9C,IAAY,GAAY;EAC5B,aAAa,MAAkC,EAAa,GAAY,GAAO,CAAI;EACnF,iBAAiB;GAGf,AAFA,EAAW,GACX,EAAM,QAAQ,SAAS,GACvB,EAAQ;EACV;EACA,UAAU,MAAe,EAAM,MAAM,EAAI,OAAO;CAClD,CAAC,GAEK,IAAY,GAAY;EAC5B,kBAAkB,EAAa,GAAY,CAAK;EAChD,iBAAiB;GAEf,AADA,EAAW,GACX,EAAM,QAAQ,SAAS;EACzB;EACA,UAAU,MAAe,EAAM,MAAM,EAAI,OAAO;CAClD,CAAC,GAEK,IAAY,QAAkB;EAClC,IAAM,IAAgC,CAAC;EACvC,KAAK,IAAM,KAAK,GAAgB,EAAK,KAAK,EAAI,MAAM;EAEpD,AADA,EAAY,CAAI,GAChB,EAAY;CACd,GAAG;EAAC;EAAK;EAAgB;CAAW,CAAC,GAE/B,IAAoB,GAAa,GAAmB,MAAiB;EACzE,GAAa,OAAU;GAAE,GAAG;IAAO,IAAY;EAAI,EAAE;CACvD,GAAG,CAAC,CAAC,GAEC,IAAY,GAAU,YACtB,IAAY,GAAU,YACtB,IAAe,GAAU,eACzB,IAAe,GAAU,eACzB,IAAc,GAAU,cACxB,IAAiB,GAAU,iBAC3B,KAAc,GAAU;CA6D9B,OA3DI,IAEA,kBAAC,OAAD;EACE,OAAO;GACL,cAAc;GACd,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,SAAS;GACT,eAAe;GACf,KAAK;EACP;YATF,CAWG,EAAe,KAAK,MAAc;GACjC,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAS;GAE9C,OADK,IAEH,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,OAAO;KAAE,UAAU;KAAI,OAAO;KAAiB,cAAc;KAAG,SAAS;IAAQ;cACrF,GAAiB,GAAO,GAAW,EAAO,QAAQ;GAC9C,CAAA,GACN,EAAY;IACX;IACA;IACA,OAAO,EAAS,MAAc;IAC9B,MAAM;IACN,WAAW,MAAQ,EAAkB,GAAW,CAAG;IACnD,QAAQ;IACR,QAAQ;GACV,CAAC,CACE,EAAA,GAbK,CAaL,IAfY;EAiBrB,CAAC,GACD,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAG,YAAY;GAAE;aAA3E,CACE,kBAAC,GAAD;IACE,MAAK;IACL,MAAK;IACL,MAAM,EAAU,YAAY,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IAAI,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;IAC3E,eAAe,EAAU,OAAO,CAAQ;IACxC,UAAU,EAAU;cAEnB,EAAU,YAAY,YAAY;GAChC,CAAA,GACL,kBAAC,GAAD;IACE,MAAK;IACL,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;IACzB,eAAe;KAEb,AADA,EAAY,CAAC,CAAC,GACd,EAAa;IACf;IACA,UAAU,EAAU;cACrB;GAEI,CAAA,CACF;IACF;MAKP,kBAAC,OAAD;EACE,WAAU;EACV,mBAAiB,KAAc,KAAA;EAC/B,OAAO;GACL,cAAc;GACd,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,YAAY;GACZ,SAAS,IAAa,KAAM;EAC9B;EACA,eAAe,MAAO,EAAE,cAAc,MAAM,cAAc;EAC1D,eAAe,MAAO,EAAE,cAAc,MAAM,cAAc;YAZ5D;GAcE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK;KACL,UAAU;KACV,UAAU;KACV,OAAO;IACT;cARF;KAUG,KAAa,EAAI,MAAc,QAAQ,EAAY,OAAO,IAAI,CAAS,KACtE,kBAAC,QAAD;MAAM,OAAO,EAAE,YAAY,IAAI;gBAC5B,EAAY;OACX,OAAO,EAAY,OAAO,IAAI,CAAS;OACvC,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC;KACG,CAAA;KAEP,KAAa,EAAI,MAAc,QAAQ,EAAY,OAAO,IAAI,CAAS,KACtE,kBAAC,GAAD;MAAM,MAAK;MAAQ,MAAK;gBACrB,EAAY;OACX,OAAO,EAAY,OAAO,IAAI,CAAS;OACvC,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC;KACG,CAAA;KAEP,KAAkB,EAAI,MAAmB,QAAQ,EAAY,OAAO,IAAI,CAAc,KACrF,kBAAC,IAAD;MAAe,OAAO,EAAI;MAAiB,OAAO,EAAY,OAAO,IAAI,CAAc;KAAK,CAAA;KAE7F,MAAe,EAAI,OAAgB,QAAQ,EAAY,OAAO,IAAI,EAAW,KAC5E,kBAAC,GAAD;MAAM,MAAK;MAAQ,MAAK;gBACrB,EAAY;OACX,OAAO,EAAY,OAAO,IAAI,EAAW;OACzC,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC;KACG,CAAA;KAEP,KAAe,EAAI,MAAgB,QAAQ,EAAY,OAAO,IAAI,CAAW,KAC5E,kBAAC,QAAD,EAAA,UAAA;MAAM;MACD;MACF,EAAY;OACX,OAAO,EAAY,OAAO,IAAI,CAAW;OACzC,WAAW;OACX,OAAO,EAAI;OACX,MAAM;OACN,QAAQ;OACR;OACF,QAAQ;MACR,CAAC;KACG,EAAA,CAAA;KAEP,CAAC,KACA,kBAAC,OAAD;MAAK,OAAO;OAAE,YAAY;OAAQ,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;gBAAhF,CACE,kBAAC,GAAD;OAAS,MAAM;OAAI,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAAG,SAAS;OAAW,OAAM;MAAQ,CAAA,GACpF,kBAAC,GAAD;OACE,MAAM;OACN,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC7B,eAAe,EAAU,OAAO;OAChC,OAAM;MACP,CAAA,CACE;;IAEJ;;GACJ,KAAgB,EAAI,MAAiB,QAAQ,EAAY,OAAO,IAAI,CAAY,KAC/E,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,WAAW;IAAE;cACzD,EAAY;KACX,OAAO,EAAY,OAAO,IAAI,CAAY;KAC1C,WAAW;KACX,OAAO,EAAI;KACX,MAAM;KACN,QAAQ;KACR;KACF,QAAQ;IACR,CAAC;GACE,CAAA;GAEN,KACC,EAAI,MAAiB,QACrB,MAAiB,KACjB,EAAY,OAAO,IAAI,CAAY,KACjC,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,WAAW;IAAE;cACzD,EAAY;KACX,OAAO,EAAY,OAAO,IAAI,CAAY;KAC1C,WAAW;KACX,OAAO,EAAI;KACX,MAAM;KACN,QAAQ;KACR;KACF,QAAQ;IACR,CAAC;GACE,CAAA;EAEN;;AAET;AAIA,SAAS,GAAO,GAAqB;CACnC,IAAI,KAAM,MAAM,OAAO;CACvB,IAAM,IAAI,IAAI,KAAK,OAAO,CAAE,CAAC;CAE7B,OADI,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAU,KAC/B,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ;AACzD;AAGA,SAAS,GAAS,GAAqB;CACrC,IAAI,KAAM,MAAM,OAAO;CACvB,IAAM,IAAI,IAAI,KAAK,OAAO,CAAE,CAAC;CAC7B,IAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,GAAG,OAAO;CACtC,IAAM,oBAAM,IAAI,KAAK,GACf,oBAAY,IAAI,KAAK;CAC3B,EAAU,QAAQ,EAAI,QAAQ,IAAI,CAAC;CACnC,IAAM,KAAW,GAAS,MAAY,EAAE,aAAa,MAAM,EAAE,aAAa;CAG1E,OAFI,EAAQ,GAAG,CAAG,IAAU,UACxB,EAAQ,GAAG,CAAS,IAAU,cAC3B,EAAE,mBAAmB,KAAA,GAAW;EAAE,SAAS;EAAS,OAAO;EAAS,KAAK;CAAU,CAAC;AAC7F;AAGA,SAAS,GAAU,EAAE,UAAO,YAA2C;CACrE,OACE,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,YACE;EACJ;YAXF;GAaE,kBAAC,QAAD;IACE,OAAO;KACL,UAAU;KACV,YAAY;KACZ,OAAO;IACT;cAEC;GACG,CAAA;GACN,kBAAC,QAAD,EAAM,OAAO;IAAE,MAAM;IAAG,QAAQ;IAAG,YAAY;GAAiB,EAAI,CAAA;GACpE,kBAAC,QAAD;IAAM,WAAU;IAAM,OAAO;KAAE,UAAU;KAAM,OAAO;IAAgB;cACnE;GACG,CAAA;EACH;;AAET;AAKA,SAAS,GAAc,EAAE,UAAO,YAAiD;CAC/E,IAAM,IAAW,GAAW,GAAO,CAAK;CAExC,OADI,MAAa,OAAa,OAE5B,kBAAC,QAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,cAAc;GACd,UAAU;GACV,YAAY;GACZ,YAAY,IAAW,uBAAuB;GAC9C,OAAO,IAAW,kBAAkB;EACtC;YAXF,CAac,EAAX,IAAY,EAAK,UAAwB,EAAK,WAAnC,EAAc,MAAM,GAAK,CAA+B,GACnE,IAAW,QAAQ,IAChB;;AAEV;AAEA,SAAS,GAAW,GAAgB,GAAoC;CACtE,IAAI,EAAM,SAAS,QAAQ,OAAO,MAAU;CAC5C,IAAI,OAAO,KAAU,UAAU;EAC7B,IAAM,IAAI,EAAM,YAAY;EAC5B,IAAI,MAAM,cAAc,MAAM,OAAO,OAAO;EAC5C,IAAI,MAAM,aAAa,MAAM,MAAM,OAAO;CAC5C;CACA,OAAO;AACT;;;ACppBA,SAAgB,GAAsB,EACpC,WACA,gBACA,WACA,SACA,aACA,qBACA,WAC6B;CAC7B,IAAM,IAAa,GAAc,GAC3B,IAAW,EAAyB,IAAI,GACxC,IAAU,EAAO,SAGjB,IAAQ,IAAI,IAAI,MAAM,QAAQ,EAAO,WAAW,IAAI,EAAO,cAAc,CAAC,EAAO,WAAW,CAAC,GAC7F,IAAkB,EAAQ,QAAQ,MAAQ;EAE9C,IADI,EAAM,IAAI,CAAG,KACb,EAAO,kBAAkB,MAAQ,EAAO,gBAAgB,OAAO;EACnE,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;EAGxC,OADA,EADI,CAAC,KACD,EAAM,QAAQ,EAAM;CAE1B,CAAC,GAGK,IAAqB,IAAI,IAC7B,EAAQ,QAAQ,MAAQ;EACtB,IAAI,EAAO,kBAAkB,MAAQ,EAAO,gBAAgB,OAAO;EACnE,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;EACxC,OAAO,MAAU,EAAM,QAAQ,EAAM;CACvC,CAAC,CACH,GAEM,IAAmB,GACtB,GAAgB,GAAmB,MAAmB;EAMrD,EALgB,EAAK,KAAK,MACxB,EAAI,SAAS,IACT;GAAE,GAAG;IAAM,IAAY;GAAO,QAAQ;EAAK,IAC3C,CAEG,CAAO;CAClB,GACA,CAAC,GAAM,CAAQ,CACjB,GAEM,IAAe,QAAkB;EACrC,IAAM,IAA0B;GAC9B,MAAM,OAAO,WAAW;GACxB,QAAQ;EACV,GAEM,IAAgB,EAAO,WAAW,YAAY,CAAC;EACrD,KAAK,IAAM,KAAO,GAAiB;GACjC,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;GACnC,MACD,EAAc,OAAS,KAAA,IAEhB,EAAM,YAAY,KAAA,IAElB,EAAM,SAAS,WACxB,EAAO,KAAO,MAFd,EAAO,KAAO,EAAM,UAFpB,EAAO,KAAO,EAAc;EAMhC;EACA,EAAS,CAAC,GAAG,GAAM,CAAM,CAAC;CAC5B,GAAG;EAAC;EAAM;EAAU;EAAiB;EAAa,EAAO,WAAW;CAAQ,CAAC,GAEvE,IAAkB,GACrB,MAAmB;EAIlB,EAHgB,EAAK,KAAK,MACxB,EAAI,SAAS,IAAS;GAAE,GAAG;GAAK,UAAU;EAAK,IAAI,CAE5C,CAAO;CAClB,GACA,CAAC,GAAM,CAAQ,CACjB,GAEM,IAAmB,GACtB,MAAmB;EAIlB,EAHgB,EAAK,KAAK,MACxB,EAAI,SAAS,IAAS;GAAE,GAAG;GAAK,UAAU;EAAM,IAAI,CAE7C,CAAO;CAClB,GACA,CAAC,GAAM,CAAQ,CACjB,GAEM,IAAgB,GACnB,MAA6C;EAC5C,IAAM,IAAS,EAAE,QACX,IAAO,EAAO,QAAQ,IAAI;EAChC,IAAI,CAAC,GAAM;EACX,IAAM,IAAM,EAAK,QAAQ,IAAI;EAC7B,IAAI,CAAC,GAAK;EACV,IAAM,IAAQ,EAAI,QAAQ,OAAO;EACjC,IAAI,CAAC,GAAO;EAEZ,IAAM,IAAY,MAAM,KAAK,EAAI,KAAK,EAAE,QAAQ,CAA4B,GACtE,IAAW,MAAM,KAAK,EAAM,IAAI,EAAE,QAAQ,CAA0B;EAE1E,IAAI,EAAE,QAAQ,OAAO;GAEnB,IAAM,IAAY,EAAE,WAAW,KAAK,GAC9B,IAAU,MAAM,KAAK,EAAM,IAAI,GACjC,IAAU,GACV,IAAW,IAAY;GAG3B,OAAO,KAAW,KAAK,IAAU,EAAQ,SACvC,IAAI,KAAY,KAAK,IAAW,EAAQ,GAAS,MAAM,QAAQ;IAE7D,IAAM,IADS,EAAQ,GAAS,MAAM,GACjB,cAAc,qCAAqC;IACxE,IAAI,GAAO;KAET,AADA,EAAE,eAAe,GACjB,EAAM,MAAM;KACZ;IACF;IACA,KAAY;GACd,OAGE,AADA,KAAW,GACX,IAAW,IAAY,IAAI,KAAK,EAAQ,IAAU,MAAM,UAAU,KAAK;EAI7E,OAAO,IAAI,EAAE,QAAQ,SAAS;GAE5B,EAAE,eAAe;GACjB,IAAM,IAAU,MAAM,KAAK,EAAM,IAAI;GACrC,IAAI,IAAW,EAAQ,SAAS,GAAG;IAEjC,IAAM,IADS,EAAQ,IAAW,GAAG,MAAM,IACrB,cAAc,qCAAqC;IACzE,AAAI,KAAO,EAAM,MAAM;GACzB,OAIE,AAFA,EAAa,GAEb,4BAA4B;IAC1B,IAAM,IAAU,EAAM,iBAAiB,IAAI,GACrC,IAAU,EAAQ,EAAQ,SAAS;IACzC,IAAI,GAAS;KACX,IAAM,IAAa,EAAQ,cAAc,qCAAqC;KAC9E,AAAI,KAAY,EAAW,MAAM;IACnC;GACF,CAAC;EAEL,OAAO,AAAI,EAAE,QAAQ,YAEnB,EAAwB,OAAO;CAEnC,GACA,CAAC,CAAY,CACf,GAGM,IAAU;CAEhB,OACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;EAAK,OAAO,EAAE,WAAW,OAAO;YAC9B,kBAAC,SAAD;GACE,KAAK;GACL,OAAO;GACP,WAAW;aAHb,CAKE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UAAA,CACG,EAAQ,KAAK,MAAQ;IACpB,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG;IACxC,OACE,kBAAC,MAAD;KAAc,OAAO;eAClB,IAAQ,GAAiB,GAAO,GAAK,EAAO,QAAQ,IAAI;IACvD,GAFK,CAEL;GAER,CAAC,GACD,kBAAC,MAAD,EAAI,OAAO;IAAE,GAAG;IAAS,OAAO;GAAG,EAAI,CAAA,CACrC,EAAA,CAAA,EACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAK,KAAK,MAAQ;IACjB,IAAM,IAAY,CAAC,CAAC,EAAI,UAClB,IAAY,IAAmB,EAAI;IACzC,OACE,kBAAC,MAAD;KAAmB,OAAO,EAAE,SAAS,IAAY,KAAM,EAAE;eAAzD,CACG,EAAQ,KAAK,MAAQ;MACpB,IAAM,IAAQ,EAAY,OAAO,IAAI,CAAG,GAClC,IAAW,GAAW,SAAS,CAAG,GAClC,IAAgB,EAAmB,IAAI,CAAG;MA6BhD,OA3BI,KAAa,IAEb,kBAAC,MAAD;OAEE,OAAO;QACL,SAAS;QACT,cAAc;QACd,eAAe;QACf,gBAAgB,IAAY,iBAAiB;QAC7C,OAAO;OACT;iBAEC,IACG,EAAY;QACV;QACA,WAAW;QACX,OAAO,EAAI;QACX,MAAM;QACN,QAAQ;QACR;QACF,QAAQ;OACR,CAAC,IACD,OAAO,EAAI,MAAQ,EAAE;MACvB,GApBG,CAoBH,IAKN,kBAAC,MAAD;OAEE,OAAO;QACL,SAAS;QACT,cAAc;QACd,eAAe;QACf,SAAS,IAAW,4BAA4B;QAChD,eAAe;QACf,cAAc,IAAW,IAAI;OAC/B;OACA,OAAO,IAAW,GAAG,EAAI,gBAAgB,KAAA;iBAExC,IACG,EAAY;QACV;QACA,WAAW;QACX,OAAO,EAAI;QACX,MAAM;QACN,WAAW,MAAQ,EAAiB,EAAI,MAAM,GAAK,CAAG;QACtD,QAAQ;QACR;QACA,SAAS;QACT,QAAQ;OACV,CAAC,IACD,OAAO,EAAI,MAAQ,EAAE;MACvB,GAxBG,CAwBH;KAER,CAAC,GACD,kBAAC,MAAD;MAAI,OAAO;OAAE,SAAS;OAAW,cAAc;OAA4B,eAAe;MAAS;gBAChG,IACC,kBAAC,GAAD;OACE,MAAM;OACN,MAAM,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;OAC/B,eAAe,EAAiB,EAAI,IAAI;OACxC,OAAM;MACP,CAAA,IAED,kBAAC,GAAD;OACE,MAAM;OACN,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAC7B,eAAe,EAAgB,EAAI,IAAI;OACvC,OAAM;MACP,CAAA;KAED,CAAA,CACF;OA/EK,EAAI,IA+ET;GAER,CAAC,EACI,CAAA,CACF;;CACJ,CAAA,GACL,kBAAC,OAAD;EAAK,OAAO,EAAE,WAAW,EAAE;YACzB,kBAAC,GAAD;GAAK,MAAK;GAAQ,MAAK;GAAK,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;GAAG,SAAS;aAAc;EAE7E,CAAA;CACF,CAAA,CACF,EAAA,CAAA;AAET;;;ACtRA,IAAM,KAAyB,SAC7B,OAAO,wCAA4B,MAAM,OAAO,EAC9C,SAAS,EAAE,uBACb,EAAE,CACJ;AAuBA,SAAgB,GAAc,EAAE,SAAM,UAA2B;CAC/D,IAAM,IAAY,EAAK;CAKvB,IAJI,CAAC,KAID,EAAI,SAAS,EAAI,SAAS,QAAQ,OAAO;CAE7C,IAAM,IAAW,GAAkB,EAAI,QAAQ,CAAS;CACxD,IAAI,CAAC,GACH,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAoD,4BACzB,CACtB;;CAIT,IAAM,IAAc,EAAI,OAAO,SAAS,EAAS;CACjD,IAAI,CAAC,GACH,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAoD,sBAC/B,EAAS,MACzB;;CAIT,IAAM,IAAS,GAAmB,GAAU,GAAa,CAAI,GAKvD,IAAQ,GAAkB,GAAM,CAAG,GACnC,IAAW,GAAiB,GAAM,CAAG,GAIrC,KAAQ,MACR,EAAO,SAAS,cAAc,EAAI,aAAmB,IAEvD,kBAAC,IAAD;EACS;EACP,MAAM,IAAW,kBAAC,GAAD;GAAa,MAAM;GAAU,MAAM;EAAK,CAAA,IAAI,KAAA;YAE5D;CACU,CAAA;CAMjB,IACE,EAAI,SAAS,UACb,EAAO,YACP,EAAO,gBAAgB,cACvB,EAAO,gBAAgB,cACvB,GAAqB,GAAa,QAAQ,KAC1C,EAAO,SAAS,WAChB,EAAI,oBAAoB,KAAA,KACxB,EAAI,kBACJ;EACA,IAAM,IAAc,EAAI,gBAAgB,MAAc,CAAC,GACjD,IAAmB,EAAI,wBAAwB,IAC/C,IAAiB,EAAO,iBAC1B,KACA;EACJ,OAAO,EACL,kBAAC,IAAD;GAAU,UAAU,kBAAC,IAAD,CAAgB,CAAA;aAClC,kBAAC,GAAD;IACU;IACK;IACb,QAAQ,EAAI;IACZ,MAAM;IACN,WAAW,MAAS,EAAI,iBAAkB,GAAW,CAAI;IACvC;IAClB,MAAM,EAAI;GACX,CAAA;EACO,CAAA,CACZ;CACF;CAGA,IAAI,EAAI,OAAO,OAAO;CAEtB,IAAM,IAAkC;EACtC;EACA,cAAc,EAAI;EAClB,UAAU,EAAI;EACd,QAAQ,EAAI;CACd;CAEA,QAAQ,EAAO,MAAf;EACE,KAAK,QACH,OAAO,EAAK,kBAAC,IAAD,EAAc,GAAI,EAAe,CAAA,CAAC;EAChD,KAAK,SACH,OAAO,EAAK,kBAAC,IAAD,EAAe,GAAI,EAAe,CAAA,CAAC;EACjD,KAAK,YACH,OAAO,EAAK,kBAAC,IAAD,EAAkB,GAAI,EAAe,CAAA,CAAC;EAEpD,SACE,OAAO,EAAK,kBAAC,IAAD,EAAe,GAAI,EAAe,CAAA,CAAC;CACnD;AACF;;;AC5HA,IAAM,KAAc,KAGd,KAAc,KASd,KAAkB,QAClB,KAAuB,GAKhB,KAAwC,IAAI,IAAI;CAC3D;CACA;CACA;CACA;CACA;AACF,CAAC,GAOK,KAAoB,GAA+B,EAAE,SAAS,EAAE,CAAC;AAWvE,SAAgB,GAAW,EAAE,aAAU,GAAG,eAA6B;CACrE,IAAM,IAAM,EAAuB,IAAI,GACjC,CAAC,GAAQ,KAAa,EAAS,EAAK;CAE1C,QAAgB;EACd,IAAM,IAAK,EAAI;EACf,IAAI,CAAC,KAAM,MAAY,KAAK,OAAO,iBAAmB,KAAa;EACnE,IAAM,IAAK,IAAI,gBAAgB,CAAC,OAAW;GACzC,EAAU,EAAM,YAAY,QAAQ,EAAW;EACjD,CAAC;EAED,OADA,EAAG,QAAQ,CAAE,SACA,EAAG,WAAW;CAC7B,GAAG,CAAC,CAAO,CAAC;CAEZ,IAAM,IAAY,MAAY,KAAK,IAAS,IAAI;CAEhD,OACE,kBAAC,GAAkB,UAAnB;EAA4B,OAAO,EAAE,SAAS,EAAU;YACtD,kBAAC,OAAD;GACO;GACL,OAAO;IACL,SAAS;IACT,qBAAqB,UAAU,EAAU;IACzC,WAAW;IACX,QAAQ;GACV;GAEC;EACE,CAAA;CACqB,CAAA;AAEhC;AAiCA,SAAgB,GAAS,EACvB,UACA,aACA,UAAO,IACP,aACA,UACA,SACA,YACA,cACA,UAAO,WACP,YACA,YACA,eAAe,KACC;CAChB,IAAM,IAAO,GAAW,EAAiB,GACnC,IAAU,KAAQ,EAAK,YAAY,GACnC,IAAW,CAAC,CAAC;CAEnB,OACE,kBAAC,OAAD;EACE,WAAU;EACV,cAAY,KAAY,KAAA;EACxB,eAAa;EACb,OAAO;GACL,SAAS;GACT,eAAe,IAAU,WAAW;GACpC,KAAK,IAAU,IAAI;GACnB,YAAY,IAAU,YAAY;GAClC,SAAS;GACT,GAAI,IAAO,EAAE,YAAY,SAAS,IAAI;EACxC;YAXF,CAcE,kBAAC,OAAD;GACE,OAAO;IACL,OAAO,IAAU,SAAS;IAC1B,YAAY;IACZ,YAAY,IAAU,IAAI;IAC1B,UAAU;IACV,YAAY;IACZ,YAAY;IACZ,OAAO;IACP,SAAS;IACT,YAAY;IACZ,KAAK;GACP;aAZF,CAcG,IACC,kBAAC,SAAD;IAAO,IAAI;IAAkB;cAC1B;GACI,CAAA,IAEP,kBAAC,QAAD;IAAM,IAAI;cAAU;GAAY,CAAA,GAEjC,KAAY,MAAS,UACpB,kBAAC,QAAD;IAAM,eAAY;IAAO,OAAO;KAAE,OAAO;KAAiB,YAAY;IAAE;cAAG;GAErE,CAAA,CAEL;MAGL,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;IAAG,SAAS;IAAQ,eAAe;IAAU,KAAK;GAAE;aAArF,CACE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAc,KAAK;KAAG,WAAW;IAAG;cAA/E,CACE,kBAAC,OAAD;KACE,OAAO;MACL,MAAM;MACN,UAAU;MACV,UAAU;MACV,YAAY;MACZ,OAAO;MACP,cAAc;MAId,YAAY,MAAS,aAAa,CAAC,IAAU,KAAuB;KACtE;eAEC,KAAY,MAAS,SAIpB,kBAAC,OAAD;MAAK,OAAO;OAAE,cAAc;OAAG,WAAW;MAA4B;MACnE;KACE,CAAA,IAEL;IAEC,CAAA,IACH,KAAa,MACb,kBAAC,OAAD;KACE,WAAU;KACV,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAG,YAAY;MAAG,WAAW;KAAG;eAFjE,CAIG,KAAa,kBAAC,GAAD;MAAY,OAAO;MAAW,MAAK;MAAK,OAAM;KAAc,CAAA,GACzE,CACE;MAEJ;QACH,KAAY,KAAQ,SACpB,kBAAC,OAAD;IACE,OAAO;KACL,UAAU;KACV,YAAY;KACZ,OAAO,IAAW,kBAAkB;KACpC,SAAS;KACT,YAAY;KACZ,KAAK;IACP;cARF,CAUG,KAAY,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA,GACtC,kBAAC,QAAD,EAAA,UAAO,IAAW,IAAQ,EAAW,CAAA,CAClC;KAEJ;IACF;;AAET;;;AClOA,SAAgB,GAAY,EAAE,WAAQ,UAAyB;CAC7D,IAAM,EAAE,WAAQ,eAAY,WAAQ,SAAM,kBAAe,cAAW,GAC9D,IAAa,GAAc,GAC3B,IAAU,EAAI,WAAW,GAGzB,IAAa,MAAY,KAAK,EAAO,WAAW;CAEtD,OACE,kBAAC,IAAD;EAAqB;YAClB,EAAO,KAAK,MAAgB;GAC3B,IAAM,IAAY,EAAY,MAIxB,IAAU,GAAG,KAAc,QAAQ,GAAG,KACtC,IAAU,GAAG,EAAQ,SAIrB,IAAW,EAAO,OAAO,IAAI,CAAS,GACtC,IAAgB,CAAC,EAAE,GAAU,QAAQ,GAAU,WAO/C,IAAS,GAAgB,GAAQ,CAAS,GAC1C,IAAa,GAAQ,SAAS,UAC9B,IAAc,GAAQ,UAAU,UAChC,IACJ,MAAS,YACL,IACA,KAAe,GACf,IACJ,KAAkB,MAAS,UAAU,IAAe,YAAY,GAC5D,IAAW,IACf,kBAAC,GAAD;IACE,MAAM,EAAQ;IACd,SAAS,GAAmB,GAAQ,GAAQ,CAAS;IACrD,oBAAoB,GAAQ;IAC5B,QAAQ;IACR,SAAS,MAAc,SAAS,UAAU;GAC3C,CAAA,IAED,GAAkB;IAChB;IACA;IACA;IACA;IACA,MAAM;IACN,UACE,KAAiB,CAAC,KACb,MAAe,EAAc,GAAW,CAAC,IAC1C,KAAA;IACN,MAAM,EAAI;IACV,UAAU,GAAQ;IAClB,SAAS,GAAQ;IACjB,QAAQ;IACR;IACA;GACF,CAAC;GAEH,IAAI,CAAC,GAAU,OAAO;GAItB,IAAM,IADJ,MAAS,aAAa,EAAY,WAAW,EAAY,QAAQ,SAAS,IAE1E,kBAAA,GAAA,EAAA,UACG,EAAY,QAAS,KAAK,GAAQ,MACjC,kBAAC,IAAD;IAEE,YAAY,GAAG,EAAU,UAAU,EAAE;IAC7B;IACI;IACZ,KAAK;IACL,SAAQ;IACR,MAAK;GACN,GAPM,CAON,CACF,EACD,CAAA,IACA,KAAA,GAGE,IAAO,CAAC,CAAC,KAAY,GAAiB,IAAI,EAAS,IAAI,GAKvD,IAAW,IAAS,IACpB,IACJ,MAAc,cACb,GAAU,YAAY,GAAU,SAAS,WAC1C,KAAY,QACZ,MAAa,KACT,OAAO,CAAQ,IACf,KAAA;GAYN,OARI,IAEA,kBAAC,OAAD;IAAqB,OAAO,EAAE,YAAY,SAAS;cAChD;GACE,GAFK,CAEL,IAKP,kBAAC,IAAD;IAEE,OAAO,GAAwB,GAAQ,GAAW,EAAO,QAAQ;IAGjE,SAAS,MAAc,SAAS,IAAU,KAAA;IACjC;IACT,UAAU,GAAU,YAAY;IAChC,MAAM;IACA;IACN,OAAO,EAAI,cAAc;IACzB,SAAS;IACE;cAEV;GACO,GAdH,CAcG;EAEd,CAAC;CACS,CAAA;AAEhB;;;ACtIA,IAAM,KAAiB,IAAI,IAAI;CAAC;CAAU;CAAQ;CAAO;CAAS;CAAW;CAAS;AAAK,CAAC,GACtF,KAAc;AAMpB,SAAgB,GAAgB,EAAE,SAAM,UAA6B;CACnE,IAAM,EAAE,WAAQ,qBAAkB,kBAAe,UAAO,SAAM,YAAS,GACjE,IAAe,EAAO,cAAc,CAAC;CAI3C,IAAI,GAAO,OAAO;CAGlB,IAAM,IAA0B,EAAK,eAAe,KAAO,KAAQ,EAAK,YAGlE,IACJ,MAAW,KACP,OAAO,QAAQ,CAAY,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,IACjE,EACE,QAAQ,MAAS,EAAa,MAAS,IAAI,EAC3C,KAAK,MAAS,CAAC,GAAM,EAAa,EAAK,CAAC;CAIjD,OAFI,EAAQ,WAAW,IAAU,OAG/B,kBAAC,IAAD;EAAY,SAAS;YAClB,EAAQ,KAAK,CAAC,GAAU,OAAU;GACjC,IAAM,IAAU,GAAkB,MAAM,MAAM,EAAE,kBAAkB,CAAQ,GACpE,IAAQ,GAAgB,CAAI,GAG5B,IAAgB,EAAK,YAAY,CAAC,IAAgB,YAAY;GAEpE,OACE,kBAAC,IAAD;IAEY;IACJ;IACC;IACP,OAAO,GAAS;IAChB,MAAM;IACS;IACT;IACE;IACR,QAAQ,EAAI;GACb,GAVM,CAUN;EAEL,CAAC;CACS,CAAA;AAEhB;AAKA,SAAS,GAAkB,EACzB,aACA,SACA,UACA,UACA,SACA,kBACA,SACA,WACA,aAeC;CACD,IAAM,IAAa,GAAc,GAC3B,IAAW,EAA6C,IAAI;CAElE,cACe;EACX,AAAI,EAAS,WAAS,aAAa,EAAS,OAAO;CACrD,GACC,CAAC,CAAC;CAEL,IAAM,IAAW,GACd,MAAe;EACT,MACD,GAAe,IAAI,EAAK,IAAI,KAC1B,EAAS,WAAS,aAAa,EAAS,OAAO,GACnD,EAAS,UAAU,iBAAiB,EAAc,GAAU,CAAC,GAAG,EAAW,KAE3E,EAAc,GAAU,CAAC;CAE7B,GACA;EAAC;EAAU,EAAK;EAAM;CAAa,CACrC,GAKM,IAAS,GAAgB,GAAQ,CAAQ,GACzC,IAAa,GAAQ,SAAS,UAC9B,IAAc,GAAQ,UAAU,UAChC,IACJ,MAAS,YAAY,IAAa,KAAe,GAC7C,IAAgB,MAAS,UAAU,IAAc,YAAY;CAEnE,OACE,kBAAC,IAAD;EAAU,OAAO,EAAI,EAAK,SAAS,QAAQ,EAAK,IAAI;EAAG,MAAM;EAAe,MAAM,GAAiB,IAAI,EAAK,IAAI;YAC7G,IACC,kBAAC,GAAD;GACE,MAAM,EAAQ;GACd,SAAS,GAAmB,GAAQ,GAAQ,CAAQ;GACpD,oBAAoB,GAAQ;GAC5B,QAAQ;GACR,SAAS,MAAkB,SAAS,UAAU;EAC/C,CAAA,IAED,EAAY;GACV;GACA,WAAW;GACX;GACA,MAAM;GACN,UAAU,MAAkB,SAAS,IAAW,KAAA;GAChD;GACA,QAAQ;EACV,CAAC;CAEK,CAAA;AAEd;;;AChIA,SAAgB,GAAc,EAC5B,WACA,YACA,eACA,aACA,aAAU,MACW;CACrB,IAAM,IAAS,EAAO,WAAW,MAAM,EAAE,SAAS,CAAO;CAEzD,OACE,kBAAC,OAAD;EACE,MAAK;EACL,OAAO;GACL,SAAS;GACT,YAAY;GACZ,cAAc;GACd,UAAU;GACV,QAAQ;GACR,YAAY;EACd;YAEC,EAAO,KAAK,GAAG,MAAM;GACpB,IAAM,IAAS,KAAU,KAAK,IAAI,GAC5B,IAAY,MAAM,GAClB,IAAW,IAAS,KAAK,IAAI,GAC7B,IAAY,CAAC,CAAC,MAAa,GAAY,IAAI,EAAE,IAAI,KAAK,KACtD,IAAQ,MAAM,GACd,IAAO,MAAM,EAAO,SAAS,GAE7B,IAAK,IACP,uBACA,IACE,wBACA,eACA,IAAK,IAAY,kBAAkB,IAAS,mBAAmB,iBAK/D,IACJ,KAAS,IACL,KAAA,IACA,IACE,gFACA,IACE,sDACA;GAEV,OACE,kBAAC,UAAD;IAEE,MAAK;IACL,MAAK;IACL,UAAU,CAAC;IACX,SAAS,UAAkB,EAAU,EAAE,IAAI,IAAI,KAAA;IAC/C,OAAO,EAAE;IACT,OAAO;KACL,MAAM;KACN,UAAU;KACV,SAAS;KACT,YAAY;KACZ,KAAK;KACL,SAAS,IACL,eAAe,IAAQ,KAAK,GAAG,MAC/B,gBAAgB,IAAQ,KAAK,GAAG;KACpC,QAAQ;KACR,YAAY,IAAI,IAAI,6BAA6B;KACjD,YAAY;KACZ,OAAO;KACP,UAAU,IAAU,OAAO;KAC3B,YAAY;KACZ,YAAY,IAAY,MAAM;KAC9B,QAAQ,IAAY,YAAY;KAChC,UAAU;KACV,YAAY;IACd;cA1BF,CA4BE,kBAAC,QAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,YAAY,IAAW,gBAAgB;MACvC,QAAQ,IAAW,mCAAmC;MACtD,OAAO,IACH,kBACA,IACE,uBACA;MACN,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,UAAU;MACV,YAAY;KACd;eAEC,IAAS,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA,IAAI,IAAI;IACrC,CAAA,GACN,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAU,cAAc;KAAW;eAAI,EAAE;IAAY,CAAA,CACxE;MAlDD,EAAE,IAkDD;EAEZ,CAAC;CACE,CAAA;AAET;;;AClHA,SAAgB,GAAoB,EAClC,iBACA,mBACA,gBACA,cACA,UAAO,UACoB;CAC3B,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,UAAU;GAAQ,YAAY;GAAU,KAAK;EAAG;YAC5E,OAAO,QAAQ,CAAY,EAAE,KAAK,CAAC,GAAQ,OAAQ;GAElD,IAAM,IADU,GAAgB,MAAM,MAAM,EAAE,eAAe,CAAM,GACtC,aACvB,IAAgB,IAClB,EAAG,SAAS,MAAM,MAAM,EAAE,SAAS,CAAW,IAC9C,KAAA;GAGJ,IAAI,EAAG,iBAAiB,WAAW;IACjC,IAAM,IAAW,MAAS,QAGpB,IAAa,IACf,EAAG,eAAe,IAChB,IAAI,IAAY,CAAC,GAAa,GAAI,EAAG,YAAY,MAAgB,CAAC,CAAE,CAAC,IACrE,IAAI,IAAY,EAAG,SAAS,KAAK,MAAM,EAAE,IAAI,CAAC,IAChD,KAAA;IACJ,OACE,kBAAC,OAAD;KAAkB,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAG,UAAU;KAAE;eAAtF,CACE,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAI,OAAO;OAAiB,YAAY;MAAE;gBAAnE,CACG,EAAI,EAAG,IAAI,GAAE,GACV;SACN,kBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,SAAS,IAAY,KAAM;OAC3B,eAAe,IAAY,SAAS;MACtC;gBAEA,kBAAC,IAAD;OACE,QAAQ,EAAG,SAAS,KAAK,OAAO;QAAE,MAAM,EAAE;QAAM,OAAO,EAAI,EAAE,IAAI;OAAE,EAAE;OACrE,SAAS;OACG;OACZ,UAAU,KAAY,MAAS,EAAY,GAAQ,CAAI,IAAI,KAAA;OAC3D,SAAA;MACD,CAAA;KACE,CAAA,CACF;OAnBK,CAmBL;GAET;GAEA,IAAI,MAAS,WACX,OACE,kBAAC,OAAD;IAAkB,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAE;cAAzE,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,OAAO;KAAgB;eAApD,CAAwD,EAAI,EAAG,IAAI,GAAE,GAAO;QAC3E,IACC,kBAAC,GAAD;KAAM,MAAO,EAAc,SAAsB;KAAS,MAAK;eAC5D,EAAI,EAAc,IAAI;IACnB,CAAA,IAEN,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAM,OAAO;KAAgB;eAAG;IAAO,CAAA,CAE/D;MATK,CASL;GAaT,IAAM,KARJ,EAAG,eAAe,IACd,EAAG,SAAS,QACT,MACC,EAAE,SAAS,MACV,EAAG,YAAa,IAAc,SAAS,EAAE,IAAI,KAAK,GACvD,IACA,EAAG,UAEiB,KAAK,OAAO;IACpC,OAAO,EAAE;IACT,OACE,kBAAC,QAAD;KAAM,OAAO;MAAE,SAAS;MAAe,YAAY;MAAU,KAAK;KAAE;eAApE,CACG,EAAE,SACD,kBAAC,QAAD,EACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY,cAAc,EAAE,MAAM,IAAI,EAAE,MAAM;MAC9C,SAAS;KACX,EACD,CAAA,GAEF,EAAI,EAAE,IAAI,CACP;;GAEV,EAAE;GAEF,OACE,kBAAC,OAAD;IAAkB,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAE;cAAzE,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAI,OAAO;KAAgB;eAApD,CAAwD,EAAI,EAAG,IAAI,GAAE,GAAO;QAC5E,kBAAC,OAAD;KACE,OAAO;MACL,UAAU;MACV,SAAS,IAAY,KAAM;MAC3B,eAAe,IAAY,SAAS;KACtC;eAEA,kBAAC,IAAD;MACE,OAAO,KAAe;MACb;MACT,WAAW,MAAe,EAAY,GAAQ,CAAU;MACxD,aAAY;KACb,CAAA;IACE,CAAA,CACF;MAhBK,CAgBL;EAET,CAAC;CACE,CAAA;AAET;;;AClIA,IAAM,WAAa,CAAC;AASpB,SAAgB,GAAc,EAAE,SAAM,UAA2B;CAC/D,IAAM,EAAE,WAAQ,mBAAgB,gBAAa,oBAAiB,aAAU,GAClE,IAAe,EAAO,iBAAiB,CAAC;CAE9C,IAAI,GAAO,OAAO;CAGlB,IAAM,IAA0B,EAAK,aAAa,KAAO,KAAQ,EAAK,UAEhE,IAAW,OAAO,YACtB,OAAO,QAAQ,CAAY,EAAE,QAC1B,CAAC,OAAU,MAAW,MAAS,EAAoB,SAAS,CAAI,CACnE,CACF;CAIA,OAFI,OAAO,KAAK,CAAQ,EAAE,WAAW,IAAU,OAG7C,kBAAC,IAAD;EACE,cAAc;EACE;EAChB,aAAa,KAAe;EAC5B,WAAW,KAAmB;EAC9B,MAAM,EAAI;CACX,CAAA;AAEL;;;AC8BA,SAAgB,GAAe,EAAE,UAAO,UAA4B;CAClE,OACE,kBAAA,GAAA,EAAA,UACG,EAAM,KAAK,GAAM,MAChB,kBAAC,IAAD;EAAkC;EAAW;CAAM,GAA1B,CAA0B,CACpD,EACD,CAAA;AAEN;AAQA,SAAgB,GAAmB,EAAE,SAAM,UAAgC;CA8BzE,OA5BI,EAAK,MACA,kBAAC,IAAD;EAAW,QAAQ,EAAK;EAAU;CAAM,CAAA,IAE7C,EAAK,SACA,kBAAC,IAAD;EAAc,QAAQ,EAAK;EAAc;EAAW;CAAM,CAAA,IAE/D,EAAK,OACA,kBAAC,IAAD;EAAY,QAAQ,EAAK;EAAW;CAAM,CAAA,IAE/C,EAAK,UACA,kBAAC,IAAD;EAAc,QAAQ,EAAK;EAAc;CAAM,CAAA,IAEpD,EAAK,WACA,kBAAC,IAAD;EAAqB;EAAW;CAAM,CAAA,IAE3C,EAAK,aACA,kBAAC,IAAD;EAAuB;EAAW;CAAM,CAAA,IAE7C,EAAK,SACA,kBAAC,IAAD;EAAa,QAAQ,EAAK;EAAa;CAAM,CAAA,IAElD,EAAK,cAAc,OAGnB,EAAK,YAAY,OAId,OAHE,kBAAC,IAAD;EAAqB;EAAW;CAAM,CAAA,IAHtC,kBAAC,IAAD;EAAuB;EAAW;CAAM,CAAA;AAOnD;;;ACzFA,SAAgB,GAAa,EAAE,WAAQ,aAA6B;CAClE,IAAI,EAAO,WAAW,GAAG,OAAO;CAChC,IAAM,IAAS,EAAO,WAAW;CAEjC,OACE,kBAAC,OAAD;EACE,MAAK;EACL,OAAO;GACL,SAAS;GACT,KAAK;GACL,SAAS;GACT,cAAc;GACd,cAAc;GACd,YAAY;GACZ,QAAQ;GACR,OAAO;EACT;YAXF,CAaE,kBAAC,QAAD;GAAM,OAAO;IAAE,OAAO;IAAiB,WAAW;IAAG,SAAS;GAAO;aACnE,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA;EACrB,CAAA,GACN,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;GAAE;aAAnC,CACE,kBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAM,YAAY;KAAK,cAAc;IAAE;cAA/D;KACG,EAAO;KAAO;KAAE,IAAS,gBAAgB;KAAc;IACrD;OACL,kBAAC,MAAD;IACE,OAAO;KACL,QAAQ;KACR,SAAS;KACT,WAAW;KACX,SAAS;KACT,eAAe;KACf,KAAK;IACP;cAEC,EAAO,KAAK,MACX,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS,UAAe,EAAO,EAAE,IAAI,IAAI,KAAA;KACzC,OAAO;MACL,YAAY;MACZ,QAAQ;MACR,SAAS;MACT,QAAQ,IAAS,YAAY;MAC7B,UAAU;MACV,WAAW;MACX,YAAY;KACd;eAXF;MAaE,kBAAC,QAAD;OAAM,OAAO;QAAE,OAAO;QAAiB,aAAa;OAAE;iBAAG;MAAO,CAAA;MAChE,kBAAC,QAAD;OAAM,OAAO;QAAE,YAAY;QAAK,OAAO;OAAgB;iBAAI,EAAE;MAAY,CAAA;MACzE,kBAAC,QAAD;OAAM,OAAO,EAAE,OAAO,gBAAgB;iBAAtC,CAAyC,OAAI,EAAE,OAAc;;KACvD;OACN,GAlBK,EAAE,IAkBP,CACL;GACC,CAAA,CACD;IACF;;AAET;;;AClEA,SAAgB,GAAgB,EAAE,YAA+B;CAE/D,OADI,EAAM,WAAW,IAAU,OAE7B,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,UAAU;GACV,KAAK;GACL,UAAU;EACZ;YAEC,EAAM,KAAK,GAAI,MACd,kBAAC,IAAD,EAAA,UAAA,CACG,IAAI,KACH,kBAAC,QAAD,EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,YAAY;GACZ,cAAc;EAChB,EACD,CAAA,GAEH,kBAAC,QAAD;GAAM,OAAO;IAAE,SAAS;IAAe,YAAY;IAAU,KAAK;IAAG,YAAY;GAAI;aAArF,CACE,kBAAC,QAAD;IAAM,OAAO;KAAE,OAAO;KAAiB,UAAU;IAAK;cAAI,EAAG;GAAY,CAAA,GACzE,kBAAC,QAAD;IAAM,OAAO,EAAE,OAAO,cAAc;cAAI,EAAG;GAAY,CAAA,CACnD;IACE,EAAA,GAfK,CAeL,CACX;CACE,CAAA;AAET;;;AC5BA,SAAgB,GAAiB,EAC/B,WACA,WACA,eACA,WACA,SACA,mBACA,WACA,aACwB;CACxB,IAAM,IAAQ,EAAO;CACrB,IAAI,CAAC,KAAS,EAAM,WAAW,GAAG,OAAO;CAEzC,IAAM,IAAyB,CAAC;CAChC,KAAK,IAAM,KAAQ,GAAO;EAExB,IAAM,IAAQ,EAAO,gBAAgB;EACrC,IAAI,GAAO;GACT,IAAM,IAAU,GAAgB,MAAM,MAAM,EAAE,eAAe,CAAI,GAC3D,IAAM,IACR,EAAM,SAAS,MAAM,MAAM,EAAE,SAAS,EAAQ,WAAW,IACzD,KAAA;GACJ,EAAM,KAAK;IACT,OAAO,EAAI,EAAM,IAAI;IACrB,OAAO,IACL,kBAAC,GAAD;KAAM,MAAO,EAAI,SAAsB;KAAS,MAAK;eAClD,EAAI,EAAI,IAAI;IACT,CAAA,IAEN,kBAAC,QAAD;KAAM,OAAO,EAAE,OAAO,gBAAgB;eAAG;IAAO,CAAA;GAEpD,CAAC;GACD;EACF;EAGA,IAAI,EAAO,OAAO,IAAI,CAAI,KAAK,EAAO,OAAO,IAAI,CAAI,GAAG;GACtD,IAAM,IAAW,GAAkB;IACjC;IACA;IACA,WAAW;IACX;IACA,MAAM;IACN;IACA,UAAU,EAAO;IACjB,SAAS,EAAO;IAChB,SAAS;IACT;GACF,CAAC;GACD,EAAM,KAAK;IACT,OAAO,GAAwB,GAAQ,GAAM,EAAO,QAAQ;IAC5D,OAAO;GACT,CAAC;GACD;EACF;CAEF;CAGA,OADI,EAAM,WAAW,IAAU,OACxB,kBAAC,IAAD,EAAwB,SAAQ,CAAA;AACzC;;;AC3DA,SAAS,GAAW,GAA+B;CACjD,OAAO,EAAK,MAAM,QAAQ,EAAK,MAAM;AACvC;AAEA,SAAgB,GAAe,EAAE,YAA8B;CAC7D,IAAM,IAAU,EAAM,OAAO,EAAU;CAGvC,OAFI,EAAQ,WAAW,IAAU,OAG/B,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,UAAU;GACV,KAAK;GACL,SAAS;GACT,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,UAAU;GACV,OAAO;EACT;YAEC,EAAQ,KAAK,GAAM,MAClB,kBAAC,IAAD,EAAyB,QAAO,GAAhB,CAAgB,CACjC;CACE,CAAA;AAET;AAEA,SAAS,GAAU,EAAE,WAAkC;CACrD,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,UAAU;EAAE;YAAzE,CACE,kBAAC,QAAD;GACE,OAAO;IACL,UAAU;IACV,YAAY;IACZ,OAAO;IACP,YAAY;GACd;aAEC,EAAK;EACF,CAAA,GACN,kBAAC,QAAD;GAAM,OAAO;IAAE,SAAS;IAAe,YAAY;IAAU,KAAK;IAAG,UAAU;GAAE;aAAjF;IACG,EAAK,MAAM,QAAQ,kBAAC,QAAD;KAAM,OAAO,EAAE,OAAO,gBAAgB;eAAI,EAAK;IAAS,CAAA;IAC3E,EAAK,MAAM,QAAQ,EAAK,MAAM,QAC7B,kBAAC,QAAD;KAAM,OAAO,EAAE,OAAO,gBAAgB;eAAG;IAAO,CAAA;IAEjD,EAAK,MAAM,QAAQ,kBAAC,QAAD;KAAM,WAAU;eAAO,EAAK;IAAS,CAAA;GACrD;IACH;;AAET;;;ACtCA,SAAgB,GAAiB,EAC/B,WACA,WACA,eACA,WACA,SACA,WACA,aACwB;CACxB,IAAM,IAAQ,EAAO;CAErB,IAAI,GAAO,YAAY,IAAO,OAAO;CAIrC,IAAM,KAAe,MAA6C;EAChE,IAAI,CAAC,KAAa,CAAC,EAAO,OAAO,IAAI,CAAS,GAAG;EACjD,IAAM,IAAQ,EAAO;EACjB,WAAS,QAAQ,MAAU,KAC/B,OAAO,GAAkB;GACvB;GACA;GACA;GACA;GACA,MAAM;GACN;GACA,UAAU,EAAO;GACjB,SAAS,EAAO;GAChB,SAAS;GACT;EACF,CAAC;CACH;CAgBA,OAAO,kBAAC,IAAD,EAAuB,OAX5B,GAAO,SAAS,EAAM,MAAM,SAAS,IACjC,EAAM,MAAM,KAAK,OAAU;EACzB,OAAO,EAAI,EAAK,KAAK;EACrB,IAAI,EAAY,EAAK,EAAE;EACvB,IAAI,EAAY,EAAK,EAAE;CACzB,EAAE,IACF,CACE;EAAE,OAAO;EAAW,IAAI,EAAY,YAAY;EAAG,IAAI,EAAY,YAAY;CAAE,GACjF;EAAE,OAAO;EAAW,IAAI,EAAY,YAAY;EAAG,IAAI,EAAY,YAAY;CAAE,CACnF,EAEgC,CAAA;AACxC;;;ACjDA,SAAgB,GAAa,EAC3B,SACA,eACA,UACA,aACA,eACA,SACA,WAAQ,IACR,cAAW,IACX,WACA,YACoB;CACpB,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAc,KAAK;EAAG;YAAjE;GACG,KACC,kBAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAG,YAAY;IAAE;cACzC,kBAAC,GAAD;KAAS,MAAM;KAAI,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAAG,OAAM;KAAO,SAAS;IAAS,CAAA;GAC9E,CAAA;IAGL,KAAc,MACd,kBAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAG,YAAY;IAAE;cACxC,IACC,kBAAC,GAAD;KAAQ,MAAM;KAAY,MAAM;IAAK,CAAA,IAErC,kBAAC,QAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,cAAc;MACd,YAAY;MACZ,OAAO;MACP,SAAS;MACT,YAAY;MACZ,gBAAgB;KAClB;eAEC;IACG,CAAA;GAEL,CAAA;GAGP,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;IAAE;cAAnC,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAG,UAAU;KAAO;eAA9E;MACE,kBAAC,MAAD;OACE,OAAO;QACL,QAAQ;QACR,UAAU;QACV,YAAY;QACZ,eAAe;QACf,YAAY;QACZ,OAAO;QACP,UAAU;QACV,UAAU;QACV,cAAc;QACd,YAAY;QACZ,UAAU;OACZ;iBAEC;MACC,CAAA;MACH,KAAY,kBAAC,GAAD,EAAe,OAAO,EAAW,CAAA;MAC7C,MAAS,UAAU,KAClB,kBAAC,QAAD;OACE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,KAAK;QACL,UAAU;QACV,OAAO;OACT;iBAPF,CASE,kBAAC,QAAD,EACE,OAAO;QAAE,OAAO;QAAG,QAAQ;QAAG,cAAc;QAAK,YAAY;OAAc,EAC5E,CAAA,GAAC,SAEE;;MAEP,MAAS,YACR,kBAAC,GAAD;OAAM,MAAK;OAAS,MAAK;OAAK,KAAA;iBAAI;MAE5B,CAAA;KAEL;QACJ,KAAc,MAAS,aAAa,kBAAC,OAAD;KAAK,OAAO,EAAE,WAAW,EAAE;eAAI;IAAgB,CAAA,CACjF;;GAEJ,KACC,kBAAC,OAAD;IACE,MAAK;IACL,cAAW;IACX,OAAO;KACL,SAAS;KACT,YAAY;KACZ,KAAK;KACL,YAAY;KACZ,UAAU;KACV,gBAAgB;IAClB;cAEC;GACE,CAAA;EAEJ;;AAET;;;AC3HA,SAAgB,GACd,GACA,GACA,GACU;CACV,IAAM,IAAoB,CAAC;CAC3B,KAAK,IAAM,CAAC,GAAM,MAAU,EAAO,QAAQ;EAkBzC,IAjBI,CAAC,EAAM,YACP,EAAM,QAEN,KAAS,EAAM,WAOf,EAAO,cAAc,MAAS,EAAO,cAOrC,EAAM,YAAY,KAAA,KAAa,EAAM,YAAY,MAAM;EAC3D,IAAM,IAAI,EAAS;EACnB,CAAI,KAAM,QAA2B,MAAM,OACzC,EAAQ,KAAK,CAAI;CAErB;CACA,OAAO;AACT;AAMA,IAAM,KAAgB;AAKtB,SAAgB,GACd,GACA,GACU;CACV,IAAM,IAAoB,CAAC;CAC3B,KAAK,IAAM,CAAC,GAAM,MAAU,EAAO,QAAQ;EACzC,IAAI,EAAM,SAAS,SAAS;EAC5B,IAAM,IAAI,EAAS;EACnB,AAAI,OAAO,KAAM,YAAY,MAAM,MAAM,CAAC,GAAc,KAAK,CAAC,KAC5D,EAAQ,KAAK,CAAI;CAErB;CACA,OAAO;AACT;AAIA,SAAS,GAAgB,GAAmB,GAA6B;CACvE,IAAM,oBAAM,IAAI,IAAY;CAC5B,KAAK,IAAM,KAAU,OAAO,OAAO,EAAO,QAAQ,GAC3C,MAAO,UACZ;OAAK,IAAM,KAAO,EAAO,UACvB,IAAI,EAAI,WAAW,KAAa,EAAI,aAAa;GAC/C,IAAM,IAAQ,MAAM,QAAQ,EAAI,WAAW,IAAI,EAAI,cAAc,CAAC,EAAI,WAAW;GACjF,KAAK,IAAM,KAAM,GACf,EAAI,IAAI,CAAE;EAEd;;CAGJ,OAAO;AACT;AAMA,SAAgB,GACd,GACA,GAC0C;CAC1C,IAAI,CAAC,KAAmB,CAAC,GAAQ,OAAO,CAAC;CAEzC,IAAM,IAAmD,CAAC;CAE1D,KAAK,IAAM,CAAC,GAAW,MAAS,OAAO,QAAQ,CAAe,GAAG;EAC/D,IAAM,IAAc,EAAO,SAAS;EACpC,IAAI,CAAC,GAAa;EAGlB,IAAM,IAAW,GAAgB,GAAW,CAAM,GAK5C,IAA2B,CAAC;EAClC,KAAK,IAAM,CAAC,GAAW,MAAU,EAAY,QACvC,EAAY,cAAc,MAAc,EAAY,cACpD,EAAM,YAAY,CAAC,EAAM,QAAQ,CAAC,EAAM,WAAW,CAAC,EAAS,IAAI,CAAS,KAC5E,EAAe,KAAK,CAAS;EAG7B,MAAe,WAAW,GAE9B,KAAK,IAAM,KAAO,GAAM;GACtB,IAAI,EAAI,UAAU;GAElB,IAAM,IAA0B,CAAC;GACjC,KAAK,IAAM,KAAa,GAAgB;IACtC,IAAM,IAAM,EAAI;IAChB,CAAI,KAAO,QAAQ,MAAQ,OACzB,EAAc,KAAK,CAAS;GAEhC;GACA,AAAI,EAAc,SAAS,MACpB,EAAO,OAAY,EAAO,KAAa,CAAC,IAC7C,EAAO,GAAW,EAAI,QAAQ;EAElC;CACF;CAEA,OAAO;AACT;;;ACxFA,IAAM,WAAa,CAAC;AAGpB,SAAS,GAAY,GAA8B,GAAuC;CAExF,OADI,EAAE,WAAW,EAAE,SACZ,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,IADX;AAEpC;AAUA,SAAS,GACP,GACA,GACQ;CACR,IAAI,GAAa;EAEf,IAAM,IAAS,GADE,GAAgB,GAAa,CAClB,CAAQ;EACpC,IAAI,EAAO,QAAQ,cAAc,EAAE,GAAG,OAAO;CAC/C;CAEA,OADI,EAAI,MAAM,OACP,OAAO,WAAW,IADE,OAAO,EAAI,EAAE;AAE1C;AAMA,SAAgB,GAAgB,EAAE,iBAAoC;CACpE,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD,EAAkC,cAAa,CAAA,EACjC,CAAA;AAEpB;AAEA,SAAS,GAAqB,EAAE,iBAAoC;CAClE,IAAM,EAAE,IAAI,MAAU,GAA0B,GAK1C,IAAW,GAAiB,GAC5B,CAAC,GAAc,KAAmB,GAAgB,GAClD,EAAE,MAAM,MAAW,GAAU,GAC7B,IAAW,GAAc,MAAM,EAAE,IAAI,GACrC,IAAa,GAAkB,GAE/B,IAAS,GAAQ,QAAQ,IACzB,IAAS,GAAQ,SAAS,GAAQ,SAAS,EAAO,UAAU,KAAA,GAC5D,IAAa,GAAQ,QACrB,IAAY,GAAqB,GAAQ,QAAQ,GACjD,IAAY,GAAqB,GAAQ,QAAQ,GACjD,IAAY,GAAqB,GAAQ,QAAQ,GACjD,IAAa,GAAqB,GAAQ,SAAS,GAmBnD,WAA2B;EAC/B,IAAI,CAAC,KAAU,CAAC,EAAO,aAAa,CAAC,EAAO,WAAW;EACvD,IAAM,IAAW,GAAoB,CAAM;EAC3C,IAAI,EAAS,WAAW,GAAG;EAC3B,IAAM,IAAU,EAAO,UAAU,EAAS;EAC1C,IAAI,MAAY,mBAAmB,OAAO,GAAU;EACpD,IAAI,MAAY,qBAAqB,OAAO,GAAU,QAAQ;CAEhE,GAAG,GACG,IAAsB,MAAsB,KAAA,GAC5C,IAAK,IAAsB,IAAoB,GAE/C,IAAQ,CAAC,KAAuB,MAAO,OAGvC,IAA0C,QAAc;EAC5D,IAAI,CAAC,KAAM,GAAO;EAClB,IAAI,CAAC,GAAQ,OAAO;EACpB,IAAM,IAAW,GAAoB,CAAM;EAC3C,IAAI,EAAS,WAAW,GACtB,OAAO,GAAG,EAAS,KAAK,EAAG;EAE7B,IAAM,IAAQ,GAAe,GAAI,EAAS,MAAM;EAChD,IAAI,CAAC,GAAO;EACZ,IAAM,IAAmB,CAAC;EAC1B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,KACnC,EAAO,EAAS,MAAM,EAAM,MAAM;EAEpC,OAAO;CACT,GAAG;EAAC;EAAI;EAAO;CAAM,CAAC,GAEhB,IAAa,QAAc;EAC1B,OAEL,OADI,OAAO,KAAa,WAAiB,IAClC,GAAa,CAAQ;CAC9B,GAAG,CAAC,CAAQ,CAAC,GAEP,EAAE,MAAM,GAAc,iBAAc,GAAc,GAAY,CAAQ,GAEtE,IAAiB,GAAgB,KAAc,EAAE,GACjD,IAAiB,GAAgB,KAAc,EAAE,GACjD,IAAiB,GAAgB,KAAc,EAAE,GACjD,IAAkB,GAAiB,KAAc,EAAE,GASnD,IAAkB,OAAO,KAAK,GAAQ,iBAAiB,CAAC,CAAC,EAAE,SAAS,GACpE,IAAW,GAAc,MACzB,IAAY,GAAW,WACvB,IAAoB,GAAW,aAC/B,IAAa,EAAa,IAAI,MAAM,MAAM,QAC1C,CAAC,IAAS,KAAc,EAAS,KAAS,CAAU,GACpD,CAAC,GAAU,KAAe,EAAkC,CAAC,CAAC,GAC9D,CAAC,IAAiB,MAAsB,EAAiC,CAAC,CAAC,GAC3E,CAAC,GAAmB,MAAwB,EAAkC,CAAC,CAAC,GAChF,CAAC,GAAiB,MAAsB,EAAwD,KAAA,CAAS,GACzG,CAAC,IAAa,MAAkB,EAAS,EAAK,GAE9C,CAAC,GAAkB,MAAuB,EAAgE,KAAA,CAAS,GACnH,CAAC,IAAe,MAAoB,EAAmD,EAAE,MAAM,GAAM,CAAC,GAKtG,CAAC,IAAmB,KAAwB,EAAS,EAAK,GAC1D,KAAc,IAAQ,IAAY,GAClC,KAAmB,MAAW,IAU9B,KAAQ,GAAS;CACvB,GAAoB,MAAoB,CAAC,GAAO,GAAM,OAAO;CAK7D,IAAM,CAAC,IAAU,MAAe,QAAe;EAC7C,IAAM,IAAI,SAAS,GAAQ,iBAAiB,OAAO,EAAE;EACrD,OAAO,MAAM,CAAC,IAAI,MAAM;CAC1B,CAAC,GACK,KAAa,EAAuB,IAAI,GACxC,KAAc,EAAkD,IAAI,GACpE,KAAqB,GACxB,MAAwB;EACvB,EAAE,eAAe;EACjB,IAAM,IAAK,GAAW;EACtB,IAAI,CAAC,GAAI;EACT,IAAM,IAAS,EAAG;EAClB,GAAY,UAAU;GAAE,QAAQ,EAAE;GAAS;EAAO;EAClD,IAAM,KAAU,MAAmB;GACjC,IAAI,CAAC,GAAY,SAAS;GAC1B,IAAM,IAAQ,GAAY,QAAQ,SAAS,EAAG,SACxC,IAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,GAAY,QAAQ,SAAS,CAAK,CAAC;GAC5E,EAAG,MAAM,QAAQ,GAAG,EAAK;EAC3B,GACM,UAAa;GAMjB,AALI,GAAY,WAAW,KACzB,GAAY,SAAS,EAAG,MAAM,OAAO,EAAE,KAAK,GAAY,QAAQ,MAAM,GAExE,GAAY,UAAU,MACtB,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,WAAW,CAAI;EAC9C;EAEA,AADA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,WAAW,CAAI;CAC3C,GACA,CAAC,CACH,GAGM,IAAS,GAAc,MACvB,KAAa,OAAO,GAAQ,gBAAiB,WAAW,EAAO,eAAe,KAAA,GAE9E,KAAa,GAAS,OACtB,KAAqB,EAA2B,EAAU;CAQhE,QAAgB;EACd,IAAI,CAAC,IAAY;EACjB,IAAM,IAAW,GAAmB;EAEpC,AADA,GAAmB,UAAU,IACzB,KAAY,MAAa,MAAc,MACzC,GAAe,EAAI;CAEvB,GAAG,CAAC,IAAS,EAAU,CAAC;CAGxB,IAAM,KAAgB,CAAC,CAAC,GAAQ,aAC1B,KAAkB,MAAiB,KAAU,QAAQ,EAAO,EAAQ,gBAAiB,MAGrF,KAAW,GAAY,GACvB,EAAE,aAAS,oBAAgB,0BAAsB,GAAY;CAqBnE,AApBA,QAAgB;EAId,IAAI,CAAC,KAAU,KAAuB,GAAM,SAAS;EACrD,IAAM,IAAW,GAAS,UACpB,IAAU,EAAI,GAAQ,SAAS,IAAI,KAAK,EAAI,EAAO,QAAQ,IAAI,GAC/D,IAAU,GAAQ,SAAS,QAAQ,EAAO,QAAQ;EAExD,AADiB,GAAkB,CAC9B,KACH,GAAQ;GACN;GACA,QAAQ;GACR,OAAO,IAAQ,OAAO,MAAY;GAClC,MAAM;GACN,UAAU;EACZ,CAAC;CAEL,GAAG;EAAC,GAAS;EAAU,GAAQ,SAAS,MAAM;EAAI,GAAQ,QAAQ,MAAM;EAAI,GAAQ,SAAS;EAAM,GAAQ,QAAQ;EAAM;EAAO;EAAqB,GAAM;CAAO,CAAC,GAEnK,QAAgB;EACd,IAAI,CAAC,KAAU,CAAC,KAAU,KAAuB,GAAM,SAAS;EAChE,IAAM,IAAM,GAAkB,GAAS,QAAQ;EAC/C,IAAI,GAAK;GAMP,IAAM,IAAc,EAAI,GAAQ,SAAS,IAAI,KAAK,EAAI,EAAO,QAAQ,IAAI,GACnE,IAAW,GAAgB,GAAmC,CAAM,GAGpE,IACJ,KAAY,CAAC,OAAO,CAAQ,EAAE,WAAW,GAAG,IAAI,OAAO,CAAQ,IAAI;GACrE,GAAe,EAAI,IAAI,CAAY;EACrC;CACF,GAAG;EAAC;EAAQ,GAAQ,QAAQ,MAAM;EAAI,GAAQ,QAAQ;EAAe,GAAS;EAAU;EAAqB,GAAM;CAAO,CAAC;CAG3H,IAAM,KAAiB,QAAc;EACnC,IAAI,CAAC,KAAU,CAAC,IAAa,uBAAO,IAAI,IAAY;EACpD,IAAM,oBAAW,IAAI,IAAY;EACjC,KAAK,IAAM,CAAC,GAAM,MAAU,EAAO,QACjC,AAAI,CAAC,EAAM,QAAQ,CAAC,EAAM,WACxB,EAAS,IAAI,CAAI;EAGrB,KAAK,IAAM,CAAC,MAAS,EAAO,SAAS,CAAC,GACpC,EAAS,IAAI,CAAI;EAEnB,OAAO;CACT,GAAG,CAAC,GAAQ,EAAW,CAAC;CAuFxB,AAzEA,QAAgB;EACV,OAAC,KAAc,CAAC,KAAU,CAAC,KAAU,CAAC,IAI1C;OAFK,MAAS,EAAW,EAAI,GAEzB,OAAO,KAAK,CAAQ,EAAE,WAAW,GAAG;IACtC,IAAM,IAAY,IAAK,GAAc,CAAE,IAAI;IAC3C,AAAI,KAOF,GANsB,GAAkB;KACtC;KACA,UAAU;KACV,QAAQ,KAAA;KACR,OAAO;IACT,CACwB,CAAa,EAAE,SAAS,MAAW;KACzD,EAAW,cAAc;MAAE;MAAQ,eAAe,GAAY,CAAM;KAAE,CAAC;IACzE,CAAC;IAEH,IAAM,IAAgC,CAAC;IAUvC,AATA,GAAe,SAAS,MAAS;KAC/B,IAAM,IAAY,IAAY,EAAU,KAAQ,KAAA;KAChD,IAAI,MAAc,KAAA,GAAW;MAC3B,EAAK,KAAQ;MACb;KACF;KACA,IAAM,IAAQ,GAAoB,GAAQ,GAAQ,CAAI;KACtD,EAAK,KAAQ,GAAmB,GAAQ,CAAI,IAAK,KAAS,OAAS,KAAS;IAC9E,CAAC,GACD,EAAY,CAAI;GAClB;GAQA,IACE,EAAO,aAAa,KAAA,KACpB,EAAO,SAAS,SAAS,KACzB,MAAoB,KAAA,GACpB;IACA,IAAM,IAAiB,EAAO;IAG9B,IAAI,KAAkB,OAAO,KAAK,CAAc,EAAE,SAAS,GAAG;KAC5D,IAAM,IAA6C,CAAC;KACpD,KAAK,IAAM,CAAC,GAAW,MAAS,OAAO,QAAQ,CAAc,GAAG;MAC9D,IAAM,IAAc,GAAQ,SAAS;MACrC,EAAQ,KAAa,EAAK,KAAK,OAAS;OACtC,GAAG;OACH,MAAM,GAAY,GAAa,CAAG;MACpC,EAAE;KACJ;KAEA,AADA,GAAmB,CAAO,GAC1B,GAAoB,CAAc;IACpC,OAKE,AADA,GAAmB,CAAC,CAAC,GACrB,GAAoB,CAAC,CAAC;GAE1B;GAEA,GAAiB,MAAS;IACxB,IAAM,IAAO,IAAI,gBAAgB,CAAI;IAErC,OADA,EAAK,OAAO,MAAM,GACX;GACT,GAAG,EAAE,SAAS,GAAK,CAAC;EAxCpB;CAyCF,GAAG;EAAC;EAAY;EAAQ;EAAgB;EAAW;EAAQ;EAAiB;CAAM,CAAC,GAGnF,QAAgB;EACV,CAAC,KAAS,CAAC,KAAa,CAAC,GAAQ,YAAY,EAAO,SAAS,WAAW,KACxE,MAAoB,KAAA,MACxB,GAAmB,CAAC,CAAC,GACrB,GAAoB,CAAC,CAAC;CACxB,GAAG;EAAC;EAAO;EAAW,GAAQ;CAAQ,CAAC;CAWvC,IAAM,KAAgB,EAAY,YAAY;EAE5C,AADA,MAAM,EAAW,eAAe,GAC5B,IAEF,EADkB,GAAQ,OAAO,QAAQ,QAAQ,EAAE,KAAK,GACtC,KAElB,EAAW,EAAK,GAChB,EAAY,CAAC,CAAC,GACd,GAAmB,CAAC,CAAC,GACrB,GAAqB,CAAC,CAAC,GACvB,GAAmB,KAAA,CAAS,GAC5B,GAAoB,KAAA,CAAS,GAC7B,EAAqB,EAAK;CAE9B,GAAG;EAAC;EAAY;EAAO;EAAQ;CAAQ,CAAC,GAElC,KAAoB,GAAa,GAAmB,MAAmB;EAC3E,GAAa,OAAU;GAAE,GAAG;IAAO,IAAY;EAAM,EAAE;CACzD,GAAG,CAAC,CAAC,GAGC,KAAY,QAAc;EAC9B,IAAI,CAAC,GAAQ,OAAO,OAAO;EAE3B,IAAM,IAAQ,EAAO,MAAM,MAAM,GAAG;EAEpC,OADA,EAAM,IAAI,GACH,EAAM,KAAK,GAAG,KAAK;CAC5B,GAAG,CAAC,CAAM,CAAC,GAEL,KAAkB,QAClB,CAAC,GAAQ,WAAW,CAAC,MAAa,OAAc,MAAY,KACzD,OAAO,OAAO,EAAO,OAAO,EAAE,MAAM,MAAM,EAAE,UAAU,EAAS,GACrE,CAAC,GAAQ,EAAS,CAAC,GAEhB,KAAa,GAAc,GAO3B,KAAyB,QAAsC;EACnE,IAAI,CAAC,GAAQ,OAAO,CAAC;EACrB,IAAM,IAA8B,CAAC;EACrC,KAAK,IAAM,KAAQ,GAA0B,GAAQ,GAAU,CAAK,GAClE,AAAI,GAAe,IAAI,CAAI,MAAG,EAAI,KAAQ;EAE5C,KAAK,IAAM,KAAQ,GAAuB,GAAQ,CAAQ,GACxD,AAAI,GAAe,IAAI,CAAI,MAAG,EAAI,KAAQ;EAE5C,OAAO;CACT,GAAG;EAAC;EAAQ;EAAU;EAAO;CAAc,CAAC,GACtC,KAAiB,OAAO,KAAK,EAAsB,EAAE,SAAS,GAE9D,KAAa,QAAkB;EASnC,IARI,CAAC,KAAc,CAAC,KACf,KAAS,CAAC,KAAe,CAAC,KAAS,CAAC,MAMzC,EAAqB,EAAI,GACrB,KAAgB;EAGpB,IAAM,IAAmC,CAAC,GACpC,IAAe,GAAkB;GAAE;GAAQ;GAAU;GAAQ;EAAM,CAAC,GACpE,IAAmB,GAAwB,CAAY;EAC7D,IAAI,GAEF,KAAK,IAAM,CAAC,GAAK,MAAQ,OAAO,QAAQ,CAAQ,GAAG;GACjD,IAAI,EAAO,OAAO,IAAI,CAAG,GAAG;GAC5B,IAAM,IAAa,GAA0B,GAAQ,GAAK,CAAG;GAC7D,AAAI,MAAe,MAAM,KAAe,SACtC,EAAQ,KAAO;EAEnB;OACK;GAEL,KAAK,IAAM,CAAC,GAAK,MAAQ,OAAO,QAAQ,CAAQ,GAAG;IACjD,IAAI,EAAO,OAAO,IAAI,CAAG,GAAG;IAC5B,IAAM,IAAW,IAAS,GAAoB,GAAQ,GAAQ,CAAG,IAAI,KAAA,GAC/D,IAAa,GAA0B,GAAQ,GAAK,CAAG,GACvD,IAAqB,GAA0B,GAAQ,GAAK,CAAQ;IAI1E,CAFsB,MAAe,KAAK,OAAO,QAC1B,MAAuB,MAAM,MAAuB,KAAA,IAAY,OAAO,OAE5F,EAAQ,KAAO;GAEnB;GACA,AAAI,OACF,EAAQ,eAAe;EAE3B;EAiBA,IAhBI,OAAO,KAAK,CAAY,EAAE,SAAS,MACrC,EAAQ,SAAS,IAGf,OAAO,KAAK,EAAe,EAAE,SAAS,MACxC,EAAQ,YAAe,OAAO,QAAQ,EAAe,EAAE,KACpD,CAAC,GAAW,QAAiB;GAAE,mBAAmB;GAAW,aAAa;EAAW,EACxF,IAGE,OAAO,KAAK,CAAiB,EAAE,SAAS,MAC1C,EAAQ,cAAiB,OAAO,QAAQ,CAAiB,EAAE,KACxD,CAAC,GAAc,QAAY;GAAE,eAAe;GAAc;EAAM,EACnE,IAGE,GAAiB;GACnB,IAAM,IAA6D,CAAC;GACpE,KAAK,IAAM,CAAC,GAAW,MAAS,OAAO,QAAQ,CAAe,GAAG;IAE/D,IAAM,IAAU,EACb,QAAQ,MAAM,CAAC,EAAE,QAAQ,EACzB,KAAK,MAAM;KACV,IAAM,EAAE,SAAM,aAAU,WAAQ,GAAG,MAAW;KAC9C,OAAO;IACT,CAAC,GAEG,IAAW,IAAmB,MAAc,CAAC;IACnD,CAAI,KAAS,CAAC,GAAY,GAAS,CAAQ,OACzC,EAAgB,KAAa;GAEjC;GACA,AAAI,OAAO,KAAK,CAAe,EAAE,SAAS,MACxC,EAAQ,YAAe;EAE3B;EAGA,IAAI,CAAC,KAAS,OAAO,KAAK,CAAO,EAAE,WAAW,GAAG;GAM/C,AALA,EAAW,EAAK,GAChB,GAAmB,CAAC,CAAC,GACrB,GAAqB,CAAC,CAAC,GACvB,GAAmB,KAAA,CAAS,GAC5B,GAAoB,KAAA,CAAS,GAC7B,EAAqB,EAAK;GAC1B;EACF;EAEA,IAAM,UAAqB;GAOzB,AANA,EAAW,EAAK,GAChB,GAAe,EAAK,GACpB,EAAqB,EAAK,GAC1B,GAAmB,CAAC,CAAC,GACrB,GAAqB,CAAC,CAAC,GACvB,GAAmB,KAAA,CAAS,GAC5B,GAAoB,KAAA,CAAS;EAG/B;EAEA,AAAI,IACF,EAAe,OAAO,GAAS,EAC7B,YAAY,MAAa;GACvB,EAAiB,SAAS,MAAW,EAAW,cAAc,CAAM,CAAC;GACrE,IAAM,IAAY,EAAS,MAErB,IAAQ,GADM,IAAS,GAAgB,GAAQ,CAAS,IAAI,CAAC,CAC7B;GAMtC,AAJE,EADE,IACO,GAAG,GAAU,GAAG,mBAAmB,CAAK,MAExC,IAF6C,EAAE,SAAS,GAAK,CAEjC,GAEvC,EAAa;EACf,EACF,CAAC,IACQ,KACT,EAAe,OACb;GAAE;GAAU,MAAM;EAAQ,GAC1B;GACE,iBAAiB;IAEf,AADA,EAAiB,SAAS,MAAW,EAAW,cAAc,CAAM,CAAC,GACrE,EAAa;GACf;GACA,UAAU,MAAQ;IAChB,AAAI,aAAe,MAAY,EAAI,SAAS,kBAC1C,GAAe,EAAI;GAEvB;EACF,CACF;CAEJ,GAAG;EAAC;EAAY;EAAO;EAAW;EAAW;EAAY;EAAU;EAAgB;EAAiB;EAAmB;EAAiB;EAAkB;EAAgB;EAAgB;EAAQ;EAAU;EAAW;EAAQ;EAAY;CAAQ,CAAC,GAE9O,KAAuB,GAAa,GAAmB,MAA4B;EACvF,IAAoB,OAAU;GAAE,GAAI,KAAQ,CAAC;IAAK,IAAY;EAAK,EAAE;CACvE,GAAG,CAAC,CAAC,GAEC,KAAe,QAAkB;EACjC,CAAC,KAAY,KAAS,CAAC,KAC3B,EAAe,OAAO,GAAU,EAC9B,iBAAiB,EAAS,IAAW,EAAE,SAAS,GAAK,CAAC,EACxD,CAAC;CACH,GAAG;EAAC;EAAU;EAAO;EAAW;EAAgB;EAAU;CAAS,CAAC,GAE9D,KAAgB,QAAkB;EAClC,CAAC,KAAY,KAAS,CAAC,KAC3B,EAAgB,OAAO,CAAQ;CACjC,GAAG;EAAC;EAAU;EAAO;EAAY;CAAe,CAAC,GAG3C,KAAwB,QACtB,GAAwB,GAAiB,CAAM,GACrD,CAAC,GAAiB,CAAM,CAC1B,GACM,KAAiB,OAAO,KAAK,EAAqB,EAAE,SAAS;CAEnE,IAAI,CAAC,KAAU,CAAC,GAAQ,OAAO;CAC/B,IAAI,CAAC,KAAS,GAAW,OAAO,kBAAC,IAAD,CAAiB,CAAA;CACjD,IAAI,CAAC,KAAS,CAAC,GACb,OACE,kBAAC,OAAD;EAAK,OAAO,EAAE,SAAS,GAAG;YAA1B,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAG,cAAc;GAAE;aAA7E,CACG,MACC,kBAAC,GAAD;IACE,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAC5B,eAAe,EAAS,EAAS;IACjC,OAAM;GACP,CAAA,GAEH,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAG,UAAU;KAAI,YAAY;KAAK,OAAO;IAAc;cAAG;GAAa,CAAA,CACzF;MACL,kBAAC,KAAD;GAAG,OAAO,EAAE,OAAO,gBAAgB;aAAG;EAAoB,CAAA,CACvD;;CAIT,IAAM,KAAgB,EAAO,SAEvB,KAAa,EAAI,EAAO,SAAS,IAAI,KAAK,EAAI,GAAc,IAAI,GAIhE,KAAe,GAAgB,GAAsD,CAAM,GAC3F,KAAQ,IACV,OAAO,OACP,KAAe,OAAO,EAAY,IAAI,IAEpC,KAAW,EAAe,aAAa,EAAe,WAGtD,KAA2B;EACvB;EACR;EACY;EACZ,UAAU,KAAc,KAAM;EAC9B,QAAQ,KAAmB;GAAE,GAAI,KAAU,CAAC;GAAI,GAAG;EAAS,IAAK,KAAU,CAAC;EAC5E;EACA,MAAM,KAAmB,SAAS;EAClC,eAAe,KAAmB,KAAoB,KAAA;EACtD,MAAM;EACN,yBAAyB;GACvB,IAAM,IAAO,KAAoB,CAAC;GAClC,IAAI,CAAC,MAAoB,OAAO,KAAK,CAAiB,EAAE,WAAW,GAAG,OAAO,EAAK,SAAS,IAAI,IAAO,KAAA;GACtG,IAAM,IAAS,EAAK,KAAK,MACvB,EAAkB,EAAE,mBAAmB,KAAA,IAEnC,IADA;IAAE,GAAG;IAAG,OAAO,EAAkB,EAAE;GAAe,CAExD;GACA,KAAK,IAAM,CAAC,GAAM,MAAU,OAAO,QAAQ,CAAiB,GAC1D,AAAK,EAAK,MAAM,MAAM,EAAE,kBAAkB,CAAI,KAC5C,EAAO,KAAK;IAAE,eAAe;IAAM,MAAM,CAAC;IAAG,MAAM;IAAU;GAAM,CAAC;GAGxE,OAAO;EACT,GAAG;EACH,eAAgB,KAAS,CAAC,IACtB,KAAA,IACA,MACG,GAAc,MAAU,IAAsB,OAAU;GAAE,GAAG;IAAO,IAAe;EAAM,EAAE,IAC5F,KAAA;EACN,mBAAmB;EAGnB,uBAAuB;GACrB,IAAM,IAAO,KAAY,CAAC;GAC1B,IAAI,CAAC,MAAoB,OAAO,KAAK,EAAe,EAAE,WAAW,GAAG,OAAO,EAAK,SAAS,IAAI,IAAO,KAAA;GAEpG,IAAM,IAAS,EAAK,KAAK,MACvB,GAAgB,EAAE,gBAAgB,KAAA,IAE9B,IADA;IAAE,GAAG;IAAG,aAAa,GAAgB,EAAE;GAAY,CAEzD;GAEA,KAAK,IAAM,CAAC,GAAI,MAAO,OAAO,QAAQ,EAAe,GACnD,AAAK,EAAK,MAAM,MAAM,EAAE,eAAe,CAAE,KACvC,EAAO,KAAK;IAAE,YAAY;IAAI,aAAa;IAAI,aAAa,CAAC;IAAG,QAAQ,EAAmB;GAAE,CAAC;GAGlG,OAAO;EACT,GAAG;EACH,aAAc,KAAS,CAAC,IACpB,KAAA,IACA,MACG,GAAW,MAAe,IAAoB,OAAU;GAAE,GAAG;IAAO,IAAY;EAAW,EAAE,IAC9F,KAAA;EACN,iBAAiB;EAEjB,iBAAiB,KAAmB,IAAkB,KAAA;EACtD,kBAAkB,KAAmB,KAAuB,KAAA;EAC5D,uBAAuB,KAAmB,KAAwB,KAAA;EAGlE,aAAa,MAAoB,KAAoB,KAAyB,KAAA;CAChF,GAEM,KAAa,EAAO,WAAW,EAAO,QAAQ,SAAS,GAIvD,KACJ,CAAC,KAAS,IACR,kBAAC,IAAD;EACU;EACA;EACI;EACJ;EACR,MAAM;EACN,gBAAgB;EACR;EACR,QAAQ;CACT,CAAA,IACC,KAAA,GAEA,KACJ,kBAAC,IAAD;EACS;EACP,UAAU;EACV,SAAS;EACF;EACP,qBAAqB,CAAC,CAAC;EACN;EACjB,OAAO;EACG;EACM;EAChB,gBAAgB,MAAqB;EACzB;EACJ;EACA;EACR,eAAe,GAAQ;EACX;EACJ;EACJ;EACO;EACX,iBAAiB,CAAC,CAAC;EACnB,YAAY;EACZ,cAAc,EAAS,KAAkB,KAAY,GAAG;EACxD,cAAc;EACd,QAAQ;EACR,WAAW;EACX,aAAa,EAAgB;CAC9B,CAAA,GAGG,KAAgB,MACpB,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,cAAc;GACd,cAAc;GACd,QAAQ;GACR,YAAY;GACZ,OAAO;GACP,UAAU;EACZ;YAZF,CAcE,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA,GACzB,kBAAC,QAAD,EAAA,UAAA,CAAM,+BACwB,GAAsB,OAAO,EAAQ,EAAO,YAAa,CAAC,CAClF,EAAA,CAAA,CACH;KAGD,KAAc,MAClB,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,cAAc;GACd,cAAc;GACd,QAAQ;GACR,YAAY;GACZ,OAAO;GACP,UAAU;EACZ;YAZF,CAcE,kBAAC,EAAK,SAAN,EAAc,MAAM,GAAK,CAAA,GACzB,kBAAC,QAAD,EAAA,UAAM,0EAA6E,CAAA,CAChF;KAGD,KAAiB,MAAoB,MAAqB,MAC9D,kBAAC,IAAD;EACE,QAAQ,OAAO,QAAQ,EAAsB,EAAE,KAAK,CAAC,GAAM,OAA8B;GACvF,IAAM,IAAQ,EAAO,OAAO,IAAI,CAAI;GACpC,OAAO;IACL;IACA,OAAO,IAAQ,GAAiB,GAAO,GAAM,GAAQ,QAAQ,IAAI;IACjE;GACF;EACF,CAAC;EACD,SAAS,MAAS;GAChB,IAAM,IAAK,SAAS,eAAe,GAAG,EAAW,GAAG,GAAM;GAC1D,AAAI,MACF,EAAG,eAAe;IAAE,OAAO;IAAU,UAAU;GAAS,CAAC,GACzD,EAAoB,QAAQ;EAEhC;CACD,CAAA,GAGG,KAAa,KAAmB,CAAC,KAAS,EAAE,EAAO,SAAS,EAAO,MAAM,SAAS,MACtF,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,IAAD;GACE,cAAc,EAAO,iBAAiB,CAAC;GACvC,gBAAgB,GAAU;GAC1B,aAAc,KAAmB,MAC5B,GAAW,MAAe,IAAoB,OAAU;IAAE,GAAG;KAAO,IAAY;GAAW,EAAE,IAC9F;GAEJ,WAAW;GACX,MAAM,KAAmB,SAAS;EACnC,CAAA;CACE,CAAA,GAGD,KACJ,kBAAA,GAAA,EAAA,UAAA;EACG,EAAO,SAAS,EAAO,MAAM,SAAS,MAAM,KAAU,MAAU,KAC/D,kBAAC,IAAD;GAAgB,OAAO,EAAO;GAAO,KAAK;EAAY,CAAA;EAEvD,CAAC,KAAS,KAAM,KAAc,EAAO,gBAAgB,OAAO,KAAK,EAAO,YAAY,EAAE,SAAS,KAAK,KACnG,kBAAC,IAAD;GAA6B;GAAY,UAAU,KAAc,KAAM;GAAI,cAAc,EAAO;GAAsB;EAAS,CAAA;EAEhI,CAAC,KAAS,KAAU,KACnB,kBAAC,IAAD;GACU;GACA;GACI;GACJ;GACR,MAAM;GACE;GACR,QAAQ;EACT,CAAA;CAEH,EAAA,CAAA,GAGE,KACJ,kBAAC,IAAD;EACE,MAAM,GAAc;EACpB,eAAe,MAAS,CAAC,KAAQ,GAAiB,EAAE,MAAM,GAAM,CAAC;EACjE,OAAO,UAAU;EACjB,aACE,GAAc,eACV,GAAc,eACd,KACE,wCAAwC,GAAW,YAAY,EAAE,2CACjE,wCAAwC,GAAW,YAAY,EAAE;EAEzE,cAAa;EACb,WAAW;EACX,aAAA;CACD,CAAA;CAuDH,OAjDI,KAEA,kBAAC,OAAD;EAAK,OAAO;GAAE,QAAQ;GAAQ,SAAS;GAAQ,UAAU;GAAU,YAAY;EAAY;YAA3F;GAEE,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;KAAG,SAAS;KAAQ,eAAe;KAAU,QAAQ;IAAO;cAA7F,CAEE,kBAAC,OAAD;KAAK,OAAO;MAAE,YAAY;MAAG,SAAS;KAAgB;eAAtD;MACG;MACA;MACA;MACA;MACA;KACE;QAEL,kBAAC,OAAD;KAAK,OAAO;MAAE,MAAM;MAAG,WAAW;MAAG,WAAW;MAAQ,SAAS;MAAiB,SAAS;MAAQ,eAAe;MAAU,KAAK;KAAG;eACjI;IACE,CAAA,CACF;;GAGL,kBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,OAAM;cAEN,kBAAC,OAAD,EACE,OAAO;KACL,UAAU;KACV,KAAK;KACL,QAAQ;KACR,MAAM;KACN,WAAW;KACX,OAAO;KACP,YAAY;IACd,EACD,CAAA;GACE,CAAA;GAEL,kBAAC,OAAD;IAAK,KAAK;IAAY,OAAO;KAAE,YAAY;KAAG,QAAQ;KAAQ,OAAO,GAAG,GAAS;IAAI;eACjF,KAAU,MAAU,KACpB,kBAAC,IAAD;KAAgB,OAAO,EAAO;KAAU,KAAK;IAAY,CAAA;GAExD,CAAA;GACJ;EACE;MAMP,kBAAC,OAAD;EAAK,OAAO;GAAE,QAAQ;GAAQ,SAAS;GAAQ,eAAe;GAAU,UAAU;GAAU,YAAY;EAAY;YAApH;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG;KACA;KACA;KACA;KACA;IACE;;GACL,kBAAC,OAAD;IAAK,WAAU;cACZ;GACE,CAAA;GACJ;EACE;;AAET;AAoCA,SAAS,GAAa,EACpB,UACA,aACA,YACA,UACA,wBACA,oBACA,UACA,aACA,mBACA,mBACA,eACA,WACA,WACA,kBACA,eACA,WACA,OACA,cACA,oBACA,eACA,WACA,iBACA,WACA,cACA,kBACoB;CACpB,IAAM,EAAE,WAAQ,GAAgB,GAC1B,IAAc,GAAe,GAI7B,IAAW,GAAiB,GAC5B,IAAS,GAAc,GAOvB,IAAsB,GAAQ,iBAAiB,MAC/C,IAAsB,GAAQ,iBAAiB,MAC/C,KAAe,KAAsB,GAIrC,IAAiB,GAHC,KACpB,GAAoB,IAAc,GAAQ,SAAS,GAAe,QAAQ,IAC1E,GAAmB,GAAQ,SAAS,QAAQ,GAG9C,GAAQ,SACR,GACA,KAAU,CAAC,GACX,CAAC,CACH,GAOM,IAAgB,QAAyC;EAC7D,IAAI,CAAC,GAAQ;EACb,IAAM,IAAS,IAAK,CAAC,CAAE,IAAI,CAAC;EAC5B,OAAO;GACL;GACA,YAAY,KAAc;GAC1B;GACA,aAAa,GAAQ,SAAS;GAC9B,cAAc,IAAS,CAAC,CAAM,IAAI,CAAC;GACnC,aAAa;GACb;GACA,cAAc,GAAK,MAAS,EAAY,GAAK;IAAE,OAAO,GAAM;IAAO,MAAM,GAAM;IAAM,QAAQ,GAAM;GAAO,CAAC;GAC3G,eAAe,GAAM,MAAW,EAAa,GAAM,CAA4C;EACjG;CACF,GAAG;EAAC;EAAQ;EAAY;EAAQ;EAAI;EAAQ;EAAQ;CAAW,CAAC,GA6B1D,IAAY,IAChB,IACE,kBAAC,GAAD;EACE,MAAK;EACL,MAAoB,EAAd,IAAe,IAAwB,EAAK,SAA9B,EAAS,MAAM,GAAK,CAA6B;EACrE,SAAS;EACT,UAAU;YAET,IAAc,eAAe;CAC3B,CAAA,IACH,OACF,IACF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;EAAK,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;EAAG,SAAS;EAAc,UAAU;YAAU;CAEvE,CAAA,GACL,kBAAC,GAAD;EACE,MAAK;EACL,MAAiB,EAAX,IAAY,IAAwB,EAAK,MAA9B,EAAS,MAAM,GAAK,CAA0B;EAC/D,SAAS;EACT,UAAU,KAAY,KAAkB;EACxC,OACE,IACI,6BACA,IACE,+BACA,KAAA;YAGP,IAAW,YAAY;CACrB,CAAA,CACL,EAAA,CAAA,IACA,CAAC,KAAS,KAAM,EAAe,SAAS,IAC1C,kBAAC,IAAD;EACE,QAAQ;EACR,KAAK;EACL,QA/DqB,GAAc,MAA4C;GACjF,IAAM,IAAW,GAAe,GAAM,GAAQ,EAAE,KAAK,KAAU,KAAA,EAAU,CAAC;GAC1E,IAAI,MAAa,MAAM;GACvB,IAAM,IAAM,IAAK,CAAC,CAAE,IAAI,CAAC,GAQnB,IAAgB,MAAS,cAAc,IAAkB,IAAY;GAG3E,OAAO,EAAI;IACT;IACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;IACpC,UAAU,MAAW,EAAa,GAAU,EAAgB,GAAQ,CAAG,CAAC;IACxE,WAAW,UACD,EAAS,GAAe,EAAE,SAAS,GAAK,CAAC,IAC/C,KAAA;GACN,CAAC;EACH;EA0CI,MAAK;EACL,eAAe,GAAQ;EACR;EACf,oBAAoB;EACL;CAChB,CAAA,IACC;CAEJ,OACE,kBAAC,IAAD;EACE,MAAM,GAAQ,QAAQ,OAAO,kBAAC,GAAD;GAAa,MAAM,EAAO,QAAQ;GAAM,MAAM;EAAK,CAAA,IAAI,KAAA;EAC7E;EACG;EACE;EACZ,MAAM,IAAQ,WAAW,IAAU,SAAS;EACrC;EACP,UAAU,CAAC,KAAuB;EAC1B;EACR,OAAO;CACR,CAAA;AAEL;;;AC3oCA,eAAsB,GACpB,GACA,GACA,GACA,GACA,GACqB;CACrB,IAAM,IAAuC;EAC3C,mBAAmB;EACnB,aAAa;CACf;CACA,AAAI,KAAQ,SAAM,EAAY,OAAO;CASrC,IAAM,KAFS,MALQ,EAAc,GAAG,EAAO,YAAY;EACzD,YAAY,CAAC,CAAE;EACf;EACA,QAAQ,EAAE,WAAW,CAAC,CAAW,EAAE;CACrC,CAAC,GACuB,QACJ,OAAO,IACH,aAAgB,CAAC;CACzC,OAAO,EAAS,MAAM,MAAM,EAAE,eAAe,CAAS,KAAK,EAAS;AACtE;;;AC9BA,SAAgB,GAAa,GAA4B,GAAwB;CAC/E,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,EAAE,cAAW,eAAY,cACpC,GAA0B,GAAS,GAAK,GAAW,GAAY,CAAI;EACrE,iBAAiB;GAGf,EAAY,kBAAkB,EAC5B,UAAU,CAAC,eAAe,CAAM,EAClC,CAAC;EACH;EACA,UAAU,MAAe;GACvB,EAAM,MAAM,EAAI,OAAO;EACzB;CACF,CAAC;AACH;;;ACTA,eAAsB,GACpB,GACA,GACA,GACA,GACwB;CAQxB,IAAM,KAFS,MALQ,EAAc,GAAG,EAAO,YAAY;EACzD,YAAY,CAAC,CAAE;EACf;EACA,QAAQ,EAAE,aAAa,CAAC;GAAE,eAAe;GAAc;EAAM,CAAC,EAAE;CAClE,CAAC,GACuB,QACJ,OAAO,IACN,eAAkB,CAAC;CACxC,OAAO,EAAM,MAAM,MAAM,EAAE,kBAAkB,CAAY,KAAK,EAAM;AACtE;AAEA,eAAsB,GACpB,GACA,GACA,GACe;CACf,MAAM,EAAc,GAAG,EAAO,YAAY;EACxC,YAAY,CAAC,CAAE;EACf;EACA,QAAQ,EAAE,aAAa,CAAC;GAAE,eAAe;GAAc,OAAO;EAAK,CAAC,EAAE;CACxE,CAAC;AACH;;;ACnCA,SAAgB,GAAe,GAA4B,GAAwB;CACjF,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,EAAE,iBAAc,eAC3B,GAA2B,GAAS,GAAK,GAAc,CAAK;EAC9D,iBAAiB;GACf,EAAY,kBAAkB,EAC5B,UAAU,CAAC,eAAe,CAAM,EAClC,CAAC;EACH;EACA,UAAU,MAAe;GACvB,EAAM,MAAM,EAAI,OAAO;EACzB;CACF,CAAC;AACH;AAEA,SAAgB,GAAkB,GAA4B,GAAwB;CACpF,IAAM,IAAc,GAAe;CAEnC,OAAO,GAAY;EACjB,aAAa,MACX,GAA8B,GAAS,GAAK,CAAY;EAC1D,iBAAiB;GACf,EAAY,kBAAkB,EAC5B,UAAU,CAAC,eAAe,CAAM,EAClC,CAAC;EACH;EACA,UAAU,MAAe;GACvB,EAAM,MAAM,EAAI,OAAO;EACzB;CACF,CAAC;AACH;;;ACGA,SAAgB,GAAoB,EAAE,iBAAwC;CAC5E,IAAM,CAAC,GAAc,KAAmB,GAAgB,GAClD,EAAE,MAAM,MAAW,GAAU,GAE7B,IAAS,GAAQ,QAAQ,IACzB,IAAS,GAAQ,SAAS,GAAQ,SAAS,EAAO,UAAU,KAAA,GAC5D,IAAa,GAAQ,QAErB,EAAE,eAAY,GAAY;CAChC,QAAgB;EACd,IAAI,GAAQ,SAAS,GAAQ;GAC3B,IAAM,IAAU,EAAI,EAAO,SAAS,IAAI,KAAK,EAAI,EAAO,QAAQ,WAAW,GACrE,IAAU,EAAO,SAAS,QAAQ,EAAO,QAAQ;GACvD,EAAQ;IACN,UAAU,EAAO;IACjB,QAAQ,EAAa,SAAS;IAC9B,OAAO;IACP,MAAM;IACN,UAAU;GACZ,CAAC;EACH;CACF,GAAG;EAAC,GAAQ;EAAO,GAAQ,SAAS,MAAM;EAAI,GAAQ,QAAQ,aAAa;CAAE,CAAC;CAE9E,IAAM,IAAa,EAAa,IAAI,UAAU,KAAK,MAC7C,IAAc,EAAa,IAAI,QAAQ,KAAK,IAC5C,IAAkB,GAAY,GAAa,GAAG,GAC9C,IAAY,EAAa,IAAI,MAAM,KAAK,GAAQ,gBAAgB,IAEhE,IAAc,QAAc;EAChC,IAAM,IAAM,EAAa,IAAI,SAAS;EACjC,OACL,IAAI;GACF,IAAM,IAAS,KAAK,MAAM,CAAG;GAC7B,OAAO,MAAM,QAAQ,CAAM,KAAK,EAAO,SAAS,IAAI,IAAS,KAAA;EAC/D,QAAQ;GACN;EACF;CACF,GAAG,CAAC,CAAY,CAAC,GAEX,EAAE,MAAM,GAAc,WAAW,MAAgB,GAAc,GAAY;EAC/E,MAAM;EACN,UAAU;EACV,MAAM,KAAa,KAAA;EACnB,QAAQ,KAAmB,KAAA;EAC3B,SAAS;CACX,CAAC,GAEK,IAAU,GAAc,QAAQ,CAAC,GACjC,IAAW,GAAc,OAEzB,IAAU,QACR,GAAQ,OAAO,MAAM,EAAE,MAAM,MAAM,EAAO,OAAO,IAAI,CAAC,GAAG,OAAO,KAAK,MAC3E,CAAC,CAAM,CACT,GAEM,IAAe,GAClB,MAAsB;EACrB,GACG,MAAS;GACR,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAGrC,OAFI,IAAI,EAAK,IAAI,YAAY,CAAE,IAC1B,EAAK,OAAO,UAAU,GACpB;EACT,GACA,EAAE,SAAS,GAAK,CAClB;CACF,GACA,CAAC,CAAe,CAClB,GAEM,IAAe,GAClB,MAAkB;EACjB,GACG,MAAS;GACR,IAAM,IAAO,IAAI,gBAAgB,CAAI;GAGrC,OAFI,IAAO,EAAK,IAAI,UAAU,CAAK,IAC9B,EAAK,OAAO,QAAQ,GAClB;EACT,GACA,EAAE,SAAS,GAAK,CAClB;CACF,GACA,CAAC,CAAe,CAClB,GAEM,IAAe,GAClB,GAAe,MAAkB;EAChC,GACG,MAAS;GACR,IAAM,IAAO,IAAI,gBAAgB,CAAI,GAC/B,IAAU,GAAoB,CAAI,GACpC;GAOJ,OANA,AAGE,IAHE,KAAS,MAAU,YACX,CAAC,GAAG,GAAa,GAAS,CAAK,GAAG,GAAS,GAAO,CAAK,CAAC,IAExD,GAAa,GAAS,CAAK,GAEvC,GAAgB,GAAS,CAAI,GACtB;EACT,GACA,EAAE,SAAS,GAAK,CAClB;CACF,GACA,CAAC,CAAe,CAClB,GAEM,IAAgB,GAAQ,kBAAkB,IAC1C,IAAe,EAAuB,IAAI,GAC1C,CAAC,GAAY,KAAiB,EAAwB,IAAI,GAC1D,IAAa,EAAO,EAAK,GAEzB,IAAe,GAAQ;CAC7B,QAAgB;EACd,IAAI,CAAC,KAAiB,MAAe,MAAM;EAC3C,IAAM,IAAY,EAAa;EAC1B,OACL,IAAI,KAAgB,EAAa,SAAS,GAAG,GAAG;GAC9C,IAAM,IAAM,WAAW,CAAY,IAAI;GACvC,EAAc,KAAK,MAAM,EAAU,sBAAsB,EAAE,QAAQ,CAAG,CAAC;EACzE,OAAO,AACL,EADS,KACK,SAAS,GAAc,EAAE,KAEzB,GAAG;CAErB,GAAG;EAAC;EAAe;EAAc;EAAY;CAAM,CAAC;CAEpD,IAAM,IAAc,EAA4B,IAAI;CACpD,cACe;EACX,EAAY,UAAU;CACxB,GACC,CAAC,CAAC;CAEL,IAAM,IAAkB,GACrB,MAAwB;EAEvB,AADA,EAAE,eAAe,GACjB,EAAW,UAAU;EACrB,IAAM,IAAS,EAAE,SACX,IAAa,KAAc,KAE3B,KAAe,MAAmB;GACtC,IAAI,CAAC,EAAW,SAAS;GACzB,IAEM,KAFiB,EAAa,SAAS,sBAAsB,EAAE,SAAS,OAAO,cAEnD;GAElC,EADiB,KAAK,IAAI,KAAU,KAAK,IAAI,GAAU,IAAa,EAAG,UAAU,CAAM,CACzE,CAAQ;EACxB,GAEM,UAAgB;GAMpB,AALA,EAAW,UAAU,IACrB,SAAS,oBAAoB,aAAa,CAAW,GACrD,SAAS,oBAAoB,WAAW,CAAO,GAC/C,SAAS,KAAK,MAAM,SAAS,IAC7B,SAAS,KAAK,MAAM,aAAa,IACjC,EAAY,UAAU;EACxB;EAMA,AAJA,SAAS,KAAK,MAAM,SAAS,cAC7B,SAAS,KAAK,MAAM,aAAa,QACjC,SAAS,iBAAiB,aAAa,CAAW,GAClD,SAAS,iBAAiB,WAAW,CAAO,GAC5C,EAAY,UAAU;CACxB,GACA,CAAC,CAAU,CACb;CAEA,IAAI,CAAC,KAAU,CAAC,KAAU,CAAC,GAAQ,OAAO;CAE1C,IAAM,IACJ,EAAO,WACP,EAAO,OAAO,MAAM,EACjB,QACE,MACC,CAAC,EAAO,OAAO,IAAI,CAAC,GAAG,WACvB,CAAC,EAAO,OAAO,IAAI,CAAC,GAAG,QACvB,EAAO,OAAO,IAAI,CAAC,GAAG,SAAS,gBAAgB,EACnD,EACC,MAAM,GAAG,CAAC,GAET,IAAoB,GAAa,UAAU;CAEjD,OACE,kBAAC,OAAD;EAAK,KAAK;EAAc,OAAO;GAAE,SAAS;GAAQ,QAAQ;GAAQ,UAAU;EAAS;YAArF;GACE,kBAAC,OAAD;IACE,OAAO;KACL,MAAM;KACN,SAAS;KACT,eAAe;KACf,UAAU;KACV,aAAa,IAAgB,SAAS;KACtC,OAAO,KAAiB,MAAe,OAAO,IAAa,EAAO,cAAc;IAClF;cARF,CAUE,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,SAAS;MACT,eAAe;MACf,KAAK;MACL,cAAc;KAChB;eAPF;MASE,kBAAC,GAAD;OACE,OAAO;OACP,UAAU;OACV,aAAa,UAAU,EAAI,EAAO,QAAQ,WAAW,EAAE;OACvD,QAAQ,kBAAC,EAAK,QAAN,EAAa,MAAM,GAAK,CAAA;MACjC,CAAA;MACA,EAAO,WAAW,EAAO,QAAQ,SAAS,KACzC,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,UAAU;QAAQ,KAAK;OAAE;iBACrD,EAAO,QAAQ,KAAK,MAAgB;QACnC,IAAM,IAAW,EAAO,OAAO,IAAI,CAAW;QAC9C,IAAI,CAAC,GAAU,QAAQ,OAAO;QAE9B,IAAM,IADa,GAAa,MAAM,MAAM,EAAE,UAAU,KAAe,EAAE,OAAO,IAAI,GAClD,SAAoB,WAChD,IAAQ,GAAiB,GAAU,GAAa,EAAO,QAAQ;QAKrE,OACE,kBAAC,OAAD;SAAuB,OAAO,EAAE,UAAU,IAAI;mBAC5C,kBAAC,IAAD;UACE,OAAO;UACE,SAAA,CAPb;WAAE,OAAO;WAAW,OAAO,OAAO;UAAQ,GAC1C,GAAG,EAAS,OAAO,KAAK,OAAO;WAAE,OAAO,EAAE;WAAM,OAAO,EAAI,EAAE,IAAI,KAAK,EAAE;UAAK,EAAE,CAMlE;UACT,WAAW,MAAM,EAAa,GAAa,CAAC;UAC5C,aAAa;SACd,CAAA;QACE,GAPK,CAOL;OAET,CAAC;MACE,CAAA;MAEP,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,gBAAgB;QAChB,UAAU;QACV,OAAO;OACT;iBAPF,CASE,kBAAC,QAAD,EAAA,UACG,IAAc,aAAa,GAAG,EAAQ,OAAO,GAAG,EAAI,EAAO,QAAQ,WAAW,IAC3E,CAAA,GACL,IAAoB,KACnB,kBAAC,UAAD;QACE,eACE,GACG,MAAS;SACR,IAAM,IAAO,IAAI,gBAAgB,CAAI;SAErC,OADA,EAAK,OAAO,SAAS,GACd;QACT,GACA,EAAE,SAAS,GAAK,CAClB;QAEF,OAAO;SACL,YAAY;SACZ,QAAQ;SACR,OAAO;SACP,QAAQ;SACR,UAAU;SACV,gBAAgB;SAChB,SAAS;QACX;kBACD;OAEO,CAAA,CAEP;;KACF;QAEL,kBAAC,OAAD;KAAK,OAAO;MAAE,MAAM;MAAG,WAAW;KAAO;eACtC,IACC,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAI,SAAS;OAAQ,eAAe;OAAU,KAAK;MAAE;gBACzE,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MACjC,kBAAC,OAAD,EAEE,OAAO;OAAE,QAAQ;OAAI,cAAc;OAAG,YAAY;MAAmB,EACtE,GAFM,CAEN,CACF;KACE,CAAA,IACH,EAAQ,WAAW,IACrB,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAI,WAAW;OAAU,UAAU;OAAM,OAAO;MAAgB;gBAAG;KAErF,CAAA,IAEL,EAAQ,KAAK,MACX,kBAAC,IAAD;MAEU;MACA;MACR,SAAS;MACA;MACT,MAAM;MACN,YAAY,EAAO,OAAa;MAChC,eAAe,EAAa,OAAO,EAAO,MAAY,EAAE,CAAC;KAC1D,GARM,OAAO,EAAO,MAAY,EAAE,CAQlC,CACF;IAEA,CAAA,CACF;;GAEJ,KACC,kBAAC,OAAD;IACE,aAAa;IACb,OAAO;KACL,MAAM;KACN,OAAO;KACP,QAAQ;KACR,YAAY;KACZ,YAAY;IACd;IACA,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;IACzD,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;GAC1D,CAAA;GAGH,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,UAAU;KAAG,UAAU;IAAS;cACpD,IACC,kBAAC,IAAD;KAEc;KACZ,UAAU;KACV,OAAO,EAAO;KACN;KACA;IACT,GANM,CAMN,IAED,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,YAAY;MACZ,gBAAgB;MAChB,QAAQ;MACR,OAAO;KACT;eAEA,kBAAC,KAAD;MAAG,OAAO,EAAE,UAAU,KAAK;gBAA3B;OAA8B;OAAU,EAAI,EAAO,QAAQ,IAAI;OAAE;MAAmB;;IACjF,CAAA;GAEJ,CAAA;EACF;;AAET;AAYA,SAAS,GAAc,EACrB,WACA,WACA,YACA,SACA,eACA,cACqB;CACrB,IAAM,IAAa,GAAc,GAC3B,IAAa,EAAQ,IACrB,IAAe,EAAQ,IACvB,IAAY,EAAQ,MAAM,GAAG,CAAC;CAEpC,OACE,kBAAC,UAAD;EACW;EACT,OAAO;GACL,OAAO;GACP,WAAW;GACX,SAAS;GACT,cAAc;GACd,YAAY,IAAa,2BAA2B;GACpD,YAAY,IAAa,4BAA4B;GACrD,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,eAAe;GACf,KAAK;GACL,QAAQ;GACR,mBAAmB;EACrB;EACA,eAAe,MAAM;GACnB,AAAK,MAAY,EAAE,cAAc,MAAM,aAAa;EACtD;EACA,eAAe,MAAM;GACnB,AAAK,MAAY,EAAE,cAAc,MAAM,aAAa;EACtD;YAtBF,CAwBG,KACC,kBAAC,OAAD;GACE,OAAO;IACL,UAAU;IACV,YAAY;IACZ,UAAU;IACV,cAAc;IACd,YAAY;IACZ,OAAO;GACT;aAEC,GAAkB;IACjB;IACA,WAAW;IACX;IACA,MAAM;IACN;IACA,QAAQ;GACV,CAAC;EACE,CAAA,GAEP,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAE;aAA5D,CACG,KACC,kBAAC,OAAD;IACE,OAAO;KACL,UAAU;KACV,OAAO;KACP,MAAM;KACN,UAAU;KACV,cAAc;KACd,YAAY;IACd;cAEC,GAAkB;KACjB;KACA,WAAW;KACX;KACA,MAAM;KACN;KACA,QAAQ;IACV,CAAC;GACE,CAAA,GAEP,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,YAAY;IAAE;cACxE,EAAU,KAAK,MACA,GAAoB,GAAQ,GAAQ,CAC9C,KAAS,OAAa,OAExB,kBAAC,OAAD;KAAe,OAAO,EAAE,UAAU,GAAG;eAClC,GAAkB;MACjB;MACA,WAAW;MACX;MACA,MAAM;MACN;MACA,QAAQ;KACV,CAAC;IACE,GATK,CASL,CAER;GACE,CAAA,CACF;IACC;;AAEZ;AAUA,SAAS,GAAkB,EACzB,eACA,aACA,UACA,WACA,aACyB;CACzB,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD;EACc;EACF;EACH;EACC;EACA;CACT,CAAA,EACa,CAAA;AAEpB;AAEA,SAAS,GAAuB,EAC9B,eACA,aACA,UACA,WACA,aACyB;CACzB,IAAM,IAAY,GAAqB,GAAQ,QAAQ,GACjD,EAAE,MAAM,GAAc,iBAAc,GAAc,GAAY,CAAQ,GACtE,IAAiB,GAAgB,CAAU,GAC3C,IAAa,GAAkB,GAE/B,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAU,KAAe,EAAkC,CAAC,CAAC,GAE9D,IAAS,GAAc,MACvB,IAAW,GAAS,WACpB,IAAmB,GAAS,aAC5B,IAAoB,GAAa,GAAY,CAAQ,GACrD,IAAsB,GAAe,GAAY,CAAQ,GAEzD,IAAiB,QAAc;EACnC,IAAM,oBAAW,IAAI,IAAY;EACjC,KAAK,IAAM,CAAC,GAAM,MAAU,EAAO,QACjC,AAAI,CAAC,EAAM,QAAQ,CAAC,EAAM,WAAS,EAAS,IAAI,CAAI;EAEtD,KAAK,IAAM,CAAC,MAAS,EAAO,SAAS,CAAC,GACpC,EAAS,IAAI,CAAI;EAEnB,OAAO;CACT,GAAG,CAAC,CAAM,CAAC,GAEL,IAAe,QAAkB;EACrC,IAAI,GAAQ;GACV,IAAM,IAAgC,CAAC;GAKvC,AAJA,EAAe,SAAS,MAAS;IAC/B,IAAM,IAAQ,GAAoB,GAAQ,GAAQ,CAAI;IACtD,EAAK,KAAQ,GAAmB,GAAQ,CAAI,IAAK,KAAS,OAAS,KAAS;GAC9E,CAAC,GACD,EAAY,CAAI;EAClB;EACA,EAAW,EAAI;CACjB,GAAG;EAAC;EAAQ;EAAgB;CAAM,CAAC,GAE7B,IAAgB,EAAY,YAAY;EAG5C,AAFA,MAAM,EAAW,eAAe,GAChC,EAAW,EAAK,GAChB,EAAY,CAAC,CAAC;CAChB,GAAG,CAAC,CAAU,CAAC,GAET,IAAa,QAAkB;EACnC,IAAM,IAAmC,CAAC,GACpC,IAAe,GAAkB;GAAE;GAAQ;GAAU;GAAQ,OAAO;EAAM,CAAC,GAC3E,IAAmB,GAAwB,CAAY;EAC7D,KAAK,IAAM,CAAC,GAAK,MAAQ,OAAO,QAAQ,CAAQ,GAC1C,EAAO,OAAO,IAAI,CAAG,MACzB,EAAQ,KAAO,GAA0B,GAAQ,GAAK,CAAG;EAK3D,IAHI,OAAO,KAAK,CAAY,EAAE,SAAS,MACrC,EAAQ,SAAS,IAEf,OAAO,KAAK,CAAO,EAAE,WAAW,GAAG;GAErC,AADA,EAAW,EAAK,GAChB,EAAY,CAAC,CAAC;GACd;EACF;EACA,EAAe,OACb;GAAE,UAAU;GAAU,MAAM;EAAQ,GACpC;GACE,iBAAiB;IAIf,AAHA,EAAiB,SAAS,MAAW,EAAW,cAAc,CAAM,CAAC,GACrE,EAAW,EAAK,GAChB,EAAY,CAAC,CAAC,GACd,EAAM,QAAQ,OAAO;GACvB;GACA,eAAe,EAAM,MAAM,gBAAgB;EAC7C,CACF;CACF,GAAG;EAAC;EAAY;EAAgB;EAAU;EAAQ;EAAU;CAAM,CAAC,GAE7D,IAAoB,GAAa,GAAe,MAAmB;EACvE,GAAa,OAAU;GAAE,GAAG;IAAO,IAAQ;EAAM,EAAE;CACrD,GAAG,CAAC,CAAC;CAEL,IAAI,GAAW,OAAO,kBAAC,IAAD,CAAiB,CAAA;CACvC,IAAI,CAAC,GAAQ,OAAO;CAIpB,IAAM,IAAgB,GAAgB,GAAmC,CAAM,GACzE,IAAQ,EAAO,SAAS,eACxB,IAAW,IACb,KAAA,IACA,IACE,EAAO,KACP,EAAO,EAAO,OAAO,MAAM,EAAE,KAC7B,IACJ,MACC,KAAY,QAAQ,OAAO,KAAa,WAAW,OAAO,CAAQ,IAAI,EAAI,EAAO,QAAQ,IAAI,IAE1F,IAAa,GAAS,OAEtB,IAAqB;EACzB;EACA;EACA;EACA;EACA,QAAQ,IAAU;GAAE,GAAG;GAAQ,GAAG;EAAS,IAAI;EAC/C,OAAO;EACP,MAAM,IAAU,SAAS;EACzB,eAAe,IAAU,IAAoB,KAAA;EAC7C,MAAM;EACN,kBAAkB,KAAoB,KAAA;EACtC,eAAgB,KAEX,GAAM,MAAQ,EAAoB,OAAO;GAAE,cAAc;GAAM,OAAO;EAAI,CAAC,IAD5E,KAAA;EAEJ,mBAAmB,EAAoB;EACvC,gBAAgB,KAAY,KAAA;EAC5B,aAAc,KAET,GAAW,MAAe,EAAkB,OAAO;GAAE;GAAW;EAAW,CAAC,IAD7E,KAAA;EAEJ,iBAAiB,EAAkB;CACrC;CAEA,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,QAAQ;EAAO;YAAvE,CACE,kBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,SAAS;IACT,cAAc;IACd,UAAU;IACV,KAAK;IACL,YAAY;IACZ,QAAQ;GACV;aAXF,CAaE,kBAAC,MAAD;IACE,OAAO;KACL,QAAQ;KACR,UAAU;KACV,YAAY;KACZ,UAAU;KACV,cAAc;KACd,YAAY;KACZ,OAAO;IACT;cAEC;GACC,CAAA,GACJ,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,KAAK;IAAE;cACzD,MACE,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;KAAK,MAAK;KAAK,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;KAAG,SAAS;eAAe;IAE9D,CAAA,GACL,kBAAC,GAAD;KACE,MAAK;KACL,MAAK;KACL,MAAM,EAAe,YAAY,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA,IAAI,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAC/E,SAAS;KACT,UAAU,EAAe;eAExB,EAAe,YAAY,YAAY;IACrC,CAAA,CACL,EAAA,CAAA,IAEF,kBAAC,GAAD;KAAK,MAAK;KAAK,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAAG,SAAS;eAAc;IAEhE,CAAA;GAEN,CAAA,CACF;MAEL,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,WAAW;IAAQ,SAAS;IAAI,SAAS;IAAQ,eAAe;IAAU,KAAK;GAAG;aACtG,KAAS,EAAM,SAAS,IACvB,kBAAC,IAAD;IAAgB,OAAO;IAAY;GAAM,CAAA,IAEzC,kBAAC,KAAD;IAAG,OAAO;KAAE,UAAU;KAAM,OAAO;IAAgB;cAAnD;KAAsD;KAC1B,kBAAC,QAAD,EAAA,UAAM,SAAY,CAAA;KAAC;IAC5C;;EAEF,CAAA,CACF;;AAET;;;ACnsBA,IAAa,KAAb,cAAmC,GAAwB;CACzD,YAAY,GAAc;EAExB,AADA,MAAM,CAAK,GACX,KAAK,QAAQ;GAAE,UAAU;GAAO,OAAO;EAAK;CAC9C;CAEA,OAAO,yBAAyB,GAAqB;EACnD,OAAO;GAAE,UAAU;GAAM;EAAM;CACjC;CAEA,kBAAkB,GAAc,GAAiB;EAC/C,QAAQ,MAAM,mBAAmB,GAAO,EAAK,cAAc;CAC7D;CAEA,oBAAoB;EAClB,KAAK,SAAS;GAAE,UAAU;GAAO,OAAO;EAAK,CAAC;CAChD;CAEA,SAAS;EA6BP,OA5BI,KAAK,MAAM,WACT,KAAK,MAAM,WAAiB,KAAK,MAAM,WAGzC,kBAAC,OAAD;GACE,OAAO;IACL,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,SAAS;GACX;aAEA,kBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAU,UAAU;KAAK,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;cAApG;KACE,kBAAC,MAAD;MAAI,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,QAAQ;OAAG,OAAO;MAAc;gBAAG;KAE3E,CAAA;KACJ,kBAAC,KAAD;MAAG,OAAO;OAAE,UAAU;OAAM,OAAO;OAAiB,QAAQ;MAAE;gBAC3D,KAAK,MAAM,OAAO,WAAW;KAC7B,CAAA;KACH,kBAAC,OAAD,EAAA,UACE,kBAAC,GAAD;MAAK,SAAS,KAAK;gBAAa;KAAc,CAAA,EAC3C,CAAA;IACF;;EACF,CAAA,IAIF,KAAK,MAAM;CACpB;AACF;;;ACtBA,SAAgB,KAA0B;CACxC,IAAM,IAAc,GAAe;CAEnC,QAAgB;EACd,IAAI,IAAY,IACZ,IAAyD,MAGvD,IAAqB,KAEvB,IAAc,GACd,IAAsB,GAEtB,IAAmB;EAGvB,SAAS,EAAa,GAAuB;GACvC,MACA,MAAqB,QAAM,aAAa,CAAgB,GAC5D,IAAmB,iBAAiB;IAC9B,QAKJ;SAAI,SAAS,oBAAoB,WAAW;MAC1C,EAAa,CAAkB;MAC/B;KACF;KACA,EAAc;IADd;GAEF,GAAG,CAAO;EACZ;EAEA,eAAe,IAA0B;GACnC,QACJ;QAAI;KAGF,AAFA,MAAM,GAAW,GACjB,IAAsB,GACtB,IAAc;IAChB,SAAS,GAAK;KAWZ,AARI,MAAwB,KAC1B,QAAQ,KAAK,iDAAiD,CAAG,GAEnE,KAAuB,GAKvB,IAAc,KAAK,IAAI,MAAQ,KAAK,KAAK,IAAI,IAAsB,GAAG,CAAC,GAAG,GAAc;IAC1F;IACA,EAAa,CAAW;GADxB;EAEF;EAEA,SAAS,IAA0B;GACjC,IAAM,IAAM,KAAK,IAAI;GACjB,IAAM,IAAmB,QAC7B,IAAmB,GAInB,EAAc;EAChB;EAEA,SAAS,IAA+B;GACtC,AAAI,SAAS,oBAAoB,aAC/B,EAAkB;EAEtB;EAIA,IAAM,IAAc,IAAW,MAAwB;GACrD,IAAI,EAAM,SAAS,eAAe;IAGhC,EAAiB,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;IAC3D;GACF;GAEI,GAAiB,KAmBrB,EAAM,KAAK,8BAA8B,+CAA+C;IACtF,IAAI;IACJ,QAAQ;IACR,QAAQ;KACN,OAAO;KACP,eAAe;MACb,OAAO,SAAS,OAAO;KACzB;IACF;GACF,CAAC;EACH,CAAC;EAYD,OATA,OAAO,iBAAiB,SAAS,CAAiB,GAClD,OAAO,iBAAiB,UAAU,CAAiB,GACnD,SAAS,iBAAiB,oBAAoB,CAAsB,GAKpE,EAAc,SAED;GAMX,AALA,IAAY,IACZ,EAAY,GACZ,OAAO,oBAAoB,SAAS,CAAiB,GACrD,OAAO,oBAAoB,UAAU,CAAiB,GACtD,SAAS,oBAAoB,oBAAoB,CAAsB,GACnE,MAAqB,QAAM,aAAa,CAAgB;EAC9D;CACF,GAAG,CAAC,CAAW,CAAC;AAClB;;;AC/JA,IAAM,KAA4D;OAE1D,OAAO;OAEP,OAAO;OAEP,OAAO;OAEP,OAAO;AACf;AAKA,SAAS,KAAgB;CACvB,KAAK,IAAM,KAAQ,IACjB,EAAU,EAAE,YAAY,CAAC,CAAC;AAE9B;AAEA,IAAI,KAAY;AAMhB,SAAgB,KAAkC;CAChD,IAAI,IAAW;CACf,KAAY;CACZ,IAAM,IACJ,WACA;CACF,AAAI,OAAO,KAAS,aAClB,EAAK,EAAO,IAGZ,WAAW,IAAS,CAAC;AAEzB;;;AChDA,IAAM,qBAAW,IAAI,IAA2B;AAEhD,SAAgB,GAAe,GAAc,GAAgC;CAC3E,GAAS,IAAI,GAAM,CAAS;AAC9B;AAEA,SAAgB,GAAoB,GAAyC;CAC3E,OAAO,GAAS,IAAI,CAAI;AAC1B;;;ACwBA,GAAe,WAAW,EAAO;AAKjC,IAAM,KAAqB,6BAErB,KAAc,IAAI,GAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAS;CACpB,OAAO;AACT,EACF,EACF,CAAC;AAMD,SAAS,GAAmB,GAA4B;CACtD,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAI;EACF,IAAM,IAAM,IAAI,IAAI,GAAK,OAAO,SAAS,MAAM;EAG/C,OAFI,EAAI,aAAa,OAAO,SAAS,YACjC,EAAI,SAAS,OAAO,SAAS,OAAa,OACvC,EAAI;CACb,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,KAAyE;CAChF,IAAM,EAAE,MAAM,MAAW,GAAU;CAoEnC,OAAO;EAAE,QAlEM,QAA2B;GACxC,IAAI,CAAC,GAAQ,OAAO,CAAC;GAErB,IAAM,IAAmB,CAAC;GAI1B,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,EAAO,WAAW,CAAC,CAAC,GAAG;IACjE,IAAI,EAAO,SAAS,QAAQ;KAI1B,IAAM,IAAc,EAA+B;KAEnD,KADe,IAAa,EAAO,WAAW,KAAc,KAAA,IAChD,WACV;KAEF,EAAI,KACF,kBAAC,IAAD;MAEE,MAAM,EAAO;MACb,SAAS,kBAAC,IAAD,EAAA,UAAe,kBAAC,IAAD,EAAe,YAAY,EAAO,CAAA,EAAgB,CAAA;KAC3E,GAHM,CAGN,CACH;KACA;IACF;IACA,IAAI,EAAO,SAAS,UAAU;KAC5B,EAAI,KACF,kBAAC,IAAD;MAEE,MAAM,EAAO;MACb,SAAS,kBAAC,IAAD,EAAA,UAAe,kBAAC,IAAD,EAAiB,YAAY,EAAO,CAAA,EAAgB,CAAA;KAC7E,GAHM,CAGN,CACH;KACA;IACF;IACA,IAAI,EAAO,SAAS,eAAe;KACjC,EAAI,KACF,kBAAC,IAAD;MAEE,MAAM,EAAO;MACb,SAAS,kBAAC,IAAD,EAAA,UAAe,kBAAC,IAAD,EAAqB,YAAY,EAAO,CAAA,EAAgB,CAAA;KACjF,GAHM,CAGN,CACH;KACA;IACF;IACA,IAAI,EAAO,SAAS,UAAU;KAC5B,IAAM,IAAkB,EAAO,YAC3B,GAAoB,EAAO,SAAS,IACpC,KAAA;KACJ,AAAI,KACF,EAAI,KACF,kBAAC,IAAD;MAEE,MAAM,EAAO;MACb,SAAS,kBAAC,IAAD,EAAA,UAAe,kBAAC,GAAD,CAAkB,CAAA,EAAgB,CAAA;KAC3D,GAHM,CAGN,CACH;IAEJ;GACF;GACA,OAAO;EACT,GAAG,CAAC,CAAM,CAID;EAAQ,cAFI,QAAc,GAAmB,CAAM,GAAG,CAAC,CAAM,CAErD;CAAa;AAChC;AAEA,SAAS,KAAY;CACnB,IAAM,IAAa,GAAc,MAAM,EAAE,UAAU,GAC7C,IAAc,GAAc,MAAM,EAAE,SAAS,GAC7C,IAAkB,GAAc,MAAM,EAAE,eAAe,GACvD,EAAE,MAAM,GAAQ,WAAW,MAAkB,GAAU,GACvD,EAAE,MAAM,GAAc,WAAW,MAAwB,GAAgB,GACzE,EAAE,WAAQ,oBAAiB,GAAiB,GAC5C,IAAQ,GAAS,GAajB,IACJ,OAAO,SAAW,OAAe,OAAO,SAAS,OAAO,OAAO,CAAC,EAAM;CAkBxE,AATA,QAAgB;EACV,EAAM,WAAW,KACrB,EAAW;CACb,GAAG;EAAC,EAAM;EAAS;EAAoB;CAAU,CAAC,GAMlD,QAAgB;EACd,GAA0B;CAC5B,GAAG,CAAC,CAAC;CAOL,IAAM,IAAW,GAAc;CAM/B,AALA,QAAgB;EACd,IAAI,OAAO,WAAa,KAAa;EACrC,IAAM,IAAQ,GAAU,UAAU,KAAK;EACvC,AAAI,MAAO,SAAS,QAAQ;CAC9B,GAAG,CAAC,GAAU,QAAQ,CAAC,GACvB,QAAgB;EACd,IAAI,OAAO,WAAa,KAAa;EACrC,IAAM,IAAU,GAAU,UACpB,IACJ,GAAe,CAAO,IAAI,GAAmB,EAAS,KAAK,CAAC,IAAI,MAC5D,IAAW,SAAS,eAAe,EAAkB;EAC3D,IAAI,CAAC,GAAU;GAIb,AAAI,KAAU,EAAS,OAAO;GAC9B;EACF;EAGA,IAAI,CAAC,GAAU;GACb,SACG,iBAAiB,iDAA6C,EAC9D,SAAS,MAAO,EAAG,OAAO,CAAC;GAC9B,IAAM,IAAO,SAAS,cAAc,MAAM;GAI1C,AAHA,EAAK,KAAK,IACV,EAAK,MAAM,QACX,EAAK,OAAO,GACZ,SAAS,KAAK,YAAY,CAAI;GAC9B;EACF;EACA,AAAI,EAAS,SAAS,MAAU,EAAS,OAAO;CAClD,GAAG,CAAC,GAAU,QAAQ,CAAC;CAevB,IAAM,IAAS,GAAQ,UAAU,WAAW;CAC5C,QAAgB;EACd,IAAI,OAAO,WAAa,KAAa;EACrC,IAAM,IAAK,sBACL,IAAW,SAAS,eAAe,CAAE;EAC3C,IAAI,CAAC,GAAQ;GACX,AAAI,KAAU,EAAS,OAAO;GAC9B;EACF;EACA,IAAM,IAAW,GAAmB,CAAM;EAC1C,IAAI,CAAC,GAAU;GAEb,AADI,KAAU,EAAS,OAAO,GAC9B,QAAQ,MACN,0CAA0C,EAAO,qEAEnD;GACA;EACF;EACA,IAAI,KAAY,EAAS,SAAS,GAAU;EAC5C,AAAI,KAAU,EAAS,OAAO;EAC9B,IAAM,IAAO,SAAS,cAAc,MAAM;EAI1C,AAHA,EAAK,KAAK,GACV,EAAK,MAAM,cACX,EAAK,OAAO,GACZ,SAAS,KAAK,YAAY,CAAI;CAChC,GAAG,CAAC,CAAM,CAAC;CAsBX,IAAM,IAAmB,EAAM,WAAW,CAAC;CAyB3C,OApBI,KAOF,KACA,KACC,KAAuB,CAAC,KACxB,KAAiB,CAAC,IAGjB,kBAAC,OAAD;EACE,eAAA;EACA,OAAO;GAAE,WAAW;GAAS,YAAY;EAAY;CACtD,CAAA,IAKH,kBAAC,IAAD,EAAA,UAAA;EACE,kBAAC,IAAD;GAAO,MAAK;GAAS,SAAS,kBAAC,IAAD,CAAS,CAAA;EAAI,CAAA;EAC3C,kBAAC,IAAD;GAAO,MAAK;GAAsB,SAAS,kBAAC,IAAD,CAAkB,CAAA;EAAI,CAAA;EACjE,kBAAC,IAAD;GAAO,MAAK;GAAU,SAAS,kBAAC,IAAD,CAAS,CAAA;EAAI,CAAA;EAC5C,kBAAC,IAAD;GAAO,MAAK;GAAU,SAAS,kBAAC,IAAD,CAAS,CAAA;EAAI,CAAA;EAC5C,kBAAC,IAAD;GAAO,MAAK;GAAS,SAAS,kBAAC,IAAD,CAAQ,CAAA;EAAI,CAAA;EAC1C,kBAAC,IAAD;GAAO,MAAK;GAAO,SAAS,kBAAC,IAAD,CAAQ,CAAA;EAAI,CAAA;EACxC,kBAAC,IAAD;GAAO,SAAS,kBAAC,IAAD,CAAS,CAAA;aAAzB;IACG,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,IAAD;MAAU,IAAI;MAAc,SAAA;KAAS,CAAA;IAAI,CAAA,GAM/D,kBAAC,IAAD;KAAO,MAAK;KAAS,SAAS,kBAAC,IAAD;MAAU,IAAI;MAAc,SAAA;KAAS,CAAA;IAAI,CAAA,CACvE,EAAA,CAAA;IAEH;IACD,kBAAC,IAAD;KAAO,MAAK;KAAe,SAAS,kBAAC,IAAD,CAAc,CAAA;IAAI,CAAA;IACtD,kBAAC,IAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,IAAD,CAAmB,CAAA;IAAI,CAAA;IAC7D,kBAAC,IAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,IAAD,CAAe,CAAA;IAAI,CAAA;GACvC;;CACD,EAAA,CAAA;AAEZ;AAMA,SAAS,KAAW;CAIlB,GAAkB;CAGlB,IAAM,IAAgB,GAAoB,MAAM,EAAE,aAAa;CAC/D,OACE,kBAAC,IAAD,EAAA,UAAA;EAME,kBAAC,IAAD,CAAqB,CAAA;EACrB,kBAAC,IAAD,CAAY,CAAA;EAYZ,kBAAC,IAAD;GACE,UAAU;GACV,cAAc,EAAE,UAAU,GAAK;GAC/B,QAAA;GACA,eAAe;GACf,KAAK;GACL,QAAQ;EACT,CAAA;CACY,EAAA,CAAA;AAEnB;AAEA,SAAgB,KAAM;CACpB,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD;EAAqB,QAAQ;YAI3B,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD,CAAW,CAAA,EACE,CAAA;CACI,CAAA,EACR,CAAA;AAEnB;;;AC/WA,SAAS,GACP,GACA,GAC8B;CAC9B,OAAO,EAAI,WAAW,MAAM,MAAM,EAAE,eAAe,CAAS;AAC9D;AAGA,SAAS,GACP,GACA,GACgC;CAChC,OAAO,EAAI,aAAa,MAAM,MAAM,EAAE,kBAAkB,CAAY;AACtE;AAOA,SAAS,GACP,GAC8B;CAC9B,IAAM,IAAW,EAAE,GAAG,EAAI;CAG1B,OAFA,EAAS,WAAW,MAAsB,GAAW,GAAU,CAAS,GACxE,EAAS,aAAa,MAAyB,GAAa,GAAU,CAAY,GAC3E;AACT;AAaA,IAAa,KAAb,MAAsB;CACpB,QAAsB,CAAC;CAGvB,MAAM,GAA8B;EAC7B,OACL,KAAK,IAAM,KAAc,OAAO,KAAK,CAAI,GAAG;GAC1C,AAAK,KAAK,MAAM,OACd,KAAK,MAAM,KAAc,CAAC;GAE5B,IAAM,IAAW,EAAK;GACtB,KAAK,IAAM,KAAM,OAAO,KAAK,CAAQ,GAEnC,KAAK,MAAM,GAAY,KAAM;IAC3B,GAAG,KAAK,MAAM,GAAY;IAC1B,GAAG,EAAS;GACd;EAEJ;CACF;CAGA,MAAY;EACV,OAAO,KAAK;CACd;CAGA,IAAI,GAAoB,GAAiD;EACvE,OAAO,KAAK,MAAM,KAAc;CAClC;CAGA,QAAc;EACZ,KAAK,QAAQ,CAAC;CAChB;AACF;AAMA,SAAS,GAAe,GAAgB,GAA8B;CACpE,IAAM,IAAM,IAAI,IAAI,aAAa,KAAU,OAAO,SAAS,MAAM;CAkBjE,OAhBI,EAAO,UAAU,EAAO,OAAO,SAAS,KAC1C,EAAI,aAAa,IAAI,UAAU,EAAO,OAAO,KAAK,GAAG,CAAC,GAEpD,EAAO,UAAU,EAAO,OAAO,SAAS,KAC1C,EAAI,aAAa,IAAI,UAAU,EAAO,OAAO,KAAK,GAAG,CAAC,GAEpD,EAAO,QAAM,EAAI,aAAa,IAAI,QAAQ,EAAO,IAAI,GACrD,EAAO,UAAQ,EAAI,aAAa,IAAI,UAAU,EAAO,MAAM,GAC3D,EAAO,SAAO,EAAI,aAAa,IAAI,SAAS,OAAO,EAAO,KAAK,CAAC,GAChE,EAAO,SAAO,EAAI,aAAa,IAAI,SAAS,EAAO,KAAK,GACxD,EAAO,mBAAiB,EAAI,aAAa,IAAI,mBAAmB,MAAM,GAEtE,EAAO,WAAW,EAAO,QAAQ,SAAS,KAC5C,EAAI,aAAa,IAAI,WAAW,KAAK,UAAU,EAAO,OAAO,CAAC,GAGzD,EAAI,WAAW,EAAI;AAC5B;AAcA,IAAa,KAAb,MAAuF;CACrF;CACA;CAEA,YAAY,GAAgB,GAAqB;EAE/C,AADA,KAAK,SAAS,GACd,KAAK,OAAO,KAAY,IAAI,GAAS;CACvC;CAKA,MAAM,IAAI,GAAmD;EAC3D,IAAM,IAAS,IAAI,gBAAgB,EAAE,MAAG,CAAC;EAIzC,OAAO,IAAc,MAHH,GAChB,aAAa,KAAK,OAAO,GAAG,EAAO,SAAS,GAC9C,GACyB,IAAI;CAC/B;CAYA,MAAM,KACJ,IAAuB,CAAC,GAC0C;EAClE,IAAI;EACJ,IAAI;GACF,IAAM,MAAM,GACV,GAAe,KAAK,QAAQ,CAAM,CACpC;EACF,SAAS,GAAK;GACZ,IAAI,EAAO,SAAS,GAAmB,CAAG,GAAG;IAE3C,IAAM,EAAE,OAAO,GAAO,GAAG,MAAgB;IACzC,IAAM,MAAM,GACV,GAAe,KAAK,QAAQ,CAAW,CACzC;GACF,OACE,MAAM;EAEV;EAMA,OAHA,KAAK,KAAK,MAAM,EAAI,KAAK,GAGlB;GAAE,MADI,EAAI,KAAK,KAAK,MAAQ,GAAc,CAAG,CAC3C;GAAM,MAAM,EAAI;GAAM,OAAO,KAAK,KAAK,IAAI;EAAE;CACxD;CAKA,MAAM,OACJ,GACuC;EAEvC,QAAO,MADe,KAAK,WAAW,CAAC,CAAI,CAAC,GAC7B;CACjB;CAiBA,MAAM,WACJ,GAC2C;EAC3C,IAAI,EAAM,WAAW,GAAG,OAAO,CAAC;EAKhC,IAAM,IAAM,MAAM,GAA2B,gBAAgB;GAC3D,SALc,EAAM,KAAK,OAAY;IACrC,QAAQ,GAAG,KAAK,OAAO;IACvB,QAAQ;KAAE,QAAQ,KAAK;KAAQ;IAAO;GACxC,EAEE;GACA,QAAQ;EACV,CAAC;EAED,OADA,GAAqB,GAAK,GAAG,KAAK,OAAO,UAAU,GAC5C,EAAI,QAAQ,KAAK,MAAO,GAAc,GAAmB,CAAE,CAAC,CAAC;CACtE;CAKA,MAAM,OACJ,GACA,GACuC;EAEvC,QAAO,MADe,KAAK,WAAW,CAAC;GAAE;GAAI,GAAG;EAAK,CAAC,CAAC,GACxC;CACjB;CAeA,MAAM,WACJ,GAC2C;EAC3C,IAAI,EAAM,WAAW,GAAG,OAAO,CAAC;EAShC,IAAM,IAAM,MAAM,GAA2B,gBAAgB;GAC3D,SATc,EAAM,KAAK,EAAE,OAAI,GAAG,SAAc;IAChD,QAAQ,GAAG,KAAK,OAAO;IACvB,QAAQ;KACN,YAAY,CAAC,CAAE;KACf,QAAQ,KAAK;KACb;IACF;GACF,EAEE;GACA,QAAQ;EACV,CAAC;EAED,OADA,GAAqB,GAAK,GAAG,KAAK,OAAO,UAAU,GAC5C,EAAI,QAAQ,KAAK,MAAO,GAAc,GAAmB,CAAE,CAAC,CAAC;CACtE;CAKA,MAAM,OAAO,GAA2B;EACtC,MAAM,KAAK,WAAW,CAAC,CAAE,CAAC;CAC5B;CAaA,MAAM,WAAW,GAA2C;EAM1D,OALI,EAAI,WAAW,IAAU,EAAE,OAAO,EAAE,IAKjC,EAAE,QAAO,MAJE,EAAc,GAAG,KAAK,OAAO,YAAY;GACzD,YAAY;GACZ,QAAQ,KAAK;EACf,CAAC,GACmB,gBAAgB,EAAI,OAAO;CACjD;CAKA,MAAM,QAAQ,GAA2B;EACvC,MAAM,KAAK,YAAY,CAAC,CAAE,CAAC;CAC7B;CAYA,MAAM,YAAY,GAA2C;EAM3D,OALI,EAAI,WAAW,IAAU,EAAE,OAAO,EAAE,IAKjC,EAAE,QAAO,MAJE,EAAc,GAAG,KAAK,OAAO,aAAa;GAC1D,YAAY;GACZ,QAAQ,KAAK;EACf,CAAC,GACmB,gBAAgB,EAAI,OAAO;CACjD;CAKA,MAAM,OACJ,GACA,GACuC;EAEvC,QAAO,MADe,KAAK,WAAW,CAAC,CAAI,GAAG,CAAY,GAC3C;CACjB;CAiBA,MAAM,WACJ,GACA,GAC2C;EAC3C,IAAI,EAAM,WAAW,GAAG,OAAO,CAAC;EAShC,IAAM,IAAM,MAAM,GAA2B,gBAAgB;GAC3D,SATc,EAAM,KAAK,OAAY;IACrC,QAAQ,GAAG,KAAK,OAAO;IACvB,QAAQ;KACN,QAAQ,KAAK;KACb;KACA,WAAW;IACb;GACF,EAEE;GACA,QAAQ;EACV,CAAC;EAED,OADA,GAAqB,GAAK,GAAG,KAAK,OAAO,UAAU,GAC5C,EAAI,QAAQ,KAAK,MACtB,GAAc,GAAoB,CAAE,CAAC,CACvC;CACF;AACF;AASA,SAAS,GAAqB,GAAoB,GAAsB;CACtE,IAAI,EAAI,WAAW;CACnB,IAAM,IAAS,EAAI,QAAQ,MAAM,MAAO,CAAC,EAAG,EAAE,GACxC,IAAS,IACX,GAAG,EAAO,cAAc,QAAQ,IAAI,EAAO,SAAS,cACpD;CACJ,MAAU,MAAM,GAAG,EAAO,iBAAiB,GAAQ;AACrD;AAOA,SAAS,GACP,GACiB;CACjB,IAAM,IAAK,EAAG,UAAU,CAAC;CAEzB,QADY,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,KAAK,KAAK,KAAA,MACjC,CAAC;AAClB;AAOA,SAAS,GACP,GACiB;CACjB,IAAM,IAAK,EAAG,UAAU,CAAC;CAEzB,QADc,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,KAAK,KAAK,KAAA,IAClC,OAAO,CAAC;AAC1B;AAWA,SAAgB,GACd,GACA,GACiB;CACjB,OAAO,IAAI,GAAgB,GAAQ,CAAQ;AAC7C;;;ACtZA,IAAa,KAAb,MAAmB;CACjB,MAAyB,CAAC;CAE1B,KAAK,GAAgB,GAAwB;EAK3C,OAJA,KAAK,IAAI,KAAK;GACZ;GACA,QAAQ,GAAe,CAAM;EAC/B,CAAC,GACM;CACT;CAIA,OACE,GACA,GACA,IAAuC,UACvC,IAAqB,CAAC,GAChB;EACN,IAAM,IAAkC;GACtC;GACA;GACA;EACF;EAIA,OAHI,EAAS,SAAS,MACpB,EAAO,YAAY,IAEd,KAAK,KAAK,eAAe,CAAM;CACxC;CAEA,MAAM,UAAkC;EACtC,IAAI,KAAK,IAAI,WAAW,GACtB,MAAU,MACR,iFACF;EAEF,OAAO,MAAM,GAA2B,gBAAgB;GACtD,SAAS,KAAK;GACd,QAAQ;EACV,CAAC;CACH;CAGA,OAA2B;EACzB,OAAO,KAAK;CACd;AACF;AAEA,SAAS,GAAe,GAA4C;CAUlE,OATI,KAAK,OACA,OAEL,OAAO,KAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IACpC,IAKF,EAAE,OAAO,EAAE;AACpB;;;ACrFA,IAAM,KAAgB,KAQhB,KAAY;AAgBlB,SAAgB,GAAiB,GAA4B,IAAqB,CAAC,GAAG;CACpF,OAAO,GAAS;EACd,UAAU;GAAC;GAAmB;GAAQ;EAAM;EAC5C,SAAS,CAAC,CAAC;EACX,SAAS,OAAO,EAAE,gBAAoC;GAIpD,IAAM,IAAyB;IAAE,GAAG;IAAQ,UAAU;IAAe,OAAO;GAAO,GAC7E,IAAQ,MAAM,EAAa,GAAS;IAAE,GAAG;IAAY,MAAM;GAAE,GAAG,EAAE,UAAO,CAAC,GAE1E,IAAa,EAAM,KAAK,eAAe;GAC7C,IAAI,KAAc,GAAG,OAAO;GAK5B,IAAI,IAAa,IACf,MAAU,MACR,6BAA6B,EAAO,UAAU,EAAW,gBAC7C,GAAU,sFAExB;GAGF,IAAM,IAAO,MAAM,QAAQ,IACzB,MAAM,KAAK,EAAE,QAAQ,IAAa,EAAE,IAAI,GAAG,MACzC,EAAa,GAAS;IAAE,GAAG;IAAY,MAAM,IAAI;GAAE,GAAG,EAAE,UAAO,CAAC,CAClE,CACF,GAEM,IAAO,IAAI,GAAS;GAC1B,EAAK,MAAM,EAAM,KAAK;GACtB,IAAM,IAAO,CAAC,GAAG,EAAM,IAAI;GAC3B,KAAK,IAAM,KAAQ,GAEjB,AADA,EAAK,KAAK,GAAG,EAAK,IAAI,GACtB,EAAK,MAAM,EAAK,KAAK;GAKvB,OAAO;IACL;IACA,MAAM;KAAE,GAAG,EAAM;KAAM,MAAM;KAAG,UAAU,EAAK;KAAQ,aAAa;IAAE;IACtE,OAAO,EAAK,IAAI;GAClB;EACF;CACF,CAAC;AACH;;;AC1EA,SAAgB,GACd,GACA,GACA;CACA,IAAM,CAAC,GAAc,KAAmB,GAAgB,GAElD,IAAiB,QAAwB;EAC7C,IAAM,IAAM,EAAa,IAAI,SAAS;EAMtC,OALI,IACc,EAAI,MAAM,GAAG,EAAE,OAAO,OAE/B,EAAQ,QAAQ,MAAM,EAAiB,SAAS,CAAC,CAAC,IAEpD,EAAe,SAAS,IAAI,IAAiB;CACtD,GAAG;EAAC;EAAc;EAAkB;CAAc,CAAC,GAE7C,IAAa,GAChB,MAAmB;EAClB,IAAM,IAAO,IAAI,gBAAgB,CAAY;EAM7C,AALI,EAAK,WAAW,KAAK,GAAY,GAAM,CAAc,IACvD,EAAK,OAAO,SAAS,IAErB,EAAK,IAAI,WAAW,EAAK,KAAK,GAAG,CAAC,GAEpC,EAAgB,CAAI;CACtB,GACA;EAAC;EAAc;EAAiB;CAAc,CAChD;CA8BA,OAAO;EACL;EACA;EACA;EACA,iBAhCsB,QAAkB;GACxC,IAAM,IAAO,IAAI,gBAAgB,CAAY;GAE7C,AADA,EAAK,OAAO,SAAS,GACrB,EAAgB,CAAI;EACtB,GAAG,CAAC,GAAc,CAAe,CA4B/B;EACA,SA3Bc,QAAkB;GAChC,EAAW,CAAgB;EAC7B,GAAG,CAAC,GAAkB,CAAU,CAyB9B;EACA,iBAxBsB,GACrB,MAAgB,EAAe,SAAS,CAAG,GAC5C,CAAC,CAAc,CAsBf;EACA,cApBmB,GAClB,MAAgB;GACf,IAAI,EAAe,SAAS,CAAG,GAAG;IAEhC,IAAI,EAAe,UAAU,GAAG;IAChC,EAAW,EAAe,QAAQ,MAAM,MAAM,CAAG,CAAC;GACpD,OACE,EAAW,CAAC,GAAG,GAAgB,CAAG,CAAC;EAEvC,GACA,CAAC,GAAgB,CAAU,CAU3B;CACF;AACF;AAEA,SAAS,GAAY,GAAa,GAAsB;CAEtD,OADI,EAAE,WAAW,EAAE,SACZ,EAAE,OAAO,GAAG,MAAM,MAAM,EAAE,EAAE,IADD;AAEpC;;;ACnEA,SAAgB,GAAW,EAAE,UAAO,WAAQ,cAA4B;CAEtE,IAAM,IAAW,GAAiB,GAC5B,EAAE,cAAW,yBAAsB,GAAY;CAErD,SAAS,IAAa;EACpB,IAAI,CAAC,GAAQ;EACb,IAAM,IAAM,EAAkB,CAAM;EAEpC,AADI,KAAK,EAAU,EAAI,EAAE,GACzB,EAAS,CAAM;CACjB;CAEA,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,cAAc;GACd,KAAK;EACP;YAPF,CASE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;IAAI,UAAU;GAAE;aAA1E,CACG,KAAU,kBAAC,GAAD;IAAS,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;IAAG,SAAS;IAAY,OAAM;GAAQ,CAAA,GACtF,kBAAC,MAAD;IACE,OAAO;KACL,QAAQ;KACR,UAAU;KACV,YAAY;KACZ,eAAe;KACf,OAAO;KACP,UAAU;KACV,cAAc;KACd,YAAY;IACd;cAEC;GACC,CAAA,CACD;MACJ,KAAW,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAE;aAAI;EAAa,CAAA,CACtF;;AAET;;;AC7BA,SAAgB,GAAQ,EAAE,UAAO,YAAS,YAAS,aAAU,eAA0B;CACrF,IAAM,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,IAAU,EAAiC,IAAI,GAC/C,IAAU,EAAQ,QAAQ,MAAM,EAAE,OAAO,GACzC,IAAW,EAAQ,QAAQ,MAAM,CAAC,EAAE,OAAO;CAEjD,OACE,kBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,MAAM;GACN,QAAQ;GACR,WAAW;GACX,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,KAAK;GACL,SAAS;GACT,YAAY;GACZ,OAAO;GACP,cAAc;GACd,WAAW;GACX,WAAW;EACb;YAhBF;GAkBE,kBAAC,SAAD,EAAA,UAAQ,yIAAgJ,CAAA;GACxJ,kBAAC,QAAD;IAAM,OAAO;KAAE,UAAU;KAAM,YAAY;IAAI;cAA/C,CACE,kBAAC,QAAD;KACE,WAAU;KACV,OAAO;MACL,SAAS;MACT,cAAc;MACd,YAAY;MACZ,OAAO;MACP,aAAa;MACb,UAAU;KACZ;eAEC;IACG,CAAA,GAAC,UAEH;;GACN,kBAAC,UAAD;IACE,SAAS;IACT,OAAM;IACN,OAAO;KACL,YAAY;KACZ,QAAQ;KACR,OAAO;KACP,SAAS;KACT,QAAQ;KACR,SAAS;KACT,SAAS;KACT,cAAc;IAChB;IACA,eAAe,MAAM;KAEnB,AADA,EAAE,cAAc,MAAM,UAAU,KAChC,EAAE,cAAc,MAAM,aAAa;IACrC;IACA,eAAe,MAAM;KAEnB,AADA,EAAE,cAAc,MAAM,UAAU,OAChC,EAAE,cAAc,MAAM,aAAa;IACrC;cAEA,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GACb,CAAA;GACR,kBAAC,QAAD,EACE,OAAO;IACL,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,QAAQ;GACV,EACD,CAAA;GACA,EAAQ,KAAK,MACZ,kBAAC,IAAD;IAAoB,MAAM,EAAE;IAAM,OAAO,EAAE;IAAO,SAAS,EAAE;GAAQ,GAAvD,EAAE,EAAqD,CACtE;IACC,EAAS,SAAS,KAAK,KAAY,MACnC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD,EAAM,OAAO;IAAE,OAAO;IAAG,QAAQ;IAAI,YAAY;GAA0B,EAAI,CAAA,GAC/E,kBAAC,OAAD;IAAK,OAAO,EAAE,UAAU,WAAW;cAAnC,CACE,kBAAC,UAAD;KACE,KAAK;KACL,eAAe,GAAa,MAAS,CAAC,CAAI;KAC1C,OAAO;MACL,SAAS;MACT,YAAY;MACZ,KAAK;MACL,QAAQ;MACR,SAAS;MACT,cAAc;MACd,YAAY;MACZ,QAAQ;MACR,OAAO;MACP,UAAU;MACV,YAAY;MACZ,QAAQ;KACV;KACA,eAAe,MACZ,EAAE,cAAc,MAAM,aAAa;KAEtC,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;eApB3D,CAsBE,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA,GAAC,OACjB;QACR,kBAAC,IAAD;KACE,MAAM;KACN,QAAQ,EAAQ;KAChB,eAAe,EAAY,EAAK;KAChC,OAAO;KACP,OAAM;eALR;MAOG,EAAS,KAAK,MACb,kBAAC,GAAD;OAEE,MAAM,EAAE;OACR,QAAQ,EAAE;OACV,eAAe;QAEb,AADA,EAAY,EAAK,GACjB,EAAE,MAAM;OACV;iBAEC,EAAE;MACK,GATH,EAAE,EASC,CACX;OACC,KAAY,MAAa,EAAS,SAAS,KAAK,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;MACpE,KACC,kBAAC,GAAD;OAAU,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;OAAG,SAAS;iBAAU;MAEtD,CAAA;MAEX,KACC,kBAAC,GAAD;OAAU,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;OAAG,QAAA;OAAO,SAAS;iBAAU;MAE1D,CAAA;KAEJ;MACP;KACL,EAAA,CAAA;EAED;;AAET;AAEA,SAAS,GAAQ,EACf,SACA,UACA,cAKC;CACD,OACE,kBAAC,UAAD;EACW;EACT,OAAO;GACL,SAAS;GACT,YAAY;GACZ,KAAK;GACL,QAAQ;GACR,SAAS;GACT,cAAc;GACd,YAAY;GACZ,QAAQ;GACR,OAAO;GACP,UAAU;GACV,YAAY;GACZ,QAAQ;EACV;EACA,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;EACzD,eAAe,MAAO,EAAE,cAAc,MAAM,aAAa;YAjB3D,CAmBG,GACA,CACK;;AAEZ;;;AC3KA,SAAgB,GAAQ,EACtB,MACA,MACA,YACA,WACA,WACA,iBACA,gBACA,eACA,aACA,cACe;CACf,IAAM,KAAQ,YAA0B;EAEtC,AADA,EAAQ,GACR,KAAM,EAAG;CACX,GACM,IAAa,KAAU,KAAU,KAAgB,KAAe,GAChE,IAAa,KAAW,EAAQ,SAAS;CAC/C,OACE,kBAAC,OAAD;EACE,OAAO;GAAE,UAAU;GAAS,KAAK;GAAG,MAAM;GAAG,QAAQ;EAAI;EACzD,UAAU,MAAM,EAAE,gBAAgB;YAFpC,CAIE,kBAAC,OAAD;GACE,OAAO;IACL,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,SAAS;IACT,WAAW;IACX,OAAO;GACT;aARF;IAUG,KACC,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,KAAN,EAAU,MAAM,GAAK,CAAA;KAAG,KAAI;KAAQ,SAAS,EAAK,CAAM;eAAG;IAEjE,CAAA;IAEX,KACC,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;KAAG,KAAI;KAAI,SAAS,EAAK,CAAM;eAAG;IAElE,CAAA;IAEX,KACC,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAAG,KAAI;KAAI,SAAS,EAAK,CAAY;eAAG;IAEpE,CAAA;IAEX,KACC,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAAG,KAAI;KAAK,SAAS,EAAK,CAAW;eAAG;IAEpE,CAAA;IAEX,KACC,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;KAAG,SAAS,EAAK,CAAU;eAAG;IAE1D,CAAA;IAEX,KAAc,KAAc,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;IAC/C,GAAS,KAAK,MACb,kBAAC,GAAD;KAEE,MAAM,EAAE;KACR,QAAQ,EAAE;KACV,KAAK,EAAE;KACP,SAAS,EAAK,EAAE,KAAK;eAEpB,EAAE;IACK,GAPH,EAAE,EAOC,CACX;IACA,KACC,kBAAA,GAAA,EAAA,UAAA,EACI,KAAc,MAAe,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA,GAClD,kBAAC,GAAD;KAAU,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;KAAG,QAAA;KAAO,SAAS,EAAK,CAAQ;eAAG;IAEhE,CAAA,CACV,EAAA,CAAA;GAED;MACL,kBAAC,OAAD;GAAK,SAAS;GAAS,OAAO;IAAE,UAAU;IAAS,OAAO;IAAG,QAAQ;GAAG;EAAI,CAAA,CACzE;;AAET;;;ACrGA,IAAa,KAA0B,gBAC1B,KAA2B;AA4DxC,SAAS,GAAe,GAA0B;CAKhD,OAAO;AACT;AAEA,SAAgB,GAAsB,GAAwC;CAC5E,IAAM,EAAE,YAAS,WAAQ,sBAAmB,GACtC,IAAgB,EAAM,wBAAwB,KAC9C,IAAa,EAAM,cAAc,GAEjC,IAAyB,CAAC;CAChC,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAO,GAE7C,CADQ,EAAO,cAAc,CAAC,GACzB,SAAA,cAAgC,KACpC,EAAc,CAAI,KACvB,EAAQ,KAAK;EAAE;EAAM;CAAO,CAAC;CAG/B,IAAM,IAAS,IAAI,IAAI,EAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAIhD,IAAa,EAAQ,QAAQ,MAAM,CAAC,GAAe,EAAE,MAAM,CAAC,GAE9D,GACE,IAAW,GAAQ;CACzB,IAAI,KAAY,EAAS,SAAS,GAAG;EACnC,IAAM,IAAkB,IAAI,IAAI,EAAW,KAAK,MAAM,EAAE,IAAI,CAAC;EAC7D,IAAU,EACP,QAAQ,MAAM,EAAO,IAAI,CAAC,KAAK,EAAgB,IAAI,CAAC,CAAC,EACrD,MAAM,GAAG,CAAU,EACnB,KAAK,MAAM,EAAO,IAAI,CAAC,CAAE;CAC9B,OAIE,IAAU,EACP,QAAQ,OAAO,EAAE,OAAO,YAAY,MAAM,EAAE,EAC5C,MAAM,GAAG,CAAU;CAMxB,IAAM,IAAwB,CAAC,GACzB,IAAgC,CAAC;CACvC,KAAK,IAAM,KAAS,GAClB,AAAI,GAAe,EAAM,MAAM,IAAG,EAAe,KAAK,CAAK,IACtD,EAAO,KAAK,CAAK;CAGxB,OAAO;EAAE;EAAS;EAAQ;EAAgB;CAAe;AAC3D;AAaA,SAAgB,GAAsB,GAAwC;CAC5E,IAAM,EAAE,YAAS,cAAW,GACtB,IAAgB,EAAM,wBAAwB,KAC9C,IAAa,EAAM,cAAc,GAEjC,IAAyB,CAAC;CAChC,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAO,GAE7C,CADQ,EAAO,cAAc,CAAC,GACzB,SAAA,eAAiC,KACrC,EAAc,CAAI,KACvB,EAAQ,KAAK;EAAE;EAAM;CAAO,CAAC;CAG/B,IAAM,IAAS,IAAI,IAAI,EAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAChD,IAAW,GAAQ,sBACrB;CAeJ,OAdA,AAME,IANE,KAAY,EAAS,SAAS,IACjB,IAAI,IACjB,EAAS,QAAQ,MAAM,EAAO,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,CAAU,CAC3D,IAGe,IAAI,IACjB,EACG,QAAQ,OAAO,EAAE,OAAO,YAAY,MAAM,EAAE,EAC5C,MAAM,GAAG,CAAU,EACnB,KAAK,MAAM,EAAE,IAAI,CACtB,GAGK;EAAE;EAAS;CAAa;AACjC;;;ACrHA,SAAS,GAAQ,GAAW,IAAO,IAAe;CAChD,IAAM,IAAO,EAAE,SAAS;CACxB,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAI,EAAK;CAEf,OADI,IAAU,kBAAC,GAAD,EAAS,QAAO,CAAA,IACvB,kBAAC,GAAD;EAAmB;EAAY;CAAO,CAAA;AAC/C;AAGA,SAAgB,GAAS,EACvB,UACA,aACA,UACA,UACA,mBACA,mBACA,gBACA,WACA,YACA,kBACA,kBACA,aACA,iBACA,iBACA,oBACA,sBACgB;CAChB,IAAM,IAAgB,EAAiC,IAAI,GACrD,CAAC,GAAa,KAAkB,EAAS,EAAK,GAC9C,EAAE,WAAQ,GAAgB,GAE1B,IAAU,QACR,GAAsB;EAAE;EAAS;EAAQ;EAAgB;CAAc,CAAC,GAC9E;EAAC;EAAS;EAAQ;EAAgB;CAAa,CACjD,GAEM,IAAO,QACL,GAAsB;EAAE;EAAS;EAAQ;EAAgB;CAAc,CAAC,GAC9E;EAAC;EAAS;EAAQ;EAAgB;CAAa,CACjD,GAEM,KAAa,GAAoB,MAA+B;EACpE,IAAI,KAAqB,MAAmB,GAAG;EAC/C,IAAM,EAAE,SAAM,cAAW;EACzB,EAAI;GACF;GACA,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;GACpC,UAAU,MAAW,EAAc,GAAM,GAAQ,CAAM;EACzD,CAAC;CACH,GAEM,IAAc,QAEhB,EAAK,QAAQ,KAAK,EAAE,SAAM,iBAAc;EACtC,IAAI;EACJ,OAAO,EAAI,EAAO,SAAS,IAAI,KAAK;EACpC,MAAM,GAAQ,CAAM;EACpB,SAAS,EAAK,aAAa,IAAI,CAAI;EACnC,aAAa,EAAO;EACpB,aAAa,EAAU;GAAE;GAAM;EAAO,GAAG,EAAI;CAC/C,EAAE,GAGJ,CAAC,GAAM,CAAc,CACvB;CAEA,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,QAAQ;GACR,YAAY;EACd;YANF;GAQE,kBAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,SAAS;KAAkB,KAAK;IAAE;cAAvF;KACE,kBAAC,OAAD;MAAK,OAAO,EAAE,MAAM,EAAE;gBACpB,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAY,KAAK;OAAG;iBAA/D,CACE,kBAAC,MAAD;QACE,OAAO;SACL,QAAQ;SACR,UAAU;SACV,YAAY;SACZ,eAAe;SACf,OAAO;QACT;kBAEC;OACC,CAAA,GACJ,kBAAC,QAAD;QAAM,WAAU;QAAW,OAAO;SAAE,UAAU;SAAI,OAAO;QAAgB;kBAAzE;SACG;SAAM;SAAK;QACR;SACH;;KACF,CAAA;KACJ,EAAQ,QAAQ,KAAK,MACpB,kBAAC,GAAD;MAEE,MAAK;MACL,MAAM,GAAQ,EAAM,QAAQ,EAAE;MAC9B,eAAe,EAAU,GAAO,EAAK;gBAEpC,EAAI,EAAM,OAAO,SAAS,IAAI,KAAK,EAAM;KACvC,GANE,EAAM,IAMR,CACN;KACD,kBAAC,OAAD;MAAK,OAAO,EAAE,UAAU,WAAW;gBAAnC,CACE,kBAAC,GAAD;OACE,KAAK;OACL,MAAK;OACL,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAC5B,WAAW,kBAAC,EAAK,MAAN;QAAW,MAAM;QAAI,OAAO,EAAE,SAAS,GAAI;OAAI,CAAA;OAC1D,eAAe,GAAgB,MAAS,CAAC,CAAI;iBAC9C;MAEI,CAAA,GACL,kBAAC,IAAD;OACE,MAAM;OACN,QAAQ,EAAc;OACtB,eAAe,EAAe,EAAK;OACnC,OAAO;OACP,OAAM;iBALR;QAOG,EAAQ,OAAO,SAAS,KAAK,kBAAC,IAAD,EAAA,UAAc,SAAoB,CAAA;QAC/D,EAAQ,OAAO,KAAK,MACnB,kBAAC,GAAD;SAEE,MAAM,GAAQ,EAAM,MAAM;SAC1B,QAAQ,EAAM,OAAO;SACrB,eAAe;UAEb,AADA,EAAe,EAAK,GACpB,EAAU,GAAO,EAAK;SACxB;mBAEC,EAAI,EAAM,OAAO,SAAS,IAAI,KAAK,EAAM;QAClC,GATH,EAAM,IASH,CACX;QACA,EAAQ,eAAe,SAAS,KAC/B,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;SACnB,kBAAC,OAAD;UACE,OAAO;WACL,SAAS;WACT,UAAU;WACV,OAAO;WACP,YAAY;WACZ,eAAe;WACf,eAAe;WACf,SAAS;WACT,YAAY;WACZ,KAAK;UACP;oBAXF,CAaE,kBAAC,QAAD,EAAA,UAAM,kBAAqB,CAAA,GAC3B,kBAAC,QAAD;WACE,WAAU;WACV,OAAO;YACL,SAAS;YACT,cAAc;YACd,YAAY;YACZ,OAAO;YACP,UAAU;YACV,YAAY;WACd;qBAEC,KAAkB;UACf,CAAA,CACH;;SACJ,EAAQ,eAAe,KAAK,MAC3B,kBAAC,OAAD;UAEE,OAAO;WACL,SAAS,MAAmB,IAAI,KAAM;WACtC,eAAe,MAAmB,IAAI,SAAS;UACjD;oBAEA,kBAAC,GAAD;WACE,MAAM,GAAQ,EAAM,MAAM;WAC1B,QAAQ,EAAM,OAAO;WACrB,eAAe;YAEb,AADA,EAAe,EAAK,GACpB,EAAU,GAAO,EAAI;WACvB;qBAEC,EAAI,EAAM,OAAO,SAAS,IAAI,KAAK,EAAM;UAClC,CAAA;SACP,GAhBE,EAAM,IAgBR,CACN;QACD,EAAA,CAAA;SAEF,KAAmB,MACnB,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,GAAD,EAAU,SAAA,GAAS,CAAA;SAClB,KACC,kBAAC,GAAD;UACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;UAChC,eAAe;WAEb,AADA,EAAe,EAAK,GACpB,EAAgB;UAClB;oBACD;SAES,CAAA;SAEX,KACC,kBAAC,GAAD;UACE,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;UAChC,eAAe;WAEb,AADA,EAAe,EAAK,GACpB,EAAgB;UAClB;oBACD;SAES,CAAA;QAEZ,EAAA,CAAA;OAEI;QACP;;KACL,kBAAC,OAAD,EACE,OAAO;MAAE,OAAO;MAAG,QAAQ;MAAI,YAAY;MAAkB,QAAQ;KAAQ,EAC9E,CAAA;KACD,kBAAC,GAAD;MAAK,MAAK;MAAU,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;MAAG,SAAS;gBAA5D,CAAyE,QAClE,CACF;;IACF;;GAEJ;GAEA,IAAiB,KAChB,kBAAC,IAAD;IACE,OAAO;IACP,SAAS;IACT,SAAS;IACT,UAAU;IACV,UAAU;GACX,CAAA;EAEA;;AAET;AAEA,SAAS,GAAa,EAAE,eAAqC;CAC3D,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,UAAU;GACV,OAAO;GACP,YAAY;GACZ,eAAe;GACf,eAAe;EACjB;EAEC;CACE,CAAA;AAET;;;ACvRA,IAAM,KAAyB;CAC7B,SAAS;CACT,QAAQ;CACR,cAAc;CACd,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,SAAS;CACT,YAAY;CACZ,KAAK;AACP;AAEA,SAAgB,GAAe,EAC7B,UACA,WACA,UAAO,QACP,YACA,aAAU,UACV,UACA,kBACsB;CAGtB,IAAM,CAAC,GAAW,KAAgB,EAA6B,CAAK,GAC9D,CAAC,GAAO,KAAY,EAA6B,CAAK,GACtD,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAK,KAAU,EAA6B,CAAK,GAClD,IAAU,EAA8B,IAAI;CAElD,QAAgB;EAGd,AAFA,EAAa,CAAK,GAClB,EAAS,CAAK,GACd,EAAO,CAAK;CACd,GAAG,CAAC,CAAK,CAAC;CAEV,IAAM,UAAiB;EAErB,AADA,EAAO,CAAK,GACZ,EAAW,EAAI;CACjB,GAEM,IAAS,OAAO,MAAiB;EACrC,IAAM,IAAQ;EAGd,AAFA,EAAS,CAAI,GACb,EAAa,CAAI,GACjB,EAAW,EAAK;EAChB,IAAI;GACF,MAAM,EAAO,CAAI;EACnB,SAAS,GAAK;GAGZ,AAFA,EAAS,CAAK,GACd,EAAa,CAAK,GAClB,EAAM,MAAM,GAAG,IAAQ,IAAQ,MAAM,GAAG,cAAc,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG,CAAC;EACxG;CACF,GAEM,UAAe;EAEnB,AADA,EAAO,CAAK,GACZ,EAAW,EAAK;CAClB,GAEM,UAAiB;EACrB,IAAI,MAAQ,KAAA,GAAW,OAAO,EAAO;EACrC,EAAY,CAAG;CACjB;CAmDA,OAjDI,IAEA,kBAAC,OAAD;EACE,OAAO;GAAE,SAAS;GAAQ,YAAY;GAAU,KAAK;EAAE;EACvD,YAAY,MAAqC;GAE/C,AADI,EAAE,QAAQ,WAAS,EAAS,GAC5B,EAAE,QAAQ,YAAU,EAAO;EACjC;YALF,CAOG,MAAS,WACR,kBAAC,IAAD;GACE,OAAO;GACP,SAAS,KAAW,CAAC;GACrB,WAAW,MAAM;IAEf,AADA,EAAO,CAAC,GACR,EAAY,CAAC;GACf;EACD,CAAA,IACC,MAAS,SACX,kBAAC,GAAD;GACE,OAAO,MAAQ;GACf,WAAW,MAAM;IACf,IAAM,IAAO,OAAO,CAAC;IAErB,AADA,EAAO,CAAI,GACX,EAAY,CAAI;GAClB;EACD,CAAA,IAED,kBAAC,GAAD;GAAW,OAAO,KAAO;GAAI,UAAU;GAAQ,WAAA;GAAuB;EAAc,CAAA,GAErF,MAAS,YAAY,MAAS,UAC7B,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GAAS,MAAM;GAAI,MAAM,kBAAC,EAAK,OAAN,EAAY,MAAM,GAAK,CAAA;GAAG,SAAS;GAAU,OAAM;EAAQ,CAAA,GACpF,kBAAC,GAAD;GAAS,MAAM;GAAI,MAAM,kBAAC,EAAK,GAAN,EAAQ,MAAM,GAAK,CAAA;GAAG,SAAS;GAAQ,OAAM;EAAU,CAAA,CAChF,EAAA,CAAA,CAED;MAcP,kBAAC,OAAD;EACE,KAAK;EACL,UAAU,MAAY,UAAU,IAAI,KAAA;EAC3B,SAbG,MAAY,WAAW,IAAW,KAAA;EAc/B,eAbG,MAAY,aAAa,IAAW,KAAA;EAc3C,WAZb,MAAY,WACP,MAAqC;GACpC,AAAI,EAAE,QAAQ,WAAS,EAAS;EAClC,IACA,KAAA;EASF,eAAe,MAAM;GAAE,EAAE,cAAc,MAAM,cAAc;EAAiB;EAC5E,eAAe,MAAM;GAAE,EAAE,cAAc,MAAM,cAAc;EAAe;EAC1E,OAAO;YAEN,KAAS,kBAAC,QAAD;GAAM,OAAO,EAAE,OAAO,gBAAgB;aAAG;EAAmB,CAAA;CACnE,CAAA;AAET;;;ACtJA,IAAM,KAAa;CACjB;EAAE,IAAI;EAAM,OAAO;EAAsC,MAAM;CAAW;CAC1E;EAAE,IAAI;EAAM,OAAO;EAAgC,MAAM;CAAW;CACpE;EAAE,IAAI;EAAM,OAAO;EAA8C,MAAM;CAAW;AACpF,GAQM,KAAqF;CACzF;EAAE,OAAO;EAAc,KAAK;EAAO,OAAO;EAAiB,MAAM;CAAU;CAC3E;EAAE,OAAO;EAAiB,KAAK;EAAM,OAAO;EAAc,MAAM;CAAM;CACtE;EAAE,OAAO;EAAkB,KAAK;EAAU,OAAO;EAAU,MAAM;CAAU;CAC3E;EAAE,OAAO;EAAgB,KAAK;EAAM,OAAO;EAAY,MAAM;CAAQ;AACvE,GAEM,KAAmD;CACvD;EAAC;EAAS;EAAS;CAAO;CAC1B;EAAC;EAAa;EAAa;CAAS;CACpC;EAAC;EAAY;EAAY;CAAU;CACnC;EAAC;EAAQ;EAAQ;CAAM;CACvB;EAAC;EAAc;EAAc;CAAU;CACvC;EAAC;EAAY;EAAY;CAAQ;AACnC;AAEA,SAAgB,GAAS,EACvB,cACA,cAAW,8BACX,iBAAc,uBACE;CAChB,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAa,QAAQ;GAAQ,WAAW;EAAO;YACpE,kBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAM,QAAQ;GAAS;aAA/C;IACE,kBAAC,MAAD;KAAI,OAAO;MAAE,UAAU;MAAI,YAAY;MAAK,eAAe;MAAY,QAAQ;KAAE;eAAI;IAAa,CAAA;IAClG,kBAAC,KAAD;KAAG,OAAO;MAAE,OAAO;MAAiB,WAAW;MAAG,UAAU;KAAG;eAA/D;MAAkE;MACjC,kBAAC,UAAD;OAAQ,OAAO,EAAE,OAAO,gBAAgB;iBAAI;MAAoB,CAAA;MAAC;KAC/F;;IACH,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,qBAAqB;MAAiB,KAAK;MAAI,QAAQ;KAAS;eAC5F,GAAK,KAAK,MACT,kBAAC,OAAD;MAEE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,QAAQ;OACR,cAAc;MAChB;gBAPF;OASE,kBAAC,OAAD;QAAK,OAAO;SAAE,UAAU;SAAM,OAAO;QAAgB;kBAAI,EAAE;OAAW,CAAA;OACtE,kBAAC,OAAD;QACE,WAAU;QACV,OAAO;SAAE,UAAU;SAAI,YAAY;SAAK,eAAe;SAAW,QAAQ;QAAQ;kBAEjF,EAAE;OACA,CAAA;OACL,kBAAC,GAAD;QAAM,MAAM,EAAE;QAAM,MAAK;kBACtB,EAAE;OACC,CAAA;MACH;QAlBE,EAAE,KAkBJ,CACN;IACE,CAAA;IACL,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,qBAAqB;MAAW,KAAK;KAAG;eAAvE,CACE,kBAAC,IAAD;MAAa,OAAM;MAAe,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;gBAC5D,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,qBAAqB;QAAiB,KAAK;OAAE;iBACzE,GAAM,KAAK,CAAC,GAAI,GAAO,OAAc;QACpC,IAAM,IAAW,EAAK;QACtB,OACE,kBAAC,UAAD;SAEE,eAAe,IAAY,CAAE;SAC7B,OAAO;UACL,SAAS;UACT,YAAY;UACZ,KAAK;UACL,SAAS;UACT,YAAY;UACZ,QAAQ;UACR,cAAc;UACd,QAAQ;UACR,WAAW;SACb;mBAbF;UAeG,KAAY,GAAc,GAAU;WAAE,MAAM;WAAI,OAAO,EAAE,OAAO,gBAAgB;UAAE,CAAC;UACpF,kBAAC,QAAD;WAAM,OAAO;YAAE,UAAU;YAAM,YAAY;WAAI;qBAAI;UAAY,CAAA;UAC/D,kBAAC,QAAD,EAAM,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;UAC3B,kBAAC,EAAK,OAAN;WAAY,MAAM;WAAI,OAAO,EAAE,OAAO,gBAAgB;UAAI,CAAA;SACpD;WAlBD,CAkBC;OAEZ,CAAC;MACE,CAAA;KACM,CAAA,GACb,kBAAC,IAAD;MAAa,OAAM;MAAkB,MAAM,kBAAC,EAAK,UAAN,EAAe,MAAM,GAAK,CAAA;gBACnE,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,eAAe;QAAU,KAAK;OAAE;iBAC5D,GAAW,MAAM,GAAG,CAAC,EAAE,KAAK,MAC3B,kBAAC,OAAD;QAAgB,OAAO;SAAE,UAAU;SAAI,OAAO;QAAgB;kBAA9D,CACE,kBAAC,OAAD;SAAK,OAAO;UAAE,YAAY;UAAK,OAAO;SAAc;mBAAI,EAAE;QAAW,CAAA,GACrE,kBAAC,OAAD;SAAK,WAAU;SAAO,OAAO;UAAE,UAAU;UAAM,OAAO;SAAgB;mBACnE,EAAE;QACA,CAAA,CACF;UALK,EAAE,EAKP,CACN;MACE,CAAA;KACM,CAAA,CACV;;GACF;;CACF,CAAA;AAET;;;ACxGA,IAAM,KAA4C;CAChD,SAAS,EAAE,OAAO,QAAQ;CAC1B,eAAe,EAAE,OAAO,MAAM;CAC9B,eAAe,EAAE,OAAO,SAAS;CACjC,cAAc,EAAE,OAAO,QAAQ;CAC/B,SAAS,EAAE,OAAO,UAAU;CAC5B,UAAU,EAAE,OAAO,OAAO;AAC5B,GAEM,KAAiC;CAAE,IAAI;CAAQ,IAAI;CAAS,IAAI;CAAO,IAAI;AAAQ,GAEnF,KAAgB;CACpB;EAAE,IAAI;EAAM,SAAS;EAAmB,SAAS;EAAa,UAAU;EAAe,UAAU;EAAM,OAAO;CAAG;CACjH;EAAE,IAAI;EAAM,SAAS;EAAc,SAAS;EAAQ,UAAU;EAAS,UAAU;EAAM,OAAO;CAAG;CACjG;EAAE,IAAI;EAAM,SAAS;EAAe,SAAS;EAAU,UAAU;EAAe,UAAU;EAAM,OAAO;CAAG;CAC1G;EAAE,IAAI;EAAM,SAAS;EAAc,SAAS;EAAW,UAAU;EAAc,UAAU;EAAM,OAAO;CAAG;CACzG;EAAE,IAAI;EAAM,SAAS;EAAe,SAAS;EAAY,UAAU;EAAS,UAAU;EAAM,OAAO;CAAG;CACtG;EAAE,IAAI;EAAM,SAAS;EAAe,SAAS;EAAS,UAAU;EAAS,UAAU;EAAM,OAAO;CAAG;CACnG;EAAE,IAAI;EAAM,SAAS;EAAe,SAAS;EAAc,UAAU;EAAU,UAAU;EAAM,OAAO;CAAG;CACzG;EAAE,IAAI;EAAM,SAAS;EAAa,SAAS;EAAW,UAAU;EAAe,UAAU;EAAM,OAAO;CAAG;AAC3G;AAEA,SAAgB,KAAe;CAC7B,IAAM,IAAS,OAAO,QAAQ,EAAI,EAAE,KAAK,CAAC,GAAG,QAAQ;EACnD,KAAK;EACL,OAAO,EAAE;EACT,OAAO,GAAM,QAAQ,MAAM,EAAE,aAAa,CAAC;CAC7C,EAAE;CACF,OACE,kBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,QAAQ;GACR,SAAS;GACT,YAAY;EACd;YAPF,CASE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAY,KAAK;IAAI,cAAc;GAAG;aAAjF,CACE,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAG,UAAU;KAAI,YAAY;KAAK,eAAe;IAAW;cAAG;GAAY,CAAA,GAChG,kBAAC,QAAD;IAAM,WAAU;IAAW,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAAzE,CACG,GAAM,QAAO,yBACV;KACH;MACL,kBAAC,OAAD;GAAK,OAAO;IAAE,MAAM;IAAG,UAAU;IAAQ,SAAS;IAAQ,KAAK;IAAI,eAAe;GAAG;aAClF,EAAO,KAAK,MACX,kBAAC,OAAD;IAEE,OAAO;KACL,UAAU;KACV,OAAO;KACP,YAAY;KACZ,QAAQ;KACR,cAAc;KACd,SAAS;KACT,eAAe;IACjB;cAVF,CAYE,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,cAAc;MACd,SAAS;MACT,YAAY;MACZ,KAAK;KACP;eAPF;MASE,kBAAC,GAAD;OAAM,MAAM,EAAE;OAAO,MAAK;iBACvB,EAAE;MACC,CAAA;MACN,kBAAC,OAAD,EAAK,OAAO,EAAE,MAAM,EAAE,EAAI,CAAA;MAC1B,kBAAC,QAAD;OAAM,WAAU;OAAW,OAAO;QAAE,UAAU;QAAI,OAAO;OAAgB;iBACtE,EAAE,MAAM;MACL,CAAA;KACH;QACL,kBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,SAAS;MACT,eAAe;MACf,KAAK;MACL,MAAM;MACN,WAAW;KACb;eAEC,EAAE,MAAM,KAAK,MACZ,kBAAC,OAAD;MAEE,OAAO;OACL,SAAS;OACT,YAAY;OACZ,QAAQ;OACR,cAAc;OACd,UAAU;OACV,QAAQ;MACV;gBATF;OAWE,kBAAC,OAAD;QAAK,OAAO;SAAE,YAAY;SAAK,OAAO;SAAe,cAAc;QAAE;kBAAI,EAAE;OAAa,CAAA;OACxF,kBAAC,OAAD;QAAK,OAAO;SAAE,UAAU;SAAI,OAAO;QAAgB;kBAAI,EAAE;OAAa,CAAA;OACtE,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,KAAK;SAAG,WAAW;SAAG,UAAU;QAAO;kBAAtE,CACG,EAAE,YAAY,EAAE,aAAa,OAC5B,kBAAC,GAAD;SAAM,MAAM,GAAK,EAAE,aAAa;SAAS,MAAK;SAAK,KAAA;mBAChD,EAAE;QACC,CAAA,GAEP,EAAE,UAAU,KAAA,KACX,kBAAC,GAAD;SAAM,MAAK;SAAQ,MAAK;mBAAxB,CAA6B,UACpB,EAAE,KACL;UAEL;;MACF;QAxBE,EAAE,EAwBJ,CACN;IACE,CAAA,CACF;MAnEE,EAAE,GAmEJ,CACN;EACE,CAAA,CACF;;AAET;;;AClHA,IAAM,KAAc;CAClB;EAAE,IAAI;EAAY,MAAM;EAA0B,QAAQ;EAAW,SAAS;EAAY,UAAU;EAAI,QAAQ;CAAW;CAC3H;EAAE,IAAI;EAAY,MAAM;EAAyB,QAAQ;EAAW,SAAS;EAAY,UAAU;EAAK,QAAQ;EAAY,MAAM;CAAS;CAC3I;EAAE,IAAI;EAAY,MAAM;EAAoB,QAAQ;EAAU,SAAS;EAAK,UAAU;EAAG,QAAQ;CAAI;CACrG;EAAE,IAAI;EAAY,MAAM;EAAuB,QAAQ;EAAU,SAAS;EAAY,UAAU;EAAI,QAAQ;EAAY,MAAM;CAAM;CACpI;EAAE,IAAI;EAAY,MAAM;EAAuB,QAAQ;EAAW,SAAS;EAAY,UAAU;EAAK,QAAQ;EAAY,MAAM;CAAS;AAC3I,GAEM,KAAoC;CACxC,SAAS;CACT,SAAS;CACT,QAAQ;CACR,QAAQ;AACV,GAEM,KAAU;CAAC;CAAM;CAAQ;CAAU;CAAY;CAAW;CAAY;CAAU;AAAE;AAExF,SAAgB,KAAW;CACzB,OACE,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAa,QAAQ;GAAQ,WAAW;EAAO;YAAtE,CACE,kBAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,YAAY;IAAY,KAAK;IAAI,cAAc;GAAG;aAAjF,CACE,kBAAC,MAAD;IAAI,OAAO;KAAE,QAAQ;KAAG,UAAU;KAAI,YAAY;IAAI;cAAG;GAAQ,CAAA,GACjE,kBAAC,QAAD;IAAM,WAAU;IAAW,OAAO;KAAE,UAAU;KAAI,OAAO;IAAgB;cAAG;GAEtE,CAAA,CACH;MACL,kBAAC,OAAD;GACE,OAAO;IACL,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,UAAU;GACZ;aAEA,kBAAC,SAAD;IAAO,OAAO;KAAE,OAAO;KAAQ,UAAU;KAAM,gBAAgB;KAAY,eAAe;IAAE;cAA5F,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;KAAI,OAAO;MAAE,QAAQ;MAAI,YAAY;MAAoB,OAAO;KAAgB;eAC7E,GAAQ,KAAK,GAAG,MACf,kBAAC,MAAD;MAEE,OAAO;OACL,SAAS;OACT,WAAW;OACX,UAAU;OACV,YAAY;OACZ,eAAe;OACf,eAAe;OACf,cAAc;MAChB;gBAEC;KACC,GAZG,KAAK,KAAK,GAYb,CACL;IACC,CAAA,EACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,GAAK,KAAK,MACT,kBAAC,MAAD;KAAe,OAAO,EAAE,QAAQ,GAAG;eAAnC;MACE,kBAAC,MAAD;OACE,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;OACrE,WAAU;iBAEV,kBAAC,KAAD;QAAG,MAAK;QAAI,OAAO;SAAE,OAAO;SAAiB,gBAAgB;QAAO;kBACjE,EAAE;OACF,CAAA;MACD,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;OACrE,WAAU;iBAET,EAAE;MACD,CAAA;MACJ,kBAAC,MAAD;OAAI,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;iBACvE,kBAAC,GAAD;QAAM,MAAM,GAAK,EAAE;QAAS,MAAK;QAAK,KAAA;kBACnC,EAAE;OACC,CAAA;MACJ,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QACL,SAAS;QACT,cAAc;QACd,OAAO;OACT;iBAEA,kBAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,YAAY;SAAU,KAAK;QAAE;kBAA5D,CACE,kBAAC,QAAD;SACE,OAAO;UACL,MAAM;UACN,QAAQ;UACR,YAAY;UACZ,cAAc;UACd,UAAU;SACZ;mBAEA,kBAAC,QAAD,EACE,OAAO;UACL,SAAS;UACT,QAAQ;UACR,OAAO,GAAG,EAAE,SAAS;UACrB,YAAY,cAAc,GAAK,EAAE,QAAQ;SAC3C,EACD,CAAA;QACG,CAAA,GACN,kBAAC,QAAD;SAAM,WAAU;SAAW,OAAO;UAAE,UAAU;UAAI,OAAO;SAAgB;mBAAzE,CACG,EAAE,UAAS,GACR;UACH;;MACH,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;OACrE,WAAU;iBAET,EAAE;MACD,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;OACrE,WAAU;iBAET,EAAE,QAAQ;MACT,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QAAE,SAAS;QAAU,cAAc;OAA2B;OACrE,WAAU;iBAET,EAAE;MACD,CAAA;MACJ,kBAAC,MAAD;OACE,OAAO;QACL,SAAS;QACT,cAAc;QACd,WAAW;OACb;iBAEA,kBAAC,GAAD;QAAS,MAAM;QAAI,MAAM,kBAAC,EAAK,MAAN,EAAW,MAAM,GAAK,CAAA;OAAI,CAAA;MACjD,CAAA;KACF;OA9EK,EAAE,EA8EP,CACL,EACI,CAAA,CACF;;EACJ,CAAA,CACF;;AAET;;;ACvIA,SAAgB,GAAwB,EAAE,SAAM,iBAAc,oBAAiB,YAAS,gBAAoB;CAC1G,IAAM,EAAE,UAAO,kBAAe,GAAiB,GAMzC,EAAE,OAAO,MAAW,GAAiB,2CAA2C,EAAE,GAClF,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAU,KAAe,EAAiB,EAAE;CAEnD,IAAI,CAAC,GAAM,OAAO;CAElB,IAAM,IAAgB,EAAO,KAAK,GAC5B,IAAW,EAAc,UAAU;CAqBzC,OACE,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,mBAAgB;EAChB,eAAY;EACZ,OAAO;GACL,UAAU;GACV,OAAO;GACP,YAAY;GACZ,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,QAAQ;EACV;EACA,SAAS;YAET,kBAAC,OAAD;GACE,UAAU,MAAM,EAAE,gBAAgB;GAClC,OAAO;IACL,YAAY;IACZ,OAAO;IACP,UAAU;IACV,UAAU;IACV,SAAS;IACT,cAAc;IACd,WAAW;GACb;aAVF;IAYE,kBAAC,MAAD;KAAI,IAAG;KAA4B,OAAO;MAAE,WAAW;MAAG,UAAU;KAAG;eAAvE,CAA0E,gBAC3D,KAAmB,CAC9B;;IACJ,kBAAC,KAAD;KAAG,OAAO;MAAE,UAAU;MAAI,OAAO;KAAgB;eAAjD;MAAoD;MACvB,KAAmB;MAAY;KAGzD;;IACH,kBAAC,SAAD;KAAO,SAAQ;KAAa,OAAO;MAAE,SAAS;MAAS,UAAU;MAAI,WAAW;KAAG;eAAnF;MAAsF;MACvE;MAAO;KACf;;IACP,kBAAC,YAAD;KACE,IAAG;KACH,eAAY;KACZ,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;KACzC,MAAM;KACN,OAAO;MACL,OAAO;MACP,WAAW;MACX,SAAS;MACT,YAAY;MACZ,OAAO;MACP,QAAQ;MACR,cAAc;MACd,UAAU;MACV,YAAY;MACZ,WAAW;KACb;IACD,CAAA;IACD,kBAAC,SAAD;KAAO,SAAQ;KAAe,OAAO;MAAE,SAAS;MAAS,UAAU;MAAI,WAAW;KAAG;eAAG;IAEjF,CAAA;IACP,kBAAC,SAAD;KACE,IAAG;KACH,eAAY;KACZ,MAAK;KACL,KAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;KAC3C,OAAO;MACL,WAAW;MACX,SAAS;MACT,OAAO;MACP,YAAY;MACZ,OAAO;MACP,QAAQ;MACR,cAAc;MACd,UAAU;KACZ;IACD,CAAA;IACD,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAG,gBAAgB;MAAY,WAAW;KAAG;eAAjF,CACE,kBAAC,UAAD;MACE,SAAS;MACT,eAAY;MACZ,OAAO;OACL,SAAS;OACT,YAAY;OACZ,OAAO;OACP,QAAQ;OACR,cAAc;OACd,QAAQ;OACR,UAAU;MACZ;gBACD;KAEO,CAAA,GACR,kBAAC,UAAD;MACE,eApHW;OACd,KACL,EACE;QACE;QACA,QAAQ;QACR,iBAAiB,IAAW,OAAO,CAAQ,IAAI,KAAA;OACjD,GACA,EACE,iBAAiB;QAIf,AAHA,EAAU,EAAE,GACZ,EAAY,EAAE,GACd,IAAY,GACZ,EAAQ;OACV,EACF,CACF;MACF;MAoGU,UAAU,CAAC,KAAY;MACvB,eAAY;MACZ,OAAO;OACL,SAAS;OACT,YAAY;OACZ,OAAO;OACP,QAAQ;OACR,cAAc;OACd,QAAQ,CAAC,KAAY,IAAa,gBAAgB;OAClD,UAAU;OACV,SAAS,CAAC,KAAY,IAAa,KAAM;MAC3C;gBAEC,IAAa,cAAc;KACtB,CAAA,CACL;;GACF;;CACF,CAAA;AAET;;;AC7JA,SAAgB,GAAiB,EAAE,SAAM,eAAmC;CAiD1E,OAhDA,QAAgB;EACd,KAAK,IAAM,KAAQ,EAAK,OACtB,QAAQ,EAAK,MAAb;GACE,KAAK;IACH,IAAI,KAAY,EAAK,UAAU,OAAO,EAAK,UAAW,UAAU;KAC9D,IAAM,IAAI,EAAK,QACT,IAAO,EAAE,SACX,SAAS,EAAE,SAAS,EAAE,SAAS,SAAS,KAAK,EAAE,KAAK,IAAI,EAAE,OAAO,OACjE;KACJ,AAAI,KAAM,EAAS,CAAI;IACzB;IACA;GACF,KAAK;IACH,IAAI,EAAK,UAAU,OAAO,EAAK,UAAW,UAAU;KAElD,IAAM,IADI,EAAK,OACD,OAAO,CAAC;KACtB,KAAK,IAAM,KAAM,GAAK;MACpB,IAAM,IAAK,SAAS,cAClB,iBAAiB,EAAG,GACtB;MACA,IAAI,CAAC,GAAI;MACT,EAAG,UAAU,IAAI,iBAAiB;MAClC,IAAM,IAAM,EAAK,SAAS;MAC1B,OAAO,iBAAiB,EAAG,UAAU,OAAO,iBAAiB,GAAG,CAAG;KACrE;IACF;IACA;GACF,KAAK;IACH,IAAI,EAAK,UAAU,OAAO,EAAK,UAAW,UAAU;KAClD,IAAM,IAAI,EAAK;KACf,IAAI,EAAE,OAAO;MACX,IAAM,IAAK,SAAS,cAClB,gBAAgB,EAAE,MAAM,GAC1B;MACA,IAAI,GAAI;OACN,EAAG,UAAU,IAAI,iBAAiB;OAClC,IAAM,IAAM,EAAK,SAAS;OAC1B,OAAO,iBAAiB,EAAG,UAAU,OAAO,iBAAiB,GAAG,CAAG;MACrE;KACF;IACF;IACA;EAGJ;CAEJ,GAAG,CAAC,EAAK,OAAO,CAAQ,CAAC,GAElB;AACT;;;ACrDA,SAAgB,GAAwB,EACtC,SACA,YACA,kBACA,aACA,aACA,UACA,gBAC+B;CAM/B,OALA,QAAgB;EACd,AAAI,KAAM,EAAe;CAC3B,GAAG,CAAC,GAAM,CAAS,CAAC,GAEf,IAEH,kBAAC,SAAD;EAAO,WAAU;YAAjB;GACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAgB;IAAmB,CAAA,GACnD,kBAAC,GAAD;KAAK,MAAK;KAAQ,SAAS;eAA3B,CACE,kBAAC,EAAK,MAAN,CAAY,CAAA,GAAC,MACV;MACC;;GACR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAc,WAAW,KACxB,kBAAC,OAAD;KAAK,WAAU;eAAoC;IAA0B,CAAA,GAE9E,EAAc,KAAK,MAClB,kBAAC,UAAD;KAEE,eAAe,EAAS,EAAE,EAAE;KAC5B,WAAW,6EACT,EAAE,OAAO,IAAW,eAAe;eAJvC,CAOE,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAE,SAAS,EAAE,UAAU,GAAc,EAAE,OAAO,IAAI;KAChD,CAAA,GACL,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,EAAE;OAAW;OAAI,EAAE;OACnB,EAAE,aAAa,aAAa,QAAQ,EAAE,YAAY,aAAa,KAAK,QAAQ,CAAC,MAAM;MACjF;OACC;OAbD,EAAE,EAaD,CACT,CACE;;GACL,kBAAC,UAAD;IAAQ,WAAU;cAChB,kBAAC,GAAD;KAAK,MAAK;KAAQ,SAAS;eAAS;IAE/B,CAAA;GACC,CAAA;EACH;MApCS;AAsCpB;AAEA,SAAS,GAAc,GAAuC;CAC5D,IAAI,CAAC,GAAK,OAAO;CACjB,IAAM,IAAI,EAAI,SAAS,OAAO,EAAI,MAAM,IAAI,IACtC,IAAK,EAAI,KAAK,IAAI,OAAO,EAAI,EAAE,EAAE,MAAM,GAAG,CAAC,MAAM;CACvD,OAAO,IAAI,GAAG,IAAI,MAAO;AAC3B"}
|