@wildo-ai/presets-components-3d 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/focal-plane/focal-plane-settings.d.ts +35 -0
- package/dist/esm/focal-plane/focal-plane-settings.d.ts.map +1 -0
- package/dist/esm/focal-plane/focal-plane-settings.js +68 -0
- package/dist/esm/focal-plane/focal-plane-settings.js.map +1 -0
- package/dist/esm/focal-plane/focal-plane-shell-container.d.ts +13 -0
- package/dist/esm/focal-plane/focal-plane-shell-container.d.ts.map +1 -0
- package/dist/esm/focal-plane/focal-plane-shell-container.js +147 -0
- package/dist/esm/focal-plane/focal-plane-shell-container.js.map +1 -0
- package/dist/esm/focal-plane/focal-plane-stylesheet.d.ts +14 -0
- package/dist/esm/focal-plane/focal-plane-stylesheet.d.ts.map +1 -0
- package/dist/esm/focal-plane/focal-plane-stylesheet.js +236 -0
- package/dist/esm/focal-plane/focal-plane-stylesheet.js.map +1 -0
- package/dist/esm/focal-plane/use-pane-depth.d.ts +20 -0
- package/dist/esm/focal-plane/use-pane-depth.d.ts.map +1 -0
- package/dist/esm/focal-plane/use-pane-depth.js +162 -0
- package/dist/esm/focal-plane/use-pane-depth.js.map +1 -0
- package/dist/esm/focal-plane/use-shared-vanishing-point.d.ts +35 -0
- package/dist/esm/focal-plane/use-shared-vanishing-point.d.ts.map +1 -0
- package/dist/esm/focal-plane/use-shared-vanishing-point.js +86 -0
- package/dist/esm/focal-plane/use-shared-vanishing-point.js.map +1 -0
- package/dist/esm/frame/frame-3d-style.d.ts.map +1 -1
- package/dist/esm/frame/frame-3d-style.js.map +1 -1
- package/dist/esm/frame/frame-appearance.d.ts.map +1 -1
- package/dist/esm/frame/frame-appearance.js.map +1 -1
- package/dist/esm/frame/frame-canvas.d.ts.map +1 -1
- package/dist/esm/frame/frame-canvas.js +3 -3
- package/dist/esm/frame/frame-canvas.js.map +1 -1
- package/dist/esm/frame/frame-canvas.lazy.d.ts.map +1 -1
- package/dist/esm/frame/frame-canvas.lazy.js.map +1 -1
- package/dist/esm/frame/frame-color.d.ts.map +1 -1
- package/dist/esm/frame/frame-color.js.map +1 -1
- package/dist/esm/frame/frame-driver.d.ts.map +1 -1
- package/dist/esm/frame/frame-driver.js.map +1 -1
- package/dist/esm/frame/frame-renderer.d.ts.map +1 -1
- package/dist/esm/frame/frame-renderer.js.map +1 -1
- package/dist/esm/frame/frame-shaders.d.ts.map +1 -1
- package/dist/esm/frame/frame-shaders.js.map +1 -1
- package/dist/esm/frame/use-frame-appearance.d.ts.map +1 -1
- package/dist/esm/frame/use-frame-appearance.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/layout/header-first-shell-container.d.ts.map +1 -1
- package/dist/esm/layout/header-first-shell-container.js.map +1 -1
- package/dist/esm/layout/layout-3d-definition.d.ts +5 -6
- package/dist/esm/layout/layout-3d-definition.d.ts.map +1 -1
- package/dist/esm/layout/layout-3d-definition.js +13 -14
- package/dist/esm/layout/layout-3d-definition.js.map +1 -1
- package/dist/esm/layout/layout-3d-transition-choreography.d.ts.map +1 -1
- package/dist/esm/layout/layout-3d-transition-choreography.js.map +1 -1
- package/dist/esm/layout/layout-3d-vocabulary.d.ts +13 -8
- package/dist/esm/layout/layout-3d-vocabulary.d.ts.map +1 -1
- package/dist/esm/layout/layout-3d-vocabulary.js +12 -7
- package/dist/esm/layout/layout-3d-vocabulary.js.map +1 -1
- package/dist/esm/layout/shell-layout-placement.d.ts.map +1 -1
- package/dist/esm/layout/shell-layout-placement.js.map +1 -1
- package/dist/esm/layout-3d-main-container.d.ts +6 -0
- package/dist/esm/layout-3d-main-container.d.ts.map +1 -1
- package/dist/esm/layout-3d-main-container.js +8 -1
- package/dist/esm/layout-3d-main-container.js.map +1 -1
- package/dist/esm/register-3d-layout.d.ts.map +1 -1
- package/dist/esm/register-3d-layout.js.map +1 -1
- package/dist/esm/shell-region-measurement.d.ts.map +1 -1
- package/dist/esm/shell-region-measurement.js.map +1 -1
- package/dist/esm/tokens/design-token-resolution.d.ts.map +1 -1
- package/dist/esm/tokens/design-token-resolution.js.map +1 -1
- package/dist/esm/tokens/use-design-token-resolution.d.ts.map +1 -1
- package/dist/esm/tokens/use-design-token-resolution.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/dist/esm/spatial/spatial-camera.d.ts +0 -106
- package/dist/esm/spatial/spatial-camera.d.ts.map +0 -1
- package/dist/esm/spatial/spatial-camera.js +0 -151
- package/dist/esm/spatial/spatial-camera.js.map +0 -1
- package/dist/esm/spatial/spatial-environment.d.ts +0 -6
- package/dist/esm/spatial/spatial-environment.d.ts.map +0 -1
- package/dist/esm/spatial/spatial-environment.js +0 -166
- package/dist/esm/spatial/spatial-environment.js.map +0 -1
- package/dist/esm/spatial/spatial-environment.lazy.d.ts +0 -10
- package/dist/esm/spatial/spatial-environment.lazy.d.ts.map +0 -1
- package/dist/esm/spatial/spatial-environment.lazy.js +0 -13
- package/dist/esm/spatial/spatial-environment.lazy.js.map +0 -1
- package/dist/esm/spatial/spatial-shell-container.d.ts +0 -21
- package/dist/esm/spatial/spatial-shell-container.d.ts.map +0 -1
- package/dist/esm/spatial/spatial-shell-container.js +0 -231
- package/dist/esm/spatial/spatial-shell-container.js.map +0 -1
- package/dist/esm/spatial/spatial-stage-store.d.ts +0 -27
- package/dist/esm/spatial/spatial-stage-store.d.ts.map +0 -1
- package/dist/esm/spatial/spatial-stage-store.js +0 -33
- package/dist/esm/spatial/spatial-stage-store.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focal-plane-stylesheet.js","sourceRoot":"","sources":["../../../src/focal-plane/focal-plane-stylesheet.ts"],"names":[],"mappings":"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","sourcesContent":["import { ShellRegion } from '@wildo-ai/presets-components-models';\nimport { NavigationZone } from '@wildo-ai/saas-frontend-lib';\n\n/**\n * The focal plane's stylesheet (#2049), scoped to `[data-focal-plane]`, so it touches nothing outside the layout.\n *\n * ## One still camera, without a nested 3D context\n *\n * Every object carries its OWN `perspective(d) translate3d(0, 0, z)`, with its `transform-origin` set (per element, by\n * `useSharedVanishingPoint`) to the one vanishing point of the stage. That is the projection of a single still camera,\n * and it needs no `transform-style: preserve-3d` ancestor — which matters, because a nested 3D context disables\n * `backdrop-filter`, and the glass windows are made of it.\n *\n * ## What counts as an object\n *\n * - the shell panels this layout wraps (`[data-fp-object]`);\n * - each navigation pane inside the content region, by the engine's own markers: `[data-content-area]` (the static and\n * single-zone branches) and the resizable panel `[data-panel-id^=\"nav-\"]` (NOT its inner `[data-content-area]`, which\n * the panel's inline `overflow: hidden` would clip).\n *\n * Depth is a custom property (`--fp-z`); states set it, and the transform reads it, so rules compose instead of fighting\n * over `transform`.\n *\n * ## Why a constructable stylesheet\n *\n * It is installed through `document.adoptedStyleSheets`, which a content security policy without `'unsafe-inline'`\n * does not block, where a `<style>` element would be.\n */\nconst ROOT = '[data-focal-plane]';\nconst CONTENT = `[data-component-area=\"${ShellRegion.CONTENT}\"]`;\n/**\n * The navigation panes, by the engine's markers: the zone itself in the static and single-zone branches, the panel in\n * the resizable branch — never the zone INSIDE a panel, which would be moved twice.\n */\nexport const FOCAL_PLANE_PANE_SELECTOR = `${CONTENT} [data-content-area]:not([data-panel-id] *), ${CONTENT} [data-panel-id^=\"nav-\"]`;\n/** The zone names the navigation state focuses, and the markers each one is found by in either branch. */\nconst ZONES = [NavigationZone.PRIMARY, NavigationZone.SECONDARY, NavigationZone.COMPANION] as const;\nconst zoneMarker = (zone: (typeof ZONES)[number]): string => `:is([data-content-area=\"${zone}\"], [data-panel-id=\"nav-${zone}\"])`;\nconst VISIBLE_PANE = `:is(${FOCAL_PLANE_PANE_SELECTOR}):not([data-zone-hidden])`;\n/** Two panes or more are side by side (the other panes are siblings in both branches). */\nconst SPLIT = `${CONTENT}:has(${VISIBLE_PANE} ~ ${VISIBLE_PANE})`;\nconst PANES = `:is(${FOCAL_PLANE_PANE_SELECTOR})`;\nconst OBJECTS = `:is([data-fp-object], ${FOCAL_PLANE_PANE_SELECTOR})`;\n/**\n * The popups the focal plane moves, by the stable `data-slot` of their portalled content (Radix `data-state` on the\n * same element). Lifting popups carry the CSS variable Radix publishes as their trigger-relative origin.\n */\nconst POPUPS = {\n lift: [\n ['dropdown-menu-content', '--radix-dropdown-menu-content-transform-origin'],\n ['dropdown-menu-sub-content', '--radix-dropdown-menu-content-transform-origin'],\n ['context-menu-content', '--radix-context-menu-content-transform-origin'],\n ['context-menu-sub-content', '--radix-context-menu-content-transform-origin'],\n ['popover-content', '--radix-popover-content-transform-origin'],\n ['hover-card-content', '--radix-hover-card-content-transform-origin'],\n ['tooltip-content', '--radix-tooltip-content-transform-origin'],\n ['select-content', '--radix-select-content-transform-origin'],\n ['menubar-content', '--radix-menubar-content-transform-origin'],\n ['menubar-sub-content', '--radix-menubar-content-transform-origin'],\n ],\n rise: ['dialog-content', 'alert-dialog-content'],\n} as const;\n/**\n * A MODAL popup is open: a dialog, an alert dialog, a sheet or a drawer, by their content SLOT. Not by `role=\"dialog\"`:\n * a Radix popover's content carries that role too (date pickers, linked-record pickers, the notification centre), and\n * opening one must not step the form you are editing back. Not by `body[data-scroll-locked]` either, which dropdown\n * and context menus also set.\n */\nconst MODAL_SLOTS = ['dialog-content', 'alert-dialog-content', 'sheet-content', 'drawer-content'] as const;\nconst MODAL_OPEN = `:has([data-shell-overlay-layer] :is(${MODAL_SLOTS.map((slot) => `[data-slot=\"${slot}\"]`).join(', ')})[data-state=\"open\"])`;\n\n/** The modal content slots, exported for the tests that pin what counts as modal. */\nexport const FOCAL_PLANE_MODAL_SLOTS: readonly string[] = MODAL_SLOTS;\n\nexport const FOCAL_PLANE_CSS = `\n/* Positioned, never isolated: an isolated root would be one stacking context, and anything positioned outside the\n shell with a lower z-index (a consent prompt) would paint over the shell's own dialogs (#1830). */\n${ROOT} { position: relative; height: 100vh; width: 100vw; overflow: hidden; background: var(--background); }\n\n/* The ambient background: soft fields of the theme's own colours, drifting very slowly. */\n${ROOT} .fp-backdrop { position: absolute; inset: -20%; z-index: 0; pointer-events: none; }\n${ROOT} .fp-backdrop i { position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.8; }\n${ROOT} .fp-backdrop .fp-a { width: 58%; height: 62%; left: 6%; top: 4%; background: radial-gradient(closest-side, color-mix(in oklab, var(--primary) 38%, var(--background)), transparent); animation: fp-drift-a 48s ease-in-out infinite alternate; }\n${ROOT} .fp-backdrop .fp-b { width: 52%; height: 58%; right: 4%; top: 30%; background: radial-gradient(closest-side, color-mix(in oklab, var(--accent, var(--primary)) 30%, var(--background)), transparent); animation: fp-drift-b 61s ease-in-out infinite alternate; }\n${ROOT} .fp-backdrop .fp-c { width: 46%; height: 50%; left: 34%; bottom: 0; background: radial-gradient(closest-side, color-mix(in oklab, var(--primary) 22%, var(--foreground) 8%), transparent); animation: fp-drift-c 55s ease-in-out infinite alternate; }\n@keyframes fp-drift-a { to { transform: translate(9%, 7%) scale(1.08); } }\n@keyframes fp-drift-b { to { transform: translate(-8%, -6%) scale(0.94); } }\n@keyframes fp-drift-c { to { transform: translate(-6%, -9%) scale(1.1); } }\n\n/* The arrangement: floating windows with a margin and gaps, never edge to edge. */\n${ROOT} .fp-grid { position: absolute; inset: 0; z-index: 1; display: flex; flex-direction: column; gap: 12px; padding: 14px; }\n${ROOT} .fp-row { display: flex; gap: 12px; min-height: 0; min-width: 0; }\n${ROOT} .fp-middle { flex: 1; }\n${ROOT} .fp-column { flex: 1; display: flex; flex-direction: column; gap: 12px; min-width: 0; min-height: 0; }\n${ROOT} .fp-content { flex: 1; min-height: 0; min-width: 0; position: relative; display: flex; flex-direction: column; }\n${ROOT} [data-fp-object]:empty { display: none; }\n${ROOT} [data-fp-object] { position: relative; display: flex; min-height: 0; min-width: 0; }\n${ROOT} [data-fp-object] > * { flex: 1; min-width: 0; min-height: 0; }\n\n/* Every object: seen through the one still camera, at its depth. */\n${ROOT} ${OBJECTS} {\n transform-origin: var(--fp-ox, 50%) var(--fp-oy, 50%);\n transform: perspective(var(--fp-distance)) translate3d(0, 0, var(--fp-z, 0px));\n filter: var(--fp-filter, none);\n transition: transform var(--fp-motion) var(--fp-ease), filter var(--fp-motion) var(--fp-ease), box-shadow var(--fp-motion) var(--fp-ease);\n}\n\n/* Glass: the theme's card colour, translucent, over the background blurred through it. */\n${ROOT} :is([data-fp-object=\"shell\"], ${FOCAL_PLANE_PANE_SELECTOR}) {\n background: color-mix(in oklab, var(--card, var(--background)) var(--fp-glass), transparent);\n -webkit-backdrop-filter: blur(26px) saturate(1.5);\n backdrop-filter: blur(26px) saturate(1.5);\n border: 1px solid color-mix(in oklab, var(--border) 55%, transparent);\n border-radius: 14px;\n box-shadow: 0 18px 44px -22px rgb(0 0 0 / 0.30), 0 1px 3px rgb(0 0 0 / 0.05);\n}\n${ROOT} ${CONTENT} { background: transparent; }\n${ROOT} ${CONTENT} :is([data-content-area=\"${NavigationZone.SECONDARY}\"], [data-content-area=\"${NavigationZone.COMPANION}\"]):not([data-panel-id] *) { margin-left: 12px; }\n\n/* Side by side, the pane you work in rests on the focal plane; every other pane steps back and dims. The focused\n zone comes from the navigation state, written on the root as data-fp-focus. Clicking a pane focuses it (the\n engine does that), which brings it forward. */\n${ZONES.map((zone) => `${ROOT}[data-fp-focus=\"${zone}\"] ${SPLIT} ${VISIBLE_PANE}:not(${zoneMarker(zone)})`).join(', ')} {\n --fp-z: calc(var(--fp-step) * -0.55);\n --fp-filter: brightness(calc(1 - var(--fp-dim) * 0.55)) saturate(calc(1 - var(--fp-dim) * 0.5)) blur(calc(var(--fp-recede-blur) * 0.55));\n}\n\n/* A side pane arrives from depth when it opens. */\n${ROOT} ${CONTENT} :is(${zoneMarker(NavigationZone.SECONDARY)}, ${zoneMarker(NavigationZone.COMPANION)}):is(${FOCAL_PLANE_PANE_SELECTOR}) {\n animation: fp-pane-arrive var(--fp-motion) var(--fp-ease);\n}\n@keyframes fp-pane-arrive {\n from { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -1.2)); opacity: 0; }\n}\n\n/* Ghosts: a discarded pane flying away, and the still image of the panes an expanded pane stands in front of. */\n${ROOT} .fp-ghosts { position: absolute; inset: 0; z-index: 0; pointer-events: none; }\n${ROOT} .fp-ghosts-front { z-index: 2; }\n${ROOT} .fp-ghost {\n position: absolute; box-sizing: border-box; overflow: hidden;\n transform-origin: var(--fp-ox) var(--fp-oy);\n transform: perspective(var(--fp-distance)) translate3d(0, 0, var(--fp-z, 0px));\n filter: var(--fp-filter, none);\n transition: transform var(--fp-motion) var(--fp-ease), filter var(--fp-motion) var(--fp-ease), opacity var(--fp-motion) var(--fp-ease);\n background: color-mix(in oklab, var(--card, var(--background)) var(--fp-glass), transparent);\n border: 1px solid color-mix(in oklab, var(--border) 55%, transparent);\n border-radius: 14px;\n box-shadow: 0 18px 44px -22px rgb(0 0 0 / 0.30);\n}\n\n/* The shell panel under the pointer rises toward the camera; its items zoom a little. */\n${ROOT} [data-fp-object=\"shell\"]:hover { --fp-z: var(--fp-hover-lift); }\n${ROOT} [data-fp-object=\"shell\"] :is(button, a):not(:disabled) { transition: transform 180ms var(--fp-ease); }\n${ROOT} [data-fp-object=\"shell\"] :is(button, a):not(:disabled):hover { transform: scale(1.045); }\n\n/* Behind a modal popup, everything steps back, dims and blurs lightly. A menu is not modal: it only lifts. */\n${ROOT}${MODAL_OPEN} ${OBJECTS} {\n --fp-z: calc(var(--fp-step) * -0.6);\n --fp-filter: brightness(calc(1 - var(--fp-dim) * 0.6)) saturate(0.9) blur(var(--fp-modal-blur));\n}\n\n/* Popups are objects too. Their recipe animations (tw-animate's enter/exit) are replaced by depth KEYFRAMES —\n keyframes, because the overlay library keeps a closing popup mounted only until its exit ANIMATION ends; a\n transition would be cut off. Each popup rests at its true size: depth shows while it travels.\n - A menu, popover or tooltip lifts off its trigger: the library publishes the trigger point as the transform\n origin, and perspective() grows the popup out of it.\n - A dialog (the command palette is one) rises from depth to the focal plane.\n - A sheet arrives from in front of the plane and leaves back into depth.\n Drawers and toasts keep their own motion: their libraries position and stack them with transforms. */\n${POPUPS.lift.map(([slot, origin]) => `${ROOT} [data-shell-overlay-layer] [data-slot=\"${slot}\"] { transform-origin: var(${origin}); }`).join('\\n')}\n${ROOT} [data-shell-overlay-layer] :is(${POPUPS.lift.map(([slot]) => `[data-slot=\"${slot}\"]`).join(', ')}):is([data-state=\"open\"], [data-state=\"delayed-open\"], [data-state=\"instant-open\"]) { animation: fp-lift-in calc(var(--fp-motion) * 0.6) var(--fp-ease) both; }\n${ROOT} [data-shell-overlay-layer] :is(${POPUPS.lift.map(([slot]) => `[data-slot=\"${slot}\"]`).join(', ')})[data-state=\"closed\"] { animation: fp-lift-out calc(var(--fp-motion) * 0.45) var(--fp-ease) both; }\n${ROOT} [data-shell-overlay-layer] :is(${POPUPS.rise.map((slot) => `[data-slot=\"${slot}\"]`).join(', ')})[data-state=\"open\"] { animation: fp-rise-in var(--fp-motion) var(--fp-ease) both; }\n${ROOT} [data-shell-overlay-layer] :is(${POPUPS.rise.map((slot) => `[data-slot=\"${slot}\"]`).join(', ')})[data-state=\"closed\"] { animation: fp-rise-out calc(var(--fp-motion) * 0.7) var(--fp-ease) both; }\n${ROOT} [data-shell-overlay-layer] [data-slot=\"sheet-content\"][data-state=\"open\"] { animation: fp-arrive-in var(--fp-motion) var(--fp-ease) both; }\n${ROOT} [data-shell-overlay-layer] [data-slot=\"sheet-content\"][data-state=\"closed\"] { animation: fp-arrive-out calc(var(--fp-motion) * 0.7) var(--fp-ease) both; }\n/* Popups are glass, a little more opaque than windows (they carry text you act on at once). Tooltips keep their own\n inverse colours. */\n${ROOT} [data-shell-overlay-layer] :is(${[...POPUPS.lift.filter(([slot]) => slot !== 'tooltip-content').map(([slot]) => slot), ...POPUPS.rise, 'sheet-content'].map((slot) => `[data-slot=\"${slot}\"]`).join(', ')}) {\n background: color-mix(in oklab, var(--popover, var(--card, var(--background))) calc(var(--fp-glass) + 26%), transparent);\n -webkit-backdrop-filter: blur(24px) saturate(1.5);\n backdrop-filter: blur(24px) saturate(1.5);\n border-color: color-mix(in oklab, var(--border) 60%, transparent);\n}\n@keyframes fp-lift-in { from { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -0.35)); opacity: 0; } to { transform: none; opacity: 1; } }\n@keyframes fp-lift-out { from { transform: none; opacity: 1; } to { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -0.35)); opacity: 0; } }\n@keyframes fp-rise-in { from { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -1.4)); opacity: 0; } to { transform: none; opacity: 1; } }\n@keyframes fp-rise-out { from { transform: none; opacity: 1; } to { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -1.4)); opacity: 0; } }\n@keyframes fp-arrive-in { from { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * 0.9)); opacity: 0; } to { transform: none; opacity: 1; } }\n@keyframes fp-arrive-out { from { transform: none; opacity: 1; } to { transform: perspective(var(--fp-distance)) translate3d(0, 0, calc(var(--fp-step) * -0.5)); opacity: 0; } }\n\n/* The modal scrim: in the focal plane, stepping back and the light blur above ARE the scrim, so the engine's dark\n overlay (and its own backdrop blur) would double it into a heavy grey. Keep the overlay element (it still catches\n the outside click), just transparent. */\n${ROOT} [data-shell-overlay-layer] :is([data-slot=\"dialog-overlay\"], [data-slot=\"alert-dialog-overlay\"], [data-slot=\"sheet-overlay\"], [data-slot=\"drawer-overlay\"]) {\n background: transparent;\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n /* !important: the motion rules above are more specific (the drifting fields, the item zoom), and the reader's\n setting must win over every one of them. */\n ${ROOT} ${OBJECTS}, ${ROOT} .fp-ghost, ${ROOT} [data-fp-object=\"shell\"] :is(button, a), ${ROOT} [data-shell-overlay-layer] [data-slot] { transition-duration: 0ms !important; animation: none !important; }\n ${ROOT} .fp-backdrop i { animation: none !important; }\n}\n`;\n\nlet installed: CSSStyleSheet | null = null;\nlet users = 0;\n\n/**\n * Installs the stylesheet while at least one focal-plane layout is mounted, and removes it when the last one unmounts.\n * Returns the release function. Falls back to a `<style>` element where constructable stylesheets are missing.\n */\nexport function installFocalPlaneStylesheet(doc: Document = document): () => void {\n users += 1;\n if (users === 1) {\n if ('adoptedStyleSheets' in doc && typeof CSSStyleSheet !== 'undefined' && 'replaceSync' in CSSStyleSheet.prototype) {\n installed = new CSSStyleSheet();\n installed.replaceSync(FOCAL_PLANE_CSS);\n doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, installed];\n } else {\n const style = doc.createElement('style');\n style.dataset.focalPlane = '';\n style.textContent = FOCAL_PLANE_CSS;\n doc.head.appendChild(style);\n }\n }\n return () => {\n users -= 1;\n if (users > 0) return;\n if (installed) doc.adoptedStyleSheets = doc.adoptedStyleSheets.filter((sheet) => sheet !== installed);\n doc.head.querySelector('style[data-focal-plane]')?.remove();\n installed = null;\n };\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { FocalPlaneSettings } from './focal-plane-settings';
|
|
3
|
+
/**
|
|
4
|
+
* What the stylesheet cannot do alone, because the engine unmounts or hides a pane at once (#2049 step 2):
|
|
5
|
+
*
|
|
6
|
+
* - **Discard.** A side pane dismissed by its close control is removed from the DOM immediately. The removed node,
|
|
7
|
+
* no longer React's, is re-parented into a ghost layer at the box it last had, and flies away into depth and fades.
|
|
8
|
+
* - **Expand.** An emphasized side pane takes the width, and the engine HIDES the other panes (`hidden`). A still,
|
|
9
|
+
* inert image of each hidden pane is left behind, stepped back, visible through the expanded pane's glass. It is a
|
|
10
|
+
* picture, not a control: stepped back, it shrinks inside the expanded pane's outline, which covers it, so a click
|
|
11
|
+
* could never reach it (an earlier version offered one; a live probe found it unreachable). Returning to side by
|
|
12
|
+
* side is the pane's own restore control, or Escape. The image travels forward and goes as the emphasis ends.
|
|
13
|
+
* - **Swap.** A pane whose content changes in place dips back and returns (`dip`).
|
|
14
|
+
*
|
|
15
|
+
* Ghosts are aria-hidden and inert: they are pictures of panes, never a second copy anyone can use.
|
|
16
|
+
*/
|
|
17
|
+
export declare function usePaneDepth(rootRef: RefObject<HTMLElement | null>, settings: FocalPlaneSettings, reducedMotion: boolean): {
|
|
18
|
+
dip: (zone: string) => void;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=use-pane-depth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-pane-depth.d.ts","sourceRoot":"","sources":["../../../src/focal-plane/use-pane-depth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACtC,QAAQ,EAAE,kBAAkB,EAC5B,aAAa,EAAE,OAAO,GACrB;IAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAqHjC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
import { ShellRegion } from '@wildo-ai/presets-components-models';
|
|
3
|
+
import { NavigationZone } from '@wildo-ai/saas-frontend-lib';
|
|
4
|
+
import { FOCAL_PLANE_PANE_SELECTOR } from './focal-plane-stylesheet.js';
|
|
5
|
+
import { lastStageRects, VANISHING_POINT } from './use-shared-vanishing-point.js';
|
|
6
|
+
/**
|
|
7
|
+
* What the stylesheet cannot do alone, because the engine unmounts or hides a pane at once (#2049 step 2):
|
|
8
|
+
*
|
|
9
|
+
* - **Discard.** A side pane dismissed by its close control is removed from the DOM immediately. The removed node,
|
|
10
|
+
* no longer React's, is re-parented into a ghost layer at the box it last had, and flies away into depth and fades.
|
|
11
|
+
* - **Expand.** An emphasized side pane takes the width, and the engine HIDES the other panes (`hidden`). A still,
|
|
12
|
+
* inert image of each hidden pane is left behind, stepped back, visible through the expanded pane's glass. It is a
|
|
13
|
+
* picture, not a control: stepped back, it shrinks inside the expanded pane's outline, which covers it, so a click
|
|
14
|
+
* could never reach it (an earlier version offered one; a live probe found it unreachable). Returning to side by
|
|
15
|
+
* side is the pane's own restore control, or Escape. The image travels forward and goes as the emphasis ends.
|
|
16
|
+
* - **Swap.** A pane whose content changes in place dips back and returns (`dip`).
|
|
17
|
+
*
|
|
18
|
+
* Ghosts are aria-hidden and inert: they are pictures of panes, never a second copy anyone can use.
|
|
19
|
+
*/
|
|
20
|
+
export function usePaneDepth(rootRef, settings, reducedMotion) {
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
const root = rootRef.current;
|
|
23
|
+
if (!root)
|
|
24
|
+
return undefined;
|
|
25
|
+
const back = root.querySelector('.fp-ghosts:not(.fp-ghosts-front)');
|
|
26
|
+
const front = root.querySelector('.fp-ghosts-front');
|
|
27
|
+
if (!back || !front)
|
|
28
|
+
return undefined;
|
|
29
|
+
const behind = new Map();
|
|
30
|
+
const motion = reducedMotion ? 0 : settings.motionMs;
|
|
31
|
+
const place = (ghost, rect) => {
|
|
32
|
+
const vx = root.clientWidth * VANISHING_POINT.x;
|
|
33
|
+
const vy = root.clientHeight * VANISHING_POINT.y;
|
|
34
|
+
Object.assign(ghost.style, { left: `${rect.left}px`, top: `${rect.top}px`, width: `${rect.width}px`, height: `${rect.height}px` });
|
|
35
|
+
ghost.style.setProperty('--fp-ox', `${vx - rect.left}px`);
|
|
36
|
+
ghost.style.setProperty('--fp-oy', `${vy - rect.top}px`);
|
|
37
|
+
};
|
|
38
|
+
const inertCopy = (node) => {
|
|
39
|
+
const copy = node.cloneNode(true);
|
|
40
|
+
sanitise(copy);
|
|
41
|
+
return copy;
|
|
42
|
+
};
|
|
43
|
+
const discard = (node) => {
|
|
44
|
+
const rect = lastStageRects.get(node);
|
|
45
|
+
// Moved rather than removed (taken out and put back in one batch): it is still on screen, not discarded.
|
|
46
|
+
if (!rect || motion === 0 || node.isConnected)
|
|
47
|
+
return;
|
|
48
|
+
const ghost = document.createElement('div');
|
|
49
|
+
ghost.className = 'fp-ghost';
|
|
50
|
+
sanitise(node);
|
|
51
|
+
ghost.appendChild(node);
|
|
52
|
+
place(ghost, rect);
|
|
53
|
+
front.appendChild(ghost);
|
|
54
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
55
|
+
ghost.style.setProperty('--fp-z', `${-settings.layerDepthPx * 1.5}px`);
|
|
56
|
+
ghost.style.opacity = '0';
|
|
57
|
+
}));
|
|
58
|
+
setTimeout(() => ghost.remove(), motion + 60);
|
|
59
|
+
};
|
|
60
|
+
const syncBehind = () => {
|
|
61
|
+
const emphasized = root.querySelector('[data-zone-emphasized]');
|
|
62
|
+
const hidden = emphasized ? [...root.querySelectorAll(FOCAL_PLANE_PANE_SELECTOR)].filter((pane) => pane.hasAttribute('data-zone-hidden')) : [];
|
|
63
|
+
for (const [pane, ghost] of behind) {
|
|
64
|
+
if (hidden.includes(pane))
|
|
65
|
+
continue;
|
|
66
|
+
behind.delete(pane);
|
|
67
|
+
ghost.style.setProperty('--fp-z', '0px');
|
|
68
|
+
ghost.style.opacity = '0';
|
|
69
|
+
setTimeout(() => ghost.remove(), motion);
|
|
70
|
+
}
|
|
71
|
+
for (const pane of hidden) {
|
|
72
|
+
if (behind.has(pane))
|
|
73
|
+
continue;
|
|
74
|
+
const rect = lastStageRects.get(pane);
|
|
75
|
+
if (!rect)
|
|
76
|
+
continue;
|
|
77
|
+
const ghost = document.createElement('div');
|
|
78
|
+
ghost.className = 'fp-ghost fp-ghost-behind';
|
|
79
|
+
const copy = inertCopy(pane);
|
|
80
|
+
copy.classList.remove('hidden');
|
|
81
|
+
copy.removeAttribute('data-zone-hidden');
|
|
82
|
+
copy.style.width = '100%';
|
|
83
|
+
copy.style.height = '100%';
|
|
84
|
+
ghost.appendChild(copy);
|
|
85
|
+
place(ghost, rect);
|
|
86
|
+
ghost.style.opacity = '0';
|
|
87
|
+
back.appendChild(ghost);
|
|
88
|
+
behind.set(pane, ghost);
|
|
89
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
90
|
+
ghost.style.setProperty('--fp-z', `${-settings.layerDepthPx}px`);
|
|
91
|
+
ghost.style.setProperty('--fp-filter', `brightness(${1 - settings.recedeDim}) saturate(${1 - settings.recedeDim * 0.9})`);
|
|
92
|
+
ghost.style.opacity = '1';
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const observer = new MutationObserver((records) => {
|
|
97
|
+
let emphasisMayHaveChanged = false;
|
|
98
|
+
for (const record of records) {
|
|
99
|
+
if (record.type === 'attributes') {
|
|
100
|
+
emphasisMayHaveChanged = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const node of record.removedNodes) {
|
|
104
|
+
if (!(node instanceof HTMLElement))
|
|
105
|
+
continue;
|
|
106
|
+
// A detached node matches no selector that names its ancestors, so the pane is recognised by the box the
|
|
107
|
+
// stage last measured for it: only objects (a zone, or a resizable panel) have one.
|
|
108
|
+
// Only a SIDE pane is ever dismissed. The primary pane leaves only when the content remounts (a breakpoint
|
|
109
|
+
// flip to the single-zone branch), which is not a discard and must not fly a ghost away.
|
|
110
|
+
if (lastStageRects.has(node) && isSidePane(node))
|
|
111
|
+
discard(node);
|
|
112
|
+
}
|
|
113
|
+
emphasisMayHaveChanged = true;
|
|
114
|
+
}
|
|
115
|
+
if (emphasisMayHaveChanged)
|
|
116
|
+
syncBehind();
|
|
117
|
+
});
|
|
118
|
+
const content = root.querySelector(`[data-component-area="${ShellRegion.CONTENT}"]`) ?? root;
|
|
119
|
+
observer.observe(content, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-zone-hidden', 'data-zone-emphasized'] });
|
|
120
|
+
syncBehind();
|
|
121
|
+
return () => {
|
|
122
|
+
observer.disconnect();
|
|
123
|
+
for (const ghost of behind.values())
|
|
124
|
+
ghost.remove();
|
|
125
|
+
};
|
|
126
|
+
}, [rootRef, settings, reducedMotion]);
|
|
127
|
+
const dipRef = useRef(() => undefined);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
dipRef.current = (zone) => {
|
|
130
|
+
const root = rootRef.current;
|
|
131
|
+
if (!root || reducedMotion)
|
|
132
|
+
return;
|
|
133
|
+
const pane = root.querySelector(`[data-component-area="${ShellRegion.CONTENT}"] :is([data-content-area="${zone}"]:not([data-panel-id] *), [data-panel-id="nav-${zone}"])`);
|
|
134
|
+
if (!pane || typeof pane.animate !== 'function')
|
|
135
|
+
return;
|
|
136
|
+
// Implicit first and last keyframes: the pane dips from, and returns to, whatever depth it rests at.
|
|
137
|
+
pane.animate([{ offset: 0.45, transform: `perspective(${settings.cameraDistancePx}px) translate3d(0, 0, ${-settings.layerDepthPx * 0.5}px)`, opacity: 0.55 }], { duration: settings.motionMs, easing: 'cubic-bezier(0.2, 0.9, 0.25, 1)' });
|
|
138
|
+
};
|
|
139
|
+
}, [rootRef, settings, reducedMotion]);
|
|
140
|
+
return { dip: (zone) => dipRef.current(zone) };
|
|
141
|
+
}
|
|
142
|
+
const SIDE_ZONES = new Set([NavigationZone.SECONDARY, NavigationZone.COMPANION]);
|
|
143
|
+
function isSidePane(element) {
|
|
144
|
+
const zone = element.dataset.contentArea ?? element.dataset.panelId?.replace(/^nav-/, '');
|
|
145
|
+
return zone !== undefined && SIDE_ZONES.has(zone);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Makes a pane a picture: no ids, no focus, invisible to assistive technology, and none of the markers a query for the
|
|
149
|
+
* REAL pane would find (a document-level lookup of the primary pane must never return its image). Embedded frames and
|
|
150
|
+
* media are dropped rather than loaded a second time.
|
|
151
|
+
*/
|
|
152
|
+
function sanitise(element) {
|
|
153
|
+
element.setAttribute('aria-hidden', 'true');
|
|
154
|
+
element.setAttribute('inert', '');
|
|
155
|
+
for (const node of [element, ...element.querySelectorAll('*')]) {
|
|
156
|
+
for (const attribute of ['id', 'data-content-area', 'data-panel-id', 'data-zone-hidden', 'data-zone-emphasized', 'data-component-area'])
|
|
157
|
+
node.removeAttribute(attribute);
|
|
158
|
+
}
|
|
159
|
+
for (const media of element.querySelectorAll('iframe, video, audio'))
|
|
160
|
+
media.remove();
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=use-pane-depth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-pane-depth.js","sourceRoot":"","sources":["../../../src/focal-plane/use-pane-depth.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useLayoutEffect, useRef, type RefObject } from 'react';\n\nimport { ShellRegion } from '@wildo-ai/presets-components-models';\nimport { NavigationZone } from '@wildo-ai/saas-frontend-lib';\n\nimport { FOCAL_PLANE_PANE_SELECTOR } from './focal-plane-stylesheet';\nimport type { FocalPlaneSettings } from './focal-plane-settings';\nimport { lastStageRects, VANISHING_POINT, type StageRect } from './use-shared-vanishing-point';\n\n/**\n * What the stylesheet cannot do alone, because the engine unmounts or hides a pane at once (#2049 step 2):\n *\n * - **Discard.** A side pane dismissed by its close control is removed from the DOM immediately. The removed node,\n * no longer React's, is re-parented into a ghost layer at the box it last had, and flies away into depth and fades.\n * - **Expand.** An emphasized side pane takes the width, and the engine HIDES the other panes (`hidden`). A still,\n * inert image of each hidden pane is left behind, stepped back, visible through the expanded pane's glass. It is a\n * picture, not a control: stepped back, it shrinks inside the expanded pane's outline, which covers it, so a click\n * could never reach it (an earlier version offered one; a live probe found it unreachable). Returning to side by\n * side is the pane's own restore control, or Escape. The image travels forward and goes as the emphasis ends.\n * - **Swap.** A pane whose content changes in place dips back and returns (`dip`).\n *\n * Ghosts are aria-hidden and inert: they are pictures of panes, never a second copy anyone can use.\n */\nexport function usePaneDepth(\n rootRef: RefObject<HTMLElement | null>,\n settings: FocalPlaneSettings,\n reducedMotion: boolean,\n): { dip: (zone: string) => void } {\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return undefined;\n const back = root.querySelector<HTMLElement>('.fp-ghosts:not(.fp-ghosts-front)');\n const front = root.querySelector<HTMLElement>('.fp-ghosts-front');\n if (!back || !front) return undefined;\n const behind = new Map<Element, HTMLElement>();\n const motion = reducedMotion ? 0 : settings.motionMs;\n\n const place = (ghost: HTMLElement, rect: StageRect): void => {\n const vx = root.clientWidth * VANISHING_POINT.x;\n const vy = root.clientHeight * VANISHING_POINT.y;\n Object.assign(ghost.style, { left: `${rect.left}px`, top: `${rect.top}px`, width: `${rect.width}px`, height: `${rect.height}px` });\n ghost.style.setProperty('--fp-ox', `${vx - rect.left}px`);\n ghost.style.setProperty('--fp-oy', `${vy - rect.top}px`);\n };\n const inertCopy = (node: Element): HTMLElement => {\n const copy = node.cloneNode(true) as HTMLElement;\n sanitise(copy);\n return copy;\n };\n\n const discard = (node: HTMLElement): void => {\n const rect = lastStageRects.get(node);\n // Moved rather than removed (taken out and put back in one batch): it is still on screen, not discarded.\n if (!rect || motion === 0 || node.isConnected) return;\n const ghost = document.createElement('div');\n ghost.className = 'fp-ghost';\n sanitise(node);\n ghost.appendChild(node);\n place(ghost, rect);\n front.appendChild(ghost);\n requestAnimationFrame(() => requestAnimationFrame(() => {\n ghost.style.setProperty('--fp-z', `${-settings.layerDepthPx * 1.5}px`);\n ghost.style.opacity = '0';\n }));\n setTimeout(() => ghost.remove(), motion + 60);\n };\n\n const syncBehind = (): void => {\n const emphasized = root.querySelector('[data-zone-emphasized]');\n const hidden = emphasized ? [...root.querySelectorAll<HTMLElement>(FOCAL_PLANE_PANE_SELECTOR)].filter((pane) => pane.hasAttribute('data-zone-hidden')) : [];\n for (const [pane, ghost] of behind) {\n if (hidden.includes(pane as HTMLElement)) continue;\n behind.delete(pane);\n ghost.style.setProperty('--fp-z', '0px');\n ghost.style.opacity = '0';\n setTimeout(() => ghost.remove(), motion);\n }\n for (const pane of hidden) {\n if (behind.has(pane)) continue;\n const rect = lastStageRects.get(pane);\n if (!rect) continue;\n const ghost = document.createElement('div');\n ghost.className = 'fp-ghost fp-ghost-behind';\n const copy = inertCopy(pane);\n copy.classList.remove('hidden');\n copy.removeAttribute('data-zone-hidden');\n copy.style.width = '100%';\n copy.style.height = '100%';\n ghost.appendChild(copy);\n place(ghost, rect);\n ghost.style.opacity = '0';\n back.appendChild(ghost);\n behind.set(pane, ghost);\n requestAnimationFrame(() => requestAnimationFrame(() => {\n ghost.style.setProperty('--fp-z', `${-settings.layerDepthPx}px`);\n ghost.style.setProperty('--fp-filter', `brightness(${1 - settings.recedeDim}) saturate(${1 - settings.recedeDim * 0.9})`);\n ghost.style.opacity = '1';\n }));\n }\n };\n\n const observer = new MutationObserver((records) => {\n let emphasisMayHaveChanged = false;\n for (const record of records) {\n if (record.type === 'attributes') {\n emphasisMayHaveChanged = true;\n continue;\n }\n for (const node of record.removedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n // A detached node matches no selector that names its ancestors, so the pane is recognised by the box the\n // stage last measured for it: only objects (a zone, or a resizable panel) have one.\n // Only a SIDE pane is ever dismissed. The primary pane leaves only when the content remounts (a breakpoint\n // flip to the single-zone branch), which is not a discard and must not fly a ghost away.\n if (lastStageRects.has(node) && isSidePane(node)) discard(node);\n }\n emphasisMayHaveChanged = true;\n }\n if (emphasisMayHaveChanged) syncBehind();\n });\n const content = root.querySelector(`[data-component-area=\"${ShellRegion.CONTENT}\"]`) ?? root;\n observer.observe(content, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-zone-hidden', 'data-zone-emphasized'] });\n syncBehind();\n return () => {\n observer.disconnect();\n for (const ghost of behind.values()) ghost.remove();\n };\n }, [rootRef, settings, reducedMotion]);\n\n const dipRef = useRef<(zone: string) => void>(() => undefined);\n useEffect(() => {\n dipRef.current = (zone: string): void => {\n const root = rootRef.current;\n if (!root || reducedMotion) return;\n const pane = root.querySelector<HTMLElement>(`[data-component-area=\"${ShellRegion.CONTENT}\"] :is([data-content-area=\"${zone}\"]:not([data-panel-id] *), [data-panel-id=\"nav-${zone}\"])`);\n if (!pane || typeof pane.animate !== 'function') return;\n // Implicit first and last keyframes: the pane dips from, and returns to, whatever depth it rests at.\n pane.animate(\n [{ offset: 0.45, transform: `perspective(${settings.cameraDistancePx}px) translate3d(0, 0, ${-settings.layerDepthPx * 0.5}px)`, opacity: 0.55 }],\n { duration: settings.motionMs, easing: 'cubic-bezier(0.2, 0.9, 0.25, 1)' },\n );\n };\n }, [rootRef, settings, reducedMotion]);\n return { dip: (zone) => dipRef.current(zone) };\n}\n\nconst SIDE_ZONES: ReadonlySet<string> = new Set([NavigationZone.SECONDARY, NavigationZone.COMPANION]);\nfunction isSidePane(element: HTMLElement): boolean {\n const zone = element.dataset.contentArea ?? element.dataset.panelId?.replace(/^nav-/, '');\n return zone !== undefined && SIDE_ZONES.has(zone);\n}\n\n/**\n * Makes a pane a picture: no ids, no focus, invisible to assistive technology, and none of the markers a query for the\n * REAL pane would find (a document-level lookup of the primary pane must never return its image). Embedded frames and\n * media are dropped rather than loaded a second time.\n */\nfunction sanitise(element: HTMLElement): void {\n element.setAttribute('aria-hidden', 'true');\n element.setAttribute('inert', '');\n for (const node of [element, ...element.querySelectorAll<HTMLElement>('*')]) {\n for (const attribute of ['id', 'data-content-area', 'data-panel-id', 'data-zone-hidden', 'data-zone-emphasized', 'data-component-area']) node.removeAttribute(attribute);\n }\n for (const media of element.querySelectorAll('iframe, video, audio')) media.remove();\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/** Where the vanishing point sits in the stage: centred, a little above the middle (the eye line of the prototype). */
|
|
3
|
+
export declare const VANISHING_POINT: {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
};
|
|
7
|
+
/** An object's laid-out box in the stage (untransformed), as last measured. */
|
|
8
|
+
export interface StageRect {
|
|
9
|
+
left: number;
|
|
10
|
+
top: number;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The last laid-out box of every object, kept so a pane that has just been REMOVED or HIDDEN can still be drawn where
|
|
16
|
+
* it was (its ghost flies into depth from there). A zero-size reading never replaces a real one: a hidden pane measures
|
|
17
|
+
* as nothing, and its last visible box is the one that matters.
|
|
18
|
+
*/
|
|
19
|
+
export declare const lastStageRects: WeakMap<Element, StageRect>;
|
|
20
|
+
/**
|
|
21
|
+
* Keeps every object's `transform-origin` on the stage's ONE vanishing point, so objects that each carry their own
|
|
22
|
+
* `perspective()` are seen through a single still camera (see `focal-plane-stylesheet.ts`).
|
|
23
|
+
*
|
|
24
|
+
* The origin is written as two custom properties on each object (`--fp-ox`, `--fp-oy`): the vanishing point minus the
|
|
25
|
+
* object's own offset in the stage. Offsets are read from `offsetLeft`/`offsetTop`, which ignore transforms, so an object
|
|
26
|
+
* that is currently stepped back still reports where it is laid out. Custom properties written this way are left alone
|
|
27
|
+
* by React, which only manages the style properties it set itself.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useSharedVanishingPoint(rootRef: RefObject<HTMLElement | null>): void;
|
|
30
|
+
/** An element's layout offset inside `root`, walking the offset-parent chain (transforms are not part of it). */
|
|
31
|
+
export declare function offsetWithin(element: HTMLElement, root: HTMLElement): {
|
|
32
|
+
left: number;
|
|
33
|
+
top: number;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=use-shared-vanishing-point.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-shared-vanishing-point.d.ts","sourceRoot":"","sources":["../../../src/focal-plane/use-shared-vanishing-point.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIxD,uHAAuH;AACvH,eAAO,MAAM,eAAe;;;CAAsB,CAAC;AAEnD,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAAoC,CAAC;AAEhE;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAuCpF;AAED,iHAAiH;AACjH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAYnG"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import { FOCAL_PLANE_PANE_SELECTOR } from './focal-plane-stylesheet.js';
|
|
3
|
+
/** Where the vanishing point sits in the stage: centred, a little above the middle (the eye line of the prototype). */
|
|
4
|
+
export const VANISHING_POINT = { x: 0.5, y: 0.42 };
|
|
5
|
+
/**
|
|
6
|
+
* The last laid-out box of every object, kept so a pane that has just been REMOVED or HIDDEN can still be drawn where
|
|
7
|
+
* it was (its ghost flies into depth from there). A zero-size reading never replaces a real one: a hidden pane measures
|
|
8
|
+
* as nothing, and its last visible box is the one that matters.
|
|
9
|
+
*/
|
|
10
|
+
export const lastStageRects = new WeakMap();
|
|
11
|
+
/**
|
|
12
|
+
* Keeps every object's `transform-origin` on the stage's ONE vanishing point, so objects that each carry their own
|
|
13
|
+
* `perspective()` are seen through a single still camera (see `focal-plane-stylesheet.ts`).
|
|
14
|
+
*
|
|
15
|
+
* The origin is written as two custom properties on each object (`--fp-ox`, `--fp-oy`): the vanishing point minus the
|
|
16
|
+
* object's own offset in the stage. Offsets are read from `offsetLeft`/`offsetTop`, which ignore transforms, so an object
|
|
17
|
+
* that is currently stepped back still reports where it is laid out. Custom properties written this way are left alone
|
|
18
|
+
* by React, which only manages the style properties it set itself.
|
|
19
|
+
*/
|
|
20
|
+
export function useSharedVanishingPoint(rootRef) {
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
const root = rootRef.current;
|
|
23
|
+
if (!root)
|
|
24
|
+
return undefined;
|
|
25
|
+
let frame = 0;
|
|
26
|
+
// Absent in some environments (a test DOM): the window resize and the mutations below still re-measure.
|
|
27
|
+
const resize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => schedule());
|
|
28
|
+
const observed = new Set();
|
|
29
|
+
const measure = () => {
|
|
30
|
+
frame = 0;
|
|
31
|
+
const vx = root.clientWidth * VANISHING_POINT.x;
|
|
32
|
+
const vy = root.clientHeight * VANISHING_POINT.y;
|
|
33
|
+
const objects = new Set(root.querySelectorAll(`[data-fp-object], ${FOCAL_PLANE_PANE_SELECTOR}`));
|
|
34
|
+
// Every object is observed, not only the stage: a sidebar collapsing MOVES the content column without resizing
|
|
35
|
+
// the stage, and a moved object with a stale origin would be seen from the wrong point.
|
|
36
|
+
for (const element of objects)
|
|
37
|
+
if (!observed.has(element)) {
|
|
38
|
+
resize?.observe(element);
|
|
39
|
+
observed.add(element);
|
|
40
|
+
}
|
|
41
|
+
for (const element of observed)
|
|
42
|
+
if (!objects.has(element)) {
|
|
43
|
+
resize?.unobserve(element);
|
|
44
|
+
observed.delete(element);
|
|
45
|
+
}
|
|
46
|
+
for (const element of objects) {
|
|
47
|
+
const { left, top } = offsetWithin(element, root);
|
|
48
|
+
element.style.setProperty('--fp-ox', `${(vx - left).toFixed(1)}px`);
|
|
49
|
+
element.style.setProperty('--fp-oy', `${(vy - top).toFixed(1)}px`);
|
|
50
|
+
if (element.offsetWidth > 0 && element.offsetHeight > 0)
|
|
51
|
+
lastStageRects.set(element, { left, top, width: element.offsetWidth, height: element.offsetHeight });
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const schedule = () => {
|
|
55
|
+
if (!frame)
|
|
56
|
+
frame = requestAnimationFrame(measure);
|
|
57
|
+
};
|
|
58
|
+
measure();
|
|
59
|
+
resize?.observe(root);
|
|
60
|
+
const mutations = new MutationObserver(schedule);
|
|
61
|
+
mutations.observe(root, { childList: true, subtree: true });
|
|
62
|
+
window.addEventListener('resize', schedule);
|
|
63
|
+
return () => {
|
|
64
|
+
cancelAnimationFrame(frame);
|
|
65
|
+
resize?.disconnect();
|
|
66
|
+
mutations.disconnect();
|
|
67
|
+
window.removeEventListener('resize', schedule);
|
|
68
|
+
};
|
|
69
|
+
}, [rootRef]);
|
|
70
|
+
}
|
|
71
|
+
/** An element's layout offset inside `root`, walking the offset-parent chain (transforms are not part of it). */
|
|
72
|
+
export function offsetWithin(element, root) {
|
|
73
|
+
let left = 0;
|
|
74
|
+
let top = 0;
|
|
75
|
+
let node = element;
|
|
76
|
+
while (node && node !== root) {
|
|
77
|
+
left += node.offsetLeft;
|
|
78
|
+
top += node.offsetTop;
|
|
79
|
+
const parent = node.offsetParent;
|
|
80
|
+
if (!parent || !root.contains(parent) && parent !== root)
|
|
81
|
+
break;
|
|
82
|
+
node = parent;
|
|
83
|
+
}
|
|
84
|
+
return { left, top };
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=use-shared-vanishing-point.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-shared-vanishing-point.js","sourceRoot":"","sources":["../../../src/focal-plane/use-shared-vanishing-point.ts"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, type RefObject } from 'react';\n\nimport { FOCAL_PLANE_PANE_SELECTOR } from './focal-plane-stylesheet';\n\n/** Where the vanishing point sits in the stage: centred, a little above the middle (the eye line of the prototype). */\nexport const VANISHING_POINT = { x: 0.5, y: 0.42 };\n\n/** An object's laid-out box in the stage (untransformed), as last measured. */\nexport interface StageRect {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\n/**\n * The last laid-out box of every object, kept so a pane that has just been REMOVED or HIDDEN can still be drawn where\n * it was (its ghost flies into depth from there). A zero-size reading never replaces a real one: a hidden pane measures\n * as nothing, and its last visible box is the one that matters.\n */\nexport const lastStageRects = new WeakMap<Element, StageRect>();\n\n/**\n * Keeps every object's `transform-origin` on the stage's ONE vanishing point, so objects that each carry their own\n * `perspective()` are seen through a single still camera (see `focal-plane-stylesheet.ts`).\n *\n * The origin is written as two custom properties on each object (`--fp-ox`, `--fp-oy`): the vanishing point minus the\n * object's own offset in the stage. Offsets are read from `offsetLeft`/`offsetTop`, which ignore transforms, so an object\n * that is currently stepped back still reports where it is laid out. Custom properties written this way are left alone\n * by React, which only manages the style properties it set itself.\n */\nexport function useSharedVanishingPoint(rootRef: RefObject<HTMLElement | null>): void {\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return undefined;\n let frame = 0;\n // Absent in some environments (a test DOM): the window resize and the mutations below still re-measure.\n const resize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => schedule());\n const observed = new Set<HTMLElement>();\n const measure = (): void => {\n frame = 0;\n const vx = root.clientWidth * VANISHING_POINT.x;\n const vy = root.clientHeight * VANISHING_POINT.y;\n const objects = new Set(root.querySelectorAll<HTMLElement>(`[data-fp-object], ${FOCAL_PLANE_PANE_SELECTOR}`));\n // Every object is observed, not only the stage: a sidebar collapsing MOVES the content column without resizing\n // the stage, and a moved object with a stale origin would be seen from the wrong point.\n for (const element of objects) if (!observed.has(element)) { resize?.observe(element); observed.add(element); }\n for (const element of observed) if (!objects.has(element)) { resize?.unobserve(element); observed.delete(element); }\n for (const element of objects) {\n const { left, top } = offsetWithin(element, root);\n element.style.setProperty('--fp-ox', `${(vx - left).toFixed(1)}px`);\n element.style.setProperty('--fp-oy', `${(vy - top).toFixed(1)}px`);\n if (element.offsetWidth > 0 && element.offsetHeight > 0) lastStageRects.set(element, { left, top, width: element.offsetWidth, height: element.offsetHeight });\n }\n };\n const schedule = (): void => {\n if (!frame) frame = requestAnimationFrame(measure);\n };\n measure();\n resize?.observe(root);\n const mutations = new MutationObserver(schedule);\n mutations.observe(root, { childList: true, subtree: true });\n window.addEventListener('resize', schedule);\n return () => {\n cancelAnimationFrame(frame);\n resize?.disconnect();\n mutations.disconnect();\n window.removeEventListener('resize', schedule);\n };\n }, [rootRef]);\n}\n\n/** An element's layout offset inside `root`, walking the offset-parent chain (transforms are not part of it). */\nexport function offsetWithin(element: HTMLElement, root: HTMLElement): { left: number; top: number } {\n let left = 0;\n let top = 0;\n let node: HTMLElement | null = element;\n while (node && node !== root) {\n left += node.offsetLeft;\n top += node.offsetTop;\n const parent = node.offsetParent as HTMLElement | null;\n if (!parent || !root.contains(parent) && parent !== root) break;\n node = parent;\n }\n return { left, top };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-3d-style.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"frame-3d-style.d.ts","sourceRoot":"","sources":["../../../src/frame/frame-3d-style.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-3d-style.js","sourceRoot":"","sources":["../../../../src/frame/frame-3d-style.ts"],"names":[],"mappings":"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","sourcesContent":["import { DesignSystemToken, ShellRegion } from '@wildo-ai/presets-components-models';\n\n/** @wildo_source:part:start presets.3d.frame-style-overrides facet:layer:frontend facet:family:layout-3d facet:audience:app-developer */\n/**\n * What the frame draws under one shell region.\n *\n * A frame style is the 3D frame's OWN vocabulary: it says how a region's slab looks, never how the region\n * itself looks. The region stays the 2D preset's HTML, with its recipes and Tailwind classes untouched; the\n * engine's theme and recipe schemas are not extended for the frame.\n */\nexport enum Frame3DSurfaceKind {\n /** A frosted slab: a translucent tint over the in-scene background, with a rim and an optional shadow. */\n FROSTED = 'frosted',\n /** Nothing is drawn under the region. */\n NONE = 'none',\n}\n\n/** The rim: a thin edge line along the slab, emphasised while the pointer is over the region. */\nexport interface Frame3DRimStyle {\n /** The colour token of the rim. */\n token: DesignSystemToken;\n /** Rim opacity at rest, 0–1. */\n opacity: number;\n /** Rim opacity while the pointer is over the region, 0–1. The only thing hover changes. */\n hoverOpacity: number;\n /** Rim width as a multiple of the `border-width` token. */\n widthScale: number;\n}\n\n/** The slab's shadow, drawn on the scene background just outside the slab. Its colour is black. */\nexport interface Frame3DShadowStyle {\n offsetXPx: number;\n offsetYPx: number;\n /** Blur radius in CSS pixels; 0 draws a hard shadow. */\n blurPx: number;\n /** Shadow opacity, 0–1. */\n opacity: number;\n}\n\n/** How the frame draws under one region kind. */\nexport interface Frame3DRegionStyle {\n surface: Frame3DSurfaceKind;\n /** The colour token the frost is tinted with. Ignored when `tintColor` is set. */\n tintToken: DesignSystemToken;\n /**\n * An explicit CSS colour for the frost instead of a token: a brand colour, or the verification sentinel\n * ({@link createSentinelFrameStyles}). The contrast bound still applies to it.\n */\n tintColor?: string;\n /** Frost opacity over the scene background, 0–1. A glass theme's navigation material replaces it. */\n frost: number;\n /** How much lighter the lit side of the slab is than its shaded side, 0–1. */\n gradient: number;\n /** Corner radius as a multiple of the `radius` token. */\n radiusScale: number;\n rim: Frame3DRimStyle;\n shadow: Frame3DShadowStyle;\n}\n\n/** The in-scene background the slabs sit on: the page's own `backgroundLayer` cannot be sampled by WebGL. */\nexport interface Frame3DBackgroundStyle {\n /** The base colour token. */\n baseToken: DesignSystemToken;\n /** The token of the glow on the lit side. */\n glowToken: DesignSystemToken;\n /** Glow strength: how far the lit side moves from the base colour toward the glow colour, 0–1. */\n glow: number;\n}\n\n/** Everything the frame draws for one theme. `regions` is total over `ShellRegion`: a new region kind must be styled. */\nexport interface Frame3DThemeStyle {\n background: Frame3DBackgroundStyle;\n /**\n * The direction the light comes from, in degrees, y UP: 0 = from the right, 90 = from the top. (A glass\n * navigation material replaces it, converted from the engine's y-down convention.)\n */\n lightAngleDeg: number;\n /**\n * The colour of the light: the lit side of every slab moves toward it. `primary` by default — the colour the\n * background's glow uses too — so the lit side takes a cast of the theme's own light. (A surface token would be\n * the frost's own colour, and a slab lit by itself shows no gradient at all.)\n */\n lightToken: DesignSystemToken;\n /** In dark mode the lit side moves this share of the way it does in light mode (a dimmer light rig), 0–1. */\n darkModeLightStrength: number;\n regions: Record<ShellRegion, Frame3DRegionStyle>;\n}\n\n/**\n * The ids of the themes the framework ships, which the built-in frame styles are keyed on. Theme ids are an\n * open vocabulary (an application can author its own theme); any id not listed here takes the fallback style.\n */\nexport enum Frame3DBuiltInThemeId {\n DEFAULT = 'default',\n COZY = 'cozy',\n DENSE = 'dense',\n EDITORIAL = 'editorial',\n GLASS = 'glass',\n NEO_BRUTALIST = 'neo-brutalist',\n TERMINAL = 'terminal',\n TRADING = 'trading',\n}\n\n/** A partial frame style an application layers over a built-in one; nested objects merge key by key. */\nexport interface Frame3DThemeStyleOverride {\n background?: Partial<Frame3DBackgroundStyle>;\n lightAngleDeg?: number;\n lightToken?: DesignSystemToken;\n darkModeLightStrength?: number;\n regions?: Partial<Record<ShellRegion, Frame3DRegionStyleOverride>>;\n}\n\nexport type Frame3DRegionStyleOverride = Partial<Omit<Frame3DRegionStyle, 'rim' | 'shadow'>> & {\n rim?: Partial<Frame3DRimStyle>;\n shadow?: Partial<Frame3DShadowStyle>;\n};\n\n/**\n * An application's frame-style overrides, passed to `register3DLayout({ frameStyles })`. `all` applies to\n * every theme; `byThemeId` applies to one theme id and is layered after `all`.\n */\nexport interface Frame3DStyleOverrides {\n all?: Frame3DThemeStyleOverride;\n byThemeId?: Partial<Record<string, Frame3DThemeStyleOverride>>;\n}\n/** @wildo_source:part:end presets.3d.frame-style-overrides */\n\n/** The styles a layout's scene ships: one per built-in theme, and the fallback for any other theme id. */\nexport interface Frame3DStyleCatalog {\n byThemeId: Record<Frame3DBuiltInThemeId, Frame3DThemeStyle>;\n fallback: Frame3DThemeStyle;\n}\n\nconst NO_SHADOW: Frame3DShadowStyle = { offsetXPx: 0, offsetYPx: 0, blurPx: 0, opacity: 0 };\n\n/**\n * One theme's region table from a chrome style (the bars and rails) and a content style (the `<main>`). Written\n * as an object literal over every `ShellRegion` member, so a member added to the enum fails to compile here\n * instead of silently falling through to nothing.\n */\nfunction regionStyles(chrome: Frame3DRegionStyle, content: Frame3DRegionStyle): Record<ShellRegion, Frame3DRegionStyle> {\n return {\n [ShellRegion.MENUBAR]: chrome,\n [ShellRegion.SIDEBAR]: chrome,\n [ShellRegion.TOOLBAR]: chrome,\n [ShellRegion.ACTIVITY_BAR]: chrome,\n [ShellRegion.UTILITY_PANEL]: chrome,\n [ShellRegion.STATUS_BAR]: chrome,\n [ShellRegion.CONTENT]: content,\n };\n}\n\nfunction theme(input: {\n radiusScale: number;\n frost: number;\n contentFrost: number;\n gradient: number;\n rim: Frame3DRimStyle;\n shadow: Frame3DShadowStyle;\n glow: number;\n glowToken?: DesignSystemToken;\n lightAngleDeg?: number;\n}): Frame3DThemeStyle {\n const chrome: Frame3DRegionStyle = {\n surface: Frame3DSurfaceKind.FROSTED,\n tintToken: DesignSystemToken.SURFACE_RAISED,\n frost: input.frost,\n gradient: input.gradient,\n radiusScale: input.radiusScale,\n rim: input.rim,\n shadow: input.shadow,\n };\n const content: Frame3DRegionStyle = { ...chrome, tintToken: DesignSystemToken.SURFACE_DEFAULT, frost: input.contentFrost };\n return {\n background: { baseToken: DesignSystemToken.BACKGROUND, glowToken: input.glowToken ?? DesignSystemToken.PRIMARY, glow: input.glow },\n lightAngleDeg: input.lightAngleDeg ?? 110,\n lightToken: DesignSystemToken.PRIMARY,\n darkModeLightStrength: 0.5,\n regions: regionStyles(chrome, content),\n };\n}\n\nconst DEFAULT_STYLE = theme({\n radiusScale: 1,\n frost: 0.5,\n contentFrost: 0.55,\n gradient: 0.06,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.5, hoverOpacity: 0.9, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 6, blurPx: 18, opacity: 0.12 },\n glow: 0.08,\n});\n\n/**\n * The INHERIT layout's styles. Each built-in entry expresses its theme's character in the frame's own terms:\n * cozy is softer and rounder, dense and trading are tight and flat, editorial is nearly square and quiet,\n * neo-brutalist is solid with a heavy rim and a hard offset shadow, terminal is square with a primary rim.\n * Colours always come from the tokens; a style only chooses WHICH token and how strongly.\n */\nexport const INHERIT_FRAME_STYLES: Frame3DStyleCatalog = {\n byThemeId: {\n [Frame3DBuiltInThemeId.DEFAULT]: DEFAULT_STYLE,\n [Frame3DBuiltInThemeId.COZY]: theme({\n radiusScale: 1.5,\n frost: 0.6,\n contentFrost: 0.62,\n gradient: 0.08,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.35, hoverOpacity: 0.75, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 10, blurPx: 28, opacity: 0.1 },\n glow: 0.1,\n }),\n [Frame3DBuiltInThemeId.DENSE]: theme({\n radiusScale: 0.5,\n frost: 0.4,\n contentFrost: 0.45,\n gradient: 0.03,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.6, hoverOpacity: 0.9, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.04,\n }),\n [Frame3DBuiltInThemeId.EDITORIAL]: theme({\n radiusScale: 0.25,\n frost: 0.3,\n contentFrost: 0.35,\n gradient: 0,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.25, hoverOpacity: 0.5, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.03,\n }),\n [Frame3DBuiltInThemeId.GLASS]: theme({\n radiusScale: 1.25,\n frost: 0.3,\n contentFrost: 0.35,\n gradient: 0.12,\n rim: { token: DesignSystemToken.BORDER_STRONG, opacity: 0.6, hoverOpacity: 1, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 8, blurPx: 24, opacity: 0.15 },\n glow: 0.14,\n }),\n [Frame3DBuiltInThemeId.NEO_BRUTALIST]: theme({\n radiusScale: 0,\n frost: 0.92,\n contentFrost: 0.92,\n gradient: 0,\n rim: { token: DesignSystemToken.FOREGROUND, opacity: 1, hoverOpacity: 1, widthScale: 2 },\n shadow: { offsetXPx: 4, offsetYPx: 4, blurPx: 0, opacity: 1 },\n glow: 0,\n }),\n [Frame3DBuiltInThemeId.TERMINAL]: theme({\n radiusScale: 0,\n frost: 0.25,\n contentFrost: 0.3,\n gradient: 0,\n rim: { token: DesignSystemToken.PRIMARY, opacity: 0.5, hoverOpacity: 0.9, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.12,\n }),\n [Frame3DBuiltInThemeId.TRADING]: theme({\n radiusScale: 0.5,\n frost: 0.45,\n contentFrost: 0.5,\n gradient: 0.02,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.45, hoverOpacity: 0.8, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.05,\n }),\n },\n fallback: DEFAULT_STYLE,\n};\n\n/**\n * The HEADER_FIRST layout's styles: the INHERIT styles, lit from straight above — the menubar spans the whole\n * width over the rails in that disposal, so the light falls from the header down.\n */\nexport const HEADER_FIRST_FRAME_STYLES: Frame3DStyleCatalog = withLightAngle(INHERIT_FRAME_STYLES, 90);\n\nfunction withLightAngle(catalog: Frame3DStyleCatalog, lightAngleDeg: number): Frame3DStyleCatalog {\n const byThemeId = {} as Record<Frame3DBuiltInThemeId, Frame3DThemeStyle>;\n for (const id of Object.values(Frame3DBuiltInThemeId)) byThemeId[id] = { ...catalog.byThemeId[id], lightAngleDeg };\n return { byThemeId, fallback: { ...catalog.fallback, lightAngleDeg } };\n}\n\nfunction isBuiltInThemeId(id: string): id is Frame3DBuiltInThemeId {\n return (Object.values(Frame3DBuiltInThemeId) as string[]).includes(id);\n}\n\nfunction mergeTheme(base: Frame3DThemeStyle, override: Frame3DThemeStyleOverride | undefined): Frame3DThemeStyle {\n if (!override) return base;\n const regions = { ...base.regions };\n for (const region of Object.values(ShellRegion)) {\n const regionOverride = override.regions?.[region];\n if (!regionOverride) continue;\n const current = regions[region];\n regions[region] = {\n ...current,\n ...regionOverride,\n rim: { ...current.rim, ...regionOverride.rim },\n shadow: { ...current.shadow, ...regionOverride.shadow },\n };\n }\n return {\n background: { ...base.background, ...override.background },\n lightAngleDeg: override.lightAngleDeg ?? base.lightAngleDeg,\n lightToken: override.lightToken ?? base.lightToken,\n darkModeLightStrength: override.darkModeLightStrength ?? base.darkModeLightStrength,\n regions,\n };\n}\n\n/**\n * The frame style for the active theme: the layout's catalog entry for a built-in theme id (its fallback for any\n * other id), then the application's `all` override, then its override for that theme id.\n */\nexport function resolveFrame3DThemeStyle(catalog: Frame3DStyleCatalog, themeId: string, overrides?: Frame3DStyleOverrides): Frame3DThemeStyle {\n const base = isBuiltInThemeId(themeId) ? catalog.byThemeId[themeId] : catalog.fallback;\n return mergeTheme(mergeTheme(base, overrides?.all), overrides?.byThemeId?.[themeId]);\n}\n\n/**\n * The frame's verification sentinel: a neon green (`#39ff14`) that appears in no theme, token or palette in the\n * framework or the dogfood applications (checked 2026-09-29 by a repository search).\n *\n * That absence is the point. A cover check counts sentinel pixels in a screenshot: one where a region is\n * transparent over its slab is expected, one over a region's CONTENT means the canvas covered it — and a colour\n * a theme could also produce would make that count meaningless.\n */\nexport const FRAME_SENTINEL_COLOR = '#39ff14';\n\n/**\n * Frame-style overrides that draw every region as a flat, square, rimless, shadowless slab in\n * {@link FRAME_SENTINEL_COLOR}, for a cover-and-alignment check: `register3DLayout({ frameStyles:\n * createSentinelFrameStyles() })`. The contrast bound still applies, so run the check where the sentinel passes\n * it against the foreground (a light mode, whose foreground is dark) — and assert the sentinel is actually\n * visible where regions are transparent, or a bound-adjusted colour would read as \"nothing covered\".\n */\nexport function createSentinelFrameStyles(): Frame3DStyleOverrides {\n const sentinel: Frame3DRegionStyleOverride = {\n surface: Frame3DSurfaceKind.FROSTED,\n tintColor: FRAME_SENTINEL_COLOR,\n frost: 1,\n gradient: 0,\n radiusScale: 0,\n rim: { opacity: 0, hoverOpacity: 0 },\n shadow: NO_SHADOW,\n };\n const regions: Partial<Record<ShellRegion, Frame3DRegionStyleOverride>> = {};\n for (const region of Object.values(ShellRegion)) regions[region] = sentinel;\n return { all: { regions } };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"frame-3d-style.js","sourceRoot":"","sources":["../../../src/frame/frame-3d-style.ts"],"names":[],"mappings":"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","sourcesContent":["import { DesignSystemToken, ShellRegion } from '@wildo-ai/presets-components-models';\n\n/** @wildo_source:part:start presets.3d.frame-style-overrides facet:layer:frontend facet:family:layout-3d facet:audience:app-developer */\n/**\n * What the frame draws under one shell region.\n *\n * A frame style is the 3D frame's OWN vocabulary: it says how a region's slab looks, never how the region\n * itself looks. The region stays the 2D preset's HTML, with its recipes and Tailwind classes untouched; the\n * engine's theme and recipe schemas are not extended for the frame.\n */\nexport enum Frame3DSurfaceKind {\n /** A frosted slab: a translucent tint over the in-scene background, with a rim and an optional shadow. */\n FROSTED = 'frosted',\n /** Nothing is drawn under the region. */\n NONE = 'none',\n}\n\n/** The rim: a thin edge line along the slab, emphasised while the pointer is over the region. */\nexport interface Frame3DRimStyle {\n /** The colour token of the rim. */\n token: DesignSystemToken;\n /** Rim opacity at rest, 0–1. */\n opacity: number;\n /** Rim opacity while the pointer is over the region, 0–1. The only thing hover changes. */\n hoverOpacity: number;\n /** Rim width as a multiple of the `border-width` token. */\n widthScale: number;\n}\n\n/** The slab's shadow, drawn on the scene background just outside the slab. Its colour is black. */\nexport interface Frame3DShadowStyle {\n offsetXPx: number;\n offsetYPx: number;\n /** Blur radius in CSS pixels; 0 draws a hard shadow. */\n blurPx: number;\n /** Shadow opacity, 0–1. */\n opacity: number;\n}\n\n/** How the frame draws under one region kind. */\nexport interface Frame3DRegionStyle {\n surface: Frame3DSurfaceKind;\n /** The colour token the frost is tinted with. Ignored when `tintColor` is set. */\n tintToken: DesignSystemToken;\n /**\n * An explicit CSS colour for the frost instead of a token: a brand colour, or the verification sentinel\n * ({@link createSentinelFrameStyles}). The contrast bound still applies to it.\n */\n tintColor?: string;\n /** Frost opacity over the scene background, 0–1. A glass theme's navigation material replaces it. */\n frost: number;\n /** How much lighter the lit side of the slab is than its shaded side, 0–1. */\n gradient: number;\n /** Corner radius as a multiple of the `radius` token. */\n radiusScale: number;\n rim: Frame3DRimStyle;\n shadow: Frame3DShadowStyle;\n}\n\n/** The in-scene background the slabs sit on: the page's own `backgroundLayer` cannot be sampled by WebGL. */\nexport interface Frame3DBackgroundStyle {\n /** The base colour token. */\n baseToken: DesignSystemToken;\n /** The token of the glow on the lit side. */\n glowToken: DesignSystemToken;\n /** Glow strength: how far the lit side moves from the base colour toward the glow colour, 0–1. */\n glow: number;\n}\n\n/** Everything the frame draws for one theme. `regions` is total over `ShellRegion`: a new region kind must be styled. */\nexport interface Frame3DThemeStyle {\n background: Frame3DBackgroundStyle;\n /**\n * The direction the light comes from, in degrees, y UP: 0 = from the right, 90 = from the top. (A glass\n * navigation material replaces it, converted from the engine's y-down convention.)\n */\n lightAngleDeg: number;\n /**\n * The colour of the light: the lit side of every slab moves toward it. `primary` by default — the colour the\n * background's glow uses too — so the lit side takes a cast of the theme's own light. (A surface token would be\n * the frost's own colour, and a slab lit by itself shows no gradient at all.)\n */\n lightToken: DesignSystemToken;\n /** In dark mode the lit side moves this share of the way it does in light mode (a dimmer light rig), 0–1. */\n darkModeLightStrength: number;\n regions: Record<ShellRegion, Frame3DRegionStyle>;\n}\n\n/**\n * The ids of the themes the framework ships, which the built-in frame styles are keyed on. Theme ids are an\n * open vocabulary (an application can author its own theme); any id not listed here takes the fallback style.\n */\nexport enum Frame3DBuiltInThemeId {\n DEFAULT = 'default',\n COZY = 'cozy',\n DENSE = 'dense',\n EDITORIAL = 'editorial',\n GLASS = 'glass',\n NEO_BRUTALIST = 'neo-brutalist',\n TERMINAL = 'terminal',\n TRADING = 'trading',\n}\n\n/** A partial frame style an application layers over a built-in one; nested objects merge key by key. */\nexport interface Frame3DThemeStyleOverride {\n background?: Partial<Frame3DBackgroundStyle>;\n lightAngleDeg?: number;\n lightToken?: DesignSystemToken;\n darkModeLightStrength?: number;\n regions?: Partial<Record<ShellRegion, Frame3DRegionStyleOverride>>;\n}\n\nexport type Frame3DRegionStyleOverride = Partial<Omit<Frame3DRegionStyle, 'rim' | 'shadow'>> & {\n rim?: Partial<Frame3DRimStyle>;\n shadow?: Partial<Frame3DShadowStyle>;\n};\n\n/**\n * An application's frame-style overrides, passed to `register3DLayout({ frameStyles })`. `all` applies to\n * every theme; `byThemeId` applies to one theme id and is layered after `all`.\n */\nexport interface Frame3DStyleOverrides {\n all?: Frame3DThemeStyleOverride;\n byThemeId?: Partial<Record<string, Frame3DThemeStyleOverride>>;\n}\n/** @wildo_source:part:end presets.3d.frame-style-overrides */\n\n/** The styles a layout's scene ships: one per built-in theme, and the fallback for any other theme id. */\nexport interface Frame3DStyleCatalog {\n byThemeId: Record<Frame3DBuiltInThemeId, Frame3DThemeStyle>;\n fallback: Frame3DThemeStyle;\n}\n\nconst NO_SHADOW: Frame3DShadowStyle = { offsetXPx: 0, offsetYPx: 0, blurPx: 0, opacity: 0 };\n\n/**\n * One theme's region table from a chrome style (the bars and rails) and a content style (the `<main>`). Written\n * as an object literal over every `ShellRegion` member, so a member added to the enum fails to compile here\n * instead of silently falling through to nothing.\n */\nfunction regionStyles(chrome: Frame3DRegionStyle, content: Frame3DRegionStyle): Record<ShellRegion, Frame3DRegionStyle> {\n return {\n [ShellRegion.MENUBAR]: chrome,\n [ShellRegion.SIDEBAR]: chrome,\n [ShellRegion.TOOLBAR]: chrome,\n [ShellRegion.ACTIVITY_BAR]: chrome,\n [ShellRegion.UTILITY_PANEL]: chrome,\n [ShellRegion.STATUS_BAR]: chrome,\n [ShellRegion.CONTENT]: content,\n };\n}\n\nfunction theme(input: {\n radiusScale: number;\n frost: number;\n contentFrost: number;\n gradient: number;\n rim: Frame3DRimStyle;\n shadow: Frame3DShadowStyle;\n glow: number;\n glowToken?: DesignSystemToken;\n lightAngleDeg?: number;\n}): Frame3DThemeStyle {\n const chrome: Frame3DRegionStyle = {\n surface: Frame3DSurfaceKind.FROSTED,\n tintToken: DesignSystemToken.SURFACE_RAISED,\n frost: input.frost,\n gradient: input.gradient,\n radiusScale: input.radiusScale,\n rim: input.rim,\n shadow: input.shadow,\n };\n const content: Frame3DRegionStyle = { ...chrome, tintToken: DesignSystemToken.SURFACE_DEFAULT, frost: input.contentFrost };\n return {\n background: { baseToken: DesignSystemToken.BACKGROUND, glowToken: input.glowToken ?? DesignSystemToken.PRIMARY, glow: input.glow },\n lightAngleDeg: input.lightAngleDeg ?? 110,\n lightToken: DesignSystemToken.PRIMARY,\n darkModeLightStrength: 0.5,\n regions: regionStyles(chrome, content),\n };\n}\n\nconst DEFAULT_STYLE = theme({\n radiusScale: 1,\n frost: 0.5,\n contentFrost: 0.55,\n gradient: 0.06,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.5, hoverOpacity: 0.9, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 6, blurPx: 18, opacity: 0.12 },\n glow: 0.08,\n});\n\n/**\n * The INHERIT layout's styles. Each built-in entry expresses its theme's character in the frame's own terms:\n * cozy is softer and rounder, dense and trading are tight and flat, editorial is nearly square and quiet,\n * neo-brutalist is solid with a heavy rim and a hard offset shadow, terminal is square with a primary rim.\n * Colours always come from the tokens; a style only chooses WHICH token and how strongly.\n */\nexport const INHERIT_FRAME_STYLES: Frame3DStyleCatalog = {\n byThemeId: {\n [Frame3DBuiltInThemeId.DEFAULT]: DEFAULT_STYLE,\n [Frame3DBuiltInThemeId.COZY]: theme({\n radiusScale: 1.5,\n frost: 0.6,\n contentFrost: 0.62,\n gradient: 0.08,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.35, hoverOpacity: 0.75, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 10, blurPx: 28, opacity: 0.1 },\n glow: 0.1,\n }),\n [Frame3DBuiltInThemeId.DENSE]: theme({\n radiusScale: 0.5,\n frost: 0.4,\n contentFrost: 0.45,\n gradient: 0.03,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.6, hoverOpacity: 0.9, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.04,\n }),\n [Frame3DBuiltInThemeId.EDITORIAL]: theme({\n radiusScale: 0.25,\n frost: 0.3,\n contentFrost: 0.35,\n gradient: 0,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.25, hoverOpacity: 0.5, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.03,\n }),\n [Frame3DBuiltInThemeId.GLASS]: theme({\n radiusScale: 1.25,\n frost: 0.3,\n contentFrost: 0.35,\n gradient: 0.12,\n rim: { token: DesignSystemToken.BORDER_STRONG, opacity: 0.6, hoverOpacity: 1, widthScale: 1 },\n shadow: { offsetXPx: 0, offsetYPx: 8, blurPx: 24, opacity: 0.15 },\n glow: 0.14,\n }),\n [Frame3DBuiltInThemeId.NEO_BRUTALIST]: theme({\n radiusScale: 0,\n frost: 0.92,\n contentFrost: 0.92,\n gradient: 0,\n rim: { token: DesignSystemToken.FOREGROUND, opacity: 1, hoverOpacity: 1, widthScale: 2 },\n shadow: { offsetXPx: 4, offsetYPx: 4, blurPx: 0, opacity: 1 },\n glow: 0,\n }),\n [Frame3DBuiltInThemeId.TERMINAL]: theme({\n radiusScale: 0,\n frost: 0.25,\n contentFrost: 0.3,\n gradient: 0,\n rim: { token: DesignSystemToken.PRIMARY, opacity: 0.5, hoverOpacity: 0.9, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.12,\n }),\n [Frame3DBuiltInThemeId.TRADING]: theme({\n radiusScale: 0.5,\n frost: 0.45,\n contentFrost: 0.5,\n gradient: 0.02,\n rim: { token: DesignSystemToken.BORDER, opacity: 0.45, hoverOpacity: 0.8, widthScale: 1 },\n shadow: NO_SHADOW,\n glow: 0.05,\n }),\n },\n fallback: DEFAULT_STYLE,\n};\n\n/**\n * The HEADER_FIRST layout's styles: the INHERIT styles, lit from straight above — the menubar spans the whole\n * width over the rails in that disposal, so the light falls from the header down.\n */\nexport const HEADER_FIRST_FRAME_STYLES: Frame3DStyleCatalog = withLightAngle(INHERIT_FRAME_STYLES, 90);\n\nfunction withLightAngle(catalog: Frame3DStyleCatalog, lightAngleDeg: number): Frame3DStyleCatalog {\n const byThemeId = {} as Record<Frame3DBuiltInThemeId, Frame3DThemeStyle>;\n for (const id of Object.values(Frame3DBuiltInThemeId)) byThemeId[id] = { ...catalog.byThemeId[id], lightAngleDeg };\n return { byThemeId, fallback: { ...catalog.fallback, lightAngleDeg } };\n}\n\nfunction isBuiltInThemeId(id: string): id is Frame3DBuiltInThemeId {\n return (Object.values(Frame3DBuiltInThemeId) as string[]).includes(id);\n}\n\nfunction mergeTheme(base: Frame3DThemeStyle, override: Frame3DThemeStyleOverride | undefined): Frame3DThemeStyle {\n if (!override) return base;\n const regions = { ...base.regions };\n for (const region of Object.values(ShellRegion)) {\n const regionOverride = override.regions?.[region];\n if (!regionOverride) continue;\n const current = regions[region];\n regions[region] = {\n ...current,\n ...regionOverride,\n rim: { ...current.rim, ...regionOverride.rim },\n shadow: { ...current.shadow, ...regionOverride.shadow },\n };\n }\n return {\n background: { ...base.background, ...override.background },\n lightAngleDeg: override.lightAngleDeg ?? base.lightAngleDeg,\n lightToken: override.lightToken ?? base.lightToken,\n darkModeLightStrength: override.darkModeLightStrength ?? base.darkModeLightStrength,\n regions,\n };\n}\n\n/**\n * The frame style for the active theme: the layout's catalog entry for a built-in theme id (its fallback for any\n * other id), then the application's `all` override, then its override for that theme id.\n */\nexport function resolveFrame3DThemeStyle(catalog: Frame3DStyleCatalog, themeId: string, overrides?: Frame3DStyleOverrides): Frame3DThemeStyle {\n const base = isBuiltInThemeId(themeId) ? catalog.byThemeId[themeId] : catalog.fallback;\n return mergeTheme(mergeTheme(base, overrides?.all), overrides?.byThemeId?.[themeId]);\n}\n\n/**\n * The frame's verification sentinel: a neon green (`#39ff14`) that appears in no theme, token or palette in the\n * framework or the dogfood applications (checked 2026-09-29 by a repository search).\n *\n * That absence is the point. A cover check counts sentinel pixels in a screenshot: one where a region is\n * transparent over its slab is expected, one over a region's CONTENT means the canvas covered it — and a colour\n * a theme could also produce would make that count meaningless.\n */\nexport const FRAME_SENTINEL_COLOR = '#39ff14';\n\n/**\n * Frame-style overrides that draw every region as a flat, square, rimless, shadowless slab in\n * {@link FRAME_SENTINEL_COLOR}, for a cover-and-alignment check: `register3DLayout({ frameStyles:\n * createSentinelFrameStyles() })`. The contrast bound still applies, so run the check where the sentinel passes\n * it against the foreground (a light mode, whose foreground is dark) — and assert the sentinel is actually\n * visible where regions are transparent, or a bound-adjusted colour would read as \"nothing covered\".\n */\nexport function createSentinelFrameStyles(): Frame3DStyleOverrides {\n const sentinel: Frame3DRegionStyleOverride = {\n surface: Frame3DSurfaceKind.FROSTED,\n tintColor: FRAME_SENTINEL_COLOR,\n frost: 1,\n gradient: 0,\n radiusScale: 0,\n rim: { opacity: 0, hoverOpacity: 0 },\n shadow: NO_SHADOW,\n };\n const regions: Partial<Record<ShellRegion, Frame3DRegionStyleOverride>> = {};\n for (const region of Object.values(ShellRegion)) regions[region] = sentinel;\n return { all: { regions } };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-appearance.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"frame-appearance.d.ts","sourceRoot":"","sources":["../../../src/frame/frame-appearance.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAgB,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAEzH,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAgG,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9I,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAExG,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,8FAA8F;AAC9F,MAAM,WAAW,mBAAmB;IAClC,qFAAqF;IACrF,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,QAAQ,EAAE,UAAU,CAAC;IACrB,iCAAiC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEpE,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,4GAA4G;IAC5G,UAAU,EAAE,UAAU,CAAC;IACvB,0DAA0D;IAC1D,UAAU,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;IACxD,4GAA4G;IAC5G,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAClD,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,sGAAsG;IACtG,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,oGAAoG;IACpG,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,WAAW,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;IACnE,KAAK,EAAE,iBAAiB,CAAC;IACzB,iGAAiG;IACjG,kBAAkB,EAAE,eAAe,GAAG,SAAS,CAAC;IAChD,mGAAmG;IACnG,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAmB1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,GAAG,IAAI,CA6G3F"}
|