-
-
Notifications
You must be signed in to change notification settings - Fork 86
Open
Description
<!doctype html>
<title>Private Links — Personal Vault</title> <style> :root{ --bg:#0f1724; --card:#111827; --accent:#60a5fa; --muted:#94a3b8; --glass: rgba(255,255,255,0.03); font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; } html,body{height:100%; margin:0; background:linear-gradient(180deg,#071126 0%,#081028 100%); color:#e6eef8} .wrap{min-height:100%; display:flex; align-items:center; justify-content:center; padding:36px;} .card{ width:100%; max-width:880px; background:var(--card); border-radius:14px; padding:28px; box-shadow: 0 10px 30px rgba(2,6,23,0.6); border:1px solid rgba(255,255,255,0.03); } header{display:flex; gap:12px; align-items:center;} .logo{width:56px;height:56px;border-radius:10px;background:linear-gradient(135deg,var(--accent),#7dd3fc);display:grid;place-items:center;font-weight:700;color:#05264a} h1{margin:0;font-size:20px} p.lead{margin:8px 0 18px;color:var(--muted)} .controls{display:flex;gap:12px;flex-wrap:wrap} input[type="password"]{ padding:10px 12px;border-radius:8px;border:none;background:var(--glass); color:inherit; outline:none; min-width:220px; font-size:15px; } button{ background:linear-gradient(90deg,var(--accent),#7dd3fc); border:none; color:#042f51; padding:10px 14px; border-radius:10px; cursor:pointer; font-weight:600; } button.ghost{background:transparent;border:1px solid rgba(255,255,255,0.06); color:var(--muted)} .links{margin-top:20px; display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:12px} .link{ background:linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01)); padding:12px; border-radius:10px; display:flex; gap:10px; align-items:center; border:1px solid rgba(255,255,255,0.02); } .icon{width:42px;height:42;border-radius:8px; display:grid; place-items:center; font-weight:700; font-size:18px; color:#042f51; background:#bde0ff} .link .meta{flex:1} .meta b{display:block} .meta small{color:var(--muted)} .actions{display:flex; gap:8px; align-items:center} .actions button{padding:8px 10px; font-size:13px} .hint{margin-top:12px; color:var(--muted); font-size:13px} .stealth{margin-left:auto; display:flex; align-items:center; gap:8px} .stealth input{accent-color:var(--accent)} .fake-content{display:none; color:var(--muted); padding:18px; background:rgba(255,255,255,0.02); border-radius:8px; margin-top:14px} .revealed .fake-content{display:none} .locked .fake-content{display:block} .hidden{display:none !important} footer{margin-top:18px; color:var(--muted); font-size:13px} .small{font-size:12px; color:var(--muted)} @media(max-width:520px){ .controls{flex-direction:column} input[type="password"]{width:100%} } </style>PV
Private Links Vault
Keep your social accounts hidden. Enter the passphrase to reveal them.
<div class="stealth" title="Stealth mode shows harmless content when locked">
<label class="small">Stealth mode</label>
<input id="stealthToggle" type="checkbox" />
</div>
</header>
<div style="margin-top:16px" class="controls">
<input id="pw" type="password" placeholder="Enter passphrase" aria-label="passphrase" />
<button id="unlock">Unlock</button>
<button id="copyAll" class="ghost" title="Copy all visible links">Copy links</button>
<button id="lockBtn" class="ghost hidden">Lock</button>
</div>
<div class="hint" id="hint">Don’t share the passphrase. Links are obfuscated client-side.</div>
<div class="fake-content" id="fake">
<strong>Grocery list</strong>
<ul>
<li>Milk</li>
<li>Eggs</li>
<li>Bread</li>
<li>Coffee</li>
</ul>
</div>
<div class="links revealed hidden" id="vault">
<!-- link cards inserted by JS -->
</div>
<footer>
<div class="small">Built: client-side protection only. For stronger privacy use server-side auth.</div>
</footer>
</div>
${L.icon}
${L.label}
${L.desc}
Open
Copy
`;
vault.appendChild(node);
});
// Attach events
vault.querySelectorAll(".open").forEach(b=>{
b.addEventListener("click", ()=> {
const idx = +b.dataset.i;
const url = atob(LINKS[idx].url_b64);
window.open(url, "_blank");
});
});
vault.querySelectorAll(".copy").forEach(b=>{
b.addEventListener("click", async ()=> {
const idx = +b.dataset.i;
const url = atob(LINKS[idx].url_b64);
await navigator.clipboard.writeText(url);
b.textContent = "Copied!";
setTimeout(()=> b.textContent = "Copy", 1200);
});
});
}
function unlockVault() {
if (pw.value === PASSWORD) {
app.classList.remove("locked");
app.classList.add("revealed");
vault.classList.remove("hidden");
lockBtn.classList.remove("hidden");
unlock.classList.add("hidden");
pw.classList.add("hidden");
renderLinks();
hint.textContent = "Links revealed. Close the tab when done.";
} else {
hint.textContent = "Wrong passphrase — try again.";
pw.classList.add("shake");
setTimeout(()=> pw.classList.remove("shake"), 400);
}
}
function lockVault() {
app.classList.remove("revealed");
app.classList.add("locked");
vault.classList.add("hidden");
lockBtn.classList.add("hidden");
unlock.classList.remove("hidden");
pw.classList.remove("hidden");
pw.value = "";
hint.textContent = "Locked.";
}
unlock.addEventListener("click", unlockVault);
pw.addEventListener("keydown", (e)=> { if (e.key === "Enter") unlockVault(); });
lockBtn.addEventListener("click", lockVault);
copyAll.addEventListener("click", async ()=>{
const urls = LINKS.map(L => atob(L.url_b64)).join("\n");
await navigator.clipboard.writeText(urls);
copyAll.textContent = "Copied!";
setTimeout(()=> copyAll.textContent = "Copy links", 1200);
});
stealthToggle.addEventListener("change", ()=> {
if (stealthToggle.checked) {
// Show fake content when locked
if (app.classList.contains("revealed")) fake.style.display = "none";
else fake.style.display = "block";
} else {
fake.style.display = "none";
}
});
// start with stealth off
fake.style.display = "none";
// Prevent basic scraping by not exposing plain urls as variables
// All links are stored base64 encoded in LINKS[].url_b64
</script>
Metadata
Metadata
Assignees
Labels
No labels