:root{--accent: 136, 58, 234;--accent-light: 224, 204, 250;--accent-dark: 49, 10, 101;--accent-gradient: linear-gradient( 45deg, rgb(var(--accent)), rgb(var(--accent-light)) 30%, white 60% )}html{font-family:system-ui,sans-serif;background:#13151a;background-size:224px}code{font-family:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace}.video-message[data-astro-cid-kcgitt3l] .bubble[data-astro-cid-kcgitt3l]{padding:.5rem;background-color:transparent;box-shadow:none}.video-container[data-astro-cid-kcgitt3l]{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-container[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l]{width:100%;border-radius:8px;background-color:#000}.play-button[data-astro-cid-kcgitt3l]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;width:50px;height:50px;font-size:24px;cursor:pointer;display:flex;align-items:center;justify-content:center}.play-button[data-astro-cid-kcgitt3l]:hover{background-color:#000000b3}.file-message[data-astro-cid-kcgitt3l] .bubble[data-astro-cid-kcgitt3l]{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;margin-bottom:.5rem;display:flex;flex-direction:column;align-items:flex-start}.file-message[data-astro-cid-kcgitt3l] .username[data-astro-cid-kcgitt3l]{font-weight:700;margin-bottom:.25rem}.file-message[data-astro-cid-kcgitt3l] .file-container[data-astro-cid-kcgitt3l]{display:flex;align-items:center}.file-message[data-astro-cid-kcgitt3l] .file-icon[data-astro-cid-kcgitt3l]{font-size:2rem;margin-right:.5rem}.file-message[data-astro-cid-kcgitt3l] .file-name[data-astro-cid-kcgitt3l]{margin-right:.5rem}.file-message[data-astro-cid-kcgitt3l] a[data-astro-cid-kcgitt3l]{color:#3897f0;text-decoration:none}.file-message[data-astro-cid-kcgitt3l] a[data-astro-cid-kcgitt3l]:hover{text-decoration:underline}.chat-interface[data-astro-cid-kcgitt3l]{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0}<style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls[data-astro-cid-kcgitt3l]{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls[data-astro-cid-kcgitt3l] input[data-astro-cid-kcgitt3l]{width:calc(50% - 5px);margin-bottom:10px}#user-controls[data-astro-cid-kcgitt3l] button[data-astro-cid-kcgitt3l]{width:calc(50% - 5px)}#room-info[data-astro-cid-kcgitt3l]{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info[data-astro-cid-kcgitt3l] h3[data-astro-cid-kcgitt3l]{margin:0;font-size:1rem;font-weight:600}.chat-container[data-astro-cid-kcgitt3l]{display:flex;flex-direction:column;height:500px}.chat-messages[data-astro-cid-kcgitt3l]{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message[data-astro-cid-kcgitt3l]{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message[data-astro-cid-kcgitt3l].self{align-self:flex-end}.message[data-astro-cid-kcgitt3l].peer{align-self:flex-start}.bubble[data-astro-cid-kcgitt3l]{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l]{border-top-right-radius:4px}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l]{border-top-left-radius:4px}.bubble[data-astro-cid-kcgitt3l] p[data-astro-cid-kcgitt3l]{margin:0;padding-right:50px}.time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message[data-astro-cid-kcgitt3l] .bubble[data-astro-cid-kcgitt3l]{padding:.25rem;background-color:transparent;box-shadow:none}.image-message[data-astro-cid-kcgitt3l] img[data-astro-cid-kcgitt3l]{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username[data-astro-cid-kcgitt3l]{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form[data-astro-cid-kcgitt3l]{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[data-astro-cid-kcgitt3l][type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button[data-astro-cid-kcgitt3l]{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button[data-astro-cid-kcgitt3l]:hover{color:#1877f2}#upload-file[data-astro-cid-kcgitt3l],#chat-form[data-astro-cid-kcgitt3l] button[data-astro-cid-kcgitt3l][type=submit]{display:flex;align-items:center;justify-content:center}#upload-file[data-astro-cid-kcgitt3l] svg[data-astro-cid-kcgitt3l],#chat-form[data-astro-cid-kcgitt3l] button[data-astro-cid-kcgitt3l][type=submit] svg[data-astro-cid-kcgitt3l]{width:24px;height:24px}.modal[data-astro-cid-kcgitt3l]{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content[data-astro-cid-kcgitt3l]{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions[data-astro-cid-kcgitt3l],.reaction-button[data-astro-cid-kcgitt3l]{display:none}.video-message[data-astro-cid-kcgitt3l] .bubble[data-astro-cid-kcgitt3l]{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.video-message[data-astro-cid-kcgitt3l] .video-container[data-astro-cid-kcgitt3l]{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-message[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l]{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.video-message[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.video-message[data-astro-cid-kcgitt3l]:hover .play-button[data-astro-cid-kcgitt3l]{opacity:1}.video-message[data-astro-cid-kcgitt3l] .file-name[data-astro-cid-kcgitt3l]{margin:0;font-size:.8rem;color:#666}.video-message[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.file-message[data-astro-cid-kcgitt3l] .bubble[data-astro-cid-kcgitt3l]{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.file-message[data-astro-cid-kcgitt3l] .file-container[data-astro-cid-kcgitt3l]{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.file-message[data-astro-cid-kcgitt3l] .file-icon[data-astro-cid-kcgitt3l]{font-size:2rem;margin-bottom:.5rem}.file-message[data-astro-cid-kcgitt3l] .file-name[data-astro-cid-kcgitt3l]{margin:0;font-size:.8rem;color:#666}.file-message[data-astro-cid-kcgitt3l] a[data-astro-cid-kcgitt3l]{color:#3897f0;text-decoration:none;margin-top:.5rem}.file-message[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] p[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] p[data-astro-cid-kcgitt3l]{color:#333;margin:0;padding-right:50px}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .reactions[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .reactions[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .reaction-button[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .reaction-button[data-astro-cid-kcgitt3l]{display:none}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l]{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l]{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .file-container[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .file-container[data-astro-cid-kcgitt3l]{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .file-icon[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .file-icon[data-astro-cid-kcgitt3l]{font-size:2rem;margin-bottom:.5rem}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] a[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] a[data-astro-cid-kcgitt3l]{color:#3897f0;text-decoration:none;margin-top:.5rem}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .video-container[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .video-container[data-astro-cid-kcgitt3l]{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] video[data-astro-cid-kcgitt3l]{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .play-button[data-astro-cid-kcgitt3l]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l]:hover .play-button[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l]:hover .play-button[data-astro-cid-kcgitt3l]{opacity:1}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .file-name[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .file-name[data-astro-cid-kcgitt3l]{margin:0;font-size:.8rem;color:#666}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] .time[data-astro-cid-kcgitt3l]{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l]{background-color:#e1ffc7;border-top-right-radius:12px;border-bottom-left-radius:12px;border-bottom-right-radius:4px;margin-left:auto;margin-right:0;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l]{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l]:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l]:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message[data-astro-cid-kcgitt3l].self .time[data-astro-cid-kcgitt3l]{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message[data-astro-cid-kcgitt3l].peer .time[data-astro-cid-kcgitt3l]{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message[data-astro-cid-kcgitt3l].self .username[data-astro-cid-kcgitt3l],.message[data-astro-cid-kcgitt3l].peer .username[data-astro-cid-kcgitt3l]{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message[data-astro-cid-kcgitt3l].self .bubble[data-astro-cid-kcgitt3l] p[data-astro-cid-kcgitt3l]{color:#333;margin:0;padding-right:50px}.message[data-astro-cid-kcgitt3l].peer .bubble[data-astro-cid-kcgitt3l] p[data-astro-cid-kcgitt3l]{color:#333;margin:0;.message.peer .bubble{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.self .bubble:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message.self .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message.peer .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message.self .username,.message.peer .username{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message.self .bubble p,.message.peer .bubble p{color:#333;margin:0;padding-right:50px}.message.self .bubble .reactions,.message.peer .bubble .reactions,.message.self .bubble .reaction-button,.message.peer .bubble .reaction-button{display:none}.message.self .bubble .file-container,.message.peer .bubble .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message.self .bubble .file-icon,.message.peer .bubble .file-icon{font-size:2rem;margin-bottom:.5rem}.message.self .bubble .file-name,.message.peer .bubble .file-name{margin:0;font-size:.8rem;color:#666}.message.self .bubble a,.message.peer .bubble a{color:#3897f0;text-decoration:none;margin-top:.5rem}.message.self .bubble .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.peer .bubble .time{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message.self .bubble .video-container,.message.peer .bubble .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message.self .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.self .bubble .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;<style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.video-message .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-message video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.video-message .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.video-message:hover .play-button{opacity:1}.video-message .file-name{margin:0;font-size:.8rem;color:#666}.video-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.file-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.file-message .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.file-message .file-icon{font-size:2rem;margin-bottom:.5rem}.file-message .file-name{margin:0;font-size:.8rem;color:#666}.file-message a{color:#3897f0;text-decoration:none;margin-top:.5rem}.file-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.self .bubble{background-color:#e1ffc7;border-top-right-radius:12px;border-bottom-left-radius:12px;border-bottom-right-radius:4px;margin-left:auto;margin-right:0;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.peer .bubble{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.self .bubble:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message.self .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message.peer .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message.self .username,.message.peer .username{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message.self .bubble p,.message.peer .bubble p{color:#333;margin:0;padding-right:50px}.message.self .bubble .reactions,.message.peer .bubble .reactions,.message.self .bubble .reaction-button,.message.peer .bubble .reaction-button{display:none}.message.self .bubble .file-container,.message.peer .bubble .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message.self .bubble .file-icon,.message.peer .bubble .file-icon{font-size:2rem;margin-bottom:.5rem}.message.self .bubble .file-name,.message.peer .bubble .file-name{margin:0;font-size:.8rem;color:#666}.message.self .bubble a,.message.peer .bubble a{color:#3897f0;text-decoration:none;margin-top:.5rem}.message.self .bubble .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.peer .bubble .time{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message.self .bubble .video-container,.message.peer .bubble .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message.self .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.self .bubble .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;<style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.video-message .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-message video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.video-message .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.video-message:hover .play-button{opacity:1}.video-message .file-name{margin:0;font-size:.8rem;color:#666}.video-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.file-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.file-message .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.file-message .file-icon{font-size:2rem;margin-bottom:.5rem}.file-message .file-name{margin:0;font-size:.8rem;color:#666}.file-message a{color:#3897f0;text-decoration:none;margin-top:.5rem}.file-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.self .bubble{background-color:#e1ffc7;border-top-right-radius:12px;border-bottom-left-radius:12px;border-bottom-right-radius:4px;margin-left:auto;margin-right:0;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.peer .bubble{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.self .bubble:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message.self .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message.peer .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message.self .username,.message.peer .username{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message.self .bubble p,.message.peer .bubble p{color:#333;margin:0;padding-right:50px}.message.self .bubble .reactions,.message.peer .bubble .reactions,.message.self .bubble .reaction-button,.message.peer .bubble .reaction-button{display:none}.message.self .bubble .file-container,.message.peer .bubble .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message.self .bubble .file-icon,.message.peer .bubble .file-icon{font-size:2rem;margin-bottom:.5rem}.message.self .bubble .file-name,.message.peer .bubble .file-name{margin:0;font-size:.8rem;color:#666}.message.self .bubble a,.message.peer .bubble a{color:#3897f0;text-decoration:none;margin-top:.5rem}.message.self .bubble .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.peer .bubble .time{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message.self .bubble .video-container,.message.peer .bubble .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message.self .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.self .bubble .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;<style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.video-message .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-message video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.video-message .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.video-message:hover .play-button{opacity:1}.video-message .file-name{margin:0;font-size:.8rem;color:#666}.video-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.file-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.file-message .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.file-message .file-icon{font-size:2rem;margin-bottom:.5rem}.file-message .file-name{margin:0;font-size:.8rem;color:#666}.file-message a{color:#3897f0;text-decoration:none;margin-top:.5rem}.file-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.self .bubble{background-color:#e1ffc7;border-top-right-radius:12px;border-bottom-left-radius:12px;border-bottom-right-radius:4px;margin-left:auto;margin-right:0;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.peer .bubble{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.self .bubble:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message.self .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message.peer .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message.self .username,.message.peer .username{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message.self .bubble p,.message.peer .bubble p{color:#333;margin:0;padding-right:50px}.message.self .bubble .reactions,.message.peer .bubble .reactions,.message.self .bubble .reaction-button,.message.peer .bubble .reaction-button{display:none}.message.self .bubble .file-container,.message.peer .bubble .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message.self .bubble .file-icon,.message.peer .bubble .file-icon{font-size:2rem;margin-bottom:.5rem}.message.self .bubble .file-name,.message.peer .bubble .file-name{margin:0;font-size:.8rem;color:#666}.message.self .bubble a,.message.peer .bubble a{color:#3897f0;text-decoration:none;margin-top:.5rem}.message.self .bubble .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.peer .bubble .time{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message.self .bubble .video-container,.message.peer .bubble .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message.self .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.self .bubble .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;<style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.video-message .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-message video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.video-message .play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;padding:.5rem;font-size:1.5rem;cursor:pointer;opacity:0;transition:opacity .2s}.video-message:hover .play-button{opacity:1}.video-message .file-name{margin:0;font-size:.8rem;color:#666}.video-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.file-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;display:flex;flex-direction:column;align-items:center;position:relative}.file-message .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.file-message .file-icon{font-size:2rem;margin-bottom:.5rem}.file-message .file-name{margin:0;font-size:.8rem;color:#666}.file-message a{color:#3897f0;text-decoration:none;margin-top:.5rem}.file-message .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.self .bubble{background-color:#e1ffc7;border-top-right-radius:12px;border-bottom-left-radius:12px;border-bottom-right-radius:4px;margin-left:auto;margin-right:0;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.peer .bubble{background-color:#f0f0f0;border-top-left-radius:12px;border-bottom-right-radius:12px;border-bottom-left-radius:4px;margin-left:0;margin-right:auto;padding-right:1rem;padding-left:1rem;max-width:75%;box-shadow:0 2px 4px #0000001a}.message.self .bubble:before{content:"";position:absolute;top:0;right:-12px;width:20px;height:20px;background-color:#e1ffc7;transform:rotate(45deg);border-bottom-right-radius:4px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble:before{content:"";position:absolute;top:0;left:-12px;width:20px;height:20px;background-color:#f0f0f0;transform:rotate(45deg);border-bottom-left-radius:4px;box-shadow:0 2px 4px #0000001a}.message.self .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;right:10px}.message.peer .time{color:#666;font-size:.7rem;position:absolute;bottom:6px;left:10px}.message.self .username,.message.peer .username{font-size:.75rem;font-weight:700;margin-bottom:.25rem;color:#333}.message.self .bubble p,.message.peer .bubble p{color:#333;margin:0;padding-right:50px}.message.self .bubble .reactions,.message.peer .bubble .reactions,.message.self .bubble .reaction-button,.message.peer .bubble .reaction-button{display:none}.message.self .bubble .file-container,.message.peer .bubble .file-container{display:flex;flex-direction:column;align-items:center;margin-bottom:.5rem}.message.self .bubble .file-icon,.message.peer .bubble .file-icon{font-size:2rem;margin-bottom:.5rem}.message.self .bubble .file-name,.message.peer .bubble .file-name{margin:0;font-size:.8rem;color:#666}.message.self .bubble a,.message.peer .bubble a{color:#3897f0;text-decoration:none;margin-top:.5rem}.message.self .bubble .time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.message.peer .bubble .time{position:absolute;bottom:6px;left:10px;font-size:.7rem;color:#666}.message.self .bubble .video-container,.message.peer .bubble .video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.message.self .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.peer .bubble video{width:100%;border-radius:8px;box-shadow:0 2px 4px #0000001a}.message.self .bubble .play-button{position:absolute;<div class="chat-interface"> <div id="user-controls"> <input type="text" id="username-input" placeholder="Username" /> <input type="text" id="room-input" placeholder="Room name" /> <button id="create-room">Create Room</button> <button id="join-room">Join Room</button> </div> <div id="profile-modal" class="modal"> <div class="modal-content"> <h2>Edit Profile</h2> <input type="text" id="display-name" placeholder="Display Name" /> <input type="text" id="bio" placeholder="Bio" /> <button id="save-profile">Save Profile</button> </div> </div> <div id="room-info" style="display: none;"> <h3>Room: <span id="current-room"></span></h3> <button id="edit-profile">Edit Profile</button> </div> <div class="chat-container"> <div class="chat-messages" id="chat-messages"></div> <form id="chat-form" style="display: none;"> <input type="file" id="file-input" style="display: none;" /> <button type="button" id="upload-file" aria-label="Upload file"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"> <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/> </svg> </button> <input type="text" id="message-input" placeholder="Type a message..." /> <button type="submit" aria-label="Send message"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"> <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/> </svg> </button> </form> </div> </div> <div id="call-controls" style="display: none;"> <button id="start-call">Start Audio Call</button> <button id="end-call" style="display: none;">End Call</button> </div> <script> import Gun from "gun/gun";import SimplePeer from "simple-peer";// Initialize Gun const gun = Gun({peers: ["https://gun-manhattan.herokuapp.com/gun"]});let user: string | null = null;let currentRoom: string | null = null;let userProfile:{displayName:string;bio:string}={displayName:"",bio: ""}const chatMessages = document.getElementById("chat-messages") as HTMLDivElement;const chatForm = document.getElementById("chat-form") as HTMLFormElement;const messageInput = document.getElementById("message-input") as HTMLInputElement;const usernameInput = document.getElementById("username-input") as HTMLInputElement;const roomInput = document.getElementById("room-input") as HTMLInputElement;const createRoomButton = document.getElementById("create-room") as HTMLButtonElement;const joinRoomButton = document.getElementById("join-room") as HTMLButtonElement;const roomInfo = document.getElementById("room-info") as HTMLDivElement;const currentRoomSpan = document.getElementById("current-room") as HTMLSpanElement;const fileInput = document.getElementById("file-input") as HTMLInputElement;const uploadFileButton = document.getElementById("upload-file") as HTMLButtonElement;const editProfileButton = document.getElementById("edit-profile") as HTMLButtonElement;const profileModal = document.getElementById("profile-modal") as HTMLDivElement;const displayNameInput = document.getElementById("display-name") as HTMLInputElement;const bioInput = document.getElementById("bio") as HTMLInputElement;const saveProfileButton = document.getElementById("save-profile") as HTMLButtonElement;const startCallButton = document.getElementById("start-call") as HTMLButtonElement;const endCallButton = document.getElementById("end-call") as HTMLButtonElement;const callControls = document.getElementById("call-controls") as HTMLDivElement;let peer: SimplePeer.Instance | null = null;startCallButton.addEventListener("click",startCall);endCallButton.addEventListener("click",endCall);function enterRoom(room: string,isCreator: boolean){if (user && room){currentRoom = room;usernameInput.disabled = true;roomInput.disabled = true;createRoomButton.disabled = true;joinRoomButton.disabled = true;chatForm.style.display = "flex";roomInfo.style.display = "block";currentRoomSpan.textContent = room;addMessage(Date.now().toString(),`Welcome to room "${room}",${user}!`,true);if (isCreator){addMessage(Date.now().toString(),`You created this room.`,true)}else{addMessage(Date.now().toString(),`You joined this room.`,true)}listenToRoom(room);loadUserProfile();callControls.style.display = "block";listenForCalls(room)}}createRoomButton.addEventListener("click",() => {const username = usernameInput.value.trim(); const room = roomInput.value.trim(); if (username && room) {user = username; gun.get(`rooms`).get(room).put({created: Date.now()}); enterRoom(room,true);}});joinRoomButton.addEventListener("click",() => {const username = usernameInput.value.trim(); const room = roomInput.value.trim(); if (username && room) {user = username; gun.get(`rooms`).get(room).once((data: any) => {if (data) {enterRoom(room,false);} else {addMessage(Date.now().toString(),`Room "${room}" does not exist. Please create it first.`,true);}});}});chatForm.addEventListener("submit",(e) => {e.preventDefault(); const message = messageInput.value.trim(); if (message && user && currentRoom) {sendTextMessage(message); messageInput.value = "";} if (fileInput.files && fileInput.files.length > 0 && user && currentRoom) {sendFile(fileInput.files[0]); fileInput.value = "";}});function sendTextMessage(message: string){const messageId = Date.now().toString() + Math.random().toString(36).substr(2,9);const messageData ={id:messageId,user: user,type: "text",content: message,timestamp: Date.now()}gun.get(`rooms`).get(currentRoom).get("messages").set(messageData);addMessage(messageId,`${user}: ${message}`,true)}function sendFile(file: File){const reader = new FileReader();reader.onload = (e) =>{const fileData = e.target?.result as string;if (fileData && user && currentRoom){const messageId = Date.now().toString() + Math.random().toString(36).substr(2,9);const messageData ={id:messageId,user: user,type: file.type.startsWith("video/") ? "video" : "file",content: fileData,fileName: file.name,fileType: file.type,timestamp: Date.now()}gun.get(`rooms`).get(currentRoom).get("messages").set(messageData);if (file.type.startsWith("video/")){addVideoMessage(user,fileData,file.name,true,messageId)}else{addFileMessage(user,fileData,file.name,file.type,true,messageId)}console.log("File sent:",messageId,file.name,file.type)}else{console.error("Failed to load file or user/room not set")}}reader.onerror = (error) =>{console.error("Error reading file:",error);addMessage(Date.now().toString(),"Failed to send file",true)}reader.readAsDataURL(file)}uploadFileButton.addEventListener("click",() => {fileInput.click();});fileInput.addEventListener("change",(e) => {const files = (e.target as HTMLInputElement).files; if (files && files.length > 0) {sendFile(files[0]);}});function addFileMessage(username: string,fileData: string,fileName: string,fileType: string,isSelf = false,messageId: string){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate file message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message","file-message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const usernameElement = document.createElement("p");usernameElement.classList.add("username");usernameElement.textContent = username;bubbleElement.appendChild(usernameElement);const fileElement = document.createElement("div");fileElement.classList.add("file-container");if (fileType.startsWith("image/")){const imageElement = document.createElement("img");imageElement.src = fileData;imageElement.alt = fileName;imageElement.style.maxWidth = "200px";imageElement.style.maxHeight = "200px";fileElement.appendChild(imageElement)}else{const fileIconElement = document.createElement("div");fileIconElement.classList.add("file-icon");fileIconElement.textContent = getFileIcon(fileType);fileElement.appendChild(fileIconElement)}const fileNameElement = document.createElement("p");fileNameElement.classList.add("file-name");fileNameElement.textContent = fileName;fileElement.appendChild(fileNameElement);const downloadLink = document.createElement("a");downloadLink.href = fileData;downloadLink.download = fileName;downloadLink.textContent = "Download";fileElement.appendChild(downloadLink);bubbleElement.appendChild(fileElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}function getFileIcon(fileType: string): string{if (fileType.startsWith("image/")) return "🖼️";if (fileType.startsWith("audio/")) return "🎵";if (fileType.startsWith("video/")) return "🎥";if (fileType.includes("pdf")) return "📄";if (fileType.includes("word")) return "📝";if (fileType.includes("excel") || fileType.includes("spreadsheet")) return "📊";if (fileType.includes("powerpoint") || fileType.includes("presentation")) return "📽️";return "📁"}function addVideoMessage(username: string,videoData: string,fileName: string,isSelf = false,messageId: string){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate video message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message","video-message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const usernameElement = document.createElement("p");usernameElement.classList.add("username");usernameElement.textContent = username;bubbleElement.appendChild(usernameElement);const videoContainer = document.createElement("div");videoContainer.classList.add("video-container");const videoElement = document.createElement("video");videoElement.src = videoData;videoElement.controls = true;videoElement.preload = "metadata";videoElement.style.maxWidth = "100%";videoElement.style.maxHeight = "300px";const playButton = document.createElement("button");playButton.classList.add("play-button");playButton.innerHTML = "▶";playButton.addEventListener("click",() => {if (videoElement.paused) {videoElement.play();} else {videoElement.pause();}});videoContainer.appendChild(videoElement);videoContainer.appendChild(playButton);bubbleElement.appendChild(videoContainer);const fileNameElement = document.createElement("p");fileNameElement.classList.add("file-name");fileNameElement.textContent = fileName;bubbleElement.appendChild(fileNameElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}function listenToRoom(room: string){gun.get(`rooms`).get(room).get("messages").map().on(function(data: any,key: string) {if (data && data.user !== user) {if (data.type === "text") {addMessage(data.id,`${data.user}: ${data.content}`,false);} else if (data.type === "file") {console.log("Received file message:",data.id); addFileMessage(data.user,data.content,data.fileName,data.fileType,false,data.id);} else if (data.type === "video") {console.log("Received video message:",data.id); addVideoMessage(data.user,data.content,data.fileName,false,data.id);}}})}function addMessage(messageId: string,message: string,isSelf = false){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const textElement = document.createElement("p");textElement.textContent = message;bubbleElement.appendChild(textElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}editProfileButton.addEventListener("click",() => {profileModal.style.display = "block"; displayNameInput.value = userProfile.displayName; bioInput.value = userProfile.bio;});saveProfileButton.addEventListener("click",() => {userProfile.displayName = displayNameInput.value; userProfile.bio = bioInput.value; gun.get("users").get(user).put(userProfile); profileModal.style.display = "none";});function loadUserProfile(){gun.get("users").get(user).on((data: any) => {if (data) {userProfile = data; console.log("User profile loaded:",userProfile);}})}function startCall(){if (!user || !currentRoom) return;navigator.mediaDevices.getUserMedia({audio: true}) .then(stream => {peer = new SimplePeer({initiator: true,stream}); peer.on("signal",data => {// Send the offer to the other peer using Gun gun.get(`rooms`).get(currentRoom).get("call").put(JSON.stringify({type: "offer",data,from: user}));}); peer.on("stream",handleIncomingStream); startCallButton.style.display = "none"; endCallButton.style.display = "inline-block";}) .catch(err => console.error("Failed to get user media",err))}function handleIncomingStream(stream: MediaStream){const audio = new Audio();audio.srcObject = stream;audio.play()}function endCall(){if (peer){peer.destroy();peer = null}startCallButton.style.display = "inline-block";endCallButton.style.display = "none";gun.get(`rooms`).get(currentRoom).get("call").put(null)}function listenForCalls(room: string){gun.get(`rooms`).get(room).get("call").on((data: any) => {if (data && typeof data === "string") {const callData = JSON.parse(data); if (callData.type === "offer" && callData.from !== user) {handleIncomingCall(callData.data);} else if (callData.type === "answer" && peer) {peer.signal(callData.data);}}})}function handleIncomingCall(offerData: any){if (confirm("Incoming call. Accept?")){navigator.mediaDevices.getUserMedia({audio: true}) .then(stream => {peer = new SimplePeer({initiator: false,stream}); peer.on("signal",data => {// Send the answer back to the caller gun.get(`rooms`).get(currentRoom).get("call").put(JSON.stringify({type: "answer",data,from: user}));}); peer.on("stream",handleIncomingStream); // Signal the peer with the offer data peer.signal(offerData); startCallButton.style.display = "none"; endCallButton.style.display = "inline-block";}) .catch(err => console.error("Failed to get user media",err))}}</script> <style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{<div class="chat-interface"> <div id="user-controls"> <input type="text" id="username-input" placeholder="Username" /> <input type="text" id="room-input" placeholder="Room name" /> <button id="create-room">Create Room</button> <button id="join-room">Join Room</button> </div> <div id="profile-modal" class="modal"> <div class="modal-content"> <h2>Edit Profile</h2> <input type="text" id="display-name" placeholder="Display Name" /> <input type="text" id="bio" placeholder="Bio" /> <button id="save-profile">Save Profile</button> </div> </div> <div id="room-info" style="display: none;"> <h3>Room: <span id="current-room"></span></h3> <button id="edit-profile">Edit Profile</button> </div> <div class="chat-container"> <div class="chat-messages" id="chat-messages"></div> <form id="chat-form" style="display: none;"> <input type="file" id="file-input" style="display: none;" /> <button type="button" id="upload-file" aria-label="Upload file"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"> <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/> </svg> </button> <input type="text" id="message-input" placeholder="Type a message..." /> <button type="submit" aria-label="Send message"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"> <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/> </svg> </button> </form> </div> </div> <script> import Gun from "gun/gun";// Initialize Gun const gun = Gun({peers: ["https://gun-manhattan.herokuapp.com/gun"]});let user: string | null = null;let currentRoom: string | null = null;let userProfile:{displayName:string;bio:string}={displayName:"",bio: ""}const chatMessages = document.getElementById("chat-messages") as HTMLDivElement;const chatForm = document.getElementById("chat-form") as HTMLFormElement;const messageInput = document.getElementById("message-input") as HTMLInputElement;const usernameInput = document.getElementById("username-input") as HTMLInputElement;const roomInput = document.getElementById("room-input") as HTMLInputElement;const createRoomButton = document.getElementById("create-room") as HTMLButtonElement;const joinRoomButton = document.getElementById("join-room") as HTMLButtonElement;const roomInfo = document.getElementById("room-info") as HTMLDivElement;const currentRoomSpan = document.getElementById("current-room") as HTMLSpanElement;const fileInput = document.getElementById("file-input") as HTMLInputElement;const uploadFileButton = document.getElementById("upload-file") as HTMLButtonElement;const editProfileButton = document.getElementById("edit-profile") as HTMLButtonElement;const profileModal = document.getElementById("profile-modal") as HTMLDivElement;const displayNameInput = document.getElementById("display-name") as HTMLInputElement;const bioInput = document.getElementById("bio") as HTMLInputElement;const saveProfileButton = document.getElementById("save-profile") as HTMLButtonElement;function enterRoom(room: string,isCreator: boolean){if (user && room){currentRoom = room;usernameInput.disabled = true;roomInput.disabled = true;createRoomButton.disabled = true;joinRoomButton.disabled = true;chatForm.style.display = "flex";roomInfo.style.display = "block";currentRoomSpan.textContent = room;addMessage(Date.now().toString(),`Welcome to room "${room}",${user}!`,true);if (isCreator){addMessage(Date.now().toString(),`You created this room.`,true)}else{addMessage(Date.now().toString(),`You joined this room.`,true)}listenToRoom(room);loadUserProfile()}}createRoomButton.addEventListener("click",() => {const username = usernameInput.value.trim(); const room = roomInput.value.trim(); if (username && room) {user = username; gun.get(`rooms`).get(room).put({created: Date.now()}); enterRoom(room,true);}});joinRoomButton.addEventListener("click",() => {const username = usernameInput.value.trim(); const room = roomInput.value.trim(); if (username && room) {user = username; gun.get(`rooms`).get(room).once((data: any) => {if (data) {enterRoom(room,false);} else {addMessage(Date.now().toString(),`Room "${room}" does not exist. Please create it first.`,true);}});}});chatForm.addEventListener("submit",(e) => {e.preventDefault(); const message = messageInput.value.trim(); if (message && user && currentRoom) {sendTextMessage(message); messageInput.value = "";} if (fileInput.files && fileInput.files.length > 0 && user && currentRoom) {sendFile(fileInput.files[0]); fileInput.value = "";}});function sendTextMessage(message: string){const messageId = Date.now().toString() + Math.random().toString(36).substr(2,9);const messageData ={id:messageId,user: user,type: "text",content: message,timestamp: Date.now()}gun.get(`rooms`).get(currentRoom).get("messages").set(messageData);addMessage(messageId,`${user}: ${message}`,true)}function sendFile(file: File){const reader = new FileReader();reader.onload = (e) =>{const fileData = e.target?.result as string;if (fileData && user && currentRoom){const messageId = Date.now().toString() + Math.random().toString(36).substr(2,9);const messageData ={id:messageId,user: user,type: file.type.startsWith("video/") ? "video" : "file",content: fileData,fileName: file.name,fileType: file.type,timestamp: Date.now()}gun.get(`rooms`).get(currentRoom).get("messages").set(messageData);if (file.type.startsWith("video/")){addVideoMessage(user,fileData,file.name,true,messageId)}else{addFileMessage(user,fileData,file.name,file.type,true,messageId)}console.log("File sent:",messageId,file.name,file.type)}else{console.error("Failed to load file or user/room not set")}}reader.onerror = (error) =>{console.error("Error reading file:",error);addMessage(Date.now().toString(),"Failed to send file",true)}reader.readAsDataURL(file)}uploadFileButton.addEventListener("click",() => {fileInput.click();});fileInput.addEventListener("change",(e) => {const files = (e.target as HTMLInputElement).files; if (files && files.length > 0) {sendFile(files[0]);}});function addFileMessage(username: string,fileData: string,fileName: string,fileType: string,isSelf = false,messageId: string){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate file message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message","file-message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const usernameElement = document.createElement("p");usernameElement.classList.add("username");usernameElement.textContent = username;bubbleElement.appendChild(usernameElement);const fileElement = document.createElement("div");fileElement.classList.add("file-container");if (fileType.startsWith("image/")){const imageElement = document.createElement("img");imageElement.src = fileData;imageElement.alt = fileName;imageElement.style.maxWidth = "200px";imageElement.style.maxHeight = "200px";fileElement.appendChild(imageElement)}else{const fileIconElement = document.createElement("div");fileIconElement.classList.add("file-icon");fileIconElement.textContent = getFileIcon(fileType);fileElement.appendChild(fileIconElement)}const fileNameElement = document.createElement("p");fileNameElement.classList.add("file-name");fileNameElement.textContent = fileName;fileElement.appendChild(fileNameElement);const downloadLink = document.createElement("a");downloadLink.href = fileData;downloadLink.download = fileName;downloadLink.textContent = "Download";fileElement.appendChild(downloadLink);bubbleElement.appendChild(fileElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}function getFileIcon(fileType: string): string{if (fileType.startsWith("image/")) return "🖼️";if (fileType.startsWith("audio/")) return "🎵";if (fileType.startsWith("video/")) return "🎥";if (fileType.includes("pdf")) return "📄";if (fileType.includes("word")) return "📝";if (fileType.includes("excel") || fileType.includes("spreadsheet")) return "📊";if (fileType.includes("powerpoint") || fileType.includes("presentation")) return "📽️";return "📁"}function addVideoMessage(username: string,videoData: string,fileName: string,isSelf = false,messageId: string){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate video message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message","video-message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const usernameElement = document.createElement("p");usernameElement.classList.add("username");usernameElement.textContent = username;bubbleElement.appendChild(usernameElement);const videoContainer = document.createElement("div");videoContainer.classList.add("video-container");const videoElement = document.createElement("video");videoElement.src = videoData;videoElement.controls = true;videoElement.preload = "metadata";videoElement.style.maxWidth = "100%";videoElement.style.maxHeight = "300px";const playButton = document.createElement("button");playButton.classList.add("play-button");playButton.innerHTML = "▶";playButton.addEventListener("click",() => {if (videoElement.paused) {videoElement.play();} else {videoElement.pause();}});videoContainer.appendChild(videoElement);videoContainer.appendChild(playButton);bubbleElement.appendChild(videoContainer);const fileNameElement = document.createElement("p");fileNameElement.classList.add("file-name");fileNameElement.textContent = fileName;bubbleElement.appendChild(fileNameElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}function listenToRoom(room: string){gun.get(`rooms`).get(room).get("messages").map().on(function(data: any,key: string) {if (data && data.user !== user) {if (data.type === "text") {addMessage(data.id,`${data.user}: ${data.content}`,false);} else if (data.type === "file") {console.log("Received file message:",data.id); addFileMessage(data.user,data.content,data.fileName,data.fileType,false,data.id);} else if (data.type === "video") {console.log("Received video message:",data.id); addVideoMessage(data.user,data.content,data.fileName,false,data.id);}}})}function addMessage(messageId: string,message: string,isSelf = false){const existingMessage = document.getElementById(messageId);if (existingMessage){console.log("Duplicate message prevented:",messageId);return}const messageElement = document.createElement("div");messageElement.id = messageId;messageElement.classList.add("message",isSelf ? "self" : "peer");const bubbleElement = document.createElement("div");bubbleElement.classList.add("bubble");const textElement = document.createElement("p");textElement.textContent = message;bubbleElement.appendChild(textElement);const timeElement = document.createElement("span");timeElement.classList.add("time");timeElement.textContent = new Date().toLocaleTimeString([],{hour: "2-digit",minute: "2-digit"});bubbleElement.appendChild(timeElement);messageElement.appendChild(bubbleElement);chatMessages.appendChild(messageElement);chatMessages.scrollTop = chatMessages.scrollHeight}editProfileButton.addEventListener("click",() => {profileModal.style.display = "block"; displayNameInput.value = userProfile.displayName; bioInput.value = userProfile.bio;});saveProfileButton.addEventListener("click",() => {userProfile.displayName = displayNameInput.value; userProfile.bio = bioInput.value; gun.get("users").get(user).put(userProfile); profileModal.style.display = "none";});function loadUserProfile(){gun.get("users").get(user).on((data: any) => {if (data) {userProfile = data; console.log("User profile loaded:",userProfile);}})}</script> <style> .chat-interface{max-width:400px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background-color:#fff;border-radius:12px;box-shadow:0 2px 10px #0000001a;overflow:hidden}#user-controls{padding:1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#user-controls input{width:calc(50% - 5px);margin-bottom:10px}#user-controls button{width:calc(50% - 5px)}#room-info{padding:.5rem 1rem;background-color:#f9f9f9;border-bottom:1px solid #eee}#room-info h3{margin:0;font-size:1rem;font-weight:600}.chat-container{display:flex;flex-direction:column;height:500px}.chat-messages{flex-grow:1;overflow-y:auto;padding:1rem;background-color:#fff;display:flex;flex-direction:column}.message{max-width:75%;margin-bottom:1rem;display:flex;flex-direction:column}.message.self{align-self:flex-end}.message.peer{align-self:flex-start}.bubble{padding:.75rem 1rem;border-radius:18px;position:relative;word-wrap:break-word;box-shadow:0 2px 4px #0000001a;background-color:#f0f0f0;color:#000}.message.self .bubble{border-top-right-radius:4px}.message.peer .bubble{border-top-left-radius:4px}.bubble p{margin:0;padding-right:50px}.time{position:absolute;bottom:6px;right:10px;font-size:.7rem;color:#666}.image-message .bubble{padding:.25rem;background-color:transparent;box-shadow:none}.image-message img{display:block;max-width:100%;border-radius:12px;margin-bottom:.25rem;box-shadow:0 1px 2px #0000001a}.username{font-size:.75rem;font-weight:700;margin-bottom:.25rem}#chat-form{display:flex;align-items:center;padding:.5rem;background-color:#fff;border-top:1px solid #eee}input[type=text]{flex-grow:1;padding:.5rem;border:1px solid #dbdbdb;border-radius:21px;font-size:.9rem;color:#000}button{background-color:transparent;border:none;cursor:pointer;padding:.5rem;margin:0 .25rem;color:#3897f0;transition:color .2s}button:hover{color:#1877f2}#upload-file,#chat-form button[type=submit]{display:flex;align-items:center;justify-content:center}#upload-file svg,#chat-form button[type=submit] svg{width:24px;height:24px}.modal{display:none;position:fixed;z-index:1;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:#0006}.modal-content{background-color:#fefefe;margin:15% auto;padding:20px;border:1px solid #888;width:80%;max-width:500px}.reactions,.reaction-button{display:none}.video-message .bubble{padding:.5rem;background-color:transparent;box-shadow:none}.video-container{position:relative;width:100%;max-width:300px;margin-bottom:.5rem}.video-container video{width:100%;border-radius:8px;background-color:#000}.play-button{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#00000080;color:#fff;border:none;border-radius:50%;width:50px;height:50px;font-size:24px;cursor:pointer;display:flex;align-items:center;justify-content:center}.play-button:hover{background-color:#000000b3}.file-message .bubble{background-color:#f0f0f0;border-radius:12px;padding:.5rem;box-shadow:0 2px 4px #0000001a;margin-bottom:.5rem;display:flex;flex-direction:column;align-items:flex-start}.file-message .username{font-weight:700;margin-bottom:.25rem}.file-message .file-container{display:flex;align-items:center}.file-message .file-icon{font-size:2rem;margin-right:.5rem}.file-message .file-name{margin-right:.5rem}.file-message a{color:#3897f0;text-decoration:none}.file-message a:hover{text-decoration:underline}}}}}}}}main[data-astro-cid-j7pv25f6]{display:flex;justify-content:center;align-items:center;min-height:100vh;background-color:#fafafa}
