Mercurial
view mrjunejune/src/public/editor.js @ 215:c3df85159b31
removed a python library that isn't used for much.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sat, 28 Feb 2026 20:34:18 -0800 |
| parents | e5aed6c36672 |
| children |
line wrap: on
line source
let editor = null; let currentNoteId = 'index'; function getAuthToken() { return localStorage.getItem('notes-auth-token'); } function requireAuth() { if (!getAuthToken()) { const returnUrl = encodeURIComponent(window.location.pathname); window.location.href = '/notes/login?return=' + returnUrl; return false; } return true; } function goHome() { window.location.href = '/notes'; } function logout() { localStorage.removeItem('notes-auth-token'); window.location.href = '/notes/login'; } function getNoteIdFromPath() { const path = window.location.pathname; const match = path.match(/^\/notes\/(.+)$/); if (match && match[1] && match[1] !== 'login') { return decodeURIComponent(match[1]); } return 'index'; } function showNewNoteDialog() { document.getElementById('new-note-dialog').classList.add('show'); document.getElementById('new-note-id').focus(); } function hideNewNoteDialog() { document.getElementById('new-note-dialog').classList.remove('show'); document.getElementById('new-note-id').value = ''; } function createNewNote() { let noteId = document.getElementById('new-note-id').value.trim(); if (!noteId) return; // Sanitize note ID noteId = noteId.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-'); hideNewNoteDialog(); window.location.href = '/notes/' + encodeURIComponent(noteId); } // Handle Enter key in new note dialog document.getElementById('new-note-id').addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); createNewNote(); } if (e.key === 'Escape') { hideNewNoteDialog(); } }); // Close dialog on backdrop click document.getElementById('new-note-dialog').addEventListener('click', function(e) { if (e.target === this) { hideNewNoteDialog(); } }); async function uploadFile(file) { const token = getAuthToken(); if (!token) { throw new Error('Not authenticated'); } // 1. Create media record const createResponse = await fetch('/api/media/create', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: file.name, content_type: file.type }) }); if (!createResponse.ok) { const error = await createResponse.json().catch(() => ({})); throw new Error(error.error || 'Failed to create media record'); } const data = await createResponse.json(); // 2. Upload file directly to S3 const uploadResponse = await fetch(data.upload_url, { method: 'PUT', headers: { 'Content-Type': file.type }, body: file }); if (!uploadResponse.ok) { throw new Error('Failed to upload file to S3'); } // 3. Mark as uploaded (triggers processing for images) await fetch(`/api/media/${data.media_id}/uploaded`, { method: 'POST', headers: { 'Authorization': 'Bearer ' + token } }); // 4. Poll for images, return immediately for non-images if (file.type.startsWith('image/')) { return await pollForProcessedImage(data.media_id, token); } else { // For non-images, use the public URL from the server return { url: data.public_url }; } } async function pollForProcessedImage(mediaId, token) { const maxAttempts = 60; // 2 minutes max (60 * 2 seconds) for (let i = 0; i < maxAttempts; i++) { await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second interval const statusResponse = await fetch(`/api/media/${mediaId}/status`, { headers: { 'Authorization': 'Bearer ' + token } }); if (!statusResponse.ok) { console.warn('Status check failed, retrying...'); continue; } const statusData = await statusResponse.json(); if (statusData.status === 'finished') { return { url: statusData.processed_url }; } else if (statusData.status === 'error') { throw new Error(statusData.error_message || 'Processing failed'); } // Status is 'uploaded' or 'processing', continue polling } throw new Error('Processing timeout after 2 minutes'); } async function saveContent(content) { const token = getAuthToken(); if (!token) return; const response = await fetch('/api/editor/save', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }, body: JSON.stringify({ doc_id: currentNoteId, content: content }) }); if (!response.ok) { throw new Error('Failed to save'); } } async function loadNote(noteId) { const token = getAuthToken(); if (!token) return; try { const response = await fetch('/api/editor/load/' + encodeURIComponent(noteId), { headers: { 'Authorization': 'Bearer ' + token } }); if (response.ok) { const data = await response.json(); editor.setContent(data.content || ''); } } catch (error) { console.error('Failed to load note:', error); } } // Initialize document.addEventListener('DOMContentLoaded', function() { if (!requireAuth()) return; currentNoteId = getNoteIdFromPath(); document.getElementById('note-id-display').textContent = currentNoteId; // Update page title document.title = currentNoteId + ' | Notes'; editor = RichEditor.init('editor-container', { uploadCallback: uploadFile, saveCallback: saveContent, debounceMs: 1500, placeholder: 'Start writing... (paste images, drag files, or use /upload)\n\nTip: Click "+ New Note" to create linked notes.' }); loadNote(currentNoteId); });