neuro-simulator 0.6.0__py3-none-any.whl → 0.6.1__py3-none-any.whl
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.
- neuro_simulator/client/assets/index-C_kzLmQy.css +1 -0
- neuro_simulator/client/assets/index-DRLWJPZv.js +7 -0
- neuro_simulator/client/assets/inter-cyrillic-400-normal-BLGc9T1a.woff2 +0 -0
- neuro_simulator/client/assets/inter-cyrillic-400-normal-alAqRL36.woff +0 -0
- neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-BE2fNs0E.woff +0 -0
- neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-Dc4VJyIJ.woff2 +0 -0
- neuro_simulator/client/assets/inter-greek-400-normal-C3I71FoW.woff +0 -0
- neuro_simulator/client/assets/inter-greek-400-normal-DxZsaF_h.woff2 +0 -0
- neuro_simulator/client/assets/inter-greek-ext-400-normal-Bput3-QP.woff2 +0 -0
- neuro_simulator/client/assets/inter-greek-ext-400-normal-XIH6-K3k.woff +0 -0
- neuro_simulator/client/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- neuro_simulator/client/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- neuro_simulator/client/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- neuro_simulator/client/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- neuro_simulator/client/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- neuro_simulator/client/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- neuro_simulator/client/avatar.webp +0 -0
- neuro_simulator/client/background.webp +0 -0
- neuro_simulator/client/background_old.webp +0 -0
- neuro_simulator/client/banner.jpeg +0 -0
- neuro_simulator/client/channel_points.png +0 -0
- neuro_simulator/client/error.mp3 +0 -0
- neuro_simulator/client/favicon.ico +0 -0
- neuro_simulator/client/fonts/causten.woff2 +0 -0
- neuro_simulator/client/fonts/comic.woff2 +0 -0
- neuro_simulator/client/fonts/first-coffee.woff2 +0 -0
- neuro_simulator/client/fonts/noto-sans-sc.woff2 +0 -0
- neuro_simulator/client/index.html +306 -0
- neuro_simulator/client/neuro_start.mp4 +0 -0
- neuro_simulator/client/neurosama.png +0 -0
- neuro_simulator/client/sc_pink.png +0 -0
- neuro_simulator/client/sc_purple.png +0 -0
- neuro_simulator/client/sub_badge.svg +4 -0
- neuro_simulator/client/user_avatar.jpg +0 -0
- neuro_simulator/core/application.py +27 -0
- {neuro_simulator-0.6.0.dist-info → neuro_simulator-0.6.1.dist-info}/METADATA +1 -1
- {neuro_simulator-0.6.0.dist-info → neuro_simulator-0.6.1.dist-info}/RECORD +40 -6
- {neuro_simulator-0.6.0.dist-info → neuro_simulator-0.6.1.dist-info}/WHEEL +0 -0
- {neuro_simulator-0.6.0.dist-info → neuro_simulator-0.6.1.dist-info}/entry_points.txt +0 -0
- {neuro_simulator-0.6.0.dist-info → neuro_simulator-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
:root{--twitch-purple: #9147FF;--twitch-dark-bg: #F2F2F2;--twitch-chat-bg: #FFFFFF;--twitch-text-color: #18181B;--twitch-secondary-text-color: #6A6A6D;--twitch-border-color: #D3D3D3;--twitch-hover-bg: #EFEFF1;--twitch-button-bg: #EFEFF1;--twitch-button-text-color: #0E0E10;--twitch-button-hover-bg: #DFDFE1;--twitch-primary-button-bg: var(--twitch-purple);--twitch-primary-button-text-color: #FFFFFF;--twitch-primary-button-hover-bg: #772CE8;--twitch-tag-bg: #EFEFF1;--twitch-tag-text-color: #6A6A6D;--twitch-live-red: #EB0400;--twitch-viewer-red: #971311;--sidebar-width: 340px;--header-height: 50px;--neuro-shadow-color: #32003C;--neuro-avatar-display-width-percent: 50%;--fast-transition: background-color .2s, color .2s;--twitch-dark-button-bg: #333333;--twitch-dark-button-hover-bg: #444444;--twitch-dark-button-text-color: #FFFFFF;--sc-pink-bg-color: #f68795;--sc-purple-bg-color: #b97ad5}@font-face{font-family:First Coffee;src:url(../fonts/first-coffee.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Comic Sans MS;src:url(../fonts/comic.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Noto Sans SC;src:url(../fonts/noto-sans-sc.woff2) format("woff2");font-weight:400;font-style:normal}@font-face{font-family:Causten;src:url(../fonts/causten.woff2) format("woff2");font-weight:400;font-style:normal}html,body{height:100%;margin:0;padding:0;overflow:hidden}body{font-family:Inter,Arial,sans-serif;display:flex;flex-direction:column;background-color:var(--twitch-dark-bg);color:var(--twitch-text-color)}.twitch-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 10px;border-radius:100px;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:var(--twitch-button-bg);color:var(--twitch-button-text-color);height:32px;box-sizing:border-box;line-height:1}.twitch-button svg{width:1.2em;height:1.2em;fill:currentColor;vertical-align:middle}.twitch-button span{padding-top:2px}.twitch-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.twitch-button.subscribe-button{background-color:var(--twitch-primary-button-bg);color:var(--twitch-primary-button-text-color)}.twitch-button.subscribe-button:hover:not(:disabled){background-color:var(--twitch-primary-button-hover-bg)}.twitch-button.icon-button{width:32px;padding:0;border-radius:50%}.nav-icon-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.nav-icon-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg);border-radius:50%}.nav-icon-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.search-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.search-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.search-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.chat-toolbar-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);width:32px;height:32px;box-sizing:border-box;line-height:1}.chat-toolbar-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg);border-radius:50%}.chat-toolbar-button svg{width:20px;height:20px;fill:currentColor;vertical-align:middle}.chat-toolbar-button img{width:20px;height:20px;object-fit:contain}.video-overlay-chat-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:#0009;color:#fff;width:32px;height:32px;box-sizing:border-box;line-height:1}.video-overlay-chat-button:hover:not(:disabled){background-color:#000c;border-radius:50%}.video-overlay-chat-button svg{width:20px;height:20px;fill:#fff;vertical-align:middle}.mute-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0;border-radius:50%;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:#0009;color:#fff;width:32px;height:32px;box-sizing:border-box;line-height:1}.mute-button:hover:not(:disabled){background-color:#000c;border-radius:50%}.mute-button svg{width:20px;height:20px;fill:#fff;vertical-align:middle}.twitch-button.dark{background-color:var(--twitch-dark-button-bg);color:var(--twitch-dark-button-text-color)}.twitch-button.dark:hover:not(:disabled){background-color:var(--twitch-dark-button-hover-bg)}.twitch-button.dark svg,.twitch-button.dark img{fill:var(--twitch-dark-button-text-color)}.twitch-button img{width:20px;height:20px;object-fit:contain;vertical-align:middle}.chat-toolbar-text-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 10px;border-radius:100px;font-size:.9em;font-weight:600;cursor:pointer;border:none;transition:var(--fast-transition);outline:none;background-color:transparent;color:var(--twitch-button-text-color);height:32px;box-sizing:border-box;line-height:1}.chat-toolbar-text-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}.chat-toolbar-text-button svg,.chat-toolbar-text-button img{width:1.2em;height:1.2em;fill:currentColor;vertical-align:middle;object-fit:contain}.chat-toolbar-text-button .points-value{padding-top:2px}.chat-toolbar-text-button .channel-points-icon{width:1.2em;height:1.2em;object-fit:contain}#twitch-header{height:var(--header-height);background-color:var(--twitch-chat-bg);display:flex;justify-content:space-between;align-items:center;padding:0 1rem;border-bottom:1px solid var(--twitch-border-color);flex-shrink:0;z-index:100;color:var(--twitch-text-color);gap:1rem}.top-nav-left,.top-nav-right{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.top-nav-center{flex-grow:1;flex-shrink:1;display:flex;justify-content:center;min-width:150px}.twitch-logo-link{display:flex;align-items:center;text-decoration:none;margin-left:.5rem}.twitch-logo-container{margin:0;transform:scaleY(-1);transition:transform .3s ease-out}.twitch-logo-container:hover{transform:scaleY(1)}.twitch-logo-svg .logo-body{fill:var(--twitch-purple)}.twitch-logo-svg .logo-face{fill:var(--twitch-chat-bg)}.twitch-logo-svg .logo-eye-path{fill:var(--twitch-purple)}.nav-links-main{display:flex;gap:1.5rem;margin-left:1.5rem}.nav-link{display:flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--twitch-text-color);font-weight:500;font-size:1.1rem;padding:.25rem .5rem;border-radius:4px;transition:var(--fast-transition)}.nav-link:hover{color:var(--twitch-purple)}.nav-link p{margin:0}.search-container{display:flex;align-items:center;width:100%;max-width:400px;border:1px solid var(--twitch-border-color);border-radius:8px;overflow:hidden;background-color:var(--twitch-button-bg)}.search-input{flex:1 1 0;min-width:50px;padding:.5rem .75rem;border:none;background-color:transparent;color:var(--twitch-text-color);font-size:.95rem;outline:none}.search-input::placeholder{color:var(--twitch-secondary-text-color)}.search-button{flex-shrink:0;display:flex;align-items:center;justify-content:center;padding:0 .5rem}.search-button svg{fill:var(--twitch-text-color)}.top-nav-right{gap:.75rem}.nav-user-avatar-button{background:none;border:none;cursor:pointer;padding:0;border-radius:50%;width:32px;height:32px;overflow:hidden;flex-shrink:0}.user-avatar-wrapper{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.user-avatar-img{width:100%;height:100%;object-fit:cover;border-radius:50%}#main-content-wrapper{flex-grow:1;display:flex;overflow:hidden;position:relative}#stream-and-info-container{flex-grow:1;flex-shrink:1;display:flex;flex-direction:column;overflow-y:auto;min-height:0;scrollbar-width:none}#stream-and-info-container::-webkit-scrollbar{display:none}#stream-display-viewport{flex-grow:1;background-color:#000;display:flex;justify-content:center;align-items:center;overflow:hidden;min-height:0;position:relative}#mute-button{position:absolute;bottom:1rem;left:1rem;z-index:60}#stream-display-area{position:relative;background-color:#000;overflow:hidden;flex-shrink:0;visibility:hidden;opacity:0;transition:opacity .3s ease-in-out;container-type:size;container-name:streamArea}#background-display,#startup-video-overlay{position:absolute;top:0;left:0;width:100%;height:100%}#background-display{z-index:0;pointer-events:none;background:url(../background.webp) center/cover no-repeat}#neuro-static-avatar-container{position:absolute;left:70%;transform:translate(-50%);width:var(--neuro-avatar-display-width-percent);height:auto;overflow:visible;pointer-events:none;z-index:15;visibility:hidden;transition:transform .5s ease-in-out}#neuro-static-avatar-container img{width:100%;height:auto;display:block}.spin-animation{animation:spin-counter-clockwise .75s linear}@keyframes spin-counter-clockwise{0%{transform:translate(-50%) rotate(0)}to{transform:translate(-50%) rotate(-360deg)}}#neuro-static-avatar-container.zoom-in{transform:translate(-50%) scale(1.15)}#neuro-caption{position:absolute;bottom:5%;left:50%;transform:translate(-50%);width:90%;padding:10px 20px;border-radius:10px;text-align:center;font-weight:400;opacity:1;pointer-events:none;z-index:20;color:#fff;transition:none;text-shadow:0 0 4px var(--neuro-shadow-color),0 0 4px var(--neuro-shadow-color),0 0 4px var(--neuro-shadow-color);font-family:First Coffee,Noto Sans SC;font-size:3cqi}#neuro-caption:not(.show){opacity:0}#startup-video-overlay{background-color:#000;z-index:10;display:flex;justify-content:center;align-items:center;opacity:1;transition:none}#startup-video-overlay.hidden{opacity:0;pointer-events:none;visibility:hidden}#startup-video{width:100%;height:100%;object-fit:contain}#show-chat-button{position:absolute;top:1rem;right:1rem;z-index:60;background-color:#0009;border:none;border-radius:50%;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;transition:background-color .2s,opacity .3s ease-in-out,visibility .3s ease-in-out;opacity:0;visibility:hidden;pointer-events:none;width:32px;height:32px;box-sizing:border-box}#show-chat-button:hover{background-color:#000c}#show-chat-button svg{width:20px;height:20px;fill:currentColor}body.chat-collapsed #show-chat-button{opacity:1;visibility:visible;pointer-events:auto}#twitch-chat-overlay{position:absolute;top:0;left:0;width:25%;height:50%;background-color:transparent;color:#fff;padding:10px;overflow-y:auto;z-index:5;font-size:1.5cqi;scrollbar-width:none;font-family:Comic Sans MS,Noto Sans SC;pointer-events:none;-webkit-user-select:none;user-select:none;scroll-behavior:auto}#twitch-chat-overlay::-webkit-scrollbar{display:none}#twitch-chat-overlay *{pointer-events:none;-webkit-user-select:none;user-select:none}#twitch-chat-overlay .chat-line__message{padding:2px 0;margin:0;text-shadow:-1px -1px 0 #00000080,1px -1px 0 #00000080,-1px 1px 0 #00000080,1px 1px 0 #00000080,2px 2px 0 rgba(0,0,0,.5);font-size:1.5cqi}#twitch-chat-overlay .chat-line__username{font-weight:700;margin-right:.25rem;font-size:1.5cqi}#twitch-chat-overlay .text-fragment{font-weight:700;color:#fff;font-size:1.5cqi}#twitch-chat-overlay .chat-author__display-name{font-size:1.5cqi}#twitch-chat-overlay .chat-line__message-container>span:not(.chat-line__username){font-weight:700;color:#fff;text-shadow:-1px -1px 0 #00000080,1px -1px 0 #00000080,-1px 1px 0 #00000080,1px 1px 0 #00000080,2px 2px 0 rgba(0,0,0,.5);font-size:1.5cqi}#stream-display-viewport *:not(.mute-button):not(#show-chat-button){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#stream-info{padding:10px 20px;background-color:var(--twitch-chat-bg);border-bottom:1px solid var(--twitch-border-color)}.stream-info-layout{display:flex;gap:16px;align-items:center}.stream-info-left-column{flex-shrink:0}.streamer-avatar-link{display:block;position:relative}.streamer-avatar-wrapper{position:relative}#streamer-avatar{width:64px;height:64px;border-radius:50%;border:2px solid transparent;transition:border-color .2s}.streamer-avatar-link:hover #streamer-avatar{border-color:var(--twitch-purple)}.live-indicator-wrapper{position:absolute;bottom:-2px;left:50%;transform:translate(-50%);z-index:2}.live-indicator-rect{background-color:var(--twitch-live-red);color:#fff;font-size:.7em;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;border:2px solid var(--twitch-chat-bg);text-transform:uppercase}.stream-info-right-column{display:flex;flex-direction:column;flex-grow:1;gap:4px;min-width:0}.stream-info-main-row{display:flex;justify-content:space-between;align-items:center}.streamer-info-and-name{display:flex;align-items:center;gap:6px;min-width:0}#streamer-nickname{margin:0;font-size:1.2em;color:var(--twitch-text-color);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.verified-badge svg{width:16px;height:16px;fill:var(--twitch-purple)}.main-action-buttons{display:flex;gap:6px;align-items:center;flex-shrink:0}#stream-info .twitch-button{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:6px 10px;box-sizing:border-box}#stream-info .twitch-button.follow-button,#stream-info .twitch-button.icon-button{width:30px;padding:0}#stream-info .twitch-button:not(.follow-button):not(.icon-button){padding-top:0;padding-bottom:1px}#stream-info .twitch-button svg{width:18px;height:18px}.stream-info-details-row{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.stream-details-left{display:flex;flex-direction:column;gap:6px;min-width:0}#stream-title-full{margin:0;font-size:.9em;font-weight:600;color:var(--twitch-text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.stream-category-and-tags{display:flex;align-items:center;gap:6px;min-width:0}.stream-category{font-size:.9em;color:var(--twitch-purple);text-decoration:none;font-weight:500;transition:color .2s;flex-shrink:0}.stream-category:hover{text-decoration:underline}.stream-tags{display:flex;gap:6px;align-items:center;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.stream-tags::-webkit-scrollbar{display:none}.stream-tag{display:inline-flex;align-items:center;padding:3px 8px;border-radius:100px;background-color:var(--twitch-tag-bg);color:var(--twitch-tag-text-color);font-size:.75em;font-weight:600;text-decoration:none;transition:background-color .2s;height:20px;box-sizing:border-box;flex-shrink:0}.stream-tag:hover{background-color:var(--twitch-button-hover-bg)}.stream-details-right{display:flex;align-items:center;gap:12px;flex-shrink:0}.stream-stats-section{display:flex;gap:0px;align-items:center;font-size:.9em;color:var(--twitch-secondary-text-color);justify-content:flex-end}.viewer-count,.stream-duration{display:flex;align-items:center;gap:4px}.viewer-count{color:var(--twitch-viewer-red)}.viewer-count svg{width:18px;height:18px;fill:currentColor}.viewer-count strong{font-weight:600}.stream-duration .duration-text{font-weight:500;color:var(--twitch-text-color);min-width:4.5em;text-align:right}.stream-secondary-actions{display:flex;gap:6px}.stream-secondary-actions .twitch-button{background-color:transparent}.stream-secondary-actions .twitch-button:hover:not(:disabled){background-color:var(--twitch-button-hover-bg)}#chat-sidebar{width:var(--sidebar-width);min-width:var(--sidebar-width);background-color:var(--twitch-chat-bg);flex-shrink:0;display:flex;flex-direction:column;border-left:1px solid var(--twitch-border-color);z-index:50;height:100%;transition:width .3s ease-in-out,min-width .3s ease-in-out}#chat-sidebar.collapsed{width:0;min-width:0;overflow:hidden;visibility:hidden;pointer-events:none}.chat-sidebar-header{height:3.5rem;display:flex;justify-content:space-between;align-items:center;padding:0 1rem;border-bottom:1px solid var(--twitch-border-color);flex-shrink:0;position:relative}.chat-header-left,.chat-header-right{display:flex;align-items:center;gap:.5rem;z-index:1}.chat-sidebar-header .chat-title{position:absolute;left:50%;transform:translate(-50%);margin:0;font-size:1rem;font-weight:600;color:var(--twitch-text-color)}.chat-toggle-button{display:flex;align-items:center;justify-content:center;background:none;border:none;border-radius:50%;padding:0;cursor:pointer;color:var(--twitch-text-color);transition:background-color .2s;width:32px;height:32px;box-sizing:border-box}.chat-toggle-button:hover{background-color:var(--twitch-hover-bg)}.chat-toggle-button svg{width:20px;height:20px;fill:currentColor}#chat-sidebar.collapsed .chat-toggle-button{transform:rotate(180deg)}.messages-display{flex-grow:1;overflow-y:auto;padding:.5rem 1rem;min-height:0;scrollbar-width:thin;scrollbar-color:#B0B0B0 var(--twitch-hover-bg)}.messages-display::-webkit-scrollbar{width:8px}.messages-display::-webkit-scrollbar-track{background:var(--twitch-hover-bg);border-radius:4px}.messages-display::-webkit-scrollbar-thumb{background:#b0b0b0;border-radius:4px}.messages-display::-webkit-scrollbar-thumb:hover{background:#888}.chat-line__message{display:flex;align-items:flex-start;padding:.3rem 0;line-height:1.5;word-wrap:break-word;color:var(--twitch-text-color);border-radius:4px}.chat-line__message:hover{background-color:#ffffff0d}.chat-line__message-container{display:inline}.chat-line__username{font-weight:700;display:inline-block;margin-right:.1rem;cursor:pointer}.chat-author__display-name{font-size:.9rem}.text-fragment{font-size:.9rem;color:var(--twitch-text-color)}.chat-line__message.user-sent-message{background-color:#9147ff1a;border-left:3px solid var(--twitch-purple);padding-left:.75rem}.chat-line__message.system-message{color:var(--twitch-secondary-text-color);font-style:italic;font-size:.85rem;justify-content:center}.chat-line__message.system-message .chat-line__username,.chat-line__message.system-message .text-fragment{font-size:inherit;color:inherit;font-weight:400}.chat-input-area{padding:.75rem 1rem;border-top:1px solid var(--twitch-border-color);flex-shrink:0}.chat-input-and-buttons{display:flex;flex-direction:column;gap:.5rem}.chat-input-textarea-container{display:flex;align-items:center;border:1px solid var(--twitch-border-color);border-radius:4px;background-color:var(--twitch-button-bg);padding:0 .5rem;transition:var(--fast-transition)}.chat-input-textarea-container:focus-within{border-color:var(--twitch-purple);box-shadow:0 0 0 2px #9147ff33}.chat-input-prefix-icons,.chat-input-suffix-icons{display:flex;align-items:center}.chat-input-wrapper{flex-grow:1}.chat-input-element{width:100%;height:2.25rem;padding:0 .5rem;border:none;background-color:transparent;color:var(--twitch-text-color);font-size:.95rem;outline:none}.chat-input-element::placeholder{color:var(--twitch-secondary-text-color)}.chat-input-buttons-container{display:flex;justify-content:space-between;align-items:center}.chat-buttons-left,.chat-buttons-right{display:flex;align-items:center;gap:.5rem}.chat-points-display{display:flex;align-items:center;gap:.25rem;font-size:.85rem;color:var(--twitch-secondary-text-color)}.chat-points-display svg,.chat-points-display .channel-points-icon{width:18px;height:18px;fill:currentColor;object-fit:contain}.chat-points-display .points-value{color:var(--twitch-text-color);font-weight:600}#send-button:disabled{background-color:#b0b0b0;cursor:not-allowed}.chat-toolbar-text-button.selected{background-color:var(--twitch-purple-light);border:1px solid var(--twitch-purple);box-shadow:0 0 5px var(--twitch-purple-light)}#highlight-message-overlay{position:absolute;left:41%;transform:translate(-50%) translateY(-200%);width:24vw;height:auto;z-index:100;transition:transform 1s}#highlight-message-overlay.is-visible{transform:translate(-50%) translateY(0);transition-timing-function:cubic-bezier(.25,1,.5,1)}.sc-background-image{width:100%;height:auto;display:block;border-radius:20px}.sc-content{position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;color:#fff;font-family:Causten}.sc-user{position:absolute;top:20%;left:40%;transform:translate(-50%);font-size:2cqi;text-align:left}.sc-message{position:absolute;top:60%;left:50%;transform:translate(-50%,-50%);width:90%;font-size:2cqi;text-align:center;overflow-wrap:break-word}.settings-modal-container{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;display:flex;justify-content:center;align-items:center}.settings-modal-container.hidden{display:none}.modal-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0009}.modal-content{position:relative;background-color:var(--twitch-chat-bg);color:var(--twitch-text-color);border-radius:8px;width:90%;max-width:450px;box-shadow:0 4px 20px #0003;display:flex;flex-direction:column}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem;border-bottom:1px solid var(--twitch-border-color)}.modal-header h2{margin:0;font-size:1.25rem}.modal-body{padding:1.5rem;display:flex;flex-direction:column;gap:1.5rem}.setting-item{display:flex;flex-direction:column;gap:.5rem}.setting-item label{font-weight:600;font-size:.9rem}.modal-input{padding:.75rem;border:1px solid var(--twitch-border-color);border-radius:4px;background-color:var(--twitch-button-bg);color:var(--twitch-text-color);font-size:1rem}.modal-input:focus{outline:none;border-color:var(--twitch-purple);box-shadow:0 0 0 2px #9147ff33}.avatar-setting .avatar-preview-container{display:flex;align-items:center;gap:1rem}.avatar-preview{width:60px;height:60px;border-radius:50%;object-fit:cover;border:2px solid var(--twitch-border-color)}.avatar-upload-input{display:none}.modal-footer{padding:1rem 1.5rem;border-top:1px solid var(--twitch-border-color);display:flex;justify-content:flex-end}.setting-description{font-size:.8rem;color:var(--twitch-secondary-text-color);margin:4px 0 0}.turbo-button-icon{display:none}.turbo-button-full{display:inline-flex}@media (max-width: 992px){.turbo-button-full{display:none}.turbo-button-icon{display:inline-flex}}@media (max-width: 767px){body,#main-content-wrapper{flex-direction:column}#stream-and-info-container{width:100%}#chat-sidebar{width:100%;height:40vh;min-height:200px;min-width:unset;border-left:none;border-top:1px solid var(--twitch-border-color)}#chat-sidebar.collapsed{height:0;min-height:0;overflow:hidden;visibility:hidden;pointer-events:none}.nav-links-main,button[aria-label=Prime],button[aria-label=打开通知],button[aria-label=悄悄话],button[aria-label=购买呼币],button[aria-label=免费体验无广告观赏],button#show-chat-button,button[aria-label=通知],button.twitch-button[aria-label=购买呼币],button.twitch-button[aria-label=赠送一次订阅],.stream-details-right,.chat-sidebar-header{display:none!important}*{scrollbar-width:none!important}*::-webkit-scrollbar{display:none!important}*::-webkit-scrollbar-thumb{display:none!important}*::-webkit-scrollbar-track{display:none!important}#streamer-avatar{width:50px;height:50px}}.tooltip{position:absolute;background:#18181b;color:#fff;padding:6px 10px;font-size:12px;border-radius:4px;pointer-events:none;opacity:0;transition:opacity .2s ease;white-space:nowrap;z-index:1000;transform-origin:center bottom;-webkit-user-select:none;user-select:none}.tooltip.show{opacity:1}.tooltip:after{content:"";position:absolute;left:50%;transform:translate(-50%);border-width:6px;border-style:solid;top:100%;border-color:#18181b transparent transparent transparent;transition:border-color .2s ease,top .2s ease}.tooltip.tooltip-arrow-up:after{top:-12px;border-color:transparent transparent #18181b transparent}.hidden{display:none!important}#offline-content-container{display:flex;flex-direction:row;align-items:center;justify-content:center;flex-wrap:wrap;width:100%;min-height:500px;flex-grow:1;background-image:url(../banner.jpeg);background-size:cover;background-position:center;background-repeat:no-repeat;padding:2rem;gap:2rem;box-sizing:border-box}.offline-info-card{display:flex;flex-direction:column;align-items:flex-start;background-color:#fff;padding:1.5rem;border-radius:0;color:#0e0e10;box-shadow:0 4px 10px #0000001a;box-sizing:border-box;flex:0 1 270px}.offline-status-section{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;flex-grow:1;gap:.75rem}.offline-status-badge{background-color:#000;color:#fff;font-size:.9em;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;text-transform:uppercase}.offline-status-title{font-size:1.6rem;font-weight:600;margin:0}.offline-notification-section{width:100%;margin-left:-10px}.offline-notification-button{background-color:transparent;color:var(--twitch-primary-button-hover-bg);justify-content:flex-start}.offline-notification-button:hover{background-color:var(--twitch-button-hover-bg);color:var(--twitch-button-text-color)}.offline-notification-button svg{fill:currentColor}.offline-video-player{aspect-ratio:16 / 9;flex:0 1 576px;min-width:300px;max-width:576px}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-cyrillic-ext-400-normal-Dc4VJyIJ.woff2) format("woff2"),url(./inter-cyrillic-ext-400-normal-BE2fNs0E.woff) format("woff");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-cyrillic-400-normal-BLGc9T1a.woff2) format("woff2"),url(./inter-cyrillic-400-normal-alAqRL36.woff) format("woff");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-greek-ext-400-normal-Bput3-QP.woff2) format("woff2"),url(./inter-greek-ext-400-normal-XIH6-K3k.woff) format("woff");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-greek-400-normal-DxZsaF_h.woff2) format("woff2"),url(./inter-greek-400-normal-C3I71FoW.woff) format("woff");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-vietnamese-400-normal-DMkecbls.woff2) format("woff2"),url(./inter-vietnamese-400-normal-Bbgyi5SW.woff) format("woff");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-latin-ext-400-normal-C1nco2VV.woff2) format("woff2"),url(./inter-latin-ext-400-normal-77YHD8bZ.woff) format("woff");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-display:swap;font-weight:400;src:url(./inter-latin-400-normal-C38fXH4l.woff2) format("woff2"),url(./inter-latin-400-normal-CyCys3Eg.woff) format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
var U=Object.defineProperty;var N=(a,e,t)=>e in a?U(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var o=(a,e,t)=>N(a,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))s(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&s(r)}).observe(document,{childList:!0,subtree:!0});function t(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerPolicy&&(i.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?i.credentials="include":n.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(n){if(n.ep)return;n.ep=!0;const i=t(n);fetch(n.href,i)}})();window.addEventListener("DOMContentLoaded",()=>{document.querySelectorAll("[data-tooltip]").forEach(e=>{const t=document.createElement("div");t.className="tooltip",t.textContent=e.getAttribute("data-tooltip")||"",document.body.appendChild(t);function s(){const i=e.getBoundingClientRect(),r=window.pageXOffset,d=window.pageYOffset,h=50;let m,O;if(i.top<h)m=i.bottom+d+8,O="translateX(-50%)",t.classList.add("tooltip-arrow-up"),t.classList.remove("tooltip-arrow-down");else{t.style.left=i.left+i.width/2+r+"px",t.style.top=i.top+d+"px",t.style.transform="translateX(-50%)",t.classList.add("show");const R=t.offsetHeight;m=i.top+d-R-8,t.classList.remove("show"),t.style.top=m+"px",t.classList.add("tooltip-arrow-down"),t.classList.remove("tooltip-arrow-up")}t.style.left=i.left+i.width/2+r+"px",t.style.top=m+"px",t.style.transform=O,t.classList.add("show")}function n(){t.classList.remove("show")}e.addEventListener("mouseenter",s),e.addEventListener("mouseleave",n)})});window.addEventListener("DOMContentLoaded",()=>{const a="https://twitchtracker.com/api/channels/summary/vedal987",e=document.getElementById("avg-viewers");let t=0;const s=1;let n=null;async function i(){try{const r=new AbortController,d=setTimeout(()=>r.abort(),3e3),h=await fetch(a,{signal:r.signal});if(clearTimeout(d),!h.ok)throw new Error(`HTTP error! status: ${h.status}`);const m=await h.json();if(m&&typeof m.avg_viewers=="number")e.textContent=m.avg_viewers.toLocaleString(),t=0,n&&clearTimeout(n),n=setTimeout(i,5*60*60*1e3);else throw new Error("接口数据格式异常")}catch(r){console.warn("获取 avg_viewers 失败:",r.message),t++,t>s?(console.log("停止获取,1分钟后重试..."),setTimeout(()=>{t=0,i()},60*1e3)):i()}}i()});const p=document.getElementById("chat-messages"),y=document.getElementById("twitch-chat-overlay"),g=document.getElementById("highlight-message-overlay"),x="One_of_Swarm";class F{constructor(){p?console.log("ChatDisplay initialized."):console.error("ChatDisplay: Required chat messages container not found in DOM!")}appendChatMessage(e){if(!p){console.error("ChatDisplay: Cannot append message, container not found.");return}const t=document.createElement("div");t.className="chat-line__message";const s=document.createElement("div");s.className="chat-line__message-container",e.username===x&&e.is_user_message?t.classList.add("user-sent-message"):e.username==="System"?t.classList.add("system-message"):t.classList.add("audience-ai-message");const n=document.createElement("span");n.className="chat-line__username",n.style.color=e.username===x?"#9147FF":this.getRandomChatColor();const i=document.createElement("span");i.className="chat-author__display-name",i.textContent=e.username,n.appendChild(i);const r=document.createElement("span");r.textContent=": ",r.style.marginRight="0.3rem",r.className="text-fragment";const d=document.createElement("span");if(d.className="text-fragment",d.textContent=e.text,s.appendChild(n),s.appendChild(r),s.appendChild(d),t.appendChild(s),p.appendChild(t),y){const h=t.cloneNode(!0);y.appendChild(h),this.scrollToBottomOverlay()}this.scrollToBottom()}clearChat(){p&&(p.innerHTML="",console.log("Chat display cleared.")),y&&(y.innerHTML="")}scrollToBottom(){p&&(p.scrollTop=p.scrollHeight)}scrollToBottomOverlay(){y&&(y.scrollTop=y.scrollHeight)}getRandomChatColor(){const e=["#FF0000","#00FF00","#0000FF","#00FFFF","#FF00FF","#FF4500","#ADFF2F","#1E90FF","#FFD700","#8A2BE2","#00CED1","#FF69B4","#DA70D6","#BA55D3","#87CEEB","#32CD32","#CD853F"];return e[Math.floor(Math.random()*e.length)]}}function $(a,e,t){if(!g)return;const s=t==="bits"?"/sc_purple.png":"/sc_pink.png";g.innerHTML=`
|
2
|
+
<img src="${s}" class="sc-background-image" alt="Super Chat background">
|
3
|
+
<div class="sc-content">
|
4
|
+
<div class="sc-user">${a}</div>
|
5
|
+
<div class="sc-message">${e}</div>
|
6
|
+
</div>
|
7
|
+
`;const n=g.querySelector(".sc-message");n&&(n.style.color=t==="bits"?"var(--sc-purple-bg-color)":"var(--sc-pink-bg-color)"),g.classList.remove("hidden"),g.offsetHeight,g.classList.add("is-visible"),setTimeout(()=>{g.classList.remove("is-visible"),setTimeout(()=>{g.classList.add("hidden")},1e3)},9e3)}class W{constructor(e){o(this,"ws",null);o(this,"options");o(this,"reconnectAttempts",0);o(this,"reconnectTimeout",null);o(this,"explicitlyClosed",!1);this.options={reconnectInterval:3e3,maxReconnectAttempts:10,...e}}connect(){if(!this.options.url){console.warn("WebSocket URL is not set. Connection aborted.");return}if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING)){console.warn(`WebSocket for ${this.options.url} is already connected or connecting.`);return}this.explicitlyClosed=!1,console.log(`Connecting to WebSocket: ${this.options.url}`),this.ws=new WebSocket(this.options.url),this.ws.onopen=()=>{var e,t;console.log(`WebSocket connected: ${this.options.url}`),this.reconnectAttempts=0,this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),(t=(e=this.options).onOpen)==null||t.call(e)},this.ws.onmessage=e=>{var t,s;try{const n=JSON.parse(e.data);n.type==="processing_superchat"&&$(n.data.username,n.data.text,n.data.sc_type),(s=(t=this.options).onMessage)==null||s.call(t,n)}catch(n){console.error(`Error parsing message from ${this.options.url}:`,n,e.data)}},this.ws.onclose=e=>{var t,s,n,i;console.warn(`WebSocket closed: ${this.options.url}. Code: ${e.code}, Reason: ${e.reason}`),this.ws=null,(s=(t=this.options).onClose)==null||s.call(t,e),this.explicitlyClosed||((i=(n=this.options).onDisconnect)==null||i.call(n),this.options.autoReconnect&&e.code!==1e3&&this.tryReconnect())},this.ws.onerror=e=>{var t,s;console.error(`WebSocket error: ${this.options.url}`,e),(s=(t=this.options).onError)==null||s.call(t,e)}}tryReconnect(){if(this.options.maxReconnectAttempts===-1||this.reconnectAttempts<this.options.maxReconnectAttempts){this.reconnectAttempts++;const t=this.options.maxReconnectAttempts===-1?`(attempt ${this.reconnectAttempts})`:`(attempt ${this.reconnectAttempts}/${this.options.maxReconnectAttempts})`;console.log(`Attempting to reconnect to ${this.options.url} in ${this.options.reconnectInterval/1e3} seconds... ${t}`),this.reconnectTimeout=setTimeout(()=>{this.connect()},this.options.reconnectInterval)}else console.error(`Max reconnect attempts reached for ${this.options.url}.`)}send(e){this.ws&&this.ws.readyState===WebSocket.OPEN?this.ws.send(JSON.stringify(e)):console.warn(`WebSocket for ${this.options.url} is not open. Message not sent.`)}disconnect(){this.explicitlyClosed=!0,this.reconnectTimeout&&clearTimeout(this.reconnectTimeout),this.ws&&this.ws.close(1e3,"Client initiated disconnect")}updateOptions(e){console.log("WebSocket client options updated:",e),this.options={...this.options,...e}}getUrl(){return this.options.url}}const c=document.getElementById("neuro-caption"),T=document.getElementById("stream-display-area"),z=16,q=2,B=.4;function M(){if(!c||!T)return;c.style.fontSize="";const a=T.offsetHeight;if(c.textContent===""||a===0)return;if(c.offsetHeight>a*B){const t=window.getComputedStyle(c);let s=parseFloat(t.fontSize);for(;c.offsetHeight>a*B&&s>z;)s-=q,c.style.fontSize=`${s}px`;c.offsetHeight>a*B&&(c.style.fontSize=`${z}px`)}}let I=null,C=null;const H=3e3;function D(a,e){if(c)if(c.classList.add("show"),e&&a.trim().length>0){const t=a.split(/\s+/).filter(r=>r.length>0);if(t.length===0){c.textContent+=(c.textContent?" ":"")+a,M();return}const s=a.length;let n=c.textContent||"";const i=r=>{if(r<t.length){const d=t[r],h=d.length/s*e*1.01,m=Math.max(50,h*1e3);n+=(n.length>0?" ":"")+d,c.textContent=n,M(),I=setTimeout(()=>i(r+1),m)}else I=null};i(0),console.log(`Starting word-by-word caption for: "${a.substring(0,30)}..." (duration: ${e}s)`)}else c.textContent+=(c.textContent?" ":"")+a,M(),console.log(`Displaying full caption: "${a.substring(0,30)}..."`)}function A(){c&&(I&&(clearTimeout(I),I=null),C&&(clearTimeout(C),C=null),c.classList.remove("show"),c.textContent="",c.style.fontSize="",console.log("NeuroCaption hidden and cleared."))}function V(){C&&clearTimeout(C),C=setTimeout(()=>{A()},H)}!c||!T?console.error("neuroCaption.ts: Could not find #neuro-caption or #stream-display-area element."):console.log("NeuroCaption module initialized.");class j{constructor(){o(this,"audioQueue",[]);o(this,"isPlayingAudio",!1);o(this,"currentPlayingAudio",null);o(this,"allSegmentsReceived",!1);o(this,"errorSound");o(this,"lastSegmentEnd",!0);this.errorSound=new Audio("/error.mp3"),console.log("AudioPlayer initialized.")}playErrorSound(){this.stopAllAudio(),console.log("Playing dedicated error sound..."),this.errorSound.play().catch(e=>{console.error("Error playing dedicated error sound:",e)})}addAudioSegment(e,t,s){this.lastSegmentEnd&&A(),this.lastSegmentEnd=!1;const n=new Audio("data:audio/mp3;base64,"+t);try{const r=f.getAppInitializer().getMuteButton();n.muted=r.getIsMuted()}catch(i){console.warn("Could not get mute state, defaulting to muted:",i),n.muted=!0}this.audioQueue.push({text:e,audio:n,duration:s}),console.log(`Audio segment added to queue. Queue size: ${this.audioQueue.length}`),this.isPlayingAudio||this.playNextAudioSegment()}playNextAudioSegment(){if(this.audioQueue.length>0&&!this.isPlayingAudio){this.isPlayingAudio=!0;const e=this.audioQueue.shift();this.currentPlayingAudio=e.audio,D(e.text,e.duration),this.currentPlayingAudio.play().catch(t=>{console.error("Error playing audio segment:",t),this.isPlayingAudio=!1,this.currentPlayingAudio=null,this.playNextAudioSegment()}),this.currentPlayingAudio.addEventListener("ended",()=>{this.isPlayingAudio=!1,this.currentPlayingAudio=null,this.playNextAudioSegment()},{once:!0})}else if(this.audioQueue.length===0&&this.allSegmentsReceived){console.log("Neuro's full audio response played. Starting caption timeout and resetting zoom."),V();try{f.getAppInitializer().getNeuroAvatar().resetZoom()}catch(e){console.warn("Could not reset neuro avatar zoom at end of speech",e)}}}setAllSegmentsReceived(){this.allSegmentsReceived=!0,this.lastSegmentEnd=!0}stopAllAudio(){this.currentPlayingAudio&&(this.currentPlayingAudio.pause(),this.currentPlayingAudio.currentTime=0,this.currentPlayingAudio=null),this.audioQueue.length=0,this.isPlayingAudio=!1,this.allSegmentsReceived=!1,A();try{f.getAppInitializer().getNeuroAvatar().resetZoom()}catch(e){console.warn("Could not reset neuro avatar zoom on stop",e)}console.log("Neuro audio playback stopped, queue cleared.")}updateMuteState(){if(this.currentPlayingAudio)try{const t=f.getAppInitializer().getMuteButton();this.currentPlayingAudio.muted=t.getIsMuted()}catch(e){console.warn("Could not update current audio mute state:",e)}for(const e of this.audioQueue)try{const s=f.getAppInitializer().getMuteButton();e.audio.muted=s.getIsMuted()}catch(t){console.warn("Could not update queued audio mute state:",t)}}}const b=document.getElementById("startup-video-overlay"),u=document.getElementById("startup-video");class Q{constructor(){!b||!u?console.error("VideoPlayer: Required video elements not found in DOM!"):console.log("VideoPlayer initialized.")}showAndPlayVideo(e=0){if(!b||!u){console.error("VideoPlayer: Cannot show and play video, elements are missing.");return}b.classList.remove("hidden"),b.style.zIndex="10";const s=f.getAppInitializer().getMuteButton(),n=()=>{isFinite(u.duration)&&e>.1&&e<u.duration&&(u.currentTime=e,console.log(`Seeked to: ${e.toFixed(2)}s.`))};u.muted=!1;const i=u.play();i!==void 0&&i.then(()=>{console.log("Unmuted autoplay successful."),n()}).catch(r=>{console.warn("Unmuted autoplay failed. Showing unmute prompt and falling back to muted playback.",r),s.show(),u.muted=!0,u.play().then(()=>{console.log("Muted fallback playback started."),n()}).catch(h=>{console.error("Muted fallback playback also failed. This is unexpected.",h)})})}pauseAndMute(){u&&(u.pause(),u.muted=!0,console.log("Startup video paused and muted."))}hide(){b&&(b.classList.add("hidden"),console.log("Startup video overlay hidden."))}getVideoDuration(){return u&&!isNaN(u.duration)?u.duration:0}}const v={HIDDEN:"hidden",STEP1:"step1",STEP2:"step2"},l=document.getElementById("neuro-static-avatar-container");class J{constructor(){l?(console.log("NeuroAvatar initialized."),this.setStage(v.HIDDEN,!0)):console.error("NeuroAvatar: Required avatar container element not found in DOM!")}startIntroAnimation(e){console.log("Starting Neuro intro animation sequence..."),this.setStage(v.STEP1),setTimeout(()=>{console.log("Animating to Step 2..."),this.setStage(v.STEP2),setTimeout(()=>{console.log("Neuro intro animation sequence finished."),e&&e()},1e3)},2e3)}setStage(e,t=!1){if(!l)return;const s="transform 0.5s ease-in-out";switch(t?(l.style.transition="none",l.offsetHeight):e===v.STEP2?l.style.transition=`bottom 1s cubic-bezier(0.4, 0.0, 1, 1), ${s}`:l.style.transition=s,e){case v.HIDDEN:l.style.visibility="hidden",l.style.bottom="-207%",l.style.left="70%",l.style.zIndex="10";break;case v.STEP1:l.style.visibility="visible",l.style.bottom="-207%",l.style.left="70%",l.style.zIndex="15";break;case v.STEP2:l.style.visibility="visible",l.style.bottom="-125%",l.style.left="70%",l.style.zIndex="15";break}}triggerSpin(){l&&(console.log("Triggering avatar spin animation."),l.classList.add("spin-animation"),setTimeout(()=>{l.classList.remove("spin-animation"),console.log("Avatar spin animation finished.")},1e3))}triggerZoom(){l&&(console.log("Triggering avatar zoom-in animation."),l.classList.add("zoom-in"))}resetZoom(){l&&l.classList.contains("zoom-in")&&(console.log("Resetting avatar zoom."),l.classList.remove("zoom-in"))}}const k=document.getElementById("chat-input"),P=document.getElementById("send-button"),w=document.getElementById("sc-bits-button"),E=document.getElementById("sc-points-button");class X{constructor(){o(this,"onSendMessageCallback",null);!k||!P||!w||!E?console.error("UserInput: Required input elements not found in DOM!"):(this.setupEventListeners(),console.log("UserInput initialized."))}onSendMessage(e){this.onSendMessageCallback=e}setupEventListeners(){P.addEventListener("click",()=>this.handleSendMessage()),k.addEventListener("keypress",e=>{e.key==="Enter"&&this.handleSendMessage()}),w.addEventListener("click",()=>this.toggleSuperchatButton(w)),E.addEventListener("click",()=>this.toggleSuperchatButton(E))}toggleSuperchatButton(e){const t=e===w?E:w;e.classList.contains("selected")?e.classList.remove("selected"):(e.classList.add("selected"),t.classList.remove("selected"))}handleSendMessage(){const e=k.value.trim();if(!e){console.warn("Attempted to send empty message.");return}let t;const s=w.classList.contains("selected"),n=E.classList.contains("selected");s||n?t={type:"superchat",text:e,sc_type:s?"bits":"points"}:t={type:"user_message",text:e},this.onSendMessageCallback?this.onSendMessageCallback(t):console.warn("No callback registered for sending message."),k.value="",this.clearSuperchatSelection()}clearSuperchatSelection(){w.classList.remove("selected"),E.classList.remove("selected")}setInputDisabled(e){k.disabled=e,P.disabled=e,console.log(`User input elements disabled: ${e}`)}}class Z{constructor(){o(this,"viewportElement");o(this,"areaElement");o(this,"resizeObserver");if(this.viewportElement=document.getElementById("stream-display-viewport"),this.areaElement=document.getElementById("stream-display-area"),!this.viewportElement||!this.areaElement)throw new Error("LayoutManager: Required viewport or area element not found in DOM!");this.resizeObserver=new ResizeObserver(this.handleResize.bind(this))}handleResize(e){for(const t of e){const{width:s,height:n}=t.contentRect;this.updateLayout(s,n)}}updateLayout(e,t){if(e===0||t===0)return;const s=e/t,n=16/9;let i,r;s>n?(r=t,i=r*n):(i=e,r=i/n),this.areaElement.style.width=`${i}px`,this.areaElement.style.height=`${r}px`}start(){this.resizeObserver.observe(this.viewportElement),this.updateLayout(this.viewportElement.clientWidth,this.viewportElement.clientHeight),console.log("LayoutManager started and observing.")}stop(){this.resizeObserver.disconnect(),console.log("LayoutManager stopped.")}}class G{constructor(){o(this,"timerElement");o(this,"intervalId",null);o(this,"streamStartTime",0);if(this.timerElement=document.getElementById("stream-duration-text"),!this.timerElement)throw new Error("StreamTimer: Duration element '#stream-duration-text' not found!");this.reset()}formatTime(e){const t=Math.floor(e/3600),s=Math.floor(e%3600/60),n=Math.floor(e%60),i=r=>String(r).padStart(2,"0");return t>0?`${t}:${i(s)}:${i(n)}`:`${s}:${i(n)}`}updateDisplay(){if(this.streamStartTime>0){const t=(Date.now()-this.streamStartTime)/1e3;this.timerElement.textContent=this.formatTime(Math.max(0,t))}}start(e=0){this.stop(),this.streamStartTime=Date.now()-e*1e3,this.updateDisplay(),this.intervalId=window.setInterval(()=>this.updateDisplay(),1e3),console.log(`Stream timer started with initial ${e.toFixed(2)}s.`)}stop(){this.intervalId!==null&&(clearInterval(this.intervalId),this.intervalId=null,console.log("Stream timer stopped."))}reset(){this.stop(),this.streamStartTime=0,this.timerElement.textContent="0:00",console.log("Stream timer reset.")}}class Y{constructor(){o(this,"sidebarElement");o(this,"toggleButton");o(this,"showChatButton");o(this,"isCollapsed",!1);o(this,"bodyElement");if(this.sidebarElement=document.getElementById("chat-sidebar"),this.toggleButton=document.getElementById("toggle-chat-button"),this.showChatButton=document.getElementById("show-chat-button"),this.bodyElement=document.body,!this.sidebarElement||!this.toggleButton||!this.showChatButton)throw new Error("ChatSidebar: Required elements not found in DOM!");this.setupEventListeners(),this.setCollapsed(!1,!0),console.log("ChatSidebar initialized.")}setupEventListeners(){this.toggleButton.addEventListener("click",()=>this.toggleCollapse()),this.showChatButton.addEventListener("click",()=>this.toggleCollapse())}toggleCollapse(){this.setCollapsed(!this.isCollapsed)}setCollapsed(e,t=!1){this.isCollapsed=e,this.isCollapsed?this.bodyElement.classList.add("chat-collapsed"):this.bodyElement.classList.remove("chat-collapsed"),t?(this.sidebarElement.style.transition="none",this.toggleButton.style.transition="none",this.showChatButton.style.transition="none"):(this.sidebarElement.style.transition="width 0.3s ease-in-out, min-width 0.3s ease-in-out",this.toggleButton.style.transition="transform 0.3s ease-in-out, background-color 0.2s, color 0.2s",this.showChatButton.style.transition="opacity 0.3s ease-in-out, visibility 0.3s ease-in-out"),this.isCollapsed?(this.sidebarElement.classList.add("collapsed"),this.toggleButton.setAttribute("aria-label","展开聊天"),this.sidebarElement.querySelectorAll(":scope > *:not(.chat-sidebar-header)").forEach(s=>{s.style.opacity="0",s.style.pointerEvents="none"}),console.log("Chat sidebar collapsed.")):(this.sidebarElement.classList.remove("collapsed"),this.toggleButton.setAttribute("aria-label","重叠聊天"),this.sidebarElement.querySelectorAll(":scope > *:not(.chat-sidebar-header)").forEach(s=>{s.style.opacity="1",s.style.pointerEvents="auto"}),console.log("Chat sidebar expanded.")),t&&requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.sidebarElement.style.transition="",this.toggleButton.style.transition="",this.showChatButton.style.transition=""})})}getIsCollapsed(){return this.isCollapsed}}class K{constructor(){o(this,"indicatorElement");if(this.indicatorElement=document.querySelector(".live-indicator-rect"),!this.indicatorElement)throw new Error("LiveIndicator: Required .live-indicator-rect element not found in DOM!");this.hide()}show(){this.indicatorElement.classList.remove("hidden")}hide(){this.indicatorElement.classList.add("hidden")}}class ee{constructor(){o(this,"nicknameElement");o(this,"titleElement");o(this,"categoryElement");o(this,"tagsContainer");if(this.nicknameElement=document.getElementById("streamer-nickname"),this.titleElement=document.getElementById("stream-title-full"),this.categoryElement=document.querySelector(".stream-category"),this.tagsContainer=document.querySelector(".stream-tags"),!this.nicknameElement||!this.titleElement||!this.categoryElement||!this.tagsContainer)throw new Error("StreamInfoDisplay: One or more required elements not found in DOM!");console.log("StreamInfoDisplay initialized.")}update(e){this.nicknameElement.textContent=e.streamer_nickname,this.titleElement.textContent=e.stream_title,this.categoryElement.textContent=e.stream_category,this.tagsContainer.innerHTML="",e.stream_tags.forEach(t=>{const s=document.createElement("a");s.href="#",s.className="stream-tag",s.textContent=t,this.tagsContainer.appendChild(s)}),console.log("Stream info display updated with new metadata.")}}class te{constructor(){o(this,"wakeLockSentinel",null);o(this,"isSupported");this.isSupported="wakeLock"in navigator,this.isSupported?console.log("WakeLockManager initialized. API is supported."):console.warn("Wake Lock API is not supported in this browser. The device may go to sleep during playback.")}async requestWakeLock(){if(!(!this.isSupported||this.wakeLockSentinel))try{this.wakeLockSentinel=await navigator.wakeLock.request("screen"),this.wakeLockSentinel.addEventListener("release",()=>{console.log("Wake Lock was released by the browser."),this.wakeLockSentinel=null}),console.log("Wake Lock is active."),document.addEventListener("visibilitychange",this.handleVisibilityChange.bind(this))}catch(e){console.error(`Failed to acquire Wake Lock: ${e.name}, ${e.message}`),this.wakeLockSentinel=null}}async releaseWakeLock(){this.wakeLockSentinel&&(await this.wakeLockSentinel.release(),this.wakeLockSentinel=null,console.log("Wake Lock has been released.")),document.removeEventListener("visibilitychange",this.handleVisibilityChange.bind(this))}async handleVisibilityChange(){this.wakeLockSentinel===null&&document.visibilityState==="visible"&&(console.log("Page is visible again, re-acquiring Wake Lock..."),await this.requestWakeLock())}}class L{constructor(e){o(this,"modalContainer");o(this,"overlay");o(this,"closeButton");o(this,"saveButton");o(this,"usernameInput");o(this,"backendUrlInput");o(this,"avatarPreview");o(this,"avatarUploadInput");o(this,"avatarUploadButton");o(this,"reconnectAttemptsInput");o(this,"onSave");if(this.onSave=e,this.modalContainer=document.getElementById("settings-modal"),this.overlay=document.getElementById("settings-modal-overlay"),this.closeButton=document.getElementById("settings-close-button"),this.saveButton=document.getElementById("settings-save-button"),this.usernameInput=document.getElementById("username-setting-input"),this.backendUrlInput=document.getElementById("backend-url-input"),this.avatarPreview=document.getElementById("avatar-setting-preview"),this.avatarUploadInput=document.getElementById("avatar-setting-upload"),this.avatarUploadButton=document.getElementById("avatar-upload-button"),this.reconnectAttemptsInput=document.getElementById("reconnect-attempts-input"),!this.modalContainer)throw new Error("Settings modal container not found!");this.setupEventListeners(),console.log("SettingsModal initialized.")}setupEventListeners(){this.closeButton.addEventListener("click",()=>this.close()),this.overlay.addEventListener("click",()=>this.close()),this.saveButton.addEventListener("click",()=>this.handleSave()),this.avatarUploadButton.addEventListener("click",()=>this.avatarUploadInput.click()),this.avatarUploadInput.addEventListener("change",e=>this.handleAvatarUpload(e))}handleAvatarUpload(e){const t=e.target;if(t.files&&t.files[0]){const s=new FileReader;s.onload=n=>{var i;(i=n.target)!=null&&i.result&&(this.avatarPreview.src=n.target.result)},s.readAsDataURL(t.files[0])}}handleSave(){const e={username:this.usernameInput.value.trim()||"User",avatarDataUrl:this.avatarPreview.src,backendUrl:this.backendUrlInput.value.trim(),reconnectAttempts:parseInt(this.reconnectAttemptsInput.value,10)||-1};localStorage.setItem("neuro_settings",JSON.stringify(e)),this.onSave(e),this.close()}open(){this.loadSettings(),this.modalContainer.classList.remove("hidden")}close(){this.modalContainer.classList.add("hidden")}loadSettings(){const e=L.getSettings();this.usernameInput.value=e.username,this.avatarPreview.src=e.avatarDataUrl,this.backendUrlInput.value=e.backendUrl,this.reconnectAttemptsInput.value=String(e.reconnectAttempts)}static getSettings(){const e=localStorage.getItem("neuro_settings");if(e)try{return JSON.parse(e)}catch(t){console.error("Failed to parse settings from localStorage, returning defaults.",t)}return{username:"One_of_Swarm",avatarDataUrl:"/user_avatar.jpg",backendUrl:"ws://127.0.0.1:8000",reconnectAttempts:-1}}}class se{constructor(){o(this,"button",null);o(this,"isMuted",!0)}create(){return this.button=document.getElementById("mute-button"),this.button?this.button.addEventListener("click",e=>{e.stopPropagation(),this.unmute()}):console.error("Mute button element not found in DOM!"),this.button}show(){this.button&&(this.button.style.display="flex")}hide(){this.button&&(this.button.style.display="none")}unmute(){this.isMuted=!1,this.hide(),this.updateMediaElements()}updateMediaElements(){const e=document.getElementById("startup-video");e&&(e.muted=this.isMuted);try{f.getAppInitializer().getAudioPlayer().updateMuteState()}catch(t){console.warn("Could not update audio player mute state:",t)}}getElement(){return this.button}getIsMuted(){return this.isMuted}}async function ne(a,e={},t){return window.__TAURI_INTERNALS__.invoke(a,e,t)}const ie=typeof window<"u"&&window.__TAURI__!==void 0,oe="3546729368520811",ae="4281748";async function re(){var a,e;try{if(ie)return console.log("Running in Tauri, invoking 'get_latest_replay_video' command..."),await ne("get_latest_replay_video");{console.log("Running in Web, fetching from proxy...");const t=`/bilibili-api/x/series/archives?mid=${oe}&series_id=${ae}&ps=1&pn=1`,s=await fetch(t);if(!s.ok)throw new Error(`Failed to fetch from proxy: ${s.statusText}`);const n=await s.json();if(n.code!==0)throw new Error(`Bilibili API error: ${n.message}`);const i=(e=(a=n==null?void 0:n.data)==null?void 0:a.archives)==null?void 0:e[0];if(i&&i.bvid&&i.aid)return i;throw new Error("No matching video found in API response.")}}catch(t){return console.error("Failed to get latest replay video:",t),null}}function le(a){const e="//www.bilibili.com/blackboard/html5mobileplayer.html",t=new URLSearchParams({bvid:a.bvid,aid:String(a.aid),p:"1",autoplay:"1",danmaku:"0",hasMuteButton:"1",hideCoverInfo:"0",fjw:"0",high_quality:"1"});return`${e}?${t.toString()}`}class ce{constructor(){o(this,"wsClient");o(this,"audioPlayer");o(this,"videoPlayer");o(this,"neuroAvatar");o(this,"chatDisplay");o(this,"userInput");o(this,"layoutManager");o(this,"streamTimer");o(this,"chatSidebar");o(this,"liveIndicator");o(this,"streamInfoDisplay");o(this,"wakeLockManager");o(this,"settingsModal");o(this,"currentSettings");o(this,"muteButton");o(this,"resizeObserver",null);o(this,"offlinePlayerSrc",null);o(this,"isStarted",!1);o(this,"currentPhase","offline");o(this,"adjustOfflineLayout",()=>{if(this.currentPhase!=="offline")return;const e=document.getElementById("offline-content-container"),t=document.querySelector(".offline-video-player"),s=document.querySelector(".offline-info-card");if(!e||!t||!s)return;if(window.innerWidth<=767)e.style.flexWrap="wrap",s.style.width=t.offsetWidth+"px",s.style.height="auto",s.style.flex="0 0 auto";else{e.style.flexWrap="nowrap";const i=t.offsetHeight;i>0&&(s.style.height=`${i}px`,s.style.width=`${i}px`),s.style.flex="0 1 auto"}});this.layoutManager=new Z,this.streamTimer=new G,this.muteButton=new se,this.currentSettings=L.getSettings(),this.settingsModal=new L(t=>this.handleSettingsUpdate(t)),this.wsClient=null,this.audioPlayer=new j,this.videoPlayer=new Q,this.neuroAvatar=new J,this.chatDisplay=new F,this.userInput=new X,this.userInput.onSendMessage(t=>this.sendUserMessage(t)),this.chatSidebar=new Y,this.liveIndicator=new K,this.streamInfoDisplay=new ee,this.wakeLockManager=new te,this.setupSettingsModalTrigger(),this.setupMuteButton();const e=document.querySelector(".offline-video-player");e&&(this.offlinePlayerSrc=e.src),this.updateOfflinePlayerSrc()}start(){this.isStarted||(this.isStarted=!0,this.probeForIntegratedServer(),this.layoutManager.start(),this.goOffline(),this.updateUiWithSettings())}setupSettingsModalTrigger(){const e=document.querySelector(".nav-user-avatar-button");e&&e.addEventListener("click",()=>{this.settingsModal.open()})}setupMuteButton(){if(this.muteButton.create()){this.muteButton.show();const t=()=>{this.muteButton.unmute(),document.removeEventListener("click",t)};document.addEventListener("click",t)}}getMuteButton(){return this.muteButton}getAudioPlayer(){return this.audioPlayer}getNeuroAvatar(){return this.neuroAvatar}handleSettingsUpdate(e){if(console.log("Settings updated. Re-initializing connection with new settings:",e),this.currentSettings=e,this.updateUiWithSettings(),this.wsClient){const t=e.backendUrl?`${e.backendUrl}/ws/stream`:"";this.wsClient.updateOptions({url:t,maxReconnectAttempts:e.reconnectAttempts}),this.wsClient.disconnect(),setTimeout(()=>{this.wsClient&&this.wsClient.getUrl()?this.wsClient.connect():console.warn("Cannot connect: Backend URL is empty after update or WebSocket client not ready.")},500)}else console.warn("WebSocket client not initialized, cannot update settings.")}initWebSocketClient(e){const t=e?`${e}/ws/stream`:"",s=n=>this.handleWebSocketMessage(n);this.wsClient?(this.wsClient.updateOptions({url:t,autoReconnect:!0,maxReconnectAttempts:this.currentSettings.reconnectAttempts,onMessage:s,onOpen:()=>this.goOnline(),onDisconnect:()=>this.goOffline()}),t&&(this.wsClient.disconnect(),setTimeout(()=>{this.wsClient.connect()},500))):this.wsClient=new W({url:t,autoReconnect:!0,maxReconnectAttempts:this.currentSettings.reconnectAttempts,onMessage:s,onOpen:()=>this.goOnline(),onDisconnect:()=>this.goOffline()})}async probeForIntegratedServer(){const e=L.getSettings();console.log("Probing for integrated server. Stored settings:",e);try{const t=new URL("/api/system/health",window.location.origin).toString();console.log("Probing integrated server at:",t);const s=await fetch(t);if(s.ok){console.log("Integrated server detected via health check. Auto-connecting...");const n=window.location.origin;this.currentSettings={...this.currentSettings,backendUrl:n},localStorage.setItem("neuro_settings",JSON.stringify(this.currentSettings)),this.initWebSocketClient(n),this.wsClient&&n&&this.wsClient.connect();return}else console.log("Health check failed, not an integrated server. Status:",s.status)}catch(t){console.log("Failed to probe for integrated server, assuming standalone mode.",t)}console.log("Falling back to stored backend URL:",e.backendUrl),this.initWebSocketClient(e.backendUrl),this.wsClient&&e.backendUrl?this.wsClient.connect():e.backendUrl||(console.warn("Backend URL is not configured via probe or stored settings. Opening settings modal."),this.settingsModal.open())}updateUiWithSettings(){document.querySelectorAll(".user-avatar-img").forEach(t=>t.src=this.currentSettings.avatarDataUrl),console.log(`UI updated with username: ${this.currentSettings.username} and avatar.`)}async updateOfflinePlayerSrc(){console.log("Attempting to fetch the latest replay video...");const e=await re();if(e){const t=le(e);if(this.offlinePlayerSrc=t,console.log("Successfully updated offline player src to:",t),this.currentPhase==="offline"){const s=document.querySelector(".offline-video-player");s&&(s.src=this.offlinePlayerSrc)}}else console.log("Failed to fetch latest replay video, using default fallback.")}goOnline(){var s,n,i,r;console.log("Entering ONLINE state."),this.updateUiWithSettings();const e=document.querySelector(".offline-video-player");e&&(e.src="about:blank"),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.adjustOfflineLayout);const t=document.querySelector(".offline-info-card");t&&(t.style.height="",t.style.width=""),(s=document.getElementById("offline-content-container"))==null||s.classList.add("hidden"),(n=document.getElementById("stream-display-viewport"))==null||n.classList.remove("hidden"),(i=document.querySelector(".stream-info-details-row"))==null||i.classList.remove("hidden"),(r=document.getElementById("chat-sidebar"))==null||r.classList.remove("hidden"),this.showStreamContent(),this.chatDisplay.clearChat(),this.liveIndicator.show(),this.wakeLockManager.requestWakeLock()}goOffline(){var s,n,i,r;console.log("Entering OFFLINE state."),this.currentPhase="offline";const e=document.querySelector(".offline-video-player");e&&this.offlinePlayerSrc&&(e.src=this.offlinePlayerSrc),(s=document.getElementById("offline-content-container"))==null||s.classList.remove("hidden"),(n=document.getElementById("stream-display-viewport"))==null||n.classList.add("hidden"),(i=document.querySelector(".stream-info-details-row"))==null||i.classList.add("hidden"),(r=document.getElementById("chat-sidebar"))==null||r.classList.add("hidden"),this.hideStreamContent(),this.audioPlayer.stopAllAudio(),this.videoPlayer.hide(),this.neuroAvatar.setStage("hidden",!0),A(),this.streamTimer.stop(),this.streamTimer.reset(),this.chatDisplay.clearChat(),this.liveIndicator.hide(),this.wakeLockManager.releaseWakeLock(),this.muteButton.show();const t=()=>{this.muteButton.unmute(),document.removeEventListener("click",t)};document.addEventListener("click",t),setTimeout(()=>{this.adjustOfflineLayout();const d=document.querySelector(".offline-video-player");d&&!this.resizeObserver&&(this.resizeObserver=new ResizeObserver(this.adjustOfflineLayout),this.resizeObserver.observe(d),window.addEventListener("resize",this.adjustOfflineLayout))},0)}handleWebSocketMessage(e){switch(this.currentPhase==="offline"&&["play_welcome_video","start_avatar_intro","enter_live_phase"].includes(e.type)&&(console.log("Connection successful, transitioning from OFFLINE to active state."),this.goOnline()),e.elapsed_time_sec!==void 0&&this.streamTimer.start(e.elapsed_time_sec),e.type){case"offline":this.goOffline();break;case"model_spin":this.neuroAvatar.triggerSpin();break;case"model_zoom":this.neuroAvatar.triggerZoom();break;case"update_stream_metadata":this.streamInfoDisplay.update(e);break;case"play_welcome_video":this.currentPhase="initializing",this.videoPlayer.showAndPlayVideo(parseFloat(e.progress));break;case"start_avatar_intro":this.currentPhase="avatar_intro",this.videoPlayer.pauseAndMute(),this.neuroAvatar.startIntroAnimation(()=>{this.videoPlayer.hide()});break;case"enter_live_phase":this.currentPhase="live",this.videoPlayer.hide(),this.neuroAvatar.setStage("step2");break;case"neuro_is_speaking":break;case"neuro_speech_segment":const t=e;t.is_end?this.audioPlayer.setAllSegmentsReceived():t.audio_base64&&t.text&&typeof t.duration=="number"?this.audioPlayer.addAudioSegment(t.text,t.audio_base64,t.duration):console.warn("Received neuro_speech_segment message with missing audio/text/duration:",t);break;case"neuro_error_signal":console.warn("Received neuro_error_signal from backend."),D("Someone tell Vedal there is a problem with my AI."),this.audioPlayer.playErrorSound();break;case"chat_message":(!this.chatSidebar.getIsCollapsed()||e.is_user_message)&&this.chatDisplay.appendChatMessage(e);break;case"error":this.chatDisplay.appendChatMessage({type:"chat_message",username:"System",text:`后端错误: ${e.message}`,is_user_message:!1});break}}sendUserMessage(e){const t={username:this.currentSettings.username,...e};this.wsClient?this.wsClient.send(t):console.warn("Cannot send message: WebSocket client is not initialized.")}showStreamContent(){const e=document.getElementById("stream-display-area");e&&(e.style.visibility="visible",e.style.opacity="1")}hideStreamContent(){const e=document.getElementById("stream-display-area");e&&(e.style.visibility="hidden",e.style.opacity="0")}}const S=class S{constructor(){o(this,"appInitializerInstance",null)}static getInstance(){return S.instance||(S.instance=new S),S.instance}getAppInitializer(){return this.appInitializerInstance?console.log("Returning existing AppInitializer instance."):(console.log("Creating new AppInitializer instance..."),this.appInitializerInstance=new ce),this.appInitializerInstance}};o(S,"instance");let _=S;const f=_.getInstance();document.addEventListener("DOMContentLoaded",()=>{console.log("DOMContentLoaded event fired."),f.getAppInitializer().start()});console.log("main.ts loaded. Waiting for DOMContentLoaded to initialize the app.");
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,306 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<link rel="icon" type="image/x-icon" href="./favicon.ico" />
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7
|
+
<title>𝐯𝐞𝐝𝐚𝐥𝟗𝟖𝟕 - 𝐓𝐰𝐢𝐭𝐜𝐡</title>
|
8
|
+
<script type="module" crossorigin src="./assets/index-DRLWJPZv.js"></script>
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-C_kzLmQy.css">
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<header id="twitch-header">
|
13
|
+
<div class="top-nav-left">
|
14
|
+
<a href="/" class="twitch-logo-link" aria-label="Twitch 主页">
|
15
|
+
<figure class="twitch-logo-container">
|
16
|
+
<svg class="twitch-logo-svg" overflow="visible" width="40px" height="40px" version="1.1" viewBox="0 0 40 40" x="0px" y="0px">
|
17
|
+
<g>
|
18
|
+
<polygon points="13 8 8 13 8 31 14 31 14 36 19 31 23 31 32 22 32 8" class="logo-body"></polygon>
|
19
|
+
<polygon points="26 25 30 21 30 10 14 10 14 25 18 25 18 29 22 25" class="logo-face"></polygon>
|
20
|
+
<g class="logo-eyes">
|
21
|
+
<path d="M20,14 L22,14 L22,20 L20,20 L20,14 Z M27,14 L27,20 L25,20 L25,14 L27,14 Z" class="logo-eye-path"></path>
|
22
|
+
</g>
|
23
|
+
</g>
|
24
|
+
</svg>
|
25
|
+
</figure>
|
26
|
+
</a>
|
27
|
+
<div class="nav-links-main">
|
28
|
+
<a class="nav-link" href="#">
|
29
|
+
<p>正在关注</p>
|
30
|
+
</a>
|
31
|
+
<a class="nav-link" href="#">
|
32
|
+
<p>浏览</p>
|
33
|
+
</a>
|
34
|
+
<button class="nav-icon-button" data-tooltip="更多选择">
|
35
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM8 4a2 2 0 1 0 4 0 2 2 0 0 0-4 0z"></path></svg>
|
36
|
+
</button>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="top-nav-center">
|
41
|
+
<div class="search-container">
|
42
|
+
<input type="search" class="search-input" placeholder="搜索" />
|
43
|
+
<button class="search-button" aria-label="搜索按钮">
|
44
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M13.192 14.606a7 7 0 1 1 1.414-1.414l3.101 3.1-1.414 1.415-3.1-3.1zM14 9A5 5 0 1 1 4 9a5 5 0 0 1 10 0z" fill-rule="evenodd"></path></svg>
|
45
|
+
</button>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
<div class="top-nav-right">
|
50
|
+
<button class="nav-icon-button" aria-label="Prime" data-tooltip="Prime 新品">
|
51
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M13.798 10.456 10 6.657l-3.798 3.799L4 8.805V13h12V8.805l-2.202 1.65zM18 5v8a2 2 0 0 1-2 2H4a2.002 2.002 0 0 1-2-2V5l4 3 4-4 4 4 4-3z" clip-rule="evenodd"></path></svg>
|
52
|
+
</button>
|
53
|
+
<button class="nav-icon-button" aria-label="通知" data-tooltip="打开通知">
|
54
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M4 3h12l2 4v10H2V7l2-4zm.236 4H8v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V7h3.764l-1-2H5.236l-1 2zM16 9h-2.17A3.001 3.001 0 0 1 11 11H9a3.001 3.001 0 0 1-2.83-2H4v6h12V9z" clip-rule="evenodd"></path></svg>
|
55
|
+
</button>
|
56
|
+
<button class="nav-icon-button" aria-label="悄悄话" data-tooltip="悄悄话">
|
57
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="M7.828 13 10 15.172 12.172 13H15V5H5v8h2.828zM10 18l-3-3H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2l-3 3z" clip-rule="evenodd"></path></svg>
|
58
|
+
</button>
|
59
|
+
<button class="nav-icon-button" aria-label="购买呼币" data-tooltip="购买呼币">
|
60
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
|
61
|
+
</button>
|
62
|
+
<button class="twitch-button turbo-button-full" aria-label="免费体验无广告观赏" data-tooltip="Turbo">
|
63
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10.114 9.622 11 7 7.175 9.323a.382.382 0 0 0 .013.65l.698.405L7 13l3.825-2.323a.382.382 0 0 0-.012-.65l-.699-.405z"></path><path fill-rule="evenodd" d="M18 7h-2V4H2v12h14v-3h2V7zm-4-1v3h2v2h-2v3H4V6h10z" clip-rule="evenodd"></path></svg>
|
64
|
+
<span>免费体验无广告观赏</span>
|
65
|
+
</button>
|
66
|
+
<button class="nav-icon-button turbo-button-icon" aria-label="免费体验无广告观赏" data-tooltip="Turbo">
|
67
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10.114 9.622 11 7 7.175 9.323a.382.382 0 0 0 .013.65l.698.405L7 13l3.825-2.323a.382.382 0 0 0-.012-.65l-.699-.405z"></path><path fill-rule="evenodd" d="M18 7h-2V4H2v12h14v-3h2V7zm-4-1v3h2v2h-2v3H4V6h10z" clip-rule="evenodd"></path></svg>
|
68
|
+
</button>
|
69
|
+
|
70
|
+
<button class="nav-user-avatar-button" aria-expanded="false" aria-label="用户菜单">
|
71
|
+
<div class="user-avatar-wrapper">
|
72
|
+
<img class="user-avatar-img" alt="用户头像" src="./user_avatar.jpg" style="object-fit: cover;">
|
73
|
+
</div>
|
74
|
+
</button>
|
75
|
+
</div>
|
76
|
+
</header>
|
77
|
+
|
78
|
+
<div id="main-content-wrapper">
|
79
|
+
<div id="stream-and-info-container">
|
80
|
+
<div id="stream-display-viewport">
|
81
|
+
<div id="stream-display-area">
|
82
|
+
<div id="background-display" data-darkreader-ignore></div>
|
83
|
+
<div id="neuro-static-avatar-container">
|
84
|
+
<img id="neuro-static-avatar" src="./neurosama.png" alt="Neuro-Sama Static Avatar" />
|
85
|
+
</div>
|
86
|
+
<div id="neuro-caption"></div>
|
87
|
+
<div id="startup-video-overlay">
|
88
|
+
<video id="startup-video" src="./neuro_start.mp4" playsinline muted preload="auto"></video>
|
89
|
+
</div>
|
90
|
+
<!-- Twitch 风格聊天覆盖层 -->
|
91
|
+
<div id="twitch-chat-overlay" class="messages-display">
|
92
|
+
<!-- 这里将显示聊天消息 -->
|
93
|
+
</div>
|
94
|
+
<div id="highlight-message-overlay" class="hidden"></div>
|
95
|
+
</div>
|
96
|
+
<button id="show-chat-button" class="twitch-button dark icon-button" aria-label="展开聊天">
|
97
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M16 16V4h2v12h-2zM6 9l2.501-2.5-1.5-1.5-5 5 5 5 1.5-1.5-2.5-2.5h8V9H6z"></path></svg>
|
98
|
+
</button>
|
99
|
+
<button id="mute-button" class="twitch-button dark icon-button" aria-label="取消静音(m)">
|
100
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation">
|
101
|
+
<path d="m5 7 4.146-4.146a.5.5 0 0 1 .854.353v13.586a.5.5 0 0 1-.854.353L5 13H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1zm7 1.414L13.414 7l1.623 1.623L16.66 7l1.414 1.414-1.623 1.623 1.623 1.623-1.414 1.414-1.623-1.623-1.623 1.623L12 11.66l1.623-1.623L12 8.414z"></path>
|
102
|
+
</svg>
|
103
|
+
</button>
|
104
|
+
</div>
|
105
|
+
|
106
|
+
<div id="offline-content-container" class="hidden">
|
107
|
+
<div class="offline-info-card">
|
108
|
+
<div class="offline-status-section">
|
109
|
+
<strong class="offline-status-badge">离线</strong>
|
110
|
+
<h2 class="offline-status-title">当前未连接到直播服务,或直播未开始。</h2>
|
111
|
+
</div>
|
112
|
+
<div class="offline-notification-section">
|
113
|
+
<button class="offline-notification-button twitch-button">
|
114
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M17 14v-2c-1-.5-1.75-1-2-2-.095-.38-.154-.905-.221-1.506C14.49 5.936 14.049 2 10 2 5.95 2 5.509 5.936 5.221 8.494 5.154 9.095 5.095 9.62 5 10c-.25 1-1 1.5-2 2v2h14zm-9.002 2h4-4zm4 0v.012V16zm-5.766-4h7.536a4.262 4.262 0 0 1-.708-1.515c-.129-.513-.2-1.154-.26-1.684a32.48 32.48 0 0 0-.009-.083c-.152-1.355-.314-2.606-.78-3.535-.21-.423-.447-.692-.703-.862C11.063 4.158 10.673 4 10 4s-1.063.158-1.308.32c-.256.171-.492.44-.704.863-.465.929-.627 2.18-.78 3.535L7.2 8.8c-.06.53-.131 1.171-.26 1.684-.15.603-.402 1.1-.708 1.515zm1.766 4a2.001 2.001 0 0 0 4 .012" clip-rule="evenodd"></path></svg>
|
115
|
+
<span>开启通知</span>
|
116
|
+
</button>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
<iframe class="offline-video-player" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" sandbox="allow-top-navigation allow-same-origin allow-forms allow-scripts"></iframe>
|
120
|
+
</div>
|
121
|
+
|
122
|
+
<section id="stream-info">
|
123
|
+
<div class="stream-info-layout">
|
124
|
+
<div class="stream-info-left-column">
|
125
|
+
<a href="https://twitch.tv/vedal987" class="streamer-avatar-link">
|
126
|
+
<div class="streamer-avatar-wrapper">
|
127
|
+
<img id="streamer-avatar" src="./avatar.webp" alt="Streamer Avatar" />
|
128
|
+
<div class="live-indicator-wrapper">
|
129
|
+
<div class="live-indicator-rect">
|
130
|
+
<span class="live-text" id="live-indicator-text">LIVE</span>
|
131
|
+
</div>
|
132
|
+
</div>
|
133
|
+
</div>
|
134
|
+
</a>
|
135
|
+
</div>
|
136
|
+
|
137
|
+
<div class="stream-info-right-column">
|
138
|
+
<div class="stream-info-main-row">
|
139
|
+
<div class="streamer-info-and-name">
|
140
|
+
<h1 id="streamer-nickname">vedal987</h1>
|
141
|
+
<div class="verified-badge">
|
142
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-label="已验证的合作伙伴"><path fill-rule="evenodd" d="M12.5 3.5 8 2 3.5 3.5 2 8l1.5 4.5L8 14l4.5-1.5L14 8l-1.5-4.5ZM7 11l4.5-4.5L10 5 7 8 5.5 6.5 4 8l3 3Z" clip-rule="evenodd"></path></svg>
|
143
|
+
</div>
|
144
|
+
</div>
|
145
|
+
<div class="main-action-buttons">
|
146
|
+
<button class="twitch-button icon-button" aria-label="关注" data-tooltip="取消关注">
|
147
|
+
<svg version="1.1" viewBox="0 0 20 20" x="0px" y="0px"><path d="M9.171 4.171A4 4 0 0 0 6.343 3H6a4 4 0 0 0-4 4v.343a4 4 0 0 0 1.172 2.829L10 17l6.828-6.828A4 4 0 0 0 18 7.343V7a4 4 0 0 0-4-4h-.343a4 4 0 0 0-2.829 1.172L10 5l-.829-.829z" fill-rule="evenodd"></path></svg>
|
148
|
+
</button>
|
149
|
+
<button class="twitch-button icon-button" aria-label="通知" data-tooltip="关闭通知">
|
150
|
+
<svg version="1.1" viewBox="0 0 20 20" x="0px" y="0px"><path d="M3 14v-2c1-.5 1.75-1 2-2 .095-.38.154-.905.221-1.506C5.51 5.936 5.951 2 10 2c4.05 0 4.491 3.936 4.779 6.494.067.601.126 1.126.221 1.506.25 1 1 1.5 2 2v2H3zm6.998 4a2 2 0 0 1-2-2h4v.012a2 2 0 0 1-2 1.988z"></path></svg>
|
151
|
+
</button>
|
152
|
+
<button class="twitch-button" aria-label="购买呼币">
|
153
|
+
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
|
154
|
+
<span>购买呼币</span>
|
155
|
+
</button>
|
156
|
+
<button class="twitch-button" aria-label="赠送一次订阅">
|
157
|
+
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M16 6h2v6h-1v6H3v-6H2V6h2V4.793c0-2.507 3.03-3.762 4.803-1.99.131.131.249.275.352.429L10 4.5l.845-1.268a2.81 2.81 0 0 1 .352-.429C12.969 1.031 16 2.286 16 4.793V6zM6 4.793V6h2.596L7.49 4.341A.814.814 0 0 0 6 4.793zm8 0V6h-2.596l1.106-1.659a.814.814 0 0 1 1.49.451zM16 8v2h-5V8h5zm-1 8v-4h-4v4h4zM9 8v2H4V8h5zm0 4H5v4h4v-4z" clip-rule="evenodd"></path></svg>
|
158
|
+
<span>赠送一次订阅</span>
|
159
|
+
<svg viewBox="0 0 20 20"><path d="M14.5 6.5 10 11 5.5 6.5 4 8l6 6 6-6-1.5-1.5z"></path></svg>
|
160
|
+
</button>
|
161
|
+
<button class="twitch-button subscribe-button">
|
162
|
+
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M11.456 8.255 10 5.125l-1.456 3.13-3.49.485 2.552 2.516-.616 3.485L10 13.064l3.01 1.677-.616-3.485 2.553-2.516-3.491-.485zM7.19 6.424l-4.2.583c-.932.13-1.318 1.209-.664 1.853l3.128 3.083-.755 4.272c-.163.92.876 1.603 1.722 1.132L10 15.354l3.579 1.993c.846.47 1.885-.212 1.722-1.132l-.755-4.272 3.128-3.083c.654-.644.268-1.723-.664-1.853l-4.2-.583-1.754-3.77c-.406-.872-1.706-.872-2.112 0L7.19 6.424z" clip-rule="evenodd"></path></svg>
|
163
|
+
<span>订阅</span>
|
164
|
+
<svg viewBox="0 0 20 20"><path d="M14.5 6.5 10 11 5.5 6.5 4 8l6 6 6-6-1.5-1.5z"></path></svg>
|
165
|
+
</button>
|
166
|
+
</div>
|
167
|
+
</div>
|
168
|
+
|
169
|
+
<div class="stream-info-details-row">
|
170
|
+
<div class="stream-details-left">
|
171
|
+
<p id="stream-title-full" title="neuro stream">neuro stream</p>
|
172
|
+
<div class="stream-category-and-tags">
|
173
|
+
<a href="#" class="stream-category">谈天说地</a>
|
174
|
+
<div class="stream-tags"></div>
|
175
|
+
</div>
|
176
|
+
</div>
|
177
|
+
|
178
|
+
<div class="stream-details-right">
|
179
|
+
<div class="stream-stats-section">
|
180
|
+
<div class="viewer-count">
|
181
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5 7a5 5 0 1 1 6.192 4.857A2 2 0 0 0 13 13h1a3 3 0 0 1 3 3v2h-2v-2a1 1 0 0 0-1-1h-1a3.99 3.99 0 0 1-3-1.354A3.99 3.99 0 0 1 7 15H6a1 1 0 0 0-1 1v2H3v-2a3 3 0 0 1 3-3h1a2 2 0 0 0 1.808-1.143A5.002 5.002 0 0 1 5 7zm5 3a3 3 0 1 1 0-6 3 3 0 0 1 0 6z" clip-rule="evenodd"></path></svg>
|
182
|
+
<strong id="avg-viewers">11,290</strong>
|
183
|
+
</div>
|
184
|
+
<div class="stream-duration">
|
185
|
+
<span class="duration-text" id="stream-duration-text">00:00:00</span>
|
186
|
+
</div>
|
187
|
+
</div>
|
188
|
+
<div class="stream-secondary-actions">
|
189
|
+
<button class="twitch-button icon-button" aria-label="分享">
|
190
|
+
<svg width="20" height="20" viewBox="0 0 20 20"><path d="M2 16v-3h2v3h12v-3h2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm13-9-1.5 1.5L11 6v7H9V6L6.5 8.5 5 7l5-5 5 5z"></path></svg>
|
191
|
+
</button>
|
192
|
+
<button class="twitch-button icon-button" aria-label="更多选项">
|
193
|
+
<svg width="20" height="20" viewBox="0 0 20 20"><path d="M10 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM8 4a2 2 0 1 0 4 0 2 2 0 0 0-4 0z"></path></svg>
|
194
|
+
</button>
|
195
|
+
</div>
|
196
|
+
</div>
|
197
|
+
</div>
|
198
|
+
</div>
|
199
|
+
</div>
|
200
|
+
</section>
|
201
|
+
</div>
|
202
|
+
|
203
|
+
<!-- 右侧聊天侧边栏 -->
|
204
|
+
<div id="chat-sidebar" class="chat-sidebar">
|
205
|
+
<!-- 顶部收缩按钮和聊天标题 (居中修正) -->
|
206
|
+
<div class="chat-sidebar-header">
|
207
|
+
<div class="chat-header-left">
|
208
|
+
<button id="toggle-chat-button" class="chat-toggle-button" aria-label="重叠聊天">
|
209
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M4 16V4H2v12h2zm9-1-1.5-1.5L14 11H6V9h8l-2.5-2.5L13 5l5 5-5 5z"></path></svg>
|
210
|
+
</button>
|
211
|
+
</div>
|
212
|
+
<h4 class="chat-title">直播聊天</h4>
|
213
|
+
<div class="chat-header-right">
|
214
|
+
<button class="chat-toolbar-button" aria-label="社区">
|
215
|
+
<svg width="20" height="20" viewBox="0 0 20 20"><g><path d="M7 2a4 4 0 0 0-1.015 7.87A1.334 1.334 0 0 1 4.667 11 2.667 2.667 0 0 0 2 13.667V18h2v-4.333c0-.368.298-.667.667-.667A3.32 3.32 0 0 0 7 12.047 3.32 3.32 0 0 0 9.333 13c.369 0 .667.299.667.667V18h2v-4.333A2.667 2.667 0 0 0 9.333 11c-.667 0-1.22-.49-1.318-1.13A4.002 4.002 0 0 0 7 2zM5 6a2 2 0 1 0 4 0 2 2 0 0 0-4 0z" fill-rule="evenodd"></path><path d="M14 11.83V18h4v-3.75c0-.69-.56-1.25-1.25-1.25a.75.75 0 0 1-.75-.75v-.42a3.001 3.001 0 1 0-2 0z"></path></g></svg>
|
216
|
+
</button>
|
217
|
+
</div>
|
218
|
+
</div>
|
219
|
+
|
220
|
+
<!-- 聊天消息显示区 -->
|
221
|
+
<div id="chat-messages" class="messages-display"></div>
|
222
|
+
|
223
|
+
<!-- 聊天输入区 (重构后) -->
|
224
|
+
<div class="chat-input-area">
|
225
|
+
<div class="chat-input-and-buttons">
|
226
|
+
<div class="chat-input-textarea-container">
|
227
|
+
<div class="chat-input-prefix-icons">
|
228
|
+
<button class="chat-toolbar-button" aria-label="聊天室身份">
|
229
|
+
<img alt="订阅者徽章" class="chat-badge-icon" src="./sub_badge.svg">
|
230
|
+
</button>
|
231
|
+
</div>
|
232
|
+
<div class="chat-input-wrapper">
|
233
|
+
<input type="text" id="chat-input" class="chat-input-element" placeholder="发送消息" />
|
234
|
+
</div>
|
235
|
+
<div class="chat-input-suffix-icons">
|
236
|
+
<button class="chat-toolbar-button" aria-label="欢呼">
|
237
|
+
<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" role="presentation"><path d="M14 4V2h-2v2h2z"></path><path fill-rule="evenodd" d="M10.293 8.293 16 14 4 18l-2-2L6 4l2.879 2.879A2.99 2.99 0 0 1 11 6h1v2h-1a.997.997 0 0 0-.707.293zm-3.419-.59-.643 1.93 2.67 4.625 2.254-.751-2.08-3.604-2.201-2.2zM4.498 14.83l.887-2.662 1.58 2.735-2.033.677-.434-.75z" clip-rule="evenodd"></path><path d="M16 5V4h2v1a3 3 0 0 1-3 3h-1V6h1a1 1 0 0 0 1-1zm2 5v2h-2v-2h2z"></path></svg>
|
238
|
+
</button>
|
239
|
+
<button class="chat-toolbar-button" aria-label="表情符号选择器">
|
240
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><g><path d="M7 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-4 4a2 2 0 0 0 2-2H8a2 2 0 0 0 2 2z"></path><path d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0zm-2 0a6 6 0 1 1-12 0 6 6 0 0 1 12 0z" fill-rule="evenodd"></path></g></svg>
|
241
|
+
</button>
|
242
|
+
</div>
|
243
|
+
</div>
|
244
|
+
<div class="chat-input-buttons-container">
|
245
|
+
<div class="chat-buttons-left">
|
246
|
+
<button id="sc-bits-button" class="chat-toolbar-text-button" aria-label="呼币">
|
247
|
+
<svg width="20" height="20" viewBox="0 0 20 20"><path fill-rule="evenodd" d="m3 12 7-10 7 10-7 6-7-6zm2.678-.338L10 5.487l4.322 6.173-.85.728L10 11l-3.473 1.39-.849-.729z" clip-rule="evenodd"></path></svg>
|
248
|
+
<span class="points-value">0</span>
|
249
|
+
</button>
|
250
|
+
<button id="sc-points-button" class="chat-toolbar-text-button" aria-label="频道点数">
|
251
|
+
<img class="channel-points-icon" src="./channel_points.png" alt="频道点数">
|
252
|
+
<span class="points-value">0</span>
|
253
|
+
</button>
|
254
|
+
</div>
|
255
|
+
<div class="chat-buttons-right">
|
256
|
+
<button class="chat-toolbar-button" aria-label="聊天设置">
|
257
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation"><path d="M10 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"></path><path fill-rule="evenodd" d="M9 2h2a2.01 2.01 0 0 0 1.235 1.855l.53.22a2.01 2.01 0 0 0 2.185-.439l1.414 1.414a2.01 2.01 0 0 0-.439 2.185l.22.53A2.01 2.01 0 0 0 18 9v2a2.01 2.01 0 0 0-1.855 1.235l-.22.53a2.01 2.01 0 0 0 .44 2.185l-1.415 1.414a2.01 2.01 0 0 0-2.184-.439l-.531.22A2.01 2.01 0 0 0 11 18H9a2.01 2.01 0 0 0-1.235-1.854l-.53-.22a2.009 2.009 0 0 0-2.185.438L3.636 14.95a2.009 2.009 0 0 0 .438-2.184l-.22-.531A2.01 2.01 0 0 0 2 11V9c.809 0 1.545-.487 1.854-1.235l.22-.53a2.009 2.009 0 0 0-.438-2.185L5.05 3.636a2.01 2.01 0 0 0 2.185.438l.53-.22A2.01 2.01 0 0 0 9 2zm-4 8 1.464 3.536L10 15l3.535-1.464L15 10l-1.465-3.536L10 5 6.464 6.464 5 10z" clip-rule="evenodd"></path></svg>
|
258
|
+
</button>
|
259
|
+
<button id="send-button" class="twitch-button subscribe-button">聊天</button>
|
260
|
+
</div>
|
261
|
+
</div>
|
262
|
+
</div>
|
263
|
+
</div>
|
264
|
+
</div>
|
265
|
+
</div>
|
266
|
+
<div id="settings-modal" class="settings-modal-container hidden">
|
267
|
+
<div id="settings-modal-overlay" class="modal-overlay"></div>
|
268
|
+
<div class="modal-content">
|
269
|
+
<header class="modal-header">
|
270
|
+
<h2>设置</h2>
|
271
|
+
<button id="settings-close-button" class="twitch-button icon-button">
|
272
|
+
<svg width="20" height="20" viewBox="0 0 20 20" focusable="false" aria-hidden="true" role="presentation">
|
273
|
+
<path d="M8.5 10 4 5.5 5.5 4 10 8.5 14.5 4 16 5.5 11.5 10l4.5 4.5-1.5 1.5-4.5-4.5L5.5 16 4 14.5 8.5 10z"></path>
|
274
|
+
</svg>
|
275
|
+
</button>
|
276
|
+
</header>
|
277
|
+
<main class="modal-body">
|
278
|
+
<div class="setting-item">
|
279
|
+
<label for="username-setting-input">用户名</label>
|
280
|
+
<input type="text" id="username-setting-input" class="modal-input" placeholder="输入你的聊天昵称">
|
281
|
+
</div>
|
282
|
+
<div class="setting-item avatar-setting">
|
283
|
+
<label>头像</label>
|
284
|
+
<div class="avatar-preview-container">
|
285
|
+
<img id="avatar-setting-preview" src="./user_avatar.jpg" alt="Avatar Preview" class="avatar-preview">
|
286
|
+
<input type="file" id="avatar-setting-upload" accept="image/*" class="avatar-upload-input">
|
287
|
+
<button id="avatar-upload-button" class="twitch-button">上传图片</button>
|
288
|
+
</div>
|
289
|
+
</div>
|
290
|
+
<div class="setting-item">
|
291
|
+
<label for="backend-url-input">后端地址</label>
|
292
|
+
<input type="text" id="backend-url-input" class="modal-input" placeholder="例如:ws://127.0.0.1:8000">
|
293
|
+
</div>
|
294
|
+
<div class="setting-item">
|
295
|
+
<label for="reconnect-attempts-input">最大重连次数</label>
|
296
|
+
<input type="number" id="reconnect-attempts-input" class="modal-input" placeholder="输入一个数字">
|
297
|
+
<p class="setting-description">连接失败后的自动重试次数。设置为 -1 可无限重试。</p>
|
298
|
+
</div>
|
299
|
+
</main>
|
300
|
+
<footer class="modal-footer">
|
301
|
+
<button id="settings-save-button" class="twitch-button subscribe-button"><span>保存并重连</span></button>
|
302
|
+
</footer>
|
303
|
+
</div>
|
304
|
+
</div>
|
305
|
+
</body>
|
306
|
+
</html>
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<rect width="72" height="72" rx="2" fill="black" fill-opacity="0.1"/>
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.7282 30.4598L36.001 19.933L31.2738 30.4598L19.8197 31.6598L28.3735 39.3116L25.9724 50.5487L36.001 44.8071L46.0296 50.5487L43.6285 39.3116L52.1823 31.6598L40.7282 30.4598ZM28.1168 26.0984L15.2257 27.4489C12.7694 27.7062 11.7798 30.7292 13.6147 32.3705L23.244 40.9844L20.5545 53.5714C20.042 55.9698 22.6326 57.8381 24.7722 56.6132L36.001 50.1845L47.2298 56.6132C49.3694 57.8381 51.96 55.9698 51.4475 53.5714L48.7579 40.9844L58.3873 32.3705C60.2221 30.7292 59.2326 27.7062 56.7763 27.4489L43.8852 26.0984L38.6077 14.346C37.6021 12.1067 34.3999 12.1067 33.3943 14.346L28.1168 26.0984Z" fill="#53535F"/>
|
4
|
+
</svg>
|
Binary file
|
@@ -258,8 +258,35 @@ async def startup_event():
|
|
258
258
|
else:
|
259
259
|
frontend_dir = None
|
260
260
|
|
261
|
+
# --- Mount Client Frontend ---
|
262
|
+
# Mount the client frontend at /client path (more specific) - MOUNT THIS FIRST
|
263
|
+
try:
|
264
|
+
# Production/Standard install: find client frontend in the package
|
265
|
+
client_frontend_dir_traversable = files('neuro_simulator').joinpath('client')
|
266
|
+
if not client_frontend_dir_traversable.is_dir(): raise FileNotFoundError
|
267
|
+
client_frontend_dir = str(client_frontend_dir_traversable)
|
268
|
+
logger.info(f"Found client frontend via package resources (production mode): '{client_frontend_dir}'")
|
269
|
+
except (ModuleNotFoundError, FileNotFoundError):
|
270
|
+
# Editable/Development install: fall back to relative path from source
|
271
|
+
logger.info("Could not find client frontend via package resources, falling back to development mode path.")
|
272
|
+
dev_client_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'client', 'dist'))
|
273
|
+
if os.path.isdir(dev_client_path):
|
274
|
+
client_frontend_dir = dev_client_path
|
275
|
+
logger.info(f"Found client frontend via relative path (development mode): '{client_frontend_dir}'")
|
276
|
+
else:
|
277
|
+
client_frontend_dir = None
|
278
|
+
|
279
|
+
if client_frontend_dir:
|
280
|
+
app.mount("/client", SPAStaticFiles(directory=client_frontend_dir, html=True), name="client")
|
281
|
+
logger.info("Client frontend mounted at /client")
|
282
|
+
else:
|
283
|
+
logger.error("Client frontend directory not found in either production or development locations.")
|
284
|
+
|
285
|
+
# --- Mount Dashboard Frontend ---
|
286
|
+
# Mount the dashboard frontend at / path (more general) - MOUNT THIS AFTER
|
261
287
|
if frontend_dir:
|
262
288
|
app.mount("/", SPAStaticFiles(directory=frontend_dir, html=True), name="dashboard")
|
289
|
+
logger.info("Dashboard frontend mounted at /")
|
263
290
|
else:
|
264
291
|
logger.error("Frontend directory not found in either production or development locations.")
|
265
292
|
|
@@ -55,7 +55,7 @@ neuro_simulator/chatbot/tools/post_chat_message.py,sha256=lchGCSQKzPZgb1z__MIDkG
|
|
55
55
|
neuro_simulator/core/__init__.py,sha256=-ojq25c8XA0CU25b0OxcGjH4IWFEDHR-HXSRSZIuKe8,57
|
56
56
|
neuro_simulator/core/agent_factory.py,sha256=jX7wRyTS7Jd-qfIGj0kGs_0pblx5zUcyVAyRETIUhvw,1359
|
57
57
|
neuro_simulator/core/agent_interface.py,sha256=ZXUCtkQUvsBQ5iCb0gTILJaShn5KmSrEgKhd7PK18HE,2794
|
58
|
-
neuro_simulator/core/application.py,sha256=
|
58
|
+
neuro_simulator/core/application.py,sha256=xgXLwYzq71AkiFLSn2L_hePuUp9sA0Eknb_DxjEeNDE,30292
|
59
59
|
neuro_simulator/core/config.py,sha256=eH5LdTm7gtLBqV7bbiUulYTW2abKYUmK934x3zOS19w,4766
|
60
60
|
neuro_simulator/core/path_manager.py,sha256=_MK3S5ab30zT9zDISC-noDbVE8Axs5y1A3WgImqD-wI,4284
|
61
61
|
neuro_simulator/services/__init__.py,sha256=s3ZrAHg5TpJakadAAGY1h0wDw_xqN4Je4aJwJyRBmk4,61
|
@@ -68,6 +68,40 @@ neuro_simulator/utils/process.py,sha256=9OYWx8fzaJZqmFUcjQX37AnBhl7YWvrLxDWBa30v
|
|
68
68
|
neuro_simulator/utils/queue.py,sha256=fJ0c7pwdAoUrLGC0qudehABkoBB7-pzROvmj_nA9D1c,2690
|
69
69
|
neuro_simulator/utils/state.py,sha256=DdBqSAYfjOFtJfB1hEGhYPh32r1ZvFuVlN_-29_-luA,664
|
70
70
|
neuro_simulator/utils/websocket.py,sha256=fjC-qipzjgM_e7XImP12DFmLhvxkMOSr2GnUWws7esE,2058
|
71
|
+
neuro_simulator/client/avatar.webp,sha256=gLkJF8g-akjXPXBzr_FRYmTt9RM9oXEmDnIEh12DkZs,25884
|
72
|
+
neuro_simulator/client/background.webp,sha256=JD9OFItvxSm0xQ8jRJZ9eiXLZ1R-1xZXd7EMWr4b170,1174858
|
73
|
+
neuro_simulator/client/background_old.webp,sha256=10Jv9ElOAvpysZUTPw24tC7kKi2-oi6x7KuvI9bE9aU,51214
|
74
|
+
neuro_simulator/client/banner.jpeg,sha256=LFqSS0alXwQPilUnHNZ4qkx63Jbc5VYF0HsJOiM5Nug,448732
|
75
|
+
neuro_simulator/client/channel_points.png,sha256=Tmy9kmKQWNfK-gPMKVvUYrpEiT3CsPgSx6V1-PJRBhI,11509
|
76
|
+
neuro_simulator/client/error.mp3,sha256=ovZ-JXAHrqYUkjFshP48_3ZQcoRi7gUxO_ZkShoeIyA,24048
|
77
|
+
neuro_simulator/client/favicon.ico,sha256=8mR4U-Ufv6l8_Md9C6yqbv6mZ0WV9DRLSPpms8WKp4g,370418
|
78
|
+
neuro_simulator/client/index.html,sha256=nlFEBvqmI2WtW_htLeJyY2-o9iYLEeYjTfk6eVOFHqE,22427
|
79
|
+
neuro_simulator/client/neuro_start.mp4,sha256=xCLnNzv4THnzRYwkdV6EiqXc-XtFd867R2ZVLDvNp0Y,8226418
|
80
|
+
neuro_simulator/client/neurosama.png,sha256=FTyEEEu0XwRxicKtmQXQONiRVhYKgTsPqlOO2Ymbp1c,1447110
|
81
|
+
neuro_simulator/client/sc_pink.png,sha256=bmC1Wn8I27nsc9lkQJ8-LeacL6Y-eOZor_JkmXy9Om0,56768
|
82
|
+
neuro_simulator/client/sc_purple.png,sha256=XUs6fB0CHPTzQQ3npS1rje5gcigy-T4uao8k3RxOk50,57224
|
83
|
+
neuro_simulator/client/sub_badge.svg,sha256=-Es6Nz9sd7c3sJ4Mys2q0vSd7AZVC9uE-Sq4ipCsioo,825
|
84
|
+
neuro_simulator/client/user_avatar.jpg,sha256=j2MZCFm0MYmigPPOLsuFqA8PhvQ5SWb0GTpqb9VKqvY,29625
|
85
|
+
neuro_simulator/client/assets/index-C_kzLmQy.css,sha256=ldHgSwFauTDidMP-jwCYOWpFSFsjJp-3FcqSniVfkFk,27793
|
86
|
+
neuro_simulator/client/assets/index-DRLWJPZv.js,sha256=5sAhfYAN0xKCbG6iZv20bjA7CaXr4zmqR57wd5ko14o,37340
|
87
|
+
neuro_simulator/client/assets/inter-cyrillic-400-normal-BLGc9T1a.woff2,sha256=Y20TqrBe2fpJ3xnFf93btvPQhPRjdD7lJXVJ27cSQAU,7712
|
88
|
+
neuro_simulator/client/assets/inter-cyrillic-400-normal-alAqRL36.woff,sha256=GmDEQlTts7GpPN_vX-7Kqig5IarhIL_nICL6SWvtq5Y,9780
|
89
|
+
neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-BE2fNs0E.woff,sha256=RHF2hsw99YHFTevMHQw7zGUUaXtPsetnWZbP0cfZaYA,13336
|
90
|
+
neuro_simulator/client/assets/inter-cyrillic-ext-400-normal-Dc4VJyIJ.woff2,sha256=7zI9hOR108QsGhOSH8Ic5nyJ_-o4Fed_J8BdH_JfLUI,10216
|
91
|
+
neuro_simulator/client/assets/inter-greek-400-normal-C3I71FoW.woff,sha256=qtjBnZ0fZGTNL3UriqYVPfuO_Pyp80PLeOV9XYi1p30,9924
|
92
|
+
neuro_simulator/client/assets/inter-greek-400-normal-DxZsaF_h.woff2,sha256=FQ3EOR5Wh4Jt9M38kM_sEMUHGRynqpC2gnIgVPO7wN4,7840
|
93
|
+
neuro_simulator/client/assets/inter-greek-ext-400-normal-Bput3-QP.woff2,sha256=q1Jss0CSlwz0KQttNYi8vaCXFJPsWr6l9bVy4IZgF-w,5232
|
94
|
+
neuro_simulator/client/assets/inter-greek-ext-400-normal-XIH6-K3k.woff,sha256=TqEseb219Xmc_lFEyJ-HsKZ_pBXvoISQ6Tg9G3JEbCg,7064
|
95
|
+
neuro_simulator/client/assets/inter-latin-400-normal-C38fXH4l.woff2,sha256=iQmQSrbIcuuZQJNIKoiijsos2VkS17b-zXIQOw3Aftw,23664
|
96
|
+
neuro_simulator/client/assets/inter-latin-400-normal-CyCys3Eg.woff,sha256=4g-gtP0t0m5NFLOsPMkiUJw6Y_pekQ6QxhRUSqBC3UU,30696
|
97
|
+
neuro_simulator/client/assets/inter-latin-ext-400-normal-77YHD8bZ.woff,sha256=Iq6DYPutJNKvfGyYmKNGyIF3Tch_fPlL0mLEitWNcRY,47560
|
98
|
+
neuro_simulator/client/assets/inter-latin-ext-400-normal-C1nco2VV.woff2,sha256=Z0Sn9QnrxqsiCmzU6nfomK3wFPA9iNzaXUXYqf7vtOk,35000
|
99
|
+
neuro_simulator/client/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff,sha256=VKpC0yW839Yj7LnqxKXjxkIMY9e4bp2FRftHn2U761c,6500
|
100
|
+
neuro_simulator/client/assets/inter-vietnamese-400-normal-DMkecbls.woff2,sha256=VHrZ_a6wrkNIf0uKAuR8NlU8DAy3PIqpj5O3thX2tV0,4972
|
101
|
+
neuro_simulator/client/fonts/causten.woff2,sha256=ttA0YdxaFPh3eaerSiQ7JC3zwls7GadBhnWIa7jO2Lg,23820
|
102
|
+
neuro_simulator/client/fonts/comic.woff2,sha256=8sW4Je7yeNTCVtBgLdBYcFgG5Se9hTLN7S10qhoV1b0,142392
|
103
|
+
neuro_simulator/client/fonts/first-coffee.woff2,sha256=pUBfpDh_6HQ20V9EuKWq2UFLnMi9RPHigLN2rOU9Jps,11496
|
104
|
+
neuro_simulator/client/fonts/noto-sans-sc.woff2,sha256=KsZkKouZc8g1eQ_5wzOWuGkIcSYzGGsHYZJhyu0hFDg,4182468
|
71
105
|
neuro_simulator/dashboard/favicon.ico,sha256=8mR4U-Ufv6l8_Md9C6yqbv6mZ0WV9DRLSPpms8WKp4g,370418
|
72
106
|
neuro_simulator/dashboard/first-coffee.woff2,sha256=pUBfpDh_6HQ20V9EuKWq2UFLnMi9RPHigLN2rOU9Jps,11496
|
73
107
|
neuro_simulator/dashboard/index.html,sha256=ClWlZyXnWx8VnCKyxANGG39z3B0ecsfLMCDpSgrmfFI,489
|
@@ -95,8 +129,8 @@ neuro_simulator/dashboard/assets/materialdesignicons-webfont-B7mPwVP_.ttf,sha256
|
|
95
129
|
neuro_simulator/dashboard/assets/materialdesignicons-webfont-CSr8KVlo.eot,sha256=CxgxBNL8XyYZbnc8d72vLgVQn9QlnS0V7O3Kebh-hPk,1307880
|
96
130
|
neuro_simulator/dashboard/assets/materialdesignicons-webfont-Dp5v-WZN.woff2,sha256=Zi_vqPL4qVwYWI0hd0eJwQfGTnccvmWmmvRikcQxGvw,403216
|
97
131
|
neuro_simulator/dashboard/assets/materialdesignicons-webfont-PXm3-2wK.woff,sha256=pZKKDVwvYk5G-Y2bFcL2AEU3f3xZTdeKF1kTLqO0Y-s,587984
|
98
|
-
neuro_simulator-0.6.
|
99
|
-
neuro_simulator-0.6.
|
100
|
-
neuro_simulator-0.6.
|
101
|
-
neuro_simulator-0.6.
|
102
|
-
neuro_simulator-0.6.
|
132
|
+
neuro_simulator-0.6.1.dist-info/METADATA,sha256=_uIC9ykhARn1EvQ6-DhDymJ0xOhxAYSkZpMJ12UOt7k,11242
|
133
|
+
neuro_simulator-0.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
134
|
+
neuro_simulator-0.6.1.dist-info/entry_points.txt,sha256=qVd5ypnRRgU8Cw7rWfZ7o0OXyS9P9hgY-cRoN_mgz9g,51
|
135
|
+
neuro_simulator-0.6.1.dist-info/licenses/LICENSE,sha256=cPsKhVNOnNSSva44vcCU-SDDFf8iE-EhpGXCiUK9tAM,1072
|
136
|
+
neuro_simulator-0.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|