@mml-io/3d-web-experience-client 0.21.6 → 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 +15 -14
  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 +1306 -189
  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,14 +1,12 @@
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
- getSpawnPositionInsideCircle,
12
10
  GroundPlane,
13
11
  Key,
14
12
  KeyInputManager,
@@ -16,55 +14,1065 @@ import {
16
14
  MMLCompositionScene,
17
15
  TimeManager,
18
16
  TweakPane,
19
- VirtualJoystick
17
+ Vect3,
18
+ VirtualJoystick,
19
+ Character,
20
+ Quat,
21
+ getSpawnData
20
22
  } from "@mml-io/3d-web-client-core";
21
23
  import {
22
- ChatNetworkingClient,
23
- TextChatUI
24
- } from "@mml-io/3d-web-text-chat";
25
- import {
26
- USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
27
- USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
28
- USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
29
- USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
24
+ FROM_SERVER_CHAT_MESSAGE_TYPE,
25
+ FROM_CLIENT_CHAT_MESSAGE_TYPE,
30
26
  UserNetworkingClient,
31
- WebsocketStatus
27
+ WebsocketStatus,
28
+ parseServerChatMessage,
29
+ DeltaNetV01ServerErrors,
30
+ SERVER_BROADCAST_MESSAGE_TYPE,
31
+ parseServerBroadcastMessage
32
32
  } from "@mml-io/3d-web-user-networking";
33
- import { VoiceChatManager } from "@mml-io/3d-web-voice-chat";
34
33
  import {
35
34
  LoadingProgressManager,
36
35
  registerCustomElementsToWindow,
37
36
  setGlobalDocumentTimeManager,
38
37
  setGlobalMMLScene
39
38
  } from "@mml-io/mml-web";
40
- 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
1047
+ function normalizeSpawnConfiguration(spawnConfig) {
1048
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1049
+ return {
1050
+ spawnPosition: {
1051
+ x: ((_a = spawnConfig == null ? void 0 : spawnConfig.spawnPosition) == null ? void 0 : _a.x) ?? 0,
1052
+ y: ((_b = spawnConfig == null ? void 0 : spawnConfig.spawnPosition) == null ? void 0 : _b.y) ?? 0,
1053
+ z: ((_c = spawnConfig == null ? void 0 : spawnConfig.spawnPosition) == null ? void 0 : _c.z) ?? 0
1054
+ },
1055
+ spawnPositionVariance: {
1056
+ x: ((_d = spawnConfig == null ? void 0 : spawnConfig.spawnPositionVariance) == null ? void 0 : _d.x) ?? 0,
1057
+ y: ((_e = spawnConfig == null ? void 0 : spawnConfig.spawnPositionVariance) == null ? void 0 : _e.y) ?? 0,
1058
+ z: ((_f = spawnConfig == null ? void 0 : spawnConfig.spawnPositionVariance) == null ? void 0 : _f.z) ?? 0
1059
+ },
1060
+ spawnYRotation: (spawnConfig == null ? void 0 : spawnConfig.spawnYRotation) ?? 0,
1061
+ respawnTrigger: {
1062
+ minX: ((_g = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _g.minX) ?? Number.NEGATIVE_INFINITY,
1063
+ maxX: ((_h = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _h.maxX) ?? Number.POSITIVE_INFINITY,
1064
+ minY: ((_i = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _i.minY) ?? -100,
1065
+ maxY: ((_j = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _j.maxY) ?? Number.POSITIVE_INFINITY,
1066
+ minZ: ((_k = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _k.minZ) ?? Number.NEGATIVE_INFINITY,
1067
+ maxZ: ((_l = spawnConfig == null ? void 0 : spawnConfig.respawnTrigger) == null ? void 0 : _l.maxZ) ?? Number.POSITIVE_INFINITY
1068
+ },
1069
+ enableRespawnButton: (spawnConfig == null ? void 0 : spawnConfig.enableRespawnButton) ?? false
1070
+ };
1071
+ }
41
1072
  var Networked3dWebExperienceClient = class {
42
1073
  constructor(holderElement, config) {
43
1074
  this.holderElement = holderElement;
44
1075
  this.config = config;
45
- this.scene = new Scene();
46
- this.tweakPane = null;
47
- this.audioListener = new AudioListener();
48
- this.collisionsManager = new CollisionsManager(this.scene);
49
- this.characterModelLoader = new CharacterModelLoader();
50
- this.timeManager = new TimeManager();
51
- this.keyInputManager = new KeyInputManager();
52
- this.mmlFrames = {};
53
- this.clientId = null;
54
- this.remoteUserStates = /* @__PURE__ */ new Map();
55
- this.userProfiles = /* @__PURE__ */ new Map();
56
- this.networkChat = null;
57
- this.textChatUI = null;
58
- this.avatarSelectionUI = null;
59
- this.voiceChatManager = null;
60
- this.latestCharacterObject = {
61
- characterState: null
62
- };
63
- this.characterControllerPaneSet = false;
64
- this.initialLoadCompleted = false;
65
- this.loadingProgressManager = new LoadingProgressManager();
66
- this.currentRequestAnimationFrame = null;
67
- this.groundPlane = null;
68
1076
  var _a;
69
1077
  this.element = document.createElement("div");
70
1078
  this.element.style.position = "absolute";
@@ -81,8 +1089,10 @@ var Networked3dWebExperienceClient = class {
81
1089
  this.canvasHolder.style.width = "100%";
82
1090
  this.canvasHolder.style.height = "100%";
83
1091
  this.element.appendChild(this.canvasHolder);
1092
+ this.collisionsManager = new CollisionsManager(this.scene);
84
1093
  this.cameraManager = new CameraManager(this.canvasHolder, this.collisionsManager);
85
1094
  this.cameraManager.camera.add(this.audioListener);
1095
+ this.characterModelLoader = new CharacterModelLoader();
86
1096
  this.virtualJoystick = new VirtualJoystick(this.element, {
87
1097
  radius: 70,
88
1098
  innerRadius: 20,
@@ -92,7 +1102,8 @@ var Networked3dWebExperienceClient = class {
92
1102
  scene: this.scene,
93
1103
  cameraManager: this.cameraManager,
94
1104
  spawnSun: true,
95
- environmentConfiguration: this.config.environmentConfiguration
1105
+ environmentConfiguration: this.config.environmentConfiguration,
1106
+ postProcessingEnabled: this.config.postProcessingEnabled
96
1107
  });
97
1108
  this.canvasHolder.appendChild(this.composer.renderer.domElement);
98
1109
  if (this.config.enableTweakPane !== false) {
@@ -102,62 +1113,88 @@ var Networked3dWebExperienceClient = class {
102
1113
  this.composer.fitContainer();
103
1114
  });
104
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);
105
1119
  const initialNetworkLoadRef = {};
106
1120
  this.loadingProgressManager.addLoadingAsset(initialNetworkLoadRef, "network", "network");
107
- this.networkClient = new UserNetworkingClient({
108
- url: this.config.userNetworkAddress,
109
- sessionToken: this.config.sessionToken,
110
- websocketFactory: (url) => new WebSocket(url),
111
- statusUpdateCallback: (status) => {
112
- if (status === WebsocketStatus.Disconnected || status === WebsocketStatus.Reconnecting) {
113
- this.characterManager.clear();
114
- this.remoteUserStates.clear();
115
- this.clientId = null;
116
- }
117
- },
118
- assignedIdentity: (clientId) => {
119
- console.log(`Assigned ID: ${clientId}`);
120
- this.clientId = clientId;
121
- if (this.initialLoadCompleted) {
122
- this.spawnCharacter();
123
- } else {
124
- this.loadingProgressManager.completedLoadingAsset(initialNetworkLoadRef);
125
- }
126
- },
127
- clientUpdate: (remoteClientId, userNetworkingClientUpdate) => {
128
- if (userNetworkingClientUpdate === null) {
129
- this.remoteUserStates.delete(remoteClientId);
130
- } else {
131
- 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
+ }
132
1182
  }
133
1183
  },
134
- clientProfileUpdated: (clientId, username, characterDescription) => {
135
- this.updateUserProfile(clientId, {
136
- username,
137
- characterDescription
138
- });
139
- },
140
- onServerError: (error) => {
141
- switch (error.errorType) {
142
- case USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE:
143
- this.disposeWithError(error.message);
144
- break;
145
- case USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE:
146
- this.disposeWithError(error.message);
147
- break;
148
- case USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE:
149
- this.disposeWithError(error.message || "Server shutdown");
150
- break;
151
- default:
152
- console.error(`Unhandled server error: ${error.message}`);
153
- this.disposeWithError(error.message);
154
- }
1184
+ {
1185
+ username: null,
1186
+ characterDescription: null,
1187
+ colors: null
155
1188
  },
156
- onServerBroadcast: (broadcast) => {
157
- var _a2, _b;
158
- (_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
159
1196
  }
160
- });
1197
+ );
161
1198
  if (this.config.allowOrbitalCamera) {
162
1199
  this.keyInputManager.createKeyBinding(Key.C, () => {
163
1200
  if (document.activeElement === document.body) {
@@ -166,6 +1203,10 @@ var Networked3dWebExperienceClient = class {
166
1203
  }
167
1204
  });
168
1205
  }
1206
+ const animationsPromise = Character.loadAnimations(
1207
+ this.characterModelLoader,
1208
+ this.config.animationConfig
1209
+ );
169
1210
  this.characterManager = new CharacterManager({
170
1211
  composer: this.composer,
171
1212
  characterModelLoader: this.characterModelLoader,
@@ -179,13 +1220,21 @@ var Networked3dWebExperienceClient = class {
179
1220
  this.latestCharacterObject.characterState = characterState;
180
1221
  this.networkClient.sendUpdate(characterState);
181
1222
  },
182
- animationConfig: this.config.animationConfig,
1223
+ sendLocalCharacterColors: (colors) => {
1224
+ this.networkClient.updateColors(colors);
1225
+ },
1226
+ animationsPromise,
1227
+ spawnConfiguration: this.spawnConfiguration,
183
1228
  characterResolve: (characterId) => {
184
1229
  return this.resolveCharacterData(characterId);
185
1230
  },
186
1231
  updateURLLocation: this.config.updateURLLocation !== false
187
1232
  });
188
1233
  this.scene.add(this.characterManager.group);
1234
+ if (this.spawnConfiguration.enableRespawnButton) {
1235
+ this.respawnButton = this.createRespawnButton();
1236
+ this.element.appendChild(this.respawnButton);
1237
+ }
189
1238
  this.setGroundPlaneEnabled(((_a = this.config.environmentConfiguration) == null ? void 0 : _a.groundPlane) ?? true);
190
1239
  this.setupMMLScene();
191
1240
  this.loadingScreen = new LoadingScreen(this.loadingProgressManager, this.config.loadingScreen);
@@ -194,14 +1243,46 @@ var Networked3dWebExperienceClient = class {
194
1243
  const [, completed] = this.loadingProgressManager.toRatio();
195
1244
  if (completed && !this.initialLoadCompleted) {
196
1245
  this.initialLoadCompleted = true;
197
- this.connectToVoiceChat();
198
1246
  this.connectToTextChat();
199
1247
  this.mountAvatarSelectionUI();
200
- this.spawnCharacter();
1248
+ this.spawnCharacter(spawnData);
201
1249
  }
202
1250
  });
203
1251
  this.loadingProgressManager.setInitialLoad(true);
204
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;
205
1286
  setGroundPlaneEnabled(enabled) {
206
1287
  if (enabled && this.groundPlane === null) {
207
1288
  this.groundPlane = new GroundPlane();
@@ -214,7 +1295,6 @@ var Networked3dWebExperienceClient = class {
214
1295
  }
215
1296
  }
216
1297
  updateConfig(config) {
217
- var _a;
218
1298
  this.config = {
219
1299
  ...this.config,
220
1300
  ...config
@@ -237,16 +1317,48 @@ var Networked3dWebExperienceClient = class {
237
1317
  this.setupTweakPane();
238
1318
  }
239
1319
  }
240
- if (config.chatNetworkAddress !== void 0) {
241
- if (config.chatNetworkAddress === null && this.networkChat !== null) {
242
- this.networkChat.stop();
243
- this.networkChat = null;
244
- (_a = this.textChatUI) == null ? void 0 : _a.dispose();
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
+ }
1328
+ if (config.allowOrbitalCamera !== void 0) {
1329
+ if (config.allowOrbitalCamera === false) {
1330
+ this.keyInputManager.removeKeyBinding(Key.C);
1331
+ if (this.cameraManager.isFlyCameraOn() === true) {
1332
+ this.cameraManager.toggleFlyCamera();
1333
+ }
1334
+ } else if (config.allowOrbitalCamera === true) {
1335
+ this.keyInputManager.createKeyBinding(Key.C, () => {
1336
+ if (document.activeElement === document.body) {
1337
+ this.cameraManager.toggleFlyCamera();
1338
+ this.composer.fitContainer();
1339
+ }
1340
+ });
1341
+ }
1342
+ }
1343
+ if (config.enableChat) {
1344
+ if (!config.enableChat && this.textChatUI !== null) {
1345
+ this.textChatUI.dispose();
245
1346
  this.textChatUI = null;
246
1347
  } else {
247
1348
  this.connectToTextChat();
248
1349
  }
249
1350
  }
1351
+ this.spawnConfiguration = normalizeSpawnConfiguration(config.spawnConfiguration);
1352
+ if (this.characterManager.localController) {
1353
+ this.characterManager.localController.updateSpawnConfig(this.spawnConfiguration);
1354
+ }
1355
+ if (this.spawnConfiguration.enableRespawnButton && !this.respawnButton) {
1356
+ this.respawnButton = this.createRespawnButton();
1357
+ this.element.appendChild(this.respawnButton);
1358
+ } else if (!this.spawnConfiguration.enableRespawnButton && this.respawnButton) {
1359
+ this.respawnButton.remove();
1360
+ this.respawnButton = null;
1361
+ }
250
1362
  if (config.mmlDocuments) {
251
1363
  this.setMMLDocuments(config.mmlDocuments);
252
1364
  }
@@ -262,6 +1374,16 @@ var Networked3dWebExperienceClient = class {
262
1374
  document.body.appendChild(holder);
263
1375
  return holder;
264
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
+ }
265
1387
  resolveCharacterData(clientId) {
266
1388
  const user = this.userProfiles.get(clientId);
267
1389
  if (!user) {
@@ -269,41 +1391,43 @@ var Networked3dWebExperienceClient = class {
269
1391
  }
270
1392
  return {
271
1393
  username: user.username,
272
- characterDescription: user.characterDescription
1394
+ characterDescription: user.characterDescription,
1395
+ colors: user.colors
273
1396
  };
274
1397
  }
275
- updateUserProfile(id, userData) {
276
- console.log(`Update user_profile for id=${id} (username=${userData.username})`);
277
- this.userProfiles.set(id, userData);
278
- if (this.textChatUI && id === this.clientId) {
279
- 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);
280
1423
  }
281
- this.characterManager.respawnIfPresent(id);
282
1424
  }
283
1425
  sendIdentityUpdateToServer(displayName, characterDescription) {
284
1426
  if (!this.clientId) {
285
1427
  throw new Error("Client ID not set");
286
1428
  }
287
- this.networkClient.sendMessage({
288
- type: USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
289
- userIdentity: {
290
- username: displayName,
291
- characterDescription
292
- }
293
- });
294
- }
295
- connectToVoiceChat() {
296
- if (this.clientId === null) return;
297
- if (this.voiceChatManager === null && this.config.voiceChatAddress) {
298
- this.voiceChatManager = new VoiceChatManager({
299
- url: this.config.voiceChatAddress,
300
- holderElement: this.element,
301
- userId: this.clientId,
302
- remoteUserStates: this.remoteUserStates,
303
- latestCharacterObj: this.latestCharacterObject,
304
- autoJoin: false
305
- });
306
- }
1429
+ this.networkClient.updateUsername(displayName);
1430
+ this.networkClient.updateCharacterDescription(characterDescription);
307
1431
  }
308
1432
  setupTweakPane() {
309
1433
  if (this.tweakPane) {
@@ -313,57 +1437,53 @@ var Networked3dWebExperienceClient = class {
313
1437
  this.element,
314
1438
  this.composer.renderer,
315
1439
  this.scene,
316
- this.composer.effectComposer
1440
+ this.composer,
1441
+ this.config.postProcessingEnabled
317
1442
  );
318
1443
  this.cameraManager.setupTweakPane(this.tweakPane);
319
1444
  this.composer.setupTweakPane(this.tweakPane);
320
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
+ }
321
1463
  connectToTextChat() {
322
1464
  if (this.clientId === null) {
323
1465
  return;
324
1466
  }
325
- if (this.networkChat === null && this.config.chatNetworkAddress) {
1467
+ if (this.config.enableChat && this.textChatUI === null) {
326
1468
  const user = this.userProfiles.get(this.clientId);
327
1469
  if (!user) {
328
1470
  throw new Error("User not found");
329
1471
  }
330
- if (this.textChatUI === null) {
331
- const textChatUISettings = {
332
- holderElement: this.element,
333
- clientname: user.username,
334
- sendMessageToServerMethod: (message) => {
335
- this.characterManager.addSelfChatBubble(message);
336
- this.mmlCompositionScene.onChatMessage(message);
337
- if (this.clientId === null || this.networkChat === null) return;
338
- this.networkChat.sendChatMessage(message);
339
- },
340
- visibleByDefault: this.config.chatVisibleByDefault,
341
- stringToHslOptions: this.config.userNameToColorOptions
342
- };
343
- this.textChatUI = new TextChatUI(textChatUISettings);
344
- this.textChatUI.init();
345
- }
346
- this.networkChat = new ChatNetworkingClient({
347
- url: this.config.chatNetworkAddress,
348
- sessionToken: this.config.sessionToken,
349
- websocketFactory: (url) => new WebSocket(url),
350
- statusUpdateCallback: (status) => {
351
- if (status === WebsocketStatus.Disconnected || status === WebsocketStatus.Reconnecting) {
352
- }
353
- },
354
- clientChatUpdate: (clientId, chatNetworkingUpdate) => {
355
- var _a;
356
- if (chatNetworkingUpdate !== null && this.textChatUI !== null) {
357
- const username = ((_a = this.userProfiles.get(clientId)) == null ? void 0 : _a.username) || "Unknown";
358
- this.textChatUI.addTextMessage(username, chatNetworkingUpdate.text);
359
- this.characterManager.addChatBubble(clientId, chatNetworkingUpdate.text);
360
- }
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
+ );
361
1481
  },
362
- onServerError: (error) => {
363
- console.error(`Chat server error: ${error.message}. errorType: ${error.errorType}`);
364
- this.disposeWithError(error.message);
365
- }
366
- });
1482
+ visibleByDefault: this.config.chatVisibleByDefault,
1483
+ stringToHslOptions: this.config.userNameToColorOptions
1484
+ };
1485
+ this.textChatUI = new TextChatUI(textChatUISettings);
1486
+ this.textChatUI.init();
367
1487
  }
368
1488
  }
369
1489
  mountAvatarSelectionUI() {
@@ -378,8 +1498,10 @@ var Networked3dWebExperienceClient = class {
378
1498
  this.avatarSelectionUI = new AvatarSelectionUI({
379
1499
  holderElement: this.element,
380
1500
  visibleByDefault: false,
381
- displayName: ownIdentity.username,
382
- characterDescription: ownIdentity.characterDescription,
1501
+ displayName: ownIdentity.username ?? `Unknown User ${this.clientId}`,
1502
+ characterDescription: ownIdentity.characterDescription ?? {
1503
+ meshFileUrl: ""
1504
+ },
383
1505
  sendIdentityUpdateToServer: this.sendIdentityUpdateToServer.bind(this),
384
1506
  availableAvatars: ((_a = this.config.avatarConfiguration) == null ? void 0 : _a.availableAvatars) ?? [],
385
1507
  allowCustomAvatars: (_b = this.config.avatarConfiguration) == null ? void 0 : _b.allowCustomAvatars,
@@ -388,16 +1510,16 @@ var Networked3dWebExperienceClient = class {
388
1510
  this.avatarSelectionUI.init();
389
1511
  }
390
1512
  update() {
391
- var _a, _b, _c, _d;
1513
+ var _a, _b, _c;
392
1514
  this.timeManager.update();
393
1515
  this.characterManager.update();
394
- (_a = this.voiceChatManager) == null ? void 0 : _a.speakingParticipants.forEach((value, id) => {
395
- this.characterManager.setSpeakingCharacter(id, value);
396
- });
397
1516
  this.cameraManager.update();
398
- (_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
+ );
399
1521
  this.composer.render(this.timeManager);
400
- if ((_d = this.tweakPane) == null ? void 0 : _d.guiVisible) {
1522
+ if ((_c = this.tweakPane) == null ? void 0 : _c.guiVisible) {
401
1523
  this.tweakPane.updateStats(this.timeManager);
402
1524
  this.tweakPane.updateCameraData(this.cameraManager);
403
1525
  if (this.characterManager.localCharacter && this.characterManager.localController) {
@@ -413,34 +1535,29 @@ var Networked3dWebExperienceClient = class {
413
1535
  this.update();
414
1536
  });
415
1537
  }
416
- spawnCharacter() {
1538
+ spawnCharacter({
1539
+ spawnPosition,
1540
+ spawnRotation,
1541
+ cameraPosition
1542
+ }) {
417
1543
  if (this.clientId === null) {
418
1544
  throw new Error("Client ID not set");
419
1545
  }
420
- const spawnPosition = getSpawnPositionInsideCircle(3, 30, this.clientId, 0.4);
421
- const spawnRotation = new Euler(0, 0, 0);
422
- let cameraPosition = null;
423
- if (window.location.hash && window.location.hash.length > 1) {
424
- const urlParams = decodeCharacterAndCamera(window.location.hash.substring(1));
425
- spawnPosition.copy(urlParams.character.position);
426
- spawnRotation.setFromQuaternion(urlParams.character.quaternion);
427
- cameraPosition = urlParams.camera.position;
428
- }
429
1546
  const ownIdentity = this.userProfiles.get(this.clientId);
430
1547
  if (!ownIdentity) {
431
1548
  throw new Error("Own identity not found");
432
1549
  }
433
1550
  this.characterManager.spawnLocalCharacter(
434
1551
  this.clientId,
435
- ownIdentity.username,
1552
+ ownIdentity.username ?? `Unknown User ${this.clientId}`,
436
1553
  ownIdentity.characterDescription,
437
1554
  spawnPosition,
438
1555
  spawnRotation
439
1556
  );
440
1557
  if (cameraPosition !== null) {
441
- this.cameraManager.camera.position.copy(cameraPosition);
1558
+ this.cameraManager.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
442
1559
  this.cameraManager.setTarget(
443
- new Vector3().add(spawnPosition).add(this.characterManager.headTargetOffset)
1560
+ new Vect3().add(spawnPosition).add(CharacterManager.headTargetOffset)
444
1561
  );
445
1562
  this.cameraManager.reverseUpdateFromPositions();
446
1563
  }
@@ -451,24 +1568,24 @@ var Networked3dWebExperienceClient = class {
451
1568
  this.element.append(this.errorScreen.element);
452
1569
  }
453
1570
  dispose() {
454
- var _a, _b, _c, _d;
1571
+ var _a, _b, _c;
1572
+ this.characterManager.dispose();
455
1573
  this.networkClient.stop();
456
- (_a = this.networkChat) == null ? void 0 : _a.stop();
457
1574
  for (const [key, element] of Object.entries(this.mmlFrames)) {
458
1575
  element.remove();
459
1576
  }
460
1577
  this.mmlFrames = {};
461
- (_b = this.textChatUI) == null ? void 0 : _b.dispose();
1578
+ (_a = this.textChatUI) == null ? void 0 : _a.dispose();
462
1579
  this.mmlCompositionScene.dispose();
463
1580
  this.composer.dispose();
464
- (_c = this.tweakPane) == null ? void 0 : _c.dispose();
1581
+ (_b = this.tweakPane) == null ? void 0 : _b.dispose();
465
1582
  if (this.currentRequestAnimationFrame !== null) {
466
1583
  cancelAnimationFrame(this.currentRequestAnimationFrame);
467
1584
  this.currentRequestAnimationFrame = null;
468
1585
  }
469
1586
  this.cameraManager.dispose();
470
1587
  this.loadingScreen.dispose();
471
- (_d = this.errorScreen) == null ? void 0 : _d.dispose();
1588
+ (_c = this.errorScreen) == null ? void 0 : _c.dispose();
472
1589
  }
473
1590
  setupMMLScene() {
474
1591
  registerCustomElementsToWindow(window);