wuic-framework-lib 1.5.0 → 1.7.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.
Files changed (27) hide show
  1. package/fesm2022/{wuic-framework-lib-chart-list.component-0J1ghIrS.mjs → wuic-framework-lib-chart-list.component-Dq9IEKsY.mjs} +50 -38
  2. package/fesm2022/{wuic-framework-lib-code-editor.component-C21mvjyr.mjs → wuic-framework-lib-code-editor.component-B6IpTSo8.mjs} +174 -274
  3. package/fesm2022/{wuic-framework-lib-designer.component-x10ZzUEy.mjs → wuic-framework-lib-designer.component-D6cbqNFg.mjs} +499 -581
  4. package/fesm2022/wuic-framework-lib-first-run-wizard.component-D1ZQUl-q.mjs +1630 -0
  5. package/fesm2022/{wuic-framework-lib-framework-docs.component-xTGLbEnp.mjs → wuic-framework-lib-framework-docs.component-C_o5r7pg.mjs} +4320 -1255
  6. package/fesm2022/{wuic-framework-lib-pivot-builder.component-BkFFjPm4.mjs → wuic-framework-lib-pivot-builder.component-DkWGzJpr.mjs} +580 -68
  7. package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-Bi-TrJB6.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-D29go5yA.mjs} +2 -2
  8. package/fesm2022/wuic-framework-lib-report-designer.component-DcpCWghx.mjs +380 -0
  9. package/fesm2022/wuic-framework-lib-report-scaffold-dialog.component-CgUjG7Bi.mjs +431 -0
  10. package/fesm2022/{wuic-framework-lib-report-viewer.component-BR-BfI2r.mjs → wuic-framework-lib-report-viewer.component-C4Opgl_M.mjs} +8 -13
  11. package/fesm2022/{wuic-framework-lib-scene3d-designer.component-BQzFxSXP.mjs → wuic-framework-lib-scene3d-designer.component-8l3vsyEb.mjs} +323 -88
  12. package/fesm2022/{wuic-framework-lib-scene3d-light-baker-gpu-0LjqhSNn.mjs → wuic-framework-lib-scene3d-light-baker-gpu-C1w-8Aai.mjs} +2 -2
  13. package/fesm2022/{wuic-framework-lib-scene3d-shader-effect-CQLWkuh1.mjs → wuic-framework-lib-scene3d-shader-effect-DjHyCAFK.mjs} +612 -32
  14. package/fesm2022/{wuic-framework-lib-scene3d-viewer.component-imoV3HOs.mjs → wuic-framework-lib-scene3d-viewer.component-nMSl5jbe.mjs} +149 -18
  15. package/fesm2022/{wuic-framework-lib-scheduler-list.component-xzlTEkoq.mjs → wuic-framework-lib-scheduler-list.component-Cvk3Zg0Q.mjs} +169 -53
  16. package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-CrS6fOGj.mjs → wuic-framework-lib-spreadsheet-list-sf.component-C1U7JPiq.mjs} +149 -108
  17. package/fesm2022/{wuic-framework-lib-stimulsoft-license.service-BIyxB4pO.mjs → wuic-framework-lib-stimulsoft-license.service-CsNNSFYs.mjs} +3 -3
  18. package/fesm2022/wuic-framework-lib-timeline-list.component-cZbPgNh1.mjs +805 -0
  19. package/fesm2022/{wuic-framework-lib-workflow-designer.component-5xtQ7-6z.mjs → wuic-framework-lib-workflow-designer.component-C_4RQGa0.mjs} +180 -150
  20. package/fesm2022/{wuic-framework-lib-wuic-framework-lib-3TZ-j-yL.mjs → wuic-framework-lib-wuic-framework-lib-BZZwta8b.mjs} +6785 -2347
  21. package/fesm2022/wuic-framework-lib.mjs +1 -1
  22. package/package.json +21 -2
  23. package/styles/_wuic-tokens.scss +82 -0
  24. package/styles/styles.scss +160 -0
  25. package/types/wuic-framework-lib.d.ts +4269 -2691
  26. package/fesm2022/wuic-framework-lib-first-run-wizard.component-BuSxs_CM.mjs +0 -1316
  27. package/fesm2022/wuic-framework-lib-report-designer.component-ZvB46-LV.mjs +0 -194
@@ -2,6 +2,25 @@ import * as THREE from 'three';
2
2
  import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
3
3
  import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
4
4
 
5
+ /** Estensioni accettate dall'import (allineate all'allowlist upload backend, F4). */
6
+ const SCENE3D_ASSET_EXTENSIONS = {
7
+ '.gltf': 'gltf',
8
+ '.glb': 'glb',
9
+ '.obj': 'obj',
10
+ '.fbx': 'fbx',
11
+ '.stl': 'stl',
12
+ '.dae': 'dae',
13
+ };
14
+ /** Risolve il formato asset da un filename, o null se l'estensione non e' supportata. */
15
+ function resolveAssetFormat(fileName) {
16
+ const dot = fileName.lastIndexOf('.');
17
+ if (dot < 0) {
18
+ return null;
19
+ }
20
+ const ext = fileName.slice(dot).toLowerCase();
21
+ return SCENE3D_ASSET_EXTENSIONS[ext] ?? null;
22
+ }
23
+
5
24
  /**
6
25
  * Physics (iter2) — wrapper Rapier per il Scene3D designer/viewer.
7
26
  *
@@ -245,7 +264,7 @@ function serializeScene(root) {
245
264
  // subito dopo, mantenendo la scena runtime intatta.
246
265
  const detached = [];
247
266
  root.traverse((obj) => {
248
- if (obj !== root && obj.userData && obj.userData[WUIC_TRANSIENT_FLAG] === true && obj.parent) {
267
+ if (obj !== root && obj.userData?.[WUIC_TRANSIENT_FLAG] === true && obj.parent) {
249
268
  detached.push({ parent: obj.parent, child: obj });
250
269
  }
251
270
  });
@@ -264,7 +283,7 @@ function serializeScene(root) {
264
283
  }
265
284
  const bindings = {};
266
285
  root.traverse((obj) => {
267
- if (obj.userData && obj.userData[WUIC_TRANSIENT_FLAG] === true) {
286
+ if (obj.userData?.[WUIC_TRANSIENT_FLAG] === true) {
268
287
  return;
269
288
  }
270
289
  const binding = obj.userData ? obj.userData[WUIC_BINDING_KEY] : undefined;
@@ -320,7 +339,8 @@ const WUIC_CAROUSEL_BASE = 'wuicCarouselBase';
320
339
  const WUIC_CAROUSEL_ARROW = 'wuicCarouselArrow';
321
340
  function defaultCarouselConfig() {
322
341
  return {
323
- visible: 3, radius: 8, slotAngleDeg: 22, itemY: 0.9,
342
+ visible: 3, radius: 8, slotAngleDeg: 22, itemY: 0.9, meshYOffset: 0,
343
+ meshRotX: 0, meshRotY: 0, meshRotZ: 0,
324
344
  focusScale: 1.35, sideScale: 0.75,
325
345
  showBase: true, baseColor: '#3b4252', baseSize: 0.66,
326
346
  baseMetalness: 0.15, baseRoughness: 0.4, baseClearcoat: 0.85,
@@ -370,9 +390,17 @@ function arrangeCarousel(items, cfg, s) {
370
390
  const halfMax = Math.max(1e-6, (visible - 1) / 2);
371
391
  const radius = Math.max(0.5, n$1(cfg.radius, 8));
372
392
  const step = THREE.MathUtils.degToRad(n$1(cfg.slotAngleDeg, 22));
373
- const itemY = n$1(cfg.itemY, 0.9);
393
+ // itemY + meshYOffset: l'offset sposta SOLO i mesh su/giu' rispetto al disco
394
+ // (il disco/base in buildCarouselDecor usa itemY "puro", quindi resta fermo).
395
+ const itemY = n$1(cfg.itemY, 0.9) + n$1(cfg.meshYOffset, 0);
374
396
  const focus = n$1(cfg.focusScale, 1.35);
375
397
  const side = n$1(cfg.sideScale, 0.75);
398
+ // Rotazione base utente (gradi->rad) composta col coverflow via quaternione.
399
+ const rx = THREE.MathUtils.degToRad(n$1(cfg.meshRotX, 0));
400
+ const ry = THREE.MathUtils.degToRad(n$1(cfg.meshRotY, 0));
401
+ const rz = THREE.MathUtils.degToRad(n$1(cfg.meshRotZ, 0));
402
+ const hasUserRot = rx !== 0 || ry !== 0 || rz !== 0;
403
+ const userQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(rx, ry, rz));
376
404
  for (let i = 0; i < items.length; i++) {
377
405
  const it = items[i];
378
406
  const slot = i - s;
@@ -384,7 +412,14 @@ function arrangeCarousel(items, cfg, s) {
384
412
  const a = slot * step;
385
413
  // Arco: centro in (0,itemY,0); i lati curvano verso -Z (lontano dalla camera).
386
414
  it.position.set(Math.sin(a) * radius, itemY, Math.cos(a) * radius - radius);
387
- it.rotation.set(0, -a, 0); // ruota verso il fronte (effetto coverflow)
415
+ // Coverflow (Y verso il fronte) composto con la rotazione base utente:
416
+ // coverflow * user -> il modello ruota di user, poi l'item si orienta al fronte.
417
+ if (hasUserRot) {
418
+ it.quaternion.setFromEuler(new THREE.Euler(0, -a, 0)).multiply(userQuat);
419
+ }
420
+ else {
421
+ it.rotation.set(0, -a, 0);
422
+ }
388
423
  const t = Math.min(Math.abs(slot) / halfMax, 1);
389
424
  it.scale.setScalar(Math.max(0.01, focus + (side - focus) * t));
390
425
  }
@@ -990,7 +1025,7 @@ function buildSurface(group, rows, cfg, route, deps) {
990
1025
  if (!cfg.rowColumn || !cfg.colColumn || !cfg.valueColumn || rows.length === 0) {
991
1026
  return 0;
992
1027
  }
993
- const uniq = (col) => Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort();
1028
+ const uniq = (col) => Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort((a, b) => a.localeCompare(b));
994
1029
  const rlabels = uniq(cfg.rowColumn);
995
1030
  const clabels = uniq(cfg.colColumn);
996
1031
  const vals = rows.map((r) => n(deps.unwrap(r[cfg.valueColumn])));
@@ -1275,7 +1310,7 @@ function buildWarehouse(group, rows, cfg, route, deps) {
1275
1310
  if (!cfg.aisleColumn || !cfg.shelfColumn || rows.length === 0) {
1276
1311
  return 0;
1277
1312
  }
1278
- const uniq = (col) => Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort();
1313
+ const uniq = (col) => Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort((a, b) => a.localeCompare(b));
1279
1314
  const aisles = uniq(cfg.aisleColumn);
1280
1315
  const shelves = uniq(cfg.shelfColumn);
1281
1316
  const qtys = rows.map((r) => n(deps.unwrap(r[cfg.qtyColumn])));
@@ -1320,7 +1355,7 @@ function buildWarehouse(group, rows, cfg, route, deps) {
1320
1355
  // Kanban 3D, Calendar-wall, Stacked bars
1321
1356
  // ===========================================================================
1322
1357
  function uniqSorted(rows, col, deps) {
1323
- return Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort();
1358
+ return Array.from(new Set(rows.map((r) => String(deps.unwrap(r[col]) ?? '')))).sort((a, b) => a.localeCompare(b));
1324
1359
  }
1325
1360
  function defaultKanbanConfig() { return { statusColumn: '', labelColumn: '', cardWidth: 2, showLabels: true }; }
1326
1361
  /** Kanban: una colonna per stato; card impilate (una per record). */
@@ -1369,7 +1404,7 @@ function buildCalendar(group, rows, cfg, route, deps) {
1369
1404
  if (!cfg.dateColumn || rows.length === 0) {
1370
1405
  return 0;
1371
1406
  }
1372
- const items = rows.map((r) => ({ rec: r, d: new Date(String(deps.unwrap(r[cfg.dateColumn]))), v: cfg.valueColumn ? n(deps.unwrap(r[cfg.valueColumn])) : 1 })).filter((x) => !isNaN(x.d.getTime()));
1407
+ const items = rows.map((r) => ({ rec: r, d: new Date(String(deps.unwrap(r[cfg.dateColumn]))), v: cfg.valueColumn ? n(deps.unwrap(r[cfg.valueColumn])) : 1 })).filter((x) => !Number.isNaN(Number(x.d.getTime())));
1373
1408
  if (items.length === 0) {
1374
1409
  return 0;
1375
1410
  }
@@ -1480,11 +1515,53 @@ const helvetikerFont = { "glyphs": { "ο": { "x_min": 0, "x_max": 712, "ha": 815
1480
1515
  const WUIC_MESH_REPEATER_KEY = 'wuicMeshRepeater';
1481
1516
  /** Nome della mesh figlia generata (per riconoscerle e ripulirle). */
1482
1517
  const WUIC_REPEATER_CHILD = 'wuicRepeaterItem';
1518
+ /** userData key: record (unwrappato) bindato alla mesh figlia, per i callback
1519
+ * di interazione (click/dblclick/rightclick). Transient, non serializzato. */
1520
+ const WUIC_REPEATER_RECORD = 'wuicRepeaterRecord';
1483
1521
  /** Nome del placeholder mostrato quando il repeater non è ancora bindato. */
1484
1522
  const WUIC_REPEATER_PLACEHOLDER = 'wuicRepeaterPlaceholder';
1485
1523
  function defaultRepeaterTextureApply() {
1486
1524
  return { sourceKind: 'auto', wrap: 'stretch', repeatX: 1, repeatY: 1, textBg: '#334155', textFg: '#ffffff' };
1487
1525
  }
1526
+ /**
1527
+ * Normalizza un asset 3D importato (glTF/OBJ/STL, unità native arbitrarie) a una
1528
+ * dimensione ~1 unità WUIC, centrato su x/z e con la BASE appoggiata a y=0:
1529
+ * avvolge l'oggetto in due Group, centra l'inner sul suo bounding box (asse Y
1530
+ * ancorato al fondo) e scala l'inner a `1/maxDim`.
1531
+ * Necessario perché nel repeater/carousel modelli con scale native diverse
1532
+ * (es. Avocado ~5cm vs BoomBox ~50cm) apparirebbero enormemente disomogenei.
1533
+ * L'ancoraggio della base (non il centro) evita che, aumentando lo scale, il
1534
+ * modello affondi nel disco del carousel: cresce verso l'alto, la base resta ferma.
1535
+ */
1536
+ function normalizeAssetToUnit(obj) {
1537
+ try {
1538
+ const box = new THREE.Box3().setFromObject(obj);
1539
+ if (box.isEmpty()) {
1540
+ return obj;
1541
+ }
1542
+ const size = box.getSize(new THREE.Vector3());
1543
+ const center = box.getCenter(new THREE.Vector3());
1544
+ const maxDim = Math.max(size.x, size.y, size.z) || 1;
1545
+ // Centra x/z, ancora la base (box.min.y) a y=0: lo scale del carousel cresce
1546
+ // verso l'alto senza spingere il modello sotto il piano del disco.
1547
+ obj.position.set(-center.x, -box.min.y, -center.z);
1548
+ // Nesting a DUE livelli: l'INNER porta la normalizzazione (1/maxDim), che va
1549
+ // PROTETTA — il carousel (arrangeCarousel `it.scale.setScalar`) e il layout
1550
+ // data-driven (fm.scale) fanno `scale.setScalar` sul nodo TOP e altrimenti
1551
+ // sovrascriverebbero la normalizzazione, riportando il modello alla dimensione
1552
+ // nativa (tiny/disomogeneo). L'OUTER resta a scala 1 e assorbe lo scale del
1553
+ // repeater/carousel: dimensione finale = scaleCarousel x (1/maxDim) x modello.
1554
+ const inner = new THREE.Group();
1555
+ inner.add(obj);
1556
+ inner.scale.setScalar(1 / maxDim);
1557
+ const outer = new THREE.Group();
1558
+ outer.add(inner);
1559
+ return outer;
1560
+ }
1561
+ catch {
1562
+ return obj;
1563
+ }
1564
+ }
1488
1565
  /** Direzioni di default (tutte positive = comportamento storico). */
1489
1566
  function defaultRepeaterAxisDir() { return { x: 1, y: 1, z: 1 }; }
1490
1567
  /** Config di default per un repeater appena droppato (route vuota = placeholder). */
@@ -1537,7 +1614,7 @@ function buildUploadSrc(col, route, pkName, record, rawValue, app, unwrap) {
1537
1614
  }
1538
1615
  let pk = unwrap(record ? record[pkName] : null);
1539
1616
  if (pk && typeof pk === 'object' && 'value' in pk) {
1540
- pk = pk.value;
1617
+ pk = (pk).value;
1541
1618
  }
1542
1619
  const pkStr = pk == null ? '' : String(pk);
1543
1620
  if (col.isDBUpload) {
@@ -1634,7 +1711,7 @@ function remap(v, inMin, inMax, outMin, outMax) {
1634
1711
  * Rigenera le mesh figlie del repeater dai record. Pura rispetto al three:
1635
1712
  * usa solo le factory/parser passati in `deps`.
1636
1713
  */
1637
- function regenerateRepeater(group, config, rows, deps) {
1714
+ async function regenerateRepeater(group, config, rows, deps) {
1638
1715
  clearRepeaterChildren(group);
1639
1716
  if (!config.route || !Array.isArray(rows) || rows.length === 0) {
1640
1717
  if (!config.route) {
@@ -1689,21 +1766,67 @@ function regenerateRepeater(group, config, rows, deps) {
1689
1766
  // il loop da arrangeCarousel/buildCarouselDecor, non dal layout per-item.
1690
1767
  const isCarousel = config.layout?.mode === 'carousel';
1691
1768
  const created = [];
1769
+ // Mesh ASSET per-record: pre-carica (dedup + cache) gli asset distinti PRIMA del
1770
+ // loop sincrono, poi ogni record clona il template. Fallback a primitiva se il
1771
+ // load fallisce o la colonna e' vuota. (Il loop resta sync: gli await stanno qui.)
1772
+ const assetCol = (config.meshSource?.mode === 'asset' && config.meshSource.column) ? config.meshSource.column : '';
1773
+ const assetTemplates = new Map();
1774
+ if (assetCol && deps.loadAsset && deps.resolveAssetSrc) {
1775
+ const urls = new Set();
1776
+ for (const record of limited) {
1777
+ const raw = String(deps.unwrap(record[assetCol]) ?? '').trim();
1778
+ if (!raw) {
1779
+ continue;
1780
+ }
1781
+ const url = deps.resolveAssetSrc(record, raw);
1782
+ if (url) {
1783
+ urls.add(url);
1784
+ }
1785
+ }
1786
+ await Promise.all([...urls].map(async (u) => {
1787
+ try {
1788
+ const tpl = await deps.loadAsset(u);
1789
+ if (tpl) {
1790
+ assetTemplates.set(u, tpl);
1791
+ }
1792
+ }
1793
+ catch { /* skip: fallback primitiva */ }
1794
+ }));
1795
+ }
1692
1796
  let generated = 0;
1693
1797
  limited.forEach((record, i) => {
1694
- // Tipo mesh: fisso oppure dalla colonna del record (fallback box).
1695
- let type = 'box';
1696
- if (config.meshSource?.mode === 'column' && config.meshSource.column) {
1697
- const raw = String(deps.unwrap(record[config.meshSource.column]) ?? '').trim();
1698
- type = raw || 'box';
1699
- }
1700
- else if (config.meshSource?.type) {
1701
- type = config.meshSource.type;
1702
- }
1703
- // Testo 3D: contenuto dalla colonna `text` del record (canvas -> plane).
1704
- const mesh = (type === 'text3d' && deps.createTextMesh)
1705
- ? deps.createTextMesh(String(deps.unwrap(record[fm.text || '']) ?? ''))
1706
- : (deps.createMesh(type) || deps.createMesh('box'));
1798
+ let mesh;
1799
+ if (assetCol && deps.resolveAssetSrc) {
1800
+ // Mesh dall'asset della colonna (clone del template pre-caricato).
1801
+ const raw = String(deps.unwrap(record[assetCol]) ?? '').trim();
1802
+ const url = raw ? deps.resolveAssetSrc(record, raw) : '';
1803
+ const tpl = url ? assetTemplates.get(url) : undefined;
1804
+ mesh = tpl ? tpl.clone(true) : (deps.createMesh('box') || undefined);
1805
+ // Texture per-record su asset: clona i material del clone per non "sporcare"
1806
+ // gli altri (i clone condividono i material del template).
1807
+ if (mesh && fm.texture) {
1808
+ mesh.traverse((o) => {
1809
+ if (o.isMesh && o.material) {
1810
+ o.material = Array.isArray(o.material) ? o.material.map((m) => m.clone()) : o.material.clone();
1811
+ }
1812
+ });
1813
+ }
1814
+ }
1815
+ else {
1816
+ // Tipo mesh: fisso oppure dalla colonna del record (fallback box).
1817
+ let type = 'box';
1818
+ if (config.meshSource?.mode === 'column' && config.meshSource.column) {
1819
+ const raw = String(deps.unwrap(record[config.meshSource.column]) ?? '').trim();
1820
+ type = raw || 'box';
1821
+ }
1822
+ else if (config.meshSource?.type) {
1823
+ type = config.meshSource.type;
1824
+ }
1825
+ // Testo 3D: contenuto dalla colonna `text` del record (canvas -> plane).
1826
+ mesh = (type === 'text3d' && deps.createTextMesh)
1827
+ ? deps.createTextMesh(String(deps.unwrap(record[fm.text || '']) ?? ''))
1828
+ : (deps.createMesh(type) || deps.createMesh('box') || undefined);
1829
+ }
1707
1830
  if (!mesh) {
1708
1831
  return;
1709
1832
  }
@@ -1748,6 +1871,14 @@ function regenerateRepeater(group, config, rows, deps) {
1748
1871
  mesh.name = WUIC_REPEATER_CHILD;
1749
1872
  mesh.userData[WUIC_TRANSIENT_FLAG] = true;
1750
1873
  mesh.userData[WUIC_BINDING_KEY] = { route: config.route, recordId: deps.resolveRecordId(record) };
1874
+ // Record unwrappato sull'item, per i callback di interazione (ctx.record).
1875
+ {
1876
+ const plain = {};
1877
+ for (const k of Object.keys(record || {})) {
1878
+ plain[k] = deps.unwrap(record[k]);
1879
+ }
1880
+ mesh.userData[WUIC_REPEATER_RECORD] = plain;
1881
+ }
1751
1882
  deps.applyShadow(mesh);
1752
1883
  group.add(mesh);
1753
1884
  created.push(mesh);
@@ -1770,7 +1901,13 @@ function axisVec(axes, vals, dir) {
1770
1901
  const v = new THREE.Vector3(0, 0, 0);
1771
1902
  for (let i = 0; i < axes.length && i < vals.length; i++) {
1772
1903
  const a = axes[i];
1773
- const s = (a === 'x' ? dir?.x : a === 'y' ? dir?.y : dir?.z);
1904
+ let s = dir?.z;
1905
+ if (a === 'x') {
1906
+ s = dir?.x;
1907
+ }
1908
+ else if (a === 'y') {
1909
+ s = dir?.y;
1910
+ }
1774
1911
  const val = vals[i] * (s === -1 ? -1 : 1);
1775
1912
  if (a === 'x') {
1776
1913
  v.x = val;
@@ -1875,9 +2012,16 @@ function applyRepeaterTextureTo(mesh, value, opts, cache, loadTexture, onReady)
1875
2012
  if (!matAny || !('map' in matAny)) {
1876
2013
  return;
1877
2014
  }
1878
- const kind = opts.sourceKind === 'text' ? 'text'
1879
- : opts.sourceKind === 'image' ? 'image'
1880
- : (looksLikeImageRef(value) ? 'image' : 'text');
2015
+ let kind;
2016
+ if (opts.sourceKind === 'text') {
2017
+ kind = 'text';
2018
+ }
2019
+ else if (opts.sourceKind === 'image') {
2020
+ kind = 'image';
2021
+ }
2022
+ else {
2023
+ kind = looksLikeImageRef(value) ? 'image' : 'text';
2024
+ }
1881
2025
  const key = `${kind}|${value}|${opts.textBg}|${opts.textFg}|${opts.wrap}|${opts.repeatX}|${opts.repeatY}`;
1882
2026
  let tex = cache.get(key);
1883
2027
  if (!tex) {
@@ -1931,6 +2075,442 @@ function makeTextSpriteMesh(text) {
1931
2075
  }
1932
2076
  }
1933
2077
 
2078
+ /**
2079
+ * Helper per i callback di interazione del Mesh Repeater (click/dblclick/rightclick).
2080
+ *
2081
+ * - `runRepeaterCallback(js, ctx)`: esegue il corpo JS dell'app-builder con `ctx`
2082
+ * (record, event, mesh, scene, camera, THREE, wuic). Sandbox = `new Function`:
2083
+ * e' codice dell'admin che costruisce l'app (stesso modello di
2084
+ * validation/suggest callback), non input utente.
2085
+ * - `showItemPopup(opts, deps)`: mostra un popup "campi a sinistra + mesh a destra".
2086
+ * `opts.mode`: 1|'overlay' = overlay HTML con preview 3D live; 2|'panel3d' =
2087
+ * pannello 3D dentro la scena (billboard verso la camera). `opts.fields`: nomi
2088
+ * dei campi del record da mostrare (default: primi campi). `opts.width`/`opts.height`:
2089
+ * dimensione popup (number=px, string='50%'|'420px'; solo overlay). `opts.panelBg`:
2090
+ * colore sfondo pannello. `opts.swap`: inverte i lati (mesh sx, campi dx).
2091
+ * `opts.maximizable`: pulsante "ingrandisci" oltre alla chiusura (solo overlay).
2092
+ * `opts.verticalPosition` ('top'|'center'|'bottom') / `opts.horizontalPosition`
2093
+ * ('left'|'center'|'right'): ancoraggio del popup nel viewport (solo overlay).
2094
+ * `opts.autorotateMesh`: false = rotazione col mouse invece dell'auto-rotazione
2095
+ * dell'anteprima (solo overlay).
2096
+ */
2097
+ // ── Stato singleton (un popup alla volta) ────────────────────────────────
2098
+ let overlayEl = null;
2099
+ let overlayRAF = 0;
2100
+ let overlayRenderer = null;
2101
+ let panel3d = null;
2102
+ let panelDeps = null;
2103
+ /** Esegue il callback JS con `ctx`. Errori loggati, mai propagati (non rompe il render). */
2104
+ function runRepeaterCallback(js, ctx) {
2105
+ const src = (js || '').trim();
2106
+ if (!src) {
2107
+ return;
2108
+ }
2109
+ try {
2110
+ // eslint-disable-next-line no-new-func
2111
+ const fn = new Function('ctx', 'record', 'wuic', 'THREE', src);
2112
+ fn(ctx, ctx.record, ctx.wuic, THREE);
2113
+ }
2114
+ catch (e) {
2115
+ console.error('[Scene3D] callback repeater fallito', e);
2116
+ }
2117
+ }
2118
+ /** Chiude qualunque popup aperto (overlay + panel3d). */
2119
+ function closeItemPopup() {
2120
+ if (overlayRAF) {
2121
+ cancelAnimationFrame(overlayRAF);
2122
+ overlayRAF = 0;
2123
+ }
2124
+ if (overlayRenderer) {
2125
+ try {
2126
+ overlayRenderer.dispose();
2127
+ }
2128
+ catch { /* noop */ }
2129
+ overlayRenderer = null;
2130
+ }
2131
+ if (overlayEl?.parentElement) {
2132
+ overlayEl.parentElement.removeChild(overlayEl);
2133
+ }
2134
+ overlayEl = null;
2135
+ if (panel3d?.parent) {
2136
+ panel3d.parent.remove(panel3d);
2137
+ }
2138
+ if (panel3d) {
2139
+ disposeObject(panel3d);
2140
+ }
2141
+ panel3d = null;
2142
+ if (panelDeps?.onFrame) {
2143
+ panelDeps.onFrame(null);
2144
+ }
2145
+ panelDeps = null;
2146
+ }
2147
+ /** Dispatcher per modalita'. */
2148
+ function showItemPopup(opts, deps) {
2149
+ closeItemPopup();
2150
+ const mode = (opts.mode === 2 || opts.mode === 'panel3d') ? 'panel3d' : 'overlay';
2151
+ if (mode === 'panel3d') {
2152
+ showPanel3d(opts, deps);
2153
+ }
2154
+ else {
2155
+ showOverlay(opts, deps);
2156
+ }
2157
+ }
2158
+ // ── util: righe campi (label:value) dal record ───────────────────────────
2159
+ function fieldRows(opts) {
2160
+ const rec = opts.record || {};
2161
+ const names = (opts.fields?.length) ? opts.fields : Object.keys(rec).slice(0, 8);
2162
+ return names.map((n) => ({ label: String(n), value: fmt(rec[n]) }));
2163
+ }
2164
+ function fmt(v) {
2165
+ if (v == null) {
2166
+ return '';
2167
+ }
2168
+ if (typeof v === 'object' && 'value' in v) {
2169
+ v = (v).value;
2170
+ }
2171
+ if (typeof v === 'number') {
2172
+ return String(v);
2173
+ }
2174
+ return String(v);
2175
+ }
2176
+ /** Dimensione CSS: number → px, string → as-is ('50%', '420px'). */
2177
+ function dim(v) {
2178
+ return typeof v === 'number' ? `${v}px` : String(v);
2179
+ }
2180
+ /**
2181
+ * Rotazione manuale di `target` trascinando il mouse su `host` (orbit-like, come
2182
+ * nel designer): drag orizzontale → yaw, verticale → pitch (clampato a ±90°).
2183
+ * I listener globali vivono solo durante il drag; il `pointerdown` resta su
2184
+ * `host`, che viene rimosso dal DOM alla chiusura del popup (nessun leak).
2185
+ */
2186
+ function enableDragRotate(host, target) {
2187
+ host.style.cursor = 'grab';
2188
+ let dragging = false, px = 0, py = 0;
2189
+ const onMove = (e) => {
2190
+ if (!dragging) {
2191
+ return;
2192
+ }
2193
+ const dx = e.clientX - px, dy = e.clientY - py;
2194
+ px = e.clientX;
2195
+ py = e.clientY;
2196
+ target.rotation.y += dx * 0.01;
2197
+ target.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, target.rotation.x + dy * 0.01));
2198
+ };
2199
+ const onUp = () => {
2200
+ dragging = false;
2201
+ host.style.cursor = 'grab';
2202
+ globalThis.removeEventListener('pointermove', onMove);
2203
+ globalThis.removeEventListener('pointerup', onUp);
2204
+ };
2205
+ host.addEventListener('pointerdown', (e) => {
2206
+ dragging = true;
2207
+ px = e.clientX;
2208
+ py = e.clientY;
2209
+ host.style.cursor = 'grabbing';
2210
+ globalThis.addEventListener('pointermove', onMove);
2211
+ globalThis.addEventListener('pointerup', onUp);
2212
+ e.preventDefault();
2213
+ });
2214
+ }
2215
+ /**
2216
+ * Zoom col mouse-wheel sull'anteprima: dolly della camera lungo il suo asse
2217
+ * verso l'origine (dove sta il mesh), con distanza clampata. `passive:false` +
2218
+ * preventDefault per non scrollare la pagina sotto.
2219
+ */
2220
+ function enableWheelZoom(host, camera) {
2221
+ host.addEventListener('wheel', (e) => {
2222
+ e.preventDefault();
2223
+ const factor = e.deltaY > 0 ? 1.1 : 0.9;
2224
+ const dist = Math.max(1.2, Math.min(12, camera.position.length() * factor));
2225
+ camera.position.normalize().multiplyScalar(dist);
2226
+ camera.lookAt(0, 0, 0);
2227
+ }, { passive: false });
2228
+ }
2229
+ // ── util: clone del mesh normalizzato per il preview (centra + scala a ~1.6) ──
2230
+ function fitClone(mesh) {
2231
+ const clone = mesh.clone(true);
2232
+ clone.position.set(0, 0, 0);
2233
+ clone.rotation.set(0, 0, 0);
2234
+ clone.scale.set(1, 1, 1);
2235
+ const box = new THREE.Box3().setFromObject(clone);
2236
+ if (!box.isEmpty()) {
2237
+ const size = box.getSize(new THREE.Vector3());
2238
+ const center = box.getCenter(new THREE.Vector3());
2239
+ const maxDim = Math.max(size.x, size.y, size.z) || 1;
2240
+ const wrap = new THREE.Group();
2241
+ clone.position.sub(center);
2242
+ wrap.add(clone);
2243
+ wrap.scale.setScalar(1.6 / maxDim);
2244
+ return wrap;
2245
+ }
2246
+ return clone;
2247
+ }
2248
+ // ── Modalita' 1: overlay HTML + preview 3D live ──────────────────────────
2249
+ function showOverlay(opts, deps) {
2250
+ const host = deps.canvas.parentElement || document.body;
2251
+ if (getComputedStyle(host).position === 'static') {
2252
+ host.style.position = 'relative';
2253
+ }
2254
+ const bg = opts.panelBg || 'rgba(20,24,33,0.94)';
2255
+ const el = document.createElement('div');
2256
+ el.className = 'wuic-scene3d-item-popup';
2257
+ const css = [
2258
+ 'position:absolute', 'z-index:30',
2259
+ 'display:flex', 'gap:14px', 'padding:14px 16px',
2260
+ `background:${bg}`, 'color:#e5e9f0',
2261
+ 'border:1px solid rgba(255,255,255,0.12)', 'border-radius:10px',
2262
+ 'box-shadow:0 8px 30px rgba(0,0,0,0.45)', 'backdrop-filter:blur(4px)',
2263
+ 'font:13px/1.5 system-ui,Segoe UI,sans-serif', 'box-sizing:border-box'
2264
+ ];
2265
+ // Dimensioni: number → px, string → CSS ('50%','420px'). Default: auto (max 520px).
2266
+ css.push(opts.width != null ? `width:${dim(opts.width)}` : 'max-width:520px');
2267
+ if (opts.height != null) {
2268
+ css.push(`height:${dim(opts.height)}`);
2269
+ }
2270
+ el.style.cssText = css.join(';');
2271
+ // Ancoraggio nel viewport: vertical/horizontalPosition → offset 16px + transform
2272
+ // di centratura sull'asse richiesto. `place()` e' riusato dal ripristino maximize.
2273
+ const vpos = opts.verticalPosition || 'top';
2274
+ const hpos = opts.horizontalPosition || 'right';
2275
+ const place = () => {
2276
+ el.style.top = el.style.right = el.style.bottom = el.style.left = '';
2277
+ const tx = [];
2278
+ if (hpos === 'left') {
2279
+ el.style.left = '16px';
2280
+ }
2281
+ else if (hpos === 'center') {
2282
+ el.style.left = '50%';
2283
+ tx.push('translateX(-50%)');
2284
+ }
2285
+ else {
2286
+ el.style.right = '16px';
2287
+ }
2288
+ if (vpos === 'bottom') {
2289
+ el.style.bottom = '16px';
2290
+ }
2291
+ else if (vpos === 'center') {
2292
+ el.style.top = '50%';
2293
+ tx.push('translateY(-50%)');
2294
+ }
2295
+ else {
2296
+ el.style.top = '16px';
2297
+ }
2298
+ el.style.transform = tx.join(' ');
2299
+ };
2300
+ place();
2301
+ // campi (colonna testo)
2302
+ const left = document.createElement('div');
2303
+ left.style.cssText = 'min-width:150px;max-width:280px;display:flex;flex-direction:column;gap:2px;overflow:auto';
2304
+ if (opts.title) {
2305
+ const t = document.createElement('div');
2306
+ t.textContent = opts.title;
2307
+ t.style.cssText = 'font-weight:700;font-size:14px;margin-bottom:6px;color:#9db4ff';
2308
+ left.appendChild(t);
2309
+ }
2310
+ for (const r of fieldRows(opts)) {
2311
+ const row = document.createElement('div');
2312
+ row.style.cssText = 'display:flex;justify-content:space-between;gap:12px;border-bottom:1px solid rgba(255,255,255,0.06);padding:2px 0';
2313
+ const lab = document.createElement('span');
2314
+ lab.textContent = r.label;
2315
+ lab.style.cssText = 'opacity:0.65';
2316
+ const val = document.createElement('span');
2317
+ val.textContent = r.value;
2318
+ val.style.cssText = 'font-weight:600;text-align:right;word-break:break-word';
2319
+ row.appendChild(lab);
2320
+ row.appendChild(val);
2321
+ left.appendChild(row);
2322
+ }
2323
+ // preview 3D live: riempie lo spazio (min 200px, cresce col popup/maximize).
2324
+ const right = document.createElement('div');
2325
+ right.style.cssText = 'position:relative;flex:1 1 auto;min-width:200px;min-height:200px;align-self:stretch;border-radius:8px;overflow:hidden;background:radial-gradient(circle at 50% 35%,#2a3140,#141821)';
2326
+ // controlli (maximize opzionale + chiusura), sempre in alto a destra del popup
2327
+ const controls = document.createElement('div');
2328
+ controls.style.cssText = 'position:absolute;top:6px;right:8px;z-index:2;display:flex;gap:2px';
2329
+ const btnCss = 'background:transparent;border:none;color:#e5e9f0;line-height:1;cursor:pointer;opacity:0.8;padding:0 4px';
2330
+ const close = document.createElement('button');
2331
+ close.textContent = '×';
2332
+ close.setAttribute('aria-label', 'Close');
2333
+ close.style.cssText = btnCss + ';font-size:20px';
2334
+ close.onclick = () => closeItemPopup();
2335
+ // ordine lati: default campi→mesh; swap → mesh→campi.
2336
+ if (opts.swap) {
2337
+ el.appendChild(right);
2338
+ el.appendChild(left);
2339
+ }
2340
+ else {
2341
+ el.appendChild(left);
2342
+ el.appendChild(right);
2343
+ }
2344
+ // mini renderer 3D (canvas al 100% del box: la size effettiva la da' resizePreview)
2345
+ const r = new THREE.WebGLRenderer({ antialias: true, alpha: true });
2346
+ r.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
2347
+ r.domElement.style.cssText = 'width:100%;height:100%;display:block';
2348
+ right.appendChild(r.domElement);
2349
+ overlayRenderer = r;
2350
+ const ms = new THREE.Scene();
2351
+ ms.add(new THREE.AmbientLight(0xffffff, 0.85));
2352
+ const dl = new THREE.DirectionalLight(0xffffff, 1.25);
2353
+ dl.position.set(2, 3, 2.5);
2354
+ ms.add(dl);
2355
+ const mc = new THREE.PerspectiveCamera(42, 1, 0.01, 100);
2356
+ mc.position.set(0, 0.25, 3.1);
2357
+ mc.lookAt(0, 0, 0);
2358
+ enableWheelZoom(right, mc); // zoom con la rotella (sempre attivo)
2359
+ const resizePreview = () => {
2360
+ const w = Math.max(1, right.clientWidth), h = Math.max(1, right.clientHeight);
2361
+ r.setSize(w, h, false);
2362
+ mc.aspect = w / h;
2363
+ mc.updateProjectionMatrix();
2364
+ };
2365
+ // maximize: il popup riempie l'host; il ripristino torna a size/posizione base.
2366
+ if (opts.maximizable) {
2367
+ let maximized = false;
2368
+ const maxBtn = document.createElement('button');
2369
+ maxBtn.textContent = '⤢';
2370
+ maxBtn.setAttribute('aria-label', 'Maximize');
2371
+ maxBtn.style.cssText = btnCss + ';font-size:16px';
2372
+ maxBtn.onclick = () => {
2373
+ maximized = !maximized;
2374
+ if (maximized) {
2375
+ el.style.transform = '';
2376
+ el.style.top = el.style.right = el.style.bottom = el.style.left = '8px';
2377
+ el.style.width = 'auto';
2378
+ el.style.height = 'auto';
2379
+ el.style.maxWidth = 'none';
2380
+ maxBtn.textContent = '⤡';
2381
+ }
2382
+ else {
2383
+ el.style.width = opts.width != null ? dim(opts.width) : '';
2384
+ el.style.height = opts.height != null ? dim(opts.height) : '';
2385
+ el.style.maxWidth = opts.width != null ? 'none' : '520px';
2386
+ place();
2387
+ maxBtn.textContent = '⤢';
2388
+ }
2389
+ resizePreview();
2390
+ };
2391
+ controls.appendChild(maxBtn);
2392
+ }
2393
+ controls.appendChild(close);
2394
+ el.appendChild(controls);
2395
+ host.appendChild(el);
2396
+ overlayEl = el;
2397
+ resizePreview(); // prima misura dopo il layout (clientWidth valorizzato)
2398
+ const spin = new THREE.Group();
2399
+ if (opts.mesh) {
2400
+ spin.add(fitClone(opts.mesh));
2401
+ }
2402
+ ms.add(spin);
2403
+ // Auto-rotazione (default) oppure rotazione manuale col mouse (drag) come nel designer.
2404
+ const autorotate = opts.autorotateMesh !== false;
2405
+ if (!autorotate) {
2406
+ enableDragRotate(right, spin);
2407
+ }
2408
+ const loop = () => {
2409
+ if (autorotate) {
2410
+ spin.rotation.y += 0.012;
2411
+ }
2412
+ r.render(ms, mc);
2413
+ overlayRAF = requestAnimationFrame(loop);
2414
+ };
2415
+ loop();
2416
+ }
2417
+ // ── Modalita' 2: pannello 3D nella scena (billboard verso la camera) ─────
2418
+ function showPanel3d(opts, deps) {
2419
+ const group = new THREE.Group();
2420
+ group.name = 'wuicItemPopupPanel';
2421
+ group.userData['wuicTransient'] = true;
2422
+ // pannello campi (canvas texture)
2423
+ const rows = fieldRows(opts);
2424
+ const cw = 512, ch = 512;
2425
+ const cvs = document.createElement('canvas');
2426
+ cvs.width = cw;
2427
+ cvs.height = ch;
2428
+ const g = cvs.getContext('2d');
2429
+ g.fillStyle = opts.panelBg || 'rgba(20,24,33,0.95)';
2430
+ roundRect(g, 0, 0, cw, ch, 24);
2431
+ g.fill();
2432
+ g.fillStyle = '#9db4ff';
2433
+ g.font = 'bold 34px system-ui,sans-serif';
2434
+ let y = 64;
2435
+ if (opts.title) {
2436
+ g.fillText(opts.title, 32, y);
2437
+ y += 20;
2438
+ }
2439
+ g.font = '26px system-ui,sans-serif';
2440
+ for (const rr of rows) {
2441
+ y += 48;
2442
+ g.fillStyle = 'rgba(229,233,240,0.62)';
2443
+ g.fillText(rr.label, 32, y);
2444
+ g.fillStyle = '#e5e9f0';
2445
+ g.font = 'bold 26px system-ui,sans-serif';
2446
+ g.fillText(rr.value.slice(0, 22), 32, y + 30);
2447
+ g.font = '26px system-ui,sans-serif';
2448
+ y += 30;
2449
+ if (y > ch - 60) {
2450
+ break;
2451
+ }
2452
+ }
2453
+ const tex = new THREE.CanvasTexture(cvs);
2454
+ tex.colorSpace = THREE.SRGBColorSpace;
2455
+ const panel = new THREE.Mesh(new THREE.PlaneGeometry(2.2, 2.2), new THREE.MeshBasicMaterial({ map: tex, transparent: true, depthTest: false }));
2456
+ // swap → campi a destra, mesh a sinistra (default: campi sx, mesh dx).
2457
+ const panelX = opts.swap ? 1.4 : -1.4;
2458
+ const meshX = opts.swap ? -1.3 : 1.3;
2459
+ panel.position.set(panelX, 0, 0);
2460
+ panel.renderOrder = 999;
2461
+ group.add(panel);
2462
+ // mesh sul lato opposto ai campi
2463
+ if (opts.mesh) {
2464
+ const c = fitClone(opts.mesh);
2465
+ c.position.set(meshX, 0, 0);
2466
+ c.scale.multiplyScalar(1.4);
2467
+ group.add(c);
2468
+ }
2469
+ // posiziona davanti alla camera + billboard
2470
+ positionInFrontOfCamera(group, deps.camera, 6);
2471
+ deps.scene.add(group);
2472
+ panel3d = group;
2473
+ panelDeps = deps;
2474
+ if (deps.onFrame) {
2475
+ deps.onFrame(() => { group.quaternion.copy(deps.camera.quaternion); });
2476
+ }
2477
+ }
2478
+ function positionInFrontOfCamera(obj, camera, dist) {
2479
+ const dir = new THREE.Vector3();
2480
+ camera.getWorldDirection(dir);
2481
+ const pos = new THREE.Vector3();
2482
+ camera.getWorldPosition(pos);
2483
+ obj.position.copy(pos).add(dir.multiplyScalar(dist));
2484
+ obj.quaternion.copy(camera.quaternion);
2485
+ }
2486
+ function roundRect(g, x, y, w, h, r) {
2487
+ g.beginPath();
2488
+ g.moveTo(x + r, y);
2489
+ g.arcTo(x + w, y, x + w, y + h, r);
2490
+ g.arcTo(x + w, y + h, x, y + h, r);
2491
+ g.arcTo(x, y + h, x, y, r);
2492
+ g.arcTo(x, y, x + w, y, r);
2493
+ g.closePath();
2494
+ }
2495
+ function disposeObject(obj) {
2496
+ obj.traverse((o) => {
2497
+ if (o.geometry) {
2498
+ try {
2499
+ o.geometry.dispose();
2500
+ }
2501
+ catch { /* noop */ }
2502
+ }
2503
+ const m = o.material;
2504
+ if (m) {
2505
+ (Array.isArray(m) ? m : [m]).forEach((mm) => { try {
2506
+ mm.map?.dispose?.();
2507
+ mm.dispose?.();
2508
+ }
2509
+ catch { /* noop */ } });
2510
+ }
2511
+ });
2512
+ }
2513
+
1934
2514
  /**
1935
2515
  * Factory del renderer di scena — WebGL (default, percorso storico) o WebGPU
1936
2516
  * (opt-in, iter WebGPU/node-materials).
@@ -2196,8 +2776,8 @@ function buildEffectNodeMaterial(effect, webgpu) {
2196
2776
  material.opacityNode = float(opacity);
2197
2777
  material.transparent = true;
2198
2778
  }
2199
- material.wuicShaderEffect = true;
2200
- material.wuicShaderEffectNode = true;
2779
+ (material).wuicShaderEffect = true;
2780
+ (material).wuicShaderEffectNode = true;
2201
2781
  return material;
2202
2782
  }
2203
2783
  /**
@@ -2223,5 +2803,5 @@ function swapNodeEffectMaterialsForSerialization(root) {
2223
2803
  };
2224
2804
  }
2225
2805
 
2226
- export { defaultRepeaterTextureApply as A, regenerateRepeater as B, CarouselController as C, getPhysicsDef as D, createPhysicsWorld as E, WUIC_PHYSICS_KEY as F, WUIC_OBJECT_TYPE_KEY as G, carouselArrowDir as H, WUIC_BINDING_KEY as I, swapNodeEffectMaterialsForSerialization as J, serializeScene as K, deserializeScene as L, SCENE3D_SHADER_EFFECT_DEFAULT as S, WUIC_TRANSIENT_FLAG as W, defaultRepeaterAxisDir as a, defaultCarouselConfig as b, defaultChartConfig as c, defaultMeshRepeaterConfig as d, defaultSurfaceConfig as e, defaultGaugeConfig as f, defaultGraphConfig as g, defaultMapConfig as h, isWebGpuSupported as i, defaultWarehouseConfig as j, defaultKanbanConfig as k, defaultCalendarConfig as l, defaultStackedConfig as m, createScene3dRenderer as n, WUIC_SHADER_EFFECT_KEY as o, makeTextSpriteMesh as p, WUIC_MESH_REPEATER_KEY as q, addRepeaterPlaceholder as r, parseShaderEffect as s, SCENE3D_SHADER_EFFECT_SCHEMA as t, buildEffectNodeMaterial as u, buildEffectShaderMaterial as v, getMeshRepeaterConfig as w, WUIC_REPEATER_CHILD as x, buildUploadTextureResolver as y, applyRepeaterTextureTo as z };
2227
- //# sourceMappingURL=wuic-framework-lib-scene3d-shader-effect-CQLWkuh1.mjs.map
2806
+ export { buildUploadTextureResolver as A, applyRepeaterTextureTo as B, CarouselController as C, defaultRepeaterTextureApply as D, regenerateRepeater as E, getPhysicsDef as F, createPhysicsWorld as G, WUIC_PHYSICS_KEY as H, WUIC_OBJECT_TYPE_KEY as I, carouselArrowDir as J, WUIC_REPEATER_RECORD as K, runRepeaterCallback as L, closeItemPopup as M, showItemPopup as N, WUIC_BINDING_KEY as O, swapNodeEffectMaterialsForSerialization as P, serializeScene as Q, deserializeScene as R, SCENE3D_SHADER_EFFECT_DEFAULT as S, WUIC_TRANSIENT_FLAG as W, defaultRepeaterAxisDir as a, defaultCarouselConfig as b, defaultChartConfig as c, defaultMeshRepeaterConfig as d, defaultSurfaceConfig as e, defaultGaugeConfig as f, defaultGraphConfig as g, defaultMapConfig as h, isWebGpuSupported as i, defaultWarehouseConfig as j, defaultKanbanConfig as k, defaultCalendarConfig as l, defaultStackedConfig as m, createScene3dRenderer as n, WUIC_SHADER_EFFECT_KEY as o, makeTextSpriteMesh as p, WUIC_MESH_REPEATER_KEY as q, addRepeaterPlaceholder as r, resolveAssetFormat as s, parseShaderEffect as t, SCENE3D_SHADER_EFFECT_SCHEMA as u, buildEffectNodeMaterial as v, buildEffectShaderMaterial as w, getMeshRepeaterConfig as x, WUIC_REPEATER_CHILD as y, normalizeAssetToUnit as z };
2807
+ //# sourceMappingURL=wuic-framework-lib-scene3d-shader-effect-DjHyCAFK.mjs.map