Basking: live preview editing

Pages open in Live mode, where every line is rendered except the one being
edited, with Edit (plain markdown) and View (fully rendered) beside it.
Arrow keys, Backspace and Delete move between lines, code blocks are edited
as a whole, and edits are spliced back into the one text the rest of the
editor already saves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-31 22:59:14 -06:00
1 parent 98f7730913
commit cec2702260
4 files changed
+179 -23

No files matched your search

+7 -7
View File
@@ -220,9 +220,9 @@ function pageView({ id, date, parent = '' }) {
function drawActions() {
const have = !!(note && note.id);
const seg = h('div', { class: 'seg', role: 'group', 'aria-label': 'View' },
h('button', { class: 'seg-b' + (ed.mode === 'write' ? ' on' : ''), 'aria-pressed': String(ed.mode === 'write'), onclick: () => ed.setMode('write') }, 'Write'),
h('button', { class: 'seg-b' + (ed.mode === 'read' ? ' on' : ''), 'aria-pressed': String(ed.mode === 'read'), onclick: () => ed.setMode('read') }, 'Read'));
const seg = h('div', { class: 'seg', role: 'group', 'aria-label': 'How the page is shown' },
[['live', 'Live', 'Rendered, except the line you are on'], ['write', 'Edit', 'Plain markdown'], ['read', 'View', 'The finished page']].map(([m, label, tip]) =>
h('button', { class: 'seg-b' + (ed.mode === m ? ' on' : ''), 'aria-pressed': String(ed.mode === m), title: tip, onclick: () => ed.setMode(m) }, label)));
fill(actions, ed.status, h('span', { class: 'grow' }), seg,
date ? [btn('Insert time', 'clock', () => ed.stamp())] : null,
btn(note?.pinned ? 'Unpin' : 'Pin', 'pin', act(async () => {
@@ -259,10 +259,10 @@ function pageView({ id, date, parent = '' }) {
},
});
ed.onMode = () => drawActions();
view.toggleMode = () => ed.setMode(ed.mode === 'write' ? 'read' : 'write');
view.toggleMode = () => ed.setMode(ed.mode === 'read' ? ed.lastEdit : 'read');
if (n?.backlinks) backlinks = n.backlinks;
const pref = store.get('mode');
const start = n && n.body.trim() ? pref || 'read' : 'write'; // an empty page is for writing
const start = pref === 'read' && !(n && n.body.trim()) ? 'live' : pref || 'live'; // an empty page is for writing
const title = date ? null : h('div', { class: 'title-wrap' }, ed.titleEl, ed.titleErr);
fill(body, ed.notice, title, date ? h('h1', { class: 'day-title' }, ed.titleEl.value) : null, ed.wrap, tagsRow, asideBox, meta);
ed.setMode(start);
@@ -272,7 +272,7 @@ function pageView({ id, date, parent = '' }) {
const set = ed.setMode; ed.setMode = m => { store.set('mode', m); set(m); };
if (!n) setTimeout(() => ed.titleEl.focus());
else if (!date && !n.body.trim() && !n.title) setTimeout(() => ed.titleEl.focus());
else if (date && !n.body.trim() && start === 'write') setTimeout(() => ed.ta.focus());
else if (date && !n.body.trim() && start !== 'read') setTimeout(() => ed.focusBody(true));
}
function trashedView(n) {
@@ -553,7 +553,7 @@ function settingsPage() {
btn('Download all pages', 'download', () => { location.href = '/api/export'; })),
card('Shortcuts', h('ul', { class: 'keys' },
row('Ctrl K', 'Search pages, or make a new one'), row('c', 'New page'), row('n', 'Jot a quick note'),
row('Ctrl E', 'Switch between Write and Read'), row('/', 'In a page: commands. Anywhere else: search'),
row('Ctrl E', 'Switch between editing and View'), row('/', 'In a page: commands. Anywhere else: search'),
row('[[', 'In a page: link to another page'), row('Ctrl B, Ctrl I', 'Bold, italic'), row('Tab', 'In a list: indent'))),
card('Privacy', h('p', { class: 'dim' }, 'Pages are stored as plain markdown files on the machine running Basking, under a folder only you map to. They are not encrypted on disk, so anyone who can read that machine can read them.'))),
};