@treasuryspatial/viewer-react 0.1.44 → 0.1.48

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 (59) hide show
  1. package/dist/AccessAssetsMenu.d.ts.map +1 -1
  2. package/dist/AccessAssetsMenu.js +53 -45
  3. package/dist/AssetUploadCard.d.ts +2 -1
  4. package/dist/AssetUploadCard.d.ts.map +1 -1
  5. package/dist/AssetUploadCard.js +3 -2
  6. package/dist/ViewControls.d.ts +36 -2
  7. package/dist/ViewControls.d.ts.map +1 -1
  8. package/dist/ViewControls.js +154 -45
  9. package/dist/ViewerCanvas.d.ts.map +1 -1
  10. package/dist/ViewerCanvas.js +15 -40
  11. package/dist/composerRouting.d.ts +6 -0
  12. package/dist/composerRouting.d.ts.map +1 -1
  13. package/dist/composerRouting.js +18 -9
  14. package/dist/index.d.ts +13 -6
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -4
  17. package/dist/tsconfig.tsbuildinfo +1 -1
  18. package/dist/useAdminGallery.d.ts +2 -30
  19. package/dist/useAdminGallery.d.ts.map +1 -1
  20. package/dist/useAdminGallery.js +15 -6
  21. package/dist/useAdminImageStream.d.ts +3 -31
  22. package/dist/useAdminImageStream.d.ts.map +1 -1
  23. package/dist/useAdminImageStream.js +22 -10
  24. package/dist/useComposerAccessGate.js +4 -4
  25. package/dist/useComposerAuthState.d.ts +1 -0
  26. package/dist/useComposerAuthState.d.ts.map +1 -1
  27. package/dist/useComposerAuthState.js +4 -1
  28. package/dist/useComposerModeDraftState.d.ts +21 -0
  29. package/dist/useComposerModeDraftState.d.ts.map +1 -0
  30. package/dist/useComposerModeDraftState.js +89 -0
  31. package/dist/useComposerUiState.d.ts +11 -4
  32. package/dist/useComposerUiState.d.ts.map +1 -1
  33. package/dist/useComposerUiState.js +118 -80
  34. package/dist/useComposerWorkspaceSession.d.ts +21 -0
  35. package/dist/useComposerWorkspaceSession.d.ts.map +1 -0
  36. package/dist/useComposerWorkspaceSession.js +291 -0
  37. package/dist/useGrasshopperSession.d.ts.map +1 -1
  38. package/dist/useGrasshopperSession.js +10 -4
  39. package/dist/useModeShellState.d.ts.map +1 -1
  40. package/dist/useModeShellState.js +52 -6
  41. package/dist/usePromptPackSelection.d.ts +20 -0
  42. package/dist/usePromptPackSelection.d.ts.map +1 -0
  43. package/dist/usePromptPackSelection.js +81 -0
  44. package/dist/useRightRailTabs.d.ts.map +1 -1
  45. package/dist/useRightRailTabs.js +4 -3
  46. package/dist/useSessionLogout.d.ts.map +1 -1
  47. package/dist/useSessionLogout.js +12 -1
  48. package/dist/useSessionRevocationPing.d.ts.map +1 -1
  49. package/dist/useSessionRevocationPing.js +15 -2
  50. package/dist/useShellPanelToggle.d.ts +7 -1
  51. package/dist/useShellPanelToggle.d.ts.map +1 -1
  52. package/dist/useShellPanelToggle.js +14 -6
  53. package/dist/workspaceSession.d.ts +36 -0
  54. package/dist/workspaceSession.d.ts.map +1 -0
  55. package/dist/workspaceSession.js +141 -0
  56. package/package.json +9 -8
  57. package/dist/usePromptPackSelectionByMode.d.ts +0 -26
  58. package/dist/usePromptPackSelectionByMode.d.ts.map +0 -1
  59. package/dist/usePromptPackSelectionByMode.js +0 -111
@@ -1 +1 @@
1
- {"version":3,"file":"AccessAssetsMenu.d.ts","sourceRoot":"","sources":["../src/AccessAssetsMenu.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,KAAuB,EACvB,KAAK,EACL,QAAgB,EAChB,SAAS,EACT,SAAiB,GAClB,EAAE,qBAAqB,kDA8IvB"}
1
+ {"version":3,"file":"AccessAssetsMenu.d.ts","sourceRoot":"","sources":["../src/AccessAssetsMenu.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,KAAuB,EACvB,KAAK,EACL,QAAgB,EAChB,SAAS,EACT,SAAiB,GAClB,EAAE,qBAAqB,kDAkKvB"}
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
3
3
  export default function AccessAssetsMenu({ label = "access assets", items, disabled = false, className, fullWidth = false, }) {
4
4
  const containerRef = useRef(null);
5
5
  const [open, setOpen] = useState(false);
6
+ const [openGroups, setOpenGroups] = useState({});
6
7
  const resolvedItems = useMemo(() => items.filter((item) => item &&
7
8
  item.label &&
8
9
  (typeof item.onClick === "function" || (Array.isArray(item.actions) && item.actions.length > 0))), [items]);
@@ -21,6 +22,14 @@ export default function AccessAssetsMenu({ label = "access assets", items, disab
21
22
  }, [open]);
22
23
  if (!resolvedItems.length)
23
24
  return null;
25
+ const runAction = async (action) => {
26
+ try {
27
+ await action.onClick?.();
28
+ }
29
+ finally {
30
+ setOpen(false);
31
+ }
32
+ };
24
33
  return (_jsxs("div", { ref: containerRef, className: className, style: { width: "100%", maxWidth: fullWidth ? "none" : "220px" }, children: [_jsxs("button", { type: "button", onClick: () => setOpen((prev) => !prev), disabled: disabled, style: {
25
34
  width: "100%",
26
35
  borderRadius: "8px",
@@ -36,49 +45,48 @@ export default function AccessAssetsMenu({ label = "access assets", items, disab
36
45
  alignItems: "center",
37
46
  justifyContent: "space-between",
38
47
  gap: "8px",
39
- }, children: [_jsx("span", { children: label }), _jsx("span", { style: { fontSize: "12px", transform: open ? "rotate(180deg)" : "rotate(0deg)", transition: "transform 0.2s ease" }, children: "\u25BE" })] }), open ? (_jsx("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "8px" }, children: resolvedItems.map((item) => (item.actions && item.actions.length > 0 ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [_jsx("div", { style: {
40
- fontSize: "11px",
41
- textTransform: "uppercase",
42
- letterSpacing: "0.14em",
43
- color: "#94a3b8",
44
- padding: "0 2px",
45
- }, children: item.label }), item.actions
46
- .filter((action) => action && action.label && typeof action.onClick === "function")
47
- .map((action) => (_jsx("button", { type: "button", onClick: async () => {
48
- try {
49
- await action.onClick?.();
50
- }
51
- finally {
52
- setOpen(false);
53
- }
54
- }, disabled: disabled || item.disabled || action.disabled, style: {
55
- width: "100%",
56
- borderRadius: "8px",
57
- border: "1px dashed #e2e8f0",
58
- padding: "10px 12px",
59
- fontSize: "12px",
60
- textTransform: "lowercase",
61
- color: "#64748b",
62
- background: "transparent",
63
- cursor: disabled || item.disabled || action.disabled ? "not-allowed" : "pointer",
64
- opacity: disabled || item.disabled || action.disabled ? 0.5 : 1,
65
- }, children: action.label }, action.key)))] }, item.key)) : (_jsx("button", { type: "button", onClick: async () => {
66
- try {
67
- await item.onClick?.();
68
- }
69
- finally {
70
- setOpen(false);
71
- }
72
- }, disabled: disabled || item.disabled, style: {
73
- width: "100%",
74
- borderRadius: "8px",
75
- border: "1px dashed #e2e8f0",
76
- padding: "10px 12px",
77
- fontSize: "12px",
78
- textTransform: "lowercase",
79
- color: "#64748b",
80
- background: "transparent",
81
- cursor: disabled || item.disabled ? "not-allowed" : "pointer",
82
- opacity: disabled || item.disabled ? 0.5 : 1,
83
- }, children: item.label }, item.key)))) })) : null] }));
48
+ }, children: [_jsx("span", { children: label }), _jsx("span", { style: { fontSize: "12px", transform: open ? "rotate(180deg)" : "rotate(0deg)", transition: "transform 0.2s ease" }, children: "\u25BE" })] }), open ? (_jsx("div", { style: { marginTop: "10px", display: "flex", flexDirection: "column", gap: "8px" }, children: resolvedItems.map((item) => {
49
+ const actions = item.actions?.filter((action) => action && action.label && typeof action.onClick === "function") ?? [];
50
+ const groupOpen = openGroups[item.key] ?? false;
51
+ const itemDisabled = disabled || item.disabled;
52
+ return actions.length > 0 ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [_jsxs("button", { type: "button", onClick: () => setOpenGroups((prev) => ({ ...prev, [item.key]: !groupOpen })), disabled: itemDisabled, style: {
53
+ width: "100%",
54
+ borderRadius: "8px",
55
+ border: "1px dashed #e2e8f0",
56
+ padding: "10px 12px",
57
+ fontSize: "12px",
58
+ color: "#475569",
59
+ background: "transparent",
60
+ cursor: itemDisabled ? "not-allowed" : "pointer",
61
+ opacity: itemDisabled ? 0.5 : 1,
62
+ display: "flex",
63
+ alignItems: "center",
64
+ justifyContent: "space-between",
65
+ gap: "8px",
66
+ }, children: [_jsx("span", { children: item.label }), _jsx("span", { style: {
67
+ fontSize: "12px",
68
+ transform: groupOpen ? "rotate(180deg)" : "rotate(0deg)",
69
+ transition: "transform 0.2s ease",
70
+ }, children: "\u25BE" })] }), groupOpen ? (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "8px", paddingLeft: "12px" }, children: actions.map((action) => (_jsx("button", { type: "button", onClick: () => void runAction(action), disabled: itemDisabled || action.disabled, style: {
71
+ width: "100%",
72
+ borderRadius: "8px",
73
+ border: "1px dashed #e2e8f0",
74
+ padding: "10px 12px",
75
+ fontSize: "12px",
76
+ color: "#64748b",
77
+ background: "transparent",
78
+ cursor: itemDisabled || action.disabled ? "not-allowed" : "pointer",
79
+ opacity: itemDisabled || action.disabled ? 0.5 : 1,
80
+ }, children: action.label }, action.key))) })) : null] }, item.key)) : (_jsx("button", { type: "button", onClick: () => void runAction(item), disabled: itemDisabled, style: {
81
+ width: "100%",
82
+ borderRadius: "8px",
83
+ border: "1px dashed #e2e8f0",
84
+ padding: "10px 12px",
85
+ fontSize: "12px",
86
+ color: "#64748b",
87
+ background: "transparent",
88
+ cursor: itemDisabled ? "not-allowed" : "pointer",
89
+ opacity: itemDisabled ? 0.5 : 1,
90
+ }, children: item.label }, item.key));
91
+ }) })) : null] }));
84
92
  }
@@ -2,6 +2,7 @@ import { type UploadResult } from "@treasuryspatial/viewer-kit";
2
2
  export type AssetUploadCardProps = {
3
3
  disabled?: boolean;
4
4
  apiRoute?: string;
5
+ resolveApiRoute?: () => string;
5
6
  geometryApiRoute?: string;
6
7
  maxBytes?: number;
7
8
  timeoutMs?: number;
@@ -15,5 +16,5 @@ export type AssetUploadCardProps = {
15
16
  description?: string;
16
17
  minimal?: boolean;
17
18
  };
18
- export default function AssetUploadCard({ disabled, apiRoute, geometryApiRoute, maxBytes, timeoutMs, preview, allowEmptyGeometry, accept, onSolved, onClear, className, title, description, minimal, }: AssetUploadCardProps): import("react/jsx-runtime").JSX.Element;
19
+ export default function AssetUploadCard({ disabled, apiRoute, resolveApiRoute, geometryApiRoute, maxBytes, timeoutMs, preview, allowEmptyGeometry, accept, onSolved, onClear, className, title, description, minimal, }: AssetUploadCardProps): import("react/jsx-runtime").JSX.Element;
19
20
  //# sourceMappingURL=AssetUploadCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssetUploadCard.d.ts","sourceRoot":"","sources":["../src/AssetUploadCard.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAUF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,QAAQ,EACR,QAA0B,EAC1B,gBAAsC,EACtC,QAAyC,EACzC,SAA2C,EAC3C,OAAO,EACP,kBAAkB,EAClB,MAAuB,EACvB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,KAAsB,EACtB,WAAsF,EACtF,OAAe,GAChB,EAAE,oBAAoB,2CAgOtB"}
1
+ {"version":3,"file":"AssetUploadCard.d.ts","sourceRoot":"","sources":["../src/AssetUploadCard.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAUF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EACtC,QAAQ,EACR,QAA0B,EAC1B,eAAe,EACf,gBAAsC,EACtC,QAAyC,EACzC,SAA2C,EAC3C,OAAO,EACP,kBAAkB,EAClB,MAAuB,EACvB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,KAAsB,EACtB,WAAsF,EACtF,OAAe,GAChB,EAAE,oBAAoB,2CAiOtB"}
@@ -8,7 +8,7 @@ const formatBytes = (bytes) => {
8
8
  const mb = bytes / (1024 * 1024);
9
9
  return `${mb.toFixed(0)}MB`;
10
10
  };
11
- export default function AssetUploadCard({ disabled, apiRoute = "/api/gh-solve", geometryApiRoute = "/api/geometry-3dm", maxBytes = DEFAULT_UPLOAD_LIMITS.maxBytes, timeoutMs = DEFAULT_UPLOAD_LIMITS.timeoutMs, preview, allowEmptyGeometry, accept = DEFAULT_ACCEPT, onSolved, onClear, className, title = "Asset Upload", description = "Upload .gh/.ghx/.3dm or mesh assets (OBJ/FBX/GLTF/STL/PLY) to preview.", minimal = false, }) {
11
+ export default function AssetUploadCard({ disabled, apiRoute = "/api/gh-solve", resolveApiRoute, geometryApiRoute = "/api/geometry-3dm", maxBytes = DEFAULT_UPLOAD_LIMITS.maxBytes, timeoutMs = DEFAULT_UPLOAD_LIMITS.timeoutMs, preview, allowEmptyGeometry, accept = DEFAULT_ACCEPT, onSolved, onClear, className, title = "Asset Upload", description = "Upload .gh/.ghx/.3dm or mesh assets (OBJ/FBX/GLTF/STL/PLY) to preview.", minimal = false, }) {
12
12
  const [busy, setBusy] = useState(false);
13
13
  const [lastFileName, setLastFileName] = useState(null);
14
14
  const [error, setError] = useState(null);
@@ -40,8 +40,9 @@ export default function AssetUploadCard({ disabled, apiRoute = "/api/gh-solve",
40
40
  try {
41
41
  let result;
42
42
  if (kind === "gh" || kind === "ghx") {
43
+ const resolvedApiRoute = resolveApiRoute?.() ?? apiRoute;
43
44
  const solved = await solveGrasshopperFile(file, {
44
- apiRoute,
45
+ apiRoute: resolvedApiRoute,
45
46
  preview,
46
47
  allowEmptyGeometry,
47
48
  onStatus: (detail) => updateStatus(file.name, detail),
@@ -1,33 +1,67 @@
1
+ import React from "react";
1
2
  export type LightingOption = {
2
3
  id: string;
3
4
  label: string;
4
5
  summary?: string;
6
+ group?: string;
5
7
  };
6
8
  export type MaterialOption = {
7
9
  id: string;
8
10
  label: string;
9
11
  summary?: string;
10
12
  swatches?: string[];
13
+ group?: string;
11
14
  };
12
15
  export type SkyOption = {
13
16
  id: string;
14
17
  label: string;
15
18
  description?: string;
19
+ group?: string;
20
+ };
21
+ export type ViewControlsAction = {
22
+ id: string;
23
+ label: string;
24
+ active?: boolean;
25
+ onClick: () => void;
26
+ };
27
+ export type ViewControlsWidget = {
28
+ id: string;
29
+ content: React.ReactNode;
30
+ minWidth?: number | string;
16
31
  };
17
32
  export type ViewControlsProps = {
18
33
  activeLightingPreset: string;
19
34
  onLightingChange: (preset: string) => void;
35
+ /**
36
+ * When environment/rig options are provided, the single LIGHTING chip is
37
+ * replaced by ENVIRONMENT + RIG axis chips (lighting-preset catalog
38
+ * restructure). Consumers that omit them keep the legacy single chip.
39
+ */
40
+ environmentOptions?: LightingOption[];
41
+ activeEnvironmentId?: string;
42
+ onEnvironmentChange?: (environmentId: string) => void;
43
+ rigOptions?: LightingOption[];
44
+ activeRigId?: string;
45
+ onRigChange?: (rigId: string) => void;
20
46
  materialOptions?: MaterialOption[];
21
47
  activeMaterialPreset?: string;
22
48
  onMaterialChange?: (preset: string) => void;
23
- onCameraViewChange: (view: string) => void;
49
+ onCameraViewChange?: (view: string) => void;
24
50
  skyOptions: SkyOption[];
25
51
  activeSkyId: string;
26
52
  onSkyChange: (skyId: string) => void;
53
+ /**
54
+ * BACKDROP gate (owner final spec 2026-07-11): while an ENVIRONMENT (HDR
55
+ * photo world) owns light + sky, the BACKDROP menu is disabled — the
56
+ * generated worlds only apply once the environment is cleared to none.
57
+ */
58
+ skyDisabled?: boolean;
27
59
  lightingOptions: LightingOption[];
60
+ extraActions?: ViewControlsAction[];
61
+ extraWidgets?: ViewControlsWidget[];
28
62
  veilsEnabled?: boolean;
29
63
  onToggleVeils?: () => void;
30
64
  onResetRender?: () => void;
31
65
  };
32
- export default function ViewControls({ activeLightingPreset, onLightingChange, materialOptions, activeMaterialPreset, onMaterialChange, onCameraViewChange, skyOptions, activeSkyId, onSkyChange, lightingOptions, veilsEnabled, onToggleVeils, onResetRender, }: ViewControlsProps): import("react/jsx-runtime").JSX.Element;
66
+ export default function ViewControls({ activeLightingPreset, onLightingChange, environmentOptions, activeEnvironmentId, onEnvironmentChange, rigOptions, activeRigId, onRigChange, materialOptions, activeMaterialPreset, onMaterialChange, onCameraViewChange, skyOptions, activeSkyId, onSkyChange, skyDisabled, lightingOptions, extraActions, extraWidgets, veilsEnabled, onToggleVeils, onResetRender, }: ViewControlsProps): import("react/jsx-runtime").JSX.Element;
33
67
  //# sourceMappingURL=ViewControls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ViewControls.d.ts","sourceRoot":"","sources":["../src/ViewControls.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AA6EF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,WAAW,EACX,eAAe,EACf,YAAmB,EACnB,aAAa,EACb,aAAa,GACd,EAAE,iBAAiB,2CA8VnB"}
1
+ {"version":3,"file":"ViewControls.d.ts","sourceRoot":"","sources":["../src/ViewControls.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useRef, useState } from "react";
2
+ import React, { useEffect, useMemo, useRef, useState } from "react";
3
3
  const cardStyle = {
4
4
  position: "relative",
5
5
  background: "var(--viewer-ui-color-surface-strong, rgba(255,255,255,0.92))",
@@ -25,6 +25,7 @@ const veilStyle = {
25
25
  letterSpacing: "0.1em",
26
26
  color: "var(--viewer-ui-color-text-subtle, rgba(100,116,139,0.7))",
27
27
  };
28
+ const compactControlHeight = "32px";
28
29
  const dropdownStyle = {
29
30
  position: "absolute",
30
31
  bottom: "calc(100% + 8px)",
@@ -56,6 +57,14 @@ const menuButtonStyle = (active) => ({
56
57
  fontSize: "12px",
57
58
  textTransform: "lowercase",
58
59
  });
60
+ const menuGroupLabelStyle = {
61
+ padding: "8px 10px 4px",
62
+ fontSize: "10px",
63
+ fontWeight: 700,
64
+ letterSpacing: "0.12em",
65
+ textTransform: "uppercase",
66
+ color: "var(--viewer-ui-color-text-subtle, rgba(100,116,139,0.72))",
67
+ };
59
68
  const swatchRowStyle = {
60
69
  display: "flex",
61
70
  gap: "6px",
@@ -68,44 +77,97 @@ const swatchStyle = (color) => ({
68
77
  background: color,
69
78
  border: "1px solid var(--viewer-ui-color-panel-border, rgba(15,23,42,0.1))",
70
79
  });
71
- export default function ViewControls({ activeLightingPreset, onLightingChange, materialOptions, activeMaterialPreset, onMaterialChange, onCameraViewChange, skyOptions, activeSkyId, onSkyChange, lightingOptions, veilsEnabled = true, onToggleVeils, onResetRender, }) {
80
+ const shouldShowGroupLabel = (items, index) => {
81
+ const current = items[index]?.group;
82
+ if (!current)
83
+ return false;
84
+ if (index === 0)
85
+ return true;
86
+ return items[index - 1]?.group !== current;
87
+ };
88
+ const dropdownHeaderStyle = {
89
+ padding: "4px 10px 8px",
90
+ fontSize: "10px",
91
+ fontWeight: 700,
92
+ letterSpacing: "0.14em",
93
+ textTransform: "uppercase",
94
+ color: "var(--viewer-ui-color-text-subtle, rgba(100,116,139,0.85))",
95
+ textAlign: "left",
96
+ borderBottom: "1px solid var(--viewer-ui-color-panel-border, #e2e8f0)",
97
+ marginBottom: "4px",
98
+ };
99
+ function AxisDropdownChip({ caption, options, activeId, onSelect, expanded, menuRef, onToggle, onClose, disabled = false, disabledHint, }) {
100
+ const active = options.find((option) => option.id === activeId) ?? options[0];
101
+ return (_jsx("div", { ref: menuRef, children: _jsxs("div", { style: { ...cardStyle, opacity: disabled ? 0.45 : 1 }, children: [expanded && !disabled ? (_jsxs("div", { style: dropdownStyle, children: [_jsx("div", { style: dropdownHeaderStyle, children: caption }), options.map((option, index) => (_jsxs(React.Fragment, { children: [shouldShowGroupLabel(options, index) ? (_jsx("div", { style: menuGroupLabelStyle, children: option.group })) : null, _jsxs("button", { type: "button", onClick: () => {
102
+ onSelect(option.id);
103
+ onClose();
104
+ }, style: menuButtonStyle(option.id === activeId), children: [_jsx("span", { children: option.label }), option.summary ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.summary })) : null] })] }, option.id)))] })) : null, _jsxs("button", { type: "button", disabled: disabled, "aria-disabled": disabled, title: disabled ? disabledHint : undefined, onClick: () => {
105
+ if (disabled)
106
+ return;
107
+ onToggle();
108
+ }, style: {
109
+ display: "flex",
110
+ alignItems: "center",
111
+ justifyContent: "space-between",
112
+ gap: "10px",
113
+ padding: "8px 12px",
114
+ border: "none",
115
+ background: "transparent",
116
+ width: "100%",
117
+ minWidth: "128px",
118
+ cursor: disabled ? "not-allowed" : "pointer",
119
+ }, children: [_jsx("span", { style: labelStyle, children: active?.label ?? activeId }), _jsx("span", { style: { fontSize: "11px", color: "var(--viewer-ui-color-text-muted, #64748b)" }, children: disabled ? "" : expanded ? "▼" : "▲" })] })] }) }));
120
+ }
121
+ export default function ViewControls({ activeLightingPreset, onLightingChange, environmentOptions, activeEnvironmentId, onEnvironmentChange, rigOptions, activeRigId, onRigChange, materialOptions, activeMaterialPreset, onMaterialChange, onCameraViewChange, skyOptions, activeSkyId, onSkyChange, skyDisabled = false, lightingOptions, extraActions = [], extraWidgets = [], veilsEnabled = true, onToggleVeils, onResetRender, }) {
72
122
  const lightingMenuRef = useRef(null);
123
+ const environmentMenuRef = useRef(null);
124
+ const rigMenuRef = useRef(null);
73
125
  const materialMenuRef = useRef(null);
74
126
  const skyMenuRef = useRef(null);
75
127
  const [lightingExpanded, setLightingExpanded] = useState(false);
128
+ const [environmentExpanded, setEnvironmentExpanded] = useState(false);
129
+ const [rigExpanded, setRigExpanded] = useState(false);
76
130
  const [materialExpanded, setMaterialExpanded] = useState(false);
77
131
  const [skyExpanded, setSkyExpanded] = useState(false);
78
132
  const [lightingHover, setLightingHover] = useState(false);
79
133
  const [materialHover, setMaterialHover] = useState(false);
80
134
  const [skyHover, setSkyHover] = useState(false);
81
135
  const [viewHover, setViewHover] = useState(false);
136
+ const showAxisChips = Boolean(environmentOptions?.length && rigOptions?.length && onEnvironmentChange && onRigChange);
82
137
  const activeLighting = useMemo(() => lightingOptions.find((option) => option.id === activeLightingPreset) ?? lightingOptions[0], [lightingOptions, activeLightingPreset]);
83
138
  const activeSky = useMemo(() => skyOptions.find((option) => option.id === activeSkyId) ?? skyOptions[0], [skyOptions, activeSkyId]);
84
139
  const activeMaterial = useMemo(() => materialOptions?.find((option) => option.id === activeMaterialPreset) ??
85
140
  materialOptions?.[0], [materialOptions, activeMaterialPreset]);
86
141
  useEffect(() => {
87
- if (!lightingExpanded && !skyExpanded && !materialExpanded)
142
+ if (!lightingExpanded && !environmentExpanded && !rigExpanded && !skyExpanded && !materialExpanded)
88
143
  return;
144
+ const closeAll = () => {
145
+ setLightingExpanded(false);
146
+ setEnvironmentExpanded(false);
147
+ setRigExpanded(false);
148
+ setMaterialExpanded(false);
149
+ setSkyExpanded(false);
150
+ };
89
151
  const onMouseDown = (event) => {
90
152
  const target = event.target;
91
153
  if (!target)
92
154
  return;
93
155
  if (lightingMenuRef.current?.contains(target))
94
156
  return;
157
+ if (environmentMenuRef.current?.contains(target))
158
+ return;
159
+ if (rigMenuRef.current?.contains(target))
160
+ return;
95
161
  if (materialMenuRef.current?.contains(target))
96
162
  return;
97
163
  if (skyMenuRef.current?.contains(target))
98
164
  return;
99
- setLightingExpanded(false);
100
- setMaterialExpanded(false);
101
- setSkyExpanded(false);
165
+ closeAll();
102
166
  };
103
167
  const onKeyDown = (event) => {
104
168
  if (event.key !== "Escape")
105
169
  return;
106
- setLightingExpanded(false);
107
- setMaterialExpanded(false);
108
- setSkyExpanded(false);
170
+ closeAll();
109
171
  };
110
172
  window.addEventListener("mousedown", onMouseDown);
111
173
  window.addEventListener("keydown", onKeyDown);
@@ -113,11 +175,46 @@ export default function ViewControls({ activeLightingPreset, onLightingChange, m
113
175
  window.removeEventListener("mousedown", onMouseDown);
114
176
  window.removeEventListener("keydown", onKeyDown);
115
177
  };
116
- }, [lightingExpanded, materialExpanded, skyExpanded]);
117
- return (_jsxs("div", { style: { display: "flex", gap: "10px", alignItems: "flex-end" }, children: [_jsx("div", { ref: lightingMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setLightingHover(true), onMouseLeave: () => setLightingHover(false), children: [lightingExpanded ? (_jsx("div", { style: dropdownStyle, children: lightingOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => {
118
- onLightingChange(option.id);
119
- setLightingExpanded(false);
120
- }, style: menuButtonStyle(option.id === activeLightingPreset), children: [_jsx("span", { children: option.label }), option.summary ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.summary })) : null] }, option.id))) })) : null, _jsxs("button", { type: "button", onClick: () => {
178
+ }, [environmentExpanded, lightingExpanded, materialExpanded, rigExpanded, skyExpanded]);
179
+ return (_jsxs("div", { style: { display: "flex", gap: "10px", alignItems: "flex-end" }, children: [showAxisChips && skyOptions.length ? (_jsx(AxisDropdownChip, { caption: "BACKGROUND", options: skyOptions, activeId: activeSkyId, onSelect: (id) => onSkyChange(id), expanded: skyExpanded, menuRef: skyMenuRef, onToggle: () => {
180
+ setSkyExpanded((prev) => !prev);
181
+ setEnvironmentExpanded(false);
182
+ setRigExpanded(false);
183
+ setMaterialExpanded(false);
184
+ }, onClose: () => setSkyExpanded(false), disabled: skyDisabled, disabledHint: "environment owns the scene \u2014 set environment to none/white to use a background" })) : skyOptions.length ? (_jsx("div", { ref: skyMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setSkyHover(true), onMouseLeave: () => setSkyHover(false), children: [skyExpanded ? (_jsx("div", { style: dropdownStyle, children: skyOptions.map((option, index) => (_jsxs(React.Fragment, { children: [shouldShowGroupLabel(skyOptions, index) ? (_jsx("div", { style: menuGroupLabelStyle, children: option.group })) : null, _jsxs("button", { type: "button", onClick: () => {
185
+ onSkyChange(option.id);
186
+ setSkyExpanded(false);
187
+ }, style: menuButtonStyle(option.id === activeSkyId), children: [_jsx("span", { children: option.label }), option.description ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.description })) : null] })] }, option.id))) })) : null, _jsxs("button", { type: "button", onClick: () => {
188
+ setSkyExpanded((prev) => !prev);
189
+ setLightingExpanded(false);
190
+ }, style: {
191
+ display: "flex",
192
+ alignItems: "center",
193
+ justifyContent: "space-between",
194
+ gap: "10px",
195
+ padding: "8px 12px",
196
+ border: "none",
197
+ background: "transparent",
198
+ width: "100%",
199
+ cursor: "pointer",
200
+ }, children: [_jsx("span", { style: {
201
+ ...labelStyle,
202
+ opacity: !veilsEnabled || skyExpanded || skyHover ? 1 : 0,
203
+ transition: "opacity 0.2s ease",
204
+ }, children: activeSky?.label ?? "preset dome" }), _jsx("span", { style: {
205
+ fontSize: "11px",
206
+ color: "var(--viewer-ui-color-text-muted, #64748b)",
207
+ opacity: !veilsEnabled || skyExpanded || skyHover ? 1 : 0,
208
+ transition: "opacity 0.2s ease",
209
+ }, children: skyExpanded ? "▼" : "▲" })] }), veilsEnabled && !skyExpanded && !skyHover ? _jsx("div", { style: veilStyle, children: "BACKGROUND" }) : null] }) })) : null, showAxisChips ? (_jsx(AxisDropdownChip, { caption: "LIGHT", options: rigOptions ?? [], activeId: activeRigId, onSelect: (id) => onRigChange?.(id), expanded: rigExpanded, menuRef: rigMenuRef, onToggle: () => {
210
+ setRigExpanded((prev) => !prev);
211
+ setEnvironmentExpanded(false);
212
+ setMaterialExpanded(false);
213
+ setSkyExpanded(false);
214
+ }, onClose: () => setRigExpanded(false) })) : (_jsx("div", { ref: lightingMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setLightingHover(true), onMouseLeave: () => setLightingHover(false), children: [lightingExpanded ? (_jsx("div", { style: dropdownStyle, children: lightingOptions.map((option, index) => (_jsxs(React.Fragment, { children: [shouldShowGroupLabel(lightingOptions, index) ? (_jsx("div", { style: menuGroupLabelStyle, children: option.group })) : null, _jsxs("button", { type: "button", onClick: () => {
215
+ onLightingChange(option.id);
216
+ setLightingExpanded(false);
217
+ }, style: menuButtonStyle(option.id === activeLightingPreset), children: [_jsx("span", { children: option.label }), option.summary ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.summary })) : null] })] }, option.id))) })) : null, _jsxs("button", { type: "button", onClick: () => {
121
218
  setLightingExpanded((prev) => !prev);
122
219
  setSkyExpanded(false);
123
220
  }, style: {
@@ -139,10 +236,10 @@ export default function ViewControls({ activeLightingPreset, onLightingChange, m
139
236
  color: "var(--viewer-ui-color-text-muted, #64748b)",
140
237
  opacity: !veilsEnabled || lightingExpanded || lightingHover ? 1 : 0,
141
238
  transition: "opacity 0.2s ease",
142
- }, children: lightingExpanded ? "▼" : "▲" })] }), veilsEnabled && !lightingExpanded && !lightingHover ? _jsx("div", { style: veilStyle, children: "LIGHTING" }) : null] }) }), materialOptions && materialOptions.length && onMaterialChange ? (_jsx("div", { ref: materialMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setMaterialHover(true), onMouseLeave: () => setMaterialHover(false), children: [materialExpanded ? (_jsx("div", { style: dropdownStyle, children: materialOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => {
143
- onMaterialChange(option.id);
144
- setMaterialExpanded(false);
145
- }, style: menuButtonStyle(option.id === activeMaterialPreset), children: [_jsx("span", { children: option.label }), option.summary ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.summary })) : null, option.swatches && option.swatches.length ? (_jsx("div", { style: swatchRowStyle, children: option.swatches.map((color, index) => (_jsx("span", { style: swatchStyle(color) }, `${option.id}-${index}`))) })) : null] }, option.id))) })) : null, _jsxs("button", { type: "button", onClick: () => {
239
+ }, children: lightingExpanded ? "▼" : "▲" })] }), veilsEnabled && !lightingExpanded && !lightingHover ? _jsx("div", { style: veilStyle, children: "LIGHTING" }) : null] }) })), materialOptions && materialOptions.length && onMaterialChange ? (_jsx("div", { ref: materialMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setMaterialHover(true), onMouseLeave: () => setMaterialHover(false), children: [materialExpanded ? (_jsxs("div", { style: dropdownStyle, children: [_jsx("div", { style: dropdownHeaderStyle, children: "SURFACES" }), materialOptions.map((option, index) => (_jsxs(React.Fragment, { children: [shouldShowGroupLabel(materialOptions, index) ? (_jsx("div", { style: menuGroupLabelStyle, children: option.group })) : null, _jsxs("button", { type: "button", onClick: () => {
240
+ onMaterialChange(option.id);
241
+ setMaterialExpanded(false);
242
+ }, style: menuButtonStyle(option.id === activeMaterialPreset), children: [_jsx("span", { children: option.label }), option.summary ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.summary })) : null, option.swatches && option.swatches.length ? (_jsx("div", { style: swatchRowStyle, children: option.swatches.map((color, swatchIndex) => (_jsx("span", { style: swatchStyle(color) }, `${option.id}-${swatchIndex}`))) })) : null] })] }, option.id)))] })) : null, _jsxs("button", { type: "button", onClick: () => {
146
243
  setMaterialExpanded((prev) => !prev);
147
244
  setLightingExpanded(false);
148
245
  setSkyExpanded(false);
@@ -165,7 +262,12 @@ export default function ViewControls({ activeLightingPreset, onLightingChange, m
165
262
  color: "var(--viewer-ui-color-text-muted, #64748b)",
166
263
  opacity: !veilsEnabled || materialExpanded || materialHover ? 1 : 0,
167
264
  transition: "opacity 0.2s ease",
168
- }, children: materialExpanded ? "▼" : "▲" })] }), veilsEnabled && !materialExpanded && !materialHover ? _jsx("div", { style: veilStyle, children: "MATERIALS" }) : null] }) })) : null, _jsxs("div", { style: { ...cardStyle, display: "flex" }, onMouseEnter: () => setViewHover(true), onMouseLeave: () => setViewHover(false), children: [_jsx("button", { type: "button", onClick: () => onCameraViewChange("interior"), style: {
265
+ }, children: materialExpanded ? "▼" : "▲" })] }), veilsEnabled && !materialExpanded && !materialHover ? _jsx("div", { style: veilStyle, children: "SURFACES" }) : null] }) })) : null, showAxisChips ? (_jsx(AxisDropdownChip, { caption: "ENVIRONMENT", options: environmentOptions ?? [], activeId: activeEnvironmentId, onSelect: (id) => onEnvironmentChange?.(id), expanded: environmentExpanded, menuRef: environmentMenuRef, onToggle: () => {
266
+ setEnvironmentExpanded((prev) => !prev);
267
+ setRigExpanded(false);
268
+ setMaterialExpanded(false);
269
+ setSkyExpanded(false);
270
+ }, onClose: () => setEnvironmentExpanded(false) })) : null, onCameraViewChange ? (_jsxs("div", { style: { ...cardStyle, display: "flex" }, onMouseEnter: () => setViewHover(true), onMouseLeave: () => setViewHover(false), children: [_jsx("button", { type: "button", onClick: () => onCameraViewChange("interior"), style: {
169
271
  padding: "8px 12px",
170
272
  border: "none",
171
273
  borderRight: "1px solid var(--viewer-ui-color-panel-border, #e2e8f0)",
@@ -182,32 +284,39 @@ export default function ViewControls({ activeLightingPreset, onLightingChange, m
182
284
  minWidth: "84px",
183
285
  ...labelStyle,
184
286
  color: !veilsEnabled || viewHover ? "var(--viewer-ui-color-text-muted, #475569)" : "transparent",
185
- }, children: "perspective" }), veilsEnabled && !viewHover ? _jsx("div", { style: veilStyle, children: "VIEW" }) : null] }), skyOptions.length ? (_jsx("div", { ref: skyMenuRef, children: _jsxs("div", { style: cardStyle, onMouseEnter: () => setSkyHover(true), onMouseLeave: () => setSkyHover(false), children: [skyExpanded ? (_jsx("div", { style: dropdownStyle, children: skyOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => {
186
- onSkyChange(option.id);
187
- setSkyExpanded(false);
188
- }, style: menuButtonStyle(option.id === activeSkyId), children: [_jsx("span", { children: option.label }), option.description ? (_jsx("span", { style: { fontSize: "10px", opacity: 0.7 }, children: option.description })) : null] }, option.id))) })) : null, _jsxs("button", { type: "button", onClick: () => {
189
- setSkyExpanded((prev) => !prev);
190
- setLightingExpanded(false);
191
- }, style: {
192
- display: "flex",
193
- alignItems: "center",
194
- justifyContent: "space-between",
195
- gap: "10px",
196
- padding: "8px 12px",
197
- border: "none",
198
- background: "transparent",
199
- width: "100%",
200
- cursor: "pointer",
201
- }, children: [_jsx("span", { style: {
202
- ...labelStyle,
203
- opacity: !veilsEnabled || skyExpanded || skyHover ? 1 : 0,
204
- transition: "opacity 0.2s ease",
205
- }, children: activeSky?.label ?? "preset dome" }), _jsx("span", { style: {
206
- fontSize: "11px",
207
- color: "var(--viewer-ui-color-text-muted, #64748b)",
208
- opacity: !veilsEnabled || skyExpanded || skyHover ? 1 : 0,
209
- transition: "opacity 0.2s ease",
210
- }, children: skyExpanded ? "▼" : "▲" })] }), veilsEnabled && !skyExpanded && !skyHover ? _jsx("div", { style: veilStyle, children: "BACKGROUND" }) : null] }) })) : null, onToggleVeils ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px", alignSelf: "stretch" }, children: [onResetRender ? (_jsx("button", { type: "button", onClick: onResetRender, "aria-label": "Reset render settings", style: {
287
+ }, children: "perspective" }), veilsEnabled && !viewHover ? _jsx("div", { style: veilStyle, children: "VIEW" }) : null] })) : null, extraActions.map((action) => (_jsx("div", { children: _jsx("div", { style: {
288
+ ...cardStyle,
289
+ border: action.active ? "1px solid transparent" : cardStyle.border,
290
+ }, children: _jsx("button", { type: "button", onClick: action.onClick, style: {
291
+ display: "flex",
292
+ alignItems: "center",
293
+ justifyContent: "space-between",
294
+ gap: "10px",
295
+ padding: "8px 12px",
296
+ minHeight: compactControlHeight,
297
+ border: "none",
298
+ background: action.active
299
+ ? "var(--viewer-ui-color-button-primary, var(--brand-primary, #318f4e))"
300
+ : "transparent",
301
+ borderRadius: "11px",
302
+ width: "100%",
303
+ cursor: "pointer",
304
+ }, children: _jsx("span", { style: {
305
+ ...labelStyle,
306
+ opacity: 1,
307
+ color: action.active
308
+ ? "var(--viewer-ui-color-button-primary-text, #ffffff)"
309
+ : labelStyle.color,
310
+ }, children: action.label }) }) }) }, action.id))), extraWidgets.map((widget) => (_jsx("div", { children: _jsx("div", { style: {
311
+ ...cardStyle,
312
+ minWidth: widget.minWidth ?? 280,
313
+ minHeight: compactControlHeight,
314
+ display: "flex",
315
+ alignItems: "stretch",
316
+ position: "relative",
317
+ zIndex: 1,
318
+ pointerEvents: "auto",
319
+ }, children: widget.content }) }, widget.id))), onToggleVeils ? (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px", alignSelf: "stretch" }, children: [onResetRender ? (_jsx("button", { type: "button", onClick: onResetRender, "aria-label": "Reset render settings", style: {
211
320
  border: "none",
212
321
  background: "transparent",
213
322
  fontSize: "11px",
@@ -1 +1 @@
1
- {"version":3,"file":"ViewerCanvas.d.ts","sourceRoot":"","sources":["../src/ViewerCanvas.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEpG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,aAAa,CAAC,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC3C,iBAAiB,CAAC,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,QAAQ,EACR,OAAO,EACP,aAAa,EACb,MAAM,EACN,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,2CAqInB"}
1
+ {"version":3,"file":"ViewerCanvas.d.ts","sourceRoot":"","sources":["../src/ViewerCanvas.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEpG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,aAAa,CAAC,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC3C,iBAAiB,CAAC,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;CACjD,CAAC;AASF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,QAAQ,EACR,OAAO,EACP,aAAa,EACb,MAAM,EACN,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,2CA4GnB"}