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.
This commit is contained in:
Steffen Becker committed 2026-09-06 19:07:24 +02:00
1 parent 9b426832e1
commit 99f10c9399
20 files changed
+21 -11

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -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' },
],
};
}
+2
View File
@@ -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 = {
+2 -2
View File
@@ -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' },
],
};
}
+12 -5
View File
@@ -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`);
+3 -3
View File
@@ -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' },
],
};
}
+1
View File
@@ -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; }}