add clear for search + regroup options in settings btn
This commit is contained in:
+170
-430
@@ -1,477 +1,217 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg: #0a0e17;
|
--bg: #0a0e17; --bg2: #131a24; --text: #e1e8f0; --text2: #8899aa;
|
||||||
--bg2: #131a24;
|
--accent: #3b82f6; --danger: #ef4444; --success: #22c55e; --warning: #f59e0b;
|
||||||
--text: #e1e8f0;
|
--border: rgba(255,255,255,0.06); --card: rgba(18,25,35,0.85);
|
||||||
--text2: #8899aa;
|
--input: #10161f; --vault-bg: rgba(255,255,255,0.03);
|
||||||
--accent: #3b82f6;
|
|
||||||
--danger: #ef4444;
|
|
||||||
--success: #22c55e;
|
|
||||||
--warning: #f59e0b;
|
|
||||||
--border: rgba(255,255,255,0.06);
|
|
||||||
--card: rgba(18,25,35,0.85);
|
|
||||||
--input: #10161f;
|
|
||||||
--vault-bg: rgba(255,255,255,0.03);
|
|
||||||
}
|
}
|
||||||
.light {
|
.light {
|
||||||
--bg: #f4f6f9;
|
--bg: #f4f6f9; --bg2: #e9ecf1; --text: #1a1f2e; --text2: #5a6475;
|
||||||
--bg2: #e9ecf1;
|
--card: rgba(255,255,255,0.9); --input: #ffffff;
|
||||||
--text: #1a1f2e;
|
--border: rgba(0,0,0,0.08); --vault-bg: rgba(255,255,255,0.5);
|
||||||
--text2: #5a6475;
|
|
||||||
--card: rgba(255,255,255,0.9);
|
|
||||||
--input: #ffffff;
|
|
||||||
--border: rgba(0,0,0,0.08);
|
|
||||||
--vault-bg: rgba(255,255,255,0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* { margin:0; padding:0; box-sizing:border-box; }
|
* { margin:0; padding:0; box-sizing:border-box; }
|
||||||
body {
|
body {
|
||||||
background: linear-gradient(145deg, var(--bg) 0%, var(--bg2) 100%);
|
background: linear-gradient(145deg, var(--bg) 0%, var(--bg2) 100%);
|
||||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||||
min-height: 100vh;
|
min-height: 100vh; display: flex; justify-content: center;
|
||||||
display: flex;
|
align-items: flex-start; padding: 1.2rem; color: var(--text);
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1.2rem;
|
|
||||||
color: var(--text);
|
|
||||||
transition: background 0.3s, color 0.3s;
|
transition: background 0.3s, color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-container {
|
.toast-container { position:fixed; top:1rem; right:1rem; z-index:9999; display:flex; flex-direction:column; gap:0.5rem; }
|
||||||
position: fixed; top: 1rem; right: 1rem; z-index: 9999;
|
.toast { padding:0.7rem 1.2rem; border-radius:0.8rem; font-size:0.85rem; animation:slideIn 0.3s ease; color:#fff; }
|
||||||
display: flex; flex-direction: column; gap: 0.5rem;
|
.toast.error { background:var(--danger); }
|
||||||
}
|
.toast.success { background:var(--success); }
|
||||||
.toast {
|
@keyframes slideIn { from { transform:translateX(100%); opacity:0; } to { transform:translateX(0); opacity:1; } }
|
||||||
padding: 0.7rem 1.2rem; border-radius: 0.8rem;
|
|
||||||
font-size: 0.85rem; animation: slideIn 0.3s ease; color: #fff;
|
|
||||||
}
|
|
||||||
.toast.error { background: var(--danger); }
|
|
||||||
.toast.success { background: var(--success); }
|
|
||||||
@keyframes slideIn {
|
|
||||||
from { transform: translateX(100%); opacity: 0; }
|
|
||||||
to { transform: translateX(0); opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.vault {
|
.vault {
|
||||||
width: 96%; max-width: 1700px;
|
width:96%; max-width:1700px; background:var(--vault-bg);
|
||||||
background: var(--vault-bg);
|
backdrop-filter:blur(20px); border:1px solid var(--border);
|
||||||
backdrop-filter: blur(20px);
|
border-radius:2rem; padding:1.8rem; margin:0.5rem auto;
|
||||||
border: 1px solid var(--border);
|
box-shadow:0 30px 50px rgba(0,0,0,0.4);
|
||||||
border-radius: 2rem; padding: 1.8rem; margin: 0.5rem auto;
|
|
||||||
box-shadow: 0 30px 50px rgba(0,0,0,0.4);
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 2rem; margin-bottom: 0.6rem;
|
|
||||||
display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
h1 span {
|
|
||||||
background: var(--accent); padding: 0.2rem 0.7rem;
|
|
||||||
border-radius: 3rem; font-size: 0.8rem; color: #fff;
|
|
||||||
}
|
}
|
||||||
|
h1 { font-size:2rem; margin-bottom:0.6rem; display:flex; align-items:center; gap:0.6rem; flex-wrap:wrap; }
|
||||||
|
h1 span { background:var(--accent); padding:0.2rem 0.7rem; border-radius:3rem; font-size:0.8rem; color:#fff; }
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
.auth-section {
|
.auth-section { background:rgba(0,0,0,0.25); border-radius:1.5rem; padding:1.3rem; margin-bottom:1rem; border:1px solid var(--border); }
|
||||||
background: rgba(0,0,0,0.25); border-radius: 1.5rem;
|
.auth-tabs { display:flex; gap:1rem; margin-bottom:0.8rem; }
|
||||||
padding: 1.3rem; margin-bottom: 1rem; border: 1px solid var(--border);
|
.auth-tab { background:none; border:none; color:var(--text2); padding:0.4rem 0.8rem; cursor:pointer; border-bottom:2px solid transparent; font-size:0.9rem; }
|
||||||
}
|
.auth-tab.active { color:var(--accent); border-bottom-color:var(--accent); }
|
||||||
.auth-tabs { display: flex; gap: 1rem; margin-bottom: 0.8rem; }
|
|
||||||
.auth-tab {
|
|
||||||
background: none; border: none; color: var(--text2);
|
|
||||||
padding: 0.4rem 0.8rem; cursor: pointer;
|
|
||||||
border-bottom: 2px solid transparent; font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
.auth-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
|
|
||||||
|
|
||||||
input, select, textarea {
|
input, select, textarea {
|
||||||
flex: 1; min-width: 130px; background: var(--input);
|
flex:1; min-width:130px; background:var(--input); border:1px solid #2d3748;
|
||||||
border: 1px solid #2d3748; padding: 0.65rem 1rem;
|
padding:0.65rem 1rem; border-radius:2rem; color:var(--text); font-size:0.85rem;
|
||||||
border-radius: 2rem; color: var(--text); font-size: 0.85rem;
|
outline:none; font-family:inherit;
|
||||||
outline: none; font-family: inherit;
|
|
||||||
}
|
}
|
||||||
input:focus, select:focus { border-color: var(--accent); }
|
input:focus, select:focus { border-color:var(--accent); }
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background: linear-gradient(135deg, #2563eb, #1d4ed8);
|
background:linear-gradient(135deg, #2563eb, #1d4ed8); border:none; color:#fff;
|
||||||
border: none; color: #fff; font-weight: 600;
|
font-weight:600; padding:0.65rem 1.3rem; border-radius:2rem; cursor:pointer;
|
||||||
padding: 0.65rem 1.3rem; border-radius: 2rem;
|
font-size:0.85rem; transition:0.2s; white-space:nowrap;
|
||||||
cursor: pointer; font-size: 0.85rem; transition: 0.2s; white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.btn:hover { filter: brightness(1.15); transform: scale(1.02); }
|
.btn:hover { filter:brightness(1.15); transform:scale(1.02); }
|
||||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
.btn:disabled { opacity:0.5; cursor:not-allowed; transform:none; }
|
||||||
.btn-outline { background: transparent; border: 1px solid #475569; color: var(--text2); }
|
.btn-outline { background:transparent; border:1px solid #475569; color:var(--text2); }
|
||||||
.btn-sm { padding: 0.35rem 0.9rem; font-size: 0.75rem; }
|
.btn-sm { padding:0.35rem 0.9rem; font-size:0.75rem; }
|
||||||
.btn-xs { padding: 0.2rem 0.5rem; font-size: 0.7rem; }
|
.btn-xs { padding:0.2rem 0.5rem; font-size:0.7rem; }
|
||||||
.btn-danger { background: var(--danger); }
|
.btn-danger { background:var(--danger); }
|
||||||
|
|
||||||
.input-group { display: flex; gap: 0.5rem; margin: 0.7rem 0; flex-wrap: wrap; align-items: center; }
|
.input-group { display:flex; gap:0.5rem; margin:0.7rem 0; flex-wrap:wrap; align-items:center; }
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 0.7rem;
|
|
||||||
}
|
|
||||||
.toolbar-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.view-toggle { display: flex; gap: 0.2rem; background: rgba(0,0,0,0.3); padding: 0.2rem; border-radius: 2rem; }
|
|
||||||
.view-btn {
|
|
||||||
background: none; border: none; color: var(--text2);
|
|
||||||
padding: 0.35rem 0.7rem; border-radius: 2rem; cursor: pointer;
|
|
||||||
font-size: 0.75rem; transition: 0.2s;
|
|
||||||
}
|
|
||||||
.view-btn.active { background: var(--accent); color: #fff; }
|
|
||||||
.view-btn:hover:not(.active) { background: rgba(255,255,255,0.08); }
|
|
||||||
|
|
||||||
.toggles-row { display: flex; gap: 1.2rem; align-items: center; flex-wrap: wrap; }
|
/* Toolbar */
|
||||||
.toggle-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.75rem; color: var(--text2); }
|
.toolbar { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem; margin-bottom:0.7rem; }
|
||||||
.toggle-switch {
|
.view-toggle { display:flex; gap:0.2rem; background:rgba(0,0,0,0.3); padding:0.2rem; border-radius:2rem; }
|
||||||
position: relative; width: 36px; height: 20px;
|
.view-btn { background:none; border:none; color:var(--text2); padding:0.35rem 0.7rem; border-radius:2rem; cursor:pointer; font-size:0.75rem; transition:0.2s; }
|
||||||
background: #334155; border-radius: 10px; cursor: pointer; transition: 0.2s;
|
.view-btn.active { background:var(--accent); color:#fff; }
|
||||||
}
|
.view-btn:hover:not(.active) { background:rgba(255,255,255,0.08); }
|
||||||
.toggle-switch.active { background: var(--accent); }
|
|
||||||
.toggle-switch::after {
|
|
||||||
content: ''; position: absolute; top: 2px; left: 2px;
|
|
||||||
width: 16px; height: 16px; background: #fff; border-radius: 50%; transition: 0.2s;
|
|
||||||
}
|
|
||||||
.toggle-switch.active::after { left: 18px; }
|
|
||||||
|
|
||||||
.status-badge {
|
.toggles-row { display:flex; gap:1.2rem; align-items:center; flex-wrap:wrap; }
|
||||||
background: var(--bg2); padding: 0.25rem 0.8rem;
|
.toggle-item { display:flex; align-items:center; gap:0.4rem; font-size:0.75rem; color:var(--text2); }
|
||||||
border-radius: 2rem; font-size: 0.75rem; white-space: nowrap;
|
.toggle-switch { position:relative; width:36px; height:20px; background:#334155; border-radius:10px; cursor:pointer; transition:0.2s; }
|
||||||
}
|
.toggle-switch.active { background:var(--accent); }
|
||||||
.hidden { display: none !important; }
|
.toggle-switch::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; background:#fff; border-radius:50%; transition:0.2s; }
|
||||||
|
.toggle-switch.active::after { left:18px; }
|
||||||
|
|
||||||
|
.status-badge { background:var(--bg2); padding:0.25rem 0.8rem; border-radius:2rem; font-size:0.75rem; white-space:nowrap; }
|
||||||
|
.hidden { display:none !important; }
|
||||||
|
|
||||||
/* Strength */
|
/* Strength */
|
||||||
.strength-bar { height: 4px; border-radius: 2px; transition: 0.3s; margin-top: 0.2rem; }
|
.strength-bar { height:4px; border-radius:2px; transition:0.3s; margin-top:0.2rem; }
|
||||||
.s0 { background: var(--danger); width: 20%; }
|
.s0 { background:var(--danger); width:20%; }
|
||||||
.s1 { background: var(--warning); width: 40%; }
|
.s1 { background:var(--warning); width:40%; }
|
||||||
.s2 { background: #eab308; width: 60%; }
|
.s2 { background:#eab308; width:60%; }
|
||||||
.s3 { background: #84cc16; width: 80%; }
|
.s3 { background:#84cc16; width:80%; }
|
||||||
.s4 { background: var(--success); width: 100%; }
|
.s4 { background:var(--success); width:100%; }
|
||||||
|
|
||||||
|
/* ========== SEARCH BOX WITH CLEAR BUTTON (FIXED) ========== */
|
||||||
|
.search-box {
|
||||||
|
position: relative;
|
||||||
|
width: 240px; /* fixed width – adjust if needed */
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.search-box input {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-right: 30px; /* space for the ✕ button */
|
||||||
|
}
|
||||||
|
.clear-search-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 6px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text2);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 6px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Folders */
|
/* Folders */
|
||||||
.folders-bar {
|
.folders-bar { display:flex; gap:0.4rem; margin-bottom:0.8rem; flex-wrap:wrap; align-items:center; padding:0.4rem 0.6rem; background:rgba(0,0,0,0.2); border-radius:1rem; }
|
||||||
display: flex; gap: 0.4rem; margin-bottom: 0.8rem;
|
.folder-chip { background:var(--bg2); border:1px solid var(--border); color:var(--text2); padding:0.3rem 0.8rem; border-radius:2rem; cursor:pointer; font-size:0.78rem; transition:0.2s; white-space:nowrap; }
|
||||||
flex-wrap: wrap; align-items: center;
|
.folder-chip:hover { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||||
padding: 0.4rem 0.6rem; background: rgba(0,0,0,0.2); border-radius: 1rem;
|
.folder-chip.active { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||||||
}
|
.folder-count { background:rgba(0,0,0,0.3); padding:0.1rem 0.4rem; border-radius:1rem; margin-left:0.3rem; font-size:0.7rem; }
|
||||||
.folder-chip {
|
.folder-delete-btn { background:transparent; border:none; color:var(--danger); cursor:pointer; font-size:0.7rem; margin-left:0.2rem; opacity:0.7; }
|
||||||
background: var(--bg2); border: 1px solid var(--border);
|
.folder-delete-btn:hover { opacity:1; }
|
||||||
color: var(--text2); padding: 0.3rem 0.8rem; border-radius: 2rem;
|
.folder-add-btn { background:transparent; border:1px dashed #475569; color:var(--text2); padding:0.3rem 0.6rem; border-radius:2rem; cursor:pointer; font-size:0.75rem; transition:0.2s; }
|
||||||
cursor: pointer; font-size: 0.78rem; transition: 0.2s; white-space: nowrap;
|
.folder-add-btn:hover { border-color:var(--accent); color:var(--accent); }
|
||||||
}
|
|
||||||
.folder-chip:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
||||||
.folder-chip.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
||||||
.folder-count {
|
|
||||||
background: rgba(0,0,0,0.3); padding: 0.1rem 0.4rem;
|
|
||||||
border-radius: 1rem; margin-left: 0.3rem; font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
.folder-delete-btn {
|
|
||||||
background: transparent; border: none; color: var(--danger);
|
|
||||||
cursor: pointer; font-size: 0.7rem; margin-left: 0.2rem; opacity: 0.7;
|
|
||||||
}
|
|
||||||
.folder-delete-btn:hover { opacity: 1; }
|
|
||||||
|
|
||||||
.folder-add-btn {
|
#addFolderSelect { min-width:110px; max-width:150px; background:var(--input); border:1px solid #2d3748; color:var(--text); padding:0.5rem 0.8rem; border-radius:2rem; font-size:0.8rem; cursor:pointer; }
|
||||||
background: transparent; border: 1px dashed #475569;
|
|
||||||
color: var(--text2); padding: 0.3rem 0.6rem; border-radius: 2rem;
|
|
||||||
cursor: pointer; font-size: 0.75rem; transition: 0.2s;
|
|
||||||
}
|
|
||||||
.folder-add-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
||||||
|
|
||||||
/* Add form folder select */
|
|
||||||
#addFolderSelect {
|
|
||||||
min-width: 110px; max-width: 150px;
|
|
||||||
background: var(--input); border: 1px solid #2d3748;
|
|
||||||
color: var(--text); padding: 0.5rem 0.8rem; border-radius: 2rem;
|
|
||||||
font-size: 0.8rem; cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Entries */
|
/* Entries */
|
||||||
#entriesContainer.grid-view {
|
#entriesContainer.grid-view { display:grid; grid-template-columns: repeat(auto-fill, minmax(180px,1fr)); gap:0.6rem; }
|
||||||
display: grid;
|
#entriesContainer.list-view { display:flex; flex-direction:column; gap:0.4rem; }
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
#entriesContainer.compact-view { display:flex; flex-direction:column; gap:0.2rem; }
|
||||||
gap: 0.6rem;
|
#entriesContainer.table-view { overflow-x:auto; }
|
||||||
}
|
#entriesContainer.table-view table { width:100%; border-collapse:collapse; }
|
||||||
#entriesContainer.list-view { display: flex; flex-direction: column; gap: 0.4rem; }
|
#entriesContainer.table-view th { text-align:left; padding:0.4rem 0.6rem; color:var(--text2); font-size:0.75rem; border-bottom:1px solid var(--border); }
|
||||||
#entriesContainer.compact-view { display: flex; flex-direction: column; gap: 0.2rem; }
|
#entriesContainer.table-view td { padding:0.4rem 0.6rem; font-size:0.8rem; border-bottom:1px solid rgba(255,255,255,0.03); }
|
||||||
#entriesContainer.table-view { overflow-x: auto; }
|
|
||||||
#entriesContainer.table-view table { width: 100%; border-collapse: collapse; }
|
|
||||||
#entriesContainer.table-view th {
|
|
||||||
text-align: left; padding: 0.4rem 0.6rem;
|
|
||||||
color: var(--text2); font-size: 0.75rem; border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
#entriesContainer.table-view td {
|
|
||||||
padding: 0.4rem 0.6rem; font-size: 0.8rem;
|
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry-card, .entry-row, .entry-compact, .table-row-drag {
|
.entry-card, .entry-row, .entry-compact, .table-row-drag { cursor:grab; user-select:none; }
|
||||||
cursor: grab; user-select: none;
|
.entry-card.drag-over, .entry-row.drag-over, .entry-compact.drag-over, .table-row-drag.drag-over { border-color:var(--accent)!important; box-shadow:0 0 15px rgba(59,130,246,0.3); }
|
||||||
}
|
.entry-card { background:var(--card); border:1px solid var(--border); border-radius:0.8rem; padding:0.9rem 2.8rem 0.9rem 0.9rem; transition:0.2s; position:relative; word-break:break-word; }
|
||||||
.entry-card.drag-over, .entry-row.drag-over, .entry-compact.drag-over, .table-row-drag.drag-over {
|
.entry-card:hover { border-color:rgba(255,255,255,0.15); transform:translateY(-2px); }
|
||||||
border-color: var(--accent) !important;
|
.card-site { font-weight:700; font-size:0.9rem; color:var(--text); margin-bottom:0.2rem; }
|
||||||
box-shadow: 0 0 15px rgba(59,130,246,0.3);
|
.card-user { color:var(--text2); font-size:0.75rem; margin-bottom:0.3rem; }
|
||||||
}
|
.card-folder { font-size:0.65rem; color:var(--accent); margin-bottom:0.3rem; background:rgba(59,130,246,0.15); display:inline-block; padding:0.1rem 0.5rem; border-radius:1rem; }
|
||||||
|
.card-password { background:var(--bg2); padding:0.3rem 0.5rem; border-radius:0.6rem; display:flex; align-items:center; justify-content:space-between; font-family:monospace; font-size:0.75rem; gap:0.2rem; }
|
||||||
|
.entry-row { background:var(--card); border:1px solid var(--border); border-radius:0.8rem; padding:0.7rem 2.8rem 0.7rem 0.9rem; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.4rem; position:relative; }
|
||||||
|
.entry-compact { display:flex; align-items:center; gap:0.5rem; padding:0.35rem 2.8rem 0.35rem 0.7rem; background:var(--card); border-radius:0.5rem; border:1px solid var(--border); font-size:0.8rem; position:relative; }
|
||||||
|
.action-btns { position:absolute; top:4px; right:6px; display:flex; gap:4px; z-index:1; }
|
||||||
|
.delete-btn { width:20px; height:20px; background:transparent; color:var(--danger); border:none; cursor:pointer; font-size:0.9rem; font-weight:700; display:flex; align-items:center; justify-content:center; }
|
||||||
|
.delete-btn:hover { color:#fff; transform:scale(1.2); }
|
||||||
|
.light .delete-btn:hover { color:#000!important; }
|
||||||
|
.edit-btn { width:20px; height:20px; background:transparent; color:var(--accent); border:none; cursor:pointer; font-size:0.75rem; display:flex; align-items:center; justify-content:center; }
|
||||||
|
.edit-btn:hover { color:#fff; transform:scale(1.2); }
|
||||||
|
.light .edit-btn:hover { color:#000!important; }
|
||||||
|
.entry-info { display:flex; gap:0.5rem; align-items:center; flex-wrap:wrap; flex:1; }
|
||||||
|
.entry-site { font-weight:700; color:var(--text); }
|
||||||
|
.entry-user { color:var(--text2); }
|
||||||
|
.entry-folder { font-size:0.7rem; color:var(--accent); background:rgba(59,130,246,0.15); padding:0.1rem 0.5rem; border-radius:1rem; }
|
||||||
|
.password-field { display:flex; align-items:center; gap:0.3rem; background:var(--bg2); padding:0.2rem 0.5rem; border-radius:2rem; }
|
||||||
|
.password-text { font-family:monospace; color:var(--text2); font-size:0.8rem; }
|
||||||
|
.icon-btn { background:none; border:1px solid #475569; color:var(--text2); border-radius:2rem; padding:0.2rem 0.5rem; font-size:0.65rem; cursor:pointer; }
|
||||||
|
.icon-btn:hover { background:#334155; }
|
||||||
|
|
||||||
.entry-card {
|
/* Settings Dropdown */
|
||||||
background: var(--card);
|
.settings-dropdown { position:relative; display:inline-block; }
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1); /* brighter border */
|
.settings-menu { position:absolute; top:100%; right:0; background:var(--bg2); border:1px solid var(--border); border-radius:0.8rem; padding:0.5rem; min-width:200px; z-index:1000; box-shadow:0 10px 25px rgba(0,0,0,0.4); }
|
||||||
border-radius: 0.9rem;
|
.settings-item { display:flex; justify-content:space-between; align-items:center; padding:0.4rem 0.6rem; border-radius:0.5rem; font-size:0.8rem; color:var(--text2); cursor:pointer; }
|
||||||
padding: 1rem 2.5rem 1rem 1rem;
|
.settings-item:hover { background:rgba(255,255,255,0.05); }
|
||||||
transition: 0.2s;
|
.settings-item select { background:var(--input); border:1px solid #334155; color:var(--text); padding:0.2rem 0.5rem; border-radius:1rem; font-size:0.75rem; }
|
||||||
position: relative;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry-card:hover { border-color: rgba(255,255,255,0.15); transform: translateY(-2px); }
|
/* Trash */
|
||||||
.card-site { font-weight: 700; font-size: 0.9rem; color: var(--text); margin-bottom: 0.2rem; }
|
.trash-badge { background:var(--danger); color:#fff; padding:0.15rem 0.5rem; border-radius:1rem; font-size:0.65rem; margin-left:0.3rem; }
|
||||||
.card-user { color: var(--text2); font-size: 0.75rem; margin-bottom: 0.3rem; }
|
.trash-info { font-size:0.7rem; color:var(--text2); margin-top:0.2rem; }
|
||||||
.card-folder {
|
.restore-btn { background:var(--success); color:#fff; border:none; padding:0.2rem 0.6rem; border-radius:1.5rem; cursor:pointer; font-size:0.7rem; }
|
||||||
font-size: 0.65rem; color: var(--accent); margin-bottom: 0.3rem;
|
.restore-btn:hover { filter:brightness(1.2); }
|
||||||
background: rgba(59,130,246,0.15); display: inline-block;
|
.empty-trash-btn { background:var(--danger); color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
|
||||||
padding: 0.1rem 0.5rem; border-radius: 1rem;
|
.empty-trash-btn:hover { filter:brightness(1.2); }
|
||||||
}
|
|
||||||
.card-password {
|
|
||||||
background: var(--bg2); padding: 0.3rem 0.5rem; border-radius: 0.6rem;
|
|
||||||
display: flex; align-items: center; justify-content: space-between;
|
|
||||||
font-family: monospace; font-size: 0.75rem; gap: 0.2rem;
|
|
||||||
}
|
|
||||||
.entry-row {
|
|
||||||
background: var(--card); border: 1px solid var(--border);
|
|
||||||
border-radius: 0.8rem; padding: 0.7rem 2.8rem 0.7rem 0.9rem;
|
|
||||||
display: flex; justify-content: space-between; align-items: center;
|
|
||||||
flex-wrap: wrap; gap: 0.4rem; position: relative;
|
|
||||||
}
|
|
||||||
.entry-compact {
|
|
||||||
display: flex; align-items: center; gap: 0.5rem;
|
|
||||||
padding: 0.35rem 2.8rem 0.35rem 0.7rem;
|
|
||||||
background: var(--card); border-radius: 0.5rem;
|
|
||||||
border: 1px solid var(--border); font-size: 0.8rem; position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btns { position: absolute; top: 4px; right: 6px; display: flex; gap: 4px; z-index: 1; }
|
|
||||||
.delete-btn {
|
|
||||||
width: 20px; height: 20px; background: transparent; color: var(--danger);
|
|
||||||
border: none; cursor: pointer; font-size: 0.9rem; font-weight: 700;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
}
|
|
||||||
.delete-btn:hover { color: #fff; transform: scale(1.2); }
|
|
||||||
.light .delete-btn:hover { color: #000 !important; }
|
|
||||||
.edit-btn {
|
|
||||||
width: 20px; height: 20px; background: transparent; color: var(--accent);
|
|
||||||
border: none; cursor: pointer; font-size: 0.75rem;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
}
|
|
||||||
.edit-btn:hover { color: #fff; transform: scale(1.2); }
|
|
||||||
.light .edit-btn:hover { color: #000 !important; }
|
|
||||||
|
|
||||||
.entry-info { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; flex: 1; }
|
|
||||||
.entry-site { font-weight: 700; color: var(--text); }
|
|
||||||
.entry-user { color: var(--text2); }
|
|
||||||
.entry-folder {
|
|
||||||
font-size: 0.7rem; color: var(--accent);
|
|
||||||
background: rgba(59,130,246,0.15); padding: 0.1rem 0.5rem; border-radius: 1rem;
|
|
||||||
}
|
|
||||||
.password-field {
|
|
||||||
display: flex; align-items: center; gap: 0.3rem;
|
|
||||||
background: var(--bg2); padding: 0.2rem 0.5rem; border-radius: 2rem;
|
|
||||||
}
|
|
||||||
.password-text { font-family: monospace; color: var(--text2); font-size: 0.8rem; }
|
|
||||||
.icon-btn {
|
|
||||||
background: none; border: 1px solid #475569; color: var(--text2);
|
|
||||||
border-radius: 2rem; padding: 0.2rem 0.5rem; font-size: 0.65rem; cursor: pointer;
|
|
||||||
}
|
|
||||||
.icon-btn:hover { background: #334155; }
|
|
||||||
|
|
||||||
.search-box { flex: 1; min-width: 160px; }
|
|
||||||
.idle-warning {
|
|
||||||
position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%);
|
|
||||||
background: rgba(0,0,0,0.95); color: #fff; padding: 2rem;
|
|
||||||
border-radius: 2rem; z-index: 10000; text-align: center; display: none;
|
|
||||||
}
|
|
||||||
.idle-warning.show { display: block; }
|
|
||||||
|
|
||||||
/* Edit Modal */
|
/* Edit Modal */
|
||||||
.edit-modal {
|
.edit-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); display:none; justify-content:center; align-items:center; z-index:1001; }
|
||||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
.edit-modal.show { display:flex; }
|
||||||
background: rgba(0,0,0,0.7); display: flex; justify-content: center;
|
.edit-box { background:var(--bg2); border-radius:1.5rem; padding:1.5rem; min-width:380px; max-width:90%; }
|
||||||
align-items: center; z-index: 1001; display: none;
|
.edit-box h3 { margin-bottom:1rem; color:var(--text); }
|
||||||
}
|
.edit-box label { color:var(--text2); font-size:0.8rem; display:block; margin-bottom:0.2rem; }
|
||||||
.edit-modal.show { display: flex; }
|
.edit-box input, .edit-box select { width:100%; margin-bottom:0.5rem; }
|
||||||
.edit-box {
|
|
||||||
background: var(--bg2); border-radius: 1.5rem; padding: 1.5rem;
|
|
||||||
min-width: 380px; max-width: 90%;
|
|
||||||
}
|
|
||||||
.edit-box h3 { margin-bottom: 1rem; color: var(--text); }
|
|
||||||
.edit-box label { color: var(--text2); font-size: 0.8rem; display: block; margin-bottom: 0.2rem; }
|
|
||||||
.edit-box input, .edit-box select { width: 100%; margin-bottom: 0.5rem; }
|
|
||||||
|
|
||||||
/* Custom modals (folder add/delete) */
|
/* Custom modals */
|
||||||
.custom-modal-overlay {
|
.custom-modal-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.6); display:flex; justify-content:center; align-items:center; z-index:10001; display:none; }
|
||||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
.custom-modal-overlay.show { display:flex; }
|
||||||
background: rgba(0,0,0,0.6); display: flex; justify-content: center;
|
.custom-modal { background:var(--bg2); border:1px solid var(--border); border-radius:1.2rem; padding:1.5rem; min-width:300px; max-width:90%; }
|
||||||
align-items: center; z-index: 10001; display: none;
|
.custom-modal h3 { margin-bottom:1rem; color:var(--text); }
|
||||||
}
|
.custom-modal input { width:100%; margin-bottom:1rem; }
|
||||||
.custom-modal-overlay.show { display: flex; }
|
.custom-modal .modal-actions { display:flex; gap:0.5rem; justify-content:flex-end; }
|
||||||
.custom-modal {
|
|
||||||
background: var(--bg2); border: 1px solid var(--border);
|
|
||||||
border-radius: 1.2rem; padding: 1.5rem; min-width: 300px; max-width: 90%;
|
|
||||||
}
|
|
||||||
.custom-modal h3 { margin-bottom: 1rem; color: var(--text); }
|
|
||||||
.custom-modal input { width: 100%; margin-bottom: 1rem; }
|
|
||||||
.custom-modal .modal-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
|
|
||||||
|
|
||||||
/* Confirm popup near element */
|
/* Confirm popup near element */
|
||||||
.custom-confirm {
|
.custom-confirm { position:fixed; background:var(--bg2); border:1px solid var(--accent); border-radius:0.8rem; padding:0.7rem 1rem; z-index:9999; box-shadow:0 10px 30px rgba(0,0,0,0.5); display:none; font-size:0.8rem; color:var(--text); white-space:nowrap; }
|
||||||
position: fixed; background: var(--bg2); border: 1px solid var(--accent);
|
.custom-confirm.show { display:block; }
|
||||||
border-radius: 0.8rem; padding: 0.7rem 1rem; z-index: 9999;
|
.custom-confirm .confirm-text { margin-bottom:0.5rem; }
|
||||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5); display: none;
|
.custom-confirm .confirm-btns { display:flex; gap:0.4rem; }
|
||||||
font-size: 0.8rem; color: var(--text); white-space: nowrap;
|
.custom-confirm .confirm-yes { background:var(--danger); color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
|
||||||
}
|
.custom-confirm .confirm-no { background:#334155; color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
|
||||||
.custom-confirm.show { display: block; }
|
|
||||||
.custom-confirm .confirm-text { margin-bottom: 0.5rem; }
|
|
||||||
.custom-confirm .confirm-btns { display: flex; gap: 0.4rem; }
|
|
||||||
.custom-confirm .confirm-yes {
|
|
||||||
background: var(--danger); color: #fff; border: none;
|
|
||||||
padding: 0.3rem 0.8rem; border-radius: 1.5rem; cursor: pointer; font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
.custom-confirm .confirm-no {
|
|
||||||
background: #334155; color: #fff; border: none;
|
|
||||||
padding: 0.3rem 0.8rem; border-radius: 1.5rem; cursor: pointer; font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Zigzag toast */
|
/* Zigzag toast */
|
||||||
/* .toast-zigzag { */
|
.toast-zigzag { position:fixed; z-index:9998; padding:0.4rem 0.7rem; border-radius:0.5rem; font-size:0.72rem; font-weight:600; pointer-events:none; animation:zigzagUp 1.2s ease-out forwards; white-space:nowrap; }
|
||||||
/* position: fixed; z-index: 9998; padding: 0.5rem 0.9rem; */
|
.toast-zigzag.success { background:rgba(34,197,94,0.95); color:#fff; }
|
||||||
/* border-radius: 0.5rem; font-size: 0.78rem; font-weight: 600; */
|
.toast-zigzag.error { background:rgba(239,68,68,0.95); color:#fff; }
|
||||||
/* pointer-events: none; animation: zigzagUp 1.4s ease-out forwards; white-space: nowrap; */
|
@keyframes zigzagUp { 0%{opacity:1;transform:translate(0,0) scale(1)} 20%{opacity:0.9;transform:translate(-10px,-14px) scale(0.9)} 40%{opacity:0.65;transform:translate(12px,-28px) scale(0.75)} 70%{opacity:0.3;transform:translate(-8px,-42px) scale(0.6)} 100%{opacity:0;transform:translate(0,-60px) scale(0.4)} }
|
||||||
/* } */
|
|
||||||
/* .toast-zigzag.success { background: rgba(34,197,94,0.95); color: #fff; } */
|
.idle-warning { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.95); color:#fff; padding:2rem; border-radius:2rem; z-index:10000; text-align:center; display:none; }
|
||||||
/* .toast-zigzag.error { background: rgba(239,68,68,0.95); color: #fff; } */
|
.idle-warning.show { display:block; }
|
||||||
/* @keyframes zigzagUp { */
|
|
||||||
/* 0% { opacity: 1; transform: translate(0,0) scale(1); } */
|
|
||||||
/* 15% { opacity: 1; transform: translate(-12px,-12px) scale(1.05); } */
|
|
||||||
/* 30% { opacity: 0.9; transform: translate(14px,-24px) scale(1); } */
|
|
||||||
/* 50% { opacity: 0.7; transform: translate(-10px,-38px) scale(0.95); } */
|
|
||||||
/* 70% { opacity: 0.4; transform: translate(10px,-52px) scale(0.9); } */
|
|
||||||
/* 100% { opacity: 0; transform: translate(0,-68px) scale(0.8); } */
|
|
||||||
/* } */
|
|
||||||
/* Zigzag toast – smaller over time, more transparent */
|
|
||||||
.toast-zigzag {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 9998;
|
|
||||||
padding: 0.4rem 0.7rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
font-size: 0.72rem;
|
|
||||||
font-weight: 600;
|
|
||||||
pointer-events: none;
|
|
||||||
animation: zigzagUp 1.2s ease-out forwards;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.toast-zigzag.success {
|
|
||||||
background: rgba(34, 197, 94, 0.95);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.toast-zigzag.error {
|
|
||||||
background: rgba(239, 68, 68, 0.95);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes zigzagUp {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate(0, 0) scale(1);
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
opacity: 0.9;
|
|
||||||
transform: translate(-10px, -14px) scale(0.9);
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
opacity: 0.65;
|
|
||||||
transform: translate(12px, -28px) scale(0.75);
|
|
||||||
}
|
|
||||||
70% {
|
|
||||||
opacity: 0.3;
|
|
||||||
transform: translate(-8px, -42px) scale(0.6);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translate(0, -60px) scale(0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media(max-width:700px) {
|
@media(max-width:700px) {
|
||||||
.vault {
|
.vault { padding:1rem; border-radius:1.5rem; width:98%; }
|
||||||
background: rgba(20, 30, 45, 0.25); /* a bit more visible */
|
.entry-row { flex-direction:column; }
|
||||||
backdrop-filter: blur(24px);
|
|
||||||
}
|
|
||||||
.entry-row { flex-direction: column; }
|
|
||||||
#entriesContainer.grid-view { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
|
#entriesContainer.grid-view { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
|
||||||
.toolbar { flex-direction: column; }
|
.toolbar { flex-direction:column; }
|
||||||
.folders-bar { flex-direction: column; align-items: stretch; }
|
.folders-bar { flex-direction:column; align-items:stretch; }
|
||||||
}
|
|
||||||
/* Table view improvements */
|
|
||||||
#entriesContainer.table-view {
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entriesContainer.table-view table {
|
|
||||||
min-width: 600px; /* Ensures columns don't squash too much */
|
|
||||||
table-layout: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entriesContainer.table-view th,
|
|
||||||
#entriesContainer.table-view td {
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: 0.5rem 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Limit password cell width and show ellipsis */
|
|
||||||
#entriesContainer.table-view td.password-cell {
|
|
||||||
max-width: 180px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
/* Trash view */
|
|
||||||
.trash-badge {
|
|
||||||
background: var(--danger);
|
|
||||||
color: #fff;
|
|
||||||
padding: 0.15rem 0.5rem;
|
|
||||||
border-radius: 1rem;
|
|
||||||
font-size: 0.65rem;
|
|
||||||
margin-left: 0.3rem;
|
|
||||||
}
|
|
||||||
.trash-info {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--text2);
|
|
||||||
margin-top: 0.2rem;
|
|
||||||
}
|
|
||||||
.restore-btn {
|
|
||||||
background: var(--success);
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 0.2rem 0.6rem;
|
|
||||||
border-radius: 1.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
.restore-btn:hover { filter: brightness(1.2); }
|
|
||||||
.empty-trash-btn {
|
|
||||||
background: var(--danger);
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 0.3rem 0.8rem;
|
|
||||||
border-radius: 1.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
.empty-trash-btn:hover { filter: brightness(1.2); }
|
|
||||||
#trashBtn.btn-danger {
|
|
||||||
background: var(--danger);
|
|
||||||
color: #fff;
|
|
||||||
border-color: var(--danger);
|
|
||||||
}
|
}
|
||||||
+124
-76
@@ -8,119 +8,167 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="toast-container" id="toastContainer"></div>
|
<div class="toast-container" id="toastContainer"></div>
|
||||||
<div class="idle-warning" id="idleWarning"><h3>⏰ Auto-lock</h3><p>Vault locks in <span id="idleCountdown">30</span>s</p><button class="btn btn-sm" onclick="resetIdle()">Stay Unlocked</button></div>
|
<div class="idle-warning" id="idleWarning">
|
||||||
|
<h3>⏰ Auto-lock</h3>
|
||||||
|
<p>Vault locks in <span id="idleCountdown">30</span>s</p>
|
||||||
|
<button class="btn btn-sm" onclick="resetIdle()">Stay Unlocked</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="edit-modal" id="editModal">
|
<div class="edit-modal" id="editModal">
|
||||||
<div class="edit-box">
|
<div class="edit-box">
|
||||||
<h3>✏️ Edit Entry</h3>
|
<h3>✏️ Edit Entry</h3>
|
||||||
<form onsubmit="return false">
|
<label>Website / App</label><input type="text" id="editSite" placeholder="example.com">
|
||||||
<label>Website / App</label><input type="text" id="editSite" placeholder="example.com" autocomplete="off">
|
<label style="margin-top:.6rem">Username / Email</label><input type="text" id="editUsername" placeholder="user@example.com">
|
||||||
<label style="margin-top:.6rem">Username / Email</label><input type="text" id="editUsername" placeholder="user@example.com" autocomplete="off">
|
<label style="margin-top:.6rem">Password</label>
|
||||||
<label style="margin-top:.6rem">Password</label>
|
<div style="position:relative">
|
||||||
<div style="position:relative">
|
<input type="password" id="editPassword" placeholder="Password" style="width:100%;padding-right:40px">
|
||||||
<input type="password" id="editPassword" placeholder="Password" style="width:100%;padding-right:40px" autocomplete="new-password">
|
<button type="button" onclick="toggleEditPassword()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:.9rem">👁️</button>
|
||||||
<button type="button" onclick="toggleEditPassword()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:.9rem">👁️</button>
|
</div>
|
||||||
</div>
|
<label style="margin-top:.6rem">Folder</label>
|
||||||
<label style="margin-top:.6rem">Folder</label>
|
<select id="editFolder" style="width:100%;margin-bottom:.4rem"></select>
|
||||||
<select id="editFolder" style="width:100%;margin-bottom:.4rem"></select>
|
<input type="hidden" id="editId">
|
||||||
<input type="hidden" id="editId">
|
<div style="display:flex;gap:.5rem;margin-top:1rem">
|
||||||
<div style="display:flex;gap:.5rem;margin-top:1rem">
|
<button class="btn" onclick="saveEdit()">💾 Save</button>
|
||||||
<button class="btn" onclick="saveEdit()">💾 Save</button>
|
<button class="btn btn-outline" onclick="closeEdit()">Cancel</button>
|
||||||
<button class="btn btn-outline" onclick="closeEdit()">Cancel</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="vault">
|
<div class="vault">
|
||||||
<h1>🔐 Vault <span>XAMPP</span>
|
<h1>🔐 Vault <span>XAMPP</span>
|
||||||
<button class="btn btn-outline btn-xs" id="soundToggle" onclick="toggleSound()" title="Sound" style="margin-left:0.3rem;">🔊</button>
|
<button class="btn btn-outline btn-xs" onclick="toggleTheme()" style="margin-left:auto">🌓</button>
|
||||||
<button class="btn btn-outline btn-xs" onclick="toggleTheme()" style="margin-left:auto">🌓</button>
|
</h1>
|
||||||
</h1>
|
|
||||||
|
<!-- Auth Section -->
|
||||||
<div id="authSection" class="auth-section">
|
<div id="authSection" class="auth-section">
|
||||||
<div class="auth-tabs"><button class="auth-tab active" onclick="switchTab('login')">Login</button><button class="auth-tab" onclick="switchTab('register')">Register</button></div>
|
<div class="auth-tabs">
|
||||||
<div id="loginForm">
|
<button class="auth-tab active" onclick="switchTab('login')">Login</button>
|
||||||
<form onsubmit="return false" class="input-group" autocomplete="off">
|
<button class="auth-tab" onclick="switchTab('register')">Register</button>
|
||||||
<input type="text" id="loginUsername" placeholder="Username" autocomplete="username">
|
</div>
|
||||||
<input type="password" id="loginPassword" placeholder="Master Password" autocomplete="current-password">
|
<div id="loginForm">
|
||||||
<button class="btn" id="loginBtn" onclick="login()">🔓 Unlock</button>
|
<div class="input-group">
|
||||||
</form>
|
<input type="text" id="loginUsername" placeholder="Username" autocomplete="off">
|
||||||
</div>
|
<input type="password" id="loginPassword" placeholder="Master Password" autocomplete="off">
|
||||||
|
<button class="btn" id="loginBtn" onclick="login()">🔓 Unlock</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="registerForm" class="hidden">
|
<div id="registerForm" class="hidden">
|
||||||
<form onsubmit="return false" class="input-group" autocomplete="off">
|
<div class="input-group">
|
||||||
<input type="text" id="regUsername" placeholder="Username (min 3)" autocomplete="username">
|
<input type="text" id="regUsername" placeholder="Username (min 3)" autocomplete="off">
|
||||||
<input type="password" id="regPassword" placeholder="Password (min 8)" autocomplete="new-password">
|
<input type="password" id="regPassword" placeholder="Password (min 8)" autocomplete="off">
|
||||||
<button class="btn" id="registerBtn" onclick="register()">✨ Create</button>
|
<button class="btn" id="registerBtn" onclick="register()">✨ Create</button>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Vault Section -->
|
||||||
<div id="vaultSection" class="hidden">
|
<div id="vaultSection" class="hidden">
|
||||||
|
<!-- Top bar -->
|
||||||
<div style="display:flex;justify-content:space-between;margin-bottom:.5rem;align-items:center;flex-wrap:wrap;gap:.4rem">
|
<div style="display:flex;justify-content:space-between;margin-bottom:.5rem;align-items:center;flex-wrap:wrap;gap:.4rem">
|
||||||
<span class="status-badge" id="connectionStatus">🟢 Connected</span>
|
<span class="status-badge" id="connectionStatus">🟢 Connected</span>
|
||||||
<span id="entryCount" style="color:var(--text2);font-size:.8rem"></span>
|
<span id="entryCount" style="color:var(--text2);font-size:.8rem"></span>
|
||||||
<div style="display:flex;gap:.4rem;align-items:center">
|
<div style="display:flex;gap:.4rem;align-items:center">
|
||||||
<select id="autoLockTimer" onchange="setAutoLock()" style="font-size:.7rem;padding:.3rem .5rem;background:var(--input);border:1px solid #2d3748;color:var(--text);border-radius:1.5rem">
|
|
||||||
<option value="0">No lock</option><option value="1">1 min</option><option value="5" selected>5 min</option><option value="15">15 min</option><option value="30">30 min</option><option value="60">1 hour</option>
|
|
||||||
</select>
|
|
||||||
<span id="currentUser" style="color:var(--text2);font-size:.8rem"></span>
|
<span id="currentUser" style="color:var(--text2);font-size:.8rem"></span>
|
||||||
|
<div class="settings-dropdown">
|
||||||
|
<button class="btn btn-outline btn-sm" id="settingsBtn" onclick="toggleSettings()">⚙️</button>
|
||||||
|
<div class="settings-menu hidden" id="settingsMenu">
|
||||||
|
<div class="settings-item">
|
||||||
|
<span>Auto-lock</span>
|
||||||
|
<select id="autoLockTimer" onchange="setAutoLock()">
|
||||||
|
<option value="0">No lock</option>
|
||||||
|
<option value="1">1 min</option>
|
||||||
|
<option value="5" selected>5 min</option>
|
||||||
|
<option value="15">15 min</option>
|
||||||
|
<option value="30">30 min</option>
|
||||||
|
<option value="60">1 hour</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="settings-item" onclick="toggleSound();event.stopPropagation();">
|
||||||
|
<span>Sound</span>
|
||||||
|
<div class="toggle-switch" id="soundToggleSwitch"></div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-item" onclick="toggleTheme();event.stopPropagation();">
|
||||||
|
<span>Theme</span>
|
||||||
|
<div class="toggle-switch" id="themeToggleSwitch"></div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-item" onclick="toggleViewBtn();event.stopPropagation();">
|
||||||
|
<span>👁️ View password</span>
|
||||||
|
<div class="toggle-switch active" id="showViewBtnToggle"></div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-item" onclick="toggleShowEmail();event.stopPropagation();">
|
||||||
|
<span>📧 Show email</span>
|
||||||
|
<div class="toggle-switch active" id="showEmailToggle"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="btn btn-outline btn-sm" onclick="doLogout()">🔒 Lock</button>
|
<button class="btn btn-outline btn-sm" onclick="doLogout()">🔒 Lock</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Entry Form -->
|
||||||
<form onsubmit="return false" class="input-group" autocomplete="off">
|
<form onsubmit="return false" class="input-group" autocomplete="off">
|
||||||
<input type="text" id="siteInput" placeholder="Website *" autocomplete="off">
|
<input type="text" id="siteInput" placeholder="Website *" autocomplete="off">
|
||||||
<input type="text" id="usernameInput" placeholder="Username (optional)" autocomplete="off">
|
<input type="text" id="usernameInput" placeholder="Username (optional)" autocomplete="off">
|
||||||
<div style="flex:1;min-width:130px;position:relative">
|
<div style="flex:1;min-width:130px;position:relative">
|
||||||
<input type="password" id="passwordInput" placeholder="Password *" autocomplete="new-password" style="width:100%" oninput="checkStrength()">
|
<input type="password" id="passwordInput" placeholder="Password *" autocomplete="new-password" style="width:100%;padding-right:40px" oninput="checkStrength()">
|
||||||
<div class="strength-bar s0" id="strengthBar"></div>
|
<button type="button" onclick="openGen()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:1.1rem;line-height:1" title="Generate password">🎲</button>
|
||||||
</div>
|
</div>
|
||||||
<select id="addFolderSelect"></select>
|
<div class="strength-bar s0" id="strengthBar" style="flex-basis:100%;margin-top:-0.3rem;margin-bottom:0.3rem"></div>
|
||||||
<button type="button" class="btn" id="addBtn" onclick="addEntry()">➕ Add</button>
|
<select id="addFolderSelect"></select>
|
||||||
</form>
|
<button type="button" class="btn" id="addBtn" onclick="addEntry()">➕ Add</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Toolbar with search + trash + views -->
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<input type="text" id="searchInput" placeholder="🔍 Search..." oninput="searchEntries()" autocomplete="off">
|
<input type="text" id="searchInput" placeholder="🔍 Search..." oninput="searchEntries()" autocomplete="off">
|
||||||
</div>
|
<button type="button" class="clear-search-btn" id="clearSearchBtn" onclick="clearSearch()" title="Clear search">✕</button>
|
||||||
<div class="toolbar-actions">
|
</div>
|
||||||
<div class="view-toggle" id="viewToggle">
|
<div style="display:flex;gap:.4rem;align-items:center;flex-wrap:wrap">
|
||||||
<button class="view-btn active" data-view="grid">🟫 Grid</button>
|
<button class="btn btn-outline btn-sm" id="trashBtn" onclick="toggleTrash()" title="Trash">🗑️</button>
|
||||||
<button class="view-btn" data-view="compact">📝 Compact</button>
|
<div class="view-toggle" id="viewToggle">
|
||||||
<button class="view-btn" data-view="table">📊 Table</button>
|
<button class="view-btn active" data-view="grid">🟫 Grid</button>
|
||||||
<button class="view-btn" data-view="list">📋 List</button>
|
<button class="view-btn" data-view="compact">📝 Compact</button>
|
||||||
</div>
|
<button class="view-btn" data-view="table">📊 Table</button>
|
||||||
<button class="btn btn-outline btn-sm" id="trashBtn" onclick="toggleTrash()">🗑️ Trash</button>
|
<button class="view-btn" data-view="list">📋 List</button>
|
||||||
<button class="btn btn-outline btn-sm" onclick="openGen()">🎲 Generate</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="toggles-row" style="margin-bottom:.5rem;padding:.3rem .7rem;background:rgba(0,0,0,0.2);border-radius:1rem">
|
|
||||||
<span class="toggle-item"><span>👁️ View</span><div class="toggle-switch active" id="showViewBtnToggle" onclick="toggleViewBtn()"></div></span>
|
|
||||||
<span class="toggle-item"><span>📧 Email</span><div class="toggle-switch active" id="showEmailToggle" onclick="toggleShowEmail()"></div></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="folders-bar" id="foldersBar"></div>
|
<div class="folders-bar" id="foldersBar"></div>
|
||||||
<div id="trashActions" class="hidden" style="margin-bottom:.5rem;text-align:right;">
|
<div id="trashActions" class="hidden" style="margin-bottom:.5rem;text-align:right;">
|
||||||
<button class="empty-trash-btn" onclick="emptyTrash()">🗑️ Empty Trash</button>
|
<button class="empty-trash-btn" onclick="emptyTrash()">🗑️ Empty Trash</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="entriesContainer" class="grid-view"><div style="text-align:center;color:var(--text2);padding:2rem;grid-column:1/-1">📭 No entries</div></div>
|
<div id="entriesContainer" class="grid-view">
|
||||||
<div style="margin-top:.6rem;text-align:right"><button class="btn btn-outline btn-sm" onclick="exportPasswords()">📤 Export</button></div>
|
<div style="text-align:center;color:var(--text2);padding:2rem;grid-column:1/-1">📭 No entries</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:.6rem;text-align:right">
|
||||||
|
<button class="btn btn-outline btn-sm" onclick="exportPasswords()">📤 Export</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Generator Modal -->
|
||||||
<div id="genModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);display:none;justify-content:center;align-items:center;z-index:1000">
|
<div id="genModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);display:none;justify-content:center;align-items:center;z-index:1000">
|
||||||
<div style="background:var(--bg2);border-radius:1.5rem;padding:1.5rem;min-width:320px;max-width:90%">
|
<div style="background:var(--bg2);border-radius:1.5rem;padding:1.5rem;min-width:320px;max-width:90%">
|
||||||
<h3 style="margin-bottom:.8rem;color:var(--text)">🎲 Generator</h3>
|
<h3 style="margin-bottom:.8rem;color:var(--text)">🎲 Generator</h3>
|
||||||
<div style="background:var(--input);padding:.8rem;border-radius:1rem;font-family:monospace;text-align:center;color:#4ade80;margin:.6rem 0;word-break:break-all" id="genPreview">Click Generate</div>
|
<div style="background:var(--input);padding:.8rem;border-radius:1rem;font-family:monospace;text-align:center;color:#4ade80;margin:.6rem 0;word-break:break-all" id="genPreview">Click Generate</div>
|
||||||
<div style="display:flex;align-items:center;gap:.6rem;margin:.8rem 0"><span>Length:</span><input type="range" id="pwdLen" min="8" max="64" value="16" oninput="onLenChange()" style="flex:1"><span id="lenVal" style="background:var(--input);padding:.2rem .6rem;border-radius:1rem;min-width:30px;text-align:center">16</span></div>
|
<div style="display:flex;align-items:center;gap:.6rem;margin:.8rem 0">
|
||||||
|
<span>Length:</span>
|
||||||
|
<input type="range" id="pwdLen" min="8" max="64" value="16" oninput="onLenChange()" style="flex:1">
|
||||||
|
<span id="lenVal" style="background:var(--input);padding:.2rem .6rem;border-radius:1rem;min-width:30px;text-align:center">16</span>
|
||||||
|
</div>
|
||||||
<div style="display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem 0">
|
<div style="display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem 0">
|
||||||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useUpper" checked onchange="genPwd()"> A-Z</label>
|
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useUpper" checked onchange="genPwd()"> A-Z</label>
|
||||||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useLower" checked onchange="genPwd()"> a-z</label>
|
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useLower" checked onchange="genPwd()"> a-z</label>
|
||||||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useNum" checked onchange="genPwd()"> 0-9</label>
|
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useNum" checked onchange="genPwd()"> 0-9</label>
|
||||||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useSym" checked onchange="genPwd()"> !@#$</label>
|
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useSym" checked onchange="genPwd()"> !@#$</label>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:.4rem;margin-top:.8rem"><button class="btn" onclick="genPwd()" style="flex:1">🔄</button><button class="btn" onclick="useGen()" style="flex:1">✅ Use</button><button class="btn btn-outline" onclick="closeGen()">Cancel</button></div>
|
<div style="display:flex;gap:.4rem;margin-top:.8rem">
|
||||||
|
<button class="btn" onclick="genPwd()" style="flex:1">🔄</button>
|
||||||
|
<button class="btn" onclick="useGen()" style="flex:1">✅ Use</button>
|
||||||
|
<button class="btn btn-outline" onclick="closeGen()">Cancel</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -93,14 +93,8 @@ function playSound(type) {
|
|||||||
function toggleSound() {
|
function toggleSound() {
|
||||||
soundEnabled = !soundEnabled;
|
soundEnabled = !soundEnabled;
|
||||||
localStorage.setItem('soundEnabled', soundEnabled);
|
localStorage.setItem('soundEnabled', soundEnabled);
|
||||||
const btn = document.getElementById('soundToggle');
|
if (soundEnabled) playTone(440, 0.05);
|
||||||
if (btn) btn.textContent = soundEnabled ? '🔊' : '🔇';
|
syncSettingsUI();
|
||||||
if (soundEnabled) playTone(440, 0.05, 'sine', 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSoundButton() {
|
|
||||||
const btn = document.getElementById('soundToggle');
|
|
||||||
if (btn) btn.textContent = soundEnabled ? '🔊' : '🔇';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== TOAST ====================
|
// ==================== TOAST ====================
|
||||||
@@ -118,7 +112,7 @@ function showZigzagToast(elem, msg, type) {
|
|||||||
|
|
||||||
// ==================== THEME ====================
|
// ==================== THEME ====================
|
||||||
function applyTheme() { document.body.classList.toggle('light', !dark); }
|
function applyTheme() { document.body.classList.toggle('light', !dark); }
|
||||||
function toggleTheme() { dark = !dark; localStorage.setItem('darkTheme', dark); applyTheme(); playSound('click'); }
|
function toggleTheme() { dark = !dark; localStorage.setItem('darkTheme', dark); applyTheme(); syncSettingsUI(); playSound('click'); }
|
||||||
|
|
||||||
// ==================== CRYPTO ====================
|
// ==================== CRYPTO ====================
|
||||||
function checkStrength() { const p = document.getElementById('passwordInput').value; const b = document.getElementById('strengthBar'); let s = 0; if (p.length >= 8) s++; if (p.length >= 12) s++; if (/[A-Z]/.test(p) && /[a-z]/.test(p)) s++; if (/\d/.test(p)) s++; if (/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(p)) s++; b.className = 'strength-bar s' + Math.min(4, s); }
|
function checkStrength() { const p = document.getElementById('passwordInput').value; const b = document.getElementById('strengthBar'); let s = 0; if (p.length >= 8) s++; if (p.length >= 12) s++; if (/[A-Z]/.test(p) && /[a-z]/.test(p)) s++; if (/\d/.test(p)) s++; if (/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(p)) s++; b.className = 'strength-bar s' + Math.min(4, s); }
|
||||||
@@ -282,18 +276,28 @@ function timeAgo(dateStr) {
|
|||||||
return days <= 0 ? 'Expiring' : days + 'd left';
|
return days <= 0 ? 'Expiring' : days + 'd left';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== SETTINGS ====================
|
||||||
|
function toggleSettings() {
|
||||||
|
document.getElementById('settingsMenu').classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
function syncSettingsUI() {
|
||||||
|
document.getElementById('soundToggleSwitch').classList.toggle('active', soundEnabled);
|
||||||
|
document.getElementById('themeToggleSwitch').classList.toggle('active', !dark);
|
||||||
|
document.getElementById('showViewBtnToggle').classList.toggle('active', showView);
|
||||||
|
document.getElementById('showEmailToggle').classList.toggle('active', showMail);
|
||||||
|
document.getElementById('autoLockTimer').value = lockMin;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== INIT ====================
|
// ==================== INIT ====================
|
||||||
function init() {
|
function init() {
|
||||||
document.querySelectorAll('.view-btn').forEach(b => b.classList.toggle('active', b.dataset.view === view));
|
document.querySelectorAll('.view-btn').forEach(b => b.classList.toggle('active', b.dataset.view === view));
|
||||||
document.getElementById('showViewBtnToggle').classList.toggle('active', showView);
|
|
||||||
document.getElementById('showEmailToggle').classList.toggle('active', showMail);
|
|
||||||
document.getElementById('autoLockTimer').value = lockMin;
|
document.getElementById('autoLockTimer').value = lockMin;
|
||||||
applyTheme();
|
applyTheme();
|
||||||
document.getElementById('usernameInput').style.display = showMail ? '' : 'none';
|
document.getElementById('usernameInput').style.display = showMail ? '' : 'none';
|
||||||
loadUsername();
|
loadUsername();
|
||||||
const sl = localStorage.getItem('savedLoginUser');
|
const sl = localStorage.getItem('savedLoginUser');
|
||||||
if (sl) document.getElementById('loginUsername').value = sl;
|
if (sl) document.getElementById('loginUsername').value = sl;
|
||||||
updateSoundButton();
|
syncSettingsUI();
|
||||||
document.getElementById('viewToggle').addEventListener('click', e => {
|
document.getElementById('viewToggle').addEventListener('click', e => {
|
||||||
if (e.target.classList.contains('view-btn')) {
|
if (e.target.classList.contains('view-btn')) {
|
||||||
document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));
|
||||||
@@ -304,9 +308,20 @@ function init() {
|
|||||||
playSound('click');
|
playSound('click');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const clearBtn = document.getElementById('clearSearchBtn');
|
||||||
|
if (clearBtn) clearBtn.style.display = 'none';
|
||||||
|
// Close settings when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const menu = document.getElementById('settingsMenu');
|
||||||
|
const btn = document.getElementById('settingsBtn');
|
||||||
|
if (menu && !menu.classList.contains('hidden') && !menu.contains(e.target) && e.target !== btn) {
|
||||||
|
menu.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function toggleViewBtn() { showView = !showView; localStorage.setItem('showViewBtn', showView); document.getElementById('showViewBtnToggle').classList.toggle('active', showView); render(); playSound('click'); }
|
|
||||||
function toggleShowEmail() { showMail = !showMail; localStorage.setItem('showEmail', showMail); document.getElementById('showEmailToggle').classList.toggle('active', showMail); document.getElementById('usernameInput').style.display = showMail ? '' : 'none'; render(); playSound('click'); }
|
function toggleViewBtn() { showView = !showView; localStorage.setItem('showViewBtn', showView); syncSettingsUI(); render(); playSound('click'); }
|
||||||
|
function toggleShowEmail() { showMail = !showMail; localStorage.setItem('showEmail', showMail); syncSettingsUI(); document.getElementById('usernameInput').style.display = showMail ? '' : 'none'; render(); playSound('click'); }
|
||||||
|
|
||||||
// ==================== GENERATOR ====================
|
// ==================== GENERATOR ====================
|
||||||
function openGen() { document.getElementById('genModal').style.display = 'flex'; genPwd(); playSound('open'); }
|
function openGen() { document.getElementById('genModal').style.display = 'flex'; genPwd(); playSound('open'); }
|
||||||
@@ -391,6 +406,7 @@ function showVault() {
|
|||||||
loadUsername();
|
loadUsername();
|
||||||
renderFolders();
|
renderFolders();
|
||||||
populateAddFolderSelect();
|
populateAddFolderSelect();
|
||||||
|
syncSettingsUI();
|
||||||
resetIdle();
|
resetIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,10 +603,22 @@ async function delEntry(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== UTILS ====================
|
// ==================== UTILS ====================
|
||||||
function searchEntries() { loadEntries(document.getElementById('searchInput').value); }
|
function searchEntries() {
|
||||||
|
const input = document.getElementById('searchInput');
|
||||||
|
const btn = document.getElementById('clearSearchBtn');
|
||||||
|
if (btn) btn.style.display = input.value.trim() ? 'block' : 'none';
|
||||||
|
loadEntries(input.value);
|
||||||
|
}
|
||||||
function exportPasswords() { if (!confirm('Export plain text?')) return; const b = new Blob([JSON.stringify(entries, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = 'vault-' + new Date().toISOString().slice(0, 10) + '.json'; a.click(); URL.revokeObjectURL(a.href); toast('Exported!'); }
|
function exportPasswords() { if (!confirm('Export plain text?')) return; const b = new Blob([JSON.stringify(entries, null, 2)], { type: 'application/json' }); const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = 'vault-' + new Date().toISOString().slice(0, 10) + '.json'; a.click(); URL.revokeObjectURL(a.href); toast('Exported!'); }
|
||||||
function esc(t) { const d = document.createElement('div'); d.textContent = t; return d.innerHTML; }
|
function esc(t) { const d = document.createElement('div'); d.textContent = t; return d.innerHTML; }
|
||||||
|
function clearSearch() {
|
||||||
|
const input = document.getElementById('searchInput');
|
||||||
|
input.value = '';
|
||||||
|
const btn = document.getElementById('clearSearchBtn');
|
||||||
|
if (btn) btn.style.display = 'none';
|
||||||
|
loadEntries();
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
// ==================== STARTUP – session persistence ====================
|
// ==================== STARTUP – session persistence ====================
|
||||||
init();
|
init();
|
||||||
applyTheme();
|
applyTheme();
|
||||||
|
|||||||
Reference in New Issue
Block a user