@neo-ui-kit/react 1.0.2 → 1.1.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/README.md CHANGED
@@ -6,7 +6,7 @@ NEO UI — React + TypeScript UI kit: glass/ruby surfaces, animated neon borders
6
6
 
7
7
  ## Install
8
8
 
9
- Live docs: https://pro100dimka.github.io/adui/
9
+ Live docs: https://pro100dimka.github.io/adui/#/components/overview
10
10
 
11
11
  Install the package in your application. React and ReactDOM are peer dependencies; the declared range is React 18.2 through 19.
12
12
 
@@ -25,7 +25,7 @@ import "@neo-ui-kit/react/styles.css";
25
25
  To test repository changes before publishing, run `npm run package` from the repository root and install the resulting archive in a separate application:
26
26
 
27
27
  ```bash
28
- npm install /path/to/adui/release/neo-ui-kit-react-1.0.2.tgz
28
+ npm install /path/to/adui/release/neo-ui-kit-react-1.1.0.tgz
29
29
  ```
30
30
 
31
31
  Maintainers publish new versions from `Pro100Dimka/adui` using the `release.yml` GitHub Actions workflow, a matching `v<version>` tag, and the `npm-publication` environment. npm trusts its OIDC identity; no `NPM_TOKEN` is required. The workflow checks the project and packed consumer before publishing the tested archive. Published `1.0.0` cannot be overwritten: code and README changes in npm require a new package version. See the [repository release instructions](https://github.com/Pro100Dimka/adui#npm-пакет).
@@ -182,6 +182,20 @@ import { ThemeProvider, useMotion } from "@neo-ui-kit/react/core";
182
182
 
183
183
  Canvas-rendered volume knob. Drag near the edge to rotate, drag from the center to move linearly, click the outer scale to jump. Wheel and arrow keys change the value, Shift gives a fine step, PageUp/PageDown change by 10, Home/End set 0/100, Escape cancels a drag, double-click resets. `onValueChange` fires while adjusting, `onValueCommit` after the gesture. `readOnly` turns it into a gauge.
184
184
 
185
+ ## 1.1.0 release notes
186
+
187
+ - **`QuantumBackground` is configured through props.** Every scene setting (`field`, `sensitivity`, `density`, `bloom`, …) is an optional prop with defaults in `quantumBackgroundDefaults`; `quantumBackgroundControls` describes them for building a settings panel. **The component no longer draws its own control panel** — build one next to it (the documentation page shows how) and drive audio through the ref (`QuantumBackgroundHandle`) with `onTransportChange` / `onWaveformChange`.
188
+ - New `QuantumBackground` props: `spectrum` / `spectrumRange` (sound from the host app), `particleCount`, `fieldSize`, `fullscreen` / `onFullscreenChange`, and `children` laid over the scene.
189
+ - Reaction lines, particle trails and lens/anamorphic light take the theme's colours at the presets' brightness, so the bloom still flashes in any theme.
190
+ - The field fills the scene edge to edge in full screen; the running reaction lights toggle and every documented setting reaches the renderer.
191
+ - New `resolveTokens` resolves many theme tokens with one style recalculation; `resolveToken` and the `ThemeEditor` use it.
192
+ - Dialog, Card and Spotlight pointer lights no longer restyle everything inside them on each pointer move.
193
+
194
+ ## 1.0.3 release notes
195
+
196
+ - Point the npm Homepage link directly to the documentation overview.
197
+ - Runtime code is unchanged from 1.0.2.
198
+
185
199
  ## 1.0.2 release notes
186
200
 
187
201
  - Restore the complete visualizer on the original documentation route and name it `QuantumBackground`.
@@ -218,15 +232,39 @@ Repository evidence, browser runners, before/after measurements and coverage lim
218
232
 
219
233
  ## QuantumBackground
220
234
 
221
- `QuantumBackground` is the complete Quantum Fields visualizer inside an iframe. A compact, transparent field sits above the kit's `FilePicker`, externally controlled `AudioPlayer`, tooltip-labelled microphone/screenshot/fullscreen actions, field selector, and sensitivity control. The selected song name appears on the file button; playback and seeking use the visualizer's single audio source. Other controls live in a closed-by-default “Тонкая настройка” section below the field. It follows the nearest `ThemeProvider` palette. This experience includes MIT-licensed upstream code and CDN-loaded Three.js modules; the upstream notice is shipped as `UPSTREAM-LICENSE.txt`. It is not mounted in overview-card previews.
235
+ `QuantumBackground` is the complete Quantum Fields visualizer inside an iframe. It renders only the scene: every visual and audio-reaction setting is an optional prop, so one component serves a hero background, a music page or a configurable studio. It follows the nearest `ThemeProvider` palette: particles, connection lines, the running reaction lights, particle trails and the lens-flare/anamorphic tints all take the theme's colours, whichever `field` is chosen. This experience includes MIT-licensed upstream code and CDN-loaded Three.js modules; the upstream notice is shipped as `UPSTREAM-LICENSE.txt`. It is not mounted in overview-card previews.
222
236
 
223
237
  ```tsx
224
238
  import { QuantumBackground, ThemeProvider } from "@neo-ui-kit/react";
225
239
  import "@neo-ui-kit/react/styles.css";
226
240
 
227
241
  <ThemeProvider theme="violet">
228
- <QuantumBackground />
242
+ <QuantumBackground field="Neutrino" bloom={0.8} connectionsEnabled={false} interactive={false} style={{ height: "100vh" }}>
243
+ <h1>Over the field</h1>
244
+ </QuantumBackground>
229
245
  </ThemeProvider>
230
246
  ```
231
247
 
232
- `QuantumBackgroundProps` accepts div attributes and an optional `particleBudget` cap. Leave the cap unset for the original adaptive particle count. `QuantumFieldExperience` and `QuantumFieldExperienceProps` remain compatible aliases. The separate lightweight `QuantumField` primitive documented above keeps its original props and rendering; it is not an alias of the full background.
248
+ Settings (`QuantumBackgroundSettings`): `field` (one of `quantumBackgroundFields`), `sensitivity`, `density`, `timeScale`, `paused`, `interactive`, nebula, connection, crawler, particle-trail, lens-flare, depth-of-field, camera-shake, bloom, trails, chromatic, anamorphic, god-ray, grain, vortex, pulse, bass-gate, onset, audio-camera, terrain-height and adaptive-quality values. Unset props use `quantumBackgroundDefaults`; only props that change are sent to the scene. `quantumBackgroundControls` lists each setting's group, label, range, step and the switch it depends on, for building your own settings panel next to the component. `particleCount` sets how many particles are drawn across both layers (0 to `quantumBackgroundMaxParticles`, 85 000); leave it unset to follow `density`. Adaptive quality may draw fewer, so turn `adaptiveQualityEnabled` off when the exact count matters. `particleBudget` is an upper cap on top of either. `fieldSize` is the share of the scene the field spans (0.78 by default, 1 reaches the edges). `fullscreen` makes the scene cover the whole viewport above the page, and the field then runs edge to edge (`quantumBackgroundFullscreenFieldSize`, 1.1) unless `fieldSize` is set (in the browser's top layer, so no ancestor's `overflow`, `transform` or `z-index` clips it, and the iframe keeps running); Escape calls `onFullscreenChange(false)`, so keep it controlled. `children` are laid over the scene, e.g. a close button while full screen. This differs from `ref.toggleFullscreen()`, which uses the browser's Fullscreen API and needs a click.
249
+
250
+ `spectrum` feeds sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band levels 0–1 from low to high frequencies, log-spaced across `spectrumRange` (default `[40, 12000]` Hz), any band count. Pass a new array whenever the levels change (10–60 times a second); bands, beats, reaction lines and flashes follow it exactly as they follow a playing song. A file or microphone started from the ref takes over while it plays; `spectrum={undefined}` returns the scene to silence.
251
+
252
+ ```tsx
253
+ const [bands, setBands] = useState<number[]>();
254
+ useEffect(() => engine.onSpectrum(setBands), []); // e.g. 18 bands from 40 Hz to 12 kHz
255
+ <QuantumBackground spectrum={bands} spectrumRange={[40, 12000]} />
256
+ ```
257
+
258
+ Audio is a one-off action rather than a setting, so it is on the ref (`QuantumBackgroundHandle`): `loadFile(file)`, `togglePlayback()`, `seek(seconds)`, `toggleMicrophone()`, `screenshot()` and `toggleFullscreen()`. `onTransportChange` reports `{ mode, playing, current, duration, track }`, and `onWaveformChange` reports the peaks of a loaded file, so a kit `FilePicker`, `AudioPlayer` and `IconButton`s can drive it:
259
+
260
+ ```tsx
261
+ const scene = useRef<QuantumBackgroundHandle>(null);
262
+ const [transport, setTransport] = useState<QuantumBackgroundTransport>();
263
+
264
+ <QuantumBackground ref={scene} field={field} sensitivity={sensitivity} onTransportChange={setTransport} />
265
+ <FilePicker variant="button" accept="audio/*" onFiles={([file]) => file && scene.current?.loadFile(file)} />
266
+ <AudioPlayer playing={transport?.playing} position={transport?.current} duration={transport?.duration}
267
+ onPlayingChange={() => scene.current?.togglePlayback()} onTimeChange={(s) => scene.current?.seek(s)} />
268
+ ```
269
+
270
+ The documentation page builds exactly such a panel (song, player, microphone, snapshot, fullscreen, field and sensitivity, plus a closed “Тонкая настройка” section) outside the component. `QuantumFieldExperience`, `QuantumFieldExperienceProps` and `quantumFieldExperienceControls` remain compatible aliases; note that `QuantumFieldExperience` no longer renders its own control panel. The separate lightweight `QuantumField` primitive documented above keeps its original props and rendering; it is not an alias of the full background.
@@ -1,26 +1,193 @@
1
- import { type HTMLAttributes } from "react";
2
- type Control = {
3
- key: string;
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export declare const quantumBackgroundFields: readonly ["Photon (EM)", "Gluon (Strong)", "Higgs (Mass)", "Gravity", "Dark Energy", "Neutrino", "Waveform Terrain"];
3
+ export type QuantumBackgroundField = typeof quantumBackgroundFields[number];
4
+ /** Every visual and audio-reaction setting of the scene. All of them are optional props of `QuantumBackground`. */
5
+ export interface QuantumBackgroundSettings {
6
+ field: QuantumBackgroundField;
7
+ sensitivity: number;
8
+ density: number;
9
+ /** Share of the scene the field spans: 1 reaches the edges, above 1 runs past them. */
10
+ fieldSize: number;
11
+ timeScale: number;
12
+ nebulaEnabled: boolean;
13
+ nebulaIntensity: number;
14
+ connectionsEnabled: boolean;
15
+ connectionThreshold: number;
16
+ connectionOpacity: number;
17
+ crawlersEnabled: boolean;
18
+ particleTrailsEnabled: boolean;
19
+ particleTrailOpacity: number;
20
+ lensFlareEnabled: boolean;
21
+ lensFlareIntensity: number;
22
+ dofEnabled: boolean;
23
+ dofFocus: number;
24
+ dofFocalLength: number;
25
+ dofBokehStrength: number;
26
+ cameraShake: number;
27
+ bloom: number;
28
+ trails: number;
29
+ chromaticAberration: number;
30
+ anamorphicStretch: number;
31
+ godRaysEnabled: boolean;
32
+ godRaysIntensity: number;
33
+ filmGrain: number;
34
+ vortexStrength: number;
35
+ pulseIntensity: number;
36
+ bassGateEnabled: boolean;
37
+ bassGateThreshold: number;
38
+ bassGateAttack: number;
39
+ bassGateRelease: number;
40
+ onsetSensitivity: number;
41
+ audioCameraEnabled: boolean;
42
+ audioCameraIntensity: number;
43
+ waveformTerrainHeight: number;
44
+ adaptiveQualityEnabled: boolean;
45
+ targetFPS: number;
46
+ interactive: boolean;
47
+ paused: boolean;
48
+ }
49
+ export type QuantumBackgroundSettingKey = keyof QuantumBackgroundSettings;
50
+ export type QuantumBackgroundControl = {
51
+ key: QuantumBackgroundSettingKey;
4
52
  label: string;
5
53
  value: number | boolean | string;
6
54
  min?: number;
7
55
  max?: number;
8
56
  step?: number;
9
57
  options?: readonly string[];
58
+ /** A boolean setting that has to be on for this one to have an effect. */
59
+ enabledBy?: QuantumBackgroundSettingKey;
10
60
  };
11
- export declare const quantumFieldExperienceControls: readonly {
61
+ /** Ranges, defaults and labels of every setting, grouped, for building a settings panel next to the scene. */
62
+ export declare const quantumBackgroundControls: readonly {
12
63
  id: string;
13
64
  label: string;
14
- controls: readonly Control[];
65
+ controls: readonly QuantumBackgroundControl[];
15
66
  }[];
16
- export type QuantumBackgroundProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
67
+ /** Values the scene uses for settings that are not passed as props. */
68
+ export declare const quantumBackgroundDefaults: Readonly<QuantumBackgroundSettings>;
69
+ /** `fieldSize` used while `fullscreen` is on and no `fieldSize` is given: the field runs past the screen edges. */
70
+ export declare const quantumBackgroundFullscreenFieldSize = 1.1;
71
+ /** Most particles the scene can draw across both layers (35 000 field + 50 000 secondary). */
72
+ export declare const quantumBackgroundMaxParticles = 85000;
73
+ /** Audio state of the scene, reported through `onTransportChange`. */
74
+ export type QuantumBackgroundTransport = {
75
+ /** `"file"` while a loaded song is the source, `"mic"` while the microphone is, otherwise `null`. */
76
+ mode: "file" | "mic" | null;
77
+ playing: boolean;
78
+ /** Seconds; 0 unless a file is loaded. */
79
+ current: number;
80
+ duration: number;
81
+ track: string;
82
+ };
83
+ /** Audio and capture actions, reached through `ref`. Settings are props; these are one-off commands. */
84
+ export interface QuantumBackgroundHandle {
85
+ /** Decodes the file and starts playing it as the scene's audio source. */
86
+ loadFile(file: File): void;
87
+ /** Plays or pauses the loaded file. */
88
+ togglePlayback(): void;
89
+ seek(seconds: number): void;
90
+ /** Starts the microphone, or stops it when it is the current source. */
91
+ toggleMicrophone(): void;
92
+ /** Downloads a PNG of the current frame. */
93
+ screenshot(): void;
94
+ /** Opens the scene full screen, or leaves full screen. */
95
+ toggleFullscreen(): void;
96
+ }
97
+ export type QuantumBackgroundProps = Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
98
+ /**
99
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
100
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
101
+ */
102
+ particleCount?: number;
17
103
  /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
18
104
  particleBudget?: number;
105
+ /**
106
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
107
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
108
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
109
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
110
+ * `undefined` returns the scene to silence.
111
+ */
112
+ spectrum?: readonly number[];
113
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
114
+ spectrumRange?: readonly [number, number];
115
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
116
+ fullscreen?: boolean;
117
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
118
+ onFullscreenChange?: (fullscreen: boolean) => void;
119
+ /** Content laid over the scene, e.g. a hero heading. */
120
+ children?: ReactNode;
121
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
122
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
123
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
124
+ onWaveformChange?: (points: number[]) => void;
19
125
  };
20
- /** Full MIT-licensed renderer; the host kit owns the controls and theme. */
21
- export declare function QuantumBackground({ className, particleBudget, ...props }: QuantumBackgroundProps): import("react").JSX.Element;
126
+ /** Full MIT-licensed audio-reactive scene. Every setting is a prop; audio actions are on the ref. */
127
+ export declare const QuantumBackground: import("react").ForwardRefExoticComponent<Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
128
+ /**
129
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
130
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
131
+ */
132
+ particleCount?: number;
133
+ /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
134
+ particleBudget?: number;
135
+ /**
136
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
137
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
138
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
139
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
140
+ * `undefined` returns the scene to silence.
141
+ */
142
+ spectrum?: readonly number[];
143
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
144
+ spectrumRange?: readonly [number, number];
145
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
146
+ fullscreen?: boolean;
147
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
148
+ onFullscreenChange?: (fullscreen: boolean) => void;
149
+ /** Content laid over the scene, e.g. a hero heading. */
150
+ children?: ReactNode;
151
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
152
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
153
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
154
+ onWaveformChange?: (points: number[]) => void;
155
+ } & import("react").RefAttributes<QuantumBackgroundHandle>>;
22
156
  /** @deprecated Use QuantumBackground. The original import remains compatible. */
23
- export declare const QuantumFieldExperience: typeof QuantumBackground;
157
+ export declare const QuantumFieldExperience: import("react").ForwardRefExoticComponent<Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
158
+ /**
159
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
160
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
161
+ */
162
+ particleCount?: number;
163
+ /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
164
+ particleBudget?: number;
165
+ /**
166
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
167
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
168
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
169
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
170
+ * `undefined` returns the scene to silence.
171
+ */
172
+ spectrum?: readonly number[];
173
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
174
+ spectrumRange?: readonly [number, number];
175
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
176
+ fullscreen?: boolean;
177
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
178
+ onFullscreenChange?: (fullscreen: boolean) => void;
179
+ /** Content laid over the scene, e.g. a hero heading. */
180
+ children?: ReactNode;
181
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
182
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
183
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
184
+ onWaveformChange?: (points: number[]) => void;
185
+ } & import("react").RefAttributes<QuantumBackgroundHandle>>;
24
186
  /** @deprecated Use QuantumBackgroundProps. */
25
187
  export type QuantumFieldExperienceProps = QuantumBackgroundProps;
26
- export {};
188
+ /** @deprecated Use quantumBackgroundControls. */
189
+ export declare const quantumFieldExperienceControls: readonly {
190
+ id: string;
191
+ label: string;
192
+ controls: readonly QuantumBackgroundControl[];
193
+ }[];
@@ -23,51 +23,34 @@
23
23
  z-index: 1;
24
24
  }
25
25
 
26
- .ad-quantum-field-experience {
27
- width: 100%;
28
- overflow: hidden;
29
- border-radius: var(--ad-radius-lg, 1rem);
30
- background: transparent;
31
- }
32
- .ad-qf-controls {
33
- display: grid;
34
- gap: 0.75rem;
35
- padding: 0.75rem 0;
36
- }
37
- .ad-qf-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.625rem; }
38
- .ad-qf-toolbar > .ad-file-picker { flex: none; }
39
- .ad-qf-toolbar > .ad-file-picker > small { display: none; }
40
- .ad-qf-toolbar > .ad-file-picker > .ad-button { max-width: 11rem; }
41
- .ad-qf-toolbar > .ad-file-picker .ad-button-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
42
- .ad-qf-toolbar > .ad-audio-player { flex: 1 1 13rem; min-width: min(100%, 13rem); padding: 0.35rem 0.5rem; }
43
- .ad-qf-toolbar .ad-player-play > .ad-icon-button { --ad-size-h: 2.25rem; width: 2.25rem; height: 2.25rem; }
44
- .ad-qf-actions { display: flex; align-items: center; gap: 0.25rem; }
45
- .ad-qf-actions > .ad-tooltip-trigger { display: inline-flex; }
46
- .ad-qf-controls nav { max-width: 100%; overflow-x: auto; }
47
- .ad-qf-controls .ad-collapse-content { display: grid; gap: 0.75rem; }
48
- .ad-qf-control-panel { display: grid; gap: 0.75rem; }
49
- .ad-qf-setting-grid {
50
- display: grid;
51
- grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
52
- gap: 0.75rem 1rem;
53
- align-items: center;
54
- }
55
- .ad-qf-essentials { flex: 1 1 18rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
56
- .ad-qf-setting { display: flex; align-items: end; gap: 0.5rem; min-width: 0; }
57
- .ad-qf-setting > .ad-slider-field { flex: 1; min-width: 0; }
58
- .ad-qf-setting > output { min-width: 3ch; padding-bottom: 0.55rem; color: var(--ad-muted); font-variant-numeric: tabular-nums; text-align: end; }
59
- .ad-qf-fps, .ad-qf-status { color: var(--ad-muted); font-size: 0.75rem; }
60
- .ad-qf-status { min-height: 1em; }
61
- .ad-qf-viewport {
26
+ .ad-quantum-background {
62
27
  position: relative;
63
28
  width: 100%;
64
29
  height: clamp(18rem, 42vh, 27rem);
30
+ overflow: hidden;
31
+ isolation: isolate;
32
+ border-radius: var(--ad-radius-lg, 1rem);
65
33
  background: transparent;
66
34
  }
67
- .ad-qf-viewport > .ad-quantum-field {
68
- height: 100%;
35
+ .ad-quantum-background[data-fullscreen] {
36
+ position: fixed;
37
+ inset: 0;
38
+ z-index: var(--ad-z-overlay, 900);
39
+ width: 100vw;
40
+ height: 100dvh;
41
+ max-width: none;
42
+ max-height: none;
43
+ margin: 0;
44
+ padding: 0;
45
+ border: 0;
46
+ border-radius: 0;
47
+ color: inherit;
48
+ background: var(--ad-surface-dialog, #050208);
69
49
  }
70
- .ad-qf-viewport > iframe {
50
+ .ad-quantum-background[data-fullscreen]::backdrop { background: transparent; }
51
+ .ad-quantum-background > iframe {
52
+ position: absolute;
53
+ inset: 0;
71
54
  display: block;
72
55
  width: 100%;
73
56
  height: 100%;
@@ -75,3 +58,10 @@
75
58
  background: transparent;
76
59
  color-scheme: dark;
77
60
  }
61
+ .ad-qb-content {
62
+ position: absolute;
63
+ inset: 0;
64
+ z-index: 1;
65
+ pointer-events: none;
66
+ }
67
+ .ad-qb-content > * { pointer-events: auto; }
@@ -8,6 +8,9 @@
8
8
  }
9
9
  .ad-spotlight::before,
10
10
  .ad-spotlight::after {
11
+ /* The pointer position is not inherited by default: the lights read it from their own surface. */
12
+ --ad-spot-x: inherit;
13
+ --ad-spot-y: inherit;
11
14
  content: "";
12
15
  position: absolute;
13
16
  inset: 0;
@@ -18,8 +18,8 @@ html:has(.ad-dialog[open]) {
18
18
  color: var(--ad-text);
19
19
  }
20
20
  /* Pointer light: a soft glow under the cursor, painted over the window's own surface. */
21
- .ad-dialog[data-ad-material] {
22
- --ad-material-bg:
21
+ .ad.ad-dialog[data-ad-material] {
22
+ background:
23
23
  radial-gradient(26rem circle at var(--ad-spot-x, -40rem) var(--ad-spot-y, -40rem), rgb(from var(--ad-primary) r g b / 0.11), transparent 60%),
24
24
  var(--ad-surface-dialog);
25
25
  }
@@ -43,5 +43,14 @@ export declare function themeProps(config: ThemeConfig): {
43
43
  export declare function exportTheme(config: ThemeConfig): string;
44
44
  /** Reads a theme back from JSON; throws an Error with a readable message when it is not one. */
45
45
  export declare function parseTheme(text: string, locale?: Locale): ThemeConfig;
46
- /** Current value of a token inside `scope`: colours as #rrggbb, everything else as written. */
46
+ /**
47
+ * Current values of several tokens inside `scope`: colours as #rrggbb, everything else as written.
48
+ * All colour probes are inserted together, so the page's styles are recalculated once instead of
49
+ * once per token — on a large page that is the difference between one frame and a visible freeze.
50
+ */
51
+ export declare function resolveTokens(scope: Element, tokens: readonly (string | {
52
+ name: string;
53
+ kind?: ThemeTokenKind;
54
+ })[]): Record<string, string>;
55
+ /** Current value of a token inside `scope`: colours as #rrggbb, everything else as written. Use `resolveTokens` for several. */
47
56
  export declare function resolveToken(scope: Element, name: string, kind?: ThemeTokenKind): string;
@@ -19,6 +19,9 @@
19
19
  }
20
20
  /* Pointer spotlight: a soft ruby light under the cursor, faded in on hover. */
21
21
  .ad-card::after {
22
+ /* The pointer position is not inherited by default: the light reads it from its own card. */
23
+ --ad-spot-x: inherit;
24
+ --ad-spot-y: inherit;
22
25
  content: "";
23
26
  position: absolute;
24
27
  inset: 0;
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ export { MelodixText } from "./components/effects/MelodixText/MelodixText";
22
22
  export type { MelodixTextProps } from "./components/effects/MelodixText/MelodixText";
23
23
  export { ThemeEditor } from "./components/foundation/ThemeEditor/ThemeEditor";
24
24
  export type { ThemeEditorProps } from "./components/foundation/ThemeEditor/ThemeEditor";
25
- export { defaultThemeConfig, deriveSecondary, exportTheme, parseTheme, resolveToken, themeProps, themeTokenGroups } from "./components/foundation/ThemeEditor/themeConfig";
25
+ export { defaultThemeConfig, deriveSecondary, exportTheme, parseTheme, resolveToken, resolveTokens, themeProps, themeTokenGroups } from "./components/foundation/ThemeEditor/themeConfig";
26
26
  export type { ThemeConfig, ThemeTokenGroup, ThemeTokenKind } from "./components/foundation/ThemeEditor/themeConfig";
27
27
  export { Chip } from "./components/controls/Chip/Chip";
28
28
  export type { ChipProps } from "./components/controls/Chip/Chip";
@@ -122,8 +122,8 @@ export { NeonWaves } from "./components/artwork/NeonWaves/NeonWaves";
122
122
  export type { NeonWavesProps } from "./components/artwork/NeonWaves/NeonWaves";
123
123
  export { QuantumField, quantumFieldModes, quantumFieldResolution, quantumFieldAudio } from "./components/artwork/QuantumField/QuantumField";
124
124
  export type { QuantumFieldProps, QuantumFieldMode, QuantumFieldQuality, QuantumAudioFrame } from "./components/artwork/QuantumField/QuantumField";
125
- export { QuantumBackground, QuantumFieldExperience } from "./components/artwork/QuantumField/QuantumFieldExperience";
126
- export type { QuantumBackgroundProps, QuantumFieldExperienceProps } from "./components/artwork/QuantumField/QuantumFieldExperience";
125
+ export { QuantumBackground, QuantumFieldExperience, quantumBackgroundControls, quantumBackgroundDefaults, quantumBackgroundFields, quantumBackgroundFullscreenFieldSize, quantumBackgroundMaxParticles, quantumFieldExperienceControls } from "./components/artwork/QuantumField/QuantumFieldExperience";
126
+ export type { QuantumBackgroundProps, QuantumBackgroundSettings, QuantumBackgroundSettingKey, QuantumBackgroundField, QuantumBackgroundControl, QuantumBackgroundHandle, QuantumBackgroundTransport, QuantumFieldExperienceProps } from "./components/artwork/QuantumField/QuantumFieldExperience";
127
127
  export { createQuantumFieldAudio } from "./components/artwork/QuantumField/audio";
128
128
  export { Spectrum } from "./components/artwork/Spectrum/Spectrum";
129
129
  export type { SpectrumProps } from "./components/artwork/Spectrum/Spectrum";