1import { onMount } from "solid-js";23interface NoteEditorProps {4 initialContent: string;5 saving: boolean;6 onSave: (html: string) => Promise<void>;7 onCancel: () => void;8}910function toHtml(raw: string): string {11 if (/<\/?[a-z]/i.test(raw)) return raw;12 return raw13 .replace(/&/g, "&")14 .replace(/</g, "<")15 .replace(/>/g, ">")16 .replace(/\n/g, "<br>");17}1819export function NoteEditor(props: NoteEditorProps) {20 let editorRef: HTMLDivElement | undefined;2122 onMount(() => {23 if (!editorRef) return;24 editorRef.innerHTML = toHtml(props.initialContent);25 editorRef.focus();26 const sel = window.getSelection();27 if (sel) {28 const range = document.createRange();29 range.selectNodeContents(editorRef);30 range.collapse(false);31 sel.removeAllRanges();32 sel.addRange(range);33 }34 });3536 function exec(cmd: string, value?: string) {37 document.execCommand(cmd, false, value);38 editorRef?.focus();39 }4041 function handleKeyDown(e: KeyboardEvent) {42 const mod = e.metaKey || e.ctrlKey;43 if (mod && e.key === "b") { e.preventDefault(); exec("bold"); return; }44 if (mod && e.key === "i") { e.preventDefault(); exec("italic"); return; }45 if (e.key === "Escape") { e.preventDefault(); props.onCancel(); }46 }4748 return (49 <div class="peach-note-editor">50 <div class="peach-note-editor-header">51 <button52 class="peach-note-editor-back"53 onClick={props.onCancel}54 disabled={props.saving}55 aria-label="Cancel"56 >57 <svg width="7" height="12" viewBox="0 0 7 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round">58 <path d="M6 1L1 6L6 11" />59 </svg>60 </button>61 <span class="peach-note-editor-label">note</span>62 <button63 class="peach-note-editor-save-btn"64 onClick={() => void props.onSave(editorRef?.innerHTML ?? "")}65 disabled={props.saving}66 >67 {props.saving ? "saving…" : "save"}68 </button>69 </div>7071 <div class="peach-note-toolbar">72 <button73 class="peach-note-tool-btn peach-note-tool-bold"74 onMouseDown={(e) => { e.preventDefault(); exec("bold"); }}75 aria-label="Bold"76 title="Bold (⌘B)"77 >B</button>78 <button79 class="peach-note-tool-btn peach-note-tool-italic"80 onMouseDown={(e) => { e.preventDefault(); exec("italic"); }}81 aria-label="Italic"82 title="Italic (⌘I)"83 >I</button>84 <div class="peach-note-tool-divider" />85 <button86 class="peach-note-tool-btn"87 onMouseDown={(e) => { e.preventDefault(); exec("fontSize", "1"); }}88 title="Small"89 >sm</button>90 <button91 class="peach-note-tool-btn"92 onMouseDown={(e) => { e.preventDefault(); exec("fontSize", "3"); }}93 title="Normal"94 >md</button>95 <button96 class="peach-note-tool-btn"97 onMouseDown={(e) => { e.preventDefault(); exec("fontSize", "5"); }}98 title="Large"99 >lg</button>100 </div>101102 <div103 ref={editorRef}104 class="peach-note-editor-body"105 contenteditable="true"106 onKeyDown={handleKeyDown}107 aria-multiline="true"108 aria-label="Note content"109 />110 </div>111 );112}