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>