Initial commit — Meridian OS browser desktop demo
This commit is contained in:
82
js/apps/notepad.js
Normal file
82
js/apps/notepad.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// Notepad app — simple text editor with localStorage persistence.
|
||||
|
||||
const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
|
||||
|
||||
export default {
|
||||
id: 'notepad',
|
||||
title: 'Notepad',
|
||||
render(container, api) {
|
||||
const docId = uid();
|
||||
const storageKey = `meridian:notepad:${docId}`;
|
||||
let renamed = false;
|
||||
let saveTimer = null;
|
||||
|
||||
// Load saved content
|
||||
const saved = localStorage.getItem(storageKey);
|
||||
let docTitle = 'Untitled';
|
||||
let content = saved !== null ? saved : '';
|
||||
|
||||
// Build UI
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.className = 'app-toolbar';
|
||||
|
||||
const titleEl = document.createElement('span');
|
||||
titleEl.className = 'doc-title';
|
||||
titleEl.textContent = docTitle;
|
||||
titleEl.title = 'Click to rename';
|
||||
titleEl.style.cursor = 'pointer';
|
||||
titleEl.addEventListener('click', () => {
|
||||
const newName = prompt('Rename document:', titleEl.textContent);
|
||||
if (newName !== null && newName.trim()) {
|
||||
docTitle = newName.trim();
|
||||
titleEl.textContent = docTitle;
|
||||
renamed = true;
|
||||
api.setTitle(docTitle);
|
||||
}
|
||||
});
|
||||
|
||||
const saveBtn = document.createElement('button');
|
||||
saveBtn.className = 'btn-amber';
|
||||
saveBtn.textContent = 'Save';
|
||||
saveBtn.addEventListener('click', () => {
|
||||
saveContent();
|
||||
saveBtn.textContent = 'Saved!';
|
||||
setTimeout(() => { saveBtn.textContent = 'Save'; }, 1200);
|
||||
});
|
||||
|
||||
toolbar.appendChild(titleEl);
|
||||
toolbar.appendChild(saveBtn);
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = content;
|
||||
|
||||
const statusbar = document.createElement('div');
|
||||
statusbar.className = 'app-status';
|
||||
|
||||
function updateCounts() {
|
||||
const text = textarea.value;
|
||||
const chars = text.length;
|
||||
const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
|
||||
statusbar.textContent = `${words} word${words !== 1 ? 's' : ''} · ${chars} character${chars !== 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
textarea.addEventListener('input', () => {
|
||||
updateCounts();
|
||||
api.bus.emit('text:typed', { chars: textarea.value.length });
|
||||
// Auto-save after 1s of inactivity
|
||||
clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(saveContent, 1000);
|
||||
});
|
||||
|
||||
function saveContent() {
|
||||
localStorage.setItem(storageKey, textarea.value);
|
||||
}
|
||||
|
||||
container.style.cssText = 'height:100%;display:flex;flex-direction:column;';
|
||||
container.appendChild(toolbar);
|
||||
container.appendChild(textarea);
|
||||
container.appendChild(statusbar);
|
||||
|
||||
updateCounts();
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user