@scenography/overlay-presets 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +902 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +337 -0
- package/dist/index.d.mts +337 -0
- package/dist/index.mjs +890 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +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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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { Overlay, OverlayComponent, OverlayContext, Text } from "@scenography/overlay";
|
|
2
|
+
import { Artwork } from "@scenography/plugin-artwork/data";
|
|
3
|
+
|
|
4
|
+
//#region src/util.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Button/label content: a glyph string, a DOM node (e.g. an SVG icon), or a
|
|
7
|
+
* factory returning either. A factory is handy for icons that must be a fresh
|
|
8
|
+
* node each time the content is swapped (a DOM node can only live in one place).
|
|
9
|
+
*/
|
|
10
|
+
type IconContent = string | Node | (() => string | Node);
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/lucide.d.ts
|
|
13
|
+
/**
|
|
14
|
+
* Render a Lucide icon by name as overlay button content.
|
|
15
|
+
*
|
|
16
|
+
* Lucide is an OPTIONAL peer dependency: it's imported lazily, so the overlay
|
|
17
|
+
* works without it. When lucide isn't installed the icon renders empty — install
|
|
18
|
+
* `lucide`, pass your own `icon` to a prefab, or use `actionButton` if you don't
|
|
19
|
+
* want the dependency.
|
|
20
|
+
*/
|
|
21
|
+
declare function lucideIcon(name: string, size?: number): IconContent;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/components/fullscreen/fullscreen.d.ts
|
|
24
|
+
interface FullscreenButtonOptions {
|
|
25
|
+
/** Icons for the enter/exit states. Default to plain glyphs ⛶ / ✕. */
|
|
26
|
+
icons?: {
|
|
27
|
+
enter: IconContent;
|
|
28
|
+
exit: IconContent;
|
|
29
|
+
};
|
|
30
|
+
/** Override the daisyUI/utility classes on the button. */
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A fullscreen toggle. daisyUI prefab (styled with `btn`). Defaults to plain
|
|
35
|
+
* glyphs and pulls in no icon library; pass `icons` (e.g. via `lucideIcon` or
|
|
36
|
+
* your own SVGs) for richer iconography.
|
|
37
|
+
*/
|
|
38
|
+
declare function fullscreenButton(options?: FullscreenButtonOptions): OverlayComponent;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/components/mute/mute.d.ts
|
|
41
|
+
interface MuteButtonOptions {
|
|
42
|
+
/** Icons for the unmuted/muted states. Default to plain glyphs 🔊 / 🔇. */
|
|
43
|
+
icons?: {
|
|
44
|
+
on: IconContent;
|
|
45
|
+
off: IconContent;
|
|
46
|
+
};
|
|
47
|
+
/** Override the daisyUI/utility classes on the button. */
|
|
48
|
+
className?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A mute toggle for background audio. daisyUI prefab (styled with `btn`). Drives
|
|
52
|
+
* `ctx.media` master mute, so it silences every source while keeping their
|
|
53
|
+
* individual volumes — one click restores the mix exactly. Defaults to plain
|
|
54
|
+
* glyphs (no icon library); pass `icons` (e.g. `lucideIcon`) for richer icons.
|
|
55
|
+
*/
|
|
56
|
+
declare function muteButton(options?: MuteButtonOptions): OverlayComponent;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/components/cardboard/cardboard.d.ts
|
|
59
|
+
interface CardboardButtonOptions {
|
|
60
|
+
/** Button icon. Defaults to a plain glyph 🥽. */
|
|
61
|
+
icon?: IconContent;
|
|
62
|
+
/** Aria-label. Defaults to "Cardboard mode". */
|
|
63
|
+
label?: string;
|
|
64
|
+
/** Override the daisyUI/utility classes on the button. */
|
|
65
|
+
className?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A cardboard (stereoscopic) toggle. Flips `ctx.gallery.setRender({ cardboard })`
|
|
69
|
+
* to split the view into a left/right eye pair for a phone-in-a-headset viewer,
|
|
70
|
+
* and reflects the on state with daisyUI's `btn-active`. Defaults to a plain
|
|
71
|
+
* glyph; pass `icon` (e.g. `lucideIcon("RectangleGoggles")`).
|
|
72
|
+
*/
|
|
73
|
+
declare function cardboardButton(options?: CardboardButtonOptions): OverlayComponent;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/components/settings/settings.d.ts
|
|
76
|
+
/** One option in a single-choice section. */
|
|
77
|
+
interface SettingsChoice {
|
|
78
|
+
value: string;
|
|
79
|
+
label: Text;
|
|
80
|
+
}
|
|
81
|
+
/** One boolean toggle in the multi-toggle section. */
|
|
82
|
+
interface SettingsToggleItem {
|
|
83
|
+
key: string;
|
|
84
|
+
label: Text;
|
|
85
|
+
value: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* One single-choice control rendered as a joined row of radio-buttons — a
|
|
89
|
+
* segmented control (e.g. antialias level, bloom strength).
|
|
90
|
+
*/
|
|
91
|
+
interface SettingsChoiceGroup {
|
|
92
|
+
key: string;
|
|
93
|
+
label: Text;
|
|
94
|
+
/** Current value (matches one option's `value`). */
|
|
95
|
+
value: string;
|
|
96
|
+
options: SettingsChoice[];
|
|
97
|
+
onChange: (value: string) => void;
|
|
98
|
+
}
|
|
99
|
+
/** One numeric slider (range) in the graphics section. */
|
|
100
|
+
interface SettingsSliderItem {
|
|
101
|
+
key: string;
|
|
102
|
+
label: Text;
|
|
103
|
+
value: number;
|
|
104
|
+
min: number;
|
|
105
|
+
max: number;
|
|
106
|
+
/** Step granularity. Defaults to `0.1`. */
|
|
107
|
+
step?: number;
|
|
108
|
+
onChange: (value: number) => void;
|
|
109
|
+
}
|
|
110
|
+
interface SettingsPanelOptions {
|
|
111
|
+
/** Modal title, also the gear button's aria-label. */
|
|
112
|
+
title: Text;
|
|
113
|
+
/** Aria-label for the ✕ close button. Defaults to "Close". */
|
|
114
|
+
closeLabel?: Text;
|
|
115
|
+
/**
|
|
116
|
+
* A single-choice section rendered as a `<select>` (e.g. language). Shown in
|
|
117
|
+
* its own tab; `tab` is the tab's title (falls back to `label`).
|
|
118
|
+
*/
|
|
119
|
+
language?: {
|
|
120
|
+
tab?: Text;
|
|
121
|
+
label: Text;
|
|
122
|
+
value: string;
|
|
123
|
+
options: SettingsChoice[];
|
|
124
|
+
onChange: (value: string) => void;
|
|
125
|
+
};
|
|
126
|
+
/** A single-choice section rendered as radios (e.g. handedness). Own tab. */
|
|
127
|
+
handedness?: {
|
|
128
|
+
tab?: Text;
|
|
129
|
+
label: Text;
|
|
130
|
+
value: string;
|
|
131
|
+
options: SettingsChoice[];
|
|
132
|
+
onChange: (value: string) => void;
|
|
133
|
+
};
|
|
134
|
+
/** A multi-toggle section (e.g. graphics: ssao / bloom / shadows). Own tab. */
|
|
135
|
+
graphics?: {
|
|
136
|
+
tab?: Text;
|
|
137
|
+
label: Text;
|
|
138
|
+
items: SettingsToggleItem[];
|
|
139
|
+
onChange: (key: string, value: boolean) => void; /** Optional segmented radio-button groups above the toggles (e.g. antialias / bloom). */
|
|
140
|
+
choiceGroups?: SettingsChoiceGroup[]; /** Optional numeric sliders shown below the toggles (e.g. eye separation). */
|
|
141
|
+
sliders?: SettingsSliderItem[];
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A settings panel: a ⚙ button that opens a centered daisyUI modal (with an ✕
|
|
146
|
+
* close, click-outside and Esc). Each section lives in its own tab (the first
|
|
147
|
+
* present one opens by default). Sections are data — the app supplies labels,
|
|
148
|
+
* current values and callbacks — so the panel stays generic. Labels are `Text`
|
|
149
|
+
* and relocalize live via the overlay's locale source. daisyUI + Lucide prefab.
|
|
150
|
+
*/
|
|
151
|
+
declare function settingsPanel(options: SettingsPanelOptions): OverlayComponent;
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/components/environment/environment.d.ts
|
|
154
|
+
interface EnvironmentPanelOptions {
|
|
155
|
+
/** Modal title, also the button's aria-label. */
|
|
156
|
+
title: Text;
|
|
157
|
+
/** Aria-label for the ✕ close button. Defaults to "Close". */
|
|
158
|
+
closeLabel?: Text;
|
|
159
|
+
/** Button icon. Defaults to a plain glyph ☀. */
|
|
160
|
+
icon?: IconContent;
|
|
161
|
+
/** Sliders shown in the modal (e.g. time of day, sun azimuth). */
|
|
162
|
+
sliders: SettingsSliderItem[];
|
|
163
|
+
/** Quick presets shown as buttons; each sets one or more sliders by `key`. */
|
|
164
|
+
presets?: {
|
|
165
|
+
label: Text;
|
|
166
|
+
values: Record<string, number>;
|
|
167
|
+
}[];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* An environment panel: a button (e.g. a sun/cloud icon) opening a daisyUI modal
|
|
171
|
+
* with sliders for ambient settings like time of day and sun azimuth. Optional
|
|
172
|
+
* in the default preset — galleries with a procedural sky opt in. daisyUI +
|
|
173
|
+
* Lucide prefab.
|
|
174
|
+
*/
|
|
175
|
+
declare function environmentPanel(options: EnvironmentPanelOptions): OverlayComponent;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/components/credits/credits.d.ts
|
|
178
|
+
/** One acknowledged asset: what it is, who made it, where it's from. */
|
|
179
|
+
interface CreditItem {
|
|
180
|
+
/** Discriminant; optional so a plain item needs no `type`. */
|
|
181
|
+
type?: "item";
|
|
182
|
+
/** Display name, e.g. "Brick Wall 006". */
|
|
183
|
+
label: string;
|
|
184
|
+
/** Author/creator. */
|
|
185
|
+
author?: string;
|
|
186
|
+
/** Source URL; turns `label` into a link. */
|
|
187
|
+
href?: string;
|
|
188
|
+
/** License, e.g. "CC0". */
|
|
189
|
+
license?: string;
|
|
190
|
+
}
|
|
191
|
+
/** A labeled separator within a flat list or a tab's children. */
|
|
192
|
+
interface CreditSection {
|
|
193
|
+
type: "section";
|
|
194
|
+
/** Section heading, e.g. "Textures". */
|
|
195
|
+
label: string;
|
|
196
|
+
}
|
|
197
|
+
/** A tab grouping a list of items/sections under a title. */
|
|
198
|
+
interface CreditTab {
|
|
199
|
+
type: "tab";
|
|
200
|
+
/** Tab title. */
|
|
201
|
+
label: string;
|
|
202
|
+
/** The grouped entries (items and sections; no nested tabs). */
|
|
203
|
+
children: Array<CreditItem | CreditSection>;
|
|
204
|
+
}
|
|
205
|
+
/** Anything that can appear at the top level of the credits. */
|
|
206
|
+
type CreditEntry = CreditItem | CreditSection | CreditTab;
|
|
207
|
+
interface CreditsPanelOptions {
|
|
208
|
+
/**
|
|
209
|
+
* The credited entries. Loose items/sections render first (above the tabs);
|
|
210
|
+
* tabs render after, as a daisyUI tab strip. A getter is re-read on locale
|
|
211
|
+
* change to relocalize.
|
|
212
|
+
*/
|
|
213
|
+
items: CreditEntry[] | (() => CreditEntry[]);
|
|
214
|
+
/** Panel heading. Defaults to "Credits". */
|
|
215
|
+
title?: Text;
|
|
216
|
+
/** Button icon. Defaults to the plain glyph ⓘ. */
|
|
217
|
+
icon?: IconContent;
|
|
218
|
+
/** Override the daisyUI/utility classes on the toggle button. */
|
|
219
|
+
className?: string;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A credits panel: a button opening a centered daisyUI modal (✕ close,
|
|
223
|
+
* click-outside, Esc) listing attributions. Entries are data (`items`): loose
|
|
224
|
+
* items and sections show first; `tab` entries become a daisyUI tab strip below
|
|
225
|
+
* (first tab open), so an exhibition can credit its author up top and group
|
|
226
|
+
* assets/technology/etc. into tabs with sectioned lists. Defaults to a plain
|
|
227
|
+
* glyph icon; pass `icon` (e.g. `lucideIcon`).
|
|
228
|
+
*/
|
|
229
|
+
declare function creditsPanel(options: CreditsPanelOptions): OverlayComponent;
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/components/action-button/action-button.d.ts
|
|
232
|
+
interface ActionButtonOptions {
|
|
233
|
+
/** Click handler; receives the overlay context (gallery, surface, root). */
|
|
234
|
+
onClick: (ctx: OverlayContext) => void;
|
|
235
|
+
/** Icon content: a glyph, an SVG node, or a factory. No lucide required. */
|
|
236
|
+
icon?: IconContent;
|
|
237
|
+
/** Text label, placed after the icon. */
|
|
238
|
+
label?: string;
|
|
239
|
+
/** Accessible label (use it when there's an icon but no text). */
|
|
240
|
+
ariaLabel?: string;
|
|
241
|
+
/** Override the daisyUI/utility classes. */
|
|
242
|
+
className?: string;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* A generic clickable button — the lucide-free alternative to the opinionated
|
|
246
|
+
* prefabs. Supply any `icon` (glyph, your own SVG, a Lucide icon you wired up)
|
|
247
|
+
* and an `onClick`. daisyUI prefab (styled with `btn`).
|
|
248
|
+
*/
|
|
249
|
+
declare function actionButton(options: ActionButtonOptions): OverlayComponent;
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/components/info/info.d.ts
|
|
252
|
+
interface InfoPanelOptions {
|
|
253
|
+
/** Modal title, also the ⓘ button's aria-label. */
|
|
254
|
+
title: Text;
|
|
255
|
+
/** Body text shown in the modal (e.g. the control instructions). */
|
|
256
|
+
text: Text;
|
|
257
|
+
/** Aria-label for the ✕ close button. Defaults to "Close". */
|
|
258
|
+
closeLabel?: Text;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* An info panel: an ⓘ button that opens a centered daisyUI modal with a title
|
|
262
|
+
* and a block of text (with an ✕ close, click-outside and Esc). Content is data,
|
|
263
|
+
* so the app supplies the (already localized) strings. daisyUI + Lucide prefab.
|
|
264
|
+
*/
|
|
265
|
+
declare function infoPanel(options: InfoPanelOptions): OverlayComponent;
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/components/artwork-info/artwork-info.d.ts
|
|
268
|
+
interface ArtworkInfoOptions {
|
|
269
|
+
/** The artworks' data, looked up by `id` on select. Usually `scene.artworks`. */
|
|
270
|
+
artworks: Artwork[];
|
|
271
|
+
/** Aria-label for the panel's ✕ close button. Defaults to "Close". */
|
|
272
|
+
closeLabel?: string;
|
|
273
|
+
/**
|
|
274
|
+
* Availability wording shown in the panel. `'for-sale'` reads `forSale`; both
|
|
275
|
+
* negative states (`'sold'` / `'not-for-sale'`) read `privateCollection`, since
|
|
276
|
+
* publicly they mean the same: not available. Defaults to English.
|
|
277
|
+
*/
|
|
278
|
+
availabilityText?: {
|
|
279
|
+
forSale?: string;
|
|
280
|
+
privateCollection?: string;
|
|
281
|
+
};
|
|
282
|
+
/** Label for the link shown when an artwork has an `href`. Defaults to "View artwork". */
|
|
283
|
+
linkText?: string;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The artwork info panel. The engine emits `artwork:select { id }` when you click
|
|
287
|
+
* a piece's cartela (or the piece itself); this opens a left-hand panel (40%
|
|
288
|
+
* width, full height, black at 85% alpha) with the full caption and asks the
|
|
289
|
+
* camera to frame the piece in the free right 60%. Closing with ✕ re-centers the
|
|
290
|
+
* piece full-width; if the user walks/turns away the engine emits
|
|
291
|
+
* `artwork:released` and the panel just closes (no camera move).
|
|
292
|
+
*
|
|
293
|
+
* Content is data (`artworks`), unlocalized for now. The panel is mounted on the
|
|
294
|
+
* surface (escaping the overlay grid); the slot element is an inert placeholder.
|
|
295
|
+
*/
|
|
296
|
+
declare function artworkInfo(options: ArtworkInfoOptions): OverlayComponent;
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/components/watermark/watermark.d.ts
|
|
299
|
+
interface WatermarkOptions {
|
|
300
|
+
/** Text shown. Defaults to "scenography". */
|
|
301
|
+
text?: string;
|
|
302
|
+
/** Replace the default styling with your own class. */
|
|
303
|
+
className?: string;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A small, unobtrusive text watermark (a "brand stamp"). No daisyUI/Lucide — just
|
|
307
|
+
* a styled element. Defaults to the text "scenography".
|
|
308
|
+
*/
|
|
309
|
+
declare function watermark(options?: WatermarkOptions): OverlayComponent;
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/presets/default.d.ts
|
|
312
|
+
interface DefaultPresetOptions {
|
|
313
|
+
/** Settings panel (language / handedness / graphics); when given, a ⚙ button is added top-right. */
|
|
314
|
+
settings?: SettingsPanelOptions;
|
|
315
|
+
/** Environment panel (time of day, sun azimuth); when given, a ☼ button is added top-right. Off by default. */
|
|
316
|
+
environment?: EnvironmentPanelOptions;
|
|
317
|
+
/** Info panel (e.g. control instructions); when given, an ⓘ button is added bottom-right, above credits. */
|
|
318
|
+
info?: InfoPanelOptions;
|
|
319
|
+
/** Artwork info panel; when given, clicking a piece opens its caption on the left. */
|
|
320
|
+
artworks?: ArtworkInfoOptions;
|
|
321
|
+
/** Asset attributions; when given, a credits panel is added bottom-right. */
|
|
322
|
+
credits?: CreditEntry[] | (() => CreditEntry[]);
|
|
323
|
+
/** Credits panel title. */
|
|
324
|
+
creditsTitle?: Text;
|
|
325
|
+
/** Bottom-left watermark. Defaults to "scenography"; pass `false` to hide it. */
|
|
326
|
+
watermark?: WatermarkOptions | false;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The default overlay layout: mute + fullscreen (and a settings ⚙ when given)
|
|
330
|
+
* top-right; an info ⓘ and a credits panel bottom-right when given. Wired with
|
|
331
|
+
* Lucide icons — this is the opinionated, batteries-included layer, so the
|
|
332
|
+
* coupling to daisyUI + Lucide lives here, not in `@scenography/overlay`.
|
|
333
|
+
*/
|
|
334
|
+
declare function defaultPreset(overlay: Overlay, options?: DefaultPresetOptions): void;
|
|
335
|
+
//#endregion
|
|
336
|
+
export { type ActionButtonOptions, type ArtworkInfoOptions, type CardboardButtonOptions, type CreditEntry, type CreditItem, type CreditSection, type CreditTab, type CreditsPanelOptions, type DefaultPresetOptions, type EnvironmentPanelOptions, type FullscreenButtonOptions, type IconContent, type InfoPanelOptions, type MuteButtonOptions, type SettingsChoice, type SettingsChoiceGroup, type SettingsPanelOptions, type SettingsSliderItem, type SettingsToggleItem, type WatermarkOptions, actionButton, artworkInfo, cardboardButton, creditsPanel, defaultPreset, environmentPanel, fullscreenButton, infoPanel, lucideIcon, muteButton, settingsPanel, watermark };
|
|
337
|
+
//# sourceMappingURL=index.d.cts.map
|