File size: 8,572 Bytes
a148270 9215025 6bb9ef5 10fcbe3 b4ebf19 6bb9ef5 10fcbe3 6bb9ef5 10fcbe3 69abab1 10fcbe3 69abab1 fbadd81 10fcbe3 69abab1 fbadd81 10fcbe3 58b0a9a 10fcbe3 b4ebf19 10fcbe3 b4ebf19 fbadd81 17b561f 69abab1 17b561f 96c5562 69abab1 17b561f 58b0a9a 69abab1 96c5562 69abab1 10fcbe3 9215025 a148270 f89aa8c 7153f47 f89aa8c 10fcbe3 69abab1 17b561f 9215025 1fef416 f89aa8c 69abab1 9215025 f89aa8c 69abab1 1fef416 7153f47 6bb9ef5 17b561f f89aa8c 7153f47 f89aa8c 9215025 6bb9ef5 17b561f 10fcbe3 b4ebf19 17b561f 96c5562 17b561f f89aa8c 7153f47 f89aa8c 9215025 f89aa8c 9215025 10fcbe3 6bb9ef5 96c5562 9215025 10fcbe3 1fef416 10fcbe3 a48e66f 17b561f 7c50ff6 17b561f 10fcbe3 96c5562 7c50ff6 10fcbe3 1fef416 f89aa8c 1fef416 10fcbe3 1fef416 10fcbe3 6bb9ef5 9215025 10fcbe3 6bb9ef5 10fcbe3 fbadd81 6bb9ef5 1fef416 9215025 300677f 9215025 10fcbe3 8348c3d 9215025 1fef416 10fcbe3 6bb9ef5 7c50ff6 17b561f 9215025 f89aa8c a148270 d70ef42 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Chat CVNSS4.0</title>
<style>
:root{
/* Light theme */
--bg:#f7f9fc; --card:#ffffff; --text:#1f2937; --muted:#6b7280;
--primary:#1a73e8; --danger:#d93025; --accent:#ffeb3b;
/* CẮT phần đầu tlk.io (icon mây + thanh xanh) — tăng/giảm nếu cần */
--cut-top: 220px;
/* Che cột phải */
--mask-right: clamp(220px, 24vw, 300px);
}
*{box-sizing:border-box}
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}
header{padding:14px 16px;text-align:center;font-weight:700;background:#fff;border-bottom:1px solid #e5e7eb}
.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}
h2{margin:0 0 6px} .muted{color:var(--muted)}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
input[type=text]{flex:1 1 260px;padding:12px 14px;font-size:16px;border:1px solid #d1d5db;border-radius:10px}
input[type=text]:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px rgba(26,115,232,.15)}
button{padding:12px 16px;border:0;border-radius:10px;font-weight:700;cursor:pointer}
.btn-red{background:#d93025;color:#ffeb3b}
.btn-ghost{background:#eef2ff;color:#1e3a8a}
.btn-secondary{background:#374151;color:#fff}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
/* Status gọn */
.status{display:none;align-items:center;gap:10px;padding:10px 12px;border:1px dashed #d1d5db;border-radius:10px;margin-bottom:10px;background:#f9fafb}
.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}
.room-pill{display:inline-block;background:#e8f0fe;color:#174ea6;padding:6px 10px;border-radius:999px}
/* Khung chat: CẮT phần đầu an toàn (không chặn click/nhập) */
.chat-wrap{
position:relative; border:1px solid #e5e7eb; border-radius:10px; overflow:hidden; background:#fff;
padding-top: var(--cut-top); /* chừa chỗ cho phần bị đẩy ra */
}
#chatFrame{
display:block; width:100%; height:72vh; min-height:640px; border:0; background:#fff;
transform: translateY(calc(-1 * var(--cut-top))); /* đẩy phần đầu ra ngoài khung nhìn */
}
/* Che cột phải – không chặn click (pointer-events:none) */
.mask-right{
position:absolute; top:0; right:0; bottom:0; width:var(--mask-right);
background:#fff; pointer-events:none; box-shadow:-8px 0 10px -10px rgba(0,0,0,.12); z-index:2;
}
/* Link phòng tự ẩn */
.links{display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
.link-badge{background:#f3f4f6;border:1px solid #e5e7eb;color:#111827;padding:6px 8px;border-radius:8px}
/* Fallback khi iframe bị chặn */
.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}
.error-text{color:#991b1b}
.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}
.toast.show{opacity:1}
footer{margin-top:auto;text-align:center;color:#6b7280;font-size:13px;padding:16px}
@media (max-width:820px){ :root{ --mask-right:0px } }
</style>
</head>
<body>
<header>💬 Chat CVNSS4.0</header>
<section id="login" class="card">
<h2>Tham gia phòng chat</h2>
<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>
<div class="row" style="margin-top:8px">
<input id="nickname" type="text" placeholder="Nhập tên của bạn…" autocomplete="name">
<button id="joinBtn" class="btn-red">Vào Chat</button>
</div>
<div class="row" style="margin-top:10px">
<button id="newRoomBtn" class="btn-ghost">✨ Tạo phòng bí mật</button>
<span id="roomHint" class="muted"></span>
</div>
<div id="linkArea" class="links" aria-live="polite">
<span>🔗 Link phòng:</span>
<span id="roomUrl" class="link-badge mono"></span>
<button id="copyBtn" class="btn-secondary">📋 Copy</button>
</div>
</section>
<section id="chatBox" class="card" style="display:none">
<div class="status" id="statusBar">
<span class="me-pill">👤 <span id="meName">Guest</span></span>
<span class="room-pill">Phòng: <span id="roomPill" class="mono"></span></span>
</div>
<div class="chat-wrap">
<div class="mask-right" aria-hidden="true"></div>
<iframe id="chatFrame" title="tlk.io chat" referrerpolicy="no-referrer"
allow="clipboard-read; clipboard-write"></iframe>
</div>
<div id="errorBox" class="error-box">
<span class="error-text">⚠️ Không tải được khung chat (trình duyệt/ISP chặn iframe).</span>
<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>
</div>
</section>
<div id="toast" class="toast">Đã copy!</div>
<footer>Nguồn: Sản phẩm thử nghiệm của Team CVNSS4.0 • Đóng tab là xóa hết dữ liệu ✅</footer>
<script>
(() => {
const qs = new URLSearchParams(location.search);
let room = (qs.get("room") || "cvnss4-0").trim().toLowerCase();
const base = `${location.origin}${location.pathname}`;
const $ = id => document.getElementById(id);
const els = {
login:$("login"), box:$("chatBox"), frame:$("chatFrame"),
name:$("nickname"), join:$("joinBtn"), newRoom:$("newRoomBtn"),
linkArea:$("linkArea"), roomUrl:$("roomUrl"), copy:$("copyBtn"),
hint:$("roomHint"), me:$("meName"), pill:$("roomPill"),
status:$("statusBar"), toast:$("toast"), err:$("errorBox"), openNew:$("openNew")
};
const toast = m=>{ els.toast.textContent=m; els.toast.classList.add("show"); setTimeout(()=>els.toast.classList.remove("show"),1400); };
const getName = ()=> (els.name.value || localStorage.getItem("cvnss4_name") || "Guest").trim() || "Guest";
const setName = n=>{ els.me.textContent=n; localStorage.setItem("cvnss4_name", n); };
const syncRoomUI = ()=>{ els.pill.textContent=room; els.hint.textContent=`(đang ở phòng ${room})`; };
const tlkUrl = n=> `https://tlk.io/${encodeURIComponent(room)}?nickname=${encodeURIComponent(n)}#embed`;
function mountChat(){
const name = getName(); setName(name); syncRoomUI();
els.status.style.display = "flex";
const url = tlkUrl(name);
els.frame.src = url; els.openNew.href = url; els.err.style.display="none";
const timer = setTimeout(()=>{ els.err.style.display="flex"; }, 6000);
els.frame.addEventListener("load", ()=> clearTimeout(timer), {once:true});
}
function enterChat(){
els.login.style.display="none";
els.box.style.display="block";
mountChat();
}
els.join.addEventListener("click", enterChat);
els.name.addEventListener("keydown", e=>{ if(e.key==="Enter") enterChat(); });
// Tạo phòng bí mật + copy tự ẩn
els.newRoom.addEventListener("click", async ()=>{
room = `cvnss4-0-${Math.random().toString(36).slice(2,8)}`;
syncRoomUI(); history.replaceState(null,"",`?room=${room}`);
const link = `${base}?room=${room}`;
els.roomUrl.textContent = link; els.linkArea.style.display="flex";
try{ await navigator.clipboard?.writeText?.(link); toast("✅ Link đã copy!"); }catch(_){}
setTimeout(()=>{ if(els.linkArea.style.display!=="none") els.linkArea.style.display="none"; }, 9000);
els.copy.onclick = async ()=>{
try{
await navigator.clipboard?.writeText?.(link);
if(navigator.share){ try{ await navigator.share({title:"Phòng chat CVNSS4.0", url:link}); }catch(_){} }
toast("✅ Đã copy link phòng!");
}catch(_){ toast("⚠️ Không copy được—hãy copy thủ công"); }
els.linkArea.style.display="none";
};
});
// Prefill + auto-enter khi có tên
const last = localStorage.getItem("cvnss4_name"); if(last) els.name.value = last;
syncRoomUI(); els.name.focus({preventScroll:true});
if (qs.get("room") && (last || els.name.value.trim() !== "")) enterChat();
})();
</script>
</body>
</html>
|