wallpaper-engine 1.0.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,119 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/helpers.ts
21
+ var helpers_exports = {};
22
+ __export(helpers_exports, {
23
+ clampAudio: () => clampAudio,
24
+ createFpsLimiter: () => createFpsLimiter,
25
+ encodeCanvasForLed: () => encodeCanvasForLed,
26
+ leftChannel: () => leftChannel,
27
+ parseWallpaperColor: () => parseWallpaperColor,
28
+ rightChannel: () => rightChannel,
29
+ toFileUrl: () => toFileUrl,
30
+ wallpaperColorToHex: () => wallpaperColorToHex,
31
+ wallpaperColorToRgb: () => wallpaperColorToRgb
32
+ });
33
+ module.exports = __toCommonJS(helpers_exports);
34
+ function parseWallpaperColor(value) {
35
+ const parts = value.split(" ");
36
+ return {
37
+ r: Math.ceil(+(parts[0] ?? "0") * 255),
38
+ g: Math.ceil(+(parts[1] ?? "0") * 255),
39
+ b: Math.ceil(+(parts[2] ?? "0") * 255)
40
+ };
41
+ }
42
+ function wallpaperColorToRgb(value) {
43
+ const { r, g, b } = parseWallpaperColor(value);
44
+ return `rgb(${r},${g},${b})`;
45
+ }
46
+ function wallpaperColorToHex(value) {
47
+ const { r, g, b } = parseWallpaperColor(value);
48
+ return "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
49
+ }
50
+ function toFileUrl(path) {
51
+ return "file:///" + path;
52
+ }
53
+ function clampAudio(audioArray) {
54
+ return audioArray.map((v) => Math.min(v, 1));
55
+ }
56
+ function leftChannel(audioArray) {
57
+ return audioArray.slice(0, 64);
58
+ }
59
+ function rightChannel(audioArray) {
60
+ return audioArray.slice(64, 128);
61
+ }
62
+ function encodeCanvasForLed(canvas) {
63
+ const ctx = canvas.getContext("2d");
64
+ if (!ctx) throw new Error("Could not get 2D context from canvas");
65
+ const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
66
+ let result = "";
67
+ for (let i = 0; i < data.length; i += 4) {
68
+ result += String.fromCodePoint(data[i], data[i + 1], data[i + 2]);
69
+ }
70
+ return result;
71
+ }
72
+ function createFpsLimiter(draw) {
73
+ let limit = 0;
74
+ let last = 0;
75
+ let threshold = 0;
76
+ let rafId = null;
77
+ function loop(timestamp) {
78
+ rafId = requestAnimationFrame(loop);
79
+ const now = timestamp / 1e3;
80
+ const dt = Math.min(now - last, 1);
81
+ last = now;
82
+ if (limit > 0) {
83
+ threshold += dt;
84
+ if (threshold < 1 / limit) return;
85
+ threshold -= 1 / limit;
86
+ }
87
+ draw(dt);
88
+ }
89
+ return {
90
+ start() {
91
+ if (rafId !== null) cancelAnimationFrame(rafId);
92
+ last = performance.now() / 1e3;
93
+ threshold = 0;
94
+ rafId = requestAnimationFrame(loop);
95
+ },
96
+ stop() {
97
+ if (rafId !== null) {
98
+ cancelAnimationFrame(rafId);
99
+ rafId = null;
100
+ }
101
+ },
102
+ setLimit(fps) {
103
+ limit = fps;
104
+ }
105
+ };
106
+ }
107
+ // Annotate the CommonJS export names for ESM import in node:
108
+ 0 && (module.exports = {
109
+ clampAudio,
110
+ createFpsLimiter,
111
+ encodeCanvasForLed,
112
+ leftChannel,
113
+ parseWallpaperColor,
114
+ rightChannel,
115
+ toFileUrl,
116
+ wallpaperColorToHex,
117
+ wallpaperColorToRgb
118
+ });
119
+ //# sourceMappingURL=helpers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/helpers.ts"],"sourcesContent":["/**\r\n * Common utility helpers for Wallpaper Engine web wallpapers.\r\n *\r\n * Every export is side-effect-free and tree-shakeable — import only what you need.\r\n *\r\n * @example\r\n * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';\r\n */\r\n\r\n// ---------------------------------------------------------------------------\r\n// Color helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Parse a Wallpaper Engine color string (`\"R G B\"`, each channel 0–1) into\r\n * individual 0–255 integer channels.\r\n *\r\n * @example\r\n * const { r, g, b } = parseWallpaperColor(props.mycolor.value);\r\n * ctx.fillStyle = `rgb(${r},${g},${b})`;\r\n */\r\nexport function parseWallpaperColor(value: string): {\r\n r: number;\r\n g: number;\r\n b: number;\r\n} {\r\n const parts = value.split(\" \");\r\n return {\r\n r: Math.ceil(+(parts[0] ?? \"0\") * 255),\r\n g: Math.ceil(+(parts[1] ?? \"0\") * 255),\r\n b: Math.ceil(+(parts[2] ?? \"0\") * 255),\r\n };\r\n}\r\n\r\n/**\r\n * Convert a Wallpaper Engine color string to a CSS `rgb()` value.\r\n *\r\n * @example\r\n * el.style.color = wallpaperColorToRgb(props.mycolor.value);\r\n * // → \"rgb(255,128,0)\"\r\n */\r\nexport function wallpaperColorToRgb(value: string): string {\r\n const { r, g, b } = parseWallpaperColor(value);\r\n return `rgb(${r},${g},${b})`;\r\n}\r\n\r\n/**\r\n * Convert a Wallpaper Engine color string to a CSS hex color.\r\n *\r\n * @example\r\n * el.style.color = wallpaperColorToHex(props.mycolor.value);\r\n * // → \"#ff8000\"\r\n */\r\nexport function wallpaperColorToHex(value: string): string {\r\n const { r, g, b } = parseWallpaperColor(value);\r\n return \"#\" + [r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\");\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// File URL helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Prefix a Wallpaper Engine file/directory path with `file:///` to make it\r\n * usable as an `<img>` or `<video>` `src`.\r\n *\r\n * The raw path delivered by `applyUserProperties` is not a valid URL on its own.\r\n *\r\n * @example\r\n * img.src = toFileUrl(props.myimage.value);\r\n * // → \"file:///C:/Users/.../myimage.png\"\r\n */\r\nexport function toFileUrl(path: string): string {\r\n return \"file:///\" + path;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Audio helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.\r\n *\r\n * Due to the internal FFT implementation, values can occasionally exceed 1.0.\r\n * Clamping before use is strongly recommended by the official documentation.\r\n *\r\n * @example\r\n * window.wallpaperRegisterAudioListener((raw) => {\r\n * const audio = clampAudio(raw);\r\n * renderBars(audio);\r\n * });\r\n */\r\nexport function clampAudio(audioArray: number[]): number[] {\r\n return audioArray.map((v) => Math.min(v, 1));\r\n}\r\n\r\n/**\r\n * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine\r\n * audio array. Index 0 = bass, index 63 = treble.\r\n *\r\n * The full array has 128 values: 0–63 left, 64–127 right.\r\n */\r\nexport function leftChannel(audioArray: number[]): number[] {\r\n return audioArray.slice(0, 64);\r\n}\r\n\r\n/**\r\n * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine\r\n * audio array. Index 64 = bass, index 127 = treble.\r\n *\r\n * The full array has 128 values: 0–63 left, 64–127 right.\r\n */\r\nexport function rightChannel(audioArray: number[]): number[] {\r\n return audioArray.slice(64, 128);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// RGB / LED helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected\r\n * by `window.wpPlugins.led.setAllDevicesByImageData` and\r\n * `window.cue.setLedColorsByImageData`.\r\n *\r\n * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for\r\n * best performance — the plugins sample it to their device layout anyway.\r\n *\r\n * @example\r\n * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;\r\n * const encoded = encodeCanvasForLed(canvas);\r\n * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);\r\n */\r\nexport function encodeCanvasForLed(canvas: HTMLCanvasElement): string {\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) throw new Error(\"Could not get 2D context from canvas\");\r\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n let result = \"\";\r\n for (let i = 0; i < data.length; i += 4) {\r\n result += String.fromCodePoint(data[i]!, data[i + 1]!, data[i + 2]!);\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// FPS limiter\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors\r\n * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.\r\n *\r\n * @param draw - Called each allowed frame with `dt` — elapsed seconds since\r\n * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).\r\n * @returns An object with `start`, `stop`, and `setLimit` methods.\r\n *\r\n * @example\r\n * const loop = createFpsLimiter((dt) => renderFrame(dt));\r\n *\r\n * window.wallpaperPropertyListener = {\r\n * applyGeneralProperties(props) {\r\n * if (props.fps !== undefined) loop.setLimit(props.fps);\r\n * },\r\n * };\r\n *\r\n * window.onload = () => loop.start();\r\n */\r\nexport function createFpsLimiter(draw: (dt: number) => void): {\r\n /** Start (or restart) the animation loop. */\r\n start(): void;\r\n /** Stop the animation loop. */\r\n stop(): void;\r\n /**\r\n * Set the FPS cap. Pass `0` for unlimited.\r\n * Safe to call at any time — takes effect on the next frame.\r\n */\r\n setLimit(fps: number): void;\r\n} {\r\n let limit = 0;\r\n let last = 0;\r\n let threshold = 0;\r\n let rafId: number | null = null;\r\n\r\n function loop(timestamp: number): void {\r\n rafId = requestAnimationFrame(loop);\r\n const now = timestamp / 1000;\r\n const dt = Math.min(now - last, 1);\r\n last = now;\r\n\r\n if (limit > 0) {\r\n threshold += dt;\r\n if (threshold < 1 / limit) return;\r\n threshold -= 1 / limit;\r\n }\r\n\r\n draw(dt);\r\n }\r\n\r\n return {\r\n start() {\r\n if (rafId !== null) cancelAnimationFrame(rafId);\r\n last = performance.now() / 1000;\r\n threshold = 0;\r\n rafId = requestAnimationFrame(loop);\r\n },\r\n stop() {\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n },\r\n setLimit(fps: number) {\r\n limit = fps;\r\n },\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,oBAAoB,OAIlC;AACA,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,SAAO;AAAA,IACL,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,IACrC,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,IACrC,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,EACvC;AACF;AASO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,oBAAoB,KAAK;AAC7C,SAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3B;AASO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,oBAAoB,KAAK;AAC7C,SAAO,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC5E;AAgBO,SAAS,UAAU,MAAsB;AAC9C,SAAO,aAAa;AACtB;AAkBO,SAAS,WAAW,YAAgC;AACzD,SAAO,WAAW,IAAI,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC;AAC7C;AAQO,SAAS,YAAY,YAAgC;AAC1D,SAAO,WAAW,MAAM,GAAG,EAAE;AAC/B;AAQO,SAAS,aAAa,YAAgC;AAC3D,SAAO,WAAW,MAAM,IAAI,GAAG;AACjC;AAmBO,SAAS,mBAAmB,QAAmC;AACpE,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sCAAsC;AAChE,QAAM,EAAE,KAAK,IAAI,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AACnE,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,cAAU,OAAO,cAAc,KAAK,CAAC,GAAI,KAAK,IAAI,CAAC,GAAI,KAAK,IAAI,CAAC,CAAE;AAAA,EACrE;AACA,SAAO;AACT;AAyBO,SAAS,iBAAiB,MAU/B;AACA,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,MAAI,QAAuB;AAE3B,WAAS,KAAK,WAAyB;AACrC,YAAQ,sBAAsB,IAAI;AAClC,UAAM,MAAM,YAAY;AACxB,UAAM,KAAK,KAAK,IAAI,MAAM,MAAM,CAAC;AACjC,WAAO;AAEP,QAAI,QAAQ,GAAG;AACb,mBAAa;AACb,UAAI,YAAY,IAAI,MAAO;AAC3B,mBAAa,IAAI;AAAA,IACnB;AAEA,SAAK,EAAE;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,UAAI,UAAU,KAAM,sBAAqB,KAAK;AAC9C,aAAO,YAAY,IAAI,IAAI;AAC3B,kBAAY;AACZ,cAAQ,sBAAsB,IAAI;AAAA,IACpC;AAAA,IACA,OAAO;AACL,UAAI,UAAU,MAAM;AAClB,6BAAqB,KAAK;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,SAAS,KAAa;AACpB,cAAQ;AAAA,IACV;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Common utility helpers for Wallpaper Engine web wallpapers.
3
+ *
4
+ * Every export is side-effect-free and tree-shakeable — import only what you need.
5
+ *
6
+ * @example
7
+ * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';
8
+ */
9
+ /**
10
+ * Parse a Wallpaper Engine color string (`"R G B"`, each channel 0–1) into
11
+ * individual 0–255 integer channels.
12
+ *
13
+ * @example
14
+ * const { r, g, b } = parseWallpaperColor(props.mycolor.value);
15
+ * ctx.fillStyle = `rgb(${r},${g},${b})`;
16
+ */
17
+ declare function parseWallpaperColor(value: string): {
18
+ r: number;
19
+ g: number;
20
+ b: number;
21
+ };
22
+ /**
23
+ * Convert a Wallpaper Engine color string to a CSS `rgb()` value.
24
+ *
25
+ * @example
26
+ * el.style.color = wallpaperColorToRgb(props.mycolor.value);
27
+ * // → "rgb(255,128,0)"
28
+ */
29
+ declare function wallpaperColorToRgb(value: string): string;
30
+ /**
31
+ * Convert a Wallpaper Engine color string to a CSS hex color.
32
+ *
33
+ * @example
34
+ * el.style.color = wallpaperColorToHex(props.mycolor.value);
35
+ * // → "#ff8000"
36
+ */
37
+ declare function wallpaperColorToHex(value: string): string;
38
+ /**
39
+ * Prefix a Wallpaper Engine file/directory path with `file:///` to make it
40
+ * usable as an `<img>` or `<video>` `src`.
41
+ *
42
+ * The raw path delivered by `applyUserProperties` is not a valid URL on its own.
43
+ *
44
+ * @example
45
+ * img.src = toFileUrl(props.myimage.value);
46
+ * // → "file:///C:/Users/.../myimage.png"
47
+ */
48
+ declare function toFileUrl(path: string): string;
49
+ /**
50
+ * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.
51
+ *
52
+ * Due to the internal FFT implementation, values can occasionally exceed 1.0.
53
+ * Clamping before use is strongly recommended by the official documentation.
54
+ *
55
+ * @example
56
+ * window.wallpaperRegisterAudioListener((raw) => {
57
+ * const audio = clampAudio(raw);
58
+ * renderBars(audio);
59
+ * });
60
+ */
61
+ declare function clampAudio(audioArray: number[]): number[];
62
+ /**
63
+ * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine
64
+ * audio array. Index 0 = bass, index 63 = treble.
65
+ *
66
+ * The full array has 128 values: 0–63 left, 64–127 right.
67
+ */
68
+ declare function leftChannel(audioArray: number[]): number[];
69
+ /**
70
+ * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine
71
+ * audio array. Index 64 = bass, index 127 = treble.
72
+ *
73
+ * The full array has 128 values: 0–63 left, 64–127 right.
74
+ */
75
+ declare function rightChannel(audioArray: number[]): number[];
76
+ /**
77
+ * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected
78
+ * by `window.wpPlugins.led.setAllDevicesByImageData` and
79
+ * `window.cue.setLedColorsByImageData`.
80
+ *
81
+ * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for
82
+ * best performance — the plugins sample it to their device layout anyway.
83
+ *
84
+ * @example
85
+ * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;
86
+ * const encoded = encodeCanvasForLed(canvas);
87
+ * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);
88
+ */
89
+ declare function encodeCanvasForLed(canvas: HTMLCanvasElement): string;
90
+ /**
91
+ * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors
92
+ * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.
93
+ *
94
+ * @param draw - Called each allowed frame with `dt` — elapsed seconds since
95
+ * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).
96
+ * @returns An object with `start`, `stop`, and `setLimit` methods.
97
+ *
98
+ * @example
99
+ * const loop = createFpsLimiter((dt) => renderFrame(dt));
100
+ *
101
+ * window.wallpaperPropertyListener = {
102
+ * applyGeneralProperties(props) {
103
+ * if (props.fps !== undefined) loop.setLimit(props.fps);
104
+ * },
105
+ * };
106
+ *
107
+ * window.onload = () => loop.start();
108
+ */
109
+ declare function createFpsLimiter(draw: (dt: number) => void): {
110
+ /** Start (or restart) the animation loop. */
111
+ start(): void;
112
+ /** Stop the animation loop. */
113
+ stop(): void;
114
+ /**
115
+ * Set the FPS cap. Pass `0` for unlimited.
116
+ * Safe to call at any time — takes effect on the next frame.
117
+ */
118
+ setLimit(fps: number): void;
119
+ };
120
+
121
+ export { clampAudio, createFpsLimiter, encodeCanvasForLed, leftChannel, parseWallpaperColor, rightChannel, toFileUrl, wallpaperColorToHex, wallpaperColorToRgb };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Common utility helpers for Wallpaper Engine web wallpapers.
3
+ *
4
+ * Every export is side-effect-free and tree-shakeable — import only what you need.
5
+ *
6
+ * @example
7
+ * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';
8
+ */
9
+ /**
10
+ * Parse a Wallpaper Engine color string (`"R G B"`, each channel 0–1) into
11
+ * individual 0–255 integer channels.
12
+ *
13
+ * @example
14
+ * const { r, g, b } = parseWallpaperColor(props.mycolor.value);
15
+ * ctx.fillStyle = `rgb(${r},${g},${b})`;
16
+ */
17
+ declare function parseWallpaperColor(value: string): {
18
+ r: number;
19
+ g: number;
20
+ b: number;
21
+ };
22
+ /**
23
+ * Convert a Wallpaper Engine color string to a CSS `rgb()` value.
24
+ *
25
+ * @example
26
+ * el.style.color = wallpaperColorToRgb(props.mycolor.value);
27
+ * // → "rgb(255,128,0)"
28
+ */
29
+ declare function wallpaperColorToRgb(value: string): string;
30
+ /**
31
+ * Convert a Wallpaper Engine color string to a CSS hex color.
32
+ *
33
+ * @example
34
+ * el.style.color = wallpaperColorToHex(props.mycolor.value);
35
+ * // → "#ff8000"
36
+ */
37
+ declare function wallpaperColorToHex(value: string): string;
38
+ /**
39
+ * Prefix a Wallpaper Engine file/directory path with `file:///` to make it
40
+ * usable as an `<img>` or `<video>` `src`.
41
+ *
42
+ * The raw path delivered by `applyUserProperties` is not a valid URL on its own.
43
+ *
44
+ * @example
45
+ * img.src = toFileUrl(props.myimage.value);
46
+ * // → "file:///C:/Users/.../myimage.png"
47
+ */
48
+ declare function toFileUrl(path: string): string;
49
+ /**
50
+ * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.
51
+ *
52
+ * Due to the internal FFT implementation, values can occasionally exceed 1.0.
53
+ * Clamping before use is strongly recommended by the official documentation.
54
+ *
55
+ * @example
56
+ * window.wallpaperRegisterAudioListener((raw) => {
57
+ * const audio = clampAudio(raw);
58
+ * renderBars(audio);
59
+ * });
60
+ */
61
+ declare function clampAudio(audioArray: number[]): number[];
62
+ /**
63
+ * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine
64
+ * audio array. Index 0 = bass, index 63 = treble.
65
+ *
66
+ * The full array has 128 values: 0–63 left, 64–127 right.
67
+ */
68
+ declare function leftChannel(audioArray: number[]): number[];
69
+ /**
70
+ * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine
71
+ * audio array. Index 64 = bass, index 127 = treble.
72
+ *
73
+ * The full array has 128 values: 0–63 left, 64–127 right.
74
+ */
75
+ declare function rightChannel(audioArray: number[]): number[];
76
+ /**
77
+ * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected
78
+ * by `window.wpPlugins.led.setAllDevicesByImageData` and
79
+ * `window.cue.setLedColorsByImageData`.
80
+ *
81
+ * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for
82
+ * best performance — the plugins sample it to their device layout anyway.
83
+ *
84
+ * @example
85
+ * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;
86
+ * const encoded = encodeCanvasForLed(canvas);
87
+ * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);
88
+ */
89
+ declare function encodeCanvasForLed(canvas: HTMLCanvasElement): string;
90
+ /**
91
+ * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors
92
+ * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.
93
+ *
94
+ * @param draw - Called each allowed frame with `dt` — elapsed seconds since
95
+ * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).
96
+ * @returns An object with `start`, `stop`, and `setLimit` methods.
97
+ *
98
+ * @example
99
+ * const loop = createFpsLimiter((dt) => renderFrame(dt));
100
+ *
101
+ * window.wallpaperPropertyListener = {
102
+ * applyGeneralProperties(props) {
103
+ * if (props.fps !== undefined) loop.setLimit(props.fps);
104
+ * },
105
+ * };
106
+ *
107
+ * window.onload = () => loop.start();
108
+ */
109
+ declare function createFpsLimiter(draw: (dt: number) => void): {
110
+ /** Start (or restart) the animation loop. */
111
+ start(): void;
112
+ /** Stop the animation loop. */
113
+ stop(): void;
114
+ /**
115
+ * Set the FPS cap. Pass `0` for unlimited.
116
+ * Safe to call at any time — takes effect on the next frame.
117
+ */
118
+ setLimit(fps: number): void;
119
+ };
120
+
121
+ export { clampAudio, createFpsLimiter, encodeCanvasForLed, leftChannel, parseWallpaperColor, rightChannel, toFileUrl, wallpaperColorToHex, wallpaperColorToRgb };
@@ -0,0 +1,86 @@
1
+ // src/helpers.ts
2
+ function parseWallpaperColor(value) {
3
+ const parts = value.split(" ");
4
+ return {
5
+ r: Math.ceil(+(parts[0] ?? "0") * 255),
6
+ g: Math.ceil(+(parts[1] ?? "0") * 255),
7
+ b: Math.ceil(+(parts[2] ?? "0") * 255)
8
+ };
9
+ }
10
+ function wallpaperColorToRgb(value) {
11
+ const { r, g, b } = parseWallpaperColor(value);
12
+ return `rgb(${r},${g},${b})`;
13
+ }
14
+ function wallpaperColorToHex(value) {
15
+ const { r, g, b } = parseWallpaperColor(value);
16
+ return "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
17
+ }
18
+ function toFileUrl(path) {
19
+ return "file:///" + path;
20
+ }
21
+ function clampAudio(audioArray) {
22
+ return audioArray.map((v) => Math.min(v, 1));
23
+ }
24
+ function leftChannel(audioArray) {
25
+ return audioArray.slice(0, 64);
26
+ }
27
+ function rightChannel(audioArray) {
28
+ return audioArray.slice(64, 128);
29
+ }
30
+ function encodeCanvasForLed(canvas) {
31
+ const ctx = canvas.getContext("2d");
32
+ if (!ctx) throw new Error("Could not get 2D context from canvas");
33
+ const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
34
+ let result = "";
35
+ for (let i = 0; i < data.length; i += 4) {
36
+ result += String.fromCodePoint(data[i], data[i + 1], data[i + 2]);
37
+ }
38
+ return result;
39
+ }
40
+ function createFpsLimiter(draw) {
41
+ let limit = 0;
42
+ let last = 0;
43
+ let threshold = 0;
44
+ let rafId = null;
45
+ function loop(timestamp) {
46
+ rafId = requestAnimationFrame(loop);
47
+ const now = timestamp / 1e3;
48
+ const dt = Math.min(now - last, 1);
49
+ last = now;
50
+ if (limit > 0) {
51
+ threshold += dt;
52
+ if (threshold < 1 / limit) return;
53
+ threshold -= 1 / limit;
54
+ }
55
+ draw(dt);
56
+ }
57
+ return {
58
+ start() {
59
+ if (rafId !== null) cancelAnimationFrame(rafId);
60
+ last = performance.now() / 1e3;
61
+ threshold = 0;
62
+ rafId = requestAnimationFrame(loop);
63
+ },
64
+ stop() {
65
+ if (rafId !== null) {
66
+ cancelAnimationFrame(rafId);
67
+ rafId = null;
68
+ }
69
+ },
70
+ setLimit(fps) {
71
+ limit = fps;
72
+ }
73
+ };
74
+ }
75
+ export {
76
+ clampAudio,
77
+ createFpsLimiter,
78
+ encodeCanvasForLed,
79
+ leftChannel,
80
+ parseWallpaperColor,
81
+ rightChannel,
82
+ toFileUrl,
83
+ wallpaperColorToHex,
84
+ wallpaperColorToRgb
85
+ };
86
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/helpers.ts"],"sourcesContent":["/**\r\n * Common utility helpers for Wallpaper Engine web wallpapers.\r\n *\r\n * Every export is side-effect-free and tree-shakeable — import only what you need.\r\n *\r\n * @example\r\n * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';\r\n */\r\n\r\n// ---------------------------------------------------------------------------\r\n// Color helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Parse a Wallpaper Engine color string (`\"R G B\"`, each channel 0–1) into\r\n * individual 0–255 integer channels.\r\n *\r\n * @example\r\n * const { r, g, b } = parseWallpaperColor(props.mycolor.value);\r\n * ctx.fillStyle = `rgb(${r},${g},${b})`;\r\n */\r\nexport function parseWallpaperColor(value: string): {\r\n r: number;\r\n g: number;\r\n b: number;\r\n} {\r\n const parts = value.split(\" \");\r\n return {\r\n r: Math.ceil(+(parts[0] ?? \"0\") * 255),\r\n g: Math.ceil(+(parts[1] ?? \"0\") * 255),\r\n b: Math.ceil(+(parts[2] ?? \"0\") * 255),\r\n };\r\n}\r\n\r\n/**\r\n * Convert a Wallpaper Engine color string to a CSS `rgb()` value.\r\n *\r\n * @example\r\n * el.style.color = wallpaperColorToRgb(props.mycolor.value);\r\n * // → \"rgb(255,128,0)\"\r\n */\r\nexport function wallpaperColorToRgb(value: string): string {\r\n const { r, g, b } = parseWallpaperColor(value);\r\n return `rgb(${r},${g},${b})`;\r\n}\r\n\r\n/**\r\n * Convert a Wallpaper Engine color string to a CSS hex color.\r\n *\r\n * @example\r\n * el.style.color = wallpaperColorToHex(props.mycolor.value);\r\n * // → \"#ff8000\"\r\n */\r\nexport function wallpaperColorToHex(value: string): string {\r\n const { r, g, b } = parseWallpaperColor(value);\r\n return \"#\" + [r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\");\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// File URL helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Prefix a Wallpaper Engine file/directory path with `file:///` to make it\r\n * usable as an `<img>` or `<video>` `src`.\r\n *\r\n * The raw path delivered by `applyUserProperties` is not a valid URL on its own.\r\n *\r\n * @example\r\n * img.src = toFileUrl(props.myimage.value);\r\n * // → \"file:///C:/Users/.../myimage.png\"\r\n */\r\nexport function toFileUrl(path: string): string {\r\n return \"file:///\" + path;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Audio helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.\r\n *\r\n * Due to the internal FFT implementation, values can occasionally exceed 1.0.\r\n * Clamping before use is strongly recommended by the official documentation.\r\n *\r\n * @example\r\n * window.wallpaperRegisterAudioListener((raw) => {\r\n * const audio = clampAudio(raw);\r\n * renderBars(audio);\r\n * });\r\n */\r\nexport function clampAudio(audioArray: number[]): number[] {\r\n return audioArray.map((v) => Math.min(v, 1));\r\n}\r\n\r\n/**\r\n * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine\r\n * audio array. Index 0 = bass, index 63 = treble.\r\n *\r\n * The full array has 128 values: 0–63 left, 64–127 right.\r\n */\r\nexport function leftChannel(audioArray: number[]): number[] {\r\n return audioArray.slice(0, 64);\r\n}\r\n\r\n/**\r\n * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine\r\n * audio array. Index 64 = bass, index 127 = treble.\r\n *\r\n * The full array has 128 values: 0–63 left, 64–127 right.\r\n */\r\nexport function rightChannel(audioArray: number[]): number[] {\r\n return audioArray.slice(64, 128);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// RGB / LED helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected\r\n * by `window.wpPlugins.led.setAllDevicesByImageData` and\r\n * `window.cue.setLedColorsByImageData`.\r\n *\r\n * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for\r\n * best performance — the plugins sample it to their device layout anyway.\r\n *\r\n * @example\r\n * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;\r\n * const encoded = encodeCanvasForLed(canvas);\r\n * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);\r\n */\r\nexport function encodeCanvasForLed(canvas: HTMLCanvasElement): string {\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) throw new Error(\"Could not get 2D context from canvas\");\r\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n let result = \"\";\r\n for (let i = 0; i < data.length; i += 4) {\r\n result += String.fromCodePoint(data[i]!, data[i + 1]!, data[i + 2]!);\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// FPS limiter\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors\r\n * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.\r\n *\r\n * @param draw - Called each allowed frame with `dt` — elapsed seconds since\r\n * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).\r\n * @returns An object with `start`, `stop`, and `setLimit` methods.\r\n *\r\n * @example\r\n * const loop = createFpsLimiter((dt) => renderFrame(dt));\r\n *\r\n * window.wallpaperPropertyListener = {\r\n * applyGeneralProperties(props) {\r\n * if (props.fps !== undefined) loop.setLimit(props.fps);\r\n * },\r\n * };\r\n *\r\n * window.onload = () => loop.start();\r\n */\r\nexport function createFpsLimiter(draw: (dt: number) => void): {\r\n /** Start (or restart) the animation loop. */\r\n start(): void;\r\n /** Stop the animation loop. */\r\n stop(): void;\r\n /**\r\n * Set the FPS cap. Pass `0` for unlimited.\r\n * Safe to call at any time — takes effect on the next frame.\r\n */\r\n setLimit(fps: number): void;\r\n} {\r\n let limit = 0;\r\n let last = 0;\r\n let threshold = 0;\r\n let rafId: number | null = null;\r\n\r\n function loop(timestamp: number): void {\r\n rafId = requestAnimationFrame(loop);\r\n const now = timestamp / 1000;\r\n const dt = Math.min(now - last, 1);\r\n last = now;\r\n\r\n if (limit > 0) {\r\n threshold += dt;\r\n if (threshold < 1 / limit) return;\r\n threshold -= 1 / limit;\r\n }\r\n\r\n draw(dt);\r\n }\r\n\r\n return {\r\n start() {\r\n if (rafId !== null) cancelAnimationFrame(rafId);\r\n last = performance.now() / 1000;\r\n threshold = 0;\r\n rafId = requestAnimationFrame(loop);\r\n },\r\n stop() {\r\n if (rafId !== null) {\r\n cancelAnimationFrame(rafId);\r\n rafId = null;\r\n }\r\n },\r\n setLimit(fps: number) {\r\n limit = fps;\r\n },\r\n };\r\n}\r\n"],"mappings":";AAqBO,SAAS,oBAAoB,OAIlC;AACA,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,SAAO;AAAA,IACL,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,IACrC,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,IACrC,GAAG,KAAK,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,GAAG;AAAA,EACvC;AACF;AASO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,oBAAoB,KAAK;AAC7C,SAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;AAC3B;AASO,SAAS,oBAAoB,OAAuB;AACzD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,oBAAoB,KAAK;AAC7C,SAAO,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC5E;AAgBO,SAAS,UAAU,MAAsB;AAC9C,SAAO,aAAa;AACtB;AAkBO,SAAS,WAAW,YAAgC;AACzD,SAAO,WAAW,IAAI,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC;AAC7C;AAQO,SAAS,YAAY,YAAgC;AAC1D,SAAO,WAAW,MAAM,GAAG,EAAE;AAC/B;AAQO,SAAS,aAAa,YAAgC;AAC3D,SAAO,WAAW,MAAM,IAAI,GAAG;AACjC;AAmBO,SAAS,mBAAmB,QAAmC;AACpE,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sCAAsC;AAChE,QAAM,EAAE,KAAK,IAAI,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AACnE,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,cAAU,OAAO,cAAc,KAAK,CAAC,GAAI,KAAK,IAAI,CAAC,GAAI,KAAK,IAAI,CAAC,CAAE;AAAA,EACrE;AACA,SAAO;AACT;AAyBO,SAAS,iBAAiB,MAU/B;AACA,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,MAAI,QAAuB;AAE3B,WAAS,KAAK,WAAyB;AACrC,YAAQ,sBAAsB,IAAI;AAClC,UAAM,MAAM,YAAY;AACxB,UAAM,KAAK,KAAK,IAAI,MAAM,MAAM,CAAC;AACjC,WAAO;AAEP,QAAI,QAAQ,GAAG;AACb,mBAAa;AACb,UAAI,YAAY,IAAI,MAAO;AAC3B,mBAAa,IAAI;AAAA,IACnB;AAEA,SAAK,EAAE;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,UAAI,UAAU,KAAM,sBAAqB,KAAK;AAC9C,aAAO,YAAY,IAAI,IAAI;AAC3B,kBAAY;AACZ,cAAQ,sBAAsB,IAAI;AAAA,IACpC;AAAA,IACA,OAAO;AACL,UAAI,UAAU,MAAM;AAClB,6BAAqB,KAAK;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,SAAS,KAAa;AACpB,cAAQ;AAAA,IACV;AAAA,EACF;AACF;","names":[]}
package/dist/index.cjs ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PLAYBACK_PAUSED: () => PLAYBACK_PAUSED,
24
+ PLAYBACK_PLAYING: () => PLAYBACK_PLAYING,
25
+ PLAYBACK_STOPPED: () => PLAYBACK_STOPPED
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/types/listeners.ts
30
+ var PLAYBACK_PLAYING = 0;
31
+ var PLAYBACK_PAUSED = 1;
32
+ var PLAYBACK_STOPPED = 2;
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ PLAYBACK_PAUSED,
36
+ PLAYBACK_PLAYING,
37
+ PLAYBACK_STOPPED
38
+ });
39
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types/listeners.ts"],"sourcesContent":["// Types for project.json structure\r\nexport type {\r\n WallpaperBoolProperty,\r\n WallpaperColorProperty,\r\n WallpaperComboOption,\r\n WallpaperComboProperty,\r\n WallpaperDirectoryProperty,\r\n WallpaperFileProperty,\r\n WallpaperFileType,\r\n WallpaperLocalization,\r\n WallpaperProject,\r\n WallpaperProjectGeneral,\r\n WallpaperPropertyDefinition,\r\n WallpaperSliderProperty,\r\n WallpaperTextInputProperty,\r\n} from \"./types/project\";\r\n\r\n// Runtime listener / event types\r\nexport type {\r\n CueDeviceInfo,\r\n CueLedColor,\r\n CueLedPosition,\r\n CueProtocolDetails,\r\n WallpaperBoolValue,\r\n WallpaperColorValue,\r\n WallpaperComboValue,\r\n WallpaperCuePlugin,\r\n WallpaperDirectoryValue,\r\n WallpaperFileValue,\r\n WallpaperGeneralProperties,\r\n WallpaperLedPlugin,\r\n WallpaperMediaPlaybackEvent,\r\n WallpaperMediaPlaybackState,\r\n WallpaperMediaPropertiesEvent,\r\n WallpaperMediaStatusEvent,\r\n WallpaperMediaThumbnailEvent,\r\n WallpaperMediaTimelineEvent,\r\n WallpaperPluginListener,\r\n WallpaperPropertyListener,\r\n WallpaperPropertyRuntimeValue,\r\n WallpaperSliderValue,\r\n WallpaperTextValue,\r\n WallpaperUserProperties,\r\n} from \"./types/listeners\";\r\n\r\nexport {\r\n PLAYBACK_PAUSED,\r\n PLAYBACK_PLAYING,\r\n PLAYBACK_STOPPED,\r\n} from \"./types/listeners\";\r\n\r\n// Window global augmentation — import this file (or the package root) in your\r\n// wallpaper entry file to get typed access to window.wallpaperPropertyListener,\r\n// window.wallpaperRegisterAudioListener, etc.\r\nimport \"./types/window\";\r\n","// ---------------------------------------------------------------------------\r\n// Runtime property values (received inside applyUserProperties)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Runtime value of a color property.\r\n * `value` is `\"R G B\"` where each channel is in the **0–1** range.\r\n * Multiply by 255 to convert for CSS `rgb()`.\r\n *\r\n * @example\r\n * const [r, g, b] = props.myColor.value.split(' ').map(c => Math.ceil(+c * 255));\r\n * el.style.color = `rgb(${r},${g},${b})`;\r\n */\r\nexport interface WallpaperColorValue {\r\n /** Color string `\"R G B\"` — each channel 0.0–1.0. */\r\n value: string;\r\n}\r\n\r\n/** Runtime value of a slider property. */\r\nexport interface WallpaperSliderValue {\r\n value: number;\r\n}\r\n\r\n/** Runtime value of a checkbox (`bool`) property. */\r\nexport interface WallpaperBoolValue {\r\n value: boolean;\r\n}\r\n\r\n/**\r\n * Runtime value of a combo (dropdown) property.\r\n * `value` is the hidden key configured in the editor;\r\n * `text` is the visible display label.\r\n */\r\nexport interface WallpaperComboValue {\r\n /** The hidden value configured on the combo option. */\r\n value: string;\r\n /** Display label of the selected option (may be a `ui_` localization token). */\r\n text: string;\r\n}\r\n\r\n/** Runtime value of a text-input property. */\r\nexport interface WallpaperTextValue {\r\n value: string;\r\n}\r\n\r\n/**\r\n * Runtime value of a file property.\r\n * Prepend `\"file:///\"` to `value` before using it as a URL.\r\n *\r\n * @example\r\n * img.src = 'file:///' + props.myImage.value;\r\n */\r\nexport interface WallpaperFileValue {\r\n /** File path — prepend `\"file:///\"` before use as a URL. */\r\n value: string;\r\n}\r\n\r\n/**\r\n * Runtime value of a directory property in `ondemand` mode.\r\n * An empty string means no directory is currently selected.\r\n * Call `wallpaperRequestRandomFileForProperty` to retrieve a random file.\r\n */\r\nexport interface WallpaperDirectoryValue {\r\n /** Directory path, or empty string when no directory is selected. */\r\n value: string;\r\n}\r\n\r\n/** Union of all possible runtime property values delivered by `applyUserProperties`. */\r\nexport type WallpaperPropertyRuntimeValue =\r\n | WallpaperColorValue\r\n | WallpaperSliderValue\r\n | WallpaperBoolValue\r\n | WallpaperComboValue\r\n | WallpaperTextValue\r\n | WallpaperFileValue\r\n | WallpaperDirectoryValue;\r\n\r\n/**\r\n * Map of property key → current runtime value passed to\r\n * `wallpaperPropertyListener.applyUserProperties`.\r\n * On first load all properties are present; on subsequent calls only the\r\n * changed ones are included — always guard with `if (properties.key)`.\r\n */\r\nexport type WallpaperUserProperties = Record<\r\n string,\r\n WallpaperPropertyRuntimeValue\r\n>;\r\n\r\n/** App-level settings delivered by `wallpaperPropertyListener.applyGeneralProperties`. */\r\nexport interface WallpaperGeneralProperties {\r\n /** Current FPS limit set by the user in Wallpaper Engine settings. `0` = unlimited. */\r\n fps?: number;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// wallpaperPropertyListener\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Assign to `window.wallpaperPropertyListener` to receive property and\r\n * app-settings updates. **Must be a top-level global** — never assign inside\r\n * `window.onload` or other events, or startup events may be missed.\r\n *\r\n * @example\r\n * window.wallpaperPropertyListener = {\r\n * applyUserProperties(properties) {\r\n * if (properties.mycolor) { ... }\r\n * },\r\n * };\r\n */\r\nexport interface WallpaperPropertyListener {\r\n /**\r\n * Called once on wallpaper load (with all user properties) and again\r\n * whenever the user changes a property. Only changed properties are\r\n * included on subsequent calls — always guard with `if (properties.key)`.\r\n */\r\n applyUserProperties?: (properties: WallpaperUserProperties) => void;\r\n /**\r\n * Called on load and whenever the user changes app-level settings\r\n * such as the FPS limit. See {@link WallpaperGeneralProperties}.\r\n */\r\n applyGeneralProperties?: (properties: WallpaperGeneralProperties) => void;\r\n /**\r\n * Called for `directory` properties in `fetchall` mode when files are\r\n * added or modified in the selected directory. Prepend `\"file:///\"` to\r\n * each path before using it as a URL.\r\n */\r\n userDirectoryFilesAddedOrChanged?: (\r\n propertyName: string,\r\n changedFiles: string[],\r\n ) => void;\r\n /**\r\n * Called for `directory` properties in `fetchall` mode when files are\r\n * removed from the selected directory.\r\n */\r\n userDirectoryFilesRemoved?: (\r\n propertyName: string,\r\n removedFiles: string[],\r\n ) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// wallpaperPluginListener\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Assign to `window.wallpaperPluginListener` to detect when RGB plugins load.\r\n * Check `name` to distinguish `\"led\"` (all RGB hardware via `wpPlugins.led`)\r\n * from `\"cue\"` (Corsair iCUE direct SDK, only needed for advanced features).\r\n *\r\n * @example\r\n * window.wallpaperPluginListener = {\r\n * onPluginLoaded(name, version) {\r\n * if (name === 'led') { // general RGB hardware ready\r\n * }\r\n * if (name === 'cue') { // Corsair iCUE SDK ready\r\n * }\r\n * },\r\n * };\r\n */\r\nexport interface WallpaperPluginListener {\r\n /**\r\n * Fired when a plugin finishes loading.\r\n * @param name - `\"led\"` for general RGB hardware or `\"cue\"` for Corsair iCUE.\r\n * @param version - Plugin version string.\r\n */\r\n onPluginLoaded?: (name: string, version: string) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Media integration events\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Event fired by `wallpaperRegisterMediaStatusListener` when the user enables\r\n * or disables media integration in the Wallpaper Engine app settings.\r\n */\r\nexport interface WallpaperMediaStatusEvent {\r\n /** `true` when media integration is active; `false` when disabled. */\r\n enabled: boolean;\r\n}\r\n\r\n/**\r\n * Event fired by `wallpaperRegisterMediaPropertiesListener` when the\r\n * currently playing track's metadata changes (title, artist, album, etc.).\r\n */\r\nexport interface WallpaperMediaPropertiesEvent {\r\n /** Title of the currently playing media. */\r\n title: string;\r\n /** Artist of the currently playing media. */\r\n artist: string;\r\n /** Optional sub-title (e.g. episode name for podcasts). */\r\n subTitle?: string;\r\n /** Optional album title. */\r\n albumTitle?: string;\r\n /** Optional album artist (may differ from `artist`). */\r\n albumArtist?: string;\r\n /** Optional comma-separated list of genres. */\r\n genres?: string;\r\n /** Media type: `\"music\"`, `\"video\"`, or `\"image\"`. */\r\n contentType: \"music\" | \"video\" | \"image\";\r\n}\r\n\r\n/**\r\n * Event fired by `wallpaperRegisterMediaThumbnailListener` when album art changes.\r\n * Assign `event.thumbnail` directly to `img.src`.\r\n *\r\n * @example\r\n * window.wallpaperRegisterMediaThumbnailListener((e) => {\r\n * document.getElementById('cover').src = e.thumbnail;\r\n * document.body.style.background = e.primaryColor;\r\n * titleEl.style.color = e.textColor;\r\n * });\r\n */\r\nexport interface WallpaperMediaThumbnailEvent {\r\n /** Base64-encoded PNG of the album art — usable directly as `img.src`. */\r\n thumbnail: string;\r\n /** Dominant color extracted from the thumbnail. */\r\n primaryColor: string;\r\n /** Secondary color from the thumbnail palette. */\r\n secondaryColor: string;\r\n /** Tertiary color from the thumbnail palette. */\r\n tertiaryColor: string;\r\n /**\r\n * Text color guaranteed to contrast sufficiently against `primaryColor`.\r\n * May be `secondaryColor` or `tertiaryColor` when the contrast is sufficient.\r\n */\r\n textColor: string;\r\n /** Either black or white — whichever contrasts more against `primaryColor`. */\r\n highContrastColor: string;\r\n}\r\n\r\n/** Media is actively playing on the system. */\r\nexport const PLAYBACK_PLAYING = 0 as const;\r\n/** Media was playing but has been temporarily paused by the user. */\r\nexport const PLAYBACK_PAUSED = 1 as const;\r\n/** Media playback is completely stopped. */\r\nexport const PLAYBACK_STOPPED = 2 as const;\r\n\r\n/**\r\n * Numeric playback state for {@link WallpaperMediaPlaybackEvent}.\r\n * Compare against `window.wallpaperMediaIntegration.PLAYBACK_PLAYING` etc.\r\n * or the module-level constants {@link PLAYBACK_PLAYING}, {@link PLAYBACK_PAUSED},\r\n * {@link PLAYBACK_STOPPED}.\r\n */\r\nexport type WallpaperMediaPlaybackState =\r\n | typeof PLAYBACK_PLAYING\r\n | typeof PLAYBACK_PAUSED\r\n | typeof PLAYBACK_STOPPED;\r\n\r\n/**\r\n * Event fired by `wallpaperRegisterMediaPlaybackListener` when playback\r\n * starts, pauses, or stops.\r\n */\r\nexport interface WallpaperMediaPlaybackEvent {\r\n /**\r\n * Current playback state. Compare against the `PLAYBACK_*` constants:\r\n * - `PLAYBACK_PLAYING` (`0`) — actively playing\r\n * - `PLAYBACK_PAUSED` (`1`) — temporarily paused\r\n * - `PLAYBACK_STOPPED` (`2`) — playback stopped\r\n */\r\n state: WallpaperMediaPlaybackState;\r\n}\r\n\r\n/**\r\n * Event fired by `wallpaperRegisterMediaTimelineListener` when track position\r\n * changes. **Not all media players support this** — ensure your wallpaper\r\n * works correctly even if this listener is never called.\r\n */\r\nexport interface WallpaperMediaTimelineEvent {\r\n /** Current playback position in seconds. */\r\n position: number;\r\n /** Total track duration in seconds. */\r\n duration: number;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Corsair iCUE SDK types (window.cue)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Returned by `window.cue.getProtocolDetails` */\r\nexport interface CueProtocolDetails {\r\n sdkVersion: string;\r\n serverVersion: string;\r\n sdkProtocolVersion: number;\r\n serverProtocolVersion: number;\r\n breakingChanges: boolean;\r\n}\r\n\r\n/** Returned by `window.cue.getDeviceInfo` — mirrors `CorsairDeviceInfo` in the C++ SDK */\r\nexport interface CueDeviceInfo {\r\n /** `CorsairDeviceType` enum value */\r\n type: number;\r\n /** Human-readable device model name */\r\n model: string;\r\n /** `CorsairPhysicalLayout` enum value */\r\n physicalLayout: number;\r\n /** `CorsairLogicalLayout` enum value */\r\n logicalLayout: number;\r\n /** Number of available LEDs on this device */\r\n ledCount: number;\r\n /** `CorsairDeviceCaps` bitmask */\r\n capsMask: number;\r\n}\r\n\r\n/** An entry in the array returned by `window.cue.getLedPositionsByDeviceIndex` */\r\nexport interface CueLedPosition {\r\n /** `CorsairLedId` as integer */\r\n ledId: number;\r\n /** `CorsairLedId` as string */\r\n ledIdName: string;\r\n /** Position in mm from the top of the device */\r\n top: number;\r\n /** Position in mm from the left of the device */\r\n left: number;\r\n width: number;\r\n height: number;\r\n}\r\n\r\n/** LED color entry used in `setLedsColorsAsync` / `setAllLedsColorsAsync` */\r\nexport interface CueLedColor {\r\n /** `CorsairLedId` as integer */\r\n ledId: number;\r\n r: number;\r\n g: number;\r\n b: number;\r\n}\r\n\r\n/**\r\n * Direct Corsair iCUE SDK access exposed as `window.cue`.\r\n * Only available after the `\"cue\"` plugin has loaded via `wallpaperPluginListener`.\r\n */\r\nexport interface WallpaperCuePlugin {\r\n /** Returns current iCUE SDK status and version info */\r\n getProtocolDetails(\r\n callback: (protocolDetails: CueProtocolDetails) => void,\r\n ): void;\r\n /** Returns the number of recognised iCUE-compatible devices */\r\n getDeviceCount(callback: (deviceCount: number) => void): void;\r\n /** Returns hardware details for a specific device by index */\r\n getDeviceInfo(\r\n deviceIndex: number,\r\n callback: (deviceInfo: CueDeviceInfo) => void,\r\n ): void;\r\n /** Returns all LED positions for a specific device */\r\n getLedPositionsByDeviceIndex(\r\n callback: (arrayOfLEDs: CueLedPosition[]) => void,\r\n ): void;\r\n /** Update specific LEDs by supplying an array of `CueLedColor` objects */\r\n setLedsColorsAsync(arrayOfLEDColors: CueLedColor[]): void;\r\n /** Set all LEDs on one or more devices to a single color */\r\n setAllLedsColorsAsync(\r\n deviceIndexOrArray: number | number[],\r\n LEDColor: CueLedColor,\r\n ): void;\r\n /**\r\n * Update device LEDs from an RGB bitmap string (same format as\r\n * `wpPlugins.led.setAllDevicesByImageData`, but targets specific devices).\r\n */\r\n setLedColorsByImageData(\r\n deviceIndexOrArray: number | number[],\r\n encodedImageData: string,\r\n width: number,\r\n height: number,\r\n ): void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// RGB / LED plugin\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface WallpaperLedPlugin {\r\n /**\r\n * Push concatenated RGB bytes (as a string) to all connected LED devices.\r\n * Use a small canvas (e.g. 100×20) and convert it with `getImageData` for\r\n * best performance.\r\n */\r\n setAllDevicesByImageData(\r\n encodedImageData: string,\r\n width: number,\r\n height: number,\r\n ): void;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACyOO,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;","names":[]}