diff --git a/Website/docs/Images/readme-previews/install.webp b/Website/docs/Images/readme-previews/install.webp
deleted file mode 100644
index 7d60813c..00000000
Binary files a/Website/docs/Images/readme-previews/install.webp and /dev/null differ
diff --git a/Website/media/readme-previews/README.md b/Website/media/readme-previews/README.md
index 1d8c33eb..0d7e7783 100644
--- a/Website/media/readme-previews/README.md
+++ b/Website/media/readme-previews/README.md
@@ -1,19 +1,22 @@
# README feature previews
-The root README cannot run the introduction's animated feature previews (`Website/src/components/FeaturePreview`) or
-the install panel's Package Manager walk-through (`Website/src/components/InstallPanel`), so
+The root README cannot run the introduction's animated feature previews (`Website/src/components/FeaturePreview`), so
`Website/scripts/github-readme.mjs` shows a recording of each one in its card:
-`Website/docs/Images/readme-previews/.webp`, `install` for the walk-through. Only the dark theme: a `` per
+`Website/docs/Images/readme-previews/.webp`. Only the dark theme: a `` per
theme follows the system theme, not GitHub's, so the README shows no light variants.
`record.mjs` opens the built introduction in headless Chrome on virtual time and records one loop of each preview
at 2x in the green accent and the dark theme: 20 fps, the Profiler's scrolling timeline 12.5 fps. `img2webp` encodes it
losslessly: lossy WebP leaves ghosts of earlier frames on these flat interfaces. The Profiler's timeline takes mixed
frames (q 90) instead, which keeps it near 2 MB. Each loop length comes from the preview's `useLoop` steps (the
-Profiler's ticker for ProfilerMarkers, `DURATIONS` in `InstallPanel` for the walk-through); update `PREVIEWS` when they
-change. The recording drops the frame of the card or panel, since on GitHub the table cell is the frame. The
-walk-through hides the package version, but its typed URL names the channel (`#upm-preview`), so re-record `install`
-when a release switches channels; `scripts/set-version.sh` says so.
+Profiler's ticker for ProfilerMarkers); update `PREVIEWS` when they change. The recording drops the frame of the card,
+since on GitHub the table cell is the frame.
+
+The installation walkthrough is generated centrally in
+[VPDPersonal](https://github.com/VPDPersonal/VPDPersonal/tree/main/scripts/installation), with cached PNG/WebP output
+on GitHub Pages. The README generator selects the stable or preview URL from its copyable install command;
+changing a release channel does not require a local recording. The documentation site's interactive
+`Website/src/components/InstallPanel` remains its own component.
1. Build and serve the site: `Website/scripts/serve-all.sh`.
2. Record every preview, or only the named ones: `node Website/media/readme-previews/record.mjs http://localhost:/Aspid.FastTools/ [name …]`.
diff --git a/Website/media/readme-previews/record.mjs b/Website/media/readme-previews/record.mjs
index 04683bb9..c84c2789 100644
--- a/Website/media/readme-previews/record.mjs
+++ b/Website/media/readme-previews/record.mjs
@@ -1,5 +1,5 @@
-// Records the introduction's animated feature previews (Website/src/components/FeaturePreview) and the install panel's
-// Package Manager walk-through (Website/src/components/InstallPanel) from a built site into looping WebPs for the GitHub
+// Records the introduction's animated feature previews (Website/src/components/FeaturePreview)
+// from a built site into looping WebPs for the GitHub
// README: Website/docs/Images/readme-previews/.webp, in the dark theme only. Headless Chrome runs on
// virtual time, so every frame lands on its exact timestamp however slow the capture is.
// Website/scripts/serve-all.sh
@@ -22,11 +22,6 @@ const OUT = fileURLToPath(new URL('../../docs/Images/readme-previews/', import.m
const FPS = 20;
const ENCODE = ['-lossless', '-m', '6'];
const PREVIEWS = {
- // InstallPanel: the sum of DURATIONS. It plays PLAYS (2) times, so a short warm-up records the first play, which runs
- // into the second one's first frame. The installed package's version is hidden, so a release needs no new recording;
- // the typed URL still names the channel.
- 'install': {loop: 1300 + 1400 + 1800 + 1100 + 2600, warmup: 100,
- box: `document.querySelector('section[class*="install_"] [class*="preview_"]')`},
'profiler-markers': {loop: 22000, fps: 12.5, encode: ['-mixed', '-q', '90', '-m', '6']},
'visual-element-extensions': {loop: 6 * 1100},
'serialized-property-extensions': {loop: 5 * 1200},
@@ -115,7 +110,7 @@ async function open() {
const page = (method, params) => send(method, params, sessionId);
await page('Page.enable');
await page('Emulation.setDeviceMetricsOverride', {...VIEWPORT, mobile: false});
- // The entrance has played, so the banner rests. The frame of a card or the install panel, its rounded corner and the
+ // The entrance has played, so the banner rests. The frame of a card, its rounded corner and the
// line between preview and text go: on GitHub the table cell is the frame.
await page('Page.addScriptToEvaluateOnNewDocument', {source: `
localStorage.setItem('theme', 'dark');
@@ -124,9 +119,6 @@ async function open() {
+ '.feature-card { border: 0 !important; border-radius: 0 !important; }'
+ '.feature-card__preview { border-right: 0 !important; }'
+ '.feature-card__live [class*="inspector_"], .feature-card__live [class*="uiStage_"] { border-radius: 0 !important; }'
- + 'section[class*="install_"] { border: 0 !important; border-radius: 0 !important; }'
- + 'section[class*="install_"] [class*="preview_"] { border-right: 0 !important; }'
- + 'section[class*="install_"] [class*="pmNew_"] span:last-child { visibility: hidden; }'
+ ''));`});
const loaded = once('Page.loadEventFired', sessionId);
await page('Page.navigate', {url: new URL('docs', base).href});
diff --git a/Website/scripts/github-readme.mjs b/Website/scripts/github-readme.mjs
index cd340b01..9a564659 100644
--- a/Website/scripts/github-readme.mjs
+++ b/Website/scripts/github-readme.mjs
@@ -3,8 +3,8 @@
* site-only blocks get the closest GitHub equivalent in plain HTML (src/remark/introBanner.js builds the site's):
* - links to doc pages and to the samples overview open the site, which renders them in full;
* - the link row's link to the site lists the site's languages: `Documentation: EN, RU`;
- * - the install instruction becomes a card like the site's install panel (src/components/InstallPanel): the recording
- * of its Package Manager walk-through beside its numbered steps, the URL to copy below;
+ * - the install instruction becomes a card like the site's install panel (src/components/InstallPanel): the shared
+ * Package Manager walk-through beside its numbered steps, the URL to copy below;
* - the note under the install URL links the site's install panel for versions and channels;
* - every feature becomes a card: a one-row table with the preview on the left, the title and summary on the right;
* a code preview gives way to the recording of the site's animated preview (`Website/docs/Images/readme-previews`);
@@ -88,13 +88,16 @@ function linkRow(tree, site, locales) {
}
/** The install section: an instruction, the URL block and a note. The instruction gives way to the install card. */
-function installCard(tree, {previews, exists}) {
+function installCard(tree, {installation}) {
const index = tree.children.findIndex((node, at) => isHeading(node, 2) && tree.children[at + 1]?.type === 'paragraph'
&& tree.children[at + 2]?.type === 'code' && /\.git#upm/.test(tree.children[at + 2].value));
- const file = `${previews}/install.webp`;
- if (index === -1 || !exists(file)) return;
+ if (index === -1 || !installation) return;
+ const branch = /#(upm(?:-preview)?)(?:\/[^\s]+)?$/.exec(tree.children[index + 2].value.trim())?.[1];
+ if (!branch) return;
+ const file = `${installation}/${branch === 'upm-preview' ? 'preview/' : ''}install`;
const steps = TEXT.steps.map((step) => `
`);
}
@@ -218,6 +221,7 @@ function donateCard(tree, {images, exists}) {
* @param options.repository the repository's GitHub URL, without a trailing `/`
* @param options.previews the folder of the preview recordings, relative to the README
* @param options.images the folder of the README images, relative to the README
+ * @param options.installation the shared installation media base URL for this package
* @param options.exists whether a URL relative to the README names a file
*/
export function githubLayout(tree, options) {
diff --git a/Website/scripts/github-readme.test.mjs b/Website/scripts/github-readme.test.mjs
index 92cafe64..146d6fd0 100644
--- a/Website/scripts/github-readme.test.mjs
+++ b/Website/scripts/github-readme.test.mjs
@@ -13,6 +13,7 @@ const repository = 'https://github.com/owner/repo';
function layout(markdown, files = []) {
const tree = processor.parse(markdown);
githubLayout(tree, {site, locales: ['en', 'ru'], repository, previews: 'Website/docs/Images/readme-previews',
+ installation: 'https://vpdpersonal.github.io/VPDPersonal/installation/aspid-fasttools',
images: 'Website/docs/Images', exists: (url) => files.includes(url)});
return processor.stringify(tree);
}
@@ -74,11 +75,16 @@ test('the note under the install URL links the install panel on the site', () =>
test('the install instruction becomes a card with the walk-through and the steps', () => {
const install = '## Installation\n\nIn **Window → Package Manager**, paste this URL:\n\n```text\nhttps://github.com/owner/repo.git#upm\n```\n\nThe URL installs the latest release.\n';
- const result = layout(install, ['Website/docs/Images/readme-previews/install.webp']);
- assert.match(result, /^## Installation\n\n