@fluid-app/portal-sdk 0.1.83 → 0.1.85
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/{AppNavigationContext-Du3Qq0yc.mjs → AppNavigationContext-BSnbFILQ.mjs} +1 -1
- package/dist/{AppNavigationContext-Du3Qq0yc.mjs.map → AppNavigationContext-BSnbFILQ.mjs.map} +1 -1
- package/dist/{AppNavigationContext-Agp0UkCQ.cjs → AppNavigationContext-DnwdUAjn.cjs} +1 -1
- package/dist/{AppNavigationContext-Agp0UkCQ.cjs.map → AppNavigationContext-DnwdUAjn.cjs.map} +1 -1
- package/dist/{ContactsScreen-FJUtn2_G.mjs → ContactsScreen-DrkHWPMm.mjs} +4 -4
- package/dist/{ContactsScreen-FJUtn2_G.mjs.map → ContactsScreen-DrkHWPMm.mjs.map} +1 -1
- package/dist/{ContactsScreen-aPH9Xaim.cjs → ContactsScreen-Du9M90zi.cjs} +5 -5
- package/dist/{ContactsScreen-6gXKTN61.cjs → ContactsScreen-DvmqkXOu.cjs} +4 -4
- package/dist/{ContactsScreen-6gXKTN61.cjs.map → ContactsScreen-DvmqkXOu.cjs.map} +1 -1
- package/dist/{CustomersScreen-9f10-_AZ.cjs → CustomersScreen-Bd9eK18y.cjs} +1 -1
- package/dist/{CustomersScreen-9f10-_AZ.cjs.map → CustomersScreen-Bd9eK18y.cjs.map} +1 -1
- package/dist/{CustomersScreen-xAauAB-B.mjs → CustomersScreen-DEFY3mRL.mjs} +1 -1
- package/dist/{CustomersScreen-xAauAB-B.mjs.map → CustomersScreen-DEFY3mRL.mjs.map} +1 -1
- package/dist/{MessagingScreen-CWJA4usG.cjs → MessagingScreen-BlN6KjV-.cjs} +3 -3
- package/dist/{MessagingScreen-DWNe8VFM.mjs → MessagingScreen-C4-8_bvB.mjs} +3 -3
- package/dist/{MessagingScreen-7_YhiLVY.mjs → MessagingScreen-Cg2XvQZF.mjs} +3 -3
- package/dist/{MessagingScreen-7_YhiLVY.mjs.map → MessagingScreen-Cg2XvQZF.mjs.map} +1 -1
- package/dist/{MessagingScreen-B_5Ci5s-.cjs → MessagingScreen-Lc81OXIN.cjs} +3 -3
- package/dist/{MessagingScreen-B_5Ci5s-.cjs.map → MessagingScreen-Lc81OXIN.cjs.map} +1 -1
- package/dist/{MySiteScreen-CtVL7wfc.cjs → MySiteScreen-B-I4V3Cc.cjs} +2 -2
- package/dist/{MySiteScreen-CtVL7wfc.cjs.map → MySiteScreen-B-I4V3Cc.cjs.map} +1 -1
- package/dist/{MySiteScreen-Cspeq0cy.mjs → MySiteScreen-BedF13nt.mjs} +2 -2
- package/dist/{MySiteScreen-Cspeq0cy.mjs.map → MySiteScreen-BedF13nt.mjs.map} +1 -1
- package/dist/{MySiteScreen-D5tr7TdT.cjs → MySiteScreen-DEOKI32H.cjs} +1 -1
- package/dist/{OrdersScreen-DiKPQvwJ.cjs → OrdersScreen-BE_E1QGL.cjs} +2 -2
- package/dist/{OrdersScreen-Bu82RCok.cjs → OrdersScreen-C-rU9cx6.cjs} +4 -4
- package/dist/{OrdersScreen-Bu82RCok.cjs.map → OrdersScreen-C-rU9cx6.cjs.map} +1 -1
- package/dist/{OrdersScreen-DrCPl7uv.mjs → OrdersScreen-CfAmUUN3.mjs} +4 -4
- package/dist/{OrdersScreen-DrCPl7uv.mjs.map → OrdersScreen-CfAmUUN3.mjs.map} +1 -1
- package/dist/{ProductsScreen-BHHo6Wen.mjs → ProductsScreen-CGo9KQ9L.mjs} +3 -3
- package/dist/{ProductsScreen-BHHo6Wen.mjs.map → ProductsScreen-CGo9KQ9L.mjs.map} +1 -1
- package/dist/{ProductsScreen-CoyGeILD.mjs → ProductsScreen-CgG79XW9.mjs} +5 -5
- package/dist/{ProductsScreen-Doc9tmAQ.cjs → ProductsScreen-Csw96ete.cjs} +3 -3
- package/dist/{ProductsScreen-Doc9tmAQ.cjs.map → ProductsScreen-Csw96ete.cjs.map} +1 -1
- package/dist/{ProductsScreen-CCNyLHqR.cjs → ProductsScreen-DUPhUJ7p.cjs} +5 -5
- package/dist/{ShareablesScreen-CNMWSoKY.mjs → ShareablesScreen-BK0H5fb2.mjs} +30 -9
- package/dist/ShareablesScreen-BK0H5fb2.mjs.map +1 -0
- package/dist/{ShareablesScreen-BRxLz3zn.mjs → ShareablesScreen-BmKYYRZq.mjs} +5 -5
- package/dist/{ShareablesScreen-Dp3BcjuA.cjs → ShareablesScreen-D0kIiW8S.cjs} +34 -7
- package/dist/ShareablesScreen-D0kIiW8S.cjs.map +1 -0
- package/dist/{ShareablesScreen-C5GJg1EP.cjs → ShareablesScreen-loETsu9v.cjs} +5 -5
- package/dist/{ShopScreen-nJhut901.cjs → ShopScreen-BK6QNd3h.cjs} +1 -1
- package/dist/{ShopScreen-CPWZhKgE.cjs → ShopScreen-D2S24-LK.cjs} +92 -73
- package/dist/ShopScreen-D2S24-LK.cjs.map +1 -0
- package/dist/{ShopScreen-CVxmqfmN.mjs → ShopScreen-DdPigYbj.mjs} +93 -74
- package/dist/{ShopScreen-CVxmqfmN.mjs.map → ShopScreen-DdPigYbj.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-CSKVsakI.cjs → SubscriptionsScreen-CfvaEuTS.cjs} +4 -4
- package/dist/{SubscriptionsScreen-CSKVsakI.cjs.map → SubscriptionsScreen-CfvaEuTS.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-CJdcyH-e.mjs → SubscriptionsScreen-D3LAx25s.mjs} +4 -4
- package/dist/{SubscriptionsScreen-CJdcyH-e.mjs.map → SubscriptionsScreen-D3LAx25s.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-C1UWxvQk.cjs → SubscriptionsScreen-Dpn0YuDb.cjs} +3 -3
- package/dist/{dist-B4Ke7bHH.cjs → dist-BK4mvUPm.cjs} +2 -2
- package/dist/{dist-B4Ke7bHH.cjs.map → dist-BK4mvUPm.cjs.map} +1 -1
- package/dist/{dist-Cl4FsM3V.mjs → dist-C9vpl_rR.mjs} +2 -2
- package/dist/{dist-Cl4FsM3V.mjs.map → dist-C9vpl_rR.mjs.map} +1 -1
- package/dist/{dist-CMGXkSgZ.mjs → dist-CkIGP8my.mjs} +1 -1
- package/dist/{dist-CMGXkSgZ.mjs.map → dist-CkIGP8my.mjs.map} +1 -1
- package/dist/{dist-BbS_7TvS.cjs → dist-thaj08s5.cjs} +1 -1
- package/dist/{dist-BbS_7TvS.cjs.map → dist-thaj08s5.cjs.map} +1 -1
- package/dist/{es-1KItbbYg.mjs → es-Dlib2eNY.mjs} +1 -1
- package/dist/{es-1KItbbYg.mjs.map → es-Dlib2eNY.mjs.map} +1 -1
- package/dist/{es-BSkb3AZk.cjs → es-xQF-WIMq.cjs} +1 -1
- package/dist/{es-BSkb3AZk.cjs.map → es-xQF-WIMq.cjs.map} +1 -1
- package/dist/index.cjs +140 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +22 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +137 -44
- package/dist/index.mjs.map +1 -1
- package/dist/{order-detail-Cj68hbgK.cjs → order-detail-06VbWJjw.cjs} +1 -1
- package/dist/{order-detail-Cj68hbgK.cjs.map → order-detail-06VbWJjw.cjs.map} +1 -1
- package/dist/{order-detail-B-4hsupW.mjs → order-detail-B5X_QRpi.mjs} +1 -1
- package/dist/{order-detail-B-4hsupW.mjs.map → order-detail-B5X_QRpi.mjs.map} +1 -1
- package/dist/{products-BKZ1siXg.cjs → products-CjZof7c2.cjs} +1 -1
- package/dist/{products-BKZ1siXg.cjs.map → products-CjZof7c2.cjs.map} +1 -1
- package/dist/{products-DOO1TBcm.mjs → products-Dkwd_Bh0.mjs} +1 -1
- package/dist/{products-DOO1TBcm.mjs.map → products-Dkwd_Bh0.mjs.map} +1 -1
- package/dist/{src-nhqydD53.mjs → src-BjmyVJoe.mjs} +21 -20
- package/dist/src-BjmyVJoe.mjs.map +1 -0
- package/dist/{src-Dzexc0vw.cjs → src-Dt66UB0B.cjs} +1 -1
- package/dist/{src-Dzexc0vw.cjs.map → src-Dt66UB0B.cjs.map} +1 -1
- package/dist/{src-fXyI4AWk.cjs → src-bzRRiTN4.cjs} +21 -20
- package/dist/src-bzRRiTN4.cjs.map +1 -0
- package/dist/{src-DfVbSKm5.mjs → src-p8DA1dH0.mjs} +1 -1
- package/dist/{src-DfVbSKm5.mjs.map → src-p8DA1dH0.mjs.map} +1 -1
- package/dist/{use-customer-account-Dysq4hpT.cjs → use-customer-account-BoobPvSQ.cjs} +1 -1
- package/dist/{use-customer-account-Dysq4hpT.cjs.map → use-customer-account-BoobPvSQ.cjs.map} +1 -1
- package/dist/{use-customer-account-B0DeL6m0.mjs → use-customer-account-Dt3kO7ti.mjs} +1 -1
- package/dist/{use-customer-account-B0DeL6m0.mjs.map → use-customer-account-Dt3kO7ti.mjs.map} +1 -1
- package/package.json +12 -12
- package/dist/ShareablesScreen-CNMWSoKY.mjs.map +0 -1
- package/dist/ShareablesScreen-Dp3BcjuA.cjs.map +0 -1
- package/dist/ShopScreen-CPWZhKgE.cjs.map +0 -1
- package/dist/src-fXyI4AWk.cjs.map +0 -1
- package/dist/src-nhqydD53.mjs.map +0 -1
- /package/dist/{ProfileScreen-BKKIMZiD.cjs → ProfileScreen-BSZLS9hY.cjs} +0 -0
- /package/dist/{src-BakNjVTk.mjs → src-B7j_mfeS.mjs} +0 -0
- /package/dist/{src-C_kOrGdZ.cjs → src-Bp-dRR29.cjs} +0 -0
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["SPIN_STYLE_ID","ensureSpinStyle","React","ICON_MAP","React","cn","cn","Skeleton","ProfileScreen","OrdersScreen","SubscriptionsScreen","MessagingScreen","ContactsScreen","ShopScreen","CustomersScreen","ProductsScreen","ShareablesScreen","MySiteScreen","now","daysFromNow","now","now","hoursAgo","now","daysAgo"],"sources":["../src/types/page-template.ts","../src/registries/page-template-registry.ts","../src/core/resolve-pages.ts","../src/providers/PageTemplateProvider.tsx","../src/components/AuthError.tsx","../src/components/RequireAuth.tsx","../../react/src/shell/use-mobile.ts","../../react/src/shell/sidebar.tsx","../../react/src/shell/AppShellLayout.tsx","../../react/src/shell/ThemeModeContext.tsx","../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/default-navigation.ts","../../react/src/shell/ScreenHeader.tsx","../../react/src/components/RepIcon.tsx","../../core/src/navigation/navigation-storage.ts","../../core/src/navigation/navigation-utils.ts","../src/shell/SdkBottomNav.tsx","../src/hooks/use-fluid-app.ts","../src/shell/AppShellLoading.tsx","../src/shell/CollapsibleNavItem.tsx","../src/shell/SdkNavigation.tsx","../src/shell/QuickLinksDropdown.tsx","../src/shell/SdkMobileQuickLinksGrid.tsx","../src/shell/SdkMobileProfileMenu.tsx","../src/shell/SdkHeader.tsx","../src/hooks/use-company-switch.ts","../../../company-switcher/core/src/use-company-switcher.ts","../../../company-switcher/ui/src/components/CompanyImage.tsx","../../../company-switcher/ui/src/components/CompanyItem.tsx","../../../company-switcher/ui/src/components/CompanyList.tsx","../../../company-switcher/ui/src/components/CompanySwitcherDropdown.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSheet.tsx","../../../company-switcher/ui/src/components/CompanySwitcherConfirmDialog.tsx","../../../company-switcher/ui/src/components/CompanySwitcher.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSkeleton.tsx","../src/shell/SdkCompanySwitcher.tsx","../../core/src/navigation/account-manage.ts","../../core/src/data-sources/presets.ts","../../react/src/data-sources/use-widget-data.ts","../../react/src/data-sources/ErrorState.tsx","../../react/src/data-sources/DataAwareWidget.tsx","../src/shell/BuilderScreenView.tsx","../src/account/account-management-layout.tsx","../src/shell/system-screen-map.ts","../src/shell/PageRouter.tsx","../../core/src/navigation/slug-utils.ts","../src/hooks/use-logout.ts","../src/shell/SdkLogoutButton.tsx","../src/shell/AppShell.tsx","../src/config/env.ts","../src/config/defaults.ts","../src/entry/create-portal.tsx","../src/hooks/use-fluid-profile.ts","../src/hooks/use-fluid-permissions.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/use-current-rep.ts","../src/hooks/demo-data/calendar-events.ts","../src/hooks/use-calendar-events.ts","../src/hooks/demo-data/todos.ts","../src/hooks/use-todos.ts","../src/hooks/hook-types.ts","../src/hooks/demo-data/activities.ts","../src/hooks/use-activities.ts","../src/hooks/demo-data/catchups.ts","../src/hooks/use-catchups.ts","../src/hooks/demo-data/mysite.ts","../src/hooks/use-mysite.ts","../src/hooks/demo-data/conversations.ts","../src/hooks/use-conversations.ts","../src/types/screen-types.ts","../src/hooks/demo-data/contacts.ts","../src/hooks/use-contacts.ts","../src/screens/index.ts","../src/utils/build-widget-registry.ts","../src/shell/AppLink.tsx","../src/scaffold/manifest.ts"],"sourcesContent":["import type { WidgetSchema } from \"./widget-schema\";\n\n/**\n * Category for organizing page templates in the registry\n */\nexport interface PageCategory {\n /** Unique identifier for the category */\n readonly id: string;\n /** Display label */\n label: string;\n /** Icon identifier (e.g., lucide icon name) */\n icon?: string;\n}\n\n/**\n * A reusable page template that can be shared across multiple navigations\n */\nexport interface PageTemplate {\n /** Unique identifier for the template */\n readonly id: string;\n /** URL-friendly slug */\n slug: string;\n /** Display name */\n name: string;\n /** Description of the template's purpose */\n description?: string;\n /** Category ID for organization */\n category: string;\n /** Tags for filtering and search */\n tags?: readonly string[];\n /** The widget tree that defines the page content */\n component_tree: readonly WidgetSchema[];\n /** Semantic version of the template */\n version: string;\n /** Whether this is a core feature that cannot be removed */\n isCore?: boolean;\n /** Default prop values that can be customized */\n defaultProps?: Readonly<Record<string, unknown>>;\n /** Thumbnail image URL for UI display */\n thumbnail?: string;\n}\n\n/**\n * Reference to a shared page template within a navigation\n */\nexport interface PageReference {\n /** ID of the page template being referenced */\n page_template_id: string;\n /** Screen ID to assign to this page in the navigation */\n screen_id: number;\n /** Optional prop overrides (only prop values, not widget structure) */\n overrides?: readonly PageOverride[];\n}\n\n/**\n * Override for a specific widget's props within a page template\n */\nexport interface PageOverride {\n /** ID of the widget to override (must match WidgetSchema.id in the template) */\n readonly widget_id: string;\n /** Props to override (merged with original props) */\n props: Readonly<Record<string, unknown>>;\n}\n\n/**\n * Built-in page category IDs\n */\nexport const PAGE_CATEGORIES = {\n CORE: \"core\",\n COMMERCE: \"commerce\",\n COMMUNICATION: \"communication\",\n DATA: \"data\",\n CUSTOM: \"custom\",\n} as const;\n\nexport type PageCategoryId =\n (typeof PAGE_CATEGORIES)[keyof typeof PAGE_CATEGORIES];\n","import type {\n PageTemplate,\n PageCategory,\n PageCategoryId,\n} from \"../types/page-template\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Registry for managing reusable page templates.\n *\n * The registry provides a central store for page templates that can be\n * shared across multiple navigations. Core pages (like Messaging, Contacts)\n * are pre-registered and cannot be removed.\n *\n * @example\n * ```ts\n * // Register a custom page template\n * PageTemplateRegistry.register({\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Custom Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Hello' } }],\n * });\n *\n * // Get a template by ID\n * const template = PageTemplateRegistry.get('custom-dashboard');\n *\n * // List all templates in a category\n * const corePages = PageTemplateRegistry.getByCategory('core');\n * ```\n */\nclass PageTemplateRegistryImpl {\n private templates = new Map<string, PageTemplate>();\n private categories: PageCategory[] = [];\n\n constructor() {\n // Initialize default categories\n this.categories = [\n { id: PAGE_CATEGORIES.CORE, label: \"Core Features\", icon: \"star\" },\n {\n id: PAGE_CATEGORIES.COMMERCE,\n label: \"Commerce\",\n icon: \"shopping-cart\",\n },\n {\n id: PAGE_CATEGORIES.COMMUNICATION,\n label: \"Communication\",\n icon: \"message-circle\",\n },\n {\n id: PAGE_CATEGORIES.DATA,\n label: \"Data & Analytics\",\n icon: \"bar-chart\",\n },\n { id: PAGE_CATEGORIES.CUSTOM, label: \"Custom\", icon: \"puzzle\" },\n ];\n }\n\n /**\n * Register a new page template.\n * @throws Error if a template with the same ID already exists\n */\n register(template: PageTemplate): void {\n if (this.templates.has(template.id)) {\n throw new Error(\n `Page template with ID \"${template.id}\" is already registered`,\n );\n }\n this.templates.set(template.id, template);\n }\n\n /**\n * Unregister a page template by ID.\n * Core templates cannot be unregistered.\n * @returns true if the template was removed, false if it didn't exist or is a core template\n */\n unregister(id: string): boolean {\n const template = this.templates.get(id);\n if (!template) {\n return false;\n }\n if (template.isCore) {\n console.warn(\n `Cannot unregister core page template \"${id}\". Core templates are required.`,\n );\n return false;\n }\n return this.templates.delete(id);\n }\n\n /**\n * Get a page template by ID.\n */\n get(id: string): PageTemplate | undefined {\n return this.templates.get(id);\n }\n\n /**\n * Get all page templates in a specific category.\n */\n getByCategory(category: PageCategoryId | string): PageTemplate[] {\n return Array.from(this.templates.values()).filter(\n (t) => t.category === category,\n );\n }\n\n /**\n * List all registered page templates.\n */\n listAll(): PageTemplate[] {\n return Array.from(this.templates.values());\n }\n\n /**\n * List all core page templates (isCore: true).\n */\n listCore(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => t.isCore);\n }\n\n /**\n * List all non-core page templates.\n */\n listOptional(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => !t.isCore);\n }\n\n /**\n * List all registered categories.\n */\n listCategories(): PageCategory[] {\n return [...this.categories];\n }\n\n /**\n * Add a custom category.\n */\n addCategory(category: PageCategory): void {\n if (this.categories.some((c) => c.id === category.id)) {\n console.warn(`Category with ID \"${category.id}\" already exists`);\n return;\n }\n this.categories.push(category);\n }\n\n /**\n * Check if a template exists by ID.\n */\n has(id: string): boolean {\n return this.templates.has(id);\n }\n\n /**\n * Get the count of registered templates.\n */\n get size(): number {\n return this.templates.size;\n }\n\n /**\n * Clear all non-core templates.\n * Useful for testing or resetting the registry.\n */\n clearNonCore(): void {\n for (const [id, template] of this.templates) {\n if (!template.isCore) {\n this.templates.delete(id);\n }\n }\n }\n}\n\n/**\n * Global page template registry singleton.\n *\n * This registry is automatically populated with core page templates\n * (Messaging, Contacts) when the SDK is imported.\n */\nexport const PageTemplateRegistry: PageTemplateRegistryImpl =\n new PageTemplateRegistryImpl();\n","import type { Navigation, ScreenDefinition, WidgetSchema } from \"../types\";\nimport type {\n PageReference,\n PageOverride,\n PageTemplate,\n} from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\n\n/**\n * Apply prop overrides to widgets in a component tree.\n * Recursively walks widget.props.children so overrides targeting\n * nested widgets (inside LayoutWidget, ContainerWidget, etc.) are applied.\n *\n * @param componentTree - The original component tree from the page template\n * @param overrides - Array of widget-specific prop overrides\n * @returns A new component tree with overrides applied\n */\nfunction applyOverrides(\n componentTree: readonly WidgetSchema[],\n overrides: readonly PageOverride[],\n): WidgetSchema[] {\n if (!overrides.length) {\n // Return a mutable copy to satisfy ScreenDefinition.component_tree type\n return [...componentTree];\n }\n\n // Build a map of widget_id -> override for O(1) lookup (built once, shared across recursion)\n const overrideMap = new Map(overrides.map((o) => [o.widget_id, o.props]));\n\n return componentTree.map((widget) =>\n applyWidgetOverrides(widget, overrideMap),\n );\n}\n\n/**\n * Recursively apply overrides to a widget and its children.\n * Only clones widgets that actually change (have an override or contain children that might).\n */\nfunction applyWidgetOverrides(\n widget: WidgetSchema,\n overrideMap: ReadonlyMap<string, Record<string, unknown>>,\n): WidgetSchema {\n const override = widget.id ? overrideMap.get(widget.id) : undefined;\n const children = widget.props.children;\n\n // Recurse into children if they exist (LayoutWidget, ContainerWidget)\n const hasChildren = Array.isArray(children) && children.length > 0;\n\n if (!override && !hasChildren) {\n return widget;\n }\n\n const newProps = override\n ? { ...widget.props, ...override }\n : { ...widget.props };\n\n if (hasChildren) {\n newProps.children = (children as readonly WidgetSchema[]).map((child) =>\n applyWidgetOverrides(child, overrideMap),\n );\n }\n\n return { ...widget, props: newProps };\n}\n\n/**\n * Resolve a page reference to a screen definition.\n *\n * @param ref - The page reference from navigation\n * @returns A ScreenDefinition or undefined if the template doesn't exist\n */\nfunction resolvePageReference(\n ref: Readonly<PageReference>,\n): ScreenDefinition | undefined {\n const template = PageTemplateRegistry.get(ref.page_template_id);\n if (!template) {\n console.warn(\n `Page template \"${ref.page_template_id}\" not found in registry`,\n );\n return undefined;\n }\n\n // Apply any overrides to the component tree\n // Spread to create mutable array for ScreenDefinition.component_tree\n const componentTree = ref.overrides\n ? applyOverrides(template.component_tree, ref.overrides)\n : [...template.component_tree];\n\n return {\n id: ref.screen_id,\n slug: template.slug,\n name: template.name,\n component_tree: componentTree,\n };\n}\n\n/**\n * Resolve all page references and local screens into a unified screen list.\n *\n * This function merges:\n * 1. Screen definitions from page_refs (shared templates)\n * 2. Local screen definitions (for backwards compatibility and custom screens)\n *\n * When a screen_id appears in both page_refs and screens, the local screen\n * takes precedence (allows local overrides of template pages).\n *\n * @param navigation - The navigation configuration\n * @returns A unified array of ScreenDefinition objects\n *\n * @example\n * ```ts\n * const navigation: Navigation = {\n * definition_id: 1,\n * id: 1,\n * name: \"Main Nav\",\n * navigation_items: [...],\n * screens: [\n * // Local custom screen\n * { id: 1, slug: \"home\", name: \"Home\", component_tree: [...] }\n * ],\n * page_refs: [\n * // Reference to shared messaging template\n * { page_template_id: \"core-messaging\", screen_id: 2 }\n * ],\n * };\n *\n * const allScreens = resolveNavigationPages(navigation);\n * // Returns: [home screen, messaging screen from template]\n * ```\n */\nexport function resolveNavigationPages(\n navigation: Readonly<Navigation>,\n): ScreenDefinition[] {\n // Start with local screens (these take precedence)\n const localScreenIds = new Set(navigation.screens.map((s) => s.id));\n const result: ScreenDefinition[] = [...navigation.screens];\n\n // Resolve page references (skip if local screen already exists with same ID)\n if (navigation.page_refs) {\n for (const ref of navigation.page_refs) {\n if (localScreenIds.has(ref.screen_id)) {\n // Local screen takes precedence\n continue;\n }\n\n const screen = resolvePageReference(ref);\n if (screen) {\n result.push(screen);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Get all available page templates for use in navigation.\n *\n * @returns Array of page templates from the registry\n */\nexport function getAvailablePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listAll();\n}\n\n/**\n * Get core page templates that are required for basic functionality.\n *\n * @returns Array of core page templates\n */\nexport function getCorePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listCore();\n}\n\n/**\n * Get optional page templates that can be added to navigation.\n *\n * @returns Array of optional (non-core) page templates\n */\nexport function getOptionalPageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listOptional();\n}\n\n/**\n * Check if a navigation has all required core pages.\n *\n * @param navigation - The navigation to check\n * @returns Object with validation result and missing page IDs\n */\nexport function validateNavigationPages(navigation: Readonly<Navigation>): {\n readonly valid: boolean;\n readonly missingCorePages: readonly string[];\n} {\n const corePages = PageTemplateRegistry.listCore();\n const referencedTemplateIds = new Set(\n navigation.page_refs?.map((ref) => ref.page_template_id) ?? [],\n );\n\n // Check which core pages are referenced\n const missingCorePages = corePages\n .filter((page) => !referencedTemplateIds.has(page.id))\n .map((page) => page.id);\n\n return {\n valid: missingCorePages.length === 0,\n missingCorePages,\n };\n}\n","import React, {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport type { PageTemplate } from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { resolveNavigationPages } from \"../core/resolve-pages\";\nimport type { Navigation, ScreenDefinition } from \"../types\";\n\n/** Stable empty array for the default `templates` prop (avoids new reference each render) */\nconst EMPTY_TEMPLATES: readonly PageTemplate[] = [];\n\n/**\n * Context value for page template resolution.\n * All properties are readonly since context values should not be mutated by consumers.\n */\ninterface PageTemplateContextValue {\n /**\n * Resolve a navigation's page_refs and screens into a unified screen list\n */\n readonly resolvePages: (navigation: Navigation) => ScreenDefinition[];\n /**\n * Get all available page templates\n */\n readonly listTemplates: () => PageTemplate[];\n /**\n * Get a specific template by ID\n */\n readonly getTemplate: (id: string) => PageTemplate | undefined;\n /**\n * Check if a template exists\n */\n readonly hasTemplate: (id: string) => boolean;\n}\n\nconst PageTemplateContext = createContext<PageTemplateContextValue | null>(\n null,\n);\n\n/**\n * Props for PageTemplateProvider\n */\ninterface PageTemplateProviderProps {\n children: React.ReactNode;\n /**\n * Additional custom page templates to register.\n * These are registered when the provider mounts and unregistered when it unmounts.\n */\n templates?: readonly PageTemplate[];\n}\n\n/**\n * Provider for page template resolution.\n *\n * This provider:\n * 1. Registers any custom templates passed via props\n * 2. Provides methods for resolving navigation pages\n * 3. Cleans up custom templates on unmount\n *\n * @example\n * ```tsx\n * // With custom templates\n * const customTemplates: PageTemplate[] = [\n * {\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Custom Dashboard' } }],\n * },\n * ];\n *\n * <PageTemplateProvider templates={customTemplates}>\n * <App />\n * </PageTemplateProvider>\n *\n * // Without custom templates (uses only core templates)\n * <PageTemplateProvider>\n * <App />\n * </PageTemplateProvider>\n * ```\n */\nexport function PageTemplateProvider({\n children,\n templates = EMPTY_TEMPLATES,\n}: PageTemplateProviderProps): React.JSX.Element {\n // Track which templates we registered so we can clean them up\n const registeredIds = useRef<string[]>([]);\n\n // Derive a stable primitive key from template IDs\n // (rerender-dependencies rule: use primitive dependencies in effects)\n const templateKey = templates.map((t) => t.id).join(\",\");\n\n // Register custom templates when template IDs change\n useEffect(() => {\n const registered: string[] = [];\n\n for (const template of templates) {\n if (!PageTemplateRegistry.has(template.id)) {\n try {\n PageTemplateRegistry.register(template);\n registered.push(template.id);\n } catch (error) {\n console.warn(\n `Failed to register page template \"${template.id}\":`,\n error,\n );\n }\n }\n }\n\n registeredIds.current = registered;\n\n // Cleanup when template IDs change or on unmount\n return () => {\n for (const id of registeredIds.current) {\n PageTemplateRegistry.unregister(id);\n }\n registeredIds.current = [];\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [templateKey]);\n\n // Memoize context value to prevent unnecessary re-renders\n // Use `satisfies` to validate shape at compile time while preserving inference\n const contextValue = useMemo(\n () =>\n ({\n resolvePages: resolveNavigationPages,\n listTemplates: () => PageTemplateRegistry.listAll(),\n getTemplate: (id: string) => PageTemplateRegistry.get(id),\n hasTemplate: (id: string) => PageTemplateRegistry.has(id),\n }) satisfies PageTemplateContextValue,\n [],\n );\n\n return (\n <PageTemplateContext.Provider value={contextValue}>\n {children}\n </PageTemplateContext.Provider>\n );\n}\n\n/**\n * Hook to access page template functionality.\n *\n * @throws Error if used outside of PageTemplateProvider\n *\n * @example\n * ```tsx\n * function NavigationRenderer({ navigation }: { navigation: Navigation }) {\n * const { resolvePages } = usePageTemplates();\n * const screens = resolvePages(navigation);\n *\n * return (\n * <div>\n * {screens.map((screen) => (\n * <Screen key={screen.id} definition={screen} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePageTemplates(): PageTemplateContextValue {\n const context = useContext(PageTemplateContext);\n if (!context) {\n throw new Error(\n \"usePageTemplates must be used within a PageTemplateProvider\",\n );\n }\n return context;\n}\n\n/**\n * Hook to resolve navigation pages directly.\n * Convenience wrapper around usePageTemplates().resolvePages.\n *\n * @param navigation - The navigation to resolve\n * @returns Array of resolved screen definitions\n */\nexport function useResolvedPages(navigation: Navigation): ScreenDefinition[] {\n const { resolvePages } = usePageTemplates();\n return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);\n}\n","/**\n * AuthError - Default Authentication Error Component\n *\n * Displayed when authentication fails. Can be customized or replaced\n * via the RequireAuth component's errorComponent prop.\n */\n\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\n\nexport interface AuthErrorProps {\n /** Error message to display */\n message?: string;\n /** Optional title */\n title?: string;\n /** Optional children for custom content */\n children?: ReactNode;\n}\n\n/**\n * Default authentication error component.\n *\n * Displays a simple error message when authentication fails.\n * Can be customized via props or replaced entirely in RequireAuth.\n *\n * @example\n * ```tsx\n * // Use with default message\n * <AuthError />\n *\n * // Use with custom message\n * <AuthError message=\"Session expired. Please log in again.\" />\n *\n * // Use with custom content\n * <AuthError>\n * <CustomLoginButton />\n * </AuthError>\n * ```\n */\nexport function AuthError({\n message = \"You need to be authenticated to view this content.\",\n title = \"Authentication Required\",\n children,\n}: AuthErrorProps): React.JSX.Element {\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n padding: \"2rem\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n color: \"#111827\",\n }}\n >\n <div\n style={{\n maxWidth: \"400px\",\n textAlign: \"center\",\n padding: \"2rem\",\n backgroundColor: \"#ffffff\",\n borderRadius: \"0.75rem\",\n boxShadow:\n \"0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)\",\n }}\n >\n {/* Lock Icon */}\n <div\n style={{\n width: \"64px\",\n height: \"64px\",\n margin: \"0 auto 1.5rem\",\n backgroundColor: \"#fee2e2\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#dc2626\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n </div>\n\n <h1\n style={{\n fontSize: \"1.5rem\",\n fontWeight: \"600\",\n marginBottom: \"0.75rem\",\n color: \"#111827\",\n }}\n >\n {title}\n </h1>\n\n <p\n style={{\n fontSize: \"1rem\",\n color: \"#6b7280\",\n marginBottom: children ? \"1.5rem\" : \"0\",\n lineHeight: \"1.5\",\n }}\n >\n {message}\n </p>\n\n {children}\n </div>\n </div>\n );\n}\n\nconst SPIN_STYLE_ID = \"fluid-auth-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Simple loading spinner component for auth loading state.\n */\nexport function AuthLoading(): React.JSX.Element {\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n Authenticating...\n </p>\n </div>\n );\n}\n","/**\n * RequireAuth - Route Protection Component\n *\n * Wraps content that requires authentication. Shows loading state\n * while checking auth, error component if not authenticated, or\n * children if authenticated.\n */\n\nimport type { ReactNode } from \"react\";\nimport { useFluidAuth } from \"../hooks/use-fluid-auth\";\nimport { AuthError, AuthLoading } from \"./AuthError\";\n\nexport interface RequireAuthProps {\n /** Content to render when authenticated */\n children: ReactNode;\n /** Component to show while checking authentication (default: AuthLoading) */\n fallback?: ReactNode;\n /** Component to show when not authenticated (default: AuthError) */\n errorComponent?: ReactNode;\n}\n\n/**\n * Component that protects content requiring authentication.\n *\n * **Important:** This provides UX-level protection only. It prevents\n * unauthenticated users from seeing the UI, but the real security\n * boundary is the server-side API. Client-side auth can always be\n * bypassed by modifying browser state.\n *\n * For defense-in-depth, configure `jwksUrl` in `FluidAuthConfig`\n * to enable client-side JWT signature verification.\n *\n * Shows different states based on auth status:\n * - Loading: Shows fallback (spinner by default)\n * - Not authenticated: Shows errorComponent (AuthError by default)\n * - Authenticated: Shows children\n *\n * @example\n * ```tsx\n * // Basic usage - shows default loading/error states\n * <RequireAuth>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Custom loading state\n * <RequireAuth fallback={<CustomSpinner />}>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Custom error component\n * <RequireAuth errorComponent={<LoginPrompt />}>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Both custom\n * <RequireAuth\n * fallback={<SkeletonLoader />}\n * errorComponent={<RedirectToLogin />}\n * >\n * <Dashboard />\n * </RequireAuth>\n * ```\n */\nexport function RequireAuth({\n children,\n fallback = <AuthLoading />,\n errorComponent = <AuthError />,\n}: RequireAuthProps): React.JSX.Element {\n const { isAuthenticated, isLoading, error } = useFluidAuth();\n\n // Show loading state while checking authentication\n if (isLoading) {\n return <>{fallback}</>;\n }\n\n // Show error state if not authenticated\n if (!isAuthenticated || error) {\n return <>{errorComponent}</>;\n }\n\n // User is authenticated, render children\n return <>{children}</>;\n}\n","import { useState, useEffect } from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\nconst TABLET_BREAKPOINT = 1024;\n\nexport function useIsMobile(): boolean {\n const [isMobile, setIsMobile] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isMobile;\n}\n\nexport function useIsTablet(): boolean {\n const [isTablet, setIsTablet] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(\n `(min-width: ${MOBILE_BREAKPOINT}px) and (max-width: ${TABLET_BREAKPOINT - 1}px)`,\n );\n const onChange = () => {\n setIsTablet(\n window.innerWidth >= MOBILE_BREAKPOINT &&\n window.innerWidth < TABLET_BREAKPOINT,\n );\n };\n mql.addEventListener(\"change\", onChange);\n setIsTablet(\n window.innerWidth >= MOBILE_BREAKPOINT &&\n window.innerWidth < TABLET_BREAKPOINT,\n );\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isTablet;\n}\n\nexport function useIsMobileOrTablet(): boolean {\n const [isMobileOrTablet, setIsMobileOrTablet] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${TABLET_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobileOrTablet(window.innerWidth < TABLET_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobileOrTablet(window.innerWidth < TABLET_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isMobileOrTablet;\n}\n","\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport * as React from \"react\";\n\nimport { useIsMobile } from \"./use-mobile\";\n\n// ---------------------------------------------------------------------------\n// Inlined utilities (avoid importing from builder's UI kit)\n// ---------------------------------------------------------------------------\n\nfunction cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentPropsWithRef<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n decorative?: boolean;\n}) {\n return (\n <div\n role=\"separator\"\n aria-orientation={orientation}\n className={cn(\n \"bg-border shrink-0\",\n orientation === \"horizontal\" ? \"h-px w-full\" : \"h-full w-px\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Skeleton({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\"bg-muted animate-pulse rounded-md\", className)}\n {...props}\n />\n );\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SIDEBAR_WIDTH = \"13rem\";\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\n\n// ---------------------------------------------------------------------------\n// Sidebar Context\n// ---------------------------------------------------------------------------\n\ntype SidebarContextValue = {\n state: \"expanded\" | \"collapsed\";\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n isPreviewMode: boolean;\n useBottomNav: boolean;\n};\n\nexport const SidebarContext: React.Context<SidebarContextValue | null> =\n React.createContext<SidebarContextValue | null>(null);\n\nfunction useSidebar(): SidebarContextValue {\n const context = React.useContext(SidebarContext);\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\");\n }\n\n return context;\n}\n\n// ---------------------------------------------------------------------------\n// SidebarProvider\n// ---------------------------------------------------------------------------\n\nconst SidebarProvider: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n viewportWidth?: number;\n previewMode?: boolean;\n useBottomNav?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n viewportWidth?: number;\n previewMode?: boolean;\n useBottomNav?: boolean;\n }\n>(\n (\n {\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n viewportWidth,\n previewMode,\n useBottomNav: useBottomNavProp = false,\n className,\n style,\n children,\n ...props\n },\n ref,\n ) => {\n const windowIsMobile = useIsMobile();\n // Use viewportWidth if provided, otherwise use actual window detection\n const isMobile =\n viewportWidth !== undefined ? viewportWidth < 768 : windowIsMobile;\n // Preview mode is active when viewportWidth is provided\n const isPreviewMode = viewportWidth !== undefined || !!previewMode;\n const [openMobile, setOpenMobile] = React.useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n },\n [setOpenProp, open],\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile\n ? setOpenMobile((open) => !open)\n : setOpen((open) => !open);\n }, [isMobile, setOpen, setOpenMobile]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n // check if composer is focused - if so, let the composer handle the shortcut\n const activeElement = document.activeElement;\n const isComposerFocused =\n activeElement?.closest(\".group\\\\/composer\") ||\n activeElement?.closest(\"[data-toolbar]\") ||\n activeElement?.classList.contains(\"ProseMirror\");\n\n if (isComposerFocused) {\n return; // let the composer handle the shortcut\n }\n\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\";\n\n const contextValue = React.useMemo<SidebarContextValue>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n isPreviewMode,\n useBottomNav: useBottomNavProp,\n }),\n [\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n isPreviewMode,\n useBottomNavProp,\n ],\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-0 w-full flex-1\",\n className,\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n },\n);\nSidebarProvider.displayName = \"SidebarProvider\";\n\n// ---------------------------------------------------------------------------\n// Sidebar\n// ---------------------------------------------------------------------------\n\nconst Sidebar: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\";\n variant?: \"sidebar\" | \"floating\" | \"inset\";\n collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\";\n variant?: \"sidebar\" | \"floating\" | \"inset\";\n collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n }\n>(\n (\n {\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const {\n isMobile,\n state,\n openMobile,\n setOpenMobile,\n isPreviewMode,\n useBottomNav,\n } = useSidebar();\n\n // Define CSS variables for expanded and collapsed sidebar widths\n const sidebarWidth =\n state === \"expanded\" ? SIDEBAR_WIDTH : SIDEBAR_WIDTH_ICON;\n\n // When bottom nav is active on mobile, hide the sidebar entirely\n if (useBottomNav && isMobile) {\n return null;\n }\n\n if (collapsible === \"none\") {\n return (\n <div\n className={cn(\n \"bg-sidebar text-sidebar-foreground flex w-(--sidebar-width) flex-col rounded-tl-lg\",\n isPreviewMode ? \"h-full\" : \"h-[97vh]\",\n className,\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n // For mobile, render a slide-out sidebar with overlay\n // Use absolute positioning in preview mode so it stays within the preview container\n const positionClass = isPreviewMode ? \"absolute\" : \"fixed\";\n return (\n <>\n {/* Overlay - only visible when sidebar is open */}\n {openMobile && (\n <div\n className={cn(positionClass, \"inset-0 z-40 bg-black/50\")}\n onClick={() => setOpenMobile(false)}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Sidebar - slides in from left */}\n <div\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className={cn(\n positionClass,\n \"bg-sidebar text-sidebar-foreground top-0 left-0 z-50 h-full w-[--sidebar-width] p-0 transition-transform duration-300 ease-in-out\",\n openMobile ? \"translate-x-0\" : \"-translate-x-full\",\n className,\n )}\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n ref={ref}\n {...props}\n >\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </div>\n </>\n );\n }\n\n return (\n <div\n ref={ref}\n className=\"group peer bg-sidebar text-sidebar-foreground hidden md:block\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n style={\n {\n \"--sidebar-width\": sidebarWidth,\n } as React.CSSProperties\n }\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={cn(\n \"relative bg-transparent transition-[width] duration-200 ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\",\n )}\n />\n <div\n className={cn(\n \"relative inset-y-0 z-[20] hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex\",\n isPreviewMode ? \"h-full\" : \"h-svh\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n // Adjust the padding for floating and inset variants.\n variant === \"floating\" || variant === \"inset\"\n ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=right]:border-l\",\n className,\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n },\n);\nSidebar.displayName = \"Sidebar\";\n\n// ---------------------------------------------------------------------------\n// SidebarRail\n// ---------------------------------------------------------------------------\n\nconst SidebarRail: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<HTMLButtonElement, React.ComponentProps<\"button\">>(\n ({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n ref={ref}\n data-sidebar=\"rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"hover:after:bg-sidebar-border absolute inset-y-0 z-[10] hidden w-4 -translate-x-full transition-all ease-linear group-data-[side=left]:-right-[1.375rem] group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex\",\n \"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarRail.displayName = \"SidebarRail\";\n\n// ---------------------------------------------------------------------------\n// SidebarInset\n// ---------------------------------------------------------------------------\n\nconst SidebarInset: React.ForwardRefExoticComponent<\n React.ComponentProps<\"main\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"main\">>(\n ({ className, ...props }, ref) => {\n const { isPreviewMode } = useSidebar();\n return (\n <main\n ref={ref}\n className={cn(\n \"relative flex flex-1 flex-col\",\n isPreviewMode\n ? \"max-h-[calc(100svh-(--spacing(4)))]\"\n : \"min-h-svh peer-data-[variant=inset]:min-h-[calc(100svh-(--spacing(4)))]\",\n \"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarInset.displayName = \"SidebarInset\";\n\n// ---------------------------------------------------------------------------\n// SidebarInput\n// ---------------------------------------------------------------------------\n\nconst SidebarInput: React.ForwardRefExoticComponent<\n React.ComponentProps<\"input\"> & React.RefAttributes<HTMLInputElement>\n> = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, ...props }, ref) => {\n return (\n <input\n ref={ref}\n data-sidebar=\"input\"\n className={cn(\n \"bg-background focus-visible:ring-sidebar-ring h-8 w-full rounded-md border px-3 text-sm shadow-none focus-visible:ring-2 focus-visible:outline-none\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarInput.displayName = \"SidebarInput\";\n\n// ---------------------------------------------------------------------------\n// SidebarHeader / Footer\n// ---------------------------------------------------------------------------\n\nconst SidebarHeader: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n },\n);\nSidebarHeader.displayName = \"SidebarHeader\";\n\nconst SidebarFooter: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n },\n);\nSidebarFooter.displayName = \"SidebarFooter\";\n\n// ---------------------------------------------------------------------------\n// SidebarSeparator\n// ---------------------------------------------------------------------------\n\nconst SidebarSeparator: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n }\n>(({ className, ...props }, ref) => {\n return (\n <Separator\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"bg-sidebar-border mx-2 w-auto\", className)}\n {...props}\n />\n );\n});\nSidebarSeparator.displayName = \"SidebarSeparator\";\n\n// ---------------------------------------------------------------------------\n// SidebarContent\n// ---------------------------------------------------------------------------\n\nconst SidebarContent: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"scrollbar-none flex min-h-0 flex-1 flex-col gap-2 overflow-auto rounded group-data-[collapsible=icon]:gap-0 group-data-[collapsible=icon]:overflow-hidden group-data-[collapsible=icon]:pt-3\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarContent.displayName = \"SidebarContent\";\n\n// ---------------------------------------------------------------------------\n// SidebarGroup\n// ---------------------------------------------------------------------------\n\nconst SidebarGroup: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\n \"relative flex w-full min-w-0 flex-col p-2 group-data-[collapsible=icon]:py-0 group-data-[collapsible=icon]:pt-4\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarGroup.displayName = \"SidebarGroup\";\n\nconst SidebarGroupLabel: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n asChild?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"div\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarGroupLabel.displayName = \"SidebarGroupLabel\";\n\nconst SidebarGroupAction: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n } & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 md:after:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarGroupAction.displayName = \"SidebarGroupAction\";\n\nconst SidebarGroupContent: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"w-full text-sm\", className)}\n {...props}\n />\n ),\n);\nSidebarGroupContent.displayName = \"SidebarGroupContent\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenu\n// ---------------------------------------------------------------------------\n\nconst SidebarMenu: React.ForwardRefExoticComponent<\n React.ComponentProps<\"ul\"> & React.RefAttributes<HTMLUListElement>\n> = React.forwardRef<HTMLUListElement, React.ComponentProps<\"ul\">>(\n ({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n ),\n);\nSidebarMenu.displayName = \"SidebarMenu\";\n\nconst SidebarMenuItem: React.ForwardRefExoticComponent<\n React.ComponentProps<\"li\"> & React.RefAttributes<HTMLLIElement>\n> = React.forwardRef<HTMLLIElement, React.ComponentProps<\"li\">>(\n ({ className, ...props }, ref) => (\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n ),\n);\nSidebarMenuItem.displayName = \"SidebarMenuItem\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuButton\n// ---------------------------------------------------------------------------\n\nconst sidebarMenuButtonVariants: (\n props?:\n | ({\n variant?: \"default\" | \"outline\" | null | undefined;\n size?: \"default\" | \"sm\" | \"lg\" | null | undefined;\n } & (\n | {\n class: ClassValue;\n className?: never;\n }\n | {\n class?: never;\n className: ClassValue;\n }\n | {\n class?: never;\n className?: never;\n }\n ))\n | undefined,\n) => string = cva(\n \"peer/menu-button ring-sidebar-ring hover:bg-sidebar-primary hover:text-sidebar-primary-foreground active:bg-sidebar-primary active:text-sidebar-primary-foreground data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground data-[state=open]:hover:bg-sidebar-primary data-[state=open]:hover:text-sidebar-primary-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding,background-color,color] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n {\n variants: {\n variant: {\n default:\n \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\",\n outline:\n \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-primary))]\",\n },\n size: {\n default: \"h-8 text-sm\",\n sm: \"h-7 text-xs\",\n lg: \"h-12 text-sm group-data-[collapsible=icon]:p-0!\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nconst SidebarMenuButton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n isActive?: boolean;\n } & VariantProps<typeof sidebarMenuButtonVariants> &\n React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n isActive?: boolean;\n } & VariantProps<typeof sidebarMenuButtonVariants>\n>(\n (\n {\n asChild = false,\n isActive = false,\n variant = \"default\",\n size = \"default\",\n className,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n\n const button = (\n <Comp\n ref={ref}\n data-sidebar=\"menu-button\"\n data-size={size}\n data-active={isActive}\n className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n {...props}\n />\n );\n\n return button;\n },\n);\nSidebarMenuButton.displayName = \"SidebarMenuButton\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuAction / Badge / Skeleton\n// ---------------------------------------------------------------------------\n\nconst SidebarMenuAction: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n showOnHover?: boolean;\n } & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n showOnHover?: boolean;\n }\n>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 md:after:hidden\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarMenuAction.displayName = \"SidebarMenuAction\";\n\nconst SidebarMenuBadge: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n ),\n);\nSidebarMenuBadge.displayName = \"SidebarMenuBadge\";\n\nconst SidebarMenuSkeleton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean;\n }\n>(({ className, showIcon = false, ...props }, ref) => {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`;\n }, []);\n\n return (\n <div\n ref={ref}\n data-sidebar=\"menu-skeleton\"\n className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n {...props}\n >\n {showIcon && (\n <Skeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <Skeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n );\n});\nSidebarMenuSkeleton.displayName = \"SidebarMenuSkeleton\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuSub\n// ---------------------------------------------------------------------------\n\nconst SidebarMenuSub: React.ForwardRefExoticComponent<\n React.ComponentProps<\"ul\"> & React.RefAttributes<HTMLUListElement>\n> = React.forwardRef<HTMLUListElement, React.ComponentProps<\"ul\">>(\n ({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n ),\n);\nSidebarMenuSub.displayName = \"SidebarMenuSub\";\n\nconst SidebarMenuSubItem: React.ForwardRefExoticComponent<\n React.ComponentProps<\"li\"> & React.RefAttributes<HTMLLIElement>\n> = React.forwardRef<HTMLLIElement, React.ComponentProps<\"li\">>(\n ({ ...props }, ref) => <li ref={ref} {...props} />,\n);\nSidebarMenuSubItem.displayName = \"SidebarMenuSubItem\";\n\nconst SidebarMenuSubButton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"a\"> & {\n asChild?: boolean;\n size?: \"sm\" | \"md\";\n isActive?: boolean;\n } & React.RefAttributes<HTMLAnchorElement>\n> = React.forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<\"a\"> & {\n asChild?: boolean;\n size?: \"sm\" | \"md\";\n isActive?: boolean;\n }\n>(({ asChild = false, size = \"md\", isActive, className, ...props }, ref) => {\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarMenuSubButton.displayName = \"SidebarMenuSubButton\";\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupAction,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInput,\n SidebarInset,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarProvider,\n SidebarRail,\n SidebarSeparator,\n useSidebar,\n};\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarInset,\n SidebarProvider,\n} from \"./sidebar\";\n\nexport interface AppShellLayoutProps {\n /** Navigation content rendered inside the sidebar's scrollable area */\n sidebarContent: React.ReactNode;\n /** Header content rendered above the main content area */\n headerContent: React.ReactNode;\n /** Main page content */\n children: React.ReactNode;\n /** Optional slot at the top of the sidebar (e.g. logo, search) */\n sidebarHeader?: React.ReactNode;\n /** Optional slot at the bottom of the sidebar (e.g. user info) */\n sidebarFooter?: React.ReactNode;\n /** Content rendered after SidebarInset (e.g. MobileBottomNav) */\n afterContent?: React.ReactNode;\n /** Enable bottom nav mode (hides sidebar on mobile, adds bottom padding) */\n useBottomNav?: boolean;\n}\n\n/**\n * Pure visual frame that replicates the RepApp layout:\n * - 13rem collapsible sidebar\n * - 52px header area\n * - rounded-xl bg-background shadow-lg content area with bg-muted gutters\n *\n * This component handles zero business logic — it simply provides the visual shell.\n */\nexport function AppShellLayout({\n sidebarContent,\n headerContent,\n children,\n sidebarHeader,\n sidebarFooter,\n afterContent,\n useBottomNav = false,\n}: AppShellLayoutProps): React.JSX.Element {\n return (\n <SidebarProvider useBottomNav={useBottomNav}>\n <div className=\"bg-muted relative flex max-h-dvh w-full overflow-hidden\">\n {/* Navigation Sidebar */}\n <Sidebar>\n {sidebarHeader && <SidebarHeader>{sidebarHeader}</SidebarHeader>}\n <SidebarContent className=\"p-4\">{sidebarContent}</SidebarContent>\n {sidebarFooter && <SidebarFooter>{sidebarFooter}</SidebarFooter>}\n </Sidebar>\n\n {/* Main Content Area */}\n <SidebarInset className=\"flex flex-1 flex-col overflow-hidden\">\n {/* Header */}\n {headerContent}\n\n {/* Screen Content */}\n <div\n className={`bg-muted flex-1 overflow-hidden md:pr-4 md:pb-4 ${useBottomNav ? \"max-md:pb-[calc(4rem+env(safe-area-inset-bottom))]\" : \"\"}`}\n >\n <div className=\"scrollbar-none bg-background text-foreground h-full overflow-auto rounded-xl shadow-lg\">\n {children}\n </div>\n </div>\n </SidebarInset>\n\n {/* After content (e.g. MobileBottomNav) */}\n {afterContent}\n </div>\n </SidebarProvider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n useContext,\n useCallback,\n useMemo,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\n\nexport type ThemeMode = \"auto\" | \"light\" | \"dark\";\n\nexport type DisplayMode = \"light\" | \"dark\";\n\nexport interface ThemeModeContextValue {\n mode: ThemeMode;\n displayMode: DisplayMode;\n setMode: (mode: ThemeMode) => void;\n autoModeEnabled: boolean;\n cycleMode: () => void;\n dataAttribute: string | undefined;\n}\n\nconst ThemeModeContext = createContext<ThemeModeContextValue | null>(null);\n\nconst darkMediaQuery =\n typeof window !== \"undefined\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n\nfunction subscribeToPrefersColorScheme(callback: () => void) {\n darkMediaQuery?.addEventListener(\"change\", callback);\n return () => darkMediaQuery?.removeEventListener(\"change\", callback);\n}\n\nfunction getSystemPrefersDark(): boolean {\n return darkMediaQuery?.matches ?? false;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\ninterface ThemeModeProviderProps {\n children: ReactNode;\n mode: ThemeMode;\n onModeChange: (mode: ThemeMode) => void;\n /** When false, auto mode is skipped in the cycle (light↔dark only). Default true. */\n autoModeEnabled?: boolean;\n}\n\nexport function ThemeModeProvider({\n children,\n mode,\n onModeChange,\n autoModeEnabled = true,\n}: ThemeModeProviderProps): React.JSX.Element {\n const systemPrefersDark = useSyncExternalStore(\n subscribeToPrefersColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n const systemMode: DisplayMode = systemPrefersDark ? \"dark\" : \"light\";\n\n const displayMode: DisplayMode = mode === \"auto\" ? systemMode : mode;\n\n const cycleMode = useCallback(() => {\n if (autoModeEnabled) {\n // Toggle displayed mode. If target matches system preference, use \"auto\".\n const target: DisplayMode = displayMode === \"light\" ? \"dark\" : \"light\";\n onModeChange(target === systemMode ? \"auto\" : target);\n } else {\n // light ↔ dark\n onModeChange(mode === \"light\" ? \"dark\" : \"light\");\n }\n }, [mode, displayMode, systemMode, onModeChange, autoModeEnabled]);\n\n const dataAttribute = getThemeModeAttribute(mode);\n\n const value = useMemo(\n () => ({\n mode,\n displayMode,\n setMode: onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n }),\n [\n mode,\n displayMode,\n onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n ],\n );\n\n return (\n <ThemeModeContext.Provider value={value}>\n {children}\n </ThemeModeContext.Provider>\n );\n}\n\n/** Access the current theme mode, setter, and cycle helper. Must be used within a `ThemeModeProvider`. */\nexport function useThemeMode(): ThemeModeContextValue {\n const ctx = useContext(ThemeModeContext);\n if (!ctx) {\n throw new Error(\"useThemeMode must be used within a ThemeModeProvider\");\n }\n return ctx;\n}\n\n/** Maps a ThemeMode to the value for `data-theme-mode`. Returns undefined for \"auto\". */\nexport function getThemeModeAttribute(mode: ThemeMode): string | undefined {\n return mode === \"auto\" ? undefined : mode;\n}\n","/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"shopping-cart\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n upgrade: {\n slug: \"upgrade\",\n label: \"Upgrade\",\n icon: \"arrow-up-from-bracket\",\n section: \"User\",\n },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\n\n/**\n * Build a NavigationItem from a system navigation slug.\n * Icons and labels come from SYSTEM_NAVIGATION_ITEMS (single source of truth).\n */\nfunction systemNavItem(slug: SystemNavigationSlug): NavigationItem {\n const item = SYSTEM_NAVIGATION_ITEMS[slug];\n return {\n slug: item.slug,\n label: item.label,\n icon: item.icon,\n children: [],\n };\n}\n\nfunction sectionHeader(label: string): NavigationItem {\n return { label, children: [] };\n}\n\n/**\n * Returns the default navigation items for a portal app when no\n * API data is available. All icons/labels derive from\n * SYSTEM_NAVIGATION_ITEMS so they can never drift.\n */\nexport function getDefaultNavigation(): NavigationItem[] {\n return [\n sectionHeader(\"We Commerce\"),\n systemNavItem(\"shop\"),\n systemNavItem(\"messages\"),\n systemNavItem(\"contacts\"),\n systemNavItem(\"account\"),\n sectionHeader(\"Marketing Assets\"),\n systemNavItem(\"share/products\"),\n ];\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { PanelLeft } from \"lucide-react\";\nimport { useSidebar } from \"./sidebar\";\nimport { useScreenHeaderContext } from \"./ScreenHeaderContext\";\n\nexport interface ScreenHeaderProps {\n title?: string;\n}\n\nexport function ScreenHeader({\n title,\n}: ScreenHeaderProps): React.JSX.Element | null {\n const { toggleSidebar, isMobile } = useSidebar();\n const { actions, breadcrumbs } = useScreenHeaderContext();\n\n if (!title && !breadcrumbs) return null;\n\n return (\n <div className=\"border-border bg-background sticky top-0 z-10 flex flex-row items-center border-b px-4 py-3 md:px-6\">\n {!isMobile && (\n <>\n <button\n type=\"button\"\n onClick={toggleSidebar}\n className=\"text-muted-foreground hover:text-foreground -ml-1 inline-flex items-center justify-center rounded-md p-1\"\n aria-label=\"Toggle Sidebar\"\n >\n <PanelLeft className=\"size-4\" />\n </button>\n <div className=\"bg-border mx-2 h-4 w-px\" />\n </>\n )}\n {breadcrumbs ? (\n <div className=\"min-w-0 flex-1\">{breadcrumbs}</div>\n ) : (\n <h1 className=\"text-foreground text-lg font-semibold\">{title}</h1>\n )}\n {actions && (\n <div className=\"ml-auto flex items-center gap-2\">{actions}</div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Bell,\n Box,\n Boxes,\n Briefcase,\n Calendar,\n CircleUser,\n Compass,\n ContactRound,\n File,\n FileText,\n FileVideo,\n Folder,\n FolderOpen,\n Globe,\n GraduationCap,\n Heart,\n House,\n Image,\n LayoutGrid,\n ListMusic,\n Mail,\n MessageCircle,\n MessageSquare,\n Repeat,\n Search,\n Settings,\n ShoppingCart,\n Star,\n Store,\n User,\n UserCog,\n Users,\n} from \"lucide-react\";\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n \"address-card\": ContactRound,\n \"arrows-repeat\": Repeat,\n bell: Bell,\n box: Box,\n \"boxes-stacked\": Boxes,\n briefcase: Briefcase,\n calendar: Calendar,\n \"cart-shopping\": ShoppingCart,\n \"circle-user\": CircleUser,\n comment: MessageCircle,\n compass: Compass,\n envelope: Mail,\n file: File,\n \"file-lines\": FileText,\n \"file-video\": FileVideo,\n folder: Folder,\n \"folder-open\": FolderOpen,\n gear: Settings,\n globe: Globe,\n \"graduation-cap\": GraduationCap,\n heart: Heart,\n house: House,\n image: Image,\n \"list-music\": ListMusic,\n \"magnifying-glass\": Search,\n message: MessageSquare,\n star: Star,\n store: Store,\n \"table-cells-large\": LayoutGrid,\n user: User,\n \"user-gear\": UserCog,\n users: Users,\n};\n\ninterface RepIconProps {\n name: string;\n className?: string;\n}\n\nexport function RepIcon({ name, className }: RepIconProps): React.JSX.Element {\n const Icon = ICON_MAP[name] ?? File;\n return <Icon className={className} />;\n}\n","/**\n * Utility functions for managing navigation state in localStorage\n */\n\nconst STORAGE_PREFIX = \"portal-app-last-tab\";\n\n/**\n * Gets the localStorage key for a specific app and top-level section\n */\nfunction getStorageKey(appDefinitionId: number, topLevelSlug: string): string {\n return `${STORAGE_PREFIX}-${appDefinitionId}-${topLevelSlug}`;\n}\n\n/**\n * Retrieves the last visited tab slug for a given section\n */\nexport function getLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n return localStorage.getItem(key);\n } catch (error) {\n console.warn(\"Failed to read from localStorage:\", error);\n return null;\n }\n}\n\n/**\n * Stores the last visited tab slug for a given section\n */\nexport function setLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n tabSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.setItem(key, tabSlug);\n } catch (error) {\n console.warn(\"Failed to write to localStorage:\", error);\n }\n}\n\n/**\n * Clears the last visited tab for a given section\n */\nexport function clearLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.removeItem(key);\n } catch (error) {\n console.warn(\"Failed to remove from localStorage:\", error);\n }\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\nimport { getLastVisitedTab } from \"./navigation-storage\";\n\n/**\n * Helper function to determine if the current route is within a top-level section\n */\nexport function isInSection(\n topLevelItem: NavigationItem,\n currentRoute: string,\n): boolean {\n const normalizedCurrentRoute = normalizeSlug(currentRoute);\n\n if (\n (!topLevelItem.children || topLevelItem.children.length <= 0) &&\n topLevelItem.slug\n ) {\n const normalizedItemSlug = normalizeSlug(topLevelItem.slug);\n if (normalizedItemSlug === normalizedCurrentRoute) {\n return true;\n }\n } else {\n return topLevelItem.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedCurrentRoute;\n });\n }\n\n return false;\n}\n\n/**\n * Get the target slug when clicking a top-level navigation item\n */\nexport function getNavigationTarget(\n item: NavigationItem,\n appDefinitionId: number,\n): string | null {\n // If it has children, it's always a section parent — navigate to a child\n if (item.children && item.children.length > 0) {\n // Try to get the last visited tab from localStorage (only if parent has a slug for the key)\n if (item.slug) {\n const lastVisitedSlug = getLastVisitedTab(appDefinitionId, item.slug);\n\n // If we have a last visited tab and it exists in the children, use it\n if (lastVisitedSlug) {\n const normalizedLastVisited = normalizeSlug(lastVisitedSlug);\n const childExists = item.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedLastVisited;\n });\n if (childExists) {\n return normalizedLastVisited;\n }\n }\n }\n\n // Default to the first child's slug\n const firstChild = item.children[0];\n if (firstChild?.slug) {\n return normalizeSlug(firstChild.slug);\n }\n\n return null;\n }\n\n // If it's a leaf node with a screen, return its slug directly (normalized)\n if (item.screen_id && item.slug) {\n return normalizeSlug(item.slug);\n }\n\n // If it's a leaf node with just a slug (system nav items like account, messages, etc.)\n if (item.slug) {\n return normalizeSlug(item.slug);\n }\n\n return null;\n}\n","import * as React from \"react\";\nimport { Ellipsis, X } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n isInSection,\n getNavigationTarget,\n} from \"@fluid-app/portal-core/navigation/utils\";\nimport type { NavigationItem } from \"../types/navigation\";\n\ninterface SdkBottomNavProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n appDefinitionId?: number;\n maxVisibleItems?: number;\n}\n\nexport function SdkBottomNav({\n navItems,\n currentSlug,\n onNavigate,\n appDefinitionId = 0,\n maxVisibleItems = 6,\n}: SdkBottomNavProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [moreOpen, setMoreOpen] = React.useState(false);\n\n if (!isMobile || !useBottomNav) return null;\n\n const navigableItems = navItems\n .filter((item) => !item.parent_id)\n .filter((item) => item.slug || (item.children && item.children.length > 0));\n\n const hasOverflow = navigableItems.length > maxVisibleItems;\n const visibleItems = hasOverflow\n ? navigableItems.slice(0, maxVisibleItems - 1)\n : navigableItems;\n const overflowItems = hasOverflow\n ? navigableItems.slice(maxVisibleItems - 1)\n : [];\n\n const handleItemClick = (item: NavigationItem) => {\n const target = getNavigationTarget(item, appDefinitionId);\n if (target) onNavigate(target);\n setMoreOpen(false);\n };\n\n return (\n <>\n <nav className=\"border-border bg-sidebar fixed right-0 bottom-0 left-0 z-40 flex h-16 items-end justify-around border-t pb-[env(safe-area-inset-bottom)]\">\n {visibleItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${\n isActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n {item.icon ? (\n <RepIcon name={item.icon} className=\"size-5\" />\n ) : (\n <span className=\"size-5\" />\n )}\n <span className=\"max-w-[72px] truncate\">{item.label}</span>\n </button>\n );\n })}\n\n {hasOverflow && (\n <button\n type=\"button\"\n onClick={() => setMoreOpen(true)}\n className={`flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${\n overflowItems.some((item) => isInSection(item, currentSlug))\n ? \"text-primary\"\n : \"text-muted-foreground\"\n }`}\n >\n <Ellipsis className=\"size-5\" />\n <span>More</span>\n </button>\n )}\n </nav>\n\n {moreOpen && (\n <div className=\"fixed inset-0 z-50 flex flex-col\">\n <div\n className=\"flex-1 bg-black/50\"\n onClick={() => setMoreOpen(false)}\n aria-hidden=\"true\"\n />\n <div className=\"bg-sidebar rounded-t-2xl pb-[env(safe-area-inset-bottom)]\">\n <div className=\"flex items-center justify-between px-4 pt-4 pb-2\">\n <span className=\"text-foreground text-sm font-semibold\">\n More\n </span>\n <button\n type=\"button\"\n onClick={() => setMoreOpen(false)}\n className=\"text-muted-foreground hover:bg-sidebar-accent flex items-center justify-center rounded-full p-1\"\n aria-label=\"Close\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n <div className=\"px-2 pb-4\">\n {overflowItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${\n isActive\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent\"\n }`}\n >\n {item.icon && (\n <RepIcon name={item.icon} className=\"size-5\" />\n )}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { createPersister } from \"@fluid-app/query-persister\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { RepAppData } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base query key for full app data (fluidos endpoint).\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DATA_QUERY_KEY = [\"fluid\", \"app\"] as const;\n\n/**\n * Module-level persister instance (browser only).\n * Created once when the module loads so all calls to useFluidApp share\n * the same IndexedDB connection. Applied at the query level so it works\n * regardless of which QueryClient the app provides.\n */\nconst appDataPersister =\n typeof window !== \"undefined\" && !import.meta.env?.DEV\n ? createPersister()\n : undefined;\n\n/**\n * Hook to fetch the full portal app data from the fluidos API.\n *\n * Returns a `RepAppData` object containing:\n * - `screens` — all screen definitions with normalized component trees\n * - `profile.themes` — fully-transformed ThemeDefinition[] (handles legacy + new formats)\n * - `profile.activeThemeId` — the currently active theme ID\n * - `profile.navigation.navigation_items` — sorted, recursive navigation tree\n *\n * Uses IndexedDB persistence so subsequent page loads hydrate instantly\n * from cache while revalidating in the background. The raw API response\n * (plain JSON) is cached; Color objects are recreated from cache via\n * `select` on every restore — this is fast (CPU only, no network).\n *\n * @example\n * ```tsx\n * function App() {\n * const { data: appData, isLoading } = useFluidApp();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <AppShell\n * appData={appData}\n * navigation={appData.profile.navigation.navigation_items}\n * />\n * );\n * }\n * ```\n */\nexport function useFluidApp(options?: {\n enabled?: boolean;\n}): UseQueryResult<RepAppData> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n RepAppData,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => api.app.getRaw(),\n select: transformManifestToRepAppData,\n ...(appDataPersister && { persister: appDataPersister.persisterFn }),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useEffect } from \"react\";\n\nconst SPIN_STYLE_ID = \"fluid-app-shell-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Full-page loading spinner shown while AppShell is fetching app data\n * for the first time. Uses inline styles (not Tailwind) since the theme\n * hasn't loaded yet.\n */\nexport function AppShellLoading(): React.JSX.Element {\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n Loading...\n </p>\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ChevronRight } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n} from \"@fluid-app/ui-primitives\";\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport interface CollapsibleNavItemProps {\n item: NavigationItem;\n isActive: boolean;\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function CollapsibleNavItem({\n item,\n isActive,\n currentSlug,\n onNavigate,\n}: CollapsibleNavItemProps): React.JSX.Element {\n const [open, setOpen] = useState(isActive);\n\n useEffect(() => {\n if (isActive) setOpen(true);\n }, [isActive]);\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n asChild\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton>\n {item.icon && <RepIcon name={item.icon} />}\n <span className=\"truncate\">{item.label}</span>\n <ChevronRight className=\"ml-auto size-3 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => {\n const childSlug = normalizeSlug(child.slug);\n const childIsActive = normalizeSlug(currentSlug) === childSlug;\n\n return (\n <SidebarMenuItem key={child.slug || child.label}>\n <SidebarMenuButton\n isActive={childIsActive}\n onClick={() => onNavigate(childSlug)}\n >\n <RepIcon name={child.icon || \"file\"} className=\"h-3 w-3\" />\n <span>{child.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n );\n}\n","import {\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { isInSection } from \"@fluid-app/portal-core/navigation/utils\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { CollapsibleNavItem } from \"./CollapsibleNavItem\";\n\nexport interface SdkNavigationProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkNavigation({\n navItems,\n currentSlug,\n onNavigate,\n}: SdkNavigationProps): React.JSX.Element {\n return (\n <SidebarMenu>\n {navItems.map((item, index) => {\n // Skip items without slug AND without label\n if (!item.slug && !item.label) return null;\n\n const isActive = isInSection(item, currentSlug);\n const hasChildren = item.children?.length > 0;\n\n // Items with children — collapsible\n if (hasChildren) {\n return (\n <CollapsibleNavItem\n key={item.slug || item.label}\n item={item}\n isActive={isActive}\n currentSlug={currentSlug}\n onNavigate={onNavigate}\n />\n );\n }\n\n // Items without a slug are section headers\n if (!item.slug) {\n return (\n <SidebarGroupLabel key={item.id ?? `section-${index}`}>\n {item.label}\n </SidebarGroupLabel>\n );\n }\n\n // Leaf item — no children\n const itemSlug = normalizeSlug(item.slug);\n\n return (\n <SidebarMenuItem key={item.id ?? itemSlug}>\n <SidebarMenuButton\n isActive={isActive}\n onClick={() => onNavigate(itemSlug)}\n >\n {item.icon && <RepIcon name={item.icon} />}\n <span className=\"truncate\">{item.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenu>\n );\n}\n","import { useState, useRef, useEffect, useCallback } from \"react\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\n\nexport interface QuickLinksDropdownProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function QuickLinksDropdown({\n onNavigate,\n}: QuickLinksDropdownProps): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const panelRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const close = useCallback(() => setOpen(false), []);\n\n // Click-outside + Escape to dismiss\n useEffect(() => {\n if (!open) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n if (\n panelRef.current &&\n !panelRef.current.contains(e.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(e.target as Node)\n ) {\n close();\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n\n document.addEventListener(\"mousedown\", handleMouseDown);\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", handleMouseDown);\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [open, close]);\n\n const sections = getSystemNavigationBySection();\n\n const handleItemClick = (slug: string) => {\n onNavigate(slug);\n close();\n };\n\n return (\n <div className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={() => setOpen((prev) => !prev)}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label=\"Quick links\"\n aria-expanded={open}\n >\n <LayoutGrid className=\"h-4 w-4\" />\n </button>\n\n {open && (\n <div\n ref={panelRef}\n className=\"border-border bg-background absolute top-full right-0 z-50 mt-1 w-[700px] max-w-[90vw] overflow-hidden rounded-lg border shadow-lg\"\n >\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"bg-background flex items-center gap-2 border-b p-4\">\n <LayoutGrid className=\"text-muted-foreground h-5 w-5\" />\n <h3 className=\"text-foreground text-sm font-semibold\">\n Shortcuts\n </h3>\n </div>\n\n {/* Sections */}\n <div className=\"bg-muted space-y-6 p-6\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName} className=\"space-y-3\">\n <h3 className=\"text-foreground text-sm font-semibold\">\n {sectionName}\n </h3>\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => (\n <button\n key={item.slug}\n type=\"button\"\n onClick={() => handleItemClick(item.slug!)}\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground flex items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors\"\n >\n {item.icon && (\n <RepIcon\n name={item.icon}\n className=\"text-muted-foreground h-4 w-4 shrink-0\"\n />\n )}\n <span className=\"truncate\">{item.label}</span>\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\n\ninterface SdkMobileQuickLinksGridProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkMobileQuickLinksGrid({\n onNavigate,\n}: SdkMobileQuickLinksGridProps): React.JSX.Element {\n const sections = getSystemNavigationBySection();\n\n return (\n <div className=\"space-y-4\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName}>\n <h4 className=\"text-muted-foreground mb-2 text-xs font-semibold uppercase\">\n {sectionName}\n </h4>\n <div className=\"grid grid-cols-3 gap-2\">\n {items.map((item) => (\n <button\n key={item.slug}\n type=\"button\"\n onClick={() => {\n if (item.slug) onNavigate(item.slug);\n }}\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground flex h-auto flex-col items-center gap-1 rounded-lg p-2\"\n >\n {item.icon && <RepIcon name={item.icon} className=\"size-5\" />}\n <span className=\"text-xs\">{item.label}</span>\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","import { useState, useCallback } from \"react\";\nimport { X, Moon, Sun, LogOut, Smartphone } from \"lucide-react\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useRepUser } from \"@fluid-app/portal-widgets/contexts\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { SdkMobileQuickLinksGrid } from \"./SdkMobileQuickLinksGrid\";\n\nexport interface SdkMobileProfileMenuProps {\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nfunction getInitials(name: string | null | undefined): string {\n if (!name) return \"U\";\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n (parts[0]?.[0] ?? \"\") + (parts[parts.length - 1]?.[0] ?? \"\")\n ).toUpperCase();\n }\n return (name[0] ?? \"U\").toUpperCase();\n}\n\nexport function SdkMobileProfileMenu({\n onNavigate,\n onLogout,\n}: SdkMobileProfileMenuProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [isOpen, setIsOpen] = useState(false);\n const user = useRepUser();\n const themeMode = useThemeMode();\n\n const handleNavigate = useCallback(\n (slug: string) => {\n setIsOpen(false);\n onNavigate(slug);\n },\n [onNavigate],\n );\n\n if (!isMobile || !useBottomNav) return null;\n\n const initials = getInitials(user?.name);\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setIsOpen(true)}\n className=\"bg-background text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-full p-1\"\n aria-label=\"Open profile menu\"\n >\n {user?.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? \"User\"}\n className=\"size-8 rounded-full object-cover\"\n />\n ) : (\n <span className=\"flex size-8 items-center justify-center text-xs font-medium\">\n {initials}\n </span>\n )}\n </button>\n\n {isOpen && (\n <div className=\"animate-in fade-in slide-in-from-bottom-4 bg-background fixed inset-0 z-50 flex flex-col duration-200\">\n <div className=\"flex items-center justify-between px-4 py-3\">\n <span className=\"text-foreground text-lg font-semibold\">\n Profile\n </span>\n <button\n type=\"button\"\n onClick={() => setIsOpen(false)}\n className=\"text-muted-foreground rounded-full p-1\"\n aria-label=\"Close profile menu\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"flex-1 overflow-auto px-4 pb-[env(safe-area-inset-bottom)]\">\n {user && (\n <div className=\"border-border flex items-center gap-3 border-b py-4\">\n {user.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? \"User\"}\n className=\"size-10 rounded-full object-cover\"\n />\n ) : (\n <span className=\"bg-muted flex size-10 items-center justify-center rounded-full text-sm font-medium\">\n {initials}\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n {user.name && (\n <p className=\"text-foreground truncate text-sm font-semibold\">\n {user.name}\n </p>\n )}\n {user.email && (\n <p className=\"text-muted-foreground truncate text-xs\">\n {user.email}\n </p>\n )}\n </div>\n </div>\n )}\n\n <div className=\"border-border border-b py-4\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"app-download\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Smartphone className=\"size-5\" />\n <span>App Download</span>\n </button>\n </div>\n\n <div className=\"border-border border-b py-4\">\n <SdkMobileQuickLinksGrid onNavigate={handleNavigate} />\n </div>\n\n <div className=\"space-y-4 py-4\">\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n {themeMode.mode === \"dark\" ? (\n <Moon className=\"size-5\" />\n ) : (\n <Sun className=\"size-5\" />\n )}\n <span>\n Theme: {themeMode.mode === \"light\" ? \"Light\" : \"Dark\"}\n </span>\n </button>\n\n {onLogout && (\n <button\n type=\"button\"\n onClick={onLogout}\n className=\"text-destructive hover:bg-sidebar-accent flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <LogOut className=\"size-5\" />\n <span>Log out</span>\n </button>\n )}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { Menu, Moon, Sun } from \"lucide-react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { QuickLinksDropdown } from \"./QuickLinksDropdown\";\nimport { SdkMobileProfileMenu } from \"./SdkMobileProfileMenu\";\n\nexport interface SdkHeaderProps {\n mobileTabs?: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nexport function SdkHeader({\n mobileTabs,\n currentSlug,\n onNavigate,\n onLogout,\n}: SdkHeaderProps): React.JSX.Element {\n const sidebar = useSidebar();\n const themeMode = useThemeMode();\n\n const ThemeIcon = themeMode.mode === \"dark\" ? Moon : Sun;\n const isMobileWithBottomNav = sidebar.isMobile && sidebar.useBottomNav;\n\n return (\n <header className=\"bg-sidebar shrink-0\">\n {isMobileWithBottomNav ? (\n <div className=\"flex h-[52px] items-center px-4\">\n <div className=\"text-muted-foreground ml-auto flex flex-none items-center justify-end\">\n <SdkMobileProfileMenu onNavigate={onNavigate} onLogout={onLogout} />\n </div>\n </div>\n ) : (\n <div className=\"flex h-[52px] items-center gap-2 px-6\">\n {/* Mobile hamburger — only show if NOT using bottom nav */}\n {sidebar.isMobile && !sidebar.useBottomNav && (\n <button\n type=\"button\"\n onClick={sidebar.toggleSidebar}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground mr-2 flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label=\"Toggle sidebar\"\n >\n <Menu className=\"size-5\" />\n </button>\n )}\n\n {/* Spacer to push actions to the right */}\n <div className=\"flex-1\" />\n\n {/* Quick Links */}\n <QuickLinksDropdown onNavigate={onNavigate} />\n\n {/* Theme toggle */}\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ml-2 flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label={`Switch theme (current: ${themeMode.mode})`}\n >\n <ThemeIcon className=\"size-3\" />\n </button>\n </div>\n )}\n\n {/* Mobile child tabs — shown below header when parent has children */}\n {isMobileWithBottomNav && mobileTabs && mobileTabs.length > 0 && (\n <nav className=\"scrollbar-none flex items-center gap-1 overflow-x-auto px-4\">\n {mobileTabs.map((tab) => {\n const tabSlug = normalizeSlug(tab.slug);\n if (!tabSlug) return null;\n\n const isActive = normalizeSlug(currentSlug) === tabSlug;\n\n return (\n <button\n key={tab.id ?? tabSlug}\n type=\"button\"\n onClick={() => onNavigate(tabSlug)}\n className={`border-b-2 px-4 py-2.5 text-sm font-medium whitespace-nowrap transition-colors ${\n isActive\n ? \"border-primary text-foreground\"\n : \"text-muted-foreground hover:border-border hover:text-foreground border-transparent\"\n }`}\n >\n {tab.label}\n </button>\n );\n })}\n </nav>\n )}\n </header>\n );\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { storeToken, type FluidAuthConfig } from \"../auth\";\n\nexport interface UseCompanySwitchOptions {\n /** Auth config forwarded to storeToken so the JWT is written to the correct cookie. */\n config?: FluidAuthConfig;\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, stores the new JWT, then\n * reloads the page so all queries re-fetch with the new company context.\n *\n * Cache clearing prevents stale data from the previous company being\n * rehydrated from the IndexedDB persisted cache after the page reloads.\n *\n * @param options - Optional config; pass `config` so that `storeToken`\n * writes the JWT to the correct cookie when a custom `cookieKey` is used.\n */\nexport function useCompanySwitch(options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const api = useFluidApi();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n // Keep config in a ref so the mutation callback always reads the latest\n // value without needing it as a useCallback dependency (avoids unstable\n // switchCompany reference when callers pass an inline config object).\n const configRef = useRef(options?.config);\n configRef.current = options?.config;\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) => api.users.switchCompany(companyId),\n onSuccess: async (data) => {\n // Clear in-memory cache and IndexedDB before storing the new token\n // to prevent stale data rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n storeToken(data.company.jwt, configRef.current);\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","import { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport type { Company } from \"@fluid-app/auth\";\n\ninterface UseCompanySwitcherOptions {\n companies: Company[];\n activeCompany?: Company;\n isCreationPage?: boolean;\n onSwitch: (id: number) => void;\n}\n\nexport function useCompanySwitcher({\n companies,\n activeCompany,\n isCreationPage = false,\n onSwitch,\n}: UseCompanySwitcherOptions) {\n const [companySearch, setCompanySearch] = useState(\"\");\n const [isOpen, setIsOpen] = useState(false);\n const [switchingToId, setSwitchingToId] = useState<number | null>(null);\n const [showConfirmDialog, setShowConfirmDialog] = useState(false);\n const [pendingCompanyId, setPendingCompanyId] = useState<number | null>(null);\n const safetyResetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n // Reset switching state when active company changes\n useEffect(() => {\n setSwitchingToId(null);\n }, [activeCompany?.id]);\n\n // Clear safety-reset timer on unmount\n useEffect(() => {\n return () => {\n if (safetyResetTimerRef.current !== null) {\n clearTimeout(safetyResetTimerRef.current);\n }\n };\n }, []);\n\n const filteredCompanies = useMemo(() => {\n if (!companySearch) return companies;\n const term = companySearch.toLowerCase();\n return companies.filter((c) => c.name.toLowerCase().includes(term));\n }, [companies, companySearch]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const scheduleSafetyReset = useCallback(() => {\n if (safetyResetTimerRef.current !== null) {\n clearTimeout(safetyResetTimerRef.current);\n }\n safetyResetTimerRef.current = setTimeout(() => {\n setSwitchingToId(null);\n safetyResetTimerRef.current = null;\n }, 10000);\n }, []);\n\n const handleCompanySelect = useCallback(\n (id: number) => {\n if (id === activeCompany?.id) return;\n if (switchingToId !== null) return;\n\n if (isCreationPage) {\n close();\n setCompanySearch(\"\");\n setPendingCompanyId(id);\n setShowConfirmDialog(true);\n return;\n }\n\n // Proceed with switch\n setSwitchingToId(id);\n onSwitch(id);\n close();\n setCompanySearch(\"\");\n scheduleSafetyReset();\n },\n [\n activeCompany?.id,\n switchingToId,\n isCreationPage,\n onSwitch,\n close,\n scheduleSafetyReset,\n ],\n );\n\n const handleConfirmSwitch = useCallback(() => {\n if (pendingCompanyId === null) return;\n setSwitchingToId(pendingCompanyId);\n onSwitch(pendingCompanyId);\n close();\n setCompanySearch(\"\");\n setPendingCompanyId(null);\n scheduleSafetyReset();\n setShowConfirmDialog(false);\n }, [pendingCompanyId, onSwitch, close, scheduleSafetyReset]);\n\n const handleCancelSwitch = useCallback(() => {\n setPendingCompanyId(null);\n setShowConfirmDialog(false);\n setCompanySearch(\"\");\n }, []);\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n if (!open) {\n setCompanySearch(\"\");\n }\n }, []);\n\n return {\n companySearch,\n setCompanySearch,\n isOpen,\n setIsOpen: handleOpenChange,\n switchingToId,\n showConfirmDialog,\n setShowConfirmDialog,\n filteredCompanies,\n handleCompanySelect,\n handleConfirmSwitch,\n handleCancelSwitch,\n close,\n };\n}\n","import { useState } from \"react\";\nimport { Building2 } from \"lucide-react\";\n\nexport function CompanyImage({\n src,\n alt,\n size,\n}: {\n size: number;\n src: string;\n alt: string;\n}) {\n const [error, setError] = useState(false);\n\n if (!src || error)\n return (\n <Building2\n style={{ height: size, width: size }}\n className=\"text-muted-foreground/25\"\n />\n );\n\n return (\n <img\n className=\"max-h-8 max-w-8 object-contain\"\n alt={alt}\n src={src}\n width={size}\n height={size}\n onError={() => setError(true)}\n />\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyImage } from \"./CompanyImage\";\n\nexport function CompanyItem({\n logo_url,\n icon_url,\n name,\n compact = false,\n}: Omit<Company, \"id\"> & { compact?: boolean }) {\n const resolvedSrc = icon_url || logo_url || \"\";\n return (\n <>\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg\">\n <CompanyImage alt={name} src={resolvedSrc} size={32} />\n </div>\n <div\n className={cn(\n \"grid text-left text-sm leading-tight\",\n compact ? \"flex-shrink-0\" : \"flex-1\",\n )}\n >\n <span className=\"truncate font-semibold\">{name}</span>\n </div>\n </>\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyItem } from \"./CompanyItem\";\n\ninterface CompanyListProps {\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n}\n\nexport function CompanyList({\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n}: CompanyListProps) {\n if (!companies.length)\n return <h2 className=\"p-2 text-center text-sm\">No matches found</h2>;\n\n return (\n <>\n {companies.map((company) => {\n const isActive = company.id === activeCompany?.id;\n const isSwitching = company.id === switchingToId;\n const isDisabled = isActive || switchingToId !== null;\n\n return (\n <button\n type=\"button\"\n key={company.id}\n disabled={isDisabled}\n onClick={() => onItemClick(company.id)}\n className={cn(\n \"flex w-full items-center gap-2 p-2 transition-colors\",\n isDisabled\n ? \"cursor-not-allowed opacity-60\"\n : \"hover:bg-accent hover:text-accent-foreground cursor-pointer\",\n isActive && \"bg-accent text-accent-foreground\",\n isSwitching && \"bg-accent/50 animate-pulse\",\n )}\n >\n <CompanyItem\n logo_url={company.logo_url}\n icon_url={company.icon_url}\n name={company.name}\n />\n {isSwitching && (\n <span className=\"text-muted-foreground ml-auto text-xs\">\n Switching...\n </span>\n )}\n </button>\n );\n })}\n </>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { Company } from \"@fluid-app/company-switcher-core\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { CompanyList } from \"./CompanyList\";\n\ninterface CompanySwitcherDropdownProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n companySearch: string;\n onSearchChange: (value: string) => void;\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n trigger: ReactNode;\n}\n\nexport function CompanySwitcherDropdown({\n isOpen,\n onOpenChange,\n companySearch,\n onSearchChange,\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n trigger,\n}: CompanySwitcherDropdownProps) {\n return (\n <DropdownMenu open={isOpen} onOpenChange={onOpenChange}>\n <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"max-h-[90dvh] w-(--radix-dropdown-menu-trigger-width) min-w-56 overflow-y-auto rounded-lg\"\n align=\"start\"\n side=\"right\"\n sideOffset={4}\n >\n <Input\n type=\"text\"\n placeholder=\"Search Company\"\n value={companySearch}\n onChange={(e) => onSearchChange(e.target.value ?? \"\")}\n className=\"bg-card text-foreground placeholder:text-muted-foreground\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n Companies\n </DropdownMenuLabel>\n <CompanyList\n companies={companies}\n onItemClick={onItemClick}\n activeCompany={activeCompany}\n switchingToId={switchingToId}\n />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { Company } from \"@fluid-app/company-switcher-core\";\nimport {\n Sheet,\n SheetContent,\n SheetTrigger,\n IconButton,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { X } from \"lucide-react\";\nimport { CompanyList } from \"./CompanyList\";\n\ninterface CompanySwitcherSheetProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n companySearch: string;\n onSearchChange: (value: string) => void;\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n trigger: ReactNode;\n}\n\nexport function CompanySwitcherSheet({\n isOpen,\n onOpenChange,\n companySearch,\n onSearchChange,\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n trigger,\n}: CompanySwitcherSheetProps) {\n return (\n <Sheet open={isOpen} onOpenChange={onOpenChange}>\n <SheetTrigger asChild>{trigger}</SheetTrigger>\n <SheetContent\n side=\"left\"\n className=\"bg-card w-full p-0 [&>button]:hidden\"\n >\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <div className=\"border-border flex items-center justify-between border-b p-4\">\n <h2 className=\"text-foreground text-lg font-semibold\">\n Select Company\n </h2>\n <IconButton\n icon={X}\n onClick={() => onOpenChange(false)}\n className=\"text-muted-foreground hover:bg-muted hover:text-foreground h-8 w-8 bg-transparent\"\n aria-label=\"Close\"\n />\n </div>\n\n {/* Search */}\n <div className=\"border-border border-b p-4\">\n <Input\n type=\"text\"\n placeholder=\"Search Company\"\n value={companySearch}\n onChange={(e) => onSearchChange(e.target.value ?? \"\")}\n className=\"bg-card text-foreground placeholder:text-muted-foreground\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n </div>\n\n {/* Companies List */}\n <div className=\"flex-1 overflow-y-auto p-2\">\n <div className=\"mb-2 px-2\">\n <h3 className=\"text-muted-foreground text-xs\">Companies</h3>\n </div>\n <CompanyList\n companies={companies}\n onItemClick={onItemClick}\n activeCompany={activeCompany}\n switchingToId={switchingToId}\n />\n </div>\n </div>\n </SheetContent>\n </Sheet>\n );\n}\n","import { useRef } from \"react\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogMedia,\n AlertDialogTitle,\n} from \"@fluid-app/ui-primitives\";\nimport { AlertTriangle } from \"lucide-react\";\n\ninterface CompanySwitcherConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function CompanySwitcherConfirmDialog({\n open,\n onOpenChange,\n onConfirm,\n onCancel,\n}: CompanySwitcherConfirmDialogProps) {\n const confirmedRef = useRef(false);\n\n return (\n <AlertDialog\n open={open}\n onOpenChange={(isOpen) => {\n onOpenChange(isOpen);\n if (!isOpen && !confirmedRef.current) {\n onCancel();\n }\n if (!isOpen) confirmedRef.current = false;\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogMedia>\n <AlertTriangle className=\"text-amber-500\" />\n </AlertDialogMedia>\n <AlertDialogTitle>Switch Company?</AlertDialogTitle>\n <AlertDialogDescription>\n Switching companies will discard any unsaved changes. Are you sure\n you want to continue?\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={() => {\n confirmedRef.current = true;\n onConfirm();\n }}\n >\n Confirm\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n );\n}\n","import type { ReactNode } from \"react\";\nimport {\n useCompanySwitcher,\n type CompanySwitcherProps,\n} from \"@fluid-app/company-switcher-core\";\nimport { CompanySwitcherDropdown } from \"./CompanySwitcherDropdown\";\nimport { CompanySwitcherSheet } from \"./CompanySwitcherSheet\";\nimport { CompanySwitcherConfirmDialog } from \"./CompanySwitcherConfirmDialog\";\nimport { CompanyItem } from \"./CompanyItem\";\nimport { ChevronDown, ChevronRight } from \"lucide-react\";\n\ninterface CompanySwitcherComponentProps extends CompanySwitcherProps {\n trigger?: ReactNode;\n}\n\nexport function CompanySwitcher({\n companies,\n activeCompany,\n show,\n isMobile,\n isCreationPage,\n onSwitch,\n trigger,\n}: CompanySwitcherComponentProps) {\n const switcher = useCompanySwitcher({\n companies,\n activeCompany,\n isCreationPage,\n onSwitch,\n });\n\n if (!show) return null;\n\n const defaultTrigger = (\n <button\n type=\"button\"\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground flex w-full items-center gap-2 rounded-md p-2 text-left\"\n >\n <CompanyItem\n logo_url={activeCompany?.logo_url}\n icon_url={activeCompany?.icon_url}\n name={activeCompany?.name || \"\"}\n compact={isMobile}\n />\n {isMobile ? (\n <ChevronRight className=\"chevron-icons text-muted-foreground ml-2 h-3 w-3\" />\n ) : (\n <ChevronDown className=\"chevron-icons text-muted-foreground ml-auto h-3 w-3\" />\n )}\n </button>\n );\n\n const triggerElement = trigger ?? defaultTrigger;\n\n return (\n <>\n {isMobile ? (\n <CompanySwitcherSheet\n isOpen={switcher.isOpen}\n onOpenChange={switcher.setIsOpen}\n companySearch={switcher.companySearch}\n onSearchChange={switcher.setCompanySearch}\n companies={switcher.filteredCompanies}\n activeCompany={activeCompany}\n onItemClick={switcher.handleCompanySelect}\n switchingToId={switcher.switchingToId}\n trigger={triggerElement}\n />\n ) : (\n <CompanySwitcherDropdown\n isOpen={switcher.isOpen}\n onOpenChange={switcher.setIsOpen}\n companySearch={switcher.companySearch}\n onSearchChange={switcher.setCompanySearch}\n companies={switcher.filteredCompanies}\n activeCompany={activeCompany}\n onItemClick={switcher.handleCompanySelect}\n switchingToId={switcher.switchingToId}\n trigger={triggerElement}\n />\n )}\n\n <CompanySwitcherConfirmDialog\n open={switcher.showConfirmDialog}\n onOpenChange={switcher.setShowConfirmDialog}\n onConfirm={switcher.handleConfirmSwitch}\n onCancel={switcher.handleCancelSwitch}\n />\n </>\n );\n}\n","import { Skeleton } from \"@fluid-app/ui-primitives\";\n\nexport function CompanySwitcherSkeleton({ show }: { show: boolean }) {\n if (!show) return null;\n\n return (\n <div className=\"flex items-center gap-4 p-2\">\n <Skeleton className=\"size-5 shrink-0 rounded-sm bg-current\" />\n <Skeleton className=\"h-4 w-36 bg-current\" />\n </div>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\nimport { useCompanySwitch } from \"../hooks/use-company-switch\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n CompanySwitcher,\n CompanySwitcherSkeleton,\n} from \"@fluid-app/company-switcher-ui\";\nimport type { Company } from \"@fluid-app/auth\";\n\n/**\n * Company switcher wired to the portal SDK's data layer.\n * Renders in the sidebar header slot; returns null for single-company users.\n */\nexport function SdkCompanySwitcher(): React.JSX.Element | null {\n const { data: user, isLoading } = useCurrentUser();\n const { switchCompany } = useCompanySwitch();\n const { isMobile } = useSidebar();\n\n const companies: Company[] = useMemo(\n () =>\n user?.companies?.map((c) => ({\n id: c.id,\n name: c.name,\n logo_url: c.logo_url ?? null,\n icon_url: c.icon_url ?? null,\n })) ?? [],\n [user?.companies],\n );\n\n // The API returns the active company as `user.company` (nested object),\n // not as a top-level `company_id`. Match by `company.id`.\n const activeCompanyId = user?.company?.id;\n const activeCompany = useMemo(\n () => companies.find((c) => c.id === activeCompanyId),\n [companies, activeCompanyId],\n );\n\n if (isLoading) {\n return <CompanySwitcherSkeleton show />;\n }\n\n if (companies.length <= 1) {\n return null;\n }\n\n return (\n <CompanySwitcher\n companies={companies}\n activeCompany={activeCompany}\n show\n isMobile={isMobile}\n onSwitch={switchCompany}\n />\n );\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\n\nexport const ACCOUNT_MANAGE_SLUGS = [\n \"profile\",\n \"orders\",\n \"subscriptions\",\n] as const satisfies readonly SystemNavigationSlug[];\n\nexport type AccountManageSlug = (typeof ACCOUNT_MANAGE_SLUGS)[number];\n\nexport interface AccountManageTab {\n key: AccountManageSlug;\n label: string;\n slug: string;\n}\n\n/** Account manage tabs with labels derived from SYSTEM_NAVIGATION_ITEMS. */\nexport const ACCOUNT_MANAGE_TABS: AccountManageTab[] = ACCOUNT_MANAGE_SLUGS.map(\n (slug) => ({\n key: slug,\n label: SYSTEM_NAVIGATION_ITEMS[slug].label,\n slug,\n }),\n);\n","/**\n * Data Source Presets Registry\n *\n * Configurable presets that allow users to select options via sub-menus\n * and fine-tune parameters in a detail panel.\n */\n\nimport type {\n SelectFieldSchema,\n NumberFieldSchema,\n} from \"../registries/property-schema-types\";\nimport type { ApiDataSource, DataSource } from \"./types\";\n\n/** Widget types that can use data sources */\nexport type DataSourceCompatibleWidget =\n | \"CarouselWidget\"\n | \"ChartWidget\"\n | \"EmbedWidget\"\n | \"ImageWidget\"\n | \"ListWidget\"\n | \"NestedWidget\"\n | \"TableWidget\"\n | \"VideoWidget\";\n\n/** Field types supported in preset config panels */\nexport type PresetConfigField = SelectFieldSchema<string> | NumberFieldSchema;\n\n/** A preset data source configuration */\nexport interface DataSourcePreset {\n /** Unique identifier for the preset */\n id: string;\n /** Human-readable name for display */\n name: string;\n /** Description of what data this endpoint provides */\n description: string;\n /** The API endpoint path (can include {variable} placeholders) */\n endpoint: string;\n /** Transformer name to apply to the data */\n transform?: string;\n /** Optional path to extract data from response (e.g., \"data.items\") */\n resultPath?: string;\n /** Which widgets this preset is compatible with */\n compatibleWidgets: DataSourceCompatibleWidget[];\n /** Optional select field shown as a sub-menu. The field's key becomes a variable in the endpoint URL. */\n menuField?: SelectFieldSchema<string>;\n /** Optional config fields rendered in the detail panel below the popover. Each field's key becomes a variable. */\n configFields?: PresetConfigField[];\n}\n\n/**\n * Pre-configured data source presets\n */\nexport const DATA_SOURCE_PRESETS: DataSourcePreset[] = [\n {\n id: \"rep-most-shared\",\n name: \"Most Shared\",\n description: \"Most shared content\",\n endpoint:\n \"/v2025-06/reps/{rep_id}/most_shared?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}\",\n resultPath: \"resources\",\n compatibleWidgets: [\n \"CarouselWidget\",\n \"ListWidget\",\n \"NestedWidget\",\n \"TableWidget\",\n ],\n menuField: {\n key: \"shareable_type\",\n label: \"Content Type\",\n type: \"select\",\n options: [\n { label: \"Products\", value: \"products\" },\n { label: \"Media\", value: \"media\" },\n { label: \"Pages\", value: \"pages\" },\n { label: \"Libraries\", value: \"libraries\" },\n { label: \"Enrollment Packs\", value: \"enrollment_packs\" },\n { label: \"Promotions\", value: \"promotions\" },\n ],\n defaultValue: \"products\",\n },\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 20,\n description: \"Maximum number of items to show\",\n },\n {\n key: \"period\",\n label: \"Time Range\",\n type: \"select\",\n options: [\n { label: \"7 days\", value: \"7d\" },\n { label: \"30 days\", value: \"30d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"1 year\", value: \"1y\" },\n { label: \"All time\", value: \"all\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"rep-most-viewed\",\n name: \"Most Viewed\",\n description: \"Most viewed content\",\n endpoint:\n \"/v2025-06/reps/{rep_id}/most_viewed?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}\",\n resultPath: \"resources\",\n compatibleWidgets: [\n \"CarouselWidget\",\n \"ListWidget\",\n \"NestedWidget\",\n \"TableWidget\",\n ],\n menuField: {\n key: \"shareable_type\",\n label: \"Content Type\",\n type: \"select\",\n options: [\n { label: \"Products\", value: \"products\" },\n { label: \"Media\", value: \"media\" },\n { label: \"Pages\", value: \"pages\" },\n { label: \"Libraries\", value: \"libraries\" },\n { label: \"Enrollment Packs\", value: \"enrollment_packs\" },\n { label: \"Promotions\", value: \"promotions\" },\n ],\n defaultValue: \"products\",\n },\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 20,\n description: \"Maximum number of items to show\",\n },\n {\n key: \"period\",\n label: \"Time Range\",\n type: \"select\",\n options: [\n { label: \"7 days\", value: \"7d\" },\n { label: \"30 days\", value: \"30d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"1 year\", value: \"1y\" },\n { label: \"All time\", value: \"all\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"customer-orders\",\n name: \"Orders\",\n description: \"Customer order history\",\n endpoint:\n \"/v202506/orders?customer_id={customer_id}&page[limit]={limit}&status={status}\",\n resultPath: \"orders\",\n transform: \"toOrderTableProps\",\n compatibleWidgets: [\"TableWidget\"],\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 50,\n description: \"Maximum number of orders to show\",\n },\n {\n key: \"status\",\n label: \"Status\",\n type: \"select\",\n options: [\n { label: \"All\", value: \"all\" },\n { label: \"Paid\", value: \"paid\" },\n { label: \"Unpaid\", value: \"unpaid\" },\n { label: \"Refunded\", value: \"refunded\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"customer-subscriptions\",\n name: \"Subscriptions\",\n description: \"Customer subscriptions\",\n // Subscriptions endpoint is unversioned (legacy API), orders uses v202506\n endpoint:\n \"/subscriptions?customer_id={customer_id}&per_page={limit}&status={status}\",\n resultPath: \"subscriptions\",\n transform: \"toSubscriptionTableProps\",\n compatibleWidgets: [\"TableWidget\"],\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 50,\n description: \"Maximum number of subscriptions to show\",\n },\n {\n key: \"status\",\n label: \"Status\",\n type: \"select\",\n options: [\n { label: \"All\", value: \"all\" },\n { label: \"Active\", value: \"active\" },\n { label: \"Paused\", value: \"paused\" },\n { label: \"Cancelled\", value: \"cancelled\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n];\n\n/** Lookup map for O(1) preset resolution */\nconst PRESET_MAP = new Map<string, DataSourcePreset>(\n DATA_SOURCE_PRESETS.map((p) => [p.id, p]),\n);\n\n/**\n * Resolves a preset by ID, returning the current preset definition.\n * Returns undefined if the preset ID is not recognized.\n */\nexport function resolvePreset(presetId: string): DataSourcePreset | undefined {\n return PRESET_MAP.get(presetId);\n}\n\n/**\n * If the source is an API source with a presetId, returns a copy with\n * endpoint, resultPath, and transform resolved from the current preset.\n * Falls back to stored values if the preset is unknown.\n * Non-API sources are returned as-is.\n */\nexport function resolveSource<T extends DataSource>(source: T): T {\n if (source.type !== \"api\" || !source.presetId) return source;\n\n const preset = resolvePreset(source.presetId);\n if (!preset) {\n console.warn(\n `[DataSource] Unknown preset \"${source.presetId}\", falling back to stored values`,\n );\n return source;\n }\n\n return {\n ...source,\n endpoint: preset.endpoint,\n resultPath: preset.resultPath,\n transform: preset.transform ?? source.transform,\n } as T;\n}\n","import { useQueries } from \"@tanstack/react-query\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type {\n DataSourceRegistry,\n WidgetDataResult,\n DataSourceContext,\n DataSource,\n} from \"@fluid-app/portal-core/data-sources/types\";\nimport { resolveSource } from \"@fluid-app/portal-core/data-sources/presets\";\nimport { useDataSourceRegistryConfig } from \"./registry-context\";\n\ninterface UseWidgetDataOptions {\n /** Override the default registry from context */\n registry?: DataSourceRegistry | undefined;\n /** Base URL for API calls (e.g., \"https://api.fluid.app/api\") */\n baseUrl?: string | undefined;\n}\n\n/**\n * Generates a cache key for the raw data fetch (endpoint/items identity only).\n * Transforms and targetProps are NOT included — they are applied per-widget\n * after reading from the shared cache.\n */\nfunction getSourceKey(source: DataSource): string {\n if (source.type === \"api\") {\n const varsKey = source.variables\n ? `:${JSON.stringify(source.variables)}`\n : \"\";\n // Use presetId as identity when available — keeps cache key stable across preset endpoint changes\n const identity = source.presetId ?? source.endpoint;\n return `${identity}${varsKey}`;\n }\n if (source.type === \"custom\") {\n const itemKeys =\n source.selectedItems?.map((item) => {\n return `${item.shareableType}:${item.id}`;\n }) ?? [];\n return `custom:${itemKeys.join(\",\")}`;\n }\n if (source.type === \"static\") {\n return `static:${source.staticType}:${source.selectedId}`;\n }\n return \"unknown\";\n}\n\n/**\n * Resolves cached raw data into widget props by applying each widget's own\n * transforms and targetProps mapping.\n */\nfunction resolvePropsFromQueries<T>(\n queryResults: ReadonlyArray<{\n data?: unknown;\n isLoading: boolean;\n error: Error | null;\n }>,\n sources: DataSource[],\n registry: DataSourceRegistry,\n errorConfig?: { fallback?: unknown },\n): T | undefined {\n if (sources.length === 0) return undefined;\n if (queryResults.some((q) => q.isLoading && !q.data)) return undefined;\n\n const failedQuery = queryResults.find((q) => q.error);\n const hasError = !!failedQuery?.error;\n\n if (hasError && errorConfig?.fallback) {\n return errorConfig.fallback as T;\n }\n\n if (hasError) {\n return undefined;\n }\n\n const resolvedProps: Record<string, unknown> = {};\n\n for (let i = 0; i < queryResults.length; i++) {\n const query = queryResults[i];\n const source = sources[i];\n if (!query?.data || !source) continue;\n\n // Sentry fix: FLUID-ADMIN-1DH — source or targetProps can be undefined from malformed widget config\n if (!source.targetProps) continue;\n\n // Apply this widget's transform to the cached raw data\n let result: unknown = query.data;\n if (source.transform) {\n const transformer = registry.transformers[source.transform];\n if (transformer) {\n result = transformer(result, source);\n } else {\n console.warn(`Transform \"${source.transform}\" not found in registry`);\n }\n }\n\n // Map to this widget's targetProps\n if (\n result !== null &&\n typeof result === \"object\" &&\n !Array.isArray(result) &&\n source.targetProps.length > 1\n ) {\n const resultObj = result as Record<string, unknown>;\n for (const prop of source.targetProps) {\n if (prop in resultObj) {\n resolvedProps[prop] = resultObj[prop];\n }\n }\n } else {\n for (const prop of source.targetProps) {\n resolvedProps[prop] = result;\n }\n }\n }\n\n return resolvedProps as T;\n}\n\n/**\n * Hook that fetches and resolves data sources for a widget using React Query.\n *\n * Raw API data is cached and shared across widgets with the same data source.\n * Transforms and targetProps mapping are applied per-widget after cache reads,\n * so multiple widgets can share one fetch but resolve data independently.\n */\nexport function useWidgetData<T = Record<string, unknown>>(\n widget: WidgetSchema,\n options?: UseWidgetDataOptions,\n): WidgetDataResult<T> {\n const config = useDataSourceRegistryConfig();\n const registry = options?.registry ?? config.registry;\n const baseUrl = options?.baseUrl ?? config.baseUrl;\n const getApiHeaders = config.getApiHeaders;\n const variables = config.variables;\n\n // Resolve preset-backed sources to their current endpoint/resultPath/transform\n const sources = useMemo(\n () => (widget.dataSource?.sources ?? []).map(resolveSource),\n [widget.dataSource?.sources],\n );\n const errorConfig = widget.dataSource?.error;\n const widgetId = widget.id ?? \"unknown\";\n const widgetType = widget.type;\n\n const queryResultsRef = useRef<Array<{ refetch: () => void }>>([]);\n\n // Cache only raw fetched data — no transforms or source objects stored\n const results = useQueries({\n queries: sources.map((source: DataSource) => ({\n queryKey: [\n \"portal-widget-data\",\n getSourceKey(source),\n baseUrl,\n variables,\n ] as const,\n queryFn: async ({\n signal,\n }: {\n signal: AbortSignal;\n }): Promise<unknown> => {\n const context: DataSourceContext = {\n widgetId,\n widgetType,\n signal,\n baseUrl,\n getApiHeaders,\n variables,\n };\n\n const fetcher = registry.fetchers[source.type];\n if (!fetcher) {\n throw new Error(\n `No fetcher registered for source type: ${source.type}`,\n );\n }\n\n return fetcher(source, context);\n },\n enabled: sources.length > 0,\n retry: errorConfig?.retryCount ?? 0,\n retryDelay: errorConfig?.retryDelay ?? 1000,\n refetchInterval: source.refreshInterval || false,\n })),\n });\n\n // Update ref for refetch callback\n queryResultsRef.current = results;\n\n // Aggregate loading state\n const isLoading = results.some((q) => q.isLoading);\n\n // Get first error from any failed query\n const failedQuery = results.find((q) => q.error);\n const error =\n failedQuery?.error instanceof Error\n ? failedQuery.error\n : failedQuery?.error\n ? new Error(String(failedQuery.error))\n : null;\n\n // Stable fingerprint that changes only when query data or loading states change\n const queryFingerprint = results\n .map((r) => `${r.dataUpdatedAt}:${r.isLoading}`)\n .join(\",\");\n\n // Resolve data: apply per-widget transforms and map to targetProps\n const resultsRef = useRef(results);\n resultsRef.current = results;\n\n const data = useMemo(() => {\n const querySnapshots = resultsRef.current.map((q) => ({\n data: q.data,\n isLoading: q.isLoading,\n error: q.error instanceof Error ? q.error : null,\n }));\n return resolvePropsFromQueries<T>(\n querySnapshots,\n sources,\n registry,\n errorConfig,\n );\n }, [queryFingerprint, sources, registry, errorConfig]);\n\n // Stable refetch callback using ref\n const refetch = useCallback(() => {\n queryResultsRef.current.forEach((q) => q.refetch());\n }, []);\n\n return {\n data,\n isLoading,\n error,\n refetch,\n };\n}\n","import type React from \"react\";\n\nexport function ErrorState(): React.JSX.Element {\n return (\n <div className=\"flex min-h-[120px] flex-col items-center justify-center p-6 text-center\">\n <p className=\"text-lg font-semibold\">Something Went Wrong</p>\n <p className=\"text-muted-foreground text-sm\">\n Please contact a company admin for help\n </p>\n </div>\n );\n}\n","import type React from \"react\";\nimport { memo, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport { useWidgetData } from \"./use-widget-data\";\nimport { ErrorState } from \"./ErrorState\";\n\nexport interface DataAwareWidgetProps {\n widget: WidgetSchema;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Component: ComponentType<any>;\n /** Additional props to pass through (e.g., edit mode callbacks) */\n additionalProps?: Record<string, unknown>;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom error component */\n errorComponent?: (error: Error) => ReactNode;\n /** Base URL for API calls (e.g., \"https://api.fluid.app/api\") */\n baseUrl?: string | undefined;\n}\n\n// Empty object constant to avoid creating new references\nconst EMPTY_OBJECT: Record<string, unknown> = {};\n\n/**\n * Wrapper component that resolves data sources before rendering the widget.\n * Merges resolved data with static props.\n */\nfunction DataAwareWidgetImpl({\n widget,\n Component,\n additionalProps,\n loadingComponent,\n errorComponent,\n baseUrl,\n}: DataAwareWidgetProps): React.JSX.Element | null {\n // Use stable empty object if no additionalProps provided\n const stableAdditionalProps = additionalProps ?? EMPTY_OBJECT;\n const { data, isLoading, error } = useWidgetData(widget, { baseUrl });\n\n // Merge static props with resolved data\n // Data takes precedence (allows overriding defaults)\n // Memoized to prevent unnecessary child re-renders\n // Note: Must be called before any early returns to satisfy Rules of Hooks\n const mergedProps = useMemo(\n () => ({\n ...widget.props,\n ...data,\n ...stableAdditionalProps,\n }),\n [widget.props, data, stableAdditionalProps],\n );\n\n // Show loading state\n if (isLoading && widget.dataSource?.loading?.showSkeleton !== false) {\n if (loadingComponent) {\n return <>{loadingComponent}</>;\n }\n // Default skeleton - can be customized\n return (\n <div\n className=\"bg-muted animate-pulse rounded-md\"\n style={{ minHeight: 100 }}\n >\n <span className=\"sr-only\">Loading...</span>\n </div>\n );\n }\n\n // Show error state (if no fallback was applied)\n if (error && !data) {\n if (errorComponent) {\n return <>{errorComponent(error)}</>;\n }\n // Default error display\n return <ErrorState />;\n }\n\n return <Component {...mergedProps} />;\n}\n\n/**\n * Memoized wrapper component that resolves data sources before rendering the widget.\n * Prevents re-renders when parent re-renders but props haven't changed.\n */\nexport const DataAwareWidget: React.NamedExoticComponent<DataAwareWidgetProps> =\n memo(DataAwareWidgetImpl);\n","/**\n * BuilderScreenView — renders a builder screen's component_tree with\n * DataAwareWidget support for data-bound widgets.\n *\n * Uses the shared ScreenRenderer from portal-widgets under the hood,\n * but wraps data-bound widgets with DataAwareWidget from portal-core.\n */\n\nimport React, { memo } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { ScreenDefinition } from \"@fluid-app/portal-core/types\";\nimport { DataAwareWidget } from \"@fluid-app/portal-react/data-sources/DataAwareWidget\";\nimport { useRegistry } from \"@fluid-app/portal-widgets/contexts\";\n\ninterface WidgetRendererProps {\n widget: WidgetSchema;\n index: number;\n}\n\n/**\n * Renders a single widget, wrapping with DataAwareWidget if it has a dataSource.\n */\nfunction WidgetRenderer({ widget, index }: WidgetRendererProps) {\n const registry = useRegistry();\n const Component = registry[widget.type];\n\n if (!Component) {\n console.warn(\n `[BuilderScreenView] Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n if (widget.dataSource) {\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <DataAwareWidget widget={widget} Component={Component} />\n </div>\n );\n }\n\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <Component {...widget.props} />\n </div>\n );\n}\n\nexport interface BuilderScreenViewProps {\n /** The screen definition to render */\n screen: ScreenDefinition;\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\n/**\n * Renders a builder screen's component_tree with full data source support.\n * Widgets with `dataSource` config are automatically wrapped with `DataAwareWidget`\n * which fetches data and merges it with static props before rendering.\n */\nfunction BuilderScreenViewImpl({\n screen,\n className,\n}: BuilderScreenViewProps): React.JSX.Element | null {\n const widgets = screen.component_tree;\n\n if (!widgets || widgets.length === 0) {\n return (\n <div className={className ?? \"h-full w-full\"}>\n <div className=\"text-muted-foreground flex h-full items-center justify-center\">\n <p>This screen has no content yet.</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className ?? \"h-full w-full\"}>\n {widgets.map((widget, index) => {\n if (!widget) return null;\n return (\n <WidgetRenderer\n key={widget.id ?? index}\n widget={widget}\n index={index}\n />\n );\n })}\n </div>\n );\n}\n\nexport const BuilderScreenView: React.MemoExoticComponent<\n typeof BuilderScreenViewImpl\n> = memo(BuilderScreenViewImpl);\n","import { PackageOpen, Repeat, User, type LucideIcon } from \"lucide-react\";\nimport {\n ACCOUNT_MANAGE_TABS,\n type AccountManageSlug,\n} from \"@fluid-app/portal-core/navigation/account-manage\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\n\nconst ICON_MAP: Record<AccountManageSlug, LucideIcon> = {\n profile: User,\n orders: PackageOpen,\n subscriptions: Repeat,\n};\n\nconst NAV_ITEMS = ACCOUNT_MANAGE_TABS.map((tab) => ({\n ...tab,\n icon: ICON_MAP[tab.key],\n}));\n\nfunction getActiveTab(slug: string): AccountManageSlug {\n const root = slug.split(\"/\")[0];\n if (root === \"orders\") return \"orders\";\n if (root === \"subscriptions\") return \"subscriptions\";\n return \"profile\";\n}\n\nexport function AccountManageLayout({\n children,\n}: {\n children: React.ReactNode;\n}): React.JSX.Element {\n const { currentSlug, navigate } = useAppNavigation();\n const activeTab = getActiveTab(currentSlug);\n\n return (\n <div className=\"flex flex-col md:flex-row\">\n {/* Mobile: horizontal tab bar */}\n <nav className=\"flex gap-1 overflow-x-auto border-b px-4 py-2 md:hidden\">\n {NAV_ITEMS.map(({ key, slug, label, icon: Icon }) => (\n <button\n key={key}\n type=\"button\"\n onClick={() => navigate(slug)}\n className={`flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium whitespace-nowrap ${\n activeTab === key\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\"\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </nav>\n\n {/* Desktop: vertical sidebar */}\n <nav className=\"hidden w-48 shrink-0 flex-col gap-1 p-4 md:flex\">\n {NAV_ITEMS.map(({ key, slug, label, icon: Icon }) => (\n <button\n key={key}\n type=\"button\"\n onClick={() => navigate(slug)}\n className={`flex items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium ${\n activeTab === key\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\"\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </nav>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n}\n","import { lazy, type ComponentType } from \"react\";\n\nconst ProfileScreen = lazy(() =>\n import(\"../screens/ProfileScreen\").then((m) => ({\n default: m.ProfileScreen,\n })),\n);\nconst OrdersScreen = lazy(() =>\n import(\"../screens/OrdersScreen\").then((m) => ({\n default: m.OrdersScreen,\n })),\n);\nconst SubscriptionsScreen = lazy(() =>\n import(\"../screens/SubscriptionsScreen\").then((m) => ({\n default: m.SubscriptionsScreen,\n })),\n);\nconst MessagingScreen = lazy(() =>\n import(\"../screens/MessagingScreen\").then((m) => ({\n default: m.MessagingScreen,\n })),\n);\nconst ContactsScreen = lazy(() =>\n import(\"../screens/ContactsScreen\").then((m) => ({\n default: m.ContactsScreen,\n })),\n);\nconst ShopScreen = lazy(() =>\n import(\"../screens/ShopScreen\").then((m) => ({ default: m.ShopScreen })),\n);\nconst CustomersScreen = lazy(() =>\n import(\"../screens/CustomersScreen\").then((m) => ({\n default: m.CustomersScreen,\n })),\n);\nconst ProductsScreen = lazy(() =>\n import(\"../screens/ProductsScreen\").then((m) => ({\n default: m.ProductsScreen,\n })),\n);\nconst ShareablesScreen = lazy(() =>\n import(\"../screens/ShareablesScreen\").then((m) => ({\n default: m.ShareablesScreen,\n })),\n);\nconst MySiteScreen = lazy(() =>\n import(\"../screens/MySiteScreen\").then((m) => ({\n default: m.MySiteScreen,\n })),\n);\nconst UpgradeScreen = lazy(() =>\n import(\"../screens/UpgradeScreen\").then((m) => ({\n default: m.UpgradeScreen,\n })),\n);\nconst AppDownloadScreen = lazy(() =>\n import(\"../screens/AppDownloadScreen\").then((m) => ({\n default: m.AppDownloadScreen,\n })),\n);\n\nexport const SYSTEM_SLUG_SCREEN_MAP: Record<string, ComponentType> = {\n profile: ProfileScreen,\n orders: OrdersScreen,\n subscriptions: SubscriptionsScreen,\n messages: MessagingScreen,\n contacts: ContactsScreen,\n shop: ShopScreen,\n customers: CustomersScreen,\n products: ProductsScreen,\n \"my-site\": MySiteScreen,\n \"share/products\": ShareablesScreen,\n \"share/product\": ShareablesScreen,\n \"share/media\": ShareablesScreen,\n \"share/playlists\": ShareablesScreen,\n \"share/playlist\": ShareablesScreen,\n \"share/files\": ShareablesScreen,\n upgrade: UpgradeScreen,\n \"app-download\": AppDownloadScreen,\n};\n\n/** Pre-sorted keys for greedy prefix matching (longest first). */\nconst SORTED_SCREEN_KEYS = Object.keys(SYSTEM_SLUG_SCREEN_MAP).sort(\n (a, b) => b.length - a.length,\n);\n\n/** Prefix-match a slug against screen map keys, longest match first. */\nexport function findScreenByPrefix(slug: string): ComponentType | undefined {\n for (const key of SORTED_SCREEN_KEYS) {\n if (slug === key || slug.startsWith(key + \"/\")) {\n return SYSTEM_SLUG_SCREEN_MAP[key];\n }\n }\n return undefined;\n}\n","import { Suspense, useMemo, type ComponentType } from \"react\";\nimport { isSystemNavigationItem } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ACCOUNT_MANAGE_SLUGS } from \"@fluid-app/portal-core/navigation/account-manage\";\nimport type { NavigationItem, ScreenDefinition } from \"../types/navigation\";\nimport { CoreScreenPlaceholder } from \"../screens/CoreScreenPlaceholder\";\nimport { BuilderScreenView } from \"./BuilderScreenView\";\nimport { AccountManageLayout } from \"../account/account-management-layout\";\nimport {\n SYSTEM_SLUG_SCREEN_MAP,\n findScreenByPrefix,\n} from \"./system-screen-map\";\n\nconst ACCOUNT_SLUGS = new Set<string>(ACCOUNT_MANAGE_SLUGS);\n\nexport interface PageRouterProps {\n currentSlug: string;\n currentNavItem?: NavigationItem | undefined;\n customPages?:\n | Record<string, ComponentType<{ slug?: string; params?: string }>>\n | undefined;\n /** Builder screen definitions (from fluidos API) */\n screens?: ScreenDefinition[] | undefined;\n baseSlug: string;\n restParams: string;\n}\n\nexport function PageRouter({\n currentSlug,\n currentNavItem,\n customPages,\n screens,\n baseSlug,\n restParams,\n}: PageRouterProps): React.JSX.Element {\n // Build a screen lookup map by ID for O(1) access\n const screenById = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.map((s) => [s.id, s]));\n }, [screens]);\n\n // Build a screen lookup map by slug for fallback resolution\n const screenBySlug = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.filter((s) => s.slug).map((s) => [s.slug, s]));\n }, [screens]);\n\n // Resolve builder screen: screen_id first, then slug fallback\n const builderScreen = useMemo(() => {\n if (currentNavItem?.screen_id && screenById) {\n const byId = screenById.get(currentNavItem.screen_id);\n if (byId) return byId;\n }\n if (screenBySlug) {\n return (\n screenBySlug.get(currentSlug) ?? screenBySlug.get(baseSlug) ?? undefined\n );\n }\n return undefined;\n }, [\n currentNavItem?.screen_id,\n screenById,\n screenBySlug,\n currentSlug,\n baseSlug,\n ]);\n\n // 1. Custom page match (takes priority over system defaults)\n const ExactCustomPage = customPages?.[currentSlug];\n if (ExactCustomPage) {\n return <ExactCustomPage slug={currentSlug} params={restParams} />;\n }\n\n // 2. Prefix custom page match (base slug with rest params)\n if (baseSlug !== currentSlug) {\n const PrefixCustomPage = customPages?.[baseSlug];\n if (PrefixCustomPage) {\n return <PrefixCustomPage slug={currentSlug} params={restParams} />;\n }\n }\n\n // 3. System navigation item with a dedicated screen component\n // First try exact match on baseSlug, then prefix-match currentSlug\n // against screen map keys (handles detail routes like \"share/product/123\"\n // matching \"share/product\" when the slug isn't a registered nav item).\n const SystemScreen =\n SYSTEM_SLUG_SCREEN_MAP[baseSlug] ?? findScreenByPrefix(currentSlug);\n if (SystemScreen) {\n const content = (\n <Suspense\n fallback={\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"border-primary h-8 w-8 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n }\n >\n <SystemScreen />\n </Suspense>\n );\n\n if (ACCOUNT_SLUGS.has(baseSlug)) {\n return <AccountManageLayout>{content}</AccountManageLayout>;\n }\n\n return content;\n }\n\n // 4. System slug without a dedicated screen (account, my-site, share/*)\n if (isSystemNavigationItem(baseSlug)) {\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? baseSlug} />;\n }\n\n // 5. Builder screen (by screen_id or slug match)\n if (builderScreen) {\n return <BuilderScreenView screen={builderScreen} />;\n }\n\n // 6. Fallback\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? currentSlug} />;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return [...slugs].sort((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that clears all client-side state:\n * - In-flight TanStack Query requests (cancelled)\n * - TanStack Query cache (in-memory)\n * - IndexedDB persisted query cache\n * - Auth tokens (cookies + localStorage)\n * - sessionStorage\n *\n * After clearing, invokes the provided callback or redirects to the given URL.\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { clearTokens } from \"../auth/token-storage\";\nimport { DEFAULT_AUTH_URL } from \"../auth/auth-redirect\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears all cached/persisted\n * client state and either redirects or calls the provided callback.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear auth tokens (cookies + localStorage)\n clearTokens();\n\n // 5. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 6. Post-logout action\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n // Default: redirect to the auth server with ?logout=true so it\n // destroys its session. Without this, the auth server's active\n // session would immediately re-authenticate and redirect back.\n const currentUrl = encodeURIComponent(window.location.href);\n window.location.href = `${DEFAULT_AUTH_URL}/?logout=true&redirect_url=${currentUrl}`;\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","import { useCallback, useState } from \"react\";\nimport { LogOut } from \"lucide-react\";\nimport {\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\n\nexport interface SdkLogoutButtonProps {\n onLogout: () => Promise<void>;\n}\n\n/**\n * Sidebar footer button that triggers the logout flow.\n * Rendered inside the AppShell sidebar footer slot.\n */\nexport function SdkLogoutButton({\n onLogout,\n}: SdkLogoutButtonProps): React.JSX.Element {\n const [isPending, setIsPending] = useState(false);\n\n const handleClick = useCallback(async () => {\n setIsPending(true);\n try {\n await onLogout();\n } catch (error) {\n console.error(\"[SdkLogoutButton] Logout failed:\", error);\n } finally {\n setIsPending(false);\n }\n }, [onLogout]);\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton onClick={handleClick} disabled={isPending}>\n <LogOut className=\"size-4\" />\n <span>{isPending ? \"Logging out\\u2026\" : \"Log out\"}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n","import {\n useState,\n useEffect,\n useCallback,\n useMemo,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport { AppShellLayout } from \"@fluid-app/portal-react/shell/AppShellLayout\";\nimport {\n ThemeModeProvider,\n type ThemeMode,\n} from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { getDefaultNavigation } from \"@fluid-app/portal-core/navigation/default-navigation\";\nimport { ScreenHeader } from \"@fluid-app/portal-react/shell/ScreenHeader\";\nimport { ScreenHeaderProvider } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { SdkBottomNav } from \"./SdkBottomNav\";\nimport type {\n RepAppData,\n ScreenDefinition,\n} from \"@fluid-app/portal-core/types\";\nimport {\n applyTheme,\n removeAllThemes,\n resolveTheme,\n} from \"@fluid-app/portal-core/theme\";\nimport { useFluidApp } from \"../hooks/use-fluid-app\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { AppShellLoading } from \"./AppShellLoading\";\nimport { SdkNavigation } from \"./SdkNavigation\";\nimport { SdkHeader } from \"./SdkHeader\";\nimport { SdkCompanySwitcher } from \"./SdkCompanySwitcher\";\nimport { PageRouter } from \"./PageRouter\";\nimport {\n collectNavSlugs,\n matchSlugPrefix,\n extractSlugFromPathname,\n} from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { AppNavigationProvider } from \"./AppNavigationContext\";\nimport { useLogout } from \"../hooks/use-logout\";\nimport { SdkLogoutButton } from \"./SdkLogoutButton\";\nimport { RepUserProvider } from \"@fluid-app/portal-widgets/contexts\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\n\nconst THEME_STORAGE_KEY = \"portal-theme-mode\";\n\nfunction getInitialThemeMode(): ThemeMode {\n if (typeof window === \"undefined\") return \"light\";\n const stored = localStorage.getItem(THEME_STORAGE_KEY);\n if (stored === \"light\" || stored === \"dark\") return stored;\n return \"light\";\n}\n\nfunction findFirstNavigableSlug(items: NavigationItem[]): string {\n for (const item of items) {\n if (item.slug) return normalizeSlug(item.slug);\n for (const child of item.children ?? []) {\n if (child.slug) return normalizeSlug(child.slug);\n }\n }\n return \"\";\n}\n\nfunction findCurrentSection(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n if (\n item.children?.some((child) => normalizeSlug(child.slug) === normalized)\n ) {\n return item;\n }\n }\n return undefined;\n}\n\nfunction findNavItem(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === normalized) return child;\n }\n }\n return undefined;\n}\n\nexport interface AppShellProps {\n /** Pre-fetched app data (skips internal useFluidApp call if provided) */\n appData?: RepAppData;\n /** Override navigation items (otherwise derived from appData/API) */\n navigation?: NavigationItem[];\n /** Custom page components keyed by slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** Base path for subpath deployments (e.g. \"/portal\"). Default: \"/\" */\n basePath?: string;\n /** Controlled current slug */\n currentSlug?: string;\n /** Navigation callback */\n onNavigate?: (slug: string) => void;\n /** Custom sidebar header slot */\n sidebarHeader?: ReactNode;\n /** Custom sidebar footer slot. When provided, replaces the default logout button. */\n sidebarFooter?: ReactNode;\n /**\n * Callback invoked after the user logs out and all client state has been cleared.\n * If not provided, a default logout button is still rendered, but no post-logout\n * action is taken (consumers should pair this with `logoutRedirectUrl`\n * or provide their own `sidebarFooter`).\n */\n onLogout?: () => void;\n /** URL to redirect to after logout (e.g. \"/login\"). Takes precedence over `onLogout`. */\n logoutRedirectUrl?: string;\n /** Render prop or static children for the content area */\n children?:\n | ReactNode\n | ((props: {\n currentSlug: string;\n currentNavItem: NavigationItem | undefined;\n }) => ReactNode);\n}\n\nexport function AppShell({\n appData: appDataProp,\n navigation: navigationProp,\n customPages,\n basePath = \"/\",\n currentSlug: controlledSlug,\n onNavigate: onNavigateProp,\n sidebarHeader,\n sidebarFooter,\n onLogout,\n logoutRedirectUrl,\n children,\n}: AppShellProps): React.JSX.Element {\n // Normalize basePath: ensure leading slash, no trailing slash\n const normalizedBasePath = useMemo(() => {\n const stripped = basePath.replace(/^\\/|\\/$/g, \"\");\n return stripped ? `/${stripped}` : \"/\";\n }, [basePath]);\n\n // Fetch full app data from fluidos API (disabled when appData is provided)\n const { data: fetchedAppData, isLoading } = useFluidApp({\n enabled: !appDataProp,\n });\n const appData = appDataProp ?? fetchedAppData;\n\n // Fetch current user for RepUserProvider\n const { data: currentUser } = useCurrentUser();\n const repUser = useMemo(\n () => ({\n name:\n currentUser?.first_name && currentUser?.last_name\n ? `${currentUser.first_name} ${currentUser.last_name}`\n : null,\n email: currentUser?.email ?? null,\n imageUrl: currentUser?.image_url ?? null,\n publicId: currentUser?.public_id,\n }),\n [\n currentUser?.first_name,\n currentUser?.last_name,\n currentUser?.email,\n currentUser?.image_url,\n currentUser?.public_id,\n ],\n );\n\n // Resolve the active theme from app data\n const activeTheme = useMemo(() => {\n if (!appData?.profile?.themes?.length) return undefined;\n const themes = appData.profile.themes;\n const activeId = appData.profile.activeThemeId;\n return activeId\n ? (themes.find((t) => t.id === activeId) ?? themes[0])\n : themes[0];\n }, [appData?.profile?.themes, appData?.profile?.activeThemeId]);\n\n // Apply theme CSS via the shared theme pipeline\n const resolvedTheme = useMemo(() => {\n if (!activeTheme) return undefined;\n return resolveTheme(activeTheme);\n }, [activeTheme]);\n\n useEffect(() => {\n if (!resolvedTheme) return;\n applyTheme(resolvedTheme);\n return () => {\n removeAllThemes();\n };\n }, [resolvedTheme]);\n\n // Mirror data-theme and data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n if (activeTheme?.id) {\n root.setAttribute(\"data-theme\", activeTheme.id);\n } else {\n root.removeAttribute(\"data-theme\");\n }\n return () => {\n root.removeAttribute(\"data-theme\");\n };\n }, [activeTheme?.id]);\n\n // Resolve navigation items\n const navItems = useMemo(() => {\n if (navigationProp) return navigationProp;\n const profileNav = appData?.profile?.navigation?.navigation_items;\n if (profileNav && profileNav.length > 0) return profileNav;\n return getDefaultNavigation();\n }, [navigationProp, appData?.profile?.navigation?.navigation_items]);\n\n // Resolve mobile navigation items (fall back to desktop nav)\n const mobileNavItems = useMemo(() => {\n const mobileNav = appData?.profile?.mobile_navigation?.navigation_items;\n if (mobileNav && mobileNav.length > 0) return mobileNav;\n return navItems;\n }, [appData?.profile?.mobile_navigation?.navigation_items, navItems]);\n\n // Resolve builder screen definitions\n const screens: ScreenDefinition[] | undefined = appData?.screens;\n\n // Collect all nav slugs for prefix matching (sorted longest-first)\n const navSlugs = useMemo(() => collectNavSlugs(navItems), [navItems]);\n\n // Theme mode state with localStorage persistence\n const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialThemeMode);\n\n const handleThemeModeChange = useCallback((mode: ThemeMode) => {\n setThemeMode(mode);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(THEME_STORAGE_KEY, mode);\n }\n }, []);\n\n // Mirror data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n const mode = themeMode === \"auto\" ? undefined : themeMode;\n if (mode) {\n root.setAttribute(\"data-theme-mode\", mode);\n } else {\n root.removeAttribute(\"data-theme-mode\");\n }\n return () => {\n root.removeAttribute(\"data-theme-mode\");\n };\n }, [themeMode]);\n\n // Set <meta name=\"theme-color\"> so mobile browser chrome matches the theme\n useEffect(() => {\n if (!resolvedTheme) return;\n\n let meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"theme-color\"]',\n );\n if (!meta) {\n meta = document.createElement(\"meta\");\n meta.name = \"theme-color\";\n document.head.appendChild(meta);\n }\n const metaEl = meta;\n\n const theme = resolvedTheme;\n function update(prefersDark: boolean) {\n const mode =\n themeMode === \"auto\" ? (prefersDark ? \"dark\" : \"light\") : themeMode;\n const color =\n mode === \"dark\" ? theme.dark.muted.base : theme.light.muted.base;\n const hex = color.toString();\n metaEl.content = hex;\n document.body.style.backgroundColor = hex;\n }\n\n const mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n update(mql.matches);\n\n if (themeMode === \"auto\") {\n const handler = (e: MediaQueryListEvent) => update(e.matches);\n mql.addEventListener(\"change\", handler);\n return () => {\n mql.removeEventListener(\"change\", handler);\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }\n\n return () => {\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }, [resolvedTheme, themeMode]);\n\n // Path-based routing for uncontrolled mode\n const [pathSlug, setPathSlug] = useState<string>(() => {\n if (typeof window === \"undefined\") return \"\";\n return extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n });\n\n // Sync initial path when navItems load — redirect to first nav slug if at root\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n if (typeof window === \"undefined\") return;\n\n const currentPath = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n\n if (!currentPath) {\n const firstSlug = findFirstNavigableSlug(navItems);\n if (firstSlug) {\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${firstSlug}`\n : `${normalizedBasePath}/${firstSlug}`;\n // replaceState to avoid polluting back-button history\n history.replaceState(null, \"\", targetPath);\n setPathSlug(firstSlug);\n }\n }\n }, [navItems, controlledSlug, normalizedBasePath]);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n\n const handlePopState = () => {\n const slug = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n setPathSlug(slug);\n };\n\n window.addEventListener(\"popstate\", handlePopState);\n return () => window.removeEventListener(\"popstate\", handlePopState);\n }, [controlledSlug, normalizedBasePath]);\n\n // Logout hook — always called (rules of hooks) but only wired to UI below\n const logout = useLogout({\n redirectUrl: logoutRedirectUrl,\n onLogout,\n });\n\n const activeSlug = controlledSlug ?? pathSlug;\n\n const handleNavigate = useCallback(\n (slug: string) => {\n if (onNavigateProp) {\n onNavigateProp(slug);\n return;\n }\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${slug}`\n : `${normalizedBasePath}/${slug}`;\n history.pushState(null, \"\", targetPath);\n setPathSlug(slug);\n },\n [onNavigateProp, normalizedBasePath],\n );\n\n // Resolve sidebar footer: explicit prop wins, otherwise render default logout button\n const resolvedSidebarFooter = useMemo(\n () =>\n sidebarFooter !== undefined ? (\n sidebarFooter\n ) : (\n <SdkLogoutButton onLogout={logout} />\n ),\n [sidebarFooter, logout],\n );\n\n // Derive base slug and rest params via prefix matching\n const slugMatch = useMemo(\n () => matchSlugPrefix(activeSlug, navSlugs),\n [activeSlug, navSlugs],\n );\n const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);\n const restParams = slugMatch?.rest ?? \"\";\n\n // Derive mobile sub-tabs for the current section\n const mobileCurrentSection = findCurrentSection(mobileNavItems, baseSlug);\n const mobileSecondLevelTabs = mobileCurrentSection?.children ?? [];\n const currentNavItem = findNavItem(navItems, baseSlug);\n\n // Resolve screen title from nav label or screen definition name\n const screenTitle =\n currentNavItem?.label ||\n screens?.find((s) => s.id === currentNavItem?.screen_id)?.name ||\n undefined;\n\n // Determine content to render\n const content =\n typeof children === \"function\"\n ? children({ currentSlug: activeSlug, currentNavItem })\n : (children ?? (\n <ScreenHeaderProvider>\n <ScreenHeader title={screenTitle} />\n <PageRouter\n currentSlug={activeSlug}\n currentNavItem={currentNavItem}\n customPages={customPages}\n screens={screens}\n baseSlug={baseSlug}\n restParams={restParams}\n />\n </ScreenHeaderProvider>\n ));\n\n // Show loading state while app data is being fetched for the first time.\n // This check is placed after all hooks to satisfy React's rules of hooks.\n if (isLoading && !appData) {\n return <AppShellLoading />;\n }\n\n return (\n <RepUserProvider user={repUser}>\n <ThemeModeProvider\n mode={themeMode}\n onModeChange={handleThemeModeChange}\n autoModeEnabled={false}\n >\n <div\n className=\"font-body\"\n data-theme={activeTheme?.id}\n data-theme-mode={themeMode === \"auto\" ? undefined : themeMode}\n >\n <AppNavigationProvider\n currentSlug={activeSlug}\n basePath={normalizedBasePath}\n navigate={handleNavigate}\n >\n <AppShellLayout\n sidebarContent={\n <SdkNavigation\n navItems={navItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n />\n }\n headerContent={\n <SdkHeader\n mobileTabs={mobileSecondLevelTabs}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n onLogout={logout}\n />\n }\n sidebarHeader={\n sidebarHeader !== undefined ? (\n sidebarHeader\n ) : (\n <SdkCompanySwitcher />\n )\n }\n sidebarFooter={resolvedSidebarFooter}\n useBottomNav\n afterContent={\n <SdkBottomNav\n navItems={mobileNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n appDefinitionId={appData?.definition_id ?? 0}\n />\n }\n >\n {content}\n </AppShellLayout>\n </AppNavigationProvider>\n </div>\n </ThemeModeProvider>\n </RepUserProvider>\n );\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../client/types\";\nimport type { FluidAuthConfig } from \"../auth/types\";\nimport { getStoredToken } from \"../auth/token-storage\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to \"https://api.fluid.app\"\n * - getAuthToken: reads from the SDK's token storage (cookie/localStorage)\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"https://api.fluid.app\",\n getAuthToken: () => getStoredToken() ?? null,\n ...overrides,\n };\n}\n\n/**\n * Creates a FluidAuthConfig with sensible defaults.\n *\n * The SDK's FluidAuthProvider already applies defaults for all fields,\n * so an empty config works out of the box. Use overrides to customize:\n * ```ts\n * const config = createDefaultAuthConfig({ authUrl: \"https://login.example.com\" });\n * ```\n */\nexport function createDefaultAuthConfig(\n overrides?: Partial<FluidAuthConfig>,\n): FluidAuthConfig {\n return { ...overrides };\n}\n","import { StrictMode, type ReactNode, type ComponentType } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { FluidAuthProvider } from \"../providers/FluidAuthProvider\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { RequireAuth } from \"../components/RequireAuth\";\nimport { AppShell, type AppShellProps } from \"../shell/AppShell\";\nimport {\n createDefaultFluidConfig,\n createDefaultAuthConfig,\n} from \"../config/defaults\";\nimport type { FluidSDKConfig } from \"../client/types\";\nimport type { FluidAuthConfig } from \"../auth/types\";\n\n/**\n * Configuration for `createPortal()`.\n *\n * Provides a single-call entry point that sets up the full provider hierarchy\n * (auth, SDK, shell) with sensible defaults. Every field is optional.\n *\n * For full control, skip `createPortal()` and compose providers manually:\n * ```tsx\n * import { FluidAuthProvider, FluidProvider, RequireAuth, AppShell } from \"@fluid-app/portal-sdk\";\n * ```\n */\nexport interface PortalConfig {\n /** Custom page components keyed by navigation slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** API client config overrides (merged with defaults) */\n fluid?: Partial<FluidSDKConfig>;\n /** Auth config overrides (merged with defaults) */\n auth?: Partial<FluidAuthConfig>;\n /** Root element ID. @default \"root\" */\n rootId?: string;\n /** AppShell props passthrough (basePath, sidebarHeader, etc.) */\n shell?: Omit<AppShellProps, \"customPages\">;\n /** Wrap AppShell with additional providers (inserted inside RequireAuth, below FluidProvider) */\n providers?: ComponentType<{ children: ReactNode }>;\n /** Disable StrictMode. @default false */\n disableStrictMode?: boolean;\n}\n\n/**\n * Bootstrap a Fluid portal with a single function call.\n *\n * Sets up the full provider hierarchy (FluidAuthProvider → FluidProvider → RequireAuth → AppShell)\n * and renders into the DOM.\n *\n * @example Minimal usage\n * ```ts\n * import { createPortal } from \"@fluid-app/portal-sdk\";\n * import \"./index.css\";\n *\n * createPortal();\n * ```\n *\n * @example With custom pages\n * ```ts\n * import { createPortal } from \"@fluid-app/portal-sdk\";\n * import { customPages } from \"./portal.config\";\n * import \"./index.css\";\n *\n * createPortal({ customPages });\n * ```\n *\n * @example With overrides\n * ```ts\n * createPortal({\n * customPages,\n * fluid: { baseUrl: \"https://my-api.example.com\" },\n * auth: { authUrl: \"https://login.example.com\" },\n * shell: { basePath: \"/portal\" },\n * });\n * ```\n */\nexport function createPortal(config: PortalConfig = {}): void {\n const rootElement = document.getElementById(config.rootId ?? \"root\");\n if (!rootElement) {\n throw new Error(\n `Portal root element \"#${config.rootId ?? \"root\"}\" not found`,\n );\n }\n\n const fluidConfig = createDefaultFluidConfig(config.fluid);\n const authConfig = createDefaultAuthConfig(config.auth);\n\n let app: ReactNode = (\n <AppShell customPages={config.customPages} {...config.shell} />\n );\n\n if (config.providers) {\n const Providers = config.providers;\n app = <Providers>{app}</Providers>;\n }\n\n const tree = (\n <FluidAuthProvider config={authConfig}>\n <FluidProvider config={fluidConfig}>\n <RequireAuth>{app}</RequireAuth>\n </FluidProvider>\n </FluidAuthProvider>\n );\n\n createRoot(rootElement).render(\n config.disableStrictMode ? tree : <StrictMode>{tree}</StrictMode>,\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => api.app.getRaw(),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport type { UserPermissions, PermissionAction } from \"../types/permissions\";\n\n/**\n * Base query key for permissions data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const PERMISSIONS_QUERY_KEY = [\"fluid\", \"permissions\"] as const;\n\n/**\n * Result of useFluidPermissions hook\n */\nexport interface UseFluidPermissionsResult {\n /** Raw permissions query result */\n query: UseQueryResult<UserPermissions>;\n /** Permissions data (alias for query.data) */\n permissions: UserPermissions | undefined;\n /** Check if user has a specific permission */\n can: (resource: string, action?: PermissionAction) => boolean;\n /** Check if user is a super admin */\n isSuperAdmin: boolean;\n}\n\n/**\n * Hook to fetch and check user permissions\n *\n * @example\n * ```tsx\n * function TeamSettings() {\n * const { can, isSuperAdmin } = useFluidPermissions();\n *\n * if (!can(\"team\", \"manage\")) {\n * return <AccessDenied />;\n * }\n *\n * return <TeamSettingsForm canDelete={can(\"team\", \"delete\")} />;\n * }\n * ```\n */\nexport function useFluidPermissions(): UseFluidPermissionsResult {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n const query = useQuery({\n queryKey: scopeKey(PERMISSIONS_QUERY_KEY),\n queryFn: () => api.permissions.get(),\n });\n\n const permissions = query.data;\n\n // Memoize the can function to maintain referential stability\n const can = useMemo(() => {\n return (resource: string, action: PermissionAction = \"view\"): boolean => {\n if (!permissions) {\n return false;\n }\n\n // Super admins have all permissions\n if (permissions.is_super_admin) {\n return true;\n }\n\n const resourcePermissions = permissions.permissions[resource];\n if (!resourcePermissions) {\n return false;\n }\n\n return resourcePermissions[action] ?? false;\n };\n }, [permissions]);\n\n const isSuperAdmin = permissions?.is_super_admin ?? false;\n\n return {\n query,\n permissions,\n can,\n isSuperAdmin,\n };\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport type { Rep } from \"../types/rep\";\n\n/**\n * Base query key for current rep data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const CURRENT_REP_QUERY_KEY = [\"fluid\", \"currentRep\"] as const;\n\n/**\n * Hook to fetch the currently authenticated rep's profile\n *\n * @example\n * ```tsx\n * function RepHeader() {\n * const { data: rep, isLoading } = useCurrentRep();\n *\n * if (isLoading) return <Skeleton />;\n *\n * return (\n * <div>\n * <Avatar src={rep?.avatar_url} />\n * <span>{rep?.first_name} {rep?.last_name}</span>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCurrentRep(): UseQueryResult<Rep> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(CURRENT_REP_QUERY_KEY),\n queryFn: () => api.reps.current(),\n });\n}\n","import type { CalendarEvent } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number, hour: number, minute: number = 0): Date {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n d.setHours(hour, minute, 0, 0);\n return d;\n}\n\nexport const DEMO_CALENDAR_EVENTS: readonly CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Standup\",\n description: { body: \"Daily sync with the sales team\" },\n color: \"#4f46e5\",\n start: daysFromNow(0, 9, 0).toISOString(),\n end: daysFromNow(0, 9, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n },\n {\n id: 2,\n title: \"Client Review - Acme Corp\",\n description: { body: \"Quarterly business review with key accounts\" },\n color: \"#059669\",\n start: daysFromNow(2, 14, 0).toISOString(),\n end: daysFromNow(2, 15, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"Conference Room B\",\n },\n {\n id: 3,\n title: \"Product Training Webinar\",\n description: { body: \"New product line training for the field\" },\n color: \"#d97706\",\n start: daysFromNow(4, 11, 0).toISOString(),\n end: daysFromNow(4, 12, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n url: \"https://example.com/webinar\",\n },\n {\n id: 4,\n title: \"Regional Conference\",\n description: { body: \"Annual West Coast regional conference\" },\n color: \"#dc2626\",\n start: daysFromNow(10, 8, 0).toISOString(),\n end: daysFromNow(12, 17, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"San Diego Convention Center\",\n isAllDay: true,\n },\n];\n","/**\n * Calendar events hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CalendarEvent, CalendarEventDescription } from \"./hook-types\";\nimport { DEMO_CALENDAR_EVENTS } from \"./demo-data/calendar-events\";\n\n// Re-export types so external consumers are unaffected\nexport type { CalendarEvent, CalendarEventDescription };\n\n/**\n * Result type for useCalendarEvents hook.\n * Uses QueryResult<CalendarEvent[]> with default Error type.\n */\nexport type UseCalendarEventsResult = QueryResult<CalendarEvent[]>;\n\n/**\n * Hook to fetch calendar events.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCalendarEvents(): UseCalendarEventsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CALENDAR_EVENTS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { Todo } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nexport const DEMO_TODOS: readonly Todo[] = [\n {\n id: 1,\n body: \"Follow up with Sarah about Q2 order\",\n dueAt: daysFromNow(1),\n completedAt: null,\n createdAt: daysFromNow(-2),\n contactName: \"Sarah Johnson\",\n },\n {\n id: 2,\n body: \"Send pricing proposal to Acme Corp\",\n dueAt: daysFromNow(7),\n completedAt: null,\n createdAt: daysFromNow(-1),\n contactName: \"Mike Chen\",\n },\n {\n id: 3,\n body: \"Schedule onboarding call with new team member\",\n dueAt: daysFromNow(3),\n completedAt: null,\n createdAt: daysFromNow(0),\n contactName: \"Emily Rodriguez\",\n },\n {\n id: 4,\n body: \"Review and approve Q1 expense report\",\n dueAt: daysFromNow(-1),\n completedAt: daysFromNow(-1),\n createdAt: daysFromNow(-5),\n contactName: null,\n },\n {\n id: 5,\n body: \"Prepare presentation for Friday team meeting\",\n dueAt: daysFromNow(4),\n completedAt: daysFromNow(0),\n createdAt: daysFromNow(-3),\n contactName: null,\n },\n];\n","/**\n * Todos hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { Todo } from \"./hook-types\";\nimport { DEMO_TODOS } from \"./demo-data/todos\";\n\n// Re-export type so external consumers are unaffected\nexport type { Todo };\n\n/**\n * Result type for useTodos hook.\n * Uses QueryResult<Todo[]> with default Error type.\n */\nexport type UseTodosResult = QueryResult<Todo[]>;\n\n/**\n * Hook to fetch todo items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useTodos(): UseTodosResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_TODOS],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n","import type { Activity } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\nexport const DEMO_ACTIVITIES: readonly Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"New Order\",\n targetName: \"Wellness Starter Kit\",\n timestamp: hoursAgo(1),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Referral from LinkedIn campaign\",\n timestamp: hoursAgo(3),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Emily Rodriguez\",\n avatarUrl: null,\n activityType: \"Page View\",\n targetName: \"Product Catalog\",\n timestamp: hoursAgo(5),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"David Park\",\n avatarUrl: null,\n activityType: \"Cart Items Added\",\n targetName: \"Premium Bundle (3 items)\",\n timestamp: hoursAgo(8),\n slug: \"cart_items_added\",\n },\n {\n id: 5,\n userName: \"Lisa Thompson\",\n avatarUrl: null,\n activityType: \"Video Watched\",\n targetName: \"Getting Started Tutorial\",\n timestamp: hoursAgo(12),\n slug: \"video_complete\",\n },\n {\n id: 6,\n userName: \"James Wilson\",\n avatarUrl: null,\n activityType: \"Message Received\",\n targetName: \"Question about shipping\",\n timestamp: hoursAgo(18),\n slug: \"message_received\",\n },\n {\n id: 7,\n userName: \"Rachel Kim\",\n avatarUrl: null,\n activityType: \"Smart Link Clicked\",\n targetName: \"Holiday Promo Link\",\n timestamp: hoursAgo(24),\n slug: \"smart_link_clicked\",\n },\n {\n id: 8,\n userName: \"Tom Martinez\",\n avatarUrl: null,\n activityType: \"Review Left\",\n targetName: \"Essential Oil Set - 5 stars\",\n timestamp: hoursAgo(36),\n slug: \"review_left\",\n },\n];\n","/**\n * Activities hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport {\n ACTIVITY_SLUGS,\n isActivitySlug,\n type Activity,\n type ActivitySlug,\n} from \"./hook-types\";\nimport { DEMO_ACTIVITIES } from \"./demo-data/activities\";\n\n// Re-export types and constants so external consumers are unaffected\nexport { ACTIVITY_SLUGS, isActivitySlug };\nexport type { Activity, ActivitySlug };\n\n/**\n * Result type for useActivities hook.\n * Uses QueryResult generic with Activity[] and default Error type.\n */\nexport type UseActivitiesResult = QueryResult<Activity[]>;\n\n/**\n * Hook to fetch recent activities.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useActivities(): UseActivitiesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_ACTIVITIES],\n isLoading: false,\n isError: false,\n };\n}\n","import type { CatchUp } from \"../hook-types\";\n\nexport const DEMO_CATCHUPS: readonly CatchUp[] = [\n {\n id: 1,\n suggestion_title: \"Sarah Johnson hasn't ordered in 30 days\",\n },\n {\n id: 2,\n suggestion_title: \"Mike Chen's subscription is expiring soon\",\n },\n {\n id: 3,\n suggestion_title: \"Emily Rodriguez left a cart with 3 items\",\n },\n];\n","/**\n * Catch ups hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CatchUp } from \"./hook-types\";\nimport { DEMO_CATCHUPS } from \"./demo-data/catchups\";\n\n// Re-export type so external consumers are unaffected\nexport type { CatchUp };\n\n/**\n * Result type for useCatchUps hook.\n * Uses QueryResult<CatchUp[]> with default Error type.\n */\nexport type UseCatchUpsResult = QueryResult<CatchUp[]>;\n\n/**\n * Hook to fetch catch up items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCatchUps(): UseCatchUpsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CATCHUPS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { MySiteData } from \"../hook-types\";\n\nexport const DEMO_MYSITE: MySiteData = {\n url: \"https://my-portal.example.com\",\n views: 1243,\n leads: 37,\n userName: \"Demo User\",\n};\n","/**\n * MySite hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResultNullable } from \"./hook-types\";\nimport type { MySiteData } from \"./hook-types\";\nimport { DEMO_MYSITE } from \"./demo-data/mysite\";\n\n// Re-export type so external consumers are unaffected\nexport type { MySiteData };\n\n/**\n * Result type for useMySite hook.\n * Uses QueryResultNullable since MySite data may not be available.\n */\nexport type UseMySiteResult = QueryResultNullable<MySiteData>;\n\n/**\n * Hook to fetch MySite data.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useMySite(): UseMySiteResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: DEMO_MYSITE,\n isLoading: false,\n isError: false,\n };\n}\n","import type { Conversation, Message } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONVERSATIONS: readonly Conversation[] = [\n {\n id: \"conv-1\",\n title: \"Sarah Johnson\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-2\",\n name: \"Sarah Johnson\",\n email: \"sarah.johnson@example.com\",\n isOnline: true,\n },\n ],\n lastMessage: {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n unreadCount: 1,\n status: \"active\",\n createdAt: daysAgo(30),\n updatedAt: hoursAgo(1),\n },\n {\n id: \"conv-2\",\n title: \"Mike Chen\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-3\",\n name: \"Mike Chen\",\n email: \"mike.chen@acmecorp.com\",\n isOnline: false,\n },\n ],\n lastMessage: {\n id: \"msg-2-2\",\n conversationId: \"conv-2\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"I've attached the updated pricing sheet. Let me know if you have any questions!\",\n timestamp: hoursAgo(5),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(14),\n updatedAt: hoursAgo(5),\n },\n {\n id: \"conv-3\",\n title: \"Team Updates\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-4\",\n name: \"Emily Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n },\n { id: \"user-5\", name: \"David Park\", email: \"david.park@email.com\" },\n ],\n lastMessage: {\n id: \"msg-3-4\",\n conversationId: \"conv-3\",\n senderId: \"user-4\",\n senderName: \"Emily Rodriguez\",\n type: \"text\",\n content:\n \"The new product samples arrived today. Will distribute to the team tomorrow.\",\n timestamp: daysAgo(1),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(60),\n updatedAt: daysAgo(1),\n },\n];\n\nexport const DEMO_MESSAGES: readonly Message[] = [\n {\n id: \"msg-1-1\",\n conversationId: \"conv-1\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"Hi Sarah! I wanted to follow up on the Wellness Starter Kit. We have a special promotion running this month.\",\n timestamp: hoursAgo(3),\n isRead: true,\n },\n {\n id: \"msg-1-2\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content:\n \"That's perfect timing! I was just thinking about reordering. What's the promo?\",\n timestamp: hoursAgo(2),\n isRead: true,\n },\n {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n];\n","/**\n * Conversations hooks - stub implementations for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Conversation, Message } from \"../types/screen-types\";\nimport type { QueryResult } from \"./hook-types\";\nimport { DEMO_CONVERSATIONS, DEMO_MESSAGES } from \"./demo-data/conversations\";\n\n// =============================================================================\n// useConversations Hook\n// =============================================================================\n\n/**\n * Result type for useConversations hook.\n * Uses QueryResult<Conversation[]> with default Error type.\n */\nexport type UseConversationsResult = QueryResult<Conversation[]>;\n\n/**\n * Hook to fetch all conversations.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @returns UseConversationsResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: conversations, isLoading, isError } = useConversations();\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return conversations.map(conv => <ConversationItem key={conv.id} {...conv} />);\n * ```\n */\nexport function useConversations(): UseConversationsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONVERSATIONS],\n isLoading: false,\n isError: false,\n };\n}\n\n// =============================================================================\n// useConversationMessages Hook\n// =============================================================================\n\n/**\n * Result type for useConversationMessages hook.\n * Uses QueryResult<Message[]> with default Error type.\n */\nexport type UseConversationMessagesResult = QueryResult<Message[]>;\n\n/**\n * Hook to fetch messages for a specific conversation.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param conversationId - The ID of the conversation to fetch messages for\n * @returns UseConversationMessagesResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: messages, isLoading, isError } = useConversationMessages(conversationId);\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return messages.map(msg => <MessageBubble key={msg.id} {...msg} />);\n * ```\n */\nexport function useConversationMessages(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _conversationId: string,\n): UseConversationMessagesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _conversationId\n return {\n data: [...DEMO_MESSAGES],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Screen Types - Type definitions for core feature screens\n *\n * All status and type unions are derived from constants for single source of truth.\n * Use the constants (e.g., CONVERSATION_STATUSES.active) for type-safe comparisons.\n */\n\n// =============================================================================\n// Messaging Types\n// =============================================================================\n\n/**\n * Conversation status constant - single source of truth.\n */\nexport const CONVERSATION_STATUSES = {\n active: \"active\",\n archived: \"archived\",\n muted: \"muted\",\n} as const;\n\n/**\n * Union type derived from CONVERSATION_STATUSES constant.\n */\nexport type ConversationStatus =\n (typeof CONVERSATION_STATUSES)[keyof typeof CONVERSATION_STATUSES];\n\n/**\n * Message type constant - single source of truth.\n */\nexport const MESSAGE_TYPES = {\n text: \"text\",\n image: \"image\",\n file: \"file\",\n system: \"system\",\n} as const;\n\n/**\n * Union type derived from MESSAGE_TYPES constant.\n */\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\nexport interface Participant {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n readonly avatarUrl?: string;\n readonly isOnline?: boolean;\n}\n\n/**\n * Message attachment type - extracted for reusability and clarity.\n */\nexport interface MessageAttachment {\n readonly id: string;\n readonly name: string;\n readonly url: string;\n readonly type: string;\n readonly size?: number;\n}\n\nexport interface Message {\n readonly id: string;\n readonly conversationId: string;\n readonly senderId: string;\n readonly senderName: string;\n readonly senderAvatarUrl?: string;\n readonly type: MessageType;\n readonly content: string;\n readonly timestamp: string;\n readonly isRead: boolean;\n readonly attachments?: readonly MessageAttachment[];\n}\n\nexport interface Conversation {\n readonly id: string;\n readonly title: string;\n readonly participants: readonly Participant[];\n readonly lastMessage?: Message;\n readonly unreadCount: number;\n readonly status: ConversationStatus;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n\n// =============================================================================\n// Contacts Types\n// =============================================================================\n\n/**\n * Contact status constant - single source of truth.\n */\nexport const CONTACT_STATUSES = {\n active: \"active\",\n inactive: \"inactive\",\n lead: \"lead\",\n prospect: \"prospect\",\n} as const;\n\n/**\n * Union type derived from CONTACT_STATUSES constant.\n */\nexport type ContactStatus =\n (typeof CONTACT_STATUSES)[keyof typeof CONTACT_STATUSES];\n\n/**\n * Contact type constant - single source of truth.\n */\nexport const CONTACT_TYPES = {\n individual: \"individual\",\n company: \"company\",\n} as const;\n\n/**\n * Union type derived from CONTACT_TYPES constant.\n */\nexport type ContactType = (typeof CONTACT_TYPES)[keyof typeof CONTACT_TYPES];\n\nexport interface ContactAddress {\n readonly street?: string;\n readonly city?: string;\n readonly state?: string;\n readonly postalCode?: string;\n readonly country?: string;\n}\n\nexport interface Contact {\n readonly id: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly email: string;\n readonly phone?: string;\n readonly company?: string;\n readonly jobTitle?: string;\n readonly avatarUrl?: string;\n readonly status: ContactStatus;\n readonly type: ContactType;\n readonly address?: ContactAddress;\n readonly tags?: readonly string[];\n readonly notes?: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n","import type { Contact } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONTACTS: readonly Contact[] = [\n {\n id: \"contact-1\",\n firstName: \"Sarah\",\n lastName: \"Johnson\",\n email: \"sarah.johnson@example.com\",\n phone: \"+1 (555) 123-4567\",\n company: \"Wellness Works Inc.\",\n jobTitle: \"Purchasing Manager\",\n status: \"active\",\n type: \"individual\",\n tags: [\"VIP\", \"Repeat Buyer\"],\n createdAt: daysAgo(90),\n updatedAt: daysAgo(2),\n },\n {\n id: \"contact-2\",\n firstName: \"Mike\",\n lastName: \"Chen\",\n email: \"mike.chen@acmecorp.com\",\n phone: \"+1 (555) 234-5678\",\n company: \"Acme Corp\",\n jobTitle: \"Director of Operations\",\n status: \"active\",\n type: \"individual\",\n tags: [\"Enterprise\"],\n createdAt: daysAgo(60),\n updatedAt: daysAgo(5),\n },\n {\n id: \"contact-3\",\n firstName: \"Emily\",\n lastName: \"Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n company: \"HealthFirst\",\n status: \"lead\",\n type: \"individual\",\n notes: \"Interested in bulk pricing for team orders\",\n createdAt: daysAgo(7),\n updatedAt: daysAgo(1),\n },\n {\n id: \"contact-4\",\n firstName: \"David\",\n lastName: \"Park\",\n email: \"david.park@email.com\",\n phone: \"+1 (555) 345-6789\",\n status: \"prospect\",\n type: \"individual\",\n tags: [\"Referral\"],\n createdAt: daysAgo(14),\n updatedAt: daysAgo(10),\n },\n {\n id: \"contact-5\",\n firstName: \"Lisa\",\n lastName: \"Thompson\",\n email: \"lisa.t@globalfit.com\",\n company: \"Global Fitness\",\n jobTitle: \"CEO\",\n status: \"active\",\n type: \"individual\",\n address: {\n city: \"Los Angeles\",\n state: \"CA\",\n country: \"US\",\n },\n createdAt: daysAgo(120),\n updatedAt: daysAgo(15),\n },\n {\n id: \"contact-6\",\n firstName: \"James\",\n lastName: \"Wilson\",\n email: \"jwilson@retired.net\",\n status: \"inactive\",\n type: \"individual\",\n notes: \"Moved out of area, no longer purchasing\",\n createdAt: daysAgo(200),\n updatedAt: daysAgo(45),\n },\n];\n\nexport const DEMO_CONTACT: Contact = DEMO_CONTACTS[0]!;\n","/**\n * Contacts hooks - stub implementation for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Contact, ContactStatus } from \"../types/screen-types\";\nimport { CONTACT_STATUSES } from \"../types/screen-types\";\nimport type { ListQueryResult, QueryResultNullable } from \"./hook-types\";\nimport { DEMO_CONTACTS, DEMO_CONTACT } from \"./demo-data/contacts\";\n\n/**\n * Type predicate to check if a status string is a valid ContactStatus.\n * Enables runtime validation with type narrowing.\n */\nexport function isContactStatus(value: string): value is ContactStatus {\n return Object.values(CONTACT_STATUSES).includes(value as ContactStatus);\n}\n\n/**\n * Parameters for filtering and paginating contacts.\n * Uses readonly properties and proper ContactStatus type for status.\n */\nexport interface UseContactsParams {\n /** Search query to filter contacts by name, email, or company */\n readonly search?: string;\n /** Filter contacts by status - uses ContactStatus union type for type safety */\n readonly status?: ContactStatus;\n /** Maximum number of contacts to return */\n readonly limit?: number;\n}\n\n/**\n * Result type for the useContacts hook.\n * Uses ListQueryResult<Contact> with totalCount and default Error type.\n */\nexport type UseContactsResult = ListQueryResult<Contact>;\n\n/**\n * Result type for the useContact hook.\n * Uses QueryResultNullable since a specific contact may not exist.\n */\nexport type UseContactResult = QueryResultNullable<Contact>;\n\n/**\n * Hook to fetch a list of contacts with optional filtering and pagination.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param params - Optional parameters for filtering and pagination\n * @returns Object containing contacts data, loading state, error state, and total count\n *\n * @example\n * ```tsx\n * const { data: contacts, isLoading, totalCount } = useContacts({\n * search: 'john',\n * status: 'active',\n * limit: 20\n * });\n * ```\n */\nexport function useContacts(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _params?: UseContactsParams,\n): UseContactsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONTACTS],\n isLoading: false,\n isError: false,\n totalCount: DEMO_CONTACTS.length,\n };\n}\n\n/**\n * Hook to fetch a single contact by ID.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param contactId - The unique identifier of the contact to fetch\n * @returns Object containing contact data, loading state, and error state\n *\n * @example\n * ```tsx\n * const { data: contact, isLoading, isError } = useContact('contact-123');\n * ```\n */\nexport function useContact(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _contactId: string,\n): UseContactResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _contactId\n return {\n data: DEMO_CONTACT,\n isLoading: false,\n isError: false,\n };\n}\n","// Screen Components\nexport { ProfileScreen, profileScreenPropertySchema } from \"./ProfileScreen\";\nexport {\n MessagingScreen,\n messagingScreenPropertySchema,\n} from \"./MessagingScreen\";\nexport { ContactsScreen, contactsScreenPropertySchema } from \"./ContactsScreen\";\nexport { OrdersScreen, ordersScreenPropertySchema } from \"./OrdersScreen\";\nexport {\n SubscriptionsScreen,\n subscriptionsScreenPropertySchema,\n} from \"./SubscriptionsScreen\";\nexport {\n CustomersScreen,\n customersScreenPropertySchema,\n} from \"./CustomersScreen\";\nexport { ProductsScreen, productsScreenPropertySchema } from \"./ProductsScreen\";\nexport { MySiteScreen, mySiteScreenPropertySchema } from \"./MySiteScreen\";\nexport {\n ShareablesScreen,\n shareablesScreenPropertySchema,\n} from \"./ShareablesScreen\";\nexport { ShopScreen, shopScreenPropertySchema } from \"./ShopScreen\";\nexport { UpgradeScreen, upgradeScreenPropertySchema } from \"./UpgradeScreen\";\nexport {\n AppDownloadScreen,\n appDownloadScreenPropertySchema,\n} from \"./AppDownloadScreen\";\n\n// Re-export all property schemas as a lazy-loadable collection\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n\nexport const screenPropertySchemas: Record<\n string,\n () => Promise<WidgetPropertySchema>\n> = {\n ProfileScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProfileScreen\").then((m) => m.profileScreenPropertySchema),\n MessagingScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MessagingScreen\").then((m) => m.messagingScreenPropertySchema),\n ContactsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ContactsScreen\").then((m) => m.contactsScreenPropertySchema),\n OrdersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./OrdersScreen\").then((m) => m.ordersScreenPropertySchema),\n SubscriptionsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./SubscriptionsScreen\").then(\n (m) => m.subscriptionsScreenPropertySchema,\n ),\n CustomersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./CustomersScreen\").then((m) => m.customersScreenPropertySchema),\n ProductsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProductsScreen\").then((m) => m.productsScreenPropertySchema),\n MySiteScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MySiteScreen\").then((m) => m.mySiteScreenPropertySchema),\n ShareablesScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShareablesScreen\").then((m) => m.shareablesScreenPropertySchema),\n ShopScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShopScreen\").then((m) => m.shopScreenPropertySchema),\n UpgradeScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./UpgradeScreen\").then((m) => m.upgradeScreenPropertySchema),\n AppDownloadScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./AppDownloadScreen\").then(\n (m) => m.appDownloadScreenPropertySchema,\n ),\n};\n\n// Page Template Registration\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Core page template IDs\n */\nexport const CORE_PAGE_IDS = {\n PROFILE: \"core-profile\",\n MESSAGING: \"core-messaging\",\n CONTACTS: \"core-contacts\",\n ORDERS: \"core-orders\",\n SUBSCRIPTIONS: \"core-subscriptions\",\n CUSTOMERS: \"core-customers\",\n PRODUCTS: \"core-products\",\n MY_SITE: \"core-my-site\",\n SHAREABLES: \"core-shareables\",\n SHOP: \"core-shop\",\n UPGRADE: \"core-upgrade\",\n APP_DOWNLOAD: \"core-app-download\",\n} as const;\n\n/**\n * Register core page templates.\n * These are automatically registered when the SDK is imported.\n */\nfunction registerCorePageTemplates(): void {\n // Profile Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PROFILE,\n slug: \"profile\",\n name: \"Profile\",\n description: \"User profile management\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"profile\", \"account\", \"user\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProfileScreen\",\n id: \"profile-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Messaging Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MESSAGING,\n slug: \"messaging\",\n name: \"Messaging\",\n description: \"Messaging interface provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.COMMUNICATION,\n tags: [\"messaging\", \"chat\", \"conversations\", \"communication\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MessagingScreen\",\n id: \"messaging-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Contacts Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CONTACTS,\n slug: \"contacts\",\n name: \"Contacts\",\n description: \"Contact management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"contacts\", \"people\", \"address-book\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ContactsScreen\",\n id: \"contacts-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Orders Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.ORDERS,\n slug: \"orders\",\n name: \"Orders\",\n description: \"Order management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"orders\", \"commerce\", \"sales\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"OrdersScreen\",\n id: \"orders-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Subscriptions Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SUBSCRIPTIONS,\n slug: \"subscriptions\",\n name: \"Subscriptions\",\n description: \"Subscription management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"subscriptions\", \"recurring\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"SubscriptionsScreen\",\n id: \"subscriptions-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Customers Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CUSTOMERS,\n slug: \"customers\",\n name: \"Customers\",\n description: \"Customer management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"customers\", \"people\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"CustomersScreen\",\n id: \"customers-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shop Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHOP,\n slug: \"shop\",\n name: \"Shop\",\n description: \"Product shop with catalog browsing and product details\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shop\", \"store\", \"products\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShopScreen\",\n id: \"shop-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Products Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PRODUCTS,\n slug: \"products\",\n name: \"Products\",\n description: \"Product catalog provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"products\", \"catalog\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProductsScreen\",\n id: \"products-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // My Site Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MY_SITE,\n slug: \"my-site\",\n name: \"My Site\",\n description: \"Personal site management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"my-site\", \"website\", \"portal\", \"personal\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MySiteScreen\",\n id: \"my-site-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Upgrade Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.UPGRADE,\n slug: \"upgrade\",\n name: \"Upgrade\",\n description: \"Pro upgrade waitlist screen\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"upgrade\", \"pro\", \"waitlist\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"UpgradeScreen\",\n id: \"upgrade-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // App Download Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.APP_DOWNLOAD,\n slug: \"app-download\",\n name: \"App Download\",\n description: \"Mobile app download page with store links and QR code\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"app-download\", \"mobile\", \"qr-code\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"AppDownloadScreen\",\n id: \"app-download-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shareables Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHAREABLES,\n slug: \"share\",\n name: \"Shareables\",\n description:\n \"Marketing assets and shareable content provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shareables\", \"marketing\", \"assets\", \"media\", \"products\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShareablesScreen\",\n id: \"shareables-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n}\n\n// Auto-register core templates on module load\nregisterCorePageTemplates();\n","import type { ComponentType } from \"react\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport { DEFAULT_SDK_WIDGET_REGISTRY } from \"../core/default-widget-registry\";\n\n/**\n * Merges custom widget components from manifests into the default\n * SDK widget registry. Returns the default registry unchanged when\n * no custom widgets are provided (avoids unnecessary object creation).\n */\nexport function buildWidgetRegistry(\n manifests: WidgetManifest[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, ComponentType<any>> {\n if (manifests.length === 0) return DEFAULT_SDK_WIDGET_REGISTRY;\n\n const customEntries = Object.fromEntries(\n manifests.map((m) => [m.type, m.component]),\n );\n\n return { ...DEFAULT_SDK_WIDGET_REGISTRY, ...customEntries };\n}\n","import React, {\n forwardRef,\n type AnchorHTMLAttributes,\n type MouseEvent,\n} from \"react\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface AppLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /** Slug to navigate to (e.g. \"contacts/123\") */\n to: string;\n}\n\n/**\n * SPA-aware link that renders a real `<a href>` for accessibility\n * (right-click, ctrl+click, screen readers) but intercepts normal\n * clicks for client-side navigation.\n */\nexport const AppLink: React.ForwardRefExoticComponent<\n AppLinkProps & React.RefAttributes<HTMLAnchorElement>\n> = forwardRef<HTMLAnchorElement, AppLinkProps>(function AppLink(\n { to, onClick, children, ...rest },\n ref,\n) {\n const { navigate, buildHref } = useAppNavigation();\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {\n // Allow custom onClick handlers to run first\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n // Skip SPA navigation for modifier keys, non-primary button, or external target\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n if (e.button !== 0) return;\n if (rest.target && rest.target !== \"_self\") return;\n\n e.preventDefault();\n navigate(to);\n };\n\n return (\n <a ref={ref} href={buildHref(to)} onClick={handleClick} {...rest}>\n {children}\n </a>\n );\n});\n","/**\n * Scaffold Manifest\n *\n * Tracks which files belong to the portal scaffold and their category.\n * Used by `fluid doctor` to detect drift between a scaffolded portal\n * and the canonical templates shipped with the SDK.\n *\n * Categories:\n * - \"entry\" — App bootstrap files (main.tsx, index.css)\n * - \"config\" — Developer-owned config (portal.config.ts, navigation.config.ts)\n * - \"infrastructure\" — Build/tooling config (vite, tsconfig, index.html, linting)\n */\n\nexport type FileCategory = \"entry\" | \"config\" | \"infrastructure\";\n\nexport interface ManifestEntry {\n readonly category: FileCategory;\n}\n\n/**\n * Canonical scaffold file manifest.\n *\n * Files marked as \"entry\" or \"infrastructure\" are checked by `fluid doctor`.\n * Files marked as \"config\" are developer-owned and intentionally excluded from drift checks.\n */\nexport const SCAFFOLD_MANIFEST = {\n version: 2,\n files: {\n \"src/main.tsx\": { category: \"entry\" },\n \"src/index.css\": { category: \"entry\" },\n \"index.html\": { category: \"infrastructure\" },\n \"tsconfig.json\": { category: \"infrastructure\" },\n \"vite.config.ts\": { category: \"infrastructure\" },\n \".oxlintrc.json\": { category: \"infrastructure\" },\n },\n /** Developer-owned files — never checked for drift */\n developerOwned: [\n \"src/portal.config.ts\",\n \"src/navigation.config.ts\",\n \"src/screens/*\",\n ],\n} as const;\n\nexport type ScaffoldFile = keyof typeof SCAFFOLD_MANIFEST.files;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,kBAAkB;CAC7B,MAAM;CACN,UAAU;CACV,eAAe;CACf,MAAM;CACN,QAAQ;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCD,IAAM,2BAAN,MAA+B;CAC7B,4BAAoB,IAAI,KAA2B;CACnD,aAAqC,EAAE;CAEvC,cAAc;AAEZ,OAAK,aAAa;GAChB;IAAE,IAAI,gBAAgB;IAAM,OAAO;IAAiB,MAAM;IAAQ;GAClE;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IAAE,IAAI,gBAAgB;IAAQ,OAAO;IAAU,MAAM;IAAU;GAChE;;;;;;CAOH,SAAS,UAA8B;AACrC,MAAI,KAAK,UAAU,IAAI,SAAS,GAAG,CACjC,OAAM,IAAI,MACR,0BAA0B,SAAS,GAAG,yBACvC;AAEH,OAAK,UAAU,IAAI,SAAS,IAAI,SAAS;;;;;;;CAQ3C,WAAW,IAAqB;EAC9B,MAAM,WAAW,KAAK,UAAU,IAAI,GAAG;AACvC,MAAI,CAAC,SACH,QAAO;AAET,MAAI,SAAS,QAAQ;AACnB,WAAQ,KACN,yCAAyC,GAAG,iCAC7C;AACD,UAAO;;AAET,SAAO,KAAK,UAAU,OAAO,GAAG;;;;;CAMlC,IAAI,IAAsC;AACxC,SAAO,KAAK,UAAU,IAAI,GAAG;;;;;CAM/B,cAAc,UAAmD;AAC/D,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QACxC,MAAM,EAAE,aAAa,SACvB;;;;;CAMH,UAA0B;AACxB,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC;;;;;CAM5C,WAA2B;AACzB,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,OAAO;;;;;CAMpE,eAA+B;AAC7B,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,CAAC,EAAE,OAAO;;;;;CAMrE,iBAAiC;AAC/B,SAAO,CAAC,GAAG,KAAK,WAAW;;;;;CAM7B,YAAY,UAA8B;AACxC,MAAI,KAAK,WAAW,MAAM,MAAM,EAAE,OAAO,SAAS,GAAG,EAAE;AACrD,WAAQ,KAAK,qBAAqB,SAAS,GAAG,kBAAkB;AAChE;;AAEF,OAAK,WAAW,KAAK,SAAS;;;;;CAMhC,IAAI,IAAqB;AACvB,SAAO,KAAK,UAAU,IAAI,GAAG;;;;;CAM/B,IAAI,OAAe;AACjB,SAAO,KAAK,UAAU;;;;;;CAOxB,eAAqB;AACnB,OAAK,MAAM,CAAC,IAAI,aAAa,KAAK,UAChC,KAAI,CAAC,SAAS,OACZ,MAAK,UAAU,OAAO,GAAG;;;;;;;;;AAYjC,MAAa,uBACX,IAAI,0BAA0B;;;;;;;;;;;;ACpKhC,SAAS,eACP,eACA,WACgB;AAChB,KAAI,CAAC,UAAU,OAEb,QAAO,CAAC,GAAG,cAAc;CAI3B,MAAM,cAAc,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAEzE,QAAO,cAAc,KAAK,WACxB,qBAAqB,QAAQ,YAAY,CAC1C;;;;;;AAOH,SAAS,qBACP,QACA,aACc;CACd,MAAM,WAAW,OAAO,KAAK,YAAY,IAAI,OAAO,GAAG,GAAG,KAAA;CAC1D,MAAM,WAAW,OAAO,MAAM;CAG9B,MAAM,cAAc,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS;AAEjE,KAAI,CAAC,YAAY,CAAC,YAChB,QAAO;CAGT,MAAM,WAAW,WACb;EAAE,GAAG,OAAO;EAAO,GAAG;EAAU,GAChC,EAAE,GAAG,OAAO,OAAO;AAEvB,KAAI,YACF,UAAS,WAAY,SAAqC,KAAK,UAC7D,qBAAqB,OAAO,YAAY,CACzC;AAGH,QAAO;EAAE,GAAG;EAAQ,OAAO;EAAU;;;;;;;;AASvC,SAAS,qBACP,KAC8B;CAC9B,MAAM,WAAW,qBAAqB,IAAI,IAAI,iBAAiB;AAC/D,KAAI,CAAC,UAAU;AACb,UAAQ,KACN,kBAAkB,IAAI,iBAAiB,yBACxC;AACD;;CAKF,MAAM,gBAAgB,IAAI,YACtB,eAAe,SAAS,gBAAgB,IAAI,UAAU,GACtD,CAAC,GAAG,SAAS,eAAe;AAEhC,QAAO;EACL,IAAI,IAAI;EACR,MAAM,SAAS;EACf,MAAM,SAAS;EACf,gBAAgB;EACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCH,SAAgB,uBACd,YACoB;CAEpB,MAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC;CACnE,MAAM,SAA6B,CAAC,GAAG,WAAW,QAAQ;AAG1D,KAAI,WAAW,UACb,MAAK,MAAM,OAAO,WAAW,WAAW;AACtC,MAAI,eAAe,IAAI,IAAI,UAAU,CAEnC;EAGF,MAAM,SAAS,qBAAqB,IAAI;AACxC,MAAI,OACF,QAAO,KAAK,OAAO;;AAKzB,QAAO;;;;;;;AAQT,SAAgB,4BAA4C;AAC1D,QAAO,qBAAqB,SAAS;;;;;;;AAQvC,SAAgB,uBAAuC;AACrD,QAAO,qBAAqB,UAAU;;;;;;;AAQxC,SAAgB,2BAA2C;AACzD,QAAO,qBAAqB,cAAc;;;;;;;;AAS5C,SAAgB,wBAAwB,YAGtC;CACA,MAAM,YAAY,qBAAqB,UAAU;CACjD,MAAM,wBAAwB,IAAI,IAChC,WAAW,WAAW,KAAK,QAAQ,IAAI,iBAAiB,IAAI,EAAE,CAC/D;CAGD,MAAM,mBAAmB,UACtB,QAAQ,SAAS,CAAC,sBAAsB,IAAI,KAAK,GAAG,CAAC,CACrD,KAAK,SAAS,KAAK,GAAG;AAEzB,QAAO;EACL,OAAO,iBAAiB,WAAW;EACnC;EACD;;;;;AChMH,MAAM,kBAA2C,EAAE;AAyBnD,MAAM,sBAAsB,cAC1B,KACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CD,SAAgB,qBAAqB,EACnC,UACA,YAAY,mBACmC;CAE/C,MAAM,gBAAgB,OAAiB,EAAE,CAAC;AAO1C,iBAAgB;EACd,MAAM,aAAuB,EAAE;AAE/B,OAAK,MAAM,YAAY,UACrB,KAAI,CAAC,qBAAqB,IAAI,SAAS,GAAG,CACxC,KAAI;AACF,wBAAqB,SAAS,SAAS;AACvC,cAAW,KAAK,SAAS,GAAG;WACrB,OAAO;AACd,WAAQ,KACN,qCAAqC,SAAS,GAAG,KACjD,MACD;;AAKP,gBAAc,UAAU;AAGxB,eAAa;AACX,QAAK,MAAM,MAAM,cAAc,QAC7B,sBAAqB,WAAW,GAAG;AAErC,iBAAc,UAAU,EAAE;;IAG3B,CA9BiB,UAAU,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CA8BxC,CAAC;CAIjB,MAAM,eAAe,eAEhB;EACC,cAAc;EACd,qBAAqB,qBAAqB,SAAS;EACnD,cAAc,OAAe,qBAAqB,IAAI,GAAG;EACzD,cAAc,OAAe,qBAAqB,IAAI,GAAG;EAC1D,GACH,EAAE,CACH;AAED,QACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAClC;EAC4B,CAAA;;;;;;;;;;;;;;;;;;;;;;;AAyBnC,SAAgB,mBAA6C;CAC3D,MAAM,UAAU,WAAW,oBAAoB;AAC/C,KAAI,CAAC,QACH,OAAM,IAAI,MACR,8DACD;AAEH,QAAO;;;;;;;;;AAUT,SAAgB,iBAAiB,YAA4C;CAC3E,MAAM,EAAE,iBAAiB,kBAAkB;AAC3C,QAAO,cAAc,aAAa,WAAW,EAAE,CAAC,cAAc,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpJ5E,SAAgB,UAAU,EACxB,UAAU,sDACV,QAAQ,2BACR,YACoC;AACpC,QACE,oBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,SAAS;GACT,YACE;GACF,iBAAiB;GACjB,OAAO;GACR;YAED,qBAAC,OAAD;GACE,OAAO;IACL,UAAU;IACV,WAAW;IACX,SAAS;IACT,iBAAiB;IACjB,cAAc;IACd,WACE;IACH;aATH;IAYE,oBAAC,OAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,QAAQ;MACR,iBAAiB;MACjB,cAAc;MACd,SAAS;MACT,YAAY;MACZ,gBAAgB;MACjB;eAED,qBAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,QAAO;MACP,aAAY;MACZ,eAAc;MACd,gBAAe;gBARjB,CAUE,oBAAC,QAAD;OAAM,GAAE;OAAI,GAAE;OAAK,OAAM;OAAK,QAAO;OAAK,IAAG;OAAI,IAAG;OAAM,CAAA,EAC1D,oBAAC,QAAD,EAAM,GAAE,4BAA6B,CAAA,CACjC;;KACF,CAAA;IAEN,oBAAC,MAAD;KACE,OAAO;MACL,UAAU;MACV,YAAY;MACZ,cAAc;MACd,OAAO;MACR;eAEA;KACE,CAAA;IAEL,oBAAC,KAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO;MACP,cAAc,WAAW,WAAW;MACpC,YAAY;MACb;eAEA;KACC,CAAA;IAEH;IACG;;EACF,CAAA;;AAIV,MAAMA,kBAAgB;;;;AAKtB,SAASC,oBAAwB;AAC/B,KAAI,OAAO,aAAa,YAAa;AACrC,KAAI,SAAS,eAAeD,gBAAc,CAAE;CAE5C,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,KAAKA;AACX,OAAM,cAAc;AACpB,UAAS,KAAK,YAAY,MAAM;;;;;AAMlC,SAAgB,cAAiC;AAC/C,iBAAgB;AACd,qBAAiB;IAChB,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,YACE;GACF,iBAAiB;GAClB;YAVH,CAYE,oBAAC,OAAD,EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,gBAAgB;GAChB,cAAc;GACd,WAAW;GACZ,EACD,CAAA,EACF,oBAAC,KAAD;GACE,OAAO;IACL,WAAW;IACX,OAAO;IACP,UAAU;IACX;aACF;GAEG,CAAA,CACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtHV,SAAgB,YAAY,EAC1B,UACA,WAAW,oBAAC,aAAD,EAAe,CAAA,EAC1B,iBAAiB,oBAAC,WAAD,EAAa,CAAA,IACQ;CACtC,MAAM,EAAE,iBAAiB,WAAW,UAAU,cAAc;AAG5D,KAAI,UACF,QAAO,oBAAA,YAAA,EAAA,UAAG,UAAY,CAAA;AAIxB,KAAI,CAAC,mBAAmB,MACtB,QAAO,oBAAA,YAAA,EAAA,UAAG,gBAAkB,CAAA;AAI9B,QAAO,oBAAA,YAAA,EAAG,UAAY,CAAA;;;;AC/ExB,MAAM,oBAAoB;AAG1B,SAAgB,cAAuB;CACrC,MAAM,CAAC,UAAU,eAAe,SAAkB,MAAM;AAExD,iBAAgB;EACd,MAAM,MAAM,OAAO,WAAW,eAAe,oBAAoB,EAAE,KAAK;EACxE,MAAM,iBAAiB;AACrB,eAAY,OAAO,aAAa,kBAAkB;;AAEpD,MAAI,iBAAiB,UAAU,SAAS;AACxC,cAAY,OAAO,aAAa,kBAAkB;AAClD,eAAa,IAAI,oBAAoB,UAAU,SAAS;IACvD,EAAE,CAAC;AAEN,QAAO;;;;ACJT,SAAS,GAAG,GAAG,QAAsB;AACnC,QAAO,QAAQ,KAAK,OAAO,CAAC;;AAG9B,SAAS,UAAU,EACjB,WACA,cAAc,cACd,GAAG,SAIF;AACD,QACE,oBAAC,OAAD;EACE,MAAK;EACL,oBAAkB;EAClB,WAAW,GACT,sBACA,gBAAgB,eAAe,gBAAgB,eAC/C,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAS,SAAS,EAChB,WACA,GAAG,SACoC;AACvC,QACE,oBAAC,OAAD;EACE,WAAW,GAAG,qCAAqC,UAAU;EAC7D,GAAI;EACJ,CAAA;;AAQN,MAAM,gBAAgB;AACtB,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,4BAA4B;AAkBlC,MAAa,iBACXE,QAAM,cAA0C,KAAK;AAEvD,SAAS,aAAkC;CACzC,MAAM,UAAUA,QAAM,WAAW,eAAe;AAChD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,oDAAoD;AAGtE,QAAO;;AAOT,MAAM,kBASFA,QAAM,YAYN,EACE,cAAc,MACd,MAAM,UACN,cAAc,aACd,eACA,aACA,cAAc,mBAAmB,OACjC,WACA,OACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,iBAAiB,aAAa;CAEpC,MAAM,WACJ,kBAAkB,KAAA,IAAY,gBAAgB,MAAM;CAEtD,MAAM,gBAAgB,kBAAkB,KAAA,KAAa,CAAC,CAAC;CACvD,MAAM,CAAC,YAAY,iBAAiBA,QAAM,SAAS,MAAM;CAIzD,MAAM,CAAC,OAAO,YAAYA,QAAM,SAAS,YAAY;CACrD,MAAM,OAAO,YAAY;CACzB,MAAM,UAAUA,QAAM,aACnB,UAAmD;EAClD,MAAM,YAAY,OAAO,UAAU,aAAa,MAAM,KAAK,GAAG;AAC9D,MAAI,YACF,aAAY,UAAU;MAEtB,UAAS,UAAU;IAGvB,CAAC,aAAa,KAAK,CACpB;CAGD,MAAM,gBAAgBA,QAAM,kBAAkB;AAC5C,SAAO,WACH,eAAe,SAAS,CAAC,KAAK,GAC9B,SAAS,SAAS,CAAC,KAAK;IAC3B;EAAC;EAAU;EAAS;EAAc,CAAC;AAGtC,SAAM,gBAAgB;EACpB,MAAM,iBAAiB,UAAyB;AAC9C,OACE,MAAM,QAAQ,8BACb,MAAM,WAAW,MAAM,UACxB;IAEA,MAAM,gBAAgB,SAAS;AAM/B,QAJE,eAAe,QAAQ,oBAAoB,IAC3C,eAAe,QAAQ,iBAAiB,IACxC,eAAe,UAAU,SAAS,cAAc,CAGhD;AAGF,UAAM,gBAAgB;AACtB,mBAAe;;;AAInB,SAAO,iBAAiB,WAAW,cAAc;AACjD,eAAa,OAAO,oBAAoB,WAAW,cAAc;IAChE,CAAC,cAAc,CAAC;CAInB,MAAM,QAAQ,OAAO,aAAa;CAElC,MAAM,eAAeA,QAAM,eAClB;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACf,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,oBAAC,eAAe,UAAhB;EAAyB,OAAO;YAC9B,oBAAC,OAAD;GACE,OACE;IACE,mBAAmB;IACnB,wBAAwB;IACxB,GAAG;IACJ;GAEH,WAAW,GACT,oDACA,UACD;GACI;GACL,GAAI;GAEH;GACG,CAAA;EACkB,CAAA;EAG/B;AACD,gBAAgB,cAAc;AAM9B,MAAM,UAMFA,QAAM,YASN,EACE,OAAO,QACP,UAAU,WACV,cAAc,aACd,WACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,EACJ,UACA,OACA,YACA,eACA,eACA,iBACE,YAAY;CAGhB,MAAM,eACJ,UAAU,aAAa,gBAAgB;AAGzC,KAAI,gBAAgB,SAClB,QAAO;AAGT,KAAI,gBAAgB,OAClB,QACE,oBAAC,OAAD;EACE,WAAW,GACT,sFACA,gBAAgB,WAAW,YAC3B,UACD;EACI;EACL,GAAI;EAEH;EACG,CAAA;AAIV,KAAI,UAAU;EAGZ,MAAM,gBAAgB,gBAAgB,aAAa;AACnD,SACE,qBAAA,YAAA,EAAA,UAAA,CAEG,cACC,oBAAC,OAAD;GACE,WAAW,GAAG,eAAe,2BAA2B;GACxD,eAAe,cAAc,MAAM;GACnC,eAAY;GACZ,CAAA,EAIJ,oBAAC,OAAD;GACE,gBAAa;GACb,eAAY;GACZ,WAAW,GACT,eACA,qIACA,aAAa,kBAAkB,qBAC/B,UACD;GACD,OACE,EACE,mBAAmB,sBACpB;GAEE;GACL,GAAI;aAEJ,oBAAC,OAAD;IAAK,WAAU;IAA+B;IAAe,CAAA;GACzD,CAAA,CACL,EAAA,CAAA;;AAIP,QACE,qBAAC,OAAD;EACO;EACL,WAAU;EACV,cAAY;EACZ,oBAAkB,UAAU,cAAc,cAAc;EACxD,gBAAc;EACd,aAAW;EACX,OACE,EACE,mBAAmB,cACpB;YAVL,CAcE,oBAAC,OAAD,EACE,WAAW,GACT,uEACA,0CACA,sCACA,YAAY,cAAc,YAAY,UAClC,qFACA,yDACL,EACD,CAAA,EACF,oBAAC,OAAD;GACE,WAAW,GACT,uHACA,gBAAgB,WAAW,SAC3B,SAAS,SACL,mFACA,oFAEJ,YAAY,cAAc,YAAY,UAClC,6FACA,2FACJ,UACD;GACD,GAAI;aAEJ,oBAAC,OAAD;IACE,gBAAa;IACb,WAAU;IAET;IACG,CAAA;GACF,CAAA,CACF;;EAGX;AACD,QAAQ,cAAc;AAMtB,MAAM,cAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;CAChC,MAAM,EAAE,kBAAkB,YAAY;AAEtC,QACE,oBAAC,UAAD;EACO;EACL,gBAAa;EACb,cAAW;EACX,UAAU;EACV,SAAS;EACT,OAAM;EACN,WAAW,GACT,+PACA,4EACA,0HACA,2JACA,6DACA,6DACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,YAAY,cAAc;AAM1B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;CAChC,MAAM,EAAE,kBAAkB,YAAY;AACtC,QACE,oBAAC,QAAD;EACO;EACL,WAAW,GACT,iCACA,gBACI,wCACA,2EACJ,mNACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAM3B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,SAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,uJACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAM3B,MAAM,gBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,2BAA2B,UAAU;EACnD,GAAI;EACJ,CAAA;EAGP;AACD,cAAc,cAAc;AAE5B,MAAM,gBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,2BAA2B,UAAU;EACnD,GAAI;EACJ,CAAA;EAGP;AACD,cAAc,cAAc;AAM5B,MAAM,mBAIFA,QAAM,YAKP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAClC,QACE,oBAAC,WAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,iCAAiC,UAAU;EACzD,GAAI;EACJ,CAAA;EAEJ;AACF,iBAAiB,cAAc;AAM/B,MAAM,iBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,gMACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,eAAe,cAAc;AAM7B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,mHACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAE3B,MAAM,oBAIFA,QAAM,YAGP,EAAE,WAAW,UAAU,OAAO,GAAG,SAAS,QAAQ;AAGnD,QACE,oBAHW,UAAU,OAAO,OAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,4OACA,4EACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,kBAAkB,cAAc;AAEhC,MAAM,qBAIFA,QAAM,YAGP,EAAE,WAAW,UAAU,OAAO,GAAG,SAAS,QAAQ;AAGnD,QACE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,8RAEA,iDACA,wCACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,mBAAmB,cAAc;AAEjC,MAAM,sBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,OAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,kBAAkB,UAAU;CAC1C,GAAI;CACJ,CAAA,CAEL;AACD,oBAAoB,cAAc;AAMlC,MAAM,cAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,sCAAsC,UAAU;CAC9D,GAAI;CACJ,CAAA,CAEL;AACD,YAAY,cAAc;AAE1B,MAAM,kBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,4BAA4B,UAAU;CACpD,GAAI;CACJ,CAAA,CAEL;AACD,gBAAgB,cAAc;AAM9B,MAAM,4BAoBQ,IACZ,o1BACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;GACH;EACD,MAAM;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACL;EACF;CACD,iBAAiB;EACf,SAAS;EACT,MAAM;EACP;CACF,CACF;AAED,MAAM,oBAMFA,QAAM,YAQN,EACE,UAAU,OACV,WAAW,OACX,UAAU,WACV,OAAO,WACP,WACA,GAAG,SAEL,QACG;AAcH,QAVE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,aAAW;EACX,eAAa;EACb,WAAW,GAAG,0BAA0B;GAAE;GAAS;GAAM,CAAC,EAAE,UAAU;EACtE,GAAI;EACJ,CAAA;EAKP;AACD,kBAAkB,cAAc;AAMhC,MAAM,oBAKFA,QAAM,YAMP,EAAE,WAAW,UAAU,OAAO,cAAc,OAAO,GAAG,SAAS,QAAQ;AAGxE,QACE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,oVAEA,iDACA,yCACA,gDACA,2CACA,wCACA,eACE,4LACF,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,kBAAkB,cAAc;AAEhC,MAAM,mBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,OAAD;CACO;CACL,gBAAa;CACb,WAAW,GACT,0KACA,4HACA,yCACA,gDACA,2CACA,wCACA,UACD;CACD,GAAI;CACJ,CAAA,CAEL;AACD,iBAAiB,cAAc;AAE/B,MAAM,sBAIFA,QAAM,YAKP,EAAE,WAAW,WAAW,OAAO,GAAG,SAAS,QAAQ;CAEpD,MAAM,QAAQA,QAAM,cAAc;AAChC,SAAO,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,GAAG,GAAG,GAAG;IAC7C,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,+CAA+C,UAAU;EACvE,GAAI;YAJN,CAMG,YACC,oBAAC,UAAD;GACE,WAAU;GACV,gBAAa;GACb,CAAA,EAEJ,oBAAC,UAAD;GACE,WAAU;GACV,gBAAa;GACb,OACE,EACE,oBAAoB,OACrB;GAEH,CAAA,CACE;;EAER;AACF,oBAAoB,cAAc;AAMlC,MAAM,iBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GACT,kGACA,wCACA,UACD;CACD,GAAI;CACJ,CAAA,CAEL;AACD,eAAe,cAAc;AAE7B,MAAM,qBAEFA,QAAM,YACP,EAAE,GAAG,SAAS,QAAQ,oBAAC,MAAD;CAAS;CAAK,GAAI;CAAS,CAAA,CACnD;AACD,mBAAmB,cAAc;AAEjC,MAAM,uBAMFA,QAAM,YAOP,EAAE,UAAU,OAAO,OAAO,MAAM,UAAU,WAAW,GAAG,SAAS,QAAQ;AAG1E,QACE,oBAHW,UAAU,OAAO,KAG5B;EACO;EACL,gBAAa;EACb,aAAW;EACX,eAAa;EACb,WAAW,GACT,ifACA,0FACA,SAAS,QAAQ,WACjB,SAAS,QAAQ,WACjB,wCACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,qBAAqB,cAAc;;;;;;;;;;;ACv4BnC,SAAgB,eAAe,EAC7B,gBACA,eACA,UACA,eACA,eACA,cACA,eAAe,SAC0B;AACzC,QACE,oBAAC,iBAAD;EAA+B;YAC7B,qBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,qBAAC,SAAD,EAAA,UAAA;KACG,iBAAiB,oBAAC,eAAD,EAAA,UAAgB,eAA8B,CAAA;KAChE,oBAAC,gBAAD;MAAgB,WAAU;gBAAO;MAAgC,CAAA;KAChE,iBAAiB,oBAAC,eAAD,EAAA,UAAgB,eAA8B,CAAA;KACxD,EAAA,CAAA;IAGV,qBAAC,cAAD;KAAc,WAAU;eAAxB,CAEG,eAGD,oBAAC,OAAD;MACE,WAAW,mDAAmD,eAAe,uDAAuD;gBAEpI,oBAAC,OAAD;OAAK,WAAU;OACZ;OACG,CAAA;MACF,CAAA,CACO;;IAGd;IACG;;EACU,CAAA;;;;ACjDtB,MAAM,mBAAmB,cAA4C,KAAK;AAE1E,MAAM,iBACJ,OAAO,WAAW,cACd,OAAO,WAAW,+BAA+B,GACjD;AAEN,SAAS,8BAA8B,UAAsB;AAC3D,iBAAgB,iBAAiB,UAAU,SAAS;AACpD,cAAa,gBAAgB,oBAAoB,UAAU,SAAS;;AAGtE,SAAS,uBAAgC;AACvC,QAAO,gBAAgB,WAAW;;AAGpC,SAAS,oBAA6B;AACpC,QAAO;;AAWT,SAAgB,kBAAkB,EAChC,UACA,MACA,cACA,kBAAkB,QAC0B;CAO5C,MAAM,aANoB,qBACxB,+BACA,sBACA,kBACD,GAEmD,SAAS;CAE7D,MAAM,cAA2B,SAAS,SAAS,aAAa;CAEhE,MAAM,YAAY,kBAAkB;AAClC,MAAI,iBAAiB;GAEnB,MAAM,SAAsB,gBAAgB,UAAU,SAAS;AAC/D,gBAAa,WAAW,aAAa,SAAS,OAAO;QAGrD,cAAa,SAAS,UAAU,SAAS,QAAQ;IAElD;EAAC;EAAM;EAAa;EAAY;EAAc;EAAgB,CAAC;CAElE,MAAM,gBAAgB,sBAAsB,KAAK;CAEjD,MAAM,QAAQ,eACL;EACL;EACA;EACA,SAAS;EACT;EACA;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,oBAAC,iBAAiB,UAAlB;EAAkC;EAC/B;EACyB,CAAA;;;AAKhC,SAAgB,eAAsC;CACpD,MAAM,MAAM,WAAW,iBAAiB;AACxC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAO;;;AAIT,SAAgB,sBAAsB,MAAqC;AACzE,QAAO,SAAS,SAAS,KAAA,IAAY;;;;AC/GvC,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACD;AAMD,MAAa,0BAGT;CACF,gBAAgB;EACd,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,WAAW;EACT,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,QAAQ;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CAOD,eAAe;EACb,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,mBAAmB;EACjB,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,kBAAkB;EAChB,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,MAAM;EACJ,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,eAAe;EACb,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACF;;;;;AAcD,SAAgB,uBACd,MAC8B;AAC9B,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,wBAAwB,SAAS,KAA6B;;;;;AAevE,SAAgB,+BAGd;CACA,MAAM,WAAmD,EAAE;CAC3D,MAAM,4BAAY,IAAI,KAAa;AACnC,MAAK,MAAM,QAAQ,OAAO,OAAO,wBAAwB,EAAE;AACzD,MAAI,KAAK,QAAQ,UAAU,IAAI,KAAK,KAAK,CAAE;AAC3C,MAAI,KAAK,KAAM,WAAU,IAAI,KAAK,KAAK;EACvC,MAAM,UAAU,KAAK,WAAW;AAChC,MAAI,CAAC,SAAS,SACZ,UAAS,WAAW,EAAE;AAExB,WAAS,SAAS,KAAK,KAAK;;AAE9B,QAAO;;;;;AAMT,SAAgB,cAAc,MAAkC;AAC9D,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,GAAG;;;;;;;;ACnKhD,SAAS,cAAc,MAA4C;CACjE,MAAM,OAAO,wBAAwB;AACrC,QAAO;EACL,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,UAAU,EAAE;EACb;;AAGH,SAAS,cAAc,OAA+B;AACpD,QAAO;EAAE;EAAO,UAAU,EAAE;EAAE;;;;;;;AAQhC,SAAgB,uBAAyC;AACvD,QAAO;EACL,cAAc,cAAc;EAC5B,cAAc,OAAO;EACrB,cAAc,WAAW;EACzB,cAAc,WAAW;EACzB,cAAc,UAAU;EACxB,cAAc,mBAAmB;EACjC,cAAc,iBAAiB;EAChC;;;;AC3BH,SAAgB,aAAa,EAC3B,SAC8C;CAC9C,MAAM,EAAE,eAAe,aAAa,YAAY;CAChD,MAAM,EAAE,SAAS,gBAAgB,wBAAwB;AAEzD,KAAI,CAAC,SAAS,CAAC,YAAa,QAAO;AAEnC,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACG,CAAC,YACA,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;IACV,cAAW;cAEX,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;IACzB,CAAA,EACT,oBAAC,OAAD,EAAK,WAAU,2BAA4B,CAAA,CAC1C,EAAA,CAAA;GAEJ,cACC,oBAAC,OAAD;IAAK,WAAU;cAAkB;IAAkB,CAAA,GAEnD,oBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA;GAEnE,WACC,oBAAC,OAAD;IAAK,WAAU;cAAmC;IAAc,CAAA;GAE9D;;;;;ACLV,MAAMC,aAAuC;CAC3C,gBAAgB;CAChB,iBAAiB;CACjB,MAAM;CACN,KAAK;CACL,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,iBAAiB;CACjB,eAAe;CACf,SAAS;CACT,SAAS;CACT,UAAU;CACV,MAAM;CACN,cAAc;CACd,cAAc;CACd,QAAQ;CACR,eAAe;CACf,MAAM;CACN,OAAO;CACP,kBAAkB;CAClB,OAAO;CACP,OAAO;CACP,OAAO;CACP,cAAc;CACd,oBAAoB;CACpB,SAAS;CACT,MAAM;CACN,OAAO;CACP,qBAAqB;CACrB,MAAM;CACN,aAAa;CACb,OAAO;CACR;AAOD,SAAgB,QAAQ,EAAE,MAAM,aAA8C;AAE5E,QAAO,oBADMA,WAAS,SAAS,MACxB,EAAiB,WAAa,CAAA;;;;;;;AC3EvC,MAAM,iBAAiB;;;;AAKvB,SAAS,cAAc,iBAAyB,cAA8B;AAC5E,QAAO,GAAG,eAAe,GAAG,gBAAgB,GAAG;;;;;AAMjD,SAAgB,kBACd,iBACA,cACe;AACf,KAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,KAAI;EACF,MAAM,MAAM,cAAc,iBAAiB,aAAa;AACxD,SAAO,aAAa,QAAQ,IAAI;UACzB,OAAO;AACd,UAAQ,KAAK,qCAAqC,MAAM;AACxD,SAAO;;;;;;;;ACpBX,SAAgB,YACd,cACA,cACS;CACT,MAAM,yBAAyB,cAAc,aAAa;AAE1D,MACG,CAAC,aAAa,YAAY,aAAa,SAAS,UAAU,MAC3D,aAAa;MAEc,cAAc,aAAa,KAAK,KAChC,uBACzB,QAAO;OAGT,QAAO,aAAa,SAAS,MAAM,UAA0B;AAE3D,SAD4B,cAAc,MAAM,KAAK,KACtB;GAC/B;AAGJ,QAAO;;;;;AAMT,SAAgB,oBACd,MACA,iBACe;AAEf,KAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AAE7C,MAAI,KAAK,MAAM;GACb,MAAM,kBAAkB,kBAAkB,iBAAiB,KAAK,KAAK;AAGrE,OAAI,iBAAiB;IACnB,MAAM,wBAAwB,cAAc,gBAAgB;AAK5D,QAJoB,KAAK,SAAS,MAAM,UAA0B;AAEhE,YAD4B,cAAc,MAAM,KAAK,KACtB;MAC/B,CAEA,QAAO;;;EAMb,MAAM,aAAa,KAAK,SAAS;AACjC,MAAI,YAAY,KACd,QAAO,cAAc,WAAW,KAAK;AAGvC,SAAO;;AAIT,KAAI,KAAK,aAAa,KAAK,KACzB,QAAO,cAAc,KAAK,KAAK;AAIjC,KAAI,KAAK,KACP,QAAO,cAAc,KAAK,KAAK;AAGjC,QAAO;;;;AC1DT,SAAgB,aAAa,EAC3B,UACA,aACA,YACA,kBAAkB,GAClB,kBAAkB,KAC4B;CAC9C,MAAM,EAAE,UAAU,iBAAiB,YAAY;CAC/C,MAAM,CAAC,UAAU,eAAeC,QAAM,SAAS,MAAM;AAErD,KAAI,CAAC,YAAY,CAAC,aAAc,QAAO;CAEvC,MAAM,iBAAiB,SACpB,QAAQ,SAAS,CAAC,KAAK,UAAU,CACjC,QAAQ,SAAS,KAAK,QAAS,KAAK,YAAY,KAAK,SAAS,SAAS,EAAG;CAE7E,MAAM,cAAc,eAAe,SAAS;CAC5C,MAAM,eAAe,cACjB,eAAe,MAAM,GAAG,kBAAkB,EAAE,GAC5C;CACJ,MAAM,gBAAgB,cAClB,eAAe,MAAM,kBAAkB,EAAE,GACzC,EAAE;CAEN,MAAM,mBAAmB,SAAyB;EAChD,MAAM,SAAS,oBAAoB,MAAM,gBAAgB;AACzD,MAAI,OAAQ,YAAW,OAAO;AAC9B,cAAY,MAAM;;AAGpB,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACG,aAAa,KAAK,SAAS;AAE1B,UACE,qBAAC,UAAD;IAEE,MAAK;IACL,eAAe,gBAAgB,KAAK;IACpC,WAAW,2GANE,YAAY,MAAM,YAAY,GAO9B,iBAAiB;cALhC,CAQG,KAAK,OACJ,oBAAC,SAAD;KAAS,MAAM,KAAK;KAAM,WAAU;KAAW,CAAA,GAE/C,oBAAC,QAAD,EAAM,WAAU,UAAW,CAAA,EAE7B,oBAAC,QAAD;KAAM,WAAU;eAAyB,KAAK;KAAa,CAAA,CACpD;MAbF,KAAK,MAAM,KAAK,QAAQ,KAAK,MAa3B;IAEX,EAED,eACC,qBAAC,UAAD;GACE,MAAK;GACL,eAAe,YAAY,KAAK;GAChC,WAAW,2GACT,cAAc,MAAM,SAAS,YAAY,MAAM,YAAY,CAAC,GACxD,iBACA;aANR,CASE,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA,EAC/B,oBAAC,QAAD,EAAA,UAAM,QAAW,CAAA,CACV;KAEP;KAEL,YACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GACE,WAAU;GACV,eAAe,YAAY,MAAM;GACjC,eAAY;GACZ,CAAA,EACF,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KAAM,WAAU;eAAwC;KAEjD,CAAA,EACP,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,YAAY,MAAM;KACjC,WAAU;KACV,cAAW;eAEX,oBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;KACjB,CAAA,CACL;OACN,oBAAC,OAAD;IAAK,WAAU;cACZ,cAAc,KAAK,SAAS;AAE3B,YACE,qBAAC,UAAD;MAEE,MAAK;MACL,eAAe,gBAAgB,KAAK;MACpC,WAAW,+FANE,YAAY,MAAM,YAAY,GAQrC,uDACA;gBAPR,CAUG,KAAK,QACJ,oBAAC,SAAD;OAAS,MAAM,KAAK;OAAM,WAAU;OAAW,CAAA,EAEjD,oBAAC,QAAD,EAAA,UAAO,KAAK,OAAa,CAAA,CAClB;QAbF,KAAK,MAAM,KAAK,QAAQ,KAAK,MAa3B;MAEX;IACE,CAAA,CACF;KACF;IAEP,EAAA,CAAA;;;;;;;;;ACzHP,MAAa,qBAAqB,CAAC,SAAS,MAAM;;;;;;;AAQlD,MAAM,mBACJ,OAAO,WAAW,eAAe,CAAC,OAAO,KAAK,KAAK,MAC/C,iBAAiB,GACjB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCN,SAAgB,YAAY,SAEG;CAC7B,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAKL;EACA,UAAU,SAAS,mBAAmB;EACtC,eAAe,IAAI,IAAI,QAAQ;EAC/B,QAAQ;EACR,GAAI,oBAAoB,EAAE,WAAW,iBAAiB,aAAa;EACnE,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,SAAS;EACnE,CAAC;;;;ACvEJ,MAAM,gBAAgB;;;;AAKtB,SAAS,kBAAwB;AAC/B,KAAI,OAAO,aAAa,YAAa;AACrC,KAAI,SAAS,eAAe,cAAc,CAAE;CAE5C,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,KAAK;AACX,OAAM,cAAc;AACpB,UAAS,KAAK,YAAY,MAAM;;;;;;;AAQlC,SAAgB,kBAAqC;AACnD,iBAAgB;AACd,mBAAiB;IAChB,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,YACE;GACF,iBAAiB;GAClB;YAVH,CAYE,oBAAC,OAAD,EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,gBAAgB;GAChB,cAAc;GACd,WAAW;GACZ,EACD,CAAA,EACF,oBAAC,KAAD;GACE,OAAO;IACL,WAAW;IACX,OAAO;IACP,UAAU;IACX;aACF;GAEG,CAAA,CACA;;;;;ACpCV,SAAgB,mBAAmB,EACjC,MACA,UACA,aACA,cAC6C;CAC7C,MAAM,CAAC,MAAM,WAAW,SAAS,SAAS;AAE1C,iBAAgB;AACd,MAAI,SAAU,SAAQ,KAAK;IAC1B,CAAC,SAAS,CAAC;AAEd,QACE,oBAAC,aAAD;EACQ;EACN,cAAc;EACd,SAAA;EACA,WAAU;YAEV,qBAAC,iBAAD,EAAA,UAAA,CACE,oBAAC,oBAAD;GAAoB,SAAA;aAClB,qBAAC,mBAAD,EAAA,UAAA;IACG,KAAK,QAAQ,oBAAC,SAAD,EAAS,MAAM,KAAK,MAAQ,CAAA;IAC1C,oBAAC,QAAD;KAAM,WAAU;eAAY,KAAK;KAAa,CAAA;IAC9C,oBAAC,cAAD,EAAc,WAAU,kGAAmG,CAAA;IACzG,EAAA,CAAA;GACD,CAAA,EACrB,oBAAC,oBAAD,EAAA,UACE,oBAAC,gBAAD,EAAA,UACG,KAAK,SAAS,KAAK,UAAU;GAC5B,MAAM,YAAY,cAAc,MAAM,KAAK;AAG3C,UACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;IACE,UALgB,cAAc,YAAY,KAAK;IAM/C,eAAe,WAAW,UAAU;cAFtC,CAIE,oBAAC,SAAD;KAAS,MAAM,MAAM,QAAQ;KAAQ,WAAU;KAAY,CAAA,EAC3D,oBAAC,QAAD,EAAA,UAAO,MAAM,OAAa,CAAA,CACR;OACJ,EARI,MAAM,QAAQ,MAAM,MAQxB;IAEpB,EACa,CAAA,EACE,CAAA,CACL,EAAA,CAAA;EACN,CAAA;;;;ACrDlB,SAAgB,cAAc,EAC5B,UACA,aACA,cACwC;AACxC,QACE,oBAAC,aAAD,EAAA,UACG,SAAS,KAAK,MAAM,UAAU;AAE7B,MAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAO,QAAO;EAEtC,MAAM,WAAW,YAAY,MAAM,YAAY;AAI/C,MAHoB,KAAK,UAAU,SAAS,EAI1C,QACE,oBAAC,oBAAD;GAEQ;GACI;GACG;GACD;GACZ,EALK,KAAK,QAAQ,KAAK,MAKvB;AAKN,MAAI,CAAC,KAAK,KACR,QACE,oBAAC,mBAAD,EAAA,UACG,KAAK,OACY,EAFI,KAAK,MAAM,WAAW,QAE1B;EAKxB,MAAM,WAAW,cAAc,KAAK,KAAK;AAEzC,SACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;GACY;GACV,eAAe,WAAW,SAAS;aAFrC,CAIG,KAAK,QAAQ,oBAAC,SAAD,EAAS,MAAM,KAAK,MAAQ,CAAA,EAC1C,oBAAC,QAAD;IAAM,WAAU;cAAY,KAAK;IAAa,CAAA,CAC5B;MACJ,EARI,KAAK,MAAM,SAQf;GAEpB,EACU,CAAA;;;;AC5DlB,SAAgB,mBAAmB,EACjC,cAC6C;CAC7C,MAAM,CAAC,MAAM,WAAW,SAAS,MAAM;CACvC,MAAM,WAAW,OAAuB,KAAK;CAC7C,MAAM,YAAY,OAA0B,KAAK;CAEjD,MAAM,QAAQ,kBAAkB,QAAQ,MAAM,EAAE,EAAE,CAAC;AAGnD,iBAAgB;AACd,MAAI,CAAC,KAAM;EAEX,MAAM,mBAAmB,MAAkB;AACzC,OACE,SAAS,WACT,CAAC,SAAS,QAAQ,SAAS,EAAE,OAAe,IAC5C,UAAU,WACV,CAAC,UAAU,QAAQ,SAAS,EAAE,OAAe,CAE7C,QAAO;;EAIX,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,EAAE,QAAQ,SAAU,QAAO;;AAGjC,WAAS,iBAAiB,aAAa,gBAAgB;AACvD,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa;AACX,YAAS,oBAAoB,aAAa,gBAAgB;AAC1D,YAAS,oBAAoB,WAAW,cAAc;;IAEvD,CAAC,MAAM,MAAM,CAAC;CAEjB,MAAM,WAAW,8BAA8B;CAE/C,MAAM,mBAAmB,SAAiB;AACxC,aAAW,KAAK;AAChB,SAAO;;AAGT,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,eAAe,SAAS,SAAS,CAAC,KAAK;GACvC,WAAU;GACV,cAAW;GACX,iBAAe;aAEf,oBAAC,YAAD,EAAY,WAAU,WAAY,CAAA;GAC3B,CAAA,EAER,QACC,oBAAC,OAAD;GACE,KAAK;GACL,WAAU;aAEV,qBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,YAAD,EAAY,WAAU,iCAAkC,CAAA,EACxD,oBAAC,MAAD;MAAI,WAAU;gBAAwC;MAEjD,CAAA,CACD;QAGN,oBAAC,OAAD;KAAK,WAAU;eACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,aAAa,WAC3C,qBAAC,OAAD;MAAuB,WAAU;gBAAjC,CACE,oBAAC,MAAD;OAAI,WAAU;iBACX;OACE,CAAA,EACL,oBAAC,OAAD;OAAK,WAAU;iBACZ,MAAM,KAAK,SACV,qBAAC,UAAD;QAEE,MAAK;QACL,eAAe,gBAAgB,KAAK,KAAM;QAC1C,WAAU;kBAJZ,CAMG,KAAK,QACJ,oBAAC,SAAD;SACE,MAAM,KAAK;SACX,WAAU;SACV,CAAA,EAEJ,oBAAC,QAAD;SAAM,WAAU;mBAAY,KAAK;SAAa,CAAA,CACvC;UAZF,KAAK,KAYH,CACT;OACE,CAAA,CACF;QAtBI,YAsBJ,CACN;KACE,CAAA,CACF;;GACF,CAAA,CAEJ;;;;;ACvGV,SAAgB,wBAAwB,EACtC,cACkD;CAClD,MAAM,WAAW,8BAA8B;AAE/C,QACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,aAAa,WAC3C,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,MAAD;GAAI,WAAU;aACX;GACE,CAAA,EACL,oBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,SACV,qBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AACb,SAAI,KAAK,KAAM,YAAW,KAAK,KAAK;;IAEtC,WAAU;cANZ,CAQG,KAAK,QAAQ,oBAAC,SAAD;KAAS,MAAM,KAAK;KAAM,WAAU;KAAW,CAAA,EAC7D,oBAAC,QAAD;KAAM,WAAU;eAAW,KAAK;KAAa,CAAA,CACtC;MATF,KAAK,KASH,CACT;GACE,CAAA,CACF,EAAA,EAnBI,YAmBJ,CACN;EACE,CAAA;;;;ACxBV,SAAS,YAAY,MAAyC;AAC5D,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,MAAM;AACtC,KAAI,MAAM,UAAU,EAClB,UACG,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,SAAS,KAAK,MAAM,KACzD,aAAa;AAEjB,SAAQ,KAAK,MAAM,KAAK,aAAa;;AAGvC,SAAgB,qBAAqB,EACnC,YACA,YACsD;CACtD,MAAM,EAAE,UAAU,iBAAiB,YAAY;CAC/C,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,OAAO,YAAY;CACzB,MAAM,YAAY,cAAc;CAEhC,MAAM,iBAAiB,aACpB,SAAiB;AAChB,YAAU,MAAM;AAChB,aAAW,KAAK;IAElB,CAAC,WAAW,CACb;AAED,KAAI,CAAC,YAAY,CAAC,aAAc,QAAO;CAEvC,MAAM,WAAW,YAAY,MAAM,KAAK;AAExC,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;EACE,MAAK;EACL,eAAe,UAAU,KAAK;EAC9B,WAAU;EACV,cAAW;YAEV,MAAM,WACL,oBAAC,OAAD;GACE,KAAK,KAAK;GACV,KAAK,KAAK,QAAQ;GAClB,WAAU;GACV,CAAA,GAEF,oBAAC,QAAD;GAAM,WAAU;aACb;GACI,CAAA;EAEF,CAAA,EAER,UACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cAAwC;IAEjD,CAAA,EACP,oBAAC,UAAD;IACE,MAAK;IACL,eAAe,UAAU,MAAM;IAC/B,WAAU;IACV,cAAW;cAEX,oBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;IACjB,CAAA,CACL;MAEN,qBAAC,OAAD;GAAK,WAAU;aAAf;IACG,QACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,KAAK,WACJ,oBAAC,OAAD;MACE,KAAK,KAAK;MACV,KAAK,KAAK,QAAQ;MAClB,WAAU;MACV,CAAA,GAEF,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA,EAET,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,KAAK,QACJ,oBAAC,KAAD;OAAG,WAAU;iBACV,KAAK;OACJ,CAAA,EAEL,KAAK,SACJ,oBAAC,KAAD;OAAG,WAAU;iBACV,KAAK;OACJ,CAAA,CAEF;QACF;;IAGR,oBAAC,OAAD;KAAK,WAAU;eACb,qBAAC,UAAD;MACE,MAAK;MACL,eAAe,eAAe,eAAe;MAC7C,WAAU;gBAHZ,CAKE,oBAAC,YAAD,EAAY,WAAU,UAAW,CAAA,EACjC,oBAAC,QAAD,EAAA,UAAM,gBAAmB,CAAA,CAClB;;KACL,CAAA;IAEN,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,yBAAD,EAAyB,YAAY,gBAAkB,CAAA;KACnD,CAAA;IAEN,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,UAAD;MACE,MAAK;MACL,SAAS,UAAU;MACnB,WAAU;gBAHZ,CAKG,UAAU,SAAS,SAClB,oBAAC,MAAD,EAAM,WAAU,UAAW,CAAA,GAE3B,oBAAC,KAAD,EAAK,WAAU,UAAW,CAAA,EAE5B,qBAAC,QAAD,EAAA,UAAA,CAAM,WACI,UAAU,SAAS,UAAU,UAAU,OAC1C,EAAA,CAAA,CACA;SAER,YACC,qBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAHZ,CAKE,oBAAC,QAAD,EAAQ,WAAU,UAAW,CAAA,EAC7B,oBAAC,QAAD,EAAA,UAAM,WAAc,CAAA,CACb;QAEP;;IACF;KACF;IAEP,EAAA,CAAA;;;;AC5IP,SAAgB,UAAU,EACxB,YACA,aACA,YACA,YACoC;CACpC,MAAM,UAAU,YAAY;CAC5B,MAAM,YAAY,cAAc;CAEhC,MAAM,YAAY,UAAU,SAAS,SAAS,OAAO;CACrD,MAAM,wBAAwB,QAAQ,YAAY,QAAQ;AAE1D,QACE,qBAAC,UAAD;EAAQ,WAAU;YAAlB,CACG,wBACC,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,sBAAD;KAAkC;KAAsB;KAAY,CAAA;IAChE,CAAA;GACF,CAAA,GAEN,qBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,QAAQ,YAAY,CAAC,QAAQ,gBAC5B,oBAAC,UAAD;KACE,MAAK;KACL,SAAS,QAAQ;KACjB,WAAU;KACV,cAAW;eAEX,oBAAC,MAAD,EAAM,WAAU,UAAW,CAAA;KACpB,CAAA;IAIX,oBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;IAG1B,oBAAC,oBAAD,EAAgC,YAAc,CAAA;IAG9C,oBAAC,UAAD;KACE,MAAK;KACL,SAAS,UAAU;KACnB,WAAU;KACV,cAAY,0BAA0B,UAAU,KAAK;eAErD,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;KACzB,CAAA;IACL;MAIP,yBAAyB,cAAc,WAAW,SAAS,KAC1D,oBAAC,OAAD;GAAK,WAAU;aACZ,WAAW,KAAK,QAAQ;IACvB,MAAM,UAAU,cAAc,IAAI,KAAK;AACvC,QAAI,CAAC,QAAS,QAAO;AAIrB,WACE,oBAAC,UAAD;KAEE,MAAK;KACL,eAAe,WAAW,QAAQ;KAClC,WAAW,kFAPE,cAAc,YAAY,KAAK,UAStC,mCACA;eAGL,IAAI;KACE,EAVF,IAAI,MAAM,QAUR;KAEX;GACE,CAAA,CAED;;;;;;;;;;;;;;;;;;ACrEb,SAAgB,iBAAiB,SAK/B;CACA,MAAM,MAAM,aAAa;CACzB,MAAM,cAAc,gBAAgB;CACpC,MAAM,eAAe,OAAO,MAAM;CAKlC,MAAM,YAAY,OAAO,SAAS,OAAO;AACzC,WAAU,UAAU,SAAS;CAE7B,MAAM,EAAE,QAAQ,WAAW,SAAS,UAAU,YAAY;EACxD,aAAa,cAAsB,IAAI,MAAM,cAAc,UAAU;EACrE,WAAW,OAAO,SAAS;AAGzB,eAAY,OAAO;AACnB,OAAI;AACF,UAAM,eAAe,iBAAiB;WAChC;AAIR,cAAW,KAAK,QAAQ,KAAK,UAAU,QAAQ;AAC/C,UAAO,SAAS,QAAQ;;EAE1B,eAAe;AACb,gBAAa,UAAU;;EAE1B,CAAC;AAWF,QAAO;EACL,eAVoB,aACnB,cAAsB;AACrB,OAAI,aAAa,QAAS;AAC1B,gBAAa,UAAU;AACvB,UAAO,UAAU;KAEnB,CAAC,OAAO,CACT;EAIY;EACF;EACF;EACR;;;;AChEH,SAAgB,mBAAmB,EACjC,WACA,eACA,iBAAiB,OACjB,YAC4B;CAC5B,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,MAAM;CACjE,MAAM,CAAC,kBAAkB,uBAAuB,SAAwB,KAAK;CAC7E,MAAM,sBAAsB,OAC1B,KACD;AAGD,iBAAgB;AACd,mBAAiB,KAAK;IACrB,CAAC,eAAe,GAAG,CAAC;AAGvB,iBAAgB;AACd,eAAa;AACX,OAAI,oBAAoB,YAAY,KAClC,cAAa,oBAAoB,QAAQ;;IAG5C,EAAE,CAAC;CAEN,MAAM,oBAAoB,cAAc;AACtC,MAAI,CAAC,cAAe,QAAO;EAC3B,MAAM,OAAO,cAAc,aAAa;AACxC,SAAO,UAAU,QAAQ,MAAM,EAAE,KAAK,aAAa,CAAC,SAAS,KAAK,CAAC;IAClE,CAAC,WAAW,cAAc,CAAC;CAE9B,MAAM,QAAQ,kBAAkB;AAC9B,YAAU,MAAM;IACf,EAAE,CAAC;CAEN,MAAM,sBAAsB,kBAAkB;AAC5C,MAAI,oBAAoB,YAAY,KAClC,cAAa,oBAAoB,QAAQ;AAE3C,sBAAoB,UAAU,iBAAiB;AAC7C,oBAAiB,KAAK;AACtB,uBAAoB,UAAU;KAC7B,IAAM;IACR,EAAE,CAAC;CAEN,MAAM,sBAAsB,aACzB,OAAe;AACd,MAAI,OAAO,eAAe,GAAI;AAC9B,MAAI,kBAAkB,KAAM;AAE5B,MAAI,gBAAgB;AAClB,UAAO;AACP,oBAAiB,GAAG;AACpB,uBAAoB,GAAG;AACvB,wBAAqB,KAAK;AAC1B;;AAIF,mBAAiB,GAAG;AACpB,WAAS,GAAG;AACZ,SAAO;AACP,mBAAiB,GAAG;AACpB,uBAAqB;IAEvB;EACE,eAAe;EACf;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,sBAAsB,kBAAkB;AAC5C,MAAI,qBAAqB,KAAM;AAC/B,mBAAiB,iBAAiB;AAClC,WAAS,iBAAiB;AAC1B,SAAO;AACP,mBAAiB,GAAG;AACpB,sBAAoB,KAAK;AACzB,uBAAqB;AACrB,uBAAqB,MAAM;IAC1B;EAAC;EAAkB;EAAU;EAAO;EAAoB,CAAC;CAE5D,MAAM,qBAAqB,kBAAkB;AAC3C,sBAAoB,KAAK;AACzB,uBAAqB,MAAM;AAC3B,mBAAiB,GAAG;IACnB,EAAE,CAAC;AASN,QAAO;EACL;EACA;EACA;EACA,WAXuB,aAAa,SAAkB;AACtD,aAAU,KAAK;AACf,OAAI,CAAC,KACH,kBAAiB,GAAG;KAErB,EAAE,CAAC;EAOJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;AC3HH,SAAgB,aAAa,EAC3B,KACA,KACA,QAKC;CACD,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;AAEzC,KAAI,CAAC,OAAO,MACV,QACE,oBAAC,WAAD;EACE,OAAO;GAAE,QAAQ;GAAM,OAAO;GAAM;EACpC,WAAU;EACV,CAAA;AAGN,QACE,oBAAC,OAAD;EACE,WAAU;EACL;EACA;EACL,OAAO;EACP,QAAQ;EACR,eAAe,SAAS,KAAK;EAC7B,CAAA;;;;AC1BN,SAAgB,YAAY,EAC1B,UACA,UACA,MACA,UAAU,SACoC;AAE9C,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,cAAD;GAAc,KAAK;GAAM,KAJX,YAAY,YAAY;GAIK,MAAM;GAAM,CAAA;EACnD,CAAA,EACN,oBAAC,OAAD;EACE,WAAWC,KACT,wCACA,UAAU,kBAAkB,SAC7B;YAED,oBAAC,QAAD;GAAM,WAAU;aAA0B;GAAY,CAAA;EAClD,CAAA,CACL,EAAA,CAAA;;;;ACbP,SAAgB,YAAY,EAC1B,WACA,eACA,aACA,iBACmB;AACnB,KAAI,CAAC,UAAU,OACb,QAAO,oBAAC,MAAD;EAAI,WAAU;YAA0B;EAAqB,CAAA;AAEtE,QACE,oBAAA,YAAA,EAAA,UACG,UAAU,KAAK,YAAY;EAC1B,MAAM,WAAW,QAAQ,OAAO,eAAe;EAC/C,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,aAAa,YAAY,kBAAkB;AAEjD,SACE,qBAAC,UAAD;GACE,MAAK;GAEL,UAAU;GACV,eAAe,YAAY,QAAQ,GAAG;GACtC,WAAWC,KACT,wDACA,aACI,kCACA,+DACJ,YAAY,oCACZ,eAAe,6BAChB;aAZH,CAcE,oBAAC,aAAD;IACE,UAAU,QAAQ;IAClB,UAAU,QAAQ;IAClB,MAAM,QAAQ;IACd,CAAA,EACD,eACC,oBAAC,QAAD;IAAM,WAAU;cAAwC;IAEjD,CAAA,CAEF;KAtBF,QAAQ,GAsBN;GAEX,EACD,CAAA;;;;AChCP,SAAgB,wBAAwB,EACtC,QACA,cACA,eACA,gBACA,WACA,eACA,aACA,eACA,WAC+B;AAC/B,QACE,qBAAC,cAAD;EAAc,MAAM;EAAsB;YAA1C,CACE,oBAAC,qBAAD;GAAqB,SAAA;aAAS;GAA8B,CAAA,EAC5D,qBAAC,qBAAD;GACE,WAAU;GACV,OAAM;GACN,MAAK;GACL,YAAY;aAJd;IAME,oBAAC,OAAD;KACE,MAAK;KACL,aAAY;KACZ,OAAO;KACP,WAAW,MAAM,eAAe,EAAE,OAAO,SAAS,GAAG;KACrD,WAAU;KACV,aAAY;KACZ,gBAAe;KACf,YAAY;KACZ,CAAA;IACF,oBAAC,mBAAD;KAAmB,WAAU;eAAgC;KAEzC,CAAA;IACpB,oBAAC,aAAD;KACa;KACE;KACE;KACA;KACf,CAAA;IACkB;KACT;;;;;ACvCnB,SAAgB,qBAAqB,EACnC,QACA,cACA,eACA,gBACA,WACA,eACA,aACA,eACA,WAC4B;AAC5B,QACE,qBAAC,OAAD;EAAO,MAAM;EAAsB;YAAnC,CACE,oBAAC,cAAD;GAAc,SAAA;aAAS;GAAuB,CAAA,EAC9C,oBAAC,cAAD;GACE,MAAK;GACL,WAAU;aAEV,qBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,MAAD;OAAI,WAAU;iBAAwC;OAEjD,CAAA,EACL,oBAAC,YAAD;OACE,MAAM;OACN,eAAe,aAAa,MAAM;OAClC,WAAU;OACV,cAAW;OACX,CAAA,CACE;;KAGN,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,OAAD;OACE,MAAK;OACL,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,eAAe,EAAE,OAAO,SAAS,GAAG;OACrD,WAAU;OACV,aAAY;OACZ,gBAAe;OACf,YAAY;OACZ,CAAA;MACE,CAAA;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,MAAD;QAAI,WAAU;kBAAgC;QAAc,CAAA;OACxD,CAAA,EACN,oBAAC,aAAD;OACa;OACE;OACE;OACA;OACf,CAAA,CACE;;KACF;;GACO,CAAA,CACT;;;;;AC/DZ,SAAgB,6BAA6B,EAC3C,MACA,cACA,WACA,YACoC;CACpC,MAAM,eAAe,OAAO,MAAM;AAElC,QACE,oBAAC,aAAD;EACQ;EACN,eAAe,WAAW;AACxB,gBAAa,OAAO;AACpB,OAAI,CAAC,UAAU,CAAC,aAAa,QAC3B,WAAU;AAEZ,OAAI,CAAC,OAAQ,cAAa,UAAU;;YAGtC,qBAAC,oBAAD,EAAA,UAAA,CACE,qBAAC,mBAAD,EAAA,UAAA;GACE,oBAAC,kBAAD,EAAA,UACE,oBAAC,eAAD,EAAe,WAAU,kBAAmB,CAAA,EAC3B,CAAA;GACnB,oBAAC,kBAAD,EAAA,UAAkB,mBAAkC,CAAA;GACpD,oBAAC,wBAAD,EAAA,UAAwB,4FAGC,CAAA;GACP,EAAA,CAAA,EACpB,qBAAC,mBAAD,EAAA,UAAA,CACE,oBAAC,mBAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,oBAAC,mBAAD;GACE,eAAe;AACb,iBAAa,UAAU;AACvB,eAAW;;aAEd;GAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;EACT,CAAA;;;;AChDlB,SAAgB,gBAAgB,EAC9B,WACA,eACA,MACA,UACA,gBACA,UACA,WACgC;CAChC,MAAM,WAAW,mBAAmB;EAClC;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,CAAC,KAAM,QAAO;CAElB,MAAM,iBACJ,qBAAC,UAAD;EACE,MAAK;EACL,WAAU;YAFZ,CAIE,oBAAC,aAAD;GACE,UAAU,eAAe;GACzB,UAAU,eAAe;GACzB,MAAM,eAAe,QAAQ;GAC7B,SAAS;GACT,CAAA,EACD,WACC,oBAAC,cAAD,EAAc,WAAU,oDAAqD,CAAA,GAE7E,oBAAC,aAAD,EAAa,WAAU,uDAAwD,CAAA,CAE1E;;CAGX,MAAM,iBAAiB,WAAW;AAElC,QACE,qBAAA,YAAA,EAAA,UAAA,CACG,WACC,oBAAC,sBAAD;EACE,QAAQ,SAAS;EACjB,cAAc,SAAS;EACvB,eAAe,SAAS;EACxB,gBAAgB,SAAS;EACzB,WAAW,SAAS;EACL;EACf,aAAa,SAAS;EACtB,eAAe,SAAS;EACxB,SAAS;EACT,CAAA,GAEF,oBAAC,yBAAD;EACE,QAAQ,SAAS;EACjB,cAAc,SAAS;EACvB,eAAe,SAAS;EACxB,gBAAgB,SAAS;EACzB,WAAW,SAAS;EACL;EACf,aAAa,SAAS;EACtB,eAAe,SAAS;EACxB,SAAS;EACT,CAAA,EAGJ,oBAAC,8BAAD;EACE,MAAM,SAAS;EACf,cAAc,SAAS;EACvB,WAAW,SAAS;EACpB,UAAU,SAAS;EACnB,CAAA,CACD,EAAA,CAAA;;;;ACtFP,SAAgB,wBAAwB,EAAE,QAA2B;AACnE,KAAI,CAAC,KAAM,QAAO;AAElB,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAACC,YAAD,EAAU,WAAU,yCAA0C,CAAA,EAC9D,oBAACA,YAAD,EAAU,WAAU,uBAAwB,CAAA,CACxC;;;;;;;;;ACKV,SAAgB,qBAA+C;CAC7D,MAAM,EAAE,MAAM,MAAM,cAAc,gBAAgB;CAClD,MAAM,EAAE,kBAAkB,kBAAkB;CAC5C,MAAM,EAAE,aAAa,YAAY;CAEjC,MAAM,YAAuB,cAEzB,MAAM,WAAW,KAAK,OAAO;EAC3B,IAAI,EAAE;EACN,MAAM,EAAE;EACR,UAAU,EAAE,YAAY;EACxB,UAAU,EAAE,YAAY;EACzB,EAAE,IAAI,EAAE,EACX,CAAC,MAAM,UAAU,CAClB;CAID,MAAM,kBAAkB,MAAM,SAAS;CACvC,MAAM,gBAAgB,cACd,UAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,EACrD,CAAC,WAAW,gBAAgB,CAC7B;AAED,KAAI,UACF,QAAO,oBAAC,yBAAD,EAAyB,MAAA,MAAO,CAAA;AAGzC,KAAI,UAAU,UAAU,EACtB,QAAO;AAGT,QACE,oBAAC,iBAAD;EACa;EACI;EACf,MAAA;EACU;EACV,UAAU;EACV,CAAA;;;;AChDN,MAAa,uBAAuB;CAClC;CACA;CACA;CACD;;AAWD,MAAa,sBAA0C,qBAAqB,KACzE,UAAU;CACT,KAAK;CACL,OAAO,wBAAwB,MAAM;CACrC;CACD,EACF;;ACyMD,MAAM,aAAa,IAAI,IA/KgC;CACrD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,mBAAmB;GACjB;GACA;GACA;GACA;GACD;EACD,WAAW;GACT,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAY,OAAO;KAAY;IACxC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC1C;KAAE,OAAO;KAAoB,OAAO;KAAoB;IACxD;KAAE,OAAO;KAAc,OAAO;KAAc;IAC7C;GACD,cAAc;GACf;EACD,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAY,OAAO;KAAO;IACpC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,mBAAmB;GACjB;GACA;GACA;GACA;GACD;EACD,WAAW;GACT,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAY,OAAO;KAAY;IACxC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC1C;KAAE,OAAO;KAAoB,OAAO;KAAoB;IACxD;KAAE,OAAO;KAAc,OAAO;KAAc;IAC7C;GACD,cAAc;GACf;EACD,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAY,OAAO;KAAO;IACpC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,WAAW;EACX,mBAAmB,CAAC,cAAc;EAClC,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAO,OAAO;KAAO;IAC9B;KAAE,OAAO;KAAQ,OAAO;KAAQ;IAChC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAY,OAAO;KAAY;IACzC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EAEb,UACE;EACF,YAAY;EACZ,WAAW;EACX,mBAAmB,CAAC,cAAc;EAClC,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAO,OAAO;KAAO;IAC9B;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC3C;GACD,cAAc;GACf,CACF;EACF;CACF,CAIqB,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAC1C;;;;;AAMD,SAAgB,cAAc,UAAgD;AAC5E,QAAO,WAAW,IAAI,SAAS;;;;;;;;AASjC,SAAgB,cAAoC,QAAc;AAChE,KAAI,OAAO,SAAS,SAAS,CAAC,OAAO,SAAU,QAAO;CAEtD,MAAM,SAAS,cAAc,OAAO,SAAS;AAC7C,KAAI,CAAC,QAAQ;AACX,UAAQ,KACN,gCAAgC,OAAO,SAAS,kCACjD;AACD,SAAO;;AAGT,QAAO;EACL,GAAG;EACH,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,WAAW,OAAO,aAAa,OAAO;EACvC;;;;;;;;;AC7OH,SAAS,aAAa,QAA4B;AAChD,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,UAAU,OAAO,YACnB,IAAI,KAAK,UAAU,OAAO,UAAU,KACpC;AAGJ,SAAO,GADU,OAAO,YAAY,OAAO,WACtB;;AAEvB,KAAI,OAAO,SAAS,SAKlB,QAAO,WAHL,OAAO,eAAe,KAAK,SAAS;AAClC,SAAO,GAAG,KAAK,cAAc,GAAG,KAAK;GACrC,IAAI,EAAE,EACgB,KAAK,IAAI;AAErC,KAAI,OAAO,SAAS,SAClB,QAAO,UAAU,OAAO,WAAW,GAAG,OAAO;AAE/C,QAAO;;;;;;AAOT,SAAS,wBACP,cAKA,SACA,UACA,aACe;AACf,KAAI,QAAQ,WAAW,EAAG,QAAO,KAAA;AACjC,KAAI,aAAa,MAAM,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,CAAE,QAAO,KAAA;CAG7D,MAAM,WAAW,CAAC,CADE,aAAa,MAAM,MAAM,EAAE,MAAM,EACrB;AAEhC,KAAI,YAAY,aAAa,SAC3B,QAAO,YAAY;AAGrB,KAAI,SACF;CAGF,MAAM,gBAAyC,EAAE;AAEjD,MAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;EAC5C,MAAM,QAAQ,aAAa;EAC3B,MAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,OAAO,QAAQ,CAAC,OAAQ;AAG7B,MAAI,CAAC,OAAO,YAAa;EAGzB,IAAI,SAAkB,MAAM;AAC5B,MAAI,OAAO,WAAW;GACpB,MAAM,cAAc,SAAS,aAAa,OAAO;AACjD,OAAI,YACF,UAAS,YAAY,QAAQ,OAAO;OAEpC,SAAQ,KAAK,cAAc,OAAO,UAAU,yBAAyB;;AAKzE,MACE,WAAW,QACX,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,OAAO,IACtB,OAAO,YAAY,SAAS,GAC5B;GACA,MAAM,YAAY;AAClB,QAAK,MAAM,QAAQ,OAAO,YACxB,KAAI,QAAQ,UACV,eAAc,QAAQ,UAAU;QAIpC,MAAK,MAAM,QAAQ,OAAO,YACxB,eAAc,QAAQ;;AAK5B,QAAO;;;;;;;;;AAUT,SAAgB,cACd,QACA,SACqB;CACrB,MAAM,SAAS,6BAA6B;CAC5C,MAAM,WAAW,SAAS,YAAY,OAAO;CAC7C,MAAM,UAAU,SAAS,WAAW,OAAO;CAC3C,MAAM,gBAAgB,OAAO;CAC7B,MAAM,YAAY,OAAO;CAGzB,MAAM,UAAU,eACP,OAAO,YAAY,WAAW,EAAE,EAAE,IAAI,cAAc,EAC3D,CAAC,OAAO,YAAY,QAAQ,CAC7B;CACD,MAAM,cAAc,OAAO,YAAY;CACvC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,aAAa,OAAO;CAE1B,MAAM,kBAAkB,OAAuC,EAAE,CAAC;CAGlE,MAAM,UAAU,WAAW,EACzB,SAAS,QAAQ,KAAK,YAAwB;EAC5C,UAAU;GACR;GACA,aAAa,OAAO;GACpB;GACA;GACD;EACD,SAAS,OAAO,EACd,aAGsB;GACtB,MAAM,UAA6B;IACjC;IACA;IACA;IACA;IACA;IACA;IACD;GAED,MAAM,UAAU,SAAS,SAAS,OAAO;AACzC,OAAI,CAAC,QACH,OAAM,IAAI,MACR,0CAA0C,OAAO,OAClD;AAGH,UAAO,QAAQ,QAAQ,QAAQ;;EAEjC,SAAS,QAAQ,SAAS;EAC1B,OAAO,aAAa,cAAc;EAClC,YAAY,aAAa,cAAc;EACvC,iBAAiB,OAAO,mBAAmB;EAC5C,EAAE,EACJ,CAAC;AAGF,iBAAgB,UAAU;CAG1B,MAAM,YAAY,QAAQ,MAAM,MAAM,EAAE,UAAU;CAGlD,MAAM,cAAc,QAAQ,MAAM,MAAM,EAAE,MAAM;CAChD,MAAM,QACJ,aAAa,iBAAiB,QAC1B,YAAY,QACZ,aAAa,QACX,IAAI,MAAM,OAAO,YAAY,MAAM,CAAC,GACpC;CAGR,MAAM,mBAAmB,QACtB,KAAK,MAAM,GAAG,EAAE,cAAc,GAAG,EAAE,YAAY,CAC/C,KAAK,IAAI;CAGZ,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;AAqBrB,QAAO;EACL,MApBW,cAAc;AAMzB,UAAO,wBALgB,WAAW,QAAQ,KAAK,OAAO;IACpD,MAAM,EAAE;IACR,WAAW,EAAE;IACb,OAAO,EAAE,iBAAiB,QAAQ,EAAE,QAAQ;IAC7C,EAAE,EAGD,SACA,UACA,YACD;KACA;GAAC;GAAkB;GAAS;GAAU;GAAY,CAAC;EASpD;EACA;EACA,SARc,kBAAkB;AAChC,mBAAgB,QAAQ,SAAS,MAAM,EAAE,SAAS,CAAC;KAClD,EAAE,CAAC;EAOL;;;;ACvOH,SAAgB,aAAgC;AAC9C,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,KAAD;GAAG,WAAU;aAAwB;GAAwB,CAAA,EAC7D,oBAAC,KAAD;GAAG,WAAU;aAAgC;GAEzC,CAAA,CACA;;;;;ACYV,MAAM,eAAwC,EAAE;;;;;AAMhD,SAAS,oBAAoB,EAC3B,QACA,WACA,iBACA,kBACA,gBACA,WACiD;CAEjD,MAAM,wBAAwB,mBAAmB;CACjD,MAAM,EAAE,MAAM,WAAW,UAAU,cAAc,QAAQ,EAAE,SAAS,CAAC;CAMrE,MAAM,cAAc,eACX;EACL,GAAG,OAAO;EACV,GAAG;EACH,GAAG;EACJ,GACD;EAAC,OAAO;EAAO;EAAM;EAAsB,CAC5C;AAGD,KAAI,aAAa,OAAO,YAAY,SAAS,iBAAiB,OAAO;AACnE,MAAI,iBACF,QAAO,oBAAA,YAAA,EAAA,UAAG,kBAAoB,CAAA;AAGhC,SACE,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,WAAW,KAAK;aAEzB,oBAAC,QAAD;IAAM,WAAU;cAAU;IAAiB,CAAA;GACvC,CAAA;;AAKV,KAAI,SAAS,CAAC,MAAM;AAClB,MAAI,eACF,QAAO,oBAAA,YAAA,EAAA,UAAG,eAAe,MAAM,EAAI,CAAA;AAGrC,SAAO,oBAAC,YAAD,EAAc,CAAA;;AAGvB,QAAO,oBAAC,WAAD,EAAW,GAAI,aAAe,CAAA;;;;;;AAOvC,MAAa,kBACX,KAAK,oBAAoB;;;;;;;;;;;;;AC/D3B,SAAS,eAAe,EAAE,QAAQ,SAA8B;CAE9D,MAAM,YADW,aAAa,CACH,OAAO;AAElC,KAAI,CAAC,WAAW;AACd,UAAQ,KACN,oCAAoC,OAAO,OAAO,KAAK,CAAC,yBACzD;AACD,SAAO;;AAGT,KAAI,OAAO,WACT,QACE,oBAAC,OAAD;EAA8B,WAAU;YACtC,oBAAC,iBAAD;GAAyB;GAAmB;GAAa,CAAA;EACrD,EAFI,OAAO,MAAM,MAEjB;AAIV,QACE,oBAAC,OAAD;EAA8B,WAAU;YACtC,oBAAC,WAAD,EAAW,GAAI,OAAO,OAAS,CAAA;EAC3B,EAFI,OAAO,MAAM,MAEjB;;;;;;;AAgBV,SAAS,sBAAsB,EAC7B,QACA,aACmD;CACnD,MAAM,UAAU,OAAO;AAEvB,KAAI,CAAC,WAAW,QAAQ,WAAW,EACjC,QACE,oBAAC,OAAD;EAAK,WAAW,aAAa;YAC3B,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,KAAD,EAAA,UAAG,mCAAmC,CAAA;GAClC,CAAA;EACF,CAAA;AAIV,QACE,oBAAC,OAAD;EAAK,WAAW,aAAa;YAC1B,QAAQ,KAAK,QAAQ,UAAU;AAC9B,OAAI,CAAC,OAAQ,QAAO;AACpB,UACE,oBAAC,gBAAD;IAEU;IACD;IACP,EAHK,OAAO,MAAM,MAGlB;IAEJ;EACE,CAAA;;AAIV,MAAa,oBAET,KAAK,sBAAsB;;;ACvF/B,MAAM,WAAkD;CACtD,SAAS;CACT,QAAQ;CACR,eAAe;CAChB;AAED,MAAM,YAAY,oBAAoB,KAAK,SAAS;CAClD,GAAG;CACH,MAAM,SAAS,IAAI;CACpB,EAAE;AAEH,SAAS,aAAa,MAAiC;CACrD,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;AAC7B,KAAI,SAAS,SAAU,QAAO;AAC9B,KAAI,SAAS,gBAAiB,QAAO;AACrC,QAAO;;AAGT,SAAgB,oBAAoB,EAClC,YAGoB;CACpB,MAAM,EAAE,aAAa,aAAa,kBAAkB;CACpD,MAAM,YAAY,aAAa,YAAY;AAE3C,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,oBAAC,OAAD;IAAK,WAAU;cACZ,UAAU,KAAK,EAAE,KAAK,MAAM,OAAO,MAAM,WACxC,qBAAC,UAAD;KAEE,MAAK;KACL,eAAe,SAAS,KAAK;KAC7B,WAAW,sFACT,cAAc,MACV,uDACA;eAPR,CAUE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,MACM;OAXF,IAWE,CACT;IACE,CAAA;GAGN,oBAAC,OAAD;IAAK,WAAU;cACZ,UAAU,KAAK,EAAE,KAAK,MAAM,OAAO,MAAM,WACxC,qBAAC,UAAD;KAEE,MAAK;KACL,eAAe,SAAS,KAAK;KAC7B,WAAW,8EACT,cAAc,MACV,uDACA;eAPR,CAUE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,MACM;OAXF,IAWE,CACT;IACE,CAAA;GAGN,oBAAC,OAAD;IAAK,WAAU;IAAkB;IAAe,CAAA;GAC5C;;;;;ACzEV,MAAMC,kBAAgB,WACpB,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAA4B,MAAM,OAAO,EAC9C,SAAS,EAAE,eACZ,EAAE,CACJ;AACD,MAAMC,iBAAe,WACnB,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,cACZ,EAAE,CACJ;AACD,MAAMC,wBAAsB,WAC1B,OAAO,sCAAA,MAAA,MAAA,EAAA,EAAA,CAAkC,MAAM,OAAO,EACpD,SAAS,EAAE,qBACZ,EAAE,CACJ;AACD,MAAMC,oBAAkB,WACtB,OAAO,kCAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,iBACZ,EAAE,CACJ;AACD,MAAMC,mBAAiB,WACrB,OAAO,iCAAA,MAAA,MAAA,EAAA,EAAA,CAA6B,MAAM,OAAO,EAC/C,SAAS,EAAE,gBACZ,EAAE,CACJ;AACD,MAAMC,eAAa,WACjB,OAAO,6BAAA,MAAA,MAAA,EAAA,EAAA,CAAyB,MAAM,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CACzE;AACD,MAAMC,oBAAkB,WACtB,OAAO,kCAAA,MAAA,MAAA,EAAA,EAAA,CAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,iBACZ,EAAE,CACJ;AACD,MAAMC,mBAAiB,WACrB,OAAO,iCAA6B,MAAM,OAAO,EAC/C,SAAS,EAAE,gBACZ,EAAE,CACJ;AACD,MAAMC,qBAAmB,WACvB,OAAO,mCAA+B,MAAM,OAAO,EACjD,SAAS,EAAE,kBACZ,EAAE,CACJ;AACD,MAAMC,iBAAe,WACnB,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,cACZ,EAAE,CACJ;AACD,MAAM,gBAAgB,WACpB,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAA4B,MAAM,OAAO,EAC9C,SAAS,EAAE,eACZ,EAAE,CACJ;AACD,MAAM,oBAAoB,WACxB,OAAO,oCAAA,MAAA,MAAA,EAAA,EAAA,CAAgC,MAAM,OAAO,EAClD,SAAS,EAAE,mBACZ,EAAE,CACJ;AAED,MAAa,yBAAwD;CACnE,SAAST;CACT,QAAQC;CACR,eAAeC;CACf,UAAUC;CACV,UAAUC;CACV,MAAMC;CACN,WAAWC;CACX,UAAUC;CACV,WAAWE;CACX,kBAAkBD;CAClB,iBAAiBA;CACjB,eAAeA;CACf,mBAAmBA;CACnB,kBAAkBA;CAClB,eAAeA;CACf,SAAS;CACT,gBAAgB;CACjB;;AAGD,MAAM,qBAAqB,OAAO,KAAK,uBAAuB,CAAC,MAC5D,GAAG,MAAM,EAAE,SAAS,EAAE,OACxB;;AAGD,SAAgB,mBAAmB,MAAyC;AAC1E,MAAK,MAAM,OAAO,mBAChB,KAAI,SAAS,OAAO,KAAK,WAAW,MAAM,IAAI,CAC5C,QAAO,uBAAuB;;;;AC9EpC,MAAM,gBAAgB,IAAI,IAAY,qBAAqB;AAc3D,SAAgB,WAAW,EACzB,aACA,gBACA,aACA,SACA,UACA,cACqC;CAErC,MAAM,aAAa,cAAc;AAC/B,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,KAAA;AAC7C,SAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC,QAAQ,CAAC;CAGb,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,KAAA;AAC7C,SAAO,IAAI,IAAI,QAAQ,QAAQ,MAAM,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC,QAAQ,CAAC;CAGb,MAAM,gBAAgB,cAAc;AAClC,MAAI,gBAAgB,aAAa,YAAY;GAC3C,MAAM,OAAO,WAAW,IAAI,eAAe,UAAU;AACrD,OAAI,KAAM,QAAO;;AAEnB,MAAI,aACF,QACE,aAAa,IAAI,YAAY,IAAI,aAAa,IAAI,SAAS,IAAI,KAAA;IAIlE;EACD,gBAAgB;EAChB;EACA;EACA;EACA;EACD,CAAC;CAGF,MAAM,kBAAkB,cAAc;AACtC,KAAI,gBACF,QAAO,oBAAC,iBAAD;EAAiB,MAAM;EAAa,QAAQ;EAAc,CAAA;AAInE,KAAI,aAAa,aAAa;EAC5B,MAAM,mBAAmB,cAAc;AACvC,MAAI,iBACF,QAAO,oBAAC,kBAAD;GAAkB,MAAM;GAAa,QAAQ;GAAc,CAAA;;CAQtE,MAAM,eACJ,uBAAuB,aAAa,mBAAmB,YAAY;AACrE,KAAI,cAAc;EAChB,MAAM,UACJ,oBAAC,UAAD;GACE,UACE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;IAC9F,CAAA;aAGR,oBAAC,cAAD,EAAgB,CAAA;GACP,CAAA;AAGb,MAAI,cAAc,IAAI,SAAS,CAC7B,QAAO,oBAAC,qBAAD,EAAA,UAAsB,SAA8B,CAAA;AAG7D,SAAO;;AAIT,KAAI,uBAAuB,SAAS,CAClC,QAAO,oBAAC,uBAAD,EAAuB,MAAM,gBAAgB,SAAS,UAAY,CAAA;AAI3E,KAAI,cACF,QAAO,oBAAC,mBAAD,EAAmB,QAAQ,eAAiB,CAAA;AAIrD,QAAO,oBAAC,uBAAD,EAAuB,MAAM,gBAAgB,SAAS,aAAe,CAAA;;;;;;;;;ACxG9E,SAAgB,gBAAgB,OAAmC;CACjE,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,KAAM,OAAM,KAAK,cAAc,KAAK,KAAK,CAAC;AACnD,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,MAAM,KAAM,OAAM,KAAK,cAAc,MAAM,KAAK,CAAC;;AAIzD,QAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,OAAO;;;;;;AAO7E,SAAgB,gBACd,UACA,UACuB;CACvB,MAAM,aAAa,cAAc,SAAS;AAC1C,KAAI,CAAC,WAAY,QAAO,KAAA;AAExB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,eAAe,QACjB,QAAO;GAAE,aAAa;GAAS,MAAM;GAAI;AAE3C,MAAI,WAAW,WAAW,UAAU,IAAI,CACtC,QAAO;GACL,aAAa;GACb,MAAM,WAAW,MAAM,QAAQ,SAAS,EAAE;GAC3C;;;;;;;;;;;;;AAiBP,SAAgB,wBACd,UACA,UACQ;AAER,KAAI,aAAa,IACf,QAAO,SAAS,QAAQ,OAAO,GAAG;AAIpC,KAAI,SAAS,WAAW,SAAS,CAC/B,QAAO,SAAS,MAAM,SAAS,OAAO,CAAC,QAAQ,OAAO,GAAG;AAI3D,QAAO,SAAS,QAAQ,OAAO,GAAG;;AAGpC,SAAgB,gBACd,MACA,aACA,UACS;CAET,MAAM,WADQ,gBAAgB,aAAa,SAAS,EAC5B,eAAe,cAAc,YAAY;AAEjE,KAAI,cAAc,KAAK,KAAK,KAAK,SAAU,QAAO;AAClD,QACE,KAAK,UAAU,MAAM,UAAU,cAAc,MAAM,KAAK,KAAK,SAAS,IACtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChDJ,SAAgB,UAAU,EACxB,aACA,aACoB,EAAE,EAAuB;CAC7C,MAAM,cAAc,gBAAgB;AA0CpC,QAxCe,YAAY,YAAY;AAErC,QAAM,YAAY,eAAe;AAGjC,cAAY,OAAO;AAGnB,MAAI;AACF,SAAM,gBAAgB;WACf,OAAO;AAEd,WAAQ,MAAM,gDAAgD,MAAM;;AAItE,eAAa;AAIb,MAAI;AACF,kBAAe,OAAO;UAChB;AAKR,MAAI,YACF,QAAO,SAAS,OAAO;WACd,SACT,WAAU;OACL;GAIL,MAAM,aAAa,mBAAmB,OAAO,SAAS,KAAK;AAC3D,UAAO,SAAS,OAAO,GAAG,iBAAiB,6BAA6B;;IAEzE;EAAC;EAAa;EAAa;EAAS,CAAC;;;;;;;;ACvE1C,SAAgB,gBAAgB,EAC9B,YAC0C;CAC1C,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;AAajD,QACE,oBAAC,aAAD,EAAA,UACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;EAAmB,SAdL,YAAY,YAAY;AAC1C,gBAAa,KAAK;AAClB,OAAI;AACF,UAAM,UAAU;YACT,OAAO;AACd,YAAQ,MAAM,oCAAoC,MAAM;aAChD;AACR,iBAAa,MAAM;;KAEpB,CAAC,SAAS,CAAC;EAKiC,UAAU;YAAnD,CACE,oBAAC,QAAD,EAAQ,WAAU,UAAW,CAAA,EAC7B,oBAAC,QAAD,EAAA,UAAO,YAAY,iBAAsB,WAAiB,CAAA,CACxC;KACJ,CAAA,EACN,CAAA;;;;ACKlB,MAAM,oBAAoB;AAE1B,SAAS,sBAAiC;AACxC,KAAI,OAAO,WAAW,YAAa,QAAO;CAC1C,MAAM,SAAS,aAAa,QAAQ,kBAAkB;AACtD,KAAI,WAAW,WAAW,WAAW,OAAQ,QAAO;AACpD,QAAO;;AAGT,SAAS,uBAAuB,OAAiC;AAC/D,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,KAAM,QAAO,cAAc,KAAK,KAAK;AAC9C,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,MAAM,KAAM,QAAO,cAAc,MAAM,KAAK;;AAGpD,QAAO;;AAGT,SAAS,mBACP,OACA,MAC4B;CAC5B,MAAM,aAAa,cAAc,KAAK;AACtC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,cAAc,KAAK,KAAK,KAAK,WAAY,QAAO;AACpD,MACE,KAAK,UAAU,MAAM,UAAU,cAAc,MAAM,KAAK,KAAK,WAAW,CAExE,QAAO;;;AAMb,SAAS,YACP,OACA,MAC4B;CAC5B,MAAM,aAAa,cAAc,KAAK;AACtC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,cAAc,KAAK,KAAK,KAAK,WAAY,QAAO;AACpD,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,cAAc,MAAM,KAAK,KAAK,WAAY,QAAO;;;AA4C3D,SAAgB,SAAS,EACvB,SAAS,aACT,YAAY,gBACZ,aACA,WAAW,KACX,aAAa,gBACb,YAAY,gBACZ,eACA,eACA,UACA,mBACA,YACmC;CAEnC,MAAM,qBAAqB,cAAc;EACvC,MAAM,WAAW,SAAS,QAAQ,YAAY,GAAG;AACjD,SAAO,WAAW,IAAI,aAAa;IAClC,CAAC,SAAS,CAAC;CAGd,MAAM,EAAE,MAAM,gBAAgB,cAAc,YAAY,EACtD,SAAS,CAAC,aACX,CAAC;CACF,MAAM,UAAU,eAAe;CAG/B,MAAM,EAAE,MAAM,gBAAgB,gBAAgB;CAC9C,MAAM,UAAU,eACP;EACL,MACE,aAAa,cAAc,aAAa,YACpC,GAAG,YAAY,WAAW,GAAG,YAAY,cACzC;EACN,OAAO,aAAa,SAAS;EAC7B,UAAU,aAAa,aAAa;EACpC,UAAU,aAAa;EACxB,GACD;EACE,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;EACd,CACF;CAGD,MAAM,cAAc,cAAc;AAChC,MAAI,CAAC,SAAS,SAAS,QAAQ,OAAQ,QAAO,KAAA;EAC9C,MAAM,SAAS,QAAQ,QAAQ;EAC/B,MAAM,WAAW,QAAQ,QAAQ;AACjC,SAAO,WACF,OAAO,MAAM,MAAM,EAAE,OAAO,SAAS,IAAI,OAAO,KACjD,OAAO;IACV,CAAC,SAAS,SAAS,QAAQ,SAAS,SAAS,cAAc,CAAC;CAG/D,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,YAAa,QAAO,KAAA;AACzB,SAAO,aAAa,YAAY;IAC/B,CAAC,YAAY,CAAC;AAEjB,iBAAgB;AACd,MAAI,CAAC,cAAe;AACpB,aAAW,cAAc;AACzB,eAAa;AACX,oBAAiB;;IAElB,CAAC,cAAc,CAAC;AAInB,iBAAgB;EACd,MAAM,OAAO,SAAS;AACtB,MAAI,aAAa,GACf,MAAK,aAAa,cAAc,YAAY,GAAG;MAE/C,MAAK,gBAAgB,aAAa;AAEpC,eAAa;AACX,QAAK,gBAAgB,aAAa;;IAEnC,CAAC,aAAa,GAAG,CAAC;CAGrB,MAAM,WAAW,cAAc;AAC7B,MAAI,eAAgB,QAAO;EAC3B,MAAM,aAAa,SAAS,SAAS,YAAY;AACjD,MAAI,cAAc,WAAW,SAAS,EAAG,QAAO;AAChD,SAAO,sBAAsB;IAC5B,CAAC,gBAAgB,SAAS,SAAS,YAAY,iBAAiB,CAAC;CAGpE,MAAM,iBAAiB,cAAc;EACnC,MAAM,YAAY,SAAS,SAAS,mBAAmB;AACvD,MAAI,aAAa,UAAU,SAAS,EAAG,QAAO;AAC9C,SAAO;IACN,CAAC,SAAS,SAAS,mBAAmB,kBAAkB,SAAS,CAAC;CAGrE,MAAM,UAA0C,SAAS;CAGzD,MAAM,WAAW,cAAc,gBAAgB,SAAS,EAAE,CAAC,SAAS,CAAC;CAGrE,MAAM,CAAC,WAAW,gBAAgB,SAAoB,oBAAoB;CAE1E,MAAM,wBAAwB,aAAa,SAAoB;AAC7D,eAAa,KAAK;AAClB,MAAI,OAAO,WAAW,YACpB,cAAa,QAAQ,mBAAmB,KAAK;IAE9C,EAAE,CAAC;AAIN,iBAAgB;EACd,MAAM,OAAO,SAAS;EACtB,MAAM,OAAO,cAAc,SAAS,KAAA,IAAY;AAChD,MAAI,KACF,MAAK,aAAa,mBAAmB,KAAK;MAE1C,MAAK,gBAAgB,kBAAkB;AAEzC,eAAa;AACX,QAAK,gBAAgB,kBAAkB;;IAExC,CAAC,UAAU,CAAC;AAGf,iBAAgB;AACd,MAAI,CAAC,cAAe;EAEpB,IAAI,OAAO,SAAS,cAClB,6BACD;AACD,MAAI,CAAC,MAAM;AACT,UAAO,SAAS,cAAc,OAAO;AACrC,QAAK,OAAO;AACZ,YAAS,KAAK,YAAY,KAAK;;EAEjC,MAAM,SAAS;EAEf,MAAM,QAAQ;EACd,SAAS,OAAO,aAAsB;GAKpC,MAAM,QAHJ,cAAc,SAAU,cAAc,SAAS,UAAW,eAEjD,SAAS,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM,MAC5C,UAAU;AAC5B,UAAO,UAAU;AACjB,YAAS,KAAK,MAAM,kBAAkB;;EAGxC,MAAM,MAAM,OAAO,WAAW,+BAA+B;AAC7D,SAAO,IAAI,QAAQ;AAEnB,MAAI,cAAc,QAAQ;GACxB,MAAM,WAAW,MAA2B,OAAO,EAAE,QAAQ;AAC7D,OAAI,iBAAiB,UAAU,QAAQ;AACvC,gBAAa;AACX,QAAI,oBAAoB,UAAU,QAAQ;AAC1C,WAAO,QAAQ;AACf,aAAS,KAAK,MAAM,kBAAkB;;;AAI1C,eAAa;AACX,UAAO,QAAQ;AACf,YAAS,KAAK,MAAM,kBAAkB;;IAEvC,CAAC,eAAe,UAAU,CAAC;CAG9B,MAAM,CAAC,UAAU,eAAe,eAAuB;AACrD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,wBACL,OAAO,SAAS,UAChB,mBACD;GACD;AAGF,iBAAgB;AACd,MAAI,mBAAmB,KAAA,EAAW;AAClC,MAAI,OAAO,WAAW,YAAa;AAOnC,MAAI,CALgB,wBAClB,OAAO,SAAS,UAChB,mBACD,EAEiB;GAChB,MAAM,YAAY,uBAAuB,SAAS;AAClD,OAAI,WAAW;IACb,MAAM,aACJ,uBAAuB,MACnB,IAAI,cACJ,GAAG,mBAAmB,GAAG;AAE/B,YAAQ,aAAa,MAAM,IAAI,WAAW;AAC1C,gBAAY,UAAU;;;IAGzB;EAAC;EAAU;EAAgB;EAAmB,CAAC;AAGlD,iBAAgB;AACd,MAAI,mBAAmB,KAAA,EAAW;EAElC,MAAM,uBAAuB;AAK3B,eAJa,wBACX,OAAO,SAAS,UAChB,mBACD,CACgB;;AAGnB,SAAO,iBAAiB,YAAY,eAAe;AACnD,eAAa,OAAO,oBAAoB,YAAY,eAAe;IAClE,CAAC,gBAAgB,mBAAmB,CAAC;CAGxC,MAAM,SAAS,UAAU;EACvB,aAAa;EACb;EACD,CAAC;CAEF,MAAM,aAAa,kBAAkB;CAErC,MAAM,iBAAiB,aACpB,SAAiB;AAChB,MAAI,gBAAgB;AAClB,kBAAe,KAAK;AACpB;;EAEF,MAAM,aACJ,uBAAuB,MACnB,IAAI,SACJ,GAAG,mBAAmB,GAAG;AAC/B,UAAQ,UAAU,MAAM,IAAI,WAAW;AACvC,cAAY,KAAK;IAEnB,CAAC,gBAAgB,mBAAmB,CACrC;CAGD,MAAM,wBAAwB,cAE1B,kBAAkB,KAAA,IAChB,gBAEA,oBAAC,iBAAD,EAAiB,UAAU,QAAU,CAAA,EAEzC,CAAC,eAAe,OAAO,CACxB;CAGD,MAAM,YAAY,cACV,gBAAgB,YAAY,SAAS,EAC3C,CAAC,YAAY,SAAS,CACvB;CACD,MAAM,WAAW,WAAW,eAAe,cAAc,WAAW;CACpE,MAAM,aAAa,WAAW,QAAQ;CAItC,MAAM,wBADuB,mBAAmB,gBAAgB,SAAS,EACrB,YAAY,EAAE;CAClE,MAAM,iBAAiB,YAAY,UAAU,SAAS;CAGtD,MAAM,cACJ,gBAAgB,SAChB,SAAS,MAAM,MAAM,EAAE,OAAO,gBAAgB,UAAU,EAAE,QAC1D,KAAA;CAGF,MAAM,UACJ,OAAO,aAAa,aAChB,SAAS;EAAE,aAAa;EAAY;EAAgB,CAAC,GACpD,YACC,qBAAC,sBAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAc,OAAO,aAAe,CAAA,EACpC,oBAAC,YAAD;EACE,aAAa;EACG;EACH;EACJ;EACC;EACE;EACZ,CAAA,CACmB,EAAA,CAAA;AAK/B,KAAI,aAAa,CAAC,QAChB,QAAO,oBAAC,iBAAD,EAAmB,CAAA;AAG5B,QACE,oBAAC,iBAAD;EAAiB,MAAM;YACrB,oBAAC,mBAAD;GACE,MAAM;GACN,cAAc;GACd,iBAAiB;aAEjB,oBAAC,OAAD;IACE,WAAU;IACV,cAAY,aAAa;IACzB,mBAAiB,cAAc,SAAS,KAAA,IAAY;cAEpD,oBAAC,uBAAD;KACE,aAAa;KACb,UAAU;KACV,UAAU;eAEV,oBAAC,gBAAD;MACE,gBACE,oBAAC,eAAD;OACY;OACV,aAAa;OACb,YAAY;OACZ,CAAA;MAEJ,eACE,oBAAC,WAAD;OACE,YAAY;OACZ,aAAa;OACb,YAAY;OACZ,UAAU;OACV,CAAA;MAEJ,eACE,kBAAkB,KAAA,IAChB,gBAEA,oBAAC,oBAAD,EAAsB,CAAA;MAG1B,eAAe;MACf,cAAA;MACA,cACE,oBAAC,cAAD;OACE,UAAU;OACV,aAAa;OACb,YAAY;OACZ,iBAAiB,SAAS,iBAAiB;OAC3C,CAAA;gBAGH;MACc,CAAA;KACK,CAAA;IACpB,CAAA;GACY,CAAA;EACJ,CAAA;;;;;;;;;;ACnetB,SAAgB,WAAW,KAAiC;AAE1D,QADY,OAAO,KAAK,MACX;;;;;;;;;;;;;;;;ACSf,SAAgB,yBACd,WACgB;AAChB,QAAO;EACL,SAAS,WAAW,eAAe,IAAI;EACvC,oBAAoB,gBAAgB,IAAI;EACxC,GAAG;EACJ;;;;;;;;;;;AAYH,SAAgB,wBACd,WACiB;AACjB,QAAO,EAAE,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsCzB,SAAgB,aAAa,SAAuB,EAAE,EAAQ;CAC5D,MAAM,cAAc,SAAS,eAAe,OAAO,UAAU,OAAO;AACpE,KAAI,CAAC,YACH,OAAM,IAAI,MACR,yBAAyB,OAAO,UAAU,OAAO,aAClD;CAGH,MAAM,cAAc,yBAAyB,OAAO,MAAM;CAC1D,MAAM,aAAa,wBAAwB,OAAO,KAAK;CAEvD,IAAI,MACF,oBAAC,UAAD;EAAU,aAAa,OAAO;EAAa,GAAI,OAAO;EAAS,CAAA;AAGjE,KAAI,OAAO,WAAW;EACpB,MAAM,YAAY,OAAO;AACzB,QAAM,oBAAC,WAAD,EAAA,UAAY,KAAgB,CAAA;;CAGpC,MAAM,OACJ,oBAAC,mBAAD;EAAmB,QAAQ;YACzB,oBAAC,eAAD;GAAe,QAAQ;aACrB,oBAAC,aAAD,EAAA,UAAc,KAAkB,CAAA;GAClB,CAAA;EACE,CAAA;AAGtB,YAAW,YAAY,CAAC,OACtB,OAAO,oBAAoB,OAAO,oBAAC,YAAD,EAAA,UAAa,MAAkB,CAAA,CAClE;;;;;;;;;;;AC5FH,MAAa,oBAAoB,CAAC,SAAS,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BrD,SAAgB,kBAA2C;CACzD,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAKL;EACA,UAAU,SAAS,mBAAmB;EACtC,eAAe,IAAI,IAAI,QAAQ;EAC/B,SAAS,QAAQ;AAEf,UADgB,8BAA8B,IAAI,CACnC;;EAElB,CAAC;;;;;;;;;ACjDJ,MAAa,wBAAwB,CAAC,SAAS,cAAc;;;;;;;;;;;;;;;;;AAgC7D,SAAgB,sBAAiD;CAC/D,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;CAE/C,MAAM,QAAQ,SAAS;EACrB,UAAU,SAAS,sBAAsB;EACzC,eAAe,IAAI,YAAY,KAAK;EACrC,CAAC;CAEF,MAAM,cAAc,MAAM;AAyB1B,QAAO;EACL;EACA;EACA,KAzBU,cAAc;AACxB,WAAQ,UAAkB,SAA2B,WAAoB;AACvE,QAAI,CAAC,YACH,QAAO;AAIT,QAAI,YAAY,eACd,QAAO;IAGT,MAAM,sBAAsB,YAAY,YAAY;AACpD,QAAI,CAAC,oBACH,QAAO;AAGT,WAAO,oBAAoB,WAAW;;KAEvC,CAAC,YAAY,CAAC;EAQf,cANmB,aAAa,kBAAkB;EAOnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjCH,SAAgB,gBAAqC;CACnD,MAAM,EAAE,cAAc,UAAU,cAAc,SAAS,iBAAiB;AAExE,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;;;;;AC/CH,MAAa,wBAAwB,CAAC,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;AAqB5D,SAAgB,gBAAqC;CACnD,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAAS;EACd,UAAU,SAAS,sBAAsB;EACzC,eAAe,IAAI,KAAK,SAAS;EAClC,CAAC;;;;ACpCJ,MAAME,wBAAM,IAAI,MAAM;AAEtB,SAASC,cAAY,MAAc,MAAc,SAAiB,GAAS;CACzE,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,GAAE,SAAS,MAAM,QAAQ,GAAG,EAAE;AAC9B,QAAO;;AAGT,MAAa,uBAAiD;CAC5D;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,kCAAkC;EACvD,OAAO;EACP,OAAOC,cAAY,GAAG,GAAG,EAAE,CAAC,aAAa;EACzC,KAAKA,cAAY,GAAG,GAAG,GAAG,CAAC,aAAa;EACxC,QAAQ;EACR,QAAQ;EACT;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,+CAA+C;EACpE,OAAO;EACP,OAAOA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EACxC,QAAQ;EACR,QAAQ;EACR,OAAO;EACR;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,2CAA2C;EAChE,OAAO;EACP,OAAOA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,GAAG,IAAI,GAAG,CAAC,aAAa;EACzC,QAAQ;EACR,QAAQ;EACR,KAAK;EACN;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,yCAAyC;EAC9D,OAAO;EACP,OAAOA,cAAY,IAAI,GAAG,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,IAAI,IAAI,EAAE,CAAC,aAAa;EACzC,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,UAAU;EACX;CACF;;;;;;;AClCD,SAAgB,oBAA6C;AAG3D,QAAO;EACL,MAAM,CAAC,GAAG,qBAAqB;EAC/B,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAAS,YAAY,MAAsB;CACzC,MAAM,IAAI,IAAI,KAAKA,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,aAA8B;CACzC;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,EAAE;EACzB,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,GAAG;EACtB,aAAa,YAAY,GAAG;EAC5B,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa,YAAY,EAAE;EAC3B,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACF;;;;;;;AC7BD,SAAgB,WAA2B;AAGzC,QAAO;EACL,MAAM,CAAC,GAAG,WAAW;EACrB,WAAW;EACX,SAAS;EACV;;;;;;;;;;;;;;;AC8DH,SAAgB,QACd,QAC4D;AAC5D,QAAO,OAAO,QAAQ,QAAQ,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;AAY7D,SAAgB,UACd,QACS;AACT,QAAO,OAAO;;;;;;;;;;;AAYhB,SAAgB,cACd,QAIA;AACA,QAAO,OAAO,WAAW,OAAO,UAAU,KAAA;;;;;;;;;;AAW5C,SAAgB,OACd,QACS;AACT,QAAO,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;;;;;AAoBtC,SAAgB,eACd,OACA,KACQ;AACR,QAAO,MAAM,KAAK,SAAS,KAAK,KAAK;;;;;;;;;AAUvC,SAAgB,YACd,MACA,KACkB;AAClB,QAAO,OAAO;;;;;;AAkDhB,MAAa,iBAAiB;CAC5B,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,kBAAkB;CAClB,OAAO;CACP,eAAe;CACf,OAAO;CACP,eAAe;CACf,sBAAsB;CACtB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,mBAAmB;CACnB,kBAAkB;CAClB,YAAY;CACb;;AAMD,SAAgB,eAAe,OAAsC;AACnE,QAAO,OAAO,OAAO,eAAe,CAAC,SAAS,MAAsB;;;;AChQtE,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAASC,WAAS,OAAuB;CACvC,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,SAAS,EAAE,UAAU,GAAG,MAAM;AAChC,QAAO,EAAE,aAAa;;AAExB,MAAa,kBAAuC;CAClD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWC,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACF;;;;;;;ACtDD,SAAgB,gBAAqC;AAGnD,QAAO;EACL,MAAM,CAAC,GAAG,gBAAgB;EAC1B,WAAW;EACX,SAAS;EACV;;;;ACjCH,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,kBAAkB;EACnB;CACD;EACE,IAAI;EACJ,kBAAkB;EACnB;CACD;EACE,IAAI;EACJ,kBAAkB;EACnB;CACF;;;;;;;ACOD,SAAgB,cAAiC;AAG/C,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAa,cAA0B;CACrC,KAAK;CACL,OAAO;CACP,OAAO;CACP,UAAU;CACX;;;;;;;ACeD,SAAgB,YAA6B;AAG3C,QAAO;EACL,MAAM;EACN,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAAS,SAAS,OAAuB;CACvC,MAAM,IAAI,IAAI,KAAKA,MAAI;AACvB,GAAE,SAAS,EAAE,UAAU,GAAG,MAAM;AAChC,QAAO,EAAE,aAAa;;AAGxB,SAASC,UAAQ,MAAsB;CACrC,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,qBAA8C;CACzD;EACE,IAAI;EACJ,OAAO;EACP,cAAc,CACZ;GAAE,IAAI;GAAU,MAAM;GAAO,OAAO;GAAkB,UAAU;GAAM,EACtE;GACE,IAAI;GACJ,MAAM;GACN,OAAO;GACP,UAAU;GACX,CACF;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SAAS;GACT,WAAW,SAAS,EAAE;GACtB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWC,UAAQ,GAAG;EACtB,WAAW,SAAS,EAAE;EACvB;CACD;EACE,IAAI;EACJ,OAAO;EACP,cAAc,CACZ;GAAE,IAAI;GAAU,MAAM;GAAO,OAAO;GAAkB,UAAU;GAAM,EACtE;GACE,IAAI;GACJ,MAAM;GACN,OAAO;GACP,UAAU;GACX,CACF;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SACE;GACF,WAAW,SAAS,EAAE;GACtB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWA,UAAQ,GAAG;EACtB,WAAW,SAAS,EAAE;EACvB;CACD;EACE,IAAI;EACJ,OAAO;EACP,cAAc;GACZ;IAAE,IAAI;IAAU,MAAM;IAAO,OAAO;IAAkB,UAAU;IAAM;GACtE;IACE,IAAI;IACJ,MAAM;IACN,OAAO;IACR;GACD;IAAE,IAAI;IAAU,MAAM;IAAc,OAAO;IAAwB;GACpE;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SACE;GACF,WAAWA,UAAQ,EAAE;GACrB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWA,UAAQ,GAAG;EACtB,WAAWA,UAAQ,EAAE;EACtB;CACF;AAED,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SACE;EACF,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACD;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SACE;EACF,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACD;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SAAS;EACT,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACF;;;;;;;;;;;;;;;;;;;ACpGD,SAAgB,mBAA2C;AAGzD,QAAO;EACL,MAAM,CAAC,GAAG,mBAAmB;EAC7B,WAAW;EACX,SAAS;EACV;;;;;;;;;;;;;;;;;;;AA8BH,SAAgB,wBAEd,iBAC+B;AAG/B,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACV;;;;;;;ACSH,MAAa,mBAAmB;CAC9B,QAAQ;CACR,UAAU;CACV,MAAM;CACN,UAAU;CACX;;;AC9FD,MAAM,sBAAM,IAAI,MAAM;AAEtB,SAAS,QAAQ,MAAsB;CACrC,MAAM,IAAI,IAAI,KAAK,IAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,OAAO,eAAe;EAC7B,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,aAAa;EACpB,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,SAAS;EACT,QAAQ;EACR,MAAM;EACN,OAAO;EACP,WAAW,QAAQ,EAAE;EACrB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,WAAW;EAClB,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,GAAG;EACvB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,SAAS;GACP,MAAM;GACN,OAAO;GACP,SAAS;GACV;EACD,WAAW,QAAQ,IAAI;EACvB,WAAW,QAAQ,GAAG;EACvB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,QAAQ;EACR,MAAM;EACN,OAAO;EACP,WAAW,QAAQ,IAAI;EACvB,WAAW,QAAQ,GAAG;EACvB;CACF;AAED,MAAa,eAAwB,cAAc;;;;;;;AC/EnD,SAAgB,gBAAgB,OAAuC;AACrE,QAAO,OAAO,OAAO,iBAAiB,CAAC,SAAS,MAAuB;;;;;;;;;;;;;;;;;;AA4CzE,SAAgB,YAEd,SACmB;AAGnB,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACT,YAAY,cAAc;EAC3B;;;;;;;;;;;;;;AAeH,SAAgB,WAEd,YACkB;AAGlB,QAAO;EACL,MAAM;EACN,WAAW;EACX,SAAS;EACV;;;;AC/DH,MAAa,wBAGT;CACF,qBACE,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAAmB,MAAM,MAAM,EAAE,4BAA4B;CACtE,uBACE,OAAO,kCAAqB,MAAM,MAAM,EAAE,8BAA8B;CAC1E,sBACE,OAAO,iCAAA,MAAA,MAAA,EAAA,EAAA,CAAoB,MAAM,MAAM,EAAE,6BAA6B;CACxE,oBACE,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAAkB,MAAM,MAAM,EAAE,2BAA2B;CACpE,2BACE,OAAO,sCAAA,MAAA,MAAA,EAAA,EAAA,CAAyB,MAC7B,MAAM,EAAE,kCACV;CACH,uBACE,OAAO,kCAAA,MAAA,MAAA,EAAA,EAAA,CAAqB,MAAM,MAAM,EAAE,8BAA8B;CAC1E,sBACE,OAAO,iCAAoB,MAAM,MAAM,EAAE,6BAA6B;CACxE,oBACE,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAAkB,MAAM,MAAM,EAAE,2BAA2B;CACpE,wBACE,OAAO,mCAAsB,MAAM,MAAM,EAAE,+BAA+B;CAC5E,kBACE,OAAO,6BAAA,MAAA,MAAA,EAAA,EAAA,CAAgB,MAAM,MAAM,EAAE,yBAAyB;CAChE,qBACE,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAAmB,MAAM,MAAM,EAAE,4BAA4B;CACtE,yBACE,OAAO,oCAAA,MAAA,MAAA,EAAA,EAAA,CAAuB,MAC3B,MAAM,EAAE,gCACV;CACJ;;;;AASD,MAAa,gBAAgB;CAC3B,SAAS;CACT,WAAW;CACX,UAAU;CACV,QAAQ;CACR,eAAe;CACf,WAAW;CACX,UAAU;CACV,SAAS;CACT,YAAY;CACZ,MAAM;CACN,SAAS;CACT,cAAc;CACf;;;;;AAMD,SAAS,4BAAkC;AAEzC,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAW;GAAO;EACpC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAa;GAAQ;GAAiB;GAAgB;EAC7D,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAY;GAAU;GAAe;EAC5C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAU;GAAY;GAAQ;EACrC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAiB;GAAa;GAAW;EAChD,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAa;GAAU;GAAW;EACzC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAQ;GAAS;GAAY;GAAW;EAC/C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAY;GAAW;GAAW;EACzC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAW;GAAU;GAAW;EAClD,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAO;GAAW;EACpC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAgB;GAAU;GAAU;EAC3C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aACE;EACF,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAc;GAAa;GAAU;GAAS;GAAW;EAChE,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;;AAIJ,2BAA2B;;;;;;;;ACvU3B,SAAgB,oBACd,WAEoC;AACpC,KAAI,UAAU,WAAW,EAAG,QAAO;CAEnC,MAAM,gBAAgB,OAAO,YAC3B,UAAU,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAC5C;AAED,QAAO;EAAE,GAAG;EAA6B,GAAG;EAAe;;;;;;;;;ACC7D,MAAa,UAET,WAA4C,SAAS,QACvD,EAAE,IAAI,SAAS,UAAU,GAAG,QAC5B,KACA;CACA,MAAM,EAAE,UAAU,cAAc,kBAAkB;CAElD,MAAM,eAAe,MAAqC;AAExD,YAAU,EAAE;AACZ,MAAI,EAAE,iBAAkB;AAGxB,MAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,OAAQ;AACtD,MAAI,EAAE,WAAW,EAAG;AACpB,MAAI,KAAK,UAAU,KAAK,WAAW,QAAS;AAE5C,IAAE,gBAAgB;AAClB,WAAS,GAAG;;AAGd,QACE,oBAAC,KAAD;EAAQ;EAAK,MAAM,UAAU,GAAG;EAAE,SAAS;EAAa,GAAI;EACzD;EACC,CAAA;EAEN;;;;;;;;;ACtBF,MAAa,oBAAoB;CAC/B,SAAS;CACT,OAAO;EACL,gBAAgB,EAAE,UAAU,SAAS;EACrC,iBAAiB,EAAE,UAAU,SAAS;EACtC,cAAc,EAAE,UAAU,kBAAkB;EAC5C,iBAAiB,EAAE,UAAU,kBAAkB;EAC/C,kBAAkB,EAAE,UAAU,kBAAkB;EAChD,kBAAkB,EAAE,UAAU,kBAAkB;EACjD;CAED,gBAAgB;EACd;EACA;EACA;EACD;CACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["SPIN_STYLE_ID","ensureSpinStyle","React","ICON_MAP","React","cn","cn","Skeleton","ProfileScreen","OrdersScreen","SubscriptionsScreen","MessagingScreen","ContactsScreen","ShopScreen","CustomersScreen","ProductsScreen","ShareablesScreen","MySiteScreen","now","daysFromNow","now","now","hoursAgo","now","daysAgo"],"sources":["../src/types/page-template.ts","../src/registries/page-template-registry.ts","../src/core/resolve-pages.ts","../src/providers/PageTemplateProvider.tsx","../src/components/AuthError.tsx","../src/components/RequireAuth.tsx","../../react/src/shell/use-mobile.ts","../../react/src/shell/sidebar.tsx","../../react/src/shell/AppShellLayout.tsx","../../react/src/shell/ThemeModeContext.tsx","../../core/src/navigation/system-navigation-items.ts","../../core/src/navigation/default-navigation.ts","../../react/src/shell/ScreenHeader.tsx","../../react/src/components/RepIcon.tsx","../../core/src/navigation/navigation-storage.ts","../../core/src/navigation/navigation-utils.ts","../src/shell/SdkBottomNav.tsx","../src/hooks/use-fluid-app.ts","../src/shell/AppShellLoading.tsx","../src/shell/CollapsibleNavItem.tsx","../src/shell/SdkNavigation.tsx","../src/navigation/filter-nav-items.ts","../src/shell/QuickLinksDropdown.tsx","../src/shell/SdkMobileQuickLinksGrid.tsx","../src/shell/SdkMobileProfileMenu.tsx","../src/shell/SdkHeader.tsx","../src/hooks/use-company-switch.ts","../../../company-switcher/core/src/use-company-switcher.ts","../../../company-switcher/ui/src/components/CompanyImage.tsx","../../../company-switcher/ui/src/components/CompanyItem.tsx","../../../company-switcher/ui/src/components/CompanyList.tsx","../../../company-switcher/ui/src/components/CompanySwitcherDropdown.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSheet.tsx","../../../company-switcher/ui/src/components/CompanySwitcherConfirmDialog.tsx","../../../company-switcher/ui/src/components/CompanySwitcher.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSkeleton.tsx","../src/shell/SdkCompanySwitcher.tsx","../../core/src/navigation/account-manage.ts","../src/screens/AccessDeniedScreen.tsx","../../core/src/data-sources/presets.ts","../../react/src/data-sources/use-widget-data.ts","../../react/src/data-sources/ErrorState.tsx","../../react/src/data-sources/DataAwareWidget.tsx","../src/shell/BuilderScreenView.tsx","../src/account/account-management-layout.tsx","../src/shell/system-screen-map.ts","../src/shell/PageRouter.tsx","../../core/src/navigation/slug-utils.ts","../src/hooks/use-logout.ts","../src/shell/SdkLogoutButton.tsx","../src/shell/AppShell.tsx","../src/config/env.ts","../src/config/defaults.ts","../src/entry/create-portal.tsx","../src/hooks/use-fluid-profile.ts","../src/hooks/use-fluid-permissions.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/use-current-rep.ts","../src/hooks/demo-data/calendar-events.ts","../src/hooks/use-calendar-events.ts","../src/hooks/demo-data/todos.ts","../src/hooks/use-todos.ts","../src/hooks/hook-types.ts","../src/hooks/demo-data/activities.ts","../src/hooks/use-activities.ts","../src/hooks/demo-data/catchups.ts","../src/hooks/use-catchups.ts","../src/hooks/demo-data/mysite.ts","../src/hooks/use-mysite.ts","../src/hooks/demo-data/conversations.ts","../src/hooks/use-conversations.ts","../src/types/screen-types.ts","../src/hooks/demo-data/contacts.ts","../src/hooks/use-contacts.ts","../src/screens/index.ts","../src/utils/build-widget-registry.ts","../src/shell/AppLink.tsx","../src/scaffold/manifest.ts"],"sourcesContent":["import type { WidgetSchema } from \"./widget-schema\";\n\n/**\n * Category for organizing page templates in the registry\n */\nexport interface PageCategory {\n /** Unique identifier for the category */\n readonly id: string;\n /** Display label */\n label: string;\n /** Icon identifier (e.g., lucide icon name) */\n icon?: string;\n}\n\n/**\n * A reusable page template that can be shared across multiple navigations\n */\nexport interface PageTemplate {\n /** Unique identifier for the template */\n readonly id: string;\n /** URL-friendly slug */\n slug: string;\n /** Display name */\n name: string;\n /** Description of the template's purpose */\n description?: string;\n /** Category ID for organization */\n category: string;\n /** Tags for filtering and search */\n tags?: readonly string[];\n /** The widget tree that defines the page content */\n component_tree: readonly WidgetSchema[];\n /** Semantic version of the template */\n version: string;\n /** Whether this is a core feature that cannot be removed */\n isCore?: boolean;\n /** Default prop values that can be customized */\n defaultProps?: Readonly<Record<string, unknown>>;\n /** Thumbnail image URL for UI display */\n thumbnail?: string;\n}\n\n/**\n * Reference to a shared page template within a navigation\n */\nexport interface PageReference {\n /** ID of the page template being referenced */\n page_template_id: string;\n /** Screen ID to assign to this page in the navigation */\n screen_id: number;\n /** Optional prop overrides (only prop values, not widget structure) */\n overrides?: readonly PageOverride[];\n}\n\n/**\n * Override for a specific widget's props within a page template\n */\nexport interface PageOverride {\n /** ID of the widget to override (must match WidgetSchema.id in the template) */\n readonly widget_id: string;\n /** Props to override (merged with original props) */\n props: Readonly<Record<string, unknown>>;\n}\n\n/**\n * Built-in page category IDs\n */\nexport const PAGE_CATEGORIES = {\n CORE: \"core\",\n COMMERCE: \"commerce\",\n COMMUNICATION: \"communication\",\n DATA: \"data\",\n CUSTOM: \"custom\",\n} as const;\n\nexport type PageCategoryId =\n (typeof PAGE_CATEGORIES)[keyof typeof PAGE_CATEGORIES];\n","import type {\n PageTemplate,\n PageCategory,\n PageCategoryId,\n} from \"../types/page-template\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Registry for managing reusable page templates.\n *\n * The registry provides a central store for page templates that can be\n * shared across multiple navigations. Core pages (like Messaging, Contacts)\n * are pre-registered and cannot be removed.\n *\n * @example\n * ```ts\n * // Register a custom page template\n * PageTemplateRegistry.register({\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Custom Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Hello' } }],\n * });\n *\n * // Get a template by ID\n * const template = PageTemplateRegistry.get('custom-dashboard');\n *\n * // List all templates in a category\n * const corePages = PageTemplateRegistry.getByCategory('core');\n * ```\n */\nclass PageTemplateRegistryImpl {\n private templates = new Map<string, PageTemplate>();\n private categories: PageCategory[] = [];\n\n constructor() {\n // Initialize default categories\n this.categories = [\n { id: PAGE_CATEGORIES.CORE, label: \"Core Features\", icon: \"star\" },\n {\n id: PAGE_CATEGORIES.COMMERCE,\n label: \"Commerce\",\n icon: \"shopping-cart\",\n },\n {\n id: PAGE_CATEGORIES.COMMUNICATION,\n label: \"Communication\",\n icon: \"message-circle\",\n },\n {\n id: PAGE_CATEGORIES.DATA,\n label: \"Data & Analytics\",\n icon: \"bar-chart\",\n },\n { id: PAGE_CATEGORIES.CUSTOM, label: \"Custom\", icon: \"puzzle\" },\n ];\n }\n\n /**\n * Register a new page template.\n * @throws Error if a template with the same ID already exists\n */\n register(template: PageTemplate): void {\n if (this.templates.has(template.id)) {\n throw new Error(\n `Page template with ID \"${template.id}\" is already registered`,\n );\n }\n this.templates.set(template.id, template);\n }\n\n /**\n * Unregister a page template by ID.\n * Core templates cannot be unregistered.\n * @returns true if the template was removed, false if it didn't exist or is a core template\n */\n unregister(id: string): boolean {\n const template = this.templates.get(id);\n if (!template) {\n return false;\n }\n if (template.isCore) {\n console.warn(\n `Cannot unregister core page template \"${id}\". Core templates are required.`,\n );\n return false;\n }\n return this.templates.delete(id);\n }\n\n /**\n * Get a page template by ID.\n */\n get(id: string): PageTemplate | undefined {\n return this.templates.get(id);\n }\n\n /**\n * Get all page templates in a specific category.\n */\n getByCategory(category: PageCategoryId | string): PageTemplate[] {\n return Array.from(this.templates.values()).filter(\n (t) => t.category === category,\n );\n }\n\n /**\n * List all registered page templates.\n */\n listAll(): PageTemplate[] {\n return Array.from(this.templates.values());\n }\n\n /**\n * List all core page templates (isCore: true).\n */\n listCore(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => t.isCore);\n }\n\n /**\n * List all non-core page templates.\n */\n listOptional(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => !t.isCore);\n }\n\n /**\n * List all registered categories.\n */\n listCategories(): PageCategory[] {\n return [...this.categories];\n }\n\n /**\n * Add a custom category.\n */\n addCategory(category: PageCategory): void {\n if (this.categories.some((c) => c.id === category.id)) {\n console.warn(`Category with ID \"${category.id}\" already exists`);\n return;\n }\n this.categories.push(category);\n }\n\n /**\n * Check if a template exists by ID.\n */\n has(id: string): boolean {\n return this.templates.has(id);\n }\n\n /**\n * Get the count of registered templates.\n */\n get size(): number {\n return this.templates.size;\n }\n\n /**\n * Clear all non-core templates.\n * Useful for testing or resetting the registry.\n */\n clearNonCore(): void {\n for (const [id, template] of this.templates) {\n if (!template.isCore) {\n this.templates.delete(id);\n }\n }\n }\n}\n\n/**\n * Global page template registry singleton.\n *\n * This registry is automatically populated with core page templates\n * (Messaging, Contacts) when the SDK is imported.\n */\nexport const PageTemplateRegistry: PageTemplateRegistryImpl =\n new PageTemplateRegistryImpl();\n","import type { Navigation, ScreenDefinition, WidgetSchema } from \"../types\";\nimport type {\n PageReference,\n PageOverride,\n PageTemplate,\n} from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\n\n/**\n * Apply prop overrides to widgets in a component tree.\n * Recursively walks widget.props.children so overrides targeting\n * nested widgets (inside LayoutWidget, ContainerWidget, etc.) are applied.\n *\n * @param componentTree - The original component tree from the page template\n * @param overrides - Array of widget-specific prop overrides\n * @returns A new component tree with overrides applied\n */\nfunction applyOverrides(\n componentTree: readonly WidgetSchema[],\n overrides: readonly PageOverride[],\n): WidgetSchema[] {\n if (!overrides.length) {\n // Return a mutable copy to satisfy ScreenDefinition.component_tree type\n return [...componentTree];\n }\n\n // Build a map of widget_id -> override for O(1) lookup (built once, shared across recursion)\n const overrideMap = new Map(overrides.map((o) => [o.widget_id, o.props]));\n\n return componentTree.map((widget) =>\n applyWidgetOverrides(widget, overrideMap),\n );\n}\n\n/**\n * Recursively apply overrides to a widget and its children.\n * Only clones widgets that actually change (have an override or contain children that might).\n */\nfunction applyWidgetOverrides(\n widget: WidgetSchema,\n overrideMap: ReadonlyMap<string, Record<string, unknown>>,\n): WidgetSchema {\n const override = widget.id ? overrideMap.get(widget.id) : undefined;\n const children = widget.props.children;\n\n // Recurse into children if they exist (LayoutWidget, ContainerWidget)\n const hasChildren = Array.isArray(children) && children.length > 0;\n\n if (!override && !hasChildren) {\n return widget;\n }\n\n const newProps = override\n ? { ...widget.props, ...override }\n : { ...widget.props };\n\n if (hasChildren) {\n newProps.children = (children as readonly WidgetSchema[]).map((child) =>\n applyWidgetOverrides(child, overrideMap),\n );\n }\n\n return { ...widget, props: newProps };\n}\n\n/**\n * Resolve a page reference to a screen definition.\n *\n * @param ref - The page reference from navigation\n * @returns A ScreenDefinition or undefined if the template doesn't exist\n */\nfunction resolvePageReference(\n ref: Readonly<PageReference>,\n): ScreenDefinition | undefined {\n const template = PageTemplateRegistry.get(ref.page_template_id);\n if (!template) {\n console.warn(\n `Page template \"${ref.page_template_id}\" not found in registry`,\n );\n return undefined;\n }\n\n // Apply any overrides to the component tree\n // Spread to create mutable array for ScreenDefinition.component_tree\n const componentTree = ref.overrides\n ? applyOverrides(template.component_tree, ref.overrides)\n : [...template.component_tree];\n\n return {\n id: ref.screen_id,\n slug: template.slug,\n name: template.name,\n component_tree: componentTree,\n };\n}\n\n/**\n * Resolve all page references and local screens into a unified screen list.\n *\n * This function merges:\n * 1. Screen definitions from page_refs (shared templates)\n * 2. Local screen definitions (for backwards compatibility and custom screens)\n *\n * When a screen_id appears in both page_refs and screens, the local screen\n * takes precedence (allows local overrides of template pages).\n *\n * @param navigation - The navigation configuration\n * @returns A unified array of ScreenDefinition objects\n *\n * @example\n * ```ts\n * const navigation: Navigation = {\n * definition_id: 1,\n * id: 1,\n * name: \"Main Nav\",\n * navigation_items: [...],\n * screens: [\n * // Local custom screen\n * { id: 1, slug: \"home\", name: \"Home\", component_tree: [...] }\n * ],\n * page_refs: [\n * // Reference to shared messaging template\n * { page_template_id: \"core-messaging\", screen_id: 2 }\n * ],\n * };\n *\n * const allScreens = resolveNavigationPages(navigation);\n * // Returns: [home screen, messaging screen from template]\n * ```\n */\nexport function resolveNavigationPages(\n navigation: Readonly<Navigation>,\n): ScreenDefinition[] {\n // Start with local screens (these take precedence)\n const localScreenIds = new Set(navigation.screens.map((s) => s.id));\n const result: ScreenDefinition[] = [...navigation.screens];\n\n // Resolve page references (skip if local screen already exists with same ID)\n if (navigation.page_refs) {\n for (const ref of navigation.page_refs) {\n if (localScreenIds.has(ref.screen_id)) {\n // Local screen takes precedence\n continue;\n }\n\n const screen = resolvePageReference(ref);\n if (screen) {\n result.push(screen);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Get all available page templates for use in navigation.\n *\n * @returns Array of page templates from the registry\n */\nexport function getAvailablePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listAll();\n}\n\n/**\n * Get core page templates that are required for basic functionality.\n *\n * @returns Array of core page templates\n */\nexport function getCorePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listCore();\n}\n\n/**\n * Get optional page templates that can be added to navigation.\n *\n * @returns Array of optional (non-core) page templates\n */\nexport function getOptionalPageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listOptional();\n}\n\n/**\n * Check if a navigation has all required core pages.\n *\n * @param navigation - The navigation to check\n * @returns Object with validation result and missing page IDs\n */\nexport function validateNavigationPages(navigation: Readonly<Navigation>): {\n readonly valid: boolean;\n readonly missingCorePages: readonly string[];\n} {\n const corePages = PageTemplateRegistry.listCore();\n const referencedTemplateIds = new Set(\n navigation.page_refs?.map((ref) => ref.page_template_id) ?? [],\n );\n\n // Check which core pages are referenced\n const missingCorePages = corePages\n .filter((page) => !referencedTemplateIds.has(page.id))\n .map((page) => page.id);\n\n return {\n valid: missingCorePages.length === 0,\n missingCorePages,\n };\n}\n","import React, {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport type { PageTemplate } from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { resolveNavigationPages } from \"../core/resolve-pages\";\nimport type { Navigation, ScreenDefinition } from \"../types\";\n\n/** Stable empty array for the default `templates` prop (avoids new reference each render) */\nconst EMPTY_TEMPLATES: readonly PageTemplate[] = [];\n\n/**\n * Context value for page template resolution.\n * All properties are readonly since context values should not be mutated by consumers.\n */\ninterface PageTemplateContextValue {\n /**\n * Resolve a navigation's page_refs and screens into a unified screen list\n */\n readonly resolvePages: (navigation: Navigation) => ScreenDefinition[];\n /**\n * Get all available page templates\n */\n readonly listTemplates: () => PageTemplate[];\n /**\n * Get a specific template by ID\n */\n readonly getTemplate: (id: string) => PageTemplate | undefined;\n /**\n * Check if a template exists\n */\n readonly hasTemplate: (id: string) => boolean;\n}\n\nconst PageTemplateContext = createContext<PageTemplateContextValue | null>(\n null,\n);\n\n/**\n * Props for PageTemplateProvider\n */\ninterface PageTemplateProviderProps {\n children: React.ReactNode;\n /**\n * Additional custom page templates to register.\n * These are registered when the provider mounts and unregistered when it unmounts.\n */\n templates?: readonly PageTemplate[];\n}\n\n/**\n * Provider for page template resolution.\n *\n * This provider:\n * 1. Registers any custom templates passed via props\n * 2. Provides methods for resolving navigation pages\n * 3. Cleans up custom templates on unmount\n *\n * @example\n * ```tsx\n * // With custom templates\n * const customTemplates: PageTemplate[] = [\n * {\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Custom Dashboard' } }],\n * },\n * ];\n *\n * <PageTemplateProvider templates={customTemplates}>\n * <App />\n * </PageTemplateProvider>\n *\n * // Without custom templates (uses only core templates)\n * <PageTemplateProvider>\n * <App />\n * </PageTemplateProvider>\n * ```\n */\nexport function PageTemplateProvider({\n children,\n templates = EMPTY_TEMPLATES,\n}: PageTemplateProviderProps): React.JSX.Element {\n // Track which templates we registered so we can clean them up\n const registeredIds = useRef<string[]>([]);\n\n // Derive a stable primitive key from template IDs\n // (rerender-dependencies rule: use primitive dependencies in effects)\n const templateKey = templates.map((t) => t.id).join(\",\");\n\n // Register custom templates when template IDs change\n useEffect(() => {\n const registered: string[] = [];\n\n for (const template of templates) {\n if (!PageTemplateRegistry.has(template.id)) {\n try {\n PageTemplateRegistry.register(template);\n registered.push(template.id);\n } catch (error) {\n console.warn(\n `Failed to register page template \"${template.id}\":`,\n error,\n );\n }\n }\n }\n\n registeredIds.current = registered;\n\n // Cleanup when template IDs change or on unmount\n return () => {\n for (const id of registeredIds.current) {\n PageTemplateRegistry.unregister(id);\n }\n registeredIds.current = [];\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [templateKey]);\n\n // Memoize context value to prevent unnecessary re-renders\n // Use `satisfies` to validate shape at compile time while preserving inference\n const contextValue = useMemo(\n () =>\n ({\n resolvePages: resolveNavigationPages,\n listTemplates: () => PageTemplateRegistry.listAll(),\n getTemplate: (id: string) => PageTemplateRegistry.get(id),\n hasTemplate: (id: string) => PageTemplateRegistry.has(id),\n }) satisfies PageTemplateContextValue,\n [],\n );\n\n return (\n <PageTemplateContext.Provider value={contextValue}>\n {children}\n </PageTemplateContext.Provider>\n );\n}\n\n/**\n * Hook to access page template functionality.\n *\n * @throws Error if used outside of PageTemplateProvider\n *\n * @example\n * ```tsx\n * function NavigationRenderer({ navigation }: { navigation: Navigation }) {\n * const { resolvePages } = usePageTemplates();\n * const screens = resolvePages(navigation);\n *\n * return (\n * <div>\n * {screens.map((screen) => (\n * <Screen key={screen.id} definition={screen} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePageTemplates(): PageTemplateContextValue {\n const context = useContext(PageTemplateContext);\n if (!context) {\n throw new Error(\n \"usePageTemplates must be used within a PageTemplateProvider\",\n );\n }\n return context;\n}\n\n/**\n * Hook to resolve navigation pages directly.\n * Convenience wrapper around usePageTemplates().resolvePages.\n *\n * @param navigation - The navigation to resolve\n * @returns Array of resolved screen definitions\n */\nexport function useResolvedPages(navigation: Navigation): ScreenDefinition[] {\n const { resolvePages } = usePageTemplates();\n return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);\n}\n","/**\n * AuthError - Default Authentication Error Component\n *\n * Displayed when authentication fails. Can be customized or replaced\n * via the RequireAuth component's errorComponent prop.\n */\n\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\n\nexport interface AuthErrorProps {\n /** Error message to display */\n message?: string;\n /** Optional title */\n title?: string;\n /** Optional children for custom content */\n children?: ReactNode;\n}\n\n/**\n * Default authentication error component.\n *\n * Displays a simple error message when authentication fails.\n * Can be customized via props or replaced entirely in RequireAuth.\n *\n * @example\n * ```tsx\n * // Use with default message\n * <AuthError />\n *\n * // Use with custom message\n * <AuthError message=\"Session expired. Please log in again.\" />\n *\n * // Use with custom content\n * <AuthError>\n * <CustomLoginButton />\n * </AuthError>\n * ```\n */\nexport function AuthError({\n message = \"You need to be authenticated to view this content.\",\n title = \"Authentication Required\",\n children,\n}: AuthErrorProps): React.JSX.Element {\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n padding: \"2rem\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n color: \"#111827\",\n }}\n >\n <div\n style={{\n maxWidth: \"400px\",\n textAlign: \"center\",\n padding: \"2rem\",\n backgroundColor: \"#ffffff\",\n borderRadius: \"0.75rem\",\n boxShadow:\n \"0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)\",\n }}\n >\n {/* Lock Icon */}\n <div\n style={{\n width: \"64px\",\n height: \"64px\",\n margin: \"0 auto 1.5rem\",\n backgroundColor: \"#fee2e2\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <svg\n width=\"32\"\n height=\"32\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"#dc2626\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n </div>\n\n <h1\n style={{\n fontSize: \"1.5rem\",\n fontWeight: \"600\",\n marginBottom: \"0.75rem\",\n color: \"#111827\",\n }}\n >\n {title}\n </h1>\n\n <p\n style={{\n fontSize: \"1rem\",\n color: \"#6b7280\",\n marginBottom: children ? \"1.5rem\" : \"0\",\n lineHeight: \"1.5\",\n }}\n >\n {message}\n </p>\n\n {children}\n </div>\n </div>\n );\n}\n\nconst SPIN_STYLE_ID = \"fluid-auth-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Simple loading spinner component for auth loading state.\n */\nexport function AuthLoading(): React.JSX.Element {\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n Authenticating...\n </p>\n </div>\n );\n}\n","/**\n * RequireAuth - Route Protection Component\n *\n * Wraps content that requires authentication. Shows loading state\n * while checking auth, error component if not authenticated, or\n * children if authenticated.\n */\n\nimport type { ReactNode } from \"react\";\nimport { useFluidAuth } from \"../hooks/use-fluid-auth\";\nimport { AuthError, AuthLoading } from \"./AuthError\";\n\nexport interface RequireAuthProps {\n /** Content to render when authenticated */\n children: ReactNode;\n /** Component to show while checking authentication (default: AuthLoading) */\n fallback?: ReactNode;\n /** Component to show when not authenticated (default: AuthError) */\n errorComponent?: ReactNode;\n}\n\n/**\n * Component that protects content requiring authentication.\n *\n * **Important:** This provides UX-level protection only. It prevents\n * unauthenticated users from seeing the UI, but the real security\n * boundary is the server-side API. Client-side auth can always be\n * bypassed by modifying browser state.\n *\n * For defense-in-depth, configure `jwksUrl` in `FluidAuthConfig`\n * to enable client-side JWT signature verification.\n *\n * Shows different states based on auth status:\n * - Loading: Shows fallback (spinner by default)\n * - Not authenticated: Shows errorComponent (AuthError by default)\n * - Authenticated: Shows children\n *\n * @example\n * ```tsx\n * // Basic usage - shows default loading/error states\n * <RequireAuth>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Custom loading state\n * <RequireAuth fallback={<CustomSpinner />}>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Custom error component\n * <RequireAuth errorComponent={<LoginPrompt />}>\n * <ProtectedContent />\n * </RequireAuth>\n *\n * // Both custom\n * <RequireAuth\n * fallback={<SkeletonLoader />}\n * errorComponent={<RedirectToLogin />}\n * >\n * <Dashboard />\n * </RequireAuth>\n * ```\n */\nexport function RequireAuth({\n children,\n fallback = <AuthLoading />,\n errorComponent = <AuthError />,\n}: RequireAuthProps): React.JSX.Element {\n const { isAuthenticated, isLoading, error } = useFluidAuth();\n\n // Show loading state while checking authentication\n if (isLoading) {\n return <>{fallback}</>;\n }\n\n // Show error state if not authenticated\n if (!isAuthenticated || error) {\n return <>{errorComponent}</>;\n }\n\n // User is authenticated, render children\n return <>{children}</>;\n}\n","import { useState, useEffect } from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\nconst TABLET_BREAKPOINT = 1024;\n\nexport function useIsMobile(): boolean {\n const [isMobile, setIsMobile] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isMobile;\n}\n\nexport function useIsTablet(): boolean {\n const [isTablet, setIsTablet] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(\n `(min-width: ${MOBILE_BREAKPOINT}px) and (max-width: ${TABLET_BREAKPOINT - 1}px)`,\n );\n const onChange = () => {\n setIsTablet(\n window.innerWidth >= MOBILE_BREAKPOINT &&\n window.innerWidth < TABLET_BREAKPOINT,\n );\n };\n mql.addEventListener(\"change\", onChange);\n setIsTablet(\n window.innerWidth >= MOBILE_BREAKPOINT &&\n window.innerWidth < TABLET_BREAKPOINT,\n );\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isTablet;\n}\n\nexport function useIsMobileOrTablet(): boolean {\n const [isMobileOrTablet, setIsMobileOrTablet] = useState<boolean>(false);\n\n useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${TABLET_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobileOrTablet(window.innerWidth < TABLET_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobileOrTablet(window.innerWidth < TABLET_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isMobileOrTablet;\n}\n","\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport * as React from \"react\";\n\nimport { useIsMobile } from \"./use-mobile\";\n\n// ---------------------------------------------------------------------------\n// Inlined utilities (avoid importing from builder's UI kit)\n// ---------------------------------------------------------------------------\n\nfunction cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentPropsWithRef<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n decorative?: boolean;\n}) {\n return (\n <div\n role=\"separator\"\n aria-orientation={orientation}\n className={cn(\n \"bg-border shrink-0\",\n orientation === \"horizontal\" ? \"h-px w-full\" : \"h-full w-px\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Skeleton({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\"bg-muted animate-pulse rounded-md\", className)}\n {...props}\n />\n );\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SIDEBAR_WIDTH = \"13rem\";\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\n\n// ---------------------------------------------------------------------------\n// Sidebar Context\n// ---------------------------------------------------------------------------\n\ntype SidebarContextValue = {\n state: \"expanded\" | \"collapsed\";\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n isPreviewMode: boolean;\n useBottomNav: boolean;\n};\n\nexport const SidebarContext: React.Context<SidebarContextValue | null> =\n React.createContext<SidebarContextValue | null>(null);\n\nfunction useSidebar(): SidebarContextValue {\n const context = React.useContext(SidebarContext);\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\");\n }\n\n return context;\n}\n\n// ---------------------------------------------------------------------------\n// SidebarProvider\n// ---------------------------------------------------------------------------\n\nconst SidebarProvider: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n viewportWidth?: number;\n previewMode?: boolean;\n useBottomNav?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n viewportWidth?: number;\n previewMode?: boolean;\n useBottomNav?: boolean;\n }\n>(\n (\n {\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n viewportWidth,\n previewMode,\n useBottomNav: useBottomNavProp = false,\n className,\n style,\n children,\n ...props\n },\n ref,\n ) => {\n const windowIsMobile = useIsMobile();\n // Use viewportWidth if provided, otherwise use actual window detection\n const isMobile =\n viewportWidth !== undefined ? viewportWidth < 768 : windowIsMobile;\n // Preview mode is active when viewportWidth is provided\n const isPreviewMode = viewportWidth !== undefined || !!previewMode;\n const [openMobile, setOpenMobile] = React.useState(false);\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value;\n if (setOpenProp) {\n setOpenProp(openState);\n } else {\n _setOpen(openState);\n }\n },\n [setOpenProp, open],\n );\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile\n ? setOpenMobile((open) => !open)\n : setOpen((open) => !open);\n }, [isMobile, setOpen, setOpenMobile]);\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n // check if composer is focused - if so, let the composer handle the shortcut\n const activeElement = document.activeElement;\n const isComposerFocused =\n activeElement?.closest(\".group\\\\/composer\") ||\n activeElement?.closest(\"[data-toolbar]\") ||\n activeElement?.classList.contains(\"ProseMirror\");\n\n if (isComposerFocused) {\n return; // let the composer handle the shortcut\n }\n\n event.preventDefault();\n toggleSidebar();\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [toggleSidebar]);\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\";\n\n const contextValue = React.useMemo<SidebarContextValue>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n isPreviewMode,\n useBottomNav: useBottomNavProp,\n }),\n [\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n isPreviewMode,\n useBottomNavProp,\n ],\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-0 w-full flex-1\",\n className,\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n },\n);\nSidebarProvider.displayName = \"SidebarProvider\";\n\n// ---------------------------------------------------------------------------\n// Sidebar\n// ---------------------------------------------------------------------------\n\nconst Sidebar: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\";\n variant?: \"sidebar\" | \"floating\" | \"inset\";\n collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\";\n variant?: \"sidebar\" | \"floating\" | \"inset\";\n collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n }\n>(\n (\n {\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const {\n isMobile,\n state,\n openMobile,\n setOpenMobile,\n isPreviewMode,\n useBottomNav,\n } = useSidebar();\n\n // Define CSS variables for expanded and collapsed sidebar widths\n const sidebarWidth =\n state === \"expanded\" ? SIDEBAR_WIDTH : SIDEBAR_WIDTH_ICON;\n\n // When bottom nav is active on mobile, hide the sidebar entirely\n if (useBottomNav && isMobile) {\n return null;\n }\n\n if (collapsible === \"none\") {\n return (\n <div\n className={cn(\n \"bg-sidebar text-sidebar-foreground flex w-(--sidebar-width) flex-col rounded-tl-lg\",\n isPreviewMode ? \"h-full\" : \"h-[97vh]\",\n className,\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n );\n }\n\n if (isMobile) {\n // For mobile, render a slide-out sidebar with overlay\n // Use absolute positioning in preview mode so it stays within the preview container\n const positionClass = isPreviewMode ? \"absolute\" : \"fixed\";\n return (\n <>\n {/* Overlay - only visible when sidebar is open */}\n {openMobile && (\n <div\n className={cn(positionClass, \"inset-0 z-40 bg-black/50\")}\n onClick={() => setOpenMobile(false)}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Sidebar - slides in from left */}\n <div\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className={cn(\n positionClass,\n \"bg-sidebar text-sidebar-foreground top-0 left-0 z-50 h-full w-[--sidebar-width] p-0 transition-transform duration-300 ease-in-out\",\n openMobile ? \"translate-x-0\" : \"-translate-x-full\",\n className,\n )}\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n ref={ref}\n {...props}\n >\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </div>\n </>\n );\n }\n\n return (\n <div\n ref={ref}\n className=\"group peer bg-sidebar text-sidebar-foreground hidden md:block\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n style={\n {\n \"--sidebar-width\": sidebarWidth,\n } as React.CSSProperties\n }\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={cn(\n \"relative bg-transparent transition-[width] duration-200 ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\",\n )}\n />\n <div\n className={cn(\n \"relative inset-y-0 z-[20] hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex\",\n isPreviewMode ? \"h-full\" : \"h-svh\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n // Adjust the padding for floating and inset variants.\n variant === \"floating\" || variant === \"inset\"\n ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=right]:border-l\",\n className,\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n >\n {children}\n </div>\n </div>\n </div>\n );\n },\n);\nSidebar.displayName = \"Sidebar\";\n\n// ---------------------------------------------------------------------------\n// SidebarRail\n// ---------------------------------------------------------------------------\n\nconst SidebarRail: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<HTMLButtonElement, React.ComponentProps<\"button\">>(\n ({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n ref={ref}\n data-sidebar=\"rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"hover:after:bg-sidebar-border absolute inset-y-0 z-[10] hidden w-4 -translate-x-full transition-all ease-linear group-data-[side=left]:-right-[1.375rem] group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex\",\n \"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarRail.displayName = \"SidebarRail\";\n\n// ---------------------------------------------------------------------------\n// SidebarInset\n// ---------------------------------------------------------------------------\n\nconst SidebarInset: React.ForwardRefExoticComponent<\n React.ComponentProps<\"main\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"main\">>(\n ({ className, ...props }, ref) => {\n const { isPreviewMode } = useSidebar();\n return (\n <main\n ref={ref}\n className={cn(\n \"relative flex flex-1 flex-col\",\n isPreviewMode\n ? \"max-h-[calc(100svh-(--spacing(4)))]\"\n : \"min-h-svh peer-data-[variant=inset]:min-h-[calc(100svh-(--spacing(4)))]\",\n \"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarInset.displayName = \"SidebarInset\";\n\n// ---------------------------------------------------------------------------\n// SidebarInput\n// ---------------------------------------------------------------------------\n\nconst SidebarInput: React.ForwardRefExoticComponent<\n React.ComponentProps<\"input\"> & React.RefAttributes<HTMLInputElement>\n> = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, ...props }, ref) => {\n return (\n <input\n ref={ref}\n data-sidebar=\"input\"\n className={cn(\n \"bg-background focus-visible:ring-sidebar-ring h-8 w-full rounded-md border px-3 text-sm shadow-none focus-visible:ring-2 focus-visible:outline-none\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarInput.displayName = \"SidebarInput\";\n\n// ---------------------------------------------------------------------------\n// SidebarHeader / Footer\n// ---------------------------------------------------------------------------\n\nconst SidebarHeader: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n },\n);\nSidebarHeader.displayName = \"SidebarHeader\";\n\nconst SidebarFooter: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n );\n },\n);\nSidebarFooter.displayName = \"SidebarFooter\";\n\n// ---------------------------------------------------------------------------\n// SidebarSeparator\n// ---------------------------------------------------------------------------\n\nconst SidebarSeparator: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n orientation?: \"horizontal\" | \"vertical\";\n }\n>(({ className, ...props }, ref) => {\n return (\n <Separator\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"bg-sidebar-border mx-2 w-auto\", className)}\n {...props}\n />\n );\n});\nSidebarSeparator.displayName = \"SidebarSeparator\";\n\n// ---------------------------------------------------------------------------\n// SidebarContent\n// ---------------------------------------------------------------------------\n\nconst SidebarContent: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"scrollbar-none flex min-h-0 flex-1 flex-col gap-2 overflow-auto rounded group-data-[collapsible=icon]:gap-0 group-data-[collapsible=icon]:overflow-hidden group-data-[collapsible=icon]:pt-3\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarContent.displayName = \"SidebarContent\";\n\n// ---------------------------------------------------------------------------\n// SidebarGroup\n// ---------------------------------------------------------------------------\n\nconst SidebarGroup: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\n \"relative flex w-full min-w-0 flex-col p-2 group-data-[collapsible=icon]:py-0 group-data-[collapsible=icon]:pt-4\",\n className,\n )}\n {...props}\n />\n );\n },\n);\nSidebarGroup.displayName = \"SidebarGroup\";\n\nconst SidebarGroupLabel: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n asChild?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"div\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarGroupLabel.displayName = \"SidebarGroupLabel\";\n\nconst SidebarGroupAction: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n } & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 md:after:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarGroupAction.displayName = \"SidebarGroupAction\";\n\nconst SidebarGroupContent: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"w-full text-sm\", className)}\n {...props}\n />\n ),\n);\nSidebarGroupContent.displayName = \"SidebarGroupContent\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenu\n// ---------------------------------------------------------------------------\n\nconst SidebarMenu: React.ForwardRefExoticComponent<\n React.ComponentProps<\"ul\"> & React.RefAttributes<HTMLUListElement>\n> = React.forwardRef<HTMLUListElement, React.ComponentProps<\"ul\">>(\n ({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n ),\n);\nSidebarMenu.displayName = \"SidebarMenu\";\n\nconst SidebarMenuItem: React.ForwardRefExoticComponent<\n React.ComponentProps<\"li\"> & React.RefAttributes<HTMLLIElement>\n> = React.forwardRef<HTMLLIElement, React.ComponentProps<\"li\">>(\n ({ className, ...props }, ref) => (\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n ),\n);\nSidebarMenuItem.displayName = \"SidebarMenuItem\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuButton\n// ---------------------------------------------------------------------------\n\nconst sidebarMenuButtonVariants: (\n props?:\n | ({\n variant?: \"default\" | \"outline\" | null | undefined;\n size?: \"default\" | \"sm\" | \"lg\" | null | undefined;\n } & (\n | {\n class: ClassValue;\n className?: never;\n }\n | {\n class?: never;\n className: ClassValue;\n }\n | {\n class?: never;\n className?: never;\n }\n ))\n | undefined,\n) => string = cva(\n \"peer/menu-button ring-sidebar-ring hover:bg-sidebar-primary hover:text-sidebar-primary-foreground active:bg-sidebar-primary active:text-sidebar-primary-foreground data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground data-[state=open]:hover:bg-sidebar-primary data-[state=open]:hover:text-sidebar-primary-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding,background-color,color] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n {\n variants: {\n variant: {\n default:\n \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\",\n outline:\n \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-primary))]\",\n },\n size: {\n default: \"h-8 text-sm\",\n sm: \"h-7 text-xs\",\n lg: \"h-12 text-sm group-data-[collapsible=icon]:p-0!\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nconst SidebarMenuButton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n isActive?: boolean;\n } & VariantProps<typeof sidebarMenuButtonVariants> &\n React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n isActive?: boolean;\n } & VariantProps<typeof sidebarMenuButtonVariants>\n>(\n (\n {\n asChild = false,\n isActive = false,\n variant = \"default\",\n size = \"default\",\n className,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n\n const button = (\n <Comp\n ref={ref}\n data-sidebar=\"menu-button\"\n data-size={size}\n data-active={isActive}\n className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n {...props}\n />\n );\n\n return button;\n },\n);\nSidebarMenuButton.displayName = \"SidebarMenuButton\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuAction / Badge / Skeleton\n// ---------------------------------------------------------------------------\n\nconst SidebarMenuAction: React.ForwardRefExoticComponent<\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n showOnHover?: boolean;\n } & React.RefAttributes<HTMLButtonElement>\n> = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean;\n showOnHover?: boolean;\n }\n>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 md:after:hidden\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarMenuAction.displayName = \"SidebarMenuAction\";\n\nconst SidebarMenuBadge: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, React.ComponentProps<\"div\">>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n ),\n);\nSidebarMenuBadge.displayName = \"SidebarMenuBadge\";\n\nconst SidebarMenuSkeleton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean;\n } & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean;\n }\n>(({ className, showIcon = false, ...props }, ref) => {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`;\n }, []);\n\n return (\n <div\n ref={ref}\n data-sidebar=\"menu-skeleton\"\n className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n {...props}\n >\n {showIcon && (\n <Skeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <Skeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n );\n});\nSidebarMenuSkeleton.displayName = \"SidebarMenuSkeleton\";\n\n// ---------------------------------------------------------------------------\n// SidebarMenuSub\n// ---------------------------------------------------------------------------\n\nconst SidebarMenuSub: React.ForwardRefExoticComponent<\n React.ComponentProps<\"ul\"> & React.RefAttributes<HTMLUListElement>\n> = React.forwardRef<HTMLUListElement, React.ComponentProps<\"ul\">>(\n ({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n ),\n);\nSidebarMenuSub.displayName = \"SidebarMenuSub\";\n\nconst SidebarMenuSubItem: React.ForwardRefExoticComponent<\n React.ComponentProps<\"li\"> & React.RefAttributes<HTMLLIElement>\n> = React.forwardRef<HTMLLIElement, React.ComponentProps<\"li\">>(\n ({ ...props }, ref) => <li ref={ref} {...props} />,\n);\nSidebarMenuSubItem.displayName = \"SidebarMenuSubItem\";\n\nconst SidebarMenuSubButton: React.ForwardRefExoticComponent<\n React.ComponentProps<\"a\"> & {\n asChild?: boolean;\n size?: \"sm\" | \"md\";\n isActive?: boolean;\n } & React.RefAttributes<HTMLAnchorElement>\n> = React.forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<\"a\"> & {\n asChild?: boolean;\n size?: \"sm\" | \"md\";\n isActive?: boolean;\n }\n>(({ asChild = false, size = \"md\", isActive, className, ...props }, ref) => {\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...props}\n />\n );\n});\nSidebarMenuSubButton.displayName = \"SidebarMenuSubButton\";\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupAction,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInput,\n SidebarInset,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarProvider,\n SidebarRail,\n SidebarSeparator,\n useSidebar,\n};\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarInset,\n SidebarProvider,\n} from \"./sidebar\";\n\nexport interface AppShellLayoutProps {\n /** Navigation content rendered inside the sidebar's scrollable area */\n sidebarContent: React.ReactNode;\n /** Header content rendered above the main content area */\n headerContent: React.ReactNode;\n /** Main page content */\n children: React.ReactNode;\n /** Optional slot at the top of the sidebar (e.g. logo, search) */\n sidebarHeader?: React.ReactNode;\n /** Optional slot at the bottom of the sidebar (e.g. user info) */\n sidebarFooter?: React.ReactNode;\n /** Content rendered after SidebarInset (e.g. MobileBottomNav) */\n afterContent?: React.ReactNode;\n /** Enable bottom nav mode (hides sidebar on mobile, adds bottom padding) */\n useBottomNav?: boolean;\n}\n\n/**\n * Pure visual frame that replicates the RepApp layout:\n * - 13rem collapsible sidebar\n * - 52px header area\n * - rounded-xl bg-background shadow-lg content area with bg-muted gutters\n *\n * This component handles zero business logic — it simply provides the visual shell.\n */\nexport function AppShellLayout({\n sidebarContent,\n headerContent,\n children,\n sidebarHeader,\n sidebarFooter,\n afterContent,\n useBottomNav = false,\n}: AppShellLayoutProps): React.JSX.Element {\n return (\n <SidebarProvider useBottomNav={useBottomNav}>\n <div className=\"bg-muted relative flex max-h-dvh w-full overflow-hidden\">\n {/* Navigation Sidebar */}\n <Sidebar>\n {sidebarHeader && <SidebarHeader>{sidebarHeader}</SidebarHeader>}\n <SidebarContent className=\"p-4\">{sidebarContent}</SidebarContent>\n {sidebarFooter && <SidebarFooter>{sidebarFooter}</SidebarFooter>}\n </Sidebar>\n\n {/* Main Content Area */}\n <SidebarInset className=\"flex flex-1 flex-col overflow-hidden\">\n {/* Header */}\n {headerContent}\n\n {/* Screen Content */}\n <div\n className={`bg-muted flex-1 overflow-hidden md:pr-4 md:pb-4 ${useBottomNav ? \"max-md:pb-[calc(4rem+env(safe-area-inset-bottom))]\" : \"\"}`}\n >\n <div className=\"scrollbar-none bg-background text-foreground h-full overflow-auto rounded-xl shadow-lg\">\n {children}\n </div>\n </div>\n </SidebarInset>\n\n {/* After content (e.g. MobileBottomNav) */}\n {afterContent}\n </div>\n </SidebarProvider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n useContext,\n useCallback,\n useMemo,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\n\nexport type ThemeMode = \"auto\" | \"light\" | \"dark\";\n\nexport type DisplayMode = \"light\" | \"dark\";\n\nexport interface ThemeModeContextValue {\n mode: ThemeMode;\n displayMode: DisplayMode;\n setMode: (mode: ThemeMode) => void;\n autoModeEnabled: boolean;\n cycleMode: () => void;\n dataAttribute: string | undefined;\n}\n\nconst ThemeModeContext = createContext<ThemeModeContextValue | null>(null);\n\nconst darkMediaQuery =\n typeof window !== \"undefined\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n\nfunction subscribeToPrefersColorScheme(callback: () => void) {\n darkMediaQuery?.addEventListener(\"change\", callback);\n return () => darkMediaQuery?.removeEventListener(\"change\", callback);\n}\n\nfunction getSystemPrefersDark(): boolean {\n return darkMediaQuery?.matches ?? false;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\ninterface ThemeModeProviderProps {\n children: ReactNode;\n mode: ThemeMode;\n onModeChange: (mode: ThemeMode) => void;\n /** When false, auto mode is skipped in the cycle (light↔dark only). Default true. */\n autoModeEnabled?: boolean;\n}\n\nexport function ThemeModeProvider({\n children,\n mode,\n onModeChange,\n autoModeEnabled = true,\n}: ThemeModeProviderProps): React.JSX.Element {\n const systemPrefersDark = useSyncExternalStore(\n subscribeToPrefersColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n const systemMode: DisplayMode = systemPrefersDark ? \"dark\" : \"light\";\n\n const displayMode: DisplayMode = mode === \"auto\" ? systemMode : mode;\n\n const cycleMode = useCallback(() => {\n if (autoModeEnabled) {\n // Toggle displayed mode. If target matches system preference, use \"auto\".\n const target: DisplayMode = displayMode === \"light\" ? \"dark\" : \"light\";\n onModeChange(target === systemMode ? \"auto\" : target);\n } else {\n // light ↔ dark\n onModeChange(mode === \"light\" ? \"dark\" : \"light\");\n }\n }, [mode, displayMode, systemMode, onModeChange, autoModeEnabled]);\n\n const dataAttribute = getThemeModeAttribute(mode);\n\n const value = useMemo(\n () => ({\n mode,\n displayMode,\n setMode: onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n }),\n [\n mode,\n displayMode,\n onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n ],\n );\n\n return (\n <ThemeModeContext.Provider value={value}>\n {children}\n </ThemeModeContext.Provider>\n );\n}\n\n/** Access the current theme mode, setter, and cycle helper. Must be used within a `ThemeModeProvider`. */\nexport function useThemeMode(): ThemeModeContextValue {\n const ctx = useContext(ThemeModeContext);\n if (!ctx) {\n throw new Error(\"useThemeMode must be used within a ThemeModeProvider\");\n }\n return ctx;\n}\n\n/** Maps a ThemeMode to the value for `data-theme-mode`. Returns undefined for \"auto\". */\nexport function getThemeModeAttribute(mode: ThemeMode): string | undefined {\n return mode === \"auto\" ? undefined : mode;\n}\n","/**\n * System navigation items for portal apps\n * These are pre-built screens that cannot be edited or deleted in the builder.\n * They are handled by custom code (slugLibrary) rather than builder screens.\n */\n\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport const SYSTEM_NAVIGATION_SLUGS = [\n \"app-download\",\n \"contacts\",\n \"account\",\n \"messages\",\n \"my-site\",\n \"orders\",\n \"profile\",\n \"subscriptions\",\n // \"share/enrollments\",\n \"share/media\",\n \"share/playlists\",\n \"share/products\",\n \"shop\",\n \"upgrade\",\n] as const;\n\nexport type SystemNavigationSlug = (typeof SYSTEM_NAVIGATION_SLUGS)[number];\n\nexport type SystemNavigationItem = Omit<NavigationItem, \"children\">;\n\n/**\n * Slugs restricted to rep (non-customer) users.\n * Customers will not see these in navigation and cannot access these routes.\n */\nexport const REP_ONLY_SLUGS: ReadonlySet<string> =\n new Set<SystemNavigationSlug>([\"messages\", \"my-site\"]);\n\n/**\n * Check if a slug is restricted to rep users only.\n * Handles both exact matches and prefix matches (e.g. \"contacts/123\").\n */\nexport function isRepOnlySlug(slug: string | undefined | null): boolean {\n if (!slug) return false;\n const normalized = slug.startsWith(\"/\") ? slug.slice(1) : slug;\n if (REP_ONLY_SLUGS.has(normalized)) return true;\n for (const repSlug of REP_ONLY_SLUGS) {\n if (normalized.startsWith(repSlug + \"/\")) return true;\n }\n return false;\n}\n\nexport const SYSTEM_NAVIGATION_ITEMS: Record<\n SystemNavigationSlug,\n SystemNavigationItem\n> = {\n \"app-download\": {\n slug: \"app-download\",\n label: \"App Download\",\n icon: \"mobile\",\n section: \"User\",\n },\n contacts: {\n slug: \"contacts\",\n label: \"Contacts\",\n icon: \"circle-user\",\n section: \"User\",\n },\n messages: {\n slug: \"messages\",\n label: \"Messaging\",\n icon: \"message\",\n section: \"User\",\n },\n \"my-site\": {\n slug: \"my-site\",\n label: \"My Site\",\n icon: \"globe\",\n section: \"Shareables\",\n },\n orders: {\n slug: \"orders\",\n label: \"Orders\",\n icon: \"shopping-cart\",\n section: \"User\",\n },\n profile: {\n slug: \"profile\",\n label: \"Profile\",\n icon: \"user\",\n section: \"User\",\n },\n account: {\n slug: \"profile\",\n label: \"Account\",\n icon: \"user\",\n section: \"User\",\n },\n // \"share/enrollments\": {\n // slug: \"share/enrollments\",\n // label: \"Enrollments\",\n // icon: \"graduation-cap\",\n // section: \"Shareables\",\n // },\n \"share/media\": {\n slug: \"share/media\",\n label: \"Media\",\n icon: \"file-video\",\n section: \"Shareables\",\n },\n \"share/playlists\": {\n slug: \"share/playlists\",\n label: \"Playlists\",\n icon: \"list-music\",\n section: \"Shareables\",\n },\n \"share/products\": {\n slug: \"share/products\",\n label: \"Products\",\n icon: \"box\",\n section: \"Shareables\",\n },\n shop: {\n slug: \"shop\",\n label: \"Shop\",\n icon: \"store\",\n section: \"User\",\n },\n subscriptions: {\n slug: \"subscriptions\",\n label: \"Subscriptions\",\n icon: \"arrows-repeat\",\n section: \"User\",\n },\n upgrade: {\n slug: \"upgrade\",\n label: \"Upgrade\",\n icon: \"arrow-up-from-bracket\",\n section: \"User\",\n },\n} as const;\n\n/**\n * Get the default system navigation items for a new portal app.\n * These items have no screen_id - they're handled by the slugLibrary.\n */\nexport function getDefaultSystemNavigationItems(): SystemNavigationItem[] {\n return Object.values(SYSTEM_NAVIGATION_ITEMS);\n}\n\n/**\n * Check if a navigation item is a system item that doesn't require a screen_id.\n * Used by RepApp to skip screen validation for these items.\n */\nexport function isSystemNavigationItem(\n slug?: string | null,\n): slug is SystemNavigationSlug {\n if (!slug) return false;\n return SYSTEM_NAVIGATION_SLUGS.includes(slug as SystemNavigationSlug);\n}\n\n/**\n * Get icon name for a navigation item by slug.\n */\nexport function getNavigationItemIcon(slug?: string): string | undefined {\n if (!slug) return undefined;\n const systemItem = SYSTEM_NAVIGATION_ITEMS[slug as SystemNavigationSlug];\n return systemItem?.icon;\n}\n\n/**\n * Get system navigation items grouped by section.\n */\nexport function getSystemNavigationBySection(): Record<\n string,\n SystemNavigationItem[]\n> {\n const sections: Record<string, SystemNavigationItem[]> = {};\n const seenSlugs = new Set<string>();\n for (const item of Object.values(SYSTEM_NAVIGATION_ITEMS)) {\n if (item.slug && seenSlugs.has(item.slug)) continue;\n if (item.slug) seenSlugs.add(item.slug);\n const section = item.section || \"Other\";\n if (!sections[section]) {\n sections[section] = [];\n }\n sections[section].push(item);\n }\n return sections;\n}\n\n/**\n * Normalize a navigation slug by stripping a leading slash.\n */\nexport function normalizeSlug(slug: string | undefined): string {\n if (!slug) return \"\";\n return slug.startsWith(\"/\") ? slug.slice(1) : slug;\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\n\n/**\n * Build a NavigationItem from a system navigation slug.\n * Icons and labels come from SYSTEM_NAVIGATION_ITEMS (single source of truth).\n */\nfunction systemNavItem(slug: SystemNavigationSlug): NavigationItem {\n const item = SYSTEM_NAVIGATION_ITEMS[slug];\n return {\n slug: item.slug,\n label: item.label,\n icon: item.icon,\n children: [],\n };\n}\n\nfunction sectionHeader(label: string): NavigationItem {\n return { label, children: [] };\n}\n\n/**\n * Returns the default navigation items for a portal app when no\n * API data is available. All icons/labels derive from\n * SYSTEM_NAVIGATION_ITEMS so they can never drift.\n */\nexport function getDefaultNavigation(): NavigationItem[] {\n return [\n sectionHeader(\"We Commerce\"),\n systemNavItem(\"shop\"),\n systemNavItem(\"messages\"),\n systemNavItem(\"contacts\"),\n systemNavItem(\"account\"),\n sectionHeader(\"Marketing Assets\"),\n systemNavItem(\"share/products\"),\n ];\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { PanelLeft } from \"lucide-react\";\nimport { useSidebar } from \"./sidebar\";\nimport { useScreenHeaderContext } from \"./ScreenHeaderContext\";\n\nexport interface ScreenHeaderProps {\n title?: string;\n}\n\nexport function ScreenHeader({\n title,\n}: ScreenHeaderProps): React.JSX.Element | null {\n const { toggleSidebar, isMobile } = useSidebar();\n const { actions, breadcrumbs } = useScreenHeaderContext();\n\n if (!title && !breadcrumbs) return null;\n\n return (\n <div className=\"border-border bg-background sticky top-0 z-10 flex flex-row items-center border-b px-4 py-3 md:px-6\">\n {!isMobile && (\n <>\n <button\n type=\"button\"\n onClick={toggleSidebar}\n className=\"text-muted-foreground hover:text-foreground -ml-1 inline-flex items-center justify-center rounded-md p-1\"\n aria-label=\"Toggle Sidebar\"\n >\n <PanelLeft className=\"size-4\" />\n </button>\n <div className=\"bg-border mx-2 h-4 w-px\" />\n </>\n )}\n {breadcrumbs ? (\n <div className=\"min-w-0 flex-1\">{breadcrumbs}</div>\n ) : (\n <h1 className=\"text-foreground text-lg font-semibold\">{title}</h1>\n )}\n {actions && (\n <div className=\"ml-auto flex items-center gap-2\">{actions}</div>\n )}\n </div>\n );\n}\n","import type React from \"react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Bell,\n Box,\n Boxes,\n Briefcase,\n Calendar,\n CircleUser,\n Compass,\n ContactRound,\n File,\n FileText,\n FileVideo,\n Folder,\n FolderOpen,\n Globe,\n GraduationCap,\n Heart,\n House,\n Image,\n LayoutGrid,\n ListMusic,\n Mail,\n MessageCircle,\n MessageSquare,\n Repeat,\n Search,\n Settings,\n ShoppingCart,\n Star,\n Store,\n User,\n UserCog,\n Users,\n} from \"lucide-react\";\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n \"address-card\": ContactRound,\n \"arrows-repeat\": Repeat,\n bell: Bell,\n box: Box,\n \"boxes-stacked\": Boxes,\n briefcase: Briefcase,\n calendar: Calendar,\n \"cart-shopping\": ShoppingCart,\n \"circle-user\": CircleUser,\n comment: MessageCircle,\n compass: Compass,\n envelope: Mail,\n file: File,\n \"file-lines\": FileText,\n \"file-video\": FileVideo,\n folder: Folder,\n \"folder-open\": FolderOpen,\n gear: Settings,\n globe: Globe,\n \"graduation-cap\": GraduationCap,\n heart: Heart,\n house: House,\n image: Image,\n \"list-music\": ListMusic,\n \"magnifying-glass\": Search,\n message: MessageSquare,\n star: Star,\n store: Store,\n \"table-cells-large\": LayoutGrid,\n user: User,\n \"user-gear\": UserCog,\n users: Users,\n};\n\ninterface RepIconProps {\n name: string;\n className?: string;\n}\n\nexport function RepIcon({ name, className }: RepIconProps): React.JSX.Element {\n const Icon = ICON_MAP[name] ?? File;\n return <Icon className={className} />;\n}\n","/**\n * Utility functions for managing navigation state in localStorage\n */\n\nconst STORAGE_PREFIX = \"portal-app-last-tab\";\n\n/**\n * Gets the localStorage key for a specific app and top-level section\n */\nfunction getStorageKey(appDefinitionId: number, topLevelSlug: string): string {\n return `${STORAGE_PREFIX}-${appDefinitionId}-${topLevelSlug}`;\n}\n\n/**\n * Retrieves the last visited tab slug for a given section\n */\nexport function getLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n return localStorage.getItem(key);\n } catch (error) {\n console.warn(\"Failed to read from localStorage:\", error);\n return null;\n }\n}\n\n/**\n * Stores the last visited tab slug for a given section\n */\nexport function setLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n tabSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.setItem(key, tabSlug);\n } catch (error) {\n console.warn(\"Failed to write to localStorage:\", error);\n }\n}\n\n/**\n * Clears the last visited tab for a given section\n */\nexport function clearLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.removeItem(key);\n } catch (error) {\n console.warn(\"Failed to remove from localStorage:\", error);\n }\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\nimport { getLastVisitedTab } from \"./navigation-storage\";\n\n/**\n * Helper function to determine if the current route is within a top-level section\n */\nexport function isInSection(\n topLevelItem: NavigationItem,\n currentRoute: string,\n): boolean {\n const normalizedCurrentRoute = normalizeSlug(currentRoute);\n\n if (\n (!topLevelItem.children || topLevelItem.children.length <= 0) &&\n topLevelItem.slug\n ) {\n const normalizedItemSlug = normalizeSlug(topLevelItem.slug);\n if (normalizedItemSlug === normalizedCurrentRoute) {\n return true;\n }\n } else {\n return topLevelItem.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedCurrentRoute;\n });\n }\n\n return false;\n}\n\n/**\n * Get the target slug when clicking a top-level navigation item\n */\nexport function getNavigationTarget(\n item: NavigationItem,\n appDefinitionId: number,\n): string | null {\n // If it has children, it's always a section parent — navigate to a child\n if (item.children && item.children.length > 0) {\n // Try to get the last visited tab from localStorage (only if parent has a slug for the key)\n if (item.slug) {\n const lastVisitedSlug = getLastVisitedTab(appDefinitionId, item.slug);\n\n // If we have a last visited tab and it exists in the children, use it\n if (lastVisitedSlug) {\n const normalizedLastVisited = normalizeSlug(lastVisitedSlug);\n const childExists = item.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedLastVisited;\n });\n if (childExists) {\n return normalizedLastVisited;\n }\n }\n }\n\n // Default to the first child's slug\n const firstChild = item.children[0];\n if (firstChild?.slug) {\n return normalizeSlug(firstChild.slug);\n }\n\n return null;\n }\n\n // If it's a leaf node with a screen, return its slug directly (normalized)\n if (item.screen_id && item.slug) {\n return normalizeSlug(item.slug);\n }\n\n // If it's a leaf node with just a slug (system nav items like account, messages, etc.)\n if (item.slug) {\n return normalizeSlug(item.slug);\n }\n\n return null;\n}\n","import * as React from \"react\";\nimport { Ellipsis, X } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n isInSection,\n getNavigationTarget,\n} from \"@fluid-app/portal-core/navigation/utils\";\nimport type { NavigationItem } from \"../types/navigation\";\n\ninterface SdkBottomNavProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n appDefinitionId?: number;\n maxVisibleItems?: number;\n}\n\nexport function SdkBottomNav({\n navItems,\n currentSlug,\n onNavigate,\n appDefinitionId = 0,\n maxVisibleItems = 6,\n}: SdkBottomNavProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [moreOpen, setMoreOpen] = React.useState(false);\n\n if (!isMobile || !useBottomNav) return null;\n\n const navigableItems = navItems\n .filter((item) => !item.parent_id)\n .filter((item) => item.slug || (item.children && item.children.length > 0));\n\n const hasOverflow = navigableItems.length > maxVisibleItems;\n const visibleItems = hasOverflow\n ? navigableItems.slice(0, maxVisibleItems - 1)\n : navigableItems;\n const overflowItems = hasOverflow\n ? navigableItems.slice(maxVisibleItems - 1)\n : [];\n\n const handleItemClick = (item: NavigationItem) => {\n const target = getNavigationTarget(item, appDefinitionId);\n if (target) onNavigate(target);\n setMoreOpen(false);\n };\n\n return (\n <>\n <nav className=\"border-border bg-sidebar fixed right-0 bottom-0 left-0 z-40 flex h-16 items-end justify-around border-t pb-[env(safe-area-inset-bottom)]\">\n {visibleItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${\n isActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n {item.icon ? (\n <RepIcon name={item.icon} className=\"size-5\" />\n ) : (\n <span className=\"size-5\" />\n )}\n <span className=\"max-w-[72px] truncate\">{item.label}</span>\n </button>\n );\n })}\n\n {hasOverflow && (\n <button\n type=\"button\"\n onClick={() => setMoreOpen(true)}\n className={`flex flex-1 flex-col items-center justify-center gap-0.5 py-2 text-[10px] font-medium transition-colors ${\n overflowItems.some((item) => isInSection(item, currentSlug))\n ? \"text-primary\"\n : \"text-muted-foreground\"\n }`}\n >\n <Ellipsis className=\"size-5\" />\n <span>More</span>\n </button>\n )}\n </nav>\n\n {moreOpen && (\n <div className=\"fixed inset-0 z-50 flex flex-col\">\n <div\n className=\"flex-1 bg-black/50\"\n onClick={() => setMoreOpen(false)}\n aria-hidden=\"true\"\n />\n <div className=\"bg-sidebar rounded-t-2xl pb-[env(safe-area-inset-bottom)]\">\n <div className=\"flex items-center justify-between px-4 pt-4 pb-2\">\n <span className=\"text-foreground text-sm font-semibold\">\n More\n </span>\n <button\n type=\"button\"\n onClick={() => setMoreOpen(false)}\n className=\"text-muted-foreground hover:bg-sidebar-accent flex items-center justify-center rounded-full p-1\"\n aria-label=\"Close\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n <div className=\"px-2 pb-4\">\n {overflowItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${\n isActive\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent\"\n }`}\n >\n {item.icon && (\n <RepIcon name={item.icon} className=\"size-5\" />\n )}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { createPersister } from \"@fluid-app/query-persister\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { RepAppData } from \"@fluid-app/portal-core/types\";\n\n/**\n * Base query key for full app data (fluidos endpoint).\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DATA_QUERY_KEY = [\"fluid\", \"app\"] as const;\n\n/**\n * Module-level persister instance (browser only).\n * Created once when the module loads so all calls to useFluidApp share\n * the same IndexedDB connection. Applied at the query level so it works\n * regardless of which QueryClient the app provides.\n */\nconst appDataPersister =\n typeof window !== \"undefined\" && !import.meta.env?.DEV\n ? createPersister()\n : undefined;\n\n/**\n * Hook to fetch the full portal app data from the fluidos API.\n *\n * Returns a `RepAppData` object containing:\n * - `screens` — all screen definitions with normalized component trees\n * - `profile.themes` — fully-transformed ThemeDefinition[] (handles legacy + new formats)\n * - `profile.activeThemeId` — the currently active theme ID\n * - `profile.navigation.navigation_items` — sorted, recursive navigation tree\n *\n * Uses IndexedDB persistence so subsequent page loads hydrate instantly\n * from cache while revalidating in the background. The raw API response\n * (plain JSON) is cached; Color objects are recreated from cache via\n * `select` on every restore — this is fast (CPU only, no network).\n *\n * @example\n * ```tsx\n * function App() {\n * const { data: appData, isLoading } = useFluidApp();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <AppShell\n * appData={appData}\n * navigation={appData.profile.navigation.navigation_items}\n * />\n * );\n * }\n * ```\n */\nexport function useFluidApp(options?: {\n enabled?: boolean;\n}): UseQueryResult<RepAppData> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n RepAppData,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => api.app.getRaw(),\n select: transformManifestToRepAppData,\n ...(appDataPersister && { persister: appDataPersister.persisterFn }),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useEffect } from \"react\";\n\nconst SPIN_STYLE_ID = \"fluid-app-shell-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Full-page loading spinner shown while AppShell is fetching app data\n * for the first time. Uses inline styles (not Tailwind) since the theme\n * hasn't loaded yet.\n */\nexport function AppShellLoading(): React.JSX.Element {\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n Loading...\n </p>\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ChevronRight } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n} from \"@fluid-app/ui-primitives\";\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport interface CollapsibleNavItemProps {\n item: NavigationItem;\n isActive: boolean;\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function CollapsibleNavItem({\n item,\n isActive,\n currentSlug,\n onNavigate,\n}: CollapsibleNavItemProps): React.JSX.Element {\n const [open, setOpen] = useState(isActive);\n\n useEffect(() => {\n if (isActive) setOpen(true);\n }, [isActive]);\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n asChild\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton>\n {item.icon && <RepIcon name={item.icon} />}\n <span className=\"truncate\">{item.label}</span>\n <ChevronRight className=\"ml-auto size-3 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => {\n const childSlug = normalizeSlug(child.slug);\n const childIsActive = normalizeSlug(currentSlug) === childSlug;\n\n return (\n <SidebarMenuItem key={child.slug || child.label}>\n <SidebarMenuButton\n isActive={childIsActive}\n onClick={() => onNavigate(childSlug)}\n >\n <RepIcon name={child.icon || \"file\"} className=\"h-3 w-3\" />\n <span>{child.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n );\n}\n","import {\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { isInSection } from \"@fluid-app/portal-core/navigation/utils\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { CollapsibleNavItem } from \"./CollapsibleNavItem\";\n\nexport interface SdkNavigationProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkNavigation({\n navItems,\n currentSlug,\n onNavigate,\n}: SdkNavigationProps): React.JSX.Element {\n return (\n <SidebarMenu>\n {navItems.map((item, index) => {\n // Skip items without slug AND without label\n if (!item.slug && !item.label) return null;\n\n const isActive = isInSection(item, currentSlug);\n const hasChildren = item.children?.length > 0;\n\n // Items with children — collapsible\n if (hasChildren) {\n return (\n <CollapsibleNavItem\n key={item.slug || item.label}\n item={item}\n isActive={isActive}\n currentSlug={currentSlug}\n onNavigate={onNavigate}\n />\n );\n }\n\n // Items without a slug are section headers\n if (!item.slug) {\n return (\n <SidebarGroupLabel key={item.id ?? `section-${index}`}>\n {item.label}\n </SidebarGroupLabel>\n );\n }\n\n // Leaf item — no children\n const itemSlug = normalizeSlug(item.slug);\n\n return (\n <SidebarMenuItem key={item.id ?? itemSlug}>\n <SidebarMenuButton\n isActive={isActive}\n onClick={() => onNavigate(itemSlug)}\n >\n {item.icon && <RepIcon name={item.icon} />}\n <span className=\"truncate\">{item.label}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenu>\n );\n}\n","import type { NavigationItem } from \"@fluid-app/portal-core/types\";\nimport {\n isRepOnlySlug,\n type SystemNavigationItem,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\n\n/**\n * Filter navigation items by removing rep-only slugs.\n * Handles nested children: rep-only children are removed, and parents\n * with no remaining children are also removed.\n */\nexport function filterRepOnlyNavItems(\n items: NavigationItem[],\n): NavigationItem[] {\n const result: NavigationItem[] = [];\n\n for (const item of items) {\n // Leaf item with a rep-only slug — skip\n if (!item.children?.length) {\n if (isRepOnlySlug(item.slug)) continue;\n result.push(item);\n continue;\n }\n\n // Parent with a rep-only slug — skip entire group\n if (isRepOnlySlug(item.slug)) continue;\n\n // Recursively filter children\n const filteredChildren = filterRepOnlyNavItems(item.children);\n\n // Drop parent if all children were removed\n if (filteredChildren.length === 0) continue;\n\n result.push({ ...item, children: filteredChildren });\n }\n\n return result;\n}\n\n/**\n * Filter system navigation sections by removing rep-only items.\n * Sections with no remaining items are dropped entirely.\n */\nexport function filterRepOnlySections(\n sections: Record<string, SystemNavigationItem[]>,\n): Record<string, SystemNavigationItem[]> {\n const filtered: Record<string, SystemNavigationItem[]> = {};\n for (const [section, items] of Object.entries(sections)) {\n const allowed = items.filter((item) => !isRepOnlySlug(item.slug));\n if (allowed.length > 0) filtered[section] = allowed;\n }\n return filtered;\n}\n","import { useState, useRef, useEffect, useCallback, useMemo } from \"react\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useUserType } from \"../hooks/use-user-type\";\nimport { filterRepOnlySections } from \"../navigation/filter-nav-items\";\n\nexport interface QuickLinksDropdownProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function QuickLinksDropdown({\n onNavigate,\n}: QuickLinksDropdownProps): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const panelRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const close = useCallback(() => setOpen(false), []);\n\n // Click-outside + Escape to dismiss\n useEffect(() => {\n if (!open) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n if (\n panelRef.current &&\n !panelRef.current.contains(e.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(e.target as Node)\n ) {\n close();\n }\n };\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") close();\n };\n\n document.addEventListener(\"mousedown\", handleMouseDown);\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", handleMouseDown);\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [open, close]);\n\n const { isCustomer } = useUserType();\n\n const sections = useMemo(() => {\n const all = getSystemNavigationBySection();\n return isCustomer ? filterRepOnlySections(all) : all;\n }, [isCustomer]);\n\n const handleItemClick = (slug: string) => {\n onNavigate(slug);\n close();\n };\n\n return (\n <div className=\"relative\">\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={() => setOpen((prev) => !prev)}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label=\"Quick links\"\n aria-expanded={open}\n >\n <LayoutGrid className=\"h-4 w-4\" />\n </button>\n\n {open && (\n <div\n ref={panelRef}\n className=\"border-border bg-background absolute top-full right-0 z-50 mt-1 w-[700px] max-w-[90vw] overflow-hidden rounded-lg border shadow-lg\"\n >\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"bg-background flex items-center gap-2 border-b p-4\">\n <LayoutGrid className=\"text-muted-foreground h-5 w-5\" />\n <h3 className=\"text-foreground text-sm font-semibold\">\n Shortcuts\n </h3>\n </div>\n\n {/* Sections */}\n <div className=\"bg-muted space-y-6 p-6\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName} className=\"space-y-3\">\n <h3 className=\"text-foreground text-sm font-semibold\">\n {sectionName}\n </h3>\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => (\n <button\n key={item.slug}\n type=\"button\"\n onClick={() => handleItemClick(item.slug!)}\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground flex items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors\"\n >\n {item.icon && (\n <RepIcon\n name={item.icon}\n className=\"text-muted-foreground h-4 w-4 shrink-0\"\n />\n )}\n <span className=\"truncate\">{item.label}</span>\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n","import { useMemo } from \"react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useUserType } from \"../hooks/use-user-type\";\nimport { filterRepOnlySections } from \"../navigation/filter-nav-items\";\n\ninterface SdkMobileQuickLinksGridProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkMobileQuickLinksGrid({\n onNavigate,\n}: SdkMobileQuickLinksGridProps): React.JSX.Element {\n const { isCustomer } = useUserType();\n\n const sections = useMemo(() => {\n const all = getSystemNavigationBySection();\n return isCustomer ? filterRepOnlySections(all) : all;\n }, [isCustomer]);\n\n return (\n <div className=\"space-y-4\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName}>\n <h4 className=\"text-muted-foreground mb-2 text-xs font-semibold uppercase\">\n {sectionName}\n </h4>\n <div className=\"grid grid-cols-3 gap-2\">\n {items.map((item) => (\n <button\n key={item.slug}\n type=\"button\"\n onClick={() => {\n if (item.slug) onNavigate(item.slug);\n }}\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground flex h-auto flex-col items-center gap-1 rounded-lg p-2\"\n >\n {item.icon && <RepIcon name={item.icon} className=\"size-5\" />}\n <span className=\"text-xs\">{item.label}</span>\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n );\n}\n","import { useState, useCallback } from \"react\";\nimport { X, Moon, Sun, LogOut, Smartphone } from \"lucide-react\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useRepUser } from \"@fluid-app/portal-widgets/contexts\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { SdkMobileQuickLinksGrid } from \"./SdkMobileQuickLinksGrid\";\n\nexport interface SdkMobileProfileMenuProps {\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nfunction getInitials(name: string | null | undefined): string {\n if (!name) return \"U\";\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n (parts[0]?.[0] ?? \"\") + (parts[parts.length - 1]?.[0] ?? \"\")\n ).toUpperCase();\n }\n return (name[0] ?? \"U\").toUpperCase();\n}\n\nexport function SdkMobileProfileMenu({\n onNavigate,\n onLogout,\n}: SdkMobileProfileMenuProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [isOpen, setIsOpen] = useState(false);\n const user = useRepUser();\n const themeMode = useThemeMode();\n\n const handleNavigate = useCallback(\n (slug: string) => {\n setIsOpen(false);\n onNavigate(slug);\n },\n [onNavigate],\n );\n\n if (!isMobile || !useBottomNav) return null;\n\n const initials = getInitials(user?.name);\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setIsOpen(true)}\n className=\"bg-background text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-full p-1\"\n aria-label=\"Open profile menu\"\n >\n {user?.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? \"User\"}\n className=\"size-8 rounded-full object-cover\"\n />\n ) : (\n <span className=\"flex size-8 items-center justify-center text-xs font-medium\">\n {initials}\n </span>\n )}\n </button>\n\n {isOpen && (\n <div className=\"animate-in fade-in slide-in-from-bottom-4 bg-background fixed inset-0 z-50 flex flex-col duration-200\">\n <div className=\"flex items-center justify-between px-4 py-3\">\n <span className=\"text-foreground text-lg font-semibold\">\n Profile\n </span>\n <button\n type=\"button\"\n onClick={() => setIsOpen(false)}\n className=\"text-muted-foreground rounded-full p-1\"\n aria-label=\"Close profile menu\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"flex-1 overflow-auto px-4 pb-[env(safe-area-inset-bottom)]\">\n {user && (\n <div className=\"border-border flex items-center gap-3 border-b py-4\">\n {user.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? \"User\"}\n className=\"size-10 rounded-full object-cover\"\n />\n ) : (\n <span className=\"bg-muted flex size-10 items-center justify-center rounded-full text-sm font-medium\">\n {initials}\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n {user.name && (\n <p className=\"text-foreground truncate text-sm font-semibold\">\n {user.name}\n </p>\n )}\n {user.email && (\n <p className=\"text-muted-foreground truncate text-xs\">\n {user.email}\n </p>\n )}\n </div>\n </div>\n )}\n\n <div className=\"border-border border-b py-4\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"app-download\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Smartphone className=\"size-5\" />\n <span>App Download</span>\n </button>\n </div>\n\n <div className=\"border-border border-b py-4\">\n <SdkMobileQuickLinksGrid onNavigate={handleNavigate} />\n </div>\n\n <div className=\"space-y-4 py-4\">\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n {themeMode.mode === \"dark\" ? (\n <Moon className=\"size-5\" />\n ) : (\n <Sun className=\"size-5\" />\n )}\n <span>\n Theme: {themeMode.mode === \"light\" ? \"Light\" : \"Dark\"}\n </span>\n </button>\n\n {onLogout && (\n <button\n type=\"button\"\n onClick={onLogout}\n className=\"text-destructive hover:bg-sidebar-accent flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <LogOut className=\"size-5\" />\n <span>Log out</span>\n </button>\n )}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","import { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { Menu, Moon, Sun } from \"lucide-react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { QuickLinksDropdown } from \"./QuickLinksDropdown\";\nimport { SdkMobileProfileMenu } from \"./SdkMobileProfileMenu\";\n\nexport interface SdkHeaderProps {\n mobileTabs?: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nexport function SdkHeader({\n mobileTabs,\n currentSlug,\n onNavigate,\n onLogout,\n}: SdkHeaderProps): React.JSX.Element {\n const sidebar = useSidebar();\n const themeMode = useThemeMode();\n\n const ThemeIcon = themeMode.mode === \"dark\" ? Moon : Sun;\n const isMobileWithBottomNav = sidebar.isMobile && sidebar.useBottomNav;\n\n return (\n <header className=\"bg-sidebar shrink-0\">\n {isMobileWithBottomNav ? (\n <div className=\"flex h-[52px] items-center px-4\">\n <div className=\"text-muted-foreground ml-auto flex flex-none items-center justify-end\">\n <SdkMobileProfileMenu onNavigate={onNavigate} onLogout={onLogout} />\n </div>\n </div>\n ) : (\n <div className=\"flex h-[52px] items-center gap-2 px-6\">\n {/* Mobile hamburger — only show if NOT using bottom nav */}\n {sidebar.isMobile && !sidebar.useBottomNav && (\n <button\n type=\"button\"\n onClick={sidebar.toggleSidebar}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground mr-2 flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label=\"Toggle sidebar\"\n >\n <Menu className=\"size-5\" />\n </button>\n )}\n\n {/* Spacer to push actions to the right */}\n <div className=\"flex-1\" />\n\n {/* Quick Links */}\n <QuickLinksDropdown onNavigate={onNavigate} />\n\n {/* Theme toggle */}\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ml-2 flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label={`Switch theme (current: ${themeMode.mode})`}\n >\n <ThemeIcon className=\"size-3\" />\n </button>\n </div>\n )}\n\n {/* Mobile child tabs — shown below header when parent has children */}\n {isMobileWithBottomNav && mobileTabs && mobileTabs.length > 0 && (\n <nav className=\"scrollbar-none flex items-center gap-1 overflow-x-auto px-4\">\n {mobileTabs.map((tab) => {\n const tabSlug = normalizeSlug(tab.slug);\n if (!tabSlug) return null;\n\n const isActive = normalizeSlug(currentSlug) === tabSlug;\n\n return (\n <button\n key={tab.id ?? tabSlug}\n type=\"button\"\n onClick={() => onNavigate(tabSlug)}\n className={`border-b-2 px-4 py-2.5 text-sm font-medium whitespace-nowrap transition-colors ${\n isActive\n ? \"border-primary text-foreground\"\n : \"text-muted-foreground hover:border-border hover:text-foreground border-transparent\"\n }`}\n >\n {tab.label}\n </button>\n );\n })}\n </nav>\n )}\n </header>\n );\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { storeToken, type FluidAuthConfig } from \"../auth\";\n\nexport interface UseCompanySwitchOptions {\n /** Auth config forwarded to storeToken so the JWT is written to the correct cookie. */\n config?: FluidAuthConfig;\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, stores the new JWT, then\n * reloads the page so all queries re-fetch with the new company context.\n *\n * Cache clearing prevents stale data from the previous company being\n * rehydrated from the IndexedDB persisted cache after the page reloads.\n *\n * @param options - Optional config; pass `config` so that `storeToken`\n * writes the JWT to the correct cookie when a custom `cookieKey` is used.\n */\nexport function useCompanySwitch(options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const api = useFluidApi();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n // Keep config in a ref so the mutation callback always reads the latest\n // value without needing it as a useCallback dependency (avoids unstable\n // switchCompany reference when callers pass an inline config object).\n const configRef = useRef(options?.config);\n configRef.current = options?.config;\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) => api.users.switchCompany(companyId),\n onSuccess: async (data) => {\n // Clear in-memory cache and IndexedDB before storing the new token\n // to prevent stale data rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n storeToken(data.company.jwt, configRef.current);\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","import { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport type { Company } from \"@fluid-app/auth\";\n\ninterface UseCompanySwitcherOptions {\n companies: Company[];\n activeCompany?: Company;\n isCreationPage?: boolean;\n onSwitch: (id: number) => void;\n}\n\nexport function useCompanySwitcher({\n companies,\n activeCompany,\n isCreationPage = false,\n onSwitch,\n}: UseCompanySwitcherOptions) {\n const [companySearch, setCompanySearch] = useState(\"\");\n const [isOpen, setIsOpen] = useState(false);\n const [switchingToId, setSwitchingToId] = useState<number | null>(null);\n const [showConfirmDialog, setShowConfirmDialog] = useState(false);\n const [pendingCompanyId, setPendingCompanyId] = useState<number | null>(null);\n const safetyResetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n // Reset switching state when active company changes\n useEffect(() => {\n setSwitchingToId(null);\n }, [activeCompany?.id]);\n\n // Clear safety-reset timer on unmount\n useEffect(() => {\n return () => {\n if (safetyResetTimerRef.current !== null) {\n clearTimeout(safetyResetTimerRef.current);\n }\n };\n }, []);\n\n const filteredCompanies = useMemo(() => {\n if (!companySearch) return companies;\n const term = companySearch.toLowerCase();\n return companies.filter((c) => c.name.toLowerCase().includes(term));\n }, [companies, companySearch]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const scheduleSafetyReset = useCallback(() => {\n if (safetyResetTimerRef.current !== null) {\n clearTimeout(safetyResetTimerRef.current);\n }\n safetyResetTimerRef.current = setTimeout(() => {\n setSwitchingToId(null);\n safetyResetTimerRef.current = null;\n }, 10000);\n }, []);\n\n const handleCompanySelect = useCallback(\n (id: number) => {\n if (id === activeCompany?.id) return;\n if (switchingToId !== null) return;\n\n if (isCreationPage) {\n close();\n setCompanySearch(\"\");\n setPendingCompanyId(id);\n setShowConfirmDialog(true);\n return;\n }\n\n // Proceed with switch\n setSwitchingToId(id);\n onSwitch(id);\n close();\n setCompanySearch(\"\");\n scheduleSafetyReset();\n },\n [\n activeCompany?.id,\n switchingToId,\n isCreationPage,\n onSwitch,\n close,\n scheduleSafetyReset,\n ],\n );\n\n const handleConfirmSwitch = useCallback(() => {\n if (pendingCompanyId === null) return;\n setSwitchingToId(pendingCompanyId);\n onSwitch(pendingCompanyId);\n close();\n setCompanySearch(\"\");\n setPendingCompanyId(null);\n scheduleSafetyReset();\n setShowConfirmDialog(false);\n }, [pendingCompanyId, onSwitch, close, scheduleSafetyReset]);\n\n const handleCancelSwitch = useCallback(() => {\n setPendingCompanyId(null);\n setShowConfirmDialog(false);\n setCompanySearch(\"\");\n }, []);\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n if (!open) {\n setCompanySearch(\"\");\n }\n }, []);\n\n return {\n companySearch,\n setCompanySearch,\n isOpen,\n setIsOpen: handleOpenChange,\n switchingToId,\n showConfirmDialog,\n setShowConfirmDialog,\n filteredCompanies,\n handleCompanySelect,\n handleConfirmSwitch,\n handleCancelSwitch,\n close,\n };\n}\n","import { useState } from \"react\";\nimport { Building2 } from \"lucide-react\";\n\nexport function CompanyImage({\n src,\n alt,\n size,\n}: {\n size: number;\n src: string;\n alt: string;\n}) {\n const [error, setError] = useState(false);\n\n if (!src || error)\n return (\n <Building2\n style={{ height: size, width: size }}\n className=\"text-muted-foreground/25\"\n />\n );\n\n return (\n <img\n className=\"max-h-8 max-w-8 object-contain\"\n alt={alt}\n src={src}\n width={size}\n height={size}\n onError={() => setError(true)}\n />\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyImage } from \"./CompanyImage\";\n\nexport function CompanyItem({\n logo_url,\n icon_url,\n name,\n compact = false,\n}: Omit<Company, \"id\"> & { compact?: boolean }) {\n const resolvedSrc = icon_url || logo_url || \"\";\n return (\n <>\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg\">\n <CompanyImage alt={name} src={resolvedSrc} size={32} />\n </div>\n <div\n className={cn(\n \"grid text-left text-sm leading-tight\",\n compact ? \"flex-shrink-0\" : \"flex-1\",\n )}\n >\n <span className=\"truncate font-semibold\">{name}</span>\n </div>\n </>\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyItem } from \"./CompanyItem\";\n\ninterface CompanyListProps {\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n}\n\nexport function CompanyList({\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n}: CompanyListProps) {\n if (!companies.length)\n return <h2 className=\"p-2 text-center text-sm\">No matches found</h2>;\n\n return (\n <>\n {companies.map((company) => {\n const isActive = company.id === activeCompany?.id;\n const isSwitching = company.id === switchingToId;\n const isDisabled = isActive || switchingToId !== null;\n\n return (\n <button\n type=\"button\"\n key={company.id}\n disabled={isDisabled}\n onClick={() => onItemClick(company.id)}\n className={cn(\n \"flex w-full items-center gap-2 p-2 transition-colors\",\n isDisabled\n ? \"cursor-not-allowed opacity-60\"\n : \"hover:bg-accent hover:text-accent-foreground cursor-pointer\",\n isActive && \"bg-accent text-accent-foreground\",\n isSwitching && \"bg-accent/50 animate-pulse\",\n )}\n >\n <CompanyItem\n logo_url={company.logo_url}\n icon_url={company.icon_url}\n name={company.name}\n />\n {isSwitching && (\n <span className=\"text-muted-foreground ml-auto text-xs\">\n Switching...\n </span>\n )}\n </button>\n );\n })}\n </>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { Company } from \"@fluid-app/company-switcher-core\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { CompanyList } from \"./CompanyList\";\n\ninterface CompanySwitcherDropdownProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n companySearch: string;\n onSearchChange: (value: string) => void;\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n trigger: ReactNode;\n}\n\nexport function CompanySwitcherDropdown({\n isOpen,\n onOpenChange,\n companySearch,\n onSearchChange,\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n trigger,\n}: CompanySwitcherDropdownProps) {\n return (\n <DropdownMenu open={isOpen} onOpenChange={onOpenChange}>\n <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"max-h-[90dvh] w-(--radix-dropdown-menu-trigger-width) min-w-56 overflow-y-auto rounded-lg\"\n align=\"start\"\n side=\"right\"\n sideOffset={4}\n >\n <Input\n type=\"text\"\n placeholder=\"Search Company\"\n value={companySearch}\n onChange={(e) => onSearchChange(e.target.value ?? \"\")}\n className=\"bg-card text-foreground placeholder:text-muted-foreground\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n <DropdownMenuLabel className=\"text-muted-foreground text-xs\">\n Companies\n </DropdownMenuLabel>\n <CompanyList\n companies={companies}\n onItemClick={onItemClick}\n activeCompany={activeCompany}\n switchingToId={switchingToId}\n />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { Company } from \"@fluid-app/company-switcher-core\";\nimport {\n Sheet,\n SheetContent,\n SheetTrigger,\n IconButton,\n Input,\n} from \"@fluid-app/ui-primitives\";\nimport { X } from \"lucide-react\";\nimport { CompanyList } from \"./CompanyList\";\n\ninterface CompanySwitcherSheetProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n companySearch: string;\n onSearchChange: (value: string) => void;\n companies: Company[];\n activeCompany?: Company;\n onItemClick: (id: number) => void;\n switchingToId: number | null;\n trigger: ReactNode;\n}\n\nexport function CompanySwitcherSheet({\n isOpen,\n onOpenChange,\n companySearch,\n onSearchChange,\n companies,\n activeCompany,\n onItemClick,\n switchingToId,\n trigger,\n}: CompanySwitcherSheetProps) {\n return (\n <Sheet open={isOpen} onOpenChange={onOpenChange}>\n <SheetTrigger asChild>{trigger}</SheetTrigger>\n <SheetContent\n side=\"left\"\n className=\"bg-card w-full p-0 [&>button]:hidden\"\n >\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <div className=\"border-border flex items-center justify-between border-b p-4\">\n <h2 className=\"text-foreground text-lg font-semibold\">\n Select Company\n </h2>\n <IconButton\n icon={X}\n onClick={() => onOpenChange(false)}\n className=\"text-muted-foreground hover:bg-muted hover:text-foreground h-8 w-8 bg-transparent\"\n aria-label=\"Close\"\n />\n </div>\n\n {/* Search */}\n <div className=\"border-border border-b p-4\">\n <Input\n type=\"text\"\n placeholder=\"Search Company\"\n value={companySearch}\n onChange={(e) => onSearchChange(e.target.value ?? \"\")}\n className=\"bg-card text-foreground placeholder:text-muted-foreground\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n </div>\n\n {/* Companies List */}\n <div className=\"flex-1 overflow-y-auto p-2\">\n <div className=\"mb-2 px-2\">\n <h3 className=\"text-muted-foreground text-xs\">Companies</h3>\n </div>\n <CompanyList\n companies={companies}\n onItemClick={onItemClick}\n activeCompany={activeCompany}\n switchingToId={switchingToId}\n />\n </div>\n </div>\n </SheetContent>\n </Sheet>\n );\n}\n","import { useRef } from \"react\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogMedia,\n AlertDialogTitle,\n} from \"@fluid-app/ui-primitives\";\nimport { AlertTriangle } from \"lucide-react\";\n\ninterface CompanySwitcherConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function CompanySwitcherConfirmDialog({\n open,\n onOpenChange,\n onConfirm,\n onCancel,\n}: CompanySwitcherConfirmDialogProps) {\n const confirmedRef = useRef(false);\n\n return (\n <AlertDialog\n open={open}\n onOpenChange={(isOpen) => {\n onOpenChange(isOpen);\n if (!isOpen && !confirmedRef.current) {\n onCancel();\n }\n if (!isOpen) confirmedRef.current = false;\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogMedia>\n <AlertTriangle className=\"text-amber-500\" />\n </AlertDialogMedia>\n <AlertDialogTitle>Switch Company?</AlertDialogTitle>\n <AlertDialogDescription>\n Switching companies will discard any unsaved changes. Are you sure\n you want to continue?\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={() => {\n confirmedRef.current = true;\n onConfirm();\n }}\n >\n Confirm\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n );\n}\n","import type { ReactNode } from \"react\";\nimport {\n useCompanySwitcher,\n type CompanySwitcherProps,\n} from \"@fluid-app/company-switcher-core\";\nimport { CompanySwitcherDropdown } from \"./CompanySwitcherDropdown\";\nimport { CompanySwitcherSheet } from \"./CompanySwitcherSheet\";\nimport { CompanySwitcherConfirmDialog } from \"./CompanySwitcherConfirmDialog\";\nimport { CompanyItem } from \"./CompanyItem\";\nimport { ChevronDown, ChevronRight } from \"lucide-react\";\n\ninterface CompanySwitcherComponentProps extends CompanySwitcherProps {\n trigger?: ReactNode;\n}\n\nexport function CompanySwitcher({\n companies,\n activeCompany,\n show,\n isMobile,\n isCreationPage,\n onSwitch,\n trigger,\n}: CompanySwitcherComponentProps) {\n const switcher = useCompanySwitcher({\n companies,\n activeCompany,\n isCreationPage,\n onSwitch,\n });\n\n if (!show) return null;\n\n const defaultTrigger = (\n <button\n type=\"button\"\n className=\"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground flex w-full items-center gap-2 rounded-md p-2 text-left\"\n >\n <CompanyItem\n logo_url={activeCompany?.logo_url}\n icon_url={activeCompany?.icon_url}\n name={activeCompany?.name || \"\"}\n compact={isMobile}\n />\n {isMobile ? (\n <ChevronRight className=\"chevron-icons text-muted-foreground ml-2 h-3 w-3\" />\n ) : (\n <ChevronDown className=\"chevron-icons text-muted-foreground ml-auto h-3 w-3\" />\n )}\n </button>\n );\n\n const triggerElement = trigger ?? defaultTrigger;\n\n return (\n <>\n {isMobile ? (\n <CompanySwitcherSheet\n isOpen={switcher.isOpen}\n onOpenChange={switcher.setIsOpen}\n companySearch={switcher.companySearch}\n onSearchChange={switcher.setCompanySearch}\n companies={switcher.filteredCompanies}\n activeCompany={activeCompany}\n onItemClick={switcher.handleCompanySelect}\n switchingToId={switcher.switchingToId}\n trigger={triggerElement}\n />\n ) : (\n <CompanySwitcherDropdown\n isOpen={switcher.isOpen}\n onOpenChange={switcher.setIsOpen}\n companySearch={switcher.companySearch}\n onSearchChange={switcher.setCompanySearch}\n companies={switcher.filteredCompanies}\n activeCompany={activeCompany}\n onItemClick={switcher.handleCompanySelect}\n switchingToId={switcher.switchingToId}\n trigger={triggerElement}\n />\n )}\n\n <CompanySwitcherConfirmDialog\n open={switcher.showConfirmDialog}\n onOpenChange={switcher.setShowConfirmDialog}\n onConfirm={switcher.handleConfirmSwitch}\n onCancel={switcher.handleCancelSwitch}\n />\n </>\n );\n}\n","import { Skeleton } from \"@fluid-app/ui-primitives\";\n\nexport function CompanySwitcherSkeleton({ show }: { show: boolean }) {\n if (!show) return null;\n\n return (\n <div className=\"flex items-center gap-4 p-2\">\n <Skeleton className=\"size-5 shrink-0 rounded-sm bg-current\" />\n <Skeleton className=\"h-4 w-36 bg-current\" />\n </div>\n );\n}\n","import React, { useMemo } from \"react\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\nimport { useCompanySwitch } from \"../hooks/use-company-switch\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n CompanySwitcher,\n CompanySwitcherSkeleton,\n} from \"@fluid-app/company-switcher-ui\";\nimport type { Company } from \"@fluid-app/auth\";\n\n/**\n * Company switcher wired to the portal SDK's data layer.\n * Renders in the sidebar header slot; returns null for single-company users.\n */\nexport function SdkCompanySwitcher(): React.JSX.Element | null {\n const { data: user, isLoading } = useCurrentUser();\n const { switchCompany } = useCompanySwitch();\n const { isMobile } = useSidebar();\n\n const companies: Company[] = useMemo(\n () =>\n user?.companies?.map((c) => ({\n id: c.id,\n name: c.name,\n logo_url: c.logo_url ?? null,\n icon_url: c.icon_url ?? null,\n })) ?? [],\n [user?.companies],\n );\n\n // The API returns the active company as `user.company` (nested object),\n // not as a top-level `company_id`. Match by `company.id`.\n const activeCompanyId = user?.company?.id;\n const activeCompany = useMemo(\n () => companies.find((c) => c.id === activeCompanyId),\n [companies, activeCompanyId],\n );\n\n if (isLoading) {\n return <CompanySwitcherSkeleton show />;\n }\n\n if (companies.length <= 1) {\n return null;\n }\n\n return (\n <CompanySwitcher\n companies={companies}\n activeCompany={activeCompany}\n show\n isMobile={isMobile}\n onSwitch={switchCompany}\n />\n );\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\n\nexport const ACCOUNT_MANAGE_SLUGS = [\n \"profile\",\n \"orders\",\n \"subscriptions\",\n] as const satisfies readonly SystemNavigationSlug[];\n\nexport type AccountManageSlug = (typeof ACCOUNT_MANAGE_SLUGS)[number];\n\nexport interface AccountManageTab {\n key: AccountManageSlug;\n label: string;\n slug: string;\n}\n\n/** Account manage tabs with labels derived from SYSTEM_NAVIGATION_ITEMS. */\nexport const ACCOUNT_MANAGE_TABS: AccountManageTab[] = ACCOUNT_MANAGE_SLUGS.map(\n (slug) => ({\n key: slug,\n label: SYSTEM_NAVIGATION_ITEMS[slug].label,\n slug,\n }),\n);\n","/**\n * Shown when a customer attempts to access a rep-only page via direct URL.\n */\nexport function AccessDeniedScreen() {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"border-border max-w-sm rounded-lg border border-dashed p-8 text-center\">\n <h2 className=\"text-foreground text-xl font-semibold\">Access Denied</h2>\n <p className=\"text-muted-foreground mt-2\">\n You don't have permission to view this page.\n </p>\n <p className=\"text-muted-foreground mt-1 text-sm\">\n This feature is not available for your account type.\n </p>\n </div>\n </div>\n );\n}\n","/**\n * Data Source Presets Registry\n *\n * Configurable presets that allow users to select options via sub-menus\n * and fine-tune parameters in a detail panel.\n */\n\nimport type {\n SelectFieldSchema,\n NumberFieldSchema,\n} from \"../registries/property-schema-types\";\nimport type { ApiDataSource, DataSource } from \"./types\";\n\n/** Widget types that can use data sources */\nexport type DataSourceCompatibleWidget =\n | \"CarouselWidget\"\n | \"ChartWidget\"\n | \"EmbedWidget\"\n | \"ImageWidget\"\n | \"ListWidget\"\n | \"NestedWidget\"\n | \"TableWidget\"\n | \"VideoWidget\";\n\n/** Field types supported in preset config panels */\nexport type PresetConfigField = SelectFieldSchema<string> | NumberFieldSchema;\n\n/** A preset data source configuration */\nexport interface DataSourcePreset {\n /** Unique identifier for the preset */\n id: string;\n /** Human-readable name for display */\n name: string;\n /** Description of what data this endpoint provides */\n description: string;\n /** The API endpoint path (can include {variable} placeholders) */\n endpoint: string;\n /** Transformer name to apply to the data */\n transform?: string;\n /** Optional path to extract data from response (e.g., \"data.items\") */\n resultPath?: string;\n /** Which widgets this preset is compatible with */\n compatibleWidgets: DataSourceCompatibleWidget[];\n /** Optional select field shown as a sub-menu. The field's key becomes a variable in the endpoint URL. */\n menuField?: SelectFieldSchema<string>;\n /** Optional config fields rendered in the detail panel below the popover. Each field's key becomes a variable. */\n configFields?: PresetConfigField[];\n}\n\n/**\n * Pre-configured data source presets\n */\nexport const DATA_SOURCE_PRESETS: DataSourcePreset[] = [\n {\n id: \"rep-most-shared\",\n name: \"Most Shared\",\n description: \"Most shared content\",\n endpoint:\n \"/v2025-06/reps/{rep_id}/most_shared?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}\",\n resultPath: \"resources\",\n compatibleWidgets: [\n \"CarouselWidget\",\n \"ListWidget\",\n \"NestedWidget\",\n \"TableWidget\",\n ],\n menuField: {\n key: \"shareable_type\",\n label: \"Content Type\",\n type: \"select\",\n options: [\n { label: \"Products\", value: \"products\" },\n { label: \"Media\", value: \"media\" },\n { label: \"Pages\", value: \"pages\" },\n { label: \"Libraries\", value: \"libraries\" },\n { label: \"Enrollment Packs\", value: \"enrollment_packs\" },\n { label: \"Promotions\", value: \"promotions\" },\n ],\n defaultValue: \"products\",\n },\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 20,\n description: \"Maximum number of items to show\",\n },\n {\n key: \"period\",\n label: \"Time Range\",\n type: \"select\",\n options: [\n { label: \"7 days\", value: \"7d\" },\n { label: \"30 days\", value: \"30d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"1 year\", value: \"1y\" },\n { label: \"All time\", value: \"all\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"rep-most-viewed\",\n name: \"Most Viewed\",\n description: \"Most viewed content\",\n endpoint:\n \"/v2025-06/reps/{rep_id}/most_viewed?shareable_type={shareable_type}&limit={limit}&period={period}&language_iso={language_iso}\",\n resultPath: \"resources\",\n compatibleWidgets: [\n \"CarouselWidget\",\n \"ListWidget\",\n \"NestedWidget\",\n \"TableWidget\",\n ],\n menuField: {\n key: \"shareable_type\",\n label: \"Content Type\",\n type: \"select\",\n options: [\n { label: \"Products\", value: \"products\" },\n { label: \"Media\", value: \"media\" },\n { label: \"Pages\", value: \"pages\" },\n { label: \"Libraries\", value: \"libraries\" },\n { label: \"Enrollment Packs\", value: \"enrollment_packs\" },\n { label: \"Promotions\", value: \"promotions\" },\n ],\n defaultValue: \"products\",\n },\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 20,\n description: \"Maximum number of items to show\",\n },\n {\n key: \"period\",\n label: \"Time Range\",\n type: \"select\",\n options: [\n { label: \"7 days\", value: \"7d\" },\n { label: \"30 days\", value: \"30d\" },\n { label: \"90 days\", value: \"90d\" },\n { label: \"1 year\", value: \"1y\" },\n { label: \"All time\", value: \"all\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"customer-orders\",\n name: \"Orders\",\n description: \"Customer order history\",\n endpoint:\n \"/v202506/orders?customer_id={customer_id}&page[limit]={limit}&status={status}\",\n resultPath: \"orders\",\n transform: \"toOrderTableProps\",\n compatibleWidgets: [\"TableWidget\"],\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 50,\n description: \"Maximum number of orders to show\",\n },\n {\n key: \"status\",\n label: \"Status\",\n type: \"select\",\n options: [\n { label: \"All\", value: \"all\" },\n { label: \"Paid\", value: \"paid\" },\n { label: \"Unpaid\", value: \"unpaid\" },\n { label: \"Refunded\", value: \"refunded\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n {\n id: \"customer-subscriptions\",\n name: \"Subscriptions\",\n description: \"Customer subscriptions\",\n // Subscriptions endpoint is unversioned (legacy API), orders uses v202506\n endpoint:\n \"/subscriptions?customer_id={customer_id}&per_page={limit}&status={status}\",\n resultPath: \"subscriptions\",\n transform: \"toSubscriptionTableProps\",\n compatibleWidgets: [\"TableWidget\"],\n configFields: [\n {\n key: \"limit\",\n label: \"Limit\",\n type: \"number\",\n defaultValue: 10,\n min: 1,\n max: 50,\n description: \"Maximum number of subscriptions to show\",\n },\n {\n key: \"status\",\n label: \"Status\",\n type: \"select\",\n options: [\n { label: \"All\", value: \"all\" },\n { label: \"Active\", value: \"active\" },\n { label: \"Paused\", value: \"paused\" },\n { label: \"Cancelled\", value: \"cancelled\" },\n ],\n defaultValue: \"all\",\n },\n ],\n },\n];\n\n/** Lookup map for O(1) preset resolution */\nconst PRESET_MAP = new Map<string, DataSourcePreset>(\n DATA_SOURCE_PRESETS.map((p) => [p.id, p]),\n);\n\n/**\n * Resolves a preset by ID, returning the current preset definition.\n * Returns undefined if the preset ID is not recognized.\n */\nexport function resolvePreset(presetId: string): DataSourcePreset | undefined {\n return PRESET_MAP.get(presetId);\n}\n\n/**\n * If the source is an API source with a presetId, returns a copy with\n * endpoint, resultPath, and transform resolved from the current preset.\n * Falls back to stored values if the preset is unknown.\n * Non-API sources are returned as-is.\n */\nexport function resolveSource<T extends DataSource>(source: T): T {\n if (source.type !== \"api\" || !source.presetId) return source;\n\n const preset = resolvePreset(source.presetId);\n if (!preset) {\n console.warn(\n `[DataSource] Unknown preset \"${source.presetId}\", falling back to stored values`,\n );\n return source;\n }\n\n return {\n ...source,\n endpoint: preset.endpoint,\n resultPath: preset.resultPath,\n transform: preset.transform ?? source.transform,\n } as T;\n}\n","import { useQueries } from \"@tanstack/react-query\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type {\n DataSourceRegistry,\n WidgetDataResult,\n DataSourceContext,\n DataSource,\n} from \"@fluid-app/portal-core/data-sources/types\";\nimport { resolveSource } from \"@fluid-app/portal-core/data-sources/presets\";\nimport { useDataSourceRegistryConfig } from \"./registry-context\";\n\ninterface UseWidgetDataOptions {\n /** Override the default registry from context */\n registry?: DataSourceRegistry | undefined;\n /** Base URL for API calls (e.g., \"https://api.fluid.app/api\") */\n baseUrl?: string | undefined;\n}\n\n/**\n * Generates a cache key for the raw data fetch (endpoint/items identity only).\n * Transforms and targetProps are NOT included — they are applied per-widget\n * after reading from the shared cache.\n */\nfunction getSourceKey(source: DataSource): string {\n if (source.type === \"api\") {\n const varsKey = source.variables\n ? `:${JSON.stringify(source.variables)}`\n : \"\";\n // Use presetId as identity when available — keeps cache key stable across preset endpoint changes\n const identity = source.presetId ?? source.endpoint;\n return `${identity}${varsKey}`;\n }\n if (source.type === \"custom\") {\n const itemKeys =\n source.selectedItems?.map((item) => {\n return `${item.shareableType}:${item.id}`;\n }) ?? [];\n return `custom:${itemKeys.join(\",\")}`;\n }\n if (source.type === \"static\") {\n return `static:${source.staticType}:${source.selectedId}`;\n }\n return \"unknown\";\n}\n\n/**\n * Resolves cached raw data into widget props by applying each widget's own\n * transforms and targetProps mapping.\n */\nfunction resolvePropsFromQueries<T>(\n queryResults: ReadonlyArray<{\n data?: unknown;\n isLoading: boolean;\n error: Error | null;\n }>,\n sources: DataSource[],\n registry: DataSourceRegistry,\n errorConfig?: { fallback?: unknown },\n): T | undefined {\n if (sources.length === 0) return undefined;\n if (queryResults.some((q) => q.isLoading && !q.data)) return undefined;\n\n const failedQuery = queryResults.find((q) => q.error);\n const hasError = !!failedQuery?.error;\n\n if (hasError && errorConfig?.fallback) {\n return errorConfig.fallback as T;\n }\n\n if (hasError) {\n return undefined;\n }\n\n const resolvedProps: Record<string, unknown> = {};\n\n for (let i = 0; i < queryResults.length; i++) {\n const query = queryResults[i];\n const source = sources[i];\n if (!query?.data || !source) continue;\n\n // Sentry fix: FLUID-ADMIN-1DH — source or targetProps can be undefined from malformed widget config\n if (!source.targetProps) continue;\n\n // Apply this widget's transform to the cached raw data\n let result: unknown = query.data;\n if (source.transform) {\n const transformer = registry.transformers[source.transform];\n if (transformer) {\n result = transformer(result, source);\n } else {\n console.warn(`Transform \"${source.transform}\" not found in registry`);\n }\n }\n\n // Map to this widget's targetProps\n if (\n result !== null &&\n typeof result === \"object\" &&\n !Array.isArray(result) &&\n source.targetProps.length > 1\n ) {\n const resultObj = result as Record<string, unknown>;\n for (const prop of source.targetProps) {\n if (prop in resultObj) {\n resolvedProps[prop] = resultObj[prop];\n }\n }\n } else {\n for (const prop of source.targetProps) {\n resolvedProps[prop] = result;\n }\n }\n }\n\n return resolvedProps as T;\n}\n\n/**\n * Hook that fetches and resolves data sources for a widget using React Query.\n *\n * Raw API data is cached and shared across widgets with the same data source.\n * Transforms and targetProps mapping are applied per-widget after cache reads,\n * so multiple widgets can share one fetch but resolve data independently.\n */\nexport function useWidgetData<T = Record<string, unknown>>(\n widget: WidgetSchema,\n options?: UseWidgetDataOptions,\n): WidgetDataResult<T> {\n const config = useDataSourceRegistryConfig();\n const registry = options?.registry ?? config.registry;\n const baseUrl = options?.baseUrl ?? config.baseUrl;\n const getApiHeaders = config.getApiHeaders;\n const variables = config.variables;\n\n // Resolve preset-backed sources to their current endpoint/resultPath/transform\n const sources = useMemo(\n () => (widget.dataSource?.sources ?? []).map(resolveSource),\n [widget.dataSource?.sources],\n );\n const errorConfig = widget.dataSource?.error;\n const widgetId = widget.id ?? \"unknown\";\n const widgetType = widget.type;\n\n const queryResultsRef = useRef<Array<{ refetch: () => void }>>([]);\n\n // Cache only raw fetched data — no transforms or source objects stored\n const results = useQueries({\n queries: sources.map((source: DataSource) => ({\n queryKey: [\n \"portal-widget-data\",\n getSourceKey(source),\n baseUrl,\n variables,\n ] as const,\n queryFn: async ({\n signal,\n }: {\n signal: AbortSignal;\n }): Promise<unknown> => {\n const context: DataSourceContext = {\n widgetId,\n widgetType,\n signal,\n baseUrl,\n getApiHeaders,\n variables,\n };\n\n const fetcher = registry.fetchers[source.type];\n if (!fetcher) {\n throw new Error(\n `No fetcher registered for source type: ${source.type}`,\n );\n }\n\n return fetcher(source, context);\n },\n enabled: sources.length > 0,\n retry: errorConfig?.retryCount ?? 0,\n retryDelay: errorConfig?.retryDelay ?? 1000,\n refetchInterval: source.refreshInterval || false,\n })),\n });\n\n // Update ref for refetch callback\n queryResultsRef.current = results;\n\n // Aggregate loading state\n const isLoading = results.some((q) => q.isLoading);\n\n // Get first error from any failed query\n const failedQuery = results.find((q) => q.error);\n const error =\n failedQuery?.error instanceof Error\n ? failedQuery.error\n : failedQuery?.error\n ? new Error(String(failedQuery.error))\n : null;\n\n // Stable fingerprint that changes only when query data or loading states change\n const queryFingerprint = results\n .map((r) => `${r.dataUpdatedAt}:${r.isLoading}`)\n .join(\",\");\n\n // Resolve data: apply per-widget transforms and map to targetProps\n const resultsRef = useRef(results);\n resultsRef.current = results;\n\n const data = useMemo(() => {\n const querySnapshots = resultsRef.current.map((q) => ({\n data: q.data,\n isLoading: q.isLoading,\n error: q.error instanceof Error ? q.error : null,\n }));\n return resolvePropsFromQueries<T>(\n querySnapshots,\n sources,\n registry,\n errorConfig,\n );\n }, [queryFingerprint, sources, registry, errorConfig]);\n\n // Stable refetch callback using ref\n const refetch = useCallback(() => {\n queryResultsRef.current.forEach((q) => q.refetch());\n }, []);\n\n return {\n data,\n isLoading,\n error,\n refetch,\n };\n}\n","import type React from \"react\";\n\nexport function ErrorState(): React.JSX.Element {\n return (\n <div className=\"flex min-h-[120px] flex-col items-center justify-center p-6 text-center\">\n <p className=\"text-lg font-semibold\">Something Went Wrong</p>\n <p className=\"text-muted-foreground text-sm\">\n Please contact a company admin for help\n </p>\n </div>\n );\n}\n","import type React from \"react\";\nimport { memo, useMemo, type ComponentType, type ReactNode } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport { useWidgetData } from \"./use-widget-data\";\nimport { ErrorState } from \"./ErrorState\";\n\nexport interface DataAwareWidgetProps {\n widget: WidgetSchema;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Component: ComponentType<any>;\n /** Additional props to pass through (e.g., edit mode callbacks) */\n additionalProps?: Record<string, unknown>;\n /** Custom loading component */\n loadingComponent?: ReactNode;\n /** Custom error component */\n errorComponent?: (error: Error) => ReactNode;\n /** Base URL for API calls (e.g., \"https://api.fluid.app/api\") */\n baseUrl?: string | undefined;\n}\n\n// Empty object constant to avoid creating new references\nconst EMPTY_OBJECT: Record<string, unknown> = {};\n\n/**\n * Wrapper component that resolves data sources before rendering the widget.\n * Merges resolved data with static props.\n */\nfunction DataAwareWidgetImpl({\n widget,\n Component,\n additionalProps,\n loadingComponent,\n errorComponent,\n baseUrl,\n}: DataAwareWidgetProps): React.JSX.Element | null {\n // Use stable empty object if no additionalProps provided\n const stableAdditionalProps = additionalProps ?? EMPTY_OBJECT;\n const { data, isLoading, error } = useWidgetData(widget, { baseUrl });\n\n // Merge static props with resolved data\n // Data takes precedence (allows overriding defaults)\n // Memoized to prevent unnecessary child re-renders\n // Note: Must be called before any early returns to satisfy Rules of Hooks\n const mergedProps = useMemo(\n () => ({\n ...widget.props,\n ...data,\n ...stableAdditionalProps,\n }),\n [widget.props, data, stableAdditionalProps],\n );\n\n // Show loading state\n if (isLoading && widget.dataSource?.loading?.showSkeleton !== false) {\n if (loadingComponent) {\n return <>{loadingComponent}</>;\n }\n // Default skeleton - can be customized\n return (\n <div\n className=\"bg-muted animate-pulse rounded-md\"\n style={{ minHeight: 100 }}\n >\n <span className=\"sr-only\">Loading...</span>\n </div>\n );\n }\n\n // Show error state (if no fallback was applied)\n if (error && !data) {\n if (errorComponent) {\n return <>{errorComponent(error)}</>;\n }\n // Default error display\n return <ErrorState />;\n }\n\n return <Component {...mergedProps} />;\n}\n\n/**\n * Memoized wrapper component that resolves data sources before rendering the widget.\n * Prevents re-renders when parent re-renders but props haven't changed.\n */\nexport const DataAwareWidget: React.NamedExoticComponent<DataAwareWidgetProps> =\n memo(DataAwareWidgetImpl);\n","/**\n * BuilderScreenView — renders a builder screen's component_tree with\n * DataAwareWidget support for data-bound widgets.\n *\n * Uses the shared ScreenRenderer from portal-widgets under the hood,\n * but wraps data-bound widgets with DataAwareWidget from portal-core.\n */\n\nimport React, { memo } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { ScreenDefinition } from \"@fluid-app/portal-core/types\";\nimport { DataAwareWidget } from \"@fluid-app/portal-react/data-sources/DataAwareWidget\";\nimport { useRegistry } from \"@fluid-app/portal-widgets/contexts\";\n\ninterface WidgetRendererProps {\n widget: WidgetSchema;\n index: number;\n}\n\n/**\n * Renders a single widget, wrapping with DataAwareWidget if it has a dataSource.\n */\nfunction WidgetRenderer({ widget, index }: WidgetRendererProps) {\n const registry = useRegistry();\n const Component = registry[widget.type];\n\n if (!Component) {\n console.warn(\n `[BuilderScreenView] Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n if (widget.dataSource) {\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <DataAwareWidget widget={widget} Component={Component} />\n </div>\n );\n }\n\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <Component {...widget.props} />\n </div>\n );\n}\n\nexport interface BuilderScreenViewProps {\n /** The screen definition to render */\n screen: ScreenDefinition;\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\n/**\n * Renders a builder screen's component_tree with full data source support.\n * Widgets with `dataSource` config are automatically wrapped with `DataAwareWidget`\n * which fetches data and merges it with static props before rendering.\n */\nfunction BuilderScreenViewImpl({\n screen,\n className,\n}: BuilderScreenViewProps): React.JSX.Element | null {\n const widgets = screen.component_tree;\n\n if (!widgets || widgets.length === 0) {\n return (\n <div className={className ?? \"h-full w-full\"}>\n <div className=\"text-muted-foreground flex h-full items-center justify-center\">\n <p>This screen has no content yet.</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className ?? \"h-full w-full\"}>\n {widgets.map((widget, index) => {\n if (!widget) return null;\n return (\n <WidgetRenderer\n key={widget.id ?? index}\n widget={widget}\n index={index}\n />\n );\n })}\n </div>\n );\n}\n\nexport const BuilderScreenView: React.MemoExoticComponent<\n typeof BuilderScreenViewImpl\n> = memo(BuilderScreenViewImpl);\n","import { PackageOpen, Repeat, User, type LucideIcon } from \"lucide-react\";\nimport {\n ACCOUNT_MANAGE_TABS,\n type AccountManageSlug,\n} from \"@fluid-app/portal-core/navigation/account-manage\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\n\nconst ICON_MAP: Record<AccountManageSlug, LucideIcon> = {\n profile: User,\n orders: PackageOpen,\n subscriptions: Repeat,\n};\n\nconst NAV_ITEMS = ACCOUNT_MANAGE_TABS.map((tab) => ({\n ...tab,\n icon: ICON_MAP[tab.key],\n}));\n\nfunction getActiveTab(slug: string): AccountManageSlug {\n const root = slug.split(\"/\")[0];\n if (root === \"orders\") return \"orders\";\n if (root === \"subscriptions\") return \"subscriptions\";\n return \"profile\";\n}\n\nexport function AccountManageLayout({\n children,\n}: {\n children: React.ReactNode;\n}): React.JSX.Element {\n const { currentSlug, navigate } = useAppNavigation();\n const activeTab = getActiveTab(currentSlug);\n\n return (\n <div className=\"flex flex-col md:flex-row\">\n {/* Mobile: horizontal tab bar */}\n <nav className=\"flex gap-1 overflow-x-auto border-b px-4 py-2 md:hidden\">\n {NAV_ITEMS.map(({ key, slug, label, icon: Icon }) => (\n <button\n key={key}\n type=\"button\"\n onClick={() => navigate(slug)}\n className={`flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium whitespace-nowrap ${\n activeTab === key\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\"\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </nav>\n\n {/* Desktop: vertical sidebar */}\n <nav className=\"hidden w-48 shrink-0 flex-col gap-1 p-4 md:flex\">\n {NAV_ITEMS.map(({ key, slug, label, icon: Icon }) => (\n <button\n key={key}\n type=\"button\"\n onClick={() => navigate(slug)}\n className={`flex items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium ${\n activeTab === key\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"hover:bg-sidebar-primary hover:text-sidebar-primary-foreground\"\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </nav>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n );\n}\n","import { lazy, type ComponentType } from \"react\";\n\nconst ProfileScreen = lazy(() =>\n import(\"../screens/ProfileScreen\").then((m) => ({\n default: m.ProfileScreen,\n })),\n);\nconst OrdersScreen = lazy(() =>\n import(\"../screens/OrdersScreen\").then((m) => ({\n default: m.OrdersScreen,\n })),\n);\nconst SubscriptionsScreen = lazy(() =>\n import(\"../screens/SubscriptionsScreen\").then((m) => ({\n default: m.SubscriptionsScreen,\n })),\n);\nconst MessagingScreen = lazy(() =>\n import(\"../screens/MessagingScreen\").then((m) => ({\n default: m.MessagingScreen,\n })),\n);\nconst ContactsScreen = lazy(() =>\n import(\"../screens/ContactsScreen\").then((m) => ({\n default: m.ContactsScreen,\n })),\n);\nconst ShopScreen = lazy(() =>\n import(\"../screens/ShopScreen\").then((m) => ({ default: m.ShopScreen })),\n);\nconst CustomersScreen = lazy(() =>\n import(\"../screens/CustomersScreen\").then((m) => ({\n default: m.CustomersScreen,\n })),\n);\nconst ProductsScreen = lazy(() =>\n import(\"../screens/ProductsScreen\").then((m) => ({\n default: m.ProductsScreen,\n })),\n);\nconst ShareablesScreen = lazy(() =>\n import(\"../screens/ShareablesScreen\").then((m) => ({\n default: m.ShareablesScreen,\n })),\n);\nconst MySiteScreen = lazy(() =>\n import(\"../screens/MySiteScreen\").then((m) => ({\n default: m.MySiteScreen,\n })),\n);\nconst UpgradeScreen = lazy(() =>\n import(\"../screens/UpgradeScreen\").then((m) => ({\n default: m.UpgradeScreen,\n })),\n);\nconst AppDownloadScreen = lazy(() =>\n import(\"../screens/AppDownloadScreen\").then((m) => ({\n default: m.AppDownloadScreen,\n })),\n);\n\nexport const SYSTEM_SLUG_SCREEN_MAP: Record<string, ComponentType> = {\n profile: ProfileScreen,\n orders: OrdersScreen,\n subscriptions: SubscriptionsScreen,\n messages: MessagingScreen,\n contacts: ContactsScreen,\n shop: ShopScreen,\n customers: CustomersScreen,\n products: ProductsScreen,\n \"my-site\": MySiteScreen,\n \"share/products\": ShareablesScreen,\n \"share/product\": ShareablesScreen,\n \"share/media\": ShareablesScreen,\n \"share/playlists\": ShareablesScreen,\n \"share/playlist\": ShareablesScreen,\n \"share/files\": ShareablesScreen,\n upgrade: UpgradeScreen,\n \"app-download\": AppDownloadScreen,\n};\n\n/** Pre-sorted keys for greedy prefix matching (longest first). */\nconst SORTED_SCREEN_KEYS = Object.keys(SYSTEM_SLUG_SCREEN_MAP).sort(\n (a, b) => b.length - a.length,\n);\n\n/** Prefix-match a slug against screen map keys, longest match first. */\nexport function findScreenByPrefix(slug: string): ComponentType | undefined {\n for (const key of SORTED_SCREEN_KEYS) {\n if (slug === key || slug.startsWith(key + \"/\")) {\n return SYSTEM_SLUG_SCREEN_MAP[key];\n }\n }\n return undefined;\n}\n","import { Suspense, useMemo, type ComponentType } from \"react\";\nimport {\n isSystemNavigationItem,\n isRepOnlySlug,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ACCOUNT_MANAGE_SLUGS } from \"@fluid-app/portal-core/navigation/account-manage\";\nimport type { NavigationItem, ScreenDefinition } from \"../types/navigation\";\nimport { CoreScreenPlaceholder } from \"../screens/CoreScreenPlaceholder\";\nimport { AccessDeniedScreen } from \"../screens/AccessDeniedScreen\";\nimport { BuilderScreenView } from \"./BuilderScreenView\";\nimport { AccountManageLayout } from \"../account/account-management-layout\";\nimport {\n SYSTEM_SLUG_SCREEN_MAP,\n findScreenByPrefix,\n} from \"./system-screen-map\";\nimport { useUserType } from \"../hooks/use-user-type\";\n\nconst ACCOUNT_SLUGS = new Set<string>(ACCOUNT_MANAGE_SLUGS);\n\nexport interface PageRouterProps {\n currentSlug: string;\n currentNavItem?: NavigationItem | undefined;\n customPages?:\n | Record<string, ComponentType<{ slug?: string; params?: string }>>\n | undefined;\n /** Builder screen definitions (from fluidos API) */\n screens?: ScreenDefinition[] | undefined;\n baseSlug: string;\n restParams: string;\n}\n\nexport function PageRouter({\n currentSlug,\n currentNavItem,\n customPages,\n screens,\n baseSlug,\n restParams,\n}: PageRouterProps): React.JSX.Element {\n // Guard: block customer access to rep-only slugs\n const { isCustomer } = useUserType();\n\n // Build a screen lookup map by ID for O(1) access\n const screenById = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.map((s) => [s.id, s]));\n }, [screens]);\n\n // Build a screen lookup map by slug for fallback resolution\n const screenBySlug = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.filter((s) => s.slug).map((s) => [s.slug, s]));\n }, [screens]);\n\n // Resolve builder screen: screen_id first, then slug fallback\n const builderScreen = useMemo(() => {\n if (currentNavItem?.screen_id && screenById) {\n const byId = screenById.get(currentNavItem.screen_id);\n if (byId) return byId;\n }\n if (screenBySlug) {\n return (\n screenBySlug.get(currentSlug) ?? screenBySlug.get(baseSlug) ?? undefined\n );\n }\n return undefined;\n }, [\n currentNavItem?.screen_id,\n screenById,\n screenBySlug,\n currentSlug,\n baseSlug,\n ]);\n\n // 0. Block customer access to rep-only slugs\n if (isCustomer && isRepOnlySlug(currentSlug)) {\n return <AccessDeniedScreen />;\n }\n\n // 1. Custom page match (takes priority over system defaults)\n const ExactCustomPage = customPages?.[currentSlug];\n if (ExactCustomPage) {\n return <ExactCustomPage slug={currentSlug} params={restParams} />;\n }\n\n // 2. Prefix custom page match (base slug with rest params)\n if (baseSlug !== currentSlug) {\n const PrefixCustomPage = customPages?.[baseSlug];\n if (PrefixCustomPage) {\n return <PrefixCustomPage slug={currentSlug} params={restParams} />;\n }\n }\n\n // 3. System navigation item with a dedicated screen component\n // First try exact match on baseSlug, then prefix-match currentSlug\n // against screen map keys (handles detail routes like \"share/product/123\"\n // matching \"share/product\" when the slug isn't a registered nav item).\n const SystemScreen =\n SYSTEM_SLUG_SCREEN_MAP[baseSlug] ?? findScreenByPrefix(currentSlug);\n if (SystemScreen) {\n const content = (\n <Suspense\n fallback={\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"border-primary h-8 w-8 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n }\n >\n <SystemScreen />\n </Suspense>\n );\n\n if (ACCOUNT_SLUGS.has(baseSlug)) {\n return <AccountManageLayout>{content}</AccountManageLayout>;\n }\n\n return content;\n }\n\n // 4. System slug without a dedicated screen (account, my-site, share/*)\n if (isSystemNavigationItem(baseSlug)) {\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? baseSlug} />;\n }\n\n // 5. Builder screen (by screen_id or slug match)\n if (builderScreen) {\n return <BuilderScreenView screen={builderScreen} />;\n }\n\n // 6. Fallback\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? currentSlug} />;\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\n\nexport interface SlugMatch {\n matchedSlug: string;\n rest: string;\n}\n\n/**\n * Extract all slugs from a navigation tree, sorted by segment count descending.\n * Longest slugs first enables greedy prefix matching (e.g. \"share/playlists\"\n * is checked before \"share\").\n */\nexport function collectNavSlugs(items: NavigationItem[]): string[] {\n const slugs: string[] = [];\n\n for (const item of items) {\n if (item.slug) slugs.push(normalizeSlug(item.slug));\n for (const child of item.children ?? []) {\n if (child.slug) slugs.push(normalizeSlug(child.slug));\n }\n }\n\n return [...slugs].sort((a, b) => b.split(\"/\").length - a.split(\"/\").length);\n}\n\n/**\n * Find the longest registered nav slug that is a prefix of `fullSlug`.\n * Uses segment-boundary checking to prevent \"shop\" from matching \"shopping\".\n */\nexport function matchSlugPrefix(\n fullSlug: string,\n navSlugs: string[],\n): SlugMatch | undefined {\n const normalized = normalizeSlug(fullSlug);\n if (!normalized) return undefined;\n\n for (const navSlug of navSlugs) {\n if (normalized === navSlug) {\n return { matchedSlug: navSlug, rest: \"\" };\n }\n if (normalized.startsWith(navSlug + \"/\")) {\n return {\n matchedSlug: navSlug,\n rest: normalized.slice(navSlug.length + 1),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the slug portion from a full pathname by stripping the basePath prefix.\n * Returns an empty string when the pathname equals the basePath exactly.\n *\n * Examples:\n * extractSlugFromPathname(\"/contacts/123\", \"/\") → \"contacts/123\"\n * extractSlugFromPathname(\"/portal/contacts\", \"/portal\") → \"contacts\"\n * extractSlugFromPathname(\"/portal\", \"/portal\") → \"\"\n * extractSlugFromPathname(\"/\", \"/\") → \"\"\n */\nexport function extractSlugFromPathname(\n pathname: string,\n basePath: string,\n): string {\n // For root basePath, just strip the leading slash\n if (basePath === \"/\") {\n return pathname.replace(/^\\//, \"\");\n }\n\n // Strip basePath prefix, then strip leading slash from remainder\n if (pathname.startsWith(basePath)) {\n return pathname.slice(basePath.length).replace(/^\\//, \"\");\n }\n\n // Fallback: return pathname without leading slash\n return pathname.replace(/^\\//, \"\");\n}\n\nexport function isSlugInSection(\n item: NavigationItem,\n currentSlug: string,\n navSlugs: string[],\n): boolean {\n const match = matchSlugPrefix(currentSlug, navSlugs);\n const baseSlug = match?.matchedSlug ?? normalizeSlug(currentSlug);\n\n if (normalizeSlug(item.slug) === baseSlug) return true;\n return (\n item.children?.some((child) => normalizeSlug(child.slug) === baseSlug) ??\n false\n );\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that clears all client-side state:\n * - In-flight TanStack Query requests (cancelled)\n * - TanStack Query cache (in-memory)\n * - IndexedDB persisted query cache\n * - Auth tokens (cookies + localStorage)\n * - sessionStorage\n *\n * After clearing, invokes the provided callback or redirects to the given URL.\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { clearTokens } from \"../auth/token-storage\";\nimport { DEFAULT_AUTH_URL } from \"../auth/auth-redirect\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears all cached/persisted\n * client state and either redirects or calls the provided callback.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear auth tokens (cookies + localStorage)\n clearTokens();\n\n // 5. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 6. Post-logout action\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n // Default: redirect to the auth server with ?logout=true so it\n // destroys its session. Without this, the auth server's active\n // session would immediately re-authenticate and redirect back.\n const currentUrl = encodeURIComponent(window.location.href);\n window.location.href = `${DEFAULT_AUTH_URL}/?logout=true&redirect_url=${currentUrl}`;\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","import { useCallback, useState } from \"react\";\nimport { LogOut } from \"lucide-react\";\nimport {\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\n\nexport interface SdkLogoutButtonProps {\n onLogout: () => Promise<void>;\n}\n\n/**\n * Sidebar footer button that triggers the logout flow.\n * Rendered inside the AppShell sidebar footer slot.\n */\nexport function SdkLogoutButton({\n onLogout,\n}: SdkLogoutButtonProps): React.JSX.Element {\n const [isPending, setIsPending] = useState(false);\n\n const handleClick = useCallback(async () => {\n setIsPending(true);\n try {\n await onLogout();\n } catch (error) {\n console.error(\"[SdkLogoutButton] Logout failed:\", error);\n } finally {\n setIsPending(false);\n }\n }, [onLogout]);\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton onClick={handleClick} disabled={isPending}>\n <LogOut className=\"size-4\" />\n <span>{isPending ? \"Logging out\\u2026\" : \"Log out\"}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n","import {\n useState,\n useEffect,\n useCallback,\n useMemo,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport { AppShellLayout } from \"@fluid-app/portal-react/shell/AppShellLayout\";\nimport {\n ThemeModeProvider,\n type ThemeMode,\n} from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { getDefaultNavigation } from \"@fluid-app/portal-core/navigation/default-navigation\";\nimport { ScreenHeader } from \"@fluid-app/portal-react/shell/ScreenHeader\";\nimport { ScreenHeaderProvider } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { SdkBottomNav } from \"./SdkBottomNav\";\nimport type {\n RepAppData,\n ScreenDefinition,\n} from \"@fluid-app/portal-core/types\";\nimport {\n applyTheme,\n removeAllThemes,\n resolveTheme,\n} from \"@fluid-app/portal-core/theme\";\nimport { useFluidApp } from \"../hooks/use-fluid-app\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { AppShellLoading } from \"./AppShellLoading\";\nimport { SdkNavigation } from \"./SdkNavigation\";\nimport { SdkHeader } from \"./SdkHeader\";\nimport { SdkCompanySwitcher } from \"./SdkCompanySwitcher\";\nimport { PageRouter } from \"./PageRouter\";\nimport {\n collectNavSlugs,\n matchSlugPrefix,\n extractSlugFromPathname,\n} from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { AppNavigationProvider } from \"./AppNavigationContext\";\nimport { useLogout } from \"../hooks/use-logout\";\nimport { SdkLogoutButton } from \"./SdkLogoutButton\";\nimport { RepUserProvider } from \"@fluid-app/portal-widgets/contexts\";\nimport { useCurrentUser } from \"../hooks/use-current-user\";\nimport { useUserType } from \"../hooks/use-user-type\";\nimport { filterRepOnlyNavItems } from \"../navigation/filter-nav-items\";\n\nconst THEME_STORAGE_KEY = \"portal-theme-mode\";\n\nfunction getInitialThemeMode(): ThemeMode {\n if (typeof window === \"undefined\") return \"light\";\n const stored = localStorage.getItem(THEME_STORAGE_KEY);\n if (stored === \"light\" || stored === \"dark\") return stored;\n return \"light\";\n}\n\nfunction findFirstNavigableSlug(items: NavigationItem[]): string {\n for (const item of items) {\n if (item.slug) return normalizeSlug(item.slug);\n for (const child of item.children ?? []) {\n if (child.slug) return normalizeSlug(child.slug);\n }\n }\n return \"\";\n}\n\nfunction findCurrentSection(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n if (\n item.children?.some((child) => normalizeSlug(child.slug) === normalized)\n ) {\n return item;\n }\n }\n return undefined;\n}\n\nfunction findNavItem(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === normalized) return child;\n }\n }\n return undefined;\n}\n\nexport interface AppShellProps {\n /** Pre-fetched app data (skips internal useFluidApp call if provided) */\n appData?: RepAppData;\n /** Override navigation items (otherwise derived from appData/API) */\n navigation?: NavigationItem[];\n /** Custom page components keyed by slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** Base path for subpath deployments (e.g. \"/portal\"). Default: \"/\" */\n basePath?: string;\n /** Controlled current slug */\n currentSlug?: string;\n /** Navigation callback */\n onNavigate?: (slug: string) => void;\n /** Custom sidebar header slot */\n sidebarHeader?: ReactNode;\n /** Custom sidebar footer slot. When provided, replaces the default logout button. */\n sidebarFooter?: ReactNode;\n /**\n * Callback invoked after the user logs out and all client state has been cleared.\n * If not provided, a default logout button is still rendered, but no post-logout\n * action is taken (consumers should pair this with `logoutRedirectUrl`\n * or provide their own `sidebarFooter`).\n */\n onLogout?: () => void;\n /** URL to redirect to after logout (e.g. \"/login\"). Takes precedence over `onLogout`. */\n logoutRedirectUrl?: string;\n /** Render prop or static children for the content area */\n children?:\n | ReactNode\n | ((props: {\n currentSlug: string;\n currentNavItem: NavigationItem | undefined;\n }) => ReactNode);\n}\n\nexport function AppShell({\n appData: appDataProp,\n navigation: navigationProp,\n customPages,\n basePath = \"/\",\n currentSlug: controlledSlug,\n onNavigate: onNavigateProp,\n sidebarHeader,\n sidebarFooter,\n onLogout,\n logoutRedirectUrl,\n children,\n}: AppShellProps): React.JSX.Element {\n // Normalize basePath: ensure leading slash, no trailing slash\n const normalizedBasePath = useMemo(() => {\n const stripped = basePath.replace(/^\\/|\\/$/g, \"\");\n return stripped ? `/${stripped}` : \"/\";\n }, [basePath]);\n\n // Fetch full app data from fluidos API (disabled when appData is provided)\n const { data: fetchedAppData, isLoading } = useFluidApp({\n enabled: !appDataProp,\n });\n const appData = appDataProp ?? fetchedAppData;\n\n // Fetch current user for RepUserProvider\n const { data: currentUser } = useCurrentUser();\n const repUser = useMemo(\n () => ({\n name:\n currentUser?.first_name && currentUser?.last_name\n ? `${currentUser.first_name} ${currentUser.last_name}`\n : null,\n email: currentUser?.email ?? null,\n imageUrl: currentUser?.image_url ?? null,\n publicId: currentUser?.public_id,\n }),\n [\n currentUser?.first_name,\n currentUser?.last_name,\n currentUser?.email,\n currentUser?.image_url,\n currentUser?.public_id,\n ],\n );\n\n // Resolve the active theme from app data\n const activeTheme = useMemo(() => {\n if (!appData?.profile?.themes?.length) return undefined;\n const themes = appData.profile.themes;\n const activeId = appData.profile.activeThemeId;\n return activeId\n ? (themes.find((t) => t.id === activeId) ?? themes[0])\n : themes[0];\n }, [appData?.profile?.themes, appData?.profile?.activeThemeId]);\n\n // Apply theme CSS via the shared theme pipeline\n const resolvedTheme = useMemo(() => {\n if (!activeTheme) return undefined;\n return resolveTheme(activeTheme);\n }, [activeTheme]);\n\n useEffect(() => {\n if (!resolvedTheme) return;\n applyTheme(resolvedTheme);\n return () => {\n removeAllThemes();\n };\n }, [resolvedTheme]);\n\n // Mirror data-theme and data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n if (activeTheme?.id) {\n root.setAttribute(\"data-theme\", activeTheme.id);\n } else {\n root.removeAttribute(\"data-theme\");\n }\n return () => {\n root.removeAttribute(\"data-theme\");\n };\n }, [activeTheme?.id]);\n\n // Resolve navigation items\n const navItems = useMemo(() => {\n if (navigationProp) return navigationProp;\n const profileNav = appData?.profile?.navigation?.navigation_items;\n if (profileNav && profileNav.length > 0) return profileNav;\n return getDefaultNavigation();\n }, [navigationProp, appData?.profile?.navigation?.navigation_items]);\n\n // Resolve mobile navigation items (fall back to desktop nav)\n const mobileNavItems = useMemo(() => {\n const mobileNav = appData?.profile?.mobile_navigation?.navigation_items;\n if (mobileNav && mobileNav.length > 0) return mobileNav;\n return navItems;\n }, [appData?.profile?.mobile_navigation?.navigation_items, navItems]);\n\n // Filter out rep-only navigation items for customer users\n const { isCustomer } = useUserType();\n\n const filteredNavItems = useMemo(\n () => (isCustomer ? filterRepOnlyNavItems(navItems) : navItems),\n [navItems, isCustomer],\n );\n\n const filteredMobileNavItems = useMemo(\n () => (isCustomer ? filterRepOnlyNavItems(mobileNavItems) : mobileNavItems),\n [mobileNavItems, isCustomer],\n );\n\n // Resolve builder screen definitions\n const screens: ScreenDefinition[] | undefined = appData?.screens;\n\n // Collect all nav slugs for prefix matching (sorted longest-first)\n const navSlugs = useMemo(\n () => collectNavSlugs(filteredNavItems),\n [filteredNavItems],\n );\n\n // Theme mode state with localStorage persistence\n const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialThemeMode);\n\n const handleThemeModeChange = useCallback((mode: ThemeMode) => {\n setThemeMode(mode);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(THEME_STORAGE_KEY, mode);\n }\n }, []);\n\n // Mirror data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n const mode = themeMode === \"auto\" ? undefined : themeMode;\n if (mode) {\n root.setAttribute(\"data-theme-mode\", mode);\n } else {\n root.removeAttribute(\"data-theme-mode\");\n }\n return () => {\n root.removeAttribute(\"data-theme-mode\");\n };\n }, [themeMode]);\n\n // Set <meta name=\"theme-color\"> so mobile browser chrome matches the theme\n useEffect(() => {\n if (!resolvedTheme) return;\n\n let meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"theme-color\"]',\n );\n if (!meta) {\n meta = document.createElement(\"meta\");\n meta.name = \"theme-color\";\n document.head.appendChild(meta);\n }\n const metaEl = meta;\n\n const theme = resolvedTheme;\n function update(prefersDark: boolean) {\n const mode =\n themeMode === \"auto\" ? (prefersDark ? \"dark\" : \"light\") : themeMode;\n const color =\n mode === \"dark\" ? theme.dark.muted.base : theme.light.muted.base;\n const hex = color.toString();\n metaEl.content = hex;\n document.body.style.backgroundColor = hex;\n }\n\n const mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n update(mql.matches);\n\n if (themeMode === \"auto\") {\n const handler = (e: MediaQueryListEvent) => update(e.matches);\n mql.addEventListener(\"change\", handler);\n return () => {\n mql.removeEventListener(\"change\", handler);\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }\n\n return () => {\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }, [resolvedTheme, themeMode]);\n\n // Path-based routing for uncontrolled mode\n const [pathSlug, setPathSlug] = useState<string>(() => {\n if (typeof window === \"undefined\") return \"\";\n return extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n });\n\n // Sync initial path when navItems load — redirect to first nav slug if at root\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n if (typeof window === \"undefined\") return;\n\n const currentPath = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n\n if (!currentPath) {\n const firstSlug = findFirstNavigableSlug(filteredNavItems);\n if (firstSlug) {\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${firstSlug}`\n : `${normalizedBasePath}/${firstSlug}`;\n // replaceState to avoid polluting back-button history\n history.replaceState(null, \"\", targetPath);\n setPathSlug(firstSlug);\n }\n }\n }, [filteredNavItems, controlledSlug, normalizedBasePath]);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n\n const handlePopState = () => {\n const slug = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n setPathSlug(slug);\n };\n\n window.addEventListener(\"popstate\", handlePopState);\n return () => window.removeEventListener(\"popstate\", handlePopState);\n }, [controlledSlug, normalizedBasePath]);\n\n // Logout hook — always called (rules of hooks) but only wired to UI below\n const logout = useLogout({\n redirectUrl: logoutRedirectUrl,\n onLogout,\n });\n\n const activeSlug = controlledSlug ?? pathSlug;\n\n const handleNavigate = useCallback(\n (slug: string) => {\n if (onNavigateProp) {\n onNavigateProp(slug);\n return;\n }\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${slug}`\n : `${normalizedBasePath}/${slug}`;\n history.pushState(null, \"\", targetPath);\n setPathSlug(slug);\n },\n [onNavigateProp, normalizedBasePath],\n );\n\n // Resolve sidebar footer: explicit prop wins, otherwise render default logout button\n const resolvedSidebarFooter = useMemo(\n () =>\n sidebarFooter !== undefined ? (\n sidebarFooter\n ) : (\n <SdkLogoutButton onLogout={logout} />\n ),\n [sidebarFooter, logout],\n );\n\n // Derive base slug and rest params via prefix matching\n const slugMatch = useMemo(\n () => matchSlugPrefix(activeSlug, navSlugs),\n [activeSlug, navSlugs],\n );\n const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);\n const restParams = slugMatch?.rest ?? \"\";\n\n // Derive mobile sub-tabs for the current section\n const mobileCurrentSection = findCurrentSection(\n filteredMobileNavItems,\n baseSlug,\n );\n const mobileSecondLevelTabs = mobileCurrentSection?.children ?? [];\n const currentNavItem = findNavItem(filteredNavItems, baseSlug);\n\n // Resolve screen title from nav label or screen definition name\n const screenTitle =\n currentNavItem?.label ||\n screens?.find((s) => s.id === currentNavItem?.screen_id)?.name ||\n undefined;\n\n // Determine content to render\n const content =\n typeof children === \"function\"\n ? children({ currentSlug: activeSlug, currentNavItem })\n : (children ?? (\n <ScreenHeaderProvider>\n <ScreenHeader title={screenTitle} />\n <PageRouter\n currentSlug={activeSlug}\n currentNavItem={currentNavItem}\n customPages={customPages}\n screens={screens}\n baseSlug={baseSlug}\n restParams={restParams}\n />\n </ScreenHeaderProvider>\n ));\n\n // Show loading state while app data is being fetched for the first time.\n // This check is placed after all hooks to satisfy React's rules of hooks.\n if (isLoading && !appData) {\n return <AppShellLoading />;\n }\n\n return (\n <RepUserProvider user={repUser}>\n <ThemeModeProvider\n mode={themeMode}\n onModeChange={handleThemeModeChange}\n autoModeEnabled={false}\n >\n <div\n className=\"font-body\"\n data-theme={activeTheme?.id}\n data-theme-mode={themeMode === \"auto\" ? undefined : themeMode}\n >\n <AppNavigationProvider\n currentSlug={activeSlug}\n basePath={normalizedBasePath}\n navigate={handleNavigate}\n >\n <AppShellLayout\n sidebarContent={\n <SdkNavigation\n navItems={filteredNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n />\n }\n headerContent={\n <SdkHeader\n mobileTabs={mobileSecondLevelTabs}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n onLogout={logout}\n />\n }\n sidebarHeader={\n sidebarHeader !== undefined ? (\n sidebarHeader\n ) : (\n <SdkCompanySwitcher />\n )\n }\n sidebarFooter={resolvedSidebarFooter}\n useBottomNav\n afterContent={\n <SdkBottomNav\n navItems={filteredMobileNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n appDefinitionId={appData?.definition_id ?? 0}\n />\n }\n >\n {content}\n </AppShellLayout>\n </AppNavigationProvider>\n </div>\n </ThemeModeProvider>\n </RepUserProvider>\n );\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../client/types\";\nimport type { FluidAuthConfig } from \"../auth/types\";\nimport { getStoredToken } from \"../auth/token-storage\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to \"https://api.fluid.app\"\n * - getAuthToken: reads from the SDK's token storage (cookie/localStorage)\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"https://api.fluid.app\",\n getAuthToken: () => getStoredToken() ?? null,\n ...overrides,\n };\n}\n\n/**\n * Creates a FluidAuthConfig with sensible defaults.\n *\n * The SDK's FluidAuthProvider already applies defaults for all fields,\n * so an empty config works out of the box. Use overrides to customize:\n * ```ts\n * const config = createDefaultAuthConfig({ authUrl: \"https://login.example.com\" });\n * ```\n */\nexport function createDefaultAuthConfig(\n overrides?: Partial<FluidAuthConfig>,\n): FluidAuthConfig {\n return { ...overrides };\n}\n","import { StrictMode, type ReactNode, type ComponentType } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { FluidAuthProvider } from \"../providers/FluidAuthProvider\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { RequireAuth } from \"../components/RequireAuth\";\nimport { AppShell, type AppShellProps } from \"../shell/AppShell\";\nimport {\n createDefaultFluidConfig,\n createDefaultAuthConfig,\n} from \"../config/defaults\";\nimport type { FluidSDKConfig } from \"../client/types\";\nimport type { FluidAuthConfig } from \"../auth/types\";\n\n/**\n * Configuration for `createPortal()`.\n *\n * Provides a single-call entry point that sets up the full provider hierarchy\n * (auth, SDK, shell) with sensible defaults. Every field is optional.\n *\n * For full control, skip `createPortal()` and compose providers manually:\n * ```tsx\n * import { FluidAuthProvider, FluidProvider, RequireAuth, AppShell } from \"@fluid-app/portal-sdk\";\n * ```\n */\nexport interface PortalConfig {\n /** Custom page components keyed by navigation slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** API client config overrides (merged with defaults) */\n fluid?: Partial<FluidSDKConfig>;\n /** Auth config overrides (merged with defaults) */\n auth?: Partial<FluidAuthConfig>;\n /** Root element ID. @default \"root\" */\n rootId?: string;\n /** AppShell props passthrough (basePath, sidebarHeader, etc.) */\n shell?: Omit<AppShellProps, \"customPages\">;\n /** Wrap AppShell with additional providers (inserted inside RequireAuth, below FluidProvider) */\n providers?: ComponentType<{ children: ReactNode }>;\n /** Disable StrictMode. @default false */\n disableStrictMode?: boolean;\n}\n\n/**\n * Bootstrap a Fluid portal with a single function call.\n *\n * Sets up the full provider hierarchy (FluidAuthProvider → FluidProvider → RequireAuth → AppShell)\n * and renders into the DOM.\n *\n * @example Minimal usage\n * ```ts\n * import { createPortal } from \"@fluid-app/portal-sdk\";\n * import \"./index.css\";\n *\n * createPortal();\n * ```\n *\n * @example With custom pages\n * ```ts\n * import { createPortal } from \"@fluid-app/portal-sdk\";\n * import { customPages } from \"./portal.config\";\n * import \"./index.css\";\n *\n * createPortal({ customPages });\n * ```\n *\n * @example With overrides\n * ```ts\n * createPortal({\n * customPages,\n * fluid: { baseUrl: \"https://my-api.example.com\" },\n * auth: { authUrl: \"https://login.example.com\" },\n * shell: { basePath: \"/portal\" },\n * });\n * ```\n */\nexport function createPortal(config: PortalConfig = {}): void {\n const rootElement = document.getElementById(config.rootId ?? \"root\");\n if (!rootElement) {\n throw new Error(\n `Portal root element \"#${config.rootId ?? \"root\"}\" not found`,\n );\n }\n\n const fluidConfig = createDefaultFluidConfig(config.fluid);\n const authConfig = createDefaultAuthConfig(config.auth);\n\n let app: ReactNode = (\n <AppShell customPages={config.customPages} {...config.shell} />\n );\n\n if (config.providers) {\n const Providers = config.providers;\n app = <Providers>{app}</Providers>;\n }\n\n const tree = (\n <FluidAuthProvider config={authConfig}>\n <FluidProvider config={fluidConfig}>\n <RequireAuth>{app}</RequireAuth>\n </FluidProvider>\n </FluidAuthProvider>\n );\n\n createRoot(rootElement).render(\n config.disableStrictMode ? tree : <StrictMode>{tree}</StrictMode>,\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => api.app.getRaw(),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport type { UserPermissions, PermissionAction } from \"../types/permissions\";\n\n/**\n * Base query key for permissions data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const PERMISSIONS_QUERY_KEY = [\"fluid\", \"permissions\"] as const;\n\n/**\n * Result of useFluidPermissions hook\n */\nexport interface UseFluidPermissionsResult {\n /** Raw permissions query result */\n query: UseQueryResult<UserPermissions>;\n /** Permissions data (alias for query.data) */\n permissions: UserPermissions | undefined;\n /** Check if user has a specific permission */\n can: (resource: string, action?: PermissionAction) => boolean;\n /** Check if user is a super admin */\n isSuperAdmin: boolean;\n}\n\n/**\n * Hook to fetch and check user permissions\n *\n * @example\n * ```tsx\n * function TeamSettings() {\n * const { can, isSuperAdmin } = useFluidPermissions();\n *\n * if (!can(\"team\", \"manage\")) {\n * return <AccessDenied />;\n * }\n *\n * return <TeamSettingsForm canDelete={can(\"team\", \"delete\")} />;\n * }\n * ```\n */\nexport function useFluidPermissions(): UseFluidPermissionsResult {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n const query = useQuery({\n queryKey: scopeKey(PERMISSIONS_QUERY_KEY),\n queryFn: () => api.permissions.get(),\n });\n\n const permissions = query.data;\n\n // Memoize the can function to maintain referential stability\n const can = useMemo(() => {\n return (resource: string, action: PermissionAction = \"view\"): boolean => {\n if (!permissions) {\n return false;\n }\n\n // Super admins have all permissions\n if (permissions.is_super_admin) {\n return true;\n }\n\n const resourcePermissions = permissions.permissions[resource];\n if (!resourcePermissions) {\n return false;\n }\n\n return resourcePermissions[action] ?? false;\n };\n }, [permissions]);\n\n const isSuperAdmin = permissions?.is_super_admin ?? false;\n\n return {\n query,\n permissions,\n can,\n isSuperAdmin,\n };\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useFluidApi } from \"./use-fluid-api\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport type { Rep } from \"../types/rep\";\n\n/**\n * Base query key for current rep data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const CURRENT_REP_QUERY_KEY = [\"fluid\", \"currentRep\"] as const;\n\n/**\n * Hook to fetch the currently authenticated rep's profile\n *\n * @example\n * ```tsx\n * function RepHeader() {\n * const { data: rep, isLoading } = useCurrentRep();\n *\n * if (isLoading) return <Skeleton />;\n *\n * return (\n * <div>\n * <Avatar src={rep?.avatar_url} />\n * <span>{rep?.first_name} {rep?.last_name}</span>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCurrentRep(): UseQueryResult<Rep> {\n const api = useFluidApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(CURRENT_REP_QUERY_KEY),\n queryFn: () => api.reps.current(),\n });\n}\n","import type { CalendarEvent } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number, hour: number, minute: number = 0): Date {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n d.setHours(hour, minute, 0, 0);\n return d;\n}\n\nexport const DEMO_CALENDAR_EVENTS: readonly CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Standup\",\n description: { body: \"Daily sync with the sales team\" },\n color: \"#4f46e5\",\n start: daysFromNow(0, 9, 0).toISOString(),\n end: daysFromNow(0, 9, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n },\n {\n id: 2,\n title: \"Client Review - Acme Corp\",\n description: { body: \"Quarterly business review with key accounts\" },\n color: \"#059669\",\n start: daysFromNow(2, 14, 0).toISOString(),\n end: daysFromNow(2, 15, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"Conference Room B\",\n },\n {\n id: 3,\n title: \"Product Training Webinar\",\n description: { body: \"New product line training for the field\" },\n color: \"#d97706\",\n start: daysFromNow(4, 11, 0).toISOString(),\n end: daysFromNow(4, 12, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n url: \"https://example.com/webinar\",\n },\n {\n id: 4,\n title: \"Regional Conference\",\n description: { body: \"Annual West Coast regional conference\" },\n color: \"#dc2626\",\n start: daysFromNow(10, 8, 0).toISOString(),\n end: daysFromNow(12, 17, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"San Diego Convention Center\",\n isAllDay: true,\n },\n];\n","/**\n * Calendar events hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CalendarEvent, CalendarEventDescription } from \"./hook-types\";\nimport { DEMO_CALENDAR_EVENTS } from \"./demo-data/calendar-events\";\n\n// Re-export types so external consumers are unaffected\nexport type { CalendarEvent, CalendarEventDescription };\n\n/**\n * Result type for useCalendarEvents hook.\n * Uses QueryResult<CalendarEvent[]> with default Error type.\n */\nexport type UseCalendarEventsResult = QueryResult<CalendarEvent[]>;\n\n/**\n * Hook to fetch calendar events.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCalendarEvents(): UseCalendarEventsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CALENDAR_EVENTS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { Todo } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nexport const DEMO_TODOS: readonly Todo[] = [\n {\n id: 1,\n body: \"Follow up with Sarah about Q2 order\",\n dueAt: daysFromNow(1),\n completedAt: null,\n createdAt: daysFromNow(-2),\n contactName: \"Sarah Johnson\",\n },\n {\n id: 2,\n body: \"Send pricing proposal to Acme Corp\",\n dueAt: daysFromNow(7),\n completedAt: null,\n createdAt: daysFromNow(-1),\n contactName: \"Mike Chen\",\n },\n {\n id: 3,\n body: \"Schedule onboarding call with new team member\",\n dueAt: daysFromNow(3),\n completedAt: null,\n createdAt: daysFromNow(0),\n contactName: \"Emily Rodriguez\",\n },\n {\n id: 4,\n body: \"Review and approve Q1 expense report\",\n dueAt: daysFromNow(-1),\n completedAt: daysFromNow(-1),\n createdAt: daysFromNow(-5),\n contactName: null,\n },\n {\n id: 5,\n body: \"Prepare presentation for Friday team meeting\",\n dueAt: daysFromNow(4),\n completedAt: daysFromNow(0),\n createdAt: daysFromNow(-3),\n contactName: null,\n },\n];\n","/**\n * Todos hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { Todo } from \"./hook-types\";\nimport { DEMO_TODOS } from \"./demo-data/todos\";\n\n// Re-export type so external consumers are unaffected\nexport type { Todo };\n\n/**\n * Result type for useTodos hook.\n * Uses QueryResult<Todo[]> with default Error type.\n */\nexport type UseTodosResult = QueryResult<Todo[]>;\n\n/**\n * Hook to fetch todo items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useTodos(): UseTodosResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_TODOS],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n","import type { Activity } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\nexport const DEMO_ACTIVITIES: readonly Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"New Order\",\n targetName: \"Wellness Starter Kit\",\n timestamp: hoursAgo(1),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Referral from LinkedIn campaign\",\n timestamp: hoursAgo(3),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Emily Rodriguez\",\n avatarUrl: null,\n activityType: \"Page View\",\n targetName: \"Product Catalog\",\n timestamp: hoursAgo(5),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"David Park\",\n avatarUrl: null,\n activityType: \"Cart Items Added\",\n targetName: \"Premium Bundle (3 items)\",\n timestamp: hoursAgo(8),\n slug: \"cart_items_added\",\n },\n {\n id: 5,\n userName: \"Lisa Thompson\",\n avatarUrl: null,\n activityType: \"Video Watched\",\n targetName: \"Getting Started Tutorial\",\n timestamp: hoursAgo(12),\n slug: \"video_complete\",\n },\n {\n id: 6,\n userName: \"James Wilson\",\n avatarUrl: null,\n activityType: \"Message Received\",\n targetName: \"Question about shipping\",\n timestamp: hoursAgo(18),\n slug: \"message_received\",\n },\n {\n id: 7,\n userName: \"Rachel Kim\",\n avatarUrl: null,\n activityType: \"Smart Link Clicked\",\n targetName: \"Holiday Promo Link\",\n timestamp: hoursAgo(24),\n slug: \"smart_link_clicked\",\n },\n {\n id: 8,\n userName: \"Tom Martinez\",\n avatarUrl: null,\n activityType: \"Review Left\",\n targetName: \"Essential Oil Set - 5 stars\",\n timestamp: hoursAgo(36),\n slug: \"review_left\",\n },\n];\n","/**\n * Activities hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport {\n ACTIVITY_SLUGS,\n isActivitySlug,\n type Activity,\n type ActivitySlug,\n} from \"./hook-types\";\nimport { DEMO_ACTIVITIES } from \"./demo-data/activities\";\n\n// Re-export types and constants so external consumers are unaffected\nexport { ACTIVITY_SLUGS, isActivitySlug };\nexport type { Activity, ActivitySlug };\n\n/**\n * Result type for useActivities hook.\n * Uses QueryResult generic with Activity[] and default Error type.\n */\nexport type UseActivitiesResult = QueryResult<Activity[]>;\n\n/**\n * Hook to fetch recent activities.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useActivities(): UseActivitiesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_ACTIVITIES],\n isLoading: false,\n isError: false,\n };\n}\n","import type { CatchUp } from \"../hook-types\";\n\nexport const DEMO_CATCHUPS: readonly CatchUp[] = [\n {\n id: 1,\n suggestion_title: \"Sarah Johnson hasn't ordered in 30 days\",\n },\n {\n id: 2,\n suggestion_title: \"Mike Chen's subscription is expiring soon\",\n },\n {\n id: 3,\n suggestion_title: \"Emily Rodriguez left a cart with 3 items\",\n },\n];\n","/**\n * Catch ups hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CatchUp } from \"./hook-types\";\nimport { DEMO_CATCHUPS } from \"./demo-data/catchups\";\n\n// Re-export type so external consumers are unaffected\nexport type { CatchUp };\n\n/**\n * Result type for useCatchUps hook.\n * Uses QueryResult<CatchUp[]> with default Error type.\n */\nexport type UseCatchUpsResult = QueryResult<CatchUp[]>;\n\n/**\n * Hook to fetch catch up items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCatchUps(): UseCatchUpsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CATCHUPS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { MySiteData } from \"../hook-types\";\n\nexport const DEMO_MYSITE: MySiteData = {\n url: \"https://my-portal.example.com\",\n views: 1243,\n leads: 37,\n userName: \"Demo User\",\n};\n","/**\n * MySite hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResultNullable } from \"./hook-types\";\nimport type { MySiteData } from \"./hook-types\";\nimport { DEMO_MYSITE } from \"./demo-data/mysite\";\n\n// Re-export type so external consumers are unaffected\nexport type { MySiteData };\n\n/**\n * Result type for useMySite hook.\n * Uses QueryResultNullable since MySite data may not be available.\n */\nexport type UseMySiteResult = QueryResultNullable<MySiteData>;\n\n/**\n * Hook to fetch MySite data.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useMySite(): UseMySiteResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: DEMO_MYSITE,\n isLoading: false,\n isError: false,\n };\n}\n","import type { Conversation, Message } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONVERSATIONS: readonly Conversation[] = [\n {\n id: \"conv-1\",\n title: \"Sarah Johnson\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-2\",\n name: \"Sarah Johnson\",\n email: \"sarah.johnson@example.com\",\n isOnline: true,\n },\n ],\n lastMessage: {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n unreadCount: 1,\n status: \"active\",\n createdAt: daysAgo(30),\n updatedAt: hoursAgo(1),\n },\n {\n id: \"conv-2\",\n title: \"Mike Chen\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-3\",\n name: \"Mike Chen\",\n email: \"mike.chen@acmecorp.com\",\n isOnline: false,\n },\n ],\n lastMessage: {\n id: \"msg-2-2\",\n conversationId: \"conv-2\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"I've attached the updated pricing sheet. Let me know if you have any questions!\",\n timestamp: hoursAgo(5),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(14),\n updatedAt: hoursAgo(5),\n },\n {\n id: \"conv-3\",\n title: \"Team Updates\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-4\",\n name: \"Emily Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n },\n { id: \"user-5\", name: \"David Park\", email: \"david.park@email.com\" },\n ],\n lastMessage: {\n id: \"msg-3-4\",\n conversationId: \"conv-3\",\n senderId: \"user-4\",\n senderName: \"Emily Rodriguez\",\n type: \"text\",\n content:\n \"The new product samples arrived today. Will distribute to the team tomorrow.\",\n timestamp: daysAgo(1),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(60),\n updatedAt: daysAgo(1),\n },\n];\n\nexport const DEMO_MESSAGES: readonly Message[] = [\n {\n id: \"msg-1-1\",\n conversationId: \"conv-1\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"Hi Sarah! I wanted to follow up on the Wellness Starter Kit. We have a special promotion running this month.\",\n timestamp: hoursAgo(3),\n isRead: true,\n },\n {\n id: \"msg-1-2\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content:\n \"That's perfect timing! I was just thinking about reordering. What's the promo?\",\n timestamp: hoursAgo(2),\n isRead: true,\n },\n {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n];\n","/**\n * Conversations hooks - stub implementations for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Conversation, Message } from \"../types/screen-types\";\nimport type { QueryResult } from \"./hook-types\";\nimport { DEMO_CONVERSATIONS, DEMO_MESSAGES } from \"./demo-data/conversations\";\n\n// =============================================================================\n// useConversations Hook\n// =============================================================================\n\n/**\n * Result type for useConversations hook.\n * Uses QueryResult<Conversation[]> with default Error type.\n */\nexport type UseConversationsResult = QueryResult<Conversation[]>;\n\n/**\n * Hook to fetch all conversations.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @returns UseConversationsResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: conversations, isLoading, isError } = useConversations();\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return conversations.map(conv => <ConversationItem key={conv.id} {...conv} />);\n * ```\n */\nexport function useConversations(): UseConversationsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONVERSATIONS],\n isLoading: false,\n isError: false,\n };\n}\n\n// =============================================================================\n// useConversationMessages Hook\n// =============================================================================\n\n/**\n * Result type for useConversationMessages hook.\n * Uses QueryResult<Message[]> with default Error type.\n */\nexport type UseConversationMessagesResult = QueryResult<Message[]>;\n\n/**\n * Hook to fetch messages for a specific conversation.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param conversationId - The ID of the conversation to fetch messages for\n * @returns UseConversationMessagesResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: messages, isLoading, isError } = useConversationMessages(conversationId);\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return messages.map(msg => <MessageBubble key={msg.id} {...msg} />);\n * ```\n */\nexport function useConversationMessages(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _conversationId: string,\n): UseConversationMessagesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _conversationId\n return {\n data: [...DEMO_MESSAGES],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Screen Types - Type definitions for core feature screens\n *\n * All status and type unions are derived from constants for single source of truth.\n * Use the constants (e.g., CONVERSATION_STATUSES.active) for type-safe comparisons.\n */\n\n// =============================================================================\n// Messaging Types\n// =============================================================================\n\n/**\n * Conversation status constant - single source of truth.\n */\nexport const CONVERSATION_STATUSES = {\n active: \"active\",\n archived: \"archived\",\n muted: \"muted\",\n} as const;\n\n/**\n * Union type derived from CONVERSATION_STATUSES constant.\n */\nexport type ConversationStatus =\n (typeof CONVERSATION_STATUSES)[keyof typeof CONVERSATION_STATUSES];\n\n/**\n * Message type constant - single source of truth.\n */\nexport const MESSAGE_TYPES = {\n text: \"text\",\n image: \"image\",\n file: \"file\",\n system: \"system\",\n} as const;\n\n/**\n * Union type derived from MESSAGE_TYPES constant.\n */\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\nexport interface Participant {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n readonly avatarUrl?: string;\n readonly isOnline?: boolean;\n}\n\n/**\n * Message attachment type - extracted for reusability and clarity.\n */\nexport interface MessageAttachment {\n readonly id: string;\n readonly name: string;\n readonly url: string;\n readonly type: string;\n readonly size?: number;\n}\n\nexport interface Message {\n readonly id: string;\n readonly conversationId: string;\n readonly senderId: string;\n readonly senderName: string;\n readonly senderAvatarUrl?: string;\n readonly type: MessageType;\n readonly content: string;\n readonly timestamp: string;\n readonly isRead: boolean;\n readonly attachments?: readonly MessageAttachment[];\n}\n\nexport interface Conversation {\n readonly id: string;\n readonly title: string;\n readonly participants: readonly Participant[];\n readonly lastMessage?: Message;\n readonly unreadCount: number;\n readonly status: ConversationStatus;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n\n// =============================================================================\n// Contacts Types\n// =============================================================================\n\n/**\n * Contact status constant - single source of truth.\n */\nexport const CONTACT_STATUSES = {\n active: \"active\",\n inactive: \"inactive\",\n lead: \"lead\",\n prospect: \"prospect\",\n} as const;\n\n/**\n * Union type derived from CONTACT_STATUSES constant.\n */\nexport type ContactStatus =\n (typeof CONTACT_STATUSES)[keyof typeof CONTACT_STATUSES];\n\n/**\n * Contact type constant - single source of truth.\n */\nexport const CONTACT_TYPES = {\n individual: \"individual\",\n company: \"company\",\n} as const;\n\n/**\n * Union type derived from CONTACT_TYPES constant.\n */\nexport type ContactType = (typeof CONTACT_TYPES)[keyof typeof CONTACT_TYPES];\n\nexport interface ContactAddress {\n readonly street?: string;\n readonly city?: string;\n readonly state?: string;\n readonly postalCode?: string;\n readonly country?: string;\n}\n\nexport interface Contact {\n readonly id: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly email: string;\n readonly phone?: string;\n readonly company?: string;\n readonly jobTitle?: string;\n readonly avatarUrl?: string;\n readonly status: ContactStatus;\n readonly type: ContactType;\n readonly address?: ContactAddress;\n readonly tags?: readonly string[];\n readonly notes?: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n","import type { Contact } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONTACTS: readonly Contact[] = [\n {\n id: \"contact-1\",\n firstName: \"Sarah\",\n lastName: \"Johnson\",\n email: \"sarah.johnson@example.com\",\n phone: \"+1 (555) 123-4567\",\n company: \"Wellness Works Inc.\",\n jobTitle: \"Purchasing Manager\",\n status: \"active\",\n type: \"individual\",\n tags: [\"VIP\", \"Repeat Buyer\"],\n createdAt: daysAgo(90),\n updatedAt: daysAgo(2),\n },\n {\n id: \"contact-2\",\n firstName: \"Mike\",\n lastName: \"Chen\",\n email: \"mike.chen@acmecorp.com\",\n phone: \"+1 (555) 234-5678\",\n company: \"Acme Corp\",\n jobTitle: \"Director of Operations\",\n status: \"active\",\n type: \"individual\",\n tags: [\"Enterprise\"],\n createdAt: daysAgo(60),\n updatedAt: daysAgo(5),\n },\n {\n id: \"contact-3\",\n firstName: \"Emily\",\n lastName: \"Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n company: \"HealthFirst\",\n status: \"lead\",\n type: \"individual\",\n notes: \"Interested in bulk pricing for team orders\",\n createdAt: daysAgo(7),\n updatedAt: daysAgo(1),\n },\n {\n id: \"contact-4\",\n firstName: \"David\",\n lastName: \"Park\",\n email: \"david.park@email.com\",\n phone: \"+1 (555) 345-6789\",\n status: \"prospect\",\n type: \"individual\",\n tags: [\"Referral\"],\n createdAt: daysAgo(14),\n updatedAt: daysAgo(10),\n },\n {\n id: \"contact-5\",\n firstName: \"Lisa\",\n lastName: \"Thompson\",\n email: \"lisa.t@globalfit.com\",\n company: \"Global Fitness\",\n jobTitle: \"CEO\",\n status: \"active\",\n type: \"individual\",\n address: {\n city: \"Los Angeles\",\n state: \"CA\",\n country: \"US\",\n },\n createdAt: daysAgo(120),\n updatedAt: daysAgo(15),\n },\n {\n id: \"contact-6\",\n firstName: \"James\",\n lastName: \"Wilson\",\n email: \"jwilson@retired.net\",\n status: \"inactive\",\n type: \"individual\",\n notes: \"Moved out of area, no longer purchasing\",\n createdAt: daysAgo(200),\n updatedAt: daysAgo(45),\n },\n];\n\nexport const DEMO_CONTACT: Contact = DEMO_CONTACTS[0]!;\n","/**\n * Contacts hooks - stub implementation for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Contact, ContactStatus } from \"../types/screen-types\";\nimport { CONTACT_STATUSES } from \"../types/screen-types\";\nimport type { ListQueryResult, QueryResultNullable } from \"./hook-types\";\nimport { DEMO_CONTACTS, DEMO_CONTACT } from \"./demo-data/contacts\";\n\n/**\n * Type predicate to check if a status string is a valid ContactStatus.\n * Enables runtime validation with type narrowing.\n */\nexport function isContactStatus(value: string): value is ContactStatus {\n return Object.values(CONTACT_STATUSES).includes(value as ContactStatus);\n}\n\n/**\n * Parameters for filtering and paginating contacts.\n * Uses readonly properties and proper ContactStatus type for status.\n */\nexport interface UseContactsParams {\n /** Search query to filter contacts by name, email, or company */\n readonly search?: string;\n /** Filter contacts by status - uses ContactStatus union type for type safety */\n readonly status?: ContactStatus;\n /** Maximum number of contacts to return */\n readonly limit?: number;\n}\n\n/**\n * Result type for the useContacts hook.\n * Uses ListQueryResult<Contact> with totalCount and default Error type.\n */\nexport type UseContactsResult = ListQueryResult<Contact>;\n\n/**\n * Result type for the useContact hook.\n * Uses QueryResultNullable since a specific contact may not exist.\n */\nexport type UseContactResult = QueryResultNullable<Contact>;\n\n/**\n * Hook to fetch a list of contacts with optional filtering and pagination.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param params - Optional parameters for filtering and pagination\n * @returns Object containing contacts data, loading state, error state, and total count\n *\n * @example\n * ```tsx\n * const { data: contacts, isLoading, totalCount } = useContacts({\n * search: 'john',\n * status: 'active',\n * limit: 20\n * });\n * ```\n */\nexport function useContacts(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _params?: UseContactsParams,\n): UseContactsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONTACTS],\n isLoading: false,\n isError: false,\n totalCount: DEMO_CONTACTS.length,\n };\n}\n\n/**\n * Hook to fetch a single contact by ID.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param contactId - The unique identifier of the contact to fetch\n * @returns Object containing contact data, loading state, and error state\n *\n * @example\n * ```tsx\n * const { data: contact, isLoading, isError } = useContact('contact-123');\n * ```\n */\nexport function useContact(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _contactId: string,\n): UseContactResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _contactId\n return {\n data: DEMO_CONTACT,\n isLoading: false,\n isError: false,\n };\n}\n","// Screen Components\nexport { ProfileScreen, profileScreenPropertySchema } from \"./ProfileScreen\";\nexport {\n MessagingScreen,\n messagingScreenPropertySchema,\n} from \"./MessagingScreen\";\nexport { ContactsScreen, contactsScreenPropertySchema } from \"./ContactsScreen\";\nexport { OrdersScreen, ordersScreenPropertySchema } from \"./OrdersScreen\";\nexport {\n SubscriptionsScreen,\n subscriptionsScreenPropertySchema,\n} from \"./SubscriptionsScreen\";\nexport {\n CustomersScreen,\n customersScreenPropertySchema,\n} from \"./CustomersScreen\";\nexport { ProductsScreen, productsScreenPropertySchema } from \"./ProductsScreen\";\nexport { MySiteScreen, mySiteScreenPropertySchema } from \"./MySiteScreen\";\nexport {\n ShareablesScreen,\n shareablesScreenPropertySchema,\n} from \"./ShareablesScreen\";\nexport { ShopScreen, shopScreenPropertySchema } from \"./ShopScreen\";\nexport { UpgradeScreen, upgradeScreenPropertySchema } from \"./UpgradeScreen\";\nexport {\n AppDownloadScreen,\n appDownloadScreenPropertySchema,\n} from \"./AppDownloadScreen\";\n\n// Re-export all property schemas as a lazy-loadable collection\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n\nexport const screenPropertySchemas: Record<\n string,\n () => Promise<WidgetPropertySchema>\n> = {\n ProfileScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProfileScreen\").then((m) => m.profileScreenPropertySchema),\n MessagingScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MessagingScreen\").then((m) => m.messagingScreenPropertySchema),\n ContactsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ContactsScreen\").then((m) => m.contactsScreenPropertySchema),\n OrdersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./OrdersScreen\").then((m) => m.ordersScreenPropertySchema),\n SubscriptionsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./SubscriptionsScreen\").then(\n (m) => m.subscriptionsScreenPropertySchema,\n ),\n CustomersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./CustomersScreen\").then((m) => m.customersScreenPropertySchema),\n ProductsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProductsScreen\").then((m) => m.productsScreenPropertySchema),\n MySiteScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MySiteScreen\").then((m) => m.mySiteScreenPropertySchema),\n ShareablesScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShareablesScreen\").then((m) => m.shareablesScreenPropertySchema),\n ShopScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShopScreen\").then((m) => m.shopScreenPropertySchema),\n UpgradeScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./UpgradeScreen\").then((m) => m.upgradeScreenPropertySchema),\n AppDownloadScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./AppDownloadScreen\").then(\n (m) => m.appDownloadScreenPropertySchema,\n ),\n};\n\n// Page Template Registration\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Core page template IDs\n */\nexport const CORE_PAGE_IDS = {\n PROFILE: \"core-profile\",\n MESSAGING: \"core-messaging\",\n CONTACTS: \"core-contacts\",\n ORDERS: \"core-orders\",\n SUBSCRIPTIONS: \"core-subscriptions\",\n CUSTOMERS: \"core-customers\",\n PRODUCTS: \"core-products\",\n MY_SITE: \"core-my-site\",\n SHAREABLES: \"core-shareables\",\n SHOP: \"core-shop\",\n UPGRADE: \"core-upgrade\",\n APP_DOWNLOAD: \"core-app-download\",\n} as const;\n\n/**\n * Register core page templates.\n * These are automatically registered when the SDK is imported.\n */\nfunction registerCorePageTemplates(): void {\n // Profile Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PROFILE,\n slug: \"profile\",\n name: \"Profile\",\n description: \"User profile management\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"profile\", \"account\", \"user\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProfileScreen\",\n id: \"profile-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Messaging Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MESSAGING,\n slug: \"messaging\",\n name: \"Messaging\",\n description: \"Messaging interface provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.COMMUNICATION,\n tags: [\"messaging\", \"chat\", \"conversations\", \"communication\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MessagingScreen\",\n id: \"messaging-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Contacts Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CONTACTS,\n slug: \"contacts\",\n name: \"Contacts\",\n description: \"Contact management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"contacts\", \"people\", \"address-book\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ContactsScreen\",\n id: \"contacts-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Orders Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.ORDERS,\n slug: \"orders\",\n name: \"Orders\",\n description: \"Order management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"orders\", \"commerce\", \"sales\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"OrdersScreen\",\n id: \"orders-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Subscriptions Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SUBSCRIPTIONS,\n slug: \"subscriptions\",\n name: \"Subscriptions\",\n description: \"Subscription management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"subscriptions\", \"recurring\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"SubscriptionsScreen\",\n id: \"subscriptions-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Customers Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CUSTOMERS,\n slug: \"customers\",\n name: \"Customers\",\n description: \"Customer management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"customers\", \"people\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"CustomersScreen\",\n id: \"customers-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shop Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHOP,\n slug: \"shop\",\n name: \"Shop\",\n description: \"Product shop with catalog browsing and product details\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shop\", \"store\", \"products\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShopScreen\",\n id: \"shop-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Products Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PRODUCTS,\n slug: \"products\",\n name: \"Products\",\n description: \"Product catalog provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"products\", \"catalog\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProductsScreen\",\n id: \"products-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // My Site Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MY_SITE,\n slug: \"my-site\",\n name: \"My Site\",\n description: \"Personal site management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"my-site\", \"website\", \"portal\", \"personal\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MySiteScreen\",\n id: \"my-site-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Upgrade Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.UPGRADE,\n slug: \"upgrade\",\n name: \"Upgrade\",\n description: \"Pro upgrade waitlist screen\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"upgrade\", \"pro\", \"waitlist\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"UpgradeScreen\",\n id: \"upgrade-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // App Download Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.APP_DOWNLOAD,\n slug: \"app-download\",\n name: \"App Download\",\n description: \"Mobile app download page with store links and QR code\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"app-download\", \"mobile\", \"qr-code\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"AppDownloadScreen\",\n id: \"app-download-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shareables Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHAREABLES,\n slug: \"share\",\n name: \"Shareables\",\n description:\n \"Marketing assets and shareable content provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shareables\", \"marketing\", \"assets\", \"media\", \"products\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShareablesScreen\",\n id: \"shareables-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n}\n\n// Auto-register core templates on module load\nregisterCorePageTemplates();\n","import type { ComponentType } from \"react\";\nimport type { WidgetManifest } from \"@fluid-app/portal-core/registries\";\nimport { DEFAULT_SDK_WIDGET_REGISTRY } from \"../core/default-widget-registry\";\n\n/**\n * Merges custom widget components from manifests into the default\n * SDK widget registry. Returns the default registry unchanged when\n * no custom widgets are provided (avoids unnecessary object creation).\n */\nexport function buildWidgetRegistry(\n manifests: WidgetManifest[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, ComponentType<any>> {\n if (manifests.length === 0) return DEFAULT_SDK_WIDGET_REGISTRY;\n\n const customEntries = Object.fromEntries(\n manifests.map((m) => [m.type, m.component]),\n );\n\n return { ...DEFAULT_SDK_WIDGET_REGISTRY, ...customEntries };\n}\n","import React, {\n forwardRef,\n type AnchorHTMLAttributes,\n type MouseEvent,\n} from \"react\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface AppLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /** Slug to navigate to (e.g. \"contacts/123\") */\n to: string;\n}\n\n/**\n * SPA-aware link that renders a real `<a href>` for accessibility\n * (right-click, ctrl+click, screen readers) but intercepts normal\n * clicks for client-side navigation.\n */\nexport const AppLink: React.ForwardRefExoticComponent<\n AppLinkProps & React.RefAttributes<HTMLAnchorElement>\n> = forwardRef<HTMLAnchorElement, AppLinkProps>(function AppLink(\n { to, onClick, children, ...rest },\n ref,\n) {\n const { navigate, buildHref } = useAppNavigation();\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {\n // Allow custom onClick handlers to run first\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n // Skip SPA navigation for modifier keys, non-primary button, or external target\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n if (e.button !== 0) return;\n if (rest.target && rest.target !== \"_self\") return;\n\n e.preventDefault();\n navigate(to);\n };\n\n return (\n <a ref={ref} href={buildHref(to)} onClick={handleClick} {...rest}>\n {children}\n </a>\n );\n});\n","/**\n * Scaffold Manifest\n *\n * Tracks which files belong to the portal scaffold and their category.\n * Used by `fluid doctor` to detect drift between a scaffolded portal\n * and the canonical templates shipped with the SDK.\n *\n * Categories:\n * - \"entry\" — App bootstrap files (main.tsx, index.css)\n * - \"config\" — Developer-owned config (portal.config.ts, navigation.config.ts)\n * - \"infrastructure\" — Build/tooling config (vite, tsconfig, index.html, linting)\n */\n\nexport type FileCategory = \"entry\" | \"config\" | \"infrastructure\";\n\nexport interface ManifestEntry {\n readonly category: FileCategory;\n}\n\n/**\n * Canonical scaffold file manifest.\n *\n * Files marked as \"entry\" or \"infrastructure\" are checked by `fluid doctor`.\n * Files marked as \"config\" are developer-owned and intentionally excluded from drift checks.\n */\nexport const SCAFFOLD_MANIFEST = {\n version: 2,\n files: {\n \"src/main.tsx\": { category: \"entry\" },\n \"src/index.css\": { category: \"entry\" },\n \"index.html\": { category: \"infrastructure\" },\n \"tsconfig.json\": { category: \"infrastructure\" },\n \"vite.config.ts\": { category: \"infrastructure\" },\n \".oxlintrc.json\": { category: \"infrastructure\" },\n },\n /** Developer-owned files — never checked for drift */\n developerOwned: [\n \"src/portal.config.ts\",\n \"src/navigation.config.ts\",\n \"src/screens/*\",\n ],\n} as const;\n\nexport type ScaffoldFile = keyof typeof SCAFFOLD_MANIFEST.files;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,kBAAkB;CAC7B,MAAM;CACN,UAAU;CACV,eAAe;CACf,MAAM;CACN,QAAQ;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCD,IAAM,2BAAN,MAA+B;CAC7B,4BAAoB,IAAI,KAA2B;CACnD,aAAqC,EAAE;CAEvC,cAAc;AAEZ,OAAK,aAAa;GAChB;IAAE,IAAI,gBAAgB;IAAM,OAAO;IAAiB,MAAM;IAAQ;GAClE;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IACE,IAAI,gBAAgB;IACpB,OAAO;IACP,MAAM;IACP;GACD;IAAE,IAAI,gBAAgB;IAAQ,OAAO;IAAU,MAAM;IAAU;GAChE;;;;;;CAOH,SAAS,UAA8B;AACrC,MAAI,KAAK,UAAU,IAAI,SAAS,GAAG,CACjC,OAAM,IAAI,MACR,0BAA0B,SAAS,GAAG,yBACvC;AAEH,OAAK,UAAU,IAAI,SAAS,IAAI,SAAS;;;;;;;CAQ3C,WAAW,IAAqB;EAC9B,MAAM,WAAW,KAAK,UAAU,IAAI,GAAG;AACvC,MAAI,CAAC,SACH,QAAO;AAET,MAAI,SAAS,QAAQ;AACnB,WAAQ,KACN,yCAAyC,GAAG,iCAC7C;AACD,UAAO;;AAET,SAAO,KAAK,UAAU,OAAO,GAAG;;;;;CAMlC,IAAI,IAAsC;AACxC,SAAO,KAAK,UAAU,IAAI,GAAG;;;;;CAM/B,cAAc,UAAmD;AAC/D,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QACxC,MAAM,EAAE,aAAa,SACvB;;;;;CAMH,UAA0B;AACxB,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC;;;;;CAM5C,WAA2B;AACzB,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,OAAO;;;;;CAMpE,eAA+B;AAC7B,SAAO,MAAM,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,CAAC,EAAE,OAAO;;;;;CAMrE,iBAAiC;AAC/B,SAAO,CAAC,GAAG,KAAK,WAAW;;;;;CAM7B,YAAY,UAA8B;AACxC,MAAI,KAAK,WAAW,MAAM,MAAM,EAAE,OAAO,SAAS,GAAG,EAAE;AACrD,WAAQ,KAAK,qBAAqB,SAAS,GAAG,kBAAkB;AAChE;;AAEF,OAAK,WAAW,KAAK,SAAS;;;;;CAMhC,IAAI,IAAqB;AACvB,SAAO,KAAK,UAAU,IAAI,GAAG;;;;;CAM/B,IAAI,OAAe;AACjB,SAAO,KAAK,UAAU;;;;;;CAOxB,eAAqB;AACnB,OAAK,MAAM,CAAC,IAAI,aAAa,KAAK,UAChC,KAAI,CAAC,SAAS,OACZ,MAAK,UAAU,OAAO,GAAG;;;;;;;;;AAYjC,MAAa,uBACX,IAAI,0BAA0B;;;;;;;;;;;;ACpKhC,SAAS,eACP,eACA,WACgB;AAChB,KAAI,CAAC,UAAU,OAEb,QAAO,CAAC,GAAG,cAAc;CAI3B,MAAM,cAAc,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAEzE,QAAO,cAAc,KAAK,WACxB,qBAAqB,QAAQ,YAAY,CAC1C;;;;;;AAOH,SAAS,qBACP,QACA,aACc;CACd,MAAM,WAAW,OAAO,KAAK,YAAY,IAAI,OAAO,GAAG,GAAG,KAAA;CAC1D,MAAM,WAAW,OAAO,MAAM;CAG9B,MAAM,cAAc,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS;AAEjE,KAAI,CAAC,YAAY,CAAC,YAChB,QAAO;CAGT,MAAM,WAAW,WACb;EAAE,GAAG,OAAO;EAAO,GAAG;EAAU,GAChC,EAAE,GAAG,OAAO,OAAO;AAEvB,KAAI,YACF,UAAS,WAAY,SAAqC,KAAK,UAC7D,qBAAqB,OAAO,YAAY,CACzC;AAGH,QAAO;EAAE,GAAG;EAAQ,OAAO;EAAU;;;;;;;;AASvC,SAAS,qBACP,KAC8B;CAC9B,MAAM,WAAW,qBAAqB,IAAI,IAAI,iBAAiB;AAC/D,KAAI,CAAC,UAAU;AACb,UAAQ,KACN,kBAAkB,IAAI,iBAAiB,yBACxC;AACD;;CAKF,MAAM,gBAAgB,IAAI,YACtB,eAAe,SAAS,gBAAgB,IAAI,UAAU,GACtD,CAAC,GAAG,SAAS,eAAe;AAEhC,QAAO;EACL,IAAI,IAAI;EACR,MAAM,SAAS;EACf,MAAM,SAAS;EACf,gBAAgB;EACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCH,SAAgB,uBACd,YACoB;CAEpB,MAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC;CACnE,MAAM,SAA6B,CAAC,GAAG,WAAW,QAAQ;AAG1D,KAAI,WAAW,UACb,MAAK,MAAM,OAAO,WAAW,WAAW;AACtC,MAAI,eAAe,IAAI,IAAI,UAAU,CAEnC;EAGF,MAAM,SAAS,qBAAqB,IAAI;AACxC,MAAI,OACF,QAAO,KAAK,OAAO;;AAKzB,QAAO;;;;;;;AAQT,SAAgB,4BAA4C;AAC1D,QAAO,qBAAqB,SAAS;;;;;;;AAQvC,SAAgB,uBAAuC;AACrD,QAAO,qBAAqB,UAAU;;;;;;;AAQxC,SAAgB,2BAA2C;AACzD,QAAO,qBAAqB,cAAc;;;;;;;;AAS5C,SAAgB,wBAAwB,YAGtC;CACA,MAAM,YAAY,qBAAqB,UAAU;CACjD,MAAM,wBAAwB,IAAI,IAChC,WAAW,WAAW,KAAK,QAAQ,IAAI,iBAAiB,IAAI,EAAE,CAC/D;CAGD,MAAM,mBAAmB,UACtB,QAAQ,SAAS,CAAC,sBAAsB,IAAI,KAAK,GAAG,CAAC,CACrD,KAAK,SAAS,KAAK,GAAG;AAEzB,QAAO;EACL,OAAO,iBAAiB,WAAW;EACnC;EACD;;;;;AChMH,MAAM,kBAA2C,EAAE;AAyBnD,MAAM,sBAAsB,cAC1B,KACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CD,SAAgB,qBAAqB,EACnC,UACA,YAAY,mBACmC;CAE/C,MAAM,gBAAgB,OAAiB,EAAE,CAAC;AAO1C,iBAAgB;EACd,MAAM,aAAuB,EAAE;AAE/B,OAAK,MAAM,YAAY,UACrB,KAAI,CAAC,qBAAqB,IAAI,SAAS,GAAG,CACxC,KAAI;AACF,wBAAqB,SAAS,SAAS;AACvC,cAAW,KAAK,SAAS,GAAG;WACrB,OAAO;AACd,WAAQ,KACN,qCAAqC,SAAS,GAAG,KACjD,MACD;;AAKP,gBAAc,UAAU;AAGxB,eAAa;AACX,QAAK,MAAM,MAAM,cAAc,QAC7B,sBAAqB,WAAW,GAAG;AAErC,iBAAc,UAAU,EAAE;;IAG3B,CA9BiB,UAAU,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CA8BxC,CAAC;CAIjB,MAAM,eAAe,eAEhB;EACC,cAAc;EACd,qBAAqB,qBAAqB,SAAS;EACnD,cAAc,OAAe,qBAAqB,IAAI,GAAG;EACzD,cAAc,OAAe,qBAAqB,IAAI,GAAG;EAC1D,GACH,EAAE,CACH;AAED,QACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;EAClC;EAC4B,CAAA;;;;;;;;;;;;;;;;;;;;;;;AAyBnC,SAAgB,mBAA6C;CAC3D,MAAM,UAAU,WAAW,oBAAoB;AAC/C,KAAI,CAAC,QACH,OAAM,IAAI,MACR,8DACD;AAEH,QAAO;;;;;;;;;AAUT,SAAgB,iBAAiB,YAA4C;CAC3E,MAAM,EAAE,iBAAiB,kBAAkB;AAC3C,QAAO,cAAc,aAAa,WAAW,EAAE,CAAC,cAAc,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpJ5E,SAAgB,UAAU,EACxB,UAAU,sDACV,QAAQ,2BACR,YACoC;AACpC,QACE,oBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,SAAS;GACT,YACE;GACF,iBAAiB;GACjB,OAAO;GACR;YAED,qBAAC,OAAD;GACE,OAAO;IACL,UAAU;IACV,WAAW;IACX,SAAS;IACT,iBAAiB;IACjB,cAAc;IACd,WACE;IACH;aATH;IAYE,oBAAC,OAAD;KACE,OAAO;MACL,OAAO;MACP,QAAQ;MACR,QAAQ;MACR,iBAAiB;MACjB,cAAc;MACd,SAAS;MACT,YAAY;MACZ,gBAAgB;MACjB;eAED,qBAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,QAAO;MACP,aAAY;MACZ,eAAc;MACd,gBAAe;gBARjB,CAUE,oBAAC,QAAD;OAAM,GAAE;OAAI,GAAE;OAAK,OAAM;OAAK,QAAO;OAAK,IAAG;OAAI,IAAG;OAAM,CAAA,EAC1D,oBAAC,QAAD,EAAM,GAAE,4BAA6B,CAAA,CACjC;;KACF,CAAA;IAEN,oBAAC,MAAD;KACE,OAAO;MACL,UAAU;MACV,YAAY;MACZ,cAAc;MACd,OAAO;MACR;eAEA;KACE,CAAA;IAEL,oBAAC,KAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO;MACP,cAAc,WAAW,WAAW;MACpC,YAAY;MACb;eAEA;KACC,CAAA;IAEH;IACG;;EACF,CAAA;;AAIV,MAAMA,kBAAgB;;;;AAKtB,SAASC,oBAAwB;AAC/B,KAAI,OAAO,aAAa,YAAa;AACrC,KAAI,SAAS,eAAeD,gBAAc,CAAE;CAE5C,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,KAAKA;AACX,OAAM,cAAc;AACpB,UAAS,KAAK,YAAY,MAAM;;;;;AAMlC,SAAgB,cAAiC;AAC/C,iBAAgB;AACd,qBAAiB;IAChB,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,YACE;GACF,iBAAiB;GAClB;YAVH,CAYE,oBAAC,OAAD,EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,gBAAgB;GAChB,cAAc;GACd,WAAW;GACZ,EACD,CAAA,EACF,oBAAC,KAAD;GACE,OAAO;IACL,WAAW;IACX,OAAO;IACP,UAAU;IACX;aACF;GAEG,CAAA,CACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtHV,SAAgB,YAAY,EAC1B,UACA,WAAW,oBAAC,aAAD,EAAe,CAAA,EAC1B,iBAAiB,oBAAC,WAAD,EAAa,CAAA,IACQ;CACtC,MAAM,EAAE,iBAAiB,WAAW,UAAU,cAAc;AAG5D,KAAI,UACF,QAAO,oBAAA,YAAA,EAAA,UAAG,UAAY,CAAA;AAIxB,KAAI,CAAC,mBAAmB,MACtB,QAAO,oBAAA,YAAA,EAAA,UAAG,gBAAkB,CAAA;AAI9B,QAAO,oBAAA,YAAA,EAAG,UAAY,CAAA;;;;AC/ExB,MAAM,oBAAoB;AAG1B,SAAgB,cAAuB;CACrC,MAAM,CAAC,UAAU,eAAe,SAAkB,MAAM;AAExD,iBAAgB;EACd,MAAM,MAAM,OAAO,WAAW,eAAe,oBAAoB,EAAE,KAAK;EACxE,MAAM,iBAAiB;AACrB,eAAY,OAAO,aAAa,kBAAkB;;AAEpD,MAAI,iBAAiB,UAAU,SAAS;AACxC,cAAY,OAAO,aAAa,kBAAkB;AAClD,eAAa,IAAI,oBAAoB,UAAU,SAAS;IACvD,EAAE,CAAC;AAEN,QAAO;;;;ACJT,SAAS,GAAG,GAAG,QAAsB;AACnC,QAAO,QAAQ,KAAK,OAAO,CAAC;;AAG9B,SAAS,UAAU,EACjB,WACA,cAAc,cACd,GAAG,SAIF;AACD,QACE,oBAAC,OAAD;EACE,MAAK;EACL,oBAAkB;EAClB,WAAW,GACT,sBACA,gBAAgB,eAAe,gBAAgB,eAC/C,UACD;EACD,GAAI;EACJ,CAAA;;AAIN,SAAS,SAAS,EAChB,WACA,GAAG,SACoC;AACvC,QACE,oBAAC,OAAD;EACE,WAAW,GAAG,qCAAqC,UAAU;EAC7D,GAAI;EACJ,CAAA;;AAQN,MAAM,gBAAgB;AACtB,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,4BAA4B;AAkBlC,MAAa,iBACXE,QAAM,cAA0C,KAAK;AAEvD,SAAS,aAAkC;CACzC,MAAM,UAAUA,QAAM,WAAW,eAAe;AAChD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,oDAAoD;AAGtE,QAAO;;AAOT,MAAM,kBASFA,QAAM,YAYN,EACE,cAAc,MACd,MAAM,UACN,cAAc,aACd,eACA,aACA,cAAc,mBAAmB,OACjC,WACA,OACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,iBAAiB,aAAa;CAEpC,MAAM,WACJ,kBAAkB,KAAA,IAAY,gBAAgB,MAAM;CAEtD,MAAM,gBAAgB,kBAAkB,KAAA,KAAa,CAAC,CAAC;CACvD,MAAM,CAAC,YAAY,iBAAiBA,QAAM,SAAS,MAAM;CAIzD,MAAM,CAAC,OAAO,YAAYA,QAAM,SAAS,YAAY;CACrD,MAAM,OAAO,YAAY;CACzB,MAAM,UAAUA,QAAM,aACnB,UAAmD;EAClD,MAAM,YAAY,OAAO,UAAU,aAAa,MAAM,KAAK,GAAG;AAC9D,MAAI,YACF,aAAY,UAAU;MAEtB,UAAS,UAAU;IAGvB,CAAC,aAAa,KAAK,CACpB;CAGD,MAAM,gBAAgBA,QAAM,kBAAkB;AAC5C,SAAO,WACH,eAAe,SAAS,CAAC,KAAK,GAC9B,SAAS,SAAS,CAAC,KAAK;IAC3B;EAAC;EAAU;EAAS;EAAc,CAAC;AAGtC,SAAM,gBAAgB;EACpB,MAAM,iBAAiB,UAAyB;AAC9C,OACE,MAAM,QAAQ,8BACb,MAAM,WAAW,MAAM,UACxB;IAEA,MAAM,gBAAgB,SAAS;AAM/B,QAJE,eAAe,QAAQ,oBAAoB,IAC3C,eAAe,QAAQ,iBAAiB,IACxC,eAAe,UAAU,SAAS,cAAc,CAGhD;AAGF,UAAM,gBAAgB;AACtB,mBAAe;;;AAInB,SAAO,iBAAiB,WAAW,cAAc;AACjD,eAAa,OAAO,oBAAoB,WAAW,cAAc;IAChE,CAAC,cAAc,CAAC;CAInB,MAAM,QAAQ,OAAO,aAAa;CAElC,MAAM,eAAeA,QAAM,eAClB;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACf,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,oBAAC,eAAe,UAAhB;EAAyB,OAAO;YAC9B,oBAAC,OAAD;GACE,OACE;IACE,mBAAmB;IACnB,wBAAwB;IACxB,GAAG;IACJ;GAEH,WAAW,GACT,oDACA,UACD;GACI;GACL,GAAI;GAEH;GACG,CAAA;EACkB,CAAA;EAG/B;AACD,gBAAgB,cAAc;AAM9B,MAAM,UAMFA,QAAM,YASN,EACE,OAAO,QACP,UAAU,WACV,cAAc,aACd,WACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,EACJ,UACA,OACA,YACA,eACA,eACA,iBACE,YAAY;CAGhB,MAAM,eACJ,UAAU,aAAa,gBAAgB;AAGzC,KAAI,gBAAgB,SAClB,QAAO;AAGT,KAAI,gBAAgB,OAClB,QACE,oBAAC,OAAD;EACE,WAAW,GACT,sFACA,gBAAgB,WAAW,YAC3B,UACD;EACI;EACL,GAAI;EAEH;EACG,CAAA;AAIV,KAAI,UAAU;EAGZ,MAAM,gBAAgB,gBAAgB,aAAa;AACnD,SACE,qBAAA,YAAA,EAAA,UAAA,CAEG,cACC,oBAAC,OAAD;GACE,WAAW,GAAG,eAAe,2BAA2B;GACxD,eAAe,cAAc,MAAM;GACnC,eAAY;GACZ,CAAA,EAIJ,oBAAC,OAAD;GACE,gBAAa;GACb,eAAY;GACZ,WAAW,GACT,eACA,qIACA,aAAa,kBAAkB,qBAC/B,UACD;GACD,OACE,EACE,mBAAmB,sBACpB;GAEE;GACL,GAAI;aAEJ,oBAAC,OAAD;IAAK,WAAU;IAA+B;IAAe,CAAA;GACzD,CAAA,CACL,EAAA,CAAA;;AAIP,QACE,qBAAC,OAAD;EACO;EACL,WAAU;EACV,cAAY;EACZ,oBAAkB,UAAU,cAAc,cAAc;EACxD,gBAAc;EACd,aAAW;EACX,OACE,EACE,mBAAmB,cACpB;YAVL,CAcE,oBAAC,OAAD,EACE,WAAW,GACT,uEACA,0CACA,sCACA,YAAY,cAAc,YAAY,UAClC,qFACA,yDACL,EACD,CAAA,EACF,oBAAC,OAAD;GACE,WAAW,GACT,uHACA,gBAAgB,WAAW,SAC3B,SAAS,SACL,mFACA,oFAEJ,YAAY,cAAc,YAAY,UAClC,6FACA,2FACJ,UACD;GACD,GAAI;aAEJ,oBAAC,OAAD;IACE,gBAAa;IACb,WAAU;IAET;IACG,CAAA;GACF,CAAA,CACF;;EAGX;AACD,QAAQ,cAAc;AAMtB,MAAM,cAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;CAChC,MAAM,EAAE,kBAAkB,YAAY;AAEtC,QACE,oBAAC,UAAD;EACO;EACL,gBAAa;EACb,cAAW;EACX,UAAU;EACV,SAAS;EACT,OAAM;EACN,WAAW,GACT,+PACA,4EACA,0HACA,2JACA,6DACA,6DACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,YAAY,cAAc;AAM1B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;CAChC,MAAM,EAAE,kBAAkB,YAAY;AACtC,QACE,oBAAC,QAAD;EACO;EACL,WAAW,GACT,iCACA,gBACI,wCACA,2EACJ,mNACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAM3B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,SAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,uJACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAM3B,MAAM,gBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,2BAA2B,UAAU;EACnD,GAAI;EACJ,CAAA;EAGP;AACD,cAAc,cAAc;AAE5B,MAAM,gBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,2BAA2B,UAAU;EACnD,GAAI;EACJ,CAAA;EAGP;AACD,cAAc,cAAc;AAM5B,MAAM,mBAIFA,QAAM,YAKP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAClC,QACE,oBAAC,WAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,iCAAiC,UAAU;EACzD,GAAI;EACJ,CAAA;EAEJ;AACF,iBAAiB,cAAc;AAM/B,MAAM,iBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,gMACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,eAAe,cAAc;AAM7B,MAAM,eAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QAAQ;AAChC,QACE,oBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GACT,mHACA,UACD;EACD,GAAI;EACJ,CAAA;EAGP;AACD,aAAa,cAAc;AAE3B,MAAM,oBAIFA,QAAM,YAGP,EAAE,WAAW,UAAU,OAAO,GAAG,SAAS,QAAQ;AAGnD,QACE,oBAHW,UAAU,OAAO,OAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,4OACA,4EACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,kBAAkB,cAAc;AAEhC,MAAM,qBAIFA,QAAM,YAGP,EAAE,WAAW,UAAU,OAAO,GAAG,SAAS,QAAQ;AAGnD,QACE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,8RAEA,iDACA,wCACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,mBAAmB,cAAc;AAEjC,MAAM,sBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,OAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,kBAAkB,UAAU;CAC1C,GAAI;CACJ,CAAA,CAEL;AACD,oBAAoB,cAAc;AAMlC,MAAM,cAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,sCAAsC,UAAU;CAC9D,GAAI;CACJ,CAAA,CAEL;AACD,YAAY,cAAc;AAE1B,MAAM,kBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GAAG,4BAA4B,UAAU;CACpD,GAAI;CACJ,CAAA,CAEL;AACD,gBAAgB,cAAc;AAM9B,MAAM,4BAoBQ,IACZ,o1BACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;GACH;EACD,MAAM;GACJ,SAAS;GACT,IAAI;GACJ,IAAI;GACL;EACF;CACD,iBAAiB;EACf,SAAS;EACT,MAAM;EACP;CACF,CACF;AAED,MAAM,oBAMFA,QAAM,YAQN,EACE,UAAU,OACV,WAAW,OACX,UAAU,WACV,OAAO,WACP,WACA,GAAG,SAEL,QACG;AAcH,QAVE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,aAAW;EACX,eAAa;EACb,WAAW,GAAG,0BAA0B;GAAE;GAAS;GAAM,CAAC,EAAE,UAAU;EACtE,GAAI;EACJ,CAAA;EAKP;AACD,kBAAkB,cAAc;AAMhC,MAAM,oBAKFA,QAAM,YAMP,EAAE,WAAW,UAAU,OAAO,cAAc,OAAO,GAAG,SAAS,QAAQ;AAGxE,QACE,oBAHW,UAAU,OAAO,UAG5B;EACO;EACL,gBAAa;EACb,WAAW,GACT,oVAEA,iDACA,yCACA,gDACA,2CACA,wCACA,eACE,4LACF,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,kBAAkB,cAAc;AAEhC,MAAM,mBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,OAAD;CACO;CACL,gBAAa;CACb,WAAW,GACT,0KACA,4HACA,yCACA,gDACA,2CACA,wCACA,UACD;CACD,GAAI;CACJ,CAAA,CAEL;AACD,iBAAiB,cAAc;AAE/B,MAAM,sBAIFA,QAAM,YAKP,EAAE,WAAW,WAAW,OAAO,GAAG,SAAS,QAAQ;CAEpD,MAAM,QAAQA,QAAM,cAAc;AAChC,SAAO,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,GAAG,GAAG,GAAG;IAC7C,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACO;EACL,gBAAa;EACb,WAAW,GAAG,+CAA+C,UAAU;EACvE,GAAI;YAJN,CAMG,YACC,oBAAC,UAAD;GACE,WAAU;GACV,gBAAa;GACb,CAAA,EAEJ,oBAAC,UAAD;GACE,WAAU;GACV,gBAAa;GACb,OACE,EACE,oBAAoB,OACrB;GAEH,CAAA,CACE;;EAER;AACF,oBAAoB,cAAc;AAMlC,MAAM,iBAEFA,QAAM,YACP,EAAE,WAAW,GAAG,SAAS,QACxB,oBAAC,MAAD;CACO;CACL,gBAAa;CACb,WAAW,GACT,kGACA,wCACA,UACD;CACD,GAAI;CACJ,CAAA,CAEL;AACD,eAAe,cAAc;AAE7B,MAAM,qBAEFA,QAAM,YACP,EAAE,GAAG,SAAS,QAAQ,oBAAC,MAAD;CAAS;CAAK,GAAI;CAAS,CAAA,CACnD;AACD,mBAAmB,cAAc;AAEjC,MAAM,uBAMFA,QAAM,YAOP,EAAE,UAAU,OAAO,OAAO,MAAM,UAAU,WAAW,GAAG,SAAS,QAAQ;AAG1E,QACE,oBAHW,UAAU,OAAO,KAG5B;EACO;EACL,gBAAa;EACb,aAAW;EACX,eAAa;EACb,WAAW,GACT,ifACA,0FACA,SAAS,QAAQ,WACjB,SAAS,QAAQ,WACjB,wCACA,UACD;EACD,GAAI;EACJ,CAAA;EAEJ;AACF,qBAAqB,cAAc;;;;;;;;;;;ACv4BnC,SAAgB,eAAe,EAC7B,gBACA,eACA,UACA,eACA,eACA,cACA,eAAe,SAC0B;AACzC,QACE,oBAAC,iBAAD;EAA+B;YAC7B,qBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,qBAAC,SAAD,EAAA,UAAA;KACG,iBAAiB,oBAAC,eAAD,EAAA,UAAgB,eAA8B,CAAA;KAChE,oBAAC,gBAAD;MAAgB,WAAU;gBAAO;MAAgC,CAAA;KAChE,iBAAiB,oBAAC,eAAD,EAAA,UAAgB,eAA8B,CAAA;KACxD,EAAA,CAAA;IAGV,qBAAC,cAAD;KAAc,WAAU;eAAxB,CAEG,eAGD,oBAAC,OAAD;MACE,WAAW,mDAAmD,eAAe,uDAAuD;gBAEpI,oBAAC,OAAD;OAAK,WAAU;OACZ;OACG,CAAA;MACF,CAAA,CACO;;IAGd;IACG;;EACU,CAAA;;;;ACjDtB,MAAM,mBAAmB,cAA4C,KAAK;AAE1E,MAAM,iBACJ,OAAO,WAAW,cACd,OAAO,WAAW,+BAA+B,GACjD;AAEN,SAAS,8BAA8B,UAAsB;AAC3D,iBAAgB,iBAAiB,UAAU,SAAS;AACpD,cAAa,gBAAgB,oBAAoB,UAAU,SAAS;;AAGtE,SAAS,uBAAgC;AACvC,QAAO,gBAAgB,WAAW;;AAGpC,SAAS,oBAA6B;AACpC,QAAO;;AAWT,SAAgB,kBAAkB,EAChC,UACA,MACA,cACA,kBAAkB,QAC0B;CAO5C,MAAM,aANoB,qBACxB,+BACA,sBACA,kBACD,GAEmD,SAAS;CAE7D,MAAM,cAA2B,SAAS,SAAS,aAAa;CAEhE,MAAM,YAAY,kBAAkB;AAClC,MAAI,iBAAiB;GAEnB,MAAM,SAAsB,gBAAgB,UAAU,SAAS;AAC/D,gBAAa,WAAW,aAAa,SAAS,OAAO;QAGrD,cAAa,SAAS,UAAU,SAAS,QAAQ;IAElD;EAAC;EAAM;EAAa;EAAY;EAAc;EAAgB,CAAC;CAElE,MAAM,gBAAgB,sBAAsB,KAAK;CAEjD,MAAM,QAAQ,eACL;EACL;EACA;EACA,SAAS;EACT;EACA;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,oBAAC,iBAAiB,UAAlB;EAAkC;EAC/B;EACyB,CAAA;;;AAKhC,SAAgB,eAAsC;CACpD,MAAM,MAAM,WAAW,iBAAiB;AACxC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAO;;;AAIT,SAAgB,sBAAsB,MAAqC;AACzE,QAAO,SAAS,SAAS,KAAA,IAAY;;;;AC/GvC,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACD;;;;;AAUD,MAAa,iBACX,IAAI,IAA0B,CAAC,YAAY,UAAU,CAAC;;;;;AAMxD,SAAgB,cAAc,MAA0C;AACtE,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,aAAa,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,GAAG;AAC1D,KAAI,eAAe,IAAI,WAAW,CAAE,QAAO;AAC3C,MAAK,MAAM,WAAW,eACpB,KAAI,WAAW,WAAW,UAAU,IAAI,CAAE,QAAO;AAEnD,QAAO;;AAGT,MAAa,0BAGT;CACF,gBAAgB;EACd,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,UAAU;EACR,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,WAAW;EACT,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,QAAQ;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CAOD,eAAe;EACb,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,mBAAmB;EACjB,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,kBAAkB;EAChB,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,MAAM;EACJ,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,eAAe;EACb,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACD,SAAS;EACP,MAAM;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACV;CACF;;;;;AAcD,SAAgB,uBACd,MAC8B;AAC9B,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,wBAAwB,SAAS,KAA6B;;;;;AAevE,SAAgB,+BAGd;CACA,MAAM,WAAmD,EAAE;CAC3D,MAAM,4BAAY,IAAI,KAAa;AACnC,MAAK,MAAM,QAAQ,OAAO,OAAO,wBAAwB,EAAE;AACzD,MAAI,KAAK,QAAQ,UAAU,IAAI,KAAK,KAAK,CAAE;AAC3C,MAAI,KAAK,KAAM,WAAU,IAAI,KAAK,KAAK;EACvC,MAAM,UAAU,KAAK,WAAW;AAChC,MAAI,CAAC,SAAS,SACZ,UAAS,WAAW,EAAE;AAExB,WAAS,SAAS,KAAK,KAAK;;AAE9B,QAAO;;;;;AAMT,SAAgB,cAAc,MAAkC;AAC9D,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,EAAE,GAAG;;;;;;;;ACxLhD,SAAS,cAAc,MAA4C;CACjE,MAAM,OAAO,wBAAwB;AACrC,QAAO;EACL,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,UAAU,EAAE;EACb;;AAGH,SAAS,cAAc,OAA+B;AACpD,QAAO;EAAE;EAAO,UAAU,EAAE;EAAE;;;;;;;AAQhC,SAAgB,uBAAyC;AACvD,QAAO;EACL,cAAc,cAAc;EAC5B,cAAc,OAAO;EACrB,cAAc,WAAW;EACzB,cAAc,WAAW;EACzB,cAAc,UAAU;EACxB,cAAc,mBAAmB;EACjC,cAAc,iBAAiB;EAChC;;;;AC3BH,SAAgB,aAAa,EAC3B,SAC8C;CAC9C,MAAM,EAAE,eAAe,aAAa,YAAY;CAChD,MAAM,EAAE,SAAS,gBAAgB,wBAAwB;AAEzD,KAAI,CAAC,SAAS,CAAC,YAAa,QAAO;AAEnC,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACG,CAAC,YACA,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;IACV,cAAW;cAEX,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;IACzB,CAAA,EACT,oBAAC,OAAD,EAAK,WAAU,2BAA4B,CAAA,CAC1C,EAAA,CAAA;GAEJ,cACC,oBAAC,OAAD;IAAK,WAAU;cAAkB;IAAkB,CAAA,GAEnD,oBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA;GAEnE,WACC,oBAAC,OAAD;IAAK,WAAU;cAAmC;IAAc,CAAA;GAE9D;;;;;ACLV,MAAMC,aAAuC;CAC3C,gBAAgB;CAChB,iBAAiB;CACjB,MAAM;CACN,KAAK;CACL,iBAAiB;CACjB,WAAW;CACX,UAAU;CACV,iBAAiB;CACjB,eAAe;CACf,SAAS;CACT,SAAS;CACT,UAAU;CACV,MAAM;CACN,cAAc;CACd,cAAc;CACd,QAAQ;CACR,eAAe;CACf,MAAM;CACN,OAAO;CACP,kBAAkB;CAClB,OAAO;CACP,OAAO;CACP,OAAO;CACP,cAAc;CACd,oBAAoB;CACpB,SAAS;CACT,MAAM;CACN,OAAO;CACP,qBAAqB;CACrB,MAAM;CACN,aAAa;CACb,OAAO;CACR;AAOD,SAAgB,QAAQ,EAAE,MAAM,aAA8C;AAE5E,QAAO,oBADMA,WAAS,SAAS,MACxB,EAAiB,WAAa,CAAA;;;;;;;AC3EvC,MAAM,iBAAiB;;;;AAKvB,SAAS,cAAc,iBAAyB,cAA8B;AAC5E,QAAO,GAAG,eAAe,GAAG,gBAAgB,GAAG;;;;;AAMjD,SAAgB,kBACd,iBACA,cACe;AACf,KAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,KAAI;EACF,MAAM,MAAM,cAAc,iBAAiB,aAAa;AACxD,SAAO,aAAa,QAAQ,IAAI;UACzB,OAAO;AACd,UAAQ,KAAK,qCAAqC,MAAM;AACxD,SAAO;;;;;;;;ACpBX,SAAgB,YACd,cACA,cACS;CACT,MAAM,yBAAyB,cAAc,aAAa;AAE1D,MACG,CAAC,aAAa,YAAY,aAAa,SAAS,UAAU,MAC3D,aAAa;MAEc,cAAc,aAAa,KAAK,KAChC,uBACzB,QAAO;OAGT,QAAO,aAAa,SAAS,MAAM,UAA0B;AAE3D,SAD4B,cAAc,MAAM,KAAK,KACtB;GAC/B;AAGJ,QAAO;;;;;AAMT,SAAgB,oBACd,MACA,iBACe;AAEf,KAAI,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AAE7C,MAAI,KAAK,MAAM;GACb,MAAM,kBAAkB,kBAAkB,iBAAiB,KAAK,KAAK;AAGrE,OAAI,iBAAiB;IACnB,MAAM,wBAAwB,cAAc,gBAAgB;AAK5D,QAJoB,KAAK,SAAS,MAAM,UAA0B;AAEhE,YAD4B,cAAc,MAAM,KAAK,KACtB;MAC/B,CAEA,QAAO;;;EAMb,MAAM,aAAa,KAAK,SAAS;AACjC,MAAI,YAAY,KACd,QAAO,cAAc,WAAW,KAAK;AAGvC,SAAO;;AAIT,KAAI,KAAK,aAAa,KAAK,KACzB,QAAO,cAAc,KAAK,KAAK;AAIjC,KAAI,KAAK,KACP,QAAO,cAAc,KAAK,KAAK;AAGjC,QAAO;;;;AC1DT,SAAgB,aAAa,EAC3B,UACA,aACA,YACA,kBAAkB,GAClB,kBAAkB,KAC4B;CAC9C,MAAM,EAAE,UAAU,iBAAiB,YAAY;CAC/C,MAAM,CAAC,UAAU,eAAeC,QAAM,SAAS,MAAM;AAErD,KAAI,CAAC,YAAY,CAAC,aAAc,QAAO;CAEvC,MAAM,iBAAiB,SACpB,QAAQ,SAAS,CAAC,KAAK,UAAU,CACjC,QAAQ,SAAS,KAAK,QAAS,KAAK,YAAY,KAAK,SAAS,SAAS,EAAG;CAE7E,MAAM,cAAc,eAAe,SAAS;CAC5C,MAAM,eAAe,cACjB,eAAe,MAAM,GAAG,kBAAkB,EAAE,GAC5C;CACJ,MAAM,gBAAgB,cAClB,eAAe,MAAM,kBAAkB,EAAE,GACzC,EAAE;CAEN,MAAM,mBAAmB,SAAyB;EAChD,MAAM,SAAS,oBAAoB,MAAM,gBAAgB;AACzD,MAAI,OAAQ,YAAW,OAAO;AAC9B,cAAY,MAAM;;AAGpB,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACG,aAAa,KAAK,SAAS;AAE1B,UACE,qBAAC,UAAD;IAEE,MAAK;IACL,eAAe,gBAAgB,KAAK;IACpC,WAAW,2GANE,YAAY,MAAM,YAAY,GAO9B,iBAAiB;cALhC,CAQG,KAAK,OACJ,oBAAC,SAAD;KAAS,MAAM,KAAK;KAAM,WAAU;KAAW,CAAA,GAE/C,oBAAC,QAAD,EAAM,WAAU,UAAW,CAAA,EAE7B,oBAAC,QAAD;KAAM,WAAU;eAAyB,KAAK;KAAa,CAAA,CACpD;MAbF,KAAK,MAAM,KAAK,QAAQ,KAAK,MAa3B;IAEX,EAED,eACC,qBAAC,UAAD;GACE,MAAK;GACL,eAAe,YAAY,KAAK;GAChC,WAAW,2GACT,cAAc,MAAM,SAAS,YAAY,MAAM,YAAY,CAAC,GACxD,iBACA;aANR,CASE,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA,EAC/B,oBAAC,QAAD,EAAA,UAAM,QAAW,CAAA,CACV;KAEP;KAEL,YACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,OAAD;GACE,WAAU;GACV,eAAe,YAAY,MAAM;GACjC,eAAY;GACZ,CAAA,EACF,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KAAM,WAAU;eAAwC;KAEjD,CAAA,EACP,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,YAAY,MAAM;KACjC,WAAU;KACV,cAAW;eAEX,oBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;KACjB,CAAA,CACL;OACN,oBAAC,OAAD;IAAK,WAAU;cACZ,cAAc,KAAK,SAAS;AAE3B,YACE,qBAAC,UAAD;MAEE,MAAK;MACL,eAAe,gBAAgB,KAAK;MACpC,WAAW,+FANE,YAAY,MAAM,YAAY,GAQrC,uDACA;gBAPR,CAUG,KAAK,QACJ,oBAAC,SAAD;OAAS,MAAM,KAAK;OAAM,WAAU;OAAW,CAAA,EAEjD,oBAAC,QAAD,EAAA,UAAO,KAAK,OAAa,CAAA,CAClB;QAbF,KAAK,MAAM,KAAK,QAAQ,KAAK,MAa3B;MAEX;IACE,CAAA,CACF;KACF;IAEP,EAAA,CAAA;;;;;;;;;ACzHP,MAAa,qBAAqB,CAAC,SAAS,MAAM;;;;;;;AAQlD,MAAM,mBACJ,OAAO,WAAW,eAAe,CAAC,OAAO,KAAK,KAAK,MAC/C,iBAAiB,GACjB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCN,SAAgB,YAAY,SAEG;CAC7B,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAKL;EACA,UAAU,SAAS,mBAAmB;EACtC,eAAe,IAAI,IAAI,QAAQ;EAC/B,QAAQ;EACR,GAAI,oBAAoB,EAAE,WAAW,iBAAiB,aAAa;EACnE,GAAI,SAAS,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,SAAS;EACnE,CAAC;;;;ACvEJ,MAAM,gBAAgB;;;;AAKtB,SAAS,kBAAwB;AAC/B,KAAI,OAAO,aAAa,YAAa;AACrC,KAAI,SAAS,eAAe,cAAc,CAAE;CAE5C,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,KAAK;AACX,OAAM,cAAc;AACpB,UAAS,KAAK,YAAY,MAAM;;;;;;;AAQlC,SAAgB,kBAAqC;AACnD,iBAAgB;AACd,mBAAiB;IAChB,EAAE,CAAC;AAEN,QACE,qBAAC,OAAD;EACE,OAAO;GACL,SAAS;GACT,eAAe;GACf,YAAY;GACZ,gBAAgB;GAChB,WAAW;GACX,YACE;GACF,iBAAiB;GAClB;YAVH,CAYE,oBAAC,OAAD,EACE,OAAO;GACL,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,gBAAgB;GAChB,cAAc;GACd,WAAW;GACZ,EACD,CAAA,EACF,oBAAC,KAAD;GACE,OAAO;IACL,WAAW;IACX,OAAO;IACP,UAAU;IACX;aACF;GAEG,CAAA,CACA;;;;;ACpCV,SAAgB,mBAAmB,EACjC,MACA,UACA,aACA,cAC6C;CAC7C,MAAM,CAAC,MAAM,WAAW,SAAS,SAAS;AAE1C,iBAAgB;AACd,MAAI,SAAU,SAAQ,KAAK;IAC1B,CAAC,SAAS,CAAC;AAEd,QACE,oBAAC,aAAD;EACQ;EACN,cAAc;EACd,SAAA;EACA,WAAU;YAEV,qBAAC,iBAAD,EAAA,UAAA,CACE,oBAAC,oBAAD;GAAoB,SAAA;aAClB,qBAAC,mBAAD,EAAA,UAAA;IACG,KAAK,QAAQ,oBAAC,SAAD,EAAS,MAAM,KAAK,MAAQ,CAAA;IAC1C,oBAAC,QAAD;KAAM,WAAU;eAAY,KAAK;KAAa,CAAA;IAC9C,oBAAC,cAAD,EAAc,WAAU,kGAAmG,CAAA;IACzG,EAAA,CAAA;GACD,CAAA,EACrB,oBAAC,oBAAD,EAAA,UACE,oBAAC,gBAAD,EAAA,UACG,KAAK,SAAS,KAAK,UAAU;GAC5B,MAAM,YAAY,cAAc,MAAM,KAAK;AAG3C,UACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;IACE,UALgB,cAAc,YAAY,KAAK;IAM/C,eAAe,WAAW,UAAU;cAFtC,CAIE,oBAAC,SAAD;KAAS,MAAM,MAAM,QAAQ;KAAQ,WAAU;KAAY,CAAA,EAC3D,oBAAC,QAAD,EAAA,UAAO,MAAM,OAAa,CAAA,CACR;OACJ,EARI,MAAM,QAAQ,MAAM,MAQxB;IAEpB,EACa,CAAA,EACE,CAAA,CACL,EAAA,CAAA;EACN,CAAA;;;;ACrDlB,SAAgB,cAAc,EAC5B,UACA,aACA,cACwC;AACxC,QACE,oBAAC,aAAD,EAAA,UACG,SAAS,KAAK,MAAM,UAAU;AAE7B,MAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAO,QAAO;EAEtC,MAAM,WAAW,YAAY,MAAM,YAAY;AAI/C,MAHoB,KAAK,UAAU,SAAS,EAI1C,QACE,oBAAC,oBAAD;GAEQ;GACI;GACG;GACD;GACZ,EALK,KAAK,QAAQ,KAAK,MAKvB;AAKN,MAAI,CAAC,KAAK,KACR,QACE,oBAAC,mBAAD,EAAA,UACG,KAAK,OACY,EAFI,KAAK,MAAM,WAAW,QAE1B;EAKxB,MAAM,WAAW,cAAc,KAAK,KAAK;AAEzC,SACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;GACY;GACV,eAAe,WAAW,SAAS;aAFrC,CAIG,KAAK,QAAQ,oBAAC,SAAD,EAAS,MAAM,KAAK,MAAQ,CAAA,EAC1C,oBAAC,QAAD;IAAM,WAAU;cAAY,KAAK;IAAa,CAAA,CAC5B;MACJ,EARI,KAAK,MAAM,SAQf;GAEpB,EACU,CAAA;;;;;;;;;AC1DlB,SAAgB,sBACd,OACkB;CAClB,MAAM,SAA2B,EAAE;AAEnC,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,CAAC,KAAK,UAAU,QAAQ;AAC1B,OAAI,cAAc,KAAK,KAAK,CAAE;AAC9B,UAAO,KAAK,KAAK;AACjB;;AAIF,MAAI,cAAc,KAAK,KAAK,CAAE;EAG9B,MAAM,mBAAmB,sBAAsB,KAAK,SAAS;AAG7D,MAAI,iBAAiB,WAAW,EAAG;AAEnC,SAAO,KAAK;GAAE,GAAG;GAAM,UAAU;GAAkB,CAAC;;AAGtD,QAAO;;;;;;AAOT,SAAgB,sBACd,UACwC;CACxC,MAAM,WAAmD,EAAE;AAC3D,MAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,SAAS,EAAE;EACvD,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,cAAc,KAAK,KAAK,CAAC;AACjE,MAAI,QAAQ,SAAS,EAAG,UAAS,WAAW;;AAE9C,QAAO;;;;ACxCT,SAAgB,mBAAmB,EACjC,cAC6C;CAC7C,MAAM,CAAC,MAAM,WAAW,SAAS,MAAM;CACvC,MAAM,WAAW,OAAuB,KAAK;CAC7C,MAAM,YAAY,OAA0B,KAAK;CAEjD,MAAM,QAAQ,kBAAkB,QAAQ,MAAM,EAAE,EAAE,CAAC;AAGnD,iBAAgB;AACd,MAAI,CAAC,KAAM;EAEX,MAAM,mBAAmB,MAAkB;AACzC,OACE,SAAS,WACT,CAAC,SAAS,QAAQ,SAAS,EAAE,OAAe,IAC5C,UAAU,WACV,CAAC,UAAU,QAAQ,SAAS,EAAE,OAAe,CAE7C,QAAO;;EAIX,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,EAAE,QAAQ,SAAU,QAAO;;AAGjC,WAAS,iBAAiB,aAAa,gBAAgB;AACvD,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa;AACX,YAAS,oBAAoB,aAAa,gBAAgB;AAC1D,YAAS,oBAAoB,WAAW,cAAc;;IAEvD,CAAC,MAAM,MAAM,CAAC;CAEjB,MAAM,EAAE,eAAe,aAAa;CAEpC,MAAM,WAAW,cAAc;EAC7B,MAAM,MAAM,8BAA8B;AAC1C,SAAO,aAAa,sBAAsB,IAAI,GAAG;IAChD,CAAC,WAAW,CAAC;CAEhB,MAAM,mBAAmB,SAAiB;AACxC,aAAW,KAAK;AAChB,SAAO;;AAGT,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,eAAe,SAAS,SAAS,CAAC,KAAK;GACvC,WAAU;GACV,cAAW;GACX,iBAAe;aAEf,oBAAC,YAAD,EAAY,WAAU,WAAY,CAAA;GAC3B,CAAA,EAER,QACC,oBAAC,OAAD;GACE,KAAK;GACL,WAAU;aAEV,qBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,YAAD,EAAY,WAAU,iCAAkC,CAAA,EACxD,oBAAC,MAAD;MAAI,WAAU;gBAAwC;MAEjD,CAAA,CACD;QAGN,oBAAC,OAAD;KAAK,WAAU;eACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,aAAa,WAC3C,qBAAC,OAAD;MAAuB,WAAU;gBAAjC,CACE,oBAAC,MAAD;OAAI,WAAU;iBACX;OACE,CAAA,EACL,oBAAC,OAAD;OAAK,WAAU;iBACZ,MAAM,KAAK,SACV,qBAAC,UAAD;QAEE,MAAK;QACL,eAAe,gBAAgB,KAAK,KAAM;QAC1C,WAAU;kBAJZ,CAMG,KAAK,QACJ,oBAAC,SAAD;SACE,MAAM,KAAK;SACX,WAAU;SACV,CAAA,EAEJ,oBAAC,QAAD;SAAM,WAAU;mBAAY,KAAK;SAAa,CAAA,CACvC;UAZF,KAAK,KAYH,CACT;OACE,CAAA,CACF;QAtBI,YAsBJ,CACN;KACE,CAAA,CACF;;GACF,CAAA,CAEJ;;;;;AC3GV,SAAgB,wBAAwB,EACtC,cACkD;CAClD,MAAM,EAAE,eAAe,aAAa;CAEpC,MAAM,WAAW,cAAc;EAC7B,MAAM,MAAM,8BAA8B;AAC1C,SAAO,aAAa,sBAAsB,IAAI,GAAG;IAChD,CAAC,WAAW,CAAC;AAEhB,QACE,oBAAC,OAAD;EAAK,WAAU;YACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,aAAa,WAC3C,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,MAAD;GAAI,WAAU;aACX;GACE,CAAA,EACL,oBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,SACV,qBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AACb,SAAI,KAAK,KAAM,YAAW,KAAK,KAAK;;IAEtC,WAAU;cANZ,CAQG,KAAK,QAAQ,oBAAC,SAAD;KAAS,MAAM,KAAK;KAAM,WAAU;KAAW,CAAA,EAC7D,oBAAC,QAAD;KAAM,WAAU;eAAW,KAAK;KAAa,CAAA,CACtC;MATF,KAAK,KASH,CACT;GACE,CAAA,CACF,EAAA,EAnBI,YAmBJ,CACN;EACE,CAAA;;;;AChCV,SAAS,YAAY,MAAyC;AAC5D,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,MAAM;AACtC,KAAI,MAAM,UAAU,EAClB,UACG,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,SAAS,KAAK,MAAM,KACzD,aAAa;AAEjB,SAAQ,KAAK,MAAM,KAAK,aAAa;;AAGvC,SAAgB,qBAAqB,EACnC,YACA,YACsD;CACtD,MAAM,EAAE,UAAU,iBAAiB,YAAY;CAC/C,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,OAAO,YAAY;CACzB,MAAM,YAAY,cAAc;CAEhC,MAAM,iBAAiB,aACpB,SAAiB;AAChB,YAAU,MAAM;AAChB,aAAW,KAAK;IAElB,CAAC,WAAW,CACb;AAED,KAAI,CAAC,YAAY,CAAC,aAAc,QAAO;CAEvC,MAAM,WAAW,YAAY,MAAM,KAAK;AAExC,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;EACE,MAAK;EACL,eAAe,UAAU,KAAK;EAC9B,WAAU;EACV,cAAW;YAEV,MAAM,WACL,oBAAC,OAAD;GACE,KAAK,KAAK;GACV,KAAK,KAAK,QAAQ;GAClB,WAAU;GACV,CAAA,GAEF,oBAAC,QAAD;GAAM,WAAU;aACb;GACI,CAAA;EAEF,CAAA,EAER,UACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IAAM,WAAU;cAAwC;IAEjD,CAAA,EACP,oBAAC,UAAD;IACE,MAAK;IACL,eAAe,UAAU,MAAM;IAC/B,WAAU;IACV,cAAW;cAEX,oBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;IACjB,CAAA,CACL;MAEN,qBAAC,OAAD;GAAK,WAAU;aAAf;IACG,QACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,KAAK,WACJ,oBAAC,OAAD;MACE,KAAK,KAAK;MACV,KAAK,KAAK,QAAQ;MAClB,WAAU;MACV,CAAA,GAEF,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA,EAET,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,KAAK,QACJ,oBAAC,KAAD;OAAG,WAAU;iBACV,KAAK;OACJ,CAAA,EAEL,KAAK,SACJ,oBAAC,KAAD;OAAG,WAAU;iBACV,KAAK;OACJ,CAAA,CAEF;QACF;;IAGR,oBAAC,OAAD;KAAK,WAAU;eACb,qBAAC,UAAD;MACE,MAAK;MACL,eAAe,eAAe,eAAe;MAC7C,WAAU;gBAHZ,CAKE,oBAAC,YAAD,EAAY,WAAU,UAAW,CAAA,EACjC,oBAAC,QAAD,EAAA,UAAM,gBAAmB,CAAA,CAClB;;KACL,CAAA;IAEN,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,yBAAD,EAAyB,YAAY,gBAAkB,CAAA;KACnD,CAAA;IAEN,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,UAAD;MACE,MAAK;MACL,SAAS,UAAU;MACnB,WAAU;gBAHZ,CAKG,UAAU,SAAS,SAClB,oBAAC,MAAD,EAAM,WAAU,UAAW,CAAA,GAE3B,oBAAC,KAAD,EAAK,WAAU,UAAW,CAAA,EAE5B,qBAAC,QAAD,EAAA,UAAA,CAAM,WACI,UAAU,SAAS,UAAU,UAAU,OAC1C,EAAA,CAAA,CACA;SAER,YACC,qBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAHZ,CAKE,oBAAC,QAAD,EAAQ,WAAU,UAAW,CAAA,EAC7B,oBAAC,QAAD,EAAA,UAAM,WAAc,CAAA,CACb;QAEP;;IACF;KACF;IAEP,EAAA,CAAA;;;;AC5IP,SAAgB,UAAU,EACxB,YACA,aACA,YACA,YACoC;CACpC,MAAM,UAAU,YAAY;CAC5B,MAAM,YAAY,cAAc;CAEhC,MAAM,YAAY,UAAU,SAAS,SAAS,OAAO;CACrD,MAAM,wBAAwB,QAAQ,YAAY,QAAQ;AAE1D,QACE,qBAAC,UAAD;EAAQ,WAAU;YAAlB,CACG,wBACC,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,sBAAD;KAAkC;KAAsB;KAAY,CAAA;IAChE,CAAA;GACF,CAAA,GAEN,qBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,QAAQ,YAAY,CAAC,QAAQ,gBAC5B,oBAAC,UAAD;KACE,MAAK;KACL,SAAS,QAAQ;KACjB,WAAU;KACV,cAAW;eAEX,oBAAC,MAAD,EAAM,WAAU,UAAW,CAAA;KACpB,CAAA;IAIX,oBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;IAG1B,oBAAC,oBAAD,EAAgC,YAAc,CAAA;IAG9C,oBAAC,UAAD;KACE,MAAK;KACL,SAAS,UAAU;KACnB,WAAU;KACV,cAAY,0BAA0B,UAAU,KAAK;eAErD,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;KACzB,CAAA;IACL;MAIP,yBAAyB,cAAc,WAAW,SAAS,KAC1D,oBAAC,OAAD;GAAK,WAAU;aACZ,WAAW,KAAK,QAAQ;IACvB,MAAM,UAAU,cAAc,IAAI,KAAK;AACvC,QAAI,CAAC,QAAS,QAAO;AAIrB,WACE,oBAAC,UAAD;KAEE,MAAK;KACL,eAAe,WAAW,QAAQ;KAClC,WAAW,kFAPE,cAAc,YAAY,KAAK,UAStC,mCACA;eAGL,IAAI;KACE,EAVF,IAAI,MAAM,QAUR;KAEX;GACE,CAAA,CAED;;;;;;;;;;;;;;;;;;ACrEb,SAAgB,iBAAiB,SAK/B;CACA,MAAM,MAAM,aAAa;CACzB,MAAM,cAAc,gBAAgB;CACpC,MAAM,eAAe,OAAO,MAAM;CAKlC,MAAM,YAAY,OAAO,SAAS,OAAO;AACzC,WAAU,UAAU,SAAS;CAE7B,MAAM,EAAE,QAAQ,WAAW,SAAS,UAAU,YAAY;EACxD,aAAa,cAAsB,IAAI,MAAM,cAAc,UAAU;EACrE,WAAW,OAAO,SAAS;AAGzB,eAAY,OAAO;AACnB,OAAI;AACF,UAAM,eAAe,iBAAiB;WAChC;AAIR,cAAW,KAAK,QAAQ,KAAK,UAAU,QAAQ;AAC/C,UAAO,SAAS,QAAQ;;EAE1B,eAAe;AACb,gBAAa,UAAU;;EAE1B,CAAC;AAWF,QAAO;EACL,eAVoB,aACnB,cAAsB;AACrB,OAAI,aAAa,QAAS;AAC1B,gBAAa,UAAU;AACvB,UAAO,UAAU;KAEnB,CAAC,OAAO,CACT;EAIY;EACF;EACF;EACR;;;;AChEH,SAAgB,mBAAmB,EACjC,WACA,eACA,iBAAiB,OACjB,YAC4B;CAC5B,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,MAAM;CACjE,MAAM,CAAC,kBAAkB,uBAAuB,SAAwB,KAAK;CAC7E,MAAM,sBAAsB,OAC1B,KACD;AAGD,iBAAgB;AACd,mBAAiB,KAAK;IACrB,CAAC,eAAe,GAAG,CAAC;AAGvB,iBAAgB;AACd,eAAa;AACX,OAAI,oBAAoB,YAAY,KAClC,cAAa,oBAAoB,QAAQ;;IAG5C,EAAE,CAAC;CAEN,MAAM,oBAAoB,cAAc;AACtC,MAAI,CAAC,cAAe,QAAO;EAC3B,MAAM,OAAO,cAAc,aAAa;AACxC,SAAO,UAAU,QAAQ,MAAM,EAAE,KAAK,aAAa,CAAC,SAAS,KAAK,CAAC;IAClE,CAAC,WAAW,cAAc,CAAC;CAE9B,MAAM,QAAQ,kBAAkB;AAC9B,YAAU,MAAM;IACf,EAAE,CAAC;CAEN,MAAM,sBAAsB,kBAAkB;AAC5C,MAAI,oBAAoB,YAAY,KAClC,cAAa,oBAAoB,QAAQ;AAE3C,sBAAoB,UAAU,iBAAiB;AAC7C,oBAAiB,KAAK;AACtB,uBAAoB,UAAU;KAC7B,IAAM;IACR,EAAE,CAAC;CAEN,MAAM,sBAAsB,aACzB,OAAe;AACd,MAAI,OAAO,eAAe,GAAI;AAC9B,MAAI,kBAAkB,KAAM;AAE5B,MAAI,gBAAgB;AAClB,UAAO;AACP,oBAAiB,GAAG;AACpB,uBAAoB,GAAG;AACvB,wBAAqB,KAAK;AAC1B;;AAIF,mBAAiB,GAAG;AACpB,WAAS,GAAG;AACZ,SAAO;AACP,mBAAiB,GAAG;AACpB,uBAAqB;IAEvB;EACE,eAAe;EACf;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,sBAAsB,kBAAkB;AAC5C,MAAI,qBAAqB,KAAM;AAC/B,mBAAiB,iBAAiB;AAClC,WAAS,iBAAiB;AAC1B,SAAO;AACP,mBAAiB,GAAG;AACpB,sBAAoB,KAAK;AACzB,uBAAqB;AACrB,uBAAqB,MAAM;IAC1B;EAAC;EAAkB;EAAU;EAAO;EAAoB,CAAC;CAE5D,MAAM,qBAAqB,kBAAkB;AAC3C,sBAAoB,KAAK;AACzB,uBAAqB,MAAM;AAC3B,mBAAiB,GAAG;IACnB,EAAE,CAAC;AASN,QAAO;EACL;EACA;EACA;EACA,WAXuB,aAAa,SAAkB;AACtD,aAAU,KAAK;AACf,OAAI,CAAC,KACH,kBAAiB,GAAG;KAErB,EAAE,CAAC;EAOJ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;AC3HH,SAAgB,aAAa,EAC3B,KACA,KACA,QAKC;CACD,MAAM,CAAC,OAAO,YAAY,SAAS,MAAM;AAEzC,KAAI,CAAC,OAAO,MACV,QACE,oBAAC,WAAD;EACE,OAAO;GAAE,QAAQ;GAAM,OAAO;GAAM;EACpC,WAAU;EACV,CAAA;AAGN,QACE,oBAAC,OAAD;EACE,WAAU;EACL;EACA;EACL,OAAO;EACP,QAAQ;EACR,eAAe,SAAS,KAAK;EAC7B,CAAA;;;;AC1BN,SAAgB,YAAY,EAC1B,UACA,UACA,MACA,UAAU,SACoC;AAE9C,QACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,WAAU;YACb,oBAAC,cAAD;GAAc,KAAK;GAAM,KAJX,YAAY,YAAY;GAIK,MAAM;GAAM,CAAA;EACnD,CAAA,EACN,oBAAC,OAAD;EACE,WAAWC,KACT,wCACA,UAAU,kBAAkB,SAC7B;YAED,oBAAC,QAAD;GAAM,WAAU;aAA0B;GAAY,CAAA;EAClD,CAAA,CACL,EAAA,CAAA;;;;ACbP,SAAgB,YAAY,EAC1B,WACA,eACA,aACA,iBACmB;AACnB,KAAI,CAAC,UAAU,OACb,QAAO,oBAAC,MAAD;EAAI,WAAU;YAA0B;EAAqB,CAAA;AAEtE,QACE,oBAAA,YAAA,EAAA,UACG,UAAU,KAAK,YAAY;EAC1B,MAAM,WAAW,QAAQ,OAAO,eAAe;EAC/C,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,aAAa,YAAY,kBAAkB;AAEjD,SACE,qBAAC,UAAD;GACE,MAAK;GAEL,UAAU;GACV,eAAe,YAAY,QAAQ,GAAG;GACtC,WAAWC,KACT,wDACA,aACI,kCACA,+DACJ,YAAY,oCACZ,eAAe,6BAChB;aAZH,CAcE,oBAAC,aAAD;IACE,UAAU,QAAQ;IAClB,UAAU,QAAQ;IAClB,MAAM,QAAQ;IACd,CAAA,EACD,eACC,oBAAC,QAAD;IAAM,WAAU;cAAwC;IAEjD,CAAA,CAEF;KAtBF,QAAQ,GAsBN;GAEX,EACD,CAAA;;;;AChCP,SAAgB,wBAAwB,EACtC,QACA,cACA,eACA,gBACA,WACA,eACA,aACA,eACA,WAC+B;AAC/B,QACE,qBAAC,cAAD;EAAc,MAAM;EAAsB;YAA1C,CACE,oBAAC,qBAAD;GAAqB,SAAA;aAAS;GAA8B,CAAA,EAC5D,qBAAC,qBAAD;GACE,WAAU;GACV,OAAM;GACN,MAAK;GACL,YAAY;aAJd;IAME,oBAAC,OAAD;KACE,MAAK;KACL,aAAY;KACZ,OAAO;KACP,WAAW,MAAM,eAAe,EAAE,OAAO,SAAS,GAAG;KACrD,WAAU;KACV,aAAY;KACZ,gBAAe;KACf,YAAY;KACZ,CAAA;IACF,oBAAC,mBAAD;KAAmB,WAAU;eAAgC;KAEzC,CAAA;IACpB,oBAAC,aAAD;KACa;KACE;KACE;KACA;KACf,CAAA;IACkB;KACT;;;;;ACvCnB,SAAgB,qBAAqB,EACnC,QACA,cACA,eACA,gBACA,WACA,eACA,aACA,eACA,WAC4B;AAC5B,QACE,qBAAC,OAAD;EAAO,MAAM;EAAsB;YAAnC,CACE,oBAAC,cAAD;GAAc,SAAA;aAAS;GAAuB,CAAA,EAC9C,oBAAC,cAAD;GACE,MAAK;GACL,WAAU;aAEV,qBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,MAAD;OAAI,WAAU;iBAAwC;OAEjD,CAAA,EACL,oBAAC,YAAD;OACE,MAAM;OACN,eAAe,aAAa,MAAM;OAClC,WAAU;OACV,cAAW;OACX,CAAA,CACE;;KAGN,oBAAC,OAAD;MAAK,WAAU;gBACb,oBAAC,OAAD;OACE,MAAK;OACL,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,eAAe,EAAE,OAAO,SAAS,GAAG;OACrD,WAAU;OACV,aAAY;OACZ,gBAAe;OACf,YAAY;OACZ,CAAA;MACE,CAAA;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,MAAD;QAAI,WAAU;kBAAgC;QAAc,CAAA;OACxD,CAAA,EACN,oBAAC,aAAD;OACa;OACE;OACE;OACA;OACf,CAAA,CACE;;KACF;;GACO,CAAA,CACT;;;;;AC/DZ,SAAgB,6BAA6B,EAC3C,MACA,cACA,WACA,YACoC;CACpC,MAAM,eAAe,OAAO,MAAM;AAElC,QACE,oBAAC,aAAD;EACQ;EACN,eAAe,WAAW;AACxB,gBAAa,OAAO;AACpB,OAAI,CAAC,UAAU,CAAC,aAAa,QAC3B,WAAU;AAEZ,OAAI,CAAC,OAAQ,cAAa,UAAU;;YAGtC,qBAAC,oBAAD,EAAA,UAAA,CACE,qBAAC,mBAAD,EAAA,UAAA;GACE,oBAAC,kBAAD,EAAA,UACE,oBAAC,eAAD,EAAe,WAAU,kBAAmB,CAAA,EAC3B,CAAA;GACnB,oBAAC,kBAAD,EAAA,UAAkB,mBAAkC,CAAA;GACpD,oBAAC,wBAAD,EAAA,UAAwB,4FAGC,CAAA;GACP,EAAA,CAAA,EACpB,qBAAC,mBAAD,EAAA,UAAA,CACE,oBAAC,mBAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,oBAAC,mBAAD;GACE,eAAe;AACb,iBAAa,UAAU;AACvB,eAAW;;aAEd;GAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;EACT,CAAA;;;;AChDlB,SAAgB,gBAAgB,EAC9B,WACA,eACA,MACA,UACA,gBACA,UACA,WACgC;CAChC,MAAM,WAAW,mBAAmB;EAClC;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,CAAC,KAAM,QAAO;CAElB,MAAM,iBACJ,qBAAC,UAAD;EACE,MAAK;EACL,WAAU;YAFZ,CAIE,oBAAC,aAAD;GACE,UAAU,eAAe;GACzB,UAAU,eAAe;GACzB,MAAM,eAAe,QAAQ;GAC7B,SAAS;GACT,CAAA,EACD,WACC,oBAAC,cAAD,EAAc,WAAU,oDAAqD,CAAA,GAE7E,oBAAC,aAAD,EAAa,WAAU,uDAAwD,CAAA,CAE1E;;CAGX,MAAM,iBAAiB,WAAW;AAElC,QACE,qBAAA,YAAA,EAAA,UAAA,CACG,WACC,oBAAC,sBAAD;EACE,QAAQ,SAAS;EACjB,cAAc,SAAS;EACvB,eAAe,SAAS;EACxB,gBAAgB,SAAS;EACzB,WAAW,SAAS;EACL;EACf,aAAa,SAAS;EACtB,eAAe,SAAS;EACxB,SAAS;EACT,CAAA,GAEF,oBAAC,yBAAD;EACE,QAAQ,SAAS;EACjB,cAAc,SAAS;EACvB,eAAe,SAAS;EACxB,gBAAgB,SAAS;EACzB,WAAW,SAAS;EACL;EACf,aAAa,SAAS;EACtB,eAAe,SAAS;EACxB,SAAS;EACT,CAAA,EAGJ,oBAAC,8BAAD;EACE,MAAM,SAAS;EACf,cAAc,SAAS;EACvB,WAAW,SAAS;EACpB,UAAU,SAAS;EACnB,CAAA,CACD,EAAA,CAAA;;;;ACtFP,SAAgB,wBAAwB,EAAE,QAA2B;AACnE,KAAI,CAAC,KAAM,QAAO;AAElB,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAACC,YAAD,EAAU,WAAU,yCAA0C,CAAA,EAC9D,oBAACA,YAAD,EAAU,WAAU,uBAAwB,CAAA,CACxC;;;;;;;;;ACKV,SAAgB,qBAA+C;CAC7D,MAAM,EAAE,MAAM,MAAM,cAAc,gBAAgB;CAClD,MAAM,EAAE,kBAAkB,kBAAkB;CAC5C,MAAM,EAAE,aAAa,YAAY;CAEjC,MAAM,YAAuB,cAEzB,MAAM,WAAW,KAAK,OAAO;EAC3B,IAAI,EAAE;EACN,MAAM,EAAE;EACR,UAAU,EAAE,YAAY;EACxB,UAAU,EAAE,YAAY;EACzB,EAAE,IAAI,EAAE,EACX,CAAC,MAAM,UAAU,CAClB;CAID,MAAM,kBAAkB,MAAM,SAAS;CACvC,MAAM,gBAAgB,cACd,UAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,EACrD,CAAC,WAAW,gBAAgB,CAC7B;AAED,KAAI,UACF,QAAO,oBAAC,yBAAD,EAAyB,MAAA,MAAO,CAAA;AAGzC,KAAI,UAAU,UAAU,EACtB,QAAO;AAGT,QACE,oBAAC,iBAAD;EACa;EACI;EACf,MAAA;EACU;EACV,UAAU;EACV,CAAA;;;;AChDN,MAAa,uBAAuB;CAClC;CACA;CACA;CACD;;AAWD,MAAa,sBAA0C,qBAAqB,KACzE,UAAU;CACT,KAAK;CACL,OAAO,wBAAwB,MAAM;CACrC;CACD,EACF;;;;;;ACvBD,SAAgB,qBAAqB;AACnC,QACE,oBAAC,OAAD;EAAK,WAAU;YACb,qBAAC,OAAD;GAAK,WAAU;aAAf;IACE,oBAAC,MAAD;KAAI,WAAU;eAAwC;KAAkB,CAAA;IACxE,oBAAC,KAAD;KAAG,WAAU;eAA6B;KAEtC,CAAA;IACJ,oBAAC,KAAD;KAAG,WAAU;eAAqC;KAE9C,CAAA;IACA;;EACF,CAAA;;;ACoNV,MAAM,aAAa,IAAI,IA/KgC;CACrD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,mBAAmB;GACjB;GACA;GACA;GACA;GACD;EACD,WAAW;GACT,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAY,OAAO;KAAY;IACxC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC1C;KAAE,OAAO;KAAoB,OAAO;KAAoB;IACxD;KAAE,OAAO;KAAc,OAAO;KAAc;IAC7C;GACD,cAAc;GACf;EACD,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAY,OAAO;KAAO;IACpC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,mBAAmB;GACjB;GACA;GACA;GACA;GACD;EACD,WAAW;GACT,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAY,OAAO;KAAY;IACxC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAS,OAAO;KAAS;IAClC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC1C;KAAE,OAAO;KAAoB,OAAO;KAAoB;IACxD;KAAE,OAAO;KAAc,OAAO;KAAc;IAC7C;GACD,cAAc;GACf;EACD,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAW,OAAO;KAAO;IAClC;KAAE,OAAO;KAAU,OAAO;KAAM;IAChC;KAAE,OAAO;KAAY,OAAO;KAAO;IACpC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,UACE;EACF,YAAY;EACZ,WAAW;EACX,mBAAmB,CAAC,cAAc;EAClC,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAO,OAAO;KAAO;IAC9B;KAAE,OAAO;KAAQ,OAAO;KAAQ;IAChC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAY,OAAO;KAAY;IACzC;GACD,cAAc;GACf,CACF;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EAEb,UACE;EACF,YAAY;EACZ,WAAW;EACX,mBAAmB,CAAC,cAAc;EAClC,cAAc,CACZ;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,cAAc;GACd,KAAK;GACL,KAAK;GACL,aAAa;GACd,EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,SAAS;IACP;KAAE,OAAO;KAAO,OAAO;KAAO;IAC9B;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAU,OAAO;KAAU;IACpC;KAAE,OAAO;KAAa,OAAO;KAAa;IAC3C;GACD,cAAc;GACf,CACF;EACF;CACF,CAIqB,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAC1C;;;;;AAMD,SAAgB,cAAc,UAAgD;AAC5E,QAAO,WAAW,IAAI,SAAS;;;;;;;;AASjC,SAAgB,cAAoC,QAAc;AAChE,KAAI,OAAO,SAAS,SAAS,CAAC,OAAO,SAAU,QAAO;CAEtD,MAAM,SAAS,cAAc,OAAO,SAAS;AAC7C,KAAI,CAAC,QAAQ;AACX,UAAQ,KACN,gCAAgC,OAAO,SAAS,kCACjD;AACD,SAAO;;AAGT,QAAO;EACL,GAAG;EACH,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,WAAW,OAAO,aAAa,OAAO;EACvC;;;;;;;;;AC7OH,SAAS,aAAa,QAA4B;AAChD,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,UAAU,OAAO,YACnB,IAAI,KAAK,UAAU,OAAO,UAAU,KACpC;AAGJ,SAAO,GADU,OAAO,YAAY,OAAO,WACtB;;AAEvB,KAAI,OAAO,SAAS,SAKlB,QAAO,WAHL,OAAO,eAAe,KAAK,SAAS;AAClC,SAAO,GAAG,KAAK,cAAc,GAAG,KAAK;GACrC,IAAI,EAAE,EACgB,KAAK,IAAI;AAErC,KAAI,OAAO,SAAS,SAClB,QAAO,UAAU,OAAO,WAAW,GAAG,OAAO;AAE/C,QAAO;;;;;;AAOT,SAAS,wBACP,cAKA,SACA,UACA,aACe;AACf,KAAI,QAAQ,WAAW,EAAG,QAAO,KAAA;AACjC,KAAI,aAAa,MAAM,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,CAAE,QAAO,KAAA;CAG7D,MAAM,WAAW,CAAC,CADE,aAAa,MAAM,MAAM,EAAE,MAAM,EACrB;AAEhC,KAAI,YAAY,aAAa,SAC3B,QAAO,YAAY;AAGrB,KAAI,SACF;CAGF,MAAM,gBAAyC,EAAE;AAEjD,MAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;EAC5C,MAAM,QAAQ,aAAa;EAC3B,MAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,OAAO,QAAQ,CAAC,OAAQ;AAG7B,MAAI,CAAC,OAAO,YAAa;EAGzB,IAAI,SAAkB,MAAM;AAC5B,MAAI,OAAO,WAAW;GACpB,MAAM,cAAc,SAAS,aAAa,OAAO;AACjD,OAAI,YACF,UAAS,YAAY,QAAQ,OAAO;OAEpC,SAAQ,KAAK,cAAc,OAAO,UAAU,yBAAyB;;AAKzE,MACE,WAAW,QACX,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,OAAO,IACtB,OAAO,YAAY,SAAS,GAC5B;GACA,MAAM,YAAY;AAClB,QAAK,MAAM,QAAQ,OAAO,YACxB,KAAI,QAAQ,UACV,eAAc,QAAQ,UAAU;QAIpC,MAAK,MAAM,QAAQ,OAAO,YACxB,eAAc,QAAQ;;AAK5B,QAAO;;;;;;;;;AAUT,SAAgB,cACd,QACA,SACqB;CACrB,MAAM,SAAS,6BAA6B;CAC5C,MAAM,WAAW,SAAS,YAAY,OAAO;CAC7C,MAAM,UAAU,SAAS,WAAW,OAAO;CAC3C,MAAM,gBAAgB,OAAO;CAC7B,MAAM,YAAY,OAAO;CAGzB,MAAM,UAAU,eACP,OAAO,YAAY,WAAW,EAAE,EAAE,IAAI,cAAc,EAC3D,CAAC,OAAO,YAAY,QAAQ,CAC7B;CACD,MAAM,cAAc,OAAO,YAAY;CACvC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,aAAa,OAAO;CAE1B,MAAM,kBAAkB,OAAuC,EAAE,CAAC;CAGlE,MAAM,UAAU,WAAW,EACzB,SAAS,QAAQ,KAAK,YAAwB;EAC5C,UAAU;GACR;GACA,aAAa,OAAO;GACpB;GACA;GACD;EACD,SAAS,OAAO,EACd,aAGsB;GACtB,MAAM,UAA6B;IACjC;IACA;IACA;IACA;IACA;IACA;IACD;GAED,MAAM,UAAU,SAAS,SAAS,OAAO;AACzC,OAAI,CAAC,QACH,OAAM,IAAI,MACR,0CAA0C,OAAO,OAClD;AAGH,UAAO,QAAQ,QAAQ,QAAQ;;EAEjC,SAAS,QAAQ,SAAS;EAC1B,OAAO,aAAa,cAAc;EAClC,YAAY,aAAa,cAAc;EACvC,iBAAiB,OAAO,mBAAmB;EAC5C,EAAE,EACJ,CAAC;AAGF,iBAAgB,UAAU;CAG1B,MAAM,YAAY,QAAQ,MAAM,MAAM,EAAE,UAAU;CAGlD,MAAM,cAAc,QAAQ,MAAM,MAAM,EAAE,MAAM;CAChD,MAAM,QACJ,aAAa,iBAAiB,QAC1B,YAAY,QACZ,aAAa,QACX,IAAI,MAAM,OAAO,YAAY,MAAM,CAAC,GACpC;CAGR,MAAM,mBAAmB,QACtB,KAAK,MAAM,GAAG,EAAE,cAAc,GAAG,EAAE,YAAY,CAC/C,KAAK,IAAI;CAGZ,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;AAqBrB,QAAO;EACL,MApBW,cAAc;AAMzB,UAAO,wBALgB,WAAW,QAAQ,KAAK,OAAO;IACpD,MAAM,EAAE;IACR,WAAW,EAAE;IACb,OAAO,EAAE,iBAAiB,QAAQ,EAAE,QAAQ;IAC7C,EAAE,EAGD,SACA,UACA,YACD;KACA;GAAC;GAAkB;GAAS;GAAU;GAAY,CAAC;EASpD;EACA;EACA,SARc,kBAAkB;AAChC,mBAAgB,QAAQ,SAAS,MAAM,EAAE,SAAS,CAAC;KAClD,EAAE,CAAC;EAOL;;;;ACvOH,SAAgB,aAAgC;AAC9C,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,KAAD;GAAG,WAAU;aAAwB;GAAwB,CAAA,EAC7D,oBAAC,KAAD;GAAG,WAAU;aAAgC;GAEzC,CAAA,CACA;;;;;ACYV,MAAM,eAAwC,EAAE;;;;;AAMhD,SAAS,oBAAoB,EAC3B,QACA,WACA,iBACA,kBACA,gBACA,WACiD;CAEjD,MAAM,wBAAwB,mBAAmB;CACjD,MAAM,EAAE,MAAM,WAAW,UAAU,cAAc,QAAQ,EAAE,SAAS,CAAC;CAMrE,MAAM,cAAc,eACX;EACL,GAAG,OAAO;EACV,GAAG;EACH,GAAG;EACJ,GACD;EAAC,OAAO;EAAO;EAAM;EAAsB,CAC5C;AAGD,KAAI,aAAa,OAAO,YAAY,SAAS,iBAAiB,OAAO;AACnE,MAAI,iBACF,QAAO,oBAAA,YAAA,EAAA,UAAG,kBAAoB,CAAA;AAGhC,SACE,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,WAAW,KAAK;aAEzB,oBAAC,QAAD;IAAM,WAAU;cAAU;IAAiB,CAAA;GACvC,CAAA;;AAKV,KAAI,SAAS,CAAC,MAAM;AAClB,MAAI,eACF,QAAO,oBAAA,YAAA,EAAA,UAAG,eAAe,MAAM,EAAI,CAAA;AAGrC,SAAO,oBAAC,YAAD,EAAc,CAAA;;AAGvB,QAAO,oBAAC,WAAD,EAAW,GAAI,aAAe,CAAA;;;;;;AAOvC,MAAa,kBACX,KAAK,oBAAoB;;;;;;;;;;;;;AC/D3B,SAAS,eAAe,EAAE,QAAQ,SAA8B;CAE9D,MAAM,YADW,aAAa,CACH,OAAO;AAElC,KAAI,CAAC,WAAW;AACd,UAAQ,KACN,oCAAoC,OAAO,OAAO,KAAK,CAAC,yBACzD;AACD,SAAO;;AAGT,KAAI,OAAO,WACT,QACE,oBAAC,OAAD;EAA8B,WAAU;YACtC,oBAAC,iBAAD;GAAyB;GAAmB;GAAa,CAAA;EACrD,EAFI,OAAO,MAAM,MAEjB;AAIV,QACE,oBAAC,OAAD;EAA8B,WAAU;YACtC,oBAAC,WAAD,EAAW,GAAI,OAAO,OAAS,CAAA;EAC3B,EAFI,OAAO,MAAM,MAEjB;;;;;;;AAgBV,SAAS,sBAAsB,EAC7B,QACA,aACmD;CACnD,MAAM,UAAU,OAAO;AAEvB,KAAI,CAAC,WAAW,QAAQ,WAAW,EACjC,QACE,oBAAC,OAAD;EAAK,WAAW,aAAa;YAC3B,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,KAAD,EAAA,UAAG,mCAAmC,CAAA;GAClC,CAAA;EACF,CAAA;AAIV,QACE,oBAAC,OAAD;EAAK,WAAW,aAAa;YAC1B,QAAQ,KAAK,QAAQ,UAAU;AAC9B,OAAI,CAAC,OAAQ,QAAO;AACpB,UACE,oBAAC,gBAAD;IAEU;IACD;IACP,EAHK,OAAO,MAAM,MAGlB;IAEJ;EACE,CAAA;;AAIV,MAAa,oBAET,KAAK,sBAAsB;;;ACvF/B,MAAM,WAAkD;CACtD,SAAS;CACT,QAAQ;CACR,eAAe;CAChB;AAED,MAAM,YAAY,oBAAoB,KAAK,SAAS;CAClD,GAAG;CACH,MAAM,SAAS,IAAI;CACpB,EAAE;AAEH,SAAS,aAAa,MAAiC;CACrD,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;AAC7B,KAAI,SAAS,SAAU,QAAO;AAC9B,KAAI,SAAS,gBAAiB,QAAO;AACrC,QAAO;;AAGT,SAAgB,oBAAoB,EAClC,YAGoB;CACpB,MAAM,EAAE,aAAa,aAAa,kBAAkB;CACpD,MAAM,YAAY,aAAa,YAAY;AAE3C,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,oBAAC,OAAD;IAAK,WAAU;cACZ,UAAU,KAAK,EAAE,KAAK,MAAM,OAAO,MAAM,WACxC,qBAAC,UAAD;KAEE,MAAK;KACL,eAAe,SAAS,KAAK;KAC7B,WAAW,sFACT,cAAc,MACV,uDACA;eAPR,CAUE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,MACM;OAXF,IAWE,CACT;IACE,CAAA;GAGN,oBAAC,OAAD;IAAK,WAAU;cACZ,UAAU,KAAK,EAAE,KAAK,MAAM,OAAO,MAAM,WACxC,qBAAC,UAAD;KAEE,MAAK;KACL,eAAe,SAAS,KAAK;KAC7B,WAAW,8EACT,cAAc,MACV,uDACA;eAPR,CAUE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,MACM;OAXF,IAWE,CACT;IACE,CAAA;GAGN,oBAAC,OAAD;IAAK,WAAU;IAAkB;IAAe,CAAA;GAC5C;;;;;ACzEV,MAAMC,kBAAgB,WACpB,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAA4B,MAAM,OAAO,EAC9C,SAAS,EAAE,eACZ,EAAE,CACJ;AACD,MAAMC,iBAAe,WACnB,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,cACZ,EAAE,CACJ;AACD,MAAMC,wBAAsB,WAC1B,OAAO,sCAAA,MAAA,MAAA,EAAA,EAAA,CAAkC,MAAM,OAAO,EACpD,SAAS,EAAE,qBACZ,EAAE,CACJ;AACD,MAAMC,oBAAkB,WACtB,OAAO,kCAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,iBACZ,EAAE,CACJ;AACD,MAAMC,mBAAiB,WACrB,OAAO,iCAAA,MAAA,MAAA,EAAA,EAAA,CAA6B,MAAM,OAAO,EAC/C,SAAS,EAAE,gBACZ,EAAE,CACJ;AACD,MAAMC,eAAa,WACjB,OAAO,6BAAA,MAAA,MAAA,EAAA,EAAA,CAAyB,MAAM,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CACzE;AACD,MAAMC,oBAAkB,WACtB,OAAO,kCAAA,MAAA,MAAA,EAAA,EAAA,CAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,iBACZ,EAAE,CACJ;AACD,MAAMC,mBAAiB,WACrB,OAAO,iCAA6B,MAAM,OAAO,EAC/C,SAAS,EAAE,gBACZ,EAAE,CACJ;AACD,MAAMC,qBAAmB,WACvB,OAAO,mCAA+B,MAAM,OAAO,EACjD,SAAS,EAAE,kBACZ,EAAE,CACJ;AACD,MAAMC,iBAAe,WACnB,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,cACZ,EAAE,CACJ;AACD,MAAM,gBAAgB,WACpB,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAA4B,MAAM,OAAO,EAC9C,SAAS,EAAE,eACZ,EAAE,CACJ;AACD,MAAM,oBAAoB,WACxB,OAAO,oCAAA,MAAA,MAAA,EAAA,EAAA,CAAgC,MAAM,OAAO,EAClD,SAAS,EAAE,mBACZ,EAAE,CACJ;AAED,MAAa,yBAAwD;CACnE,SAAST;CACT,QAAQC;CACR,eAAeC;CACf,UAAUC;CACV,UAAUC;CACV,MAAMC;CACN,WAAWC;CACX,UAAUC;CACV,WAAWE;CACX,kBAAkBD;CAClB,iBAAiBA;CACjB,eAAeA;CACf,mBAAmBA;CACnB,kBAAkBA;CAClB,eAAeA;CACf,SAAS;CACT,gBAAgB;CACjB;;AAGD,MAAM,qBAAqB,OAAO,KAAK,uBAAuB,CAAC,MAC5D,GAAG,MAAM,EAAE,SAAS,EAAE,OACxB;;AAGD,SAAgB,mBAAmB,MAAyC;AAC1E,MAAK,MAAM,OAAO,mBAChB,KAAI,SAAS,OAAO,KAAK,WAAW,MAAM,IAAI,CAC5C,QAAO,uBAAuB;;;;ACzEpC,MAAM,gBAAgB,IAAI,IAAY,qBAAqB;AAc3D,SAAgB,WAAW,EACzB,aACA,gBACA,aACA,SACA,UACA,cACqC;CAErC,MAAM,EAAE,eAAe,aAAa;CAGpC,MAAM,aAAa,cAAc;AAC/B,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,KAAA;AAC7C,SAAO,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC,QAAQ,CAAC;CAGb,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO,KAAA;AAC7C,SAAO,IAAI,IAAI,QAAQ,QAAQ,MAAM,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC,QAAQ,CAAC;CAGb,MAAM,gBAAgB,cAAc;AAClC,MAAI,gBAAgB,aAAa,YAAY;GAC3C,MAAM,OAAO,WAAW,IAAI,eAAe,UAAU;AACrD,OAAI,KAAM,QAAO;;AAEnB,MAAI,aACF,QACE,aAAa,IAAI,YAAY,IAAI,aAAa,IAAI,SAAS,IAAI,KAAA;IAIlE;EACD,gBAAgB;EAChB;EACA;EACA;EACA;EACD,CAAC;AAGF,KAAI,cAAc,cAAc,YAAY,CAC1C,QAAO,oBAAC,oBAAD,EAAsB,CAAA;CAI/B,MAAM,kBAAkB,cAAc;AACtC,KAAI,gBACF,QAAO,oBAAC,iBAAD;EAAiB,MAAM;EAAa,QAAQ;EAAc,CAAA;AAInE,KAAI,aAAa,aAAa;EAC5B,MAAM,mBAAmB,cAAc;AACvC,MAAI,iBACF,QAAO,oBAAC,kBAAD;GAAkB,MAAM;GAAa,QAAQ;GAAc,CAAA;;CAQtE,MAAM,eACJ,uBAAuB,aAAa,mBAAmB,YAAY;AACrE,KAAI,cAAc;EAChB,MAAM,UACJ,oBAAC,UAAD;GACE,UACE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;IAC9F,CAAA;aAGR,oBAAC,cAAD,EAAgB,CAAA;GACP,CAAA;AAGb,MAAI,cAAc,IAAI,SAAS,CAC7B,QAAO,oBAAC,qBAAD,EAAA,UAAsB,SAA8B,CAAA;AAG7D,SAAO;;AAIT,KAAI,uBAAuB,SAAS,CAClC,QAAO,oBAAC,uBAAD,EAAuB,MAAM,gBAAgB,SAAS,UAAY,CAAA;AAI3E,KAAI,cACF,QAAO,oBAAC,mBAAD,EAAmB,QAAQ,eAAiB,CAAA;AAIrD,QAAO,oBAAC,uBAAD,EAAuB,MAAM,gBAAgB,SAAS,aAAe,CAAA;;;;;;;;;ACrH9E,SAAgB,gBAAgB,OAAmC;CACjE,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,KAAM,OAAM,KAAK,cAAc,KAAK,KAAK,CAAC;AACnD,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,MAAM,KAAM,OAAM,KAAK,cAAc,MAAM,KAAK,CAAC;;AAIzD,QAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,OAAO;;;;;;AAO7E,SAAgB,gBACd,UACA,UACuB;CACvB,MAAM,aAAa,cAAc,SAAS;AAC1C,KAAI,CAAC,WAAY,QAAO,KAAA;AAExB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,eAAe,QACjB,QAAO;GAAE,aAAa;GAAS,MAAM;GAAI;AAE3C,MAAI,WAAW,WAAW,UAAU,IAAI,CACtC,QAAO;GACL,aAAa;GACb,MAAM,WAAW,MAAM,QAAQ,SAAS,EAAE;GAC3C;;;;;;;;;;;;;AAiBP,SAAgB,wBACd,UACA,UACQ;AAER,KAAI,aAAa,IACf,QAAO,SAAS,QAAQ,OAAO,GAAG;AAIpC,KAAI,SAAS,WAAW,SAAS,CAC/B,QAAO,SAAS,MAAM,SAAS,OAAO,CAAC,QAAQ,OAAO,GAAG;AAI3D,QAAO,SAAS,QAAQ,OAAO,GAAG;;AAGpC,SAAgB,gBACd,MACA,aACA,UACS;CAET,MAAM,WADQ,gBAAgB,aAAa,SAAS,EAC5B,eAAe,cAAc,YAAY;AAEjE,KAAI,cAAc,KAAK,KAAK,KAAK,SAAU,QAAO;AAClD,QACE,KAAK,UAAU,MAAM,UAAU,cAAc,MAAM,KAAK,KAAK,SAAS,IACtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChDJ,SAAgB,UAAU,EACxB,aACA,aACoB,EAAE,EAAuB;CAC7C,MAAM,cAAc,gBAAgB;AA0CpC,QAxCe,YAAY,YAAY;AAErC,QAAM,YAAY,eAAe;AAGjC,cAAY,OAAO;AAGnB,MAAI;AACF,SAAM,gBAAgB;WACf,OAAO;AAEd,WAAQ,MAAM,gDAAgD,MAAM;;AAItE,eAAa;AAIb,MAAI;AACF,kBAAe,OAAO;UAChB;AAKR,MAAI,YACF,QAAO,SAAS,OAAO;WACd,SACT,WAAU;OACL;GAIL,MAAM,aAAa,mBAAmB,OAAO,SAAS,KAAK;AAC3D,UAAO,SAAS,OAAO,GAAG,iBAAiB,6BAA6B;;IAEzE;EAAC;EAAa;EAAa;EAAS,CAAC;;;;;;;;ACvE1C,SAAgB,gBAAgB,EAC9B,YAC0C;CAC1C,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;AAajD,QACE,oBAAC,aAAD,EAAA,UACE,oBAAC,iBAAD,EAAA,UACE,qBAAC,mBAAD;EAAmB,SAdL,YAAY,YAAY;AAC1C,gBAAa,KAAK;AAClB,OAAI;AACF,UAAM,UAAU;YACT,OAAO;AACd,YAAQ,MAAM,oCAAoC,MAAM;aAChD;AACR,iBAAa,MAAM;;KAEpB,CAAC,SAAS,CAAC;EAKiC,UAAU;YAAnD,CACE,oBAAC,QAAD,EAAQ,WAAU,UAAW,CAAA,EAC7B,oBAAC,QAAD,EAAA,UAAO,YAAY,iBAAsB,WAAiB,CAAA,CACxC;KACJ,CAAA,EACN,CAAA;;;;ACOlB,MAAM,oBAAoB;AAE1B,SAAS,sBAAiC;AACxC,KAAI,OAAO,WAAW,YAAa,QAAO;CAC1C,MAAM,SAAS,aAAa,QAAQ,kBAAkB;AACtD,KAAI,WAAW,WAAW,WAAW,OAAQ,QAAO;AACpD,QAAO;;AAGT,SAAS,uBAAuB,OAAiC;AAC/D,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,KAAM,QAAO,cAAc,KAAK,KAAK;AAC9C,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,MAAM,KAAM,QAAO,cAAc,MAAM,KAAK;;AAGpD,QAAO;;AAGT,SAAS,mBACP,OACA,MAC4B;CAC5B,MAAM,aAAa,cAAc,KAAK;AACtC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,cAAc,KAAK,KAAK,KAAK,WAAY,QAAO;AACpD,MACE,KAAK,UAAU,MAAM,UAAU,cAAc,MAAM,KAAK,KAAK,WAAW,CAExE,QAAO;;;AAMb,SAAS,YACP,OACA,MAC4B;CAC5B,MAAM,aAAa,cAAc,KAAK;AACtC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,cAAc,KAAK,KAAK,KAAK,WAAY,QAAO;AACpD,OAAK,MAAM,SAAS,KAAK,YAAY,EAAE,CACrC,KAAI,cAAc,MAAM,KAAK,KAAK,WAAY,QAAO;;;AA4C3D,SAAgB,SAAS,EACvB,SAAS,aACT,YAAY,gBACZ,aACA,WAAW,KACX,aAAa,gBACb,YAAY,gBACZ,eACA,eACA,UACA,mBACA,YACmC;CAEnC,MAAM,qBAAqB,cAAc;EACvC,MAAM,WAAW,SAAS,QAAQ,YAAY,GAAG;AACjD,SAAO,WAAW,IAAI,aAAa;IAClC,CAAC,SAAS,CAAC;CAGd,MAAM,EAAE,MAAM,gBAAgB,cAAc,YAAY,EACtD,SAAS,CAAC,aACX,CAAC;CACF,MAAM,UAAU,eAAe;CAG/B,MAAM,EAAE,MAAM,gBAAgB,gBAAgB;CAC9C,MAAM,UAAU,eACP;EACL,MACE,aAAa,cAAc,aAAa,YACpC,GAAG,YAAY,WAAW,GAAG,YAAY,cACzC;EACN,OAAO,aAAa,SAAS;EAC7B,UAAU,aAAa,aAAa;EACpC,UAAU,aAAa;EACxB,GACD;EACE,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;EACb,aAAa;EACd,CACF;CAGD,MAAM,cAAc,cAAc;AAChC,MAAI,CAAC,SAAS,SAAS,QAAQ,OAAQ,QAAO,KAAA;EAC9C,MAAM,SAAS,QAAQ,QAAQ;EAC/B,MAAM,WAAW,QAAQ,QAAQ;AACjC,SAAO,WACF,OAAO,MAAM,MAAM,EAAE,OAAO,SAAS,IAAI,OAAO,KACjD,OAAO;IACV,CAAC,SAAS,SAAS,QAAQ,SAAS,SAAS,cAAc,CAAC;CAG/D,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,YAAa,QAAO,KAAA;AACzB,SAAO,aAAa,YAAY;IAC/B,CAAC,YAAY,CAAC;AAEjB,iBAAgB;AACd,MAAI,CAAC,cAAe;AACpB,aAAW,cAAc;AACzB,eAAa;AACX,oBAAiB;;IAElB,CAAC,cAAc,CAAC;AAInB,iBAAgB;EACd,MAAM,OAAO,SAAS;AACtB,MAAI,aAAa,GACf,MAAK,aAAa,cAAc,YAAY,GAAG;MAE/C,MAAK,gBAAgB,aAAa;AAEpC,eAAa;AACX,QAAK,gBAAgB,aAAa;;IAEnC,CAAC,aAAa,GAAG,CAAC;CAGrB,MAAM,WAAW,cAAc;AAC7B,MAAI,eAAgB,QAAO;EAC3B,MAAM,aAAa,SAAS,SAAS,YAAY;AACjD,MAAI,cAAc,WAAW,SAAS,EAAG,QAAO;AAChD,SAAO,sBAAsB;IAC5B,CAAC,gBAAgB,SAAS,SAAS,YAAY,iBAAiB,CAAC;CAGpE,MAAM,iBAAiB,cAAc;EACnC,MAAM,YAAY,SAAS,SAAS,mBAAmB;AACvD,MAAI,aAAa,UAAU,SAAS,EAAG,QAAO;AAC9C,SAAO;IACN,CAAC,SAAS,SAAS,mBAAmB,kBAAkB,SAAS,CAAC;CAGrE,MAAM,EAAE,eAAe,aAAa;CAEpC,MAAM,mBAAmB,cAChB,aAAa,sBAAsB,SAAS,GAAG,UACtD,CAAC,UAAU,WAAW,CACvB;CAED,MAAM,yBAAyB,cACtB,aAAa,sBAAsB,eAAe,GAAG,gBAC5D,CAAC,gBAAgB,WAAW,CAC7B;CAGD,MAAM,UAA0C,SAAS;CAGzD,MAAM,WAAW,cACT,gBAAgB,iBAAiB,EACvC,CAAC,iBAAiB,CACnB;CAGD,MAAM,CAAC,WAAW,gBAAgB,SAAoB,oBAAoB;CAE1E,MAAM,wBAAwB,aAAa,SAAoB;AAC7D,eAAa,KAAK;AAClB,MAAI,OAAO,WAAW,YACpB,cAAa,QAAQ,mBAAmB,KAAK;IAE9C,EAAE,CAAC;AAIN,iBAAgB;EACd,MAAM,OAAO,SAAS;EACtB,MAAM,OAAO,cAAc,SAAS,KAAA,IAAY;AAChD,MAAI,KACF,MAAK,aAAa,mBAAmB,KAAK;MAE1C,MAAK,gBAAgB,kBAAkB;AAEzC,eAAa;AACX,QAAK,gBAAgB,kBAAkB;;IAExC,CAAC,UAAU,CAAC;AAGf,iBAAgB;AACd,MAAI,CAAC,cAAe;EAEpB,IAAI,OAAO,SAAS,cAClB,6BACD;AACD,MAAI,CAAC,MAAM;AACT,UAAO,SAAS,cAAc,OAAO;AACrC,QAAK,OAAO;AACZ,YAAS,KAAK,YAAY,KAAK;;EAEjC,MAAM,SAAS;EAEf,MAAM,QAAQ;EACd,SAAS,OAAO,aAAsB;GAKpC,MAAM,QAHJ,cAAc,SAAU,cAAc,SAAS,UAAW,eAEjD,SAAS,MAAM,KAAK,MAAM,OAAO,MAAM,MAAM,MAAM,MAC5C,UAAU;AAC5B,UAAO,UAAU;AACjB,YAAS,KAAK,MAAM,kBAAkB;;EAGxC,MAAM,MAAM,OAAO,WAAW,+BAA+B;AAC7D,SAAO,IAAI,QAAQ;AAEnB,MAAI,cAAc,QAAQ;GACxB,MAAM,WAAW,MAA2B,OAAO,EAAE,QAAQ;AAC7D,OAAI,iBAAiB,UAAU,QAAQ;AACvC,gBAAa;AACX,QAAI,oBAAoB,UAAU,QAAQ;AAC1C,WAAO,QAAQ;AACf,aAAS,KAAK,MAAM,kBAAkB;;;AAI1C,eAAa;AACX,UAAO,QAAQ;AACf,YAAS,KAAK,MAAM,kBAAkB;;IAEvC,CAAC,eAAe,UAAU,CAAC;CAG9B,MAAM,CAAC,UAAU,eAAe,eAAuB;AACrD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,wBACL,OAAO,SAAS,UAChB,mBACD;GACD;AAGF,iBAAgB;AACd,MAAI,mBAAmB,KAAA,EAAW;AAClC,MAAI,OAAO,WAAW,YAAa;AAOnC,MAAI,CALgB,wBAClB,OAAO,SAAS,UAChB,mBACD,EAEiB;GAChB,MAAM,YAAY,uBAAuB,iBAAiB;AAC1D,OAAI,WAAW;IACb,MAAM,aACJ,uBAAuB,MACnB,IAAI,cACJ,GAAG,mBAAmB,GAAG;AAE/B,YAAQ,aAAa,MAAM,IAAI,WAAW;AAC1C,gBAAY,UAAU;;;IAGzB;EAAC;EAAkB;EAAgB;EAAmB,CAAC;AAG1D,iBAAgB;AACd,MAAI,mBAAmB,KAAA,EAAW;EAElC,MAAM,uBAAuB;AAK3B,eAJa,wBACX,OAAO,SAAS,UAChB,mBACD,CACgB;;AAGnB,SAAO,iBAAiB,YAAY,eAAe;AACnD,eAAa,OAAO,oBAAoB,YAAY,eAAe;IAClE,CAAC,gBAAgB,mBAAmB,CAAC;CAGxC,MAAM,SAAS,UAAU;EACvB,aAAa;EACb;EACD,CAAC;CAEF,MAAM,aAAa,kBAAkB;CAErC,MAAM,iBAAiB,aACpB,SAAiB;AAChB,MAAI,gBAAgB;AAClB,kBAAe,KAAK;AACpB;;EAEF,MAAM,aACJ,uBAAuB,MACnB,IAAI,SACJ,GAAG,mBAAmB,GAAG;AAC/B,UAAQ,UAAU,MAAM,IAAI,WAAW;AACvC,cAAY,KAAK;IAEnB,CAAC,gBAAgB,mBAAmB,CACrC;CAGD,MAAM,wBAAwB,cAE1B,kBAAkB,KAAA,IAChB,gBAEA,oBAAC,iBAAD,EAAiB,UAAU,QAAU,CAAA,EAEzC,CAAC,eAAe,OAAO,CACxB;CAGD,MAAM,YAAY,cACV,gBAAgB,YAAY,SAAS,EAC3C,CAAC,YAAY,SAAS,CACvB;CACD,MAAM,WAAW,WAAW,eAAe,cAAc,WAAW;CACpE,MAAM,aAAa,WAAW,QAAQ;CAOtC,MAAM,wBAJuB,mBAC3B,wBACA,SACD,EACmD,YAAY,EAAE;CAClE,MAAM,iBAAiB,YAAY,kBAAkB,SAAS;CAG9D,MAAM,cACJ,gBAAgB,SAChB,SAAS,MAAM,MAAM,EAAE,OAAO,gBAAgB,UAAU,EAAE,QAC1D,KAAA;CAGF,MAAM,UACJ,OAAO,aAAa,aAChB,SAAS;EAAE,aAAa;EAAY;EAAgB,CAAC,GACpD,YACC,qBAAC,sBAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAc,OAAO,aAAe,CAAA,EACpC,oBAAC,YAAD;EACE,aAAa;EACG;EACH;EACJ;EACC;EACE;EACZ,CAAA,CACmB,EAAA,CAAA;AAK/B,KAAI,aAAa,CAAC,QAChB,QAAO,oBAAC,iBAAD,EAAmB,CAAA;AAG5B,QACE,oBAAC,iBAAD;EAAiB,MAAM;YACrB,oBAAC,mBAAD;GACE,MAAM;GACN,cAAc;GACd,iBAAiB;aAEjB,oBAAC,OAAD;IACE,WAAU;IACV,cAAY,aAAa;IACzB,mBAAiB,cAAc,SAAS,KAAA,IAAY;cAEpD,oBAAC,uBAAD;KACE,aAAa;KACb,UAAU;KACV,UAAU;eAEV,oBAAC,gBAAD;MACE,gBACE,oBAAC,eAAD;OACE,UAAU;OACV,aAAa;OACb,YAAY;OACZ,CAAA;MAEJ,eACE,oBAAC,WAAD;OACE,YAAY;OACZ,aAAa;OACb,YAAY;OACZ,UAAU;OACV,CAAA;MAEJ,eACE,kBAAkB,KAAA,IAChB,gBAEA,oBAAC,oBAAD,EAAsB,CAAA;MAG1B,eAAe;MACf,cAAA;MACA,cACE,oBAAC,cAAD;OACE,UAAU;OACV,aAAa;OACb,YAAY;OACZ,iBAAiB,SAAS,iBAAiB;OAC3C,CAAA;gBAGH;MACc,CAAA;KACK,CAAA;IACpB,CAAA;GACY,CAAA;EACJ,CAAA;;;;;;;;;;ACxftB,SAAgB,WAAW,KAAiC;AAE1D,QADY,OAAO,KAAK,MACX;;;;;;;;;;;;;;;;ACSf,SAAgB,yBACd,WACgB;AAChB,QAAO;EACL,SAAS,WAAW,eAAe,IAAI;EACvC,oBAAoB,gBAAgB,IAAI;EACxC,GAAG;EACJ;;;;;;;;;;;AAYH,SAAgB,wBACd,WACiB;AACjB,QAAO,EAAE,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsCzB,SAAgB,aAAa,SAAuB,EAAE,EAAQ;CAC5D,MAAM,cAAc,SAAS,eAAe,OAAO,UAAU,OAAO;AACpE,KAAI,CAAC,YACH,OAAM,IAAI,MACR,yBAAyB,OAAO,UAAU,OAAO,aAClD;CAGH,MAAM,cAAc,yBAAyB,OAAO,MAAM;CAC1D,MAAM,aAAa,wBAAwB,OAAO,KAAK;CAEvD,IAAI,MACF,oBAAC,UAAD;EAAU,aAAa,OAAO;EAAa,GAAI,OAAO;EAAS,CAAA;AAGjE,KAAI,OAAO,WAAW;EACpB,MAAM,YAAY,OAAO;AACzB,QAAM,oBAAC,WAAD,EAAA,UAAY,KAAgB,CAAA;;CAGpC,MAAM,OACJ,oBAAC,mBAAD;EAAmB,QAAQ;YACzB,oBAAC,eAAD;GAAe,QAAQ;aACrB,oBAAC,aAAD,EAAA,UAAc,KAAkB,CAAA;GAClB,CAAA;EACE,CAAA;AAGtB,YAAW,YAAY,CAAC,OACtB,OAAO,oBAAoB,OAAO,oBAAC,YAAD,EAAA,UAAa,MAAkB,CAAA,CAClE;;;;;;;;;;;AC5FH,MAAa,oBAAoB,CAAC,SAAS,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BrD,SAAgB,kBAA2C;CACzD,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAKL;EACA,UAAU,SAAS,mBAAmB;EACtC,eAAe,IAAI,IAAI,QAAQ;EAC/B,SAAS,QAAQ;AAEf,UADgB,8BAA8B,IAAI,CACnC;;EAElB,CAAC;;;;;;;;;ACjDJ,MAAa,wBAAwB,CAAC,SAAS,cAAc;;;;;;;;;;;;;;;;;AAgC7D,SAAgB,sBAAiD;CAC/D,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;CAE/C,MAAM,QAAQ,SAAS;EACrB,UAAU,SAAS,sBAAsB;EACzC,eAAe,IAAI,YAAY,KAAK;EACrC,CAAC;CAEF,MAAM,cAAc,MAAM;AAyB1B,QAAO;EACL;EACA;EACA,KAzBU,cAAc;AACxB,WAAQ,UAAkB,SAA2B,WAAoB;AACvE,QAAI,CAAC,YACH,QAAO;AAIT,QAAI,YAAY,eACd,QAAO;IAGT,MAAM,sBAAsB,YAAY,YAAY;AACpD,QAAI,CAAC,oBACH,QAAO;AAGT,WAAO,oBAAoB,WAAW;;KAEvC,CAAC,YAAY,CAAC;EAQf,cANmB,aAAa,kBAAkB;EAOnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjCH,SAAgB,gBAAqC;CACnD,MAAM,EAAE,cAAc,UAAU,cAAc,SAAS,iBAAiB;AAExE,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;;;;;AC/CH,MAAa,wBAAwB,CAAC,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;AAqB5D,SAAgB,gBAAqC;CACnD,MAAM,MAAM,aAAa;CACzB,MAAM,EAAE,aAAa,0BAA0B;AAE/C,QAAO,SAAS;EACd,UAAU,SAAS,sBAAsB;EACzC,eAAe,IAAI,KAAK,SAAS;EAClC,CAAC;;;;ACpCJ,MAAME,wBAAM,IAAI,MAAM;AAEtB,SAASC,cAAY,MAAc,MAAc,SAAiB,GAAS;CACzE,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,GAAE,SAAS,MAAM,QAAQ,GAAG,EAAE;AAC9B,QAAO;;AAGT,MAAa,uBAAiD;CAC5D;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,kCAAkC;EACvD,OAAO;EACP,OAAOC,cAAY,GAAG,GAAG,EAAE,CAAC,aAAa;EACzC,KAAKA,cAAY,GAAG,GAAG,GAAG,CAAC,aAAa;EACxC,QAAQ;EACR,QAAQ;EACT;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,+CAA+C;EACpE,OAAO;EACP,OAAOA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EACxC,QAAQ;EACR,QAAQ;EACR,OAAO;EACR;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,2CAA2C;EAChE,OAAO;EACP,OAAOA,cAAY,GAAG,IAAI,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,GAAG,IAAI,GAAG,CAAC,aAAa;EACzC,QAAQ;EACR,QAAQ;EACR,KAAK;EACN;CACD;EACE,IAAI;EACJ,OAAO;EACP,aAAa,EAAE,MAAM,yCAAyC;EAC9D,OAAO;EACP,OAAOA,cAAY,IAAI,GAAG,EAAE,CAAC,aAAa;EAC1C,KAAKA,cAAY,IAAI,IAAI,EAAE,CAAC,aAAa;EACzC,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,UAAU;EACX;CACF;;;;;;;AClCD,SAAgB,oBAA6C;AAG3D,QAAO;EACL,MAAM,CAAC,GAAG,qBAAqB;EAC/B,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAAS,YAAY,MAAsB;CACzC,MAAM,IAAI,IAAI,KAAKA,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,aAA8B;CACzC;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa;EACb,WAAW,YAAY,EAAE;EACzB,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,GAAG;EACtB,aAAa,YAAY,GAAG;EAC5B,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACD;EACE,IAAI;EACJ,MAAM;EACN,OAAO,YAAY,EAAE;EACrB,aAAa,YAAY,EAAE;EAC3B,WAAW,YAAY,GAAG;EAC1B,aAAa;EACd;CACF;;;;;;;AC7BD,SAAgB,WAA2B;AAGzC,QAAO;EACL,MAAM,CAAC,GAAG,WAAW;EACrB,WAAW;EACX,SAAS;EACV;;;;;;;;;;;;;;;AC8DH,SAAgB,QACd,QAC4D;AAC5D,QAAO,OAAO,QAAQ,QAAQ,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;AAY7D,SAAgB,UACd,QACS;AACT,QAAO,OAAO;;;;;;;;;;;AAYhB,SAAgB,cACd,QAIA;AACA,QAAO,OAAO,WAAW,OAAO,UAAU,KAAA;;;;;;;;;;AAW5C,SAAgB,OACd,QACS;AACT,QAAO,CAAC,OAAO,aAAa,CAAC,OAAO;;;;;;;;;;;;;;;AAoBtC,SAAgB,eACd,OACA,KACQ;AACR,QAAO,MAAM,KAAK,SAAS,KAAK,KAAK;;;;;;;;;AAUvC,SAAgB,YACd,MACA,KACkB;AAClB,QAAO,OAAO;;;;;;AAkDhB,MAAa,iBAAiB;CAC5B,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,eAAe;CACf,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,kBAAkB;CAClB,OAAO;CACP,eAAe;CACf,OAAO;CACP,eAAe;CACf,sBAAsB;CACtB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,mBAAmB;CACnB,kBAAkB;CAClB,YAAY;CACb;;AAMD,SAAgB,eAAe,OAAsC;AACnE,QAAO,OAAO,OAAO,eAAe,CAAC,SAAS,MAAsB;;;;AChQtE,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAASC,WAAS,OAAuB;CACvC,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,SAAS,EAAE,UAAU,GAAG,MAAM;AAChC,QAAO,EAAE,aAAa;;AAExB,MAAa,kBAAuC;CAClD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWC,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,EAAE;EACtB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACD;EACE,IAAI;EACJ,UAAU;EACV,WAAW;EACX,cAAc;EACd,YAAY;EACZ,WAAWA,WAAS,GAAG;EACvB,MAAM;EACP;CACF;;;;;;;ACtDD,SAAgB,gBAAqC;AAGnD,QAAO;EACL,MAAM,CAAC,GAAG,gBAAgB;EAC1B,WAAW;EACX,SAAS;EACV;;;;ACjCH,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,kBAAkB;EACnB;CACD;EACE,IAAI;EACJ,kBAAkB;EACnB;CACD;EACE,IAAI;EACJ,kBAAkB;EACnB;CACF;;;;;;;ACOD,SAAgB,cAAiC;AAG/C,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAa,cAA0B;CACrC,KAAK;CACL,OAAO;CACP,OAAO;CACP,UAAU;CACX;;;;;;;ACeD,SAAgB,YAA6B;AAG3C,QAAO;EACL,MAAM;EACN,WAAW;EACX,SAAS;EACV;;;;AC3BH,MAAMC,wBAAM,IAAI,MAAM;AAEtB,SAAS,SAAS,OAAuB;CACvC,MAAM,IAAI,IAAI,KAAKA,MAAI;AACvB,GAAE,SAAS,EAAE,UAAU,GAAG,MAAM;AAChC,QAAO,EAAE,aAAa;;AAGxB,SAASC,UAAQ,MAAsB;CACrC,MAAM,IAAI,IAAI,KAAKD,MAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,qBAA8C;CACzD;EACE,IAAI;EACJ,OAAO;EACP,cAAc,CACZ;GAAE,IAAI;GAAU,MAAM;GAAO,OAAO;GAAkB,UAAU;GAAM,EACtE;GACE,IAAI;GACJ,MAAM;GACN,OAAO;GACP,UAAU;GACX,CACF;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SAAS;GACT,WAAW,SAAS,EAAE;GACtB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWC,UAAQ,GAAG;EACtB,WAAW,SAAS,EAAE;EACvB;CACD;EACE,IAAI;EACJ,OAAO;EACP,cAAc,CACZ;GAAE,IAAI;GAAU,MAAM;GAAO,OAAO;GAAkB,UAAU;GAAM,EACtE;GACE,IAAI;GACJ,MAAM;GACN,OAAO;GACP,UAAU;GACX,CACF;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SACE;GACF,WAAW,SAAS,EAAE;GACtB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWA,UAAQ,GAAG;EACtB,WAAW,SAAS,EAAE;EACvB;CACD;EACE,IAAI;EACJ,OAAO;EACP,cAAc;GACZ;IAAE,IAAI;IAAU,MAAM;IAAO,OAAO;IAAkB,UAAU;IAAM;GACtE;IACE,IAAI;IACJ,MAAM;IACN,OAAO;IACR;GACD;IAAE,IAAI;IAAU,MAAM;IAAc,OAAO;IAAwB;GACpE;EACD,aAAa;GACX,IAAI;GACJ,gBAAgB;GAChB,UAAU;GACV,YAAY;GACZ,MAAM;GACN,SACE;GACF,WAAWA,UAAQ,EAAE;GACrB,QAAQ;GACT;EACD,aAAa;EACb,QAAQ;EACR,WAAWA,UAAQ,GAAG;EACtB,WAAWA,UAAQ,EAAE;EACtB;CACF;AAED,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SACE;EACF,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACD;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SACE;EACF,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACD;EACE,IAAI;EACJ,gBAAgB;EAChB,UAAU;EACV,YAAY;EACZ,MAAM;EACN,SAAS;EACT,WAAW,SAAS,EAAE;EACtB,QAAQ;EACT;CACF;;;;;;;;;;;;;;;;;;;ACpGD,SAAgB,mBAA2C;AAGzD,QAAO;EACL,MAAM,CAAC,GAAG,mBAAmB;EAC7B,WAAW;EACX,SAAS;EACV;;;;;;;;;;;;;;;;;;;AA8BH,SAAgB,wBAEd,iBAC+B;AAG/B,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACV;;;;;;;ACSH,MAAa,mBAAmB;CAC9B,QAAQ;CACR,UAAU;CACV,MAAM;CACN,UAAU;CACX;;;AC9FD,MAAM,sBAAM,IAAI,MAAM;AAEtB,SAAS,QAAQ,MAAsB;CACrC,MAAM,IAAI,IAAI,KAAK,IAAI;AACvB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAC7B,QAAO,EAAE,aAAa;;AAGxB,MAAa,gBAAoC;CAC/C;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,OAAO,eAAe;EAC7B,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,aAAa;EACpB,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,SAAS;EACT,QAAQ;EACR,MAAM;EACN,OAAO;EACP,WAAW,QAAQ,EAAE;EACrB,WAAW,QAAQ,EAAE;EACtB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,OAAO;EACP,QAAQ;EACR,MAAM;EACN,MAAM,CAAC,WAAW;EAClB,WAAW,QAAQ,GAAG;EACtB,WAAW,QAAQ,GAAG;EACvB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,SAAS;EACT,UAAU;EACV,QAAQ;EACR,MAAM;EACN,SAAS;GACP,MAAM;GACN,OAAO;GACP,SAAS;GACV;EACD,WAAW,QAAQ,IAAI;EACvB,WAAW,QAAQ,GAAG;EACvB;CACD;EACE,IAAI;EACJ,WAAW;EACX,UAAU;EACV,OAAO;EACP,QAAQ;EACR,MAAM;EACN,OAAO;EACP,WAAW,QAAQ,IAAI;EACvB,WAAW,QAAQ,GAAG;EACvB;CACF;AAED,MAAa,eAAwB,cAAc;;;;;;;AC/EnD,SAAgB,gBAAgB,OAAuC;AACrE,QAAO,OAAO,OAAO,iBAAiB,CAAC,SAAS,MAAuB;;;;;;;;;;;;;;;;;;AA4CzE,SAAgB,YAEd,SACmB;AAGnB,QAAO;EACL,MAAM,CAAC,GAAG,cAAc;EACxB,WAAW;EACX,SAAS;EACT,YAAY,cAAc;EAC3B;;;;;;;;;;;;;;AAeH,SAAgB,WAEd,YACkB;AAGlB,QAAO;EACL,MAAM;EACN,WAAW;EACX,SAAS;EACV;;;;AC/DH,MAAa,wBAGT;CACF,qBACE,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAAmB,MAAM,MAAM,EAAE,4BAA4B;CACtE,uBACE,OAAO,kCAAqB,MAAM,MAAM,EAAE,8BAA8B;CAC1E,sBACE,OAAO,iCAAA,MAAA,MAAA,EAAA,EAAA,CAAoB,MAAM,MAAM,EAAE,6BAA6B;CACxE,oBACE,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAAkB,MAAM,MAAM,EAAE,2BAA2B;CACpE,2BACE,OAAO,sCAAA,MAAA,MAAA,EAAA,EAAA,CAAyB,MAC7B,MAAM,EAAE,kCACV;CACH,uBACE,OAAO,kCAAA,MAAA,MAAA,EAAA,EAAA,CAAqB,MAAM,MAAM,EAAE,8BAA8B;CAC1E,sBACE,OAAO,iCAAoB,MAAM,MAAM,EAAE,6BAA6B;CACxE,oBACE,OAAO,+BAAA,MAAA,MAAA,EAAA,EAAA,CAAkB,MAAM,MAAM,EAAE,2BAA2B;CACpE,wBACE,OAAO,mCAAsB,MAAM,MAAM,EAAE,+BAA+B;CAC5E,kBACE,OAAO,6BAAA,MAAA,MAAA,EAAA,EAAA,CAAgB,MAAM,MAAM,EAAE,yBAAyB;CAChE,qBACE,OAAO,gCAAA,MAAA,MAAA,EAAA,EAAA,CAAmB,MAAM,MAAM,EAAE,4BAA4B;CACtE,yBACE,OAAO,oCAAA,MAAA,MAAA,EAAA,EAAA,CAAuB,MAC3B,MAAM,EAAE,gCACV;CACJ;;;;AASD,MAAa,gBAAgB;CAC3B,SAAS;CACT,WAAW;CACX,UAAU;CACV,QAAQ;CACR,eAAe;CACf,WAAW;CACX,UAAU;CACV,SAAS;CACT,YAAY;CACZ,MAAM;CACN,SAAS;CACT,cAAc;CACf;;;;;AAMD,SAAS,4BAAkC;AAEzC,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAW;GAAO;EACpC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAa;GAAQ;GAAiB;GAAgB;EAC7D,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAY;GAAU;GAAe;EAC5C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAU;GAAY;GAAQ;EACrC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAiB;GAAa;GAAW;EAChD,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAa;GAAU;GAAW;EACzC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAQ;GAAS;GAAY;GAAW;EAC/C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAY;GAAW;GAAW;EACzC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAW;GAAU;GAAW;EAClD,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAW;GAAO;GAAW;EACpC,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aAAa;EACb,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAgB;GAAU;GAAU;EAC3C,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;AAGF,sBAAqB,SAAS;EAC5B,IAAI,cAAc;EAClB,MAAM;EACN,MAAM;EACN,aACE;EACF,UAAU,gBAAgB;EAC1B,MAAM;GAAC;GAAc;GAAa;GAAU;GAAS;GAAW;EAChE,SAAS;EACT,QAAQ;EACR,gBAAgB,CACd;GACE,MAAM;GACN,IAAI;GACJ,OAAO,EAAE;GACV,CACF;EACD,cAAc,EAAE;EACjB,CAAC;;AAIJ,2BAA2B;;;;;;;;ACvU3B,SAAgB,oBACd,WAEoC;AACpC,KAAI,UAAU,WAAW,EAAG,QAAO;CAEnC,MAAM,gBAAgB,OAAO,YAC3B,UAAU,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAC5C;AAED,QAAO;EAAE,GAAG;EAA6B,GAAG;EAAe;;;;;;;;;ACC7D,MAAa,UAET,WAA4C,SAAS,QACvD,EAAE,IAAI,SAAS,UAAU,GAAG,QAC5B,KACA;CACA,MAAM,EAAE,UAAU,cAAc,kBAAkB;CAElD,MAAM,eAAe,MAAqC;AAExD,YAAU,EAAE;AACZ,MAAI,EAAE,iBAAkB;AAGxB,MAAI,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,OAAQ;AACtD,MAAI,EAAE,WAAW,EAAG;AACpB,MAAI,KAAK,UAAU,KAAK,WAAW,QAAS;AAE5C,IAAE,gBAAgB;AAClB,WAAS,GAAG;;AAGd,QACE,oBAAC,KAAD;EAAQ;EAAK,MAAM,UAAU,GAAG;EAAE,SAAS;EAAa,GAAI;EACzD;EACC,CAAA;EAEN;;;;;;;;;ACtBF,MAAa,oBAAoB;CAC/B,SAAS;CACT,OAAO;EACL,gBAAgB,EAAE,UAAU,SAAS;EACrC,iBAAiB,EAAE,UAAU,SAAS;EACtC,cAAc,EAAE,UAAU,kBAAkB;EAC5C,iBAAiB,EAAE,UAAU,kBAAkB;EAC/C,kBAAkB,EAAE,UAAU,kBAAkB;EAChD,kBAAkB,EAAE,UAAU,kBAAkB;EACjD;CAED,gBAAgB;EACd;EACA;EACA;EACD;CACF"}
|