1/** 2 * CitrusOS sticky notes â local document store (browser only, no sync). 3 * Used by the Sticky Notes folder explorer and note widgets. 4 */ 5(function (global) { 6 'use strict'; 7 8 var STORAGE_KEY = 'citrusos_sticky_notes_v1'; 9 var DEFAULT_TITLE = 'Untitled note'; 10 11 function uid() { 12 return 'note-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 7); 13 } 14 15 function readStore() { 16 try { 17 var raw = localStorage.getItem(STORAGE_KEY); 18 if (!raw) return { version: 1, notes: [] }; 19 var data = JSON.parse(raw); 20 if (!data || !Array.isArray(data.notes)) return { version: 1, notes: [] }; 21 return data; 22 } catch (e) { 23 return { version: 1, notes: [] }; 24 } 25 } 26 27 function writeStore(data) { 28 try { 29 localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); 30 } catch (e) { /* ignore */ } 31 } 32 33 function emitChanged() { 34 try { 35 global.dispatchEvent(new CustomEvent('pupwife:citrus-sticky-notes-changed')); 36 } catch (e) { /* ignore */ } 37 } 38 39 function deriveTitle(text, fallback) { 40 var fb = fallback || DEFAULT_TITLE; 41 if (typeof text !== 'string') return fb; 42 var line = text.split('\n')[0].replace(/\s+/g, ' ').trim(); 43 if (!line) return fb; 44 if (line.length > 48) line = line.slice(0, 45) + '...'; 45 return line; 46 } 47 48 function normalizeNote(note) { 49 if (!note || !note.id) return null; 50 return { 51 id: note.id, 52 title: note.title || DEFAULT_TITLE, 53 text: typeof note.text === 'string' ? note.text : '', 54 createdAt: note.createdAt || new Date().toISOString(), 55 updatedAt: note.updatedAt || note.createdAt || new Date().toISOString() 56 }; 57 } 58 59 function listNotes() { 60 var store = readStore(); 61 return store.notes.map(normalizeNote).filter(Boolean).sort(function (a, b) { 62 return (b.updatedAt || '').localeCompare(a.updatedAt || ''); 63 }); 64 } 65 66 function getNote(id) { 67 if (!id) return null; 68 var store = readStore(); 69 for (var i = 0; i < store.notes.length; i++) { 70 if (store.notes[i].id === id) { 71 return normalizeNote(store.notes[i]); 72 } 73 } 74 return null; 75 } 76 77 function createNote(opts) { 78 opts = opts || {}; 79 var now = new Date().toISOString(); 80 var text = typeof opts.text === 'string' ? opts.text : ''; 81 var note = { 82 id: uid(), 83 title: deriveTitle(text, opts.title || DEFAULT_TITLE), 84 text: text, 85 createdAt: now, 86 updatedAt: now 87 }; 88 var store = readStore(); 89 store.notes.unshift(note); 90 writeStore(store); 91 emitChanged(); 92 return normalizeNote(note); 93 } 94 95 function updateNote(id, patch) { 96 if (!id) return null; 97 var store = readStore(); 98 var updated = null; 99 for (var i = 0; i < store.notes.length; i++) { 100 if (store.notes[i].id !== id) continue; 101 var next = Object.assign({}, store.notes[i]); 102 if (patch && typeof patch.text === 'string') { 103 next.text = patch.text; 104 next.title = deriveTitle(next.text, next.title || DEFAULT_TITLE); 105 } 106 if (patch && typeof patch.title === 'string' && patch.title.trim()) { 107 next.title = patch.title.trim(); 108 } 109 next.updatedAt = new Date().toISOString(); 110 store.notes[i] = next; 111 updated = normalizeNote(next); 112 break; 113 } 114 if (updated) { 115 writeStore(store); 116 emitChanged(); 117 } 118 return updated; 119 } 120 121 function deleteNote(id) { 122 if (!id) return false; 123 var store = readStore(); 124 var nextNotes = store.notes.filter(function (n) { return n.id !== id; }); 125 if (nextNotes.length === store.notes.length) return false; 126 store.notes = nextNotes; 127 writeStore(store); 128 emitChanged(); 129 return true; 130 } 131 132 global.CitrusStickyNotes = { 133 listNotes: listNotes, 134 getNote: getNote, 135 createNote: createNote, 136 updateNote: updateNote, 137 deleteNote: deleteNote, 138 deriveTitle: deriveTitle, 139 DEFAULT_TITLE: DEFAULT_TITLE 140 }; 141})(typeof window !== 'undefined' ? window : this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.