@glittering0717/ai-browser 2.1.18 → 2.1.19
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/README.md +9 -3
- package/package.json +3 -2
- package/src/main/page_manager.js +30 -4
- package/src/shared/version.js +1 -1
package/README.md
CHANGED
|
@@ -724,7 +724,7 @@ not bugs. The full write-up, including why the first measurement of this was wro
|
|
|
724
724
|
## Test & dev
|
|
725
725
|
|
|
726
726
|
```bash
|
|
727
|
-
npm test # vitest —
|
|
727
|
+
npm test # vitest — 970 tests across 69 files
|
|
728
728
|
npm run smoke # Electron contract layer, offline fixture — 31 checks
|
|
729
729
|
npm run capabilities # text fields, canvas, page source, network, DOM editing — 54 checks
|
|
730
730
|
npm run richtext # rich-text editors, nested menus, forms, errors, tags, upload — 117 checks
|
|
@@ -736,8 +736,14 @@ npm run audit # every advertised action must exist; wait/downloads/permis
|
|
|
736
736
|
npm run nav # address bar + load-failure error page (the "white screen" report) — 19 checks
|
|
737
737
|
npm run focus # no focus stealing + full capability while backgrounded — 32 checks
|
|
738
738
|
npm run interact # trusted drag / key press / wheel (pointer gestures) — 31 checks
|
|
739
|
-
npm run view # viewport repaint + snapshot/diff verification —
|
|
740
|
-
npm run
|
|
739
|
+
npm run view # viewport repaint budget + snapshot/diff verification — 29 checks
|
|
740
|
+
npm run dialog # browser-level dialogs (native sheet blocks the renderer) — 15 checks
|
|
741
|
+
npm run wrapper # "in the tree but won't click" = matched a container, not a link — 13 checks
|
|
742
|
+
npm run paint # blank-page self-check: verdicts, bounded ladder, no false alarms — 23 checks
|
|
743
|
+
npm run layout # geometry invariants + "no twitching" (zero extra repaints on nav) — 25 checks
|
|
744
|
+
npm run flicker # frame-level: no frame may render the page shrunk (needs screen recording) — 6 checks
|
|
745
|
+
npm run e2e:all # 一次跑完上面全部 19 个套件(每个独立端口)+ 可信汇总 — 636 checks
|
|
746
|
+
npm run verify:app # boot the packaged /Applications app and assert on it — 22 checks
|
|
741
747
|
npm run realsites # navigate/read/act against a matrix of real sites
|
|
742
748
|
```
|
|
743
749
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glittering0717/ai-browser",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.19",
|
|
4
4
|
"description": "AI-native browser — renders pixels for humans, structured semantic-tree API for agents. No screenshots, no OCR, no coordinate guessing.",
|
|
5
5
|
"main": "src/main/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -84,7 +84,8 @@
|
|
|
84
84
|
"verify:app": "node e2e/shipped_ws.cjs",
|
|
85
85
|
"wrapper": "node e2e/wrapper_ws.cjs",
|
|
86
86
|
"paint": "node e2e/paint_ws.cjs",
|
|
87
|
-
"layout": "node e2e/layout_ws.cjs"
|
|
87
|
+
"layout": "node e2e/layout_ws.cjs",
|
|
88
|
+
"flicker": "node e2e/flicker_ws.cjs"
|
|
88
89
|
},
|
|
89
90
|
"dependencies": {
|
|
90
91
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
package/src/main/page_manager.js
CHANGED
|
@@ -1040,7 +1040,18 @@ class PageManager {
|
|
|
1040
1040
|
captureBeyondViewport: o.fullPage,
|
|
1041
1041
|
};
|
|
1042
1042
|
if (o.quality !== undefined) params.quality = o.quality;
|
|
1043
|
-
|
|
1043
|
+
// clip.scale < 1("给我一张缩略概览")**绝不交给合成器**:带 clip.scale 的
|
|
1044
|
+
// 截图会让 Chromium 按该比例临时重建 surface,窗口恰好在合成时就会贴上那张
|
|
1045
|
+
// 缩小的图 1–3 帧(见 SPEC「取帧两条铁律」,2026-10-25 实锤)。
|
|
1046
|
+
// 现在按 scale=1 抓、用 nativeImage 在我们自己这边缩 —— 输出尺寸一致,
|
|
1047
|
+
// 但不再碰合成器。scale>1(放大取值)仍交给 Chromium:本地放大得不到像素。
|
|
1048
|
+
let localScale = 1;
|
|
1049
|
+
if (o.clip && typeof o.clip.scale === 'number' && o.clip.scale > 0 && o.clip.scale < 1) {
|
|
1050
|
+
localScale = o.clip.scale;
|
|
1051
|
+
params.clip = { ...o.clip, scale: 1 };
|
|
1052
|
+
} else if (o.clip) {
|
|
1053
|
+
params.clip = o.clip;
|
|
1054
|
+
}
|
|
1044
1055
|
const shot = await withDeadline(
|
|
1045
1056
|
dbg.sendCommand('Page.captureScreenshot', params),
|
|
1046
1057
|
20000, 'captureScreenshot'
|
|
@@ -1048,6 +1059,21 @@ class PageManager {
|
|
|
1048
1059
|
if (!shot || typeof shot.data !== 'string') {
|
|
1049
1060
|
return { ok: false, error: 'empty screenshot result', tab: tid };
|
|
1050
1061
|
}
|
|
1062
|
+
// 需要缩小就自己做(见上:不把缩放交给合成器)。
|
|
1063
|
+
let imgData = shot.data;
|
|
1064
|
+
if (localScale !== 1) {
|
|
1065
|
+
try {
|
|
1066
|
+
const img0 = nativeImage.createFromBuffer(Buffer.from(shot.data, 'base64'));
|
|
1067
|
+
if (!img0.isEmpty()) {
|
|
1068
|
+
const sz = img0.getSize();
|
|
1069
|
+
const w = Math.max(1, Math.round(sz.width * localScale));
|
|
1070
|
+
const resized = img0.resize({ width: w });
|
|
1071
|
+
imgData = o.format === 'jpeg'
|
|
1072
|
+
? resized.toJPEG(typeof o.quality === 'number' ? o.quality : 80).toString('base64')
|
|
1073
|
+
: resized.toPNG().toString('base64');
|
|
1074
|
+
}
|
|
1075
|
+
} catch (_) { /* 缩失败就用原图(宁可图大,不要空图) */ }
|
|
1076
|
+
}
|
|
1051
1077
|
|
|
1052
1078
|
const mime = 'image/' + (o.format === 'jpeg' ? 'jpeg' : o.format);
|
|
1053
1079
|
// 一次 evaluate 拿两样东西:可见密码框(截图不脱敏,泄密了得让调用方
|
|
@@ -1070,7 +1096,7 @@ class PageManager {
|
|
|
1070
1096
|
const summary = screenshotOpts.describeShot({
|
|
1071
1097
|
format: o.format,
|
|
1072
1098
|
mime,
|
|
1073
|
-
bytes: Buffer.byteLength(
|
|
1099
|
+
bytes: Buffer.byteLength(imgData, 'base64'),
|
|
1074
1100
|
width: meta.width,
|
|
1075
1101
|
height: meta.height,
|
|
1076
1102
|
url: (this._getView(tid) && this._getView(tid).webContents.getURL()) || undefined,
|
|
@@ -1082,7 +1108,7 @@ class PageManager {
|
|
|
1082
1108
|
if (o.path) {
|
|
1083
1109
|
try {
|
|
1084
1110
|
fs.mkdirSync(path.dirname(o.path), { recursive: true });
|
|
1085
|
-
fs.writeFileSync(o.path, Buffer.from(
|
|
1111
|
+
fs.writeFileSync(o.path, Buffer.from(imgData, 'base64'));
|
|
1086
1112
|
filePath = o.path;
|
|
1087
1113
|
} catch (e) {
|
|
1088
1114
|
return { ok: false, tab: tid, error: 'screenshot captured but writing to ' + o.path + ' failed: ' + ((e && e.message) || e), ...summary };
|
|
@@ -1095,7 +1121,7 @@ class PageManager {
|
|
|
1095
1121
|
ok: true,
|
|
1096
1122
|
tab: tid,
|
|
1097
1123
|
// data 是 base64 裸数据(不带 data: 前缀)—— 调用方按需自己拼。
|
|
1098
|
-
data:
|
|
1124
|
+
data: imgData,
|
|
1099
1125
|
mime,
|
|
1100
1126
|
...(filePath ? { path: filePath } : {}),
|
|
1101
1127
|
...(ignored.length ? { ignored_params: ignored } : {}),
|
package/src/shared/version.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
// Kept in JS (not imported from package.json) to avoid Node import-attribute
|
|
3
3
|
// (JSON module) compatibility issues inside Electron. package.json "version"
|
|
4
4
|
// is manually synced to this value; tests assert they match.
|
|
5
|
-
export const VERSION = '2.1.
|
|
5
|
+
export const VERSION = '2.1.19';
|