diff --git a/css/style.css b/css/style.css
index 8146348..2b5dfc0 100644
--- a/css/style.css
+++ b/css/style.css
@@ -368,27 +368,6 @@ input[type="range"]::-webkit-slider-thumb {
.chip button:hover { opacity: 1; background: rgba(0,0,0,0.1); }
.chip button svg { width: 10px; height: 10px; }
-/* Tag suggestions dropdown */
-.chip-suggestions {
- position: absolute;
- z-index: 50;
- background: var(--bg-elev);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- box-shadow: var(--shadow);
- padding: 4px;
- max-height: 200px;
- overflow-y: auto;
- min-width: 160px;
-}
-.chip-suggestion {
- padding: 6px 10px;
- border-radius: 6px;
- font-size: 12px;
- cursor: pointer;
-}
-.chip-suggestion:hover, .chip-suggestion.is-active { background: var(--bg-elev-2); color: var(--text); }
-.chip-suggestion-empty { padding: 6px 10px; color: var(--text-faint); font-size: 12px; }
/* Settings rows */
.setting-row {
diff --git a/index.html b/index.html
index ead01ec..f47523f 100644
--- a/index.html
+++ b/index.html
@@ -1013,60 +1013,6 @@
-
-
-
-
-
diff --git a/js/app.js b/js/app.js
index ec3ef25..f29ab87 100644
--- a/js/app.js
+++ b/js/app.js
@@ -5026,210 +5026,6 @@ async function requestCloseSlideOver() {
return true;
}
-// ============================================================
-// TAG CHIP INPUT
-// ============================================================
-// Local mutable list of tags currently in the entry modal. Synced to the
-// hidden #entryTags field on every change so saveEntry can read it.
-
-let editingTags = [];
-let chipSuggestEl = null;
-let chipSuggestActive = -1;
-
-function syncTagsHidden() {
- $('#entryTags').value = editingTags.join(',');
-}
-
-function renderChips() {
- const container = $('#entryTagsInput');
- // Wipe existing chips but keep the input element
- $$('.chip', container).forEach(c => c.remove());
- const input = $('#entryTagsField');
- editingTags.forEach((t, i) => {
- const chip = el('span', { class: 'chip' });
- chip.appendChild(el('span', null, t));
- const x = el('button', {
- type: 'button',
- on: { click: ev => {
- ev.stopPropagation();
- editingTags.splice(i, 1);
- renderChips();
- syncTagsHidden();
- } },
- });
- x.appendChild(icon('i-x'));
- chip.appendChild(x);
- container.insertBefore(chip, input);
- });
- syncTagsHidden();
-}
-
-function setEditingTags(arr) {
- editingTags = (arr || []).filter(Boolean).map(t => t.trim()).filter(Boolean);
- renderChips();
-}
-
-function addTag(raw) {
- const t = (raw || '').trim().replace(/,/g, '');
- if (!t) return;
- if (editingTags.includes(t)) return;
- editingTags.push(t);
- renderChips();
-}
-
-function closeChipSuggest() {
- if (chipSuggestEl) { chipSuggestEl.remove(); chipSuggestEl = null; }
- chipSuggestActive = -1;
-}
-
-function openChipSuggest() {
- closeChipSuggest();
- const field = $('#entryTagsField');
- const q = field.value.trim().toLowerCase();
- const existing = allTags();
- const candidates = existing
- .filter(t => !editingTags.includes(t))
- .filter(t => !q || t.toLowerCase().includes(q))
- .slice(0, 8);
- if (!q && candidates.length === 0) return;
-
- chipSuggestEl = el('div', { class: 'chip-suggestions' });
- if (candidates.length === 0) {
- chipSuggestEl.appendChild(el('div', { class: 'chip-suggestion-empty' }, 'Press Enter to create "' + q + '"'));
- } else {
- candidates.forEach((t, i) => {
- const item = el('div', {
- class: 'chip-suggestion' + (i === 0 ? ' is-active' : ''),
- on: { mousedown: ev => { ev.preventDefault(); addTag(t); field.value = ''; closeChipSuggest(); } },
- }, t);
- chipSuggestEl.appendChild(item);
- });
- chipSuggestActive = 0;
- }
- // Position under the chip input
- const rect = $('#entryTagsInput').getBoundingClientRect();
- chipSuggestEl.style.position = 'fixed';
- chipSuggestEl.style.top = (rect.bottom + 4) + 'px';
- chipSuggestEl.style.left = rect.left + 'px';
- chipSuggestEl.style.width = Math.max(160, rect.width / 2) + 'px';
- document.body.appendChild(chipSuggestEl);
-}
-
-function moveChipSuggest(dir) {
- if (!chipSuggestEl) return;
- const items = $$('.chip-suggestion', chipSuggestEl);
- if (items.length === 0) return;
- items.forEach(it => it.classList.remove('is-active'));
- chipSuggestActive = (chipSuggestActive + dir + items.length) % items.length;
- items[chipSuggestActive].classList.add('is-active');
-}
-
-function selectActiveSuggestion() {
- if (!chipSuggestEl || chipSuggestActive < 0) return false;
- const items = $$('.chip-suggestion', chipSuggestEl);
- if (items[chipSuggestActive]) {
- addTag(items[chipSuggestActive].textContent);
- $('#entryTagsField').value = '';
- closeChipSuggest();
- return true;
- }
- return false;
-}
-
-// ============================================================
-// ENTRY MODAL (new / edit)
-// ============================================================
-
-async function openEntryModal(entry) {
- populateFolderSelect();
- if (entry) {
- $('#entryModalTitle').textContent = 'Edit entry';
- $('#entryId').value = entry.id;
- $('#entryTitle').value = entry.title || '';
- $('#entrySite').value = entry.site;
- $('#entryUsername').value = entry.username || '';
- $('#entryFolder').value = entry.folder || 'All';
- setEditingTags(parseTags(entry.tags));
- $('#entryPassword').value = await decryptPwd(entry.encrypted_password, entry.iv);
- if ($('#entryPassword').value === '[ERROR]') $('#entryPassword').value = '';
- } else {
- $('#entryModalTitle').textContent = 'New entry';
- $('#entryId').value = '';
- $('#entryForm').reset();
- $('#entryFolder').value = state.view.startsWith('folder:') ? state.view.slice(7) : 'All';
- setEditingTags([]);
- }
- $('#entryTagsField').value = '';
- closeChipSuggest();
- updateEntryStrength();
- $('#entryModal').classList.remove('is-hidden');
- $('#entrySite').focus();
-}
-
-function closeEntryModal() {
- $('#entryModal').classList.add('is-hidden');
-}
-
-function populateFolderSelect() {
- const sel = $('#entryFolder');
- sel.innerHTML = '';
- state.folders.forEach(f => sel.appendChild(el('option', { value: f.name }, folderLabel(f.name))));
-}
-
-async function saveEntry(e) {
- e && e.preventDefault();
- // Flush any pending text in the chip input as a final tag
- const pending = $('#entryTagsField').value.trim();
- if (pending) { addTag(pending); $('#entryTagsField').value = ''; }
- const id = $('#entryId').value;
- const title = $('#entryTitle').value.trim();
- const site = $('#entrySite').value.trim();
- const user = $('#entryUsername').value.trim();
- const pwd = $('#entryPassword').value;
- const fold = $('#entryFolder').value;
- const tags = editingTags.join(',');
- if (!site || !pwd) return toast('Site and password required', 'error');
-
- const enc = await encryptPwd(pwd);
- const body = JSON.stringify(await withEncryptedMeta({
- site, title, username: user, encrypted_password: enc.encrypted, iv: enc.iv,
- folder: fold, tags,
- }));
- try {
- let savedId = id ? parseInt(id) : null;
- if (id) {
- await api('/entries/' + id, {
- method: 'PUT',
- headers: authHeaders({ 'Content-Type': 'application/json' }),
- body,
- });
- toast('Updated');
- } else {
- const r = await api('/entries', {
- method: 'POST',
- headers: authHeaders({ 'Content-Type': 'application/json' }),
- body,
- });
- if (r && typeof r.id === 'number') savedId = r.id;
- toast('Saved');
- }
- closeEntryModal();
- await loadEntries();
- if (typeof healthCache !== 'undefined') healthCache = null;
- render();
- if (savedId) flashEntry(savedId);
- // Fire-and-forget favicon fetch for the saved entry. Updates the
- // card in place when it arrives. No-op when feature is off or
- // the entry already has a cached icon.
- if (savedId && state.faviconsEnabled) {
- const saved = state.entries.find(e => e.id === savedId);
- if (saved) ensureEntryFavicon(saved); // honours the toggle
- }
- } catch (err) {
- toast(err.message, 'error');
- }
-}
-
async function restoreEntry(id) {
try {
await api('/entries/' + id + '/restore', { method: 'POST', headers: authHeaders() });
@@ -5802,12 +5598,12 @@ function genPassword() {
$('#genPreview').textContent = p;
}
-// genTarget: 'entry' (insert into entry form) or 'standalone' (just copy/dismiss)
-let genTarget = 'entry';
+// genTarget: 'slideover' (insert into the editor) or 'standalone' (just copy)
+let genTarget = 'slideover';
function openGen(target) {
- genTarget = target || 'entry';
- // Show "Use" when targeting an editable field (entry modal or slide-over)
+ genTarget = target || 'slideover';
+ // Show "Use" when targeting the editor; hide it in standalone (copy-only).
$('#genUse').style.display = (genTarget === 'standalone') ? 'none' : '';
$('#genModal').classList.remove('is-hidden');
genPassword();
@@ -5832,11 +5628,6 @@ function updateRegStrength() {
const p = $('#regPassword').value;
$('#regStrengthBar').style.setProperty('--strength', computeStrength(p) + '%');
}
-function updateEntryStrength() {
- const p = $('#entryPassword').value;
- $('#entryStrengthBar').style.setProperty('--strength', computeStrength(p) + '%');
-}
-
// ============================================================
// COMMAND PALETTE
// ============================================================
@@ -7187,7 +6978,6 @@ async function init() {
$('#loginForm').addEventListener('submit', doLogin);
$('#registerForm').addEventListener('submit', doRegister);
$('#regPassword').addEventListener('input', updateRegStrength);
- $('#entryPassword').addEventListener('input', updateEntryStrength);
// Top-bar
function applyViewMode() {
@@ -7446,14 +7236,6 @@ async function init() {
closeSettings();
});
- // Entry modal
- $('#entryForm').addEventListener('submit', saveEntry);
- $('#entrySaveBtn').addEventListener('click', saveEntry);
- $$('#entryModal [data-close]').forEach(b => b.addEventListener('click', closeEntryModal));
- $('#entryPwToggle').addEventListener('click', () => {
- const input = $('#entryPassword');
- input.type = input.type === 'password' ? 'text' : 'password';
- });
$('#loginPwToggle').addEventListener('click', () => {
const input = $('#loginPassword');
input.type = input.type === 'password' ? 'text' : 'password';
@@ -7467,30 +7249,6 @@ async function init() {
const p = document.getElementById('loginPassword');
if (p) p.focus();
});
- $('#entryPwGen').addEventListener('click', openGen);
-
- // Chip input (tags)
- $('#entryTagsInput').addEventListener('click', () => $('#entryTagsField').focus());
- $('#entryTagsField').addEventListener('keydown', e => {
- const field = e.target;
- if (e.key === 'Enter' || e.key === ',') {
- e.preventDefault();
- if (!selectActiveSuggestion()) {
- addTag(field.value);
- field.value = '';
- closeChipSuggest();
- }
- } else if (e.key === 'Backspace' && !field.value && editingTags.length) {
- editingTags.pop();
- renderChips();
- } else if (e.key === 'ArrowDown') { e.preventDefault(); moveChipSuggest(+1); }
- else if (e.key === 'ArrowUp') { e.preventDefault(); moveChipSuggest(-1); }
- else if (e.key === 'Escape') { closeChipSuggest(); }
- });
- $('#entryTagsField').addEventListener('input', openChipSuggest);
- $('#entryTagsField').addEventListener('focus', openChipSuggest);
- $('#entryTagsField').addEventListener('blur', () => setTimeout(closeChipSuggest, 150));
-
// Generator
$('#genLen').addEventListener('input', genPassword);
$$('#genModal input[type=checkbox]').forEach(c => c.addEventListener('change', genPassword));
@@ -7507,12 +7265,11 @@ async function init() {
}
});
$('#genUse').addEventListener('click', () => {
+ // Only the slideover editor consumes a generated password now (the
+ // 'standalone' target hides #genUse). The old entry-modal target is gone.
if (genTarget === 'slideover') {
const soPw = $('#soPassword');
if (soPw) { soPw.value = genCurrent; soDirtyCheck(); }
- } else {
- $('#entryPassword').value = genCurrent;
- updateEntryStrength();
}
closeGen();
});
@@ -8085,10 +7842,6 @@ async function init() {
closeHistoryModal();
return;
}
- if (!$('#entryModal').classList.contains('is-hidden')) {
- closeEntryModal();
- return;
- }
if (!$('#genModal').classList.contains('is-hidden')) {
closeGen();
return;