fd0858

feat: expanded note toolbar (U, S, H, lists, rule) + 250px editor body (v1.4.55)

time
2026-04-23 13:49 UTC · 6 months ago
machine
koh2
parent
fb33e4
5 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/popup.css+39 −13src/popup/views/NoteEditor.tsx+56 −20
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "1.4.54",4+  "version": "1.4.55",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "1.4.54",4+  "version": "1.4.55",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "1.4.54",3+  "version": "1.4.55",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/popup/styles/popup.css+39 −13
@@ -2324,17 +2324,12 @@23242324   border-color: var(--peach-t3);23252325 }23262326 2327-.peach-note-tool-bold {2328-  font-weight: 700;2329-  font-family: Inter, system-ui, sans-serif;2330-  font-size: 11px;2327+.peach-note-tool-icon {2328+  padding: 3px 7px;2329+  display: inline-flex;2330+  align-items: center;2331+  justify-content: center;23312332 }2332-.peach-note-tool-italic {2333-  font-style: italic;2334-  font-family: "Cormorant Garamond", serif;2335-  font-size: 14px;2336-  padding: 1px 7px;2337-}23382333 23392334 .peach-note-tool-divider {23402335   width: 1px;@@ -2375,11 +2370,42 @@23752370   padding: 5px 10px 4px;23762371 }23772372 .peach-note-editor-body--inline {2378-  min-height: 140px;2379-  max-height: 200px;2373+  min-height: 250px;23802374   font-size: 15px;2381-  padding: 10px 14px 12px;2375+  padding: 10px 14px 14px;23822376   background: var(--peach-t4);2377+}2378+2379+/* Rich content element styles (editor + display) */2380+.peach-note-editor-body h2,2381+.peach-note-content h2 {2382+  font-family: "Cormorant Garamond", serif;2383+  font-size: 20px;2384+  font-weight: 600;2385+  font-style: italic;2386+  color: var(--peach-t0);2387+  margin: 6px 0 2px;2388+  line-height: 1.3;2389+}2390+.peach-note-editor-body ul,2391+.peach-note-content ul {2392+  padding-left: 18px;2393+  margin: 3px 0;2394+  list-style: disc;2395+}2396+.peach-note-editor-body ol,2397+.peach-note-content ol {2398+  padding-left: 18px;2399+  margin: 3px 0;2400+  list-style: decimal;2401+}2402+.peach-note-editor-body li,2403+.peach-note-content li { margin: 2px 0; }2404+.peach-note-editor-body hr,2405+.peach-note-content hr {2406+  border: none;2407+  border-top: 1px solid var(--peach-t3);2408+  margin: 10px 0;23832409 }23842410 23852411 /* font size elements from execCommand */

view file

src/popup/views/NoteEditor.tsx+56 −20
@@ -9,31 +9,67 @@99     .replace(/\n/g, "<br>");1010 }1111 12-function Toolbar(props: { exec: (cmd: string, val?: string) => void }) {12+const BoldIcon = () => <span style="font-family:Inter,system-ui,sans-serif;font-weight:700;font-size:11px;">B</span>;13+const ItalicIcon = () => <span style="font-family:'Cormorant Garamond',serif;font-style:italic;font-size:13px;">I</span>;14+const UnderlineIcon = () => <span style="text-decoration:underline;font-size:10px;font-family:'IBM Plex Mono',monospace;">U</span>;15+const StrikeIcon = () => <span style="text-decoration:line-through;font-size:10px;font-family:'IBM Plex Mono',monospace;">S</span>;16+const HeadingIcon = () => <span style="font-family:Inter,system-ui,sans-serif;font-weight:600;font-size:10px;letter-spacing:0.03em;">H</span>;17+18+const BulletIcon = () => (19+  <svg width="12" height="11" viewBox="0 0 12 11" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round">20+    <circle cx="1.5" cy="2.5" r="1" fill="currentColor" stroke="none"/>21+    <line x1="4" y1="2.5" x2="11" y2="2.5"/>22+    <circle cx="1.5" cy="6" r="1" fill="currentColor" stroke="none"/>23+    <line x1="4" y1="6" x2="11" y2="6"/>24+    <circle cx="1.5" cy="9.5" r="1" fill="currentColor" stroke="none"/>25+    <line x1="4" y1="9.5" x2="11" y2="9.5"/>26+  </svg>27+);28+29+const NumberedIcon = () => (30+  <svg width="12" height="11" viewBox="0 0 12 11" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round">31+    <text x="0" y="4" font-size="4" fill="currentColor" stroke="none" font-family="monospace">1.</text>32+    <line x1="4" y1="2.5" x2="11" y2="2.5"/>33+    <text x="0" y="7.5" font-size="4" fill="currentColor" stroke="none" font-family="monospace">2.</text>34+    <line x1="4" y1="6" x2="11" y2="6"/>35+    <text x="0" y="11" font-size="4" fill="currentColor" stroke="none" font-family="monospace">3.</text>36+    <line x1="4" y1="9.5" x2="11" y2="9.5"/>37+  </svg>38+);39+40+const RuleIcon = () => (41+  <svg width="12" height="8" viewBox="0 0 12 8" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round">42+    <line x1="0" y1="4" x2="12" y2="4"/>43+    <line x1="2" y1="1.5" x2="2" y2="6.5" stroke-width="0.6" opacity="0.5"/>44+    <line x1="10" y1="1.5" x2="10" y2="6.5" stroke-width="0.6" opacity="0.5"/>45+  </svg>46+);47+48+type ExecFn = (cmd: string, val?: string) => void;49+50+function Toolbar(props: { exec: ExecFn }) {1351   const e = props.exec;52+  function toggleHeading() {53+    const val = document.queryCommandValue("formatBlock").toLowerCase();54+    e("formatBlock", val === "h2" ? "p" : "h2");55+  }1456   return (1557     <div class="peach-note-toolbar">16-      <button class="peach-note-tool-btn peach-note-tool-bold"17-        onMouseDown={(ev) => { ev.preventDefault(); e("bold"); }}18-        title="Bold (⌘B)">B</button>19-      <button class="peach-note-tool-btn peach-note-tool-italic"20-        onMouseDown={(ev) => { ev.preventDefault(); e("italic"); }}21-        title="Italic (⌘I)">I</button>58+      <button class="peach-note-tool-btn" onMouseDown={(ev) => { ev.preventDefault(); e("bold"); }} title="Bold (⌘B)"><BoldIcon /></button>59+      <button class="peach-note-tool-btn" onMouseDown={(ev) => { ev.preventDefault(); e("italic"); }} title="Italic (⌘I)"><ItalicIcon /></button>60+      <button class="peach-note-tool-btn" onMouseDown={(ev) => { ev.preventDefault(); e("underline"); }} title="Underline (⌘U)"><UnderlineIcon /></button>61+      <button class="peach-note-tool-btn" onMouseDown={(ev) => { ev.preventDefault(); e("strikeThrough"); }} title="Strikethrough"><StrikeIcon /></button>2262       <div class="peach-note-tool-divider" />23-      <button class="peach-note-tool-btn"24-        onMouseDown={(ev) => { ev.preventDefault(); e("fontSize", "1"); }}25-        title="Small">sm</button>26-      <button class="peach-note-tool-btn"27-        onMouseDown={(ev) => { ev.preventDefault(); e("fontSize", "3"); }}28-        title="Normal">md</button>29-      <button class="peach-note-tool-btn"30-        onMouseDown={(ev) => { ev.preventDefault(); e("fontSize", "5"); }}31-        title="Large">lg</button>63+      <button class="peach-note-tool-btn" onMouseDown={(ev) => { ev.preventDefault(); toggleHeading(); }} title="Heading"><HeadingIcon /></button>64+      <button class="peach-note-tool-btn peach-note-tool-icon" onMouseDown={(ev) => { ev.preventDefault(); e("insertUnorderedList"); }} title="Bullet list"><BulletIcon /></button>65+      <button class="peach-note-tool-btn peach-note-tool-icon" onMouseDown={(ev) => { ev.preventDefault(); e("insertOrderedList"); }} title="Numbered list"><NumberedIcon /></button>66+      <div class="peach-note-tool-divider" />67+      <button class="peach-note-tool-btn peach-note-tool-icon" onMouseDown={(ev) => { ev.preventDefault(); e("insertHorizontalRule"); }} title="Divider"><RuleIcon /></button>3268     </div>3369   );3470 }3571 36-/* ── Full-popup overlay editor (Entry.tsx — editing existing notes) ── */72+/* ── Full-popup overlay editor (Entry.tsx) ── */3773 3874 interface NoteEditorProps {3975   initialContent: string;@@ -68,6 +104,7 @@68104     const mod = e.metaKey || e.ctrlKey;69105     if (mod && e.key === "b") { e.preventDefault(); exec("bold"); return; }70106     if (mod && e.key === "i") { e.preventDefault(); exec("italic"); return; }107+    if (mod && e.key === "u") { e.preventDefault(); exec("underline"); return; }71108     if (e.key === "Escape") { e.preventDefault(); props.onCancel(); }72109   }73110 @@ -88,9 +125,7 @@88125           {props.saving ? "saving…" : "save"}89126         </button>90127       </div>91-92128       <Toolbar exec={exec} />93-94129       <div ref={editorRef} class="peach-note-editor-body"95130         contenteditable="true" onKeyDown={handleKeyDown}96131         aria-multiline="true" aria-label="Note content" />@@ -98,7 +133,7 @@98133   );99134 }100135 101-/* ── Inline editor (Add.tsx — embedded in the new-entry form) ── */136+/* ── Inline editor (Add.tsx) ── */102137 103138 interface NoteEditorInlineProps {104139   initialContent: string;@@ -123,6 +158,7 @@123158     const mod = e.metaKey || e.ctrlKey;124159     if (mod && e.key === "b") { e.preventDefault(); exec("bold"); }125160     if (mod && e.key === "i") { e.preventDefault(); exec("italic"); }161+    if (mod && e.key === "u") { e.preventDefault(); exec("underline"); }126162   }127163 128164   return (

view file