Replace entry dialog with full-page edit view
- Remove Entry_Dialog class; edit/new now navigate to #edit/<id> and #new/... - render_edit_view() builds a full-page layout: header with title input, Write/Preview tabs, and a metadata sidebar - Tab key inserts a literal tab character in the editor - Ctrl+S saves from both title input and textarea - Body/main element uses flex column layout so edit view fills viewport Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
478
public/app.mjs
478
public/app.mjs
@@ -10,8 +10,9 @@ class App_State {
|
||||
this.entry_types = [];
|
||||
this.entries = [];
|
||||
this.active_type = null; // null = all types
|
||||
this.active_view = 'entries'; // 'entries' | 'entry' | 'manage-types'
|
||||
this.active_view = 'entries'; // 'entries' | 'entry' | 'edit' | 'manage-types'
|
||||
this.active_entry_id = null;
|
||||
this.active_edit = null; // { mode: 'new'|'edit', id?, type?, parent_id? }
|
||||
this.filter_status = 'open';
|
||||
this.filter_priority = '';
|
||||
this.filter_tag = '';
|
||||
@@ -160,141 +161,6 @@ class Entry_Picker_Dialog {
|
||||
|
||||
let entry_picker;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry dialog
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class Entry_Dialog {
|
||||
constructor() {
|
||||
const el = clone('t-entry-dialog');
|
||||
document.body.appendChild(el);
|
||||
this.dialog = el;
|
||||
this.form = el.querySelector('form');
|
||||
this.on_save = null;
|
||||
this._editing_id = null;
|
||||
|
||||
// Tab switching
|
||||
const tab_btns = el.querySelectorAll('.tab-btn');
|
||||
const tab_panes = el.querySelectorAll('.tab-pane');
|
||||
for (const btn of tab_btns) {
|
||||
btn.addEventListener('click', () => {
|
||||
const target = btn.dataset.tab;
|
||||
for (const b of tab_btns) { b.classList.toggle('active', b.dataset.tab === target); }
|
||||
for (const p of tab_panes) { p.hidden = p.dataset.tab !== target; }
|
||||
if (target === 'preview') { this._load_preview(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Parent controls
|
||||
el.querySelector('.btn-set-parent').addEventListener('click', () => {
|
||||
const current_id = Number(this.form.elements['parent_id'].value) || null;
|
||||
const self_id = this._editing_id;
|
||||
const excluded = self_id ? new Set([self_id, ...get_descendants(self_id)]) : new Set();
|
||||
if (current_id) { excluded.add(current_id); }
|
||||
entry_picker.open(excluded, (picked_id) => { this._set_parent(picked_id); });
|
||||
});
|
||||
|
||||
el.querySelector('.btn-clear-parent').addEventListener('click', () => { this._set_parent(null); });
|
||||
el.querySelector('.btn-cancel').addEventListener('click', () => el.close());
|
||||
this.form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (this.on_save) { this.on_save(this._read_form()); }
|
||||
el.close();
|
||||
});
|
||||
}
|
||||
|
||||
_set_parent(parent_id) {
|
||||
const hidden = this.form.elements['parent_id'];
|
||||
const display = this.dialog.querySelector('.parent-display');
|
||||
const clear_btn = this.dialog.querySelector('.btn-clear-parent');
|
||||
if (parent_id) {
|
||||
const parent = state.entries.find(e => e.id === parent_id);
|
||||
hidden.value = parent_id;
|
||||
display.textContent = parent ? `#${parent.id} ${parent.title}` : `#${parent_id}`;
|
||||
show(clear_btn);
|
||||
} else {
|
||||
hidden.value = '';
|
||||
display.textContent = '—';
|
||||
hide(clear_btn);
|
||||
}
|
||||
}
|
||||
|
||||
_load_preview() {
|
||||
const text = this.form.elements['body'].value;
|
||||
const preview = this.dialog.querySelector('.preview-content');
|
||||
if (!text.trim()) { preview.innerHTML = '<em style="color:#555">Nothing to preview.</em>'; return; }
|
||||
preview.classList.add('loading');
|
||||
preview.textContent = 'Loading…';
|
||||
render_markdown(text).then(html => {
|
||||
preview.classList.remove('loading');
|
||||
preview.innerHTML = html;
|
||||
}).catch(err => {
|
||||
preview.classList.remove('loading');
|
||||
preview.textContent = 'Error: ' + err.message;
|
||||
});
|
||||
}
|
||||
|
||||
_read_form() {
|
||||
const fd = new FormData(this.form);
|
||||
const tags_raw = fd.get('tags').trim();
|
||||
const parent_id_raw = fd.get('parent_id');
|
||||
return {
|
||||
type: fd.get('type'),
|
||||
title: fd.get('title').trim(),
|
||||
body: fd.get('body').trim(),
|
||||
status: fd.get('status'),
|
||||
priority: fd.get('priority'),
|
||||
tags: tags_raw ? tags_raw.split(',').map(s => s.trim()).filter(Boolean) : [],
|
||||
parent_id: parent_id_raw ? Number(parent_id_raw) : null,
|
||||
};
|
||||
}
|
||||
|
||||
// fixed_type: string — type is pre-set and shown as label; null — show select
|
||||
open(title_text, initial = {}, fixed_type, on_save) {
|
||||
this._editing_id = initial.id ?? null;
|
||||
set_text(this.dialog, '.dialog-title', title_text);
|
||||
|
||||
const type_display = this.dialog.querySelector('.type-display');
|
||||
const type_select = this.dialog.querySelector('.type-select');
|
||||
|
||||
// Always populate select options
|
||||
type_select.innerHTML = '';
|
||||
for (const et of state.entry_types) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = et.id;
|
||||
opt.textContent = et.title;
|
||||
type_select.appendChild(opt);
|
||||
}
|
||||
type_select.value = fixed_type ?? initial.type ?? (state.entry_types[0]?.id ?? '');
|
||||
|
||||
if (fixed_type) {
|
||||
const et = state.entry_types.find(t => t.id === fixed_type);
|
||||
type_display.textContent = et ? et.title : fixed_type;
|
||||
show(type_display);
|
||||
hide(type_select);
|
||||
} else {
|
||||
hide(type_display);
|
||||
show(type_select);
|
||||
}
|
||||
|
||||
this.form.elements['title'].value = initial.title ?? '';
|
||||
this.form.elements['body'].value = initial.body ?? '';
|
||||
this.form.elements['status'].value = initial.status ?? 'open';
|
||||
this.form.elements['priority'].value = initial.priority ?? 'normal';
|
||||
this.form.elements['tags'].value = (initial.tags ?? []).join(', ');
|
||||
this._set_parent(initial.parent_id ?? null);
|
||||
|
||||
// Always open on edit tab
|
||||
for (const b of this.dialog.querySelectorAll('.tab-btn')) { b.classList.toggle('active', b.dataset.tab === 'edit'); }
|
||||
for (const p of this.dialog.querySelectorAll('.tab-pane')) { p.hidden = p.dataset.tab !== 'edit'; }
|
||||
|
||||
this.on_save = on_save;
|
||||
this.dialog.showModal();
|
||||
}
|
||||
}
|
||||
|
||||
let entry_dialog;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry type dialog
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -707,20 +573,43 @@ function apply_hash(hash) {
|
||||
state.active_view = 'manage-types';
|
||||
state.active_type = null;
|
||||
state.active_entry_id = null;
|
||||
state.active_edit = null;
|
||||
} else if (hash.startsWith('type/')) {
|
||||
state.active_view = 'entries';
|
||||
state.active_type = hash.slice(5);
|
||||
state.active_entry_id = null;
|
||||
state.active_edit = null;
|
||||
} else if (/^\d+$/.test(hash)) {
|
||||
state.active_view = 'entry';
|
||||
state.active_entry_id = Number(hash);
|
||||
state.active_edit = null;
|
||||
} else if (hash.startsWith('edit/')) {
|
||||
state.active_view = 'edit';
|
||||
state.active_edit = { mode: 'edit', id: Number(hash.slice(5)) };
|
||||
} else if (hash === 'new' || hash.startsWith('new/')) {
|
||||
state.active_view = 'edit';
|
||||
const rest = hash.length > 4 ? hash.slice(4) : '';
|
||||
const [type_part = '', parent_part = ''] = rest.split('/');
|
||||
state.active_edit = {
|
||||
mode: 'new',
|
||||
type: (type_part && type_part !== '_') ? type_part : null,
|
||||
parent_id: parent_part ? Number(parent_part) : null,
|
||||
};
|
||||
} else {
|
||||
state.active_view = 'entries';
|
||||
state.active_type = null;
|
||||
state.active_entry_id = null;
|
||||
state.active_edit = null;
|
||||
}
|
||||
}
|
||||
|
||||
function navigate_new(type, parent_id) {
|
||||
let hash = 'new';
|
||||
if (type || parent_id) { hash += '/' + (type || '_'); }
|
||||
if (parent_id) { hash += '/' + parent_id; }
|
||||
navigate(hash);
|
||||
}
|
||||
|
||||
function navigate(hash) {
|
||||
history.pushState(null, '', '#' + hash);
|
||||
apply_hash(hash);
|
||||
@@ -859,6 +748,289 @@ function render_entry_detail(container) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Edit view
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function render_edit_view(container) {
|
||||
container.innerHTML = '';
|
||||
|
||||
const ae = state.active_edit;
|
||||
const entry = ae.mode === 'edit' ? state.entries.find(e => e.id === ae.id) : null;
|
||||
if (ae.mode === 'edit' && !entry) {
|
||||
container.textContent = `Entry #${ae.id} not found.`;
|
||||
return;
|
||||
}
|
||||
|
||||
const back_hash = ae.mode === 'edit' ? String(ae.id) : (ae.type ? 'type/' + ae.type : 'all');
|
||||
let current_parent_id = ae.parent_id ?? entry?.parent_id ?? null;
|
||||
|
||||
// View wrapper
|
||||
const view = document.createElement('div');
|
||||
view.className = 'edit-view';
|
||||
|
||||
// --- Header ---
|
||||
const header = document.createElement('div');
|
||||
header.className = 'edit-header';
|
||||
|
||||
const back_btn = document.createElement('a');
|
||||
back_btn.className = 'detail-back';
|
||||
back_btn.href = '#' + back_hash;
|
||||
back_btn.textContent = '← Back';
|
||||
back_btn.addEventListener('click', e => { e.preventDefault(); navigate(back_hash); });
|
||||
header.appendChild(back_btn);
|
||||
|
||||
const title_input = document.createElement('input');
|
||||
title_input.type = 'text';
|
||||
title_input.className = 'edit-title-input';
|
||||
title_input.placeholder = 'Title';
|
||||
title_input.value = entry?.title ?? '';
|
||||
header.appendChild(title_input);
|
||||
|
||||
const save_btn = document.createElement('button');
|
||||
save_btn.className = 'btn-primary';
|
||||
save_btn.textContent = 'Save';
|
||||
const cancel_btn = document.createElement('button');
|
||||
cancel_btn.textContent = 'Cancel';
|
||||
cancel_btn.addEventListener('click', () => navigate(back_hash));
|
||||
header.appendChild(save_btn);
|
||||
header.appendChild(cancel_btn);
|
||||
view.appendChild(header);
|
||||
|
||||
// --- Body area: (tabs + textarea/preview) | sidebar ---
|
||||
const body_area = document.createElement('div');
|
||||
body_area.className = 'edit-body-area';
|
||||
|
||||
// Tab bar
|
||||
const tab_bar = document.createElement('div');
|
||||
tab_bar.className = 'edit-tab-bar';
|
||||
|
||||
const write_tab = document.createElement('button');
|
||||
write_tab.type = 'button';
|
||||
write_tab.textContent = 'Write';
|
||||
write_tab.className = 'edit-tab active';
|
||||
|
||||
const preview_tab = document.createElement('button');
|
||||
preview_tab.type = 'button';
|
||||
preview_tab.textContent = 'Preview';
|
||||
preview_tab.className = 'edit-tab';
|
||||
|
||||
tab_bar.appendChild(write_tab);
|
||||
tab_bar.appendChild(preview_tab);
|
||||
|
||||
// Textarea
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.className = 'edit-textarea';
|
||||
textarea.value = entry?.body ?? '';
|
||||
textarea.placeholder = 'Body (markdown)';
|
||||
|
||||
// Preview pane
|
||||
const preview_el = document.createElement('div');
|
||||
preview_el.className = 'edit-preview markup';
|
||||
preview_el.hidden = true;
|
||||
|
||||
write_tab.addEventListener('click', () => {
|
||||
write_tab.classList.add('active');
|
||||
preview_tab.classList.remove('active');
|
||||
textarea.hidden = false;
|
||||
preview_el.hidden = true;
|
||||
textarea.focus();
|
||||
});
|
||||
|
||||
preview_tab.addEventListener('click', () => {
|
||||
preview_tab.classList.add('active');
|
||||
write_tab.classList.remove('active');
|
||||
textarea.hidden = true;
|
||||
preview_el.hidden = false;
|
||||
const text = textarea.value;
|
||||
if (!text.trim()) {
|
||||
preview_el.innerHTML = '<em style="color:#555">Nothing to preview.</em>';
|
||||
} else {
|
||||
preview_el.textContent = '…';
|
||||
render_markdown(text).then(html => { preview_el.innerHTML = html; });
|
||||
}
|
||||
});
|
||||
|
||||
// Ctrl+S to save
|
||||
title_input.addEventListener('keydown', e => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); save_btn.click(); }
|
||||
});
|
||||
textarea.addEventListener('keydown', e => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); save_btn.click(); }
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
textarea.value = textarea.value.slice(0, start) + '\t' + textarea.value.slice(end);
|
||||
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
||||
}
|
||||
});
|
||||
|
||||
// --- Sidebar ---
|
||||
const sidebar = document.createElement('div');
|
||||
sidebar.className = 'edit-sidebar';
|
||||
|
||||
function sidebar_group(label) {
|
||||
const g = document.createElement('div');
|
||||
g.className = 'edit-sidebar-group';
|
||||
const l = document.createElement('div');
|
||||
l.className = 'edit-sidebar-label';
|
||||
l.textContent = label;
|
||||
g.appendChild(l);
|
||||
return g;
|
||||
}
|
||||
|
||||
// Type
|
||||
const type_group = sidebar_group('Type');
|
||||
let type_el;
|
||||
if (ae.mode === 'edit') {
|
||||
const et = state.entry_types.find(t => t.id === entry.type);
|
||||
type_el = document.createElement('span');
|
||||
type_el.className = 'edit-sidebar-value';
|
||||
type_el.textContent = et ? et.title : entry.type;
|
||||
} else {
|
||||
type_el = document.createElement('select');
|
||||
type_el.className = 'edit-sidebar-select';
|
||||
for (const et of state.entry_types) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = et.id; opt.textContent = et.title;
|
||||
if (et.id === (ae.type ?? state.entry_types[0]?.id)) { opt.selected = true; }
|
||||
type_el.appendChild(opt);
|
||||
}
|
||||
}
|
||||
type_group.appendChild(type_el);
|
||||
sidebar.appendChild(type_group);
|
||||
|
||||
// Status
|
||||
const status_group = sidebar_group('Status');
|
||||
const status_sel = document.createElement('select');
|
||||
status_sel.className = 'edit-sidebar-select';
|
||||
for (const v of ['open', 'deferred', 'done', 'cancelled']) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = v; opt.textContent = v;
|
||||
if (v === (entry?.status ?? 'open')) { opt.selected = true; }
|
||||
status_sel.appendChild(opt);
|
||||
}
|
||||
status_group.appendChild(status_sel);
|
||||
sidebar.appendChild(status_group);
|
||||
|
||||
// Priority
|
||||
const priority_group = sidebar_group('Priority');
|
||||
const priority_sel = document.createElement('select');
|
||||
priority_sel.className = 'edit-sidebar-select';
|
||||
for (const v of ['high', 'normal', 'low']) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = v; opt.textContent = v;
|
||||
if (v === (entry?.priority ?? 'normal')) { opt.selected = true; }
|
||||
priority_sel.appendChild(opt);
|
||||
}
|
||||
priority_group.appendChild(priority_sel);
|
||||
sidebar.appendChild(priority_group);
|
||||
|
||||
// Tags
|
||||
const tags_group = sidebar_group('Tags');
|
||||
const tags_input = document.createElement('input');
|
||||
tags_input.type = 'text';
|
||||
tags_input.className = 'edit-sidebar-input';
|
||||
tags_input.placeholder = 'comma-separated';
|
||||
tags_input.value = (entry?.tags ?? []).join(', ');
|
||||
tags_group.appendChild(tags_input);
|
||||
sidebar.appendChild(tags_group);
|
||||
|
||||
// Parent
|
||||
const parent_group = sidebar_group('Parent');
|
||||
const parent_display = document.createElement('span');
|
||||
parent_display.className = 'edit-sidebar-value';
|
||||
|
||||
function refresh_parent_display() {
|
||||
if (current_parent_id) {
|
||||
const p = state.entries.find(e => e.id === current_parent_id);
|
||||
parent_display.textContent = p ? `#${p.id} ${p.title}` : `#${current_parent_id}`;
|
||||
} else {
|
||||
parent_display.textContent = '—';
|
||||
}
|
||||
}
|
||||
refresh_parent_display();
|
||||
|
||||
const parent_btns = document.createElement('div');
|
||||
parent_btns.className = 'edit-parent-btns';
|
||||
|
||||
const set_parent_btn = document.createElement('button');
|
||||
set_parent_btn.textContent = 'Set…';
|
||||
set_parent_btn.addEventListener('click', () => {
|
||||
const excluded = entry ? new Set([entry.id, ...get_descendants(entry.id)]) : new Set();
|
||||
if (current_parent_id) { excluded.add(current_parent_id); }
|
||||
entry_picker.open(excluded, id => {
|
||||
current_parent_id = id;
|
||||
refresh_parent_display();
|
||||
show(clear_parent_btn);
|
||||
});
|
||||
});
|
||||
|
||||
const clear_parent_btn = document.createElement('button');
|
||||
clear_parent_btn.textContent = '× Clear';
|
||||
if (!current_parent_id) { hide(clear_parent_btn); }
|
||||
clear_parent_btn.addEventListener('click', () => {
|
||||
current_parent_id = null;
|
||||
refresh_parent_display();
|
||||
hide(clear_parent_btn);
|
||||
});
|
||||
|
||||
parent_btns.appendChild(set_parent_btn);
|
||||
parent_btns.appendChild(clear_parent_btn);
|
||||
parent_group.appendChild(parent_display);
|
||||
parent_group.appendChild(parent_btns);
|
||||
sidebar.appendChild(parent_group);
|
||||
|
||||
const editor_pane = document.createElement('div');
|
||||
editor_pane.className = 'edit-editor-pane';
|
||||
editor_pane.appendChild(tab_bar);
|
||||
editor_pane.appendChild(textarea);
|
||||
editor_pane.appendChild(preview_el);
|
||||
|
||||
body_area.appendChild(editor_pane);
|
||||
body_area.appendChild(sidebar);
|
||||
view.appendChild(body_area);
|
||||
container.appendChild(view);
|
||||
|
||||
if (!title_input.value) { title_input.focus(); }
|
||||
else { textarea.focus(); }
|
||||
|
||||
// Save handler
|
||||
save_btn.addEventListener('click', async () => {
|
||||
const data = {
|
||||
type: ae.mode === 'new' ? (type_el.value ?? ae.type) : entry.type,
|
||||
title: title_input.value.trim(),
|
||||
body: textarea.value.trim(),
|
||||
status: status_sel.value,
|
||||
priority: priority_sel.value,
|
||||
tags: tags_input.value.split(',').map(s => s.trim()).filter(Boolean),
|
||||
parent_id: current_parent_id,
|
||||
};
|
||||
if (!data.title) { title_input.focus(); return; }
|
||||
try {
|
||||
save_btn.disabled = true;
|
||||
save_btn.textContent = 'Saving…';
|
||||
if (ae.mode === 'edit') {
|
||||
const { entry: updated } = await api.update_entry(ae.id, data);
|
||||
const idx = state.entries.findIndex(e => e.id === ae.id);
|
||||
if (idx !== -1) { state.entries[idx] = updated; }
|
||||
md_cache.delete(entry.title);
|
||||
md_cache.delete(entry.body);
|
||||
navigate(String(ae.id));
|
||||
} else {
|
||||
const { entry: created } = await api.create_entry(data);
|
||||
state.entries.unshift(created);
|
||||
navigate(String(created.id));
|
||||
}
|
||||
} catch (err) {
|
||||
save_btn.disabled = false;
|
||||
save_btn.textContent = 'Save';
|
||||
alert(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main render
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -866,10 +1038,13 @@ function render_entry_detail(container) {
|
||||
function render() {
|
||||
render_nav();
|
||||
const main = document.getElementById('main');
|
||||
main.classList.toggle('edit-mode', state.active_view === 'edit');
|
||||
if (state.active_view === 'manage-types') {
|
||||
render_manage_types(main);
|
||||
} else if (state.active_view === 'entry') {
|
||||
render_entry_detail(main);
|
||||
} else if (state.active_view === 'edit') {
|
||||
render_edit_view(main);
|
||||
} else {
|
||||
render_entries(main);
|
||||
}
|
||||
@@ -880,35 +1055,11 @@ function render() {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function open_add_dialog(parent_id, type_id) {
|
||||
// If no type given and only one type exists, auto-select it
|
||||
const resolved = type_id ?? (state.entry_types.length === 1 ? state.entry_types[0].id : null);
|
||||
entry_dialog.open('New entry', { parent_id }, resolved, async (data) => {
|
||||
try {
|
||||
const { entry } = await api.create_entry(data);
|
||||
state.entries.unshift(entry);
|
||||
if (parent_id) { state.expanded.add(parent_id); }
|
||||
render();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
});
|
||||
navigate_new(type_id, parent_id);
|
||||
}
|
||||
|
||||
function open_edit_dialog(entry) {
|
||||
entry_dialog.open('Edit entry', entry, entry.type, async (data) => {
|
||||
try {
|
||||
const { entry: updated } = await api.update_entry(entry.id, data);
|
||||
const idx = state.entries.findIndex(e => e.id === entry.id);
|
||||
if (idx !== -1) { state.entries[idx] = updated; }
|
||||
md_cache.delete(entry.title);
|
||||
md_cache.delete(entry.body);
|
||||
md_cache.delete(updated.title);
|
||||
md_cache.delete(updated.body);
|
||||
render();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
});
|
||||
navigate('edit/' + entry.id);
|
||||
}
|
||||
|
||||
function confirm_delete(entry) {
|
||||
@@ -938,8 +1089,7 @@ async function init() {
|
||||
document.body.appendChild(tmpl);
|
||||
}
|
||||
|
||||
entry_picker = new Entry_Picker_Dialog();
|
||||
entry_dialog = new Entry_Dialog();
|
||||
entry_picker = new Entry_Picker_Dialog();
|
||||
entry_type_dialog = new Entry_Type_Dialog();
|
||||
|
||||
const [types_res, entries_res] = await Promise.all([
|
||||
|
||||
176
public/style.css
176
public/style.css
@@ -6,7 +6,10 @@ body {
|
||||
font-size: 14px;
|
||||
background: #1a1a1a;
|
||||
color: #e0e0e0;
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
header {
|
||||
@@ -608,3 +611,174 @@ dialog input:focus, dialog textarea:focus, dialog select:focus {
|
||||
.detail-children .task-list {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Full-page edit mode */
|
||||
header { flex-shrink: 0; }
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
main.edit-mode {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.edit-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.edit-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.6rem 1rem;
|
||||
background: #1e1e1e;
|
||||
border-bottom: 1px solid #333;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.edit-title-input {
|
||||
flex: 1;
|
||||
padding: 0.35rem 0.6rem;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
color: #e0e0e0;
|
||||
font-size: 15px;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.edit-title-input:focus {
|
||||
outline: none;
|
||||
border-color: #5588e0;
|
||||
}
|
||||
|
||||
.edit-body-area {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 240px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.edit-editor-pane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #2a2a2a;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.edit-tab-bar {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border-bottom: 1px solid #2a2a2a;
|
||||
flex-shrink: 0;
|
||||
background: #161616;
|
||||
}
|
||||
|
||||
.edit-tab {
|
||||
padding: 0.4rem 1rem;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.edit-tab:hover { color: #aaa; background: transparent; }
|
||||
.edit-tab.active { color: #e0e0e0; border-bottom-color: #5588e0; background: transparent; }
|
||||
|
||||
.edit-textarea {
|
||||
flex: 1;
|
||||
resize: none;
|
||||
padding: 1rem;
|
||||
background: #111;
|
||||
border: none;
|
||||
color: #d0d0d0;
|
||||
font-size: 13px;
|
||||
font-family: monospace;
|
||||
line-height: 1.6;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.edit-textarea:focus {
|
||||
outline: none;
|
||||
background: #141414;
|
||||
}
|
||||
|
||||
.edit-preview {
|
||||
flex: 1;
|
||||
padding: 1rem 1.25rem;
|
||||
overflow-y: auto;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
background: #111;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
|
||||
.edit-sidebar {
|
||||
overflow-y: auto;
|
||||
padding: 0.75rem;
|
||||
background: #1a1a1a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.edit-sidebar-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.edit-sidebar-label {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.edit-sidebar-value {
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
|
||||
.edit-sidebar-select,
|
||||
.edit-sidebar-input {
|
||||
width: 100%;
|
||||
padding: 0.3rem 0.5rem;
|
||||
background: #242424;
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.edit-sidebar-select:focus,
|
||||
.edit-sidebar-input:focus {
|
||||
outline: none;
|
||||
border-color: #5588e0;
|
||||
}
|
||||
|
||||
.edit-parent-btns {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user