Add Notes trash action

This commit is contained in:
mirivlad 2026-06-27 13:37:11 +08:00
parent 084580d3fd
commit 5349973f04
3 changed files with 76 additions and 2 deletions

View File

@ -50,6 +50,8 @@
'.notes-modal-btn.cancel:hover{background:#3a3a5e}', '.notes-modal-btn.cancel:hover{background:#3a3a5e}',
'.notes-modal-btn.confirm{background:#4ecca3;color:#111;border-color:#4ecca3}', '.notes-modal-btn.confirm{background:#4ecca3;color:#111;border-color:#4ecca3}',
'.notes-modal-btn.confirm:hover{background:#3dbb92}', '.notes-modal-btn.confirm:hover{background:#3dbb92}',
'.notes-modal-btn.danger{background:#7a2a35;color:#fff;border-color:#b84a5a}',
'.notes-modal-btn.danger:hover{background:#963545}',
'.notes-status{font-size:.72rem;color:#8b8ba8;padding:.15rem .5rem;white-space:nowrap}', '.notes-status{font-size:.72rem;color:#8b8ba8;padding:.15rem .5rem;white-space:nowrap}',
'.notes-status.success{color:#4ecca3}', '.notes-status.success{color:#4ecca3}',
'.notes-status.error{color:#e74c3c}', '.notes-status.error{color:#e74c3c}',
@ -88,7 +90,8 @@
add: 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z', add: 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z',
rename: 'M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z', rename: 'M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z',
open: 'M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7zM5 5h6V3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6h-2v6H5V5z', open: 'M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7zM5 5h6V3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6h-2v6H5V5z',
search: 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z' search: 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z',
trash: 'M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5-1-1h-5l-1 1H5v2h14V4z'
}; };
function iconSvg(name) { function iconSvg(name) {
@ -336,8 +339,16 @@
el('button', { el('button', {
className: 'notes-item-btn', className: 'notes-item-btn',
title: 'Rename', title: 'Rename',
'data-note-action': 'rename',
innerHTML: iconSvg('rename'), innerHTML: iconSvg('rename'),
onClick: function (e) { e.stopPropagation(); beginRename(note); } onClick: function (e) { e.stopPropagation(); beginRename(note); }
}),
el('button', {
className: 'notes-item-btn',
title: 'Move to Trash',
'data-note-action': 'trash',
innerHTML: iconSvg('trash'),
onClick: function (e) { e.stopPropagation(); confirmTrashNote(note); }
}) })
]) ])
]); ]);
@ -460,6 +471,24 @@
}); });
} }
// ─── Trash ──────────────────────────────────────────────
function confirmTrashNote(note) {
if (!note) return;
showTrashModal(note).then(function (confirmed) {
if (!confirmed || disposed) return;
setStatus('Moving note to trash...', 'loading');
api.files.trash(note.path).then(function () {
if (disposed) return;
if (selectedPath === note.path) selectedPath = '';
setStatus('Note moved to trash', 'success');
loadNotes();
}).catch(function (err) {
setStatus('Error: ' + (err.message || err), 'error');
});
});
}
// ─── Conflict Modal ───────────────────────────────────── // ─── Conflict Modal ─────────────────────────────────────
function showConflictModal(title, existingPath) { function showConflictModal(title, existingPath) {
@ -478,6 +507,28 @@
document.body.appendChild(overlay); document.body.appendChild(overlay);
} }
function showTrashModal(note) {
return new Promise(function (resolve) {
var overlay = el('div', { className: 'notes-modal-overlay' });
function close(value) {
overlay.remove();
resolve(value);
}
var modal = el('div', { className: 'notes-modal' }, [
el('div', { className: 'notes-modal-title' }, ['Move Note to Trash']),
el('div', { className: 'notes-modal-msg' }, [
'Move "' + (note.title || fileName(note.path)) + '" to trash?'
]),
el('div', { className: 'notes-modal-actions' }, [
el('button', { className: 'notes-modal-btn cancel', textContent: 'Cancel', onClick: function () { close(false); } }),
el('button', { className: 'notes-modal-btn danger', 'data-notes-confirm-trash': '', textContent: 'Move to Trash', onClick: function () { close(true); } })
])
]);
overlay.appendChild(modal);
document.body.appendChild(overlay);
});
}
// ─── Event Wiring ─────────────────────────────────────── // ─── Event Wiring ───────────────────────────────────────
createBtn.addEventListener('click', showCreate); createBtn.addEventListener('click', showCreate);

View File

@ -4,7 +4,7 @@
"name": "Notes", "name": "Notes",
"version": "0.1.0", "version": "0.1.0",
"apiVersion": "0.1.0", "apiVersion": "0.1.0",
"description": "Workspace-scoped notes manager for Markdown files in canonical Notes folders with create, rename, and Workbench integration.", "description": "Workspace-scoped notes manager for Markdown files in canonical Notes folders with create, rename, trash, and Workbench integration.",
"source": "official", "source": "official",
"icon": "edit", "icon": "edit",
"provides": [ "provides": [
@ -17,6 +17,7 @@
"permissions": [ "permissions": [
"files.read", "files.read",
"files.write", "files.write",
"files.delete",
"workbench.open", "workbench.open",
"ui.register" "ui.register"
], ],

View File

@ -170,6 +170,16 @@ function makeApi(options = {}) {
entries.set(toRelativePath, entry); entries.set(toRelativePath, entry);
entries.delete(fromRelativePath); entries.delete(fromRelativePath);
}, },
trash: async (relativePath) => {
if (!entries.has(relativePath)) throw new Error(`not-found: ${relativePath}`);
entries.delete(relativePath);
return {
originalPath: relativePath,
trashPath: `.verstak/trash/files/mock/${path.basename(relativePath)}`,
trashId: 'mock',
deletedAt: new Date().toISOString(),
};
},
}, },
workbench: { workbench: {
openResource: async (request) => { openResource: async (request) => {
@ -229,6 +239,18 @@ async function mountNotes(api) {
throw new Error('create note did not open the newly created file'); throw new Error('create note did not open the newly created file');
} }
const trashButton = walk(container, (node) => node.getAttribute && node.getAttribute('data-note-action') === 'trash');
if (!trashButton) throw new Error('trash note button not found');
trashButton.click();
await flush();
const trashConfirm = walk(document.body, (node) => node.getAttribute && node.getAttribute('data-notes-confirm-trash') !== undefined);
if (!trashConfirm) throw new Error('trash confirmation button not found');
trashConfirm.click();
await flush();
if (createApi.entries.has('Project/Notes/First_Note.md')) {
throw new Error('trash note did not remove the markdown file from active entries');
}
console.log('notes plugin smoke passed'); console.log('notes plugin smoke passed');
})().catch((err) => { })().catch((err) => {
console.error(err); console.error(err);