@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/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 };