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
|
@@ -1014,17 +1014,36 @@ function hasTransparentBackground(element) {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
function injectCaptureBackground(element, backgroundColor = DEFAULT_CAPTURE_BACKGROUND) {
|
|
1016
1016
|
const restoreFunctions = [];
|
|
1017
|
-
const originalBg = element.style.backgroundColor;
|
|
1018
1017
|
const wasTransparent = hasTransparentBackground(element);
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
element.style.backgroundColor =
|
|
1022
|
-
|
|
1018
|
+
if (wasTransparent) {
|
|
1019
|
+
const originalBg = element.style.backgroundColor;
|
|
1020
|
+
element.style.backgroundColor = backgroundColor;
|
|
1021
|
+
restoreFunctions.push(() => {
|
|
1022
|
+
element.style.backgroundColor = originalBg;
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
const shouldInjectDescendantBackground = (candidate) => {
|
|
1026
|
+
if (!hasTransparentBackground(candidate)) {
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
const style = window.getComputedStyle(candidate);
|
|
1030
|
+
if (style.display === "inline" || style.display === "contents") {
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
const rect = candidate.getBoundingClientRect();
|
|
1034
|
+
const hasMeaningfulBox = rect.width >= 32 && rect.height >= 32;
|
|
1035
|
+
if (!hasMeaningfulBox) {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
const hasNestedLayout = candidate.children.length > 0;
|
|
1039
|
+
const hasVisualContainerTraits = style.borderRadius !== "0px" || style.boxShadow !== "none" || style.backdropFilter !== "none" || style.overflow !== "visible" || style.borderStyle !== "none";
|
|
1040
|
+
return hasNestedLayout || hasVisualContainerTraits;
|
|
1041
|
+
};
|
|
1023
1042
|
const allDescendants = element.querySelectorAll("*");
|
|
1024
1043
|
let injectedCount = 0;
|
|
1025
1044
|
allDescendants.forEach((el) => {
|
|
1026
1045
|
const htmlEl = el;
|
|
1027
|
-
if (
|
|
1046
|
+
if (shouldInjectDescendantBackground(htmlEl)) {
|
|
1028
1047
|
const childOriginalBg = htmlEl.style.backgroundColor;
|
|
1029
1048
|
htmlEl.style.backgroundColor = backgroundColor;
|
|
1030
1049
|
injectedCount++;
|
|
@@ -1133,121 +1152,51 @@ async function captureWithSnapdom(snapdom, element, scale) {
|
|
|
1133
1152
|
return null;
|
|
1134
1153
|
}
|
|
1135
1154
|
}
|
|
1136
|
-
function
|
|
1137
|
-
const
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
const parentLeft = parseFloat(parentStyle.left) || 0;
|
|
1160
|
-
if (parentTop !== 0 || parentLeft !== 0) {
|
|
1161
|
-
console.log(
|
|
1162
|
-
`[WebSpatial] Found offset element: ${parent.tagName}.${parent.className?.split(" ")[0] || ""} top=${parentTop}px, left=${parentLeft}px`
|
|
1163
|
-
);
|
|
1164
|
-
offsetElements.push({
|
|
1165
|
-
element: parent,
|
|
1166
|
-
originalTop: parent.style.top,
|
|
1167
|
-
originalLeft: parent.style.left,
|
|
1168
|
-
topValue: parentTop,
|
|
1169
|
-
leftValue: parentLeft
|
|
1170
|
-
});
|
|
1155
|
+
function createVisibleCaptureClone(element) {
|
|
1156
|
+
const rect = element.getBoundingClientRect();
|
|
1157
|
+
const sandbox = document.createElement("div");
|
|
1158
|
+
sandbox.setAttribute("aria-hidden", "true");
|
|
1159
|
+
sandbox.style.position = "fixed";
|
|
1160
|
+
sandbox.style.left = "-10000px";
|
|
1161
|
+
sandbox.style.top = "0px";
|
|
1162
|
+
sandbox.style.pointerEvents = "none";
|
|
1163
|
+
sandbox.style.zIndex = "-1";
|
|
1164
|
+
sandbox.style.contain = "layout style paint";
|
|
1165
|
+
sandbox.style.opacity = "1";
|
|
1166
|
+
const clone = element.cloneNode(true);
|
|
1167
|
+
const makeCloneVisible = (node) => {
|
|
1168
|
+
node.style.visibility = "visible";
|
|
1169
|
+
node.style.opacity = "1";
|
|
1170
|
+
node.style.transition = "none";
|
|
1171
|
+
node.style.animation = "none";
|
|
1172
|
+
node.style.transform = "none";
|
|
1173
|
+
node.style.top = "0px";
|
|
1174
|
+
node.style.left = "0px";
|
|
1175
|
+
Array.from(node.children).forEach((child) => {
|
|
1176
|
+
if (child instanceof HTMLElement) {
|
|
1177
|
+
makeCloneVisible(child);
|
|
1171
1178
|
}
|
|
1172
|
-
}
|
|
1173
|
-
parent = parent.parentElement;
|
|
1174
|
-
depth++;
|
|
1175
|
-
}
|
|
1176
|
-
return offsetElements;
|
|
1177
|
-
}
|
|
1178
|
-
function getContentOffset(element) {
|
|
1179
|
-
const offsetElements = findOffsetElements(element);
|
|
1180
|
-
return {
|
|
1181
|
-
top: offsetElements.reduce((sum, el) => sum + el.topValue, 0),
|
|
1182
|
-
left: offsetElements.reduce((sum, el) => sum + el.leftValue, 0)
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
function makeElementVisible(element) {
|
|
1186
|
-
const restoreFunctions = [];
|
|
1187
|
-
const originalVisibility = element.style.visibility;
|
|
1188
|
-
const computedVisibility = window.getComputedStyle(element).visibility;
|
|
1189
|
-
if (computedVisibility === "hidden") {
|
|
1190
|
-
element.style.visibility = "visible";
|
|
1191
|
-
console.log(`[WebSpatial] Made element visible for capture (was: ${computedVisibility})`);
|
|
1192
|
-
restoreFunctions.push(() => {
|
|
1193
|
-
element.style.visibility = originalVisibility;
|
|
1194
1179
|
});
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
}
|
|
1207
|
-
});
|
|
1208
|
-
if (restoreFunctions.length > 1) {
|
|
1209
|
-
console.log(`[WebSpatial] Made ${restoreFunctions.length} elements visible for capture`);
|
|
1210
|
-
}
|
|
1211
|
-
return () => {
|
|
1212
|
-
restoreFunctions.forEach((restore) => restore());
|
|
1180
|
+
};
|
|
1181
|
+
makeCloneVisible(clone);
|
|
1182
|
+
clone.style.position = "relative";
|
|
1183
|
+
clone.style.margin = "0px";
|
|
1184
|
+
clone.style.width = `${Math.ceil(rect.width)}px`;
|
|
1185
|
+
clone.style.minHeight = `${Math.ceil(rect.height)}px`;
|
|
1186
|
+
sandbox.appendChild(clone);
|
|
1187
|
+
document.body.appendChild(sandbox);
|
|
1188
|
+
return {
|
|
1189
|
+
clone,
|
|
1190
|
+
cleanup: () => sandbox.remove()
|
|
1213
1191
|
};
|
|
1214
1192
|
}
|
|
1215
1193
|
async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundColor) {
|
|
1216
1194
|
try {
|
|
1217
1195
|
const rect = element.getBoundingClientRect();
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1196
|
+
console.log(
|
|
1197
|
+
`[WebSpatial] html2canvas capturing via visible clone: rect=(${rect.x.toFixed(0)},${rect.y.toFixed(0)},${rect.width.toFixed(0)},${rect.height.toFixed(0)})`
|
|
1198
|
+
);
|
|
1221
1199
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
|
1222
|
-
const absoluteX = rect.x + scrollX;
|
|
1223
|
-
const absoluteY = rect.y + scrollY;
|
|
1224
|
-
const offsetElements = findOffsetElements(element);
|
|
1225
|
-
if (offsetElements.length > 0) {
|
|
1226
|
-
console.log(
|
|
1227
|
-
`[WebSpatial] Temporarily resetting ${offsetElements.length} offset elements for capture`
|
|
1228
|
-
);
|
|
1229
|
-
offsetElements.forEach(({ element: el, topValue, leftValue }) => {
|
|
1230
|
-
el.style.top = "0px";
|
|
1231
|
-
el.style.left = "0px";
|
|
1232
|
-
console.log(
|
|
1233
|
-
`[WebSpatial] Reset: ${el.tagName}.${el.className?.split(" ")[0] || ""} from top=${topValue}px, left=${leftValue}px to 0`
|
|
1234
|
-
);
|
|
1235
|
-
});
|
|
1236
|
-
void element.offsetHeight;
|
|
1237
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1238
|
-
console.log("[WebSpatial] Reflow complete after offset reset");
|
|
1239
|
-
}
|
|
1240
|
-
const restorePositions = () => {
|
|
1241
|
-
offsetElements.forEach(({ element: el, originalTop, originalLeft }) => {
|
|
1242
|
-
el.style.top = originalTop;
|
|
1243
|
-
el.style.left = originalLeft;
|
|
1244
|
-
});
|
|
1245
|
-
};
|
|
1246
|
-
if (element.scrollTop !== 0) {
|
|
1247
|
-
console.log(`[WebSpatial] Resetting element scroll from ${element.scrollTop} to 0`);
|
|
1248
|
-
element.scrollTop = 0;
|
|
1249
|
-
}
|
|
1250
|
-
const restoreVisibility = makeElementVisible(element);
|
|
1251
1200
|
const viewportTop = scrollY;
|
|
1252
1201
|
const viewportBottom = scrollY + window.innerHeight;
|
|
1253
1202
|
const elementTop = rect.y + scrollY;
|
|
@@ -1256,14 +1205,20 @@ async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundCol
|
|
|
1256
1205
|
`[WebSpatial] Capture context: viewport=(${viewportTop}-${viewportBottom}), element=(${elementTop}-${elementBottom}), innerHeight=${window.innerHeight}`
|
|
1257
1206
|
);
|
|
1258
1207
|
let canvas;
|
|
1208
|
+
const captureClone = createVisibleCaptureClone(element);
|
|
1209
|
+
const restoreBackground = injectCaptureBackground(
|
|
1210
|
+
captureClone.clone,
|
|
1211
|
+
backgroundColor || DEFAULT_CAPTURE_BACKGROUND
|
|
1212
|
+
);
|
|
1259
1213
|
try {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1214
|
+
await new Promise(
|
|
1215
|
+
(resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))
|
|
1216
|
+
);
|
|
1217
|
+
const cloneRect = captureClone.clone.getBoundingClientRect();
|
|
1263
1218
|
console.log(
|
|
1264
|
-
`[WebSpatial]
|
|
1219
|
+
`[WebSpatial] Visible clone ready: rect=(${cloneRect.x.toFixed(0)},${cloneRect.y.toFixed(0)},${cloneRect.width.toFixed(0)},${cloneRect.height.toFixed(0)})`
|
|
1265
1220
|
);
|
|
1266
|
-
canvas = await html2canvas(
|
|
1221
|
+
canvas = await html2canvas(captureClone.clone, {
|
|
1267
1222
|
backgroundColor,
|
|
1268
1223
|
logging: true,
|
|
1269
1224
|
// Enable logging to debug
|
|
@@ -1272,87 +1227,11 @@ async function captureWithHtml2Canvas(html2canvas, element, scale, backgroundCol
|
|
|
1272
1227
|
allowTaint: true,
|
|
1273
1228
|
imageTimeout: 5e3,
|
|
1274
1229
|
removeContainer: true,
|
|
1275
|
-
foreignObjectRendering: false
|
|
1276
|
-
// Don't set scroll offset - let html2canvas use default (current scroll)
|
|
1277
|
-
// scrollX and scrollY would affect rendering position but we want document coordinates
|
|
1278
|
-
// Crop to element's document position
|
|
1279
|
-
// x/y define the top-left corner of the crop region in document coordinates
|
|
1280
|
-
x: captureX,
|
|
1281
|
-
y: captureY,
|
|
1282
|
-
width: currentRect.width,
|
|
1283
|
-
height: currentRect.height,
|
|
1284
|
-
// Use onclone to MOVE the element to (0,0) in the cloned document
|
|
1285
|
-
// This is the key fix for elements far down the page (like footer at Y=1142)
|
|
1286
|
-
// By moving to absolute position (0,0), html2canvas will render at the top-left
|
|
1287
|
-
onclone: (clonedDoc, clonedElement) => {
|
|
1288
|
-
const originalRect = clonedElement.getBoundingClientRect();
|
|
1289
|
-
console.log(`[WebSpatial] CLONE: Element position in clone (${originalRect.x.toFixed(0)},${originalRect.y.toFixed(0)})`);
|
|
1290
|
-
clonedElement.style.visibility = "visible";
|
|
1291
|
-
const hiddenElements = clonedElement.querySelectorAll("*");
|
|
1292
|
-
let visibilityFixCount = 0;
|
|
1293
|
-
hiddenElements.forEach((el) => {
|
|
1294
|
-
const htmlEl = el;
|
|
1295
|
-
const style = clonedDoc.defaultView?.getComputedStyle(htmlEl);
|
|
1296
|
-
if (style && style.visibility === "hidden") {
|
|
1297
|
-
htmlEl.style.visibility = "visible";
|
|
1298
|
-
visibilityFixCount++;
|
|
1299
|
-
}
|
|
1300
|
-
});
|
|
1301
|
-
if (visibilityFixCount > 0) {
|
|
1302
|
-
console.log(`[WebSpatial] CLONE: Made ${visibilityFixCount + 1} elements visible`);
|
|
1303
|
-
}
|
|
1304
|
-
let parent = clonedElement.parentElement;
|
|
1305
|
-
let depth = 0;
|
|
1306
|
-
const maxDepth = 10;
|
|
1307
|
-
while (parent && depth < maxDepth) {
|
|
1308
|
-
const parentStyle = clonedDoc.defaultView?.getComputedStyle(parent);
|
|
1309
|
-
if (parentStyle && parentStyle.position === "relative") {
|
|
1310
|
-
const parentTop = parseFloat(parentStyle.top) || 0;
|
|
1311
|
-
const parentLeft = parseFloat(parentStyle.left) || 0;
|
|
1312
|
-
if (parentTop !== 0 || parentLeft !== 0) {
|
|
1313
|
-
console.log(
|
|
1314
|
-
`[WebSpatial] CLONE: Reset ${parent.tagName}.${parent.className?.split(" ")[0] || ""} from top=${parentTop}px, left=${parentLeft}px to 0`
|
|
1315
|
-
);
|
|
1316
|
-
parent.style.top = "0px";
|
|
1317
|
-
parent.style.left = "0px";
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
parent = parent.parentElement;
|
|
1321
|
-
depth++;
|
|
1322
|
-
}
|
|
1323
|
-
void clonedElement.offsetHeight;
|
|
1324
|
-
const clonedRect = clonedElement.getBoundingClientRect();
|
|
1325
|
-
const clonedStyle = clonedDoc.defaultView?.getComputedStyle(clonedElement);
|
|
1326
|
-
console.log(
|
|
1327
|
-
`[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}`
|
|
1328
|
-
);
|
|
1329
|
-
const numChildren = Math.min(5, clonedElement.children.length);
|
|
1330
|
-
for (let i = 0; i < numChildren; i++) {
|
|
1331
|
-
const child = clonedElement.children[i];
|
|
1332
|
-
const childRect = child.getBoundingClientRect();
|
|
1333
|
-
const childStyle = clonedDoc.defaultView?.getComputedStyle(child);
|
|
1334
|
-
console.log(
|
|
1335
|
-
`[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}`
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
const productGrid = clonedElement.querySelector(".auto-fill-grid");
|
|
1339
|
-
if (productGrid) {
|
|
1340
|
-
const gridRect = productGrid.getBoundingClientRect();
|
|
1341
|
-
const gridStyle = clonedDoc.defaultView?.getComputedStyle(productGrid);
|
|
1342
|
-
console.log(
|
|
1343
|
-
`[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)}`
|
|
1344
|
-
);
|
|
1345
|
-
const firstCard = productGrid.children[0];
|
|
1346
|
-
if (firstCard) {
|
|
1347
|
-
const cardRect = firstCard.getBoundingClientRect();
|
|
1348
|
-
console.log(`[WebSpatial] First product card: rect=(${cardRect.x.toFixed(0)},${cardRect.y.toFixed(0)},${cardRect.width.toFixed(0)},${cardRect.height.toFixed(0)})`);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1230
|
+
foreignObjectRendering: false
|
|
1352
1231
|
});
|
|
1353
1232
|
} finally {
|
|
1354
|
-
|
|
1355
|
-
|
|
1233
|
+
restoreBackground();
|
|
1234
|
+
captureClone.cleanup();
|
|
1356
1235
|
}
|
|
1357
1236
|
const ctx = canvas.getContext("2d");
|
|
1358
1237
|
if (ctx) {
|
|
@@ -1415,42 +1294,34 @@ async function captureElementBitmap(element, options) {
|
|
|
1415
1294
|
if (options?.waitForImages !== false) {
|
|
1416
1295
|
await waitForContent(element, 500);
|
|
1417
1296
|
}
|
|
1418
|
-
const restoreBackground = injectCaptureBackground(
|
|
1419
|
-
element,
|
|
1420
|
-
options?.backgroundColor || DEFAULT_CAPTURE_BACKGROUND
|
|
1421
|
-
);
|
|
1422
1297
|
let result = null;
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
return result;
|
|
1437
|
-
}
|
|
1298
|
+
const html2canvas = await loadHtml2Canvas();
|
|
1299
|
+
if (html2canvas) {
|
|
1300
|
+
console.log("[WebSpatial] Using html2canvas (primary)");
|
|
1301
|
+
result = await captureWithHtml2Canvas(
|
|
1302
|
+
html2canvas,
|
|
1303
|
+
element,
|
|
1304
|
+
scale,
|
|
1305
|
+
options?.backgroundColor ?? DEFAULT_CAPTURE_BACKGROUND
|
|
1306
|
+
);
|
|
1307
|
+
if (result) {
|
|
1308
|
+
const elapsed = Math.round(performance.now() - startTime);
|
|
1309
|
+
console.log(`[WebSpatial] Capture complete (html2canvas, ${elapsed}ms)`);
|
|
1310
|
+
return result;
|
|
1438
1311
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1312
|
+
}
|
|
1313
|
+
const snapdom = await loadSnapdom();
|
|
1314
|
+
if (snapdom) {
|
|
1315
|
+
console.log("[WebSpatial] Falling back to snapdom");
|
|
1316
|
+
result = await captureWithSnapdom(snapdom, element, scale);
|
|
1317
|
+
if (result) {
|
|
1318
|
+
const elapsed = Math.round(performance.now() - startTime);
|
|
1319
|
+
console.log(`[WebSpatial] Capture complete (snapdom, ${elapsed}ms)`);
|
|
1320
|
+
return result;
|
|
1448
1321
|
}
|
|
1449
|
-
console.error("[WebSpatial] No capture library available");
|
|
1450
|
-
return null;
|
|
1451
|
-
} finally {
|
|
1452
|
-
restoreBackground();
|
|
1453
1322
|
}
|
|
1323
|
+
console.error("[WebSpatial] No capture library available");
|
|
1324
|
+
return null;
|
|
1454
1325
|
}
|
|
1455
1326
|
function observeContentChanges(element, onContentChange) {
|
|
1456
1327
|
const observer = new MutationObserver((_mutations) => {
|