Add guide metadata, redesign PDF cover, paginate steps per-page, and run exports in a helper process
Lets users record author/co-authors/organization for a guide via a new "Guide information…" dialog; this metadata renders below the title on the PDF cover (title now sits above the accent rule). PDF export also paginates so each step fits its own page where possible, keeps a step's title/image/lead-in together, and forces the next step onto a fresh page after an oversized step overflows. Exports now run in a forked helper process so large guides no longer freeze the UI. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
@@ -317,6 +317,7 @@ class StepForgeApp {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
contextMenu(rect.left, rect.bottom + 4, [
|
||||
{ label: 'Rename guide…', action: () => this.renameGuide() },
|
||||
{ label: 'Guide information…', action: () => this.editor.openGuideInfo() },
|
||||
{ label: 'Guide placeholders…', action: () => this.editor.openGuidePlaceholders() },
|
||||
{ label: 'Backups & snapshots…', action: () => this.editor.openBackupsDialog() },
|
||||
{ label: guide && guide.linkedSource ? 'Linked guide…' : 'Linked guide (not linked)', action: () => this.editor.openLinkedGuide() },
|
||||
|
||||
@@ -639,6 +639,44 @@ function showPlaceholdersDialog({ title = 'Placeholders', hint = '', values = {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional document metadata (author, co-authors, organization) shown at the
|
||||
* top of the guide, below the title, and surfaced on the PDF cover page.
|
||||
*/
|
||||
function showGuideInfoDialog({ values = {}, onSave } = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const authorInput = makeInput(values.author || '', 'text', { placeholder: 'e.g. Jane Doe' });
|
||||
const coAuthorsInput = makeInput(values.coAuthors || '', 'text', { placeholder: 'e.g. Alex Lee, Sam Patel' });
|
||||
const organizationInput = makeInput(values.organization || '', 'text', { placeholder: 'e.g. Acme Corp' });
|
||||
|
||||
const { close } = openModal({
|
||||
title: 'Guide information',
|
||||
body: el('div', {},
|
||||
el('div.muted', { style: { marginBottom: '10px' } },
|
||||
'All fields are optional. They appear below the title on the guide and on the PDF cover page.'),
|
||||
labeledRow('Author', authorInput),
|
||||
labeledRow('Co-authors', coAuthorsInput),
|
||||
labeledRow('Organization', organizationInput),
|
||||
),
|
||||
footer: [
|
||||
el('button', { onClick: () => { close(); resolve(false); } }, 'Cancel'),
|
||||
el('button.primary', {
|
||||
onClick: async () => {
|
||||
await onSave?.({
|
||||
author: authorInput.value.trim(),
|
||||
coAuthors: coAuthorsInput.value.trim(),
|
||||
organization: organizationInput.value.trim(),
|
||||
});
|
||||
close();
|
||||
resolve(true);
|
||||
},
|
||||
}, 'Save'),
|
||||
],
|
||||
onClose: () => resolve(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const SHORTCUTS = [
|
||||
['Capture & steps', [
|
||||
['Ctrl+S', 'Save (writes linked archive when guide is linked)'],
|
||||
@@ -727,6 +765,7 @@ window.StepForgeDialogs = {
|
||||
showInfoDialog,
|
||||
showBackupsDialog,
|
||||
showPlaceholdersDialog,
|
||||
showGuideInfoDialog,
|
||||
showShortcutsDialog,
|
||||
showTemplateManager,
|
||||
showRecordingReminder,
|
||||
|
||||
@@ -1571,6 +1571,18 @@ class GuideEditor {
|
||||
});
|
||||
}
|
||||
|
||||
async openGuideInfo() {
|
||||
if (!this.guide) return;
|
||||
await dialogs.showGuideInfoDialog({
|
||||
values: this.guide.metadata || {},
|
||||
onSave: async (metadata) => {
|
||||
this.guide.metadata = metadata;
|
||||
await api.guide.save({ guide: this.guide });
|
||||
this.onToast('Guide information saved.');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
openShortcutsHelp() {
|
||||
dialogs.showShortcutsDialog();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user