playwright-checkpoint 0.1.0-beta.0 → 0.4.0

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.
Files changed (43) hide show
  1. package/README.md +57 -0
  2. package/dist/{chunk-KG37WSYS.js → chunk-M3BRR3LT.js} +9 -3
  3. package/dist/{chunk-KG37WSYS.js.map → chunk-M3BRR3LT.js.map} +1 -1
  4. package/dist/{chunk-X5IPL32H.js → chunk-WXZOP7XI.js} +153 -35
  5. package/dist/chunk-WXZOP7XI.js.map +1 -0
  6. package/dist/{chunk-K5DX32TO.js → chunk-YUFXGGZM.js} +2 -2
  7. package/dist/cli/bin.cjs +2502 -2387
  8. package/dist/cli/bin.cjs.map +1 -1
  9. package/dist/cli/bin.js +3 -2
  10. package/dist/cli/bin.js.map +1 -1
  11. package/dist/cli/index.cjs +1405 -68
  12. package/dist/cli/index.cjs.map +1 -1
  13. package/dist/cli/index.d.cts +2 -2
  14. package/dist/cli/index.d.ts +2 -2
  15. package/dist/cli/index.js +3 -2
  16. package/dist/{core-CD4jHGgI.d.cts → core-6gyzs35M.d.ts} +2 -1
  17. package/dist/{core-CZvnc0rE.d.ts → core-Dd3WLuTs.d.cts} +2 -1
  18. package/dist/core.cjs +8 -2
  19. package/dist/core.cjs.map +1 -1
  20. package/dist/core.d.cts +2 -2
  21. package/dist/core.d.ts +2 -2
  22. package/dist/core.js +1 -1
  23. package/dist/{index-BjYQX_hK.d.ts → index-CvcgBzvl.d.ts} +1 -1
  24. package/dist/{index-Cabk31qi.d.cts → index-OQx9qcVO.d.cts} +1 -1
  25. package/dist/index.cjs +216 -42
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +5 -5
  28. package/dist/index.d.ts +5 -5
  29. package/dist/index.js +73 -19
  30. package/dist/index.js.map +1 -1
  31. package/dist/mcp/index.cjs +149 -35
  32. package/dist/mcp/index.cjs.map +1 -1
  33. package/dist/mcp/index.js +5 -5
  34. package/dist/mcp/index.js.map +1 -1
  35. package/dist/teardown.cjs +1409 -72
  36. package/dist/teardown.cjs.map +1 -1
  37. package/dist/teardown.js +3 -2
  38. package/dist/teardown.js.map +1 -1
  39. package/dist/{types-G7w4n8kR.d.cts → types-wX4eB9mb.d.cts} +16 -1
  40. package/dist/{types-G7w4n8kR.d.ts → types-wX4eB9mb.d.ts} +16 -1
  41. package/package.json +2 -1
  42. package/dist/chunk-X5IPL32H.js.map +0 -1
  43. /package/dist/{chunk-K5DX32TO.js.map → chunk-YUFXGGZM.js.map} +0 -0
@@ -1,3 +1,7 @@
1
+ import {
2
+ warn
3
+ } from "./chunk-M3BRR3LT.js";
4
+
1
5
  // src/report/story-utils.ts
2
6
  function groupByStory(runs) {
3
7
  const stories = /* @__PURE__ */ new Map();
@@ -630,6 +634,28 @@ function stripTags(value) {
630
634
  const stripped = value.replace(/\s+@[a-z0-9-]+/gi, " ").replace(/\s+/g, " ").trim();
631
635
  return stripped || value.trim() || "Untitled story";
632
636
  }
637
+ function articleTitle(run) {
638
+ const override = run.article?.title?.trim();
639
+ return override || stripTags(run.title);
640
+ }
641
+ function articleDescription(run) {
642
+ const description = run.article?.description?.trim();
643
+ return description ? description : null;
644
+ }
645
+ function articleSlug(run) {
646
+ const override = run.article?.slug?.trim();
647
+ return slugify2(override || stripTags(run.title));
648
+ }
649
+ function uniqueArticleSlug(baseSlug, usedSlugs) {
650
+ if (!usedSlugs.has(baseSlug)) {
651
+ return baseSlug;
652
+ }
653
+ let index = 1;
654
+ while (usedSlugs.has(`${baseSlug}-${index}`)) {
655
+ index += 1;
656
+ }
657
+ return `${baseSlug}-${index}`;
658
+ }
633
659
  function normalizeConfig(config) {
634
660
  return {
635
661
  storiesDir: typeof config.storiesDir === "string" ? config.storiesDir : ".",
@@ -640,7 +666,8 @@ function normalizeConfig(config) {
640
666
  footer: typeof config.footer === "string" ? config.footer : void 0,
641
667
  frontmatter: config.frontmatter === true || config.frontmatter === false || config.frontmatter != null && typeof config.frontmatter === "object" && !Array.isArray(config.frontmatter) ? config.frontmatter : false,
642
668
  imagePathPrefix: typeof config.imagePathPrefix === "string" ? config.imagePathPrefix : void 0,
643
- copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true
669
+ copyScreenshots: typeof config.copyScreenshots === "boolean" ? config.copyScreenshots : true,
670
+ requireExplicitStep: typeof config.requireExplicitStep === "boolean" ? config.requireExplicitStep : false
644
671
  };
645
672
  }
646
673
  function normalizeTags(tags) {
@@ -652,6 +679,9 @@ function shouldIncludeRun(run, config) {
652
679
  const runTags = new Set(normalizeTags(run.tags));
653
680
  return includeTags.some((tag) => runTags.has(tag));
654
681
  }
682
+ if (run.articles) {
683
+ return true;
684
+ }
655
685
  return run.checkpoints.some((checkpoint) => {
656
686
  const hasDescription = typeof checkpoint.description === "string" && checkpoint.description.trim().length > 0;
657
687
  return hasDescription || typeof checkpoint.step === "number";
@@ -770,17 +800,22 @@ async function materializeScreenshot(args) {
770
800
  if (!sourcePath) {
771
801
  return null;
772
802
  }
803
+ const cachedTargetPath = args.screenshotCopies?.get(sourcePath);
804
+ if (cachedTargetPath) {
805
+ return rewriteImagePath(args.markdownFile, cachedTargetPath, args.outputDir, args.config.imagePathPrefix);
806
+ }
773
807
  const extension = path2.extname(sourcePath) || ".png";
774
808
  const targetPath = path2.join(
775
809
  args.outputDir,
776
810
  args.config.screenshotsDir ?? "screenshots",
777
- args.storySlug,
778
- `${String(args.stepOrder).padStart(2, "0")}-${slugify2(args.checkpoint.name)}${extension}`
811
+ args.screenshotDirSlug,
812
+ `${args.screenshotFileSlug}${extension}`
779
813
  );
780
814
  try {
781
815
  if (args.config.copyScreenshots !== false) {
782
816
  await fs2.mkdir(path2.dirname(targetPath), { recursive: true });
783
817
  await fs2.copyFile(sourcePath, targetPath);
818
+ args.screenshotCopies?.set(sourcePath, targetPath);
784
819
  args.writtenFiles.add(targetPath);
785
820
  return rewriteImagePath(args.markdownFile, targetPath, args.outputDir, args.config.imagePathPrefix);
786
821
  }
@@ -800,10 +835,31 @@ function orderedCheckpoints(checkpoints) {
800
835
  });
801
836
  }
802
837
  async function buildSteps(args) {
803
- const checkpoints = orderedCheckpoints(args.run.checkpoints);
838
+ let checkpoints;
839
+ if (args.stepNames && args.stepNames.length > 0) {
840
+ const byName = /* @__PURE__ */ new Map();
841
+ for (const checkpoint of args.run.checkpoints) {
842
+ if (byName.has(checkpoint.name)) {
843
+ warn(`Duplicate checkpoint name "${checkpoint.name}" in "${args.run.title}". Using the latest capture for article generation.`);
844
+ }
845
+ byName.set(checkpoint.name, checkpoint);
846
+ }
847
+ checkpoints = args.stepNames.map((stepName) => {
848
+ const checkpoint = byName.get(stepName);
849
+ if (!checkpoint) {
850
+ warn(`Markdown article step "${stepName}" was not captured in "${args.run.title}". Skipping step.`);
851
+ return null;
852
+ }
853
+ return checkpoint;
854
+ }).filter((checkpoint) => checkpoint !== null);
855
+ } else {
856
+ checkpoints = orderedCheckpoints(args.run.checkpoints).filter(
857
+ (checkpoint) => !args.config.requireExplicitStep || typeof checkpoint.step === "number"
858
+ );
859
+ }
804
860
  const steps = [];
805
861
  for (const [index, checkpoint] of checkpoints.entries()) {
806
- const order = typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
862
+ const order = args.stepNames ? index + 1 : typeof checkpoint.step === "number" ? checkpoint.step : index + 1;
807
863
  steps.push({
808
864
  checkpoint,
809
865
  order,
@@ -812,12 +868,13 @@ async function buildSteps(args) {
812
868
  imagePath: await materializeScreenshot({
813
869
  run: args.run,
814
870
  checkpoint,
815
- storySlug: args.storySlug,
816
- stepOrder: order,
871
+ screenshotDirSlug: args.screenshotDirSlug,
872
+ screenshotFileSlug: args.stepNames ? checkpoint.slug : `${String(order).padStart(2, "0")}-${slugify2(checkpoint.name)}`,
817
873
  outputDir: args.outputDir,
818
874
  markdownFile: args.markdownFile,
819
875
  config: args.config,
820
- writtenFiles: args.writtenFiles
876
+ writtenFiles: args.writtenFiles,
877
+ screenshotCopies: args.screenshotCopies
821
878
  }),
822
879
  urlLabel: urlLabel(checkpoint.url),
823
880
  breadcrumbLabel: breadcrumbLabel(checkpoint.url),
@@ -828,13 +885,14 @@ async function buildSteps(args) {
828
885
  }
829
886
  function renderMarkdown(args) {
830
887
  const frontmatterFields = args.config.frontmatter === true || typeof args.config.frontmatter === "object" ? {
831
- title: args.title,
832
888
  project: args.run.project,
833
- testId: args.run.testId,
834
889
  tags: args.run.tags,
890
+ ...args.config.frontmatter && typeof args.config.frontmatter === "object" ? args.config.frontmatter : {},
891
+ ...args.article?.frontmatter ?? {},
892
+ testId: args.run.testId,
835
893
  startedAt: args.run.startedAt,
836
894
  generatedAt: args.generatedAt,
837
- ...args.config.frontmatter && typeof args.config.frontmatter === "object" ? args.config.frontmatter : {}
895
+ title: args.title
838
896
  } : null;
839
897
  const sections = args.steps.map((step) => {
840
898
  const lines = [`## Step ${step.order}: ${step.heading}`, ""];
@@ -854,6 +912,7 @@ function renderMarkdown(args) {
854
912
  const parts = [
855
913
  frontmatterFields ? serializeFrontmatter(frontmatterFields) : "",
856
914
  `# ${args.title}`,
915
+ args.description?.trim() ?? "",
857
916
  args.config.header ? args.config.header.trim() : "",
858
917
  sections,
859
918
  args.config.footer ? args.config.footer.trim() : ""
@@ -861,6 +920,42 @@ function renderMarkdown(args) {
861
920
  return `${parts.join("\n\n")}
862
921
  `;
863
922
  }
923
+ function resolveArticles(run) {
924
+ const multiArticles = (run.articles ?? []).filter((article) => Array.isArray(article.steps));
925
+ if (run.articles && multiArticles.length === 0) {
926
+ warn(`Markdown reporter received an empty articles array for "${run.title}". Falling back to the default single-article output.`);
927
+ }
928
+ if (multiArticles.length === 0) {
929
+ return [
930
+ {
931
+ title: articleTitle(run),
932
+ description: articleDescription(run),
933
+ slug: articleSlug(run),
934
+ metadata: run.article,
935
+ screenshotDirSlug: articleSlug(run)
936
+ }
937
+ ];
938
+ }
939
+ const usedSlugs = /* @__PURE__ */ new Set();
940
+ const screenshotDirSlug = slugify2(stripTags(run.title));
941
+ return multiArticles.map((article, index) => {
942
+ const fallbackSlug = `${screenshotDirSlug}-${index + 1}`;
943
+ const baseSlug = slugify2(article.slug?.trim() || fallbackSlug);
944
+ const uniqueSlug = uniqueArticleSlug(baseSlug, usedSlugs);
945
+ if (uniqueSlug !== baseSlug) {
946
+ warn(`Markdown article slug collision for "${article.title ?? run.title}" resolved as "${uniqueSlug}".`);
947
+ }
948
+ usedSlugs.add(uniqueSlug);
949
+ return {
950
+ title: article.title?.trim() || stripTags(run.title),
951
+ description: article.description?.trim() || null,
952
+ slug: uniqueSlug,
953
+ metadata: article,
954
+ stepNames: [...article.steps],
955
+ screenshotDirSlug
956
+ };
957
+ });
958
+ }
864
959
  var markdownReporter = {
865
960
  name: "markdown",
866
961
  description: "Generates one Markdown help article per captured story.",
@@ -872,37 +967,56 @@ var markdownReporter = {
872
967
  const stories = groupByStory(context.runs);
873
968
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
874
969
  const writtenFiles = /* @__PURE__ */ new Set();
970
+ const usedStorySlugs = /* @__PURE__ */ new Set();
971
+ const screenshotCopies = /* @__PURE__ */ new Map();
875
972
  let articleCount = 0;
876
973
  for (const [storyTitle, runs] of stories) {
877
974
  const primaryRun = choosePrimaryRun(runs, config.preferredProject);
878
975
  if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {
879
976
  continue;
880
977
  }
881
- const title = stripTags(storyTitle);
882
- const storySlug = slugify2(title);
883
- const markdownFile = path2.join(context.outputDir, config.storiesDir ?? ".", `${storySlug}.md`);
884
- const steps = await buildSteps({
885
- run: primaryRun,
886
- storySlug,
887
- outputDir: context.outputDir,
888
- markdownFile,
889
- config,
890
- writtenFiles
891
- });
892
- await fs2.mkdir(path2.dirname(markdownFile), { recursive: true });
893
- await fs2.writeFile(
894
- markdownFile,
895
- renderMarkdown({
896
- title,
897
- steps,
978
+ for (const article of resolveArticles(primaryRun)) {
979
+ let storySlug = article.slug;
980
+ if (usedStorySlugs.has(storySlug)) {
981
+ let index = 2;
982
+ while (usedStorySlugs.has(`${article.slug}-${index}`)) {
983
+ index += 1;
984
+ }
985
+ storySlug = `${article.slug}-${index}`;
986
+ warn(`Markdown article slug collision for "${article.title || storyTitle}" resolved as "${storySlug}".`);
987
+ }
988
+ usedStorySlugs.add(storySlug);
989
+ const markdownFile = path2.join(context.outputDir, config.storiesDir ?? ".", `${storySlug}.md`);
990
+ const steps = await buildSteps({
898
991
  run: primaryRun,
992
+ stepNames: article.stepNames,
993
+ screenshotDirSlug: article.screenshotDirSlug,
994
+ outputDir: context.outputDir,
995
+ markdownFile,
899
996
  config,
900
- generatedAt
901
- }),
902
- "utf8"
903
- );
904
- writtenFiles.add(markdownFile);
905
- articleCount += 1;
997
+ writtenFiles,
998
+ screenshotCopies
999
+ });
1000
+ if (steps.length === 0) {
1001
+ continue;
1002
+ }
1003
+ await fs2.mkdir(path2.dirname(markdownFile), { recursive: true });
1004
+ await fs2.writeFile(
1005
+ markdownFile,
1006
+ renderMarkdown({
1007
+ title: article.title,
1008
+ description: article.description,
1009
+ steps,
1010
+ run: primaryRun,
1011
+ article: article.metadata,
1012
+ config,
1013
+ generatedAt
1014
+ }),
1015
+ "utf8"
1016
+ );
1017
+ writtenFiles.add(markdownFile);
1018
+ articleCount += 1;
1019
+ }
906
1020
  }
907
1021
  return {
908
1022
  files: [...writtenFiles],
@@ -1370,6 +1484,8 @@ function toRunRecord(manifest, sourceManifestPath) {
1370
1484
  project: manifest.project,
1371
1485
  testId: manifest.testId,
1372
1486
  title: manifest.title,
1487
+ ...manifest.article ? { article: manifest.article } : {},
1488
+ ...manifest.articles ? { articles: manifest.articles } : {},
1373
1489
  tags: manifest.tags,
1374
1490
  startedAt: manifest.startedAt,
1375
1491
  checkpoints: manifest.checkpoints
@@ -1381,6 +1497,8 @@ function toManifest(run) {
1381
1497
  project: run.project,
1382
1498
  testId: run.testId,
1383
1499
  title: run.title,
1500
+ ...run.article ? { article: run.article } : {},
1501
+ ...run.articles ? { articles: run.articles } : {},
1384
1502
  tags: run.tags,
1385
1503
  startedAt: run.startedAt,
1386
1504
  checkpoints: run.checkpoints
@@ -1481,4 +1599,4 @@ export {
1481
1599
  loadRuns,
1482
1600
  runReporters
1483
1601
  };
1484
- //# sourceMappingURL=chunk-X5IPL32H.js.map
1602
+ //# sourceMappingURL=chunk-WXZOP7XI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/report/story-utils.ts","../src/report/html-reporter.ts","../src/report/markdown-reporter.ts","../src/report/mdx-reporter.ts","../src/report/annotate.ts","../src/report/index.ts"],"sourcesContent":["import type { RunRecord } from '../types';\n\nexport function groupByStory(runs: RunRecord[]): Map<string, RunRecord[]> {\n const stories = new Map<string, RunRecord[]>();\n\n for (const run of runs) {\n const existing = stories.get(run.title) ?? [];\n existing.push(run);\n stories.set(run.title, existing);\n }\n\n return stories;\n}\n\nexport function orderedCheckpointNames(runs: RunRecord[]): string[] {\n const names: string[] = [];\n const seen = new Set<string>();\n\n for (const run of runs) {\n for (const checkpoint of run.checkpoints) {\n if (seen.has(checkpoint.name)) {\n continue;\n }\n\n seen.add(checkpoint.name);\n names.push(checkpoint.name);\n }\n }\n\n return names;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { groupByStory, orderedCheckpointNames } from './story-utils';\n\ntype HtmlReporterConfig = {\n title?: string;\n projectOrder?: string[];\n};\n\nconst DEFAULT_PROJECT_ORDER = ['desktop-light', 'desktop-dark', 'mobile-light', 'mobile-dark'];\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;');\n}\n\nfunction slugify(value: string): string {\n return (\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction formatDateTime(isoDate: string): string {\n const date = new Date(isoDate);\n if (Number.isNaN(date.getTime())) {\n return isoDate;\n }\n\n return new Intl.DateTimeFormat('en-US', {\n dateStyle: 'medium',\n timeStyle: 'short',\n }).format(date);\n}\n\nfunction projectWeight(projectName: string, projectOrder: string[]): number {\n const index = projectOrder.indexOf(projectName);\n return index === -1 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction formatProjectLabel(projectName: string): string {\n const [device, mode] = projectName.split('-');\n if (!device || !mode) {\n return projectName;\n }\n\n const deviceLabel = device === 'desktop' ? 'Desktop' : device === 'mobile' ? 'Mobile' : device;\n const modeLabel = mode === 'light' ? 'Light' : mode === 'dark' ? 'Dark' : mode;\n return `${deviceLabel} / ${modeLabel}`;\n}\n\nfunction sortByProjectAndTime(a: RunRecord, b: RunRecord, projectOrder: string[]): number {\n const byProject = projectWeight(a.project, projectOrder) - projectWeight(b.project, projectOrder);\n if (byProject !== 0) {\n return byProject;\n }\n\n const byProjectName = a.project.localeCompare(b.project);\n if (byProjectName !== 0) {\n return byProjectName;\n }\n\n return new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime();\n}\n\nfunction getCollectorSummaryNumber(checkpoint: CheckpointRecord, collectorName: string, key: string): number | null {\n const value = checkpoint.collectors[collectorName]?.summary[key];\n return typeof value === 'number' ? value : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction highlightOverlayStyle(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const bounds = data?.highlightBounds;\n const imageSize = data?.imageSize;\n\n if (\n !bounds ||\n !imageSize ||\n typeof bounds.x !== 'number' ||\n typeof bounds.y !== 'number' ||\n typeof bounds.width !== 'number' ||\n typeof bounds.height !== 'number' ||\n typeof imageSize.width !== 'number' ||\n typeof imageSize.height !== 'number' ||\n imageSize.width <= 0 ||\n imageSize.height <= 0\n ) {\n return null;\n }\n\n const left = (bounds.x / imageSize.width) * 100;\n const top = (bounds.y / imageSize.height) * 100;\n const width = (bounds.width / imageSize.width) * 100;\n const height = (bounds.height / imageSize.height) * 100;\n\n return [\n `left:${left.toFixed(4)}%`,\n `top:${top.toFixed(4)}%`,\n `width:${width.toFixed(4)}%`,\n `height:${height.toFixed(4)}%`,\n ].join(';');\n}\n\nfunction highlightLabel(checkpoint: CheckpointRecord): string | null {\n const selector = screenshotData(checkpoint)?.highlightSelector;\n return typeof selector === 'string' && selector.trim().length > 0 ? `Focus: ${selector.trim()}` : null;\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction toEncodedHref(outputDir: string, filePath: string | null): string | null {\n if (!filePath) {\n return null;\n }\n\n const relativePath = path.relative(outputDir, filePath);\n return relativePath.split(path.sep).map(encodeURIComponent).join('/');\n}\n\nfunction getArtifactHref(\n run: RunRecord,\n checkpoint: CheckpointRecord,\n outputDir: string,\n collectorName: string,\n artifactName?: string,\n): string | null {\n const artifacts = checkpoint.collectors[collectorName]?.artifacts ?? [];\n const artifact = artifactName\n ? artifacts.find((entry) => entry.name === artifactName)\n : artifacts[0];\n\n if (!artifact?.path) {\n return null;\n }\n\n return toEncodedHref(outputDir, resolveArtifactPath(run, artifact.path));\n}\n\nfunction renderArtifactLinks(run: RunRecord, checkpoint: CheckpointRecord, outputDir: string): string {\n const links: Array<{ label: string; href: string | null }> = [\n { label: 'DOM HTML', href: getArtifactHref(run, checkpoint, outputDir, 'html', 'html') },\n { label: 'Axe', href: getArtifactHref(run, checkpoint, outputDir, 'axe', 'axe') },\n { label: 'Web Vitals', href: getArtifactHref(run, checkpoint, outputDir, 'web-vitals', 'web-vitals') },\n { label: 'Console', href: getArtifactHref(run, checkpoint, outputDir, 'console', 'console-errors') },\n { label: 'Failed Requests', href: getArtifactHref(run, checkpoint, outputDir, 'network', 'failed-requests') },\n ];\n\n return links\n .map((link) => {\n if (!link.href) {\n return `<span class=\"artifact disabled\">${escapeHtml(link.label)}</span>`;\n }\n\n return `<a class=\"artifact\" href=\"${link.href}\" target=\"_blank\" rel=\"noreferrer\">${escapeHtml(link.label)}</a>`;\n })\n .join('');\n}\n\nfunction renderCheckpointCard(run: RunRecord, checkpointName: string, outputDir: string): string {\n const checkpoint = run.checkpoints.find((entry) => entry.name === checkpointName);\n if (!checkpoint) {\n return `\n <article class=\"variant-card missing\">\n <header class=\"variant-card-header\">\n <div>\n <h5>${escapeHtml(formatProjectLabel(run.project))}</h5>\n <p>${escapeHtml(run.project)}</p>\n </div>\n <time>${escapeHtml(formatDateTime(run.startedAt))}</time>\n </header>\n <div class=\"empty-card\">No checkpoint captured for this run.</div>\n </article>\n `;\n }\n\n const screenshotHref = getArtifactHref(run, checkpoint, outputDir, 'screenshot', 'screenshot');\n const overlayStyle = highlightOverlayStyle(checkpoint);\n const focus = highlightLabel(checkpoint);\n const axeViolations = getCollectorSummaryNumber(checkpoint, 'axe', 'violations');\n const consoleErrors = getCollectorSummaryNumber(checkpoint, 'console', 'consoleErrorCount') ?? 0;\n const failedRequests = getCollectorSummaryNumber(checkpoint, 'network', 'failedRequestCount') ?? 0;\n\n return `\n <article class=\"variant-card\">\n <header class=\"variant-card-header\">\n <div>\n <h5>${escapeHtml(formatProjectLabel(run.project))}</h5>\n <p>${escapeHtml(run.project)}</p>\n </div>\n <time>${escapeHtml(formatDateTime(checkpoint.timestamp || run.startedAt))}</time>\n </header>\n <p class=\"page-meta\">\n <span>${escapeHtml(checkpoint.title || 'Untitled page')}</span>\n <span class=\"page-url\">${escapeHtml(checkpoint.url)}</span>\n </p>\n ${\n screenshotHref\n ? `<a class=\"thumbnail-link\" href=\"${screenshotHref}\" target=\"_blank\" rel=\"noreferrer\">\n <img src=\"${screenshotHref}\" alt=\"${escapeHtml(`${run.project} — ${checkpoint.name}`)}\" loading=\"lazy\" />\n ${overlayStyle ? `<span class=\"highlight-overlay\" style=\"${overlayStyle}\" aria-hidden=\"true\"></span>` : ''}\n ${focus ? `<span class=\"highlight-label\">${escapeHtml(focus)}</span>` : ''}\n </a>`\n : '<div class=\"empty-card\">Screenshot unavailable.</div>'\n }\n <div class=\"stats-grid\">\n <span><strong>${axeViolations ?? 'n/a'}</strong><small>Axe violations</small></span>\n <span><strong>${consoleErrors}</strong><small>Console errors</small></span>\n <span><strong>${failedRequests}</strong><small>Failed requests</small></span>\n </div>\n <div class=\"artifact-list\">${renderArtifactLinks(run, checkpoint, outputDir)}</div>\n </article>\n `;\n}\n\nfunction renderStorySection(title: string, runs: RunRecord[], outputDir: string): string {\n const checkpointNames = orderedCheckpointNames(runs);\n const environments = [...new Set(runs.map((run) => run.environment))].sort();\n const tags = [...new Set(runs.flatMap((run) => run.tags))].sort();\n\n const checkpointBlocks = checkpointNames\n .map(\n (checkpointName) => `\n <details class=\"accordion checkpoint-block\">\n <summary class=\"checkpoint-summary\">\n <span>${escapeHtml(checkpointName)}</span>\n <span class=\"checkpoint-meta\">${runs.length} variants</span>\n </summary>\n <div class=\"variant-grid\">\n ${runs.map((run) => renderCheckpointCard(run, checkpointName, outputDir)).join('')}\n </div>\n </details>\n `,\n )\n .join('');\n\n return `\n <details class=\"accordion story-block\" id=\"story-${slugify(title)}\" open>\n <summary class=\"story-summary\">\n <span class=\"story-title\">${escapeHtml(title)}</span>\n <span class=\"story-meta-chip\">${runs.length} run${runs.length === 1 ? '' : 's'}</span>\n </summary>\n <div class=\"story-body\">\n <div class=\"story-meta-row\">\n <span><strong>Projects</strong> ${escapeHtml(runs.map((run) => run.project).join(', '))}</span>\n <span><strong>Environments</strong> ${escapeHtml(environments.join(', ') || 'n/a')}</span>\n <span><strong>Tags</strong> ${escapeHtml(tags.join(', ') || 'none')}</span>\n </div>\n ${checkpointBlocks || '<p class=\"empty-state\">No checkpoints captured for this story.</p>'}\n </div>\n </details>\n `;\n}\n\nfunction buildHtmlReport(runs: RunRecord[], outputDir: string, config: HtmlReporterConfig): string {\n const groupedRuns = groupByStory(runs);\n const storyTitles = [...groupedRuns.keys()].sort((a, b) => a.localeCompare(b));\n const projectOrder = Array.isArray(config.projectOrder)\n ? config.projectOrder.filter((value): value is string => typeof value === 'string')\n : DEFAULT_PROJECT_ORDER;\n const generatedAt = new Date().toISOString();\n const reportTitle = typeof config.title === 'string' && config.title.trim() ? config.title.trim() : 'Playwright Checkpoint Report';\n\n for (const title of storyTitles) {\n groupedRuns.get(title)?.sort((a, b) => sortByProjectAndTime(a, b, projectOrder));\n }\n\n const navLinks = storyTitles\n .map((title) => `<a href=\"#story-${slugify(title)}\">${escapeHtml(title)}</a>`)\n .join('');\n\n const storySections = storyTitles\n .map((title) => renderStorySection(title, groupedRuns.get(title) ?? [], outputDir))\n .join('');\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(reportTitle)}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: #0b1020;\n --panel: rgba(15, 23, 42, 0.88);\n --panel-2: rgba(17, 25, 40, 0.98);\n --text: #e5eefb;\n --muted: #9fb3c8;\n --accent: #60a5fa;\n --accent-2: #22d3ee;\n --border: rgba(148, 163, 184, 0.18);\n --success: #34d399;\n --warning: #fbbf24;\n --danger: #fb7185;\n --shadow: 0 24px 64px rgba(2, 6, 23, 0.45);\n }\n * { box-sizing: border-box; }\n html { scroll-behavior: smooth; }\n body {\n margin: 0;\n min-height: 100vh;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background:\n radial-gradient(circle at top, rgba(96, 165, 250, 0.14), transparent 30%),\n linear-gradient(180deg, #07101f 0%, #0b1020 100%);\n color: var(--text);\n }\n a { color: inherit; }\n .page {\n width: min(1600px, calc(100vw - 32px));\n margin: 0 auto;\n padding: 28px 0 56px;\n }\n .hero {\n background: linear-gradient(180deg, rgba(15, 23, 42, 0.96), rgba(15, 23, 42, 0.84));\n border: 1px solid var(--border);\n border-radius: 24px;\n padding: 24px;\n box-shadow: var(--shadow);\n backdrop-filter: blur(18px);\n }\n .hero h1 {\n margin: 0;\n font-size: clamp(1.9rem, 2.6vw, 3rem);\n line-height: 1.1;\n }\n .hero p {\n margin: 10px 0 0;\n color: var(--muted);\n max-width: 72ch;\n line-height: 1.6;\n }\n .summary-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 18px;\n }\n .summary-pill {\n display: inline-flex;\n gap: 8px;\n align-items: center;\n padding: 9px 12px;\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.72);\n color: var(--muted);\n font-size: 0.92rem;\n }\n .summary-pill strong { color: var(--text); }\n .toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n gap: 16px;\n margin-top: 18px;\n padding-top: 18px;\n border-top: 1px solid var(--border);\n }\n .story-nav {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n }\n .story-nav a,\n .toolbar button,\n .artifact {\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.7);\n color: var(--text);\n text-decoration: none;\n padding: 8px 12px;\n font: inherit;\n font-size: 0.86rem;\n transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;\n }\n .toolbar button:hover,\n .story-nav a:hover,\n .artifact:hover {\n transform: translateY(-1px);\n border-color: rgba(96, 165, 250, 0.55);\n background: rgba(30, 41, 59, 0.96);\n cursor: pointer;\n }\n .content {\n display: grid;\n gap: 18px;\n margin-top: 22px;\n }\n .accordion {\n border: 1px solid var(--border);\n border-radius: 22px;\n background: var(--panel);\n box-shadow: var(--shadow);\n overflow: hidden;\n }\n .accordion summary {\n list-style: none;\n cursor: pointer;\n }\n .accordion summary::-webkit-details-marker { display: none; }\n .story-summary,\n .checkpoint-summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n }\n .story-summary {\n padding: 20px 22px;\n background: linear-gradient(180deg, rgba(15, 23, 42, 0.92), rgba(15, 23, 42, 0.74));\n }\n .story-title {\n font-size: 1.1rem;\n font-weight: 700;\n }\n .story-meta-chip,\n .checkpoint-meta {\n color: var(--muted);\n font-size: 0.84rem;\n white-space: nowrap;\n }\n .story-body {\n padding: 0 22px 22px;\n }\n .story-meta-row {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n color: var(--muted);\n font-size: 0.92rem;\n line-height: 1.5;\n margin: 4px 0 18px;\n }\n .story-meta-row strong { color: var(--text); margin-right: 6px; }\n .checkpoint-block {\n margin-top: 14px;\n border-radius: 18px;\n background: var(--panel-2);\n border: 1px solid rgba(148, 163, 184, 0.14);\n }\n .checkpoint-summary {\n padding: 16px 18px;\n font-weight: 600;\n background: rgba(15, 23, 42, 0.68);\n }\n .variant-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 14px;\n padding: 0 18px 18px;\n }\n .variant-card {\n display: grid;\n gap: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 18px;\n background: rgba(15, 23, 42, 0.72);\n padding: 16px;\n min-height: 100%;\n }\n .variant-card.missing {\n opacity: 0.72;\n border-style: dashed;\n }\n .variant-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 10px;\n }\n .variant-card-header h5 {\n margin: 0;\n font-size: 1rem;\n }\n .variant-card-header p,\n .variant-card-header time {\n margin: 4px 0 0;\n color: var(--muted);\n font-size: 0.83rem;\n }\n .page-meta {\n display: grid;\n gap: 4px;\n margin: 0;\n color: var(--muted);\n font-size: 0.9rem;\n }\n .page-url {\n overflow-wrap: anywhere;\n font-size: 0.82rem;\n }\n .thumbnail-link {\n position: relative;\n display: block;\n border-radius: 14px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(2, 6, 23, 0.65);\n }\n .thumbnail-link img {\n display: block;\n width: 100%;\n aspect-ratio: 16 / 10;\n object-fit: cover;\n }\n .highlight-overlay {\n position: absolute;\n border: 2px solid var(--danger);\n border-radius: 12px;\n background: rgba(251, 113, 133, 0.08);\n box-shadow: 0 0 0 999px rgba(251, 113, 133, 0.02);\n pointer-events: none;\n }\n .highlight-label {\n position: absolute;\n left: 12px;\n bottom: 12px;\n max-width: calc(100% - 24px);\n padding: 6px 9px;\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.9);\n border: 1px solid rgba(251, 113, 133, 0.35);\n color: #ffe4e6;\n font-size: 0.74rem;\n line-height: 1.3;\n overflow-wrap: anywhere;\n }\n .stats-grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n }\n .stats-grid span {\n display: grid;\n gap: 6px;\n padding: 10px 12px;\n border-radius: 14px;\n background: rgba(2, 6, 23, 0.42);\n border: 1px solid rgba(148, 163, 184, 0.12);\n }\n .stats-grid strong {\n font-size: 1.15rem;\n line-height: 1;\n }\n .stats-grid small {\n color: var(--muted);\n font-size: 0.76rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n }\n .artifact-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n .artifact.disabled {\n opacity: 0.45;\n pointer-events: none;\n }\n .empty-state,\n .empty-card {\n margin: 0;\n color: var(--muted);\n padding: 12px;\n border: 1px dashed rgba(148, 163, 184, 0.2);\n border-radius: 14px;\n background: rgba(2, 6, 23, 0.24);\n }\n @media (max-width: 720px) {\n .page {\n width: min(100vw - 20px, 1600px);\n padding-top: 18px;\n }\n .hero,\n .story-summary,\n .story-body,\n .checkpoint-summary,\n .variant-grid {\n padding-left: 16px;\n padding-right: 16px;\n }\n .variant-card-header,\n .story-summary,\n .checkpoint-summary,\n .toolbar {\n flex-direction: column;\n align-items: flex-start;\n }\n .stats-grid {\n grid-template-columns: 1fr;\n }\n }\n </style>\n</head>\n<body>\n <main class=\"page\">\n <section class=\"hero\">\n <h1>${escapeHtml(reportTitle)}</h1>\n <p>Explore checkpoint runs by story, inspect every project variant side-by-side, and jump directly to screenshots and generated artifacts.</p>\n <div class=\"summary-bar\">\n <span class=\"summary-pill\"><strong>Generated</strong> ${escapeHtml(formatDateTime(generatedAt))}</span>\n <span class=\"summary-pill\"><strong>Stories</strong> ${storyTitles.length}</span>\n <span class=\"summary-pill\"><strong>Runs</strong> ${runs.length}</span>\n <span class=\"summary-pill\"><strong>Output</strong> ${escapeHtml(outputDir)}</span>\n </div>\n <div class=\"toolbar\">\n <nav class=\"story-nav\">${navLinks || '<span class=\"summary-pill\">No stories found</span>'}</nav>\n <div class=\"toolbar-actions\">\n <button type=\"button\" data-action=\"expand-all\">Expand all</button>\n <button type=\"button\" data-action=\"collapse-all\">Collapse all</button>\n </div>\n </div>\n </section>\n <section class=\"content\">\n ${storySections || '<p class=\"empty-state\">No checkpoint manifests found.</p>'}\n </section>\n </main>\n <script>\n (() => {\n const details = Array.from(document.querySelectorAll('details.accordion'));\n const setAll = (open) => {\n details.forEach((entry) => {\n entry.open = open;\n });\n };\n document.querySelector('[data-action=\"expand-all\"]')?.addEventListener('click', () => setAll(true));\n document.querySelector('[data-action=\"collapse-all\"]')?.addEventListener('click', () => setAll(false));\n const revealHash = () => {\n if (!window.location.hash) {\n return;\n }\n const target = document.querySelector(window.location.hash);\n if (!(target instanceof HTMLElement)) {\n return;\n }\n const parentDetails = target.closest('details');\n if (parentDetails instanceof HTMLDetailsElement) {\n parentDetails.open = true;\n }\n target.scrollIntoView({ block: 'start', behavior: 'smooth' });\n };\n window.addEventListener('hashchange', revealHash);\n revealHash();\n })();\n </script>\n</body>\n</html>\n`;\n}\n\nexport const htmlReporter: ReportGenerator = {\n name: 'html',\n description: 'Responsive HTML report for checkpoint manifests.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const outputFile = path.join(context.outputDir, 'index.html');\n const html = buildHtmlReport(context.runs, context.outputDir, context.config as HtmlReporterConfig);\n\n await fs.mkdir(context.outputDir, { recursive: true });\n await fs.writeFile(outputFile, html, 'utf8');\n\n const storyCount = new Set(context.runs.map((run) => run.title)).size;\n\n return {\n files: [outputFile],\n summary: `Generated HTML report for ${storyCount} stor${storyCount === 1 ? 'y' : 'ies'} (${context.runs.length} run${context.runs.length === 1 ? '' : 's'}).`,\n };\n },\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { ArticleDefinition, ArticleMetadata, CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { warn } from '../core';\nimport { groupByStory } from './story-utils';\n\ntype MarkdownReporterConfig = {\n storiesDir?: string;\n screenshotsDir?: string;\n includeTags?: string[];\n preferredProject?: string;\n header?: string;\n footer?: string;\n frontmatter?: boolean | Record<string, unknown>;\n imagePathPrefix?: string;\n copyScreenshots?: boolean;\n requireExplicitStep?: boolean;\n};\n\ntype MarkdownStep = {\n checkpoint: CheckpointRecord;\n order: number;\n heading: string;\n description: string;\n imagePath: string | null;\n urlLabel: string;\n breadcrumbLabel: string | null;\n focusNote: string | null;\n};\n\ntype MarkdownArticle = {\n title: string;\n description: string | null;\n slug: string;\n metadata?: ArticleMetadata;\n stepNames?: string[];\n screenshotDirSlug: string;\n};\n\nfunction slugify(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction stripTags(value: string): string {\n const stripped = value.replace(/\\s+@[a-z0-9-]+/gi, ' ').replace(/\\s+/g, ' ').trim();\n return stripped || value.trim() || 'Untitled story';\n}\n\nfunction articleTitle(run: RunRecord): string {\n const override = run.article?.title?.trim();\n return override || stripTags(run.title);\n}\n\nfunction articleDescription(run: RunRecord): string | null {\n const description = run.article?.description?.trim();\n return description ? description : null;\n}\n\nfunction articleSlug(run: RunRecord): string {\n const override = run.article?.slug?.trim();\n return slugify(override || stripTags(run.title));\n}\n\nfunction uniqueArticleSlug(baseSlug: string, usedSlugs: Set<string>): string {\n if (!usedSlugs.has(baseSlug)) {\n return baseSlug;\n }\n\n let index = 1;\n while (usedSlugs.has(`${baseSlug}-${index}`)) {\n index += 1;\n }\n\n return `${baseSlug}-${index}`;\n}\n\nfunction normalizeConfig(config: Record<string, unknown>): MarkdownReporterConfig {\n return {\n storiesDir: typeof config.storiesDir === 'string' ? config.storiesDir : '.',\n screenshotsDir: typeof config.screenshotsDir === 'string' ? config.screenshotsDir : 'screenshots',\n includeTags: Array.isArray(config.includeTags)\n ? config.includeTags.filter((value): value is string => typeof value === 'string')\n : undefined,\n preferredProject: typeof config.preferredProject === 'string' ? config.preferredProject : undefined,\n header: typeof config.header === 'string' ? config.header : undefined,\n footer: typeof config.footer === 'string' ? config.footer : undefined,\n frontmatter:\n config.frontmatter === true ||\n config.frontmatter === false ||\n (config.frontmatter != null && typeof config.frontmatter === 'object' && !Array.isArray(config.frontmatter))\n ? (config.frontmatter as MarkdownReporterConfig['frontmatter'])\n : false,\n imagePathPrefix: typeof config.imagePathPrefix === 'string' ? config.imagePathPrefix : undefined,\n copyScreenshots: typeof config.copyScreenshots === 'boolean' ? config.copyScreenshots : true,\n requireExplicitStep: typeof config.requireExplicitStep === 'boolean' ? config.requireExplicitStep : false,\n };\n}\n\nfunction normalizeTags(tags: string[] | undefined): string[] {\n return (tags ?? []).map((tag) => tag.trim().toLowerCase()).filter(Boolean);\n}\n\nfunction shouldIncludeRun(run: RunRecord, config: MarkdownReporterConfig): boolean {\n const includeTags = normalizeTags(config.includeTags);\n if (includeTags.length > 0) {\n const runTags = new Set(normalizeTags(run.tags));\n return includeTags.some((tag) => runTags.has(tag));\n }\n\n if (run.articles) {\n return true;\n }\n\n return run.checkpoints.some((checkpoint) => {\n const hasDescription = typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0;\n return hasDescription || typeof checkpoint.step === 'number';\n });\n}\n\nfunction choosePrimaryRun(runs: RunRecord[], preferredProject?: string): RunRecord | null {\n if (runs.length === 0) {\n return null;\n }\n\n return [...runs].sort((left, right) => {\n const leftPreferred = preferredProject && left.project === preferredProject ? 0 : 1;\n const rightPreferred = preferredProject && right.project === preferredProject ? 0 : 1;\n if (leftPreferred !== rightPreferred) {\n return leftPreferred - rightPreferred;\n }\n\n const rightTime = new Date(right.startedAt).getTime();\n const leftTime = new Date(left.startedAt).getTime();\n if (rightTime !== leftTime) {\n return rightTime - leftTime;\n }\n\n return left.project.localeCompare(right.project);\n })[0] ?? null;\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction screenshotSourcePath(run: RunRecord, checkpoint: CheckpointRecord): string | null {\n const artifacts = checkpoint.collectors.screenshot?.artifacts ?? [];\n const artifact = artifacts.find((entry) => entry.name === 'screenshot') ?? artifacts[0];\n return artifact?.path ? resolveArtifactPath(run, artifact.path) : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction focusNote(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const selector = typeof data?.highlightSelector === 'string' ? data.highlightSelector.trim() : '';\n if (selector) {\n return `Focus: \\`${selector}\\``;\n }\n\n const bounds = data?.highlightBounds;\n if (\n bounds &&\n typeof bounds.x === 'number' &&\n typeof bounds.y === 'number' &&\n typeof bounds.width === 'number' &&\n typeof bounds.height === 'number'\n ) {\n return 'Focus: highlighted UI element.';\n }\n\n return null;\n}\n\nfunction urlLabel(url: string): string {\n try {\n const parsed = new URL(url);\n const value = `${parsed.pathname}${parsed.search}${parsed.hash}`;\n return value || '/';\n } catch {\n return url || '/';\n }\n}\n\nfunction breadcrumbLabel(url: string): string | null {\n const label = urlLabel(url);\n if (!label.startsWith('/')) {\n return null;\n }\n\n const [withoutQuery = label] = label.split('?');\n const [withoutHash = withoutQuery] = withoutQuery.split('#');\n\n const segments = withoutHash\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean)\n .map((segment) => decodeURIComponent(segment).replace(/[-_]+/g, ' '));\n\n return segments.length > 0 ? segments.join(' › ') : 'home';\n}\n\nfunction autoDescription(checkpoint: CheckpointRecord): string {\n const pageTitle = checkpoint.title.trim();\n const location = urlLabel(checkpoint.url);\n\n if (pageTitle) {\n return `This step captures **${pageTitle}** at \\`${location}\\`.`;\n }\n\n return `This step captures **${checkpoint.name}** at \\`${location}\\`.`;\n}\n\nfunction markdownRelativePath(fromFile: string, toFile: string): string {\n const relativePath = path.relative(path.dirname(fromFile), toFile).split(path.sep).join('/');\n if (relativePath.startsWith('.')) {\n return relativePath;\n }\n\n return `./${relativePath}`;\n}\n\nfunction rewriteImagePath(markdownFile: string, imageFile: string, outputDir: string, prefix?: string): string {\n const relativePath = path.relative(outputDir, imageFile).split(path.sep).join('/');\n if (prefix) {\n return `${prefix.replace(/\\/+$/g, '')}/${relativePath.replace(/^\\/+/, '')}`;\n }\n\n return markdownRelativePath(markdownFile, imageFile);\n}\n\nfunction yamlScalar(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction serializeFrontmatter(fields: Record<string, unknown>): string {\n const lines = ['---'];\n\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n lines.push(`${key}:`);\n if (value.length === 0) {\n lines.push(' []');\n continue;\n }\n\n for (const item of value) {\n lines.push(` - ${yamlScalar(item)}`);\n }\n continue;\n }\n\n lines.push(`${key}: ${yamlScalar(value)}`);\n }\n\n lines.push('---', '');\n return lines.join('\\n');\n}\n\nasync function materializeScreenshot(args: {\n run: RunRecord;\n checkpoint: CheckpointRecord;\n screenshotDirSlug: string;\n screenshotFileSlug: string;\n outputDir: string;\n markdownFile: string;\n config: MarkdownReporterConfig;\n writtenFiles: Set<string>;\n screenshotCopies?: Map<string, string>;\n}): Promise<string | null> {\n const sourcePath = screenshotSourcePath(args.run, args.checkpoint);\n if (!sourcePath) {\n return null;\n }\n\n const cachedTargetPath = args.screenshotCopies?.get(sourcePath);\n if (cachedTargetPath) {\n return rewriteImagePath(args.markdownFile, cachedTargetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n const extension = path.extname(sourcePath) || '.png';\n const targetPath = path.join(\n args.outputDir,\n args.config.screenshotsDir ?? 'screenshots',\n args.screenshotDirSlug,\n `${args.screenshotFileSlug}${extension}`,\n );\n\n try {\n if (args.config.copyScreenshots !== false) {\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\n await fs.copyFile(sourcePath, targetPath);\n args.screenshotCopies?.set(sourcePath, targetPath);\n args.writtenFiles.add(targetPath);\n return rewriteImagePath(args.markdownFile, targetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n return rewriteImagePath(args.markdownFile, sourcePath, args.outputDir, args.config.imagePathPrefix);\n } catch {\n return null;\n }\n}\n\nfunction orderedCheckpoints(checkpoints: CheckpointRecord[]): CheckpointRecord[] {\n return [...checkpoints].sort((left, right) => {\n const leftOrder = typeof left.step === 'number' ? left.step : Number.MAX_SAFE_INTEGER;\n const rightOrder = typeof right.step === 'number' ? right.step : Number.MAX_SAFE_INTEGER;\n if (leftOrder !== rightOrder) {\n return leftOrder - rightOrder;\n }\n\n return checkpoints.indexOf(left) - checkpoints.indexOf(right);\n });\n}\n\nasync function buildSteps(args: {\n run: RunRecord;\n stepNames?: string[];\n screenshotDirSlug: string;\n outputDir: string;\n markdownFile: string;\n config: MarkdownReporterConfig;\n writtenFiles: Set<string>;\n screenshotCopies?: Map<string, string>;\n}): Promise<MarkdownStep[]> {\n let checkpoints: CheckpointRecord[];\n if (args.stepNames && args.stepNames.length > 0) {\n const byName = new Map<string, CheckpointRecord>();\n\n for (const checkpoint of args.run.checkpoints) {\n if (byName.has(checkpoint.name)) {\n warn(`Duplicate checkpoint name \"${checkpoint.name}\" in \"${args.run.title}\". Using the latest capture for article generation.`);\n }\n\n byName.set(checkpoint.name, checkpoint);\n }\n\n checkpoints = args.stepNames\n .map((stepName) => {\n const checkpoint = byName.get(stepName);\n if (!checkpoint) {\n warn(`Markdown article step \"${stepName}\" was not captured in \"${args.run.title}\". Skipping step.`);\n return null;\n }\n\n return checkpoint;\n })\n .filter((checkpoint): checkpoint is CheckpointRecord => checkpoint !== null);\n } else {\n checkpoints = orderedCheckpoints(args.run.checkpoints).filter(\n (checkpoint) => !args.config.requireExplicitStep || typeof checkpoint.step === 'number',\n );\n }\n\n const steps: MarkdownStep[] = [];\n\n for (const [index, checkpoint] of checkpoints.entries()) {\n const order = args.stepNames ? index + 1 : typeof checkpoint.step === 'number' ? checkpoint.step : index + 1;\n steps.push({\n checkpoint,\n order,\n heading: checkpoint.name,\n description:\n typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0\n ? checkpoint.description.trim()\n : autoDescription(checkpoint),\n imagePath: await materializeScreenshot({\n run: args.run,\n checkpoint,\n screenshotDirSlug: args.screenshotDirSlug,\n screenshotFileSlug: args.stepNames ? checkpoint.slug : `${String(order).padStart(2, '0')}-${slugify(checkpoint.name)}`,\n outputDir: args.outputDir,\n markdownFile: args.markdownFile,\n config: args.config,\n writtenFiles: args.writtenFiles,\n screenshotCopies: args.screenshotCopies,\n }),\n urlLabel: urlLabel(checkpoint.url),\n breadcrumbLabel: breadcrumbLabel(checkpoint.url),\n focusNote: focusNote(checkpoint),\n });\n }\n\n return steps;\n}\n\nfunction renderMarkdown(args: {\n title: string;\n description?: string | null;\n steps: MarkdownStep[];\n run: RunRecord;\n article?: ArticleMetadata;\n config: MarkdownReporterConfig;\n generatedAt: string;\n}): string {\n const frontmatterFields =\n args.config.frontmatter === true || typeof args.config.frontmatter === 'object'\n ? {\n project: args.run.project,\n tags: args.run.tags,\n ...(args.config.frontmatter && typeof args.config.frontmatter === 'object' ? args.config.frontmatter : {}),\n ...(args.article?.frontmatter ?? {}),\n testId: args.run.testId,\n startedAt: args.run.startedAt,\n generatedAt: args.generatedAt,\n title: args.title,\n }\n : null;\n\n const sections = args.steps\n .map((step) => {\n const lines = [`## Step ${step.order}: ${step.heading}`, ''];\n\n if (step.imagePath) {\n lines.push(`![${step.checkpoint.title || step.heading}](${step.imagePath})`, '');\n }\n\n lines.push(`**URL:** \\`${step.urlLabel}\\``);\n if (step.breadcrumbLabel) {\n lines.push('', `**Breadcrumb:** ${step.breadcrumbLabel}`);\n }\n\n if (step.focusNote) {\n lines.push('', `> ${step.focusNote}`);\n }\n\n lines.push('', step.description);\n\n return lines.join('\\n');\n })\n .join('\\n\\n');\n\n const parts = [\n frontmatterFields ? serializeFrontmatter(frontmatterFields) : '',\n `# ${args.title}`,\n args.description?.trim() ?? '',\n args.config.header ? args.config.header.trim() : '',\n sections,\n args.config.footer ? args.config.footer.trim() : '',\n ].filter((value) => value.trim().length > 0);\n\n return `${parts.join('\\n\\n')}\\n`;\n}\n\nfunction resolveArticles(run: RunRecord): MarkdownArticle[] {\n const multiArticles = (run.articles ?? []).filter((article): article is ArticleDefinition => Array.isArray(article.steps));\n if (run.articles && multiArticles.length === 0) {\n warn(`Markdown reporter received an empty articles array for \"${run.title}\". Falling back to the default single-article output.`);\n }\n\n if (multiArticles.length === 0) {\n return [\n {\n title: articleTitle(run),\n description: articleDescription(run),\n slug: articleSlug(run),\n metadata: run.article,\n screenshotDirSlug: articleSlug(run),\n },\n ];\n }\n\n const usedSlugs = new Set<string>();\n const screenshotDirSlug = slugify(stripTags(run.title));\n\n return multiArticles.map((article, index) => {\n const fallbackSlug = `${screenshotDirSlug}-${index + 1}`;\n const baseSlug = slugify(article.slug?.trim() || fallbackSlug);\n const uniqueSlug = uniqueArticleSlug(baseSlug, usedSlugs);\n if (uniqueSlug !== baseSlug) {\n warn(`Markdown article slug collision for \"${article.title ?? run.title}\" resolved as \"${uniqueSlug}\".`);\n }\n usedSlugs.add(uniqueSlug);\n\n return {\n title: article.title?.trim() || stripTags(run.title),\n description: article.description?.trim() || null,\n slug: uniqueSlug,\n metadata: article,\n stepNames: [...article.steps],\n screenshotDirSlug,\n };\n });\n}\n\nexport const markdownReporter: ReportGenerator = {\n name: 'markdown',\n description: 'Generates one Markdown help article per captured story.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const config = normalizeConfig(context.config);\n const stories = groupByStory(context.runs);\n const generatedAt = new Date().toISOString();\n const writtenFiles = new Set<string>();\n const usedStorySlugs = new Set<string>();\n const screenshotCopies = new Map<string, string>();\n let articleCount = 0;\n\n for (const [storyTitle, runs] of stories) {\n const primaryRun = choosePrimaryRun(runs, config.preferredProject);\n if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {\n continue;\n }\n\n for (const article of resolveArticles(primaryRun)) {\n let storySlug = article.slug;\n if (usedStorySlugs.has(storySlug)) {\n let index = 2;\n while (usedStorySlugs.has(`${article.slug}-${index}`)) {\n index += 1;\n }\n storySlug = `${article.slug}-${index}`;\n warn(`Markdown article slug collision for \"${article.title || storyTitle}\" resolved as \"${storySlug}\".`);\n }\n\n usedStorySlugs.add(storySlug);\n const markdownFile = path.join(context.outputDir, config.storiesDir ?? '.', `${storySlug}.md`);\n const steps = await buildSteps({\n run: primaryRun,\n stepNames: article.stepNames,\n screenshotDirSlug: article.screenshotDirSlug,\n outputDir: context.outputDir,\n markdownFile,\n config,\n writtenFiles,\n screenshotCopies,\n });\n if (steps.length === 0) {\n continue;\n }\n\n await fs.mkdir(path.dirname(markdownFile), { recursive: true });\n await fs.writeFile(\n markdownFile,\n renderMarkdown({\n title: article.title,\n description: article.description,\n steps,\n run: primaryRun,\n article: article.metadata,\n config,\n generatedAt,\n }),\n 'utf8',\n );\n\n writtenFiles.add(markdownFile);\n articleCount += 1;\n }\n }\n\n return {\n files: [...writtenFiles],\n summary: `Generated ${articleCount} Markdown article${articleCount === 1 ? '' : 's'}.`,\n };\n },\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CheckpointRecord, ReportGenerator, RunRecord, ScreenshotCollectorData } from '../types';\nimport { groupByStory } from './story-utils';\n\ntype MdxReporterConfig = {\n storiesDir?: string;\n screenshotsDir?: string;\n includeTags?: string[];\n preferredProject?: string;\n imagePathPrefix?: string;\n copyScreenshots?: boolean;\n componentImportPath?: string;\n};\n\ntype MdxVariant = {\n project: string;\n projectLabel: string;\n imagePath: string | null;\n imageAlt: string;\n};\n\ntype MdxStep = {\n checkpoint: CheckpointRecord;\n order: number;\n title: string;\n description: string;\n focusNote: string | null;\n variants: MdxVariant[];\n};\n\nconst DEFAULT_PROJECT_ORDER = ['desktop-light', 'desktop-dark', 'mobile-light', 'mobile-dark'];\n\nfunction slugify(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'story'\n );\n}\n\nfunction stripTags(value: string): string {\n const stripped = value.replace(/\\s+@[a-z0-9-]+/gi, ' ').replace(/\\s+/g, ' ').trim();\n return stripped || value.trim() || 'Untitled story';\n}\n\nfunction normalizeConfig(config: Record<string, unknown>): MdxReporterConfig {\n return {\n storiesDir: typeof config.storiesDir === 'string' ? config.storiesDir : '.',\n screenshotsDir: typeof config.screenshotsDir === 'string' ? config.screenshotsDir : 'screenshots',\n includeTags: Array.isArray(config.includeTags)\n ? config.includeTags.filter((value): value is string => typeof value === 'string')\n : undefined,\n preferredProject: typeof config.preferredProject === 'string' ? config.preferredProject : undefined,\n imagePathPrefix: typeof config.imagePathPrefix === 'string' ? config.imagePathPrefix : undefined,\n copyScreenshots: typeof config.copyScreenshots === 'boolean' ? config.copyScreenshots : true,\n componentImportPath:\n typeof config.componentImportPath === 'string' ? config.componentImportPath : 'playwright-checkpoint/components',\n };\n}\n\nfunction normalizeTags(tags: string[] | undefined): string[] {\n return (tags ?? []).map((tag) => tag.trim().toLowerCase()).filter(Boolean);\n}\n\nfunction frontmatterTags(tags: string[]): string[] {\n return normalizeTags(tags).map((tag) => tag.replace(/^@+/, '')).filter(Boolean);\n}\n\nfunction shouldIncludeRun(run: RunRecord, config: MdxReporterConfig): boolean {\n const includeTags = normalizeTags(config.includeTags);\n if (includeTags.length > 0) {\n const runTags = new Set(normalizeTags(run.tags));\n return includeTags.some((tag) => runTags.has(tag));\n }\n\n return run.checkpoints.some((checkpoint) => {\n const hasDescription = typeof checkpoint.description === 'string' && checkpoint.description.trim().length > 0;\n return hasDescription || typeof checkpoint.step === 'number';\n });\n}\n\nfunction choosePrimaryRun(runs: RunRecord[], preferredProject?: string): RunRecord | null {\n if (runs.length === 0) {\n return null;\n }\n\n return [...runs].sort((left, right) => {\n const leftPreferred = preferredProject && left.project === preferredProject ? 0 : 1;\n const rightPreferred = preferredProject && right.project === preferredProject ? 0 : 1;\n if (leftPreferred !== rightPreferred) {\n return leftPreferred - rightPreferred;\n }\n\n const rightTime = new Date(right.startedAt).getTime();\n const leftTime = new Date(left.startedAt).getTime();\n if (rightTime !== leftTime) {\n return rightTime - leftTime;\n }\n\n return left.project.localeCompare(right.project);\n })[0] ?? null;\n}\n\nfunction orderedCheckpoints(checkpoints: CheckpointRecord[]): CheckpointRecord[] {\n return [...checkpoints].sort((left, right) => {\n const leftOrder = typeof left.step === 'number' ? left.step : Number.MAX_SAFE_INTEGER;\n const rightOrder = typeof right.step === 'number' ? right.step : Number.MAX_SAFE_INTEGER;\n if (leftOrder !== rightOrder) {\n return leftOrder - rightOrder;\n }\n\n return checkpoints.indexOf(left) - checkpoints.indexOf(right);\n });\n}\n\nfunction resolveArtifactPath(run: RunRecord, artifactPath: string): string {\n return path.isAbsolute(artifactPath) ? artifactPath : path.resolve(path.dirname(run.sourceManifestPath), artifactPath);\n}\n\nfunction screenshotSourcePath(run: RunRecord, checkpoint: CheckpointRecord): string | null {\n const artifacts = checkpoint.collectors.screenshot?.artifacts ?? [];\n const artifact = artifacts.find((entry) => entry.name === 'screenshot') ?? artifacts[0];\n return artifact?.path ? resolveArtifactPath(run, artifact.path) : null;\n}\n\nfunction screenshotData(checkpoint: CheckpointRecord): Partial<ScreenshotCollectorData> | null {\n const data = checkpoint.collectors.screenshot?.data;\n return data && typeof data === 'object' ? (data as Partial<ScreenshotCollectorData>) : null;\n}\n\nfunction focusNote(checkpoint: CheckpointRecord): string | null {\n const data = screenshotData(checkpoint);\n const selector = typeof data?.highlightSelector === 'string' ? data.highlightSelector.trim() : '';\n if (selector) {\n return `Focus: \\`${selector}\\``;\n }\n\n const bounds = data?.highlightBounds;\n if (\n bounds &&\n typeof bounds.x === 'number' &&\n typeof bounds.y === 'number' &&\n typeof bounds.width === 'number' &&\n typeof bounds.height === 'number'\n ) {\n return 'Focus: highlighted UI element.';\n }\n\n return null;\n}\n\nfunction urlLabel(url: string): string {\n try {\n const parsed = new URL(url);\n const value = `${parsed.pathname}${parsed.search}${parsed.hash}`;\n return value || '/';\n } catch {\n return url || '/';\n }\n}\n\nfunction autoDescription(checkpoint: CheckpointRecord): string {\n const pageTitle = checkpoint.title.trim();\n const location = urlLabel(checkpoint.url);\n\n if (pageTitle) {\n return `This step captures **${pageTitle}** at \\`${location}\\`.`;\n }\n\n return `This step captures **${checkpoint.name}** at \\`${location}\\`.`;\n}\n\nfunction markdownRelativePath(fromFile: string, toFile: string): string {\n const relativePath = path.relative(path.dirname(fromFile), toFile).split(path.sep).join('/');\n if (relativePath.startsWith('.')) {\n return relativePath;\n }\n\n return `./${relativePath}`;\n}\n\nfunction rewriteImagePath(mdxFile: string, imageFile: string, outputDir: string, prefix?: string): string {\n const relativePath = path.relative(outputDir, imageFile).split(path.sep).join('/');\n if (prefix) {\n return `${prefix.replace(/\\/+$/g, '')}/${relativePath.replace(/^\\/+/, '')}`;\n }\n\n return markdownRelativePath(mdxFile, imageFile);\n}\n\nfunction yamlScalar(value: unknown): string {\n return JSON.stringify(value);\n}\n\nfunction serializeFrontmatter(fields: Record<string, unknown>): string {\n const lines = ['---'];\n\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n lines.push(`${key}:`);\n if (value.length === 0) {\n lines.push(' []');\n continue;\n }\n\n for (const item of value) {\n lines.push(` - ${yamlScalar(item)}`);\n }\n continue;\n }\n\n lines.push(`${key}: ${yamlScalar(value)}`);\n }\n\n lines.push('---', '');\n return lines.join('\\n');\n}\n\nfunction quoteJsx(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction projectWeight(projectName: string): number {\n const index = DEFAULT_PROJECT_ORDER.indexOf(projectName);\n return index === -1 ? Number.MAX_SAFE_INTEGER : index;\n}\n\nfunction formatProjectLabel(projectName: string): string {\n const [device, mode] = projectName.split('-');\n if (!device || !mode) {\n return projectName;\n }\n\n const deviceLabel = device === 'desktop' ? 'Desktop' : device === 'mobile' ? 'Mobile' : device;\n const modeLabel = mode === 'light' ? 'Light' : mode === 'dark' ? 'Dark' : mode;\n return `${deviceLabel} / ${modeLabel}`;\n}\n\nfunction sortRunsForVariants(runs: RunRecord[]): RunRecord[] {\n return [...runs].sort((left, right) => {\n const byWeight = projectWeight(left.project) - projectWeight(right.project);\n if (byWeight !== 0) {\n return byWeight;\n }\n\n return left.project.localeCompare(right.project);\n });\n}\n\nfunction findMatchingCheckpoint(run: RunRecord, baseCheckpoint: CheckpointRecord, fallbackIndex: number): CheckpointRecord | null {\n const checkpoints = orderedCheckpoints(run.checkpoints);\n\n if (typeof baseCheckpoint.step === 'number') {\n const byStep = checkpoints.find((entry) => entry.step === baseCheckpoint.step);\n if (byStep) {\n return byStep;\n }\n }\n\n const byName = checkpoints.find((entry) => entry.name === baseCheckpoint.name);\n if (byName) {\n return byName;\n }\n\n return checkpoints[fallbackIndex] ?? null;\n}\n\nasync function materializeScreenshot(args: {\n run: RunRecord;\n checkpoint: CheckpointRecord;\n storySlug: string;\n stepOrder: number;\n outputDir: string;\n mdxFile: string;\n config: MdxReporterConfig;\n writtenFiles: Set<string>;\n}): Promise<string | null> {\n const sourcePath = screenshotSourcePath(args.run, args.checkpoint);\n if (!sourcePath) {\n return null;\n }\n\n const extension = path.extname(sourcePath) || '.png';\n const targetPath = path.join(\n args.outputDir,\n args.config.screenshotsDir ?? 'screenshots',\n args.storySlug,\n `${String(args.stepOrder).padStart(2, '0')}-${slugify(args.run.project)}-${slugify(args.checkpoint.name)}${extension}`,\n );\n\n try {\n if (args.config.copyScreenshots !== false) {\n await fs.mkdir(path.dirname(targetPath), { recursive: true });\n await fs.copyFile(sourcePath, targetPath);\n args.writtenFiles.add(targetPath);\n return rewriteImagePath(args.mdxFile, targetPath, args.outputDir, args.config.imagePathPrefix);\n }\n\n return rewriteImagePath(args.mdxFile, sourcePath, args.outputDir, args.config.imagePathPrefix);\n } catch {\n return null;\n }\n}\n\nasync function buildSteps(args: {\n runs: RunRecord[];\n primaryRun: RunRecord;\n storySlug: string;\n outputDir: string;\n mdxFile: string;\n config: MdxReporterConfig;\n writtenFiles: Set<string>;\n}): Promise<MdxStep[]> {\n const baseCheckpoints = orderedCheckpoints(args.primaryRun.checkpoints);\n const sortedRuns = sortRunsForVariants(args.runs);\n const steps: MdxStep[] = [];\n\n for (const [index, checkpoint] of baseCheckpoints.entries()) {\n const order = typeof checkpoint.step === 'number' ? checkpoint.step : index + 1;\n const variants: MdxVariant[] = [];\n const matchedCheckpoints: CheckpointRecord[] = [];\n\n for (const run of sortedRuns) {\n const variantCheckpoint = findMatchingCheckpoint(run, checkpoint, index);\n if (!variantCheckpoint) {\n continue;\n }\n\n matchedCheckpoints.push(variantCheckpoint);\n variants.push({\n project: run.project,\n projectLabel: formatProjectLabel(run.project),\n imagePath: await materializeScreenshot({\n run,\n checkpoint: variantCheckpoint,\n storySlug: args.storySlug,\n stepOrder: order,\n outputDir: args.outputDir,\n mdxFile: args.mdxFile,\n config: args.config,\n writtenFiles: args.writtenFiles,\n }),\n imageAlt: variantCheckpoint.title || `${checkpoint.name} (${formatProjectLabel(run.project)})`,\n });\n }\n\n const descriptionSource = matchedCheckpoints.find(\n (entry) => typeof entry.description === 'string' && entry.description.trim().length > 0,\n ) ?? checkpoint;\n const stepFocus = matchedCheckpoints.map((entry) => focusNote(entry)).find((value): value is string => Boolean(value)) ?? null;\n\n steps.push({\n checkpoint,\n order,\n title: checkpoint.name,\n description:\n typeof descriptionSource.description === 'string' && descriptionSource.description.trim().length > 0\n ? descriptionSource.description.trim()\n : autoDescription(descriptionSource),\n focusNote: stepFocus,\n variants,\n });\n }\n\n return steps;\n}\n\nfunction renderVariantTabs(variants: MdxVariant[]): string {\n if (variants.length === 0) {\n return '';\n }\n\n if (variants.length === 1) {\n const [variant] = variants;\n if (!variant?.imagePath) {\n return '';\n }\n\n return `<Screenshot src={${quoteJsx(variant.imagePath)}} alt={${quoteJsx(variant.imageAlt)}} />`;\n }\n\n const tabs = variants\n .map((variant) => {\n const lines = [` <DeviceTab label={${quoteJsx(variant.projectLabel)}}>`];\n if (variant.imagePath) {\n lines.push(` <Screenshot src={${quoteJsx(variant.imagePath)}} alt={${quoteJsx(variant.imageAlt)}} />`);\n } else {\n lines.push(` <p>No screenshot captured for ${variant.projectLabel}.</p>`);\n }\n lines.push(' </DeviceTab>');\n return lines.join('\\n');\n })\n .join('\\n');\n\n return `<DeviceTabs>\\n${tabs}\\n</DeviceTabs>`;\n}\n\nfunction renderStep(step: MdxStep): string {\n const lines = [` <Step number={${step.order}} title={${quoteJsx(step.title)}}>`];\n const variantBlock = renderVariantTabs(step.variants);\n if (variantBlock) {\n lines.push(` ${variantBlock.replace(/\\n/g, '\\n ')}`, '');\n }\n\n if (step.focusNote) {\n lines.push(` ${step.focusNote}`, '');\n }\n\n lines.push(` ${step.description}`, ' </Step>');\n return lines.join('\\n');\n}\n\nfunction renderMdx(args: {\n title: string;\n steps: MdxStep[];\n runs: RunRecord[];\n config: MdxReporterConfig;\n generatedAt: string;\n}): string {\n const importNames = new Set(['Screenshot', 'StepList', 'Step']);\n if (args.steps.some((step) => step.variants.length > 1)) {\n importNames.add('DeviceTabs');\n importNames.add('DeviceTab');\n }\n\n const frontmatter = serializeFrontmatter({\n title: args.title,\n tags: frontmatterTags([...new Set(args.runs.flatMap((run) => run.tags))]),\n generatedAt: args.generatedAt,\n projects: [...new Set(args.runs.map((run) => run.project))],\n });\n\n const stepBlocks = args.steps.map(renderStep).join('\\n\\n');\n\n return `${frontmatter}import { ${[...importNames].join(', ')} } from '${args.config.componentImportPath}';\\n\\n<StepList>\\n${stepBlocks}\\n</StepList>\\n`;\n}\n\nexport const mdxReporter: ReportGenerator = {\n name: 'mdx',\n description: 'Generates one MDX help article per captured story.',\n\n validateConfig(config): boolean {\n return config != null && typeof config === 'object' && !Array.isArray(config);\n },\n\n async generate(context) {\n const config = normalizeConfig(context.config);\n const stories = groupByStory(context.runs);\n const generatedAt = new Date().toISOString();\n const writtenFiles = new Set<string>();\n let articleCount = 0;\n\n for (const [storyTitle, runs] of stories) {\n const primaryRun = choosePrimaryRun(runs, config.preferredProject);\n if (!primaryRun || !shouldIncludeRun(primaryRun, config)) {\n continue;\n }\n\n const title = stripTags(storyTitle);\n const storySlug = slugify(title);\n const mdxFile = path.join(context.outputDir, config.storiesDir ?? '.', `${storySlug}.mdx`);\n const steps = await buildSteps({\n runs,\n primaryRun,\n storySlug,\n outputDir: context.outputDir,\n mdxFile,\n config,\n writtenFiles,\n });\n\n await fs.mkdir(path.dirname(mdxFile), { recursive: true });\n await fs.writeFile(\n mdxFile,\n renderMdx({\n title,\n steps,\n runs,\n config,\n generatedAt,\n }),\n 'utf8',\n );\n\n writtenFiles.add(mdxFile);\n articleCount += 1;\n }\n\n return {\n files: [...writtenFiles],\n summary: `Generated ${articleCount} MDX article${articleCount === 1 ? '' : 's'}.`,\n };\n },\n};\n","import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport type { BoundingBox } from '../types';\n\ntype SharpLike = {\n (input: string | Buffer): {\n metadata(): Promise<{ width?: number; height?: number }>;\n composite(items: Array<{ input: Buffer; top?: number; left?: number }>): {\n png(): {\n toFile(filePath: string): Promise<void>;\n };\n };\n };\n};\n\nconst require = (() => {\n try {\n return Function('return require')() as NodeRequire;\n } catch {\n return createRequire(path.join(process.cwd(), 'playwright-checkpoint-annotate-runtime.cjs'));\n }\n})();\n\nasync function loadSharp(): Promise<SharpLike | null> {\n try {\n const loaded = require('sharp') as SharpLike | { default?: SharpLike };\n return typeof loaded === 'function' ? loaded : loaded.default ?? null;\n } catch {\n return null;\n }\n}\n\nfunction buildOverlaySvg(width: number, height: number, bounds: BoundingBox): Buffer {\n const strokeWidth = Math.max(2, Math.round(Math.min(width, height) * 0.005));\n const radius = Math.max(6, Math.round(Math.min(bounds.width, bounds.height) * 0.08));\n\n const svg = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n <rect\n x=\"${bounds.x}\"\n y=\"${bounds.y}\"\n width=\"${bounds.width}\"\n height=\"${bounds.height}\"\n rx=\"${radius}\"\n ry=\"${radius}\"\n fill=\"rgba(239, 68, 68, 0.10)\"\n stroke=\"rgba(239, 68, 68, 0.95)\"\n stroke-width=\"${strokeWidth}\"\n />\n </svg>\n `.trim();\n\n return Buffer.from(svg, 'utf8');\n}\n\nexport async function annotateScreenshot(\n imagePath: string,\n bounds: BoundingBox,\n outputPath: string,\n): Promise<string | null> {\n const sharp = await loadSharp();\n if (!sharp) {\n return null;\n }\n\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n if (!metadata.width || !metadata.height) {\n return null;\n }\n\n const overlay = buildOverlaySvg(metadata.width, metadata.height, bounds);\n await image.composite([{ input: overlay, top: 0, left: 0 }]).png().toFile(outputPath);\n return outputPath;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type {\n CheckpointConfig,\n CheckpointManifest,\n ReportGenerationResults,\n ReportGenerator,\n ReporterConfig,\n RunRecord,\n} from '../types';\nimport { htmlReporter } from './html-reporter';\nimport { markdownReporter } from './markdown-reporter';\nimport { mdxReporter } from './mdx-reporter';\nexport { groupByStory, orderedCheckpointNames } from './story-utils';\n\nconst builtinReporters = new Map<string, ReportGenerator>();\nconst builtinReporterDefaults: Partial<Record<string, ReporterConfig>> = {\n html: true,\n markdown: false,\n mdx: false,\n};\n\nasync function walkFiles(directory: string): Promise<string[]> {\n const dirents = await fs.readdir(directory, { withFileTypes: true });\n const files: string[] = [];\n\n for (const dirent of dirents) {\n const absolutePath = path.join(directory, dirent.name);\n if (dirent.isDirectory()) {\n files.push(...(await walkFiles(absolutePath)));\n continue;\n }\n if (dirent.isFile()) {\n files.push(absolutePath);\n }\n }\n\n return files;\n}\n\nfunction isCheckpointManifestFile(filePath: string): boolean {\n const fileName = path.basename(filePath);\n return fileName === 'checkpoint-manifest.json' || (fileName.startsWith('checkpoint-manifest-') && fileName.endsWith('.json'));\n}\n\nfunction isCheckpointManifest(value: unknown): value is CheckpointManifest {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const manifest = value as Partial<CheckpointManifest>;\n return (\n typeof manifest.project === 'string' &&\n typeof manifest.testId === 'string' &&\n typeof manifest.title === 'string' &&\n typeof manifest.startedAt === 'string' &&\n Array.isArray(manifest.tags) &&\n Array.isArray(manifest.checkpoints)\n );\n}\n\nfunction toRunRecord(manifest: CheckpointManifest, sourceManifestPath: string): RunRecord {\n return {\n key: `${manifest.testId}|${manifest.project}|${manifest.startedAt}`,\n sourceManifestPath,\n environment: manifest.environment || 'unknown',\n project: manifest.project,\n testId: manifest.testId,\n title: manifest.title,\n ...(manifest.article ? { article: manifest.article } : {}),\n ...(manifest.articles ? { articles: manifest.articles } : {}),\n tags: manifest.tags,\n startedAt: manifest.startedAt,\n checkpoints: manifest.checkpoints,\n };\n}\n\nfunction toManifest(run: RunRecord): CheckpointManifest {\n return {\n environment: run.environment,\n project: run.project,\n testId: run.testId,\n title: run.title,\n ...(run.article ? { article: run.article } : {}),\n ...(run.articles ? { articles: run.articles } : {}),\n tags: run.tags,\n startedAt: run.startedAt,\n checkpoints: run.checkpoints,\n };\n}\n\nfunction normalizeReporterConfig(config: ReporterConfig | undefined): Record<string, unknown> | null {\n if (config == null || config === false) {\n return null;\n }\n\n if (config === true) {\n return {};\n }\n\n return { ...config };\n}\n\nexport function registerBuiltinReporter(reporter: ReportGenerator): void {\n builtinReporters.set(reporter.name, reporter);\n}\n\nexport function dedupeRuns(runs: RunRecord[]): RunRecord[] {\n const map = new Map<string, RunRecord>();\n\n for (const run of runs) {\n const existing = map.get(run.key);\n if (!existing) {\n map.set(run.key, run);\n continue;\n }\n\n const existingTime = new Date(existing.startedAt).getTime();\n const currentTime = new Date(run.startedAt).getTime();\n if (currentTime >= existingTime) {\n map.set(run.key, run);\n }\n }\n\n return [...map.values()];\n}\n\nexport async function loadRuns(testResultsDir: string): Promise<RunRecord[]> {\n let manifestFiles: string[];\n try {\n manifestFiles = (await walkFiles(testResultsDir)).filter(isCheckpointManifestFile);\n } catch {\n return [];\n }\n\n const runs: RunRecord[] = [];\n for (const manifestPath of manifestFiles) {\n let rawManifest: unknown;\n try {\n rawManifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));\n } catch {\n continue;\n }\n\n if (!isCheckpointManifest(rawManifest)) {\n continue;\n }\n\n runs.push(toRunRecord(rawManifest, manifestPath));\n }\n\n return dedupeRuns(runs);\n}\n\nexport async function runReporters(\n config: CheckpointConfig,\n testResultsDir: string,\n outputDir: string,\n): Promise<ReportGenerationResults> {\n const runs = await loadRuns(testResultsDir);\n const manifests = runs.map(toManifest);\n const results: ReportGenerationResults = {};\n const reporterConfigMap: Partial<Record<string, ReporterConfig>> = {\n ...builtinReporterDefaults,\n ...(config.reporters ?? {}),\n };\n\n for (const [name, value] of Object.entries(reporterConfigMap)) {\n const reporterConfig = normalizeReporterConfig(value);\n if (!reporterConfig) {\n continue;\n }\n\n const reporter = builtinReporters.get(name);\n if (!reporter) {\n throw new Error(`Reporter \"${name}\" is enabled but no implementation is registered.`);\n }\n\n if (reporter.validateConfig && !reporter.validateConfig(reporterConfig)) {\n throw new Error(`Reporter \"${name}\" received invalid configuration.`);\n }\n\n results[name] = await reporter.generate({\n runs,\n outputDir,\n config: reporterConfig,\n manifests,\n });\n }\n\n return results;\n}\n\nregisterBuiltinReporter(htmlReporter);\nregisterBuiltinReporter(markdownReporter);\nregisterBuiltinReporter(mdxReporter);\n\nexport { annotateScreenshot } from './annotate';\nexport { htmlReporter, markdownReporter, mdxReporter };\nexport type { ReportGenerator } from '../types';\n"],"mappings":"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","names":["fs","path","slugify","resolveArtifactPath","path","screenshotData","fs","fs","path","DEFAULT_PROJECT_ORDER","slugify","stripTags","normalizeConfig","normalizeTags","shouldIncludeRun","choosePrimaryRun","orderedCheckpoints","resolveArtifactPath","path","screenshotSourcePath","screenshotData","focusNote","urlLabel","autoDescription","markdownRelativePath","rewriteImagePath","yamlScalar","serializeFrontmatter","projectWeight","formatProjectLabel","materializeScreenshot","fs","buildSteps","path","require","fs","path","fs","path"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runReporters
3
- } from "./chunk-X5IPL32H.js";
3
+ } from "./chunk-WXZOP7XI.js";
4
4
 
5
5
  // src/cli/index.ts
6
6
  import path from "path";
@@ -211,4 +211,4 @@ export {
211
211
  parseCliArgs,
212
212
  runCli
213
213
  };
214
- //# sourceMappingURL=chunk-K5DX32TO.js.map
214
+ //# sourceMappingURL=chunk-YUFXGGZM.js.map