Skip to content

Commit 1bd4a0b

Browse files
committed
微调
1 parent feca676 commit 1bd4a0b

File tree

8 files changed

+336
-126
lines changed

8 files changed

+336
-126
lines changed

client/css/body.css

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ body {
454454
top: 50%;
455455
transform: translateY(-50%);
456456
transition: transform 0.2s ease, color 0.2s ease;
457+
z-index: 5;
457458
}
458459

459460
.chat-attach-btn-abs:hover {
@@ -522,30 +523,49 @@ body {
522523
padding: 0.7em 0.7em;
523524
border-radius: 50px;
524525
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
525-
margin: 0px 0px 3px 10px;
526-
transition: all 0.2s ease;
526+
margin: 0px 0px 3px 7px;
527+
transition: all 0.3s ease;
528+
z-index: 10;
527529
}
528530
.send-message-btn:hover {
529-
background: #fff;
530-
transition: all 0.6s;
531+
transform: translateY(-2px);
532+
transition: all 0.3s ease;
531533
}
532534
.send-message-btn:active {
533-
transform: scale(0.95);
534-
box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.15);
535-
transition: all 0.1s;
535+
transform: scale(0.95) translateY(0px);
536+
box-shadow: 0 0.3rem 0.8rem rgba(3, 169, 244, 0.2);
537+
transition: all 0.1s ease;
536538
}
537539
.send-message-btn .text {
538540
display: flex;
539541
align-items: center;
540542
justify-content: center;
541543
fill: #fff;
542-
transition: all 0.2s;
544+
transition: all 0.3s ease;
543545
}
544546
.send-message-btn:hover .text {
545-
fill: rgb(3, 169, 244);
546-
transition: all 0.6s;
547+
transform: scale(1.05);
548+
transition: all 0.3s ease;
547549
}
548550
.send-message-btn:active .text {
549-
fill: #fff;
550-
transition: all 0.1s;
551+
transform: scale(0.95);
552+
transition: all 0.1s ease;
553+
}
554+
555+
/* 确保发送按钮的点击区域不被其他元素遮挡 */
556+
.send-message-btn {
557+
position: relative;
558+
z-index: 10;
559+
}
560+
561+
/* 确保发送按钮在所有父容器中都有足够的层级 */
562+
.chat-input-wrapper .send-message-btn {
563+
position: relative;
564+
z-index: 15;
551565
}
566+
567+
/* 防止任何绝对定位的元素遮挡发送按钮 */
568+
.new-message-wrapper {
569+
position: relative;
570+
z-index: 1;
571+
}

client/css/style.css

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ flex-direction: column;
2525
display: flex;
2626
flex-direction: column;
2727
box-shadow: 2px 0 8px #e0e0e0a0;
28-
transition: width 0.3s ease;
28+
transition: opacity 0.3s ease;
2929
}
3030

3131
.sidebar-user {
@@ -58,16 +58,25 @@ flex-direction: column;
5858
display: block;
5959
}
6060

61-
/* New settings sidebar styles */
61+
/* Settings sidebar overlay styles */
6262
.settings-sidebar {
6363
width: 250px;
6464
background: #fff;
6565
border-right: 1px solid #e0e0e0;
6666
display: flex;
6767
flex-direction: column;
68-
box-shadow: 2px 0 8px #e0e0e0a0;
69-
transition: width 0.3s ease;
70-
position: relative;
68+
box-shadow: 2px 0 10px #e0e0e0a0;
69+
position: absolute;
70+
top: 0;
71+
left: 0;
72+
height: 100%;
73+
z-index: 1000;
74+
transform: translateX(-100%);
75+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
76+
}
77+
78+
.settings-sidebar.open {
79+
transform: translateX(0);
7180
}
7281

7382
.settings-header {
@@ -544,7 +553,7 @@ flex-direction: column;
544553
.chat-area {
545554
flex: 1;
546555
overflow-y: auto;
547-
padding: 32px 24px 24px 24px;
556+
padding: 15px 15px 15px 15px;
548557
display: flex;
549558
flex-direction: column;
550559
background: none;

client/index.html

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@
77
<link rel="stylesheet" href="css/body.css">
88
<link rel="stylesheet" href="css/phone.css">
99
<link rel="stylesheet" href="css/form-animation.css">
10-
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
11-
<meta name="description" content="NodeCrypt - 真正的端到端加密聊天系统,无数据库,所有消息本地加密,服务器仅做加密数据中转,支持 Cloudflare Workers、Docker、自托管和本地开发。">
12-
<meta name="keywords" content="端到端加密, 安全, 聊天, WebSocket, Cloudflare Workers, JavaScript, E2EE, 匿名通信, AES, ECDH, RSA, ChaCha20, 安全, 开源, NodeCrypt, shuaiplus">
10+
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg"> <meta name="description" content="NodeCrypt - True end-to-end encrypted chat system, no database, all messages encrypted locally, server only relays encrypted data, supports Cloudflare Workers, Docker, self-hosting and local development.">
11+
<meta name="keywords" content="end-to-end encryption, security, chat, WebSocket, Cloudflare Workers, JavaScript, E2EE, anonymous communication, AES, ECDH, RSA, ChaCha20, security, open source, NodeCrypt, shuaiplus">
1312
<meta name="author" content="shuaiplus">
14-
<meta property="og:title" content="NodeCrypt - 端到端加密聊天系统">
15-
<meta property="og:description" content="NodeCrypt 是一个零知识、端到端加密的开源聊天系统,所有加密解密均在客户端本地完成,服务器无法获取明文。支持多平台部署,安全、匿名、无历史消息。">
13+
<meta property="og:title" content="NodeCrypt - End-to-End Encrypted Chat System">
14+
<meta property="og:description" content="NodeCrypt is a zero-knowledge, end-to-end encrypted open source chat system where all encryption and decryption is done locally on the client side, and servers cannot access plaintext. Supports multi-platform deployment, secure, anonymous, no message history.">
1615
<meta property="og:type" content="website">
1716
<meta property="og:url" content="https://github.com/shuaiplus/NodeCrypt">
1817
<meta property="og:image" content="https://crypt.works/client/assets/favicon.svg">
1918
<meta name="twitter:card" content="summary_large_image">
20-
<meta name="twitter:title" content="NodeCrypt - 端到端加密聊天系统">
21-
<meta name="twitter:description" content="NodeCrypt 是一个零知识、端到端加密的开源聊天系统,所有加密解密均在客户端本地完成,服务器无法获取明文。">
19+
<meta name="twitter:title" content="NodeCrypt - End-to-End Encrypted Chat System">
20+
<meta name="twitter:description" content="NodeCrypt is a zero-knowledge, end-to-end encrypted open source chat system where all encryption and decryption is done locally on the client side, and servers cannot access plaintext.">
2221
<meta name="twitter:image" content="https://crypt.works/client/assets/favicon.svg">
2322
<meta name="google-site-verification" content="2KGYZcUDyYzGVeYAQ9mMRUGcTiMyZqFF6D7232SyGgw" />
2423
<style>
@@ -55,30 +54,71 @@ <h1 id="login-title"></h1>
5554
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
5655
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5756
</svg>
58-
</button> <h2 data-i18n="help.usage_guide">使用说明</h2>
57+
</button> <h2 data-i18n="help.usage_guide"></h2>
5958
<div class="help-content">
6059
<div class="help-section">
61-
<h3 data-i18n="help.what_is_nodecrypt">🔐 什么是 NodeCrypt?</h3>
62-
<p data-i18n="help.what_is_nodecrypt_desc">NodeCrypt 是一个开源的端到端加密聊天系统,采用无数据库架构设计。所有消息在您的设备上本地加密,服务器和中间人无法解密您的任何聊天内容。</p>
60+
<h3 data-i18n="help.what_is_nodecrypt"></h3>
61+
<p data-i18n="help.what_is_nodecrypt_desc"></p>
6362
</div>
6463

6564
<div class="help-section">
66-
<h3 data-i18n="help.how_to_start">🚀 如何开始?</h3>
65+
<h3 data-i18n="help.how_to_start"></h3>
6766
<ol>
68-
<li><strong data-i18n="help.step_username">输入用户名</strong><span data-i18n="help.step_username_desc">选择一个昵称</span></li>
69-
<li><strong data-i18n="help.step_node_name">输入节点名</strong><span data-i18n="help.step_node_name_desc">创建或加入现有房间</span></li>
70-
<li><strong data-i18n="help.step_password">设置节点密码</strong><span data-i18n="help.step_password_desc">用于排除巧合性</span></li>
71-
<li><strong data-i18n="help.step_join">点击"加入房间"</strong><span data-i18n="help.step_join_desc">开始安全聊天</span></li>
67+
<li>
68+
<strong data-i18n="help.step_username"></strong>
69+
<span data-i18n="help.step_username_desc"></span>
70+
</li>
71+
<li>
72+
<strong data-i18n="help.step_node_name"></strong>
73+
<span data-i18n="help.step_node_name_desc"></span>
74+
</li>
75+
<li>
76+
<strong data-i18n="help.step_password"></strong>
77+
<span data-i18n="help.step_password_desc"></span>
78+
</li>
79+
<li>
80+
<strong data-i18n="help.step_join"></strong>
81+
<span data-i18n="help.step_join_desc"></span>
82+
</li>
7283
</ol>
84+
<div class="help-important">
85+
<strong data-i18n="help.important_note"></strong><span data-i18n="help.room_isolation_note"></span>
86+
</div>
7387
</div>
7488

7589
<div class="help-section">
76-
<h3 data-i18n="help.security_features">🔑 安全特性</h3>
90+
<h3 data-i18n="help.security_features"></h3>
7791
<ul>
78-
<li><strong data-i18n="help.e2e_encryption">端到端加密</strong><span data-i18n="help.e2e_encryption_desc">消息仅您和接收者可解密</span></li>
79-
<li><strong data-i18n="help.no_history">无历史记录</strong><span data-i18n="help.no_history_desc">新用户无法看到历史消息</span></li>
80-
<li><strong data-i18n="help.password_protection">密码保护</strong><span data-i18n="help.password_protection_desc">房间密码参与加密过程</span></li>
81-
<li><strong data-i18n="help.anonymous_communication">匿名通信</strong><span data-i18n="help.anonymous_communication_desc">无需注册真实身份</span></li>
92+
<li>
93+
<strong data-i18n="help.e2e_encryption"></strong>
94+
<span data-i18n="help.e2e_encryption_desc"></span>
95+
</li>
96+
<li>
97+
<strong data-i18n="help.password_enhanced_encryption"></strong>
98+
<span data-i18n="help.password_enhanced_encryption_desc"></span>
99+
</li>
100+
<li>
101+
<strong data-i18n="help.no_history"></strong>
102+
<span data-i18n="help.no_history_desc"></span>
103+
</li>
104+
<li>
105+
<strong data-i18n="help.anonymous_communication"></strong>
106+
<span data-i18n="help.anonymous_communication_desc"></span>
107+
</li>
108+
<li>
109+
<strong data-i18n="help.decentralized"></strong>
110+
<span data-i18n="help.decentralized_desc"></span>
111+
</li>
112+
</ul>
113+
</div>
114+
115+
<div class="help-section">
116+
<h3 data-i18n="help.usage_tips"></h3>
117+
<ul>
118+
<li><strong data-i18n="help.tip_private_chat"></strong><span data-i18n="help.tip_private_chat_desc"></span></li>
119+
<li><strong data-i18n="help.tip_group_chat"></strong><span data-i18n="help.tip_group_chat_desc"></span></li>
120+
<li><strong data-i18n="help.tip_security_reminder"></strong><span data-i18n="help.tip_security_reminder_desc"></span></li>
121+
<li><strong data-i18n="help.tip_password_strategy"></strong><span data-i18n="help.tip_password_strategy_desc"></span></li>
82122
</ul>
83123
</div>
84124
</div>

client/js/room.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -353,19 +353,6 @@ export function togglePrivateChat(targetId, targetName) {
353353
updateChatInputStyle()
354354
}
355355

356-
// Set the status bar text
357-
// 设置状态栏文本
358-
export function setStatus(text) {
359-
let statusBar = $id('status-bar');
360-
if (!statusBar) {
361-
statusBar = createElement('div', {
362-
id: 'status-bar',
363-
style: 'color:green;padding:4px 10px;font-size:13px;'
364-
});
365-
document.body.appendChild(statusBar)
366-
}
367-
statusBar.innerText = text
368-
}
369356

370357
// Exit the current room
371358
// 退出当前房间

client/js/ui.js

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -232,23 +232,48 @@ export function setupMobileUIHandlers() {
232232
rightbar.classList.remove('mobile-open');
233233
rightbarMask.classList.remove('active')
234234
}
235-
} document.addEventListener('click', function(ev) {
236-
if (!isMobile()) return;
237-
if (sidebar && sidebar.classList.contains('mobile-open')) {
238-
if (!sidebar.contains(ev.target) && ev.target !== mobileMenuBtn) {
239-
sidebar.classList.remove('mobile-open');
240-
if (sidebarMask) sidebarMask.classList.remove('active')
241-
}
242-
}
243-
if (settingsSidebar && settingsSidebar.classList.contains('mobile-open')) {
244-
if (!settingsSidebar.contains(ev.target)) {
235+
} // Consolidated click event listener for closing sidebars
236+
document.addEventListener('click', function(ev) {
237+
const settingsBtn = $id('settings-btn');
238+
const isSettingsButtonClick = settingsBtn && settingsBtn.contains(ev.target);
239+
const isSettingsBackButtonClick = $id('settings-back-btn') && $id('settings-back-btn').contains(ev.target);
240+
241+
// Close settings sidebar if open and click is outside (and not on the open button or back button)
242+
if (settingsSidebar && (settingsSidebar.classList.contains('open') || settingsSidebar.classList.contains('mobile-open'))) {
243+
if (!settingsSidebar.contains(ev.target) && !isSettingsButtonClick && !isSettingsBackButtonClick) {
245244
closeSettingsPanel();
246245
}
247246
}
248-
if (rightbar && rightbar.classList.contains('mobile-open')) {
249-
if (!rightbar.contains(ev.target) && ev.target !== mobileInfoBtn) {
250-
rightbar.classList.remove('mobile-open');
251-
if (rightbarMask) rightbarMask.classList.remove('active')
247+
248+
if (isMobile()) {
249+
// Mobile-specific logic
250+
if (sidebar && sidebar.classList.contains('mobile-open')) {
251+
if (!sidebar.contains(ev.target) && ev.target !== mobileMenuBtn) {
252+
sidebar.classList.remove('mobile-open');
253+
if (sidebarMask) sidebarMask.classList.remove('active');
254+
}
255+
}
256+
if (settingsSidebar && settingsSidebar.classList.contains('mobile-open')) {
257+
// 检查点击目标是否为设置按钮本身
258+
const isSettingsButton = settingsBtn && settingsBtn.contains(ev.target);
259+
if (!settingsSidebar.contains(ev.target) && !isSettingsButton) {
260+
closeSettingsPanel();
261+
}
262+
}
263+
if (rightbar && rightbar.classList.contains('mobile-open')) {
264+
if (!rightbar.contains(ev.target) && ev.target !== mobileInfoBtn) {
265+
rightbar.classList.remove('mobile-open');
266+
if (rightbarMask) rightbarMask.classList.remove('active');
267+
}
268+
}
269+
} else {
270+
// Desktop-specific logic
271+
// 如果设置侧边栏打开,并且点击位置在侧边栏外部且不是设置按钮本身
272+
if (settingsSidebar && settingsSidebar.classList.contains('open')) {
273+
const isSettingsButton = settingsBtn && settingsBtn.contains(ev.target);
274+
if (!settingsSidebar.contains(ev.target) && !isSettingsButton) {
275+
closeSettingsPanel();
276+
}
252277
}
253278
}
254279
})
@@ -633,7 +658,7 @@ export function initFlipCard() {
633658

634659
// 开始持续旋转
635660
function startContinuousRotation() {
636-
if (isHovering) return; // 避免重复启动
661+
if (isHovering) return; // 已经在旋转中
637662
isHovering = true;
638663

639664
// 立即执行一次旋转
@@ -656,18 +681,49 @@ export function initFlipCard() {
656681
}
657682
}
658683

659-
// 帮助按钮事件
660-
helpBtn.addEventListener('mouseenter', startContinuousRotation);
661-
helpBtn.addEventListener('mouseleave', stopContinuousRotation);
684+
// 检查鼠标是否在任一按钮上
685+
function isMouseOverButton(e) {
686+
const helpBtnRect = helpBtn.getBoundingClientRect();
687+
const backBtnRect = backBtn.getBoundingClientRect();
688+
const x = e.clientX;
689+
const y = e.clientY;
690+
691+
// 检查是否在帮助按钮上
692+
const onHelpBtn = x >= helpBtnRect.left && x <= helpBtnRect.right &&
693+
y >= helpBtnRect.top && y <= helpBtnRect.bottom;
694+
695+
// 检查是否在返回按钮上
696+
const onBackBtn = x >= backBtnRect.left && x <= backBtnRect.right &&
697+
y >= backBtnRect.top && y <= backBtnRect.bottom;
698+
699+
return onHelpBtn || onBackBtn;
700+
}
701+
702+
// 监听整个卡片的鼠标移动
703+
flipCard.addEventListener('mousemove', (e) => {
704+
if (isMouseOverButton(e)) {
705+
if (!isHovering) {
706+
startContinuousRotation();
707+
}
708+
} else {
709+
if (isHovering) {
710+
stopContinuousRotation();
711+
}
712+
}
713+
});
714+
715+
// 监听鼠标离开卡片
716+
flipCard.addEventListener('mouseleave', () => {
717+
stopContinuousRotation();
718+
});
719+
720+
// 按钮点击事件(保持原有功能)
662721
helpBtn.addEventListener('click', (e) => {
663722
e.preventDefault();
664723
e.stopPropagation();
665724
performFlip();
666725
});
667726

668-
// 返回按钮事件(同样的逻辑)
669-
backBtn.addEventListener('mouseenter', startContinuousRotation);
670-
backBtn.addEventListener('mouseleave', stopContinuousRotation);
671727
backBtn.addEventListener('click', (e) => {
672728
e.preventDefault();
673729
e.stopPropagation();

client/js/util.emoji.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const addEmojiPickerStyles = () => {
1111
if (document.querySelector('#emoji-picker-styles')) return;
1212
const style = document.createElement('style');
1313
style.id = 'emoji-picker-styles';
14-
style.textContent = `emoji-picker{--background:#fff;--border-color:rgba(0,0,0,0.1);--border-radius:10px;--emoji-padding:0.4rem;--category-emoji-size:1.2rem;--font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;position:absolute;bottom:60px;left:22px;z-index:5;box-shadow:0 3px 12px rgba(0,0,0,0.15);width:320px;display:none;opacity:0;transform:translateY(-10px) scale(0.95);transition:opacity 0.3s ease,transform 0.3s ease}emoji-picker.show{opacity:1;transform:translateY(0) scale(1)}`;
14+
style.textContent = `emoji-picker{--background:#fff;--border-color:rgba(0,0,0,0.1);--border-radius:10px;--emoji-padding:0.4rem;--category-emoji-size:1.2rem;--font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;position:absolute;bottom:60px;left:22px;z-index:5;box-shadow:0 3px 12px rgba(0,0,0,0.15);display:none;opacity:0;transform:translateY(-10px) scale(0.95);transition:opacity 0.3s ease,transform 0.3s ease}emoji-picker.show{opacity:1;transform:translateY(0) scale(1)}`;
1515
document.head.appendChild(style)
1616
};
1717
// Setup emoji picker for chat input

0 commit comments

Comments
 (0)