hdrify-react 1.3.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +57 -29
  2. package/dist/HdrifyCanvas.d.ts +31 -4
  3. package/dist/HdrifyCanvas.d.ts.map +1 -1
  4. package/dist/HdrifyCanvas.js +197 -38
  5. package/dist/HdrifyCanvas.js.map +1 -1
  6. package/dist/ImageViewer.d.ts +86 -0
  7. package/dist/ImageViewer.d.ts.map +1 -0
  8. package/dist/ImageViewer.js +241 -0
  9. package/dist/ImageViewer.js.map +1 -0
  10. package/dist/capture.d.ts +22 -0
  11. package/dist/capture.d.ts.map +1 -0
  12. package/dist/capture.js +62 -0
  13. package/dist/capture.js.map +1 -0
  14. package/dist/decode.d.ts +7 -0
  15. package/dist/decode.d.ts.map +1 -0
  16. package/dist/decode.js +25 -0
  17. package/dist/decode.js.map +1 -0
  18. package/dist/display.d.ts +17 -0
  19. package/dist/display.d.ts.map +1 -0
  20. package/dist/display.js +37 -0
  21. package/dist/display.js.map +1 -0
  22. package/dist/element.d.ts +51 -0
  23. package/dist/element.d.ts.map +1 -0
  24. package/dist/element.js +215 -0
  25. package/dist/element.js.map +1 -0
  26. package/dist/image-viewer.js +174 -0
  27. package/dist/image-viewer.js.map +7 -0
  28. package/dist/index.d.ts +7 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/progress.d.ts +19 -0
  33. package/dist/progress.d.ts.map +1 -0
  34. package/dist/progress.js +47 -0
  35. package/dist/progress.js.map +1 -0
  36. package/dist/readiness.d.ts +15 -0
  37. package/dist/readiness.d.ts.map +1 -0
  38. package/dist/readiness.js +46 -0
  39. package/dist/readiness.js.map +1 -0
  40. package/dist/styles.d.ts +6 -0
  41. package/dist/styles.d.ts.map +1 -0
  42. package/dist/styles.js +84 -0
  43. package/dist/styles.js.map +1 -0
  44. package/dist/webgpuDisplay.d.ts +4 -0
  45. package/dist/webgpuDisplay.d.ts.map +1 -0
  46. package/dist/webgpuDisplay.js +208 -0
  47. package/dist/webgpuDisplay.js.map +1 -0
  48. package/package.json +42 -13
package/dist/index.d.ts CHANGED
@@ -1,3 +1,9 @@
1
- export type { ToneMappingType } from 'hdrify';
1
+ export type { HdrifyImage, ToneMappingType } from 'hdrify';
2
+ export { captureImage, type ImageViewerCaptureOptions } from './capture.js';
3
+ export { decodeHdrifyImage } from './decode.js';
2
4
  export { HdrifyCanvas, type HdrifyCanvasProps } from './HdrifyCanvas.js';
5
+ export { DEFAULT_IMAGE_VIEWER_SETTINGS, EXPOSURE_SLIDER_RANGE, type ImageFit, ImageViewer, type ImageViewerHandle, type ImageViewerProps, type ImageViewerSettings, type SettingsPanelMode, TONE_MAPPING_OPTIONS, } from './ImageViewer.js';
6
+ export type { DownloadProgress, ImageViewerProgress } from './progress.js';
7
+ export { IMAGE_VIEWER_CSS } from './styles.js';
8
+ export type { DisplayMode, ViewerToneMapping } from './display.js';
3
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,KAAK,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,KAAK,QAAQ,EACb,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,6 @@
1
+ export { captureImage } from './capture.js';
2
+ export { decodeHdrifyImage } from './decode.js';
1
3
  export { HdrifyCanvas } from './HdrifyCanvas.js';
4
+ export { DEFAULT_IMAGE_VIEWER_SETTINGS, EXPOSURE_SLIDER_RANGE, ImageViewer, TONE_MAPPING_OPTIONS, } from './ImageViewer.js';
5
+ export { IMAGE_VIEWER_CSS } from './styles.js';
2
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAkC,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EAErB,WAAW,EAKX,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** Download progress for the image file. */
2
+ export interface DownloadProgress {
3
+ /** Bytes received so far. */
4
+ loaded: number;
5
+ /** File size in bytes, or null when the server does not provide a usable Content-Length. */
6
+ total: number | null;
7
+ /** Fraction from 0 to 1, or null while the file size is unknown. */
8
+ progress: number | null;
9
+ }
10
+ /** Overall loading progress reported by {@link ImageViewer}'s `onProgress` and `<image-viewer>`'s `progress` event. */
11
+ export interface ImageViewerProgress extends DownloadProgress {
12
+ /** Fraction from 0 to 1 across downloading, decoding and drawing. Never moves backwards within a load. */
13
+ totalProgress: number;
14
+ /** The current step: `download`, then `decode`, then `render` (reported once, with `totalProgress` 1). */
15
+ stage: 'download' | 'decode' | 'render';
16
+ }
17
+ /** Read a response body, reporting immediately, then at most every 100 ms, and on completion. */
18
+ export declare function readResponse(response: Response, signal: AbortSignal, onProgress: (progress: DownloadProgress) => void): Promise<Uint8Array>;
19
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,uHAAuH;AACvH,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,0GAA0G;IAC1G,aAAa,EAAE,MAAM,CAAC;IACtB,0GAA0G;IAC1G,KAAK,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACzC;AAED,iGAAiG;AACjG,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,GAC/C,OAAO,CAAC,UAAU,CAAC,CA0CrB"}
@@ -0,0 +1,47 @@
1
+ /** Read a response body, reporting immediately, then at most every 100 ms, and on completion. */
2
+ export async function readResponse(response, signal, onProgress) {
3
+ const length = Number(response.headers.get('content-length') ?? NaN);
4
+ // Fetch decodes compressed responses; their Content-Length describes different bytes.
5
+ const encoding = response.headers.get('content-encoding');
6
+ const total = Number.isSafeInteger(length) && length >= 0 && (!encoding || encoding === 'identity') ? length : null;
7
+ let loaded = 0;
8
+ const publish = () => onProgress({ loaded, total, progress: total ? Math.min(loaded / total, 1) : null });
9
+ publish();
10
+ const reader = response.body?.getReader();
11
+ if (!reader) {
12
+ const bytes = new Uint8Array(await response.arrayBuffer());
13
+ loaded = bytes.byteLength;
14
+ onProgress({ loaded, total: loaded, progress: 1 });
15
+ return bytes;
16
+ }
17
+ const cancel = () => void reader.cancel().catch(() => { });
18
+ signal.addEventListener('abort', cancel, { once: true });
19
+ try {
20
+ const chunks = [];
21
+ let published = performance.now();
22
+ while (true) {
23
+ const { done, value } = await reader.read();
24
+ signal.throwIfAborted();
25
+ if (done)
26
+ break;
27
+ chunks.push(value);
28
+ loaded += value.byteLength;
29
+ if (performance.now() - published >= 100) {
30
+ published = performance.now();
31
+ publish();
32
+ }
33
+ }
34
+ const bytes = new Uint8Array(loaded);
35
+ let offset = 0;
36
+ for (const chunk of chunks) {
37
+ bytes.set(chunk, offset);
38
+ offset += chunk.byteLength;
39
+ }
40
+ onProgress({ loaded, total: loaded, progress: 1 });
41
+ return bytes;
42
+ }
43
+ finally {
44
+ signal.removeEventListener('abort', cancel);
45
+ }
46
+ }
47
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sources":["../src/progress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Readiness shared by `<image-viewer>` and the React `ImageViewer` (the ies-tools `<light-viewer>` design). The host
3
+ * marks each load with {@link pending} and {@link finish}, and teardown with {@link disconnect}.
4
+ */
5
+ export declare class ViewerReadiness {
6
+ #private;
7
+ /** Resolve once the latest load has settled, following any newer load that starts while waiting. */
8
+ whenReady(): Promise<void>;
9
+ /** Start waiting for a load. A superseded load resolves its promise so waiters move on to the new one. */
10
+ pending(): void;
11
+ /** Settle the current load: resolve, or reject with `error`. Without a pending load, mark the viewer ready. */
12
+ finish(error?: unknown): void;
13
+ disconnect(): void;
14
+ }
15
+ //# sourceMappingURL=readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.d.ts","sources":["../src/readiness.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qBAAa,eAAe;;IAK1B,oGAAoG;IAC9F,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAM/B;IACD,0GAA0G;IAC1G,OAAO,IAAI,IAAI,CAQd;IACD,+GAA+G;IAC/G,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAI5B;IACD,UAAU,IAAI,IAAI,CAIjB;CACF"}
@@ -0,0 +1,46 @@
1
+ const notConnected = () => settled(Promise.reject(new Error('The viewer is not connected to a document')));
2
+ /**
3
+ * Readiness shared by `<image-viewer>` and the React `ImageViewer` (the ies-tools `<light-viewer>` design). The host
4
+ * marks each load with {@link pending} and {@link finish}, and teardown with {@link disconnect}.
5
+ */
6
+ export class ViewerReadiness {
7
+ /** Settles when the latest load settles; replaced by every load, so a superseded load never settles a waiter. */
8
+ #ready = notConnected();
9
+ #settle;
10
+ /** Resolve once the latest load has settled, following any newer load that starts while waiting. */
11
+ async whenReady() {
12
+ let ready;
13
+ do {
14
+ ready = this.#ready;
15
+ await ready;
16
+ } while (ready !== this.#ready);
17
+ }
18
+ /** Start waiting for a load. A superseded load resolves its promise so waiters move on to the new one. */
19
+ pending() {
20
+ let settle;
21
+ const next = new Promise((resolve, reject) => {
22
+ settle = (error) => (error === undefined ? resolve() : reject(error));
23
+ });
24
+ this.#settle?.();
25
+ this.#settle = settle;
26
+ this.#ready = settled(next);
27
+ }
28
+ /** Settle the current load: resolve, or reject with `error`. Without a pending load, mark the viewer ready. */
29
+ finish(error) {
30
+ if (!this.#settle)
31
+ this.pending();
32
+ this.#settle(error);
33
+ this.#settle = undefined;
34
+ }
35
+ disconnect() {
36
+ this.#settle?.(new Error('The viewer was disconnected'));
37
+ this.#settle = undefined;
38
+ this.#ready = notConnected();
39
+ }
40
+ }
41
+ /** Mark a stored promise handled, so an unawaited rejection is not reported; awaiting it still rejects. */
42
+ function settled(promise) {
43
+ promise.catch(() => { });
44
+ return promise;
45
+ }
46
+ //# sourceMappingURL=readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.js","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC,CAAC,CAAC;AAE3G;;;GAGG;AACH,MAAM,OAAO,eAAe;IAC1B,iHAAiH;IACjH,MAAM,GAAG,YAAY,EAAE,CAAC;IACxB,OAAO,CAA6B;IAEpC,oGAAoG;IACpG,KAAK,CAAC,SAAS;QACb,IAAI,KAAoB,CAAC;QACzB,GAAG,CAAC;YACF,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;YACpB,MAAM,KAAK,CAAC;QACd,CAAC,QAAQ,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE;IAClC,CAAC;IACD,0GAA0G;IAC1G,OAAO;QACL,IAAI,MAAkC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,MAAM,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,+GAA+G;IAC/G,MAAM,CAAC,KAAe;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,OAAQ,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC3B,CAAC;IACD,UAAU;QACR,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,2GAA2G;AAC3G,SAAS,OAAO,CAAC,OAAsB;IACrC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Styles for {@link ImageViewer}, rendered inline by the component so it needs no CSS loader. Re-theme it by setting
3
+ * `--hdrify-viewer-*` custom properties on the viewer or an ancestor; they inherit into the element's shadow root.
4
+ */
5
+ export declare const IMAGE_VIEWER_CSS = "\n.hdrify-viewer {\n --hv-bg: var(--hdrify-viewer-background, #000);\n --hv-fg: var(--hdrify-viewer-foreground, #e5e7eb);\n --hv-muted: var(--hdrify-viewer-muted-foreground, #a3adba);\n --hv-popover: var(--hdrify-viewer-popover, rgb(17 24 32 / 92%));\n --hv-border: var(--hdrify-viewer-border, #49515e);\n --hv-input: var(--hdrify-viewer-input, #20252e);\n --hv-primary: var(--hdrify-viewer-primary, #7dd3fc);\n --hv-ring: var(--hdrify-viewer-ring, #7dd3fc);\n --hv-error: var(--hdrify-viewer-destructive, #f87171);\n --hv-radius: var(--hdrify-viewer-radius, 6px);\n position: relative;\n overflow: hidden;\n box-sizing: border-box;\n min-width: 0;\n background: var(--hv-bg);\n color: var(--hv-fg);\n font: var(--hdrify-viewer-font-size, var(--vscode-font-size, 13px)) / 1.4\n var(--hdrify-viewer-font-family, var(--vscode-font-family, system-ui, sans-serif));\n}\n.hdrify-viewer *, .hdrify-viewer *::before, .hdrify-viewer *::after { box-sizing: inherit; }\n.hdrify-viewer :is(button, input, select) { font: inherit; color: inherit; }\n.hdrify-viewer :is(button, input, select):focus-visible { outline: 2px solid var(--hv-ring); outline-offset: 1px; }\n.hdrify-viewer [hidden] { display: none !important; }\n.hdrify-viewer-viewport { position: absolute; inset: 0; display: flex; overflow: auto; }\n.hdrify-viewer-canvas { display: block; flex: none; margin: auto; }\n.hdrify-viewer[data-fit='contain'] .hdrify-viewer-canvas { width: 100%; height: 100%; object-fit: contain; }\n.hdrify-viewer-progress { position: absolute; inset: 0; z-index: 2; overflow: hidden; pointer-events: none; }\n.hdrify-viewer-progress-fill {\n position: absolute; top: 0; left: 0; width: 100%; height: var(--progress-bar-height, 5px);\n background-color: var(--progress-bar-color, rgb(255 255 255 / 50%));\n transform-origin: top left; transition: transform 0.09s;\n}\n.hdrify-viewer-progress-fill.complete { opacity: 0; transition: transform 0.09s, opacity 0.3s 1s; }\n@media (prefers-reduced-motion: reduce) { .hdrify-viewer-progress-fill { transition: none; } }\n.hdrify-viewer-error {\n position: absolute; top: 12px; left: 12px; right: 12px; max-width: 460px; margin: 0; padding: 10px 14px;\n border: 1px solid color-mix(in srgb, var(--hv-error) 40%, transparent); border-radius: var(--hv-radius);\n background: rgb(24 18 22 / 90%); color: var(--hv-error); overflow-wrap: anywhere;\n}\n.hdrify-viewer-surface {\n border: 1px solid var(--hv-border); border-radius: var(--hv-radius); background: var(--hv-popover);\n box-shadow: 0 6px 20px rgb(0 0 0 / 30%); backdrop-filter: blur(12px);\n}\n.hdrify-viewer-exposure {\n position: absolute; left: 12px; top: 50%; transform: translateY(-50%); height: min(260px, calc(100% - 24px));\n width: 52px; padding: 8px 0; display: flex; flex-direction: column; align-items: center; gap: 6px;\n}\n.hdrify-viewer-exposure output { font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; }\n.hdrify-viewer-exposure input {\n flex: 1; min-height: 0; width: 20px; margin: 0; writing-mode: vertical-lr; direction: rtl;\n accent-color: var(--hv-primary); cursor: pointer;\n}\n.hdrify-viewer-toggle {\n position: absolute; right: 12px; bottom: 12px; display: grid; place-items: center; width: 36px; height: 36px;\n padding: 0; cursor: pointer;\n}\n.hdrify-viewer-panel {\n position: absolute; right: 12px; bottom: 56px; width: min(280px, calc(100% - 24px));\n max-height: calc(100% - 68px); overflow: auto; padding: 12px 14px;\n}\n.hdrify-viewer-panel header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }\n.hdrify-viewer-panel header button {\n width: 26px; height: 26px; padding: 0; border: 0; border-radius: var(--hv-radius); background: transparent;\n font-size: 18px; line-height: 1; cursor: pointer;\n}\n.hdrify-viewer-panel label { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 10px 0; }\n.hdrify-viewer-panel :is(select, input) {\n width: 128px; padding: 3px 6px; border: 1px solid var(--hv-border); border-radius: var(--hv-radius);\n background: var(--hv-input);\n}\n.hdrify-viewer-panel dl {\n display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 12px 0 0; padding-top: 10px;\n border-top: 1px solid var(--hv-border); color: var(--hv-muted); font-size: 12px;\n}\n.hdrify-viewer-panel dt { color: var(--hv-fg); }\n.hdrify-viewer-panel dd { margin: 0; overflow-wrap: anywhere; }\n";
6
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,q3IA8E5B,CAAC"}
package/dist/styles.js ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Styles for {@link ImageViewer}, rendered inline by the component so it needs no CSS loader. Re-theme it by setting
3
+ * `--hdrify-viewer-*` custom properties on the viewer or an ancestor; they inherit into the element's shadow root.
4
+ */
5
+ export const IMAGE_VIEWER_CSS = `
6
+ .hdrify-viewer {
7
+ --hv-bg: var(--hdrify-viewer-background, #000);
8
+ --hv-fg: var(--hdrify-viewer-foreground, #e5e7eb);
9
+ --hv-muted: var(--hdrify-viewer-muted-foreground, #a3adba);
10
+ --hv-popover: var(--hdrify-viewer-popover, rgb(17 24 32 / 92%));
11
+ --hv-border: var(--hdrify-viewer-border, #49515e);
12
+ --hv-input: var(--hdrify-viewer-input, #20252e);
13
+ --hv-primary: var(--hdrify-viewer-primary, #7dd3fc);
14
+ --hv-ring: var(--hdrify-viewer-ring, #7dd3fc);
15
+ --hv-error: var(--hdrify-viewer-destructive, #f87171);
16
+ --hv-radius: var(--hdrify-viewer-radius, 6px);
17
+ position: relative;
18
+ overflow: hidden;
19
+ box-sizing: border-box;
20
+ min-width: 0;
21
+ background: var(--hv-bg);
22
+ color: var(--hv-fg);
23
+ font: var(--hdrify-viewer-font-size, var(--vscode-font-size, 13px)) / 1.4
24
+ var(--hdrify-viewer-font-family, var(--vscode-font-family, system-ui, sans-serif));
25
+ }
26
+ .hdrify-viewer *, .hdrify-viewer *::before, .hdrify-viewer *::after { box-sizing: inherit; }
27
+ .hdrify-viewer :is(button, input, select) { font: inherit; color: inherit; }
28
+ .hdrify-viewer :is(button, input, select):focus-visible { outline: 2px solid var(--hv-ring); outline-offset: 1px; }
29
+ .hdrify-viewer [hidden] { display: none !important; }
30
+ .hdrify-viewer-viewport { position: absolute; inset: 0; display: flex; overflow: auto; }
31
+ .hdrify-viewer-canvas { display: block; flex: none; margin: auto; }
32
+ .hdrify-viewer[data-fit='contain'] .hdrify-viewer-canvas { width: 100%; height: 100%; object-fit: contain; }
33
+ .hdrify-viewer-progress { position: absolute; inset: 0; z-index: 2; overflow: hidden; pointer-events: none; }
34
+ .hdrify-viewer-progress-fill {
35
+ position: absolute; top: 0; left: 0; width: 100%; height: var(--progress-bar-height, 5px);
36
+ background-color: var(--progress-bar-color, rgb(255 255 255 / 50%));
37
+ transform-origin: top left; transition: transform 0.09s;
38
+ }
39
+ .hdrify-viewer-progress-fill.complete { opacity: 0; transition: transform 0.09s, opacity 0.3s 1s; }
40
+ @media (prefers-reduced-motion: reduce) { .hdrify-viewer-progress-fill { transition: none; } }
41
+ .hdrify-viewer-error {
42
+ position: absolute; top: 12px; left: 12px; right: 12px; max-width: 460px; margin: 0; padding: 10px 14px;
43
+ border: 1px solid color-mix(in srgb, var(--hv-error) 40%, transparent); border-radius: var(--hv-radius);
44
+ background: rgb(24 18 22 / 90%); color: var(--hv-error); overflow-wrap: anywhere;
45
+ }
46
+ .hdrify-viewer-surface {
47
+ border: 1px solid var(--hv-border); border-radius: var(--hv-radius); background: var(--hv-popover);
48
+ box-shadow: 0 6px 20px rgb(0 0 0 / 30%); backdrop-filter: blur(12px);
49
+ }
50
+ .hdrify-viewer-exposure {
51
+ position: absolute; left: 12px; top: 50%; transform: translateY(-50%); height: min(260px, calc(100% - 24px));
52
+ width: 52px; padding: 8px 0; display: flex; flex-direction: column; align-items: center; gap: 6px;
53
+ }
54
+ .hdrify-viewer-exposure output { font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; }
55
+ .hdrify-viewer-exposure input {
56
+ flex: 1; min-height: 0; width: 20px; margin: 0; writing-mode: vertical-lr; direction: rtl;
57
+ accent-color: var(--hv-primary); cursor: pointer;
58
+ }
59
+ .hdrify-viewer-toggle {
60
+ position: absolute; right: 12px; bottom: 12px; display: grid; place-items: center; width: 36px; height: 36px;
61
+ padding: 0; cursor: pointer;
62
+ }
63
+ .hdrify-viewer-panel {
64
+ position: absolute; right: 12px; bottom: 56px; width: min(280px, calc(100% - 24px));
65
+ max-height: calc(100% - 68px); overflow: auto; padding: 12px 14px;
66
+ }
67
+ .hdrify-viewer-panel header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
68
+ .hdrify-viewer-panel header button {
69
+ width: 26px; height: 26px; padding: 0; border: 0; border-radius: var(--hv-radius); background: transparent;
70
+ font-size: 18px; line-height: 1; cursor: pointer;
71
+ }
72
+ .hdrify-viewer-panel label { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 10px 0; }
73
+ .hdrify-viewer-panel :is(select, input) {
74
+ width: 128px; padding: 3px 6px; border: 1px solid var(--hv-border); border-radius: var(--hv-radius);
75
+ background: var(--hv-input);
76
+ }
77
+ .hdrify-viewer-panel dl {
78
+ display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 12px 0 0; padding-top: 10px;
79
+ border-top: 1px solid var(--hv-border); color: var(--hv-muted); font-size: 12px;
80
+ }
81
+ .hdrify-viewer-panel dt { color: var(--hv-fg); }
82
+ .hdrify-viewer-panel dd { margin: 0; overflow-wrap: anywhere; }
83
+ `;
84
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8E/B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type ImageDisplay } from './display.js';
2
+ /** A single FP16 texture drawn directly with WebGPU; unsupported presentation falls back to SDR. */
3
+ export declare function createWebGPUDisplay(canvas: HTMLCanvasElement, onFailure: () => void): Promise<ImageDisplay>;
4
+ //# sourceMappingURL=webgpuDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgpuDisplay.d.ts","sources":["../src/webgpuDisplay.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,YAAY,EAA0B,MAAM,cAAc,CAAC;AAkExF,oGAAoG;AACpG,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAoIjH"}
@@ -0,0 +1,208 @@
1
+ import { displayPixels } from './display.js';
2
+ // One full-screen triangle. UVs keep the source's first row at the top of the canvas.
3
+ // The canvas is tagged sRGB, so encode linear Rec.709 explicitly, preserving signed HDR values.
4
+ const SHADER = `
5
+ struct Vertex { @builtin(position) position: vec4f, @location(0) uv: vec2f };
6
+ @vertex fn vertex(@builtin(vertex_index) index: u32) -> Vertex {
7
+ let uv = vec2f(f32((index << 1u) & 2u), f32(index & 2u));
8
+ return Vertex(vec4f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, 0.0, 1.0), uv);
9
+ }
10
+ @group(0) @binding(0) var imageSampler: sampler;
11
+ @group(0) @binding(1) var image: texture_2d<f32>;
12
+ @group(0) @binding(2) var<uniform> exposure: f32;
13
+ @fragment fn fragment(input: Vertex) -> @location(0) vec4f {
14
+ let rgb = textureSample(image, imageSampler, input.uv).rgb * exposure;
15
+ let magnitude = abs(rgb);
16
+ let encoded = select(1.055 * pow(magnitude, vec3f(1.0 / 2.4)) - 0.055,
17
+ magnitude * 12.92, magnitude <= vec3f(0.0031308));
18
+ return vec4f(sign(rgb) * encoded, 1.0);
19
+ }`;
20
+ // Saturating float32 -> binary16, including subnormals and round-to-nearest-even.
21
+ const float = new Float32Array(1);
22
+ const bits = new Uint32Array(float.buffer);
23
+ function toHalfFloat(value) {
24
+ float[0] = Math.max(-65504, Math.min(65504, value));
25
+ const word = bits[0];
26
+ const sign = (word >>> 16) & 0x8000;
27
+ const exponent = ((word >>> 23) & 0xff) - 127;
28
+ if (exponent < -25)
29
+ return sign;
30
+ const mantissa = (word & 0x7fffff) | 0x800000;
31
+ const shift = exponent < -14 ? -exponent - 1 : 13;
32
+ const rounded = (mantissa + (2 ** (shift - 1) - 1) + ((mantissa >>> shift) & 1)) >>> shift;
33
+ return sign | (exponent < -14 ? rounded : ((exponent + 14) << 10) + rounded);
34
+ }
35
+ // One capability probe shared by every viewer in this page. Cache failure too.
36
+ let hdrCapability;
37
+ async function probeHdrCapability(document) {
38
+ const adapter = await document.defaultView?.navigator.gpu?.requestAdapter();
39
+ if (!adapter)
40
+ return false;
41
+ const device = await adapter.requestDevice();
42
+ const canvas = document.createElement('canvas');
43
+ const context = canvas.getContext('webgpu');
44
+ try {
45
+ if (!context)
46
+ return false;
47
+ context.configure({
48
+ device,
49
+ format: 'rgba16float',
50
+ colorSpace: 'srgb',
51
+ alphaMode: 'opaque',
52
+ toneMapping: { mode: 'extended' },
53
+ });
54
+ const configuration = context.getConfiguration();
55
+ return (configuration?.format === 'rgba16float' &&
56
+ configuration.toneMapping?.mode === 'extended' &&
57
+ configuration.colorSpace === 'srgb');
58
+ }
59
+ finally {
60
+ context?.unconfigure();
61
+ device.destroy();
62
+ }
63
+ }
64
+ /** A single FP16 texture drawn directly with WebGPU; unsupported presentation falls back to SDR. */
65
+ export async function createWebGPUDisplay(canvas, onFailure) {
66
+ const supported = await (hdrCapability ??= probeHdrCapability(canvas.ownerDocument).catch(() => false));
67
+ if (!supported)
68
+ throw new Error('WebGPU extended FP16 presentation is not supported');
69
+ // Adapters can create only one device; each viewer acquires its own rendering resources.
70
+ const adapter = await canvas.ownerDocument.defaultView?.navigator.gpu?.requestAdapter();
71
+ if (!adapter)
72
+ throw new Error('WebGPU is not available');
73
+ const device = await adapter.requestDevice({
74
+ requiredLimits: { maxTextureDimension2D: adapter.limits.maxTextureDimension2D },
75
+ });
76
+ let disposed = false;
77
+ let map;
78
+ let exposureBuffer;
79
+ let context = null;
80
+ const fail = () => {
81
+ if (!disposed)
82
+ onFailure();
83
+ };
84
+ const dispose = () => {
85
+ if (disposed)
86
+ return;
87
+ disposed = true;
88
+ device.removeEventListener('uncapturederror', fail);
89
+ context?.unconfigure();
90
+ map?.destroy();
91
+ exposureBuffer?.destroy();
92
+ device.destroy();
93
+ };
94
+ try {
95
+ device.addEventListener('uncapturederror', fail);
96
+ void device.lost.then(fail);
97
+ context = canvas.getContext('webgpu');
98
+ if (!context)
99
+ throw new Error('WebGPU canvas is not available');
100
+ context.configure({
101
+ device,
102
+ format: 'rgba16float',
103
+ colorSpace: 'srgb',
104
+ alphaMode: 'opaque',
105
+ toneMapping: { mode: 'extended' },
106
+ });
107
+ const surface = context;
108
+ const module = device.createShaderModule({ code: SHADER });
109
+ const pipeline = await device.createRenderPipelineAsync({
110
+ layout: 'auto',
111
+ vertex: { module, entryPoint: 'vertex' },
112
+ fragment: { module, entryPoint: 'fragment', targets: [{ format: 'rgba16float' }] },
113
+ primitive: { topology: 'triangle-list' },
114
+ });
115
+ const uniformBuffer = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
116
+ exposureBuffer = uniformBuffer;
117
+ const exposureValue = new Float32Array(1);
118
+ const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' });
119
+ let bindings;
120
+ let previousImage;
121
+ let previousToneMapping;
122
+ let previousExposure;
123
+ let wideSource = false;
124
+ let size = '';
125
+ return {
126
+ mode: 'hdr',
127
+ draw(image, toneMapping, exposure) {
128
+ if (disposed)
129
+ return;
130
+ // Ordinary exposure-only changes are a uniform update. Explicit tone mappers use the same HDRify
131
+ // operators as SDR, retaining float output rather than quantizing through an 8-bit preview.
132
+ if (previousImage !== image ||
133
+ previousToneMapping !== toneMapping ||
134
+ ((toneMapping !== 'none' || wideSource) && previousExposure !== exposure)) {
135
+ const pixels = displayPixels(image, toneMapping, toneMapping === 'none' ? 1 : exposure);
136
+ wideSource =
137
+ toneMapping === 'none' && pixels.some((value, index) => index % 4 !== 3 && Math.abs(value) > 65504);
138
+ // Bake exposure for values outside FP16's range, so lowering exposure can recover bright
139
+ // float32 source highlights rather than magnifying an already-clipped upload.
140
+ if (wideSource)
141
+ for (let i = 0; i < pixels.length; i++)
142
+ if (i % 4 !== 3)
143
+ pixels[i] = pixels[i] * exposure;
144
+ const halves = new Uint16Array(pixels.length);
145
+ for (let i = 0; i < pixels.length; i++)
146
+ halves[i] = toHalfFloat(pixels[i]);
147
+ if (!map || map.width !== image.width || map.height !== image.height) {
148
+ map?.destroy();
149
+ map = device.createTexture({
150
+ size: [image.width, image.height],
151
+ format: 'rgba16float',
152
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
153
+ });
154
+ bindings = device.createBindGroup({
155
+ layout: pipeline.getBindGroupLayout(0),
156
+ entries: [
157
+ { binding: 0, resource: sampler },
158
+ { binding: 1, resource: map.createView() },
159
+ { binding: 2, resource: { buffer: uniformBuffer } },
160
+ ],
161
+ });
162
+ }
163
+ device.queue.writeTexture({ texture: map }, halves, { bytesPerRow: image.width * 8 }, [
164
+ image.width,
165
+ image.height,
166
+ ]);
167
+ previousImage = image;
168
+ previousToneMapping = toneMapping;
169
+ }
170
+ previousExposure = exposure;
171
+ exposureValue[0] = toneMapping === 'none' && !wideSource ? exposure : 1;
172
+ device.queue.writeBuffer(uniformBuffer, 0, exposureValue);
173
+ const encoder = device.createCommandEncoder();
174
+ const pass = encoder.beginRenderPass({
175
+ colorAttachments: [
176
+ {
177
+ view: surface.getCurrentTexture().createView(),
178
+ loadOp: 'clear',
179
+ storeOp: 'store',
180
+ clearValue: { r: 0, g: 0, b: 0, a: 1 },
181
+ },
182
+ ],
183
+ });
184
+ pass.setPipeline(pipeline);
185
+ pass.setBindGroup(0, bindings);
186
+ pass.draw(3);
187
+ pass.end();
188
+ device.queue.submit([encoder.finish()]);
189
+ },
190
+ resize(width, height, pixelRatio) {
191
+ const ratio = Math.min(pixelRatio, device.limits.maxTextureDimension2D / Math.max(width, height));
192
+ const next = `${width}:${height}:${ratio}`;
193
+ if (next === size)
194
+ return;
195
+ size = next;
196
+ canvas.width = Math.max(1, Math.floor(width * ratio));
197
+ canvas.height = Math.max(1, Math.floor(height * ratio));
198
+ },
199
+ complete: () => device.queue.onSubmittedWorkDone(),
200
+ dispose,
201
+ };
202
+ }
203
+ catch (error) {
204
+ dispose();
205
+ throw error;
206
+ }
207
+ }
208
+ //# sourceMappingURL=webgpuDisplay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgpuDisplay.js","sources":["../src/webgpuDisplay.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "hdrify-react",
3
- "version": "1.3.0",
4
- "description": "React component for displaying HDR HdrifyImage with tone mapping and exposure",
3
+ "version": "2.1.0",
4
+ "description": "React component and <image-viewer> web component for displaying HDR images with tone mapping and exposure",
5
5
  "keywords": [
6
6
  "exr",
7
7
  "float-image",
8
8
  "gainmap",
9
9
  "hdr",
10
+ "image-viewer",
11
+ "openexr",
12
+ "radiance",
10
13
  "react",
11
14
  "tone-mapping",
12
- "ultra-hdr"
15
+ "ultra-hdr",
16
+ "web-component"
13
17
  ],
14
18
  "bugs": {
15
19
  "url": "https://github.com/bhouston/hdrify/issues"
@@ -22,33 +26,58 @@
22
26
  "directory": "packages/hdrify-react"
23
27
  },
24
28
  "files": [
25
- "dist"
29
+ "dist",
30
+ "!dist/**/*.tsbuildinfo"
26
31
  ],
27
32
  "type": "module",
28
- "main": "dist/index.js",
29
- "module": "dist/index.js",
30
- "types": "dist/index.d.ts",
33
+ "sideEffects": [
34
+ "./dist/element.js",
35
+ "./dist/image-viewer.js"
36
+ ],
37
+ "main": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "default": "./dist/index.js"
43
+ },
44
+ "./element": {
45
+ "types": "./dist/element.d.ts",
46
+ "default": "./dist/element.js"
47
+ },
48
+ "./image-viewer.js": "./dist/image-viewer.js",
49
+ "./dist/*": "./dist/*",
50
+ "./package.json": "./package.json",
51
+ "./decode": {
52
+ "types": "./dist/decode.d.ts",
53
+ "default": "./dist/decode.js"
54
+ }
55
+ },
31
56
  "publishConfig": {
32
57
  "access": "public",
33
58
  "registry": "https://registry.npmjs.org/"
34
59
  },
35
60
  "dependencies": {
36
- "hdrify": "1.3.0"
61
+ "hdrify": "2.1.0"
37
62
  },
38
63
  "devDependencies": {
39
- "@types/react": "^19.2.0",
40
- "@types/react-dom": "^19.2.0"
64
+ "@types/react": "^19.3.0",
65
+ "@types/react-dom": "^19.3.0",
66
+ "@webgpu/types": "^0.1.74",
67
+ "esbuild": "^0.27.3",
68
+ "react": "^19.3.1",
69
+ "react-dom": "^19.3.1"
41
70
  },
42
71
  "peerDependencies": {
43
72
  "react": "^18.0.0 || ^19.0.0",
44
73
  "react-dom": "^18.0.0 || ^19.0.0"
45
74
  },
46
75
  "engines": {
47
- "node": ">=22.0.0"
76
+ "node": ">=22"
48
77
  },
49
78
  "scripts": {
50
- "build": "tsc",
51
- "tsc": "tsc",
79
+ "build": "tsc && node scripts/build-element.mjs",
80
+ "tsc": "tsc --noEmit",
52
81
  "dev": "tsc --watch"
53
82
  }
54
83
  }