slides-grab 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/bin/ppt-agent.js +35 -5
- package/package.json +3 -3
- package/scripts/build-viewer.js +48 -21
- package/scripts/editor-server.js +45 -10
- package/scripts/figma-export.js +16 -1
- package/scripts/html2pdf.js +51 -23
- package/scripts/html2pptx.js +22 -1
- package/scripts/validate-slides.js +22 -5
- package/skills/slides-grab/SKILL.md +9 -5
- package/skills/slides-grab-card-news/SKILL.md +35 -0
- package/skills/slides-grab-design/SKILL.md +13 -11
- package/skills/slides-grab-design/references/design-rules.md +5 -0
- package/skills/slides-grab-design/references/detailed-design-rules.md +5 -0
- package/skills/slides-grab-export/SKILL.md +15 -8
- package/skills/slides-grab-export/references/html2pptx.md +4 -4
- package/src/editor/codex-edit.js +45 -8
- package/src/editor/editor-codex-prompt.md +3 -1
- package/src/editor/js/editor-init.js +34 -2
- package/src/editor/js/editor-state.js +9 -2
- package/src/editor/screenshot.js +4 -3
- package/src/export-resolution.cjs +21 -11
- package/src/figma.js +11 -3
- package/src/pptx-raster-export.cjs +79 -21
- package/src/slide-mode.cjs +72 -0
- package/src/validation/cli.js +23 -0
- package/src/validation/core.js +39 -25
package/src/validation/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { access, readdir } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import { basename, join } from 'node:path';
|
|
3
4
|
import { pathToFileURL } from 'node:url';
|
|
4
5
|
import { chromium } from 'playwright';
|
|
@@ -9,12 +10,14 @@ import {
|
|
|
9
10
|
resolveSlideSourcePath,
|
|
10
11
|
} from '../image-contract.js';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const {
|
|
15
|
+
DEFAULT_SLIDE_MODE,
|
|
16
|
+
getSlideModeConfig,
|
|
17
|
+
} = require('../slide-mode.cjs');
|
|
18
|
+
|
|
19
|
+
export const FRAME_PT = getSlideModeConfig(DEFAULT_SLIDE_MODE).framePt;
|
|
20
|
+
export const FRAME_PX = getSlideModeConfig(DEFAULT_SLIDE_MODE).framePx;
|
|
18
21
|
export const SLIDE_FILE_PATTERN = /^slide-.*\.html$/i;
|
|
19
22
|
export const TEXT_SELECTOR = 'p,h1,h2,h3,h4,h5,h6,li';
|
|
20
23
|
export const TOLERANCE_PX = 0.5;
|
|
@@ -58,28 +61,30 @@ export function summarizeSlides(slides) {
|
|
|
58
61
|
return summary;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
export function createValidationResult(slides) {
|
|
64
|
+
export function createValidationResult(slides, slideMode = DEFAULT_SLIDE_MODE) {
|
|
65
|
+
const { framePt, framePx } = getSlideModeConfig(slideMode);
|
|
62
66
|
return {
|
|
63
67
|
generatedAt: new Date().toISOString(),
|
|
64
68
|
frame: {
|
|
65
|
-
widthPt:
|
|
66
|
-
heightPt:
|
|
67
|
-
widthPx:
|
|
68
|
-
heightPx:
|
|
69
|
+
widthPt: framePt.width,
|
|
70
|
+
heightPt: framePt.height,
|
|
71
|
+
widthPx: framePx.width,
|
|
72
|
+
heightPx: framePx.height,
|
|
69
73
|
},
|
|
70
74
|
slides,
|
|
71
75
|
summary: summarizeSlides(slides),
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
export function createValidationFailure(error) {
|
|
79
|
+
export function createValidationFailure(error, slideMode = DEFAULT_SLIDE_MODE) {
|
|
80
|
+
const { framePt, framePx } = getSlideModeConfig(slideMode);
|
|
76
81
|
return {
|
|
77
82
|
generatedAt: new Date().toISOString(),
|
|
78
83
|
frame: {
|
|
79
|
-
widthPt:
|
|
80
|
-
heightPt:
|
|
81
|
-
widthPx:
|
|
82
|
-
heightPx:
|
|
84
|
+
widthPt: framePt.width,
|
|
85
|
+
heightPt: framePt.height,
|
|
86
|
+
widthPx: framePx.width,
|
|
87
|
+
heightPx: framePx.height,
|
|
83
88
|
},
|
|
84
89
|
slides: [],
|
|
85
90
|
summary: {
|
|
@@ -347,9 +352,10 @@ export function selectSlideFiles(slideFiles, selectedSlides = [], slidesDir = ''
|
|
|
347
352
|
return slideFiles.filter((slide) => available.has(slide) && requested.includes(slide));
|
|
348
353
|
}
|
|
349
354
|
|
|
350
|
-
export async function inspectSlide(page, fileName, slidesDir) {
|
|
355
|
+
export async function inspectSlide(page, fileName, slidesDir, slideMode = DEFAULT_SLIDE_MODE) {
|
|
351
356
|
const slidePath = join(slidesDir, fileName);
|
|
352
357
|
const slideUrl = pathToFileURL(slidePath).href;
|
|
358
|
+
const { framePx, sizeLabel } = getSlideModeConfig(slideMode);
|
|
353
359
|
|
|
354
360
|
await page.goto(slideUrl, { waitUntil: 'load' });
|
|
355
361
|
await page.evaluate(async () => {
|
|
@@ -359,7 +365,7 @@ export async function inspectSlide(page, fileName, slidesDir) {
|
|
|
359
365
|
});
|
|
360
366
|
|
|
361
367
|
const inspection = await page.evaluate(
|
|
362
|
-
({ framePx, textSelector, tolerancePx }) => {
|
|
368
|
+
({ framePx, sizeLabel, textSelector, tolerancePx }) => {
|
|
363
369
|
const skipTags = new Set(['SCRIPT', 'STYLE', 'META', 'LINK', 'HEAD', 'TITLE', 'NOSCRIPT']);
|
|
364
370
|
const critical = [];
|
|
365
371
|
const warning = [];
|
|
@@ -552,7 +558,7 @@ export async function inspectSlide(page, fileName, slidesDir) {
|
|
|
552
558
|
if (outsideFrame) {
|
|
553
559
|
critical.push({
|
|
554
560
|
code: 'overflow-outside-frame',
|
|
555
|
-
message:
|
|
561
|
+
message: `Element exceeds the ${sizeLabel} slide frame.`,
|
|
556
562
|
element: elementPath(element),
|
|
557
563
|
bbox: normalizeRect(rect),
|
|
558
564
|
frame: normalizeRect(frameRect),
|
|
@@ -663,7 +669,8 @@ export async function inspectSlide(page, fileName, slidesDir) {
|
|
|
663
669
|
};
|
|
664
670
|
},
|
|
665
671
|
{
|
|
666
|
-
framePx
|
|
672
|
+
framePx,
|
|
673
|
+
sizeLabel,
|
|
667
674
|
textSelector: TEXT_SELECTOR,
|
|
668
675
|
tolerancePx: TOLERANCE_PX,
|
|
669
676
|
},
|
|
@@ -696,12 +703,12 @@ export async function inspectSlide(page, fileName, slidesDir) {
|
|
|
696
703
|
};
|
|
697
704
|
}
|
|
698
705
|
|
|
699
|
-
export async function scanSlides(page, slidesDir, slideFiles) {
|
|
706
|
+
export async function scanSlides(page, slidesDir, slideFiles, slideMode = DEFAULT_SLIDE_MODE) {
|
|
700
707
|
const slides = [];
|
|
701
708
|
|
|
702
709
|
for (const slideFile of slideFiles) {
|
|
703
710
|
try {
|
|
704
|
-
const result = await inspectSlide(page, slideFile, slidesDir);
|
|
711
|
+
const result = await inspectSlide(page, slideFile, slidesDir, slideMode);
|
|
705
712
|
slides.push(result);
|
|
706
713
|
} catch (error) {
|
|
707
714
|
slides.push({
|
|
@@ -740,7 +747,10 @@ export function formatValidationFailureForExport(result, exportLabel = 'Export')
|
|
|
740
747
|
}
|
|
741
748
|
|
|
742
749
|
const suffix = findings.length > 0 ? `\n${findings.join('\n')}` : '';
|
|
743
|
-
|
|
750
|
+
const modeHint = result.slideMode && result.slideMode !== DEFAULT_SLIDE_MODE
|
|
751
|
+
? ` --mode ${result.slideMode}`
|
|
752
|
+
: '';
|
|
753
|
+
return `${exportLabel} blocked by slide validation. Run \`slides-grab validate --slides-dir <path>${modeHint}\` for full diagnostics.${suffix}`;
|
|
744
754
|
}
|
|
745
755
|
|
|
746
756
|
const EXPORT_BLOCKING_IMAGE_CONTRACT_CODES = new Set([
|
|
@@ -799,6 +809,7 @@ export async function ensureSlidesPassValidation(
|
|
|
799
809
|
slidesDir,
|
|
800
810
|
{
|
|
801
811
|
exportLabel = 'Export',
|
|
812
|
+
slideMode = DEFAULT_SLIDE_MODE,
|
|
802
813
|
shouldBlockIssue = isBlockingImageContractIssue,
|
|
803
814
|
} = {},
|
|
804
815
|
) {
|
|
@@ -812,8 +823,11 @@ export async function ensureSlidesPassValidation(
|
|
|
812
823
|
const page = await context.newPage();
|
|
813
824
|
|
|
814
825
|
try {
|
|
815
|
-
const slides = await scanSlides(page, slidesDir, slideFiles);
|
|
816
|
-
const result =
|
|
826
|
+
const slides = await scanSlides(page, slidesDir, slideFiles, slideMode);
|
|
827
|
+
const result = {
|
|
828
|
+
...createValidationResult(slides, slideMode),
|
|
829
|
+
slideMode,
|
|
830
|
+
};
|
|
817
831
|
const blockingResult = filterExportBlockingSlides(result, shouldBlockIssue);
|
|
818
832
|
if (blockingResult.summary.failedSlides > 0) {
|
|
819
833
|
throw new Error(formatValidationFailureForExport(blockingResult, exportLabel));
|