diff --git a/notes-workspace.html b/notes-workspace.html
index a1a4641..365d9fd 100644
--- a/notes-workspace.html
+++ b/notes-workspace.html
@@ -159,6 +159,7 @@
+
@@ -260,16 +262,42 @@
const saveBtn = document.getElementById('saveBtn');
const hideBtn = document.getElementById('hideBtn');
const deleteBtn = document.getElementById('deleteBtn');
+ const createBtn = document.getElementById('createBtn');
+ const copyBtn = document.getElementById('copyBtn');
let recorderStream = null;
let mediaRecorder = null;
let recordChunks = [];
let lastRecordedFile = null;
+ let actionInFlight = false;
function writeOutput(value) {
output.textContent = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
}
+ function setActionBusy(isBusy) {
+ actionInFlight = isBusy;
+ const disabled = !!isBusy;
+ createBtn.disabled = disabled || !!selectedNote;
+ copyBtn.disabled = disabled || !selectedNote;
+ saveBtn.disabled = disabled || !selectedNote;
+ hideBtn.disabled = disabled || !selectedNote;
+ deleteBtn.disabled = disabled || !selectedNote;
+ }
+
+ async function runNoteAction(handler) {
+ if (actionInFlight) {
+ writeOutput('Please wait for the current action to finish.');
+ return;
+ }
+ setActionBusy(true);
+ try {
+ await handler();
+ } finally {
+ setActionBusy(false);
+ }
+ }
+
function escapeHtml(value) {
return String(value || '')
.replace(/&/g, '&')
@@ -747,9 +775,9 @@
lastRecordedFile = null;
selectedAttachmentsStatus.textContent = 'Select a note to view attachments.';
selectedAttachmentsList.innerHTML = '';
- saveBtn.disabled = true;
- hideBtn.disabled = true;
- deleteBtn.disabled = true;
+ setActionBusy(false);
+ createBtn.disabled = false;
+ copyBtn.disabled = true;
}
function selectNote(note) {
@@ -769,9 +797,9 @@
recordPreview.classList.add('hidden');
recordStatus.textContent = 'Ready';
lastRecordedFile = null;
- saveBtn.disabled = false;
- hideBtn.disabled = false;
- deleteBtn.disabled = false;
+ setActionBusy(false);
+ createBtn.disabled = true;
+ copyBtn.disabled = false;
hideBtn.textContent = toBoolFlag(note.hidden) ? 'Unhide' : 'Hide';
}
@@ -882,6 +910,9 @@
async function createNote() {
await ensurePocketBaseClient();
ensureLoggedIn();
+ if (selectedNote) {
+ throw new Error('A note is selected. Use Save Changes to update it or Copy Note to duplicate it intentionally.');
+ }
const payload = buildNotePayload();
const record = await pb.collection(NOTES_COLLECTION).create(payload);
const uploaded = await uploadPendingAttachments(record.id);
@@ -893,6 +924,23 @@
writeOutput({ message: 'Note created', id: record.id, attachmentsUploaded: uploaded.length });
}
+ async function copyNote() {
+ if (!selectedNote) throw new Error('Select a note first');
+ const sourceId = selectedNote.id;
+ await ensurePocketBaseClient();
+ ensureLoggedIn();
+ const payload = buildNotePayload();
+ payload.title = `${payload.title} (Copy)`;
+ const record = await pb.collection(NOTES_COLLECTION).create(payload);
+ const uploaded = await uploadPendingAttachments(record.id);
+ await loadNotes();
+ const refreshed = allNotes.find((n) => n.id === record.id) || record;
+ selectNote(refreshed);
+ renderList();
+ await renderSelectedAttachments();
+ writeOutput({ message: 'Note copied', sourceId, newId: record.id, attachmentsUploaded: uploaded.length });
+ }
+
async function saveNote() {
if (!selectedNote) throw new Error('Select a note first');
await ensurePocketBaseClient();
@@ -946,10 +994,11 @@
bind('loginBtn', loginPocketBase);
bind('logoutBtn', logoutPocketBase);
bind('refreshBtn', loadNotes);
- bind('createBtn', createNote);
- bind('saveBtn', saveNote);
- bind('hideBtn', toggleHidden);
- bind('deleteBtn', softDelete);
+ bind('createBtn', () => runNoteAction(createNote));
+ bind('copyBtn', () => runNoteAction(copyNote));
+ bind('saveBtn', () => runNoteAction(saveNote));
+ bind('hideBtn', () => runNoteAction(toggleHidden));
+ bind('deleteBtn', () => runNoteAction(softDelete));
document.getElementById('pickFilesBtn').addEventListener('click', () => {
attachmentsInput.click();