测试 Gemini Pro 2.5
好的,我已经明白了您的需求。您希望:
- 增大概览消息(Toast)的尺寸:使其更加醒目。
- 消息持久性:当在用户中心内部切换不同标签页(例如从“个人信息”切换到“安全设置”)时,右下角弹出的提示消息不应立即消失,而是应该等到其设定的显示时间结束后才消失。
针对这些需求,我会进行以下调整:
1. cdn/css/style.css
* 调整 .toast-message
及其内部元素(如 .toast-icon
, .toast-body
)的 padding
, font-size
, min-width
, max-width
等样式,以增大整体视觉尺寸。
* 确保关闭按钮和进度条也相应调整大小。
2. cdn/js/main.js
* 现有的 showMessage
函数中的计时器逻辑 (setTimeout(removeToast, duration)
) 已经是独立于页面内部导航的。这意味着当您在用户中心内部切换标签页时,只要 Toast 的显示时间未到,它就不会消失。这个机制已经满足了您的第二个需求。
以下是修改后的文件内容:
/*
--------------------------------------------------------------------------------qmwneb946/my/my-b30fba1fe5de85674b52ba4a36498afbff0a9c2e/cdn/css/style.css
--------------------------------------------------------------------------------
*/
:root {
--primary-color: #1abc9c;
--primary-color-dark: #16a085;
--secondary-color: #7f8c8d;
--accent-color: #3498db;
--success-color: #2ecc71;
--danger-color: #e74c3c;
--warning-color: #f39c12;
--info-color: #3498db;
--text-color-base: #34495e;
--text-color-muted: #7f8c8d;
--text-color-inverted: #ffffff;
--heading-color: #2c3e50;
--bg-color-light: #f4f6f8;
--surface-color-light: #ffffff;
--border-color-light: #dfe4e8;
--bg-color-dark: #1e272e;
--surface-color-dark: #2c3a47;
--border-color-dark: #4a5568;
--font-family-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-family-mono: 'SFMono-Regular', Consolas, "Liberation Mono", Menlo, Courier, monospace;
--border-radius: 8px;
--box-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--box-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--box-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--sidebar-width: 260px;
--top-bar-height: 64px;
--current-bg-color: var(--bg-color-light);
--current-surface-color: var(--surface-color-light);
--current-text-color: var(--text-color-base);
--current-text-muted-color: var(--text-color-muted);
--current-heading-color: var(--heading-color);
--current-border-color: var(--border-color-light);
--current-input-bg: var(--surface-color-light);
--current-input-placeholder-color: #95a5a6;
}
body.dark-mode {
--current-bg-color: var(--bg-color-dark);
--current-surface-color: var(--surface-color-dark);
--current-text-color: #e2e8f0;
--current-text-muted-color: #a0aec0;
--current-heading-color: #e2e8f0;
--current-border-color: var(--border-color-dark);
--current-input-bg: var(--surface-color-dark);
--current-input-placeholder-color: #718096;
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: var(--font-family-sans);
line-height: 1.6;
margin: 0;
padding: 0;
background-color: var(--current-bg-color);
color: var(--current-text-color);
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
}
.hidden { display: none !important; }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
a {
color: var(--primary-color);
text-decoration: none;
transition: color 0.2s ease;
}
a:hover { color: var(--primary-color-dark); text-decoration: underline; }
h1, h2, h3, h4, h5, h6 {
color: var(--current-heading-color);
margin-top: 0;
margin-bottom: 0.75em;
font-weight: 600;
line-height: 1.3;
}
h1 { font-size: 2.25rem; }
h2 { font-size: 1.875rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
.app-wrapper {
display: flex;
min-height: 100vh;
}
.app-wrapper.logged-out-layout .sidebar { display: none; }
.app-wrapper.logged-out-layout .main-content { margin-left: 0; }
.app-wrapper.logged-in-layout .sidebar { display: block; }
.top-bar {
background-color: var(--current-surface-color);
height: var(--top-bar-height);
border-bottom: 1px solid var(--current-border-color);
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
box-shadow: var(--box-shadow-sm);
}
.top-bar-left { display: flex; align-items: center; }
.site-title { font-size: 1.25rem; font-weight: 600; color: var(--current-heading-color); margin-left: 10px; }
.top-bar-right { display: flex; align-items: center; gap: 10px; }
.sidebar-toggle-button, .theme-toggle-button, .user-menu-button, .messaging-button {
background: none;
border: none;
color: var(--current-text-muted-color);
padding: 8px;
border-radius: var(--border-radius);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.sidebar-toggle-button:hover, .theme-toggle-button:hover, .user-menu-button:hover, .messaging-button:hover {
background-color: color-mix(in srgb, var(--current-surface-color) 80%, var(--current-bg-color));
color: var(--current-text-color);
}
.messaging-button.active {
color: var(--primary-color);
background-color: color-mix(in srgb, var(--primary-color) 10%, transparent);
}
.h-5 { height: 1.25rem !important; }
.w-5 { width: 1.25rem !important; }
.h-6 { height: 1.5rem !important; }
.w-6 { width: 1.5rem !important; }
.mr-1 { margin-right: 0.25rem !important; }
.mr-2 { margin-right: 0.5rem !important; }
.sidebar-nav .sidebar-link svg {height: 1.25rem;width: 1.25rem;margin-right: 0.5rem;display: inline-block;vertical-align: middle;position: relative;top: -0.05em;flex-shrink: 0;
}
.unread-badge {
background-color: var(--danger-color);
color: var(--text-color-inverted);
border-radius: 50%;
padding: 0px 5px;
font-size: 0.75em;
min-width: 1.7em;
height: 1.7em;
line-height: 1.7em;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: bold;
vertical-align: middle;
box-sizing: border-box;
}
.messaging-button .unread-badge {
position: absolute;
top: 4px;
right: 4px;
}
.contact-meta .unread-badge {
margin-top: 2px;
}
.auth-actions a.button { margin-left: 10px; }
.user-info-dropdown { position: relative; }
.user-menu-button .username-text { margin-left: 6px; font-weight: 500; }
.dropdown-menu {
position: absolute;
top: calc(100% + 8px);
right: 0;
background-color: var(--current-surface-color);
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow-md);
min-width: 200px;
z-index: 1010;
padding: 8px 0;
}
.dropdown-user-email { padding: 8px 16px; font-size: 0.875rem; color: var(--current-text-muted-color); }
.dropdown-divider { border-top: 1px solid var(--current-border-color); margin: 8px 0; }
.dropdown-item {
display: flex;
align-items: center;
padding: 8px 16px;
color: var(--current-text-color);
font-size: 0.95rem;
background: none;
border: none;
width: 100%;
text-align: left;
cursor: pointer;
}
.dropdown-item:hover { background-color: color-mix(in srgb, var(--current-surface-color) 85%, var(--current-bg-color)); }
.dropdown-item.logout { color: var(--danger-color); }
.dropdown-item svg {height: 1.25rem;width: 1.25rem;margin-right: 0.5rem;flex-shrink: 0;
}
.sidebar {
width: var(--sidebar-width);
background-color: var(--current-surface-color);
border-right: 1px solid var(--current-border-color);
position: fixed;
top: var(--top-bar-height);
left: 0;
bottom: 0;
padding: 20px 0;
overflow-y: auto;
z-index: 999;
transition: transform 0.3s ease-in-out;
box-shadow: var(--box-shadow-sm);
}
.sidebar.open { transform: translateX(0); }
.sidebar-nav ul { list-style: none; padding: 0; margin: 0; }
.sidebar-nav .sidebar-link {
display: flex;
align-items: center;
padding: 12px 20px;
color: var(--current-text-muted-color);
font-weight: 500;
border-left: 3px solid transparent;
transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.sidebar-nav .sidebar-link:hover {
color: var(--primary-color);
background-color: color-mix(in srgb, var(--primary-color) 10%, transparent);
}
.sidebar-nav .sidebar-link.selected {
color: var(--primary-color);
background-color: color-mix(in srgb, var(--primary-color) 15%, transparent);
border-left-color: var(--primary-color);
font-weight: 600;
}
.sidebar-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
z-index: 998;
}
.main-content {
flex-grow: 1;
padding-top: var(--top-bar-height);
transition: margin-left 0.3s ease-in-out;
}
.main-content .container {
padding: 25px;
margin: 0;
max-width: none;
background-color: var(--current-bg-color);
box-shadow: none;
border-radius: 0;
}
.app-wrapper.logged-in-layout .main-content {
margin-left: var(--sidebar-width);
}
.app-wrapper.logged-out-layout .main-content {
margin-left: 0;
}
.main-content h3 {
font-size: 1.75rem;
margin-bottom: 25px;
border-bottom: 1px solid var(--current-border-color);
padding-bottom: 10px;
}
.auth-section {
display: flex;
justify-content: center;
align-items: flex-start;
padding-top: 40px;
}
.form-container {
background-color: var(--current-surface-color);
padding: 30px 40px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow-md);
width: 100%;
max-width: 450px;
}
.form-container h2 { text-align: center; margin-bottom: 25px; font-size: 1.75rem; }
.form-group { margin-bottom: 20px; }
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
font-size: 0.9rem;
}
input[type="email"], input[type="password"], input[type="text"], input[type="tel"], input[type="url"], input[type="search"], textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
font-size: 1rem;
color: var(--current-text-color);
background-color: var(--current-input-bg);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input:focus, textarea:focus {
border-color: var(--primary-color);
outline: 0;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 20%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--current-input-placeholder-color); }
input[readonly], input[disabled] {
background-color: color-mix(in srgb, var(--current-input-bg) 80%, var(--current-bg-color)) !important;
opacity: 0.7;
cursor: not-allowed;
}
.input-hint { font-size: 0.8rem; color: var(--current-text-muted-color); margin-top: 4px; }
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
border: 1px solid transparent;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
text-align: center;
white-space: nowrap;
}
.button.primary { background-color: var(--primary-color); color: var(--text-color-inverted); border-color: var(--primary-color); }
.button.primary:hover { background-color: var(--primary-color-dark); border-color: var(--primary-color-dark); }
.button.secondary { background-color: var(--secondary-color); color: var(--text-color-inverted); border-color: var(--secondary-color); }
.button.secondary:hover { background-color: color-mix(in srgb, var(--secondary-color) 85%, black); border-color: color-mix(in srgb, var(--secondary-color) 85%, black); }
.button.success { background-color: var(--success-color); color: var(--text-color-inverted); border-color: var(--success-color); }
.button.success:hover { background-color: color-mix(in srgb, var(--success-color) 85%, black); border-color: color-mix(in srgb, var(--success-color) 85%, black); }
.button.danger { background-color: var(--danger-color); color: var(--text-color-inverted); border-color: var(--danger-color); }
.button.danger:hover { background-color: color-mix(in srgb, var(--danger-color) 85%, black); border-color: color-mix(in srgb, var(--danger-color) 85%, black); }
.button.full-width { width: 100%; margin-top: 10px; }
.button.small { padding: 8px 16px; font-size: 0.875rem; }
.button svg {height: 1.25rem;width: 1.25rem;margin-right: 0.25rem;
}
.form-actions { margin-top: 20px; display: flex; gap: 10px; justify-content: flex-start; }
.toggle-link { text-align: center; margin-top: 20px; font-size: 0.9rem; }
.account-content .tab-pane { padding: 15px; }
.setting-block { margin-bottom: 30px; padding: 20px; background-color: var(--current-surface-color); border-radius: var(--border-radius); box-shadow: var(--box-shadow-sm); }
.setting-block h4 { font-size: 1.15rem; margin-bottom: 15px; color: var(--current-heading-color); }
.description-text { font-size: 0.9rem; color: var(--current-text-muted-color); margin-bottom: 15px; }
.status-display { margin-bottom: 15px; font-size: 0.95rem; }
#2fa-current-status { font-weight: bold; }
#2fa-controls button { margin-right: 10px; }
.otpauth-uri-text {
word-break: break-all; font-family: var(--font-family-mono);
background: color-mix(in srgb, var(--current-bg-color) 90%, var(--current-surface-color));
padding: 10px; border-radius: 4px; margin: 10px 0; display: block;
border: 1px dashed var(--current-border-color);
}
#qrcode-display {
background: white; padding: 10px; border: 1px solid var(--border-color-light);
display: inline-block; margin: 15px auto;
}
body.dark-mode #qrcode-display { background: var(--surface-color-light); border-color: var(--border-color-light); }
.api-key-display { margin-top: 15px; }
.api-key-value-container { display: flex; align-items: center; margin-bottom: 10px; }
.api-key-value-container input[type="text"] { flex-grow: 1; margin-right: 10px; }
.external-link { color: var(--accent-color); font-weight: 500; }
.external-link:hover { color: color-mix(in srgb, var(--accent-color) 80%, black); }
hr.section-divider { border: none; border-top: 1px solid var(--current-border-color); margin: 30px 0; }
.security-settings-list {
list-style: none;
padding: 0;
margin: 0;
}
.security-setting-item {
background-color: var(--current-surface-color);
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
margin-bottom: 15px;
box-shadow: var(--box-shadow-sm);
}
.setting-entry {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
cursor: pointer;
font-weight: 500;
color: var(--current-heading-color);
transition: background-color 0.2s ease;
}
.setting-entry:hover {
background-color: color-mix(in srgb, var(--current-surface-color) 95%, var(--current-bg-color));
}
.setting-entry .entry-title {
flex-grow: 1;
}
.setting-entry .entry-status {
font-size: 0.85rem;
color: var(--current-text-muted-color);
margin-right: 10px;
}
.setting-entry .entry-arrow {
font-size: 0.8rem;
transition: transform 0.2s ease-in-out;
}
.setting-entry.open .entry-arrow {
transform: rotate(90deg);
}
.setting-content-panel {
padding: 0 20px 20px 20px;
border-top: 1px solid var(--current-border-color);
background-color: color-mix(in srgb, var(--current-surface-color) 98%, var(--current-bg-color));
}
.application-card {
background-color: color-mix(in srgb, var(--current-surface-color) 95%, var(--current-bg-color));
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
padding: 15px; margin-bottom: 15px;
}
.application-card h4 { font-size: 1.1em; color: var(--primary-color); margin-top: 0; margin-bottom: 8px; }
.application-card p { font-size: 0.9em; margin-bottom: 5px; color: var(--current-text-muted-color); word-break: break-word; }
.application-card code {
background-color: color-mix(in srgb, var(--current-text-color) 10%, transparent);
padding: 2px 5px; border-radius: 3px; font-family: var(--font-family-mono); word-break: break-all;
}
.new-client-secret-warning {
background-color: var(--warning-color); color: var(--text-color-inverted);
padding: 10px; border-radius: var(--border-radius); margin-top: 15px; font-size: 0.9em;
}
body.dark-mode .new-client-secret-warning {
background-color: color-mix(in srgb, var(--warning-color) 70%, black);
color: var(--current-text-color);
}
.messaging-tab-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--current-border-color);
}
.messaging-tab-header h3 {
margin-bottom: 0;
border-bottom: none;
padding-bottom: 0;
font-size: 1.75rem;
}
.messaging-tab-header svg {
color: var(--primary-color);
height: 28px;
width: 28px;
margin-right: 10px;
}
.new-conversation-trigger {
margin-bottom: 25px;
display: flex;
gap: 10px;
align-items: center;
}
.new-conversation-trigger input[type="email"] {
flex-grow: 1;
font-size: 0.95rem;
padding: 10px 12px;
}
.new-conversation-trigger button.button.small {
padding: 10px 15px;
font-size: 0.9rem;
flex-shrink: 0;
}
.messaging-layout-new {
display: flex;
height: calc(100vh - var(--top-bar-height) - 230px);
min-height: 450px;
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
background-color: var(--current-surface-color);
overflow: hidden;
box-shadow: var(--box-shadow-sm);
}
.messaging-contacts-panel {
width: 320px;
min-width: 280px;
border-right: 1px solid var(--current-border-color);
display: flex;
flex-direction: column;
background-color: var(--current-surface-color);
}
.contact-search-bar {
padding: 12px 15px;
border-bottom: 1px solid var(--current-border-color);
display: flex;
gap: 8px;
background-color: color-mix(in srgb, var(--current-surface-color) 95%, var(--current-bg-color));
}
.contact-search-bar input[type="search"] {
flex-grow: 1;
padding: 8px 10px;
font-size: 0.9rem;
}
.contact-search-bar button {
padding: 8px 12px;
font-size: 0.85rem;
}
.recent-contacts-title {
padding: 12px 15px;
font-size: 0.75rem;
font-weight: 600;
color: var(--current-text-muted-color);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0;
border-bottom: 1px solid var(--current-border-color);
background-color: color-mix(in srgb, var(--current-surface-color) 98%, var(--current-bg-color));
}
.contact-list {
flex-grow: 1;
overflow-y: auto;
list-style-type: none;
padding: 0;
margin: 0;
}
.contact-list li {
display: flex;
align-items: center;
padding: 10px 15px;
cursor: pointer;
border-bottom: 1px solid var(--current-border-color);
transition: background-color 0.2s ease;
}
.contact-list li:last-child { border-bottom: none; }
.contact-list li:hover { background-color: color-mix(in srgb, var(--current-surface-color) 90%, var(--current-bg-color)); }
.contact-list li.selected { background-color: var(--primary-color); color: var(--text-color-inverted); }
.contact-list li.selected .contact-name,
.contact-list li.selected .contact-last-message,
.contact-list li.selected .contact-meta .contact-time,
.contact-list li.selected .unread-badge { color: var(--text-color-inverted) !important; }
.contact-list li.selected .contact-avatar { border: 2px solid var(--text-color-inverted); }
.contact-avatar {
width: 38px;
height: 38px;
border-radius: 50%;
background-color: var(--secondary-color);
color: var(--text-color-inverted);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 12px;
font-size: 1rem;
overflow: hidden;
flex-shrink: 0;
}
.contact-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.contact-info {
flex-grow: 1;
overflow: hidden;
}
.contact-name {
font-weight: 500;
display: block; margin-bottom: 2px; color: var(--current-heading-color);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
font-size: 0.95rem;
}
.contact-last-message {
font-size: 0.8rem;
color: var(--current-text-muted-color);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display:block;
}
.contact-meta {
margin-left: auto;
text-align: right;
font-size: 0.75rem;
color: var(--current-text-muted-color);
min-width: 50px;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
line-height: 1.3;
}
.contact-time {
display: block;
margin-bottom: 3px;
}
.message-display-panel {
flex-grow: 1;
display: flex;
flex-direction: column;
background-color: var(--current-bg-color);
}
.messages-list {
flex-grow: 1; overflow-y: auto; padding: 20px;
display: flex; flex-direction: column;
}
.messages-list .messages-loading-indicator-wrapper,
.messages-list .load-more-messages-button-wrapper {display: flex;justify-content: center;padding: 10px 0;
}
.messages-list .messages-loading-indicator-wrapper .spinner {border: 3px solid var(--current-border-color);border-top: 3px solid var(--primary-color);border-radius: 50%;width: 20px;height: 20px;animation: spin 1s linear infinite;
}
.messages-list .load-more-messages-button-wrapper .load-more-messages-button {font-size: 0.8em;padding: 6px 12px;background-color: transparent;border: 1px solid var(--primary-color);color: var(--primary-color);
}
.messages-list .load-more-messages-button-wrapper .load-more-messages-button:hover {background-color: color-mix(in srgb, var(--primary-color) 10%, transparent);
}
@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}
.empty-messages-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: var(--current-text-muted-color);
height: 100%;
padding: 20px;
}
.empty-messages-placeholder svg {
margin-bottom: 15px;
opacity: 0.6;
width: 64px;
height: 64px;
}
.empty-messages-placeholder p {
font-size: 1.2em;
font-weight: 500;
margin-bottom: 8px;
color: var(--current-heading-color);
}
.empty-messages-placeholder span {
font-size: 0.9em;
max-width: 300px;
}
.message-item {
max-width: 75%; padding: 8px 12px; border-radius: 12px;
margin-bottom: 10px; line-height: 1.5; word-wrap: break-word;
}
.message-item.sent {
background-color: var(--primary-color); color: var(--text-color-inverted);
align-self: flex-end; border-bottom-right-radius: 4px;
}
.message-item.received {
background-color: var(--current-surface-color);
color: var(--current-text-color);
align-self: flex-start; border-bottom-left-radius: 4px;
border: 1px solid var(--current-border-color);
}
.message-sender { font-size: 0.75em; color: var(--current-text-muted-color); margin-bottom: 2px; display: block; }
body.dark-mode .message-item.sent .message-sender { color: rgba(255,255,255,0.6); }
.message-content { font-size: 0.95em; }
.message-content p:first-child { margin-top: 0; }
.message-content p:last-child { margin-bottom: 0; }
.message-content ul, .message-content ol { margin: 8px 0 8px 20px; padding-left: 0; }
.message-content pre {
background-color: color-mix(in srgb, var(--current-text-color) 8%, transparent);
border: 1px solid var(--current-border-color);
padding: 8px; border-radius: 4px; overflow-x: auto;
font-size: 0.85em; margin: 8px 0; font-family: var(--font-family-mono);
}
body.dark-mode .message-content pre {
background-color: color-mix(in srgb, var(--current-surface-color) 80%, var(--current-bg-color));
}
.message-time { font-size: 0.7rem; color: var(--current-text-muted-color); margin-top: 4px; text-align: right; display: block; opacity: 0.8; }
body.dark-mode .message-item.sent .message-time { color: rgba(255,255,255,0.5); }
.message-input-area {
display: flex; padding: 15px;
border-top: 1px solid var(--current-border-color);
background-color: var(--current-surface-color);
}
.message-input-area textarea {
flex-grow: 1; padding: 10px;
border: 1px solid var(--current-border-color);
border-radius: var(--border-radius);
resize: none; margin-right: 10px;
font-family: var(--font-family-sans); font-size: 0.95rem;
min-height: 42px; max-height: 120px; overflow-y: auto;
background-color: var(--current-input-bg); color: var(--current-text-color);
}
.message-input-area button { padding: 10px 18px; }
.api-usage-section pre {
background-color: color-mix(in srgb, var(--current-bg-color) 95%, var(--current-surface-color));
color: var(--current-text-color);
padding: 15px; border-radius: var(--border-radius); overflow-x: auto;
font-family: var(--font-family-mono); font-size: 0.875em;
border: 1px solid var(--current-border-color); margin-bottom: 15px;
}
.api-usage-section code.inline-code {
background-color: color-mix(in srgb, var(--current-text-color) 10%, transparent);
padding: 2px 5px; border-radius: 3px; font-family: var(--font-family-mono);
}
.admin-panel-table { width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 0.9em; }
.admin-panel-table th, .admin-panel-table td { border: 1px solid var(--current-border-color); padding: 8px 12px; text-align: left; }
.admin-panel-table th { background-color: var(--current-surface-color); font-weight: 600; }
.admin-panel-table tr:nth-child(even) { background-color: color-mix(in srgb, var(--current-surface-color) 95%, var(--current-bg-color));}
.admin-panel-table td code { background-color: color-mix(in srgb, var(--current-text-color) 10%, transparent); padding: 2px 4px; border-radius: 3px; font-family: var(--font-family-mono); }
.status-active { color: var(--success-color); font-weight: bold; }
.status-inactive, .status-suspended { color: var(--danger-color); font-weight: bold; }
.admin-actions button { margin-right: 5px; }
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
top: 0;
height: 100vh;
border-right: 1px solid var(--current-border-color);
}
.sidebar.open {
transform: translateX(0);
}
.app-wrapper.logged-in-layout .main-content {
margin-left: 0;
}
.app-wrapper.logged-out-layout .main-content {
margin-left: 0;
}
.messaging-layout-new {
flex-direction: column;
height: calc(100vh - var(--top-bar-height) - 70px);
}
.messaging-contacts-panel {
width: 100%; max-width: none; height: 40%; min-height: 250px;
border-right: none; border-bottom: 1px solid var(--current-border-color);
}
.message-display-panel { height: 60%; }
}
@media (min-width: 769px) {
.sidebar-toggle-button { display: none; }
.sidebar-overlay { display: none !important; }
.sidebar { transform: translateX(0); }
}
#login-form-el .cf-turnstile,
#register-form-el .cf-turnstile {
margin-left: 30px;
margin-right: 30px;
margin-top: 20px;
margin-bottom: 20px;
}
.modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background-color: rgba(0,0,0,0.6); display: flex;
justify-content: center; align-items: center; z-index: 2000;
opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.modal.active { opacity: 1; visibility: visible; transition: opacity 0.3s ease; }
.modal-content {
background-color: var(--current-surface-color); color: var(--current-text-color);
padding: 25px 30px; border-radius: var(--border-radius);
box-shadow: 0 5px 15px rgba(0,0,0,0.3); width: 90%; max-width: 500px;
transform: translateY(-20px); transition: transform 0.3s ease;
}
.modal.active .modal-content { transform: translateY(0); }
.toast-container {position: fixed;bottom: 25px; /* Increased distance from bottom */right: 25px; /* Increased distance from right */z-index: 9999;display: flex;flex-direction: column-reverse;gap: 12px; /* Increased gap */max-height: calc(100vh - 50px);overflow-y: auto;overflow-x: hidden;
}
.toast-message {background-color: var(--current-surface-color);color: var(--current-text-color);padding: 16px 22px; /* Increased padding */border-radius: var(--border-radius); /* Using slightly larger global radius */box-shadow: var(--box-shadow-lg);display: flex;align-items: center;min-width: 320px; /* Increased min-width */max-width: 420px; /* Increased max-width */opacity: 0;transform: translateX(calc(100% + 25px));transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.215, 0.610, 0.355, 1);position: relative;overflow: hidden;border-left: 6px solid transparent; /* Thicker border */
}
.toast-message.show {opacity: 1;transform: translateX(0);
}
.toast-icon {flex-shrink: 0;margin-right: 15px; /* Increased margin */width: 24px; /* Increased icon size */height: 24px; /* Increased icon size */
}
.toast-icon svg {width: 100%;height: 100%;
}
.toast-body {flex-grow: 1;font-size: 1rem; /* Increased font size */line-height: 1.5; /* Adjusted line height for readability */
}
.toast-close-button {background: none;border: none;color: var(--current-text-muted-color);font-size: 1.5rem; /* Increased close button size */font-weight: bold;cursor: pointer;padding: 0 0 0 18px; /* Increased padding */margin-left: auto;line-height: 1;align-self: flex-start;
}
.toast-close-button:hover {color: var(--current-text-color);
}
.toast-progress-bar {position: absolute;bottom: 0;left: 0;height: 5px; /* Thicker progress bar */width: 100%;background-color: rgba(0,0,0,0.2);animation: shrink var(--toast-duration, 5000ms) linear forwards;
}
body.dark-mode .toast-progress-bar {background-color: rgba(255,255,255,0.2);
}
@keyframes shrink {from { width: 100%; }to { width: 0%; }
}
.toast-message.toast-success {border-left-color: var(--success-color);
}
.toast-message.toast-success .toast-icon svg {fill: var(--success-color);
}
.toast-message.toast-success .toast-progress-bar {background-color: var(--success-color);
}
.toast-message.toast-error {border-left-color: var(--danger-color);
}
.toast-message.toast-error .toast-icon svg {fill: var(--danger-color);
}
.toast-message.toast-error .toast-progress-bar {background-color: var(--danger-color);
}
.toast-message.toast-info {border-left-color: var(--info-color);
}
.toast-message.toast-info .toast-icon svg {fill: var(--info-color);
}
.toast-message.toast-info .toast-progress-bar {background-color: var(--info-color);
}
.toast-message.toast-warning {border-left-color: var(--warning-color);
}
.toast-message.toast-warning .toast-icon svg {fill: var(--warning-color);
}
.toast-message.toast-warning .toast-progress-bar {background-color: var(--warning-color);
}
/*
--------------------------------------------------------------------------------qmwneb946/my/my-b30fba1fe5de85674b52ba4a36498afbff0a9c2e/cdn/js/main.js
--------------------------------------------------------------------------------
*/
let TURNSTILE_SITE_KEY = '1x00000000000000000000AA';
const activeTurnstileWidgets = new Map();
let loginEmailFor2FA = null;
let currentUserData = null;
let authSection, loggedInSection, loginFormEl, registerFormEl, adminSection;
let topBarUserEmailEl, topBarUserUsernameEl, topBarUserInfoEl, topBarAuthButtonsEl, topBarLogoutButtonEl, userMenuButtonEl, userDropdownMenuEl, topBarAccountLinkEl, topBarAdminLinkEl;
let sidebarEl, sidebarToggleEl, mainContentContainerEl, sidebarOverlayEl;
let accountTabLinks = [], adminTabLinks = [];
let tabPanes = [];
let themeToggleButton, themeToggleDarkIcon, themeToggleLightIcon;
let unreadMessagesIndicator;
let appWrapper;
let topBarMessagingButton;
let userPresenceSocket = null;
const pathToPaneIdMap = {'/user/profile': 'tab-content-personal-info','/user/security': 'tab-content-security-settings','/user/api-keys': 'tab-content-api-keys','/user/applications': 'tab-content-my-applications','/user/messaging': 'tab-content-messaging','/user/admin': 'tab-content-admin-users','/user/admin/users': 'tab-content-admin-users','/user/admin/apps': 'tab-content-admin-apps'
};
const icons = {success: `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>`,error: `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg>`,info: `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>`,warning: `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" class="svg-inline--fa fa-exclamation-triangle fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.982 12.654z"></path></svg>`
};
function renderTurnstile(containerElement) {if (!containerElement || !window.turnstile || typeof window.turnstile.render !== 'function') return;if (!TURNSTILE_SITE_KEY) { return; }if (activeTurnstileWidgets.has(containerElement)) {try { turnstile.remove(activeTurnstileWidgets.get(containerElement)); } catch (e) { }activeTurnstileWidgets.delete(containerElement);}containerElement.innerHTML = '';try {const widgetId = turnstile.render(containerElement, {sitekey: TURNSTILE_SITE_KEY,callback: (token) => {const specificCallbackName = containerElement.getAttribute('data-callback');if (specificCallbackName && typeof window[specificCallbackName] === 'function') {window[specificCallbackName](token);}}});if (widgetId) activeTurnstileWidgets.set(containerElement, widgetId);} catch (e) { }
}
function removeTurnstile(containerElement) {if (containerElement && activeTurnstileWidgets.has(containerElement)) {try { turnstile.remove(activeTurnstileWidgets.get(containerElement)); } catch (e) { }activeTurnstileWidgets.delete(containerElement);}
}
function resetTurnstileInContainer(containerElement) {if (containerElement && activeTurnstileWidgets.has(containerElement)) {try { turnstile.reset(activeTurnstileWidgets.get(containerElement)); } catch (e) { }} else if (containerElement) {renderTurnstile(containerElement);}
}
function showMessage(text, type = 'error', isHtml = false, duration = 5000) {let toastContainer = document.getElementById('toast-container');if (!toastContainer) {toastContainer = document.createElement('div');toastContainer.id = 'toast-container';toastContainer.className = 'toast-container bottom-right';document.body.appendChild(toastContainer);}const toast = document.createElement('div');toast.className = `toast-message toast-${type}`;toast.style.setProperty('--toast-duration', `${duration}ms`);const iconDiv = document.createElement('div');iconDiv.className = 'toast-icon';iconDiv.innerHTML = icons[type] || icons.info;const bodyDiv = document.createElement('div');bodyDiv.className = 'toast-body';if (isHtml) {bodyDiv.innerHTML = text;} else {bodyDiv.textContent = text;}const closeButton = document.createElement('button');closeButton.className = 'toast-close-button';closeButton.innerHTML = '×';closeButton.setAttribute('aria-label', 'close');const progressBar = document.createElement('div');progressBar.className = 'toast-progress-bar';toast.appendChild(iconDiv);toast.appendChild(bodyDiv);toast.appendChild(closeButton);toast.appendChild(progressBar);toastContainer.appendChild(toast);requestAnimationFrame(() => {setTimeout(() => {toast.classList.add('show');}, 50);});const removeToast = () => {toast.classList.remove('show');toast.addEventListener('transitionend', () => {if (toast.parentElement) {toast.parentElement.removeChild(toast);}}, { once: true });setTimeout(() => {if (toast.parentElement) {toast.parentElement.removeChild(toast);}}, 500);};const timerId = setTimeout(removeToast, duration);closeButton.onclick = () => {clearTimeout(timerId);removeToast();};
}
function clearMessages() {const toastContainer = document.getElementById('toast-container');if (toastContainer) {while (toastContainer.firstChild) {toastContainer.removeChild(toastContainer.firstChild);}}
}
async function apiCall(endpoint, method = 'GET', body = null) {const options = { method, headers: {}, credentials: 'include' };if (body) { options.headers['Content-Type'] = 'application/json'; options.body = JSON.stringify(body); }try {const response = await fetch(endpoint, options);let resultData = {};const contentType = response.headers.get("content-type");if (contentType && contentType.includes("application/json") && response.status !== 204) {try { resultData = await response.json(); } catch (e) { }}return { ok: response.ok, status: response.status, data: resultData };} catch (e) {showMessage('发生网络或服务器错误,请稍后重试。', 'error');return { ok: false, status: 0, data: { error: '网络错误' } };}
}
function updateUnreadMessagesIndicatorUI(count) {const localUnreadIndicator = document.getElementById('unread-messages-indicator');const localMessagingButton = document.getElementById('top-bar-messaging-button');if (!localUnreadIndicator || !localMessagingButton) return;if (count > 0) {localUnreadIndicator.textContent = count;localUnreadIndicator.classList.remove('hidden');localMessagingButton.classList.add('active');} else {localUnreadIndicator.textContent = '';localUnreadIndicator.classList.add('hidden');localMessagingButton.classList.remove('active');}
}
function connectUserPresenceWebSocket() {if (userPresenceSocket && (userPresenceSocket.readyState === WebSocket.OPEN || userPresenceSocket.readyState === WebSocket.CONNECTING)) {return;}if (!currentUserData || !currentUserData.email) {return;}const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';const wsUrl = `${protocol}//${window.location.host}/api/ws/user`;userPresenceSocket = new WebSocket(wsUrl);userPresenceSocket.onopen = () => {};userPresenceSocket.onmessage = (event) => {try {const message = JSON.parse(event.data);if (message.type === "CONVERSATIONS_LIST") {if (typeof window.handleConversationsListUpdate === 'function') {window.handleConversationsListUpdate(message.data);}} else if (message.type === "UNREAD_COUNT_TOTAL") {updateUnreadMessagesIndicatorUI(message.data.unread_count);} else if (message.type === "CONVERSATION_UPDATE") {if (typeof window.handleSingleConversationUpdate === 'function') {window.handleSingleConversationUpdate(message.data);}}} catch (e) {}};userPresenceSocket.onclose = (event) => {userPresenceSocket = null;if (currentUserData && currentUserData.email) {setTimeout(connectUserPresenceWebSocket, 5000);}};userPresenceSocket.onerror = (error) => {};
}
function applyTheme(isDark) {document.body.classList.toggle('dark-mode', isDark);if (themeToggleDarkIcon) themeToggleDarkIcon.style.display = isDark ? 'block' : 'none';if (themeToggleLightIcon) themeToggleLightIcon.style.display = isDark ? 'none' : 'block';const qrCodeDisplay = document.getElementById('qrcode-display');const otpAuthUriTextDisplay = document.getElementById('otpauth-uri-text-display');if (qrCodeDisplay && typeof QRCode !== 'undefined' && otpAuthUriTextDisplay) {const otpauthUri = otpAuthUriTextDisplay.textContent;if (otpauthUri && qrCodeDisplay.innerHTML.includes('canvas')) {qrCodeDisplay.innerHTML = '';new QRCode(qrCodeDisplay, {text: otpauthUri, width: 180, height: 180,colorDark: isDark ? "#e2e8f0" : "#000000",colorLight: "#ffffff",correctLevel: QRCode.CorrectLevel.H});}}
}
function toggleSidebar() {if (sidebarEl && sidebarOverlayEl && appWrapper) {const isOpen = sidebarEl.classList.toggle('open');sidebarOverlayEl.classList.toggle('hidden', !isOpen);appWrapper.classList.toggle('sidebar-open-app', isOpen);}
}
function activatePane(paneIdToActivate) {if (!paneIdToActivate) return;const allNavLinks = [...accountTabLinks, ...adminTabLinks];allNavLinks.forEach(link => {link.classList.toggle('selected', link.dataset.paneId === paneIdToActivate);});if (topBarMessagingButton) {topBarMessagingButton.classList.toggle('active', paneIdToActivate === 'tab-content-messaging');}if (!mainContentContainerEl) return;tabPanes.forEach(pane => {const turnstileDivsInPane = pane.querySelectorAll('.cf-turnstile');if (pane.id === paneIdToActivate) {pane.classList.remove('hidden');turnstileDivsInPane.forEach(div => renderTurnstile(div));mainContentContainerEl.classList.toggle('messaging-active', pane.id === 'tab-content-messaging');if (pane.id === 'tab-content-api-keys' && typeof window.initializeApiKeysTab === 'function') window.initializeApiKeysTab();if (pane.id === 'tab-content-my-applications' && typeof window.loadOauthAppsTabData === 'function') window.loadOauthAppsTabData();if (pane.id === 'tab-content-messaging' && typeof window.loadMessagingTabData === 'function') window.loadMessagingTabData();if (pane.id === 'tab-content-security-settings' && typeof window.initializeSecuritySettings === 'function') {if (currentUserData) {window.initializeSecuritySettings(currentUserData);} else {apiCall('/api/me').then(response => {if (response.ok && response.data) {currentUserData = response.data;window.initializeSecuritySettings(currentUserData);}});}}if (pane.id === 'tab-content-admin-users' && typeof window.loadAdminUsersData === 'function') window.loadAdminUsersData();if (pane.id === 'tab-content-admin-apps' && typeof window.loadAdminOauthClientsData === 'function') window.loadAdminOauthClientsData();} else {turnstileDivsInPane.forEach(div => removeTurnstile(div));pane.classList.add('hidden');}});clearMessages();const newlyCreatedApiKeyDisplayDiv = document.getElementById('newly-created-api-key-display');const newOauthClientCredentialsDiv = document.getElementById('new-oauth-client-credentials');if (newlyCreatedApiKeyDisplayDiv) newlyCreatedApiKeyDisplayDiv.classList.add('hidden');if (newOauthClientCredentialsDiv) newOauthClientCredentialsDiv.classList.add('hidden');if (window.innerWidth < 769 && sidebarEl && sidebarEl.classList.contains('open')) {toggleSidebar();}if (paneIdToActivate === 'tab-content-messaging') {if (typeof window.updateUnreadMessagesIndicator === 'function') {window.updateUnreadMessagesIndicator();} else if (userPresenceSocket && userPresenceSocket.readyState === WebSocket.OPEN) {userPresenceSocket.send(JSON.stringify({type: "REQUEST_INITIAL_STATE"}));}}
}
function displayCorrectView(userData) {clearMessages();currentUserData = userData;const isLoggedIn = !!userData?.email;const isAdmin = isLoggedIn && userData.is_admin === true;document.querySelectorAll('.cf-turnstile').forEach(div => removeTurnstile(div));if (topBarUserInfoEl) topBarUserInfoEl.classList.toggle('hidden', !isLoggedIn);if (isLoggedIn && topBarUserEmailEl) topBarUserEmailEl.textContent = userData.email || '未知邮箱';if (isLoggedIn && topBarUserUsernameEl) topBarUserUsernameEl.textContent = userData.username || '用户';if (topBarAuthButtonsEl) topBarAuthButtonsEl.classList.toggle('hidden', isLoggedIn);if (topBarMessagingButton) topBarMessagingButton.classList.toggle('hidden', !isLoggedIn);if (topBarAdminLinkEl) topBarAdminLinkEl.classList.toggle('hidden', !isAdmin);const adminTabsUl = document.getElementById('admin-tabs');if (adminTabsUl) adminTabsUl.classList.toggle('hidden', !isAdmin);const currentPath = window.location.pathname;if (isLoggedIn) {if (sidebarEl) sidebarEl.classList.remove('hidden');if (appWrapper) appWrapper.classList.add('logged-in-layout');if (appWrapper) appWrapper.classList.remove('logged-out-layout');if (['/', '/user/login', '/user/register'].includes(currentPath)) {window.location.pathname = isAdmin ? '/user/admin/users' : '/user/profile';return;}if(authSection) authSection.classList.add('hidden');if(loggedInSection) loggedInSection.classList.toggle('hidden', currentPath.startsWith('/user/admin'));if(adminSection) adminSection.classList.toggle('hidden', !currentPath.startsWith('/user/admin'));if (typeof window.initializePersonalInfoForm === 'function') window.initializePersonalInfoForm(userData);connectUserPresenceWebSocket();let paneIdToActivate = pathToPaneIdMap[currentPath];if (!paneIdToActivate) {paneIdToActivate = isAdmin ? 'tab-content-admin-users' : 'tab-content-personal-info';}if (!isAdmin && paneIdToActivate.startsWith('tab-content-admin-')) {window.location.pathname = '/user/profile'; return;}activatePane(paneIdToActivate);const allNavLinks = [...accountTabLinks, ...adminTabLinks];allNavLinks.forEach(link => {link.classList.toggle('selected', link.dataset.paneId === paneIdToActivate);});} else {if (userPresenceSocket && userPresenceSocket.readyState === WebSocket.OPEN) {userPresenceSocket.close();userPresenceSocket = null;}if (sidebarEl) sidebarEl.classList.add('hidden');if (appWrapper) appWrapper.classList.remove('logged-in-layout');if (appWrapper) appWrapper.classList.add('logged-out-layout');if (mainContentContainerEl) mainContentContainerEl.classList.remove('messaging-active');if (Object.keys(pathToPaneIdMap).includes(currentPath) || currentPath === '/user/account' || currentPath.startsWith('/user/admin')) {window.location.pathname = '/user/login'; return;}if(loggedInSection) loggedInSection.classList.add('hidden');if(adminSection) adminSection.classList.add('hidden');if(authSection) authSection.classList.remove('hidden');const login2FASection = document.getElementById('login-2fa-section');const loginFormContainer = document.getElementById('login-form');const registerFormContainer = document.getElementById('register-form');if (currentPath === '/' || currentPath === '/user/login') {if(loginFormContainer) { loginFormContainer.classList.remove('hidden'); if(loginFormEl) loginFormEl.reset(); renderTurnstile(loginFormContainer.querySelector('.cf-turnstile')); }if(registerFormContainer) registerFormContainer.classList.add('hidden');} else if (currentPath === '/user/register') {if(loginFormContainer) loginFormContainer.classList.add('hidden');if(registerFormContainer) { registerFormContainer.classList.remove('hidden'); if(registerFormEl) registerFormEl.reset(); renderTurnstile(registerFormContainer.querySelector('.cf-turnstile'));}}if(login2FASection) login2FASection.classList.add('hidden');loginEmailFor2FA = null;updateUnreadMessagesIndicatorUI(0);}
}
async function fetchAppConfigAndInitialize() {try {const response = await apiCall('/api/config');if (response.ok && response.data.turnstileSiteKey) {TURNSTILE_SITE_KEY = response.data.turnstileSiteKey;}} catch (error) { }await checkLoginStatus();
}
async function checkLoginStatus() {const { ok, status, data } = await apiCall('/api/me');displayCorrectView(ok && data.email ? data : null);const urlParams = new URLSearchParams(window.location.search);if (urlParams.has('registered') && (window.location.pathname === '/user/login' || window.location.pathname === '/')) {showMessage('注册成功!请使用您的邮箱或用户名登录。', 'success');const newUrl = new URL(window.location);newUrl.searchParams.delete('registered');window.history.replaceState({}, document.title, newUrl.toString());}
}
document.addEventListener('DOMContentLoaded', () => {authSection = document.getElementById('auth-section');loggedInSection = document.getElementById('logged-in-section');adminSection = document.getElementById('admin-section');loginFormEl = document.getElementById('login-form-el');registerFormEl = document.getElementById('register-form-el');appWrapper = document.getElementById('app-wrapper');topBarUserEmailEl = document.getElementById('top-bar-user-email');topBarUserUsernameEl = document.getElementById('top-bar-user-username');topBarUserInfoEl = document.getElementById('top-bar-user-info');topBarAuthButtonsEl = document.getElementById('top-bar-auth-buttons');topBarLogoutButtonEl = document.getElementById('top-bar-logout-button');userMenuButtonEl = document.getElementById('user-menu-button');userDropdownMenuEl = document.getElementById('user-dropdown-menu');topBarAccountLinkEl = document.getElementById('top-bar-account-link');topBarAdminLinkEl = document.getElementById('top-bar-admin-link');topBarMessagingButton = document.getElementById('top-bar-messaging-button');sidebarEl = document.getElementById('sidebar');sidebarToggleEl = document.getElementById('sidebar-toggle');mainContentContainerEl = document.getElementById('main-content').querySelector('.container');sidebarOverlayEl = document.getElementById('sidebar-overlay');accountTabLinks = Array.from(document.querySelectorAll('#account-tabs .sidebar-link'));adminTabLinks = Array.from(document.querySelectorAll('#admin-tabs .sidebar-link'));tabPanes = Array.from(document.querySelectorAll('.tab-pane'));themeToggleButton = document.getElementById('theme-toggle-button');themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');unreadMessagesIndicator = document.getElementById('unread-messages-indicator');let isDarkMode = localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches);applyTheme(isDarkMode);if (themeToggleButton) {themeToggleButton.addEventListener('click', () => {isDarkMode = !isDarkMode;localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');applyTheme(isDarkMode);});}window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {if (localStorage.getItem('theme') === null) {isDarkMode = e.matches;applyTheme(isDarkMode);}});[...accountTabLinks, ...adminTabLinks].forEach(link => {link.addEventListener('click', (event) => {});});if (topBarMessagingButton) {topBarMessagingButton.addEventListener('click', (e) => {e.preventDefault();window.location.pathname = '/user/messaging';});}if (sidebarToggleEl) sidebarToggleEl.addEventListener('click', toggleSidebar);if (sidebarOverlayEl) sidebarOverlayEl.addEventListener('click', toggleSidebar);if (userMenuButtonEl && userDropdownMenuEl) {userMenuButtonEl.addEventListener('click', (event) => {event.stopPropagation();userDropdownMenuEl.classList.toggle('hidden');});document.addEventListener('click', (event) => {if (!userDropdownMenuEl.classList.contains('hidden') && !userMenuButtonEl.contains(event.target) && !userDropdownMenuEl.contains(event.target)) {userDropdownMenuEl.classList.add('hidden');}});}if (topBarAccountLinkEl) {topBarAccountLinkEl.addEventListener('click', (e) => {e.preventDefault();window.location.pathname = '/user/profile';if (userDropdownMenuEl) userDropdownMenuEl.classList.add('hidden');});}if (topBarAdminLinkEl) {topBarAdminLinkEl.addEventListener('click', (e) => {e.preventDefault();window.location.pathname = '/user/admin/users';if (userDropdownMenuEl) userDropdownMenuEl.classList.add('hidden');});}if (loginFormEl) loginFormEl.addEventListener('submit', (event) => handleAuth(event, 'login'));if (registerFormEl) registerFormEl.addEventListener('submit', (event) => handleAuth(event, 'register'));if (topBarLogoutButtonEl) topBarLogoutButtonEl.addEventListener('click', handleLogout);fetchAppConfigAndInitialize();
});
window.handleAuth = async function(event, type) {event.preventDefault(); clearMessages();const form = event.target;const turnstileContainer = form.querySelector('.cf-turnstile');const turnstileToken = form.querySelector('[name="cf-turnstile-response"]')?.value;const login2FASection = document.getElementById('login-2fa-section');const loginTotpCodeInput = document.getElementById('login-totp-code');if (!turnstileToken && turnstileContainer) {showMessage('人机验证失败,请刷新页面或稍后重试。', 'error');if (turnstileContainer) resetTurnstileInContainer(turnstileContainer);return;}let endpoint = '', requestBody = {};if (type === 'login') {const identifier = form.elements['identifier'].value, password = form.elements['password'].value;const totpCode = loginTotpCodeInput ? loginTotpCodeInput.value : '';if (!identifier || !password) { showMessage('邮箱/用户名和密码不能为空。'); return; }if (loginEmailFor2FA && totpCode) { endpoint = '/api/login/2fa-verify'; requestBody = { email: loginEmailFor2FA, totpCode }; }else { endpoint = '/api/login'; requestBody = { identifier, password, turnstileToken }; }} else {endpoint = '/api/register';const {email, username, password, confirmPassword, phoneNumber} = Object.fromEntries(new FormData(form));if (password !== confirmPassword) { showMessage('两次输入的密码不一致。'); return; }if (!email || !username || !password) { showMessage('邮箱、用户名和密码为必填项。'); return; }if (password.length < 6) { showMessage('密码至少需要6个字符。'); return; }requestBody = { email, username, password, confirmPassword, phoneNumber, turnstileToken };}const { ok, status, data } = await apiCall(endpoint, 'POST', requestBody);if (turnstileContainer) resetTurnstileInContainer(turnstileContainer);if (ok && data.success) {if (data.twoFactorRequired && data.email) {showMessage('需要两步验证。请输入验证码。', 'info'); loginEmailFor2FA = data.email;if(login2FASection) login2FASection.classList.remove('hidden'); if(loginTotpCodeInput) loginTotpCodeInput.focus();} else {form.reset();if(login2FASection) login2FASection.classList.add('hidden'); loginEmailFor2FA = null;if (type === 'login' || (type === 'login' && loginEmailFor2FA) || (data.twoFactorRequired === undefined)) {window.location.href = (data.is_admin === true) ? '/user/admin/users' : '/user/profile';} else {window.location.href = '/user/login?registered=true';}}} else {showMessage(data.error || ('操作失败 (' + status + ')'), 'error', data.details ? true : false);if (type === 'login' && loginEmailFor2FA && status !== 401 && login2FASection) { login2FASection.classList.remove('hidden'); }else if (status === 401 && data.error === '两步验证码无效' && login2FASection) {login2FASection.classList.remove('hidden'); if(loginTotpCodeInput) { loginTotpCodeInput.value = ''; loginTotpCodeInput.focus(); }} else if (login2FASection) { login2FASection.classList.add('hidden'); loginEmailFor2FA = null; }}
};
window.handleLogout = async function() {clearMessages();if (userPresenceSocket && userPresenceSocket.readyState === WebSocket.OPEN) {userPresenceSocket.close();}if (typeof window.closeActiveConversationSocket === 'function') {window.closeActiveConversationSocket();}await apiCall('/api/logout', 'POST');currentUserData = null;window.location.href = '/user/login';
};
window.turnstileCallbackLogin = function(token) { };
window.turnstileCallbackRegister = function(token) { };
window.turnstileCallbackPasteApi = function(token) { };
window.turnstileCallbackCloudPc = function(token) { };
window.turnstileCallbackOauthClient = function(token) { };
window.copyToClipboard = function(text, itemNameToCopy = '内容') {if (!text) { showMessage('没有可复制的'+itemNameToCopy+'。', 'warning'); return; }navigator.clipboard.writeText(text).then(() => {showMessage(itemNameToCopy + '已复制到剪贴板!', 'success');}).catch(err => {showMessage('复制失败: ' + err, 'error');});
};
window.escapeHtml = function(unsafe) {if (unsafe === null || typeof unsafe === 'undefined') return '';return String(unsafe).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
};
window.apiCall = apiCall;
window.showMessage = showMessage;
window.clearMessages = clearMessages;
window.renderTurnstile = renderTurnstile;
window.removeTurnstile = removeTurnstile;
window.resetTurnstileInContainer = resetTurnstileInContainer;
window.checkLoginStatus = checkLoginStatus;
window.isValidEmail = function(email) {if (typeof email !== 'string') return false;const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;return emailRegex.test(email);
};
window.updateUnreadMessagesIndicatorUI = updateUnreadMessagesIndicatorUI;