@scenography/overlay-presets 0.2.0 → 0.3.1

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":"index.cjs","names":[],"sources":["../src/lucide.ts","../src/util.ts","../src/components/fullscreen/fullscreen.ts","../src/components/mute/mute.ts","../src/components/cardboard/cardboard.ts","../src/components/environment/environment.ts","../src/components/credits/credits.ts","../src/components/action-button/action-button.ts","../src/components/info/info.ts","../src/components/artwork-info/artwork-info.ts","../src/components/watermark/watermark.ts","../src/components/settings/settings.ts","../src/presets/default.ts"],"sourcesContent":["import type { IconContent } from \"./util\";\n\n/**\n * Render a Lucide icon by name as overlay button content.\n *\n * Lucide is an OPTIONAL peer dependency: it's imported lazily, so the overlay\n * works without it. When lucide isn't installed the icon renders empty — install\n * `lucide`, pass your own `icon` to a prefab, or use `actionButton` if you don't\n * want the dependency.\n */\nexport function lucideIcon(name: string, size = 18): IconContent {\n return () => {\n const span = document.createElement(\"span\");\n span.style.display = \"inline-flex\";\n void import(\"lucide\")\n .then((lucide) => {\n const node = (lucide as Record<string, unknown>)[name];\n if (node === undefined) return;\n const svg = lucide.createElement(node as Parameters<typeof lucide.createElement>[0]);\n svg.setAttribute(\"width\", String(size));\n svg.setAttribute(\"height\", String(size));\n span.replaceChildren(svg);\n })\n .catch(() => {\n /* lucide not installed: leave the placeholder empty */\n });\n return span;\n };\n}\n","import type { OverlayContext } from \"@scenography/overlay\";\n\n/**\n * Button/label content: a glyph string, a DOM node (e.g. an SVG icon), or a\n * factory returning either. A factory is handy for icons that must be a fresh\n * node each time the content is swapped (a DOM node can only live in one place).\n */\nexport type IconContent = string | Node | (() => string | Node);\n\n/** Replace an element's content with an icon/glyph (string → text, node → child). */\nexport function setIcon(el: HTMLElement, content: IconContent): void {\n const value = typeof content === \"function\" ? content() : content;\n el.replaceChildren();\n if (typeof value === \"string\") el.textContent = value;\n else el.appendChild(value);\n}\n\n/**\n * Run `apply` once and re-run it whenever the overlay's locale changes, so a\n * component's `Text` values stay localized live. Returns an unsubscribe to call\n * on teardown (a no-op when no locale source is configured).\n */\nexport function bindLocale(ctx: OverlayContext, apply: () => void): () => void {\n apply();\n return ctx.locale?.subscribe(apply) ?? (() => {});\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface FullscreenButtonOptions {\n /** Icons for the enter/exit states. Default to plain glyphs ⛶ / ✕. */\n icons?: { enter: IconContent; exit: IconContent };\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A fullscreen toggle. daisyUI prefab (styled with `btn`). Defaults to plain\n * glyphs and pulls in no icon library; pass `icons` (e.g. via `lucideIcon` or\n * your own SVGs) for richer iconography.\n */\nexport function fullscreenButton(options: FullscreenButtonOptions = {}): OverlayComponent {\n const enter = options.icons?.enter ?? \"⛶\";\n const exit = options.icons?.exit ?? \"✕\";\n\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n\n const sync = (): void => {\n const active = document.fullscreenElement !== null;\n setIcon(button, active ? exit : enter);\n button.setAttribute(\"aria-label\", active ? \"Exit fullscreen\" : \"Enter fullscreen\");\n };\n const onClick = (): void => {\n if (document.fullscreenElement) void document.exitFullscreen();\n else void ctx.surface.requestFullscreen?.();\n };\n\n button.addEventListener(\"click\", onClick);\n document.addEventListener(\"fullscreenchange\", sync);\n sync();\n\n return {\n element: button,\n destroy() {\n button.removeEventListener(\"click\", onClick);\n document.removeEventListener(\"fullscreenchange\", sync);\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface MuteButtonOptions {\n /** Icons for the unmuted/muted states. Default to plain glyphs 🔊 / 🔇. */\n icons?: { on: IconContent; off: IconContent };\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A mute toggle for background audio. daisyUI prefab (styled with `btn`). Drives\n * `ctx.media` master mute, so it silences every source while keeping their\n * individual volumes — one click restores the mix exactly. Defaults to plain\n * glyphs (no icon library); pass `icons` (e.g. `lucideIcon`) for richer icons.\n */\nexport function muteButton(options: MuteButtonOptions = {}): OverlayComponent {\n const on = options.icons?.on ?? \"🔊\";\n const off = options.icons?.off ?? \"🔇\";\n\n return (ctx) => {\n const media = ctx.gallery.ctx.media;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n\n const sync = (): void => {\n const muted = media.muted();\n setIcon(button, muted ? off : on);\n button.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n };\n const onClick = (): void => {\n media.setMuted(!media.muted());\n sync();\n };\n\n button.addEventListener(\"click\", onClick);\n sync();\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\n\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface CardboardButtonOptions {\n /** Button icon. Defaults to a plain glyph 🥽. */\n icon?: IconContent;\n /** Aria-label. Defaults to \"Cardboard mode\". */\n label?: string;\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A cardboard (stereoscopic) toggle. Flips `ctx.gallery.setRender({ cardboard })`\n * to split the view into a left/right eye pair for a phone-in-a-headset viewer,\n * and reflects the on state with daisyUI's `btn-active`. Defaults to a plain\n * glyph; pass `icon` (e.g. `lucideIcon(\"RectangleGoggles\")`).\n */\nexport function cardboardButton(options: CardboardButtonOptions = {}): OverlayComponent {\n const icon = options.icon ?? \"🥽\";\n const label = options.label ?? \"Cardboard mode\";\n\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n button.setAttribute(\"aria-label\", label);\n setIcon(button, icon);\n\n let enabled = false;\n const onClick = (): void => {\n enabled = !enabled;\n ctx.gallery.setRender({ cardboard: enabled });\n button.classList.toggle(\"btn-active\", enabled);\n button.setAttribute(\"aria-pressed\", String(enabled));\n };\n\n button.addEventListener(\"click\", onClick);\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { bindLocale, setIcon, type IconContent } from \"../../util\";\nimport type { SettingsSliderItem } from \"../settings\";\n\nexport interface EnvironmentPanelOptions {\n /** Modal title, also the button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /** Button icon. Defaults to a plain glyph ☀. */\n icon?: IconContent;\n /** Sliders shown in the modal (e.g. time of day, sun azimuth). */\n sliders: SettingsSliderItem[];\n /** Quick presets shown as buttons; each sets one or more sliders by `key`. */\n presets?: { label: Text; values: Record<string, number> }[];\n}\n\n/**\n * An environment panel: a button (e.g. a sun/cloud icon) opening a daisyUI modal\n * with sliders for ambient settings like time of day and sun azimuth. Optional\n * in the default preset — galleries with a procedural sky opt in. daisyUI +\n * Lucide prefab.\n */\nexport function environmentPanel(options: EnvironmentPanelOptions): OverlayComponent {\n const icon = options.icon ?? \"☀\";\n\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, icon);\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n\n box.append(close, heading);\n\n const cleanups: Array<() => void> = [];\n const relabels: Array<() => void> = [];\n // Slider controls by key, so presets can set them and reflect visually.\n const controls = new Map<string, { input: HTMLInputElement; value: HTMLSpanElement; onChange: (v: number) => void }>();\n\n for (const slider of options.sliders) {\n const field = document.createElement(\"label\");\n field.className = \"flex flex-col gap-1 mt-2 first:mt-0\";\n const head = document.createElement(\"span\");\n head.className = \"label-text text-sm flex justify-between\";\n const name = document.createElement(\"span\");\n const value = document.createElement(\"span\");\n value.className = \"opacity-60\";\n head.append(name, value);\n const input = document.createElement(\"input\");\n input.type = \"range\";\n input.className = \"range range-sm\";\n input.min = String(slider.min);\n input.max = String(slider.max);\n input.step = String(slider.step ?? 0.1);\n input.value = String(slider.value);\n value.textContent = input.value;\n const onInput = (): void => {\n value.textContent = input.value;\n slider.onChange(parseFloat(input.value));\n };\n input.addEventListener(\"input\", onInput);\n cleanups.push(() => input.removeEventListener(\"input\", onInput));\n field.append(head, input);\n box.appendChild(field);\n controls.set(slider.key, { input, value, onChange: slider.onChange });\n relabels.push(() => {\n name.textContent = resolveText(slider.label);\n });\n }\n\n // Preset buttons: apply values to the matching sliders (and update them).\n if (options.presets?.length) {\n const row = document.createElement(\"div\");\n row.className = \"flex flex-wrap gap-2 mt-4\";\n for (const preset of options.presets) {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"btn btn-xs\";\n const apply = (): void => {\n for (const [key, val] of Object.entries(preset.values)) {\n const ctrl = controls.get(key);\n if (!ctrl) continue;\n ctrl.input.value = String(val);\n ctrl.value.textContent = String(val);\n ctrl.onChange(val);\n }\n };\n btn.addEventListener(\"click\", apply);\n cleanups.push(() => btn.removeEventListener(\"click\", apply));\n row.appendChild(btn);\n relabels.push(() => {\n btn.textContent = resolveText(preset.label);\n });\n }\n box.appendChild(row);\n }\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n for (const fn of cleanups) fn();\n unbind();\n },\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { bindLocale, setIcon, type IconContent } from \"../../util\";\n\n/** One acknowledged asset: what it is, who made it, where it's from. */\nexport interface CreditItem {\n /** Discriminant; optional so a plain item needs no `type`. */\n type?: \"item\";\n /** Display name, e.g. \"Brick Wall 006\". */\n label: string;\n /** Author/creator. */\n author?: string;\n /** Source URL; turns `label` into a link. */\n href?: string;\n /** License, e.g. \"CC0\". */\n license?: string;\n}\n\n/** A labeled separator within a flat list or a tab's children. */\nexport interface CreditSection {\n type: \"section\";\n /** Section heading, e.g. \"Textures\". */\n label: string;\n}\n\n/** A tab grouping a list of items/sections under a title. */\nexport interface CreditTab {\n type: \"tab\";\n /** Tab title. */\n label: string;\n /** The grouped entries (items and sections; no nested tabs). */\n children: Array<CreditItem | CreditSection>;\n}\n\n/** Anything that can appear at the top level of the credits. */\nexport type CreditEntry = CreditItem | CreditSection | CreditTab;\n\nexport interface CreditsPanelOptions {\n /**\n * The credited entries. Loose items/sections render first (above the tabs);\n * tabs render after, as a daisyUI tab strip. A getter is re-read on locale\n * change to relocalize.\n */\n items: CreditEntry[] | (() => CreditEntry[]);\n /** Panel heading. Defaults to \"Credits\". */\n title?: Text;\n /** Button icon. Defaults to the plain glyph ⓘ. */\n icon?: IconContent;\n /** Override the daisyUI/utility classes on the toggle button. */\n className?: string;\n}\n\nlet panelCount = 0;\n\nconst buildRow = (item: CreditItem): HTMLDivElement => {\n const row = document.createElement(\"div\");\n row.className = \"text-sm leading-snug\";\n\n const name = document.createElement(item.href ? \"a\" : \"span\");\n name.textContent = item.label;\n if (item.href) {\n const link = name as HTMLAnchorElement;\n link.href = item.href;\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.className = \"link link-hover font-medium\";\n } else {\n name.className = \"font-medium\";\n }\n row.appendChild(name);\n\n const meta = [item.author, item.license].filter(Boolean).join(\" · \");\n if (meta) {\n const small = document.createElement(\"span\");\n small.className = \"opacity-60\";\n small.textContent = ` — ${meta}`;\n row.appendChild(small);\n }\n return row;\n};\n\nconst buildSection = (section: CreditSection): HTMLDivElement => {\n const heading = document.createElement(\"div\");\n heading.className = \"font-semibold text-xs uppercase tracking-wide opacity-50 mt-3 first:mt-0\";\n heading.textContent = section.label;\n return heading;\n};\n\nconst buildEntries = (entries: Array<CreditItem | CreditSection>): Node[] =>\n entries.map((entry) => (entry.type === \"section\" ? buildSection(entry) : buildRow(entry)));\n\n/**\n * A credits panel: a button opening a centered daisyUI modal (✕ close,\n * click-outside, Esc) listing attributions. Entries are data (`items`): loose\n * items and sections show first; `tab` entries become a daisyUI tab strip below\n * (first tab open), so an exhibition can credit its author up top and group\n * assets/technology/etc. into tabs with sectioned lists. Defaults to a plain\n * glyph icon; pass `icon` (e.g. `lucideIcon`).\n */\nexport function creditsPanel(options: CreditsPanelOptions): OverlayComponent {\n const icon = options.icon ?? \"ⓘ\";\n\n return (ctx) => {\n const tabsName = `sg-credits-tabs-${panelCount++}`;\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n setIcon(button, icon);\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n close.setAttribute(\"aria-label\", \"Close\");\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-3\";\n\n // Loose items/sections on top; the tab strip below them.\n const loose = document.createElement(\"div\");\n loose.className = \"flex flex-col gap-1\";\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n tablist.className = \"tabs tabs-border mt-2\";\n\n box.append(close, heading, loose, tablist);\n\n // Click-outside-to-close backdrop (daisyUI pattern for native <dialog>).\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const render = (entries: CreditEntry[]): void => {\n const flat = entries.filter((e): e is CreditItem | CreditSection => e.type !== \"tab\");\n const tabs = entries.filter((e): e is CreditTab => e.type === \"tab\");\n\n loose.replaceChildren(...buildEntries(flat));\n loose.style.display = flat.length ? \"\" : \"none\";\n\n const nodes: Node[] = [];\n tabs.forEach((tab, i) => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = tabsName;\n input.role = \"tab\";\n input.className = \"tab\";\n input.setAttribute(\"aria-label\", tab.label);\n if (i === 0) input.checked = true;\n const pane = document.createElement(\"div\");\n pane.className = \"tab-content pt-3\";\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-1\";\n list.replaceChildren(...buildEntries(tab.children));\n pane.appendChild(list);\n nodes.push(input, pane);\n });\n tablist.replaceChildren(...nodes);\n tablist.style.display = tabs.length ? \"\" : \"none\";\n };\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title ?? \"Credits\");\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n render(typeof options.items === \"function\" ? options.items() : options.items);\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy() {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n unbind();\n },\n };\n };\n}\n","import type { OverlayComponent, OverlayContext } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface ActionButtonOptions {\n /** Click handler; receives the overlay context (gallery, surface, root). */\n onClick: (ctx: OverlayContext) => void;\n /** Icon content: a glyph, an SVG node, or a factory. No lucide required. */\n icon?: IconContent;\n /** Text label, placed after the icon. */\n label?: string;\n /** Accessible label (use it when there's an icon but no text). */\n ariaLabel?: string;\n /** Override the daisyUI/utility classes. */\n className?: string;\n}\n\n/**\n * A generic clickable button — the lucide-free alternative to the opinionated\n * prefabs. Supply any `icon` (glyph, your own SVG, a Lucide icon you wired up)\n * and an `onClick`. daisyUI prefab (styled with `btn`).\n */\nexport function actionButton(options: ActionButtonOptions): OverlayComponent {\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-sm\";\n\n if (options.icon !== undefined) setIcon(button, options.icon);\n if (options.label !== undefined) {\n const label = document.createElement(\"span\");\n label.textContent = options.label;\n button.appendChild(label);\n }\n if (options.ariaLabel) button.setAttribute(\"aria-label\", options.ariaLabel);\n\n const onClick = (): void => options.onClick(ctx);\n button.addEventListener(\"click\", onClick);\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\n\nexport interface InfoPanelOptions {\n /** Modal title, also the ⓘ button's aria-label. */\n title: Text;\n /** Body text shown in the modal (e.g. the control instructions). */\n text: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n}\n\n/**\n * An info panel: an ⓘ button that opens a centered daisyUI modal with a title\n * and a block of text (with an ✕ close, click-outside and Esc). Content is data,\n * so the app supplies the (already localized) strings. daisyUI + Lucide prefab.\n */\nexport function infoPanel(options: InfoPanelOptions): OverlayComponent {\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"Info\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-3\";\n\n const text = document.createElement(\"p\");\n text.className = \"text-sm leading-relaxed\";\n\n box.append(close, heading, text);\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n text.textContent = resolveText(options.text);\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n unbind();\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport type { Artwork } from \"@scenography/plugin-artwork/data\";\n\nexport interface ArtworkInfoOptions {\n /** The artworks' data, looked up by `id` on select. Usually `scene.artworks`. */\n artworks: Artwork[];\n /** Aria-label for the panel's ✕ close button. Defaults to \"Close\". */\n closeLabel?: string;\n /**\n * Availability wording shown in the panel. `'for-sale'` reads `forSale`; both\n * negative states (`'sold'` / `'not-for-sale'`) read `privateCollection`, since\n * publicly they mean the same: not available. Defaults to English.\n */\n availabilityText?: { forSale?: string; privateCollection?: string };\n /** Label for the link shown when an artwork has an `href`. Defaults to \"View artwork\". */\n linkText?: string;\n}\n\nconst AVAILABILITY_COLOR = { available: \"#22c55e\", unavailable: \"#ef4444\" } as const;\n\nconst STYLE_ID = \"scenography-artwork-info-styles\";\n// Panel occupies the left 40%; the piece is framed in the right 60% (centered at 0.7).\nconst PANEL_FRACTION = 0.4;\nconst FREE_FRACTION = 1 - PANEL_FRACTION;\nconst CENTER_X = PANEL_FRACTION + FREE_FRACTION / 2;\n\n/**\n * Inject the panel's own CSS once. Self-contained on purpose: a consumer's\n * Tailwind build doesn't scan this package in `node_modules`, so utility classes\n * wouldn't be generated — the panel must not depend on them.\n */\nfunction ensureStyles(): void {\n if (typeof document === \"undefined\" || document.getElementById(STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent =\n // z-index above the touch-joystick layer (z-index:5) so it sits on top and,\n // marked as an overlay item, the joysticks won't steal its touches.\n `.sg-artwork-panel{position:absolute;left:0;top:0;height:100%;width:${PANEL_FRACTION * 100}%;` +\n `box-sizing:border-box;background:rgba(0,0,0,0.85);color:#fff;padding:2.5rem;z-index:10;` +\n `overflow-y:auto;display:none;flex-direction:column;gap:0.75rem;pointer-events:auto;` +\n `font-family:system-ui,sans-serif;}` +\n `.sg-artwork-panel--open{display:flex;}` +\n `.sg-artwork-panel__close{align-self:flex-end;background:none;border:none;color:#fff;` +\n `font-size:1.5rem;line-height:1;cursor:pointer;opacity:0.7;padding:0;}` +\n `.sg-artwork-panel__close:hover{opacity:1;}` +\n `.sg-artwork-panel__title{font-size:1.75rem;font-weight:700;margin:0;}` +\n `.sg-artwork-panel__meta{display:flex;flex-direction:column;gap:0.15rem;font-size:0.9rem;opacity:0.8;}` +\n `.sg-artwork-panel__status{display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;font-weight:600;}` +\n `.sg-artwork-panel__dot{width:0.7rem;height:0.7rem;border-radius:50%;flex:none;}` +\n `.sg-artwork-panel__desc{font-size:1rem;line-height:1.6;margin-top:0.5rem;white-space:pre-line;}` +\n `.sg-artwork-panel__link{align-self:flex-start;margin-top:0.5rem;display:inline-flex;` +\n `align-items:center;gap:0.4rem;color:#fff;text-decoration:underline;font-size:0.95rem;}` +\n `.sg-artwork-panel__link:hover{opacity:0.8;}`;\n document.head.appendChild(style);\n}\n\nconst join = (parts: (string | undefined)[], sep: string): string =>\n parts.filter((p): p is string => Boolean(p)).join(sep);\n\n/**\n * The artwork info panel. The engine emits `artwork:select { id }` when you click\n * a piece's cartela (or the piece itself); this opens a left-hand panel (40%\n * width, full height, black at 85% alpha) with the full caption and asks the\n * camera to frame the piece in the free right 60%. Closing with ✕ re-centers the\n * piece full-width; if the user walks/turns away the engine emits\n * `artwork:released` and the panel just closes (no camera move).\n *\n * Content is data (`artworks`), unlocalized for now. The panel is mounted on the\n * surface (escaping the overlay grid); the slot element is an inert placeholder.\n */\nexport function artworkInfo(options: ArtworkInfoOptions): OverlayComponent {\n const byId = new Map(options.artworks.map((a) => [a.id, a]));\n\n return (ctx) => {\n ensureStyles();\n const camera = ctx.gallery.ctx.camera;\n\n const panel = document.createElement(\"div\");\n // `sg-overlay__item` marks it as an interactive overlay control so the touch\n // joysticks (which read window touches) don't steal taps/scroll over it.\n panel.className = \"sg-artwork-panel sg-overlay__item\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"sg-artwork-panel__close\";\n close.textContent = \"✕\";\n close.setAttribute(\"aria-label\", options.closeLabel ?? \"Close\");\n\n const title = document.createElement(\"h2\");\n title.className = \"sg-artwork-panel__title\";\n const meta = document.createElement(\"div\");\n meta.className = \"sg-artwork-panel__meta\";\n\n // Availability badge: a colored dot + a derived label (the dot matches the\n // physical cartela; the text spells out \"Private collection\" when not for sale).\n const status = document.createElement(\"div\");\n status.className = \"sg-artwork-panel__status\";\n const statusDot = document.createElement(\"span\");\n statusDot.className = \"sg-artwork-panel__dot\";\n const statusText = document.createElement(\"span\");\n status.append(statusDot, statusText);\n\n const desc = document.createElement(\"p\");\n desc.className = \"sg-artwork-panel__desc\";\n\n // Optional link to the piece's page (catalog / marketplace), opens a new tab.\n const link = document.createElement(\"a\");\n link.className = \"sg-artwork-panel__link\";\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.textContent = `${options.linkText ?? \"View artwork\"} ↗`;\n\n panel.append(close, title, meta, status, desc, link);\n ctx.surface.appendChild(panel);\n\n let currentId: string | null = null;\n\n const render = (artwork: Artwork): void => {\n title.textContent = artwork.title ?? \"\";\n const lines = [\n join([artwork.author, artwork.year?.toString()], \", \"),\n join([artwork.technique, artwork.dimensions], \", \"),\n ].filter(Boolean);\n meta.replaceChildren(\n ...lines.map((line) => {\n const span = document.createElement(\"span\");\n span.textContent = line;\n return span;\n }),\n );\n\n // Availability: for-sale shows green + \"For sale\"; both negative states\n // collapse to red + \"Private collection\" (publicly identical: not for sale).\n const av = artwork.availability;\n if (av === undefined) {\n status.style.display = \"none\";\n } else {\n status.style.display = \"flex\";\n const forSale = av === \"for-sale\";\n statusDot.style.backgroundColor = forSale\n ? AVAILABILITY_COLOR.available\n : AVAILABILITY_COLOR.unavailable;\n statusText.textContent = forSale\n ? (options.availabilityText?.forSale ?? \"For sale\")\n : (options.availabilityText?.privateCollection ?? \"Private collection\");\n }\n\n desc.textContent = artwork.description ?? \"\";\n\n if (artwork.href) {\n link.href = artwork.href;\n link.style.display = \"inline-flex\";\n } else {\n link.style.display = \"none\";\n }\n };\n\n const hide = (): void => {\n panel.classList.remove(\"sg-artwork-panel--open\");\n currentId = null;\n };\n\n const onSelect = (payload?: unknown): void => {\n const id = (payload as { id?: string } | undefined)?.id;\n const artwork = id ? byId.get(id) : undefined;\n if (!artwork || !id) return;\n currentId = id;\n render(artwork);\n panel.classList.add(\"sg-artwork-panel--open\");\n camera.focusArtwork(id, { centerX: CENTER_X, widthFraction: FREE_FRACTION });\n };\n\n const onReleased = (payload?: unknown): void => {\n const id = (payload as { id?: string } | undefined)?.id;\n if (id && id === currentId) hide();\n };\n\n const onClose = (): void => {\n const id = currentId;\n hide();\n if (id) camera.centerArtwork(id);\n };\n\n const offSelect = ctx.gallery.on(\"artwork:select\", onSelect);\n const offReleased = ctx.gallery.on(\"artwork:released\", onReleased);\n close.addEventListener(\"click\", onClose);\n\n // Inert placeholder for the slot; the real panel lives on the surface.\n const placeholder = document.createElement(\"div\");\n placeholder.style.display = \"none\";\n\n return {\n element: placeholder,\n destroy() {\n offSelect();\n offReleased();\n close.removeEventListener(\"click\", onClose);\n panel.remove();\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\n\nexport interface WatermarkOptions {\n /** Text shown. Defaults to \"scenography\". */\n text?: string;\n /** Replace the default styling with your own class. */\n className?: string;\n}\n\n/**\n * A small, unobtrusive text watermark (a \"brand stamp\"). No daisyUI/Lucide — just\n * a styled element. Defaults to the text \"scenography\".\n */\nexport function watermark(options: WatermarkOptions = {}): OverlayComponent {\n return () => {\n const el = document.createElement(\"div\");\n el.textContent = options.text ?? \"scenography\";\n if (options.className) el.className = options.className;\n else el.style.cssText = \"font:600 13px system-ui,sans-serif;color:#fff;opacity:0.6;\";\n return el;\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\n\n/** One option in a single-choice section. */\nexport interface SettingsChoice {\n value: string;\n label: Text;\n}\n\n/** One boolean toggle in the multi-toggle section. */\nexport interface SettingsToggleItem {\n key: string;\n label: Text;\n value: boolean;\n}\n\n/**\n * One single-choice control rendered as a joined row of radio-buttons — a\n * segmented control (e.g. antialias level, bloom strength).\n */\nexport interface SettingsChoiceGroup {\n key: string;\n label: Text;\n /** Current value (matches one option's `value`). */\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n}\n\n/** One numeric slider (range) in the graphics section. */\nexport interface SettingsSliderItem {\n key: string;\n label: Text;\n value: number;\n min: number;\n max: number;\n /** Step granularity. Defaults to `0.1`. */\n step?: number;\n onChange: (value: number) => void;\n}\n\nexport interface SettingsPanelOptions {\n /** Modal title, also the gear button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /**\n * A single-choice section rendered as a `<select>` (e.g. language). Shown in\n * its own tab; `tab` is the tab's title (falls back to `label`).\n */\n language?: {\n tab?: Text;\n label: Text;\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n };\n /** A single-choice section rendered as radios (e.g. handedness). Own tab. */\n handedness?: {\n tab?: Text;\n label: Text;\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n };\n /** A multi-toggle section (e.g. graphics: ssao / bloom / shadows). Own tab. */\n graphics?: {\n tab?: Text;\n label: Text;\n items: SettingsToggleItem[];\n onChange: (key: string, value: boolean) => void;\n /** Optional segmented radio-button groups above the toggles (e.g. antialias / bloom). */\n choiceGroups?: SettingsChoiceGroup[];\n /** Optional numeric sliders shown below the toggles (e.g. eye separation). */\n sliders?: SettingsSliderItem[];\n };\n}\n\n/**\n * A settings panel: a ⚙ button that opens a centered daisyUI modal (with an ✕\n * close, click-outside and Esc). Each section lives in its own tab (the first\n * present one opens by default). Sections are data — the app supplies labels,\n * current values and callbacks — so the panel stays generic. Labels are `Text`\n * and relocalize live via the overlay's locale source. daisyUI + Lucide prefab.\n */\nexport function settingsPanel(options: SettingsPanelOptions): OverlayComponent {\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"Settings\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n box.appendChild(close);\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n box.appendChild(heading);\n\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n tablist.className = \"tabs tabs-border\";\n box.appendChild(tablist);\n\n const cleanups: Array<() => void> = [];\n // Text setters re-run on every locale change.\n const relabels: Array<() => void> = [];\n\n // Add a tab (radio + content); the first one added opens by default. The\n // tab's visible title is its (relocalizable) `aria-label`, the daisyUI way.\n let first = true;\n const addTab = (tab: Text | undefined, fallback: Text, content: HTMLElement): void => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = \"sg-settings-tabs\";\n input.role = \"tab\";\n input.className = \"tab\";\n if (first) {\n input.checked = true;\n first = false;\n }\n const pane = document.createElement(\"div\");\n pane.className = \"tab-content pt-4\";\n pane.appendChild(content);\n tablist.append(input, pane);\n relabels.push(() => input.setAttribute(\"aria-label\", resolveText(tab ?? fallback)));\n };\n\n // General: language as a <select>.\n if (options.language) {\n const lang = options.language;\n const field = document.createElement(\"label\");\n field.className = \"form-control w-full\";\n const label = document.createElement(\"span\");\n label.className = \"label-text mb-1 block\";\n const select = document.createElement(\"select\");\n select.className = \"select select-bordered select-sm w-full\";\n const optionEls = lang.options.map((opt) => {\n const o = document.createElement(\"option\");\n o.value = opt.value;\n select.appendChild(o);\n return { o, label: opt.label };\n });\n select.value = lang.value;\n const onChange = (): void => lang.onChange(select.value);\n select.addEventListener(\"change\", onChange);\n cleanups.push(() => select.removeEventListener(\"change\", onChange));\n relabels.push(() => {\n label.textContent = resolveText(lang.label);\n for (const { o, label: l } of optionEls) o.textContent = resolveText(l);\n });\n field.append(label, select);\n addTab(lang.tab, lang.label, field);\n }\n\n // Controls: handedness as radios.\n if (options.handedness) {\n const hand = options.handedness;\n const pane = document.createElement(\"div\");\n const legend = document.createElement(\"div\");\n legend.className = \"label-text mb-1 block\";\n pane.appendChild(legend);\n const onChange = (e: Event): void => hand.onChange((e.target as HTMLInputElement).value);\n const texts: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const opt of hand.options) {\n const row = document.createElement(\"label\");\n row.className = \"label cursor-pointer justify-start gap-3 py-1\";\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = \"sg-settings-handedness\";\n input.value = opt.value;\n input.className = \"radio radio-sm\";\n input.checked = opt.value === hand.value;\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n const text = document.createElement(\"span\");\n text.className = \"label-text text-sm\";\n row.append(input, text);\n pane.appendChild(row);\n texts.push({ el: text, label: opt.label });\n }\n relabels.push(() => {\n legend.textContent = resolveText(hand.label);\n for (const { el, label } of texts) el.textContent = resolveText(label);\n });\n addTab(hand.tab, hand.label, pane);\n }\n\n // Graphics: toggles, stacked vertically (one per row).\n if (options.graphics) {\n const gfx = options.graphics;\n const pane = document.createElement(\"div\");\n const legend = document.createElement(\"div\");\n legend.className = \"label-text mb-1 block\";\n pane.appendChild(legend);\n\n // Segmented radio-button groups (e.g. antialias / bloom), above the toggles.\n // daisyUI renders `<input type=radio class=\"btn\">` as a button showing its\n // aria-label; `join` fuses them into one segmented control.\n const groupLabels: Array<{\n el: HTMLSpanElement;\n label: Text;\n opts: Array<{ input: HTMLInputElement; label: Text }>;\n }> = [];\n for (const group of gfx.choiceGroups ?? []) {\n const field = document.createElement(\"div\");\n field.className = \"mb-3\";\n const label = document.createElement(\"span\");\n label.className = \"label-text text-sm mb-1 block\";\n const row = document.createElement(\"div\");\n row.className = \"join w-full\";\n const name = `sg-settings-${group.key}`;\n const opts = group.options.map((opt) => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = name;\n input.className = \"btn btn-sm join-item flex-1\";\n input.value = opt.value;\n input.checked = opt.value === group.value;\n const onChange = (): void => group.onChange(input.value);\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n row.appendChild(input);\n return { input, label: opt.label };\n });\n field.append(label, row);\n pane.appendChild(field);\n groupLabels.push({ el: label, label: group.label, opts });\n }\n\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-1\";\n pane.appendChild(list);\n const texts: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const item of gfx.items) {\n const row = document.createElement(\"label\");\n row.className = \"label cursor-pointer justify-start gap-3 py-1\";\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.className = \"toggle toggle-sm\";\n input.checked = item.value;\n const onChange = (): void => gfx.onChange(item.key, input.checked);\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n const text = document.createElement(\"span\");\n text.className = \"label-text text-sm\";\n row.append(input, text);\n list.appendChild(row);\n texts.push({ el: text, label: item.label });\n }\n // Numeric sliders below the toggles (e.g. cardboard eye separation).\n const sliderLabels: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const slider of gfx.sliders ?? []) {\n const field = document.createElement(\"label\");\n field.className = \"flex flex-col gap-1 mt-2\";\n const head = document.createElement(\"span\");\n head.className = \"label-text text-sm flex justify-between\";\n const name = document.createElement(\"span\");\n const value = document.createElement(\"span\");\n value.className = \"opacity-60\";\n head.append(name, value);\n const input = document.createElement(\"input\");\n input.type = \"range\";\n input.className = \"range range-sm\";\n input.min = String(slider.min);\n input.max = String(slider.max);\n input.step = String(slider.step ?? 0.1);\n input.value = String(slider.value);\n value.textContent = input.value;\n const onInput = (): void => {\n value.textContent = input.value;\n slider.onChange(parseFloat(input.value));\n };\n input.addEventListener(\"input\", onInput);\n cleanups.push(() => input.removeEventListener(\"input\", onInput));\n field.append(head, input);\n pane.appendChild(field);\n sliderLabels.push({ el: name, label: slider.label });\n }\n\n relabels.push(() => {\n legend.textContent = resolveText(gfx.label);\n for (const { el, label, opts } of groupLabels) {\n el.textContent = resolveText(label);\n // daisyUI shows the button's aria-label as its visible text.\n for (const { input, label: l } of opts) input.setAttribute(\"aria-label\", resolveText(l));\n }\n for (const { el, label } of texts) el.textContent = resolveText(label);\n for (const { el, label } of sliderLabels) el.textContent = resolveText(label);\n });\n addTab(gfx.tab, gfx.label, pane);\n }\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n for (const fn of cleanups) fn();\n unbind();\n },\n };\n };\n}\n","import type { Overlay, Text } from \"@scenography/overlay\";\n\nimport { artworkInfo, type ArtworkInfoOptions } from \"../components/artwork-info\";\nimport { cardboardButton } from \"../components/cardboard\";\nimport { creditsPanel, type CreditEntry } from \"../components/credits\";\nimport { environmentPanel, type EnvironmentPanelOptions } from \"../components/environment\";\nimport { fullscreenButton } from \"../components/fullscreen\";\nimport { infoPanel, type InfoPanelOptions } from \"../components/info\";\nimport { muteButton } from \"../components/mute\";\nimport { settingsPanel, type SettingsPanelOptions } from \"../components/settings\";\nimport { watermark, type WatermarkOptions } from \"../components/watermark\";\nimport { lucideIcon } from \"../lucide\";\n\nexport interface DefaultPresetOptions {\n /** Settings panel (language / handedness / graphics); when given, a ⚙ button is added top-right. */\n settings?: SettingsPanelOptions;\n /** Environment panel (time of day, sun azimuth); when given, a ☼ button is added top-right. Off by default. */\n environment?: EnvironmentPanelOptions;\n /** Info panel (e.g. control instructions); when given, an ⓘ button is added bottom-right, above credits. */\n info?: InfoPanelOptions;\n /** Artwork info panel; when given, clicking a piece opens its caption on the left. */\n artworks?: ArtworkInfoOptions;\n /** Asset attributions; when given, a credits panel is added bottom-right. */\n credits?: CreditEntry[] | (() => CreditEntry[]);\n /** Credits panel title. */\n creditsTitle?: Text;\n /** Bottom-left watermark. Defaults to \"scenography\"; pass `false` to hide it. */\n watermark?: WatermarkOptions | false;\n}\n\n/**\n * The default overlay layout: mute + fullscreen (and a settings ⚙ when given)\n * top-right; an info ⓘ and a credits panel bottom-right when given. Wired with\n * Lucide icons — this is the opinionated, batteries-included layer, so the\n * coupling to daisyUI + Lucide lives here, not in `@scenography/overlay`.\n */\nexport function defaultPreset(overlay: Overlay, options: DefaultPresetOptions = {}): void {\n overlay.add(\n \"top-right\",\n muteButton({ icons: { on: lucideIcon(\"Volume2\"), off: lucideIcon(\"VolumeX\") } }),\n );\n overlay.add(\n \"top-right\",\n fullscreenButton({ icons: { enter: lucideIcon(\"Maximize\"), exit: lucideIcon(\"Minimize\") } }),\n );\n if (options.settings) overlay.add(\"top-right\", settingsPanel(options.settings));\n // Environment (time of day) is opt-in — only galleries with a procedural sky.\n if (options.environment)\n overlay.add(\"top-right\", environmentPanel({ icon: lucideIcon(\"CloudSun\"), ...options.environment }));\n // Cardboard sits just below in the top-right stack.\n overlay.add(\"top-right\", cardboardButton({ icon: lucideIcon(\"RectangleGoggles\") }));\n if (options.artworks) overlay.add(\"middle-left\", artworkInfo(options.artworks));\n // Info goes before credits so it sits above it in the bottom-right stack.\n if (options.info) overlay.add(\"bottom-right\", infoPanel(options.info));\n if (options.credits) {\n overlay.add(\n \"bottom-right\",\n creditsPanel({ items: options.credits, title: options.creditsTitle, icon: lucideIcon(\"Copyleft\") }),\n );\n }\n // Watermark shown by default (bottom-left); pass `watermark: false` to hide.\n if (options.watermark !== false) {\n overlay.add(\"bottom-left\", watermark(options.watermark ?? {}));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/lucide.ts","../src/util.ts","../src/components/fullscreen/fullscreen.ts","../src/components/mute/mute.ts","../src/components/cardboard/cardboard.ts","../src/components/reset-view/reset-view.ts","../src/components/view-mode/view-mode.ts","../src/components/view-mode/capabilities.ts","../src/components/environment/environment.ts","../src/components/credits/credits.ts","../src/components/action-button/action-button.ts","../src/components/info/info.ts","../src/components/help/diagrams.ts","../src/components/help/modality.ts","../src/components/help/help.ts","../src/components/artwork-info/artwork-info.ts","../src/components/watermark/watermark.ts","../src/components/settings/settings.ts","../src/presets/default.ts"],"sourcesContent":["import type { IconContent } from \"./util\";\n\n/**\n * Render a Lucide icon by name as overlay button content.\n *\n * Lucide is an OPTIONAL peer dependency: it's imported lazily, so the overlay\n * works without it. When lucide isn't installed the icon renders empty — install\n * `lucide`, pass your own `icon` to a prefab, or use `actionButton` if you don't\n * want the dependency.\n */\nexport function lucideIcon(name: string, size = 18): IconContent {\n return () => {\n const span = document.createElement(\"span\");\n span.style.display = \"inline-flex\";\n void import(\"lucide\")\n .then((lucide) => {\n const node = (lucide as Record<string, unknown>)[name];\n if (node === undefined) return;\n const svg = lucide.createElement(node as Parameters<typeof lucide.createElement>[0]);\n svg.setAttribute(\"width\", String(size));\n svg.setAttribute(\"height\", String(size));\n span.replaceChildren(svg);\n })\n .catch(() => {\n /* lucide not installed: leave the placeholder empty */\n });\n return span;\n };\n}\n","import type { OverlayContext } from \"@scenography/overlay\";\n\n/**\n * Button/label content: a glyph string, a DOM node (e.g. an SVG icon), or a\n * factory returning either. A factory is handy for icons that must be a fresh\n * node each time the content is swapped (a DOM node can only live in one place).\n */\nexport type IconContent = string | Node | (() => string | Node);\n\n/** Replace an element's content with an icon/glyph (string → text, node → child). */\nexport function setIcon(el: HTMLElement, content: IconContent): void {\n const value = typeof content === \"function\" ? content() : content;\n el.replaceChildren();\n if (typeof value === \"string\") el.textContent = value;\n else el.appendChild(value);\n}\n\n/**\n * Run `apply` once and re-run it whenever the overlay's locale changes, so a\n * component's `Text` values stay localized live. Returns an unsubscribe to call\n * on teardown (a no-op when no locale source is configured).\n */\nexport function bindLocale(ctx: OverlayContext, apply: () => void): () => void {\n apply();\n return ctx.locale?.subscribe(apply) ?? (() => {});\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface FullscreenButtonOptions {\n /** Icons for the enter/exit states. Default to plain glyphs ⛶ / ✕. */\n icons?: { enter: IconContent; exit: IconContent };\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A fullscreen toggle. daisyUI prefab (styled with `btn`). Defaults to plain\n * glyphs and pulls in no icon library; pass `icons` (e.g. via `lucideIcon` or\n * your own SVGs) for richer iconography.\n */\nexport function fullscreenButton(options: FullscreenButtonOptions = {}): OverlayComponent {\n const enter = options.icons?.enter ?? \"⛶\";\n const exit = options.icons?.exit ?? \"✕\";\n\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n\n const sync = (): void => {\n const active = document.fullscreenElement !== null;\n setIcon(button, active ? exit : enter);\n button.setAttribute(\"aria-label\", active ? \"Exit fullscreen\" : \"Enter fullscreen\");\n };\n const onClick = (): void => {\n if (document.fullscreenElement) void document.exitFullscreen();\n else void ctx.surface.requestFullscreen?.();\n };\n\n button.addEventListener(\"click\", onClick);\n document.addEventListener(\"fullscreenchange\", sync);\n sync();\n\n return {\n element: button,\n destroy() {\n button.removeEventListener(\"click\", onClick);\n document.removeEventListener(\"fullscreenchange\", sync);\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface MuteButtonOptions {\n /** Icons for the unmuted/muted states. Default to plain glyphs 🔊 / 🔇. */\n icons?: { on: IconContent; off: IconContent };\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A mute toggle for background audio. daisyUI prefab (styled with `btn`). Drives\n * `ctx.media` master mute, so it silences every source while keeping their\n * individual volumes — one click restores the mix exactly. Defaults to plain\n * glyphs (no icon library); pass `icons` (e.g. `lucideIcon`) for richer icons.\n */\nexport function muteButton(options: MuteButtonOptions = {}): OverlayComponent {\n const on = options.icons?.on ?? \"🔊\";\n const off = options.icons?.off ?? \"🔇\";\n\n return (ctx) => {\n const media = ctx.gallery.ctx.media;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n\n const sync = (): void => {\n const muted = media.muted();\n setIcon(button, muted ? off : on);\n button.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n };\n const onClick = (): void => {\n media.setMuted(!media.muted());\n sync();\n };\n\n button.addEventListener(\"click\", onClick);\n sync();\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\n\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface CardboardButtonOptions {\n /** Button icon. Defaults to a plain glyph 🥽. */\n icon?: IconContent;\n /** Aria-label. Defaults to \"Cardboard mode\". */\n label?: string;\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A cardboard (stereoscopic) toggle. Flips `ctx.gallery.setRender({ cardboard })`\n * to split the view into a left/right eye pair for a phone-in-a-headset viewer,\n * and reflects the on state with daisyUI's `btn-active`. Defaults to a plain\n * glyph; pass `icon` (e.g. `lucideIcon(\"RectangleGoggles\")`).\n */\nexport function cardboardButton(options: CardboardButtonOptions = {}): OverlayComponent {\n const icon = options.icon ?? \"🥽\";\n const label = options.label ?? \"Cardboard mode\";\n\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n button.setAttribute(\"aria-label\", label);\n setIcon(button, icon);\n\n let enabled = false;\n const onClick = (): void => {\n enabled = !enabled;\n ctx.gallery.setRender({ cardboard: enabled });\n button.classList.toggle(\"btn-active\", enabled);\n button.setAttribute(\"aria-pressed\", String(enabled));\n // Announce the mode so other overlay components (e.g. the help panel) can\n // react — the toggle owns the state, so this is the way to observe it.\n ctx.gallery.emit(\"cardboard:change\", { enabled });\n };\n\n button.addEventListener(\"click\", onClick);\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { bindLocale, setIcon, type IconContent } from \"../../util\";\n\nexport interface ResetViewButtonOptions {\n /** Button icon. Defaults to a plain glyph ⌂; pass `lucideIcon(\"House\")`. */\n icon?: IconContent;\n /** Aria-label (relocalized live). Defaults to \"Reset view\". */\n label?: Text;\n /** Override the daisyUI/utility classes on the button. */\n className?: string;\n}\n\n/**\n * A \"home\" button that returns the camera to the scene's start position and\n * orientation (`ctx.camera.reset()`) — for visitors who wander off and want to\n * get back to the entrance. Defaults to a plain glyph; pass `lucideIcon(\"House\")`.\n */\nexport function resetViewButton(options: ResetViewButtonOptions = {}): OverlayComponent {\n const icon = options.icon ?? \"⌂\";\n\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n setIcon(button, icon);\n\n const onClick = (): void => ctx.gallery.ctx.camera.reset();\n button.addEventListener(\"click\", onClick);\n\n const unbind = bindLocale(ctx, () =>\n button.setAttribute(\"aria-label\", resolveText(options.label ?? \"Reset view\")),\n );\n\n return {\n element: button,\n destroy: () => {\n button.removeEventListener(\"click\", onClick);\n unbind();\n },\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\nimport type { SelectableMode } from \"./capabilities\";\n\n/** Lucide icon per mode, shown beside its label in the picker. */\nconst MODE_ICON: Record<SelectableMode, string> = {\n touch: \"Hand\",\n vr: \"Orbit\",\n cardboard: \"RectangleGoggles\",\n};\n\n/** One selectable mode in the picker. */\nexport interface ViewModeItem {\n mode: SelectableMode;\n /** Short mode name (e.g. \"Touch\", \"VR\", \"Cardboard\"). */\n label: Text;\n}\n\nexport interface ViewModeButtonOptions {\n /** Modal title, also the button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /**\n * The modes to offer, in order. Usually pre-filtered to what the device\n * supports (see `availableModes`); the caller decides whether to show the\n * button at all (typically only when two or more modes are available).\n */\n modes: ViewModeItem[];\n}\n\n/**\n * A camera-control mode picker: a `ScanEye` button opening a small modal with one\n * entry per mode (Touch / VR / Cardboard). Picking one calls\n * `ctx.viewControls.setMode` — which, inside this click gesture, flips the\n * gyroscope and stereo split (so iOS grants motion here). The active mode is\n * marked and kept in sync via the `controls:change` event, so the settings panel\n * and this picker never disagree.\n */\nexport function viewModeButton(options: ViewModeButtonOptions): OverlayComponent {\n return (ctx) => {\n const viewControls = ctx.gallery.ctx.viewControls;\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"ScanEye\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-xs\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n box.appendChild(close);\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n box.appendChild(heading);\n\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-2\";\n box.appendChild(list);\n\n const cleanups: Array<() => void> = [];\n const relabels: Array<() => void> = [];\n const optionButtons: Array<{ el: HTMLButtonElement; mode: SelectableMode }> = [];\n\n for (const item of options.modes) {\n const optBtn = document.createElement(\"button\");\n optBtn.type = \"button\";\n optBtn.className = \"btn btn-block justify-start gap-3\";\n const iconEl = document.createElement(\"span\");\n iconEl.style.display = \"inline-flex\";\n setIcon(iconEl, lucideIcon(MODE_ICON[item.mode]));\n const label = document.createElement(\"span\");\n optBtn.append(iconEl, label);\n\n const onPick = (): void => {\n viewControls.setMode(item.mode);\n syncActive();\n dialog.close();\n };\n optBtn.addEventListener(\"click\", onPick);\n cleanups.push(() => optBtn.removeEventListener(\"click\", onPick));\n list.appendChild(optBtn);\n optionButtons.push({ el: optBtn, mode: item.mode });\n relabels.push(() => (label.textContent = resolveText(item.label)));\n }\n\n const syncActive = (): void => {\n const current = viewControls.getMode();\n for (const { el, mode } of optionButtons) el.classList.toggle(\"btn-active\", mode === current);\n };\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n // Keep the active mark in sync when the mode changes elsewhere (settings panel).\n const offChange = ctx.gallery.on(\"controls:change\", syncActive);\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => {\n syncActive();\n dialog.showModal();\n };\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n for (const fn of cleanups) fn();\n offChange();\n unbind();\n },\n };\n };\n}\n","import type { ControlMode } from \"@scenography/engine\";\n\n/** The camera-control modes a visitor can pick between (excludes `custom`). */\nexport type SelectableMode = Exclude<ControlMode, \"custom\">;\n\n/** What the current device can do, as far as camera control is concerned. */\nexport interface ControlCapabilities {\n /** A touch screen (coarse pointer) — needed for the on-screen joysticks. */\n touch: boolean;\n /** A motion sensor for gyroscope head-look (VR / Cardboard). */\n gyro: boolean;\n}\n\n/**\n * Sniff the device's control capabilities. Deliberately conservative: gyro is\n * gated behind a touch (mobile) device too, since desktops expose the\n * `DeviceOrientationEvent` interface but never fire it — pairing it with a coarse\n * pointer avoids offering VR/Cardboard where they can't work.\n */\nexport function detectControlCapabilities(): ControlCapabilities {\n const touch =\n typeof matchMedia !== \"undefined\"\n ? matchMedia(\"(pointer: coarse)\").matches\n : typeof navigator !== \"undefined\" && (navigator.maxTouchPoints ?? 0) > 0;\n const gyro = typeof window !== \"undefined\" && \"DeviceOrientationEvent\" in window;\n return { touch, gyro };\n}\n\n/**\n * The modes worth offering on this device, in display order. Touch needs a touch\n * screen; VR and Cardboard additionally need the gyro. Desktop (no touch) gets an\n * empty list — it uses keyboard/mouse and shows no mode picker at all.\n */\nexport function availableModes(caps: ControlCapabilities): SelectableMode[] {\n const modes: SelectableMode[] = [];\n if (caps.touch) modes.push(\"touch\");\n if (caps.touch && caps.gyro) modes.push(\"vr\", \"cardboard\");\n return modes;\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { bindLocale, setIcon, type IconContent } from \"../../util\";\nimport type { SettingsSliderItem } from \"../settings\";\n\nexport interface EnvironmentPanelOptions {\n /** Modal title, also the button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /** Button icon. Defaults to a plain glyph ☀. */\n icon?: IconContent;\n /** Sliders shown in the modal (e.g. time of day, sun azimuth). */\n sliders: SettingsSliderItem[];\n /** Quick presets shown as buttons; each sets one or more sliders by `key`. */\n presets?: { label: Text; values: Record<string, number> }[];\n}\n\n/**\n * An environment panel: a button (e.g. a sun/cloud icon) opening a daisyUI modal\n * with sliders for ambient settings like time of day and sun azimuth. Optional\n * in the default preset — galleries with a procedural sky opt in. daisyUI +\n * Lucide prefab.\n */\nexport function environmentPanel(options: EnvironmentPanelOptions): OverlayComponent {\n const icon = options.icon ?? \"☀\";\n\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, icon);\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n\n box.append(close, heading);\n\n const cleanups: Array<() => void> = [];\n const relabels: Array<() => void> = [];\n // Slider controls by key, so presets can set them and reflect visually.\n const controls = new Map<string, { input: HTMLInputElement; value: HTMLSpanElement; onChange: (v: number) => void }>();\n\n for (const slider of options.sliders) {\n const field = document.createElement(\"label\");\n field.className = \"flex flex-col gap-1 mt-2 first:mt-0\";\n const head = document.createElement(\"span\");\n head.className = \"label-text text-sm flex justify-between\";\n const name = document.createElement(\"span\");\n const value = document.createElement(\"span\");\n value.className = \"opacity-60\";\n head.append(name, value);\n const input = document.createElement(\"input\");\n input.type = \"range\";\n input.className = \"range range-sm\";\n input.min = String(slider.min);\n input.max = String(slider.max);\n input.step = String(slider.step ?? 0.1);\n input.value = String(slider.value);\n value.textContent = input.value;\n const onInput = (): void => {\n value.textContent = input.value;\n slider.onChange(parseFloat(input.value));\n };\n input.addEventListener(\"input\", onInput);\n cleanups.push(() => input.removeEventListener(\"input\", onInput));\n field.append(head, input);\n box.appendChild(field);\n controls.set(slider.key, { input, value, onChange: slider.onChange });\n relabels.push(() => {\n name.textContent = resolveText(slider.label);\n });\n }\n\n // Preset buttons: apply values to the matching sliders (and update them).\n if (options.presets?.length) {\n const row = document.createElement(\"div\");\n row.className = \"flex flex-wrap gap-2 mt-4\";\n for (const preset of options.presets) {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"btn btn-xs\";\n const apply = (): void => {\n for (const [key, val] of Object.entries(preset.values)) {\n const ctrl = controls.get(key);\n if (!ctrl) continue;\n ctrl.input.value = String(val);\n ctrl.value.textContent = String(val);\n ctrl.onChange(val);\n }\n };\n btn.addEventListener(\"click\", apply);\n cleanups.push(() => btn.removeEventListener(\"click\", apply));\n row.appendChild(btn);\n relabels.push(() => {\n btn.textContent = resolveText(preset.label);\n });\n }\n box.appendChild(row);\n }\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n for (const fn of cleanups) fn();\n unbind();\n },\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { bindLocale, setIcon, type IconContent } from \"../../util\";\n\n/** One acknowledged asset: what it is, who made it, where it's from. */\nexport interface CreditItem {\n /** Discriminant; optional so a plain item needs no `type`. */\n type?: \"item\";\n /** Display name, e.g. \"Brick Wall 006\". */\n label: string;\n /** Author/creator. */\n author?: string;\n /** Source URL; turns `label` into a link. */\n href?: string;\n /** License, e.g. \"CC0\". */\n license?: string;\n}\n\n/** A labeled separator within a flat list or a tab's children. */\nexport interface CreditSection {\n type: \"section\";\n /** Section heading, e.g. \"Textures\". */\n label: string;\n}\n\n/** A tab grouping a list of items/sections under a title. */\nexport interface CreditTab {\n type: \"tab\";\n /** Tab title. */\n label: string;\n /** The grouped entries (items and sections; no nested tabs). */\n children: Array<CreditItem | CreditSection>;\n}\n\n/** Anything that can appear at the top level of the credits. */\nexport type CreditEntry = CreditItem | CreditSection | CreditTab;\n\nexport interface CreditsPanelOptions {\n /**\n * The credited entries. Loose items/sections render first (above the tabs);\n * tabs render after, as a daisyUI tab strip. A getter is re-read on locale\n * change to relocalize.\n */\n items: CreditEntry[] | (() => CreditEntry[]);\n /** Panel heading. Defaults to \"Credits\". */\n title?: Text;\n /** Button icon. Defaults to the plain glyph ⓘ. */\n icon?: IconContent;\n /** Override the daisyUI/utility classes on the toggle button. */\n className?: string;\n}\n\nlet panelCount = 0;\n\nconst buildRow = (item: CreditItem): HTMLDivElement => {\n const row = document.createElement(\"div\");\n row.className = \"text-sm leading-snug\";\n\n const name = document.createElement(item.href ? \"a\" : \"span\");\n name.textContent = item.label;\n if (item.href) {\n const link = name as HTMLAnchorElement;\n link.href = item.href;\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.className = \"link link-hover font-medium\";\n } else {\n name.className = \"font-medium\";\n }\n row.appendChild(name);\n\n const meta = [item.author, item.license].filter(Boolean).join(\" · \");\n if (meta) {\n const small = document.createElement(\"span\");\n small.className = \"opacity-60\";\n small.textContent = ` — ${meta}`;\n row.appendChild(small);\n }\n return row;\n};\n\nconst buildSection = (section: CreditSection): HTMLDivElement => {\n const heading = document.createElement(\"div\");\n heading.className = \"font-semibold text-xs uppercase tracking-wide opacity-50 mt-3 first:mt-0\";\n heading.textContent = section.label;\n return heading;\n};\n\nconst buildEntries = (entries: Array<CreditItem | CreditSection>): Node[] =>\n entries.map((entry) => (entry.type === \"section\" ? buildSection(entry) : buildRow(entry)));\n\n/**\n * A credits panel: a button opening a centered daisyUI modal (✕ close,\n * click-outside, Esc) listing attributions. Entries are data (`items`): loose\n * items and sections show first; `tab` entries become a daisyUI tab strip below\n * (first tab open), so an exhibition can credit its author up top and group\n * assets/technology/etc. into tabs with sectioned lists. Defaults to a plain\n * glyph icon; pass `icon` (e.g. `lucideIcon`).\n */\nexport function creditsPanel(options: CreditsPanelOptions): OverlayComponent {\n const icon = options.icon ?? \"ⓘ\";\n\n return (ctx) => {\n const tabsName = `sg-credits-tabs-${panelCount++}`;\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-circle btn-sm\";\n setIcon(button, icon);\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n close.setAttribute(\"aria-label\", \"Close\");\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-3\";\n\n // Loose items/sections on top; the tab strip below them.\n const loose = document.createElement(\"div\");\n loose.className = \"flex flex-col gap-1\";\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n tablist.className = \"tabs tabs-border mt-2\";\n\n box.append(close, heading, loose, tablist);\n\n // Click-outside-to-close backdrop (daisyUI pattern for native <dialog>).\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const render = (entries: CreditEntry[]): void => {\n const flat = entries.filter((e): e is CreditItem | CreditSection => e.type !== \"tab\");\n const tabs = entries.filter((e): e is CreditTab => e.type === \"tab\");\n\n loose.replaceChildren(...buildEntries(flat));\n loose.style.display = flat.length ? \"\" : \"none\";\n\n const nodes: Node[] = [];\n tabs.forEach((tab, i) => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = tabsName;\n input.role = \"tab\";\n input.className = \"tab\";\n input.setAttribute(\"aria-label\", tab.label);\n if (i === 0) input.checked = true;\n const pane = document.createElement(\"div\");\n pane.className = \"tab-content pt-3\";\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-1\";\n list.replaceChildren(...buildEntries(tab.children));\n pane.appendChild(list);\n nodes.push(input, pane);\n });\n tablist.replaceChildren(...nodes);\n tablist.style.display = tabs.length ? \"\" : \"none\";\n };\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title ?? \"Credits\");\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n render(typeof options.items === \"function\" ? options.items() : options.items);\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy() {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n unbind();\n },\n };\n };\n}\n","import type { OverlayComponent, OverlayContext } from \"@scenography/overlay\";\nimport { setIcon, type IconContent } from \"../../util\";\n\nexport interface ActionButtonOptions {\n /** Click handler; receives the overlay context (gallery, surface, root). */\n onClick: (ctx: OverlayContext) => void;\n /** Icon content: a glyph, an SVG node, or a factory. No lucide required. */\n icon?: IconContent;\n /** Text label, placed after the icon. */\n label?: string;\n /** Accessible label (use it when there's an icon but no text). */\n ariaLabel?: string;\n /** Override the daisyUI/utility classes. */\n className?: string;\n}\n\n/**\n * A generic clickable button — the lucide-free alternative to the opinionated\n * prefabs. Supply any `icon` (glyph, your own SVG, a Lucide icon you wired up)\n * and an `onClick`. daisyUI prefab (styled with `btn`).\n */\nexport function actionButton(options: ActionButtonOptions): OverlayComponent {\n return (ctx) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = options.className ?? \"btn btn-sm\";\n\n if (options.icon !== undefined) setIcon(button, options.icon);\n if (options.label !== undefined) {\n const label = document.createElement(\"span\");\n label.textContent = options.label;\n button.appendChild(label);\n }\n if (options.ariaLabel) button.setAttribute(\"aria-label\", options.ariaLabel);\n\n const onClick = (): void => options.onClick(ctx);\n button.addEventListener(\"click\", onClick);\n\n return {\n element: button,\n destroy: () => button.removeEventListener(\"click\", onClick),\n };\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\n\nexport interface InfoPanelOptions {\n /** Modal title, also the ⓘ button's aria-label. */\n title: Text;\n /** Body text shown in the modal (e.g. the control instructions). */\n text: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n}\n\n/**\n * An info panel: an ⓘ button that opens a centered daisyUI modal with a title\n * and a block of text (with an ✕ close, click-outside and Esc). Content is data,\n * so the app supplies the (already localized) strings. daisyUI + Lucide prefab.\n */\nexport function infoPanel(options: InfoPanelOptions): OverlayComponent {\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"Info\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-3\";\n\n const text = document.createElement(\"p\");\n text.className = \"text-sm leading-relaxed\";\n\n box.append(close, heading, text);\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n text.textContent = resolveText(options.text);\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n unbind();\n },\n };\n };\n}\n","/**\n * Built-in control-scheme diagrams for the help panel. Standard schemes, so they\n * live in the preset (the app only supplies the localized text). Cardboard is a\n * single diagram; touch, keyboard-mouse and gamepad are split into one figure per\n * control (see `sectionFigures`). Pure pictograms: no words inside the SVG (only\n * universal key legends like WASD), so nothing here needs localizing. Outlines use\n * `currentColor`; the interactive/highlighted parts are wrapped in a `text-primary`\n * group so they pop and stay theme-aware in light and dark.\n */\n\n/** Which built-in diagram a help tab shows. */\nexport type HelpTabKind = \"touch\" | \"vr\" | \"keyboard-mouse\" | \"gamepad\" | \"cardboard\";\n\n/** Tabs whose pane is a single diagram. Touch, VR, keyboard-mouse and gamepad are\n * split into per-control sections instead (see `sectionFigures`). */\ntype SimpleTabKind = Exclude<HelpTabKind, \"touch\" | \"vr\" | \"keyboard-mouse\" | \"gamepad\">;\n\nconst CLOSE = `</svg>`;\n\n// Cardboard: a VR headset (CC0, https://www.svgrepo.com/svg/.../cardboard-vr),\n// a filled pictogram with the two lenses cut out.\nconst CARDBOARD =\n `<svg viewBox=\"0 0 24 24\" width=\"100%\" height=\"100%\" fill=\"currentColor\" ` +\n `role=\"img\" aria-hidden=\"true\">` +\n `<path d=\"M9.26402604,18 C8.79719433,18.6226198 8.05922695,19 7.26393202,19 L4.5,19` +\n ` C3.11928813,19 2,17.8807119 2,16.5 L2,9.5 C2,8.27576856 2.8799598,7.25706275 4.04188098,7.04188098` +\n ` C4.25706275,5.8799598 5.27576856,5 6.5,5 L17.5,5 C18.7242314,5 19.7429373,5.8799598 19.958119,7.04188098` +\n ` C21.1200402,7.25706275 22,8.27576856 22,9.5 L22,16.5 C22,17.8807119 20.8807119,19 19.5,19 L16.736068,19` +\n ` C15.940773,19 15.2028057,18.6226198 14.735974,18 L9.26402604,18 Z M9.82576985,17 L14.1742301,17` +\n ` C13.7685119,16.3807773 13.0746523,16 12.322949,16 L11.677051,16 C10.9253477,16 10.2314881,16.3807773 9.82576985,17 Z` +\n ` M5.08535285,7 L18.9146471,7 C18.7087289,6.41740381 18.1531094,6 17.5,6 L6.5,6 C5.84689059,6 5.29127106,6.41740381 5.08535285,7 Z` +\n ` M8.8027864,16.7763932 C9.34713196,15.6877021 10.4598573,15 11.677051,15 L12.322949,15` +\n ` C13.5401427,15 14.652868,15.6877021 15.1972136,16.7763932 L15.3944272,17.1708204` +\n ` C15.6485152,17.6789964 16.1679099,18 16.736068,18 L19.5,18 C20.3284271,18 21,17.3284271 21,16.5 L21,9.5` +\n ` C21,8.67157288 20.3284271,8 19.5,8 L4.5,8 C3.67157288,8 3,8.67157288 3,9.5 L3,16.5 C3,17.3284271 3.67157288,18 4.5,18` +\n ` L7.26393202,18 C7.83209011,18 8.35148479,17.6789964 8.60557281,17.1708204 L8.8027864,16.7763932 Z` +\n ` M7.5,15 C6.11928813,15 5,13.8807119 5,12.5 C5,11.1192881 6.11928813,10 7.5,10 C8.88071187,10 10,11.1192881 10,12.5` +\n ` C10,13.8807119 8.88071187,15 7.5,15 Z M7.5,14 C8.32842712,14 9,13.3284271 9,12.5 C9,11.6715729 8.32842712,11 7.5,11` +\n ` C6.67157288,11 6,11.6715729 6,12.5 C6,13.3284271 6.67157288,14 7.5,14 Z` +\n ` M16.5,15 C15.1192881,15 14,13.8807119 14,12.5 C14,11.1192881 15.1192881,10 16.5,10 C17.8807119,10 19,11.1192881 19,12.5` +\n ` C19,13.8807119 17.8807119,15 16.5,15 Z M16.5,14 C17.3284271,14 18,13.3284271 18,12.5 C18,11.6715729 17.3284271,11 16.5,11` +\n ` C15.6715729,11 15,11.6715729 15,12.5 C15,13.3284271 15.6715729,14 16.5,14 Z\"/>` +\n CLOSE;\n\nconst DIAGRAMS: Record<SimpleTabKind, string> = {\n cardboard: CARDBOARD,\n};\n\n/** Build the diagram figure node for a simple tab kind (a div wrapping the SVG). */\nexport function diagramNode(kind: SimpleTabKind): HTMLElement {\n const figure = document.createElement(\"div\");\n // Muted outline color; the `text-primary` groups inside override to the accent.\n figure.className = \"text-base-content/70 mx-auto mb-3 h-28 w-full max-w-xs\";\n figure.innerHTML = DIAGRAMS[kind];\n return figure;\n}\n\n/*\n * Gamepad — the real pad icon (CC0, https://www.svgrepo.com/svg/57965/gamepad),\n * unlike the other diagrams this is a *filled* pictogram, not a stroked one. Its\n * pane is split into one section per control, so instead of a single figure it\n * exposes a parametrized icon that lights up (in `text-primary`) only the part\n * used by that section: the left stick + D-pad for moving, the right stick for\n * looking, the bottom action button for selecting.\n */\ntype GamepadHighlight = \"move\" | \"look\" | \"select\";\n\nconst GP_OPEN =\n `<svg viewBox=\"0 0 512 512\" width=\"100%\" height=\"100%\" fill=\"currentColor\" ` +\n `role=\"img\" aria-hidden=\"true\">`;\n\n// Pad body (outer shell). Always drawn muted.\nconst GP_BODY =\n `<path d=\"M467.8,173.6c-8.4-50-49.7-87-100.6-90.1l-2.1-0.4c-0.7-0.1-1.4-0.2-2.1-0.2H151.5` +\n `c-53.3,0-98.4,38.1-107.2,90.4L12,346.4c-1,4.7-1.5,9.4-1.5,14.1c0,37.8,30.7,68.5,68.5,68.5` +\n `c22.8,0,44.1-11.3,56.6-29.9l15.6-21.6c4-5.5,2.7-13.1-2.8-17.1c-5.5-4-13.1-2.7-17.1,2.8l-15.8,21.9` +\n `c-8.2,12.2-21.9,19.5-36.5,19.5c-24.3,0-44-19.8-44-44c0-3,0.3-6.1,1-9.4l32.3-173.5` +\n `c6.8-40.7,41.8-70.2,83.1-70.2H362l1.7,0.3c0.5,0.1,0.9,0.1,1.4,0.2c39,2.1,72,31.5,78.5,69.9L476,351.4` +\n `c0.6,3,0.9,6.1,0.9,9.1c0,24.3-19.8,44-44,44c-14.7,0-28.3-7.3-36.8-19.8l-16.8-23.2` +\n `c-4-5.5-11.6-6.7-17.1-2.8c-5.5,4-6.7,11.6-2.8,17.1l16.5,22.9c12.8,18.9,34,30.2,56.8,30.2` +\n `c37.8,0,68.5-30.7,68.5-68.5c0-4.7-0.5-9.5-1.4-13.9L467.8,173.6z\"/>`;\n// Left analog stick (ring). Highlighted when moving.\nconst GP_LEFT_STICK =\n `<path d=\"M182.4,251.5c-28.9,0-52.5,23.5-52.5,52.5s23.5,52.5,52.5,52.5c28.9,0,52.5-23.5,52.5-52.5` +\n `S211.3,251.5,182.4,251.5z M182.4,331.9c-15.4,0-28-12.5-28-28s12.5-28,28-28c15.4,0,28,12.5,28,28` +\n `S197.8,331.9,182.4,331.9z\"/>`;\n// Right analog stick (ring). Highlighted when looking.\nconst GP_RIGHT_STICK =\n `<path d=\"M382.1,303.9c0-28.9-23.5-52.5-52.5-52.5c-28.9,0-52.5,23.5-52.5,52.5s23.5,52.5,52.5,52.5` +\n `C358.6,356.4,382.1,332.8,382.1,303.9z M329.6,331.9c-15.4,0-28-12.5-28-28s12.5-28,28-28c15.4,0,28,12.5,28,28` +\n `S345.1,331.9,329.6,331.9z\"/>`;\n// D-pad (cross). Highlighted when moving, alongside the left stick.\nconst GP_DPAD =\n `<path d=\"M159.7,132.6c-6.8,0-12.3,5.5-12.3,12.3V167h-22.1c-6.8,0-12.3,5.5-12.3,12.3s5.5,12.3,12.3,12.3h22.1v22.1` +\n `c0,6.8,5.5,12.3,12.3,12.3s12.3-5.5,12.3-12.3v-22.1h22.1c6.8,0,12.3-5.5,12.3-12.3s-5.5-12.3-12.3-12.3H172v-22.1` +\n `C172,138.1,166.5,132.6,159.7,132.6z\"/>`;\n// Action buttons (diamond). Only the bottom one is highlighted, when selecting.\nconst GP_BTN_TOP = `<circle cx=\"353.4\" cy=\"147.8\" r=\"16.6\"/>`;\nconst GP_BTN_LEFT = `<circle cx=\"319.5\" cy=\"181.8\" r=\"16.6\"/>`;\nconst GP_BTN_RIGHT = `<circle cx=\"387.4\" cy=\"181.8\" r=\"16.6\"/>`;\nconst GP_BTN_BOTTOM = `<circle cx=\"353.4\" cy=\"215.7\" r=\"16.6\"/>`;\n\n// Wrap a part so it renders in the accent color instead of the muted outline.\nconst lit = (part: string): string =>\n `<g class=\"text-primary\" fill=\"currentColor\">${part}</g>`;\n\n/** The gamepad icon with only the parts used by `highlight` lit in the accent. */\nfunction gamepadSvg(highlight: GamepadHighlight): string {\n const on = (part: string, when: GamepadHighlight): string =>\n highlight === when ? lit(part) : part;\n return (\n GP_OPEN +\n GP_BODY +\n on(GP_LEFT_STICK, \"move\") +\n on(GP_RIGHT_STICK, \"look\") +\n on(GP_DPAD, \"move\") +\n GP_BTN_TOP +\n GP_BTN_LEFT +\n GP_BTN_RIGHT +\n on(GP_BTN_BOTTOM, \"select\") +\n CLOSE\n );\n}\n\n/*\n * Touch — hand-drawn phone pictograms (CC0). The touch pane is one section per\n * step: rotate the phone to landscape, grab it with both hands, then two grips\n * lighting (in `text-primary`) the thumb that moves (left) and the thumb that\n * looks (right). Filled phone + stroked hands, both in `currentColor`.\n */\ntype PhoneStep = \"rotate\" | \"hold\" | \"move\" | \"look\";\n\n// Rotate the phone to landscape: an upright phone with a curved rotation arrow.\nconst PHONE_ROTATE =\n `<svg viewBox=\"-8.04 0 36.945037 32.134997\" width=\"100%\" height=\"100%\" ` +\n `role=\"img\" aria-hidden=\"true\">` +\n `<g fill=\"currentColor\" transform=\"translate(-614.78856,-194.333)\">` +\n `<path d=\"m 630.278,196.333 a 0.855,0.855 0 0 1 0.854,0.854 v 26.427` +\n ` a 0.855,0.855 0 0 1 -0.854,0.854 h -10.345 a 0.855,0.855 0 0 1 -0.854,-0.854` +\n ` v -26.427 a 0.855,0.855 0 0 1 0.854,-0.854 h 10.345 m 0,-2 h -10.345` +\n ` a 2.854,2.854 0 0 0 -2.854,2.854 v 26.427 a 2.854,2.854 0 0 0 2.854,2.854` +\n ` h 10.345 a 2.854,2.854 0 0 0 2.854,-2.854 v -26.427` +\n ` a 2.854,2.854 0 0 0 -2.854,-2.854 z\"/>` +\n `<path d=\"m 625.106,220.375 a 1.144,1.144 0 1 0 1.143,1.143` +\n ` 1.144,1.144 0 0 0 -1.143,-1.143 z\"/>` +\n `</g>` +\n `<path fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.1371\" ` +\n `stroke-linecap=\"round\" stroke-linejoin=\"round\" ` +\n `d=\"m 21.517396,7.653044 c 0.34046,0.09237 0.678922,0.2068044 1.0138,0.3440646` +\n ` 4.368435,1.7905494 6.458225,6.7833844 4.667677,11.1518194` +\n ` -1.013391,2.472391 -3.052499,4.214862 -5.423632,4.937107` +\n ` M -0.65235214,23.84002 C -0.99282974,23.74769 -1.3312767,23.633223 -1.6661557,23.495962` +\n ` -6.0346112,21.705405 -8.1243897,16.712597 -6.3338324,12.344142` +\n ` -5.3655121,9.981708 -3.4606611,8.2856913 -1.2244333,7.5094329` +\n ` M -2.1519003,10.593762 -0.93612324,7.6276059 -1.2244333,7.5094329` +\n ` M 24.686186,25.279184 21.77524,24.086036 m 0,0 -0.05514,-0.02261` +\n ` 1.215708,-2.966184 M -3.9022805,6.41183 -1.2244349,7.5094331\"/>` +\n `</svg>`;\n\n// A dot lighting a thumb tip in the accent color, at (cx, cy) in grip coords.\nconst thumbDot = (cx: number, cy: number): string =>\n `<g class=\"text-primary\" fill=\"currentColor\"><circle cx=\"${cx}\" cy=\"${cy}\" r=\"3\"/></g>`;\n\n// Phone gripped in both hands (landscape). `light` puts a dot on a thumb tip.\nconst grabPhone = (light?: \"move\" | \"look\"): string =>\n `<svg viewBox=\"-8.04 0 49.591388 22.890837\" width=\"100%\" height=\"100%\" ` +\n `role=\"img\" aria-hidden=\"true\">` +\n `<g fill=\"currentColor\" transform=\"rotate(-90,219.77734,413.35469)\">` +\n `<path d=\"m 633.13203,197.18696 a 2.854,2.854 0 0 0 -2.85402,-2.85394 h -8.64185` +\n ` l 1.67971,1.99997 h 6.96214 a 0.855,0.855 0 0 1 0.85398,0.85397 V 223.614` +\n ` a 0.855,0.855 0 0 1 -0.85398,0.85398 h -7.1771 l -1.67979,2.00004 h 8.85689` +\n ` a 2.854,2.854 0 0 0 2.85402,-2.85402 z m -14.05302,1.1933 -1.82611,-2.17421` +\n ` a 2.854,2.854 0 0 0 -0.17393,0.98091 V 223.614 a 2.854,2.854 0 0 0 0.11352,0.79686` +\n ` l 1.88652,-2.24615 z\"/>` +\n `</g>` +\n `<path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.73127\" ` +\n `stroke-linecap=\"round\" stroke-linejoin=\"round\" ` +\n `d=\"m -7.1743772,20.405591 6.27449192,1.61962 c 0,0 2.28297208,-1.746 3.01734048,-3.303819` +\n ` 0.6250483,-1.325918 1.2501011,-3.541343 1.2501011,-3.541343 0,0 4.9303549,-3.331849 4.990109,-4.64706` +\n ` C 8.4038939,9.51548 7.4523448,8.44669 6.489279,8.115078 5.4553799,7.759076 -0.61569418,12.732235 -0.61569418,12.732235` +\n ` L 1.7109732,10.62309 1.7633602,4.158494 c 0,0 -2.9599019,-0.1661 -5.3375374,2.108198` +\n ` -1.407702,1.34652 -3.6002,14.138899 -3.6002,14.138899 z\"/>` +\n `<path fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.73127\" ` +\n `stroke-linecap=\"round\" stroke-linejoin=\"round\" ` +\n `d=\"m 40.685765,20.076393 -6.274492,1.61962 c 0,0 -2.282972,-1.746 -3.01734,-3.303819` +\n ` -0.625049,-1.325918 -1.250101,-3.541343 -1.250101,-3.541343 0,0 -4.930355,-3.331849 -4.990109,-4.64706` +\n ` -0.04623,-1.017509 0.90532,-2.086299 1.868386,-2.417911 1.033899,-0.356002 7.104973,4.617157 7.104973,4.617157` +\n ` l -2.326667,-2.109145 -0.05239,-6.464596 c 0,0 2.959902,-0.1661 5.337537,2.108198` +\n ` 1.407702,1.34652 3.6002,14.138899 3.6002,14.138899 z\"/>` +\n (light === \"move\" ? thumbDot(8, 8) : \"\") +\n (light === \"look\" ? thumbDot(26, 8) : \"\") +\n `</svg>`;\n\nconst PHONE: Record<PhoneStep, string> = {\n rotate: PHONE_ROTATE,\n hold: grabPhone(),\n move: grabPhone(\"move\"),\n look: grabPhone(\"look\"),\n};\n\n// VR (gyro): a single move joystick — a ring with a lit knob and 4-way arrows.\n// The look half is dropped there (the gyro aims), so one stick walks you.\nconst MOVE_STICK =\n `<svg viewBox=\"0 0 100 100\" width=\"100%\" height=\"100%\" role=\"img\" aria-hidden=\"true\">` +\n `<circle cx=\"50\" cy=\"50\" r=\"30\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"6\"/>` +\n `<g class=\"text-primary\" fill=\"currentColor\"><circle cx=\"50\" cy=\"50\" r=\"12\"/></g>` +\n `<g fill=\"currentColor\">` +\n `<path d=\"M50 12 l-7 10 h14 z\"/>` +\n `<path d=\"M50 88 l-7 -10 h14 z\"/>` +\n `<path d=\"M12 50 l10 -7 v14 z\"/>` +\n `<path d=\"M88 50 l-10 -7 v14 z\"/>` +\n `</g>` +\n CLOSE;\n\n/*\n * Keyboard + mouse. Keys: the WASD block (awsd-keys.svg) and the arrow-key block\n * (arrows-keys.svg) are the same 4-key cross, so we overlay them in one space —\n * WASD slightly up-left, arrows slightly down-right — to say \"either works\". Shift\n * and Q/E have no icon (Shift stays plain text). Mouse: the pad icon\n * (mouse-alt-svgrepo-com.svg) with the button used by a section lit in the accent.\n */\nconst KEY = 152; // side of a key, in the source SVG's units\nconst KEY_HALF = KEY / 2;\n// Key centers of the cross, in the source SVG's coordinate space.\nconst KEY_UP: [number, number] = [262, 82];\nconst KEY_LEFT: [number, number] = [82, 259];\nconst KEY_DOWN: [number, number] = [262, 259];\nconst KEY_RIGHT: [number, number] = [442, 259];\n\nconst keyRect = ([cx, cy]: [number, number]): string =>\n `<rect x=\"${cx - KEY_HALF}\" y=\"${cy - KEY_HALF}\" width=\"${KEY}\" height=\"${KEY}\" ` +\n `rx=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"11\"/>`;\n\nconst keyLetter = ([cx, cy]: [number, number], ch: string): string =>\n `<text x=\"${cx}\" y=\"${cy}\" font-family=\"system-ui, sans-serif\" font-weight=\"800\" ` +\n `font-size=\"96\" text-anchor=\"middle\" dominant-baseline=\"central\" ` +\n `fill=\"currentColor\" stroke=\"none\">${ch}</text>`;\n\n// An arrow filling a key, pointing `dir` (shaft + chevron head).\nconst keyArrow = (\n [cx, cy]: [number, number],\n dir: \"up\" | \"down\" | \"left\" | \"right\",\n): string => {\n const L = 42; // half shaft length\n const H = 20; // chevron half-size\n const line = (d: string): string =>\n `<path d=\"${d}\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"11\" ` +\n `stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`;\n switch (dir) {\n case \"up\":\n return line(`M${cx} ${cy + L} V${cy - L}`) +\n line(`M${cx - H} ${cy - L + H} L${cx} ${cy - L} L${cx + H} ${cy - L + H}`);\n case \"down\":\n return line(`M${cx} ${cy - L} V${cy + L}`) +\n line(`M${cx - H} ${cy + L - H} L${cx} ${cy + L} L${cx + H} ${cy + L - H}`);\n case \"left\":\n return line(`M${cx + L} ${cy} H${cx - L}`) +\n line(`M${cx - L + H} ${cy - H} L${cx - L} ${cy} L${cx - L + H} ${cy + H}`);\n default: // right\n return line(`M${cx - L} ${cy} H${cx + L}`) +\n line(`M${cx + L - H} ${cy - H} L${cx + L} ${cy} L${cx + L - H} ${cy + H}`);\n }\n};\n\nconst AWSD_LAYER =\n keyRect(KEY_UP) + keyLetter(KEY_UP, \"W\") +\n keyRect(KEY_LEFT) + keyLetter(KEY_LEFT, \"A\") +\n keyRect(KEY_DOWN) + keyLetter(KEY_DOWN, \"S\") +\n keyRect(KEY_RIGHT) + keyLetter(KEY_RIGHT, \"D\");\nconst ARROWS_LAYER =\n keyRect(KEY_UP) + keyArrow(KEY_UP, \"up\") +\n keyRect(KEY_LEFT) + keyArrow(KEY_LEFT, \"left\") +\n keyRect(KEY_DOWN) + keyArrow(KEY_DOWN, \"down\") +\n keyRect(KEY_RIGHT) + keyArrow(KEY_RIGHT, \"right\");\n\n// How far the arrow block sits right of / below the WASD one.\nconst KEYS_OFFSET_X = 0;\nconst KEYS_OFFSET_Y = 400;\nconst KEYS =\n `<svg viewBox=\"0 0 ${524 + KEYS_OFFSET_X} ${341 + KEYS_OFFSET_Y}\" width=\"100%\" ` +\n `height=\"100%\" role=\"img\" aria-hidden=\"true\">` +\n `<g transform=\"translate(${KEYS_OFFSET_X} ${KEYS_OFFSET_Y})\">${ARROWS_LAYER}</g>` +\n `<g>${AWSD_LAYER}</g>` +\n CLOSE;\n\n// The mouse silhouette (evenodd → a line-art outline) and its two buttons.\nconst MOUSE_BODY =\n `M8.5 0c-3.033 0-5.5 2.467-5.5 5.5v6c0 3.033 2.467 5.5 5.5 5.5s5.5-2.467 5.5-5.5` +\n `v-6c0-3.033-2.467-5.5-5.5-5.5zM13 5.5v0.5h-4v-4.949c2.244 0.252 4 2.139 4 4.449z` +\n `M8 1.051v4.949h-4v-0.5c0-2.31 1.756-4.197 4-4.449zM8.5 16c-2.481 0-4.5-2.019-4.5-4.5` +\n `v-4.5h9v4.5c0 2.481-2.019 4.5-4.5 4.5z`;\nconst MOUSE_LEFT_BTN = `M8 1.051v4.949h-4v-0.5c0-2.31 1.756-4.197 4-4.449z`;\nconst MOUSE_RIGHT_BTN = `M13 5.5v0.5h-4v-4.949c2.244 0.252 4 2.139 4 4.449z`;\n\n// The mouse icon, with the `light` button filled in the accent behind the outline.\nconst mouse = (light?: \"left\" | \"right\"): string =>\n `<svg viewBox=\"0 0 17 17\" width=\"100%\" height=\"100%\" role=\"img\" aria-hidden=\"true\">` +\n (light === \"left\"\n ? `<g class=\"text-primary\" fill=\"currentColor\"><path d=\"${MOUSE_LEFT_BTN}\"/></g>`\n : \"\") +\n (light === \"right\"\n ? `<g class=\"text-primary\" fill=\"currentColor\"><path d=\"${MOUSE_RIGHT_BTN}\"/></g>`\n : \"\") +\n `<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"${MOUSE_BODY}\"/>` +\n CLOSE;\n\n// A small square figure holding one inline SVG, muted so `text-primary` pops.\nfunction figureFrom(svg: string): HTMLElement {\n const figure = document.createElement(\"div\");\n figure.className = \"text-base-content/70 h-16 w-16 shrink-0\";\n figure.innerHTML = svg;\n return figure;\n}\n\n/**\n * Icon factories for a tab whose pane is split into per-control sections, in the\n * order its lines document them; `null` for single-diagram tabs. Each factory\n * makes a fresh node (a DOM node can only live in one place).\n */\nexport function sectionFigures(kind: HelpTabKind): Array<() => HTMLElement> | null {\n switch (kind) {\n case \"touch\":\n return ([\"rotate\", \"hold\", \"move\", \"look\"] as const).map(\n (step) => () => figureFrom(PHONE[step]),\n );\n case \"vr\":\n // Move the phone to look (the rotate/orient icon), one stick to walk.\n return [() => figureFrom(PHONE.rotate), () => figureFrom(MOVE_STICK)];\n case \"keyboard-mouse\":\n return [\n () => figureFrom(KEYS),\n () => figureFrom(mouse(\"left\")),\n () => figureFrom(mouse(\"right\")),\n ];\n case \"gamepad\":\n return ([\"move\", \"look\", \"select\"] as const).map(\n (h) => () => figureFrom(gamepadSvg(h)),\n );\n default:\n return null;\n }\n}\n","import type { HelpTabKind } from \"./diagrams\";\n\n/**\n * Tracks the last input modality the visitor used, so the help panel can\n * pre-select a sensible tab. It is a heuristic on purpose: a touchscreen laptop\n * is both touch and mouse, so this follows the *last-used* input, seeded from the\n * device's primary pointer. Cardboard isn't tracked here — it's an explicit mode\n * the panel overrides with separately.\n */\nexport type Modality = \"touch\" | \"keyboard-mouse\" | \"gamepad\";\n\n/** Map a modality to the help tab that documents it. */\nexport function modalityTab(modality: Modality): HelpTabKind {\n return modality; // the names line up (touch / keyboard-mouse / gamepad)\n}\n\nexport interface ModalityTracker {\n /** The last-used modality. */\n get(): Modality;\n /** Stop listening. */\n destroy(): void;\n}\n\nexport function createModalityTracker(): ModalityTracker {\n const coarse =\n typeof matchMedia !== \"undefined\" && matchMedia(\"(pointer: coarse)\").matches;\n let last: Modality = coarse ? \"touch\" : \"keyboard-mouse\";\n\n const onTouch = (): void => {\n last = \"touch\";\n };\n // Touch also synthesizes pointer events; ignore those so a tap doesn't read as mouse.\n const onPointer = (e: Event): void => {\n if ((e as PointerEvent).pointerType === \"touch\") return;\n last = \"keyboard-mouse\";\n };\n const onKey = (): void => {\n last = \"keyboard-mouse\";\n };\n const onPad = (): void => {\n last = \"gamepad\";\n };\n\n window.addEventListener(\"touchstart\", onTouch, { passive: true });\n window.addEventListener(\"pointerdown\", onPointer, { passive: true });\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"gamepadconnected\", onPad);\n\n return {\n get: () => last,\n destroy: () => {\n window.removeEventListener(\"touchstart\", onTouch);\n window.removeEventListener(\"pointerdown\", onPointer);\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"gamepadconnected\", onPad);\n },\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\nimport { diagramNode, sectionFigures, type HelpTabKind } from \"./diagrams\";\nimport { createModalityTracker, modalityTab } from \"./modality\";\n\n/** One help tab: a built-in control diagram plus localized instruction lines. */\nexport interface HelpTab {\n /** Which input mode this tab documents (picks the built-in diagram). */\n kind: HelpTabKind;\n /** Tab title (short). */\n title: Text;\n /** Instruction lines shown under the diagram. */\n lines: Text[];\n /** Render the lines as a numbered list (e.g. Cardboard setup steps). */\n ordered?: boolean;\n}\n\nexport interface HelpPanelOptions {\n /** Modal title, also the ⓘ button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /** The tabs to show, in order. */\n tabs: HelpTab[];\n /**\n * On open, pre-select the tab for the current mode: the active view mode when\n * it's VR or Cardboard (an explicit gyro mode), else the last-used input\n * modality (touch / keyboard-mouse / gamepad). Defaults to `true`. The visitor\n * can still switch tabs freely. When no match is present, the first tab stays\n * selected.\n */\n autoSelect?: boolean;\n}\n\n/** A relocalizable text element paired with its source `Text`. */\ninterface LineEl {\n el: HTMLElement;\n line: Text;\n}\n\n/**\n * Build a help tab's pane body and return the text nodes to relocalize. Most tabs\n * are one diagram above a bullet list. Touch and gamepad are split into sections:\n * one icon per control (with just that part lit) next to its instruction, so the\n * visitor sees which part does what; any extra lines (e.g. \"connect a controller\")\n * stay as notes below.\n */\nfunction buildPane(pane: HTMLElement, tab: HelpTab): LineEl[] {\n const figures = sectionFigures(tab.kind);\n if (figures) return buildSectionedPane(pane, tab.lines, figures);\n\n // Not sectioned → a single-diagram kind (cardboard).\n pane.appendChild(diagramNode(tab.kind as \"cardboard\"));\n const list = document.createElement(tab.ordered ? \"ol\" : \"ul\");\n list.className = `${tab.ordered ? \"list-decimal\" : \"list-disc\"} list-outside pl-5 space-y-1.5 text-sm leading-relaxed`;\n const lineEls = tab.lines.map((line) => {\n const el = document.createElement(\"li\");\n list.appendChild(el);\n return { el, line };\n });\n pane.appendChild(list);\n return lineEls;\n}\n\nfunction buildSectionedPane(\n pane: HTMLElement,\n lines: Text[],\n figures: Array<() => HTMLElement>,\n): LineEl[] {\n const lineEls: LineEl[] = [];\n figures.forEach((makeFigure, i) => {\n const line = lines[i];\n if (!line) return;\n const row = document.createElement(\"div\");\n row.className = \"flex items-center gap-4 mb-3\";\n row.appendChild(makeFigure());\n const p = document.createElement(\"p\");\n p.className = \"text-sm leading-relaxed\";\n row.appendChild(p);\n pane.appendChild(row);\n lineEls.push({ el: p, line });\n });\n const notes = lines.slice(figures.length);\n if (notes.length > 0) {\n const list = document.createElement(\"ul\");\n list.className =\n \"list-disc list-outside pl-5 space-y-1.5 text-sm leading-relaxed text-base-content/70\";\n for (const line of notes) {\n const el = document.createElement(\"li\");\n list.appendChild(el);\n lineEls.push({ el, line });\n }\n pane.appendChild(list);\n }\n return lineEls;\n}\n\nlet instanceCount = 0;\n\n/**\n * A visual controls-help panel: an ⓘ button opening a centered daisyUI modal\n * with one tab per input mode, each showing an SVG diagram and a few localized\n * lines. On open it pre-selects the tab for the mode you're using. Diagrams are\n * built in (standard schemes); the text is app-supplied `Text`, relocalized live.\n */\nexport function helpPanel(options: HelpPanelOptions): OverlayComponent {\n const autoSelect = options.autoSelect ?? true;\n const groupName = `sg-help-tabs-${(instanceCount += 1)}`;\n\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"Info\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-md\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n box.appendChild(close);\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n box.appendChild(heading);\n\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n tablist.className = \"tabs tabs-border\";\n box.appendChild(tablist);\n\n // Re-run on every locale change to relocalize all text.\n const relabels: Array<() => void> = [];\n const tabInputs = new Map<HelpTabKind, HTMLInputElement>();\n let firstInput: HTMLInputElement | null = null;\n\n for (const tab of options.tabs) {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = groupName;\n input.role = \"tab\";\n input.className = \"tab\";\n if (!firstInput) {\n input.checked = true;\n firstInput = input;\n }\n\n const pane = document.createElement(\"div\");\n pane.className = \"tab-content pt-4\";\n const lineEls = buildPane(pane, tab);\n\n tablist.append(input, pane);\n tabInputs.set(tab.kind, input);\n relabels.push(() => {\n input.setAttribute(\"aria-label\", resolveText(tab.title));\n for (const { el, line } of lineEls) el.textContent = resolveText(line);\n });\n }\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const tracker = createModalityTracker();\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => {\n if (autoSelect) {\n // Prefer the active view mode's tab (VR / Cardboard are explicit gyro modes\n // the input tracker can't know about); otherwise fall back to the last-used\n // input modality (touch / keyboard-mouse / gamepad — e.g. on desktop).\n const mode = ctx.gallery.ctx.viewControls.getMode();\n const kind: HelpTabKind =\n mode === \"cardboard\" ? \"cardboard\" : mode === \"vr\" ? \"vr\" : modalityTab(tracker.get());\n const target = tabInputs.get(kind) ?? firstInput;\n if (target) target.checked = true;\n }\n dialog.showModal();\n };\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n tracker.destroy();\n unbind();\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\nimport type { Artwork } from \"@scenography/plugin-artwork/data\";\n\nexport interface ArtworkInfoOptions {\n /** The artworks' data, looked up by `id` on select. Usually `scene.artworks`. */\n artworks: Artwork[];\n /** Aria-label for the panel's ✕ close button. Defaults to \"Close\". */\n closeLabel?: string;\n /**\n * Availability wording shown in the panel. `'for-sale'` reads `forSale`; both\n * negative states (`'sold'` / `'not-for-sale'`) read `privateCollection`, since\n * publicly they mean the same: not available. Defaults to English.\n */\n availabilityText?: { forSale?: string; privateCollection?: string };\n /** Label for the link shown when an artwork has an `href`. Defaults to \"View artwork\". */\n linkText?: string;\n /** Label/aria for the \"previous piece\" button. Defaults to \"Previous\". */\n prevLabel?: string;\n /** Label/aria for the \"next piece\" button. Defaults to \"Next\". */\n nextLabel?: string;\n /**\n * Minimum camera distance from the piece (world units) enforced when the panel\n * closes. If the centered framing would be closer, the camera pulls back to\n * this so a small piece doesn't leave you pinned to the wall. Default `0`.\n */\n minCloseDistance?: number;\n}\n\nconst AVAILABILITY_COLOR = { available: \"#22c55e\", unavailable: \"#ef4444\" } as const;\n\nconst STYLE_ID = \"scenography-artwork-info-styles\";\n// Panel occupies the left 40%; the piece is framed in the right 60% (centered at 0.7).\nconst PANEL_FRACTION = 0.4;\nconst FREE_FRACTION = 1 - PANEL_FRACTION;\nconst CENTER_X = PANEL_FRACTION + FREE_FRACTION / 2;\n\n/**\n * Inject the panel's own CSS once. Self-contained on purpose: a consumer's\n * Tailwind build doesn't scan this package in `node_modules`, so utility classes\n * wouldn't be generated — the panel must not depend on them.\n */\nfunction ensureStyles(): void {\n if (typeof document === \"undefined\" || document.getElementById(STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent =\n // z-index above the touch-joystick layer (z-index:5) so it sits on top and,\n // marked as an overlay item, the joysticks won't steal its touches.\n `.sg-artwork-panel{position:absolute;left:0;top:0;height:100%;width:${PANEL_FRACTION * 100}%;` +\n `box-sizing:border-box;background:rgba(0,0,0,0.85);color:#fff;padding:2.5rem;z-index:10;` +\n `overflow-y:auto;display:none;flex-direction:column;gap:0.75rem;pointer-events:auto;` +\n `font-family:system-ui,sans-serif;}` +\n `.sg-artwork-panel--open{display:flex;}` +\n `.sg-artwork-panel__close{align-self:flex-end;background:none;border:none;color:#fff;` +\n `font-size:1.5rem;line-height:1;cursor:pointer;opacity:0.7;padding:0;}` +\n `.sg-artwork-panel__close:hover{opacity:1;}` +\n `.sg-artwork-panel__title{font-size:1.75rem;font-weight:700;margin:0;}` +\n `.sg-artwork-panel__meta{display:flex;flex-direction:column;gap:0.15rem;font-size:0.9rem;opacity:0.8;}` +\n `.sg-artwork-panel__status{display:flex;align-items:center;gap:0.5rem;font-size:0.9rem;font-weight:600;}` +\n `.sg-artwork-panel__dot{width:0.7rem;height:0.7rem;border-radius:50%;flex:none;}` +\n `.sg-artwork-panel__desc{font-size:1rem;line-height:1.6;margin-top:0.5rem;white-space:pre-line;}` +\n `.sg-artwork-panel__link{align-self:flex-start;margin-top:0.5rem;display:inline-flex;` +\n `align-items:center;gap:0.4rem;color:#fff;text-decoration:underline;font-size:0.95rem;}` +\n `.sg-artwork-panel__link:hover{opacity:0.8;}` +\n // Prev/next navigation, pinned to the bottom of the panel (margin-top:auto).\n `.sg-artwork-panel__nav{display:flex;gap:0.75rem;margin-top:auto;padding-top:1.5rem;}` +\n `.sg-artwork-panel__navbtn{flex:1;display:inline-flex;align-items:center;justify-content:center;` +\n `gap:0.4rem;background:rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.25);color:#fff;` +\n `border-radius:0.5rem;padding:0.6rem 0.8rem;font-size:0.95rem;cursor:pointer;font-family:inherit;}` +\n `.sg-artwork-panel__navbtn:hover{background:rgba(255,255,255,0.2);}`;\n document.head.appendChild(style);\n}\n\nconst join = (parts: (string | undefined)[], sep: string): string =>\n parts.filter((p): p is string => Boolean(p)).join(sep);\n\n/**\n * The artwork info panel. The engine emits `artwork:select { id }` when you click\n * a piece's cartela (or the piece itself); this opens a left-hand panel (40%\n * width, full height, black at 85% alpha) with the full caption and asks the\n * camera to frame the piece in the free right 60%. Prev/next buttons at the\n * bottom cycle through the pieces (wrapping) without leaving the panel. Closing\n * with ✕ re-centers the piece full-width, pulling back to `minCloseDistance` when\n * the framing would otherwise be too close; if the user walks/turns away the\n * engine emits `artwork:released` and the panel just closes (no camera move).\n *\n * Content is data (`artworks`), unlocalized for now. The panel is mounted on the\n * surface (escaping the overlay grid); the slot element is an inert placeholder.\n */\nexport function artworkInfo(options: ArtworkInfoOptions): OverlayComponent {\n const list = options.artworks;\n const byId = new Map(list.map((a) => [a.id, a]));\n const indexOf = new Map(list.map((a, i) => [a.id, i]));\n\n return (ctx) => {\n ensureStyles();\n const camera = ctx.gallery.ctx.camera;\n\n const panel = document.createElement(\"div\");\n // `sg-overlay__item` marks it as an interactive overlay control so the touch\n // joysticks (which read window touches) don't steal taps/scroll over it.\n panel.className = \"sg-artwork-panel sg-overlay__item\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"sg-artwork-panel__close\";\n close.textContent = \"✕\";\n close.setAttribute(\"aria-label\", options.closeLabel ?? \"Close\");\n\n const title = document.createElement(\"h2\");\n title.className = \"sg-artwork-panel__title\";\n const meta = document.createElement(\"div\");\n meta.className = \"sg-artwork-panel__meta\";\n\n // Availability badge: a colored dot + a derived label (the dot matches the\n // physical cartela; the text spells out \"Private collection\" when not for sale).\n const status = document.createElement(\"div\");\n status.className = \"sg-artwork-panel__status\";\n const statusDot = document.createElement(\"span\");\n statusDot.className = \"sg-artwork-panel__dot\";\n const statusText = document.createElement(\"span\");\n status.append(statusDot, statusText);\n\n const desc = document.createElement(\"p\");\n desc.className = \"sg-artwork-panel__desc\";\n\n // Optional link to the piece's page (catalog / marketplace), opens a new tab.\n const link = document.createElement(\"a\");\n link.className = \"sg-artwork-panel__link\";\n link.target = \"_blank\";\n link.rel = \"noopener noreferrer\";\n link.textContent = `${options.linkText ?? \"View artwork\"} ↗`;\n\n // Prev/next navigation to cycle through the pieces without leaving the panel.\n // Hidden when there's nothing to cycle through (0 or 1 piece).\n const nav = document.createElement(\"div\");\n nav.className = \"sg-artwork-panel__nav\";\n const prevBtn = document.createElement(\"button\");\n prevBtn.type = \"button\";\n prevBtn.className = \"sg-artwork-panel__navbtn\";\n const prevText = options.prevLabel ?? \"Previous\";\n prevBtn.textContent = `‹ ${prevText}`;\n prevBtn.setAttribute(\"aria-label\", prevText);\n const nextBtn = document.createElement(\"button\");\n nextBtn.type = \"button\";\n nextBtn.className = \"sg-artwork-panel__navbtn\";\n const nextText = options.nextLabel ?? \"Next\";\n nextBtn.textContent = `${nextText} ›`;\n nextBtn.setAttribute(\"aria-label\", nextText);\n nav.append(prevBtn, nextBtn);\n if (list.length <= 1) nav.style.display = \"none\";\n\n panel.append(close, title, meta, status, desc, link, nav);\n ctx.surface.appendChild(panel);\n\n let currentId: string | null = null;\n\n const render = (artwork: Artwork): void => {\n title.textContent = artwork.title ?? \"\";\n const lines = [\n join([artwork.author, artwork.year?.toString()], \", \"),\n join([artwork.technique, artwork.dimensions], \", \"),\n ].filter(Boolean);\n meta.replaceChildren(\n ...lines.map((line) => {\n const span = document.createElement(\"span\");\n span.textContent = line;\n return span;\n }),\n );\n\n // Availability: for-sale shows green + \"For sale\"; both negative states\n // collapse to red + \"Private collection\" (publicly identical: not for sale).\n const av = artwork.availability;\n if (av === undefined) {\n status.style.display = \"none\";\n } else {\n status.style.display = \"flex\";\n const forSale = av === \"for-sale\";\n statusDot.style.backgroundColor = forSale\n ? AVAILABILITY_COLOR.available\n : AVAILABILITY_COLOR.unavailable;\n statusText.textContent = forSale\n ? (options.availabilityText?.forSale ?? \"For sale\")\n : (options.availabilityText?.privateCollection ?? \"Private collection\");\n }\n\n desc.textContent = artwork.description ?? \"\";\n\n if (artwork.href) {\n link.href = artwork.href;\n link.style.display = \"inline-flex\";\n } else {\n link.style.display = \"none\";\n }\n };\n\n const hide = (): void => {\n panel.classList.remove(\"sg-artwork-panel--open\");\n currentId = null;\n };\n\n // Open (or switch to) a piece: render its caption and frame it in the free\n // area. Shared by clicking a piece and by the prev/next buttons.\n const show = (id: string): void => {\n const artwork = byId.get(id);\n if (!artwork) return;\n currentId = id;\n render(artwork);\n panel.scrollTop = 0;\n panel.classList.add(\"sg-artwork-panel--open\");\n camera.focusArtwork(id, { centerX: CENTER_X, widthFraction: FREE_FRACTION });\n };\n\n // Step to the piece `delta` places away, wrapping around (cyclic).\n const step = (delta: number): void => {\n if (currentId === null || list.length <= 1) return;\n const i = indexOf.get(currentId);\n if (i === undefined) return;\n const next = list[(i + delta + list.length) % list.length];\n if (next) show(next.id);\n };\n\n const onSelect = (payload?: unknown): void => {\n const id = (payload as { id?: string } | undefined)?.id;\n if (id) show(id);\n };\n\n const onReleased = (payload?: unknown): void => {\n const id = (payload as { id?: string } | undefined)?.id;\n if (id && id === currentId) hide();\n };\n\n const onClose = (): void => {\n const id = currentId;\n hide();\n // Re-center the piece; pull back to the minimum distance when framing it\n // would otherwise leave the camera too close (e.g. against the wall).\n if (id) camera.centerArtwork(id, { minDistance: options.minCloseDistance ?? 0 });\n };\n // Going home closes the panel without a camera move of its own (reset\n // already tweens the camera back to the start pose).\n const onReset = (): void => hide();\n const onPrev = (): void => step(-1);\n const onNext = (): void => step(1);\n\n const offSelect = ctx.gallery.on(\"artwork:select\", onSelect);\n const offReleased = ctx.gallery.on(\"artwork:released\", onReleased);\n const offReset = ctx.gallery.on(\"camera:reset\", onReset);\n close.addEventListener(\"click\", onClose);\n prevBtn.addEventListener(\"click\", onPrev);\n nextBtn.addEventListener(\"click\", onNext);\n\n // Inert placeholder for the slot; the real panel lives on the surface.\n const placeholder = document.createElement(\"div\");\n placeholder.style.display = \"none\";\n\n return {\n element: placeholder,\n destroy() {\n offSelect();\n offReleased();\n offReset();\n close.removeEventListener(\"click\", onClose);\n prevBtn.removeEventListener(\"click\", onPrev);\n nextBtn.removeEventListener(\"click\", onNext);\n panel.remove();\n },\n };\n };\n}\n","import type { OverlayComponent } from \"@scenography/overlay\";\n\nexport interface WatermarkOptions {\n /** Text shown. Defaults to \"scenography\". */\n text?: string;\n /** Replace the default styling with your own class. */\n className?: string;\n}\n\n/**\n * A small, unobtrusive text watermark (a \"brand stamp\"). No daisyUI/Lucide — just\n * a styled element. Defaults to the text \"scenography\".\n */\nexport function watermark(options: WatermarkOptions = {}): OverlayComponent {\n return () => {\n const el = document.createElement(\"div\");\n el.textContent = options.text ?? \"scenography\";\n if (options.className) el.className = options.className;\n else el.style.cssText = \"font:600 13px system-ui,sans-serif;color:#fff;opacity:0.6;\";\n return el;\n };\n}\n","import { resolveText, type OverlayComponent, type Text } from \"@scenography/overlay\";\nimport type { ControlFlags } from \"@scenography/engine\";\n\nimport { lucideIcon } from \"../../lucide\";\nimport { bindLocale, setIcon } from \"../../util\";\n\n/** One option in a single-choice section. */\nexport interface SettingsChoice {\n value: string;\n label: Text;\n}\n\n/** One control-flag toggle in the Controls section (wired to `ctx.viewControls`). */\nexport interface SettingsControlItem {\n /** Which control flag this toggles (e.g. `gyro`, `invertLookX`). */\n key: keyof ControlFlags;\n label: Text;\n}\n\n/** One boolean toggle in the multi-toggle section. */\nexport interface SettingsToggleItem {\n key: string;\n label: Text;\n value: boolean;\n}\n\n/**\n * One single-choice control rendered as a joined row of radio-buttons — a\n * segmented control (e.g. antialias level, bloom strength).\n */\nexport interface SettingsChoiceGroup {\n key: string;\n label: Text;\n /** Current value (matches one option's `value`). */\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n}\n\n/** One numeric slider (range) in the graphics section. */\nexport interface SettingsSliderItem {\n key: string;\n label: Text;\n value: number;\n min: number;\n max: number;\n /** Step granularity. Defaults to `0.1`. */\n step?: number;\n onChange: (value: number) => void;\n}\n\nexport interface SettingsPanelOptions {\n /** Modal title, also the gear button's aria-label. */\n title: Text;\n /** Aria-label for the ✕ close button. Defaults to \"Close\". */\n closeLabel?: Text;\n /**\n * A single-choice section rendered as a `<select>` (e.g. language). Shown in\n * its own tab; `tab` is the tab's title (falls back to `label`).\n */\n language?: {\n tab?: Text;\n label: Text;\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n };\n /** A single-choice section rendered as radios (e.g. handedness). Own tab. */\n handedness?: {\n tab?: Text;\n label: Text;\n value: string;\n options: SettingsChoice[];\n onChange: (value: string) => void;\n };\n /**\n * The Controls section: toggles for the camera-control flags (gyroscope, split\n * screen, virtual joysticks, invert look X/Y). Unlike the other sections this\n * one is *self-wired* to `ctx.viewControls` — pass which flags to show and their\n * labels; the panel reads/writes the live state and stays in sync with the mode\n * picker. Own tab.\n */\n controls?: {\n tab?: Text;\n label: Text;\n items: SettingsControlItem[];\n };\n /** A multi-toggle section (e.g. graphics: ssao / bloom / shadows). Own tab. */\n graphics?: {\n tab?: Text;\n label: Text;\n items: SettingsToggleItem[];\n onChange: (key: string, value: boolean) => void;\n /** Optional segmented radio-button groups above the toggles (e.g. antialias / bloom). */\n choiceGroups?: SettingsChoiceGroup[];\n /** Optional numeric sliders shown below the toggles (e.g. eye separation). */\n sliders?: SettingsSliderItem[];\n };\n}\n\n/**\n * A settings panel: a ⚙ button that opens a centered daisyUI modal (with an ✕\n * close, click-outside and Esc). Each section lives in its own tab (the first\n * present one opens by default). Sections are data — the app supplies labels,\n * current values and callbacks — so the panel stays generic. Labels are `Text`\n * and relocalize live via the overlay's locale source. daisyUI + Lucide prefab.\n */\nexport function settingsPanel(options: SettingsPanelOptions): OverlayComponent {\n return (ctx) => {\n const wrap = document.createElement(\"div\");\n\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"btn btn-circle btn-sm\";\n setIcon(button, lucideIcon(\"Settings\"));\n\n const dialog = document.createElement(\"dialog\");\n dialog.className = \"modal\";\n const box = document.createElement(\"div\");\n box.className = \"modal-box max-w-sm\";\n\n const close = document.createElement(\"button\");\n close.type = \"button\";\n close.className = \"btn btn-sm btn-circle btn-ghost absolute right-2 top-2\";\n close.textContent = \"✕\";\n box.appendChild(close);\n\n const heading = document.createElement(\"h3\");\n heading.className = \"text-lg font-bold mb-4\";\n box.appendChild(heading);\n\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n tablist.className = \"tabs tabs-border\";\n box.appendChild(tablist);\n\n const cleanups: Array<() => void> = [];\n // Text setters re-run on every locale change.\n const relabels: Array<() => void> = [];\n\n // Add a tab (radio + content); the first one added opens by default. The\n // tab's visible title is its (relocalizable) `aria-label`, the daisyUI way.\n let first = true;\n const addTab = (tab: Text | undefined, fallback: Text, content: HTMLElement): void => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = \"sg-settings-tabs\";\n input.role = \"tab\";\n input.className = \"tab\";\n if (first) {\n input.checked = true;\n first = false;\n }\n const pane = document.createElement(\"div\");\n pane.className = \"tab-content pt-4\";\n pane.appendChild(content);\n tablist.append(input, pane);\n relabels.push(() => input.setAttribute(\"aria-label\", resolveText(tab ?? fallback)));\n };\n\n // General: language as a <select>.\n if (options.language) {\n const lang = options.language;\n const field = document.createElement(\"label\");\n field.className = \"form-control w-full\";\n const label = document.createElement(\"span\");\n label.className = \"label-text mb-1 block\";\n const select = document.createElement(\"select\");\n select.className = \"select select-bordered select-sm w-full\";\n const optionEls = lang.options.map((opt) => {\n const o = document.createElement(\"option\");\n o.value = opt.value;\n select.appendChild(o);\n return { o, label: opt.label };\n });\n select.value = lang.value;\n const onChange = (): void => lang.onChange(select.value);\n select.addEventListener(\"change\", onChange);\n cleanups.push(() => select.removeEventListener(\"change\", onChange));\n relabels.push(() => {\n label.textContent = resolveText(lang.label);\n for (const { o, label: l } of optionEls) o.textContent = resolveText(l);\n });\n field.append(label, select);\n addTab(lang.tab, lang.label, field);\n }\n\n // Controls: handedness (radios) and the camera-control flags (toggles),\n // together in one tab. The flags are self-wired to `ctx.viewControls` and stay\n // in sync with the mode picker; the rest is app-supplied data like the others.\n if (options.handedness || options.controls) {\n const pane = document.createElement(\"div\");\n\n if (options.handedness) {\n const hand = options.handedness;\n const legend = document.createElement(\"div\");\n legend.className = \"label-text mb-1 block\";\n pane.appendChild(legend);\n const onChange = (e: Event): void => hand.onChange((e.target as HTMLInputElement).value);\n const texts: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const opt of hand.options) {\n const row = document.createElement(\"label\");\n row.className = \"label cursor-pointer justify-start gap-3 py-1\";\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = \"sg-settings-handedness\";\n input.value = opt.value;\n input.className = \"radio radio-sm\";\n input.checked = opt.value === hand.value;\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n const text = document.createElement(\"span\");\n text.className = \"label-text text-sm\";\n row.append(input, text);\n pane.appendChild(row);\n texts.push({ el: text, label: opt.label });\n }\n relabels.push(() => {\n legend.textContent = resolveText(hand.label);\n for (const { el, label } of texts) el.textContent = resolveText(label);\n });\n }\n\n if (options.controls) {\n const cfg = options.controls;\n const viewControls = ctx.gallery.ctx.viewControls;\n const legend = document.createElement(\"div\");\n legend.className = `label-text mb-1 block${options.handedness ? \" mt-3\" : \"\"}`;\n pane.appendChild(legend);\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-1\";\n pane.appendChild(list);\n const rows: Array<{ input: HTMLInputElement; key: keyof ControlFlags; el: HTMLSpanElement; label: Text }> = [];\n for (const item of cfg.items) {\n const row = document.createElement(\"label\");\n row.className = \"label cursor-pointer justify-start gap-3 py-1\";\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.className = \"toggle toggle-sm\";\n input.checked = viewControls.get()[item.key];\n const onChange = (): void => viewControls.setFlag(item.key, input.checked);\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n const text = document.createElement(\"span\");\n text.className = \"label-text text-sm\";\n row.append(input, text);\n list.appendChild(row);\n rows.push({ input, key: item.key, el: text, label: item.label });\n }\n // Reflect changes made elsewhere (the ScanEye picker flips these too).\n const sync = (): void => {\n const flags = viewControls.get();\n for (const { input, key } of rows) input.checked = flags[key];\n };\n cleanups.push(ctx.gallery.on(\"controls:change\", sync));\n relabels.push(() => {\n legend.textContent = resolveText(cfg.label);\n for (const { el, label } of rows) el.textContent = resolveText(label);\n });\n }\n\n const section = (options.handedness ?? options.controls)!;\n addTab(section.tab, section.label, pane);\n }\n\n // Graphics: toggles, stacked vertically (one per row).\n if (options.graphics) {\n const gfx = options.graphics;\n const pane = document.createElement(\"div\");\n const legend = document.createElement(\"div\");\n legend.className = \"label-text mb-1 block\";\n pane.appendChild(legend);\n\n // Segmented radio-button groups (e.g. antialias / bloom), above the toggles.\n // daisyUI renders `<input type=radio class=\"btn\">` as a button showing its\n // aria-label; `join` fuses them into one segmented control.\n const groupLabels: Array<{\n el: HTMLSpanElement;\n label: Text;\n opts: Array<{ input: HTMLInputElement; label: Text }>;\n }> = [];\n for (const group of gfx.choiceGroups ?? []) {\n const field = document.createElement(\"div\");\n field.className = \"mb-3\";\n const label = document.createElement(\"span\");\n label.className = \"label-text text-sm mb-1 block\";\n const row = document.createElement(\"div\");\n row.className = \"join w-full\";\n const name = `sg-settings-${group.key}`;\n const opts = group.options.map((opt) => {\n const input = document.createElement(\"input\");\n input.type = \"radio\";\n input.name = name;\n input.className = \"btn btn-sm join-item flex-1\";\n input.value = opt.value;\n input.checked = opt.value === group.value;\n const onChange = (): void => group.onChange(input.value);\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n row.appendChild(input);\n return { input, label: opt.label };\n });\n field.append(label, row);\n pane.appendChild(field);\n groupLabels.push({ el: label, label: group.label, opts });\n }\n\n const list = document.createElement(\"div\");\n list.className = \"flex flex-col gap-1\";\n pane.appendChild(list);\n const texts: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const item of gfx.items) {\n const row = document.createElement(\"label\");\n row.className = \"label cursor-pointer justify-start gap-3 py-1\";\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.className = \"toggle toggle-sm\";\n input.checked = item.value;\n const onChange = (): void => gfx.onChange(item.key, input.checked);\n input.addEventListener(\"change\", onChange);\n cleanups.push(() => input.removeEventListener(\"change\", onChange));\n const text = document.createElement(\"span\");\n text.className = \"label-text text-sm\";\n row.append(input, text);\n list.appendChild(row);\n texts.push({ el: text, label: item.label });\n }\n // Numeric sliders below the toggles (e.g. cardboard eye separation).\n const sliderLabels: Array<{ el: HTMLSpanElement; label: Text }> = [];\n for (const slider of gfx.sliders ?? []) {\n const field = document.createElement(\"label\");\n field.className = \"flex flex-col gap-1 mt-2\";\n const head = document.createElement(\"span\");\n head.className = \"label-text text-sm flex justify-between\";\n const name = document.createElement(\"span\");\n const value = document.createElement(\"span\");\n value.className = \"opacity-60\";\n head.append(name, value);\n const input = document.createElement(\"input\");\n input.type = \"range\";\n input.className = \"range range-sm\";\n input.min = String(slider.min);\n input.max = String(slider.max);\n input.step = String(slider.step ?? 0.1);\n input.value = String(slider.value);\n value.textContent = input.value;\n const onInput = (): void => {\n value.textContent = input.value;\n slider.onChange(parseFloat(input.value));\n };\n input.addEventListener(\"input\", onInput);\n cleanups.push(() => input.removeEventListener(\"input\", onInput));\n field.append(head, input);\n pane.appendChild(field);\n sliderLabels.push({ el: name, label: slider.label });\n }\n\n relabels.push(() => {\n legend.textContent = resolveText(gfx.label);\n for (const { el, label, opts } of groupLabels) {\n el.textContent = resolveText(label);\n // daisyUI shows the button's aria-label as its visible text.\n for (const { input, label: l } of opts) input.setAttribute(\"aria-label\", resolveText(l));\n }\n for (const { el, label } of texts) el.textContent = resolveText(label);\n for (const { el, label } of sliderLabels) el.textContent = resolveText(label);\n });\n addTab(gfx.tab, gfx.label, pane);\n }\n\n const backdrop = document.createElement(\"form\");\n backdrop.method = \"dialog\";\n backdrop.className = \"modal-backdrop\";\n const backdropBtn = document.createElement(\"button\");\n backdropBtn.textContent = \"close\";\n backdrop.appendChild(backdropBtn);\n\n dialog.append(box, backdrop);\n wrap.append(button, dialog);\n\n const unbind = bindLocale(ctx, () => {\n const title = resolveText(options.title);\n button.setAttribute(\"aria-label\", title);\n heading.textContent = title;\n close.setAttribute(\"aria-label\", resolveText(options.closeLabel ?? \"Close\"));\n for (const relabel of relabels) relabel();\n });\n\n const onOpen = (): void => dialog.showModal();\n const onClose = (): void => dialog.close();\n button.addEventListener(\"click\", onOpen);\n close.addEventListener(\"click\", onClose);\n\n return {\n element: wrap,\n destroy: () => {\n button.removeEventListener(\"click\", onOpen);\n close.removeEventListener(\"click\", onClose);\n for (const fn of cleanups) fn();\n unbind();\n },\n };\n };\n}\n","import type { Overlay, Text } from \"@scenography/overlay\";\n\nimport { artworkInfo, type ArtworkInfoOptions } from \"../components/artwork-info\";\nimport { cardboardButton, type CardboardButtonOptions } from \"../components/cardboard\";\nimport { creditsPanel, type CreditEntry } from \"../components/credits\";\nimport { environmentPanel, type EnvironmentPanelOptions } from \"../components/environment\";\nimport { fullscreenButton } from \"../components/fullscreen\";\nimport { helpPanel, type HelpPanelOptions } from \"../components/help\";\nimport { infoPanel, type InfoPanelOptions } from \"../components/info\";\nimport { muteButton } from \"../components/mute\";\nimport { resetViewButton, type ResetViewButtonOptions } from \"../components/reset-view\";\nimport { settingsPanel, type SettingsPanelOptions } from \"../components/settings\";\nimport {\n availableModes,\n detectControlCapabilities,\n viewModeButton,\n type ViewModeButtonOptions,\n} from \"../components/view-mode\";\nimport { watermark, type WatermarkOptions } from \"../components/watermark\";\nimport { lucideIcon } from \"../lucide\";\n\nexport interface DefaultPresetOptions {\n /** Settings panel (language / handedness / graphics); when given, a ⚙ button is added top-right. */\n settings?: SettingsPanelOptions;\n /** Environment panel (time of day, sun azimuth); when given, a ☼ button is added top-right. Off by default. */\n environment?: EnvironmentPanelOptions;\n /** Cardboard (stereoscopic VR) toggle — experimental, so it is opt-in. Pass `true` (or options) to add the 🥽 button top-right. Off by default. */\n cardboard?: CardboardButtonOptions | boolean;\n /**\n * Camera-control mode picker (Touch / VR / Cardboard) as a `ScanEye` button\n * top-right. The button only appears when the device supports two or more of\n * the given modes (e.g. a phone with a gyroscope); on desktop it's hidden. Pass\n * labels for every mode — the preset filters to the available ones.\n */\n viewMode?: ViewModeButtonOptions;\n /**\n * \"Home\" button (top-right) that returns the camera to the scene's start, for\n * visitors who lose their way. Pass `true` (or options) to add it. Off by default.\n */\n resetView?: ResetViewButtonOptions | boolean;\n /** Info panel (e.g. control instructions); when given, an ⓘ button is added bottom-right, above credits. */\n info?: InfoPanelOptions;\n /**\n * Visual controls-help panel (tabbed, with per-mode diagrams). When given, an\n * ⓘ button is added bottom-right, above credits. Prefer this over `info` for\n * control instructions; if both are set, `help` wins (they share the slot).\n */\n help?: HelpPanelOptions;\n /** Artwork info panel; when given, clicking a piece opens its caption on the left. */\n artworks?: ArtworkInfoOptions;\n /** Asset attributions; when given, a credits panel is added bottom-right. */\n credits?: CreditEntry[] | (() => CreditEntry[]);\n /** Credits panel title. */\n creditsTitle?: Text;\n /** Bottom-left watermark. Defaults to \"scenography\"; pass `false` to hide it. */\n watermark?: WatermarkOptions | false;\n}\n\n/**\n * The default overlay layout: mute + fullscreen (and a settings ⚙ when given)\n * top-right; an info ⓘ and a credits panel bottom-right when given. Wired with\n * Lucide icons — this is the opinionated, batteries-included layer, so the\n * coupling to daisyUI + Lucide lives here, not in `@scenography/overlay`.\n */\nexport function defaultPreset(overlay: Overlay, options: DefaultPresetOptions = {}): void {\n overlay.add(\n \"top-right\",\n muteButton({ icons: { on: lucideIcon(\"Volume2\"), off: lucideIcon(\"VolumeX\") } }),\n );\n overlay.add(\n \"top-right\",\n fullscreenButton({ icons: { enter: lucideIcon(\"Maximize\"), exit: lucideIcon(\"Minimize\") } }),\n );\n // \"Home\" reset sits with the other controls; broadly useful, so opt-in per gallery.\n if (options.resetView)\n overlay.add(\n \"top-right\",\n resetViewButton({\n icon: lucideIcon(\"House\"),\n ...(typeof options.resetView === \"object\" ? options.resetView : {}),\n }),\n );\n // Mode picker: only shown when the device actually supports ≥2 of the modes.\n if (options.viewMode) {\n const supported = availableModes(detectControlCapabilities());\n const modes = options.viewMode.modes.filter((m) => supported.includes(m.mode));\n if (modes.length >= 2) overlay.add(\"top-right\", viewModeButton({ ...options.viewMode, modes }));\n }\n if (options.settings) overlay.add(\"top-right\", settingsPanel(options.settings));\n // Environment (time of day) is opt-in — only galleries with a procedural sky.\n if (options.environment)\n overlay.add(\"top-right\", environmentPanel({ icon: lucideIcon(\"CloudSun\"), ...options.environment }));\n // Cardboard (stereoscopic) is experimental → opt-in. Sits just below in the\n // top-right stack when enabled.\n if (options.cardboard)\n overlay.add(\n \"top-right\",\n cardboardButton({\n icon: lucideIcon(\"RectangleGoggles\"),\n ...(typeof options.cardboard === \"object\" ? options.cardboard : {}),\n }),\n );\n if (options.artworks) overlay.add(\"middle-left\", artworkInfo(options.artworks));\n // Help/info goes before credits so it sits above it in the bottom-right stack.\n // `help` (the richer, tabbed panel) takes the slot when both are given.\n if (options.help) overlay.add(\"bottom-right\", helpPanel(options.help));\n else if (options.info) overlay.add(\"bottom-right\", infoPanel(options.info));\n if (options.credits) {\n overlay.add(\n \"bottom-right\",\n creditsPanel({ items: options.credits, title: options.creditsTitle, icon: lucideIcon(\"Copyleft\") }),\n );\n }\n // Watermark shown by default (bottom-left); pass `watermark: false` to hide.\n if (options.watermark !== false) {\n overlay.add(\"bottom-left\", watermark(options.watermark ?? {}));\n }\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,WAAW,MAAc,OAAO,IAAiB;CAC/D,aAAa;EACX,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,MAAM,UAAU;EACrB,OAAY,SAAS,CAClB,MAAM,WAAW;GAChB,MAAM,OAAQ,OAAmC;GACjD,IAAI,SAAS,KAAA,GAAW;GACxB,MAAM,MAAM,OAAO,cAAc,IAAkD;GACnF,IAAI,aAAa,SAAS,OAAO,IAAI,CAAC;GACtC,IAAI,aAAa,UAAU,OAAO,IAAI,CAAC;GACvC,KAAK,gBAAgB,GAAG;EAC1B,CAAC,CAAC,CACD,YAAY,CAEb,CAAC;EACH,OAAO;CACT;AACF;;;;AClBA,SAAgB,QAAQ,IAAiB,SAA4B;CACnE,MAAM,QAAQ,OAAO,YAAY,aAAa,QAAQ,IAAI;CAC1D,GAAG,gBAAgB;CACnB,IAAI,OAAO,UAAU,UAAU,GAAG,cAAc;MAC3C,GAAG,YAAY,KAAK;AAC3B;;;;;;AAOA,SAAgB,WAAW,KAAqB,OAA+B;CAC7E,MAAM;CACN,OAAO,IAAI,QAAQ,UAAU,KAAK,YAAY,CAAC;AACjD;;;;;;;;ACVA,SAAgB,iBAAiB,UAAmC,CAAC,GAAqB;CACxF,MAAM,QAAQ,QAAQ,OAAO,SAAS;CACtC,MAAM,OAAO,QAAQ,OAAO,QAAQ;CAEpC,QAAQ,QAAQ;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EAExC,MAAM,aAAmB;GACvB,MAAM,SAAS,SAAS,sBAAsB;GAC9C,QAAQ,QAAQ,SAAS,OAAO,KAAK;GACrC,OAAO,aAAa,cAAc,SAAS,oBAAoB,kBAAkB;EACnF;EACA,MAAM,gBAAsB;GAC1B,IAAI,SAAS,mBAAmB,SAAc,eAAe;QACxD,IAAS,QAAQ,oBAAoB;EAC5C;EAEA,OAAO,iBAAiB,SAAS,OAAO;EACxC,SAAS,iBAAiB,oBAAoB,IAAI;EAClD,KAAK;EAEL,OAAO;GACL,SAAS;GACT,UAAU;IACR,OAAO,oBAAoB,SAAS,OAAO;IAC3C,SAAS,oBAAoB,oBAAoB,IAAI;GACvD;EACF;CACF;AACF;;;;;;;;;AC9BA,SAAgB,WAAW,UAA6B,CAAC,GAAqB;CAC5E,MAAM,KAAK,QAAQ,OAAO,MAAM;CAChC,MAAM,MAAM,QAAQ,OAAO,OAAO;CAElC,QAAQ,QAAQ;EACd,MAAM,QAAQ,IAAI,QAAQ,IAAI;EAC9B,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EAExC,MAAM,aAAmB;GACvB,MAAM,QAAQ,MAAM,MAAM;GAC1B,QAAQ,QAAQ,QAAQ,MAAM,EAAE;GAChC,OAAO,aAAa,cAAc,QAAQ,WAAW,MAAM;EAC7D;EACA,MAAM,gBAAsB;GAC1B,MAAM,SAAS,CAAC,MAAM,MAAM,CAAC;GAC7B,KAAK;EACP;EAEA,OAAO,iBAAiB,SAAS,OAAO;EACxC,KAAK;EAEL,OAAO;GACL,SAAS;GACT,eAAe,OAAO,oBAAoB,SAAS,OAAO;EAC5D;CACF;AACF;;;;;;;;;ACzBA,SAAgB,gBAAgB,UAAkC,CAAC,GAAqB;CACtF,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;CAE/B,QAAQ,QAAQ;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EACxC,OAAO,aAAa,cAAc,KAAK;EACvC,QAAQ,QAAQ,IAAI;EAEpB,IAAI,UAAU;EACd,MAAM,gBAAsB;GAC1B,UAAU,CAAC;GACX,IAAI,QAAQ,UAAU,EAAE,WAAW,QAAQ,CAAC;GAC5C,OAAO,UAAU,OAAO,cAAc,OAAO;GAC7C,OAAO,aAAa,gBAAgB,OAAO,OAAO,CAAC;GAGnD,IAAI,QAAQ,KAAK,oBAAoB,EAAE,QAAQ,CAAC;EAClD;EAEA,OAAO,iBAAiB,SAAS,OAAO;EAExC,OAAO;GACL,SAAS;GACT,eAAe,OAAO,oBAAoB,SAAS,OAAO;EAC5D;CACF;AACF;;;;;;;;AC9BA,SAAgB,gBAAgB,UAAkC,CAAC,GAAqB;CACtF,MAAM,OAAO,QAAQ,QAAQ;CAE7B,QAAQ,QAAQ;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EACxC,QAAQ,QAAQ,IAAI;EAEpB,MAAM,gBAAsB,IAAI,QAAQ,IAAI,OAAO,MAAM;EACzD,OAAO,iBAAiB,SAAS,OAAO;EAExC,MAAM,SAAS,WAAW,WACxB,OAAO,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,SAAS,YAAY,CAAC,CAC9E;EAEA,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,OAAO;IAC3C,OAAO;GACT;EACF;CACF;AACF;;;;ACnCA,MAAM,YAA4C;CAChD,OAAO;CACP,IAAI;CACJ,WAAW;AACb;;;;;;;;;AA8BA,SAAgB,eAAe,SAAkD;CAC/E,QAAQ,QAAQ;EACd,MAAM,eAAe,IAAI,QAAQ,IAAI;EACrC,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,QAAQ,QAAQ,WAAW,SAAS,CAAC;EAErC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EACpB,IAAI,YAAY,KAAK;EAErB,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EACpB,IAAI,YAAY,OAAO;EAEvB,MAAM,OAAO,SAAS,cAAc,KAAK;EACzC,KAAK,YAAY;EACjB,IAAI,YAAY,IAAI;EAEpB,MAAM,WAA8B,CAAC;EACrC,MAAM,WAA8B,CAAC;EACrC,MAAM,gBAAwE,CAAC;EAE/E,KAAK,MAAM,QAAQ,QAAQ,OAAO;GAChC,MAAM,SAAS,SAAS,cAAc,QAAQ;GAC9C,OAAO,OAAO;GACd,OAAO,YAAY;GACnB,MAAM,SAAS,SAAS,cAAc,MAAM;GAC5C,OAAO,MAAM,UAAU;GACvB,QAAQ,QAAQ,WAAW,UAAU,KAAK,KAAK,CAAC;GAChD,MAAM,QAAQ,SAAS,cAAc,MAAM;GAC3C,OAAO,OAAO,QAAQ,KAAK;GAE3B,MAAM,eAAqB;IACzB,aAAa,QAAQ,KAAK,IAAI;IAC9B,WAAW;IACX,OAAO,MAAM;GACf;GACA,OAAO,iBAAiB,SAAS,MAAM;GACvC,SAAS,WAAW,OAAO,oBAAoB,SAAS,MAAM,CAAC;GAC/D,KAAK,YAAY,MAAM;GACvB,cAAc,KAAK;IAAE,IAAI;IAAQ,MAAM,KAAK;GAAK,CAAC;GAClD,SAAS,WAAY,MAAM,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK,KAAK,CAAE;EACnE;EAEA,MAAM,mBAAyB;GAC7B,MAAM,UAAU,aAAa,QAAQ;GACrC,KAAK,MAAM,EAAE,IAAI,UAAU,eAAe,GAAG,UAAU,OAAO,cAAc,SAAS,OAAO;EAC9F;EAEA,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAG1B,MAAM,YAAY,IAAI,QAAQ,GAAG,mBAAmB,UAAU;EAC9D,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,KAAK;GACvC,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,cAAc,OAAO,CAAC;GAC3E,KAAK,MAAM,WAAW,UAAU,QAAQ;EAC1C,CAAC;EAED,MAAM,eAAqB;GACzB,WAAW;GACX,OAAO,UAAU;EACnB;EACA,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,KAAK,MAAM,MAAM,UAAU,GAAG;IAC9B,UAAU;IACV,OAAO;GACT;EACF;CACF;AACF;;;;;;;;;ACzHA,SAAgB,4BAAiD;CAM/D,OAAO;EAAE,OAJP,OAAO,eAAe,cAClB,WAAW,mBAAmB,CAAC,CAAC,UAChC,OAAO,cAAc,gBAAgB,UAAU,kBAAkB,KAAK;EAE5D,MADH,OAAO,WAAW,eAAe,4BAA4B;CACrD;AACvB;;;;;;AAOA,SAAgB,eAAe,MAA6C;CAC1E,MAAM,QAA0B,CAAC;CACjC,IAAI,KAAK,OAAO,MAAM,KAAK,OAAO;CAClC,IAAI,KAAK,SAAS,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW;CACzD,OAAO;AACT;;;;;;;;;ACdA,SAAgB,iBAAiB,SAAoD;CACnF,MAAM,OAAO,QAAQ,QAAQ;CAE7B,QAAQ,QAAQ;EACd,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,QAAQ,QAAQ,IAAI;EAEpB,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EAEpB,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EAEpB,IAAI,OAAO,OAAO,OAAO;EAEzB,MAAM,WAA8B,CAAC;EACrC,MAAM,WAA8B,CAAC;EAErC,MAAM,2BAAW,IAAI,IAAgG;EAErH,KAAK,MAAM,UAAU,QAAQ,SAAS;GACpC,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,YAAY;GAClB,MAAM,OAAO,SAAS,cAAc,MAAM;GAC1C,KAAK,YAAY;GACjB,MAAM,OAAO,SAAS,cAAc,MAAM;GAC1C,MAAM,QAAQ,SAAS,cAAc,MAAM;GAC3C,MAAM,YAAY;GAClB,KAAK,OAAO,MAAM,KAAK;GACvB,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,OAAO;GACb,MAAM,YAAY;GAClB,MAAM,MAAM,OAAO,OAAO,GAAG;GAC7B,MAAM,MAAM,OAAO,OAAO,GAAG;GAC7B,MAAM,OAAO,OAAO,OAAO,QAAQ,EAAG;GACtC,MAAM,QAAQ,OAAO,OAAO,KAAK;GACjC,MAAM,cAAc,MAAM;GAC1B,MAAM,gBAAsB;IAC1B,MAAM,cAAc,MAAM;IAC1B,OAAO,SAAS,WAAW,MAAM,KAAK,CAAC;GACzC;GACA,MAAM,iBAAiB,SAAS,OAAO;GACvC,SAAS,WAAW,MAAM,oBAAoB,SAAS,OAAO,CAAC;GAC/D,MAAM,OAAO,MAAM,KAAK;GACxB,IAAI,YAAY,KAAK;GACrB,SAAS,IAAI,OAAO,KAAK;IAAE;IAAO;IAAO,UAAU,OAAO;GAAS,CAAC;GACpE,SAAS,WAAW;IAClB,KAAK,eAAA,GAAA,qBAAA,YAAA,CAA0B,OAAO,KAAK;GAC7C,CAAC;EACH;EAGA,IAAI,QAAQ,SAAS,QAAQ;GAC3B,MAAM,MAAM,SAAS,cAAc,KAAK;GACxC,IAAI,YAAY;GAChB,KAAK,MAAM,UAAU,QAAQ,SAAS;IACpC,MAAM,MAAM,SAAS,cAAc,QAAQ;IAC3C,IAAI,OAAO;IACX,IAAI,YAAY;IAChB,MAAM,cAAoB;KACxB,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,OAAO,MAAM,GAAG;MACtD,MAAM,OAAO,SAAS,IAAI,GAAG;MAC7B,IAAI,CAAC,MAAM;MACX,KAAK,MAAM,QAAQ,OAAO,GAAG;MAC7B,KAAK,MAAM,cAAc,OAAO,GAAG;MACnC,KAAK,SAAS,GAAG;KACnB;IACF;IACA,IAAI,iBAAiB,SAAS,KAAK;IACnC,SAAS,WAAW,IAAI,oBAAoB,SAAS,KAAK,CAAC;IAC3D,IAAI,YAAY,GAAG;IACnB,SAAS,WAAW;KAClB,IAAI,eAAA,GAAA,qBAAA,YAAA,CAA0B,OAAO,KAAK;IAC5C,CAAC;GACH;GACA,IAAI,YAAY,GAAG;EACrB;EAEA,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAE1B,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,KAAK;GACvC,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,cAAc,OAAO,CAAC;GAC3E,KAAK,MAAM,WAAW,UAAU,QAAQ;EAC1C,CAAC;EAED,MAAM,eAAqB,OAAO,UAAU;EAC5C,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,KAAK,MAAM,MAAM,UAAU,GAAG;IAC9B,OAAO;GACT;EACF;CACF;AACF;;;AC9FA,IAAI,aAAa;AAEjB,MAAM,YAAY,SAAqC;CACrD,MAAM,MAAM,SAAS,cAAc,KAAK;CACxC,IAAI,YAAY;CAEhB,MAAM,OAAO,SAAS,cAAc,KAAK,OAAO,MAAM,MAAM;CAC5D,KAAK,cAAc,KAAK;CACxB,IAAI,KAAK,MAAM;EACb,MAAM,OAAO;EACb,KAAK,OAAO,KAAK;EACjB,KAAK,SAAS;EACd,KAAK,MAAM;EACX,KAAK,YAAY;CACnB,OACE,KAAK,YAAY;CAEnB,IAAI,YAAY,IAAI;CAEpB,MAAM,OAAO,CAAC,KAAK,QAAQ,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CACnE,IAAI,MAAM;EACR,MAAM,QAAQ,SAAS,cAAc,MAAM;EAC3C,MAAM,YAAY;EAClB,MAAM,cAAc,MAAM;EAC1B,IAAI,YAAY,KAAK;CACvB;CACA,OAAO;AACT;AAEA,MAAM,gBAAgB,YAA2C;CAC/D,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY;CACpB,QAAQ,cAAc,QAAQ;CAC9B,OAAO;AACT;AAEA,MAAM,gBAAgB,YACpB,QAAQ,KAAK,UAAW,MAAM,SAAS,YAAY,aAAa,KAAK,IAAI,SAAS,KAAK,CAAE;;;;;;;;;AAU3F,SAAgB,aAAa,SAAgD;CAC3E,MAAM,OAAO,QAAQ,QAAQ;CAE7B,QAAQ,QAAQ;EACd,MAAM,WAAW,mBAAmB;EACpC,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EACxC,QAAQ,QAAQ,IAAI;EAEpB,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EACpB,MAAM,aAAa,cAAc,OAAO;EAExC,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EAGpB,MAAM,QAAQ,SAAS,cAAc,KAAK;EAC1C,MAAM,YAAY;EAClB,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,aAAa,QAAQ,SAAS;EACtC,QAAQ,YAAY;EAEpB,IAAI,OAAO,OAAO,SAAS,OAAO,OAAO;EAGzC,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAE1B,MAAM,UAAU,YAAiC;GAC/C,MAAM,OAAO,QAAQ,QAAQ,MAAuC,EAAE,SAAS,KAAK;GACpF,MAAM,OAAO,QAAQ,QAAQ,MAAsB,EAAE,SAAS,KAAK;GAEnE,MAAM,gBAAgB,GAAG,aAAa,IAAI,CAAC;GAC3C,MAAM,MAAM,UAAU,KAAK,SAAS,KAAK;GAEzC,MAAM,QAAgB,CAAC;GACvB,KAAK,SAAS,KAAK,MAAM;IACvB,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,OAAO;IACb,MAAM,OAAO;IACb,MAAM,OAAO;IACb,MAAM,YAAY;IAClB,MAAM,aAAa,cAAc,IAAI,KAAK;IAC1C,IAAI,MAAM,GAAG,MAAM,UAAU;IAC7B,MAAM,OAAO,SAAS,cAAc,KAAK;IACzC,KAAK,YAAY;IACjB,MAAM,OAAO,SAAS,cAAc,KAAK;IACzC,KAAK,YAAY;IACjB,KAAK,gBAAgB,GAAG,aAAa,IAAI,QAAQ,CAAC;IAClD,KAAK,YAAY,IAAI;IACrB,MAAM,KAAK,OAAO,IAAI;GACxB,CAAC;GACD,QAAQ,gBAAgB,GAAG,KAAK;GAChC,QAAQ,MAAM,UAAU,KAAK,SAAS,KAAK;EAC7C;EAEA,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,SAAS,SAAS;GACpD,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,OAAO,OAAO,QAAQ,UAAU,aAAa,QAAQ,MAAM,IAAI,QAAQ,KAAK;EAC9E,CAAC;EAED,MAAM,eAAqB,OAAO,UAAU;EAC5C,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,UAAU;IACR,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,OAAO;GACT;EACF;CACF;AACF;;;;;;;;AC7KA,SAAgB,aAAa,SAAgD;CAC3E,QAAQ,QAAQ;EACd,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY,QAAQ,aAAa;EAExC,IAAI,QAAQ,SAAS,KAAA,GAAW,QAAQ,QAAQ,QAAQ,IAAI;EAC5D,IAAI,QAAQ,UAAU,KAAA,GAAW;GAC/B,MAAM,QAAQ,SAAS,cAAc,MAAM;GAC3C,MAAM,cAAc,QAAQ;GAC5B,OAAO,YAAY,KAAK;EAC1B;EACA,IAAI,QAAQ,WAAW,OAAO,aAAa,cAAc,QAAQ,SAAS;EAE1E,MAAM,gBAAsB,QAAQ,QAAQ,GAAG;EAC/C,OAAO,iBAAiB,SAAS,OAAO;EAExC,OAAO;GACL,SAAS;GACT,eAAe,OAAO,oBAAoB,SAAS,OAAO;EAC5D;CACF;AACF;;;;;;;;ACxBA,SAAgB,UAAU,SAA6C;CACrE,QAAQ,QAAQ;EACd,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,QAAQ,QAAQ,WAAW,MAAM,CAAC;EAElC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EAEpB,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EAEpB,MAAM,OAAO,SAAS,cAAc,GAAG;EACvC,KAAK,YAAY;EAEjB,IAAI,OAAO,OAAO,SAAS,IAAI;EAE/B,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAE1B,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,KAAK;GACvC,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,KAAK,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,IAAI;GAC3C,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,cAAc,OAAO,CAAC;EAC7E,CAAC;EAED,MAAM,eAAqB,OAAO,UAAU;EAC5C,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,OAAO;GACT;EACF;CACF;AACF;;;AC7DA,MAAM,QAAQ;AA2Bd,MAAM,WAA0C,EAC9C,WAvBA,46DAoBA,MAIF;;AAGA,SAAgB,YAAY,MAAkC;CAC5D,MAAM,SAAS,SAAS,cAAc,KAAK;CAE3C,OAAO,YAAY;CACnB,OAAO,YAAY,SAAS;CAC5B,OAAO;AACT;AA2BA,MAAM,gBACJ;AAIF,MAAM,iBACJ;AAIF,MAAM,UACJ;AAIF,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,eAAe;AACrB,MAAM,gBAAgB;AAGtB,MAAM,OAAO,SACX,+CAA+C,KAAK;;AAGtD,SAAS,WAAW,WAAqC;CACvD,MAAM,MAAM,MAAc,SACxB,cAAc,OAAO,IAAI,IAAI,IAAI;CACnC,OACE,6yBAEA,GAAG,eAAe,MAAM,IACxB,GAAG,gBAAgB,MAAM,IACzB,GAAG,SAAS,MAAM,IAClB,aACA,cACA,eACA,GAAG,eAAe,QAAQ,IAC1B;AAEJ;AAWA,MAAM,eACJ;AA0BF,MAAM,YAAY,IAAY,OAC5B,2DAA2D,GAAG,QAAQ,GAAG;AAG3E,MAAM,aAAa,UACjB,2sDAwBC,UAAU,SAAS,SAAS,GAAG,CAAC,IAAI,OACpC,UAAU,SAAS,SAAS,IAAI,CAAC,IAAI,MACtC;AAEF,MAAM,QAAmC;CACvC,QAAQ;CACR,MAAM,UAAU;CAChB,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,MAAM;AACxB;AAIA,MAAM,aACJ,+bASA;AASF,MAAM,MAAM;AACZ,MAAM,WAAW,MAAM;AAEvB,MAAM,SAA2B,CAAC,KAAK,EAAE;AACzC,MAAM,WAA6B,CAAC,IAAI,GAAG;AAC3C,MAAM,WAA6B,CAAC,KAAK,GAAG;AAC5C,MAAM,YAA8B,CAAC,KAAK,GAAG;AAE7C,MAAM,WAAW,CAAC,IAAI,QACpB,YAAY,KAAK,SAAS,OAAO,KAAK,SAAS,WAAW,IAAI,YAAY,IAAI;AAGhF,MAAM,aAAa,CAAC,IAAI,KAAuB,OAC7C,YAAY,GAAG,OAAO,GAAG,4JAEY,GAAG;AAG1C,MAAM,YACJ,CAAC,IAAI,KACL,QACW;CACX,MAAM,IAAI;CACV,MAAM,IAAI;CACV,MAAM,QAAQ,MACZ,YAAY,EAAE;CAEhB,QAAQ,KAAR;EACE,KAAK,MACH,OAAO,KAAK,IAAI,GAAG,GAAG,KAAK,EAAE,IAAI,KAAK,GAAG,IACvC,KAAK,IAAI,KAAK,EAAE,GAAG,KAAK,IAAI,EAAE,IAAI,GAAG,GAAG,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG;EAC7E,KAAK,QACH,OAAO,KAAK,IAAI,GAAG,GAAG,KAAK,EAAE,IAAI,KAAK,GAAG,IACvC,KAAK,IAAI,KAAK,EAAE,GAAG,KAAK,IAAI,EAAE,IAAI,GAAG,GAAG,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG;EAC7E,KAAK,QACH,OAAO,KAAK,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,KAAK,GAAG,IACvC,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG;EAC7E,SACE,OAAO,KAAK,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,KAAK,GAAG,IACvC,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG;CAC/E;AACF;AAEA,MAAM,aACJ,QAAQ,MAAM,IAAI,UAAU,QAAQ,GAAG,IACvC,QAAQ,QAAQ,IAAI,UAAU,UAAU,GAAG,IAC3C,QAAQ,QAAQ,IAAI,UAAU,UAAU,GAAG,IAC3C,QAAQ,SAAS,IAAI,UAAU,WAAW,GAAG;AAU/C,MAAM,OACJ,uHATA,QAAQ,MAAM,IAAI,SAAS,QAAQ,IAAI,IACvC,QAAQ,QAAQ,IAAI,SAAS,UAAU,MAAM,IAC7C,QAAQ,QAAQ,IAAI,SAAS,UAAU,MAAM,IAC7C,QAAQ,SAAS,IAAI,SAAS,WAAW,OAAO,EAQ4B,SACtE,WAAW,QACjB;AAGF,MAAM,aACJ;AAIF,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAGxB,MAAM,SAAS,UACb,wFACC,UAAU,SACP,wDAAwD,eAAe,WACvE,OACH,UAAU,UACP,wDAAwD,gBAAgB,WACxE,MACJ,oDAAoD,WAAW,OAC/D;AAGF,SAAS,WAAW,KAA0B;CAC5C,MAAM,SAAS,SAAS,cAAc,KAAK;CAC3C,OAAO,YAAY;CACnB,OAAO,YAAY;CACnB,OAAO;AACT;;;;;;AAOA,SAAgB,eAAe,MAAoD;CACjF,QAAQ,MAAR;EACE,KAAK,SACH,OAAQ;GAAC;GAAU;GAAQ;GAAQ;EAAM,CAAC,CAAW,KAClD,eAAe,WAAW,MAAM,KAAK,CACxC;EACF,KAAK,MAEH,OAAO,OAAO,WAAW,MAAM,MAAM,SAAS,WAAW,UAAU,CAAC;EACtE,KAAK,kBACH,OAAO;SACC,WAAW,IAAI;SACf,WAAW,MAAM,MAAM,CAAC;SACxB,WAAW,MAAM,OAAO,CAAC;EACjC;EACF,KAAK,WACH,OAAQ;GAAC;GAAQ;GAAQ;EAAQ,CAAC,CAAW,KAC1C,YAAY,WAAW,WAAW,CAAC,CAAC,CACvC;EACF,SACE,OAAO;CACX;AACF;;;;ACzUA,SAAgB,YAAY,UAAiC;CAC3D,OAAO;AACT;AASA,SAAgB,wBAAyC;CAGvD,IAAI,OADF,OAAO,eAAe,eAAe,WAAW,mBAAmB,CAAC,CAAC,UACzC,UAAU;CAExC,MAAM,gBAAsB;EAC1B,OAAO;CACT;CAEA,MAAM,aAAa,MAAmB;EACpC,IAAK,EAAmB,gBAAgB,SAAS;EACjD,OAAO;CACT;CACA,MAAM,cAAoB;EACxB,OAAO;CACT;CACA,MAAM,cAAoB;EACxB,OAAO;CACT;CAEA,OAAO,iBAAiB,cAAc,SAAS,EAAE,SAAS,KAAK,CAAC;CAChE,OAAO,iBAAiB,eAAe,WAAW,EAAE,SAAS,KAAK,CAAC;CACnE,OAAO,iBAAiB,WAAW,KAAK;CACxC,OAAO,iBAAiB,oBAAoB,KAAK;CAEjD,OAAO;EACL,WAAW;EACX,eAAe;GACb,OAAO,oBAAoB,cAAc,OAAO;GAChD,OAAO,oBAAoB,eAAe,SAAS;GACnD,OAAO,oBAAoB,WAAW,KAAK;GAC3C,OAAO,oBAAoB,oBAAoB,KAAK;EACtD;CACF;AACF;;;;;;;;;;ACRA,SAAS,UAAU,MAAmB,KAAwB;CAC5D,MAAM,UAAU,eAAe,IAAI,IAAI;CACvC,IAAI,SAAS,OAAO,mBAAmB,MAAM,IAAI,OAAO,OAAO;CAG/D,KAAK,YAAY,YAAY,IAAI,IAAmB,CAAC;CACrD,MAAM,OAAO,SAAS,cAAc,IAAI,UAAU,OAAO,IAAI;CAC7D,KAAK,YAAY,GAAG,IAAI,UAAU,iBAAiB,YAAY;CAC/D,MAAM,UAAU,IAAI,MAAM,KAAK,SAAS;EACtC,MAAM,KAAK,SAAS,cAAc,IAAI;EACtC,KAAK,YAAY,EAAE;EACnB,OAAO;GAAE;GAAI;EAAK;CACpB,CAAC;CACD,KAAK,YAAY,IAAI;CACrB,OAAO;AACT;AAEA,SAAS,mBACP,MACA,OACA,SACU;CACV,MAAM,UAAoB,CAAC;CAC3B,QAAQ,SAAS,YAAY,MAAM;EACjC,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM;EACX,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAChB,IAAI,YAAY,WAAW,CAAC;EAC5B,MAAM,IAAI,SAAS,cAAc,GAAG;EACpC,EAAE,YAAY;EACd,IAAI,YAAY,CAAC;EACjB,KAAK,YAAY,GAAG;EACpB,QAAQ,KAAK;GAAE,IAAI;GAAG;EAAK,CAAC;CAC9B,CAAC;CACD,MAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM;CACxC,IAAI,MAAM,SAAS,GAAG;EACpB,MAAM,OAAO,SAAS,cAAc,IAAI;EACxC,KAAK,YACH;EACF,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,KAAK,SAAS,cAAc,IAAI;GACtC,KAAK,YAAY,EAAE;GACnB,QAAQ,KAAK;IAAE;IAAI;GAAK,CAAC;EAC3B;EACA,KAAK,YAAY,IAAI;CACvB;CACA,OAAO;AACT;AAEA,IAAI,gBAAgB;;;;;;;AAQpB,SAAgB,UAAU,SAA6C;CACrE,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,YAAY,gBAAiB,iBAAiB;CAEpD,QAAQ,QAAQ;EACd,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,QAAQ,QAAQ,WAAW,MAAM,CAAC;EAElC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EACpB,IAAI,YAAY,KAAK;EAErB,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EACpB,IAAI,YAAY,OAAO;EAEvB,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,aAAa,QAAQ,SAAS;EACtC,QAAQ,YAAY;EACpB,IAAI,YAAY,OAAO;EAGvB,MAAM,WAA8B,CAAC;EACrC,MAAM,4BAAY,IAAI,IAAmC;EACzD,IAAI,aAAsC;EAE1C,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC9B,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,YAAY;GAClB,IAAI,CAAC,YAAY;IACf,MAAM,UAAU;IAChB,aAAa;GACf;GAEA,MAAM,OAAO,SAAS,cAAc,KAAK;GACzC,KAAK,YAAY;GACjB,MAAM,UAAU,UAAU,MAAM,GAAG;GAEnC,QAAQ,OAAO,OAAO,IAAI;GAC1B,UAAU,IAAI,IAAI,MAAM,KAAK;GAC7B,SAAS,WAAW;IAClB,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,IAAI,KAAK,CAAC;IACvD,KAAK,MAAM,EAAE,IAAI,UAAU,SAAS,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,IAAI;GACvE,CAAC;EACH;EAEA,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAE1B,MAAM,UAAU,sBAAsB;EAEtC,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,KAAK;GACvC,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,cAAc,OAAO,CAAC;GAC3E,KAAK,MAAM,WAAW,UAAU,QAAQ;EAC1C,CAAC;EAED,MAAM,eAAqB;GACzB,IAAI,YAAY;IAId,MAAM,OAAO,IAAI,QAAQ,IAAI,aAAa,QAAQ;IAClD,MAAM,OACJ,SAAS,cAAc,cAAc,SAAS,OAAO,OAAO,YAAY,QAAQ,IAAI,CAAC;IACvF,MAAM,SAAS,UAAU,IAAI,IAAI,KAAK;IACtC,IAAI,QAAQ,OAAO,UAAU;GAC/B;GACA,OAAO,UAAU;EACnB;EACA,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,QAAQ,QAAQ;IAChB,OAAO;GACT;EACF;CACF;AACF;;;AC1LA,MAAM,qBAAqB;CAAE,WAAW;CAAW,aAAa;AAAU;AAE1E,MAAM,WAAW;AAEjB,MAAM,iBAAiB;AACvB,MAAM,gBAAgB,IAAI;AAC1B,MAAM,WAAW;;;;;;AAOjB,SAAS,eAAqB;CAC5B,IAAI,OAAO,aAAa,eAAe,SAAS,eAAe,QAAQ,GAAG;CAC1E,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,KAAK;CACX,MAAM,cAGJ,sEAAsE,iBAAiB,IAAI;CAsB7F,SAAS,KAAK,YAAY,KAAK;AACjC;AAEA,MAAM,QAAQ,OAA+B,QAC3C,MAAM,QAAQ,MAAmB,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;;;;;;;;;;;;;;AAevD,SAAgB,YAAY,SAA+C;CACzE,MAAM,OAAO,QAAQ;CACrB,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAC/C,MAAM,UAAU,IAAI,IAAI,KAAK,KAAK,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CAErD,QAAQ,QAAQ;EACd,aAAa;EACb,MAAM,SAAS,IAAI,QAAQ,IAAI;EAE/B,MAAM,QAAQ,SAAS,cAAc,KAAK;EAG1C,MAAM,YAAY;EAElB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EACpB,MAAM,aAAa,cAAc,QAAQ,cAAc,OAAO;EAE9D,MAAM,QAAQ,SAAS,cAAc,IAAI;EACzC,MAAM,YAAY;EAClB,MAAM,OAAO,SAAS,cAAc,KAAK;EACzC,KAAK,YAAY;EAIjB,MAAM,SAAS,SAAS,cAAc,KAAK;EAC3C,OAAO,YAAY;EACnB,MAAM,YAAY,SAAS,cAAc,MAAM;EAC/C,UAAU,YAAY;EACtB,MAAM,aAAa,SAAS,cAAc,MAAM;EAChD,OAAO,OAAO,WAAW,UAAU;EAEnC,MAAM,OAAO,SAAS,cAAc,GAAG;EACvC,KAAK,YAAY;EAGjB,MAAM,OAAO,SAAS,cAAc,GAAG;EACvC,KAAK,YAAY;EACjB,KAAK,SAAS;EACd,KAAK,MAAM;EACX,KAAK,cAAc,GAAG,QAAQ,YAAY,eAAe;EAIzD,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAChB,MAAM,UAAU,SAAS,cAAc,QAAQ;EAC/C,QAAQ,OAAO;EACf,QAAQ,YAAY;EACpB,MAAM,WAAW,QAAQ,aAAa;EACtC,QAAQ,cAAc,KAAK;EAC3B,QAAQ,aAAa,cAAc,QAAQ;EAC3C,MAAM,UAAU,SAAS,cAAc,QAAQ;EAC/C,QAAQ,OAAO;EACf,QAAQ,YAAY;EACpB,MAAM,WAAW,QAAQ,aAAa;EACtC,QAAQ,cAAc,GAAG,SAAS;EAClC,QAAQ,aAAa,cAAc,QAAQ;EAC3C,IAAI,OAAO,SAAS,OAAO;EAC3B,IAAI,KAAK,UAAU,GAAG,IAAI,MAAM,UAAU;EAE1C,MAAM,OAAO,OAAO,OAAO,MAAM,QAAQ,MAAM,MAAM,GAAG;EACxD,IAAI,QAAQ,YAAY,KAAK;EAE7B,IAAI,YAA2B;EAE/B,MAAM,UAAU,YAA2B;GACzC,MAAM,cAAc,QAAQ,SAAS;GACrC,MAAM,QAAQ,CACZ,KAAK,CAAC,QAAQ,QAAQ,QAAQ,MAAM,SAAS,CAAC,GAAG,IAAI,GACrD,KAAK,CAAC,QAAQ,WAAW,QAAQ,UAAU,GAAG,IAAI,CACpD,CAAC,CAAC,OAAO,OAAO;GAChB,KAAK,gBACH,GAAG,MAAM,KAAK,SAAS;IACrB,MAAM,OAAO,SAAS,cAAc,MAAM;IAC1C,KAAK,cAAc;IACnB,OAAO;GACT,CAAC,CACH;GAIA,MAAM,KAAK,QAAQ;GACnB,IAAI,OAAO,KAAA,GACT,OAAO,MAAM,UAAU;QAClB;IACL,OAAO,MAAM,UAAU;IACvB,MAAM,UAAU,OAAO;IACvB,UAAU,MAAM,kBAAkB,UAC9B,mBAAmB,YACnB,mBAAmB;IACvB,WAAW,cAAc,UACpB,QAAQ,kBAAkB,WAAW,aACrC,QAAQ,kBAAkB,qBAAqB;GACtD;GAEA,KAAK,cAAc,QAAQ,eAAe;GAE1C,IAAI,QAAQ,MAAM;IAChB,KAAK,OAAO,QAAQ;IACpB,KAAK,MAAM,UAAU;GACvB,OACE,KAAK,MAAM,UAAU;EAEzB;EAEA,MAAM,aAAmB;GACvB,MAAM,UAAU,OAAO,wBAAwB;GAC/C,YAAY;EACd;EAIA,MAAM,QAAQ,OAAqB;GACjC,MAAM,UAAU,KAAK,IAAI,EAAE;GAC3B,IAAI,CAAC,SAAS;GACd,YAAY;GACZ,OAAO,OAAO;GACd,MAAM,YAAY;GAClB,MAAM,UAAU,IAAI,wBAAwB;GAC5C,OAAO,aAAa,IAAI;IAAE,SAAS;IAAU,eAAe;GAAc,CAAC;EAC7E;EAGA,MAAM,QAAQ,UAAwB;GACpC,IAAI,cAAc,QAAQ,KAAK,UAAU,GAAG;GAC5C,MAAM,IAAI,QAAQ,IAAI,SAAS;GAC/B,IAAI,MAAM,KAAA,GAAW;GACrB,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,UAAU,KAAK;GACnD,IAAI,MAAM,KAAK,KAAK,EAAE;EACxB;EAEA,MAAM,YAAY,YAA4B;GAC5C,MAAM,KAAM,SAAyC;GACrD,IAAI,IAAI,KAAK,EAAE;EACjB;EAEA,MAAM,cAAc,YAA4B;GAC9C,MAAM,KAAM,SAAyC;GACrD,IAAI,MAAM,OAAO,WAAW,KAAK;EACnC;EAEA,MAAM,gBAAsB;GAC1B,MAAM,KAAK;GACX,KAAK;GAGL,IAAI,IAAI,OAAO,cAAc,IAAI,EAAE,aAAa,QAAQ,oBAAoB,EAAE,CAAC;EACjF;EAGA,MAAM,gBAAsB,KAAK;EACjC,MAAM,eAAqB,KAAK,EAAE;EAClC,MAAM,eAAqB,KAAK,CAAC;EAEjC,MAAM,YAAY,IAAI,QAAQ,GAAG,kBAAkB,QAAQ;EAC3D,MAAM,cAAc,IAAI,QAAQ,GAAG,oBAAoB,UAAU;EACjE,MAAM,WAAW,IAAI,QAAQ,GAAG,gBAAgB,OAAO;EACvD,MAAM,iBAAiB,SAAS,OAAO;EACvC,QAAQ,iBAAiB,SAAS,MAAM;EACxC,QAAQ,iBAAiB,SAAS,MAAM;EAGxC,MAAM,cAAc,SAAS,cAAc,KAAK;EAChD,YAAY,MAAM,UAAU;EAE5B,OAAO;GACL,SAAS;GACT,UAAU;IACR,UAAU;IACV,YAAY;IACZ,SAAS;IACT,MAAM,oBAAoB,SAAS,OAAO;IAC1C,QAAQ,oBAAoB,SAAS,MAAM;IAC3C,QAAQ,oBAAoB,SAAS,MAAM;IAC3C,MAAM,OAAO;GACf;EACF;CACF;AACF;;;;;;;ACjQA,SAAgB,UAAU,UAA4B,CAAC,GAAqB;CAC1E,aAAa;EACX,MAAM,KAAK,SAAS,cAAc,KAAK;EACvC,GAAG,cAAc,QAAQ,QAAQ;EACjC,IAAI,QAAQ,WAAW,GAAG,YAAY,QAAQ;OACzC,GAAG,MAAM,UAAU;EACxB,OAAO;CACT;AACF;;;;;;;;;;ACsFA,SAAgB,cAAc,SAAiD;CAC7E,QAAQ,QAAQ;EACd,MAAM,OAAO,SAAS,cAAc,KAAK;EAEzC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,QAAQ,QAAQ,WAAW,UAAU,CAAC;EAEtC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,MAAM,QAAQ,SAAS,cAAc,QAAQ;EAC7C,MAAM,OAAO;EACb,MAAM,YAAY;EAClB,MAAM,cAAc;EACpB,IAAI,YAAY,KAAK;EAErB,MAAM,UAAU,SAAS,cAAc,IAAI;EAC3C,QAAQ,YAAY;EACpB,IAAI,YAAY,OAAO;EAEvB,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,aAAa,QAAQ,SAAS;EACtC,QAAQ,YAAY;EACpB,IAAI,YAAY,OAAO;EAEvB,MAAM,WAA8B,CAAC;EAErC,MAAM,WAA8B,CAAC;EAIrC,IAAI,QAAQ;EACZ,MAAM,UAAU,KAAuB,UAAgB,YAA+B;GACpF,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,YAAY;GAClB,IAAI,OAAO;IACT,MAAM,UAAU;IAChB,QAAQ;GACV;GACA,MAAM,OAAO,SAAS,cAAc,KAAK;GACzC,KAAK,YAAY;GACjB,KAAK,YAAY,OAAO;GACxB,QAAQ,OAAO,OAAO,IAAI;GAC1B,SAAS,WAAW,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,OAAO,QAAQ,CAAC,CAAC;EACpF;EAGA,IAAI,QAAQ,UAAU;GACpB,MAAM,OAAO,QAAQ;GACrB,MAAM,QAAQ,SAAS,cAAc,OAAO;GAC5C,MAAM,YAAY;GAClB,MAAM,QAAQ,SAAS,cAAc,MAAM;GAC3C,MAAM,YAAY;GAClB,MAAM,SAAS,SAAS,cAAc,QAAQ;GAC9C,OAAO,YAAY;GACnB,MAAM,YAAY,KAAK,QAAQ,KAAK,QAAQ;IAC1C,MAAM,IAAI,SAAS,cAAc,QAAQ;IACzC,EAAE,QAAQ,IAAI;IACd,OAAO,YAAY,CAAC;IACpB,OAAO;KAAE;KAAG,OAAO,IAAI;IAAM;GAC/B,CAAC;GACD,OAAO,QAAQ,KAAK;GACpB,MAAM,iBAAuB,KAAK,SAAS,OAAO,KAAK;GACvD,OAAO,iBAAiB,UAAU,QAAQ;GAC1C,SAAS,WAAW,OAAO,oBAAoB,UAAU,QAAQ,CAAC;GAClE,SAAS,WAAW;IAClB,MAAM,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK,KAAK;IAC1C,KAAK,MAAM,EAAE,GAAG,OAAO,OAAO,WAAW,EAAE,eAAA,GAAA,qBAAA,YAAA,CAA0B,CAAC;GACxE,CAAC;GACD,MAAM,OAAO,OAAO,MAAM;GAC1B,OAAO,KAAK,KAAK,KAAK,OAAO,KAAK;EACpC;EAKA,IAAI,QAAQ,cAAc,QAAQ,UAAU;GAC1C,MAAM,OAAO,SAAS,cAAc,KAAK;GAEzC,IAAI,QAAQ,YAAY;IACtB,MAAM,OAAO,QAAQ;IACrB,MAAM,SAAS,SAAS,cAAc,KAAK;IAC3C,OAAO,YAAY;IACnB,KAAK,YAAY,MAAM;IACvB,MAAM,YAAY,MAAmB,KAAK,SAAU,EAAE,OAA4B,KAAK;IACvF,MAAM,QAAqD,CAAC;IAC5D,KAAK,MAAM,OAAO,KAAK,SAAS;KAC9B,MAAM,MAAM,SAAS,cAAc,OAAO;KAC1C,IAAI,YAAY;KAChB,MAAM,QAAQ,SAAS,cAAc,OAAO;KAC5C,MAAM,OAAO;KACb,MAAM,OAAO;KACb,MAAM,QAAQ,IAAI;KAClB,MAAM,YAAY;KAClB,MAAM,UAAU,IAAI,UAAU,KAAK;KACnC,MAAM,iBAAiB,UAAU,QAAQ;KACzC,SAAS,WAAW,MAAM,oBAAoB,UAAU,QAAQ,CAAC;KACjE,MAAM,OAAO,SAAS,cAAc,MAAM;KAC1C,KAAK,YAAY;KACjB,IAAI,OAAO,OAAO,IAAI;KACtB,KAAK,YAAY,GAAG;KACpB,MAAM,KAAK;MAAE,IAAI;MAAM,OAAO,IAAI;KAAM,CAAC;IAC3C;IACA,SAAS,WAAW;KAClB,OAAO,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK,KAAK;KAC3C,KAAK,MAAM,EAAE,IAAI,WAAW,OAAO,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK;IACvE,CAAC;GACH;GAEA,IAAI,QAAQ,UAAU;IACpB,MAAM,MAAM,QAAQ;IACpB,MAAM,eAAe,IAAI,QAAQ,IAAI;IACrC,MAAM,SAAS,SAAS,cAAc,KAAK;IAC3C,OAAO,YAAY,wBAAwB,QAAQ,aAAa,UAAU;IAC1E,KAAK,YAAY,MAAM;IACvB,MAAM,OAAO,SAAS,cAAc,KAAK;IACzC,KAAK,YAAY;IACjB,KAAK,YAAY,IAAI;IACrB,MAAM,OAAsG,CAAC;IAC7G,KAAK,MAAM,QAAQ,IAAI,OAAO;KAC5B,MAAM,MAAM,SAAS,cAAc,OAAO;KAC1C,IAAI,YAAY;KAChB,MAAM,QAAQ,SAAS,cAAc,OAAO;KAC5C,MAAM,OAAO;KACb,MAAM,YAAY;KAClB,MAAM,UAAU,aAAa,IAAI,CAAC,CAAC,KAAK;KACxC,MAAM,iBAAuB,aAAa,QAAQ,KAAK,KAAK,MAAM,OAAO;KACzE,MAAM,iBAAiB,UAAU,QAAQ;KACzC,SAAS,WAAW,MAAM,oBAAoB,UAAU,QAAQ,CAAC;KACjE,MAAM,OAAO,SAAS,cAAc,MAAM;KAC1C,KAAK,YAAY;KACjB,IAAI,OAAO,OAAO,IAAI;KACtB,KAAK,YAAY,GAAG;KACpB,KAAK,KAAK;MAAE;MAAO,KAAK,KAAK;MAAK,IAAI;MAAM,OAAO,KAAK;KAAM,CAAC;IACjE;IAEA,MAAM,aAAmB;KACvB,MAAM,QAAQ,aAAa,IAAI;KAC/B,KAAK,MAAM,EAAE,OAAO,SAAS,MAAM,MAAM,UAAU,MAAM;IAC3D;IACA,SAAS,KAAK,IAAI,QAAQ,GAAG,mBAAmB,IAAI,CAAC;IACrD,SAAS,WAAW;KAClB,OAAO,eAAA,GAAA,qBAAA,YAAA,CAA0B,IAAI,KAAK;KAC1C,KAAK,MAAM,EAAE,IAAI,WAAW,MAAM,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK;IACtE,CAAC;GACH;GAEA,MAAM,UAAW,QAAQ,cAAc,QAAQ;GAC/C,OAAO,QAAQ,KAAK,QAAQ,OAAO,IAAI;EACzC;EAGA,IAAI,QAAQ,UAAU;GACpB,MAAM,MAAM,QAAQ;GACpB,MAAM,OAAO,SAAS,cAAc,KAAK;GACzC,MAAM,SAAS,SAAS,cAAc,KAAK;GAC3C,OAAO,YAAY;GACnB,KAAK,YAAY,MAAM;GAKvB,MAAM,cAID,CAAC;GACN,KAAK,MAAM,SAAS,IAAI,gBAAgB,CAAC,GAAG;IAC1C,MAAM,QAAQ,SAAS,cAAc,KAAK;IAC1C,MAAM,YAAY;IAClB,MAAM,QAAQ,SAAS,cAAc,MAAM;IAC3C,MAAM,YAAY;IAClB,MAAM,MAAM,SAAS,cAAc,KAAK;IACxC,IAAI,YAAY;IAChB,MAAM,OAAO,eAAe,MAAM;IAClC,MAAM,OAAO,MAAM,QAAQ,KAAK,QAAQ;KACtC,MAAM,QAAQ,SAAS,cAAc,OAAO;KAC5C,MAAM,OAAO;KACb,MAAM,OAAO;KACb,MAAM,YAAY;KAClB,MAAM,QAAQ,IAAI;KAClB,MAAM,UAAU,IAAI,UAAU,MAAM;KACpC,MAAM,iBAAuB,MAAM,SAAS,MAAM,KAAK;KACvD,MAAM,iBAAiB,UAAU,QAAQ;KACzC,SAAS,WAAW,MAAM,oBAAoB,UAAU,QAAQ,CAAC;KACjE,IAAI,YAAY,KAAK;KACrB,OAAO;MAAE;MAAO,OAAO,IAAI;KAAM;IACnC,CAAC;IACD,MAAM,OAAO,OAAO,GAAG;IACvB,KAAK,YAAY,KAAK;IACtB,YAAY,KAAK;KAAE,IAAI;KAAO,OAAO,MAAM;KAAO;IAAK,CAAC;GAC1D;GAEA,MAAM,OAAO,SAAS,cAAc,KAAK;GACzC,KAAK,YAAY;GACjB,KAAK,YAAY,IAAI;GACrB,MAAM,QAAqD,CAAC;GAC5D,KAAK,MAAM,QAAQ,IAAI,OAAO;IAC5B,MAAM,MAAM,SAAS,cAAc,OAAO;IAC1C,IAAI,YAAY;IAChB,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,OAAO;IACb,MAAM,YAAY;IAClB,MAAM,UAAU,KAAK;IACrB,MAAM,iBAAuB,IAAI,SAAS,KAAK,KAAK,MAAM,OAAO;IACjE,MAAM,iBAAiB,UAAU,QAAQ;IACzC,SAAS,WAAW,MAAM,oBAAoB,UAAU,QAAQ,CAAC;IACjE,MAAM,OAAO,SAAS,cAAc,MAAM;IAC1C,KAAK,YAAY;IACjB,IAAI,OAAO,OAAO,IAAI;IACtB,KAAK,YAAY,GAAG;IACpB,MAAM,KAAK;KAAE,IAAI;KAAM,OAAO,KAAK;IAAM,CAAC;GAC5C;GAEA,MAAM,eAA4D,CAAC;GACnE,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,GAAG;IACtC,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,YAAY;IAClB,MAAM,OAAO,SAAS,cAAc,MAAM;IAC1C,KAAK,YAAY;IACjB,MAAM,OAAO,SAAS,cAAc,MAAM;IAC1C,MAAM,QAAQ,SAAS,cAAc,MAAM;IAC3C,MAAM,YAAY;IAClB,KAAK,OAAO,MAAM,KAAK;IACvB,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,OAAO;IACb,MAAM,YAAY;IAClB,MAAM,MAAM,OAAO,OAAO,GAAG;IAC7B,MAAM,MAAM,OAAO,OAAO,GAAG;IAC7B,MAAM,OAAO,OAAO,OAAO,QAAQ,EAAG;IACtC,MAAM,QAAQ,OAAO,OAAO,KAAK;IACjC,MAAM,cAAc,MAAM;IAC1B,MAAM,gBAAsB;KAC1B,MAAM,cAAc,MAAM;KAC1B,OAAO,SAAS,WAAW,MAAM,KAAK,CAAC;IACzC;IACA,MAAM,iBAAiB,SAAS,OAAO;IACvC,SAAS,WAAW,MAAM,oBAAoB,SAAS,OAAO,CAAC;IAC/D,MAAM,OAAO,MAAM,KAAK;IACxB,KAAK,YAAY,KAAK;IACtB,aAAa,KAAK;KAAE,IAAI;KAAM,OAAO,OAAO;IAAM,CAAC;GACrD;GAEA,SAAS,WAAW;IAClB,OAAO,eAAA,GAAA,qBAAA,YAAA,CAA0B,IAAI,KAAK;IAC1C,KAAK,MAAM,EAAE,IAAI,OAAO,UAAU,aAAa;KAC7C,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK;KAElC,KAAK,MAAM,EAAE,OAAO,OAAO,OAAO,MAAM,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,CAAC,CAAC;IACzF;IACA,KAAK,MAAM,EAAE,IAAI,WAAW,OAAO,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK;IACrE,KAAK,MAAM,EAAE,IAAI,WAAW,cAAc,GAAG,eAAA,GAAA,qBAAA,YAAA,CAA0B,KAAK;GAC9E,CAAC;GACD,OAAO,IAAI,KAAK,IAAI,OAAO,IAAI;EACjC;EAEA,MAAM,WAAW,SAAS,cAAc,MAAM;EAC9C,SAAS,SAAS;EAClB,SAAS,YAAY;EACrB,MAAM,cAAc,SAAS,cAAc,QAAQ;EACnD,YAAY,cAAc;EAC1B,SAAS,YAAY,WAAW;EAEhC,OAAO,OAAO,KAAK,QAAQ;EAC3B,KAAK,OAAO,QAAQ,MAAM;EAE1B,MAAM,SAAS,WAAW,WAAW;GACnC,MAAM,SAAA,GAAA,qBAAA,YAAA,CAAoB,QAAQ,KAAK;GACvC,OAAO,aAAa,cAAc,KAAK;GACvC,QAAQ,cAAc;GACtB,MAAM,aAAa,eAAA,GAAA,qBAAA,YAAA,CAA0B,QAAQ,cAAc,OAAO,CAAC;GAC3E,KAAK,MAAM,WAAW,UAAU,QAAQ;EAC1C,CAAC;EAED,MAAM,eAAqB,OAAO,UAAU;EAC5C,MAAM,gBAAsB,OAAO,MAAM;EACzC,OAAO,iBAAiB,SAAS,MAAM;EACvC,MAAM,iBAAiB,SAAS,OAAO;EAEvC,OAAO;GACL,SAAS;GACT,eAAe;IACb,OAAO,oBAAoB,SAAS,MAAM;IAC1C,MAAM,oBAAoB,SAAS,OAAO;IAC1C,KAAK,MAAM,MAAM,UAAU,GAAG;IAC9B,OAAO;GACT;EACF;CACF;AACF;;;;;;;;;ACnVA,SAAgB,cAAc,SAAkB,UAAgC,CAAC,GAAS;CACxF,QAAQ,IACN,aACA,WAAW,EAAE,OAAO;EAAE,IAAI,WAAW,SAAS;EAAG,KAAK,WAAW,SAAS;CAAE,EAAE,CAAC,CACjF;CACA,QAAQ,IACN,aACA,iBAAiB,EAAE,OAAO;EAAE,OAAO,WAAW,UAAU;EAAG,MAAM,WAAW,UAAU;CAAE,EAAE,CAAC,CAC7F;CAEA,IAAI,QAAQ,WACV,QAAQ,IACN,aACA,gBAAgB;EACd,MAAM,WAAW,OAAO;EACxB,GAAI,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY,CAAC;CACnE,CAAC,CACH;CAEF,IAAI,QAAQ,UAAU;EACpB,MAAM,YAAY,eAAe,0BAA0B,CAAC;EAC5D,MAAM,QAAQ,QAAQ,SAAS,MAAM,QAAQ,MAAM,UAAU,SAAS,EAAE,IAAI,CAAC;EAC7E,IAAI,MAAM,UAAU,GAAG,QAAQ,IAAI,aAAa,eAAe;GAAE,GAAG,QAAQ;GAAU;EAAM,CAAC,CAAC;CAChG;CACA,IAAI,QAAQ,UAAU,QAAQ,IAAI,aAAa,cAAc,QAAQ,QAAQ,CAAC;CAE9E,IAAI,QAAQ,aACV,QAAQ,IAAI,aAAa,iBAAiB;EAAE,MAAM,WAAW,UAAU;EAAG,GAAG,QAAQ;CAAY,CAAC,CAAC;CAGrG,IAAI,QAAQ,WACV,QAAQ,IACN,aACA,gBAAgB;EACd,MAAM,WAAW,kBAAkB;EACnC,GAAI,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY,CAAC;CACnE,CAAC,CACH;CACF,IAAI,QAAQ,UAAU,QAAQ,IAAI,eAAe,YAAY,QAAQ,QAAQ,CAAC;CAG9E,IAAI,QAAQ,MAAM,QAAQ,IAAI,gBAAgB,UAAU,QAAQ,IAAI,CAAC;MAChE,IAAI,QAAQ,MAAM,QAAQ,IAAI,gBAAgB,UAAU,QAAQ,IAAI,CAAC;CAC1E,IAAI,QAAQ,SACV,QAAQ,IACN,gBACA,aAAa;EAAE,OAAO,QAAQ;EAAS,OAAO,QAAQ;EAAc,MAAM,WAAW,UAAU;CAAE,CAAC,CACpG;CAGF,IAAI,QAAQ,cAAc,OACxB,QAAQ,IAAI,eAAe,UAAU,QAAQ,aAAa,CAAC,CAAC,CAAC;AAEjE"}