@relaya-chat/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +215 -0
- package/dist/AdminPanel-CBs17LkG.js +3159 -0
- package/dist/AdminPanel-DqI4RFt9.cjs +1 -0
- package/dist/admin.cjs +1 -0
- package/dist/admin.js +2 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +3224 -0
- package/dist/relaya.css +2 -0
- package/package.json +54 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3224 @@
|
|
|
1
|
+
import { S as e, _ as t, a as n, b as r, c as i, d as a, f as o, g as s, h as c, i as l, l as u, m as d, n as f, o as p, p as m, r as h, s as g, t as _, u as v, v as y, x as b, y as x } from "./AdminPanel-CBs17LkG.js";
|
|
2
|
+
import S, { createContext as C, useCallback as w, useContext as T, useEffect as E, useMemo as D, useRef as O, useState as k } from "react";
|
|
3
|
+
import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
4
|
+
//#region src/styles/embed.css?inline
|
|
5
|
+
var N = ":root,[data-theme=light]{--relaya-color-bg:#f0f2f5;--relaya-color-surface:#fff;--relaya-color-surface-2:#f8f9fa;--relaya-color-border:#e0e3e8;--relaya-color-text:#1a1a2e;--relaya-color-text-muted:#6c757d;--relaya-color-text-faint:#adb5bd;--relaya-color-accent:#007aff;--relaya-color-accent-hover:#0062cc;--relaya-color-danger:#dc3545;--relaya-color-danger-hover:#c82333;--relaya-color-warning:#ffc107;--relaya-color-success:#28a745;--relaya-color-online:#28a745;--relaya-color-shadow:#00000014;--relaya-color-own-msg-bg:#007aff;--relaya-color-own-msg-text:#fff;--relaya-color-other-msg-bg:#e9ecef;--relaya-color-other-msg-text:#1a1a2e;--relaya-color-deleted-bg:#f1f3f5;--relaya-color-deleted-text:#868e96;--relaya-color-optimistic-bg:#cce5ff;--relaya-color-failed-bg:#f8d7da;--relaya-color-status-bar:#343a40;--relaya-color-status-text:#fff;--relaya-radius-sm:4px;--relaya-radius-md:8px;--relaya-radius-lg:16px;--relaya-radius-bubble:18px;--relaya-font-size-xs:11px;--relaya-font-size-sm:13px;--relaya-font-size-base:15px;--relaya-font-size-lg:17px;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:12px;--spacing-lg:16px;--spacing-xl:24px;--sidebar-width:220px;--input-height:52px;--header-height:52px;--sp-title-bg:var(--relaya-color-surface);--sp-name-color:var(--relaya-color-text);--sp-name-font:inherit;--sp-ui-font:-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--sp-online-color:var(--relaya-color-text-muted);--sp-no-name-color:var(--relaya-color-text-muted);--sp-btn-border:var(--relaya-color-border);--sp-btn-bg:var(--relaya-color-surface);--sp-btn-text:var(--relaya-color-text);--sp-avatar-bg:var(--relaya-color-accent);--sp-avatar-text:#fff;--sp-own-msg-bg:var(--relaya-color-own-msg-bg);--sp-own-msg-text:var(--relaya-color-own-msg-text);--sp-other-msg-bg:var(--relaya-color-other-msg-bg);--sp-other-msg-text:var(--relaya-color-other-msg-text);--sp-msg-font:inherit;--sp-msg-font-size:var(--relaya-font-size-base);--sp-time-color:var(--relaya-color-text-faint);--sp-send-btn-bg:var(--sp-own-msg-bg);--sp-send-btn-text:var(--sp-own-msg-text)}[data-theme=dark]{--relaya-color-bg:#0d1117;--relaya-color-surface:#161b22;--relaya-color-surface-2:#21262d;--relaya-color-border:#30363d;--relaya-color-text:#e6edf3;--relaya-color-text-muted:#8b949e;--relaya-color-text-faint:#484f58;--relaya-color-accent:#58a6ff;--relaya-color-accent-hover:#79b8ff;--relaya-color-danger:#f85149;--relaya-color-danger-hover:#ff7b72;--relaya-color-warning:#d29922;--relaya-color-success:#3fb950;--relaya-color-online:#3fb950;--relaya-color-shadow:#0006;--relaya-color-own-msg-bg:#1f6feb;--relaya-color-own-msg-text:#fff;--relaya-color-other-msg-bg:#21262d;--relaya-color-other-msg-text:#e6edf3;--relaya-color-deleted-bg:#2d333b;--relaya-color-deleted-text:#8b949e;--relaya-color-optimistic-bg:#1f3a5c;--relaya-color-failed-bg:#3d1f1f;--relaya-color-status-bar:#1c2128;--relaya-color-status-text:#e6edf3}:root,[data-theme=light]{--relaya-color-message-bg:var(--sp-other-msg-bg,var(--relaya-color-other-msg-bg));--relaya-color-message-own-bg:var(--sp-own-msg-bg,var(--relaya-color-own-msg-bg));--relaya-color-text-secondary:var(--relaya-color-text-muted);--relaya-color-input-bg:var(--relaya-color-surface-2);--relaya-color-input-text:var(--relaya-color-text);--relaya-color-btn-bg:var(--sp-send-btn-bg,var(--sp-own-msg-bg));--relaya-color-btn-text:var(--sp-send-btn-text,var(--sp-own-msg-text));--relaya-color-name-mod:var(--relaya-color-accent);--relaya-color-link:var(--relaya-color-accent);--relaya-color-link-active:var(--relaya-color-accent-hover)}.relaya-root .app{background:var(--relaya-color-bg);flex-direction:column;height:100%;display:flex}.relaya-root .chat-window{background:var(--relaya-color-surface);flex-direction:column;height:100%;display:flex}.relaya-root .chat-header{align-items:center;gap:var(--spacing-sm);padding:0 var(--spacing-md);height:var(--header-height);background:var(--sp-title-bg);border-bottom:1px solid color-mix(in srgb, var(--relaya-color-border) 40%, transparent);z-index:10;flex-shrink:0;display:flex;overflow:hidden}.relaya-root .chat-body{flex:1;min-height:0;display:flex;overflow:hidden}.relaya-root .message-list-container{flex-direction:column;flex:1;min-width:0;display:flex;position:relative;overflow:hidden}.relaya-root .message-list{padding:var(--spacing-md) var(--spacing-lg);gap:var(--spacing-xs);scroll-behavior:smooth;flex-direction:column;flex:1;display:flex;overflow-y:auto}.relaya-root .message-list::-webkit-scrollbar{width:6px}.relaya-root .message-list::-webkit-scrollbar-track{background:0 0}.relaya-root .message-list::-webkit-scrollbar-thumb{background:var(--relaya-color-border);border-radius:3px}.relaya-root .loading-screen{height:100%;color:var(--relaya-color-text-muted);font-size:var(--relaya-font-size-sm);justify-content:center;align-items:center;gap:var(--spacing-sm);display:flex}.relaya-root .btn{justify-content:center;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-sm) var(--spacing-lg);border-radius:var(--relaya-radius-md);font-size:var(--relaya-font-size-base);white-space:nowrap;font-weight:500;transition:background-color .15s,opacity .15s;display:inline-flex}.relaya-root .btn--primary{background:var(--relaya-color-accent);color:#fff;width:100%;padding:var(--spacing-md)}.relaya-root .btn--primary:hover:not(:disabled){background:var(--relaya-color-accent-hover)}.relaya-root .btn--danger{background:var(--relaya-color-danger);color:#fff}.relaya-root .btn--danger:hover:not(:disabled){background:var(--relaya-color-danger-hover)}.relaya-root .btn--ghost{color:var(--relaya-color-text-muted);border:1px solid var(--relaya-color-border);background:0 0}.relaya-root .btn--ghost:hover:not(:disabled){background:var(--relaya-color-surface-2)}.relaya-root .btn--icon{padding:var(--spacing-xs);color:var(--relaya-color-text-muted);border-radius:var(--relaya-radius-sm);flex-shrink:0;min-width:32px;min-height:32px}.relaya-root .btn--icon:hover{background:var(--relaya-color-surface-2);color:var(--relaya-color-text)}.relaya-root .btn:disabled{opacity:.55;cursor:not-allowed}.relaya-root .error-message{color:var(--relaya-color-danger);font-size:var(--relaya-font-size-sm);padding:var(--spacing-sm) var(--spacing-md);background:color-mix(in srgb, var(--relaya-color-danger) 10%, transparent);border-radius:var(--relaya-radius-sm)}.relaya-root .modal-overlay{z-index:100;padding:var(--spacing-lg);background:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.relaya-root .modal{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-lg);padding:var(--spacing-xl);width:100%;max-width:440px;box-shadow:0 8px 32px var(--relaya-color-shadow)}.relaya-root .modal__title{font-size:var(--relaya-font-size-base);margin-bottom:var(--spacing-md);font-weight:600}.relaya-root .modal__body{margin-bottom:var(--spacing-lg)}.relaya-root .modal__body p{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin-bottom:var(--spacing-sm);line-height:1.5}.relaya-root .modal__body label{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin-bottom:var(--spacing-xs);margin-top:var(--spacing-md);font-weight:500;display:block}.relaya-root .modal__body select,.relaya-root .modal__body input,.relaya-root .modal__body textarea{width:100%;padding:var(--spacing-sm) var(--spacing-md);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);background:var(--relaya-color-surface-2);color:var(--relaya-color-text);font-size:var(--relaya-font-size-sm)}.relaya-root .modal__body select:focus,.relaya-root .modal__body input:focus,.relaya-root .modal__body textarea:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .modal__body textarea{resize:vertical;min-height:80px}.relaya-root .modal__footer{gap:var(--spacing-sm);justify-content:flex-end;display:flex}.relaya-root .login-screen{height:100%;padding:var(--spacing-lg);justify-content:center;align-items:center;display:flex}.relaya-root .login-card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-lg);padding:var(--spacing-xl) var(--spacing-xl);width:100%;max-width:400px;box-shadow:0 4px 24px var(--relaya-color-shadow)}.relaya-root .login-card__logo{text-align:center;margin-bottom:var(--spacing-xl)}.relaya-root .login-card__title{font-size:var(--relaya-font-size-lg);color:var(--relaya-color-text);margin-bottom:var(--spacing-xs);text-align:center;font-weight:600}.relaya-root .login-card__subtitle{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);text-align:center;margin-bottom:var(--spacing-xl)}.relaya-root .login-card__form{gap:var(--spacing-md);flex-direction:column;display:flex}.relaya-root .form-field label{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin-bottom:var(--spacing-xs);font-weight:500;display:block}.relaya-root .form-field input{width:100%;padding:var(--spacing-sm) var(--spacing-md);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);background:var(--relaya-color-surface-2);color:var(--relaya-color-text);font-size:var(--relaya-font-size-base);transition:border-color .15s}.relaya-root .form-field input:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .magic-link-sent{height:100%;padding:var(--spacing-lg);justify-content:center;align-items:center;display:flex}.relaya-root .magic-link-sent__card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-lg);padding:var(--spacing-xl);text-align:center;width:100%;max-width:400px;box-shadow:0 4px 24px var(--relaya-color-shadow)}.relaya-root .magic-link-sent__icon{margin-bottom:var(--spacing-lg);font-size:48px}.relaya-root .magic-link-sent__title{font-size:var(--relaya-font-size-lg);margin-bottom:var(--spacing-sm);font-weight:600}.relaya-root .magic-link-sent__body{color:var(--relaya-color-text-muted);font-size:var(--relaya-font-size-sm);margin-bottom:var(--spacing-xl);line-height:1.6}.relaya-root .modal-overlay{z-index:1000;background:#00000080;animation:.2s ease-out fadeIn;position:fixed;inset:0}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.relaya-root .auth-modal{z-index:1001;padding:var(--spacing-lg);justify-content:center;align-items:center;animation:.3s ease-out slideUp;display:flex;position:fixed;inset:0}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.relaya-root .auth-modal__content{background:var(--relaya-color-surface);border-radius:var(--relaya-radius-lg);padding:var(--spacing-xl);width:100%;max-width:400px;box-shadow:0 8px 32px var(--relaya-color-shadow);gap:var(--spacing-lg);flex-direction:column;display:flex;position:relative}.relaya-root .auth-modal__close-x{top:var(--spacing-md);right:var(--spacing-md);border-radius:var(--relaya-radius-sm);width:32px;height:32px;color:var(--relaya-color-text-muted);cursor:pointer;background:0 0;justify-content:center;align-items:center;font-size:20px;line-height:1;transition:background-color .15s,color .15s;display:flex;position:absolute}.relaya-root .auth-modal__close-x:hover{background:var(--relaya-color-surface-2);color:var(--relaya-color-text)}.relaya-root .auth-modal__icon{text-align:center;font-size:48px;line-height:1}.relaya-root .auth-modal__title{font-size:var(--relaya-font-size-lg);color:var(--relaya-color-text);text-align:center;margin:0;font-weight:600;line-height:1.3}.relaya-root .auth-modal__message{font-size:var(--relaya-font-size-base);color:var(--relaya-color-text);text-align:center;margin:0;line-height:1.5}.relaya-root .auth-modal__hint{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);text-align:center;margin:0;line-height:1.4}.relaya-root .auth-modal__input{width:100%;padding:12px var(--spacing-md);font-size:var(--relaya-font-size-base);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);background:var(--relaya-color-surface);color:var(--relaya-color-text);font-family:inherit;transition:border-color .2s}.relaya-root .auth-modal__input:focus{border-color:var(--relaya-color-accent);outline:none;box-shadow:0 0 0 3px #007aff1a}.relaya-root .auth-modal__input:disabled{opacity:.6;cursor:not-allowed}.relaya-root .auth-modal__buttons{gap:var(--spacing-md);justify-content:flex-end;display:flex}.relaya-root .auth-modal__close-btn{width:100%}.relaya-root .auth-success{height:100%;padding:var(--spacing-lg);justify-content:center;align-items:center;display:flex}.relaya-root .auth-success__card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-lg);padding:var(--spacing-xl);text-align:center;width:100%;max-width:480px;box-shadow:0 4px 24px var(--relaya-color-shadow)}.relaya-root .auth-success .success-icon{margin-bottom:var(--spacing-lg);font-size:64px;line-height:1}.relaya-root .auth-success h1{font-size:var(--relaya-font-size-xl);margin-bottom:var(--spacing-sm);color:var(--relaya-color-text);font-weight:600}.relaya-root .auth-success .user-greeting{font-size:var(--relaya-font-size-base);color:var(--relaya-color-text-muted);margin-bottom:var(--spacing-xl)}.relaya-root .auth-success .instructions{margin-bottom:var(--spacing-xl)}.relaya-root .auth-success .instructions p{font-size:var(--relaya-font-size-base);color:var(--relaya-color-text);margin-bottom:var(--spacing-md);line-height:1.6}.relaya-root .auth-success .instructions p:last-child{margin-bottom:0}.relaya-root .auth-success .hint{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted)}.relaya-root .auth-success .btn{min-width:140px}.relaya-root .otp-code-input{flex-direction:column;align-items:center;gap:20px;width:100%;display:flex}.relaya-root .otp-code-input__icon{font-size:48px;line-height:1}.relaya-root .otp-code-input__title{color:var(--relaya-color-text);text-align:center;margin:0;font-size:24px;font-weight:600}.relaya-root .otp-code-input__hint{color:var(--relaya-color-text-muted);text-align:center;margin:0;font-size:14px;line-height:1.5}.relaya-root .otp-code-input__hint strong{color:var(--relaya-color-text);font-weight:500}.relaya-root .otp-code-input__field{letter-spacing:.5em;text-align:center;border:2px solid var(--relaya-color-border);background:var(--relaya-color-surface);width:100%;max-width:240px;color:var(--relaya-color-text);border-radius:8px;padding:16px 12px;font-family:Courier New,Courier,monospace;font-size:28px;font-weight:600;transition:all .2s}@media (width<=600px){.relaya-root .otp-code-input__field{letter-spacing:.4em;padding:14px 10px;font-size:24px}}.relaya-root .otp-code-input__field:focus{border-color:var(--relaya-color-accent);outline:none;box-shadow:0 0 0 3px #007aff1a}.relaya-root .otp-code-input__field:disabled{opacity:.6;cursor:not-allowed}.relaya-root .otp-code-input__field::placeholder{opacity:.3}.relaya-root .otp-code-input__error{color:#ff3b30;text-align:center;background:#ff3b301a;border:1px solid #ff3b304d;border-radius:6px;width:100%;padding:12px 16px;font-size:14px}.relaya-root .otp-code-input__actions{flex-wrap:wrap;justify-content:center;gap:12px;width:100%;display:flex}@media (width<=400px){.relaya-root .otp-code-input__actions{flex-direction:column;gap:8px}.relaya-root .otp-code-input__actions .btn{width:100%}}.relaya-root .otp-code-input__auto-submit{color:var(--relaya-color-text-muted);margin:0;font-size:13px;font-style:italic}.relaya-root .auth-modal__error{color:#ff3b30;text-align:center;background:#ff3b301a;border:1px solid #ff3b304d;border-radius:6px;width:100%;margin-top:8px;padding:12px 16px;font-size:14px}.relaya-root .mention-chip{background:var(--relaya-color-surface-2);border:1px solid var(--relaya-color-message-own-bg);border-radius:var(--relaya-radius-sm);color:var(--relaya-color-message-own-bg);white-space:nowrap;padding:0 4px;font-size:.9em;font-weight:600;display:inline}.relaya-root .mention-chip--self{background:var(--relaya-color-message-own-bg);border-color:var(--relaya-color-message-own-bg);color:var(--sp-own-msg-text)}.relaya-root .chat-header__title{font-size:var(--relaya-font-size-base);font-weight:600;font-family:var(--sp-name-font);color:var(--sp-name-color);white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.relaya-root .chat-header__online{font-size:var(--relaya-font-size-sm);color:var(--sp-online-color);white-space:nowrap;border-radius:var(--relaya-radius-sm);flex-shrink:0;align-items:center;gap:2px;padding:4px 6px;transition:opacity .15s;display:flex}.relaya-root .chat-header__online:hover{opacity:.7}.relaya-root .online-dot{background:var(--relaya-color-online);border-radius:50%;flex-shrink:0;width:8px;height:8px}.relaya-root .chat-header .btn--ghost{background:var(--sp-btn-bg);border:1px solid var(--sp-btn-border);color:var(--sp-btn-text);padding:3px 10px;font-size:12px}.relaya-root .chat-header .btn--ghost:hover:not(:disabled){opacity:.85;background:var(--sp-btn-bg)}.relaya-root .chat-header .btn--primary{background:var(--sp-btn-bg);color:var(--sp-btn-text);width:auto;padding:3px 10px;font-size:12px}.relaya-root .chat-header .btn--primary:hover:not(:disabled){opacity:.85;background:var(--sp-btn-bg)}.relaya-root .chat-name-btn{min-width:0;max-width:120px;overflow:hidden}.relaya-root .chat-name-btn__label{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.relaya-root .chat-header__popout{flex-shrink:0;line-height:0;padding:4px 6px!important}.relaya-root .chat-header .chat-name-editor__display{font-size:12px;color:var(--sp-no-name-color)!important}.relaya-root .chat-name-editor__input{border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-surface);color:var(--relaya-color-text);max-width:140px;padding:3px 8px;font-size:13px}.relaya-root .chat-name-editor__input:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .retention-boundary{padding:var(--spacing-sm) var(--spacing-md);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint);text-align:center;border-top:1px solid var(--relaya-color-border);margin-bottom:var(--spacing-sm);justify-content:center;align-items:center;display:flex}.relaya-root .load-older-btn{padding:var(--spacing-sm);flex-shrink:0;justify-content:center;display:flex}.relaya-root .load-older-btn button{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-accent);padding:var(--spacing-xs) var(--spacing-md);border-radius:var(--relaya-radius-sm)}.relaya-root .load-older-btn button:hover:not(:disabled){background:var(--relaya-color-surface-2)}.relaya-root .messages-empty{color:var(--relaya-color-text-faint);font-size:var(--relaya-font-size-sm);text-align:center;padding:var(--spacing-xl);flex:1;justify-content:center;align-items:center;display:flex}.relaya-root .message-item{--message-avatar-slot-width:32px;gap:var(--spacing-sm);align-items:flex-start;max-width:85%;display:flex;position:relative}.relaya-root .message-item--has-image{max-width:100%}.relaya-root .message-item--has-image.message-item--other .message-item__bubble-wrap{max-width:calc(100% - var(--message-avatar-slot-width) - var(--spacing-sm))}.relaya-root .message-item--has-image.message-item--own .message-item__bubble-wrap{max-width:100%}.relaya-root .message-item--own{flex-direction:row-reverse;align-self:flex-end}.relaya-root .message-item--other{align-self:flex-start}.relaya-root .message-item--deleted{opacity:.65}.relaya-root .message-item__avatar{background:var(--sp-avatar-bg);width:48px;height:48px;color:var(--sp-avatar-text);font-size:var(--relaya-font-size-xs);border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;font-weight:600;display:flex}.relaya-root .message-item__avatar-column{flex-direction:column;flex-shrink:0;align-items:flex-start;gap:2px;display:flex}.relaya-root .message-item__avatar-slot{width:var(--message-avatar-slot-width);flex-shrink:0;justify-content:center;padding-top:16px;display:flex}.relaya-root .message-item--own .message-item__avatar-slot{margin-left:3px}.relaya-root .message-item--other .message-item__avatar-slot{margin-right:3px;padding-top:0}.relaya-root .message-item__avatar-column .message-item__avatar-slot{padding-top:0}.relaya-root .message-item__avatar--image{object-fit:cover}.relaya-root .message-item__bubble-wrap{flex-direction:column;min-width:0;display:flex}.relaya-root .message-item--own .message-item__bubble-wrap{align-items:flex-end}.relaya-root .message-item__name-row{align-items:baseline;gap:var(--spacing-sm);padding:0 var(--spacing-sm) 2px;width:100%;min-width:0;display:flex}.relaya-root .message-item--other .message-item__name-row{justify-content:space-between}.relaya-root .message-item--own .message-item__name-row{justify-content:flex-end}.relaya-root .message-item__author--mod{color:var(--relaya-color-name-mod)}.relaya-root .message-item__author{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.relaya-root .message-item__time{font-size:var(--relaya-font-size-xs);color:var(--sp-time-color);white-space:nowrap;flex-shrink:0}.relaya-root .message-item__status-row{align-items:center;gap:var(--spacing-xs);padding:2px var(--spacing-sm) 0;display:flex}.relaya-root .message-item__status{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint)}.relaya-root .message-item__bubble{padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--relaya-radius-bubble);font-family:var(--sp-msg-font);font-size:var(--sp-msg-font-size);word-break:break-word;line-height:1.4;position:relative}.relaya-root .message-link{color:var(--relaya-color-link);word-break:break-all;text-decoration:underline}.relaya-root .message-link:hover,.relaya-root .message-link:active{color:var(--relaya-color-link-active)}.relaya-root .message-item__bubble strong{font-weight:700}.relaya-root .message-item__bare-image-wrap{max-width:100%}.relaya-root .chat-image{justify-content:center;align-items:center;max-width:100%;display:inline-flex;position:relative}.relaya-root .chat-image--inline{vertical-align:middle;margin:1px 2px}.relaya-root .chat-image--bare{display:block}.relaya-root .chat-image__img{width:auto;max-width:100%;height:auto;transition:opacity .15s;display:block}.relaya-root .chat-image__loading{background:color-mix(in srgb, var(--relaya-color-text-faint) 20%, transparent);border-radius:999px;justify-content:center;align-items:center;gap:4px;min-width:56px;min-height:32px;padding:6px 8px;display:inline-flex}.relaya-root .chat-image__loading>span{background:var(--relaya-color-text-muted);border-radius:50%;width:6px;height:6px;animation:.9s ease-in-out infinite chat-image-dot-pulse}.relaya-root .chat-image__loading>span:nth-child(2){animation-delay:.12s}.relaya-root .chat-image__loading>span:nth-child(3){animation-delay:.24s}@keyframes chat-image-dot-pulse{0%,80%,to{opacity:.45;transform:scale(.7)}40%{transform:scale(1)}}.relaya-root .chat-image__fallback{border-radius:var(--relaya-radius-sm);border:1px dashed var(--relaya-color-border);min-width:120px;min-height:32px;color:var(--relaya-color-text-muted);font-size:var(--relaya-font-size-xs);background:var(--relaya-color-surface-2);justify-content:center;align-items:center;padding:6px 10px;display:inline-flex}.relaya-root .message-item--own .message-item__bubble{background:var(--relaya-color-message-own-bg);color:var(--sp-own-msg-text);border-bottom-right-radius:var(--relaya-radius-sm)}.relaya-root .message-item--other .message-item__bubble{background:var(--relaya-color-message-bg);color:var(--sp-other-msg-text);border-bottom-left-radius:var(--relaya-radius-sm)}.relaya-root .message-item--deleted .message-item__bubble{background:var(--relaya-color-deleted-bg);color:var(--relaya-color-deleted-text);font-style:italic;font-size:var(--relaya-font-size-sm)}.relaya-root .message-item--optimistic .message-item__bubble{background:var(--relaya-color-optimistic-bg);opacity:.85}.relaya-root .message-item--failed .message-item__bubble{background:var(--relaya-color-failed-bg)}.relaya-root .scroll-to-bottom{bottom:var(--spacing-md);right:var(--spacing-lg);background:var(--relaya-color-accent);color:#fff;width:36px;height:36px;box-shadow:0 2px 8px var(--relaya-color-shadow);z-index:5;border-radius:50%;justify-content:center;align-items:center;font-size:18px;display:flex;position:absolute}.relaya-root .scroll-to-bottom:hover{background:var(--relaya-color-accent-hover)}.relaya-root .message-item__kebab-btn{width:18px;height:18px;color:var(--relaya-color-text-faint);border-radius:var(--relaya-radius-sm);cursor:pointer;background:0 0;flex-shrink:0;justify-content:center;align-items:center;padding:0;font-size:15px;line-height:1;display:none;position:relative}.relaya-root .message-item__kebab-btn:active{color:var(--relaya-color-text-muted);background:#00000014}.relaya-root .message-item__kebab-menu{z-index:200;background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);min-width:140px;box-shadow:0 4px 16px var(--relaya-color-shadow);position:fixed;overflow:hidden}.relaya-root .message-item__kebab-menu__item{align-items:center;gap:var(--spacing-sm);width:100%;padding:10px var(--spacing-md);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;font-family:inherit;display:flex}.relaya-root .message-item__kebab-menu__item:hover,.relaya-root .message-item__kebab-menu__item:active{background:var(--relaya-color-surface-2)}.relaya-root .message-item__kebab-menu__item--danger{color:var(--relaya-color-danger)}.relaya-root .kebab-overlay{z-index:199;position:fixed;inset:0}.relaya-root .connection-status{justify-content:center;align-items:center;gap:var(--spacing-sm);padding:var(--spacing-xs) var(--spacing-md);font-size:var(--relaya-font-size-xs);flex-shrink:0;font-weight:500;display:flex}.relaya-root .connection-status--connecting,.relaya-root .connection-status--reconnecting{background:var(--relaya-color-warning);color:#1a1a2e}.relaya-root .connection-status--disconnected{background:var(--relaya-color-danger);color:#fff}.relaya-root .connection-status--connected{display:none}.relaya-root .connection-spinner{border:2px solid;border-top-color:#0000;border-radius:50%;width:12px;height:12px;animation:.8s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.relaya-root .relaya-branding{text-align:center;color:var(--relaya-color-text);border-top:1px solid var(--relaya-color-border,#ffffff14);flex-shrink:0;padding:4px 8px;font-size:11px}.relaya-root .relaya-branding a{color:inherit;text-decoration:none;transition:opacity .15s}.relaya-root .relaya-branding a:hover{opacity:.75;text-decoration:underline}.relaya-root .message-input-bar{align-items:flex-end;gap:var(--spacing-sm);padding:var(--spacing-sm) var(--spacing-lg);border-top:1px solid var(--relaya-color-border);background:var(--relaya-color-surface);flex-shrink:0;display:flex;position:relative}.relaya-root .sticker-btn{border:1px solid var(--relaya-color-border);background:var(--relaya-color-surface-2);width:36px;height:36px;color:var(--relaya-color-text-muted);border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;transition:background-color .15s,color .15s;display:flex}.relaya-root .sticker-btn:hover:not(:disabled){background:var(--relaya-color-surface);color:var(--relaya-color-text)}.relaya-root .sticker-btn:disabled{opacity:.45;cursor:not-allowed}.relaya-root .message-input{min-height:36px;max-height:120px;padding:var(--spacing-sm) var(--spacing-md);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-bubble);background:var(--relaya-color-input-bg);color:var(--relaya-color-input-text);font-size:var(--relaya-font-size-base);font-family:var(--sp-msg-font);resize:none;flex:1;line-height:1.4;transition:border-color .15s;overflow-y:auto}.relaya-root .message-input:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .message-input::placeholder{color:var(--relaya-color-text-faint)}.relaya-root .message-input:disabled{opacity:.55}.relaya-root .send-btn{background:var(--relaya-color-btn-bg);width:36px;height:36px;color:var(--relaya-color-btn-text);border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;transition:opacity .15s;display:flex}.relaya-root .send-btn:hover:not(:disabled){opacity:.85}.relaya-root .send-btn:disabled{opacity:.4;cursor:not-allowed}.relaya-root .sticker-picker__overlay{z-index:170;background:#00000059;position:fixed;inset:0}.relaya-root .sticker-picker{left:50%;bottom:calc(var(--input-height) + var(--spacing-md));border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-lg);background:var(--relaya-color-surface);width:min(92vw,920px);max-height:min(70vh,620px);box-shadow:0 10px 32px var(--relaya-color-shadow);z-index:171;flex-direction:column;display:flex;position:fixed;transform:translate(-50%)}.relaya-root .sticker-picker__header{padding:var(--spacing-sm) var(--spacing-md);border-bottom:1px solid var(--relaya-color-border);font-size:var(--relaya-font-size-sm);justify-content:space-between;align-items:center;font-weight:600;display:flex}.relaya-root .sticker-picker__close{min-width:28px;min-height:28px}.relaya-root .sticker-picker__body{padding:var(--spacing-md);overflow-y:auto}.relaya-root .sticker-picker__state{color:var(--relaya-color-text-muted);font-size:var(--relaya-font-size-sm);padding:var(--spacing-lg) var(--spacing-sm)}.relaya-root .sticker-picker__grid{gap:var(--spacing-sm);grid-template-columns:repeat(auto-fill,minmax(128px,1fr));display:grid}.relaya-root .sticker-picker__item{aspect-ratio:1;border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);background:var(--relaya-color-surface-2);justify-content:center;align-items:center;gap:var(--spacing-xs);width:100%;padding:var(--spacing-xs);flex-direction:column;display:flex;overflow:hidden}.relaya-root .sticker-picker__item:hover{border-color:var(--relaya-color-accent);background:var(--relaya-color-surface)}.relaya-root .sticker-picker__item img{object-fit:contain;width:auto;max-width:100%;height:auto;max-height:calc(100% - 20px)}.relaya-root .sticker-picker__item-label{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);line-height:1.2}.relaya-root .sticker-suggestions{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);box-shadow:0 8px 24px var(--relaya-color-shadow);z-index:175;flex-direction:column;max-height:220px;display:flex;position:absolute;bottom:calc(100% + 8px);left:56px;right:52px;overflow-y:auto}.relaya-root .sticker-suggestions__item{align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);text-align:left;display:flex}.relaya-root .sticker-suggestions__item:hover{background:var(--relaya-color-surface-2)}.relaya-root .sticker-suggestions__item img{object-fit:contain;flex-shrink:0;width:32px;height:32px}.relaya-root .sticker-suggestions__code{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text)}.relaya-root .mention-suggestions{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);box-shadow:0 8px 24px var(--relaya-color-shadow);z-index:175;flex-direction:column;min-width:120px;max-height:200px;display:flex;position:absolute;bottom:calc(100% + 8px);left:56px;overflow-y:auto}.relaya-root .mention-suggestions__item{align-items:center;gap:var(--spacing-xs);width:100%;padding:var(--spacing-sm) var(--spacing-md);text-align:left;font-size:var(--relaya-font-size-sm);display:flex}.relaya-root .mention-suggestions__item:hover,.relaya-root .mention-suggestions__item--highlighted{background:var(--relaya-color-surface-2)}.relaya-root .mention-suggestions__at{color:var(--relaya-color-accent);flex-shrink:0;font-weight:700}.relaya-root .mention-suggestions__name{color:var(--relaya-color-text);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.relaya-root .user-list{width:var(--sidebar-width);border-left:1px solid var(--relaya-color-border);background:var(--relaya-color-surface-2);padding:var(--spacing-md);gap:var(--spacing-sm);flex-direction:column;flex-shrink:0;display:none;overflow-y:auto}.relaya-root .user-list__title{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);text-transform:uppercase;letter-spacing:.05em;padding-bottom:var(--spacing-xs);border-bottom:1px solid var(--relaya-color-border);font-weight:600}.relaya-root .user-list__item{align-items:center;gap:var(--spacing-sm);padding:var(--spacing-xs);border-radius:var(--relaya-radius-sm);font-size:var(--relaya-font-size-sm);display:flex}.relaya-root .user-list__avatar{background:var(--sp-avatar-bg);width:24px;height:24px;color:var(--sp-avatar-text);font-size:var(--relaya-font-size-xs);border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-weight:600;display:flex}.relaya-root .user-list__name{white-space:nowrap;text-overflow:ellipsis;min-width:0;color:var(--relaya-color-text);flex:1;overflow:hidden}.relaya-root .user-list__self-badge{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint)}.relaya-root .pane-divider{cursor:col-resize;background:var(--relaya-color-border);flex-shrink:0;width:5px;transition:background .15s;display:none}.relaya-root .pane-divider:hover{background:var(--relaya-color-text-faint)}.relaya-root .user-list-modal__header{justify-content:space-between;align-items:center;display:flex}.relaya-root .user-list-modal__body{max-height:50vh;padding-right:var(--spacing-xs);margin-bottom:0;overflow-y:auto}.relaya-root .user-list-modal__item{padding:var(--spacing-xs) 0}.relaya-root .user-list-modal__close{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);padding:var(--spacing-xs)}.relaya-root .user-list-modal__close:hover{color:var(--relaya-color-text);background:var(--relaya-color-surface-2);border-radius:var(--relaya-radius-sm)}.relaya-root .admin-panel{background:var(--relaya-color-bg);height:100%;overflow-y:auto}.relaya-root .admin-panel__header{padding:var(--spacing-xl) var(--spacing-xl) var(--spacing-lg);border-bottom:1px solid var(--relaya-color-border);background:var(--relaya-color-surface)}.relaya-root .admin-panel__title{color:var(--relaya-color-text);margin:0;font-size:22px;font-weight:600}.relaya-root .admin-panel__subtitle{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin:var(--spacing-xs) 0 0}.relaya-root .admin-panel__loading,.relaya-root .admin-panel__unauthorized{justify-content:center;align-items:center;gap:var(--spacing-md);height:100%;color:var(--relaya-color-text-muted);font-size:var(--relaya-font-size-sm);text-align:center;padding:var(--spacing-xl);flex-direction:column;display:flex}.relaya-root .admin-settings{border-top:1px solid var(--relaya-color-border);background:var(--relaya-color-surface-2);padding:var(--spacing-md) var(--spacing-lg)}.relaya-root .admin-settings__toggle{align-items:center;gap:var(--spacing-sm);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);width:100%;padding:0;font-weight:500;display:flex}.relaya-root .admin-settings__toggle:hover{color:var(--relaya-color-text)}.relaya-root .admin-settings__panel{margin-top:var(--spacing-md)}.relaya-root .admin-settings__section{justify-content:space-between;align-items:center;gap:var(--spacing-md);margin-bottom:var(--spacing-md);display:flex}.relaya-root .admin-settings__section-copy{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);flex-direction:column;gap:2px;display:flex}.relaya-root .admin-settings__section-copy strong{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text)}.relaya-root .admin-settings__notice{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text);background:color-mix(in srgb, var(--relaya-color-warning) 15%, transparent);border-left:3px solid var(--relaya-color-warning);padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--relaya-radius-sm);margin-bottom:var(--spacing-md);line-height:1.4}.relaya-root .admin-settings__grid{gap:var(--spacing-md);margin-bottom:var(--spacing-md);grid-template-columns:repeat(auto-fit,minmax(160px,1fr));display:grid}.relaya-root .admin-settings__field label{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);margin-bottom:var(--spacing-xs);font-weight:500;display:block}.relaya-root .admin-settings__field input{width:100%;padding:var(--spacing-xs) var(--spacing-sm);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-surface);color:var(--relaya-color-text);font-size:var(--relaya-font-size-sm)}.relaya-root .sticker-admin-page{background:var(--relaya-color-bg);flex-direction:column;gap:6px;min-height:100%;padding:8px;display:flex;overflow-y:auto}.relaya-root .sticker-admin-page__topbar{justify-content:space-between;align-items:flex-start;gap:8px;display:flex}.relaya-root .sticker-admin-page__title-group{flex-direction:column;gap:2px;min-width:0;display:flex}.relaya-root .sticker-admin-page__title-row{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.relaya-root .sticker-admin-page__header{justify-content:space-between;align-items:flex-start;gap:var(--spacing-md);display:flex}.relaya-root .sticker-admin-page__back{margin-bottom:0;font-size:12px;padding:3px 8px!important}.relaya-root .sticker-admin-page__title{font-size:18px;line-height:1.1}.relaya-root .sticker-admin-page__subtitle{color:var(--relaya-color-text-muted);margin-top:0;font-size:12px}.relaya-root .sticker-admin-page__status-inline{background:color-mix(in srgb, var(--relaya-color-success) 10%, transparent);min-height:18px;color:var(--relaya-color-success);border-radius:999px;align-items:center;gap:4px;width:fit-content;max-width:100%;padding:1px 8px;font-size:11px;line-height:1.2;display:inline-flex}.relaya-root .sticker-admin-page__header-actions{flex-shrink:0;gap:6px;display:flex}.relaya-root .sticker-admin-page__header-actions .btn{min-height:32px;padding:3px 10px;font-size:12px}.relaya-root .sticker-admin-page__notice,.relaya-root .sticker-admin-page__error,.relaya-root .sticker-admin-page__success,.relaya-root .sticker-admin-page__state{border-radius:var(--relaya-radius-md);padding:6px 10px;font-size:12px}.relaya-root .sticker-admin-page__notice{background:color-mix(in srgb, var(--relaya-color-accent) 10%, transparent);color:var(--relaya-color-text)}.relaya-root .sticker-admin-page__error{background:color-mix(in srgb, var(--relaya-color-danger) 10%, transparent);color:var(--relaya-color-danger)}.relaya-root .sticker-admin-page__success{background:color-mix(in srgb, var(--relaya-color-success) 10%, transparent);color:var(--relaya-color-success)}.relaya-root .sticker-admin-page__toolbar{justify-content:space-between;align-items:center;gap:var(--spacing-sm);display:flex}.relaya-root .sticker-admin-page__count{color:var(--relaya-color-text-muted);font-size:12px;line-height:1}.relaya-root .sticker-admin-grid{grid-template-columns:repeat(auto-fill,minmax(116px,1fr));gap:6px;display:grid}.relaya-root .sticker-admin-card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:10px;flex-direction:column;transition:border-color .15s,box-shadow .15s,opacity .15s,transform .15s;display:flex;overflow:hidden}.relaya-root .sticker-admin-card--dragging{opacity:.45;transform:scale(.98)}.relaya-root .sticker-admin-card--drop-target{border-color:var(--relaya-color-accent);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--relaya-color-accent) 65%, transparent)}.relaya-root .sticker-admin-card__preview{background:var(--relaya-color-surface-2);flex-direction:column;justify-content:center;align-items:center;gap:4px;min-height:122px;padding:4px;display:flex}.relaya-root .sticker-admin-card__preview--draggable{cursor:grab;-webkit-user-select:none;user-select:none;position:relative}.relaya-root .sticker-admin-card__preview--draggable:active{cursor:grabbing}.relaya-root .sticker-admin-card__preview img{object-fit:contain;width:96px;max-width:96px;height:auto;max-height:96px}.relaya-root .sticker-admin-card__drag-badge{background:color-mix(in srgb, var(--relaya-color-surface) 92%, transparent);border:1px solid color-mix(in srgb, var(--relaya-color-border) 90%, transparent);min-height:20px;color:var(--relaya-color-text-muted);pointer-events:none;border-radius:999px;justify-content:center;align-items:center;gap:4px;padding:4px 8px;font-size:9px;line-height:1;display:inline-flex;position:static}.relaya-root .sticker-admin-card__drag-badge svg{width:10px;height:10px;display:block}.relaya-root .sticker-admin-card__body{flex-direction:column;gap:4px;padding:4px;display:flex}.relaya-root .sticker-admin-card__filename{color:var(--relaya-color-text);text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:600;overflow:hidden}.relaya-root .sticker-admin-card__field{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);flex-direction:column;gap:0;display:flex}.relaya-root .sticker-admin-card__shortcode-input{border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-surface-2);align-items:center;gap:4px;padding:0 4px;display:flex}.relaya-root .sticker-admin-card__shortcode-input input{background:0 0;border:none;flex:1;min-width:0;padding:4px 0;font-size:11px}.relaya-root .sticker-admin-card__shortcode-input input:focus{outline:none}.relaya-root .sticker-admin-card__meta{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint);flex-direction:column;gap:2px;display:flex}.relaya-root .sticker-admin-card__actions{flex-wrap:wrap;gap:3px;display:flex}.relaya-root .sticker-admin-card__actions .btn{min-height:20px;padding:2px 6px;font-size:10px;line-height:1}.relaya-root .report-review{border-top:1px solid var(--relaya-color-border);background:var(--relaya-color-surface-2);padding:var(--spacing-md) var(--spacing-lg)}.relaya-root .report-review__toggle{align-items:center;gap:var(--spacing-sm);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);width:100%;padding:0;font-weight:500;display:flex}.relaya-root .report-review__toggle:hover{color:var(--relaya-color-text)}.relaya-root .report-review__badge{min-width:18px;height:18px;margin-left:var(--spacing-sm);background:var(--relaya-color-danger);color:#fff;font-size:var(--relaya-font-size-xs);border-radius:9px;justify-content:center;align-items:center;padding:0 5px;font-weight:600;line-height:1;display:inline-flex}.relaya-root .report-review__panel{margin-top:var(--spacing-md);gap:var(--spacing-sm);flex-direction:column;max-height:360px;display:flex;overflow-y:auto}.relaya-root .report-review__loading,.relaya-root .report-review__empty{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);padding:var(--spacing-sm) 0}.relaya-root .report-review__error{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-danger);padding:var(--spacing-sm) 0}.relaya-root .report-card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);padding:var(--spacing-sm) var(--spacing-md);gap:var(--spacing-xs);flex-direction:column;display:flex}.relaya-root .report-card__meta{justify-content:space-between;align-items:center;gap:var(--spacing-sm);display:flex}.relaya-root .report-card__reason{font-size:var(--relaya-font-size-xs);text-transform:capitalize;color:var(--relaya-color-text-muted);background:color-mix(in srgb, var(--relaya-color-warning) 15%, transparent);border-radius:var(--relaya-radius-sm);padding:1px 6px;font-weight:600}.relaya-root .report-card__time{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint);flex-shrink:0}.relaya-root .report-card__message{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);word-break:break-word;padding:var(--spacing-xs) 0;border-top:1px solid var(--relaya-color-border);border-bottom:1px solid var(--relaya-color-border);line-height:1.4}.relaya-root .report-card__deleted{color:var(--relaya-color-text-muted)}.relaya-root .report-card__details{gap:var(--spacing-sm) var(--spacing-lg);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);flex-wrap:wrap;display:flex}.relaya-root .report-card__details strong{color:var(--relaya-color-text)}.relaya-root .report-card__actions{gap:var(--spacing-xs);padding-top:var(--spacing-xs);flex-wrap:wrap;display:flex}.relaya-root .report-review__pagination{justify-content:center;align-items:center;gap:var(--spacing-md);padding-top:var(--spacing-xs);display:flex}.relaya-root .report-review__page-info{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted)}.relaya-root .ban-management{border-top:1px solid var(--relaya-color-border);background:var(--relaya-color-surface-2);padding:var(--spacing-md) var(--spacing-lg)}.relaya-root .ban-management__toggle{align-items:center;gap:var(--spacing-sm);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);width:100%;padding:0;font-weight:500;display:flex}.relaya-root .ban-management__toggle:hover{color:var(--relaya-color-text)}.relaya-root .ban-management__badge{min-width:18px;height:18px;margin-left:var(--spacing-sm);background:var(--relaya-color-warning);color:#1a1a2e;font-size:var(--relaya-font-size-xs);border-radius:9px;justify-content:center;align-items:center;padding:0 5px;font-weight:600;line-height:1;display:inline-flex}.relaya-root .ban-management__panel{margin-top:var(--spacing-md);gap:var(--spacing-sm);flex-direction:column;max-height:320px;display:flex;overflow-y:auto}.relaya-root .ban-management__loading,.relaya-root .ban-management__empty{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);padding:var(--spacing-sm) 0}.relaya-root .ban-management__error{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-danger);padding:var(--spacing-sm) 0}.relaya-root .ban-card{background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);padding:var(--spacing-sm) var(--spacing-md);gap:var(--spacing-xs);flex-direction:column;display:flex}.relaya-root .ban-card__header{justify-content:space-between;align-items:center;gap:var(--spacing-sm);display:flex}.relaya-root .ban-card__user{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);font-weight:600}.relaya-root .ban-card__expiry{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);flex-shrink:0}.relaya-root .ban-card__expiry--permanent{color:var(--relaya-color-danger);font-weight:600}.relaya-root .ban-card__details{gap:var(--spacing-xs) var(--spacing-lg);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);flex-wrap:wrap;display:flex}.relaya-root .ban-card__details strong{color:var(--relaya-color-text)}.relaya-root .ban-card__actions{padding-top:var(--spacing-xs)}.relaya-root .theme-admin-page{min-height:100%;padding:var(--spacing-lg);gap:var(--spacing-md);flex-direction:column;display:flex;overflow-y:auto}.relaya-root .theme-admin-page__topbar{justify-content:space-between;align-items:flex-start;gap:var(--spacing-md);flex-wrap:wrap;display:flex}.relaya-root .theme-admin-page__title-group{flex:1;min-width:0}.relaya-root .theme-admin-page__title-row{align-items:center;gap:var(--spacing-sm);flex-wrap:wrap;display:flex}.relaya-root .theme-admin-page__back{flex-shrink:0;margin-bottom:0}.relaya-root .theme-admin-page__title{color:var(--relaya-color-text);margin:0;font-size:18px;font-weight:600}.relaya-root .theme-admin-page__subtitle{margin-top:var(--spacing-xs);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin-bottom:0}.relaya-root .theme-admin-page__status-inline{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-success);background:color-mix(in srgb, var(--relaya-color-success) 12%, transparent);border-radius:var(--relaya-radius-sm);align-items:center;padding:2px 8px;display:inline-flex}.relaya-root .theme-admin-page__header-actions{gap:var(--spacing-xs);flex-shrink:0;align-items:center;display:flex}.relaya-root .theme-admin-page__header-actions .btn{font-size:var(--relaya-font-size-sm);padding:3px 10px}.relaya-root .theme-admin-page__error,.relaya-root .theme-admin-page__state{border-radius:var(--relaya-radius-md);padding:var(--spacing-sm) var(--spacing-md);font-size:var(--relaya-font-size-sm)}.relaya-root .theme-admin-page__error{background:color-mix(in srgb, var(--relaya-color-danger) 10%, transparent);color:var(--relaya-color-danger)}.relaya-root .theme-admin-page__state{color:var(--relaya-color-text-muted);background:var(--relaya-color-surface)}.relaya-root .theme-admin-list{border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);flex-direction:column;gap:0;display:flex;overflow:hidden}.relaya-root .theme-admin-row{align-items:center;gap:var(--spacing-sm);padding:8px var(--spacing-sm);background:var(--relaya-color-surface);border-bottom:1px solid var(--relaya-color-border);grid-template-columns:36px 1fr 90px;transition:background .1s;display:grid}.relaya-root .theme-admin-row:last-child{border-bottom:none}.relaya-root .theme-admin-row:hover{background:color-mix(in srgb, var(--relaya-color-accent) 4%, var(--relaya-color-surface))}.relaya-root .theme-admin-row__swatch{border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);cursor:pointer;background:0 0;flex-shrink:0;width:32px;height:28px;padding:0}.relaya-root .theme-admin-row__label{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);white-space:nowrap;text-overflow:ellipsis;cursor:default;overflow:hidden}.relaya-root .theme-admin-row__hex{font-size:11px;font-family:var(--relaya-font-mono,monospace);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-bg);color:var(--relaya-color-text);width:100%;padding:3px 6px}.relaya-root .theme-admin-row__hex--invalid{border-color:var(--relaya-color-danger);color:var(--relaya-color-danger)}.relaya-root .theme-admin-row__hex:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .theme-admin-page__tabs{border-bottom:1px solid var(--relaya-color-border);margin-bottom:var(--spacing-xs);gap:2px;display:flex}.relaya-root .theme-admin-page__tab{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-1px;padding:6px 16px;transition:color .1s,border-color .1s}.relaya-root .theme-admin-page__tab:hover{color:var(--relaya-color-text)}.relaya-root .theme-admin-page__tab--active{color:var(--relaya-color-accent);border-bottom-color:var(--relaya-color-accent);font-weight:500}.relaya-root .geo-admin{gap:var(--spacing-lg);padding:var(--spacing-sm) 0;flex-direction:column;display:flex}.relaya-root .geo-admin__status{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);padding:var(--spacing-sm) 0}.relaya-root .geo-admin__status--unavailable{background:color-mix(in srgb, var(--relaya-color-warning) 10%, transparent);border-left:3px solid var(--relaya-color-warning);padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--relaya-radius-sm);color:var(--relaya-color-text)}.relaya-root .geo-admin__status--error{color:var(--relaya-color-danger)}.relaya-root .geo-admin__action-error{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-danger);background:color-mix(in srgb, var(--relaya-color-danger) 8%, transparent);border-radius:var(--relaya-radius-sm);padding:var(--spacing-xs) var(--spacing-sm)}.relaya-root .geo-admin__section{gap:var(--spacing-sm);flex-direction:column;display:flex}.relaya-root .geo-admin__section-title{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);align-items:center;gap:var(--spacing-sm);border-bottom:1px solid var(--relaya-color-border);padding-bottom:var(--spacing-xs);margin:0;font-weight:600;display:flex}.relaya-root .geo-admin__badge{background:var(--relaya-color-warning);color:#1a1a2e;min-width:18px;height:18px;font-size:var(--relaya-font-size-xs);border-radius:9px;justify-content:center;align-items:center;padding:0 5px;font-weight:600;line-height:1;display:inline-flex}.relaya-root .geo-admin__field{gap:var(--spacing-xs);flex-direction:column;display:flex}.relaya-root .geo-admin__label{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);font-weight:500}.relaya-root .geo-admin__select,.relaya-root .geo-admin__input,.relaya-root .geo-admin__filter-input,.relaya-root .geo-admin__combobox-input{padding:var(--spacing-xs) var(--spacing-sm);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-surface);color:var(--relaya-color-text);font-size:var(--relaya-font-size-sm);width:100%}.relaya-root .geo-admin__select:focus,.relaya-root .geo-admin__input:focus,.relaya-root .geo-admin__filter-input:focus,.relaya-root .geo-admin__combobox-input:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .geo-admin__hint{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);margin:0}.relaya-root .geo-admin__hint--empty{font-style:italic}.relaya-root .geo-admin__chips{flex-wrap:wrap;gap:6px;display:flex}.relaya-root .geo-admin__chip{background:color-mix(in srgb, var(--relaya-color-accent) 12%, var(--relaya-color-surface));border:1px solid color-mix(in srgb, var(--relaya-color-accent) 30%, var(--relaya-color-border));font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text);border-radius:999px;align-items:center;gap:5px;padding:3px 8px 3px 10px;line-height:1;display:inline-flex}.relaya-root .geo-admin__chip-remove{width:16px;height:16px;color:var(--relaya-color-text-muted);cursor:pointer;background:0 0;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;padding:0;font-size:14px;line-height:1;transition:color .1s,background .1s;display:inline-flex}.relaya-root .geo-admin__chip-remove:hover{color:var(--relaya-color-danger);background:color-mix(in srgb, var(--relaya-color-danger) 12%, transparent)}.relaya-root .geo-admin__combobox{position:relative}.relaya-root .geo-admin__combobox-list{z-index:100;background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);max-height:220px;margin:0;padding:2px 0;list-style:none;position:absolute;top:calc(100% + 2px);left:0;right:0;overflow-y:auto;box-shadow:0 4px 12px #00000026}.relaya-root .geo-admin__combobox-item{width:100%;padding:6px var(--spacing-sm);text-align:left;font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:none;display:block;overflow:hidden}.relaya-root .geo-admin__combobox-item:hover{background:color-mix(in srgb, var(--relaya-color-accent) 10%, transparent);color:var(--relaya-color-accent)}.relaya-root .geo-admin__combobox-empty{padding:8px var(--spacing-sm);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);font-style:italic}.relaya-root .geo-admin__ban-list{flex-direction:column;gap:4px;max-height:300px;display:flex;overflow-y:auto}.relaya-root .geo-admin__ban-row{justify-content:space-between;align-items:center;gap:var(--spacing-sm);background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);padding:6px var(--spacing-sm);display:flex}.relaya-root .geo-admin__ban-info{align-items:baseline;gap:4px var(--spacing-sm);flex-wrap:wrap;min-width:0;display:flex}.relaya-root .geo-admin__ban-ip{font-family:var(--relaya-font-mono,monospace);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text);font-weight:600}.relaya-root .geo-admin__ban-reason{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted)}.relaya-root .geo-admin__ban-expiry{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-faint);flex-shrink:0}.relaya-root .geo-admin__lift-btn{flex-shrink:0;min-height:22px;padding:2px 8px;font-size:11px}.relaya-root .geo-admin__add-toggle{align-items:center;gap:var(--spacing-xs);font-size:var(--relaya-font-size-xs);color:var(--relaya-color-accent);cursor:pointer;margin-top:var(--spacing-xs);background:0 0;border:none;padding:0;font-weight:500;display:inline-flex}.relaya-root .geo-admin__add-toggle:hover{text-decoration:underline}.relaya-root .geo-admin__add-ban-form{gap:var(--spacing-sm);padding:var(--spacing-sm);background:var(--relaya-color-surface);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);margin-top:var(--spacing-xs);flex-direction:column;display:flex}.relaya-root .export-admin-page{padding:var(--spacing-sm) 0}.relaya-root .export-admin-page__notice{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);margin:0 0 var(--spacing-md)}.relaya-root .export-admin-page__unavailable{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-muted);background:color-mix(in srgb, var(--relaya-color-warning) 10%, transparent);border-left:3px solid var(--relaya-color-warning);padding:var(--spacing-sm) var(--spacing-md);border-radius:var(--relaya-radius-sm);margin:0}.relaya-root .export-admin-page__error{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-danger);background:color-mix(in srgb, var(--relaya-color-danger) 8%, transparent);border-radius:var(--relaya-radius-sm);padding:var(--spacing-xs) var(--spacing-sm);margin-bottom:var(--spacing-md)}.relaya-root .export-admin-page__form{gap:var(--spacing-md);flex-direction:column;display:flex}.relaya-root .export-admin-page__date-row{gap:var(--spacing-md);flex-wrap:wrap;display:flex}.relaya-root .export-admin-page__exclude-row{gap:var(--spacing-xs);flex-direction:column;display:flex}.relaya-root .export-admin-page__label{align-items:center;gap:var(--spacing-sm);font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);font-weight:500;display:flex}.relaya-root .export-admin-page__date-input{padding:var(--spacing-xs) var(--spacing-sm);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);background:var(--relaya-color-surface);color:var(--relaya-color-text);font-size:var(--relaya-font-size-sm)}.relaya-root .export-admin-page__date-input:focus{border-color:var(--relaya-color-accent);outline:none}.relaya-root .export-admin-page__field-error{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-danger);margin:0}.relaya-root .export-admin-page__btn{align-self:stretch}.relaya-root .export-admin-page__hint{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-muted);margin:0}.relaya-root .moderator-admin-page{flex-direction:column;gap:0;display:flex}.relaya-root .moderator-admin-page__topbar{padding:var(--spacing-sm) var(--spacing-md);border-bottom:1px solid var(--relaya-color-border);margin-bottom:var(--spacing-sm)}.relaya-root .moderator-admin-page__title-row{align-items:baseline;gap:var(--spacing-sm);flex-wrap:wrap;display:flex}.relaya-root .moderator-admin-page__title{color:var(--relaya-color-text);margin:0;font-size:16px;font-weight:600}.relaya-root .moderator-admin-page__section{padding:var(--spacing-sm) var(--spacing-md)}.relaya-root .moderator-admin-page__section+.moderator-admin-page__section{margin-top:var(--spacing-xs)}.relaya-root .moderator-admin-page__section-title{color:var(--relaya-color-text-muted);text-transform:uppercase;letter-spacing:.07em;margin:0 0 var(--spacing-xs) 0;font-size:11px;font-weight:600}.relaya-root .moderator-admin-page__divider{border:none;border-top:1px solid var(--relaya-color-border);margin:0 0 var(--spacing-sm) 0}.relaya-root .moderator-admin-page__filter{box-sizing:border-box;width:100%;padding:5px var(--spacing-sm);color:var(--relaya-color-text);background:var(--relaya-color-bg);border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-sm);margin-bottom:var(--spacing-sm);outline:none;font-size:11px;transition:border-color .15s;display:block}.relaya-root .moderator-admin-page__filter::placeholder{color:var(--relaya-color-text-faint)}.relaya-root .moderator-admin-page__filter:focus{border-color:var(--relaya-color-accent)}.relaya-root .moderator-admin-page__list{border:1px solid var(--relaya-color-border);border-radius:var(--relaya-radius-md);margin:0;padding:0;list-style:none;overflow:hidden}.relaya-root .moderator-admin-page__item{justify-content:space-between;align-items:center;gap:var(--spacing-sm);padding:2px var(--spacing-sm);background:var(--relaya-color-surface);border-bottom:1px solid var(--relaya-color-border);transition:background .1s;display:flex}.relaya-root .moderator-admin-page__item:last-child{border-bottom:none}.relaya-root .moderator-admin-page__item:hover{background:color-mix(in srgb, var(--relaya-color-accent) 4%, var(--relaya-color-surface))}.relaya-root .moderator-admin-page__member-label{color:var(--relaya-color-text);text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:11px;font-weight:500;overflow:hidden}.relaya-root .moderator-admin-page__list-hint{color:var(--relaya-color-text-muted);margin:0 0 var(--spacing-xs) 0;font-size:10px}.relaya-root .moderator-admin-page__item .btn{flex-shrink:0;min-height:22px;padding:2px 8px;font-size:11px}.relaya-root .moderator-admin-page__demote-link{color:var(--relaya-color-text-muted);cursor:pointer;background:0 0;border:none;flex-shrink:0;padding:0;font-size:11px;font-weight:500;text-decoration:none}.relaya-root .moderator-admin-page__demote-link:hover:not(:disabled){color:var(--relaya-color-danger);text-decoration:underline}.relaya-root .moderator-admin-page__demote-link:disabled{opacity:.4;cursor:default}.relaya-root .moderator-admin-page__promote-link{color:var(--relaya-color-accent);cursor:pointer;background:0 0;border:none;flex-shrink:0;padding:0;font-size:11px;font-weight:500;text-decoration:none}.relaya-root .moderator-admin-page__promote-link:hover:not(:disabled){text-decoration:underline}.relaya-root .moderator-admin-page__promote-link:disabled{opacity:.4;cursor:default}.relaya-root .message-item__edit-form{background:var(--relaya-color-surface-2);border-radius:8px;flex-direction:column;gap:8px;padding:8px;display:flex}.relaya-root .message-item__edit-textarea{border:1px solid var(--relaya-color-border);background:var(--relaya-color-surface);width:100%;min-height:60px;color:var(--relaya-color-text);resize:vertical;border-radius:4px;padding:8px;font-family:inherit;font-size:14px;line-height:1.4}.relaya-root .message-item__edit-textarea:focus{outline:2px solid var(--relaya-color-accent);outline-offset:1px}.relaya-root .message-item__edit-error{color:var(--relaya-color-error,#e74c3c);padding:4px 0;font-size:12px}.relaya-root .message-item__edit-actions{justify-content:flex-end;gap:8px;display:flex}.relaya-root .message-item__edit-actions .btn{min-width:60px}.relaya-root .message-input-container{flex-direction:column;display:flex}.relaya-root .reply-preview{padding:var(--spacing-sm) var(--spacing-md);background:var(--relaya-color-surface-2,#f5f5f5);border-top:1px solid var(--relaya-color-border);justify-content:space-between;align-items:center;gap:var(--spacing-sm);display:flex}.relaya-root .reply-preview__content{align-items:center;gap:var(--spacing-sm);flex:1;min-width:0;display:flex}.relaya-root .reply-preview__line{background:var(--relaya-color-accent,#3b82f6);border-radius:2px;flex-shrink:0;width:3px;height:36px}.relaya-root .reply-preview__text{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.relaya-root .reply-preview__author{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text);font-weight:600}.relaya-root .reply-preview__excerpt{font-size:var(--relaya-font-size-sm);color:var(--relaya-color-text-secondary,#6b7280);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.relaya-root .reply-preview__cancel{padding:var(--spacing-xs);cursor:pointer;color:var(--relaya-color-text-secondary,#6b7280);border-radius:var(--relaya-radius-sm);background:0 0;border:none;flex-shrink:0;font-size:18px;line-height:1;transition:background .15s}.relaya-root .reply-preview__cancel:hover{color:var(--relaya-color-text);background:#0000000d}.relaya-root .message-reply-bubble{align-items:stretch;gap:var(--spacing-sm);margin-bottom:var(--spacing-sm);background:var(--reply-bubble-bg,#0000000f);border-radius:var(--relaya-radius-sm);border-left:3px solid var(--relaya-color-accent,#3b82f6);padding:6px 8px;display:flex}.relaya-root .message-reply-bubble__text{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.relaya-root .message-reply-bubble__author{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-accent,#3b82f6);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.relaya-root .message-reply-bubble__excerpt{font-size:var(--relaya-font-size-xs);color:var(--relaya-color-text-secondary,#6b7280);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@media (prefers-color-scheme:dark){.relaya-root .message-reply-bubble{--reply-bubble-bg:#ffffff14}}.relaya-root .message-item--own .message-reply-bubble{--reply-bubble-bg:#fff3;border-left-color:#ffffffb3}.relaya-root .message-item--own .message-reply-bubble__author{color:#fff}.relaya-root .message-item--own .message-reply-bubble__excerpt{color:#ffffffbf}.relaya-root .mute-toggle{cursor:pointer;color:#fff;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;min-width:24px;min-height:24px;margin-left:auto;margin-right:-10px;padding:4px 6px;transition:opacity .2s;display:flex}.relaya-root .mute-toggle:hover{opacity:.8}.relaya-root .mute-toggle:focus{outline:2px solid var(--primary-color,#007bff);outline-offset:2px;border-radius:4px}.relaya-root .mute-toggle:focus:not(:focus-visible){outline:none}.relaya-root .mute-toggle:active{opacity:.6}.relaya-root .mute-toggle__icon{width:18px;height:18px;display:block}.relaya-root .sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width>=768px){.relaya-root .user-list{display:flex}.relaya-root .pane-divider{display:block}}@media (width<=479px){.relaya-root .chat-header__signout{display:none}.relaya-root .chat-name-btn{max-width:80px}}@media (width<=599px){.relaya-root .message-item{max-width:95%}.relaya-root .chat-header{padding:0 var(--spacing-sm);gap:var(--spacing-xs)}.relaya-root .message-list,.relaya-root .message-input-bar{padding:var(--spacing-sm) var(--spacing-md)}.relaya-root .send-btn,.relaya-root .sticker-btn{width:44px;height:44px}.relaya-root .sticker-picker{width:min(96vw,560px);bottom:calc(var(--input-height) + var(--spacing-sm));max-height:62vh}.relaya-root .sticker-suggestions{left:var(--spacing-md);right:var(--spacing-md);bottom:calc(100% + 6px)}.relaya-root .sticker-picker__grid{grid-template-columns:repeat(auto-fill,minmax(96px,1fr))}.relaya-root .btn--icon{min-width:40px;min-height:40px}.relaya-root .message-item__kebab-btn{display:inline-flex}.relaya-root .sticker-admin-page{padding:var(--spacing-md)}.relaya-root .sticker-admin-page__topbar,.relaya-root .admin-settings__section{flex-direction:column;align-items:stretch}.relaya-root .sticker-admin-page__header-actions{width:100%}.relaya-root .sticker-admin-page__header-actions .btn{flex:1}.relaya-root .auth-modal{padding:0}.relaya-root .auth-modal__content{width:100%;max-width:none;height:100%;padding:var(--spacing-xl) var(--spacing-lg);border-radius:0;justify-content:center}.relaya-root .auth-modal__buttons{flex-direction:column-reverse}.relaya-root .auth-modal__buttons .btn{width:100%}}.relaya-root *,.relaya-root :before,.relaya-root :after{box-sizing:border-box;margin:0;padding:0}.relaya-root *{font-family:var(--sp-ui-font);-webkit-font-smoothing:antialiased}.relaya-root button{cursor:pointer;font-family:inherit;font-size:inherit;background:0 0;border:none}.relaya-root input,.relaya-root textarea{font-family:inherit;font-size:inherit}", P = "relaya-default-styles";
|
|
6
|
+
function F() {
|
|
7
|
+
if (typeof document > "u" || document.getElementById(P)) return;
|
|
8
|
+
let e = document.createElement("style");
|
|
9
|
+
e.id = P, e.textContent = N, document.head.appendChild(e);
|
|
10
|
+
}
|
|
11
|
+
function I() {
|
|
12
|
+
typeof document > "u" || document.getElementById(P)?.remove();
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region ../core/dist/messageUtils.js
|
|
16
|
+
function ee() {
|
|
17
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`;
|
|
18
|
+
}
|
|
19
|
+
function L(e, t = 1e3, n = 3e4) {
|
|
20
|
+
let r = t * 2 ** e;
|
|
21
|
+
return Math.min(r, n);
|
|
22
|
+
}
|
|
23
|
+
function R(e, t) {
|
|
24
|
+
return t ? e.filter((e) => e.clientId !== t) : e;
|
|
25
|
+
}
|
|
26
|
+
function z(e, t) {
|
|
27
|
+
return e.map((e) => e.clientId === t ? {
|
|
28
|
+
...e,
|
|
29
|
+
status: "failed"
|
|
30
|
+
} : e);
|
|
31
|
+
}
|
|
32
|
+
function B(e) {
|
|
33
|
+
let t = /* @__PURE__ */ new Set();
|
|
34
|
+
return e.filter((e) => t.has(e.id) ? !1 : (t.add(e.id), !0));
|
|
35
|
+
}
|
|
36
|
+
var V = new Set([
|
|
37
|
+
".gif",
|
|
38
|
+
".png",
|
|
39
|
+
".jpg",
|
|
40
|
+
".jpeg",
|
|
41
|
+
".webp"
|
|
42
|
+
]), H = {
|
|
43
|
+
pathPrefixes: ["/files/stations/"],
|
|
44
|
+
hosts: [
|
|
45
|
+
"tenor.com",
|
|
46
|
+
"media.tenor.com",
|
|
47
|
+
"giphy.com",
|
|
48
|
+
"media.giphy.com",
|
|
49
|
+
"i.giphy.com",
|
|
50
|
+
"editablegifs.com",
|
|
51
|
+
"imgur.com",
|
|
52
|
+
"i.imgur.com"
|
|
53
|
+
]
|
|
54
|
+
}, te = /(https?:\/\/[^\s]+|\/files\/stations\/[^\s]+)/gi, ne = /:([a-z0-9_-]{1,64}):/gi;
|
|
55
|
+
function re(e) {
|
|
56
|
+
return e.trim().toLowerCase();
|
|
57
|
+
}
|
|
58
|
+
function ie(e) {
|
|
59
|
+
return e.reduce((e, t) => (t.shortcode && (e[re(t.shortcode)] = t.url), e), {});
|
|
60
|
+
}
|
|
61
|
+
function ae(e, t) {
|
|
62
|
+
let n = ie(t);
|
|
63
|
+
return e.replace(ne, (e, t) => n[re(t)] ?? e);
|
|
64
|
+
}
|
|
65
|
+
function U(e) {
|
|
66
|
+
let t = e.toLowerCase();
|
|
67
|
+
for (let e of V) if (t.endsWith(e)) return !0;
|
|
68
|
+
return !1;
|
|
69
|
+
}
|
|
70
|
+
function W(e) {
|
|
71
|
+
let t = e.toLowerCase();
|
|
72
|
+
return H.hosts.some((e) => t === e || t.endsWith(`.${e}`));
|
|
73
|
+
}
|
|
74
|
+
function G(e) {
|
|
75
|
+
let t = e.match(/[\],.!?:;]+$/);
|
|
76
|
+
if (!t) return {
|
|
77
|
+
core: e,
|
|
78
|
+
trailing: ""
|
|
79
|
+
};
|
|
80
|
+
let n = t[0];
|
|
81
|
+
return {
|
|
82
|
+
core: e.slice(0, -n.length),
|
|
83
|
+
trailing: n
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function K(e, t) {
|
|
87
|
+
if (!t) return;
|
|
88
|
+
let n = e[e.length - 1];
|
|
89
|
+
if (n && !n.isImage) {
|
|
90
|
+
n.text += t;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
e.push({
|
|
94
|
+
text: t,
|
|
95
|
+
isImage: !1
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function oe(e) {
|
|
99
|
+
try {
|
|
100
|
+
if (e.startsWith("/")) return H.pathPrefixes.some((t) => e.startsWith(t)) && U(e.split("?")[0] ?? e);
|
|
101
|
+
let t = new URL(e);
|
|
102
|
+
if (t.protocol !== "http:" && t.protocol !== "https:") return !1;
|
|
103
|
+
let n = t.pathname, r = H.pathPrefixes.some((e) => n.startsWith(e)), i = W(t.hostname);
|
|
104
|
+
return !r && !i ? !1 : U(n);
|
|
105
|
+
} catch {
|
|
106
|
+
return !1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function q(e) {
|
|
110
|
+
let t = [], n = 0;
|
|
111
|
+
for (let r of e.matchAll(te)) {
|
|
112
|
+
let i = r[0], a = r.index ?? 0, o = a + i.length;
|
|
113
|
+
a > n && K(t, e.slice(n, a));
|
|
114
|
+
let { core: s, trailing: c } = G(i);
|
|
115
|
+
oe(s) ? (t.push({
|
|
116
|
+
text: s,
|
|
117
|
+
isImage: !0,
|
|
118
|
+
url: s
|
|
119
|
+
}), K(t, c)) : K(t, i), n = o;
|
|
120
|
+
}
|
|
121
|
+
return n < e.length && K(t, e.slice(n)), t;
|
|
122
|
+
}
|
|
123
|
+
function J(e) {
|
|
124
|
+
let t = q(e);
|
|
125
|
+
return t.filter((e) => e.isImage).length === 1 ? t.every((e) => e.isImage || e.text.trim() === "") : !1;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region ../core/dist/chatConnection.js
|
|
129
|
+
var se = class {
|
|
130
|
+
buildWsUrl;
|
|
131
|
+
onMessage;
|
|
132
|
+
onStatusChange;
|
|
133
|
+
ws = null;
|
|
134
|
+
status = "disconnected";
|
|
135
|
+
reconnectAttempt = 0;
|
|
136
|
+
reconnectTimer = null;
|
|
137
|
+
closed = !1;
|
|
138
|
+
backoffBaseMs;
|
|
139
|
+
backoffMaxMs;
|
|
140
|
+
onAuthRevoked;
|
|
141
|
+
constructor(e, t, n, r = {}) {
|
|
142
|
+
this.buildWsUrl = e, this.onMessage = t, this.onStatusChange = n, this.backoffBaseMs = r.backoffBaseMs ?? 1e3, this.backoffMaxMs = r.backoffMaxMs ?? 3e4, this.onAuthRevoked = r.onAuthRevoked;
|
|
143
|
+
}
|
|
144
|
+
getStatus() {
|
|
145
|
+
return this.status;
|
|
146
|
+
}
|
|
147
|
+
connect() {
|
|
148
|
+
this.closed || (this.setStatus("connecting"), this.openSocket());
|
|
149
|
+
}
|
|
150
|
+
send(e) {
|
|
151
|
+
this.ws?.readyState === WebSocket.OPEN && this.ws.send(JSON.stringify(e));
|
|
152
|
+
}
|
|
153
|
+
close() {
|
|
154
|
+
this.closed = !0, this.clearReconnectTimer(), this.ws &&= (this.ws.onclose = null, this.ws.close(), null), this.setStatus("disconnected");
|
|
155
|
+
}
|
|
156
|
+
openSocket() {
|
|
157
|
+
if (this.closed) return;
|
|
158
|
+
let e = this.buildWsUrl(), t = new WebSocket(e);
|
|
159
|
+
this.ws = t, t.onopen = () => {
|
|
160
|
+
if (this.closed) {
|
|
161
|
+
t.close();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.reconnectAttempt = 0, this.setStatus("connected");
|
|
165
|
+
}, t.onmessage = (e) => {
|
|
166
|
+
let t;
|
|
167
|
+
try {
|
|
168
|
+
t = JSON.parse(e.data);
|
|
169
|
+
} catch {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (t.type === "ping") {
|
|
173
|
+
this.send({ type: "pong" });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (t.type === "force_logout") {
|
|
177
|
+
this.handleAuthRevoked();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.onMessage(t);
|
|
181
|
+
}, t.onclose = (e) => {
|
|
182
|
+
if (!this.closed) {
|
|
183
|
+
if (e.code === 4001) {
|
|
184
|
+
this.handleAuthRevoked();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.scheduleReconnect();
|
|
188
|
+
}
|
|
189
|
+
}, t.onerror = () => {};
|
|
190
|
+
}
|
|
191
|
+
scheduleReconnect() {
|
|
192
|
+
if (this.closed) return;
|
|
193
|
+
this.setStatus("reconnecting");
|
|
194
|
+
let e = L(this.reconnectAttempt, this.backoffBaseMs, this.backoffMaxMs);
|
|
195
|
+
this.reconnectAttempt++, this.reconnectTimer = setTimeout(() => {
|
|
196
|
+
this.closed || this.openSocket();
|
|
197
|
+
}, e);
|
|
198
|
+
}
|
|
199
|
+
clearReconnectTimer() {
|
|
200
|
+
this.reconnectTimer !== null && (clearTimeout(this.reconnectTimer), this.reconnectTimer = null);
|
|
201
|
+
}
|
|
202
|
+
setStatus(e) {
|
|
203
|
+
this.status !== e && (this.status = e, this.onStatusChange(e));
|
|
204
|
+
}
|
|
205
|
+
handleAuthRevoked() {
|
|
206
|
+
this.closed = !0, this.clearReconnectTimer(), this.ws &&= (this.ws.onclose = null, this.ws.close(), null), this.setStatus("disconnected"), this.onAuthRevoked?.();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
function ce(e, t, n) {
|
|
210
|
+
return (r) => {
|
|
211
|
+
switch (r.type) {
|
|
212
|
+
case "auth:success":
|
|
213
|
+
e.userDirectory.current.clear(), e.avatarHistory.current.clear(), r.users.forEach((t) => {
|
|
214
|
+
e.userDirectory.current.set(t.id, {
|
|
215
|
+
id: t.id,
|
|
216
|
+
displayName: t.displayName,
|
|
217
|
+
avatarUrl: t.avatarUrl
|
|
218
|
+
}), t.avatarUrl !== null && e.avatarHistory.current.set(t.id, [{
|
|
219
|
+
url: t.avatarUrl,
|
|
220
|
+
changedAt: /* @__PURE__ */ new Date(0)
|
|
221
|
+
}]);
|
|
222
|
+
}), e.newestMessageIdRef.current ? console.log("[auth:success] reconnect — skipping full reload (catch-up already in flight)") : (console.log("[auth:success] fresh connect — loading initial messages"), t((e) => ({
|
|
223
|
+
...e,
|
|
224
|
+
loadingInitial: !0
|
|
225
|
+
})), n());
|
|
226
|
+
break;
|
|
227
|
+
case "message:broadcast": {
|
|
228
|
+
let { message: n, clientId: i } = r;
|
|
229
|
+
t((t) => {
|
|
230
|
+
let r = B([...t.messages, n]), a = r.length > 150 ? r.slice(r.length - 150) : r, o = R(t.optimistic, i);
|
|
231
|
+
return e.newestMessageIdRef.current = n.id, a.length > 0 && (e.oldestMessageIdRef.current = a[0].id), {
|
|
232
|
+
...t,
|
|
233
|
+
messages: a,
|
|
234
|
+
optimistic: o
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
case "message:deleted": {
|
|
240
|
+
let { messageId: e } = r;
|
|
241
|
+
t((t) => ({
|
|
242
|
+
...t,
|
|
243
|
+
messages: t.messages.map((t) => t.id === e ? {
|
|
244
|
+
...t,
|
|
245
|
+
is_deleted: !0,
|
|
246
|
+
content: null
|
|
247
|
+
} : t)
|
|
248
|
+
}));
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case "message:edited": {
|
|
252
|
+
let { message: e } = r;
|
|
253
|
+
t((t) => ({
|
|
254
|
+
...t,
|
|
255
|
+
messages: t.messages.map((t) => t.id === e.id ? e : t)
|
|
256
|
+
}));
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "mention:notification": {
|
|
260
|
+
let { mentionedBy: t, excerpt: n } = r;
|
|
261
|
+
e.mentionSoundPlayRef.current && e.mentionSoundPlayRef.current(), console.log(`[Mention] @${t.displayName}: ${n}`);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case "channel:notification": {
|
|
265
|
+
let { mentionedBy: t, excerpt: n } = r;
|
|
266
|
+
e.channelSoundPlayRef.current && e.channelSoundPlayRef.current(), console.log(`[Channel] @channel by ${t.displayName}: ${n}`);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "presence:update":
|
|
270
|
+
r.users.forEach((t) => {
|
|
271
|
+
e.userDirectory.current.set(t.id, {
|
|
272
|
+
id: t.id,
|
|
273
|
+
displayName: t.displayName,
|
|
274
|
+
avatarUrl: t.avatarUrl
|
|
275
|
+
});
|
|
276
|
+
}), t((e) => ({
|
|
277
|
+
...e,
|
|
278
|
+
users: r.users.map((e) => ({
|
|
279
|
+
id: e.id,
|
|
280
|
+
displayName: e.displayName,
|
|
281
|
+
avatarUrl: e.avatarUrl
|
|
282
|
+
})),
|
|
283
|
+
userCount: r.userCount,
|
|
284
|
+
totalCount: r.totalCount
|
|
285
|
+
}));
|
|
286
|
+
break;
|
|
287
|
+
case "user:update": {
|
|
288
|
+
let { userId: n, updates: i, timestamp: a } = r, o = e.userDirectory.current.get(n);
|
|
289
|
+
if (e.userDirectory.current.set(n, {
|
|
290
|
+
id: n,
|
|
291
|
+
displayName: i.displayName ?? o?.displayName ?? "Unknown User",
|
|
292
|
+
avatarUrl: i.avatarUrl === void 0 ? o?.avatarUrl ?? null : i.avatarUrl
|
|
293
|
+
}), i.avatarUrl !== void 0) {
|
|
294
|
+
let t = e.avatarHistory.current.get(n) || [];
|
|
295
|
+
t.push({
|
|
296
|
+
url: i.avatarUrl,
|
|
297
|
+
changedAt: new Date(a)
|
|
298
|
+
});
|
|
299
|
+
let r = t.length > 20 ? t.slice(t.length - 20) : t;
|
|
300
|
+
e.avatarHistory.current.set(n, r);
|
|
301
|
+
}
|
|
302
|
+
t((e) => ({
|
|
303
|
+
...e,
|
|
304
|
+
users: e.users.map((e) => e.id === n ? {
|
|
305
|
+
...e,
|
|
306
|
+
...i
|
|
307
|
+
} : e)
|
|
308
|
+
}));
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case "error":
|
|
312
|
+
t((e) => {
|
|
313
|
+
let t = e.optimistic.filter((e) => e.status === "sending");
|
|
314
|
+
return t.length === 1 ? {
|
|
315
|
+
...e,
|
|
316
|
+
optimistic: z(e.optimistic, t[0].clientId),
|
|
317
|
+
error: r.message
|
|
318
|
+
} : {
|
|
319
|
+
...e,
|
|
320
|
+
error: r.message
|
|
321
|
+
};
|
|
322
|
+
});
|
|
323
|
+
break;
|
|
324
|
+
case "stickers:updated":
|
|
325
|
+
e.onStickersUpdatedRef.current?.();
|
|
326
|
+
break;
|
|
327
|
+
default: break;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/contexts/NotificationMuteContext.tsx
|
|
333
|
+
var Y = C(void 0);
|
|
334
|
+
function X({ children: e }) {
|
|
335
|
+
let [t, n] = k(!1);
|
|
336
|
+
return /* @__PURE__ */ j(Y.Provider, {
|
|
337
|
+
value: {
|
|
338
|
+
isMuted: t,
|
|
339
|
+
setIsMuted: n
|
|
340
|
+
},
|
|
341
|
+
children: e
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function le() {
|
|
345
|
+
let e = T(Y);
|
|
346
|
+
if (!e) throw Error("useNotificationMute must be used within NotificationMuteProvider");
|
|
347
|
+
return e;
|
|
348
|
+
}
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/hooks/useRelayaChat.ts
|
|
351
|
+
function ue(e, t, n) {
|
|
352
|
+
if (!e) return y(t, n);
|
|
353
|
+
let r = new URL(e, window.location.href), i = r.protocol === "https:" ? "wss:" : "ws:", a = n ? `token=${encodeURIComponent(n)}&` : "";
|
|
354
|
+
return `${i}//${r.host}/ws?${a}station=${encodeURIComponent(t)}`;
|
|
355
|
+
}
|
|
356
|
+
var de = 50;
|
|
357
|
+
function Z(e, t, n) {
|
|
358
|
+
let [r, i] = k({
|
|
359
|
+
messages: [],
|
|
360
|
+
optimistic: [],
|
|
361
|
+
users: [],
|
|
362
|
+
userCount: 0,
|
|
363
|
+
totalCount: 0,
|
|
364
|
+
connectionStatus: "disconnected",
|
|
365
|
+
loadingInitial: !1,
|
|
366
|
+
loadingOlder: !1,
|
|
367
|
+
hasOlderMessages: !1,
|
|
368
|
+
retentionCutoff: null,
|
|
369
|
+
error: null
|
|
370
|
+
}), a = O(null), o = O(void 0), s = O(void 0), c = O(/* @__PURE__ */ new Map()), l = O(/* @__PURE__ */ new Map()), u = O(/* @__PURE__ */ new Map()), d = O(new b("", t)).current, f = O(null), p = O(null), m = O(null), h = O(null), g = O(void 0), { isMuted: _ } = le(), v = O(_), y = e.stationSlug, x = e.user?.id ?? "", S = w((e, t) => {
|
|
371
|
+
let n = (u.current.get(e) || []).filter((e) => e.changedAt <= t).pop();
|
|
372
|
+
return n ? n.url : l.current.get(e)?.avatarUrl ?? null;
|
|
373
|
+
}, []), C = w(async (e = {}) => {
|
|
374
|
+
if (y) try {
|
|
375
|
+
let t = await d.getMessages(y, {
|
|
376
|
+
after: e.after,
|
|
377
|
+
limit: de
|
|
378
|
+
}), n = t.messages ?? [];
|
|
379
|
+
console.log(`[loadMessages] ${e.after ? "catch-up (after=" + e.after.slice(0, 8) + "…)" : "initial"} → ${n.length} messages, oldest=${n[0]?.id?.slice(0, 8) ?? "none"}… newest=${n[n.length - 1]?.id?.slice(0, 8) ?? "none"}…, hasMore=${t.hasMore}`), i((r) => {
|
|
380
|
+
let i;
|
|
381
|
+
return i = e.after ? B([...r.messages, ...n]) : n, n.length > 0 && (s.current = i[i.length - 1].id, e.after || (o.current = i[0].id)), {
|
|
382
|
+
...r,
|
|
383
|
+
messages: i,
|
|
384
|
+
hasOlderMessages: t.hasMore ?? !1,
|
|
385
|
+
loadingInitial: !1,
|
|
386
|
+
...e.after ? {} : { retentionCutoff: t.retentionCutoff ?? null }
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
} catch (e) {
|
|
390
|
+
console.error("[loadMessages] fetch failed:", e), i((e) => ({
|
|
391
|
+
...e,
|
|
392
|
+
loadingInitial: !1
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
395
|
+
}, [d, y]), T = w(ce({
|
|
396
|
+
userDirectory: l,
|
|
397
|
+
avatarHistory: u,
|
|
398
|
+
newestMessageIdRef: s,
|
|
399
|
+
oldestMessageIdRef: o,
|
|
400
|
+
mentionSoundPlayRef: m,
|
|
401
|
+
channelSoundPlayRef: h,
|
|
402
|
+
onStickersUpdatedRef: g
|
|
403
|
+
}, i, C), [C]), D = w((e) => {
|
|
404
|
+
i((t) => ({
|
|
405
|
+
...t,
|
|
406
|
+
connectionStatus: e
|
|
407
|
+
})), e === "connected" && s.current && C({ after: s.current });
|
|
408
|
+
}, [C]);
|
|
409
|
+
E(() => {
|
|
410
|
+
f.current = T, p.current = D, v.current = _, g.current = n?.onStickersUpdated;
|
|
411
|
+
}), E(() => {
|
|
412
|
+
if (!y || e.status === "loading") return;
|
|
413
|
+
let r = () => {
|
|
414
|
+
let r = new se(() => {
|
|
415
|
+
let r = e.status === "authenticated" ? t() ?? void 0 : void 0;
|
|
416
|
+
return ue(n?.wsBaseUrl, y, r);
|
|
417
|
+
}, (e) => f.current?.(e), (e) => p.current?.(e), { onAuthRevoked: n?.onForcedLogout });
|
|
418
|
+
return a.current = r, r.connect(), r;
|
|
419
|
+
};
|
|
420
|
+
r();
|
|
421
|
+
let i = () => {
|
|
422
|
+
document.hidden ? (a.current?.close(), a.current = null) : r();
|
|
423
|
+
};
|
|
424
|
+
return document.addEventListener("visibilitychange", i), () => {
|
|
425
|
+
document.removeEventListener("visibilitychange", i), a.current?.close(), a.current = null;
|
|
426
|
+
};
|
|
427
|
+
}, [
|
|
428
|
+
y,
|
|
429
|
+
e.status,
|
|
430
|
+
t,
|
|
431
|
+
n?.wsBaseUrl
|
|
432
|
+
]);
|
|
433
|
+
let A = w(async () => {
|
|
434
|
+
if (!(!y || !o.current || r.loadingOlder)) {
|
|
435
|
+
i((e) => ({
|
|
436
|
+
...e,
|
|
437
|
+
loadingOlder: !0
|
|
438
|
+
}));
|
|
439
|
+
try {
|
|
440
|
+
let e = await d.getMessages(y, {
|
|
441
|
+
before: o.current,
|
|
442
|
+
limit: de
|
|
443
|
+
}), t = e.messages ?? [];
|
|
444
|
+
i((n) => {
|
|
445
|
+
let r = B([...t, ...n.messages]);
|
|
446
|
+
return t.length > 0 && (o.current = t[0].id), {
|
|
447
|
+
...n,
|
|
448
|
+
messages: r,
|
|
449
|
+
hasOlderMessages: e.hasMore ?? !1,
|
|
450
|
+
loadingOlder: !1
|
|
451
|
+
};
|
|
452
|
+
});
|
|
453
|
+
} catch {
|
|
454
|
+
i((e) => ({
|
|
455
|
+
...e,
|
|
456
|
+
loadingOlder: !1
|
|
457
|
+
}));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}, [
|
|
461
|
+
d,
|
|
462
|
+
y,
|
|
463
|
+
r.loadingOlder
|
|
464
|
+
]), j = w((t, n) => {
|
|
465
|
+
if (!a.current || !e.user) return;
|
|
466
|
+
let r = ee(), o = {
|
|
467
|
+
clientId: r,
|
|
468
|
+
content: t.trim(),
|
|
469
|
+
authorId: x,
|
|
470
|
+
authorDisplayName: e.user.displayName,
|
|
471
|
+
authorAvatarUrl: e.user.avatarUrl,
|
|
472
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
473
|
+
status: "sending"
|
|
474
|
+
};
|
|
475
|
+
i((e) => ({
|
|
476
|
+
...e,
|
|
477
|
+
optimistic: [...e.optimistic, o]
|
|
478
|
+
})), c.current.set(r, t.trim()), a.current.send({
|
|
479
|
+
type: "message:send",
|
|
480
|
+
content: t.trim(),
|
|
481
|
+
clientId: r,
|
|
482
|
+
replyToMessageId: n?.messageId,
|
|
483
|
+
replyAuthorName: n?.authorName,
|
|
484
|
+
replyExcerpt: n?.excerpt
|
|
485
|
+
});
|
|
486
|
+
}, [e.user, x]), M = w((t) => {
|
|
487
|
+
if (!a.current || !e.user) return;
|
|
488
|
+
let n = c.current.get(t);
|
|
489
|
+
n && (i((e) => ({
|
|
490
|
+
...e,
|
|
491
|
+
optimistic: e.optimistic.filter((e) => e.clientId !== t)
|
|
492
|
+
})), c.current.delete(t), j(n));
|
|
493
|
+
}, [e.user, j]), N = w(async (e) => {
|
|
494
|
+
y && (await d.deleteMessage(y, e), i((t) => ({
|
|
495
|
+
...t,
|
|
496
|
+
messages: t.messages.map((t) => t.id === e ? {
|
|
497
|
+
...t,
|
|
498
|
+
is_deleted: !0,
|
|
499
|
+
content: null
|
|
500
|
+
} : t)
|
|
501
|
+
})));
|
|
502
|
+
}, [d, y]), P = w(async (e, t) => {
|
|
503
|
+
y && await d.createBan(y, e, t);
|
|
504
|
+
}, [d, y]), F = w(async (e, t, n) => {
|
|
505
|
+
y && await d.createReport(y, e, t, n);
|
|
506
|
+
}, [d, y]), I = w(async (e, t) => {
|
|
507
|
+
if (y) try {
|
|
508
|
+
let n = await d.editMessage(y, e, t);
|
|
509
|
+
i((t) => ({
|
|
510
|
+
...t,
|
|
511
|
+
messages: t.messages.map((t) => t.id === e ? {
|
|
512
|
+
...t,
|
|
513
|
+
...n
|
|
514
|
+
} : t)
|
|
515
|
+
}));
|
|
516
|
+
} catch (e) {
|
|
517
|
+
throw i((e) => ({
|
|
518
|
+
...e,
|
|
519
|
+
error: "Failed to edit message"
|
|
520
|
+
})), e;
|
|
521
|
+
}
|
|
522
|
+
}, [d, y]), L = w((e) => {
|
|
523
|
+
m.current = e;
|
|
524
|
+
}, []), R = w((e) => {
|
|
525
|
+
h.current = e;
|
|
526
|
+
}, []), z = w((e) => l.current.get(e), []);
|
|
527
|
+
return {
|
|
528
|
+
...r,
|
|
529
|
+
sendMessage: j,
|
|
530
|
+
loadOlderMessages: A,
|
|
531
|
+
editMessage: I,
|
|
532
|
+
deleteMessage: N,
|
|
533
|
+
banUser: P,
|
|
534
|
+
reportMessage: F,
|
|
535
|
+
retryFailed: M,
|
|
536
|
+
registerMentionSound: L,
|
|
537
|
+
registerChannelSound: R,
|
|
538
|
+
getUserInfo: z,
|
|
539
|
+
getAvatarForMessage: S
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/mentionInputUtils.ts
|
|
544
|
+
function fe(e, t) {
|
|
545
|
+
if (t <= 0 || t > e.length) return null;
|
|
546
|
+
let n = t - 1;
|
|
547
|
+
for (; n >= 0 && /[a-zA-Z0-9_-]/.test(e[n] ?? "");) --n;
|
|
548
|
+
if (e[n] !== "@") return null;
|
|
549
|
+
let r = n > 0 ? e[n - 1] : "";
|
|
550
|
+
return r && !/\s/.test(r) ? null : {
|
|
551
|
+
query: e.slice(n + 1, t).toLowerCase(),
|
|
552
|
+
start: n,
|
|
553
|
+
end: t
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function pe(e, t, n, r = 8) {
|
|
557
|
+
let i = t.trim().toLowerCase();
|
|
558
|
+
return e.filter((e) => e.id !== n).filter((e) => !i || e.displayName.toLowerCase().startsWith(i) || e.displayName.toLowerCase().includes(i)).sort((e, t) => i && !e.displayName.toLowerCase().startsWith(i) - +!t.displayName.toLowerCase().startsWith(i) || e.displayName.localeCompare(t.displayName)).slice(0, r);
|
|
559
|
+
}
|
|
560
|
+
function me(e, t, n, r) {
|
|
561
|
+
if (t <= 0) return null;
|
|
562
|
+
let i = e.slice(0, t).match(/(?:^|(?<=\s))@([a-zA-Z0-9_-]+)$/);
|
|
563
|
+
if (!i) return null;
|
|
564
|
+
let a = i[1].toLowerCase(), o = t, s = o - i[0].length, c = n.filter((e) => e.id !== r).filter((e) => !e.displayName.includes(" ")).filter((e) => e.displayName.toLowerCase() === a);
|
|
565
|
+
return c.length === 1 ? {
|
|
566
|
+
user: c[0],
|
|
567
|
+
start: s,
|
|
568
|
+
end: o
|
|
569
|
+
} : null;
|
|
570
|
+
}
|
|
571
|
+
function he(e, t, n, r) {
|
|
572
|
+
let i = `@[${r.displayName}|${r.id}] `;
|
|
573
|
+
return {
|
|
574
|
+
nextText: `${e.slice(0, t)}${i}${e.slice(n)}`,
|
|
575
|
+
caretPosition: t + i.length
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region src/stickerInputUtils.ts
|
|
580
|
+
function ge(e, t) {
|
|
581
|
+
if (t <= 0 || t > e.length) return null;
|
|
582
|
+
let n = t - 1;
|
|
583
|
+
for (; n >= 0 && /[A-Za-z0-9_-]/.test(e[n] ?? "");) --n;
|
|
584
|
+
if (e[n] !== ":") return null;
|
|
585
|
+
let r = n > 0 ? e[n - 1] : "";
|
|
586
|
+
if (r && !/\s/.test(r)) return null;
|
|
587
|
+
let i = e.slice(n + 1, t);
|
|
588
|
+
return /^[A-Za-z0-9_-]*$/.test(i) ? {
|
|
589
|
+
query: i.toLowerCase(),
|
|
590
|
+
start: n,
|
|
591
|
+
end: t
|
|
592
|
+
} : null;
|
|
593
|
+
}
|
|
594
|
+
function _e(e, t, n, r) {
|
|
595
|
+
let i = `:${r}: `;
|
|
596
|
+
return {
|
|
597
|
+
nextText: `${e.slice(0, t)}${i}${e.slice(n)}`,
|
|
598
|
+
caretPosition: t + i.length
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function ve(e, t, n = 6) {
|
|
602
|
+
let r = t.trim().toLowerCase();
|
|
603
|
+
return e.filter((e) => !!e.shortcode).filter((e) => !r || e.shortcode.startsWith(r) || e.shortcode.includes(r)).sort((e, t) => !e.shortcode.startsWith(r) - +!t.shortcode.startsWith(r) || e.order - t.order || e.filename.localeCompare(t.filename)).slice(0, n);
|
|
604
|
+
}
|
|
605
|
+
//#endregion
|
|
606
|
+
//#region src/components/LoginScreen.tsx
|
|
607
|
+
function ye({ onLogin: e, error: t, stationSlug: n }) {
|
|
608
|
+
let [r, i] = k(""), [a, o] = k(!1);
|
|
609
|
+
async function s(t) {
|
|
610
|
+
if (t.preventDefault(), !(!r.trim() || a)) {
|
|
611
|
+
o(!0);
|
|
612
|
+
try {
|
|
613
|
+
await e(r.trim());
|
|
614
|
+
} catch {} finally {
|
|
615
|
+
o(!1);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
let c = n.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
|
|
620
|
+
return /* @__PURE__ */ j("div", {
|
|
621
|
+
className: "login-screen",
|
|
622
|
+
children: /* @__PURE__ */ M("div", {
|
|
623
|
+
className: "login-card",
|
|
624
|
+
children: [
|
|
625
|
+
/* @__PURE__ */ j("div", {
|
|
626
|
+
className: "login-card__logo",
|
|
627
|
+
children: /* @__PURE__ */ j("span", {
|
|
628
|
+
style: { fontSize: 40 },
|
|
629
|
+
children: "📻"
|
|
630
|
+
})
|
|
631
|
+
}),
|
|
632
|
+
/* @__PURE__ */ M("h1", {
|
|
633
|
+
className: "login-card__title",
|
|
634
|
+
children: [c, " Chat"]
|
|
635
|
+
}),
|
|
636
|
+
/* @__PURE__ */ j("p", {
|
|
637
|
+
className: "login-card__subtitle",
|
|
638
|
+
children: "Enter your email to receive a magic link and join the chat."
|
|
639
|
+
}),
|
|
640
|
+
/* @__PURE__ */ M("form", {
|
|
641
|
+
className: "login-card__form",
|
|
642
|
+
onSubmit: s,
|
|
643
|
+
children: [
|
|
644
|
+
/* @__PURE__ */ M("div", {
|
|
645
|
+
className: "form-field",
|
|
646
|
+
children: [/* @__PURE__ */ j("label", {
|
|
647
|
+
htmlFor: "email",
|
|
648
|
+
children: "Email address"
|
|
649
|
+
}), /* @__PURE__ */ j("input", {
|
|
650
|
+
id: "email",
|
|
651
|
+
type: "email",
|
|
652
|
+
value: r,
|
|
653
|
+
onChange: (e) => i(e.target.value),
|
|
654
|
+
placeholder: "you@example.com",
|
|
655
|
+
autoComplete: "email",
|
|
656
|
+
autoFocus: !0,
|
|
657
|
+
required: !0,
|
|
658
|
+
disabled: a
|
|
659
|
+
})]
|
|
660
|
+
}),
|
|
661
|
+
t && /* @__PURE__ */ j("div", {
|
|
662
|
+
className: "error-message",
|
|
663
|
+
children: t
|
|
664
|
+
}),
|
|
665
|
+
/* @__PURE__ */ j("button", {
|
|
666
|
+
type: "submit",
|
|
667
|
+
className: "btn btn--primary",
|
|
668
|
+
disabled: a || !r.trim(),
|
|
669
|
+
children: a ? /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("span", { className: "connection-spinner" }), "Sending…"] }) : "Send magic link"
|
|
670
|
+
})
|
|
671
|
+
]
|
|
672
|
+
})
|
|
673
|
+
]
|
|
674
|
+
})
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
//#endregion
|
|
678
|
+
//#region src/components/MagicLinkSent.tsx
|
|
679
|
+
function be({ onBack: e }) {
|
|
680
|
+
return /* @__PURE__ */ j("div", {
|
|
681
|
+
className: "magic-link-sent",
|
|
682
|
+
children: /* @__PURE__ */ M("div", {
|
|
683
|
+
className: "magic-link-sent__card",
|
|
684
|
+
children: [
|
|
685
|
+
/* @__PURE__ */ j("div", {
|
|
686
|
+
className: "magic-link-sent__icon",
|
|
687
|
+
children: "✉️"
|
|
688
|
+
}),
|
|
689
|
+
/* @__PURE__ */ j("h1", {
|
|
690
|
+
className: "magic-link-sent__title",
|
|
691
|
+
children: "Check your email"
|
|
692
|
+
}),
|
|
693
|
+
/* @__PURE__ */ M("p", {
|
|
694
|
+
className: "magic-link-sent__body",
|
|
695
|
+
children: [
|
|
696
|
+
"We've sent a magic link to your email address. Click the link to join the chat.",
|
|
697
|
+
/* @__PURE__ */ j("br", {}),
|
|
698
|
+
/* @__PURE__ */ j("br", {}),
|
|
699
|
+
"The link expires in 15 minutes and can only be used once."
|
|
700
|
+
]
|
|
701
|
+
}),
|
|
702
|
+
/* @__PURE__ */ j("button", {
|
|
703
|
+
className: "btn btn--ghost",
|
|
704
|
+
onClick: e,
|
|
705
|
+
style: { width: "100%" },
|
|
706
|
+
children: "Use a different email"
|
|
707
|
+
})
|
|
708
|
+
]
|
|
709
|
+
})
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region ../../node_modules/react-icons/lib/iconContext.mjs
|
|
714
|
+
var Q = {
|
|
715
|
+
color: void 0,
|
|
716
|
+
size: void 0,
|
|
717
|
+
className: void 0,
|
|
718
|
+
style: void 0,
|
|
719
|
+
attr: void 0
|
|
720
|
+
}, xe = S.createContext && /* @__PURE__ */ S.createContext(Q), Se = [
|
|
721
|
+
"attr",
|
|
722
|
+
"size",
|
|
723
|
+
"title"
|
|
724
|
+
];
|
|
725
|
+
function Ce(e, t) {
|
|
726
|
+
if (e == null) return {};
|
|
727
|
+
var n, r, i = we(e, t);
|
|
728
|
+
if (Object.getOwnPropertySymbols) {
|
|
729
|
+
var a = Object.getOwnPropertySymbols(e);
|
|
730
|
+
for (r = 0; r < a.length; r++) n = a[r], t.indexOf(n) === -1 && {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]);
|
|
731
|
+
}
|
|
732
|
+
return i;
|
|
733
|
+
}
|
|
734
|
+
function we(e, t) {
|
|
735
|
+
if (e == null) return {};
|
|
736
|
+
var n = {};
|
|
737
|
+
for (var r in e) if ({}.hasOwnProperty.call(e, r)) {
|
|
738
|
+
if (t.indexOf(r) !== -1) continue;
|
|
739
|
+
n[r] = e[r];
|
|
740
|
+
}
|
|
741
|
+
return n;
|
|
742
|
+
}
|
|
743
|
+
function Te() {
|
|
744
|
+
return Te = Object.assign ? Object.assign.bind() : function(e) {
|
|
745
|
+
for (var t = 1; t < arguments.length; t++) {
|
|
746
|
+
var n = arguments[t];
|
|
747
|
+
for (var r in n) ({}).hasOwnProperty.call(n, r) && (e[r] = n[r]);
|
|
748
|
+
}
|
|
749
|
+
return e;
|
|
750
|
+
}, Te.apply(null, arguments);
|
|
751
|
+
}
|
|
752
|
+
function Ee(e, t) {
|
|
753
|
+
var n = Object.keys(e);
|
|
754
|
+
if (Object.getOwnPropertySymbols) {
|
|
755
|
+
var r = Object.getOwnPropertySymbols(e);
|
|
756
|
+
t && (r = r.filter(function(t) {
|
|
757
|
+
return Object.getOwnPropertyDescriptor(e, t).enumerable;
|
|
758
|
+
})), n.push.apply(n, r);
|
|
759
|
+
}
|
|
760
|
+
return n;
|
|
761
|
+
}
|
|
762
|
+
function De(e) {
|
|
763
|
+
for (var t = 1; t < arguments.length; t++) {
|
|
764
|
+
var n = arguments[t] == null ? {} : arguments[t];
|
|
765
|
+
t % 2 ? Ee(Object(n), !0).forEach(function(t) {
|
|
766
|
+
Oe(e, t, n[t]);
|
|
767
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : Ee(Object(n)).forEach(function(t) {
|
|
768
|
+
Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t));
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
return e;
|
|
772
|
+
}
|
|
773
|
+
function Oe(e, t, n) {
|
|
774
|
+
return (t = ke(t)) in e ? Object.defineProperty(e, t, {
|
|
775
|
+
value: n,
|
|
776
|
+
enumerable: !0,
|
|
777
|
+
configurable: !0,
|
|
778
|
+
writable: !0
|
|
779
|
+
}) : e[t] = n, e;
|
|
780
|
+
}
|
|
781
|
+
function ke(e) {
|
|
782
|
+
var t = Ae(e, "string");
|
|
783
|
+
return typeof t == "symbol" ? t : t + "";
|
|
784
|
+
}
|
|
785
|
+
function Ae(e, t) {
|
|
786
|
+
if (typeof e != "object" || !e) return e;
|
|
787
|
+
var n = e[Symbol.toPrimitive];
|
|
788
|
+
if (n !== void 0) {
|
|
789
|
+
var r = n.call(e, t || "default");
|
|
790
|
+
if (typeof r != "object") return r;
|
|
791
|
+
throw TypeError("@@toPrimitive must return a primitive value.");
|
|
792
|
+
}
|
|
793
|
+
return (t === "string" ? String : Number)(e);
|
|
794
|
+
}
|
|
795
|
+
function je(e) {
|
|
796
|
+
return e && e.map((e, t) => /* @__PURE__ */ S.createElement(e.tag, De({ key: t }, e.attr), je(e.child)));
|
|
797
|
+
}
|
|
798
|
+
function Me(e) {
|
|
799
|
+
return (t) => /* @__PURE__ */ S.createElement(Ne, Te({ attr: De({}, e.attr) }, t), je(e.child));
|
|
800
|
+
}
|
|
801
|
+
function Ne(e) {
|
|
802
|
+
var t = (t) => {
|
|
803
|
+
var { attr: n, size: r, title: i } = e, a = Ce(e, Se), o = r || t.size || "1em", s;
|
|
804
|
+
return t.className && (s = t.className), e.className && (s = (s ? s + " " : "") + e.className), /* @__PURE__ */ S.createElement("svg", Te({
|
|
805
|
+
stroke: "currentColor",
|
|
806
|
+
fill: "currentColor",
|
|
807
|
+
strokeWidth: "0"
|
|
808
|
+
}, t.attr, n, a, {
|
|
809
|
+
className: s,
|
|
810
|
+
style: De(De({ color: e.color || t.color }, t.style), e.style),
|
|
811
|
+
height: o,
|
|
812
|
+
width: o,
|
|
813
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
814
|
+
}), i && /* @__PURE__ */ S.createElement("title", null, i), e.children);
|
|
815
|
+
};
|
|
816
|
+
return xe === void 0 ? t(Q) : /* @__PURE__ */ S.createElement(xe.Consumer, null, (e) => t(e));
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
819
|
+
//#region ../../node_modules/react-icons/hi2/index.mjs
|
|
820
|
+
function Pe(e) {
|
|
821
|
+
return Me({
|
|
822
|
+
tag: "svg",
|
|
823
|
+
attr: {
|
|
824
|
+
viewBox: "0 0 24 24",
|
|
825
|
+
fill: "currentColor",
|
|
826
|
+
"aria-hidden": "true"
|
|
827
|
+
},
|
|
828
|
+
child: [{
|
|
829
|
+
tag: "path",
|
|
830
|
+
attr: {
|
|
831
|
+
fillRule: "evenodd",
|
|
832
|
+
d: "M5.25 9a6.75 6.75 0 0 1 13.5 0v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 0 1-.297 1.206c-1.544.57-3.16.99-4.831 1.243a3.75 3.75 0 1 1-7.48 0 24.585 24.585 0 0 1-4.831-1.244.75.75 0 0 1-.298-1.205A8.217 8.217 0 0 0 5.25 9.75V9Zm4.502 8.9a2.25 2.25 0 1 0 4.496 0 25.057 25.057 0 0 1-4.496 0Z",
|
|
833
|
+
clipRule: "evenodd"
|
|
834
|
+
},
|
|
835
|
+
child: []
|
|
836
|
+
}]
|
|
837
|
+
})(e);
|
|
838
|
+
}
|
|
839
|
+
function Fe(e) {
|
|
840
|
+
return Me({
|
|
841
|
+
tag: "svg",
|
|
842
|
+
attr: {
|
|
843
|
+
viewBox: "0 0 24 24",
|
|
844
|
+
fill: "currentColor",
|
|
845
|
+
"aria-hidden": "true"
|
|
846
|
+
},
|
|
847
|
+
child: [{
|
|
848
|
+
tag: "path",
|
|
849
|
+
attr: { d: "M3.53 2.47a.75.75 0 0 0-1.06 1.06l18 18a.75.75 0 1 0 1.06-1.06l-18-18ZM20.57 16.476c-.223.082-.448.161-.674.238L7.319 4.137A6.75 6.75 0 0 1 18.75 9v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 0 1-.297 1.206Z" },
|
|
850
|
+
child: []
|
|
851
|
+
}, {
|
|
852
|
+
tag: "path",
|
|
853
|
+
attr: {
|
|
854
|
+
fillRule: "evenodd",
|
|
855
|
+
d: "M5.25 9c0-.184.007-.366.022-.546l10.384 10.384a3.751 3.751 0 0 1-7.396-1.119 24.585 24.585 0 0 1-4.831-1.244.75.75 0 0 1-.298-1.205A8.217 8.217 0 0 0 5.25 9.75V9Zm4.502 8.9a2.25 2.25 0 1 0 4.496 0 25.057 25.057 0 0 1-4.496 0Z",
|
|
856
|
+
clipRule: "evenodd"
|
|
857
|
+
},
|
|
858
|
+
child: []
|
|
859
|
+
}]
|
|
860
|
+
})(e);
|
|
861
|
+
}
|
|
862
|
+
function Ie(e) {
|
|
863
|
+
return Me({
|
|
864
|
+
tag: "svg",
|
|
865
|
+
attr: {
|
|
866
|
+
fill: "none",
|
|
867
|
+
viewBox: "0 0 24 24",
|
|
868
|
+
strokeWidth: "1.5",
|
|
869
|
+
stroke: "currentColor",
|
|
870
|
+
"aria-hidden": "true"
|
|
871
|
+
},
|
|
872
|
+
child: [{
|
|
873
|
+
tag: "path",
|
|
874
|
+
attr: {
|
|
875
|
+
strokeLinecap: "round",
|
|
876
|
+
strokeLinejoin: "round",
|
|
877
|
+
d: "M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
|
|
878
|
+
},
|
|
879
|
+
child: []
|
|
880
|
+
}, {
|
|
881
|
+
tag: "path",
|
|
882
|
+
attr: {
|
|
883
|
+
strokeLinecap: "round",
|
|
884
|
+
strokeLinejoin: "round",
|
|
885
|
+
d: "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
|
886
|
+
},
|
|
887
|
+
child: []
|
|
888
|
+
}]
|
|
889
|
+
})(e);
|
|
890
|
+
}
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/components/ReportModal.tsx
|
|
893
|
+
var Le = [
|
|
894
|
+
{
|
|
895
|
+
value: "spam",
|
|
896
|
+
label: "Spam"
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
value: "harassment",
|
|
900
|
+
label: "Harassment or bullying"
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
value: "offensive_content",
|
|
904
|
+
label: "Offensive or hateful content"
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
value: "misinformation",
|
|
908
|
+
label: "Misinformation"
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
value: "other",
|
|
912
|
+
label: "Other"
|
|
913
|
+
}
|
|
914
|
+
];
|
|
915
|
+
function Re({ messageId: e, authorName: t, onReport: n, onClose: r }) {
|
|
916
|
+
let [i, a] = k(Le[0].value), [o, s] = k(""), [c, l] = k(!1), [u, d] = k(null), [f, p] = k(!1);
|
|
917
|
+
async function m() {
|
|
918
|
+
l(!0), d(null);
|
|
919
|
+
try {
|
|
920
|
+
await n(e, i, o.trim() || void 0), p(!0);
|
|
921
|
+
} catch (e) {
|
|
922
|
+
d(e?.message ?? "Failed to submit report.");
|
|
923
|
+
} finally {
|
|
924
|
+
l(!1);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return /* @__PURE__ */ j("div", {
|
|
928
|
+
className: "modal-overlay",
|
|
929
|
+
onClick: (e) => e.target === e.currentTarget && r(),
|
|
930
|
+
children: /* @__PURE__ */ M("div", {
|
|
931
|
+
className: "modal",
|
|
932
|
+
role: "dialog",
|
|
933
|
+
"aria-modal": "true",
|
|
934
|
+
"aria-label": "Report message",
|
|
935
|
+
children: [
|
|
936
|
+
/* @__PURE__ */ j("h2", {
|
|
937
|
+
className: "modal__title",
|
|
938
|
+
children: "Report message"
|
|
939
|
+
}),
|
|
940
|
+
f ? /* @__PURE__ */ j("div", {
|
|
941
|
+
className: "modal__body",
|
|
942
|
+
children: /* @__PURE__ */ j("p", { children: "✅ Your report has been submitted. Our moderators will review it shortly." })
|
|
943
|
+
}) : /* @__PURE__ */ M("div", {
|
|
944
|
+
className: "modal__body",
|
|
945
|
+
children: [
|
|
946
|
+
/* @__PURE__ */ M("p", { children: [
|
|
947
|
+
"Report a message from ",
|
|
948
|
+
/* @__PURE__ */ j("strong", { children: t }),
|
|
949
|
+
"."
|
|
950
|
+
] }),
|
|
951
|
+
/* @__PURE__ */ j("label", {
|
|
952
|
+
htmlFor: "report-reason",
|
|
953
|
+
children: "Reason"
|
|
954
|
+
}),
|
|
955
|
+
/* @__PURE__ */ j("select", {
|
|
956
|
+
id: "report-reason",
|
|
957
|
+
value: i,
|
|
958
|
+
onChange: (e) => a(e.target.value),
|
|
959
|
+
disabled: c,
|
|
960
|
+
children: Le.map((e) => /* @__PURE__ */ j("option", {
|
|
961
|
+
value: e.value,
|
|
962
|
+
children: e.label
|
|
963
|
+
}, e.value))
|
|
964
|
+
}),
|
|
965
|
+
/* @__PURE__ */ j("label", {
|
|
966
|
+
htmlFor: "report-details",
|
|
967
|
+
children: "Additional details (optional)"
|
|
968
|
+
}),
|
|
969
|
+
/* @__PURE__ */ j("textarea", {
|
|
970
|
+
id: "report-details",
|
|
971
|
+
value: o,
|
|
972
|
+
onChange: (e) => s(e.target.value),
|
|
973
|
+
placeholder: "Describe the issue…",
|
|
974
|
+
disabled: c,
|
|
975
|
+
maxLength: 500
|
|
976
|
+
}),
|
|
977
|
+
u && /* @__PURE__ */ j("p", {
|
|
978
|
+
style: {
|
|
979
|
+
color: "var(--color-danger)",
|
|
980
|
+
marginTop: 8
|
|
981
|
+
},
|
|
982
|
+
children: u
|
|
983
|
+
})
|
|
984
|
+
]
|
|
985
|
+
}),
|
|
986
|
+
/* @__PURE__ */ j("div", {
|
|
987
|
+
className: "modal__footer",
|
|
988
|
+
children: f ? /* @__PURE__ */ j("button", {
|
|
989
|
+
className: "btn btn--primary",
|
|
990
|
+
onClick: r,
|
|
991
|
+
children: "Close"
|
|
992
|
+
}) : /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("button", {
|
|
993
|
+
className: "btn btn--ghost",
|
|
994
|
+
onClick: r,
|
|
995
|
+
disabled: c,
|
|
996
|
+
children: "Cancel"
|
|
997
|
+
}), /* @__PURE__ */ j("button", {
|
|
998
|
+
className: "btn btn--danger",
|
|
999
|
+
onClick: m,
|
|
1000
|
+
disabled: c,
|
|
1001
|
+
children: c ? "Submitting…" : "Submit report"
|
|
1002
|
+
})] })
|
|
1003
|
+
})
|
|
1004
|
+
]
|
|
1005
|
+
})
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
//#endregion
|
|
1009
|
+
//#region src/components/MessageAvatar.tsx
|
|
1010
|
+
function ze(e) {
|
|
1011
|
+
return e.split(" ").map((e) => e[0]).join("").toUpperCase().slice(0, 2);
|
|
1012
|
+
}
|
|
1013
|
+
function $({ displayName: e, avatarUrl: t }) {
|
|
1014
|
+
let [n, r] = k(!1);
|
|
1015
|
+
return /* @__PURE__ */ j("div", {
|
|
1016
|
+
className: "message-item__avatar-slot",
|
|
1017
|
+
"aria-hidden": "true",
|
|
1018
|
+
children: t && !n ? /* @__PURE__ */ j("img", {
|
|
1019
|
+
className: "message-item__avatar message-item__avatar--image",
|
|
1020
|
+
src: t,
|
|
1021
|
+
alt: "",
|
|
1022
|
+
onError: () => r(!0),
|
|
1023
|
+
loading: "lazy"
|
|
1024
|
+
}) : /* @__PURE__ */ j("div", {
|
|
1025
|
+
className: "message-item__avatar",
|
|
1026
|
+
children: ze(e)
|
|
1027
|
+
})
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
//#endregion
|
|
1031
|
+
//#region src/components/MessageContextMenu.tsx
|
|
1032
|
+
function Be({ position: e, onClose: t, showAvatarOptions: n, hasGalleryImages: r, onSelectGravatarPhoto: i, onUseDefaultGravatar: a, onUseInitials: o, showReply: s, showEdit: c, showDelete: l, showReport: u, showBan: d, onReply: f, onEdit: p, onDelete: m, onReport: h, onBan: g }) {
|
|
1033
|
+
let _ = S(), v = window.innerWidth, y = window.innerHeight, b = e.x + 220 > v ? e.x - 220 : e.x, x = e.y + _ > y ? e.y - _ : e.y;
|
|
1034
|
+
function S() {
|
|
1035
|
+
let e = 0;
|
|
1036
|
+
n && (e += 3), s && e++, c && e++, l && e++, u && e++, d && e++;
|
|
1037
|
+
let t = e * 40, r = n && (s || c || l || u || d) ? 1 : 0, i = s && (u || d || l) ? 1 : 0;
|
|
1038
|
+
return t + r + i + 4;
|
|
1039
|
+
}
|
|
1040
|
+
let C = ({ onClick: e, children: n, icon: r, danger: i = !1 }) => /* @__PURE__ */ M("button", {
|
|
1041
|
+
onClick: () => {
|
|
1042
|
+
e(), t();
|
|
1043
|
+
},
|
|
1044
|
+
style: {
|
|
1045
|
+
display: "flex",
|
|
1046
|
+
alignItems: "center",
|
|
1047
|
+
gap: "8px",
|
|
1048
|
+
width: "100%",
|
|
1049
|
+
padding: "10px 12px",
|
|
1050
|
+
textAlign: "left",
|
|
1051
|
+
background: "none",
|
|
1052
|
+
border: "none",
|
|
1053
|
+
color: i ? "var(--relaya-color-danger, #e74c3c)" : "var(--relaya-color-text)",
|
|
1054
|
+
fontSize: "var(--relaya-font-size-sm)",
|
|
1055
|
+
cursor: "pointer",
|
|
1056
|
+
fontFamily: "inherit"
|
|
1057
|
+
},
|
|
1058
|
+
onMouseEnter: (e) => {
|
|
1059
|
+
e.currentTarget.style.background = "var(--relaya-color-surface-2)";
|
|
1060
|
+
},
|
|
1061
|
+
onMouseLeave: (e) => {
|
|
1062
|
+
e.currentTarget.style.background = "none";
|
|
1063
|
+
},
|
|
1064
|
+
children: [/* @__PURE__ */ j("span", { children: r }), /* @__PURE__ */ j("span", { children: n })]
|
|
1065
|
+
});
|
|
1066
|
+
return n || s || c || l || u || d ? /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("div", {
|
|
1067
|
+
onClick: t,
|
|
1068
|
+
style: {
|
|
1069
|
+
position: "fixed",
|
|
1070
|
+
top: 0,
|
|
1071
|
+
left: 0,
|
|
1072
|
+
right: 0,
|
|
1073
|
+
bottom: 0,
|
|
1074
|
+
zIndex: 999
|
|
1075
|
+
}
|
|
1076
|
+
}), /* @__PURE__ */ M("div", {
|
|
1077
|
+
style: {
|
|
1078
|
+
position: "fixed",
|
|
1079
|
+
top: x,
|
|
1080
|
+
left: b,
|
|
1081
|
+
zIndex: 1e3,
|
|
1082
|
+
minWidth: "220px",
|
|
1083
|
+
background: "var(--relaya-color-surface)",
|
|
1084
|
+
border: "1px solid var(--relaya-color-border)",
|
|
1085
|
+
borderRadius: "var(--relaya-radius-md)",
|
|
1086
|
+
boxShadow: "0 4px 16px var(--relaya-color-shadow)",
|
|
1087
|
+
overflow: "hidden"
|
|
1088
|
+
},
|
|
1089
|
+
children: [
|
|
1090
|
+
n && /* @__PURE__ */ M(A, { children: [
|
|
1091
|
+
i && /* @__PURE__ */ j(C, {
|
|
1092
|
+
onClick: i,
|
|
1093
|
+
icon: "🌐",
|
|
1094
|
+
children: "Select gravatar image..."
|
|
1095
|
+
}),
|
|
1096
|
+
a && /* @__PURE__ */ j(C, {
|
|
1097
|
+
onClick: a,
|
|
1098
|
+
icon: "🌐",
|
|
1099
|
+
children: "Use default Gravatar"
|
|
1100
|
+
}),
|
|
1101
|
+
o && /* @__PURE__ */ j(C, {
|
|
1102
|
+
onClick: o,
|
|
1103
|
+
icon: "⭕",
|
|
1104
|
+
children: "Use initials (no avatar)"
|
|
1105
|
+
})
|
|
1106
|
+
] }),
|
|
1107
|
+
n && (s || c || l || u || d) && /* @__PURE__ */ j("div", { style: {
|
|
1108
|
+
height: "1px",
|
|
1109
|
+
background: "var(--relaya-color-border)",
|
|
1110
|
+
margin: "4px 0"
|
|
1111
|
+
} }),
|
|
1112
|
+
s && f && /* @__PURE__ */ j(C, {
|
|
1113
|
+
onClick: f,
|
|
1114
|
+
icon: "↩️",
|
|
1115
|
+
children: "Reply"
|
|
1116
|
+
}),
|
|
1117
|
+
s && (u || d || l) && /* @__PURE__ */ j("div", { style: {
|
|
1118
|
+
height: "1px",
|
|
1119
|
+
background: "var(--relaya-color-border)",
|
|
1120
|
+
margin: "4px 0"
|
|
1121
|
+
} }),
|
|
1122
|
+
c && p && /* @__PURE__ */ j(C, {
|
|
1123
|
+
onClick: p,
|
|
1124
|
+
icon: "✏️",
|
|
1125
|
+
children: "Edit message"
|
|
1126
|
+
}),
|
|
1127
|
+
u && h && /* @__PURE__ */ j(C, {
|
|
1128
|
+
onClick: h,
|
|
1129
|
+
icon: "🚩",
|
|
1130
|
+
children: "Report"
|
|
1131
|
+
}),
|
|
1132
|
+
d && g && /* @__PURE__ */ j(C, {
|
|
1133
|
+
onClick: g,
|
|
1134
|
+
icon: "🔨",
|
|
1135
|
+
children: "Ban user"
|
|
1136
|
+
}),
|
|
1137
|
+
l && m && /* @__PURE__ */ j(C, {
|
|
1138
|
+
onClick: m,
|
|
1139
|
+
icon: "🗑",
|
|
1140
|
+
danger: !0,
|
|
1141
|
+
children: "Delete"
|
|
1142
|
+
})
|
|
1143
|
+
]
|
|
1144
|
+
})] }) : null;
|
|
1145
|
+
}
|
|
1146
|
+
//#endregion
|
|
1147
|
+
//#region src/components/GravatarStyleModal.tsx
|
|
1148
|
+
var Ve = [
|
|
1149
|
+
{
|
|
1150
|
+
id: "identicon",
|
|
1151
|
+
label: "Identicon",
|
|
1152
|
+
icon: "🔷"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
id: "monsterid",
|
|
1156
|
+
label: "Monster",
|
|
1157
|
+
icon: "👾"
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
id: "retro",
|
|
1161
|
+
label: "Retro",
|
|
1162
|
+
icon: "🎮"
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
id: "wavatar",
|
|
1166
|
+
label: "Wavatar",
|
|
1167
|
+
icon: "🌊"
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
id: "robohash",
|
|
1171
|
+
label: "Robohash",
|
|
1172
|
+
icon: "🤖"
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
id: "mp",
|
|
1176
|
+
label: "Mystery Person",
|
|
1177
|
+
icon: "👤"
|
|
1178
|
+
}
|
|
1179
|
+
];
|
|
1180
|
+
function He({ stationSlug: e, getToken: t, onClose: n, onSelect: r, currentAvatarUrl: i }) {
|
|
1181
|
+
let [a, o] = k([]), [s, c] = k(!0), [l, u] = k(null), [d, f] = k(i), [p, m] = k(!1);
|
|
1182
|
+
E(() => {
|
|
1183
|
+
(async () => {
|
|
1184
|
+
try {
|
|
1185
|
+
let n = t(), r = {};
|
|
1186
|
+
n && (r.Authorization = `Bearer ${n}`);
|
|
1187
|
+
let i = await fetch(`/api/chat/${e}/me/gravatar/gallery`, { headers: r });
|
|
1188
|
+
if (!i.ok) throw Error("Failed to fetch gallery");
|
|
1189
|
+
o((await i.json()).gallery || []);
|
|
1190
|
+
} catch (e) {
|
|
1191
|
+
console.error("Error fetching Gravatar gallery:", e), u("Failed to load Gravatar gallery"), o([]);
|
|
1192
|
+
} finally {
|
|
1193
|
+
c(!1);
|
|
1194
|
+
}
|
|
1195
|
+
})();
|
|
1196
|
+
}, [e, t]);
|
|
1197
|
+
let h = async () => {
|
|
1198
|
+
if (d) {
|
|
1199
|
+
m(!0);
|
|
1200
|
+
try {
|
|
1201
|
+
await r(d, "gravatar"), n();
|
|
1202
|
+
} catch {
|
|
1203
|
+
u("Failed to update avatar"), m(!1);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}, g = (e) => {
|
|
1207
|
+
f(e);
|
|
1208
|
+
}, _ = (e) => {
|
|
1209
|
+
f(`https://www.gravatar.com/avatar/HASH?d=${e}&s=96`);
|
|
1210
|
+
};
|
|
1211
|
+
return /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("div", {
|
|
1212
|
+
onClick: n,
|
|
1213
|
+
style: {
|
|
1214
|
+
position: "fixed",
|
|
1215
|
+
top: 0,
|
|
1216
|
+
left: 0,
|
|
1217
|
+
right: 0,
|
|
1218
|
+
bottom: 0,
|
|
1219
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
1220
|
+
zIndex: 1e3,
|
|
1221
|
+
display: "flex",
|
|
1222
|
+
alignItems: "center",
|
|
1223
|
+
justifyContent: "center"
|
|
1224
|
+
}
|
|
1225
|
+
}), /* @__PURE__ */ M("div", {
|
|
1226
|
+
onClick: (e) => e.stopPropagation(),
|
|
1227
|
+
style: {
|
|
1228
|
+
position: "fixed",
|
|
1229
|
+
top: "50%",
|
|
1230
|
+
left: "50%",
|
|
1231
|
+
transform: "translate(-50%, -50%)",
|
|
1232
|
+
zIndex: 1001,
|
|
1233
|
+
width: "90%",
|
|
1234
|
+
maxWidth: "600px",
|
|
1235
|
+
maxHeight: "80vh",
|
|
1236
|
+
background: "var(--relaya-color-input-bg)",
|
|
1237
|
+
border: "1px solid var(--relaya-color-border)",
|
|
1238
|
+
borderRadius: "var(--relaya-radius-lg)",
|
|
1239
|
+
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
|
|
1240
|
+
display: "flex",
|
|
1241
|
+
flexDirection: "column",
|
|
1242
|
+
overflow: "hidden"
|
|
1243
|
+
},
|
|
1244
|
+
children: [
|
|
1245
|
+
/* @__PURE__ */ M("div", {
|
|
1246
|
+
style: {
|
|
1247
|
+
padding: "16px 20px",
|
|
1248
|
+
borderBottom: "1px solid var(--relaya-color-border)",
|
|
1249
|
+
display: "flex",
|
|
1250
|
+
alignItems: "center",
|
|
1251
|
+
justifyContent: "space-between"
|
|
1252
|
+
},
|
|
1253
|
+
children: [/* @__PURE__ */ j("h3", {
|
|
1254
|
+
style: {
|
|
1255
|
+
margin: 0,
|
|
1256
|
+
fontSize: "var(--relaya-font-size-lg)",
|
|
1257
|
+
fontWeight: 600
|
|
1258
|
+
},
|
|
1259
|
+
children: "Select Gravatar Image"
|
|
1260
|
+
}), /* @__PURE__ */ j("button", {
|
|
1261
|
+
onClick: n,
|
|
1262
|
+
style: {
|
|
1263
|
+
background: "none",
|
|
1264
|
+
border: "none",
|
|
1265
|
+
fontSize: "20px",
|
|
1266
|
+
cursor: "pointer",
|
|
1267
|
+
color: "var(--relaya-color-text-muted)",
|
|
1268
|
+
padding: "4px 8px",
|
|
1269
|
+
lineHeight: 1
|
|
1270
|
+
},
|
|
1271
|
+
children: "×"
|
|
1272
|
+
})]
|
|
1273
|
+
}),
|
|
1274
|
+
/* @__PURE__ */ M("div", {
|
|
1275
|
+
style: {
|
|
1276
|
+
padding: "20px",
|
|
1277
|
+
overflowY: "auto",
|
|
1278
|
+
flex: 1
|
|
1279
|
+
},
|
|
1280
|
+
children: [
|
|
1281
|
+
s && /* @__PURE__ */ j("div", { children: "Loading..." }),
|
|
1282
|
+
l && /* @__PURE__ */ j("div", {
|
|
1283
|
+
style: {
|
|
1284
|
+
color: "var(--relaya-color-danger)",
|
|
1285
|
+
marginBottom: "16px"
|
|
1286
|
+
},
|
|
1287
|
+
children: l
|
|
1288
|
+
}),
|
|
1289
|
+
!s && /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ M("div", {
|
|
1290
|
+
style: { marginBottom: "24px" },
|
|
1291
|
+
children: [/* @__PURE__ */ j("h4", {
|
|
1292
|
+
style: {
|
|
1293
|
+
fontSize: "var(--relaya-font-size-sm)",
|
|
1294
|
+
fontWeight: 600,
|
|
1295
|
+
marginBottom: "12px",
|
|
1296
|
+
color: "var(--relaya-color-text-muted)"
|
|
1297
|
+
},
|
|
1298
|
+
children: "Your Uploaded Images (from gravatar.com)"
|
|
1299
|
+
}), a.length > 0 ? /* @__PURE__ */ j("div", {
|
|
1300
|
+
style: {
|
|
1301
|
+
display: "grid",
|
|
1302
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(80px, 1fr))",
|
|
1303
|
+
gap: "12px"
|
|
1304
|
+
},
|
|
1305
|
+
children: a.map((e, t) => /* @__PURE__ */ M("div", {
|
|
1306
|
+
onClick: () => g(e.url),
|
|
1307
|
+
style: {
|
|
1308
|
+
cursor: "pointer",
|
|
1309
|
+
border: d === e.url ? "2px solid var(--relaya-color-accent)" : "2px solid transparent",
|
|
1310
|
+
borderRadius: "var(--relaya-radius-sm)",
|
|
1311
|
+
padding: "4px"
|
|
1312
|
+
},
|
|
1313
|
+
children: [/* @__PURE__ */ j("img", {
|
|
1314
|
+
src: e.url,
|
|
1315
|
+
alt: e.alt || "Gallery image",
|
|
1316
|
+
style: {
|
|
1317
|
+
width: "100%",
|
|
1318
|
+
height: "auto",
|
|
1319
|
+
borderRadius: "var(--relaya-radius-sm)",
|
|
1320
|
+
display: "block"
|
|
1321
|
+
}
|
|
1322
|
+
}), e.alt && /* @__PURE__ */ j("div", {
|
|
1323
|
+
style: {
|
|
1324
|
+
fontSize: "10px",
|
|
1325
|
+
color: "var(--relaya-color-text-muted)",
|
|
1326
|
+
marginTop: "4px",
|
|
1327
|
+
textAlign: "center"
|
|
1328
|
+
},
|
|
1329
|
+
children: e.alt
|
|
1330
|
+
})]
|
|
1331
|
+
}, t))
|
|
1332
|
+
}) : /* @__PURE__ */ M("div", {
|
|
1333
|
+
style: {
|
|
1334
|
+
fontSize: "var(--relaya-font-size-sm)",
|
|
1335
|
+
color: "var(--relaya-color-text-muted)",
|
|
1336
|
+
padding: "12px 0"
|
|
1337
|
+
},
|
|
1338
|
+
children: [
|
|
1339
|
+
"Upload some images to your Gravatar ",
|
|
1340
|
+
/* @__PURE__ */ j("strong", { children: /* @__PURE__ */ j("em", { children: "photos" }) }),
|
|
1341
|
+
" section to show them here"
|
|
1342
|
+
]
|
|
1343
|
+
})]
|
|
1344
|
+
}), /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("h4", {
|
|
1345
|
+
style: {
|
|
1346
|
+
fontSize: "var(--relaya-font-size-sm)",
|
|
1347
|
+
fontWeight: 600,
|
|
1348
|
+
marginBottom: "12px",
|
|
1349
|
+
color: "var(--relaya-color-text-muted)"
|
|
1350
|
+
},
|
|
1351
|
+
children: a.length > 0 ? "Generated Alternatives" : "Gravatar Generated Styles"
|
|
1352
|
+
}), /* @__PURE__ */ j("div", {
|
|
1353
|
+
style: {
|
|
1354
|
+
display: "grid",
|
|
1355
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(80px, 1fr))",
|
|
1356
|
+
gap: "12px"
|
|
1357
|
+
},
|
|
1358
|
+
children: Ve.map((e) => /* @__PURE__ */ M("div", {
|
|
1359
|
+
onClick: () => _(e.id),
|
|
1360
|
+
style: {
|
|
1361
|
+
cursor: "pointer",
|
|
1362
|
+
border: "2px solid transparent",
|
|
1363
|
+
borderRadius: "var(--relaya-radius-sm)",
|
|
1364
|
+
padding: "8px",
|
|
1365
|
+
textAlign: "center"
|
|
1366
|
+
},
|
|
1367
|
+
children: [/* @__PURE__ */ j("div", {
|
|
1368
|
+
style: {
|
|
1369
|
+
fontSize: "32px",
|
|
1370
|
+
marginBottom: "4px"
|
|
1371
|
+
},
|
|
1372
|
+
children: e.icon
|
|
1373
|
+
}), /* @__PURE__ */ j("div", {
|
|
1374
|
+
style: {
|
|
1375
|
+
fontSize: "11px",
|
|
1376
|
+
color: "var(--relaya-color-text-muted)"
|
|
1377
|
+
},
|
|
1378
|
+
children: e.label
|
|
1379
|
+
})]
|
|
1380
|
+
}, e.id))
|
|
1381
|
+
})] })] })
|
|
1382
|
+
]
|
|
1383
|
+
}),
|
|
1384
|
+
/* @__PURE__ */ M("div", {
|
|
1385
|
+
style: {
|
|
1386
|
+
padding: "12px 20px",
|
|
1387
|
+
borderTop: "1px solid var(--relaya-color-border)",
|
|
1388
|
+
display: "flex",
|
|
1389
|
+
gap: "8px",
|
|
1390
|
+
justifyContent: "flex-end"
|
|
1391
|
+
},
|
|
1392
|
+
children: [/* @__PURE__ */ j("button", {
|
|
1393
|
+
onClick: n,
|
|
1394
|
+
disabled: p,
|
|
1395
|
+
style: {
|
|
1396
|
+
padding: "8px 16px",
|
|
1397
|
+
borderRadius: "var(--relaya-radius-sm)",
|
|
1398
|
+
border: "1px solid var(--relaya-color-border)",
|
|
1399
|
+
background: "var(--relaya-color-input-bg)",
|
|
1400
|
+
color: "var(--relaya-color-text)",
|
|
1401
|
+
cursor: "pointer",
|
|
1402
|
+
fontSize: "var(--relaya-font-size-sm)"
|
|
1403
|
+
},
|
|
1404
|
+
children: "Cancel"
|
|
1405
|
+
}), /* @__PURE__ */ j("button", {
|
|
1406
|
+
onClick: h,
|
|
1407
|
+
disabled: !d || p,
|
|
1408
|
+
style: {
|
|
1409
|
+
padding: "8px 16px",
|
|
1410
|
+
borderRadius: "var(--relaya-radius-sm)",
|
|
1411
|
+
border: "none",
|
|
1412
|
+
background: d && !p ? "var(--relaya-color-accent)" : "var(--relaya-color-surface-2)",
|
|
1413
|
+
color: d && !p ? "#ffffff" : "var(--relaya-color-text-muted)",
|
|
1414
|
+
cursor: d && !p ? "pointer" : "not-allowed",
|
|
1415
|
+
fontSize: "var(--relaya-font-size-sm)"
|
|
1416
|
+
},
|
|
1417
|
+
children: p ? "Saving..." : "Select Image"
|
|
1418
|
+
})]
|
|
1419
|
+
})
|
|
1420
|
+
]
|
|
1421
|
+
})] });
|
|
1422
|
+
}
|
|
1423
|
+
//#endregion
|
|
1424
|
+
//#region src/components/messageItemUtils.ts
|
|
1425
|
+
function Ue(e) {
|
|
1426
|
+
let t = typeof e == "string" ? new Date(e) : e, n = /* @__PURE__ */ new Date(), r = t.getFullYear() === n.getFullYear() && t.getMonth() === n.getMonth() && t.getDate() === n.getDate(), i = t.toLocaleTimeString([], {
|
|
1427
|
+
hour: "2-digit",
|
|
1428
|
+
minute: "2-digit"
|
|
1429
|
+
});
|
|
1430
|
+
return r ? i : `${t.toLocaleDateString([], {
|
|
1431
|
+
month: "short",
|
|
1432
|
+
day: "numeric"
|
|
1433
|
+
})}, ${i}`;
|
|
1434
|
+
}
|
|
1435
|
+
function We(e) {
|
|
1436
|
+
return (typeof e == "string" ? new Date(e) : e) > /* @__PURE__ */ new Date(Date.now() - 900 * 1e3);
|
|
1437
|
+
}
|
|
1438
|
+
function Ge(e) {
|
|
1439
|
+
return J(e) ? q(e).find((e) => e.isImage && e.url)?.url ?? null : null;
|
|
1440
|
+
}
|
|
1441
|
+
function Ke(e) {
|
|
1442
|
+
let t = e.split("/").pop() ?? "image", n = t.split("?")[0] ?? t;
|
|
1443
|
+
return decodeURIComponent(n).replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ") || "Sticker image";
|
|
1444
|
+
}
|
|
1445
|
+
//#endregion
|
|
1446
|
+
//#region src/components/ChatImage.tsx
|
|
1447
|
+
function qe({ url: e, bare: t = !1, title: n }) {
|
|
1448
|
+
let [r, i] = k(!1), [a, o] = k(!1);
|
|
1449
|
+
return /* @__PURE__ */ M("span", {
|
|
1450
|
+
className: ["chat-image", t ? "chat-image--bare" : "chat-image--inline"].join(" "),
|
|
1451
|
+
title: n,
|
|
1452
|
+
children: [!r && !a && /* @__PURE__ */ M("span", {
|
|
1453
|
+
className: "chat-image__loading",
|
|
1454
|
+
"aria-label": "Loading image",
|
|
1455
|
+
children: [
|
|
1456
|
+
/* @__PURE__ */ j("span", {}),
|
|
1457
|
+
/* @__PURE__ */ j("span", {}),
|
|
1458
|
+
/* @__PURE__ */ j("span", {})
|
|
1459
|
+
]
|
|
1460
|
+
}), a ? /* @__PURE__ */ j("span", {
|
|
1461
|
+
className: "chat-image__fallback",
|
|
1462
|
+
children: "Image unavailable"
|
|
1463
|
+
}) : /* @__PURE__ */ j("img", {
|
|
1464
|
+
src: e,
|
|
1465
|
+
alt: Ke(e),
|
|
1466
|
+
loading: "lazy",
|
|
1467
|
+
className: "chat-image__img",
|
|
1468
|
+
title: n,
|
|
1469
|
+
style: { opacity: +!!r },
|
|
1470
|
+
onLoad: () => i(!0),
|
|
1471
|
+
onError: () => {
|
|
1472
|
+
i(!1), o(!0);
|
|
1473
|
+
}
|
|
1474
|
+
})]
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
//#endregion
|
|
1478
|
+
//#region src/components/MentionRenderer.tsx
|
|
1479
|
+
var Je = /@\[([^\]|]+)\|([^\]]+)\]/g, Ye = /(https?:\/\/\S+)/g;
|
|
1480
|
+
function Xe(e) {
|
|
1481
|
+
return e.replace(/[.,;:!?)]+$/, "");
|
|
1482
|
+
}
|
|
1483
|
+
function Ze(e, t) {
|
|
1484
|
+
if (!e.includes("@")) return e;
|
|
1485
|
+
let n = e.split(/(@[a-zA-Z0-9_-]+)/g);
|
|
1486
|
+
return n.length === 1 ? e : /* @__PURE__ */ j(S.Fragment, { children: n.map((e, t) => /^@[a-zA-Z0-9_-]+$/.test(e) ? /* @__PURE__ */ j("strong", { children: e }, t) : /* @__PURE__ */ j(S.Fragment, { children: e }, t)) }, `legacy-${t}`);
|
|
1487
|
+
}
|
|
1488
|
+
function Qe(e, t) {
|
|
1489
|
+
let n = e.split(Ye);
|
|
1490
|
+
return n.length === 1 ? $e(e, t) : /* @__PURE__ */ j(A, { children: n.map((e, n) => {
|
|
1491
|
+
if (n % 2 == 1) {
|
|
1492
|
+
let t = Xe(e), r = e.slice(t.length);
|
|
1493
|
+
return /* @__PURE__ */ M(S.Fragment, { children: [/* @__PURE__ */ j("a", {
|
|
1494
|
+
href: t,
|
|
1495
|
+
className: "message-link",
|
|
1496
|
+
target: "_blank",
|
|
1497
|
+
rel: "noopener noreferrer",
|
|
1498
|
+
children: t
|
|
1499
|
+
}), r] }, `link-${n}`);
|
|
1500
|
+
}
|
|
1501
|
+
return e ? /* @__PURE__ */ j(S.Fragment, { children: $e(e, t) }, `text-${n}`) : null;
|
|
1502
|
+
}) });
|
|
1503
|
+
}
|
|
1504
|
+
function $e(e, t) {
|
|
1505
|
+
if (!e.includes("@")) return e;
|
|
1506
|
+
let n = [], r = 0;
|
|
1507
|
+
Je.lastIndex = 0;
|
|
1508
|
+
let i = Je.exec(e);
|
|
1509
|
+
for (; i !== null;) {
|
|
1510
|
+
let [a, o, s] = i, c = i.index;
|
|
1511
|
+
c > r && n.push(Ze(e.slice(r, c), `${r}`));
|
|
1512
|
+
let l = !!t && s.trim() === t;
|
|
1513
|
+
n.push(/* @__PURE__ */ M("span", {
|
|
1514
|
+
className: `mention-chip${l ? " mention-chip--self" : ""}`,
|
|
1515
|
+
children: ["@", o]
|
|
1516
|
+
}, `mention-${c}`)), r = c + a.length, i = Je.exec(e);
|
|
1517
|
+
}
|
|
1518
|
+
return r < e.length && n.push(Ze(e.slice(r), `${r}-end`)), n.length === 0 ? e : n.length === 1 ? n[0] : /* @__PURE__ */ j(A, { children: n });
|
|
1519
|
+
}
|
|
1520
|
+
function et({ content: e, stickers: t, currentUserId: n }) {
|
|
1521
|
+
return /* @__PURE__ */ j(A, { children: q(e).map((e, r) => {
|
|
1522
|
+
if (e.isImage && e.url) {
|
|
1523
|
+
let n = t.find((t) => t.url === e.url && t.shortcode);
|
|
1524
|
+
return /* @__PURE__ */ j(qe, {
|
|
1525
|
+
url: e.url,
|
|
1526
|
+
title: n?.shortcode ? `:${n.shortcode}:` : void 0
|
|
1527
|
+
}, `img-${r}-${e.url}`);
|
|
1528
|
+
}
|
|
1529
|
+
return /* @__PURE__ */ j(S.Fragment, { children: Qe(e.text, n) }, `txt-${r}`);
|
|
1530
|
+
}) });
|
|
1531
|
+
}
|
|
1532
|
+
//#endregion
|
|
1533
|
+
//#region src/components/OptimisticMessageItem.tsx
|
|
1534
|
+
function tt({ msg: e, stickers: t, currentUserId: n, onRetry: r }) {
|
|
1535
|
+
let i = ae(e.content, t), a = e.authorId === n, o = q(i).some((e) => e.isImage), s = Ge(i), c = !!s, l = e.status === "sending" ? "Sending…" : e.status === "failed" ? "⚠ Failed" : "";
|
|
1536
|
+
return /* @__PURE__ */ M("div", {
|
|
1537
|
+
className: [
|
|
1538
|
+
"message-item",
|
|
1539
|
+
a ? "message-item--own" : "message-item--other",
|
|
1540
|
+
o ? "message-item--has-image" : "",
|
|
1541
|
+
c ? "message-item--image" : "",
|
|
1542
|
+
`message-item--${e.status === "failed" ? "failed" : "optimistic"}`
|
|
1543
|
+
].join(" "),
|
|
1544
|
+
children: [
|
|
1545
|
+
a && /* @__PURE__ */ j($, {
|
|
1546
|
+
displayName: e.authorDisplayName,
|
|
1547
|
+
avatarUrl: e.authorAvatarUrl
|
|
1548
|
+
}),
|
|
1549
|
+
!a && /* @__PURE__ */ M("div", {
|
|
1550
|
+
className: "message-item__avatar-column",
|
|
1551
|
+
children: [/* @__PURE__ */ j("span", {
|
|
1552
|
+
className: "message-item__author",
|
|
1553
|
+
children: e.authorDisplayName
|
|
1554
|
+
}), /* @__PURE__ */ j($, {
|
|
1555
|
+
displayName: e.authorDisplayName,
|
|
1556
|
+
avatarUrl: e.authorAvatarUrl
|
|
1557
|
+
})]
|
|
1558
|
+
}),
|
|
1559
|
+
/* @__PURE__ */ M("div", {
|
|
1560
|
+
className: "message-item__bubble-wrap",
|
|
1561
|
+
children: [
|
|
1562
|
+
/* @__PURE__ */ j("div", {
|
|
1563
|
+
className: "message-item__name-row",
|
|
1564
|
+
children: /* @__PURE__ */ j("span", {
|
|
1565
|
+
className: "message-item__time",
|
|
1566
|
+
children: Ue(e.createdAt)
|
|
1567
|
+
})
|
|
1568
|
+
}),
|
|
1569
|
+
c && s ? /* @__PURE__ */ j("div", {
|
|
1570
|
+
className: "message-item__bare-image-wrap",
|
|
1571
|
+
children: /* @__PURE__ */ j(qe, {
|
|
1572
|
+
url: s,
|
|
1573
|
+
bare: !0
|
|
1574
|
+
})
|
|
1575
|
+
}) : /* @__PURE__ */ j("div", {
|
|
1576
|
+
className: "message-item__bubble",
|
|
1577
|
+
children: /* @__PURE__ */ j(et, {
|
|
1578
|
+
content: i,
|
|
1579
|
+
stickers: t,
|
|
1580
|
+
currentUserId: n
|
|
1581
|
+
})
|
|
1582
|
+
}),
|
|
1583
|
+
l && /* @__PURE__ */ M("div", {
|
|
1584
|
+
className: "message-item__status-row",
|
|
1585
|
+
children: [/* @__PURE__ */ j("span", {
|
|
1586
|
+
className: "message-item__status",
|
|
1587
|
+
children: l
|
|
1588
|
+
}), e.status === "failed" && r && /* @__PURE__ */ j("button", {
|
|
1589
|
+
style: {
|
|
1590
|
+
fontSize: 11,
|
|
1591
|
+
color: "var(--color-accent)"
|
|
1592
|
+
},
|
|
1593
|
+
onClick: () => r(e.clientId),
|
|
1594
|
+
children: "Retry"
|
|
1595
|
+
})]
|
|
1596
|
+
})
|
|
1597
|
+
]
|
|
1598
|
+
})
|
|
1599
|
+
]
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
//#endregion
|
|
1603
|
+
//#region src/components/MessageEditForm.tsx
|
|
1604
|
+
function nt({ editContent: e, editError: t, onChange: n, onSave: r, onCancel: i }) {
|
|
1605
|
+
return /* @__PURE__ */ M("div", {
|
|
1606
|
+
className: "message-item__edit-form",
|
|
1607
|
+
children: [
|
|
1608
|
+
/* @__PURE__ */ j("textarea", {
|
|
1609
|
+
value: e,
|
|
1610
|
+
onChange: (e) => n(e.target.value),
|
|
1611
|
+
maxLength: 2e3,
|
|
1612
|
+
autoFocus: !0,
|
|
1613
|
+
className: "message-item__edit-textarea",
|
|
1614
|
+
rows: 3
|
|
1615
|
+
}),
|
|
1616
|
+
t && /* @__PURE__ */ j("div", {
|
|
1617
|
+
className: "message-item__edit-error",
|
|
1618
|
+
children: t
|
|
1619
|
+
}),
|
|
1620
|
+
/* @__PURE__ */ M("div", {
|
|
1621
|
+
className: "message-item__edit-actions",
|
|
1622
|
+
children: [/* @__PURE__ */ j("button", {
|
|
1623
|
+
className: "btn btn--primary btn--sm",
|
|
1624
|
+
onClick: r,
|
|
1625
|
+
children: "Save"
|
|
1626
|
+
}), /* @__PURE__ */ j("button", {
|
|
1627
|
+
className: "btn btn--secondary btn--sm",
|
|
1628
|
+
onClick: i,
|
|
1629
|
+
children: "Cancel"
|
|
1630
|
+
})]
|
|
1631
|
+
})
|
|
1632
|
+
]
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
//#endregion
|
|
1636
|
+
//#region src/components/MessageItem.tsx
|
|
1637
|
+
function rt({ item: t, stickers: n, currentUserId: r, currentUserPermissions: i, stationSlug: a, getToken: o, onDelete: s, onBan: c, onReport: l, onReply: u, onEdit: d, onRetry: f, getUserInfo: p, getAvatarForMessage: m }) {
|
|
1638
|
+
let [h, _] = k(!1), [v, y] = k(!1), [b, x] = k(!1), [S, C] = k(null), [w, T] = k(!1), [E, D] = k(""), [N, P] = k(null), [F, I] = k(!1), [ee, L] = k(!1), [R, z] = k(!1), B = O(null), V = O(null), H = O(null), te = i.includes(e.DELETE_ANY), ne = i.includes(e.BAN_USER), re = i.includes(e.REPORT), ie = i.includes(e.EDIT_OWN);
|
|
1639
|
+
if (t.kind === "optimistic") return /* @__PURE__ */ j(tt, {
|
|
1640
|
+
msg: t.msg,
|
|
1641
|
+
stickers: n,
|
|
1642
|
+
currentUserId: r,
|
|
1643
|
+
onRetry: f
|
|
1644
|
+
});
|
|
1645
|
+
let U = t.msg, W = U.user_id === r, G = U.is_deleted, K = U.user_id, oe = p(K), J = oe?.displayName ?? "Unknown User", se = oe?.isModerator ?? !1, ce = m(K, new Date(U.created_at)), Y = U.content ?? "", X = G ? Y : ae(Y, n), le = !G && q(X).some((e) => e.isImage), ue = G ? null : Ge(X), de = !!ue, Z = K !== r, fe = !G && W && ie && (U.edit_count ?? 0) < 2 && We(U.created_at) && !!d, pe = !G && (te || i.includes(e.DELETE_OWN) && W), me = !G && ne && Z, he = !G && re && Z, ge = !G && Z, _e = fe || pe || me || he || W || ge, ve = async () => {
|
|
1646
|
+
if (!R) {
|
|
1647
|
+
try {
|
|
1648
|
+
let e = o(), t = {};
|
|
1649
|
+
e && (t.Authorization = `Bearer ${e}`);
|
|
1650
|
+
let n = await fetch(`/api/chat/${a}/me/gravatar/gallery`, { headers: t });
|
|
1651
|
+
n.ok && L(((await n.json()).gallery || []).length > 0);
|
|
1652
|
+
} catch (e) {
|
|
1653
|
+
console.error("Failed to fetch Gravatar gallery:", e);
|
|
1654
|
+
}
|
|
1655
|
+
z(!0);
|
|
1656
|
+
}
|
|
1657
|
+
}, ye = async (e) => {
|
|
1658
|
+
e.preventDefault(), e.stopPropagation(), W && await ve(), C({
|
|
1659
|
+
x: e.clientX,
|
|
1660
|
+
y: e.clientY
|
|
1661
|
+
}), x(!0);
|
|
1662
|
+
}, be = async (e) => {
|
|
1663
|
+
e.stopPropagation(), W && await ve();
|
|
1664
|
+
let t = B.current?.getBoundingClientRect();
|
|
1665
|
+
t && C({
|
|
1666
|
+
x: t.right,
|
|
1667
|
+
y: t.bottom + 2
|
|
1668
|
+
}), x(!0);
|
|
1669
|
+
}, Q = () => {
|
|
1670
|
+
x(!1), C(null);
|
|
1671
|
+
}, xe = (e) => {
|
|
1672
|
+
if (W || G) return;
|
|
1673
|
+
let t = e.touches[0];
|
|
1674
|
+
H.current = {
|
|
1675
|
+
x: t.clientX,
|
|
1676
|
+
y: t.clientY
|
|
1677
|
+
}, V.current = setTimeout(() => {
|
|
1678
|
+
C({
|
|
1679
|
+
x: t.clientX,
|
|
1680
|
+
y: t.clientY
|
|
1681
|
+
}), x(!0);
|
|
1682
|
+
}, 500);
|
|
1683
|
+
}, Se = () => {
|
|
1684
|
+
V.current &&= (clearTimeout(V.current), null);
|
|
1685
|
+
}, Ce = (e) => {
|
|
1686
|
+
if (!H.current) return;
|
|
1687
|
+
let t = e.touches[0], n = t.clientX - H.current.x, r = t.clientY - H.current.y;
|
|
1688
|
+
(Math.abs(n) > 10 || Math.abs(r) > 10) && (V.current &&= (clearTimeout(V.current), null));
|
|
1689
|
+
}, we = () => {
|
|
1690
|
+
D(Y), P(null), T(!0), Q();
|
|
1691
|
+
}, Te = () => {
|
|
1692
|
+
T(!1), D(""), P(null);
|
|
1693
|
+
}, Ee = async () => {
|
|
1694
|
+
if (!d) return;
|
|
1695
|
+
let e = E.trim();
|
|
1696
|
+
if (!e) {
|
|
1697
|
+
P("Message cannot be empty");
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (e.length > 2e3) {
|
|
1701
|
+
P("Message exceeds 2000 characters");
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
try {
|
|
1705
|
+
await d(U.id, e), T(!1), D(""), P(null);
|
|
1706
|
+
} catch {
|
|
1707
|
+
P("Failed to edit message");
|
|
1708
|
+
}
|
|
1709
|
+
}, De = U.edited_at || U.created_at, Oe = U.edited_at ? "Edited: " : "", ke = Ue(De);
|
|
1710
|
+
return /* @__PURE__ */ M(A, { children: [
|
|
1711
|
+
/* @__PURE__ */ M("div", {
|
|
1712
|
+
className: [
|
|
1713
|
+
"message-item",
|
|
1714
|
+
W ? "message-item--own" : "message-item--other",
|
|
1715
|
+
le ? "message-item--has-image" : "",
|
|
1716
|
+
de ? "message-item--image" : "",
|
|
1717
|
+
G ? "message-item--deleted" : ""
|
|
1718
|
+
].filter(Boolean).join(" "),
|
|
1719
|
+
onContextMenu: !G && _e ? ye : void 0,
|
|
1720
|
+
onTouchStart: xe,
|
|
1721
|
+
onTouchEnd: Se,
|
|
1722
|
+
onTouchMove: Ce,
|
|
1723
|
+
children: [
|
|
1724
|
+
W && !G && /* @__PURE__ */ j($, {
|
|
1725
|
+
displayName: J,
|
|
1726
|
+
avatarUrl: ce
|
|
1727
|
+
}),
|
|
1728
|
+
!W && /* @__PURE__ */ j("div", {
|
|
1729
|
+
className: "message-item__avatar-column",
|
|
1730
|
+
children: /* @__PURE__ */ j($, {
|
|
1731
|
+
displayName: J,
|
|
1732
|
+
avatarUrl: ce
|
|
1733
|
+
})
|
|
1734
|
+
}),
|
|
1735
|
+
/* @__PURE__ */ M("div", {
|
|
1736
|
+
className: "message-item__bubble-wrap",
|
|
1737
|
+
children: [/* @__PURE__ */ M("div", {
|
|
1738
|
+
className: "message-item__name-row",
|
|
1739
|
+
children: [
|
|
1740
|
+
!W && !G && /* @__PURE__ */ j("span", {
|
|
1741
|
+
className: `message-item__author${se ? " message-item__author--mod" : ""}`,
|
|
1742
|
+
children: J
|
|
1743
|
+
}),
|
|
1744
|
+
/* @__PURE__ */ M("span", {
|
|
1745
|
+
className: "message-item__time",
|
|
1746
|
+
children: [Oe, ke]
|
|
1747
|
+
}),
|
|
1748
|
+
_e && /* @__PURE__ */ j("button", {
|
|
1749
|
+
ref: B,
|
|
1750
|
+
className: "message-item__kebab-btn",
|
|
1751
|
+
onClick: be,
|
|
1752
|
+
"aria-label": "Message actions",
|
|
1753
|
+
children: "⋮"
|
|
1754
|
+
})
|
|
1755
|
+
]
|
|
1756
|
+
}), G ? /* @__PURE__ */ j("div", {
|
|
1757
|
+
className: "message-item__bubble",
|
|
1758
|
+
children: "Message removed"
|
|
1759
|
+
}) : w ? /* @__PURE__ */ j(nt, {
|
|
1760
|
+
editContent: E,
|
|
1761
|
+
editError: N,
|
|
1762
|
+
onChange: D,
|
|
1763
|
+
onSave: Ee,
|
|
1764
|
+
onCancel: Te
|
|
1765
|
+
}) : de && ue ? /* @__PURE__ */ j("div", {
|
|
1766
|
+
className: "message-item__bare-image-wrap",
|
|
1767
|
+
children: /* @__PURE__ */ j(qe, {
|
|
1768
|
+
url: ue,
|
|
1769
|
+
bare: !0
|
|
1770
|
+
})
|
|
1771
|
+
}) : /* @__PURE__ */ M("div", {
|
|
1772
|
+
className: "message-item__bubble",
|
|
1773
|
+
children: [U.reply_excerpt && /* @__PURE__ */ j("div", {
|
|
1774
|
+
className: "message-reply-bubble",
|
|
1775
|
+
children: /* @__PURE__ */ M("div", {
|
|
1776
|
+
className: "message-reply-bubble__text",
|
|
1777
|
+
children: [/* @__PURE__ */ j("div", {
|
|
1778
|
+
className: "message-reply-bubble__author",
|
|
1779
|
+
children: U.reply_author_name
|
|
1780
|
+
}), /* @__PURE__ */ j("div", {
|
|
1781
|
+
className: "message-reply-bubble__excerpt",
|
|
1782
|
+
children: U.reply_excerpt
|
|
1783
|
+
})]
|
|
1784
|
+
})
|
|
1785
|
+
}), /* @__PURE__ */ j(et, {
|
|
1786
|
+
content: X,
|
|
1787
|
+
stickers: n,
|
|
1788
|
+
currentUserId: r
|
|
1789
|
+
})]
|
|
1790
|
+
})]
|
|
1791
|
+
})
|
|
1792
|
+
]
|
|
1793
|
+
}),
|
|
1794
|
+
b && S && !G && _e && /* @__PURE__ */ j(Be, {
|
|
1795
|
+
position: S,
|
|
1796
|
+
onClose: Q,
|
|
1797
|
+
showAvatarOptions: W,
|
|
1798
|
+
hasGalleryImages: ee,
|
|
1799
|
+
onSelectGravatarPhoto: () => {
|
|
1800
|
+
Q(), I(!0);
|
|
1801
|
+
},
|
|
1802
|
+
onUseDefaultGravatar: async () => {
|
|
1803
|
+
try {
|
|
1804
|
+
let e = o();
|
|
1805
|
+
if (!(await fetch(`/api/chat/${a}/me/avatar/preference`, {
|
|
1806
|
+
method: "PATCH",
|
|
1807
|
+
headers: {
|
|
1808
|
+
"Content-Type": "application/json",
|
|
1809
|
+
...e ? { Authorization: `Bearer ${e}` } : {}
|
|
1810
|
+
},
|
|
1811
|
+
body: JSON.stringify({ preference: "default" })
|
|
1812
|
+
})).ok) throw Error("Failed to update avatar preference");
|
|
1813
|
+
Q();
|
|
1814
|
+
} catch (e) {
|
|
1815
|
+
console.error("Failed to set default Gravatar:", e), alert("Failed to update avatar. Please try again.");
|
|
1816
|
+
}
|
|
1817
|
+
},
|
|
1818
|
+
onUseInitials: async () => {
|
|
1819
|
+
try {
|
|
1820
|
+
let e = o();
|
|
1821
|
+
if (!(await fetch(`/api/chat/${a}/me/avatar/preference`, {
|
|
1822
|
+
method: "PATCH",
|
|
1823
|
+
headers: {
|
|
1824
|
+
"Content-Type": "application/json",
|
|
1825
|
+
...e ? { Authorization: `Bearer ${e}` } : {}
|
|
1826
|
+
},
|
|
1827
|
+
body: JSON.stringify({ preference: null })
|
|
1828
|
+
})).ok) throw Error("Failed to update avatar preference");
|
|
1829
|
+
Q();
|
|
1830
|
+
} catch (e) {
|
|
1831
|
+
console.error("Failed to clear avatar:", e), alert("Failed to update avatar. Please try again.");
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
showReply: ge,
|
|
1835
|
+
showEdit: fe,
|
|
1836
|
+
showDelete: pe,
|
|
1837
|
+
showReport: he,
|
|
1838
|
+
showBan: me,
|
|
1839
|
+
onReply: () => {
|
|
1840
|
+
Q(), u(U.id, J, Y);
|
|
1841
|
+
},
|
|
1842
|
+
onEdit: we,
|
|
1843
|
+
onDelete: () => {
|
|
1844
|
+
Q(), s(U.id);
|
|
1845
|
+
},
|
|
1846
|
+
onReport: () => {
|
|
1847
|
+
Q(), _(!0);
|
|
1848
|
+
},
|
|
1849
|
+
onBan: () => {
|
|
1850
|
+
Q(), y(!0);
|
|
1851
|
+
}
|
|
1852
|
+
}),
|
|
1853
|
+
F && /* @__PURE__ */ j(He, {
|
|
1854
|
+
stationSlug: a,
|
|
1855
|
+
getToken: o,
|
|
1856
|
+
onClose: () => I(!1),
|
|
1857
|
+
onSelect: async (e, t) => {
|
|
1858
|
+
try {
|
|
1859
|
+
let t = o(), n = e.startsWith("https://") ? {
|
|
1860
|
+
preference: "gravatar",
|
|
1861
|
+
avatarUrl: e
|
|
1862
|
+
} : {
|
|
1863
|
+
preference: "gravatar",
|
|
1864
|
+
style: e.split("d=")[1]?.split("&")[0]
|
|
1865
|
+
};
|
|
1866
|
+
if (!(await fetch(`/api/chat/${a}/me/avatar/preference`, {
|
|
1867
|
+
method: "PATCH",
|
|
1868
|
+
headers: {
|
|
1869
|
+
"Content-Type": "application/json",
|
|
1870
|
+
...t ? { Authorization: `Bearer ${t}` } : {}
|
|
1871
|
+
},
|
|
1872
|
+
body: JSON.stringify(n)
|
|
1873
|
+
})).ok) throw Error("Failed to update avatar preference");
|
|
1874
|
+
I(!1);
|
|
1875
|
+
} catch (e) {
|
|
1876
|
+
throw console.error("Failed to select avatar:", e), e;
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
currentAvatarUrl: ce
|
|
1880
|
+
}),
|
|
1881
|
+
h && /* @__PURE__ */ j(Re, {
|
|
1882
|
+
messageId: U.id,
|
|
1883
|
+
authorName: J,
|
|
1884
|
+
onReport: l,
|
|
1885
|
+
onClose: () => _(!1)
|
|
1886
|
+
}),
|
|
1887
|
+
v && /* @__PURE__ */ j(g, {
|
|
1888
|
+
userId: K,
|
|
1889
|
+
displayName: J,
|
|
1890
|
+
onBan: c,
|
|
1891
|
+
onClose: () => y(!1)
|
|
1892
|
+
})
|
|
1893
|
+
] });
|
|
1894
|
+
}
|
|
1895
|
+
//#endregion
|
|
1896
|
+
//#region src/components/MessageList.tsx
|
|
1897
|
+
function it({ messages: e, optimistic: t, stickers: n, currentUserId: r, currentUserPermissions: i, stationSlug: a, getToken: o, loadingInitial: s, loadingOlder: c, hasOlderMessages: l, retentionCutoff: u, onLoadOlder: d, onEdit: f, onDelete: p, onBan: m, onReport: h, onReply: g, onRetry: _, getUserInfo: v, getAvatarForMessage: y }) {
|
|
1898
|
+
let b = O(null), [x, S] = k(!0), [C, w] = k(!1), T = O(!0);
|
|
1899
|
+
E(() => {
|
|
1900
|
+
x && !T.current && b.current && b.current.scrollTo({
|
|
1901
|
+
top: b.current.scrollHeight,
|
|
1902
|
+
behavior: "smooth"
|
|
1903
|
+
});
|
|
1904
|
+
}, [
|
|
1905
|
+
e.length,
|
|
1906
|
+
t.length,
|
|
1907
|
+
x
|
|
1908
|
+
]), E(() => {
|
|
1909
|
+
!s && e.length > 0 && T.current && requestAnimationFrame(() => {
|
|
1910
|
+
requestAnimationFrame(() => {
|
|
1911
|
+
b.current && (b.current.scrollTop = b.current.scrollHeight, T.current = !1);
|
|
1912
|
+
});
|
|
1913
|
+
});
|
|
1914
|
+
}, [s, e.length]);
|
|
1915
|
+
function D() {
|
|
1916
|
+
let e = b.current;
|
|
1917
|
+
if (!e) return;
|
|
1918
|
+
let t = e.scrollHeight - e.scrollTop - e.clientHeight < 80;
|
|
1919
|
+
S(t), w(!t);
|
|
1920
|
+
}
|
|
1921
|
+
function A() {
|
|
1922
|
+
b.current?.scrollTo({
|
|
1923
|
+
top: b.current.scrollHeight,
|
|
1924
|
+
behavior: "smooth"
|
|
1925
|
+
}), S(!0), w(!1);
|
|
1926
|
+
}
|
|
1927
|
+
let N = [...e.map((e) => ({
|
|
1928
|
+
kind: "server",
|
|
1929
|
+
msg: e
|
|
1930
|
+
})), ...t.map((e) => ({
|
|
1931
|
+
kind: "optimistic",
|
|
1932
|
+
msg: e
|
|
1933
|
+
}))], P = !l && u !== null, F = u ? new Date(u).toLocaleDateString(void 0, {
|
|
1934
|
+
year: "numeric",
|
|
1935
|
+
month: "long",
|
|
1936
|
+
day: "numeric"
|
|
1937
|
+
}) : null;
|
|
1938
|
+
return s ? /* @__PURE__ */ j("div", {
|
|
1939
|
+
className: "message-list-container",
|
|
1940
|
+
children: /* @__PURE__ */ M("div", {
|
|
1941
|
+
className: "messages-empty",
|
|
1942
|
+
children: [/* @__PURE__ */ j("span", { className: "connection-spinner" }), " Loading messages…"]
|
|
1943
|
+
})
|
|
1944
|
+
}) : /* @__PURE__ */ M("div", {
|
|
1945
|
+
className: "message-list-container",
|
|
1946
|
+
children: [
|
|
1947
|
+
l && /* @__PURE__ */ j("div", {
|
|
1948
|
+
className: "load-older-btn",
|
|
1949
|
+
children: /* @__PURE__ */ j("button", {
|
|
1950
|
+
onClick: d,
|
|
1951
|
+
disabled: c,
|
|
1952
|
+
children: c ? "Loading…" : "↑ Load older messages"
|
|
1953
|
+
})
|
|
1954
|
+
}),
|
|
1955
|
+
/* @__PURE__ */ M("div", {
|
|
1956
|
+
className: "message-list",
|
|
1957
|
+
ref: b,
|
|
1958
|
+
onScroll: D,
|
|
1959
|
+
children: [
|
|
1960
|
+
P && F && /* @__PURE__ */ M("div", {
|
|
1961
|
+
className: "retention-boundary",
|
|
1962
|
+
children: [
|
|
1963
|
+
"Chat history before ",
|
|
1964
|
+
F,
|
|
1965
|
+
" is not available on this plan."
|
|
1966
|
+
]
|
|
1967
|
+
}),
|
|
1968
|
+
N.length === 0 && !P && /* @__PURE__ */ j("div", {
|
|
1969
|
+
className: "messages-empty",
|
|
1970
|
+
children: "No messages yet. Be the first to say something! 👋"
|
|
1971
|
+
}),
|
|
1972
|
+
N.map((e) => /* @__PURE__ */ j(rt, {
|
|
1973
|
+
item: e,
|
|
1974
|
+
stickers: n,
|
|
1975
|
+
currentUserId: r,
|
|
1976
|
+
currentUserPermissions: i,
|
|
1977
|
+
stationSlug: a,
|
|
1978
|
+
getToken: o,
|
|
1979
|
+
onEdit: f,
|
|
1980
|
+
onDelete: p,
|
|
1981
|
+
onBan: m,
|
|
1982
|
+
onReport: h,
|
|
1983
|
+
onReply: g,
|
|
1984
|
+
onRetry: _,
|
|
1985
|
+
getUserInfo: v,
|
|
1986
|
+
getAvatarForMessage: y
|
|
1987
|
+
}, e.kind === "server" ? e.msg.id : `opt-${e.msg.clientId}`))
|
|
1988
|
+
]
|
|
1989
|
+
}),
|
|
1990
|
+
C && /* @__PURE__ */ j("button", {
|
|
1991
|
+
className: "scroll-to-bottom",
|
|
1992
|
+
onClick: A,
|
|
1993
|
+
title: "Scroll to latest",
|
|
1994
|
+
children: "↓"
|
|
1995
|
+
})
|
|
1996
|
+
]
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
//#endregion
|
|
2000
|
+
//#region src/hooks/useStickerPicker.ts
|
|
2001
|
+
function at({ isConnected: e, stationSlug: t, onRefreshStickers: n }) {
|
|
2002
|
+
let [r, i] = k(!1), [a, o] = k(!1), [s, c] = k(null), l = O(null), u = O(null), d = w(() => i(!1), []), f = w(() => {
|
|
2003
|
+
e && i((e) => !e);
|
|
2004
|
+
}, [e]);
|
|
2005
|
+
return E(() => {
|
|
2006
|
+
if (!r) return;
|
|
2007
|
+
async function e() {
|
|
2008
|
+
if (n) {
|
|
2009
|
+
o(!0), c(null);
|
|
2010
|
+
try {
|
|
2011
|
+
await n();
|
|
2012
|
+
} catch {
|
|
2013
|
+
c("Could not load stickers right now.");
|
|
2014
|
+
} finally {
|
|
2015
|
+
o(!1);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
e().catch(() => void 0);
|
|
2020
|
+
}, [
|
|
2021
|
+
r,
|
|
2022
|
+
n,
|
|
2023
|
+
t
|
|
2024
|
+
]), E(() => {
|
|
2025
|
+
if (!r) return;
|
|
2026
|
+
let e = (e) => {
|
|
2027
|
+
e.key === "Escape" && d();
|
|
2028
|
+
}, t = (e) => {
|
|
2029
|
+
let t = e.target;
|
|
2030
|
+
if (!t) return;
|
|
2031
|
+
let n = l.current?.contains(t) ?? !1, r = u.current?.contains(t) ?? !1;
|
|
2032
|
+
!n && !r && d();
|
|
2033
|
+
};
|
|
2034
|
+
return document.addEventListener("keydown", e), document.addEventListener("mousedown", t), () => {
|
|
2035
|
+
document.removeEventListener("keydown", e), document.removeEventListener("mousedown", t);
|
|
2036
|
+
};
|
|
2037
|
+
}, [r, d]), E(() => {
|
|
2038
|
+
!e && r && d();
|
|
2039
|
+
}, [
|
|
2040
|
+
e,
|
|
2041
|
+
r,
|
|
2042
|
+
d
|
|
2043
|
+
]), {
|
|
2044
|
+
pickerOpen: r,
|
|
2045
|
+
pickerLoading: a,
|
|
2046
|
+
pickerError: s,
|
|
2047
|
+
pickerRef: l,
|
|
2048
|
+
stickerBtnRef: u,
|
|
2049
|
+
closePicker: d,
|
|
2050
|
+
togglePicker: f
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
//#endregion
|
|
2054
|
+
//#region src/hooks/useMentionSuggestions.ts
|
|
2055
|
+
function ot(e, t, n, r, i, a, o) {
|
|
2056
|
+
let [s, c] = k(-1), [l, u] = k(!1), d = i ? pe(n, i.query, r) : [];
|
|
2057
|
+
E(() => {
|
|
2058
|
+
c(-1), u(!1);
|
|
2059
|
+
}, [i?.query]);
|
|
2060
|
+
let f = !a && !!i && d.length > 0 && !l, p = D(() => {
|
|
2061
|
+
if (n.length === 0) return;
|
|
2062
|
+
let e = n.reduce((e, t) => Math.max(e, t.displayName.length), 0);
|
|
2063
|
+
return Math.min(320, Math.max(120, e * 8 + 52));
|
|
2064
|
+
}, [n]);
|
|
2065
|
+
function m(n) {
|
|
2066
|
+
let r = he(e, i?.start ?? t, i?.end ?? t, n);
|
|
2067
|
+
o(r.nextText, r.caretPosition);
|
|
2068
|
+
}
|
|
2069
|
+
return {
|
|
2070
|
+
mentionSuggestions: d,
|
|
2071
|
+
mentionHighlight: s,
|
|
2072
|
+
setMentionHighlight: c,
|
|
2073
|
+
mentionStripDismissed: l,
|
|
2074
|
+
setMentionStripDismissed: u,
|
|
2075
|
+
mentionStripVisible: f,
|
|
2076
|
+
mentionStripWidth: p,
|
|
2077
|
+
insertMention: m
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
//#endregion
|
|
2081
|
+
//#region src/components/StickerPickerDialog.tsx
|
|
2082
|
+
function st(e) {
|
|
2083
|
+
return e.replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ") || "Sticker";
|
|
2084
|
+
}
|
|
2085
|
+
function ct({ pickerRef: e, pickerLoading: t, pickerError: n, pickerStickers: r, onClose: i, onInsert: a }) {
|
|
2086
|
+
return /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("div", {
|
|
2087
|
+
className: "sticker-picker__overlay",
|
|
2088
|
+
onClick: i
|
|
2089
|
+
}), /* @__PURE__ */ M("div", {
|
|
2090
|
+
className: "sticker-picker",
|
|
2091
|
+
ref: e,
|
|
2092
|
+
role: "dialog",
|
|
2093
|
+
"aria-label": "Sticker picker",
|
|
2094
|
+
children: [/* @__PURE__ */ M("div", {
|
|
2095
|
+
className: "sticker-picker__header",
|
|
2096
|
+
children: [/* @__PURE__ */ j("span", { children: "Stickers" }), /* @__PURE__ */ j("button", {
|
|
2097
|
+
type: "button",
|
|
2098
|
+
className: "btn btn--icon sticker-picker__close",
|
|
2099
|
+
onClick: i,
|
|
2100
|
+
"aria-label": "Close sticker picker",
|
|
2101
|
+
title: "Close",
|
|
2102
|
+
children: "✕"
|
|
2103
|
+
})]
|
|
2104
|
+
}), /* @__PURE__ */ M("div", {
|
|
2105
|
+
className: "sticker-picker__body",
|
|
2106
|
+
children: [
|
|
2107
|
+
t && /* @__PURE__ */ j("div", {
|
|
2108
|
+
className: "sticker-picker__state",
|
|
2109
|
+
children: "Loading stickers…"
|
|
2110
|
+
}),
|
|
2111
|
+
!t && n && /* @__PURE__ */ j("div", {
|
|
2112
|
+
className: "sticker-picker__state",
|
|
2113
|
+
children: n
|
|
2114
|
+
}),
|
|
2115
|
+
!t && !n && r.length === 0 && /* @__PURE__ */ j("div", {
|
|
2116
|
+
className: "sticker-picker__state",
|
|
2117
|
+
children: "No stickers are available yet. Ask a station admin to upload them in the sticker manager."
|
|
2118
|
+
}),
|
|
2119
|
+
!t && !n && r.length > 0 && /* @__PURE__ */ j("div", {
|
|
2120
|
+
className: "sticker-picker__grid",
|
|
2121
|
+
children: r.map((e) => /* @__PURE__ */ M("button", {
|
|
2122
|
+
type: "button",
|
|
2123
|
+
className: "sticker-picker__item",
|
|
2124
|
+
onClick: () => a(e),
|
|
2125
|
+
title: `:${e.shortcode}:`,
|
|
2126
|
+
"aria-label": `Insert sticker ${st(e.filename)}`,
|
|
2127
|
+
children: [/* @__PURE__ */ j("img", {
|
|
2128
|
+
src: e.url,
|
|
2129
|
+
alt: st(e.filename),
|
|
2130
|
+
loading: "lazy"
|
|
2131
|
+
}), /* @__PURE__ */ M("span", {
|
|
2132
|
+
className: "sticker-picker__item-label",
|
|
2133
|
+
children: [
|
|
2134
|
+
":",
|
|
2135
|
+
e.shortcode,
|
|
2136
|
+
":"
|
|
2137
|
+
]
|
|
2138
|
+
})]
|
|
2139
|
+
}, e.filename))
|
|
2140
|
+
})
|
|
2141
|
+
]
|
|
2142
|
+
})]
|
|
2143
|
+
})] });
|
|
2144
|
+
}
|
|
2145
|
+
//#endregion
|
|
2146
|
+
//#region src/components/MessageInput.tsx
|
|
2147
|
+
function lt({ onSend: e, connectionStatus: t, canPost: n, onRequestAuth: r, stationSlug: i, getToken: a, stickers: o, onRefreshStickers: s, replyingTo: c, onCancelReply: l, onlineUsers: u = [], currentUserId: d }) {
|
|
2148
|
+
let [f, p] = k(""), [m, h] = k(0), g = O(null), _ = t === "connected", { pickerOpen: v, pickerLoading: y, pickerError: b, pickerRef: x, stickerBtnRef: S, closePicker: C, togglePicker: w } = at({
|
|
2149
|
+
isConnected: _,
|
|
2150
|
+
stationSlug: i,
|
|
2151
|
+
onRefreshStickers: s
|
|
2152
|
+
}), T = ge(f, m), D = T ? ve(o, T.query) : [], A = o.filter((e) => !!e.shortcode);
|
|
2153
|
+
function N(e, t) {
|
|
2154
|
+
p(e), C(), requestAnimationFrame(() => {
|
|
2155
|
+
let e = g.current;
|
|
2156
|
+
e && (e.focus(), e.setSelectionRange(t, t), h(t), e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, 120)}px`);
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
let { mentionSuggestions: P, mentionHighlight: F, setMentionHighlight: I, setMentionStripDismissed: ee, mentionStripVisible: L, mentionStripWidth: R, insertMention: z } = ot(f, m, u, d, fe(f, m), v, N);
|
|
2160
|
+
function B() {
|
|
2161
|
+
let t = f.trim();
|
|
2162
|
+
if (!(!t || !_)) {
|
|
2163
|
+
if (!n) {
|
|
2164
|
+
r(), p(""), g.current && (g.current.style.height = "auto");
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
e(t), p(""), g.current && (g.current.style.height = "auto");
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
function V(e) {
|
|
2171
|
+
if (L) {
|
|
2172
|
+
if (e.key === "ArrowDown") {
|
|
2173
|
+
e.preventDefault(), I((e) => (e + 1) % P.length);
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2176
|
+
if (e.key === "ArrowUp") {
|
|
2177
|
+
e.preventDefault(), I((e) => e <= 0 ? P.length - 1 : e - 1);
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
if (e.key === "Escape") {
|
|
2181
|
+
e.preventDefault(), I(-1), ee(!0);
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
if (e.key === "Enter" && F >= 0) {
|
|
2185
|
+
e.preventDefault(), z(P[F]);
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (e.key === "Tab" && P.length === 1) {
|
|
2190
|
+
e.preventDefault(), z(P[0]);
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
if (e.key === " ") {
|
|
2194
|
+
let t = me(f, m, u, d);
|
|
2195
|
+
if (t) {
|
|
2196
|
+
e.preventDefault();
|
|
2197
|
+
let n = he(f, t.start, t.end, t.user);
|
|
2198
|
+
N(n.nextText, n.caretPosition);
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), B());
|
|
2203
|
+
}
|
|
2204
|
+
function H() {
|
|
2205
|
+
let e = g.current;
|
|
2206
|
+
e && (e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, 120)}px`);
|
|
2207
|
+
}
|
|
2208
|
+
function te(e) {
|
|
2209
|
+
let t = g.current, n = e.shortcode ? `:${e.shortcode}: ` : `${e.url} `;
|
|
2210
|
+
if (!t) {
|
|
2211
|
+
let e = `${f}${n}`;
|
|
2212
|
+
N(e, e.length);
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
let r = t.selectionStart ?? f.length, i = t.selectionEnd ?? f.length;
|
|
2216
|
+
N(`${f.slice(0, r)}${n}${f.slice(i)}`, r + n.length);
|
|
2217
|
+
}
|
|
2218
|
+
function ne(e) {
|
|
2219
|
+
let t = g.current, n = _e(f, T?.start ?? t?.selectionStart ?? f.length, T?.end ?? t?.selectionEnd ?? f.length, e);
|
|
2220
|
+
N(n.nextText, n.caretPosition);
|
|
2221
|
+
}
|
|
2222
|
+
E(() => {
|
|
2223
|
+
let e = (e) => {
|
|
2224
|
+
e.key === "Escape" && c && !v && l();
|
|
2225
|
+
};
|
|
2226
|
+
return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
|
|
2227
|
+
}, [
|
|
2228
|
+
c,
|
|
2229
|
+
v,
|
|
2230
|
+
l
|
|
2231
|
+
]), E(() => {
|
|
2232
|
+
c && g.current && g.current.focus();
|
|
2233
|
+
}, [c]);
|
|
2234
|
+
let re = _ ? "Message…" : "Connecting…";
|
|
2235
|
+
return /* @__PURE__ */ M("div", {
|
|
2236
|
+
className: "message-input-container",
|
|
2237
|
+
children: [c && /* @__PURE__ */ M("div", {
|
|
2238
|
+
className: "reply-preview",
|
|
2239
|
+
children: [/* @__PURE__ */ M("div", {
|
|
2240
|
+
className: "reply-preview__content",
|
|
2241
|
+
children: [/* @__PURE__ */ j("div", { className: "reply-preview__line" }), /* @__PURE__ */ M("div", {
|
|
2242
|
+
className: "reply-preview__text",
|
|
2243
|
+
children: [/* @__PURE__ */ j("div", {
|
|
2244
|
+
className: "reply-preview__author",
|
|
2245
|
+
children: c.authorName
|
|
2246
|
+
}), /* @__PURE__ */ j("div", {
|
|
2247
|
+
className: "reply-preview__excerpt",
|
|
2248
|
+
children: c.excerpt
|
|
2249
|
+
})]
|
|
2250
|
+
})]
|
|
2251
|
+
}), /* @__PURE__ */ j("button", {
|
|
2252
|
+
type: "button",
|
|
2253
|
+
className: "reply-preview__cancel",
|
|
2254
|
+
onClick: l,
|
|
2255
|
+
"aria-label": "Cancel reply",
|
|
2256
|
+
title: "Cancel reply (Esc)",
|
|
2257
|
+
children: "✕"
|
|
2258
|
+
})]
|
|
2259
|
+
}), /* @__PURE__ */ M("div", {
|
|
2260
|
+
className: "message-input-bar",
|
|
2261
|
+
children: [
|
|
2262
|
+
/* @__PURE__ */ j("button", {
|
|
2263
|
+
type: "button",
|
|
2264
|
+
ref: S,
|
|
2265
|
+
className: "sticker-btn",
|
|
2266
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2267
|
+
onClick: w,
|
|
2268
|
+
disabled: !_,
|
|
2269
|
+
title: "Open sticker picker",
|
|
2270
|
+
"aria-label": "Open sticker picker",
|
|
2271
|
+
children: "☺"
|
|
2272
|
+
}),
|
|
2273
|
+
/* @__PURE__ */ j("textarea", {
|
|
2274
|
+
ref: g,
|
|
2275
|
+
className: "message-input",
|
|
2276
|
+
value: f,
|
|
2277
|
+
onChange: (e) => {
|
|
2278
|
+
p(e.target.value), h(e.target.selectionStart ?? e.target.value.length);
|
|
2279
|
+
},
|
|
2280
|
+
onKeyDown: V,
|
|
2281
|
+
onInput: H,
|
|
2282
|
+
onClick: (e) => h(e.currentTarget.selectionStart ?? f.length),
|
|
2283
|
+
onKeyUp: (e) => h(e.currentTarget.selectionStart ?? f.length),
|
|
2284
|
+
onSelect: (e) => h(e.currentTarget.selectionStart ?? f.length),
|
|
2285
|
+
placeholder: re,
|
|
2286
|
+
disabled: !_,
|
|
2287
|
+
rows: 1,
|
|
2288
|
+
maxLength: 2e3
|
|
2289
|
+
}),
|
|
2290
|
+
/* @__PURE__ */ j("button", {
|
|
2291
|
+
type: "button",
|
|
2292
|
+
className: "send-btn",
|
|
2293
|
+
onClick: B,
|
|
2294
|
+
disabled: !_ || !f.trim(),
|
|
2295
|
+
title: "Send (Enter)",
|
|
2296
|
+
"aria-label": "Send message",
|
|
2297
|
+
children: "↑"
|
|
2298
|
+
}),
|
|
2299
|
+
L && /* @__PURE__ */ j("div", {
|
|
2300
|
+
className: "mention-suggestions",
|
|
2301
|
+
role: "listbox",
|
|
2302
|
+
"aria-label": "Mention suggestions",
|
|
2303
|
+
style: R ? { width: R } : void 0,
|
|
2304
|
+
children: P.map((e, t) => /* @__PURE__ */ M("button", {
|
|
2305
|
+
type: "button",
|
|
2306
|
+
className: `mention-suggestions__item${t === F ? " mention-suggestions__item--highlighted" : ""}`,
|
|
2307
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2308
|
+
onClick: () => z(e),
|
|
2309
|
+
children: [/* @__PURE__ */ j("span", {
|
|
2310
|
+
className: "mention-suggestions__at",
|
|
2311
|
+
children: "@"
|
|
2312
|
+
}), /* @__PURE__ */ j("span", {
|
|
2313
|
+
className: "mention-suggestions__name",
|
|
2314
|
+
children: e.displayName
|
|
2315
|
+
})]
|
|
2316
|
+
}, e.id))
|
|
2317
|
+
}),
|
|
2318
|
+
!v && T && D.length > 0 && /* @__PURE__ */ j("div", {
|
|
2319
|
+
className: "sticker-suggestions",
|
|
2320
|
+
role: "listbox",
|
|
2321
|
+
"aria-label": "Sticker shortcode suggestions",
|
|
2322
|
+
children: D.map((e) => /* @__PURE__ */ M("button", {
|
|
2323
|
+
type: "button",
|
|
2324
|
+
className: "sticker-suggestions__item",
|
|
2325
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2326
|
+
onClick: () => ne(e.shortcode),
|
|
2327
|
+
children: [/* @__PURE__ */ j("img", {
|
|
2328
|
+
src: e.url,
|
|
2329
|
+
alt: e.shortcode ?? "",
|
|
2330
|
+
loading: "lazy"
|
|
2331
|
+
}), /* @__PURE__ */ M("span", {
|
|
2332
|
+
className: "sticker-suggestions__code",
|
|
2333
|
+
children: [
|
|
2334
|
+
":",
|
|
2335
|
+
e.shortcode,
|
|
2336
|
+
":"
|
|
2337
|
+
]
|
|
2338
|
+
})]
|
|
2339
|
+
}, e.filename))
|
|
2340
|
+
}),
|
|
2341
|
+
v && /* @__PURE__ */ j(ct, {
|
|
2342
|
+
pickerRef: x,
|
|
2343
|
+
pickerLoading: y,
|
|
2344
|
+
pickerError: b,
|
|
2345
|
+
pickerStickers: A,
|
|
2346
|
+
onClose: C,
|
|
2347
|
+
onInsert: te
|
|
2348
|
+
})
|
|
2349
|
+
]
|
|
2350
|
+
})]
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
//#endregion
|
|
2354
|
+
//#region src/components/UserList.tsx
|
|
2355
|
+
function ut(e) {
|
|
2356
|
+
return e.split(" ").map((e) => e[0]).join("").toUpperCase().slice(0, 2);
|
|
2357
|
+
}
|
|
2358
|
+
function dt({ users: e, currentUserId: t, style: n }) {
|
|
2359
|
+
return /* @__PURE__ */ M("div", {
|
|
2360
|
+
className: "user-list",
|
|
2361
|
+
style: n,
|
|
2362
|
+
children: [/* @__PURE__ */ M("div", {
|
|
2363
|
+
className: "user-list__title",
|
|
2364
|
+
children: ["Online — ", e.length]
|
|
2365
|
+
}), e.map((e) => /* @__PURE__ */ M("div", {
|
|
2366
|
+
className: "user-list__item",
|
|
2367
|
+
children: [
|
|
2368
|
+
/* @__PURE__ */ j("div", {
|
|
2369
|
+
className: "user-list__avatar",
|
|
2370
|
+
children: ut(e.displayName)
|
|
2371
|
+
}),
|
|
2372
|
+
/* @__PURE__ */ j("span", {
|
|
2373
|
+
className: "user-list__name",
|
|
2374
|
+
children: e.displayName
|
|
2375
|
+
}),
|
|
2376
|
+
e.id === t && /* @__PURE__ */ j("span", {
|
|
2377
|
+
className: "user-list__self-badge",
|
|
2378
|
+
children: "you"
|
|
2379
|
+
})
|
|
2380
|
+
]
|
|
2381
|
+
}, e.id))]
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
//#endregion
|
|
2385
|
+
//#region src/components/ConnectionStatus.tsx
|
|
2386
|
+
var ft = {
|
|
2387
|
+
disconnected: "Disconnected — check your connection",
|
|
2388
|
+
connecting: "Connecting…",
|
|
2389
|
+
reconnecting: "Reconnecting…",
|
|
2390
|
+
connected: ""
|
|
2391
|
+
};
|
|
2392
|
+
function pt({ status: e }) {
|
|
2393
|
+
return e === "connected" ? null : /* @__PURE__ */ M("div", {
|
|
2394
|
+
className: `connection-status connection-status--${e}`,
|
|
2395
|
+
children: [(e === "connecting" || e === "reconnecting") && /* @__PURE__ */ j("span", { className: "connection-spinner" }), ft[e]]
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
//#endregion
|
|
2399
|
+
//#region src/components/ChatNameEditor.tsx
|
|
2400
|
+
function mt({ stationSlug: e, initialChatName: t, getToken: n, onUpdated: r }) {
|
|
2401
|
+
let [i, a] = k(t), [o, s] = k(!1), [c, l] = k(""), [u, d] = k(!1), [f, p] = k(null), m = O(null), h = O(new b("", n)).current;
|
|
2402
|
+
E(() => {
|
|
2403
|
+
o || a(t);
|
|
2404
|
+
}, [t]);
|
|
2405
|
+
let g = w(() => {
|
|
2406
|
+
l(i ?? ""), p(null), s(!0), setTimeout(() => m.current?.focus(), 0);
|
|
2407
|
+
}, [i]), _ = w(() => {
|
|
2408
|
+
s(!1), p(null);
|
|
2409
|
+
}, []), v = w(async () => {
|
|
2410
|
+
let t = c.trim();
|
|
2411
|
+
if (t.length > 40) {
|
|
2412
|
+
p("Name must be 40 characters or fewer");
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
d(!0), p(null);
|
|
2416
|
+
try {
|
|
2417
|
+
let n = await h.updateChatName(e, t.length === 0 ? null : t);
|
|
2418
|
+
a(n.chatName), s(!1), r?.(n.chatName, n.displayName);
|
|
2419
|
+
} catch (e) {
|
|
2420
|
+
p(e?.message ?? "Failed to save chat name");
|
|
2421
|
+
} finally {
|
|
2422
|
+
d(!1);
|
|
2423
|
+
}
|
|
2424
|
+
}, [
|
|
2425
|
+
h,
|
|
2426
|
+
c,
|
|
2427
|
+
e,
|
|
2428
|
+
r
|
|
2429
|
+
]), y = w((e) => {
|
|
2430
|
+
e.key === "Enter" && v(), e.key === "Escape" && _();
|
|
2431
|
+
}, [v, _]);
|
|
2432
|
+
return /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("button", {
|
|
2433
|
+
className: "btn btn--ghost chat-name-btn",
|
|
2434
|
+
onClick: g,
|
|
2435
|
+
title: i ? "Change your display name" : "Set your display name",
|
|
2436
|
+
"aria-label": i ? `Display name: ${i}. Click to change.` : "Set your display name",
|
|
2437
|
+
children: /* @__PURE__ */ j("span", {
|
|
2438
|
+
className: "chat-name-btn__label",
|
|
2439
|
+
children: i ?? "Set name"
|
|
2440
|
+
})
|
|
2441
|
+
}), o && /* @__PURE__ */ j("div", {
|
|
2442
|
+
className: "modal-overlay",
|
|
2443
|
+
onClick: _,
|
|
2444
|
+
children: /* @__PURE__ */ M("div", {
|
|
2445
|
+
className: "modal",
|
|
2446
|
+
onClick: (e) => e.stopPropagation(),
|
|
2447
|
+
children: [
|
|
2448
|
+
/* @__PURE__ */ j("div", {
|
|
2449
|
+
className: "modal__title",
|
|
2450
|
+
children: i ? "Change display name" : "Set display name"
|
|
2451
|
+
}),
|
|
2452
|
+
/* @__PURE__ */ M("div", {
|
|
2453
|
+
className: "modal__body",
|
|
2454
|
+
children: [
|
|
2455
|
+
/* @__PURE__ */ j("p", { children: "This is how other listeners will see you in chat." }),
|
|
2456
|
+
/* @__PURE__ */ j("input", {
|
|
2457
|
+
ref: m,
|
|
2458
|
+
type: "text",
|
|
2459
|
+
value: c,
|
|
2460
|
+
maxLength: 40,
|
|
2461
|
+
placeholder: "Your chat name (max 40 characters)",
|
|
2462
|
+
onChange: (e) => l(e.target.value),
|
|
2463
|
+
onKeyDown: y,
|
|
2464
|
+
disabled: u,
|
|
2465
|
+
"aria-label": "Chat display name"
|
|
2466
|
+
}),
|
|
2467
|
+
f && /* @__PURE__ */ j("p", {
|
|
2468
|
+
style: {
|
|
2469
|
+
color: "var(--color-danger)",
|
|
2470
|
+
fontSize: "var(--font-size-xs)",
|
|
2471
|
+
marginTop: "var(--spacing-xs)"
|
|
2472
|
+
},
|
|
2473
|
+
children: f
|
|
2474
|
+
})
|
|
2475
|
+
]
|
|
2476
|
+
}),
|
|
2477
|
+
/* @__PURE__ */ M("div", {
|
|
2478
|
+
className: "modal__footer",
|
|
2479
|
+
children: [/* @__PURE__ */ j("button", {
|
|
2480
|
+
className: "btn btn--ghost",
|
|
2481
|
+
onClick: _,
|
|
2482
|
+
disabled: u,
|
|
2483
|
+
children: "Cancel"
|
|
2484
|
+
}), /* @__PURE__ */ j("button", {
|
|
2485
|
+
className: "btn btn--primary",
|
|
2486
|
+
onClick: v,
|
|
2487
|
+
disabled: u,
|
|
2488
|
+
style: { width: "auto" },
|
|
2489
|
+
children: u ? "…" : "Save"
|
|
2490
|
+
})]
|
|
2491
|
+
})
|
|
2492
|
+
]
|
|
2493
|
+
})
|
|
2494
|
+
})] });
|
|
2495
|
+
}
|
|
2496
|
+
//#endregion
|
|
2497
|
+
//#region src/components/UserListModal.tsx
|
|
2498
|
+
function ht(e) {
|
|
2499
|
+
return e.split(" ").map((e) => e[0]).join("").toUpperCase().slice(0, 2);
|
|
2500
|
+
}
|
|
2501
|
+
function gt({ users: e, currentUserId: t, onClose: n }) {
|
|
2502
|
+
let r = w((e) => {
|
|
2503
|
+
e.key === "Escape" && n();
|
|
2504
|
+
}, [n]);
|
|
2505
|
+
return E(() => (document.addEventListener("keydown", r), () => document.removeEventListener("keydown", r)), [r]), /* @__PURE__ */ j("div", {
|
|
2506
|
+
className: "modal-overlay",
|
|
2507
|
+
onClick: n,
|
|
2508
|
+
children: /* @__PURE__ */ M("div", {
|
|
2509
|
+
className: "modal user-list-modal",
|
|
2510
|
+
onClick: (e) => e.stopPropagation(),
|
|
2511
|
+
children: [/* @__PURE__ */ M("div", {
|
|
2512
|
+
className: "modal__title user-list-modal__header",
|
|
2513
|
+
children: [/* @__PURE__ */ M("span", { children: ["Online — ", e.length] }), /* @__PURE__ */ j("button", {
|
|
2514
|
+
className: "btn btn--icon user-list-modal__close",
|
|
2515
|
+
onClick: n,
|
|
2516
|
+
"aria-label": "Close",
|
|
2517
|
+
title: "Close",
|
|
2518
|
+
children: "✕"
|
|
2519
|
+
})]
|
|
2520
|
+
}), /* @__PURE__ */ j("div", {
|
|
2521
|
+
className: "modal__body user-list-modal__body",
|
|
2522
|
+
children: e.length === 0 ? /* @__PURE__ */ j("p", {
|
|
2523
|
+
style: { color: "var(--color-text-muted)" },
|
|
2524
|
+
children: "No listeners online."
|
|
2525
|
+
}) : e.map((e) => /* @__PURE__ */ M("div", {
|
|
2526
|
+
className: "user-list__item user-list-modal__item",
|
|
2527
|
+
children: [
|
|
2528
|
+
/* @__PURE__ */ j("div", {
|
|
2529
|
+
className: "user-list__avatar",
|
|
2530
|
+
children: ht(e.displayName)
|
|
2531
|
+
}),
|
|
2532
|
+
/* @__PURE__ */ j("span", {
|
|
2533
|
+
className: "user-list__name",
|
|
2534
|
+
children: e.displayName
|
|
2535
|
+
}),
|
|
2536
|
+
e.id === t && /* @__PURE__ */ j("span", {
|
|
2537
|
+
className: "user-list__self-badge",
|
|
2538
|
+
children: "you"
|
|
2539
|
+
})
|
|
2540
|
+
]
|
|
2541
|
+
}, e.id))
|
|
2542
|
+
})]
|
|
2543
|
+
})
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
//#endregion
|
|
2547
|
+
//#region src/components/OTPCodeInput.tsx
|
|
2548
|
+
function _t({ email: e, pendingId: t, onVerify: n, onResend: r, onChangeEmail: i, isSubmitting: a, error: o }) {
|
|
2549
|
+
let [s, c] = k(""), [l, u] = k(30), [d, f] = k(!1), p = O(null);
|
|
2550
|
+
E(() => {
|
|
2551
|
+
p.current?.focus();
|
|
2552
|
+
}, []), E(() => {
|
|
2553
|
+
if (l > 0) {
|
|
2554
|
+
let e = setTimeout(() => u(l - 1), 1e3);
|
|
2555
|
+
return () => clearTimeout(e);
|
|
2556
|
+
}
|
|
2557
|
+
}, [l]), E(() => {
|
|
2558
|
+
s.length === 6 && !a && h();
|
|
2559
|
+
}, [s]);
|
|
2560
|
+
function m(e) {
|
|
2561
|
+
c(e.replace(/\D/g, "").slice(0, 6));
|
|
2562
|
+
}
|
|
2563
|
+
async function h() {
|
|
2564
|
+
s.length !== 6 || a || await n(s);
|
|
2565
|
+
}
|
|
2566
|
+
async function g() {
|
|
2567
|
+
if (!(l > 0 || d)) {
|
|
2568
|
+
f(!0);
|
|
2569
|
+
try {
|
|
2570
|
+
await r(), u(30), c("");
|
|
2571
|
+
} finally {
|
|
2572
|
+
f(!1);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
return /* @__PURE__ */ M("div", {
|
|
2577
|
+
className: "otp-code-input",
|
|
2578
|
+
children: [
|
|
2579
|
+
/* @__PURE__ */ j("div", {
|
|
2580
|
+
className: "otp-code-input__icon",
|
|
2581
|
+
children: "🔑"
|
|
2582
|
+
}),
|
|
2583
|
+
/* @__PURE__ */ j("h2", {
|
|
2584
|
+
className: "otp-code-input__title",
|
|
2585
|
+
children: "Enter verification code"
|
|
2586
|
+
}),
|
|
2587
|
+
/* @__PURE__ */ M("p", {
|
|
2588
|
+
className: "otp-code-input__hint",
|
|
2589
|
+
children: ["We sent a 6-digit code to ", /* @__PURE__ */ j("strong", { children: e })]
|
|
2590
|
+
}),
|
|
2591
|
+
/* @__PURE__ */ j("input", {
|
|
2592
|
+
ref: p,
|
|
2593
|
+
type: "text",
|
|
2594
|
+
inputMode: "numeric",
|
|
2595
|
+
pattern: "[0-9]*",
|
|
2596
|
+
className: "otp-code-input__field",
|
|
2597
|
+
placeholder: "000000",
|
|
2598
|
+
value: s,
|
|
2599
|
+
onChange: (e) => m(e.target.value),
|
|
2600
|
+
disabled: a,
|
|
2601
|
+
autoComplete: "one-time-code",
|
|
2602
|
+
maxLength: 6
|
|
2603
|
+
}),
|
|
2604
|
+
o && /* @__PURE__ */ j("div", {
|
|
2605
|
+
className: "otp-code-input__error",
|
|
2606
|
+
children: o
|
|
2607
|
+
}),
|
|
2608
|
+
/* @__PURE__ */ M("div", {
|
|
2609
|
+
className: "otp-code-input__actions",
|
|
2610
|
+
children: [/* @__PURE__ */ j("button", {
|
|
2611
|
+
type: "button",
|
|
2612
|
+
className: "btn btn--ghost btn--small",
|
|
2613
|
+
onClick: i,
|
|
2614
|
+
disabled: a || d,
|
|
2615
|
+
children: "Use different email"
|
|
2616
|
+
}), /* @__PURE__ */ j("button", {
|
|
2617
|
+
type: "button",
|
|
2618
|
+
className: "btn btn--ghost btn--small",
|
|
2619
|
+
onClick: g,
|
|
2620
|
+
disabled: l > 0 || d,
|
|
2621
|
+
children: d ? "Sending…" : l > 0 ? `Resend (${l}s)` : "Resend code"
|
|
2622
|
+
})]
|
|
2623
|
+
}),
|
|
2624
|
+
s.length === 6 && /* @__PURE__ */ j("p", {
|
|
2625
|
+
className: "otp-code-input__auto-submit",
|
|
2626
|
+
children: "Verifying..."
|
|
2627
|
+
})
|
|
2628
|
+
]
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
//#endregion
|
|
2632
|
+
//#region src/components/AuthModal.tsx
|
|
2633
|
+
function vt({ isOpen: e, onClose: t, onRequestCode: n, onVerifyCode: r, stationSlug: i, error: a }) {
|
|
2634
|
+
let [o, s] = k(""), [c, l] = k(!1), [u, d] = k(null), [f, p] = k(null), m = O(null);
|
|
2635
|
+
E(() => {
|
|
2636
|
+
e || (s(""), l(!1), d(null), p(null));
|
|
2637
|
+
}, [e]), E(() => {
|
|
2638
|
+
e && !u && m.current?.focus();
|
|
2639
|
+
}, [e, u]), E(() => {
|
|
2640
|
+
if (!e) return;
|
|
2641
|
+
function t(e) {
|
|
2642
|
+
e.key === "Escape" && y();
|
|
2643
|
+
}
|
|
2644
|
+
return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
|
|
2645
|
+
}, [e]);
|
|
2646
|
+
async function h() {
|
|
2647
|
+
if (!(!o.trim() || c)) {
|
|
2648
|
+
l(!0), p(null);
|
|
2649
|
+
try {
|
|
2650
|
+
d((await n(o)).pendingId);
|
|
2651
|
+
} catch {} finally {
|
|
2652
|
+
l(!1);
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
async function g(e) {
|
|
2657
|
+
if (u) {
|
|
2658
|
+
l(!0), p(null);
|
|
2659
|
+
try {
|
|
2660
|
+
await r(u, e);
|
|
2661
|
+
} catch (e) {
|
|
2662
|
+
p(e?.message ?? "Invalid or expired code");
|
|
2663
|
+
} finally {
|
|
2664
|
+
l(!1);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
async function _() {
|
|
2669
|
+
o.trim() && (p(null), d((await n(o)).pendingId));
|
|
2670
|
+
}
|
|
2671
|
+
function v() {
|
|
2672
|
+
d(null), p(null), s("");
|
|
2673
|
+
}
|
|
2674
|
+
function y() {
|
|
2675
|
+
c || (console.log("[AuthModal] handleClose called"), t());
|
|
2676
|
+
}
|
|
2677
|
+
function b(e) {
|
|
2678
|
+
e.key === "Enter" && o.trim() && !c && (e.preventDefault(), h());
|
|
2679
|
+
}
|
|
2680
|
+
return e ? u ? /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("div", {
|
|
2681
|
+
className: "modal-overlay",
|
|
2682
|
+
onClick: y
|
|
2683
|
+
}), /* @__PURE__ */ j("div", {
|
|
2684
|
+
className: "auth-modal",
|
|
2685
|
+
role: "dialog",
|
|
2686
|
+
"aria-modal": "true",
|
|
2687
|
+
"aria-labelledby": "auth-modal-title",
|
|
2688
|
+
children: /* @__PURE__ */ M("div", {
|
|
2689
|
+
className: "auth-modal__content",
|
|
2690
|
+
children: [/* @__PURE__ */ j("button", {
|
|
2691
|
+
type: "button",
|
|
2692
|
+
className: "auth-modal__close-x",
|
|
2693
|
+
onClick: y,
|
|
2694
|
+
"aria-label": "Close",
|
|
2695
|
+
title: "Close",
|
|
2696
|
+
children: "✕"
|
|
2697
|
+
}), /* @__PURE__ */ j(_t, {
|
|
2698
|
+
email: o,
|
|
2699
|
+
pendingId: u,
|
|
2700
|
+
onVerify: g,
|
|
2701
|
+
onResend: _,
|
|
2702
|
+
onChangeEmail: v,
|
|
2703
|
+
isSubmitting: c,
|
|
2704
|
+
error: f
|
|
2705
|
+
})]
|
|
2706
|
+
})
|
|
2707
|
+
})] }) : /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j("div", {
|
|
2708
|
+
className: "modal-overlay",
|
|
2709
|
+
onClick: y
|
|
2710
|
+
}), /* @__PURE__ */ j("div", {
|
|
2711
|
+
className: "auth-modal",
|
|
2712
|
+
role: "dialog",
|
|
2713
|
+
"aria-modal": "true",
|
|
2714
|
+
"aria-labelledby": "auth-modal-title",
|
|
2715
|
+
children: /* @__PURE__ */ M("div", {
|
|
2716
|
+
className: "auth-modal__content",
|
|
2717
|
+
children: [
|
|
2718
|
+
/* @__PURE__ */ j("div", {
|
|
2719
|
+
className: "auth-modal__icon",
|
|
2720
|
+
children: "🔒"
|
|
2721
|
+
}),
|
|
2722
|
+
/* @__PURE__ */ j("h2", {
|
|
2723
|
+
id: "auth-modal-title",
|
|
2724
|
+
className: "auth-modal__title",
|
|
2725
|
+
children: "Sign in to post messages"
|
|
2726
|
+
}),
|
|
2727
|
+
/* @__PURE__ */ j("input", {
|
|
2728
|
+
ref: m,
|
|
2729
|
+
type: "email",
|
|
2730
|
+
className: "auth-modal__input",
|
|
2731
|
+
placeholder: "your@email.com",
|
|
2732
|
+
value: o,
|
|
2733
|
+
onChange: (e) => s(e.target.value),
|
|
2734
|
+
onKeyDown: b,
|
|
2735
|
+
disabled: c,
|
|
2736
|
+
autoComplete: "email"
|
|
2737
|
+
}),
|
|
2738
|
+
a && /* @__PURE__ */ j("div", {
|
|
2739
|
+
className: "auth-modal__error",
|
|
2740
|
+
children: a
|
|
2741
|
+
}),
|
|
2742
|
+
/* @__PURE__ */ M("div", {
|
|
2743
|
+
className: "auth-modal__buttons",
|
|
2744
|
+
children: [/* @__PURE__ */ j("button", {
|
|
2745
|
+
type: "button",
|
|
2746
|
+
className: "btn btn--ghost",
|
|
2747
|
+
onClick: y,
|
|
2748
|
+
disabled: c,
|
|
2749
|
+
children: "Cancel"
|
|
2750
|
+
}), /* @__PURE__ */ j("button", {
|
|
2751
|
+
type: "button",
|
|
2752
|
+
className: "btn btn--primary",
|
|
2753
|
+
onClick: h,
|
|
2754
|
+
disabled: !o.trim() || c,
|
|
2755
|
+
children: c ? "Sending…" : "Continue"
|
|
2756
|
+
})]
|
|
2757
|
+
})
|
|
2758
|
+
]
|
|
2759
|
+
})
|
|
2760
|
+
})] }) : null;
|
|
2761
|
+
}
|
|
2762
|
+
//#endregion
|
|
2763
|
+
//#region src/components/AudioNotification.tsx
|
|
2764
|
+
function yt({ onMention: e, onChannel: t, mentionSoundUrl: n, channelSoundUrl: r }) {
|
|
2765
|
+
let i = O(null), a = O(null), o = O(null), s = O(n), c = O(r);
|
|
2766
|
+
E(() => {
|
|
2767
|
+
s.current = n;
|
|
2768
|
+
}, [n]), E(() => {
|
|
2769
|
+
c.current = r;
|
|
2770
|
+
}, [r]), E(() => {
|
|
2771
|
+
function e() {
|
|
2772
|
+
t();
|
|
2773
|
+
let e = new AudioContext();
|
|
2774
|
+
i.current = e, e.resume().then(() => {
|
|
2775
|
+
if (e.state !== "running") return;
|
|
2776
|
+
let t = e.createBuffer(1, 1, e.sampleRate), n = e.createBufferSource();
|
|
2777
|
+
n.buffer = t, n.connect(e.destination), n.start(0);
|
|
2778
|
+
let r = [];
|
|
2779
|
+
s.current && r.push(fetch(s.current).then((e) => e.arrayBuffer()).then((t) => e.decodeAudioData(t)).then((e) => {
|
|
2780
|
+
a.current = e;
|
|
2781
|
+
}).catch(() => {})), c.current && r.push(fetch(c.current).then((e) => e.arrayBuffer()).then((t) => e.decodeAudioData(t)).then((e) => {
|
|
2782
|
+
o.current = e;
|
|
2783
|
+
}).catch(() => {})), Promise.all(r);
|
|
2784
|
+
}).catch(() => {});
|
|
2785
|
+
}
|
|
2786
|
+
function t() {
|
|
2787
|
+
document.removeEventListener("mousedown", e, !0), document.removeEventListener("touchstart", e, !0), document.removeEventListener("touchend", e, !0), document.removeEventListener("keydown", e, !0);
|
|
2788
|
+
}
|
|
2789
|
+
return document.addEventListener("mousedown", e, !0), document.addEventListener("touchstart", e, !0), document.addEventListener("touchend", e, !0), document.addEventListener("keydown", e, !0), t;
|
|
2790
|
+
}, []);
|
|
2791
|
+
async function l() {
|
|
2792
|
+
let e = i.current;
|
|
2793
|
+
if (!e) return null;
|
|
2794
|
+
if (e.state !== "running") try {
|
|
2795
|
+
await e.resume();
|
|
2796
|
+
} catch {
|
|
2797
|
+
return null;
|
|
2798
|
+
}
|
|
2799
|
+
return e.state === "running" ? e : null;
|
|
2800
|
+
}
|
|
2801
|
+
async function u() {
|
|
2802
|
+
let e = await l();
|
|
2803
|
+
if (!e || !a.current) return;
|
|
2804
|
+
let t = e.createBufferSource();
|
|
2805
|
+
t.buffer = a.current, t.connect(e.destination), t.start(0);
|
|
2806
|
+
}
|
|
2807
|
+
async function d() {
|
|
2808
|
+
let e = await l();
|
|
2809
|
+
if (!e || !o.current) return;
|
|
2810
|
+
let t = e.createBufferSource();
|
|
2811
|
+
t.buffer = o.current, t.connect(e.destination), t.start(0);
|
|
2812
|
+
}
|
|
2813
|
+
return E(() => {
|
|
2814
|
+
e(u);
|
|
2815
|
+
}, [e]), E(() => {
|
|
2816
|
+
t(d);
|
|
2817
|
+
}, [t]), null;
|
|
2818
|
+
}
|
|
2819
|
+
//#endregion
|
|
2820
|
+
//#region src/components/MuteToggle.tsx
|
|
2821
|
+
function bt() {
|
|
2822
|
+
let { isMuted: e, setIsMuted: t } = le();
|
|
2823
|
+
return /* @__PURE__ */ j("button", {
|
|
2824
|
+
className: "mute-toggle",
|
|
2825
|
+
onClick: () => {
|
|
2826
|
+
let n = !e;
|
|
2827
|
+
t(n);
|
|
2828
|
+
let r = n ? "Notifications muted" : "Notifications unmuted", i = document.createElement("div");
|
|
2829
|
+
i.setAttribute("role", "status"), i.setAttribute("aria-live", "polite"), i.className = "sr-only", i.textContent = r, document.body.appendChild(i), setTimeout(() => document.body.removeChild(i), 1e3);
|
|
2830
|
+
},
|
|
2831
|
+
"aria-label": e ? "Unmute notifications" : "Mute notifications",
|
|
2832
|
+
title: e ? "Unmute notifications" : "Mute notifications",
|
|
2833
|
+
type: "button",
|
|
2834
|
+
children: j(e ? Fe : Pe, {
|
|
2835
|
+
className: "mute-toggle__icon",
|
|
2836
|
+
"aria-hidden": "true"
|
|
2837
|
+
})
|
|
2838
|
+
});
|
|
2839
|
+
}
|
|
2840
|
+
//#endregion
|
|
2841
|
+
//#region src/components/PaneDivider.tsx
|
|
2842
|
+
function xt({ onResize: e, containerRef: t, minWidth: n = 120, maxFraction: r = .5 }) {
|
|
2843
|
+
let i = O(!1), a = w((a) => {
|
|
2844
|
+
i.current = !0, document.body.style.userSelect = "none", document.body.style.cursor = "col-resize";
|
|
2845
|
+
let o = (a) => {
|
|
2846
|
+
if (!i.current || !t.current) return;
|
|
2847
|
+
let o = t.current.getBoundingClientRect(), s = o.width * r, c = o.right - a;
|
|
2848
|
+
e(Math.min(s, Math.max(n, c)));
|
|
2849
|
+
}, s = (e) => o(e.clientX), c = (e) => {
|
|
2850
|
+
e.touches.length > 0 && o(e.touches[0].clientX);
|
|
2851
|
+
}, l = () => {
|
|
2852
|
+
i.current = !1, document.body.style.userSelect = "", document.body.style.cursor = "", document.removeEventListener("mousemove", s), document.removeEventListener("mouseup", l), document.removeEventListener("touchmove", c), document.removeEventListener("touchend", l);
|
|
2853
|
+
};
|
|
2854
|
+
document.addEventListener("mousemove", s), document.addEventListener("mouseup", l), document.addEventListener("touchmove", c, { passive: !0 }), document.addEventListener("touchend", l);
|
|
2855
|
+
}, [
|
|
2856
|
+
t,
|
|
2857
|
+
n,
|
|
2858
|
+
r,
|
|
2859
|
+
e
|
|
2860
|
+
]);
|
|
2861
|
+
return /* @__PURE__ */ j("div", {
|
|
2862
|
+
className: "pane-divider",
|
|
2863
|
+
onMouseDown: w((e) => {
|
|
2864
|
+
e.preventDefault(), a(e.clientX);
|
|
2865
|
+
}, [a]),
|
|
2866
|
+
onTouchStart: w((e) => {
|
|
2867
|
+
e.touches.length > 0 && a(e.touches[0].clientX);
|
|
2868
|
+
}, [a]),
|
|
2869
|
+
"aria-hidden": "true"
|
|
2870
|
+
});
|
|
2871
|
+
}
|
|
2872
|
+
//#endregion
|
|
2873
|
+
//#region src/components/ChatWindow.tsx
|
|
2874
|
+
var St = "relaya_sidebar_width", Ct = 768, wt = 220;
|
|
2875
|
+
function Tt({ auth: n, showBranding: r = !0, serverUrl: i, hideSignOut: a = !1, hideAdmin: o = !1 }) {
|
|
2876
|
+
let { user: s, station: c, stationSlug: l, getToken: u } = n, [d, f] = k(s?.chatName ?? null), [p, m] = k(!1), [h, g] = k(!1), [_, v] = k(null), [y, x] = k([]), [S, C] = k({
|
|
2877
|
+
mentionSoundUrl: null,
|
|
2878
|
+
channelSoundUrl: null
|
|
2879
|
+
}), T = O(new b("", u)), A = O(null), [N, P] = k(() => typeof window < "u" && window.innerWidth >= Ct), [F, I] = k(() => {
|
|
2880
|
+
try {
|
|
2881
|
+
let e = localStorage.getItem(St);
|
|
2882
|
+
return e ? parseInt(e, 10) : wt;
|
|
2883
|
+
} catch {
|
|
2884
|
+
return wt;
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
E(() => {
|
|
2888
|
+
let e = window.matchMedia(`(min-width: ${Ct}px)`), t = (e) => P(e.matches);
|
|
2889
|
+
return e.addEventListener("change", t), () => e.removeEventListener("change", t);
|
|
2890
|
+
}, []);
|
|
2891
|
+
let ee = w((e) => {
|
|
2892
|
+
I(e);
|
|
2893
|
+
try {
|
|
2894
|
+
localStorage.setItem(St, String(Math.round(e)));
|
|
2895
|
+
} catch {}
|
|
2896
|
+
}, []), L = D(() => ({ width: F }), [F]), R = w(async () => {
|
|
2897
|
+
try {
|
|
2898
|
+
x((await T.current.getStickers(l)).stickers ?? []);
|
|
2899
|
+
} catch {}
|
|
2900
|
+
}, [l]), z = Z(n, u, {
|
|
2901
|
+
onStickersUpdated: R,
|
|
2902
|
+
wsBaseUrl: i,
|
|
2903
|
+
onForcedLogout: n.logout
|
|
2904
|
+
});
|
|
2905
|
+
E(() => {
|
|
2906
|
+
n.status === "authenticated" && s && T.current.getMe(l).then((e) => f(e.chatName)).catch(() => {}), R().catch(() => void 0), T.current.getSounds(l).then((e) => C(e)).catch(() => {});
|
|
2907
|
+
}, [
|
|
2908
|
+
l,
|
|
2909
|
+
n.status,
|
|
2910
|
+
s
|
|
2911
|
+
]), E(() => {
|
|
2912
|
+
let e = window.setInterval(() => {
|
|
2913
|
+
R().catch(() => void 0);
|
|
2914
|
+
}, 6e4), t = () => {
|
|
2915
|
+
R().catch(() => void 0);
|
|
2916
|
+
};
|
|
2917
|
+
return window.addEventListener("focus", t), document.addEventListener("visibilitychange", t), () => {
|
|
2918
|
+
window.clearInterval(e), window.removeEventListener("focus", t), document.removeEventListener("visibilitychange", t);
|
|
2919
|
+
};
|
|
2920
|
+
}, [R]);
|
|
2921
|
+
let B = n.status === "authenticated" && (s?.permissions.includes(e.POST) ?? !1), V = s?.permissions.includes(e.DELETE_ANY) ?? !1, H = s?.permissions.includes(e.MANAGE_ROLES) ?? !1, te = t.embed, ne = c?.name || l.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" "), re = (() => {
|
|
2922
|
+
let e = new URL(window.location.href);
|
|
2923
|
+
return e.searchParams.delete("embed"), e.toString();
|
|
2924
|
+
})(), ie = (() => {
|
|
2925
|
+
let e = new URL(window.location.href);
|
|
2926
|
+
return e.searchParams.set("admin", "true"), e.searchParams.delete("embed"), e.toString();
|
|
2927
|
+
})();
|
|
2928
|
+
return /* @__PURE__ */ M("div", {
|
|
2929
|
+
className: "chat-window",
|
|
2930
|
+
children: [
|
|
2931
|
+
/* @__PURE__ */ M("div", {
|
|
2932
|
+
className: "chat-header",
|
|
2933
|
+
children: [
|
|
2934
|
+
/* @__PURE__ */ j("div", {
|
|
2935
|
+
className: "chat-header__title",
|
|
2936
|
+
children: ne
|
|
2937
|
+
}),
|
|
2938
|
+
n.status === "authenticated" && /* @__PURE__ */ j(bt, {}),
|
|
2939
|
+
/* @__PURE__ */ M("button", {
|
|
2940
|
+
className: "chat-header__online",
|
|
2941
|
+
onClick: () => m(!0),
|
|
2942
|
+
title: `${z.totalCount} listener${z.totalCount === 1 ? "" : "s"} online — tap to see who's here`,
|
|
2943
|
+
"aria-label": `${z.totalCount} listeners online. Tap to see list.`,
|
|
2944
|
+
children: [/* @__PURE__ */ j("svg", {
|
|
2945
|
+
width: "18",
|
|
2946
|
+
height: "14",
|
|
2947
|
+
viewBox: "0 0 24 24",
|
|
2948
|
+
fill: "currentColor",
|
|
2949
|
+
"aria-hidden": "true",
|
|
2950
|
+
children: /* @__PURE__ */ j("path", { d: "M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" })
|
|
2951
|
+
}), z.totalCount]
|
|
2952
|
+
}),
|
|
2953
|
+
n.status === "authenticated" && /* @__PURE__ */ j(mt, {
|
|
2954
|
+
stationSlug: l,
|
|
2955
|
+
initialChatName: d,
|
|
2956
|
+
getToken: u,
|
|
2957
|
+
onUpdated: (e) => f(e)
|
|
2958
|
+
}),
|
|
2959
|
+
te && /* @__PURE__ */ j("a", {
|
|
2960
|
+
href: re,
|
|
2961
|
+
target: "_blank",
|
|
2962
|
+
rel: "noopener noreferrer",
|
|
2963
|
+
className: "btn btn--ghost chat-header__popout",
|
|
2964
|
+
title: "Open in new window",
|
|
2965
|
+
"aria-label": "Open chat in new window",
|
|
2966
|
+
children: /* @__PURE__ */ j("svg", {
|
|
2967
|
+
width: "14",
|
|
2968
|
+
height: "14",
|
|
2969
|
+
viewBox: "0 0 14 14",
|
|
2970
|
+
fill: "currentColor",
|
|
2971
|
+
"aria-hidden": "true",
|
|
2972
|
+
children: /* @__PURE__ */ j("path", { d: "M9 1h4v4l-1.5-1.5L8 7 7 6l3.5-3.5L9 1zM2 3h5v1H3v7h7V8h1v3a1 1 0 01-1 1H3a1 1 0 01-1-1V4a1 1 0 011-1z" })
|
|
2973
|
+
})
|
|
2974
|
+
}),
|
|
2975
|
+
n.status === "anonymous" ? /* @__PURE__ */ j("button", {
|
|
2976
|
+
className: "btn btn--primary chat-header__signin",
|
|
2977
|
+
onClick: () => g(!0),
|
|
2978
|
+
title: "Sign in",
|
|
2979
|
+
children: "Sign in"
|
|
2980
|
+
}) : a ? null : /* @__PURE__ */ j("button", {
|
|
2981
|
+
className: "btn btn--ghost chat-header__signout",
|
|
2982
|
+
onClick: n.logout,
|
|
2983
|
+
title: "Sign out",
|
|
2984
|
+
children: "Sign out"
|
|
2985
|
+
}),
|
|
2986
|
+
!o && (H || V) && /* @__PURE__ */ j("a", {
|
|
2987
|
+
href: ie,
|
|
2988
|
+
target: "_blank",
|
|
2989
|
+
rel: "noopener noreferrer",
|
|
2990
|
+
className: "btn btn--ghost chat-header__admin",
|
|
2991
|
+
title: "Open admin panel",
|
|
2992
|
+
"aria-label": "Open admin panel in new window",
|
|
2993
|
+
children: /* @__PURE__ */ j(Ie, {
|
|
2994
|
+
className: "chat-header__admin-icon",
|
|
2995
|
+
"aria-hidden": "true"
|
|
2996
|
+
})
|
|
2997
|
+
})
|
|
2998
|
+
]
|
|
2999
|
+
}),
|
|
3000
|
+
/* @__PURE__ */ j(pt, { status: z.connectionStatus }),
|
|
3001
|
+
/* @__PURE__ */ M("div", {
|
|
3002
|
+
className: "chat-body",
|
|
3003
|
+
ref: A,
|
|
3004
|
+
children: [
|
|
3005
|
+
/* @__PURE__ */ j(it, {
|
|
3006
|
+
messages: z.messages,
|
|
3007
|
+
optimistic: z.optimistic,
|
|
3008
|
+
stickers: y,
|
|
3009
|
+
currentUserId: s?.id ?? "",
|
|
3010
|
+
currentUserPermissions: s?.permissions ?? [],
|
|
3011
|
+
stationSlug: l,
|
|
3012
|
+
getToken: u,
|
|
3013
|
+
loadingInitial: z.loadingInitial,
|
|
3014
|
+
loadingOlder: z.loadingOlder,
|
|
3015
|
+
hasOlderMessages: z.hasOlderMessages,
|
|
3016
|
+
retentionCutoff: z.retentionCutoff,
|
|
3017
|
+
onLoadOlder: z.loadOlderMessages,
|
|
3018
|
+
onEdit: z.editMessage,
|
|
3019
|
+
onDelete: z.deleteMessage,
|
|
3020
|
+
onBan: z.banUser,
|
|
3021
|
+
onReport: z.reportMessage,
|
|
3022
|
+
onRetry: z.retryFailed,
|
|
3023
|
+
onReply: (e, t, n) => {
|
|
3024
|
+
v({
|
|
3025
|
+
messageId: e,
|
|
3026
|
+
authorName: t,
|
|
3027
|
+
excerpt: n.length > 60 ? n.substring(0, 60) + "…" : n
|
|
3028
|
+
});
|
|
3029
|
+
},
|
|
3030
|
+
getUserInfo: z.getUserInfo,
|
|
3031
|
+
getAvatarForMessage: z.getAvatarForMessage
|
|
3032
|
+
}),
|
|
3033
|
+
N && /* @__PURE__ */ j(xt, {
|
|
3034
|
+
onResize: ee,
|
|
3035
|
+
containerRef: A
|
|
3036
|
+
}),
|
|
3037
|
+
/* @__PURE__ */ j(dt, {
|
|
3038
|
+
users: z.users,
|
|
3039
|
+
currentUserId: s?.id ?? "",
|
|
3040
|
+
style: L
|
|
3041
|
+
})
|
|
3042
|
+
]
|
|
3043
|
+
}),
|
|
3044
|
+
/* @__PURE__ */ j(lt, {
|
|
3045
|
+
onSend: (e) => {
|
|
3046
|
+
z.sendMessage(e, _ || void 0), v(null);
|
|
3047
|
+
},
|
|
3048
|
+
connectionStatus: z.connectionStatus,
|
|
3049
|
+
canPost: B,
|
|
3050
|
+
onRequestAuth: () => g(!0),
|
|
3051
|
+
stationSlug: l,
|
|
3052
|
+
getToken: u,
|
|
3053
|
+
stickers: y,
|
|
3054
|
+
onRefreshStickers: R,
|
|
3055
|
+
replyingTo: _,
|
|
3056
|
+
onCancelReply: () => v(null),
|
|
3057
|
+
onlineUsers: z.users,
|
|
3058
|
+
currentUserId: s?.id
|
|
3059
|
+
}),
|
|
3060
|
+
r && /* @__PURE__ */ j("div", {
|
|
3061
|
+
className: "relaya-branding",
|
|
3062
|
+
children: /* @__PURE__ */ j("a", {
|
|
3063
|
+
href: "https://relaya.chat?ref=powered-by",
|
|
3064
|
+
target: "_blank",
|
|
3065
|
+
rel: "noopener noreferrer",
|
|
3066
|
+
children: "Powered by Relaya"
|
|
3067
|
+
})
|
|
3068
|
+
}),
|
|
3069
|
+
p && /* @__PURE__ */ j(gt, {
|
|
3070
|
+
users: z.users,
|
|
3071
|
+
currentUserId: s?.id ?? "",
|
|
3072
|
+
onClose: () => m(!1)
|
|
3073
|
+
}),
|
|
3074
|
+
/* @__PURE__ */ j(vt, {
|
|
3075
|
+
isOpen: h,
|
|
3076
|
+
onClose: () => {
|
|
3077
|
+
console.log("[ChatWindow] Closing AuthModal"), g(!1);
|
|
3078
|
+
},
|
|
3079
|
+
onRequestCode: async (e) => ({ pendingId: (await T.current.requestCode(e, l)).pendingId }),
|
|
3080
|
+
onVerifyCode: async (e, t) => {
|
|
3081
|
+
let r = await T.current.verifyCode(e, t, l);
|
|
3082
|
+
n.onOtpVerified(r), g(!1);
|
|
3083
|
+
},
|
|
3084
|
+
stationSlug: l,
|
|
3085
|
+
error: n.error
|
|
3086
|
+
}),
|
|
3087
|
+
n.status === "authenticated" && /* @__PURE__ */ j(yt, {
|
|
3088
|
+
onMention: z.registerMentionSound,
|
|
3089
|
+
onChannel: z.registerChannelSound,
|
|
3090
|
+
mentionSoundUrl: S.mentionSoundUrl,
|
|
3091
|
+
channelSoundUrl: S.channelSoundUrl
|
|
3092
|
+
})
|
|
3093
|
+
]
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
//#endregion
|
|
3097
|
+
//#region src/components/AuthSuccess.tsx
|
|
3098
|
+
function Et({ stationSlug: e, userDisplayName: t }) {
|
|
3099
|
+
return e.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" "), /* @__PURE__ */ j("div", {
|
|
3100
|
+
className: "auth-success",
|
|
3101
|
+
children: /* @__PURE__ */ M("div", {
|
|
3102
|
+
className: "auth-success__card",
|
|
3103
|
+
children: [
|
|
3104
|
+
/* @__PURE__ */ j("div", {
|
|
3105
|
+
className: "success-icon",
|
|
3106
|
+
children: "✅"
|
|
3107
|
+
}),
|
|
3108
|
+
/* @__PURE__ */ j("h1", { children: "You're signed in!" }),
|
|
3109
|
+
/* @__PURE__ */ M("p", {
|
|
3110
|
+
className: "user-greeting",
|
|
3111
|
+
children: ["Welcome, ", t]
|
|
3112
|
+
}),
|
|
3113
|
+
/* @__PURE__ */ j("div", {
|
|
3114
|
+
className: "instructions",
|
|
3115
|
+
children: /* @__PURE__ */ M("p", { children: [
|
|
3116
|
+
"Close this tab and",
|
|
3117
|
+
/* @__PURE__ */ j("br", {}),
|
|
3118
|
+
/* @__PURE__ */ j("strong", { children: "refresh the chat page" }),
|
|
3119
|
+
/* @__PURE__ */ j("br", {}),
|
|
3120
|
+
"to start posting messages."
|
|
3121
|
+
] })
|
|
3122
|
+
})
|
|
3123
|
+
]
|
|
3124
|
+
})
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
//#endregion
|
|
3128
|
+
//#region src/RelayaChat.tsx
|
|
3129
|
+
function Dt(e) {
|
|
3130
|
+
return t.admin ? /* @__PURE__ */ j(_, {
|
|
3131
|
+
className: e.className,
|
|
3132
|
+
spaceSlug: e.spaceSlug,
|
|
3133
|
+
serverUrl: e.serverUrl,
|
|
3134
|
+
token: e.token,
|
|
3135
|
+
manageOwnRefreshToken: e.manageOwnRefreshToken,
|
|
3136
|
+
onSessionEnded: e.onSessionEnded
|
|
3137
|
+
}) : /* @__PURE__ */ j(Ot, { ...e });
|
|
3138
|
+
}
|
|
3139
|
+
function Ot({ serverUrl: e, spaceSlug: n, token: r, className: i, manageOwnRefreshToken: a, onSessionEnded: o, hideSignOut: c, hideAdmin: l }) {
|
|
3140
|
+
let f = a ?? !t.managed, p = c ?? !f, m = s({
|
|
3141
|
+
spaceSlug: n,
|
|
3142
|
+
initialToken: r ?? null,
|
|
3143
|
+
manageOwnRefreshToken: f,
|
|
3144
|
+
onSessionEnded: o
|
|
3145
|
+
}), [h, g] = k(!1), [_, y] = k(!0);
|
|
3146
|
+
E(() => {
|
|
3147
|
+
F();
|
|
3148
|
+
}, []), E(() => {
|
|
3149
|
+
document.documentElement.setAttribute("data-theme", t.theme);
|
|
3150
|
+
let r = v(n);
|
|
3151
|
+
r && u(r);
|
|
3152
|
+
let i = `${e}/api/chat/${n}/theme`;
|
|
3153
|
+
fetch(i).then((e) => e.ok ? e.json() : {
|
|
3154
|
+
light: {},
|
|
3155
|
+
dark: {}
|
|
3156
|
+
}).then((e) => {
|
|
3157
|
+
(Object.keys(e?.light ?? {}).length > 0 || Object.keys(e?.dark ?? {}).length > 0) && d({
|
|
3158
|
+
light: e.light ?? {},
|
|
3159
|
+
dark: e.dark ?? {}
|
|
3160
|
+
}, t.theme);
|
|
3161
|
+
}).catch(() => {});
|
|
3162
|
+
}, [e, n]), E(() => {
|
|
3163
|
+
let t = `${e}/api/public/config?slug=${encodeURIComponent(n)}`;
|
|
3164
|
+
fetch(t).then((e) => e.ok ? e.json() : null).then((e) => {
|
|
3165
|
+
e && typeof e.showBranding == "boolean" && y(e.showBranding);
|
|
3166
|
+
}).catch(() => {});
|
|
3167
|
+
}, [e, n]), E(() => {
|
|
3168
|
+
t.magicLinkToken && m.status === "authenticated" && g(!0);
|
|
3169
|
+
}, [m.status]);
|
|
3170
|
+
let b = i ? `relaya-root ${i}` : "relaya-root";
|
|
3171
|
+
if (m.status === "loading") return /* @__PURE__ */ j("div", {
|
|
3172
|
+
className: b,
|
|
3173
|
+
children: /* @__PURE__ */ j("div", {
|
|
3174
|
+
className: "app",
|
|
3175
|
+
children: /* @__PURE__ */ M("div", {
|
|
3176
|
+
className: "loading-screen",
|
|
3177
|
+
children: [/* @__PURE__ */ j("div", { className: "connection-spinner" }), /* @__PURE__ */ j("span", { children: "Loading…" })]
|
|
3178
|
+
})
|
|
3179
|
+
})
|
|
3180
|
+
});
|
|
3181
|
+
if (m.status === "magic-link-sent") return /* @__PURE__ */ j("div", {
|
|
3182
|
+
className: b,
|
|
3183
|
+
children: /* @__PURE__ */ j("div", {
|
|
3184
|
+
className: "app",
|
|
3185
|
+
children: /* @__PURE__ */ j(be, { onBack: m.logout })
|
|
3186
|
+
})
|
|
3187
|
+
});
|
|
3188
|
+
if (m.status === "unauthenticated") return /* @__PURE__ */ j("div", {
|
|
3189
|
+
className: b,
|
|
3190
|
+
children: /* @__PURE__ */ j("div", {
|
|
3191
|
+
className: "app",
|
|
3192
|
+
children: /* @__PURE__ */ j(ye, {
|
|
3193
|
+
onLogin: m.login,
|
|
3194
|
+
error: m.error,
|
|
3195
|
+
stationSlug: n
|
|
3196
|
+
})
|
|
3197
|
+
})
|
|
3198
|
+
});
|
|
3199
|
+
let x = window.self !== window.top;
|
|
3200
|
+
return m.status === "authenticated" && h && !x ? /* @__PURE__ */ j("div", {
|
|
3201
|
+
className: b,
|
|
3202
|
+
children: /* @__PURE__ */ j("div", {
|
|
3203
|
+
className: "app",
|
|
3204
|
+
children: /* @__PURE__ */ j(Et, {
|
|
3205
|
+
stationSlug: m.stationSlug,
|
|
3206
|
+
userDisplayName: m.user?.displayName ?? "there"
|
|
3207
|
+
})
|
|
3208
|
+
})
|
|
3209
|
+
}) : /* @__PURE__ */ j("div", {
|
|
3210
|
+
className: b,
|
|
3211
|
+
children: /* @__PURE__ */ j("div", {
|
|
3212
|
+
className: "app",
|
|
3213
|
+
children: /* @__PURE__ */ j(X, { children: /* @__PURE__ */ j(Tt, {
|
|
3214
|
+
auth: m,
|
|
3215
|
+
showBranding: _,
|
|
3216
|
+
serverUrl: e,
|
|
3217
|
+
hideSignOut: p,
|
|
3218
|
+
hideAdmin: l
|
|
3219
|
+
}) })
|
|
3220
|
+
})
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
//#endregion
|
|
3224
|
+
export { _ as AdminPanel, l as AdminSettings, yt as AudioNotification, vt as AuthModal, Et as AuthSuccess, n as BanManagement, g as BanModal, mt as ChatNameEditor, Tt as ChatWindow, pt as ConnectionStatus, He as GravatarStyleModal, ye as LoginScreen, be as MagicLinkSent, $ as MessageAvatar, Be as MessageContextMenu, lt as MessageInput, rt as MessageItem, it as MessageList, bt as MuteToggle, X as NotificationMuteProvider, _t as OTPCodeInput, Dt as RelayaChat, Re as ReportModal, p as ReportReview, h as StickerAdminPage, f as ThemeAdminPage, dt as UserList, gt as UserListModal, t as appConfig, d as applyDbTheme, u as applySpaceTheme, y as buildWsUrl, x as clearTokenFromUrl, fe as findActiveMentionQuery, ge as findActiveShortcodeQuery, pe as getMentionSuggestions, v as getSpaceTheme, ve as getStickerSuggestions, F as injectRelayaStyles, he as insertMentionToken, _e as insertStickerShortcode, r as parseConfig, I as removeRelayaStyles, i as reorderStickersByFilename, me as resolveSpaceCompletion, m as useBans, o as useModerationConfig, le as useNotificationMute, s as useRelayaAuth, Z as useRelayaChat, a as useReports, c as useSpaceTheme };
|