Browse Source

Notes icon optimization

Toby Chui 4 weeks ago
parent
commit
71718bbd0a

+ 1 - 0
src/web/Notes/img/delete_dark.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#434343"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>

+ 1 - 0
src/web/Notes/img/delete_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>

+ 1 - 0
src/web/Notes/img/download_dark.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#434343"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>

+ 1 - 0
src/web/Notes/img/download_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>

+ 1 - 0
src/web/Notes/img/edit_dark.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#434343"><path d="M200-200h57l391-391-57-57-391 391v57Zm-80 80v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm640-584-56-56 56 56Zm-141 85-28-29 57 57-29-28Z"/></svg>

+ 1 - 0
src/web/Notes/img/edit_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="M200-200h57l391-391-57-57-391 391v57Zm-80 80v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm640-584-56-56 56 56Zm-141 85-28-29 57 57-29-28Z"/></svg>

+ 1 - 0
src/web/Notes/img/theme_dark.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#434343"><path d="M324-111.5Q251-143 197-197t-85.5-127Q80-397 80-480t31.5-156Q143-709 197-763t127-85.5Q397-880 480-880t156 31.5Q709-817 763-763t85.5 127Q880-563 880-480t-31.5 156Q817-251 763-197t-127 85.5Q563-80 480-80t-156-31.5ZM520-163q119-15 199.5-104.5T800-480q0-123-80.5-212.5T520-797v634Z"/></svg>

+ 1 - 0
src/web/Notes/img/theme_white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#F3F3F3"><path d="M324-111.5Q251-143 197-197t-85.5-127Q80-397 80-480t31.5-156Q143-709 197-763t127-85.5Q397-880 480-880t156 31.5Q709-817 763-763t85.5 127Q880-563 880-480t-31.5 156Q817-251 763-197t-127 85.5Q563-80 480-80t-156-31.5ZM520-163q119-15 199.5-104.5T800-480q0-123-80.5-212.5T520-797v634Z"/></svg>

+ 98 - 4
src/web/Notes/index.html

@@ -285,7 +285,31 @@
         .delete-btn { color: #ff453a; }
         body.light-mode .delete-btn:hover { background: rgba(255, 69, 58, 0.1) !important; }
         body.dark-mode  .delete-btn:hover { background: rgba(255, 69, 58, 0.15) !important; }
-
+        .icon-btn img { display: block; width: 18px; height: 18px; }
+        .new-note-btn img { display: block; width: 26px; height: 26px; }
+
+        /* ── Snackbar ─────────────────────────────────────────────────────────── */
+        #snackbar {
+            position: fixed;
+            bottom: 28px;
+            left: 50%;
+            transform: translateX(-50%) translateY(12px);
+            padding: 9px 20px;
+            border-radius: 10px;
+            font-size: 13px;
+            font-family: inherit;
+            opacity: 0;
+            pointer-events: none;
+            transition: opacity 0.22s ease, transform 0.22s ease;
+            z-index: 9999;
+            white-space: nowrap;
+        }
+        #snackbar.show {
+            opacity: 1;
+            transform: translateX(-50%) translateY(0);
+        }
+        body.light-mode #snackbar { background: #1c1c1e; color: #ffffff; }
+        body.dark-mode  #snackbar { background: #f2f2f7; color: #1c1c1e; }
         /* ── Scrollbar ────────────────────────────────────────────────────── */
         ::-webkit-scrollbar { width: 4px; }
         ::-webkit-scrollbar-track { background: transparent; }
@@ -300,7 +324,9 @@
         <div class="sidebar-header">
             <span class="sidebar-title">Notes</span>
             <div class="sidebar-header-actions">
-                <button class="icon-btn" id="themeToggle" title="Toggle theme">◐</button>
+                <button class="icon-btn" id="themeToggle" title="Toggle theme">
+                    <img id="themeIcon" src="img/theme_white.svg" alt="theme">
+                </button>
             </div>
         </div>
 
@@ -311,7 +337,9 @@
         <div class="note-list" id="noteList"></div>
 
         <div class="new-note-bar">
-            <button class="new-note-btn" id="newNoteBtn" title="New Note (Ctrl+N)">&#9998;</button>
+            <button class="new-note-btn" id="newNoteBtn" title="New Note (Ctrl+N)">
+                <img id="newNoteIcon" src="img/edit_white.svg" alt="new note">
+            </button>
         </div>
     </div>
 
@@ -329,7 +357,12 @@
             <div class="editor-header">
                 <span class="editor-date" id="editorDate"></span>
                 <div class="editor-actions">
-                    <button class="icon-btn delete-btn" id="deleteNoteBtn" title="Delete note">❌</button>
+                    <button class="icon-btn" id="downloadNoteBtn" title="Export as .txt">
+                        <img id="downloadIcon" src="img/download_white.svg" alt="download">
+                    </button>
+                    <button class="icon-btn delete-btn" id="deleteNoteBtn" title="Delete note">
+                        <img id="deleteIcon" src="img/delete_white.svg" alt="delete">
+                    </button>
                 </div>
             </div>
             <div class="editor-area">
@@ -339,6 +372,9 @@
 
     </div>
 
+    <!-- Snackbar -->
+    <div id="snackbar"></div>
+
     <script>
         // ── State ──────────────────────────────────────────────────────────────
         var notes       = [];       // [{id, title, updatedAt}] – no content in memory
@@ -353,6 +389,18 @@
             var isDark = (theme !== 'white');
             document.body.classList.toggle('dark-mode',  isDark);
             document.body.classList.toggle('light-mode', !isDark);
+            // Swap SVG icons: dark UI → white icons; light UI → dark icons
+            var suffix = isDark ? 'white' : 'dark';
+            var iconMap = {
+                themeIcon:    'theme_',
+                newNoteIcon:  'edit_',
+                deleteIcon:   'delete_',
+                downloadIcon: 'download_'
+            };
+            Object.keys(iconMap).forEach(function (id) {
+                var el = document.getElementById(id);
+                if (el) el.src = 'img/' + iconMap[id] + suffix + '.svg';
+            });
         }
 
         // ── AGI API wrappers ───────────────────────────────────────────────────
@@ -584,9 +632,51 @@
             }, 400);
         });
 
+        // ── Snackbar ──────────────────────────────────────────────────────────
+        var _snackbarTimer = null;
+        function showSnackbar(msg) {
+            var sb = document.getElementById('snackbar');
+            if (!sb) return;
+            sb.textContent = msg;
+            sb.classList.add('show');
+            clearTimeout(_snackbarTimer);
+            _snackbarTimer = setTimeout(function () { sb.classList.remove('show'); }, 2200);
+        }
+
+        // ── Save now (used by Ctrl+S) ──────────────────────────────────────────
+        function saveNow() {
+            if (!activeId) return;
+            clearTimeout(saveTimer);
+            var content = document.getElementById('noteTextarea').value;
+            var ts = Date.now();
+            var note = notes.find(function (n) { return n.id === activeId; });
+            if (note) { note.title = getTitle(content); note.updatedAt = ts; }
+            apiWrite(activeId, content, ts);
+            showSnackbar('Note saved');
+        }
+
+        // ── Export / download current note as .txt ─────────────────────────────
+        function downloadActiveNote() {
+            if (!activeId) return;
+            var content = document.getElementById('noteTextarea').value;
+            var note = notes.find(function (n) { return n.id === activeId; });
+            var filename = ((note && note.title) ? note.title : 'note');
+            filename = filename.replace(/[\\\/:*?"<>|]/g, '_').trim() || 'note';
+            filename += '.txt';
+            var blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
+            var url = URL.createObjectURL(blob);
+            var a = document.createElement('a');
+            a.href = url;
+            a.download = filename;
+            document.body.appendChild(a);
+            a.click();
+            setTimeout(function () { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100);
+        }
+
         // ── Toolbar buttons ────────────────────────────────────────────────────
         document.getElementById('newNoteBtn').addEventListener('click', createNote);
         document.getElementById('deleteNoteBtn').addEventListener('click', deleteActiveNote);
+        document.getElementById('downloadNoteBtn').addEventListener('click', downloadActiveNote);
 
         // ── Search ─────────────────────────────────────────────────────────────
         document.getElementById('searchInput').addEventListener('input', function () {
@@ -607,6 +697,10 @@
                 e.preventDefault();
                 createNote();
             }
+            if ((e.ctrlKey || e.metaKey) && e.key === 's') {
+                e.preventDefault();
+                saveNow();
+            }
         });
 
         // ── Boot ───────────────────────────────────────────────────────────────