oomi-ai 0.2.40 → 0.2.42
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.
- package/lib/personaRuntimeManager.js +57 -38
- package/lib/personaRuntimeProcess.js +145 -26
- package/lib/personaRuntimeSupervisor.js +76 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/templates/persona-app/src/App.css +4 -8
- package/templates/persona-app/src/pages/HomePage.tsx +9 -9
- package/templates/persona-app/src/spatial.ts +1 -1
- package/templates/persona-app/vendor/webspatial/FORK.md +1 -1
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js +100 -229
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/default/index.js.map +1 -1
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js +100 -229
- package/templates/persona-app/vendor/webspatial/react-sdk/dist/web/index.js.map +1 -1
|
@@ -1055,17 +1055,36 @@ function hasTransparentBackground(element) {
|
|
|
1055
1055
|
}
|
|
1056
1056
|
function injectCaptureBackground(element, backgroundColor = DEFAULT_CAPTURE_BACKGROUND) {
|
|
1057
1057
|
const restoreFunctions = [];
|
|
1058
|
-
const originalBg = element.style.backgroundColor;
|
|
1059
1058
|
const wasTransparent = hasTransparentBackground(element);
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
element.style.backgroundColor =
|
|
1063
|
-
|
|
1059
|
+
if (wasTransparent) {
|
|
1060
|
+
const originalBg = element.style.backgroundColor;
|
|
1061
|
+
element.style.backgroundColor = backgroundColor;
|
|
1062
|
+
restoreFunctions.push(() => {
|
|
1063
|
+
element.style.backgroundColor = originalBg;
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
const shouldInjectDescendantBackground = (candidate) => {
|
|
1067
|
+
if (!hasTransparentBackground(candidate)) {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
const style = window.getComputedStyle(candidate);
|
|
1071
|
+
if (style.display === "inline" || style.display === "contents") {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
const rect = candidate.getBoundingClientRect();
|
|
1075
|
+
const hasMeaningfulBox = rect.width >= 32 && rect.height >= 32;
|
|
1076
|
+
if (!hasMeaningfulBox) {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
const hasNestedLayout = candidate.children.length > 0;
|
|
1080
|
+
const hasVisualContainerTraits = style.borderRadius !== "0px" || style.boxShadow !== "none" || style.backdropFilter !== "none" || style.overflow !== "visible" || style.borderStyle !== "none";
|
|
1081
|
+
return hasNestedLayout || hasVisualContainerTraits;
|
|
1082
|
+
};
|
|
1064
1083
|
const allDescendants = element.querySelectorAll("*");
|
|
1065
1084
|
let injectedCount = 0;
|
|
1066
1085
|
allDescendants.forEach((el) => {
|
|
1067
1086
|
const htmlEl = el;
|
|
1068
|
-
if (
|
|
1087
|
+
if (shouldInjectDescendantBackground(htmlEl)) {
|
|
1069
1088
|
const childOriginalBg = htmlEl.style.backgroundColor;
|
|
1070
1089
|
htmlEl.style.backgroundColor = backgroundColor;
|
|
1071
1090
|
injectedCount++;
|
|
@@ -1174,121 +1193,51 @@ async function captureWithSnapdom(snapdom, element, scale) {
|
|
|
1174
1193
|
return null;
|
|
1175
1194
|
}
|
|
1176
1195
|
}
|
|
1177
|
-
function
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
const parentLeft = parseFloat(parentStyle.left) || 0;
|
|
1201
|
-
if (parentTop !== 0 || parentLeft !== 0) {
|
|
1202
|
-
console.log(
|
|
1203
|
-
`[WebSpatial] Found offset element: ${parent.tagName}.${parent.className?.split(" ")[0] || ""} top=${parentTop}px, left=${parentLeft}px`
|
|
1204
|
-
);
|
|
1205
|
-
offsetElements.push({
|
|
1206
|
-
element: parent,
|
|
1207
|
-
originalTop: parent.style.top,
|
|
1208
|
-
originalLeft: parent.style.left,
|
|
1209
|
-
topValue: parentTop,
|
|
1210
|
-
leftValue: parentLeft
|
|
1211
|
-
});
|
|
1196
|
+
function createVisibleCaptureClone(element) {
|
|
1197
|
+
const rect = element.getBoundingClientRect();
|
|
1198
|
+
const sandbox = document.createElement("div");
|
|
1199
|
+
sandbox.setAttribute("aria-hidden", "true");
|
|
1200
|
+
sandbox.style.position = "fixed";
|
|
1201
|
+
sandbox.style.left = "-10000px";
|
|
1202
|
+
sandbox.style.top = "0px";
|
|
1203
|
+
sandbox.style.pointerEvents = "none";
|
|
1204
|
+
sandbox.style.zIndex = "-1";
|
|
1205
|
+
sandbox.style.contain = "layout style paint";
|
|
1206
|
+
sandbox.style.opacity = "1";
|
|
1207
|
+
const clone = element.cloneNode(true);
|
|
1208
|
+
const makeCloneVisible = (node) => {
|
|
1209
|
+
node.style.visibility = "visible";
|
|
1210
|
+
node.style.opacity = "1";
|
|
1211
|
+
node.style.transition = "none";
|
|
1212
|
+
node.style.animation = "none";
|
|
1213
|
+
node.style.transform = "none";
|
|
1214
|
+
node.style.top = "0px";
|
|
1215
|
+
node.style.left = "0px";
|
|
1216
|
+
Array.from(node.children).forEach((child) => {
|
|
1217
|
+
if (child instanceof HTMLElement) {
|
|
1218
|
+
makeCloneVisible(child);
|
|
1212
1219
|
}
|
|
1213
|
-
}
|
|
1214
|
-
parent = parent.parentElement;
|
|
1215
|
-
depth++;
|
|
1216
|
-
}
|
|
1217
|
-
return offsetElements;
|
|
1218
|
-
}
|
|
1219
|
-
function getContentOffset(element) {
|
|
1220
|
-
const offsetElements = findOffsetElements(element);
|
|
1221
|
-
return {
|
|
1222
|
-
top: offsetElements.reduce((sum, el) => sum + el.topValue, 0),
|
|
1223
|
-
left: offsetElements.reduce((sum, el) => sum + el.leftValue, 0)
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
function makeElementVisible(element) {
|
|
1227
|
-
const restoreFunctions = [];
|
|
1228
|
-
const originalVisibility = element.style.visibility;
|
|
1229
|
-
const computedVisibility = window.getComputedStyle(element).visibility;
|
|
1230
|
-
if (computedVisibility === "hidden") {
|
|
1231
|
-
element.style.visibility = "visible";
|
|
1232
|
-
console.log(`[WebSpatial] Made element visible for capture (was: ${computedVisibility})`);
|
|
1233
|
-
restoreFunctions.push(() => {
|
|
1234
|
-
element.style.visibility = originalVisibility;
|
|
1235
1220
|
});
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1249
|
-
if (restoreFunctions.length > 1) {
|
|
1250
|
-
console.log(`[WebSpatial] Made ${restoreFunctions.length} elements visible for capture`);
|
|
1251
|
-
}
|
|
1252
|
-
return () => {
|
|
1253
|
-
restoreFunctions.forEach((restore) => restore());
|
|
1221
|
+
};
|
|
1222
|
+
makeCloneVisible(clone);
|
|
1223
|
+
clone.style.position = "relative";
|
|
1224
|
+
clone.style.margin = "0px";
|
|
1225
|
+
clone.style.width = `${Math.ceil(rect.width)}px`;
|
|
1226
|
+
clone.style.minHeight = `${Math.ceil(rect.height)}px`;
|
|
1227
|
+
sandbox.appendChild(clone);
|
|
1228
|
+
document.body.appendChild(sandbox);
|
|
1229
|
+
return {
|
|
1230
|
+
clone,
|
|
1231
|
+
cleanup: () => sandbox.remove()
|
|
1254
1232
|
};
|
|
1255
1233
|
}
|
|
1256
1234
|
async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundColor) {
|
|
1257
1235
|
try {
|
|
1258
1236
|
const rect = element.getBoundingClientRect();
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1237
|
+
console.log(
|
|
1238
|
+
`[WebSpatial] html2canvas capturing via visible clone: rect=(${rect.x.toFixed(0)},${rect.y.toFixed(0)},${rect.width.toFixed(0)},${rect.height.toFixed(0)})`
|
|
1239
|
+
);
|
|
1262
1240
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
1263
|
-
const absoluteX = rect.x + scrollX;
|
|
1264
|
-
const absoluteY = rect.y + scrollY;
|
|
1265
|
-
const offsetElements = findOffsetElements(element);
|
|
1266
|
-
if (offsetElements.length > 0) {
|
|
1267
|
-
console.log(
|
|
1268
|
-
`[WebSpatial] Temporarily resetting ${offsetElements.length} offset elements for capture`
|
|
1269
|
-
);
|
|
1270
|
-
offsetElements.forEach(({ element: el, topValue, leftValue }) => {
|
|
1271
|
-
el.style.top = "0px";
|
|
1272
|
-
el.style.left = "0px";
|
|
1273
|
-
console.log(
|
|
1274
|
-
`[WebSpatial] Reset: ${el.tagName}.${el.className?.split(" ")[0] || ""} from top=${topValue}px, left=${leftValue}px to 0`
|
|
1275
|
-
);
|
|
1276
|
-
});
|
|
1277
|
-
void element.offsetHeight;
|
|
1278
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1279
|
-
console.log("[WebSpatial] Reflow complete after offset reset");
|
|
1280
|
-
}
|
|
1281
|
-
const restorePositions = () => {
|
|
1282
|
-
offsetElements.forEach(({ element: el, originalTop, originalLeft }) => {
|
|
1283
|
-
el.style.top = originalTop;
|
|
1284
|
-
el.style.left = originalLeft;
|
|
1285
|
-
});
|
|
1286
|
-
};
|
|
1287
|
-
if (element.scrollTop !== 0) {
|
|
1288
|
-
console.log(`[WebSpatial] Resetting element scroll from ${element.scrollTop} to 0`);
|
|
1289
|
-
element.scrollTop = 0;
|
|
1290
|
-
}
|
|
1291
|
-
const restoreVisibility = makeElementVisible(element);
|
|
1292
1241
|
const viewportTop = scrollY;
|
|
1293
1242
|
const viewportBottom = scrollY + window.innerHeight;
|
|
1294
1243
|
const elementTop = rect.y + scrollY;
|
|
@@ -1297,14 +1246,20 @@ async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundCol
|
|
|
1297
1246
|
`[WebSpatial] Capture context: viewport=(${viewportTop}-${viewportBottom}), element=(${elementTop}-${elementBottom}), innerHeight=${window.innerHeight}`
|
|
1298
1247
|
);
|
|
1299
1248
|
let canvas;
|
|
1249
|
+
const captureClone = createVisibleCaptureClone(element);
|
|
1250
|
+
const restoreBackground = injectCaptureBackground(
|
|
1251
|
+
captureClone.clone,
|
|
1252
|
+
backgroundColor || DEFAULT_CAPTURE_BACKGROUND
|
|
1253
|
+
);
|
|
1300
1254
|
try {
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1255
|
+
await new Promise(
|
|
1256
|
+
(resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))
|
|
1257
|
+
);
|
|
1258
|
+
const cloneRect = captureClone.clone.getBoundingClientRect();
|
|
1304
1259
|
console.log(
|
|
1305
|
-
`[WebSpatial]
|
|
1260
|
+
`[WebSpatial] Visible clone ready: rect=(${cloneRect.x.toFixed(0)},${cloneRect.y.toFixed(0)},${cloneRect.width.toFixed(0)},${cloneRect.height.toFixed(0)})`
|
|
1306
1261
|
);
|
|
1307
|
-
canvas = await html2canvas(
|
|
1262
|
+
canvas = await html2canvas(captureClone.clone, {
|
|
1308
1263
|
backgroundColor,
|
|
1309
1264
|
logging: true,
|
|
1310
1265
|
// Enable logging to debug
|
|
@@ -1313,87 +1268,11 @@ async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundCol
|
|
|
1313
1268
|
allowTaint: true,
|
|
1314
1269
|
imageTimeout: 5e3,
|
|
1315
1270
|
removeContainer: true,
|
|
1316
|
-
foreignObjectRendering: false
|
|
1317
|
-
// Don't set scroll offset - let html2canvas use default (current scroll)
|
|
1318
|
-
// scrollX and scrollY would affect rendering position but we want document coordinates
|
|
1319
|
-
// Crop to element's document position
|
|
1320
|
-
// x/y define the top-left corner of the crop region in document coordinates
|
|
1321
|
-
x: captureX,
|
|
1322
|
-
y: captureY,
|
|
1323
|
-
width: currentRect.width,
|
|
1324
|
-
height: currentRect.height,
|
|
1325
|
-
// Use onclone to MOVE the element to (0,0) in the cloned document
|
|
1326
|
-
// This is the key fix for elements far down the page (like footer at Y=1142)
|
|
1327
|
-
// By moving to absolute position (0,0), html2canvas will render at the top-left
|
|
1328
|
-
onclone: (clonedDoc, clonedElement) => {
|
|
1329
|
-
const originalRect = clonedElement.getBoundingClientRect();
|
|
1330
|
-
console.log(`[WebSpatial] CLONE: Element position in clone (${originalRect.x.toFixed(0)},${originalRect.y.toFixed(0)})`);
|
|
1331
|
-
clonedElement.style.visibility = "visible";
|
|
1332
|
-
const hiddenElements = clonedElement.querySelectorAll("*");
|
|
1333
|
-
let visibilityFixCount = 0;
|
|
1334
|
-
hiddenElements.forEach((el) => {
|
|
1335
|
-
const htmlEl = el;
|
|
1336
|
-
const style = clonedDoc.defaultView?.getComputedStyle(htmlEl);
|
|
1337
|
-
if (style && style.visibility === "hidden") {
|
|
1338
|
-
htmlEl.style.visibility = "visible";
|
|
1339
|
-
visibilityFixCount++;
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
if (visibilityFixCount > 0) {
|
|
1343
|
-
console.log(`[WebSpatial] CLONE: Made ${visibilityFixCount + 1} elements visible`);
|
|
1344
|
-
}
|
|
1345
|
-
let parent = clonedElement.parentElement;
|
|
1346
|
-
let depth = 0;
|
|
1347
|
-
const maxDepth = 10;
|
|
1348
|
-
while (parent && depth < maxDepth) {
|
|
1349
|
-
const parentStyle = clonedDoc.defaultView?.getComputedStyle(parent);
|
|
1350
|
-
if (parentStyle && parentStyle.position === "relative") {
|
|
1351
|
-
const parentTop = parseFloat(parentStyle.top) || 0;
|
|
1352
|
-
const parentLeft = parseFloat(parentStyle.left) || 0;
|
|
1353
|
-
if (parentTop !== 0 || parentLeft !== 0) {
|
|
1354
|
-
console.log(
|
|
1355
|
-
`[WebSpatial] CLONE: Reset ${parent.tagName}.${parent.className?.split(" ")[0] || ""} from top=${parentTop}px, left=${parentLeft}px to 0`
|
|
1356
|
-
);
|
|
1357
|
-
parent.style.top = "0px";
|
|
1358
|
-
parent.style.left = "0px";
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
parent = parent.parentElement;
|
|
1362
|
-
depth++;
|
|
1363
|
-
}
|
|
1364
|
-
void clonedElement.offsetHeight;
|
|
1365
|
-
const clonedRect = clonedElement.getBoundingClientRect();
|
|
1366
|
-
const clonedStyle = clonedDoc.defaultView?.getComputedStyle(clonedElement);
|
|
1367
|
-
console.log(
|
|
1368
|
-
`[WebSpatial] Cloned element (after visibility fix): rect=(${clonedRect.x.toFixed(0)},${clonedRect.y.toFixed(0)},${clonedRect.width.toFixed(0)},${clonedRect.height.toFixed(0)}), visibility=${clonedStyle?.visibility}`
|
|
1369
|
-
);
|
|
1370
|
-
const numChildren = Math.min(5, clonedElement.children.length);
|
|
1371
|
-
for (let i = 0; i < numChildren; i++) {
|
|
1372
|
-
const child = clonedElement.children[i];
|
|
1373
|
-
const childRect = child.getBoundingClientRect();
|
|
1374
|
-
const childStyle = clonedDoc.defaultView?.getComputedStyle(child);
|
|
1375
|
-
console.log(
|
|
1376
|
-
`[WebSpatial] Child ${i}: ${child.tagName}.${child.className?.split(" ")[0] || ""}, rect=(${childRect.x.toFixed(0)},${childRect.y.toFixed(0)},${childRect.width.toFixed(0)},${childRect.height.toFixed(0)}), vis=${childStyle?.visibility}, display=${childStyle?.display}`
|
|
1377
|
-
);
|
|
1378
|
-
}
|
|
1379
|
-
const productGrid = clonedElement.querySelector(".auto-fill-grid");
|
|
1380
|
-
if (productGrid) {
|
|
1381
|
-
const gridRect = productGrid.getBoundingClientRect();
|
|
1382
|
-
const gridStyle = clonedDoc.defaultView?.getComputedStyle(productGrid);
|
|
1383
|
-
console.log(
|
|
1384
|
-
`[WebSpatial] Product grid: rect=(${gridRect.x.toFixed(0)},${gridRect.y.toFixed(0)},${gridRect.width.toFixed(0)},${gridRect.height.toFixed(0)}), columns=${gridStyle?.gridTemplateColumns?.substring(0, 100)}`
|
|
1385
|
-
);
|
|
1386
|
-
const firstCard = productGrid.children[0];
|
|
1387
|
-
if (firstCard) {
|
|
1388
|
-
const cardRect = firstCard.getBoundingClientRect();
|
|
1389
|
-
console.log(`[WebSpatial] First product card: rect=(${cardRect.x.toFixed(0)},${cardRect.y.toFixed(0)},${cardRect.width.toFixed(0)},${cardRect.height.toFixed(0)})`);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1271
|
+
foreignObjectRendering: false
|
|
1393
1272
|
});
|
|
1394
1273
|
} finally {
|
|
1395
|
-
|
|
1396
|
-
|
|
1274
|
+
restoreBackground();
|
|
1275
|
+
captureClone.cleanup();
|
|
1397
1276
|
}
|
|
1398
1277
|
const ctx = canvas.getContext("2d");
|
|
1399
1278
|
if (ctx) {
|
|
@@ -1456,42 +1335,34 @@ async function captureElementBitmap(element, options) {
|
|
|
1456
1335
|
if (options?.waitForImages !== false) {
|
|
1457
1336
|
await waitForContent(element, 500);
|
|
1458
1337
|
}
|
|
1459
|
-
const restoreBackground = injectCaptureBackground(
|
|
1460
|
-
element,
|
|
1461
|
-
options?.backgroundColor || DEFAULT_CAPTURE_BACKGROUND
|
|
1462
|
-
);
|
|
1463
1338
|
let result = null;
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
return result;
|
|
1478
|
-
}
|
|
1339
|
+
const html2canvas = await loadHtml2Canvas();
|
|
1340
|
+
if (html2canvas) {
|
|
1341
|
+
console.log("[WebSpatial] Using html2canvas (primary)");
|
|
1342
|
+
result = await captureWithHtml2Canvas(
|
|
1343
|
+
html2canvas,
|
|
1344
|
+
element,
|
|
1345
|
+
scale,
|
|
1346
|
+
options?.backgroundColor ?? DEFAULT_CAPTURE_BACKGROUND
|
|
1347
|
+
);
|
|
1348
|
+
if (result) {
|
|
1349
|
+
const elapsed = Math.round(performance.now() - startTime);
|
|
1350
|
+
console.log(`[WebSpatial] Capture complete (html2canvas, ${elapsed}ms)`);
|
|
1351
|
+
return result;
|
|
1479
1352
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1353
|
+
}
|
|
1354
|
+
const snapdom = await loadSnapdom();
|
|
1355
|
+
if (snapdom) {
|
|
1356
|
+
console.log("[WebSpatial] Falling back to snapdom");
|
|
1357
|
+
result = await captureWithSnapdom(snapdom, element, scale);
|
|
1358
|
+
if (result) {
|
|
1359
|
+
const elapsed = Math.round(performance.now() - startTime);
|
|
1360
|
+
console.log(`[WebSpatial] Capture complete (snapdom, ${elapsed}ms)`);
|
|
1361
|
+
return result;
|
|
1489
1362
|
}
|
|
1490
|
-
console.error("[WebSpatial] No capture library available");
|
|
1491
|
-
return null;
|
|
1492
|
-
} finally {
|
|
1493
|
-
restoreBackground();
|
|
1494
1363
|
}
|
|
1364
|
+
console.error("[WebSpatial] No capture library available");
|
|
1365
|
+
return null;
|
|
1495
1366
|
}
|
|
1496
1367
|
function observeContentChanges(element, onContentChange) {
|
|
1497
1368
|
const observer = new MutationObserver((_mutations) => {
|