@valbuild/shared 0.150.11 → 0.150.13

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.
@@ -26,6 +26,7 @@ export * from "./zod/SerializedSchema.js";
26
26
  export * from "./valCanvasProtocol.js";
27
27
  export * from "./elementPicker.js";
28
28
  export * from "./pageInspection.js";
29
+ export * from "./pageScreenshot.js";
29
30
  export * from "./quickPreview.js";
30
31
  export * from "./emptyOf.js";
31
32
  export * from "./aiTools/toolNames.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * A picture of the page, taken in the page: the assistant's
3
+ * `screenshot_page`, so it can see a change it made rather than only read it
4
+ * (`read_page` gives the words and the boxes).
5
+ *
6
+ * Taken HERE because nothing else can: the design tab is another origin than
7
+ * the proposal's page in its frame, and a browser lets no page photograph a
8
+ * frame of another origin. So the bridge renders its own document --
9
+ * `modern-screenshot` draws the DOM, with its computed styles, fonts and
10
+ * same-origin images, into a canvas -- and hands back a JPEG.
11
+ *
12
+ * It is a rendering of the DOM, not of the screen: an image from another
13
+ * origin that does not allow it, a video frame, a `<canvas>` a script drew
14
+ * and an iframe come out blank. Good enough to see a layout, a colour, a
15
+ * button that wrapped; the tool says what it cannot show.
16
+ *
17
+ * Loaded when asked: the library is not in a page that is never
18
+ * photographed.
19
+ */
20
+ /** What `screenshotPage` answers. */
21
+ export type PageScreenshot = {
22
+ /** A JPEG, as a data URL. */
23
+ dataUrl: string;
24
+ width: number;
25
+ height: number;
26
+ /** The whole page, from the top, rather than what was on screen. */
27
+ fullPage: boolean;
28
+ /** The page went on below what the picture shows. */
29
+ clipped: boolean;
30
+ };
31
+ /**
32
+ * The page in `win`, as it is now: what is on screen, or (`fullPage`) the
33
+ * whole page from the top, cut off at {@link MAX_FULL_PAGE_HEIGHT}. Elements
34
+ * `exclude` says are not the page's -- the picker's highlight -- are left
35
+ * out.
36
+ */
37
+ export declare function screenshotPage(win: Window, options?: {
38
+ fullPage?: boolean;
39
+ exclude?: (element: Element) => boolean;
40
+ }): Promise<PageScreenshot>;
@@ -1,7 +1,7 @@
1
1
  import { Json } from "@valbuild/core";
2
- export declare const VAL_SESSION_COOKIE: any;
3
- export declare const VAL_STATE_COOKIE: any;
4
- export declare const VAL_ENABLE_COOKIE_NAME: any;
2
+ export declare const VAL_SESSION_COOKIE: "val_session";
3
+ export declare const VAL_STATE_COOKIE: "val_state";
4
+ export declare const VAL_ENABLE_COOKIE_NAME: "val_enable";
5
5
  export type VAL_SESSION_COOKIE = typeof VAL_SESSION_COOKIE;
6
6
  export type VAL_STATE_COOKIE = typeof VAL_STATE_COOKIE;
7
7
  export type VAL_ENABLE_COOKIE_NAME = typeof VAL_ENABLE_COOKIE_NAME;
@@ -22,6 +22,7 @@
22
22
  import type { ModuleFilePath, SourcePath } from "@valbuild/core";
23
23
  import type { PickedElement, PickModifier } from "./elementPicker.js";
24
24
  import type { PageLocation, PageSnapshot } from "./pageInspection.js";
25
+ import type { PageScreenshot } from "./pageScreenshot.js";
25
26
  import { type QuickPreviewFiles } from "./quickPreview.js";
26
27
  /** Marks a page load as the studio's canvas frame. */
27
28
  export declare const VAL_CANVAS_PARAM = "val_canvas";
@@ -205,6 +206,16 @@ export type ValCanvasPageMessage = {
205
206
  requestId: string;
206
207
  snapshot: PageSnapshot | null;
207
208
  error?: string;
209
+ } | {
210
+ /**
211
+ * What `screenshotPage` asked for: the page as a JPEG data URL, or why
212
+ * it could not be taken (`pageScreenshot.ts`).
213
+ */
214
+ val: typeof VAL_CANVAS_MESSAGE;
215
+ type: "pageScreenshot";
216
+ requestId: string;
217
+ screenshot: PageScreenshot | null;
218
+ error?: string;
208
219
  } | {
209
220
  /**
210
221
  * A quick preview's files are stored in the page's worker, and a page
@@ -334,6 +345,16 @@ export type ValCanvasStudioMessage = {
334
345
  requestId: string;
335
346
  selector?: string;
336
347
  maxItems?: number;
348
+ } | {
349
+ /**
350
+ * Take a picture of the page (`screenshotPage`): the assistant's
351
+ * `screenshot_page`. Answered with `pageScreenshot` and the same
352
+ * `requestId`; a bridge that predates it never answers.
353
+ */
354
+ val: typeof VAL_CANVAS_MESSAGE;
355
+ type: "screenshotPage";
356
+ requestId: string;
357
+ fullPage?: boolean;
337
358
  } | {
338
359
  /**
339
360
  * Store a quick preview's files in this origin's worker
@@ -64,9 +64,9 @@ export declare const BinaryFileRef: z.ZodObject<{
64
64
  }, z.core.$strip>;
65
65
  export type BinaryFileRef = z.infer<typeof BinaryFileRef>;
66
66
  export declare const HistoricalModule: z.ZodObject<{
67
- source: z.ZodNullable<z.ZodType<JSONValueT, unknown, z.core.$ZodTypeInternals<JSONValueT, unknown>>>;
68
- schema: z.ZodNullable<z.ZodType<SerializedSchemaT, unknown, z.core.$ZodTypeInternals<SerializedSchemaT, unknown>>>;
69
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
67
+ source: z.ZodNullable<z.ZodType<import("@valbuild/core/patch").JSONValue, unknown, z.core.$ZodTypeInternals<import("@valbuild/core/patch").JSONValue, unknown>>>;
68
+ schema: z.ZodNullable<z.ZodType<import("@valbuild/core").SerializedSchema, unknown, z.core.$ZodTypeInternals<import("@valbuild/core").SerializedSchema, unknown>>>;
69
+ patchIds: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
70
70
  changedPaths: z.ZodArray<z.ZodString>;
71
71
  failures: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
72
72
  kind: z.ZodLiteral<"commit-not-found">;
@@ -109,9 +109,9 @@ export declare const HistoricalPatchSet: z.ZodObject<{
109
109
  hasArchive: z.ZodBoolean;
110
110
  }, z.core.$strip>;
111
111
  modules: z.ZodRecord<z.ZodString, z.ZodObject<{
112
- source: z.ZodNullable<z.ZodType<JSONValueT, unknown, z.core.$ZodTypeInternals<JSONValueT, unknown>>>;
113
- schema: z.ZodNullable<z.ZodType<SerializedSchemaT, unknown, z.core.$ZodTypeInternals<SerializedSchemaT, unknown>>>;
114
- patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
112
+ source: z.ZodNullable<z.ZodType<import("@valbuild/core/patch").JSONValue, unknown, z.core.$ZodTypeInternals<import("@valbuild/core/patch").JSONValue, unknown>>>;
113
+ schema: z.ZodNullable<z.ZodType<import("@valbuild/core").SerializedSchema, unknown, z.core.$ZodTypeInternals<import("@valbuild/core").SerializedSchema, unknown>>>;
114
+ patchIds: z.ZodArray<z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>>;
115
115
  changedPaths: z.ZodArray<z.ZodString>;
116
116
  failures: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
117
117
  kind: z.ZodLiteral<"commit-not-found">;
@@ -139,7 +139,7 @@ export declare const HistoricalPatchSet: z.ZodObject<{
139
139
  }, z.core.$strip>], "kind">>;
140
140
  }, z.core.$strip>>;
141
141
  patches: z.ZodArray<z.ZodObject<{
142
- patchId: z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>;
142
+ patchId: z.ZodString & z.ZodType<import("@valbuild/core").PatchId, string, z.core.$ZodTypeInternals<import("@valbuild/core").PatchId, string>>;
143
143
  moduleFilePath: z.ZodString;
144
144
  patch: z.ZodUnknown;
145
145
  authorId: z.ZodNullable<z.ZodString>;
@@ -1,4 +1,5 @@
1
1
  import type { JSONValue as JSONValueT, PatchJSON as PatchJSONT, PatchBlock as PatchBlockT, ParentRef as ParentRefT } from "@valbuild/core/patch";
2
+ import type { PatchId as PatchIdT } from "@valbuild/core";
2
3
  import { z } from "zod";
3
4
  /**
4
5
  * JSON, recursively — the shape of a Source over the wire.
@@ -66,7 +66,25 @@ export declare const ColorOptions: z.ZodObject<{
66
66
  export declare const SerializedColorSchema: z.ZodType<SerializedColorSchemaT>;
67
67
  export declare const CodeOptions: z.ZodObject<{
68
68
  language: z.ZodOptional<z.ZodEnum<{
69
- [x: string]: any;
69
+ html: "html";
70
+ css: "css";
71
+ json: "json";
72
+ typescript: "typescript";
73
+ javascript: "javascript";
74
+ javascriptreact: "javascriptreact";
75
+ typescriptreact: "typescriptreact";
76
+ java: "java";
77
+ xml: "xml";
78
+ markdown: "markdown";
79
+ sql: "sql";
80
+ python: "python";
81
+ rust: "rust";
82
+ php: "php";
83
+ go: "go";
84
+ cpp: "cpp";
85
+ sass: "sass";
86
+ vue: "vue";
87
+ angular: "angular";
70
88
  }>>;
71
89
  }, z.core.$strip>;
72
90
  export declare const SerializedCodeSchema: z.ZodType<SerializedCodeSchemaT>;
@@ -83,7 +101,7 @@ export declare const SerializedSettingsSchema: z.ZodType<SerializedSettingsSchem
83
101
  */
84
102
  export declare const SerializedValViewSchema: z.ZodObject<{
85
103
  type: z.ZodLiteral<"view">;
86
- moduleFilePath: z.ZodType<ModuleFilePathT, unknown, z.core.$ZodTypeInternals<ModuleFilePathT, unknown>>;
104
+ moduleFilePath: z.ZodType<import("@valbuild/core").ModuleFilePath, unknown, z.core.$ZodTypeInternals<import("@valbuild/core").ModuleFilePath, unknown>>;
87
105
  opt: z.ZodLiteral<false>;
88
106
  readonly: z.ZodOptional<z.ZodBoolean>;
89
107
  hidden: z.ZodOptional<z.ZodBoolean>;
@@ -322,7 +322,7 @@ function isValCanvasPageMessage(data) {
322
322
  if (_typeof(data) !== "object" || data === null) return false;
323
323
  var message = data;
324
324
  if (message.val !== VAL_CANVAS_MESSAGE) return false;
325
- return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
325
+ return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
326
326
  }
327
327
  function isPageLocation(value) {
328
328
  var location = value;
@@ -379,6 +379,10 @@ function isValCanvasStudioMessage(data) {
379
379
  var preview = data;
380
380
  return typeof preview.id === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(preview.id) && isQuickPreviewFiles(preview.files);
381
381
  }
382
+ if (message.type === "screenshotPage") {
383
+ var asked = data;
384
+ return typeof asked.requestId === "string" && (asked.fullPage === undefined || typeof asked.fullPage === "boolean");
385
+ }
382
386
  if (message.type === "readPage") {
383
387
  var read = data;
384
388
  return typeof read.requestId === "string" && (read.selector === undefined || typeof read.selector === "string") && (read.maxItems === undefined || typeof read.maxItems === "number");
@@ -1137,6 +1141,137 @@ function selectorOf(element, root) {
1137
1141
  return parts.join(" > ");
1138
1142
  }
1139
1143
 
1144
+ /**
1145
+ * A picture of the page, taken in the page: the assistant's
1146
+ * `screenshot_page`, so it can see a change it made rather than only read it
1147
+ * (`read_page` gives the words and the boxes).
1148
+ *
1149
+ * Taken HERE because nothing else can: the design tab is another origin than
1150
+ * the proposal's page in its frame, and a browser lets no page photograph a
1151
+ * frame of another origin. So the bridge renders its own document --
1152
+ * `modern-screenshot` draws the DOM, with its computed styles, fonts and
1153
+ * same-origin images, into a canvas -- and hands back a JPEG.
1154
+ *
1155
+ * It is a rendering of the DOM, not of the screen: an image from another
1156
+ * origin that does not allow it, a video frame, a `<canvas>` a script drew
1157
+ * and an iframe come out blank. Good enough to see a layout, a colour, a
1158
+ * button that wrapped; the tool says what it cannot show.
1159
+ *
1160
+ * Loaded when asked: the library is not in a page that is never
1161
+ * photographed.
1162
+ */
1163
+
1164
+ /** What `screenshotPage` answers. */
1165
+
1166
+ /** Wider than this is scaled down: a model reads a page no better larger. */
1167
+ var MAX_WIDTH = 1280;
1168
+ /** A whole page longer than this, in CSS pixels, is cut off below it. */
1169
+ var MAX_FULL_PAGE_HEIGHT = 4000;
1170
+ var JPEG_QUALITY = 0.85;
1171
+ /** How long an image or a font on the page is waited for. */
1172
+ var LOAD_TIMEOUT_MS = 8000;
1173
+
1174
+ /**
1175
+ * The page in `win`, as it is now: what is on screen, or (`fullPage`) the
1176
+ * whole page from the top, cut off at {@link MAX_FULL_PAGE_HEIGHT}. Elements
1177
+ * `exclude` says are not the page's -- the picker's highlight -- are left
1178
+ * out.
1179
+ */
1180
+ function screenshotPage(_x) {
1181
+ return _screenshotPage.apply(this, arguments);
1182
+ }
1183
+
1184
+ /** What the page is drawn on: the body's background, else the root's, else white. */
1185
+ function _screenshotPage() {
1186
+ _screenshotPage = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(win) {
1187
+ var _doc$body$scrollHeigh, _doc$body;
1188
+ var options,
1189
+ _yield$import,
1190
+ domToCanvas,
1191
+ doc,
1192
+ root,
1193
+ fullPage,
1194
+ pageWidth,
1195
+ pageHeight,
1196
+ top,
1197
+ height,
1198
+ exclude,
1199
+ drawn,
1200
+ scale,
1201
+ out,
1202
+ context,
1203
+ _args = arguments;
1204
+ return _regenerator().w(function (_context) {
1205
+ while (1) switch (_context.n) {
1206
+ case 0:
1207
+ options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
1208
+ _context.n = 1;
1209
+ return import('modern-screenshot');
1210
+ case 1:
1211
+ _yield$import = _context.v;
1212
+ domToCanvas = _yield$import.domToCanvas;
1213
+ doc = win.document;
1214
+ root = doc.documentElement;
1215
+ fullPage = options.fullPage === true;
1216
+ pageWidth = root.clientWidth || win.innerWidth;
1217
+ pageHeight = Math.max(root.scrollHeight, (_doc$body$scrollHeigh = (_doc$body = doc.body) === null || _doc$body === void 0 ? void 0 : _doc$body.scrollHeight) !== null && _doc$body$scrollHeigh !== void 0 ? _doc$body$scrollHeigh : 0);
1218
+ top = fullPage ? 0 : win.scrollY;
1219
+ height = fullPage ? Math.min(pageHeight, MAX_FULL_PAGE_HEIGHT) : Math.min(win.innerHeight, pageHeight - top);
1220
+ exclude = options.exclude; // Drawn to the bottom of what is wanted, and no further: the cost is the
1221
+ // page's size, so a viewport near the top is cheap.
1222
+ _context.n = 2;
1223
+ return domToCanvas(root, _objectSpread2({
1224
+ width: pageWidth,
1225
+ height: top + height,
1226
+ scale: 1,
1227
+ backgroundColor: pageBackground(win),
1228
+ timeout: LOAD_TIMEOUT_MS
1229
+ }, exclude !== undefined ? {
1230
+ filter: function filter(node) {
1231
+ return !(isElement(node) && exclude(node));
1232
+ }
1233
+ } : {}));
1234
+ case 2:
1235
+ drawn = _context.v;
1236
+ scale = Math.min(1, MAX_WIDTH / pageWidth);
1237
+ out = doc.createElement("canvas");
1238
+ out.width = Math.round(pageWidth * scale);
1239
+ out.height = Math.round(height * scale);
1240
+ context = out.getContext("2d");
1241
+ if (!(context === null)) {
1242
+ _context.n = 3;
1243
+ break;
1244
+ }
1245
+ throw new Error("This browser would not draw the picture.");
1246
+ case 3:
1247
+ context.drawImage(drawn, 0, top, pageWidth, height, 0, 0, out.width, out.height);
1248
+ return _context.a(2, {
1249
+ dataUrl: out.toDataURL("image/jpeg", JPEG_QUALITY),
1250
+ width: out.width,
1251
+ height: out.height,
1252
+ fullPage: fullPage,
1253
+ clipped: top + height < pageHeight
1254
+ });
1255
+ }
1256
+ }, _callee);
1257
+ }));
1258
+ return _screenshotPage.apply(this, arguments);
1259
+ }
1260
+ function pageBackground(win) {
1261
+ for (var _i = 0, _arr = [win.document.body, win.document.documentElement]; _i < _arr.length; _i++) {
1262
+ var element = _arr[_i];
1263
+ if (element === null) continue;
1264
+ var color = win.getComputedStyle(element).backgroundColor;
1265
+ if (color && color !== "transparent" && color !== "rgba(0, 0, 0, 0)") {
1266
+ return color;
1267
+ }
1268
+ }
1269
+ return "#ffffff";
1270
+ }
1271
+ function isElement(node) {
1272
+ return node.nodeType === 1;
1273
+ }
1274
+
1140
1275
  function parseRoutePattern(pattern) {
1141
1276
  if (pattern === "" || pattern === "/") {
1142
1277
  return [];
@@ -1378,4 +1513,4 @@ function getPatternFromModuleFilePath(moduleFilePath, srcFolder) {
1378
1513
  .replace("/page.val.ts", "").replace("/page.val.js", "");
1379
1514
  }
1380
1515
 
1381
- export { quickPreviewSiteOrigin as A, isDesignTabOrigin as B, parseRoutePattern as C, DEFAULT_CANVAS_SELECTION as D, formatRoutePattern as E, getNextAppRouterSourceFolder as F, getTanStackRouterSourceFolder as G, getPageRouterSourceFolder as H, isTanStackRoutesFolder as I, getPageRouterSitemapTree as J, getPatternFromModuleFilePath as K, QUICK_PREVIEW_PARAM as Q, VAL_CONFIG_SESSION_STORAGE_KEY as V, VAL_THEME_SESSION_STORAGE_KEY as a, VAL_AI_SESSION_STORAGE_KEY as b, VAL_CANVAS_PARAM as c, VAL_CANVAS_MESSAGE as d, DEFAULT_CANVAS_SELECTION_SOFT as e, isValCanvasStudioMessage as f, isValCanvasFrame as g, describeElement as h, isValCanvasPageMessage as i, attachElementPicker as j, createPickerOverlay as k, elementLabel as l, modifierLabel as m, watchPageLocation as n, readPageSnapshot as o, pageLocationOf as p, findBySelector as q, restoreScroll as r, startElementPicking as s, QUICK_PREVIEW_SERVICE_WORKER_PATH as t, QUICK_PREVIEW_STORE_MESSAGE as u, isQuickPreviewFiles as v, withValCanvasParam as w, QUICK_PREVIEW_SERVICE_WORKER_SOURCE as x, storeQuickPreview as y, withQuickPreviewParam as z };
1516
+ export { withQuickPreviewParam as A, quickPreviewSiteOrigin as B, isDesignTabOrigin as C, DEFAULT_CANVAS_SELECTION as D, parseRoutePattern as E, formatRoutePattern as F, getNextAppRouterSourceFolder as G, getTanStackRouterSourceFolder as H, getPageRouterSourceFolder as I, isTanStackRoutesFolder as J, getPageRouterSitemapTree as K, getPatternFromModuleFilePath as L, QUICK_PREVIEW_PARAM as Q, VAL_CONFIG_SESSION_STORAGE_KEY as V, VAL_THEME_SESSION_STORAGE_KEY as a, VAL_AI_SESSION_STORAGE_KEY as b, VAL_CANVAS_PARAM as c, VAL_CANVAS_MESSAGE as d, DEFAULT_CANVAS_SELECTION_SOFT as e, isValCanvasStudioMessage as f, isValCanvasFrame as g, describeElement as h, isValCanvasPageMessage as i, attachElementPicker as j, createPickerOverlay as k, elementLabel as l, modifierLabel as m, watchPageLocation as n, readPageSnapshot as o, pageLocationOf as p, findBySelector as q, restoreScroll as r, startElementPicking as s, screenshotPage as t, QUICK_PREVIEW_SERVICE_WORKER_PATH as u, QUICK_PREVIEW_STORE_MESSAGE as v, withValCanvasParam as w, isQuickPreviewFiles as x, QUICK_PREVIEW_SERVICE_WORKER_SOURCE as y, storeQuickPreview as z };
@@ -3,6 +3,24 @@
3
3
  var createForOfIteratorHelper = require('./createForOfIteratorHelper-9ca2078d.cjs.prod.js');
4
4
  var core = require('@valbuild/core');
5
5
 
6
+ function _interopNamespace(e) {
7
+ if (e && e.__esModule) return e;
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n["default"] = e;
21
+ return Object.freeze(n);
22
+ }
23
+
6
24
  var VAL_CONFIG_SESSION_STORAGE_KEY = "val-config";
7
25
  var VAL_THEME_SESSION_STORAGE_KEY = "val-theme";
8
26
  var VAL_AI_SESSION_STORAGE_KEY = "val-ai-session";
@@ -324,7 +342,7 @@ function isValCanvasPageMessage(data) {
324
342
  if (createForOfIteratorHelper._typeof(data) !== "object" || data === null) return false;
325
343
  var message = data;
326
344
  if (message.val !== VAL_CANVAS_MESSAGE) return false;
327
- return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
345
+ return message.type === "ready" || message.type === "elements" || message.type === "clicked" || message.type === "pinch" || message.type === "zoom" || message.type === "refreshing" || message.type === "elementPickingStopped" || message.type === "elementPicked" && isPickedElement(data.element) || message.type === "location" && isPageLocation(data) || message.type === "pageRead" && typeof data.requestId === "string" || message.type === "pageScreenshot" && typeof data.requestId === "string" || message.type === "quickPreviewStored" && typeof data.id === "string";
328
346
  }
329
347
  function isPageLocation(value) {
330
348
  var location = value;
@@ -381,6 +399,10 @@ function isValCanvasStudioMessage(data) {
381
399
  var preview = data;
382
400
  return typeof preview.id === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(preview.id) && isQuickPreviewFiles(preview.files);
383
401
  }
402
+ if (message.type === "screenshotPage") {
403
+ var asked = data;
404
+ return typeof asked.requestId === "string" && (asked.fullPage === undefined || typeof asked.fullPage === "boolean");
405
+ }
384
406
  if (message.type === "readPage") {
385
407
  var read = data;
386
408
  return typeof read.requestId === "string" && (read.selector === undefined || typeof read.selector === "string") && (read.maxItems === undefined || typeof read.maxItems === "number");
@@ -1139,6 +1161,137 @@ function selectorOf(element, root) {
1139
1161
  return parts.join(" > ");
1140
1162
  }
1141
1163
 
1164
+ /**
1165
+ * A picture of the page, taken in the page: the assistant's
1166
+ * `screenshot_page`, so it can see a change it made rather than only read it
1167
+ * (`read_page` gives the words and the boxes).
1168
+ *
1169
+ * Taken HERE because nothing else can: the design tab is another origin than
1170
+ * the proposal's page in its frame, and a browser lets no page photograph a
1171
+ * frame of another origin. So the bridge renders its own document --
1172
+ * `modern-screenshot` draws the DOM, with its computed styles, fonts and
1173
+ * same-origin images, into a canvas -- and hands back a JPEG.
1174
+ *
1175
+ * It is a rendering of the DOM, not of the screen: an image from another
1176
+ * origin that does not allow it, a video frame, a `<canvas>` a script drew
1177
+ * and an iframe come out blank. Good enough to see a layout, a colour, a
1178
+ * button that wrapped; the tool says what it cannot show.
1179
+ *
1180
+ * Loaded when asked: the library is not in a page that is never
1181
+ * photographed.
1182
+ */
1183
+
1184
+ /** What `screenshotPage` answers. */
1185
+
1186
+ /** Wider than this is scaled down: a model reads a page no better larger. */
1187
+ var MAX_WIDTH = 1280;
1188
+ /** A whole page longer than this, in CSS pixels, is cut off below it. */
1189
+ var MAX_FULL_PAGE_HEIGHT = 4000;
1190
+ var JPEG_QUALITY = 0.85;
1191
+ /** How long an image or a font on the page is waited for. */
1192
+ var LOAD_TIMEOUT_MS = 8000;
1193
+
1194
+ /**
1195
+ * The page in `win`, as it is now: what is on screen, or (`fullPage`) the
1196
+ * whole page from the top, cut off at {@link MAX_FULL_PAGE_HEIGHT}. Elements
1197
+ * `exclude` says are not the page's -- the picker's highlight -- are left
1198
+ * out.
1199
+ */
1200
+ function screenshotPage(_x) {
1201
+ return _screenshotPage.apply(this, arguments);
1202
+ }
1203
+
1204
+ /** What the page is drawn on: the body's background, else the root's, else white. */
1205
+ function _screenshotPage() {
1206
+ _screenshotPage = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee(win) {
1207
+ var _doc$body$scrollHeigh, _doc$body;
1208
+ var options,
1209
+ _yield$import,
1210
+ domToCanvas,
1211
+ doc,
1212
+ root,
1213
+ fullPage,
1214
+ pageWidth,
1215
+ pageHeight,
1216
+ top,
1217
+ height,
1218
+ exclude,
1219
+ drawn,
1220
+ scale,
1221
+ out,
1222
+ context,
1223
+ _args = arguments;
1224
+ return createForOfIteratorHelper._regenerator().w(function (_context) {
1225
+ while (1) switch (_context.n) {
1226
+ case 0:
1227
+ options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
1228
+ _context.n = 1;
1229
+ return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('modern-screenshot')); });
1230
+ case 1:
1231
+ _yield$import = _context.v;
1232
+ domToCanvas = _yield$import.domToCanvas;
1233
+ doc = win.document;
1234
+ root = doc.documentElement;
1235
+ fullPage = options.fullPage === true;
1236
+ pageWidth = root.clientWidth || win.innerWidth;
1237
+ pageHeight = Math.max(root.scrollHeight, (_doc$body$scrollHeigh = (_doc$body = doc.body) === null || _doc$body === void 0 ? void 0 : _doc$body.scrollHeight) !== null && _doc$body$scrollHeigh !== void 0 ? _doc$body$scrollHeigh : 0);
1238
+ top = fullPage ? 0 : win.scrollY;
1239
+ height = fullPage ? Math.min(pageHeight, MAX_FULL_PAGE_HEIGHT) : Math.min(win.innerHeight, pageHeight - top);
1240
+ exclude = options.exclude; // Drawn to the bottom of what is wanted, and no further: the cost is the
1241
+ // page's size, so a viewport near the top is cheap.
1242
+ _context.n = 2;
1243
+ return domToCanvas(root, createForOfIteratorHelper._objectSpread2({
1244
+ width: pageWidth,
1245
+ height: top + height,
1246
+ scale: 1,
1247
+ backgroundColor: pageBackground(win),
1248
+ timeout: LOAD_TIMEOUT_MS
1249
+ }, exclude !== undefined ? {
1250
+ filter: function filter(node) {
1251
+ return !(isElement(node) && exclude(node));
1252
+ }
1253
+ } : {}));
1254
+ case 2:
1255
+ drawn = _context.v;
1256
+ scale = Math.min(1, MAX_WIDTH / pageWidth);
1257
+ out = doc.createElement("canvas");
1258
+ out.width = Math.round(pageWidth * scale);
1259
+ out.height = Math.round(height * scale);
1260
+ context = out.getContext("2d");
1261
+ if (!(context === null)) {
1262
+ _context.n = 3;
1263
+ break;
1264
+ }
1265
+ throw new Error("This browser would not draw the picture.");
1266
+ case 3:
1267
+ context.drawImage(drawn, 0, top, pageWidth, height, 0, 0, out.width, out.height);
1268
+ return _context.a(2, {
1269
+ dataUrl: out.toDataURL("image/jpeg", JPEG_QUALITY),
1270
+ width: out.width,
1271
+ height: out.height,
1272
+ fullPage: fullPage,
1273
+ clipped: top + height < pageHeight
1274
+ });
1275
+ }
1276
+ }, _callee);
1277
+ }));
1278
+ return _screenshotPage.apply(this, arguments);
1279
+ }
1280
+ function pageBackground(win) {
1281
+ for (var _i = 0, _arr = [win.document.body, win.document.documentElement]; _i < _arr.length; _i++) {
1282
+ var element = _arr[_i];
1283
+ if (element === null) continue;
1284
+ var color = win.getComputedStyle(element).backgroundColor;
1285
+ if (color && color !== "transparent" && color !== "rgba(0, 0, 0, 0)") {
1286
+ return color;
1287
+ }
1288
+ }
1289
+ return "#ffffff";
1290
+ }
1291
+ function isElement(node) {
1292
+ return node.nodeType === 1;
1293
+ }
1294
+
1142
1295
  function parseRoutePattern(pattern) {
1143
1296
  if (pattern === "" || pattern === "/") {
1144
1297
  return [];
@@ -1414,6 +1567,7 @@ exports.parseRoutePattern = parseRoutePattern;
1414
1567
  exports.quickPreviewSiteOrigin = quickPreviewSiteOrigin;
1415
1568
  exports.readPageSnapshot = readPageSnapshot;
1416
1569
  exports.restoreScroll = restoreScroll;
1570
+ exports.screenshotPage = screenshotPage;
1417
1571
  exports.startElementPicking = startElementPicking;
1418
1572
  exports.storeQuickPreview = storeQuickPreview;
1419
1573
  exports.watchPageLocation = watchPageLocation;