@neo-ui-kit/react 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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.3.tgz
28
+ npm install /path/to/adui/release/neo-ui-kit-react-1.2.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.2.0 release notes
186
+
187
+ - `QuantumBackground` takes `threeUrl`, the base address of a three.js 0.182 copy (the folder with `build/` and `examples/jsm/`), so the scene can work offline and without a third-party CDN. A relative address keeps the scene's import map — and its CSP hash — the same wherever the app runs. The default stays `quantumBackgroundThreeUrl` (unpkg).
188
+ - The scene no longer loads web fonts from Google Fonts: it makes no requests beyond three.js.
189
+
190
+ ## 1.1.0 release notes
191
+
192
+ - **`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`.
193
+ - New `QuantumBackground` props: `spectrum` / `spectrumRange` (sound from the host app), `particleCount`, `fieldSize`, `fullscreen` / `onFullscreenChange`, and `children` laid over the scene.
194
+ - 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.
195
+ - The field fills the scene edge to edge in full screen; the running reaction lights toggle and every documented setting reaches the renderer.
196
+ - New `resolveTokens` resolves many theme tokens with one style recalculation; `resolveToken` and the `ThemeEditor` use it.
197
+ - Dialog, Card and Spotlight pointer lights no longer restyle everything inside them on each pointer move.
198
+
185
199
  ## 1.0.3 release notes
186
200
 
187
201
  - Point the npm Homepage link directly to the documentation overview.
@@ -223,15 +237,41 @@ Repository evidence, browser runners, before/after measurements and coverage lim
223
237
 
224
238
  ## QuantumBackground
225
239
 
226
- `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.
240
+ `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.
227
241
 
228
242
  ```tsx
229
243
  import { QuantumBackground, ThemeProvider } from "@neo-ui-kit/react";
230
244
  import "@neo-ui-kit/react/styles.css";
231
245
 
232
246
  <ThemeProvider theme="violet">
233
- <QuantumBackground />
247
+ <QuantumBackground field="Neutrino" bloom={0.8} connectionsEnabled={false} interactive={false} style={{ height: "100vh" }}>
248
+ <h1>Over the field</h1>
249
+ </QuantumBackground>
234
250
  </ThemeProvider>
235
251
  ```
236
252
 
237
- `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.
253
+ 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.
254
+
255
+ `threeUrl` points the scene at your own copy of three.js 0.182 instead of unpkg (for example `threeUrl="./vendor/three/"` with `build/three.module.js` and `examples/jsm/` served there); with it the scene makes no network requests at all. Only the scene's import map changes, so a strict CSP pins the same script hashes everywhere.
256
+
257
+ `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.
258
+
259
+ ```tsx
260
+ const [bands, setBands] = useState<number[]>();
261
+ useEffect(() => engine.onSpectrum(setBands), []); // e.g. 18 bands from 40 Hz to 12 kHz
262
+ <QuantumBackground spectrum={bands} spectrumRange={[40, 12000]} />
263
+ ```
264
+
265
+ 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:
266
+
267
+ ```tsx
268
+ const scene = useRef<QuantumBackgroundHandle>(null);
269
+ const [transport, setTransport] = useState<QuantumBackgroundTransport>();
270
+
271
+ <QuantumBackground ref={scene} field={field} sensitivity={sensitivity} onTransportChange={setTransport} />
272
+ <FilePicker variant="button" accept="audio/*" onFiles={([file]) => file && scene.current?.loadFile(file)} />
273
+ <AudioPlayer playing={transport?.playing} position={transport?.current} duration={transport?.duration}
274
+ onPlayingChange={() => scene.current?.togglePlayback()} onTimeChange={(s) => scene.current?.seek(s)} />
275
+ ```
276
+
277
+ 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,216 @@
1
- import { type HTMLAttributes } from "react";
2
- type Control = {
3
- key: string;
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Where the scene loads three.js from unless `threeUrl` says otherwise: the version it is built and tested with. */
3
+ export declare const quantumBackgroundThreeUrl = "https://unpkg.com/three@0.182.0/";
4
+ export declare const quantumBackgroundFields: readonly ["Photon (EM)", "Gluon (Strong)", "Higgs (Mass)", "Gravity", "Dark Energy", "Neutrino", "Waveform Terrain"];
5
+ export type QuantumBackgroundField = typeof quantumBackgroundFields[number];
6
+ /** Every visual and audio-reaction setting of the scene. All of them are optional props of `QuantumBackground`. */
7
+ export interface QuantumBackgroundSettings {
8
+ field: QuantumBackgroundField;
9
+ sensitivity: number;
10
+ density: number;
11
+ /** Share of the scene the field spans: 1 reaches the edges, above 1 runs past them. */
12
+ fieldSize: number;
13
+ timeScale: number;
14
+ nebulaEnabled: boolean;
15
+ nebulaIntensity: number;
16
+ connectionsEnabled: boolean;
17
+ connectionThreshold: number;
18
+ connectionOpacity: number;
19
+ crawlersEnabled: boolean;
20
+ particleTrailsEnabled: boolean;
21
+ particleTrailOpacity: number;
22
+ lensFlareEnabled: boolean;
23
+ lensFlareIntensity: number;
24
+ dofEnabled: boolean;
25
+ dofFocus: number;
26
+ dofFocalLength: number;
27
+ dofBokehStrength: number;
28
+ cameraShake: number;
29
+ bloom: number;
30
+ trails: number;
31
+ chromaticAberration: number;
32
+ anamorphicStretch: number;
33
+ godRaysEnabled: boolean;
34
+ godRaysIntensity: number;
35
+ filmGrain: number;
36
+ vortexStrength: number;
37
+ pulseIntensity: number;
38
+ bassGateEnabled: boolean;
39
+ bassGateThreshold: number;
40
+ bassGateAttack: number;
41
+ bassGateRelease: number;
42
+ onsetSensitivity: number;
43
+ audioCameraEnabled: boolean;
44
+ audioCameraIntensity: number;
45
+ waveformTerrainHeight: number;
46
+ adaptiveQualityEnabled: boolean;
47
+ targetFPS: number;
48
+ interactive: boolean;
49
+ paused: boolean;
50
+ }
51
+ export type QuantumBackgroundSettingKey = keyof QuantumBackgroundSettings;
52
+ export type QuantumBackgroundControl = {
53
+ key: QuantumBackgroundSettingKey;
4
54
  label: string;
5
55
  value: number | boolean | string;
6
56
  min?: number;
7
57
  max?: number;
8
58
  step?: number;
9
59
  options?: readonly string[];
60
+ /** A boolean setting that has to be on for this one to have an effect. */
61
+ enabledBy?: QuantumBackgroundSettingKey;
10
62
  };
11
- export declare const quantumFieldExperienceControls: readonly {
63
+ /** Ranges, defaults and labels of every setting, grouped, for building a settings panel next to the scene. */
64
+ export declare const quantumBackgroundControls: readonly {
12
65
  id: string;
13
66
  label: string;
14
- controls: readonly Control[];
67
+ controls: readonly QuantumBackgroundControl[];
15
68
  }[];
16
- export type QuantumBackgroundProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
69
+ /** Values the scene uses for settings that are not passed as props. */
70
+ export declare const quantumBackgroundDefaults: Readonly<QuantumBackgroundSettings>;
71
+ /** `fieldSize` used while `fullscreen` is on and no `fieldSize` is given: the field runs past the screen edges. */
72
+ export declare const quantumBackgroundFullscreenFieldSize = 1.1;
73
+ /** Most particles the scene can draw across both layers (35 000 field + 50 000 secondary). */
74
+ export declare const quantumBackgroundMaxParticles = 85000;
75
+ /** Audio state of the scene, reported through `onTransportChange`. */
76
+ export type QuantumBackgroundTransport = {
77
+ /** `"file"` while a loaded song is the source, `"mic"` while the microphone is, otherwise `null`. */
78
+ mode: "file" | "mic" | null;
79
+ playing: boolean;
80
+ /** Seconds; 0 unless a file is loaded. */
81
+ current: number;
82
+ duration: number;
83
+ track: string;
84
+ };
85
+ /** Audio and capture actions, reached through `ref`. Settings are props; these are one-off commands. */
86
+ export interface QuantumBackgroundHandle {
87
+ /** Decodes the file and starts playing it as the scene's audio source. */
88
+ loadFile(file: File): void;
89
+ /** Plays or pauses the loaded file. */
90
+ togglePlayback(): void;
91
+ seek(seconds: number): void;
92
+ /** Starts the microphone, or stops it when it is the current source. */
93
+ toggleMicrophone(): void;
94
+ /** Downloads a PNG of the current frame. */
95
+ screenshot(): void;
96
+ /** Opens the scene full screen, or leaves full screen. */
97
+ toggleFullscreen(): void;
98
+ }
99
+ export type QuantumBackgroundProps = Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
100
+ /**
101
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
102
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
103
+ */
104
+ particleCount?: number;
17
105
  /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
18
106
  particleBudget?: number;
107
+ /**
108
+ * Base address of a three.js 0.182 copy — the folder holding `build/three.module.js` and
109
+ * `examples/jsm/` — so the scene works offline and without a third-party CDN. A relative address
110
+ * resolves against the page and keeps the scene's import map (and its CSP hash) the same wherever
111
+ * the app is installed. Default: `quantumBackgroundThreeUrl` (unpkg).
112
+ */
113
+ threeUrl?: string;
114
+ /**
115
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
116
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
117
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
118
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
119
+ * `undefined` returns the scene to silence.
120
+ */
121
+ spectrum?: readonly number[];
122
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
123
+ spectrumRange?: readonly [number, number];
124
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
125
+ fullscreen?: boolean;
126
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
127
+ onFullscreenChange?: (fullscreen: boolean) => void;
128
+ /** Content laid over the scene, e.g. a hero heading. */
129
+ children?: ReactNode;
130
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
131
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
132
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
133
+ onWaveformChange?: (points: number[]) => void;
19
134
  };
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;
135
+ /** Full MIT-licensed audio-reactive scene. Every setting is a prop; audio actions are on the ref. */
136
+ export declare const QuantumBackground: import("react").ForwardRefExoticComponent<Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
137
+ /**
138
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
139
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
140
+ */
141
+ particleCount?: number;
142
+ /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
143
+ particleBudget?: number;
144
+ /**
145
+ * Base address of a three.js 0.182 copy — the folder holding `build/three.module.js` and
146
+ * `examples/jsm/` — so the scene works offline and without a third-party CDN. A relative address
147
+ * resolves against the page and keeps the scene's import map (and its CSP hash) the same wherever
148
+ * the app is installed. Default: `quantumBackgroundThreeUrl` (unpkg).
149
+ */
150
+ threeUrl?: string;
151
+ /**
152
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
153
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
154
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
155
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
156
+ * `undefined` returns the scene to silence.
157
+ */
158
+ spectrum?: readonly number[];
159
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
160
+ spectrumRange?: readonly [number, number];
161
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
162
+ fullscreen?: boolean;
163
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
164
+ onFullscreenChange?: (fullscreen: boolean) => void;
165
+ /** Content laid over the scene, e.g. a hero heading. */
166
+ children?: ReactNode;
167
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
168
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
169
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
170
+ onWaveformChange?: (points: number[]) => void;
171
+ } & import("react").RefAttributes<QuantumBackgroundHandle>>;
22
172
  /** @deprecated Use QuantumBackground. The original import remains compatible. */
23
- export declare const QuantumFieldExperience: typeof QuantumBackground;
173
+ export declare const QuantumFieldExperience: import("react").ForwardRefExoticComponent<Partial<QuantumBackgroundSettings> & Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
174
+ /**
175
+ * Number of particles across both layers, 0–`quantumBackgroundMaxParticles`. Unset follows `density`.
176
+ * Adaptive quality may draw fewer; turn `adaptiveQualityEnabled` off to keep the exact count.
177
+ */
178
+ particleCount?: number;
179
+ /** Maximum number of visible particles across both field layers. Adaptive quality may draw fewer. */
180
+ particleBudget?: number;
181
+ /**
182
+ * Base address of a three.js 0.182 copy — the folder holding `build/three.module.js` and
183
+ * `examples/jsm/` — so the scene works offline and without a third-party CDN. A relative address
184
+ * resolves against the page and keeps the scene's import map (and its CSP hash) the same wherever
185
+ * the app is installed. Default: `quantumBackgroundThreeUrl` (unpkg).
186
+ */
187
+ threeUrl?: string;
188
+ /**
189
+ * Sound the scene cannot hear itself — a native audio engine, a WebRTC call, a server stream: band
190
+ * levels 0–1 from low to high frequencies, log-spaced across `spectrumRange`. Any band count works;
191
+ * pass a new array whenever the levels change (10–60 times a second). While set, it drives the field
192
+ * like a playing song; a file or microphone started from the ref takes over while it plays.
193
+ * `undefined` returns the scene to silence.
194
+ */
195
+ spectrum?: readonly number[];
196
+ /** Frequencies in Hz of the first and last `spectrum` band. Default `[40, 12000]`. */
197
+ spectrumRange?: readonly [number, number];
198
+ /** Covers the whole viewport above the page; the field then fills it edge to edge unless `fieldSize` is set. */
199
+ fullscreen?: boolean;
200
+ /** Asks to leave `fullscreen` when the viewer presses Escape. */
201
+ onFullscreenChange?: (fullscreen: boolean) => void;
202
+ /** Content laid over the scene, e.g. a hero heading. */
203
+ children?: ReactNode;
204
+ /** Called when the audio source, playback state or position changes (about 4 times a second while playing). */
205
+ onTransportChange?: (transport: QuantumBackgroundTransport) => void;
206
+ /** Peaks (0–1) of a newly loaded file, for drawing its waveform. */
207
+ onWaveformChange?: (points: number[]) => void;
208
+ } & import("react").RefAttributes<QuantumBackgroundHandle>>;
24
209
  /** @deprecated Use QuantumBackgroundProps. */
25
210
  export type QuantumFieldExperienceProps = QuantumBackgroundProps;
26
- export {};
211
+ /** @deprecated Use quantumBackgroundControls. */
212
+ export declare const quantumFieldExperienceControls: readonly {
213
+ id: string;
214
+ label: string;
215
+ controls: readonly QuantumBackgroundControl[];
216
+ }[];
@@ -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, quantumBackgroundThreeUrl, 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";