wallpaper-engine 1.0.0 → 1.0.1

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.
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../src/helpers.ts"],"sourcesContent":["/**\n * Common utility helpers for Wallpaper Engine web wallpapers.\n *\n * Every export is side-effect-free and tree-shakeable — import only what you need.\n *\n * @example\n * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';\n */\n\n// ---------------------------------------------------------------------------\n// Color helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a Wallpaper Engine color string (`\"R G B\"`, each channel 0–1) into\n * individual 0–255 integer channels.\n *\n * @example\n * const { r, g, b } = parseWallpaperColor(props.mycolor.value);\n * ctx.fillStyle = `rgb(${r},${g},${b})`;\n */\nexport function parseWallpaperColor(value: string): {\n r: number;\n g: number;\n b: number;\n} {\n const parts = value.split(\" \");\n return {\n r: Math.ceil(+(parts[0] ?? \"0\") * 255),\n g: Math.ceil(+(parts[1] ?? \"0\") * 255),\n b: Math.ceil(+(parts[2] ?? \"0\") * 255),\n };\n}\n\n/**\n * Convert a Wallpaper Engine color string to a CSS `rgb()` value.\n *\n * @example\n * el.style.color = wallpaperColorToRgb(props.mycolor.value);\n * // → \"rgb(255,128,0)\"\n */\nexport function wallpaperColorToRgb(value: string): string {\n const { r, g, b } = parseWallpaperColor(value);\n return `rgb(${r},${g},${b})`;\n}\n\n/**\n * Convert a Wallpaper Engine color string to a CSS hex color.\n *\n * @example\n * el.style.color = wallpaperColorToHex(props.mycolor.value);\n * // → \"#ff8000\"\n */\nexport function wallpaperColorToHex(value: string): string {\n const { r, g, b } = parseWallpaperColor(value);\n return \"#\" + [r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// File URL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Prefix a Wallpaper Engine file/directory path with `file:///` to make it\n * usable as an `<img>` or `<video>` `src`.\n *\n * The raw path delivered by `applyUserProperties` is not a valid URL on its own.\n *\n * @example\n * img.src = toFileUrl(props.myimage.value);\n * // → \"file:///C:/Users/.../myimage.png\"\n */\nexport function toFileUrl(path: string): string {\n return \"file:///\" + path;\n}\n\n// ---------------------------------------------------------------------------\n// Audio helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.\n *\n * Due to the internal FFT implementation, values can occasionally exceed 1.0.\n * Clamping before use is strongly recommended by the official documentation.\n *\n * @example\n * window.wallpaperRegisterAudioListener((raw) => {\n * const audio = clampAudio(raw);\n * renderBars(audio);\n * });\n */\nexport function clampAudio(audioArray: number[]): number[] {\n return audioArray.map((v) => Math.min(v, 1));\n}\n\n/**\n * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine\n * audio array. Index 0 = bass, index 63 = treble.\n *\n * The full array has 128 values: 0–63 left, 64–127 right.\n */\nexport function leftChannel(audioArray: number[]): number[] {\n return audioArray.slice(0, 64);\n}\n\n/**\n * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine\n * audio array. Index 64 = bass, index 127 = treble.\n *\n * The full array has 128 values: 0–63 left, 64–127 right.\n */\nexport function rightChannel(audioArray: number[]): number[] {\n return audioArray.slice(64, 128);\n}\n\n// ---------------------------------------------------------------------------\n// RGB / LED helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected\n * by `window.wpPlugins.led.setAllDevicesByImageData` and\n * `window.cue.setLedColorsByImageData`.\n *\n * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for\n * best performance — the plugins sample it to their device layout anyway.\n *\n * @example\n * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;\n * const encoded = encodeCanvasForLed(canvas);\n * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);\n */\nexport function encodeCanvasForLed(canvas: HTMLCanvasElement): string {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Could not get 2D context from canvas\");\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\n let result = \"\";\n for (let i = 0; i < data.length; i += 4) {\n result += String.fromCodePoint(data[i]!, data[i + 1]!, data[i + 2]!);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// FPS limiter\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors\n * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.\n *\n * @param draw - Called each allowed frame with `dt` — elapsed seconds since\n * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).\n * @returns An object with `start`, `stop`, and `setLimit` methods.\n *\n * @example\n * const loop = createFpsLimiter((dt) => renderFrame(dt));\n *\n * window.wallpaperPropertyListener = {\n * applyGeneralProperties(props) {\n * if (props.fps !== undefined) loop.setLimit(props.fps);\n * },\n * };\n *\n * window.onload = () => loop.start();\n */\nexport function createFpsLimiter(draw: (dt: number) => void): {\n /** Start (or restart) the animation loop. */\n start(): void;\n /** Stop the animation loop. */\n stop(): void;\n /**\n * Set the FPS cap. Pass `0` for unlimited.\n * Safe to call at any time — takes effect on the next frame.\n */\n setLimit(fps: number): void;\n} {\n let limit = 0;\n let last = 0;\n let threshold = 0;\n let rafId: number | null = null;\n\n function loop(timestamp: number): void {\n rafId = requestAnimationFrame(loop);\n const now = timestamp / 1000;\n const dt = Math.min(now - last, 1);\n last = now;\n\n if (limit > 0) {\n threshold += dt;\n if (threshold < 1 / limit) return;\n threshold -= 1 / limit;\n }\n\n draw(dt);\n }\n\n return {\n start() {\n if (rafId !== null) cancelAnimationFrame(rafId);\n last = performance.now() / 1000;\n threshold = 0;\n rafId = requestAnimationFrame(loop);\n },\n stop() {\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n },\n setLimit(fps: number) {\n limit = fps;\n },\n };\n}\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":[]}
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../src/helpers.ts"],"sourcesContent":["/**\n * Common utility helpers for Wallpaper Engine web wallpapers.\n *\n * Every export is side-effect-free and tree-shakeable — import only what you need.\n *\n * @example\n * import { wallpaperColorToRgb, toFileUrl, clampAudio, createFpsLimiter } from 'wallpaper-engine/helpers';\n */\n\n// ---------------------------------------------------------------------------\n// Color helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a Wallpaper Engine color string (`\"R G B\"`, each channel 0–1) into\n * individual 0–255 integer channels.\n *\n * @example\n * const { r, g, b } = parseWallpaperColor(props.mycolor.value);\n * ctx.fillStyle = `rgb(${r},${g},${b})`;\n */\nexport function parseWallpaperColor(value: string): {\n r: number;\n g: number;\n b: number;\n} {\n const parts = value.split(\" \");\n return {\n r: Math.ceil(+(parts[0] ?? \"0\") * 255),\n g: Math.ceil(+(parts[1] ?? \"0\") * 255),\n b: Math.ceil(+(parts[2] ?? \"0\") * 255),\n };\n}\n\n/**\n * Convert a Wallpaper Engine color string to a CSS `rgb()` value.\n *\n * @example\n * el.style.color = wallpaperColorToRgb(props.mycolor.value);\n * // → \"rgb(255,128,0)\"\n */\nexport function wallpaperColorToRgb(value: string): string {\n const { r, g, b } = parseWallpaperColor(value);\n return `rgb(${r},${g},${b})`;\n}\n\n/**\n * Convert a Wallpaper Engine color string to a CSS hex color.\n *\n * @example\n * el.style.color = wallpaperColorToHex(props.mycolor.value);\n * // → \"#ff8000\"\n */\nexport function wallpaperColorToHex(value: string): string {\n const { r, g, b } = parseWallpaperColor(value);\n return \"#\" + [r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// File URL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Prefix a Wallpaper Engine file/directory path with `file:///` to make it\n * usable as an `<img>` or `<video>` `src`.\n *\n * The raw path delivered by `applyUserProperties` is not a valid URL on its own.\n *\n * @example\n * img.src = toFileUrl(props.myimage.value);\n * // → \"file:///C:/Users/.../myimage.png\"\n */\nexport function toFileUrl(path: string): string {\n return \"file:///\" + path;\n}\n\n// ---------------------------------------------------------------------------\n// Audio helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Clamp all values in a Wallpaper Engine audio array to the 0–1 range.\n *\n * Due to the internal FFT implementation, values can occasionally exceed 1.0.\n * Clamping before use is strongly recommended by the official documentation.\n *\n * @example\n * window.wallpaperRegisterAudioListener((raw) => {\n * const audio = clampAudio(raw);\n * renderBars(audio);\n * });\n */\nexport function clampAudio(audioArray: number[]): number[] {\n return audioArray.map((v) => Math.min(v, 1));\n}\n\n/**\n * Extract the **left** audio channel (indices 0–63) from a Wallpaper Engine\n * audio array. Index 0 = bass, index 63 = treble.\n *\n * The full array has 128 values: 0–63 left, 64–127 right.\n */\nexport function leftChannel(audioArray: number[]): number[] {\n return audioArray.slice(0, 64);\n}\n\n/**\n * Extract the **right** audio channel (indices 64–127) from a Wallpaper Engine\n * audio array. Index 64 = bass, index 127 = treble.\n *\n * The full array has 128 values: 0–63 left, 64–127 right.\n */\nexport function rightChannel(audioArray: number[]): number[] {\n return audioArray.slice(64, 128);\n}\n\n// ---------------------------------------------------------------------------\n// RGB / LED helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Encode an `HTMLCanvasElement` into the concatenated RGB byte string expected\n * by `window.wpPlugins.led.setAllDevicesByImageData` and\n * `window.cue.setLedColorsByImageData`.\n *\n * The alpha channel is discarded. Use a small canvas (e.g. 100×20 px) for\n * best performance — the plugins sample it to their device layout anyway.\n *\n * @example\n * const canvas = document.getElementById('RGBCanvas') as HTMLCanvasElement;\n * const encoded = encodeCanvasForLed(canvas);\n * window.wpPlugins.led.setAllDevicesByImageData(encoded, canvas.width, canvas.height);\n */\nexport function encodeCanvasForLed(canvas: HTMLCanvasElement): string {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Could not get 2D context from canvas\");\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\n let result = \"\";\n for (let i = 0; i < data.length; i += 4) {\n result += String.fromCodePoint(data[i]!, data[i + 1]!, data[i + 2]!);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// FPS limiter\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `requestAnimationFrame` loop with an optional FPS cap that mirrors\n * the limit delivered by `applyGeneralProperties`. Pass `0` for unlimited.\n *\n * @param draw - Called each allowed frame with `dt` — elapsed seconds since\n * the last allowed frame (capped at 1 s to avoid spiral-of-death on tab focus).\n * @returns An object with `start`, `stop`, and `setLimit` methods.\n *\n * @example\n * const loop = createFpsLimiter((dt) => renderFrame(dt));\n *\n * window.wallpaperPropertyListener = {\n * applyGeneralProperties(props) {\n * if (props.fps !== undefined) loop.setLimit(props.fps);\n * },\n * };\n *\n * window.onload = () => loop.start();\n */\nexport function createFpsLimiter(draw: (dt: number) => void): {\n /** Start (or restart) the animation loop. */\n start(): void;\n /** Stop the animation loop. */\n stop(): void;\n /**\n * Set the FPS cap. Pass `0` for unlimited.\n * Safe to call at any time — takes effect on the next frame.\n */\n setLimit(fps: number): void;\n} {\n let limit = 0;\n let last = 0;\n let threshold = 0;\n let rafId: number | null = null;\n\n function loop(timestamp: number): void {\n rafId = requestAnimationFrame(loop);\n const now = timestamp / 1000;\n const dt = Math.min(now - last, 1);\n last = now;\n\n if (limit > 0) {\n threshold += dt;\n if (threshold < 1 / limit) return;\n threshold -= 1 / limit;\n }\n\n draw(dt);\n }\n\n return {\n start() {\n if (rafId !== null) cancelAnimationFrame(rafId);\n last = performance.now() / 1000;\n threshold = 0;\n rafId = requestAnimationFrame(loop);\n },\n stop() {\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n },\n setLimit(fps: number) {\n limit = fps;\n },\n };\n}\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":[]}
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types/listeners.ts"],"sourcesContent":["// Types for project.json structure\nexport type {\n WallpaperBoolProperty,\n WallpaperColorProperty,\n WallpaperComboOption,\n WallpaperComboProperty,\n WallpaperDirectoryProperty,\n WallpaperFileProperty,\n WallpaperFileType,\n WallpaperLocalization,\n WallpaperProject,\n WallpaperProjectGeneral,\n WallpaperPropertyDefinition,\n WallpaperSliderProperty,\n WallpaperTextInputProperty,\n} from \"./types/project\";\n\n// Runtime listener / event types\nexport type {\n CueDeviceInfo,\n CueLedColor,\n CueLedPosition,\n CueProtocolDetails,\n WallpaperBoolValue,\n WallpaperColorValue,\n WallpaperComboValue,\n WallpaperCuePlugin,\n WallpaperDirectoryValue,\n WallpaperFileValue,\n WallpaperGeneralProperties,\n WallpaperLedPlugin,\n WallpaperMediaPlaybackEvent,\n WallpaperMediaPlaybackState,\n WallpaperMediaPropertiesEvent,\n WallpaperMediaStatusEvent,\n WallpaperMediaThumbnailEvent,\n WallpaperMediaTimelineEvent,\n WallpaperPluginListener,\n WallpaperPropertyListener,\n WallpaperPropertyRuntimeValue,\n WallpaperSliderValue,\n WallpaperTextValue,\n WallpaperUserProperties,\n} from \"./types/listeners\";\n\nexport {\n PLAYBACK_PAUSED,\n PLAYBACK_PLAYING,\n PLAYBACK_STOPPED,\n} from \"./types/listeners\";\n\n// Window global augmentation — import this file (or the package root) in your\n// wallpaper entry file to get typed access to window.wallpaperPropertyListener,\n// window.wallpaperRegisterAudioListener, etc.\nimport \"./types/window\";\n","// ---------------------------------------------------------------------------\n// Runtime property values (received inside applyUserProperties)\n// ---------------------------------------------------------------------------\n\n/**\n * Runtime value of a color property.\n * `value` is `\"R G B\"` where each channel is in the **0–1** range.\n * Multiply by 255 to convert for CSS `rgb()`.\n *\n * @example\n * const [r, g, b] = props.myColor.value.split(' ').map(c => Math.ceil(+c * 255));\n * el.style.color = `rgb(${r},${g},${b})`;\n */\nexport interface WallpaperColorValue {\n /** Color string `\"R G B\"` — each channel 0.0–1.0. */\n value: string;\n}\n\n/** Runtime value of a slider property. */\nexport interface WallpaperSliderValue {\n value: number;\n}\n\n/** Runtime value of a checkbox (`bool`) property. */\nexport interface WallpaperBoolValue {\n value: boolean;\n}\n\n/**\n * Runtime value of a combo (dropdown) property.\n * `value` is the hidden key configured in the editor;\n * `text` is the visible display label.\n */\nexport interface WallpaperComboValue {\n /** The hidden value configured on the combo option. */\n value: string;\n /** Display label of the selected option (may be a `ui_` localization token). */\n text: string;\n}\n\n/** Runtime value of a text-input property. */\nexport interface WallpaperTextValue {\n value: string;\n}\n\n/**\n * Runtime value of a file property.\n * Prepend `\"file:///\"` to `value` before using it as a URL.\n *\n * @example\n * img.src = 'file:///' + props.myImage.value;\n */\nexport interface WallpaperFileValue {\n /** File path — prepend `\"file:///\"` before use as a URL. */\n value: string;\n}\n\n/**\n * Runtime value of a directory property in `ondemand` mode.\n * An empty string means no directory is currently selected.\n * Call `wallpaperRequestRandomFileForProperty` to retrieve a random file.\n */\nexport interface WallpaperDirectoryValue {\n /** Directory path, or empty string when no directory is selected. */\n value: string;\n}\n\n/** Union of all possible runtime property values delivered by `applyUserProperties`. */\nexport type WallpaperPropertyRuntimeValue =\n | WallpaperColorValue\n | WallpaperSliderValue\n | WallpaperBoolValue\n | WallpaperComboValue\n | WallpaperTextValue\n | WallpaperFileValue\n | WallpaperDirectoryValue;\n\n/**\n * Map of property key → current runtime value passed to\n * `wallpaperPropertyListener.applyUserProperties`.\n * On first load all properties are present; on subsequent calls only the\n * changed ones are included — always guard with `if (properties.key)`.\n */\nexport type WallpaperUserProperties = Record<\n string,\n WallpaperPropertyRuntimeValue\n>;\n\n/** App-level settings delivered by `wallpaperPropertyListener.applyGeneralProperties`. */\nexport interface WallpaperGeneralProperties {\n /** Current FPS limit set by the user in Wallpaper Engine settings. `0` = unlimited. */\n fps?: number;\n}\n\n// ---------------------------------------------------------------------------\n// wallpaperPropertyListener\n// ---------------------------------------------------------------------------\n\n/**\n * Assign to `window.wallpaperPropertyListener` to receive property and\n * app-settings updates. **Must be a top-level global** — never assign inside\n * `window.onload` or other events, or startup events may be missed.\n *\n * @example\n * window.wallpaperPropertyListener = {\n * applyUserProperties(properties) {\n * if (properties.mycolor) { ... }\n * },\n * };\n */\nexport interface WallpaperPropertyListener {\n /**\n * Called once on wallpaper load (with all user properties) and again\n * whenever the user changes a property. Only changed properties are\n * included on subsequent calls — always guard with `if (properties.key)`.\n */\n applyUserProperties?: (properties: WallpaperUserProperties) => void;\n /**\n * Called on load and whenever the user changes app-level settings\n * such as the FPS limit. See {@link WallpaperGeneralProperties}.\n */\n applyGeneralProperties?: (properties: WallpaperGeneralProperties) => void;\n /**\n * Called for `directory` properties in `fetchall` mode when files are\n * added or modified in the selected directory. Prepend `\"file:///\"` to\n * each path before using it as a URL.\n */\n userDirectoryFilesAddedOrChanged?: (\n propertyName: string,\n changedFiles: string[],\n ) => void;\n /**\n * Called for `directory` properties in `fetchall` mode when files are\n * removed from the selected directory.\n */\n userDirectoryFilesRemoved?: (\n propertyName: string,\n removedFiles: string[],\n ) => void;\n}\n\n// ---------------------------------------------------------------------------\n// wallpaperPluginListener\n// ---------------------------------------------------------------------------\n\n/**\n * Assign to `window.wallpaperPluginListener` to detect when RGB plugins load.\n * Check `name` to distinguish `\"led\"` (all RGB hardware via `wpPlugins.led`)\n * from `\"cue\"` (Corsair iCUE direct SDK, only needed for advanced features).\n *\n * @example\n * window.wallpaperPluginListener = {\n * onPluginLoaded(name, version) {\n * if (name === 'led') { // general RGB hardware ready\n * }\n * if (name === 'cue') { // Corsair iCUE SDK ready\n * }\n * },\n * };\n */\nexport interface WallpaperPluginListener {\n /**\n * Fired when a plugin finishes loading.\n * @param name - `\"led\"` for general RGB hardware or `\"cue\"` for Corsair iCUE.\n * @param version - Plugin version string.\n */\n onPluginLoaded?: (name: string, version: string) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Media integration events\n// ---------------------------------------------------------------------------\n\n/**\n * Event fired by `wallpaperRegisterMediaStatusListener` when the user enables\n * or disables media integration in the Wallpaper Engine app settings.\n */\nexport interface WallpaperMediaStatusEvent {\n /** `true` when media integration is active; `false` when disabled. */\n enabled: boolean;\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaPropertiesListener` when the\n * currently playing track's metadata changes (title, artist, album, etc.).\n */\nexport interface WallpaperMediaPropertiesEvent {\n /** Title of the currently playing media. */\n title: string;\n /** Artist of the currently playing media. */\n artist: string;\n /** Optional sub-title (e.g. episode name for podcasts). */\n subTitle?: string;\n /** Optional album title. */\n albumTitle?: string;\n /** Optional album artist (may differ from `artist`). */\n albumArtist?: string;\n /** Optional comma-separated list of genres. */\n genres?: string;\n /** Media type: `\"music\"`, `\"video\"`, or `\"image\"`. */\n contentType: \"music\" | \"video\" | \"image\";\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaThumbnailListener` when album art changes.\n * Assign `event.thumbnail` directly to `img.src`.\n *\n * @example\n * window.wallpaperRegisterMediaThumbnailListener((e) => {\n * document.getElementById('cover').src = e.thumbnail;\n * document.body.style.background = e.primaryColor;\n * titleEl.style.color = e.textColor;\n * });\n */\nexport interface WallpaperMediaThumbnailEvent {\n /** Base64-encoded PNG of the album art — usable directly as `img.src`. */\n thumbnail: string;\n /** Dominant color extracted from the thumbnail. */\n primaryColor: string;\n /** Secondary color from the thumbnail palette. */\n secondaryColor: string;\n /** Tertiary color from the thumbnail palette. */\n tertiaryColor: string;\n /**\n * Text color guaranteed to contrast sufficiently against `primaryColor`.\n * May be `secondaryColor` or `tertiaryColor` when the contrast is sufficient.\n */\n textColor: string;\n /** Either black or white — whichever contrasts more against `primaryColor`. */\n highContrastColor: string;\n}\n\n/** Media is actively playing on the system. */\nexport const PLAYBACK_PLAYING = 0 as const;\n/** Media was playing but has been temporarily paused by the user. */\nexport const PLAYBACK_PAUSED = 1 as const;\n/** Media playback is completely stopped. */\nexport const PLAYBACK_STOPPED = 2 as const;\n\n/**\n * Numeric playback state for {@link WallpaperMediaPlaybackEvent}.\n * Compare against `window.wallpaperMediaIntegration.PLAYBACK_PLAYING` etc.\n * or the module-level constants {@link PLAYBACK_PLAYING}, {@link PLAYBACK_PAUSED},\n * {@link PLAYBACK_STOPPED}.\n */\nexport type WallpaperMediaPlaybackState =\n | typeof PLAYBACK_PLAYING\n | typeof PLAYBACK_PAUSED\n | typeof PLAYBACK_STOPPED;\n\n/**\n * Event fired by `wallpaperRegisterMediaPlaybackListener` when playback\n * starts, pauses, or stops.\n */\nexport interface WallpaperMediaPlaybackEvent {\n /**\n * Current playback state. Compare against the `PLAYBACK_*` constants:\n * - `PLAYBACK_PLAYING` (`0`) — actively playing\n * - `PLAYBACK_PAUSED` (`1`) — temporarily paused\n * - `PLAYBACK_STOPPED` (`2`) — playback stopped\n */\n state: WallpaperMediaPlaybackState;\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaTimelineListener` when track position\n * changes. **Not all media players support this** — ensure your wallpaper\n * works correctly even if this listener is never called.\n */\nexport interface WallpaperMediaTimelineEvent {\n /** Current playback position in seconds. */\n position: number;\n /** Total track duration in seconds. */\n duration: number;\n}\n\n// ---------------------------------------------------------------------------\n// Corsair iCUE SDK types (window.cue)\n// ---------------------------------------------------------------------------\n\n/** Returned by `window.cue.getProtocolDetails` */\nexport interface CueProtocolDetails {\n sdkVersion: string;\n serverVersion: string;\n sdkProtocolVersion: number;\n serverProtocolVersion: number;\n breakingChanges: boolean;\n}\n\n/** Returned by `window.cue.getDeviceInfo` — mirrors `CorsairDeviceInfo` in the C++ SDK */\nexport interface CueDeviceInfo {\n /** `CorsairDeviceType` enum value */\n type: number;\n /** Human-readable device model name */\n model: string;\n /** `CorsairPhysicalLayout` enum value */\n physicalLayout: number;\n /** `CorsairLogicalLayout` enum value */\n logicalLayout: number;\n /** Number of available LEDs on this device */\n ledCount: number;\n /** `CorsairDeviceCaps` bitmask */\n capsMask: number;\n}\n\n/** An entry in the array returned by `window.cue.getLedPositionsByDeviceIndex` */\nexport interface CueLedPosition {\n /** `CorsairLedId` as integer */\n ledId: number;\n /** `CorsairLedId` as string */\n ledIdName: string;\n /** Position in mm from the top of the device */\n top: number;\n /** Position in mm from the left of the device */\n left: number;\n width: number;\n height: number;\n}\n\n/** LED color entry used in `setLedsColorsAsync` / `setAllLedsColorsAsync` */\nexport interface CueLedColor {\n /** `CorsairLedId` as integer */\n ledId: number;\n r: number;\n g: number;\n b: number;\n}\n\n/**\n * Direct Corsair iCUE SDK access exposed as `window.cue`.\n * Only available after the `\"cue\"` plugin has loaded via `wallpaperPluginListener`.\n */\nexport interface WallpaperCuePlugin {\n /** Returns current iCUE SDK status and version info */\n getProtocolDetails(\n callback: (protocolDetails: CueProtocolDetails) => void,\n ): void;\n /** Returns the number of recognised iCUE-compatible devices */\n getDeviceCount(callback: (deviceCount: number) => void): void;\n /** Returns hardware details for a specific device by index */\n getDeviceInfo(\n deviceIndex: number,\n callback: (deviceInfo: CueDeviceInfo) => void,\n ): void;\n /** Returns all LED positions for a specific device */\n getLedPositionsByDeviceIndex(\n callback: (arrayOfLEDs: CueLedPosition[]) => void,\n ): void;\n /** Update specific LEDs by supplying an array of `CueLedColor` objects */\n setLedsColorsAsync(arrayOfLEDColors: CueLedColor[]): void;\n /** Set all LEDs on one or more devices to a single color */\n setAllLedsColorsAsync(\n deviceIndexOrArray: number | number[],\n LEDColor: CueLedColor,\n ): void;\n /**\n * Update device LEDs from an RGB bitmap string (same format as\n * `wpPlugins.led.setAllDevicesByImageData`, but targets specific devices).\n */\n setLedColorsByImageData(\n deviceIndexOrArray: number | number[],\n encodedImageData: string,\n width: number,\n height: number,\n ): void;\n}\n\n// ---------------------------------------------------------------------------\n// RGB / LED plugin\n// ---------------------------------------------------------------------------\n\nexport interface WallpaperLedPlugin {\n /**\n * Push concatenated RGB bytes (as a string) to all connected LED devices.\n * Use a small canvas (e.g. 100×20) and convert it with `getImageData` for\n * best performance.\n */\n setAllDevicesByImageData(\n encodedImageData: string,\n width: number,\n height: number,\n ): void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACyOO,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;","names":[]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/listeners.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\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":";AAyOO,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;","names":[]}
1
+ {"version":3,"sources":["../src/types/listeners.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Runtime property values (received inside applyUserProperties)\n// ---------------------------------------------------------------------------\n\n/**\n * Runtime value of a color property.\n * `value` is `\"R G B\"` where each channel is in the **0–1** range.\n * Multiply by 255 to convert for CSS `rgb()`.\n *\n * @example\n * const [r, g, b] = props.myColor.value.split(' ').map(c => Math.ceil(+c * 255));\n * el.style.color = `rgb(${r},${g},${b})`;\n */\nexport interface WallpaperColorValue {\n /** Color string `\"R G B\"` — each channel 0.0–1.0. */\n value: string;\n}\n\n/** Runtime value of a slider property. */\nexport interface WallpaperSliderValue {\n value: number;\n}\n\n/** Runtime value of a checkbox (`bool`) property. */\nexport interface WallpaperBoolValue {\n value: boolean;\n}\n\n/**\n * Runtime value of a combo (dropdown) property.\n * `value` is the hidden key configured in the editor;\n * `text` is the visible display label.\n */\nexport interface WallpaperComboValue {\n /** The hidden value configured on the combo option. */\n value: string;\n /** Display label of the selected option (may be a `ui_` localization token). */\n text: string;\n}\n\n/** Runtime value of a text-input property. */\nexport interface WallpaperTextValue {\n value: string;\n}\n\n/**\n * Runtime value of a file property.\n * Prepend `\"file:///\"` to `value` before using it as a URL.\n *\n * @example\n * img.src = 'file:///' + props.myImage.value;\n */\nexport interface WallpaperFileValue {\n /** File path — prepend `\"file:///\"` before use as a URL. */\n value: string;\n}\n\n/**\n * Runtime value of a directory property in `ondemand` mode.\n * An empty string means no directory is currently selected.\n * Call `wallpaperRequestRandomFileForProperty` to retrieve a random file.\n */\nexport interface WallpaperDirectoryValue {\n /** Directory path, or empty string when no directory is selected. */\n value: string;\n}\n\n/** Union of all possible runtime property values delivered by `applyUserProperties`. */\nexport type WallpaperPropertyRuntimeValue =\n | WallpaperColorValue\n | WallpaperSliderValue\n | WallpaperBoolValue\n | WallpaperComboValue\n | WallpaperTextValue\n | WallpaperFileValue\n | WallpaperDirectoryValue;\n\n/**\n * Map of property key → current runtime value passed to\n * `wallpaperPropertyListener.applyUserProperties`.\n * On first load all properties are present; on subsequent calls only the\n * changed ones are included — always guard with `if (properties.key)`.\n */\nexport type WallpaperUserProperties = Record<\n string,\n WallpaperPropertyRuntimeValue\n>;\n\n/** App-level settings delivered by `wallpaperPropertyListener.applyGeneralProperties`. */\nexport interface WallpaperGeneralProperties {\n /** Current FPS limit set by the user in Wallpaper Engine settings. `0` = unlimited. */\n fps?: number;\n}\n\n// ---------------------------------------------------------------------------\n// wallpaperPropertyListener\n// ---------------------------------------------------------------------------\n\n/**\n * Assign to `window.wallpaperPropertyListener` to receive property and\n * app-settings updates. **Must be a top-level global** — never assign inside\n * `window.onload` or other events, or startup events may be missed.\n *\n * @example\n * window.wallpaperPropertyListener = {\n * applyUserProperties(properties) {\n * if (properties.mycolor) { ... }\n * },\n * };\n */\nexport interface WallpaperPropertyListener {\n /**\n * Called once on wallpaper load (with all user properties) and again\n * whenever the user changes a property. Only changed properties are\n * included on subsequent calls — always guard with `if (properties.key)`.\n */\n applyUserProperties?: (properties: WallpaperUserProperties) => void;\n /**\n * Called on load and whenever the user changes app-level settings\n * such as the FPS limit. See {@link WallpaperGeneralProperties}.\n */\n applyGeneralProperties?: (properties: WallpaperGeneralProperties) => void;\n /**\n * Called for `directory` properties in `fetchall` mode when files are\n * added or modified in the selected directory. Prepend `\"file:///\"` to\n * each path before using it as a URL.\n */\n userDirectoryFilesAddedOrChanged?: (\n propertyName: string,\n changedFiles: string[],\n ) => void;\n /**\n * Called for `directory` properties in `fetchall` mode when files are\n * removed from the selected directory.\n */\n userDirectoryFilesRemoved?: (\n propertyName: string,\n removedFiles: string[],\n ) => void;\n}\n\n// ---------------------------------------------------------------------------\n// wallpaperPluginListener\n// ---------------------------------------------------------------------------\n\n/**\n * Assign to `window.wallpaperPluginListener` to detect when RGB plugins load.\n * Check `name` to distinguish `\"led\"` (all RGB hardware via `wpPlugins.led`)\n * from `\"cue\"` (Corsair iCUE direct SDK, only needed for advanced features).\n *\n * @example\n * window.wallpaperPluginListener = {\n * onPluginLoaded(name, version) {\n * if (name === 'led') { // general RGB hardware ready\n * }\n * if (name === 'cue') { // Corsair iCUE SDK ready\n * }\n * },\n * };\n */\nexport interface WallpaperPluginListener {\n /**\n * Fired when a plugin finishes loading.\n * @param name - `\"led\"` for general RGB hardware or `\"cue\"` for Corsair iCUE.\n * @param version - Plugin version string.\n */\n onPluginLoaded?: (name: string, version: string) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Media integration events\n// ---------------------------------------------------------------------------\n\n/**\n * Event fired by `wallpaperRegisterMediaStatusListener` when the user enables\n * or disables media integration in the Wallpaper Engine app settings.\n */\nexport interface WallpaperMediaStatusEvent {\n /** `true` when media integration is active; `false` when disabled. */\n enabled: boolean;\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaPropertiesListener` when the\n * currently playing track's metadata changes (title, artist, album, etc.).\n */\nexport interface WallpaperMediaPropertiesEvent {\n /** Title of the currently playing media. */\n title: string;\n /** Artist of the currently playing media. */\n artist: string;\n /** Optional sub-title (e.g. episode name for podcasts). */\n subTitle?: string;\n /** Optional album title. */\n albumTitle?: string;\n /** Optional album artist (may differ from `artist`). */\n albumArtist?: string;\n /** Optional comma-separated list of genres. */\n genres?: string;\n /** Media type: `\"music\"`, `\"video\"`, or `\"image\"`. */\n contentType: \"music\" | \"video\" | \"image\";\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaThumbnailListener` when album art changes.\n * Assign `event.thumbnail` directly to `img.src`.\n *\n * @example\n * window.wallpaperRegisterMediaThumbnailListener((e) => {\n * document.getElementById('cover').src = e.thumbnail;\n * document.body.style.background = e.primaryColor;\n * titleEl.style.color = e.textColor;\n * });\n */\nexport interface WallpaperMediaThumbnailEvent {\n /** Base64-encoded PNG of the album art — usable directly as `img.src`. */\n thumbnail: string;\n /** Dominant color extracted from the thumbnail. */\n primaryColor: string;\n /** Secondary color from the thumbnail palette. */\n secondaryColor: string;\n /** Tertiary color from the thumbnail palette. */\n tertiaryColor: string;\n /**\n * Text color guaranteed to contrast sufficiently against `primaryColor`.\n * May be `secondaryColor` or `tertiaryColor` when the contrast is sufficient.\n */\n textColor: string;\n /** Either black or white — whichever contrasts more against `primaryColor`. */\n highContrastColor: string;\n}\n\n/** Media is actively playing on the system. */\nexport const PLAYBACK_PLAYING = 0 as const;\n/** Media was playing but has been temporarily paused by the user. */\nexport const PLAYBACK_PAUSED = 1 as const;\n/** Media playback is completely stopped. */\nexport const PLAYBACK_STOPPED = 2 as const;\n\n/**\n * Numeric playback state for {@link WallpaperMediaPlaybackEvent}.\n * Compare against `window.wallpaperMediaIntegration.PLAYBACK_PLAYING` etc.\n * or the module-level constants {@link PLAYBACK_PLAYING}, {@link PLAYBACK_PAUSED},\n * {@link PLAYBACK_STOPPED}.\n */\nexport type WallpaperMediaPlaybackState =\n | typeof PLAYBACK_PLAYING\n | typeof PLAYBACK_PAUSED\n | typeof PLAYBACK_STOPPED;\n\n/**\n * Event fired by `wallpaperRegisterMediaPlaybackListener` when playback\n * starts, pauses, or stops.\n */\nexport interface WallpaperMediaPlaybackEvent {\n /**\n * Current playback state. Compare against the `PLAYBACK_*` constants:\n * - `PLAYBACK_PLAYING` (`0`) — actively playing\n * - `PLAYBACK_PAUSED` (`1`) — temporarily paused\n * - `PLAYBACK_STOPPED` (`2`) — playback stopped\n */\n state: WallpaperMediaPlaybackState;\n}\n\n/**\n * Event fired by `wallpaperRegisterMediaTimelineListener` when track position\n * changes. **Not all media players support this** — ensure your wallpaper\n * works correctly even if this listener is never called.\n */\nexport interface WallpaperMediaTimelineEvent {\n /** Current playback position in seconds. */\n position: number;\n /** Total track duration in seconds. */\n duration: number;\n}\n\n// ---------------------------------------------------------------------------\n// Corsair iCUE SDK types (window.cue)\n// ---------------------------------------------------------------------------\n\n/** Returned by `window.cue.getProtocolDetails` */\nexport interface CueProtocolDetails {\n sdkVersion: string;\n serverVersion: string;\n sdkProtocolVersion: number;\n serverProtocolVersion: number;\n breakingChanges: boolean;\n}\n\n/** Returned by `window.cue.getDeviceInfo` — mirrors `CorsairDeviceInfo` in the C++ SDK */\nexport interface CueDeviceInfo {\n /** `CorsairDeviceType` enum value */\n type: number;\n /** Human-readable device model name */\n model: string;\n /** `CorsairPhysicalLayout` enum value */\n physicalLayout: number;\n /** `CorsairLogicalLayout` enum value */\n logicalLayout: number;\n /** Number of available LEDs on this device */\n ledCount: number;\n /** `CorsairDeviceCaps` bitmask */\n capsMask: number;\n}\n\n/** An entry in the array returned by `window.cue.getLedPositionsByDeviceIndex` */\nexport interface CueLedPosition {\n /** `CorsairLedId` as integer */\n ledId: number;\n /** `CorsairLedId` as string */\n ledIdName: string;\n /** Position in mm from the top of the device */\n top: number;\n /** Position in mm from the left of the device */\n left: number;\n width: number;\n height: number;\n}\n\n/** LED color entry used in `setLedsColorsAsync` / `setAllLedsColorsAsync` */\nexport interface CueLedColor {\n /** `CorsairLedId` as integer */\n ledId: number;\n r: number;\n g: number;\n b: number;\n}\n\n/**\n * Direct Corsair iCUE SDK access exposed as `window.cue`.\n * Only available after the `\"cue\"` plugin has loaded via `wallpaperPluginListener`.\n */\nexport interface WallpaperCuePlugin {\n /** Returns current iCUE SDK status and version info */\n getProtocolDetails(\n callback: (protocolDetails: CueProtocolDetails) => void,\n ): void;\n /** Returns the number of recognised iCUE-compatible devices */\n getDeviceCount(callback: (deviceCount: number) => void): void;\n /** Returns hardware details for a specific device by index */\n getDeviceInfo(\n deviceIndex: number,\n callback: (deviceInfo: CueDeviceInfo) => void,\n ): void;\n /** Returns all LED positions for a specific device */\n getLedPositionsByDeviceIndex(\n callback: (arrayOfLEDs: CueLedPosition[]) => void,\n ): void;\n /** Update specific LEDs by supplying an array of `CueLedColor` objects */\n setLedsColorsAsync(arrayOfLEDColors: CueLedColor[]): void;\n /** Set all LEDs on one or more devices to a single color */\n setAllLedsColorsAsync(\n deviceIndexOrArray: number | number[],\n LEDColor: CueLedColor,\n ): void;\n /**\n * Update device LEDs from an RGB bitmap string (same format as\n * `wpPlugins.led.setAllDevicesByImageData`, but targets specific devices).\n */\n setLedColorsByImageData(\n deviceIndexOrArray: number | number[],\n encodedImageData: string,\n width: number,\n height: number,\n ): void;\n}\n\n// ---------------------------------------------------------------------------\n// RGB / LED plugin\n// ---------------------------------------------------------------------------\n\nexport interface WallpaperLedPlugin {\n /**\n * Push concatenated RGB bytes (as a string) to all connected LED devices.\n * Use a small canvas (e.g. 100×20) and convert it with `getImageData` for\n * best performance.\n */\n setAllDevicesByImageData(\n encodedImageData: string,\n width: number,\n height: number,\n ): void;\n}\n"],"mappings":";AAyOO,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugin/index.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\r\nimport type {\r\n WallpaperBoolValue,\r\n WallpaperColorValue,\r\n WallpaperComboValue,\r\n WallpaperDirectoryValue,\r\n WallpaperFileValue,\r\n WallpaperSliderValue,\r\n WallpaperTextValue,\r\n} from \"../types/listeners\";\r\nimport type {\r\n WallpaperBoolProperty,\r\n WallpaperColorProperty,\r\n WallpaperComboProperty,\r\n WallpaperDirectoryProperty,\r\n WallpaperFileProperty,\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\nexport type {\r\n WallpaperBoolProperty,\r\n WallpaperColorProperty,\r\n WallpaperComboProperty,\r\n WallpaperDirectoryProperty,\r\n WallpaperFileProperty,\r\n WallpaperLocalization,\r\n WallpaperProject,\r\n WallpaperProjectGeneral,\r\n WallpaperPropertyDefinition,\r\n WallpaperSliderProperty,\r\n WallpaperTextInputProperty,\r\n};\r\nexport type { WallpaperComboOption, WallpaperFileType } from \"../types/project\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// Property builder helpers\r\n// ---------------------------------------------------------------------------\r\n\r\ntype Without<T, K extends keyof T> = Omit<T, K>;\r\n\r\n/** Define a color property (value: `\"R G B\"` in 0–1 range) */\r\nexport function colorProperty(\r\n opts: Without<WallpaperColorProperty, \"type\">,\r\n): WallpaperColorProperty {\r\n return { type: \"color\", ...opts };\r\n}\r\n\r\n/** Define a numeric slider property */\r\nexport function sliderProperty(\r\n opts: Without<WallpaperSliderProperty, \"type\">,\r\n): WallpaperSliderProperty {\r\n return { type: \"slider\", ...opts };\r\n}\r\n\r\n/** Define a boolean checkbox property */\r\nexport function boolProperty(\r\n opts: Without<WallpaperBoolProperty, \"type\">,\r\n): WallpaperBoolProperty {\r\n return { type: \"bool\", ...opts };\r\n}\r\n\r\n/** Define a dropdown (combo) property */\r\nexport function comboProperty(\r\n opts: Without<WallpaperComboProperty, \"type\">,\r\n): WallpaperComboProperty {\r\n return { type: \"combo\", ...opts };\r\n}\r\n\r\n/** Define a text input property */\r\nexport function textInputProperty(\r\n opts: Without<WallpaperTextInputProperty, \"type\">,\r\n): WallpaperTextInputProperty {\r\n return { type: \"textinput\", ...opts };\r\n}\r\n\r\n/** Define a file picker property */\r\nexport function fileProperty(\r\n opts: Without<WallpaperFileProperty, \"type\">,\r\n): WallpaperFileProperty {\r\n return { type: \"file\", ...opts };\r\n}\r\n\r\n/** Define a directory picker property */\r\nexport function directoryProperty(\r\n opts: Without<WallpaperDirectoryProperty, \"type\">,\r\n): WallpaperDirectoryProperty {\r\n return { type: \"directory\", ...opts };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Property definition → runtime value type mapping\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Maps a single property **definition** type to its **runtime value** type.\r\n *\r\n * Useful for building generic helpers that operate on any property kind.\r\n *\r\n * @example\r\n * type T = PropertyDefinitionToValue<WallpaperColorProperty>;\r\n * // → WallpaperColorValue\r\n */\r\nexport type PropertyDefinitionToValue<T extends WallpaperPropertyDefinition> =\r\n T extends { type: \"color\" }\r\n ? WallpaperColorValue\r\n : T extends { type: \"slider\" }\r\n ? WallpaperSliderValue\r\n : T extends { type: \"bool\" }\r\n ? WallpaperBoolValue\r\n : T extends { type: \"combo\" }\r\n ? WallpaperComboValue\r\n : T extends { type: \"textinput\" }\r\n ? WallpaperTextValue\r\n : T extends { type: \"file\" }\r\n ? WallpaperFileValue\r\n : T extends { type: \"directory\" }\r\n ? WallpaperDirectoryValue\r\n : never;\r\n\r\n/**\r\n * Infer the strongly-typed `applyUserProperties` argument from a property\r\n * definition record. Define your properties once in a shared file, then pass\r\n * `typeof yourProperties` here to get full autocomplete on every property key\r\n * and its value type.\r\n *\r\n * @example\r\n * // properties.ts — shared between vite.config.ts and wallpaper source\r\n * import { colorProperty, sliderProperty } from 'wallpaper-engine/plugin';\r\n *\r\n * export const myProperties = {\r\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\r\n * speed: sliderProperty({ text: 'Speed', value: 1, min: 0, max: 5 }),\r\n * };\r\n *\r\n * // vite.config.ts\r\n * import { wallpaperEnginePlugin } from 'wallpaper-engine/plugin';\r\n * import { myProperties } from './properties';\r\n *\r\n * export default defineConfig({\r\n * plugins: [wallpaperEnginePlugin({ title: 'My Wallpaper', properties: myProperties })],\r\n * });\r\n *\r\n * // wallpaper.ts\r\n * import type { WallpaperUserPropertiesOf } from 'wallpaper-engine/plugin';\r\n * import type { myProperties } from './properties';\r\n *\r\n * type MyProps = WallpaperUserPropertiesOf<typeof myProperties>;\r\n * // → { bgcolor: WallpaperColorValue; speed: WallpaperSliderValue }\r\n *\r\n * window.wallpaperPropertyListener = {\r\n * applyUserProperties(props: Partial<MyProps>) {\r\n * if (props.bgcolor) el.style.background = wallpaperColorToRgb(props.bgcolor.value);\r\n * if (props.speed !== undefined) setSpeed(props.speed.value); // number ✓\r\n * },\r\n * };\r\n */\r\nexport type WallpaperUserPropertiesOf<\r\n T extends Record<string, WallpaperPropertyDefinition>,\r\n> = {\r\n readonly [K in keyof T]: PropertyDefinitionToValue<T[K]>;\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// Vite plugin\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface WallpaperEnginePluginOptions {\r\n /**\r\n * Entry HTML file name relative to the project root.\r\n * @default \"index.html\"\r\n */\r\n file?: string;\r\n /** Wallpaper title shown in the Wallpaper Engine UI */\r\n title: string;\r\n /**\r\n * Enable audio data delivery to `wallpaperRegisterAudioListener`.\r\n * Wallpaper Engine can auto-detect this, but you can set it explicitly.\r\n * @default false\r\n */\r\n supportsAudioProcessing?: boolean;\r\n /**\r\n * User-configurable properties exposed in the Wallpaper Engine properties panel.\r\n * Keys become the property identifiers accessed in `applyUserProperties`.\r\n *\r\n * @example\r\n * properties: {\r\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\r\n * speed: sliderProperty({ text: 'Speed', value: 1, min: 0, max: 5 }),\r\n * }\r\n */\r\n properties?: Record<string, WallpaperPropertyDefinition>;\r\n /**\r\n * Localization strings for property labels and combo option labels.\r\n * Keys are BCP 47 language codes (e.g. `\"en-us\"`, `\"de-de\"`, `\"zh-chs\"`).\r\n * Property labels that should be translated must start with `ui_`.\r\n *\r\n * @example\r\n * localization: {\r\n * 'en-us': { 'ui_bgcolor': 'Background Color' },\r\n * 'de-de': { 'ui_bgcolor': 'Hintergrundfarbe' },\r\n * }\r\n */\r\n localization?: WallpaperLocalization;\r\n /**\r\n * Enable the in-browser dev overlay during `vite dev`. The overlay stubs\r\n * every `window.wallpaper*` global, renders a draggable panel to edit each\r\n * property in real time, and lets you fire audio/media/plugin events\r\n * manually — so wallpapers can be developed without round-tripping through\r\n * the Wallpaper Engine host application.\r\n *\r\n * Disabled automatically for production builds regardless of this setting.\r\n *\r\n * @default true\r\n */\r\n devtools?: boolean;\r\n}\r\n\r\n/**\r\n * Vite plugin that auto-generates `project.json` into the build output.\r\n *\r\n * @example\r\n * // vite.config.ts\r\n * import { wallpaperEnginePlugin, colorProperty } from 'wallpaper-engine/plugin';\r\n *\r\n * export default defineConfig({\r\n * plugins: [\r\n * wallpaperEnginePlugin({\r\n * title: 'My Wallpaper',\r\n * properties: {\r\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\r\n * },\r\n * }),\r\n * ],\r\n * });\r\n */\r\nexport function wallpaperEnginePlugin(\r\n options: WallpaperEnginePluginOptions,\r\n): Plugin {\r\n const devtoolsEnabled = options.devtools !== false;\r\n const VIRTUAL_ID = \"virtual:wallpaper-engine/devtools\";\r\n const RESOLVED_ID = \"\\0\" + VIRTUAL_ID;\r\n let isServe = false;\r\n let cachedClientCode: string | undefined;\r\n const loadClientCode = async (): Promise<string> => {\r\n if (cachedClientCode !== undefined) return cachedClientCode;\r\n // Reads the Vue UI bundle emitted by `vite build -c vite.devtools.config.ts`\r\n // into `dist/plugin/devtools/client.js`. Imports are dynamic so this file\r\n // stays browser-safe — property builders below are imported by user code.\r\n const { readFileSync } = await import(\"node:fs\");\r\n const { fileURLToPath } = await import(\"node:url\");\r\n const url = new URL(\"./devtools/client.js\", import.meta.url);\r\n cachedClientCode = readFileSync(fileURLToPath(url), \"utf8\");\r\n return cachedClientCode;\r\n };\r\n\r\n return {\r\n name: \"wallpaper-engine\",\r\n\r\n configResolved(config) {\r\n isServe = config.command === \"serve\";\r\n },\r\n\r\n configureServer(server) {\r\n if (!devtoolsEnabled) return;\r\n void Promise.all([import(\"node:fs\"), import(\"node:url\")]).then(\r\n ([fs, { fileURLToPath }]) => {\r\n const clientPath = fileURLToPath(\r\n new URL(\"./devtools/client.js\", import.meta.url),\r\n );\r\n // Use stat-polling watchFile instead of chokidar: avoids Windows\r\n // path-normalisation mismatches (forward vs back slashes) and works\r\n // correctly across Bun workspace symlinks.\r\n fs.watchFile(clientPath, { interval: 500 }, () => {\r\n cachedClientCode = undefined;\r\n const mod = server.moduleGraph.getModuleById(RESOLVED_ID);\r\n if (mod) server.moduleGraph.invalidateModule(mod);\r\n server.ws.send({ type: \"full-reload\" });\r\n });\r\n server.httpServer?.once(\"close\", () => {\r\n fs.unwatchFile(clientPath);\r\n });\r\n },\r\n );\r\n },\r\n\r\n resolveId(id) {\r\n if (id === VIRTUAL_ID) return RESOLVED_ID;\r\n return null;\r\n },\r\n\r\n async load(id) {\r\n if (id !== RESOLVED_ID) return null;\r\n const properties = options.properties\r\n ? assignIndices(options.properties)\r\n : {};\r\n const cfg = {\r\n title: options.title,\r\n properties,\r\n localization: options.localization ?? {},\r\n };\r\n return (\r\n \"window.__WE_DEVTOOLS_CONFIG__ = \" +\r\n JSON.stringify(cfg) +\r\n \";\\n\" +\r\n (await loadClientCode())\r\n );\r\n },\r\n\r\n transformIndexHtml() {\r\n if (!isServe || !devtoolsEnabled) return;\r\n return [\r\n {\r\n tag: \"script\",\r\n attrs: { type: \"module\", src: \"/@id/\" + VIRTUAL_ID },\r\n injectTo: \"head-prepend\",\r\n },\r\n ];\r\n },\r\n\r\n generateBundle() {\r\n const properties = options.properties\r\n ? assignIndices(options.properties)\r\n : undefined;\r\n\r\n const general: WallpaperProjectGeneral = {};\r\n if (properties) general.properties = properties;\r\n if (options.localization) general.localization = options.localization;\r\n\r\n const project: WallpaperProject = {\r\n file: options.file ?? \"index.html\",\r\n title: options.title,\r\n type: \"web\",\r\n };\r\n\r\n if (options.supportsAudioProcessing) {\r\n project.supportsaudioprocessing = true;\r\n }\r\n\r\n if (Object.keys(general).length > 0) {\r\n project.general = general;\r\n }\r\n\r\n this.emitFile({\r\n type: \"asset\",\r\n fileName: \"project.json\",\r\n source: JSON.stringify(project, null, \"\\t\"),\r\n });\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Auto-assigns `index` and `order` to any properties that don't already have\r\n * them, based on their insertion order in the record.\r\n */\r\nfunction assignIndices(\r\n properties: Record<string, WallpaperPropertyDefinition>,\r\n): Record<string, WallpaperPropertyDefinition> {\r\n const result: Record<string, WallpaperPropertyDefinition> = {};\r\n let i = 0;\r\n\r\n for (const [key, prop] of Object.entries(properties)) {\r\n result[key] = {\r\n index: i,\r\n order: i,\r\n ...prop,\r\n };\r\n i++;\r\n }\r\n\r\n return result;\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/plugin/index.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport type {\n WallpaperBoolValue,\n WallpaperColorValue,\n WallpaperComboValue,\n WallpaperDirectoryValue,\n WallpaperFileValue,\n WallpaperSliderValue,\n WallpaperTextValue,\n} from \"../types/listeners\";\nimport type {\n WallpaperBoolProperty,\n WallpaperColorProperty,\n WallpaperComboProperty,\n WallpaperDirectoryProperty,\n WallpaperFileProperty,\n WallpaperLocalization,\n WallpaperProject,\n WallpaperProjectGeneral,\n WallpaperPropertyDefinition,\n WallpaperSliderProperty,\n WallpaperTextInputProperty,\n} from \"../types/project\";\n\nexport type {\n WallpaperBoolProperty,\n WallpaperColorProperty,\n WallpaperComboProperty,\n WallpaperDirectoryProperty,\n WallpaperFileProperty,\n WallpaperLocalization,\n WallpaperProject,\n WallpaperProjectGeneral,\n WallpaperPropertyDefinition,\n WallpaperSliderProperty,\n WallpaperTextInputProperty,\n};\nexport type { WallpaperComboOption, WallpaperFileType } from \"../types/project\";\n\n// ---------------------------------------------------------------------------\n// Property builder helpers\n// ---------------------------------------------------------------------------\n\ntype Without<T, K extends keyof T> = Omit<T, K>;\n\n/** Define a color property (value: `\"R G B\"` in 0–1 range) */\nexport function colorProperty(\n opts: Without<WallpaperColorProperty, \"type\">,\n): WallpaperColorProperty {\n return { type: \"color\", ...opts };\n}\n\n/** Define a numeric slider property */\nexport function sliderProperty(\n opts: Without<WallpaperSliderProperty, \"type\">,\n): WallpaperSliderProperty {\n return { type: \"slider\", ...opts };\n}\n\n/** Define a boolean checkbox property */\nexport function boolProperty(\n opts: Without<WallpaperBoolProperty, \"type\">,\n): WallpaperBoolProperty {\n return { type: \"bool\", ...opts };\n}\n\n/** Define a dropdown (combo) property */\nexport function comboProperty(\n opts: Without<WallpaperComboProperty, \"type\">,\n): WallpaperComboProperty {\n return { type: \"combo\", ...opts };\n}\n\n/** Define a text input property */\nexport function textInputProperty(\n opts: Without<WallpaperTextInputProperty, \"type\">,\n): WallpaperTextInputProperty {\n return { type: \"textinput\", ...opts };\n}\n\n/** Define a file picker property */\nexport function fileProperty(\n opts: Without<WallpaperFileProperty, \"type\">,\n): WallpaperFileProperty {\n return { type: \"file\", ...opts };\n}\n\n/** Define a directory picker property */\nexport function directoryProperty(\n opts: Without<WallpaperDirectoryProperty, \"type\">,\n): WallpaperDirectoryProperty {\n return { type: \"directory\", ...opts };\n}\n\n// ---------------------------------------------------------------------------\n// Property definition → runtime value type mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a single property **definition** type to its **runtime value** type.\n *\n * Useful for building generic helpers that operate on any property kind.\n *\n * @example\n * type T = PropertyDefinitionToValue<WallpaperColorProperty>;\n * // → WallpaperColorValue\n */\nexport type PropertyDefinitionToValue<T extends WallpaperPropertyDefinition> =\n T extends { type: \"color\" }\n ? WallpaperColorValue\n : T extends { type: \"slider\" }\n ? WallpaperSliderValue\n : T extends { type: \"bool\" }\n ? WallpaperBoolValue\n : T extends { type: \"combo\" }\n ? WallpaperComboValue\n : T extends { type: \"textinput\" }\n ? WallpaperTextValue\n : T extends { type: \"file\" }\n ? WallpaperFileValue\n : T extends { type: \"directory\" }\n ? WallpaperDirectoryValue\n : never;\n\n/**\n * Infer the strongly-typed `applyUserProperties` argument from a property\n * definition record. Define your properties once in a shared file, then pass\n * `typeof yourProperties` here to get full autocomplete on every property key\n * and its value type.\n *\n * @example\n * // properties.ts — shared between vite.config.ts and wallpaper source\n * import { colorProperty, sliderProperty } from 'wallpaper-engine/plugin';\n *\n * export const myProperties = {\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\n * speed: sliderProperty({ text: 'Speed', value: 1, min: 0, max: 5 }),\n * };\n *\n * // vite.config.ts\n * import { wallpaperEnginePlugin } from 'wallpaper-engine/plugin';\n * import { myProperties } from './properties';\n *\n * export default defineConfig({\n * plugins: [wallpaperEnginePlugin({ title: 'My Wallpaper', properties: myProperties })],\n * });\n *\n * // wallpaper.ts\n * import type { WallpaperUserPropertiesOf } from 'wallpaper-engine/plugin';\n * import type { myProperties } from './properties';\n *\n * type MyProps = WallpaperUserPropertiesOf<typeof myProperties>;\n * // → { bgcolor: WallpaperColorValue; speed: WallpaperSliderValue }\n *\n * window.wallpaperPropertyListener = {\n * applyUserProperties(props: Partial<MyProps>) {\n * if (props.bgcolor) el.style.background = wallpaperColorToRgb(props.bgcolor.value);\n * if (props.speed !== undefined) setSpeed(props.speed.value); // number ✓\n * },\n * };\n */\nexport type WallpaperUserPropertiesOf<\n T extends Record<string, WallpaperPropertyDefinition>,\n> = {\n readonly [K in keyof T]: PropertyDefinitionToValue<T[K]>;\n};\n\n// ---------------------------------------------------------------------------\n// Vite plugin\n// ---------------------------------------------------------------------------\n\nexport interface WallpaperEnginePluginOptions {\n /**\n * Entry HTML file name relative to the project root.\n * @default \"index.html\"\n */\n file?: string;\n /** Wallpaper title shown in the Wallpaper Engine UI */\n title: string;\n /**\n * Enable audio data delivery to `wallpaperRegisterAudioListener`.\n * Wallpaper Engine can auto-detect this, but you can set it explicitly.\n * @default false\n */\n supportsAudioProcessing?: boolean;\n /**\n * User-configurable properties exposed in the Wallpaper Engine properties panel.\n * Keys become the property identifiers accessed in `applyUserProperties`.\n *\n * @example\n * properties: {\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\n * speed: sliderProperty({ text: 'Speed', value: 1, min: 0, max: 5 }),\n * }\n */\n properties?: Record<string, WallpaperPropertyDefinition>;\n /**\n * Localization strings for property labels and combo option labels.\n * Keys are BCP 47 language codes (e.g. `\"en-us\"`, `\"de-de\"`, `\"zh-chs\"`).\n * Property labels that should be translated must start with `ui_`.\n *\n * @example\n * localization: {\n * 'en-us': { 'ui_bgcolor': 'Background Color' },\n * 'de-de': { 'ui_bgcolor': 'Hintergrundfarbe' },\n * }\n */\n localization?: WallpaperLocalization;\n /**\n * Enable the in-browser dev overlay during `vite dev`. The overlay stubs\n * every `window.wallpaper*` global, renders a draggable panel to edit each\n * property in real time, and lets you fire audio/media/plugin events\n * manually — so wallpapers can be developed without round-tripping through\n * the Wallpaper Engine host application.\n *\n * Disabled automatically for production builds regardless of this setting.\n *\n * @default true\n */\n devtools?: boolean;\n}\n\n/**\n * Vite plugin that auto-generates `project.json` into the build output.\n *\n * @example\n * // vite.config.ts\n * import { wallpaperEnginePlugin, colorProperty } from 'wallpaper-engine/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * wallpaperEnginePlugin({\n * title: 'My Wallpaper',\n * properties: {\n * bgcolor: colorProperty({ text: 'Background Color', value: '0 0 0' }),\n * },\n * }),\n * ],\n * });\n */\nexport function wallpaperEnginePlugin(\n options: WallpaperEnginePluginOptions,\n): Plugin {\n const devtoolsEnabled = options.devtools !== false;\n const VIRTUAL_ID = \"virtual:wallpaper-engine/devtools\";\n const RESOLVED_ID = \"\\0\" + VIRTUAL_ID;\n let isServe = false;\n let cachedClientCode: string | undefined;\n const loadClientCode = async (): Promise<string> => {\n if (cachedClientCode !== undefined) return cachedClientCode;\n // Reads the Vue UI bundle emitted by `vite build -c vite.devtools.config.ts`\n // into `dist/plugin/devtools/client.js`. Imports are dynamic so this file\n // stays browser-safe — property builders below are imported by user code.\n const { readFileSync } = await import(\"node:fs\");\n const { fileURLToPath } = await import(\"node:url\");\n const url = new URL(\"./devtools/client.js\", import.meta.url);\n cachedClientCode = readFileSync(fileURLToPath(url), \"utf8\");\n return cachedClientCode;\n };\n\n return {\n name: \"wallpaper-engine\",\n\n configResolved(config) {\n isServe = config.command === \"serve\";\n },\n\n configureServer(server) {\n if (!devtoolsEnabled) return;\n void Promise.all([import(\"node:fs\"), import(\"node:url\")]).then(\n ([fs, { fileURLToPath }]) => {\n const clientPath = fileURLToPath(\n new URL(\"./devtools/client.js\", import.meta.url),\n );\n // Use stat-polling watchFile instead of chokidar: avoids Windows\n // path-normalisation mismatches (forward vs back slashes) and works\n // correctly across Bun workspace symlinks.\n fs.watchFile(clientPath, { interval: 500 }, () => {\n cachedClientCode = undefined;\n const mod = server.moduleGraph.getModuleById(RESOLVED_ID);\n if (mod) server.moduleGraph.invalidateModule(mod);\n server.ws.send({ type: \"full-reload\" });\n });\n server.httpServer?.once(\"close\", () => {\n fs.unwatchFile(clientPath);\n });\n },\n );\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_ID;\n return null;\n },\n\n async load(id) {\n if (id !== RESOLVED_ID) return null;\n const properties = options.properties\n ? assignIndices(options.properties)\n : {};\n const cfg = {\n title: options.title,\n properties,\n localization: options.localization ?? {},\n };\n return (\n \"window.__WE_DEVTOOLS_CONFIG__ = \" +\n JSON.stringify(cfg) +\n \";\\n\" +\n (await loadClientCode())\n );\n },\n\n transformIndexHtml() {\n if (!isServe || !devtoolsEnabled) return;\n return [\n {\n tag: \"script\",\n attrs: { type: \"module\", src: \"/@id/\" + VIRTUAL_ID },\n injectTo: \"head-prepend\",\n },\n ];\n },\n\n generateBundle() {\n const properties = options.properties\n ? assignIndices(options.properties)\n : undefined;\n\n const general: WallpaperProjectGeneral = {};\n if (properties) general.properties = properties;\n if (options.localization) general.localization = options.localization;\n\n const project: WallpaperProject = {\n file: options.file ?? \"index.html\",\n title: options.title,\n type: \"web\",\n };\n\n if (options.supportsAudioProcessing) {\n project.supportsaudioprocessing = true;\n }\n\n if (Object.keys(general).length > 0) {\n project.general = general;\n }\n\n this.emitFile({\n type: \"asset\",\n fileName: \"project.json\",\n source: JSON.stringify(project, null, \"\\t\"),\n });\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Auto-assigns `index` and `order` to any properties that don't already have\n * them, based on their insertion order in the record.\n */\nfunction assignIndices(\n properties: Record<string, WallpaperPropertyDefinition>,\n): Record<string, WallpaperPropertyDefinition> {\n const result: Record<string, WallpaperPropertyDefinition> = {};\n let i = 0;\n\n for (const [key, prop] of Object.entries(properties)) {\n result[key] = {\n index: i,\n order: i,\n ...prop,\n };\n i++;\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,70 +1,70 @@
1
- {
2
- "name": "wallpaper-engine",
3
- "version": "1.0.0",
4
- "description": "TypeScript types, Vite plugin, and runtime helpers for Wallpaper Engine web wallpapers",
5
- "keywords": [
6
- "wallpaper-engine",
7
- "wallpaper engine",
8
- "web wallpaper",
9
- "vite plugin",
10
- "typescript"
11
- ],
12
- "license": "MIT",
13
- "author": "ShadowNineX",
14
- "homepage": "https://github.com/ShadowNineX/wallpaper-engine#readme",
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/ShadowNineX/wallpaper-engine.git"
18
- },
19
- "bugs": {
20
- "url": "https://github.com/ShadowNineX/wallpaper-engine/issues"
21
- },
22
- "type": "module",
23
- "main": "./dist/index.cjs",
24
- "module": "./dist/index.js",
25
- "types": "./dist/index.d.ts",
26
- "exports": {
27
- ".": {
28
- "types": "./dist/index.d.ts",
29
- "import": "./dist/index.js",
30
- "require": "./dist/index.cjs"
31
- },
32
- "./plugin": {
33
- "types": "./dist/plugin/index.d.ts",
34
- "import": "./dist/plugin/index.js"
35
- },
36
- "./helpers": {
37
- "types": "./dist/helpers.d.ts",
38
- "import": "./dist/helpers.js",
39
- "require": "./dist/helpers.cjs"
40
- }
41
- },
42
- "files": [
43
- "dist",
44
- "README.md",
45
- "LICENSE"
46
- ],
47
- "scripts": {
48
- "build": "tsup",
49
- "build:lib": "tsup",
50
- "dev": "tsup --watch",
51
- "typecheck": "tsc --noEmit",
52
- "test": "vitest",
53
- "test:run": "vitest run",
54
- "prepublishOnly": "bun run --cwd ../.. build"
55
- },
56
- "peerDependencies": {
57
- "vite": ">=5.0.0"
58
- },
59
- "peerDependenciesMeta": {
60
- "vite": {
61
- "optional": true
62
- }
63
- },
64
- "devDependencies": {
65
- "@vitest/coverage-v8": "^4.1.6",
66
- "tsup": "^8.5.1",
67
- "vite": "^8.0.13",
68
- "vitest": "^4.1.6"
69
- }
70
- }
1
+ {
2
+ "name": "wallpaper-engine",
3
+ "version": "1.0.1",
4
+ "description": "TypeScript types, Vite plugin, and runtime helpers for Wallpaper Engine web wallpapers",
5
+ "keywords": [
6
+ "wallpaper-engine",
7
+ "wallpaper engine",
8
+ "web wallpaper",
9
+ "vite plugin",
10
+ "typescript"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "ShadowNineX",
14
+ "homepage": "https://github.com/ShadowNineX/wallpaper-engine#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/ShadowNineX/wallpaper-engine.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/ShadowNineX/wallpaper-engine/issues"
21
+ },
22
+ "type": "module",
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ },
32
+ "./plugin": {
33
+ "types": "./dist/plugin/index.d.ts",
34
+ "import": "./dist/plugin/index.js"
35
+ },
36
+ "./helpers": {
37
+ "types": "./dist/helpers.d.ts",
38
+ "import": "./dist/helpers.js",
39
+ "require": "./dist/helpers.cjs"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "build:lib": "tsup",
50
+ "dev": "tsup --watch",
51
+ "typecheck": "tsc --noEmit",
52
+ "test": "vitest",
53
+ "test:run": "vitest run",
54
+ "prepublishOnly": "bun run --cwd ../.. build"
55
+ },
56
+ "peerDependencies": {
57
+ "vite": ">=5.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "vite": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "devDependencies": {
65
+ "@vitest/coverage-v8": "^4.1.6",
66
+ "tsup": "^8.5.1",
67
+ "vite": "^8.0.13",
68
+ "vitest": "^4.1.6"
69
+ }
70
+ }