CVNSS commited on
Commit
baed586
·
verified ·
1 Parent(s): 58b0a9a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +110 -60
index.html CHANGED
@@ -6,60 +6,96 @@
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
  :root{
9
- /* Light theme */
10
  --bg:#f7f9fc; --card:#ffffff; --text:#1f2937; --muted:#6b7280;
11
  --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
12
- /* CẮT phần đầu tlk.io (icon mây + thanh xanh) — tăng/giảm nếu cần */
13
- --cut-top: 220px;
14
- /* Che cột phải */
15
- --mask-right: clamp(220px, 24vw, 300px);
16
  }
 
17
  *{box-sizing:border-box}
18
- body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;background:var(--bg);color:var(--text);display:flex;flex-direction:column;min-height:100vh}
19
- header{padding:14px 16px;text-align:center;font-weight:700;background:#fff;border-bottom:1px solid #e5e7eb}
20
- .card{width:min(980px,94vw);margin:22px auto;background:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 6px 18px rgba(0,0,0,.06);padding:18px}
21
- h2{margin:0 0 6px} .muted{color:var(--muted)}
22
- .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
23
- input[type=text]{flex:1 1 260px;padding:12px 14px;font-size:16px;border:1px solid #d1d5db;border-radius:10px}
24
- input[type=text]:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px rgba(26,115,232,.15)}
25
- button{padding:12px 16px;border:0;border-radius:10px;font-weight:700;cursor:pointer}
26
- .btn-red{background:#d93025;color:#ffeb3b}
27
- .btn-ghost{background:#eef2ff;color:#1e3a8a}
28
- .btn-secondary{background:#374151;color:#fff}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
30
 
31
- /* Status gọn */
32
- .status{display:none;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #d1d5db;border-radius:10px;margin-bottom:10px;background:#f9fafb}
33
- .me-pill{display:inline-flex;align-items:center;gap:6px;background:#fff3cd;color:#7a5b00;border:1px solid #ffe8a1;padding:6px 12px;border-radius:999px;font-weight:800}
34
- .room-pill{display:inline-block;background:#e8f0fe;color:#174ea6;padding:6px 10px;border-radius:999px}
 
 
 
 
 
 
35
 
36
- /* Khung chat: CẮT phần đầu an toàn (không chặn click/nhập) */
37
  .chat-wrap{
38
  position:relative; border:1px solid #e5e7eb; border-radius:10px; overflow:hidden; background:#fff;
39
- padding-top: var(--cut-top); /* chừa chỗ cho phần bị đẩy ra */
40
  }
41
- #chatFrame{
42
- display:block; width:100%; height:72vh; min-height:640px; border:0; background:#fff;
43
- transform: translateY(calc(-1 * var(--cut-top))); /* đẩy phần đầu ra ngoài khung nhìn */
 
 
 
 
44
  }
45
- /* Che cột phải – không chặn click (pointer-events:none) */
46
  .mask-right{
47
  position:absolute; top:0; right:0; bottom:0; width:var(--mask-right);
48
- background:#fff; pointer-events:none; box-shadow:-8px 0 10px -10px rgba(0,0,0,.12); z-index:2;
 
49
  }
50
 
51
  /* Link phòng tự ẩn */
52
- .links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
53
- .link-badge{background:#f3f4f6;border:1px solid #e5e7eb;color:#111827;padding:6px 8px;border-radius:8px}
54
 
55
  /* Fallback khi iframe bị chặn */
56
- .error-box{display:none;gap:10px;align-items:center;justify-content:space-between;padding:12px;border:1px solid #fecaca;background:#fef2f2;border-radius:10px;margin-top:10px}
 
 
 
57
  .error-text{color:#991b1b}
58
 
59
- .toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:#111827;color:#f9fafb;border:1px solid #374151;padding:10px 14px;border-radius:10px;font-size:14px;opacity:0;pointer-events:none;transition:opacity .2s}
 
 
 
 
60
  .toast.show{opacity:1}
61
- footer{margin-top:auto;text-align:center;color:#6b7280;font-size:13px;padding:16px}
62
- @media (max-width:820px){ :root{ --mask-right:0px } }
 
 
63
  </style>
64
  </head>
65
  <body>
@@ -67,18 +103,15 @@
67
 
68
  <section id="login" class="card">
69
  <h2>Tham gia phòng chat</h2>
70
- <p class="muted">Nhập tên → <b>Vào Chat</b> → *(tuỳ chọn)* <b>✨ Tạo phòng bí mật</b>.</p>
71
-
72
  <div class="row" style="margin-top:8px">
73
  <input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
74
  <button id="joinBtn" class="btn-red">Vào Chat</button>
75
  </div>
76
-
77
  <div class="row" style="margin-top:10px">
78
  <button id="newRoomBtn" class="btn-ghost">✨ Tạo phòng bí mật</button>
79
  <span id="roomHint" class="muted"></span>
80
  </div>
81
-
82
  <div id="linkArea" class="links" aria-live="polite">
83
  <span>🔗 Link phòng:</span>
84
  <span id="roomUrl" class="link-badge mono"></span>
@@ -93,7 +126,10 @@
93
  </div>
94
 
95
  <div class="chat-wrap">
 
 
96
  <div class="mask-right" aria-hidden="true"></div>
 
97
  <iframe id="chatFrame" title="tlk.io chat" referrerpolicy="no-referrer"
98
  allow="clipboard-read; clipboard-write"></iframe>
99
  </div>
@@ -109,61 +145,75 @@
109
 
110
  <script>
111
  (() => {
112
- const qs = new URLSearchParams(location.search);
113
- let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
 
114
  const base = `${location.origin}${location.pathname}`;
115
 
 
 
 
 
 
 
116
  const $ = id => document.getElementById(id);
117
  const els = {
118
- login:$("login"), box:$("chatBox"), frame:$("chatFrame"),
119
- name:$("nickname"), join:$("joinBtn"), newRoom:$("newRoomBtn"),
120
- linkArea:$("linkArea"), roomUrl:$("roomUrl"), copy:$("copyBtn"),
121
- hint:$("roomHint"), me:$("meName"), pill:$("roomPill"),
122
- status:$("statusBar"), toast:$("toast"), err:$("errorBox"), openNew:$("openNew")
123
  };
124
 
125
- const toast = m=>{ els.toast.textContent=m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"),1400); };
126
- const getName = ()=> (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
127
- const setName = n=>{ els.me.textContent=n; localStorage.setItem("cvnss4_name", n); };
128
- const syncRoomUI = ()=>{ els.pill.textContent=room; els.hint.textContent=`(đang ở phòng ${room})`; };
129
- const tlkUrl = n=> `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(n)}#embed`;
130
 
131
  function mountChat(){
132
  const name = getName(); setName(name); syncRoomUI();
133
  els.status.style.display = "flex";
134
  const url = tlkUrl(name);
135
- els.frame.src = url; els.openNew.href = url; els.err.style.display="none";
136
- const timer = setTimeout(()=>{ els.err.style.display="flex"; }, 6000);
 
 
137
  els.frame.addEventListener("load", ()=> clearTimeout(timer), {once:true});
138
  }
 
139
  function enterChat(){
140
- els.login.style.display="none";
141
- els.box.style.display="block";
142
  mountChat();
143
  }
144
 
 
145
  els.join.addEventListener("click", enterChat);
146
- els.name.addEventListener("keydown", e=>{ if(e.key==="Enter") enterChat(); });
147
 
148
- // Tạo phòng bí mật + copy tự ẩn
149
  els.newRoom.addEventListener("click", async ()=>{
150
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
151
- syncRoomUI(); history.replaceState(null,"",`?room=${room}`);
 
152
  const link = `${base}?room=${room}`;
153
- els.roomUrl.textContent = link; els.linkArea.style.display="flex";
154
- try{ await navigator.clipboard?.writeText?.(link); toast("✅ Link đã copy!"); }catch(_){}
 
155
  setTimeout(()=>{ if(els.linkArea.style.display!=="none") els.linkArea.style.display="none"; }, 9000);
 
156
  els.copy.onclick = async ()=>{
157
  try{
158
  await navigator.clipboard?.writeText?.(link);
159
  if(navigator.share){ try{ await navigator.share({title:"Phòng chat CVNSS4.0", url:link}); }catch(_){} }
160
  toast("✅ Đã copy link phòng!");
161
  }catch(_){ toast("⚠️ Không copy được—hãy copy thủ công"); }
162
- els.linkArea.style.display="none";
163
  };
164
  });
165
 
166
- // Prefill + auto-enter khi có tên
167
  const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
168
  syncRoomUI(); els.name.focus({preventScroll:true});
169
  if (qs.get("room") && (last || els.name.value.trim() !== "")) enterChat();
 
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
  :root{
9
+ /* Giao diện sáng, tối giản */
10
  --bg:#f7f9fc; --card:#ffffff; --text:#1f2937; --muted:#6b7280;
11
  --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
12
+
13
+ /* Mặt nạ (mask) để ẩn phần thừa của tlk.io */
14
+ --header-mask: 260px; /* dải trắng che icon mây + thanh xanh tiêu đề */
15
+ --mask-right: clamp(220px, 24vw, 300px); /* dải trắng che cột phải */
16
  }
17
+
18
  *{box-sizing:border-box}
19
+ body{
20
+ margin:0; font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
21
+ background:var(--bg); color:var(--text); display:flex; flex-direction:column; min-height:100vh;
22
+ }
23
+ header{
24
+ padding:14px 16px; text-align:center; font-weight:700;
25
+ background:#fff; border-bottom:1px solid #e5e7eb;
26
+ }
27
+
28
+ .card{
29
+ width:min(980px,94vw); margin:22px auto; background:var(--card);
30
+ border:1px solid #e5e7eb; border-radius:12px; box-shadow:0 6px 18px rgba(0,0,0,.06);
31
+ padding:18px;
32
+ }
33
+ h2{margin:0 0 6px}
34
+ .muted{color:var(--muted)}
35
+ .row{display:flex; gap:10px; flex-wrap:wrap; align-items:center}
36
+
37
+ input[type=text]{
38
+ flex:1 1 260px; padding:12px 14px; font-size:16px;
39
+ border:1px solid #d1d5db; border-radius:10px;
40
+ }
41
+ input[type=text]:focus{outline:none; border-color:var(--primary); box-shadow:0 0 0 3px rgba(26,115,232,.15)}
42
+
43
+ button{padding:12px 16px; border:0; border-radius:10px; font-weight:700; cursor:pointer}
44
+ .btn-red{background:var(--danger); color:var(--accent)}
45
+ .btn-ghost{background:#eef2ff; color:#1e3a8a}
46
+ .btn-secondary{background:#374151; color:#fff}
47
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
48
 
49
+ /* Trạng thái gọn: chỉ tên + phòng */
50
+ .status{
51
+ display:none; align-items:center; gap:10px;
52
+ padding:10px 12px; border:1px dashed #d1d5db; border-radius:10px; margin-bottom:10px; background:#f9fafb;
53
+ }
54
+ .me-pill{
55
+ display:inline-flex; align-items:center; gap:6px;
56
+ background:#fff3cd; color:#7a5b00; border:1px solid #ffe8a1; padding:6px 12px; border-radius:999px; font-weight:800;
57
+ }
58
+ .room-pill{display:inline-block; background:#e8f0fe; color:#174ea6; padding:6px 10px; border-radius:999px}
59
 
60
+ /* Khung chat */
61
  .chat-wrap{
62
  position:relative; border:1px solid #e5e7eb; border-radius:10px; overflow:hidden; background:#fff;
 
63
  }
64
+ iframe#chatFrame{width:100%; height:72vh; min-height:640px; border:0; background:#fff}
65
+
66
+ /* Mask TRẮNG không cản bấm/nhập (pointer-events:none) */
67
+ .mask-header{
68
+ position:absolute; left:0; right:var(--mask-right); top:0; height:var(--header-mask);
69
+ background:#fff; pointer-events:none; user-select:none; z-index:5;
70
+ box-shadow:0 8px 10px -10px rgba(0,0,0,.12);
71
  }
 
72
  .mask-right{
73
  position:absolute; top:0; right:0; bottom:0; width:var(--mask-right);
74
+ background:#fff; pointer-events:none; user-select:none; z-index:4;
75
+ box-shadow:-8px 0 10px -10px rgba(0,0,0,.12);
76
  }
77
 
78
  /* Link phòng tự ẩn */
79
+ .links{display:none; gap:8px; align-items:center; flex-wrap:wrap; margin-top:10px}
80
+ .link-badge{background:#f3f4f6; border:1px solid #e5e7eb; color:#111827; padding:6px 8px; border-radius:8px}
81
 
82
  /* Fallback khi iframe bị chặn */
83
+ .error-box{
84
+ display:none; gap:10px; align-items:center; justify-content:space-between;
85
+ padding:12px; border:1px solid #fecaca; background:#fef2f2; border-radius:10px; margin-top:10px;
86
+ }
87
  .error-text{color:#991b1b}
88
 
89
+ .toast{
90
+ position:fixed; left:50%; bottom:22px; transform:translateX(-50%);
91
+ background:#111827; color:#f9fafb; border:1px solid #374151;
92
+ padding:10px 14px; border-radius:10px; font-size:14px; opacity:0; pointer-events:none; transition:opacity .2s;
93
+ }
94
  .toast.show{opacity:1}
95
+
96
+ footer{margin-top:auto; text-align:center; color:#6b7280; font-size:13px; padding:16px}
97
+
98
+ @media (max-width:820px){ :root{ --mask-right:0px } } /* mobile thường không có panel phải */
99
  </style>
100
  </head>
101
  <body>
 
103
 
104
  <section id="login" class="card">
105
  <h2>Tham gia phòng chat</h2>
106
+ <p class="muted">Nhập tên → <b>Vào Chat</b> (nút đỏ) → *(tuỳ chọn)* <b>✨ Tạo phòng bí mật</b> để mời người khác. Nếu không cần “✨ Tạo phòng bí mật”, hãy bỏ qua bước này.</p>
 
107
  <div class="row" style="margin-top:8px">
108
  <input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
109
  <button id="joinBtn" class="btn-red">Vào Chat</button>
110
  </div>
 
111
  <div class="row" style="margin-top:10px">
112
  <button id="newRoomBtn" class="btn-ghost">✨ Tạo phòng bí mật</button>
113
  <span id="roomHint" class="muted"></span>
114
  </div>
 
115
  <div id="linkArea" class="links" aria-live="polite">
116
  <span>🔗 Link phòng:</span>
117
  <span id="roomUrl" class="link-badge mono"></span>
 
126
  </div>
127
 
128
  <div class="chat-wrap">
129
+ <!-- Mask trắng: che icon mây + thanh xanh + panel phải; KHÔNG cản click/nhập -->
130
+ <div class="mask-header" aria-hidden="true"></div>
131
  <div class="mask-right" aria-hidden="true"></div>
132
+
133
  <iframe id="chatFrame" title="tlk.io chat" referrerpolicy="no-referrer"
134
  allow="clipboard-read; clipboard-write"></iframe>
135
  </div>
 
145
 
146
  <script>
147
  (() => {
148
+ // --- State ---
149
+ const qs = new URLSearchParams(location.search);
150
+ let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
151
  const base = `${location.origin}${location.pathname}`;
152
 
153
+ // Cho phép tinh chỉnh nhanh qua URL nếu cần: &hdr=260 &maskr=260
154
+ const hdr = parseInt(qs.get("hdr"),10);
155
+ if(!isNaN(hdr)) document.documentElement.style.setProperty('--header-mask', hdr+'px');
156
+ const maskr = parseInt(qs.get("maskr"),10);
157
+ if(!isNaN(maskr)) document.documentElement.style.setProperty('--mask-right', maskr+'px');
158
+
159
  const $ = id => document.getElementById(id);
160
  const els = {
161
+ login: $("login"), box: $("chatBox"), frame: $("chatFrame"),
162
+ name: $("nickname"), join: $("joinBtn"), newRoom: $("newRoomBtn"),
163
+ linkArea: $("linkArea"), roomUrl: $("roomUrl"), copy: $("copyBtn"),
164
+ hint: $("roomHint"), me: $("meName"), pill: $("roomPill"),
165
+ status: $("statusBar"), toast: $("toast"), err: $("errorBox"), openNew: $("openNew"),
166
  };
167
 
168
+ const toast = m => { els.toast.textContent = m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"), 1400); };
169
+ const getName = () => (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
170
+ const setName = n => { els.me.textContent = n; localStorage.setItem("cvnss4_name", n); };
171
+ const syncRoomUI = () => { els.pill.textContent = room; els.hint.textContent = `(đang ở phòng ${room})`; };
172
+ const tlkUrl = n => `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(n)}#embed`;
173
 
174
  function mountChat(){
175
  const name = getName(); setName(name); syncRoomUI();
176
  els.status.style.display = "flex";
177
  const url = tlkUrl(name);
178
+ els.frame.src = url; els.openNew.href = url; els.err.style.display = "none";
179
+
180
+ // Watchdog: nếu iframe không load trong 6s → hiện fallback
181
+ const timer = setTimeout(()=>{ els.err.style.display = "flex"; }, 6000);
182
  els.frame.addEventListener("load", ()=> clearTimeout(timer), {once:true});
183
  }
184
+
185
  function enterChat(){
186
+ els.login.style.display = "none";
187
+ els.box.style.display = "block";
188
  mountChat();
189
  }
190
 
191
+ // Events
192
  els.join.addEventListener("click", enterChat);
193
+ els.name.addEventListener("keydown", e => { if(e.key === "Enter") enterChat(); });
194
 
195
+ // Secret room + copy tự ẩn
196
  els.newRoom.addEventListener("click", async ()=>{
197
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
198
+ syncRoomUI();
199
+ history.replaceState(null,"",`?room=${room}`);
200
  const link = `${base}?room=${room}`;
201
+ els.roomUrl.textContent = link;
202
+ els.linkArea.style.display = "flex";
203
+ try{ await navigator.clipboard?.writeText?.(link); toast("✅ Đã copy link phòng!"); }catch(_){}
204
  setTimeout(()=>{ if(els.linkArea.style.display!=="none") els.linkArea.style.display="none"; }, 9000);
205
+
206
  els.copy.onclick = async ()=>{
207
  try{
208
  await navigator.clipboard?.writeText?.(link);
209
  if(navigator.share){ try{ await navigator.share({title:"Phòng chat CVNSS4.0", url:link}); }catch(_){} }
210
  toast("✅ Đã copy link phòng!");
211
  }catch(_){ toast("⚠️ Không copy được—hãy copy thủ công"); }
212
+ els.linkArea.style.display = "none";
213
  };
214
  });
215
 
216
+ // Prefill + auto-enter nếu đã có tên
217
  const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
218
  syncRoomUI(); els.name.focus({preventScroll:true});
219
  if (qs.get("room") && (last || els.name.value.trim() !== "")) enterChat();