CVNSS commited on
Commit
6bb9ef5
·
verified ·
1 Parent(s): 9215025

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +98 -118
index.html CHANGED
@@ -5,48 +5,68 @@
5
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
- :root{ --bg:#f6f7fb; --text:#202124; --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b; }
9
- *{box-sizing:border-box} body{margin:0;font-family:"Google Sans",Arial,sans-serif;background:var(--bg);color:var(--text);display:flex;flex-direction:column;min-height:100vh}
10
- header{background:var(--primary);color:#fff;padding:14px 16px;text-align:center;font-weight:700;box-shadow:0 2px 6px rgba(0,0,0,.08)}
11
- .card{width:min(960px,92vw);margin:22px auto;background:#fff;border-radius:12px;box-shadow:0 6px 18px rgba(0,0,0,.08);padding:20px}
12
- h2{margin:0 0 8px;font-weight:800}
13
- .muted{color:#5f6368}
 
 
 
 
 
 
 
 
 
 
14
  .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
15
- input[type=text]{flex:1 1 260px;padding:12px 14px;font-size:16px;border:1px solid #dadce0;border-radius:8px}
16
- input[type=text]:focus{outline:none;border-color:var(--primary)}
17
- button{padding:12px 18px;border:0;border-radius:8px;font-weight:800;cursor:pointer}
 
18
  .btn-red{background:var(--danger);color:var(--accent)}
19
- .btn-ghost{background:#e9eef6;color:#174ea6}
20
- .btn-secondary{background:#5f6368;color:#fff}
21
- .tag-danger{display:inline-block;background:var(--danger);color:var(--accent);font-weight:900;padding:4px 10px;border-radius:999px;font-size:12px;letter-spacing:.3px}
22
- .links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
23
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
24
- .status{display:none;align-items:center;gap:8px;padding:10px 12px;border:1px dashed #e0e3e7;border-radius:10px;margin-bottom:10px;background:#fbfcff}
25
- .pill{display:inline-block;background:#eef3fd;color:#174ea6;padding:6px 10px;border-radius:999px;font-size:13px}
26
- .chat-wrap{position:relative;border:1px solid #e3e6ea;border-radius:10px;overflow:hidden}
27
- /* khung mount cho chat (embed/iframe) */
28
- #chatMount{min-height:560px}
29
- iframe{width:100%;height:72vh;min-height:560px;border:0;background:#fff}
30
- .toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:#323232;color:#fff;padding:10px 14px;border-radius:8px;font-size:14px;opacity:0;pointer-events:none;transition:opacity .2s}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  .toast.show{opacity:1}
32
- footer{margin-top:auto;text-align:center;color:#6c727a;font-size:13px;padding:16px}
33
  ol.guide{margin:0;padding-left:20px} ol.guide li{margin:6px 0}
 
34
  </style>
35
  </head>
36
  <body>
37
  <header>💬 Chat CVNSS4.0</header>
38
 
 
39
  <section id="login" class="card">
40
  <h2>Tham gia phòng chat</h2>
41
  <p class="muted">Luồng 3 bước siêu gọn: 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>
42
  <ol class="guide">
43
- <li><b>Nhập tên</b> của bạn ở ô dưới đây (có thể đổi sau).</li>
44
  <li>Nhấn <b>Vào Chat</b> (nút đỏ). <span class="muted">Form Join gốc của tlk.io sẽ bị ẩn hoàn toàn.</span></li>
45
- <li>
46
- <b>✨ Tạo phòng bí mật</b> nếu cần (ID ngẫu nhiên + Copy link tự ẩn).
47
- <div style="margin-top:6px"><span class="muted">Nếu bạn không cần “✨ Tạo phòng bí mật”, hãy <u>bỏ qua bước này</u>.</span></div>
48
- <div style="margin-top:8px"><span class="tag-danger">Vào Chat</span></div>
49
- </li>
50
  </ol>
51
 
52
  <div class="row" style="margin-top:12px">
@@ -61,19 +81,25 @@
61
 
62
  <div id="linkArea" class="links" aria-live="polite">
63
  <span>🔗 Link phòng:</span>
64
- <span id="roomUrl" class="mono"></span>
65
- <button id="copyBtn" class="btn-secondary">📋 Copy</button>
66
  </div>
67
  </section>
68
 
 
69
  <section id="chatBox" class="card" style="display:none">
70
  <div class="status" id="statusBar">
71
- <span class="pill">👤 Bạn: <b id="meName">Guest</b></span>
72
- <span class="pill">Phòng: <b id="roomPill"></b></span>
73
- <button id="renameBtn" class="btn-ghost" title="Đổi tên">Đổi tên</button>
74
- <button id="fallbackBtn" class="btn-secondary" title="Nếu không thấy chat">Dùng chế độ iframe</button>
 
 
 
 
 
 
75
  </div>
76
- <div class="chat-wrap"><div id="chatMount"></div></div>
77
  </section>
78
 
79
  <div id="toast" class="toast">Đã copy!</div>
@@ -81,104 +107,69 @@
81
 
82
  <script>
83
  (() => {
84
- // --------- State & helpers ----------
85
- const qs = new URLSearchParams(location.search);
86
- let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
87
- let mode = "embed"; // "embed" (mặc định) -> gọn, không Join; "iframe" là phương án dự phòng
 
88
  const els = {
89
- login: document.getElementById("login"),
90
- chatBox: document.getElementById("chatBox"),
91
- chatMount: document.getElementById("chatMount"),
92
- name: document.getElementById("nickname"),
93
- join: document.getElementById("joinBtn"),
94
- newRoom: document.getElementById("newRoomBtn"),
95
- roomUrl: document.getElementById("roomUrl"),
96
- linkArea: document.getElementById("linkArea"),
97
- copy: document.getElementById("copyBtn"),
98
- toast: document.getElementById("toast"),
99
- roomHint: document.getElementById("roomHint"),
100
- status: document.getElementById("statusBar"),
101
- meName: document.getElementById("meName"),
102
- roomPill: document.getElementById("roomPill"),
103
- rename: document.getElementById("renameBtn"),
104
- fallback: document.getElementById("fallbackBtn"),
105
  };
106
 
107
- const baseUrl = `${location.origin}${location.pathname}`;
108
-
109
  function toast(msg){ els.toast.textContent = msg; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"), 1400); }
110
- function syncRoomUI(){ els.roomHint.textContent = `(đang ở phòng ${room})`; els.roomPill.textContent = room; }
111
  function getName(){ return (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest"; }
112
  function setName(n){ els.meName.textContent = n; localStorage.setItem("cvnss4_name", n); }
 
113
 
114
- // --------- Chat renderers ----------
115
- function clearMount(){ els.chatMount.replaceChildren(); }
116
-
117
- // Prefer embed.js (gọn, không có Join/header)
118
- function renderEmbed(name){
119
- clearMount();
120
- const holder = document.createElement("div");
121
- holder.id = "tlkio";
122
- holder.dataset.channel = room;
123
- holder.dataset.nickname = name;
124
- holder.dataset.theme = "theme--day";
125
- holder.style.minHeight = "560px";
126
- els.chatMount.appendChild(holder);
127
-
128
- // load/reload embed.js mỗi lần mount (an toàn cho nhiều host)
129
- const s = document.createElement("script");
130
- s.async = true; s.src = "https://tlk.io/embed.js";
131
- s.onerror = () => { mode = "iframe"; renderIframe(name); }; // nếu host chặn script → fallback
132
- els.chatMount.appendChild(s);
133
- }
134
-
135
- // Fallback: iframe (đi thẳng vào room bằng ?nickname=), không overlay rối rắm
136
- function renderIframe(name){
137
- clearMount();
138
- const url = `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(name)}#embed`;
139
- const ifr = document.createElement("iframe");
140
- ifr.title = "tlk.io chat";
141
- ifr.src = url;
142
- ifr.referrerPolicy = "no-referrer";
143
- els.chatMount.appendChild(ifr);
144
- }
145
 
146
- function renderChat(){
147
- const name = getName();
148
- setName(name);
 
149
  syncRoomUI();
150
- els.status.style.display = "flex";
151
- if(mode === "embed") renderEmbed(name); else renderIframe(name);
152
- // cuộn tới chat
153
- setTimeout(()=> els.chatBox.scrollIntoView({behavior:"smooth", block:"start"}), 50);
154
- }
155
 
156
- // --------- Actions ----------
157
- function enterChat(){
158
  els.login.style.display = "none";
159
  els.chatBox.style.display = "block";
160
- renderChat();
 
 
 
 
 
 
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
167
- els.newRoom.addEventListener("click", async () => {
168
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
169
  syncRoomUI();
170
  history.replaceState(null, "", `?room=${room}`);
171
 
172
- const link = `${baseUrl}?room=${room}`;
173
  els.roomUrl.textContent = link;
174
  els.linkArea.style.display = "flex";
175
 
176
- // auto copy nếu được cấp quyền
177
  try{ await navigator.clipboard?.writeText?.(link); toast("✅ Link đã copy!"); }catch(_){}
178
- // ẩn tự động sau 10s để gọn
179
  setTimeout(()=>{ if(els.linkArea.style.display!=="none") els.linkArea.style.display="none"; }, 10000);
180
 
181
- els.copy.onclick = async () => {
182
  try{
183
  await navigator.clipboard?.writeText?.(link);
184
  if(navigator.share){ try{ await navigator.share({title:"Phòng chat CVNSS4.0", url:link}); }catch(_){} }
@@ -188,19 +179,8 @@
188
  };
189
  });
190
 
191
- // Đổi tên nhanh không cần reload
192
- els.rename.addEventListener("click", () => {
193
- const cur = getName();
194
- const n = prompt("Nhập tên mới:", cur) || cur;
195
- setName(n);
196
- renderChat();
197
- });
198
-
199
- // Buộc chuyển sang iframe nếu embed không hiện
200
- els.fallback.addEventListener("click", () => { mode = "iframe"; renderChat(); });
201
-
202
- // Prefill
203
- const lastName = localStorage.getItem("cvnss4_name"); if(lastName) els.name.value = lastName;
204
  syncRoomUI();
205
  els.name.focus({preventScroll:true});
206
  })();
 
5
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
  <title>Chat CVNSS4.0</title>
7
  <style>
8
+ :root{
9
+ --bg:#0b0d10; --card:#101418; --text:#e6edf3; --muted:#98a2ad;
10
+ --primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
11
+ /* Overlay để ẩn header (#channel) & cột phải của tlk.io */
12
+ --ov-top: 220px; /* nếu header/join còn hở, tăng 240–280px */
13
+ --ov-right: 280px; /* ẩn panel phải: 240–320px tùy màn hình */
14
+ }
15
+ *{box-sizing:border-box}
16
+ body{margin:0;font-family:"Google Sans",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:800;background:#0f172a;color:#e2e8f0;
19
+ border-bottom:1px solid #1f2937}
20
+ .card{width:min(980px,94vw);margin:22px auto;background:var(--card);border:1px solid #1f2937;
21
+ border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,.35);padding:18px}
22
+ h2{margin:0 0 6px;font-weight:900}
23
+ .muted{color:var(--muted)}
24
  .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
25
+ input[type=text]{flex:1 1 260px;padding:12px 14px;font-size:16px;border:1px solid #263042;background:#0b1220;
26
+ color:var(--text);border-radius:10px}
27
+ input[type=text]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.25)}
28
+ button{padding:12px 18px;border:0;border-radius:10px;font-weight:900;cursor:pointer;letter-spacing:.2px}
29
  .btn-red{background:var(--danger);color:var(--accent)}
30
+ .btn-ghost{background:#0f172a;color:#93c5fd;border:1px dashed #1f2937}
 
 
 
31
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
32
+ /* Thanh trạng thái: CHỈ hiện tên + phòng, KHÔNG có nút “Đổi tên / iframe” */
33
+ .status{display:none;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #223047;border-radius:12px;margin-bottom:10px;background:#0b1220}
34
+ .me-pill{display:inline-flex;align-items:center;gap:6px;background:#0a0a0a;border:1px solid #2a1d00;
35
+ color:#ffd166;padding:6px 12px;border-radius:999px;font-weight:900}
36
+ .room-pill{display:inline-block;background:#0a1628;color:#8ab4ff;padding:6px 10px;border-radius:999px}
37
+ /* Khung chat nổi bật + overlay giấu nội dung thừa */
38
+ .chat-wrap{position:relative;border:1px solid #243145;border-radius:12px;overflow:hidden;
39
+ box-shadow:inset 0 0 0 1px #1f2a3a, 0 12px 30px rgba(0,0,0,.35)}
40
+ iframe{width:100%;height:72vh;min-height:560px;border:0;background:#0b1220}
41
+ .ov-top{position:absolute;left:0;right:0;top:0;height:var(--ov-top);background:var(--card);
42
+ z-index:3;pointer-events:none;box-shadow:0 10px 12px -10px rgba(0,0,0,.6)}
43
+ .ov-right{position:absolute;top:0;right:0;bottom:0;width:var(--ov-right);background:var(--card);
44
+ z-index:2;pointer-events:none;box-shadow:-10px 0 12px -10px rgba(0,0,0,.6)}
45
+ /* Chỉ highlight đáy (khu textarea message-input) — không che click */
46
+ .focus-bottom{position:absolute;left:0;right:var(--ov-right);bottom:0;height:120px;pointer-events:none;z-index:1;
47
+ background:radial-gradient(120% 120% at 50% 100%, rgba(255,235,59,.18) 0%, rgba(255,235,59,.10) 35%, rgba(0,0,0,0) 70%)}
48
+ /* Khối link tự ẩn sau copy */
49
+ .links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
50
+ .link-badge{background:#0b1220;border:1px solid #223047;color:#b6c2d2;padding:6px 8px;border-radius:8px}
51
+ .toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:#111827;color:#e5e7eb;
52
+ border:1px solid #374151;padding:10px 14px;border-radius:10px;font-size:14px;opacity:0;pointer-events:none;transition:opacity .2s}
53
  .toast.show{opacity:1}
54
+ footer{margin-top:auto;text-align:center;color:#8b9bb1;font-size:13px;padding:16px}
55
  ol.guide{margin:0;padding-left:20px} ol.guide li{margin:6px 0}
56
+ @media (max-width:820px){ :root{ --ov-right:0px } } /* mobile: tlk.io hầu như không có panel phải */
57
  </style>
58
  </head>
59
  <body>
60
  <header>💬 Chat CVNSS4.0</header>
61
 
62
+ <!-- Bước 1-2-3 -->
63
  <section id="login" class="card">
64
  <h2>Tham gia phòng chat</h2>
65
  <p class="muted">Luồng 3 bước siêu gọn: 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>
66
  <ol class="guide">
67
+ <li><b>Nhập tên</b> của bạn ở ô dưới đây (có thể sửa lại sau).</li>
68
  <li>Nhấn <b>Vào Chat</b> (nút đỏ). <span class="muted">Form Join gốc của tlk.io sẽ bị ẩn hoàn toàn.</span></li>
69
+ <li><b>✨ Tạo phòng bí mật</b> (ID ngẫu nhiên + Copy link tự ẩn). <span class="muted">Nếu bạn không cần “✨ Tạo phòng bí mật”, hãy <u>bỏ qua bước này</u>.</span></li>
 
 
 
 
70
  </ol>
71
 
72
  <div class="row" style="margin-top:12px">
 
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>
85
+ <button id="copyBtn" class="btn-ghost">📋 Copy</button>
86
  </div>
87
  </section>
88
 
89
+ <!-- Khối chat (không có “Đổi tên / Iframe”) -->
90
  <section id="chatBox" class="card" style="display:none">
91
  <div class="status" id="statusBar">
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
+ <div class="chat-wrap">
96
+ <!-- Ẩn header `#channel` + Join + cột phải -->
97
+ <div class="ov-top"></div>
98
+ <div class="ov-right"></div>
99
+ <!-- chỉ highlight đáy để người dùng thấy khu nhập tin nhắn -->
100
+ <div class="focus-bottom"></div>
101
+ <iframe id="chatFrame" title="tlk.io chat"></iframe>
102
  </div>
 
103
  </section>
104
 
105
  <div id="toast" class="toast">Đã copy!</div>
 
107
 
108
  <script>
109
  (() => {
110
+ // ======== State & helpers ========
111
+ const qs = new URLSearchParams(location.search);
112
+ let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
113
+ const base = `${location.origin}${location.pathname}`;
114
+
115
  const els = {
116
+ login: document.getElementById("login"),
117
+ chatBox: document.getElementById("chatBox"),
118
+ frame: document.getElementById("chatFrame"),
119
+ name: document.getElementById("nickname"),
120
+ join: document.getElementById("joinBtn"),
121
+ newRoom: document.getElementById("newRoomBtn"),
122
+ linkArea: document.getElementById("linkArea"),
123
+ roomUrl: document.getElementById("roomUrl"),
124
+ copy: document.getElementById("copyBtn"),
125
+ hint: document.getElementById("roomHint"),
126
+ meName: document.getElementById("meName"),
127
+ roomPill: document.getElementById("roomPill"),
128
+ status: document.getElementById("statusBar"),
129
+ toast: document.getElementById("toast"),
 
 
130
  };
131
 
 
 
132
  function toast(msg){ els.toast.textContent = msg; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"), 1400); }
 
133
  function getName(){ return (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest"; }
134
  function setName(n){ els.meName.textContent = n; localStorage.setItem("cvnss4_name", n); }
135
+ function syncRoomUI(){ els.roomPill.textContent = room; els.hint.textContent = `(đang ở phòng ${room})`; }
136
 
137
+ function tlkUrl(name){ return `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(name)}#embed`; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ // ======== Actions ========
140
+ function enterChat(){
141
+ const n = getName();
142
+ setName(n);
143
  syncRoomUI();
 
 
 
 
 
144
 
 
 
145
  els.login.style.display = "none";
146
  els.chatBox.style.display = "block";
147
+ els.status.style.display = "flex";
148
+
149
+ // Iframe đi thẳng vào room với nickname => không bao giờ thấy Join
150
+ els.frame.src = tlkUrl(n);
151
+
152
+ // Cuộn tới chat
153
+ setTimeout(()=> els.chatBox.scrollIntoView({behavior:"smooth"}), 40);
154
  }
155
 
156
  els.join.addEventListener("click", enterChat);
157
  els.name.addEventListener("keydown", e => { if(e.key === "Enter") enterChat(); });
158
 
159
+ // Tạo phòng bí mật (ID random) + Copy + tự ẩn
160
+ els.newRoom.addEventListener("click", async ()=>{
161
  room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
162
  syncRoomUI();
163
  history.replaceState(null, "", `?room=${room}`);
164
 
165
+ const link = `${base}?room=${room}`;
166
  els.roomUrl.textContent = link;
167
  els.linkArea.style.display = "flex";
168
 
 
169
  try{ await navigator.clipboard?.writeText?.(link); toast("✅ Link đã copy!"); }catch(_){}
 
170
  setTimeout(()=>{ if(els.linkArea.style.display!=="none") els.linkArea.style.display="none"; }, 10000);
171
 
172
+ els.copy.onclick = async ()=>{
173
  try{
174
  await navigator.clipboard?.writeText?.(link);
175
  if(navigator.share){ try{ await navigator.share({title:"Phòng chat CVNSS4.0", url:link}); }catch(_){} }
 
179
  };
180
  });
181
 
182
+ // Prefill tên nếu
183
+ const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
 
 
 
 
 
 
 
 
 
 
 
184
  syncRoomUI();
185
  els.name.focus({preventScroll:true});
186
  })();