docs: isolate screenshot tooling from release fixtures
Validate / process-gate (push) Failing after 4m52s
Validate / provenance (push) Successful in 4m55s
Validate / docs (push) Failing after 5m3s
Validate / changes (push) Successful in 4m39s
Validate / hacs (push) Skipped
Validate / hassfest (push) Skipped
Validate / frontend (push) Skipped
Validate / smoke (push) Skipped
Validate / golden (push) Skipped
Validate / performance_smoke (push) Skipped
Validate / backend (push) Skipped
Issue: #35 User-Visible: no
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v7
|
- uses: actions/setup-node@v7
|
||||||
with: { node-version: 22 }
|
with: { node-version: 22 }
|
||||||
- name: Validate public documentation
|
- name: Validate public documentation
|
||||||
run: npm run docs:check -- --external
|
run: node scripts/check-docs.mjs --external
|
||||||
|
|
||||||
provenance:
|
provenance:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ concurrent clients, but avoid editing the same object in two browsers at once.
|
|||||||
may be replaced with fictional ones.
|
may be replaced with fictional ones.
|
||||||
|
|
||||||
Documentation screenshots are produced by the reproducible
|
Documentation screenshots are produced by the reproducible
|
||||||
`npm run docs:capture` command using synthetic data only. Scenario version,
|
`npm run build && node demo/docs/capture.mjs` command using synthetic data only. Scenario version,
|
||||||
source fingerprint and every image hash are recorded in the
|
source fingerprint and every image hash are recorded in the
|
||||||
[screenshot manifest](docs/images/manifest.json).
|
[screenshot index](docs/images/screenshots.json).
|
||||||
|
|
||||||
License: [MIT](LICENSE).
|
License: [MIT](LICENSE).
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ default_floor: ground
|
|||||||
воспроизведения; приватные entity ID можно заменить вымышленными.
|
воспроизведения; приватные entity ID можно заменить вымышленными.
|
||||||
|
|
||||||
Скриншоты в документации получены воспроизводимой командой
|
Скриншоты в документации получены воспроизводимой командой
|
||||||
`npm run docs:capture` только на синтетических данных. Версия сценариев,
|
`npm run build && node demo/docs/capture.mjs` только на синтетических данных. Версия сценариев,
|
||||||
fingerprint исходников и хеш каждого изображения находятся в
|
fingerprint исходников и хеш каждого изображения находятся в
|
||||||
[манифесте снимков](docs/images/manifest.json).
|
[индексе снимков](docs/images/screenshots.json).
|
||||||
|
|
||||||
Лицензия: [MIT](LICENSE).
|
Лицензия: [MIT](LICENSE).
|
||||||
|
|||||||
@@ -102,6 +102,74 @@ const roomCardClip = (page) => page.evaluate(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation-only presentation state. Keep these mutations out of the
|
||||||
|
* golden harness: changing that release fixture would invalidate every visual
|
||||||
|
* baseline even though the production component and golden matrix are intact.
|
||||||
|
*/
|
||||||
|
const applyDocumentationState = (page, scenario) => page.evaluate(async (current) => {
|
||||||
|
const frame = () => new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done)));
|
||||||
|
const card = window.__goldenCard;
|
||||||
|
if (!card) throw new Error(`documentation card is missing: ${current.id}`);
|
||||||
|
|
||||||
|
if (current.title) {
|
||||||
|
card.setConfig({ ...card._config, title: current.title });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.roomMetrics) {
|
||||||
|
const space = card._serverCfg?.spaces?.find((item) => item.id === current.space);
|
||||||
|
if (!space) throw new Error(`documentation room metrics space is missing: ${current.space}`);
|
||||||
|
space.settings = {
|
||||||
|
...(space.settings || {}),
|
||||||
|
label_temp: true,
|
||||||
|
label_hum: true,
|
||||||
|
label_lqi: true,
|
||||||
|
label_light: true,
|
||||||
|
};
|
||||||
|
card._cfgEpoch += 1;
|
||||||
|
card._modelCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.fixture === 'empty') {
|
||||||
|
card._serverCfg = { ...(card._serverCfg || {}), spaces: [] };
|
||||||
|
card._cfgEpoch += 1;
|
||||||
|
card._modelCache = null;
|
||||||
|
card._space = '';
|
||||||
|
card._onboardingShown = true;
|
||||||
|
card.hass = { ...card.hass, floors: {} };
|
||||||
|
card._openSpaceDialog('create');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.dialog === 'device-info') {
|
||||||
|
const device = card._devices.find((item) => item.id === current.deviceId);
|
||||||
|
if (!device) throw new Error(`documentation device is missing: ${current.deviceId}`);
|
||||||
|
card._infoCard = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.requestUpdate();
|
||||||
|
await card.updateComplete;
|
||||||
|
await frame();
|
||||||
|
|
||||||
|
if (current.devicePresentationPreview) {
|
||||||
|
const dialog = card.renderRoot.querySelector('hp-dialog');
|
||||||
|
const body = dialog?.querySelector('.body');
|
||||||
|
const preview = dialog?.querySelector('hp-device-preview');
|
||||||
|
await preview?.updateComplete;
|
||||||
|
if (!body || !preview)
|
||||||
|
throw new Error('documentation device presentation preview is missing');
|
||||||
|
const bodyRect = body.getBoundingClientRect();
|
||||||
|
const previewRect = preview.getBoundingClientRect();
|
||||||
|
body.scrollTop += previewRect.top - bodyRect.top - 180;
|
||||||
|
await frame();
|
||||||
|
const visibleBody = body.getBoundingClientRect();
|
||||||
|
const visiblePreview = preview.getBoundingClientRect();
|
||||||
|
if (visiblePreview.top < visibleBody.top - 1 || visiblePreview.bottom > visibleBody.bottom + 1)
|
||||||
|
throw new Error('documentation viewport does not show the device presentation preview');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { dialog: !!card.renderRoot.querySelector('hp-dialog') };
|
||||||
|
}, scenario);
|
||||||
|
|
||||||
mkdirSync(OUTPUT, { recursive: true });
|
mkdirSync(OUTPUT, { recursive: true });
|
||||||
copyFileSync(BUNDLE, DEMO_BUNDLE);
|
copyFileSync(BUNDLE, DEMO_BUNDLE);
|
||||||
copyFileSync(BUNDLE, INTEGRATION_BUNDLE);
|
copyFileSync(BUNDLE, INTEGRATION_BUNDLE);
|
||||||
@@ -114,7 +182,8 @@ try {
|
|||||||
const fingerprint = await assertFreshDemoBundle(page, ROOT);
|
const fingerprint = await assertFreshDemoBundle(page, ROOT);
|
||||||
const scenarios = {};
|
const scenarios = {};
|
||||||
for (const scenario of DOC_SCREENSHOTS) {
|
for (const scenario of DOC_SCREENSHOTS) {
|
||||||
const runtime = await prepareGoldenScenario(page, scenario);
|
await prepareGoldenScenario(page, scenario);
|
||||||
|
const runtime = await applyDocumentationState(page, scenario);
|
||||||
if (scenario.expectDialog && !runtime.dialog)
|
if (scenario.expectDialog && !runtime.dialog)
|
||||||
throw new Error(`documentation scenario did not open its dialog: ${scenario.id}`);
|
throw new Error(`documentation scenario did not open its dialog: ${scenario.id}`);
|
||||||
const clip = scenario.capture === 'room-card'
|
const clip = scenario.capture === 'room-card'
|
||||||
@@ -140,10 +209,10 @@ try {
|
|||||||
fixture: 'synthetic-only',
|
fixture: 'synthetic-only',
|
||||||
sourceFingerprint: fingerprint,
|
sourceFingerprint: fingerprint,
|
||||||
captureScriptSha256: sha256(readFileSync(SCRIPT)),
|
captureScriptSha256: sha256(readFileSync(SCRIPT)),
|
||||||
command: 'npm run docs:capture',
|
command: 'npm run build && node demo/docs/capture.mjs',
|
||||||
scenarios,
|
scenarios,
|
||||||
};
|
};
|
||||||
writeFileSync(resolve(OUTPUT, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`);
|
writeFileSync(resolve(OUTPUT, 'screenshots.json'), `${JSON.stringify(manifest, null, 2)}\n`);
|
||||||
} finally {
|
} finally {
|
||||||
await browser.close();
|
await browser.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import { makeLargeHouseFixture } from '../fixtures/large-house.mjs';
|
import { makeLargeHouseFixture } from '../fixtures/large-house.mjs';
|
||||||
import { fixtureWallKey, makeVisualMatrixFixture } from '../fixtures/visual-matrix.mjs';
|
import { fixtureWallKey, makeVisualMatrixFixture } from '../fixtures/visual-matrix.mjs';
|
||||||
|
|
||||||
const fixtureFor = (name) => {
|
const fixtureFor = (name) => name === 'large' ? makeLargeHouseFixture() : makeVisualMatrixFixture();
|
||||||
if (name === 'large') return makeLargeHouseFixture();
|
|
||||||
if (name === 'empty') {
|
|
||||||
return {
|
|
||||||
config: { spaces: [], markers: [], settings: {} },
|
|
||||||
layout: {}, devices: {}, entities: {}, states: {}, areas: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return makeVisualMatrixFixture();
|
|
||||||
};
|
|
||||||
|
|
||||||
const themeVars = {
|
const themeVars = {
|
||||||
dark: {
|
dark: {
|
||||||
@@ -129,8 +120,7 @@ export function prepareGoldenFixture(scenario) {
|
|||||||
fixture.devices[scenario.deviceId].name = scenario.deviceName;
|
fixture.devices[scenario.deviceId].name = scenario.deviceName;
|
||||||
}
|
}
|
||||||
if (scenario.fillMode || scenario.bgMode || typeof scenario.glowEnabled === 'boolean'
|
if (scenario.fillMode || scenario.bgMode || typeof scenario.glowEnabled === 'boolean'
|
||||||
|| typeof scenario.sunRays === 'boolean' || typeof scenario.showBorders === 'boolean'
|
|| typeof scenario.sunRays === 'boolean' || typeof scenario.showBorders === 'boolean') {
|
||||||
|| scenario.roomMetrics) {
|
|
||||||
const space = requireSpace();
|
const space = requireSpace();
|
||||||
space.settings = {
|
space.settings = {
|
||||||
...(space.settings || {}),
|
...(space.settings || {}),
|
||||||
@@ -139,9 +129,6 @@ export function prepareGoldenFixture(scenario) {
|
|||||||
...(typeof scenario.glowEnabled === 'boolean' ? { glow_enabled: scenario.glowEnabled } : {}),
|
...(typeof scenario.glowEnabled === 'boolean' ? { glow_enabled: scenario.glowEnabled } : {}),
|
||||||
...(typeof scenario.sunRays === 'boolean' ? { sun_rays: scenario.sunRays } : {}),
|
...(typeof scenario.sunRays === 'boolean' ? { sun_rays: scenario.sunRays } : {}),
|
||||||
...(typeof scenario.showBorders === 'boolean' ? { show_borders: scenario.showBorders } : {}),
|
...(typeof scenario.showBorders === 'boolean' ? { show_borders: scenario.showBorders } : {}),
|
||||||
...(scenario.roomMetrics ? {
|
|
||||||
label_temp: true, label_hum: true, label_lqi: true, label_light: true,
|
|
||||||
} : {}),
|
|
||||||
...(scenario.customFill ? { custom_fill: scenario.customFill } : {}),
|
...(scenario.customFill ? { custom_fill: scenario.customFill } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -289,7 +276,7 @@ export async function prepareGoldenScenario(page, scenario) {
|
|||||||
}
|
}
|
||||||
const host = document.getElementById('host');
|
const host = document.getElementById('host');
|
||||||
const cardConfig = {
|
const cardConfig = {
|
||||||
type: 'custom:houseplan-card', title: scenario.title || `Golden ${scenario.id}`, icon_size: 3.4,
|
type: 'custom:houseplan-card', title: `Golden ${scenario.id}`, icon_size: 3.4,
|
||||||
language: scenario.language || 'en',
|
language: scenario.language || 'en',
|
||||||
...(scenario.kiosk ? { kiosk: true } : {}),
|
...(scenario.kiosk ? { kiosk: true } : {}),
|
||||||
};
|
};
|
||||||
@@ -298,7 +285,7 @@ export async function prepareGoldenScenario(page, scenario) {
|
|||||||
user: { id: 'golden', name: 'Golden fixture', is_admin: true },
|
user: { id: 'golden', name: 'Golden fixture', is_admin: true },
|
||||||
devices: fixture.devices || {}, entities: fixture.entities || {},
|
devices: fixture.devices || {}, entities: fixture.entities || {},
|
||||||
areas: fixture.areas || {}, states: fixture.states || {},
|
areas: fixture.areas || {}, states: fixture.states || {},
|
||||||
floors: scenario.noFloors ? {} : {
|
floors: {
|
||||||
one: { floor_id: 'one', name: 'One', level: 0 },
|
one: { floor_id: 'one', name: 'One', level: 0 },
|
||||||
two: { floor_id: 'two', name: 'Two', level: 1 },
|
two: { floor_id: 'two', name: 'Two', level: 1 },
|
||||||
three: { floor_id: 'three', name: 'Three', level: 2 },
|
three: { floor_id: 'three', name: 'Three', level: 2 },
|
||||||
@@ -551,21 +538,6 @@ export async function prepareGoldenScenario(page, scenario) {
|
|||||||
if (visibleRole.top < visibleBody.top - 1 || visibleRadius.bottom > visibleBody.bottom + 1)
|
if (visibleRole.top < visibleBody.top - 1 || visibleRadius.bottom > visibleBody.bottom + 1)
|
||||||
throw new Error('golden viewport does not show the complete device light-source controls');
|
throw new Error('golden viewport does not show the complete device light-source controls');
|
||||||
}
|
}
|
||||||
if (scenario.devicePresentationPreview) {
|
|
||||||
const dialog = card.renderRoot.querySelector('hp-dialog');
|
|
||||||
const body = dialog?.querySelector('.body');
|
|
||||||
const preview = dialog?.querySelector('hp-device-preview');
|
|
||||||
await preview?.updateComplete;
|
|
||||||
if (!body || !preview) throw new Error('golden device presentation preview is missing');
|
|
||||||
const bodyRect = body.getBoundingClientRect();
|
|
||||||
const previewRect = preview.getBoundingClientRect();
|
|
||||||
body.scrollTop += previewRect.top - bodyRect.top - 180;
|
|
||||||
await frame();
|
|
||||||
const visibleBody = body.getBoundingClientRect();
|
|
||||||
const visiblePreview = preview.getBoundingClientRect();
|
|
||||||
if (visiblePreview.top < visibleBody.top - 1 || visiblePreview.bottom > visibleBody.bottom + 1)
|
|
||||||
throw new Error('golden viewport does not show the device presentation preview');
|
|
||||||
}
|
|
||||||
if (scenario.openHelp) {
|
if (scenario.openHelp) {
|
||||||
const help = card.renderRoot.querySelector(`hp-help[data-help-key="${scenario.openHelp}"]`);
|
const help = card.renderRoot.querySelector(`hp-help[data-help-key="${scenario.openHelp}"]`);
|
||||||
await help?.updateComplete;
|
await help?.updateComplete;
|
||||||
@@ -585,15 +557,6 @@ export async function prepareGoldenScenario(page, scenario) {
|
|||||||
await dialog?.updateComplete;
|
await dialog?.updateComplete;
|
||||||
dialog?.renderRoot?.querySelector('.close')?.focus();
|
dialog?.renderRoot?.querySelector('.close')?.focus();
|
||||||
}
|
}
|
||||||
} else if (scenario.dialog === 'device-info') {
|
|
||||||
const device = card._devices.find((item) => item.id === scenario.deviceId);
|
|
||||||
if (!device) throw new Error(`golden device missing: ${scenario.deviceId}`);
|
|
||||||
card._infoCard = device;
|
|
||||||
card.requestUpdate();
|
|
||||||
await card.updateComplete;
|
|
||||||
await frame();
|
|
||||||
if (!card.renderRoot.querySelector('hp-dialog'))
|
|
||||||
throw new Error(`golden device info did not open: ${scenario.deviceId}`);
|
|
||||||
} else if (scenario.dialog === 'backup-full' || scenario.dialog === 'backup-space') {
|
} else if (scenario.dialog === 'backup-full' || scenario.dialog === 'backup-space') {
|
||||||
const full = scenario.dialog === 'backup-full';
|
const full = scenario.dialog === 'backup-full';
|
||||||
card._backupImportDialog = {
|
card._backupImportDialog = {
|
||||||
|
|||||||
@@ -250,9 +250,9 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
|
|||||||
`~/.git-credentials`; pushes go over SSH with the `ha_jb` key. The old classic PAT
|
`~/.git-credentials`; pushes go over SSH with the `ha_jb` key. The old classic PAT
|
||||||
expired and is gone.
|
expired and is gone.
|
||||||
3. Privacy: legacy real-house plan sources (`assets/`) and screenshots were
|
3. Privacy: legacy real-house plan sources (`assets/`) and screenshots were
|
||||||
removed from the current tree. Public documentation images are generated
|
removed from the current tree. Public documentation images are generated
|
||||||
from synthetic fixtures by `npm run docs:capture` and indexed in
|
from synthetic fixtures by `npm run build && node demo/docs/capture.mjs` and indexed in
|
||||||
`docs/images/manifest.json`. Old images persist in git history and release
|
`docs/images/screenshots.json`. Old images persist in git history and release
|
||||||
archives; history rewrite is deliberately not done because it would break
|
archives; history rewrite is deliberately not done because it would break
|
||||||
release tags and HACS installs.
|
release tags and HACS installs.
|
||||||
4. Stale files on the mount that cannot be deleted from the sandbox: `src/data/` leftovers,
|
4. Stale files on the mount that cannot be deleted from the sandbox: `src/data/` leftovers,
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 342 KiB After Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 328 KiB After Width: | Height: | Size: 328 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"fixture": "synthetic-only",
|
"fixture": "synthetic-only",
|
||||||
"sourceFingerprint": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceFingerprint": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"captureScriptSha256": "a0022c98553c5baae8335d7ba26f64129699c4ef6162dd1d88df9f70226ec404",
|
"captureScriptSha256": "34f2219790d46efd8250e7a1bd829cb8fc0b0547e1260635fefa52407551b41b",
|
||||||
"command": "npm run docs:capture",
|
"command": "npm run build && node demo/docs/capture.mjs",
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
"view-desktop": {
|
"view-desktop": {
|
||||||
"file": "01-view-desktop.png",
|
"file": "01-view-desktop.png",
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "d36b6f9f8139f31ef73a780c6511a640a26055efd9d7a24c24fd48b1d8379bf0"
|
"imageSha256": "d36b6f9f8139f31ef73a780c6511a640a26055efd9d7a24c24fd48b1d8379bf0"
|
||||||
},
|
},
|
||||||
"view-touch": {
|
"view-touch": {
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "358e25ff9984d0fb0c03cfbb848df40c425fdfe64cd5f4f613b1e754ca9d4249"
|
"imageSha256": "358e25ff9984d0fb0c03cfbb848df40c425fdfe64cd5f4f613b1e754ca9d4249"
|
||||||
},
|
},
|
||||||
"space-create": {
|
"space-create": {
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "c53db2e5c642a5549c13f3c93a5b359fed69bdb2621bf71a243a877ffcb95e6b"
|
"imageSha256": "c53db2e5c642a5549c13f3c93a5b359fed69bdb2621bf71a243a877ffcb95e6b"
|
||||||
},
|
},
|
||||||
"room-contour-close": {
|
"room-contour-close": {
|
||||||
@@ -46,8 +46,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "59fe5b9df034d255840e7ae6984efb675d39e84ad80dee6f9ad9394c54fb9cda"
|
"imageSha256": "2f4770869f04c8d7f1e8ff33af1e8bd79f45ad6d97be6d5e6135b3a82fec9e4c"
|
||||||
},
|
},
|
||||||
"plan-context-tray": {
|
"plan-context-tray": {
|
||||||
"file": "05-plan-context-tray.png",
|
"file": "05-plan-context-tray.png",
|
||||||
@@ -57,8 +57,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "997bba208897b219051131dc51254ee3c76ed9f73597fde33070ac8cefdcd745"
|
"imageSha256": "47d8fc2ac1c14cb699e990ca9c00d89b9ac57f6cc76b40420ceb26b4d7f50df4"
|
||||||
},
|
},
|
||||||
"device-editor": {
|
"device-editor": {
|
||||||
"file": "06-device-editor.png",
|
"file": "06-device-editor.png",
|
||||||
@@ -68,8 +68,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "7a22a81e2abedf5a520bd36b7509d953cbfa9962b48c0ffbc233666a6215c04b"
|
"imageSha256": "7c3e25534fc819c45431907616c3d523961505859ee68af27cce2daec9f04fb0"
|
||||||
},
|
},
|
||||||
"device-display-preview": {
|
"device-display-preview": {
|
||||||
"file": "06-device-display-preview.png",
|
"file": "06-device-display-preview.png",
|
||||||
@@ -79,8 +79,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "f6014caed7b7d28790b8548996ba09d806a4e4d51fbb7e8d3fb3c582ebe49167"
|
"imageSha256": "df3a54395c83bffd7a161f1fb3de0143e12e76567535aedf9883873ead74df91"
|
||||||
},
|
},
|
||||||
"background-editor": {
|
"background-editor": {
|
||||||
"file": "07-background-editor.png",
|
"file": "07-background-editor.png",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "4e7b3b1220a3fe104cf11e5b32a31b343dc95c45fd2cbe3365d145f0a66a560d"
|
"imageSha256": "4e7b3b1220a3fe104cf11e5b32a31b343dc95c45fd2cbe3365d145f0a66a560d"
|
||||||
},
|
},
|
||||||
"room-card": {
|
"room-card": {
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "176abba71d41cfb045a33f82a794d9fbb2a5d3c48e6df66e6ec3a8e448311b11"
|
"imageSha256": "176abba71d41cfb045a33f82a794d9fbb2a5d3c48e6df66e6ec3a8e448311b11"
|
||||||
},
|
},
|
||||||
"device-info": {
|
"device-info": {
|
||||||
@@ -112,8 +112,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "7f726b212839add4f9fc4491fd23f053b09bf82e38ea380a7cc84d1dc2bcf81a",
|
"sourceSha256": "4343126d84c3579b6dc970ed755152b1206c02aacff8abe43b89535f48eee2c1",
|
||||||
"imageSha256": "655873ba709a2a702fde533b33ef109343ce0c6ca85ff985fdd7c4d41c877b33"
|
"imageSha256": "2199ed88b215bf2bff63bf665028f78b2aa6a92c3032b803931790f2ef071893"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,6 @@
|
|||||||
"golden:verify": "node demo/golden/run.mjs --mode=verify",
|
"golden:verify": "node demo/golden/run.mjs --mode=verify",
|
||||||
"continuity:screencast": "node demo/screencast_visual_continuity.mjs",
|
"continuity:screencast": "node demo/screencast_visual_continuity.mjs",
|
||||||
"golden:accept": "node demo/golden/accept.mjs",
|
"golden:accept": "node demo/golden/accept.mjs",
|
||||||
"docs:capture": "npm run build && node demo/docs/capture.mjs",
|
|
||||||
"docs:check": "node scripts/check-docs.mjs",
|
|
||||||
"release:check": "node scripts/release-prerelease.mjs --check",
|
"release:check": "node scripts/release-prerelease.mjs --check",
|
||||||
"release:prerelease": "node scripts/release-prerelease.mjs",
|
"release:prerelease": "node scripts/release-prerelease.mjs",
|
||||||
"prepare": "node scripts/install-hooks.mjs"
|
"prepare": "node scripts/install-hooks.mjs"
|
||||||
|
|||||||
@@ -123,16 +123,17 @@ for (const relative of PUBLIC_DOCS.slice(0, 4)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const manifestPath = resolve(ROOT, 'docs/images/manifest.json');
|
const manifestPath = resolve(ROOT, 'docs/images/screenshots.json');
|
||||||
if (!existsSync(manifestPath)) {
|
if (!existsSync(manifestPath)) {
|
||||||
errors.push('docs/images/manifest.json: missing screenshot manifest');
|
errors.push('docs/images/screenshots.json: missing screenshot index');
|
||||||
} else {
|
} else {
|
||||||
const manifest = JSON.parse(canonicalText(manifestPath));
|
const manifest = JSON.parse(canonicalText(manifestPath));
|
||||||
if (manifest.fixture !== 'synthetic-only') errors.push('screenshot manifest must declare synthetic-only fixture');
|
if (manifest.fixture !== 'synthetic-only') errors.push('screenshot manifest must declare synthetic-only fixture');
|
||||||
if (manifest.sourceFingerprint !== sourceFingerprint(ROOT)) errors.push('screenshot source fingerprint is stale; run npm run docs:capture');
|
if (manifest.sourceFingerprint !== sourceFingerprint(ROOT))
|
||||||
|
errors.push('screenshot source fingerprint is stale; run npm run build && node demo/docs/capture.mjs');
|
||||||
const scriptPath = resolve(ROOT, 'demo/docs/capture.mjs');
|
const scriptPath = resolve(ROOT, 'demo/docs/capture.mjs');
|
||||||
if (manifest.captureScriptSha256 !== sha256(readFileSync(scriptPath)))
|
if (manifest.captureScriptSha256 !== sha256(readFileSync(scriptPath)))
|
||||||
errors.push('screenshot capture script changed; run npm run docs:capture');
|
errors.push('screenshot capture script changed; run npm run build && node demo/docs/capture.mjs');
|
||||||
const ids = Object.keys(manifest.scenarios || {});
|
const ids = Object.keys(manifest.scenarios || {});
|
||||||
if (JSON.stringify(ids.sort()) !== JSON.stringify([...EXPECTED_SCREENSHOTS].sort()))
|
if (JSON.stringify(ids.sort()) !== JSON.stringify([...EXPECTED_SCREENSHOTS].sort()))
|
||||||
errors.push('screenshot manifest scenario set is incomplete');
|
errors.push('screenshot manifest scenario set is incomplete');
|
||||||
|
|||||||