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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +76 -76
index.html CHANGED
@@ -6,17 +6,17 @@
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
- /* Overlays (chỉ dùng cho iframe) */
13
- --ov-top:140px; /* che header/#channel; tăng nếu còn hở */
14
- --ov-right:clamp(220px,24vw,300px); /* che panel phải */
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:#ffffff;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)}
@@ -26,31 +26,35 @@
26
  button{padding:12px 16px;border:0;border-radius:10px;font-weight:700;cursor:pointer}
27
  .btn-red{background:var(--danger);color:var(--accent)}
28
  .btn-ghost{background:#eef2ff;color:#1e3a8a}
 
29
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
30
 
31
- /* Trạng thái: tên + phòng (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 */
37
  .chat-wrap{position:relative;border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;background:#fff}
38
- #chatMount{min-height:560px}
39
- iframe{width:100%;height:72vh;min-height:560px;border:0;background:#fff}
40
 
41
- /* Overlays – chỉ thêm khi chế độ iframe */
42
- .ov-top,.ov-right,.focus-bottom{display:none} /* mặc định ẩn (embed không cần) */
43
- .iframe-mode .ov-top{display:block;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)}
44
- .iframe-mode .ov-right{display:block;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)}
45
- .iframe-mode .focus-bottom{display:block;position:absolute;left:0;right:var(--ov-right);bottom:0;height:110px;pointer-events:none;z-index:1;
46
- background:radial-gradient(120% 120% at 50% 100%, rgba(26,115,232,.12) 0%, rgba(26,115,232,.05) 40%, rgba(255,255,255,0) 75%)}
47
 
 
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
  .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}
51
  .toast.show{opacity:1}
 
 
 
 
52
  footer{margin-top:auto;text-align:center;color:#6b7280;font-size:13px;padding:16px}
53
- ol.guide{margin:0;padding-left:20px} ol.guide li{margin:6px 0}
54
  @media (max-width:820px){ :root{ --ov-right:0px } }
55
  </style>
56
  </head>
@@ -59,15 +63,14 @@
59
 
60
  <section id="login" class="card">
61
  <h2>Tham gia phòng chat</h2>
62
- <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>
63
-
64
- <ol class="guide">
65
  <li>Nhập tên ở ô dưới.</li>
66
  <li>Nhấn <b>Vào Chat</b> (nút đỏ). Form Join bên trong tlk.io luôn bị ẩn.</li>
67
  <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>
68
  </ol>
69
 
70
- <div class="row" style="margin-top:12px">
71
  <input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
72
  <button id="joinBtn" class="btn-red">Vào Chat</button>
73
  </div>
@@ -80,7 +83,7 @@
80
  <div id="linkArea" class="links" aria-live="polite">
81
  <span>🔗 Link phòng:</span>
82
  <span id="roomUrl" class="link-badge mono"></span>
83
- <button id="copyBtn" class="btn-ghost">📋 Copy</button>
84
  </div>
85
  </section>
86
 
@@ -89,13 +92,21 @@
89
  <span class="me-pill">👤 <span id="meName">Guest</span></span>
90
  <span class="room-pill">Phòng: <span id="roomPill" class="mono"></span></span>
91
  </div>
92
- <div id="chatWrap" class="chat-wrap">
93
- <!-- Overlays chỉ bật khi dùng iframe -->
 
94
  <div class="ov-top"></div>
95
  <div class="ov-right"></div>
96
  <div class="focus-bottom"></div>
97
- <!-- mount cho embed hoặc iframe -->
98
- <div id="chatMount"></div>
 
 
 
 
 
 
 
99
  </div>
100
  </section>
101
 
@@ -104,16 +115,15 @@
104
 
105
  <script>
106
  (() => {
107
- // ====== State ======
108
  const qs = new URLSearchParams(location.search);
109
  let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
110
  const base = `${location.origin}${location.pathname}`;
111
 
112
  const els = {
113
  login: document.getElementById("login"),
114
- chatBox: document.getElementById("chatBox"),
115
- wrap: document.getElementById("chatWrap"),
116
- mount: document.getElementById("chatMount"),
117
  name: document.getElementById("nickname"),
118
  join: document.getElementById("joinBtn"),
119
  newRoom: document.getElementById("newRoomBtn"),
@@ -121,71 +131,59 @@
121
  roomUrl: document.getElementById("roomUrl"),
122
  copy: document.getElementById("copyBtn"),
123
  hint: document.getElementById("roomHint"),
124
- meName: document.getElementById("meName"),
125
- roomPill: document.getElementById("roomPill"),
126
  status: document.getElementById("statusBar"),
127
  toast: document.getElementById("toast"),
 
 
128
  };
129
 
130
- // ====== Helpers ======
131
- const toast = (m)=>{ els.toast.textContent=m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"),1400); };
132
  const getName = ()=> (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
133
- const setName = (n)=>{ els.meName.textContent=n; localStorage.setItem("cvnss4_name", n); };
134
- const syncRoomUI = ()=>{ els.roomPill.textContent=room; els.hint.textContent=`(đang ở phòng ${room})`; };
135
- const clearMount = ()=> els.mount.replaceChildren();
136
-
137
- // ====== Renderers ======
138
- function renderEmbed(name){
139
- clearMount();
140
- els.wrap.classList.remove("iframe-mode"); // không cần overlay
141
- const holder = document.createElement("div");
142
- holder.id = "tlkio";
143
- holder.dataset.channel = room;
144
- holder.dataset.nickname = name;
145
- holder.dataset.theme = "theme--day"; // sáng
146
- holder.style.minHeight = "560px";
147
- els.mount.appendChild(holder);
148
-
149
- const s = document.createElement("script");
150
- s.async = true; s.src = "https://tlk.io/embed.js";
151
- // fallback nếu bị chặn
152
- let t = setTimeout(()=>{ renderIframe(name); }, 3500);
153
- s.onload = ()=> clearTimeout(t);
154
- s.onerror = ()=> { clearTimeout(t); renderIframe(name); };
155
- els.mount.appendChild(s);
156
- }
157
 
158
- function renderIframe(name){
159
- clearMount();
160
- els.wrap.classList.add("iframe-mode"); // bật overlay để ẩn header + cột phải
161
- const url = `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(name)}#embed`;
162
- const ifr = document.createElement("iframe");
163
- ifr.title = "tlk.io chat"; ifr.src = url; ifr.referrerPolicy = "no-referrer";
164
- els.mount.appendChild(ifr);
165
- }
166
-
167
- function renderChat(){
168
- const name = getName();
169
- setName(name); syncRoomUI();
170
  els.status.style.display = "flex";
171
- renderEmbed(name); // sẽ tự fallback sang iframe nếu cần
172
- setTimeout(()=> els.chatBox.scrollIntoView({behavior:"smooth"}), 40);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
- // ====== Actions ======
176
  function enterChat(){
177
  els.login.style.display = "none";
178
- els.chatBox.style.display = "block";
179
- renderChat();
 
180
  }
181
 
 
182
  els.join.addEventListener("click", enterChat);
183
  els.name.addEventListener("keydown", e => { if(e.key === "Enter") enterChat(); });
184
 
185
  els.newRoom.addEventListener("click", async ()=>{
186
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
187
  syncRoomUI();
188
- history.replaceState(null, "", `?room=${room}`);
189
 
190
  const link = `${base}?room=${room}`;
191
  els.roomUrl.textContent = link;
@@ -204,9 +202,11 @@
204
  };
205
  });
206
 
207
- // Prefill
208
  const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
209
  syncRoomUI(); els.name.focus({preventScroll:true});
 
 
210
  })();
211
  </script>
212
  </body>
 
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)}
 
26
  button{padding:12px 16px;border:0;border-radius:10px;font-weight:700;cursor:pointer}
27
  .btn-red{background:var(--danger);color:var(--accent)}
28
  .btn-ghost{background:#eef2ff;color:#1e3a8a}
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>
 
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">
75
  <button id="joinBtn" class="btn-red">Vào Chat</button>
76
  </div>
 
83
  <div id="linkArea" class="links" aria-live="polite">
84
  <span>🔗 Link phòng:</span>
85
  <span id="roomUrl" class="link-badge mono"></span>
86
+ <button id="copyBtn" class="btn-secondary">📋 Copy</button>
87
  </div>
88
  </section>
89
 
 
92
  <span class="me-pill">👤 <span id="meName">Guest</span></span>
93
  <span class="room-pill">Phòng: <span id="roomPill" class="mono"></span></span>
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>
112
 
 
115
 
116
  <script>
117
  (() => {
118
+ // ===== State =====
119
  const qs = new URLSearchParams(location.search);
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"),
 
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();
186
+ history.replaceState(null,"",`?room=${room}`);
187
 
188
  const link = `${base}?room=${room}`;
189
  els.roomUrl.textContent = link;
 
202
  };
203
  });
204
 
205
+ // ===== Prefill + auto-enter nếu đã có 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>
212
  </body>