humanish 0.96.0 → 0.96.1

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.
@@ -786,6 +786,8 @@ export declare function applyMobileEmulation(desktop: E2BDesktopSandbox, request
786
786
  targetId?: string;
787
787
  holderName: string;
788
788
  }>;
789
+ /** Root-relative physical client bounds from xwininfo's C-locale stats. */
790
+ export declare function parseXwininfoGeometry(output: string): RunDesktopGeometry["browserWindow"] | undefined;
789
791
  /** Shared hosted-browser geometry capture used by per-lane and sequential shared-world routes. */
790
792
  export declare function captureDesktopBrowserGeometry(args: {
791
793
  desktop: E2BDesktopSandbox;
@@ -1164,36 +1164,48 @@ function isMeasuredViewport(value) {
1164
1164
  function isPositiveMeasurement(value) {
1165
1165
  return typeof value === "number" && Number.isFinite(value) && value > 0;
1166
1166
  }
1167
- async function measureBrowserWindowWithXdotool(desktop, windowId, requestTimeoutMs) {
1167
+ async function measureBrowserWindowWithXwininfo(desktop, windowId, requestTimeoutMs) {
1168
1168
  const result = await desktop.commands.run([
1169
1169
  "set -euo pipefail",
1170
1170
  `win=${shellSingleQuote(windowId)}`,
1171
- "xdotool getwindowgeometry --shell \"$win\" 2>/dev/null || true"
1171
+ // Older xdotool builds translate parent-relative offsets twice. With window
1172
+ // decorations that falsely reports a visible client as clipped, triggering
1173
+ // fullscreen and hiding the participant's address bar. Read root-relative
1174
+ // client coordinates directly; never substitute emulated CDP outer bounds.
1175
+ "LC_ALL=C xwininfo -id \"$win\" -stats 2>/dev/null"
1172
1176
  ].join("\n"), { requestTimeoutMs, timeoutMs: 5_000 });
1173
- const output = result.stdout ?? "";
1174
- const read = (name) => {
1175
- const raw = output.match(new RegExp(`^${name}=(-?\\d+)$`, "m"))?.[1];
1176
- if (raw === undefined)
1177
+ if (result.exitCode !== undefined && result.exitCode !== 0)
1178
+ return undefined;
1179
+ return parseXwininfoGeometry(result.stdout ?? "");
1180
+ }
1181
+ /** Root-relative physical client bounds from xwininfo's C-locale stats. */
1182
+ export function parseXwininfoGeometry(output) {
1183
+ const read = (label) => {
1184
+ const matches = [...output.matchAll(new RegExp(`^\\s*${label}:\\s*(-?\\d+)\\s*$`, "gm"))];
1185
+ if (matches.length !== 1)
1177
1186
  return undefined;
1178
- const value = Number(raw);
1179
- return Number.isFinite(value) ? value : undefined;
1187
+ const value = Number(matches[0][1]);
1188
+ return Number.isSafeInteger(value) ? value : undefined;
1180
1189
  };
1181
- const x = read("X");
1182
- const y = read("Y");
1183
- const width = read("WIDTH");
1184
- const height = read("HEIGHT");
1190
+ const x = read("Absolute upper-left X");
1191
+ const y = read("Absolute upper-left Y");
1192
+ const width = read("Width");
1193
+ const height = read("Height");
1194
+ const mapStates = [...output.matchAll(/^\s*Map State:\s*(\S+)\s*$/gm)];
1195
+ if (mapStates.length !== 1 || mapStates[0][1] !== "IsViewable")
1196
+ return undefined;
1185
1197
  if (x === undefined || y === undefined || width === undefined || height === undefined || width <= 0 || height <= 0) {
1186
1198
  return undefined;
1187
1199
  }
1188
- return { x, y, width, height, source: "xdotool" };
1200
+ return { x, y, width, height, source: "xwininfo" };
1189
1201
  }
1190
1202
  /** Physical X client bounds, never the page's emulated window.outerWidth/Height. */
1191
1203
  function isBrowserWindowContained(bounds, [width, height]) {
1192
1204
  return bounds.x >= 0 && bounds.y >= 0
1193
1205
  && bounds.x + bounds.width <= width && bounds.y + bounds.height <= height;
1194
1206
  }
1195
- /** One bounded repair. Window-manager decorations may keep the client origin below (0, 0),
1196
- * so a full-screen client height can clip the bottom even after windowmove succeeds. */
1207
+ /** Bounded repair. Resizing can clear a window-manager maximize state and move the
1208
+ * client origin as decorations return, so remeasure before a second adjustment. */
1197
1209
  async function fitBrowserWindowWithinDesktop(desktop, windowId, resolution, requestTimeoutMs) {
1198
1210
  const run = (command) => desktop.commands.run([
1199
1211
  "set -euo pipefail",
@@ -1202,18 +1214,22 @@ async function fitBrowserWindowWithinDesktop(desktop, windowId, resolution, requ
1202
1214
  ].join("\n"), { requestTimeoutMs, timeoutMs: 5_000 }).catch(() => undefined);
1203
1215
  await run('xdotool windowmove "$win" 0 0');
1204
1216
  await desktop.wait(250).catch(() => undefined);
1205
- const moved = await measureBrowserWindowWithXdotool(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1217
+ const moved = await measureBrowserWindowWithXwininfo(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1206
1218
  if (moved === undefined)
1207
1219
  return moved;
1208
1220
  let resized = moved;
1209
- const width = resolution[0] - moved.x;
1210
- const height = resolution[1] - moved.y;
1211
1221
  // Resizing alone cannot fix an offscreen client origin. The window manager
1212
1222
  // can also center a minimum-width client at a negative x on a narrow screen.
1213
- if (moved.x >= 0 && moved.y >= 0 && width > 0 && height > 0) {
1223
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1224
+ if (isBrowserWindowContained(resized, resolution))
1225
+ return resized;
1226
+ const width = resolution[0] - resized.x;
1227
+ const height = resolution[1] - resized.y;
1228
+ if (resized.x < 0 || resized.y < 0 || width <= 0 || height <= 0)
1229
+ break;
1214
1230
  await run(`xdotool windowsize "$win" ${width} ${height}`);
1215
1231
  await desktop.wait(250).catch(() => undefined);
1216
- const measured = await measureBrowserWindowWithXdotool(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1232
+ const measured = await measureBrowserWindowWithXwininfo(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1217
1233
  if (measured === undefined)
1218
1234
  return measured;
1219
1235
  resized = measured;
@@ -1236,7 +1252,7 @@ async function fitBrowserWindowWithinDesktop(desktop, windowId, resolution, requ
1236
1252
  // Give the window manager a bounded settling window, keeping missing reads unverified.
1237
1253
  for (let attempt = 0; attempt < 4; attempt += 1) {
1238
1254
  await desktop.wait(250).catch(() => undefined);
1239
- const measured = await measureBrowserWindowWithXdotool(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1255
+ const measured = await measureBrowserWindowWithXwininfo(desktop, windowId, requestTimeoutMs).catch(() => undefined);
1240
1256
  if (measured === undefined || isBrowserWindowContained(measured, resolution))
1241
1257
  return measured;
1242
1258
  resized = measured;
@@ -1253,40 +1269,40 @@ export async function captureDesktopBrowserGeometry(args) {
1253
1269
  return undefined;
1254
1270
  });
1255
1271
  }
1256
- let xdotoolWindow;
1272
+ let physicalWindow;
1257
1273
  if (browserWindowId !== undefined) {
1258
1274
  if (args.resize !== false) {
1259
1275
  await fillDesktopBrowserWindow(args.desktop, browserWindowId, args.requestedScreen, args.requestTimeoutMs);
1260
1276
  // Let the window manager apply the resize before querying both X and page layout geometry.
1261
1277
  await args.desktop.wait(250).catch(() => undefined);
1262
1278
  }
1263
- xdotoolWindow = await measureBrowserWindowWithXdotool(args.desktop, browserWindowId, args.requestTimeoutMs)
1279
+ physicalWindow = await measureBrowserWindowWithXwininfo(args.desktop, browserWindowId, args.requestTimeoutMs)
1264
1280
  .catch(() => undefined);
1265
1281
  }
1266
1282
  else {
1267
1283
  warnings.push(`Browser window bounds could not be measured for lane ${args.laneId}; the live stream will use the full desktop.`);
1268
1284
  }
1269
1285
  let unusable;
1270
- if (xdotoolWindow !== undefined && !isBrowserWindowContained(xdotoolWindow, args.requestedScreen)) {
1271
- const before = xdotoolWindow;
1286
+ if (physicalWindow !== undefined && !isBrowserWindowContained(physicalWindow, args.requestedScreen)) {
1287
+ const before = physicalWindow;
1272
1288
  if (args.resize !== false && browserWindowId !== undefined) {
1273
- xdotoolWindow = await fitBrowserWindowWithinDesktop(args.desktop, browserWindowId, args.requestedScreen, args.requestTimeoutMs);
1274
- if (xdotoolWindow === undefined) {
1289
+ physicalWindow = await fitBrowserWindowWithinDesktop(args.desktop, browserWindowId, args.requestedScreen, args.requestTimeoutMs);
1290
+ if (physicalWindow === undefined) {
1275
1291
  // Keep the last measured bad state; a missing observation cannot prove a successful fix.
1276
- xdotoolWindow = before;
1292
+ physicalWindow = before;
1277
1293
  unusable = `Physical browser containment could not be verified after correction for lane ${args.laneId}; the last measured window was clipped.`;
1278
1294
  }
1279
- else if (isBrowserWindowContained(xdotoolWindow, args.requestedScreen)) {
1280
- warnings.push(`Browser window clipping corrected for lane ${args.laneId}; physical bounds are ${xdotoolWindow.width}x${xdotoolWindow.height} at (${xdotoolWindow.x}, ${xdotoolWindow.y}).`);
1295
+ else if (isBrowserWindowContained(physicalWindow, args.requestedScreen)) {
1296
+ warnings.push(`Browser window clipping corrected for lane ${args.laneId}; physical bounds are ${physicalWindow.width}x${physicalWindow.height} at (${physicalWindow.x}, ${physicalWindow.y}).`);
1281
1297
  }
1282
1298
  }
1283
- if (unusable === undefined && !isBrowserWindowContained(xdotoolWindow, args.requestedScreen)) {
1284
- unusable = `Browser window is outside the captured ${args.requestedScreen[0]}x${args.requestedScreen[1]} desktop for lane ${args.laneId}: physical bounds ${xdotoolWindow.width}x${xdotoolWindow.height} at (${xdotoolWindow.x}, ${xdotoolWindow.y}), right=${xdotoolWindow.x + xdotoolWindow.width}, bottom=${xdotoolWindow.y + xdotoolWindow.height}.`;
1299
+ if (unusable === undefined && !isBrowserWindowContained(physicalWindow, args.requestedScreen)) {
1300
+ unusable = `Browser window is outside the captured ${args.requestedScreen[0]}x${args.requestedScreen[1]} desktop for lane ${args.laneId}: physical bounds ${physicalWindow.width}x${physicalWindow.height} at (${physicalWindow.x}, ${physicalWindow.y}), right=${physicalWindow.x + physicalWindow.width}, bottom=${physicalWindow.y + physicalWindow.height}.`;
1285
1301
  }
1286
1302
  if (unusable !== undefined)
1287
1303
  warnings.push(unusable);
1288
1304
  }
1289
- if (xdotoolWindow === undefined) {
1305
+ if (physicalWindow === undefined) {
1290
1306
  warnings.push(`Physical browser containment is unverified for lane ${args.laneId}; X window bounds could not be measured. Page-reported outer dimensions can be emulated and do not prove physical visibility.`);
1291
1307
  }
1292
1308
  let cdpUnavailable;
@@ -1302,11 +1318,11 @@ export async function captureDesktopBrowserGeometry(args) {
1302
1318
  return undefined;
1303
1319
  })
1304
1320
  : undefined;
1305
- const browserWindow = xdotoolWindow ?? chromeGeometry?.browserWindow;
1321
+ const browserWindow = physicalWindow ?? chromeGeometry?.browserWindow;
1306
1322
  const viewport = chromeGeometry?.viewport;
1307
1323
  // The fill check reads the X window when it was measured: under mobile emulation (#221) the
1308
1324
  // page's window.outerWidth reports the EMULATED screen (414), which is not a fill failure.
1309
- const fillBounds = xdotoolWindow;
1325
+ const fillBounds = physicalWindow;
1310
1326
  if (!browserWindow) {
1311
1327
  warnings.push(`Browser outer bounds could not be measured for lane ${args.laneId}.`);
1312
1328
  }