docs: each showcase shot opens the tab its feature lives in
A shot names a ribbon tab and, optionally, where the caret goes: the tables one lands in a cell, so the contextual Table Layout tab is up. The floating table toolbar is hidden while shooting — it covered the table the ribbon is already showing. `data-tab` on the tab buttons is what the script clicks.
No files matched your search
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 334 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 238 KiB |
@@ -79,7 +79,7 @@ export async function build(): Promise<Showcase> {
|
||||
props: { title: 'Alice’s Adventures in Wonderland', subject: '', author: 'Lewis Carroll', keywords: '', description: '' },
|
||||
shots: [
|
||||
{ file: 'book', page: 3 },
|
||||
{ file: 'book-spread', page: 3, columns: 2 },
|
||||
{ file: 'book-spread', page: 3, columns: 2, tab: 'layout' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -93,6 +93,8 @@ export type Shot = {
|
||||
file: string; page?: number; at?: string; zoom?: number; theme?: 'light' | 'dark';
|
||||
/** Pages side by side (View ▸ page columns). */
|
||||
columns?: number; markup?: boolean; spelling?: boolean;
|
||||
/** The ribbon tab to open, and where to put the caret (first match in view). */
|
||||
tab?: string; caret?: string;
|
||||
};
|
||||
|
||||
export type Showcase = {
|
||||
|
||||
@@ -112,8 +112,8 @@ export async function build(): Promise<Showcase> {
|
||||
hyphenate: true,
|
||||
props: { title: 'Newsletter 3/2026', subject: 'Eden Astronomy Society', author: 'The committee', keywords: 'astronomy, society', description: '' },
|
||||
shots: [
|
||||
{ file: 'newsletter', page: 1 },
|
||||
{ file: 'newsletter-tables', page: 2 },
|
||||
{ file: 'newsletter', page: 1, tab: 'layout' },
|
||||
{ file: 'newsletter-tables', page: 2, tab: 'tableLayout', caret: '.tiptap table td' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -74,11 +74,13 @@ try {
|
||||
'edentext-markup-mode': shot.markup ? 'all' : 'none',
|
||||
'edentext-page-columns': String(shot.columns ?? 1),
|
||||
});
|
||||
// Unknown names would wear squiggles in every shot; the review shot keeps them.
|
||||
if (!shot.spelling) await page.addStyleTag({ content: '.pm-spell-error { text-decoration: none !important; background: none !important; }' });
|
||||
// Unknown names would wear squiggles in every shot; the review shot keeps them. The
|
||||
// floating table toolbar would cover the table the ribbon is already showing.
|
||||
await page.addStyleTag({ content: `.table-toolbar { display: none !important; }
|
||||
${shot.spelling ? '' : '.pm-spell-error { text-decoration: none !important; background: none !important; }'}` });
|
||||
// One .paper holds every page; page n starts n-1 page cycles down it. The caret
|
||||
// goes to the first paragraph in view, so the ribbon shows the body's formatting.
|
||||
await page.evaluate(({ page: n, at }) => {
|
||||
await page.evaluate(({ page: n, at, caret }) => {
|
||||
const editor = document.querySelector('.editor');
|
||||
const paper = document.querySelector('.paper');
|
||||
const cycle = parseFloat(getComputedStyle(paper).getPropertyValue('--page-height')) + 20;
|
||||
@@ -91,10 +93,15 @@ try {
|
||||
editor.scrollTop = top + (n - 1) * cycle * zoom - 12;
|
||||
const limit = editor.getBoundingClientRect().top + 40;
|
||||
const plain = (p) => [...p.querySelectorAll('*')].every((e) => /^(SPAN|STRONG|EM|B|I|U|S|A|SUP|SUB|BR)$/.test(e.tagName));
|
||||
const first = [...document.querySelectorAll('.tiptap > p')].find((p) => p.getBoundingClientRect().top > limit && p.textContent.trim() && plain(p));
|
||||
// A `caret` selector puts it in that element instead — a table cell brings up the
|
||||
// contextual tabs, so the ribbon shows what the shot is about.
|
||||
const first = caret
|
||||
? [...document.querySelectorAll(caret)].find((e) => e.getBoundingClientRect().top > limit)
|
||||
: [...document.querySelectorAll('.tiptap > p')].find((p) => p.getBoundingClientRect().top > limit && p.textContent.trim() && plain(p));
|
||||
const view = document.querySelector('.tiptap').editor;
|
||||
if (first) view.commands.focus(view.view.posAtDOM(first, 0) + 1, { scrollIntoView: false });
|
||||
}, { page: shot.page ?? 1, at: shot.at });
|
||||
}, { page: shot.page ?? 1, at: shot.at, caret: shot.caret });
|
||||
if (shot.tab) await page.click(`.ribbon-tab[data-tab="${shot.tab}"]`);
|
||||
await page.waitForTimeout(500);
|
||||
await page.screenshot({ path: join(OUT, `${shot.file}.png`) });
|
||||
console.log(` ${shot.file}.png`);
|
||||
|
||||
@@ -186,12 +186,12 @@ export function build(review = false): Showcase {
|
||||
decor: review ? { background: null, border: null,
|
||||
watermark: { text: 'DRAFT', font: 'Liberation Sans', color: '#C8C8C8', angle: 45, transparency: 60 } } : undefined,
|
||||
shots: review
|
||||
? [{ file: 'thesis-review', at: 'Introduction', markup: true }]
|
||||
? [{ file: 'thesis-review', at: 'Introduction', markup: true, tab: 'review' }]
|
||||
: [
|
||||
{ file: 'thesis', at: 'Method' },
|
||||
{ file: 'thesis-dark', at: 'Method', theme: 'dark' },
|
||||
{ file: 'thesis-contents', at: 'Contents' },
|
||||
{ file: 'thesis-overview', page: 1, zoom: 50, columns: 2 },
|
||||
{ file: 'thesis-contents', at: 'Contents', tab: 'references' },
|
||||
{ file: 'thesis-overview', page: 1, zoom: 50, columns: 2, tab: 'view' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -278,6 +278,7 @@
|
||||
{#each shown as id}
|
||||
<button
|
||||
class="ribbon-tab"
|
||||
data-tab={id}
|
||||
class:active={tab === id}
|
||||
class:contextual={!(TABS as readonly string[]).includes(id)}
|
||||
onclick={() => { tab = id; collapsed = false; }}
|
||||
|
||||