experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(editor): re-apply notes UI changes on top of pane redesign

- Rename 'Annotations' to 'Notes' in pane header and tooltips
- Add '+' button in notes header for adding notes (Cmd+Shift+L)
- Show 'Line N' instead of selected text excerpt in notes list
- Notes don't store selected text (text: '' in highlights)
- Cmd+Shift+L adds note, Cmd+Shift+H toggles notes pane
- Anchor to current line when no selection exists
- Pass doc to annotations pane for line number lookup
- Add CSS for annotation-add-btn

+84 -21
+44 -11
extensions/editor/annotations-pane.js
··· 1 1 /** 2 - * Annotations Pane - displays persistent highlights and their annotations. 2 + * Notes Pane - displays persistent highlights and user notes. 3 3 * Anchored to the far right of the editor layout. 4 - * Clicking an annotation scrolls to and selects the highlighted range. 4 + * Clicking a note scrolls to and selects the highlighted range. 5 5 */ 6 6 7 7 export class AnnotationsPane { ··· 10 10 this.onAnnotationClick = options.onAnnotationClick; // (highlight) => void 11 11 this.onAnnotationDelete = options.onAnnotationDelete; // (highlightId) => void 12 12 this.onAnnotationEdit = options.onAnnotationEdit; // (highlightId, note) => void 13 + this.onAddNote = options.onAddNote; // () => void — add note at current selection/cursor 13 14 this.onToggle = options.onToggle; // callback: () => void — layout handles state 14 15 this.collapsed = false; // Layout owns this state 15 16 this.highlights = []; 17 + this.doc = null; // CodeMirror doc for line number lookup 16 18 17 19 this.element = document.createElement('div'); 18 20 this.element.className = 'annotations-pane'; ··· 23 25 24 26 const title = document.createElement('span'); 25 27 title.className = 'sidebar-title'; 26 - title.textContent = 'Annotations'; 28 + title.textContent = 'Notes'; 27 29 this.header.appendChild(title); 28 30 29 31 const countBadge = document.createElement('span'); ··· 32 34 this.countBadge = countBadge; 33 35 this.header.appendChild(countBadge); 34 36 37 + // Add note button 38 + const addBtn = document.createElement('button'); 39 + addBtn.className = 'annotation-btn annotation-add-btn'; 40 + addBtn.textContent = '+'; 41 + addBtn.title = 'Add note at selection (\u2318\u21E7L)'; 42 + addBtn.addEventListener('mousedown', (e) => e.preventDefault()); 43 + addBtn.addEventListener('click', () => { 44 + if (this.onAddNote) this.onAddNote(); 45 + }); 46 + this.header.appendChild(addBtn); 47 + 35 48 this.toggleBtn = document.createElement('button'); 36 49 this.toggleBtn.className = 'sidebar-toggle'; 37 50 this.toggleBtn.innerHTML = '\u25B6'; // > ··· 54 67 * Update the displayed highlights. 55 68 * @param {Array} highlights - Array of {id, from, to, text, note, color} 56 69 */ 57 - update(highlights) { 70 + /** 71 + * Update the displayed highlights. 72 + * @param {Array} highlights - Array of {id, from, to, text, note, color} 73 + * @param {Object} [doc] - CodeMirror doc for line number lookup 74 + */ 75 + update(highlights, doc) { 58 76 this.highlights = highlights || []; 77 + if (doc) this.doc = doc; 59 78 this.countBadge.textContent = String(this.highlights.length); 60 79 this.render(); 61 80 } 62 81 82 + _getLineNumber(pos) { 83 + if (this.doc && pos < this.doc.length) { 84 + try { return this.doc.lineAt(pos).number; } catch (e) { /* ignore */ } 85 + } 86 + return null; 87 + } 88 + 63 89 render() { 64 90 this.content.innerHTML = ''; 65 91 66 92 if (this.highlights.length === 0) { 67 93 const empty = document.createElement('div'); 68 94 empty.className = 'annotations-empty'; 69 - empty.textContent = 'No highlights yet. Select text and use Cmd+H to highlight.'; 95 + empty.textContent = 'No notes yet. Select text or place cursor, then click + or press \u2318\u21E7L.'; 70 96 this.content.appendChild(empty); 71 97 return; 72 98 } ··· 88 114 const textWrap = document.createElement('div'); 89 115 textWrap.className = 'annotation-text-wrap'; 90 116 117 + const lineNum = this._getLineNumber(hl.from); 91 118 const text = document.createElement('div'); 92 119 text.className = 'annotation-text'; 93 - text.textContent = hl.text.length > 80 ? hl.text.substring(0, 80) + '...' : hl.text; 120 + if (hl.note) { 121 + // Has user note — show "Line N" as label, note as primary text 122 + text.textContent = lineNum ? `Line ${lineNum}` : 'Note'; 123 + } else { 124 + // No note — show "Line N — highlight" 125 + text.textContent = lineNum ? `Line ${lineNum} \u2014 highlight` : 'Highlight'; 126 + } 94 127 textWrap.appendChild(text); 95 128 96 129 if (hl.note) { ··· 108 141 109 142 const editBtn = document.createElement('button'); 110 143 editBtn.className = 'annotation-btn'; 111 - editBtn.textContent = 'Note'; 144 + editBtn.textContent = 'Edit'; 112 145 editBtn.title = 'Edit note'; 113 146 editBtn.addEventListener('mousedown', (e) => e.preventDefault()); 114 147 editBtn.addEventListener('click', (e) => { ··· 120 153 const deleteBtn = document.createElement('button'); 121 154 deleteBtn.className = 'annotation-btn annotation-btn-delete'; 122 155 deleteBtn.textContent = '\u00D7'; // x 123 - deleteBtn.title = 'Remove highlight'; 156 + deleteBtn.title = 'Remove note'; 124 157 deleteBtn.addEventListener('mousedown', (e) => e.preventDefault()); 125 158 deleteBtn.addEventListener('click', (e) => { 126 159 e.stopPropagation(); ··· 145 178 } 146 179 147 180 _editNote(highlight) { 148 - const note = prompt('Annotation note:', highlight.note || ''); 181 + const note = prompt('Note:', highlight.note || ''); 149 182 if (note !== null && this.onAnnotationEdit) { 150 183 this.onAnnotationEdit(highlight.id, note); 151 184 } ··· 159 192 this.element.classList.toggle('collapsed', collapsed); 160 193 if (collapsed) { 161 194 this.toggleBtn.innerHTML = '\u25B6'; 162 - this.toggleBtn.title = 'Show Annotations'; 195 + this.toggleBtn.title = 'Show Notes'; 163 196 } else { 164 197 this.toggleBtn.innerHTML = '\u25C0'; 165 - this.toggleBtn.title = 'Hide Annotations'; 198 + this.toggleBtn.title = 'Hide Notes'; 166 199 } 167 200 } 168 201
+21 -10
extensions/editor/editor-layout.js
··· 174 174 this.previewToggleBtn = this._createToggleBtn('Preview', 'Toggle preview (Cmd+Shift+P)', () => this.togglePane('preview')); 175 175 sidebarToggles.appendChild(this.previewToggleBtn); 176 176 177 - this.annotationsToggleBtn = this._createToggleBtn('Notes', 'Toggle annotations (Cmd+Shift+H)', () => this.togglePane('annotations')); 177 + this.annotationsToggleBtn = this._createToggleBtn('Notes', 'Toggle notes (Cmd+Shift+H)', () => this.togglePane('annotations')); 178 178 sidebarToggles.appendChild(this.annotationsToggleBtn); 179 179 180 180 // Pane order cycle button ··· 218 218 onAnnotationClick: (hl) => this._jumpToHighlight(hl), 219 219 onAnnotationDelete: (id) => this._removeHighlight(id), 220 220 onAnnotationEdit: (id, note) => this._updateHighlightNote(id, note), 221 + onAddNote: () => this._addHighlightFromSelection(), 221 222 onToggle: () => this.togglePane('annotations'), 222 223 }); 223 224 ··· 696 697 e.preventDefault(); 697 698 this.togglePane('preview'); 698 699 } 699 - // Cmd+Shift+H: Toggle annotations 700 + // Cmd+Shift+H: Toggle notes pane 700 701 if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'h') { 701 702 e.preventDefault(); 702 703 this.togglePane('annotations'); 703 704 } 704 - // Cmd+H: Add highlight on selected text 705 - if ((e.metaKey || e.ctrlKey) && e.key === 'h' && !e.shiftKey) { 705 + // Cmd+Shift+L: Add note at selection/cursor 706 + if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'l') { 706 707 e.preventDefault(); 707 708 this._addHighlightFromSelection(); 708 709 } ··· 720 721 721 722 const state = this.cmEditor.state; 722 723 const sel = state.selection.main; 723 - if (sel.from === sel.to) return; // No selection 724 724 725 - const text = state.doc.sliceString(sel.from, sel.to); 725 + let from, to; 726 + if (sel.from !== sel.to) { 727 + // Has selection — anchor to selection range 728 + from = sel.from; 729 + to = sel.to; 730 + } else { 731 + // No selection — anchor to current line 732 + const line = state.doc.lineAt(sel.from); 733 + from = line.from; 734 + to = line.to; 735 + } 736 + 726 737 const highlight = { 727 738 id: generateHighlightId(), 728 - from: sel.from, 729 - to: sel.to, 730 - text, 739 + from, 740 + to, 741 + text: '', // Notes don't store selected text 731 742 note: '', 732 743 color: nextHighlightColor(), 733 744 }; ··· 771 782 _syncAnnotationsPane() { 772 783 if (!this.cmEditor || !this.annotationsPane) return; 773 784 const highlights = getHighlights(this.cmEditor.state); 774 - this.annotationsPane.update(highlights); 785 + this.annotationsPane.update(highlights, this.cmEditor.state.doc); 775 786 } 776 787 777 788 _persistHighlights() {
+19
extensions/editor/home.css
··· 391 391 text-align: center; 392 392 } 393 393 394 + .annotation-add-btn { 395 + background: var(--base02); 396 + border: none; 397 + color: var(--base04); 398 + cursor: pointer; 399 + padding: 0 6px; 400 + font-size: 14px; 401 + font-weight: 600; 402 + line-height: 1; 403 + border-radius: 3px; 404 + transition: all 0.1s; 405 + margin-left: auto; 406 + } 407 + 408 + .annotation-add-btn:hover { 409 + background: var(--base03); 410 + color: var(--base05); 411 + } 412 + 394 413 .annotations-content { 395 414 flex: 1; 396 415 overflow-y: auto;