auto-title from first line, click title to rename

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
King Omar 2026-07-19 22:42:37 +10:00
parent 811c60d889
commit 11d86b17e5

View file

@ -205,14 +205,45 @@
position: relative;
}
/* ── Editor title bar ── */
.editor-title-bar {
display: flex;
align-items: center;
padding: 0 14px;
height: 44px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
gap: 10px;
}
#note-title-display {
flex: 1;
font-size: 15px;
font-weight: 700;
color: var(--fg);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: text;
opacity: 0.75;
}
#note-title-display:hover { opacity: 1; }
#note-title-input {
flex: 1;
font-size: 15px;
font-weight: 700;
font-family: 'Roboto Mono', monospace;
border: none;
outline: none;
background: transparent;
color: var(--fg);
display: none;
}
/* floating share + dark toggle */
.float-actions {
position: absolute;
top: 10px;
right: 12px;
display: flex;
gap: 6px;
z-index: 10;
flex-shrink: 0;
}
.float-btn {
padding: 5px 12px;
@ -226,28 +257,14 @@
color: #000;
}
.float-btn:hover { background: var(--accent-hover); }
.float-icon-btn {
background: none;
border: 1px solid var(--border);
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 15px;
color: var(--fg);
}
.float-icon-btn:hover { background: var(--sidebar-hover); }
/* save flash */
.save-flash {
position: absolute;
top: 12px;
left: 50%;
transform: translateX(-50%);
font-size: 11px;
color: var(--muted);
opacity: 0;
transition: opacity 0.4s;
pointer-events: none;
white-space: nowrap;
}
.save-flash.show { opacity: 1; }
@ -415,10 +432,13 @@
<!-- Main editor -->
<div class="main" id="main">
<div class="float-actions">
<div class="editor-title-bar">
<span id="note-title-display">Untitled</span>
<input type="text" id="note-title-input" placeholder="Note title">
<span class="save-flash" id="save-flash">✓ saved</span>
<button class="float-icon-btn" id="dark-toggle-alt" title="Dark mode" style="display:none">🌙</button>
<button class="float-btn" id="share-btn">Share</button>
<div class="float-actions">
<button class="float-btn" id="share-btn">Share</button>
</div>
</div>
<textarea class="notepad" id="notepad" placeholder="Start typing..."></textarea>
@ -499,16 +519,63 @@
let autoSaveTimer = null, syncTimer = null, flashTimer = null;
const $ = id => document.getElementById(id);
const notepad = $('notepad');
const mdView = $('md-view');
const canvasWrap = $('canvas-wrap');
const canvas = $('drawing-canvas');
const drawCtrls = $('draw-controls');
const sidebar = $('sidebar');
const notesList = $('notes-list');
const saveFlash = $('save-flash');
const wordCount = $('word-count');
const syncStatus = $('sync-status');
const notepad = $('notepad');
const mdView = $('md-view');
const canvasWrap = $('canvas-wrap');
const canvas = $('drawing-canvas');
const drawCtrls = $('draw-controls');
const sidebar = $('sidebar');
const notesList = $('notes-list');
const saveFlash = $('save-flash');
const wordCount = $('word-count');
const syncStatus = $('sync-status');
const titleDisplay = $('note-title-display');
const titleInput = $('note-title-input');
// ── Title display & rename ────────────────────────────────────────────────
function setTitleDisplay(title) {
titleDisplay.textContent = title || 'Untitled';
}
titleDisplay.addEventListener('click', () => {
titleDisplay.style.display = 'none';
titleInput.style.display = '';
titleInput.value = notes[currentId]?.title || '';
titleInput.focus();
titleInput.select();
});
titleInput.addEventListener('blur', commitTitleInput);
titleInput.addEventListener('keydown', e => {
if (e.key === 'Enter') titleInput.blur();
if (e.key === 'Escape') { renderTitle(); titleInput.style.display = 'none'; titleDisplay.style.display = ''; }
});
function commitTitleInput() {
if (!notes[currentId]) return;
notes[currentId].title = titleInput.value.trim();
notes[currentId].updatedAt = Date.now();
persist();
renderTitle();
renderList();
titleInput.style.display = 'none';
titleDisplay.style.display = '';
scheduleSync();
}
function renderTitle() {
setTitleDisplay(notes[currentId]?.title || '');
}
function autoTitleFromContent() {
const n = notes[currentId];
if (!n) return;
const firstLine = notepad.value.split('\n')[0].trim();
n.title = firstLine;
n.updatedAt = Date.now();
renderTitle();
renderList();
}
// ── Persist ──────────────────────────────────────────────────────────────
function persist() { localStorage.setItem('noted_notes', JSON.stringify(notes)); }
@ -622,6 +689,7 @@
$('text-color').value = n.textColor || '#000000';
updateWordCount();
renderMd();
renderTitle();
undoStack = []; redoStack = [];
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
@ -664,7 +732,7 @@
clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(() => saveNote(), 3000);
}
notepad.addEventListener('input', () => { updateWordCount(); renderMd(); scheduleAutoSave(); });
notepad.addEventListener('input', () => { updateWordCount(); renderMd(); autoTitleFromContent(); scheduleAutoSave(); });
// ── Markdown ──────────────────────────────────────────────────────────────
function renderMd() { if (isMd) mdView.innerHTML = marked.parse(notepad.value || ''); }