/*
 Stencil Screenshot v4.29.1 | MIT Licensed | https://stenciljs.com
 */
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/screenshot/index.ts
var index_exports = {};
__export(index_exports, {
  ScreenshotConnector: () => ScreenshotConnector,
  ScreenshotLocalConnector: () => ScreenshotLocalConnector
});
module.exports = __toCommonJS(index_exports);

// src/screenshot/connector-base.ts
var import_os = require("os");
var import_path2 = require("path");

// src/screenshot/screenshot-fs.ts
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
function fileExists(filePath) {
  return new Promise((resolve) => {
    import_fs.default.access(filePath, (err2) => resolve(!err2));
  });
}
function readFile(filePath) {
  return new Promise((resolve, reject) => {
    import_fs.default.readFile(filePath, "utf-8", (err2, data) => {
      if (err2) {
        reject(err2);
      } else {
        resolve(data);
      }
    });
  });
}
function readFileBuffer(filePath) {
  return new Promise((resolve, reject) => {
    import_fs.default.readFile(filePath, (err2, data) => {
      if (err2) {
        reject(err2);
      } else {
        resolve(data);
      }
    });
  });
}
function writeFile(filePath, data) {
  return new Promise((resolve, reject) => {
    import_fs.default.writeFile(filePath, data, (err2) => {
      if (err2) {
        reject(err2);
      } else {
        resolve();
      }
    });
  });
}
function mkDir(filePath) {
  return new Promise((resolve) => {
    import_fs.default.mkdir(filePath, () => {
      resolve();
    });
  });
}
function rmDir(filePath) {
  return new Promise((resolve) => {
    import_fs.default.rmdir(filePath, () => {
      resolve();
    });
  });
}
async function emptyDir(dir) {
  const files = await readDir(dir);
  const promises = files.map(async (fileName) => {
    const filePath = import_path.default.join(dir, fileName);
    const isDirFile = await isFile(filePath);
    if (isDirFile) {
      await unlink(filePath);
    }
  });
  await Promise.all(promises);
}
async function readDir(dir) {
  return new Promise((resolve) => {
    import_fs.default.readdir(dir, (err2, files) => {
      if (err2) {
        resolve([]);
      } else {
        resolve(files);
      }
    });
  });
}
async function isFile(itemPath) {
  return new Promise((resolve) => {
    import_fs.default.stat(itemPath, (err2, stat) => {
      if (err2) {
        resolve(false);
      } else {
        resolve(stat.isFile());
      }
    });
  });
}
async function unlink(filePath) {
  return new Promise((resolve) => {
    import_fs.default.unlink(filePath, () => {
      resolve();
    });
  });
}

// src/screenshot/connector-base.ts
var ScreenshotConnector = class {
  constructor() {
    this.screenshotDirName = "screenshot";
    this.imagesDirName = "images";
    this.buildsDirName = "builds";
    this.masterBuildFileName = "master.json";
    this.screenshotCacheFileName = "screenshot-cache.json";
  }
  async initBuild(opts) {
    this.logger = opts.logger;
    this.buildId = opts.buildId;
    this.buildMessage = opts.buildMessage || "";
    this.buildAuthor = opts.buildAuthor;
    this.buildUrl = opts.buildUrl;
    this.previewUrl = opts.previewUrl;
    this.buildTimestamp = typeof opts.buildTimestamp === "number" ? opts.buildTimestamp : Date.now();
    this.cacheDir = opts.cacheDir;
    this.packageDir = opts.packageDir;
    this.rootDir = opts.rootDir;
    this.appNamespace = opts.appNamespace;
    this.waitBeforeScreenshot = opts.waitBeforeScreenshot;
    this.pixelmatchModulePath = opts.pixelmatchModulePath;
    if (!opts.logger) {
      throw new Error(`logger option required`);
    }
    if (typeof opts.buildId !== "string") {
      throw new Error(`buildId option required`);
    }
    if (typeof opts.cacheDir !== "string") {
      throw new Error(`cacheDir option required`);
    }
    if (typeof opts.packageDir !== "string") {
      throw new Error(`packageDir option required`);
    }
    if (typeof opts.rootDir !== "string") {
      throw new Error(`rootDir option required`);
    }
    this.updateMaster = !!opts.updateMaster;
    this.allowableMismatchedPixels = opts.allowableMismatchedPixels;
    this.allowableMismatchedRatio = opts.allowableMismatchedRatio;
    this.pixelmatchThreshold = opts.pixelmatchThreshold;
    this.logger.debug(`screenshot build: ${this.buildId}, ${this.buildMessage}, updateMaster: ${this.updateMaster}`);
    this.logger.debug(
      `screenshot, allowableMismatchedPixels: ${this.allowableMismatchedPixels}, allowableMismatchedRatio: ${this.allowableMismatchedRatio}, pixelmatchThreshold: ${this.pixelmatchThreshold}`
    );
    if (typeof opts.screenshotDirName === "string") {
      this.screenshotDirName = opts.screenshotDirName;
    }
    if (typeof opts.imagesDirName === "string") {
      this.imagesDirName = opts.imagesDirName;
    }
    if (typeof opts.buildsDirName === "string") {
      this.buildsDirName = opts.buildsDirName;
    }
    this.screenshotDir = (0, import_path2.join)(this.rootDir, this.screenshotDirName);
    this.imagesDir = (0, import_path2.join)(this.screenshotDir, this.imagesDirName);
    this.buildsDir = (0, import_path2.join)(this.screenshotDir, this.buildsDirName);
    this.masterBuildFilePath = (0, import_path2.join)(this.buildsDir, this.masterBuildFileName);
    this.screenshotCacheFilePath = (0, import_path2.join)(this.cacheDir, this.screenshotCacheFileName);
    this.currentBuildDir = (0, import_path2.join)((0, import_os.tmpdir)(), "screenshot-build-" + this.buildId);
    this.logger.debug(`screenshotDirPath: ${this.screenshotDir}`);
    this.logger.debug(`imagesDirPath: ${this.imagesDir}`);
    this.logger.debug(`buildsDirPath: ${this.buildsDir}`);
    this.logger.debug(`currentBuildDir: ${this.currentBuildDir}`);
    this.logger.debug(`cacheDir: ${this.cacheDir}`);
    await mkDir(this.screenshotDir);
    await Promise.all([
      mkDir(this.imagesDir),
      mkDir(this.buildsDir),
      mkDir(this.currentBuildDir),
      mkDir(this.cacheDir)
    ]);
  }
  async pullMasterBuild() {
  }
  async getMasterBuild() {
    try {
      const masterBuild = JSON.parse(await readFile(this.masterBuildFilePath));
      return masterBuild;
    } catch (e) {
    }
    return null;
  }
  async completeBuild(masterBuild) {
    const filePaths = (await readDir(this.currentBuildDir)).map((f) => (0, import_path2.join)(this.currentBuildDir, f)).filter((f) => f.endsWith(".json"));
    const screenshots = await Promise.all(filePaths.map(async (f) => JSON.parse(await readFile(f))));
    this.sortScreenshots(screenshots);
    if (!masterBuild) {
      masterBuild = {
        id: this.buildId,
        message: this.buildMessage,
        author: this.buildAuthor,
        url: this.buildUrl,
        previewUrl: this.previewUrl,
        appNamespace: this.appNamespace,
        timestamp: this.buildTimestamp,
        screenshots
      };
    }
    const results = {
      appNamespace: this.appNamespace,
      masterBuild,
      currentBuild: {
        id: this.buildId,
        message: this.buildMessage,
        author: this.buildAuthor,
        url: this.buildUrl,
        previewUrl: this.previewUrl,
        appNamespace: this.appNamespace,
        timestamp: this.buildTimestamp,
        screenshots
      },
      compare: {
        id: `${masterBuild.id}-${this.buildId}`,
        a: {
          id: masterBuild.id,
          message: masterBuild.message,
          author: masterBuild.author,
          url: masterBuild.url,
          previewUrl: masterBuild.previewUrl
        },
        b: {
          id: this.buildId,
          message: this.buildMessage,
          author: this.buildAuthor,
          url: this.buildUrl,
          previewUrl: this.previewUrl
        },
        url: null,
        appNamespace: this.appNamespace,
        timestamp: this.buildTimestamp,
        diffs: []
      }
    };
    results.currentBuild.screenshots.forEach((screenshot) => {
      screenshot.diff.device = screenshot.diff.device || screenshot.diff.userAgent;
      results.compare.diffs.push(screenshot.diff);
      delete screenshot.diff;
    });
    this.sortCompares(results.compare.diffs);
    await emptyDir(this.currentBuildDir);
    await rmDir(this.currentBuildDir);
    return results;
  }
  async publishBuild(results) {
    return results;
  }
  async generateJsonpDataUris(build) {
    if (build && Array.isArray(build.screenshots)) {
      for (let i = 0; i < build.screenshots.length; i++) {
        const screenshot = build.screenshots[i];
        const jsonpFileName = `screenshot_${screenshot.image}.js`;
        const jsonFilePath = (0, import_path2.join)(this.cacheDir, jsonpFileName);
        const jsonpExists = await fileExists(jsonFilePath);
        if (!jsonpExists) {
          const imageFilePath = (0, import_path2.join)(this.imagesDir, screenshot.image);
          const imageBuf = await readFileBuffer(imageFilePath);
          const jsonpContent = `loadScreenshot("${screenshot.image}","data:image/png;base64,${imageBuf.toString(
            "base64"
          )}");`;
          await writeFile(jsonFilePath, jsonpContent);
        }
      }
    }
  }
  async getScreenshotCache() {
    return null;
  }
  async updateScreenshotCache(screenshotCache, buildResults) {
    screenshotCache = screenshotCache || {};
    screenshotCache.timestamp = this.buildTimestamp;
    screenshotCache.lastBuildId = this.buildId;
    screenshotCache.size = 0;
    screenshotCache.items = screenshotCache.items || [];
    if (buildResults && buildResults.compare && Array.isArray(buildResults.compare.diffs)) {
      buildResults.compare.diffs.forEach((diff) => {
        if (typeof diff.cacheKey !== "string") {
          return;
        }
        if (diff.imageA === diff.imageB) {
          return;
        }
        const existingItem = screenshotCache.items.find((i) => i.key === diff.cacheKey);
        if (existingItem) {
          existingItem.ts = this.buildTimestamp;
        } else {
          screenshotCache.items.push({
            key: diff.cacheKey,
            ts: this.buildTimestamp,
            mp: diff.mismatchedPixels
          });
        }
      });
    }
    screenshotCache.items.sort((a, b) => {
      if (a.ts > b.ts) return -1;
      if (a.ts < b.ts) return 1;
      if (a.mp > b.mp) return -1;
      if (a.mp < b.mp) return 1;
      return 0;
    });
    screenshotCache.items = screenshotCache.items.slice(0, 1e3);
    screenshotCache.size = screenshotCache.items.length;
    return screenshotCache;
  }
  toJson(masterBuild, screenshotCache) {
    const masterScreenshots = {};
    if (masterBuild && Array.isArray(masterBuild.screenshots)) {
      masterBuild.screenshots.forEach((masterScreenshot) => {
        masterScreenshots[masterScreenshot.id] = masterScreenshot.image;
      });
    }
    const mismatchCache = {};
    if (screenshotCache && Array.isArray(screenshotCache.items)) {
      screenshotCache.items.forEach((cacheItem) => {
        mismatchCache[cacheItem.key] = cacheItem.mp;
      });
    }
    const screenshotBuild = {
      buildId: this.buildId,
      rootDir: this.rootDir,
      screenshotDir: this.screenshotDir,
      imagesDir: this.imagesDir,
      buildsDir: this.buildsDir,
      masterScreenshots,
      cache: mismatchCache,
      currentBuildDir: this.currentBuildDir,
      updateMaster: this.updateMaster,
      allowableMismatchedPixels: this.allowableMismatchedPixels,
      allowableMismatchedRatio: this.allowableMismatchedRatio,
      pixelmatchThreshold: this.pixelmatchThreshold,
      timeoutBeforeScreenshot: this.waitBeforeScreenshot,
      pixelmatchModulePath: this.pixelmatchModulePath
    };
    return JSON.stringify(screenshotBuild);
  }
  sortScreenshots(screenshots) {
    return screenshots.sort((a, b) => {
      if (a.desc && b.desc) {
        if (a.desc.toLowerCase() < b.desc.toLowerCase()) return -1;
        if (a.desc.toLowerCase() > b.desc.toLowerCase()) return 1;
      }
      if (a.device && b.device) {
        if (a.device.toLowerCase() < b.device.toLowerCase()) return -1;
        if (a.device.toLowerCase() > b.device.toLowerCase()) return 1;
      }
      if (a.userAgent && b.userAgent) {
        if (a.userAgent.toLowerCase() < b.userAgent.toLowerCase()) return -1;
        if (a.userAgent.toLowerCase() > b.userAgent.toLowerCase()) return 1;
      }
      if (a.width < b.width) return -1;
      if (a.width > b.width) return 1;
      if (a.height < b.height) return -1;
      if (a.height > b.height) return 1;
      if (a.id < b.id) return -1;
      if (a.id > b.id) return 1;
      return 0;
    });
  }
  sortCompares(compares) {
    return compares.sort((a, b) => {
      if (a.allowableMismatchedPixels > b.allowableMismatchedPixels) return -1;
      if (a.allowableMismatchedPixels < b.allowableMismatchedPixels) return 1;
      if (a.allowableMismatchedRatio > b.allowableMismatchedRatio) return -1;
      if (a.allowableMismatchedRatio < b.allowableMismatchedRatio) return 1;
      if (a.desc && b.desc) {
        if (a.desc.toLowerCase() < b.desc.toLowerCase()) return -1;
        if (a.desc.toLowerCase() > b.desc.toLowerCase()) return 1;
      }
      if (a.device && b.device) {
        if (a.device.toLowerCase() < b.device.toLowerCase()) return -1;
        if (a.device.toLowerCase() > b.device.toLowerCase()) return 1;
      }
      if (a.userAgent && b.userAgent) {
        if (a.userAgent.toLowerCase() < b.userAgent.toLowerCase()) return -1;
        if (a.userAgent.toLowerCase() > b.userAgent.toLowerCase()) return 1;
      }
      if (a.width < b.width) return -1;
      if (a.width > b.width) return 1;
      if (a.height < b.height) return -1;
      if (a.height > b.height) return 1;
      if (a.id < b.id) return -1;
      if (a.id > b.id) return 1;
      return 0;
    });
  }
};

// src/utils/path.ts
var normalizePath = (path2, relativize = true) => {
  if (typeof path2 !== "string") {
    throw new Error(`invalid path to normalize`);
  }
  path2 = normalizeSlashes(path2.trim());
  const components = pathComponents(path2, getRootLength(path2));
  const reducedComponents = reducePathComponents(components);
  const rootPart = reducedComponents[0];
  const secondPart = reducedComponents[1];
  const normalized = rootPart + reducedComponents.slice(1).join("/");
  if (normalized === "") {
    return ".";
  }
  if (rootPart === "" && secondPart && path2.includes("/") && !secondPart.startsWith(".") && !secondPart.startsWith("@") && relativize) {
    return "./" + normalized;
  }
  return normalized;
};
var normalizeSlashes = (path2) => path2.replace(backslashRegExp, "/");
var altDirectorySeparator = "\\";
var urlSchemeSeparator = "://";
var backslashRegExp = /\\/g;
var reducePathComponents = (components) => {
  if (!Array.isArray(components) || components.length === 0) {
    return [];
  }
  const reduced = [components[0]];
  for (let i = 1; i < components.length; i++) {
    const component = components[i];
    if (!component) continue;
    if (component === ".") continue;
    if (component === "..") {
      if (reduced.length > 1) {
        if (reduced[reduced.length - 1] !== "..") {
          reduced.pop();
          continue;
        }
      } else if (reduced[0]) continue;
    }
    reduced.push(component);
  }
  return reduced;
};
var getRootLength = (path2) => {
  const rootLength = getEncodedRootLength(path2);
  return rootLength < 0 ? ~rootLength : rootLength;
};
var getEncodedRootLength = (path2) => {
  if (!path2) return 0;
  const ch0 = path2.charCodeAt(0);
  if (ch0 === 47 /* slash */ || ch0 === 92 /* backslash */) {
    if (path2.charCodeAt(1) !== ch0) return 1;
    const p1 = path2.indexOf(ch0 === 47 /* slash */ ? "/" : altDirectorySeparator, 2);
    if (p1 < 0) return path2.length;
    return p1 + 1;
  }
  if (isVolumeCharacter(ch0) && path2.charCodeAt(1) === 58 /* colon */) {
    const ch2 = path2.charCodeAt(2);
    if (ch2 === 47 /* slash */ || ch2 === 92 /* backslash */) return 3;
    if (path2.length === 2) return 2;
  }
  const schemeEnd = path2.indexOf(urlSchemeSeparator);
  if (schemeEnd !== -1) {
    const authorityStart = schemeEnd + urlSchemeSeparator.length;
    const authorityEnd = path2.indexOf("/", authorityStart);
    if (authorityEnd !== -1) {
      const scheme = path2.slice(0, schemeEnd);
      const authority = path2.slice(authorityStart, authorityEnd);
      if (scheme === "file" && (authority === "" || authority === "localhost") && isVolumeCharacter(path2.charCodeAt(authorityEnd + 1))) {
        const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path2, authorityEnd + 2);
        if (volumeSeparatorEnd !== -1) {
          if (path2.charCodeAt(volumeSeparatorEnd) === 47 /* slash */) {
            return ~(volumeSeparatorEnd + 1);
          }
          if (volumeSeparatorEnd === path2.length) {
            return ~volumeSeparatorEnd;
          }
        }
      }
      return ~(authorityEnd + 1);
    }
    return ~path2.length;
  }
  return 0;
};
var isVolumeCharacter = (charCode) => charCode >= 97 /* a */ && charCode <= 122 /* z */ || charCode >= 65 /* A */ && charCode <= 90 /* Z */;
var getFileUrlVolumeSeparatorEnd = (url, start) => {
  const ch0 = url.charCodeAt(start);
  if (ch0 === 58 /* colon */) return start + 1;
  if (ch0 === 37 /* percent */ && url.charCodeAt(start + 1) === 51 /* _3 */) {
    const ch2 = url.charCodeAt(start + 2);
    if (ch2 === 97 /* a */ || ch2 === 65 /* A */) return start + 3;
  }
  return -1;
};
var pathComponents = (path2, rootLength) => {
  const root = path2.substring(0, rootLength);
  const rest = path2.substring(rootLength).split("/");
  const restLen = rest.length;
  if (restLen > 0 && !rest[restLen - 1]) {
    rest.pop();
  }
  return [root, ...rest];
};

// src/utils/result.ts
var result_exports = {};
__export(result_exports, {
  err: () => err,
  map: () => map,
  ok: () => ok,
  unwrap: () => unwrap,
  unwrapErr: () => unwrapErr
});
var ok = (value) => ({
  isOk: true,
  isErr: false,
  value
});
var err = (value) => ({
  isOk: false,
  isErr: true,
  value
});
function map(result, fn) {
  if (result.isOk) {
    const val = fn(result.value);
    if (val instanceof Promise) {
      return val.then((newVal) => ok(newVal));
    } else {
      return ok(val);
    }
  }
  if (result.isErr) {
    const value = result.value;
    return err(value);
  }
  throw "should never get here";
}
var unwrap = (result) => {
  if (result.isOk) {
    return result.value;
  } else {
    throw result.value;
  }
};
var unwrapErr = (result) => {
  if (result.isErr) {
    return result.value;
  } else {
    throw result.value;
  }
};

// src/utils/util.ts
var lowerPathParam = (fn) => (p) => fn(p.toLowerCase());
var isDtsFile = lowerPathParam((p) => p.endsWith(".d.ts") || p.endsWith(".d.mts") || p.endsWith(".d.cts"));
var isTsFile = lowerPathParam(
  (p) => !isDtsFile(p) && (p.endsWith(".ts") || p.endsWith(".mts") || p.endsWith(".cts"))
);
var isTsxFile = lowerPathParam(
  (p) => p.endsWith(".tsx") || p.endsWith(".mtsx") || p.endsWith(".ctsx")
);
var isJsxFile = lowerPathParam(
  (p) => p.endsWith(".jsx") || p.endsWith(".mjsx") || p.endsWith(".cjsx")
);
var isJsFile = lowerPathParam((p) => p.endsWith(".js") || p.endsWith(".mjs") || p.endsWith(".cjs"));

// src/screenshot/connector-local.ts
var import_path4 = require("path");
var ScreenshotLocalConnector = class extends ScreenshotConnector {
  async publishBuild(results) {
    if (this.updateMaster || !results.masterBuild) {
      results.masterBuild = {
        id: "master",
        message: "Master",
        appNamespace: this.appNamespace,
        timestamp: Date.now(),
        screenshots: []
      };
    }
    results.currentBuild.screenshots.forEach((currentScreenshot) => {
      const masterHasScreenshot = results.masterBuild.screenshots.some((masterScreenshot) => {
        return currentScreenshot.id === masterScreenshot.id;
      });
      if (!masterHasScreenshot) {
        results.masterBuild.screenshots.push(Object.assign({}, currentScreenshot));
      }
    });
    this.sortScreenshots(results.masterBuild.screenshots);
    await writeFile(this.masterBuildFilePath, JSON.stringify(results.masterBuild, null, 2));
    await this.generateJsonpDataUris(results.currentBuild);
    const compareAppSourceDir = (0, import_path4.join)(this.packageDir, "screenshot", "compare");
    const appSrcUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, compareAppSourceDir));
    const imagesUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, this.imagesDir));
    const jsonpUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, this.cacheDir));
    const compareAppHtml = createLocalCompareApp(
      this.appNamespace,
      appSrcUrl,
      imagesUrl,
      jsonpUrl,
      results.masterBuild,
      results.currentBuild
    );
    const compareAppFileName = "compare.html";
    const compareAppFilePath = (0, import_path4.join)(this.screenshotDir, compareAppFileName);
    await writeFile(compareAppFilePath, compareAppHtml);
    const gitIgnorePath = (0, import_path4.join)(this.screenshotDir, ".gitignore");
    const gitIgnoreExists = await fileExists(gitIgnorePath);
    if (!gitIgnoreExists) {
      const content = [this.imagesDirName, this.buildsDirName, compareAppFileName];
      await writeFile(gitIgnorePath, content.join("\n"));
    }
    const url = new URL(`file://${compareAppFilePath}`);
    results.compare.url = url.href;
    return results;
  }
  async getScreenshotCache() {
    let screenshotCache = null;
    try {
      screenshotCache = JSON.parse(await readFile(this.screenshotCacheFilePath));
    } catch (e) {
    }
    return screenshotCache;
  }
  async updateScreenshotCache(cache, buildResults) {
    cache = await super.updateScreenshotCache(cache, buildResults);
    await writeFile(this.screenshotCacheFilePath, JSON.stringify(cache, null, 2));
    return cache;
  }
};
function createLocalCompareApp(namespace, appSrcUrl, imagesUrl, jsonpUrl, a, b) {
  return `<!doctype html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <title>Local ${namespace || ""} - Stencil Screenshot Visual Diff</title>
  <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta http-equiv="x-ua-compatible" content="IE=Edge">
  <link href="${appSrcUrl}/build/app.css" rel="stylesheet">
  <script type="module" src="${appSrcUrl}/build/app.esm.js"></script>
  <script nomodule src="${appSrcUrl}/build/app.js"></script>
  <link rel="icon" type="image/x-icon" href="${appSrcUrl}/assets/favicon.ico">
</head>
<body>
  <script>
    (function() {
      var app = document.createElement('screenshot-compare');
      app.appSrcUrl = '${appSrcUrl}';
      app.imagesUrl = '${imagesUrl}/';
      app.jsonpUrl = '${jsonpUrl}/';
      app.a = ${JSON.stringify(a)};
      app.b = ${JSON.stringify(b)};
      document.body.appendChild(app);
    })();
  </script>
</body>
</html>`;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  ScreenshotConnector,
  ScreenshotLocalConnector
});
