@mml-io/3d-web-experience-client 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/build/Networked3dWebExperienceClient.d.ts +10 -13
  2. package/build/avatar-selection-ui/AvatarSelectionUI.d.ts +20 -0
  3. package/build/avatar-selection-ui/AvatarType.d.ts +21 -0
  4. package/build/avatar-selection-ui/components/AvatarPanel/AvatarSectionUIComponent.d.ts +14 -0
  5. package/build/avatar-selection-ui/index.d.ts +2 -0
  6. package/build/chat-ui/TextChatUI.d.ts +25 -0
  7. package/build/chat-ui/components/ChatPanel/TextChatUIComponent.d.ts +9 -0
  8. package/build/chat-ui/components/Input/InputBox.d.ts +9 -0
  9. package/build/chat-ui/components/Message/Message.d.ts +9 -0
  10. package/build/chat-ui/components/Messages/Messages.d.ts +12 -0
  11. package/build/chat-ui/helpers.d.ts +3 -0
  12. package/build/chat-ui/index.d.ts +1 -0
  13. package/build/index.css +875 -0
  14. package/build/index.css.map +7 -0
  15. package/build/index.js +1253 -218
  16. package/build/index.js.map +4 -4
  17. package/build/src/Networked3dWebExperience.module.css.d.ts +4 -0
  18. package/build/src/Networked3dWebExperience.module.d.css.ts +4 -0
  19. package/build/src/avatar-selection-ui/components/AvatarPanel/AvatarSelectionUIComponent.module.css.d.ts +26 -0
  20. package/build/src/avatar-selection-ui/components/AvatarPanel/AvatarSelectionUIComponent.module.d.css.ts +26 -0
  21. package/build/src/chat-ui/components/ChatPanel/TextChatUIComponent.module.css.d.ts +15 -0
  22. package/build/src/chat-ui/components/ChatPanel/TextChatUIComponent.module.d.css.ts +15 -0
  23. package/build/src/chat-ui/components/Input/InputBox.module.css.d.ts +7 -0
  24. package/build/src/chat-ui/components/Input/InputBox.module.d.css.ts +7 -0
  25. package/build/src/chat-ui/components/Message/Message.module.css.d.ts +5 -0
  26. package/build/src/chat-ui/components/Message/Message.module.d.css.ts +5 -0
  27. package/build/src/chat-ui/components/Messages/Messages.module.css.d.ts +5 -0
  28. package/build/src/chat-ui/components/Messages/Messages.module.d.css.ts +5 -0
  29. package/package.json +13 -10
package/build/index.js CHANGED
@@ -1,12 +1,11 @@
1
+ globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__']=".AvatarSelectionUIComponent-module__menuButton_VjZ7cq__0230{z-index:102;user-select:none;width:70px;min-height:70px;position:absolute;top:0;right:0}.AvatarSelectionUIComponent-module__input_VjZ7cq__0230{color:#fff;backdrop-filter:blur(10px);background-color:#0006;border:1px solid #fff3;border-radius:8px;outline:none;flex:1;margin-right:6px;padding:6px 8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;font-weight:400;transition:all .2s ease-in-out}.AvatarSelectionUIComponent-module__input_VjZ7cq__0230:focus{background-color:#0009;border-color:#fff6;box-shadow:0 0 0 3px #ffffff26}.AvatarSelectionUIComponent-module__input_VjZ7cq__0230::placeholder{color:#ffffff80}.AvatarSelectionUIComponent-module__closeButton_VjZ7cq__0230{backdrop-filter:blur(10px);color:#fffc;cursor:pointer;user-select:none;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:44px;height:44px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:16px;font-weight:500;transition:all .2s ease-in-out;display:flex;position:absolute;top:8px;right:8px}.AvatarSelectionUIComponent-module__closeButton_VjZ7cq__0230:hover{color:#ffffffe6;background:#000c;transform:scale(1.05)}.AvatarSelectionUIComponent-module__closeButton_VjZ7cq__0230:active{transform:scale(.95)}.AvatarSelectionUIComponent-module__openTab_VjZ7cq__0230{backdrop-filter:blur(10px);cursor:pointer;user-select:none;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s ease-in-out;display:flex;position:absolute;top:8px;right:8px}.AvatarSelectionUIComponent-module__openTab_VjZ7cq__0230:hover{background:#000c;transform:scale(1.05)}.AvatarSelectionUIComponent-module__openTab_VjZ7cq__0230:active{transform:scale(.95)}.AvatarSelectionUIComponent-module__openTab_VjZ7cq__0230 img{filter:invert(80%);width:24px;height:24px;transition:all .2s ease-in-out}.AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230{backdrop-filter:blur(20px);z-index:103;user-select:none;background:#000000bf;border:1px solid #fff3;border-radius:16px;width:400px;max-width:78vw;max-height:calc(100vh - 24px);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;position:absolute;top:8px;right:60px;overflow:hidden auto;box-shadow:0 8px 32px #0006,0 0 0 1px #ffffff1a}.AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230::-webkit-scrollbar{width:8px}.AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230::-webkit-scrollbar-track{background:none}.AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230::-webkit-scrollbar-thumb{background:#fff3;border:1px solid #ffffff1a;border-radius:4px}.AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230::-webkit-scrollbar-thumb:hover{background:#ffffff4d}.AvatarSelectionUIComponent-module__avatarSelectionSection_VjZ7cq__0230{border-bottom:1px solid #ffffff26;padding:10px}.AvatarSelectionUIComponent-module__avatarSelectionSection_VjZ7cq__0230:last-child{border-bottom:none}.AvatarSelectionUIComponent-module__avatarSelectionUi_VjZ7cq__0230{background:#0000004d;border:1px solid #ffffff26;border-radius:12px;grid-template-columns:repeat(auto-fill,minmax(85px,1fr));gap:8px;max-height:320px;padding:10px;display:grid;overflow:hidden auto}.AvatarSelectionUIComponent-module__avatarSelectionUi_VjZ7cq__0230::-webkit-scrollbar{width:6px}.AvatarSelectionUIComponent-module__avatarSelectionUi_VjZ7cq__0230::-webkit-scrollbar-track{background:none}.AvatarSelectionUIComponent-module__avatarSelectionUi_VjZ7cq__0230::-webkit-scrollbar-thumb{background:#ffffff26;border-radius:3px}.AvatarSelectionUIComponent-module__avatarSelectionUiHeader_VjZ7cq__0230{color:#ffffffe6;text-align:center;font-weight:600;position:relative}.AvatarSelectionUIComponent-module__avatarSelectionUiCloseButton_VjZ7cq__0230{position:absolute;top:20px;right:20px}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230{color:#ffffffe6;text-align:center;cursor:pointer;border-radius:8px;padding:4px;transition:all .2s ease-in-out}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230:hover{background:#0000004d;transform:translateY(-2px)}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImgContainer_VjZ7cq__0230{border-radius:8px;position:relative;overflow:hidden}.AvatarSelectionUIComponent-module__avatarSelectionNoImage_VjZ7cq__0230{aspect-ratio:1;box-sizing:border-box;background:#0003;border:1px solid #ffffff26;border-radius:8px;justify-content:center;align-items:center;width:100%;display:flex}.AvatarSelectionUIComponent-module__avatarSelectionNoImage_VjZ7cq__0230 img{filter:invert(70%);opacity:.7;width:40%}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230 p{text-overflow:ellipsis;white-space:nowrap;color:#fffc;margin:4px 0 0;font-size:12px;font-weight:500;position:relative;overflow:hidden}.AvatarSelectionUIComponent-module__tooltipText_VjZ7cq__0230{pointer-events:none;z-index:1000;color:#ffffffe6;backdrop-filter:blur(10px);opacity:0;visibility:hidden;white-space:nowrap;background:#000c;border-radius:8px;margin-bottom:4px;padding:4px 6px;font-size:12px;font-weight:500;transition:opacity .2s ease-in-out;position:absolute;bottom:100%;left:50%;transform:translate(-50%)}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230 p:hover+.AvatarSelectionUIComponent-module__tooltipText_VjZ7cq__0230{opacity:1;visibility:visible;transition-delay:.5s}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImgContainer_VjZ7cq__0230 .AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImage_VjZ7cq__0230{aspect-ratio:1;border-radius:8px;width:100%;transition:all .2s ease-in-out}.AvatarSelectionUIComponent-module__selectedPill_VjZ7cq__0230{color:#fff;backdrop-filter:blur(10px);background:linear-gradient(135deg,#22c55e,#16a34a);border:1px solid #fff3;border-radius:6px;padding:2px 4px;font-size:11px;font-weight:600;box-shadow:0 2px 4px #0003}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImgContainer_VjZ7cq__0230 .AvatarSelectionUIComponent-module__selectedPill_VjZ7cq__0230{z-index:2;position:absolute;top:4px;right:4px}.AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230 img:hover{opacity:.8;transform:scale(1.05)}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230{color:#ffffffe6;border-bottom:1px solid #ffffff26;padding:10px;position:relative}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230:last-child{border-bottom:none}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 .AvatarSelectionUIComponent-module__radioGroup_VjZ7cq__0230{flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px;display:flex}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 .AvatarSelectionUIComponent-module__radioItem_VjZ7cq__0230{white-space:nowrap;align-items:center;gap:3px;margin-right:6px;display:flex}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 label{color:#fffc;cursor:pointer;margin:0;font-size:14px;font-weight:500;transition:color .2s ease-in-out}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 label:hover{color:#fff}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio]{appearance:none;cursor:pointer;background:none;border:2px solid #ffffff4d;border-radius:50%;outline:none;width:18px;height:18px;transition:all .2s ease-in-out;position:relative}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio]:hover{border-color:#ffffff80}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio]:focus{box-shadow:0 0 0 3px #ffffff1a}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio]:checked{background:#ffffff1a;border-color:#fffc}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio]:checked:after{content:\"\";background:#ffffffe6;border-radius:50%;width:8px;height:8px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 input[type=radio][disabled]{opacity:.4;cursor:not-allowed}.AvatarSelectionUIComponent-module__customAvatarInputSection_VjZ7cq__0230{align-items:stretch;display:flex}.AvatarSelectionUIComponent-module__setButton_VjZ7cq__0230{color:#ffffffe6;backdrop-filter:blur(10px);cursor:pointer;background:linear-gradient(135deg,#0009,#0006);border:1px solid #fff3;border-radius:8px;justify-content:center;align-items:center;height:44px;padding-left:16px;padding-right:16px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;font-weight:500;transition:all .2s ease-in-out;display:flex}.AvatarSelectionUIComponent-module__setButton_VjZ7cq__0230:hover:not(:disabled){background:linear-gradient(135deg,#000c,#0009);border-color:#ffffff4d;transform:translateY(-1px);box-shadow:0 4px 8px #0000004d}.AvatarSelectionUIComponent-module__setButton_VjZ7cq__0230:active:not(:disabled){transform:translateY(0);box-shadow:0 2px 4px #0003}.AvatarSelectionUIComponent-module__setButton_VjZ7cq__0230:disabled{opacity:.5;cursor:not-allowed;transform:none}.AvatarSelectionUIComponent-module__sectionHeading_VjZ7cq__0230{color:#ffffffe6;letter-spacing:-.01em;margin-bottom:8px;font-size:18px;font-weight:600}.AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230 .AvatarSelectionUIComponent-module__selectedPill_VjZ7cq__0230{margin-left:8px;position:relative;top:-2px}.AvatarSelectionUIComponent-module__displayNameSection_VjZ7cq__0230{color:#ffffffe6;border-bottom:1px solid #ffffff26;padding:10px;position:relative}.AvatarSelectionUIComponent-module__displayNameInputSection_VjZ7cq__0230{align-items:stretch;display:flex}.InputBox-module__inputWrapper_adOgOW__0230{pointer-events:all;align-items:stretch;gap:6px;display:flex}.InputBox-module__chatInput_adOgOW__0230{color:#fff;backdrop-filter:blur(10px);box-sizing:border-box;background:#0006;border:1px solid #fff3;border-radius:8px;outline:none;flex:1;height:36px;padding:0 8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;font-weight:400;transition:all .2s ease-in-out}.InputBox-module__chatInput_adOgOW__0230:focus{background:#0009;border-color:#fff6;box-shadow:0 0 0 3px #ffffff26}.InputBox-module__chatInput_adOgOW__0230::placeholder{color:#ffffff80}.InputBox-module__sendButton_adOgOW__0230{color:#ffffffe6;backdrop-filter:blur(10px);cursor:pointer;background:linear-gradient(135deg,#0009,#0006);border:1px solid #fff3;border-radius:8px;outline:none;justify-content:center;align-items:center;width:36px;height:36px;transition:all .2s ease-in-out;display:flex}.InputBox-module__sendButton_adOgOW__0230:hover{background:linear-gradient(135deg,#000c,#0009);border-color:#ffffff4d;transform:translateY(-1px);box-shadow:0 4px 8px #0000004d}.InputBox-module__sendButton_adOgOW__0230:active{transform:translateY(0);box-shadow:0 2px 4px #0003}.InputBox-module__sendButton_adOgOW__0230 .InputBox-module__svgIcon_adOgOW__0230 img{filter:invert(80%);width:18px;height:18px;transition:all .2s ease-in-out}.InputBox-module__sendButton_adOgOW__0230:hover .InputBox-module__svgIcon_adOgOW__0230 img{filter:invert(90%)}.Message-module__messageContainer_ikOQiq__0230{backdrop-filter:blur(8px);word-break:break-word;color:#ffffffe6;direction:ltr;background:#0000004d;border:1px solid #fff3;border-radius:8px;width:fit-content;margin:6px auto 6px 2px;padding:4px 8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;transition:all .2s ease-in-out;overflow-x:hidden}.Message-module__messageContainer_ikOQiq__0230:hover{background:#0006;border-color:#ffffff4d}.Message-module__userName_ikOQiq__0230{color:#ffffffe6;margin-right:4px;font-weight:600}.Messages-module__messagesContainer_LXaUUW__0230{z-index:1000;max-height:inherit;scrollbar-width:thin;scrollbar-color:#fff3 transparent;pointer-events:fill;direction:rtl;position:relative;bottom:0;left:-1px;overflow-y:auto}.Messages-module__messagesContainer_LXaUUW__0230::-webkit-scrollbar{width:6px}.Messages-module__messagesContainer_LXaUUW__0230::-webkit-scrollbar-track{background:none}.Messages-module__messagesContainer_LXaUUW__0230::-webkit-scrollbar-thumb{background:#fff3;border:1px solid #ffffff1a;border-radius:3px}.Messages-module__messagesContainer_LXaUUW__0230::-webkit-scrollbar-thumb:hover{background:#ffffff4d}.Messages-module__newMessagesButton_LXaUUW__0230{-webkit-backdrop-filter:blur(10px);color:#fff;cursor:pointer;z-index:1001;direction:ltr;background:#000000bf;border:1px solid #fff3;border-radius:20px;padding:8px 16px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:14px;font-weight:500;transition:all .2s;position:absolute;bottom:50px;left:50%;transform:translate(-50%);box-shadow:0 4px 12px #0000004d}.Messages-module__newMessagesButton_LXaUUW__0230:hover{background:#000000d9;border-color:#ffffff4d;transform:translate(-50%)translateY(-2px);box-shadow:0 6px 16px #0006}.Messages-module__newMessagesButton_LXaUUW__0230:active{transform:translate(-50%)translateY(0);box-shadow:0 2px 8px #0000004d}.TextChatUIComponent-module__uiHover_gFDdcW__0230{z-index:102;pointer-events:all;width:58px;min-height:58px;position:absolute;bottom:0;left:0}.TextChatUIComponent-module__textChatUi_gFDdcW__0230{flex-direction:row;align-items:flex-end;width:100%;max-width:500px;height:0;display:flex;position:absolute;bottom:0;left:0}.TextChatUIComponent-module__textChat_gFDdcW__0230{z-index:-1;color:#fff;user-select:none;opacity:0;flex-direction:column;justify-content:space-between;width:100%;max-height:500px;margin-bottom:8px;margin-left:60px;padding:5px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;transition:opacity .25s ease-in-out,transform .2s ease-in-out;display:flex;transform:translate(-100%)}.TextChatUIComponent-module__fadeIn_gFDdcW__0230{opacity:1;transform:translate(0)}.TextChatUIComponent-module__fadeOut_gFDdcW__0230{opacity:.6;transform:translate(calc(-100% - 72px))}.TextChatUIComponent-module__controls_gFDdcW__0230{position:absolute;top:2px;right:0}.TextChatUIComponent-module__openTab_gFDdcW__0230{backdrop-filter:blur(10px);cursor:pointer;z-index:102;user-select:none;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:44px;height:44px;transition:all .2s ease-in-out;display:flex;position:absolute;bottom:8px;left:8px}.TextChatUIComponent-module__openTab_gFDdcW__0230:hover{background:#000c;border-color:#ffffff4d;transform:scale(1.05)}.TextChatUIComponent-module__openTab_gFDdcW__0230:active{transform:scale(.95)}.TextChatUIComponent-module__openTab_gFDdcW__0230 img{filter:invert(80%);width:24px;height:24px;transition:all .2s ease-in-out}.TextChatUIComponent-module__stickyButton_gFDdcW__0230{z-index:103;backdrop-filter:blur(10px);cursor:pointer;user-select:none;opacity:1;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;transition:all .2s ease-in-out;display:flex;position:absolute;bottom:38px;left:38px}.TextChatUIComponent-module__stickyButton_gFDdcW__0230:hover{background:#000c;border-color:#ffffff4d;transform:scale(1.05)}.TextChatUIComponent-module__stickyButtonFadeOut_gFDdcW__0230{z-index:103;backdrop-filter:blur(10px);cursor:pointer;user-select:none;opacity:0;pointer-events:none;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;transition:all .2s ease-in-out;display:flex;position:absolute;bottom:42px;left:42px}.TextChatUIComponent-module__stickyButtonEnabled_gFDdcW__0230{z-index:103;backdrop-filter:blur(10px);cursor:pointer;user-select:none;background:#000c;border:1px solid #22c55e99;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;transition:all .2s ease-in-out;display:flex;position:absolute;bottom:42px;left:42px;box-shadow:0 0 8px #22c55e4d}.TextChatUIComponent-module__stickyButtonEnabled_gFDdcW__0230:hover{border-color:#22c55ecc;transform:scale(1.05);box-shadow:0 0 12px #22c55e66}.TextChatUIComponent-module__stickyButton_gFDdcW__0230 img,.TextChatUIComponent-module__stickyButtonFadeOut_gFDdcW__0230 img,.TextChatUIComponent-module__stickyButtonEnabled_gFDdcW__0230 img{filter:invert(70%);width:12px;height:12px;transition:all .2s ease-in-out}.TextChatUIComponent-module__stickyButton_gFDdcW__0230:hover img,.TextChatUIComponent-module__stickyButtonEnabled_gFDdcW__0230:hover img{filter:invert(90%)}.TextChatUIComponent-module__controls_gFDdcW__0230 .TextChatUIComponent-module__closeButton_gFDdcW__0230{color:#fffc;backdrop-filter:blur(10px);cursor:pointer;background:#0009;border:1px solid #fff3;border-radius:50%;justify-content:center;align-items:center;width:28px;height:28px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;font-weight:500;transition:all .2s ease-in-out;display:flex;position:absolute;top:3px;right:3px}.TextChatUIComponent-module__closeButton_gFDdcW__0230:hover{color:#ffffffe6;background:#000c;border-color:#ffffff4d;transform:scale(1.05)}.TextChatUIComponent-module__closeButton_gFDdcW__0230:active{transform:scale(.95)}.TextChatUIComponent-module__messagesWrapper_gFDdcW__0230{direction:rtl;width:fit-content;max-height:450px;margin-bottom:8px;position:relative}.Networked3dWebExperience-module__respawnButton_7g9l0W__0230{z-index:102;color:#ffffffe6;backdrop-filter:blur(10px);cursor:pointer;user-select:none;background:linear-gradient(135deg,#0009,#0006);border:1px solid #fff3;border-radius:8px;justify-content:center;align-items:center;padding:12px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;font-weight:500;transition:all .2s ease-in-out;display:flex;position:absolute;top:8px;left:8px}.Networked3dWebExperience-module__respawnButton_7g9l0W__0230:hover{background:linear-gradient(135deg,#000c,#0009);border-color:#ffffff4d;transform:translateY(-1px)}.Networked3dWebExperience-module__respawnButton_7g9l0W__0230:active{transform:translateY(0)}\n";globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__']="693110aba86e39d3ac419c61db5456a8";
1
2
  // src/Networked3dWebExperienceClient.ts
2
- import { AvatarSelectionUI } from "@mml-io/3d-web-avatar-selection-ui";
3
3
  import {
4
4
  CameraManager,
5
5
  CharacterManager,
6
6
  CharacterModelLoader,
7
7
  CollisionsManager,
8
8
  Composer,
9
- decodeCharacterAndCamera,
10
9
  ErrorScreen,
11
10
  GroundPlane,
12
11
  Key,
@@ -15,28 +14,1036 @@ import {
15
14
  MMLCompositionScene,
16
15
  TimeManager,
17
16
  TweakPane,
18
- VirtualJoystick
17
+ Vect3,
18
+ VirtualJoystick,
19
+ Character,
20
+ Quat,
21
+ getSpawnData
19
22
  } from "@mml-io/3d-web-client-core";
20
23
  import {
21
- ChatNetworkingClient,
22
- TextChatUI
23
- } from "@mml-io/3d-web-text-chat";
24
- import {
25
- USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
26
- USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
27
- USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
28
- USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
24
+ FROM_SERVER_CHAT_MESSAGE_TYPE,
25
+ FROM_CLIENT_CHAT_MESSAGE_TYPE,
29
26
  UserNetworkingClient,
30
- WebsocketStatus
27
+ WebsocketStatus,
28
+ parseServerChatMessage,
29
+ DeltaNetV01ServerErrors,
30
+ SERVER_BROADCAST_MESSAGE_TYPE,
31
+ parseServerBroadcastMessage
31
32
  } from "@mml-io/3d-web-user-networking";
32
- import { VoiceChatManager } from "@mml-io/3d-web-voice-chat";
33
33
  import {
34
34
  LoadingProgressManager,
35
35
  registerCustomElementsToWindow,
36
36
  setGlobalDocumentTimeManager,
37
37
  setGlobalMMLScene
38
38
  } from "@mml-io/mml-web";
39
- import { AudioListener, Euler, Scene, Vector3 } from "three";
39
+ import { Scene, AudioListener, Vector3 } from "three";
40
+
41
+ // src/avatar-selection-ui/AvatarSelectionUI.tsx
42
+ import { forwardRef } from "react";
43
+ import { flushSync } from "react-dom";
44
+ import { createRoot } from "react-dom/client";
45
+
46
+ // src/avatar-selection-ui/components/AvatarPanel/AvatarSectionUIComponent.tsx
47
+ import {
48
+ useRef,
49
+ useState
50
+ } from "react";
51
+
52
+ // src/avatar-selection-ui/icons/Avatar.svg
53
+ var Avatar_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z"/></svg>\n';
54
+
55
+ // esbuild-css-modules-plugin-ns-js::src/avatar-selection-ui/components/AvatarPanel/AvatarSelectionUIComponent.module.css:injector.js
56
+ var content = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
57
+ var digest = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
58
+ var inject = () => {
59
+ setTimeout(() => {
60
+ if (!globalThis.document) {
61
+ return;
62
+ }
63
+ let root = globalThis.document.querySelector("head");
64
+ if (root && root.shadowRoot) {
65
+ root = root.shadowRoot;
66
+ }
67
+ if (!root) {
68
+ root = globalThis.document.head;
69
+ }
70
+ let container = root.querySelector("#_" + digest);
71
+ if (!container) {
72
+ container = globalThis.document.createElement("style");
73
+ container.id = "_" + digest;
74
+ const text = globalThis.document.createTextNode(content);
75
+ container.appendChild(text);
76
+ root.appendChild(container);
77
+ }
78
+ }, 0);
79
+ };
80
+
81
+ // src/avatar-selection-ui/components/AvatarPanel/AvatarSelectionUIComponent.module.css
82
+ var AvatarSelectionUIComponent_default = new Proxy({
83
+ "avatarSelectionContainer": "AvatarSelectionUIComponent-module__avatarSelectionContainer_VjZ7cq__0230",
84
+ "avatarSelectionNoImage": "AvatarSelectionUIComponent-module__avatarSelectionNoImage_VjZ7cq__0230",
85
+ "avatarSelectionSection": "AvatarSelectionUIComponent-module__avatarSelectionSection_VjZ7cq__0230",
86
+ "avatarSelectionUi": "AvatarSelectionUIComponent-module__avatarSelectionUi_VjZ7cq__0230",
87
+ "avatarSelectionUiAvatar": "AvatarSelectionUIComponent-module__avatarSelectionUiAvatar_VjZ7cq__0230",
88
+ "avatarSelectionUiAvatarImage": "AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImage_VjZ7cq__0230",
89
+ "avatarSelectionUiAvatarImgContainer": "AvatarSelectionUIComponent-module__avatarSelectionUiAvatarImgContainer_VjZ7cq__0230",
90
+ "avatarSelectionUiCloseButton": "AvatarSelectionUIComponent-module__avatarSelectionUiCloseButton_VjZ7cq__0230",
91
+ "avatarSelectionUiHeader": "AvatarSelectionUIComponent-module__avatarSelectionUiHeader_VjZ7cq__0230",
92
+ "closeButton": "AvatarSelectionUIComponent-module__closeButton_VjZ7cq__0230",
93
+ "customAvatarInputSection": "AvatarSelectionUIComponent-module__customAvatarInputSection_VjZ7cq__0230",
94
+ "customAvatarSection": "AvatarSelectionUIComponent-module__customAvatarSection_VjZ7cq__0230",
95
+ "displayNameInputSection": "AvatarSelectionUIComponent-module__displayNameInputSection_VjZ7cq__0230",
96
+ "displayNameSection": "AvatarSelectionUIComponent-module__displayNameSection_VjZ7cq__0230",
97
+ "input": "AvatarSelectionUIComponent-module__input_VjZ7cq__0230",
98
+ "menuButton": "AvatarSelectionUIComponent-module__menuButton_VjZ7cq__0230",
99
+ "openTab": "AvatarSelectionUIComponent-module__openTab_VjZ7cq__0230",
100
+ "radioGroup": "AvatarSelectionUIComponent-module__radioGroup_VjZ7cq__0230",
101
+ "radioItem": "AvatarSelectionUIComponent-module__radioItem_VjZ7cq__0230",
102
+ "sectionHeading": "AvatarSelectionUIComponent-module__sectionHeading_VjZ7cq__0230",
103
+ "selectedPill": "AvatarSelectionUIComponent-module__selectedPill_VjZ7cq__0230",
104
+ "setButton": "AvatarSelectionUIComponent-module__setButton_VjZ7cq__0230",
105
+ "tooltipText": "AvatarSelectionUIComponent-module__tooltipText_VjZ7cq__0230"
106
+ }, {
107
+ get: function(source, key) {
108
+ inject();
109
+ return source[key];
110
+ }
111
+ });
112
+
113
+ // src/avatar-selection-ui/components/AvatarPanel/AvatarSectionUIComponent.tsx
114
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
115
+ function SelectedPill() {
116
+ return /* @__PURE__ */ jsx("span", { className: AvatarSelectionUIComponent_default.selectedPill, children: "Selected" });
117
+ }
118
+ var AvatarSelectionUIComponent = (props) => {
119
+ const visibleByDefault = props.visibleByDefault ?? false;
120
+ const [isVisible, setIsVisible] = useState(visibleByDefault);
121
+ const [selectedAvatar, setSelectedAvatar] = useState(
122
+ props.characterDescription
123
+ );
124
+ const [customAvatarType, setCustomAvatarType] = useState(
125
+ 1 /* mmlUrl */
126
+ );
127
+ const [customAvatarValue, setCustomAvatarValue] = useState("");
128
+ const inputRef = useRef(null);
129
+ const textareaRef = useRef(null);
130
+ const [displayNameValue, setDisplayNameValue] = useState(props.displayName);
131
+ const displayNameRef = useRef(null);
132
+ const handleRootClick = (e) => {
133
+ e.stopPropagation();
134
+ };
135
+ const selectAvatar = (avatar) => {
136
+ setSelectedAvatar(avatar);
137
+ props.onUpdateUserAvatar(avatar);
138
+ };
139
+ const handleInputChange = (e) => {
140
+ setCustomAvatarValue(e.target.value);
141
+ };
142
+ const handleDisplayNameChange = (e) => {
143
+ setDisplayNameValue(e.target.value);
144
+ };
145
+ const setDisplayName = () => {
146
+ if (!displayNameValue) {
147
+ return;
148
+ }
149
+ props.onUpdateDisplayName(displayNameValue);
150
+ };
151
+ const addCustomAvatar = () => {
152
+ if (!customAvatarValue) {
153
+ return;
154
+ }
155
+ let newSelectedAvatar;
156
+ switch (customAvatarType) {
157
+ case 2 /* mml */:
158
+ newSelectedAvatar = {
159
+ mmlCharacterString: customAvatarValue
160
+ };
161
+ break;
162
+ case 1 /* mmlUrl */:
163
+ newSelectedAvatar = {
164
+ mmlCharacterUrl: customAvatarValue
165
+ };
166
+ break;
167
+ case 0 /* meshFileUrl */:
168
+ newSelectedAvatar = {
169
+ meshFileUrl: customAvatarValue
170
+ };
171
+ break;
172
+ }
173
+ setSelectedAvatar(newSelectedAvatar);
174
+ props.onUpdateUserAvatar(newSelectedAvatar);
175
+ };
176
+ const handleKeyPress = (e) => {
177
+ e.stopPropagation();
178
+ };
179
+ const handleAvatarInputKeyPress = (e) => {
180
+ e.stopPropagation();
181
+ if (e.key === "Enter") {
182
+ addCustomAvatar();
183
+ }
184
+ };
185
+ const handleDisplayNameKeyPress = (e) => {
186
+ e.stopPropagation();
187
+ if (e.key === "Enter") {
188
+ setDisplayName();
189
+ }
190
+ };
191
+ const handleTypeSwitch = (type) => {
192
+ setCustomAvatarType(type);
193
+ setCustomAvatarValue("");
194
+ };
195
+ const getPlaceholderByType = (type) => {
196
+ switch (type) {
197
+ case 0 /* meshFileUrl */:
198
+ return "https://.../avatar.glb";
199
+ case 1 /* mmlUrl */:
200
+ return "https://.../avatar.html";
201
+ case 2 /* mml */:
202
+ return '<m-character src="https://link-to-avatar">\n</m-character';
203
+ }
204
+ };
205
+ if (!props.availableAvatars.length && !props.allowCustomAvatars && !props.allowCustomDisplayName) {
206
+ return null;
207
+ }
208
+ let recognizedAvatar = false;
209
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
210
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.menuButton, onClick: handleRootClick, children: [
211
+ !isVisible && /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.openTab, onClick: () => setIsVisible(true), children: /* @__PURE__ */ jsx("img", { src: `data:image/svg+xml;utf8,${encodeURIComponent(Avatar_default)}` }) }),
212
+ isVisible && /* @__PURE__ */ jsx("button", { className: AvatarSelectionUIComponent_default.closeButton, onClick: (e) => setIsVisible(false), children: "X" })
213
+ ] }),
214
+ isVisible && /* @__PURE__ */ jsxs("div", { className: `${AvatarSelectionUIComponent_default.avatarSelectionContainer}`, children: [
215
+ props.allowCustomDisplayName && /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.displayNameSection, children: [
216
+ /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.sectionHeading, children: "Display Name" }),
217
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.displayNameInputSection, children: [
218
+ /* @__PURE__ */ jsx(
219
+ "input",
220
+ {
221
+ ref: displayNameRef,
222
+ className: AvatarSelectionUIComponent_default.input,
223
+ value: displayNameValue,
224
+ onKeyDown: handleDisplayNameKeyPress,
225
+ onChange: handleDisplayNameChange,
226
+ placeholder: "Enter your display name"
227
+ }
228
+ ),
229
+ /* @__PURE__ */ jsx(
230
+ "button",
231
+ {
232
+ className: AvatarSelectionUIComponent_default.setButton,
233
+ disabled: !displayNameValue,
234
+ type: "button",
235
+ onClick: setDisplayName,
236
+ children: "Set"
237
+ }
238
+ )
239
+ ] })
240
+ ] }),
241
+ !!props.availableAvatars.length && /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.avatarSelectionSection, children: [
242
+ /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.sectionHeading, children: "Choose your Avatar" }),
243
+ /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.avatarSelectionUi, children: props.availableAvatars.map((avatar, index) => {
244
+ const isSelected = (selectedAvatar == null ? void 0 : selectedAvatar.meshFileUrl) && (selectedAvatar == null ? void 0 : selectedAvatar.meshFileUrl) === avatar.meshFileUrl || (selectedAvatar == null ? void 0 : selectedAvatar.mmlCharacterUrl) && (selectedAvatar == null ? void 0 : selectedAvatar.mmlCharacterUrl) === avatar.mmlCharacterUrl || (selectedAvatar == null ? void 0 : selectedAvatar.mmlCharacterString) && (selectedAvatar == null ? void 0 : selectedAvatar.mmlCharacterString) === avatar.mmlCharacterString;
245
+ if (isSelected) {
246
+ recognizedAvatar = true;
247
+ }
248
+ return /* @__PURE__ */ jsx(
249
+ "div",
250
+ {
251
+ className: AvatarSelectionUIComponent_default.avatarSelectionUiAvatar,
252
+ onClick: () => selectAvatar(avatar),
253
+ children: /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.avatarSelectionUiAvatarImgContainer, children: [
254
+ isSelected && /* @__PURE__ */ jsx(SelectedPill, {}),
255
+ avatar.thumbnailUrl ? /* @__PURE__ */ jsx(
256
+ "img",
257
+ {
258
+ className: AvatarSelectionUIComponent_default.avatarSelectionUiAvatarImage,
259
+ src: avatar.thumbnailUrl,
260
+ alt: avatar.name
261
+ }
262
+ ) : /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.avatarSelectionNoImage, children: /* @__PURE__ */ jsx(
263
+ "img",
264
+ {
265
+ alt: avatar.name,
266
+ src: `data:image/svg+xml;utf8,${encodeURIComponent(Avatar_default)}`
267
+ }
268
+ ) }),
269
+ /* @__PURE__ */ jsx("p", { children: avatar.name }),
270
+ /* @__PURE__ */ jsx("span", { className: AvatarSelectionUIComponent_default.tooltipText, children: avatar.name })
271
+ ] })
272
+ },
273
+ index
274
+ );
275
+ }) })
276
+ ] }),
277
+ props.allowCustomAvatars && /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.customAvatarSection, children: [
278
+ /* @__PURE__ */ jsx("div", { className: AvatarSelectionUIComponent_default.sectionHeading, children: "Custom Avatar" }),
279
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.radioGroup, children: [
280
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.radioItem, children: [
281
+ /* @__PURE__ */ jsx(
282
+ "input",
283
+ {
284
+ type: "radio",
285
+ id: "html",
286
+ name: "customAvatarType",
287
+ onChange: () => handleTypeSwitch(1 /* mmlUrl */),
288
+ checked: customAvatarType === 1 /* mmlUrl */
289
+ }
290
+ ),
291
+ /* @__PURE__ */ jsx("label", { htmlFor: "html", children: "MML URL" })
292
+ ] }),
293
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.radioItem, children: [
294
+ /* @__PURE__ */ jsx(
295
+ "input",
296
+ {
297
+ type: "radio",
298
+ id: "mml",
299
+ name: "customAvatarType",
300
+ onChange: () => handleTypeSwitch(2 /* mml */),
301
+ checked: customAvatarType === 2 /* mml */
302
+ }
303
+ ),
304
+ /* @__PURE__ */ jsx("label", { htmlFor: "mml", children: "MML" })
305
+ ] }),
306
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.radioItem, children: [
307
+ /* @__PURE__ */ jsx(
308
+ "input",
309
+ {
310
+ type: "radio",
311
+ id: "glb",
312
+ name: "customAvatarType",
313
+ onChange: () => handleTypeSwitch(0 /* meshFileUrl */),
314
+ checked: customAvatarType === 0 /* meshFileUrl */
315
+ }
316
+ ),
317
+ /* @__PURE__ */ jsx("label", { htmlFor: "glb", children: "Mesh URL" })
318
+ ] }),
319
+ !recognizedAvatar && /* @__PURE__ */ jsx(SelectedPill, {})
320
+ ] }),
321
+ /* @__PURE__ */ jsxs("div", { className: AvatarSelectionUIComponent_default.customAvatarInputSection, children: [
322
+ customAvatarType === 2 /* mml */ ? /* @__PURE__ */ jsx(
323
+ "textarea",
324
+ {
325
+ ref: textareaRef,
326
+ className: AvatarSelectionUIComponent_default.input,
327
+ value: customAvatarValue,
328
+ onChange: handleInputChange,
329
+ onKeyDown: handleKeyPress,
330
+ placeholder: getPlaceholderByType(customAvatarType),
331
+ rows: 4
332
+ }
333
+ ) : /* @__PURE__ */ jsx(
334
+ "input",
335
+ {
336
+ ref: inputRef,
337
+ className: AvatarSelectionUIComponent_default.input,
338
+ value: customAvatarValue,
339
+ onKeyDown: handleAvatarInputKeyPress,
340
+ onChange: handleInputChange,
341
+ placeholder: getPlaceholderByType(customAvatarType)
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsx(
345
+ "button",
346
+ {
347
+ className: AvatarSelectionUIComponent_default.setButton,
348
+ disabled: !customAvatarValue,
349
+ type: "button",
350
+ onClick: addCustomAvatar,
351
+ children: "Set"
352
+ }
353
+ )
354
+ ] })
355
+ ] })
356
+ ] })
357
+ ] });
358
+ };
359
+
360
+ // src/avatar-selection-ui/AvatarSelectionUI.tsx
361
+ import { jsx as jsx2 } from "react/jsx-runtime";
362
+ var ForwardedAvatarSelectionUIComponent = forwardRef(AvatarSelectionUIComponent);
363
+ var AvatarSelectionUI = class {
364
+ constructor(config) {
365
+ this.config = config;
366
+ this.config.holderElement.appendChild(this.wrapper);
367
+ this.root = createRoot(this.wrapper);
368
+ }
369
+ root;
370
+ wrapper = document.createElement("div");
371
+ onUpdateUserAvatar = (avatar) => {
372
+ this.config.characterDescription = avatar;
373
+ this.config.sendIdentityUpdateToServer(this.config.displayName, avatar);
374
+ };
375
+ onUpdateDisplayName = (displayName) => {
376
+ this.config.displayName = displayName;
377
+ this.config.sendIdentityUpdateToServer(displayName, this.config.characterDescription);
378
+ };
379
+ updateAvatarConfig(avatarConfig) {
380
+ this.config = {
381
+ ...this.config,
382
+ ...avatarConfig
383
+ };
384
+ this.init();
385
+ }
386
+ updateAllowCustomDisplayName(allowCustomDisplayName) {
387
+ this.config = {
388
+ ...this.config,
389
+ allowCustomDisplayName
390
+ };
391
+ this.init();
392
+ }
393
+ init() {
394
+ flushSync(
395
+ () => this.root.render(
396
+ /* @__PURE__ */ jsx2(
397
+ ForwardedAvatarSelectionUIComponent,
398
+ {
399
+ onUpdateUserAvatar: this.onUpdateUserAvatar,
400
+ onUpdateDisplayName: this.onUpdateDisplayName,
401
+ visibleByDefault: this.config.visibleByDefault,
402
+ displayName: this.config.displayName,
403
+ characterDescription: this.config.characterDescription,
404
+ availableAvatars: this.config.availableAvatars,
405
+ allowCustomAvatars: this.config.allowCustomAvatars || false,
406
+ allowCustomDisplayName: this.config.allowCustomDisplayName || false
407
+ }
408
+ )
409
+ )
410
+ );
411
+ }
412
+ };
413
+
414
+ // src/chat-ui/TextChatUI.tsx
415
+ import { createRef, forwardRef as forwardRef3 } from "react";
416
+ import { flushSync as flushSync2 } from "react-dom";
417
+ import { createRoot as createRoot2 } from "react-dom/client";
418
+
419
+ // src/chat-ui/components/ChatPanel/TextChatUIComponent.tsx
420
+ import {
421
+ useCallback as useCallback2,
422
+ useEffect as useEffect4,
423
+ useImperativeHandle as useImperativeHandle2,
424
+ useRef as useRef5,
425
+ useState as useState5
426
+ } from "react";
427
+
428
+ // src/chat-ui/helpers.tsx
429
+ import { useEffect, useLayoutEffect, useRef as useRef2 } from "react";
430
+ function useClickOutside(cb) {
431
+ const ref = useRef2(null);
432
+ const refCb = useRef2(cb);
433
+ useLayoutEffect(() => {
434
+ refCb.current = cb;
435
+ });
436
+ useEffect(() => {
437
+ const handler = (e) => {
438
+ const element = ref.current;
439
+ if (element && !element.contains(e.target)) {
440
+ refCb.current(e);
441
+ }
442
+ };
443
+ document.addEventListener("mousedown", handler);
444
+ document.addEventListener("touchstart", handler);
445
+ return () => {
446
+ document.removeEventListener("mousedown", handler);
447
+ document.removeEventListener("touchstart", handler);
448
+ };
449
+ }, []);
450
+ return ref;
451
+ }
452
+
453
+ // src/chat-ui/icons/Chat.svg
454
+ var Chat_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M88.2 309.1c9.8-18.3 6.8-40.8-7.5-55.8C59.4 230.9 48 204 48 176c0-63.5 63.8-128 160-128s160 64.5 160 128s-63.8 128-160 128c-13.1 0-25.8-1.3-37.8-3.6c-10.4-2-21.2-.6-30.7 4.2c-4.1 2.1-8.3 4.1-12.6 6c-16 7.2-32.9 13.5-49.9 18c2.8-4.6 5.4-9.1 7.9-13.6c1.1-1.9 2.2-3.9 3.2-5.9zM0 176c0 41.8 17.2 80.1 45.9 110.3c-.9 1.7-1.9 3.5-2.8 5.1c-10.3 18.4-22.3 36.5-36.6 52.1c-6.6 7-8.3 17.2-4.6 25.9C5.8 378.3 14.4 384 24 384c43 0 86.5-13.3 122.7-29.7c4.8-2.2 9.6-4.5 14.2-6.8c15.1 3 30.9 4.5 47.1 4.5c114.9 0 208-78.8 208-176S322.9 0 208 0S0 78.8 0 176zM432 480c16.2 0 31.9-1.6 47.1-4.5c4.6 2.3 9.4 4.6 14.2 6.8C529.5 498.7 573 512 616 512c9.6 0 18.2-5.7 22-14.5c3.8-8.8 2-19-4.6-25.9c-14.2-15.6-26.2-33.7-36.6-52.1c-.9-1.7-1.9-3.4-2.8-5.1C622.8 384.1 640 345.8 640 304c0-94.4-87.9-171.5-198.2-175.8c4.1 15.2 6.2 31.2 6.2 47.8l0 .6c87.2 6.7 144 67.5 144 127.4c0 28-11.4 54.9-32.7 77.2c-14.3 15-17.3 37.6-7.5 55.8c1.1 2 2.2 4 3.2 5.9c2.5 4.5 5.2 9 7.9 13.6c-17-4.5-33.9-10.7-49.9-18c-4.3-1.9-8.5-3.9-12.6-6c-9.5-4.8-20.3-6.2-30.7-4.2c-12.1 2.4-24.7 3.6-37.8 3.6c-61.7 0-110-26.5-136.8-62.3c-16 5.4-32.8 9.4-50 11.8C279 439.8 350 480 432 480z"/></svg>';
455
+
456
+ // src/chat-ui/icons/Pin.svg
457
+ var Pin_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M32 32C32 14.3 46.3 0 64 0H320c17.7 0 32 14.3 32 32s-14.3 32-32 32H290.5l11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3H32c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64H64C46.3 64 32 49.7 32 32zM160 384h64v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V384z"/></svg>';
458
+
459
+ // src/chat-ui/components/Input/InputBox.tsx
460
+ import { forwardRef as forwardRef2, useImperativeHandle, useRef as useRef3, useState as useState2 } from "react";
461
+
462
+ // src/chat-ui/icons/PaperPlane.svg
463
+ var PaperPlane_default = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480V396.4c0-4 1.5-7.8 4.2-10.7L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z"/></svg>';
464
+
465
+ // esbuild-css-modules-plugin-ns-js::src/chat-ui/components/Input/InputBox.module.css:injector.js
466
+ var content2 = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
467
+ var digest2 = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
468
+ var inject2 = () => {
469
+ setTimeout(() => {
470
+ if (!globalThis.document) {
471
+ return;
472
+ }
473
+ let root = globalThis.document.querySelector("head");
474
+ if (root && root.shadowRoot) {
475
+ root = root.shadowRoot;
476
+ }
477
+ if (!root) {
478
+ root = globalThis.document.head;
479
+ }
480
+ let container = root.querySelector("#_" + digest2);
481
+ if (!container) {
482
+ container = globalThis.document.createElement("style");
483
+ container.id = "_" + digest2;
484
+ const text = globalThis.document.createTextNode(content2);
485
+ container.appendChild(text);
486
+ root.appendChild(container);
487
+ }
488
+ }, 0);
489
+ };
490
+
491
+ // src/chat-ui/components/Input/InputBox.module.css
492
+ var InputBox_default = new Proxy({
493
+ "chatInput": "InputBox-module__chatInput_adOgOW__0230",
494
+ "inputWrapper": "InputBox-module__inputWrapper_adOgOW__0230",
495
+ "sendButton": "InputBox-module__sendButton_adOgOW__0230",
496
+ "svgIcon": "InputBox-module__svgIcon_adOgOW__0230"
497
+ }, {
498
+ get: function(source, key) {
499
+ inject2();
500
+ return source[key];
501
+ }
502
+ });
503
+
504
+ // src/chat-ui/components/Input/InputBox.tsx
505
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
506
+ var InputBox = forwardRef2(
507
+ ({ onSendMessage, hide, setFocus }, ref) => {
508
+ const inputRef = useRef3(null);
509
+ const buttonRef = useRef3(null);
510
+ const [inputValue, setInputValue] = useState2("");
511
+ useImperativeHandle(ref, () => ({
512
+ focusInput: () => {
513
+ if (inputRef.current) inputRef.current.focus();
514
+ }
515
+ }));
516
+ const handleInputChange = (e) => {
517
+ setInputValue(e.target.value);
518
+ };
519
+ const handleSendClick = () => {
520
+ if (inputValue.trim() !== "") {
521
+ onSendMessage(inputValue.trim());
522
+ setInputValue("");
523
+ }
524
+ };
525
+ const handleKeyPress = (e) => {
526
+ var _a;
527
+ e.stopPropagation();
528
+ if (e.key === "Enter") {
529
+ if (((_a = inputRef.current) == null ? void 0 : _a.value.trim().length) === 0) {
530
+ if (buttonRef.current) buttonRef.current.focus();
531
+ hide();
532
+ return;
533
+ }
534
+ handleSendClick();
535
+ }
536
+ };
537
+ return /* @__PURE__ */ jsxs2("div", { className: InputBox_default.inputWrapper, children: [
538
+ /* @__PURE__ */ jsx3(
539
+ "input",
540
+ {
541
+ ref: inputRef,
542
+ type: "text",
543
+ placeholder: "Type your message here...",
544
+ value: inputValue,
545
+ onChange: handleInputChange,
546
+ className: InputBox_default.chatInput,
547
+ onKeyDown: handleKeyPress,
548
+ onFocus: setFocus
549
+ }
550
+ ),
551
+ /* @__PURE__ */ jsx3("button", { ref: buttonRef, onClick: handleSendClick, className: InputBox_default.sendButton, children: /* @__PURE__ */ jsx3("div", { className: InputBox_default.svgIcon, children: /* @__PURE__ */ jsx3("img", { src: `data:image/svg+xml;utf8,${encodeURIComponent(PaperPlane_default)}` }) }) })
552
+ ] });
553
+ }
554
+ );
555
+ InputBox.displayName = "InputBox";
556
+
557
+ // src/chat-ui/components/Messages/Messages.tsx
558
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
559
+
560
+ // src/chat-ui/components/Message/Message.tsx
561
+ import { useState as useState3, useEffect as useEffect2, useCallback } from "react";
562
+
563
+ // esbuild-css-modules-plugin-ns-js::src/chat-ui/components/Message/Message.module.css:injector.js
564
+ var content3 = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
565
+ var digest3 = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
566
+ var inject3 = () => {
567
+ setTimeout(() => {
568
+ if (!globalThis.document) {
569
+ return;
570
+ }
571
+ let root = globalThis.document.querySelector("head");
572
+ if (root && root.shadowRoot) {
573
+ root = root.shadowRoot;
574
+ }
575
+ if (!root) {
576
+ root = globalThis.document.head;
577
+ }
578
+ let container = root.querySelector("#_" + digest3);
579
+ if (!container) {
580
+ container = globalThis.document.createElement("style");
581
+ container.id = "_" + digest3;
582
+ const text = globalThis.document.createTextNode(content3);
583
+ container.appendChild(text);
584
+ root.appendChild(container);
585
+ }
586
+ }, 0);
587
+ };
588
+
589
+ // src/chat-ui/components/Message/Message.module.css
590
+ var Message_default = new Proxy({
591
+ "messageContainer": "Message-module__messageContainer_ikOQiq__0230",
592
+ "userName": "Message-module__userName_ikOQiq__0230"
593
+ }, {
594
+ get: function(source, key) {
595
+ inject3();
596
+ return source[key];
597
+ }
598
+ });
599
+
600
+ // src/chat-ui/components/Message/Message.tsx
601
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
602
+ function ReverseHash(input) {
603
+ const stringLength = input.length;
604
+ let hash = 5381;
605
+ for (let i = stringLength - 1; i >= 0; i--) {
606
+ hash = (hash << 5) + hash + input.charCodeAt(i);
607
+ }
608
+ return hash;
609
+ }
610
+ function generateValueFromThresholds(hash, thresholds) {
611
+ const selectedThreshold = thresholds[hash % thresholds.length];
612
+ const min = Math.min(...selectedThreshold);
613
+ const max = Math.max(...selectedThreshold);
614
+ const thresholdRange = Math.abs(max - min);
615
+ return hash % thresholdRange + min;
616
+ }
617
+ function hslForString(input, options = DEFAULT_HSL_OPTIONS) {
618
+ let hash = Math.abs(ReverseHash("lightness: " + input));
619
+ const lightness = options.lightnessThresholds ? generateValueFromThresholds(hash, options.lightnessThresholds) : generateValueFromThresholds(hash, DEFAULT_HSL_OPTIONS.lightnessThresholds);
620
+ hash = Math.abs(ReverseHash("saturation:" + input));
621
+ const saturation = options.saturationThresholds ? generateValueFromThresholds(hash, options.saturationThresholds) : generateValueFromThresholds(hash, DEFAULT_HSL_OPTIONS.saturationThresholds);
622
+ hash = Math.abs(ReverseHash("hue:" + input));
623
+ const hue = options.hueThresholds ? generateValueFromThresholds(hash, options.hueThresholds) : generateValueFromThresholds(hash, DEFAULT_HSL_OPTIONS.hueThresholds);
624
+ return [hue, saturation, lightness];
625
+ }
626
+ var Message = ({ username, message, stringToHslOptions }) => {
627
+ const [userColors, setUserColors] = useState3(/* @__PURE__ */ new Map());
628
+ const generateColorForUsername = useCallback(() => {
629
+ const [hue, saturation, lightness] = hslForString(username, stringToHslOptions);
630
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
631
+ }, [stringToHslOptions, username]);
632
+ useEffect2(() => {
633
+ if (!userColors.has(username)) {
634
+ const color = generateColorForUsername();
635
+ setUserColors(new Map(userColors).set(username, color));
636
+ }
637
+ }, [username, userColors, generateColorForUsername]);
638
+ const userColor = userColors.get(username) || "hsl(0, 0%, 0%)";
639
+ return /* @__PURE__ */ jsxs3("div", { className: Message_default.messageContainer, children: [
640
+ /* @__PURE__ */ jsx4("span", { className: Message_default.userName, style: { color: userColor }, children: username }),
641
+ ": ",
642
+ message
643
+ ] });
644
+ };
645
+ var Message_default2 = Message;
646
+
647
+ // esbuild-css-modules-plugin-ns-js::src/chat-ui/components/Messages/Messages.module.css:injector.js
648
+ var content4 = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
649
+ var digest4 = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
650
+ var inject4 = () => {
651
+ setTimeout(() => {
652
+ if (!globalThis.document) {
653
+ return;
654
+ }
655
+ let root = globalThis.document.querySelector("head");
656
+ if (root && root.shadowRoot) {
657
+ root = root.shadowRoot;
658
+ }
659
+ if (!root) {
660
+ root = globalThis.document.head;
661
+ }
662
+ let container = root.querySelector("#_" + digest4);
663
+ if (!container) {
664
+ container = globalThis.document.createElement("style");
665
+ container.id = "_" + digest4;
666
+ const text = globalThis.document.createTextNode(content4);
667
+ container.appendChild(text);
668
+ root.appendChild(container);
669
+ }
670
+ }, 0);
671
+ };
672
+
673
+ // src/chat-ui/components/Messages/Messages.module.css
674
+ var Messages_default = new Proxy({
675
+ "messagesContainer": "Messages-module__messagesContainer_LXaUUW__0230",
676
+ "newMessagesButton": "Messages-module__newMessagesButton_LXaUUW__0230"
677
+ }, {
678
+ get: function(source, key) {
679
+ inject4();
680
+ return source[key];
681
+ }
682
+ });
683
+
684
+ // src/chat-ui/components/Messages/Messages.tsx
685
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
686
+ var SCROLL_THRESHOLD_PX = 10;
687
+ var Messages = ({ messages, stringToHslOptions, shouldAutoScroll }) => {
688
+ const messagesEndRef = useRef4(null);
689
+ const containerRef = useRef4(null);
690
+ const [unreadCount, setUnreadCount] = useState4(0);
691
+ const [lastReadMessageIndex, setLastReadMessageIndex] = useState4(0);
692
+ const [wasAtBottom, setWasAtBottom] = useState4(true);
693
+ const isAtBottom = () => {
694
+ if (!containerRef.current) return true;
695
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
696
+ return Math.abs(scrollHeight - clientHeight - scrollTop) < SCROLL_THRESHOLD_PX;
697
+ };
698
+ const handleScroll = () => {
699
+ const atBottom = isAtBottom();
700
+ setWasAtBottom(atBottom);
701
+ if (atBottom && unreadCount > 0) {
702
+ setUnreadCount(0);
703
+ setLastReadMessageIndex(messages.length - 1);
704
+ }
705
+ };
706
+ const scrollToBottom = () => {
707
+ if (messagesEndRef.current) {
708
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
709
+ }
710
+ setUnreadCount(0);
711
+ setLastReadMessageIndex(messages.length - 1);
712
+ setWasAtBottom(true);
713
+ };
714
+ useEffect3(() => {
715
+ if (messages.length === 0) return;
716
+ const shouldScroll = shouldAutoScroll || wasAtBottom;
717
+ if (shouldScroll) {
718
+ setTimeout(() => {
719
+ if (!isAtBottom()) {
720
+ if (messagesEndRef.current) {
721
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
722
+ }
723
+ }
724
+ setWasAtBottom(true);
725
+ }, 0);
726
+ setUnreadCount(0);
727
+ setLastReadMessageIndex(messages.length - 1);
728
+ } else {
729
+ const newUnreadCount = messages.length - 1 - lastReadMessageIndex;
730
+ setUnreadCount(newUnreadCount);
731
+ }
732
+ }, [messages, shouldAutoScroll, wasAtBottom, lastReadMessageIndex]);
733
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
734
+ /* @__PURE__ */ jsxs4("div", { ref: containerRef, className: Messages_default.messagesContainer, onScroll: handleScroll, children: [
735
+ messages.map((msg, index) => /* @__PURE__ */ jsx5(
736
+ Message_default2,
737
+ {
738
+ username: msg.username,
739
+ message: msg.message,
740
+ stringToHslOptions
741
+ },
742
+ index
743
+ )),
744
+ /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
745
+ ] }),
746
+ unreadCount > 0 && /* @__PURE__ */ jsxs4("button", { className: Messages_default.newMessagesButton, onClick: scrollToBottom, children: [
747
+ unreadCount,
748
+ " new message",
749
+ unreadCount !== 1 ? "s" : ""
750
+ ] })
751
+ ] });
752
+ };
753
+
754
+ // esbuild-css-modules-plugin-ns-js::src/chat-ui/components/ChatPanel/TextChatUIComponent.module.css:injector.js
755
+ var content5 = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
756
+ var digest5 = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
757
+ var inject5 = () => {
758
+ setTimeout(() => {
759
+ if (!globalThis.document) {
760
+ return;
761
+ }
762
+ let root = globalThis.document.querySelector("head");
763
+ if (root && root.shadowRoot) {
764
+ root = root.shadowRoot;
765
+ }
766
+ if (!root) {
767
+ root = globalThis.document.head;
768
+ }
769
+ let container = root.querySelector("#_" + digest5);
770
+ if (!container) {
771
+ container = globalThis.document.createElement("style");
772
+ container.id = "_" + digest5;
773
+ const text = globalThis.document.createTextNode(content5);
774
+ container.appendChild(text);
775
+ root.appendChild(container);
776
+ }
777
+ }, 0);
778
+ };
779
+
780
+ // src/chat-ui/components/ChatPanel/TextChatUIComponent.module.css
781
+ var TextChatUIComponent_default = new Proxy({
782
+ "closeButton": "TextChatUIComponent-module__closeButton_gFDdcW__0230",
783
+ "controls": "TextChatUIComponent-module__controls_gFDdcW__0230",
784
+ "fadeIn": "TextChatUIComponent-module__fadeIn_gFDdcW__0230",
785
+ "fadeOut": "TextChatUIComponent-module__fadeOut_gFDdcW__0230",
786
+ "messagesWrapper": "TextChatUIComponent-module__messagesWrapper_gFDdcW__0230",
787
+ "openTab": "TextChatUIComponent-module__openTab_gFDdcW__0230",
788
+ "stickyButton": "TextChatUIComponent-module__stickyButton_gFDdcW__0230",
789
+ "stickyButtonEnabled": "TextChatUIComponent-module__stickyButtonEnabled_gFDdcW__0230",
790
+ "stickyButtonFadeOut": "TextChatUIComponent-module__stickyButtonFadeOut_gFDdcW__0230",
791
+ "textChat": "TextChatUIComponent-module__textChat_gFDdcW__0230",
792
+ "textChatUi": "TextChatUIComponent-module__textChatUi_gFDdcW__0230",
793
+ "uiHover": "TextChatUIComponent-module__uiHover_gFDdcW__0230"
794
+ }, {
795
+ get: function(source, key) {
796
+ inject5();
797
+ return source[key];
798
+ }
799
+ });
800
+
801
+ // src/chat-ui/components/ChatPanel/TextChatUIComponent.tsx
802
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
803
+ var MAX_MESSAGES = 50;
804
+ var SECONDS_TO_FADE_OUT = 6;
805
+ var AUTO_SCROLL_RESET_DELAY_MS = 100;
806
+ var ChatUIComponent = (props, ref) => {
807
+ const visibleByDefault = props.visibleByDefault ?? true;
808
+ const [messages, setMessages] = useState5([]);
809
+ const [isVisible, setIsVisible] = useState5(visibleByDefault);
810
+ const [isSticky, setSticky] = useState5(visibleByDefault);
811
+ const [isFocused, setIsFocused] = useState5(false);
812
+ const [isOpenHovered, setOpenHovered] = useState5(false);
813
+ const [shouldAutoScroll, setShouldAutoScroll] = useState5(false);
814
+ const [panelStyle, setPanelStyle] = useState5(TextChatUIComponent_default.fadeOut);
815
+ const [stickyStyle, setStickyStyle] = useState5(TextChatUIComponent_default.stickyButton);
816
+ const stickyButtonRef = useRef5(null);
817
+ const closeButtonRef = useRef5(null);
818
+ const hideTimeoutRef = useRef5(null);
819
+ const inputBoxRef = useRef5(null);
820
+ const startHideTimeout = useCallback2(() => {
821
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
822
+ hideTimeoutRef.current = setTimeout(() => {
823
+ if (isVisible) {
824
+ setIsVisible(false);
825
+ }
826
+ }, SECONDS_TO_FADE_OUT * 1e3);
827
+ }, [isVisible]);
828
+ const handleKeyDown = useCallback2(
829
+ (e) => {
830
+ if (e.key === "Enter") {
831
+ if (!isVisible) setIsVisible(true);
832
+ setIsFocused(true);
833
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
834
+ if (inputBoxRef.current) inputBoxRef.current.focusInput();
835
+ }
836
+ },
837
+ [isVisible]
838
+ );
839
+ const handleBlur = useCallback2(() => {
840
+ if (isFocused) setIsFocused(false);
841
+ startHideTimeout();
842
+ if (closeButtonRef.current) closeButtonRef.current.focus();
843
+ }, [isFocused, startHideTimeout]);
844
+ const hide = () => {
845
+ setIsVisible(false);
846
+ setIsFocused(false);
847
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
848
+ };
849
+ const handleRootClick = (e) => {
850
+ e.stopPropagation();
851
+ setOpenHovered(true);
852
+ if (!isVisible) setIsVisible(true);
853
+ };
854
+ const handleStickyButton = (e) => {
855
+ e.stopPropagation();
856
+ setSticky(!isSticky);
857
+ };
858
+ const handleWheel = (e) => {
859
+ e.stopPropagation();
860
+ };
861
+ const handleMouseLeave = () => {
862
+ setOpenHovered(false);
863
+ if (!isFocused && !isSticky && isVisible) {
864
+ startHideTimeout();
865
+ }
866
+ };
867
+ const handleMouseEnter = () => {
868
+ setOpenHovered(true);
869
+ if (!isVisible) setIsVisible(true);
870
+ };
871
+ const chatPanelRef = useClickOutside(() => {
872
+ if (isFocused) {
873
+ setIsFocused(false);
874
+ startHideTimeout();
875
+ }
876
+ });
877
+ useEffect4(() => {
878
+ if (isVisible && isSticky) {
879
+ if (chatPanelRef.current) chatPanelRef.current.style.zIndex = "100";
880
+ }
881
+ setPanelStyle(isVisible || isFocused || isSticky ? TextChatUIComponent_default.fadeIn : TextChatUIComponent_default.fadeOut);
882
+ setStickyStyle(
883
+ isSticky ? TextChatUIComponent_default.stickyButtonEnabled : isOpenHovered ? TextChatUIComponent_default.stickyButton : TextChatUIComponent_default.stickyButtonFadeOut
884
+ );
885
+ if (chatPanelRef.current && chatPanelRef.current.style.zIndex !== "100") {
886
+ setTimeout(() => {
887
+ if (chatPanelRef.current) chatPanelRef.current.style.zIndex = "100";
888
+ }, 2e3);
889
+ }
890
+ }, [isVisible, isSticky, isFocused, chatPanelRef, isOpenHovered]);
891
+ const appendMessages = (username, message) => {
892
+ setMessages((prev) => {
893
+ const newMessages = [...prev, { username, message }];
894
+ return newMessages.length > MAX_MESSAGES ? newMessages.slice(-MAX_MESSAGES) : newMessages;
895
+ });
896
+ };
897
+ useImperativeHandle2(ref, () => ({
898
+ addMessage: (username, message) => {
899
+ appendMessages(username, message);
900
+ if (!isVisible) setIsVisible(true);
901
+ startHideTimeout();
902
+ }
903
+ }));
904
+ const handleSendMessage = (message) => {
905
+ setShouldAutoScroll(true);
906
+ props.sendMessageToServer(message);
907
+ setTimeout(() => setShouldAutoScroll(false), AUTO_SCROLL_RESET_DELAY_MS);
908
+ };
909
+ const setFocus = () => setIsFocused(true);
910
+ useEffect4(() => {
911
+ window.addEventListener("keydown", handleKeyDown, false);
912
+ window.addEventListener("blur", handleBlur, false);
913
+ return () => {
914
+ window.removeEventListener("keydown", handleKeyDown, false);
915
+ window.removeEventListener("blur", handleBlur, false);
916
+ };
917
+ }, [handleBlur, handleKeyDown]);
918
+ return /* @__PURE__ */ jsxs5("div", { className: TextChatUIComponent_default.textChatUi, children: [
919
+ /* @__PURE__ */ jsxs5(
920
+ "div",
921
+ {
922
+ className: TextChatUIComponent_default.uiHover,
923
+ onMouseEnter: handleMouseEnter,
924
+ onMouseLeave: handleMouseLeave,
925
+ onClick: handleRootClick,
926
+ children: [
927
+ /* @__PURE__ */ jsx6("div", { className: TextChatUIComponent_default.openTab, onClick: hide, children: /* @__PURE__ */ jsx6("img", { src: `data:image/svg+xml;utf8,${encodeURIComponent(Chat_default)}` }) }),
928
+ /* @__PURE__ */ jsx6("div", { ref: stickyButtonRef, className: stickyStyle, onClick: handleStickyButton, children: /* @__PURE__ */ jsx6("img", { src: `data:image/svg+xml;utf8,${encodeURIComponent(Pin_default)}` }) })
929
+ ]
930
+ }
931
+ ),
932
+ /* @__PURE__ */ jsxs5(
933
+ "div",
934
+ {
935
+ ref: chatPanelRef,
936
+ style: { zIndex: -1 },
937
+ id: "text-chat-wrapper",
938
+ className: `${TextChatUIComponent_default.textChat} ${panelStyle}`,
939
+ onWheel: handleWheel,
940
+ children: [
941
+ /* @__PURE__ */ jsx6("div", { className: TextChatUIComponent_default.messagesWrapper, children: /* @__PURE__ */ jsx6(
942
+ Messages,
943
+ {
944
+ messages,
945
+ stringToHslOptions: props.stringToHslOptions,
946
+ shouldAutoScroll
947
+ }
948
+ ) }),
949
+ /* @__PURE__ */ jsx6(
950
+ InputBox,
951
+ {
952
+ ref: inputBoxRef,
953
+ onSendMessage: handleSendMessage,
954
+ hide,
955
+ setFocus
956
+ }
957
+ )
958
+ ]
959
+ }
960
+ )
961
+ ] });
962
+ };
963
+
964
+ // src/chat-ui/TextChatUI.tsx
965
+ import { jsx as jsx7 } from "react/jsx-runtime";
966
+ var DEFAULT_HUE_RANGES = [[10, 350]];
967
+ var DEFAULT_SATURATION_RANGES = [[60, 100]];
968
+ var DEFAULT_LIGHTNESS_RANGES = [[65, 75]];
969
+ var DEFAULT_HSL_OPTIONS = {
970
+ hueThresholds: DEFAULT_HUE_RANGES,
971
+ saturationThresholds: DEFAULT_SATURATION_RANGES,
972
+ lightnessThresholds: DEFAULT_LIGHTNESS_RANGES
973
+ };
974
+ var ForwardedChatUIComponent = forwardRef3(ChatUIComponent);
975
+ var TextChatUI = class {
976
+ constructor(config) {
977
+ this.config = config;
978
+ this.config.holderElement.appendChild(this.wrapper);
979
+ this.root = createRoot2(this.wrapper);
980
+ }
981
+ root;
982
+ appRef = createRef();
983
+ addTextMessage(username, message) {
984
+ if (this.appRef.current) {
985
+ this.appRef.current.addMessage(username, message);
986
+ }
987
+ }
988
+ wrapper = document.createElement("div");
989
+ dispose() {
990
+ this.root.unmount();
991
+ this.wrapper.remove();
992
+ }
993
+ init() {
994
+ flushSync2(
995
+ () => this.root.render(
996
+ /* @__PURE__ */ jsx7(
997
+ ForwardedChatUIComponent,
998
+ {
999
+ ref: this.appRef,
1000
+ sendMessageToServer: this.config.sendMessageToServerMethod,
1001
+ visibleByDefault: this.config.visibleByDefault,
1002
+ stringToHslOptions: this.config.stringToHslOptions
1003
+ }
1004
+ )
1005
+ )
1006
+ );
1007
+ }
1008
+ };
1009
+
1010
+ // esbuild-css-modules-plugin-ns-js::src/Networked3dWebExperience.module.css:injector.js
1011
+ var content6 = globalThis['__css-content-693110aba86e39d3ac419c61db5456a8__'];
1012
+ var digest6 = globalThis['__css-digest-693110aba86e39d3ac419c61db5456a8__'];
1013
+ var inject6 = () => {
1014
+ setTimeout(() => {
1015
+ if (!globalThis.document) {
1016
+ return;
1017
+ }
1018
+ let root = globalThis.document.querySelector("head");
1019
+ if (root && root.shadowRoot) {
1020
+ root = root.shadowRoot;
1021
+ }
1022
+ if (!root) {
1023
+ root = globalThis.document.head;
1024
+ }
1025
+ let container = root.querySelector("#_" + digest6);
1026
+ if (!container) {
1027
+ container = globalThis.document.createElement("style");
1028
+ container.id = "_" + digest6;
1029
+ const text = globalThis.document.createTextNode(content6);
1030
+ container.appendChild(text);
1031
+ root.appendChild(container);
1032
+ }
1033
+ }, 0);
1034
+ };
1035
+
1036
+ // src/Networked3dWebExperience.module.css
1037
+ var Networked3dWebExperience_default = new Proxy({
1038
+ "respawnButton": "Networked3dWebExperience-module__respawnButton_7g9l0W__0230"
1039
+ }, {
1040
+ get: function(source, key) {
1041
+ inject6();
1042
+ return source[key];
1043
+ }
1044
+ });
1045
+
1046
+ // src/Networked3dWebExperienceClient.ts
40
1047
  function normalizeSpawnConfiguration(spawnConfig) {
41
1048
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
42
1049
  return {
@@ -66,30 +1073,6 @@ var Networked3dWebExperienceClient = class {
66
1073
  constructor(holderElement, config) {
67
1074
  this.holderElement = holderElement;
68
1075
  this.config = config;
69
- this.scene = new Scene();
70
- this.tweakPane = null;
71
- this.audioListener = new AudioListener();
72
- this.collisionsManager = new CollisionsManager(this.scene);
73
- this.characterModelLoader = new CharacterModelLoader();
74
- this.timeManager = new TimeManager();
75
- this.keyInputManager = new KeyInputManager();
76
- this.mmlFrames = {};
77
- this.clientId = null;
78
- this.remoteUserStates = /* @__PURE__ */ new Map();
79
- this.userProfiles = /* @__PURE__ */ new Map();
80
- this.networkChat = null;
81
- this.textChatUI = null;
82
- this.avatarSelectionUI = null;
83
- this.voiceChatManager = null;
84
- this.latestCharacterObject = {
85
- characterState: null
86
- };
87
- this.characterControllerPaneSet = false;
88
- this.initialLoadCompleted = false;
89
- this.loadingProgressManager = new LoadingProgressManager();
90
- this.currentRequestAnimationFrame = null;
91
- this.groundPlane = null;
92
- this.respawnButton = null;
93
1076
  var _a;
94
1077
  this.element = document.createElement("div");
95
1078
  this.element.style.position = "absolute";
@@ -106,8 +1089,10 @@ var Networked3dWebExperienceClient = class {
106
1089
  this.canvasHolder.style.width = "100%";
107
1090
  this.canvasHolder.style.height = "100%";
108
1091
  this.element.appendChild(this.canvasHolder);
1092
+ this.collisionsManager = new CollisionsManager(this.scene);
109
1093
  this.cameraManager = new CameraManager(this.canvasHolder, this.collisionsManager);
110
1094
  this.cameraManager.camera.add(this.audioListener);
1095
+ this.characterModelLoader = new CharacterModelLoader();
111
1096
  this.virtualJoystick = new VirtualJoystick(this.element, {
112
1097
  radius: 70,
113
1098
  innerRadius: 20,
@@ -117,7 +1102,8 @@ var Networked3dWebExperienceClient = class {
117
1102
  scene: this.scene,
118
1103
  cameraManager: this.cameraManager,
119
1104
  spawnSun: true,
120
- environmentConfiguration: this.config.environmentConfiguration
1105
+ environmentConfiguration: this.config.environmentConfiguration,
1106
+ postProcessingEnabled: this.config.postProcessingEnabled
121
1107
  });
122
1108
  this.canvasHolder.appendChild(this.composer.renderer.domElement);
123
1109
  if (this.config.enableTweakPane !== false) {
@@ -127,62 +1113,88 @@ var Networked3dWebExperienceClient = class {
127
1113
  this.composer.fitContainer();
128
1114
  });
129
1115
  resizeObserver.observe(this.element);
1116
+ this.spawnConfiguration = normalizeSpawnConfiguration(this.config.spawnConfiguration);
1117
+ const spawnData = getSpawnData(this.spawnConfiguration, true);
1118
+ const spawnRotation = new Quat().setFromEulerXYZ(spawnData.spawnRotation);
130
1119
  const initialNetworkLoadRef = {};
131
1120
  this.loadingProgressManager.addLoadingAsset(initialNetworkLoadRef, "network", "network");
132
- this.networkClient = new UserNetworkingClient({
133
- url: this.config.userNetworkAddress,
134
- sessionToken: this.config.sessionToken,
135
- websocketFactory: (url) => new WebSocket(url),
136
- statusUpdateCallback: (status) => {
137
- if (status === WebsocketStatus.Disconnected || status === WebsocketStatus.Reconnecting) {
138
- this.characterManager.clear();
139
- this.remoteUserStates.clear();
140
- this.clientId = null;
141
- }
142
- },
143
- assignedIdentity: (clientId) => {
144
- console.log(`Assigned ID: ${clientId}`);
145
- this.clientId = clientId;
146
- if (this.initialLoadCompleted) {
147
- this.spawnCharacter();
148
- } else {
149
- this.loadingProgressManager.completedLoadingAsset(initialNetworkLoadRef);
150
- }
151
- },
152
- clientUpdate: (remoteClientId, userNetworkingClientUpdate) => {
153
- if (userNetworkingClientUpdate === null) {
154
- this.remoteUserStates.delete(remoteClientId);
155
- } else {
156
- this.remoteUserStates.set(remoteClientId, userNetworkingClientUpdate);
1121
+ this.networkClient = new UserNetworkingClient(
1122
+ {
1123
+ url: this.config.userNetworkAddress,
1124
+ sessionToken: this.config.sessionToken,
1125
+ websocketFactory: (url) => new WebSocket(url, "delta-net-v0.1"),
1126
+ statusUpdateCallback: (status) => {
1127
+ console.log(`Websocket status: ${status}`);
1128
+ if (status === WebsocketStatus.Disconnected || status === WebsocketStatus.Reconnecting) {
1129
+ this.characterManager.clear();
1130
+ this.remoteUserStates.clear();
1131
+ this.clientId = null;
1132
+ }
1133
+ },
1134
+ assignedIdentity: (clientId) => {
1135
+ console.log(`Assigned ID: ${clientId}`);
1136
+ this.clientId = clientId;
1137
+ if (this.initialLoadCompleted) {
1138
+ const spawnData2 = getSpawnData(this.spawnConfiguration, true);
1139
+ this.spawnCharacter(spawnData2);
1140
+ } else {
1141
+ this.loadingProgressManager.completedLoadingAsset(initialNetworkLoadRef);
1142
+ }
1143
+ },
1144
+ onUpdate: (update) => {
1145
+ this.onNetworkUpdate(update);
1146
+ },
1147
+ onServerError: (error) => {
1148
+ switch (error.errorType) {
1149
+ case DeltaNetV01ServerErrors.USER_AUTHENTICATION_FAILED_ERROR_TYPE:
1150
+ this.disposeWithError(error.message);
1151
+ break;
1152
+ case DeltaNetV01ServerErrors.USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE:
1153
+ this.disposeWithError(error.message);
1154
+ break;
1155
+ case DeltaNetV01ServerErrors.USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE:
1156
+ this.disposeWithError(error.message || "Server shutdown");
1157
+ break;
1158
+ default:
1159
+ console.error(`Unhandled server error: ${error.message}`);
1160
+ this.disposeWithError(error.message);
1161
+ }
1162
+ },
1163
+ onCustomMessage: (customType, contents) => {
1164
+ var _a2, _b;
1165
+ if (customType === SERVER_BROADCAST_MESSAGE_TYPE) {
1166
+ const serverBroadcastMessage = parseServerBroadcastMessage(contents);
1167
+ if (serverBroadcastMessage instanceof Error) {
1168
+ console.error(`Invalid server broadcast message: ${contents}`);
1169
+ } else {
1170
+ (_b = (_a2 = this.config).onServerBroadcast) == null ? void 0 : _b.call(_a2, serverBroadcastMessage);
1171
+ }
1172
+ } else if (customType === FROM_SERVER_CHAT_MESSAGE_TYPE) {
1173
+ const serverChatMessage = parseServerChatMessage(contents);
1174
+ if (serverChatMessage instanceof Error) {
1175
+ console.error(`Invalid server chat message: ${contents}`);
1176
+ } else {
1177
+ this.handleChatMessage(serverChatMessage.fromUserId, serverChatMessage.message);
1178
+ }
1179
+ } else {
1180
+ console.warn(`Did not recognize custom message type ${customType}`);
1181
+ }
157
1182
  }
158
1183
  },
159
- clientProfileUpdated: (clientId, username, characterDescription) => {
160
- this.updateUserProfile(clientId, {
161
- username,
162
- characterDescription
163
- });
1184
+ {
1185
+ username: null,
1186
+ characterDescription: null,
1187
+ colors: null
164
1188
  },
165
- onServerError: (error) => {
166
- switch (error.errorType) {
167
- case USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE:
168
- this.disposeWithError(error.message);
169
- break;
170
- case USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE:
171
- this.disposeWithError(error.message);
172
- break;
173
- case USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE:
174
- this.disposeWithError(error.message || "Server shutdown");
175
- break;
176
- default:
177
- console.error(`Unhandled server error: ${error.message}`);
178
- this.disposeWithError(error.message);
179
- }
180
- },
181
- onServerBroadcast: (broadcast) => {
182
- var _a2, _b;
183
- (_b = (_a2 = this.config).onServerBroadcast) == null ? void 0 : _b.call(_a2, broadcast);
1189
+ {
1190
+ position: spawnData.spawnPosition,
1191
+ rotation: {
1192
+ quaternionY: spawnRotation.y,
1193
+ quaternionW: spawnRotation.w
1194
+ },
1195
+ state: 0
184
1196
  }
185
- });
1197
+ );
186
1198
  if (this.config.allowOrbitalCamera) {
187
1199
  this.keyInputManager.createKeyBinding(Key.C, () => {
188
1200
  if (document.activeElement === document.body) {
@@ -191,7 +1203,10 @@ var Networked3dWebExperienceClient = class {
191
1203
  }
192
1204
  });
193
1205
  }
194
- this.spawnConfiguration = normalizeSpawnConfiguration(this.config.spawnConfiguration);
1206
+ const animationsPromise = Character.loadAnimations(
1207
+ this.characterModelLoader,
1208
+ this.config.animationConfig
1209
+ );
195
1210
  this.characterManager = new CharacterManager({
196
1211
  composer: this.composer,
197
1212
  characterModelLoader: this.characterModelLoader,
@@ -205,7 +1220,10 @@ var Networked3dWebExperienceClient = class {
205
1220
  this.latestCharacterObject.characterState = characterState;
206
1221
  this.networkClient.sendUpdate(characterState);
207
1222
  },
208
- animationConfig: this.config.animationConfig,
1223
+ sendLocalCharacterColors: (colors) => {
1224
+ this.networkClient.updateColors(colors);
1225
+ },
1226
+ animationsPromise,
209
1227
  spawnConfiguration: this.spawnConfiguration,
210
1228
  characterResolve: (characterId) => {
211
1229
  return this.resolveCharacterData(characterId);
@@ -214,7 +1232,8 @@ var Networked3dWebExperienceClient = class {
214
1232
  });
215
1233
  this.scene.add(this.characterManager.group);
216
1234
  if (this.spawnConfiguration.enableRespawnButton) {
217
- this.element.appendChild(this.characterManager.createRespawnButton());
1235
+ this.respawnButton = this.createRespawnButton();
1236
+ this.element.appendChild(this.respawnButton);
218
1237
  }
219
1238
  this.setGroundPlaneEnabled(((_a = this.config.environmentConfiguration) == null ? void 0 : _a.groundPlane) ?? true);
220
1239
  this.setupMMLScene();
@@ -224,14 +1243,46 @@ var Networked3dWebExperienceClient = class {
224
1243
  const [, completed] = this.loadingProgressManager.toRatio();
225
1244
  if (completed && !this.initialLoadCompleted) {
226
1245
  this.initialLoadCompleted = true;
227
- this.connectToVoiceChat();
228
1246
  this.connectToTextChat();
229
1247
  this.mountAvatarSelectionUI();
230
- this.spawnCharacter();
1248
+ this.spawnCharacter(spawnData);
231
1249
  }
232
1250
  });
233
1251
  this.loadingProgressManager.setInitialLoad(true);
234
1252
  }
1253
+ element;
1254
+ canvasHolder;
1255
+ scene = new Scene();
1256
+ composer;
1257
+ tweakPane = null;
1258
+ audioListener = new AudioListener();
1259
+ cameraManager;
1260
+ collisionsManager;
1261
+ characterModelLoader;
1262
+ characterManager;
1263
+ timeManager = new TimeManager();
1264
+ keyInputManager = new KeyInputManager();
1265
+ virtualJoystick;
1266
+ mmlCompositionScene;
1267
+ mmlFrames = {};
1268
+ clientId = null;
1269
+ networkClient;
1270
+ remoteUserStates = /* @__PURE__ */ new Map();
1271
+ userProfiles = /* @__PURE__ */ new Map();
1272
+ textChatUI = null;
1273
+ avatarSelectionUI = null;
1274
+ latestCharacterObject = {
1275
+ characterState: null
1276
+ };
1277
+ characterControllerPaneSet = false;
1278
+ spawnConfiguration;
1279
+ initialLoadCompleted = false;
1280
+ loadingProgressManager = new LoadingProgressManager();
1281
+ loadingScreen;
1282
+ errorScreen;
1283
+ groundPlane = null;
1284
+ respawnButton = null;
1285
+ currentRequestAnimationFrame = null;
235
1286
  setGroundPlaneEnabled(enabled) {
236
1287
  if (enabled && this.groundPlane === null) {
237
1288
  this.groundPlane = new GroundPlane();
@@ -244,7 +1295,6 @@ var Networked3dWebExperienceClient = class {
244
1295
  }
245
1296
  }
246
1297
  updateConfig(config) {
247
- var _a;
248
1298
  this.config = {
249
1299
  ...this.config,
250
1300
  ...config
@@ -267,6 +1317,14 @@ var Networked3dWebExperienceClient = class {
267
1317
  this.setupTweakPane();
268
1318
  }
269
1319
  }
1320
+ if (this.config.postProcessingEnabled !== void 0) {
1321
+ this.composer.togglePostProcessing(this.config.postProcessingEnabled);
1322
+ if (this.tweakPane) {
1323
+ this.tweakPane.dispose();
1324
+ this.tweakPane = null;
1325
+ this.setupTweakPane();
1326
+ }
1327
+ }
270
1328
  if (config.allowOrbitalCamera !== void 0) {
271
1329
  if (config.allowOrbitalCamera === false) {
272
1330
  this.keyInputManager.removeKeyBinding(Key.C);
@@ -282,11 +1340,9 @@ var Networked3dWebExperienceClient = class {
282
1340
  });
283
1341
  }
284
1342
  }
285
- if (config.chatNetworkAddress !== void 0) {
286
- if (config.chatNetworkAddress === null && this.networkChat !== null) {
287
- this.networkChat.stop();
288
- this.networkChat = null;
289
- (_a = this.textChatUI) == null ? void 0 : _a.dispose();
1343
+ if (config.enableChat) {
1344
+ if (!config.enableChat && this.textChatUI !== null) {
1345
+ this.textChatUI.dispose();
290
1346
  this.textChatUI = null;
291
1347
  } else {
292
1348
  this.connectToTextChat();
@@ -297,7 +1353,7 @@ var Networked3dWebExperienceClient = class {
297
1353
  this.characterManager.localController.updateSpawnConfig(this.spawnConfiguration);
298
1354
  }
299
1355
  if (this.spawnConfiguration.enableRespawnButton && !this.respawnButton) {
300
- this.respawnButton = this.characterManager.createRespawnButton();
1356
+ this.respawnButton = this.createRespawnButton();
301
1357
  this.element.appendChild(this.respawnButton);
302
1358
  } else if (!this.spawnConfiguration.enableRespawnButton && this.respawnButton) {
303
1359
  this.respawnButton.remove();
@@ -318,6 +1374,16 @@ var Networked3dWebExperienceClient = class {
318
1374
  document.body.appendChild(holder);
319
1375
  return holder;
320
1376
  }
1377
+ createRespawnButton() {
1378
+ const respawnButton = document.createElement("div");
1379
+ respawnButton.className = Networked3dWebExperience_default.respawnButton;
1380
+ respawnButton.textContent = "RESPAWN";
1381
+ respawnButton.addEventListener("click", () => {
1382
+ var _a;
1383
+ (_a = this.characterManager.localController) == null ? void 0 : _a.resetPosition();
1384
+ });
1385
+ return respawnButton;
1386
+ }
321
1387
  resolveCharacterData(clientId) {
322
1388
  const user = this.userProfiles.get(clientId);
323
1389
  if (!user) {
@@ -325,41 +1391,43 @@ var Networked3dWebExperienceClient = class {
325
1391
  }
326
1392
  return {
327
1393
  username: user.username,
328
- characterDescription: user.characterDescription
1394
+ characterDescription: user.characterDescription,
1395
+ colors: user.colors
329
1396
  };
330
1397
  }
331
- updateUserProfile(id, userData) {
332
- console.log(`Update user_profile for id=${id} (username=${userData.username})`);
333
- this.userProfiles.set(id, userData);
334
- if (this.textChatUI && id === this.clientId) {
335
- this.textChatUI.setClientName(userData.username);
1398
+ onNetworkUpdate(update) {
1399
+ const { removedUserIds, addedUserIds, updatedUsers } = update;
1400
+ for (const clientId of removedUserIds) {
1401
+ this.userProfiles.delete(clientId);
1402
+ this.remoteUserStates.delete(clientId);
1403
+ }
1404
+ for (const [clientId, userData] of addedUserIds) {
1405
+ this.userProfiles.set(clientId, userData.userState);
1406
+ this.remoteUserStates.set(clientId, userData.components);
1407
+ }
1408
+ for (const [clientId, userDataUpdate] of updatedUsers) {
1409
+ const userState = userDataUpdate.userState;
1410
+ if (userState) {
1411
+ if (userState.username !== void 0) {
1412
+ this.userProfiles.get(clientId).username = userState.username;
1413
+ }
1414
+ if (userState.characterDescription !== void 0) {
1415
+ this.userProfiles.get(clientId).characterDescription = userState.characterDescription;
1416
+ }
1417
+ if (userState.colors !== void 0) {
1418
+ this.userProfiles.get(clientId).colors = userState.colors;
1419
+ }
1420
+ this.characterManager.networkCharacterInfoUpdated(clientId);
1421
+ }
1422
+ this.remoteUserStates.set(clientId, userDataUpdate.components);
336
1423
  }
337
- this.characterManager.respawnIfPresent(id);
338
1424
  }
339
1425
  sendIdentityUpdateToServer(displayName, characterDescription) {
340
1426
  if (!this.clientId) {
341
1427
  throw new Error("Client ID not set");
342
1428
  }
343
- this.networkClient.sendMessage({
344
- type: USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
345
- userIdentity: {
346
- username: displayName,
347
- characterDescription
348
- }
349
- });
350
- }
351
- connectToVoiceChat() {
352
- if (this.clientId === null) return;
353
- if (this.voiceChatManager === null && this.config.voiceChatAddress) {
354
- this.voiceChatManager = new VoiceChatManager({
355
- url: this.config.voiceChatAddress,
356
- holderElement: this.element,
357
- userId: this.clientId,
358
- remoteUserStates: this.remoteUserStates,
359
- latestCharacterObj: this.latestCharacterObject,
360
- autoJoin: false
361
- });
362
- }
1429
+ this.networkClient.updateUsername(displayName);
1430
+ this.networkClient.updateCharacterDescription(characterDescription);
363
1431
  }
364
1432
  setupTweakPane() {
365
1433
  if (this.tweakPane) {
@@ -369,57 +1437,53 @@ var Networked3dWebExperienceClient = class {
369
1437
  this.element,
370
1438
  this.composer.renderer,
371
1439
  this.scene,
372
- this.composer.effectComposer
1440
+ this.composer,
1441
+ this.config.postProcessingEnabled
373
1442
  );
374
1443
  this.cameraManager.setupTweakPane(this.tweakPane);
375
1444
  this.composer.setupTweakPane(this.tweakPane);
376
1445
  }
1446
+ handleChatMessage(fromUserId, message) {
1447
+ if (this.textChatUI === null) {
1448
+ return;
1449
+ }
1450
+ if (fromUserId === 0) {
1451
+ this.textChatUI.addTextMessage("System", message);
1452
+ } else {
1453
+ const user = this.userProfiles.get(fromUserId);
1454
+ if (!user) {
1455
+ console.error(`User not found for clientId ${fromUserId}`);
1456
+ return;
1457
+ }
1458
+ const username = user.username ?? `Unknown User ${fromUserId}`;
1459
+ this.textChatUI.addTextMessage(username, message);
1460
+ this.characterManager.addChatBubble(fromUserId, message);
1461
+ }
1462
+ }
377
1463
  connectToTextChat() {
378
1464
  if (this.clientId === null) {
379
1465
  return;
380
1466
  }
381
- if (this.networkChat === null && this.config.chatNetworkAddress) {
1467
+ if (this.config.enableChat && this.textChatUI === null) {
382
1468
  const user = this.userProfiles.get(this.clientId);
383
1469
  if (!user) {
384
1470
  throw new Error("User not found");
385
1471
  }
386
- if (this.textChatUI === null) {
387
- const textChatUISettings = {
388
- holderElement: this.element,
389
- clientname: user.username,
390
- sendMessageToServerMethod: (message) => {
391
- this.characterManager.addSelfChatBubble(message);
392
- this.mmlCompositionScene.onChatMessage(message);
393
- if (this.clientId === null || this.networkChat === null) return;
394
- this.networkChat.sendChatMessage(message);
395
- },
396
- visibleByDefault: this.config.chatVisibleByDefault,
397
- stringToHslOptions: this.config.userNameToColorOptions
398
- };
399
- this.textChatUI = new TextChatUI(textChatUISettings);
400
- this.textChatUI.init();
401
- }
402
- this.networkChat = new ChatNetworkingClient({
403
- url: this.config.chatNetworkAddress,
404
- sessionToken: this.config.sessionToken,
405
- websocketFactory: (url) => new WebSocket(url),
406
- statusUpdateCallback: (status) => {
407
- if (status === WebsocketStatus.Disconnected || status === WebsocketStatus.Reconnecting) {
408
- }
1472
+ const textChatUISettings = {
1473
+ holderElement: this.canvasHolder,
1474
+ sendMessageToServerMethod: (message) => {
1475
+ this.characterManager.addSelfChatBubble(message);
1476
+ this.mmlCompositionScene.onChatMessage(message);
1477
+ this.networkClient.sendCustomMessage(
1478
+ FROM_CLIENT_CHAT_MESSAGE_TYPE,
1479
+ JSON.stringify({ message })
1480
+ );
409
1481
  },
410
- clientChatUpdate: (clientId, chatNetworkingUpdate) => {
411
- var _a;
412
- if (chatNetworkingUpdate !== null && this.textChatUI !== null) {
413
- const username = ((_a = this.userProfiles.get(clientId)) == null ? void 0 : _a.username) || "Unknown";
414
- this.textChatUI.addTextMessage(username, chatNetworkingUpdate.text);
415
- this.characterManager.addChatBubble(clientId, chatNetworkingUpdate.text);
416
- }
417
- },
418
- onServerError: (error) => {
419
- console.error(`Chat server error: ${error.message}. errorType: ${error.errorType}`);
420
- this.disposeWithError(error.message);
421
- }
422
- });
1482
+ visibleByDefault: this.config.chatVisibleByDefault,
1483
+ stringToHslOptions: this.config.userNameToColorOptions
1484
+ };
1485
+ this.textChatUI = new TextChatUI(textChatUISettings);
1486
+ this.textChatUI.init();
423
1487
  }
424
1488
  }
425
1489
  mountAvatarSelectionUI() {
@@ -434,8 +1498,10 @@ var Networked3dWebExperienceClient = class {
434
1498
  this.avatarSelectionUI = new AvatarSelectionUI({
435
1499
  holderElement: this.element,
436
1500
  visibleByDefault: false,
437
- displayName: ownIdentity.username,
438
- characterDescription: ownIdentity.characterDescription,
1501
+ displayName: ownIdentity.username ?? `Unknown User ${this.clientId}`,
1502
+ characterDescription: ownIdentity.characterDescription ?? {
1503
+ meshFileUrl: ""
1504
+ },
439
1505
  sendIdentityUpdateToServer: this.sendIdentityUpdateToServer.bind(this),
440
1506
  availableAvatars: ((_a = this.config.avatarConfiguration) == null ? void 0 : _a.availableAvatars) ?? [],
441
1507
  allowCustomAvatars: (_b = this.config.avatarConfiguration) == null ? void 0 : _b.allowCustomAvatars,
@@ -444,16 +1510,16 @@ var Networked3dWebExperienceClient = class {
444
1510
  this.avatarSelectionUI.init();
445
1511
  }
446
1512
  update() {
447
- var _a, _b, _c, _d;
1513
+ var _a, _b, _c;
448
1514
  this.timeManager.update();
449
1515
  this.characterManager.update();
450
- (_a = this.voiceChatManager) == null ? void 0 : _a.speakingParticipants.forEach((value, id) => {
451
- this.characterManager.setSpeakingCharacter(id, value);
452
- });
453
1516
  this.cameraManager.update();
454
- (_c = this.composer.sun) == null ? void 0 : _c.updateCharacterPosition((_b = this.characterManager.localCharacter) == null ? void 0 : _b.position);
1517
+ const characterPosition = (_a = this.characterManager.localCharacter) == null ? void 0 : _a.getPosition();
1518
+ (_b = this.composer.sun) == null ? void 0 : _b.updateCharacterPosition(
1519
+ new Vector3((characterPosition == null ? void 0 : characterPosition.x) || 0, (characterPosition == null ? void 0 : characterPosition.y) || 0, (characterPosition == null ? void 0 : characterPosition.z) || 0)
1520
+ );
455
1521
  this.composer.render(this.timeManager);
456
- if ((_d = this.tweakPane) == null ? void 0 : _d.guiVisible) {
1522
+ if ((_c = this.tweakPane) == null ? void 0 : _c.guiVisible) {
457
1523
  this.tweakPane.updateStats(this.timeManager);
458
1524
  this.tweakPane.updateCameraData(this.cameraManager);
459
1525
  if (this.characterManager.localCharacter && this.characterManager.localController) {
@@ -469,60 +1535,29 @@ var Networked3dWebExperienceClient = class {
469
1535
  this.update();
470
1536
  });
471
1537
  }
472
- randomWithVariance(value, variance) {
473
- const min = value - variance;
474
- const max = value + variance;
475
- return Math.random() * (max - min) + min;
476
- }
477
- spawnCharacter() {
1538
+ spawnCharacter({
1539
+ spawnPosition,
1540
+ spawnRotation,
1541
+ cameraPosition
1542
+ }) {
478
1543
  if (this.clientId === null) {
479
1544
  throw new Error("Client ID not set");
480
1545
  }
481
- const spawnPosition = new Vector3();
482
- spawnPosition.set(
483
- this.randomWithVariance(
484
- this.spawnConfiguration.spawnPosition.x,
485
- this.spawnConfiguration.spawnPositionVariance.x
486
- ),
487
- this.randomWithVariance(
488
- this.spawnConfiguration.spawnPosition.y,
489
- this.spawnConfiguration.spawnPositionVariance.y
490
- ),
491
- this.randomWithVariance(
492
- this.spawnConfiguration.spawnPosition.z,
493
- this.spawnConfiguration.spawnPositionVariance.z
494
- )
495
- );
496
- const spawnRotation = new Euler(
497
- 0,
498
- -this.spawnConfiguration.spawnYRotation * (Math.PI / 180),
499
- 0
500
- );
501
- let cameraPosition = null;
502
- const offset = new Vector3(0, 0, 3.3);
503
- offset.applyEuler(new Euler(0, spawnRotation.y, 0));
504
- cameraPosition = spawnPosition.clone().sub(offset).add(this.characterManager.headTargetOffset);
505
- if (window.location.hash && window.location.hash.length > 1) {
506
- const urlParams = decodeCharacterAndCamera(window.location.hash.substring(1));
507
- spawnPosition.copy(urlParams.character.position);
508
- spawnRotation.setFromQuaternion(urlParams.character.quaternion);
509
- cameraPosition = urlParams.camera.position;
510
- }
511
1546
  const ownIdentity = this.userProfiles.get(this.clientId);
512
1547
  if (!ownIdentity) {
513
1548
  throw new Error("Own identity not found");
514
1549
  }
515
1550
  this.characterManager.spawnLocalCharacter(
516
1551
  this.clientId,
517
- ownIdentity.username,
1552
+ ownIdentity.username ?? `Unknown User ${this.clientId}`,
518
1553
  ownIdentity.characterDescription,
519
1554
  spawnPosition,
520
1555
  spawnRotation
521
1556
  );
522
1557
  if (cameraPosition !== null) {
523
- this.cameraManager.camera.position.copy(cameraPosition);
1558
+ this.cameraManager.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
524
1559
  this.cameraManager.setTarget(
525
- new Vector3().add(spawnPosition).add(this.characterManager.headTargetOffset)
1560
+ new Vect3().add(spawnPosition).add(CharacterManager.headTargetOffset)
526
1561
  );
527
1562
  this.cameraManager.reverseUpdateFromPositions();
528
1563
  }
@@ -533,24 +1568,24 @@ var Networked3dWebExperienceClient = class {
533
1568
  this.element.append(this.errorScreen.element);
534
1569
  }
535
1570
  dispose() {
536
- var _a, _b, _c, _d;
1571
+ var _a, _b, _c;
1572
+ this.characterManager.dispose();
537
1573
  this.networkClient.stop();
538
- (_a = this.networkChat) == null ? void 0 : _a.stop();
539
1574
  for (const [key, element] of Object.entries(this.mmlFrames)) {
540
1575
  element.remove();
541
1576
  }
542
1577
  this.mmlFrames = {};
543
- (_b = this.textChatUI) == null ? void 0 : _b.dispose();
1578
+ (_a = this.textChatUI) == null ? void 0 : _a.dispose();
544
1579
  this.mmlCompositionScene.dispose();
545
1580
  this.composer.dispose();
546
- (_c = this.tweakPane) == null ? void 0 : _c.dispose();
1581
+ (_b = this.tweakPane) == null ? void 0 : _b.dispose();
547
1582
  if (this.currentRequestAnimationFrame !== null) {
548
1583
  cancelAnimationFrame(this.currentRequestAnimationFrame);
549
1584
  this.currentRequestAnimationFrame = null;
550
1585
  }
551
1586
  this.cameraManager.dispose();
552
1587
  this.loadingScreen.dispose();
553
- (_d = this.errorScreen) == null ? void 0 : _d.dispose();
1588
+ (_c = this.errorScreen) == null ? void 0 : _c.dispose();
554
1589
  }
555
1590
  setupMMLScene() {
556
1591
  registerCustomElementsToWindow(window);