@preference-sl/pref-viewer 2.13.0-beta.0 → 2.13.0-beta.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.
@@ -0,0 +1,94 @@
1
+ import translations from "./translations.js";
2
+
3
+ export const DEFAULT_LOCALE = "en-EN";
4
+
5
+ export const SUPPORTED_LOCALES = Object.keys(translations);
6
+ const listeners = new Set();
7
+ let currentLocale = DEFAULT_LOCALE;
8
+
9
+ const findLocaleKey = (localeId) => {
10
+ if (typeof localeId !== "string") {
11
+ return DEFAULT_LOCALE;
12
+ }
13
+ const normalized = localeId.trim().toLowerCase();
14
+ const match = SUPPORTED_LOCALES.find((locale) => locale.toLowerCase() === normalized);
15
+ return match ?? DEFAULT_LOCALE;
16
+ };
17
+
18
+ const getBundle = (localeId = currentLocale) => {
19
+ const key = findLocaleKey(localeId);
20
+ return translations[key] ?? translations[DEFAULT_LOCALE];
21
+ };
22
+
23
+ const resolveValue = (bundle, parts) => parts.reduce((acc, part) => (
24
+ acc && Object.prototype.hasOwnProperty.call(acc, part) ? acc[part] : undefined
25
+ ), bundle);
26
+
27
+ export const availableLocales = () => [...SUPPORTED_LOCALES];
28
+
29
+ export const getLocale = () => currentLocale;
30
+
31
+ export const resolveLocale = (localeId) => findLocaleKey(localeId);
32
+
33
+ export const setLocale = (localeId) => {
34
+ const resolved = findLocaleKey(localeId);
35
+ if (resolved === currentLocale) {
36
+ return currentLocale;
37
+ }
38
+ currentLocale = resolved;
39
+ listeners.forEach((listener) => {
40
+ try {
41
+ listener(currentLocale);
42
+ } catch (error) {
43
+ console.warn("PrefViewer i18n listener failed", error);
44
+ }
45
+ });
46
+ return currentLocale;
47
+ };
48
+
49
+ export const onLocaleChange = (listener) => {
50
+ if (typeof listener !== "function") {
51
+ return () => {};
52
+ }
53
+ listeners.add(listener);
54
+ return () => listeners.delete(listener);
55
+ };
56
+
57
+ export const translate = (key, options = {}) => {
58
+ if (!key) {
59
+ return "";
60
+ }
61
+ const { locale, fallback } = options;
62
+ const parts = Array.isArray(key) ? key : key.split(".");
63
+ const bundle = getBundle(locale);
64
+ let value = resolveValue(bundle, parts);
65
+ if (value !== undefined) {
66
+ return value;
67
+ }
68
+ if (bundle !== translations[DEFAULT_LOCALE]) {
69
+ value = resolveValue(translations[DEFAULT_LOCALE], parts);
70
+ if (value !== undefined) {
71
+ return value;
72
+ }
73
+ }
74
+ return fallback ?? "";
75
+ };
76
+
77
+ export const getNamespace = (namespace, options = {}) => {
78
+ if (!namespace) {
79
+ return getBundle(options.locale);
80
+ }
81
+ return translate(namespace, options);
82
+ };
83
+
84
+ export default {
85
+ availableLocales,
86
+ DEFAULT_LOCALE,
87
+ getLocale,
88
+ getNamespace,
89
+ onLocaleChange,
90
+ resolveLocale,
91
+ setLocale,
92
+ SUPPORTED_LOCALES,
93
+ translate,
94
+ };
@@ -0,0 +1,104 @@
1
+ export const translations = {
2
+ "en-EN": {
3
+ prefViewer: {
4
+ menu3d: {
5
+ title: "3D render settings",
6
+ subtitle: "Toggle visual effects on or off.",
7
+ pendingLabel: "Pending changes",
8
+ actions: {
9
+ apply: "Apply",
10
+ applying: "Applying...",
11
+ },
12
+ status: {
13
+ error: "Couldn't apply the changes.",
14
+ },
15
+ switches: {
16
+ antiAliasingEnabled: {
17
+ label: "Antialiasing",
18
+ helper: "Softens jagged edges and lines.",
19
+ },
20
+ ambientOcclusionEnabled: {
21
+ label: "Ambient Occlusion",
22
+ helper: "Reduction of ambient light in corners and areas close to surfaces.",
23
+ },
24
+ iblEnabled: {
25
+ label: "IBL",
26
+ helper: "Uses the HDR environment to light the scene.",
27
+ },
28
+ shadowsEnabled: {
29
+ label: "Shadows",
30
+ helper: "Enables shadows.",
31
+ },
32
+ },
33
+ },
34
+ downloadDialog: {
35
+ title: "Download 3D Scene",
36
+ sections: {
37
+ content: "Content",
38
+ format: "Format",
39
+ },
40
+ options: {
41
+ model: "Model",
42
+ scene: "Scene",
43
+ both: "Both",
44
+ },
45
+ buttons: {
46
+ download: "Download",
47
+ cancel: "Cancel",
48
+ },
49
+ },
50
+ },
51
+ },
52
+ "es-ES": {
53
+ prefViewer: {
54
+ menu3d: {
55
+ title: "Ajustes de render 3D",
56
+ subtitle: "Activa o desactiva los efectos visuales.",
57
+ pendingLabel: "Pendiente de aplicar",
58
+ actions: {
59
+ apply: "Aplicar",
60
+ applying: "Aplicando...",
61
+ },
62
+ status: {
63
+ error: "No se pudo aplicar los cambios.",
64
+ },
65
+ switches: {
66
+ antiAliasingEnabled: {
67
+ label: "Antialiasing",
68
+ helper: "Suaviza aristas y líneas.",
69
+ },
70
+ ambientOcclusionEnabled: {
71
+ label: "Ambient Occlusion",
72
+ helper: "Atenuación de la luz ambiental en rincones y zonas cercanas entre superficies.",
73
+ },
74
+ iblEnabled: {
75
+ label: "IBL",
76
+ helper: "Usa la imagen HDR de entorno para iluminar la escena.",
77
+ },
78
+ shadowsEnabled: {
79
+ label: "Sombras",
80
+ helper: "Activa sombras.",
81
+ },
82
+ },
83
+ },
84
+ downloadDialog: {
85
+ title: "Descargar escena 3D",
86
+ sections: {
87
+ content: "Contenido",
88
+ format: "Formato",
89
+ },
90
+ options: {
91
+ model: "Modelo",
92
+ scene: "Escena",
93
+ both: "Ambos",
94
+ },
95
+ buttons: {
96
+ download: "Descargar",
97
+ cancel: "Cancelar",
98
+ },
99
+ },
100
+ },
101
+ },
102
+ };
103
+
104
+ export default translations;
@@ -18,6 +18,7 @@ export class ContainerData {
18
18
  this.show = true;
19
19
  this.size = 0;
20
20
  this.timeStamp = null;
21
+ this.storage = null;
21
22
  this.visible = false;
22
23
  // Set initial information about ongoing update
23
24
  this.reset();
@@ -40,6 +41,9 @@ export class ContainerData {
40
41
  this.size = this.update.size;
41
42
  this.timeStamp = this.update.timeStamp;
42
43
  this.metadata = { ...(this.update.metadata ?? {}) };
44
+ if (this.update.storage !== undefined && this.update.storage !== null) {
45
+ this.storage = this.update.storage;
46
+ }
43
47
  } else {
44
48
  this.update.success = false;
45
49
  }
@@ -50,12 +54,27 @@ export class ContainerData {
50
54
  this.update.storage = storage;
51
55
  this.update.success = false;
52
56
  this.update.show = show !== undefined ? show : this.update.show !== null ? this.update.show : this.show;
57
+ if (storage !== undefined && storage !== null) {
58
+ this.storage = storage;
59
+ }
53
60
  }
54
61
  setPendingCacheData(size = 0, timeStamp = null, metadata = {}) {
55
62
  this.update.size = size;
56
63
  this.update.timeStamp = timeStamp;
57
64
  this.update.metadata = { ...(metadata ?? {}) };
58
65
  }
66
+ setPendingWithCurrentStorage() {
67
+ const storedSource = this.storage ?? null;
68
+ if (!storedSource) {
69
+ return false;
70
+ }
71
+ const targetShow = this.update.show !== null ? this.update.show : this.show;
72
+ this.setPending(storedSource, targetShow);
73
+ this.update.size = this.size;
74
+ this.update.timeStamp = this.timeStamp;
75
+ this.update.metadata = { ...(this.metadata ?? {}) };
76
+ return true;
77
+ }
59
78
  get isPending() {
60
79
  return this.update.pending === true;
61
80
  }
@@ -114,7 +133,11 @@ export class MaterialData {
114
133
  this.update.value = value;
115
134
  }
116
135
  setPendingWithCurrent() {
136
+ if (this.value === undefined) {
137
+ return false;
138
+ }
117
139
  this.setPending(this.value);
140
+ return true;
118
141
  }
119
142
  get isPending() {
120
143
  return this.update.pending === true;
@@ -175,6 +198,11 @@ export class CameraData {
175
198
  this.update.value = value;
176
199
  this.update.locked = locked;
177
200
  }
201
+ setPendingWithCurrent() {
202
+ const currentValue = this.value !== undefined ? this.value : null;
203
+ this.setPending(currentValue, this.locked);
204
+ return true;
205
+ }
178
206
  get isPending() {
179
207
  return this.update.pending === true;
180
208
  }
@@ -8,14 +8,16 @@ import { FileStorage } from "./file-storage.js";
8
8
  *
9
9
  * Overview:
10
10
  * - Encapsulates a Babylon.js-powered 3D viewer for displaying models, environments, and materials.
11
- * - Manages internal state for containers (model, environment, materials) and options (camera, materials).
11
+ * - Manages internal state for containers (model, environment, materials) and options (camera, materials, IBL, render toggles).
12
12
  * - Handles asset loading, configuration, and option updates (camera, materials, IBL) through attributes and public methods.
13
+ * - Exposes getters/setters for render settings so PrefViewerMenu3D or other UIs can persistently toggle AA/SSAO/IBL/shadows.
13
14
  * - Provides API for showing/hiding the viewer, model, and environment, and for downloading assets.
14
- * - Emits custom events for loading, loaded, and option-setting states.
15
+ * - Emits custom events for loading, loaded, option-setting, and render-setting workflows.
15
16
  *
16
17
  * Usage:
17
18
  * - Use as a custom HTML element: <pref-viewer-3d ...>
18
19
  * - Configure via attributes (config, options, show-model, show-scene, visible).
20
+ * - Call getRenderSettings()/applyRenderSettings() to synchronize UI toggles with BabylonJSController persistence.
19
21
  * - Control viewer state and assets via public methods.
20
22
  *
21
23
  * Observed Attributes:
@@ -28,6 +30,8 @@ import { FileStorage } from "./file-storage.js";
28
30
  * - hide(): Hides the 3D viewer component.
29
31
  * - load(config): Loads the provided configuration into the viewer.
30
32
  * - setOptions(options): Sets viewer options such as camera, materials, and image-based lighting (IBL).
33
+ * - getRenderSettings(): Returns the current anti-aliasing, SSAO, IBL, and shadow flags.
34
+ * - applyRenderSettings(settings): Persists incoming render toggles, reloads if needed, and reports the result.
31
35
  * - showModel(): Shows the 3D model.
32
36
  * - hideModel(): Hides the 3D model.
33
37
  * - showEnvironment(): Shows the 3D environment/scene.
@@ -63,7 +67,7 @@ import { FileStorage } from "./file-storage.js";
63
67
  *
64
68
  * Notes:
65
69
  * - Internal state and heavy initialization are handled in connectedCallback.
66
- * - Designed for extensibility and integration in product configurators and visualization tools.
70
+ * - Designed for extensibility and integration in product configurators, PrefViewer host elements, and visualization tools.
67
71
  * - All resource management and rendering operations are performed asynchronously for performance.
68
72
  */
69
73
  export default class PrefViewer3D extends HTMLElement {
@@ -337,13 +341,12 @@ export default class PrefViewer3D extends HTMLElement {
337
341
 
338
342
  if (options.ibl.url) {
339
343
  url = options.ibl.url;
340
- // TEMPORARY: Disable FileStorage usage due to efficiency problems
341
- // const fileStorage = new FileStorage("PrefViewer", "Files");
342
- // const newURL = await fileStorage.getURL(options.ibl.url);
343
- // if (newURL) {
344
- // url = newURL;
345
- // timeStamp = await fileStorage.getTimeStamp(options.ibl.url);
346
- // }
344
+ const fileStorage = new FileStorage("PrefViewer", "Files");
345
+ const newURL = await fileStorage.getURL(options.ibl.url);
346
+ if (newURL) {
347
+ url = newURL;
348
+ timeStamp = await fileStorage.getTimeStamp(options.ibl.url);
349
+ }
347
350
  }
348
351
  if (options.ibl.shadows !== undefined) {
349
352
  shadows = options.ibl.shadows;
@@ -740,6 +743,39 @@ export default class PrefViewer3D extends HTMLElement {
740
743
  this.#babylonJSController.downloadUSDZ(2);
741
744
  }
742
745
 
746
+ /**
747
+ * Returns the current render settings used by the BabylonJSController.
748
+ * @public
749
+ * @returns {object|null} A shallow copy of the render settings or null if unavailable.
750
+ */
751
+ getRenderSettings() {
752
+ if (!this.#babylonJSController) {
753
+ return null;
754
+ }
755
+ return this.#babylonJSController.getRenderSettings();
756
+ }
757
+
758
+ /**
759
+ * Applies render settings that require reloading the Babylon.js scene.
760
+ * @public
761
+ * @param {object} settings - Partial settings for anti-aliasing, SSAO, IBL, and shadows.
762
+ * @returns {Promise<object>} Resolves with the reload summary.
763
+ */
764
+ async applyRenderSettings(settings) {
765
+ if (!this.#babylonJSController) {
766
+ return { changed: false, success: false };
767
+ }
768
+ const scheduled = this.#babylonJSController.scheduleRenderSettingsReload(settings);
769
+ if (!scheduled.changed) {
770
+ return { changed: false, success: true };
771
+ }
772
+
773
+ this.#onLoading();
774
+ const reloadResult = await this.#babylonJSController.reloadWithCurrentSettings();
775
+ const detail = this.#onLoaded();
776
+ return { changed: true, success: reloadResult.success, detail, controller: reloadResult };
777
+ }
778
+
743
779
  /**
744
780
  * Indicates whether the component has completed its initialization.
745
781
  * @public