hdrify-react 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,8 @@
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';
3
8
  //# 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"}
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"}
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "hdrify-react",
3
- "version": "1.2.2",
4
- "description": "React component for displaying HDR HdrifyImage with tone mapping and exposure",
3
+ "version": "1.4.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,53 @@
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
+ },
31
52
  "publishConfig": {
32
53
  "access": "public",
33
54
  "registry": "https://registry.npmjs.org/"
34
55
  },
35
56
  "dependencies": {
36
- "hdrify": "1.2.2"
57
+ "hdrify": "1.4.0"
37
58
  },
38
59
  "devDependencies": {
39
- "@types/react": "^19.2.0",
40
- "@types/react-dom": "^19.2.0"
60
+ "@types/react": "^19.3.0",
61
+ "@types/react-dom": "^19.3.0",
62
+ "esbuild": "^0.27.3",
63
+ "react": "^19.3.1",
64
+ "react-dom": "^19.3.1"
41
65
  },
42
66
  "peerDependencies": {
43
67
  "react": "^18.0.0 || ^19.0.0",
44
68
  "react-dom": "^18.0.0 || ^19.0.0"
45
69
  },
46
70
  "engines": {
47
- "node": ">=26.0.0"
71
+ "node": ">=22"
48
72
  },
49
73
  "scripts": {
50
- "build": "tsc",
51
- "tsc": "tsc",
74
+ "build": "tsc && node scripts/build-element.mjs",
75
+ "tsc": "tsc --noEmit",
52
76
  "dev": "tsc --watch"
53
77
  }
54
78
  }