CVNSS commited on
Commit
96c5562
·
verified ·
1 Parent(s): 17b561f

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +59 -76
index.html CHANGED
@@ -6,18 +6,19 @@
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
  :root{
9
- /* Light theme tối giản */
10
  --bg:#f7f9fc; --card:#ffffff; --text:#1f2937; --muted:#6b7280;
11
  --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
12
- /* Overlays cho iframe (ẩn header & panel phải) */
13
- --ov-top: 128px; /* tăng lên 140–180 nếu vẫn hở header tlk.io */
14
- --ov-right: clamp(220px, 24vw, 300px);
15
  }
16
  *{box-sizing:border-box}
17
  body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
18
  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:var(--card);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}
22
  .muted{color:var(--muted)}
23
  .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
@@ -29,33 +30,46 @@
29
  .btn-secondary{background:#374151;color:#fff}
30
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
31
 
32
- /* Trạng thái gọn: tên + phòng */
33
  .status{display:none;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #d1d5db;border-radius:10px;margin-bottom:10px;background:#f9fafb}
34
  .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}
35
  .room-pill{display:inline-block;background:#e8f0fe;color:#174ea6;padding:6px 10px;border-radius:999px}
36
 
37
- /* Khung chat (iframe) */
38
- .chat-wrap{position:relative;border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;background:#fff}
39
- iframe{width:100%;height:72vh;min-height:640px;border:0;background:#fff}
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- /* Overlays ẩn đúng phần thừa của tlk.io, không chặn click */
42
- .ov-top{position:absolute;left:0;right:0;top:0;height:var(--ov-top);background:#fff;z-index:3;pointer-events:none;box-shadow:0 8px 10px -10px rgba(0,0,0,.12)}
43
- .ov-right{position:absolute;top:0;right:0;bottom:0;width:var(--ov-right);background:#fff;z-index:2;pointer-events:none;box-shadow:-8px 0 10px -10px rgba(0,0,0,.12)}
44
- .focus-bottom{position:absolute;left:0;right:var(--ov-right);bottom:0;height:110px;pointer-events:none;z-index:1;
45
- background:radial-gradient(120% 120% at 50% 100%, rgba(26,115,232,.10) 0%, rgba(26,115,232,.04) 40%, rgba(255,255,255,0) 75%)}
46
 
47
  /* Link phòng tự ẩn */
48
  .links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
49
  .link-badge{background:#f3f4f6;border:1px solid #e5e7eb;color:#111827;padding:6px 8px;border-radius:8px}
50
 
51
- .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}
52
- .toast.show{opacity:1}
53
-
54
- /* Thông báo lỗi/blocked */
55
  .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}
56
  .error-text{color:#991b1b}
 
 
 
57
  footer{margin-top:auto;text-align:center;color:#6b7280;font-size:13px;padding:16px}
58
- @media (max-width:820px){ :root{ --ov-right:0px } }
 
59
  </style>
60
  </head>
61
  <body>
@@ -63,12 +77,7 @@
63
 
64
  <section id="login" class="card">
65
  <h2>Tham gia phòng chat</h2>
66
- <p class="muted">Nhập tên → <b>Vào Chat</b> → (tuỳ chọn) <b>✨ Tạo phòng bí mật</b> để mời người khác.</p>
67
- <ol style="margin:0 0 10px 18px">
68
- <li>Nhập tên ở ô dưới.</li>
69
- <li>Nhấn <b>Vào Chat</b> (nút đỏ). Form Join bên trong tlk.io luôn bị ẩn.</li>
70
- <li><b>✨ Tạo phòng bí mật</b> nếu cần (ID ngẫu nhiên + Copy tự ẩn). <span class="muted">Không cần thì bỏ qua.</span></li>
71
- </ol>
72
 
73
  <div class="row" style="margin-top:8px">
74
  <input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
@@ -94,18 +103,14 @@
94
  </div>
95
 
96
  <div class="chat-wrap">
97
- <!-- Overlays: ẩn header kênh + cột phải -->
98
- <div class="ov-top"></div>
99
- <div class="ov-right"></div>
100
- <div class="focus-bottom"></div>
101
-
102
- <!-- KHUNG CHAT (iframe) -->
103
- <iframe id="chatFrame" title="tlk.io chat" referrerpolicy="no-referrer" allow="clipboard-read; clipboard-write"></iframe>
104
  </div>
105
 
106
- <!-- Fallback khi iframe bị chặn -->
107
  <div id="errorBox" class="error-box">
108
- <span class="error-text">⚠️ Không tải được khung chat trong trang (trình duyệt/nhà mạng đang chặn iframe).</span>
109
  <a id="openNew" class="btn-red" style="text-decoration:none;padding:10px 12px;border-radius:10px" target="_blank" rel="noopener">Mở cửa sổ chat</a>
110
  </div>
111
  </section>
@@ -120,66 +125,45 @@
120
  let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
121
  const base = `${location.origin}${location.pathname}`;
122
 
 
123
  const els = {
124
- login: document.getElementById("login"),
125
- box: document.getElementById("chatBox"),
126
- frame: document.getElementById("chatFrame"),
127
- name: document.getElementById("nickname"),
128
- join: document.getElementById("joinBtn"),
129
- newRoom: document.getElementById("newRoomBtn"),
130
- linkArea: document.getElementById("linkArea"),
131
- roomUrl: document.getElementById("roomUrl"),
132
- copy: document.getElementById("copyBtn"),
133
- hint: document.getElementById("roomHint"),
134
- me: document.getElementById("meName"),
135
- pill: document.getElementById("roomPill"),
136
- status: document.getElementById("statusBar"),
137
- toast: document.getElementById("toast"),
138
- err: document.getElementById("errorBox"),
139
- openNew: document.getElementById("openNew"),
140
  };
141
 
142
- // ===== Helpers =====
143
- const toast = (m)=>{ els.toast.textContent=m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"), 1400); };
144
- const getName = ()=> (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
145
- const setName = (n)=>{ els.me.textContent=n; localStorage.setItem("cvnss4_name", n); };
146
- const syncRoomUI = ()=>{ els.pill.textContent=room; els.hint.textContent=`(đang ở phòng ${room})`; };
147
- const tlkUrl = (n)=> `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(n)}#embed`;
148
 
149
- // ===== Core: load iframe + fallback =====
150
  function mountChat(){
151
  const name = getName(); setName(name); syncRoomUI();
152
  els.status.style.display = "flex";
153
 
154
  const url = tlkUrl(name);
155
- els.frame.src = url;
156
- els.openNew.href = url;
157
- els.err.style.display = "none";
158
-
159
- // Nếu iframe không load trong 6s → hiện fallback
160
- let failTimer = setTimeout(()=>{ els.err.style.display = "flex"; }, 6000);
161
- els.frame.addEventListener("load", () => {
162
- clearTimeout(failTimer);
163
- // Một số trình duyệt vẫn load event nhưng nội dung bị chặn; thêm watchdog ẩn hiện
164
- setTimeout(()=> {
165
- // Không thể đọc bên trong iframe (cross-origin), nên hiển thị luôn; người dùng vẫn có thể chat nếu hiện.
166
- // Nếu vẫn trắng, user có nút "Mở cửa sổ chat".
167
- /* no-op */
168
- }, 300);
169
- }, {once:true});
170
  }
171
 
172
  function enterChat(){
173
  els.login.style.display = "none";
174
  els.box.style.display = "block";
175
  mountChat();
176
- setTimeout(()=> els.box.scrollIntoView({behavior:"smooth"}), 40);
 
177
  }
178
 
179
- // ===== Events =====
180
  els.join.addEventListener("click", enterChat);
181
  els.name.addEventListener("keydown", e => { if(e.key === "Enter") enterChat(); });
182
 
 
183
  els.newRoom.addEventListener("click", async ()=>{
184
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
185
  syncRoomUI();
@@ -202,10 +186,9 @@
202
  };
203
  });
204
 
205
- // ===== Prefill + auto-enter nếu đã room & tên =====
206
  const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
207
  syncRoomUI(); els.name.focus({preventScroll:true});
208
- // Nếu URL có ?room= và có tên trước đó → tự vào chat
209
  if (qs.get("room") && (last || els.name.value.trim() !== "")) enterChat();
210
  })();
211
  </script>
 
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
  :root{
9
+ /* Light + tham số cắt header/panel của tlk.io */
10
  --bg:#f7f9fc; --card:#ffffff; --text:#1f2937; --muted:#6b7280;
11
  --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
12
+ --mask-top: 150px; /* CẮT header tlk.io (tăng 160–200 nếu còn hở) */
13
+ --mask-right: clamp(220px, 24vw, 300px); /* Ẩn panel phải */
 
14
  }
15
  *{box-sizing:border-box}
16
  body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
17
  background:var(--bg);color:var(--text);display:flex;flex-direction:column;min-height:100vh}
18
  header{padding:14px 16px;text-align:center;font-weight:700;background:#fff;border-bottom:1px solid #e5e7eb}
19
+
20
+ .card{width:min(980px,94vw);margin:22px auto;background:var(--card);
21
+ border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 6px 18px rgba(0,0,0,.06);padding:18px}
22
  h2{margin:0 0 6px}
23
  .muted{color:var(--muted)}
24
  .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
 
30
  .btn-secondary{background:#374151;color:#fff}
31
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
32
 
33
+ /* Trạng thái gọn */
34
  .status{display:none;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #d1d5db;border-radius:10px;margin-bottom:10px;background:#f9fafb}
35
  .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}
36
  .room-pill{display:inline-block;background:#e8f0fe;color:#174ea6;padding:6px 10px;border-radius:999px}
37
 
38
+ /* Khung chat: kỹ thuật MASK (không overlay che đầu) */
39
+ .chat-wrap{
40
+ position:relative; border:1px solid #e5e7eb; border-radius:10px; overflow:hidden; background:#fff;
41
+ padding-top: var(--mask-top); /* chừa chỗ phần bị dịch âm */
42
+ }
43
+ #chatFrame{
44
+ display:block; width:100%; height:72vh; min-height:640px; border:0; background:#fff;
45
+ transform: translateY(calc(-1 * var(--mask-top))); /* đẩy phần header ra ngoài khung nhìn */
46
+ }
47
+
48
+ /* Ẩn panel phải bằng mask phải (không chặn click khu trung tâm/đáy) */
49
+ .mask-right{
50
+ position:absolute; top:0; right:0; bottom:0; width:var(--mask-right);
51
+ background:#fff; pointer-events:none; box-shadow:-8px 0 10px -10px rgba(0,0,0,.12); z-index:2;
52
+ }
53
 
54
+ /* Gợi ý vùng nhập đáy không chặn bấm */
55
+ .hint-bottom{
56
+ position:absolute; left:0; right:var(--mask-right); bottom:0; height:110px; pointer-events:none; z-index:1;
57
+ background:radial-gradient(120% 120% at 50% 100%, rgba(26,115,232,.08) 0%, rgba(26,115,232,.03) 40%, rgba(255,255,255,0) 75%);
58
+ }
59
 
60
  /* Link phòng tự ẩn */
61
  .links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
62
  .link-badge{background:#f3f4f6;border:1px solid #e5e7eb;color:#111827;padding:6px 8px;border-radius:8px}
63
 
64
+ /* Fallback khi iframe bị chặn */
 
 
 
65
  .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}
66
  .error-text{color:#991b1b}
67
+
68
+ .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}
69
+ .toast.show{opacity:1}
70
  footer{margin-top:auto;text-align:center;color:#6b7280;font-size:13px;padding:16px}
71
+
72
+ @media (max-width:820px){ :root{ --mask-right:0px } }
73
  </style>
74
  </head>
75
  <body>
 
77
 
78
  <section id="login" class="card">
79
  <h2>Tham gia phòng chat</h2>
80
+ <p class="muted">Nhập tên → <b>Vào Chat</b> → *(tuỳ chọn)* <b>✨ Tạo phòng bí mật</b> để mời người khác.</p>
 
 
 
 
 
81
 
82
  <div class="row" style="margin-top:8px">
83
  <input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
 
103
  </div>
104
 
105
  <div class="chat-wrap">
106
+ <div class="mask-right"></div>
107
+ <div class="hint-bottom"></div>
108
+ <iframe id="chatFrame" title="tlk.io chat" referrerpolicy="no-referrer"
109
+ allow="clipboard-read; clipboard-write"></iframe>
 
 
 
110
  </div>
111
 
 
112
  <div id="errorBox" class="error-box">
113
+ <span class="error-text">⚠️ Không tải được khung chat (trình duyệt/ISP chặn iframe).</span>
114
  <a id="openNew" class="btn-red" style="text-decoration:none;padding:10px 12px;border-radius:10px" target="_blank" rel="noopener">Mở cửa sổ chat</a>
115
  </div>
116
  </section>
 
125
  let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
126
  const base = `${location.origin}${location.pathname}`;
127
 
128
+ const $ = id => document.getElementById(id);
129
  const els = {
130
+ login: $("login"), box: $("chatBox"), frame: $("chatFrame"),
131
+ name: $("nickname"), join: $("joinBtn"), newRoom: $("newRoomBtn"),
132
+ linkArea: $("linkArea"), roomUrl: $("roomUrl"), copy: $("copyBtn"),
133
+ hint: $("roomHint"), me: $("meName"), pill: $("roomPill"),
134
+ status: $("statusBar"), toast: $("toast"), err: $("errorBox"), openNew: $("openNew")
 
 
 
 
 
 
 
 
 
 
 
135
  };
136
 
137
+ const toast = m => { els.toast.textContent=m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"),1400); };
138
+ const getName = () => (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
139
+ const setName = n => { els.me.textContent=n; localStorage.setItem("cvnss4_name", n); };
140
+ const syncRoomUI = () => { els.pill.textContent=room; els.hint.textContent=`(đang ở phòng ${room})`; };
141
+ const tlkUrl = n => `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(n)}#embed`;
 
142
 
 
143
  function mountChat(){
144
  const name = getName(); setName(name); syncRoomUI();
145
  els.status.style.display = "flex";
146
 
147
  const url = tlkUrl(name);
148
+ els.frame.src = url; els.openNew.href = url; els.err.style.display = "none";
149
+
150
+ // Watchdog: nếu 6s vẫn trắng → hiện fallback
151
+ const timer = setTimeout(()=>{ els.err.style.display = "flex"; }, 6000);
152
+ els.frame.addEventListener("load", ()=> clearTimeout(timer), {once:true});
 
 
 
 
 
 
 
 
 
 
153
  }
154
 
155
  function enterChat(){
156
  els.login.style.display = "none";
157
  els.box.style.display = "block";
158
  mountChat();
159
+ // KHÔNG scrollIntoView sau này nữa để tránh “nhảy khung” khi Enter
160
+ window.scrollBy({ top: 1, behavior: "smooth" }); // nudge nhỏ để ổn định viewport
161
  }
162
 
 
163
  els.join.addEventListener("click", enterChat);
164
  els.name.addEventListener("keydown", e => { if(e.key === "Enter") enterChat(); });
165
 
166
+ // Tạo phòng bí mật + Copy tự ẩn
167
  els.newRoom.addEventListener("click", async ()=>{
168
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
169
  syncRoomUI();
 
186
  };
187
  });
188
 
189
+ // Prefill + auto-enter nếu có tên sẵn
190
  const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
191
  syncRoomUI(); els.name.focus({preventScroll:true});
 
192
  if (qs.get("room") && (last || els.name.value.trim() !== "")) enterChat();
193
  })();
194
  </script>