obsidian-integration-testing 17.2.0 → 17.3.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 (35) hide show
  1. package/dist/lib/cjs/apply-obsidian-theme.cjs +95 -0
  2. package/dist/lib/cjs/apply-obsidian-theme.d.cts +99 -0
  3. package/dist/lib/cjs/capture-obsidian-screenshot.cjs +17 -1
  4. package/dist/lib/cjs/capture-obsidian-screenshot.d.cts +41 -2
  5. package/dist/lib/cjs/deterministic-raster.cjs +31 -0
  6. package/dist/lib/cjs/deterministic-raster.d.cts +37 -0
  7. package/dist/lib/cjs/eval-in-obsidian.cjs +1 -1
  8. package/dist/lib/cjs/eval-in-obsidian.d.cts +60 -0
  9. package/dist/lib/cjs/index.cjs +9 -1
  10. package/dist/lib/cjs/index.d.cts +4 -0
  11. package/dist/lib/cjs/library.cjs +1 -1
  12. package/dist/lib/cjs/namespace-bootstrap.cjs +51 -7
  13. package/dist/lib/cjs/pin-window-focus.cjs +60 -0
  14. package/dist/lib/cjs/pin-window-focus.d.cts +76 -0
  15. package/dist/lib/cjs/soft-keyboard.cjs +16 -1
  16. package/dist/lib/cjs/soft-keyboard.d.cts +2 -1
  17. package/dist/lib/cjs/transport-desktop-cdp.cjs +3 -1
  18. package/dist/lib/esm/apply-obsidian-theme.d.mts +99 -0
  19. package/dist/lib/esm/apply-obsidian-theme.mjs +70 -0
  20. package/dist/lib/esm/capture-obsidian-screenshot.d.mts +41 -2
  21. package/dist/lib/esm/capture-obsidian-screenshot.mjs +17 -1
  22. package/dist/lib/esm/deterministic-raster.d.mts +37 -0
  23. package/dist/lib/esm/deterministic-raster.mjs +7 -0
  24. package/dist/lib/esm/eval-in-obsidian.d.mts +60 -0
  25. package/dist/lib/esm/eval-in-obsidian.mjs +1 -1
  26. package/dist/lib/esm/index.d.mts +4 -0
  27. package/dist/lib/esm/index.mjs +9 -1
  28. package/dist/lib/esm/library.mjs +1 -1
  29. package/dist/lib/esm/namespace-bootstrap.mjs +51 -7
  30. package/dist/lib/esm/pin-window-focus.d.mts +76 -0
  31. package/dist/lib/esm/pin-window-focus.mjs +36 -0
  32. package/dist/lib/esm/soft-keyboard.d.mts +2 -1
  33. package/dist/lib/esm/soft-keyboard.mjs +16 -1
  34. package/dist/lib/esm/transport-desktop-cdp.mjs +3 -1
  35. package/package.json +1 -1
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Switches the driven Obsidian to its dark or light theme, and makes the switch
5
+ * survive a config reload.
6
+ *
7
+ * `app.changeTheme` only sets the theme in the in-memory config and SCHEDULES
8
+ * the save, a second later (`requestSaveConfig` is debounced). Until that save
9
+ * lands, any file-watcher event on `.obsidian/app.json` or
10
+ * `.obsidian/appearance.json` makes Obsidian run `vault.reloadConfig`, which
11
+ * re-reads both files and deletes every in-memory key the disk does not have —
12
+ * `theme` included. The body then falls back to the light theme, and a capture
13
+ * suite that set the dark theme shoots every frame light, silently writing over
14
+ * the frames it has committed. Read from the 1.14.2 `app.js`, and reproduced in a
15
+ * plugin's capture suite: one external rewrite of `app.json` straight after
16
+ * `changeTheme` turned all five of its desktop frames light.
17
+ *
18
+ * {@link applyObsidianTheme} saves the config at once, so a reload finds the
19
+ * theme on disk and has nothing to drop, and waits until the body carries the
20
+ * theme's class and `appearance.json` carries its name. It also records the
21
+ * theme it applied, so `captureObsidianScreenshot` can refuse a frame the theme
22
+ * has left since.
23
+ */
24
+ import type { ObsidianTransport } from './transport.mjs';
25
+ /**
26
+ * Parameters for {@link applyObsidianTheme}.
27
+ */
28
+ export interface ApplyObsidianThemeParams {
29
+ /**
30
+ * The theme to switch to.
31
+ */
32
+ readonly theme: ObsidianTheme;
33
+ /**
34
+ * How long to wait for the theme to be applied and saved.
35
+ *
36
+ * @default 10000
37
+ */
38
+ readonly timeoutInMilliseconds?: number;
39
+ /**
40
+ * Override the transport. When omitted, the transport the current test
41
+ * context is driving is used.
42
+ */
43
+ readonly transport?: ObsidianTransport;
44
+ /**
45
+ * The vault whose Obsidian window to switch. When omitted, the current test
46
+ * context's vault is used.
47
+ */
48
+ readonly vaultPath?: string;
49
+ }
50
+ /**
51
+ * Parameters for {@link assertObsidianThemeUnchanged}.
52
+ */
53
+ export interface AssertObsidianThemeUnchangedParams {
54
+ /**
55
+ * What the refusal is refusing, for its message, e.g. `capture a screenshot`.
56
+ */
57
+ readonly action: string;
58
+ /**
59
+ * Override the transport. When omitted, the transport the current test
60
+ * context is driving is used.
61
+ */
62
+ readonly transport?: ObsidianTransport;
63
+ /**
64
+ * The vault whose Obsidian window to check. When omitted, the current test
65
+ * context's vault is used.
66
+ */
67
+ readonly vaultPath?: string;
68
+ }
69
+ /**
70
+ * One of Obsidian's two built-in base themes.
71
+ */
72
+ export type ObsidianTheme = 'dark' | 'light';
73
+ /**
74
+ * Switches Obsidian to its dark or light theme, saves the config at once, and
75
+ * waits until the theme is both on screen and on disk.
76
+ *
77
+ * Use it instead of `app.changeTheme` in a capture suite: see the module
78
+ * comment for the reload that silently undoes a bare `changeTheme`. The theme
79
+ * is recorded, and `captureObsidianScreenshot` then refuses any frame whose body
80
+ * is no longer in it, so a theme lost after this call fails the capture by name
81
+ * instead of rewriting a committed frame.
82
+ *
83
+ * @param params - The theme, how long to wait, and transport / vault overrides.
84
+ * @returns A {@link Promise} that resolves once the theme is applied and saved.
85
+ * @throws Error if the theme is not applied and saved within the timeout.
86
+ */
87
+ export declare function applyObsidianTheme(params: ApplyObsidianThemeParams): Promise<void>;
88
+ /**
89
+ * Throws when the body has left the theme {@link applyObsidianTheme} applied.
90
+ *
91
+ * Nothing is checked when no theme was applied through it, since there is then
92
+ * no expectation to hold the frame to.
93
+ *
94
+ * @param params - What is being refused, and transport / vault overrides.
95
+ * @returns A {@link Promise} that resolves when the theme still holds, or none
96
+ * was applied.
97
+ * @throws Error naming the applied theme and the one on screen, if they differ.
98
+ */
99
+ export declare function assertObsidianThemeUnchanged(params: AssertObsidianThemeUnchangedParams): Promise<void>;
@@ -0,0 +1,70 @@
1
+ import { evalInObsidian } from "./eval-in-obsidian.mjs";
2
+ import { normalizeOptionalProperties } from "./normalize-optional-properties.mjs";
3
+ const DEFAULT_TIMEOUT_IN_MILLISECONDS = 1e4;
4
+ async function applyObsidianTheme(params) {
5
+ const {
6
+ theme,
7
+ timeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MILLISECONDS,
8
+ transport,
9
+ vaultPath
10
+ } = params;
11
+ await evalInObsidian(normalizeOptionalProperties({
12
+ async callback({ app, lib: { waitUntil }, theme: requestedTheme, timeoutInMilliseconds: timeout }) {
13
+ const themeName = requestedTheme === "dark" ? "obsidian" : "moonstone";
14
+ const bodyClass = `theme-${requestedTheme}`;
15
+ app.changeTheme(themeName);
16
+ await app.vault.saveConfig();
17
+ await waitUntil({
18
+ message: `the ${requestedTheme} theme to be applied and saved to appearance.json`,
19
+ async predicate() {
20
+ const appearance = await app.vault.readConfigJson("appearance");
21
+ return document.body.classList.contains(bodyClass) && appearance?.theme === themeName;
22
+ },
23
+ timeoutInMilliseconds: timeout
24
+ });
25
+ globalThis.__obsidianIntegrationTestingAppliedTheme = requestedTheme;
26
+ return void 0;
27
+ },
28
+ input: {
29
+ theme,
30
+ timeoutInMilliseconds
31
+ },
32
+ transport,
33
+ vaultPath
34
+ }));
35
+ }
36
+ async function assertObsidianThemeUnchanged(params) {
37
+ const {
38
+ action,
39
+ transport,
40
+ vaultPath
41
+ } = params;
42
+ const mismatch = await evalInObsidian(normalizeOptionalProperties({
43
+ callback() {
44
+ const appliedTheme = globalThis.__obsidianIntegrationTestingAppliedTheme;
45
+ if (appliedTheme === void 0 || document.body.classList.contains(`theme-${appliedTheme}`)) {
46
+ return null;
47
+ }
48
+ let shownTheme = "neither dark nor light";
49
+ if (document.body.classList.contains("theme-dark")) {
50
+ shownTheme = "dark";
51
+ } else if (document.body.classList.contains("theme-light")) {
52
+ shownTheme = "light";
53
+ }
54
+ return `applyObsidianTheme applied the ${appliedTheme} theme, but the body is now ${shownTheme}`;
55
+ },
56
+ input: {},
57
+ transport,
58
+ vaultPath
59
+ }));
60
+ if (mismatch !== null) {
61
+ throw new Error(
62
+ `Refusing to ${action}: ${mismatch}. Something reset the theme after it was applied - typically a bare app.changeTheme, or a config reload. Call applyObsidianTheme again, or pass shouldVerifyTheme: false to capture the frame as it is.`
63
+ );
64
+ }
65
+ }
66
+ export {
67
+ applyObsidianTheme,
68
+ assertObsidianThemeUnchanged
69
+ };
70
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2FwcGx5LW9ic2lkaWFuLXRoZW1lLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogU3dpdGNoZXMgdGhlIGRyaXZlbiBPYnNpZGlhbiB0byBpdHMgZGFyayBvciBsaWdodCB0aGVtZSwgYW5kIG1ha2VzIHRoZSBzd2l0Y2hcbiAqIHN1cnZpdmUgYSBjb25maWcgcmVsb2FkLlxuICpcbiAqIGBhcHAuY2hhbmdlVGhlbWVgIG9ubHkgc2V0cyB0aGUgdGhlbWUgaW4gdGhlIGluLW1lbW9yeSBjb25maWcgYW5kIFNDSEVEVUxFU1xuICogdGhlIHNhdmUsIGEgc2Vjb25kIGxhdGVyIChgcmVxdWVzdFNhdmVDb25maWdgIGlzIGRlYm91bmNlZCkuIFVudGlsIHRoYXQgc2F2ZVxuICogbGFuZHMsIGFueSBmaWxlLXdhdGNoZXIgZXZlbnQgb24gYC5vYnNpZGlhbi9hcHAuanNvbmAgb3JcbiAqIGAub2JzaWRpYW4vYXBwZWFyYW5jZS5qc29uYCBtYWtlcyBPYnNpZGlhbiBydW4gYHZhdWx0LnJlbG9hZENvbmZpZ2AsIHdoaWNoXG4gKiByZS1yZWFkcyBib3RoIGZpbGVzIGFuZCBkZWxldGVzIGV2ZXJ5IGluLW1lbW9yeSBrZXkgdGhlIGRpc2sgZG9lcyBub3QgaGF2ZSBcdTIwMTRcbiAqIGB0aGVtZWAgaW5jbHVkZWQuIFRoZSBib2R5IHRoZW4gZmFsbHMgYmFjayB0byB0aGUgbGlnaHQgdGhlbWUsIGFuZCBhIGNhcHR1cmVcbiAqIHN1aXRlIHRoYXQgc2V0IHRoZSBkYXJrIHRoZW1lIHNob290cyBldmVyeSBmcmFtZSBsaWdodCwgc2lsZW50bHkgd3JpdGluZyBvdmVyXG4gKiB0aGUgZnJhbWVzIGl0IGhhcyBjb21taXR0ZWQuIFJlYWQgZnJvbSB0aGUgMS4xNC4yIGBhcHAuanNgLCBhbmQgcmVwcm9kdWNlZCBpbiBhXG4gKiBwbHVnaW4ncyBjYXB0dXJlIHN1aXRlOiBvbmUgZXh0ZXJuYWwgcmV3cml0ZSBvZiBgYXBwLmpzb25gIHN0cmFpZ2h0IGFmdGVyXG4gKiBgY2hhbmdlVGhlbWVgIHR1cm5lZCBhbGwgZml2ZSBvZiBpdHMgZGVza3RvcCBmcmFtZXMgbGlnaHQuXG4gKlxuICoge0BsaW5rIGFwcGx5T2JzaWRpYW5UaGVtZX0gc2F2ZXMgdGhlIGNvbmZpZyBhdCBvbmNlLCBzbyBhIHJlbG9hZCBmaW5kcyB0aGVcbiAqIHRoZW1lIG9uIGRpc2sgYW5kIGhhcyBub3RoaW5nIHRvIGRyb3AsIGFuZCB3YWl0cyB1bnRpbCB0aGUgYm9keSBjYXJyaWVzIHRoZVxuICogdGhlbWUncyBjbGFzcyBhbmQgYGFwcGVhcmFuY2UuanNvbmAgY2FycmllcyBpdHMgbmFtZS4gSXQgYWxzbyByZWNvcmRzIHRoZVxuICogdGhlbWUgaXQgYXBwbGllZCwgc28gYGNhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RgIGNhbiByZWZ1c2UgYSBmcmFtZSB0aGUgdGhlbWVcbiAqIGhhcyBsZWZ0IHNpbmNlLlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGNvZGUgKGRyaXZlcyBhIGxpdmUgT2JzaWRpYW4pIGNvdmVyZWQgYnkgaW50ZWdyYXRpb24gdGVzdHMsIG5vdCB1bml0IHRlc3RzLiAqL1xuXG5pbXBvcnQgdHlwZSB7XG4gIEV2YWxJbk9ic2lkaWFuUGFyYW1zLFxuICBHZW5lcmljT2JqZWN0XG59IGZyb20gJy4vZXZhbC1pbi1vYnNpZGlhbi5tanMnO1xuaW1wb3J0IHR5cGUgeyBPYnNpZGlhblRyYW5zcG9ydCB9IGZyb20gJy4vdHJhbnNwb3J0Lm1qcyc7XG5cbmltcG9ydCB7IGV2YWxJbk9ic2lkaWFuIH0gZnJvbSAnLi9ldmFsLWluLW9ic2lkaWFuLm1qcyc7XG5pbXBvcnQgeyBub3JtYWxpemVPcHRpb25hbFByb3BlcnRpZXMgfSBmcm9tICcuL25vcm1hbGl6ZS1vcHRpb25hbC1wcm9wZXJ0aWVzLm1qcyc7XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3Ige0BsaW5rIGFwcGx5T2JzaWRpYW5UaGVtZX0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBwbHlPYnNpZGlhblRoZW1lUGFyYW1zIHtcbiAgLyoqXG4gICAqIFRoZSB0aGVtZSB0byBzd2l0Y2ggdG8uXG4gICAqL1xuICByZWFkb25seSB0aGVtZTogT2JzaWRpYW5UaGVtZTtcblxuICAvKipcbiAgICogSG93IGxvbmcgdG8gd2FpdCBmb3IgdGhlIHRoZW1lIHRvIGJlIGFwcGxpZWQgYW5kIHNhdmVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAxMDAwMFxuICAgKi9cbiAgcmVhZG9ubHkgdGltZW91dEluTWlsbGlzZWNvbmRzPzogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB0aGUgdHJhbnNwb3J0LiBXaGVuIG9taXR0ZWQsIHRoZSB0cmFuc3BvcnQgdGhlIGN1cnJlbnQgdGVzdFxuICAgKiBjb250ZXh0IGlzIGRyaXZpbmcgaXMgdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IHRyYW5zcG9ydD86IE9ic2lkaWFuVHJhbnNwb3J0O1xuXG4gIC8qKlxuICAgKiBUaGUgdmF1bHQgd2hvc2UgT2JzaWRpYW4gd2luZG93IHRvIHN3aXRjaC4gV2hlbiBvbWl0dGVkLCB0aGUgY3VycmVudCB0ZXN0XG4gICAqIGNvbnRleHQncyB2YXVsdCBpcyB1c2VkLlxuICAgKi9cbiAgcmVhZG9ubHkgdmF1bHRQYXRoPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBhc3NlcnRPYnNpZGlhblRoZW1lVW5jaGFuZ2VkfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBBc3NlcnRPYnNpZGlhblRoZW1lVW5jaGFuZ2VkUGFyYW1zIHtcbiAgLyoqXG4gICAqIFdoYXQgdGhlIHJlZnVzYWwgaXMgcmVmdXNpbmcsIGZvciBpdHMgbWVzc2FnZSwgZS5nLiBgY2FwdHVyZSBhIHNjcmVlbnNob3RgLlxuICAgKi9cbiAgcmVhZG9ubHkgYWN0aW9uOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIE92ZXJyaWRlIHRoZSB0cmFuc3BvcnQuIFdoZW4gb21pdHRlZCwgdGhlIHRyYW5zcG9ydCB0aGUgY3VycmVudCB0ZXN0XG4gICAqIGNvbnRleHQgaXMgZHJpdmluZyBpcyB1c2VkLlxuICAgKi9cbiAgcmVhZG9ubHkgdHJhbnNwb3J0PzogT2JzaWRpYW5UcmFuc3BvcnQ7XG5cbiAgLyoqXG4gICAqIFRoZSB2YXVsdCB3aG9zZSBPYnNpZGlhbiB3aW5kb3cgdG8gY2hlY2suIFdoZW4gb21pdHRlZCwgdGhlIGN1cnJlbnQgdGVzdFxuICAgKiBjb250ZXh0J3MgdmF1bHQgaXMgdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IHZhdWx0UGF0aD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBPbmUgb2YgT2JzaWRpYW4ncyB0d28gYnVpbHQtaW4gYmFzZSB0aGVtZXMuXG4gKi9cbmV4cG9ydCB0eXBlIE9ic2lkaWFuVGhlbWUgPSAnZGFyaycgfCAnbGlnaHQnO1xuXG5pbnRlcmZhY2UgQXBwZWFyYW5jZUNvbmZpZyB7XG4gIHJlYWRvbmx5IHRoZW1lPzogdW5rbm93bjtcbn1cblxuLy8gQSBgd2luZG93YCBwcm9wZXJ0eSByYXRoZXIgdGhhbiBhIGNsb3N1cmUgdmFyaWFibGU6IHRoZSBhcHBseSBhbmQgdGhlIGNoZWNrXG4vLyBhcmUgc2VwYXJhdGUgZXZhbHVhdGlvbnMuIFNwZWxsZWQgb3V0IGluIHRoZSBob2xkZXIgcmF0aGVyIHRoYW4gc2hhcmVkIHRocm91Z2hcbi8vIGEgY29uc3RhbnQsIGJlY2F1c2UgYSBjbG9zdXJlIGlzIHNlcmlhbGl6ZWQgYW5kIGNhbm5vdCByZWFjaCBtb2R1bGUgc2NvcGUuXG4vL1xuLy8gS2VwdCBsb2NhbCByYXRoZXIgdGhhbiBkZWNsYXJlZCBnbG9iYWxseSwgbGlrZSBldmVyeSBvdGhlciBob2xkZXIgb2YgYVxuLy8gaGFybmVzcyBgd2luZG93YCBwcm9wZXJ0eSwgc28gaXQgbmV2ZXIgbGVha3MgaW50byBjb25zdW1lciB0eXBlcy5cbmludGVyZmFjZSBBcHBsaWVkVGhlbWVIb2xkZXIge1xuICBfX29ic2lkaWFuSW50ZWdyYXRpb25UZXN0aW5nQXBwbGllZFRoZW1lPzogT2JzaWRpYW5UaGVtZSB8IHVuZGVmaW5lZDtcbn1cblxuaW50ZXJmYWNlIEFwcGx5VGhlbWVJbnB1dCBleHRlbmRzIEdlbmVyaWNPYmplY3Qge1xuICByZWFkb25seSB0aGVtZTogT2JzaWRpYW5UaGVtZTtcbiAgcmVhZG9ubHkgdGltZW91dEluTWlsbGlzZWNvbmRzOiBudW1iZXI7XG59XG5cbmNvbnN0IERFRkFVTFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAxMF8wMDA7XG5cbi8qKlxuICogU3dpdGNoZXMgT2JzaWRpYW4gdG8gaXRzIGRhcmsgb3IgbGlnaHQgdGhlbWUsIHNhdmVzIHRoZSBjb25maWcgYXQgb25jZSwgYW5kXG4gKiB3YWl0cyB1bnRpbCB0aGUgdGhlbWUgaXMgYm90aCBvbiBzY3JlZW4gYW5kIG9uIGRpc2suXG4gKlxuICogVXNlIGl0IGluc3RlYWQgb2YgYGFwcC5jaGFuZ2VUaGVtZWAgaW4gYSBjYXB0dXJlIHN1aXRlOiBzZWUgdGhlIG1vZHVsZVxuICogY29tbWVudCBmb3IgdGhlIHJlbG9hZCB0aGF0IHNpbGVudGx5IHVuZG9lcyBhIGJhcmUgYGNoYW5nZVRoZW1lYC4gVGhlIHRoZW1lXG4gKiBpcyByZWNvcmRlZCwgYW5kIGBjYXB0dXJlT2JzaWRpYW5TY3JlZW5zaG90YCB0aGVuIHJlZnVzZXMgYW55IGZyYW1lIHdob3NlIGJvZHlcbiAqIGlzIG5vIGxvbmdlciBpbiBpdCwgc28gYSB0aGVtZSBsb3N0IGFmdGVyIHRoaXMgY2FsbCBmYWlscyB0aGUgY2FwdHVyZSBieSBuYW1lXG4gKiBpbnN0ZWFkIG9mIHJld3JpdGluZyBhIGNvbW1pdHRlZCBmcmFtZS5cbiAqXG4gKiBAcGFyYW0gcGFyYW1zIC0gVGhlIHRoZW1lLCBob3cgbG9uZyB0byB3YWl0LCBhbmQgdHJhbnNwb3J0IC8gdmF1bHQgb3ZlcnJpZGVzLlxuICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyBvbmNlIHRoZSB0aGVtZSBpcyBhcHBsaWVkIGFuZCBzYXZlZC5cbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIHRoZW1lIGlzIG5vdCBhcHBsaWVkIGFuZCBzYXZlZCB3aXRoaW4gdGhlIHRpbWVvdXQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhcHBseU9ic2lkaWFuVGhlbWUocGFyYW1zOiBBcHBseU9ic2lkaWFuVGhlbWVQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qge1xuICAgIHRoZW1lLFxuICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kcyA9IERFRkFVTFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMsXG4gICAgdHJhbnNwb3J0LFxuICAgIHZhdWx0UGF0aFxuICB9ID0gcGFyYW1zO1xuXG4gIGF3YWl0IGV2YWxJbk9ic2lkaWFuKG5vcm1hbGl6ZU9wdGlvbmFsUHJvcGVydGllczxFdmFsSW5PYnNpZGlhblBhcmFtczxBcHBseVRoZW1lSW5wdXQsIHVuZGVmaW5lZD4+KHtcbiAgICBhc3luYyBjYWxsYmFjayh7IGFwcCwgbGliOiB7IHdhaXRVbnRpbCB9LCB0aGVtZTogcmVxdWVzdGVkVGhlbWUsIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogdGltZW91dCB9KTogUHJvbWlzZTx1bmRlZmluZWQ+IHtcbiAgICAgIGNvbnN0IHRoZW1lTmFtZSA9IHJlcXVlc3RlZFRoZW1lID09PSAnZGFyaycgPyAnb2JzaWRpYW4nIDogJ21vb25zdG9uZSc7XG4gICAgICBjb25zdCBib2R5Q2xhc3MgPSBgdGhlbWUtJHtyZXF1ZXN0ZWRUaGVtZX1gO1xuXG4gICAgICBhcHAuY2hhbmdlVGhlbWUodGhlbWVOYW1lKTtcbiAgICAgIC8vIFNhdmluZyBub3csIHJhdGhlciB0aGFuIG9uIGBjaGFuZ2VUaGVtZWAncyBvd24gZGVib3VuY2VkIHNjaGVkdWxlLCBpcyB0aGVcbiAgICAgIC8vIHdob2xlIGZpeDogYSByZWxvYWQgdGhhdCBmaW5kcyB0aGUgdGhlbWUgb24gZGlzayBoYXMgbm90aGluZyB0byBkcm9wLlxuICAgICAgYXdhaXQgYXBwLnZhdWx0LnNhdmVDb25maWcoKTtcblxuICAgICAgYXdhaXQgd2FpdFVudGlsKHtcbiAgICAgICAgbWVzc2FnZTogYHRoZSAke3JlcXVlc3RlZFRoZW1lfSB0aGVtZSB0byBiZSBhcHBsaWVkIGFuZCBzYXZlZCB0byBhcHBlYXJhbmNlLmpzb25gLFxuICAgICAgICBhc3luYyBwcmVkaWNhdGUoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICAgICAgY29uc3QgYXBwZWFyYW5jZSA9IGF3YWl0IGFwcC52YXVsdC5yZWFkQ29uZmlnSnNvbignYXBwZWFyYW5jZScpIGFzIEFwcGVhcmFuY2VDb25maWcgfCBudWxsO1xuICAgICAgICAgIHJldHVybiBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5jb250YWlucyhib2R5Q2xhc3MpICYmIGFwcGVhcmFuY2U/LnRoZW1lID09PSB0aGVtZU5hbWU7XG4gICAgICAgIH0sXG4gICAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogdGltZW91dFxuICAgICAgfSk7XG5cbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLXN5bnRheCAtLSBBcHByb3ZlZCBkb3VibGUgY2FzdDogdGhlIGFwcGxpZWQgdGhlbWUgaXMgdGhpcyBoZWxwZXIncyBvd24gV2luZG93IHByb3BlcnR5LCBrZXB0IGxvY2FsIHJhdGhlciB0aGFuIGRlY2xhcmVkIGdsb2JhbGx5LlxuICAgICAgKGdsb2JhbFRoaXMgYXMgdW5rbm93biBhcyBBcHBsaWVkVGhlbWVIb2xkZXIpLl9fb2JzaWRpYW5JbnRlZ3JhdGlvblRlc3RpbmdBcHBsaWVkVGhlbWUgPSByZXF1ZXN0ZWRUaGVtZTtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSxcbiAgICBpbnB1dDoge1xuICAgICAgdGhlbWUsXG4gICAgICB0aW1lb3V0SW5NaWxsaXNlY29uZHNcbiAgICB9LFxuICAgIHRyYW5zcG9ydCxcbiAgICB2YXVsdFBhdGhcbiAgfSkpO1xufVxuXG4vKipcbiAqIFRocm93cyB3aGVuIHRoZSBib2R5IGhhcyBsZWZ0IHRoZSB0aGVtZSB7QGxpbmsgYXBwbHlPYnNpZGlhblRoZW1lfSBhcHBsaWVkLlxuICpcbiAqIE5vdGhpbmcgaXMgY2hlY2tlZCB3aGVuIG5vIHRoZW1lIHdhcyBhcHBsaWVkIHRocm91Z2ggaXQsIHNpbmNlIHRoZXJlIGlzIHRoZW5cbiAqIG5vIGV4cGVjdGF0aW9uIHRvIGhvbGQgdGhlIGZyYW1lIHRvLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBXaGF0IGlzIGJlaW5nIHJlZnVzZWQsIGFuZCB0cmFuc3BvcnQgLyB2YXVsdCBvdmVycmlkZXMuXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHRoZW1lIHN0aWxsIGhvbGRzLCBvciBub25lXG4gKiAgIHdhcyBhcHBsaWVkLlxuICogQHRocm93cyBFcnJvciBuYW1pbmcgdGhlIGFwcGxpZWQgdGhlbWUgYW5kIHRoZSBvbmUgb24gc2NyZWVuLCBpZiB0aGV5IGRpZmZlci5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFzc2VydE9ic2lkaWFuVGhlbWVVbmNoYW5nZWQocGFyYW1zOiBBc3NlcnRPYnNpZGlhblRoZW1lVW5jaGFuZ2VkUGFyYW1zKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHtcbiAgICBhY3Rpb24sXG4gICAgdHJhbnNwb3J0LFxuICAgIHZhdWx0UGF0aFxuICB9ID0gcGFyYW1zO1xuXG4gIGNvbnN0IG1pc21hdGNoID0gYXdhaXQgZXZhbEluT2JzaWRpYW4obm9ybWFsaXplT3B0aW9uYWxQcm9wZXJ0aWVzPEV2YWxJbk9ic2lkaWFuUGFyYW1zPFJlY29yZDxzdHJpbmcsIG5ldmVyPiwgbnVsbCB8IHN0cmluZz4+KHtcbiAgICBjYWxsYmFjaygpOiBudWxsIHwgc3RyaW5nIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLXN5bnRheCAtLSBBcHByb3ZlZCBkb3VibGUgY2FzdDogdGhlIGFwcGxpZWQgdGhlbWUgaXMgdGhpcyBoZWxwZXIncyBvd24gV2luZG93IHByb3BlcnR5LCBrZXB0IGxvY2FsIHJhdGhlciB0aGFuIGRlY2xhcmVkIGdsb2JhbGx5LlxuICAgICAgY29uc3QgYXBwbGllZFRoZW1lID0gKGdsb2JhbFRoaXMgYXMgdW5rbm93biBhcyBBcHBsaWVkVGhlbWVIb2xkZXIpLl9fb2JzaWRpYW5JbnRlZ3JhdGlvblRlc3RpbmdBcHBsaWVkVGhlbWU7XG4gICAgICBpZiAoYXBwbGllZFRoZW1lID09PSB1bmRlZmluZWQgfHwgZG9jdW1lbnQuYm9keS5jbGFzc0xpc3QuY29udGFpbnMoYHRoZW1lLSR7YXBwbGllZFRoZW1lfWApKSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBsZXQgc2hvd25UaGVtZSA9ICduZWl0aGVyIGRhcmsgbm9yIGxpZ2h0JztcbiAgICAgIGlmIChkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5jb250YWlucygndGhlbWUtZGFyaycpKSB7XG4gICAgICAgIHNob3duVGhlbWUgPSAnZGFyayc7XG4gICAgICB9IGVsc2UgaWYgKGRvY3VtZW50LmJvZHkuY2xhc3NMaXN0LmNvbnRhaW5zKCd0aGVtZS1saWdodCcpKSB7XG4gICAgICAgIHNob3duVGhlbWUgPSAnbGlnaHQnO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGBhcHBseU9ic2lkaWFuVGhlbWUgYXBwbGllZCB0aGUgJHthcHBsaWVkVGhlbWV9IHRoZW1lLCBidXQgdGhlIGJvZHkgaXMgbm93ICR7c2hvd25UaGVtZX1gO1xuICAgIH0sXG4gICAgaW5wdXQ6IHt9LFxuICAgIHRyYW5zcG9ydCxcbiAgICB2YXVsdFBhdGhcbiAgfSkpO1xuXG4gIGlmIChtaXNtYXRjaCAhPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBSZWZ1c2luZyB0byAke2FjdGlvbn06ICR7bWlzbWF0Y2h9LiBTb21ldGhpbmcgcmVzZXQgdGhlIHRoZW1lIGFmdGVyIGl0IHdhcyBhcHBsaWVkIC0gdHlwaWNhbGx5IGEgYmFyZSBhcHAuY2hhbmdlVGhlbWUsIG9yIGEgY29uZmlnIHJlbG9hZC4gQ2FsbCBhcHBseU9ic2lkaWFuVGhlbWUgYWdhaW4sIG9yIHBhc3Mgc2hvdWxkVmVyaWZ5VGhlbWU6IGZhbHNlIHRvIGNhcHR1cmUgdGhlIGZyYW1lIGFzIGl0IGlzLmBcbiAgICApO1xuICB9XG59XG5cbi8qIHY4IGlnbm9yZSBzdG9wICovXG4iXSwKICAibWFwcGluZ3MiOiAiQUFnQ0EsU0FBUyxzQkFBc0I7QUFDL0IsU0FBUyxtQ0FBbUM7QUE2RTVDLE1BQU0sa0NBQWtDO0FBZ0J4QyxlQUFzQixtQkFBbUIsUUFBaUQ7QUFDeEYsUUFBTTtBQUFBLElBQ0o7QUFBQSxJQUNBLHdCQUF3QjtBQUFBLElBQ3hCO0FBQUEsSUFDQTtBQUFBLEVBQ0YsSUFBSTtBQUVKLFFBQU0sZUFBZSw0QkFBOEU7QUFBQSxJQUNqRyxNQUFNLFNBQVMsRUFBRSxLQUFLLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxnQkFBZ0IsdUJBQXVCLFFBQVEsR0FBdUI7QUFDckgsWUFBTSxZQUFZLG1CQUFtQixTQUFTLGFBQWE7QUFDM0QsWUFBTSxZQUFZLFNBQVMsY0FBYztBQUV6QyxVQUFJLFlBQVksU0FBUztBQUd6QixZQUFNLElBQUksTUFBTSxXQUFXO0FBRTNCLFlBQU0sVUFBVTtBQUFBLFFBQ2QsU0FBUyxPQUFPLGNBQWM7QUFBQSxRQUM5QixNQUFNLFlBQThCO0FBQ2xDLGdCQUFNLGFBQWEsTUFBTSxJQUFJLE1BQU0sZUFBZSxZQUFZO0FBQzlELGlCQUFPLFNBQVMsS0FBSyxVQUFVLFNBQVMsU0FBUyxLQUFLLFlBQVksVUFBVTtBQUFBLFFBQzlFO0FBQUEsUUFDQSx1QkFBdUI7QUFBQSxNQUN6QixDQUFDO0FBR0QsTUFBQyxXQUE2QywyQ0FBMkM7QUFDekYsYUFBTztBQUFBLElBQ1Q7QUFBQSxJQUNBLE9BQU87QUFBQSxNQUNMO0FBQUEsTUFDQTtBQUFBLElBQ0Y7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLEVBQ0YsQ0FBQyxDQUFDO0FBQ0o7QUFhQSxlQUFzQiw2QkFBNkIsUUFBMkQ7QUFDNUcsUUFBTTtBQUFBLElBQ0o7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLEVBQ0YsSUFBSTtBQUVKLFFBQU0sV0FBVyxNQUFNLGVBQWUsNEJBQXdGO0FBQUEsSUFDNUgsV0FBMEI7QUFFeEIsWUFBTSxlQUFnQixXQUE2QztBQUNuRSxVQUFJLGlCQUFpQixVQUFhLFNBQVMsS0FBSyxVQUFVLFNBQVMsU0FBUyxZQUFZLEVBQUUsR0FBRztBQUMzRixlQUFPO0FBQUEsTUFDVDtBQUVBLFVBQUksYUFBYTtBQUNqQixVQUFJLFNBQVMsS0FBSyxVQUFVLFNBQVMsWUFBWSxHQUFHO0FBQ2xELHFCQUFhO0FBQUEsTUFDZixXQUFXLFNBQVMsS0FBSyxVQUFVLFNBQVMsYUFBYSxHQUFHO0FBQzFELHFCQUFhO0FBQUEsTUFDZjtBQUNBLGFBQU8sa0NBQWtDLFlBQVksK0JBQStCLFVBQVU7QUFBQSxJQUNoRztBQUFBLElBQ0EsT0FBTyxDQUFDO0FBQUEsSUFDUjtBQUFBLElBQ0E7QUFBQSxFQUNGLENBQUMsQ0FBQztBQUVGLE1BQUksYUFBYSxNQUFNO0FBQ3JCLFVBQU0sSUFBSTtBQUFBLE1BQ1IsZUFBZSxNQUFNLEtBQUssUUFBUTtBQUFBLElBQ3BDO0FBQUEsRUFDRjtBQUNGOyIsCiAgIm5hbWVzIjogW10KfQo=
@@ -15,7 +15,14 @@
15
15
  * name — see `hide-vault-name.ts` for why a `temp-vault-<random>` bleeds
16
16
  * through the caption band and rewrites a checked-in PNG on every run — and by
17
17
  * hiding the focused element's blinking caret for the length of the capture —
18
- * see `hide-caret.ts` for why that is a transparent caret and not a blur.
18
+ * see `hide-caret.ts` for why that is a transparent caret and not a blur —
19
+ * and by drawing the window focused whether or not the OS gave it focus — see
20
+ * `pin-window-focus.ts` for the title bar that otherwise darkens one run in
21
+ * five.
22
+ *
23
+ * And it refuses a frame whose theme has changed since `applyObsidianTheme` set
24
+ * it — see `apply-obsidian-theme.ts` for the config reload that silently turns a
25
+ * dark capture run light.
19
26
  */
20
27
  import type { ObsidianTransport } from './transport.mjs';
21
28
  /**
@@ -56,6 +63,33 @@ export interface CaptureObsidianScreenshotOptions {
56
63
  * @default `true`
57
64
  */
58
65
  readonly shouldHideVaultName?: boolean;
66
+ /**
67
+ * Whether to draw the window as focused while capturing, so the frame does
68
+ * not depend on whether the operating system gave the window foreground
69
+ * focus that run.
70
+ *
71
+ * Obsidian darkens the title bar of an unfocused window, and a freshly
72
+ * launched one is routinely refused the foreground. The `is-focused` class is
73
+ * added for the capture and handed back to Obsidian afterwards; the window's
74
+ * real focus is never touched. Turn it off only to photograph the unfocused
75
+ * chrome itself, and expect such a frame to differ between runs.
76
+ *
77
+ * @default `true`
78
+ */
79
+ readonly shouldPinWindowFocus?: boolean;
80
+ /**
81
+ * Whether to refuse the frame when the body is no longer in the theme
82
+ * `applyObsidianTheme` last applied.
83
+ *
84
+ * A theme lost after it was applied fails nothing on its own: the frame is
85
+ * simply shot in the other theme and written over the committed one. Refusing
86
+ * turns that into a named error. Nothing is checked when no theme was applied
87
+ * through `applyObsidianTheme`. Turn it off only for a frame that changes the
88
+ * theme on purpose.
89
+ *
90
+ * @default `true`
91
+ */
92
+ readonly shouldVerifyTheme?: boolean;
59
93
  /**
60
94
  * Override the transport. When omitted, the transport the current test
61
95
  * context is driving is used.
@@ -83,10 +117,15 @@ export interface CaptureObsidianScreenshotOptions {
83
117
  * photograph it. The focused element's caret is hidden for the capture too, and
84
118
  * restored after it, so a frame with a focused field does not alternate between
85
119
  * two blink phases; {@link CaptureObsidianScreenshotOptions.shouldHideCaret}
120
+ * turns that off. The window is drawn focused whether or not the OS gave it
121
+ * focus; {@link CaptureObsidianScreenshotOptions.shouldPinWindowFocus} turns
122
+ * that off. A frame whose body has left the theme `applyObsidianTheme`
123
+ * applied is refused; {@link CaptureObsidianScreenshotOptions.shouldVerifyTheme}
86
124
  * turns that off.
87
125
  *
88
126
  * @param options - Optional size, transport and vault overrides.
89
127
  * @returns A {@link Promise} that resolves to the raw PNG bytes.
90
- * @throws Error if the active transport cannot capture screenshots.
128
+ * @throws Error if the active transport cannot capture screenshots, or if the
129
+ * theme has changed since `applyObsidianTheme` applied it.
91
130
  */
92
131
  export declare function captureObsidianScreenshot(options?: CaptureObsidianScreenshotOptions): Promise<Uint8Array>;
@@ -1,4 +1,5 @@
1
1
  import process from "node:process";
2
+ import { assertObsidianThemeUnchanged } from "./apply-obsidian-theme.mjs";
2
3
  import {
3
4
  getTransportOptions,
4
5
  getVaultPath
@@ -6,12 +7,15 @@ import {
6
7
  import { hideCaret } from "./hide-caret.mjs";
7
8
  import { hideVaultName } from "./hide-vault-name.mjs";
8
9
  import { normalizeOptionalProperties } from "./normalize-optional-properties.mjs";
10
+ import { pinWindowFocus } from "./pin-window-focus.mjs";
9
11
  import { getOrCreateTransport } from "./transport-factory.mjs";
10
12
  async function captureObsidianScreenshot(options) {
11
13
  const {
12
14
  heightInPixels,
13
15
  shouldHideCaret = true,
14
16
  shouldHideVaultName = true,
17
+ shouldPinWindowFocus = true,
18
+ shouldVerifyTheme = true,
15
19
  transport: transportOverride,
16
20
  vaultPath,
17
21
  widthInPixels
@@ -21,6 +25,13 @@ async function captureObsidianScreenshot(options) {
21
25
  if (!transport.captureScreenshot) {
22
26
  throw new Error("captureObsidianScreenshot: the active transport cannot capture screenshots.");
23
27
  }
28
+ if (shouldVerifyTheme) {
29
+ await assertObsidianThemeUnchanged({
30
+ action: "capture a screenshot",
31
+ transport,
32
+ vaultPath: cwd
33
+ });
34
+ }
24
35
  if (shouldHideVaultName) {
25
36
  await hideVaultName({
26
37
  transport,
@@ -31,6 +42,10 @@ async function captureObsidianScreenshot(options) {
31
42
  transport,
32
43
  vaultPath: cwd
33
44
  }) : void 0;
45
+ const pinnedWindowFocus = shouldPinWindowFocus ? await pinWindowFocus({
46
+ transport,
47
+ vaultPath: cwd
48
+ }) : void 0;
34
49
  try {
35
50
  return await transport.captureScreenshot(normalizeOptionalProperties({
36
51
  cwd,
@@ -38,10 +53,11 @@ async function captureObsidianScreenshot(options) {
38
53
  widthInPixels
39
54
  }));
40
55
  } finally {
56
+ await pinnedWindowFocus?.restore();
41
57
  await hiddenCaret?.restore();
42
58
  }
43
59
  }
44
60
  export {
45
61
  captureObsidianScreenshot
46
62
  };
47
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NhcHR1cmUtb2JzaWRpYW4tc2NyZWVuc2hvdC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBAZmlsZVxuICpcbiAqIENhcHR1cmVzIGEgc2NyZWVuc2hvdCBvZiB0aGUgT2JzaWRpYW4gaW5zdGFuY2UgdGhlIGN1cnJlbnQgdGVzdCBjb250ZXh0IGlzXG4gKiBhbHJlYWR5IGRyaXZpbmcuXG4gKlxuICogVGhlIHRyYW5zcG9ydC1sZXZlbCBgY2FwdHVyZVNjcmVlbnNob3RgIG5lZWRzIGEgdHJhbnNwb3J0IGluIGhhbmQsIHdoaWNoIGFcbiAqIHRlc3QgcnVubmluZyB1bmRlciB0aGUgaGFybmVzcydzIGdsb2JhbCBzZXR1cCBuZXZlciBoYXMgXHUyMDE0IHRoZSBpbnN0YW5jZSBpc1xuICogb3duZWQgYnkgdGhhdCBzZXR1cCBhbmQgcmVhY2hlZCB0aHJvdWdoIHRoZSBjb250ZXh0IHByb3ZpZGVyLiBUaGlzIGlzIHRoZVxuICogY29udGV4dC1yZXNvbHZpbmcgZW50cnkgcG9pbnQsIHRoZSBzY3JlZW5zaG90IGNvdW50ZXJwYXJ0IG9mXG4gKiBgZXZhbEluT2JzaWRpYW5gIC8gYHBvbGxJbk9ic2lkaWFuYDogd2l0aCBubyBhcmd1bWVudHMgYXQgYWxsIGl0IGNhcHR1cmVzXG4gKiB3aGF0ZXZlciBpbnN0YW5jZSB0aGUgYWN0aXZlIHByb2plY3QgaXMgZHJpdmluZywgZGVza3RvcCBvciBtb2JpbGUuXG4gKlxuICogSXQgYWxzbyBtYWtlcyB0aGUgZnJhbWUgUkVQUk9EVUNJQkxFIGJlZm9yZSB0YWtpbmcgaXQsIGJ5IGhpZGluZyB0aGUgdmF1bHQnc1xuICogbmFtZSBcdTIwMTQgc2VlIGBoaWRlLXZhdWx0LW5hbWUudHNgIGZvciB3aHkgYSBgdGVtcC12YXVsdC08cmFuZG9tPmAgYmxlZWRzXG4gKiB0aHJvdWdoIHRoZSBjYXB0aW9uIGJhbmQgYW5kIHJld3JpdGVzIGEgY2hlY2tlZC1pbiBQTkcgb24gZXZlcnkgcnVuIFx1MjAxNCBhbmQgYnlcbiAqIGhpZGluZyB0aGUgZm9jdXNlZCBlbGVtZW50J3MgYmxpbmtpbmcgY2FyZXQgZm9yIHRoZSBsZW5ndGggb2YgdGhlIGNhcHR1cmUgXHUyMDE0XG4gKiBzZWUgYGhpZGUtY2FyZXQudHNgIGZvciB3aHkgdGhhdCBpcyBhIHRyYW5zcGFyZW50IGNhcmV0IGFuZCBub3QgYSBibHVyLlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGNvZGUgKGRyaXZlcyBhIGxpdmUgT2JzaWRpYW4pIGNvdmVyZWQgYnkgaW50ZWdyYXRpb24gdGVzdHMsIG5vdCB1bml0IHRlc3RzLiAqL1xuXG5pbXBvcnQgcHJvY2VzcyBmcm9tICdub2RlOnByb2Nlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IENhcHR1cmVTY3JlZW5zaG90UGFyYW1zIH0gZnJvbSAnLi9jYXB0dXJlLXNjcmVlbnNob3QubWpzJztcbmltcG9ydCB0eXBlIHsgT2JzaWRpYW5UcmFuc3BvcnQgfSBmcm9tICcuL3RyYW5zcG9ydC5tanMnO1xuXG5pbXBvcnQge1xuICBnZXRUcmFuc3BvcnRPcHRpb25zLFxuICBnZXRWYXVsdFBhdGhcbn0gZnJvbSAnLi9jb250ZXh0LXByb3ZpZGVyLm1qcyc7XG5pbXBvcnQgeyBoaWRlQ2FyZXQgfSBmcm9tICcuL2hpZGUtY2FyZXQubWpzJztcbmltcG9ydCB7IGhpZGVWYXVsdE5hbWUgfSBmcm9tICcuL2hpZGUtdmF1bHQtbmFtZS5tanMnO1xuaW1wb3J0IHsgbm9ybWFsaXplT3B0aW9uYWxQcm9wZXJ0aWVzIH0gZnJvbSAnLi9ub3JtYWxpemUtb3B0aW9uYWwtcHJvcGVydGllcy5tanMnO1xuaW1wb3J0IHsgZ2V0T3JDcmVhdGVUcmFuc3BvcnQgfSBmcm9tICcuL3RyYW5zcG9ydC1mYWN0b3J5Lm1qcyc7XG5cbi8qKlxuICogT3B0aW9ucyBmb3Ige0BsaW5rIGNhcHR1cmVPYnNpZGlhblNjcmVlbnNob3R9LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zIHtcbiAgLyoqXG4gICAqIFRoZSBleGFjdCBoZWlnaHQgaW4gcGl4ZWxzIHRoZSBjYXB0dXJlZCBpbWFnZSBzaG91bGQgaGF2ZS5cbiAgICpcbiAgICogRGVza3RvcCBvbmx5LCBhbmQgb25seSBtZWFuaW5nZnVsIHRvZ2V0aGVyIHdpdGgge0BsaW5rIHdpZHRoSW5QaXhlbHN9LlxuICAgKiBJZ25vcmVkIG9uIG1vYmlsZSwgd2hlcmUgdGhlIGltYWdlIGlzIGFsd2F5cyB0aGUgZGV2aWNlJ3MgbmF0aXZlXG4gICAqIGZyYW1lYnVmZmVyIFx1MjAxNCBzaXplIHRob3NlIGJ5IGNob29zaW5nIGFuIEFWRCB3aXRoIHRoZSB3YW50ZWQgc2NyZWVuXG4gICAqIGdlb21ldHJ5LlxuICAgKi9cbiAgcmVhZG9ubHkgaGVpZ2h0SW5QaXhlbHM/OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gaGlkZSB0aGUgZm9jdXNlZCBlbGVtZW50J3MgY2FyZXQgd2hpbGUgY2FwdHVyaW5nLCBzbyB0aGUgZnJhbWVcbiAgICogZG9lcyBub3QgZGVwZW5kIG9uIHdoaWNoIHBoYXNlIG9mIGl0cyBibGluayB0aGUgY2FwdHVyZSBjYXVnaHQuXG4gICAqXG4gICAqIFRoZSBjYXJldCBpcyBtYWRlIHRyYW5zcGFyZW50LCBub3QgYmx1cnJlZDogdGhlIGZvY3VzLCBpdHMgcmluZyBhbmRcbiAgICogYW55dGhpbmcgb3BlbiBiZWNhdXNlIG9mIGl0IFx1MjAxNCBhIHN1Z2dlc3RlciB1bmRlciBhbiBpbnB1dCBcdTIwMTQgYWxsIHN0YXkgYXMgdGhlXG4gICAqIHRlc3QgYnVpbHQgdGhlbSwgYW5kIHRoZSBjYXJldCBpcyBwdXQgYmFjayBvbmNlIHRoZSBmcmFtZSBpcyB0YWtlbi4gVHVybiBpdFxuICAgKiBvZmYgb25seSB0byBwaG90b2dyYXBoIHRoZSBjYXJldCBpdHNlbGYsIGFuZCBleHBlY3Qgc3VjaCBhIGZyYW1lIHRvIGRpZmZlclxuICAgKiBiZXR3ZWVuIHJ1bnMuXG4gICAqXG4gICAqIEBkZWZhdWx0IGB0cnVlYFxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkSGlkZUNhcmV0PzogYm9vbGVhbjtcblxuICAvKipcbiAgICogV2hldGhlciB0byBoaWRlIHRoZSB2YXVsdCdzIG5hbWUgYmVmb3JlIGNhcHR1cmluZywgc28gdGhlIGZyYW1lIGRvZXMgbm90XG4gICAqIGRlcGVuZCBvbiB0aGUgcmFuZG9tIHN1ZmZpeCBvZiB0aGUgaGFybmVzcydzIHRlbXBvcmFyeSB2YXVsdC5cbiAgICpcbiAgICogQSBkZWZhdWx0IHJhdGhlciB0aGFuIGEga25vYjogcmVwcm9kdWNpYmlsaXR5IGlzIHdoYXQgYSBjaGVja2VkLWluXG4gICAqIHNjcmVlbnNob3QgaXMgZm9yLCBhbmQgdGhlIHJvdyBpdCBjb2xsYXBzZXMgc2l0cyB1bmRlciB0aGUgY2FwdGlvbiBiYW5kLFxuICAgKiBzbyBub3RoaW5nIGEgcmVhZGVyIHNlZXMgbW92ZXMuIFR1cm4gaXQgb2ZmIG9ubHkgdG8gcGhvdG9ncmFwaCB0aGUgdmF1bHRcbiAgICogc3dpdGNoZXIgaXRzZWxmLlxuICAgKlxuICAgKiBAZGVmYXVsdCBgdHJ1ZWBcbiAgICovXG4gIHJlYWRvbmx5IHNob3VsZEhpZGVWYXVsdE5hbWU/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB0aGUgdHJhbnNwb3J0LiBXaGVuIG9taXR0ZWQsIHRoZSB0cmFuc3BvcnQgdGhlIGN1cnJlbnQgdGVzdFxuICAgKiBjb250ZXh0IGlzIGRyaXZpbmcgaXMgdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IHRyYW5zcG9ydD86IE9ic2lkaWFuVHJhbnNwb3J0O1xuXG4gIC8qKlxuICAgKiBUaGUgdmF1bHQgcGF0aCB0byBjYXB0dXJlLiBXaGVuIG9taXR0ZWQsIHRoZSBjdXJyZW50IHRlc3QgY29udGV4dCdzIHZhdWx0XG4gICAqIGlzIHVzZWQuXG4gICAqL1xuICByZWFkb25seSB2YXVsdFBhdGg/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBleGFjdCB3aWR0aCBpbiBwaXhlbHMgdGhlIGNhcHR1cmVkIGltYWdlIHNob3VsZCBoYXZlLlxuICAgKlxuICAgKiBTZWUge0BsaW5rIGhlaWdodEluUGl4ZWxzfSBcdTIwMTQgdGhlIHR3byBhcmUgc2V0IHRvZ2V0aGVyIG9yIG5vdCBhdCBhbGwuXG4gICAqL1xuICByZWFkb25seSB3aWR0aEluUGl4ZWxzPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIENhcHR1cmVzIGEgUE5HIHNjcmVlbnNob3Qgb2YgdGhlIHJ1bm5pbmcgT2JzaWRpYW4gaW5zdGFuY2UsIHJlc29sdmluZyB0aGVcbiAqIHRyYW5zcG9ydCBhbmQgdmF1bHQgZnJvbSB0aGUgY3VycmVudCB0ZXN0IGNvbnRleHQuXG4gKlxuICogVGhlIHZhdWx0J3MgbmFtZSBpcyBoaWRkZW4gZmlyc3QsIHNvIHR3byBydW5zIG9mIHRoZSBzYW1lIHN1aXRlIGFnYWluc3QgdHdvXG4gKiBkaWZmZXJlbnRseS1uYW1lZCB0ZW1wb3JhcnkgdmF1bHRzIHByb2R1Y2UgYnl0ZS1pZGVudGljYWwgUE5Hcy4gUGFzc1xuICoge0BsaW5rIENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zLnNob3VsZEhpZGVWYXVsdE5hbWV9IGFzIGBmYWxzZWAgdG9cbiAqIHBob3RvZ3JhcGggaXQuIFRoZSBmb2N1c2VkIGVsZW1lbnQncyBjYXJldCBpcyBoaWRkZW4gZm9yIHRoZSBjYXB0dXJlIHRvbywgYW5kXG4gKiByZXN0b3JlZCBhZnRlciBpdCwgc28gYSBmcmFtZSB3aXRoIGEgZm9jdXNlZCBmaWVsZCBkb2VzIG5vdCBhbHRlcm5hdGUgYmV0d2VlblxuICogdHdvIGJsaW5rIHBoYXNlczsge0BsaW5rIENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zLnNob3VsZEhpZGVDYXJldH1cbiAqIHR1cm5zIHRoYXQgb2ZmLlxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9uYWwgc2l6ZSwgdHJhbnNwb3J0IGFuZCB2YXVsdCBvdmVycmlkZXMuXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHRvIHRoZSByYXcgUE5HIGJ5dGVzLlxuICogQHRocm93cyBFcnJvciBpZiB0aGUgYWN0aXZlIHRyYW5zcG9ydCBjYW5ub3QgY2FwdHVyZSBzY3JlZW5zaG90cy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhcHR1cmVPYnNpZGlhblNjcmVlbnNob3Qob3B0aW9ucz86IENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gIGNvbnN0IHtcbiAgICBoZWlnaHRJblBpeGVscyxcbiAgICBzaG91bGRIaWRlQ2FyZXQgPSB0cnVlLFxuICAgIHNob3VsZEhpZGVWYXVsdE5hbWUgPSB0cnVlLFxuICAgIHRyYW5zcG9ydDogdHJhbnNwb3J0T3ZlcnJpZGUsXG4gICAgdmF1bHRQYXRoLFxuICAgIHdpZHRoSW5QaXhlbHNcbiAgfSA9IG9wdGlvbnMgPz8ge307XG5cbiAgY29uc3QgY3dkID0gdmF1bHRQYXRoID8/IGdldFZhdWx0UGF0aCgpID8/IHByb2Nlc3MuY3dkKCk7XG4gIGNvbnN0IHRyYW5zcG9ydCA9IHRyYW5zcG9ydE92ZXJyaWRlID8/IGF3YWl0IGdldE9yQ3JlYXRlVHJhbnNwb3J0KGdldFRyYW5zcG9ydE9wdGlvbnMoKSk7XG5cbiAgaWYgKCF0cmFuc3BvcnQuY2FwdHVyZVNjcmVlbnNob3QpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NhcHR1cmVPYnNpZGlhblNjcmVlbnNob3Q6IHRoZSBhY3RpdmUgdHJhbnNwb3J0IGNhbm5vdCBjYXB0dXJlIHNjcmVlbnNob3RzLicpO1xuICB9XG5cbiAgaWYgKHNob3VsZEhpZGVWYXVsdE5hbWUpIHtcbiAgICBhd2FpdCBoaWRlVmF1bHROYW1lKHtcbiAgICAgIHRyYW5zcG9ydCxcbiAgICAgIHZhdWx0UGF0aDogY3dkXG4gICAgfSk7XG4gIH1cblxuICBjb25zdCBoaWRkZW5DYXJldCA9IHNob3VsZEhpZGVDYXJldFxuICAgID8gYXdhaXQgaGlkZUNhcmV0KHtcbiAgICAgIHRyYW5zcG9ydCxcbiAgICAgIHZhdWx0UGF0aDogY3dkXG4gICAgfSlcbiAgICA6IHVuZGVmaW5lZDtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCB0cmFuc3BvcnQuY2FwdHVyZVNjcmVlbnNob3Qobm9ybWFsaXplT3B0aW9uYWxQcm9wZXJ0aWVzPENhcHR1cmVTY3JlZW5zaG90UGFyYW1zPih7XG4gICAgICBjd2QsXG4gICAgICBoZWlnaHRJblBpeGVscyxcbiAgICAgIHdpZHRoSW5QaXhlbHNcbiAgICB9KSk7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgaGlkZGVuQ2FyZXQ/LnJlc3RvcmUoKTtcbiAgfVxufVxuXG4vKiB2OCBpZ25vcmUgc3RvcCAqL1xuIl0sCiAgIm1hcHBpbmdzIjogIkFBc0JBLE9BQU8sYUFBYTtBQUtwQjtBQUFBLEVBQ0U7QUFBQSxFQUNBO0FBQUEsT0FDSztBQUNQLFNBQVMsaUJBQWlCO0FBQzFCLFNBQVMscUJBQXFCO0FBQzlCLFNBQVMsbUNBQW1DO0FBQzVDLFNBQVMsNEJBQTRCO0FBK0VyQyxlQUFzQiwwQkFBMEIsU0FBaUU7QUFDL0csUUFBTTtBQUFBLElBQ0o7QUFBQSxJQUNBLGtCQUFrQjtBQUFBLElBQ2xCLHNCQUFzQjtBQUFBLElBQ3RCLFdBQVc7QUFBQSxJQUNYO0FBQUEsSUFDQTtBQUFBLEVBQ0YsSUFBSSxXQUFXLENBQUM7QUFFaEIsUUFBTSxNQUFNLGFBQWEsYUFBYSxLQUFLLFFBQVEsSUFBSTtBQUN2RCxRQUFNLFlBQVkscUJBQXFCLE1BQU0scUJBQXFCLG9CQUFvQixDQUFDO0FBRXZGLE1BQUksQ0FBQyxVQUFVLG1CQUFtQjtBQUNoQyxVQUFNLElBQUksTUFBTSw2RUFBNkU7QUFBQSxFQUMvRjtBQUVBLE1BQUkscUJBQXFCO0FBQ3ZCLFVBQU0sY0FBYztBQUFBLE1BQ2xCO0FBQUEsTUFDQSxXQUFXO0FBQUEsSUFDYixDQUFDO0FBQUEsRUFDSDtBQUVBLFFBQU0sY0FBYyxrQkFDaEIsTUFBTSxVQUFVO0FBQUEsSUFDaEI7QUFBQSxJQUNBLFdBQVc7QUFBQSxFQUNiLENBQUMsSUFDQztBQUVKLE1BQUk7QUFDRixXQUFPLE1BQU0sVUFBVSxrQkFBa0IsNEJBQXFEO0FBQUEsTUFDNUY7QUFBQSxNQUNBO0FBQUEsTUFDQTtBQUFBLElBQ0YsQ0FBQyxDQUFDO0FBQUEsRUFDSixVQUFFO0FBQ0EsVUFBTSxhQUFhLFFBQVE7QUFBQSxFQUM3QjtBQUNGOyIsCiAgIm5hbWVzIjogW10KfQo=
63
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NhcHR1cmUtb2JzaWRpYW4tc2NyZWVuc2hvdC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBAZmlsZVxuICpcbiAqIENhcHR1cmVzIGEgc2NyZWVuc2hvdCBvZiB0aGUgT2JzaWRpYW4gaW5zdGFuY2UgdGhlIGN1cnJlbnQgdGVzdCBjb250ZXh0IGlzXG4gKiBhbHJlYWR5IGRyaXZpbmcuXG4gKlxuICogVGhlIHRyYW5zcG9ydC1sZXZlbCBgY2FwdHVyZVNjcmVlbnNob3RgIG5lZWRzIGEgdHJhbnNwb3J0IGluIGhhbmQsIHdoaWNoIGFcbiAqIHRlc3QgcnVubmluZyB1bmRlciB0aGUgaGFybmVzcydzIGdsb2JhbCBzZXR1cCBuZXZlciBoYXMgXHUyMDE0IHRoZSBpbnN0YW5jZSBpc1xuICogb3duZWQgYnkgdGhhdCBzZXR1cCBhbmQgcmVhY2hlZCB0aHJvdWdoIHRoZSBjb250ZXh0IHByb3ZpZGVyLiBUaGlzIGlzIHRoZVxuICogY29udGV4dC1yZXNvbHZpbmcgZW50cnkgcG9pbnQsIHRoZSBzY3JlZW5zaG90IGNvdW50ZXJwYXJ0IG9mXG4gKiBgZXZhbEluT2JzaWRpYW5gIC8gYHBvbGxJbk9ic2lkaWFuYDogd2l0aCBubyBhcmd1bWVudHMgYXQgYWxsIGl0IGNhcHR1cmVzXG4gKiB3aGF0ZXZlciBpbnN0YW5jZSB0aGUgYWN0aXZlIHByb2plY3QgaXMgZHJpdmluZywgZGVza3RvcCBvciBtb2JpbGUuXG4gKlxuICogSXQgYWxzbyBtYWtlcyB0aGUgZnJhbWUgUkVQUk9EVUNJQkxFIGJlZm9yZSB0YWtpbmcgaXQsIGJ5IGhpZGluZyB0aGUgdmF1bHQnc1xuICogbmFtZSBcdTIwMTQgc2VlIGBoaWRlLXZhdWx0LW5hbWUudHNgIGZvciB3aHkgYSBgdGVtcC12YXVsdC08cmFuZG9tPmAgYmxlZWRzXG4gKiB0aHJvdWdoIHRoZSBjYXB0aW9uIGJhbmQgYW5kIHJld3JpdGVzIGEgY2hlY2tlZC1pbiBQTkcgb24gZXZlcnkgcnVuIFx1MjAxNCBhbmQgYnlcbiAqIGhpZGluZyB0aGUgZm9jdXNlZCBlbGVtZW50J3MgYmxpbmtpbmcgY2FyZXQgZm9yIHRoZSBsZW5ndGggb2YgdGhlIGNhcHR1cmUgXHUyMDE0XG4gKiBzZWUgYGhpZGUtY2FyZXQudHNgIGZvciB3aHkgdGhhdCBpcyBhIHRyYW5zcGFyZW50IGNhcmV0IGFuZCBub3QgYSBibHVyIFx1MjAxNFxuICogYW5kIGJ5IGRyYXdpbmcgdGhlIHdpbmRvdyBmb2N1c2VkIHdoZXRoZXIgb3Igbm90IHRoZSBPUyBnYXZlIGl0IGZvY3VzIFx1MjAxNCBzZWVcbiAqIGBwaW4td2luZG93LWZvY3VzLnRzYCBmb3IgdGhlIHRpdGxlIGJhciB0aGF0IG90aGVyd2lzZSBkYXJrZW5zIG9uZSBydW4gaW5cbiAqIGZpdmUuXG4gKlxuICogQW5kIGl0IHJlZnVzZXMgYSBmcmFtZSB3aG9zZSB0aGVtZSBoYXMgY2hhbmdlZCBzaW5jZSBgYXBwbHlPYnNpZGlhblRoZW1lYCBzZXRcbiAqIGl0IFx1MjAxNCBzZWUgYGFwcGx5LW9ic2lkaWFuLXRoZW1lLnRzYCBmb3IgdGhlIGNvbmZpZyByZWxvYWQgdGhhdCBzaWxlbnRseSB0dXJucyBhXG4gKiBkYXJrIGNhcHR1cmUgcnVuIGxpZ2h0LlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGNvZGUgKGRyaXZlcyBhIGxpdmUgT2JzaWRpYW4pIGNvdmVyZWQgYnkgaW50ZWdyYXRpb24gdGVzdHMsIG5vdCB1bml0IHRlc3RzLiAqL1xuXG5pbXBvcnQgcHJvY2VzcyBmcm9tICdub2RlOnByb2Nlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IENhcHR1cmVTY3JlZW5zaG90UGFyYW1zIH0gZnJvbSAnLi9jYXB0dXJlLXNjcmVlbnNob3QubWpzJztcbmltcG9ydCB0eXBlIHsgT2JzaWRpYW5UcmFuc3BvcnQgfSBmcm9tICcuL3RyYW5zcG9ydC5tanMnO1xuXG5pbXBvcnQgeyBhc3NlcnRPYnNpZGlhblRoZW1lVW5jaGFuZ2VkIH0gZnJvbSAnLi9hcHBseS1vYnNpZGlhbi10aGVtZS5tanMnO1xuaW1wb3J0IHtcbiAgZ2V0VHJhbnNwb3J0T3B0aW9ucyxcbiAgZ2V0VmF1bHRQYXRoXG59IGZyb20gJy4vY29udGV4dC1wcm92aWRlci5tanMnO1xuaW1wb3J0IHsgaGlkZUNhcmV0IH0gZnJvbSAnLi9oaWRlLWNhcmV0Lm1qcyc7XG5pbXBvcnQgeyBoaWRlVmF1bHROYW1lIH0gZnJvbSAnLi9oaWRlLXZhdWx0LW5hbWUubWpzJztcbmltcG9ydCB7IG5vcm1hbGl6ZU9wdGlvbmFsUHJvcGVydGllcyB9IGZyb20gJy4vbm9ybWFsaXplLW9wdGlvbmFsLXByb3BlcnRpZXMubWpzJztcbmltcG9ydCB7IHBpbldpbmRvd0ZvY3VzIH0gZnJvbSAnLi9waW4td2luZG93LWZvY3VzLm1qcyc7XG5pbXBvcnQgeyBnZXRPckNyZWF0ZVRyYW5zcG9ydCB9IGZyb20gJy4vdHJhbnNwb3J0LWZhY3RvcnkubWpzJztcblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgY2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdH0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdE9wdGlvbnMge1xuICAvKipcbiAgICogVGhlIGV4YWN0IGhlaWdodCBpbiBwaXhlbHMgdGhlIGNhcHR1cmVkIGltYWdlIHNob3VsZCBoYXZlLlxuICAgKlxuICAgKiBEZXNrdG9wIG9ubHksIGFuZCBvbmx5IG1lYW5pbmdmdWwgdG9nZXRoZXIgd2l0aCB7QGxpbmsgd2lkdGhJblBpeGVsc30uXG4gICAqIElnbm9yZWQgb24gbW9iaWxlLCB3aGVyZSB0aGUgaW1hZ2UgaXMgYWx3YXlzIHRoZSBkZXZpY2UncyBuYXRpdmVcbiAgICogZnJhbWVidWZmZXIgXHUyMDE0IHNpemUgdGhvc2UgYnkgY2hvb3NpbmcgYW4gQVZEIHdpdGggdGhlIHdhbnRlZCBzY3JlZW5cbiAgICogZ2VvbWV0cnkuXG4gICAqL1xuICByZWFkb25seSBoZWlnaHRJblBpeGVscz86IG51bWJlcjtcblxuICAvKipcbiAgICogV2hldGhlciB0byBoaWRlIHRoZSBmb2N1c2VkIGVsZW1lbnQncyBjYXJldCB3aGlsZSBjYXB0dXJpbmcsIHNvIHRoZSBmcmFtZVxuICAgKiBkb2VzIG5vdCBkZXBlbmQgb24gd2hpY2ggcGhhc2Ugb2YgaXRzIGJsaW5rIHRoZSBjYXB0dXJlIGNhdWdodC5cbiAgICpcbiAgICogVGhlIGNhcmV0IGlzIG1hZGUgdHJhbnNwYXJlbnQsIG5vdCBibHVycmVkOiB0aGUgZm9jdXMsIGl0cyByaW5nIGFuZFxuICAgKiBhbnl0aGluZyBvcGVuIGJlY2F1c2Ugb2YgaXQgXHUyMDE0IGEgc3VnZ2VzdGVyIHVuZGVyIGFuIGlucHV0IFx1MjAxNCBhbGwgc3RheSBhcyB0aGVcbiAgICogdGVzdCBidWlsdCB0aGVtLCBhbmQgdGhlIGNhcmV0IGlzIHB1dCBiYWNrIG9uY2UgdGhlIGZyYW1lIGlzIHRha2VuLiBUdXJuIGl0XG4gICAqIG9mZiBvbmx5IHRvIHBob3RvZ3JhcGggdGhlIGNhcmV0IGl0c2VsZiwgYW5kIGV4cGVjdCBzdWNoIGEgZnJhbWUgdG8gZGlmZmVyXG4gICAqIGJldHdlZW4gcnVucy5cbiAgICpcbiAgICogQGRlZmF1bHQgYHRydWVgXG4gICAqL1xuICByZWFkb25seSBzaG91bGRIaWRlQ2FyZXQ/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIGhpZGUgdGhlIHZhdWx0J3MgbmFtZSBiZWZvcmUgY2FwdHVyaW5nLCBzbyB0aGUgZnJhbWUgZG9lcyBub3RcbiAgICogZGVwZW5kIG9uIHRoZSByYW5kb20gc3VmZml4IG9mIHRoZSBoYXJuZXNzJ3MgdGVtcG9yYXJ5IHZhdWx0LlxuICAgKlxuICAgKiBBIGRlZmF1bHQgcmF0aGVyIHRoYW4gYSBrbm9iOiByZXByb2R1Y2liaWxpdHkgaXMgd2hhdCBhIGNoZWNrZWQtaW5cbiAgICogc2NyZWVuc2hvdCBpcyBmb3IsIGFuZCB0aGUgcm93IGl0IGNvbGxhcHNlcyBzaXRzIHVuZGVyIHRoZSBjYXB0aW9uIGJhbmQsXG4gICAqIHNvIG5vdGhpbmcgYSByZWFkZXIgc2VlcyBtb3Zlcy4gVHVybiBpdCBvZmYgb25seSB0byBwaG90b2dyYXBoIHRoZSB2YXVsdFxuICAgKiBzd2l0Y2hlciBpdHNlbGYuXG4gICAqXG4gICAqIEBkZWZhdWx0IGB0cnVlYFxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkSGlkZVZhdWx0TmFtZT86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gZHJhdyB0aGUgd2luZG93IGFzIGZvY3VzZWQgd2hpbGUgY2FwdHVyaW5nLCBzbyB0aGUgZnJhbWUgZG9lc1xuICAgKiBub3QgZGVwZW5kIG9uIHdoZXRoZXIgdGhlIG9wZXJhdGluZyBzeXN0ZW0gZ2F2ZSB0aGUgd2luZG93IGZvcmVncm91bmRcbiAgICogZm9jdXMgdGhhdCBydW4uXG4gICAqXG4gICAqIE9ic2lkaWFuIGRhcmtlbnMgdGhlIHRpdGxlIGJhciBvZiBhbiB1bmZvY3VzZWQgd2luZG93LCBhbmQgYSBmcmVzaGx5XG4gICAqIGxhdW5jaGVkIG9uZSBpcyByb3V0aW5lbHkgcmVmdXNlZCB0aGUgZm9yZWdyb3VuZC4gVGhlIGBpcy1mb2N1c2VkYCBjbGFzcyBpc1xuICAgKiBhZGRlZCBmb3IgdGhlIGNhcHR1cmUgYW5kIGhhbmRlZCBiYWNrIHRvIE9ic2lkaWFuIGFmdGVyd2FyZHM7IHRoZSB3aW5kb3cnc1xuICAgKiByZWFsIGZvY3VzIGlzIG5ldmVyIHRvdWNoZWQuIFR1cm4gaXQgb2ZmIG9ubHkgdG8gcGhvdG9ncmFwaCB0aGUgdW5mb2N1c2VkXG4gICAqIGNocm9tZSBpdHNlbGYsIGFuZCBleHBlY3Qgc3VjaCBhIGZyYW1lIHRvIGRpZmZlciBiZXR3ZWVuIHJ1bnMuXG4gICAqXG4gICAqIEBkZWZhdWx0IGB0cnVlYFxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkUGluV2luZG93Rm9jdXM/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIHJlZnVzZSB0aGUgZnJhbWUgd2hlbiB0aGUgYm9keSBpcyBubyBsb25nZXIgaW4gdGhlIHRoZW1lXG4gICAqIGBhcHBseU9ic2lkaWFuVGhlbWVgIGxhc3QgYXBwbGllZC5cbiAgICpcbiAgICogQSB0aGVtZSBsb3N0IGFmdGVyIGl0IHdhcyBhcHBsaWVkIGZhaWxzIG5vdGhpbmcgb24gaXRzIG93bjogdGhlIGZyYW1lIGlzXG4gICAqIHNpbXBseSBzaG90IGluIHRoZSBvdGhlciB0aGVtZSBhbmQgd3JpdHRlbiBvdmVyIHRoZSBjb21taXR0ZWQgb25lLiBSZWZ1c2luZ1xuICAgKiB0dXJucyB0aGF0IGludG8gYSBuYW1lZCBlcnJvci4gTm90aGluZyBpcyBjaGVja2VkIHdoZW4gbm8gdGhlbWUgd2FzIGFwcGxpZWRcbiAgICogdGhyb3VnaCBgYXBwbHlPYnNpZGlhblRoZW1lYC4gVHVybiBpdCBvZmYgb25seSBmb3IgYSBmcmFtZSB0aGF0IGNoYW5nZXMgdGhlXG4gICAqIHRoZW1lIG9uIHB1cnBvc2UuXG4gICAqXG4gICAqIEBkZWZhdWx0IGB0cnVlYFxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkVmVyaWZ5VGhlbWU/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB0aGUgdHJhbnNwb3J0LiBXaGVuIG9taXR0ZWQsIHRoZSB0cmFuc3BvcnQgdGhlIGN1cnJlbnQgdGVzdFxuICAgKiBjb250ZXh0IGlzIGRyaXZpbmcgaXMgdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IHRyYW5zcG9ydD86IE9ic2lkaWFuVHJhbnNwb3J0O1xuXG4gIC8qKlxuICAgKiBUaGUgdmF1bHQgcGF0aCB0byBjYXB0dXJlLiBXaGVuIG9taXR0ZWQsIHRoZSBjdXJyZW50IHRlc3QgY29udGV4dCdzIHZhdWx0XG4gICAqIGlzIHVzZWQuXG4gICAqL1xuICByZWFkb25seSB2YXVsdFBhdGg/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBleGFjdCB3aWR0aCBpbiBwaXhlbHMgdGhlIGNhcHR1cmVkIGltYWdlIHNob3VsZCBoYXZlLlxuICAgKlxuICAgKiBTZWUge0BsaW5rIGhlaWdodEluUGl4ZWxzfSBcdTIwMTQgdGhlIHR3byBhcmUgc2V0IHRvZ2V0aGVyIG9yIG5vdCBhdCBhbGwuXG4gICAqL1xuICByZWFkb25seSB3aWR0aEluUGl4ZWxzPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIENhcHR1cmVzIGEgUE5HIHNjcmVlbnNob3Qgb2YgdGhlIHJ1bm5pbmcgT2JzaWRpYW4gaW5zdGFuY2UsIHJlc29sdmluZyB0aGVcbiAqIHRyYW5zcG9ydCBhbmQgdmF1bHQgZnJvbSB0aGUgY3VycmVudCB0ZXN0IGNvbnRleHQuXG4gKlxuICogVGhlIHZhdWx0J3MgbmFtZSBpcyBoaWRkZW4gZmlyc3QsIHNvIHR3byBydW5zIG9mIHRoZSBzYW1lIHN1aXRlIGFnYWluc3QgdHdvXG4gKiBkaWZmZXJlbnRseS1uYW1lZCB0ZW1wb3JhcnkgdmF1bHRzIHByb2R1Y2UgYnl0ZS1pZGVudGljYWwgUE5Hcy4gUGFzc1xuICoge0BsaW5rIENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zLnNob3VsZEhpZGVWYXVsdE5hbWV9IGFzIGBmYWxzZWAgdG9cbiAqIHBob3RvZ3JhcGggaXQuIFRoZSBmb2N1c2VkIGVsZW1lbnQncyBjYXJldCBpcyBoaWRkZW4gZm9yIHRoZSBjYXB0dXJlIHRvbywgYW5kXG4gKiByZXN0b3JlZCBhZnRlciBpdCwgc28gYSBmcmFtZSB3aXRoIGEgZm9jdXNlZCBmaWVsZCBkb2VzIG5vdCBhbHRlcm5hdGUgYmV0d2VlblxuICogdHdvIGJsaW5rIHBoYXNlczsge0BsaW5rIENhcHR1cmVPYnNpZGlhblNjcmVlbnNob3RPcHRpb25zLnNob3VsZEhpZGVDYXJldH1cbiAqIHR1cm5zIHRoYXQgb2ZmLiBUaGUgd2luZG93IGlzIGRyYXduIGZvY3VzZWQgd2hldGhlciBvciBub3QgdGhlIE9TIGdhdmUgaXRcbiAqIGZvY3VzOyB7QGxpbmsgQ2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdE9wdGlvbnMuc2hvdWxkUGluV2luZG93Rm9jdXN9IHR1cm5zXG4gKiB0aGF0IG9mZi4gQSBmcmFtZSB3aG9zZSBib2R5IGhhcyBsZWZ0IHRoZSB0aGVtZSBgYXBwbHlPYnNpZGlhblRoZW1lYFxuICogYXBwbGllZCBpcyByZWZ1c2VkOyB7QGxpbmsgQ2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdE9wdGlvbnMuc2hvdWxkVmVyaWZ5VGhlbWV9XG4gKiB0dXJucyB0aGF0IG9mZi5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIHNpemUsIHRyYW5zcG9ydCBhbmQgdmF1bHQgb3ZlcnJpZGVzLlxuICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyB0byB0aGUgcmF3IFBORyBieXRlcy5cbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIGFjdGl2ZSB0cmFuc3BvcnQgY2Fubm90IGNhcHR1cmUgc2NyZWVuc2hvdHMsIG9yIGlmIHRoZVxuICogICB0aGVtZSBoYXMgY2hhbmdlZCBzaW5jZSBgYXBwbHlPYnNpZGlhblRoZW1lYCBhcHBsaWVkIGl0LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdChvcHRpb25zPzogQ2FwdHVyZU9ic2lkaWFuU2NyZWVuc2hvdE9wdGlvbnMpOiBQcm9taXNlPFVpbnQ4QXJyYXk+IHtcbiAgY29uc3Qge1xuICAgIGhlaWdodEluUGl4ZWxzLFxuICAgIHNob3VsZEhpZGVDYXJldCA9IHRydWUsXG4gICAgc2hvdWxkSGlkZVZhdWx0TmFtZSA9IHRydWUsXG4gICAgc2hvdWxkUGluV2luZG93Rm9jdXMgPSB0cnVlLFxuICAgIHNob3VsZFZlcmlmeVRoZW1lID0gdHJ1ZSxcbiAgICB0cmFuc3BvcnQ6IHRyYW5zcG9ydE92ZXJyaWRlLFxuICAgIHZhdWx0UGF0aCxcbiAgICB3aWR0aEluUGl4ZWxzXG4gIH0gPSBvcHRpb25zID8/IHt9O1xuXG4gIGNvbnN0IGN3ZCA9IHZhdWx0UGF0aCA/PyBnZXRWYXVsdFBhdGgoKSA/PyBwcm9jZXNzLmN3ZCgpO1xuICBjb25zdCB0cmFuc3BvcnQgPSB0cmFuc3BvcnRPdmVycmlkZSA/PyBhd2FpdCBnZXRPckNyZWF0ZVRyYW5zcG9ydChnZXRUcmFuc3BvcnRPcHRpb25zKCkpO1xuXG4gIGlmICghdHJhbnNwb3J0LmNhcHR1cmVTY3JlZW5zaG90KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdjYXB0dXJlT2JzaWRpYW5TY3JlZW5zaG90OiB0aGUgYWN0aXZlIHRyYW5zcG9ydCBjYW5ub3QgY2FwdHVyZSBzY3JlZW5zaG90cy4nKTtcbiAgfVxuXG4gIGlmIChzaG91bGRWZXJpZnlUaGVtZSkge1xuICAgIGF3YWl0IGFzc2VydE9ic2lkaWFuVGhlbWVVbmNoYW5nZWQoe1xuICAgICAgYWN0aW9uOiAnY2FwdHVyZSBhIHNjcmVlbnNob3QnLFxuICAgICAgdHJhbnNwb3J0LFxuICAgICAgdmF1bHRQYXRoOiBjd2RcbiAgICB9KTtcbiAgfVxuXG4gIGlmIChzaG91bGRIaWRlVmF1bHROYW1lKSB7XG4gICAgYXdhaXQgaGlkZVZhdWx0TmFtZSh7XG4gICAgICB0cmFuc3BvcnQsXG4gICAgICB2YXVsdFBhdGg6IGN3ZFxuICAgIH0pO1xuICB9XG5cbiAgY29uc3QgaGlkZGVuQ2FyZXQgPSBzaG91bGRIaWRlQ2FyZXRcbiAgICA/IGF3YWl0IGhpZGVDYXJldCh7XG4gICAgICB0cmFuc3BvcnQsXG4gICAgICB2YXVsdFBhdGg6IGN3ZFxuICAgIH0pXG4gICAgOiB1bmRlZmluZWQ7XG5cbiAgY29uc3QgcGlubmVkV2luZG93Rm9jdXMgPSBzaG91bGRQaW5XaW5kb3dGb2N1c1xuICAgID8gYXdhaXQgcGluV2luZG93Rm9jdXMoe1xuICAgICAgdHJhbnNwb3J0LFxuICAgICAgdmF1bHRQYXRoOiBjd2RcbiAgICB9KVxuICAgIDogdW5kZWZpbmVkO1xuXG4gIHRyeSB7XG4gICAgcmV0dXJuIGF3YWl0IHRyYW5zcG9ydC5jYXB0dXJlU2NyZWVuc2hvdChub3JtYWxpemVPcHRpb25hbFByb3BlcnRpZXM8Q2FwdHVyZVNjcmVlbnNob3RQYXJhbXM+KHtcbiAgICAgIGN3ZCxcbiAgICAgIGhlaWdodEluUGl4ZWxzLFxuICAgICAgd2lkdGhJblBpeGVsc1xuICAgIH0pKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCBwaW5uZWRXaW5kb3dGb2N1cz8ucmVzdG9yZSgpO1xuICAgIGF3YWl0IGhpZGRlbkNhcmV0Py5yZXN0b3JlKCk7XG4gIH1cbn1cblxuLyogdjggaWdub3JlIHN0b3AgKi9cbiJdLAogICJtYXBwaW5ncyI6ICJBQTZCQSxPQUFPLGFBQWE7QUFLcEIsU0FBUyxvQ0FBb0M7QUFDN0M7QUFBQSxFQUNFO0FBQUEsRUFDQTtBQUFBLE9BQ0s7QUFDUCxTQUFTLGlCQUFpQjtBQUMxQixTQUFTLHFCQUFxQjtBQUM5QixTQUFTLG1DQUFtQztBQUM1QyxTQUFTLHNCQUFzQjtBQUMvQixTQUFTLDRCQUE0QjtBQWlIckMsZUFBc0IsMEJBQTBCLFNBQWlFO0FBQy9HLFFBQU07QUFBQSxJQUNKO0FBQUEsSUFDQSxrQkFBa0I7QUFBQSxJQUNsQixzQkFBc0I7QUFBQSxJQUN0Qix1QkFBdUI7QUFBQSxJQUN2QixvQkFBb0I7QUFBQSxJQUNwQixXQUFXO0FBQUEsSUFDWDtBQUFBLElBQ0E7QUFBQSxFQUNGLElBQUksV0FBVyxDQUFDO0FBRWhCLFFBQU0sTUFBTSxhQUFhLGFBQWEsS0FBSyxRQUFRLElBQUk7QUFDdkQsUUFBTSxZQUFZLHFCQUFxQixNQUFNLHFCQUFxQixvQkFBb0IsQ0FBQztBQUV2RixNQUFJLENBQUMsVUFBVSxtQkFBbUI7QUFDaEMsVUFBTSxJQUFJLE1BQU0sNkVBQTZFO0FBQUEsRUFDL0Y7QUFFQSxNQUFJLG1CQUFtQjtBQUNyQixVQUFNLDZCQUE2QjtBQUFBLE1BQ2pDLFFBQVE7QUFBQSxNQUNSO0FBQUEsTUFDQSxXQUFXO0FBQUEsSUFDYixDQUFDO0FBQUEsRUFDSDtBQUVBLE1BQUkscUJBQXFCO0FBQ3ZCLFVBQU0sY0FBYztBQUFBLE1BQ2xCO0FBQUEsTUFDQSxXQUFXO0FBQUEsSUFDYixDQUFDO0FBQUEsRUFDSDtBQUVBLFFBQU0sY0FBYyxrQkFDaEIsTUFBTSxVQUFVO0FBQUEsSUFDaEI7QUFBQSxJQUNBLFdBQVc7QUFBQSxFQUNiLENBQUMsSUFDQztBQUVKLFFBQU0sb0JBQW9CLHVCQUN0QixNQUFNLGVBQWU7QUFBQSxJQUNyQjtBQUFBLElBQ0EsV0FBVztBQUFBLEVBQ2IsQ0FBQyxJQUNDO0FBRUosTUFBSTtBQUNGLFdBQU8sTUFBTSxVQUFVLGtCQUFrQiw0QkFBcUQ7QUFBQSxNQUM1RjtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsSUFDRixDQUFDLENBQUM7QUFBQSxFQUNKLFVBQUU7QUFDQSxVQUFNLG1CQUFtQixRQUFRO0FBQ2pDLFVBQU0sYUFBYSxRQUFRO0FBQUEsRUFDN0I7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * The Chromium switches that make an owned desktop instance rasterize the same
5
+ * frame to the same bytes every time it is captured.
6
+ *
7
+ * A sized capture pins the viewport with `Emulation.setDeviceMetricsOverride`
8
+ * and clears the override afterwards, so every capture re-rasterizes the page
9
+ * at the requested size. With GPU rasterization on, that re-raster is not
10
+ * deterministic: the soft `box-shadow` of a modal and of a suggester come back
11
+ * one value off in a single channel in a fraction of the captures, with nothing
12
+ * visibly different. Measured on a 1200x800 frame with a modal and a suggester
13
+ * open, 300 captures each:
14
+ *
15
+ * | Launch | Distinct frames |
16
+ * | --- | --- |
17
+ * | default | 3 (253 / 39 / 8) |
18
+ * | `--disable-partial-raster` | 3 (201 / 83 / 16) |
19
+ * | `--force-color-profile=srgb` | 3 (209 / 78 / 13) |
20
+ * | no size override at all | 1 |
21
+ * | `--disable-gpu-rasterization` | 1 |
22
+ *
23
+ * So the noise is the GPU rasterizer redoing the page after a resize, and the
24
+ * resize cannot go, because a sized capture is the whole point of the size
25
+ * options. Rasterizing on the CPU closes it while compositing stays on the GPU,
26
+ * which is why this is `--disable-gpu-rasterization` and not `--disable-gpu`
27
+ * (also stable, but it moves compositing to software as well). It cost no
28
+ * capture time in the same measurement.
29
+ */
30
+ /**
31
+ * Chromium switches passed to every **owned** desktop instance so its captures
32
+ * are byte-reproducible.
33
+ *
34
+ * Only an owned instance gets them: an attached Obsidian was launched by
35
+ * somebody else, and its switches are not the harness's to choose.
36
+ */
37
+ export declare const DETERMINISTIC_RASTER_LAUNCH_FLAGS: readonly string[];
@@ -0,0 +1,7 @@
1
+ const DETERMINISTIC_RASTER_LAUNCH_FLAGS = [
2
+ "--disable-gpu-rasterization"
3
+ ];
4
+ export {
5
+ DETERMINISTIC_RASTER_LAUNCH_FLAGS
6
+ };
7
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2RldGVybWluaXN0aWMtcmFzdGVyLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogVGhlIENocm9taXVtIHN3aXRjaGVzIHRoYXQgbWFrZSBhbiBvd25lZCBkZXNrdG9wIGluc3RhbmNlIHJhc3Rlcml6ZSB0aGUgc2FtZVxuICogZnJhbWUgdG8gdGhlIHNhbWUgYnl0ZXMgZXZlcnkgdGltZSBpdCBpcyBjYXB0dXJlZC5cbiAqXG4gKiBBIHNpemVkIGNhcHR1cmUgcGlucyB0aGUgdmlld3BvcnQgd2l0aCBgRW11bGF0aW9uLnNldERldmljZU1ldHJpY3NPdmVycmlkZWBcbiAqIGFuZCBjbGVhcnMgdGhlIG92ZXJyaWRlIGFmdGVyd2FyZHMsIHNvIGV2ZXJ5IGNhcHR1cmUgcmUtcmFzdGVyaXplcyB0aGUgcGFnZVxuICogYXQgdGhlIHJlcXVlc3RlZCBzaXplLiBXaXRoIEdQVSByYXN0ZXJpemF0aW9uIG9uLCB0aGF0IHJlLXJhc3RlciBpcyBub3RcbiAqIGRldGVybWluaXN0aWM6IHRoZSBzb2Z0IGBib3gtc2hhZG93YCBvZiBhIG1vZGFsIGFuZCBvZiBhIHN1Z2dlc3RlciBjb21lIGJhY2tcbiAqIG9uZSB2YWx1ZSBvZmYgaW4gYSBzaW5nbGUgY2hhbm5lbCBpbiBhIGZyYWN0aW9uIG9mIHRoZSBjYXB0dXJlcywgd2l0aCBub3RoaW5nXG4gKiB2aXNpYmx5IGRpZmZlcmVudC4gTWVhc3VyZWQgb24gYSAxMjAweDgwMCBmcmFtZSB3aXRoIGEgbW9kYWwgYW5kIGEgc3VnZ2VzdGVyXG4gKiBvcGVuLCAzMDAgY2FwdHVyZXMgZWFjaDpcbiAqXG4gKiB8IExhdW5jaCB8IERpc3RpbmN0IGZyYW1lcyB8XG4gKiB8IC0tLSB8IC0tLSB8XG4gKiB8IGRlZmF1bHQgfCAzICgyNTMgLyAzOSAvIDgpIHxcbiAqIHwgYC0tZGlzYWJsZS1wYXJ0aWFsLXJhc3RlcmAgfCAzICgyMDEgLyA4MyAvIDE2KSB8XG4gKiB8IGAtLWZvcmNlLWNvbG9yLXByb2ZpbGU9c3JnYmAgfCAzICgyMDkgLyA3OCAvIDEzKSB8XG4gKiB8IG5vIHNpemUgb3ZlcnJpZGUgYXQgYWxsIHwgMSB8XG4gKiB8IGAtLWRpc2FibGUtZ3B1LXJhc3Rlcml6YXRpb25gIHwgMSB8XG4gKlxuICogU28gdGhlIG5vaXNlIGlzIHRoZSBHUFUgcmFzdGVyaXplciByZWRvaW5nIHRoZSBwYWdlIGFmdGVyIGEgcmVzaXplLCBhbmQgdGhlXG4gKiByZXNpemUgY2Fubm90IGdvLCBiZWNhdXNlIGEgc2l6ZWQgY2FwdHVyZSBpcyB0aGUgd2hvbGUgcG9pbnQgb2YgdGhlIHNpemVcbiAqIG9wdGlvbnMuIFJhc3Rlcml6aW5nIG9uIHRoZSBDUFUgY2xvc2VzIGl0IHdoaWxlIGNvbXBvc2l0aW5nIHN0YXlzIG9uIHRoZSBHUFUsXG4gKiB3aGljaCBpcyB3aHkgdGhpcyBpcyBgLS1kaXNhYmxlLWdwdS1yYXN0ZXJpemF0aW9uYCBhbmQgbm90IGAtLWRpc2FibGUtZ3B1YFxuICogKGFsc28gc3RhYmxlLCBidXQgaXQgbW92ZXMgY29tcG9zaXRpbmcgdG8gc29mdHdhcmUgYXMgd2VsbCkuIEl0IGNvc3Qgbm9cbiAqIGNhcHR1cmUgdGltZSBpbiB0aGUgc2FtZSBtZWFzdXJlbWVudC5cbiAqL1xuXG4vKipcbiAqIENocm9taXVtIHN3aXRjaGVzIHBhc3NlZCB0byBldmVyeSAqKm93bmVkKiogZGVza3RvcCBpbnN0YW5jZSBzbyBpdHMgY2FwdHVyZXNcbiAqIGFyZSBieXRlLXJlcHJvZHVjaWJsZS5cbiAqXG4gKiBPbmx5IGFuIG93bmVkIGluc3RhbmNlIGdldHMgdGhlbTogYW4gYXR0YWNoZWQgT2JzaWRpYW4gd2FzIGxhdW5jaGVkIGJ5XG4gKiBzb21lYm9keSBlbHNlLCBhbmQgaXRzIHN3aXRjaGVzIGFyZSBub3QgdGhlIGhhcm5lc3MncyB0byBjaG9vc2UuXG4gKi9cbmV4cG9ydCBjb25zdCBERVRFUk1JTklTVElDX1JBU1RFUl9MQVVOQ0hfRkxBR1M6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAnLS1kaXNhYmxlLWdwdS1yYXN0ZXJpemF0aW9uJ1xuXTtcbiJdLAogICJtYXBwaW5ncyI6ICJBQXFDTyxNQUFNLG9DQUF1RDtBQUFBLEVBQ2xFO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -22,6 +22,10 @@ export interface ClickElementParams {
22
22
  * The element to click. The pointer is moved to its center. This is a live
23
23
  * renderer DOM node — the callback runs in-renderer, so no cross-process
24
24
  * serialization is needed (same as {@link TypeIntoEditorParams.editor}).
25
+ *
26
+ * The input goes to the window that owns the element (its
27
+ * `ownerDocument.defaultView`), so an element in a popout is driven in that
28
+ * popout with no extra parameter.
25
29
  */
26
30
  readonly element: HTMLElement;
27
31
  /**
@@ -49,6 +53,18 @@ export interface ClickMouseParams {
49
53
  * @default `[]`
50
54
  */
51
55
  readonly modifiers?: readonly Modifier[];
56
+ /**
57
+ * The Obsidian window to click in: the main window or a popout. Pass the
58
+ * window that owns the target, e.g. `leaf.view.containerEl.win` for a popout
59
+ * leaf. Each Obsidian window is its own Electron web contents, so a click
60
+ * sent to one never reaches another, and the coordinates are in the named
61
+ * window's viewport.
62
+ *
63
+ * **On mobile** there are no popouts, so any window but the main one throws.
64
+ *
65
+ * @default the main window (`globalThis`)
66
+ */
67
+ readonly window?: Window;
52
68
  /**
53
69
  * The x coordinate (web-contents DIP) to click at.
54
70
  */
@@ -148,6 +164,10 @@ export interface HoverElementParams {
148
164
  * The element to hover. The pointer is moved to its center. This is a live
149
165
  * renderer DOM node — the callback runs in-renderer, so no cross-process
150
166
  * serialization is needed (same as {@link TypeIntoEditorParams.editor}).
167
+ *
168
+ * The input goes to the window that owns the element (its
169
+ * `ownerDocument.defaultView`), so an element in a popout is driven in that
170
+ * popout with no extra parameter.
151
171
  */
152
172
  readonly element: HTMLElement;
153
173
  }
@@ -226,6 +246,10 @@ export interface Lib {
226
246
  * A real context menu actually opens, so a suite driving a right click must
227
247
  * close it (or remove the leftover `.menu` element) before the next test.
228
248
  *
249
+ * It clicks in the main window unless {@link ClickMouseParams.window} names a
250
+ * popout. {@link Lib.clickElement} needs no such parameter: it clicks in the
251
+ * window that owns its element.
252
+ *
229
253
  * **On mobile** the button model does not survive the port: touch has no
230
254
  * buttons, so `'left'` (the default) is a **tap**, `'right'` is the
231
255
  * **long-press** that opens Obsidian Mobile's context menu, and `'middle'`
@@ -313,6 +337,9 @@ export interface Lib {
313
337
  * **Desktop only — it throws on mobile**, for the same reason as
314
338
  * {@link Lib.hoverElement}: there is no touch pointer to move.
315
339
  *
340
+ * It moves the pointer in the main window unless
341
+ * {@link MoveMouseParams.window} names a popout.
342
+ *
316
343
  * **Must be awaited** — see {@link Lib.clickElement}.
317
344
  *
318
345
  * @param params - The web-contents DIP coordinates to move to.
@@ -371,6 +398,10 @@ export interface Lib {
371
398
  * expected effect via {@link Lib.waitUntil}. It targets the single shared
372
399
  * window's **global** focus, so only the DOM-focused element receives the key.
373
400
  *
401
+ * It presses the key in the main window unless
402
+ * {@link PressKeyParams.window} names a popout. Each Obsidian window is its
403
+ * own web contents, so a key pressed in one never reaches another.
404
+ *
374
405
  * **On mobile** the same sequence is injected through the WebView's debugger
375
406
  * (`Input.dispatchKeyEvent`), which is equally trusted. Named keys (`'Enter'`,
376
407
  * `'Escape'`, `'Tab'`, `'Backspace'`, `'Delete'`, the arrows) and single
@@ -461,6 +492,17 @@ export type MouseButton = 'left' | 'middle' | 'right';
461
492
  * Parameters for {@link Lib.moveMouse}.
462
493
  */
463
494
  export interface MoveMouseParams {
495
+ /**
496
+ * The Obsidian window to move the pointer in: the main window or a popout.
497
+ * Each Obsidian window is its own Electron web contents with its own pointer,
498
+ * and the coordinates are in the named window's viewport.
499
+ *
500
+ * Mobile has no pointer to move, so {@link Lib.moveMouse} throws there
501
+ * whatever this is.
502
+ *
503
+ * @default the main window (`globalThis`)
504
+ */
505
+ readonly window?: Window;
464
506
  /**
465
507
  * The x coordinate (web-contents DIP) to move the pointer to.
466
508
  */
@@ -515,6 +557,18 @@ export interface PressKeyParams {
515
557
  * @default `[]`
516
558
  */
517
559
  readonly modifiers?: readonly Modifier[];
560
+ /**
561
+ * The Obsidian window to press the key in: the main window or a popout. Pass
562
+ * the window that owns the focused target, e.g. `leaf.view.containerEl.win`
563
+ * for a popout leaf. Each Obsidian window is its own Electron web contents,
564
+ * so the key goes to the DOM-focused element of the window named here,
565
+ * whichever window the OS has focused, and never reaches another window.
566
+ *
567
+ * **On mobile** there are no popouts, so any window but the main one throws.
568
+ *
569
+ * @default the main window (`globalThis`)
570
+ */
571
+ readonly window?: Window;
518
572
  }
519
573
  /**
520
574
  * Parameters for {@link Lib.typeIntoEditor}.
@@ -523,6 +577,9 @@ export interface TypeIntoEditorParams {
523
577
  /**
524
578
  * The editor to type into. It is focused (with the caret moved to the end of
525
579
  * the document) before the keystrokes are injected.
580
+ *
581
+ * The keystrokes go to the window that owns the editor, so an editor in a
582
+ * popout is typed into in that popout with no extra parameter.
526
583
  */
527
584
  readonly editor: Editor;
528
585
  /**
@@ -540,6 +597,9 @@ export interface UnhoverElementParams {
540
597
  * just outside its bounding box. This is a live renderer DOM node — the
541
598
  * callback runs in-renderer, so no cross-process serialization is needed
542
599
  * (same as {@link TypeIntoEditorParams.editor}).
600
+ *
601
+ * The move goes to the window that owns the element, so an element in a
602
+ * popout is driven in that popout with no extra parameter.
543
603
  */
544
604
  readonly element: HTMLElement;
545
605
  }