shardlight 0.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.
@@ -0,0 +1,204 @@
1
+ import { useShardRegistry, normalizeEffects, ShardLightContext } from '../chunk-W7WG7K5R.js';
2
+ export { Glow, Halo, Rays, Shard, Streaks, createShardComponent, normalizeEffects } from '../chunk-W7WG7K5R.js';
3
+ import { createLightModel, collapseTrigger, isLowMemoryDevice } from '../chunk-ZWPHIC3K.js';
4
+ import { forwardRef, useRef, useState, useMemo, useCallback, useEffect } from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var ShardLight = forwardRef(
8
+ function ShardLight2(props, ref) {
9
+ const {
10
+ preset,
11
+ config,
12
+ color,
13
+ size = 256,
14
+ resolution = 1024,
15
+ rayScale,
16
+ effects,
17
+ flicker,
18
+ collapse,
19
+ channels,
20
+ blend,
21
+ baker,
22
+ onReady,
23
+ fallback,
24
+ className,
25
+ style,
26
+ children
27
+ } = props;
28
+ const rootRef = useRef(null);
29
+ const modelRef = useRef(null);
30
+ const readyFiredRef = useRef(false);
31
+ const [layers, setLayers] = useState([]);
32
+ const [ready, setReady] = useState(false);
33
+ const { registry, shards } = useShardRegistry();
34
+ const effectsList = useMemo(
35
+ () => normalizeEffects(effects, flicker, collapse),
36
+ [effects, flicker, collapse]
37
+ );
38
+ const bakeResolution = useCallback(
39
+ () => resolution === "auto" ? autoResolution(size) : resolution,
40
+ [resolution, size]
41
+ );
42
+ const applyFrame = useCallback((values) => {
43
+ const root = rootRef.current;
44
+ if (!root) return;
45
+ for (const name of values.channels()) {
46
+ const channel = values.channel(name);
47
+ root.style.setProperty(`--shardlight-${name}-scale`, String(channel.scale));
48
+ root.style.setProperty(`--shardlight-${name}-opacity`, String(channel.opacity));
49
+ root.style.setProperty(`--shardlight-${name}-rotation`, `${channel.rotation}deg`);
50
+ }
51
+ root.style.setProperty("--shardlight-opacity", String(values.opacity));
52
+ }, []);
53
+ const setRefs = useCallback(
54
+ (node) => {
55
+ rootRef.current = node;
56
+ if (typeof ref === "function") ref(node);
57
+ else if (ref)
58
+ ref.current = node;
59
+ },
60
+ [ref]
61
+ );
62
+ useEffect(() => {
63
+ const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
64
+ const model = createLightModel({
65
+ preset,
66
+ config,
67
+ shards,
68
+ effects: effectsList,
69
+ color,
70
+ resolution: bakeResolution(),
71
+ rayScale,
72
+ baker,
73
+ accepts: ["url"],
74
+ reducedMotion
75
+ });
76
+ modelRef.current = model;
77
+ const unsubscribeLayers = model.subscribe(setLayers);
78
+ const unsubscribeFrame = model.onFrame(applyFrame);
79
+ model.ready.then(() => {
80
+ if (readyFiredRef.current) return;
81
+ readyFiredRef.current = true;
82
+ setReady(true);
83
+ onReady?.();
84
+ });
85
+ let raf = 0;
86
+ let last = typeof performance !== "undefined" ? performance.now() : 0;
87
+ const loop = (now) => {
88
+ const dt = last === 0 ? 0 : (now - last) / 1e3;
89
+ last = now;
90
+ model.tick(dt);
91
+ raf = requestAnimationFrame(loop);
92
+ };
93
+ raf = requestAnimationFrame(loop);
94
+ return () => {
95
+ cancelAnimationFrame(raf);
96
+ unsubscribeLayers();
97
+ unsubscribeFrame();
98
+ model.dispose();
99
+ modelRef.current = null;
100
+ };
101
+ }, []);
102
+ const updateKey = JSON.stringify({
103
+ preset,
104
+ config,
105
+ color,
106
+ size,
107
+ resolution,
108
+ rayScale,
109
+ effects: effectsList,
110
+ shards
111
+ });
112
+ useEffect(() => {
113
+ modelRef.current?.update({
114
+ preset,
115
+ config,
116
+ color,
117
+ shards,
118
+ effects: effectsList,
119
+ resolution: bakeResolution(),
120
+ rayScale,
121
+ baker
122
+ });
123
+ }, [updateKey]);
124
+ const channelKey = JSON.stringify(channels ?? null);
125
+ useEffect(() => {
126
+ if (channels) modelRef.current?.set({ channels });
127
+ }, [channelKey]);
128
+ useEffect(() => {
129
+ const node = rootRef.current;
130
+ if (!node) return;
131
+ node.set = (values) => modelRef.current?.set(values);
132
+ node.setChannel = (channel, values) => modelRef.current?.setChannel(channel, values);
133
+ node.setHover = (hovered) => modelRef.current?.setHover(hovered);
134
+ node.setCollapsed = (collapsed) => modelRef.current?.setCollapsed(collapsed);
135
+ node.toggleCollapsed = () => modelRef.current?.toggleCollapsed();
136
+ Object.defineProperty(node, "ready", {
137
+ configurable: true,
138
+ get: () => modelRef.current?.ready ?? Promise.resolve()
139
+ });
140
+ Object.defineProperty(node, "model", {
141
+ configurable: true,
142
+ get: () => modelRef.current
143
+ });
144
+ }, []);
145
+ const rootStyle = {
146
+ position: "relative",
147
+ display: "inline-block",
148
+ width: size,
149
+ height: size,
150
+ opacity: "var(--shardlight-opacity, 1)",
151
+ mixBlendMode: blend ?? "plus-lighter",
152
+ ...style
153
+ };
154
+ return /* @__PURE__ */ jsx(ShardLightContext.Provider, { value: registry, children: /* @__PURE__ */ jsxs(
155
+ "div",
156
+ {
157
+ ref: setRefs,
158
+ className,
159
+ style: rootStyle,
160
+ "aria-hidden": "true",
161
+ onPointerEnter: () => modelRef.current?.setHover(true),
162
+ onPointerLeave: () => modelRef.current?.setHover(false),
163
+ onPointerCancel: () => modelRef.current?.setHover(false),
164
+ onClick: () => {
165
+ const model = modelRef.current;
166
+ if (!model) return;
167
+ const trigger = collapseTrigger(model.resolved.effects);
168
+ if (trigger === "click") model.toggleCollapsed();
169
+ else if (trigger === "once") model.setCollapsed(true);
170
+ },
171
+ children: [
172
+ layers.map((layer) => renderLayer(layer)),
173
+ !ready && fallback ? /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0 }, children: fallback }) : null,
174
+ children
175
+ ]
176
+ }
177
+ ) });
178
+ }
179
+ );
180
+ function renderLayer(layer) {
181
+ if (!layer.source || layer.source.type !== "url") return null;
182
+ const src = layer.source.url;
183
+ const imgStyle = {
184
+ position: "absolute",
185
+ inset: 0,
186
+ width: "100%",
187
+ height: "100%",
188
+ pointerEvents: "none",
189
+ transformOrigin: "center",
190
+ transform: `rotate(var(--shardlight-${layer.channel}-rotation, 0deg)) scale(var(--shardlight-${layer.channel}-scale, 1))`,
191
+ opacity: `var(--shardlight-${layer.channel}-opacity, 1)`
192
+ };
193
+ return /* @__PURE__ */ jsx("img", { src, alt: "", draggable: false, style: imgStyle }, layer.id);
194
+ }
195
+ function autoResolution(size) {
196
+ const edge = typeof size === "number" ? size : 256;
197
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
198
+ const cap = isLowMemoryDevice() ? 1024 : 2048;
199
+ return Math.min(cap, Math.max(64, Math.round(edge * dpr)));
200
+ }
201
+
202
+ export { ShardLight };
203
+ //# sourceMappingURL=index.js.map
204
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/ShardLight.tsx"],"names":["ShardLight"],"mappings":"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","file":"index.js","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { CSSProperties, MutableRefObject, ReactNode } from 'react';\nimport {\n collapseTrigger,\n createLightModel,\n isLowMemoryDevice,\n type Baker,\n type EffectConfig,\n type FrameValues,\n type Layer,\n type LightModel,\n type Preset,\n type SetValues,\n type ShardLightConfig,\n} from '../core/index.js';\nimport {\n normalizeEffects,\n ShardLightContext,\n useShardRegistry,\n type EffectShorthand,\n} from '../shared-react/index.js';\n\nexport interface ShardLightProps {\n /** Starting config. Omitted or `null` = start empty. */\n preset?: Preset | null;\n /** Data-driven alternative or addition to children. */\n config?: Partial<ShardLightConfig>;\n /** Shorthand for `config.color`. */\n color?: string;\n /** CSS edge length of the light box. */\n size?: number | string;\n /** Bake size. `'auto'` uses rendered size × DPR, capped. */\n resolution?: number | 'auto';\n /** Thins every ray. */\n rayScale?: number;\n effects?: EffectConfig[];\n flicker?: EffectShorthand;\n /** Click the light to collapse it (click again to expand). */\n collapse?: EffectShorthand;\n /** Static channel values. */\n channels?: Record<string, { scale?: number; opacity?: number }>;\n /** CSS `mix-blend-mode` of the whole light. Default `plus-lighter`. */\n blend?: string;\n baker?: Baker;\n /** Fires once every layer is baked and decoded. */\n onReady?: () => void;\n /** Shown until it's ready (SSR, first paint). */\n fallback?: ReactNode;\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n}\n\n/** The root `<div>` plus the imperative API. */\nexport type ShardLightHandle = HTMLDivElement & {\n set(values: SetValues): void;\n setChannel(\n channel: string,\n values: { scale?: number; opacity?: number; rotation?: number },\n ): void;\n /** Drives the whole-light hover amount (`spin`/`hover` shards use it). */\n setHover(hovered: boolean): void;\n /** Eases the light toward collapsed (click behavior). */\n setCollapsed(collapsed: boolean): void;\n /** Flips between expanded and collapsed. */\n toggleCollapsed(): void;\n readonly ready: Promise<void>;\n readonly model: LightModel | null;\n};\n\nexport const ShardLight = forwardRef<ShardLightHandle, ShardLightProps>(\n function ShardLight(props, ref) {\n const {\n preset,\n config,\n color,\n size = 256,\n resolution = 1024,\n rayScale,\n effects,\n flicker,\n collapse,\n channels,\n blend,\n baker,\n onReady,\n fallback,\n className,\n style,\n children,\n } = props;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const modelRef = useRef<LightModel | null>(null);\n const readyFiredRef = useRef(false);\n const [layers, setLayers] = useState<Layer[]>([]);\n const [ready, setReady] = useState(false);\n\n const { registry, shards } = useShardRegistry();\n const effectsList = useMemo(\n () => normalizeEffects(effects, flicker, collapse),\n [effects, flicker, collapse],\n );\n\n const bakeResolution = useCallback(\n () => (resolution === 'auto' ? autoResolution(size) : resolution),\n [resolution, size],\n );\n\n const applyFrame = useCallback((values: FrameValues) => {\n const root = rootRef.current;\n if (!root) return;\n for (const name of values.channels()) {\n const channel = values.channel(name);\n root.style.setProperty(`--shardlight-${name}-scale`, String(channel.scale));\n root.style.setProperty(`--shardlight-${name}-opacity`, String(channel.opacity));\n root.style.setProperty(`--shardlight-${name}-rotation`, `${channel.rotation}deg`);\n }\n root.style.setProperty('--shardlight-opacity', String(values.opacity));\n }, []);\n\n const setRefs = useCallback(\n (node: HTMLDivElement | null) => {\n rootRef.current = node;\n if (typeof ref === 'function') ref(node as ShardLightHandle | null);\n else if (ref)\n (ref as MutableRefObject<ShardLightHandle | null>).current =\n node as ShardLightHandle | null;\n },\n [ref],\n );\n\n // Create the model once, on the client.\n useEffect(() => {\n const reducedMotion =\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n const model = createLightModel({\n preset,\n config,\n shards,\n effects: effectsList,\n color,\n resolution: bakeResolution(),\n rayScale,\n baker,\n accepts: ['url'],\n reducedMotion,\n });\n modelRef.current = model;\n\n const unsubscribeLayers = model.subscribe(setLayers);\n const unsubscribeFrame = model.onFrame(applyFrame);\n model.ready.then(() => {\n if (readyFiredRef.current) return;\n readyFiredRef.current = true;\n setReady(true);\n onReady?.();\n });\n\n let raf = 0;\n let last = typeof performance !== 'undefined' ? performance.now() : 0;\n const loop = (now: number) => {\n const dt = last === 0 ? 0 : (now - last) / 1000;\n last = now;\n model.tick(dt);\n raf = requestAnimationFrame(loop);\n };\n raf = requestAnimationFrame(loop);\n\n return () => {\n cancelAnimationFrame(raf);\n unsubscribeLayers();\n unsubscribeFrame();\n model.dispose();\n modelRef.current = null;\n };\n // Model is created once; prop changes are pushed through update() below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Push prop and `<Shard>` changes into the model.\n const updateKey = JSON.stringify({\n preset,\n config,\n color,\n size,\n resolution,\n rayScale,\n effects: effectsList,\n shards,\n });\n useEffect(() => {\n modelRef.current?.update({\n preset,\n config,\n color,\n shards,\n effects: effectsList,\n resolution: bakeResolution(),\n rayScale,\n baker,\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [updateKey]);\n\n // Static channel values.\n const channelKey = JSON.stringify(channels ?? null);\n useEffect(() => {\n if (channels) modelRef.current?.set({ channels });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [channelKey]);\n\n // Expose the imperative API on the root element.\n useEffect(() => {\n const node = rootRef.current as ShardLightHandle | null;\n if (!node) return;\n node.set = (values: SetValues) => modelRef.current?.set(values);\n node.setChannel = (\n channel: string,\n values: { scale?: number; opacity?: number; rotation?: number },\n ) => modelRef.current?.setChannel(channel, values);\n node.setHover = (hovered: boolean) => modelRef.current?.setHover(hovered);\n node.setCollapsed = (collapsed: boolean) => modelRef.current?.setCollapsed(collapsed);\n node.toggleCollapsed = () => modelRef.current?.toggleCollapsed();\n Object.defineProperty(node, 'ready', {\n configurable: true,\n get: () => modelRef.current?.ready ?? Promise.resolve(),\n });\n Object.defineProperty(node, 'model', {\n configurable: true,\n get: () => modelRef.current,\n });\n }, []);\n\n const rootStyle: CSSProperties = {\n position: 'relative',\n display: 'inline-block',\n width: size,\n height: size,\n opacity: 'var(--shardlight-opacity, 1)' as unknown as number,\n mixBlendMode: (blend ?? 'plus-lighter') as CSSProperties['mixBlendMode'],\n ...style,\n };\n\n return (\n <ShardLightContext.Provider value={registry}>\n <div\n ref={setRefs}\n className={className}\n style={rootStyle}\n aria-hidden=\"true\"\n onPointerEnter={() => modelRef.current?.setHover(true)}\n onPointerLeave={() => modelRef.current?.setHover(false)}\n onPointerCancel={() => modelRef.current?.setHover(false)}\n onClick={() => {\n const model = modelRef.current;\n if (!model) return;\n const trigger = collapseTrigger(model.resolved.effects);\n if (trigger === 'click') model.toggleCollapsed();\n else if (trigger === 'once') model.setCollapsed(true);\n }}\n >\n {layers.map((layer) => renderLayer(layer))}\n {!ready && fallback ? (\n <div style={{ position: 'absolute', inset: 0 }}>{fallback}</div>\n ) : null}\n {children}\n </div>\n </ShardLightContext.Provider>\n );\n },\n);\n\nfunction renderLayer(layer: Layer): ReactNode {\n if (!layer.source || layer.source.type !== 'url') return null;\n const src = layer.source.url;\n const imgStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n transformOrigin: 'center',\n transform: `rotate(var(--shardlight-${layer.channel}-rotation, 0deg)) scale(var(--shardlight-${layer.channel}-scale, 1))`,\n opacity: `var(--shardlight-${layer.channel}-opacity, 1)` as unknown as number,\n };\n return <img key={layer.id} src={src} alt=\"\" draggable={false} style={imgStyle} />;\n}\n\nfunction autoResolution(size: number | string): number {\n const edge = typeof size === 'number' ? size : 256;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const cap = isLowMemoryDevice() ? 1024 : 2048;\n return Math.min(cap, Math.max(64, Math.round(edge * dpr)));\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { P as Preset, S as ShardInput, B as Baker } from '../baker-BmlWqsXG.js';
2
+
3
+ /**
4
+ * **shardlight/testing** — the adapter contract suite. Any
5
+ * adapter (ours or a third party's) can run it against itself: add / replace /
6
+ * remove / dispose, checking the adapter's host objects follow the model.
7
+ */
8
+
9
+ interface FakeBakerOptions {
10
+ id?: string;
11
+ /** Delay before resolving, to exercise cancellation. */
12
+ delayMs?: number;
13
+ }
14
+ interface FakeBaker extends Baker {
15
+ /** Layer keys that finished baking. */
16
+ baked: string[];
17
+ /** Layer keys that were cancelled before finishing. */
18
+ cancelled: string[];
19
+ }
20
+ declare function createFakeBaker(options?: FakeBakerOptions): FakeBaker;
21
+ declare function waitFor(condition: () => boolean, timeoutMs?: number, intervalMs?: number): Promise<void>;
22
+ interface AdapterContractInstance {
23
+ /** Ids of the host objects currently on screen (one per layer). */
24
+ hostIds(): string[];
25
+ update(patch: {
26
+ preset?: Preset | null;
27
+ shards?: ShardInput[];
28
+ }): Promise<void>;
29
+ dispose(): void;
30
+ }
31
+ interface AdapterContract {
32
+ name: string;
33
+ create(config: {
34
+ preset?: Preset | null;
35
+ shards?: ShardInput[];
36
+ }): Promise<AdapterContractInstance>;
37
+ }
38
+ /**
39
+ * Drive an adapter through add / replace / remove / dispose and check its host
40
+ * objects follow. Runs with a fake baker when the adapter uses the default.
41
+ */
42
+ declare function runAdapterContract(contract: AdapterContract): Promise<void>;
43
+
44
+ export { type AdapterContract, type AdapterContractInstance, type FakeBaker, type FakeBakerOptions, createFakeBaker, runAdapterContract, waitFor };
@@ -0,0 +1,81 @@
1
+ // src/testing/index.ts
2
+ function createFakeBaker(options = {}) {
3
+ const baked = [];
4
+ const cancelled = [];
5
+ const delay = options.delayMs ?? 0;
6
+ return {
7
+ id: options.id ?? "fake",
8
+ baked,
9
+ cancelled,
10
+ async bake(layer, opts, signal) {
11
+ if (delay > 0) await sleep(delay);
12
+ if (signal.aborted) {
13
+ cancelled.push(layer.key);
14
+ throw abortError();
15
+ }
16
+ baked.push(layer.key);
17
+ if (opts.accept.includes("bitmap")) {
18
+ return {
19
+ type: "bitmap",
20
+ bitmap: {
21
+ width: opts.resolution,
22
+ height: opts.resolution,
23
+ close() {
24
+ }
25
+ }
26
+ };
27
+ }
28
+ return {
29
+ type: "canvas",
30
+ canvas: {
31
+ width: opts.resolution,
32
+ height: opts.resolution
33
+ }
34
+ };
35
+ }
36
+ };
37
+ }
38
+ async function waitFor(condition, timeoutMs = 2e3, intervalMs = 5) {
39
+ const start = Date.now();
40
+ while (!condition()) {
41
+ if (Date.now() - start > timeoutMs) {
42
+ throw new Error("shardlight/testing: waitFor timed out");
43
+ }
44
+ await sleep(intervalMs);
45
+ }
46
+ }
47
+ async function runAdapterContract(contract) {
48
+ const adapter = await contract.create({ preset: "star" });
49
+ try {
50
+ await waitFor(() => adapter.hostIds().length >= 2, 3e3);
51
+ const initial = adapter.hostIds();
52
+ assert(initial.length >= 2, `${contract.name}: expected at least two layers for "star"`);
53
+ await adapter.update({
54
+ shards: [{ id: "extra", kind: "fan", channel: "extra", count: 3, size: 0.4 }]
55
+ });
56
+ await waitFor(() => adapter.hostIds().some((id) => id.startsWith("extra|")), 3e3);
57
+ await adapter.update({
58
+ preset: null,
59
+ shards: []
60
+ });
61
+ await waitFor(() => adapter.hostIds().length === 0, 3e3);
62
+ } finally {
63
+ adapter.dispose();
64
+ }
65
+ assert(adapter.hostIds().length === 0, `${contract.name}: hosts not cleared on dispose`);
66
+ }
67
+ function assert(condition, message) {
68
+ if (!condition) throw new Error(message);
69
+ }
70
+ function sleep(ms) {
71
+ return new Promise((resolve) => setTimeout(resolve, ms));
72
+ }
73
+ function abortError() {
74
+ const error = new Error("Aborted");
75
+ error.name = "AbortError";
76
+ return error;
77
+ }
78
+
79
+ export { createFakeBaker, runAdapterContract, waitFor };
80
+ //# sourceMappingURL=index.js.map
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * **shardlight/testing** — the adapter contract suite. Any\n * adapter (ours or a third party's) can run it against itself: add / replace /\n * remove / dispose, checking the adapter's host objects follow the model.\n */\nimport type {\n Baker,\n BakeOptions,\n LayerSource,\n Preset,\n ResolvedLayer,\n ShardInput,\n} from '../core/index.js';\n\n// ---------------------------------------------------------------------------\n// A fake baker that needs no canvas, so tests run headlessly.\n// ---------------------------------------------------------------------------\n\nexport interface FakeBakerOptions {\n id?: string;\n /** Delay before resolving, to exercise cancellation. */\n delayMs?: number;\n}\n\nexport interface FakeBaker extends Baker {\n /** Layer keys that finished baking. */\n baked: string[];\n /** Layer keys that were cancelled before finishing. */\n cancelled: string[];\n}\n\nexport function createFakeBaker(options: FakeBakerOptions = {}): FakeBaker {\n const baked: string[] = [];\n const cancelled: string[] = [];\n const delay = options.delayMs ?? 0;\n\n return {\n id: options.id ?? 'fake',\n baked,\n cancelled,\n async bake(layer: ResolvedLayer, opts: BakeOptions, signal: AbortSignal): Promise<LayerSource> {\n if (delay > 0) await sleep(delay);\n if (signal.aborted) {\n cancelled.push(layer.key);\n throw abortError();\n }\n baked.push(layer.key);\n if (opts.accept.includes('bitmap')) {\n return {\n type: 'bitmap',\n bitmap: {\n width: opts.resolution,\n height: opts.resolution,\n close() {},\n } as unknown as ImageBitmap,\n };\n }\n return {\n type: 'canvas',\n canvas: {\n width: opts.resolution,\n height: opts.resolution,\n } as unknown as HTMLCanvasElement,\n };\n },\n } as FakeBaker;\n}\n\n// ---------------------------------------------------------------------------\n// Async helpers\n// ---------------------------------------------------------------------------\n\nexport async function waitFor(\n condition: () => boolean,\n timeoutMs = 2000,\n intervalMs = 5,\n): Promise<void> {\n const start = Date.now();\n while (!condition()) {\n if (Date.now() - start > timeoutMs) {\n throw new Error('shardlight/testing: waitFor timed out');\n }\n await sleep(intervalMs);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Contract\n// ---------------------------------------------------------------------------\n\nexport interface AdapterContractInstance {\n /** Ids of the host objects currently on screen (one per layer). */\n hostIds(): string[];\n update(patch: { preset?: Preset | null; shards?: ShardInput[] }): Promise<void>;\n dispose(): void;\n}\n\nexport interface AdapterContract {\n name: string;\n create(config: {\n preset?: Preset | null;\n shards?: ShardInput[];\n }): Promise<AdapterContractInstance>;\n}\n\n/**\n * Drive an adapter through add / replace / remove / dispose and check its host\n * objects follow. Runs with a fake baker when the adapter uses the default.\n */\nexport async function runAdapterContract(contract: AdapterContract): Promise<void> {\n const adapter = await contract.create({ preset: 'star' });\n try {\n await waitFor(() => adapter.hostIds().length >= 2, 3000);\n const initial = adapter.hostIds();\n assert(initial.length >= 2, `${contract.name}: expected at least two layers for \"star\"`);\n\n // Add a shard on a brand-new channel => a brand-new layer / host object.\n await adapter.update({\n shards: [{ id: 'extra', kind: 'fan', channel: 'extra', count: 3, size: 0.4 }],\n });\n await waitFor(() => adapter.hostIds().some((id) => id.startsWith('extra|')), 3000);\n\n // Hide every preset shard => no layers left.\n await adapter.update({\n preset: null,\n shards: [],\n });\n await waitFor(() => adapter.hostIds().length === 0, 3000);\n } finally {\n adapter.dispose();\n }\n\n assert(adapter.hostIds().length === 0, `${contract.name}: hosts not cleared on dispose`);\n}\n\nfunction assert(condition: boolean, message: string): void {\n if (!condition) throw new Error(message);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction abortError(): Error {\n const error = new Error('Aborted');\n error.name = 'AbortError';\n return error;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ export { C as CreateShardLightOptions, S as ShardLightController, c as createShardLight } from '../createShardLight-DTUyJQML.js';
2
+ import * as THREE from 'three';
3
+ import { L as LayerSource } from '../baker-BmlWqsXG.js';
4
+ export { B as Baker, E as EffectConfig, P as Preset, S as ShardInput, a as ShardLightConfig } from '../baker-BmlWqsXG.js';
5
+ export { Layer, LightModel, SetValues } from '../core/index.js';
6
+
7
+ declare function acquireTexture(key: string, source: LayerSource): THREE.Texture;
8
+ declare function releaseTexture(key: string): void;
9
+ /** Live texture count; used by the memory tests. */
10
+ declare function textureCacheSize(): number;
11
+ declare function clearTextureCache(): void;
12
+
13
+ export { LayerSource, acquireTexture, clearTextureCache, releaseTexture, textureCacheSize };
@@ -0,0 +1,4 @@
1
+ export { acquireTexture, clearTextureCache, createShardLight, releaseTexture, textureCacheSize } from '../chunk-MARSXMGB.js';
2
+ import '../chunk-ZWPHIC3K.js';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/package.json ADDED
@@ -0,0 +1,121 @@
1
+ {
2
+ "name": "shardlight",
3
+ "version": "0.1.0",
4
+ "description": "Layered shard lens-flare lights that render identically in 2D React (DOM), three.js and React Three Fiber.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": [
8
+ "./dist/**/*.js",
9
+ "./src/**/*.ts",
10
+ "./src/**/*.tsx"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/core/index.d.ts",
15
+ "import": "./dist/core/index.js"
16
+ },
17
+ "./react": {
18
+ "types": "./dist/react/index.d.ts",
19
+ "import": "./dist/react/index.js"
20
+ },
21
+ "./three": {
22
+ "types": "./dist/three/index.d.ts",
23
+ "import": "./dist/three/index.js"
24
+ },
25
+ "./r3f": {
26
+ "types": "./dist/r3f/index.d.ts",
27
+ "import": "./dist/r3f/index.js"
28
+ },
29
+ "./testing": {
30
+ "types": "./dist/testing/index.d.ts",
31
+ "import": "./dist/testing/index.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "main": "./dist/core/index.js",
36
+ "module": "./dist/core/index.js",
37
+ "types": "./dist/core/index.d.ts",
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "dev": "tsup --watch",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "test:visual": "playwright test",
49
+ "test:visual:update": "playwright test --update-snapshots",
50
+ "typecheck": "tsc --noEmit",
51
+ "prepublishOnly": "pnpm build"
52
+ },
53
+ "peerDependencies": {
54
+ "@react-three/fiber": ">=8",
55
+ "react": ">=18",
56
+ "react-dom": ">=18",
57
+ "three": ">=0.150"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@react-three/fiber": {
61
+ "optional": true
62
+ },
63
+ "react": {
64
+ "optional": true
65
+ },
66
+ "react-dom": {
67
+ "optional": true
68
+ },
69
+ "three": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "devDependencies": {
74
+ "@playwright/test": "^1.48.2",
75
+ "@react-three/fiber": "^8.17.10",
76
+ "@types/node": "^22.7.5",
77
+ "@types/pngjs": "^6.0.5",
78
+ "@types/react": "^18.3.11",
79
+ "@types/react-dom": "^18.3.1",
80
+ "@types/three": "^0.169.0",
81
+ "@vitejs/plugin-react": "^4.3.3",
82
+ "pngjs": "^7.0.0",
83
+ "react": "^18.3.1",
84
+ "react-dom": "^18.3.1",
85
+ "three": "^0.169.0",
86
+ "tsup": "^8.3.0",
87
+ "typescript": "^5.6.3",
88
+ "vite": "^5.4.10",
89
+ "vitest": "^2.1.3"
90
+ },
91
+ "keywords": [
92
+ "lens-flare",
93
+ "flare",
94
+ "light",
95
+ "glow",
96
+ "shards",
97
+ "three",
98
+ "threejs",
99
+ "react",
100
+ "react-three-fiber",
101
+ "r3f",
102
+ "webgl",
103
+ "canvas"
104
+ ],
105
+ "publishConfig": {
106
+ "access": "public"
107
+ },
108
+ "author": "Jukrapop K <jukrapopk@gmail.com>",
109
+ "engines": {
110
+ "node": ">=18"
111
+ },
112
+ "repository": {
113
+ "type": "git",
114
+ "url": "https://github.com/jukrapopk/shardlight.git",
115
+ "directory": "packages/shardlight"
116
+ },
117
+ "bugs": {
118
+ "url": "https://github.com/jukrapopk/shardlight/issues"
119
+ },
120
+ "homepage": "https://github.com/jukrapopk/shardlight#readme"
121
+ }