@bitforgehq/ui 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"bitforgehq-ui-mobile.mjs","names":[],"sources":["../../../projects/ui/mobile/shell/shell-config.ts","../../../projects/ui/mobile/platform/platform.service.ts","../../../projects/ui/mobile/platform/back-button.service.ts","../../../projects/ui/mobile/platform/haptics.service.ts","../../../projects/ui/mobile/platform/text-scale.service.ts","../../../projects/ui/mobile/platform/keyboard.service.ts","../../../projects/ui/mobile/navigation/stack-reuse-strategy.ts","../../../projects/ui/mobile/shell/provide-bf-shell.ts","../../../projects/ui/mobile/navigation/view-lifecycle.ts","../../../projects/ui/mobile/navigation/nav-controller.service.ts","../../../projects/ui/mobile/motion.ts","../../../projects/ui/mobile/navigation/transitions.ts","../../../projects/ui/mobile/navigation/page/page-context.ts","../../../projects/ui/mobile/navigation/tabs/tab-context.ts","../../../projects/ui/mobile/navigation/stack-outlet/stack-outlet.component.ts","../../../projects/ui/mobile/navigation/page/page.component.ts","../../../projects/ui/mobile/navigation/back-button/back-button.component.ts","../../../projects/ui/mobile/navigation/horizontal-scroll/horizontal-scroll.directive.ts","../../../projects/ui/mobile/content/keyboard-assist/keyboard-assist.directive.ts","../../../projects/ui/mobile/content/overscroll-stretch/overscroll-stretch.directive.ts","../../../projects/ui/mobile/content/content/content.component.ts","../../../projects/ui/mobile/content/header/header.component.ts","../../../projects/ui/mobile/content/refresher/refresher.component.ts","../../../projects/ui/mobile/navigation/tabs/tabs.component.ts","../../../projects/ui/mobile/overlays/deprecated-core.ts","../../../projects/ui/mobile/overlays/sheet/sheet-ref.ts","../../../projects/ui/mobile/overlays/keyboard-cover.ts","../../../projects/ui/mobile/overlays/sheet/sheet.component.ts","../../../projects/ui/mobile/overlays/sheet/sheet.service.ts","../../../projects/ui/mobile/overlays/modal/modal.component.ts","../../../projects/ui/mobile/overlays/modal/modal.service.ts","../../../projects/ui/mobile/overlays/toast/toast.component.ts","../../../projects/ui/mobile/overlays/toast/toast.service.ts","../../../projects/ui/mobile/overlays/action-sheet/action-sheet.component.ts","../../../projects/ui/mobile/overlays/action-sheet/action-sheet.service.ts","../../../projects/ui/mobile/overlays/alert-dialog/alert-dialog.component.ts","../../../projects/ui/mobile/overlays/alert-dialog/alert-dialog.service.ts","../../../projects/ui/mobile/testing/provide-bf-shell-testing.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\n/**\n * The shell's transition and gesture style: `ios`, `md` (Material), or `auto` (iOS on iOS, else\n * `md`).\n *\n * @summary The shell style setting, iOS, Material or automatic from the platform\n * @since 0.3.0\n */\nexport type BfShellConfigMode = 'ios' | 'md' | 'auto';\n\n/**\n * The cap of iOS Dynamic Type: the largest root font scale the shell applies.\n *\n * @summary The largest root font scale iOS Dynamic Type may apply in the shell\n * @since 0.3.0\n */\nexport interface BfShellDynamicTypeLimit {\n /** The largest root font scale (2.35, the AX3 size, by default); `Infinity` removes the cap. */\n maxScale: number;\n}\n\n/**\n * The shell's iOS Dynamic Type setting: `false` opts out, a `BfShellDynamicTypeLimit` caps it.\n *\n * @summary Whether the shell follows iOS Dynamic Type, and up to which scale\n * @since 0.3.0\n */\nexport type BfShellDynamicType = false | BfShellDynamicTypeLimit;\n\n/**\n * What the Android back button does at the root of every stack, when no overlay or page claims\n * it: `minimize` moves the app to the background (`App.minimizeApp()`), so a relaunch resumes it\n * warm; `exit` finishes the activity (`App.exitApp()`), so the next launch starts cold.\n *\n * @summary What Android back does at a stack root, background the app or exit it\n * @since 0.3.0\n */\nexport type BfBackAtRoot = 'minimize' | 'exit';\n\n/**\n * Configuration of the mobile shell, set once through `provideBfShell()`.\n *\n * @summary The mobile shell settings: mode, swipe-back, haptics, Dynamic Type and system bars\n * @since 0.2.0\n */\nexport interface BfShellConfig {\n /**\n * Transition and gesture style: `ios`, `md` (Material), or `auto` (iOS on iOS, else `md`).\n * Overscroll is not part of it: `bf-content`'s iOS bounce and Android stretch and\n * `bf-refresher`'s iOS bounce mode follow the engine (`BfPlatformService.mode()`), because the\n * bounce is WebKit's own and Chrome never reports a negative `scrollTop`.\n * @default 'auto'\n */\n mode: BfShellConfigMode;\n /**\n * Enables the iOS edge swipe-back in every stack outlet (iOS mode only).\n * @default true\n */\n swipeBack: boolean;\n /**\n * Holds a transition at frame 0 for two animation frames while the entering page renders.\n * @default true\n */\n deferTransitionStart: boolean;\n /**\n * Plays a light haptic tick on tab changes (native only).\n * @default true\n */\n hapticsOnTabChange: boolean;\n /**\n * iOS Dynamic Type (native iOS only; see `BfTextScaleService`): the root font size follows the\n * user's text size. `false` opts out; `maxScale` caps the root scale (default 2.35, the AX3\n * size, at least 200 %). `Infinity` removes the cap entirely; a non-finite value other than\n * `Infinity` (e.g. `NaN`) falls back to the default cap. Left out, the default applies. Android\n * needs no setting: its WebView applies the system font scale.\n * @default { maxScale: 2.35 }\n */\n dynamicType?: BfShellDynamicType;\n /**\n * Native iOS and Android only: the status-bar and navigation-bar icons follow the in-app theme\n * (`BfPlatformService.dark()`) through Capacitor's `SystemBars.setStyle()`, on start and on every\n * theme change (see `bfSystemBarsStyle()`). `false` leaves the system bars to the app.\n * @default true\n */\n syncSystemBars?: boolean;\n /**\n * Native Android only: what the back button does when no overlay and no stack can go back\n * (every stack is at its first page). `minimize` (default) moves the app to the background, as\n * Android does for a root activity, so a relaunch resumes it warm on the same tab; `exit`\n * finishes the activity, as 0.2 did, so the next launch starts cold.\n * @default 'minimize'\n */\n backAtRoot?: BfBackAtRoot;\n /**\n * How many pages of one stack stay alive in each `bf-stack-outlet` (default `Infinity`: every\n * page). Above it, the deepest pages are destroyed and kept as a record (route, URL, history\n * position and the content's scroll position); a pop back to one creates the page again, fires\n * its lifecycle and restores its scroll. Deep pages come back fresh: their component state and\n * the stacks of outlets nested in them are gone. Values below 1 count as 1.\n * @default Infinity\n */\n maxLivePages?: number;\n}\n\n/** The default Dynamic Type cap: the AX3 size (40 / 17 px), at least 200 %. Internal. */\nexport const DEFAULT_MAX_TEXT_SCALE = 2.35;\n\n/**\n * The shell defaults, merged under the overrides given to `provideBfShell()`.\n *\n * @summary The mobile shell defaults that provideBfShell merges the app settings over\n * @since 0.2.0\n */\nexport const BF_SHELL_DEFAULTS: Readonly<BfShellConfig> = {\n mode: 'auto',\n swipeBack: true,\n deferTransitionStart: true,\n hapticsOnTabChange: true,\n dynamicType: { maxScale: DEFAULT_MAX_TEXT_SCALE },\n syncSystemBars: true,\n backAtRoot: 'minimize',\n maxLivePages: Infinity,\n};\n\n/**\n * The resolved shell configuration.\n *\n * @summary Injection token holding the resolved mobile shell configuration of the app\n * @since 0.2.0\n */\nexport const BF_SHELL_CONFIG = new InjectionToken<BfShellConfig>('BF_SHELL_CONFIG');\n","import { DOCUMENT, Injectable, computed, effect, inject } from '@angular/core';\nimport { Capacitor, SystemBars, SystemBarsStyle } from '@capacitor/core';\nimport {\n BF_COLOR_SCHEME_STORAGE,\n BfColorSchemeService,\n type BfColorScheme,\n ɵcreateMemoryColorSchemeStorage,\n} from '@bitforgehq/ui';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, type BfShellConfig } from '../shell/shell-config';\n\n/**\n * The platform the app runs on: native iOS, native Android, or a browser.\n *\n * @summary The platform the app runs on, native iOS, native Android or the web\n * @since 0.2.0\n */\nexport type BfPlatform = 'ios' | 'android' | 'web';\n/**\n * The shell's look and motion: `ios`, or `md` (Material) everywhere else.\n *\n * @summary The shell's look and motion conventions, iOS or Material Design\n * @since 0.2.0\n */\nexport type BfShellMode = 'ios' | 'md';\n/**\n * The app theme choice: follow the system, or force light or dark. The same type as\n * `BfColorScheme` from `@bitforgehq/ui`.\n *\n * @summary The app theme choice, following the system or forcing light or dark\n * @since 0.2.0\n */\nexport type BfTheme = BfColorScheme;\n/** The shell's viewport: edge to edge, and no zoom. */\nconst VIEWPORT_CONTENT =\n 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover';\n/** Viewport directives that forbid zoom, with the value each must have. */\nconst NO_ZOOM_DIRECTIVES: readonly (readonly [string, string])[] = [\n ['maximum-scale', '1'],\n ['user-scalable', 'no'],\n];\n\nfunction toPlatform(platform: string): BfPlatform {\n return platform === 'ios' || platform === 'android' ? platform : 'web';\n}\n\n/** A theme store in memory, gone with the app: where `localStorage` is unavailable, and in tests. Internal. */\nexport const createMemoryThemeStorage = ɵcreateMemoryColorSchemeStorage;\n\n/**\n * Where `BfPlatformService` keeps the theme choice (the key `bf-theme`): `localStorage` when it is\n * available, otherwise an in-memory map. Provide another store (a native preferences plugin, or an\n * in-memory one in tests, as `provideBfShellTesting()` does) to keep the choice elsewhere. It is\n * the same token as `BF_COLOR_SCHEME_STORAGE` from `@bitforgehq/ui`, which `BfColorSchemeService`\n * reads, so providing either one moves the choice for both.\n *\n * @example\n * providers: [{ provide: BF_THEME_STORAGE, useValue: sessionStorage }]\n * @summary Injection token for the store that keeps the app theme choice between visits\n * @since 0.3.0\n */\nexport const BF_THEME_STORAGE = BF_COLOR_SCHEME_STORAGE;\n\n/**\n * Makes the viewport meta forbid zoom: sets `maximum-scale=1` and `user-scalable=no`, keeping the\n * other directives, or creates the meta with the full shell viewport when there is none. A meta\n * that already forbids zoom is left untouched.\n */\nfunction forbidViewportZoom(document: Document): void {\n const meta = document.head.querySelector<HTMLMetaElement>('meta[name=\"viewport\"]');\n if (!meta) {\n const created = document.createElement('meta');\n created.name = 'viewport';\n created.content = VIEWPORT_CONTENT;\n document.head.appendChild(created);\n return;\n }\n const directives = meta.content\n .split(',')\n .map((directive) => directive.trim())\n .filter((directive) => directive.length > 0);\n let changed = false;\n for (const [key, value] of NO_ZOOM_DIRECTIVES) {\n const wanted = `${key}=${value}`;\n const index = directives.findIndex(\n (directive) => directive.split('=')[0].trim().toLowerCase() === key,\n );\n if (index === -1) {\n directives.push(wanted);\n changed = true;\n } else if (directives[index].replace(/\\s/g, '').toLowerCase() !== wanted) {\n directives[index] = wanted;\n changed = true;\n }\n }\n if (changed) {\n meta.content = directives.join(', ');\n }\n}\n\n/** The first Android WebView whose system bars draw over the page (edge to edge). */\nconst EDGE_TO_EDGE_WEBVIEW = 140;\n\n/**\n * The SystemBars style for a theme: Dark = light icons. Android WebView < 140 pads the WebView, so\n * the bar areas show the activity's window background (which follows the system theme): use\n * Default there.\n *\n * The WebView ≥ 140 case assumes the shell's setup: `viewport-fit=cover` and Capacitor's default\n * `SystemBars.insetsHandling: 'css'`, so the page draws under the bars. With `insetsHandling`\n * `'disable'` or `'native'` the page never reaches under them and the bars show the window\n * background as on older WebViews; pass `syncSystemBars: false` and style them yourself there.\n * `webViewMajor` is read from `Chrome/<n>` in the user agent (Capacitor reads the WebView\n * package, which normally agrees).\n *\n * @example\n * await SystemBars.setStyle({ style: bfSystemBarsStyle(true, 'ios', null) });\n * @param dark Whether the app is dark now (`BfPlatformService.dark()`).\n * @param platform The platform the app runs on.\n * @param webViewMajor The Android WebView's major version (`Chrome/<n>` in the user agent), or\n * `null` when unknown.\n * @returns The `SystemBarsStyle` to pass to `SystemBars.setStyle()`.\n * @summary The system-bar icon style that keeps the bars readable in the in-app theme\n * @since 0.3.0\n */\nexport function bfSystemBarsStyle(\n dark: boolean,\n platform: BfPlatform,\n webViewMajor: number | null,\n): SystemBarsStyle {\n if (platform === 'android' && (webViewMajor ?? 0) < EDGE_TO_EDGE_WEBVIEW) {\n return SystemBarsStyle.Default;\n }\n return dark ? SystemBarsStyle.Dark : SystemBarsStyle.Light;\n}\n\n/** The WebView's Chromium major version from its user agent (`Chrome/134.…`), or `null`. */\nfunction webViewMajor(navigator: Navigator | undefined): number | null {\n const match = /Chrome\\/(\\d+)/.exec(navigator?.userAgent ?? '');\n return match ? Number(match[1]) : null;\n}\n\n/**\n * Platform facts and the app theme: native or web, iOS or Android, the shell mode that follows\n * from it, and the user's theme choice (system, light, dark) reflected as the `dark` class on\n * `<html>`. The theme is `BfColorSchemeService` from `@bitforgehq/ui` (the Capacitor-free colour\n * scheme a desktop app uses through `provideBfColorScheme()`): `theme` and `dark` are its\n * `scheme` and `dark` signals, and `matchMedia('(prefers-color-scheme: dark)')` is the single\n * source for the system theme on every platform.\n *\n * The `theme`/`dark` signals always compute, but the `<html>` class and storage writes\n * only run when `provideBfShell()` provided `BF_SHELL_CONFIG` (or the app provided\n * `provideBfColorScheme()`) — i.e. this service is the app's own shell, not merely injected by\n * something that wants `platform`/`mode`. Without it (e.g. a docs demo that instantiates\n * `BfPlatformService` directly to preview `bf-tabs`), this service would otherwise fight the host\n * page's own theme, overwriting `<html class=\"dark\">` with whatever `prefers-color-scheme` says.\n *\n * With `provideBfShell()` on a native platform it also makes the viewport meta forbid zoom\n * (`maximum-scale=1, user-scalable=no`, other directives kept; the meta is created if missing):\n * WKWebView zooms a focused control whose font is under 16 px and never zooms back, so the native\n * shell forbids zoom — pinch, double tap and focus alike — and honours the system text size\n * instead (iOS Dynamic Type through `BfTextScaleService`, Android's font scale in the WebView).\n * On the web the meta is never touched, so a web build keeps pinch zoom (the owner's ruling,\n * audit §12): ship `index.html` without `maximum-scale`/`user-scalable`.\n *\n * With `provideBfShell()` on native iOS and Android (unless `syncSystemBars: false`) the status-bar\n * and navigation-bar icons follow `dark()`: `SystemBars.setStyle()` with `bfSystemBarsStyle()`,\n * on start and on every theme change (Android WebView < 140 keeps `Default`, see there).\n * @summary Platform facts and the app theme: native or web, iOS or Android, light or dark\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfPlatformService {\n private readonly document = inject(DOCUMENT);\n private readonly shellConfig = inject(BF_SHELL_CONFIG, { optional: true });\n private readonly colorScheme = inject(BfColorSchemeService);\n /** True inside the native iOS or Android app, false in a browser. */\n readonly isNative = Capacitor.isNativePlatform();\n /** The platform the app runs on. */\n readonly platform: BfPlatform = toPlatform(Capacitor.getPlatform());\n /** The platform's shell mode: `ios` on iOS, else `md`; `resolveShellMode()` applies the config. */\n readonly mode = computed<BfShellMode>(() => (this.platform === 'ios' ? 'ios' : 'md'));\n // `model()` is compiler-restricted to @Component/@Directive classes (NG8110); a plain writable\n // `signal()` gives callers the same `theme()` / `theme.set(...)` surface in a service.\n /** The user's theme choice, stored in `BF_THEME_STORAGE` (`localStorage`); set it with\n * `theme.set(…)`. The same signal as `BfColorSchemeService.scheme`. */\n readonly theme = this.colorScheme.scheme;\n private readonly systemDark = this.colorScheme.systemDark;\n /** Whether the app is dark now: the theme choice, or the system's under `system`. The same\n * signal as `BfColorSchemeService.dark`. */\n readonly dark = this.colorScheme.dark;\n\n constructor() {\n if (this.shellConfig === null) {\n return;\n }\n if (this.isNative) {\n forbidViewportZoom(this.document);\n }\n // The shell owns <html>: the colour scheme applies the class (and the stored theme)\n // synchronously, before any change detection runs, and keeps later changes in sync.\n this.colorScheme.claimDocument();\n if (this.isNative && this.shellConfig.syncSystemBars !== false) {\n const major = webViewMajor(this.document.defaultView?.navigator);\n effect(() => {\n const style = bfSystemBarsStyle(this.dark(), this.platform, major);\n // `Default` is resolved natively when it is set (Capacitor keeps the resolved style across\n // a configuration change), so it is sent again whenever the system theme changes.\n this.systemDark();\n void this.applySystemBars(style);\n });\n }\n }\n\n /** The native system bars are outside the page; a failed call leaves them as they were. */\n private async applySystemBars(style: SystemBarsStyle): Promise<void> {\n try {\n // effect-justified: the native system bars are external to any view\n await SystemBars.setStyle({ style });\n } catch (error) {\n console.error(error);\n }\n }\n}\n\n/**\n * The shell mode in effect: `config.mode`, or the platform's (`BfPlatformService.mode()`: iOS on\n * iOS, else Material) when it is `'auto'`. The one resolution every mode-dependent part of the\n * shell (stack outlet, modal, header, back button) shares; apps use it (or `injectShellMode()`)\n * to follow the same per-mode conventions, e.g. text vs icon header actions.\n *\n * @example\n * const mode = resolveShellMode(inject(BF_SHELL_CONFIG), inject(BfPlatformService));\n * @param config The shell configuration, usually `inject(BF_SHELL_CONFIG)`.\n * @param platform The platform service.\n * @returns The mode in effect.\n * @summary Resolves the shell mode in effect from the configuration and the platform\n * @since 0.2.0\n */\nexport function resolveShellMode(\n config: Readonly<BfShellConfig>,\n platform: BfPlatformService,\n): BfShellMode {\n return config.mode === 'auto' ? platform.mode() : config.mode;\n}\n\n/**\n * `resolveShellMode()` from the injector: `BF_SHELL_CONFIG` (the defaults, i.e. `'auto'`, without\n * `provideBfShell()`) and `BfPlatformService`. Call it in an injection context (a field\n * initializer or constructor); the mode is fixed for the app's lifetime.\n *\n * @example\n * protected readonly ios = injectShellMode() === 'ios';\n * @returns The mode in effect.\n * @summary Reads the shell mode in effect from the injector, iOS or Material\n * @since 0.2.0\n */\nexport function injectShellMode(): BfShellMode {\n const config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n return resolveShellMode(config, inject(BfPlatformService));\n}\n","import { DestroyRef, Injectable, inject } from '@angular/core';\nimport { BF_SHELL_CONFIG } from '../shell/shell-config';\nimport { BfPlatformService } from './platform.service';\n\ninterface BackButtonHandler {\n priority: number;\n /** Registration order; on equal priority the newest registration runs first. */\n sequence: number;\n run: () => boolean | Promise<boolean>;\n}\n\n/**\n * The hardware back button queue: on native Android it listens to `@capacitor/app`'s\n * `backButton` event; on iOS and web nothing is registered (there is no hardware button to\n * listen to), but `register` and `dispatch` still work so overlays and tests can drive the same\n * queue. `register` adds a handler at a priority (an open overlay registers at 100,\n * `BfNavController` at 0 — it pops the deepest active `bf-stack-outlet`); `dispatch` runs the\n * queue from the highest priority down until one handler returns true; on equal priority the\n * newest registration runs first, so stacked overlays close top-down. When none does (every\n * stack is at its first page), the app moves to the background (`App.minimizeApp()`), so a\n * relaunch resumes it warm; `provideBfShell({ backAtRoot: 'exit' })` finishes it instead\n * (`App.exitApp()`).\n *\n * @example\n * const unregister = backButton.register(100, () => {\n * closeTopOverlay();\n * return true;\n * });\n * @summary The Android hardware back button queue that overlays and navigation stacks share\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfBackButtonService {\n private readonly platform = inject(BfPlatformService);\n private readonly destroyRef = inject(DestroyRef);\n private readonly backAtRoot =\n inject(BF_SHELL_CONFIG, { optional: true })?.backAtRoot ?? 'minimize';\n private readonly handlers: BackButtonHandler[] = [];\n private sequence = 0;\n\n constructor() {\n if (this.platform.isNative && this.platform.platform === 'android') {\n void this.attachNativeListener();\n }\n }\n\n /**\n * Adds a handler at `priority` (higher priority runs first; on a tie, the handler registered\n * last runs first). Returns a function that removes\n * it again; call it when the registrant (an overlay, an outlet) is torn down.\n * @param priority Higher runs first: an open overlay uses 100, `BfNavController` 0.\n * @param handler Returns (or resolves) true when it handled the press.\n */\n register(priority: number, handler: () => boolean | Promise<boolean>): () => void {\n const entry: BackButtonHandler = { priority, sequence: this.sequence++, run: handler };\n this.handlers.push(entry);\n return () => {\n const index = this.handlers.indexOf(entry);\n if (index >= 0) {\n this.handlers.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs the registered handlers from highest to lowest priority until one claims the event\n * (returns true); when none does, minimizes the app (or exits it with `backAtRoot: 'exit'`).\n * Public so tests and the native listener share one code path, and so overlays outside the\n * native listener's reach (phase 2c) can still be driven by it, e.g. `Escape`.\n */\n async dispatch(): Promise<void> {\n const ordered = [...this.handlers].sort(\n (a, b) => b.priority - a.priority || b.sequence - a.sequence,\n );\n for (const handler of ordered) {\n if (await handler.run()) {\n return;\n }\n }\n if (this.backAtRoot === 'exit') {\n await this.exitApp();\n } else {\n await this.minimizeApp();\n }\n }\n\n private async attachNativeListener(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n const listener = await App.addListener('backButton', () => {\n void this.dispatch();\n });\n this.destroyRef.onDestroy(() => void listener.remove());\n } catch (error) {\n console.error('BfBackButtonService: failed to attach the native back button listener', error);\n }\n }\n\n private async minimizeApp(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n await App.minimizeApp();\n } catch (error) {\n console.error('BfBackButtonService: failed to minimize the app', error);\n }\n }\n\n private async exitApp(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n await App.exitApp();\n } catch (error) {\n console.error('BfBackButtonService: failed to exit the app', error);\n }\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport type { HapticsPlugin, ImpactStyle, NotificationType } from '@capacitor/haptics';\nimport { BfPlatformService } from './platform.service';\n\n/**\n * Impact weight, mapped to `ImpactStyle.Light` / `Medium` / `Heavy` on native.\n *\n * @summary The weight of an impact haptic, light, medium or heavy\n * @since 0.2.0\n */\nexport type BfHapticsImpactStyle = 'light' | 'medium' | 'heavy';\n/**\n * Outcome of a task, mapped to `NotificationType.Success` / `Warning` / `Error` on native.\n *\n * @summary The outcome a notification haptic reports: success, warning or error\n * @since 0.2.0\n */\nexport type BfHapticsNotificationType = 'success' | 'warning' | 'error';\n\n/** The shape of `@capacitor/haptics`'s module namespace, cached after the first lazy import. */\ninterface HapticsModule {\n Haptics: HapticsPlugin;\n ImpactStyle: typeof ImpactStyle;\n NotificationType: typeof NotificationType;\n}\n\n/**\n * Haptic feedback: `impact`, `selection` and `notification` forward to `@capacitor/haptics` on\n * native (loaded lazily on first use, the import cached like `BfKeyboardService`'s plugin); on\n * the web, and when the plugin fails to load (an app that didn't add the optional dependency),\n * every method resolves as a silent no-op.\n *\n * @example\n * const haptics = inject(BfHapticsService);\n * await haptics.impact('light');\n * @since 0.2.0\n * @summary Haptic feedback on native devices that silently does nothing on the web\n */\n@Injectable({ providedIn: 'root' })\nexport class BfHapticsService {\n private readonly platformService = inject(BfPlatformService);\n private modulePromise: Promise<HapticsModule | null> | null = null;\n\n /**\n * Impact feedback: a collision between UI elements of the given weight.\n * @param style The weight of the impact.\n */\n async impact(style: BfHapticsImpactStyle): Promise<void> {\n await this.run(async ({ Haptics, ImpactStyle: Style }) => {\n const map: Record<BfHapticsImpactStyle, ImpactStyle> = {\n light: Style.Light,\n medium: Style.Medium,\n heavy: Style.Heavy,\n };\n await Haptics.impact({ style: map[style] });\n });\n }\n\n /** A light tick for a discrete selection change (scrubbing a picker, crossing a threshold). */\n async selection(): Promise<void> {\n await this.run(({ Haptics }) => Haptics.selectionChanged());\n }\n\n /**\n * Feedback for the outcome of a task.\n * @param type The outcome of the task.\n */\n async notification(type: BfHapticsNotificationType): Promise<void> {\n await this.run(async ({ Haptics, NotificationType: Type }) => {\n const map: Record<BfHapticsNotificationType, NotificationType> = {\n success: Type.Success,\n warning: Type.Warning,\n error: Type.Error,\n };\n await Haptics.notification({ type: map[type] });\n });\n }\n\n private async run(fn: (module: HapticsModule) => Promise<void>): Promise<void> {\n if (!this.platformService.isNative) {\n return;\n }\n const plugin = await this.loadPlugin();\n if (!plugin) {\n return;\n }\n try {\n await fn(plugin);\n } catch {\n // The native call failed (no native runtime behind the plugin); haptics are best-effort.\n }\n }\n\n /**\n * Resolves the plugin, or `null` when it is not installed. The `import()` stays lexically inside\n * the `try` so esbuild tolerates an unresolved optional peer at build time (as in\n * `BfBackButtonService` and `BfKeyboardService`); a bare `import()` fails the consumer's build.\n */\n private loadPlugin(): Promise<HapticsModule | null> {\n if (!this.modulePromise) {\n try {\n this.modulePromise = import('@capacitor/haptics').catch(() => null);\n } catch {\n this.modulePromise = Promise.resolve(null);\n }\n }\n return this.modulePromise;\n }\n}\n","import { DOCUMENT, DestroyRef, Injectable, inject, signal } from '@angular/core';\nimport { BF_SHELL_CONFIG, DEFAULT_MAX_TEXT_SCALE } from '../shell/shell-config';\nimport { BfPlatformService } from './platform.service';\n\n/** The body text size of iOS's default Dynamic Type setting (\"Large\"), in px. */\nconst DEFAULT_BODY_PX = 17;\nconst PROBE_ATTRIBUTE = 'data-bf-text-scale-probe';\n\n/**\n * Scales the root font size with iOS Dynamic Type inside the native shell. WKWebView resolves the\n * system text style `font: -apple-system-body` to the user's text size (17 px at the default\n * \"Large\", 21 px at xxL, 40 px at AX3, 53 px at AX5); a hidden, persistent probe element with that\n * font gives the size, and `<html>` gets `font-size: probe / 17 × 100 %` (rounded to 0.1 %). Every\n * type token and spacing step of the library is in `rem`, so text and rem-based layout grow\n * together, as UIKit's Dynamic Type does.\n *\n * A `ResizeObserver` on the probe re-applies the scale when the setting changes while the app\n * runs (WebKit re-resolves system fonts then); `visibilitychange` re-probes as the fallback. The\n * scale never goes below 1 (the smaller iOS sizes, and a Mac running the iOS app, keep the design\n * size) and is capped at `BfShellConfig.dynamicType.maxScale` (default 2.35; `Infinity` means no\n * cap; `NaN` falls back to the default). A missing or non-finite probe value gives 1; at 1 the\n * root font size is left unset.\n *\n * Acts only with `provideBfShell()` (which starts it at bootstrap), on native iOS, and while\n * `dynamicType` is not `false`. Android needs nothing: its WebView applies the system font scale\n * (`textZoom`) itself, so probing there would scale twice. On the web the browser's own text\n * size and zoom apply. Destroying the service removes the probe, its listeners and the root font\n * size.\n *\n * @example\n * const textScale = inject(BfTextScaleService);\n * const compact = computed(() => textScale.scale() > 1.5);\n * @since 0.2.0\n * @summary Follows iOS Dynamic Type by scaling the root font size of the app\n */\n@Injectable({ providedIn: 'root' })\nexport class BfTextScaleService {\n private readonly document = inject(DOCUMENT);\n private readonly platformService = inject(BfPlatformService);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n private readonly scaleState = signal(1);\n\n /** Current root scale (1 = the system default \"Large\"); always 1 on Android and the web. */\n readonly scale = this.scaleState.asReadonly();\n\n constructor() {\n const view = this.document.defaultView;\n const dynamicType = this.config\n ? (this.config.dynamicType ?? { maxScale: DEFAULT_MAX_TEXT_SCALE })\n : false;\n if (\n !view ||\n !this.document.body ||\n dynamicType === false ||\n !this.platformService.isNative ||\n this.platformService.platform !== 'ios'\n ) {\n return;\n }\n // `Infinity` is a deliberate, finite-arithmetic way to ask for no cap at all; only `NaN` (not\n // a number the configured value could ever legitimately be) falls back to the default cap.\n const maxScale = Number.isNaN(dynamicType.maxScale)\n ? DEFAULT_MAX_TEXT_SCALE\n : Math.max(1, dynamicType.maxScale);\n const probe = this.createProbe();\n const apply = (): void => {\n const size = parseFloat(view.getComputedStyle(probe).fontSize);\n const ratio = Number.isFinite(size) ? size / DEFAULT_BODY_PX : 1;\n const percent = Math.round(Math.min(Math.max(ratio, 1), maxScale) * 1000) / 10;\n this.scaleState.set(percent / 100);\n const root = this.document.documentElement.style;\n if (percent === 100) {\n root.removeProperty('font-size');\n } else {\n root.fontSize = `${percent}%`;\n }\n };\n const onVisibilityChange = (): void => {\n if (this.document.visibilityState !== 'hidden') {\n apply();\n }\n };\n const observer =\n typeof view.ResizeObserver === 'function' ? new view.ResizeObserver(apply) : null;\n observer?.observe(probe);\n this.document.addEventListener('visibilitychange', onVisibilityChange);\n apply();\n this.destroyRef.onDestroy(() => {\n observer?.disconnect();\n this.document.removeEventListener('visibilitychange', onVisibilityChange);\n probe.remove();\n this.document.documentElement.style.removeProperty('font-size');\n });\n }\n\n private createProbe(): HTMLElement {\n const probe = this.document.createElement('span');\n probe.setAttribute(PROBE_ATTRIBUTE, '');\n probe.setAttribute('aria-hidden', 'true');\n probe.setAttribute(\n 'style',\n 'position: fixed; top: 0; left: -200px; font: -apple-system-body; white-space: nowrap; visibility: hidden; pointer-events: none',\n );\n probe.textContent = 'A';\n this.document.body.appendChild(probe);\n return probe;\n }\n}\n","import { DOCUMENT, DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { KeyboardPlugin } from '@capacitor/keyboard';\nimport { BfPlatformService } from './platform.service';\n\n/**\n * Keyboard lifecycle phase, for animations only; never gate logic on the order of the phases.\n * `showing` starts at `keyboardWillShow`, `shown` at `keyboardDidShow`. Either hide event\n * (`keyboardWillHide` or `keyboardDidHide`, whichever arrives first — iOS does not guarantee\n * the order) moves straight to `hidden`, so there is no `hiding` phase.\n *\n * @summary The soft keyboard phase, hidden, showing or shown, for animations only\n * @since 0.2.0\n */\nexport type BfKeyboardPhase = 'hidden' | 'showing' | 'shown';\n\n/** A covered height below this is browser chrome (URL bar, toolbar), not a keyboard. */\nconst WEB_KEYBOARD_THRESHOLD = 80;\n/** A `visualViewport.scale` further than this from 1 is pinch zoom: no keyboard is read from it. */\nconst ZOOM_TOLERANCE = 0.01;\nconst PROBE_ATTRIBUTE = 'data-bf-keyboard-probe';\n/** CSS variable on `<html>` carrying the keyboard height (`0px` while hidden). */\nconst HEIGHT_VARIABLE = '--bf-keyboard-height';\n/**\n * How long `imminent` waits for the keyboard after `focusin` before it gives up and clears: a\n * hardware keyboard (iPad Magic Keyboard, a simulator with the host keyboard) opens no soft\n * keyboard, so no keyboard event would ever clear it. A cold Android emulator has been seen to\n * send will-show about 1.1 s after focus.\n */\nconst IMMINENT_TIMEOUT_MS = 2500;\n\n/** `input` types that open no text keyboard. */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'color',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]);\n\n/**\n * True for an element that opens the soft keyboard when focused: a text-like `input` (not a\n * button, checkbox, color, file, hidden, image, radio, range, reset or submit), a `textarea`, or\n * a `[contenteditable]` element other than `contenteditable=\"false\"`.\n *\n * @param element The element to test, usually `document.activeElement`.\n * @returns Whether it opens the soft keyboard.\n * @summary Tells whether focusing an element opens the soft keyboard on a phone\n * @since 0.2.0\n */\nexport function isTextEntry(element: Element | null): element is HTMLElement {\n if (element instanceof HTMLTextAreaElement) {\n return true;\n }\n if (element instanceof HTMLInputElement) {\n return !NON_TEXT_INPUT_TYPES.has(element.type);\n }\n return (\n element instanceof HTMLElement &&\n (element.isContentEditable ||\n element.matches('[contenteditable]:not([contenteditable=\"false\"])'))\n );\n}\n\n/**\n * The soft keyboard as signals. On native it listens to the Capacitor Keyboard plugin\n * (`@capacitor/keyboard`, loaded lazily, height known at `keyboardWillShow`); on the web it falls\n * back to `visualViewport` resizes with an 80 px threshold (height known only once the viewport has\n * shrunk; ignored while the page is pinch-zoomed, `visualViewport.scale` ≠ 1); in Jest and SSR it\n * stays inert (height 0, never visible). The rest height (viewport with the keyboard hidden) comes\n * from a fixed `100lvh` probe element, never `innerHeight`, which sticks at a wrong value after a\n * field-to-field focus jump on iOS; it is measured at construction and on window resizes while the\n * keyboard is hidden and no text control is focused, never at will-show. `imminent` flags a focused\n * text control before any keyboard event, and `keyboardTop()` gives the keyboard's top edge in\n * viewport px. It also writes the height as `--bf-keyboard-height` on `<html>` (`0px` while hidden)\n * for CSS that follows the keyboard.\n *\n * @example\n * const keyboard = inject(BfKeyboardService);\n * const hideTabBar = computed(() => keyboard.visible());\n * @since 0.2.0\n * @summary The soft keyboard state as signals: visible, its height and its phase\n */\n@Injectable({ providedIn: 'root' })\nexport class BfKeyboardService {\n private readonly platformService = inject(BfPlatformService);\n private readonly document = inject(DOCUMENT);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly heightState = signal(0);\n private readonly phaseState = signal<BfKeyboardPhase>('hidden');\n private readonly restHeightState = signal(0);\n private readonly willShowState = signal(0);\n private readonly imminentState = signal(false);\n private imminentTarget: EventTarget | null = null;\n private imminentTimer: ReturnType<typeof setTimeout> | null = null;\n private plugin: KeyboardPlugin | null = null;\n private probe: HTMLElement | null = null;\n private destroyed = false;\n\n /** Keyboard height in CSS px (iOS: includes the home-indicator strip); 0 when hidden. */\n readonly height = this.heightState.asReadonly();\n /** Lifecycle phase for animations; see `BfKeyboardPhase`. */\n readonly phase = this.phaseState.asReadonly();\n /** True from will-show until either hide event: the moment to hide chrome such as the tab bar. */\n readonly visible = computed(() => this.phase() === 'showing' || this.phase() === 'shown');\n /** True when the keyboard overlays the WebView (native iOS, `resize: 'none'`); false when the WebView resizes. */\n readonly overlaysContent = computed(\n () => this.platformService.isNative && this.platformService.platform === 'ios',\n );\n /** Viewport height with the keyboard hidden, in CSS px, from a fixed `100lvh` probe. */\n readonly restHeight = this.restHeightState.asReadonly();\n /** Increments once per keyboard show (at will-show), for one-off reactions such as closing popovers. */\n readonly willShow = this.willShowState.asReadonly();\n /**\n * True from `focusin` on a text-entry control (see `isTextEntry`) until that control's\n * `focusout` or either keyboard hide event; never cleared by will-show. Hide chrome such as the\n * tab bar on `visible() || imminent()`: on Android the WebView shrinks before the plugin's\n * will-show, which lifts `position: fixed` elements above the keyboard; `focusin` is the only\n * earlier signal. When it turns true without the keyboard up, it also clears itself after\n * `IMMINENT_TIMEOUT_MS` (2500 ms) unless will-show or a visible keyboard arrives first, so a\n * hardware keyboard, which fires no keyboard event, does not keep the tab bar hidden.\n */\n readonly imminent = this.imminentState.asReadonly();\n\n constructor() {\n const view = this.document.defaultView;\n if (!view || !this.document.body) {\n return;\n }\n this.probe = this.createProbe();\n this.measureRestHeight();\n this.writeHeightVariable(0);\n // A resize while a text control is focused is the keyboard, not a new rest size.\n const onWindowResize = (): void => {\n if (this.phase() === 'hidden' && !this.imminent()) {\n this.measureRestHeight();\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n if (isTextEntry(event.target as Element | null)) {\n this.imminentTarget = event.target;\n if (!this.imminentState()) {\n this.imminentState.set(true);\n this.armImminentTimeout();\n }\n }\n };\n const onFocusOut = (event: FocusEvent): void => {\n if (event.target === this.imminentTarget) {\n this.clearImminent();\n }\n };\n view.addEventListener('resize', onWindowResize);\n this.document.addEventListener('focusin', onFocusIn, true);\n this.document.addEventListener('focusout', onFocusOut, true);\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.cancelImminentTimeout();\n view.removeEventListener('resize', onWindowResize);\n this.document.removeEventListener('focusin', onFocusIn, true);\n this.document.removeEventListener('focusout', onFocusOut, true);\n this.probe?.remove();\n this.document.documentElement.style.removeProperty(HEIGHT_VARIABLE);\n });\n\n if (this.platformService.isNative) {\n void this.attachPlugin();\n } else if (view.visualViewport) {\n this.attachVisualViewport(view.visualViewport);\n }\n }\n\n /** Hides the keyboard: `Keyboard.hide()` on native, blurs the focused element on the web. */\n async hide(): Promise<void> {\n if (this.platformService.isNative) {\n try {\n const plugin = this.plugin ?? (await import('@capacitor/keyboard')).Keyboard;\n await plugin.hide();\n } catch {\n // The plugin is an optional peer; without it there is nothing to hide.\n }\n return;\n }\n const active = this.document.activeElement;\n if (active instanceof HTMLElement) {\n active.blur();\n }\n }\n\n /**\n * The keyboard's top edge in viewport CSS px, read on demand. While the keyboard overlays the\n * WebView (native iOS) it is `restHeight() - height()`; where the viewport shrinks for it\n * (Android, the web) it is the bottom of `visualViewport` (else `innerHeight`). With the\n * keyboard hidden it is `restHeight()`.\n */\n keyboardTop(): number {\n if (!this.visible()) {\n return this.restHeight();\n }\n if (this.overlaysContent()) {\n return this.restHeight() - this.height();\n }\n const view = this.document.defaultView;\n const viewport = view?.visualViewport;\n if (viewport) {\n return viewport.height + viewport.offsetTop;\n }\n return view?.innerHeight ?? this.restHeight();\n }\n\n private createProbe(): HTMLElement {\n const existing = this.document.body.querySelector<HTMLElement>(`[${PROBE_ATTRIBUTE}]`);\n if (existing) {\n return existing;\n }\n const probe = this.document.createElement('div');\n probe.setAttribute(PROBE_ATTRIBUTE, '');\n probe.setAttribute('aria-hidden', 'true');\n probe.setAttribute(\n 'style',\n 'position: fixed; top: 0; left: -2px; width: 1px; height: 100lvh; visibility: hidden; pointer-events: none',\n );\n this.document.body.appendChild(probe);\n return probe;\n }\n\n private measureRestHeight(): void {\n if (this.probe) {\n this.restHeightState.set(Math.round(this.probe.offsetHeight));\n }\n }\n\n /** Never measures the rest height: on Android the WebView has already shrunk by will-show. */\n private onWillShow(height: number): void {\n this.cancelImminentTimeout();\n this.setHeight(height);\n this.phaseState.set('showing');\n this.willShowState.update((count) => count + 1);\n }\n\n private onDidShow(height: number): void {\n this.setHeight(height);\n this.phaseState.set('shown');\n }\n\n private onHidden(): void {\n this.setHeight(0);\n this.phaseState.set('hidden');\n }\n\n /** Either plugin hide event: the keyboard is gone even if the control keeps focus. */\n private onHideEvent(): void {\n this.clearImminent();\n this.onHidden();\n }\n\n private clearImminent(): void {\n this.cancelImminentTimeout();\n this.imminentTarget = null;\n this.imminentState.set(false);\n }\n\n /** Arms the hardware-keyboard fallback; a keyboard already up needs none. */\n private armImminentTimeout(): void {\n this.cancelImminentTimeout();\n if (this.visible()) {\n return;\n }\n this.imminentTimer = setTimeout(() => {\n this.imminentTimer = null;\n if (!this.visible()) {\n this.clearImminent();\n }\n }, IMMINENT_TIMEOUT_MS);\n }\n\n private cancelImminentTimeout(): void {\n if (this.imminentTimer !== null) {\n clearTimeout(this.imminentTimer);\n this.imminentTimer = null;\n }\n }\n\n private setHeight(height: number): void {\n this.heightState.set(height);\n this.writeHeightVariable(height);\n }\n\n private writeHeightVariable(height: number): void {\n this.document.documentElement.style.setProperty(HEIGHT_VARIABLE, `${height}px`);\n }\n\n private async attachPlugin(): Promise<void> {\n try {\n const { Keyboard } = await import('@capacitor/keyboard');\n this.plugin = Keyboard;\n const handles = await Promise.all([\n Keyboard.addListener('keyboardWillShow', (info) => this.onWillShow(info.keyboardHeight)),\n Keyboard.addListener('keyboardDidShow', (info) => this.onDidShow(info.keyboardHeight)),\n Keyboard.addListener('keyboardWillHide', () => this.onHideEvent()),\n Keyboard.addListener('keyboardDidHide', () => this.onHideEvent()),\n ]);\n const removeAll = (): void => {\n for (const handle of handles) {\n void handle.remove();\n }\n };\n if (this.destroyed) {\n removeAll();\n } else {\n this.destroyRef.onDestroy(removeAll);\n }\n } catch {\n // `@capacitor/keyboard` is an optional peer: without it the service stays inert.\n }\n }\n\n private attachVisualViewport(viewport: VisualViewport): void {\n const onResize = (): void => {\n // A pinch-zoomed visual viewport shrinks too; it is never a keyboard.\n const zoomed = Math.abs((viewport.scale ?? 1) - 1) > ZOOM_TOLERANCE;\n const covered = zoomed\n ? 0\n : Math.max(0, Math.round(this.restHeightState() - viewport.height - viewport.offsetTop));\n if (covered > WEB_KEYBOARD_THRESHOLD) {\n if (!this.visible()) {\n this.cancelImminentTimeout();\n this.willShowState.update((count) => count + 1);\n }\n this.setHeight(covered);\n this.phaseState.set('shown');\n } else if (this.visible()) {\n this.onHideEvent();\n } else {\n this.onHidden();\n }\n };\n viewport.addEventListener('resize', onResize);\n this.destroyRef.onDestroy(() => viewport.removeEventListener('resize', onResize));\n }\n}\n","import { Injectable } from '@angular/core';\nimport { type ActivatedRouteSnapshot, BaseRouteReuseStrategy } from '@angular/router';\n\n/**\n * The route reuse strategy the stack needs: the default one, except that a route whose *path\n * params* change (`/items/4` → `/items/5`) is a new page, not an in-place update. The default only\n * compares `routeConfig`, so the router would reuse the component and never call the outlet, and\n * the stack would no longer match the history. Query-param changes still reuse the page (filters,\n * tabs). Exported so an app can extend it for its own reuse rules.\n * @summary Route reuse strategy that treats a changed path parameter as a new stack page\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfStackReuseStrategy extends BaseRouteReuseStrategy {\n /**\n * Whether the router may reuse the current page for `future`: same route config and path params.\n * @param future The route being navigated to.\n * @param curr The route shown now.\n * @returns True to reuse the page in place.\n */\n override shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {\n if (future.routeConfig !== curr.routeConfig) {\n return false;\n }\n const a = future.params;\n const b = curr.params;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]);\n for (const key of keys) {\n if (a[key] !== b[key]) {\n return false;\n }\n }\n return true;\n }\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\nimport {\n RouteReuseStrategy,\n type RouterConfigurationFeature,\n withRouterConfig,\n} from '@angular/router';\nimport { BF_OVERLAY_HOST, type BfOverlayHost } from '@bitforgehq/ui/overlay';\nimport { BfStackReuseStrategy } from '../navigation/stack-reuse-strategy';\nimport { BfBackButtonService } from '../platform/back-button.service';\nimport { BfHapticsService } from '../platform/haptics.service';\nimport { BfKeyboardService } from '../platform/keyboard.service';\nimport { BfPlatformService } from '../platform/platform.service';\nimport { BfTextScaleService } from '../platform/text-scale.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, type BfShellConfig } from './shell-config';\n\nexport {\n BF_SHELL_CONFIG,\n BF_SHELL_DEFAULTS,\n type BfBackAtRoot,\n type BfShellConfig,\n type BfShellConfigMode,\n type BfShellDynamicType,\n type BfShellDynamicTypeLimit,\n} from './shell-config';\nexport { injectShellMode, resolveShellMode } from '../platform/platform.service';\n\n/**\n * Provides the mobile shell: its configuration, the stack-aware route reuse strategy\n * (`BfStackReuseStrategy`) and `BF_OVERLAY_HOST` (the hardware back button closes the top overlay\n * through `BfBackButtonService`, which on native Android also stands in for the overlays'\n * `CloseWatcher`, and the document never scrolls, so overlays skip the scroll lock). An environment initializer starts `BfPlatformService` (theme sync, the `dark` class),\n * `BfBackButtonService` (the Android hardware back listener), `BfKeyboardService` (keyboard\n * listeners and the rest-height probe), `BfHapticsService` (its native plugin loads lazily on first\n * use, so this only makes the singleton available) and `BfTextScaleService` (iOS Dynamic Type on\n * the root font size) at bootstrap, before any page renders. Every service is\n * `providedIn: 'root'`, so this function lists none of them: `BfNavController` and the overlay\n * services (`BfOverlayStack`, `BfOverlayService`, `BfSheetService`, `BfModalService`,\n * `BfToastService`, `BfActionSheetService`, `BfAlertDialogService`) are created on first\n * injection, and an app that never injects an overlay service bundles no overlay code. Use it\n * together with `provideRouter(routes, withComponentInputBinding(), withBfShellRouting())`, in the\n * application providers: in a lazy route's environment injector the services still resolve from\n * the root injector.\n *\n * @param config The settings to change; any left out keep `BF_SHELL_DEFAULTS`.\n * @returns The shell's environment providers.\n * @summary Provides the Capacitor app shell: its configuration, navigation and platform services\n * @since 0.2.0\n */\nexport function provideBfShell(config: Partial<BfShellConfig> = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: BF_SHELL_CONFIG, useValue: { ...BF_SHELL_DEFAULTS, ...config } },\n { provide: RouteReuseStrategy, useClass: BfStackReuseStrategy },\n {\n provide: BF_OVERLAY_HOST,\n useFactory: (): BfOverlayHost => {\n const back = inject(BfBackButtonService);\n const platform = inject(BfPlatformService);\n return {\n documentScrolls: false,\n // Capacitor's backButton event already reaches the queue on native Android; everywhere\n // else (a mobile browser, a PWA) the overlays' own CloseWatcher takes Android back.\n handlesSystemBack: platform.isNative && platform.platform === 'android',\n registerBack: (priority, handler) => back.register(priority, handler),\n };\n },\n },\n provideEnvironmentInitializer(() => {\n inject(BfPlatformService);\n inject(BfBackButtonService);\n inject(BfKeyboardService);\n inject(BfHapticsService);\n inject(BfTextScaleService);\n }),\n ]);\n}\n\n/**\n * The router feature the stack needs: `canceledNavigationResolution: 'computed'`, so a refused\n * back navigation (a guard stopping a swipe-back) restores the browser history position and the\n * stack stays in sync with it. Pass it to `provideRouter()`.\n *\n * @returns The router configuration feature.\n * @summary The router setting the mobile shell's stack needs, passed to provideRouter\n * @since 0.2.0\n */\nexport function withBfShellRouting(): RouterConfigurationFeature {\n return withRouterConfig({ canceledNavigationResolution: 'computed' });\n}\n","import { signal } from '@angular/core';\n\n/**\n * Where a stacked page is in its lifecycle.\n *\n * @summary Where a stacked page is in its lifecycle: entering, active, leaving or inactive\n * @since 0.2.0\n */\nexport type BfViewState = 'entering' | 'active' | 'leaving' | 'inactive';\n\n/**\n * One lifecycle event of a stacked page.\n *\n * @summary The four lifecycle events of a stacked page as it enters and leaves\n * @since 0.2.0\n */\nexport type BfViewEvent = 'willEnter' | 'didEnter' | 'willLeave' | 'didLeave';\n\n/**\n * Per-page lifecycle. `bf-stack-outlet` creates one per page and provides it through the page's\n * element injector; `bf-page` re-exposes it as outputs, and any page or service can\n * `inject(BfViewLifecycle)` to react to the page entering or leaving the top of the stack.\n * `state` and the counters are signals for `computed`/`effect`; `on()` delivers every event\n * synchronously, including the ones that happen before the page's first change detection and\n * right before a popped page is destroyed.\n * @summary The lifecycle of one stacked page as signals and events, from enter to leave\n * @since 0.2.0\n */\nexport class BfViewLifecycle {\n /** The page's current lifecycle state. */\n readonly state = signal<BfViewState>('inactive');\n /** How often the page started entering; monotonic, so a page can `effect()` on it. */\n readonly willEnter = signal(0);\n /** How often the page became the top page (its enter transition ended). */\n readonly didEnter = signal(0);\n /** How often the page started leaving the top of the stack. */\n readonly willLeave = signal(0);\n /** How often the page stopped being the top page (its leave transition ended). */\n readonly didLeave = signal(0);\n\n private readonly listeners = new Set<(event: BfViewEvent) => void>();\n\n /**\n * Calls `listener` synchronously on every event; returns a function that removes it.\n * @param listener Called with each event, synchronously.\n */\n on(listener: (event: BfViewEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Moves the page to its `willEnter` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireWillEnter(): void {\n this.state.set('entering');\n this.willEnter.update((n) => n + 1);\n this.notify('willEnter');\n }\n\n /**\n * Moves the page to its `didEnter` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireDidEnter(): void {\n this.state.set('active');\n this.didEnter.update((n) => n + 1);\n this.notify('didEnter');\n }\n\n /**\n * Moves the page to its `willLeave` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireWillLeave(): void {\n this.state.set('leaving');\n this.willLeave.update((n) => n + 1);\n this.notify('willLeave');\n }\n\n /**\n * Moves the page to its `didLeave` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireDidLeave(): void {\n this.state.set('inactive');\n this.didLeave.update((n) => n + 1);\n this.notify('didLeave');\n }\n\n private notify(event: BfViewEvent): void {\n for (const listener of [...this.listeners]) {\n listener(event);\n }\n }\n}\n","import { DestroyRef, Injectable, type Signal, computed, inject, signal } from '@angular/core';\nimport { Location } from '@angular/common';\nimport {\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n type NavigationExtras,\n NavigationSkipped,\n NavigationStart,\n Router,\n type UrlTree,\n} from '@angular/router';\nimport { BfBackButtonService } from '../platform/back-button.service';\n\n/**\n * The direction a navigation should animate in.\n *\n * @summary The direction a stack navigation animates in: forward, back or a new root\n * @since 0.2.0\n */\nexport type BfNavDirection = 'forward' | 'back' | 'root';\n\n/**\n * A router target: a URL string, a `UrlTree` or a commands array, as `Router` accepts.\n *\n * @summary A navigation target as the router accepts it: a URL, a UrlTree or commands\n * @since 0.2.0\n */\nexport type BfNavTarget = string | UrlTree | readonly unknown[];\n\n/** What a stack outlet tells `BfNavController` when it registers (see `registerOutlet`). */\nexport interface BfNavOutlet {\n /** Number of pages in the outlet's shown stack. */\n readonly depth: Signal<number>;\n /** False while the page that hosts the outlet is parked under another page (or leaving). */\n readonly active: Signal<boolean>;\n /** The (negative) history step that pops the outlet's top page, or null when unknown. */\n popDelta(): number | null;\n}\n\ninterface OutletRegistration {\n readonly outlet: BfNavOutlet;\n /** Number of stack outlets around this one: 0 for the root outlet. */\n readonly level: number;\n}\n\n/**\n * Stack-aware navigation. `forward` pushes, `back` pops through the real history (so the browser\n * or WebView history stays in sync), `root` replaces the whole stack. Each call leaves a direction\n * hint that `bf-stack-outlet` consumes on the next activation; without a hint the outlet infers\n * the direction from the router event. A hint lives only as long as the navigation it started:\n * when that navigation ends without an activation (a guard refused it), the hint is dropped so it\n * cannot mislabel the next one. Every primary `bf-stack-outlet` registers here with its nesting\n * level; `depth`, `canGoBack` and `back()` follow the **deepest active outlet** (the innermost one\n * whose page is shown, e.g. the outlet inside `bf-tabs`, or the root outlet once it pushed a page\n * above the tabs page). The controller also owns the priority-0 hardware back handler: it pops the\n * deepest active outlet that has a page to pop, and lets the app exit only when every active\n * outlet is at its first page (a parked outlet cannot be popped, so it never blocks the exit).\n * @summary Stack-aware navigation that pushes, pops through the history and replaces the root\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfNavController {\n private readonly router = inject(Router);\n private readonly location = inject(Location);\n private readonly outlets = signal<readonly OutletRegistration[]>([]);\n private hint: BfNavDirection | null = null;\n /** Id of the navigation the hint belongs to; null until that navigation starts. */\n private hintNavigationId: number | null = null;\n\n /** The active outlets, deepest first (on equal level, the one registered last first). */\n private readonly activeOutlets = computed(() =>\n this.outlets()\n .map((r, order) => ({ ...r, order }))\n .filter((r) => r.outlet.active())\n .sort((a, b) => b.level - a.level || b.order - a.order)\n .map((r) => r.outlet),\n );\n\n /** Number of pages in the deepest active stack outlet (0 without an outlet). */\n readonly depth = computed(() => this.activeOutlets()[0]?.depth() ?? 0);\n /** True when the deepest active stack has a page to go back to. */\n readonly canGoBack = computed(() => this.depth() > 1);\n\n constructor() {\n // `router.events`, not the router's navigation signals: the hint belongs to one navigation id\n // and needs that navigation's ordered Start and End/Cancel/Error/Skipped events, which a signal\n // of the latest navigation coalesces.\n const sub = this.router.events.subscribe((e) => {\n if (e instanceof NavigationStart) {\n if (this.hint !== null && this.hintNavigationId === null) {\n this.hintNavigationId = e.id;\n }\n } else if (\n (e instanceof NavigationEnd ||\n e instanceof NavigationCancel ||\n e instanceof NavigationError ||\n e instanceof NavigationSkipped) &&\n e.id === this.hintNavigationId\n ) {\n this.clearHint();\n }\n });\n const unregisterBack = inject(BfBackButtonService).register(0, () => this.handleBackButton());\n inject(DestroyRef).onDestroy(() => {\n sub.unsubscribe();\n unregisterBack();\n });\n }\n\n /**\n * Pushes a page.\n * @param url Where to go.\n * @param extras Router navigation extras, such as `queryParams`.\n * @returns Whether the navigation succeeded.\n */\n forward(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n this.setHint('forward');\n return this.go(url, extras);\n }\n\n /**\n * Replaces the whole stack with one page (`replaceUrl`, no animation).\n * @param url Where to go.\n * @param extras Router navigation extras, such as `queryParams`.\n * @returns Whether the navigation succeeded.\n */\n root(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n this.setHint('root');\n return this.go(url, { ...extras, replaceUrl: true });\n }\n\n /**\n * Pops the top page of the deepest active outlet that has one to pop: always a real history\n * step. When the page added history entries of its own (a query-only or nested child\n * navigation), it goes back past them in one step (`Location.historyGo(-n)`) so the page below\n * is shown; otherwise `Location.back()`.\n */\n back(): void {\n this.setHint('back');\n const delta = this.popTarget()?.popDelta() ?? null;\n if (delta !== null && delta < -1) {\n this.location.historyGo(delta);\n } else {\n this.location.back();\n }\n }\n\n /**\n * Called by every primary stack outlet on init with its nesting level (the number of stack\n * outlets around it). Returns a function that removes it again.\n * @internal\n */\n registerOutlet(outlet: BfNavOutlet, level: number): () => void {\n const registration: OutletRegistration = { outlet, level };\n this.outlets.update((all) => [...all, registration]);\n return () => this.outlets.update((all) => all.filter((r) => r !== registration));\n }\n\n /**\n * Returns the pending direction hint and clears it; called by the stack outlet.\n * @internal\n */\n consumeHint(): BfNavDirection | null {\n const hint = this.hint;\n this.clearHint();\n return hint;\n }\n\n /**\n * Drops a pending hint (its navigation never happened).\n * @internal\n */\n clearHint(): void {\n this.hint = null;\n this.hintNavigationId = null;\n }\n\n /** The deepest active outlet with a page to pop, else the deepest active outlet. */\n private popTarget(): BfNavOutlet | undefined {\n const active = this.activeOutlets();\n return active.find((o) => o.depth() > 1) ?? active[0];\n }\n\n /**\n * The hardware back button: pops while an active outlet has a page to pop; returns false (and\n * the back button service exits the app) when every active outlet is at depth 1 or less. A\n * parked outlet cannot be popped by `back()`, so it does not keep the app from exiting.\n */\n private handleBackButton(): boolean {\n if (this.activeOutlets().every((o) => o.depth() <= 1)) {\n return false;\n }\n this.back();\n return true;\n }\n\n private setHint(hint: BfNavDirection): void {\n this.hint = hint;\n this.hintNavigationId = null;\n }\n\n private go(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n if (Array.isArray(url)) {\n return this.router.navigate(url, extras);\n }\n return this.router.navigateByUrl(url as string | UrlTree, extras);\n }\n}\n","/**\n * Motion constants of the mobile shell, shared by the page transitions, the overlays and the\n * refresher. `theme.css` mirrors them for CSS: `--ease-ios`, `--ease-md`, `--ease-settle`\n * (`@theme`) and `--bf-duration-fast`, `--bf-duration-overlay`, `--bf-duration-push` (`:root`).\n */\n\nimport { OVERLAY_DURATION, OVERLAY_REDUCED_DURATION } from '@bitforgehq/ui/overlay';\n\n/**\n * UIKit's navigation and sheet curve (push/pop, overlays, modal depth); `OVERLAY_EASING` too.\n *\n * @summary The UIKit navigation and sheet easing curve of pushes, pops and overlays\n * @since 0.2.0\n */\nexport const IOS_EASING = 'cubic-bezier(0.32, 0.72, 0, 1)';\n/**\n * Material standard easing (the Android fade-up).\n *\n * @summary The Material standard easing curve of the Android page fade-up transition\n * @since 0.2.0\n */\nexport const MD_EASING = 'cubic-bezier(0.2, 0, 0, 1)';\n/**\n * Ease-out of a released gesture settling into place (swipe-back, refresher).\n *\n * @summary The ease-out curve a released swipe-back or refresher gesture settles with\n * @since 0.2.0\n */\nexport const SETTLE_EASING = 'cubic-bezier(0.2, 0.8, 0.3, 1)';\n\n/**\n * Colour transitions and the reduced-motion cross-fade (ms); the overlay core's reduced fade.\n *\n * @summary The duration of colour transitions and the reduced-motion fade, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_FAST = OVERLAY_REDUCED_DURATION;\n/**\n * Overlay enter/exit and the modal depth effect (ms); the overlay core's duration.\n *\n * @summary The duration of overlay enter and exit and the modal depth effect, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_OVERLAY = OVERLAY_DURATION;\n/**\n * iOS push/pop (ms).\n *\n * @summary The duration of the iOS push and pop page transitions, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_PUSH = 500;\n/**\n * Material fade-up and the refresher settle (ms).\n *\n * @summary The duration of the Material fade-up and the refresher settle, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_MD = 220;\n","/**\n * Page transitions of the stack, built from the Web Animations API. The iOS values follow UIKit's\n * navigation transition: the incoming page slides 100 % → 0, the outgoing one parallaxes\n * 0 → -30 % under a dimming layer, spring-like ease-out, 500 ms.\n */\nimport {\n DURATION_FAST,\n DURATION_MD,\n DURATION_PUSH,\n IOS_EASING,\n MD_EASING,\n SETTLE_EASING,\n} from '../motion';\n\nexport { IOS_EASING, MD_EASING, SETTLE_EASING };\n\n/**\n * The iOS push and pop (ms), `DURATION_PUSH`.\n *\n * @summary The duration of the iOS push and pop stack transitions, in milliseconds\n * @since 0.2.0\n */\nexport const IOS_DURATION = DURATION_PUSH;\n/**\n * Upper bound of the settle animation after a released swipe.\n *\n * @summary The longest a released swipe-back may take to settle, in milliseconds\n * @since 0.2.0\n */\nexport const MAX_COMPLETE_MS = 540;\n/**\n * Material fade-up.\n *\n * @summary The duration of the Material fade-up stack transition, in milliseconds\n * @since 0.2.0\n */\nexport const MD_DURATION = DURATION_MD;\n/**\n * Cross-fade used under `prefers-reduced-motion`.\n *\n * @summary The duration of the cross-fade that replaces page motion under reduced motion\n * @since 0.2.0\n */\nexport const REDUCED_DURATION = DURATION_FAST;\n\n/**\n * A running set of animations that finish together.\n *\n * @summary A running set of page transition animations that finish together\n * @since 0.2.0\n */\nexport interface BfStackTransition {\n /** The Web Animations of the transition, one per moving layer. */\n readonly animations: Animation[];\n /** Their shared duration (ms). */\n readonly duration: number;\n /** Resolves when every animation finished or was cancelled; never rejects. */\n readonly finished: Promise<void>;\n}\n\n/**\n * The direction of a stack transition: a page pushed on, or popped off.\n *\n * @summary The direction of a stack transition, a page pushed on or popped off\n * @since 0.2.0\n */\nexport type BfTransitionKind = 'push' | 'pop';\n\nfunction group(animations: Animation[], duration: number): BfStackTransition {\n const finished = Promise.all(animations.map((a) => a.finished)).then(\n () => undefined,\n () => undefined,\n );\n return { animations, duration, finished };\n}\n\nfunction translateX(px: number): string {\n return `translate3d(${px}px,0,0)`;\n}\n\n/**\n * The iOS push or pop. `pop` is written as the literal reverse of a push but as forward\n * keyframes, so `currentTime` maps directly to swipe progress (0 = nothing moved, duration = pop\n * complete). Returns three animations: the top page (slide and edge shadow), the page under it\n * (parallax) and the dim layer.\n *\n * @param kind `push` or `pop`.\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @param dim The dimming layer between the two pages.\n * @param width The width the pages travel (the outlet width, px).\n * @param easing The curve of the transition.\n * @returns The three running animations.\n * @summary Builds the iOS push or pop with its slide, parallax and dimming layers\n * @since 0.2.0\n */\nexport function createTransition(\n kind: BfTransitionKind,\n entering: HTMLElement,\n leaving: HTMLElement,\n dim: HTMLElement,\n width: number,\n easing: string = IOS_EASING,\n): BfStackTransition {\n const duration = IOS_DURATION;\n const options: KeyframeAnimationOptions = { duration, easing, fill: 'both' };\n const top = kind === 'push' ? entering : leaving;\n const under = kind === 'push' ? leaving : entering;\n const shown = { transform: translateX(0), boxShadow: 'var(--shadow-floating)' };\n const offscreen = { transform: translateX(width), boxShadow: 'none' };\n const rest = { transform: translateX(0) };\n const parallax = { transform: translateX(-width * 0.3) };\n const animations = [\n top.animate(kind === 'push' ? [offscreen, shown] : [shown, offscreen], options),\n under.animate(kind === 'push' ? [rest, parallax] : [parallax, rest], options),\n dim.animate(\n kind === 'push' ? [{ opacity: 0 }, { opacity: 1 }] : [{ opacity: 1 }, { opacity: 0 }],\n options,\n ),\n ];\n return group(animations, duration);\n}\n\n/**\n * Material fade-up: the entering page fades in while rising 16 px; a pop is the reverse on the\n * leaving page. No parallax, no dim.\n *\n * @param kind `push` or `pop`.\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @returns The running animation.\n * @summary Builds the Material fade-up page transition of a push or a pop\n * @since 0.2.0\n */\nexport function mdTransition(\n kind: BfTransitionKind,\n entering: HTMLElement,\n leaving: HTMLElement,\n): BfStackTransition {\n const options: KeyframeAnimationOptions = {\n duration: MD_DURATION,\n easing: MD_EASING,\n fill: 'both',\n };\n const hidden = { opacity: 0, transform: 'translate3d(0,16px,0)' };\n const shown = { opacity: 1, transform: 'translate3d(0,0,0)' };\n const animation =\n kind === 'push'\n ? entering.animate([hidden, shown], options)\n : leaving.animate([shown, hidden], options);\n return group([animation], MD_DURATION);\n}\n\n/**\n * Reduced motion: a 150 ms cross-fade of the two pages, nothing moves.\n *\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @returns The two running fades.\n * @summary Builds the cross-fade that replaces page motion when the user reduces motion\n * @since 0.2.0\n */\nexport function reducedTransition(entering: HTMLElement, leaving: HTMLElement): BfStackTransition {\n const options: KeyframeAnimationOptions = {\n duration: REDUCED_DURATION,\n easing: 'linear',\n fill: 'both',\n };\n return group(\n [\n entering.animate([{ opacity: 0 }, { opacity: 1 }], options),\n leaving.animate([{ opacity: 1 }, { opacity: 0 }], options),\n ],\n REDUCED_DURATION,\n );\n}\n\n/** True when the user asked the system to minimise motion (shared with the overlay core). */\nexport { prefersReducedMotion } from '@bitforgehq/ui/overlay';\n\n/**\n * Frame values of the pop at progress `p` (0 = start, 1 = done), used to hand a scrubbed gesture\n * over to a fresh eased animation on release.\n *\n * @param p The pop progress, 0 (start) to 1 (done).\n * @param width The outlet width (px).\n * @returns The top page offset, the page under it and the dim opacity.\n * @summary The positions of the pop layers at a given progress, for scrubbing a swipe\n * @since 0.2.0\n */\nexport function popFramesAt(p: number, width: number): { top: number; under: number; dim: number } {\n return { top: width * p, under: -width * 0.3 * (1 - p), dim: 1 - p };\n}\n\n/**\n * Animates top, under and dim from pop progress `from` to `to` with an ease-out.\n *\n * @param top The page being popped.\n * @param under The page under it.\n * @param dim The dimming layer.\n * @param width The outlet width (px).\n * @param from The progress at release, 0 to 1.\n * @param to The progress to settle at: 1 completes the pop, 0 cancels it.\n * @param duration The settle duration (ms), see `settleDuration`.\n * @returns The three running animations.\n * @summary Animates a released swipe-back from its progress to done or back to the start\n * @since 0.2.0\n */\nexport function settlePop(\n top: HTMLElement,\n under: HTMLElement,\n dim: HTMLElement,\n width: number,\n from: number,\n to: number,\n duration: number,\n): BfStackTransition {\n const a = popFramesAt(from, width);\n const b = popFramesAt(to, width);\n const options: KeyframeAnimationOptions = { duration, easing: SETTLE_EASING, fill: 'both' };\n return group(\n [\n top.animate([{ transform: translateX(a.top) }, { transform: translateX(b.top) }], options),\n under.animate(\n [{ transform: translateX(a.under) }, { transform: translateX(b.under) }],\n options,\n ),\n dim.animate([{ opacity: a.dim }, { opacity: b.dim }], options),\n ],\n duration,\n );\n}\n\n/**\n * The release rule of the swipe-back: a flick (`velocity` above 0.2 px/ms) or a drag past half\n * the width completes the pop; anything else settles back.\n *\n * @param velocity The release speed (px/ms, positive towards the end edge).\n * @param delta The distance swiped (px).\n * @param width The outlet width (px).\n * @returns True when the pop completes.\n * @summary Decides whether a released swipe-back completes the pop or settles back\n * @since 0.2.0\n */\nexport function shouldCompletePop(velocity: number, delta: number, width: number): boolean {\n return velocity > 0.2 || delta > width / 2;\n}\n\n/**\n * Duration of the settle animation after a release: the remaining distance at the release speed\n * (at least 0.5 px/ms), clamped to 120 ms … `MAX_COMPLETE_MS`.\n *\n * @param remainingPx The distance left to travel (px).\n * @param velocity The release speed (px/ms).\n * @returns The duration (ms), 120 to `MAX_COMPLETE_MS`.\n * @summary The duration of the settle animation after a released swipe-back gesture\n * @since 0.2.0\n */\nexport function settleDuration(remainingPx: number, velocity: number): number {\n const speed = Math.max(Math.abs(velocity), 0.5);\n return Math.round(Math.min(MAX_COMPLETE_MS, Math.max(120, remainingPx / speed)));\n}\n","import { signal } from '@angular/core';\n\n/**\n * Per-page state shared between the parts of one `bf-page`. `bf-page` provides a fresh instance\n * for each page through its element injector; `bf-content` registers its scroll container here\n * on init (and clears it on destroy), so siblings such as `bf-header` can follow the page's\n * scroll without querying the DOM. A plain class, like `BfViewLifecycle`: it is only ever\n * provided per page, never from the root injector.\n *\n * @example\n * const scroller = inject(BfPageContext, { optional: true })?.scroller;\n * @since 0.2.0\n * @summary Per-page state that the header and content of one page share\n */\nexport class BfPageContext {\n /** The page's scroll container (`bf-content`'s scroller), or `null` before one registers. */\n readonly scroller = signal<HTMLElement | null>(null);\n /**\n * Height (px) of a `translucent` `bf-header` drawn over the content, which `bf-content` pads its\n * scroller by (`--bf-header-height`); 0 while the header sits in the page's flow (or none).\n */\n readonly headerHeight = signal(0);\n /**\n * Whether the page reaches the top of the screen, so its `bf-header` toolbar (and a `fullscreen`\n * `bf-content`) pads by the top safe area. True for a page; `bf-modal` in the `sheet`\n * presentation sets it to false, because its card already sits below the status bar.\n */\n readonly safeAreaTop = signal(true);\n /**\n * Whether the page's `bf-content` scroller is the document's `main` landmark (`role=\"main\"`).\n * True for the page a `bf-stack-outlet` shows; `bf-modal` sets it to false (content in a dialog\n * is not `main`), and so does a `bf-page` outside an outlet (a docs illustration) or nested in\n * another page, so the document keeps exactly one `main`: parked pages are `inert` and\n * `aria-hidden`, so only the shown page's landmark is exposed.\n */\n readonly mainLandmark = signal(true);\n}\n\n/**\n * Provided by `bf-stack-outlet` for each stacked page: the page's outermost `bf-page` registers\n * its `BfPageContext` here (a nested `bf-page` finds the slot taken and leaves it), so the outlet\n * can reach the page's scroller, e.g. to scroll it to the top when its tab is re-selected.\n * Internal to the mobile entry.\n */\nexport class BfPageContextSlot {\n context: BfPageContext | null = null;\n}\n","import { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What `bf-tabs` tells the `bf-stack-outlet` inside it: which tab a URL belongs to. The outlet\n * keeps one stack per tab key and switches stacks when a navigation lands on another key.\n *\n * @since 0.2.0\n * @summary What the tabs page tells its stack outlet: which tab a URL belongs to\n */\nexport interface BfTabContext {\n /** The active tab's key (the URL segment after the tabs route), or null outside the tabs. */\n readonly tabKey: Signal<string | null>;\n /**\n * The tab key of `url` (query and fragment ignored), or null when it is not under the tabs route.\n * @param url The URL to read.\n */\n keyOf(url: string): string | null;\n}\n\n/**\n * Provided by `bf-tabs`; injected optionally by `bf-stack-outlet`.\n *\n * @summary Injection token through which the tabs page tells its outlet about the tabs\n * @since 0.2.0\n */\nexport const BF_TAB_CONTEXT = new InjectionToken<BfTabContext>('BF_TAB_CONTEXT');\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n reflectComponentType,\n signal,\n type AfterViewInit,\n type ComponentRef,\n type EnvironmentInjector,\n type OnDestroy,\n type OnInit,\n type Signal,\n viewChild,\n} from '@angular/core';\nimport {\n ActivatedRoute,\n ChildrenOutletContexts,\n type Data,\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationStart,\n type OutletContext,\n PRIMARY_OUTLET,\n Router,\n type RouterOutletContract,\n} from '@angular/router';\nimport { Location } from '@angular/common';\nimport { type Subscription, combineLatest } from 'rxjs';\nimport { BfPlatformService, type BfShellMode } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, resolveShellMode } from '../../shell/provide-bf-shell';\nimport { BfNavController } from '../nav-controller.service';\nimport { BfOverlayStack } from '@bitforgehq/ui/overlay';\nimport {\n type BfStackTransition,\n type BfTransitionKind,\n createTransition,\n mdTransition,\n prefersReducedMotion,\n reducedTransition,\n settleDuration,\n settlePop,\n shouldCompletePop,\n} from '../transitions';\nimport { type BfViewEvent, BfViewLifecycle } from '../view-lifecycle';\nimport { BfPageContextSlot } from '../page/page-context';\nimport { BF_TAB_CONTEXT } from '../tabs/tab-context';\n\n/**\n * Emitted by `bf-stack-outlet` after every change of its stack.\n *\n * @summary What a stack outlet reports after its stack changed: the depth and the tab\n * @since 0.2.0\n */\nexport interface BfStackChange {\n /** Number of pages in the active stack. */\n depth: number;\n /** Key of the active tab's stack inside `bf-tabs`; null for an outlet outside `bf-tabs`. */\n tabKey: string | null;\n}\n\n/**\n * Emitted by `bf-stack-outlet` around a transition between two pages.\n *\n * @summary What a stack outlet reports around a page transition: its kind and animation\n * @since 0.2.0\n */\nexport interface BfStackTransitionEvent {\n /** `push` for a new page (also a `replaceUrl` navigation), `pop` for going back. */\n kind: BfTransitionKind;\n /** False when the pages switched without an animation (`animated=false`). */\n animated: boolean;\n}\n\n/**\n * Provided to every page of a stack outlet: tells the stack outlets inside that page whether the\n * page is shown. A parked page (another page pushed above it, another tab's stack shown) makes\n * its nested outlets inactive, so `BfNavController` follows the outlet whose page is on top.\n */\nclass BfOutletHost {\n /** True while the page is not its outlet's active page (parked, or on its way out). */\n readonly parked = signal(false);\n /** True while the page is shown: not parked, and its own outlet is active. */\n readonly shown: Signal<boolean>;\n\n constructor(outletActive: Signal<boolean>) {\n this.shown = computed(() => !this.parked() && outletActive());\n }\n}\n\ninterface StackEntry {\n /** The page component; destroyed while the entry is `dormant`, and replaced when it comes back. */\n ref: ComponentRef<unknown>;\n /** What the stack outlets inside the page read to know whether it is shown. */\n host: BfOutletHost;\n /** The positioned wrapper the page host lives in; animated, parked and focused. */\n frame: HTMLElement;\n lifecycle: BfViewLifecycle;\n /** Where the page's outermost `bf-page` registers its `BfPageContext`. */\n pageSlot: BfPageContextSlot;\n /**\n * True while the page is beyond `maxLivePages`: its component is destroyed and its frame\n * removed, and only this record (route, key, history position, URL, scroll) is kept.\n */\n dormant: boolean;\n /** The page scroller's `scrollTop` when it went dormant, restored when it is created again. */\n scrollTop: number;\n route: ActivatedRoute;\n /** Outlet-level URL key: path of this route from the root, without child segments or query. */\n key: string;\n /** Router navigation id that created the history entry of this page. */\n navigationId: number;\n /**\n * History position (`ɵrouterPageId`) of the entry this page was pushed onto, or null when\n * unknown (no `computed` resolution, a `skipLocationChange` push).\n */\n pageId: number | null;\n /** Nested outlet contexts saved on deactivate, restored on re-activation. */\n childContexts: Map<string, OutletContext> | null;\n focusReturn: HTMLElement | null;\n inputSub: Subscription | null;\n /** Router URL (with query and fragment) of the last navigation that ended on this page. */\n url: string;\n}\n\ntype Classification =\n | { kind: 'push' | 'replace' | 'root' }\n | { kind: 'pop'; index: number }\n | { kind: 'switch'; tab: string | null; fresh: boolean };\n\ntype FocusMove = 'title' | 'restore' | 'none';\n\ninterface Gesture {\n pointerId: number;\n startX: number;\n startY: number;\n started: boolean;\n top: StackEntry;\n under: StackEntry;\n width: number;\n samples: { x: number; t: number }[];\n transition: BfStackTransition | null;\n}\n\n/**\n * The history position the router writes into every history entry's state as `ɵrouterPageId`\n * when `canceledNavigationResolution` is `'computed'` (which `withBfShellRouting()` sets). It\n * mirrors the private `HistoryStateManager` of Angular 22.1: a push increments it, a\n * `replaceUrl` keeps it, and a traversal restores the entry's value.\n */\nfunction pageIdOf(state: unknown): number | null {\n if (typeof state !== 'object' || state === null) {\n return null;\n }\n const value: unknown = (state as { ɵrouterPageId?: unknown }).ɵrouterPageId;\n return typeof value === 'number' ? value : null;\n}\n\n/**\n * Pointer events whose swipe-back an outlet already armed. Nested outlets see the same\n * `pointerdown` (it bubbles from the inner outlet to the outer one): the innermost outlet that can\n * pop claims it, so a swipe never pops two stacks at once.\n */\nconst armedEvents = new WeakSet<Event>();\n\n/** Width of the left-edge zone that arms the swipe-back. */\nconst EDGE = 50;\n/** Horizontal travel that claims the swipe-back (and vertical travel that aborts it). */\nconst CLAIM = 10;\n/** Duration of the slide-back after the router refused a swiped pop. */\nconst REFUSED_POP_MS = 300;\n/**\n * How long a completed swipe waits for its `Location.back()` to start a navigation. Without one\n * (no history entry, a `skipLocationChange` push) the page slides back as after a refused pop.\n */\nconst BACK_FALLBACK_MS = 600;\nconst PAGE_TITLE = '[data-bf-page-title]';\nconst FRAME_CLASS = 'bf-stack-page absolute inset-0 bg-bg outline-hidden';\n\n/**\n * A router outlet that keeps a stack of live pages: a push slides the new page in and parks the\n * previous one (still in the DOM, so scroll and state survive), a pop slides the top page out and\n * destroys it, and on iOS a finger's edge swipe scrubs the pop (touch only: a mouse or pen drag\n * never arms it). `provideBfShell({ maxLivePages })` bounds the live pages of each stack: the\n * deepest ones are destroyed and created again, scroll restored, on a pop back to them. Replaces\n * `<router-outlet>` in a mobile app.\n * Pages receive route params, query params and route data as inputs, bound by the outlet itself.\n * Inside `bf-tabs` it keeps one stack per tab and switches between them without a transition.\n * Outlets nest: an outlet inside a page (the tabs page) keeps its stacks while an outer outlet\n * parks that page, and `BfNavController` follows the innermost outlet whose page is shown.\n *\n * @example\n * <bf-stack-outlet />\n * @summary Router outlet keeping a live page stack with slide transitions and iOS swipe back\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-stack-outlet',\n host: { class: 'bf-stack' },\n template: `<div\n #dim\n class=\"bg-scrim/15 pointer-events-none absolute inset-0 z-(--bf-z-dim) opacity-0\"\n aria-hidden=\"true\"\n ></div>\n <ng-container #anchor />`,\n styles: `\n :host {\n position: relative;\n display: block;\n height: 100%;\n overflow: hidden;\n overscroll-behavior-x: none;\n /* Keeps horizontal drags as pointer events for the edge swipe-back where the host itself is\n a touch's nearest scroll container (the header, other unscrolled parts of a page). It is\n not inherited, and a touch only intersects the values up to its nearest scroll container,\n so a page's own scrollers (bf-content, an overflow-x-auto row) keep their panning. */\n touch-action: pan-y;\n contain: strict;\n /* Modal depth (iOS card stack): per open modal the page shrinks by 8 % and drops 8 px,\n and rounds to the tile radius. */\n transform: scale(calc(1 - var(--bf-modal-depth, 0) * 0.08))\n translateY(calc(var(--bf-modal-depth, 0) * 8px));\n border-radius: calc(var(--bf-modal-depth, 0) * var(--radius-tile));\n transition:\n transform var(--bf-duration-overlay) var(--ease-ios),\n border-radius var(--bf-duration-overlay);\n transform-origin: top center;\n }\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n }\n `,\n})\nexport class BfStackOutletComponent\n implements RouterOutletContract, OnInit, AfterViewInit, OnDestroy\n{\n /** Outlet name, as on `router-outlet`. */\n readonly name = input(PRIMARY_OUTLET);\n /** Enables the iOS edge swipe-back for this outlet (ignored in Material mode). */\n readonly swipeGesture = input(true, { transform: booleanAttribute });\n /** Animates push and pop; false switches pages instantly (lifecycle events still fire). */\n readonly animated = input(true, { transform: booleanAttribute });\n\n /** Emitted with the new depth after every change of the stack. */\n readonly stackChanged = output<BfStackChange>();\n /** Emitted when a transition between two pages starts (also the start of a swipe-back). */\n readonly transitionStarted = output<BfStackTransitionEvent>();\n /** Emitted when a transition between two pages has settled. */\n readonly transitionEnded = output<BfStackTransitionEvent>();\n /**\n * Emitted with the new depth after every change of the stack, together with `stackChanged`.\n * @deprecated 0.3.0 use stackChanged; removed in 0.4.0\n */\n readonly stackChange = output<BfStackChange>();\n /**\n * Emitted when a transition starts, together with `transitionStarted`.\n * @deprecated 0.3.0 use transitionStarted; removed in 0.4.0\n */\n readonly transitionStart = output<BfStackTransitionEvent>();\n /**\n * Emitted when a transition has settled, together with `transitionEnded`.\n * @deprecated 0.3.0 use transitionEnded; removed in 0.4.0\n */\n readonly transitionEnd = output<BfStackTransitionEvent>();\n\n readonly supportsBindingToComponentInputs = true as const;\n\n private readonly parentContexts = inject(ChildrenOutletContexts);\n private readonly router = inject(Router);\n private readonly location = inject(Location);\n private readonly nav = inject(BfNavController);\n private readonly overlays = inject(BfOverlayStack);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n private readonly platform = inject(BfPlatformService);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly tabContext = inject(BF_TAB_CONTEXT, { optional: true });\n /** The stack outlet around this one (this outlet is inside one of its pages), if any. */\n private readonly parentOutlet = inject(BfStackOutletComponent, {\n skipSelf: true,\n optional: true,\n });\n /** The page of the parent outlet this outlet lives in, if any. */\n private readonly outletHost = inject(BfOutletHost, { optional: true });\n /** Lifecycle of the page hosting this outlet (null for a root outlet); forwarded to our page. */\n private readonly hostLifecycle = inject(BfViewLifecycle, { optional: true });\n /** Removes the listener on `hostLifecycle`. */\n private hostLifecycleOff: (() => void) | null = null;\n /** Number of stack outlets around this one: 0 for the root outlet. */\n private readonly level: number = this.parentOutlet ? this.parentOutlet.level + 1 : 0;\n /** False while the page hosting this outlet is parked, so none of its pages is shown. */\n private readonly shown = computed(() => this.outletHost?.shown() ?? true);\n private readonly depthState = signal(0);\n private readonly anchor = viewChild.required('anchor', { read: ViewContainerRef });\n private readonly dim = viewChild.required<ElementRef<HTMLElement>>('dim');\n\n private readonly mode: BfShellMode = resolveShellMode(this.config, this.platform);\n private readonly injector = inject(Injector);\n /** Pages of one stack kept alive (`BfShellConfig.maxLivePages`): at least 1, else every page. */\n private readonly maxLivePages = livePagesOf(this.config.maxLivePages);\n /** One stack per tab key inside `bf-tabs`; a single stack under the `null` key otherwise. */\n private readonly stacks = new Map<string | null, StackEntry[]>();\n /** Key of the stack that is shown; always null outside `bf-tabs`. */\n private activeTab: string | null = null;\n private active: StackEntry | null = null;\n private leaving: StackEntry | null = null;\n /**\n * The page deactivated while the host page was parked (an outer outlet pushed above it): kept,\n * with every stack, and resumed as it was when the host page comes back.\n */\n private suspended: StackEntry | null = null;\n private pending: { route: ActivatedRoute; env: EnvironmentInjector } | null = null;\n private viewReady = false;\n /** The running transition and the completion that must run if it gets interrupted. */\n private current: { transition: BfStackTransition; complete: () => void } | null = null;\n /** Final frame of a completed swipe, held until the router re-activates the page below. */\n private held: BfStackTransition | null = null;\n /** Set while the swipe gesture has already animated the pop the router now performs. */\n private gestureCommitted = false;\n private gesture: Gesture | null = null;\n private lastStart: NavigationStart | null = null;\n private destroyed = false;\n /** Pending rAF of the deferred transition start. */\n private startFrame: number | null = null;\n /** Fallback timer after a completed swipe's `Location.back()`. */\n private backFallback: ReturnType<typeof setTimeout> | null = null;\n /** Unregisters this outlet from `BfNavController`; primary outlets only. */\n private navUnregister: (() => void) | null = null;\n /** Unregisters the host as a page root of `BfOverlayStack`; outermost outlets only. */\n private rootUnregister: (() => void) | null = null;\n /** History position after the last completed navigation (see `pageIdOf`). */\n private settledPageId: number | null = null;\n /** Element that had focus in the top page when a swipe was claimed, restored on cancel. */\n private swipeFocus: HTMLElement | null = null;\n\n /** The stack that is shown (the active tab's stack inside `bf-tabs`). */\n private get stack(): StackEntry[] {\n let stack = this.stacks.get(this.activeTab);\n if (!stack) {\n stack = [];\n this.stacks.set(this.activeTab, stack);\n }\n return stack;\n }\n\n constructor() {\n // `router.events`, not the router's navigation signals: the stack pairs each navigation id's\n // NavigationStart (direction, popstate restore) with its own End/Cancel/Skipped, in order,\n // which a signal of the latest navigation coalesces.\n const routerSub = this.router.events.subscribe((e) => {\n if (e instanceof NavigationStart) {\n this.lastStart = e;\n // A navigation is under way; its end decides whether the swipe's pop landed.\n this.clearBackFallback();\n return;\n }\n if (e instanceof NavigationEnd || e instanceof NavigationSkipped) {\n this.lastStart = null;\n this.settledPageId = pageIdOf(this.location.getState());\n }\n if (e instanceof NavigationEnd && this.active) {\n this.active.url = e.urlAfterRedirects;\n }\n if (\n (e instanceof NavigationEnd ||\n e instanceof NavigationCancel ||\n e instanceof NavigationError ||\n e instanceof NavigationSkipped) &&\n this.gestureCommitted\n ) {\n // The swipe finished visually, but the navigation ended without popping this outlet (a\n // guard refused it, or it only moved within the top page): slide the page back.\n this.gestureCommitted = false;\n this.nav.clearHint();\n this.restoreAfterRefusedPop();\n }\n });\n inject(DestroyRef).onDestroy(() => routerSub.unsubscribe());\n }\n\n // ---- RouterOutletContract -------------------------------------------------------------------\n\n get isActivated(): boolean {\n return this.active !== null;\n }\n\n get component(): object {\n return this.requireActive().ref.instance as object;\n }\n\n get activatedRoute(): ActivatedRoute {\n return this.requireActive().route;\n }\n\n get activatedRouteData(): Data {\n return this.active?.route.snapshot.data ?? {};\n }\n\n get activatedComponentRef(): ComponentRef<unknown> | null {\n return this.active?.ref ?? null;\n }\n\n ngOnInit(): void {\n this.parentContexts.onChildOutletCreated(this.name(), this);\n const context = this.parentContexts.getContext(this.name());\n if (context?.route && !this.active) {\n this.activateWith(context.route, context.injector);\n }\n if (this.mode === 'ios' && this.config.swipeBack) {\n this.host.addEventListener('pointerdown', this.onPointerDown);\n }\n this.hostLifecycleOff = this.hostLifecycle?.on((e) => this.forwardHostEvent(e)) ?? null;\n // The page stack goes inert while a modal-like overlay is open. A nested outlet lives inside\n // an outer outlet's page, which the outer host already covers.\n if (!this.parentOutlet) {\n this.rootUnregister = this.overlays.registerRoot(this.host);\n }\n if (this.name() === PRIMARY_OUTLET) {\n this.navUnregister = this.nav.registerOutlet(\n {\n depth: this.depthState.asReadonly(),\n active: this.shown,\n popDelta: () => this.popDelta(),\n },\n this.level,\n );\n }\n }\n\n ngAfterViewInit(): void {\n this.viewReady = true;\n if (this.pending) {\n const { route, env } = this.pending;\n this.pending = null;\n this.activateWith(route, env);\n }\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n this.navUnregister?.();\n this.navUnregister = null;\n this.rootUnregister?.();\n this.rootUnregister = null;\n this.hostLifecycleOff?.();\n this.hostLifecycleOff = null;\n this.host.removeEventListener('pointerdown', this.onPointerDown);\n const gesture = this.gesture;\n if (gesture?.started) {\n gesture.transition?.animations.forEach((a) => a.cancel());\n try {\n this.host.releasePointerCapture(gesture.pointerId);\n } catch {\n // The pointer may already be gone.\n }\n }\n this.endGesture();\n // Drop everything that could still run after teardown: the running transition (without its\n // completion), a held swipe settle, a pending `Location.back()` and the deferred start.\n this.current?.transition.animations.forEach((a) => a.cancel());\n this.current = null;\n this.releaseHeld();\n this.gestureCommitted = false;\n this.clearBackFallback();\n if (this.startFrame !== null) {\n cancelAnimationFrame(this.startFrame);\n this.startFrame = null;\n }\n if (this.parentContexts.getContext(this.name())?.outlet === this) {\n this.parentContexts.onChildOutletDestroyed(this.name());\n }\n this.suspended = null;\n this.destroyAllStacks();\n this.depthState.set(0);\n }\n\n /**\n * The router tears down the active page. It is NOT destroyed here: it either stays under the\n * next page (push) or is removed after the pop animation (decided in `activateWith`).\n * @internal\n */\n deactivate(): void {\n const entry = this.active;\n if (!entry) {\n return;\n }\n this.setActive(null);\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n // The router calls `children.onOutletDeactivated()` right after this and discards the map,\n // so take it first; it holds the nested outlets of this page for re-activation.\n entry.childContexts = this.parentContexts\n .getOrCreateContext(this.name())\n .children.onOutletDeactivated();\n const focused = this.document.activeElement;\n entry.focusReturn =\n focused instanceof HTMLElement && entry.frame.contains(focused) ? focused : null;\n this.leaving = entry;\n // Nothing activated in this pass means the route left this outlet: clear the stack. The\n // router deactivates nested outlets before their parent, so this runs after the parent\n // outlet has decided what happens to the page hosting this outlet.\n queueMicrotask(() => {\n if (this.destroyed || this.active || this.leaving !== entry) {\n return;\n }\n if (!this.shown()) {\n // The host page is parked above (it comes back with this outlet as it was) or leaving\n // (its destruction tears this outlet down): keep every stack.\n this.suspended = entry;\n } else {\n this.interruptTransition();\n this.leaving = null;\n this.destroyAllStacks();\n this.emitDepth();\n }\n });\n }\n\n /**\n * The router shows a page: pushed, popped to, or a new root (the router outlet contract).\n * @internal\n */\n activateWith(activatedRoute: ActivatedRoute, environmentInjector: EnvironmentInjector): void {\n if (!this.viewReady) {\n this.pending = { route: activatedRoute, env: environmentInjector };\n return;\n }\n // Finish a running transition before touching the stack, so its completion (which parks its\n // leaving page) cannot undo what this activation does to the same pages.\n this.interruptTransition();\n const suspended = this.suspended;\n this.suspended = null;\n if (suspended && this.resume(suspended, activatedRoute)) {\n return;\n }\n const decision = this.classify(activatedRoute);\n const leaving = this.leaving;\n this.leaving = null;\n\n if (decision.kind === 'switch') {\n this.switchStack(decision.tab, decision.fresh, activatedRoute, environmentInjector, leaving);\n return;\n }\n\n if (decision.kind === 'pop') {\n const target = this.stack[decision.index];\n if (!target) {\n return;\n }\n this.bringBack(target, activatedRoute, environmentInjector);\n const removed = this.stack.splice(decision.index + 1);\n this.emitDepth();\n this.runTransition('pop', target, leaving, removed, 'restore');\n return;\n }\n\n const entry = this.createEntry(activatedRoute, environmentInjector);\n if (decision.kind === 'root') {\n const removed = this.stack.splice(0);\n this.stack.push(entry);\n this.setActive(entry);\n this.emitDepth();\n this.runTransition(null, entry, leaving, removed, leaving ? 'title' : 'none');\n return;\n }\n if (decision.kind === 'push' && entry.pageId !== null && entry.pageId === leaving?.pageId) {\n // A push that created no history entry (`skipLocationChange`): its position is unknown.\n entry.pageId = null;\n }\n const removed =\n decision.kind === 'replace' && leaving\n ? this.stack.splice(this.stack.indexOf(leaving), 1)\n : [];\n this.stack.push(entry);\n this.setActive(entry);\n this.emitDepth();\n this.runTransition(\n leaving ? 'push' : null,\n entry,\n leaving,\n removed,\n leaving ? 'title' : 'none',\n );\n }\n\n /**\n * Only called by a store/retrieve `RouteReuseStrategy`; the stack itself never needs one.\n * @internal\n */\n detach(): ComponentRef<unknown> {\n const entry = this.requireActive();\n this.stack.splice(this.stack.indexOf(entry), 1);\n this.setActive(null);\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n this.anchor().detach(this.anchor().indexOf(entry.ref.hostView));\n entry.frame.remove();\n this.emitDepth();\n return entry.ref;\n }\n\n /**\n * Re-attaches a page a store/retrieve `RouteReuseStrategy` kept (the router outlet contract).\n * @internal\n */\n attach(ref: ComponentRef<unknown>, activatedRoute: ActivatedRoute): void {\n this.interruptTransition();\n this.leaving = null;\n this.anchor().insert(ref.hostView);\n const entry = this.wrap(\n ref,\n activatedRoute,\n new BfViewLifecycle(),\n new BfPageContextSlot(),\n new BfOutletHost(this.shown),\n );\n this.stack.push(entry);\n this.setActive(entry);\n this.bindInputs(entry);\n this.settleLayers();\n this.emitDepth();\n this.trimStack();\n }\n\n // ---- tabs -----------------------------------------------------------------------------------\n\n /**\n * The URL of the top page of a tab's stack (inside `bf-tabs`), or null when that tab has no\n * stack yet. `bf-tabs` navigates there on a tab switch, so the tab comes back as it was left.\n * @param tabKey The key of the tab, the URL segment after `tabsPath`.\n */\n lastUrlOf(tabKey: string): string | null {\n return this.stacks.get(tabKey)?.at(-1)?.url ?? null;\n }\n\n /**\n * Pops the active stack back to its first page in one history step (`Location.historyGo(-n)`\n * to the entry before the second page was pushed), keeping that page alive. Returns false, and\n * does nothing, when the stack has one page or the history position is unknown.\n */\n popToRoot(): boolean {\n const second = this.stack[1]?.pageId ?? null;\n const current = pageIdOf(this.location.getState());\n if (second === null || current === null) {\n return false;\n }\n const delta = second - 1 - current;\n if (delta >= 0) {\n return false;\n }\n this.location.historyGo(delta);\n return true;\n }\n\n /**\n * Scrolls the active page's `bf-content` back to the top (a re-selected tab).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollActiveContentToTop(behavior: ScrollBehavior = 'smooth'): void {\n this.active?.pageSlot.context?.scroller()?.scrollTo({ top: 0, behavior });\n }\n\n /**\n * A navigation onto another tab's key: parks the whole current stack and shows the target\n * stack (its page for this URL, the pages above it dropped) or starts it with a new page. No\n * transition and no gesture; `fresh` (a `root` navigation) discards the target stack first.\n */\n private switchStack(\n tab: string | null,\n fresh: boolean,\n route: ActivatedRoute,\n env: EnvironmentInjector,\n leaving: StackEntry | null,\n ): void {\n const from = this.stack;\n if (fresh) {\n for (const e of this.stacks.get(tab)?.splice(0) ?? []) {\n this.destroyEntry(e);\n }\n }\n this.activeTab = tab;\n const target = this.stack;\n const key = keyOf(route);\n const index = lastIndexBelow(target, target.length, (e) => e.key === key);\n let entering: StackEntry;\n let removed: StackEntry[] = [];\n const existing = target[index];\n if (existing) {\n this.bringBack(existing, route, env);\n removed = target.splice(index + 1);\n entering = existing;\n } else {\n entering = this.createEntry(route, env);\n target.push(entering);\n this.setActive(entering);\n }\n this.emitDepth();\n this.runTransition(null, entering, leaving, removed, 'title');\n for (const e of from) {\n park(e.frame);\n }\n }\n\n /** Shows a page of the stack again: a live one as it was, a dormant one created anew. */\n private bringBack(target: StackEntry, route: ActivatedRoute, env: EnvironmentInjector): void {\n if (target.dormant) {\n this.revive(target, route, env);\n } else {\n this.reactivate(target, route);\n }\n }\n\n /**\n * Creates a dormant page again from the route the router re-activated, in a new frame, keeping\n * its record (key, history position, URL); its scroller gets the `scrollTop` it had back after\n * the first render. Its nested outlets start empty (\"deep pages come back fresh\").\n */\n private revive(target: StackEntry, route: ActivatedRoute, env: EnvironmentInjector): void {\n const fresh = this.createEntry(route, env);\n fresh.inputSub?.unsubscribe();\n target.ref = fresh.ref;\n target.frame = fresh.frame;\n target.host = fresh.host;\n target.lifecycle = fresh.lifecycle;\n target.pageSlot = fresh.pageSlot;\n target.route = route;\n target.key = keyOf(route);\n target.dormant = false;\n target.childContexts = null;\n target.focusReturn = null;\n this.setActive(target);\n this.bindInputs(target);\n const scrollTop = target.scrollTop;\n target.scrollTop = 0;\n if (scrollTop > 0) {\n const ref = target.ref;\n // `read`: after `bf-content` has registered its scroller (in the default phase) in the\n // render that follows the page's first change detection.\n afterNextRender(\n {\n read: () => {\n const scroller = target.ref === ref ? target.pageSlot.context?.scroller() : null;\n if (scroller) {\n scroller.scrollTop = scrollTop;\n }\n },\n },\n { injector: this.injector },\n );\n }\n }\n\n /**\n * Keeps at most `maxLivePages` pages of the shown stack alive: the deepest ones beyond it go\n * dormant (component destroyed, frame removed, record kept). The top pages stay alive.\n */\n private trimStack(): void {\n const stack = this.stack;\n const excess = stack.length - this.maxLivePages;\n for (let i = 0; i < excess; i++) {\n const entry = stack[i];\n if (entry && !entry.dormant && entry !== this.active && entry !== this.leaving) {\n this.makeDormant(entry);\n }\n }\n }\n\n private makeDormant(entry: StackEntry): void {\n entry.scrollTop = entry.pageSlot.context?.scroller()?.scrollTop ?? 0;\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n // The nested outlets are destroyed with the page; their saved contexts are of no use.\n entry.childContexts = null;\n entry.focusReturn = null;\n entry.dormant = true;\n entry.ref.destroy();\n entry.frame.remove();\n }\n\n /** Shows a kept page again: new route, nested outlets re-attached, change detection back on. */\n private reactivate(target: StackEntry, route: ActivatedRoute): void {\n target.route = route;\n target.key = keyOf(route);\n const children = this.parentContexts.getOrCreateContext(this.name()).children;\n if (target.childContexts) {\n children.onOutletReAttached(target.childContexts);\n target.childContexts = null;\n }\n target.ref.changeDetectorRef.reattach();\n target.ref.changeDetectorRef.markForCheck();\n this.setActive(target);\n this.bindInputs(target);\n }\n\n private tabOf(route: ActivatedRoute): string | null {\n return this.tabContext?.keyOf(keyOf(route)) ?? null;\n }\n\n private destroyAllStacks(): void {\n for (const stack of this.stacks.values()) {\n for (const e of stack.splice(0)) {\n this.destroyEntry(e);\n }\n }\n this.stacks.clear();\n this.activeTab = null;\n }\n\n // ---- direction ------------------------------------------------------------------------------\n\n private classify(route: ActivatedRoute): Classification {\n const hint = this.nav.consumeHint();\n const start = this.lastStart;\n const key = keyOf(route);\n if (this.tabContext) {\n const tab = this.tabOf(route);\n if (this.stack.length === 0 && ![...this.stacks.values()].some((s) => s.length > 0)) {\n this.activeTab = tab;\n } else if (tab !== this.activeTab) {\n return { kind: 'switch', tab, fresh: hint === 'root' };\n }\n }\n if (this.stack.length === 0 || hint === 'root') {\n return { kind: 'root' };\n }\n const below = this.stack.length - 1; // entries [0, below) are under the leaving page\n if (start?.navigationTrigger === 'popstate' || hint === 'back') {\n const restoredPageId = pageIdOf(start?.restoredState);\n if (restoredPageId !== null && this.settledPageId !== null) {\n // History positions tell a back traversal from a forward one: forward always pushes,\n // back lands on the highest page pushed at or before the restored entry.\n if (restoredPageId > this.settledPageId) {\n return { kind: 'push' };\n }\n const byPosition = lastIndexBelow(\n this.stack,\n below,\n (e) => e.pageId !== null && e.pageId <= restoredPageId,\n );\n if (byPosition >= 0) {\n return { kind: 'pop', index: byPosition };\n }\n }\n const restored = start?.restoredState?.navigationId;\n let index = this.stack.findIndex((e, i) => i < below && e.navigationId === restored);\n if (index < 0) {\n // The page's URL may have changed through a nested child route since it was pushed.\n index = lastIndexBelow(this.stack, below, (e) => e.key === key);\n }\n // A popstate that matches nothing (browser forward, a destroyed entry) is a push.\n return index >= 0 ? { kind: 'pop', index } : { kind: 'push' };\n }\n if (this.router.currentNavigation()?.extras.replaceUrl) {\n return { kind: 'replace' };\n }\n return { kind: 'push' };\n }\n\n // ---- entries --------------------------------------------------------------------------------\n\n private createEntry(route: ActivatedRoute, env: EnvironmentInjector): StackEntry {\n const component = route.snapshot.component;\n if (!component) {\n throw new Error('bf-stack-outlet: the activated route has no component');\n }\n const lifecycle = new BfViewLifecycle();\n const pageSlot = new BfPageContextSlot();\n const host = new BfOutletHost(this.shown);\n const anchor = this.anchor();\n const injector = Injector.create({\n providers: [\n { provide: ActivatedRoute, useValue: route },\n {\n provide: ChildrenOutletContexts,\n useValue: this.parentContexts.getOrCreateContext(this.name()).children,\n },\n { provide: BfViewLifecycle, useValue: lifecycle },\n { provide: BfPageContextSlot, useValue: pageSlot },\n { provide: BfOutletHost, useValue: host },\n ],\n parent: anchor.injector,\n });\n const ref = anchor.createComponent(component, {\n index: anchor.length,\n injector,\n environmentInjector: env,\n });\n const entry = this.wrap(ref, route, lifecycle, pageSlot, host);\n this.bindInputs(entry);\n return entry;\n }\n\n /**\n * Moves the page host into its own positioned frame. The host stays a view of the anchor's\n * `ViewContainerRef` (Angular removes a destroyed host from whatever parent it has); the frame\n * is what gets animated, parked and focused, so the page host stays free of outlet state.\n */\n private wrap(\n ref: ComponentRef<unknown>,\n route: ActivatedRoute,\n lifecycle: BfViewLifecycle,\n pageSlot: BfPageContextSlot,\n host: BfOutletHost,\n ): StackEntry {\n // The router writes the new history entry before it activates routes, so this is the entry\n // the page is pushed onto.\n const pageId = pageIdOf(this.location.getState());\n const element = ref.location.nativeElement as HTMLElement;\n const frame = this.document.createElement('div');\n frame.className = FRAME_CLASS;\n frame.tabIndex = -1;\n this.host.insertBefore(frame, element);\n frame.appendChild(element);\n return {\n ref,\n host,\n frame,\n lifecycle,\n pageSlot,\n dormant: false,\n scrollTop: 0,\n route,\n key: keyOf(route),\n navigationId: this.router.currentNavigation()?.id ?? this.lastStart?.id ?? 0,\n pageId,\n childContexts: null,\n focusReturn: null,\n inputSub: null,\n // The router moves its current URL before it activates routes; NavigationEnd refines it.\n url: this.router.url,\n };\n }\n\n /**\n * The outlet's own component-input binder: the router's (`RoutedComponentInputBinder` behind\n * the private `INPUT_BINDER` token) only serves `RouterOutlet`. Mirrors its Angular 22.1\n * behaviour with the default options, whether or not `withComponentInputBinding()` is present:\n * query params, then path params, then route data (including resolved data), each input set\n * (or cleared with `undefined`) on every emission. Not supported: the feature's\n * `queryParams: false` and `unmatchedInputBehavior` options and the experimental route\n * `resources`.\n */\n private bindInputs(entry: StackEntry): void {\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n const component = entry.route.component;\n const mirror = component ? reflectComponentType(component) : null;\n if (!mirror) {\n return;\n }\n const route = entry.route;\n entry.inputSub = combineLatest([route.queryParams, route.params, route.data]).subscribe(\n ([query, params, data]) => {\n const all: Record<string, unknown> = { ...query, ...params, ...data };\n for (const { templateName } of mirror.inputs) {\n entry.ref.setInput(templateName, all[templateName]);\n }\n },\n );\n }\n\n private destroyEntry(entry: StackEntry): void {\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n if (!entry.dormant) {\n entry.ref.destroy();\n }\n entry.frame.remove();\n }\n\n private emitTransitionStart(event: BfStackTransitionEvent): void {\n this.transitionStarted.emit(event);\n this.transitionStart.emit(event);\n }\n\n private emitTransitionEnd(event: BfStackTransitionEvent): void {\n this.transitionEnded.emit(event);\n this.transitionEnd.emit(event);\n }\n\n private emitDepth(): void {\n const depth = this.stack.length;\n this.depthState.set(depth);\n const change: BfStackChange = { depth, tabKey: this.activeTab };\n this.stackChanged.emit(change);\n this.stackChange.emit(change);\n }\n\n /** Makes `entry` the active page; the page it replaces is parked for its nested outlets. */\n private setActive(entry: StackEntry | null): void {\n if (this.active && this.active !== entry) {\n this.active.host.parked.set(true);\n }\n entry?.host.parked.set(false);\n this.active = entry;\n }\n\n /**\n * The host page is back (the outer outlet popped to it): shows the page that was active when\n * it was parked, as it was, when the router activates the same route key on the same stack.\n * Returns false for any other route, which then goes through the normal classification.\n */\n private resume(entry: StackEntry, route: ActivatedRoute): boolean {\n if (\n this.leaving !== entry ||\n keyOf(route) !== entry.key ||\n (this.tabContext !== null && this.tabOf(route) !== this.activeTab) ||\n !this.stack.includes(entry)\n ) {\n return false;\n }\n this.leaving = null;\n this.reactivate(entry, route);\n this.emitDepth();\n this.settleLayers();\n return true;\n }\n\n /**\n * The host page entered or left (the parent outlet parked or restored it): the page this outlet\n * shows follows it. Fires only on a real state change, so a page that just ran its own\n * transition (or its first activation) does not get an event twice.\n */\n private forwardHostEvent(event: BfViewEvent): void {\n const target = this.active ?? this.suspended ?? this.leaving;\n if (!target || this.current) {\n return;\n }\n const lifecycle = target.lifecycle;\n const state = lifecycle.state();\n if (event === 'willEnter' && (state === 'inactive' || state === 'leaving')) {\n lifecycle.fireWillEnter();\n } else if (event === 'didEnter' && state === 'entering') {\n lifecycle.fireDidEnter();\n } else if (event === 'willLeave' && (state === 'active' || state === 'entering')) {\n lifecycle.fireWillLeave();\n } else if (event === 'didLeave' && state === 'leaving') {\n lifecycle.fireDidLeave();\n }\n }\n\n private requireActive(): StackEntry {\n if (!this.active) {\n throw new Error('bf-stack-outlet: the outlet is not activated');\n }\n return this.active;\n }\n\n // ---- transitions ----------------------------------------------------------------------------\n\n /**\n * Layers the two pages, fires the lifecycle, animates (unless `kind` is null, `animated` is\n * off or a swipe already animated the pop), then destroys `removed`, parks `leaving` when it\n * stays in the stack and moves focus.\n */\n private runTransition(\n kind: BfTransitionKind | null,\n entering: StackEntry,\n leaving: StackEntry | null,\n removed: StackEntry[],\n focus: FocusMove,\n ): void {\n const alreadyAnimated = kind === 'pop' && this.gestureCommitted;\n this.gestureCommitted = false;\n this.clearBackFallback();\n const animate = kind !== null && leaving !== null && !alreadyAnimated && this.animated();\n const event: BfStackTransitionEvent | null =\n kind && leaving ? { kind, animated: animate || alreadyAnimated } : null;\n\n this.layer(entering, leaving, kind);\n leaving?.lifecycle.fireWillLeave();\n entering.lifecycle.fireWillEnter();\n if (event && !alreadyAnimated) {\n this.emitTransitionStart(event);\n }\n\n let transition: BfStackTransition | null = null;\n const complete = (): void => {\n this.current = null;\n transition?.animations.forEach((a) => a.cancel());\n this.releaseHeld();\n leaving?.lifecycle.fireDidLeave();\n for (const e of removed) {\n if (e !== entering) {\n this.destroyEntry(e);\n }\n }\n if (leaving && !removed.includes(leaving)) {\n // Render the page's final state (its didLeave) once, then stop checking it while parked.\n leaving.ref.changeDetectorRef.detectChanges();\n leaving.ref.changeDetectorRef.detach();\n }\n entering.lifecycle.fireDidEnter();\n this.settleLayers();\n this.trimStack();\n this.moveFocus(entering, focus);\n if (event) {\n this.emitTransitionEnd(event);\n }\n };\n\n if (!animate || !kind || !leaving) {\n complete();\n return;\n }\n const t = this.createAnimation(kind, entering, leaving);\n transition = t;\n this.current = { transition: t, complete };\n if (this.config.deferTransitionStart) {\n // Hold frame 0 while the entering page renders its first frame, then play: the render\n // cost lands before the motion instead of as a long frame inside it.\n t.animations.forEach((a) => a.pause());\n this.startFrame = requestAnimationFrame(() => {\n this.startFrame = requestAnimationFrame(() => {\n this.startFrame = null;\n if (this.current?.transition === t) {\n t.animations.forEach((a) => a.play());\n }\n });\n });\n }\n void t.finished.then(() => {\n if (!this.destroyed && this.current?.transition === t) {\n complete();\n }\n });\n }\n\n private createAnimation(\n kind: BfTransitionKind,\n entering: StackEntry,\n leaving: StackEntry,\n ): BfStackTransition {\n if (prefersReducedMotion(this.document.defaultView)) {\n return reducedTransition(entering.frame, leaving.frame);\n }\n if (this.mode === 'md') {\n return mdTransition(kind, entering.frame, leaving.frame);\n }\n return createTransition(\n kind,\n entering.frame,\n leaving.frame,\n this.dim().nativeElement,\n this.host.clientWidth,\n );\n }\n\n /** A new activation during a running transition finishes the old one at once. */\n private interruptTransition(): void {\n const current = this.current;\n if (current) {\n current.complete();\n }\n }\n\n private clearBackFallback(): void {\n if (this.backFallback !== null) {\n clearTimeout(this.backFallback);\n this.backFallback = null;\n }\n }\n\n private releaseHeld(): void {\n this.held?.animations.forEach((a) => a.cancel());\n this.held = null;\n }\n\n private moveFocus(entering: StackEntry, focus: FocusMove): void {\n if (focus === 'restore') {\n (entering.focusReturn ?? entering.frame).focus({ preventScroll: true });\n entering.focusReturn = null;\n } else if (focus === 'title') {\n const title = entering.frame.querySelector<HTMLElement>(PAGE_TITLE);\n if (title && !title.hasAttribute('tabindex')) {\n title.setAttribute('tabindex', '-1');\n }\n (title ?? entering.frame).focus({ preventScroll: true });\n }\n }\n\n /** During a transition: both pages visible, only the entering one interactive. */\n private layer(\n entering: StackEntry,\n leaving: StackEntry | null,\n kind: BfTransitionKind | null,\n ): void {\n const top = kind === 'pop' ? leaving : entering;\n const under = kind === 'pop' ? entering : leaving;\n for (const e of this.stack) {\n if (e !== entering && e !== leaving) {\n park(e.frame);\n }\n }\n unpark(entering.frame);\n if (leaving) {\n leaving.frame.style.visibility = 'visible';\n leaving.frame.setAttribute('inert', '');\n }\n if (under) {\n under.frame.style.zIndex = '1';\n }\n if (top) {\n top.frame.style.zIndex = '3';\n }\n }\n\n /** Final state: only the top page visible and interactive; the rest parked. */\n private settleLayers(): void {\n const top = this.stack[this.stack.length - 1];\n for (const e of this.stack) {\n if (e === top) {\n unpark(e.frame);\n e.frame.style.zIndex = '3';\n } else {\n park(e.frame);\n }\n }\n }\n\n // ---- swipe-back gesture (iOS mode) ----------------------------------------------------------\n\n /**\n * Non-passive `touchmove` guard, attached only while a swipe is armed (which happens only inside\n * the 50 px edge zone). Once the swipe is claimed, native scrolling must not start: a horizontal\n * scroller under the finger (any `overflow-x-auto` row, which pans on its own) would take over\n * the touch and cancel the pointer stream. Inside the edge zone the gesture wins; outside it the\n * guard is never attached, so such scrollers keep their own pan.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n if (this.gesture?.started && event.cancelable) {\n event.preventDefault();\n }\n };\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n // A finger only: a mouse drag or a text selection from the edge (desktop web with the mode\n // forced to `ios`) and a pen never pop the page.\n if (\n event.pointerType !== 'touch' ||\n !this.swipeGesture() ||\n this.stack.length < 2 ||\n !event.isPrimary ||\n armedEvents.has(event)\n ) {\n return;\n }\n const rect = this.host.getBoundingClientRect();\n // Only the left-edge zone arms the swipe; everywhere else the page and its horizontal\n // scrollers keep the touch.\n if (event.clientX - rect.left > EDGE) {\n return;\n }\n // This outlet can pop: claim the event, even while busy, so an outer outlet does not arm on a\n // page that is about to change.\n armedEvents.add(event);\n if (this.gesture || this.current || this.held) {\n return;\n }\n const top = this.stack[this.stack.length - 1];\n const under = this.stack[this.stack.length - 2];\n // A dormant page below (`maxLivePages: 1`) has nothing to show under the finger.\n if (!top || !under || top !== this.active || under.dormant) {\n return;\n }\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n started: false,\n top,\n under,\n width: rect.width,\n samples: [{ x: event.clientX, t: event.timeStamp }],\n transition: null,\n };\n this.host.addEventListener('pointermove', this.onPointerMove);\n this.host.addEventListener('pointerup', this.onPointerUp);\n this.host.addEventListener('pointercancel', this.onPointerCancel);\n this.host.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const g = this.gesture;\n if (g?.pointerId !== event.pointerId || !g) {\n return;\n }\n const dx = event.clientX - g.startX;\n const dy = event.clientY - g.startY;\n if (!g.started) {\n if (Math.abs(dy) > CLAIM && Math.abs(dy) > Math.abs(dx)) {\n this.endGesture(); // vertical: let the page scroll\n return;\n }\n if (dx < CLAIM || Math.abs(dx) <= Math.abs(dy)) {\n return;\n }\n g.started = true;\n const focused = this.document.activeElement;\n this.swipeFocus =\n focused instanceof HTMLElement && g.top.frame.contains(focused) ? focused : null;\n try {\n this.host.setPointerCapture(g.pointerId);\n } catch {\n // Synthetic pointers have no active pointer to capture.\n }\n this.layer(g.under, g.top, 'pop');\n g.transition = createTransition(\n 'pop',\n g.under.frame,\n g.top.frame,\n this.dim().nativeElement,\n g.width,\n 'linear',\n );\n g.transition.animations.forEach((a) => a.pause());\n this.emitTransitionStart({ kind: 'pop', animated: true });\n }\n g.samples.push({ x: event.clientX, t: event.timeStamp });\n if (g.samples.length > 6) {\n g.samples.shift();\n }\n const time = clamp(dx / g.width, 0, 1) * (g.transition?.duration ?? 0);\n g.transition?.animations.forEach((a) => (a.currentTime = time));\n };\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const g = this.gesture;\n if (g?.pointerId !== event.pointerId || !g) {\n return;\n }\n if (!g.started) {\n this.endGesture();\n return;\n }\n const delta = event.clientX - g.startX;\n const first = g.samples[0] ?? { x: g.startX, t: event.timeStamp };\n const velocity = (event.clientX - first.x) / Math.max(1, event.timeStamp - first.t); // px/ms\n const complete = shouldCompletePop(velocity, delta, g.width);\n const duration = settleDuration(complete ? g.width - delta : delta, velocity);\n g.transition?.animations.forEach((a) => a.cancel());\n const settle = settlePop(\n g.top.frame,\n g.under.frame,\n this.dim().nativeElement,\n g.width,\n clamp(delta / g.width, 0, 1),\n complete ? 1 : 0,\n duration,\n );\n this.endGesture();\n this.held = settle;\n void settle.finished.then(() => {\n if (this.destroyed || this.held !== settle) {\n return;\n }\n if (complete) {\n // Keep the final frame (top page off-screen) until the router re-activates `under`.\n this.gestureCommitted = true;\n this.nav.back();\n this.backFallback = setTimeout(() => {\n this.backFallback = null;\n if (this.gestureCommitted) {\n // `back()` produced no navigation: nothing will consume the hint or pop the page.\n this.gestureCommitted = false;\n this.nav.clearHint();\n this.restoreAfterRefusedPop();\n }\n }, BACK_FALLBACK_MS);\n } else {\n this.cancelSwipe();\n }\n });\n };\n\n private readonly onPointerCancel = (): void => {\n const g = this.gesture;\n if (g?.started) {\n g.transition?.animations.forEach((a) => a.cancel());\n this.cancelSwipe();\n }\n this.endGesture();\n };\n\n /** Settles a swipe that did not pop: the top page stays, the page under it is parked again. */\n private cancelSwipe(): void {\n this.releaseHeld();\n this.settleLayers();\n // The top page was inert while it was dragged, which dropped its focus: put it back.\n const top = this.active;\n const focus = this.swipeFocus;\n this.swipeFocus = null;\n if (top) {\n (focus ?? top.frame.querySelector<HTMLElement>(PAGE_TITLE) ?? top.frame).focus({\n preventScroll: true,\n });\n }\n this.emitTransitionEnd({ kind: 'pop', animated: true });\n }\n\n /**\n * The history step that pops the top page in one navigation: back to the entry before the page\n * was pushed, skipping the entries it added itself (query-only or nested child navigations).\n * Null when unknown, and `Location.back()` is used instead.\n */\n private popDelta(): number | null {\n const top = this.active;\n const current = pageIdOf(this.location.getState());\n if (!top || this.stack.length < 2 || top.pageId === null || current === null) {\n return null;\n }\n const delta = top.pageId - 1 - current;\n return delta < 0 ? delta : null;\n }\n\n private endGesture(): void {\n this.host.removeEventListener('pointermove', this.onPointerMove);\n this.host.removeEventListener('pointerup', this.onPointerUp);\n this.host.removeEventListener('pointercancel', this.onPointerCancel);\n this.host.removeEventListener('touchmove', this.touchMoveGuard);\n this.gesture = null;\n }\n\n /** The router refused a swiped pop (a guard): slide the top page back in over 300 ms. */\n private restoreAfterRefusedPop(): void {\n const top = this.active;\n const under = this.stack[this.stack.length - 2];\n const held = this.held;\n if (!top || !under) {\n this.releaseHeld();\n return;\n }\n const t = settlePop(\n top.frame,\n under.frame,\n this.dim().nativeElement,\n this.host.clientWidth,\n 1,\n 0,\n REFUSED_POP_MS,\n );\n held?.animations.forEach((a) => a.cancel());\n this.held = t;\n void t.finished.then(() => {\n if (!this.destroyed && this.held === t) {\n this.cancelSwipe();\n }\n });\n }\n}\n\n/** `BfShellConfig.maxLivePages` as a count: `Infinity` (or unset, or NaN) keeps every page. */\nfunction livePagesOf(value: number | undefined): number {\n if (value === undefined || Number.isNaN(value)) {\n return Infinity;\n }\n return Math.max(1, Math.floor(value));\n}\n\nfunction park(frame: HTMLElement): void {\n frame.style.visibility = 'hidden';\n frame.style.zIndex = '0';\n frame.setAttribute('inert', '');\n frame.setAttribute('aria-hidden', 'true');\n}\n\nfunction unpark(frame: HTMLElement): void {\n frame.style.visibility = 'visible';\n frame.removeAttribute('inert');\n frame.removeAttribute('aria-hidden');\n}\n\nfunction keyOf(route: ActivatedRoute): string {\n return (\n '/' +\n route.snapshot.pathFromRoot\n .flatMap((s) => s.url.map((u) => u.path))\n .filter((p) => p.length > 0)\n .join('/')\n );\n}\n\nfunction lastIndexBelow(\n stack: readonly StackEntry[],\n below: number,\n match: (entry: StackEntry) => boolean,\n): number {\n for (let i = below - 1; i >= 0; i--) {\n const entry = stack[i];\n if (entry && match(entry)) {\n return i;\n }\n }\n return -1;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n","import { Component, DestroyRef, computed, inject, output } from '@angular/core';\nimport { BfViewLifecycle } from '../view-lifecycle';\nimport { BfPageContext, BfPageContextSlot } from './page-context';\n\n/**\n * The page frame of a mobile app: `bf-stack-outlet` positions one per stacked page and provides\n * its `BfViewLifecycle` through the element injector. Everything scrollable inside a page goes\n * into `bf-content`; `bf-page` is only the frame, the lifecycle signal and a per-page\n * `BfPageContext` (provided for its children, where `bf-content` registers its scroller). The\n * page an outlet shows makes its `bf-content` the document's `main` landmark. Used\n * standalone (outside an outlet, e.g. in a docs demo) it falls back to a local, inert lifecycle\n * instance instead of throwing, and exposes no `main` landmark.\n *\n * @example\n * <bf-page (bfViewDidEnter)=\"load()\">\n * <p>Page content</p>\n * </bf-page>\n * @summary Frame for one stacked mobile screen that supplies lifecycle events and shared page state\n * @category Navigation\n * @since 0.2.0\n * @slot default The page: a `bf-header` and a `bf-content`.\n */\n@Component({\n selector: 'bf-page',\n host: { class: 'absolute inset-0 flex flex-col bg-bg' },\n providers: [BfPageContext],\n template: `<ng-content />`,\n})\nexport class BfPageComponent {\n private readonly lifecycle = inject(BfViewLifecycle, { optional: true }) ?? new BfViewLifecycle();\n\n /** The page's current lifecycle state, mirrored from `BfViewLifecycle`. */\n readonly viewState = computed(() => this.lifecycle.state());\n\n /** Emitted when the page starts entering the top of the stack. */\n readonly bfViewWillEnter = output<void>();\n /** Emitted once the page is active at the top of the stack. */\n readonly bfViewDidEnter = output<void>();\n /** Emitted when the page starts leaving the top of the stack. */\n readonly bfViewWillLeave = output<void>();\n /** Emitted once the page has left the top of the stack. */\n readonly bfViewDidLeave = output<void>();\n\n constructor() {\n // Forward synchronously: the outlet fires `willEnter` (and, without an animation, `didEnter`)\n // before the page's first change detection and `didLeave` right before a popped page is\n // destroyed, so a signal effect would miss them.\n const off = this.lifecycle.on((event) => {\n switch (event) {\n case 'willEnter':\n this.bfViewWillEnter.emit();\n break;\n case 'didEnter':\n this.bfViewDidEnter.emit();\n break;\n case 'willLeave':\n this.bfViewWillLeave.emit();\n break;\n case 'didLeave':\n this.bfViewDidLeave.emit();\n break;\n }\n });\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(off);\n\n // Hand this page's context to the stack outlet (outermost `bf-page` only).\n const slot = inject(BfPageContextSlot, { optional: true });\n const context = inject(BfPageContext);\n if (slot?.context === null) {\n slot.context = context;\n destroyRef.onDestroy(() => {\n if (slot.context === context) {\n slot.context = null;\n }\n });\n } else {\n // Outside an outlet (a docs illustration) or nested in another page: not the `main` one.\n context.mainLandmark.set(false);\n }\n }\n}\n","import { Component, computed, inject, input } from '@angular/core';\nimport { BF_LABELS, BfIconButtonComponent } from '@bitforgehq/ui';\nimport { BfIconComponent } from '@bitforgehq/ui/icons';\nimport { ArrowLeft, ChevronLeft } from 'lucide';\nimport { type BfShellMode } from '../../platform/platform.service';\nimport { injectShellMode } from '../../shell/provide-bf-shell';\nimport { BfNavController } from '../nav-controller.service';\n\n/**\n * iOS: chevron + text as one 44 px tall control, in the primary text colour like a UIKit bar button.\n * It may shrink down to its 44 px hit area when the bar is short of space (large text sizes): the\n * text truncates, then disappears, before the chevron does, as UIKit drops the back title.\n */\nconst IOS_CLASSES =\n 'relative inline-flex h-11 min-w-hit max-w-48 shrink cursor-pointer items-center gap-0.5 rounded-field pe-1 text-primary-text select-none ' +\n 'hover:text-primary-strong focus-visible:outline-hidden focus-visible:ring-4 focus-visible:ring-ring';\n\n/**\n * Back button for a `bf-header` start slot (or anywhere else in a page): pops the shown stack,\n * or replaces it with `defaultHref` when there's nothing to pop back to, e.g. a deep link that\n * landed on this page directly. Hidden when neither applies. Its anatomy follows the shell mode:\n * iOS, a chevron with the `label` as text; Material, a bare arrow icon button named by `label`.\n *\n * @example\n * <bf-back-button defaultHref=\"/tabs/today\" />\n * @summary Header back button that pops the page stack or falls back to a default route\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-back-button',\n imports: [BfIconButtonComponent, BfIconComponent],\n // Shrinkable inside a shrinkable `start` slot, so the text gives way before anything clips.\n host: { class: 'inline-flex min-w-0 shrink', '[attr.data-bf-mode]': 'mode' },\n template: `\n @if (visible()) {\n @if (mode === 'ios') {\n <button type=\"button\" [class]=\"iosClasses\" (click)=\"go()\">\n <bf-icon [icon]=\"chevronLeft\" size=\"lg\" />\n <span data-bf-back-text class=\"text-heading min-w-0 truncate font-normal\">{{\n name()\n }}</span>\n </button>\n } @else {\n <bf-icon-button variant=\"text\" [label]=\"name()\" (clicked)=\"go()\">\n <bf-icon [icon]=\"arrowLeft\" size=\"lg\" />\n </bf-icon-button>\n }\n }\n `,\n})\nexport class BfBackButtonComponent {\n private readonly nav = inject(BfNavController);\n private readonly labels = inject(BF_LABELS);\n\n /** Destination `nav.root()` navigates to when there's no page to pop back to. */\n readonly defaultHref = input('');\n /**\n * The previous page's name: the text beside the chevron in iOS mode, the arrow button's\n * accessible name in Material mode. Empty falls back to `provideBfLabels()`'s `back` (\"Back\").\n */\n readonly label = input('');\n\n /** The shell mode (`provideBfShell({ mode })`, or the platform's for `'auto'`). */\n protected readonly mode: BfShellMode = injectShellMode();\n protected readonly iosClasses = IOS_CLASSES;\n protected readonly chevronLeft = ChevronLeft;\n protected readonly arrowLeft = ArrowLeft;\n\n /** The rendered label: `label`, else the `back` label. */\n readonly name = computed(() => this.label() || this.labels.back);\n\n /** Shown when the stack has a page to go back to, or a `defaultHref` is set. */\n readonly visible = computed(() => this.nav.canGoBack() || this.defaultHref() !== '');\n\n /** Goes back one page, or to `defaultHref` when the stack has none to go back to. */\n go(): void {\n if (this.nav.canGoBack()) {\n this.nav.back();\n } else {\n void this.nav.root(this.defaultHref());\n }\n }\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Sets `touch-action: pan-x pan-y` (and a `data-bf-horizontal-scroll` marker) on a horizontally\n * scrolling element inside a `bf-stack-outlet` page. Not needed: `touch-action` is not inherited,\n * and a touch's effective value is the intersection of the values from the touched element up to\n * its nearest scroll container only, so an `overflow-x-auto` row (a chips row, a carousel) pans\n * on its own under the outlet's `touch-action: pan-y`, on iOS and on Android. Only the 50 px\n * left-edge zone belongs to the swipe-back, with or without the directive.\n *\n * @example\n * <div bfHorizontalScroll class=\"flex gap-2 overflow-x-auto\">\n * <bf-chip>All</bf-chip>\n * <bf-chip>Open</bf-chip>\n * </div>\n * @deprecated 0.3.0 use a plain overflow-x-auto scroller, which pans on its own; removed in 0.4.0\n * @category Navigation\n * @since 0.2.0\n * @summary Deprecated touch-action opt-in for horizontal scrollers in stack pages, not needed\n */\n@Directive({\n selector: '[bfHorizontalScroll]',\n host: { class: 'touch-pan-x touch-pan-y', '[attr.data-bf-horizontal-scroll]': '\"\"' },\n})\nexport class BfHorizontalScrollDirective {}\n","import {\n DOCUMENT,\n DestroyRef,\n Directive,\n ElementRef,\n Injector,\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n effect,\n inject,\n input,\n untracked,\n} from '@angular/core';\nimport { BfKeyboardService, isTextEntry } from '../../platform/keyboard.service';\n\n/** What the padding write needs from its measurement. */\ninterface KeyboardPadding {\n /** Keyboard height the paddings answer to; 0 while hidden or the assist is off. */\n readonly height: number;\n /** Gap kept above the keyboard, in px. */\n readonly margin: number;\n /** Part of the host below the keyboard's top edge, in px. */\n readonly covered: number;\n /** Whether the host is rendered visibly. */\n readonly shown: boolean;\n}\n\n/** Wait after arming when no `scroll` event arrives (the engine did not scroll on its own). */\nconst REVEAL_SETTLE_MS = 120;\n/** Quiet time after the scroller's last `scroll` event before the reveal or correction runs. */\nconst REVEAL_QUIET_MS = 100;\n/** Longest wait after arming, however long the scroller keeps scrolling. */\nconst REVEAL_MAX_WAIT_MS = 400;\n\n/**\n * How `bfKeyboardAssist` scrolls the focused control into view: `smooth` or `instant`.\n *\n * @summary Whether the keyboard assist scrolls a focused field into view smoothly or at once\n * @since 0.3.0\n */\nexport type BfKeyboardAssistBehavior = 'smooth' | 'instant';\n\n/**\n * Keeps the focused text control of a scroll container clear of the soft keyboard. On keyboard\n * will-show it pads the scroller by the part below the keyboard's top edge\n * (`BfKeyboardService.keyboardTop()`: `padding-bottom`, plus `scroll-padding-bottom` with the\n * margin for engine scrolls), then scrolls the focused `input`, `textarea` or `[contenteditable]`\n * up until it sits `bfKeyboardAssistMargin` px above the keyboard (or down until it sits the margin\n * below the scroller's top edge; in a scroller too short for the control and both margins, the gap\n * above the control shrinks first, then the one above the keyboard, so a control shorter than the\n * scroller stays whole; a taller one keeps its bottom at the keyboard top). The covered part is\n * measured again at did-show and on every window or `visualViewport` resize, so a WebView that\n * resizes for the keyboard (Android) gets no padding and a padding never outlives the keyboard,\n * even on a detached page. A hidden host (a parked page, `visibility: hidden`) is never padded or\n * scrolled. The reveal waits for the engine's own focus scroll to settle (100 ms without a `scroll`\n * event, 120 ms if none arrives, at most 400 ms), then scrolls once; one correction follows at\n * did-show once the scroller is quiet (skipped while a reveal is pending). Focus moving to another\n * field while the keyboard is open re-arms the reveal, and both paddings go away on hide.\n * `refresh()` measures and corrects again for a container that moved on its own. `bf-content`\n * applies it to its own scroller; use it directly only on a custom scroll container.\n *\n * @example\n * <div class=\"h-full overflow-y-auto\" bfKeyboardAssist [bfKeyboardAssistMargin]=\"32\">\n * <bf-input label=\"Name\" />\n * </div>\n * @category Navigation\n * @since 0.2.0\n * @summary Keeps the focused text field of a scroll container clear of the soft keyboard\n */\n@Directive({\n selector: '[bfKeyboardAssist]',\n})\nexport class BfKeyboardAssistDirective {\n /** Enables the assist; set it false for screens that manage the keyboard themselves (a chat composer). */\n readonly bfKeyboardAssist = input(true, { transform: booleanAttribute });\n /** Gap kept between the focused control and the top of the keyboard, in px. */\n readonly bfKeyboardAssistMargin = input(24);\n /** Scroll behaviour of the reveal. */\n readonly bfKeyboardAssistBehavior = input<BfKeyboardAssistBehavior>('smooth');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly keyboard = inject(BfKeyboardService);\n private settleCallback: (() => void) | null = null;\n private quietTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n private revealPending = false;\n private lastScrollAt = Number.NEGATIVE_INFINITY;\n\n constructor() {\n const onScroll = (): void => {\n this.lastScrollAt = Date.now();\n if (this.settleCallback) {\n this.restartQuietTimer(REVEAL_QUIET_MS);\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n // Field-to-field focus while open fires no will-show; the engine's own reveal follows.\n if (\n this.bfKeyboardAssist() &&\n this.keyboard.visible() &&\n isTextEntry(event.target as Element | null)\n ) {\n this.scheduleReveal();\n }\n };\n // Every resize measures the covered part again, keyboard up or not: a padded scroller whose\n // view is detached (a parked page) runs no effect on hide, but still ends at 0 here.\n const onResize = (): void => this.updatePadding();\n const view = this.document.defaultView;\n this.host.addEventListener('scroll', onScroll, { passive: true });\n this.host.addEventListener('focusin', onFocusIn);\n view?.addEventListener('resize', onResize);\n view?.visualViewport?.addEventListener('resize', onResize);\n inject(DestroyRef).onDestroy(() => {\n this.cancelReveal();\n this.host.removeEventListener('scroll', onScroll);\n this.host.removeEventListener('focusin', onFocusIn);\n view?.removeEventListener('resize', onResize);\n view?.visualViewport?.removeEventListener('resize', onResize);\n });\n\n // Paddings follow the keyboard height and are measured again once it has settled (did-show,\n // after Android's resize); hiding (or disabling) removes them and any pending reveal. The\n // host's own styles: measured in the early read phase, written in the write phase.\n afterRenderEffect({\n earlyRead: () => {\n this.keyboard.phase();\n return this.measurePadding();\n },\n write: (padding) => this.applyPadding(padding()),\n });\n\n // Every will-show schedules the delayed reveal, after the paddings above are in place.\n let lastWillShow = untracked(() => this.keyboard.willShow());\n effect(() => {\n const count = this.keyboard.willShow();\n if (count === lastWillShow) {\n return;\n }\n lastWillShow = count;\n untracked(() => {\n if (this.bfKeyboardAssist() && this.keyboard.visible()) {\n this.scheduleReveal();\n }\n });\n });\n\n // One correction once the keyboard has settled, after any scroll in progress has ended; a\n // pending reveal measures the settled rects itself, so the correction is skipped then. It is\n // measured in the early read phase and scrolled in the write phase, after this render's\n // padding write (registered earlier, so its write runs first); it re-checks that the keyboard\n // is still shown before it scrolls, here and after a deferral.\n const injector = inject(Injector);\n effect(() => {\n if (this.keyboard.phase() !== 'shown') {\n return;\n }\n untracked(() =>\n afterNextRender(\n {\n earlyRead: () => this.settledCorrection(),\n write: (delta) => {\n if (this.keyboard.phase() === 'shown') {\n this.scrollReveal(delta, 'instant');\n }\n },\n },\n { injector },\n ),\n );\n });\n }\n\n /**\n * The did-show correction's measurement: the scroll it needs now, or 0 when it is skipped (a\n * reveal is pending, the assist is off, the keyboard is no longer shown) or deferred until the\n * scroller is quiet.\n */\n private settledCorrection(): number {\n if (this.keyboard.phase() !== 'shown' || !this.bfKeyboardAssist() || this.revealPending) {\n return 0;\n }\n const sinceScroll = Date.now() - this.lastScrollAt;\n if (sinceScroll < REVEAL_QUIET_MS) {\n this.waitForQuiet(REVEAL_QUIET_MS - sinceScroll, () => {\n if (this.keyboard.phase() === 'shown') {\n this.reveal('instant');\n }\n });\n return 0;\n }\n this.cancelReveal();\n return this.revealDelta();\n }\n\n /**\n * Measures the covered part again and, while the keyboard is up, corrects the reveal once\n * (instantly, after any scroll in progress). For a container that moves with the keyboard\n * outside the keyboard events — `bf-sheet` calls it once its lift above the keyboard has ended.\n */\n refresh(): void {\n this.updatePadding();\n if (this.bfKeyboardAssist() && this.keyboard.visible() && !this.revealPending) {\n this.whenScrollSettled(() => this.reveal('instant'));\n }\n }\n\n /**\n * False for a host that is not rendered visibly. A parked page is `visibility: hidden`, which\n * `checkVisibility()` ignores unless asked with `visibilityProperty`.\n */\n private hostVisible(): boolean {\n return this.host.checkVisibility?.({ visibilityProperty: true }) !== false;\n }\n\n /** Part of the host below the keyboard's top edge, in px; 0 while hidden or not applicable. */\n private covered(height: number): number {\n if (height <= 0 || !this.hostVisible()) {\n return 0;\n }\n const keyboardTop = this.keyboard.keyboardTop();\n return Math.max(0, Math.round(this.host.getBoundingClientRect().bottom - keyboardTop));\n }\n\n /** Measures and applies the paddings at once, outside change detection (resize, `refresh()`). */\n private updatePadding(): void {\n this.applyPadding(this.measurePadding());\n }\n\n /** The keyboard height the paddings answer to and the part of the host it covers. */\n private measurePadding(): KeyboardPadding {\n const visible = this.keyboard.visible() ? this.keyboard.height() : 0;\n const height = this.bfKeyboardAssist() ? visible : 0;\n return {\n height,\n margin: this.bfKeyboardAssistMargin(),\n covered: this.covered(height),\n shown: this.hostVisible(),\n };\n }\n\n private applyPadding({ height, margin, covered, shown }: KeyboardPadding): void {\n if (covered > 0) {\n this.host.style.setProperty('padding-bottom', `${covered}px`);\n this.host.style.setProperty('scroll-padding-bottom', `${covered + margin}px`);\n } else {\n this.host.style.removeProperty('padding-bottom');\n this.host.style.removeProperty('scroll-padding-bottom');\n }\n if (height <= 0 || !shown) {\n this.cancelReveal();\n }\n }\n\n /**\n * Arms a single-shot reveal: it runs once the scroller has been quiet for `REVEAL_QUIET_MS`\n * after its last `scroll` event, `REVEAL_SETTLE_MS` after arming if none arrives, and at most\n * `REVEAL_MAX_WAIT_MS` after arming.\n */\n private scheduleReveal(): void {\n this.waitForQuiet(REVEAL_SETTLE_MS, () => this.reveal(this.bfKeyboardAssistBehavior()));\n this.revealPending = true;\n }\n\n /** Runs `callback` now if the scroller is quiet, else once it has been quiet for `REVEAL_QUIET_MS`. */\n private whenScrollSettled(callback: () => void): void {\n const sinceScroll = Date.now() - this.lastScrollAt;\n if (sinceScroll >= REVEAL_QUIET_MS) {\n this.cancelReveal();\n callback();\n return;\n }\n this.waitForQuiet(REVEAL_QUIET_MS - sinceScroll, callback);\n }\n\n private waitForQuiet(initialWait: number, callback: () => void): void {\n this.cancelReveal();\n this.settleCallback = callback;\n this.restartQuietTimer(initialWait);\n this.maxWaitTimer = setTimeout(() => this.finishSettle(), REVEAL_MAX_WAIT_MS);\n }\n\n private restartQuietTimer(wait: number): void {\n if (this.quietTimer !== null) {\n clearTimeout(this.quietTimer);\n }\n this.quietTimer = setTimeout(() => this.finishSettle(), wait);\n }\n\n private finishSettle(): void {\n const callback = this.settleCallback;\n this.cancelReveal();\n callback?.();\n }\n\n /** Drops a pending reveal or correction and its timers. */\n private cancelReveal(): void {\n this.settleCallback = null;\n this.revealPending = false;\n if (this.quietTimer !== null) {\n clearTimeout(this.quietTimer);\n this.quietTimer = null;\n }\n if (this.maxWaitTimer !== null) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n\n /** Scrolls the focused text control so that its bottom plus the margin clears the keyboard top. */\n private reveal(behavior: 'smooth' | 'instant'): void {\n this.scrollReveal(this.revealDelta(), behavior);\n }\n\n /** How far the scroller must scroll for the focused text control to clear the keyboard; 0 when\n * nothing needs revealing. */\n private revealDelta(): number {\n const active = this.document.activeElement;\n if (\n !isTextEntry(active) ||\n !this.host.contains(active) ||\n !this.keyboard.visible() ||\n !this.hostVisible()\n ) {\n return 0;\n }\n const scroller = this.host.getBoundingClientRect();\n const keyboardTop = Math.min(scroller.bottom, this.keyboard.keyboardTop());\n const rect = active.getBoundingClientRect();\n const margin = this.bfKeyboardAssistMargin();\n // A scroller too short for the control and both margins (a keyboard-capped sheet or modal in\n // phone landscape) shrinks the gaps, the one above the control first, so a control shorter\n // than the scroller stays whole; the gap above the keyboard is the last to go.\n const room = Math.max(0, keyboardTop - scroller.top - rect.height);\n const gapBelow = Math.min(margin, room);\n const gapAbove = Math.min(margin, room - gapBelow);\n if (rect.bottom + gapBelow > keyboardTop) {\n return rect.bottom + gapBelow - keyboardTop;\n }\n if (rect.top - gapAbove < scroller.top) {\n return rect.top - gapAbove - scroller.top;\n }\n return 0;\n }\n\n private scrollReveal(delta: number, behavior: 'smooth' | 'instant'): void {\n if (delta === 0) {\n return;\n }\n if (behavior === 'smooth') {\n // Our own smooth scroll counts as scrolling, so a did-show correction waits for it.\n this.lastScrollAt = Date.now();\n }\n this.host.scrollBy({ top: delta, behavior });\n }\n}\n","import {\n DOCUMENT,\n Directive,\n ElementRef,\n booleanAttribute,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { DURATION_MD, SETTLE_EASING } from '../../motion';\nimport { prefersReducedMotion } from '../../navigation/transitions';\n\n/** Largest extra vertical scale of the stretched content (0.14 = 14 %). */\nconst STRETCH_MAX = 0.14;\n/** Pull distance (px) over which the stretch approaches its maximum (1 − 1/e of it at 240 px). */\nconst STRETCH_DISTANCE = 240;\n/** Finger travel (px) from the start before the gesture's axis is decided. */\nconst AXIS_SLOP = 8;\n\ninterface BfStretchGesture {\n /** `Touch.identifier` of the finger that started the gesture; other fingers are ignored. */\n readonly touchId: number;\n readonly startX: number;\n readonly startY: number;\n readonly topOwned: boolean;\n readonly body: HTMLElement;\n lastY: number;\n pull: number;\n axis: 'x' | 'y' | undefined;\n origin: 'top' | 'bottom';\n}\n\nfunction stretchScale(pull: number): number {\n return 1 + STRETCH_MAX * (1 - Math.exp(-pull / STRETCH_DISTANCE));\n}\n\n/** The touch with `identifier` in `list`, if that finger is in it. */\nfunction findTouch(list: TouchList, identifier: number): Touch | undefined {\n return Array.from(list).find((touch) => touch.identifier === identifier);\n}\n\nfunction clearStretch(body: HTMLElement): void {\n body.style.removeProperty('transform');\n body.style.removeProperty('transform-origin');\n body.style.removeProperty('will-change');\n}\n\n/**\n * Android-style edge stretch for a scroll container: a vertical touch drag past the top or bottom\n * edge scales the container's first element child (the content body) up along the y axis from\n * that edge — `scaleY(1 + 0.14 × (1 − e^(−pull / 240)))` — and springs it back on release (220 ms,\n * the settle curve; instant under `prefers-reduced-motion`). Touch only, with passive listeners:\n * it never prevents a touch, so scrolling is untouched. It follows the finger that started the\n * gesture and ignores any other. A horizontal-dominant gesture (decided after 8 px) is ignored,\n * and the top edge is left alone while an active `bf-refresher` (`[data-bf-refresher]`) inside\n * the container owns the top pull. `bf-content` applies it to its\n * scroller in Material mode, where the WebView draws no overscroll effect of its own.\n *\n * @example\n * <div class=\"h-full overflow-y-auto\" bfOverscrollStretch>\n * <div>…</div>\n * </div>\n * @category Navigation\n * @since 0.2.0\n * @summary Android-style stretch of a scroll container dragged past its top or bottom edge\n */\n@Directive({\n selector: '[bfOverscrollStretch]',\n})\nexport class BfOverscrollStretchDirective {\n /** Enables the stretch; false leaves the container inert. */\n readonly bfOverscrollStretch = input(true, { transform: booleanAttribute });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private gesture: BfStretchGesture | null = null;\n private settle: { readonly animation: Animation; readonly body: HTMLElement } | null = null;\n\n constructor() {\n effect((onCleanup) => {\n if (!this.bfOverscrollStretch()) {\n return;\n }\n const host = this.host;\n const options: AddEventListenerOptions = { passive: true };\n host.addEventListener('touchstart', this.onTouchStart, options);\n host.addEventListener('touchmove', this.onTouchMove, options);\n host.addEventListener('touchend', this.onTouchEnd, options);\n host.addEventListener('touchcancel', this.onTouchEnd, options);\n onCleanup(() => {\n host.removeEventListener('touchstart', this.onTouchStart);\n host.removeEventListener('touchmove', this.onTouchMove);\n host.removeEventListener('touchend', this.onTouchEnd);\n host.removeEventListener('touchcancel', this.onTouchEnd);\n this.stopSettle();\n if (this.gesture) {\n clearStretch(this.gesture.body);\n this.gesture = null;\n }\n });\n });\n }\n\n private readonly onTouchStart = (event: TouchEvent): void => {\n const touch = event.touches[0];\n const body = this.host.firstElementChild;\n if (this.gesture || !touch || !(body instanceof HTMLElement)) {\n return;\n }\n // A new touch takes over from a spring-back still running.\n this.stopSettle();\n this.gesture = {\n touchId: touch.identifier,\n startX: touch.clientX,\n startY: touch.clientY,\n topOwned: this.host.querySelector('[data-bf-refresher]') !== null,\n body,\n lastY: touch.clientY,\n pull: 0,\n axis: undefined,\n origin: 'top',\n };\n };\n\n private readonly onTouchMove = (event: TouchEvent): void => {\n const gesture = this.gesture;\n const touch = gesture ? findTouch(event.touches, gesture.touchId) : undefined;\n if (!gesture || !touch || gesture.axis === 'x') {\n return;\n }\n if (gesture.axis === undefined) {\n const dx = touch.clientX - gesture.startX;\n const dy = touch.clientY - gesture.startY;\n if (Math.hypot(dx, dy) < AXIS_SLOP) {\n return;\n }\n // Horizontal-dominant: a horizontal scroller's or a swipe's gesture, never a stretch.\n gesture.axis = Math.abs(dx) > Math.abs(dy) ? 'x' : 'y';\n if (gesture.axis === 'x') {\n return;\n }\n }\n const dy = touch.clientY - gesture.lastY;\n gesture.lastY = touch.clientY;\n if (dy === 0) {\n return;\n }\n const host = this.host;\n const atTop = host.scrollTop <= 0;\n const atBottom = host.scrollTop >= host.scrollHeight - host.clientHeight - 1;\n const previous = gesture.pull;\n const reversing = gesture.pull > 0 && (gesture.origin === 'top' ? dy < 0 : dy > 0);\n if (reversing) {\n gesture.pull = Math.max(0, gesture.pull - Math.abs(dy));\n } else if (atTop && dy > 0 && !gesture.topOwned) {\n gesture.pull += dy;\n gesture.origin = 'top';\n } else if (atBottom && dy < 0) {\n gesture.pull += -dy;\n gesture.origin = 'bottom';\n } else if (!atTop && !atBottom) {\n gesture.pull = 0;\n }\n if (previous > 0 || gesture.pull > 0) {\n this.apply(gesture);\n }\n };\n\n private readonly onTouchEnd = (event: TouchEvent): void => {\n const gesture = this.gesture;\n // The gesture ends when the finger that started it lifts, whatever other fingers stay down.\n if (!gesture || findTouch(event.touches, gesture.touchId)) {\n return;\n }\n this.gesture = null;\n const body = gesture.body;\n if (gesture.pull <= 0 || prefersReducedMotion(this.document.defaultView)) {\n clearStretch(body);\n return;\n }\n const animation = body.animate(\n [{ transform: `scaleY(${stretchScale(gesture.pull)})` }, { transform: 'scaleY(1)' }],\n { duration: DURATION_MD, easing: SETTLE_EASING, fill: 'forwards' },\n );\n this.settle = { animation, body };\n void animation.finished.then(\n () => {\n if (this.settle?.animation !== animation) {\n return;\n }\n this.settle = null;\n clearStretch(body);\n animation.cancel();\n },\n () => {\n // Cancelled by a newer touch, which reset the body itself.\n },\n );\n };\n\n private apply(gesture: BfStretchGesture): void {\n const style = gesture.body.style;\n style.willChange = 'transform';\n style.transformOrigin = `center ${gesture.origin}`;\n style.transform = `scaleY(${stretchScale(gesture.pull)})`;\n }\n\n private stopSettle(): void {\n const settle = this.settle;\n if (!settle) {\n return;\n }\n this.settle = null;\n settle.animation.cancel();\n clearStretch(settle.body);\n }\n}\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n type ElementRef,\n viewChild,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { BfPlatformService } from '../../platform/platform.service';\nimport { BfKeyboardAssistDirective } from '../keyboard-assist/keyboard-assist.directive';\nimport { BfOverscrollStretchDirective } from '../overscroll-stretch/overscroll-stretch.directive';\n\n/* The focus ring shows only when the scroller is a tab stop (a keyboard can scroll a page with\n nothing focusable in it), inset because the scroller fills the page. */\nconst SCROLLER_CLASSES =\n 'h-full overflow-y-auto overscroll-contain touch-pan-y pl-safe-left pr-safe-right pb-[calc(var(--bf-tab-bar-inset,0px)+var(--bf-header-collapse,0px))] focus-visible:ring-4 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-hidden';\n/** What a keyboard can reach by Tab inside the scroller. */\nconst TABBABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(',');\n\n/**\n * True when `scroller` holds a control a keyboard can Tab to: one that is not `hidden`, not inside\n * an `inert` or `hidden` subtree and rendered (`checkVisibility()`, where the engine has it).\n */\nfunction hasTabbable(scroller: HTMLElement): boolean {\n for (const el of Array.from(scroller.querySelectorAll<HTMLElement>(TABBABLE))) {\n if (el.closest('[inert], [hidden]')) {\n continue;\n }\n if (\n typeof el.checkVisibility === 'function' &&\n !el.checkVisibility({ visibilityProperty: true })\n ) {\n continue;\n }\n return true;\n }\n return false;\n}\n\n/**\n * How far the content must overflow the scroller to count as scrolling: more than the 1 px the iOS\n * body always adds for the rubber band (`min-h-[calc(100%+1px)]`), with room for subpixel rounding.\n */\nconst OVERFLOW_PX = 2;\n/** The scroller's top: a header's height, the status bar (`fullscreen`), or nothing (`fullscreen`\n * inside a page-sheet modal, which already sits below the status bar). */\nconst TOP_CLASSES: Record<'header' | 'fullscreen' | 'fullscreenBelowStatusBar', string> = {\n header: 'pt-(--bf-header-height,0px) scroll-pt-(--bf-header-height,0px)',\n fullscreen: 'pt-safe-top',\n fullscreenBelowStatusBar: '',\n};\n/** iOS: the body always overflows the scroller by 1 px, so WebKit rubber-bands short pages too. */\nconst IOS_BODY_CLASSES = 'min-h-[calc(100%+1px)]';\n\n/**\n * True in iOS WebKit (Safari, a home-screen app, an in-app browser), including an iPad that reports\n * itself as a Mac: Capacitor reports platform `web` there, so the mode is Material, yet WebKit\n * rubber-bands the scroller natively. False without a `navigator` (SSR).\n */\nfunction isIosWebKit(navigator: Navigator | undefined): boolean {\n if (!navigator) {\n return false;\n }\n return (\n /iP(hone|ad|od)/.test(navigator.userAgent) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n );\n}\n\n/**\n * The payload of `bf-content`'s `scrolled` output: the scroller's `scrollTop` after the scroll.\n *\n * @summary What a content scroll event carries: the scroller's current top offset\n * @since 0.3.0\n */\nexport interface BfContentScrollEvent {\n /** The scroller's `scrollTop`, in px. */\n scrollTop: number;\n}\n\n/**\n * The page's single scroll container: fills the rest of `bf-page` under the header, keeps the\n * focused text control clear of the soft keyboard (`bfKeyboardAssist`), pads for the horizontal\n * safe areas, for the floating tab bar (`--bf-tab-bar-inset`), for a `translucent` header drawn\n * over it (`--bf-header-height`, from `BfPageContext`) and at the bottom for the height a solid\n * `bf-header` has collapsed by (`--bf-header-collapse`, written by the header, so the scroll range\n * stays the same while the large title collapses), and contains overscroll so the document\n * never scrolls. On iOS (`ios.scrollEnabled: false`) it is the only thing that scrolls. The\n * projected content sits in a block body inside the scroller: in iOS mode the body is 1 px taller\n * than the scroller, so WebKit rubber-bands even a short page; in Material mode the scroller gets\n * `bfOverscrollStretch`, the Android edge stretch the WebView does not draw itself, except in iOS\n * WebKit (Safari or a home-screen app, platform `web`), which rubber-bands natively. Both follow the\n * engine (`BfPlatformService.mode()`), not `provideBfShell({ mode })`. In the page a\n * `bf-stack-outlet` shows, the scroller is the document's `main` landmark (`role=\"main\"`; not in a\n * `bf-modal`, see `BfPageContext.mainLandmark`). While it overflows and holds nothing focusable,\n * it is a tab stop (`tabindex=\"0\"`), so a keyboard can scroll it in Safari too (axe\n * `scrollable-region-focusable`); otherwise Tab reaches its controls and the arrow keys scroll it.\n *\n * @example\n * <bf-page>\n * <bf-content>\n * <bf-list>…</bf-list>\n * </bf-content>\n * </bf-page>\n * @summary Page's single scroll container that keeps focused fields clear of the soft keyboard\n * @category Navigation\n * @since 0.2.0\n * @slot default The page content, in the scroll container.\n */\n@Component({\n selector: 'bf-content',\n imports: [BfKeyboardAssistDirective, BfOverscrollStretchDirective],\n host: { class: 'block min-h-0 flex-1' },\n template: `<div\n #scroller\n [class]=\"scrollerClasses()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"tabStop() ? 0 : null\"\n [bfKeyboardAssist]=\"keyboardAssist()\"\n [bfOverscrollStretch]=\"stretch()\"\n >\n <div [class]=\"bodyClasses()\"><ng-content /></div>\n </div>`,\n})\nexport class BfContentComponent {\n /** Extends the content under the status bar: adds the top safe-area padding (no header above). */\n readonly fullscreen = input(false, { transform: booleanAttribute });\n /** Keeps the focused text control clear of the soft keyboard; false for screens that handle it themselves. */\n readonly keyboardAssist = input(true, { transform: booleanAttribute });\n /** Emits `scrolled` on every scroll; off by default so plain pages add no listener. */\n readonly scrollEvents = input(false, { transform: booleanAttribute });\n\n /** The scroller's `scrollTop` on each scroll, only while `scrollEvents` is true. */\n readonly scrolled = output<BfContentScrollEvent>();\n\n private readonly scroller = viewChild.required<ElementRef<HTMLElement>>('scroller');\n private readonly pageContext = inject(BfPageContext, { optional: true });\n private readonly platform = inject(BfPlatformService);\n\n private readonly iosWebKit = isIosWebKit(inject(DOCUMENT).defaultView?.navigator);\n\n protected readonly ios = computed(() => this.platform.mode() === 'ios');\n /** The edge stretch only where nothing rubber-bands natively: never in iOS mode or iOS WebKit. */\n protected readonly stretch = computed(() => !this.ios() && !this.iosWebKit);\n protected readonly bodyClasses = computed(() => (this.ios() ? IOS_BODY_CLASSES : ''));\n\n /** The shown page's content is the document's `main` landmark; a modal's is not. */\n protected readonly role = computed(() => (this.pageContext?.mainLandmark() ? 'main' : null));\n /** True while the scroller overflows and holds nothing a keyboard can Tab to. */\n protected readonly tabStop = signal(false);\n\n /** A page-sheet modal already sits below the status bar (`BfPageContext.safeAreaTop`). */\n private readonly safeAreaTop = computed(() => this.pageContext?.safeAreaTop() ?? true);\n protected readonly scrollerClasses = computed(\n () =>\n `${SCROLLER_CLASSES} ${TOP_CLASSES[this.fullscreen() ? (this.safeAreaTop() ? 'fullscreen' : 'fullscreenBelowStatusBar') : 'header']}`,\n );\n\n constructor() {\n // A passive native listener, attached only when asked for, so scrolling never schedules\n // change detection on pages that do not listen.\n effect((onCleanup) => {\n if (!this.scrollEvents()) {\n return;\n }\n const element = this.scroller().nativeElement;\n const listener = (): void => this.scrolled.emit({ scrollTop: element.scrollTop });\n element.addEventListener('scroll', listener, { passive: true });\n onCleanup(() => element.removeEventListener('scroll', listener));\n });\n\n this.watchKeyboardReach();\n\n if (this.pageContext) {\n const context = this.pageContext;\n let registered: HTMLElement | null = null;\n // The scroller is a static element of this view, so it is registered once, after the first\n // render, for the page's header and the stack outlet to find.\n afterNextRender(() => {\n registered = this.scroller().nativeElement;\n context.scroller.set(registered);\n });\n // A translucent `bf-header` is drawn over the content: start the content below it. The\n // scroller is this component's own DOM, so it is written in the render phase.\n afterRenderEffect({\n write: () => {\n const element = this.scroller().nativeElement;\n const height = context.headerHeight();\n if (height > 0) {\n element.style.setProperty('--bf-header-height', `${height}px`);\n } else {\n element.style.removeProperty('--bf-header-height');\n }\n },\n });\n inject(DestroyRef).onDestroy(() => {\n if (registered && context.scroller() === registered) {\n context.scroller.set(null);\n }\n });\n }\n }\n\n /**\n * Keeps `tabStop` current: re-checked once per frame after the scroller or its content resizes\n * or the content changes (rows added, a control enabled, shown, hidden or made inert). Hidden,\n * inert and unrendered controls do not count.\n */\n private watchKeyboardReach(): void {\n let frame: number | null = null;\n let resize: ResizeObserver | null = null;\n let mutations: MutationObserver | null = null;\n const check = (): void => {\n frame = null;\n const element = this.scroller().nativeElement;\n this.tabStop.set(\n element.scrollHeight > element.clientHeight + OVERFLOW_PX && !hasTabbable(element),\n );\n };\n const schedule = (): void => {\n frame ??= requestAnimationFrame(check);\n };\n afterNextRender(() => {\n const element = this.scroller().nativeElement;\n check();\n if (typeof ResizeObserver === 'function') {\n resize = new ResizeObserver(schedule);\n resize.observe(element);\n const body = element.firstElementChild;\n if (body) {\n resize.observe(body);\n }\n }\n mutations = new MutationObserver(schedule);\n mutations.observe(element, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\n 'disabled',\n 'tabindex',\n 'href',\n 'contenteditable',\n 'type',\n 'hidden',\n 'inert',\n ],\n });\n });\n inject(DestroyRef).onDestroy(() => {\n resize?.disconnect();\n mutations?.disconnect();\n if (frame !== null) {\n cancelAnimationFrame(frame);\n }\n });\n }\n\n /** The scroll container element, for code that needs to read or drive it directly. */\n get scrollElement(): HTMLElement {\n return this.scroller().nativeElement;\n }\n\n /**\n * Scrolls back to the top (e.g. on a second tap of the active tab).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollToTop(behavior: ScrollBehavior = 'smooth'): void {\n this.scrollElement.scrollTo({ top: 0, behavior });\n }\n\n /**\n * Scrolls to the given offset in px.\n * @param x The horizontal offset (px).\n * @param y The vertical offset (px).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollToPoint(x: number, y: number, behavior: ScrollBehavior = 'smooth'): void {\n this.scrollElement.scrollTo({ left: x, top: y, behavior });\n }\n}\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n afterRenderEffect,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { type BfShellMode } from '../../platform/platform.service';\nimport { injectShellMode } from '../../shell/provide-bf-shell';\n\n/**\n * Scroll offset (px) over which the large title collapses before it is measured (and the range a\n * header without one would use): the collapse runs over the large title block's rendered height.\n */\nconst COLLAPSE_FALLBACK = 56;\n/** The collapse range's bounds (px): at least the iOS bar's height, at most 200 px however large\n * the title grows with the text size. */\nconst COLLAPSE_MIN = 44;\nconst COLLAPSE_MAX = 200;\n/** Compact heights (phone landscape, split screen): no large title, and a 44 px bar in both modes. */\nconst COMPACT_QUERY = '(max-height: 500px)';\n/** Quiet time after the scroller's last `scroll` event that counts as scroll end (no `scrollend`). */\nconst SNAP_QUIET_MS = 120;\n/** The toolbar row's horizontal padding (`px-2`), the slots' distance from the bar's edges (px). */\nconst ROW_PADDING = 8;\n/** Space kept between the centred title and the wider slot (px). */\nconst TITLE_GAP = 8;\n/** The smallest the iOS `start` slot is capped to: one 44 px hit area (a back button's chevron). */\nconst START_MIN = 44;\n/**\n * The `start` slot per mode. iOS: capped by `--bf-header-start-max`, so a wide back button gives\n * way (its text truncates, then disappears) before the absolutely positioned centred title is\n * squeezed; Material: the row's flow already shrinks it.\n */\nconst START_SLOT_CLASSES: Record<BfShellMode, string> = {\n ios: 'flex min-w-0 max-w-(--bf-header-start-max,none) shrink items-center gap-1',\n md: 'flex min-w-0 shrink items-center gap-1',\n};\n\ntype BfHeaderLayout = 'solid' | 'translucent';\n/** In the page's column, or drawn over the content (`bf-content` pads its scroller by the header). */\nconst LAYOUT_CLASSES: Record<BfHeaderLayout, string> = {\n solid: 'relative block shrink-0',\n translucent: 'absolute inset-x-0 top-0 z-(--bf-z-sticky) block',\n};\n/**\n * The bar's surface at rest and once content scrolls under it (`data-bf-scrolled`): iOS turns\n * translucent with a blur and shows a 1 px hairline under the whole header (the host's `::after`,\n * so showing it never changes the header's height); Material takes a tonal tint, no blur and\n * never a hairline.\n */\nconst SURFACE_CLASSES: Record<BfShellMode, string> = {\n ios:\n 'bg-bg data-bf-scrolled:bg-bg/85 data-bf-scrolled:backdrop-blur-xl ' +\n \"after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border after:opacity-0 after:content-[''] data-bf-scrolled:after:opacity-100\",\n md: 'bg-bg data-bf-scrolled:bg-surface-muted motion-safe:transition-colors motion-safe:duration-150',\n};\n/**\n * The persistent bar: at least 44 px in iOS mode, 64 px in Material mode and 44 px in both at\n * compact heights, and never shorter than its title (`--bf-header-title-height`, measured: the iOS\n * title box is absolutely positioned, so a wrapped title would not grow the bar by itself). It\n * grows with its content: larger text and taller slots.\n */\ntype BfHeaderBar = BfShellMode | 'compact';\nconst BAR_CLASSES: Record<BfHeaderBar, string> = {\n ios: 'min-h-[max(--spacing(11),var(--bf-header-title-height,0px))]',\n md: 'min-h-[max(--spacing(16),var(--bf-header-title-height,0px))]',\n compact: 'min-h-[max(--spacing(11),var(--bf-header-title-height,0px))]',\n};\n/**\n * The large title block's exit, inside the collapsing clip and driven by the scroll position (no\n * transition): it fades out (`1 − progress`) and rises 8 px (`progress × 8px`), so it reads as\n * sliding under the bar rather than being cut off. Reduced motion keeps the fade, drops the rise.\n */\nconst LARGE_BLOCK_CLASSES =\n 'bf-large px-gutter pb-2 opacity-[calc(1_-_var(--bf-header-progress,0))] motion-safe:-translate-y-[calc(var(--bf-header-progress,0)_*_8px)]';\n/** iOS centres the title on the bar; Material leads with it after the `start` slot. */\nconst TITLE_BOX_CLASSES: Record<BfShellMode, string> = {\n ios: 'pointer-events-none absolute inset-y-0 left-(--bf-header-title-inset,16px) right-(--bf-header-title-inset,16px) flex items-center justify-center',\n md: 'flex min-w-0 flex-1 items-center px-2',\n};\n/**\n * 18 px `text-heading`: semibold in iOS mode (17 pt), regular in Material mode (22 dp title). A\n * long title (or a large text size) wraps, anywhere in a long word, to at most two lines.\n */\nconst TITLE_TEXT_CLASSES: Record<BfShellMode, string> = {\n ios: 'text-heading min-w-0 line-clamp-2 text-center wrap-anywhere font-semibold',\n md: 'text-heading min-w-0 line-clamp-2 wrap-anywhere font-normal',\n};\n/**\n * The large title: `text-display` (40/44 px), its font and line height capped at 200 % (80/88 px),\n * so the largest iOS text sizes (the root scaled up to 235 %) still leave room for the page. In px,\n * since Dynamic Type scales the root and with it every rem.\n */\nconst LARGE_TITLE_CLASSES =\n 'text-display text-[length:min(var(--text-display),80px)] leading-[min(var(--text-display--line-height),88px)] wrap-anywhere';\n\n/**\n * The page header: a persistent bar (`start` | title | `end`) and, with `largeTitle` (the\n * default), the Meadow Sage large title below it that collapses into the bar's title as the\n * page's `bf-content` scrolls. The bar follows the shell mode (`provideBfShell({ mode })`, the\n * platform's for `'auto'`): iOS, at least 44 px with a semibold title centred on the bar whatever\n * the slots hold (symmetric insets of the wider slot's width plus 8 px, measured with a\n * `ResizeObserver`); Material, at least 64 px with a regular title leading after the `start` slot.\n * The bar grows with its content at large text sizes: the title wraps to at most two lines (the\n * bar is never shorter than the measured title, `--bf-header-title-height`), the `start` slot may\n * shrink (a back button drops its text before its chevron; in iOS mode capped by\n * `--bf-header-start-max` so the centred title keeps up to half the bar) and the `end` slot never\n * does. At\n * compact heights (`(max-height: 500px)`, phone landscape) it renders no large title: the title is\n * the bar's `h1`, the bar is 44 px in both modes and nothing collapses. Once content scrolls under\n * it (the large title fully collapsed; without one, `scrollTop > 0`, overscroll excluded) the\n * host gets `data-bf-scrolled`: iOS turns translucent with a blur and a 1 px hairline, Material\n * takes a tonal tint. Reads its sibling scroller through the page's `BfPageContext` (set by\n * `bf-content`) and, from a passive scroll listener, writes `--bf-header-progress` (0…1, over the\n * large title block's rendered height, bounded to 44–200 px (a taller title collapses faster), of the scroll position clamped to the\n * scrollable range, so overscroll never moves it, and 0 on a page that cannot scroll that far) on\n * its host and the large title block's height (its natural height, measured once and on resize,\n * times `1 − progress`), so at progress 1 the header is exactly the toolbar; scrolling schedules\n * no Angular change detection. Solid, it also writes the collapsed height as\n * `--bf-header-collapse` on the scroller, which `bf-content` adds to its bottom padding: the\n * scroller grows by what the header gives up, so its scroll range stays the same through the\n * collapse and the short-page rule reads one stable value. When a scroll ends part-way\n * (`scrollend`, or 120 ms without a `scroll` event) with nothing pressed on the scroller, it snaps\n * the scroller to fully expanded or fully collapsed, whichever is nearer, like UIKit. Solid\n * (default), it sits in the page's flow above the content; `translucent`, it is drawn over the\n * content and `bf-content` pads its scroller by the header's height (`--bf-header-height`).\n *\n * @example\n * <bf-header title=\"Today\" subtitle=\"Wed, 23 Sep\">\n * <bf-icon-button bfSlot=\"end\" variant=\"text\" label=\"Profile\">\n * <bf-icon [icon]=\"userIcon\" />\n * </bf-icon-button>\n * </bf-header>\n * @summary Page header toolbar with a large title that collapses into the toolbar on scroll\n * @category Navigation\n * @since 0.2.0\n * @slot start Toolbar buttons at the start, such as `bf-back-button`.\n * @slot default The toolbar title, when the `title` input is empty.\n * @slot end Toolbar buttons at the end.\n */\n@Component({\n selector: 'bf-header',\n host: { '[class]': 'hostClasses()', '[attr.data-bf-mode]': 'mode' },\n template: `\n <header class=\"bf-header relative flex flex-col\">\n <div #toolbar [class]=\"toolbarClasses()\">\n <div [class]=\"barClasses()\">\n <div #startSlot [class]=\"startSlotClasses\">\n <ng-content select=\"[bfSlot=start]\" />\n </div>\n <div [class]=\"titleBoxClasses\">\n @if (mode === 'ios' && title()) {\n <!-- The title's one-line width, measured to cap the start slot; never shown. -->\n <span class=\"invisible absolute h-0 w-0 overflow-hidden\" aria-hidden=\"true\"\n ><span\n #titleSizer\n class=\"text-heading inline-block font-semibold whitespace-nowrap\"\n >{{ title() }}</span\n ></span\n >\n }\n @if (showLarge()) {\n <span\n #titleText\n class=\"bf-small opacity-(--bf-header-progress,0) {{ titleTextClasses }}\"\n aria-hidden=\"true\"\n >{{ title() }}</span\n >\n } @else {\n <h1 #titleText data-bf-page-title class=\"pointer-events-auto {{ titleTextClasses }}\">\n @if (title()) {\n {{ title() }}\n } @else {\n <ng-content />\n }\n </h1>\n }\n </div>\n <div #endSlot class=\"ms-auto flex shrink-0 items-center gap-1\">\n <ng-content select=\"[bfSlot=end]\" />\n </div>\n </div>\n </div>\n @if (showLarge()) {\n <div #largeClip class=\"overflow-hidden\">\n <div #largeBlock [class]=\"largeBlockClasses\">\n @if (subtitle()) {\n <p class=\"text-caption text-text-muted\">{{ subtitle() }}</p>\n }\n <h1 data-bf-page-title [class]=\"largeTitleClasses\">\n @if (title()) {\n {{ title() }}\n } @else {\n <ng-content />\n }\n </h1>\n </div>\n </div>\n }\n </header>\n `,\n})\nexport class BfHeaderComponent {\n /** The page title, rendered in the toolbar and, with `largeTitle`, in the large title block. */\n readonly title = input('');\n /** Eyebrow line above the large title (e.g. a date); rendered only with `largeTitle`. */\n readonly subtitle = input('');\n /**\n * Renders the collapsing large title block below the toolbar; never at compact heights\n * (`(max-height: 500px)`), where the title is the bar's `h1`.\n */\n readonly largeTitle = input(true, { transform: booleanAttribute });\n /**\n * Draws the header over the content (`absolute`) instead of in the page's flow, so content\n * scrolls under its scroll-under surface; `bf-content` pads its scroller by the header's height.\n */\n readonly translucent = input(false, { transform: booleanAttribute });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly pageContext = inject(BfPageContext, { optional: true });\n /** The status-bar band, unless the page sits below the status bar already (a page-sheet modal). */\n protected readonly toolbarClasses = computed(() =>\n (this.pageContext?.safeAreaTop() ?? true) ? 'pt-safe-top' : '',\n );\n private readonly keyboard = inject(BfKeyboardService);\n private readonly document = inject(DOCUMENT);\n private readonly toolbar = viewChild.required<ElementRef<HTMLElement>>('toolbar');\n private readonly startSlot = viewChild.required<ElementRef<HTMLElement>>('startSlot');\n private readonly endSlot = viewChild.required<ElementRef<HTMLElement>>('endSlot');\n private readonly largeClip = viewChild<ElementRef<HTMLElement>>('largeClip');\n private readonly largeBlock = viewChild<ElementRef<HTMLElement>>('largeBlock');\n private readonly titleText = viewChild<ElementRef<HTMLElement>>('titleText');\n private readonly titleSizer = viewChild<ElementRef<HTMLElement>>('titleSizer');\n\n /**\n * True at compact heights (`(max-height: 500px)`: phone landscape, split screen), read with\n * `matchMedia` and followed live: no large title, the title in the bar, a 44 px bar.\n */\n protected readonly compact = signal(false);\n /** Whether the large title block renders: `largeTitle`, except at compact heights. */\n protected readonly showLarge = computed(() => this.largeTitle() && !this.compact());\n\n /** The shell mode (`provideBfShell({ mode })`, or the platform's for `'auto'`), fixed per header. */\n protected readonly mode: BfShellMode = injectShellMode();\n protected readonly barClasses = computed(\n () => `relative flex items-center px-2 ${BAR_CLASSES[this.compact() ? 'compact' : this.mode]}`,\n );\n protected readonly titleBoxClasses = TITLE_BOX_CLASSES[this.mode];\n protected readonly titleTextClasses = TITLE_TEXT_CLASSES[this.mode];\n protected readonly startSlotClasses = START_SLOT_CLASSES[this.mode];\n protected readonly largeBlockClasses = LARGE_BLOCK_CLASSES;\n protected readonly largeTitleClasses = LARGE_TITLE_CLASSES;\n protected readonly hostClasses = computed(\n () =>\n `${LAYOUT_CLASSES[this.translucent() ? 'translucent' : 'solid']} ${SURFACE_CLASSES[this.mode]}`,\n );\n\n /** Last written progress (0…1). */\n private progress = 0;\n /** Natural height of the large title block, 0 until measured (or without one). */\n private largeHeight = 0;\n /** The scroller `--bf-header-collapse` was last written on, so it can be cleared again. */\n private compensated: HTMLElement | null = null;\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n // Compact heights, followed live (rotation, a split-screen resize); the listener goes with\n // the header.\n const view = this.document.defaultView;\n const compactQuery =\n typeof view?.matchMedia === 'function' ? view.matchMedia(COMPACT_QUERY) : null;\n if (compactQuery) {\n this.compact.set(compactQuery.matches);\n const onCompactChange = (event: MediaQueryListEvent): void => this.compact.set(event.matches);\n compactQuery.addEventListener('change', onCompactChange);\n destroyRef.onDestroy(() => compactQuery.removeEventListener('change', onCompactChange));\n }\n\n let lastProgress: number | null = null;\n let lastScrolled: boolean | null = null;\n const setProgress = (scroller: HTMLElement): void => {\n // Clamped to the scrollable range, so a rubber-band past either end never moves the header,\n // and 0 when the page cannot scroll the whole range (it could never fully collapse). The\n // range is the rest range at any progress: `--bf-header-collapse` pads the scroller's bottom\n // by exactly what the collapse adds to its height, so the range never shrinks under it.\n // Without a large title block (none asked for, or a compact height) nothing collapses.\n const large = untracked(this.showLarge);\n const range = this.collapseRange();\n const max = scroller.scrollHeight - scroller.clientHeight;\n const scrollTop = !large || max < range ? 0 : Math.min(max, Math.max(0, scroller.scrollTop));\n const progress = Math.round(Math.min(1, Math.max(0, scrollTop / range)) * 1000) / 1000;\n if (progress !== lastProgress) {\n lastProgress = progress;\n this.progress = progress;\n this.host.style.setProperty('--bf-header-progress', String(progress));\n this.applyCollapse();\n }\n // The bar takes its scrolled-under surface once the large title has fully collapsed (the\n // title is the scroll-edge state, like UIKit's `scrollEdgeAppearance`), so a snap to the\n // collapsed position shows it; 0.5 px of tolerance covers a fractional snap landing on 3×\n // screens. Without one, as soon as the page leaves its top. A rubber-band past the top (a\n // negative `scrollTop`) never counts.\n const scrolled = large ? scrollTop >= range - 0.5 : Math.min(max, scroller.scrollTop) > 0;\n if (scrolled !== lastScrolled) {\n lastScrolled = scrolled;\n this.host.toggleAttribute('data-bf-scrolled', scrolled);\n }\n };\n\n // Natural heights and the slots' widths, measured once the view exists and again whenever the\n // toolbar, a slot, the bar title or the large title block resizes (fonts, text size, rotation,\n // a slot's content changing) — never per scroll frame.\n afterRenderEffect((onCleanup) => {\n const toolbar = this.toolbar().nativeElement;\n const start = this.startSlot().nativeElement;\n const end = this.endSlot().nativeElement;\n const title = this.titleText()?.nativeElement ?? null;\n const sizer = this.titleSizer()?.nativeElement ?? null;\n const block = this.largeBlock()?.nativeElement ?? null;\n const translucent = this.translucent();\n const measure = (): void => {\n // iOS: only when the title does not fit on one line beside the slots at their full width,\n // it may claim up to half the bar before the start slot gives way, as UIKit drops the back\n // title when the title needs the room; only a back button's text gives way, so the cap\n // never goes below the slot's other content. The cap depends only on the bar's width, the\n // title's one-line width and the slot's full and minimum widths (both read the same\n // whatever the cap), never on the capped slot itself, so it cannot loop.\n const cap = sizer\n ? this.startCap(toolbar.clientWidth, sizer.offsetWidth, start, end)\n : null;\n if (cap === null) {\n this.host.style.removeProperty('--bf-header-start-max');\n } else {\n this.host.style.setProperty('--bf-header-start-max', `${cap}px`);\n }\n // The centred title keeps the same distance from both edges: clear of the wider slot\n // (its content's extent, should anything overflow it).\n const inset =\n ROW_PADDING + Math.max(start.offsetWidth, start.scrollWidth, end.offsetWidth) + TITLE_GAP;\n this.host.style.setProperty('--bf-header-title-inset', `${inset}px`);\n // The bar is never shorter than its (wrapped) title.\n this.host.style.setProperty('--bf-header-title-height', `${title?.offsetHeight ?? 0}px`);\n this.largeHeight = block ? block.offsetHeight : 0;\n // The collapse range follows the large title's height: re-read the progress against it.\n const scroller = untracked(() => this.pageContext?.scroller() ?? null);\n if (scroller) {\n setProgress(scroller);\n }\n this.applyCollapse();\n this.pageContext?.headerHeight.set(\n translucent ? toolbar.offsetHeight + this.largeHeight : 0,\n );\n };\n measure();\n if (typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver(measure);\n observer.observe(toolbar);\n observer.observe(start);\n observer.observe(end);\n for (const element of [title, sizer]) {\n if (element) {\n observer.observe(element);\n }\n }\n if (block) {\n observer.observe(block);\n }\n onCleanup(() => observer.disconnect());\n }\n });\n let destroyed = false;\n destroyRef.onDestroy(() => {\n destroyed = true;\n this.pageContext?.headerHeight.set(0);\n this.compensated?.style.removeProperty('--bf-header-collapse');\n this.compensated = null;\n });\n\n // Passive native listeners on the page's registered scroller (set by `bf-content`), so the\n // collapse and the snap never schedule Angular change detection while scrolling.\n effect((onCleanup) => {\n const scroller = this.pageContext?.scroller() ?? null;\n if (!scroller) {\n return;\n }\n // A new scroller gets the current progress and compensation written on its first read.\n lastProgress = null;\n lastScrolled = null;\n const passive = { passive: true };\n /** Pending scroll-end timer; the scroll end fires once, from it or from `scrollend`. */\n let quietTimer: ReturnType<typeof setTimeout> | null = null;\n let settlePending = false;\n /** True from our own snap until its scroll ends, so the snap never schedules another. */\n let snapping = false;\n let touchDown = false;\n let pointerDown = false;\n /** True once the scroller scrolled during the current press; a bare tap never snaps. */\n let scrolledSincePress = false;\n const doc = this.document;\n\n const clearQuietTimer = (): void => {\n if (quietTimer !== null) {\n clearTimeout(quietTimer);\n quietTimer = null;\n }\n };\n const waitForQuiet = (): void => {\n settlePending = true;\n clearQuietTimer();\n quietTimer = setTimeout(onScrollEnd, SNAP_QUIET_MS);\n };\n const snap = (): void => {\n if (\n destroyed ||\n touchDown ||\n pointerDown ||\n this.progress <= 0 ||\n this.progress >= 1 ||\n this.keyboard.visible() ||\n // A parked page hides its frame, the common ancestor of the header and the scroller, and\n // `visibility` inherits, so the header's own visibility is the scroller's.\n this.host.checkVisibility?.({ visibilityProperty: true }) === false\n ) {\n return;\n }\n snapping = true;\n waitForQuiet();\n scroller.scrollTo({\n top: this.progress < 0.5 ? 0 : this.collapseRange(),\n behavior: prefersReducedMotion(this.document.defaultView) ? 'instant' : 'smooth',\n });\n };\n function onScrollEnd(): void {\n clearQuietTimer();\n if (!settlePending) {\n return;\n }\n settlePending = false;\n if (snapping) {\n snapping = false;\n return;\n }\n snap();\n }\n const onScroll = (): void => {\n scrolledSincePress = true;\n setProgress(scroller);\n waitForQuiet();\n };\n const onTouch = (event: TouchEvent): void => {\n const down = event.touches.length > 0;\n if (down && !touchDown && !pointerDown) {\n press();\n }\n touchDown = down;\n release();\n };\n // A mouse released outside the scroller never fires `pointerup` on it, so the release is\n // heard on the document, and only while a press is tracked.\n const stopTrackingPointer = (): void => {\n doc.removeEventListener('pointerup', onPointerUp);\n doc.removeEventListener('pointercancel', onPointerUp);\n };\n const onPointerDown = (): void => {\n if (!touchDown && !pointerDown) {\n press();\n }\n pointerDown = true;\n stopTrackingPointer();\n doc.addEventListener('pointerup', onPointerUp, passive);\n doc.addEventListener('pointercancel', onPointerUp, passive);\n release();\n };\n function onPointerUp(): void {\n pointerDown = false;\n stopTrackingPointer();\n release();\n }\n /** A new press; one that interrupts our snap counts as a scroll, so its release snaps again. */\n function press(): void {\n scrolledSincePress = snapping;\n }\n /**\n * A press takes over from our snap; a release after a scroll waits for any momentum to\n * settle, then snaps. A release without a scroll (a tap) schedules nothing.\n */\n function release(): void {\n if (touchDown || pointerDown) {\n snapping = false;\n settlePending = false;\n clearQuietTimer();\n } else if (scrolledSincePress) {\n waitForQuiet();\n }\n }\n\n scroller.addEventListener('scroll', onScroll, passive);\n scroller.addEventListener('scrollend', onScrollEnd, passive);\n scroller.addEventListener('touchstart', onTouch, passive);\n scroller.addEventListener('touchend', onTouch, passive);\n scroller.addEventListener('touchcancel', onTouch, passive);\n scroller.addEventListener('pointerdown', onPointerDown, passive);\n setProgress(scroller);\n onCleanup(() => {\n clearQuietTimer();\n scroller.removeEventListener('scroll', onScroll);\n scroller.removeEventListener('scrollend', onScrollEnd);\n scroller.removeEventListener('touchstart', onTouch);\n scroller.removeEventListener('touchend', onTouch);\n scroller.removeEventListener('touchcancel', onTouch);\n scroller.removeEventListener('pointerdown', onPointerDown);\n stopTrackingPointer();\n });\n });\n }\n\n /**\n * The iOS `start` slot's cap (px), or `null` when the title fits on one line beside the slots\n * at their full width. A back button's text (`[data-bf-back-text]`) is the only part that gives\n * way: the cap is at least the slot's width with every back text gone (down to the button's own\n * minimum width) and at least one 44 px hit area.\n * @param width The bar's width.\n * @param titleWidth The title's one-line width.\n * @param start The start slot.\n * @param end The end slot.\n */\n private startCap(\n width: number,\n titleWidth: number,\n start: HTMLElement,\n end: HTMLElement,\n ): number | null {\n // The slot's width now, what it is with every back text in full, and with every one gone.\n const current = Math.max(start.offsetWidth, start.scrollWidth);\n let natural = current;\n let floor = current;\n for (const text of start.querySelectorAll<HTMLElement>('[data-bf-back-text]')) {\n natural += Math.max(0, text.scrollWidth - text.clientWidth);\n const button = text.parentElement;\n const style = button ? this.document.defaultView?.getComputedStyle(button) : undefined;\n const buttonMin = parseFloat(style?.minWidth ?? '') || 0;\n const room = button ? button.offsetWidth - buttonMin : text.clientWidth;\n floor -= Math.max(0, Math.min(text.clientWidth, room));\n }\n const room = width - 2 * (ROW_PADDING + Math.max(natural, end.offsetWidth) + TITLE_GAP);\n if (titleWidth <= room) {\n return null;\n }\n const wanted = Math.min(titleWidth, width / 2);\n const cap = Math.floor(Math.max(START_MIN, (width - wanted) / 2 - ROW_PADDING - TITLE_GAP));\n return Math.max(cap, Math.ceil(floor));\n }\n\n /**\n * The scroll distance (px) over which the large title collapses: its block's rendered height\n * (the eyebrow, the wrapped title and its bottom padding), so the collapse ends exactly when the\n * title has scrolled away, bounded to 44–200 px (a taller title collapses its whole height over\n * 200 px, so faster than it scrolls); 56 px until it is measured.\n */\n private collapseRange(): number {\n if (this.largeHeight <= 0) {\n return COLLAPSE_FALLBACK;\n }\n return Math.min(COLLAPSE_MAX, Math.max(COLLAPSE_MIN, this.largeHeight));\n }\n\n /**\n * Collapses the large title block's height with the progress, so the header ends at exactly the\n * toolbar (left at its natural (auto) height until measured), and writes the height it gave up\n * as `--bf-header-collapse` on the page's scroller (`0px` at rest). Solid, the header is in the\n * page's column, so the scroller grows by that much and `bf-content` pads its bottom by the same\n * amount, keeping the scroll range constant; translucent, the header is drawn over the scroller,\n * which never resizes, so the compensation stays `0px`.\n */\n private applyCollapse(): void {\n const clip = this.largeClip()?.nativeElement;\n const measured = clip !== undefined && this.largeHeight > 0;\n let collapsed = 0;\n if (clip && measured) {\n const height = Math.round(this.largeHeight * (1 - this.progress));\n clip.style.height = `${height}px`;\n collapsed = this.translucent() ? 0 : this.largeHeight - height;\n }\n const scroller = untracked(() => this.pageContext?.scroller() ?? null);\n if (this.compensated && this.compensated !== scroller) {\n this.compensated.style.removeProperty('--bf-header-collapse');\n }\n this.compensated = scroller;\n scroller?.style.setProperty('--bf-header-collapse', `${collapsed}px`);\n }\n}\n","import {\n Component,\n DOCUMENT,\n ElementRef,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n type AfterViewInit,\n viewChild,\n} from '@angular/core';\nimport { BF_LABELS, BfSpinnerComponent } from '@bitforgehq/ui';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { DURATION_MD, SETTLE_EASING } from '../../motion';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { BfHapticsService } from '../../platform/haptics.service';\nimport { BfPlatformService } from '../../platform/platform.service';\n\n/** Resting position of the disc, above the scroller's clipped content. */\nconst RESTING = -48;\n/** Parked position while refreshing: right at the top of the scroller's content. */\nconst PARKED = 0;\n/** Raw pointer travel (px) that counts as a completed pull. */\nconst THRESHOLD = 64;\n/** Cap on how far the resisted pull can push the disc past `PARKED`. */\nconst MAX_PULL = THRESHOLD + 8;\n/** The pull moves the disc at half the pointer's travel. */\nconst RESISTANCE = 0.5;\n/** Downward travel (px) a press must clear before it's claimed as a pull, not a click. */\nconst SLOP = 8;\nconst SETTLE_DURATION = DURATION_MD;\n/** Bounce mode: rubber-band depth (px) the disc needs before it shows in the gap. */\nconst BOUNCE_REVEAL = 8;\n/** Bounce mode: the disc's scale at progress 0; it grows to 1 at the threshold. */\nconst BOUNCE_MIN_SCALE = 0.6;\n\n/**\n * The scroller a refresher used without a page context tracks: its nearest ancestor that scrolls\n * vertically (`overflow-y` auto or scroll) — past `bf-content`'s body wrapper — else its parent.\n */\nfunction findScroller(host: HTMLElement): HTMLElement | null {\n const view = host.ownerDocument.defaultView;\n for (let element = host.parentElement; element && view; element = element.parentElement) {\n const overflowY = view.getComputedStyle(element).overflowY;\n if (overflowY === 'auto' || overflowY === 'scroll') {\n return element;\n }\n }\n return host.parentElement;\n}\n\nfunction translateStyle(px: number): string {\n return `translate(-50%, ${px}px)`;\n}\n\n/** A touch in iOS mode: the scroller rubber-bands natively and the disc rides the gap. */\ninterface BfRefresherBounce {\n readonly reducedMotion: boolean;\n /** Last rubber-band depth (`−scrollTop`, px) seen during the touch. */\n pull: number;\n hapticFired: boolean;\n /** The disc is showing in the gap; styles are written only when this changes or while shown. */\n shown: boolean;\n}\n\ninterface BfRefresherGesture {\n readonly pointerId: number;\n readonly startX: number;\n readonly startY: number;\n readonly reducedMotion: boolean;\n hapticFired: boolean;\n /** Set once the drag clears `SLOP` downward and out-vertical'd any horizontal travel. */\n claimed: boolean;\n}\n\n/**\n * The payload of `bf-refresher`'s `refreshRequested` output: call `complete()` once the reload is\n * done to animate the disc away.\n *\n * @summary What a pull-to-refresh request carries: the callback that ends the refresh\n * @since 0.3.0\n */\nexport interface BfRefresherRefreshEvent {\n /** Ends the refresh: animates the disc away and resets the refresher. */\n complete: () => void;\n}\n\n/**\n * Pull-to-refresh, placed as the first child of `bf-content`. It tracks Pointer Events on the\n * page's scroller (read from `BfPageContext`, or its nearest scrolling ancestor when used without one)\n * only while the scroller is at `scrollTop === 0`: the disc resists the pull (half the pointer's\n * travel, capped) from a resting position above the clipped content to a 64 px threshold, ticks\n * `BfHapticsService.impact('light')` once on crossing it, and on release past the threshold\n * parks at the top with a spinner and emits `refreshRequested` — call `complete()` on the event to animate\n * the disc away and reset. A press by itself changes nothing: only once a pointermove clears an\n * 8 px downward slop (out-vertical'ing any horizontal travel) does it claim the gesture and\n * capture the pointer on the scroller, so a plain click on the content underneath (a row, a\n * button) still reaches it. For touch, a non-passive `touchmove` guard (attached only while a\n * press is tracked) keeps the browser from taking a downward drag at the top as a pan, which\n * would cancel the pointer stream; upward drags still scroll the list. The disc itself is\n * `aria-hidden`; a permanent, visually hidden `role=\"status\"` region announces\n * `provideBfLabels()`'s `refreshing` (\"Refreshing\"). `prefers-reduced-motion` still follows the\n * pointer 1:1 during the pull (direct manipulation is not motion); only the settle, park and `complete()` animations\n * become instant.\n *\n * That pull is the Material (and mouse/pen) mode. A touch in iOS mode (`BfPlatformService.mode()`)\n * is the bounce mode instead: the refresher claims nothing (no capture, no `preventDefault`, no\n * guard), the scroller rubber-bands natively, and while the finger is down a negative `scrollTop`\n * shows the disc inside the gap the bounce opens, at its resting −48 px, with opacity and scale\n * (0.6 → 1) following `−scrollTop / 64` once it passes 8 px; one light haptic at 64 px. Lifting the\n * finger at 64 px or more parks the disc and emits `refreshRequested`, like `UIRefreshControl`; a bounce\n * with no finger down (a fling's overshoot) never shows it. The host carries `data-bf-refresher`\n * while enabled, so `bfOverscrollStretch` leaves the top edge to it.\n *\n * @example\n * <bf-content>\n * <bf-refresher (refreshRequested)=\"reload($event.complete)\" />\n * <bf-list>…</bf-list>\n * </bf-content>\n * @summary Pull-to-refresh indicator with haptic feedback, native iOS bounce and an Android-style pull\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-refresher',\n imports: [BfSpinnerComponent],\n host: { class: 'relative block h-0', '[attr.data-bf-refresher]': 'disabled() ? null : \"\"' },\n template: `\n <div\n #disc\n data-bf-refresher-disc\n aria-hidden=\"true\"\n class=\"bg-surface shadow-raised absolute top-0 left-1/2 flex h-10 w-10 items-center justify-center rounded-full\"\n >\n <bf-spinner size=\"sm\" />\n </div>\n <div role=\"status\" aria-live=\"polite\" class=\"sr-only\">\n @if (refreshing()) {\n {{ labels.refreshing }}\n }\n </div>\n `,\n})\nexport class BfRefresherComponent implements AfterViewInit {\n /** Disables pull tracking; the disc never moves and `refreshRequested` never emits. */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /** Emitted when a pull past the threshold is released; call `complete()` to end it. */\n readonly refreshRequested = output<BfRefresherRefreshEvent>();\n /**\n * Emitted with the same event as `refreshRequested`, together with it.\n * @deprecated 0.3.0 use refreshRequested; removed in 0.4.0\n */\n readonly refresh = output<BfRefresherRefreshEvent>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly disc = viewChild.required<ElementRef<HTMLElement>>('disc');\n private readonly pageContext = inject(BfPageContext, { optional: true });\n private readonly haptics = inject(BfHapticsService);\n protected readonly labels = inject(BF_LABELS);\n private readonly document = inject(DOCUMENT);\n private readonly platform = inject(BfPlatformService);\n private readonly ios = computed(() => this.platform.mode() === 'ios');\n\n private readonly refreshingState = signal(false);\n /** True from the moment a completed pull is released until `complete()` is called. */\n readonly refreshing = this.refreshingState.asReadonly();\n\n private scroller: HTMLElement | null = null;\n private gesture: BfRefresherGesture | null = null;\n private settleAnimation: Animation | null = null;\n private currentOffset = RESTING;\n /** A finger is on the scroller (tracked in iOS mode only). */\n private touchActive = false;\n private bounce: BfRefresherBounce | null = null;\n\n constructor() {\n // A native Pointer Events listener on the page's scroller, attached only once it is known —\n // set by `bf-content` through `BfPageContext`, or the nearest scrolling ancestor when used\n // without one.\n effect((onCleanup) => {\n const scroller = this.pageContext?.scroller() ?? findScroller(this.host);\n if (!scroller) {\n return;\n }\n this.scroller = scroller;\n scroller.addEventListener('pointerdown', this.onPointerDown);\n // iOS: a touch rides WebKit's own rubber band (bounce mode), tracked with passive listeners.\n const ios = this.ios();\n if (ios) {\n const passive: AddEventListenerOptions = { passive: true };\n scroller.addEventListener('touchstart', this.onTouchStart, passive);\n scroller.addEventListener('touchend', this.onTouchEnd, passive);\n scroller.addEventListener('touchcancel', this.onTouchEnd, passive);\n }\n onCleanup(() => {\n this.endGesture(scroller);\n scroller.removeEventListener('pointerdown', this.onPointerDown);\n if (ios) {\n if (this.bounce?.shown) {\n this.hideBounceDisc();\n }\n this.endBounce(scroller);\n this.touchActive = false;\n scroller.removeEventListener('touchstart', this.onTouchStart);\n scroller.removeEventListener('touchend', this.onTouchEnd);\n scroller.removeEventListener('touchcancel', this.onTouchEnd);\n }\n this.settleAnimation?.cancel();\n this.scroller = null;\n });\n });\n }\n\n /** Paints the disc's resting, hidden transform once the view exists (no template `style`). */\n ngAfterViewInit(): void {\n this.setOffset(RESTING);\n }\n\n /** Emits one refresh event on `refreshRequested` and the deprecated `refresh`. */\n private requestRefresh(): void {\n const event: BfRefresherRefreshEvent = { complete: () => this.complete() };\n this.refreshRequested.emit(event);\n this.refresh.emit(event);\n }\n\n /** Ends a refresh: animates the disc away (skipped under reduced motion) and resets state. */\n complete(): void {\n if (!this.refreshingState()) {\n return;\n }\n this.settleTo(RESTING, prefersReducedMotion(this.document.defaultView), () =>\n this.refreshingState.set(false),\n );\n }\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n const scroller = this.scroller;\n if (!scroller) {\n return;\n }\n if (this.ios() && (event.pointerType === 'touch' || this.touchActive)) {\n // Bounce mode: the scroller keeps the touch and rubber-bands natively.\n return;\n }\n if (\n this.disabled() ||\n this.refreshingState() ||\n this.gesture ||\n !event.isPrimary ||\n scroller.scrollTop !== 0\n ) {\n return;\n }\n this.settleAnimation?.cancel();\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n hapticFired: false,\n claimed: false,\n };\n // Tracked, but not claimed: no Pointer Capture yet, and nothing prevented, so a press\n // released without a qualifying move still reaches a plain `click` on whatever is\n // underneath it (a row, a button) instead of being swallowed by the scroller.\n scroller.addEventListener('pointermove', this.onPointerMove);\n scroller.addEventListener('pointerup', this.onPointerUp);\n scroller.addEventListener('pointercancel', this.onPointerCancel);\n scroller.addEventListener('lostpointercapture', this.onLostPointerCapture);\n scroller.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n private readonly onTouchStart = (): void => {\n this.touchActive = true;\n const scroller = this.scroller;\n // A pen press in pull mode fires touch events too; the pull owns that press.\n if (!scroller || this.gesture || this.bounce || this.disabled() || this.refreshingState()) {\n return;\n }\n this.bounce = {\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n pull: 0,\n hapticFired: false,\n shown: false,\n };\n scroller.addEventListener('scroll', this.onBounceScroll, { passive: true });\n };\n\n /**\n * While a finger is down, a negative `scrollTop` is WebKit's rubber band at the top: the disc,\n * which sits in the content above its top edge, shows in the gap the bounce opens. Direct\n * manipulation (styles set straight from the scroll position, no animation).\n */\n private readonly onBounceScroll = (): void => {\n const bounce = this.bounce;\n const scroller = this.scroller;\n if (!bounce || !scroller) {\n return;\n }\n const pull = Math.max(0, -scroller.scrollTop);\n bounce.pull = pull;\n if (pull <= BOUNCE_REVEAL) {\n if (bounce.shown) {\n bounce.shown = false;\n this.hideBounceDisc();\n }\n return;\n }\n bounce.shown = true;\n const progress = Math.min(1, pull / THRESHOLD);\n const element = this.disc().nativeElement;\n element.style.opacity = String(progress);\n element.style.transform = `${translateStyle(RESTING)} scale(${BOUNCE_MIN_SCALE + (1 - BOUNCE_MIN_SCALE) * progress})`;\n element.style.visibility = 'visible';\n if (progress >= 1 && !bounce.hapticFired) {\n bounce.hapticFired = true;\n void this.haptics.impact('light');\n }\n };\n\n private readonly onTouchEnd = (event: TouchEvent): void => {\n if (event.touches.length > 0) {\n return;\n }\n this.touchActive = false;\n const bounce = this.bounce;\n const scroller = this.scroller;\n if (!bounce || !scroller) {\n return;\n }\n // The rubber band keeps scrolling back after the finger lifts; that is not a pull.\n this.endBounce(scroller);\n if (!this.disabled() && !this.refreshingState() && bounce.pull >= THRESHOLD) {\n // The disc settles from the gap onto the content top while the content snaps back.\n this.disc().nativeElement.style.removeProperty('opacity');\n this.refreshingState.set(true);\n this.settleTo(PARKED, bounce.reducedMotion);\n this.requestRefresh();\n } else if (bounce.shown) {\n this.hideBounceDisc();\n }\n };\n\n private endBounce(scroller: HTMLElement): void {\n scroller.removeEventListener('scroll', this.onBounceScroll);\n this.bounce = null;\n }\n\n private hideBounceDisc(): void {\n this.disc().nativeElement.style.removeProperty('opacity');\n this.setOffset(RESTING);\n }\n\n /**\n * Non-passive `touchmove` guard, attached only while a press is tracked (the pattern of the\n * stack outlet's swipe guard). The scroller is `touch-pan-y`, so without it the browser takes a\n * downward drag as a pan and ends the pointer stream with `pointercancel`. It prevents a\n * cancelable move once the pull is claimed, or — before the claim — a downward-dominant move\n * while the scroller is at the top, where a pan has nothing to scroll anyway. Upward and\n * horizontal drags are left alone, so the list (and any horizontal scroller) keeps its pan.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n const gesture = this.gesture;\n if (!gesture || !event.cancelable) {\n return;\n }\n if (gesture.claimed) {\n event.preventDefault();\n return;\n }\n const touch = event.touches[0];\n if (!touch || this.scroller?.scrollTop !== 0) {\n return;\n }\n const dy = touch.clientY - gesture.startY;\n const dx = touch.clientX - gesture.startX;\n if (dy > 0 && dy > Math.abs(dx)) {\n event.preventDefault();\n }\n };\n\n /**\n * A touch pointer is implicitly captured by the element under the finger; claiming it for the\n * scroller makes that child fire its own `lostpointercapture`, which bubbles here. Only the\n * scroller losing the capture it took ends the pull.\n */\n private readonly onLostPointerCapture = (event: PointerEvent): void => {\n if (event.target !== this.scroller) {\n return;\n }\n this.onPointerCancel(event);\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n if (scroller.scrollTop !== 0) {\n // The list itself started scrolling under the drag; let it, the pull is over.\n this.endGesture(scroller);\n this.setOffset(RESTING);\n return;\n }\n const delta = event.clientY - gesture.startY;\n if (!gesture.claimed) {\n const dx = event.clientX - gesture.startX;\n if (delta <= SLOP || delta <= Math.abs(dx)) {\n // Not yet a clear, primarily-downward pull past the slop: leave the pointer alone.\n return;\n }\n gesture.claimed = true;\n try {\n // Captured only now: before this, an un-moved (or horizontal, or upward) pointerdown\n // and pointerup pair must behave like a normal click on the content underneath.\n scroller.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic pointers (tests) have no active pointer to capture.\n }\n }\n if (event.cancelable) {\n event.preventDefault();\n }\n // Direct manipulation, not an animation: the disc tracks the pointer 1:1 (resisted) even\n // under prefers-reduced-motion — only the settle/park/complete animations go instant.\n const resisted = delta > 0 ? Math.min(delta * RESISTANCE, MAX_PULL) : 0;\n this.setOffset(RESTING + resisted);\n if (delta >= THRESHOLD && !gesture.hapticFired) {\n gesture.hapticFired = true;\n void this.haptics.impact('light');\n }\n };\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n const claimed = gesture.claimed;\n const delta = event.clientY - gesture.startY;\n this.endGesture(scroller);\n if (!claimed) {\n // Never cleared the slop: a plain press-and-release, left untouched for its `click`.\n return;\n }\n if (!this.disabled() && delta >= THRESHOLD) {\n this.refreshingState.set(true);\n this.settleTo(PARKED, gesture.reducedMotion);\n this.requestRefresh();\n } else {\n this.settleTo(RESTING, gesture.reducedMotion);\n }\n };\n\n private readonly onPointerCancel = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n const claimed = gesture.claimed;\n this.endGesture(scroller);\n if (claimed) {\n this.settleTo(RESTING, gesture.reducedMotion);\n }\n };\n\n private endGesture(scroller: HTMLElement): void {\n const pointerId = this.gesture?.pointerId;\n this.removeDragListeners(scroller);\n if (pointerId !== undefined) {\n try {\n scroller.releasePointerCapture(pointerId);\n } catch {\n // Already released (or never captured, e.g. a synthetic pointer in tests).\n }\n }\n this.gesture = null;\n }\n\n private removeDragListeners(scroller: HTMLElement): void {\n scroller.removeEventListener('pointermove', this.onPointerMove);\n scroller.removeEventListener('pointerup', this.onPointerUp);\n scroller.removeEventListener('pointercancel', this.onPointerCancel);\n scroller.removeEventListener('lostpointercapture', this.onLostPointerCapture);\n scroller.removeEventListener('touchmove', this.touchMoveGuard);\n }\n\n private setOffset(px: number): void {\n this.currentOffset = px;\n const element = this.disc().nativeElement;\n element.style.transform = translateStyle(px);\n // Clipping alone (the resting −48 px sits above the scroller's visible content) still let\n // the disc's shadow bleed a few px above the content; fully hide it at rest instead.\n element.style.visibility = px === RESTING ? 'hidden' : 'visible';\n }\n\n private settleTo(target: number, reducedMotion: boolean, onDone?: () => void): void {\n this.settleAnimation?.cancel();\n if (reducedMotion) {\n this.setOffset(target);\n onDone?.();\n return;\n }\n const from = this.currentOffset;\n const animation = this.disc().nativeElement.animate(\n [{ transform: translateStyle(from) }, { transform: translateStyle(target) }],\n { duration: SETTLE_DURATION, easing: SETTLE_EASING, fill: 'forwards' },\n );\n this.settleAnimation = animation;\n this.currentOffset = target;\n void animation.finished.then(\n () => {\n this.setOffset(target);\n onDone?.();\n },\n () => {\n // Cancelled by a newer gesture or settle; that one owns the disc now.\n },\n );\n }\n}\n","import { LocationStrategy } from '@angular/common';\nimport {\n DOCUMENT,\n Component,\n DestroyRef,\n ElementRef,\n afterRenderEffect,\n computed,\n contentChild,\n inject,\n input,\n linkedSignal,\n signal,\n type Signal,\n} from '@angular/core';\nimport { PRIMARY_OUTLET, Router, type UrlTree } from '@angular/router';\nimport {\n BF_TAB_BAR_CONTROLLER,\n BF_TAB_LINKS,\n BfTabBarComponent,\n type BfTabBarController,\n} from '@bitforgehq/ui';\nimport { BfHapticsService } from '../../platform/haptics.service';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { BfPlatformService } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS } from '../../shell/provide-bf-shell';\nimport { BfOverlayStack } from '@bitforgehq/ui/overlay';\nimport { BfNavController } from '../nav-controller.service';\nimport { BfStackOutletComponent } from '../stack-outlet/stack-outlet.component';\nimport { BF_TAB_CONTEXT, type BfTabContext } from './tab-context';\n\n/**\n * Bottom inset of a visible floating tab bar: its height, its bottom offset (8 px above the safe\n * area, which counts as at least 12 px) and 8 px of breathing room above the bar. The height is the\n * bar's measured one (it grows with large text and is slimmer at compact heights).\n * @param height The bar's height as a CSS length.\n */\nfunction barInset(height: string): string {\n return `calc(${height} + 8px + max(var(--spacing-safe-bottom), 12px) + 8px)`;\n}\n/** The inset until the bar is first measured: its minimum height, 64 px. */\nconst BAR_INSET_FALLBACK = barInset('64px');\n\n/**\n * The tabs page of a mobile app: one navigation stack per tab. It wraps a `bf-stack-outlet`, which\n * keeps a separate stack for every tab (keyed by the URL segment after `tabsPath`), and a\n * `bf-tab-bar`, which it drives through `BF_TAB_BAR_CONTROLLER`: the bar shows the tab of the\n * in-flight navigation's target, else of the current URL, and hides while the keyboard is open,\n * without anything writing into it. Selecting another tab brings\n * that tab back as it was left; re-selecting the active tab pops its stack to the root, or scrolls\n * its content to the top when it is already there. The bar hides while the soft keyboard is open\n * or about to open (a focused text control), and `--bf-tab-bar-inset` on the host tells\n * `bf-content` how much to pad its bottom (the bar's measured height, a `ResizeObserver` on its\n * `nav`, plus its bottom offset and 8 px; 64 px for the height until it is measured); the same value is mirrored on `<html>` as\n * `--bf-tab-bar-inset-global` for overlays (the toast), which render outside the host. It is removed when the tabs are destroyed.\n * It also provides `BF_TAB_LINKS`, so the projected bar is a `nav` of links to each tab's root URL\n * (`aria-current` on the active one: `page` at the tab's root, `true` on a child page of it)\n * instead of a `tablist`.\n *\n * @example\n * <bf-tabs tabsPath=\"tabs\">\n * <bf-stack-outlet />\n * <bf-tab-bar>\n * <bf-tab id=\"today\" label=\"Today\"><bf-icon [icon]=\"Sun\" /></bf-tab>\n * <bf-tab id=\"me\" label=\"Me\"><bf-icon [icon]=\"User\" /></bf-tab>\n * </bf-tab-bar>\n * </bf-tabs>\n * @summary Tabs page with one navigation stack per tab, wiring the tab bar to the router\n * @category Navigation\n * @since 0.2.0\n * @slot default The tabs page body: a `bf-stack-outlet` and a `bf-tab-bar`.\n */\n@Component({\n selector: 'bf-tabs',\n host: { class: 'block h-full' },\n providers: [\n { provide: BF_TAB_CONTEXT, useExisting: BfTabsComponent },\n // The projected bar renders links (href per tab) instead of a tablist.\n { provide: BF_TAB_LINKS, useExisting: BfTabsComponent },\n // ...and derives its selection and hiding from this component.\n { provide: BF_TAB_BAR_CONTROLLER, useExisting: BfTabsComponent },\n ],\n template: `<div class=\"relative flex h-full flex-col\"><ng-content /></div>`,\n})\nexport class BfTabsComponent implements BfTabContext, BfTabBarController {\n /** Route path of the tabs page (e.g. `'tabs'`); the URL segment after it is the tab key. */\n readonly tabsPath = input.required<string>();\n\n private readonly router = inject(Router);\n private readonly nav = inject(BfNavController);\n private readonly haptics = inject(BfHapticsService);\n private readonly keyboard = inject(BfKeyboardService);\n private readonly platform = inject(BfPlatformService);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly overlays = inject(BfOverlayStack);\n private readonly locationStrategy = inject(LocationStrategy);\n\n private readonly bar = contentChild(BfTabBarComponent);\n private readonly barHost = contentChild<BfTabBarComponent, ElementRef<HTMLElement>>(\n BfTabBarComponent,\n { read: ElementRef },\n );\n /** The bar's measured height (px), `null` until its `nav` is first measured. */\n private readonly barHeight = signal<number | null>(null);\n private readonly outlet = contentChild(BfStackOutletComponent);\n\n /** The settled URL: the last successful navigation's (after redirects), else the router's. */\n private readonly url = computed(\n () => this.router.lastSuccessfulNavigation()?.finalUrl ?? this.router.parseUrl(this.router.url),\n );\n\n /** The active tab's key, from the router URL; null while the URL is not under `tabsPath`. */\n readonly tabKey = computed(() => this.keyOfTree(this.url()));\n\n /**\n * The tab the bar shows: the in-flight navigation's target (after redirects once they are\n * applied), else the settled URL's. A refused or cancelled navigation ends, so the bar drops\n * back to the settled tab by itself. While neither is under `tabsPath` (a page pushed above the\n * tabs page) it keeps the last tab it showed.\n */\n private readonly currentTab = linkedSignal<string | null, string | null>({\n source: () => {\n const navigation = this.router.currentNavigation();\n return this.keyOfTree(\n navigation?.finalUrl ??\n navigation?.extractedUrl ??\n this.router.lastSuccessfulNavigation()?.finalUrl ??\n this.router.parseUrl(this.router.url),\n );\n },\n computation: (key, previous) => key ?? previous?.value ?? null,\n });\n\n /** The tab the bar shows (read-only): the in-flight navigation's target, else the current URL's;\n * null when unknown. */\n readonly current: Signal<string | null> = this.currentTab.asReadonly();\n\n /** Hide the bar while the keyboard is open or about to open (a focused text control: on Android\n * the WebView shrinks before will-show and would lift the fixed bar above the keyboard). */\n readonly hidden = computed(() => this.keyboard.visible() || this.keyboard.imminent());\n\n /** The bar hides animated on iOS, instantly on Android and the web. */\n readonly hideTransition = computed(() => this.platform.platform === 'ios');\n\n constructor() {\n // The tabs host is a page root too: apps that mount `bf-tabs` without an outer outlet still\n // need the tab bar to go inert while a modal-like overlay is open.\n const unregisterRoot = this.overlays.registerRoot(this.host);\n inject(DestroyRef).onDestroy(() => {\n unregisterRoot();\n this.document.documentElement.style.removeProperty('--bf-tab-bar-inset-global');\n });\n\n // The bar's real height, measured on its `nav` whenever it resizes (text size, rotation, a\n // label wrapping) and never per frame; the observer goes with the bar or the tabs.\n afterRenderEffect((onCleanup) => {\n const nav = this.barHost()?.nativeElement.querySelector('nav') ?? null;\n if (!(nav instanceof HTMLElement) || typeof ResizeObserver === 'undefined') {\n return;\n }\n const observer = new ResizeObserver(() => {\n const height = nav.offsetHeight;\n this.barHeight.set(height > 0 ? height : null);\n });\n observer.observe(nav);\n onCleanup(() => observer.disconnect());\n });\n\n // The inset is this host's own style (and `<html>`'s, for overlays): written in the render\n // phase, after the bar's hidden state for this change detection is known.\n afterRenderEffect({\n write: () => {\n const bar = this.bar();\n const shown = bar !== undefined && !bar.isHidden();\n const height = this.barHeight();\n const inset = !shown\n ? '0px'\n : height === null\n ? BAR_INSET_FALLBACK\n : barInset(`${height}px`);\n this.host.style.setProperty('--bf-tab-bar-inset', inset);\n // Overlays render in the CDK container outside `bf-tabs`, so they read the inset from\n // `<html>` under a name of its own: `bf-content` outside the tabs must not inherit it.\n this.document.documentElement.style.setProperty('--bf-tab-bar-inset-global', inset);\n },\n });\n }\n\n /**\n * The tab key of `url`: the primary segment after `tabsPath`. Query, fragment, matrix params and\n * auxiliary routes (`(popup:…)`) are ignored.\n * @param url The URL to read.\n */\n keyOf(url: string): string | null {\n return this.keyOfTree(this.router.parseUrl(url));\n }\n\n /** `keyOf` for a parsed URL. */\n private keyOfTree(tree: UrlTree): string | null {\n const segments = primarySegments(tree);\n const base = this.baseSegments();\n for (let i = 0; i + base.length < segments.length; i++) {\n if (base.every((b, j) => segments[i + j] === b)) {\n return segments[i + base.length] ?? null;\n }\n }\n return null;\n }\n\n /**\n * The URL of tab `id`'s root page (`/tabs/<id>`, with the app's base href), the `href` of its\n * link in the bar: a new browser tab opens the tab's root. A plain click still restores the page\n * the tab was left on.\n * @param id The `id` of the tab.\n */\n href(id: string): string {\n const tree = this.router.createUrlTree(['/', ...this.baseSegments(), id]);\n return this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(tree));\n }\n\n /**\n * Whether the current URL is tab `id`'s root page (`…/<tabsPath>/<id>` with nothing after it,\n * ignoring query, fragment and auxiliary routes); reactive through the router's signals.\n * @param id The `id` of the tab.\n */\n isRoot(id: string): boolean {\n const segments = primarySegments(this.url());\n const base = this.baseSegments();\n // The same match as `keyOf`: `tabsPath` wherever it sits, then the tab key as the last segment.\n const start = segments.length - base.length - 1;\n return (\n start >= 0 &&\n segments[segments.length - 1] === id &&\n base.every((b, j) => segments[start + j] === b) &&\n this.tabKey() === id\n );\n }\n\n /** The non-empty segments of `tabsPath`; empty when the tabs are mounted at the root URL. */\n private baseSegments(): string[] {\n return this.tabsPath()\n .split('/')\n .filter((s) => s.length > 0);\n }\n\n /**\n * Another tab, selected by the user in the bar: go back to where that tab was left (its stack's\n * top page), or its root. The bar shows the target while the navigation is in flight and drops\n * back by itself when a guard refuses it.\n * @param id The `id` of the selected tab.\n */\n select(id: string): void {\n const target = this.outlet()?.lastUrlOf(id) ?? ['/', ...this.baseSegments(), id];\n this.nav.forward(target).then(\n (navigated) => {\n // Only a switch that happened ticks; a refused one stays silent.\n if (navigated && this.config.hapticsOnTabChange) {\n void this.haptics.selection();\n }\n },\n (error: unknown) => {\n console.error('bf-tabs: tab navigation failed', error);\n },\n );\n }\n\n /**\n * The active tab again: pop its stack back to its existing root page (a history step), or\n * replace the stack with the root when the history position is unknown; at the root, scroll its\n * content to the top.\n * @param id The `id` of the active tab.\n */\n reselect(id: string): void {\n if (this.nav.depth() > 1) {\n if (!this.outlet()?.popToRoot()) {\n void this.nav.root(['/', ...this.baseSegments(), id]);\n }\n } else {\n this.outlet()?.scrollActiveContentToTop();\n }\n }\n}\n\n/** The paths of the primary outlet's segments of `tree`, from the root down. */\nfunction primarySegments(tree: UrlTree): string[] {\n const paths: string[] = [];\n let group = tree.root.children[PRIMARY_OUTLET];\n while (group) {\n paths.push(...group.segments.map((s) => s.path));\n group = group.children[PRIMARY_OUTLET];\n }\n return paths;\n}\n","/**\n * The overlay core's names as `@bitforgehq/ui/mobile` exported them before 0.3.0, kept for one\n * minor. Each is a `const` and/or `type` alias rather than an `export … from`, because TypeScript\n * does not carry `@deprecated` through a re-export. `/mobile` source imports the core from\n * `@bitforgehq/ui/overlay` directly, never through these aliases.\n */\nimport * as core from '@bitforgehq/ui/overlay';\n\n/** @deprecated 0.3.0 use BF_OVERLAY_DATA from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BF_OVERLAY_DATA = core.BF_OVERLAY_DATA;\n\n/** @deprecated 0.3.0 use BfOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayRef = core.BfOverlayRef;\n/** @deprecated 0.3.0 use BfOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayRef<T = unknown> = core.BfOverlayRef<T>;\n\n/** @deprecated 0.3.0 use BfComponentOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfComponentOverlayRef = core.BfComponentOverlayRef;\n/** @deprecated 0.3.0 use BfComponentOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfComponentOverlayRef<C, T = unknown> = core.BfComponentOverlayRef<C, T>;\n\n/** @deprecated 0.3.0 use BfOverlayStack from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayStack = core.BfOverlayStack;\n/** @deprecated 0.3.0 use BfOverlayStack from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayStack = core.BfOverlayStack;\n\n/** @deprecated 0.3.0 use BfOverlayStackOptions from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayStackOptions = core.BfOverlayStackOptions;\n\n/** @deprecated 0.3.0 use BfStackedOverlay from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfStackedOverlay = core.BfStackedOverlay;\n\n/** @deprecated 0.3.0 use BfOverlayService from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayService = core.BfOverlayService;\n/** @deprecated 0.3.0 use BfOverlayService from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayService = core.BfOverlayService;\n\n/** @deprecated 0.3.0 use BfOverlayOptions from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayOptions = core.BfOverlayOptions;\n\n/** @deprecated 0.3.0 use BfOverlayPosition from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayPosition = core.BfOverlayPosition;\n\n/** @deprecated 0.3.0 use BfOverlayTemplateContext from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayTemplateContext = core.BfOverlayTemplateContext;\n\n/** @deprecated 0.3.0 use OVERLAY_DURATION from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const OVERLAY_DURATION = core.OVERLAY_DURATION;\n\n/** @deprecated 0.3.0 use OVERLAY_EASING from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const OVERLAY_EASING = core.OVERLAY_EASING;\n\n/** @deprecated 0.3.0 use fadeDown from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeDown = core.fadeDown;\n\n/** @deprecated 0.3.0 use fadeIn from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeIn = core.fadeIn;\n\n/** @deprecated 0.3.0 use fadeOut from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeOut = core.fadeOut;\n\n/** @deprecated 0.3.0 use fadeUp from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeUp = core.fadeUp;\n\n/** @deprecated 0.3.0 use scaleIn from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const scaleIn = core.scaleIn;\n\n/** @deprecated 0.3.0 use slideDown from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const slideDown = core.slideDown;\n\n/** @deprecated 0.3.0 use slideUp from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const slideUp = core.slideUp;\n","import { type Signal, type WritableSignal, signal } from '@angular/core';\nimport { type BfOverlayController, BfOverlayRef } from '@bitforgehq/ui/overlay';\n\n/** What `BfSheetService` does for a sheet ref; internal. */\nexport interface BfSheetController extends BfOverlayController {\n /** True while the underlying overlay is closing (backdrop, `Escape`, back button, drag). */\n isClosing(): boolean;\n}\n\n/**\n * Handle to an open bottom sheet, returned by `BfSheetService.open()` and injectable in the\n * sheet's content as `BfSheetRef` (and as `BfOverlayRef`). On top of `close(result)` and\n * `afterClosed` it moves the sheet between its snap points: `snapTo(index)` animates to a point\n * and `snapIndex()` tracks the current one (also after a drag).\n *\n * @example\n * const ref = sheets.open(FiltersComponent, { snapPoints: [0.4, 0.9] });\n * ref.snapTo(1);\n * const filters = await ref.afterClosed;\n\n * @since 0.2.0\n * @summary Handle on an open bottom sheet that moves it between snap points and closes it\n */\nexport class BfSheetRef<T = unknown> extends BfOverlayRef<T> {\n private readonly snapIndexState: WritableSignal<number>;\n /** Index of the snap point the sheet rests at (or is settling to). */\n readonly snapIndex: Signal<number>;\n private snapHandler: ((index: number) => void) | null = null;\n\n constructor(\n private readonly sheetController: BfSheetController,\n initialIndex = 0,\n ) {\n super(sheetController);\n this.snapIndexState = signal(initialIndex);\n this.snapIndex = this.snapIndexState.asReadonly();\n }\n\n /** True from the first close on, whichever way the sheet is being closed. */\n override get isClosing(): boolean {\n return super.isClosing || this.sheetController.isClosing();\n }\n\n /**\n * Animates the sheet to the snap point at `index` (clamped to the configured points).\n * @param index The position of the snap point in `snapPoints`.\n */\n snapTo(index: number): void {\n if (this.isClosing) {\n return;\n }\n this.snapHandler?.(index);\n }\n\n /** @internal Connects the `bf-sheet` container that performs `snapTo`. */\n connect(handler: (index: number) => void): void {\n this.snapHandler = handler;\n }\n\n /** @internal Set by `bf-sheet` whenever the sheet settles on a snap point. */\n setSnapIndex(index: number): void {\n this.snapIndexState.set(index);\n }\n}\n","import { DOCUMENT, DestroyRef, type Signal, computed, inject, signal } from '@angular/core';\nimport { BfKeyboardService } from '../platform/keyboard.service';\n\n/** The soft keyboard as a bottom-anchored overlay sees it; internal. */\nexport interface BfKeyboardCover {\n /**\n * Part of the layout viewport's bottom edge the keyboard overlays, in CSS px: the keyboard\n * height where it overlays the WebView (native iOS), else the layout viewport below the\n * keyboard's top edge — 0 where the WebView resizes for it (native Android), the covered part\n * where only the visual viewport shrinks (a mobile browser).\n */\n readonly covered: number;\n /** The keyboard's top edge in viewport CSS px (`BfKeyboardService.keyboardTop()`). */\n readonly keyboardTop: number;\n}\n\n/**\n * The keyboard's effect on an overlay anchored to the bottom of the layout viewport, `null` while\n * it is hidden. Re-read on every keyboard signal change and on window and `visualViewport`\n * resizes, since the layout and visual viewport heights are not signals. Call in an injection\n * context.\n */\nexport function injectKeyboardCover(): Signal<BfKeyboardCover | null> {\n const keyboard = inject(BfKeyboardService);\n const view = inject(DOCUMENT).defaultView;\n const resizes = signal(0);\n const onResize = (): void => resizes.update((count) => count + 1);\n view?.addEventListener('resize', onResize);\n view?.visualViewport?.addEventListener('resize', onResize);\n inject(DestroyRef).onDestroy(() => {\n view?.removeEventListener('resize', onResize);\n view?.visualViewport?.removeEventListener('resize', onResize);\n });\n\n return computed(\n () => {\n resizes();\n if (!keyboard.visible()) {\n return null;\n }\n const height = keyboard.height();\n const keyboardTop = Math.round(keyboard.keyboardTop());\n const covered = keyboard.overlaysContent()\n ? height\n : Math.max(0, Math.round((view?.innerHeight ?? 0) - keyboardTop));\n return { covered, keyboardTop };\n },\n {\n equal: (a, b) => a?.covered === b?.covered && a?.keyboardTop === b?.keyboardTop,\n },\n );\n}\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\nimport { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n Injector,\n TemplateRef,\n type Type,\n afterNextRender,\n afterRenderEffect,\n computed,\n inject,\n viewChild,\n} from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfKeyboardAssistDirective } from '../../content/keyboard-assist/keyboard-assist.directive';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { IOS_EASING } from '../../motion';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { injectKeyboardCover } from '../keyboard-cover';\nimport {\n BF_OVERLAY_DATA,\n BfOverlayRef,\n type BfOverlayTemplateContext,\n OVERLAY_DURATION,\n OVERLAY_EASING,\n ɵwatchPanelHeading as watchPanelHeading,\n} from '@bitforgehq/ui/overlay';\nimport { BfSheetRef } from './sheet-ref';\n\n/** What `BfSheetService` hands the `bf-sheet` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfSheetConfig {\n content: Type<unknown> | TemplateRef<unknown>;\n /** Ascending fractions of the viewport height. */\n snapPoints: number[];\n initial: number;\n dismissible: boolean;\n /** The caller's data, re-provided to the content as `BF_OVERLAY_DATA`. */\n data: unknown;\n /** No `ariaLabel` was given: the panel is named by the content's first heading, if any. */\n labelledByHeading: boolean;\n ref: BfSheetRef<unknown>;\n}\n\n/** Travel (px) a press must clear before it becomes a drag, so taps still reach buttons. */\nconst SLOP = 6;\n/** Release velocity (px/ms) above which the direction of the fling picks the snap point. */\nconst FLING_VELOCITY = 0.3;\n/** Past the top point (and below the lowest when not dismissible) the sheet moves at 30 %. */\nconst RUBBER_BAND = 0.3;\n/** A release this far below the lowest point, relative to its height, dismisses the sheet. */\nconst DISMISS_FRACTION = 0.25;\n/** Pointer samples older than this (ms) are ignored for the release velocity. */\nconst VELOCITY_WINDOW = 100;\n/** Gap (px) kept between a keyboard-capped sheet's top edge and the safe area's. */\nconst KEYBOARD_TOP_GAP = 8;\n/** The lift follows the keyboard: about as long as its slide, on the iOS curve. */\nconst KEYBOARD_LIFT_TRANSITION = `translate 250ms ${IOS_EASING}`;\n\ninterface BfSheetGesture {\n readonly pointerId: number;\n readonly startX: number;\n readonly startY: number;\n /** Visible height of the sheet when the press started. */\n readonly startHeight: number;\n /** The handle row or a `[data-bf-sheet-drag]` element, or the scrolling content. */\n readonly source: 'handle' | 'content';\n readonly reducedMotion: boolean;\n claimed: boolean;\n /** Raw, un-rubber-banded visible height the pointer asks for. */\n proposed: number;\n samples: { t: number; y: number }[];\n}\n\n/**\n * The bottom-sheet container that `BfSheetService.open()` renders on the overlay core: a\n * bottom-anchored `bg-surface` card with a grabber, resting at snap points (fractions of the\n * viewport height, applied as `--bf-sheet-height`) and rendering the content component or\n * template with `BF_OVERLAY_DATA` and `BfSheetRef` injectable. Dragging the handle row, any\n * element marked `data-bf-sheet-drag` (a header) or the content (unless it can scroll up) moves\n * the sheet with Pointer Events under pointer capture: rubber-banded above the top point, released\n * onto the nearest point (a fling faster than 0.3 px/ms picks the direction), and dismissed when\n * released more than 25 % below the lowest point or flung down from it — if the sheet is\n * dismissible. The content scrolls only at the top snap point, and at every point while the soft\n * keyboard is up. While the keyboard overlays the sheet's bottom edge (iOS, a mobile browser) the\n * sheet is lifted by the covered height (`translate`, its bottom safe-area padding dropped); while\n * the keyboard is up its height is capped at the keyboard's top edge minus the safe-area top minus\n * 8 px (a drag starts from that capped height), and the content region's `bfKeyboardAssist`\n * scrolls the focused control 24 px clear of the keyboard. The lift follows the keyboard (250 ms,\n * instant under `prefers-reduced-motion`). Hiding the keyboard restores the snap height at once\n * (the drop is instant, so the top edge stays on screen) and scrolls content left scrolled below\n * the top point back to its top. With two or more snap points the grabber is a button: a press,\n * `Enter` or `Space` moves one point up (from the top point back to the lowest), the arrow keys\n * step, `Home` and `End` jump; it is named `expand` or `collapse` (`BF_LABELS`) after the next\n * press, then `sheetPosition`, and every settle on another point announces `sheetPosition`\n * politely. A dismissible sheet starts with a visually hidden Close (`BF_LABELS.close`) that screen\n * readers reach and a keyboard user sees when focused. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfSheetService).open(NewTaskComponent, { snapPoints: [0.5, 0.92] });\n * @summary Bottom sheet with snap points, drag to dismiss and a backdrop\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-sheet',\n imports: [NgComponentOutlet, NgTemplateOutlet, BfKeyboardAssistDirective],\n // A fresh page context: a `bf-content` in the sheet never takes over the page's (it would become\n // a second `main` inside the dialog and replace the page's registered scroller).\n providers: [BfPageContext],\n host: {\n class:\n 'bg-surface rounded-t-card shadow-floating pb-safe-bottom relative flex h-(--bf-sheet-height) w-screen flex-col',\n },\n template: `\n @if (dismissible) {\n <button\n type=\"button\"\n data-bf-sheet-close\n data-bf-overlay-chrome\n class=\"focus-visible:bg-surface-muted focus-visible:text-label-sm focus-visible:text-text focus-visible:ring-ring sr-only focus-visible:not-sr-only focus-visible:absolute focus-visible:end-3 focus-visible:top-3 focus-visible:z-10 focus-visible:rounded-full focus-visible:px-3 focus-visible:py-1 focus-visible:ring-4 focus-visible:outline-none\"\n (click)=\"close()\"\n >\n {{ labels.close }}\n </button>\n }\n <div\n data-bf-sheet-handle\n class=\"group/bf-sheet-handle flex h-6 shrink-0 cursor-grab touch-none items-center justify-center active:cursor-grabbing\"\n >\n @if (adjustable) {\n <button\n type=\"button\"\n data-bf-sheet-grabber-button\n data-bf-overlay-chrome\n class=\"rounded-t-card focus-visible:ring-ring relative flex h-6 w-full cursor-grab items-center justify-center focus-visible:ring-4 focus-visible:outline-none focus-visible:ring-inset active:cursor-grabbing pointer-coarse:after:absolute pointer-coarse:after:inset-x-0 pointer-coarse:after:top-0 pointer-coarse:after:-bottom-5 pointer-coarse:after:content-['']\"\n [attr.aria-label]=\"grabberLabel()\"\n (click)=\"onGrabberClick($event)\"\n (keydown)=\"onGrabberKeydown($event)\"\n >\n <span\n aria-hidden=\"true\"\n data-bf-sheet-grabber\n class=\"bg-control-off group-hover/bf-sheet-handle:bg-text-muted h-1.25 w-9 rounded-full transition-colors forced-colors:bg-[ButtonText]\"\n ></span>\n </button>\n } @else {\n <div\n aria-hidden=\"true\"\n data-bf-sheet-grabber\n class=\"bg-control-off group-hover/bf-sheet-handle:bg-text-muted h-1.25 w-9 rounded-full transition-colors forced-colors:bg-[ButtonText]\"\n ></div>\n }\n </div>\n <div #content data-bf-sheet-content bfKeyboardAssist [class]=\"contentClass()\">\n @if (template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"templateContext\"\n [ngTemplateOutletInjector]=\"contentInjector\"\n />\n } @else if (component) {\n <ng-container\n [ngComponentOutlet]=\"component\"\n [ngComponentOutletInjector]=\"contentInjector\"\n />\n }\n </div>\n `,\n})\nexport class BfSheetComponent {\n private readonly config = inject(BF_OVERLAY_DATA) as BfSheetConfig;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly keyboard = inject(BfKeyboardService);\n private readonly keyboardCover = injectKeyboardCover();\n private readonly contentElement = viewChild.required<ElementRef<HTMLElement>>('content');\n private readonly contentAssist = viewChild.required(BfKeyboardAssistDirective);\n\n protected readonly template =\n this.config.content instanceof TemplateRef ? this.config.content : null;\n protected readonly component =\n this.config.content instanceof TemplateRef ? null : this.config.content;\n protected readonly contentInjector = Injector.create({\n parent: inject(Injector),\n providers: [\n { provide: BF_OVERLAY_DATA, useValue: this.config.data },\n { provide: BfOverlayRef, useValue: this.config.ref },\n { provide: BfSheetRef, useValue: this.config.ref },\n ],\n });\n protected readonly templateContext: BfOverlayTemplateContext = {\n $implicit: this.config.ref,\n data: this.config.data,\n };\n\n private readonly lastIndex = this.config.snapPoints.length - 1;\n protected readonly labels = inject(BF_LABELS);\n protected readonly dismissible = this.config.dismissible;\n /** Two or more snap points: the grabber is a button that moves between them. */\n protected readonly adjustable = this.lastIndex > 0;\n private readonly announcer = inject(LiveAnnouncer);\n /**\n * What the next press does (collapse at the top point, else expand), then where the sheet is:\n * \"Expand, Sheet at 1 of 2\".\n */\n protected readonly grabberLabel = computed(() => {\n const index = this.config.ref.snapIndex();\n const action = index === this.lastIndex ? this.labels.collapse : this.labels.expand;\n return `${action}, ${this.labels.sheetPosition(index + 1, this.lastIndex + 1)}`;\n });\n /** A drag that started on the grabber button ends without the click that may follow it. */\n private suppressGrabberClick = false;\n /**\n * The content scrolls at the top snap point and whenever the keyboard is up (the sheet may be\n * capped below its content); otherwise drags move the sheet.\n */\n private readonly contentScrolls = computed(\n () => this.config.ref.snapIndex() === this.lastIndex || this.keyboard.visible(),\n );\n protected readonly contentClass = computed(\n () =>\n `min-h-0 flex-1 overscroll-contain ${\n this.contentScrolls() ? 'overflow-y-auto' : 'overflow-hidden'\n }`,\n );\n\n private index = this.config.initial;\n /** Rendered height (px) and downward translation (px) below the lowest point. */\n private height = 0;\n private offset = 0;\n private gesture: BfSheetGesture | null = null;\n private settleAnimation: Animation | null = null;\n\n constructor() {\n // Content in a dialog is not the document's `main`.\n inject(BfPageContext, { self: true }).mainLandmark.set(false);\n this.apply(this.heights()[this.index] ?? 0, 0);\n this.config.ref.setSnapIndex(this.index);\n this.config.ref.connect((index) => this.snapTo(index));\n this.host.addEventListener('pointerdown', this.onPointerDown);\n this.host.addEventListener('transitionend', this.onTransitionEnd);\n const view = this.document.defaultView;\n view?.addEventListener('resize', this.onResize);\n // The host's own styles, so they are written in the render phase.\n afterRenderEffect({ write: () => this.applyKeyboard() });\n if (this.config.labelledByHeading) {\n let stopWatching: (() => void) | null = null;\n afterNextRender(() => {\n const panel = this.host.closest<HTMLElement>('.bf-overlay-panel');\n if (panel) {\n stopWatching = watchPanelHeading(panel);\n }\n });\n inject(DestroyRef).onDestroy(() => stopWatching?.());\n }\n // Content left scrolled while the keyboard was up starts at its top again once the keyboard\n // hides below the top point, since nothing could scroll it back there.\n let keyboardWasUp = false;\n afterRenderEffect(() => {\n const keyboardUp = this.keyboard.visible();\n if (keyboardWasUp && !keyboardUp && !this.contentScrolls()) {\n this.contentElement().nativeElement.scrollTop = 0;\n }\n keyboardWasUp = keyboardUp;\n });\n inject(DestroyRef).onDestroy(() => {\n this.endGesture();\n this.settleAnimation?.cancel();\n this.host.removeEventListener('pointerdown', this.onPointerDown);\n this.host.removeEventListener('transitionend', this.onTransitionEnd);\n view?.removeEventListener('resize', this.onResize);\n });\n }\n\n /**\n * Lifts the sheet by the part of the viewport the keyboard covers and caps its height at the\n * keyboard's top edge; both go away with the keyboard, at once. Separate from\n * `--bf-sheet-height`, so drag and snap logic never see them.\n */\n private applyKeyboard(): void {\n const cover = this.keyboardCover();\n if (this.config.ref.isClosing) {\n // A keyboard hiding during the exit would make the sliding sheet jump by the lift.\n return;\n }\n const style = this.host.style;\n // Only the lift slides. The drop is instant, together with the cap: an animated drop without\n // the cap put the full-height sheet's top edge above the screen for most of the slide.\n if (cover && !prefersReducedMotion(this.document.defaultView)) {\n style.setProperty('transition', KEYBOARD_LIFT_TRANSITION);\n } else {\n style.removeProperty('transition');\n }\n if (cover && cover.covered > 0) {\n style.setProperty('translate', `0 ${-cover.covered}px`);\n // The keyboard covers the home indicator: no bottom safe-area padding above it.\n style.setProperty('padding-bottom', '0px');\n } else {\n style.removeProperty('translate');\n style.removeProperty('padding-bottom');\n }\n if (cover) {\n style.setProperty(\n 'max-height',\n `calc(${cover.keyboardTop}px - var(--spacing-safe-top) - ${KEYBOARD_TOP_GAP}px)`,\n );\n } else {\n style.removeProperty('max-height');\n }\n }\n\n /** The content's keyboard assist measured a sheet still sliding up: it measures once more. */\n private readonly onTransitionEnd = (event: TransitionEvent): void => {\n if (event.target === this.host && event.propertyName === 'translate') {\n this.contentAssist().refresh();\n }\n };\n\n protected close(): void {\n this.config.ref.close();\n }\n\n /**\n * A press: one point higher, or from the top point back to the lowest. The click a pointer drag\n * may leave behind is swallowed; a keyboard or assistive-technology click (`detail` 0) never is.\n */\n protected onGrabberClick(event: MouseEvent): void {\n const afterDrag = this.suppressGrabberClick;\n this.suppressGrabberClick = false;\n if (afterDrag && event.detail !== 0) {\n return;\n }\n const index = this.config.ref.snapIndex();\n this.config.ref.snapTo(index >= this.lastIndex ? 0 : index + 1);\n }\n\n /** Arrow keys step between points (never past the ends, never dismissing); Home and End jump. */\n protected onGrabberKeydown(event: KeyboardEvent): void {\n this.suppressGrabberClick = false;\n if (event.altKey || event.ctrlKey || event.metaKey) {\n // Browser and assistive-technology shortcuts, not the sheet's.\n return;\n }\n const index = this.config.ref.snapIndex();\n const targets: Record<string, number> = {\n ArrowUp: Math.min(index + 1, this.lastIndex),\n ArrowDown: Math.max(index - 1, 0),\n Home: 0,\n End: this.lastIndex,\n };\n const target = targets[event.key];\n if (target === undefined) {\n return;\n }\n event.preventDefault();\n this.config.ref.snapTo(target);\n }\n\n private snapTo(index: number): void {\n const clamped = Math.min(Math.max(Math.trunc(index), 0), this.lastIndex);\n this.endGesture();\n this.settleTo(clamped, prefersReducedMotion(this.document.defaultView));\n }\n\n /** Viewport height with the keyboard hidden: the `100lvh` probe, else `innerHeight`. */\n private viewportHeight(): number {\n const rest = this.keyboard.restHeight();\n return rest > 0 ? rest : (this.document.defaultView?.innerHeight ?? 0);\n }\n\n private heights(): number[] {\n const viewport = this.viewportHeight();\n return this.config.snapPoints.map((fraction) => Math.round(fraction * viewport));\n }\n\n private readonly onResize = (): void => {\n if (!this.gesture) {\n this.settleAnimation?.cancel();\n this.apply(this.heights()[this.index] ?? 0, 0);\n }\n };\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n if (\n this.gesture ||\n !event.isPrimary ||\n this.config.ref.isClosing ||\n (event.pointerType === 'mouse' && event.button !== 0)\n ) {\n return;\n }\n this.suppressGrabberClick = false;\n const target = event.target instanceof Element ? event.target : null;\n const content = this.contentElement().nativeElement;\n const dragHandle = target?.closest('[data-bf-sheet-handle], [data-bf-sheet-drag]');\n let source: BfSheetGesture['source'];\n if (dragHandle && this.host.contains(dragHandle)) {\n source = 'handle';\n } else if (target && content.contains(target)) {\n source = 'content';\n } else {\n return;\n }\n this.captureAnimatedHeight();\n const visible = this.renderedHeight() - this.offset;\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n startHeight: visible,\n source,\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n claimed: false,\n proposed: visible,\n samples: [{ t: performance.now(), y: event.clientY }],\n };\n this.host.addEventListener('pointermove', this.onPointerMove);\n this.host.addEventListener('pointerup', this.onPointerUp);\n this.host.addEventListener('pointercancel', this.onPointerCancel);\n this.host.addEventListener('lostpointercapture', this.onLostPointerCapture);\n this.host.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n /**\n * Whether a content drag may move the sheet: always while the content does not scroll (below the\n * top snap point, keyboard hidden), and otherwise only downward while it is scrolled to 0.\n */\n private contentMayDrag(dy: number): boolean {\n if (!this.contentScrolls()) {\n return true;\n }\n return dy > 0 && this.contentElement().nativeElement.scrollTop <= 0;\n }\n\n /**\n * Non-passive `touchmove` guard, attached only while a press is tracked (the refresher's\n * pattern): keeps the browser from taking the drag as a pan, which would end the pointer stream\n * with `pointercancel`. Content drags the sheet does not want (upward at the top point,\n * horizontal) are left to scroll.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n const gesture = this.gesture;\n if (!gesture || !event.cancelable) {\n return;\n }\n if (gesture.claimed || gesture.source === 'handle') {\n event.preventDefault();\n return;\n }\n const touch = event.touches[0];\n if (!touch) {\n return;\n }\n const dy = touch.clientY - gesture.startY;\n const dx = touch.clientX - gesture.startX;\n if (Math.abs(dy) > Math.abs(dx) && this.contentMayDrag(dy)) {\n event.preventDefault();\n }\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n const dy = event.clientY - gesture.startY;\n if (!gesture.claimed) {\n const dx = event.clientX - gesture.startX;\n if (Math.abs(dy) <= SLOP && Math.abs(dx) <= SLOP) {\n return;\n }\n if (\n Math.abs(dx) >= Math.abs(dy) ||\n (gesture.source === 'content' && !this.contentMayDrag(dy))\n ) {\n // A horizontal move, or a content drag the content scrolls itself: not the sheet's.\n this.endGesture();\n return;\n }\n gesture.claimed = true;\n try {\n this.host.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic pointers (tests) have no active pointer to capture.\n }\n }\n if (event.cancelable) {\n event.preventDefault();\n }\n const now = performance.now();\n gesture.samples = [\n ...gesture.samples.filter((s) => now - s.t <= VELOCITY_WINDOW),\n { t: now, y: event.clientY },\n ];\n gesture.proposed = gesture.startHeight - dy;\n // Direct manipulation: follows the pointer even under prefers-reduced-motion.\n this.show(gesture.proposed);\n };\n\n /** Renders a proposed visible height: rubber-banded past the ends, translated below the lowest. */\n private show(proposed: number): void {\n const heights = this.heights();\n const lowest = heights[0] ?? 0;\n const top = heights[heights.length - 1] ?? 0;\n if (proposed > top) {\n this.apply(top + (proposed - top) * RUBBER_BAND, 0);\n } else if (proposed >= lowest) {\n this.apply(proposed, 0);\n } else {\n const below = lowest - proposed;\n this.apply(lowest, this.config.dismissible ? below : below * RUBBER_BAND);\n }\n }\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n this.endGesture();\n if (!gesture.claimed) {\n // An unclaimed press (under the slop) ends as a click: on the grabber button it cycles.\n return;\n }\n this.suppressGrabberClick = true;\n this.release(gesture, this.velocity(gesture));\n };\n\n private readonly onPointerCancel = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n this.endGesture();\n if (gesture.claimed) {\n this.settleTo(this.index, gesture.reducedMotion);\n }\n };\n\n /** Only the host losing the capture it took ends the drag (a child's implicit capture bubbles). */\n private readonly onLostPointerCapture = (event: PointerEvent): void => {\n if (event.target === this.host) {\n this.onPointerCancel(event);\n }\n };\n\n /** Downward velocity (px/ms) over the last samples; 0 when they carry no elapsed time. */\n private velocity(gesture: BfSheetGesture): number {\n const first = gesture.samples[0];\n const last = gesture.samples[gesture.samples.length - 1];\n if (!first || !last || last.t <= first.t) {\n return 0;\n }\n return (last.y - first.y) / (last.t - first.t);\n }\n\n private release(gesture: BfSheetGesture, velocity: number): void {\n const heights = this.heights();\n const lowest = heights[0] ?? 0;\n const proposed = gesture.proposed;\n let target: number | null;\n if (velocity > FLING_VELOCITY) {\n // Downward fling: the next point below, or out.\n const below = heights.filter((h) => h < proposed).length - 1;\n target = below >= 0 ? below : null;\n } else if (velocity < -FLING_VELOCITY) {\n const above = heights.findIndex((h) => h > proposed);\n target = above >= 0 ? above : this.lastIndex;\n } else if (proposed < lowest * (1 - DISMISS_FRACTION)) {\n target = null;\n } else {\n target = this.nearest(heights, proposed);\n }\n if (target === null) {\n if (this.config.dismissible) {\n this.config.ref.close();\n return;\n }\n target = 0;\n }\n this.settleTo(target, gesture.reducedMotion);\n }\n\n private nearest(heights: number[], proposed: number): number {\n let best = 0;\n heights.forEach((h, i) => {\n if (Math.abs(h - proposed) < Math.abs((heights[best] ?? 0) - proposed)) {\n best = i;\n }\n });\n return best;\n }\n\n /** Animates (instantly under reduced motion) to the snap point at `index`. */\n private settleTo(index: number, reducedMotion: boolean): void {\n const target = this.heights()[index] ?? 0;\n const fromHeight = this.height;\n const fromOffset = this.offset;\n this.settleAnimation?.cancel();\n this.settleAnimation = null;\n const moved = index !== this.index;\n this.index = index;\n this.config.ref.setSnapIndex(index);\n if (moved && this.adjustable) {\n // The new position is announced politely whichever way it came (button, key, drag, snapTo).\n void this.announcer.announce(\n this.labels.sheetPosition(index + 1, this.lastIndex + 1),\n 'polite',\n );\n }\n // The end state is written at once; the animation (no fill) plays over it.\n this.apply(target, 0);\n if (reducedMotion || (fromHeight === target && fromOffset === 0)) {\n return;\n }\n const animation = this.host.animate(\n [\n { height: `${fromHeight}px`, transform: `translate3d(0,${fromOffset}px,0)` },\n { height: `${target}px`, transform: 'translate3d(0,0,0)' },\n ],\n { duration: OVERLAY_DURATION, easing: OVERLAY_EASING },\n );\n this.settleAnimation = animation;\n void animation.finished.then(\n () => {\n if (this.settleAnimation === animation) {\n this.settleAnimation = null;\n }\n },\n () => {\n // Cancelled by a newer drag or settle, which owns the sheet now.\n },\n );\n }\n\n /**\n * The height the sheet shows: its snap height, or less while the keyboard caps it (`max-height`),\n * so a drag of a capped sheet moves it from the first pixel.\n */\n private renderedHeight(): number {\n const rendered = this.host.offsetHeight;\n return this.keyboardCover() && rendered > 0 ? Math.min(this.height, rendered) : this.height;\n }\n\n /** A press during a settle continues from where the animation had got to. */\n private captureAnimatedHeight(): void {\n if (!this.settleAnimation) {\n return;\n }\n const animated = parseFloat(\n this.document.defaultView?.getComputedStyle(this.host).height ?? '',\n );\n this.settleAnimation.cancel();\n this.settleAnimation = null;\n if (Number.isFinite(animated) && animated > 0) {\n this.apply(animated, 0);\n }\n }\n\n private apply(height: number, offset: number): void {\n this.height = height;\n this.offset = offset;\n this.host.style.setProperty('--bf-sheet-height', `${height}px`);\n this.host.style.transform = offset > 0 ? `translate3d(0,${offset}px,0)` : '';\n }\n\n private endGesture(): void {\n const pointerId = this.gesture?.pointerId;\n this.host.removeEventListener('pointermove', this.onPointerMove);\n this.host.removeEventListener('pointerup', this.onPointerUp);\n this.host.removeEventListener('pointercancel', this.onPointerCancel);\n this.host.removeEventListener('lostpointercapture', this.onLostPointerCapture);\n this.host.removeEventListener('touchmove', this.touchMoveGuard);\n if (pointerId !== undefined) {\n try {\n this.host.releasePointerCapture(pointerId);\n } catch {\n // Already released (or never captured, e.g. a synthetic pointer in tests).\n }\n }\n this.gesture = null;\n }\n}\n","import { Injectable, Injector, type TemplateRef, type Type, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport {\n type BfComponentOverlayRef,\n type BfOverlayAutoFocus,\n BfOverlayService,\n BfOverlayStack,\n slideDown,\n slideUp,\n} from '@bitforgehq/ui/overlay';\nimport { BfSheetComponent, type BfSheetConfig } from './sheet.component';\nimport { BfSheetRef } from './sheet-ref';\n\n/**\n * Where focus goes when a sheet opens: the sheet itself (`panel`), so no soft keyboard comes up\n * and a screen reader starts reading at the sheet, or its first field (`first-field`): the element\n * marked `[autofocus]`/`[cdkFocusInitial]`, else the first focusable element of the content, which\n * raises the keyboard on Android when it is a text field.\n *\n * @summary Where focus lands when a sheet opens, the sheet itself or its first field\n * @since 0.3.0\n */\nexport type BfSheetAutoFocus = 'panel' | 'first-field';\n\n/** The overlay core's focus target for each sheet setting. */\nconst AUTO_FOCUS: Record<BfSheetAutoFocus, BfOverlayAutoFocus> = {\n panel: 'panel',\n 'first-field': 'first-tabbable',\n};\n\n/**\n * Options of `BfSheetService.open()`.\n *\n * @summary Options of a bottom sheet: where it rests, how it closes and its name\n * @since 0.2.0\n */\nexport interface BfSheetOptions {\n /**\n * Heights the sheet rests at, as fractions of the viewport height (default `[0.5, 0.92]`).\n * @default [0.5, 0.92]\n */\n snapPoints?: number[];\n /**\n * Index of the snap point the sheet opens at (default 0).\n * @default 0\n */\n initial?: number;\n /**\n * The backdrop, `Escape`, the hardware back button and a downward drag close the sheet\n * (default true). When false only `close()` does, or a navigation (see `closeOnNavigation`).\n * @default true\n */\n dismissible?: boolean;\n /** Injected into the content as `BF_OVERLAY_DATA`. */\n data?: unknown;\n /**\n * A navigation to another URL path (a pushed or popped page, a tab switch) closes the sheet with\n * no result (default true), also a non-dismissible one.\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Accessible name of the sheet. Without it the sheet is named by the first heading in its\n * content (`aria-labelledby`), else by `provideBfLabels()`'s `sheet` (\"Sheet\").\n */\n ariaLabel?: string;\n /**\n * Where focus goes on open: `panel` (default) focuses the sheet itself (a `role=\"dialog\"` with a\n * name and `tabindex=\"-1\"`), so a screen reader lands inside it and no soft keyboard hides it;\n * `first-field` focuses the element marked `[autofocus]`/`[cdkFocusInitial]`, else the first\n * focusable element of the content (never the sheet's own Close and grabber), which raises the\n * keyboard on Android when it is a text field.\n * @default 'panel'\n */\n autoFocus?: BfSheetAutoFocus;\n /**\n * Injector the sheet resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Snap points used when none (or no valid ones) are given.\n *\n * @summary The snap points a bottom sheet rests at when none are given, half and full\n * @since 0.2.0\n */\nexport const BF_SHEET_DEFAULT_SNAP_POINTS: readonly number[] = [0.5, 0.92];\n\nfunction normalizeSnapPoints(points: readonly number[] | undefined): number[] {\n const valid = (points ?? [])\n .filter((p) => Number.isFinite(p) && p > 0)\n .map((p) => Math.min(p, 1))\n .sort((a, b) => a - b);\n const unique = valid.filter((p, i) => i === 0 || p !== valid[i - 1]);\n return unique.length > 0 ? unique : [...BF_SHEET_DEFAULT_SNAP_POINTS];\n}\n\n/**\n * Opens bottom sheets: a component or template in a `bf-sheet` container on the overlay core,\n * bottom-anchored over a backdrop, resting at snap points (fractions of the viewport height).\n * The content injects `BF_OVERLAY_DATA` and `BfSheetRef` (also as `BfOverlayRef`) to read its\n * data, move between snap points and close with a result. A dismissible sheet (the default)\n * closes on a backdrop tap, `Escape`, the hardware back button and a drag below its lowest\n * point; a non-dismissible one only through `close()`, and swallows the back button while open.\n * A navigation to another URL path closes either (`closeOnNavigation`). On open, focus goes to the\n * sheet itself, so no soft keyboard comes up (`autoFocus: 'first-field'` focuses the first field).\n *\n * @example\n * const ref = inject(BfSheetService).open<NewTaskComponent, Task>(NewTaskComponent, {\n * snapPoints: [0.5, 0.92],\n * data: { listId },\n * });\n * const task = await ref.afterClosed;\n\n * @since 0.2.0\n * @summary Opens bottom sheets that rest at snap points and close with a result\n */\n@Injectable({ providedIn: 'root' })\nexport class BfSheetService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens `content` (a component or a template) in a bottom sheet.\n * @param content The component or template to render in the sheet.\n * @param options Where the sheet rests, how it closes and its name.\n */\n open<C, T = unknown>(\n content: Type<C> | TemplateRef<unknown>,\n options: BfSheetOptions = {},\n ): BfSheetRef<T> {\n const snapPoints = normalizeSnapPoints(options.snapPoints);\n const initial = Math.min(Math.max(Math.trunc(options.initial ?? 0), 0), snapPoints.length - 1);\n const dismissible = options.dismissible ?? true;\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n\n let overlay: BfComponentOverlayRef<BfSheetComponent> | null = null;\n let releaseBackGuard: (() => void) | null = null;\n const ref = new BfSheetRef<T>(\n {\n close: async (): Promise<void> => {\n releaseBackGuard?.();\n if (overlay) {\n overlay.close();\n await overlay.afterClosed;\n }\n },\n updatePosition: (): void => overlay?.updatePosition(),\n isClosing: (): boolean => overlay?.isClosing ?? false,\n },\n initial,\n );\n\n const config: BfSheetConfig = {\n content,\n snapPoints,\n initial,\n dismissible,\n data: options.data,\n labelledByHeading: !options.ariaLabel,\n ref,\n };\n overlay = this.overlays.open(BfSheetComponent, {\n data: config,\n position: 'bottom',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: dismissible,\n dismissOnEscape: dismissible,\n closeOnNavigation: options.closeOnNavigation,\n ariaLabel: options.ariaLabel ?? labels.sheet,\n autoFocus: AUTO_FOCUS[options.autoFocus ?? 'panel'],\n injector,\n enter: (el) => slideUp(el),\n exit: (el) => slideDown(el),\n });\n\n if (!dismissible) {\n // The stack's handler would close the sheet; claim the back button without closing it\n // instead, so it does not fall through to the page behind either.\n this.stack.releaseBack(overlay);\n releaseBackGuard = this.backButton.register(100, () => true);\n }\n // However the overlay closes (backdrop, Escape, back, drag, close()), settle the sheet ref.\n void overlay.afterClosed.then(() => ref.close());\n return ref;\n }\n}\n","import { NgComponentOutlet } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n type Type,\n afterNextRender,\n computed,\n inject,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { injectKeyboardCover } from '../keyboard-cover';\nimport {\n BF_OVERLAY_DATA,\n BfOverlayRef,\n ɵwatchPanelHeading as watchPanelHeading,\n} from '@bitforgehq/ui/overlay';\n\n/**\n * Where `bf-modal` sits: a page-sheet card, or edge to edge.\n *\n * @summary How a modal is presented: a page-sheet card over the stack, or fullscreen\n * @since 0.2.0\n */\nexport type BfModalPresentation = 'sheet' | 'fullscreen';\n\n/** What `BfModalService` hands the `bf-modal` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfModalConfig {\n content: Type<unknown>;\n presentation: BfModalPresentation;\n /** The caller's data, re-provided to the content as `BF_OVERLAY_DATA`. */\n data: unknown;\n /** No `ariaLabel` was given: the panel is named by the content's first heading, if any. */\n labelledByHeading: boolean;\n}\n\nconst HOST_CLASSES: Record<BfModalPresentation, string> = {\n // A transparent border is invisible here but forced colours paints it CanvasText, giving the\n // sheet card a top edge once its shadow and the parked page stack behind it both flatten to\n // Canvas; the fullscreen frame has no exposed edge (it fills the viewport) and needs none.\n sheet:\n 'absolute inset-x-0 bottom-0 top-[calc(var(--spacing-safe-top)+10px)] bg-bg rounded-t-card shadow-floating border border-transparent flex flex-col overflow-hidden',\n fullscreen: 'absolute inset-0 bg-bg flex flex-col',\n};\n\n/**\n * The modal frame that `BfModalService.open()` renders on the overlay core, `position:\n * 'fullscreen'`: a `sheet` presentation (default) is a card whose top sits 10 px below the top\n * safe area (UIKit's page-sheet inset; `bg-bg rounded-t-card shadow-floating`) over the parked page stack, which scales down and\n * grows rounded corners behind it (`--bf-modal-depth`, written by `BfOverlayStack`, consumed by\n * `bf-stack-outlet`'s host rule); `fullscreen` covers the viewport edge to edge with no scale.\n * Like `bf-page`, it provides a fresh `BfPageContext` so the content's `bf-header` and\n * `bf-content` work inside it (with `mainLandmark` false, so its content is no `main` landmark,\n * and in the `sheet` presentation with `safeAreaTop` false, so the header adds no status-bar band\n * inside the card) — the content supplies its own header (typically a close\n * `bf-icon-button` in `start`) and content region. While the soft keyboard overlays the bottom of\n * a `sheet` card (iOS, a mobile browser), the card is padded at the bottom by the covered height,\n * so the content's `bf-content` ends at the keyboard's top edge and its keyboard assist reveals\n * the focused control there. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfModalService).open(EditTaskComponent, { data: { taskId } });\n * @summary Modal overlay shown as an inset card over the scaled-down page, or full screen\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-modal',\n imports: [NgComponentOutlet],\n host: { '[class]': 'hostClasses', '[style.padding-bottom.px]': 'keyboardPadding()' },\n providers: [BfPageContext],\n template: `<ng-container\n [ngComponentOutlet]=\"config.content\"\n [ngComponentOutletInjector]=\"contentInjector\"\n />`,\n})\nexport class BfModalComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfModalConfig;\n private readonly outerRef = inject(BfOverlayRef);\n private readonly keyboardCover = injectKeyboardCover();\n\n protected readonly hostClasses = HOST_CLASSES[this.config.presentation];\n /** A sheet card ends at the keyboard's top edge; a fullscreen frame leaves it to `bf-content`. */\n protected readonly keyboardPadding = computed(() => {\n const covered = this.keyboardCover()?.covered ?? 0;\n return this.config.presentation === 'sheet' && covered > 0 ? covered : null;\n });\n protected readonly contentInjector = Injector.create({\n parent: inject(Injector),\n providers: [\n { provide: BF_OVERLAY_DATA, useValue: this.config.data },\n { provide: BfOverlayRef, useValue: this.outerRef },\n ],\n });\n\n constructor() {\n const pageContext = inject(BfPageContext, { self: true });\n // Content in a dialog is not the document's `main` (the page behind stays it, inert).\n pageContext.mainLandmark.set(false);\n // The card starts below the status bar, so its header needs no safe-area band of its own.\n if (this.config.presentation === 'sheet') {\n pageContext.safeAreaTop.set(false);\n }\n if (this.config.labelledByHeading) {\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n let stopWatching: (() => void) | null = null;\n afterNextRender(() => {\n const panel = host.closest<HTMLElement>('.bf-overlay-panel');\n if (panel) {\n stopWatching = watchPanelHeading(panel);\n }\n });\n inject(DestroyRef).onDestroy(() => stopWatching?.());\n }\n }\n}\n","import { Injectable, Injector, type Type, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport { BfPlatformService, resolveShellMode } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS } from '../../shell/shell-config';\nimport {\n type BfOverlayRef,\n BfOverlayService,\n BfOverlayStack,\n fadeDown,\n fadeUp,\n slideDown,\n slideUp,\n} from '@bitforgehq/ui/overlay';\nimport { BfModalComponent, type BfModalConfig, type BfModalPresentation } from './modal.component';\n\n/**\n * Options of `BfModalService.open()`.\n *\n * @summary Options of a modal: its data, presentation, dismissal and accessible name\n * @since 0.2.0\n */\nexport interface BfModalOptions {\n /** Injected into the content as `BF_OVERLAY_DATA`. */\n data?: unknown;\n /**\n * A page-sheet card over the parked page stack (default), or a fullscreen frame.\n * @default 'sheet'\n */\n presentation?: BfModalPresentation;\n /**\n * `Escape` and the hardware back button close the modal (default true); with false only the\n * content's own close control does, or a navigation (see `closeOnNavigation`), and back is\n * swallowed while the modal is open instead of\n * falling through to the page behind. There is no backdrop dismiss or drag-to-dismiss.\n * @default true\n */\n dismissible?: boolean;\n /**\n * A navigation to another URL path closes the modal with no result (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Accessible name of the modal panel. Without it the modal is named by the first heading in\n * its content (`aria-labelledby`, e.g. the `bf-header` title), else by `provideBfLabels()`'s\n * `modal` (\"Modal\").\n */\n ariaLabel?: string;\n /**\n * Injector the modal resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Opens modals: a component in a `bf-modal` frame on the overlay core (`position: 'fullscreen'`,\n * no backdrop dismiss). `presentation: 'sheet'` (default) is a page-sheet card 10 px below the\n * top safe area over the parked page stack, which in iOS mode scales down and grows rounded corners\n * behind it (`countsAsModal: true`, driving `--bf-modal-depth` on the registered\n * `bf-stack-outlet` roots; Material mode leaves the page unscaled); `'fullscreen'` covers the\n * viewport edge to edge and does not count towards that depth. iOS mode\n * slides the card up and down; Material mode fades it up (and down) 16 px. The content supplies\n * its own `bf-header` (`largeTitle=\"false\"`, typically a close `bf-icon-button` in `start`) and\n * `bf-content` — `bf-modal` provides a `BfPageContext` for them, like `bf-page`. It injects the\n * caller's `data` as `BF_OVERLAY_DATA` and closes itself with a result through the injected\n * `BfOverlayRef`, which `open()` also returns. A navigation to another URL path closes it with no\n * result (`closeOnNavigation`).\n *\n * @example\n * const ref = inject(BfModalService).open<EditTaskComponent, Task>(EditTaskComponent, {\n * data: { taskId },\n * });\n * const saved = await ref.afterClosed;\n * @since 0.2.0\n * @summary Opens a component as a modal page sheet over the stack, or fullscreen\n */\n@Injectable({ providedIn: 'root' })\nexport class BfModalService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly platform = inject(BfPlatformService);\n private readonly injector = inject(Injector);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n\n /**\n * Opens `component` as a modal.\n * @param component The component to render; it injects `BfOverlayRef` and `BF_OVERLAY_DATA`.\n * @param options The modal's data, presentation, dismissal and name.\n */\n open<C, T = unknown>(component: Type<C>, options: BfModalOptions = {}): BfOverlayRef<T> {\n const presentation = options.presentation ?? 'sheet';\n const dismissible = options.dismissible ?? true;\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n const mode = resolveShellMode(this.config, this.platform);\n const ios = mode === 'ios';\n\n const modalConfig: BfModalConfig = {\n content: component,\n presentation,\n data: options.data,\n labelledByHeading: !options.ariaLabel,\n };\n const ref = this.overlays.open<BfModalComponent, T>(BfModalComponent, {\n data: modalConfig,\n position: 'fullscreen',\n // The parked page scales down behind a page sheet in iOS mode only; Material fades up\n // over it.\n countsAsModal: presentation === 'sheet' && ios,\n backPriority: 100,\n dismissOnBackdrop: false,\n dismissOnEscape: dismissible,\n closeOnNavigation: options.closeOnNavigation,\n ariaLabel: options.ariaLabel ?? labels.modal,\n injector,\n enter: ios ? (el) => slideUp(el) : (el) => fadeUp(el),\n exit: ios ? (el) => slideDown(el) : (el) => fadeDown(el),\n });\n\n if (!dismissible) {\n // The stack's handler would close the modal; claim the back button without closing it\n // instead, so it does not fall through to the page behind either.\n this.stack.releaseBack(ref);\n const releaseBackGuard = this.backButton.register(100, () => true);\n void ref.afterClosed.then(() => releaseBackGuard());\n }\n return ref;\n }\n}\n","import { Component, ElementRef, afterRenderEffect, inject } from '@angular/core';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { BF_OVERLAY_DATA } from '@bitforgehq/ui/overlay';\n\n/**\n * Where the toast pill anchors in the viewport.\n *\n * @summary Where a mobile toast pill anchors in the viewport, at the bottom or top\n * @since 0.2.0\n */\nexport type BfToastPosition = 'bottom' | 'top';\n\n/** The action rendered as a real button inside the pill; internal. */\nexport interface BfToastActionConfig {\n label: string;\n run: () => void;\n}\n\n/** What holds a toast's timer: the pointer over it, or focus inside it; internal. */\nexport type BfToastHoldSource = 'pointer' | 'focus';\n\n/** What `BfToastService` hands the `bf-toast` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfToastConfig {\n message: string;\n action: BfToastActionConfig | null;\n position: BfToastPosition;\n /** Told when the pointer or focus enters (`held: true`) or leaves the pill; pauses the timer. */\n hold?: (source: BfToastHoldSource, held: boolean) => void;\n /** `Escape` pressed while focus is inside the pill; the service dismisses the toast. */\n escape?: () => void;\n}\n\nfunction relatedTarget(event: Event): EventTarget | null {\n return event instanceof FocusEvent ? event.relatedTarget : null;\n}\n\nconst POSITION_CLASS: Record<BfToastPosition, string> = {\n bottom: 'bf-toast-bottom-offset',\n // A margin, never padding: the host is the pill, and a padding-top would override its own `py-3`.\n top: 'mt-[calc(var(--spacing-safe-top)+var(--spacing)*4)]',\n};\n\n/**\n * The status pill that `BfToastService.show()` renders on the overlay core (`passive: true`, no\n * backdrop, no focus trap, no inert page and no back handler — it never steals focus): an ink\n * `bg-text text-bg rounded-full shadow-floating` pill holding the message (truncated to two lines)\n * and an optional action. The pill is not a live region: the service announces each toast once\n * through CDK `LiveAnnouncer`. The pointer over it or focus inside it pauses its timer, and\n * `Escape` with focus inside dismisses it. `position: 'bottom'` (default) sits 16 px above the\n * tab-bar inset (`--bf-tab-bar-inset-global`, published on `<html>` by `bf-tabs`, which leaves\n * 8 px above the floating bar, so the toast clears it by 24 px) and the soft keyboard\n * (`--bf-toast-keyboard` on the host: `BfKeyboardService.height()` where the keyboard overlays\n * the WebView — native iOS — and 0 where the WebView already shrinks); `position: 'top'` sits\n * 16 px below the status bar (a top margin of the safe-area inset plus 16 px, outside the pill)\n * instead. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfToastService).show({ message: 'Saved' });\n * @summary Passive status pill with an optional action that never steals focus\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-toast',\n host: {\n '[class]': 'classes',\n '(pointerenter)': \"hold('pointer', true)\",\n '(pointerleave)': \"hold('pointer', false)\",\n '(focusin)': \"hold('focus', true)\",\n '(focusout)': 'focusLeft($event)',\n '(keydown)': 'keydown($event)',\n },\n styles: `\n :host.bf-toast-bottom-offset {\n margin-bottom: calc(\n var(--bf-tab-bar-inset-global, 0px) + 16px + var(--bf-toast-keyboard, 0px)\n );\n }\n `,\n template: `\n <span class=\"line-clamp-2 min-w-0 flex-1\">{{ config.message }}</span>\n @if (config.action; as action) {\n <button\n type=\"button\"\n class=\"text-bg focus-visible:ring-ring min-h-hit -mx-3 -my-2.5 inline-flex shrink-0 cursor-pointer items-center rounded-full px-3 font-bold underline underline-offset-4 transition-opacity hover:opacity-80 focus-visible:ring-4 focus-visible:outline-hidden\"\n (click)=\"action.run()\"\n >\n {{ action.label }}\n </button>\n }\n `,\n})\nexport class BfToastComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfToastConfig;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly keyboard = inject(BfKeyboardService);\n\n protected readonly classes =\n 'bg-text text-bg rounded-full shadow-floating px-5 py-3 text-body flex items-center gap-4 ' +\n `max-w-[calc(100vw-2*var(--spacing-gutter))] ${POSITION_CLASS[this.config.position]}`;\n\n constructor() {\n // The host's own style, so it is written in the render phase.\n afterRenderEffect({\n write: () => {\n // Android and the web resize the viewport for the keyboard, so its bottom already is the\n // keyboard's top; only an overlaying keyboard (native iOS) needs lifting over.\n const lift = this.keyboard.overlaysContent() ? this.keyboard.height() : 0;\n this.host.style.setProperty('--bf-toast-keyboard', `${lift}px`);\n },\n });\n }\n\n protected hold(source: BfToastHoldSource, held: boolean): void {\n this.config.hold?.(source, held);\n }\n\n /** Focus moving between elements inside the pill still holds it. */\n protected focusLeft(event: Event): void {\n const to = relatedTarget(event);\n if (to instanceof Node && this.host.contains(to)) {\n return;\n }\n this.hold('focus', false);\n }\n\n /** `Escape` with focus inside dismisses the toast; claimed, so no overlay underneath sees it. */\n protected keydown(event: Event): void {\n if (!(event instanceof KeyboardEvent) || event.key !== 'Escape' || !this.config.escape) {\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n this.config.escape();\n }\n}\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\nimport { DOCUMENT, DestroyRef, Injectable, inject } from '@angular/core';\nimport { REDUCED_DURATION, prefersReducedMotion } from '../../navigation/transitions';\nimport {\n type BfToastAction,\n type BfToastDismissReason,\n type BfToastRef,\n BfOverlayService,\n fadeOut,\n OVERLAY_DURATION,\n OVERLAY_EASING,\n type ɵBfToastHandle,\n ɵBfToastQueue,\n type ɵBfToastView,\n} from '@bitforgehq/ui/overlay';\nimport { BfToastComponent, type BfToastConfig, type BfToastPosition } from './toast.component';\n\n/**\n * Options of `BfToastService.show()`.\n *\n * @summary Options of a mobile toast: its message, an action, its duration and position\n * @since 0.2.0\n */\nexport interface BfToastOptions {\n /** Text of the pill, truncated to two lines. */\n message: string;\n /** An optional action button. */\n action?: BfToastAction;\n /**\n * Milliseconds before the toast auto-dismisses, not counting time the pointer is over it or\n * focus is inside it; `0` keeps it until dismissed or its action fires (default 3000 without an\n * action, `0` with one).\n * @default 3000\n */\n duration?: number;\n /**\n * Where the pill anchors (default `'bottom'`).\n * @default 'bottom'\n */\n position?: BfToastPosition;\n}\n\n/** Fades `el` in while it slides 12 px in from the edge it anchors to; reduced motion: fade only. */\nfunction toastEnter(position: BfToastPosition) {\n return (el: HTMLElement): Animation => {\n if (prefersReducedMotion(el.ownerDocument.defaultView)) {\n return el.animate([{ opacity: 0 }, { opacity: 1 }], {\n duration: REDUCED_DURATION,\n easing: 'linear',\n fill: 'backwards',\n });\n }\n const offset = position === 'bottom' ? '12px' : '-12px';\n return el.animate(\n [\n { opacity: 0, transform: `translate3d(0,${offset},0)` },\n { opacity: 1, transform: 'translate3d(0,0,0)' },\n ],\n { duration: OVERLAY_DURATION, easing: OVERLAY_EASING, fill: 'backwards' },\n );\n };\n}\n\n/**\n * Queues status pills: `show(options)` renders `bf-toast` on the overlay core (`passive: true` —\n * no backdrop, no focus trap, the page stays interactive, and it never counts towards\n * `BfOverlayStack.count()`) and returns a `BfToastRef`. One toast is visible at a time; later\n * calls queue in order and show once the current one ends. A toast auto-dismisses after\n * `duration` ms (default 3000, resolving `'timeout'`); a toast with an action defaults to\n * `duration: 0`, which keeps it until `dismiss()`, `Escape` inside it (`'dismiss'`) or its action\n * button (`'action'`) ends it; such a persistent toast never blocks the queue — the next `show()`\n * replaces it (`'dismiss'`). The timer pauses while the pointer is over the toast or focus is\n * inside it and resumes with the remaining time. When a toast still holding focus closes, focus\n * returns to the element focused last outside it. Each toast is announced once, politely, through\n * CDK `LiveAnnouncer` as it shows. A navigation (a pushed or popped page, a tab switch) never\n * closes it.\n *\n * @example\n * const ref = inject(BfToastService).show({\n * message: 'Task deleted',\n * action: { label: 'Undo', handler: () => restore(task) },\n * });\n * const reason = await ref.afterDismissed;\n * @since 0.2.0\n * @summary Shows brief toast pills on phones, one at a time, queued in order\n */\n@Injectable({ providedIn: 'root' })\nexport class BfToastService {\n private readonly overlays = inject(BfOverlayService);\n // The shared engine (timing, hold, replace, focus return, announcements, teardown), one at a time.\n private readonly queue = new ɵBfToastQueue<BfToastOptions>(\n { present: (view, options) => this.present(view, options.position ?? 'bottom') },\n {\n maxVisible: 1,\n announcer: inject(LiveAnnouncer),\n document: inject(DOCUMENT),\n destroyRef: inject(DestroyRef),\n },\n );\n\n /**\n * Queues `options` as a toast; shows it now if none is visible, else once the current one ends.\n * A persistent toast (`duration` 0, the default with an action) never blocks the queue: a later\n * `show()` dismisses it (`'dismiss'`), whether it is visible or still waiting.\n * @param options The toast's message, action, duration and position.\n * @returns The toast's ref, to dismiss it or await why it ended.\n */\n show(options: BfToastOptions): BfToastRef {\n return this.queue.show(options);\n }\n\n /** Opens one passive overlay per toast, exactly as before the engine moved to `/overlay`. */\n private present(view: ɵBfToastView, position: BfToastPosition): ɵBfToastHandle {\n const config: BfToastConfig = {\n message: view.message,\n action: view.action,\n position,\n hold: (source, held) => view.hold(source, held),\n escape: () => view.escape(),\n };\n const overlayRef = this.overlays.open<BfToastComponent, BfToastDismissReason>(\n BfToastComponent,\n {\n data: config,\n passive: true,\n // A \"Saved\" toast survives the navigation that follows the save.\n closeOnNavigation: false,\n position,\n // No pane role and no live region on the pill: `LiveAnnouncer` announces the toast once.\n enter: toastEnter(position),\n exit: (el) => fadeOut(el),\n },\n );\n const element: unknown = overlayRef.componentRef.location.nativeElement;\n const host = element instanceof HTMLElement ? element : null;\n return {\n host,\n close: async (reason) => {\n overlayRef.close(reason);\n await overlayRef.afterClosed;\n },\n };\n }\n}\n","import { Component, inject } from '@angular/core';\nimport { BfButtonComponent, BfListComponent, BfListItemComponent } from '@bitforgehq/ui';\nimport { BfIconComponent } from '@bitforgehq/ui/icons';\nimport { type IconNode } from 'lucide';\nimport { BF_OVERLAY_DATA, BfOverlayRef } from '@bitforgehq/ui/overlay';\n\n/**\n * The role of an action sheet row: `destructive` renders it in `text-danger`, `cancel` makes it the\n * separate cancel button.\n *\n * @summary The role of an action sheet row, a destructive action or the cancel button\n * @since 0.3.0\n */\nexport type BfActionSheetActionRole = 'destructive' | 'cancel';\n\n/**\n * One row of an action sheet, or its cancel action when `role: 'cancel'`.\n *\n * @summary One row of an action sheet, or its cancel button, with its answer and role\n * @since 0.2.0\n */\nexport interface BfActionSheetAction {\n /** What `open()` resolves with when the row is tapped (default `label`); keeps the answer\n * stable when the label is translated or two rows share a label. Ignored on the cancel action,\n * which always resolves `undefined`. */\n id?: string;\n /** Row text; also what `open()` resolves with when the row is tapped and has no `id`. */\n label: string;\n /** Icon rendered in the row's leading slot. */\n icon?: IconNode;\n /**\n * `destructive` renders the row's label in `text-danger`; `cancel` renders it as the separate\n * cancel button instead of a row (a plain row when omitted).\n */\n role?: BfActionSheetActionRole;\n}\n\n/** What `BfActionSheetService` hands the `bf-action-sheet` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfActionSheetConfig {\n /** Heading above the rows, or `undefined` for none. */\n title: string | undefined;\n /** The caller's non-cancel actions, in order. */\n actions: BfActionSheetAction[];\n /** Label of the cancel button: the caller's `cancel`-role action, or \"Cancel\". */\n cancelLabel: string;\n}\n\n/**\n * The action sheet panel that `BfActionSheetService.open()` renders on the overlay core\n * (`position: 'bottom'`): an optional title above a `bf-list` of interactive rows for the\n * actions (destructive ones in `text-danger`, an icon in the leading slot) and a separate\n * `tonal` `bf-button` for the cancel action — rendered even when the caller passes none,\n * labelled \"Cancel\", which takes the initial focus. Tapping a row resolves the overlay with its\n * `id ?? label`; the cancel button, like\n * the backdrop, `Escape` and the hardware back button, resolves `undefined`. Not used directly\n * in templates.\n *\n * @example\n * const answer = await inject(BfActionSheetService).open({\n * title: 'Delete list?',\n * actions: [{ id: 'delete', label: 'Delete', role: 'destructive' }],\n * });\n * @summary Bottom panel of labelled actions with a separate cancel button, destructive ones shown in red\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-action-sheet',\n imports: [BfListComponent, BfListItemComponent, BfButtonComponent, BfIconComponent],\n /* px-3 (12) is the panel inset, deliberately tighter than the 16 px gutter: the rows add their own\n 14 px padding, so row text still sits 26 px from the screen edge. */\n host: {\n class:\n 'bg-surface rounded-t-card shadow-floating flex w-screen flex-col px-3 pt-3 pb-[max(var(--spacing-safe-bottom),16px)]',\n },\n template: `\n @if (config.title) {\n <p class=\"text-caption text-text-muted px-3 pb-2\">{{ config.title }}</p>\n }\n <bf-list [inset]=\"false\">\n @for (action of config.actions; track $index) {\n <bf-list-item\n [interactive]=\"true\"\n [title]=\"action.role === 'destructive' ? '' : action.label\"\n (clicked)=\"select(action)\"\n >\n @if (action.icon) {\n <bf-icon bfSlot=\"leading\" [icon]=\"action.icon\" />\n }\n @if (action.role === 'destructive') {\n <span class=\"text-danger text-label truncate font-bold\">{{ action.label }}</span>\n }\n </bf-list-item>\n }\n </bf-list>\n <bf-button variant=\"tonal\" class=\"mt-2\" [fullWidth]=\"true\" autofocus (clicked)=\"cancel()\">\n {{ config.cancelLabel }}\n </bf-button>\n `,\n})\nexport class BfActionSheetComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfActionSheetConfig;\n private readonly ref = inject(BfOverlayRef) as BfOverlayRef<string>;\n\n protected select(action: BfActionSheetAction): void {\n this.ref.close(action.id ?? action.label);\n }\n\n protected cancel(): void {\n this.ref.close();\n }\n}\n","import { Injectable, Injector, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfOverlayService, slideDown, slideUp } from '@bitforgehq/ui/overlay';\nimport {\n BfActionSheetComponent,\n type BfActionSheetAction,\n type BfActionSheetConfig,\n} from './action-sheet.component';\n\n/**\n * Options of `BfActionSheetService.open()`.\n *\n * @summary Options of an action sheet: its title, its rows and when it closes\n * @since 0.2.0\n */\nexport interface BfActionSheetOptions {\n /** Heading above the rows; also the panel's accessible name (default `provideBfLabels()`'s\n * `actions`, \"Actions\"). */\n title?: string;\n /**\n * Rows, in order; a `cancel`-role entry becomes the separate cancel button instead of a row.\n * Only the first `cancel`-role entry is used; any further ones are ignored.\n */\n actions: BfActionSheetAction[];\n /**\n * A navigation to another URL path closes the sheet, resolving `undefined` (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Injector the action sheet resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Opens action sheets: a bottom-anchored list of actions on the overlay core (`position:\n * 'bottom'`, not counted as a modal). Renders `title` above a `bf-list` of the non-cancel\n * `actions` as interactive rows (destructive ones in `text-danger`, an icon in the leading slot\n * from `icon`), and a separate `tonal` `bf-button` for the cancel action — the caller's\n * first `cancel`-role entry if any (further ones are ignored), else a synthesized one labelled `provideBfLabels()`'s `cancel` (\"Cancel\").\n * Focus starts on the cancel button. `open()` resolves with the tapped row's `id ?? label`; the\n * cancel button, a backdrop tap, `Escape`, the hardware back button and a navigation to another\n * URL path all resolve `undefined`.\n *\n * @example\n * const answer = await inject(BfActionSheetService).open({\n * title: 'Delete list?',\n * actions: [\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * { id: 'rename', label: 'Rename' },\n * ],\n * });\n * @since 0.2.0\n * @summary Opens bottom action sheets that resolve with the row the user tapped\n */\n@Injectable({ providedIn: 'root' })\nexport class BfActionSheetService {\n private readonly overlays = inject(BfOverlayService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens the action sheet described by `options`; resolves with the tapped row's `id ?? label`,\n * or `undefined` when cancelled.\n * @param options The title and rows of the sheet.\n */\n open(options: BfActionSheetOptions): Promise<string | undefined> {\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n const cancelAction = options.actions.find((action) => action.role === 'cancel');\n const config: BfActionSheetConfig = {\n title: options.title,\n actions: options.actions.filter((action) => action.role !== 'cancel'),\n cancelLabel: cancelAction?.label ?? labels.cancel,\n };\n const ref = this.overlays.open<BfActionSheetComponent, string>(BfActionSheetComponent, {\n data: config,\n position: 'bottom',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: true,\n dismissOnEscape: true,\n closeOnNavigation: options.closeOnNavigation,\n role: 'dialog',\n ariaLabel: options.title ?? labels.actions,\n injector,\n enter: (el) => slideUp(el),\n exit: (el) => slideDown(el),\n });\n return ref.afterClosed;\n }\n}\n","import { Component, inject } from '@angular/core';\nimport { BfButtonComponent, type BfButtonVariant } from '@bitforgehq/ui';\nimport { BF_OVERLAY_DATA, BfOverlayRef } from '@bitforgehq/ui/overlay';\nimport { type BfAlertDialogButtonRole } from './alert-dialog.service';\n\n/** A button of an alert dialog, with the variant `BfAlertDialogService` resolved for it. */\nexport interface BfAlertDialogButtonConfig {\n /** Button text. */\n label: string;\n /** What `open()` resolves with when it is tapped: the caller's `id ?? label`. */\n answer: string;\n /** `cancel` answers `Escape`/the hardware back button; `destructive` renders `danger`. */\n role?: BfAlertDialogButtonRole;\n variant: BfButtonVariant;\n /** Whether it takes the initial focus: the `cancel`-role button, else the first one. */\n initial: boolean;\n}\n\n/** What `BfAlertDialogService` hands the `bf-alert-dialog` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfAlertDialogConfig {\n title: string;\n message: string | undefined;\n /** Id of the `h2` title, referenced by the panel's `aria-labelledby`. */\n titleId: string;\n /** Id of the message `p`, referenced by the panel's `aria-describedby` when there is a message. */\n messageId: string;\n buttons: BfAlertDialogButtonConfig[];\n}\n\n/**\n * The alert dialog card that `BfAlertDialogService.open()` renders on the overlay core\n * (`position: 'center'`): an `h2.text-heading` title, an optional `text-body text-text-secondary`\n * message, and a right-aligned row of `bf-button`s (`cancel`-role `text`, `destructive`-role\n * `danger`, the first plain button `primary`, further plain ones `tonal`). Focus starts on the\n * `cancel`-role button, else the first one. Tapping a button resolves the overlay with its\n * `id ?? label`. There is no backdrop dismiss; `Escape` and the hardware\n * back button are handled by `BfAlertDialogService` (the cancel button's answer, or claimed and\n * ignored when there is none). Not used directly in templates.\n *\n * @example\n * const answer = await inject(BfAlertDialogService).open({\n * title: 'Delete list?',\n * message: 'This cannot be undone.',\n * buttons: [\n * { id: 'cancel', label: 'Cancel', role: 'cancel' },\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * ],\n * });\n * @summary Centred confirmation dialog with a title, message and a row of role-styled buttons\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-alert-dialog',\n imports: [BfButtonComponent],\n host: {\n // A transparent border is invisible here but forced colours paints it CanvasText, giving the\n // card an edge once its shadow and the scrim behind it both flatten to Canvas.\n class:\n 'w-75 max-w-[calc(100vw-2*var(--spacing-gutter))] bg-surface rounded-card shadow-floating border border-transparent p-6 flex flex-col gap-4',\n },\n template: `\n <h2 [id]=\"config.titleId\" class=\"text-heading\">{{ config.title }}</h2>\n @if (config.message) {\n <p [id]=\"config.messageId\" class=\"text-body text-text-secondary\">{{ config.message }}</p>\n }\n <div class=\"flex justify-end gap-2\">\n @for (button of config.buttons; track $index) {\n <bf-button\n [variant]=\"button.variant\"\n [attr.autofocus]=\"button.initial ? '' : null\"\n (clicked)=\"select(button)\"\n >\n {{ button.label }}\n </bf-button>\n }\n </div>\n `,\n})\nexport class BfAlertDialogComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfAlertDialogConfig;\n private readonly ref = inject(BfOverlayRef) as BfOverlayRef<string>;\n\n protected select(button: BfAlertDialogButtonConfig): void {\n this.ref.close(button.answer);\n }\n}\n","import { Injectable, Injector, inject } from '@angular/core';\nimport { BF_LABELS, type BfButtonVariant } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport { BfOverlayService, BfOverlayStack, fadeOut, scaleIn } from '@bitforgehq/ui/overlay';\nimport {\n BfAlertDialogComponent,\n type BfAlertDialogButtonConfig,\n type BfAlertDialogConfig,\n} from './alert-dialog.component';\n\n/**\n * The role of an alert dialog button: `cancel` answers `Escape` and the hardware back button,\n * `destructive` renders `danger`.\n *\n * @summary The role of an alert dialog button, the cancel answer or a destructive action\n * @since 0.3.0\n */\nexport type BfAlertDialogButtonRole = 'cancel' | 'destructive';\n\n/**\n * A button of `BfAlertDialogService.open()`.\n *\n * @summary One button of an alert dialog: its answer, its label and its role\n * @since 0.2.0\n */\nexport interface BfAlertDialogButton {\n /** What `open()` resolves with when the button is tapped (default `label`); keeps the answer\n * stable when the label is translated or two buttons share a label. */\n id?: string;\n /** Button text; also the value `open()` resolves with when it is tapped and has no `id`. */\n label: string;\n /**\n * `cancel` renders as a `text` button, takes the initial focus and is what `Escape`/the\n * hardware back button answer with; `destructive` renders `danger` (a plain `primary`/`tonal` button when omitted).\n */\n role?: BfAlertDialogButtonRole;\n}\n\n/**\n * Options of `BfAlertDialogService.open()`.\n *\n * @summary Options of an alert dialog: its title, message, buttons and when it closes\n * @since 0.2.0\n */\nexport interface BfAlertDialogOptions {\n /** Dialog heading, rendered as the `h2` the panel's `aria-labelledby` points to. */\n title: string;\n /** Body text under the title. */\n message?: string;\n /** Buttons, left to right (default one button labelled `provideBfLabels()`'s `ok`, \"OK\"). */\n buttons?: BfAlertDialogButton[];\n /**\n * A navigation to another URL path closes the alert, resolving the cancel button's answer, or\n * `undefined` without one (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Injector the dialog resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\nlet nextTitleId = 0;\n\n/**\n * Opens alert dialogs: a centred, 300 px card on the overlay core (`position: 'center'`, no\n * backdrop dismiss). Renders `title` as an `h2`, an optional `message`, and `buttons` (default\n * one `ok` button from the app's `provideBfLabels()`, \"OK\") as a right-aligned row — `cancel`-role `text`, `destructive`-role\n * `danger`, the first plain button `primary`, further plain ones `tonal`. Focus starts on the\n * `cancel`-role button, else the first one. `open()` resolves with the tapped button's\n * `id ?? label`. `Escape` and the hardware back button resolve with the cancel button's answer\n * when `buttons` has one; with none, they are claimed and ignored — an alert\n * with no cancel button can only be answered by tapping a button, or closed by a navigation to\n * another URL path, which resolves the cancel button's answer, else `undefined`.\n *\n * @example\n * const answer = await inject(BfAlertDialogService).open({\n * title: 'Delete list?',\n * message: 'This cannot be undone.',\n * buttons: [\n * { id: 'cancel', label: 'Cancel', role: 'cancel' },\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * ],\n * });\n * @since 0.2.0\n * @summary Opens small centred alerts that resolve with the button the user tapped\n */\n@Injectable({ providedIn: 'root' })\nexport class BfAlertDialogService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens the alert described by `options`; resolves with the tapped button's `id ?? label`, or\n * `undefined` when a navigation closes an alert that has no cancel button.\n * @param options The title, message and buttons of the alert.\n */\n open(options: BfAlertDialogOptions): Promise<string | undefined> {\n const injector = options.injector ?? this.injector;\n const buttons = options.buttons?.length\n ? options.buttons\n : [{ label: injector.get(BF_LABELS).ok }];\n const cancelIndex = buttons.findIndex((button) => button.role === 'cancel');\n const initialIndex = cancelIndex >= 0 ? cancelIndex : 0;\n const id = nextTitleId++;\n const titleId = `bf-alert-dialog-title-${id}`;\n const messageId = `bf-alert-dialog-message-${id}`;\n\n let primaryAssigned = false;\n const buttonConfigs: BfAlertDialogButtonConfig[] = buttons.map((button, index) => {\n let variant: BfButtonVariant;\n if (button.role === 'cancel') {\n variant = 'text';\n } else if (button.role === 'destructive') {\n variant = 'danger';\n } else if (!primaryAssigned) {\n variant = 'primary';\n primaryAssigned = true;\n } else {\n variant = 'tonal';\n }\n return {\n label: button.label,\n answer: button.id ?? button.label,\n role: button.role,\n variant,\n initial: index === initialIndex,\n };\n });\n\n const config: BfAlertDialogConfig = {\n title: options.title,\n message: options.message,\n titleId,\n messageId,\n buttons: buttonConfigs,\n };\n\n const answerCancel = (): void => {\n const cancel = buttonConfigs[cancelIndex];\n if (cancel) {\n ref.close(cancel.answer);\n }\n };\n const ref = this.overlays.open<BfAlertDialogComponent, string>(BfAlertDialogComponent, {\n data: config,\n position: 'center',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: false,\n dismissOnEscape: false,\n // Escape answers like the cancel button, or is claimed and ignored without one.\n onEscape: answerCancel,\n closeOnNavigation: options.closeOnNavigation,\n // A navigation always closes it: with the cancel answer, else with no answer.\n onNavigation: () => ref.close(buttonConfigs[cancelIndex]?.answer),\n role: 'alertdialog',\n injector,\n ariaLabelledBy: titleId,\n ariaDescribedBy: options.message ? messageId : undefined,\n enter: (el) => scaleIn(el),\n exit: (el) => fadeOut(el),\n });\n\n // The stack's handler would close the dialog unconditionally; claim the back button instead\n // and answer with the cancel button's answer when there is one, otherwise ignore it entirely — an\n // alert with no cancel button is only answered by tapping a button.\n this.stack.releaseBack(ref);\n const unregisterBack = this.backButton.register(100, () => {\n answerCancel();\n return true;\n });\n void ref.afterClosed.then(() => unregisterBack());\n\n return ref.afterClosed;\n }\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { type Routes, provideRouter, withComponentInputBinding } from '@angular/router';\nimport { ɵprovideBfDocumentReset } from '@bitforgehq/ui';\nimport { BF_THEME_STORAGE, createMemoryThemeStorage } from '../platform/platform.service';\nimport { provideBfShell, withBfShellRouting } from '../shell/provide-bf-shell';\nimport { type BfShellConfig } from '../shell/shell-config';\n\n/**\n * Options of `provideBfShellTesting()`.\n *\n * @summary Options of the shell test providers: the routes and the shell settings\n * @since 0.3.0\n */\nexport interface BfShellTestingOptions {\n /**\n * The routes of the test router.\n * @default []\n */\n routes?: Routes;\n /** Shell settings over the test defaults (`swipeBack: false`, `hapticsOnTabChange: false`). */\n config?: Partial<BfShellConfig>;\n}\n\n/**\n * Test providers for an app on the mobile shell: `provideRouter(routes ?? [],\n * withComponentInputBinding(), withBfShellRouting())` plus `provideBfShell({ swipeBack: false,\n * hapticsOnTabChange: false, ...config })`, an in-memory `BF_THEME_STORAGE` (so a theme change\n * never reaches `localStorage`), and the same document cleanup as `provideBfTesting()` from\n * `@bitforgehq/ui/testing` on teardown (`dark` and `cdk-global-scrollblock` on `<html>`, `inert` on\n * body children, stray overlay panes). Like that cleanup, it needs TestBed's default\n * `teardown: { destroyAfterEach: true }`. Use it instead of the app's own shell providers in\n * `TestBed.configureTestingModule()`.\n *\n * @example\n * TestBed.configureTestingModule({\n * providers: [provideBfShellTesting({ routes: [{ path: 'inbox', component: InboxPage }] })],\n * });\n * @param options The routes and the shell settings.\n * @returns The environment providers for `TestBed.configureTestingModule()`.\n * @summary Test providers for the mobile shell: router, shell, in-memory theme and cleanup\n * @since 0.3.0\n */\nexport function provideBfShellTesting(options: BfShellTestingOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideRouter(options.routes ?? [], withComponentInputBinding(), withBfShellRouting()),\n provideBfShell({ swipeBack: false, hapticsOnTabChange: false, ...options.config }),\n { provide: BF_THEME_STORAGE, useFactory: createMemoryThemeStorage },\n ɵprovideBfDocumentReset(),\n ]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bitforgehq-ui-mobile.mjs","names":[],"sources":["../../../projects/ui/mobile/shell/shell-config.ts","../../../projects/ui/mobile/platform/platform.service.ts","../../../projects/ui/mobile/platform/back-button.service.ts","../../../projects/ui/mobile/platform/haptics.service.ts","../../../projects/ui/mobile/platform/text-scale.service.ts","../../../projects/ui/mobile/platform/keyboard.service.ts","../../../projects/ui/mobile/navigation/stack-reuse-strategy.ts","../../../projects/ui/mobile/shell/provide-bf-shell.ts","../../../projects/ui/mobile/navigation/view-lifecycle.ts","../../../projects/ui/mobile/navigation/nav-controller.service.ts","../../../projects/ui/mobile/motion.ts","../../../projects/ui/mobile/navigation/transitions.ts","../../../projects/ui/mobile/navigation/page/page-context.ts","../../../projects/ui/mobile/navigation/tabs/tab-context.ts","../../../projects/ui/mobile/navigation/stack-outlet/stack-outlet.component.ts","../../../projects/ui/mobile/navigation/page/page.component.ts","../../../projects/ui/mobile/navigation/back-button/back-button.component.ts","../../../projects/ui/mobile/navigation/horizontal-scroll/horizontal-scroll.directive.ts","../../../projects/ui/mobile/content/keyboard-assist/keyboard-assist.directive.ts","../../../projects/ui/mobile/content/overscroll-stretch/overscroll-stretch.directive.ts","../../../projects/ui/mobile/content/content/content.component.ts","../../../projects/ui/mobile/content/header/header.component.ts","../../../projects/ui/mobile/content/refresher/refresher.component.ts","../../../projects/ui/mobile/navigation/tabs/tabs.component.ts","../../../projects/ui/mobile/overlays/deprecated-core.ts","../../../projects/ui/mobile/overlays/sheet/sheet-ref.ts","../../../projects/ui/mobile/overlays/keyboard-cover.ts","../../../projects/ui/mobile/overlays/sheet/sheet.component.ts","../../../projects/ui/mobile/overlays/sheet/sheet.service.ts","../../../projects/ui/mobile/overlays/modal/modal.component.ts","../../../projects/ui/mobile/overlays/modal/modal.service.ts","../../../projects/ui/mobile/overlays/toast/toast.component.ts","../../../projects/ui/mobile/overlays/toast/toast.service.ts","../../../projects/ui/mobile/overlays/action-sheet/action-sheet.component.ts","../../../projects/ui/mobile/overlays/action-sheet/action-sheet.service.ts","../../../projects/ui/mobile/overlays/alert-dialog/alert-dialog.component.ts","../../../projects/ui/mobile/overlays/alert-dialog/alert-dialog.service.ts","../../../projects/ui/mobile/testing/provide-bf-shell-testing.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\n/**\n * The shell's transition and gesture style: `ios`, `md` (Material), or `auto` (iOS on iOS, else\n * `md`).\n *\n * @summary The shell style setting, iOS, Material or automatic from the platform\n * @since 0.3.0\n */\nexport type BfShellConfigMode = 'ios' | 'md' | 'auto';\n\n/**\n * The cap of iOS Dynamic Type: the largest root font scale the shell applies.\n *\n * @summary The largest root font scale iOS Dynamic Type may apply in the shell\n * @since 0.3.0\n */\nexport interface BfShellDynamicTypeLimit {\n /** The largest root font scale (2.35, the AX3 size, by default); `Infinity` removes the cap. */\n maxScale: number;\n}\n\n/**\n * The shell's iOS Dynamic Type setting: `false` opts out, a `BfShellDynamicTypeLimit` caps it.\n *\n * @summary Whether the shell follows iOS Dynamic Type, and up to which scale\n * @since 0.3.0\n */\nexport type BfShellDynamicType = false | BfShellDynamicTypeLimit;\n\n/**\n * What the Android back button does at the root of every stack, when no overlay or page claims\n * it: `minimize` moves the app to the background (`App.minimizeApp()`), so a relaunch resumes it\n * warm; `exit` finishes the activity (`App.exitApp()`), so the next launch starts cold.\n *\n * @summary What Android back does at a stack root, background the app or exit it\n * @since 0.3.0\n */\nexport type BfBackAtRoot = 'minimize' | 'exit';\n\n/**\n * Configuration of the mobile shell, set once through `provideBfShell()`.\n *\n * @summary The mobile shell settings: mode, swipe-back, haptics, Dynamic Type and system bars\n * @since 0.2.0\n */\nexport interface BfShellConfig {\n /**\n * Transition and gesture style: `ios`, `md` (Material), or `auto` (iOS on iOS, else `md`).\n * Overscroll is not part of it: `bf-content`'s iOS bounce and Android stretch and\n * `bf-refresher`'s iOS bounce mode follow the engine (`BfPlatformService.mode()`), because the\n * bounce is WebKit's own and Chrome never reports a negative `scrollTop`.\n * @default 'auto'\n */\n mode: BfShellConfigMode;\n /**\n * Enables the iOS edge swipe-back in every stack outlet (iOS mode only).\n * @default true\n */\n swipeBack: boolean;\n /**\n * Holds a transition at frame 0 for two animation frames while the entering page renders.\n * @default true\n */\n deferTransitionStart: boolean;\n /**\n * Plays a light haptic tick on tab changes (native only).\n * @default true\n */\n hapticsOnTabChange: boolean;\n /**\n * iOS Dynamic Type (native iOS only; see `BfTextScaleService`): the root font size follows the\n * user's text size. `false` opts out; `maxScale` caps the root scale (default 2.35, the AX3\n * size, at least 200 %). `Infinity` removes the cap entirely; a non-finite value other than\n * `Infinity` (e.g. `NaN`) falls back to the default cap. Left out, the default applies. Android\n * needs no setting: its WebView applies the system font scale.\n * @default { maxScale: 2.35 }\n */\n dynamicType?: BfShellDynamicType;\n /**\n * Native iOS and Android only: the status-bar and navigation-bar icons follow the in-app theme\n * (`BfPlatformService.dark()`) through Capacitor's `SystemBars.setStyle()`, on start and on every\n * theme change (see `bfSystemBarsStyle()`). `false` leaves the system bars to the app.\n * @default true\n */\n syncSystemBars?: boolean;\n /**\n * Native Android only: what the back button does when no overlay and no stack can go back\n * (every stack is at its first page). `minimize` (default) moves the app to the background, as\n * Android does for a root activity, so a relaunch resumes it warm on the same tab; `exit`\n * finishes the activity, as 0.2 did, so the next launch starts cold.\n * @default 'minimize'\n */\n backAtRoot?: BfBackAtRoot;\n /**\n * How many pages of one stack stay alive in each `bf-stack-outlet` (default `Infinity`: every\n * page). Above it, the deepest pages are destroyed and kept as a record (route, URL, history\n * position and the content's scroll position); a pop back to one creates the page again, fires\n * its lifecycle and restores its scroll. Deep pages come back fresh: their component state and\n * the stacks of outlets nested in them are gone. Values below 1 count as 1.\n * @default Infinity\n */\n maxLivePages?: number;\n}\n\n/** The default Dynamic Type cap: the AX3 size (40 / 17 px), at least 200 %. Internal. */\nexport const DEFAULT_MAX_TEXT_SCALE = 2.35;\n\n/**\n * The shell defaults, merged under the overrides given to `provideBfShell()`.\n *\n * @summary The mobile shell defaults that provideBfShell merges the app settings over\n * @since 0.2.0\n */\nexport const BF_SHELL_DEFAULTS: Readonly<BfShellConfig> = {\n mode: 'auto',\n swipeBack: true,\n deferTransitionStart: true,\n hapticsOnTabChange: true,\n dynamicType: { maxScale: DEFAULT_MAX_TEXT_SCALE },\n syncSystemBars: true,\n backAtRoot: 'minimize',\n maxLivePages: Infinity,\n};\n\n/**\n * The resolved shell configuration.\n *\n * @summary Injection token holding the resolved mobile shell configuration of the app\n * @since 0.2.0\n */\nexport const BF_SHELL_CONFIG = new InjectionToken<BfShellConfig>('BF_SHELL_CONFIG');\n","import { DOCUMENT, Injectable, computed, effect, inject } from '@angular/core';\nimport { Capacitor, SystemBars, SystemBarsStyle } from '@capacitor/core';\nimport {\n BF_COLOR_SCHEME_STORAGE,\n BfColorSchemeService,\n type BfColorScheme,\n ɵcreateMemoryColorSchemeStorage,\n} from '@bitforgehq/ui';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, type BfShellConfig } from '../shell/shell-config';\n\n/**\n * The platform the app runs on: native iOS, native Android, or a browser.\n *\n * @summary The platform the app runs on, native iOS, native Android or the web\n * @since 0.2.0\n */\nexport type BfPlatform = 'ios' | 'android' | 'web';\n/**\n * The shell's look and motion: `ios`, or `md` (Material) everywhere else.\n *\n * @summary The shell's look and motion conventions, iOS or Material Design\n * @since 0.2.0\n */\nexport type BfShellMode = 'ios' | 'md';\n/**\n * The app theme choice: follow the system, or force light or dark. The same type as\n * `BfColorScheme` from `@bitforgehq/ui`.\n *\n * @summary The app theme choice, following the system or forcing light or dark\n * @since 0.2.0\n */\nexport type BfTheme = BfColorScheme;\n/** The shell's viewport: edge to edge, and no zoom. */\nconst VIEWPORT_CONTENT =\n 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover';\n/** Viewport directives that forbid zoom, with the value each must have. */\nconst NO_ZOOM_DIRECTIVES: readonly (readonly [string, string])[] = [\n ['maximum-scale', '1'],\n ['user-scalable', 'no'],\n];\n\nfunction toPlatform(platform: string): BfPlatform {\n return platform === 'ios' || platform === 'android' ? platform : 'web';\n}\n\n/** A theme store in memory, gone with the app: where `localStorage` is unavailable, and in tests. Internal. */\nexport const createMemoryThemeStorage = ɵcreateMemoryColorSchemeStorage;\n\n/**\n * Where `BfPlatformService` keeps the theme choice (the key `bf-theme`): `localStorage` when it is\n * available, otherwise an in-memory map. Provide another store (a native preferences plugin, or an\n * in-memory one in tests, as `provideBfShellTesting()` does) to keep the choice elsewhere. It is\n * the same token as `BF_COLOR_SCHEME_STORAGE` from `@bitforgehq/ui`, which `BfColorSchemeService`\n * reads, so providing either one moves the choice for both.\n *\n * @example\n * providers: [{ provide: BF_THEME_STORAGE, useValue: sessionStorage }]\n * @summary Injection token for the store that keeps the app theme choice between visits\n * @since 0.3.0\n */\nexport const BF_THEME_STORAGE = BF_COLOR_SCHEME_STORAGE;\n\n/**\n * Makes the viewport meta forbid zoom: sets `maximum-scale=1` and `user-scalable=no`, keeping the\n * other directives, or creates the meta with the full shell viewport when there is none. A meta\n * that already forbids zoom is left untouched.\n */\nfunction forbidViewportZoom(document: Document): void {\n const meta = document.head.querySelector<HTMLMetaElement>('meta[name=\"viewport\"]');\n if (!meta) {\n const created = document.createElement('meta');\n created.name = 'viewport';\n created.content = VIEWPORT_CONTENT;\n document.head.appendChild(created);\n return;\n }\n const directives = meta.content\n .split(',')\n .map((directive) => directive.trim())\n .filter((directive) => directive.length > 0);\n let changed = false;\n for (const [key, value] of NO_ZOOM_DIRECTIVES) {\n const wanted = `${key}=${value}`;\n const index = directives.findIndex(\n (directive) => directive.split('=')[0].trim().toLowerCase() === key,\n );\n if (index === -1) {\n directives.push(wanted);\n changed = true;\n } else if (directives[index].replace(/\\s/g, '').toLowerCase() !== wanted) {\n directives[index] = wanted;\n changed = true;\n }\n }\n if (changed) {\n meta.content = directives.join(', ');\n }\n}\n\n/** The first Android WebView whose system bars draw over the page (edge to edge). */\nconst EDGE_TO_EDGE_WEBVIEW = 140;\n\n/**\n * The SystemBars style for a theme: Dark = light icons. Android WebView < 140 pads the WebView, so\n * the bar areas show the activity's window background (which follows the system theme): use\n * Default there.\n *\n * The WebView ≥ 140 case assumes the shell's setup: `viewport-fit=cover` and Capacitor's default\n * `SystemBars.insetsHandling: 'css'`, so the page draws under the bars. With `insetsHandling`\n * `'disable'` or `'native'` the page never reaches under them and the bars show the window\n * background as on older WebViews; pass `syncSystemBars: false` and style them yourself there.\n * `webViewMajor` is read from `Chrome/<n>` in the user agent (Capacitor reads the WebView\n * package, which normally agrees).\n *\n * @example\n * await SystemBars.setStyle({ style: bfSystemBarsStyle(true, 'ios', null) });\n * @param dark Whether the app is dark now (`BfPlatformService.dark()`).\n * @param platform The platform the app runs on.\n * @param webViewMajor The Android WebView's major version (`Chrome/<n>` in the user agent), or\n * `null` when unknown.\n * @returns The `SystemBarsStyle` to pass to `SystemBars.setStyle()`.\n * @summary The system-bar icon style that keeps the bars readable in the in-app theme\n * @since 0.3.0\n */\nexport function bfSystemBarsStyle(\n dark: boolean,\n platform: BfPlatform,\n webViewMajor: number | null,\n): SystemBarsStyle {\n if (platform === 'android' && (webViewMajor ?? 0) < EDGE_TO_EDGE_WEBVIEW) {\n return SystemBarsStyle.Default;\n }\n return dark ? SystemBarsStyle.Dark : SystemBarsStyle.Light;\n}\n\n/** The WebView's Chromium major version from its user agent (`Chrome/134.…`), or `null`. */\nfunction webViewMajor(navigator: Navigator | undefined): number | null {\n const match = /Chrome\\/(\\d+)/.exec(navigator?.userAgent ?? '');\n return match ? Number(match[1]) : null;\n}\n\n/**\n * Platform facts and the app theme: native or web, iOS or Android, the shell mode that follows\n * from it, and the user's theme choice (system, light, dark) reflected as the `dark` class on\n * `<html>`. The theme is `BfColorSchemeService` from `@bitforgehq/ui` (the Capacitor-free colour\n * scheme a desktop app uses through `provideBfColorScheme()`): `theme` and `dark` are its\n * `scheme` and `dark` signals, and `matchMedia('(prefers-color-scheme: dark)')` is the single\n * source for the system theme on every platform.\n *\n * The `theme`/`dark` signals always compute, but the `<html>` class and storage writes\n * only run when `provideBfShell()` provided `BF_SHELL_CONFIG` (or the app provided\n * `provideBfColorScheme()`) — i.e. this service is the app's own shell, not merely injected by\n * something that wants `platform`/`mode`. Without it (e.g. a docs demo that instantiates\n * `BfPlatformService` directly to preview `bf-tabs`), this service would otherwise fight the host\n * page's own theme, overwriting `<html class=\"dark\">` with whatever `prefers-color-scheme` says.\n *\n * With `provideBfShell()` on a native platform it also makes the viewport meta forbid zoom\n * (`maximum-scale=1, user-scalable=no`, other directives kept; the meta is created if missing):\n * WKWebView zooms a focused control whose font is under 16 px and never zooms back, so the native\n * shell forbids zoom — pinch, double tap and focus alike — and honours the system text size\n * instead (iOS Dynamic Type through `BfTextScaleService`, Android's font scale in the WebView).\n * On the web the meta is never touched, so a web build keeps pinch zoom (the owner's ruling,\n * audit §12): ship `index.html` without `maximum-scale`/`user-scalable`.\n *\n * With `provideBfShell()` on native iOS and Android (unless `syncSystemBars: false`) the status-bar\n * and navigation-bar icons follow `dark()`: `SystemBars.setStyle()` with `bfSystemBarsStyle()`,\n * on start and on every theme change (Android WebView < 140 keeps `Default`, see there).\n * @summary Platform facts and the app theme: native or web, iOS or Android, light or dark\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfPlatformService {\n private readonly document = inject(DOCUMENT);\n private readonly shellConfig = inject(BF_SHELL_CONFIG, { optional: true });\n private readonly colorScheme = inject(BfColorSchemeService);\n /** True inside the native iOS or Android app, false in a browser. */\n readonly isNative = Capacitor.isNativePlatform();\n /** The platform the app runs on. */\n readonly platform: BfPlatform = toPlatform(Capacitor.getPlatform());\n /** The platform's shell mode: `ios` on iOS, else `md`; `resolveShellMode()` applies the config. */\n readonly mode = computed<BfShellMode>(() => (this.platform === 'ios' ? 'ios' : 'md'));\n // `model()` is compiler-restricted to @Component/@Directive classes (NG8110); a plain writable\n // `signal()` gives callers the same `theme()` / `theme.set(...)` surface in a service.\n /** The user's theme choice, stored in `BF_THEME_STORAGE` (`localStorage`); set it with\n * `theme.set(…)`. The same signal as `BfColorSchemeService.scheme`. */\n readonly theme = this.colorScheme.scheme;\n private readonly systemDark = this.colorScheme.systemDark;\n /** Whether the app is dark now: the theme choice, or the system's under `system`. The same\n * signal as `BfColorSchemeService.dark`. */\n readonly dark = this.colorScheme.dark;\n\n constructor() {\n if (this.shellConfig === null) {\n return;\n }\n if (this.isNative) {\n forbidViewportZoom(this.document);\n }\n // The shell owns <html>: the colour scheme applies the class (and the stored theme)\n // synchronously, before any change detection runs, and keeps later changes in sync.\n this.colorScheme.claimDocument();\n if (this.isNative && this.shellConfig.syncSystemBars !== false) {\n const major = webViewMajor(this.document.defaultView?.navigator);\n effect(() => {\n const style = bfSystemBarsStyle(this.dark(), this.platform, major);\n // `Default` is resolved natively when it is set (Capacitor keeps the resolved style across\n // a configuration change), so it is sent again whenever the system theme changes.\n this.systemDark();\n void this.applySystemBars(style);\n });\n }\n }\n\n /** The native system bars are outside the page; a failed call leaves them as they were. */\n private async applySystemBars(style: SystemBarsStyle): Promise<void> {\n try {\n // effect-justified: the native system bars are external to any view\n await SystemBars.setStyle({ style });\n } catch (error) {\n console.error(error);\n }\n }\n}\n\n/**\n * The shell mode in effect: `config.mode`, or the platform's (`BfPlatformService.mode()`: iOS on\n * iOS, else Material) when it is `'auto'`. The one resolution every mode-dependent part of the\n * shell (stack outlet, modal, header, back button) shares; apps use it (or `injectShellMode()`)\n * to follow the same per-mode conventions, e.g. text vs icon header actions.\n *\n * @example\n * const mode = resolveShellMode(inject(BF_SHELL_CONFIG), inject(BfPlatformService));\n * @param config The shell configuration, usually `inject(BF_SHELL_CONFIG)`.\n * @param platform The platform service.\n * @returns The mode in effect.\n * @summary Resolves the shell mode in effect from the configuration and the platform\n * @since 0.2.0\n */\nexport function resolveShellMode(\n config: Readonly<BfShellConfig>,\n platform: BfPlatformService,\n): BfShellMode {\n return config.mode === 'auto' ? platform.mode() : config.mode;\n}\n\n/**\n * `resolveShellMode()` from the injector: `BF_SHELL_CONFIG` (the defaults, i.e. `'auto'`, without\n * `provideBfShell()`) and `BfPlatformService`. Call it in an injection context (a field\n * initializer or constructor); the mode is fixed for the app's lifetime.\n *\n * @example\n * protected readonly ios = injectShellMode() === 'ios';\n * @returns The mode in effect.\n * @summary Reads the shell mode in effect from the injector, iOS or Material\n * @since 0.2.0\n */\nexport function injectShellMode(): BfShellMode {\n const config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n return resolveShellMode(config, inject(BfPlatformService));\n}\n","import { DestroyRef, Injectable, inject } from '@angular/core';\nimport { BF_SHELL_CONFIG } from '../shell/shell-config';\nimport { BfPlatformService } from './platform.service';\n\ninterface BackButtonHandler {\n priority: number;\n /** Registration order; on equal priority the newest registration runs first. */\n sequence: number;\n run: () => boolean | Promise<boolean>;\n}\n\n/**\n * The hardware back button queue: on native Android it listens to `@capacitor/app`'s\n * `backButton` event; on iOS and web nothing is registered (there is no hardware button to\n * listen to), but `register` and `dispatch` still work so overlays and tests can drive the same\n * queue. `register` adds a handler at a priority (an open overlay registers at 100,\n * `BfNavController` at 0 — it pops the deepest active `bf-stack-outlet`); `dispatch` runs the\n * queue from the highest priority down until one handler returns true; on equal priority the\n * newest registration runs first, so stacked overlays close top-down. When none does (every\n * stack is at its first page), the app moves to the background (`App.minimizeApp()`), so a\n * relaunch resumes it warm; `provideBfShell({ backAtRoot: 'exit' })` finishes it instead\n * (`App.exitApp()`).\n *\n * @example\n * const unregister = backButton.register(100, () => {\n * closeTopOverlay();\n * return true;\n * });\n * @summary The Android hardware back button queue that overlays and navigation stacks share\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfBackButtonService {\n private readonly platform = inject(BfPlatformService);\n private readonly destroyRef = inject(DestroyRef);\n private readonly backAtRoot =\n inject(BF_SHELL_CONFIG, { optional: true })?.backAtRoot ?? 'minimize';\n private readonly handlers: BackButtonHandler[] = [];\n private sequence = 0;\n\n constructor() {\n if (this.platform.isNative && this.platform.platform === 'android') {\n void this.attachNativeListener();\n }\n }\n\n /**\n * Adds a handler at `priority` (higher priority runs first; on a tie, the handler registered\n * last runs first). Returns a function that removes\n * it again; call it when the registrant (an overlay, an outlet) is torn down.\n * @param priority Higher runs first: an open overlay uses 100, `BfNavController` 0.\n * @param handler Returns (or resolves) true when it handled the press.\n */\n register(priority: number, handler: () => boolean | Promise<boolean>): () => void {\n const entry: BackButtonHandler = { priority, sequence: this.sequence++, run: handler };\n this.handlers.push(entry);\n return () => {\n const index = this.handlers.indexOf(entry);\n if (index >= 0) {\n this.handlers.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs the registered handlers from highest to lowest priority until one claims the event\n * (returns true); when none does, minimizes the app (or exits it with `backAtRoot: 'exit'`).\n * Public so tests and the native listener share one code path, and so overlays outside the\n * native listener's reach (phase 2c) can still be driven by it, e.g. `Escape`.\n */\n async dispatch(): Promise<void> {\n const ordered = [...this.handlers].sort(\n (a, b) => b.priority - a.priority || b.sequence - a.sequence,\n );\n for (const handler of ordered) {\n if (await handler.run()) {\n return;\n }\n }\n if (this.backAtRoot === 'exit') {\n await this.exitApp();\n } else {\n await this.minimizeApp();\n }\n }\n\n private async attachNativeListener(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n const listener = await App.addListener('backButton', () => {\n void this.dispatch();\n });\n this.destroyRef.onDestroy(() => void listener.remove());\n } catch (error) {\n console.error('BfBackButtonService: failed to attach the native back button listener', error);\n }\n }\n\n private async minimizeApp(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n await App.minimizeApp();\n } catch (error) {\n console.error('BfBackButtonService: failed to minimize the app', error);\n }\n }\n\n private async exitApp(): Promise<void> {\n try {\n const { App } = await import('@capacitor/app');\n await App.exitApp();\n } catch (error) {\n console.error('BfBackButtonService: failed to exit the app', error);\n }\n }\n}\n","import { Injectable, inject } from '@angular/core';\nimport type { HapticsPlugin, ImpactStyle, NotificationType } from '@capacitor/haptics';\nimport { BfPlatformService } from './platform.service';\n\n/**\n * Impact weight, mapped to `ImpactStyle.Light` / `Medium` / `Heavy` on native.\n *\n * @summary The weight of an impact haptic, light, medium or heavy\n * @since 0.2.0\n */\nexport type BfHapticsImpactStyle = 'light' | 'medium' | 'heavy';\n/**\n * Outcome of a task, mapped to `NotificationType.Success` / `Warning` / `Error` on native.\n *\n * @summary The outcome a notification haptic reports: success, warning or error\n * @since 0.2.0\n */\nexport type BfHapticsNotificationType = 'success' | 'warning' | 'error';\n\n/** The shape of `@capacitor/haptics`'s module namespace, cached after the first lazy import. */\ninterface HapticsModule {\n Haptics: HapticsPlugin;\n ImpactStyle: typeof ImpactStyle;\n NotificationType: typeof NotificationType;\n}\n\n/**\n * Haptic feedback: `impact`, `selection` and `notification` forward to `@capacitor/haptics` on\n * native (loaded lazily on first use, the import cached like `BfKeyboardService`'s plugin); on\n * the web, and when the plugin fails to load (an app that didn't add the optional dependency),\n * every method resolves as a silent no-op.\n *\n * @example\n * const haptics = inject(BfHapticsService);\n * await haptics.impact('light');\n * @since 0.2.0\n * @summary Haptic feedback on native devices that silently does nothing on the web\n */\n@Injectable({ providedIn: 'root' })\nexport class BfHapticsService {\n private readonly platformService = inject(BfPlatformService);\n private modulePromise: Promise<HapticsModule | null> | null = null;\n\n /**\n * Impact feedback: a collision between UI elements of the given weight.\n * @param style The weight of the impact.\n */\n async impact(style: BfHapticsImpactStyle): Promise<void> {\n await this.run(async ({ Haptics, ImpactStyle: Style }) => {\n const map: Record<BfHapticsImpactStyle, ImpactStyle> = {\n light: Style.Light,\n medium: Style.Medium,\n heavy: Style.Heavy,\n };\n await Haptics.impact({ style: map[style] });\n });\n }\n\n /** A light tick for a discrete selection change (scrubbing a picker, crossing a threshold). */\n async selection(): Promise<void> {\n await this.run(({ Haptics }) => Haptics.selectionChanged());\n }\n\n /**\n * Feedback for the outcome of a task.\n * @param type The outcome of the task.\n */\n async notification(type: BfHapticsNotificationType): Promise<void> {\n await this.run(async ({ Haptics, NotificationType: Type }) => {\n const map: Record<BfHapticsNotificationType, NotificationType> = {\n success: Type.Success,\n warning: Type.Warning,\n error: Type.Error,\n };\n await Haptics.notification({ type: map[type] });\n });\n }\n\n private async run(fn: (module: HapticsModule) => Promise<void>): Promise<void> {\n if (!this.platformService.isNative) {\n return;\n }\n const plugin = await this.loadPlugin();\n if (!plugin) {\n return;\n }\n try {\n await fn(plugin);\n } catch {\n // The native call failed (no native runtime behind the plugin); haptics are best-effort.\n }\n }\n\n /**\n * Resolves the plugin, or `null` when it is not installed. The `import()` stays lexically inside\n * the `try` so esbuild tolerates an unresolved optional peer at build time (as in\n * `BfBackButtonService` and `BfKeyboardService`); a bare `import()` fails the consumer's build.\n */\n private loadPlugin(): Promise<HapticsModule | null> {\n if (!this.modulePromise) {\n try {\n this.modulePromise = import('@capacitor/haptics').catch(() => null);\n } catch {\n this.modulePromise = Promise.resolve(null);\n }\n }\n return this.modulePromise;\n }\n}\n","import { DOCUMENT, DestroyRef, Injectable, inject, signal } from '@angular/core';\nimport { BF_SHELL_CONFIG, DEFAULT_MAX_TEXT_SCALE } from '../shell/shell-config';\nimport { BfPlatformService } from './platform.service';\n\n/** The body text size of iOS's default Dynamic Type setting (\"Large\"), in px. */\nconst DEFAULT_BODY_PX = 17;\nconst PROBE_ATTRIBUTE = 'data-bf-text-scale-probe';\n\n/**\n * Scales the root font size with iOS Dynamic Type inside the native shell. WKWebView resolves the\n * system text style `font: -apple-system-body` to the user's text size (17 px at the default\n * \"Large\", 21 px at xxL, 40 px at AX3, 53 px at AX5); a hidden, persistent probe element with that\n * font gives the size, and `<html>` gets `font-size: probe / 17 × 100 %` (rounded to 0.1 %). Every\n * type token and spacing step of the library is in `rem`, so text and rem-based layout grow\n * together, as UIKit's Dynamic Type does.\n *\n * A `ResizeObserver` on the probe re-applies the scale when the setting changes while the app\n * runs (WebKit re-resolves system fonts then); `visibilitychange` re-probes as the fallback. The\n * scale never goes below 1 (the smaller iOS sizes, and a Mac running the iOS app, keep the design\n * size) and is capped at `BfShellConfig.dynamicType.maxScale` (default 2.35; `Infinity` means no\n * cap; `NaN` falls back to the default). A missing or non-finite probe value gives 1; at 1 the\n * root font size is left unset.\n *\n * Acts only with `provideBfShell()` (which starts it at bootstrap), on native iOS, and while\n * `dynamicType` is not `false`. Android needs nothing: its WebView applies the system font scale\n * (`textZoom`) itself, so probing there would scale twice. On the web the browser's own text\n * size and zoom apply. Destroying the service removes the probe, its listeners and the root font\n * size.\n *\n * @example\n * const textScale = inject(BfTextScaleService);\n * const compact = computed(() => textScale.scale() > 1.5);\n * @since 0.2.0\n * @summary Follows iOS Dynamic Type by scaling the root font size of the app\n */\n@Injectable({ providedIn: 'root' })\nexport class BfTextScaleService {\n private readonly document = inject(DOCUMENT);\n private readonly platformService = inject(BfPlatformService);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n private readonly scaleState = signal(1);\n\n /** Current root scale (1 = the system default \"Large\"); always 1 on Android and the web. */\n readonly scale = this.scaleState.asReadonly();\n\n constructor() {\n const view = this.document.defaultView;\n const dynamicType = this.config\n ? (this.config.dynamicType ?? { maxScale: DEFAULT_MAX_TEXT_SCALE })\n : false;\n if (\n !view ||\n !this.document.body ||\n dynamicType === false ||\n !this.platformService.isNative ||\n this.platformService.platform !== 'ios'\n ) {\n return;\n }\n // `Infinity` is a deliberate, finite-arithmetic way to ask for no cap at all; only `NaN` (not\n // a number the configured value could ever legitimately be) falls back to the default cap.\n const maxScale = Number.isNaN(dynamicType.maxScale)\n ? DEFAULT_MAX_TEXT_SCALE\n : Math.max(1, dynamicType.maxScale);\n const probe = this.createProbe();\n const apply = (): void => {\n const size = parseFloat(view.getComputedStyle(probe).fontSize);\n const ratio = Number.isFinite(size) ? size / DEFAULT_BODY_PX : 1;\n const percent = Math.round(Math.min(Math.max(ratio, 1), maxScale) * 1000) / 10;\n this.scaleState.set(percent / 100);\n const root = this.document.documentElement.style;\n if (percent === 100) {\n root.removeProperty('font-size');\n } else {\n root.fontSize = `${percent}%`;\n }\n };\n const onVisibilityChange = (): void => {\n if (this.document.visibilityState !== 'hidden') {\n apply();\n }\n };\n const observer =\n typeof view.ResizeObserver === 'function' ? new view.ResizeObserver(apply) : null;\n observer?.observe(probe);\n this.document.addEventListener('visibilitychange', onVisibilityChange);\n apply();\n this.destroyRef.onDestroy(() => {\n observer?.disconnect();\n this.document.removeEventListener('visibilitychange', onVisibilityChange);\n probe.remove();\n this.document.documentElement.style.removeProperty('font-size');\n });\n }\n\n private createProbe(): HTMLElement {\n const probe = this.document.createElement('span');\n probe.setAttribute(PROBE_ATTRIBUTE, '');\n probe.setAttribute('aria-hidden', 'true');\n probe.setAttribute(\n 'style',\n 'position: fixed; top: 0; left: -200px; font: -apple-system-body; white-space: nowrap; visibility: hidden; pointer-events: none',\n );\n probe.textContent = 'A';\n this.document.body.appendChild(probe);\n return probe;\n }\n}\n","import { DOCUMENT, DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { KeyboardPlugin } from '@capacitor/keyboard';\nimport { BfPlatformService } from './platform.service';\n\n/**\n * Keyboard lifecycle phase, for animations only; never gate logic on the order of the phases.\n * `showing` starts at `keyboardWillShow`, `shown` at `keyboardDidShow`. Either hide event\n * (`keyboardWillHide` or `keyboardDidHide`, whichever arrives first — iOS does not guarantee\n * the order) moves straight to `hidden`, so there is no `hiding` phase.\n *\n * @summary The soft keyboard phase, hidden, showing or shown, for animations only\n * @since 0.2.0\n */\nexport type BfKeyboardPhase = 'hidden' | 'showing' | 'shown';\n\n/** A covered height below this is browser chrome (URL bar, toolbar), not a keyboard. */\nconst WEB_KEYBOARD_THRESHOLD = 80;\n/** A `visualViewport.scale` further than this from 1 is pinch zoom: no keyboard is read from it. */\nconst ZOOM_TOLERANCE = 0.01;\nconst PROBE_ATTRIBUTE = 'data-bf-keyboard-probe';\n/** CSS variable on `<html>` carrying the keyboard height (`0px` while hidden). */\nconst HEIGHT_VARIABLE = '--bf-keyboard-height';\n/**\n * How long `imminent` waits for the keyboard after `focusin` before it gives up and clears: a\n * hardware keyboard (iPad Magic Keyboard, a simulator with the host keyboard) opens no soft\n * keyboard, so no keyboard event would ever clear it. A cold Android emulator has been seen to\n * send will-show about 1.1 s after focus.\n */\nconst IMMINENT_TIMEOUT_MS = 2500;\n\n/** `input` types that open no text keyboard. */\nconst NON_TEXT_INPUT_TYPES = new Set([\n 'button',\n 'checkbox',\n 'color',\n 'file',\n 'hidden',\n 'image',\n 'radio',\n 'range',\n 'reset',\n 'submit',\n]);\n\n/**\n * True for an element that opens the soft keyboard when focused: a text-like `input` (not a\n * button, checkbox, color, file, hidden, image, radio, range, reset or submit), a `textarea`, or\n * a `[contenteditable]` element other than `contenteditable=\"false\"`.\n *\n * @param element The element to test, usually `document.activeElement`.\n * @returns Whether it opens the soft keyboard.\n * @summary Tells whether focusing an element opens the soft keyboard on a phone\n * @since 0.2.0\n */\nexport function isTextEntry(element: Element | null): element is HTMLElement {\n if (element instanceof HTMLTextAreaElement) {\n return true;\n }\n if (element instanceof HTMLInputElement) {\n return !NON_TEXT_INPUT_TYPES.has(element.type);\n }\n return (\n element instanceof HTMLElement &&\n (element.isContentEditable ||\n element.matches('[contenteditable]:not([contenteditable=\"false\"])'))\n );\n}\n\n/**\n * The soft keyboard as signals. On native it listens to the Capacitor Keyboard plugin\n * (`@capacitor/keyboard`, loaded lazily, height known at `keyboardWillShow`); on the web it falls\n * back to `visualViewport` resizes with an 80 px threshold (height known only once the viewport has\n * shrunk; ignored while the page is pinch-zoomed, `visualViewport.scale` ≠ 1); in Jest and SSR it\n * stays inert (height 0, never visible). The rest height (viewport with the keyboard hidden) comes\n * from a fixed `100lvh` probe element, never `innerHeight`, which sticks at a wrong value after a\n * field-to-field focus jump on iOS; it is measured at construction and on window resizes while the\n * keyboard is hidden and no text control is focused, never at will-show. `imminent` flags a focused\n * text control before any keyboard event, and `keyboardTop()` gives the keyboard's top edge in\n * viewport px. It also writes the height as `--bf-keyboard-height` on `<html>` (`0px` while hidden)\n * for CSS that follows the keyboard.\n *\n * @example\n * const keyboard = inject(BfKeyboardService);\n * const hideTabBar = computed(() => keyboard.visible());\n * @since 0.2.0\n * @summary The soft keyboard state as signals: visible, its height and its phase\n */\n@Injectable({ providedIn: 'root' })\nexport class BfKeyboardService {\n private readonly platformService = inject(BfPlatformService);\n private readonly document = inject(DOCUMENT);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly heightState = signal(0);\n private readonly phaseState = signal<BfKeyboardPhase>('hidden');\n private readonly restHeightState = signal(0);\n private readonly willShowState = signal(0);\n private readonly imminentState = signal(false);\n private imminentTarget: EventTarget | null = null;\n private imminentTimer: ReturnType<typeof setTimeout> | null = null;\n private plugin: KeyboardPlugin | null = null;\n private probe: HTMLElement | null = null;\n private destroyed = false;\n\n /** Keyboard height in CSS px (iOS: includes the home-indicator strip); 0 when hidden. */\n readonly height = this.heightState.asReadonly();\n /** Lifecycle phase for animations; see `BfKeyboardPhase`. */\n readonly phase = this.phaseState.asReadonly();\n /** True from will-show until either hide event: the moment to hide chrome such as the tab bar. */\n readonly visible = computed(() => this.phase() === 'showing' || this.phase() === 'shown');\n /** True when the keyboard overlays the WebView (native iOS, `resize: 'none'`); false when the WebView resizes. */\n readonly overlaysContent = computed(\n () => this.platformService.isNative && this.platformService.platform === 'ios',\n );\n /** Viewport height with the keyboard hidden, in CSS px, from a fixed `100lvh` probe. */\n readonly restHeight = this.restHeightState.asReadonly();\n /** Increments once per keyboard show (at will-show), for one-off reactions such as closing popovers. */\n readonly willShow = this.willShowState.asReadonly();\n /**\n * True from `focusin` on a text-entry control (see `isTextEntry`) until that control's\n * `focusout` or either keyboard hide event; never cleared by will-show. Hide chrome such as the\n * tab bar on `visible() || imminent()`: on Android the WebView shrinks before the plugin's\n * will-show, which lifts `position: fixed` elements above the keyboard; `focusin` is the only\n * earlier signal. When it turns true without the keyboard up, it also clears itself after\n * `IMMINENT_TIMEOUT_MS` (2500 ms) unless will-show or a visible keyboard arrives first, so a\n * hardware keyboard, which fires no keyboard event, does not keep the tab bar hidden.\n */\n readonly imminent = this.imminentState.asReadonly();\n\n constructor() {\n const view = this.document.defaultView;\n if (!view || !this.document.body) {\n return;\n }\n this.probe = this.createProbe();\n this.measureRestHeight();\n this.writeHeightVariable(0);\n // A resize while a text control is focused is the keyboard, not a new rest size.\n const onWindowResize = (): void => {\n if (this.phase() === 'hidden' && !this.imminent()) {\n this.measureRestHeight();\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n if (isTextEntry(event.target as Element | null)) {\n this.imminentTarget = event.target;\n if (!this.imminentState()) {\n this.imminentState.set(true);\n this.armImminentTimeout();\n }\n }\n };\n const onFocusOut = (event: FocusEvent): void => {\n if (event.target === this.imminentTarget) {\n this.clearImminent();\n }\n };\n view.addEventListener('resize', onWindowResize);\n this.document.addEventListener('focusin', onFocusIn, true);\n this.document.addEventListener('focusout', onFocusOut, true);\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.cancelImminentTimeout();\n view.removeEventListener('resize', onWindowResize);\n this.document.removeEventListener('focusin', onFocusIn, true);\n this.document.removeEventListener('focusout', onFocusOut, true);\n this.probe?.remove();\n this.document.documentElement.style.removeProperty(HEIGHT_VARIABLE);\n });\n\n if (this.platformService.isNative) {\n void this.attachPlugin();\n } else if (view.visualViewport) {\n this.attachVisualViewport(view.visualViewport);\n }\n }\n\n /** Hides the keyboard: `Keyboard.hide()` on native, blurs the focused element on the web. */\n async hide(): Promise<void> {\n if (this.platformService.isNative) {\n try {\n const plugin = this.plugin ?? (await import('@capacitor/keyboard')).Keyboard;\n await plugin.hide();\n } catch {\n // The plugin is an optional peer; without it there is nothing to hide.\n }\n return;\n }\n const active = this.document.activeElement;\n if (active instanceof HTMLElement) {\n active.blur();\n }\n }\n\n /**\n * The keyboard's top edge in viewport CSS px, read on demand. While the keyboard overlays the\n * WebView (native iOS) it is `restHeight() - height()`; where the viewport shrinks for it\n * (Android, the web) it is the bottom of `visualViewport` (else `innerHeight`). With the\n * keyboard hidden it is `restHeight()`.\n */\n keyboardTop(): number {\n if (!this.visible()) {\n return this.restHeight();\n }\n if (this.overlaysContent()) {\n return this.restHeight() - this.height();\n }\n const view = this.document.defaultView;\n const viewport = view?.visualViewport;\n if (viewport) {\n return viewport.height + viewport.offsetTop;\n }\n return view?.innerHeight ?? this.restHeight();\n }\n\n private createProbe(): HTMLElement {\n const existing = this.document.body.querySelector<HTMLElement>(`[${PROBE_ATTRIBUTE}]`);\n if (existing) {\n return existing;\n }\n const probe = this.document.createElement('div');\n probe.setAttribute(PROBE_ATTRIBUTE, '');\n probe.setAttribute('aria-hidden', 'true');\n probe.setAttribute(\n 'style',\n 'position: fixed; top: 0; left: -2px; width: 1px; height: 100lvh; visibility: hidden; pointer-events: none',\n );\n this.document.body.appendChild(probe);\n return probe;\n }\n\n private measureRestHeight(): void {\n if (this.probe) {\n this.restHeightState.set(Math.round(this.probe.offsetHeight));\n }\n }\n\n /** Never measures the rest height: on Android the WebView has already shrunk by will-show. */\n private onWillShow(height: number): void {\n this.cancelImminentTimeout();\n this.setHeight(height);\n this.phaseState.set('showing');\n this.willShowState.update((count) => count + 1);\n }\n\n private onDidShow(height: number): void {\n this.setHeight(height);\n this.phaseState.set('shown');\n }\n\n private onHidden(): void {\n this.setHeight(0);\n this.phaseState.set('hidden');\n }\n\n /** Either plugin hide event: the keyboard is gone even if the control keeps focus. */\n private onHideEvent(): void {\n this.clearImminent();\n this.onHidden();\n }\n\n private clearImminent(): void {\n this.cancelImminentTimeout();\n this.imminentTarget = null;\n this.imminentState.set(false);\n }\n\n /** Arms the hardware-keyboard fallback; a keyboard already up needs none. */\n private armImminentTimeout(): void {\n this.cancelImminentTimeout();\n if (this.visible()) {\n return;\n }\n this.imminentTimer = setTimeout(() => {\n this.imminentTimer = null;\n if (!this.visible()) {\n this.clearImminent();\n }\n }, IMMINENT_TIMEOUT_MS);\n }\n\n private cancelImminentTimeout(): void {\n if (this.imminentTimer !== null) {\n clearTimeout(this.imminentTimer);\n this.imminentTimer = null;\n }\n }\n\n private setHeight(height: number): void {\n this.heightState.set(height);\n this.writeHeightVariable(height);\n }\n\n private writeHeightVariable(height: number): void {\n this.document.documentElement.style.setProperty(HEIGHT_VARIABLE, `${height}px`);\n }\n\n private async attachPlugin(): Promise<void> {\n try {\n const { Keyboard } = await import('@capacitor/keyboard');\n this.plugin = Keyboard;\n const handles = await Promise.all([\n Keyboard.addListener('keyboardWillShow', (info) => this.onWillShow(info.keyboardHeight)),\n Keyboard.addListener('keyboardDidShow', (info) => this.onDidShow(info.keyboardHeight)),\n Keyboard.addListener('keyboardWillHide', () => this.onHideEvent()),\n Keyboard.addListener('keyboardDidHide', () => this.onHideEvent()),\n ]);\n const removeAll = (): void => {\n for (const handle of handles) {\n void handle.remove();\n }\n };\n if (this.destroyed) {\n removeAll();\n } else {\n this.destroyRef.onDestroy(removeAll);\n }\n } catch {\n // `@capacitor/keyboard` is an optional peer: without it the service stays inert.\n }\n }\n\n private attachVisualViewport(viewport: VisualViewport): void {\n const onResize = (): void => {\n // A pinch-zoomed visual viewport shrinks too; it is never a keyboard.\n const zoomed = Math.abs((viewport.scale ?? 1) - 1) > ZOOM_TOLERANCE;\n const covered = zoomed\n ? 0\n : Math.max(0, Math.round(this.restHeightState() - viewport.height - viewport.offsetTop));\n if (covered > WEB_KEYBOARD_THRESHOLD) {\n if (!this.visible()) {\n this.cancelImminentTimeout();\n this.willShowState.update((count) => count + 1);\n }\n this.setHeight(covered);\n this.phaseState.set('shown');\n } else if (this.visible()) {\n this.onHideEvent();\n } else {\n this.onHidden();\n }\n };\n viewport.addEventListener('resize', onResize);\n this.destroyRef.onDestroy(() => viewport.removeEventListener('resize', onResize));\n }\n}\n","import { Injectable } from '@angular/core';\nimport { type ActivatedRouteSnapshot, BaseRouteReuseStrategy } from '@angular/router';\n\n/**\n * The route reuse strategy the stack needs: the default one, except that a route whose *path\n * params* change (`/items/4` → `/items/5`) is a new page, not an in-place update. The default only\n * compares `routeConfig`, so the router would reuse the component and never call the outlet, and\n * the stack would no longer match the history. Query-param changes still reuse the page (filters,\n * tabs). Exported so an app can extend it for its own reuse rules.\n * @summary Route reuse strategy that treats a changed path parameter as a new stack page\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfStackReuseStrategy extends BaseRouteReuseStrategy {\n /**\n * Whether the router may reuse the current page for `future`: same route config and path params.\n * @param future The route being navigated to.\n * @param curr The route shown now.\n * @returns True to reuse the page in place.\n */\n override shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {\n if (future.routeConfig !== curr.routeConfig) {\n return false;\n }\n const a = future.params;\n const b = curr.params;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]);\n for (const key of keys) {\n if (a[key] !== b[key]) {\n return false;\n }\n }\n return true;\n }\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\nimport {\n RouteReuseStrategy,\n type RouterConfigurationFeature,\n withRouterConfig,\n} from '@angular/router';\nimport { BF_OVERLAY_HOST, type BfOverlayHost } from '@bitforgehq/ui/overlay';\nimport { BfStackReuseStrategy } from '../navigation/stack-reuse-strategy';\nimport { BfBackButtonService } from '../platform/back-button.service';\nimport { BfHapticsService } from '../platform/haptics.service';\nimport { BfKeyboardService } from '../platform/keyboard.service';\nimport { BfPlatformService } from '../platform/platform.service';\nimport { BfTextScaleService } from '../platform/text-scale.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, type BfShellConfig } from './shell-config';\n\nexport {\n BF_SHELL_CONFIG,\n BF_SHELL_DEFAULTS,\n type BfBackAtRoot,\n type BfShellConfig,\n type BfShellConfigMode,\n type BfShellDynamicType,\n type BfShellDynamicTypeLimit,\n} from './shell-config';\nexport { injectShellMode, resolveShellMode } from '../platform/platform.service';\n\n/**\n * Provides the mobile shell: its configuration, the stack-aware route reuse strategy\n * (`BfStackReuseStrategy`) and `BF_OVERLAY_HOST` (the hardware back button closes the top overlay\n * through `BfBackButtonService`, which on native Android also stands in for the overlays'\n * `CloseWatcher`, and the document never scrolls, so overlays skip the scroll lock). An environment initializer starts `BfPlatformService` (theme sync, the `dark` class),\n * `BfBackButtonService` (the Android hardware back listener), `BfKeyboardService` (keyboard\n * listeners and the rest-height probe), `BfHapticsService` (its native plugin loads lazily on first\n * use, so this only makes the singleton available) and `BfTextScaleService` (iOS Dynamic Type on\n * the root font size) at bootstrap, before any page renders. Every service is\n * `providedIn: 'root'`, so this function lists none of them: `BfNavController` and the overlay\n * services (`BfOverlayStack`, `BfOverlayService`, `BfSheetService`, `BfModalService`,\n * `BfToastService`, `BfActionSheetService`, `BfAlertDialogService`) are created on first\n * injection, and an app that never injects an overlay service bundles no overlay code. Use it\n * together with `provideRouter(routes, withComponentInputBinding(), withBfShellRouting())`, in the\n * application providers: in a lazy route's environment injector the services still resolve from\n * the root injector.\n *\n * @param config The settings to change; any left out keep `BF_SHELL_DEFAULTS`.\n * @returns The shell's environment providers.\n * @summary Provides the Capacitor app shell: its configuration, navigation and platform services\n * @since 0.2.0\n */\nexport function provideBfShell(config: Partial<BfShellConfig> = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: BF_SHELL_CONFIG, useValue: { ...BF_SHELL_DEFAULTS, ...config } },\n { provide: RouteReuseStrategy, useClass: BfStackReuseStrategy },\n {\n provide: BF_OVERLAY_HOST,\n useFactory: (): BfOverlayHost => {\n const back = inject(BfBackButtonService);\n const platform = inject(BfPlatformService);\n return {\n documentScrolls: false,\n // Capacitor's backButton event already reaches the queue on native Android; everywhere\n // else (a mobile browser, a PWA) the overlays' own CloseWatcher takes Android back.\n handlesSystemBack: platform.isNative && platform.platform === 'android',\n registerBack: (priority, handler) => back.register(priority, handler),\n };\n },\n },\n provideEnvironmentInitializer(() => {\n inject(BfPlatformService);\n inject(BfBackButtonService);\n inject(BfKeyboardService);\n inject(BfHapticsService);\n inject(BfTextScaleService);\n }),\n ]);\n}\n\n/**\n * The router feature the stack needs: `canceledNavigationResolution: 'computed'`, so a refused\n * back navigation (a guard stopping a swipe-back) restores the browser history position and the\n * stack stays in sync with it. Pass it to `provideRouter()`.\n *\n * @returns The router configuration feature.\n * @summary The router setting the mobile shell's stack needs, passed to provideRouter\n * @since 0.2.0\n */\nexport function withBfShellRouting(): RouterConfigurationFeature {\n return withRouterConfig({ canceledNavigationResolution: 'computed' });\n}\n","import { signal } from '@angular/core';\n\n/**\n * Where a stacked page is in its lifecycle.\n *\n * @summary Where a stacked page is in its lifecycle: entering, active, leaving or inactive\n * @since 0.2.0\n */\nexport type BfViewState = 'entering' | 'active' | 'leaving' | 'inactive';\n\n/**\n * One lifecycle event of a stacked page.\n *\n * @summary The four lifecycle events of a stacked page as it enters and leaves\n * @since 0.2.0\n */\nexport type BfViewEvent = 'willEnter' | 'didEnter' | 'willLeave' | 'didLeave';\n\n/**\n * Per-page lifecycle. `bf-stack-outlet` creates one per page and provides it through the page's\n * element injector; `bf-page` re-exposes it as outputs, and any page or service can\n * `inject(BfViewLifecycle)` to react to the page entering or leaving the top of the stack.\n * `state` and the counters are signals for `computed`/`effect`; `on()` delivers every event\n * synchronously, including the ones that happen before the page's first change detection and\n * right before a popped page is destroyed.\n * @summary The lifecycle of one stacked page as signals and events, from enter to leave\n * @since 0.2.0\n */\nexport class BfViewLifecycle {\n /** The page's current lifecycle state. */\n readonly state = signal<BfViewState>('inactive');\n /** How often the page started entering; monotonic, so a page can `effect()` on it. */\n readonly willEnter = signal(0);\n /** How often the page became the top page (its enter transition ended). */\n readonly didEnter = signal(0);\n /** How often the page started leaving the top of the stack. */\n readonly willLeave = signal(0);\n /** How often the page stopped being the top page (its leave transition ended). */\n readonly didLeave = signal(0);\n\n private readonly listeners = new Set<(event: BfViewEvent) => void>();\n\n /**\n * Calls `listener` synchronously on every event; returns a function that removes it.\n * @param listener Called with each event, synchronously.\n */\n on(listener: (event: BfViewEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Moves the page to its `willEnter` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireWillEnter(): void {\n this.state.set('entering');\n this.willEnter.update((n) => n + 1);\n this.notify('willEnter');\n }\n\n /**\n * Moves the page to its `didEnter` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireDidEnter(): void {\n this.state.set('active');\n this.didEnter.update((n) => n + 1);\n this.notify('didEnter');\n }\n\n /**\n * Moves the page to its `willLeave` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireWillLeave(): void {\n this.state.set('leaving');\n this.willLeave.update((n) => n + 1);\n this.notify('willLeave');\n }\n\n /**\n * Moves the page to its `didLeave` state and notifies; called by `bf-stack-outlet`.\n * @internal\n */\n fireDidLeave(): void {\n this.state.set('inactive');\n this.didLeave.update((n) => n + 1);\n this.notify('didLeave');\n }\n\n private notify(event: BfViewEvent): void {\n for (const listener of [...this.listeners]) {\n listener(event);\n }\n }\n}\n","import { DestroyRef, Injectable, type Signal, computed, inject, signal } from '@angular/core';\nimport { Location } from '@angular/common';\nimport {\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n type NavigationExtras,\n NavigationSkipped,\n NavigationStart,\n Router,\n type UrlTree,\n} from '@angular/router';\nimport { BfBackButtonService } from '../platform/back-button.service';\n\n/**\n * The direction a navigation should animate in.\n *\n * @summary The direction a stack navigation animates in: forward, back or a new root\n * @since 0.2.0\n */\nexport type BfNavDirection = 'forward' | 'back' | 'root';\n\n/**\n * A router target: a URL string, a `UrlTree` or a commands array, as `Router` accepts.\n *\n * @summary A navigation target as the router accepts it: a URL, a UrlTree or commands\n * @since 0.2.0\n */\nexport type BfNavTarget = string | UrlTree | readonly unknown[];\n\n/** What a stack outlet tells `BfNavController` when it registers (see `registerOutlet`). */\nexport interface BfNavOutlet {\n /** Number of pages in the outlet's shown stack. */\n readonly depth: Signal<number>;\n /** False while the page that hosts the outlet is parked under another page (or leaving). */\n readonly active: Signal<boolean>;\n /** The (negative) history step that pops the outlet's top page, or null when unknown. */\n popDelta(): number | null;\n}\n\ninterface OutletRegistration {\n readonly outlet: BfNavOutlet;\n /** Number of stack outlets around this one: 0 for the root outlet. */\n readonly level: number;\n}\n\n/**\n * Stack-aware navigation. `forward` pushes, `back` pops through the real history (so the browser\n * or WebView history stays in sync), `root` replaces the whole stack. Each call leaves a direction\n * hint that `bf-stack-outlet` consumes on the next activation; without a hint the outlet infers\n * the direction from the router event. A hint lives only as long as the navigation it started:\n * when that navigation ends without an activation (a guard refused it), the hint is dropped so it\n * cannot mislabel the next one. Every primary `bf-stack-outlet` registers here with its nesting\n * level; `depth`, `canGoBack` and `back()` follow the **deepest active outlet** (the innermost one\n * whose page is shown, e.g. the outlet inside `bf-tabs`, or the root outlet once it pushed a page\n * above the tabs page). The controller also owns the priority-0 hardware back handler: it pops the\n * deepest active outlet that has a page to pop, and lets the app exit only when every active\n * outlet is at its first page (a parked outlet cannot be popped, so it never blocks the exit).\n * @summary Stack-aware navigation that pushes, pops through the history and replaces the root\n * @since 0.2.0\n */\n@Injectable({ providedIn: 'root' })\nexport class BfNavController {\n private readonly router = inject(Router);\n private readonly location = inject(Location);\n private readonly outlets = signal<readonly OutletRegistration[]>([]);\n private hint: BfNavDirection | null = null;\n /** Id of the navigation the hint belongs to; null until that navigation starts. */\n private hintNavigationId: number | null = null;\n\n /** The active outlets, deepest first (on equal level, the one registered last first). */\n private readonly activeOutlets = computed(() =>\n this.outlets()\n .map((r, order) => ({ ...r, order }))\n .filter((r) => r.outlet.active())\n .sort((a, b) => b.level - a.level || b.order - a.order)\n .map((r) => r.outlet),\n );\n\n /** Number of pages in the deepest active stack outlet (0 without an outlet). */\n readonly depth = computed(() => this.activeOutlets()[0]?.depth() ?? 0);\n /** True when the deepest active stack has a page to go back to. */\n readonly canGoBack = computed(() => this.depth() > 1);\n\n constructor() {\n // `router.events`, not the router's navigation signals: the hint belongs to one navigation id\n // and needs that navigation's ordered Start and End/Cancel/Error/Skipped events, which a signal\n // of the latest navigation coalesces.\n const sub = this.router.events.subscribe((e) => {\n if (e instanceof NavigationStart) {\n if (this.hint !== null && this.hintNavigationId === null) {\n this.hintNavigationId = e.id;\n }\n } else if (\n (e instanceof NavigationEnd ||\n e instanceof NavigationCancel ||\n e instanceof NavigationError ||\n e instanceof NavigationSkipped) &&\n e.id === this.hintNavigationId\n ) {\n this.clearHint();\n }\n });\n const unregisterBack = inject(BfBackButtonService).register(0, () => this.handleBackButton());\n inject(DestroyRef).onDestroy(() => {\n sub.unsubscribe();\n unregisterBack();\n });\n }\n\n /**\n * Pushes a page.\n * @param url Where to go.\n * @param extras Router navigation extras, such as `queryParams`.\n * @returns Whether the navigation succeeded.\n */\n forward(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n this.setHint('forward');\n return this.go(url, extras);\n }\n\n /**\n * Replaces the whole stack with one page (`replaceUrl`, no animation).\n * @param url Where to go.\n * @param extras Router navigation extras, such as `queryParams`.\n * @returns Whether the navigation succeeded.\n */\n root(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n this.setHint('root');\n return this.go(url, { ...extras, replaceUrl: true });\n }\n\n /**\n * Pops the top page of the deepest active outlet that has one to pop: always a real history\n * step. When the page added history entries of its own (a query-only or nested child\n * navigation), it goes back past them in one step (`Location.historyGo(-n)`) so the page below\n * is shown; otherwise `Location.back()`.\n */\n back(): void {\n this.setHint('back');\n const delta = this.popTarget()?.popDelta() ?? null;\n if (delta !== null && delta < -1) {\n this.location.historyGo(delta);\n } else {\n this.location.back();\n }\n }\n\n /**\n * Called by every primary stack outlet on init with its nesting level (the number of stack\n * outlets around it). Returns a function that removes it again.\n * @internal\n */\n registerOutlet(outlet: BfNavOutlet, level: number): () => void {\n const registration: OutletRegistration = { outlet, level };\n this.outlets.update((all) => [...all, registration]);\n return () => this.outlets.update((all) => all.filter((r) => r !== registration));\n }\n\n /**\n * Returns the pending direction hint and clears it; called by the stack outlet.\n * @internal\n */\n consumeHint(): BfNavDirection | null {\n const hint = this.hint;\n this.clearHint();\n return hint;\n }\n\n /**\n * Drops a pending hint (its navigation never happened).\n * @internal\n */\n clearHint(): void {\n this.hint = null;\n this.hintNavigationId = null;\n }\n\n /** The deepest active outlet with a page to pop, else the deepest active outlet. */\n private popTarget(): BfNavOutlet | undefined {\n const active = this.activeOutlets();\n return active.find((o) => o.depth() > 1) ?? active[0];\n }\n\n /**\n * The hardware back button: pops while an active outlet has a page to pop; returns false (and\n * the back button service exits the app) when every active outlet is at depth 1 or less. A\n * parked outlet cannot be popped by `back()`, so it does not keep the app from exiting.\n */\n private handleBackButton(): boolean {\n if (this.activeOutlets().every((o) => o.depth() <= 1)) {\n return false;\n }\n this.back();\n return true;\n }\n\n private setHint(hint: BfNavDirection): void {\n this.hint = hint;\n this.hintNavigationId = null;\n }\n\n private go(url: BfNavTarget, extras?: NavigationExtras): Promise<boolean> {\n if (Array.isArray(url)) {\n return this.router.navigate(url, extras);\n }\n return this.router.navigateByUrl(url as string | UrlTree, extras);\n }\n}\n","/**\n * Motion constants of the mobile shell, shared by the page transitions, the overlays and the\n * refresher. `theme.css` mirrors them for CSS: `--ease-ios`, `--ease-md`, `--ease-settle`\n * (`@theme`) and `--bf-duration-fast`, `--bf-duration-overlay`, `--bf-duration-push` (`:root`).\n */\n\nimport { OVERLAY_DURATION, OVERLAY_REDUCED_DURATION } from '@bitforgehq/ui/overlay';\n\n/**\n * UIKit's navigation and sheet curve (push/pop, overlays, modal depth); `OVERLAY_EASING` too.\n *\n * @summary The UIKit navigation and sheet easing curve of pushes, pops and overlays\n * @since 0.2.0\n */\nexport const IOS_EASING = 'cubic-bezier(0.32, 0.72, 0, 1)';\n/**\n * Material standard easing (the Android fade-up).\n *\n * @summary The Material standard easing curve of the Android page fade-up transition\n * @since 0.2.0\n */\nexport const MD_EASING = 'cubic-bezier(0.2, 0, 0, 1)';\n/**\n * Ease-out of a released gesture settling into place (swipe-back, refresher).\n *\n * @summary The ease-out curve a released swipe-back or refresher gesture settles with\n * @since 0.2.0\n */\nexport const SETTLE_EASING = 'cubic-bezier(0.2, 0.8, 0.3, 1)';\n\n/**\n * Colour transitions and the reduced-motion cross-fade (ms); the overlay core's reduced fade.\n *\n * @summary The duration of colour transitions and the reduced-motion fade, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_FAST = OVERLAY_REDUCED_DURATION;\n/**\n * Overlay enter/exit and the modal depth effect (ms); the overlay core's duration.\n *\n * @summary The duration of overlay enter and exit and the modal depth effect, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_OVERLAY = OVERLAY_DURATION;\n/**\n * iOS push/pop (ms).\n *\n * @summary The duration of the iOS push and pop page transitions, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_PUSH = 500;\n/**\n * Material fade-up and the refresher settle (ms).\n *\n * @summary The duration of the Material fade-up and the refresher settle, in milliseconds\n * @since 0.2.0\n */\nexport const DURATION_MD = 220;\n","/**\n * Page transitions of the stack, built from the Web Animations API. The iOS values follow UIKit's\n * navigation transition: the incoming page slides 100 % → 0, the outgoing one parallaxes\n * 0 → -30 % under a dimming layer, spring-like ease-out, 500 ms.\n */\nimport {\n DURATION_FAST,\n DURATION_MD,\n DURATION_PUSH,\n IOS_EASING,\n MD_EASING,\n SETTLE_EASING,\n} from '../motion';\n\nexport { IOS_EASING, MD_EASING, SETTLE_EASING };\n\n/**\n * The iOS push and pop (ms), `DURATION_PUSH`.\n *\n * @summary The duration of the iOS push and pop stack transitions, in milliseconds\n * @since 0.2.0\n */\nexport const IOS_DURATION = DURATION_PUSH;\n/**\n * Upper bound of the settle animation after a released swipe.\n *\n * @summary The longest a released swipe-back may take to settle, in milliseconds\n * @since 0.2.0\n */\nexport const MAX_COMPLETE_MS = 540;\n/**\n * Material fade-up.\n *\n * @summary The duration of the Material fade-up stack transition, in milliseconds\n * @since 0.2.0\n */\nexport const MD_DURATION = DURATION_MD;\n/**\n * Cross-fade used under `prefers-reduced-motion`.\n *\n * @summary The duration of the cross-fade that replaces page motion under reduced motion\n * @since 0.2.0\n */\nexport const REDUCED_DURATION = DURATION_FAST;\n\n/**\n * A running set of animations that finish together.\n *\n * @summary A running set of page transition animations that finish together\n * @since 0.2.0\n */\nexport interface BfStackTransition {\n /** The Web Animations of the transition, one per moving layer. */\n readonly animations: Animation[];\n /** Their shared duration (ms). */\n readonly duration: number;\n /** Resolves when every animation finished or was cancelled; never rejects. */\n readonly finished: Promise<void>;\n}\n\n/**\n * The direction of a stack transition: a page pushed on, or popped off.\n *\n * @summary The direction of a stack transition, a page pushed on or popped off\n * @since 0.2.0\n */\nexport type BfTransitionKind = 'push' | 'pop';\n\nfunction group(animations: Animation[], duration: number): BfStackTransition {\n const finished = Promise.all(animations.map((a) => a.finished)).then(\n () => undefined,\n () => undefined,\n );\n return { animations, duration, finished };\n}\n\nfunction translateX(px: number): string {\n return `translate3d(${px}px,0,0)`;\n}\n\n/**\n * The iOS push or pop. `pop` is written as the literal reverse of a push but as forward\n * keyframes, so `currentTime` maps directly to swipe progress (0 = nothing moved, duration = pop\n * complete). Returns three animations: the top page (slide and edge shadow), the page under it\n * (parallax) and the dim layer.\n *\n * @param kind `push` or `pop`.\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @param dim The dimming layer between the two pages.\n * @param width The width the pages travel (the outlet width, px).\n * @param easing The curve of the transition.\n * @returns The three running animations.\n * @summary Builds the iOS push or pop with its slide, parallax and dimming layers\n * @since 0.2.0\n */\nexport function createTransition(\n kind: BfTransitionKind,\n entering: HTMLElement,\n leaving: HTMLElement,\n dim: HTMLElement,\n width: number,\n easing: string = IOS_EASING,\n): BfStackTransition {\n const duration = IOS_DURATION;\n const options: KeyframeAnimationOptions = { duration, easing, fill: 'both' };\n const top = kind === 'push' ? entering : leaving;\n const under = kind === 'push' ? leaving : entering;\n const shown = { transform: translateX(0), boxShadow: 'var(--shadow-floating)' };\n const offscreen = { transform: translateX(width), boxShadow: 'none' };\n const rest = { transform: translateX(0) };\n const parallax = { transform: translateX(-width * 0.3) };\n const animations = [\n top.animate(kind === 'push' ? [offscreen, shown] : [shown, offscreen], options),\n under.animate(kind === 'push' ? [rest, parallax] : [parallax, rest], options),\n dim.animate(\n kind === 'push' ? [{ opacity: 0 }, { opacity: 1 }] : [{ opacity: 1 }, { opacity: 0 }],\n options,\n ),\n ];\n return group(animations, duration);\n}\n\n/**\n * Material fade-up: the entering page fades in while rising 16 px; a pop is the reverse on the\n * leaving page. No parallax, no dim.\n *\n * @param kind `push` or `pop`.\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @returns The running animation.\n * @summary Builds the Material fade-up page transition of a push or a pop\n * @since 0.2.0\n */\nexport function mdTransition(\n kind: BfTransitionKind,\n entering: HTMLElement,\n leaving: HTMLElement,\n): BfStackTransition {\n const options: KeyframeAnimationOptions = {\n duration: MD_DURATION,\n easing: MD_EASING,\n fill: 'both',\n };\n const hidden = { opacity: 0, transform: 'translate3d(0,16px,0)' };\n const shown = { opacity: 1, transform: 'translate3d(0,0,0)' };\n const animation =\n kind === 'push'\n ? entering.animate([hidden, shown], options)\n : leaving.animate([shown, hidden], options);\n return group([animation], MD_DURATION);\n}\n\n/**\n * Reduced motion: a 150 ms cross-fade of the two pages, nothing moves.\n *\n * @param entering The page that becomes visible.\n * @param leaving The page that goes away.\n * @returns The two running fades.\n * @summary Builds the cross-fade that replaces page motion when the user reduces motion\n * @since 0.2.0\n */\nexport function reducedTransition(entering: HTMLElement, leaving: HTMLElement): BfStackTransition {\n const options: KeyframeAnimationOptions = {\n duration: REDUCED_DURATION,\n easing: 'linear',\n fill: 'both',\n };\n return group(\n [\n entering.animate([{ opacity: 0 }, { opacity: 1 }], options),\n leaving.animate([{ opacity: 1 }, { opacity: 0 }], options),\n ],\n REDUCED_DURATION,\n );\n}\n\n/** True when the user asked the system to minimise motion (shared with the overlay core). */\nexport { prefersReducedMotion } from '@bitforgehq/ui/overlay';\n\n/**\n * Frame values of the pop at progress `p` (0 = start, 1 = done), used to hand a scrubbed gesture\n * over to a fresh eased animation on release.\n *\n * @param p The pop progress, 0 (start) to 1 (done).\n * @param width The outlet width (px).\n * @returns The top page offset, the page under it and the dim opacity.\n * @summary The positions of the pop layers at a given progress, for scrubbing a swipe\n * @since 0.2.0\n */\nexport function popFramesAt(p: number, width: number): { top: number; under: number; dim: number } {\n return { top: width * p, under: -width * 0.3 * (1 - p), dim: 1 - p };\n}\n\n/**\n * Animates top, under and dim from pop progress `from` to `to` with an ease-out.\n *\n * @param top The page being popped.\n * @param under The page under it.\n * @param dim The dimming layer.\n * @param width The outlet width (px).\n * @param from The progress at release, 0 to 1.\n * @param to The progress to settle at: 1 completes the pop, 0 cancels it.\n * @param duration The settle duration (ms), see `settleDuration`.\n * @returns The three running animations.\n * @summary Animates a released swipe-back from its progress to done or back to the start\n * @since 0.2.0\n */\nexport function settlePop(\n top: HTMLElement,\n under: HTMLElement,\n dim: HTMLElement,\n width: number,\n from: number,\n to: number,\n duration: number,\n): BfStackTransition {\n const a = popFramesAt(from, width);\n const b = popFramesAt(to, width);\n const options: KeyframeAnimationOptions = { duration, easing: SETTLE_EASING, fill: 'both' };\n return group(\n [\n top.animate([{ transform: translateX(a.top) }, { transform: translateX(b.top) }], options),\n under.animate(\n [{ transform: translateX(a.under) }, { transform: translateX(b.under) }],\n options,\n ),\n dim.animate([{ opacity: a.dim }, { opacity: b.dim }], options),\n ],\n duration,\n );\n}\n\n/**\n * The release rule of the swipe-back: a flick (`velocity` above 0.2 px/ms) or a drag past half\n * the width completes the pop; anything else settles back.\n *\n * @param velocity The release speed (px/ms, positive towards the end edge).\n * @param delta The distance swiped (px).\n * @param width The outlet width (px).\n * @returns True when the pop completes.\n * @summary Decides whether a released swipe-back completes the pop or settles back\n * @since 0.2.0\n */\nexport function shouldCompletePop(velocity: number, delta: number, width: number): boolean {\n return velocity > 0.2 || delta > width / 2;\n}\n\n/**\n * Duration of the settle animation after a release: the remaining distance at the release speed\n * (at least 0.5 px/ms), clamped to 120 ms … `MAX_COMPLETE_MS`.\n *\n * @param remainingPx The distance left to travel (px).\n * @param velocity The release speed (px/ms).\n * @returns The duration (ms), 120 to `MAX_COMPLETE_MS`.\n * @summary The duration of the settle animation after a released swipe-back gesture\n * @since 0.2.0\n */\nexport function settleDuration(remainingPx: number, velocity: number): number {\n const speed = Math.max(Math.abs(velocity), 0.5);\n return Math.round(Math.min(MAX_COMPLETE_MS, Math.max(120, remainingPx / speed)));\n}\n","import { signal } from '@angular/core';\n\n/**\n * Per-page state shared between the parts of one `bf-page`. `bf-page` provides a fresh instance\n * for each page through its element injector; `bf-content` registers its scroll container here\n * on init (and clears it on destroy), so siblings such as `bf-header` can follow the page's\n * scroll without querying the DOM. A plain class, like `BfViewLifecycle`: it is only ever\n * provided per page, never from the root injector.\n *\n * @example\n * const scroller = inject(BfPageContext, { optional: true })?.scroller;\n * @since 0.2.0\n * @summary Per-page state that the header and content of one page share\n */\nexport class BfPageContext {\n /** The page's scroll container (`bf-content`'s scroller), or `null` before one registers. */\n readonly scroller = signal<HTMLElement | null>(null);\n /**\n * Height (px) of a `translucent` `bf-header` drawn over the content, which `bf-content` pads its\n * scroller by (`--bf-header-height`); 0 while the header sits in the page's flow (or none).\n */\n readonly headerHeight = signal(0);\n /**\n * Whether the page reaches the top of the screen, so its `bf-header` toolbar (and a `fullscreen`\n * `bf-content`) pads by the top safe area. True for a page; `bf-modal` in the `sheet`\n * presentation sets it to false, because its card already sits below the status bar.\n */\n readonly safeAreaTop = signal(true);\n /**\n * Whether the page's `bf-content` scroller is the document's `main` landmark (`role=\"main\"`).\n * True for the page a `bf-stack-outlet` shows; `bf-modal` sets it to false (content in a dialog\n * is not `main`), and so does a `bf-page` outside an outlet (a docs illustration) or nested in\n * another page, so the document keeps exactly one `main`: parked pages are `inert` and\n * `aria-hidden`, so only the shown page's landmark is exposed.\n */\n readonly mainLandmark = signal(true);\n}\n\n/**\n * Provided by `bf-stack-outlet` for each stacked page: the page's outermost `bf-page` registers\n * its `BfPageContext` here (a nested `bf-page` finds the slot taken and leaves it), so the outlet\n * can reach the page's scroller, e.g. to scroll it to the top when its tab is re-selected.\n * Internal to the mobile entry.\n */\nexport class BfPageContextSlot {\n context: BfPageContext | null = null;\n}\n","import { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What `bf-tabs` tells the `bf-stack-outlet` inside it: which tab a URL belongs to. The outlet\n * keeps one stack per tab key and switches stacks when a navigation lands on another key.\n *\n * @since 0.2.0\n * @summary What the tabs page tells its stack outlet: which tab a URL belongs to\n */\nexport interface BfTabContext {\n /** The active tab's key (the URL segment after the tabs route), or null outside the tabs. */\n readonly tabKey: Signal<string | null>;\n /**\n * The tab key of `url` (query and fragment ignored), or null when it is not under the tabs route.\n * @param url The URL to read.\n */\n keyOf(url: string): string | null;\n}\n\n/**\n * Provided by `bf-tabs`; injected optionally by `bf-stack-outlet`.\n *\n * @summary Injection token through which the tabs page tells its outlet about the tabs\n * @since 0.2.0\n */\nexport const BF_TAB_CONTEXT = new InjectionToken<BfTabContext>('BF_TAB_CONTEXT');\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n Injector,\n ViewContainerRef,\n afterNextRender,\n booleanAttribute,\n computed,\n inject,\n input,\n output,\n reflectComponentType,\n signal,\n type AfterViewInit,\n type ComponentRef,\n type EnvironmentInjector,\n type OnDestroy,\n type OnInit,\n type Signal,\n viewChild,\n} from '@angular/core';\nimport {\n ActivatedRoute,\n ChildrenOutletContexts,\n type Data,\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationStart,\n type OutletContext,\n PRIMARY_OUTLET,\n Router,\n type RouterOutletContract,\n} from '@angular/router';\nimport { Location } from '@angular/common';\nimport { type Subscription, combineLatest } from 'rxjs';\nimport { BfPlatformService, type BfShellMode } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS, resolveShellMode } from '../../shell/provide-bf-shell';\nimport { BfNavController } from '../nav-controller.service';\nimport { BfOverlayStack } from '@bitforgehq/ui/overlay';\nimport {\n type BfStackTransition,\n type BfTransitionKind,\n createTransition,\n mdTransition,\n prefersReducedMotion,\n reducedTransition,\n settleDuration,\n settlePop,\n shouldCompletePop,\n} from '../transitions';\nimport { type BfViewEvent, BfViewLifecycle } from '../view-lifecycle';\nimport { BfPageContextSlot } from '../page/page-context';\nimport { BF_TAB_CONTEXT } from '../tabs/tab-context';\n\n/**\n * Emitted by `bf-stack-outlet` after every change of its stack.\n *\n * @summary What a stack outlet reports after its stack changed: the depth and the tab\n * @since 0.2.0\n */\nexport interface BfStackChange {\n /** Number of pages in the active stack. */\n depth: number;\n /** Key of the active tab's stack inside `bf-tabs`; null for an outlet outside `bf-tabs`. */\n tabKey: string | null;\n}\n\n/**\n * Emitted by `bf-stack-outlet` around a transition between two pages.\n *\n * @summary What a stack outlet reports around a page transition: its kind and animation\n * @since 0.2.0\n */\nexport interface BfStackTransitionEvent {\n /** `push` for a new page (also a `replaceUrl` navigation), `pop` for going back. */\n kind: BfTransitionKind;\n /** False when the pages switched without an animation (`animated=false`). */\n animated: boolean;\n}\n\n/**\n * Provided to every page of a stack outlet: tells the stack outlets inside that page whether the\n * page is shown. A parked page (another page pushed above it, another tab's stack shown) makes\n * its nested outlets inactive, so `BfNavController` follows the outlet whose page is on top.\n */\nclass BfOutletHost {\n /** True while the page is not its outlet's active page (parked, or on its way out). */\n readonly parked = signal(false);\n /** True while the page is shown: not parked, and its own outlet is active. */\n readonly shown: Signal<boolean>;\n\n constructor(outletActive: Signal<boolean>) {\n this.shown = computed(() => !this.parked() && outletActive());\n }\n}\n\ninterface StackEntry {\n /** The page component; destroyed while the entry is `dormant`, and replaced when it comes back. */\n ref: ComponentRef<unknown>;\n /** What the stack outlets inside the page read to know whether it is shown. */\n host: BfOutletHost;\n /** The positioned wrapper the page host lives in; animated, parked and focused. */\n frame: HTMLElement;\n lifecycle: BfViewLifecycle;\n /** Where the page's outermost `bf-page` registers its `BfPageContext`. */\n pageSlot: BfPageContextSlot;\n /**\n * True while the page is beyond `maxLivePages`: its component is destroyed and its frame\n * removed, and only this record (route, key, history position, URL, scroll) is kept.\n */\n dormant: boolean;\n /** The page scroller's `scrollTop` when it went dormant, restored when it is created again. */\n scrollTop: number;\n route: ActivatedRoute;\n /** Outlet-level URL key: path of this route from the root, without child segments or query. */\n key: string;\n /** Router navigation id that created the history entry of this page. */\n navigationId: number;\n /**\n * History position (`ɵrouterPageId`) of the entry this page was pushed onto, or null when\n * unknown (no `computed` resolution, a `skipLocationChange` push).\n */\n pageId: number | null;\n /** Nested outlet contexts saved on deactivate, restored on re-activation. */\n childContexts: Map<string, OutletContext> | null;\n focusReturn: HTMLElement | null;\n inputSub: Subscription | null;\n /** Router URL (with query and fragment) of the last navigation that ended on this page. */\n url: string;\n}\n\ntype Classification =\n | { kind: 'push' | 'replace' | 'root' }\n | { kind: 'pop'; index: number }\n | { kind: 'switch'; tab: string | null; fresh: boolean };\n\ntype FocusMove = 'title' | 'restore' | 'none';\n\ninterface Gesture {\n pointerId: number;\n startX: number;\n startY: number;\n started: boolean;\n top: StackEntry;\n under: StackEntry;\n width: number;\n samples: { x: number; t: number }[];\n transition: BfStackTransition | null;\n}\n\n/**\n * The history position the router writes into every history entry's state as `ɵrouterPageId`\n * when `canceledNavigationResolution` is `'computed'` (which `withBfShellRouting()` sets). It\n * mirrors the private `HistoryStateManager` of Angular 22.1: a push increments it, a\n * `replaceUrl` keeps it, and a traversal restores the entry's value.\n */\nfunction pageIdOf(state: unknown): number | null {\n if (typeof state !== 'object' || state === null) {\n return null;\n }\n const value: unknown = (state as { ɵrouterPageId?: unknown }).ɵrouterPageId;\n return typeof value === 'number' ? value : null;\n}\n\n/**\n * Pointer events whose swipe-back an outlet already armed. Nested outlets see the same\n * `pointerdown` (it bubbles from the inner outlet to the outer one): the innermost outlet that can\n * pop claims it, so a swipe never pops two stacks at once.\n */\nconst armedEvents = new WeakSet<Event>();\n\n/** Width of the left-edge zone that arms the swipe-back. */\nconst EDGE = 50;\n/** Horizontal travel that claims the swipe-back (and vertical travel that aborts it). */\nconst CLAIM = 10;\n/** Duration of the slide-back after the router refused a swiped pop. */\nconst REFUSED_POP_MS = 300;\n/**\n * How long a completed swipe waits for its `Location.back()` to start a navigation. Without one\n * (no history entry, a `skipLocationChange` push) the page slides back as after a refused pop.\n */\nconst BACK_FALLBACK_MS = 600;\nconst PAGE_TITLE = '[data-bf-page-title]';\nconst FRAME_CLASS = 'bf-stack-page absolute inset-0 bg-bg outline-hidden';\n\n/**\n * A router outlet that keeps a stack of live pages: a push slides the new page in and parks the\n * previous one (still in the DOM, so scroll and state survive), a pop slides the top page out and\n * destroys it, and on iOS a finger's edge swipe scrubs the pop (touch only: a mouse or pen drag\n * never arms it). `provideBfShell({ maxLivePages })` bounds the live pages of each stack: the\n * deepest ones are destroyed and created again, scroll restored, on a pop back to them. Replaces\n * `<router-outlet>` in a mobile app.\n * Pages receive route params, query params and route data as inputs, bound by the outlet itself.\n * Inside `bf-tabs` it keeps one stack per tab and switches between them without a transition.\n * Outlets nest: an outlet inside a page (the tabs page) keeps its stacks while an outer outlet\n * parks that page, and `BfNavController` follows the innermost outlet whose page is shown.\n *\n * @example\n * <bf-stack-outlet />\n * @summary Router outlet keeping a live page stack with slide transitions and iOS swipe back\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-stack-outlet',\n host: { class: 'bf-stack' },\n template: `<div\n #dim\n class=\"bg-scrim/15 pointer-events-none absolute inset-0 z-(--bf-z-dim) opacity-0\"\n aria-hidden=\"true\"\n ></div>\n <ng-container #anchor />`,\n styles: `\n :host {\n position: relative;\n display: block;\n height: 100%;\n overflow: hidden;\n overscroll-behavior-x: none;\n /* Keeps horizontal drags as pointer events for the edge swipe-back where the host itself is\n a touch's nearest scroll container (the header, other unscrolled parts of a page). It is\n not inherited, and a touch only intersects the values up to its nearest scroll container,\n so a page's own scrollers (bf-content, an overflow-x-auto row) keep their panning. */\n touch-action: pan-y;\n contain: strict;\n /* Modal depth (iOS card stack): per open modal the page shrinks by 8 % and drops 8 px,\n and rounds to the tile radius. */\n transform: scale(calc(1 - var(--bf-modal-depth, 0) * 0.08))\n translateY(calc(var(--bf-modal-depth, 0) * 8px));\n border-radius: calc(var(--bf-modal-depth, 0) * var(--radius-tile));\n transition:\n transform var(--bf-duration-overlay) var(--ease-ios),\n border-radius var(--bf-duration-overlay);\n transform-origin: top center;\n }\n @media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n }\n `,\n})\nexport class BfStackOutletComponent\n implements RouterOutletContract, OnInit, AfterViewInit, OnDestroy\n{\n /** Outlet name, as on `router-outlet`. */\n readonly name = input(PRIMARY_OUTLET);\n /** Enables the iOS edge swipe-back for this outlet (ignored in Material mode). */\n readonly swipeGesture = input(true, { transform: booleanAttribute });\n /** Animates push and pop; false switches pages instantly (lifecycle events still fire). */\n readonly animated = input(true, { transform: booleanAttribute });\n\n /** Emitted with the new depth after every change of the stack. */\n readonly stackChanged = output<BfStackChange>();\n /** Emitted when a transition between two pages starts (also the start of a swipe-back). */\n readonly transitionStarted = output<BfStackTransitionEvent>();\n /** Emitted when a transition between two pages has settled. */\n readonly transitionEnded = output<BfStackTransitionEvent>();\n /**\n * Emitted with the new depth after every change of the stack, together with `stackChanged`.\n * @deprecated 0.3.0 use stackChanged; removed in 0.4.0\n */\n readonly stackChange = output<BfStackChange>();\n /**\n * Emitted when a transition starts, together with `transitionStarted`.\n * @deprecated 0.3.0 use transitionStarted; removed in 0.4.0\n */\n readonly transitionStart = output<BfStackTransitionEvent>();\n /**\n * Emitted when a transition has settled, together with `transitionEnded`.\n * @deprecated 0.3.0 use transitionEnded; removed in 0.4.0\n */\n readonly transitionEnd = output<BfStackTransitionEvent>();\n\n readonly supportsBindingToComponentInputs = true as const;\n\n private readonly parentContexts = inject(ChildrenOutletContexts);\n private readonly router = inject(Router);\n private readonly location = inject(Location);\n private readonly nav = inject(BfNavController);\n private readonly overlays = inject(BfOverlayStack);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n private readonly platform = inject(BfPlatformService);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly tabContext = inject(BF_TAB_CONTEXT, { optional: true });\n /** The stack outlet around this one (this outlet is inside one of its pages), if any. */\n private readonly parentOutlet = inject(BfStackOutletComponent, {\n skipSelf: true,\n optional: true,\n });\n /** The page of the parent outlet this outlet lives in, if any. */\n private readonly outletHost = inject(BfOutletHost, { optional: true });\n /** Lifecycle of the page hosting this outlet (null for a root outlet); forwarded to our page. */\n private readonly hostLifecycle = inject(BfViewLifecycle, { optional: true });\n /** Removes the listener on `hostLifecycle`. */\n private hostLifecycleOff: (() => void) | null = null;\n /** Number of stack outlets around this one: 0 for the root outlet. */\n private readonly level: number = this.parentOutlet ? this.parentOutlet.level + 1 : 0;\n /** False while the page hosting this outlet is parked, so none of its pages is shown. */\n private readonly shown = computed(() => this.outletHost?.shown() ?? true);\n private readonly depthState = signal(0);\n private readonly anchor = viewChild.required('anchor', { read: ViewContainerRef });\n private readonly dim = viewChild.required<ElementRef<HTMLElement>>('dim');\n\n private readonly mode: BfShellMode = resolveShellMode(this.config, this.platform);\n private readonly injector = inject(Injector);\n /** Pages of one stack kept alive (`BfShellConfig.maxLivePages`): at least 1, else every page. */\n private readonly maxLivePages = livePagesOf(this.config.maxLivePages);\n /** One stack per tab key inside `bf-tabs`; a single stack under the `null` key otherwise. */\n private readonly stacks = new Map<string | null, StackEntry[]>();\n /** Key of the stack that is shown; always null outside `bf-tabs`. */\n private activeTab: string | null = null;\n private active: StackEntry | null = null;\n private leaving: StackEntry | null = null;\n /**\n * The page deactivated while the host page was parked (an outer outlet pushed above it): kept,\n * with every stack, and resumed as it was when the host page comes back.\n */\n private suspended: StackEntry | null = null;\n private pending: { route: ActivatedRoute; env: EnvironmentInjector } | null = null;\n private viewReady = false;\n /** The running transition and the completion that must run if it gets interrupted. */\n private current: { transition: BfStackTransition; complete: () => void } | null = null;\n /** Final frame of a completed swipe, held until the router re-activates the page below. */\n private held: BfStackTransition | null = null;\n /** Set while the swipe gesture has already animated the pop the router now performs. */\n private gestureCommitted = false;\n private gesture: Gesture | null = null;\n private lastStart: NavigationStart | null = null;\n private destroyed = false;\n /** Pending rAF of the deferred transition start. */\n private startFrame: number | null = null;\n /** Fallback timer after a completed swipe's `Location.back()`. */\n private backFallback: ReturnType<typeof setTimeout> | null = null;\n /** Unregisters this outlet from `BfNavController`; primary outlets only. */\n private navUnregister: (() => void) | null = null;\n /** Unregisters the host as a page root of `BfOverlayStack`; outermost outlets only. */\n private rootUnregister: (() => void) | null = null;\n /** History position after the last completed navigation (see `pageIdOf`). */\n private settledPageId: number | null = null;\n /** Element that had focus in the top page when a swipe was claimed, restored on cancel. */\n private swipeFocus: HTMLElement | null = null;\n\n /** The stack that is shown (the active tab's stack inside `bf-tabs`). */\n private get stack(): StackEntry[] {\n let stack = this.stacks.get(this.activeTab);\n if (!stack) {\n stack = [];\n this.stacks.set(this.activeTab, stack);\n }\n return stack;\n }\n\n constructor() {\n // `router.events`, not the router's navigation signals: the stack pairs each navigation id's\n // NavigationStart (direction, popstate restore) with its own End/Cancel/Skipped, in order,\n // which a signal of the latest navigation coalesces.\n const routerSub = this.router.events.subscribe((e) => {\n if (e instanceof NavigationStart) {\n this.lastStart = e;\n // A navigation is under way; its end decides whether the swipe's pop landed.\n this.clearBackFallback();\n return;\n }\n if (e instanceof NavigationEnd || e instanceof NavigationSkipped) {\n this.lastStart = null;\n this.settledPageId = pageIdOf(this.location.getState());\n }\n if (e instanceof NavigationEnd && this.active) {\n this.active.url = e.urlAfterRedirects;\n }\n if (\n (e instanceof NavigationEnd ||\n e instanceof NavigationCancel ||\n e instanceof NavigationError ||\n e instanceof NavigationSkipped) &&\n this.gestureCommitted\n ) {\n // The swipe finished visually, but the navigation ended without popping this outlet (a\n // guard refused it, or it only moved within the top page): slide the page back.\n this.gestureCommitted = false;\n this.nav.clearHint();\n this.restoreAfterRefusedPop();\n }\n });\n inject(DestroyRef).onDestroy(() => routerSub.unsubscribe());\n }\n\n // ---- RouterOutletContract -------------------------------------------------------------------\n\n get isActivated(): boolean {\n return this.active !== null;\n }\n\n get component(): object {\n return this.requireActive().ref.instance as object;\n }\n\n get activatedRoute(): ActivatedRoute {\n return this.requireActive().route;\n }\n\n get activatedRouteData(): Data {\n return this.active?.route.snapshot.data ?? {};\n }\n\n get activatedComponentRef(): ComponentRef<unknown> | null {\n return this.active?.ref ?? null;\n }\n\n ngOnInit(): void {\n this.parentContexts.onChildOutletCreated(this.name(), this);\n const context = this.parentContexts.getContext(this.name());\n if (context?.route && !this.active) {\n this.activateWith(context.route, context.injector);\n }\n if (this.mode === 'ios' && this.config.swipeBack) {\n this.host.addEventListener('pointerdown', this.onPointerDown);\n }\n this.hostLifecycleOff = this.hostLifecycle?.on((e) => this.forwardHostEvent(e)) ?? null;\n // The page stack goes inert while a modal-like overlay is open. A nested outlet lives inside\n // an outer outlet's page, which the outer host already covers.\n if (!this.parentOutlet) {\n this.rootUnregister = this.overlays.registerRoot(this.host);\n }\n if (this.name() === PRIMARY_OUTLET) {\n this.navUnregister = this.nav.registerOutlet(\n {\n depth: this.depthState.asReadonly(),\n active: this.shown,\n popDelta: () => this.popDelta(),\n },\n this.level,\n );\n }\n }\n\n ngAfterViewInit(): void {\n this.viewReady = true;\n if (this.pending) {\n const { route, env } = this.pending;\n this.pending = null;\n this.activateWith(route, env);\n }\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n this.navUnregister?.();\n this.navUnregister = null;\n this.rootUnregister?.();\n this.rootUnregister = null;\n this.hostLifecycleOff?.();\n this.hostLifecycleOff = null;\n this.host.removeEventListener('pointerdown', this.onPointerDown);\n const gesture = this.gesture;\n if (gesture?.started) {\n gesture.transition?.animations.forEach((a) => a.cancel());\n try {\n this.host.releasePointerCapture(gesture.pointerId);\n } catch {\n // The pointer may already be gone.\n }\n }\n this.endGesture();\n // Drop everything that could still run after teardown: the running transition (without its\n // completion), a held swipe settle, a pending `Location.back()` and the deferred start.\n this.current?.transition.animations.forEach((a) => a.cancel());\n this.current = null;\n this.releaseHeld();\n this.gestureCommitted = false;\n this.clearBackFallback();\n if (this.startFrame !== null) {\n cancelAnimationFrame(this.startFrame);\n this.startFrame = null;\n }\n if (this.parentContexts.getContext(this.name())?.outlet === this) {\n this.parentContexts.onChildOutletDestroyed(this.name());\n }\n this.suspended = null;\n this.destroyAllStacks();\n this.depthState.set(0);\n }\n\n /**\n * The router tears down the active page. It is NOT destroyed here: it either stays under the\n * next page (push) or is removed after the pop animation (decided in `activateWith`).\n * @internal\n */\n deactivate(): void {\n const entry = this.active;\n if (!entry) {\n return;\n }\n this.setActive(null);\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n // The router calls `children.onOutletDeactivated()` right after this and discards the map,\n // so take it first; it holds the nested outlets of this page for re-activation.\n entry.childContexts = this.parentContexts\n .getOrCreateContext(this.name())\n .children.onOutletDeactivated();\n const focused = this.document.activeElement;\n entry.focusReturn =\n focused instanceof HTMLElement && entry.frame.contains(focused) ? focused : null;\n this.leaving = entry;\n // Nothing activated in this pass means the route left this outlet: clear the stack. The\n // router deactivates nested outlets before their parent, so this runs after the parent\n // outlet has decided what happens to the page hosting this outlet.\n queueMicrotask(() => {\n if (this.destroyed || this.active || this.leaving !== entry) {\n return;\n }\n if (!this.shown()) {\n // The host page is parked above (it comes back with this outlet as it was) or leaving\n // (its destruction tears this outlet down): keep every stack.\n this.suspended = entry;\n } else {\n this.interruptTransition();\n this.leaving = null;\n this.destroyAllStacks();\n this.emitDepth();\n }\n });\n }\n\n /**\n * The router shows a page: pushed, popped to, or a new root (the router outlet contract).\n * @internal\n */\n activateWith(activatedRoute: ActivatedRoute, environmentInjector: EnvironmentInjector): void {\n if (!this.viewReady) {\n this.pending = { route: activatedRoute, env: environmentInjector };\n return;\n }\n // Finish a running transition before touching the stack, so its completion (which parks its\n // leaving page) cannot undo what this activation does to the same pages.\n this.interruptTransition();\n const suspended = this.suspended;\n this.suspended = null;\n if (suspended && this.resume(suspended, activatedRoute)) {\n return;\n }\n const decision = this.classify(activatedRoute);\n const leaving = this.leaving;\n this.leaving = null;\n\n if (decision.kind === 'switch') {\n this.switchStack(decision.tab, decision.fresh, activatedRoute, environmentInjector, leaving);\n return;\n }\n\n if (decision.kind === 'pop') {\n const target = this.stack[decision.index];\n if (!target) {\n return;\n }\n this.bringBack(target, activatedRoute, environmentInjector);\n const removed = this.stack.splice(decision.index + 1);\n this.emitDepth();\n this.runTransition('pop', target, leaving, removed, 'restore');\n return;\n }\n\n const entry = this.createEntry(activatedRoute, environmentInjector);\n if (decision.kind === 'root') {\n const removed = this.stack.splice(0);\n this.stack.push(entry);\n this.setActive(entry);\n this.emitDepth();\n this.runTransition(null, entry, leaving, removed, leaving ? 'title' : 'none');\n return;\n }\n if (decision.kind === 'push' && entry.pageId !== null && entry.pageId === leaving?.pageId) {\n // A push that created no history entry (`skipLocationChange`): its position is unknown.\n entry.pageId = null;\n }\n const removed =\n decision.kind === 'replace' && leaving\n ? this.stack.splice(this.stack.indexOf(leaving), 1)\n : [];\n this.stack.push(entry);\n this.setActive(entry);\n this.emitDepth();\n this.runTransition(\n leaving ? 'push' : null,\n entry,\n leaving,\n removed,\n leaving ? 'title' : 'none',\n );\n }\n\n /**\n * Only called by a store/retrieve `RouteReuseStrategy`; the stack itself never needs one.\n * @internal\n */\n detach(): ComponentRef<unknown> {\n const entry = this.requireActive();\n this.stack.splice(this.stack.indexOf(entry), 1);\n this.setActive(null);\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n this.anchor().detach(this.anchor().indexOf(entry.ref.hostView));\n entry.frame.remove();\n this.emitDepth();\n return entry.ref;\n }\n\n /**\n * Re-attaches a page a store/retrieve `RouteReuseStrategy` kept (the router outlet contract).\n * @internal\n */\n attach(ref: ComponentRef<unknown>, activatedRoute: ActivatedRoute): void {\n this.interruptTransition();\n this.leaving = null;\n this.anchor().insert(ref.hostView);\n const entry = this.wrap(\n ref,\n activatedRoute,\n new BfViewLifecycle(),\n new BfPageContextSlot(),\n new BfOutletHost(this.shown),\n );\n this.stack.push(entry);\n this.setActive(entry);\n this.bindInputs(entry);\n this.settleLayers();\n this.emitDepth();\n this.trimStack();\n }\n\n // ---- tabs -----------------------------------------------------------------------------------\n\n /**\n * The URL of the top page of a tab's stack (inside `bf-tabs`), or null when that tab has no\n * stack yet. `bf-tabs` navigates there on a tab switch, so the tab comes back as it was left.\n * @param tabKey The key of the tab, the URL segment after `tabsPath`.\n */\n lastUrlOf(tabKey: string): string | null {\n return this.stacks.get(tabKey)?.at(-1)?.url ?? null;\n }\n\n /**\n * Pops the active stack back to its first page in one history step (`Location.historyGo(-n)`\n * to the entry before the second page was pushed), keeping that page alive. Returns false, and\n * does nothing, when the stack has one page or the history position is unknown.\n */\n popToRoot(): boolean {\n const second = this.stack[1]?.pageId ?? null;\n const current = pageIdOf(this.location.getState());\n if (second === null || current === null) {\n return false;\n }\n const delta = second - 1 - current;\n if (delta >= 0) {\n return false;\n }\n this.location.historyGo(delta);\n return true;\n }\n\n /**\n * Scrolls the active page's `bf-content` back to the top (a re-selected tab).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollActiveContentToTop(behavior: ScrollBehavior = 'smooth'): void {\n this.active?.pageSlot.context?.scroller()?.scrollTo({ top: 0, behavior });\n }\n\n /**\n * A navigation onto another tab's key: parks the whole current stack and shows the target\n * stack (its page for this URL, the pages above it dropped) or starts it with a new page. No\n * transition and no gesture; `fresh` (a `root` navigation) discards the target stack first.\n */\n private switchStack(\n tab: string | null,\n fresh: boolean,\n route: ActivatedRoute,\n env: EnvironmentInjector,\n leaving: StackEntry | null,\n ): void {\n const from = this.stack;\n if (fresh) {\n for (const e of this.stacks.get(tab)?.splice(0) ?? []) {\n this.destroyEntry(e);\n }\n }\n this.activeTab = tab;\n const target = this.stack;\n const key = keyOf(route);\n const index = lastIndexBelow(target, target.length, (e) => e.key === key);\n let entering: StackEntry;\n let removed: StackEntry[] = [];\n const existing = target[index];\n if (existing) {\n this.bringBack(existing, route, env);\n removed = target.splice(index + 1);\n entering = existing;\n } else {\n entering = this.createEntry(route, env);\n target.push(entering);\n this.setActive(entering);\n }\n this.emitDepth();\n this.runTransition(null, entering, leaving, removed, 'title');\n for (const e of from) {\n park(e.frame);\n }\n }\n\n /** Shows a page of the stack again: a live one as it was, a dormant one created anew. */\n private bringBack(target: StackEntry, route: ActivatedRoute, env: EnvironmentInjector): void {\n if (target.dormant) {\n this.revive(target, route, env);\n } else {\n this.reactivate(target, route);\n }\n }\n\n /**\n * Creates a dormant page again from the route the router re-activated, in a new frame, keeping\n * its record (key, history position, URL); its scroller gets the `scrollTop` it had back after\n * the first render. Its nested outlets start empty (\"deep pages come back fresh\").\n */\n private revive(target: StackEntry, route: ActivatedRoute, env: EnvironmentInjector): void {\n const fresh = this.createEntry(route, env);\n fresh.inputSub?.unsubscribe();\n target.ref = fresh.ref;\n target.frame = fresh.frame;\n target.host = fresh.host;\n target.lifecycle = fresh.lifecycle;\n target.pageSlot = fresh.pageSlot;\n target.route = route;\n target.key = keyOf(route);\n target.dormant = false;\n target.childContexts = null;\n target.focusReturn = null;\n this.setActive(target);\n this.bindInputs(target);\n const scrollTop = target.scrollTop;\n target.scrollTop = 0;\n if (scrollTop > 0) {\n const ref = target.ref;\n // `read`: after `bf-content` has registered its scroller (in the default phase) in the\n // render that follows the page's first change detection.\n afterNextRender(\n {\n read: () => {\n const scroller = target.ref === ref ? target.pageSlot.context?.scroller() : null;\n if (scroller) {\n scroller.scrollTop = scrollTop;\n }\n },\n },\n { injector: this.injector },\n );\n }\n }\n\n /**\n * Keeps at most `maxLivePages` pages of the shown stack alive: the deepest ones beyond it go\n * dormant (component destroyed, frame removed, record kept). The top pages stay alive.\n */\n private trimStack(): void {\n const stack = this.stack;\n const excess = stack.length - this.maxLivePages;\n for (let i = 0; i < excess; i++) {\n const entry = stack[i];\n if (entry && !entry.dormant && entry !== this.active && entry !== this.leaving) {\n this.makeDormant(entry);\n }\n }\n }\n\n private makeDormant(entry: StackEntry): void {\n entry.scrollTop = entry.pageSlot.context?.scroller()?.scrollTop ?? 0;\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n // The nested outlets are destroyed with the page; their saved contexts are of no use.\n entry.childContexts = null;\n entry.focusReturn = null;\n entry.dormant = true;\n entry.ref.destroy();\n entry.frame.remove();\n }\n\n /** Shows a kept page again: new route, nested outlets re-attached, change detection back on. */\n private reactivate(target: StackEntry, route: ActivatedRoute): void {\n target.route = route;\n target.key = keyOf(route);\n const children = this.parentContexts.getOrCreateContext(this.name()).children;\n if (target.childContexts) {\n children.onOutletReAttached(target.childContexts);\n target.childContexts = null;\n }\n target.ref.changeDetectorRef.reattach();\n target.ref.changeDetectorRef.markForCheck();\n this.setActive(target);\n this.bindInputs(target);\n }\n\n private tabOf(route: ActivatedRoute): string | null {\n return this.tabContext?.keyOf(keyOf(route)) ?? null;\n }\n\n private destroyAllStacks(): void {\n for (const stack of this.stacks.values()) {\n for (const e of stack.splice(0)) {\n this.destroyEntry(e);\n }\n }\n this.stacks.clear();\n this.activeTab = null;\n }\n\n // ---- direction ------------------------------------------------------------------------------\n\n private classify(route: ActivatedRoute): Classification {\n const hint = this.nav.consumeHint();\n const start = this.lastStart;\n const key = keyOf(route);\n if (this.tabContext) {\n const tab = this.tabOf(route);\n if (this.stack.length === 0 && ![...this.stacks.values()].some((s) => s.length > 0)) {\n this.activeTab = tab;\n } else if (tab !== this.activeTab) {\n return { kind: 'switch', tab, fresh: hint === 'root' };\n }\n }\n if (this.stack.length === 0 || hint === 'root') {\n return { kind: 'root' };\n }\n const below = this.stack.length - 1; // entries [0, below) are under the leaving page\n if (start?.navigationTrigger === 'popstate' || hint === 'back') {\n const restoredPageId = pageIdOf(start?.restoredState);\n if (restoredPageId !== null && this.settledPageId !== null) {\n // History positions tell a back traversal from a forward one: forward always pushes,\n // back lands on the highest page pushed at or before the restored entry.\n if (restoredPageId > this.settledPageId) {\n return { kind: 'push' };\n }\n const byPosition = lastIndexBelow(\n this.stack,\n below,\n (e) => e.pageId !== null && e.pageId <= restoredPageId,\n );\n if (byPosition >= 0) {\n return { kind: 'pop', index: byPosition };\n }\n }\n const restored = start?.restoredState?.navigationId;\n let index = this.stack.findIndex((e, i) => i < below && e.navigationId === restored);\n if (index < 0) {\n // The page's URL may have changed through a nested child route since it was pushed.\n index = lastIndexBelow(this.stack, below, (e) => e.key === key);\n }\n // A popstate that matches nothing (browser forward, a destroyed entry) is a push.\n return index >= 0 ? { kind: 'pop', index } : { kind: 'push' };\n }\n if (this.router.currentNavigation()?.extras.replaceUrl) {\n return { kind: 'replace' };\n }\n return { kind: 'push' };\n }\n\n // ---- entries --------------------------------------------------------------------------------\n\n private createEntry(route: ActivatedRoute, env: EnvironmentInjector): StackEntry {\n const component = route.snapshot.component;\n if (!component) {\n throw new Error('bf-stack-outlet: the activated route has no component');\n }\n const lifecycle = new BfViewLifecycle();\n const pageSlot = new BfPageContextSlot();\n const host = new BfOutletHost(this.shown);\n const anchor = this.anchor();\n const injector = Injector.create({\n providers: [\n { provide: ActivatedRoute, useValue: route },\n {\n provide: ChildrenOutletContexts,\n useValue: this.parentContexts.getOrCreateContext(this.name()).children,\n },\n { provide: BfViewLifecycle, useValue: lifecycle },\n { provide: BfPageContextSlot, useValue: pageSlot },\n { provide: BfOutletHost, useValue: host },\n ],\n parent: anchor.injector,\n });\n const ref = anchor.createComponent(component, {\n index: anchor.length,\n injector,\n environmentInjector: env,\n });\n const entry = this.wrap(ref, route, lifecycle, pageSlot, host);\n this.bindInputs(entry);\n return entry;\n }\n\n /**\n * Moves the page host into its own positioned frame. The host stays a view of the anchor's\n * `ViewContainerRef` (Angular removes a destroyed host from whatever parent it has); the frame\n * is what gets animated, parked and focused, so the page host stays free of outlet state.\n */\n private wrap(\n ref: ComponentRef<unknown>,\n route: ActivatedRoute,\n lifecycle: BfViewLifecycle,\n pageSlot: BfPageContextSlot,\n host: BfOutletHost,\n ): StackEntry {\n // The router writes the new history entry before it activates routes, so this is the entry\n // the page is pushed onto.\n const pageId = pageIdOf(this.location.getState());\n const element = ref.location.nativeElement as HTMLElement;\n const frame = this.document.createElement('div');\n frame.className = FRAME_CLASS;\n frame.tabIndex = -1;\n this.host.insertBefore(frame, element);\n frame.appendChild(element);\n return {\n ref,\n host,\n frame,\n lifecycle,\n pageSlot,\n dormant: false,\n scrollTop: 0,\n route,\n key: keyOf(route),\n navigationId: this.router.currentNavigation()?.id ?? this.lastStart?.id ?? 0,\n pageId,\n childContexts: null,\n focusReturn: null,\n inputSub: null,\n // The router moves its current URL before it activates routes; NavigationEnd refines it.\n url: this.router.url,\n };\n }\n\n /**\n * The outlet's own component-input binder: the router's (`RoutedComponentInputBinder` behind\n * the private `INPUT_BINDER` token) only serves `RouterOutlet`. Mirrors its Angular 22.1\n * behaviour with the default options, whether or not `withComponentInputBinding()` is present:\n * query params, then path params, then route data (including resolved data), each input set\n * (or cleared with `undefined`) on every emission. Not supported: the feature's\n * `queryParams: false` and `unmatchedInputBehavior` options and the experimental route\n * `resources`.\n */\n private bindInputs(entry: StackEntry): void {\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n const component = entry.route.component;\n const mirror = component ? reflectComponentType(component) : null;\n if (!mirror) {\n return;\n }\n const route = entry.route;\n entry.inputSub = combineLatest([route.queryParams, route.params, route.data]).subscribe(\n ([query, params, data]) => {\n const all: Record<string, unknown> = { ...query, ...params, ...data };\n for (const { templateName } of mirror.inputs) {\n entry.ref.setInput(templateName, all[templateName]);\n }\n },\n );\n }\n\n private destroyEntry(entry: StackEntry): void {\n entry.inputSub?.unsubscribe();\n entry.inputSub = null;\n if (!entry.dormant) {\n entry.ref.destroy();\n }\n entry.frame.remove();\n }\n\n private emitTransitionStart(event: BfStackTransitionEvent): void {\n this.transitionStarted.emit(event);\n this.transitionStart.emit(event);\n }\n\n private emitTransitionEnd(event: BfStackTransitionEvent): void {\n this.transitionEnded.emit(event);\n this.transitionEnd.emit(event);\n }\n\n private emitDepth(): void {\n const depth = this.stack.length;\n this.depthState.set(depth);\n const change: BfStackChange = { depth, tabKey: this.activeTab };\n this.stackChanged.emit(change);\n this.stackChange.emit(change);\n }\n\n /** Makes `entry` the active page; the page it replaces is parked for its nested outlets. */\n private setActive(entry: StackEntry | null): void {\n if (this.active && this.active !== entry) {\n this.active.host.parked.set(true);\n }\n entry?.host.parked.set(false);\n this.active = entry;\n }\n\n /**\n * The host page is back (the outer outlet popped to it): shows the page that was active when\n * it was parked, as it was, when the router activates the same route key on the same stack.\n * Returns false for any other route, which then goes through the normal classification.\n */\n private resume(entry: StackEntry, route: ActivatedRoute): boolean {\n if (\n this.leaving !== entry ||\n keyOf(route) !== entry.key ||\n (this.tabContext !== null && this.tabOf(route) !== this.activeTab) ||\n !this.stack.includes(entry)\n ) {\n return false;\n }\n this.leaving = null;\n this.reactivate(entry, route);\n this.emitDepth();\n this.settleLayers();\n return true;\n }\n\n /**\n * The host page entered or left (the parent outlet parked or restored it): the page this outlet\n * shows follows it. Fires only on a real state change, so a page that just ran its own\n * transition (or its first activation) does not get an event twice.\n */\n private forwardHostEvent(event: BfViewEvent): void {\n const target = this.active ?? this.suspended ?? this.leaving;\n if (!target || this.current) {\n return;\n }\n const lifecycle = target.lifecycle;\n const state = lifecycle.state();\n if (event === 'willEnter' && (state === 'inactive' || state === 'leaving')) {\n lifecycle.fireWillEnter();\n } else if (event === 'didEnter' && state === 'entering') {\n lifecycle.fireDidEnter();\n } else if (event === 'willLeave' && (state === 'active' || state === 'entering')) {\n lifecycle.fireWillLeave();\n } else if (event === 'didLeave' && state === 'leaving') {\n lifecycle.fireDidLeave();\n }\n }\n\n private requireActive(): StackEntry {\n if (!this.active) {\n throw new Error('bf-stack-outlet: the outlet is not activated');\n }\n return this.active;\n }\n\n // ---- transitions ----------------------------------------------------------------------------\n\n /**\n * Layers the two pages, fires the lifecycle, animates (unless `kind` is null, `animated` is\n * off or a swipe already animated the pop), then destroys `removed`, parks `leaving` when it\n * stays in the stack and moves focus.\n */\n private runTransition(\n kind: BfTransitionKind | null,\n entering: StackEntry,\n leaving: StackEntry | null,\n removed: StackEntry[],\n focus: FocusMove,\n ): void {\n const alreadyAnimated = kind === 'pop' && this.gestureCommitted;\n this.gestureCommitted = false;\n this.clearBackFallback();\n const animate = kind !== null && leaving !== null && !alreadyAnimated && this.animated();\n const event: BfStackTransitionEvent | null =\n kind && leaving ? { kind, animated: animate || alreadyAnimated } : null;\n\n this.layer(entering, leaving, kind);\n leaving?.lifecycle.fireWillLeave();\n entering.lifecycle.fireWillEnter();\n if (event && !alreadyAnimated) {\n this.emitTransitionStart(event);\n }\n\n let transition: BfStackTransition | null = null;\n const complete = (): void => {\n this.current = null;\n transition?.animations.forEach((a) => a.cancel());\n this.releaseHeld();\n leaving?.lifecycle.fireDidLeave();\n for (const e of removed) {\n if (e !== entering) {\n this.destroyEntry(e);\n }\n }\n if (leaving && !removed.includes(leaving)) {\n // Render the page's final state (its didLeave) once, then stop checking it while parked.\n leaving.ref.changeDetectorRef.detectChanges();\n leaving.ref.changeDetectorRef.detach();\n }\n entering.lifecycle.fireDidEnter();\n this.settleLayers();\n this.trimStack();\n this.moveFocus(entering, focus);\n if (event) {\n this.emitTransitionEnd(event);\n }\n };\n\n if (!animate || !kind || !leaving) {\n complete();\n return;\n }\n const t = this.createAnimation(kind, entering, leaving);\n transition = t;\n this.current = { transition: t, complete };\n if (this.config.deferTransitionStart) {\n // Hold frame 0 while the entering page renders its first frame, then play: the render\n // cost lands before the motion instead of as a long frame inside it.\n t.animations.forEach((a) => a.pause());\n this.startFrame = requestAnimationFrame(() => {\n this.startFrame = requestAnimationFrame(() => {\n this.startFrame = null;\n if (this.current?.transition === t) {\n t.animations.forEach((a) => a.play());\n }\n });\n });\n }\n void t.finished.then(() => {\n if (!this.destroyed && this.current?.transition === t) {\n complete();\n }\n });\n }\n\n private createAnimation(\n kind: BfTransitionKind,\n entering: StackEntry,\n leaving: StackEntry,\n ): BfStackTransition {\n if (prefersReducedMotion(this.document.defaultView)) {\n return reducedTransition(entering.frame, leaving.frame);\n }\n if (this.mode === 'md') {\n return mdTransition(kind, entering.frame, leaving.frame);\n }\n return createTransition(\n kind,\n entering.frame,\n leaving.frame,\n this.dim().nativeElement,\n this.host.clientWidth,\n );\n }\n\n /** A new activation during a running transition finishes the old one at once. */\n private interruptTransition(): void {\n const current = this.current;\n if (current) {\n current.complete();\n }\n }\n\n private clearBackFallback(): void {\n if (this.backFallback !== null) {\n clearTimeout(this.backFallback);\n this.backFallback = null;\n }\n }\n\n private releaseHeld(): void {\n this.held?.animations.forEach((a) => a.cancel());\n this.held = null;\n }\n\n private moveFocus(entering: StackEntry, focus: FocusMove): void {\n if (focus === 'restore') {\n (entering.focusReturn ?? entering.frame).focus({ preventScroll: true });\n entering.focusReturn = null;\n } else if (focus === 'title') {\n const title = entering.frame.querySelector<HTMLElement>(PAGE_TITLE);\n if (title && !title.hasAttribute('tabindex')) {\n title.setAttribute('tabindex', '-1');\n }\n (title ?? entering.frame).focus({ preventScroll: true });\n }\n }\n\n /** During a transition: both pages visible, only the entering one interactive. */\n private layer(\n entering: StackEntry,\n leaving: StackEntry | null,\n kind: BfTransitionKind | null,\n ): void {\n const top = kind === 'pop' ? leaving : entering;\n const under = kind === 'pop' ? entering : leaving;\n for (const e of this.stack) {\n if (e !== entering && e !== leaving) {\n park(e.frame);\n }\n }\n unpark(entering.frame);\n if (leaving) {\n leaving.frame.style.visibility = 'visible';\n leaving.frame.setAttribute('inert', '');\n }\n if (under) {\n under.frame.style.zIndex = '1';\n }\n if (top) {\n top.frame.style.zIndex = '3';\n }\n }\n\n /** Final state: only the top page visible and interactive; the rest parked. */\n private settleLayers(): void {\n const top = this.stack[this.stack.length - 1];\n for (const e of this.stack) {\n if (e === top) {\n unpark(e.frame);\n e.frame.style.zIndex = '3';\n } else {\n park(e.frame);\n }\n }\n }\n\n // ---- swipe-back gesture (iOS mode) ----------------------------------------------------------\n\n /**\n * Non-passive `touchmove` guard, attached only while a swipe is armed (which happens only inside\n * the 50 px edge zone). Once the swipe is claimed, native scrolling must not start: a horizontal\n * scroller under the finger (any `overflow-x-auto` row, which pans on its own) would take over\n * the touch and cancel the pointer stream. Inside the edge zone the gesture wins; outside it the\n * guard is never attached, so such scrollers keep their own pan.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n if (this.gesture?.started && event.cancelable) {\n event.preventDefault();\n }\n };\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n // A finger only: a mouse drag or a text selection from the edge (desktop web with the mode\n // forced to `ios`) and a pen never pop the page.\n if (\n event.pointerType !== 'touch' ||\n !this.swipeGesture() ||\n this.stack.length < 2 ||\n !event.isPrimary ||\n armedEvents.has(event)\n ) {\n return;\n }\n const rect = this.host.getBoundingClientRect();\n // Only the left-edge zone arms the swipe; everywhere else the page and its horizontal\n // scrollers keep the touch.\n if (event.clientX - rect.left > EDGE) {\n return;\n }\n // This outlet can pop: claim the event, even while busy, so an outer outlet does not arm on a\n // page that is about to change.\n armedEvents.add(event);\n if (this.gesture || this.current || this.held) {\n return;\n }\n const top = this.stack[this.stack.length - 1];\n const under = this.stack[this.stack.length - 2];\n // A dormant page below (`maxLivePages: 1`) has nothing to show under the finger.\n if (!top || !under || top !== this.active || under.dormant) {\n return;\n }\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n started: false,\n top,\n under,\n width: rect.width,\n samples: [{ x: event.clientX, t: event.timeStamp }],\n transition: null,\n };\n this.host.addEventListener('pointermove', this.onPointerMove);\n this.host.addEventListener('pointerup', this.onPointerUp);\n this.host.addEventListener('pointercancel', this.onPointerCancel);\n this.host.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const g = this.gesture;\n if (g?.pointerId !== event.pointerId || !g) {\n return;\n }\n const dx = event.clientX - g.startX;\n const dy = event.clientY - g.startY;\n if (!g.started) {\n if (Math.abs(dy) > CLAIM && Math.abs(dy) > Math.abs(dx)) {\n this.endGesture(); // vertical: let the page scroll\n return;\n }\n if (dx < CLAIM || Math.abs(dx) <= Math.abs(dy)) {\n return;\n }\n g.started = true;\n const focused = this.document.activeElement;\n this.swipeFocus =\n focused instanceof HTMLElement && g.top.frame.contains(focused) ? focused : null;\n try {\n this.host.setPointerCapture(g.pointerId);\n } catch {\n // Synthetic pointers have no active pointer to capture.\n }\n this.layer(g.under, g.top, 'pop');\n g.transition = createTransition(\n 'pop',\n g.under.frame,\n g.top.frame,\n this.dim().nativeElement,\n g.width,\n 'linear',\n );\n g.transition.animations.forEach((a) => a.pause());\n this.emitTransitionStart({ kind: 'pop', animated: true });\n }\n g.samples.push({ x: event.clientX, t: event.timeStamp });\n if (g.samples.length > 6) {\n g.samples.shift();\n }\n const time = clamp(dx / g.width, 0, 1) * (g.transition?.duration ?? 0);\n g.transition?.animations.forEach((a) => (a.currentTime = time));\n };\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const g = this.gesture;\n if (g?.pointerId !== event.pointerId || !g) {\n return;\n }\n if (!g.started) {\n this.endGesture();\n return;\n }\n const delta = event.clientX - g.startX;\n const first = g.samples[0] ?? { x: g.startX, t: event.timeStamp };\n const velocity = (event.clientX - first.x) / Math.max(1, event.timeStamp - first.t); // px/ms\n const complete = shouldCompletePop(velocity, delta, g.width);\n const duration = settleDuration(complete ? g.width - delta : delta, velocity);\n g.transition?.animations.forEach((a) => a.cancel());\n const settle = settlePop(\n g.top.frame,\n g.under.frame,\n this.dim().nativeElement,\n g.width,\n clamp(delta / g.width, 0, 1),\n complete ? 1 : 0,\n duration,\n );\n this.endGesture();\n this.held = settle;\n void settle.finished.then(() => {\n if (this.destroyed || this.held !== settle) {\n return;\n }\n if (complete) {\n // Keep the final frame (top page off-screen) until the router re-activates `under`.\n this.gestureCommitted = true;\n this.nav.back();\n this.backFallback = setTimeout(() => {\n this.backFallback = null;\n if (this.gestureCommitted) {\n // `back()` produced no navigation: nothing will consume the hint or pop the page.\n this.gestureCommitted = false;\n this.nav.clearHint();\n this.restoreAfterRefusedPop();\n }\n }, BACK_FALLBACK_MS);\n } else {\n this.cancelSwipe();\n }\n });\n };\n\n private readonly onPointerCancel = (): void => {\n const g = this.gesture;\n if (g?.started) {\n g.transition?.animations.forEach((a) => a.cancel());\n this.cancelSwipe();\n }\n this.endGesture();\n };\n\n /** Settles a swipe that did not pop: the top page stays, the page under it is parked again. */\n private cancelSwipe(): void {\n this.releaseHeld();\n this.settleLayers();\n // The top page was inert while it was dragged, which dropped its focus: put it back.\n const top = this.active;\n const focus = this.swipeFocus;\n this.swipeFocus = null;\n if (top) {\n (focus ?? top.frame.querySelector<HTMLElement>(PAGE_TITLE) ?? top.frame).focus({\n preventScroll: true,\n });\n }\n this.emitTransitionEnd({ kind: 'pop', animated: true });\n }\n\n /**\n * The history step that pops the top page in one navigation: back to the entry before the page\n * was pushed, skipping the entries it added itself (query-only or nested child navigations).\n * Null when unknown, and `Location.back()` is used instead.\n */\n private popDelta(): number | null {\n const top = this.active;\n const current = pageIdOf(this.location.getState());\n if (!top || this.stack.length < 2 || top.pageId === null || current === null) {\n return null;\n }\n const delta = top.pageId - 1 - current;\n return delta < 0 ? delta : null;\n }\n\n private endGesture(): void {\n this.host.removeEventListener('pointermove', this.onPointerMove);\n this.host.removeEventListener('pointerup', this.onPointerUp);\n this.host.removeEventListener('pointercancel', this.onPointerCancel);\n this.host.removeEventListener('touchmove', this.touchMoveGuard);\n this.gesture = null;\n }\n\n /** The router refused a swiped pop (a guard): slide the top page back in over 300 ms. */\n private restoreAfterRefusedPop(): void {\n const top = this.active;\n const under = this.stack[this.stack.length - 2];\n const held = this.held;\n if (!top || !under) {\n this.releaseHeld();\n return;\n }\n const t = settlePop(\n top.frame,\n under.frame,\n this.dim().nativeElement,\n this.host.clientWidth,\n 1,\n 0,\n REFUSED_POP_MS,\n );\n held?.animations.forEach((a) => a.cancel());\n this.held = t;\n void t.finished.then(() => {\n if (!this.destroyed && this.held === t) {\n this.cancelSwipe();\n }\n });\n }\n}\n\n/** `BfShellConfig.maxLivePages` as a count: `Infinity` (or unset, or NaN) keeps every page. */\nfunction livePagesOf(value: number | undefined): number {\n if (value === undefined || Number.isNaN(value)) {\n return Infinity;\n }\n return Math.max(1, Math.floor(value));\n}\n\nfunction park(frame: HTMLElement): void {\n frame.style.visibility = 'hidden';\n frame.style.zIndex = '0';\n frame.setAttribute('inert', '');\n frame.setAttribute('aria-hidden', 'true');\n}\n\nfunction unpark(frame: HTMLElement): void {\n frame.style.visibility = 'visible';\n frame.removeAttribute('inert');\n frame.removeAttribute('aria-hidden');\n}\n\nfunction keyOf(route: ActivatedRoute): string {\n return (\n '/' +\n route.snapshot.pathFromRoot\n .flatMap((s) => s.url.map((u) => u.path))\n .filter((p) => p.length > 0)\n .join('/')\n );\n}\n\nfunction lastIndexBelow(\n stack: readonly StackEntry[],\n below: number,\n match: (entry: StackEntry) => boolean,\n): number {\n for (let i = below - 1; i >= 0; i--) {\n const entry = stack[i];\n if (entry && match(entry)) {\n return i;\n }\n }\n return -1;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n","import { Component, DestroyRef, computed, inject, output } from '@angular/core';\nimport { BfViewLifecycle } from '../view-lifecycle';\nimport { BfPageContext, BfPageContextSlot } from './page-context';\n\n/**\n * The page frame of a mobile app: `bf-stack-outlet` positions one per stacked page and provides\n * its `BfViewLifecycle` through the element injector. Everything scrollable inside a page goes\n * into `bf-content`; `bf-page` is only the frame, the lifecycle signal and a per-page\n * `BfPageContext` (provided for its children, where `bf-content` registers its scroller). The\n * page an outlet shows makes its `bf-content` the document's `main` landmark. Used\n * standalone (outside an outlet, e.g. in a docs demo) it falls back to a local, inert lifecycle\n * instance instead of throwing, and exposes no `main` landmark.\n *\n * @example\n * <bf-page (bfViewDidEnter)=\"load()\">\n * <p>Page content</p>\n * </bf-page>\n * @summary Frame for one stacked mobile screen that supplies lifecycle events and shared page state\n * @category Navigation\n * @since 0.2.0\n * @slot default The page: a `bf-header` and a `bf-content`.\n */\n@Component({\n selector: 'bf-page',\n host: { class: 'absolute inset-0 flex flex-col bg-bg' },\n providers: [BfPageContext],\n template: `<ng-content />`,\n})\nexport class BfPageComponent {\n private readonly lifecycle = inject(BfViewLifecycle, { optional: true }) ?? new BfViewLifecycle();\n\n /** The page's current lifecycle state, mirrored from `BfViewLifecycle`. */\n readonly viewState = computed(() => this.lifecycle.state());\n\n /** Emitted when the page starts entering the top of the stack. */\n readonly bfViewWillEnter = output<void>();\n /** Emitted once the page is active at the top of the stack. */\n readonly bfViewDidEnter = output<void>();\n /** Emitted when the page starts leaving the top of the stack. */\n readonly bfViewWillLeave = output<void>();\n /** Emitted once the page has left the top of the stack. */\n readonly bfViewDidLeave = output<void>();\n\n constructor() {\n // Forward synchronously: the outlet fires `willEnter` (and, without an animation, `didEnter`)\n // before the page's first change detection and `didLeave` right before a popped page is\n // destroyed, so a signal effect would miss them.\n const off = this.lifecycle.on((event) => {\n switch (event) {\n case 'willEnter':\n this.bfViewWillEnter.emit();\n break;\n case 'didEnter':\n this.bfViewDidEnter.emit();\n break;\n case 'willLeave':\n this.bfViewWillLeave.emit();\n break;\n case 'didLeave':\n this.bfViewDidLeave.emit();\n break;\n }\n });\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(off);\n\n // Hand this page's context to the stack outlet (outermost `bf-page` only).\n const slot = inject(BfPageContextSlot, { optional: true });\n const context = inject(BfPageContext);\n if (slot?.context === null) {\n slot.context = context;\n destroyRef.onDestroy(() => {\n if (slot.context === context) {\n slot.context = null;\n }\n });\n } else {\n // Outside an outlet (a docs illustration) or nested in another page: not the `main` one.\n context.mainLandmark.set(false);\n }\n }\n}\n","import { Component, computed, inject, input } from '@angular/core';\nimport { BF_LABELS, BfIconButtonComponent } from '@bitforgehq/ui';\nimport { BfIconComponent } from '@bitforgehq/ui/icons';\nimport { ArrowLeft, ChevronLeft } from 'lucide';\nimport { type BfShellMode } from '../../platform/platform.service';\nimport { injectShellMode } from '../../shell/provide-bf-shell';\nimport { BfNavController } from '../nav-controller.service';\n\n/**\n * iOS: chevron + text as one 44 px tall control, in the primary text colour like a UIKit bar button.\n * It may shrink down to its 44 px hit area when the bar is short of space (large text sizes): the\n * text truncates, then disappears, before the chevron does, as UIKit drops the back title.\n */\nconst IOS_CLASSES =\n 'relative inline-flex h-11 min-w-hit max-w-48 shrink cursor-pointer items-center gap-0.5 rounded-field pe-1 text-primary-text select-none ' +\n 'hover:text-primary-strong focus-visible:outline-hidden focus-visible:ring-4 focus-visible:ring-ring';\n\n/**\n * Back button for a `bf-header` start slot (or anywhere else in a page): pops the shown stack,\n * or replaces it with `defaultHref` when there's nothing to pop back to, e.g. a deep link that\n * landed on this page directly. Hidden when neither applies. Its anatomy follows the shell mode:\n * iOS, a chevron with the `label` as text; Material, a bare arrow icon button named by `label`.\n *\n * @example\n * <bf-back-button defaultHref=\"/tabs/today\" />\n * @summary Header back button that pops the page stack or falls back to a default route\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-back-button',\n imports: [BfIconButtonComponent, BfIconComponent],\n // Shrinkable inside a shrinkable `start` slot, so the text gives way before anything clips.\n host: { class: 'inline-flex min-w-0 shrink', '[attr.data-bf-mode]': 'mode' },\n template: `\n @if (visible()) {\n @if (mode === 'ios') {\n <button type=\"button\" [class]=\"iosClasses\" (click)=\"go()\">\n <bf-icon [icon]=\"chevronLeft\" size=\"lg\" />\n <span data-bf-back-text class=\"text-heading min-w-0 truncate font-normal\">{{\n name()\n }}</span>\n </button>\n } @else {\n <bf-icon-button variant=\"text\" [label]=\"name()\" (clicked)=\"go()\">\n <bf-icon [icon]=\"arrowLeft\" size=\"lg\" />\n </bf-icon-button>\n }\n }\n `,\n})\nexport class BfBackButtonComponent {\n private readonly nav = inject(BfNavController);\n private readonly labels = inject(BF_LABELS);\n\n /** Destination `nav.root()` navigates to when there's no page to pop back to. */\n readonly defaultHref = input('');\n /**\n * The previous page's name: the text beside the chevron in iOS mode, the arrow button's\n * accessible name in Material mode. Empty falls back to `provideBfLabels()`'s `back` (\"Back\").\n */\n readonly label = input('');\n\n /** The shell mode (`provideBfShell({ mode })`, or the platform's for `'auto'`). */\n protected readonly mode: BfShellMode = injectShellMode();\n protected readonly iosClasses = IOS_CLASSES;\n protected readonly chevronLeft = ChevronLeft;\n protected readonly arrowLeft = ArrowLeft;\n\n /** The rendered label: `label`, else the `back` label. */\n readonly name = computed(() => this.label() || this.labels.back);\n\n /** Shown when the stack has a page to go back to, or a `defaultHref` is set. */\n readonly visible = computed(() => this.nav.canGoBack() || this.defaultHref() !== '');\n\n /** Goes back one page, or to `defaultHref` when the stack has none to go back to. */\n go(): void {\n if (this.nav.canGoBack()) {\n this.nav.back();\n } else {\n void this.nav.root(this.defaultHref());\n }\n }\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Sets `touch-action: pan-x pan-y` (and a `data-bf-horizontal-scroll` marker) on a horizontally\n * scrolling element inside a `bf-stack-outlet` page. Not needed: `touch-action` is not inherited,\n * and a touch's effective value is the intersection of the values from the touched element up to\n * its nearest scroll container only, so an `overflow-x-auto` row (a chips row, a carousel) pans\n * on its own under the outlet's `touch-action: pan-y`, on iOS and on Android. Only the 50 px\n * left-edge zone belongs to the swipe-back, with or without the directive.\n *\n * @example\n * <div bfHorizontalScroll class=\"flex gap-2 overflow-x-auto\">\n * <bf-chip>All</bf-chip>\n * <bf-chip>Open</bf-chip>\n * </div>\n * @deprecated 0.3.0 use a plain overflow-x-auto scroller, which pans on its own; removed in 0.4.0\n * @category Navigation\n * @since 0.2.0\n * @summary Deprecated touch-action opt-in for horizontal scrollers in stack pages, not needed\n */\n@Directive({\n selector: '[bfHorizontalScroll]',\n host: { class: 'touch-pan-x touch-pan-y', '[attr.data-bf-horizontal-scroll]': '\"\"' },\n})\nexport class BfHorizontalScrollDirective {}\n","import {\n DOCUMENT,\n DestroyRef,\n Directive,\n ElementRef,\n Injector,\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n effect,\n inject,\n input,\n untracked,\n} from '@angular/core';\nimport { BfKeyboardService, isTextEntry } from '../../platform/keyboard.service';\n\n/** What the padding write needs from its measurement. */\ninterface KeyboardPadding {\n /** Keyboard height the paddings answer to; 0 while hidden or the assist is off. */\n readonly height: number;\n /** Gap kept above the keyboard, in px. */\n readonly margin: number;\n /** Part of the host below the keyboard's top edge, in px. */\n readonly covered: number;\n /** Whether the host is rendered visibly. */\n readonly shown: boolean;\n}\n\n/** Wait after arming when no `scroll` event arrives (the engine did not scroll on its own). */\nconst REVEAL_SETTLE_MS = 120;\n/** Quiet time after the scroller's last `scroll` event before the reveal or correction runs. */\nconst REVEAL_QUIET_MS = 100;\n/** Longest wait after arming, however long the scroller keeps scrolling. */\nconst REVEAL_MAX_WAIT_MS = 400;\n\n/**\n * How `bfKeyboardAssist` scrolls the focused control into view: `smooth` or `instant`.\n *\n * @summary Whether the keyboard assist scrolls a focused field into view smoothly or at once\n * @since 0.3.0\n */\nexport type BfKeyboardAssistBehavior = 'smooth' | 'instant';\n\n/**\n * Keeps the focused text control of a scroll container clear of the soft keyboard. On keyboard\n * will-show it pads the scroller by the part below the keyboard's top edge\n * (`BfKeyboardService.keyboardTop()`: `padding-bottom`, plus `scroll-padding-bottom` with the\n * margin for engine scrolls), then scrolls the focused `input`, `textarea` or `[contenteditable]`\n * up until it sits `bfKeyboardAssistMargin` px above the keyboard (or down until it sits the margin\n * below the scroller's top edge; in a scroller too short for the control and both margins, the gap\n * above the control shrinks first, then the one above the keyboard, so a control shorter than the\n * scroller stays whole; a taller one keeps its bottom at the keyboard top). The covered part is\n * measured again at did-show and on every window or `visualViewport` resize, so a WebView that\n * resizes for the keyboard (Android) gets no padding and a padding never outlives the keyboard,\n * even on a detached page. A hidden host (a parked page, `visibility: hidden`) is never padded or\n * scrolled. The reveal waits for the engine's own focus scroll to settle (100 ms without a `scroll`\n * event, 120 ms if none arrives, at most 400 ms), then scrolls once; one correction follows at\n * did-show once the scroller is quiet (skipped while a reveal is pending). Focus moving to another\n * field while the keyboard is open re-arms the reveal, and both paddings go away on hide.\n * `refresh()` measures and corrects again for a container that moved on its own. `bf-content`\n * applies it to its own scroller; use it directly only on a custom scroll container.\n *\n * @example\n * <div class=\"h-full overflow-y-auto\" bfKeyboardAssist [bfKeyboardAssistMargin]=\"32\">\n * <bf-input label=\"Name\" />\n * </div>\n * @category Navigation\n * @since 0.2.0\n * @summary Keeps the focused text field of a scroll container clear of the soft keyboard\n */\n@Directive({\n selector: '[bfKeyboardAssist]',\n})\nexport class BfKeyboardAssistDirective {\n /** Enables the assist; set it false for screens that manage the keyboard themselves (a chat composer). */\n readonly bfKeyboardAssist = input(true, { transform: booleanAttribute });\n /** Gap kept between the focused control and the top of the keyboard, in px. */\n readonly bfKeyboardAssistMargin = input(24);\n /** Scroll behaviour of the reveal. */\n readonly bfKeyboardAssistBehavior = input<BfKeyboardAssistBehavior>('smooth');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly keyboard = inject(BfKeyboardService);\n private settleCallback: (() => void) | null = null;\n private quietTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n private revealPending = false;\n private lastScrollAt = Number.NEGATIVE_INFINITY;\n\n constructor() {\n const onScroll = (): void => {\n this.lastScrollAt = Date.now();\n if (this.settleCallback) {\n this.restartQuietTimer(REVEAL_QUIET_MS);\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n // Field-to-field focus while open fires no will-show; the engine's own reveal follows.\n if (\n this.bfKeyboardAssist() &&\n this.keyboard.visible() &&\n isTextEntry(event.target as Element | null)\n ) {\n this.scheduleReveal();\n }\n };\n // Every resize measures the covered part again, keyboard up or not: a padded scroller whose\n // view is detached (a parked page) runs no effect on hide, but still ends at 0 here.\n const onResize = (): void => this.updatePadding();\n const view = this.document.defaultView;\n this.host.addEventListener('scroll', onScroll, { passive: true });\n this.host.addEventListener('focusin', onFocusIn);\n view?.addEventListener('resize', onResize);\n view?.visualViewport?.addEventListener('resize', onResize);\n inject(DestroyRef).onDestroy(() => {\n this.cancelReveal();\n this.host.removeEventListener('scroll', onScroll);\n this.host.removeEventListener('focusin', onFocusIn);\n view?.removeEventListener('resize', onResize);\n view?.visualViewport?.removeEventListener('resize', onResize);\n });\n\n // Paddings follow the keyboard height and are measured again once it has settled (did-show,\n // after Android's resize); hiding (or disabling) removes them and any pending reveal. The\n // host's own styles: measured in the early read phase, written in the write phase.\n afterRenderEffect({\n earlyRead: () => {\n this.keyboard.phase();\n return this.measurePadding();\n },\n write: (padding) => this.applyPadding(padding()),\n });\n\n // Every will-show schedules the delayed reveal, after the paddings above are in place.\n let lastWillShow = untracked(() => this.keyboard.willShow());\n effect(() => {\n const count = this.keyboard.willShow();\n if (count === lastWillShow) {\n return;\n }\n lastWillShow = count;\n untracked(() => {\n if (this.bfKeyboardAssist() && this.keyboard.visible()) {\n this.scheduleReveal();\n }\n });\n });\n\n // One correction once the keyboard has settled, after any scroll in progress has ended; a\n // pending reveal measures the settled rects itself, so the correction is skipped then. It is\n // measured in the early read phase and scrolled in the write phase, after this render's\n // padding write (registered earlier, so its write runs first); it re-checks that the keyboard\n // is still shown before it scrolls, here and after a deferral.\n const injector = inject(Injector);\n effect(() => {\n if (this.keyboard.phase() !== 'shown') {\n return;\n }\n untracked(() =>\n afterNextRender(\n {\n earlyRead: () => this.settledCorrection(),\n write: (delta) => {\n if (this.keyboard.phase() === 'shown') {\n this.scrollReveal(delta, 'instant');\n }\n },\n },\n { injector },\n ),\n );\n });\n }\n\n /**\n * The did-show correction's measurement: the scroll it needs now, or 0 when it is skipped (a\n * reveal is pending, the assist is off, the keyboard is no longer shown) or deferred until the\n * scroller is quiet.\n */\n private settledCorrection(): number {\n if (this.keyboard.phase() !== 'shown' || !this.bfKeyboardAssist() || this.revealPending) {\n return 0;\n }\n const sinceScroll = Date.now() - this.lastScrollAt;\n if (sinceScroll < REVEAL_QUIET_MS) {\n this.waitForQuiet(REVEAL_QUIET_MS - sinceScroll, () => {\n if (this.keyboard.phase() === 'shown') {\n this.reveal('instant');\n }\n });\n return 0;\n }\n this.cancelReveal();\n return this.revealDelta();\n }\n\n /**\n * Measures the covered part again and, while the keyboard is up, corrects the reveal once\n * (instantly, after any scroll in progress). For a container that moves with the keyboard\n * outside the keyboard events — `bf-sheet` calls it once its lift above the keyboard has ended.\n */\n refresh(): void {\n this.updatePadding();\n if (this.bfKeyboardAssist() && this.keyboard.visible() && !this.revealPending) {\n this.whenScrollSettled(() => this.reveal('instant'));\n }\n }\n\n /**\n * False for a host that is not rendered visibly. A parked page is `visibility: hidden`, which\n * `checkVisibility()` ignores unless asked with `visibilityProperty`.\n */\n private hostVisible(): boolean {\n return this.host.checkVisibility?.({ visibilityProperty: true }) !== false;\n }\n\n /** Part of the host below the keyboard's top edge, in px; 0 while hidden or not applicable. */\n private covered(height: number): number {\n if (height <= 0 || !this.hostVisible()) {\n return 0;\n }\n const keyboardTop = this.keyboard.keyboardTop();\n return Math.max(0, Math.round(this.host.getBoundingClientRect().bottom - keyboardTop));\n }\n\n /** Measures and applies the paddings at once, outside change detection (resize, `refresh()`). */\n private updatePadding(): void {\n this.applyPadding(this.measurePadding());\n }\n\n /** The keyboard height the paddings answer to and the part of the host it covers. */\n private measurePadding(): KeyboardPadding {\n const visible = this.keyboard.visible() ? this.keyboard.height() : 0;\n const height = this.bfKeyboardAssist() ? visible : 0;\n return {\n height,\n margin: this.bfKeyboardAssistMargin(),\n covered: this.covered(height),\n shown: this.hostVisible(),\n };\n }\n\n private applyPadding({ height, margin, covered, shown }: KeyboardPadding): void {\n if (covered > 0) {\n this.host.style.setProperty('padding-bottom', `${covered}px`);\n this.host.style.setProperty('scroll-padding-bottom', `${covered + margin}px`);\n } else {\n this.host.style.removeProperty('padding-bottom');\n this.host.style.removeProperty('scroll-padding-bottom');\n }\n if (height <= 0 || !shown) {\n this.cancelReveal();\n }\n }\n\n /**\n * Arms a single-shot reveal: it runs once the scroller has been quiet for `REVEAL_QUIET_MS`\n * after its last `scroll` event, `REVEAL_SETTLE_MS` after arming if none arrives, and at most\n * `REVEAL_MAX_WAIT_MS` after arming.\n */\n private scheduleReveal(): void {\n this.waitForQuiet(REVEAL_SETTLE_MS, () => this.reveal(this.bfKeyboardAssistBehavior()));\n this.revealPending = true;\n }\n\n /** Runs `callback` now if the scroller is quiet, else once it has been quiet for `REVEAL_QUIET_MS`. */\n private whenScrollSettled(callback: () => void): void {\n const sinceScroll = Date.now() - this.lastScrollAt;\n if (sinceScroll >= REVEAL_QUIET_MS) {\n this.cancelReveal();\n callback();\n return;\n }\n this.waitForQuiet(REVEAL_QUIET_MS - sinceScroll, callback);\n }\n\n private waitForQuiet(initialWait: number, callback: () => void): void {\n this.cancelReveal();\n this.settleCallback = callback;\n this.restartQuietTimer(initialWait);\n this.maxWaitTimer = setTimeout(() => this.finishSettle(), REVEAL_MAX_WAIT_MS);\n }\n\n private restartQuietTimer(wait: number): void {\n if (this.quietTimer !== null) {\n clearTimeout(this.quietTimer);\n }\n this.quietTimer = setTimeout(() => this.finishSettle(), wait);\n }\n\n private finishSettle(): void {\n const callback = this.settleCallback;\n this.cancelReveal();\n callback?.();\n }\n\n /** Drops a pending reveal or correction and its timers. */\n private cancelReveal(): void {\n this.settleCallback = null;\n this.revealPending = false;\n if (this.quietTimer !== null) {\n clearTimeout(this.quietTimer);\n this.quietTimer = null;\n }\n if (this.maxWaitTimer !== null) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n\n /** Scrolls the focused text control so that its bottom plus the margin clears the keyboard top. */\n private reveal(behavior: 'smooth' | 'instant'): void {\n this.scrollReveal(this.revealDelta(), behavior);\n }\n\n /** How far the scroller must scroll for the focused text control to clear the keyboard; 0 when\n * nothing needs revealing. */\n private revealDelta(): number {\n const active = this.document.activeElement;\n if (\n !isTextEntry(active) ||\n !this.host.contains(active) ||\n !this.keyboard.visible() ||\n !this.hostVisible()\n ) {\n return 0;\n }\n const scroller = this.host.getBoundingClientRect();\n const keyboardTop = Math.min(scroller.bottom, this.keyboard.keyboardTop());\n const rect = active.getBoundingClientRect();\n const margin = this.bfKeyboardAssistMargin();\n // A scroller too short for the control and both margins (a keyboard-capped sheet or modal in\n // phone landscape) shrinks the gaps, the one above the control first, so a control shorter\n // than the scroller stays whole; the gap above the keyboard is the last to go.\n const room = Math.max(0, keyboardTop - scroller.top - rect.height);\n const gapBelow = Math.min(margin, room);\n const gapAbove = Math.min(margin, room - gapBelow);\n if (rect.bottom + gapBelow > keyboardTop) {\n return rect.bottom + gapBelow - keyboardTop;\n }\n if (rect.top - gapAbove < scroller.top) {\n return rect.top - gapAbove - scroller.top;\n }\n return 0;\n }\n\n private scrollReveal(delta: number, behavior: 'smooth' | 'instant'): void {\n if (delta === 0) {\n return;\n }\n if (behavior === 'smooth') {\n // Our own smooth scroll counts as scrolling, so a did-show correction waits for it.\n this.lastScrollAt = Date.now();\n }\n this.host.scrollBy({ top: delta, behavior });\n }\n}\n","import {\n DOCUMENT,\n Directive,\n ElementRef,\n booleanAttribute,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { DURATION_MD, SETTLE_EASING } from '../../motion';\nimport { prefersReducedMotion } from '../../navigation/transitions';\n\n/** Largest extra vertical scale of the stretched content (0.14 = 14 %). */\nconst STRETCH_MAX = 0.14;\n/** Pull distance (px) over which the stretch approaches its maximum (1 − 1/e of it at 240 px). */\nconst STRETCH_DISTANCE = 240;\n/** Finger travel (px) from the start before the gesture's axis is decided. */\nconst AXIS_SLOP = 8;\n\ninterface BfStretchGesture {\n /** `Touch.identifier` of the finger that started the gesture; other fingers are ignored. */\n readonly touchId: number;\n readonly startX: number;\n readonly startY: number;\n readonly topOwned: boolean;\n readonly body: HTMLElement;\n lastY: number;\n pull: number;\n axis: 'x' | 'y' | undefined;\n origin: 'top' | 'bottom';\n}\n\nfunction stretchScale(pull: number): number {\n return 1 + STRETCH_MAX * (1 - Math.exp(-pull / STRETCH_DISTANCE));\n}\n\n/** The touch with `identifier` in `list`, if that finger is in it. */\nfunction findTouch(list: TouchList, identifier: number): Touch | undefined {\n return Array.from(list).find((touch) => touch.identifier === identifier);\n}\n\nfunction clearStretch(body: HTMLElement): void {\n body.style.removeProperty('transform');\n body.style.removeProperty('transform-origin');\n body.style.removeProperty('will-change');\n}\n\n/**\n * Android-style edge stretch for a scroll container: a vertical touch drag past the top or bottom\n * edge scales the container's first element child (the content body) up along the y axis from\n * that edge — `scaleY(1 + 0.14 × (1 − e^(−pull / 240)))` — and springs it back on release (220 ms,\n * the settle curve; instant under `prefers-reduced-motion`). Touch only, with passive listeners:\n * it never prevents a touch, so scrolling is untouched. It follows the finger that started the\n * gesture and ignores any other. A horizontal-dominant gesture (decided after 8 px) is ignored,\n * and the top edge is left alone while an active `bf-refresher` (`[data-bf-refresher]`) inside\n * the container owns the top pull. `bf-content` applies it to its\n * scroller in Material mode, where the WebView draws no overscroll effect of its own.\n *\n * @example\n * <div class=\"h-full overflow-y-auto\" bfOverscrollStretch>\n * <div>…</div>\n * </div>\n * @category Navigation\n * @since 0.2.0\n * @summary Android-style stretch of a scroll container dragged past its top or bottom edge\n */\n@Directive({\n selector: '[bfOverscrollStretch]',\n})\nexport class BfOverscrollStretchDirective {\n /** Enables the stretch; false leaves the container inert. */\n readonly bfOverscrollStretch = input(true, { transform: booleanAttribute });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private gesture: BfStretchGesture | null = null;\n private settle: { readonly animation: Animation; readonly body: HTMLElement } | null = null;\n\n constructor() {\n effect((onCleanup) => {\n if (!this.bfOverscrollStretch()) {\n return;\n }\n const host = this.host;\n const options: AddEventListenerOptions = { passive: true };\n host.addEventListener('touchstart', this.onTouchStart, options);\n host.addEventListener('touchmove', this.onTouchMove, options);\n host.addEventListener('touchend', this.onTouchEnd, options);\n host.addEventListener('touchcancel', this.onTouchEnd, options);\n onCleanup(() => {\n host.removeEventListener('touchstart', this.onTouchStart);\n host.removeEventListener('touchmove', this.onTouchMove);\n host.removeEventListener('touchend', this.onTouchEnd);\n host.removeEventListener('touchcancel', this.onTouchEnd);\n this.stopSettle();\n if (this.gesture) {\n clearStretch(this.gesture.body);\n this.gesture = null;\n }\n });\n });\n }\n\n private readonly onTouchStart = (event: TouchEvent): void => {\n const touch = event.touches[0];\n const body = this.host.firstElementChild;\n if (this.gesture || !touch || !(body instanceof HTMLElement)) {\n return;\n }\n // A new touch takes over from a spring-back still running.\n this.stopSettle();\n this.gesture = {\n touchId: touch.identifier,\n startX: touch.clientX,\n startY: touch.clientY,\n topOwned: this.host.querySelector('[data-bf-refresher]') !== null,\n body,\n lastY: touch.clientY,\n pull: 0,\n axis: undefined,\n origin: 'top',\n };\n };\n\n private readonly onTouchMove = (event: TouchEvent): void => {\n const gesture = this.gesture;\n const touch = gesture ? findTouch(event.touches, gesture.touchId) : undefined;\n if (!gesture || !touch || gesture.axis === 'x') {\n return;\n }\n if (gesture.axis === undefined) {\n const dx = touch.clientX - gesture.startX;\n const dy = touch.clientY - gesture.startY;\n if (Math.hypot(dx, dy) < AXIS_SLOP) {\n return;\n }\n // Horizontal-dominant: a horizontal scroller's or a swipe's gesture, never a stretch.\n gesture.axis = Math.abs(dx) > Math.abs(dy) ? 'x' : 'y';\n if (gesture.axis === 'x') {\n return;\n }\n }\n const dy = touch.clientY - gesture.lastY;\n gesture.lastY = touch.clientY;\n if (dy === 0) {\n return;\n }\n const host = this.host;\n const atTop = host.scrollTop <= 0;\n const atBottom = host.scrollTop >= host.scrollHeight - host.clientHeight - 1;\n const previous = gesture.pull;\n const reversing = gesture.pull > 0 && (gesture.origin === 'top' ? dy < 0 : dy > 0);\n if (reversing) {\n gesture.pull = Math.max(0, gesture.pull - Math.abs(dy));\n } else if (atTop && dy > 0 && !gesture.topOwned) {\n gesture.pull += dy;\n gesture.origin = 'top';\n } else if (atBottom && dy < 0) {\n gesture.pull += -dy;\n gesture.origin = 'bottom';\n } else if (!atTop && !atBottom) {\n gesture.pull = 0;\n }\n if (previous > 0 || gesture.pull > 0) {\n this.apply(gesture);\n }\n };\n\n private readonly onTouchEnd = (event: TouchEvent): void => {\n const gesture = this.gesture;\n // The gesture ends when the finger that started it lifts, whatever other fingers stay down.\n if (!gesture || findTouch(event.touches, gesture.touchId)) {\n return;\n }\n this.gesture = null;\n const body = gesture.body;\n if (gesture.pull <= 0 || prefersReducedMotion(this.document.defaultView)) {\n clearStretch(body);\n return;\n }\n const animation = body.animate(\n [{ transform: `scaleY(${stretchScale(gesture.pull)})` }, { transform: 'scaleY(1)' }],\n { duration: DURATION_MD, easing: SETTLE_EASING, fill: 'forwards' },\n );\n this.settle = { animation, body };\n void animation.finished.then(\n () => {\n if (this.settle?.animation !== animation) {\n return;\n }\n this.settle = null;\n clearStretch(body);\n animation.cancel();\n },\n () => {\n // Cancelled by a newer touch, which reset the body itself.\n },\n );\n };\n\n private apply(gesture: BfStretchGesture): void {\n const style = gesture.body.style;\n style.willChange = 'transform';\n style.transformOrigin = `center ${gesture.origin}`;\n style.transform = `scaleY(${stretchScale(gesture.pull)})`;\n }\n\n private stopSettle(): void {\n const settle = this.settle;\n if (!settle) {\n return;\n }\n this.settle = null;\n settle.animation.cancel();\n clearStretch(settle.body);\n }\n}\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n afterNextRender,\n afterRenderEffect,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n type ElementRef,\n viewChild,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { BfPlatformService } from '../../platform/platform.service';\nimport { BfKeyboardAssistDirective } from '../keyboard-assist/keyboard-assist.directive';\nimport { BfOverscrollStretchDirective } from '../overscroll-stretch/overscroll-stretch.directive';\n\n/* The focus ring shows only when the scroller is a tab stop (a keyboard can scroll a page with\n nothing focusable in it), inset because the scroller fills the page. */\nconst SCROLLER_CLASSES =\n 'h-full overflow-y-auto overscroll-contain touch-pan-y pl-safe-left pr-safe-right pb-[calc(var(--bf-tab-bar-inset,0px)+var(--bf-header-collapse,0px))] focus-visible:ring-4 focus-visible:ring-ring focus-visible:ring-inset focus-visible:outline-hidden';\n/** What a keyboard can reach by Tab inside the scroller. */\nconst TABBABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(',');\n\n/**\n * True when `scroller` holds a control a keyboard can Tab to: one that is not `hidden`, not inside\n * an `inert` or `hidden` subtree and rendered (`checkVisibility()`, where the engine has it).\n */\nfunction hasTabbable(scroller: HTMLElement): boolean {\n for (const el of Array.from(scroller.querySelectorAll<HTMLElement>(TABBABLE))) {\n if (el.closest('[inert], [hidden]')) {\n continue;\n }\n if (\n typeof el.checkVisibility === 'function' &&\n !el.checkVisibility({ visibilityProperty: true })\n ) {\n continue;\n }\n return true;\n }\n return false;\n}\n\n/**\n * How far the content must overflow the scroller to count as scrolling: more than the 1 px the iOS\n * body always adds for the rubber band (`min-h-[calc(100%+1px)]`), with room for subpixel rounding.\n */\nconst OVERFLOW_PX = 2;\n/** The scroller's top: a header's height, the status bar (`fullscreen`), or nothing (`fullscreen`\n * inside a page-sheet modal, which already sits below the status bar). */\nconst TOP_CLASSES: Record<'header' | 'fullscreen' | 'fullscreenBelowStatusBar', string> = {\n header: 'pt-(--bf-header-height,0px) scroll-pt-(--bf-header-height,0px)',\n fullscreen: 'pt-safe-top',\n fullscreenBelowStatusBar: '',\n};\n/** iOS: the body always overflows the scroller by 1 px, so WebKit rubber-bands short pages too. */\nconst IOS_BODY_CLASSES = 'min-h-[calc(100%+1px)]';\n\n/**\n * True in iOS WebKit (Safari, a home-screen app, an in-app browser), including an iPad that reports\n * itself as a Mac: Capacitor reports platform `web` there, so the mode is Material, yet WebKit\n * rubber-bands the scroller natively. False without a `navigator` (SSR).\n */\nfunction isIosWebKit(navigator: Navigator | undefined): boolean {\n if (!navigator) {\n return false;\n }\n return (\n /iP(hone|ad|od)/.test(navigator.userAgent) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n );\n}\n\n/**\n * The payload of `bf-content`'s `scrolled` output: the scroller's `scrollTop` after the scroll.\n *\n * @summary What a content scroll event carries: the scroller's current top offset\n * @since 0.3.0\n */\nexport interface BfContentScrollEvent {\n /** The scroller's `scrollTop`, in px. */\n scrollTop: number;\n}\n\n/**\n * The page's single scroll container: fills the rest of `bf-page` under the header, keeps the\n * focused text control clear of the soft keyboard (`bfKeyboardAssist`), pads for the horizontal\n * safe areas, for the floating tab bar (`--bf-tab-bar-inset`), for a `translucent` header drawn\n * over it (`--bf-header-height`, from `BfPageContext`) and at the bottom for the height a solid\n * `bf-header` has collapsed by (`--bf-header-collapse`, written by the header, so the scroll range\n * stays the same while the large title collapses), and contains overscroll so the document\n * never scrolls. On iOS (`ios.scrollEnabled: false`) it is the only thing that scrolls. The\n * projected content sits in a block body inside the scroller: in iOS mode the body is 1 px taller\n * than the scroller, so WebKit rubber-bands even a short page; in Material mode the scroller gets\n * `bfOverscrollStretch`, the Android edge stretch the WebView does not draw itself, except in iOS\n * WebKit (Safari or a home-screen app, platform `web`), which rubber-bands natively. Both follow the\n * engine (`BfPlatformService.mode()`), not `provideBfShell({ mode })`. In the page a\n * `bf-stack-outlet` shows, the scroller is the document's `main` landmark (`role=\"main\"`; not in a\n * `bf-modal`, see `BfPageContext.mainLandmark`). While it overflows and holds nothing focusable,\n * it is a tab stop (`tabindex=\"0\"`), so a keyboard can scroll it in Safari too (axe\n * `scrollable-region-focusable`); otherwise Tab reaches its controls and the arrow keys scroll it.\n *\n * @example\n * <bf-page>\n * <bf-content>\n * <bf-list>…</bf-list>\n * </bf-content>\n * </bf-page>\n * @summary Page's single scroll container that keeps focused fields clear of the soft keyboard\n * @category Navigation\n * @since 0.2.0\n * @slot default The page content, in the scroll container.\n */\n@Component({\n selector: 'bf-content',\n imports: [BfKeyboardAssistDirective, BfOverscrollStretchDirective],\n host: { class: 'block min-h-0 flex-1' },\n template: `<div\n #scroller\n [class]=\"scrollerClasses()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"tabStop() ? 0 : null\"\n [bfKeyboardAssist]=\"keyboardAssist()\"\n [bfOverscrollStretch]=\"stretch()\"\n >\n <div [class]=\"bodyClasses()\"><ng-content /></div>\n </div>`,\n})\nexport class BfContentComponent {\n /** Extends the content under the status bar: adds the top safe-area padding (no header above). */\n readonly fullscreen = input(false, { transform: booleanAttribute });\n /** Keeps the focused text control clear of the soft keyboard; false for screens that handle it themselves. */\n readonly keyboardAssist = input(true, { transform: booleanAttribute });\n /** Emits `scrolled` on every scroll; off by default so plain pages add no listener. */\n readonly scrollEvents = input(false, { transform: booleanAttribute });\n\n /** The scroller's `scrollTop` on each scroll, only while `scrollEvents` is true. */\n readonly scrolled = output<BfContentScrollEvent>();\n\n private readonly scroller = viewChild.required<ElementRef<HTMLElement>>('scroller');\n private readonly pageContext = inject(BfPageContext, { optional: true });\n private readonly platform = inject(BfPlatformService);\n\n private readonly iosWebKit = isIosWebKit(inject(DOCUMENT).defaultView?.navigator);\n\n protected readonly ios = computed(() => this.platform.mode() === 'ios');\n /** The edge stretch only where nothing rubber-bands natively: never in iOS mode or iOS WebKit. */\n protected readonly stretch = computed(() => !this.ios() && !this.iosWebKit);\n protected readonly bodyClasses = computed(() => (this.ios() ? IOS_BODY_CLASSES : ''));\n\n /** The shown page's content is the document's `main` landmark; a modal's is not. */\n protected readonly role = computed(() => (this.pageContext?.mainLandmark() ? 'main' : null));\n /** True while the scroller overflows and holds nothing a keyboard can Tab to. */\n protected readonly tabStop = signal(false);\n\n /** A page-sheet modal already sits below the status bar (`BfPageContext.safeAreaTop`). */\n private readonly safeAreaTop = computed(() => this.pageContext?.safeAreaTop() ?? true);\n protected readonly scrollerClasses = computed(\n () =>\n `${SCROLLER_CLASSES} ${TOP_CLASSES[this.fullscreen() ? (this.safeAreaTop() ? 'fullscreen' : 'fullscreenBelowStatusBar') : 'header']}`,\n );\n\n constructor() {\n // A passive native listener, attached only when asked for, so scrolling never schedules\n // change detection on pages that do not listen.\n effect((onCleanup) => {\n if (!this.scrollEvents()) {\n return;\n }\n const element = this.scroller().nativeElement;\n const listener = (): void => this.scrolled.emit({ scrollTop: element.scrollTop });\n element.addEventListener('scroll', listener, { passive: true });\n onCleanup(() => element.removeEventListener('scroll', listener));\n });\n\n this.watchKeyboardReach();\n\n if (this.pageContext) {\n const context = this.pageContext;\n let registered: HTMLElement | null = null;\n // The scroller is a static element of this view, so it is registered once, after the first\n // render, for the page's header and the stack outlet to find.\n afterNextRender(() => {\n registered = this.scroller().nativeElement;\n context.scroller.set(registered);\n });\n // A translucent `bf-header` is drawn over the content: start the content below it. The\n // scroller is this component's own DOM, so it is written in the render phase.\n afterRenderEffect({\n write: () => {\n const element = this.scroller().nativeElement;\n const height = context.headerHeight();\n if (height > 0) {\n element.style.setProperty('--bf-header-height', `${height}px`);\n } else {\n element.style.removeProperty('--bf-header-height');\n }\n },\n });\n inject(DestroyRef).onDestroy(() => {\n if (registered && context.scroller() === registered) {\n context.scroller.set(null);\n }\n });\n }\n }\n\n /**\n * Keeps `tabStop` current: re-checked once per frame after the scroller or its content resizes\n * or the content changes (rows added, a control enabled, shown, hidden or made inert). Hidden,\n * inert and unrendered controls do not count.\n */\n private watchKeyboardReach(): void {\n let frame: number | null = null;\n let resize: ResizeObserver | null = null;\n let mutations: MutationObserver | null = null;\n const check = (): void => {\n frame = null;\n const element = this.scroller().nativeElement;\n this.tabStop.set(\n element.scrollHeight > element.clientHeight + OVERFLOW_PX && !hasTabbable(element),\n );\n };\n const schedule = (): void => {\n frame ??= requestAnimationFrame(check);\n };\n afterNextRender(() => {\n const element = this.scroller().nativeElement;\n check();\n if (typeof ResizeObserver === 'function') {\n resize = new ResizeObserver(schedule);\n resize.observe(element);\n const body = element.firstElementChild;\n if (body) {\n resize.observe(body);\n }\n }\n mutations = new MutationObserver(schedule);\n mutations.observe(element, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\n 'disabled',\n 'tabindex',\n 'href',\n 'contenteditable',\n 'type',\n 'hidden',\n 'inert',\n ],\n });\n });\n inject(DestroyRef).onDestroy(() => {\n resize?.disconnect();\n mutations?.disconnect();\n if (frame !== null) {\n cancelAnimationFrame(frame);\n }\n });\n }\n\n /** The scroll container element, for code that needs to read or drive it directly. */\n get scrollElement(): HTMLElement {\n return this.scroller().nativeElement;\n }\n\n /**\n * Scrolls back to the top (e.g. on a second tap of the active tab).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollToTop(behavior: ScrollBehavior = 'smooth'): void {\n this.scrollElement.scrollTo({ top: 0, behavior });\n }\n\n /**\n * Scrolls to the given offset in px.\n * @param x The horizontal offset (px).\n * @param y The vertical offset (px).\n * @param behavior `smooth` (the default) or `instant`.\n */\n scrollToPoint(x: number, y: number, behavior: ScrollBehavior = 'smooth'): void {\n this.scrollElement.scrollTo({ left: x, top: y, behavior });\n }\n}\n","import {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n afterRenderEffect,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { type BfShellMode } from '../../platform/platform.service';\nimport { injectShellMode } from '../../shell/provide-bf-shell';\n\n/**\n * Scroll offset (px) over which the large title collapses before it is measured (and the range a\n * header without one would use): the collapse runs over the large title block's rendered height.\n */\nconst COLLAPSE_FALLBACK = 56;\n/** The collapse range's bounds (px): at least the iOS bar's height, at most 200 px however large\n * the title grows with the text size. */\nconst COLLAPSE_MIN = 44;\nconst COLLAPSE_MAX = 200;\n/** Compact heights (phone landscape, split screen): no large title, and a 44 px bar in both modes. */\nconst COMPACT_QUERY = '(max-height: 500px)';\n/** Quiet time after the scroller's last `scroll` event that counts as scroll end (no `scrollend`). */\nconst SNAP_QUIET_MS = 120;\n/** The toolbar row's horizontal padding (`px-2`), the slots' distance from the bar's edges (px). */\nconst ROW_PADDING = 8;\n/** Space kept between the centred title and the wider slot (px). */\nconst TITLE_GAP = 8;\n/** The smallest the iOS `start` slot is capped to: one 44 px hit area (a back button's chevron). */\nconst START_MIN = 44;\n/**\n * The `start` slot per mode. iOS: capped by `--bf-header-start-max`, so a wide back button gives\n * way (its text truncates, then disappears) before the absolutely positioned centred title is\n * squeezed; Material: the row's flow already shrinks it.\n */\nconst START_SLOT_CLASSES: Record<BfShellMode, string> = {\n ios: 'flex min-w-0 max-w-(--bf-header-start-max,none) shrink items-center gap-1',\n md: 'flex min-w-0 shrink items-center gap-1',\n};\n\ntype BfHeaderLayout = 'solid' | 'translucent';\n/** In the page's column, or drawn over the content (`bf-content` pads its scroller by the header). */\nconst LAYOUT_CLASSES: Record<BfHeaderLayout, string> = {\n solid: 'relative block shrink-0',\n translucent: 'absolute inset-x-0 top-0 z-(--bf-z-sticky) block',\n};\n/**\n * The bar's surface at rest and once content scrolls under it (`data-bf-scrolled`): iOS turns\n * translucent with a blur and shows a 1 px hairline under the whole header (the host's `::after`,\n * so showing it never changes the header's height); Material takes a tonal tint, no blur and\n * never a hairline.\n */\nconst SURFACE_CLASSES: Record<BfShellMode, string> = {\n ios:\n 'bg-bg data-bf-scrolled:bg-bg/85 data-bf-scrolled:backdrop-blur-xl ' +\n \"after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-border after:opacity-0 after:content-[''] data-bf-scrolled:after:opacity-100\",\n md: 'bg-bg data-bf-scrolled:bg-surface-muted motion-safe:transition-colors motion-safe:duration-150',\n};\n/**\n * The persistent bar: at least 44 px in iOS mode, 64 px in Material mode and 44 px in both at\n * compact heights, and never shorter than its title (`--bf-header-title-height`, measured: the iOS\n * title box is absolutely positioned, so a wrapped title would not grow the bar by itself). It\n * grows with its content: larger text and taller slots.\n */\ntype BfHeaderBar = BfShellMode | 'compact';\nconst BAR_CLASSES: Record<BfHeaderBar, string> = {\n ios: 'min-h-[max(--spacing(11),var(--bf-header-title-height,0px))]',\n md: 'min-h-[max(--spacing(16),var(--bf-header-title-height,0px))]',\n compact: 'min-h-[max(--spacing(11),var(--bf-header-title-height,0px))]',\n};\n/**\n * The large title block's exit, inside the collapsing clip and driven by the scroll position (no\n * transition): it fades out (`1 − progress`) and rises 8 px (`progress × 8px`), so it reads as\n * sliding under the bar rather than being cut off. Reduced motion keeps the fade, drops the rise.\n */\nconst LARGE_BLOCK_CLASSES =\n 'bf-large px-gutter pb-2 opacity-[calc(1_-_var(--bf-header-progress,0))] motion-safe:-translate-y-[calc(var(--bf-header-progress,0)_*_8px)]';\n/** iOS centres the title on the bar; Material leads with it after the `start` slot. */\nconst TITLE_BOX_CLASSES: Record<BfShellMode, string> = {\n ios: 'pointer-events-none absolute inset-y-0 left-(--bf-header-title-inset,16px) right-(--bf-header-title-inset,16px) flex items-center justify-center',\n md: 'flex min-w-0 flex-1 items-center px-2',\n};\n/**\n * 18 px `text-heading`: semibold in iOS mode (17 pt), regular in Material mode (22 dp title). A\n * long title (or a large text size) wraps, anywhere in a long word, to at most two lines.\n */\nconst TITLE_TEXT_CLASSES: Record<BfShellMode, string> = {\n ios: 'text-heading min-w-0 line-clamp-2 text-center wrap-anywhere font-semibold',\n md: 'text-heading min-w-0 line-clamp-2 wrap-anywhere font-normal',\n};\n/**\n * The large title: `text-display` (40/44 px), its font and line height capped at 200 % (80/88 px),\n * so the largest iOS text sizes (the root scaled up to 235 %) still leave room for the page. In px,\n * since Dynamic Type scales the root and with it every rem.\n */\nconst LARGE_TITLE_CLASSES =\n 'text-display text-[length:min(var(--text-display),80px)] leading-[min(var(--text-display--line-height),88px)] wrap-anywhere';\n\n/**\n * The page header: a persistent bar (`start` | title | `end`) and, with `largeTitle` (the\n * default), the Meadow Sage large title below it that collapses into the bar's title as the\n * page's `bf-content` scrolls. The bar follows the shell mode (`provideBfShell({ mode })`, the\n * platform's for `'auto'`): iOS, at least 44 px with a semibold title centred on the bar whatever\n * the slots hold (symmetric insets of the wider slot's width plus 8 px, measured with a\n * `ResizeObserver`); Material, at least 64 px with a regular title leading after the `start` slot.\n * The bar grows with its content at large text sizes: the title wraps to at most two lines (the\n * bar is never shorter than the measured title, `--bf-header-title-height`), the `start` slot may\n * shrink (a back button drops its text before its chevron; in iOS mode capped by\n * `--bf-header-start-max` so the centred title keeps up to half the bar) and the `end` slot never\n * does. At\n * compact heights (`(max-height: 500px)`, phone landscape) it renders no large title: the title is\n * the bar's `h1`, the bar is 44 px in both modes and nothing collapses. Once content scrolls under\n * it (the large title fully collapsed; without one, `scrollTop > 0`, overscroll excluded) the\n * host gets `data-bf-scrolled`: iOS turns translucent with a blur and a 1 px hairline, Material\n * takes a tonal tint. Reads its sibling scroller through the page's `BfPageContext` (set by\n * `bf-content`) and, from a passive scroll listener, writes `--bf-header-progress` (0…1, over the\n * large title block's rendered height, bounded to 44–200 px (a taller title collapses faster), of the scroll position clamped to the\n * scrollable range, so overscroll never moves it, and 0 on a page that cannot scroll that far) on\n * its host and the large title block's height (its natural height, measured once and on resize,\n * times `1 − progress`), so at progress 1 the header is exactly the toolbar; scrolling schedules\n * no Angular change detection. Solid, it also writes the collapsed height as\n * `--bf-header-collapse` on the scroller, which `bf-content` adds to its bottom padding: the\n * scroller grows by what the header gives up, so its scroll range stays the same through the\n * collapse and the short-page rule reads one stable value. When a scroll ends part-way\n * (`scrollend`, or 120 ms without a `scroll` event) with nothing pressed on the scroller, it snaps\n * the scroller to fully expanded or fully collapsed, whichever is nearer, like UIKit. Solid\n * (default), it sits in the page's flow above the content; `translucent`, it is drawn over the\n * content and `bf-content` pads its scroller by the header's height (`--bf-header-height`).\n *\n * @example\n * <bf-header title=\"Today\" subtitle=\"Wed, 23 Sep\">\n * <bf-icon-button bfSlot=\"end\" variant=\"text\" label=\"Profile\">\n * <bf-icon [icon]=\"userIcon\" />\n * </bf-icon-button>\n * </bf-header>\n * @summary Page header toolbar with a large title that collapses into the toolbar on scroll\n * @category Navigation\n * @since 0.2.0\n * @slot start Toolbar buttons at the start, such as `bf-back-button`.\n * @slot default The toolbar title, when the `title` input is empty.\n * @slot end Toolbar buttons at the end.\n */\n@Component({\n selector: 'bf-header',\n host: { '[class]': 'hostClasses()', '[attr.data-bf-mode]': 'mode' },\n template: `\n <header class=\"bf-header relative flex flex-col\">\n <div #toolbar [class]=\"toolbarClasses()\">\n <div [class]=\"barClasses()\">\n <div #startSlot [class]=\"startSlotClasses\">\n <ng-content select=\"[bfSlot=start]\" />\n </div>\n <div [class]=\"titleBoxClasses\">\n @if (mode === 'ios' && title()) {\n <!-- The title's one-line width, measured to cap the start slot; never shown. -->\n <span class=\"invisible absolute h-0 w-0 overflow-hidden\" aria-hidden=\"true\"\n ><span\n #titleSizer\n class=\"text-heading inline-block font-semibold whitespace-nowrap\"\n >{{ title() }}</span\n ></span\n >\n }\n @if (showLarge()) {\n <span\n #titleText\n class=\"bf-small opacity-(--bf-header-progress,0) {{ titleTextClasses }}\"\n aria-hidden=\"true\"\n >{{ title() }}</span\n >\n } @else {\n <h1 #titleText data-bf-page-title class=\"pointer-events-auto {{ titleTextClasses }}\">\n @if (title()) {\n {{ title() }}\n } @else {\n <ng-content />\n }\n </h1>\n }\n </div>\n <div #endSlot class=\"ms-auto flex shrink-0 items-center gap-1\">\n <ng-content select=\"[bfSlot=end]\" />\n </div>\n </div>\n </div>\n @if (showLarge()) {\n <div #largeClip class=\"overflow-hidden\">\n <div #largeBlock [class]=\"largeBlockClasses\">\n @if (subtitle()) {\n <p class=\"text-caption text-text-muted\">{{ subtitle() }}</p>\n }\n <h1 data-bf-page-title [class]=\"largeTitleClasses\">\n @if (title()) {\n {{ title() }}\n } @else {\n <ng-content />\n }\n </h1>\n </div>\n </div>\n }\n </header>\n `,\n})\nexport class BfHeaderComponent {\n /** The page title, rendered in the toolbar and, with `largeTitle`, in the large title block. */\n readonly title = input('');\n /** Eyebrow line above the large title (e.g. a date); rendered only with `largeTitle`. */\n readonly subtitle = input('');\n /**\n * Renders the collapsing large title block below the toolbar; never at compact heights\n * (`(max-height: 500px)`), where the title is the bar's `h1`.\n */\n readonly largeTitle = input(true, { transform: booleanAttribute });\n /**\n * Draws the header over the content (`absolute`) instead of in the page's flow, so content\n * scrolls under its scroll-under surface; `bf-content` pads its scroller by the header's height.\n */\n readonly translucent = input(false, { transform: booleanAttribute });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly pageContext = inject(BfPageContext, { optional: true });\n /** The status-bar band, unless the page sits below the status bar already (a page-sheet modal). */\n protected readonly toolbarClasses = computed(() =>\n (this.pageContext?.safeAreaTop() ?? true) ? 'pt-safe-top' : '',\n );\n private readonly keyboard = inject(BfKeyboardService);\n private readonly document = inject(DOCUMENT);\n private readonly toolbar = viewChild.required<ElementRef<HTMLElement>>('toolbar');\n private readonly startSlot = viewChild.required<ElementRef<HTMLElement>>('startSlot');\n private readonly endSlot = viewChild.required<ElementRef<HTMLElement>>('endSlot');\n private readonly largeClip = viewChild<ElementRef<HTMLElement>>('largeClip');\n private readonly largeBlock = viewChild<ElementRef<HTMLElement>>('largeBlock');\n private readonly titleText = viewChild<ElementRef<HTMLElement>>('titleText');\n private readonly titleSizer = viewChild<ElementRef<HTMLElement>>('titleSizer');\n\n /**\n * True at compact heights (`(max-height: 500px)`: phone landscape, split screen), read with\n * `matchMedia` and followed live: no large title, the title in the bar, a 44 px bar.\n */\n protected readonly compact = signal(false);\n /** Whether the large title block renders: `largeTitle`, except at compact heights. */\n protected readonly showLarge = computed(() => this.largeTitle() && !this.compact());\n\n /** The shell mode (`provideBfShell({ mode })`, or the platform's for `'auto'`), fixed per header. */\n protected readonly mode: BfShellMode = injectShellMode();\n protected readonly barClasses = computed(\n () => `relative flex items-center px-2 ${BAR_CLASSES[this.compact() ? 'compact' : this.mode]}`,\n );\n protected readonly titleBoxClasses = TITLE_BOX_CLASSES[this.mode];\n protected readonly titleTextClasses = TITLE_TEXT_CLASSES[this.mode];\n protected readonly startSlotClasses = START_SLOT_CLASSES[this.mode];\n protected readonly largeBlockClasses = LARGE_BLOCK_CLASSES;\n protected readonly largeTitleClasses = LARGE_TITLE_CLASSES;\n protected readonly hostClasses = computed(\n () =>\n `${LAYOUT_CLASSES[this.translucent() ? 'translucent' : 'solid']} ${SURFACE_CLASSES[this.mode]}`,\n );\n\n /** Last written progress (0…1). */\n private progress = 0;\n /** Natural height of the large title block, 0 until measured (or without one). */\n private largeHeight = 0;\n /** The scroller `--bf-header-collapse` was last written on, so it can be cleared again. */\n private compensated: HTMLElement | null = null;\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n // Compact heights, followed live (rotation, a split-screen resize); the listener goes with\n // the header.\n const view = this.document.defaultView;\n const compactQuery =\n typeof view?.matchMedia === 'function' ? view.matchMedia(COMPACT_QUERY) : null;\n if (compactQuery) {\n this.compact.set(compactQuery.matches);\n const onCompactChange = (event: MediaQueryListEvent): void => this.compact.set(event.matches);\n compactQuery.addEventListener('change', onCompactChange);\n destroyRef.onDestroy(() => compactQuery.removeEventListener('change', onCompactChange));\n }\n\n let lastProgress: number | null = null;\n let lastScrolled: boolean | null = null;\n const setProgress = (scroller: HTMLElement): void => {\n // Clamped to the scrollable range, so a rubber-band past either end never moves the header,\n // and 0 when the page cannot scroll the whole range (it could never fully collapse). The\n // range is the rest range at any progress: `--bf-header-collapse` pads the scroller's bottom\n // by exactly what the collapse adds to its height, so the range never shrinks under it.\n // Without a large title block (none asked for, or a compact height) nothing collapses.\n const large = untracked(this.showLarge);\n const range = this.collapseRange();\n const max = scroller.scrollHeight - scroller.clientHeight;\n const scrollTop = !large || max < range ? 0 : Math.min(max, Math.max(0, scroller.scrollTop));\n const progress = Math.round(Math.min(1, Math.max(0, scrollTop / range)) * 1000) / 1000;\n if (progress !== lastProgress) {\n lastProgress = progress;\n this.progress = progress;\n this.host.style.setProperty('--bf-header-progress', String(progress));\n this.applyCollapse();\n }\n // The bar takes its scrolled-under surface once the large title has fully collapsed (the\n // title is the scroll-edge state, like UIKit's `scrollEdgeAppearance`), so a snap to the\n // collapsed position shows it; 0.5 px of tolerance covers a fractional snap landing on 3×\n // screens. Without one, as soon as the page leaves its top. A rubber-band past the top (a\n // negative `scrollTop`) never counts.\n const scrolled = large ? scrollTop >= range - 0.5 : Math.min(max, scroller.scrollTop) > 0;\n if (scrolled !== lastScrolled) {\n lastScrolled = scrolled;\n this.host.toggleAttribute('data-bf-scrolled', scrolled);\n }\n };\n\n // Natural heights and the slots' widths, measured once the view exists and again whenever the\n // toolbar, a slot, the bar title or the large title block resizes (fonts, text size, rotation,\n // a slot's content changing) — never per scroll frame.\n afterRenderEffect((onCleanup) => {\n const toolbar = this.toolbar().nativeElement;\n const start = this.startSlot().nativeElement;\n const end = this.endSlot().nativeElement;\n const title = this.titleText()?.nativeElement ?? null;\n const sizer = this.titleSizer()?.nativeElement ?? null;\n const block = this.largeBlock()?.nativeElement ?? null;\n const translucent = this.translucent();\n const measure = (): void => {\n // iOS: only when the title does not fit on one line beside the slots at their full width,\n // it may claim up to half the bar before the start slot gives way, as UIKit drops the back\n // title when the title needs the room; only a back button's text gives way, so the cap\n // never goes below the slot's other content. The cap depends only on the bar's width, the\n // title's one-line width and the slot's full and minimum widths (both read the same\n // whatever the cap), never on the capped slot itself, so it cannot loop.\n const cap = sizer\n ? this.startCap(toolbar.clientWidth, sizer.offsetWidth, start, end)\n : null;\n if (cap === null) {\n this.host.style.removeProperty('--bf-header-start-max');\n } else {\n this.host.style.setProperty('--bf-header-start-max', `${cap}px`);\n }\n // The centred title keeps the same distance from both edges: clear of the wider slot\n // (its content's extent, should anything overflow it).\n const inset =\n ROW_PADDING + Math.max(start.offsetWidth, start.scrollWidth, end.offsetWidth) + TITLE_GAP;\n this.host.style.setProperty('--bf-header-title-inset', `${inset}px`);\n // The bar is never shorter than its (wrapped) title.\n this.host.style.setProperty('--bf-header-title-height', `${title?.offsetHeight ?? 0}px`);\n this.largeHeight = block ? block.offsetHeight : 0;\n // The collapse range follows the large title's height: re-read the progress against it.\n const scroller = untracked(() => this.pageContext?.scroller() ?? null);\n if (scroller) {\n setProgress(scroller);\n }\n this.applyCollapse();\n this.pageContext?.headerHeight.set(\n translucent ? toolbar.offsetHeight + this.largeHeight : 0,\n );\n };\n measure();\n if (typeof ResizeObserver !== 'undefined') {\n // Measured in the next frame, not in the observer's callback: `measure` writes the title\n // inset and height, which resize observed elements, and a write inside the callback makes\n // the browser report \"ResizeObserver loop completed with undelivered notifications\" (an\n // error event the app's global error listener logs; WebKit hits it).\n let frame: number | null = null;\n const observer = new ResizeObserver(() => {\n frame ??= requestAnimationFrame(() => {\n frame = null;\n measure();\n });\n });\n observer.observe(toolbar);\n observer.observe(start);\n observer.observe(end);\n for (const element of [title, sizer]) {\n if (element) {\n observer.observe(element);\n }\n }\n if (block) {\n observer.observe(block);\n }\n onCleanup(() => {\n observer.disconnect();\n if (frame !== null) {\n cancelAnimationFrame(frame);\n }\n });\n }\n });\n let destroyed = false;\n destroyRef.onDestroy(() => {\n destroyed = true;\n this.pageContext?.headerHeight.set(0);\n this.compensated?.style.removeProperty('--bf-header-collapse');\n this.compensated = null;\n });\n\n // Passive native listeners on the page's registered scroller (set by `bf-content`), so the\n // collapse and the snap never schedule Angular change detection while scrolling.\n effect((onCleanup) => {\n const scroller = this.pageContext?.scroller() ?? null;\n if (!scroller) {\n return;\n }\n // A new scroller gets the current progress and compensation written on its first read.\n lastProgress = null;\n lastScrolled = null;\n const passive = { passive: true };\n /** Pending scroll-end timer; the scroll end fires once, from it or from `scrollend`. */\n let quietTimer: ReturnType<typeof setTimeout> | null = null;\n let settlePending = false;\n /** True from our own snap until its scroll ends, so the snap never schedules another. */\n let snapping = false;\n let touchDown = false;\n let pointerDown = false;\n /** True once the scroller scrolled during the current press; a bare tap never snaps. */\n let scrolledSincePress = false;\n const doc = this.document;\n\n const clearQuietTimer = (): void => {\n if (quietTimer !== null) {\n clearTimeout(quietTimer);\n quietTimer = null;\n }\n };\n const waitForQuiet = (): void => {\n settlePending = true;\n clearQuietTimer();\n quietTimer = setTimeout(onScrollEnd, SNAP_QUIET_MS);\n };\n const snap = (): void => {\n if (\n destroyed ||\n touchDown ||\n pointerDown ||\n this.progress <= 0 ||\n this.progress >= 1 ||\n this.keyboard.visible() ||\n // A parked page hides its frame, the common ancestor of the header and the scroller, and\n // `visibility` inherits, so the header's own visibility is the scroller's.\n this.host.checkVisibility?.({ visibilityProperty: true }) === false\n ) {\n return;\n }\n snapping = true;\n waitForQuiet();\n scroller.scrollTo({\n top: this.progress < 0.5 ? 0 : this.collapseRange(),\n behavior: prefersReducedMotion(this.document.defaultView) ? 'instant' : 'smooth',\n });\n };\n function onScrollEnd(): void {\n clearQuietTimer();\n if (!settlePending) {\n return;\n }\n settlePending = false;\n if (snapping) {\n snapping = false;\n return;\n }\n snap();\n }\n const onScroll = (): void => {\n scrolledSincePress = true;\n setProgress(scroller);\n waitForQuiet();\n };\n const onTouch = (event: TouchEvent): void => {\n const down = event.touches.length > 0;\n if (down && !touchDown && !pointerDown) {\n press();\n }\n touchDown = down;\n release();\n };\n // A mouse released outside the scroller never fires `pointerup` on it, so the release is\n // heard on the document, and only while a press is tracked.\n const stopTrackingPointer = (): void => {\n doc.removeEventListener('pointerup', onPointerUp);\n doc.removeEventListener('pointercancel', onPointerUp);\n };\n const onPointerDown = (): void => {\n if (!touchDown && !pointerDown) {\n press();\n }\n pointerDown = true;\n stopTrackingPointer();\n doc.addEventListener('pointerup', onPointerUp, passive);\n doc.addEventListener('pointercancel', onPointerUp, passive);\n release();\n };\n function onPointerUp(): void {\n pointerDown = false;\n stopTrackingPointer();\n release();\n }\n /** A new press; one that interrupts our snap counts as a scroll, so its release snaps again. */\n function press(): void {\n scrolledSincePress = snapping;\n }\n /**\n * A press takes over from our snap; a release after a scroll waits for any momentum to\n * settle, then snaps. A release without a scroll (a tap) schedules nothing.\n */\n function release(): void {\n if (touchDown || pointerDown) {\n snapping = false;\n settlePending = false;\n clearQuietTimer();\n } else if (scrolledSincePress) {\n waitForQuiet();\n }\n }\n\n scroller.addEventListener('scroll', onScroll, passive);\n scroller.addEventListener('scrollend', onScrollEnd, passive);\n scroller.addEventListener('touchstart', onTouch, passive);\n scroller.addEventListener('touchend', onTouch, passive);\n scroller.addEventListener('touchcancel', onTouch, passive);\n scroller.addEventListener('pointerdown', onPointerDown, passive);\n setProgress(scroller);\n onCleanup(() => {\n clearQuietTimer();\n scroller.removeEventListener('scroll', onScroll);\n scroller.removeEventListener('scrollend', onScrollEnd);\n scroller.removeEventListener('touchstart', onTouch);\n scroller.removeEventListener('touchend', onTouch);\n scroller.removeEventListener('touchcancel', onTouch);\n scroller.removeEventListener('pointerdown', onPointerDown);\n stopTrackingPointer();\n });\n });\n }\n\n /**\n * The iOS `start` slot's cap (px), or `null` when the title fits on one line beside the slots\n * at their full width. A back button's text (`[data-bf-back-text]`) is the only part that gives\n * way: the cap is at least the slot's width with every back text gone (down to the button's own\n * minimum width) and at least one 44 px hit area.\n * @param width The bar's width.\n * @param titleWidth The title's one-line width.\n * @param start The start slot.\n * @param end The end slot.\n */\n private startCap(\n width: number,\n titleWidth: number,\n start: HTMLElement,\n end: HTMLElement,\n ): number | null {\n // The slot's width now, what it is with every back text in full, and with every one gone.\n const current = Math.max(start.offsetWidth, start.scrollWidth);\n let natural = current;\n let floor = current;\n for (const text of start.querySelectorAll<HTMLElement>('[data-bf-back-text]')) {\n natural += Math.max(0, text.scrollWidth - text.clientWidth);\n const button = text.parentElement;\n const style = button ? this.document.defaultView?.getComputedStyle(button) : undefined;\n const buttonMin = parseFloat(style?.minWidth ?? '') || 0;\n const room = button ? button.offsetWidth - buttonMin : text.clientWidth;\n floor -= Math.max(0, Math.min(text.clientWidth, room));\n }\n const room = width - 2 * (ROW_PADDING + Math.max(natural, end.offsetWidth) + TITLE_GAP);\n if (titleWidth <= room) {\n return null;\n }\n const wanted = Math.min(titleWidth, width / 2);\n const cap = Math.floor(Math.max(START_MIN, (width - wanted) / 2 - ROW_PADDING - TITLE_GAP));\n return Math.max(cap, Math.ceil(floor));\n }\n\n /**\n * The scroll distance (px) over which the large title collapses: its block's rendered height\n * (the eyebrow, the wrapped title and its bottom padding), so the collapse ends exactly when the\n * title has scrolled away, bounded to 44–200 px (a taller title collapses its whole height over\n * 200 px, so faster than it scrolls); 56 px until it is measured.\n */\n private collapseRange(): number {\n if (this.largeHeight <= 0) {\n return COLLAPSE_FALLBACK;\n }\n return Math.min(COLLAPSE_MAX, Math.max(COLLAPSE_MIN, this.largeHeight));\n }\n\n /**\n * Collapses the large title block's height with the progress, so the header ends at exactly the\n * toolbar (left at its natural (auto) height until measured), and writes the height it gave up\n * as `--bf-header-collapse` on the page's scroller (`0px` at rest). Solid, the header is in the\n * page's column, so the scroller grows by that much and `bf-content` pads its bottom by the same\n * amount, keeping the scroll range constant; translucent, the header is drawn over the scroller,\n * which never resizes, so the compensation stays `0px`.\n */\n private applyCollapse(): void {\n const clip = this.largeClip()?.nativeElement;\n const measured = clip !== undefined && this.largeHeight > 0;\n let collapsed = 0;\n if (clip && measured) {\n const height = Math.round(this.largeHeight * (1 - this.progress));\n clip.style.height = `${height}px`;\n collapsed = this.translucent() ? 0 : this.largeHeight - height;\n }\n const scroller = untracked(() => this.pageContext?.scroller() ?? null);\n if (this.compensated && this.compensated !== scroller) {\n this.compensated.style.removeProperty('--bf-header-collapse');\n }\n this.compensated = scroller;\n scroller?.style.setProperty('--bf-header-collapse', `${collapsed}px`);\n }\n}\n","import {\n Component,\n DOCUMENT,\n ElementRef,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n type AfterViewInit,\n viewChild,\n} from '@angular/core';\nimport { BF_LABELS, BfSpinnerComponent } from '@bitforgehq/ui';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { DURATION_MD, SETTLE_EASING } from '../../motion';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { BfHapticsService } from '../../platform/haptics.service';\nimport { BfPlatformService } from '../../platform/platform.service';\n\n/** Resting position of the disc, above the scroller's clipped content. */\nconst RESTING = -48;\n/** Parked position while refreshing: right at the top of the scroller's content. */\nconst PARKED = 0;\n/** Raw pointer travel (px) that counts as a completed pull. */\nconst THRESHOLD = 64;\n/** Cap on how far the resisted pull can push the disc past `PARKED`. */\nconst MAX_PULL = THRESHOLD + 8;\n/** The pull moves the disc at half the pointer's travel. */\nconst RESISTANCE = 0.5;\n/** Downward travel (px) a press must clear before it's claimed as a pull, not a click. */\nconst SLOP = 8;\nconst SETTLE_DURATION = DURATION_MD;\n/** Bounce mode: rubber-band depth (px) the disc needs before it shows in the gap. */\nconst BOUNCE_REVEAL = 8;\n/** Bounce mode: the disc's scale at progress 0; it grows to 1 at the threshold. */\nconst BOUNCE_MIN_SCALE = 0.6;\n\n/**\n * The scroller a refresher used without a page context tracks: its nearest ancestor that scrolls\n * vertically (`overflow-y` auto or scroll) — past `bf-content`'s body wrapper — else its parent.\n */\nfunction findScroller(host: HTMLElement): HTMLElement | null {\n const view = host.ownerDocument.defaultView;\n for (let element = host.parentElement; element && view; element = element.parentElement) {\n const overflowY = view.getComputedStyle(element).overflowY;\n if (overflowY === 'auto' || overflowY === 'scroll') {\n return element;\n }\n }\n return host.parentElement;\n}\n\nfunction translateStyle(px: number): string {\n return `translate(-50%, ${px}px)`;\n}\n\n/** A touch in iOS mode: the scroller rubber-bands natively and the disc rides the gap. */\ninterface BfRefresherBounce {\n readonly reducedMotion: boolean;\n /** Last rubber-band depth (`−scrollTop`, px) seen during the touch. */\n pull: number;\n hapticFired: boolean;\n /** The disc is showing in the gap; styles are written only when this changes or while shown. */\n shown: boolean;\n}\n\ninterface BfRefresherGesture {\n readonly pointerId: number;\n readonly startX: number;\n readonly startY: number;\n readonly reducedMotion: boolean;\n hapticFired: boolean;\n /** Set once the drag clears `SLOP` downward and out-vertical'd any horizontal travel. */\n claimed: boolean;\n}\n\n/**\n * The payload of `bf-refresher`'s `refreshRequested` output: call `complete()` once the reload is\n * done to animate the disc away.\n *\n * @summary What a pull-to-refresh request carries: the callback that ends the refresh\n * @since 0.3.0\n */\nexport interface BfRefresherRefreshEvent {\n /** Ends the refresh: animates the disc away and resets the refresher. */\n complete: () => void;\n}\n\n/**\n * Pull-to-refresh, placed as the first child of `bf-content`. It tracks Pointer Events on the\n * page's scroller (read from `BfPageContext`, or its nearest scrolling ancestor when used without one)\n * only while the scroller is at `scrollTop === 0`: the disc resists the pull (half the pointer's\n * travel, capped) from a resting position above the clipped content to a 64 px threshold, ticks\n * `BfHapticsService.impact('light')` once on crossing it, and on release past the threshold\n * parks at the top with a spinner and emits `refreshRequested` — call `complete()` on the event to animate\n * the disc away and reset. A press by itself changes nothing: only once a pointermove clears an\n * 8 px downward slop (out-vertical'ing any horizontal travel) does it claim the gesture and\n * capture the pointer on the scroller, so a plain click on the content underneath (a row, a\n * button) still reaches it. For touch, a non-passive `touchmove` guard (attached only while a\n * press is tracked) keeps the browser from taking a downward drag at the top as a pan, which\n * would cancel the pointer stream; upward drags still scroll the list. The disc itself is\n * `aria-hidden`; a permanent, visually hidden `role=\"status\"` region announces\n * `provideBfLabels()`'s `refreshing` (\"Refreshing\"). `prefers-reduced-motion` still follows the\n * pointer 1:1 during the pull (direct manipulation is not motion); only the settle, park and `complete()` animations\n * become instant.\n *\n * That pull is the Material (and mouse/pen) mode. A touch in iOS mode (`BfPlatformService.mode()`)\n * is the bounce mode instead: the refresher claims nothing (no capture, no `preventDefault`, no\n * guard), the scroller rubber-bands natively, and while the finger is down a negative `scrollTop`\n * shows the disc inside the gap the bounce opens, at its resting −48 px, with opacity and scale\n * (0.6 → 1) following `−scrollTop / 64` once it passes 8 px; one light haptic at 64 px. Lifting the\n * finger at 64 px or more parks the disc and emits `refreshRequested`, like `UIRefreshControl`; a bounce\n * with no finger down (a fling's overshoot) never shows it. The host carries `data-bf-refresher`\n * while enabled, so `bfOverscrollStretch` leaves the top edge to it.\n *\n * @example\n * <bf-content>\n * <bf-refresher (refreshRequested)=\"reload($event.complete)\" />\n * <bf-list>…</bf-list>\n * </bf-content>\n * @summary Pull-to-refresh indicator with haptic feedback, native iOS bounce and an Android-style pull\n * @category Navigation\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-refresher',\n imports: [BfSpinnerComponent],\n host: { class: 'relative block h-0', '[attr.data-bf-refresher]': 'disabled() ? null : \"\"' },\n template: `\n <div\n #disc\n data-bf-refresher-disc\n aria-hidden=\"true\"\n class=\"bg-surface shadow-raised absolute top-0 left-1/2 flex h-10 w-10 items-center justify-center rounded-full\"\n >\n <bf-spinner size=\"sm\" />\n </div>\n <div role=\"status\" aria-live=\"polite\" class=\"sr-only\">\n @if (refreshing()) {\n {{ labels.refreshing }}\n }\n </div>\n `,\n})\nexport class BfRefresherComponent implements AfterViewInit {\n /** Disables pull tracking; the disc never moves and `refreshRequested` never emits. */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /** Emitted when a pull past the threshold is released; call `complete()` to end it. */\n readonly refreshRequested = output<BfRefresherRefreshEvent>();\n /**\n * Emitted with the same event as `refreshRequested`, together with it.\n * @deprecated 0.3.0 use refreshRequested; removed in 0.4.0\n */\n readonly refresh = output<BfRefresherRefreshEvent>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly disc = viewChild.required<ElementRef<HTMLElement>>('disc');\n private readonly pageContext = inject(BfPageContext, { optional: true });\n private readonly haptics = inject(BfHapticsService);\n protected readonly labels = inject(BF_LABELS);\n private readonly document = inject(DOCUMENT);\n private readonly platform = inject(BfPlatformService);\n private readonly ios = computed(() => this.platform.mode() === 'ios');\n\n private readonly refreshingState = signal(false);\n /** True from the moment a completed pull is released until `complete()` is called. */\n readonly refreshing = this.refreshingState.asReadonly();\n\n private scroller: HTMLElement | null = null;\n private gesture: BfRefresherGesture | null = null;\n private settleAnimation: Animation | null = null;\n private currentOffset = RESTING;\n /** A finger is on the scroller (tracked in iOS mode only). */\n private touchActive = false;\n private bounce: BfRefresherBounce | null = null;\n\n constructor() {\n // A native Pointer Events listener on the page's scroller, attached only once it is known —\n // set by `bf-content` through `BfPageContext`, or the nearest scrolling ancestor when used\n // without one.\n effect((onCleanup) => {\n const scroller = this.pageContext?.scroller() ?? findScroller(this.host);\n if (!scroller) {\n return;\n }\n this.scroller = scroller;\n scroller.addEventListener('pointerdown', this.onPointerDown);\n // iOS: a touch rides WebKit's own rubber band (bounce mode), tracked with passive listeners.\n const ios = this.ios();\n if (ios) {\n const passive: AddEventListenerOptions = { passive: true };\n scroller.addEventListener('touchstart', this.onTouchStart, passive);\n scroller.addEventListener('touchend', this.onTouchEnd, passive);\n scroller.addEventListener('touchcancel', this.onTouchEnd, passive);\n }\n onCleanup(() => {\n this.endGesture(scroller);\n scroller.removeEventListener('pointerdown', this.onPointerDown);\n if (ios) {\n if (this.bounce?.shown) {\n this.hideBounceDisc();\n }\n this.endBounce(scroller);\n this.touchActive = false;\n scroller.removeEventListener('touchstart', this.onTouchStart);\n scroller.removeEventListener('touchend', this.onTouchEnd);\n scroller.removeEventListener('touchcancel', this.onTouchEnd);\n }\n this.settleAnimation?.cancel();\n this.scroller = null;\n });\n });\n }\n\n /** Paints the disc's resting, hidden transform once the view exists (no template `style`). */\n ngAfterViewInit(): void {\n this.setOffset(RESTING);\n }\n\n /** Emits one refresh event on `refreshRequested` and the deprecated `refresh`. */\n private requestRefresh(): void {\n const event: BfRefresherRefreshEvent = { complete: () => this.complete() };\n this.refreshRequested.emit(event);\n this.refresh.emit(event);\n }\n\n /** Ends a refresh: animates the disc away (skipped under reduced motion) and resets state. */\n complete(): void {\n if (!this.refreshingState()) {\n return;\n }\n this.settleTo(RESTING, prefersReducedMotion(this.document.defaultView), () =>\n this.refreshingState.set(false),\n );\n }\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n const scroller = this.scroller;\n if (!scroller) {\n return;\n }\n if (this.ios() && (event.pointerType === 'touch' || this.touchActive)) {\n // Bounce mode: the scroller keeps the touch and rubber-bands natively.\n return;\n }\n if (\n this.disabled() ||\n this.refreshingState() ||\n this.gesture ||\n !event.isPrimary ||\n scroller.scrollTop !== 0\n ) {\n return;\n }\n this.settleAnimation?.cancel();\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n hapticFired: false,\n claimed: false,\n };\n // Tracked, but not claimed: no Pointer Capture yet, and nothing prevented, so a press\n // released without a qualifying move still reaches a plain `click` on whatever is\n // underneath it (a row, a button) instead of being swallowed by the scroller.\n scroller.addEventListener('pointermove', this.onPointerMove);\n scroller.addEventListener('pointerup', this.onPointerUp);\n scroller.addEventListener('pointercancel', this.onPointerCancel);\n scroller.addEventListener('lostpointercapture', this.onLostPointerCapture);\n scroller.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n private readonly onTouchStart = (): void => {\n this.touchActive = true;\n const scroller = this.scroller;\n // A pen press in pull mode fires touch events too; the pull owns that press.\n if (!scroller || this.gesture || this.bounce || this.disabled() || this.refreshingState()) {\n return;\n }\n this.bounce = {\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n pull: 0,\n hapticFired: false,\n shown: false,\n };\n scroller.addEventListener('scroll', this.onBounceScroll, { passive: true });\n };\n\n /**\n * While a finger is down, a negative `scrollTop` is WebKit's rubber band at the top: the disc,\n * which sits in the content above its top edge, shows in the gap the bounce opens. Direct\n * manipulation (styles set straight from the scroll position, no animation).\n */\n private readonly onBounceScroll = (): void => {\n const bounce = this.bounce;\n const scroller = this.scroller;\n if (!bounce || !scroller) {\n return;\n }\n const pull = Math.max(0, -scroller.scrollTop);\n bounce.pull = pull;\n if (pull <= BOUNCE_REVEAL) {\n if (bounce.shown) {\n bounce.shown = false;\n this.hideBounceDisc();\n }\n return;\n }\n bounce.shown = true;\n const progress = Math.min(1, pull / THRESHOLD);\n const element = this.disc().nativeElement;\n element.style.opacity = String(progress);\n element.style.transform = `${translateStyle(RESTING)} scale(${BOUNCE_MIN_SCALE + (1 - BOUNCE_MIN_SCALE) * progress})`;\n element.style.visibility = 'visible';\n if (progress >= 1 && !bounce.hapticFired) {\n bounce.hapticFired = true;\n void this.haptics.impact('light');\n }\n };\n\n private readonly onTouchEnd = (event: TouchEvent): void => {\n if (event.touches.length > 0) {\n return;\n }\n this.touchActive = false;\n const bounce = this.bounce;\n const scroller = this.scroller;\n if (!bounce || !scroller) {\n return;\n }\n // The rubber band keeps scrolling back after the finger lifts; that is not a pull.\n this.endBounce(scroller);\n if (!this.disabled() && !this.refreshingState() && bounce.pull >= THRESHOLD) {\n // The disc settles from the gap onto the content top while the content snaps back.\n this.disc().nativeElement.style.removeProperty('opacity');\n this.refreshingState.set(true);\n this.settleTo(PARKED, bounce.reducedMotion);\n this.requestRefresh();\n } else if (bounce.shown) {\n this.hideBounceDisc();\n }\n };\n\n private endBounce(scroller: HTMLElement): void {\n scroller.removeEventListener('scroll', this.onBounceScroll);\n this.bounce = null;\n }\n\n private hideBounceDisc(): void {\n this.disc().nativeElement.style.removeProperty('opacity');\n this.setOffset(RESTING);\n }\n\n /**\n * Non-passive `touchmove` guard, attached only while a press is tracked (the pattern of the\n * stack outlet's swipe guard). The scroller is `touch-pan-y`, so without it the browser takes a\n * downward drag as a pan and ends the pointer stream with `pointercancel`. It prevents a\n * cancelable move once the pull is claimed, or — before the claim — a downward-dominant move\n * while the scroller is at the top, where a pan has nothing to scroll anyway. Upward and\n * horizontal drags are left alone, so the list (and any horizontal scroller) keeps its pan.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n const gesture = this.gesture;\n if (!gesture || !event.cancelable) {\n return;\n }\n if (gesture.claimed) {\n event.preventDefault();\n return;\n }\n const touch = event.touches[0];\n if (!touch || this.scroller?.scrollTop !== 0) {\n return;\n }\n const dy = touch.clientY - gesture.startY;\n const dx = touch.clientX - gesture.startX;\n if (dy > 0 && dy > Math.abs(dx)) {\n event.preventDefault();\n }\n };\n\n /**\n * A touch pointer is implicitly captured by the element under the finger; claiming it for the\n * scroller makes that child fire its own `lostpointercapture`, which bubbles here. Only the\n * scroller losing the capture it took ends the pull.\n */\n private readonly onLostPointerCapture = (event: PointerEvent): void => {\n if (event.target !== this.scroller) {\n return;\n }\n this.onPointerCancel(event);\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n if (scroller.scrollTop !== 0) {\n // The list itself started scrolling under the drag; let it, the pull is over.\n this.endGesture(scroller);\n this.setOffset(RESTING);\n return;\n }\n const delta = event.clientY - gesture.startY;\n if (!gesture.claimed) {\n const dx = event.clientX - gesture.startX;\n if (delta <= SLOP || delta <= Math.abs(dx)) {\n // Not yet a clear, primarily-downward pull past the slop: leave the pointer alone.\n return;\n }\n gesture.claimed = true;\n try {\n // Captured only now: before this, an un-moved (or horizontal, or upward) pointerdown\n // and pointerup pair must behave like a normal click on the content underneath.\n scroller.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic pointers (tests) have no active pointer to capture.\n }\n }\n if (event.cancelable) {\n event.preventDefault();\n }\n // Direct manipulation, not an animation: the disc tracks the pointer 1:1 (resisted) even\n // under prefers-reduced-motion — only the settle/park/complete animations go instant.\n const resisted = delta > 0 ? Math.min(delta * RESISTANCE, MAX_PULL) : 0;\n this.setOffset(RESTING + resisted);\n if (delta >= THRESHOLD && !gesture.hapticFired) {\n gesture.hapticFired = true;\n void this.haptics.impact('light');\n }\n };\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n const claimed = gesture.claimed;\n const delta = event.clientY - gesture.startY;\n this.endGesture(scroller);\n if (!claimed) {\n // Never cleared the slop: a plain press-and-release, left untouched for its `click`.\n return;\n }\n if (!this.disabled() && delta >= THRESHOLD) {\n this.refreshingState.set(true);\n this.settleTo(PARKED, gesture.reducedMotion);\n this.requestRefresh();\n } else {\n this.settleTo(RESTING, gesture.reducedMotion);\n }\n };\n\n private readonly onPointerCancel = (event: PointerEvent): void => {\n const gesture = this.gesture;\n const scroller = this.scroller;\n if (!gesture || !scroller || event.pointerId !== gesture.pointerId) {\n return;\n }\n const claimed = gesture.claimed;\n this.endGesture(scroller);\n if (claimed) {\n this.settleTo(RESTING, gesture.reducedMotion);\n }\n };\n\n private endGesture(scroller: HTMLElement): void {\n const pointerId = this.gesture?.pointerId;\n this.removeDragListeners(scroller);\n if (pointerId !== undefined) {\n try {\n scroller.releasePointerCapture(pointerId);\n } catch {\n // Already released (or never captured, e.g. a synthetic pointer in tests).\n }\n }\n this.gesture = null;\n }\n\n private removeDragListeners(scroller: HTMLElement): void {\n scroller.removeEventListener('pointermove', this.onPointerMove);\n scroller.removeEventListener('pointerup', this.onPointerUp);\n scroller.removeEventListener('pointercancel', this.onPointerCancel);\n scroller.removeEventListener('lostpointercapture', this.onLostPointerCapture);\n scroller.removeEventListener('touchmove', this.touchMoveGuard);\n }\n\n private setOffset(px: number): void {\n this.currentOffset = px;\n const element = this.disc().nativeElement;\n element.style.transform = translateStyle(px);\n // Clipping alone (the resting −48 px sits above the scroller's visible content) still let\n // the disc's shadow bleed a few px above the content; fully hide it at rest instead.\n element.style.visibility = px === RESTING ? 'hidden' : 'visible';\n }\n\n private settleTo(target: number, reducedMotion: boolean, onDone?: () => void): void {\n this.settleAnimation?.cancel();\n if (reducedMotion) {\n this.setOffset(target);\n onDone?.();\n return;\n }\n const from = this.currentOffset;\n const animation = this.disc().nativeElement.animate(\n [{ transform: translateStyle(from) }, { transform: translateStyle(target) }],\n { duration: SETTLE_DURATION, easing: SETTLE_EASING, fill: 'forwards' },\n );\n this.settleAnimation = animation;\n this.currentOffset = target;\n void animation.finished.then(\n () => {\n this.setOffset(target);\n onDone?.();\n },\n () => {\n // Cancelled by a newer gesture or settle; that one owns the disc now.\n },\n );\n }\n}\n","import { LocationStrategy } from '@angular/common';\nimport {\n DOCUMENT,\n Component,\n DestroyRef,\n ElementRef,\n afterRenderEffect,\n computed,\n contentChild,\n inject,\n input,\n linkedSignal,\n signal,\n type Signal,\n} from '@angular/core';\nimport { PRIMARY_OUTLET, Router, type UrlTree } from '@angular/router';\nimport {\n BF_TAB_BAR_CONTROLLER,\n BF_TAB_LINKS,\n BfTabBarComponent,\n type BfTabBarController,\n} from '@bitforgehq/ui';\nimport { BfHapticsService } from '../../platform/haptics.service';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { BfPlatformService } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS } from '../../shell/provide-bf-shell';\nimport { BfOverlayStack } from '@bitforgehq/ui/overlay';\nimport { BfNavController } from '../nav-controller.service';\nimport { BfStackOutletComponent } from '../stack-outlet/stack-outlet.component';\nimport { BF_TAB_CONTEXT, type BfTabContext } from './tab-context';\n\n/**\n * Bottom inset of a visible floating tab bar: its height, its bottom offset (8 px above the safe\n * area, which counts as at least 12 px) and 8 px of breathing room above the bar. The height is the\n * bar's measured one (it grows with large text and is slimmer at compact heights).\n * @param height The bar's height as a CSS length.\n */\nfunction barInset(height: string): string {\n return `calc(${height} + 8px + max(var(--spacing-safe-bottom), 12px) + 8px)`;\n}\n/** The inset until the bar is first measured: its minimum height, 64 px. */\nconst BAR_INSET_FALLBACK = barInset('64px');\n\n/**\n * The tabs page of a mobile app: one navigation stack per tab. It wraps a `bf-stack-outlet`, which\n * keeps a separate stack for every tab (keyed by the URL segment after `tabsPath`), and a\n * `bf-tab-bar`, which it drives through `BF_TAB_BAR_CONTROLLER`: the bar shows the tab of the\n * in-flight navigation's target, else of the current URL, and hides while the keyboard is open,\n * without anything writing into it. Selecting another tab brings\n * that tab back as it was left; re-selecting the active tab pops its stack to the root, or scrolls\n * its content to the top when it is already there. The bar hides while the soft keyboard is open\n * or about to open (a focused text control), and `--bf-tab-bar-inset` on the host tells\n * `bf-content` how much to pad its bottom (the bar's measured height, a `ResizeObserver` on its\n * `nav`, plus its bottom offset and 8 px; 64 px for the height until it is measured); the same value is mirrored on `<html>` as\n * `--bf-tab-bar-inset-global` for overlays (the toast), which render outside the host. It is removed when the tabs are destroyed.\n * It also provides `BF_TAB_LINKS`, so the projected bar is a `nav` of links to each tab's root URL\n * (`aria-current` on the active one: `page` at the tab's root, `true` on a child page of it)\n * instead of a `tablist`.\n *\n * @example\n * <bf-tabs tabsPath=\"tabs\">\n * <bf-stack-outlet />\n * <bf-tab-bar>\n * <bf-tab id=\"today\" label=\"Today\"><bf-icon [icon]=\"Sun\" /></bf-tab>\n * <bf-tab id=\"me\" label=\"Me\"><bf-icon [icon]=\"User\" /></bf-tab>\n * </bf-tab-bar>\n * </bf-tabs>\n * @summary Tabs page with one navigation stack per tab, wiring the tab bar to the router\n * @category Navigation\n * @since 0.2.0\n * @slot default The tabs page body: a `bf-stack-outlet` and a `bf-tab-bar`.\n */\n@Component({\n selector: 'bf-tabs',\n host: { class: 'block h-full' },\n providers: [\n { provide: BF_TAB_CONTEXT, useExisting: BfTabsComponent },\n // The projected bar renders links (href per tab) instead of a tablist.\n { provide: BF_TAB_LINKS, useExisting: BfTabsComponent },\n // ...and derives its selection and hiding from this component.\n { provide: BF_TAB_BAR_CONTROLLER, useExisting: BfTabsComponent },\n ],\n template: `<div class=\"relative flex h-full flex-col\"><ng-content /></div>`,\n})\nexport class BfTabsComponent implements BfTabContext, BfTabBarController {\n /** Route path of the tabs page (e.g. `'tabs'`); the URL segment after it is the tab key. */\n readonly tabsPath = input.required<string>();\n\n private readonly router = inject(Router);\n private readonly nav = inject(BfNavController);\n private readonly haptics = inject(BfHapticsService);\n private readonly keyboard = inject(BfKeyboardService);\n private readonly platform = inject(BfPlatformService);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly overlays = inject(BfOverlayStack);\n private readonly locationStrategy = inject(LocationStrategy);\n\n private readonly bar = contentChild(BfTabBarComponent);\n private readonly barHost = contentChild<BfTabBarComponent, ElementRef<HTMLElement>>(\n BfTabBarComponent,\n { read: ElementRef },\n );\n /** The bar's measured height (px), `null` until its `nav` is first measured. */\n private readonly barHeight = signal<number | null>(null);\n private readonly outlet = contentChild(BfStackOutletComponent);\n\n /** The settled URL: the last successful navigation's (after redirects), else the router's. */\n private readonly url = computed(\n () => this.router.lastSuccessfulNavigation()?.finalUrl ?? this.router.parseUrl(this.router.url),\n );\n\n /** The active tab's key, from the router URL; null while the URL is not under `tabsPath`. */\n readonly tabKey = computed(() => this.keyOfTree(this.url()));\n\n /**\n * The tab the bar shows: the in-flight navigation's target (after redirects once they are\n * applied), else the settled URL's. A refused or cancelled navigation ends, so the bar drops\n * back to the settled tab by itself. While neither is under `tabsPath` (a page pushed above the\n * tabs page) it keeps the last tab it showed.\n */\n private readonly currentTab = linkedSignal<string | null, string | null>({\n source: () => {\n const navigation = this.router.currentNavigation();\n return this.keyOfTree(\n navigation?.finalUrl ??\n navigation?.extractedUrl ??\n this.router.lastSuccessfulNavigation()?.finalUrl ??\n this.router.parseUrl(this.router.url),\n );\n },\n computation: (key, previous) => key ?? previous?.value ?? null,\n });\n\n /** The tab the bar shows (read-only): the in-flight navigation's target, else the current URL's;\n * null when unknown. */\n readonly current: Signal<string | null> = this.currentTab.asReadonly();\n\n /** Hide the bar while the keyboard is open or about to open (a focused text control: on Android\n * the WebView shrinks before will-show and would lift the fixed bar above the keyboard). */\n readonly hidden = computed(() => this.keyboard.visible() || this.keyboard.imminent());\n\n /** The bar hides animated on iOS, instantly on Android and the web. */\n readonly hideTransition = computed(() => this.platform.platform === 'ios');\n\n constructor() {\n // The tabs host is a page root too: apps that mount `bf-tabs` without an outer outlet still\n // need the tab bar to go inert while a modal-like overlay is open.\n const unregisterRoot = this.overlays.registerRoot(this.host);\n inject(DestroyRef).onDestroy(() => {\n unregisterRoot();\n this.document.documentElement.style.removeProperty('--bf-tab-bar-inset-global');\n });\n\n // The bar's real height, measured on its `nav` whenever it resizes (text size, rotation, a\n // label wrapping) and never per frame; the observer goes with the bar or the tabs.\n afterRenderEffect((onCleanup) => {\n const nav = this.barHost()?.nativeElement.querySelector('nav') ?? null;\n if (!(nav instanceof HTMLElement) || typeof ResizeObserver === 'undefined') {\n return;\n }\n const observer = new ResizeObserver(() => {\n const height = nav.offsetHeight;\n this.barHeight.set(height > 0 ? height : null);\n });\n observer.observe(nav);\n onCleanup(() => observer.disconnect());\n });\n\n // The inset is this host's own style (and `<html>`'s, for overlays): written in the render\n // phase, after the bar's hidden state for this change detection is known.\n afterRenderEffect({\n write: () => {\n const bar = this.bar();\n const shown = bar !== undefined && !bar.isHidden();\n const height = this.barHeight();\n const inset = !shown\n ? '0px'\n : height === null\n ? BAR_INSET_FALLBACK\n : barInset(`${height}px`);\n this.host.style.setProperty('--bf-tab-bar-inset', inset);\n // Overlays render in the CDK container outside `bf-tabs`, so they read the inset from\n // `<html>` under a name of its own: `bf-content` outside the tabs must not inherit it.\n this.document.documentElement.style.setProperty('--bf-tab-bar-inset-global', inset);\n },\n });\n }\n\n /**\n * The tab key of `url`: the primary segment after `tabsPath`. Query, fragment, matrix params and\n * auxiliary routes (`(popup:…)`) are ignored.\n * @param url The URL to read.\n */\n keyOf(url: string): string | null {\n return this.keyOfTree(this.router.parseUrl(url));\n }\n\n /** `keyOf` for a parsed URL. */\n private keyOfTree(tree: UrlTree): string | null {\n const segments = primarySegments(tree);\n const base = this.baseSegments();\n for (let i = 0; i + base.length < segments.length; i++) {\n if (base.every((b, j) => segments[i + j] === b)) {\n return segments[i + base.length] ?? null;\n }\n }\n return null;\n }\n\n /**\n * The URL of tab `id`'s root page (`/tabs/<id>`, with the app's base href), the `href` of its\n * link in the bar: a new browser tab opens the tab's root. A plain click still restores the page\n * the tab was left on.\n * @param id The `id` of the tab.\n */\n href(id: string): string {\n const tree = this.router.createUrlTree(['/', ...this.baseSegments(), id]);\n return this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(tree));\n }\n\n /**\n * Whether the current URL is tab `id`'s root page (`…/<tabsPath>/<id>` with nothing after it,\n * ignoring query, fragment and auxiliary routes); reactive through the router's signals.\n * @param id The `id` of the tab.\n */\n isRoot(id: string): boolean {\n const segments = primarySegments(this.url());\n const base = this.baseSegments();\n // The same match as `keyOf`: `tabsPath` wherever it sits, then the tab key as the last segment.\n const start = segments.length - base.length - 1;\n return (\n start >= 0 &&\n segments[segments.length - 1] === id &&\n base.every((b, j) => segments[start + j] === b) &&\n this.tabKey() === id\n );\n }\n\n /** The non-empty segments of `tabsPath`; empty when the tabs are mounted at the root URL. */\n private baseSegments(): string[] {\n return this.tabsPath()\n .split('/')\n .filter((s) => s.length > 0);\n }\n\n /**\n * Another tab, selected by the user in the bar: go back to where that tab was left (its stack's\n * top page), or its root. The bar shows the target while the navigation is in flight and drops\n * back by itself when a guard refuses it.\n * @param id The `id` of the selected tab.\n */\n select(id: string): void {\n const target = this.outlet()?.lastUrlOf(id) ?? ['/', ...this.baseSegments(), id];\n this.nav.forward(target).then(\n (navigated) => {\n // Only a switch that happened ticks; a refused one stays silent.\n if (navigated && this.config.hapticsOnTabChange) {\n void this.haptics.selection();\n }\n },\n (error: unknown) => {\n console.error('bf-tabs: tab navigation failed', error);\n },\n );\n }\n\n /**\n * The active tab again: pop its stack back to its existing root page (a history step), or\n * replace the stack with the root when the history position is unknown; at the root, scroll its\n * content to the top.\n * @param id The `id` of the active tab.\n */\n reselect(id: string): void {\n if (this.nav.depth() > 1) {\n if (!this.outlet()?.popToRoot()) {\n void this.nav.root(['/', ...this.baseSegments(), id]);\n }\n } else {\n this.outlet()?.scrollActiveContentToTop();\n }\n }\n}\n\n/** The paths of the primary outlet's segments of `tree`, from the root down. */\nfunction primarySegments(tree: UrlTree): string[] {\n const paths: string[] = [];\n let group = tree.root.children[PRIMARY_OUTLET];\n while (group) {\n paths.push(...group.segments.map((s) => s.path));\n group = group.children[PRIMARY_OUTLET];\n }\n return paths;\n}\n","/**\n * The overlay core's names as `@bitforgehq/ui/mobile` exported them before 0.3.0, kept for one\n * minor. Each is a `const` and/or `type` alias rather than an `export … from`, because TypeScript\n * does not carry `@deprecated` through a re-export. `/mobile` source imports the core from\n * `@bitforgehq/ui/overlay` directly, never through these aliases.\n */\nimport * as core from '@bitforgehq/ui/overlay';\n\n/** @deprecated 0.3.0 use BF_OVERLAY_DATA from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BF_OVERLAY_DATA = core.BF_OVERLAY_DATA;\n\n/** @deprecated 0.3.0 use BfOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayRef = core.BfOverlayRef;\n/** @deprecated 0.3.0 use BfOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayRef<T = unknown> = core.BfOverlayRef<T>;\n\n/** @deprecated 0.3.0 use BfComponentOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfComponentOverlayRef = core.BfComponentOverlayRef;\n/** @deprecated 0.3.0 use BfComponentOverlayRef from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfComponentOverlayRef<C, T = unknown> = core.BfComponentOverlayRef<C, T>;\n\n/** @deprecated 0.3.0 use BfOverlayStack from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayStack = core.BfOverlayStack;\n/** @deprecated 0.3.0 use BfOverlayStack from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayStack = core.BfOverlayStack;\n\n/** @deprecated 0.3.0 use BfOverlayStackOptions from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayStackOptions = core.BfOverlayStackOptions;\n\n/** @deprecated 0.3.0 use BfStackedOverlay from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfStackedOverlay = core.BfStackedOverlay;\n\n/** @deprecated 0.3.0 use BfOverlayService from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const BfOverlayService = core.BfOverlayService;\n/** @deprecated 0.3.0 use BfOverlayService from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayService = core.BfOverlayService;\n\n/** @deprecated 0.3.0 use BfOverlayOptions from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayOptions = core.BfOverlayOptions;\n\n/** @deprecated 0.3.0 use BfOverlayPosition from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayPosition = core.BfOverlayPosition;\n\n/** @deprecated 0.3.0 use BfOverlayTemplateContext from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport type BfOverlayTemplateContext = core.BfOverlayTemplateContext;\n\n/** @deprecated 0.3.0 use OVERLAY_DURATION from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const OVERLAY_DURATION = core.OVERLAY_DURATION;\n\n/** @deprecated 0.3.0 use OVERLAY_EASING from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const OVERLAY_EASING = core.OVERLAY_EASING;\n\n/** @deprecated 0.3.0 use fadeDown from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeDown = core.fadeDown;\n\n/** @deprecated 0.3.0 use fadeIn from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeIn = core.fadeIn;\n\n/** @deprecated 0.3.0 use fadeOut from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeOut = core.fadeOut;\n\n/** @deprecated 0.3.0 use fadeUp from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const fadeUp = core.fadeUp;\n\n/** @deprecated 0.3.0 use scaleIn from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const scaleIn = core.scaleIn;\n\n/** @deprecated 0.3.0 use slideDown from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const slideDown = core.slideDown;\n\n/** @deprecated 0.3.0 use slideUp from '@bitforgehq/ui/overlay'; removed in 0.4.0 */\nexport const slideUp = core.slideUp;\n","import { type Signal, type WritableSignal, signal } from '@angular/core';\nimport { type BfOverlayController, BfOverlayRef } from '@bitforgehq/ui/overlay';\n\n/** What `BfSheetService` does for a sheet ref; internal. */\nexport interface BfSheetController extends BfOverlayController {\n /** True while the underlying overlay is closing (backdrop, `Escape`, back button, drag). */\n isClosing(): boolean;\n}\n\n/**\n * Handle to an open bottom sheet, returned by `BfSheetService.open()` and injectable in the\n * sheet's content as `BfSheetRef` (and as `BfOverlayRef`). On top of `close(result)` and\n * `afterClosed` it moves the sheet between its snap points: `snapTo(index)` animates to a point\n * and `snapIndex()` tracks the current one (also after a drag).\n *\n * @example\n * const ref = sheets.open(FiltersComponent, { snapPoints: [0.4, 0.9] });\n * ref.snapTo(1);\n * const filters = await ref.afterClosed;\n\n * @since 0.2.0\n * @summary Handle on an open bottom sheet that moves it between snap points and closes it\n */\nexport class BfSheetRef<T = unknown> extends BfOverlayRef<T> {\n private readonly snapIndexState: WritableSignal<number>;\n /** Index of the snap point the sheet rests at (or is settling to). */\n readonly snapIndex: Signal<number>;\n private snapHandler: ((index: number) => void) | null = null;\n\n constructor(\n private readonly sheetController: BfSheetController,\n initialIndex = 0,\n ) {\n super(sheetController);\n this.snapIndexState = signal(initialIndex);\n this.snapIndex = this.snapIndexState.asReadonly();\n }\n\n /** True from the first close on, whichever way the sheet is being closed. */\n override get isClosing(): boolean {\n return super.isClosing || this.sheetController.isClosing();\n }\n\n /**\n * Animates the sheet to the snap point at `index` (clamped to the configured points).\n * @param index The position of the snap point in `snapPoints`.\n */\n snapTo(index: number): void {\n if (this.isClosing) {\n return;\n }\n this.snapHandler?.(index);\n }\n\n /** @internal Connects the `bf-sheet` container that performs `snapTo`. */\n connect(handler: (index: number) => void): void {\n this.snapHandler = handler;\n }\n\n /** @internal Set by `bf-sheet` whenever the sheet settles on a snap point. */\n setSnapIndex(index: number): void {\n this.snapIndexState.set(index);\n }\n}\n","import { DOCUMENT, DestroyRef, type Signal, computed, inject, signal } from '@angular/core';\nimport { BfKeyboardService } from '../platform/keyboard.service';\n\n/** The soft keyboard as a bottom-anchored overlay sees it; internal. */\nexport interface BfKeyboardCover {\n /**\n * Part of the layout viewport's bottom edge the keyboard overlays, in CSS px: the keyboard\n * height where it overlays the WebView (native iOS), else the layout viewport below the\n * keyboard's top edge — 0 where the WebView resizes for it (native Android), the covered part\n * where only the visual viewport shrinks (a mobile browser).\n */\n readonly covered: number;\n /** The keyboard's top edge in viewport CSS px (`BfKeyboardService.keyboardTop()`). */\n readonly keyboardTop: number;\n}\n\n/**\n * The keyboard's effect on an overlay anchored to the bottom of the layout viewport, `null` while\n * it is hidden. Re-read on every keyboard signal change and on window and `visualViewport`\n * resizes, since the layout and visual viewport heights are not signals. Call in an injection\n * context.\n */\nexport function injectKeyboardCover(): Signal<BfKeyboardCover | null> {\n const keyboard = inject(BfKeyboardService);\n const view = inject(DOCUMENT).defaultView;\n const resizes = signal(0);\n const onResize = (): void => resizes.update((count) => count + 1);\n view?.addEventListener('resize', onResize);\n view?.visualViewport?.addEventListener('resize', onResize);\n inject(DestroyRef).onDestroy(() => {\n view?.removeEventListener('resize', onResize);\n view?.visualViewport?.removeEventListener('resize', onResize);\n });\n\n return computed(\n () => {\n resizes();\n if (!keyboard.visible()) {\n return null;\n }\n const height = keyboard.height();\n const keyboardTop = Math.round(keyboard.keyboardTop());\n const covered = keyboard.overlaysContent()\n ? height\n : Math.max(0, Math.round((view?.innerHeight ?? 0) - keyboardTop));\n return { covered, keyboardTop };\n },\n {\n equal: (a, b) => a?.covered === b?.covered && a?.keyboardTop === b?.keyboardTop,\n },\n );\n}\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\nimport { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n Injector,\n TemplateRef,\n type Type,\n afterNextRender,\n afterRenderEffect,\n computed,\n inject,\n viewChild,\n} from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfKeyboardAssistDirective } from '../../content/keyboard-assist/keyboard-assist.directive';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { prefersReducedMotion } from '../../navigation/transitions';\nimport { IOS_EASING } from '../../motion';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { injectKeyboardCover } from '../keyboard-cover';\nimport {\n BF_OVERLAY_DATA,\n BfOverlayRef,\n type BfOverlayTemplateContext,\n OVERLAY_DURATION,\n OVERLAY_EASING,\n ɵwatchPanelHeading as watchPanelHeading,\n} from '@bitforgehq/ui/overlay';\nimport { BfSheetRef } from './sheet-ref';\n\n/** What `BfSheetService` hands the `bf-sheet` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfSheetConfig {\n content: Type<unknown> | TemplateRef<unknown>;\n /** Ascending fractions of the viewport height. */\n snapPoints: number[];\n initial: number;\n dismissible: boolean;\n /** The caller's data, re-provided to the content as `BF_OVERLAY_DATA`. */\n data: unknown;\n /** No `ariaLabel` was given: the panel is named by the content's first heading, if any. */\n labelledByHeading: boolean;\n ref: BfSheetRef<unknown>;\n}\n\n/** Travel (px) a press must clear before it becomes a drag, so taps still reach buttons. */\nconst SLOP = 6;\n/** Release velocity (px/ms) above which the direction of the fling picks the snap point. */\nconst FLING_VELOCITY = 0.3;\n/** Past the top point (and below the lowest when not dismissible) the sheet moves at 30 %. */\nconst RUBBER_BAND = 0.3;\n/** A release this far below the lowest point, relative to its height, dismisses the sheet. */\nconst DISMISS_FRACTION = 0.25;\n/** Pointer samples older than this (ms) are ignored for the release velocity. */\nconst VELOCITY_WINDOW = 100;\n/** Gap (px) kept between a keyboard-capped sheet's top edge and the safe area's. */\nconst KEYBOARD_TOP_GAP = 8;\n/** The lift follows the keyboard: about as long as its slide, on the iOS curve. */\nconst KEYBOARD_LIFT_TRANSITION = `translate 250ms ${IOS_EASING}`;\n\ninterface BfSheetGesture {\n readonly pointerId: number;\n readonly startX: number;\n readonly startY: number;\n /** Visible height of the sheet when the press started. */\n readonly startHeight: number;\n /** The handle row or a `[data-bf-sheet-drag]` element, or the scrolling content. */\n readonly source: 'handle' | 'content';\n readonly reducedMotion: boolean;\n claimed: boolean;\n /** Raw, un-rubber-banded visible height the pointer asks for. */\n proposed: number;\n samples: { t: number; y: number }[];\n}\n\n/**\n * The bottom-sheet container that `BfSheetService.open()` renders on the overlay core: a\n * bottom-anchored `bg-surface` card with a grabber, resting at snap points (fractions of the\n * viewport height, applied as `--bf-sheet-height`) and rendering the content component or\n * template with `BF_OVERLAY_DATA` and `BfSheetRef` injectable. Dragging the handle row, any\n * element marked `data-bf-sheet-drag` (a header) or the content (unless it can scroll up) moves\n * the sheet with Pointer Events under pointer capture: rubber-banded above the top point, released\n * onto the nearest point (a fling faster than 0.3 px/ms picks the direction), and dismissed when\n * released more than 25 % below the lowest point or flung down from it — if the sheet is\n * dismissible. The content scrolls only at the top snap point, and at every point while the soft\n * keyboard is up. While the keyboard overlays the sheet's bottom edge (iOS, a mobile browser) the\n * sheet is lifted by the covered height (`translate`, its bottom safe-area padding dropped); while\n * the keyboard is up its height is capped at the keyboard's top edge minus the safe-area top minus\n * 8 px (a drag starts from that capped height), and the content region's `bfKeyboardAssist`\n * scrolls the focused control 24 px clear of the keyboard. The lift follows the keyboard (250 ms,\n * instant under `prefers-reduced-motion`). Hiding the keyboard restores the snap height at once\n * (the drop is instant, so the top edge stays on screen) and scrolls content left scrolled below\n * the top point back to its top. With two or more snap points the grabber is a button: a press,\n * `Enter` or `Space` moves one point up (from the top point back to the lowest), the arrow keys\n * step, `Home` and `End` jump; it is named `expand` or `collapse` (`BF_LABELS`) after the next\n * press, then `sheetPosition`, and every settle on another point announces `sheetPosition`\n * politely. A dismissible sheet starts with a visually hidden Close (`BF_LABELS.close`) that screen\n * readers reach and a keyboard user sees when focused. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfSheetService).open(NewTaskComponent, { snapPoints: [0.5, 0.92] });\n * @summary Bottom sheet with snap points, drag to dismiss and a backdrop\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-sheet',\n imports: [NgComponentOutlet, NgTemplateOutlet, BfKeyboardAssistDirective],\n // A fresh page context: a `bf-content` in the sheet never takes over the page's (it would become\n // a second `main` inside the dialog and replace the page's registered scroller).\n providers: [BfPageContext],\n host: {\n class:\n 'bg-surface rounded-t-card shadow-floating pb-safe-bottom relative flex h-(--bf-sheet-height) w-screen flex-col',\n },\n template: `\n @if (dismissible) {\n <button\n type=\"button\"\n data-bf-sheet-close\n data-bf-overlay-chrome\n class=\"focus-visible:bg-surface-muted focus-visible:text-label-sm focus-visible:text-text focus-visible:ring-ring sr-only focus-visible:not-sr-only focus-visible:absolute focus-visible:end-3 focus-visible:top-3 focus-visible:z-10 focus-visible:rounded-full focus-visible:px-3 focus-visible:py-1 focus-visible:ring-4 focus-visible:outline-none\"\n (click)=\"close()\"\n >\n {{ labels.close }}\n </button>\n }\n <div\n data-bf-sheet-handle\n class=\"group/bf-sheet-handle flex h-6 shrink-0 cursor-grab touch-none items-center justify-center active:cursor-grabbing\"\n >\n @if (adjustable) {\n <button\n type=\"button\"\n data-bf-sheet-grabber-button\n data-bf-overlay-chrome\n class=\"rounded-t-card focus-visible:ring-ring relative flex h-6 w-full cursor-grab items-center justify-center focus-visible:ring-4 focus-visible:outline-none focus-visible:ring-inset active:cursor-grabbing pointer-coarse:after:absolute pointer-coarse:after:inset-x-0 pointer-coarse:after:top-0 pointer-coarse:after:-bottom-5 pointer-coarse:after:content-['']\"\n [attr.aria-label]=\"grabberLabel()\"\n (click)=\"onGrabberClick($event)\"\n (keydown)=\"onGrabberKeydown($event)\"\n >\n <span\n aria-hidden=\"true\"\n data-bf-sheet-grabber\n class=\"bg-control-off group-hover/bf-sheet-handle:bg-text-muted h-1.25 w-9 rounded-full transition-colors forced-colors:bg-[ButtonText]\"\n ></span>\n </button>\n } @else {\n <div\n aria-hidden=\"true\"\n data-bf-sheet-grabber\n class=\"bg-control-off group-hover/bf-sheet-handle:bg-text-muted h-1.25 w-9 rounded-full transition-colors forced-colors:bg-[ButtonText]\"\n ></div>\n }\n </div>\n <div #content data-bf-sheet-content bfKeyboardAssist [class]=\"contentClass()\">\n @if (template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"templateContext\"\n [ngTemplateOutletInjector]=\"contentInjector\"\n />\n } @else if (component) {\n <ng-container\n [ngComponentOutlet]=\"component\"\n [ngComponentOutletInjector]=\"contentInjector\"\n />\n }\n </div>\n `,\n})\nexport class BfSheetComponent {\n private readonly config = inject(BF_OVERLAY_DATA) as BfSheetConfig;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly document = inject(DOCUMENT);\n private readonly keyboard = inject(BfKeyboardService);\n private readonly keyboardCover = injectKeyboardCover();\n private readonly contentElement = viewChild.required<ElementRef<HTMLElement>>('content');\n private readonly contentAssist = viewChild.required(BfKeyboardAssistDirective);\n\n protected readonly template =\n this.config.content instanceof TemplateRef ? this.config.content : null;\n protected readonly component =\n this.config.content instanceof TemplateRef ? null : this.config.content;\n protected readonly contentInjector = Injector.create({\n parent: inject(Injector),\n providers: [\n { provide: BF_OVERLAY_DATA, useValue: this.config.data },\n { provide: BfOverlayRef, useValue: this.config.ref },\n { provide: BfSheetRef, useValue: this.config.ref },\n ],\n });\n protected readonly templateContext: BfOverlayTemplateContext = {\n $implicit: this.config.ref,\n data: this.config.data,\n };\n\n private readonly lastIndex = this.config.snapPoints.length - 1;\n protected readonly labels = inject(BF_LABELS);\n protected readonly dismissible = this.config.dismissible;\n /** Two or more snap points: the grabber is a button that moves between them. */\n protected readonly adjustable = this.lastIndex > 0;\n private readonly announcer = inject(LiveAnnouncer);\n /**\n * What the next press does (collapse at the top point, else expand), then where the sheet is:\n * \"Expand, Sheet at 1 of 2\".\n */\n protected readonly grabberLabel = computed(() => {\n const index = this.config.ref.snapIndex();\n const action = index === this.lastIndex ? this.labels.collapse : this.labels.expand;\n return `${action}, ${this.labels.sheetPosition(index + 1, this.lastIndex + 1)}`;\n });\n /** A drag that started on the grabber button ends without the click that may follow it. */\n private suppressGrabberClick = false;\n /**\n * The content scrolls at the top snap point and whenever the keyboard is up (the sheet may be\n * capped below its content); otherwise drags move the sheet.\n */\n private readonly contentScrolls = computed(\n () => this.config.ref.snapIndex() === this.lastIndex || this.keyboard.visible(),\n );\n protected readonly contentClass = computed(\n () =>\n `min-h-0 flex-1 overscroll-contain ${\n this.contentScrolls() ? 'overflow-y-auto' : 'overflow-hidden'\n }`,\n );\n\n private index = this.config.initial;\n /** Rendered height (px) and downward translation (px) below the lowest point. */\n private height = 0;\n private offset = 0;\n private gesture: BfSheetGesture | null = null;\n private settleAnimation: Animation | null = null;\n\n constructor() {\n // Content in a dialog is not the document's `main`.\n inject(BfPageContext, { self: true }).mainLandmark.set(false);\n this.apply(this.heights()[this.index] ?? 0, 0);\n this.config.ref.setSnapIndex(this.index);\n this.config.ref.connect((index) => this.snapTo(index));\n this.host.addEventListener('pointerdown', this.onPointerDown);\n this.host.addEventListener('transitionend', this.onTransitionEnd);\n const view = this.document.defaultView;\n view?.addEventListener('resize', this.onResize);\n // The host's own styles, so they are written in the render phase.\n afterRenderEffect({ write: () => this.applyKeyboard() });\n if (this.config.labelledByHeading) {\n let stopWatching: (() => void) | null = null;\n afterNextRender(() => {\n const panel = this.host.closest<HTMLElement>('.bf-overlay-panel');\n if (panel) {\n stopWatching = watchPanelHeading(panel);\n }\n });\n inject(DestroyRef).onDestroy(() => stopWatching?.());\n }\n // Content left scrolled while the keyboard was up starts at its top again once the keyboard\n // hides below the top point, since nothing could scroll it back there.\n let keyboardWasUp = false;\n afterRenderEffect(() => {\n const keyboardUp = this.keyboard.visible();\n if (keyboardWasUp && !keyboardUp && !this.contentScrolls()) {\n this.contentElement().nativeElement.scrollTop = 0;\n }\n keyboardWasUp = keyboardUp;\n });\n inject(DestroyRef).onDestroy(() => {\n this.endGesture();\n this.settleAnimation?.cancel();\n this.host.removeEventListener('pointerdown', this.onPointerDown);\n this.host.removeEventListener('transitionend', this.onTransitionEnd);\n view?.removeEventListener('resize', this.onResize);\n });\n }\n\n /**\n * Lifts the sheet by the part of the viewport the keyboard covers and caps its height at the\n * keyboard's top edge; both go away with the keyboard, at once. Separate from\n * `--bf-sheet-height`, so drag and snap logic never see them.\n */\n private applyKeyboard(): void {\n const cover = this.keyboardCover();\n if (this.config.ref.isClosing) {\n // A keyboard hiding during the exit would make the sliding sheet jump by the lift.\n return;\n }\n const style = this.host.style;\n // Only the lift slides. The drop is instant, together with the cap: an animated drop without\n // the cap put the full-height sheet's top edge above the screen for most of the slide.\n if (cover && !prefersReducedMotion(this.document.defaultView)) {\n style.setProperty('transition', KEYBOARD_LIFT_TRANSITION);\n } else {\n style.removeProperty('transition');\n }\n if (cover && cover.covered > 0) {\n style.setProperty('translate', `0 ${-cover.covered}px`);\n // The keyboard covers the home indicator: no bottom safe-area padding above it.\n style.setProperty('padding-bottom', '0px');\n } else {\n style.removeProperty('translate');\n style.removeProperty('padding-bottom');\n }\n if (cover) {\n style.setProperty(\n 'max-height',\n `calc(${cover.keyboardTop}px - var(--spacing-safe-top) - ${KEYBOARD_TOP_GAP}px)`,\n );\n } else {\n style.removeProperty('max-height');\n }\n }\n\n /** The content's keyboard assist measured a sheet still sliding up: it measures once more. */\n private readonly onTransitionEnd = (event: TransitionEvent): void => {\n if (event.target === this.host && event.propertyName === 'translate') {\n this.contentAssist().refresh();\n }\n };\n\n protected close(): void {\n this.config.ref.close();\n }\n\n /**\n * A press: one point higher, or from the top point back to the lowest. The click a pointer drag\n * may leave behind is swallowed; a keyboard or assistive-technology click (`detail` 0) never is.\n */\n protected onGrabberClick(event: MouseEvent): void {\n const afterDrag = this.suppressGrabberClick;\n this.suppressGrabberClick = false;\n if (afterDrag && event.detail !== 0) {\n return;\n }\n const index = this.config.ref.snapIndex();\n this.config.ref.snapTo(index >= this.lastIndex ? 0 : index + 1);\n }\n\n /** Arrow keys step between points (never past the ends, never dismissing); Home and End jump. */\n protected onGrabberKeydown(event: KeyboardEvent): void {\n this.suppressGrabberClick = false;\n if (event.altKey || event.ctrlKey || event.metaKey) {\n // Browser and assistive-technology shortcuts, not the sheet's.\n return;\n }\n const index = this.config.ref.snapIndex();\n const targets: Record<string, number> = {\n ArrowUp: Math.min(index + 1, this.lastIndex),\n ArrowDown: Math.max(index - 1, 0),\n Home: 0,\n End: this.lastIndex,\n };\n const target = targets[event.key];\n if (target === undefined) {\n return;\n }\n event.preventDefault();\n this.config.ref.snapTo(target);\n }\n\n private snapTo(index: number): void {\n const clamped = Math.min(Math.max(Math.trunc(index), 0), this.lastIndex);\n this.endGesture();\n this.settleTo(clamped, prefersReducedMotion(this.document.defaultView));\n }\n\n /** Viewport height with the keyboard hidden: the `100lvh` probe, else `innerHeight`. */\n private viewportHeight(): number {\n const rest = this.keyboard.restHeight();\n return rest > 0 ? rest : (this.document.defaultView?.innerHeight ?? 0);\n }\n\n private heights(): number[] {\n const viewport = this.viewportHeight();\n return this.config.snapPoints.map((fraction) => Math.round(fraction * viewport));\n }\n\n private readonly onResize = (): void => {\n if (!this.gesture) {\n this.settleAnimation?.cancel();\n this.apply(this.heights()[this.index] ?? 0, 0);\n }\n };\n\n private readonly onPointerDown = (event: PointerEvent): void => {\n if (\n this.gesture ||\n !event.isPrimary ||\n this.config.ref.isClosing ||\n (event.pointerType === 'mouse' && event.button !== 0)\n ) {\n return;\n }\n this.suppressGrabberClick = false;\n const target = event.target instanceof Element ? event.target : null;\n const content = this.contentElement().nativeElement;\n const dragHandle = target?.closest('[data-bf-sheet-handle], [data-bf-sheet-drag]');\n let source: BfSheetGesture['source'];\n if (dragHandle && this.host.contains(dragHandle)) {\n source = 'handle';\n } else if (target && content.contains(target)) {\n source = 'content';\n } else {\n return;\n }\n this.captureAnimatedHeight();\n const visible = this.renderedHeight() - this.offset;\n this.gesture = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n startHeight: visible,\n source,\n reducedMotion: prefersReducedMotion(this.document.defaultView),\n claimed: false,\n proposed: visible,\n samples: [{ t: performance.now(), y: event.clientY }],\n };\n this.host.addEventListener('pointermove', this.onPointerMove);\n this.host.addEventListener('pointerup', this.onPointerUp);\n this.host.addEventListener('pointercancel', this.onPointerCancel);\n this.host.addEventListener('lostpointercapture', this.onLostPointerCapture);\n this.host.addEventListener('touchmove', this.touchMoveGuard, { passive: false });\n };\n\n /**\n * Whether a content drag may move the sheet: always while the content does not scroll (below the\n * top snap point, keyboard hidden), and otherwise only downward while it is scrolled to 0.\n */\n private contentMayDrag(dy: number): boolean {\n if (!this.contentScrolls()) {\n return true;\n }\n return dy > 0 && this.contentElement().nativeElement.scrollTop <= 0;\n }\n\n /**\n * Non-passive `touchmove` guard, attached only while a press is tracked (the refresher's\n * pattern): keeps the browser from taking the drag as a pan, which would end the pointer stream\n * with `pointercancel`. Content drags the sheet does not want (upward at the top point,\n * horizontal) are left to scroll.\n */\n private readonly touchMoveGuard = (event: TouchEvent): void => {\n const gesture = this.gesture;\n if (!gesture || !event.cancelable) {\n return;\n }\n if (gesture.claimed || gesture.source === 'handle') {\n event.preventDefault();\n return;\n }\n const touch = event.touches[0];\n if (!touch) {\n return;\n }\n const dy = touch.clientY - gesture.startY;\n const dx = touch.clientX - gesture.startX;\n if (Math.abs(dy) > Math.abs(dx) && this.contentMayDrag(dy)) {\n event.preventDefault();\n }\n };\n\n private readonly onPointerMove = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n const dy = event.clientY - gesture.startY;\n if (!gesture.claimed) {\n const dx = event.clientX - gesture.startX;\n if (Math.abs(dy) <= SLOP && Math.abs(dx) <= SLOP) {\n return;\n }\n if (\n Math.abs(dx) >= Math.abs(dy) ||\n (gesture.source === 'content' && !this.contentMayDrag(dy))\n ) {\n // A horizontal move, or a content drag the content scrolls itself: not the sheet's.\n this.endGesture();\n return;\n }\n gesture.claimed = true;\n try {\n this.host.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic pointers (tests) have no active pointer to capture.\n }\n }\n if (event.cancelable) {\n event.preventDefault();\n }\n const now = performance.now();\n gesture.samples = [\n ...gesture.samples.filter((s) => now - s.t <= VELOCITY_WINDOW),\n { t: now, y: event.clientY },\n ];\n gesture.proposed = gesture.startHeight - dy;\n // Direct manipulation: follows the pointer even under prefers-reduced-motion.\n this.show(gesture.proposed);\n };\n\n /** Renders a proposed visible height: rubber-banded past the ends, translated below the lowest. */\n private show(proposed: number): void {\n const heights = this.heights();\n const lowest = heights[0] ?? 0;\n const top = heights[heights.length - 1] ?? 0;\n if (proposed > top) {\n this.apply(top + (proposed - top) * RUBBER_BAND, 0);\n } else if (proposed >= lowest) {\n this.apply(proposed, 0);\n } else {\n const below = lowest - proposed;\n this.apply(lowest, this.config.dismissible ? below : below * RUBBER_BAND);\n }\n }\n\n private readonly onPointerUp = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n this.endGesture();\n if (!gesture.claimed) {\n // An unclaimed press (under the slop) ends as a click: on the grabber button it cycles.\n return;\n }\n this.suppressGrabberClick = true;\n this.release(gesture, this.velocity(gesture));\n };\n\n private readonly onPointerCancel = (event: PointerEvent): void => {\n const gesture = this.gesture;\n if (gesture?.pointerId !== event.pointerId) {\n return;\n }\n this.endGesture();\n if (gesture.claimed) {\n this.settleTo(this.index, gesture.reducedMotion);\n }\n };\n\n /** Only the host losing the capture it took ends the drag (a child's implicit capture bubbles). */\n private readonly onLostPointerCapture = (event: PointerEvent): void => {\n if (event.target === this.host) {\n this.onPointerCancel(event);\n }\n };\n\n /** Downward velocity (px/ms) over the last samples; 0 when they carry no elapsed time. */\n private velocity(gesture: BfSheetGesture): number {\n const first = gesture.samples[0];\n const last = gesture.samples[gesture.samples.length - 1];\n if (!first || !last || last.t <= first.t) {\n return 0;\n }\n return (last.y - first.y) / (last.t - first.t);\n }\n\n private release(gesture: BfSheetGesture, velocity: number): void {\n const heights = this.heights();\n const lowest = heights[0] ?? 0;\n const proposed = gesture.proposed;\n let target: number | null;\n if (velocity > FLING_VELOCITY) {\n // Downward fling: the next point below, or out.\n const below = heights.filter((h) => h < proposed).length - 1;\n target = below >= 0 ? below : null;\n } else if (velocity < -FLING_VELOCITY) {\n const above = heights.findIndex((h) => h > proposed);\n target = above >= 0 ? above : this.lastIndex;\n } else if (proposed < lowest * (1 - DISMISS_FRACTION)) {\n target = null;\n } else {\n target = this.nearest(heights, proposed);\n }\n if (target === null) {\n if (this.config.dismissible) {\n this.config.ref.close();\n return;\n }\n target = 0;\n }\n this.settleTo(target, gesture.reducedMotion);\n }\n\n private nearest(heights: number[], proposed: number): number {\n let best = 0;\n heights.forEach((h, i) => {\n if (Math.abs(h - proposed) < Math.abs((heights[best] ?? 0) - proposed)) {\n best = i;\n }\n });\n return best;\n }\n\n /** Animates (instantly under reduced motion) to the snap point at `index`. */\n private settleTo(index: number, reducedMotion: boolean): void {\n const target = this.heights()[index] ?? 0;\n const fromHeight = this.height;\n const fromOffset = this.offset;\n this.settleAnimation?.cancel();\n this.settleAnimation = null;\n const moved = index !== this.index;\n this.index = index;\n this.config.ref.setSnapIndex(index);\n if (moved && this.adjustable) {\n // The new position is announced politely whichever way it came (button, key, drag, snapTo).\n void this.announcer.announce(\n this.labels.sheetPosition(index + 1, this.lastIndex + 1),\n 'polite',\n );\n }\n // The end state is written at once; the animation (no fill) plays over it.\n this.apply(target, 0);\n if (reducedMotion || (fromHeight === target && fromOffset === 0)) {\n return;\n }\n const animation = this.host.animate(\n [\n { height: `${fromHeight}px`, transform: `translate3d(0,${fromOffset}px,0)` },\n { height: `${target}px`, transform: 'translate3d(0,0,0)' },\n ],\n { duration: OVERLAY_DURATION, easing: OVERLAY_EASING },\n );\n this.settleAnimation = animation;\n void animation.finished.then(\n () => {\n if (this.settleAnimation === animation) {\n this.settleAnimation = null;\n }\n },\n () => {\n // Cancelled by a newer drag or settle, which owns the sheet now.\n },\n );\n }\n\n /**\n * The height the sheet shows: its snap height, or less while the keyboard caps it (`max-height`),\n * so a drag of a capped sheet moves it from the first pixel.\n */\n private renderedHeight(): number {\n const rendered = this.host.offsetHeight;\n return this.keyboardCover() && rendered > 0 ? Math.min(this.height, rendered) : this.height;\n }\n\n /** A press during a settle continues from where the animation had got to. */\n private captureAnimatedHeight(): void {\n if (!this.settleAnimation) {\n return;\n }\n const animated = parseFloat(\n this.document.defaultView?.getComputedStyle(this.host).height ?? '',\n );\n this.settleAnimation.cancel();\n this.settleAnimation = null;\n if (Number.isFinite(animated) && animated > 0) {\n this.apply(animated, 0);\n }\n }\n\n private apply(height: number, offset: number): void {\n this.height = height;\n this.offset = offset;\n this.host.style.setProperty('--bf-sheet-height', `${height}px`);\n this.host.style.transform = offset > 0 ? `translate3d(0,${offset}px,0)` : '';\n }\n\n private endGesture(): void {\n const pointerId = this.gesture?.pointerId;\n this.host.removeEventListener('pointermove', this.onPointerMove);\n this.host.removeEventListener('pointerup', this.onPointerUp);\n this.host.removeEventListener('pointercancel', this.onPointerCancel);\n this.host.removeEventListener('lostpointercapture', this.onLostPointerCapture);\n this.host.removeEventListener('touchmove', this.touchMoveGuard);\n if (pointerId !== undefined) {\n try {\n this.host.releasePointerCapture(pointerId);\n } catch {\n // Already released (or never captured, e.g. a synthetic pointer in tests).\n }\n }\n this.gesture = null;\n }\n}\n","import { Injectable, Injector, type TemplateRef, type Type, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport {\n type BfComponentOverlayRef,\n type BfOverlayAutoFocus,\n BfOverlayService,\n BfOverlayStack,\n slideDown,\n slideUp,\n} from '@bitforgehq/ui/overlay';\nimport { BfSheetComponent, type BfSheetConfig } from './sheet.component';\nimport { BfSheetRef } from './sheet-ref';\n\n/**\n * Where focus goes when a sheet opens: the sheet itself (`panel`), so no soft keyboard comes up\n * and a screen reader starts reading at the sheet, or its first field (`first-field`): the element\n * marked `[autofocus]`/`[cdkFocusInitial]`, else the first focusable element of the content, which\n * raises the keyboard on Android when it is a text field.\n *\n * @summary Where focus lands when a sheet opens, the sheet itself or its first field\n * @since 0.3.0\n */\nexport type BfSheetAutoFocus = 'panel' | 'first-field';\n\n/** The overlay core's focus target for each sheet setting. */\nconst AUTO_FOCUS: Record<BfSheetAutoFocus, BfOverlayAutoFocus> = {\n panel: 'panel',\n 'first-field': 'first-tabbable',\n};\n\n/**\n * Options of `BfSheetService.open()`.\n *\n * @summary Options of a bottom sheet: where it rests, how it closes and its name\n * @since 0.2.0\n */\nexport interface BfSheetOptions {\n /**\n * Heights the sheet rests at, as fractions of the viewport height (default `[0.5, 0.92]`).\n * @default [0.5, 0.92]\n */\n snapPoints?: number[];\n /**\n * Index of the snap point the sheet opens at (default 0).\n * @default 0\n */\n initial?: number;\n /**\n * The backdrop, `Escape`, the hardware back button and a downward drag close the sheet\n * (default true). When false only `close()` does, or a navigation (see `closeOnNavigation`).\n * @default true\n */\n dismissible?: boolean;\n /** Injected into the content as `BF_OVERLAY_DATA`. */\n data?: unknown;\n /**\n * A navigation to another URL path (a pushed or popped page, a tab switch) closes the sheet with\n * no result (default true), also a non-dismissible one.\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Accessible name of the sheet. Without it the sheet is named by the first heading in its\n * content (`aria-labelledby`), else by `provideBfLabels()`'s `sheet` (\"Sheet\").\n */\n ariaLabel?: string;\n /**\n * Where focus goes on open: `panel` (default) focuses the sheet itself (a `role=\"dialog\"` with a\n * name and `tabindex=\"-1\"`), so a screen reader lands inside it and no soft keyboard hides it;\n * `first-field` focuses the element marked `[autofocus]`/`[cdkFocusInitial]`, else the first\n * focusable element of the content (never the sheet's own Close and grabber), which raises the\n * keyboard on Android when it is a text field.\n * @default 'panel'\n */\n autoFocus?: BfSheetAutoFocus;\n /**\n * Injector the sheet resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Snap points used when none (or no valid ones) are given.\n *\n * @summary The snap points a bottom sheet rests at when none are given, half and full\n * @since 0.2.0\n */\nexport const BF_SHEET_DEFAULT_SNAP_POINTS: readonly number[] = [0.5, 0.92];\n\nfunction normalizeSnapPoints(points: readonly number[] | undefined): number[] {\n const valid = (points ?? [])\n .filter((p) => Number.isFinite(p) && p > 0)\n .map((p) => Math.min(p, 1))\n .sort((a, b) => a - b);\n const unique = valid.filter((p, i) => i === 0 || p !== valid[i - 1]);\n return unique.length > 0 ? unique : [...BF_SHEET_DEFAULT_SNAP_POINTS];\n}\n\n/**\n * Opens bottom sheets: a component or template in a `bf-sheet` container on the overlay core,\n * bottom-anchored over a backdrop, resting at snap points (fractions of the viewport height).\n * The content injects `BF_OVERLAY_DATA` and `BfSheetRef` (also as `BfOverlayRef`) to read its\n * data, move between snap points and close with a result. A dismissible sheet (the default)\n * closes on a backdrop tap, `Escape`, the hardware back button and a drag below its lowest\n * point; a non-dismissible one only through `close()`, and swallows the back button while open.\n * A navigation to another URL path closes either (`closeOnNavigation`). On open, focus goes to the\n * sheet itself, so no soft keyboard comes up (`autoFocus: 'first-field'` focuses the first field).\n *\n * @example\n * const ref = inject(BfSheetService).open<NewTaskComponent, Task>(NewTaskComponent, {\n * snapPoints: [0.5, 0.92],\n * data: { listId },\n * });\n * const task = await ref.afterClosed;\n\n * @since 0.2.0\n * @summary Opens bottom sheets that rest at snap points and close with a result\n */\n@Injectable({ providedIn: 'root' })\nexport class BfSheetService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens `content` (a component or a template) in a bottom sheet.\n * @param content The component or template to render in the sheet.\n * @param options Where the sheet rests, how it closes and its name.\n */\n open<C, T = unknown>(\n content: Type<C> | TemplateRef<unknown>,\n options: BfSheetOptions = {},\n ): BfSheetRef<T> {\n const snapPoints = normalizeSnapPoints(options.snapPoints);\n const initial = Math.min(Math.max(Math.trunc(options.initial ?? 0), 0), snapPoints.length - 1);\n const dismissible = options.dismissible ?? true;\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n\n let overlay: BfComponentOverlayRef<BfSheetComponent> | null = null;\n let releaseBackGuard: (() => void) | null = null;\n const ref = new BfSheetRef<T>(\n {\n close: async (): Promise<void> => {\n releaseBackGuard?.();\n if (overlay) {\n overlay.close();\n await overlay.afterClosed;\n }\n },\n updatePosition: (): void => overlay?.updatePosition(),\n isClosing: (): boolean => overlay?.isClosing ?? false,\n },\n initial,\n );\n\n const config: BfSheetConfig = {\n content,\n snapPoints,\n initial,\n dismissible,\n data: options.data,\n labelledByHeading: !options.ariaLabel,\n ref,\n };\n overlay = this.overlays.open(BfSheetComponent, {\n data: config,\n position: 'bottom',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: dismissible,\n dismissOnEscape: dismissible,\n closeOnNavigation: options.closeOnNavigation,\n ariaLabel: options.ariaLabel ?? labels.sheet,\n autoFocus: AUTO_FOCUS[options.autoFocus ?? 'panel'],\n injector,\n enter: (el) => slideUp(el),\n exit: (el) => slideDown(el),\n });\n\n if (!dismissible) {\n // The stack's handler would close the sheet; claim the back button without closing it\n // instead, so it does not fall through to the page behind either.\n this.stack.releaseBack(overlay);\n releaseBackGuard = this.backButton.register(100, () => true);\n }\n // However the overlay closes (backdrop, Escape, back, drag, close()), settle the sheet ref.\n void overlay.afterClosed.then(() => ref.close());\n return ref;\n }\n}\n","import { NgComponentOutlet } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n type Type,\n afterNextRender,\n computed,\n inject,\n} from '@angular/core';\nimport { BfPageContext } from '../../navigation/page/page-context';\nimport { injectKeyboardCover } from '../keyboard-cover';\nimport {\n BF_OVERLAY_DATA,\n BfOverlayRef,\n ɵwatchPanelHeading as watchPanelHeading,\n} from '@bitforgehq/ui/overlay';\n\n/**\n * Where `bf-modal` sits: a page-sheet card, or edge to edge.\n *\n * @summary How a modal is presented: a page-sheet card over the stack, or fullscreen\n * @since 0.2.0\n */\nexport type BfModalPresentation = 'sheet' | 'fullscreen';\n\n/** What `BfModalService` hands the `bf-modal` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfModalConfig {\n content: Type<unknown>;\n presentation: BfModalPresentation;\n /** The caller's data, re-provided to the content as `BF_OVERLAY_DATA`. */\n data: unknown;\n /** No `ariaLabel` was given: the panel is named by the content's first heading, if any. */\n labelledByHeading: boolean;\n}\n\nconst HOST_CLASSES: Record<BfModalPresentation, string> = {\n // A transparent border is invisible here but forced colours paints it CanvasText, giving the\n // sheet card a top edge once its shadow and the parked page stack behind it both flatten to\n // Canvas; the fullscreen frame has no exposed edge (it fills the viewport) and needs none.\n sheet:\n 'absolute inset-x-0 bottom-0 top-[calc(var(--spacing-safe-top)+10px)] bg-bg rounded-t-card shadow-floating border border-transparent flex flex-col overflow-hidden',\n fullscreen: 'absolute inset-0 bg-bg flex flex-col',\n};\n\n/**\n * The modal frame that `BfModalService.open()` renders on the overlay core, `position:\n * 'fullscreen'`: a `sheet` presentation (default) is a card whose top sits 10 px below the top\n * safe area (UIKit's page-sheet inset; `bg-bg rounded-t-card shadow-floating`) over the parked page stack, which scales down and\n * grows rounded corners behind it (`--bf-modal-depth`, written by `BfOverlayStack`, consumed by\n * `bf-stack-outlet`'s host rule); `fullscreen` covers the viewport edge to edge with no scale.\n * Like `bf-page`, it provides a fresh `BfPageContext` so the content's `bf-header` and\n * `bf-content` work inside it (with `mainLandmark` false, so its content is no `main` landmark,\n * and in the `sheet` presentation with `safeAreaTop` false, so the header adds no status-bar band\n * inside the card) — the content supplies its own header (typically a close\n * `bf-icon-button` in `start`) and content region. While the soft keyboard overlays the bottom of\n * a `sheet` card (iOS, a mobile browser), the card is padded at the bottom by the covered height,\n * so the content's `bf-content` ends at the keyboard's top edge and its keyboard assist reveals\n * the focused control there. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfModalService).open(EditTaskComponent, { data: { taskId } });\n * @summary Modal overlay shown as an inset card over the scaled-down page, or full screen\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-modal',\n imports: [NgComponentOutlet],\n host: { '[class]': 'hostClasses', '[style.padding-bottom.px]': 'keyboardPadding()' },\n providers: [BfPageContext],\n template: `<ng-container\n [ngComponentOutlet]=\"config.content\"\n [ngComponentOutletInjector]=\"contentInjector\"\n />`,\n})\nexport class BfModalComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfModalConfig;\n private readonly outerRef = inject(BfOverlayRef);\n private readonly keyboardCover = injectKeyboardCover();\n\n protected readonly hostClasses = HOST_CLASSES[this.config.presentation];\n /** A sheet card ends at the keyboard's top edge; a fullscreen frame leaves it to `bf-content`. */\n protected readonly keyboardPadding = computed(() => {\n const covered = this.keyboardCover()?.covered ?? 0;\n return this.config.presentation === 'sheet' && covered > 0 ? covered : null;\n });\n protected readonly contentInjector = Injector.create({\n parent: inject(Injector),\n providers: [\n { provide: BF_OVERLAY_DATA, useValue: this.config.data },\n { provide: BfOverlayRef, useValue: this.outerRef },\n ],\n });\n\n constructor() {\n const pageContext = inject(BfPageContext, { self: true });\n // Content in a dialog is not the document's `main` (the page behind stays it, inert).\n pageContext.mainLandmark.set(false);\n // The card starts below the status bar, so its header needs no safe-area band of its own.\n if (this.config.presentation === 'sheet') {\n pageContext.safeAreaTop.set(false);\n }\n if (this.config.labelledByHeading) {\n const host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n let stopWatching: (() => void) | null = null;\n afterNextRender(() => {\n const panel = host.closest<HTMLElement>('.bf-overlay-panel');\n if (panel) {\n stopWatching = watchPanelHeading(panel);\n }\n });\n inject(DestroyRef).onDestroy(() => stopWatching?.());\n }\n }\n}\n","import { Injectable, Injector, type Type, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport { BfPlatformService, resolveShellMode } from '../../platform/platform.service';\nimport { BF_SHELL_CONFIG, BF_SHELL_DEFAULTS } from '../../shell/shell-config';\nimport {\n type BfOverlayRef,\n BfOverlayService,\n BfOverlayStack,\n fadeDown,\n fadeUp,\n slideDown,\n slideUp,\n} from '@bitforgehq/ui/overlay';\nimport { BfModalComponent, type BfModalConfig, type BfModalPresentation } from './modal.component';\n\n/**\n * Options of `BfModalService.open()`.\n *\n * @summary Options of a modal: its data, presentation, dismissal and accessible name\n * @since 0.2.0\n */\nexport interface BfModalOptions {\n /** Injected into the content as `BF_OVERLAY_DATA`. */\n data?: unknown;\n /**\n * A page-sheet card over the parked page stack (default), or a fullscreen frame.\n * @default 'sheet'\n */\n presentation?: BfModalPresentation;\n /**\n * `Escape` and the hardware back button close the modal (default true); with false only the\n * content's own close control does, or a navigation (see `closeOnNavigation`), and back is\n * swallowed while the modal is open instead of\n * falling through to the page behind. There is no backdrop dismiss or drag-to-dismiss.\n * @default true\n */\n dismissible?: boolean;\n /**\n * A navigation to another URL path closes the modal with no result (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Accessible name of the modal panel. Without it the modal is named by the first heading in\n * its content (`aria-labelledby`, e.g. the `bf-header` title), else by `provideBfLabels()`'s\n * `modal` (\"Modal\").\n */\n ariaLabel?: string;\n /**\n * Injector the modal resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Opens modals: a component in a `bf-modal` frame on the overlay core (`position: 'fullscreen'`,\n * no backdrop dismiss). `presentation: 'sheet'` (default) is a page-sheet card 10 px below the\n * top safe area over the parked page stack, which in iOS mode scales down and grows rounded corners\n * behind it (`countsAsModal: true`, driving `--bf-modal-depth` on the registered\n * `bf-stack-outlet` roots; Material mode leaves the page unscaled); `'fullscreen'` covers the\n * viewport edge to edge and does not count towards that depth. iOS mode\n * slides the card up and down; Material mode fades it up (and down) 16 px. The content supplies\n * its own `bf-header` (`largeTitle=\"false\"`, typically a close `bf-icon-button` in `start`) and\n * `bf-content` — `bf-modal` provides a `BfPageContext` for them, like `bf-page`. It injects the\n * caller's `data` as `BF_OVERLAY_DATA` and closes itself with a result through the injected\n * `BfOverlayRef`, which `open()` also returns. A navigation to another URL path closes it with no\n * result (`closeOnNavigation`).\n *\n * @example\n * const ref = inject(BfModalService).open<EditTaskComponent, Task>(EditTaskComponent, {\n * data: { taskId },\n * });\n * const saved = await ref.afterClosed;\n * @since 0.2.0\n * @summary Opens a component as a modal page sheet over the stack, or fullscreen\n */\n@Injectable({ providedIn: 'root' })\nexport class BfModalService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly platform = inject(BfPlatformService);\n private readonly injector = inject(Injector);\n private readonly config = inject(BF_SHELL_CONFIG, { optional: true }) ?? BF_SHELL_DEFAULTS;\n\n /**\n * Opens `component` as a modal.\n * @param component The component to render; it injects `BfOverlayRef` and `BF_OVERLAY_DATA`.\n * @param options The modal's data, presentation, dismissal and name.\n */\n open<C, T = unknown>(component: Type<C>, options: BfModalOptions = {}): BfOverlayRef<T> {\n const presentation = options.presentation ?? 'sheet';\n const dismissible = options.dismissible ?? true;\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n const mode = resolveShellMode(this.config, this.platform);\n const ios = mode === 'ios';\n\n const modalConfig: BfModalConfig = {\n content: component,\n presentation,\n data: options.data,\n labelledByHeading: !options.ariaLabel,\n };\n const ref = this.overlays.open<BfModalComponent, T>(BfModalComponent, {\n data: modalConfig,\n position: 'fullscreen',\n // The parked page scales down behind a page sheet in iOS mode only; Material fades up\n // over it.\n countsAsModal: presentation === 'sheet' && ios,\n backPriority: 100,\n dismissOnBackdrop: false,\n dismissOnEscape: dismissible,\n closeOnNavigation: options.closeOnNavigation,\n ariaLabel: options.ariaLabel ?? labels.modal,\n injector,\n enter: ios ? (el) => slideUp(el) : (el) => fadeUp(el),\n exit: ios ? (el) => slideDown(el) : (el) => fadeDown(el),\n });\n\n if (!dismissible) {\n // The stack's handler would close the modal; claim the back button without closing it\n // instead, so it does not fall through to the page behind either.\n this.stack.releaseBack(ref);\n const releaseBackGuard = this.backButton.register(100, () => true);\n void ref.afterClosed.then(() => releaseBackGuard());\n }\n return ref;\n }\n}\n","import { Component, ElementRef, afterRenderEffect, inject } from '@angular/core';\nimport { BfKeyboardService } from '../../platform/keyboard.service';\nimport { BF_OVERLAY_DATA } from '@bitforgehq/ui/overlay';\n\n/**\n * Where the toast pill anchors in the viewport.\n *\n * @summary Where a mobile toast pill anchors in the viewport, at the bottom or top\n * @since 0.2.0\n */\nexport type BfToastPosition = 'bottom' | 'top';\n\n/** The action rendered as a real button inside the pill; internal. */\nexport interface BfToastActionConfig {\n label: string;\n run: () => void;\n}\n\n/** What holds a toast's timer: the pointer over it, or focus inside it; internal. */\nexport type BfToastHoldSource = 'pointer' | 'focus';\n\n/** What `BfToastService` hands the `bf-toast` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfToastConfig {\n message: string;\n action: BfToastActionConfig | null;\n position: BfToastPosition;\n /** Told when the pointer or focus enters (`held: true`) or leaves the pill; pauses the timer. */\n hold?: (source: BfToastHoldSource, held: boolean) => void;\n /** `Escape` pressed while focus is inside the pill; the service dismisses the toast. */\n escape?: () => void;\n}\n\nfunction relatedTarget(event: Event): EventTarget | null {\n return event instanceof FocusEvent ? event.relatedTarget : null;\n}\n\nconst POSITION_CLASS: Record<BfToastPosition, string> = {\n bottom: 'bf-toast-bottom-offset',\n // A margin, never padding: the host is the pill, and a padding-top would override its own `py-3`.\n top: 'mt-[calc(var(--spacing-safe-top)+var(--spacing)*4)]',\n};\n\n/**\n * The status pill that `BfToastService.show()` renders on the overlay core (`passive: true`, no\n * backdrop, no focus trap, no inert page and no back handler — it never steals focus): an ink\n * `bg-text text-bg rounded-full shadow-floating` pill holding the message (truncated to two lines)\n * and an optional action. The pill is not a live region: the service announces each toast once\n * through CDK `LiveAnnouncer`. The pointer over it or focus inside it pauses its timer, and\n * `Escape` with focus inside dismisses it. `position: 'bottom'` (default) sits 16 px above the\n * tab-bar inset (`--bf-tab-bar-inset-global`, published on `<html>` by `bf-tabs`, which leaves\n * 8 px above the floating bar, so the toast clears it by 24 px) and the soft keyboard\n * (`--bf-toast-keyboard` on the host: `BfKeyboardService.height()` where the keyboard overlays\n * the WebView — native iOS — and 0 where the WebView already shrinks); `position: 'top'` sits\n * 16 px below the status bar (a top margin of the safe-area inset plus 16 px, outside the pill)\n * instead. Not used directly in templates.\n *\n * @example\n * const ref = inject(BfToastService).show({ message: 'Saved' });\n * @summary Passive status pill with an optional action that never steals focus\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-toast',\n host: {\n '[class]': 'classes',\n '(pointerenter)': \"hold('pointer', true)\",\n '(pointerleave)': \"hold('pointer', false)\",\n '(focusin)': \"hold('focus', true)\",\n '(focusout)': 'focusLeft($event)',\n '(keydown)': 'keydown($event)',\n },\n styles: `\n :host.bf-toast-bottom-offset {\n margin-bottom: calc(\n var(--bf-tab-bar-inset-global, 0px) + 16px + var(--bf-toast-keyboard, 0px)\n );\n }\n `,\n template: `\n <span class=\"line-clamp-2 min-w-0 flex-1\">{{ config.message }}</span>\n @if (config.action; as action) {\n <button\n type=\"button\"\n class=\"text-bg focus-visible:ring-ring min-h-hit -mx-3 -my-2.5 inline-flex shrink-0 cursor-pointer items-center rounded-full px-3 font-bold underline underline-offset-4 transition-opacity hover:opacity-80 focus-visible:ring-4 focus-visible:outline-hidden\"\n (click)=\"action.run()\"\n >\n {{ action.label }}\n </button>\n }\n `,\n})\nexport class BfToastComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfToastConfig;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly keyboard = inject(BfKeyboardService);\n\n protected readonly classes =\n 'bg-text text-bg rounded-full shadow-floating px-5 py-3 text-body flex items-center gap-4 ' +\n `max-w-[calc(100vw-2*var(--spacing-gutter))] ${POSITION_CLASS[this.config.position]}`;\n\n constructor() {\n // The host's own style, so it is written in the render phase.\n afterRenderEffect({\n write: () => {\n // Android and the web resize the viewport for the keyboard, so its bottom already is the\n // keyboard's top; only an overlaying keyboard (native iOS) needs lifting over.\n const lift = this.keyboard.overlaysContent() ? this.keyboard.height() : 0;\n this.host.style.setProperty('--bf-toast-keyboard', `${lift}px`);\n },\n });\n }\n\n protected hold(source: BfToastHoldSource, held: boolean): void {\n this.config.hold?.(source, held);\n }\n\n /** Focus moving between elements inside the pill still holds it. */\n protected focusLeft(event: Event): void {\n const to = relatedTarget(event);\n if (to instanceof Node && this.host.contains(to)) {\n return;\n }\n this.hold('focus', false);\n }\n\n /** `Escape` with focus inside dismisses the toast; claimed, so no overlay underneath sees it. */\n protected keydown(event: Event): void {\n if (!(event instanceof KeyboardEvent) || event.key !== 'Escape' || !this.config.escape) {\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n this.config.escape();\n }\n}\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\nimport { DOCUMENT, DestroyRef, Injectable, inject } from '@angular/core';\nimport { REDUCED_DURATION, prefersReducedMotion } from '../../navigation/transitions';\nimport {\n type BfToastAction,\n type BfToastDismissReason,\n type BfToastRef,\n BfOverlayService,\n fadeOut,\n OVERLAY_DURATION,\n OVERLAY_EASING,\n type ɵBfToastHandle,\n ɵBfToastQueue,\n type ɵBfToastView,\n} from '@bitforgehq/ui/overlay';\nimport { BfToastComponent, type BfToastConfig, type BfToastPosition } from './toast.component';\n\n/**\n * Options of `BfToastService.show()`.\n *\n * @summary Options of a mobile toast: its message, an action, its duration and position\n * @since 0.2.0\n */\nexport interface BfToastOptions {\n /** Text of the pill, truncated to two lines. */\n message: string;\n /** An optional action button. */\n action?: BfToastAction;\n /**\n * Milliseconds before the toast auto-dismisses, not counting time the pointer is over it or\n * focus is inside it; `0` keeps it until dismissed or its action fires (default 3000 without an\n * action, `0` with one).\n * @default 3000\n */\n duration?: number;\n /**\n * Where the pill anchors (default `'bottom'`).\n * @default 'bottom'\n */\n position?: BfToastPosition;\n}\n\n/** Fades `el` in while it slides 12 px in from the edge it anchors to; reduced motion: fade only. */\nfunction toastEnter(position: BfToastPosition) {\n return (el: HTMLElement): Animation => {\n if (prefersReducedMotion(el.ownerDocument.defaultView)) {\n return el.animate([{ opacity: 0 }, { opacity: 1 }], {\n duration: REDUCED_DURATION,\n easing: 'linear',\n fill: 'backwards',\n });\n }\n const offset = position === 'bottom' ? '12px' : '-12px';\n return el.animate(\n [\n { opacity: 0, transform: `translate3d(0,${offset},0)` },\n { opacity: 1, transform: 'translate3d(0,0,0)' },\n ],\n { duration: OVERLAY_DURATION, easing: OVERLAY_EASING, fill: 'backwards' },\n );\n };\n}\n\n/**\n * Queues status pills: `show(options)` renders `bf-toast` on the overlay core (`passive: true` —\n * no backdrop, no focus trap, the page stays interactive, and it never counts towards\n * `BfOverlayStack.count()`) and returns a `BfToastRef`. One toast is visible at a time; later\n * calls queue in order and show once the current one ends. A toast auto-dismisses after\n * `duration` ms (default 3000, resolving `'timeout'`); a toast with an action defaults to\n * `duration: 0`, which keeps it until `dismiss()`, `Escape` inside it (`'dismiss'`) or its action\n * button (`'action'`) ends it; such a persistent toast never blocks the queue — the next `show()`\n * replaces it (`'dismiss'`). The timer pauses while the pointer is over the toast or focus is\n * inside it and resumes with the remaining time. When a toast still holding focus closes, focus\n * returns to the element focused last outside it. Each toast is announced once, politely, through\n * CDK `LiveAnnouncer` as it shows. A navigation (a pushed or popped page, a tab switch) never\n * closes it.\n *\n * @example\n * const ref = inject(BfToastService).show({\n * message: 'Task deleted',\n * action: { label: 'Undo', handler: () => restore(task) },\n * });\n * const reason = await ref.afterDismissed;\n * @since 0.2.0\n * @summary Shows brief toast pills on phones, one at a time, queued in order\n */\n@Injectable({ providedIn: 'root' })\nexport class BfToastService {\n private readonly overlays = inject(BfOverlayService);\n // The shared engine (timing, hold, replace, focus return, announcements, teardown), one at a time.\n private readonly queue = new ɵBfToastQueue<BfToastOptions>(\n { present: (view, options) => this.present(view, options.position ?? 'bottom') },\n {\n maxVisible: 1,\n announcer: inject(LiveAnnouncer),\n document: inject(DOCUMENT),\n destroyRef: inject(DestroyRef),\n },\n );\n\n /**\n * Queues `options` as a toast; shows it now if none is visible, else once the current one ends.\n * A persistent toast (`duration` 0, the default with an action) never blocks the queue: a later\n * `show()` dismisses it (`'dismiss'`), whether it is visible or still waiting.\n * @param options The toast's message, action, duration and position.\n * @returns The toast's ref, to dismiss it or await why it ended.\n */\n show(options: BfToastOptions): BfToastRef {\n return this.queue.show(options);\n }\n\n /** Opens one passive overlay per toast, exactly as before the engine moved to `/overlay`. */\n private present(view: ɵBfToastView, position: BfToastPosition): ɵBfToastHandle {\n const config: BfToastConfig = {\n message: view.message,\n action: view.action,\n position,\n hold: (source, held) => view.hold(source, held),\n escape: () => view.escape(),\n };\n const overlayRef = this.overlays.open<BfToastComponent, BfToastDismissReason>(\n BfToastComponent,\n {\n data: config,\n passive: true,\n // A \"Saved\" toast survives the navigation that follows the save.\n closeOnNavigation: false,\n position,\n // No pane role and no live region on the pill: `LiveAnnouncer` announces the toast once.\n enter: toastEnter(position),\n exit: (el) => fadeOut(el),\n },\n );\n const element: unknown = overlayRef.componentRef.location.nativeElement;\n const host = element instanceof HTMLElement ? element : null;\n return {\n host,\n close: async (reason) => {\n overlayRef.close(reason);\n await overlayRef.afterClosed;\n },\n };\n }\n}\n","import { Component, inject } from '@angular/core';\nimport { BfButtonComponent, BfListComponent, BfListItemComponent } from '@bitforgehq/ui';\nimport { BfIconComponent } from '@bitforgehq/ui/icons';\nimport { type IconNode } from 'lucide';\nimport { BF_OVERLAY_DATA, BfOverlayRef } from '@bitforgehq/ui/overlay';\n\n/**\n * The role of an action sheet row: `destructive` renders it in `text-danger`, `cancel` makes it the\n * separate cancel button.\n *\n * @summary The role of an action sheet row, a destructive action or the cancel button\n * @since 0.3.0\n */\nexport type BfActionSheetActionRole = 'destructive' | 'cancel';\n\n/**\n * One row of an action sheet, or its cancel action when `role: 'cancel'`.\n *\n * @summary One row of an action sheet, or its cancel button, with its answer and role\n * @since 0.2.0\n */\nexport interface BfActionSheetAction {\n /** What `open()` resolves with when the row is tapped (default `label`); keeps the answer\n * stable when the label is translated or two rows share a label. Ignored on the cancel action,\n * which always resolves `undefined`. */\n id?: string;\n /** Row text; also what `open()` resolves with when the row is tapped and has no `id`. */\n label: string;\n /** Icon rendered in the row's leading slot. */\n icon?: IconNode;\n /**\n * `destructive` renders the row's label in `text-danger`; `cancel` renders it as the separate\n * cancel button instead of a row (a plain row when omitted).\n */\n role?: BfActionSheetActionRole;\n}\n\n/** What `BfActionSheetService` hands the `bf-action-sheet` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfActionSheetConfig {\n /** Heading above the rows, or `undefined` for none. */\n title: string | undefined;\n /** The caller's non-cancel actions, in order. */\n actions: BfActionSheetAction[];\n /** Label of the cancel button: the caller's `cancel`-role action, or \"Cancel\". */\n cancelLabel: string;\n}\n\n/**\n * The action sheet panel that `BfActionSheetService.open()` renders on the overlay core\n * (`position: 'bottom'`): an optional title above a `bf-list` of interactive rows for the\n * actions (destructive ones in `text-danger`, an icon in the leading slot) and a separate\n * `tonal` `bf-button` for the cancel action — rendered even when the caller passes none,\n * labelled \"Cancel\", which takes the initial focus. Tapping a row resolves the overlay with its\n * `id ?? label`; the cancel button, like\n * the backdrop, `Escape` and the hardware back button, resolves `undefined`. Not used directly\n * in templates.\n *\n * @example\n * const answer = await inject(BfActionSheetService).open({\n * title: 'Delete list?',\n * actions: [{ id: 'delete', label: 'Delete', role: 'destructive' }],\n * });\n * @summary Bottom panel of labelled actions with a separate cancel button, destructive ones shown in red\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-action-sheet',\n imports: [BfListComponent, BfListItemComponent, BfButtonComponent, BfIconComponent],\n /* px-3 (12) is the panel inset, deliberately tighter than the 16 px gutter: the rows add their own\n 14 px padding, so row text still sits 26 px from the screen edge. */\n host: {\n class:\n 'bg-surface rounded-t-card shadow-floating flex w-screen flex-col px-3 pt-3 pb-[max(var(--spacing-safe-bottom),16px)]',\n },\n template: `\n @if (config.title) {\n <p class=\"text-caption text-text-muted px-3 pb-2\">{{ config.title }}</p>\n }\n <bf-list [inset]=\"false\">\n @for (action of config.actions; track $index) {\n <bf-list-item\n [interactive]=\"true\"\n [title]=\"action.role === 'destructive' ? '' : action.label\"\n (clicked)=\"select(action)\"\n >\n @if (action.icon) {\n <bf-icon bfSlot=\"leading\" [icon]=\"action.icon\" />\n }\n @if (action.role === 'destructive') {\n <span class=\"text-danger text-label truncate font-bold\">{{ action.label }}</span>\n }\n </bf-list-item>\n }\n </bf-list>\n <bf-button variant=\"tonal\" class=\"mt-2\" [fullWidth]=\"true\" autofocus (clicked)=\"cancel()\">\n {{ config.cancelLabel }}\n </bf-button>\n `,\n})\nexport class BfActionSheetComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfActionSheetConfig;\n private readonly ref = inject(BfOverlayRef) as BfOverlayRef<string>;\n\n protected select(action: BfActionSheetAction): void {\n this.ref.close(action.id ?? action.label);\n }\n\n protected cancel(): void {\n this.ref.close();\n }\n}\n","import { Injectable, Injector, inject } from '@angular/core';\nimport { BF_LABELS } from '@bitforgehq/ui';\nimport { BfOverlayService, slideDown, slideUp } from '@bitforgehq/ui/overlay';\nimport {\n BfActionSheetComponent,\n type BfActionSheetAction,\n type BfActionSheetConfig,\n} from './action-sheet.component';\n\n/**\n * Options of `BfActionSheetService.open()`.\n *\n * @summary Options of an action sheet: its title, its rows and when it closes\n * @since 0.2.0\n */\nexport interface BfActionSheetOptions {\n /** Heading above the rows; also the panel's accessible name (default `provideBfLabels()`'s\n * `actions`, \"Actions\"). */\n title?: string;\n /**\n * Rows, in order; a `cancel`-role entry becomes the separate cancel button instead of a row.\n * Only the first `cancel`-role entry is used; any further ones are ignored.\n */\n actions: BfActionSheetAction[];\n /**\n * A navigation to another URL path closes the sheet, resolving `undefined` (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Injector the action sheet resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\n/**\n * Opens action sheets: a bottom-anchored list of actions on the overlay core (`position:\n * 'bottom'`, not counted as a modal). Renders `title` above a `bf-list` of the non-cancel\n * `actions` as interactive rows (destructive ones in `text-danger`, an icon in the leading slot\n * from `icon`), and a separate `tonal` `bf-button` for the cancel action — the caller's\n * first `cancel`-role entry if any (further ones are ignored), else a synthesized one labelled `provideBfLabels()`'s `cancel` (\"Cancel\").\n * Focus starts on the cancel button. `open()` resolves with the tapped row's `id ?? label`; the\n * cancel button, a backdrop tap, `Escape`, the hardware back button and a navigation to another\n * URL path all resolve `undefined`.\n *\n * @example\n * const answer = await inject(BfActionSheetService).open({\n * title: 'Delete list?',\n * actions: [\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * { id: 'rename', label: 'Rename' },\n * ],\n * });\n * @since 0.2.0\n * @summary Opens bottom action sheets that resolve with the row the user tapped\n */\n@Injectable({ providedIn: 'root' })\nexport class BfActionSheetService {\n private readonly overlays = inject(BfOverlayService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens the action sheet described by `options`; resolves with the tapped row's `id ?? label`,\n * or `undefined` when cancelled.\n * @param options The title and rows of the sheet.\n */\n open(options: BfActionSheetOptions): Promise<string | undefined> {\n const injector = options.injector ?? this.injector;\n const labels = injector.get(BF_LABELS);\n const cancelAction = options.actions.find((action) => action.role === 'cancel');\n const config: BfActionSheetConfig = {\n title: options.title,\n actions: options.actions.filter((action) => action.role !== 'cancel'),\n cancelLabel: cancelAction?.label ?? labels.cancel,\n };\n const ref = this.overlays.open<BfActionSheetComponent, string>(BfActionSheetComponent, {\n data: config,\n position: 'bottom',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: true,\n dismissOnEscape: true,\n closeOnNavigation: options.closeOnNavigation,\n role: 'dialog',\n ariaLabel: options.title ?? labels.actions,\n injector,\n enter: (el) => slideUp(el),\n exit: (el) => slideDown(el),\n });\n return ref.afterClosed;\n }\n}\n","import { Component, inject } from '@angular/core';\nimport { BfButtonComponent, type BfButtonVariant } from '@bitforgehq/ui';\nimport { BF_OVERLAY_DATA, BfOverlayRef } from '@bitforgehq/ui/overlay';\nimport { type BfAlertDialogButtonRole } from './alert-dialog.service';\n\n/** A button of an alert dialog, with the variant `BfAlertDialogService` resolved for it. */\nexport interface BfAlertDialogButtonConfig {\n /** Button text. */\n label: string;\n /** What `open()` resolves with when it is tapped: the caller's `id ?? label`. */\n answer: string;\n /** `cancel` answers `Escape`/the hardware back button; `destructive` renders `danger`. */\n role?: BfAlertDialogButtonRole;\n variant: BfButtonVariant;\n /** Whether it takes the initial focus: the `cancel`-role button, else the first one. */\n initial: boolean;\n}\n\n/** What `BfAlertDialogService` hands the `bf-alert-dialog` container (as its `BF_OVERLAY_DATA`); internal. */\nexport interface BfAlertDialogConfig {\n title: string;\n message: string | undefined;\n /** Id of the `h2` title, referenced by the panel's `aria-labelledby`. */\n titleId: string;\n /** Id of the message `p`, referenced by the panel's `aria-describedby` when there is a message. */\n messageId: string;\n buttons: BfAlertDialogButtonConfig[];\n}\n\n/**\n * The alert dialog card that `BfAlertDialogService.open()` renders on the overlay core\n * (`position: 'center'`): an `h2.text-heading` title, an optional `text-body text-text-secondary`\n * message, and a right-aligned row of `bf-button`s (`cancel`-role `text`, `destructive`-role\n * `danger`, the first plain button `primary`, further plain ones `tonal`). Focus starts on the\n * `cancel`-role button, else the first one. Tapping a button resolves the overlay with its\n * `id ?? label`. There is no backdrop dismiss; `Escape` and the hardware\n * back button are handled by `BfAlertDialogService` (the cancel button's answer, or claimed and\n * ignored when there is none). Not used directly in templates.\n *\n * @example\n * const answer = await inject(BfAlertDialogService).open({\n * title: 'Delete list?',\n * message: 'This cannot be undone.',\n * buttons: [\n * { id: 'cancel', label: 'Cancel', role: 'cancel' },\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * ],\n * });\n * @summary Centred confirmation dialog with a title, message and a row of role-styled buttons\n * @category Overlays\n * @since 0.2.0\n */\n@Component({\n selector: 'bf-alert-dialog',\n imports: [BfButtonComponent],\n host: {\n // A transparent border is invisible here but forced colours paints it CanvasText, giving the\n // card an edge once its shadow and the scrim behind it both flatten to Canvas.\n class:\n 'w-75 max-w-[calc(100vw-2*var(--spacing-gutter))] bg-surface rounded-card shadow-floating border border-transparent p-6 flex flex-col gap-4',\n },\n template: `\n <h2 [id]=\"config.titleId\" class=\"text-heading\">{{ config.title }}</h2>\n @if (config.message) {\n <p [id]=\"config.messageId\" class=\"text-body text-text-secondary\">{{ config.message }}</p>\n }\n <div class=\"flex justify-end gap-2\">\n @for (button of config.buttons; track $index) {\n <bf-button\n [variant]=\"button.variant\"\n [attr.autofocus]=\"button.initial ? '' : null\"\n (clicked)=\"select(button)\"\n >\n {{ button.label }}\n </bf-button>\n }\n </div>\n `,\n})\nexport class BfAlertDialogComponent {\n protected readonly config = inject(BF_OVERLAY_DATA) as BfAlertDialogConfig;\n private readonly ref = inject(BfOverlayRef) as BfOverlayRef<string>;\n\n protected select(button: BfAlertDialogButtonConfig): void {\n this.ref.close(button.answer);\n }\n}\n","import { Injectable, Injector, inject } from '@angular/core';\nimport { BF_LABELS, type BfButtonVariant } from '@bitforgehq/ui';\nimport { BfBackButtonService } from '../../platform/back-button.service';\nimport { BfOverlayService, BfOverlayStack, fadeOut, scaleIn } from '@bitforgehq/ui/overlay';\nimport {\n BfAlertDialogComponent,\n type BfAlertDialogButtonConfig,\n type BfAlertDialogConfig,\n} from './alert-dialog.component';\n\n/**\n * The role of an alert dialog button: `cancel` answers `Escape` and the hardware back button,\n * `destructive` renders `danger`.\n *\n * @summary The role of an alert dialog button, the cancel answer or a destructive action\n * @since 0.3.0\n */\nexport type BfAlertDialogButtonRole = 'cancel' | 'destructive';\n\n/**\n * A button of `BfAlertDialogService.open()`.\n *\n * @summary One button of an alert dialog: its answer, its label and its role\n * @since 0.2.0\n */\nexport interface BfAlertDialogButton {\n /** What `open()` resolves with when the button is tapped (default `label`); keeps the answer\n * stable when the label is translated or two buttons share a label. */\n id?: string;\n /** Button text; also the value `open()` resolves with when it is tapped and has no `id`. */\n label: string;\n /**\n * `cancel` renders as a `text` button, takes the initial focus and is what `Escape`/the\n * hardware back button answer with; `destructive` renders `danger` (a plain `primary`/`tonal` button when omitted).\n */\n role?: BfAlertDialogButtonRole;\n}\n\n/**\n * Options of `BfAlertDialogService.open()`.\n *\n * @summary Options of an alert dialog: its title, message, buttons and when it closes\n * @since 0.2.0\n */\nexport interface BfAlertDialogOptions {\n /** Dialog heading, rendered as the `h2` the panel's `aria-labelledby` points to. */\n title: string;\n /** Body text under the title. */\n message?: string;\n /** Buttons, left to right (default one button labelled `provideBfLabels()`'s `ok`, \"OK\"). */\n buttons?: BfAlertDialogButton[];\n /**\n * A navigation to another URL path closes the alert, resolving the cancel button's answer, or\n * `undefined` without one (default true).\n * @default true\n */\n closeOnNavigation?: boolean;\n /**\n * Injector the dialog resolves its labels (`BF_LABELS`) from and the parent of its content's\n * injector (default the root injector). Pass the opening component's `inject(Injector)` so a\n * feature-level `provideBfLabels()` and other feature providers reach it.\n */\n injector?: Injector;\n}\n\nlet nextTitleId = 0;\n\n/**\n * Opens alert dialogs: a centred, 300 px card on the overlay core (`position: 'center'`, no\n * backdrop dismiss). Renders `title` as an `h2`, an optional `message`, and `buttons` (default\n * one `ok` button from the app's `provideBfLabels()`, \"OK\") as a right-aligned row — `cancel`-role `text`, `destructive`-role\n * `danger`, the first plain button `primary`, further plain ones `tonal`. Focus starts on the\n * `cancel`-role button, else the first one. `open()` resolves with the tapped button's\n * `id ?? label`. `Escape` and the hardware back button resolve with the cancel button's answer\n * when `buttons` has one; with none, they are claimed and ignored — an alert\n * with no cancel button can only be answered by tapping a button, or closed by a navigation to\n * another URL path, which resolves the cancel button's answer, else `undefined`.\n *\n * @example\n * const answer = await inject(BfAlertDialogService).open({\n * title: 'Delete list?',\n * message: 'This cannot be undone.',\n * buttons: [\n * { id: 'cancel', label: 'Cancel', role: 'cancel' },\n * { id: 'delete', label: 'Delete', role: 'destructive' },\n * ],\n * });\n * @since 0.2.0\n * @summary Opens small centred alerts that resolve with the button the user tapped\n */\n@Injectable({ providedIn: 'root' })\nexport class BfAlertDialogService {\n private readonly overlays = inject(BfOverlayService);\n private readonly stack = inject(BfOverlayStack);\n private readonly backButton = inject(BfBackButtonService);\n private readonly injector = inject(Injector);\n\n /**\n * Opens the alert described by `options`; resolves with the tapped button's `id ?? label`, or\n * `undefined` when a navigation closes an alert that has no cancel button.\n * @param options The title, message and buttons of the alert.\n */\n open(options: BfAlertDialogOptions): Promise<string | undefined> {\n const injector = options.injector ?? this.injector;\n const buttons = options.buttons?.length\n ? options.buttons\n : [{ label: injector.get(BF_LABELS).ok }];\n const cancelIndex = buttons.findIndex((button) => button.role === 'cancel');\n const initialIndex = cancelIndex >= 0 ? cancelIndex : 0;\n const id = nextTitleId++;\n const titleId = `bf-alert-dialog-title-${id}`;\n const messageId = `bf-alert-dialog-message-${id}`;\n\n let primaryAssigned = false;\n const buttonConfigs: BfAlertDialogButtonConfig[] = buttons.map((button, index) => {\n let variant: BfButtonVariant;\n if (button.role === 'cancel') {\n variant = 'text';\n } else if (button.role === 'destructive') {\n variant = 'danger';\n } else if (!primaryAssigned) {\n variant = 'primary';\n primaryAssigned = true;\n } else {\n variant = 'tonal';\n }\n return {\n label: button.label,\n answer: button.id ?? button.label,\n role: button.role,\n variant,\n initial: index === initialIndex,\n };\n });\n\n const config: BfAlertDialogConfig = {\n title: options.title,\n message: options.message,\n titleId,\n messageId,\n buttons: buttonConfigs,\n };\n\n const answerCancel = (): void => {\n const cancel = buttonConfigs[cancelIndex];\n if (cancel) {\n ref.close(cancel.answer);\n }\n };\n const ref = this.overlays.open<BfAlertDialogComponent, string>(BfAlertDialogComponent, {\n data: config,\n position: 'center',\n countsAsModal: false,\n backPriority: 100,\n dismissOnBackdrop: false,\n dismissOnEscape: false,\n // Escape answers like the cancel button, or is claimed and ignored without one.\n onEscape: answerCancel,\n closeOnNavigation: options.closeOnNavigation,\n // A navigation always closes it: with the cancel answer, else with no answer.\n onNavigation: () => ref.close(buttonConfigs[cancelIndex]?.answer),\n role: 'alertdialog',\n injector,\n ariaLabelledBy: titleId,\n ariaDescribedBy: options.message ? messageId : undefined,\n enter: (el) => scaleIn(el),\n exit: (el) => fadeOut(el),\n });\n\n // The stack's handler would close the dialog unconditionally; claim the back button instead\n // and answer with the cancel button's answer when there is one, otherwise ignore it entirely — an\n // alert with no cancel button is only answered by tapping a button.\n this.stack.releaseBack(ref);\n const unregisterBack = this.backButton.register(100, () => {\n answerCancel();\n return true;\n });\n void ref.afterClosed.then(() => unregisterBack());\n\n return ref.afterClosed;\n }\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { type Routes, provideRouter, withComponentInputBinding } from '@angular/router';\nimport { ɵprovideBfDocumentReset } from '@bitforgehq/ui';\nimport { BF_THEME_STORAGE, createMemoryThemeStorage } from '../platform/platform.service';\nimport { provideBfShell, withBfShellRouting } from '../shell/provide-bf-shell';\nimport { type BfShellConfig } from '../shell/shell-config';\n\n/**\n * Options of `provideBfShellTesting()`.\n *\n * @summary Options of the shell test providers: the routes and the shell settings\n * @since 0.3.0\n */\nexport interface BfShellTestingOptions {\n /**\n * The routes of the test router.\n * @default []\n */\n routes?: Routes;\n /** Shell settings over the test defaults (`swipeBack: false`, `hapticsOnTabChange: false`). */\n config?: Partial<BfShellConfig>;\n}\n\n/**\n * Test providers for an app on the mobile shell: `provideRouter(routes ?? [],\n * withComponentInputBinding(), withBfShellRouting())` plus `provideBfShell({ swipeBack: false,\n * hapticsOnTabChange: false, ...config })`, an in-memory `BF_THEME_STORAGE` (so a theme change\n * never reaches `localStorage`), and the same document cleanup as `provideBfTesting()` from\n * `@bitforgehq/ui/testing` on teardown (`dark` and `cdk-global-scrollblock` on `<html>`, `inert` on\n * body children, stray overlay panes). Like that cleanup, it needs TestBed's default\n * `teardown: { destroyAfterEach: true }`. Use it instead of the app's own shell providers in\n * `TestBed.configureTestingModule()`.\n *\n * @example\n * TestBed.configureTestingModule({\n * providers: [provideBfShellTesting({ routes: [{ path: 'inbox', component: InboxPage }] })],\n * });\n * @param options The routes and the shell settings.\n * @returns The environment providers for `TestBed.configureTestingModule()`.\n * @summary Test providers for the mobile shell: router, shell, in-memory theme and cleanup\n * @since 0.3.0\n */\nexport function provideBfShellTesting(options: BfShellTestingOptions = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideRouter(options.routes ?? [], withComponentInputBinding(), withBfShellRouting()),\n provideBfShell({ swipeBack: false, hapticsOnTabChange: false, ...options.config }),\n { provide: BF_THEME_STORAGE, useFactory: createMemoryThemeStorage },\n ɵprovideBfDocumentReset(),\n ]);\n}\n"],"mappings":";;;;;;;;;;;;AA0GA,MAAa,yBAAyB;AAQtC,MAAa,oBAA6C;CACxD,MAAM;CACN,WAAW;CACX,sBAAsB;CACtB,oBAAoB;CACpB,aAAa,EAAE,UAAU,uBAAsB;CAC/C,gBAAgB;CAChB,YAAY;CACZ,cAAc;AACf;AAQD,MAAa,kBAAkB,IAAI,eAA8B,iBAAiB;AClGlF,MAAM,mBACJ;AAEF,MAAM,qBAA6D,CACjE,CAAC,iBAAiB,GAAG,GACrB,CAAC,iBAAiB,IAAI,CACvB;AAED,SAAS,WAAW,UAAgB;CAClC,OAAO,aAAa,SAAS,aAAa,YAAY,WAAW;AACnE;AAGA,MAAa,2BAA2B;AAcxC,MAAa,mBAAmB;AAOhC,SAAS,mBAAmB,UAAkB;CAC5C,MAAM,OAAO,SAAS,KAAK,cAA+B,yBAAuB;CACjF,IAAI,CAAC,MAAM;EACT,MAAM,UAAU,SAAS,cAAc,MAAM;EAC7C,QAAQ,OAAO;EACf,QAAQ,UAAU;EAClB,SAAS,KAAK,YAAY,OAAO;EACjC;CACF;CACA,MAAM,aAAa,KAAK,QACrB,MAAM,GAAG,CAAC,CACV,KAAK,cAAc,UAAU,KAAI,CAAE,CAAC,CACpC,QAAQ,cAAc,UAAU,SAAS,CAAC;CAC7C,IAAI,UAAU;CACd,KAAK,MAAM,CAAC,KAAK,UAAU,oBAAoB;EAC7C,MAAM,SAAS,GAAG,IAAG,GAAI;EACzB,MAAM,QAAQ,WAAW,WACtB,cAAc,UAAU,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,KAAI,CAAE,CAAC,YAAW,MAAO,GAAG;EAErE,IAAI,UAAU,IAAI;GAChB,WAAW,KAAK,MAAM;GACtB,UAAU;EACZ,OAAO,IAAI,WAAW,MAAM,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,YAAW,MAAO,QAAQ;GACxE,WAAW,SAAS;GACpB,UAAU;EACZ;CACF;CACA,IAAI,SACF,KAAK,UAAU,WAAW,KAAK,IAAI;AAEvC;AAGA,MAAM,uBAAuB;AAwB7B,SAAgB,kBACd,MACA,UACA,cAA2B;CAE3B,IAAI,aAAa,cAAc,gBAAgB,KAAK,sBAClD,OAAO,gBAAgB;CAEzB,OAAO,OAAO,gBAAgB,OAAO,gBAAgB;AACvD;AAGA,SAAS,aAAa,WAAgC;CACpD,MAAM,QAAQ,gBAAgB,KAAK,WAAW,aAAa,EAAE;CAC7D,OAAO,QAAQ,OAAO,MAAM,EAAE,IAAI;AACpC;AAgCA,IAAa,oBAAb,MAAa,kBAAiB;CACX,WAAW,OAAO,QAAQ;CAC1B,cAAc,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE;CACxD,cAAc,OAAO,oBAAoB;CAEjD,WAAW,UAAU,iBAAgB;CAErC,WAAuB,WAAW,UAAU,YAAW,CAAE;CAEzD,OAAO,eAA6B,KAAK,aAAa,QAAQ,QAAQ,MAAM,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,gCAAA,CAAA,CAAA;CAK5E,QAAQ,KAAK,YAAY;CACjB,aAAa,KAAK,YAAY;CAGtC,OAAO,KAAK,YAAY;CAEjC,cAAA;EACE,IAAI,KAAK,gBAAgB,MACvB;EAEF,IAAI,KAAK,UACP,mBAAmB,KAAK,QAAQ;EAIlC,KAAK,YAAY,cAAa;EAC9B,IAAI,KAAK,YAAY,KAAK,YAAY,mBAAmB,OAAO;GAC9D,MAAM,QAAQ,aAAa,KAAK,SAAS,aAAa,SAAS;GAC/D,aAAY;IACV,MAAM,QAAQ,kBAAkB,KAAK,KAAI,GAAI,KAAK,UAAU,KAAK;IAGjE,KAAK,WAAU;IACf,AAAK,KAAK,gBAAgB,KAAK;GACjC,CAAC;EACH;CACF;CAGQ,MAAM,gBAAgB,OAAsB;EAClD,IAAI;GAEF,MAAM,WAAW,SAAS,EAAE,MAAK,CAAE;EACrC,SAAS,OAAO;GACd,QAAQ,MAAM,KAAK;EACrB;CACF;CAlDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,YADJ;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAD7B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;AAoElC,SAAgB,iBACd,QACA,UAA2B;CAE3B,OAAO,OAAO,SAAS,SAAS,SAAS,KAAI,IAAK,OAAO;AAC3D;AAaA,SAAgB,kBAAe;CAE7B,OAAO,iBADQ,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,KAAK,mBAC9B,OAAO,iBAAiB,CAAC;AAC3D;ACnOA,IAAa,sBAAb,MAAa,oBAAmB;CACb,WAAW,OAAO,iBAAiB;CACnC,aAAa,OAAO,UAAU;CAC9B,aACf,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,CAAC,EAAE,cAAc;CAC5C,WAAgC,CAAA;CACzC,WAAW;CAEnB,cAAA;EACE,IAAI,KAAK,SAAS,YAAY,KAAK,SAAS,aAAa,WACvD,AAAK,KAAK,qBAAoB;CAElC;CASA,SAAS,UAAkB,SAAyC;EAClE,MAAM,QAA2B;GAAE;GAAU,UAAU,KAAK;GAAY,KAAK;EAAO;EACpF,KAAK,SAAS,KAAK,KAAK;EACxB,aAAY;GACV,MAAM,QAAQ,KAAK,SAAS,QAAQ,KAAK;GACzC,IAAI,SAAS,GACX,KAAK,SAAS,OAAO,OAAO,CAAC;EAEjC;CACF;CAQA,MAAM,WAAQ;EACZ,MAAM,UAAU,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,MAChC,GAAG,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ;EAE9D,KAAK,MAAM,WAAW,SACpB,IAAI,MAAM,QAAQ,IAAG,GACnB;EAGJ,IAAI,KAAK,eAAe,QACtB,MAAM,KAAK,QAAO;OAElB,MAAM,KAAK,YAAW;CAE1B;CAEQ,MAAM,uBAAoB;EAChC,IAAI;GACF,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,WAAW,MAAM,IAAI,YAAY,oBAAmB;IACxD,AAAK,KAAK,SAAQ;GACpB,CAAC;GACD,KAAK,WAAW,gBAAgB,KAAK,SAAS,OAAM,CAAE;EACxD,SAAS,OAAO;GACd,QAAQ,MAAM,yEAAyE,KAAK;EAC9F;CACF;CAEQ,MAAM,cAAW;EACvB,IAAI;GACF,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,IAAI,YAAW;EACvB,SAAS,OAAO;GACd,QAAQ,MAAM,mDAAmD,KAAK;EACxE;CACF;CAEQ,MAAM,UAAO;EACnB,IAAI;GACF,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,IAAI,QAAO;EACnB,SAAS,OAAO;GACd,QAAQ,MAAM,+CAA+C,KAAK;EACpE;CACF;CAlFW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,YADN;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAD/B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;ACQlC,IAAa,mBAAb,MAAa,iBAAgB;CACV,kBAAkB,OAAO,iBAAiB;CACnD,gBAAsD;CAM9D,MAAM,OAAO,OAA2B;EACtC,MAAM,KAAK,IAAI,OAAO,EAAE,SAAS,aAAa,YAAW;GACvD,MAAM,MAAiD;IACrD,OAAO,MAAM;IACb,QAAQ,MAAM;IACd,OAAO,MAAM;GACd;GACD,MAAM,QAAQ,OAAO,EAAE,OAAO,IAAI,OAAM,CAAE;EAC5C,CAAC;CACH;CAGA,MAAM,YAAS;EACb,MAAM,KAAK,KAAK,EAAE,cAAc,QAAQ,iBAAgB,CAAE;CAC5D;CAMA,MAAM,aAAa,MAA+B;EAChD,MAAM,KAAK,IAAI,OAAO,EAAE,SAAS,kBAAkB,WAAU;GAC3D,MAAM,MAA2D;IAC/D,SAAS,KAAK;IACd,SAAS,KAAK;IACd,OAAO,KAAK;GACb;GACD,MAAM,QAAQ,aAAa,EAAE,MAAM,IAAI,MAAK,CAAE;EAChD,CAAC;CACH;CAEQ,MAAM,IAAI,IAA4C;EAC5D,IAAI,CAAC,KAAK,gBAAgB,UACxB;EAEF,MAAM,SAAS,MAAM,KAAK,WAAU;EACpC,IAAI,CAAC,QACH;EAEF,IAAI;GACF,MAAM,GAAG,MAAM;EACjB,QAAQ,CAER;CACF;CAOQ,aAAU;EAChB,IAAI,CAAC,KAAK,eACR,IAAI;GACF,KAAK,gBAAgB,OAAO,qBAAqB,CAAC,YAAY,IAAI;EACpE,QAAQ;GACN,KAAK,gBAAgB,QAAQ,QAAQ,IAAI;EAC3C;EAEF,OAAO,KAAK;CACd;CApEW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,YADH;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAD5B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACjClC,MAAM,kBAAkB;AACxB,MAAM,oBAAkB;AA8BxB,IAAa,qBAAb,MAAa,mBAAkB;CACZ,WAAW,OAAO,QAAQ;CAC1B,kBAAkB,OAAO,iBAAiB;CAC1C,SAAS,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE;CACnD,aAAa,OAAO,UAAU;CAC9B,aAAa,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CAG9B,QAAQ,KAAK,WAAW,WAAU;CAE3C,cAAA;EACE,MAAM,OAAO,KAAK,SAAS;EAC3B,MAAM,cAAc,KAAK,SACpB,KAAK,OAAO,eAAe,EAAE,UAAA,KAAgC,IAC9D;EACJ,IACE,CAAC,QACD,CAAC,KAAK,SAAS,QACf,gBAAgB,SAChB,CAAC,KAAK,gBAAgB,YACtB,KAAK,gBAAgB,aAAa,OAElC;EAIF,MAAM,WAAW,OAAO,MAAM,YAAY,QAAQ,IAC9C,yBACA,KAAK,IAAI,GAAG,YAAY,QAAQ;EACpC,MAAM,QAAQ,KAAK,YAAW;EAC9B,MAAM,cAAmB;GACvB,MAAM,OAAO,WAAW,KAAK,iBAAiB,KAAK,CAAC,CAAC,QAAQ;GAC7D,MAAM,QAAQ,OAAO,SAAS,IAAI,IAAI,OAAO,kBAAkB;GAC/D,MAAM,UAAU,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,QAAQ,IAAI,GAAI,IAAI;GAC5E,KAAK,WAAW,IAAI,UAAU,GAAG;GACjC,MAAM,OAAO,KAAK,SAAS,gBAAgB;GAC3C,IAAI,YAAY,KACd,KAAK,eAAe,WAAW;QAE/B,KAAK,WAAW,GAAG,QAAO;EAE9B;EACA,MAAM,2BAAgC;GACpC,IAAI,KAAK,SAAS,oBAAoB,UACpC,MAAK;EAET;EACA,MAAM,WACJ,OAAO,KAAK,mBAAmB,aAAa,IAAI,KAAK,eAAe,KAAK,IAAI;EAC/E,UAAU,QAAQ,KAAK;EACvB,KAAK,SAAS,iBAAiB,oBAAoB,kBAAkB;EACrE,MAAK;EACL,KAAK,WAAW,gBAAe;GAC7B,UAAU,WAAU;GACpB,KAAK,SAAS,oBAAoB,oBAAoB,kBAAkB;GACxE,MAAM,OAAM;GACZ,KAAK,SAAS,gBAAgB,MAAM,eAAe,WAAW;EAChE,CAAC;CACH;CAEQ,cAAW;EACjB,MAAM,QAAQ,KAAK,SAAS,cAAc,MAAM;EAChD,MAAM,aAAa,mBAAiB,EAAE;EACtC,MAAM,aAAa,eAAe,MAAM;EACxC,MAAM,aACJ,SACA,gIAAgI;EAElI,MAAM,cAAc;EACpB,KAAK,SAAS,KAAK,YAAY,KAAK;EACpC,OAAO;CACT;CAvEW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAkB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAlB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAkB,YADL;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAkB,YAAA,CAAA;EAD9B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;ACnBlC,MAAM,yBAAyB;AAE/B,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAExB,MAAM,kBAAkB;AAOxB,MAAM,sBAAsB;AAG5B,MAAM,uCAAuB,IAAI,IAAI;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAA;AAYD,SAAgB,YAAY,SAAuB;CACjD,IAAI,mBAAmB,qBACrB,OAAO;CAET,IAAI,mBAAmB,kBACrB,OAAO,CAAC,qBAAqB,IAAI,QAAQ,IAAI;CAE/C,OACE,mBAAmB,gBAClB,QAAQ,qBACP,QAAQ,QAAQ,oDAAkD;AAExE;AAsBA,IAAa,oBAAb,MAAa,kBAAiB;CACX,kBAAkB,OAAO,iBAAiB;CAC1C,WAAW,OAAO,QAAQ;CAC1B,aAAa,OAAO,UAAU;CAE9B,cAAc,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CACvB,aAAa,OAAwB,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CAC9C,kBAAkB,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,gCAAA,CAAA,CAAA;CAC3B,gBAAgB,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,gCAAA,CAAA,CAAA;CACzB,gBAAgB,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,gCAAA,CAAA,CAAA;CACtC,iBAAqC;CACrC,gBAAsD;CACtD,SAAgC;CAChC,QAA4B;CAC5B,YAAY;CAGX,SAAS,KAAK,YAAY,WAAU;CAEpC,QAAQ,KAAK,WAAW,WAAU;CAElC,UAAU,eAAe,KAAK,MAAK,MAAO,aAAa,KAAK,MAAK,MAAO,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAEhF,kBAAkB,eACnB,KAAK,gBAAgB,YAAY,KAAK,gBAAgB,aAAa,OAC1E,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,gCAAA,CAAA,CAAA;CAEQ,aAAa,KAAK,gBAAgB,WAAU;CAE5C,WAAW,KAAK,cAAc,WAAU;CAUxC,WAAW,KAAK,cAAc,WAAU;CAEjD,cAAA;EACE,MAAM,OAAO,KAAK,SAAS;EAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,MAC1B;EAEF,KAAK,QAAQ,KAAK,YAAW;EAC7B,KAAK,kBAAiB;EACtB,KAAK,oBAAoB,CAAC;EAE1B,MAAM,uBAA4B;GAChC,IAAI,KAAK,MAAK,MAAO,YAAY,CAAC,KAAK,SAAQ,GAC7C,KAAK,kBAAiB;EAE1B;EACA,MAAM,aAAa,UAA2B;GAC5C,IAAI,YAAY,MAAM,MAAwB,GAAG;IAC/C,KAAK,iBAAiB,MAAM;IAC5B,IAAI,CAAC,KAAK,cAAa,GAAI;KACzB,KAAK,cAAc,IAAI,IAAI;KAC3B,KAAK,mBAAkB;IACzB;GACF;EACF;EACA,MAAM,cAAc,UAA2B;GAC7C,IAAI,MAAM,WAAW,KAAK,gBACxB,KAAK,cAAa;EAEtB;EACA,KAAK,iBAAiB,UAAU,cAAc;EAC9C,KAAK,SAAS,iBAAiB,WAAW,WAAW,IAAI;EACzD,KAAK,SAAS,iBAAiB,YAAY,YAAY,IAAI;EAC3D,KAAK,WAAW,gBAAe;GAC7B,KAAK,YAAY;GACjB,KAAK,sBAAqB;GAC1B,KAAK,oBAAoB,UAAU,cAAc;GACjD,KAAK,SAAS,oBAAoB,WAAW,WAAW,IAAI;GAC5D,KAAK,SAAS,oBAAoB,YAAY,YAAY,IAAI;GAC9D,KAAK,OAAO,OAAM;GAClB,KAAK,SAAS,gBAAgB,MAAM,eAAe,eAAe;EACpE,CAAC;EAED,IAAI,KAAK,gBAAgB,UACvB,AAAK,KAAK,aAAY;OACjB,IAAI,KAAK,gBACd,KAAK,qBAAqB,KAAK,cAAc;CAEjD;CAGA,MAAM,OAAI;EACR,IAAI,KAAK,gBAAgB,UAAU;GACjC,IAAI;IAEF,OADe,KAAK,WAAW,MAAM,OAAO,uBAAA,CAAwB,SAC9D,CAAO,KAAI;GACnB,QAAQ,CAER;GACA;EACF;EACA,MAAM,SAAS,KAAK,SAAS;EAC7B,IAAI,kBAAkB,aACpB,OAAO,KAAI;CAEf;CAQA,cAAW;EACT,IAAI,CAAC,KAAK,QAAO,GACf,OAAO,KAAK,WAAU;EAExB,IAAI,KAAK,gBAAe,GACtB,OAAO,KAAK,WAAU,IAAK,KAAK,OAAM;EAExC,MAAM,OAAO,KAAK,SAAS;EAC3B,MAAM,WAAW,MAAM;EACvB,IAAI,UACF,OAAO,SAAS,SAAS,SAAS;EAEpC,OAAO,MAAM,eAAe,KAAK,WAAU;CAC7C;CAEQ,cAAW;EACjB,MAAM,WAAW,KAAK,SAAS,KAAK,cAA2B,IAAI,gBAAe,EAAG;EACrF,IAAI,UACF,OAAO;EAET,MAAM,QAAQ,KAAK,SAAS,cAAc,KAAK;EAC/C,MAAM,aAAa,iBAAiB,EAAE;EACtC,MAAM,aAAa,eAAe,MAAM;EACxC,MAAM,aACJ,SACA,2GAA2G;EAE7G,KAAK,SAAS,KAAK,YAAY,KAAK;EACpC,OAAO;CACT;CAEQ,oBAAiB;EACvB,IAAI,KAAK,OACP,KAAK,gBAAgB,IAAI,KAAK,MAAM,KAAK,MAAM,YAAY,CAAC;CAEhE;CAGQ,WAAW,QAAc;EAC/B,KAAK,sBAAqB;EAC1B,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW,IAAI,SAAS;EAC7B,KAAK,cAAc,QAAQ,UAAU,QAAQ,CAAC;CAChD;CAEQ,UAAU,QAAc;EAC9B,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW,IAAI,OAAO;CAC7B;CAEQ,WAAQ;EACd,KAAK,UAAU,CAAC;EAChB,KAAK,WAAW,IAAI,QAAQ;CAC9B;CAGQ,cAAW;EACjB,KAAK,cAAa;EAClB,KAAK,SAAQ;CACf;CAEQ,gBAAa;EACnB,KAAK,sBAAqB;EAC1B,KAAK,iBAAiB;EACtB,KAAK,cAAc,IAAI,KAAK;CAC9B;CAGQ,qBAAkB;EACxB,KAAK,sBAAqB;EAC1B,IAAI,KAAK,QAAO,GACd;EAEF,KAAK,gBAAgB,iBAAgB;GACnC,KAAK,gBAAgB;GACrB,IAAI,CAAC,KAAK,QAAO,GACf,KAAK,cAAa;EAEtB,GAAG,mBAAmB;CACxB;CAEQ,wBAAqB;EAC3B,IAAI,KAAK,kBAAkB,MAAM;GAC/B,aAAa,KAAK,aAAa;GAC/B,KAAK,gBAAgB;EACvB;CACF;CAEQ,UAAU,QAAc;EAC9B,KAAK,YAAY,IAAI,MAAM;EAC3B,KAAK,oBAAoB,MAAM;CACjC;CAEQ,oBAAoB,QAAc;EACxC,KAAK,SAAS,gBAAgB,MAAM,YAAY,iBAAiB,GAAG,OAAM,GAAI;CAChF;CAEQ,MAAM,eAAY;EACxB,IAAI;GACF,MAAM,EAAE,aAAa,MAAM,OAAO;GAClC,KAAK,SAAS;GACd,MAAM,UAAU,MAAM,QAAQ,IAAI;IAChC,SAAS,YAAY,qBAAqB,SAAS,KAAK,WAAW,KAAK,cAAc,CAAC;IACvF,SAAS,YAAY,oBAAoB,SAAS,KAAK,UAAU,KAAK,cAAc,CAAC;IACrF,SAAS,YAAY,0BAA0B,KAAK,YAAW,CAAE;IACjE,SAAS,YAAY,yBAAyB,KAAK,YAAW,CAAE;GACjE,CAAA;GACD,MAAM,kBAAuB;IAC3B,KAAK,MAAM,UAAU,SACnB,AAAK,OAAO,OAAM;GAEtB;GACA,IAAI,KAAK,WACP,UAAS;QAET,KAAK,WAAW,UAAU,SAAS;EAEvC,QAAQ,CAER;CACF;CAEQ,qBAAqB,UAAwB;EACnD,MAAM,iBAAsB;GAG1B,MAAM,UADS,KAAK,KAAK,SAAS,SAAS,KAAK,CAAC,IAAI,iBAEjD,IACA,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,gBAAe,IAAK,SAAS,SAAS,SAAS,SAAS,CAAC;GACzF,IAAI,UAAU,wBAAwB;IACpC,IAAI,CAAC,KAAK,QAAO,GAAI;KACnB,KAAK,sBAAqB;KAC1B,KAAK,cAAc,QAAQ,UAAU,QAAQ,CAAC;IAChD;IACA,KAAK,UAAU,OAAO;IACtB,KAAK,WAAW,IAAI,OAAO;GAC7B,OAAO,IAAI,KAAK,QAAO,GACrB,KAAK,YAAW;QAEhB,KAAK,SAAQ;EAEjB;EACA,SAAS,iBAAiB,UAAU,QAAQ;EAC5C,KAAK,WAAW,gBAAgB,SAAS,oBAAoB,UAAU,QAAQ,CAAC;CAClF;CAhQW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,YADJ;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EAD7B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;AC1ElC,IAAa,uBAAb,MAAa,6BAA6B,uBAAsB;CAOrD,iBAAiB,QAAgC,MAA4B;EACpF,IAAI,OAAO,gBAAgB,KAAK,aAC9B,OAAO;EAET,MAAM,IAAI,OAAO;EACjB,MAAM,IAAI,KAAK;EACf,MAAM,uBAAO,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC;EAC3D,KAAK,MAAM,OAAO,MAChB,IAAI,EAAE,SAAS,EAAE,MACf,OAAO;EAGX,OAAO;CACT;CApBW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,YADP;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EADhC,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACyClC,SAAgB,eAAe,SAAiC,CAAA,GAAE;CAChE,OAAO,yBAAyB;EAC9B;GAAE,SAAS;GAAiB,UAAU;IAAE,GAAG;IAAmB,GAAG;GAAM;EAAE;EACzE;GAAE,SAAS;GAAoB,UAAU;EAAoB;EAC7D;GACE,SAAS;GACT,kBAAgC;IAC9B,MAAM,OAAO,OAAO,mBAAmB;IACvC,MAAM,WAAW,OAAO,iBAAiB;IACzC,OAAO;KACL,iBAAiB;KAGjB,mBAAmB,SAAS,YAAY,SAAS,aAAa;KAC9D,eAAe,UAAU,YAAY,KAAK,SAAS,UAAU,OAAO;IACrE;GACH;EACD;EACD,oCAAmC;GACjC,OAAO,iBAAiB;GACxB,OAAO,mBAAmB;GAC1B,OAAO,iBAAiB;GACxB,OAAO,gBAAgB;GACvB,OAAO,kBAAkB;EAC3B,CAAC;CACF,CAAA;AACH;AAWA,SAAgB,qBAAkB;CAChC,OAAO,iBAAiB,EAAE,8BAA8B,WAAU,CAAE;AACtE;AChEA,IAAa,kBAAb,MAA4B;CAEjB,QAAQ,OAAoB,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,gCAAA,CAAA,CAAA;CAEvC,YAAY,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAErB,WAAW,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAEpB,YAAY,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAErB,WAAW,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAEZ,4BAAY,IAAI,IAAG;CAMpC,GAAG,UAAsC;EACvC,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;CAC7C;CAMA,gBAAa;EACX,KAAK,MAAM,IAAI,UAAU;EACzB,KAAK,UAAU,QAAQ,MAAM,IAAI,CAAC;EAClC,KAAK,OAAO,WAAW;CACzB;CAMA,eAAY;EACV,KAAK,MAAM,IAAI,QAAQ;EACvB,KAAK,SAAS,QAAQ,MAAM,IAAI,CAAC;EACjC,KAAK,OAAO,UAAU;CACxB;CAMA,gBAAa;EACX,KAAK,MAAM,IAAI,SAAS;EACxB,KAAK,UAAU,QAAQ,MAAM,IAAI,CAAC;EAClC,KAAK,OAAO,WAAW;CACzB;CAMA,eAAY;EACV,KAAK,MAAM,IAAI,UAAU;EACzB,KAAK,SAAS,QAAQ,MAAM,IAAI,CAAC;EACjC,KAAK,OAAO,UAAU;CACxB;CAEQ,OAAO,OAAkB;EAC/B,KAAK,MAAM,YAAY,CAAC,GAAG,KAAK,SAAS,GACvC,SAAS,KAAK;CAElB;AACD;AClCD,IAAa,kBAAb,MAAa,gBAAe;CACT,SAAS,OAAO,MAAM;CACtB,WAAW,OAAO,QAAQ;CAC1B,UAAU,OAAsC,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAC5D,OAA8B;CAE9B,mBAAkC;CAGzB,gBAAgB,eAC/B,KAAK,QAAO,CAAE,CACX,KAAK,GAAG,WAAW;EAAE,GAAG;EAAG;CAAK,EAAG,CAAC,CACpC,QAAQ,MAAM,EAAE,OAAO,OAAM,CAAE,CAAC,CAChC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CACtD,KAAK,MAAM,EAAE,MAAM,GACvB,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,gCAAA,CAAA,CAAA;CAGQ,QAAQ,eAAe,KAAK,cAAa,CAAE,CAAC,EAAE,EAAE,MAAK,KAAM,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,gCAAA,CAAA,CAAA;CAE7D,YAAY,eAAe,KAAK,MAAK,IAAK,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAErD,cAAA;EAIE,MAAM,MAAM,KAAK,OAAO,OAAO,WAAW,MAAK;GAC7C,IAAI,aAAa,iBACf;QAAI,KAAK,SAAS,QAAQ,KAAK,qBAAqB,MAClD,KAAK,mBAAmB,EAAE;GAC5B,OACK,KACJ,aAAa,iBACZ,aAAa,oBACb,aAAa,mBACb,aAAa,sBACf,EAAE,OAAO,KAAK,kBAEd,KAAK,UAAS;EAElB,CAAC;EACD,MAAM,iBAAiB,OAAO,mBAAmB,CAAC,CAAC,SAAS,SAAS,KAAK,iBAAgB,CAAE;EAC5F,OAAO,UAAU,CAAC,CAAC,gBAAe;GAChC,IAAI,YAAW;GACf,eAAc;EAChB,CAAC;CACH;CAQA,QAAQ,KAAkB,QAAyB;EACjD,KAAK,QAAQ,SAAS;EACtB,OAAO,KAAK,GAAG,KAAK,MAAM;CAC5B;CAQA,KAAK,KAAkB,QAAyB;EAC9C,KAAK,QAAQ,MAAM;EACnB,OAAO,KAAK,GAAG,KAAK;GAAE,GAAG;GAAQ,YAAY;EAAI,CAAE;CACrD;CAQA,OAAI;EACF,KAAK,QAAQ,MAAM;EACnB,MAAM,QAAQ,KAAK,UAAS,CAAE,EAAE,SAAQ,KAAM;EAC9C,IAAI,UAAU,QAAQ,QAAQ,IAC5B,KAAK,SAAS,UAAU,KAAK;OAE7B,KAAK,SAAS,KAAI;CAEtB;CAOA,eAAe,QAAqB,OAAa;EAC/C,MAAM,eAAmC;GAAE;GAAQ;EAAK;EACxD,KAAK,QAAQ,QAAQ,QAAQ,CAAC,GAAG,KAAK,YAAY,CAAC;EACnD,aAAa,KAAK,QAAQ,QAAQ,QAAQ,IAAI,QAAQ,MAAM,MAAM,YAAY,CAAC;CACjF;CAMA,cAAW;EACT,MAAM,OAAO,KAAK;EAClB,KAAK,UAAS;EACd,OAAO;CACT;CAMA,YAAS;EACP,KAAK,OAAO;EACZ,KAAK,mBAAmB;CAC1B;CAGQ,YAAS;EACf,MAAM,SAAS,KAAK,cAAa;EACjC,OAAO,OAAO,MAAM,MAAM,EAAE,MAAK,IAAK,CAAC,KAAK,OAAO;CACrD;CAOQ,mBAAgB;EACtB,IAAI,KAAK,cAAa,CAAE,CAAC,OAAO,MAAM,EAAE,MAAK,KAAM,CAAC,GAClD,OAAO;EAET,KAAK,KAAI;EACT,OAAO;CACT;CAEQ,QAAQ,MAAoB;EAClC,KAAK,OAAO;EACZ,KAAK,mBAAmB;CAC1B;CAEQ,GAAG,KAAkB,QAAyB;EACpD,IAAI,MAAM,QAAQ,GAAG,GACnB,OAAO,KAAK,OAAO,SAAS,KAAK,MAAM;EAEzC,OAAO,KAAK,OAAO,cAAc,KAAyB,MAAM;CAClE;CAjJW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAf,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,YADF;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAe,YAAA,CAAA;EAD3B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;;AC/ClC,MAAa,aAAa;AAO1B,MAAa,YAAY;AAOzB,MAAa,gBAAgB;AAQ7B,MAAa,gBAAgB;AAO7B,MAAa,mBAAmB;AAOhC,MAAa,gBAAgB;AAO7B,MAAa,cAAc;ACnC3B,MAAa,eAAA;AAOb,MAAa,kBAAkB;AAO/B,MAAa,cAAA;AAOb,MAAa,mBAAmB;AAyBhC,SAAS,MAAM,YAAyB,UAAgB;CAKtD,OAAO;EAAE;EAAY;EAAU,UAJd,QAAQ,IAAI,WAAW,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,WACxD,cACA,MAEuB;CAAQ;AACzC;AAEA,SAAS,WAAW,IAAU;CAC5B,OAAO,eAAe,GAAE;AAC1B;AAkBA,SAAgB,iBACd,MACA,UACA,SACA,KACA,OACA,SAAiB,YAAU;CAE3B,MAAM,WAAW;CACjB,MAAM,UAAoC;EAAE;EAAU;EAAQ,MAAM;CAAM;CAC1E,MAAM,MAAM,SAAS,SAAS,WAAW;CACzC,MAAM,QAAQ,SAAS,SAAS,UAAU;CAC1C,MAAM,QAAQ;EAAE,WAAW,WAAW,CAAC;EAAG,WAAW;CAAwB;CAC7E,MAAM,YAAY;EAAE,WAAW,WAAW,KAAK;EAAG,WAAW;CAAM;CACnE,MAAM,OAAO,EAAE,WAAW,WAAW,CAAC,EAAC;CACvC,MAAM,WAAW,EAAE,WAAW,WAAW,CAAC,QAAQ,EAAG,EAAC;CAStD,OAAO,MAAM;EAPX,IAAI,QAAQ,SAAS,SAAS,CAAC,WAAW,KAAK,IAAI,CAAC,OAAO,SAAS,GAAG,OAAO;EAC9E,MAAM,QAAQ,SAAS,SAAS,CAAC,MAAM,QAAQ,IAAI,CAAC,UAAU,IAAI,GAAG,OAAO;EAC5E,IAAI,QACF,SAAS,SAAS,CAAC,EAAE,SAAS,EAAC,GAAI,EAAE,SAAS,EAAC,CAAE,IAAI,CAAC,EAAE,SAAS,EAAC,GAAI,EAAE,SAAS,EAAC,CAAE,GACpF,OAAO;CAGE,GAAY,QAAQ;AACnC;AAaA,SAAgB,aACd,MACA,UACA,SAAoB;CAEpB,MAAM,UAAoC;EACxC,UAAU;EACV,QAAQ;EACR,MAAM;CACP;CACD,MAAM,SAAS;EAAE,SAAS;EAAG,WAAW;CAAuB;CAC/D,MAAM,QAAQ;EAAE,SAAS;EAAG,WAAW;CAAoB;CAK3D,OAAO,MAAM,CAHX,SAAS,SACL,SAAS,QAAQ,CAAC,QAAQ,KAAK,GAAG,OAAO,IACzC,QAAQ,QAAQ,CAAC,OAAO,MAAM,GAAG,OAAO,CACvB,GAAG,WAAW;AACvC;AAWA,SAAgB,kBAAkB,UAAuB,SAAoB;CAC3E,MAAM,UAAoC;EACxC,UAAU;EACV,QAAQ;EACR,MAAM;CACP;CACD,OAAO,MACL,CACE,SAAS,QAAQ,CAAC,EAAE,SAAS,EAAC,GAAI,EAAE,SAAS,EAAC,CAAE,GAAG,OAAO,GAC1D,QAAQ,QAAQ,CAAC,EAAE,SAAS,EAAC,GAAI,EAAE,SAAS,EAAC,CAAE,GAAG,OAAO,CAC1D,GACD,gBAAgB;AAEpB;AAeA,SAAgB,YAAY,GAAW,OAAa;CAClD,OAAO;EAAE,KAAK,QAAQ;EAAG,OAAO,CAAC,QAAQ,MAAO,IAAI;EAAI,KAAK,IAAI;CAAC;AACpE;AAgBA,SAAgB,UACd,KACA,OACA,KACA,OACA,MACA,IACA,UAAgB;CAEhB,MAAM,IAAI,YAAY,MAAM,KAAK;CACjC,MAAM,IAAI,YAAY,IAAI,KAAK;CAC/B,MAAM,UAAoC;EAAE;EAAU,QAAQ;EAAe,MAAM;CAAM;CACzF,OAAO,MACL;EACE,IAAI,QAAQ,CAAC,EAAE,WAAW,WAAW,EAAE,GAAG,EAAC,GAAI,EAAE,WAAW,WAAW,EAAE,GAAG,EAAC,CAAE,GAAG,OAAO;EACzF,MAAM,QACJ,CAAC,EAAE,WAAW,WAAW,EAAE,KAAK,EAAC,GAAI,EAAE,WAAW,WAAW,EAAE,KAAK,EAAC,CAAE,GACvE,OAAO;EAET,IAAI,QAAQ,CAAC,EAAE,SAAS,EAAE,IAAG,GAAI,EAAE,SAAS,EAAE,IAAG,CAAE,GAAG,OAAO;CAC9D,GACD,QAAQ;AAEZ;AAaA,SAAgB,kBAAkB,UAAkB,OAAe,OAAa;CAC9E,OAAO,WAAW,MAAO,QAAQ,QAAQ;AAC3C;AAYA,SAAgB,eAAe,aAAqB,UAAgB;CAElE,OAAO,KAAK,MAAM,KAAK,IAAA,KAAqB,KAAK,IAAI,KAAK,cAD5C,KAAK,IAAI,KAAK,IAAI,QAAQ,GAAG,EAC6B,CAAK,CAAC,CAAC;AACjF;ACvPA,IAAa,gBAAb,MAA0B;CAEf,WAAW,OAA2B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAK3C,eAAe,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,gCAAA,CAAA,CAAA;CAMxB,cAAc,OAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CAQ1B,eAAe,OAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,gCAAA,CAAA,CAAA;AACrC;AAQD,IAAa,oBAAb,MAA8B;CAC5B,UAAgC;AACjC;ACrBD,MAAa,iBAAiB,IAAI,eAA6B,gBAAgB;ACgE/E,IAAM,eAAN,MAAkB;CAEP,SAAS,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,gCAAA,CAAA,CAAA;CAEtB;CAET,YAAY,cAA6B;EACvC,KAAK,QAAQ,eAAe,CAAC,KAAK,OAAM,KAAM,aAAY,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,iCAAA,CAAA,CAAA;CAC/D;AACD;AA8DD,SAAS,SAAS,OAAc;CAC9B,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAET,MAAM,QAAkB,MAAsC;CAC9D,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAOA,MAAM,8BAAc,IAAI,QAAO;AAG/B,MAAM,OAAO;AAEb,MAAM,QAAQ;AAEd,MAAM,iBAAiB;AAKvB,MAAM,mBAAmB;AACzB,MAAM,aAAa;AACnB,MAAM,cAAc;AA2DpB,IAAa,yBAAb,MAAa,uBAAsB;CAIxB,OAAO,MAAM,gBAAe,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,gCAAA,CAAA,CAAA;CAE5B,eAAe,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,eAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAExD,WAAW,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,WAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAGpD,eAAe,OAAM;CAErB,oBAAoB,OAAM;CAE1B,kBAAkB,OAAM;CAKxB,cAAc,OAAM;CAKpB,kBAAkB,OAAM;CAKxB,gBAAgB,OAAM;CAEtB,mCAAmC;CAE3B,iBAAiB,OAAO,sBAAsB;CAC9C,SAAS,OAAO,MAAM;CACtB,WAAW,OAAO,QAAQ;CAC1B,MAAM,OAAO,eAAe;CAC5B,WAAW,OAAO,gBAAc;CAChC,SAAS,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,KAAK;CACxD,WAAW,OAAO,iBAAiB;CACnC,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,QAAQ;CAC1B,aAAa,OAAO,gBAAgB,EAAE,UAAU,KAAI,CAAE;CAEtD,eAAe,OAAO,wBAAwB;EAC7D,UAAU;EACV,UAAU;CACX,CAAA;CAEgB,aAAa,OAAO,cAAc,EAAE,UAAU,KAAI,CAAE;CAEpD,gBAAgB,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE;CAEnE,mBAAwC;CAE/B,QAAgB,KAAK,eAAe,KAAK,aAAa,QAAQ,IAAI;CAElE,QAAQ,eAAe,KAAK,YAAY,MAAK,KAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,gCAAA,CAAA,CAAA;CACxD,aAAa,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CACtB,SAAS,UAAU,SAAS,UAAQ;EAAA,GAAA,YAAA,EAAA,WAAA,SAAA,iCAAA,CAAA;EAAI,MAAM;CAAgB,CAAA;CAC9D,MAAM,UAAU,SAAkC,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,gCAAA,CAAA,CAAA;CAExD,OAAoB,iBAAiB,KAAK,QAAQ,KAAK,QAAQ;CAC/D,WAAW,OAAO,QAAQ;CAE1B,eAAe,YAAY,KAAK,OAAO,YAAY;CAEnD,yBAAS,IAAI,IAAG;CAEzB,YAA2B;CAC3B,SAA4B;CAC5B,UAA6B;CAK7B,YAA+B;CAC/B,UAAsE;CACtE,YAAY;CAEZ,UAA0E;CAE1E,OAAiC;CAEjC,mBAAmB;CACnB,UAA0B;CAC1B,YAAoC;CACpC,YAAY;CAEZ,aAA4B;CAE5B,eAAqD;CAErD,gBAAqC;CAErC,iBAAsC;CAEtC,gBAA+B;CAE/B,aAAiC;CAGzC,IAAY,QAAK;EACf,IAAI,QAAQ,KAAK,OAAO,IAAI,KAAK,SAAS;EAC1C,IAAI,CAAC,OAAO;GACV,QAAQ,CAAA;GACR,KAAK,OAAO,IAAI,KAAK,WAAW,KAAK;EACvC;EACA,OAAO;CACT;CAEA,cAAA;EAIE,MAAM,YAAY,KAAK,OAAO,OAAO,WAAW,MAAK;GACnD,IAAI,aAAa,iBAAiB;IAChC,KAAK,YAAY;IAEjB,KAAK,kBAAiB;IACtB;GACF;GACA,IAAI,aAAa,iBAAiB,aAAa,mBAAmB;IAChE,KAAK,YAAY;IACjB,KAAK,gBAAgB,SAAS,KAAK,SAAS,SAAQ,CAAE;GACxD;GACA,IAAI,aAAa,iBAAiB,KAAK,QACrC,KAAK,OAAO,MAAM,EAAE;GAEtB,KACG,aAAa,iBACZ,aAAa,oBACb,aAAa,mBACb,aAAa,sBACf,KAAK,kBACL;IAGA,KAAK,mBAAmB;IACxB,KAAK,IAAI,UAAS;IAClB,KAAK,uBAAsB;GAC7B;EACF,CAAC;EACD,OAAO,UAAU,CAAC,CAAC,gBAAgB,UAAU,YAAW,CAAE;CAC5D;CAIA,IAAI,cAAW;EACb,OAAO,KAAK,WAAW;CACzB;CAEA,IAAI,YAAS;EACX,OAAO,KAAK,cAAa,CAAE,CAAC,IAAI;CAClC;CAEA,IAAI,iBAAc;EAChB,OAAO,KAAK,cAAa,CAAE,CAAC;CAC9B;CAEA,IAAI,qBAAkB;EACpB,OAAO,KAAK,QAAQ,MAAM,SAAS,QAAQ,CAAA;CAC7C;CAEA,IAAI,wBAAqB;EACvB,OAAO,KAAK,QAAQ,OAAO;CAC7B;CAEA,WAAQ;EACN,KAAK,eAAe,qBAAqB,KAAK,KAAI,GAAI,IAAI;EAC1D,MAAM,UAAU,KAAK,eAAe,WAAW,KAAK,KAAI,CAAE;EAC1D,IAAI,SAAS,SAAS,CAAC,KAAK,QAC1B,KAAK,aAAa,QAAQ,OAAO,QAAQ,QAAQ;EAEnD,IAAI,KAAK,SAAS,SAAS,KAAK,OAAO,WACrC,KAAK,KAAK,iBAAiB,eAAe,KAAK,aAAa;EAE9D,KAAK,mBAAmB,KAAK,eAAe,IAAI,MAAM,KAAK,iBAAiB,CAAC,CAAC,KAAK;EAGnF,IAAI,CAAC,KAAK,cACR,KAAK,iBAAiB,KAAK,SAAS,aAAa,KAAK,IAAI;EAE5D,IAAI,KAAK,KAAI,MAAO,gBAClB,KAAK,gBAAgB,KAAK,IAAI,eAC5B;GACE,OAAO,KAAK,WAAW,WAAU;GACjC,QAAQ,KAAK;GACb,gBAAgB,KAAK,SAAQ;EAC9B,GACD,KAAK,KAAK;CAGhB;CAEA,kBAAe;EACb,KAAK,YAAY;EACjB,IAAI,KAAK,SAAS;GAChB,MAAM,EAAE,OAAO,QAAQ,KAAK;GAC5B,KAAK,UAAU;GACf,KAAK,aAAa,OAAO,GAAG;EAC9B;CACF;CAEA,cAAW;EACT,KAAK,YAAY;EACjB,KAAK,gBAAe;EACpB,KAAK,gBAAgB;EACrB,KAAK,iBAAgB;EACrB,KAAK,iBAAiB;EACtB,KAAK,mBAAkB;EACvB,KAAK,mBAAmB;EACxB,KAAK,KAAK,oBAAoB,eAAe,KAAK,aAAa;EAC/D,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS,SAAS;GACpB,QAAQ,YAAY,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;GACxD,IAAI;IACF,KAAK,KAAK,sBAAsB,QAAQ,SAAS;GACnD,QAAQ,CAER;EACF;EACA,KAAK,WAAU;EAGf,KAAK,SAAS,WAAW,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;EAC7D,KAAK,UAAU;EACf,KAAK,YAAW;EAChB,KAAK,mBAAmB;EACxB,KAAK,kBAAiB;EACtB,IAAI,KAAK,eAAe,MAAM;GAC5B,qBAAqB,KAAK,UAAU;GACpC,KAAK,aAAa;EACpB;EACA,IAAI,KAAK,eAAe,WAAW,KAAK,KAAI,CAAE,CAAC,EAAE,WAAW,MAC1D,KAAK,eAAe,uBAAuB,KAAK,KAAI,CAAE;EAExD,KAAK,YAAY;EACjB,KAAK,iBAAgB;EACrB,KAAK,WAAW,IAAI,CAAC;CACvB;CAOA,aAAU;EACR,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,OACH;EAEF,KAAK,UAAU,IAAI;EACnB,MAAM,UAAU,YAAW;EAC3B,MAAM,WAAW;EAGjB,MAAM,gBAAgB,KAAK,eACxB,mBAAmB,KAAK,KAAI,CAAE,CAAC,CAC/B,SAAS,oBAAmB;EAC/B,MAAM,UAAU,KAAK,SAAS;EAC9B,MAAM,cACJ,mBAAmB,eAAe,MAAM,MAAM,SAAS,OAAO,IAAI,UAAU;EAC9E,KAAK,UAAU;EAIf,qBAAoB;GAClB,IAAI,KAAK,aAAa,KAAK,UAAU,KAAK,YAAY,OACpD;GAEF,IAAI,CAAC,KAAK,MAAK,GAGb,KAAK,YAAY;QACZ;IACL,KAAK,oBAAmB;IACxB,KAAK,UAAU;IACf,KAAK,iBAAgB;IACrB,KAAK,UAAS;GAChB;EACF,CAAC;CACH;CAMA,aAAa,gBAAgC,qBAAwC;EACnF,IAAI,CAAC,KAAK,WAAW;GACnB,KAAK,UAAU;IAAE,OAAO;IAAgB,KAAK;GAAmB;GAChE;EACF;EAGA,KAAK,oBAAmB;EACxB,MAAM,YAAY,KAAK;EACvB,KAAK,YAAY;EACjB,IAAI,aAAa,KAAK,OAAO,WAAW,cAAc,GACpD;EAEF,MAAM,WAAW,KAAK,SAAS,cAAc;EAC7C,MAAM,UAAU,KAAK;EACrB,KAAK,UAAU;EAEf,IAAI,SAAS,SAAS,UAAU;GAC9B,KAAK,YAAY,SAAS,KAAK,SAAS,OAAO,gBAAgB,qBAAqB,OAAO;GAC3F;EACF;EAEA,IAAI,SAAS,SAAS,OAAO;GAC3B,MAAM,SAAS,KAAK,MAAM,SAAS;GACnC,IAAI,CAAC,QACH;GAEF,KAAK,UAAU,QAAQ,gBAAgB,mBAAmB;GAC1D,MAAM,UAAU,KAAK,MAAM,OAAO,SAAS,QAAQ,CAAC;GACpD,KAAK,UAAS;GACd,KAAK,cAAc,OAAO,QAAQ,SAAS,SAAS,SAAS;GAC7D;EACF;EAEA,MAAM,QAAQ,KAAK,YAAY,gBAAgB,mBAAmB;EAClE,IAAI,SAAS,SAAS,QAAQ;GAC5B,MAAM,UAAU,KAAK,MAAM,OAAO,CAAC;GACnC,KAAK,MAAM,KAAK,KAAK;GACrB,KAAK,UAAU,KAAK;GACpB,KAAK,UAAS;GACd,KAAK,cAAc,MAAM,OAAO,SAAS,SAAS,UAAU,UAAU,MAAM;GAC5E;EACF;EACA,IAAI,SAAS,SAAS,UAAU,MAAM,WAAW,QAAQ,MAAM,WAAW,SAAS,QAEjF,MAAM,SAAS;EAEjB,MAAM,UACJ,SAAS,SAAS,aAAa,UAC3B,KAAK,MAAM,OAAO,KAAK,MAAM,QAAQ,OAAO,GAAG,CAAC,IAChD,CAAA;EACN,KAAK,MAAM,KAAK,KAAK;EACrB,KAAK,UAAU,KAAK;EACpB,KAAK,UAAS;EACd,KAAK,cACH,UAAU,SAAS,MACnB,OACA,SACA,SACA,UAAU,UAAU,MAAM;CAE9B;CAMA,SAAM;EACJ,MAAM,QAAQ,KAAK,cAAa;EAChC,KAAK,MAAM,OAAO,KAAK,MAAM,QAAQ,KAAK,GAAG,CAAC;EAC9C,KAAK,UAAU,IAAI;EACnB,MAAM,UAAU,YAAW;EAC3B,MAAM,WAAW;EACjB,KAAK,OAAM,CAAE,CAAC,OAAO,KAAK,OAAM,CAAE,CAAC,QAAQ,MAAM,IAAI,QAAQ,CAAC;EAC9D,MAAM,MAAM,OAAM;EAClB,KAAK,UAAS;EACd,OAAO,MAAM;CACf;CAMA,OAAO,KAA4B,gBAA8B;EAC/D,KAAK,oBAAmB;EACxB,KAAK,UAAU;EACf,KAAK,OAAM,CAAE,CAAC,OAAO,IAAI,QAAQ;EACjC,MAAM,QAAQ,KAAK,KACjB,KACA,gBACA,IAAI,gBAAe,GACnB,IAAI,kBAAiB,GACrB,IAAI,aAAa,KAAK,KAAK,CAAC;EAE9B,KAAK,MAAM,KAAK,KAAK;EACrB,KAAK,UAAU,KAAK;EACpB,KAAK,WAAW,KAAK;EACrB,KAAK,aAAY;EACjB,KAAK,UAAS;EACd,KAAK,UAAS;CAChB;CASA,UAAU,QAAc;EACtB,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO;CACjD;CAOA,YAAS;EACP,MAAM,SAAS,KAAK,MAAM,EAAE,EAAE,UAAU;EACxC,MAAM,UAAU,SAAS,KAAK,SAAS,SAAQ,CAAE;EACjD,IAAI,WAAW,QAAQ,YAAY,MACjC,OAAO;EAET,MAAM,QAAQ,SAAS,IAAI;EAC3B,IAAI,SAAS,GACX,OAAO;EAET,KAAK,SAAS,UAAU,KAAK;EAC7B,OAAO;CACT;CAMA,yBAAyB,WAA2B,UAAQ;EAC1D,KAAK,QAAQ,SAAS,SAAS,SAAQ,CAAE,EAAE,SAAS;GAAE,KAAK;GAAG;EAAQ,CAAE;CAC1E;CAOQ,YACN,KACA,OACA,OACA,KACA,SAA0B;EAE1B,MAAM,OAAO,KAAK;EAClB,IAAI,OACF,KAAK,MAAM,KAAK,KAAK,OAAO,IAAI,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAA,GACjD,KAAK,aAAa,CAAC;EAGvB,KAAK,YAAY;EACjB,MAAM,SAAS,KAAK;EACpB,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,QAAQ,eAAe,QAAQ,OAAO,SAAS,MAAM,EAAE,QAAQ,GAAG;EACxE,IAAI;EACJ,IAAI,UAAwB,CAAA;EAC5B,MAAM,WAAW,OAAO;EACxB,IAAI,UAAU;GACZ,KAAK,UAAU,UAAU,OAAO,GAAG;GACnC,UAAU,OAAO,OAAO,QAAQ,CAAC;GACjC,WAAW;EACb,OAAO;GACL,WAAW,KAAK,YAAY,OAAO,GAAG;GACtC,OAAO,KAAK,QAAQ;GACpB,KAAK,UAAU,QAAQ;EACzB;EACA,KAAK,UAAS;EACd,KAAK,cAAc,MAAM,UAAU,SAAS,SAAS,OAAO;EAC5D,KAAK,MAAM,KAAK,MACd,KAAK,EAAE,KAAK;CAEhB;CAGQ,UAAU,QAAoB,OAAuB,KAAwB;EACnF,IAAI,OAAO,SACT,KAAK,OAAO,QAAQ,OAAO,GAAG;OAE9B,KAAK,WAAW,QAAQ,KAAK;CAEjC;CAOQ,OAAO,QAAoB,OAAuB,KAAwB;EAChF,MAAM,QAAQ,KAAK,YAAY,OAAO,GAAG;EACzC,MAAM,UAAU,YAAW;EAC3B,OAAO,MAAM,MAAM;EACnB,OAAO,QAAQ,MAAM;EACrB,OAAO,OAAO,MAAM;EACpB,OAAO,YAAY,MAAM;EACzB,OAAO,WAAW,MAAM;EACxB,OAAO,QAAQ;EACf,OAAO,MAAM,MAAM,KAAK;EACxB,OAAO,UAAU;EACjB,OAAO,gBAAgB;EACvB,OAAO,cAAc;EACrB,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW,MAAM;EACtB,MAAM,YAAY,OAAO;EACzB,OAAO,YAAY;EACnB,IAAI,YAAY,GAAG;GACjB,MAAM,MAAM,OAAO;GAGnB,gBACE,EACE,YAAW;IACT,MAAM,WAAW,OAAO,QAAQ,MAAM,OAAO,SAAS,SAAS,SAAQ,IAAK;IAC5E,IAAI,UACF,SAAS,YAAY;GAEzB,EACD,GACD,EAAE,UAAU,KAAK,SAAQ,CAAE;EAE/B;CACF;CAMQ,YAAS;EACf,MAAM,QAAQ,KAAK;EACnB,MAAM,SAAS,MAAM,SAAS,KAAK;EACnC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK;GAC/B,MAAM,QAAQ,MAAM;GACpB,IAAI,SAAS,CAAC,MAAM,WAAW,UAAU,KAAK,UAAU,UAAU,KAAK,SACrE,KAAK,YAAY,KAAK;EAE1B;CACF;CAEQ,YAAY,OAAiB;EACnC,MAAM,YAAY,MAAM,SAAS,SAAS,SAAQ,CAAE,EAAE,aAAa;EACnE,MAAM,UAAU,YAAW;EAC3B,MAAM,WAAW;EAEjB,MAAM,gBAAgB;EACtB,MAAM,cAAc;EACpB,MAAM,UAAU;EAChB,MAAM,IAAI,QAAO;EACjB,MAAM,MAAM,OAAM;CACpB;CAGQ,WAAW,QAAoB,OAAqB;EAC1D,OAAO,QAAQ;EACf,OAAO,MAAM,MAAM,KAAK;EACxB,MAAM,WAAW,KAAK,eAAe,mBAAmB,KAAK,KAAI,CAAE,CAAC,CAAC;EACrE,IAAI,OAAO,eAAe;GACxB,SAAS,mBAAmB,OAAO,aAAa;GAChD,OAAO,gBAAgB;EACzB;EACA,OAAO,IAAI,kBAAkB,SAAQ;EACrC,OAAO,IAAI,kBAAkB,aAAY;EACzC,KAAK,UAAU,MAAM;EACrB,KAAK,WAAW,MAAM;CACxB;CAEQ,MAAM,OAAqB;EACjC,OAAO,KAAK,YAAY,MAAM,MAAM,KAAK,CAAC,KAAK;CACjD;CAEQ,mBAAgB;EACtB,KAAK,MAAM,SAAS,KAAK,OAAO,OAAM,GACpC,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC,GAC5B,KAAK,aAAa,CAAC;EAGvB,KAAK,OAAO,MAAK;EACjB,KAAK,YAAY;CACnB;CAIQ,SAAS,OAAqB;EACpC,MAAM,OAAO,KAAK,IAAI,YAAW;EACjC,MAAM,QAAQ,KAAK;EACnB,MAAM,MAAM,MAAM,KAAK;EACvB,IAAI,KAAK,YAAY;GACnB,MAAM,MAAM,KAAK,MAAM,KAAK;GAC5B,IAAI,KAAK,MAAM,WAAW,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,OAAM,CAAE,CAAC,CAAC,MAAM,MAAM,EAAE,SAAS,CAAC,GAChF,KAAK,YAAY;QACZ,IAAI,QAAQ,KAAK,WACtB,OAAO;IAAE,MAAM;IAAU;IAAK,OAAO,SAAS;GAAM;EAExD;EACA,IAAI,KAAK,MAAM,WAAW,KAAK,SAAS,QACtC,OAAO,EAAE,MAAM,OAAM;EAEvB,MAAM,QAAQ,KAAK,MAAM,SAAS;EAClC,IAAI,OAAO,sBAAsB,cAAc,SAAS,QAAQ;GAC9D,MAAM,iBAAiB,SAAS,OAAO,aAAa;GACpD,IAAI,mBAAmB,QAAQ,KAAK,kBAAkB,MAAM;IAG1D,IAAI,iBAAiB,KAAK,eACxB,OAAO,EAAE,MAAM,OAAM;IAEvB,MAAM,aAAa,eACjB,KAAK,OACL,QACC,MAAM,EAAE,WAAW,QAAQ,EAAE,UAAU,cAAc;IAExD,IAAI,cAAc,GAChB,OAAO;KAAE,MAAM;KAAO,OAAO;IAAU;GAE3C;GACA,MAAM,WAAW,OAAO,eAAe;GACvC,IAAI,QAAQ,KAAK,MAAM,WAAW,GAAG,MAAM,IAAI,SAAS,EAAE,iBAAiB,QAAQ;GACnF,IAAI,QAAQ,GAEV,QAAQ,eAAe,KAAK,OAAO,QAAQ,MAAM,EAAE,QAAQ,GAAG;GAGhE,OAAO,SAAS,IAAI;IAAE,MAAM;IAAO;GAAK,IAAK,EAAE,MAAM,OAAM;EAC7D;EACA,IAAI,KAAK,OAAO,kBAAiB,CAAE,EAAE,OAAO,YAC1C,OAAO,EAAE,MAAM,UAAS;EAE1B,OAAO,EAAE,MAAM,OAAM;CACvB;CAIQ,YAAY,OAAuB,KAAwB;EACjE,MAAM,YAAY,MAAM,SAAS;EACjC,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,uDAAuD;EAEzE,MAAM,YAAY,IAAI,gBAAe;EACrC,MAAM,WAAW,IAAI,kBAAiB;EACtC,MAAM,OAAO,IAAI,aAAa,KAAK,KAAK;EACxC,MAAM,SAAS,KAAK,OAAM;EAC1B,MAAM,WAAW,SAAS,OAAO;GAC/B,WAAW;IACT;KAAE,SAAS;KAAgB,UAAU;IAAK;IAC1C;KACE,SAAS;KACT,UAAU,KAAK,eAAe,mBAAmB,KAAK,KAAI,CAAE,CAAC,CAAC;IAC/D;IACD;KAAE,SAAS;KAAiB,UAAU;IAAS;IAC/C;KAAE,SAAS;KAAmB,UAAU;IAAQ;IAChD;KAAE,SAAS;KAAc,UAAU;IAAI;GACxC;GACD,QAAQ,OAAO;EAChB,CAAA;EACD,MAAM,MAAM,OAAO,gBAAgB,WAAW;GAC5C,OAAO,OAAO;GACd;GACA,qBAAqB;EACtB,CAAA;EACD,MAAM,QAAQ,KAAK,KAAK,KAAK,OAAO,WAAW,UAAU,IAAI;EAC7D,KAAK,WAAW,KAAK;EACrB,OAAO;CACT;CAOQ,KACN,KACA,OACA,WACA,UACA,MAAkB;EAIlB,MAAM,SAAS,SAAS,KAAK,SAAS,SAAQ,CAAE;EAChD,MAAM,UAAU,IAAI,SAAS;EAC7B,MAAM,QAAQ,KAAK,SAAS,cAAc,KAAK;EAC/C,MAAM,YAAY;EAClB,MAAM,WAAW;EACjB,KAAK,KAAK,aAAa,OAAO,OAAO;EACrC,MAAM,YAAY,OAAO;EACzB,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,SAAS;GACT,WAAW;GACX;GACA,KAAK,MAAM,KAAK;GAChB,cAAc,KAAK,OAAO,kBAAiB,CAAE,EAAE,MAAM,KAAK,WAAW,MAAM;GAC3E;GACA,eAAe;GACf,aAAa;GACb,UAAU;GAEV,KAAK,KAAK,OAAO;EAClB;CACH;CAWQ,WAAW,OAAiB;EAClC,MAAM,UAAU,YAAW;EAC3B,MAAM,WAAW;EACjB,MAAM,YAAY,MAAM,MAAM;EAC9B,MAAM,SAAS,YAAY,qBAAqB,SAAS,IAAI;EAC7D,IAAI,CAAC,QACH;EAEF,MAAM,QAAQ,MAAM;EACpB,MAAM,WAAW,cAAc;GAAC,MAAM;GAAa,MAAM;GAAQ,MAAM;EAAI,CAAC,CAAC,CAAC,WAC3E,CAAC,OAAO,QAAQ,UAAS;GACxB,MAAM,MAA+B;IAAE,GAAG;IAAO,GAAG;IAAQ,GAAG;GAAI;GACnE,KAAK,MAAM,EAAE,kBAAkB,OAAO,QACpC,MAAM,IAAI,SAAS,cAAc,IAAI,aAAa;EAEtD,CAAC;CAEL;CAEQ,aAAa,OAAiB;EACpC,MAAM,UAAU,YAAW;EAC3B,MAAM,WAAW;EACjB,IAAI,CAAC,MAAM,SACT,MAAM,IAAI,QAAO;EAEnB,MAAM,MAAM,OAAM;CACpB;CAEQ,oBAAoB,OAA6B;EACvD,KAAK,kBAAkB,KAAK,KAAK;EACjC,KAAK,gBAAgB,KAAK,KAAK;CACjC;CAEQ,kBAAkB,OAA6B;EACrD,KAAK,gBAAgB,KAAK,KAAK;EAC/B,KAAK,cAAc,KAAK,KAAK;CAC/B;CAEQ,YAAS;EACf,MAAM,QAAQ,KAAK,MAAM;EACzB,KAAK,WAAW,IAAI,KAAK;EACzB,MAAM,SAAwB;GAAE;GAAO,QAAQ,KAAK;EAAS;EAC7D,KAAK,aAAa,KAAK,MAAM;EAC7B,KAAK,YAAY,KAAK,MAAM;CAC9B;CAGQ,UAAU,OAAwB;EACxC,IAAI,KAAK,UAAU,KAAK,WAAW,OACjC,KAAK,OAAO,KAAK,OAAO,IAAI,IAAI;EAElC,OAAO,KAAK,OAAO,IAAI,KAAK;EAC5B,KAAK,SAAS;CAChB;CAOQ,OAAO,OAAmB,OAAqB;EACrD,IACE,KAAK,YAAY,SACjB,MAAM,KAAK,MAAM,MAAM,OACtB,KAAK,eAAe,QAAQ,KAAK,MAAM,KAAK,MAAM,KAAK,aACxD,CAAC,KAAK,MAAM,SAAS,KAAK,GAE1B,OAAO;EAET,KAAK,UAAU;EACf,KAAK,WAAW,OAAO,KAAK;EAC5B,KAAK,UAAS;EACd,KAAK,aAAY;EACjB,OAAO;CACT;CAOQ,iBAAiB,OAAkB;EACzC,MAAM,SAAS,KAAK,UAAU,KAAK,aAAa,KAAK;EACrD,IAAI,CAAC,UAAU,KAAK,SAClB;EAEF,MAAM,YAAY,OAAO;EACzB,MAAM,QAAQ,UAAU,MAAK;EAC7B,IAAI,UAAU,gBAAgB,UAAU,cAAc,UAAU,YAC9D,UAAU,cAAa;OAClB,IAAI,UAAU,cAAc,UAAU,YAC3C,UAAU,aAAY;OACjB,IAAI,UAAU,gBAAgB,UAAU,YAAY,UAAU,aACnE,UAAU,cAAa;OAClB,IAAI,UAAU,cAAc,UAAU,WAC3C,UAAU,aAAY;CAE1B;CAEQ,gBAAa;EACnB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,8CAA8C;EAEhE,OAAO,KAAK;CACd;CASQ,cACN,MACA,UACA,SACA,SACA,OAAgB;EAEhB,MAAM,kBAAkB,SAAS,SAAS,KAAK;EAC/C,KAAK,mBAAmB;EACxB,KAAK,kBAAiB;EACtB,MAAM,UAAU,SAAS,QAAQ,YAAY,QAAQ,CAAC,mBAAmB,KAAK,SAAQ;EACtF,MAAM,QACJ,QAAQ,UAAU;GAAE;GAAM,UAAU,WAAW;EAAe,IAAK;EAErE,KAAK,MAAM,UAAU,SAAS,IAAI;EAClC,SAAS,UAAU,cAAa;EAChC,SAAS,UAAU,cAAa;EAChC,IAAI,SAAS,CAAC,iBACZ,KAAK,oBAAoB,KAAK;EAGhC,IAAI,aAAuC;EAC3C,MAAM,iBAAsB;GAC1B,KAAK,UAAU;GACf,YAAY,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;GAChD,KAAK,YAAW;GAChB,SAAS,UAAU,aAAY;GAC/B,KAAK,MAAM,KAAK,SACd,IAAI,MAAM,UACR,KAAK,aAAa,CAAC;GAGvB,IAAI,WAAW,CAAC,QAAQ,SAAS,OAAO,GAAG;IAEzC,QAAQ,IAAI,kBAAkB,cAAa;IAC3C,QAAQ,IAAI,kBAAkB,OAAM;GACtC;GACA,SAAS,UAAU,aAAY;GAC/B,KAAK,aAAY;GACjB,KAAK,UAAS;GACd,KAAK,UAAU,UAAU,KAAK;GAC9B,IAAI,OACF,KAAK,kBAAkB,KAAK;EAEhC;EAEA,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS;GACjC,SAAQ;GACR;EACF;EACA,MAAM,IAAI,KAAK,gBAAgB,MAAM,UAAU,OAAO;EACtD,aAAa;EACb,KAAK,UAAU;GAAE,YAAY;GAAG;EAAQ;EACxC,IAAI,KAAK,OAAO,sBAAsB;GAGpC,EAAE,WAAW,SAAS,MAAM,EAAE,MAAK,CAAE;GACrC,KAAK,aAAa,4BAA2B;IAC3C,KAAK,aAAa,4BAA2B;KAC3C,KAAK,aAAa;KAClB,IAAI,KAAK,SAAS,eAAe,GAC/B,EAAE,WAAW,SAAS,MAAM,EAAE,KAAI,CAAE;IAExC,CAAC;GACH,CAAC;EACH;EACA,AAAK,EAAE,SAAS,WAAU;GACxB,IAAI,CAAC,KAAK,aAAa,KAAK,SAAS,eAAe,GAClD,SAAQ;EAEZ,CAAC;CACH;CAEQ,gBACN,MACA,UACA,SAAmB;EAEnB,IAAI,qBAAqB,KAAK,SAAS,WAAW,GAChD,OAAO,kBAAkB,SAAS,OAAO,QAAQ,KAAK;EAExD,IAAI,KAAK,SAAS,MAChB,OAAO,aAAa,MAAM,SAAS,OAAO,QAAQ,KAAK;EAEzD,OAAO,iBACL,MACA,SAAS,OACT,QAAQ,OACR,KAAK,IAAG,CAAE,CAAC,eACX,KAAK,KAAK,WAAW;CAEzB;CAGQ,sBAAmB;EACzB,MAAM,UAAU,KAAK;EACrB,IAAI,SACF,QAAQ,SAAQ;CAEpB;CAEQ,oBAAiB;EACvB,IAAI,KAAK,iBAAiB,MAAM;GAC9B,aAAa,KAAK,YAAY;GAC9B,KAAK,eAAe;EACtB;CACF;CAEQ,cAAW;EACjB,KAAK,MAAM,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;EAC/C,KAAK,OAAO;CACd;CAEQ,UAAU,UAAsB,OAAgB;EACtD,IAAI,UAAU,WAAW;GACvB,CAAC,SAAS,eAAe,SAAS,MAAA,CAAO,MAAM,EAAE,eAAe,KAAI,CAAE;GACtE,SAAS,cAAc;EACzB,OAAO,IAAI,UAAU,SAAS;GAC5B,MAAM,QAAQ,SAAS,MAAM,cAA2B,UAAU;GAClE,IAAI,SAAS,CAAC,MAAM,aAAa,UAAU,GACzC,MAAM,aAAa,YAAY,IAAI;GAErC,CAAC,SAAS,SAAS,MAAA,CAAO,MAAM,EAAE,eAAe,KAAI,CAAE;EACzD;CACF;CAGQ,MACN,UACA,SACA,MAA6B;EAE7B,MAAM,MAAM,SAAS,QAAQ,UAAU;EACvC,MAAM,QAAQ,SAAS,QAAQ,WAAW;EAC1C,KAAK,MAAM,KAAK,KAAK,OACnB,IAAI,MAAM,YAAY,MAAM,SAC1B,KAAK,EAAE,KAAK;EAGhB,OAAO,SAAS,KAAK;EACrB,IAAI,SAAS;GACX,QAAQ,MAAM,MAAM,aAAa;GACjC,QAAQ,MAAM,aAAa,SAAS,EAAE;EACxC;EACA,IAAI,OACF,MAAM,MAAM,MAAM,SAAS;EAE7B,IAAI,KACF,IAAI,MAAM,MAAM,SAAS;CAE7B;CAGQ,eAAY;EAClB,MAAM,MAAM,KAAK,MAAM,KAAK,MAAM,SAAS;EAC3C,KAAK,MAAM,KAAK,KAAK,OACnB,IAAI,MAAM,KAAK;GACb,OAAO,EAAE,KAAK;GACd,EAAE,MAAM,MAAM,SAAS;EACzB,OACE,KAAK,EAAE,KAAK;CAGlB;CAWiB,kBAAkB,UAA2B;EAC5D,IAAI,KAAK,SAAS,WAAW,MAAM,YACjC,MAAM,eAAc;CAExB;CAEiB,iBAAiB,UAA6B;EAG7D,IACE,MAAM,gBAAgB,WACtB,CAAC,KAAK,aAAY,KAClB,KAAK,MAAM,SAAS,KACpB,CAAC,MAAM,aACP,YAAY,IAAI,KAAK,GAErB;EAEF,MAAM,OAAO,KAAK,KAAK,sBAAqB;EAG5C,IAAI,MAAM,UAAU,KAAK,OAAO,MAC9B;EAIF,YAAY,IAAI,KAAK;EACrB,IAAI,KAAK,WAAW,KAAK,WAAW,KAAK,MACvC;EAEF,MAAM,MAAM,KAAK,MAAM,KAAK,MAAM,SAAS;EAC3C,MAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,SAAS;EAE7C,IAAI,CAAC,OAAO,CAAC,SAAS,QAAQ,KAAK,UAAU,MAAM,SACjD;EAEF,KAAK,UAAU;GACb,WAAW,MAAM;GACjB,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,SAAS;GACT;GACA;GACA,OAAO,KAAK;GACZ,SAAS,CAAC;IAAE,GAAG,MAAM;IAAS,GAAG,MAAM;GAAS,CAAE;GAClD,YAAY;EACb;EACD,KAAK,KAAK,iBAAiB,eAAe,KAAK,aAAa;EAC5D,KAAK,KAAK,iBAAiB,aAAa,KAAK,WAAW;EACxD,KAAK,KAAK,iBAAiB,iBAAiB,KAAK,eAAe;EAChE,KAAK,KAAK,iBAAiB,aAAa,KAAK,gBAAgB,EAAE,SAAS,MAAK,CAAE;CACjF;CAEiB,iBAAiB,UAA6B;EAC7D,MAAM,IAAI,KAAK;EACf,IAAI,GAAG,cAAc,MAAM,aAAa,CAAC,GACvC;EAEF,MAAM,KAAK,MAAM,UAAU,EAAE;EAC7B,MAAM,KAAK,MAAM,UAAU,EAAE;EAC7B,IAAI,CAAC,EAAE,SAAS;GACd,IAAI,KAAK,IAAI,EAAE,IAAI,SAAS,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,GAAG;IACvD,KAAK,WAAU;IACf;GACF;GACA,IAAI,KAAK,SAAS,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,GAC3C;GAEF,EAAE,UAAU;GACZ,MAAM,UAAU,KAAK,SAAS;GAC9B,KAAK,aACH,mBAAmB,eAAe,EAAE,IAAI,MAAM,SAAS,OAAO,IAAI,UAAU;GAC9E,IAAI;IACF,KAAK,KAAK,kBAAkB,EAAE,SAAS;GACzC,QAAQ,CAER;GACA,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,KAAK;GAChC,EAAE,aAAa,iBACb,OACA,EAAE,MAAM,OACR,EAAE,IAAI,OACN,KAAK,IAAG,CAAE,CAAC,eACX,EAAE,OACF,QAAQ;GAEV,EAAE,WAAW,WAAW,SAAS,MAAM,EAAE,MAAK,CAAE;GAChD,KAAK,oBAAoB;IAAE,MAAM;IAAO,UAAU;GAAI,CAAE;EAC1D;EACA,EAAE,QAAQ,KAAK;GAAE,GAAG,MAAM;GAAS,GAAG,MAAM;EAAS,CAAE;EACvD,IAAI,EAAE,QAAQ,SAAS,GACrB,EAAE,QAAQ,MAAK;EAEjB,MAAM,OAAO,MAAM,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,EAAE,YAAY,YAAY;EACpE,EAAE,YAAY,WAAW,SAAS,MAAO,EAAE,cAAc,IAAK;CAChE;CAEiB,eAAe,UAA6B;EAC3D,MAAM,IAAI,KAAK;EACf,IAAI,GAAG,cAAc,MAAM,aAAa,CAAC,GACvC;EAEF,IAAI,CAAC,EAAE,SAAS;GACd,KAAK,WAAU;GACf;EACF;EACA,MAAM,QAAQ,MAAM,UAAU,EAAE;EAChC,MAAM,QAAQ,EAAE,QAAQ,MAAM;GAAE,GAAG,EAAE;GAAQ,GAAG,MAAM;EAAS;EAC/D,MAAM,YAAY,MAAM,UAAU,MAAM,KAAK,KAAK,IAAI,GAAG,MAAM,YAAY,MAAM,CAAC;EAClF,MAAM,WAAW,kBAAkB,UAAU,OAAO,EAAE,KAAK;EAC3D,MAAM,WAAW,eAAe,WAAW,EAAE,QAAQ,QAAQ,OAAO,QAAQ;EAC5E,EAAE,YAAY,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;EAClD,MAAM,SAAS,UACb,EAAE,IAAI,OACN,EAAE,MAAM,OACR,KAAK,IAAG,CAAE,CAAC,eACX,EAAE,OACF,MAAM,QAAQ,EAAE,OAAO,GAAG,CAAC,GAC3B,WAAW,IAAI,GACf,QAAQ;EAEV,KAAK,WAAU;EACf,KAAK,OAAO;EACZ,AAAK,OAAO,SAAS,WAAU;GAC7B,IAAI,KAAK,aAAa,KAAK,SAAS,QAClC;GAEF,IAAI,UAAU;IAEZ,KAAK,mBAAmB;IACxB,KAAK,IAAI,KAAI;IACb,KAAK,eAAe,iBAAgB;KAClC,KAAK,eAAe;KACpB,IAAI,KAAK,kBAAkB;MAEzB,KAAK,mBAAmB;MACxB,KAAK,IAAI,UAAS;MAClB,KAAK,uBAAsB;KAC7B;IACF,GAAG,gBAAgB;GACrB,OACE,KAAK,YAAW;EAEpB,CAAC;CACH;CAEiB,wBAA6B;EAC5C,MAAM,IAAI,KAAK;EACf,IAAI,GAAG,SAAS;GACd,EAAE,YAAY,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;GAClD,KAAK,YAAW;EAClB;EACA,KAAK,WAAU;CACjB;CAGQ,cAAW;EACjB,KAAK,YAAW;EAChB,KAAK,aAAY;EAEjB,MAAM,MAAM,KAAK;EACjB,MAAM,QAAQ,KAAK;EACnB,KAAK,aAAa;EAClB,IAAI,KACF,CAAC,SAAS,IAAI,MAAM,cAA2B,UAAU,KAAK,IAAI,MAAA,CAAO,MAAM,EAC7E,eAAe,KAChB,CAAA;EAEH,KAAK,kBAAkB;GAAE,MAAM;GAAO,UAAU;EAAI,CAAE;CACxD;CAOQ,WAAQ;EACd,MAAM,MAAM,KAAK;EACjB,MAAM,UAAU,SAAS,KAAK,SAAS,SAAQ,CAAE;EACjD,IAAI,CAAC,OAAO,KAAK,MAAM,SAAS,KAAK,IAAI,WAAW,QAAQ,YAAY,MACtE,OAAO;EAET,MAAM,QAAQ,IAAI,SAAS,IAAI;EAC/B,OAAO,QAAQ,IAAI,QAAQ;CAC7B;CAEQ,aAAU;EAChB,KAAK,KAAK,oBAAoB,eAAe,KAAK,aAAa;EAC/D,KAAK,KAAK,oBAAoB,aAAa,KAAK,WAAW;EAC3D,KAAK,KAAK,oBAAoB,iBAAiB,KAAK,eAAe;EACnE,KAAK,KAAK,oBAAoB,aAAa,KAAK,cAAc;EAC9D,KAAK,UAAU;CACjB;CAGQ,yBAAsB;EAC5B,MAAM,MAAM,KAAK;EACjB,MAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,SAAS;EAC7C,MAAM,OAAO,KAAK;EAClB,IAAI,CAAC,OAAO,CAAC,OAAO;GAClB,KAAK,YAAW;GAChB;EACF;EACA,MAAM,IAAI,UACR,IAAI,OACJ,MAAM,OACN,KAAK,IAAG,CAAE,CAAC,eACX,KAAK,KAAK,aACV,GACA,GACA,cAAc;EAEhB,MAAM,WAAW,SAAS,MAAM,EAAE,OAAM,CAAE;EAC1C,KAAK,OAAO;EACZ,AAAK,EAAE,SAAS,WAAU;GACxB,IAAI,CAAC,KAAK,aAAa,KAAK,SAAS,GACnC,KAAK,YAAW;EAEpB,CAAC;CACH;CA/rCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,cAAA;GAAA,mBAAA;GAAA,iBAAA;GAAA,aAAA;GAAA,iBAAA;GAAA,eAAA;EAAA;EAAA,MAAA,EAAA,gBAAA,WAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,QAAA;GAAA,aAAA;GAAA,MA4D8B;GAAgB,UAAA;EAAA,GAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,KAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UAhGrE;;;;;;EAKiB,UAAA;EAAA,QAAA,CAAA,kfAAA;CAAA,CAAA;;AA+BhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EAvClC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAiB,MACrB,EAAE,OAAO,WAAU;GAAE,UACjB;;;;;;GAKiB,QAAA,CAAA,kfAAA;EAAA,CAAA;CA2FkB,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,cAAA;EAAA,CAAA;EAAA,mBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,mBAAA;EAAA,CAAA;EAAA,iBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,iBAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,iBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,iBAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,eAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,UAAQ;IAAI,MAAM;IAAkB,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,KAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACd,OAAK,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AAsoC1E,SAAS,YAAY,OAAyB;CAC5C,IAAI,UAAU,UAAa,OAAO,MAAM,KAAK,GAC3C,OAAO;CAET,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACtC;AAEA,SAAS,KAAK,OAAkB;CAC9B,MAAM,MAAM,aAAa;CACzB,MAAM,MAAM,SAAS;CACrB,MAAM,aAAa,SAAS,EAAE;CAC9B,MAAM,aAAa,eAAe,MAAM;AAC1C;AAEA,SAAS,OAAO,OAAkB;CAChC,MAAM,MAAM,aAAa;CACzB,MAAM,gBAAgB,OAAO;CAC7B,MAAM,gBAAgB,aAAa;AACrC;AAEA,SAAS,MAAM,OAAqB;CAClC,OACE,MACA,MAAM,SAAS,aACZ,SAAS,MAAM,EAAE,IAAI,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,CACxC,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,CAC3B,KAAK,GAAG;AAEf;AAEA,SAAS,eACP,OACA,OACA,OAAqC;CAErC,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,GAAG,KAAK;EACnC,MAAM,QAAQ,MAAM;EACpB,IAAI,SAAS,MAAM,KAAK,GACtB,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAS,MAAM,GAAW,KAAa,KAAW;CAChD,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;AACvC;AC38CA,IAAa,kBAAb,MAAa,gBAAe;CACT,YAAY,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,KAAK,IAAI,gBAAe;CAGtF,YAAY,eAAe,KAAK,UAAU,MAAK,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAGlD,kBAAkB,OAAM;CAExB,iBAAiB,OAAM;CAEvB,kBAAkB,OAAM;CAExB,iBAAiB,OAAM;CAEhC,cAAA;EAIE,MAAM,MAAM,KAAK,UAAU,IAAI,UAAS;GACtC,QAAQ,OAAR;IACE,KAAK;KACH,KAAK,gBAAgB,KAAI;KACzB;IACF,KAAK;KACH,KAAK,eAAe,KAAI;KACxB;IACF,KAAK;KACH,KAAK,gBAAgB,KAAI;KACzB;IACF,KAAK;KACH,KAAK,eAAe,KAAI;KACxB;GACJ;EACF,CAAC;EACD,MAAM,aAAa,OAAO,UAAU;EACpC,WAAW,UAAU,GAAG;EAGxB,MAAM,OAAO,OAAO,mBAAmB,EAAE,UAAU,KAAI,CAAE;EACzD,MAAM,UAAU,OAAO,aAAa;EACpC,IAAI,MAAM,YAAY,MAAM;GAC1B,KAAK,UAAU;GACf,WAAW,gBAAe;IACxB,IAAI,KAAK,YAAY,SACnB,KAAK,UAAU;GAEnB,CAAC;EACH,OAEE,QAAQ,aAAa,IAAI,KAAK;CAElC;CApDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAf,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAe,cAAA;EAAA,UAAA;EAAA,SAAA;GAAA,iBAAA;GAAA,gBAAA;GAAA,iBAAA;GAAA,gBAAA;EAAA;EAAA,MAAA,EAAA,gBAAA,uCAAA;EAAA,WAHf,CAAC,aAAa;EAAC,UAAA;EAAA,UAChB;EAAgB,UAAA;CAAA,CAAA;;AAEf,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAe,YAAA,CAAA;EAN3B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,MAAM,EAAE,OAAO,uCAAsC;GACrD,WAAW,CAAC,aAAa;GACzB,UAAU;EACX,CAAA;;;;;;;;;;;;;;;;;;;;;;ACdD,MAAM,cACJ;AAqCF,IAAa,wBAAb,MAAa,sBAAqB;CACf,MAAM,OAAO,eAAe;CAC5B,SAAS,OAAO,SAAS;CAGjC,cAAc,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CAKvB,QAAQ,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,gCAAA,CAAA,CAAA;CAGP,OAAoB,gBAAe;CACnC,aAAa;CACb,cAAc;CACd,YAAY;CAGtB,OAAO,eAAe,KAAK,MAAK,KAAM,KAAK,OAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,gCAAA,CAAA,CAAA;CAGvD,UAAU,eAAe,KAAK,IAAI,UAAS,KAAM,KAAK,YAAW,MAAO,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAGpF,KAAE;EACA,IAAI,KAAK,IAAI,UAAS,GACpB,KAAK,IAAI,KAAI;OAEb,AAAK,KAAK,IAAI,KAAK,KAAK,YAAW,CAAE;CAEzC;CA/BW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAqB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAArB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAqB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,qBAAA,OAAA;GAAA,gBAAA;EAAA;EAAA,UAAA;EAAA,UAjBtB;;;;;;;;;;;;;;;;EAeT,UAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MAlBS;GAAqB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,SAAA,CAAA,SAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAAe,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;CAAA,CAAA;;AAoBrC,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAqB,YAAA,CAAA;EAtBjC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS,CAAC,uBAAuB,eAAe;GAEhD,MAAM;IAAE,OAAO;IAA8B,uBAAuB;GAAM;GAC1E,UAAU;;;;;;;;;;;;;;;;EAgBX,CAAA;;;;;;;;;;;;;;;;;;;;;AC1BD,IAAa,8BAAb,MAAa,4BAA2B;CAA3B,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAA2B,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAA3B,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAA2B,cAAA;EAAA,UAAA;EAAA,MAAA;GAAA,YAAA,EAAA,kCAAA,OAAA;GAAA,gBAAA;EAAA;EAAA,UAAA;CAAA,CAAA;;AAA3B,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA2B,YAAA,CAAA;EAJvC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,MAAM;IAAE,OAAO;IAA2B,oCAAoC;GAAI;EACnF,CAAA;;;ACMD,MAAM,mBAAmB;AAEzB,MAAM,kBAAkB;AAExB,MAAM,qBAAqB;AAwC3B,IAAa,4BAAb,MAAa,0BAAyB;CAE3B,mBAAmB,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,mBAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAE5D,yBAAyB,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,yBAAA,CAAA,gCAAA,CAAA,CAAA;CAElC,2BAA2B,MAAgC,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,2BAAA,CAAA,gCAAA,CAAA,CAAA;CAE5D,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,QAAQ;CAC1B,WAAW,OAAO,iBAAiB;CAC5C,iBAAsC;CACtC,aAAmD;CACnD,eAAqD;CACrD,gBAAgB;CAChB,eAAe,OAAO;CAE9B,cAAA;EACE,MAAM,iBAAsB;GAC1B,KAAK,eAAe,KAAK,IAAG;GAC5B,IAAI,KAAK,gBACP,KAAK,kBAAkB,eAAe;EAE1C;EACA,MAAM,aAAa,UAA2B;GAE5C,IACE,KAAK,iBAAgB,KACrB,KAAK,SAAS,QAAO,KACrB,YAAY,MAAM,MAAwB,GAE1C,KAAK,eAAc;EAEvB;EAGA,MAAM,iBAAuB,KAAK,cAAa;EAC/C,MAAM,OAAO,KAAK,SAAS;EAC3B,KAAK,KAAK,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAI,CAAE;EAChE,KAAK,KAAK,iBAAiB,WAAW,SAAS;EAC/C,MAAM,iBAAiB,UAAU,QAAQ;EACzC,MAAM,gBAAgB,iBAAiB,UAAU,QAAQ;EACzD,OAAO,UAAU,CAAC,CAAC,gBAAe;GAChC,KAAK,aAAY;GACjB,KAAK,KAAK,oBAAoB,UAAU,QAAQ;GAChD,KAAK,KAAK,oBAAoB,WAAW,SAAS;GAClD,MAAM,oBAAoB,UAAU,QAAQ;GAC5C,MAAM,gBAAgB,oBAAoB,UAAU,QAAQ;EAC9D,CAAC;EAKD,kBAAkB;GAChB,iBAAgB;IACd,KAAK,SAAS,MAAK;IACnB,OAAO,KAAK,eAAc;GAC5B;GACA,QAAQ,YAAY,KAAK,aAAa,QAAO,CAAE;EAChD,CAAA;EAGD,IAAI,eAAe,gBAAgB,KAAK,SAAS,SAAQ,CAAE;EAC3D,aAAY;GACV,MAAM,QAAQ,KAAK,SAAS,SAAQ;GACpC,IAAI,UAAU,cACZ;GAEF,eAAe;GACf,gBAAe;IACb,IAAI,KAAK,iBAAgB,KAAM,KAAK,SAAS,QAAO,GAClD,KAAK,eAAc;GAEvB,CAAC;EACH,CAAC;EAOD,MAAM,WAAW,OAAO,QAAQ;EAChC,aAAY;GACV,IAAI,KAAK,SAAS,MAAK,MAAO,SAC5B;GAEF,gBACE,gBACE;IACE,iBAAiB,KAAK,kBAAiB;IACvC,QAAQ,UAAS;KACf,IAAI,KAAK,SAAS,MAAK,MAAO,SAC5B,KAAK,aAAa,OAAO,SAAS;IAEtC;GACD,GACD,EAAE,SAAQ,CAAE,CACb;EAEL,CAAC;CACH;CAOQ,oBAAiB;EACvB,IAAI,KAAK,SAAS,MAAK,MAAO,WAAW,CAAC,KAAK,iBAAgB,KAAM,KAAK,eACxE,OAAO;EAET,MAAM,cAAc,KAAK,IAAG,IAAK,KAAK;EACtC,IAAI,cAAc,iBAAiB;GACjC,KAAK,aAAa,kBAAkB,mBAAkB;IACpD,IAAI,KAAK,SAAS,MAAK,MAAO,SAC5B,KAAK,OAAO,SAAS;GAEzB,CAAC;GACD,OAAO;EACT;EACA,KAAK,aAAY;EACjB,OAAO,KAAK,YAAW;CACzB;CAOA,UAAO;EACL,KAAK,cAAa;EAClB,IAAI,KAAK,iBAAgB,KAAM,KAAK,SAAS,QAAO,KAAM,CAAC,KAAK,eAC9D,KAAK,wBAAwB,KAAK,OAAO,SAAS,CAAC;CAEvD;CAMQ,cAAW;EACjB,OAAO,KAAK,KAAK,kBAAkB,EAAE,oBAAoB,KAAI,CAAE,MAAM;CACvE;CAGQ,QAAQ,QAAc;EAC5B,IAAI,UAAU,KAAK,CAAC,KAAK,YAAW,GAClC,OAAO;EAET,MAAM,cAAc,KAAK,SAAS,YAAW;EAC7C,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAK,sBAAqB,CAAE,CAAC,SAAS,WAAW,CAAC;CACvF;CAGQ,gBAAa;EACnB,KAAK,aAAa,KAAK,eAAc,CAAE;CACzC;CAGQ,iBAAc;EACpB,MAAM,UAAU,KAAK,SAAS,QAAO,IAAK,KAAK,SAAS,OAAM,IAAK;EACnE,MAAM,SAAS,KAAK,iBAAgB,IAAK,UAAU;EACnD,OAAO;GACL;GACA,QAAQ,KAAK,uBAAsB;GACnC,SAAS,KAAK,QAAQ,MAAM;GAC5B,OAAO,KAAK,YAAW;EACxB;CACH;CAEQ,aAAa,EAAE,QAAQ,QAAQ,SAAS,SAAwB;EACtE,IAAI,UAAU,GAAG;GACf,KAAK,KAAK,MAAM,YAAY,kBAAkB,GAAG,QAAO,GAAI;GAC5D,KAAK,KAAK,MAAM,YAAY,yBAAyB,GAAG,UAAU,OAAM,GAAI;EAC9E,OAAO;GACL,KAAK,KAAK,MAAM,eAAe,gBAAgB;GAC/C,KAAK,KAAK,MAAM,eAAe,uBAAuB;EACxD;EACA,IAAI,UAAU,KAAK,CAAC,OAClB,KAAK,aAAY;CAErB;CAOQ,iBAAc;EACpB,KAAK,aAAa,wBAAwB,KAAK,OAAO,KAAK,yBAAwB,CAAE,CAAC;EACtF,KAAK,gBAAgB;CACvB;CAGQ,kBAAkB,UAAoB;EAC5C,MAAM,cAAc,KAAK,IAAG,IAAK,KAAK;EACtC,IAAI,eAAe,iBAAiB;GAClC,KAAK,aAAY;GACjB,SAAQ;GACR;EACF;EACA,KAAK,aAAa,kBAAkB,aAAa,QAAQ;CAC3D;CAEQ,aAAa,aAAqB,UAAoB;EAC5D,KAAK,aAAY;EACjB,KAAK,iBAAiB;EACtB,KAAK,kBAAkB,WAAW;EAClC,KAAK,eAAe,iBAAiB,KAAK,aAAY,GAAI,kBAAkB;CAC9E;CAEQ,kBAAkB,MAAY;EACpC,IAAI,KAAK,eAAe,MACtB,aAAa,KAAK,UAAU;EAE9B,KAAK,aAAa,iBAAiB,KAAK,aAAY,GAAI,IAAI;CAC9D;CAEQ,eAAY;EAClB,MAAM,WAAW,KAAK;EACtB,KAAK,aAAY;EACjB,WAAU;CACZ;CAGQ,eAAY;EAClB,KAAK,iBAAiB;EACtB,KAAK,gBAAgB;EACrB,IAAI,KAAK,eAAe,MAAM;GAC5B,aAAa,KAAK,UAAU;GAC5B,KAAK,aAAa;EACpB;EACA,IAAI,KAAK,iBAAiB,MAAM;GAC9B,aAAa,KAAK,YAAY;GAC9B,KAAK,eAAe;EACtB;CACF;CAGQ,OAAO,UAA8B;EAC3C,KAAK,aAAa,KAAK,YAAW,GAAI,QAAQ;CAChD;CAIQ,cAAW;EACjB,MAAM,SAAS,KAAK,SAAS;EAC7B,IACE,CAAC,YAAY,MAAM,KACnB,CAAC,KAAK,KAAK,SAAS,MAAM,KAC1B,CAAC,KAAK,SAAS,QAAO,KACtB,CAAC,KAAK,YAAW,GAEjB,OAAO;EAET,MAAM,WAAW,KAAK,KAAK,sBAAqB;EAChD,MAAM,cAAc,KAAK,IAAI,SAAS,QAAQ,KAAK,SAAS,YAAW,CAAE;EACzE,MAAM,OAAO,OAAO,sBAAqB;EACzC,MAAM,SAAS,KAAK,uBAAsB;EAI1C,MAAM,OAAO,KAAK,IAAI,GAAG,cAAc,SAAS,MAAM,KAAK,MAAM;EACjE,MAAM,WAAW,KAAK,IAAI,QAAQ,IAAI;EACtC,MAAM,WAAW,KAAK,IAAI,QAAQ,OAAO,QAAQ;EACjD,IAAI,KAAK,SAAS,WAAW,aAC3B,OAAO,KAAK,SAAS,WAAW;EAElC,IAAI,KAAK,MAAM,WAAW,SAAS,KACjC,OAAO,KAAK,MAAM,WAAW,SAAS;EAExC,OAAO;CACT;CAEQ,aAAa,OAAe,UAA8B;EAChE,IAAI,UAAU,GACZ;EAEF,IAAI,aAAa,UAEf,KAAK,eAAe,KAAK,IAAG;EAE9B,KAAK,KAAK,SAAS;GAAE,KAAK;GAAO;EAAQ,CAAE;CAC7C;CA3RW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAyB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAzB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAyB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,kBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,wBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,0BAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;CAAA,CAAA;;AAAzB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAyB,YAAA,CAAA;EAHrC,MAAA;EAAU,MAAA,CAAA,EACT,UAAU,qBACX,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3DD,MAAM,cAAc;AAEpB,MAAM,mBAAmB;AAEzB,MAAM,YAAY;AAelB,SAAS,aAAa,MAAY;CAChC,OAAO,IAAI,eAAe,IAAI,KAAK,IAAI,CAAC,OAAO,gBAAgB;AACjE;AAGA,SAAS,UAAU,MAAiB,YAAkB;CACpD,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,UAAU,MAAM,eAAe,UAAU;AACzE;AAEA,SAAS,aAAa,MAAiB;CACrC,KAAK,MAAM,eAAe,WAAW;CACrC,KAAK,MAAM,eAAe,kBAAkB;CAC5C,KAAK,MAAM,eAAe,aAAa;AACzC;AAwBA,IAAa,+BAAb,MAAa,6BAA4B;CAE9B,sBAAsB,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,sBAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAEvD,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,QAAQ;CACnC,UAAmC;CACnC,SAA+E;CAEvF,cAAA;EACE,QAAQ,cAAa;GACnB,IAAI,CAAC,KAAK,oBAAmB,GAC3B;GAEF,MAAM,OAAO,KAAK;GAClB,MAAM,UAAmC,EAAE,SAAS,KAAI;GACxD,KAAK,iBAAiB,cAAc,KAAK,cAAc,OAAO;GAC9D,KAAK,iBAAiB,aAAa,KAAK,aAAa,OAAO;GAC5D,KAAK,iBAAiB,YAAY,KAAK,YAAY,OAAO;GAC1D,KAAK,iBAAiB,eAAe,KAAK,YAAY,OAAO;GAC7D,gBAAe;IACb,KAAK,oBAAoB,cAAc,KAAK,YAAY;IACxD,KAAK,oBAAoB,aAAa,KAAK,WAAW;IACtD,KAAK,oBAAoB,YAAY,KAAK,UAAU;IACpD,KAAK,oBAAoB,eAAe,KAAK,UAAU;IACvD,KAAK,WAAU;IACf,IAAI,KAAK,SAAS;KAChB,aAAa,KAAK,QAAQ,IAAI;KAC9B,KAAK,UAAU;IACjB;GACF,CAAC;EACH,CAAC;CACH;CAEiB,gBAAgB,UAA2B;EAC1D,MAAM,QAAQ,MAAM,QAAQ;EAC5B,MAAM,OAAO,KAAK,KAAK;EACvB,IAAI,KAAK,WAAW,CAAC,SAAS,EAAE,gBAAgB,cAC9C;EAGF,KAAK,WAAU;EACf,KAAK,UAAU;GACb,SAAS,MAAM;GACf,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,UAAU,KAAK,KAAK,cAAc,qBAAqB,MAAM;GAC7D;GACA,OAAO,MAAM;GACb,MAAM;GACN,MAAM;GACN,QAAQ;EACT;CACH;CAEiB,eAAe,UAA2B;EACzD,MAAM,UAAU,KAAK;EACrB,MAAM,QAAQ,UAAU,UAAU,MAAM,SAAS,QAAQ,OAAO,IAAI;EACpE,IAAI,CAAC,WAAW,CAAC,SAAS,QAAQ,SAAS,KACzC;EAEF,IAAI,QAAQ,SAAS,QAAW;GAC9B,MAAM,KAAK,MAAM,UAAU,QAAQ;GACnC,MAAM,KAAK,MAAM,UAAU,QAAQ;GACnC,IAAI,KAAK,MAAM,IAAI,EAAE,IAAI,WACvB;GAGF,QAAQ,OAAO,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,MAAM;GACnD,IAAI,QAAQ,SAAS,KACnB;EAEJ;EACA,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,QAAQ,QAAQ,MAAM;EACtB,IAAI,OAAO,GACT;EAEF,MAAM,OAAO,KAAK;EAClB,MAAM,QAAQ,KAAK,aAAa;EAChC,MAAM,WAAW,KAAK,aAAa,KAAK,eAAe,KAAK,eAAe;EAC3E,MAAM,WAAW,QAAQ;EAEzB,IADkB,QAAQ,OAAO,MAAM,QAAQ,WAAW,QAAQ,KAAK,IAAI,KAAK,IAE9E,QAAQ,OAAO,KAAK,IAAI,GAAG,QAAQ,OAAO,KAAK,IAAI,EAAE,CAAC;OACjD,IAAI,SAAS,KAAK,KAAK,CAAC,QAAQ,UAAU;GAC/C,QAAQ,QAAQ;GAChB,QAAQ,SAAS;EACnB,OAAO,IAAI,YAAY,KAAK,GAAG;GAC7B,QAAQ,QAAQ,CAAC;GACjB,QAAQ,SAAS;EACnB,OAAO,IAAI,CAAC,SAAS,CAAC,UACpB,QAAQ,OAAO;EAEjB,IAAI,WAAW,KAAK,QAAQ,OAAO,GACjC,KAAK,MAAM,OAAO;CAEtB;CAEiB,cAAc,UAA2B;EACxD,MAAM,UAAU,KAAK;EAErB,IAAI,CAAC,WAAW,UAAU,MAAM,SAAS,QAAQ,OAAO,GACtD;EAEF,KAAK,UAAU;EACf,MAAM,OAAO,QAAQ;EACrB,IAAI,QAAQ,QAAQ,KAAK,qBAAqB,KAAK,SAAS,WAAW,GAAG;GACxE,aAAa,IAAI;GACjB;EACF;EACA,MAAM,YAAY,KAAK,QACrB,CAAC,EAAE,WAAW,UAAU,aAAa,QAAQ,IAAI,EAAC,GAAG,GAAI,EAAE,WAAW,YAAW,CAAE,GACnF;GAAE,UAAA;GAAuB,QAAQ;GAAe,MAAM;EAAU,CAAE;EAEpE,KAAK,SAAS;GAAE;GAAW;EAAI;EAC/B,AAAK,UAAU,SAAS,WACjB;GACH,IAAI,KAAK,QAAQ,cAAc,WAC7B;GAEF,KAAK,SAAS;GACd,aAAa,IAAI;GACjB,UAAU,OAAM;EAClB,SACK,CAEL,CAAC;CAEL;CAEQ,MAAM,SAAyB;EACrC,MAAM,QAAQ,QAAQ,KAAK;EAC3B,MAAM,aAAa;EACnB,MAAM,kBAAkB,UAAU,QAAQ;EAC1C,MAAM,YAAY,UAAU,aAAa,QAAQ,IAAI,EAAC;CACxD;CAEQ,aAAU;EAChB,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,QACH;EAEF,KAAK,SAAS;EACd,OAAO,UAAU,OAAM;EACvB,aAAa,OAAO,IAAI;CAC1B;CAlJW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAA4B,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAA5B,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAA4B,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,qBAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,UAAA;CAAA,CAAA;;AAA5B,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAA4B,YAAA,CAAA;EAHxC,MAAA;EAAU,MAAA,CAAA,EACT,UAAU,wBACX,CAAA;;;;;;;;;;;;AC7CD,MAAM,mBACJ;AAEF,MAAM,WAAW;CACf;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAA,CAAC,KAAK,GAAG;AAMV,SAAS,YAAY,UAAqB;CACxC,KAAK,MAAM,MAAM,MAAM,KAAK,SAAS,iBAA8B,QAAQ,CAAC,GAAG;EAC7E,IAAI,GAAG,QAAQ,mBAAmB,GAChC;EAEF,IACE,OAAO,GAAG,oBAAoB,cAC9B,CAAC,GAAG,gBAAgB,EAAE,oBAAoB,KAAI,CAAE,GAEhD;EAEF,OAAO;CACT;CACA,OAAO;AACT;AAMA,MAAM,cAAc;AAGpB,MAAM,cAAoF;CACxF,QAAQ;CACR,YAAY;CACZ,0BAA0B;AAC3B;AAED,MAAM,mBAAmB;AAOzB,SAAS,YAAY,WAAgC;CACnD,IAAI,CAAC,WACH,OAAO;CAET,OACE,iBAAiB,KAAK,UAAU,SAAS,KACxC,UAAU,aAAa,cAAc,UAAU,iBAAiB;AAErE;AAyDA,IAAa,qBAAb,MAAa,mBAAkB;CAEpB,aAAa,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,aAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAEvD,iBAAiB,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,iBAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAE1D,eAAe,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,eAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAGzD,WAAW,OAAM;CAET,WAAW,UAAU,SAAkC,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAClE,cAAc,OAAO,eAAe,EAAE,UAAU,KAAI,CAAE;CACtD,WAAW,OAAO,iBAAiB;CAEnC,YAAY,YAAY,OAAO,QAAQ,CAAC,CAAC,aAAa,SAAS;CAE7D,MAAM,eAAe,KAAK,SAAS,KAAI,MAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,gCAAA,CAAA,CAAA;CAEpD,UAAU,eAAe,CAAC,KAAK,IAAG,KAAM,CAAC,KAAK,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CACxD,cAAc,eAAgB,KAAK,IAAG,IAAK,mBAAmB,IAAI,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CAGlE,OAAO,eAAgB,KAAK,aAAa,aAAY,IAAK,SAAS,MAAM,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,gCAAA,CAAA,CAAA;CAEzE,UAAU,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAGzB,cAAc,eAAe,KAAK,aAAa,YAAW,KAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CACnE,kBAAkB,eAEjC,GAAG,iBAAgB,GAAI,YAAY,KAAK,WAAU,IAAM,KAAK,YAAW,IAAK,eAAe,6BAA8B,aAC7H,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,gCAAA,CAAA,CAAA;CAED,cAAA;EAGE,QAAQ,cAAa;GACnB,IAAI,CAAC,KAAK,aAAY,GACpB;GAEF,MAAM,UAAU,KAAK,SAAQ,CAAE,CAAC;GAChC,MAAM,iBAAuB,KAAK,SAAS,KAAK,EAAE,WAAW,QAAQ,UAAS,CAAE;GAChF,QAAQ,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAI,CAAE;GAC9D,gBAAgB,QAAQ,oBAAoB,UAAU,QAAQ,CAAC;EACjE,CAAC;EAED,KAAK,mBAAkB;EAEvB,IAAI,KAAK,aAAa;GACpB,MAAM,UAAU,KAAK;GACrB,IAAI,aAAiC;GAGrC,sBAAqB;IACnB,aAAa,KAAK,SAAQ,CAAE,CAAC;IAC7B,QAAQ,SAAS,IAAI,UAAU;GACjC,CAAC;GAGD,kBAAkB,EAChB,aAAY;IACV,MAAM,UAAU,KAAK,SAAQ,CAAE,CAAC;IAChC,MAAM,SAAS,QAAQ,aAAY;IACnC,IAAI,SAAS,GACX,QAAQ,MAAM,YAAY,sBAAsB,GAAG,OAAM,GAAI;SAE7D,QAAQ,MAAM,eAAe,oBAAoB;GAErD,EACD,CAAA;GACD,OAAO,UAAU,CAAC,CAAC,gBAAe;IAChC,IAAI,cAAc,QAAQ,SAAQ,MAAO,YACvC,QAAQ,SAAS,IAAI,IAAI;GAE7B,CAAC;EACH;CACF;CAOQ,qBAAkB;EACxB,IAAI,QAAuB;EAC3B,IAAI,SAAgC;EACpC,IAAI,YAAqC;EACzC,MAAM,cAAmB;GACvB,QAAQ;GACR,MAAM,UAAU,KAAK,SAAQ,CAAE,CAAC;GAChC,KAAK,QAAQ,IACX,QAAQ,eAAe,QAAQ,eAAe,eAAe,CAAC,YAAY,OAAO,CAAC;EAEtF;EACA,MAAM,iBAAsB;GAC1B,UAAU,sBAAsB,KAAK;EACvC;EACA,sBAAqB;GACnB,MAAM,UAAU,KAAK,SAAQ,CAAE,CAAC;GAChC,MAAK;GACL,IAAI,OAAO,mBAAmB,YAAY;IACxC,SAAS,IAAI,eAAe,QAAQ;IACpC,OAAO,QAAQ,OAAO;IACtB,MAAM,OAAO,QAAQ;IACrB,IAAI,MACF,OAAO,QAAQ,IAAI;GAEvB;GACA,YAAY,IAAI,iBAAiB,QAAQ;GACzC,UAAU,QAAQ,SAAS;IACzB,WAAW;IACX,SAAS;IACT,YAAY;IACZ,iBAAiB;KACf;KACA;KACA;KACA;KACA;KACA;KACA;IACD;GACF,CAAA;EACH,CAAC;EACD,OAAO,UAAU,CAAC,CAAC,gBAAe;GAChC,QAAQ,WAAU;GAClB,WAAW,WAAU;GACrB,IAAI,UAAU,MACZ,qBAAqB,KAAK;EAE9B,CAAC;CACH;CAGA,IAAI,gBAAa;EACf,OAAO,KAAK,SAAQ,CAAE,CAAC;CACzB;CAMA,YAAY,WAA2B,UAAQ;EAC7C,KAAK,cAAc,SAAS;GAAE,KAAK;GAAG;EAAQ,CAAE;CAClD;CAQA,cAAc,GAAW,GAAW,WAA2B,UAAQ;EACrE,KAAK,cAAc,SAAS;GAAE,MAAM;GAAG,KAAK;GAAG;EAAQ,CAAE;CAC3D;CA3JW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAkB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAlB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAkB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,UAAA,WAAA;EAAA,MAAA,EAAA,gBAAA,uBAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,UAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UAXnB;;;;;;;;;;EASH,UAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MAXG;GAAyB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;GAAA;EAAA,GAAA;GAAA,MAAA;GAAA,MAAE;GAA4B,UAAA;GAAA,QAAA,CAAA,qBAAA;EAAA,CAAA;CAAA,CAAA;;AAatD,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAkB,YAAA,CAAA;EAf9B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS,CAAC,2BAA2B,4BAA4B;GACjE,MAAM,EAAE,OAAO,uBAAsB;GACrC,UAAU;;;;;;;;;;EAUX,CAAA;CAYyE,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,gBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,UAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,YAAU,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AC/HpF,MAAM,oBAAoB;AAG1B,MAAM,eAAe;AACrB,MAAM,eAAe;AAErB,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AAEtB,MAAM,cAAc;AAEpB,MAAM,YAAY;AAElB,MAAM,YAAY;AAMlB,MAAM,qBAAkD;CACtD,KAAK;CACL,IAAI;AACL;AAID,MAAM,iBAAiD;CACrD,OAAO;CACP,aAAa;AACd;AAOD,MAAM,kBAA+C;CACnD,KACE;CAEF,IAAI;AACL;AAQD,MAAM,cAA2C;CAC/C,KAAK;CACL,IAAI;CACJ,SAAS;AACV;AAMD,MAAM,sBACJ;AAEF,MAAM,oBAAiD;CACrD,KAAK;CACL,IAAI;AACL;AAKD,MAAM,qBAAkD;CACtD,KAAK;CACL,IAAI;AACL;AAMD,MAAM,sBACJ;AA4GF,IAAa,oBAAb,MAAa,kBAAiB;CAEnB,QAAQ,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,gCAAA,CAAA,CAAA;CAEjB,WAAW,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAKpB,aAAa,MAAM,MAAI;EAAA,GAAA,YAAA,EAAA,WAAA,aAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAKtD,cAAc,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,cAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAEhD,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,cAAc,OAAO,eAAe,EAAE,UAAU,KAAI,CAAE;CAEpD,iBAAiB,eACjC,KAAK,aAAa,YAAW,KAAM,OAAQ,gBAAgB,IAC7D,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,gCAAA,CAAA,CAAA;CACgB,WAAW,OAAO,iBAAiB;CACnC,WAAW,OAAO,QAAQ;CAC1B,UAAU,UAAU,SAAkC,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAChE,YAAY,UAAU,SAAkC,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CACpE,UAAU,UAAU,SAAkC,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAChE,YAAY,UAAmC,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAC3D,aAAa,UAAmC,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CAC7D,YAAY,UAAmC,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAC3D,aAAa,UAAmC,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CAM3D,UAAU,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CAEvB,YAAY,eAAe,KAAK,WAAU,KAAM,CAAC,KAAK,QAAO,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CAGhE,OAAoB,gBAAe;CACnC,aAAa,eACxB,mCAAmC,YAAY,KAAK,QAAO,IAAK,YAAY,KAAK,SACxF,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,gCAAA,CAAA,CAAA;CACkB,kBAAkB,kBAAkB,KAAK;CACzC,mBAAmB,mBAAmB,KAAK;CAC3C,mBAAmB,mBAAmB,KAAK;CAC3C,oBAAoB;CACpB,oBAAoB;CACpB,cAAc,eAE7B,GAAG,eAAe,KAAK,YAAW,IAAK,gBAAgB,SAAQ,GAAI,gBAAgB,KAAK,SAC3F,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,gCAAA,CAAA,CAAA;CAGO,WAAW;CAEX,cAAc;CAEd,cAAkC;CAE1C,cAAA;EACE,MAAM,aAAa,OAAO,UAAU;EAGpC,MAAM,OAAO,KAAK,SAAS;EAC3B,MAAM,eACJ,OAAO,MAAM,eAAe,aAAa,KAAK,WAAW,aAAa,IAAI;EAC5E,IAAI,cAAc;GAChB,KAAK,QAAQ,IAAI,aAAa,OAAO;GACrC,MAAM,mBAAmB,UAAqC,KAAK,QAAQ,IAAI,MAAM,OAAO;GAC5F,aAAa,iBAAiB,UAAU,eAAe;GACvD,WAAW,gBAAgB,aAAa,oBAAoB,UAAU,eAAe,CAAC;EACxF;EAEA,IAAI,eAA8B;EAClC,IAAI,eAA+B;EACnC,MAAM,eAAe,aAA+B;GAMlD,MAAM,QAAQ,UAAU,KAAK,SAAS;GACtC,MAAM,QAAQ,KAAK,cAAa;GAChC,MAAM,MAAM,SAAS,eAAe,SAAS;GAC7C,MAAM,YAAY,CAAC,SAAS,MAAM,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC;GAC3F,MAAM,WAAW,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,YAAY,KAAK,CAAC,IAAI,GAAI,IAAI;GAClF,IAAI,aAAa,cAAc;IAC7B,eAAe;IACf,KAAK,WAAW;IAChB,KAAK,KAAK,MAAM,YAAY,wBAAwB,OAAO,QAAQ,CAAC;IACpE,KAAK,cAAa;GACpB;GAMA,MAAM,WAAW,QAAQ,aAAa,QAAQ,KAAM,KAAK,IAAI,KAAK,SAAS,SAAS,IAAI;GACxF,IAAI,aAAa,cAAc;IAC7B,eAAe;IACf,KAAK,KAAK,gBAAgB,oBAAoB,QAAQ;GACxD;EACF;EAKA,mBAAmB,cAAa;GAC9B,MAAM,UAAU,KAAK,QAAO,CAAE,CAAC;GAC/B,MAAM,QAAQ,KAAK,UAAS,CAAE,CAAC;GAC/B,MAAM,MAAM,KAAK,QAAO,CAAE,CAAC;GAC3B,MAAM,QAAQ,KAAK,UAAS,CAAE,EAAE,iBAAiB;GACjD,MAAM,QAAQ,KAAK,WAAU,CAAE,EAAE,iBAAiB;GAClD,MAAM,QAAQ,KAAK,WAAU,CAAE,EAAE,iBAAiB;GAClD,MAAM,cAAc,KAAK,YAAW;GACpC,MAAM,gBAAqB;IAOzB,MAAM,MAAM,QACR,KAAK,SAAS,QAAQ,aAAa,MAAM,aAAa,OAAO,GAAG,IAChE;IACJ,IAAI,QAAQ,MACV,KAAK,KAAK,MAAM,eAAe,uBAAuB;SAEtD,KAAK,KAAK,MAAM,YAAY,yBAAyB,GAAG,IAAG,GAAI;IAIjE,MAAM,QACJ,cAAc,KAAK,IAAI,MAAM,aAAa,MAAM,aAAa,IAAI,WAAW,IAAI;IAClF,KAAK,KAAK,MAAM,YAAY,2BAA2B,GAAG,MAAK,GAAI;IAEnE,KAAK,KAAK,MAAM,YAAY,4BAA4B,GAAG,OAAO,gBAAgB,EAAC,GAAI;IACvF,KAAK,cAAc,QAAQ,MAAM,eAAe;IAEhD,MAAM,WAAW,gBAAgB,KAAK,aAAa,SAAQ,KAAM,IAAI;IACrE,IAAI,UACF,YAAY,QAAQ;IAEtB,KAAK,cAAa;IAClB,KAAK,aAAa,aAAa,IAC7B,cAAc,QAAQ,eAAe,KAAK,cAAc,CAAC;GAE7D;GACA,QAAO;GACP,IAAI,OAAO,mBAAmB,aAAa;IAKzC,IAAI,QAAuB;IAC3B,MAAM,WAAW,IAAI,qBAAoB;KACvC,UAAU,4BAA2B;MACnC,QAAQ;MACR,QAAO;KACT,CAAC;IACH,CAAC;IACD,SAAS,QAAQ,OAAO;IACxB,SAAS,QAAQ,KAAK;IACtB,SAAS,QAAQ,GAAG;IACpB,KAAK,MAAM,WAAW,CAAC,OAAO,KAAK,GACjC,IAAI,SACF,SAAS,QAAQ,OAAO;IAG5B,IAAI,OACF,SAAS,QAAQ,KAAK;IAExB,gBAAe;KACb,SAAS,WAAU;KACnB,IAAI,UAAU,MACZ,qBAAqB,KAAK;IAE9B,CAAC;GACH;EACF,CAAC;EACD,IAAI,YAAY;EAChB,WAAW,gBAAe;GACxB,YAAY;GACZ,KAAK,aAAa,aAAa,IAAI,CAAC;GACpC,KAAK,aAAa,MAAM,eAAe,sBAAsB;GAC7D,KAAK,cAAc;EACrB,CAAC;EAID,QAAQ,cAAa;GACnB,MAAM,WAAW,KAAK,aAAa,SAAQ,KAAM;GACjD,IAAI,CAAC,UACH;GAGF,eAAe;GACf,eAAe;GACf,MAAM,UAAU,EAAE,SAAS,KAAI;GAE/B,IAAI,aAAmD;GACvD,IAAI,gBAAgB;GAEpB,IAAI,WAAW;GACf,IAAI,YAAY;GAChB,IAAI,cAAc;GAElB,IAAI,qBAAqB;GACzB,MAAM,MAAM,KAAK;GAEjB,MAAM,wBAA6B;IACjC,IAAI,eAAe,MAAM;KACvB,aAAa,UAAU;KACvB,aAAa;IACf;GACF;GACA,MAAM,qBAA0B;IAC9B,gBAAgB;IAChB,gBAAe;IACf,aAAa,WAAW,aAAa,aAAa;GACpD;GACA,MAAM,aAAkB;IACtB,IACE,aACA,aACA,eACA,KAAK,YAAY,KACjB,KAAK,YAAY,KACjB,KAAK,SAAS,QAAO,KAGrB,KAAK,KAAK,kBAAkB,EAAE,oBAAoB,KAAI,CAAE,MAAM,OAE9D;IAEF,WAAW;IACX,aAAY;IACZ,SAAS,SAAS;KAChB,KAAK,KAAK,WAAW,KAAM,IAAI,KAAK,cAAa;KACjD,UAAU,qBAAqB,KAAK,SAAS,WAAW,IAAI,YAAY;IACzE,CAAA;GACH;GACA,SAAS,cAAW;IAClB,gBAAe;IACf,IAAI,CAAC,eACH;IAEF,gBAAgB;IAChB,IAAI,UAAU;KACZ,WAAW;KACX;IACF;IACA,KAAI;GACN;GACA,MAAM,iBAAsB;IAC1B,qBAAqB;IACrB,YAAY,QAAQ;IACpB,aAAY;GACd;GACA,MAAM,WAAW,UAA2B;IAC1C,MAAM,OAAO,MAAM,QAAQ,SAAS;IACpC,IAAI,QAAQ,CAAC,aAAa,CAAC,aACzB,MAAK;IAEP,YAAY;IACZ,QAAO;GACT;GAGA,MAAM,4BAAiC;IACrC,IAAI,oBAAoB,aAAa,WAAW;IAChD,IAAI,oBAAoB,iBAAiB,WAAW;GACtD;GACA,MAAM,sBAA2B;IAC/B,IAAI,CAAC,aAAa,CAAC,aACjB,MAAK;IAEP,cAAc;IACd,oBAAmB;IACnB,IAAI,iBAAiB,aAAa,aAAa,OAAO;IACtD,IAAI,iBAAiB,iBAAiB,aAAa,OAAO;IAC1D,QAAO;GACT;GACA,SAAS,cAAW;IAClB,cAAc;IACd,oBAAmB;IACnB,QAAO;GACT;GAEA,SAAS,QAAK;IACZ,qBAAqB;GACvB;GAKA,SAAS,UAAO;IACd,IAAI,aAAa,aAAa;KAC5B,WAAW;KACX,gBAAgB;KAChB,gBAAe;IACjB,OAAO,IAAI,oBACT,aAAY;GAEhB;GAEA,SAAS,iBAAiB,UAAU,UAAU,OAAO;GACrD,SAAS,iBAAiB,aAAa,aAAa,OAAO;GAC3D,SAAS,iBAAiB,cAAc,SAAS,OAAO;GACxD,SAAS,iBAAiB,YAAY,SAAS,OAAO;GACtD,SAAS,iBAAiB,eAAe,SAAS,OAAO;GACzD,SAAS,iBAAiB,eAAe,eAAe,OAAO;GAC/D,YAAY,QAAQ;GACpB,gBAAe;IACb,gBAAe;IACf,SAAS,oBAAoB,UAAU,QAAQ;IAC/C,SAAS,oBAAoB,aAAa,WAAW;IACrD,SAAS,oBAAoB,cAAc,OAAO;IAClD,SAAS,oBAAoB,YAAY,OAAO;IAChD,SAAS,oBAAoB,eAAe,OAAO;IACnD,SAAS,oBAAoB,eAAe,aAAa;IACzD,oBAAmB;GACrB,CAAC;EACH,CAAC;CACH;CAYQ,SACN,OACA,YACA,OACA,KAAgB;EAGhB,MAAM,UAAU,KAAK,IAAI,MAAM,aAAa,MAAM,WAAW;EAC7D,IAAI,UAAU;EACd,IAAI,QAAQ;EACZ,KAAK,MAAM,QAAQ,MAAM,iBAA8B,qBAAqB,GAAG;GAC7E,WAAW,KAAK,IAAI,GAAG,KAAK,cAAc,KAAK,WAAW;GAC1D,MAAM,SAAS,KAAK;GACpB,MAAM,QAAQ,SAAS,KAAK,SAAS,aAAa,iBAAiB,MAAM,IAAI;GAC7E,MAAM,YAAY,WAAW,OAAO,YAAY,EAAE,KAAK;GACvD,MAAM,OAAO,SAAS,OAAO,cAAc,YAAY,KAAK;GAC5D,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,aAAa,IAAI,CAAC;EACvD;EAEA,IAAI,cADS,QAAQ,KAAK,cAAc,KAAK,IAAI,SAAS,IAAI,WAAW,IAAI,YAE3E,OAAO;EAET,MAAM,SAAS,KAAK,IAAI,YAAY,QAAQ,CAAC;EAC7C,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,YAAY,QAAQ,UAAU,IAAI,cAAc,SAAS,CAAC;EAC1F,OAAO,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,CAAC;CACvC;CAQQ,gBAAa;EACnB,IAAI,KAAK,eAAe,GACtB,OAAO;EAET,OAAO,KAAK,IAAI,cAAc,KAAK,IAAI,cAAc,KAAK,WAAW,CAAC;CACxE;CAUQ,gBAAa;EACnB,MAAM,OAAO,KAAK,UAAS,CAAE,EAAE;EAC/B,MAAM,WAAW,SAAS,UAAa,KAAK,cAAc;EAC1D,IAAI,YAAY;EAChB,IAAI,QAAQ,UAAU;GACpB,MAAM,SAAS,KAAK,MAAM,KAAK,eAAe,IAAI,KAAK,SAAS;GAChE,KAAK,MAAM,SAAS,GAAG,OAAM;GAC7B,YAAY,KAAK,YAAW,IAAK,IAAI,KAAK,cAAc;EAC1D;EACA,MAAM,WAAW,gBAAgB,KAAK,aAAa,SAAQ,KAAM,IAAI;EACrE,IAAI,KAAK,eAAe,KAAK,gBAAgB,UAC3C,KAAK,YAAY,MAAM,eAAe,sBAAsB;EAE9D,KAAK,cAAc;EACnB,UAAU,MAAM,YAAY,wBAAwB,GAAG,UAAS,GAAI;CACtE;CAlZW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAiB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAjB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAiB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA,EAAA,YAAA;GAAA,SAAA;GAAA,qBAAA;EAAA,EAAA;EAAA,aAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,SAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,WAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,SAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,WAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,YAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,WAAA;IAAA,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAAA,CAAA,YAAA;IAAA,aAAA;IAAA,UAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UA3DlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDT,UAAA;CAAA,CAAA;;AAEU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAiB,YAAA,CAAA;EA9D7B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,MAAM;IAAE,WAAW;IAAiB,uBAAuB;GAAM;GACjE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DX,CAAA;CAyBwE,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,WAAS,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACP,aAAW,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACb,WAAS,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAChB,aAAW,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACV,cAAY,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACb,aAAW,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,YAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACV,cAAY,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AC9N/E,MAAM,UAAU;AAEhB,MAAM,SAAS;AAEf,MAAM,YAAY;AAElB,MAAM,WAAW;AAEjB,MAAM,aAAa;AAEnB,MAAM,SAAO;AACb,MAAM,kBAAA;AAEN,MAAM,gBAAgB;AAEtB,MAAM,mBAAmB;AAMzB,SAAS,aAAa,MAAiB;CACrC,MAAM,OAAO,KAAK,cAAc;CAChC,KAAK,IAAI,UAAU,KAAK,eAAe,WAAW,MAAM,UAAU,QAAQ,eAAe;EACvF,MAAM,YAAY,KAAK,iBAAiB,OAAO,CAAC,CAAC;EACjD,IAAI,cAAc,UAAU,cAAc,UACxC,OAAO;CAEX;CACA,OAAO,KAAK;AACd;AAEA,SAAS,eAAe,IAAU;CAChC,OAAO,mBAAmB,GAAE;AAC9B;AA0FA,IAAa,uBAAb,MAAa,qBAAoB;CAEtB,WAAW,MAAM,OAAK;EAAA,GAAA,YAAA,EAAA,WAAA,WAAA,iCAAA,CAAA;EAAI,WAAW;CAAgB,CAAA;CAGrD,mBAAmB,OAAM;CAKzB,UAAU,OAAM;CAER,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,OAAO,UAAU,SAAkC,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,gCAAA,CAAA,CAAA;CAC1D,cAAc,OAAO,eAAe,EAAE,UAAU,KAAI,CAAE;CACtD,UAAU,OAAO,gBAAgB;CAC/B,SAAS,OAAO,SAAS;CAC3B,WAAW,OAAO,QAAQ;CAC1B,WAAW,OAAO,iBAAiB;CACnC,MAAM,eAAe,KAAK,SAAS,KAAI,MAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,gCAAA,CAAA,CAAA;CAEpD,kBAAkB,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,gCAAA,CAAA,CAAA;CAEvC,aAAa,KAAK,gBAAgB,WAAU;CAE7C,WAA+B;CAC/B,UAAqC;CACrC,kBAAoC;CACpC,gBAAgB;CAEhB,cAAc;CACd,SAAmC;CAE3C,cAAA;EAIE,QAAQ,cAAa;GACnB,MAAM,WAAW,KAAK,aAAa,SAAQ,KAAM,aAAa,KAAK,IAAI;GACvE,IAAI,CAAC,UACH;GAEF,KAAK,WAAW;GAChB,SAAS,iBAAiB,eAAe,KAAK,aAAa;GAE3D,MAAM,MAAM,KAAK,IAAG;GACpB,IAAI,KAAK;IACP,MAAM,UAAmC,EAAE,SAAS,KAAI;IACxD,SAAS,iBAAiB,cAAc,KAAK,cAAc,OAAO;IAClE,SAAS,iBAAiB,YAAY,KAAK,YAAY,OAAO;IAC9D,SAAS,iBAAiB,eAAe,KAAK,YAAY,OAAO;GACnE;GACA,gBAAe;IACb,KAAK,WAAW,QAAQ;IACxB,SAAS,oBAAoB,eAAe,KAAK,aAAa;IAC9D,IAAI,KAAK;KACP,IAAI,KAAK,QAAQ,OACf,KAAK,eAAc;KAErB,KAAK,UAAU,QAAQ;KACvB,KAAK,cAAc;KACnB,SAAS,oBAAoB,cAAc,KAAK,YAAY;KAC5D,SAAS,oBAAoB,YAAY,KAAK,UAAU;KACxD,SAAS,oBAAoB,eAAe,KAAK,UAAU;IAC7D;IACA,KAAK,iBAAiB,OAAM;IAC5B,KAAK,WAAW;GAClB,CAAC;EACH,CAAC;CACH;CAGA,kBAAe;EACb,KAAK,UAAU,OAAO;CACxB;CAGQ,iBAAc;EACpB,MAAM,QAAiC,EAAE,gBAAgB,KAAK,SAAQ,EAAE;EACxE,KAAK,iBAAiB,KAAK,KAAK;EAChC,KAAK,QAAQ,KAAK,KAAK;CACzB;CAGA,WAAQ;EACN,IAAI,CAAC,KAAK,gBAAe,GACvB;EAEF,KAAK,SAAS,SAAS,qBAAqB,KAAK,SAAS,WAAW,SACnE,KAAK,gBAAgB,IAAI,KAAK,CAAC;CAEnC;CAEiB,iBAAiB,UAA6B;EAC7D,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,UACH;EAEF,IAAI,KAAK,IAAG,MAAO,MAAM,gBAAgB,WAAW,KAAK,cAEvD;EAEF,IACE,KAAK,SAAQ,KACb,KAAK,gBAAe,KACpB,KAAK,WACL,CAAC,MAAM,aACP,SAAS,cAAc,GAEvB;EAEF,KAAK,iBAAiB,OAAM;EAC5B,KAAK,UAAU;GACb,WAAW,MAAM;GACjB,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,eAAe,qBAAqB,KAAK,SAAS,WAAW;GAC7D,aAAa;GACb,SAAS;EACV;EAID,SAAS,iBAAiB,eAAe,KAAK,aAAa;EAC3D,SAAS,iBAAiB,aAAa,KAAK,WAAW;EACvD,SAAS,iBAAiB,iBAAiB,KAAK,eAAe;EAC/D,SAAS,iBAAiB,sBAAsB,KAAK,oBAAoB;EACzE,SAAS,iBAAiB,aAAa,KAAK,gBAAgB,EAAE,SAAS,MAAK,CAAE;CAChF;CAEiB,qBAA0B;EACzC,KAAK,cAAc;EACnB,MAAM,WAAW,KAAK;EAEtB,IAAI,CAAC,YAAY,KAAK,WAAW,KAAK,UAAU,KAAK,SAAQ,KAAM,KAAK,gBAAe,GACrF;EAEF,KAAK,SAAS;GACZ,eAAe,qBAAqB,KAAK,SAAS,WAAW;GAC7D,MAAM;GACN,aAAa;GACb,OAAO;EACR;EACD,SAAS,iBAAiB,UAAU,KAAK,gBAAgB,EAAE,SAAS,KAAI,CAAE;CAC5E;CAOiB,uBAA4B;EAC3C,MAAM,SAAS,KAAK;EACpB,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,UAAU,CAAC,UACd;EAEF,MAAM,OAAO,KAAK,IAAI,GAAG,CAAC,SAAS,SAAS;EAC5C,OAAO,OAAO;EACd,IAAI,QAAQ,eAAe;GACzB,IAAI,OAAO,OAAO;IAChB,OAAO,QAAQ;IACf,KAAK,eAAc;GACrB;GACA;EACF;EACA,OAAO,QAAQ;EACf,MAAM,WAAW,KAAK,IAAI,GAAG,OAAO,SAAS;EAC7C,MAAM,UAAU,KAAK,KAAI,CAAE,CAAC;EAC5B,QAAQ,MAAM,UAAU,OAAO,QAAQ;EACvC,QAAQ,MAAM,YAAY,GAAG,eAAe,OAAO,EAAC,SAAU,oBAAoB,IAAI,oBAAoB,SAAQ;EAClH,QAAQ,MAAM,aAAa;EAC3B,IAAI,YAAY,KAAK,CAAC,OAAO,aAAa;GACxC,OAAO,cAAc;GACrB,AAAK,KAAK,QAAQ,OAAO,OAAO;EAClC;CACF;CAEiB,cAAc,UAA2B;EACxD,IAAI,MAAM,QAAQ,SAAS,GACzB;EAEF,KAAK,cAAc;EACnB,MAAM,SAAS,KAAK;EACpB,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,UAAU,CAAC,UACd;EAGF,KAAK,UAAU,QAAQ;EACvB,IAAI,CAAC,KAAK,SAAQ,KAAM,CAAC,KAAK,gBAAe,KAAM,OAAO,QAAQ,WAAW;GAE3E,KAAK,KAAI,CAAE,CAAC,cAAc,MAAM,eAAe,SAAS;GACxD,KAAK,gBAAgB,IAAI,IAAI;GAC7B,KAAK,SAAS,QAAQ,OAAO,aAAa;GAC1C,KAAK,eAAc;EACrB,OAAO,IAAI,OAAO,OAChB,KAAK,eAAc;CAEvB;CAEQ,UAAU,UAAqB;EACrC,SAAS,oBAAoB,UAAU,KAAK,cAAc;EAC1D,KAAK,SAAS;CAChB;CAEQ,iBAAc;EACpB,KAAK,KAAI,CAAE,CAAC,cAAc,MAAM,eAAe,SAAS;EACxD,KAAK,UAAU,OAAO;CACxB;CAUiB,kBAAkB,UAA2B;EAC5D,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,MAAM,YACrB;EAEF,IAAI,QAAQ,SAAS;GACnB,MAAM,eAAc;GACpB;EACF;EACA,MAAM,QAAQ,MAAM,QAAQ;EAC5B,IAAI,CAAC,SAAS,KAAK,UAAU,cAAc,GACzC;EAEF,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,IAAI,KAAK,KAAK,KAAK,KAAK,IAAI,EAAE,GAC5B,MAAM,eAAc;CAExB;CAOiB,wBAAwB,UAA6B;EACpE,IAAI,MAAM,WAAW,KAAK,UACxB;EAEF,KAAK,gBAAgB,KAAK;CAC5B;CAEiB,iBAAiB,UAA6B;EAC7D,MAAM,UAAU,KAAK;EACrB,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,WAAW,CAAC,YAAY,MAAM,cAAc,QAAQ,WACvD;EAEF,IAAI,SAAS,cAAc,GAAG;GAE5B,KAAK,WAAW,QAAQ;GACxB,KAAK,UAAU,OAAO;GACtB;EACF;EACA,MAAM,QAAQ,MAAM,UAAU,QAAQ;EACtC,IAAI,CAAC,QAAQ,SAAS;GACpB,MAAM,KAAK,MAAM,UAAU,QAAQ;GACnC,IAAI,SAAS,UAAQ,SAAS,KAAK,IAAI,EAAE,GAEvC;GAEF,QAAQ,UAAU;GAClB,IAAI;IAGF,SAAS,kBAAkB,MAAM,SAAS;GAC5C,QAAQ,CAER;EACF;EACA,IAAI,MAAM,YACR,MAAM,eAAc;EAItB,MAAM,WAAW,QAAQ,IAAI,KAAK,IAAI,QAAQ,YAAY,QAAQ,IAAI;EACtE,KAAK,UAAU,UAAU,QAAQ;EACjC,IAAI,SAAS,aAAa,CAAC,QAAQ,aAAa;GAC9C,QAAQ,cAAc;GACtB,AAAK,KAAK,QAAQ,OAAO,OAAO;EAClC;CACF;CAEiB,eAAe,UAA6B;EAC3D,MAAM,UAAU,KAAK;EACrB,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,WAAW,CAAC,YAAY,MAAM,cAAc,QAAQ,WACvD;EAEF,MAAM,UAAU,QAAQ;EACxB,MAAM,QAAQ,MAAM,UAAU,QAAQ;EACtC,KAAK,WAAW,QAAQ;EACxB,IAAI,CAAC,SAEH;EAEF,IAAI,CAAC,KAAK,SAAQ,KAAM,SAAS,WAAW;GAC1C,KAAK,gBAAgB,IAAI,IAAI;GAC7B,KAAK,SAAS,QAAQ,QAAQ,aAAa;GAC3C,KAAK,eAAc;EACrB,OACE,KAAK,SAAS,SAAS,QAAQ,aAAa;CAEhD;CAEiB,mBAAmB,UAA6B;EAC/D,MAAM,UAAU,KAAK;EACrB,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,WAAW,CAAC,YAAY,MAAM,cAAc,QAAQ,WACvD;EAEF,MAAM,UAAU,QAAQ;EACxB,KAAK,WAAW,QAAQ;EACxB,IAAI,SACF,KAAK,SAAS,SAAS,QAAQ,aAAa;CAEhD;CAEQ,WAAW,UAAqB;EACtC,MAAM,YAAY,KAAK,SAAS;EAChC,KAAK,oBAAoB,QAAQ;EACjC,IAAI,cAAc,QAChB,IAAI;GACF,SAAS,sBAAsB,SAAS;EAC1C,QAAQ,CAER;EAEF,KAAK,UAAU;CACjB;CAEQ,oBAAoB,UAAqB;EAC/C,SAAS,oBAAoB,eAAe,KAAK,aAAa;EAC9D,SAAS,oBAAoB,aAAa,KAAK,WAAW;EAC1D,SAAS,oBAAoB,iBAAiB,KAAK,eAAe;EAClE,SAAS,oBAAoB,sBAAsB,KAAK,oBAAoB;EAC5E,SAAS,oBAAoB,aAAa,KAAK,cAAc;CAC/D;CAEQ,UAAU,IAAU;EAC1B,KAAK,gBAAgB;EACrB,MAAM,UAAU,KAAK,KAAI,CAAE,CAAC;EAC5B,QAAQ,MAAM,YAAY,eAAe,EAAE;EAG3C,QAAQ,MAAM,aAAa,OAAO,UAAU,WAAW;CACzD;CAEQ,SAAS,QAAgB,eAAwB,QAAmB;EAC1E,KAAK,iBAAiB,OAAM;EAC5B,IAAI,eAAe;GACjB,KAAK,UAAU,MAAM;GACrB,SAAQ;GACR;EACF;EACA,MAAM,OAAO,KAAK;EAClB,MAAM,YAAY,KAAK,KAAI,CAAE,CAAC,cAAc,QAC1C,CAAC,EAAE,WAAW,eAAe,IAAI,EAAC,GAAI,EAAE,WAAW,eAAe,MAAM,EAAC,CAAE,GAC3E;GAAE,UAAU;GAAiB,QAAQ;GAAe,MAAM;EAAU,CAAE;EAExE,KAAK,kBAAkB;EACvB,KAAK,gBAAgB;EACrB,AAAK,UAAU,SAAS,WACjB;GACH,KAAK,UAAU,MAAM;GACrB,SAAQ;EACV,SACK,CAEL,CAAC;CAEL;CA5XW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAoB,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,UAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,SAAA;GAAA,kBAAA;GAAA,SAAA;EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,0BAAA,2BAAA;GAAA,gBAAA;EAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,MAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UAhBrB;;;;;;;;;;;;;;;EAcT,UAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MAhBS;GAAkB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;CAAA,CAAA;;AAkBjB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EApBhC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS,CAAC,kBAAkB;GAC5B,MAAM;IAAE,OAAO;IAAsB,4BAA4B;GAAwB;GACzF,UAAU;;;;;;;;;;;;;;;EAeX,CAAA;CAcqE,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,kBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,kBAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,SAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,QAAM,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AC1H5E,SAAS,SAAS,QAAc;CAC9B,OAAO,QAAQ,OAAM;AACvB;AAEA,MAAM,qBAAqB,SAAS,MAAM;AA2C1C,IAAa,kBAAb,MAAa,gBAAe;CAEjB,WAAW,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,gCAAA,CAAA,CAAA;CAE3B,SAAS,OAAO,MAAM;CACtB,MAAM,OAAO,eAAe;CAC5B,UAAU,OAAO,gBAAgB;CACjC,WAAW,OAAO,iBAAiB;CACnC,WAAW,OAAO,iBAAiB;CACnC,SAAS,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,KAAK;CACxD,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,QAAQ;CAC1B,WAAW,OAAO,gBAAc;CAChC,mBAAmB,OAAO,gBAAgB;CAE1C,MAAM,aAAa,mBAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,gCAAA,CAAA,CAAA;CACrC,UAAU,aACzB,mBAAiB;EAAA,GAAA,YAAA,EAAA,WAAA,UAAA,iCAAA,CAAA;EACf,MAAM;CAAU,CAAA;CAGH,YAAY,OAAsB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,gCAAA,CAAA,CAAA;CACvC,SAAS,aAAa,wBAAuB,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,gCAAA,CAAA,CAAA;CAG7C,MAAM,eACf,KAAK,OAAO,yBAAwB,CAAE,EAAE,YAAY,KAAK,OAAO,SAAS,KAAK,OAAO,GAAG,GAC/F,GAAA,YAAA,CAAA,EAAA,WAAA,MAAA,CAAA,gCAAA,CAAA,CAAA;CAGQ,SAAS,eAAe,KAAK,UAAU,KAAK,IAAG,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,gCAAA,CAAA,CAAA;CAQ3C,aAAa,aAAY;EAAA,GAAA,YAAA,EAAA,WAAA,aAAA,iCAAA,CAAA;EACxC,cAAa;GACX,MAAM,aAAa,KAAK,OAAO,kBAAiB;GAChD,OAAO,KAAK,UACV,YAAY,YACV,YAAY,gBACZ,KAAK,OAAO,yBAAwB,CAAE,EAAE,YACxC,KAAK,OAAO,SAAS,KAAK,OAAO,GAAG,CAAC;EAE3C;EACA,cAAc,KAAK,aAAa,OAAO,UAAU,SAAS;CAAI,CAAA;CAKvD,UAAiC,KAAK,WAAW,WAAU;CAI3D,SAAS,eAAe,KAAK,SAAS,QAAO,KAAM,KAAK,SAAS,SAAQ,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,gCAAA,CAAA,CAAA;CAG5E,iBAAiB,eAAe,KAAK,SAAS,aAAa,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,gCAAA,CAAA,CAAA;CAE1E,cAAA;EAGE,MAAM,iBAAiB,KAAK,SAAS,aAAa,KAAK,IAAI;EAC3D,OAAO,UAAU,CAAC,CAAC,gBAAe;GAChC,eAAc;GACd,KAAK,SAAS,gBAAgB,MAAM,eAAe,2BAA2B;EAChF,CAAC;EAID,mBAAmB,cAAa;GAC9B,MAAM,MAAM,KAAK,QAAO,CAAE,EAAE,cAAc,cAAc,KAAK,KAAK;GAClE,IAAI,EAAE,eAAe,gBAAgB,OAAO,mBAAmB,aAC7D;GAEF,MAAM,WAAW,IAAI,qBAAoB;IACvC,MAAM,SAAS,IAAI;IACnB,KAAK,UAAU,IAAI,SAAS,IAAI,SAAS,IAAI;GAC/C,CAAC;GACD,SAAS,QAAQ,GAAG;GACpB,gBAAgB,SAAS,WAAU,CAAE;EACvC,CAAC;EAID,kBAAkB,EAChB,aAAY;GACV,MAAM,MAAM,KAAK,IAAG;GACpB,MAAM,QAAQ,QAAQ,UAAa,CAAC,IAAI,SAAQ;GAChD,MAAM,SAAS,KAAK,UAAS;GAC7B,MAAM,QAAQ,CAAC,QACX,QACA,WAAW,OACT,qBACA,SAAS,GAAG,OAAM,GAAI;GAC5B,KAAK,KAAK,MAAM,YAAY,sBAAsB,KAAK;GAGvD,KAAK,SAAS,gBAAgB,MAAM,YAAY,6BAA6B,KAAK;EACpF,EACD,CAAA;CACH;CAOA,MAAM,KAAW;EACf,OAAO,KAAK,UAAU,KAAK,OAAO,SAAS,GAAG,CAAC;CACjD;CAGQ,UAAU,MAAa;EAC7B,MAAM,WAAW,gBAAgB,IAAI;EACrC,MAAM,OAAO,KAAK,aAAY;EAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,SAAS,QAAQ,KACjD,IAAI,KAAK,OAAO,GAAG,MAAM,SAAS,IAAI,OAAO,CAAC,GAC5C,OAAO,SAAS,IAAI,KAAK,WAAW;EAGxC,OAAO;CACT;CAQA,KAAK,IAAU;EACb,MAAM,OAAO,KAAK,OAAO,cAAc;GAAC;GAAK,GAAG,KAAK,aAAY;GAAI;EAAE,CAAC;EACxE,OAAO,KAAK,iBAAiB,mBAAmB,KAAK,OAAO,aAAa,IAAI,CAAC;CAChF;CAOA,OAAO,IAAU;EACf,MAAM,WAAW,gBAAgB,KAAK,IAAG,CAAE;EAC3C,MAAM,OAAO,KAAK,aAAY;EAE9B,MAAM,QAAQ,SAAS,SAAS,KAAK,SAAS;EAC9C,OACE,SAAS,KACT,SAAS,SAAS,SAAS,OAAO,MAClC,KAAK,OAAO,GAAG,MAAM,SAAS,QAAQ,OAAO,CAAC,KAC9C,KAAK,OAAM,MAAO;CAEtB;CAGQ,eAAY;EAClB,OAAO,KAAK,SAAQ,CAAE,CACnB,MAAM,GAAG,CAAC,CACV,QAAQ,MAAM,EAAE,SAAS,CAAC;CAC/B;CAQA,OAAO,IAAU;EACf,MAAM,SAAS,KAAK,OAAM,CAAE,EAAE,UAAU,EAAE,KAAK;GAAC;GAAK,GAAG,KAAK,aAAY;GAAI;EAAE;EAC/E,KAAK,IAAI,QAAQ,MAAM,CAAC,CAAC,MACtB,cAAa;GAEZ,IAAI,aAAa,KAAK,OAAO,oBAC3B,AAAK,KAAK,QAAQ,UAAS;EAE/B,IACC,UAAkB;GACjB,QAAQ,MAAM,kCAAkC,KAAK;EACvD,CAAC;CAEL;CAQA,SAAS,IAAU;EACjB,IAAI,KAAK,IAAI,MAAK,IAAK,GACrB;OAAI,CAAC,KAAK,OAAM,CAAE,EAAE,UAAS,GAC3B,AAAK,KAAK,IAAI,KAAK;IAAC;IAAK,GAAG,KAAK,aAAY;IAAI;GAAE,CAAC;EACtD,OAEA,KAAK,OAAM,CAAE,EAAE,yBAAwB;CAE3C;CAtMW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAf,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAe,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,UAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,MAAA,EAAA,gBAAA,eAAA;EAAA,WATf;GACT;IAAE,SAAS;IAAgB,aAAa;GAAe;GAEvD;IAAE,SAAS;IAAc,aAAa;GAAe;GAErD;IAAE,SAAS;IAAuB,aAAa;GAAe;EAC/D;EAAA,SAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAkBmC;IAAiB,aAAA;IAAA,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAEnD;IAAiB,aAAA;IAAA,MACT;IAAU,UAAA;GAAA;GAAA;IAAA,cAAA;IAAA,OAAA;IAAA,WAImB;IAAsB,aAAA;IAAA,UAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UAxBnD;EAAiE,UAAA;CAAA,CAAA;;AAEhE,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAe,YAAA,CAAA;EAZ3B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,MAAM,EAAE,OAAO,eAAc;GAC7B,WAAW;IACT;KAAE,SAAS;KAAgB,aAAW;IAAiB;IAEvD;KAAE,SAAS;KAAc,aAAW;IAAiB;IAErD;KAAE,SAAS;KAAuB,aAAW;IAAiB;GAC/D;GACD,UAAU;EACX,CAAA;CAgBqC,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,KAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAAA,iBAAiB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAEnD,iBAAiB,GAAA;IACf,MAAM;IAAY,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBAIiB,sBAAsB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AAoL/D,SAAS,gBAAgB,MAAa;CACpC,MAAM,QAAkB,CAAA;CACxB,IAAI,QAAQ,KAAK,KAAK,SAAS;CAC/B,OAAO,OAAO;EACZ,MAAM,KAAK,GAAG,MAAM,SAAS,KAAK,MAAM,EAAE,IAAI,CAAC;EAC/C,QAAQ,MAAM,SAAS;CACzB;CACA,OAAO;AACT;AC7RA,MAAa,kBAAkB,KAAK;AAGpC,MAAa,eAAe,KAAK;AAKjC,MAAa,wBAAwB,KAAK;AAK1C,MAAa,iBAAiB,KAAK;AAWnC,MAAa,mBAAmB,KAAK;AAcrC,MAAa,mBAAmB,KAAK;AAGrC,MAAa,iBAAiB,KAAK;AAGnC,MAAa,WAAW,KAAK;AAG7B,MAAa,SAAS,KAAK;AAG3B,MAAa,UAAU,KAAK;AAG5B,MAAa,SAAS,KAAK;AAG3B,MAAa,UAAU,KAAK;AAG5B,MAAa,YAAY,KAAK;AAG9B,MAAa,UAAU,KAAK;AChD5B,IAAa,aAAb,cAA6C,eAAe;CAOvC;CANF;CAER;CACD,cAAgD;CAExD,YACmB,iBACjB,eAAe,GAAC;EAEhB,MAAM,eAAe;EAHJ,KAAA,kBAAA;EAIjB,KAAK,iBAAiB,OAAO,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,iCAAA,CAAA,CAAA;EAC1C,KAAK,YAAY,KAAK,eAAe,WAAU;CACjD;CAGA,IAAa,YAAS;EACpB,OAAO,MAAM,aAAa,KAAK,gBAAgB,UAAS;CAC1D;CAMA,OAAO,OAAa;EAClB,IAAI,KAAK,WACP;EAEF,KAAK,cAAc,KAAK;CAC1B;CAGA,QAAQ,SAAgC;EACtC,KAAK,cAAc;CACrB;CAGA,aAAa,OAAa;EACxB,KAAK,eAAe,IAAI,KAAK;CAC/B;AACD;ACzCD,SAAgB,sBAAmB;CACjC,MAAM,WAAW,OAAO,iBAAiB;CACzC,MAAM,OAAO,OAAO,QAAQ,CAAC,CAAC;CAC9B,MAAM,UAAU,OAAO,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,gCAAA,CAAA,CAAA;CACzB,MAAM,iBAAuB,QAAQ,QAAQ,UAAU,QAAQ,CAAC;CAChE,MAAM,iBAAiB,UAAU,QAAQ;CACzC,MAAM,gBAAgB,iBAAiB,UAAU,QAAQ;CACzD,OAAO,UAAU,CAAC,CAAC,gBAAe;EAChC,MAAM,oBAAoB,UAAU,QAAQ;EAC5C,MAAM,gBAAgB,oBAAoB,UAAU,QAAQ;CAC9D,CAAC;CAED,OAAO,eACA;EACH,QAAO;EACP,IAAI,CAAC,SAAS,QAAO,GACnB,OAAO;EAET,MAAM,SAAS,SAAS,OAAM;EAC9B,MAAM,cAAc,KAAK,MAAM,SAAS,YAAW,CAAE;EAIrD,OAAO;GAAE,SAHO,SAAS,gBAAe,IACpC,SACA,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,eAAe,KAAK,WAAW,CAAC;GAChD;EAAW;CAC/B,GACA,EACE,QAAQ,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,gBAAgB,GAAG,YACrE,CAAA;AAEL;ACHA,MAAM,OAAO;AAEb,MAAM,iBAAiB;AAEvB,MAAM,cAAc;AAEpB,MAAM,mBAAmB;AAEzB,MAAM,kBAAkB;AAExB,MAAM,mBAAmB;AAEzB,MAAM,2BAA2B,mBAAmB;AAiHpD,IAAa,mBAAb,MAAa,iBAAgB;CACV,SAAS,OAAO,iBAAe;CAC/B,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,QAAQ;CAC1B,WAAW,OAAO,iBAAiB;CACnC,gBAAgB,oBAAmB;CACnC,iBAAiB,UAAU,SAAkC,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,gCAAA,CAAA,CAAA;CACvE,gBAAgB,UAAU,SAAS,2BAA0B,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,gCAAA,CAAA,CAAA;CAE3D,WACjB,KAAK,OAAO,mBAAmB,cAAc,KAAK,OAAO,UAAU;CAClD,YACjB,KAAK,OAAO,mBAAmB,cAAc,OAAO,KAAK,OAAO;CAC/C,kBAAkB,SAAS,OAAO;EACnD,QAAQ,OAAO,QAAQ;EACvB,WAAW;GACT;IAAE,SAAS;IAAiB,UAAU,KAAK,OAAO;GAAI;GACtD;IAAE,SAAS;IAAc,UAAU,KAAK,OAAO;GAAG;GAClD;IAAE,SAAS;IAAY,UAAU,KAAK,OAAO;GAAG;EACjD;CACF,CAAA;CACkB,kBAA4C;EAC7D,WAAW,KAAK,OAAO;EACvB,MAAM,KAAK,OAAO;CACnB;CAEgB,YAAY,KAAK,OAAO,WAAW,SAAS;CAC1C,SAAS,OAAO,SAAS;CACzB,cAAc,KAAK,OAAO;CAE1B,aAAa,KAAK,YAAY;CAChC,YAAY,OAAO,aAAa;CAK9B,eAAe,eAAc;EAC9C,MAAM,QAAQ,KAAK,OAAO,IAAI,UAAS;EAEvC,OAAO,GADQ,UAAU,KAAK,YAAY,KAAK,OAAO,WAAW,KAAK,OAAO,OAC7D,IAAK,KAAK,OAAO,cAAc,QAAQ,GAAG,KAAK,YAAY,CAAC;CAC9E,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,gCAAA,CAAA,CAAA;CAEM,uBAAuB;CAKd,iBAAiB,eAC1B,KAAK,OAAO,IAAI,UAAS,MAAO,KAAK,aAAa,KAAK,SAAS,QAAO,GAC9E,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,gCAAA,CAAA,CAAA;CACkB,eAAe,eAE9B,qCACE,KAAK,eAAc,IAAK,oBAAoB,qBAEjD,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,gCAAA,CAAA,CAAA;CAEO,QAAQ,KAAK,OAAO;CAEpB,SAAS;CACT,SAAS;CACT,UAAiC;CACjC,kBAAoC;CAE5C,cAAA;EAEE,OAAO,eAAe,EAAE,MAAM,KAAI,CAAE,CAAC,CAAC,aAAa,IAAI,KAAK;EAC5D,KAAK,MAAM,KAAK,QAAO,CAAE,CAAC,KAAK,UAAU,GAAG,CAAC;EAC7C,KAAK,OAAO,IAAI,aAAa,KAAK,KAAK;EACvC,KAAK,OAAO,IAAI,SAAS,UAAU,KAAK,OAAO,KAAK,CAAC;EACrD,KAAK,KAAK,iBAAiB,eAAe,KAAK,aAAa;EAC5D,KAAK,KAAK,iBAAiB,iBAAiB,KAAK,eAAe;EAChE,MAAM,OAAO,KAAK,SAAS;EAC3B,MAAM,iBAAiB,UAAU,KAAK,QAAQ;EAE9C,kBAAkB,EAAE,aAAa,KAAK,cAAa,EAAE,CAAE;EACvD,IAAI,KAAK,OAAO,mBAAmB;GACjC,IAAI,eAAoC;GACxC,sBAAqB;IACnB,MAAM,QAAQ,KAAK,KAAK,QAAqB,mBAAmB;IAChE,IAAI,OACF,eAAe,mBAAkB,KAAK;GAE1C,CAAC;GACD,OAAO,UAAU,CAAC,CAAC,gBAAgB,eAAc,CAAE;EACrD;EAGA,IAAI,gBAAgB;EACpB,wBAAuB;GACrB,MAAM,aAAa,KAAK,SAAS,QAAO;GACxC,IAAI,iBAAiB,CAAC,cAAc,CAAC,KAAK,eAAc,GACtD,KAAK,eAAc,CAAE,CAAC,cAAc,YAAY;GAElD,gBAAgB;EAClB,CAAC;EACD,OAAO,UAAU,CAAC,CAAC,gBAAe;GAChC,KAAK,WAAU;GACf,KAAK,iBAAiB,OAAM;GAC5B,KAAK,KAAK,oBAAoB,eAAe,KAAK,aAAa;GAC/D,KAAK,KAAK,oBAAoB,iBAAiB,KAAK,eAAe;GACnE,MAAM,oBAAoB,UAAU,KAAK,QAAQ;EACnD,CAAC;CACH;CAOQ,gBAAa;EACnB,MAAM,QAAQ,KAAK,cAAa;EAChC,IAAI,KAAK,OAAO,IAAI,WAElB;EAEF,MAAM,QAAQ,KAAK,KAAK;EAGxB,IAAI,SAAS,CAAC,qBAAqB,KAAK,SAAS,WAAW,GAC1D,MAAM,YAAY,cAAc,wBAAwB;OAExD,MAAM,eAAe,YAAY;EAEnC,IAAI,SAAS,MAAM,UAAU,GAAG;GAC9B,MAAM,YAAY,aAAa,KAAK,CAAC,MAAM,QAAO,GAAI;GAEtD,MAAM,YAAY,kBAAkB,KAAK;EAC3C,OAAO;GACL,MAAM,eAAe,WAAW;GAChC,MAAM,eAAe,gBAAgB;EACvC;EACA,IAAI,OACF,MAAM,YACJ,cACA,QAAQ,MAAM,YAAW,iCAAkC,iBAAgB,IAAK;OAGlF,MAAM,eAAe,YAAY;CAErC;CAGiB,mBAAmB,UAAgC;EAClE,IAAI,MAAM,WAAW,KAAK,QAAQ,MAAM,iBAAiB,aACvD,KAAK,cAAa,CAAE,CAAC,QAAO;CAEhC;CAEU,QAAK;EACb,KAAK,OAAO,IAAI,MAAK;CACvB;CAMU,eAAe,OAAiB;EACxC,MAAM,YAAY,KAAK;EACvB,KAAK,uBAAuB;EAC5B,IAAI,aAAa,MAAM,WAAW,GAChC;EAEF,MAAM,QAAQ,KAAK,OAAO,IAAI,UAAS;EACvC,KAAK,OAAO,IAAI,OAAO,SAAS,KAAK,YAAY,IAAI,QAAQ,CAAC;CAChE;CAGU,iBAAiB,OAAoB;EAC7C,KAAK,uBAAuB;EAC5B,IAAI,MAAM,UAAU,MAAM,WAAW,MAAM,SAEzC;EAEF,MAAM,QAAQ,KAAK,OAAO,IAAI,UAAS;EAOvC,MAAM,SAAS;GALb,SAAS,KAAK,IAAI,QAAQ,GAAG,KAAK,SAAS;GAC3C,WAAW,KAAK,IAAI,QAAQ,GAAG,CAAC;GAChC,MAAM;GACN,KAAK,KAAK;EAEG,EAAQ,MAAM;EAC7B,IAAI,WAAW,QACb;EAEF,MAAM,eAAc;EACpB,KAAK,OAAO,IAAI,OAAO,MAAM;CAC/B;CAEQ,OAAO,OAAa;EAC1B,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,SAAS;EACvE,KAAK,WAAU;EACf,KAAK,SAAS,SAAS,qBAAqB,KAAK,SAAS,WAAW,CAAC;CACxE;CAGQ,iBAAc;EACpB,MAAM,OAAO,KAAK,SAAS,WAAU;EACrC,OAAO,OAAO,IAAI,OAAQ,KAAK,SAAS,aAAa,eAAe;CACtE;CAEQ,UAAO;EACb,MAAM,WAAW,KAAK,eAAc;EACpC,OAAO,KAAK,OAAO,WAAW,KAAK,aAAa,KAAK,MAAM,WAAW,QAAQ,CAAC;CACjF;CAEiB,iBAAsB;EACrC,IAAI,CAAC,KAAK,SAAS;GACjB,KAAK,iBAAiB,OAAM;GAC5B,KAAK,MAAM,KAAK,QAAO,CAAE,CAAC,KAAK,UAAU,GAAG,CAAC;EAC/C;CACF;CAEiB,iBAAiB,UAA6B;EAC7D,IACE,KAAK,WACL,CAAC,MAAM,aACP,KAAK,OAAO,IAAI,aACf,MAAM,gBAAgB,WAAW,MAAM,WAAW,GAEnD;EAEF,KAAK,uBAAuB;EAC5B,MAAM,SAAS,MAAM,kBAAkB,UAAU,MAAM,SAAS;EAChE,MAAM,UAAU,KAAK,eAAc,CAAE,CAAC;EACtC,MAAM,aAAa,QAAQ,QAAQ,8CAA8C;EACjF,IAAI;EACJ,IAAI,cAAc,KAAK,KAAK,SAAS,UAAU,GAC7C,SAAS;OACJ,IAAI,UAAU,QAAQ,SAAS,MAAM,GAC1C,SAAS;OAET;EAEF,KAAK,sBAAqB;EAC1B,MAAM,UAAU,KAAK,eAAc,IAAK,KAAK;EAC7C,KAAK,UAAU;GACb,WAAW,MAAM;GACjB,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,aAAa;GACb;GACA,eAAe,qBAAqB,KAAK,SAAS,WAAW;GAC7D,SAAS;GACT,UAAU;GACV,SAAS,CAAC;IAAE,GAAG,YAAY,IAAG;IAAI,GAAG,MAAM;GAAO,CAAE;EACrD;EACD,KAAK,KAAK,iBAAiB,eAAe,KAAK,aAAa;EAC5D,KAAK,KAAK,iBAAiB,aAAa,KAAK,WAAW;EACxD,KAAK,KAAK,iBAAiB,iBAAiB,KAAK,eAAe;EAChE,KAAK,KAAK,iBAAiB,sBAAsB,KAAK,oBAAoB;EAC1E,KAAK,KAAK,iBAAiB,aAAa,KAAK,gBAAgB,EAAE,SAAS,MAAK,CAAE;CACjF;CAMQ,eAAe,IAAU;EAC/B,IAAI,CAAC,KAAK,eAAc,GACtB,OAAO;EAET,OAAO,KAAK,KAAK,KAAK,eAAc,CAAE,CAAC,cAAc,aAAa;CACpE;CAQiB,kBAAkB,UAA2B;EAC5D,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,MAAM,YACrB;EAEF,IAAI,QAAQ,WAAW,QAAQ,WAAW,UAAU;GAClD,MAAM,eAAc;GACpB;EACF;EACA,MAAM,QAAQ,MAAM,QAAQ;EAC5B,IAAI,CAAC,OACH;EAEF,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,IAAI,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,eAAe,EAAE,GACvD,MAAM,eAAc;CAExB;CAEiB,iBAAiB,UAA6B;EAC7D,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS,cAAc,MAAM,WAC/B;EAEF,MAAM,KAAK,MAAM,UAAU,QAAQ;EACnC,IAAI,CAAC,QAAQ,SAAS;GACpB,MAAM,KAAK,MAAM,UAAU,QAAQ;GACnC,IAAI,KAAK,IAAI,EAAE,KAAK,QAAQ,KAAK,IAAI,EAAE,KAAK,MAC1C;GAEF,IACE,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAC1B,QAAQ,WAAW,aAAa,CAAC,KAAK,eAAe,EAAE,GACxD;IAEA,KAAK,WAAU;IACf;GACF;GACA,QAAQ,UAAU;GAClB,IAAI;IACF,KAAK,KAAK,kBAAkB,MAAM,SAAS;GAC7C,QAAQ,CAER;EACF;EACA,IAAI,MAAM,YACR,MAAM,eAAc;EAEtB,MAAM,MAAM,YAAY,IAAG;EAC3B,QAAQ,UAAU,CAChB,GAAG,QAAQ,QAAQ,QAAQ,MAAM,MAAM,EAAE,KAAK,eAAe,GAC7D;GAAE,GAAG;GAAK,GAAG,MAAM;EAAO,CAC3B;EACD,QAAQ,WAAW,QAAQ,cAAc;EAEzC,KAAK,KAAK,QAAQ,QAAQ;CAC5B;CAGQ,KAAK,UAAgB;EAC3B,MAAM,UAAU,KAAK,QAAO;EAC5B,MAAM,SAAS,QAAQ,MAAM;EAC7B,MAAM,MAAM,QAAQ,QAAQ,SAAS,MAAM;EAC3C,IAAI,WAAW,KACb,KAAK,MAAM,OAAO,WAAW,OAAO,aAAa,CAAC;OAC7C,IAAI,YAAY,QACrB,KAAK,MAAM,UAAU,CAAC;OACjB;GACL,MAAM,QAAQ,SAAS;GACvB,KAAK,MAAM,QAAQ,KAAK,OAAO,cAAc,QAAQ,QAAQ,WAAW;EAC1E;CACF;CAEiB,eAAe,UAA6B;EAC3D,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS,cAAc,MAAM,WAC/B;EAEF,KAAK,WAAU;EACf,IAAI,CAAC,QAAQ,SAEX;EAEF,KAAK,uBAAuB;EAC5B,KAAK,QAAQ,SAAS,KAAK,SAAS,OAAO,CAAC;CAC9C;CAEiB,mBAAmB,UAA6B;EAC/D,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS,cAAc,MAAM,WAC/B;EAEF,KAAK,WAAU;EACf,IAAI,QAAQ,SACV,KAAK,SAAS,KAAK,OAAO,QAAQ,aAAa;CAEnD;CAGiB,wBAAwB,UAA6B;EACpE,IAAI,MAAM,WAAW,KAAK,MACxB,KAAK,gBAAgB,KAAK;CAE9B;CAGQ,SAAS,SAAuB;EACtC,MAAM,QAAQ,QAAQ,QAAQ;EAC9B,MAAM,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,SAAS;EACtD,IAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,KAAK,MAAM,GACrC,OAAO;EAET,QAAQ,KAAK,IAAI,MAAM,MAAM,KAAK,IAAI,MAAM;CAC9C;CAEQ,QAAQ,SAAyB,UAAgB;EACvD,MAAM,UAAU,KAAK,QAAO;EAC5B,MAAM,SAAS,QAAQ,MAAM;EAC7B,MAAM,WAAW,QAAQ;EACzB,IAAI;EACJ,IAAI,WAAW,gBAAgB;GAE7B,MAAM,QAAQ,QAAQ,QAAQ,MAAM,IAAI,QAAQ,CAAC,CAAC,SAAS;GAC3D,SAAS,SAAS,IAAI,QAAQ;EAChC,OAAO,IAAI,WAAW,KAAiB;GACrC,MAAM,QAAQ,QAAQ,WAAW,MAAM,IAAI,QAAQ;GACnD,SAAS,SAAS,IAAI,QAAQ,KAAK;EACrC,OAAO,IAAI,WAAW,UAAU,IAAI,mBAClC,SAAS;OAET,SAAS,KAAK,QAAQ,SAAS,QAAQ;EAEzC,IAAI,WAAW,MAAM;GACnB,IAAI,KAAK,OAAO,aAAa;IAC3B,KAAK,OAAO,IAAI,MAAK;IACrB;GACF;GACA,SAAS;EACX;EACA,KAAK,SAAS,QAAQ,QAAQ,aAAa;CAC7C;CAEQ,QAAQ,SAAmB,UAAgB;EACjD,IAAI,OAAO;EACX,QAAQ,SAAS,GAAG,MAAK;GACvB,IAAI,KAAK,IAAI,IAAI,QAAQ,IAAI,KAAK,KAAK,QAAQ,SAAS,KAAK,QAAQ,GACnE,OAAO;EAEX,CAAC;EACD,OAAO;CACT;CAGQ,SAAS,OAAe,eAAsB;EACpD,MAAM,SAAS,KAAK,QAAO,CAAE,CAAC,UAAU;EACxC,MAAM,aAAa,KAAK;EACxB,MAAM,aAAa,KAAK;EACxB,KAAK,iBAAiB,OAAM;EAC5B,KAAK,kBAAkB;EACvB,MAAM,QAAQ,UAAU,KAAK;EAC7B,KAAK,QAAQ;EACb,KAAK,OAAO,IAAI,aAAa,KAAK;EAClC,IAAI,SAAS,KAAK,YAEhB,AAAK,KAAK,UAAU,SAClB,KAAK,OAAO,cAAc,QAAQ,GAAG,KAAK,YAAY,CAAC,GACvD,QAAQ;EAIZ,KAAK,MAAM,QAAQ,CAAC;EACpB,IAAI,iBAAkB,eAAe,UAAU,eAAe,GAC5D;EAEF,MAAM,YAAY,KAAK,KAAK,QAC1B,CACE;GAAE,QAAQ,GAAG,WAAU;GAAM,WAAW,iBAAiB,WAAU;EAAO,GAC1E;GAAE,QAAQ,GAAG,OAAM;GAAM,WAAW;EAAoB,CACzD,GACD;GAAE,UAAU;GAAkB,QAAQ;EAAc,CAAE;EAExD,KAAK,kBAAkB;EACvB,AAAK,UAAU,SAAS,WACjB;GACH,IAAI,KAAK,oBAAoB,WAC3B,KAAK,kBAAkB;EAE3B,SACK,CAEL,CAAC;CAEL;CAMQ,iBAAc;EACpB,MAAM,WAAW,KAAK,KAAK;EAC3B,OAAO,KAAK,cAAa,KAAM,WAAW,IAAI,KAAK,IAAI,KAAK,QAAQ,QAAQ,IAAI,KAAK;CACvF;CAGQ,wBAAqB;EAC3B,IAAI,CAAC,KAAK,iBACR;EAEF,MAAM,WAAW,WACf,KAAK,SAAS,aAAa,iBAAiB,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE;EAErE,KAAK,gBAAgB,OAAM;EAC3B,KAAK,kBAAkB;EACvB,IAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,GAC1C,KAAK,MAAM,UAAU,CAAC;CAE1B;CAEQ,MAAM,QAAgB,QAAc;EAC1C,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,KAAK,MAAM,YAAY,qBAAqB,GAAG,OAAM,GAAI;EAC9D,KAAK,KAAK,MAAM,YAAY,SAAS,IAAI,iBAAiB,OAAM,SAAU;CAC5E;CAEQ,aAAU;EAChB,MAAM,YAAY,KAAK,SAAS;EAChC,KAAK,KAAK,oBAAoB,eAAe,KAAK,aAAa;EAC/D,KAAK,KAAK,oBAAoB,aAAa,KAAK,WAAW;EAC3D,KAAK,KAAK,oBAAoB,iBAAiB,KAAK,eAAe;EACnE,KAAK,KAAK,oBAAoB,sBAAsB,KAAK,oBAAoB;EAC7E,KAAK,KAAK,oBAAoB,aAAa,KAAK,cAAc;EAC9D,IAAI,cAAc,QAChB,IAAI;GACF,KAAK,KAAK,sBAAsB,SAAS;EAC3C,QAAQ,CAER;EAEF,KAAK,UAAU;CACjB;CAhgBW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAgB,cAAA;EAAA,UAAA;EAAA,MAAA,EAAA,gBAAA,iHAAA;EAAA,WA7DhB,CAAC,aAAa;EAAC,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,SAAA;GAAA,aAAA;GAAA,UAAA;EAAA,GAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAoE0B;GAAyB,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UA/DnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDT,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MA9DS;IAAiB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,mBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAgB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAyB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;CAAA,CAAA;;AAgE7D,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAlE5B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS;IAAC;IAAmB;IAAkB;GAAyB;GAGxE,WAAW,CAAC,aAAa;GACzB,MAAM,EACJ,OACE,iHACH;GACD,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuDX,CAAA;CAO+E,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,gBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,WAAS,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,GAAA,iBACnC,yBAAyB,GAAA,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA;AC1J/E,MAAM,aAA2D;CAC/D,OAAO;CACP,eAAe;AAChB;AA6DD,MAAa,+BAAkD,CAAC,IAAK,GAAI;AAEzE,SAAS,oBAAoB,QAAqC;CAChE,MAAM,SAAS,UAAU,CAAA,EAAA,CACtB,QAAQ,MAAM,OAAO,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAC1C,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAC1B,MAAM,GAAG,MAAM,IAAI,CAAC;CACvB,MAAM,SAAS,MAAM,QAAQ,GAAG,MAAM,MAAM,KAAK,MAAM,MAAM,IAAI,EAAE;CACnE,OAAO,OAAO,SAAS,IAAI,SAAS,CAAC,GAAG,4BAA4B;AACtE;AAuBA,IAAa,iBAAb,MAAa,eAAc;CACR,WAAW,OAAO,kBAAgB;CAClC,QAAQ,OAAO,gBAAc;CAC7B,aAAa,OAAO,mBAAmB;CACvC,WAAW,OAAO,QAAQ;CAO3C,KACE,SACA,UAA0B,CAAA,GAAE;EAE5B,MAAM,aAAa,oBAAoB,QAAQ,UAAU;EACzD,MAAM,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,QAAQ,WAAW,CAAC,GAAG,CAAC,GAAG,WAAW,SAAS,CAAC;EAC7F,MAAM,cAAc,QAAQ,eAAe;EAC3C,MAAM,WAAW,QAAQ,YAAY,KAAK;EAC1C,MAAM,SAAS,SAAS,IAAI,SAAS;EAErC,IAAI,UAA0D;EAC9D,IAAI,mBAAwC;EAC5C,MAAM,MAAM,IAAI,WACd;GACE,OAAO,YAA0B;IAC/B,mBAAkB;IAClB,IAAI,SAAS;KACX,QAAQ,MAAK;KACb,MAAM,QAAQ;IAChB;GACF;GACA,sBAA4B,SAAS,eAAc;GACnD,iBAA0B,SAAS,aAAa;EACjD,GACD,OAAO;EAGT,MAAM,SAAwB;GAC5B;GACA;GACA;GACA;GACA,MAAM,QAAQ;GACd,mBAAmB,CAAC,QAAQ;GAC5B;EACD;EACD,UAAU,KAAK,SAAS,KAAK,kBAAkB;GAC7C,MAAM;GACN,UAAU;GACV,eAAe;GACf,cAAc;GACd,mBAAmB;GACnB,iBAAiB;GACjB,mBAAmB,QAAQ;GAC3B,WAAW,QAAQ,aAAa,OAAO;GACvC,WAAW,WAAW,QAAQ,aAAa;GAC3C;GACA,QAAQ,OAAO,UAAQ,EAAE;GACzB,OAAO,OAAO,YAAU,EAAE;EAC3B,CAAA;EAED,IAAI,CAAC,aAAa;GAGhB,KAAK,MAAM,YAAY,OAAO;GAC9B,mBAAmB,KAAK,WAAW,SAAS,WAAW,IAAI;EAC7D;EAEA,AAAK,QAAQ,YAAY,WAAW,IAAI,MAAK,CAAE;EAC/C,OAAO;CACT;CAvEW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAd,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,YADD;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAc,YAAA,CAAA;EAD1B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACpFlC,MAAM,eAAoD;CAIxD,OACE;CACF,YAAY;AACb;AAiCD,IAAa,mBAAb,MAAa,iBAAgB;CACR,SAAS,OAAO,iBAAe;CACjC,WAAW,OAAO,cAAY;CAC9B,gBAAgB,oBAAmB;CAEjC,cAAc,aAAa,KAAK,OAAO;CAEvC,kBAAkB,eAAc;EACjD,MAAM,UAAU,KAAK,cAAa,CAAE,EAAE,WAAW;EACjD,OAAO,KAAK,OAAO,iBAAiB,WAAW,UAAU,IAAI,UAAU;CACzE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,gCAAA,CAAA,CAAA;CACiB,kBAAkB,SAAS,OAAO;EACnD,QAAQ,OAAO,QAAQ;EACvB,WAAW,CACT;GAAE,SAAS;GAAiB,UAAU,KAAK,OAAO;EAAI,GACtD;GAAE,SAAS;GAAc,UAAU,KAAK;EAAQ,CACjD;CACF,CAAA;CAED,cAAA;EACE,MAAM,cAAc,OAAO,eAAe,EAAE,MAAM,KAAI,CAAE;EAExD,YAAY,aAAa,IAAI,KAAK;EAElC,IAAI,KAAK,OAAO,iBAAiB,SAC/B,YAAY,YAAY,IAAI,KAAK;EAEnC,IAAI,KAAK,OAAO,mBAAmB;GACjC,MAAM,OAAO,OAAgC,UAAU,CAAC,CAAC;GACzD,IAAI,eAAoC;GACxC,sBAAqB;IACnB,MAAM,QAAQ,KAAK,QAAqB,mBAAmB;IAC3D,IAAI,OACF,eAAe,mBAAkB,KAAK;GAE1C,CAAC;GACD,OAAO,UAAU,CAAC,CAAC,gBAAgB,eAAc,CAAE;EACrD;CACF;CAtCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAgB,cAAA;EAAA,UAAA;EAAA,MAAA,EAAA,YAAA;GAAA,SAAA;GAAA,2BAAA;EAAA,EAAA;EAAA,WANhB,CAAC,aAAa;EAAC,UAAA;EAAA,UAChB;;;;EAGP,UAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MANO;GAAiB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,UAAA,CAAA,mBAAA;EAAA,CAAA;CAAA,CAAA;;AAQhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAV5B,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS,CAAC,iBAAiB;GAC3B,MAAM;IAAE,WAAW;IAAe,6BAA6B;GAAmB;GAClF,WAAW,CAAC,aAAa;GACzB,UAAU;;;;EAIX,CAAA;;;;ACID,IAAa,iBAAb,MAAa,eAAc;CACR,WAAW,OAAO,kBAAgB;CAClC,QAAQ,OAAO,gBAAc;CAC7B,aAAa,OAAO,mBAAmB;CACvC,WAAW,OAAO,iBAAiB;CACnC,WAAW,OAAO,QAAQ;CAC1B,SAAS,OAAO,iBAAiB,EAAE,UAAU,KAAI,CAAE,KAAK;CAOzE,KAAqB,WAAoB,UAA0B,CAAA,GAAE;EACnE,MAAM,eAAe,QAAQ,gBAAgB;EAC7C,MAAM,cAAc,QAAQ,eAAe;EAC3C,MAAM,WAAW,QAAQ,YAAY,KAAK;EAC1C,MAAM,SAAS,SAAS,IAAI,SAAS;EAErC,MAAM,MADO,iBAAiB,KAAK,QAAQ,KAAK,QACpC,MAAS;EAErB,MAAM,cAA6B;GACjC,SAAS;GACT;GACA,MAAM,QAAQ;GACd,mBAAmB,CAAC,QAAQ;EAC7B;EACD,MAAM,MAAM,KAAK,SAAS,KAA0B,kBAAkB;GACpE,MAAM;GACN,UAAU;GAGV,eAAe,iBAAiB,WAAW;GAC3C,cAAc;GACd,mBAAmB;GACnB,iBAAiB;GACjB,mBAAmB,QAAQ;GAC3B,WAAW,QAAQ,aAAa,OAAO;GACvC;GACA,OAAO,OAAO,OAAO,UAAQ,EAAE,KAAK,OAAO,SAAO,EAAE;GACpD,MAAM,OAAO,OAAO,YAAU,EAAE,KAAK,OAAO,WAAS,EAAE;EACxD,CAAA;EAED,IAAI,CAAC,aAAa;GAGhB,KAAK,MAAM,YAAY,GAAG;GAC1B,MAAM,mBAAmB,KAAK,WAAW,SAAS,WAAW,IAAI;GACjE,AAAK,IAAI,YAAY,WAAW,iBAAgB,CAAE;EACpD;EACA,OAAO;CACT;CAnDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAd,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,YADD;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAc,YAAA,CAAA;EAD1B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;AC/ClC,SAAS,cAAc,OAAY;CACjC,OAAO,iBAAiB,aAAa,MAAM,gBAAgB;AAC7D;AAEA,MAAM,iBAAkD;CACtD,QAAQ;CAER,KAAK;AACN;AAoDD,IAAa,mBAAb,MAAa,iBAAgB;CACR,SAAS,OAAO,iBAAe;CACjC,OAAO,OAAgC,UAAU,CAAC,CAAC;CACnD,WAAW,OAAO,iBAAiB;CAEjC,UACjB,wIAC+C,eAAe,KAAK,OAAO;CAE5E,cAAA;EAEE,kBAAkB,EAChB,aAAY;GAGV,MAAM,OAAO,KAAK,SAAS,gBAAe,IAAK,KAAK,SAAS,OAAM,IAAK;GACxE,KAAK,KAAK,MAAM,YAAY,uBAAuB,GAAG,KAAI,GAAI;EAChE,EACD,CAAA;CACH;CAEU,KAAK,QAA2B,MAAa;EACrD,KAAK,OAAO,OAAO,QAAQ,IAAI;CACjC;CAGU,UAAU,OAAY;EAC9B,MAAM,KAAK,cAAc,KAAK;EAC9B,IAAI,cAAc,QAAQ,KAAK,KAAK,SAAS,EAAE,GAC7C;EAEF,KAAK,KAAK,SAAS,KAAK;CAC1B;CAGU,QAAQ,OAAY;EAC5B,IAAI,EAAE,iBAAiB,kBAAkB,MAAM,QAAQ,YAAY,CAAC,KAAK,OAAO,QAC9E;EAEF,MAAM,eAAc;EACpB,MAAM,gBAAe;EACrB,KAAK,OAAO,OAAM;CACpB;CA1CW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAgB,cAAA;EAAA,UAAA;EAAA,MAAA;GAAA,WAAA;IAAA,gBAAA;IAAA,gBAAA;IAAA,WAAA;IAAA,YAAA;IAAA,WAAA;GAAA;GAAA,YAAA,EAAA,SAAA,UAAA;EAAA;EAAA,UAAA;EAAA,UAbjB;;;;;;;;;;;;EAWT,UAAA;EAAA,QAAA,CAAA,gIAAA;CAAA,CAAA;;AAEU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EA9B5B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAU,MACd;IACJ,WAAW;IACX,kBAAkB;IAClB,kBAAkB;IAClB,aAAa;IACb,cAAc;IACd,aAAa;GACd;GAAA,UAQS;;;;;;;;;;;;GAWT,QAAA,CAAA,gIAAA;EAAA,CAAA;;;;AC/CH,SAAS,WAAW,UAAyB;CAC3C,QAAQ,OAA8B;EACpC,IAAI,qBAAqB,GAAG,cAAc,WAAW,GACnD,OAAO,GAAG,QAAQ,CAAC,EAAE,SAAS,EAAC,GAAI,EAAE,SAAS,EAAC,CAAE,GAAG;GAClD,UAAU;GACV,QAAQ;GACR,MAAM;EACP,CAAA;EAEH,MAAM,SAAS,aAAa,WAAW,SAAS;EAChD,OAAO,GAAG,QACR,CACE;GAAE,SAAS;GAAG,WAAW,iBAAiB,OAAM;EAAK,GACrD;GAAE,SAAS;GAAG,WAAW;EAAoB,CAC9C,GACD;GAAE,UAAU;GAAkB,QAAQ;GAAgB,MAAM;EAAW,CAAE;CAE7E;AACF;AA0BA,IAAa,iBAAb,MAAa,eAAc;CACR,WAAW,OAAO,kBAAgB;CAElC,QAAQ,IAAI,cAC3B,EAAE,UAAU,MAAM,YAAY,KAAK,QAAQ,MAAM,QAAQ,YAAY,QAAQ,EAAC,GAC9E;EACE,YAAY;EACZ,WAAW,OAAO,aAAa;EAC/B,UAAU,OAAO,QAAQ;EACzB,YAAY,OAAO,UAAU;CAC9B,CAAA;CAUH,KAAK,SAAuB;EAC1B,OAAO,KAAK,MAAM,KAAK,OAAO;CAChC;CAGQ,QAAQ,MAAoB,UAAyB;EAC3D,MAAM,SAAwB;GAC5B,SAAS,KAAK;GACd,QAAQ,KAAK;GACb;GACA,OAAO,QAAQ,SAAS,KAAK,KAAK,QAAQ,IAAI;GAC9C,cAAc,KAAK,OAAM;EAC1B;EACD,MAAM,aAAa,KAAK,SAAS,KAC/B,kBACA;GACE,MAAM;GACN,SAAS;GAET,mBAAmB;GACnB;GAEA,OAAO,WAAW,QAAQ;GAC1B,OAAO,OAAO,UAAQ,EAAE;EACzB,CAAA;EAEH,MAAM,UAAmB,WAAW,aAAa,SAAS;EAE1D,OAAO;GACL,MAFW,mBAAmB,cAAc,UAAU;GAGtD,OAAO,OAAO,WAAU;IACtB,WAAW,MAAM,MAAM;IACvB,MAAM,WAAW;GACnB;EACD;CACH;CAvDW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAd,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,YADD;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAc,YAAA,CAAA;EAD1B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACclC,IAAa,yBAAb,MAAa,uBAAsB;CACd,SAAS,OAAO,iBAAe;CACjC,MAAM,OAAO,cAAY;CAEhC,OAAO,QAA2B;EAC1C,KAAK,IAAI,MAAM,OAAO,MAAM,OAAO,KAAK;CAC1C;CAEU,SAAM;EACd,KAAK,IAAI,MAAK;CAChB;CAVW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,MAAA,EAAA,gBAAA,uHAAA;EAAA,UAAA;EAAA,UAzBvB;;;;;;;;;;;;;;;;;;;;;;;;EAuBT,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MA9BS;IAAe,UAAA;IAAA,QAAA,CAAA,SAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAmB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAiB,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAe,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;CAAA,CAAA;;AAgCvE,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EAlClC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS;IAAC;IAAiB;IAAqB;IAAmB;GAAe;GAGlF,MAAM,EACJ,OACE,uHACH;GACD,UAAU;;;;;;;;;;;;;;;;;;;;;;;;EAwBX,CAAA;;;ACxCD,IAAa,uBAAb,MAAa,qBAAoB;CACd,WAAW,OAAO,kBAAgB;CAClC,WAAW,OAAO,QAAQ;CAO3C,KAAK,SAA6B;EAChC,MAAM,WAAW,QAAQ,YAAY,KAAK;EAC1C,MAAM,SAAS,SAAS,IAAI,SAAS;EACrC,MAAM,eAAe,QAAQ,QAAQ,MAAM,WAAW,OAAO,SAAS,QAAQ;EAC9E,MAAM,SAA8B;GAClC,OAAO,QAAQ;GACf,SAAS,QAAQ,QAAQ,QAAQ,WAAW,OAAO,SAAS,QAAQ;GACpE,aAAa,cAAc,SAAS,OAAO;EAC5C;EAeD,OAdY,KAAK,SAAS,KAAqC,wBAAwB;GACrF,MAAM;GACN,UAAU;GACV,eAAe;GACf,cAAc;GACd,mBAAmB;GACnB,iBAAiB;GACjB,mBAAmB,QAAQ;GAC3B,MAAM;GACN,WAAW,QAAQ,SAAS,OAAO;GACnC;GACA,QAAQ,OAAO,UAAQ,EAAE;GACzB,OAAO,OAAO,YAAU,EAAE;EAC3B,CACM,CAAA,CAAI;CACb;CAjCW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,YADP;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EADhC,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;ACqBlC,IAAa,yBAAb,MAAa,uBAAsB;CACd,SAAS,OAAO,iBAAe;CACjC,MAAM,OAAO,cAAY;CAEhC,OAAO,QAAiC;EAChD,KAAK,IAAI,MAAM,OAAO,MAAM;CAC9B;CANW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,MAAA,EAAA,gBAAA,6IAAA;EAAA,UAAA;EAAA,UAlBvB;;;;;;;;;;;;;;;;;EAgBT,UAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MAvBS;GAAiB,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA,SAAA,CAAA,SAAA;EAAA,CAAA;CAAA,CAAA;;AAyBhB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EA3BlC,MAAA;EAAU,MAAA,CAAA;GACT,UAAU;GACV,SAAS,CAAC,iBAAiB;GAC3B,MAAM,EAGJ,OACE,6IACH;GACD,UAAU;;;;;;;;;;;;;;;;;EAiBX,CAAA;;;ACbD,IAAI,cAAc;AA0BlB,IAAa,uBAAb,MAAa,qBAAoB;CACd,WAAW,OAAO,kBAAgB;CAClC,QAAQ,OAAO,gBAAc;CAC7B,aAAa,OAAO,mBAAmB;CACvC,WAAW,OAAO,QAAQ;CAO3C,KAAK,SAA6B;EAChC,MAAM,WAAW,QAAQ,YAAY,KAAK;EAC1C,MAAM,UAAU,QAAQ,SAAS,SAC7B,QAAQ,UACR,CAAC,EAAE,OAAO,SAAS,IAAI,SAAS,CAAC,CAAC,GAAE,CAAE;EAC1C,MAAM,cAAc,QAAQ,WAAW,WAAW,OAAO,SAAS,QAAQ;EAC1E,MAAM,eAAe,eAAe,IAAI,cAAc;EACtD,MAAM,KAAK;EACX,MAAM,UAAU,yBAAyB;EACzC,MAAM,YAAY,2BAA2B;EAE7C,IAAI,kBAAkB;EACtB,MAAM,gBAA6C,QAAQ,KAAK,QAAQ,UAAS;GAC/E,IAAI;GACJ,IAAI,OAAO,SAAS,UAClB,UAAU;QACL,IAAI,OAAO,SAAS,eACzB,UAAU;QACL,IAAI,CAAC,iBAAiB;IAC3B,UAAU;IACV,kBAAkB;GACpB,OACE,UAAU;GAEZ,OAAO;IACL,OAAO,OAAO;IACd,QAAQ,OAAO,MAAM,OAAO;IAC5B,MAAM,OAAO;IACb;IACA,SAAS,UAAU;GACpB;EACH,CAAC;EAED,MAAM,SAA8B;GAClC,OAAO,QAAQ;GACf,SAAS,QAAQ;GACjB;GACA;GACA,SAAS;EACV;EAED,MAAM,qBAA0B;GAC9B,MAAM,SAAS,cAAc;GAC7B,IAAI,QACF,IAAI,MAAM,OAAO,MAAM;EAE3B;EACA,MAAM,MAAM,KAAK,SAAS,KAAqC,wBAAwB;GACrF,MAAM;GACN,UAAU;GACV,eAAe;GACf,cAAc;GACd,mBAAmB;GACnB,iBAAiB;GAEjB,UAAU;GACV,mBAAmB,QAAQ;GAE3B,oBAAoB,IAAI,MAAM,cAAc,YAAY,EAAE,MAAM;GAChE,MAAM;GACN;GACA,gBAAgB;GAChB,iBAAiB,QAAQ,UAAU,YAAY;GAC/C,QAAQ,OAAO,UAAQ,EAAE;GACzB,OAAO,OAAO,UAAQ,EAAE;EACzB,CAAA;EAKD,KAAK,MAAM,YAAY,GAAG;EAC1B,MAAM,iBAAiB,KAAK,WAAW,SAAS,WAAU;GACxD,aAAY;GACZ,OAAO;EACT,CAAC;EACD,AAAK,IAAI,YAAY,WAAW,eAAc,CAAE;EAEhD,OAAO,IAAI;CACb;CAzFW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAApB,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAoB,YADP;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EADhC,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;AChDlC,SAAgB,sBAAsB,UAAiC,CAAA,GAAE;CACvE,OAAO,yBAAyB;EAC9B,cAAc,QAAQ,UAAU,CAAA,GAAI,0BAAyB,GAAI,mBAAkB,CAAE;EACrF,eAAe;GAAE,WAAW;GAAO,oBAAoB;GAAO,GAAG,QAAQ;EAAM,CAAE;EACjF;GAAE,SAAS;GAAkB,YAAY;EAAwB;EACjE,wBAAuB;CACxB,CAAA;AACH"}