@liveroom-tech/react-immersive 6.2.1 → 6.2.3

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 (71) hide show
  1. package/README.md +23 -0
  2. package/dist/AnimationsTab-IKAAKT3P.mjs +1 -0
  3. package/dist/AnnotationsTab-NEJIXCOM.mjs +1 -0
  4. package/dist/CinematicTab-TIQXCPFE.mjs +1 -0
  5. package/dist/ConfigureTab-NYRMOFW4.mjs +6 -0
  6. package/dist/RulesTab-2U4MFYOZ.mjs +1 -0
  7. package/dist/SequencesTab-JZEE5WAM.mjs +1 -0
  8. package/dist/SequencesTab-O7USMDFP.css +1 -0
  9. package/dist/{StreamingTilesModel-6Z7SIC2J.mjs → StreamingTilesModel-IM2DDWO5.mjs} +1 -1
  10. package/dist/binding-builder.css +1 -1
  11. package/dist/binding-builder.d.mts +3 -3
  12. package/dist/binding-builder.d.ts +3 -3
  13. package/dist/binding-builder.js +45 -45
  14. package/dist/binding-builder.mjs +1 -1
  15. package/dist/chunk-37QOMZW6.mjs +1 -0
  16. package/dist/{chunk-NMRTI3NQ.mjs → chunk-3GA7JFNH.mjs} +1 -1
  17. package/dist/chunk-3I7PPFUV.mjs +1 -0
  18. package/dist/chunk-3PLML3PO.mjs +1 -0
  19. package/dist/chunk-5C4H7SRF.mjs +1 -0
  20. package/dist/chunk-CHMFH4D4.mjs +1 -0
  21. package/dist/chunk-CUDL77ZY.mjs +1 -0
  22. package/dist/{chunk-UTVFRMYW.mjs → chunk-IEKGBS46.mjs} +2 -2
  23. package/dist/chunk-LU3BQ7WG.mjs +69 -0
  24. package/dist/chunk-QZNUWZG7.mjs +50 -0
  25. package/dist/{chunk-3OCHN3CA.mjs → chunk-SOAUR4DL.mjs} +1 -1
  26. package/dist/chunk-SXN3YYD3.mjs +1 -0
  27. package/dist/chunk-TI2YXAHW.mjs +3 -0
  28. package/dist/exportSceneMesh-MSQIMBFF.mjs +1 -0
  29. package/dist/hosted.css +1 -1
  30. package/dist/hosted.d.mts +5 -3
  31. package/dist/hosted.d.ts +5 -3
  32. package/dist/hosted.js +44 -44
  33. package/dist/hosted.mjs +1 -1
  34. package/dist/index.css +1 -1
  35. package/dist/index.d.mts +8 -4
  36. package/dist/index.d.ts +8 -4
  37. package/dist/index.js +42 -42
  38. package/dist/index.mjs +1 -1
  39. package/dist/{modelFileResolver-CVmON8hg.d.mts → modelFileResolver-An1aQ0D3.d.mts} +42 -29
  40. package/dist/{modelFileResolver-CVmON8hg.d.ts → modelFileResolver-An1aQ0D3.d.ts} +42 -29
  41. package/dist/{modelViewerProps-BBnZTFO-.d.ts → modelViewerProps-BnVflYRA.d.mts} +22 -3
  42. package/dist/{modelViewerProps-CIY4nsNY.d.mts → modelViewerProps-H_K454u_.d.ts} +22 -3
  43. package/dist/{publicProps-BARVwXu7.d.ts → publicProps-AdNUI06n.d.ts} +1 -1
  44. package/dist/{publicProps-n0Yvr_Km.d.mts → publicProps-BOQBJBA_.d.mts} +1 -1
  45. package/dist/simple-model-viewer.css +1 -1
  46. package/dist/simple-model-viewer.d.mts +6 -2
  47. package/dist/simple-model-viewer.d.ts +6 -2
  48. package/dist/simple-model-viewer.js +5 -5
  49. package/dist/simple-model-viewer.mjs +1 -1
  50. package/dist/utils.d.mts +4 -4
  51. package/dist/utils.d.ts +4 -4
  52. package/dist/utils.js +15 -15
  53. package/dist/utils.mjs +1 -1
  54. package/package.json +7 -5
  55. package/dist/AnimationsTab-NUJTEMZ5.mjs +0 -1
  56. package/dist/AnnotationsTab-OHRVWRKI.mjs +0 -1
  57. package/dist/CinematicTab-IJAY2BS6.mjs +0 -1
  58. package/dist/ConfigureTab-UQRRATCY.mjs +0 -6
  59. package/dist/RulesTab-YVBZGJZX.mjs +0 -1
  60. package/dist/SequencesTab-IC5LWQ6N.css +0 -1
  61. package/dist/SequencesTab-VZKEKAUG.mjs +0 -1
  62. package/dist/chunk-25WC5KDO.mjs +0 -50
  63. package/dist/chunk-7OVEHDDE.mjs +0 -1
  64. package/dist/chunk-AL6UIGWP.mjs +0 -1
  65. package/dist/chunk-KYXJG3TX.mjs +0 -1
  66. package/dist/chunk-LD7QWHB4.mjs +0 -3
  67. package/dist/chunk-RKDATTUM.mjs +0 -1
  68. package/dist/chunk-UAZ6I25Y.mjs +0 -1
  69. package/dist/chunk-UHRJ2RBF.mjs +0 -1
  70. package/dist/chunk-YKDDNX2D.mjs +0 -69
  71. package/dist/exportSceneMesh-YJFOLFHS.mjs +0 -1
package/README.md CHANGED
@@ -23,6 +23,11 @@ visibility, camera control, animations, annotations, and scene authoring.
23
23
  Import each component's stylesheet once. Give the viewer's container an explicit
24
24
  height. The package does not require Tailwind in the consuming application.
25
25
 
26
+ `SimpleModelViewer` exposes a named region even with both panels disabled. Set
27
+ `aria-label="Product preview"` or `aria-labelledby="product-heading"` to give it
28
+ a model-specific name. Its default name is "3D model viewer", translated through
29
+ `labels.modelViewer`; collapsed panel buttons also have localized names.
30
+
26
31
  ## Install
27
32
 
28
33
  In a React 19 application:
@@ -104,6 +109,24 @@ Pure data helpers, including `defineObjectBindings`, `patchBindings`,
104
109
  `patchSceneConfig`, and `selectBindings`, are available from the server-safe
105
110
  `@liveroom-tech/react-immersive/utils` entry.
106
111
 
112
+ To fly to an annotation and open its card from your own UI, call
113
+ `await viewer.connection.goToAnnotation(annotationId)`. The numeric ID comes
114
+ from `sceneConfig.annotations`. It restores a saved camera view or frames the
115
+ marker automatically, and returns `false` before connection or for an unknown
116
+ or filtered-out ID. Controlled cards still use `onActiveAnnotationChange`.
117
+ Hotspot markers expose their annotation title as a tooltip and accessible name.
118
+
119
+ `viewer.camera.focusObject(id, { padding: 0.2, paddingRight: 1 })` sets object-fit
120
+ padding in scene units. Per-side options override uniform padding; omitted
121
+ values keep the `0.5` default. Padding does not alter a binding's saved camera view.
122
+
123
+ With `enableXR` (or `xr={{ enabled: true }}`), an app-owned button can call
124
+ `await viewer.connection.startAR()`. Invoke it directly in the click handler
125
+ and catch launch errors. It starts WebXR AR when supported, otherwise launches
126
+ the supplied iOS USDZ or opens the configured-AR/phone handoff. It returns
127
+ `false` when disabled, unavailable, or not ready; `true` means the session
128
+ started or a platform handoff opened.
129
+
107
130
  ## Embed a hosted project without React
108
131
 
109
132
  For Shopify, WordPress, Webflow, or plain HTML, open a hosted project's **Share**
@@ -0,0 +1 @@
1
+ import{b as M,f as C,k,l as L}from"./chunk-SXN3YYD3.mjs";import{c as A,h as P,i as w}from"./chunk-FGAJ2UT7.mjs";import{b as S}from"./chunk-3PLML3PO.mjs";import"./chunk-JR557W27.mjs";import{useMemo as D,useState as I}from"react";import{ChevronRight as V,EyeOff as G,Pause as R,Play as J,Rewind as K,Square as Q}from"lucide-react";import{Fragment as Z,jsx as n,jsxs as i}from"react/jsx-runtime";var W={playing:"Playing",paused:"Paused",finished:"Finished"};function x(o){let r=Number.isFinite(o)&&o>0?o:0;if(r<60)return`${r.toFixed(1)}s`;let u=Math.floor(r/60),m=(r%60).toFixed(1).padStart(4,"0");return`${u}:${m}`}function X(o){return Number.isFinite(o)?Math.min(3,Math.max(.1,Number(o.toFixed(2)))):1}function Y({config:o,onChange:r,clipDetails:u,active:m,play:b,pause:y,stop:h,setSpeed:T,seek:$}){let v=A(P().tier,"animationConfig"),[c,F]=I(!1),B=D(()=>new Map(u.map(e=>[e.sourceName,e.duration])),[u]),f=o.animations.clips.filter(e=>!e.hidden),d=o.animations.clips.filter(e=>e.hidden),N=(e,a=o.animations.autoplayClip)=>{r({...o,animations:{clips:e(o.animations.clips),autoplayClip:a}})},g=(e,a)=>{N(t=>t.map(l=>l.sourceName===e?a(l):l))},z=(e,a)=>{g(e,t=>({...t,loopMode:a}))},O=(e,a)=>{let t=X(a);g(e,l=>({...l,speed:t})),m.some(l=>l.clip===e)&&T(t,e)},H=(e,a)=>{g(e,t=>({...t,displayName:a}))},E=e=>{let a=o.animations.autoplayClip===e?null:e;r({...o,animations:{...o.animations,autoplayClip:a}})},U=e=>{N(a=>a.map(t=>t.sourceName===e?{...t,hidden:!0}:t),o.animations.autoplayClip===e?null:o.animations.autoplayClip),m.some(a=>a.clip===e)&&h(e)},_=e=>{N(a=>a.map(t=>t.sourceName===e?{...t,hidden:!1}:t))};return o.animations.clips.length===0?n(C,{title:"Clips",children:n("div",{className:"se-helperCard",children:"This model has no animation clips. Open one that does to set playback, names, and autoplay here."})}):i(C,{title:"Clips",children:[v?null:n(w,{requiredTier:"starter",feature:"Autoplay, looping, speed and clip names"}),f.length===0?n("p",{className:"se-listEmpty",children:"All clips are hidden. Show one below to use it again."}):n("div",{className:"se-animationList",children:f.map(e=>{let a=m.find(s=>s.clip===e.sourceName),t=a?.status==="playing",l=a?.time??0,p=B.get(e.sourceName)??0,q=a?W[a.status]:"Ready";return i("article",{className:`se-clip ${a?"se-clip--active":""}`,children:[i("div",{className:"se-clipHeader",children:[n("input",{className:"se-clipName","aria-label":`Name of ${e.sourceName}`,title:e.sourceName,value:e.displayName,onChange:s=>H(e.sourceName,s.target.value),placeholder:e.sourceName}),o.animations.autoplayClip===e.sourceName?n("span",{className:"rui-chip",children:"Autoplay"}):null,n("span",{className:"rui-chip",children:q}),n("button",{type:"button",className:"se-iconButton",onClick:()=>U(e.sourceName),title:"Hide clip","aria-label":`Hide ${e.sourceName}`,children:n(G,{size:13})})]}),i("div",{className:"se-clipTransport",children:[n("button",{type:"button",className:"se-iconButton",onClick:()=>t&&!a?.reverse?y(e.sourceName):b(e.sourceName,{alongside:!0}),"aria-label":t&&!a?.reverse?"Pause":"Play",title:t&&!a?.reverse?"Pause":"Play",children:t&&!a?.reverse?n(R,{size:13}):n(J,{size:13})}),n("button",{type:"button",className:"se-iconButton",onClick:()=>t&&a?.reverse?y(e.sourceName):b(e.sourceName,{alongside:!0,reverse:!0}),"aria-label":t&&a?.reverse?"Pause":"Play backwards",title:t&&a?.reverse?"Pause":"Play backwards",children:t&&a?.reverse?n(R,{size:13}):n(K,{size:13})}),n("button",{type:"button",className:"se-iconButton",onClick:()=>h(e.sourceName),disabled:!a,"aria-label":"Stop",title:"Stop",children:n(Q,{size:13})}),n("input",{className:"se-slider se-clipSeek",type:"range",min:0,max:Math.max(p,.001),step:.01,value:Math.min(l,p),disabled:!a||p<=0,onChange:s=>$(Number(s.target.value),e.sourceName),"aria-label":"Seek"}),i("code",{className:"se-clipTime",children:[x(Math.min(l,p))," / ",x(p)]})]}),v?i(Z,{children:[n(M,{label:"Loop",path:"sceneConfig.animations.clips[\u2026].loopMode",children:i(S,{className:"se-select",value:e.loopMode,onChange:s=>z(e.sourceName,s.target.value),children:[n("option",{value:"repeat",children:"Loop"}),n("option",{value:"once",children:"Play once"}),n("option",{value:"ping-pong",children:"Ping-pong"})]})}),n(k,{label:"Speed",path:"sceneConfig.animations.clips[\u2026].speed",value:e.speed,min:.1,max:3,step:.1,formatValue:s=>`${s.toFixed(2)}x`,onChange:s=>O(e.sourceName,s)}),n(L,{label:"Autoplay",path:"sceneConfig.animations.autoplayClip",checked:o.animations.autoplayClip===e.sourceName,onChange:()=>E(e.sourceName)})]}):null]},e.sourceName)})}),d.length>0?i("section",{className:"se-removedAnimations",children:[n("div",{className:"se-panelHeaderRow",children:i("button",{type:"button",className:"se-panelHeader","aria-expanded":c,onClick:()=>F(e=>!e),children:[n(V,{size:13,className:`se-panelChevron ${c?"se-panelChevron--open":""}`}),i("span",{className:"se-panelTitle",children:["Hidden clips (",d.length,")"]})]})}),c?n("div",{className:"rui-list",children:d.map(e=>i("div",{className:"rui-listRow",children:[i("span",{className:"rui-listRowMain",children:[n("span",{className:"rui-listRowTitle",children:e.displayName||e.sourceName}),n("code",{className:"rui-listRowMeta",children:e.sourceName})]}),n("button",{type:"button",className:"bb-button bb-button--small",onClick:()=>_(e.sourceName),children:"Show"})]},e.sourceName))}):null]}):null]})}export{Y as default};
@@ -0,0 +1 @@
1
+ import{a as f}from"./chunk-FXV4CLAK.mjs";import{b as u,c as L,d as M,e as c}from"./chunk-SXN3YYD3.mjs";import"./chunk-3PLML3PO.mjs";import{a as k,e as B}from"./chunk-7OWHW66D.mjs";import"./chunk-JR557W27.mjs";import{useState as v}from"react";import{Eye as I,Pencil as E,Plus as V,Trash2 as G,X as P}from"lucide-react";import{Fragment as $,jsx as t,jsxs as o}from"react/jsx-runtime";function H({config:r,onChange:w,cameraState:N,onPreviewView:b}){let[a,n]=v(null),[h,y]=v(""),[C,R]=v(""),z=e=>{n(structuredClone(e)),y(e.minDistance?.toString()??""),R(e.maxDistance?.toString()??"")},p=h.trim()===""?void 0:Number(h),d=C.trim()===""?void 0:Number(C),g=[p,d].some(e=>e!==void 0&&(!Number.isFinite(e)||e<0))?"Distances must be non-negative numbers.":p!==void 0&&d!==void 0&&p>d?"Maximum distance must be at least the minimum.":a?.images?.some(e=>e.src.trim()&&!k(e.src))||a?.links?.some(e=>e.href.trim()&&!k(e.href))?"Use an HTTP(S) URL or a relative path for images and links.":null,T=()=>{if(!a?.title.trim()||g)return;let e={...a,title:a.title.trim(),category:a.category?.trim()||void 0,minDistance:p,maxDistance:d,images:a.images?.filter(i=>i.src.trim()).map(i=>({...i,src:i.src.trim()})),links:a.links?.filter(i=>i.href.trim()).map(i=>({...i,href:i.href.trim(),label:i.label.trim()||"Learn more"}))};w({...r,annotations:r.annotations.map(i=>i.id===a.id?e:i)}),n(null)},[D,m]=v(null),F=e=>{w({...r,annotations:r.annotations.filter(i=>i.id!==e).map((i,l)=>({...i,id:l+1}))}),n(null)},S=(e,i)=>a&&n({...a,images:a.images.map((l,s)=>s===e?{...l,...i}:l)}),A=(e,i)=>a&&n({...a,links:a.links.map((l,s)=>s===e?{...l,...i}:l)});return o("div",{className:"se-annotations",children:[t(M,{title:"Annotations",children:t("span",{className:"se-listHeaderHint",children:"Double-click the model to add"})}),D!==null?t(f,{title:"Delete this annotation?",onCancel:()=>m(null),footer:o($,{children:[t("button",{type:"button",className:"bb-dialogButton",onClick:()=>m(null),children:"Cancel"}),t("button",{type:"button",className:"bb-dialogButton bb-dialogButton--danger",autoFocus:!0,onClick:()=>{F(D),m(null)},children:"Delete"})]}),children:t("p",{children:"You can undo this."})}):null,r.annotations.length===0?t("p",{className:"se-listEmpty",children:"No annotations yet."}):t("div",{className:"rui-list",children:r.annotations.map(e=>o("div",{className:"rui-listRow",children:[t("span",{className:"rui-listRowIndex",children:e.id}),o("span",{className:"rui-listRowMain",children:[t("span",{className:"rui-listRowTitle",title:e.title,children:e.title}),e.description||e.category?.trim()?t("span",{className:"rui-listRowMeta",title:e.description,children:[e.category?.trim(),e.description].filter(Boolean).join(" \xB7 ")}):null]}),o("span",{className:"rui-listRowActions",children:[B(e.cameraView)&&t("button",{type:"button",className:"se-iconButton",disabled:!b,onClick:()=>b?.(e.cameraView),title:"Preview saved view","aria-label":`Preview saved view for ${e.title}`,children:t(I,{size:13,"aria-hidden":"true"})}),t("button",{type:"button",className:"se-iconButton",onClick:()=>z(e),title:"Edit annotation","aria-label":`Edit ${e.title}`,children:t(E,{size:13,"aria-hidden":"true"})}),t("button",{type:"button",className:"se-iconButton",onClick:()=>m(e.id),title:"Delete annotation","aria-label":`Delete ${e.title}`,children:t(G,{size:13,"aria-hidden":"true"})})]})]},e.id))}),a&&t(f,{title:"Edit annotation",onCancel:()=>n(null),footer:o($,{children:[t("button",{type:"button",className:"bb-dialogButton bb-dialogFooterStart",onClick:()=>{m(a.id),n(null)},children:"Delete"}),t("button",{type:"button",className:"bb-dialogButton",onClick:()=>n(null),children:"Cancel"}),t("button",{type:"button",className:"bb-dialogButton bb-dialogButton--primary",disabled:!a.title.trim()||!!g,onClick:T,children:"Save"})]}),children:o("div",{className:"rui-dialogRows",children:[t(u,{label:"Title",children:t("input",{className:"bb-input",autoFocus:!0,value:a.title,onChange:e=>n({...a,title:e.target.value})})}),t(u,{label:"Description",children:t("textarea",{className:"bb-textarea bb-textarea--short",rows:2,value:a.description,onChange:e=>n({...a,description:e.target.value})})}),t(u,{label:"Category",children:t("input",{className:"bb-input",value:a.category??"",placeholder:"Features",onChange:e=>n({...a,category:e.target.value})})}),t(u,{label:"Position",children:t("code",{className:"rui-readout",children:a.worldPosition.map(e=>e.toFixed(2)).join(", ")})}),o(c,{title:"Camera view",children:[t("p",{className:"rui-note",children:a.cameraView?"Tours fly to this view.":"Tours zoom in on the marker unless you capture a view."}),o(L,{children:[t("button",{type:"button",className:"bb-button",disabled:!N,onClick:()=>N&&n({...a,cameraView:structuredClone(N)}),children:"Capture"}),t("button",{type:"button",className:"bb-button",disabled:!a.cameraView||!b,onClick:()=>a.cameraView&&b?.(a.cameraView),children:"Preview"}),t("button",{type:"button",className:"bb-button",disabled:!a.cameraView,onClick:()=>n({...a,cameraView:void 0}),children:"Clear"})]})]}),t(c,{title:"Visible between",children:t(u,{label:"Distance",title:"In scene units. Leave blank to show the marker at any distance.",children:o("span",{className:"rui-pair",children:[t("input",{className:"bb-input",type:"number",min:"0",step:"any","aria-label":"Minimum distance",placeholder:"Min",value:h,onChange:e=>y(e.target.value)}),t("input",{className:"bb-input",type:"number",min:"0",step:"any","aria-label":"Maximum distance",placeholder:"Max",value:C,onChange:e=>R(e.target.value)})]})})}),t(c,{title:"Images",actions:t("button",{type:"button",className:"se-iconButton",title:"Add image","aria-label":"Add image",onClick:()=>n({...a,images:[...a.images??[],{src:"",alt:""}]}),children:t(V,{size:14})}),children:a.images?.map((e,i)=>o("div",{className:"rui-mediaRow",children:[t("input",{className:"bb-input","aria-label":`Image URL ${i+1}`,placeholder:"https://example.com/image.jpg",value:e.src,onChange:l=>S(i,{src:l.target.value})}),t("input",{className:"bb-input","aria-label":`Image description ${i+1}`,placeholder:"Description",value:e.alt??"",onChange:l=>S(i,{alt:l.target.value})}),t("button",{type:"button",className:"se-iconButton",title:"Remove image","aria-label":`Remove image ${i+1}`,onClick:()=>n({...a,images:a.images.filter((l,s)=>s!==i)}),children:t(P,{size:13})})]},i))}),t(c,{title:"Links",actions:t("button",{type:"button",className:"se-iconButton",title:"Add link","aria-label":"Add link",onClick:()=>n({...a,links:[...a.links??[],{href:"",label:""}]}),children:t(V,{size:14})}),children:a.links?.map((e,i)=>o("div",{className:"rui-mediaRow",children:[t("input",{className:"bb-input","aria-label":`Link URL ${i+1}`,placeholder:"https://example.com/details",value:e.href,onChange:l=>A(i,{href:l.target.value})}),t("input",{className:"bb-input","aria-label":`Link label ${i+1}`,placeholder:"Learn more",value:e.label,onChange:l=>A(i,{label:l.target.value})}),t("button",{type:"button",className:"se-iconButton",title:"Remove link","aria-label":`Remove link ${i+1}`,onClick:()=>n({...a,links:a.links.filter((l,s)=>s!==i)}),children:t(P,{size:13})})]},i))}),g?t("p",{className:"bb-note bb-note--error",role:"alert",children:g}):null]})})]})}export{H as default};
@@ -0,0 +1 @@
1
+ import{b as A,d as h,k as C,l as d}from"./chunk-SXN3YYD3.mjs";import{a as N}from"./chunk-37YITTIK.mjs";import"./chunk-3PLML3PO.mjs";import"./chunk-JR557W27.mjs";import{useState as T,useCallback as p}from"react";import{ArrowDown as M,ArrowUp as E,MapPin as F,Plus as L,Trash2 as B}from"lucide-react";import{jsx as n,jsxs as m}from"react/jsx-runtime";var I={enabled:!0,waypoints:[],loop:!0,autoPlay:!1},b=3.5;function U({config:g,onChange:w,cameraState:r,onPreviewWaypoint:f}){let i=g.cinematic??I,W=N(),{waypoints:o}=i,u=p((t,e)=>{if(t.length<2)return 0;let s=0;for(let a=0;a<t.length-1;a++){let c=t[a].segmentDuration??b;s+=c}return e&&t.length>1&&(s+=t[t.length-1].segmentDuration??b),Math.max(s,0)},[]),l=p(t=>{w({...g,cinematic:{...i,...t}})},[g,i,w]),R=p(()=>{if(!r)return;let t={position:[...r.position],target:[...r.target],...r.azimuthAngle!=null?{azimuthAngle:r.azimuthAngle}:{},...r.polarAngle!=null?{polarAngle:r.polarAngle}:{},segmentDuration:b},e=[...o,t],s=u(e,i.loop);l({waypoints:e,duration:s})},[r,o,i.loop,u,l]),k=p(t=>{let e=o.filter((a,c)=>c!==t),s=u(e,i.loop);l({waypoints:e,duration:s})},[o,i.loop,u,l]),v=p((t,e)=>{let s=t+e;if(s<0||s>=o.length)return;let a=[...o];[a[t],a[s]]=[a[s],a[t]];let c=u(a,i.loop);l({waypoints:a,duration:c})},[o,i.loop,u,l]),z=p((t,e)=>{let s=o.map((c,P)=>P===t?{...c,...e}:c),a=u(s,i.loop);l({waypoints:s,duration:a})},[o,i.loop,u,l]),[y,S]=T(!1),_=o.some(t=>t.segmentDuration!==void 0),$=p(t=>t>=o.length-1?b:o[t]?.segmentDuration??b,[o]),D=o.length>=2;return m("div",{className:"se-cinematic",children:[m("div",{className:"se-cinematicSettings",children:[n(h,{title:"Playback"}),n(d,{label:"Enabled",path:"sceneConfig.cinematic.enabled",checked:i.enabled,onChange:t=>l({enabled:t})}),n(d,{label:"Loop",path:"sceneConfig.cinematic.loop",checked:i.loop,onChange:t=>{l({loop:t,duration:u(o,t)})}}),n(d,{label:"Autoplay",path:"sceneConfig.cinematic.autoPlay",checked:i.autoPlay,onChange:t=>l({autoPlay:t})}),i.autoPlay&&W?n("p",{className:"rui-note",children:"Your device reduces motion, so this preview won't play on its own. Visitors without that setting will see it."}):null,n(d,{label:"Segment timing",checked:y,onChange:S}),!y&&D?n(C,{label:"Duration",path:"sceneConfig.cinematic.duration",value:i.duration??u(o,i.loop),min:4,max:120,step:1,formatValue:t=>`${t}s`,onChange:t=>l({duration:t})}):n(A,{label:"Total",children:n("code",{className:"rui-readout",children:D?`${(i.duration??u(o,i.loop)).toFixed(1)}s`:"Add at least 2 waypoints"})})]}),m("div",{className:"se-cinematicWaypoints",children:[n(h,{title:`Waypoints${o.length?` (${o.length})`:""}`,children:m("button",{type:"button",className:"bb-button bb-button--small",onClick:R,disabled:!r,title:"Capture the current camera view as a waypoint",children:[n(L,{size:13,"aria-hidden":"true"}),"Add from view"]})}),o.length===0?n("p",{className:"se-listEmpty",children:"Frame the model, then add it as a waypoint. The camera glides through them in order."}):n("div",{className:"rui-list",children:o.map((t,e)=>{let s=e===o.length-1;return m("div",{children:[m("div",{className:"rui-listRow",children:[n("span",{className:"rui-listRowIndex",children:e+1}),n("span",{className:"rui-listRowMain",children:n("code",{className:"rui-listRowCode",title:"Camera position",children:t.position.map(a=>a.toFixed(2)).join(", ")})}),m("span",{className:"rui-listRowActions rui-listRowActions--visible",children:[n("button",{type:"button",className:"se-iconButton",disabled:!f,onClick:()=>f?.(t),title:"Fly to this waypoint","aria-label":`Fly to waypoint ${e+1}`,children:n(F,{size:13})}),n("button",{type:"button",className:"se-iconButton",disabled:e===0,onClick:()=>v(e,-1),title:"Move up","aria-label":`Move waypoint ${e+1} up`,children:n(E,{size:13})}),n("button",{type:"button",className:"se-iconButton",disabled:s,onClick:()=>v(e,1),title:"Move down","aria-label":`Move waypoint ${e+1} down`,children:n(M,{size:13})}),n("button",{type:"button",className:"se-iconButton",onClick:()=>k(e),title:"Delete waypoint","aria-label":`Delete waypoint ${e+1}`,children:n(B,{size:13})})]})]}),y&&!s?n(C,{label:`${e+1} \u2192 ${e+2}`,path:"sceneConfig.cinematic.waypoints[\u2026].segmentDuration",value:$(e),min:1,max:20,step:.5,formatValue:a=>`${a.toFixed(1)}s`,onChange:a=>z(e,{segmentDuration:a})}):null]},`waypoint-${e}`)})})]})]})}export{U as default};
@@ -0,0 +1,6 @@
1
+ import{b as B,d as Q,g as z,l as Z,m as ce,o as de}from"./chunk-SXN3YYD3.mjs";import{c as ke,e as we,f as Oe,h as Pe,i as X}from"./chunk-FGAJ2UT7.mjs";import{b as G}from"./chunk-3PLML3PO.mjs";import{d as le,e as ve,f as ye,m as Ce,n as Ne}from"./chunk-7JCFNZJM.mjs";import"./chunk-GV4ZRXL6.mjs";import"./chunk-BNKTI3CY.mjs";import"./chunk-JR557W27.mjs";import{useEffect as oe,useState as T}from"react";import{ArrowDown as Be,ArrowUp as De,Check as ze,ChevronRight as Ee,Copy as Ae,Crosshair as je,EyeOff as Ue,MousePointerClick as Ke,Palette as Le,Play as Fe,Plus as be,Trash2 as te,X as Je}from"lucide-react";import{Fragment as ee,jsx as t,jsxs as c}from"react/jsx-runtime";var We=["list","chips","swatches"],ue=["USD","EUR","GBP","SGD","AUD","CAD","INR","JPY"],Ve="https://shop.example.com/cart/add?sku={sku}&qty={quantity}";function ne(o,n){let r=1;for(;n.includes(`${o}-${r}`);)r++;return`${o}-${r}`}function E(o,n,r){let a=o.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||n;return r.includes(a)?ne(a,r):a}function Se(o,n="New choice"){return{id:E(n,"choice",o),label:n,required:!0,defaultOptionId:"option-1",options:[{id:"option-1",label:"Option 1"},{id:"option-2",label:"Option 2"}]}}function ie(o,n){return o[n]?.label||n}function Te(o){let n=o?.style?.material?.baseColor;return typeof n=="string"&&/^#[0-9a-f]{3,8}$/i.test(n)?n:"#888888"}function Ie(o,n,r){let a=`${ie(o,n)} color`,h=Te(o[n]);return{id:E(a,"color",r),label:a,required:!0,display:"swatches",defaultOptionId:"original",options:[{id:"original",label:"Original",swatch:h},{id:"color-2",label:"New color",swatch:"#2457c5",appearance:[{target:n,material:{baseColor:"#2457c5"}}]}]}}function Re(o,n,r){let a=ie(o,n);return{id:E(a,"part",r),label:a,required:!0,display:"chips",defaultOptionId:"included",options:[{id:"included",label:"Included",appearance:[{target:n,visible:!0}]},{id:"not-included",label:"Not included",appearance:[{target:n,visible:!1}]}]}}function He(o){try{return new Intl.NumberFormat(void 0,{style:"currency",currency:o}).resolvedOptions().maximumFractionDigits??2}catch{return 2}}function Ye(o){return typeof o=="string"?`object:${o}`:Array.isArray(o)?`object:${o[0]??""}`:"group"in o?`group:${o.group}`:"tag"in o?`tag:${o.tag}`:"type"in o?`type:${o.type}`:""}function _e(o){let n=o.indexOf(":"),r=o.slice(0,n),a=o.slice(n+1);return r==="group"?{group:a}:r==="tag"?{tag:a}:r==="type"?{type:a}:a}function Xe(o){let n=Object.entries(o).filter(([,a])=>!a.removed),r=a=>[...new Set(a.filter(h=>!!h))].sort();return{groups:r(n.map(([,a])=>a.group)),tags:r(n.flatMap(([,a])=>a.tags??[])),objects:n.map(([a,h])=>({key:a,label:h.label||a}))}}function $({label:o,value:n,placeholder:r,onCommit:a,path:h}){let[w,v]=T(n);oe(()=>v(n),[n]);let N=()=>{w.trim()!==n&&a(w.trim())};return t(B,{label:o,path:h,children:t("input",{className:"se-textInput","aria-label":o,value:w,placeholder:r,onChange:m=>v(m.target.value),onBlur:N,onKeyDown:m=>{m.key==="Enter"&&N()}})})}function xe({label:o,valueMinor:n,currency:r,signed:a,onCommit:h,path:w}){let v=He(r),N=n===void 0?"":(n/10**v).toFixed(v),[m,M]=T(N);oe(()=>M(N),[N]);let O=()=>{if(m.trim()===N)return;let y=Number(m);if(!m.trim()||!Number.isFinite(y)||!a&&y<0){M(N),m.trim()||h(void 0);return}h(Math.round(y*10**v))};return t(B,{label:o,path:w,children:c("span",{className:"se-productMoney",children:[t("input",{className:"se-textInput","aria-label":o,inputMode:"decimal",value:m,placeholder:"0",onChange:y=>M(y.target.value),onBlur:O,onKeyDown:y=>{y.key==="Enter"&&O()}}),t("span",{children:r})]})})}function P({label:o,onClick:n,disabled:r,children:a}){return t("button",{type:"button",className:"se-iconButton","aria-label":o,title:o,disabled:r,onClick:n,children:a})}function pe(o,n,r){let a=[...o],[h]=a.splice(n,1);return a.splice(n+r,0,h),a}function Qe(o){return o.visible===!1?"hide":o.visible===!0?"show":"color"}function Ze({edit:o,bindings:n,selectedKey:r,onChange:a,onRemove:h}){let{groups:w,tags:v,objects:N}=Xe(n),m=Ye(o.target),M=m.startsWith("group:")?w.includes(m.slice(6)):m.startsWith("tag:")?v.includes(m.slice(4)):m.startsWith("object:")?N.some(u=>u.key===m.slice(7)):!1,O=Qe(o),y=o.material?.baseColor;return c("div",{className:"se-productChange",children:[t(B,{label:"Part",children:c("span",{className:"se-productInline",children:[c(G,{className:"se-select","aria-label":"Part of the model",value:m,onChange:u=>a({...o,target:_e(u.target.value)}),children:[M?null:t("option",{value:m,children:m||"Choose a part"}),t("optgroup",{label:"Objects",children:N.map(u=>t("option",{value:`object:${u.key}`,children:u.label},u.key))}),w.length?t("optgroup",{label:"Groups",children:w.map(u=>t("option",{value:`group:${u}`,children:u},u))}):null,v.length?t("optgroup",{label:"Tags",children:v.map(u=>t("option",{value:`tag:${u}`,children:u},u))}):null]}),r&&m!==`object:${r}`?t(P,{label:`Use the selected object: ${ie(n,r)}`,onClick:()=>a({...o,target:r}),children:t(je,{size:13})}):null,t(P,{label:"Remove this change",onClick:h,children:t(Je,{size:13})})]})}),t(B,{label:"Does",children:c(G,{className:"se-select","aria-label":"What this does",value:O,onChange:u=>{let k=u.target.value;a(k==="color"?{target:o.target,material:{...o.material,baseColor:typeof y=="string"?y:"#888888"}}:{target:o.target,visible:k==="show"})},children:[t("option",{value:"color",children:"Changes its color"}),t("option",{value:"hide",children:"Hides it"}),t("option",{value:"show",children:"Shows it"})]})}),O==="color"?t(de,{label:"Color",value:typeof y=="string"?y:"#888888",onChange:u=>a({...o,material:{...o.material,baseColor:u}})}):null]})}function et({group:o,option:n,index:r,definition:a,bindings:h,variantGroups:w,canSell:v,selectedKey:N,isStart:m,isShown:M,open:O,onToggleOpen:y,onSetStart:u,onChange:k,onRemove:j,onMove:U,onDuplicate:J}){let D=n.appearance??[],K=w.flatMap(p=>p.variants.map(R=>R.name)),L=N??Object.keys(h)[0]??"",x=[m?"customers start here":"",!D.length&&!n.variants?.length?"changes nothing":""].filter(Boolean).join(" \xB7 "),F=p=>{let R=n.appearance?.[0]?.material?.baseColor,S=p[0]?.material?.baseColor,I=n.swatch!==void 0&&n.swatch===R&&typeof S=="string";k({appearance:p.length?p:void 0,...I?{swatch:S}:{}})};return c("div",{className:`se-productOption ${O?"se-productOption--open":""} ${M?"se-productOption--shown":""}`,children:[t("div",{className:"se-productOptionHeader",children:c("button",{type:"button",className:"se-productOptionToggle","aria-expanded":O,title:"Edit this option and show it on the model",onClick:y,children:[t(Ee,{size:12,"aria-hidden":"true",className:O?"se-productChevron--open":""}),n.swatch?t("span",{className:"se-productSwatch",style:{background:n.swatch},"aria-hidden":"true"}):null,t("span",{className:"se-productOptionName",children:n.label}),x?t("small",{children:x}):null]})}),O?c("div",{className:"se-productOptionBody",children:[t($,{label:"Name",value:n.label,onCommit:p=>{p&&k({label:p})}}),v&&a.pricing?t(xe,{label:"Price change",signed:!0,currency:a.pricing.currency,valueMinor:n.priceDeltaMinor,onCommit:p=>k({priceDeltaMinor:p})}):null,D.map((p,R)=>t(Ze,{edit:p,bindings:h,selectedKey:N,onChange:S=>F(D.map((I,C)=>C===R?S:I)),onRemove:()=>F(D.filter((S,I)=>I!==R))},R)),c("div",{className:"se-productActions",children:[c("button",{type:"button",className:"bb-button",disabled:!L,onClick:()=>F([...D,{target:L,material:{baseColor:Te(h[L])}}]),children:[t(be,{size:13,"aria-hidden":"true"})," ",D.length?"Change another part":"Change a part"]}),m?null:t("button",{type:"button",className:"bb-button",onClick:u,children:"Start here"})]}),c("details",{className:"se-productMore",children:[t("summary",{children:"More"}),t($,{label:"Description",value:n.description??"",placeholder:"Shown under the name",onCommit:p=>k({description:p||void 0})}),t(Z,{label:"Swatch",checked:!!n.swatch,onChange:p=>k({swatch:p?"#888888":void 0})}),n.swatch?t(de,{label:"Swatch color",value:n.swatch,onChange:p=>k({swatch:p})}):null,K.length?t(B,{label:"Material variant",children:c(G,{className:"se-select","aria-label":"Material variant",value:n.variants?.[0]??"",onChange:p=>k({variants:p.target.value?[p.target.value]:void 0}),children:[t("option",{value:"",children:"None"}),K.map(p=>t("option",{value:p,children:p},p))]})}):null,v?t($,{label:"SKU",value:n.sku??"",placeholder:"Your stock code",onCommit:p=>k({sku:p||void 0})}):null,c("div",{className:"se-productActions",children:[t(P,{label:`Move ${n.label} up`,disabled:r===0,onClick:()=>U(-1),children:t(De,{size:13})}),t(P,{label:`Move ${n.label} down`,disabled:r===o.options.length-1,onClick:()=>U(1),children:t(Be,{size:13})}),t(P,{label:`Duplicate ${n.label}`,onClick:J,children:t(Ae,{size:13})}),t(P,{label:`Delete ${n.label}`,disabled:o.options.length<=1,onClick:j,children:t(te,{size:13})})]})]})]}):null]})}function $e(o,n){let r=o.groups.find(a=>a.id===n.groupId);return`${r?.label??n.groupId}: ${r?.options.find(a=>a.id===n.optionId)?.label??n.optionId}`}function Me({bindings:o,selectedKey:n,disabled:r,onColor:a,onVisibility:h}){return!n||!o[n]?c("p",{className:"se-productHint",children:[t(Ke,{size:14,"aria-hidden":"true"}),"Click a part of the model to let customers change it."]}):c("div",{className:"se-productStarter",children:[c("p",{children:["Let customers change ",t("strong",{children:ie(o,n)}),":"]}),c("div",{className:"se-productActions",children:[c("button",{type:"button",className:"bb-button",disabled:r,onClick:()=>a(n),children:[t(Le,{size:13,"aria-hidden":"true"})," Its color"]}),c("button",{type:"button",className:"bb-button",disabled:r,onClick:()=>h(n),children:[t(Ue,{size:13,"aria-hidden":"true"})," Show or hide it"]})]})]})}function tt({code:o}){let[n,r]=T(!1);return oe(()=>{if(!n)return;let a=setTimeout(()=>r(!1),2e3);return()=>clearTimeout(a)},[n]),c("div",{className:"se-productCode",children:[t("pre",{children:o}),t(P,{label:n?"Copied":"Copy code",onClick:()=>{navigator.clipboard.writeText(o).then(()=>r(!0)).catch(()=>{})},children:n?t(ze,{size:13}):t(Ae,{size:13})})]})}function nt({config:o,onChange:n,objectBindings:r,materialVariantGroups:a,selectedKey:h,previewSelections:w,onPreviewChange:v,onPreviewAsCustomer:N,publish:m,modelFileName:M}){let{tier:O}=Pe(),y=ke(O,"productCommerce"),u=o.product,[k,j]=T(null),[U,J]=T(null),[D,K]=T({}),[L,x]=T(null),F=JSON.stringify(u?.definition??null),[p,R]=T("");oe(()=>{R(u?JSON.stringify(u.definition,null,2):"")},[F]);let S=(e,l=!1)=>D[e]??l,I=(e,l=!1)=>K(s=>({...s,[e]:!(s[e]??l)})),C=e=>{try{let l=le(e),s=Object.fromEntries(l.groups.map(d=>{let b=u?.selections[d.id];return[d.id,b===null||d.options.some(g=>g.id===b)?b??null:d.defaultOptionId??null]}));n({...o,product:{definition:l,selections:s}}),j(null)}catch(l){j(l instanceof Error?l.message:String(l))}},me=M.replace(/\.[a-z0-9]+$/i,"").replace(/[-_]+/g," ").trim()||"My product",W=e=>{let l={id:E(me,"product",[]),label:me,revision:"1",groups:e};n({...o,product:{definition:l,selections:ve(l)}})};if(!u)return c("div",{className:"se-productEmpty",children:[t("h3",{children:"Let customers customize this model"}),t("p",{children:"They pick from your options beside the model and watch it change, with a price and a button to buy."}),c("div",{className:"se-productExample","aria-hidden":"true",children:[t("span",{children:"Wood"}),t("i",{style:{background:"#b89063"}}),t("i",{className:"is-on",style:{background:"#453025"}}),t("i",{style:{background:"#d7c4a3"}}),t("span",{children:"Fabric"}),t("b",{children:"Linen"}),t("b",{className:"is-on",children:"Leather"}),t("em",{children:"$304.00"}),t("u",{children:"Buy"})]}),t(Me,{bindings:r,selectedKey:h,onColor:e=>{let l=Ie(r,e,[]);W([l]),x(`${l.id}/color-2`),v({[l.id]:"color-2"})},onVisibility:e=>W([Re(r,e,[])])}),a.length?c("button",{type:"button",className:"bb-button",onClick:()=>{let e=[];W(a.map(l=>{let s=E(l.name??"","variants",e);e.push(s);let d=[],b=l.variants.map(f=>{let _=E(f.label,"option",d);return d.push(_),{id:_,label:f.label,variants:[f.name],...f.swatch?.startsWith("#")?{swatch:f.swatch}:{}}}),g=b.every(f=>f.swatch)?"swatches":"list";return{id:s,label:l.name??"Variant",required:!0,defaultOptionId:b[0]?.id,display:g,options:b}}))},children:["Use this model's ",a.flatMap(e=>e.variants).length," material variants"]}):null,t("button",{type:"button",className:"se-productLink",onClick:()=>W([Se([],"Finish")]),children:"Or start with a blank choice"})]});let i=u.definition,se=i.groups.map(e=>e.id),Ge=we(O,"productGroups",i.groups.length),V=i.groups.flatMap(e=>e.options.map(l=>({groupId:e.id,optionId:l.id}))),q=e=>JSON.stringify([e.groupId,e.optionId]),he=e=>{let[l,s]=JSON.parse(e);return{groupId:l,optionId:s}},ge=ye(i,u.selections),fe=Ne(i,r,a.length?a.flatMap(e=>e.variants.map(l=>l.name)):void 0),H=w??u.selections,ae=e=>{let l=b=>e.some(g=>g.id===b.groupId&&g.options.some(f=>f.id===b.optionId)),s=(i.rules??[]).filter(b=>l(b.when)).map(b=>({...b,requires:b.requires?.filter(l),excludes:b.excludes?.filter(l)})).filter(b=>b.requires?.length||b.excludes?.length),d=b=>JSON.stringify(b.map(g=>[g.id,g.required,g.options.map(f=>f.id)]));C({...i,groups:e,rules:s,skus:d(e)===d(i.groups)?i.skus:void 0})},re=(e,l)=>{ae([...i.groups,e]),K(s=>({...s,[`group:${e.id}`]:!0})),l&&(x(`${e.id}/${l}`),v({...H,[e.id]:l}))},A=(e,l)=>ae(i.groups.map(s=>s.id===e?{...s,...l}:s)),qe=(e,l,s)=>A(e.id,{options:e.options.map(d=>d.id===l?{...d,...s}:d)}),Y=(e,l)=>C({...i,rules:i.rules.map(s=>s.id===e?{...s,...l}:s)});return c("div",{className:"se-generalContent",children:[c("div",{className:"se-productTop",children:[t($,{label:"Product",path:"sceneConfig.product.definition.label",value:i.label,onCommit:e=>{e&&C({...i,label:e})}}),t("div",{className:"se-productActions",children:c("button",{type:"button",className:"bb-button bb-button--primary",onClick:N,children:[t(Fe,{size:13,"aria-hidden":"true"})," Preview as a customer"]})})]}),fe.length?t("ul",{className:"se-productWarnings","aria-label":"Problems to fix",children:fe.map((e,l)=>t("li",{children:e.message},l))}):null,k?t("p",{className:"bb-note bb-note--error se-productError",role:"alert",children:k}):null,t(Q,{title:"What customers can choose"}),i.groups.map(e=>{let l=u.selections[e.id]??null;return c(z,{title:e.label,open:S(`group:${e.id}`,i.groups.length===1),onToggle:()=>I(`group:${e.id}`,i.groups.length===1),trailing:c("small",{className:"se-productCount",children:[e.options.length," options"]}),children:[t("div",{className:"se-productOptions",children:e.options.map((s,d)=>{let b=`${e.id}/${s.id}`;return t(et,{group:e,option:s,index:d,definition:i,bindings:r,variantGroups:a,canSell:y,selectedKey:h,isStart:l===s.id,isShown:H[e.id]===s.id,open:L===b,onToggleOpen:()=>{x(g=>g===b?null:b),v({...H,[e.id]:s.id})},onSetStart:()=>{let g={...i,groups:i.groups.map(f=>f.id===e.id?{...f,defaultOptionId:s.id}:f)};n({...o,product:{definition:g,selections:{...u.selections,[e.id]:s.id}}})},onChange:g=>qe(e,s.id,g),onMove:g=>A(e.id,{options:pe(e.options,d,g)}),onDuplicate:()=>{let g=e.options.map(_=>_.id),f=[...e.options];f.splice(d+1,0,{...structuredClone(s),id:ne("option",g),label:`${s.label} copy`,sku:void 0}),A(e.id,{options:f})},onRemove:()=>{let g=e.options.filter(f=>f.id!==s.id);A(e.id,{options:g,defaultOptionId:e.defaultOptionId===s.id?e.required?g[0]?.id:void 0:e.defaultOptionId})}},s.id)})}),t("div",{className:"se-productActions",children:c("button",{type:"button",className:"bb-button",onClick:()=>{let s=ne("option",e.options.map(b=>b.id)),d=e.display==="swatches";A(e.id,{options:[...e.options,{id:s,label:d?"New color":`Option ${e.options.length+1}`,...d?{swatch:"#888888"}:{}}]}),x(`${e.id}/${s}`),v({...H,[e.id]:s})},children:[t(be,{size:13,"aria-hidden":"true"})," Add option"]})}),c("details",{className:"se-productMore",children:[t("summary",{children:"Choice settings"}),t($,{label:"Name",value:e.label,onCommit:s=>{s&&A(e.id,{label:s})}}),t(ce,{label:"Show as",value:e.display??"list",options:We,onChange:s=>A(e.id,{display:s})}),t(Z,{label:"Customers can skip it",checked:!e.required,onChange:s=>A(e.id,{required:!s,defaultOptionId:e.defaultOptionId??(s?void 0:e.options[0]?.id)})}),c("div",{className:"se-productActions",children:[t(P,{label:`Move ${e.label} up`,disabled:i.groups.indexOf(e)===0,onClick:()=>{let s=i.groups.indexOf(e);C({...i,groups:pe(i.groups,s,-1)})},children:t(De,{size:13})}),t(P,{label:`Move ${e.label} down`,disabled:i.groups.indexOf(e)===i.groups.length-1,onClick:()=>{let s=i.groups.indexOf(e);C({...i,groups:pe(i.groups,s,1)})},children:t(Be,{size:13})}),t(P,{label:`Delete ${e.label}`,disabled:i.groups.length<=1,onClick:()=>ae(i.groups.filter(s=>s.id!==e.id)),children:t(te,{size:13})})]})]})]},e.id)}),Ge?c("div",{className:"se-productAdd",children:[t(Me,{bindings:r,selectedKey:h,onColor:e=>re(Ie(r,e,se),"color-2"),onVisibility:e=>re(Re(r,e,se))}),t("button",{type:"button",className:"se-productLink",onClick:()=>re(Se(se)),children:"Or add a blank choice"})]}):t(X,{requiredTier:Oe("productGroups",i.groups.length),feature:"More choices"}),t(z,{title:"Prices and buying",open:S("selling"),onToggle:()=>I("selling"),children:y?c(ee,{children:[t(Z,{label:"Show a price",checked:!!i.pricing,onChange:e=>C({...i,pricing:e?{currency:"USD",basePriceMinor:0}:void 0})}),i.pricing?c(ee,{children:[t(ce,{label:"Currency",path:"sceneConfig.product.definition.pricing.currency",value:i.pricing.currency,options:ue.includes(i.pricing.currency)?ue:[...ue,i.pricing.currency],onChange:e=>C({...i,pricing:{...i.pricing,currency:e}})}),t(xe,{label:"Base price",path:"sceneConfig.product.definition.pricing.basePriceMinor",currency:i.pricing.currency,valueMinor:i.pricing.basePriceMinor,onCommit:e=>C({...i,pricing:{...i.pricing,basePriceMinor:e??0}})}),t("p",{className:"bb-note",children:"Open an option to add what it costs extra. This price is for show: always charge from your own store."})]}):null,t($,{label:"Buy link",path:"sceneConfig.product.definition.checkout.urlTemplate",placeholder:Ve,value:i.checkout?.urlTemplate??"",onCommit:e=>C({...i,checkout:e?{...i.checkout,urlTemplate:e}:void 0})}),i.checkout?t($,{label:"Button text",placeholder:"Buy now",value:i.checkout.label??"",onCommit:e=>C({...i,checkout:{...i.checkout,label:e||void 0}})}):null,c("p",{className:"bb-note",children:["Where the button goes. The link can include ","{sku}",", ","{quantity}",", ","{productId}"," and ","{selections}","."]})]}):t(X,{requiredTier:"starter",feature:"Prices and buy links"})}),t(z,{title:`Restrictions${i.rules?.length?` (${i.rules.length})`:""}`,open:S("rules"),onToggle:()=>I("rules"),children:y?c(ee,{children:[t("p",{className:"bb-note",children:"For combinations you don't sell. Customers see your message, and picking a blocked option changes the others to fit."}),i.rules?.map(e=>{let l=e.excludes?.length?"excludes":"requires",s=(e[l]??[])[0];return c("div",{className:"se-productRule",children:[t(B,{label:"When",children:t(G,{className:"se-select","aria-label":`Condition for ${e.id}`,value:q(e.when),onChange:d=>Y(e.id,{when:he(d.target.value)}),children:V.map(d=>t("option",{value:q(d),children:$e(i,d)},q(d)))})}),t(B,{label:"Then",children:c(G,{className:"se-select","aria-label":`Kind of rule for ${e.id}`,value:l,onChange:d=>{let b=d.target.value;Y(e.id,b==="requires"?{requires:e.excludes,excludes:void 0}:{excludes:e.requires,requires:void 0})},children:[t("option",{value:"requires",children:"Must have"}),t("option",{value:"excludes",children:"Can't have"})]})}),t(B,{label:"Option",children:t(G,{className:"se-select","aria-label":`Choice for ${e.id}`,value:s?q(s):"",onChange:d=>Y(e.id,{[l]:[he(d.target.value)]}),children:V.filter(d=>d.groupId!==e.when.groupId).map(d=>t("option",{value:q(d),children:$e(i,d)},q(d)))})}),t($,{label:"Message",placeholder:"Shown when it's blocked",value:e.message??"",onCommit:d=>Y(e.id,{message:d||void 0})}),t("div",{className:"se-productActions",children:t(P,{label:`Delete restriction ${e.id}`,onClick:()=>C({...i,rules:i.rules.filter(d=>d.id!==e.id)}),children:t(te,{size:13})})})]},e.id)}),i.groups.length<2?t("p",{className:"bb-note",children:"Add a second choice to restrict combinations."}):t("div",{className:"se-productActions",children:c("button",{type:"button",className:"bb-button",onClick:()=>{let e=V[0],l=V.find(s=>s.groupId!==e.groupId);C({...i,rules:[...i.rules??[],{id:ne("rule",(i.rules??[]).map(s=>s.id)),when:e,requires:[l]}]})},children:[t(be,{size:13,"aria-hidden":"true"})," Add restriction"]})})]}):t(X,{requiredTier:"starter",feature:"Restrictions"})}),t(z,{title:"Publish",open:S("publish"),onToggle:()=>I("publish"),children:m?c("div",{className:"se-productPublish",children:[t("p",{className:"bb-note",children:"Customers can choose wherever this project is shared or embedded. Set access to Anyone with the link, then copy the embed code."}),m]}):c("div",{className:"se-productPublish",children:[c("p",{className:"bb-note",children:["Use ",t("strong",{children:"Export \u203A Scene settings"})," in the top bar, then show it with the options panel on:"]}),t(tt,{code:`<ModelViewer
2
+ modelUrl="/model.glb"
3
+ licenseKey={licenseKey}
4
+ sceneConfig={sceneConfig}
5
+ productOptions
6
+ />`})]})}),c(z,{title:"Advanced",open:S("advanced"),onToggle:()=>I("advanced"),children:[t($,{label:"Product ID",path:"sceneConfig.product.definition.id",value:i.id,onCommit:e=>{e&&C({...i,id:e})}}),t($,{label:"Revision",path:"sceneConfig.product.definition.revision",value:i.revision,onCommit:e=>{e&&C({...i,revision:e})}}),y?c(ee,{children:[t(Q,{title:`SKU combinations (${i.skus?.length??0})`}),t("p",{className:"bb-note",children:"Once this list has entries, combinations not on it can't be bought."}),c("div",{className:"se-productActions",children:[t("button",{type:"button",className:"bb-button",onClick:()=>{try{let e=Ce({...i,skus:void 0});J(e.length?`Generated ${e.length} ${e.length===1?"combination":"combinations"}.`:"Give each option an SKU first, under More."),e.length&&C({...i,skus:e})}catch(e){J(e instanceof Error?e.message:String(e))}},children:"Generate from option SKUs"}),i.skus?.length?t(P,{label:"Clear SKU combinations",onClick:()=>C({...i,skus:void 0}),children:t(te,{size:13})}):null]}),U?t("p",{className:"bb-note",role:"status",children:U}):null,ge.sku?c("p",{className:"bb-note",children:["Customers start on: ",ge.sku]}):null]}):null,t(Q,{title:"Definition JSON"}),t("p",{className:"bb-note",children:"For textures and anything else without a control. It's checked before it's applied."}),t("div",{className:"se-productJson",children:t("textarea",{className:"bb-textarea","aria-label":"Product definition JSON",rows:10,value:p,spellCheck:!1,onChange:e=>R(e.target.value)})}),c("div",{className:"se-productActions",children:[t("button",{type:"button",className:"bb-button",onClick:()=>{try{C(le(p))}catch(e){j(e instanceof Error?e.message:String(e))}},children:"Apply JSON"}),t("button",{type:"button",className:"bb-button",onClick:()=>{v(null),n({...o,product:void 0})},children:"Remove all of this"})]})]})]})}export{nt as default};
@@ -0,0 +1 @@
1
+ import{b as U}from"./chunk-E24EFHB5.mjs";import{c as L,d as _,k as z,n as G}from"./chunk-SXN3YYD3.mjs";import{b as w}from"./chunk-3PLML3PO.mjs";import{a as D}from"./chunk-CCBFMNAU.mjs";import{b as F}from"./chunk-GV4ZRXL6.mjs";import"./chunk-JR557W27.mjs";import{useMemo as te,useState as ae}from"react";import{ArrowDown as ne,ArrowUp as le,ChevronRight as se,Plus as H,Trash2 as oe,X as V}from"lucide-react";import{Fragment as Q,jsx as a,jsxs as r}from"react/jsx-runtime";var I={all:"All objects",group:"Group",tag:"Tag",type:"Type",object:"One object"},W={metric:"Metric",status:"Status",metadata:"Metadata"},ie={metric:"",stops:[[0,"#3b82f6"],[100,"#ef4444"]]};function re(n){return n===void 0?"all":typeof n=="string"||Array.isArray(n)?"object":"group"in n?"group":"tag"in n?"tag":"type"}function j(n){return n===void 0?"":typeof n=="string"?n:Array.isArray(n)?n[0]??"":"group"in n?n.group:"tag"in n?n.tag:n.type}function X(n,s){if(n!=="all")return n==="object"?s:n==="group"?{group:s}:n==="tag"?{tag:s}:{type:s}}function ce(n){return"metric"in n?"metric":"status"in n?"status":"metadata"}function J(n){return n==="metric"?{metric:""}:n==="status"?{status:["disabled"]}:{metadata:"",equals:""}}function ue(n){let s=n.trim();return s==="true"?!0:s==="false"?!1:s==="null"?null:s!==""&&Number.isFinite(Number(s))?Number(s):n}function $(n){return n.trim()===""||!Number.isFinite(Number(n))?void 0:Number(n)}function Y(n,s="",c=0){return typeof n!="object"||n===null||Array.isArray(n)?s?[s]:[]:c>2?s?[s]:[]:Object.entries(n).flatMap(([d,h])=>Y(h,s?`${s}.${d}`:d,c+1))}var O=n=>[...new Set(n.filter(s=>!!s))].sort((s,c)=>s.localeCompare(c));function y({label:n,children:s}){return r("label",{className:"se-field se-fieldInline",children:[a("span",{className:"se-fieldLabel",title:n,children:n}),s]})}function T({label:n,checked:s,color:c,onToggle:d,onColorChange:h}){return r("div",{className:"se-field se-fieldInline",children:[r("label",{className:"se-ruleLook",children:[a("input",{type:"checkbox",className:"se-checkbox",checked:s,onChange:A=>d(A.target.checked)}),n]}),s&&c!==void 0&&h?a(G,{label:n,value:c,onChange:h}):null]})}function de({title:n,open:s,onToggle:c,actions:d,children:h}){return r("section",{className:"se-panel",children:[r("div",{className:"se-panelHeaderRow",children:[r("button",{type:"button",className:"se-panelHeader",onClick:c,"aria-expanded":s,children:[a(se,{size:13,className:`se-panelChevron ${s?"se-panelChevron--open":""}`}),a("span",{className:"se-panelTitle",children:n})]}),a("span",{className:"se-listHeaderActions",children:d})]}),s?a("div",{className:"se-panelBody",children:h}):null]})}function pe({config:n,onChange:s,objectBindings:c}){let d=n.styleRules??[],[h,A]=ae(d[0]?.id??null),b=te(()=>{let t=Object.values(c);return{groups:O(t.map(i=>i.group)),tags:O(t.flatMap(i=>i.tags??[])),types:O(t.map(i=>i.type??"other")),objects:t.map(i=>({id:i.id,label:i.label||i.id})).sort((i,u)=>i.label.localeCompare(u.label)),metrics:O(t.flatMap(i=>Object.keys(i.metrics??{}))),metadata:O(t.flatMap(i=>Y(i.metadata)))}},[c]),M=t=>s({...n,styleRules:t}),v=(t,i)=>M(d.map(u=>u.id===t?i(u):u)),Z=()=>{let t=crypto.randomUUID();M([...d,{id:t,label:`Rule ${d.length+1}`,when:[{status:["disabled"]}],material:{baseColor:"#9ca3af"}}]),A(t)},q=(t,i)=>{let u=[...d],[m]=u.splice(t,1);u.splice(t+i,0,m),M(u)},x=t=>{let i=t.target?F(c,t.target):Object.keys(c),u=t.when?Array.isArray(t.when)?t.when:[t.when]:[];return i.filter(m=>u.every(f=>D(c[m],f))).length};return r("div",{className:"se-rules",children:[a(_,{title:"Rules",children:r("button",{type:"button",className:"bb-button bb-button--small",onClick:Z,children:[a(H,{size:13,"aria-hidden":"true"})," Add rule"]})}),a("p",{className:"rui-note",children:"Restyle objects from their metrics, status, or metadata while the app runs. Later rules win."}),a("datalist",{id:"se-ruleMetrics",children:b.metrics.map(t=>a("option",{value:t},t))}),a("datalist",{id:"se-ruleMetadata",children:b.metadata.map(t=>a("option",{value:t},t))}),d.map((t,i)=>{let u=t.when?Array.isArray(t.when)?t.when:[t.when]:[],m=re(t.target),f=t.material??{},S=t.pulse===!0?{}:t.pulse||void 0,E=t.target?F(c,t.target).length:Object.keys(c).length,B=e=>v(t.id,l=>({...l,when:e.length>0?e:void 0})),C=(e,l)=>B(u.map((p,o)=>o===e?l:p)),N=e=>v(t.id,l=>{let p={...l.material,...e};for(let o of Object.keys(p))p[o]===void 0&&delete p[o];return{...l,material:Object.keys(p).length>0?p:void 0}}),k=e=>v(t.id,l=>({...l,colorScale:e})),P=m==="group"?b.groups:m==="tag"?b.tags:m==="type"?b.types:[];return r(de,{title:t.label||`Rule ${i+1}`,open:h===t.id,onToggle:()=>A(e=>e===t.id?null:t.id),actions:r(Q,{children:[a("button",{type:"button",className:"se-iconButton",onClick:()=>q(i,-1),disabled:i===0,"aria-label":"Move rule up",title:"Move up",children:a(le,{size:13})}),a("button",{type:"button",className:"se-iconButton",onClick:()=>q(i,1),disabled:i===d.length-1,"aria-label":"Move rule down",title:"Move down",children:a(ne,{size:13})}),a("button",{type:"button",className:"se-iconButton",onClick:()=>M(d.filter(e=>e.id!==t.id)),"aria-label":"Delete rule",title:"Delete rule",children:a(oe,{size:13})})]}),children:[a(y,{label:"Name",children:a("input",{className:"se-textInput",value:t.label??"",placeholder:`Rule ${i+1}`,onChange:e=>v(t.id,l=>({...l,label:e.target.value}))})}),r("div",{className:"se-ruleGrid",children:[a(y,{label:"Applies to",children:a(w,{className:"se-select",value:m,onChange:e=>{let l=e.target.value,p=l==="object"?b.objects[0]?.id:l==="group"?b.groups[0]:l==="tag"?b.tags[0]:l==="type"?b.types[0]:"";v(t.id,o=>({...o,target:X(l,p??"")}))},children:Object.keys(I).map(e=>a("option",{value:e,children:I[e]},e))})}),m!=="all"?a(y,{label:I[m],children:r(w,{className:"se-select",value:j(t.target),onChange:e=>v(t.id,l=>({...l,target:X(m,e.target.value)})),children:[m==="object"?b.objects.map(e=>a("option",{value:e.id,children:e.label},e.id)):P.map(e=>a("option",{value:e,children:e},e)),(m==="object"?b.objects.some(e=>e.id===j(t.target)):P.includes(j(t.target)))?null:a("option",{value:j(t.target),children:j(t.target)||"None in this model"})]})}):null]}),a("div",{className:"rui-groupHeader",children:a("span",{children:"When"})}),u.length===0?a("p",{className:"rui-note",children:"Always applies."}):null,u.map((e,l)=>{let p=ce(e);return r("div",{className:"se-ruleCondition",children:[r("div",{className:"se-ruleConditionHeader",children:[a(w,{className:"se-select","aria-label":"Condition type",value:p,onChange:o=>C(l,J(o.target.value)),children:Object.keys(W).map(o=>a("option",{value:o,children:W[o]},o))}),a("button",{type:"button",className:"se-iconButton","aria-label":"Remove condition",title:"Remove condition",onClick:()=>B(u.filter((o,g)=>g!==l)),children:a(V,{size:13})})]}),"metric"in e?r("div",{className:"se-ruleGrid se-ruleGrid--three",children:[a(y,{label:"Metric",children:a("input",{className:"se-textInput",list:"se-ruleMetrics",value:e.metric,placeholder:"temperature",onChange:o=>C(l,{...e,metric:o.target.value})})}),a(y,{label:"Above",children:a("input",{className:"se-numberInput",type:"number",value:e.above??"",placeholder:"any",onChange:o=>C(l,{...e,above:$(o.target.value)})})}),a(y,{label:"Below",children:a("input",{className:"se-numberInput",type:"number",value:e.below??"",placeholder:"any",onChange:o=>C(l,{...e,below:$(o.target.value)})})})]}):"status"in e?a("div",{className:"se-ruleStatuses",children:U.map(o=>{let g=[].concat(e.status).includes(o);return r("label",{className:"se-ruleStatus",children:[a("input",{type:"checkbox",className:"se-checkbox",checked:g,onChange:()=>{let R=[].concat(e.status),K=g?R.filter(ee=>ee!==o):[...R,o];C(l,{status:K})}}),o]},o)})}):r("div",{className:"se-ruleGrid",children:[a(y,{label:"Key",children:a("input",{className:"se-textInput",list:"se-ruleMetadata",value:e.metadata,placeholder:"alarm.state",onChange:o=>C(l,{...e,metadata:o.target.value})})}),a(y,{label:"Equals",children:a("input",{className:"se-textInput",value:e.equals===null?"null":String(e.equals),placeholder:"fault",onChange:o=>C(l,{...e,equals:ue(o.target.value)})})})]})]},l)}),a(L,{children:r("button",{type:"button",className:"bb-button",onClick:()=>B([...u,J("metric")]),children:[a(H,{size:13,"aria-hidden":"true"})," Add condition"]})}),a("div",{className:"rui-groupHeader",children:a("span",{children:"Look"})}),a(T,{label:"Color",checked:f.baseColor!==void 0,color:f.baseColor,onToggle:e=>N({baseColor:e?"#9ca3af":void 0}),onColorChange:e=>N({baseColor:e})}),a(T,{label:"Opacity",checked:f.opacity!==void 0,onToggle:e=>N({opacity:e?.4:void 0})}),f.opacity!==void 0?a(z,{label:"Opacity",value:f.opacity,min:0,max:1,step:.05,formatValue:e=>`${Math.round(e*100)}%`,onChange:e=>N({opacity:e})}):null,a(T,{label:"Glow",checked:f.emissive!==void 0,color:f.emissive,onToggle:e=>N({emissive:e?"#ff3b30":void 0,emissiveIntensity:e?f.emissiveIntensity??1:void 0}),onColorChange:e=>N({emissive:e})}),a(T,{label:"Color from a metric",checked:t.colorScale!==void 0,onToggle:e=>k(e?{...ie,metric:b.metrics[0]??""}:void 0)}),t.colorScale?r("div",{className:"se-ruleCondition",children:[r("div",{className:"se-ruleGrid",children:[a(y,{label:"Metric",children:a("input",{className:"se-textInput",list:"se-ruleMetrics",value:t.colorScale.metric,placeholder:"temperature",onChange:e=>k({...t.colorScale,metric:e.target.value})})}),a(y,{label:"Sets",children:r(w,{className:"se-select",value:t.colorScale.channel??"baseColor",onChange:e=>k({...t.colorScale,channel:e.target.value}),children:[a("option",{value:"baseColor",children:"Color"}),a("option",{value:"emissive",children:"Glow color"})]})})]}),t.colorScale.stops.map(([e,l],p)=>{let o=g=>k({...t.colorScale,stops:t.colorScale.stops.map((R,K)=>K===p?g:R)});return r("div",{className:"se-ruleStop",children:[a("input",{className:"se-numberInput",type:"number","aria-label":"Stop value",value:e,onChange:g=>o([$(g.target.value)??0,l])}),a(G,{label:`Color at ${e}`,value:l,onChange:g=>o([e,g])}),a("button",{type:"button",className:"se-iconButton","aria-label":"Remove stop",title:"Remove stop",disabled:t.colorScale.stops.length<=1,onClick:()=>k({...t.colorScale,stops:t.colorScale.stops.filter((g,R)=>R!==p)}),children:a(V,{size:13})})]},p)}),a(L,{children:r("button",{type:"button",className:"bb-button",onClick:()=>{let e=t.colorScale.stops,l=e[e.length-1];k({...t.colorScale,stops:[...e,[(l?.[0]??0)+10,l?.[1]??"#ffffff"]]})},children:[a(H,{size:13,"aria-hidden":"true"})," Add stop"]})})]}):null,a(T,{label:"Pulse",checked:S!==void 0,color:S?.color??"#ff3b30",onToggle:e=>v(t.id,l=>({...l,pulse:e?{color:"#ff3b30",speed:1}:void 0})),onColorChange:e=>v(t.id,l=>({...l,pulse:{...S,color:e}}))}),S?a(Q,{children:a(z,{label:"Pulses per second",value:S.speed??1,min:.2,max:4,step:.1,formatValue:e=>e.toFixed(1),onChange:e=>v(t.id,l=>({...l,pulse:{...S,speed:e}}))})}):null,r("p",{className:"rui-note",children:["Matches ",x(t)," of ",E," ",E===1?"object":"objects"," right now."]})]},t.id)})]})}export{pe as default};
@@ -0,0 +1 @@
1
+ import{e as ue}from"./chunk-SXN3YYD3.mjs";import{a as ne,b as se}from"./chunk-CUDL77ZY.mjs";import{e as H,f as oe,h as le,i as ce}from"./chunk-FGAJ2UT7.mjs";import"./chunk-3GA7JFNH.mjs";import{b as j}from"./chunk-3PLML3PO.mjs";import{a as te,b as ae,c as L,d as ie,e as U,g as re}from"./chunk-LX3GXQNU.mjs";import"./chunk-3I7PPFUV.mjs";import"./chunk-JR557W27.mjs";import{useEffect as ge,useMemo as we,useRef as je,useState as S}from"react";import{Copy as Ie,DiamondPlus as Ee,Plus as G,ScanLine as Pe,Trash2 as Q}from"lucide-react";import{useEffect as pe,useState as me}from"react";import{Fragment as de,jsx as g,jsxs as T}from"react/jsx-runtime";function I({label:p,value:o,onChange:r,min:l,max:u,step:f=.1,disabled:q=!1}){let[y,d]=me(String(Number(o.toFixed(5))));return pe(()=>d(String(Number(o.toFixed(5)))),[o]),T("label",{className:"sq-field",children:[g("span",{children:p}),g("input",{type:"number","aria-label":p,value:y,step:f,min:l,max:u,disabled:q,onChange:m=>d(m.target.value),onBlur:()=>{let m=y.trim()?Number(y):NaN;Number.isFinite(m)&&(l===void 0||m>=l)&&(u===void 0||m<=u)&&m!==o&&r(m),d(String(Number(o.toFixed(5))))},onKeyDown:m=>{m.stopPropagation(),m.key==="Enter"&&m.currentTarget.blur(),m.key==="Escape"&&(m.preventDefault(),d(String(o)))}})]})}function _({label:p,value:o,onChange:r}){let[l,u]=me(o);return pe(()=>u(o),[o]),T("label",{className:"sq-field",children:[g("span",{children:p}),g("input",{"aria-label":p,value:l,maxLength:256,onChange:f=>u(f.target.value),onBlur:()=>{l.trim()&&l.trim()!==o?r(l.trim()):u(o)},onKeyDown:f=>{f.stopPropagation(),f.key==="Enter"&&f.currentTarget.blur()}})]})}function X({label:p,value:o,onChange:r,degrees:l=!1}){return T("fieldset",{className:"sq-vector",children:[T("legend",{children:[p,l?" (\xB0)":""]}),g("div",{children:o.map((u,f)=>g(I,{label:["X","Y","Z"][f],value:l?u*180/Math.PI:u,onChange:q=>r(o.map((y,d)=>f===d?l?q*Math.PI/180:q:y))},f))})]})}function be({target:p,value:o,onChange:r}){if(p.kind==="camera"){let l=o;return T(de,{children:[g(X,{label:"Camera position",value:l.position,onChange:u=>r({...l,position:u})}),g(X,{label:"Camera target",value:l.target,onChange:u=>r({...l,target:u})}),T(de,{children:[l.fov!==null&&g(I,{label:"FOV (\xB0)",min:1,max:179,value:l.fov,onChange:u=>r({...l,fov:u})}),g(I,{label:"Zoom",min:.001,value:l.zoom,onChange:u=>r({...l,zoom:u})})]})]})}return Array.isArray(o)?g(X,{label:p.kind==="object"?p.property[0].toUpperCase()+p.property.slice(1):"Value",value:o,degrees:p.kind==="object"&&p.property==="rotation",onChange:r}):typeof o=="boolean"?T("label",{className:"sq-check",children:[g("input",{type:"checkbox",checked:o,onChange:l=>r(l.target.checked)}),"Visible"]}):typeof o=="string"?T("label",{className:"sq-field sq-color",children:[g("span",{children:"Color"}),g("input",{type:"color","aria-label":"Keyframe color",value:o,onChange:l=>r(l.target.value)}),g("code",{children:o})]}):g(I,{label:p.kind==="clip"?"Clip time (s)":"Value",min:0,max:p.kind==="object"&&p.property!=="emissiveIntensity"?1:void 0,step:.01,value:o,onChange:r})}import{Fragment as qe,jsx as t,jsxs as s}from"react/jsx-runtime";var E=()=>crypto.randomUUID(),fe={position:"Position",rotation:"Rotation",scale:"Scale",visible:"Visibility",baseColor:"Color",emissive:"Emissive color",opacity:"Opacity",roughness:"Roughness",metalness:"Metalness",emissiveIntensity:"Emissive intensity"},Ae={linear:"Linear","ease-in":"Ease in","ease-out":"Ease out","ease-in-out":"Ease in / out",step:"Hold / step"};function Me({config:p,onChange:o,controls:r,bindings:l,clipDetails:u}){let{tier:f}=le(),q=ne(r),y=we(()=>{try{return{sequences:U(p.sequences??[]),error:""}}catch(e){return{sequences:[],error:e instanceof Error?e.message:"Invalid sequence data."}}},[p.sequences]),d=y.sequences,[Y,m]=S(null),[ke,N]=S(null),[he,x]=S(null),[w,ve]=S("object"),[ye,xe]=S(""),[z,Se]=S("position"),[Z,Ne]=S(""),[J,A]=S(""),[K,B]=S(null),M=je(null),i=d.find(e=>e.id===Y)??d[0],c=i?.tracks.find(e=>e.id===ke)??i?.tracks[0],h=c?.keyframes.find(e=>e.id===he)??c?.keyframes[0],R=Object.values(l).filter((e,a,k)=>!e.removed&&k.findIndex(n=>n.modelObjectId===e.modelObjectId)===a),O=R.find(e=>e.modelObjectId===ye)??R[0],F=u.some(e=>e.sourceName===Z)?Z:u[0]?.sourceName;ge(()=>{i&&r&&q.sequenceId!==i.id&&r.select(i.id)},[r,i?.id,q]),ge(()=>()=>r?.stop(),[r]);let V=e=>{try{let a=U(e);o({...p,sequences:a}),A("")}catch(a){A(a instanceof Error?a.message:"Invalid sequence.")}},P=e=>V(d.map(a=>a.id===e.id?e:a)),D=e=>{i&&P({...i,tracks:i.tracks.map(a=>a.id===e.id?e:a)})},C=(e,a)=>{if(!i||!ie(a.value,e.target)){A("Use a valid value; scale components must be nonzero.");return}D(re(e,{...a,time:Math.max(0,Math.min(i.duration,a.time))})),N(e.id),x(a.id),r?.pause(),r?.seek(a.time)},W=()=>{if(d.length>=64)return;let e={id:E(),name:`Sequence ${d.length+1}`,duration:5,loop:!1,autoPlay:!1,tracks:[]};V([...d,e]),m(e.id),N(null),x(null)},v=w==="camera"?{kind:"camera"}:w==="clip"?F?{kind:"clip",clip:F}:null:O?{kind:"object",objectId:O.modelObjectId,property:z}:null,$=!!v&&i?.tracks.some(e=>L(e.target)===L(v)),Ce=()=>{if(!i||!v||$)return;let e=r?.capture(v);if(e==null){A("That target is not ready. Wait for the model, or choose another object/clip.");return}let a=v.kind==="clip"?Math.min(i.duration,u.find(n=>n.sourceName===v.clip)?.duration??i.duration):structuredClone(e),k={id:E(),name:v.kind==="camera"?"Camera":v.kind==="clip"?v.clip:`${O?.label||O?.modelObjectId} \xB7 ${fe[z]}`,target:v,keyframes:[{id:E(),time:0,value:e,easing:z==="visible"&&w==="object"?"step":"ease-in-out"},{id:E(),time:i.duration,value:a}]};P({...i,tracks:[...i.tracks,k]}),N(k.id),x(k.keyframes[1].id),r?.seek(i.duration)},Te=()=>{if(!c||!i||c.keyframes.length>=2048)return;let e=r?.capture(c.target);if(e==null){A("The selected target is unavailable.");return}let a={id:E(),time:Math.min(q.time,i.duration),value:e,easing:"ease-in-out"};C(c,a)};return s("section",{className:"sq-editor","aria-label":"Visual sequence authoring",children:[y.error&&t("p",{role:"alert",className:"sq-error",children:y.error}),H(f,"sequences",d.length)?null:t(ce,{requiredTier:oe("sequences",d.length),feature:"More sequences"}),i?s(qe,{children:[s("div",{className:"sq-toolbar",children:[t(j,{className:"se-select sq-toolbarPicker","aria-label":"Authored sequence",value:i.id,onChange:e=>{m(e.target.value),N(null),x(null),r?.select(e.target.value)},children:d.map(e=>t("option",{value:e.id,children:e.name},e.id))}),t("button",{type:"button",className:"se-iconButton",title:"Add sequence","aria-label":"Add sequence",disabled:d.length>=64||!H(f,"sequences",d.length),onClick:W,children:t(G,{size:14})}),t("button",{type:"button",className:"se-iconButton",title:"Duplicate sequence","aria-label":"Duplicate sequence",disabled:d.length>=64,onClick:()=>{let e={...structuredClone(i),id:E(),name:`${i.name} copy`.slice(0,256),autoPlay:!1};V([...d,e]),m(e.id)},children:t(Ie,{size:13})}),t("button",{type:"button",className:"se-iconButton",title:"Delete sequence","aria-label":"Delete sequence",onClick:()=>{r?.stop(),V(d.filter(e=>e.id!==i.id)),m(null)},children:t(Q,{size:13})}),t("span",{className:"sq-toolbarDivider"}),t(_,{label:"Name",value:i.name,onChange:e=>P({...i,name:e})}),t(I,{label:"Duration",min:.1,max:3600,value:i.duration,onChange:e=>P({...i,duration:e,tracks:i.tracks.map(a=>({...a,keyframes:a.keyframes.map(k=>({...k,time:k.time/i.duration*e}))}))})}),s("label",{className:"sq-check",children:[t("input",{type:"checkbox",checked:i.loop,onChange:e=>P({...i,loop:e.target.checked})}),"Loop"]}),s("label",{className:"sq-check",children:[t("input",{type:"checkbox",checked:i.autoPlay,onChange:e=>V(d.map(a=>({...a,autoPlay:a.id===i.id?e.target.checked:!1})))}),"Autoplay"]})]}),s("div",{className:"sq-body",children:[s("div",{className:"sq-main",children:[t(se,{controls:r,duration:i.duration,hasTracks:i.tracks.some(e=>!e.muted&&e.keyframes.length>0)}),s("div",{className:"sq-timeline","aria-label":"Sequence timeline",title:"Drag keys to retime. Arrow keys move 0.1s; Shift moves 0.01s. A key at the same time is replaced.",children:[s("div",{className:"sq-ruler",children:[t("span",{className:"sq-rulerTitle",children:"Tracks"}),t("div",{className:"sq-rulerScale",children:[0,.25,.5,.75,1].map(e=>s("span",{children:[(i.duration*e).toFixed(1),"s"]},e))})]}),!i.tracks.length&&t("p",{className:"sq-hint",children:"Add a track to create its start and end keyframes."}),i.tracks.map(e=>s("div",{className:`sq-track ${c?.id===e.id?"sq-track--active":""}`,children:[s("div",{className:"sq-trackLabel",children:[t("button",{type:"button",title:e.name,onClick:()=>{N(e.id),x(e.keyframes[0]?.id??null)},children:e.name}),s("label",{title:"Mute track",children:[t("input",{"aria-label":`Mute ${e.name}`,type:"checkbox",checked:!!e.muted,onChange:a=>D({...e,muted:a.target.checked})}),"Mute"]})]}),s("div",{className:"sq-lane",role:"group","aria-label":`${e.name} keyframes`,onDoubleClick:a=>{if(a.target!==a.currentTarget)return;let k=a.currentTarget.getBoundingClientRect(),n=Math.round(Math.max(0,Math.min(1,(a.clientX-k.left)/k.width))*i.duration*100)/100,b=r?.capture(e.target);b!=null&&C(e,{id:E(),time:n,value:b})},children:[t("span",{className:"sq-playhead",style:{left:`${q.time/i.duration*100}%`}}),e.keyframes.map(a=>{let k=K?.frameId===a.id&&K.trackId===e.id?K.time:a.time;return t("button",{type:"button",className:`sq-key ${h?.id===a.id&&c?.id===e.id?"sq-key--active":""}`,style:{left:`${k/i.duration*100}%`},"aria-label":`${e.name} keyframe at ${k.toFixed(2)} seconds`,"aria-pressed":h?.id===a.id&&c?.id===e.id,onClick:n=>{n.detail===0&&(N(e.id),x(a.id),r?.pause(),r?.seek(a.time))},onPointerDown:n=>{if(n.button!==0)return;n.preventDefault(),n.stopPropagation(),n.currentTarget.focus(),n.currentTarget.setPointerCapture(n.pointerId);let b=n.currentTarget.parentElement.getBoundingClientRect();M.current={track:e,frame:a,left:b.left,width:b.width,time:a.time},N(e.id),x(a.id),r?.pause(),r?.seek(a.time)},onPointerMove:n=>{let b=M.current;if(!b||b.frame.id!==a.id||b.track.id!==e.id)return;let ee=n.shiftKey?.01:.1;b.time=Math.min(i.duration,Math.max(0,Math.round((n.clientX-b.left)/b.width*i.duration/ee)*ee)),B({trackId:e.id,frameId:a.id,time:b.time}),r?.seek(b.time)},onPointerUp:()=>{let n=M.current;n&&(n.time!==n.frame.time&&C(n.track,{...n.frame,time:n.time}),M.current=null,B(null))},onPointerCancel:()=>{M.current=null,B(null),r?.seek(a.time)},onKeyDown:n=>{if(!["ArrowLeft","ArrowRight","Home","End"].includes(n.key))return;n.preventDefault(),n.stopPropagation();let b=n.key==="Home"?0:n.key==="End"?i.duration:a.time+(n.key==="ArrowLeft"?-1:1)*(n.shiftKey?.01:.1);C(e,{...a,time:b})}},a.id)})]})]},e.id))]})]}),s("aside",{className:"sq-side",children:[s(ue,{title:"Add track",children:[s("label",{className:"sq-field",children:[t("span",{children:"Type"}),s(j,{"aria-label":"Track type",value:w,onChange:e=>ve(e.target.value),children:[t("option",{value:"object",children:"Object"}),t("option",{value:"camera",children:"Camera"}),t("option",{value:"clip",children:"Animation clip"})]})]}),w==="object"&&s("label",{className:"sq-field",children:[t("span",{children:"Property"}),t(j,{"aria-label":"Track property",value:z,onChange:e=>Se(e.target.value),children:te.map(e=>t("option",{value:e,children:fe[e]},e))})]}),w==="object"&&s("label",{className:"sq-field",children:[t("span",{children:"Object"}),t(j,{"aria-label":"Track object",value:O?.modelObjectId??"",onChange:e=>xe(e.target.value),children:R.map(e=>t("option",{value:e.modelObjectId,children:e.label||e.modelObjectId},e.modelObjectId))})]}),w==="clip"&&s("label",{className:"sq-field",children:[t("span",{children:"Clip"}),t(j,{"aria-label":"Track animation clip",value:F??"",onChange:e=>Ne(e.target.value),children:u.map(e=>t("option",{value:e.sourceName,children:e.sourceName},e.sourceName))}),!u.length&&t("small",{children:"This model has no animation clips."})]}),t("div",{className:"rui-actions",children:s("button",{type:"button",className:"bb-button",onClick:Ce,disabled:!v||!q.ready||$||i.tracks.length>=128,children:[t(G,{size:13,"aria-hidden":"true"}),"Add track"]})}),$&&t("p",{className:"rui-note",children:"That property already has a track."})]}),c&&s("section",{className:"sq-inspector","aria-label":"Selected track and keyframe",children:[s("div",{className:"rui-groupHeader",children:[t("span",{children:"Selected track"}),t("button",{type:"button",className:"se-iconButton",title:"Delete track","aria-label":"Delete track",onClick:()=>{P({...i,tracks:i.tracks.filter(e=>e.id!==c.id)}),N(null),x(null)},children:t(Q,{size:13})})]}),t(_,{label:"Name",value:c.name,onChange:e=>D({...c,name:e})}),t("div",{className:"rui-actions",children:s("button",{type:"button",className:"bb-button",disabled:!q.ready||c.keyframes.length>=2048,onClick:Te,children:[t(Ee,{size:13,"aria-hidden":"true"}),"Add keyframe at playhead"]})}),h&&s(qe,{children:[s("div",{className:"rui-groupHeader",children:[t("span",{children:"Keyframe"}),t("button",{type:"button",className:"se-iconButton",title:"Delete keyframe","aria-label":"Delete keyframe",onClick:()=>{D({...c,keyframes:c.keyframes.filter(e=>e.id!==h.id)}),x(null)},children:t(Q,{size:14})})]}),t(I,{label:"Time",min:0,max:i.duration,value:h.time,step:.01,onChange:e=>C(c,{...h,time:e})}),s("label",{className:"sq-field",children:[t("span",{children:"Easing"}),t(j,{"aria-label":"Keyframe easing",value:c.target.kind==="object"&&c.target.property==="visible"?"step":h.easing??"ease-in-out",disabled:c.target.kind==="object"&&c.target.property==="visible",onChange:e=>C(c,{...h,easing:e.target.value}),children:ae.map(e=>t("option",{value:e,children:Ae[e]},e))})]}),t(be,{target:c.target,value:h.value,onChange:e=>C(c,{...h,value:e})}),t("div",{className:"rui-actions",children:s("button",{type:"button",className:"bb-button",disabled:!q.ready,onClick:()=>{let e=r?.capture(c.target);e!=null&&C(c,{...h,value:e})},children:[t(Pe,{size:13,"aria-hidden":"true"}),"Capture current value"]})})]})]})]})]})]}):s("div",{className:"sq-empty",children:[t("p",{children:"Add a sequence, then add tracks and capture keyframes on the timeline."}),s("button",{type:"button",className:"bb-button bb-button--primary",onClick:W,children:[t(G,{size:13,"aria-hidden":"true"}),"Create sequence"]})]}),(J||q.issues.length>0)&&t("div",{role:"status",className:"sq-error",children:J||q.issues.join(" ")})]})}export{Me as default};
@@ -0,0 +1 @@
1
+ .sq-editor{color:var(--ri-ui-text-2);padding:12px;font-size:12px}.sq-header,.sq-picker,.sq-inspectorTitle,.sq-settings{display:flex;align-items:center;gap:10px}.sq-header{justify-content:space-between;margin-bottom:16px}.sq-header h2{margin:0;font-size:13px}.sq-header p,.sq-hint{color:var(--ri-ui-text-2);font-size:11px;line-height:1.5}.sq-header p{margin:6px 0 0}.sq-picker .sq-field{flex:1;min-width:0}.sq-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px;min-width:0}.sq-field>span{font-size:11px;color:var(--ri-ui-text-2)}.sq-field input,.sq-field :is(select,.ri-select){width:100%;min-width:0;box-sizing:border-box;border:1px solid var(--ri-ui-line);border-radius:var(--ri-ui-radius-m);padding:8px;color:var(--ri-ui-text);background:var(--ri-ui-raised);font:inherit}.sq-field input:focus-visible,.sq-field :is(select,.ri-select):focus-visible,.sq-editor button:focus-visible{outline:2px solid var(--ri-ui-accent);outline-offset:2px}.sq-settings{flex-wrap:wrap;align-items:center;margin:10px 0}.sq-settings .sq-field{width:105px;margin:0}.sq-check{display:flex;align-items:center;gap:6px;color:var(--ri-ui-text-2);font-size:11px}.sq-editor input[type=checkbox]{accent-color:var(--ri-ui-accent)}.sq-twoFields{display:grid;grid-template-columns:1fr 1fr;gap:10px}.sq-vector{border:0;padding:0;margin:10px 0;min-width:0}.sq-vector legend{color:var(--ri-ui-text-2);margin-bottom:8px}.sq-vector>div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px}.sq-color{flex-direction:row;align-items:center;gap:12px}.sq-color input{width:40px;height:32px;padding:3px}.sq-empty{padding:24px 12px;border:1px dashed var(--ri-ui-line-strong);border-radius:var(--ri-ui-radius-m);text-align:center;line-height:1.6;color:var(--ri-ui-text-2)}.sq-empty button{margin-top:8px}.sq-timeline{margin-top:16px;padding:10px;border:1px solid var(--ri-ui-line);border-radius:var(--ri-ui-radius-m);background:transparent}.sq-ruler{display:flex;justify-content:space-between;color:var(--ri-ui-text-2);font-size:11px;padding-bottom:10px;font-variant-numeric:tabular-nums}.sq-track{margin-bottom:10px;border-radius:var(--ri-ui-radius-s);padding:7px 8px;background:var(--ri-ui-hover);border:1px solid transparent}.sq-track--active{border-color:var(--ri-ui-accent);background:var(--ri-ui-accent-dim)}.sq-trackLabel{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.sq-trackLabel>button{min-width:0;border:0;padding:0;color:var(--ri-ui-text-2);background:none;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:inherit;cursor:pointer}.sq-trackLabel label{display:flex;gap:4px;align-items:center;font-size:11px;color:var(--ri-ui-text-2)}.sq-lane{position:relative;height:28px;margin:0 8px;background:repeating-linear-gradient(90deg,transparent,transparent calc(25% - 1px),rgba(148,163,184,.12) calc(25% - 1px),rgba(148,163,184,.12) 25%);border-bottom:1px solid var(--ri-ui-line)}.sq-key{position:absolute;top:8px;width:12px;height:12px;padding:0;border:1px solid var(--ri-ui-line-strong);background:var(--ri-ui-field);border-radius:2px;transform:translate(-50%) rotate(45deg);cursor:ew-resize;touch-action:none}.sq-key--active{background:var(--ri-ui-accent);border-color:var(--ri-ui-accent)}.sq-playhead{position:absolute;top:0;bottom:-1px;width:1px;background:var(--ri-ui-warning);pointer-events:none}.sq-addTrack,.sq-inspector{border-top:1px solid var(--ri-ui-line);padding-top:14px;margin-top:18px}.sq-editor h3{margin:0 0 12px;font-size:12px;font-weight:600}.sq-inspectorTitle{justify-content:space-between;margin-bottom:10px}.sq-inspectorTitle h3{margin:0}.sq-inspector>button{margin-bottom:16px}.sq-error{border:1px solid var(--ri-ui-warning);border-radius:var(--ri-ui-radius-m);padding:10px;color:var(--ri-ui-warning);background:var(--ri-ui-warning-dim);line-height:1.5}.sq-key{top:3px;width:22px;height:22px;border:0;background:transparent;transform:translate(-50%)}.sq-key:before{content:"";position:absolute;left:5px;top:5px;width:10px;height:10px;border:1px solid var(--ri-ui-line-strong);border-radius:2px;background:var(--ri-ui-field);transform:rotate(45deg)}.sq-key--active:before{background:var(--ri-ui-accent);border-color:var(--ri-ui-accent)}