Mercurial
diff mrjunejune/src/editor/index.html @ 201:6cdee35a7ba9
[MrJuneJune] notes
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 07:07:50 -0800 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/src/editor/index.html Sun Feb 15 07:07:50 2026 -0800 @@ -0,0 +1,221 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + {{/parts/base_head.html}} + <title>Editor | MrJuneJune</title> + <style> + .editor-page { + max-width: 900px; + margin: 0 auto; + padding: 20px; + } + .editor-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + .editor-header h1 { + margin: 0; + } + .doc-selector { + display: flex; + gap: 10px; + align-items: center; + } + .doc-selector input { + padding: 8px 12px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + } + .doc-selector button { + padding: 8px 16px; + background: #0078ff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + } + .doc-selector button:hover { + background: #0066dd; + } + .auth-section { + margin-bottom: 20px; + padding: 16px; + background: #f5f5f5; + border-radius: 4px; + } + .auth-section input { + width: 300px; + padding: 8px 12px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + } + .auth-section label { + display: block; + margin-bottom: 8px; + font-weight: bold; + } + </style> +</head> +<body> + {{/parts/header.html}} + + <main class="editor-page"> + <div class="editor-header"> + <h1>Rich Editor</h1> + <div class="doc-selector"> + <input type="text" id="doc-id" placeholder="Document ID" value="default"> + <button onclick="loadDocument()">Load</button> + </div> + </div> + + <div class="auth-section"> + <label for="auth-token">Auth Token:</label> + <input type="password" id="auth-token" placeholder="Enter your auth token"> + </div> + + <div id="editor-container"></div> + </main> + + {{/parts/footer.html}} + + <script src="/public/js/rich_editor.js"></script> + <script> + let editor = null; + + function getAuthToken() { + return document.getElementById('auth-token').value; + } + + function getDocId() { + return document.getElementById('doc-id').value || 'default'; + } + + async function uploadFile(file) { + const token = getAuthToken(); + if (!token) { + alert('Please enter your auth token'); + throw new Error('No auth token'); + } + + // Get presigned upload URL + const response = await fetch('/api/s3/upload-url', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filename: file.name, + content_type: file.type + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Failed to get upload URL'); + } + + const data = await response.json(); + + // 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'); + } + + return { + url: data.public_url, + key: data.key + }; + } + + async function saveContent(content) { + const token = getAuthToken(); + if (!token) { + console.warn('No auth token, skipping save'); + return; + } + + const response = await fetch('/api/editor/save', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + doc_id: getDocId(), + content: content + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || 'Failed to save'); + } + } + + async function loadDocument() { + const token = getAuthToken(); + if (!token) { + alert('Please enter your auth token'); + return; + } + + const docId = getDocId(); + + try { + const response = await fetch('/api/editor/load/' + encodeURIComponent(docId), { + headers: { + 'Authorization': 'Bearer ' + token + } + }); + + if (!response.ok) { + const error = await response.json(); + alert('Error: ' + (error.error || 'Failed to load')); + return; + } + + const data = await response.json(); + editor.setContent(data.content || ''); + console.log('Loaded document:', docId); + } catch (error) { + console.error('Load error:', error); + alert('Failed to load document'); + } + } + + // Initialize editor + document.addEventListener('DOMContentLoaded', function() { + editor = RichEditor.init('editor-container', { + uploadCallback: uploadFile, + saveCallback: saveContent, + debounceMs: 1500, + placeholder: 'Start writing... (paste images, use /upload for files)' + }); + + // Try to load saved token from localStorage + const savedToken = localStorage.getItem('editor-auth-token'); + if (savedToken) { + document.getElementById('auth-token').value = savedToken; + } + + // Save token to localStorage on change + document.getElementById('auth-token').addEventListener('change', function() { + localStorage.setItem('editor-auth-token', this.value); + }); + }); + </script> +</body> +</html>