@ticktockbent/charlotte 0.1.0
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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/dist/browser/browser-manager.d.ts +14 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.js +72 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/cdp-session.d.ts +7 -0
- package/dist/browser/cdp-session.d.ts.map +1 -0
- package/dist/browser/cdp-session.js +35 -0
- package/dist/browser/cdp-session.js.map +1 -0
- package/dist/browser/page-manager.d.ts +30 -0
- package/dist/browser/page-manager.d.ts.map +1 -0
- package/dist/browser/page-manager.js +123 -0
- package/dist/browser/page-manager.js.map +1 -0
- package/dist/dev/auditor.d.ts +39 -0
- package/dist/dev/auditor.d.ts.map +1 -0
- package/dist/dev/auditor.js +474 -0
- package/dist/dev/auditor.js.map +1 -0
- package/dist/dev/dev-mode-state.d.ts +24 -0
- package/dist/dev/dev-mode-state.d.ts.map +1 -0
- package/dist/dev/dev-mode-state.js +93 -0
- package/dist/dev/dev-mode-state.js.map +1 -0
- package/dist/dev/file-watcher.d.ts +20 -0
- package/dist/dev/file-watcher.d.ts.map +1 -0
- package/dist/dev/file-watcher.js +78 -0
- package/dist/dev/file-watcher.js.map +1 -0
- package/dist/dev/static-server.d.ts +18 -0
- package/dist/dev/static-server.d.ts.map +1 -0
- package/dist/dev/static-server.js +73 -0
- package/dist/dev/static-server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer/accessibility-extractor.d.ts +19 -0
- package/dist/renderer/accessibility-extractor.d.ts.map +1 -0
- package/dist/renderer/accessibility-extractor.js +138 -0
- package/dist/renderer/accessibility-extractor.js.map +1 -0
- package/dist/renderer/content-extractor.d.ts +6 -0
- package/dist/renderer/content-extractor.d.ts.map +1 -0
- package/dist/renderer/content-extractor.js +150 -0
- package/dist/renderer/content-extractor.js.map +1 -0
- package/dist/renderer/dom-path.d.ts +4 -0
- package/dist/renderer/dom-path.d.ts.map +1 -0
- package/dist/renderer/dom-path.js +34 -0
- package/dist/renderer/dom-path.js.map +1 -0
- package/dist/renderer/element-id-generator.d.ts +19 -0
- package/dist/renderer/element-id-generator.d.ts.map +1 -0
- package/dist/renderer/element-id-generator.js +73 -0
- package/dist/renderer/element-id-generator.js.map +1 -0
- package/dist/renderer/interactive-extractor.d.ts +13 -0
- package/dist/renderer/interactive-extractor.d.ts.map +1 -0
- package/dist/renderer/interactive-extractor.js +161 -0
- package/dist/renderer/interactive-extractor.js.map +1 -0
- package/dist/renderer/layout-extractor.d.ts +8 -0
- package/dist/renderer/layout-extractor.d.ts.map +1 -0
- package/dist/renderer/layout-extractor.js +48 -0
- package/dist/renderer/layout-extractor.js.map +1 -0
- package/dist/renderer/renderer-pipeline.d.ts +26 -0
- package/dist/renderer/renderer-pipeline.d.ts.map +1 -0
- package/dist/renderer/renderer-pipeline.js +163 -0
- package/dist/renderer/renderer-pipeline.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +39 -0
- package/dist/server.js.map +1 -0
- package/dist/state/differ.d.ts +9 -0
- package/dist/state/differ.d.ts.map +1 -0
- package/dist/state/differ.js +295 -0
- package/dist/state/differ.js.map +1 -0
- package/dist/state/snapshot-store.d.ts +52 -0
- package/dist/state/snapshot-store.d.ts.map +1 -0
- package/dist/state/snapshot-store.js +98 -0
- package/dist/state/snapshot-store.js.map +1 -0
- package/dist/tools/dev-mode.d.ts +4 -0
- package/dist/tools/dev-mode.d.ts.map +1 -0
- package/dist/tools/dev-mode.js +160 -0
- package/dist/tools/dev-mode.js.map +1 -0
- package/dist/tools/evaluate.d.ts +10 -0
- package/dist/tools/evaluate.d.ts.map +1 -0
- package/dist/tools/evaluate.js +109 -0
- package/dist/tools/evaluate.js.map +1 -0
- package/dist/tools/interaction.d.ts +4 -0
- package/dist/tools/interaction.d.ts.map +1 -0
- package/dist/tools/interaction.js +680 -0
- package/dist/tools/interaction.js.map +1 -0
- package/dist/tools/navigation.d.ts +4 -0
- package/dist/tools/navigation.d.ts.map +1 -0
- package/dist/tools/navigation.js +136 -0
- package/dist/tools/navigation.js.map +1 -0
- package/dist/tools/observation.d.ts +4 -0
- package/dist/tools/observation.d.ts.map +1 -0
- package/dist/tools/observation.js +278 -0
- package/dist/tools/observation.js.map +1 -0
- package/dist/tools/session.d.ts +4 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +372 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/tool-helpers.d.ts +89 -0
- package/dist/tools/tool-helpers.d.ts.map +1 -0
- package/dist/tools/tool-helpers.js +127 -0
- package/dist/tools/tool-helpers.js.map +1 -0
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/element-id.d.ts +8 -0
- package/dist/types/element-id.d.ts.map +1 -0
- package/dist/types/element-id.js +19 -0
- package/dist/types/element-id.js.map +1 -0
- package/dist/types/errors.d.ts +22 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +30 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/page-representation.d.ts +84 -0
- package/dist/types/page-representation.d.ts.map +1 -0
- package/dist/types/page-representation.js +2 -0
- package/dist/types/page-representation.js.map +1 -0
- package/dist/types/snapshot.d.ts +22 -0
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +6 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +31 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/wait.d.ts +21 -0
- package/dist/utils/wait.d.ts.map +1 -0
- package/dist/utils/wait.js +55 -0
- package/dist/utils/wait.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { watch } from "chokidar";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
const DEFAULT_DEBOUNCE_MS = 300;
|
|
5
|
+
export class FileWatcher {
|
|
6
|
+
watcher = null;
|
|
7
|
+
pendingChanges = new Set();
|
|
8
|
+
debounceTimer = null;
|
|
9
|
+
directoryPath = null;
|
|
10
|
+
onFilesChanged = null;
|
|
11
|
+
debounceMs = DEFAULT_DEBOUNCE_MS;
|
|
12
|
+
async start(options) {
|
|
13
|
+
// Stop any existing watcher first
|
|
14
|
+
if (this.watcher) {
|
|
15
|
+
await this.stop();
|
|
16
|
+
}
|
|
17
|
+
this.directoryPath = path.resolve(options.directoryPath);
|
|
18
|
+
this.onFilesChanged = options.onFilesChanged;
|
|
19
|
+
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
20
|
+
this.watcher = watch(this.directoryPath, {
|
|
21
|
+
ignoreInitial: true,
|
|
22
|
+
ignored: ["**/node_modules/**", "**/.git/**"],
|
|
23
|
+
usePolling: options.usePolling ?? false,
|
|
24
|
+
interval: options.usePolling ? 100 : undefined,
|
|
25
|
+
});
|
|
26
|
+
const handleFileEvent = (filePath) => {
|
|
27
|
+
const relativePath = path.relative(this.directoryPath, filePath);
|
|
28
|
+
this.pendingChanges.add(relativePath);
|
|
29
|
+
// Clear existing debounce timer and set a new one
|
|
30
|
+
if (this.debounceTimer) {
|
|
31
|
+
clearTimeout(this.debounceTimer);
|
|
32
|
+
}
|
|
33
|
+
this.debounceTimer = setTimeout(() => {
|
|
34
|
+
this.flushPendingChanges();
|
|
35
|
+
}, this.debounceMs);
|
|
36
|
+
};
|
|
37
|
+
this.watcher.on("change", handleFileEvent);
|
|
38
|
+
this.watcher.on("add", handleFileEvent);
|
|
39
|
+
this.watcher.on("unlink", handleFileEvent);
|
|
40
|
+
// Wait for the watcher to be ready before resolving
|
|
41
|
+
await new Promise((resolve, reject) => {
|
|
42
|
+
this.watcher.on("ready", resolve);
|
|
43
|
+
this.watcher.on("error", reject);
|
|
44
|
+
});
|
|
45
|
+
logger.info("File watcher started", {
|
|
46
|
+
directory: this.directoryPath,
|
|
47
|
+
debounceMs: this.debounceMs,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async stop() {
|
|
51
|
+
if (this.debounceTimer) {
|
|
52
|
+
clearTimeout(this.debounceTimer);
|
|
53
|
+
this.debounceTimer = null;
|
|
54
|
+
}
|
|
55
|
+
if (this.watcher) {
|
|
56
|
+
await this.watcher.close();
|
|
57
|
+
this.watcher = null;
|
|
58
|
+
}
|
|
59
|
+
this.pendingChanges.clear();
|
|
60
|
+
this.directoryPath = null;
|
|
61
|
+
this.onFilesChanged = null;
|
|
62
|
+
logger.info("File watcher stopped");
|
|
63
|
+
}
|
|
64
|
+
isWatching() {
|
|
65
|
+
return this.watcher !== null;
|
|
66
|
+
}
|
|
67
|
+
flushPendingChanges() {
|
|
68
|
+
if (this.pendingChanges.size === 0 || !this.onFilesChanged) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const changedFiles = [...this.pendingChanges];
|
|
72
|
+
this.pendingChanges.clear();
|
|
73
|
+
this.debounceTimer = null;
|
|
74
|
+
logger.debug("File changes detected", { files: changedFiles });
|
|
75
|
+
this.onFilesChanged(changedFiles);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=file-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../../src/dev/file-watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAU5C,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,MAAM,OAAO,WAAW;IACd,OAAO,GAAqB,IAAI,CAAC;IACjC,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,aAAa,GAAyC,IAAI,CAAC;IAC3D,aAAa,GAAkB,IAAI,CAAC;IACpC,cAAc,GAA8C,IAAI,CAAC;IACjE,UAAU,GAAG,mBAAmB,CAAC;IAEzC,KAAK,CAAC,KAAK,CAAC,OAA2B;QACrC,kCAAkC;QAClC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;QAE5D,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE;YACvC,aAAa,EAAE,IAAI;YACnB,OAAO,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC;YAC7C,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,KAAK;YACvC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;SAC/C,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAE,EAAE;YAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAc,EAAE,QAAQ,CAAC,CAAC;YAClE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAEtC,kDAAkD;YAClD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;gBACnC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;QAE3C,oDAAoD;QACpD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,CAAC,OAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACnC,IAAI,CAAC,OAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE;YAClC,SAAS,EAAE,IAAI,CAAC,aAAa;YAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACjC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAE3B,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;IAEO,mBAAmB;QACzB,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC3D,OAAO;QACT,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9C,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface StaticServerOptions {
|
|
2
|
+
directoryPath: string;
|
|
3
|
+
port?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface StaticServerInfo {
|
|
6
|
+
url: string;
|
|
7
|
+
port: number;
|
|
8
|
+
directoryPath: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class StaticServer {
|
|
11
|
+
private httpServer;
|
|
12
|
+
private serverInfo;
|
|
13
|
+
start(options: StaticServerOptions): Promise<StaticServerInfo>;
|
|
14
|
+
stop(): Promise<void>;
|
|
15
|
+
isRunning(): boolean;
|
|
16
|
+
getInfo(): StaticServerInfo | null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=static-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.d.ts","sourceRoot":"","sources":["../../src/dev/static-server.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,UAAU,CAAiC;IAE7C,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+C9D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B,SAAS,IAAI,OAAO;IAIpB,OAAO,IAAI,gBAAgB,GAAG,IAAI;CAGnC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
export class StaticServer {
|
|
5
|
+
httpServer = null;
|
|
6
|
+
serverInfo = null;
|
|
7
|
+
async start(options) {
|
|
8
|
+
// Stop any existing server first
|
|
9
|
+
if (this.httpServer) {
|
|
10
|
+
await this.stop();
|
|
11
|
+
}
|
|
12
|
+
const absoluteDirectoryPath = path.resolve(options.directoryPath);
|
|
13
|
+
const app = express();
|
|
14
|
+
app.use(express.static(absoluteDirectoryPath));
|
|
15
|
+
const listenPort = options.port ?? 0;
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const server = app.listen(listenPort, () => {
|
|
18
|
+
const address = server.address();
|
|
19
|
+
if (!address || typeof address === "string") {
|
|
20
|
+
reject(new Error("Failed to get server address"));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const assignedPort = address.port;
|
|
24
|
+
const info = {
|
|
25
|
+
url: `http://localhost:${assignedPort}`,
|
|
26
|
+
port: assignedPort,
|
|
27
|
+
directoryPath: absoluteDirectoryPath,
|
|
28
|
+
};
|
|
29
|
+
this.httpServer = server;
|
|
30
|
+
this.serverInfo = info;
|
|
31
|
+
logger.info("Static server started", {
|
|
32
|
+
url: info.url,
|
|
33
|
+
directory: info.directoryPath,
|
|
34
|
+
});
|
|
35
|
+
resolve(info);
|
|
36
|
+
});
|
|
37
|
+
server.on("error", (error) => {
|
|
38
|
+
if (error.code === "EADDRINUSE") {
|
|
39
|
+
reject(new Error(`Port ${listenPort} is already in use`));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
reject(error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async stop() {
|
|
48
|
+
if (!this.httpServer) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.httpServer.close((error) => {
|
|
53
|
+
if (error) {
|
|
54
|
+
logger.warn("Error closing static server", error);
|
|
55
|
+
reject(error);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
logger.info("Static server stopped");
|
|
59
|
+
resolve();
|
|
60
|
+
}
|
|
61
|
+
this.httpServer = null;
|
|
62
|
+
this.serverInfo = null;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
isRunning() {
|
|
67
|
+
return this.httpServer !== null;
|
|
68
|
+
}
|
|
69
|
+
getInfo() {
|
|
70
|
+
return this.serverInfo;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=static-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.js","sourceRoot":"","sources":["../../src/dev/static-server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { BrowserManager } from "./browser/browser-manager.js";
|
|
4
|
+
import { PageManager } from "./browser/page-manager.js";
|
|
5
|
+
import { CDPSessionManager } from "./browser/cdp-session.js";
|
|
6
|
+
import { RendererPipeline } from "./renderer/renderer-pipeline.js";
|
|
7
|
+
import { ElementIdGenerator } from "./renderer/element-id-generator.js";
|
|
8
|
+
import { SnapshotStore } from "./state/snapshot-store.js";
|
|
9
|
+
import { createDefaultConfig } from "./types/config.js";
|
|
10
|
+
import { createServer } from "./server.js";
|
|
11
|
+
import { DevModeState } from "./dev/dev-mode-state.js";
|
|
12
|
+
import { logger } from "./utils/logger.js";
|
|
13
|
+
async function main() {
|
|
14
|
+
logger.info("Charlotte starting");
|
|
15
|
+
// Initialize browser
|
|
16
|
+
const browserManager = new BrowserManager();
|
|
17
|
+
await browserManager.launch();
|
|
18
|
+
// Initialize page management
|
|
19
|
+
const pageManager = new PageManager();
|
|
20
|
+
// Open a default tab
|
|
21
|
+
await pageManager.openTab(browserManager);
|
|
22
|
+
// Initialize renderer pipeline
|
|
23
|
+
const cdpSessionManager = new CDPSessionManager();
|
|
24
|
+
const elementIdGenerator = new ElementIdGenerator();
|
|
25
|
+
const rendererPipeline = new RendererPipeline(cdpSessionManager, elementIdGenerator);
|
|
26
|
+
// Initialize state management
|
|
27
|
+
const config = createDefaultConfig();
|
|
28
|
+
const snapshotStore = new SnapshotStore(config.snapshotDepth);
|
|
29
|
+
// Initialize dev mode state
|
|
30
|
+
const devModeState = new DevModeState();
|
|
31
|
+
// Create and configure MCP server
|
|
32
|
+
const mcpServer = createServer({
|
|
33
|
+
browserManager,
|
|
34
|
+
pageManager,
|
|
35
|
+
rendererPipeline,
|
|
36
|
+
elementIdGenerator,
|
|
37
|
+
snapshotStore,
|
|
38
|
+
config,
|
|
39
|
+
devModeState,
|
|
40
|
+
});
|
|
41
|
+
// Connect stdio transport
|
|
42
|
+
const transport = new StdioServerTransport();
|
|
43
|
+
await mcpServer.connect(transport);
|
|
44
|
+
logger.info("Charlotte MCP server running on stdio");
|
|
45
|
+
// Graceful shutdown
|
|
46
|
+
const shutdown = async () => {
|
|
47
|
+
logger.info("Shutting down");
|
|
48
|
+
await devModeState.stopAll();
|
|
49
|
+
await mcpServer.close();
|
|
50
|
+
await browserManager.close();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
};
|
|
53
|
+
process.on("SIGINT", shutdown);
|
|
54
|
+
process.on("SIGTERM", shutdown);
|
|
55
|
+
}
|
|
56
|
+
main().catch((error) => {
|
|
57
|
+
logger.error("Fatal error", error);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAElC,qBAAqB;IACrB,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC5C,MAAM,cAAc,CAAC,MAAM,EAAE,CAAC;IAE9B,6BAA6B;IAC7B,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IAEtC,qBAAqB;IACrB,MAAM,WAAW,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAE1C,+BAA+B;IAC/B,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAClD,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACpD,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,CAC3C,iBAAiB,EACjB,kBAAkB,CACnB,CAAC;IAEF,8BAA8B;IAC9B,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE9D,4BAA4B;IAC5B,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;IAExC,kCAAkC;IAClC,MAAM,SAAS,GAAG,YAAY,CAAC;QAC7B,cAAc;QACd,WAAW;QACX,gBAAgB;QAChB,kBAAkB;QAClB,aAAa;QACb,MAAM;QACN,YAAY;KACb,CAAC,CAAC;IAEH,0BAA0B;IAC1B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEnC,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IAErD,oBAAoB;IACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,MAAM,YAAY,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,cAAc,CAAC,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CDPSession } from "puppeteer";
|
|
2
|
+
export interface ParsedAXNode {
|
|
3
|
+
nodeId: string;
|
|
4
|
+
role: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
value: string | null;
|
|
8
|
+
properties: Record<string, unknown>;
|
|
9
|
+
backendDOMNodeId: number | null;
|
|
10
|
+
children: ParsedAXNode[];
|
|
11
|
+
parent: ParsedAXNode | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function isLandmarkRole(role: string): boolean;
|
|
14
|
+
export declare function isHeadingRole(role: string): boolean;
|
|
15
|
+
export declare function isInteractiveRole(role: string): boolean;
|
|
16
|
+
export declare class AccessibilityExtractor {
|
|
17
|
+
extract(session: CDPSession): Promise<ParsedAXNode[]>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=accessibility-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-extractor.d.ts","sourceRoot":"","sources":["../../src/renderer/accessibility-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B;AAkCD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AA2BD,qBAAa,sBAAsB;IAC3B,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAkG5D"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
const LANDMARK_ROLES = new Set([
|
|
3
|
+
"banner",
|
|
4
|
+
"navigation",
|
|
5
|
+
"main",
|
|
6
|
+
"complementary",
|
|
7
|
+
"contentinfo",
|
|
8
|
+
"form",
|
|
9
|
+
"region",
|
|
10
|
+
"search",
|
|
11
|
+
]);
|
|
12
|
+
const HEADING_ROLE = "heading";
|
|
13
|
+
const INTERACTIVE_ROLES = new Set([
|
|
14
|
+
"button",
|
|
15
|
+
"link",
|
|
16
|
+
"textbox",
|
|
17
|
+
"combobox",
|
|
18
|
+
"listbox",
|
|
19
|
+
"checkbox",
|
|
20
|
+
"radio",
|
|
21
|
+
"switch",
|
|
22
|
+
"slider",
|
|
23
|
+
"spinbutton",
|
|
24
|
+
"searchbox",
|
|
25
|
+
"menuitem",
|
|
26
|
+
"menuitemcheckbox",
|
|
27
|
+
"menuitemradio",
|
|
28
|
+
"tab",
|
|
29
|
+
"treeitem",
|
|
30
|
+
]);
|
|
31
|
+
export function isLandmarkRole(role) {
|
|
32
|
+
return LANDMARK_ROLES.has(role);
|
|
33
|
+
}
|
|
34
|
+
export function isHeadingRole(role) {
|
|
35
|
+
return role === HEADING_ROLE;
|
|
36
|
+
}
|
|
37
|
+
export function isInteractiveRole(role) {
|
|
38
|
+
return INTERACTIVE_ROLES.has(role);
|
|
39
|
+
}
|
|
40
|
+
function extractProperties(rawProperties) {
|
|
41
|
+
const properties = {};
|
|
42
|
+
if (!rawProperties)
|
|
43
|
+
return properties;
|
|
44
|
+
for (const prop of rawProperties) {
|
|
45
|
+
properties[prop.name] = prop.value.value;
|
|
46
|
+
}
|
|
47
|
+
return properties;
|
|
48
|
+
}
|
|
49
|
+
export class AccessibilityExtractor {
|
|
50
|
+
async extract(session) {
|
|
51
|
+
logger.debug("Extracting accessibility tree");
|
|
52
|
+
const result = await session.send("Accessibility.getFullAXTree");
|
|
53
|
+
const rawNodes = result.nodes;
|
|
54
|
+
if (!rawNodes || rawNodes.length === 0) {
|
|
55
|
+
logger.warn("Empty accessibility tree returned");
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
logger.debug(`Got ${rawNodes.length} AX nodes`);
|
|
59
|
+
// Build lookup map
|
|
60
|
+
const nodeMap = new Map();
|
|
61
|
+
const childToParent = new Map();
|
|
62
|
+
// Track which raw nodes are ignored, so we can reparent their children
|
|
63
|
+
const rawNodeMap = new Map();
|
|
64
|
+
for (const raw of rawNodes) {
|
|
65
|
+
rawNodeMap.set(raw.nodeId, raw);
|
|
66
|
+
}
|
|
67
|
+
// For ignored nodes, find the nearest non-ignored ancestor
|
|
68
|
+
function findNonIgnoredParent(nodeId) {
|
|
69
|
+
let currentId = nodeId;
|
|
70
|
+
while (true) {
|
|
71
|
+
const rawNode = rawNodeMap.get(currentId);
|
|
72
|
+
if (!rawNode?.parentId)
|
|
73
|
+
return undefined;
|
|
74
|
+
const parentRaw = rawNodeMap.get(rawNode.parentId);
|
|
75
|
+
if (!parentRaw)
|
|
76
|
+
return undefined;
|
|
77
|
+
if (!parentRaw.ignored)
|
|
78
|
+
return parentRaw.nodeId;
|
|
79
|
+
currentId = rawNode.parentId;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// First pass: create ParsedAXNode for each non-ignored node
|
|
83
|
+
for (const raw of rawNodes) {
|
|
84
|
+
if (raw.ignored) {
|
|
85
|
+
// Reparent children of ignored nodes to the nearest non-ignored ancestor
|
|
86
|
+
if (raw.childIds) {
|
|
87
|
+
const nonIgnoredParentId = findNonIgnoredParent(raw.nodeId);
|
|
88
|
+
for (const childId of raw.childIds) {
|
|
89
|
+
if (nonIgnoredParentId) {
|
|
90
|
+
childToParent.set(childId, nonIgnoredParentId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const role = raw.role?.value ?? "none";
|
|
97
|
+
const name = raw.name?.value ?? "";
|
|
98
|
+
const description = raw.description?.value ?? "";
|
|
99
|
+
const rawValue = raw.value?.value;
|
|
100
|
+
const value = rawValue !== undefined && rawValue !== null
|
|
101
|
+
? String(rawValue)
|
|
102
|
+
: null;
|
|
103
|
+
const parsed = {
|
|
104
|
+
nodeId: raw.nodeId,
|
|
105
|
+
role,
|
|
106
|
+
name,
|
|
107
|
+
description,
|
|
108
|
+
value,
|
|
109
|
+
properties: extractProperties(raw.properties),
|
|
110
|
+
backendDOMNodeId: raw.backendDOMNodeId ?? null,
|
|
111
|
+
children: [],
|
|
112
|
+
parent: null,
|
|
113
|
+
};
|
|
114
|
+
nodeMap.set(raw.nodeId, parsed);
|
|
115
|
+
if (raw.childIds) {
|
|
116
|
+
for (const childId of raw.childIds) {
|
|
117
|
+
childToParent.set(childId, raw.nodeId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Second pass: link parent-child relationships
|
|
122
|
+
const rootNodes = [];
|
|
123
|
+
for (const [nodeId, node] of nodeMap) {
|
|
124
|
+
const parentId = childToParent.get(nodeId);
|
|
125
|
+
if (parentId && nodeMap.has(parentId)) {
|
|
126
|
+
const parentNode = nodeMap.get(parentId);
|
|
127
|
+
node.parent = parentNode;
|
|
128
|
+
parentNode.children.push(node);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
rootNodes.push(node);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
logger.debug(`Reconstructed tree with ${rootNodes.length} root node(s)`);
|
|
135
|
+
return rootNodes;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=accessibility-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-extractor.js","sourceRoot":"","sources":["../../src/renderer/accessibility-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ParsedAXNode } from "./accessibility-extractor.js";
|
|
2
|
+
export declare class ContentExtractor {
|
|
3
|
+
extractSummary(rootNodes: ParsedAXNode[]): string;
|
|
4
|
+
extractFullContent(rootNodes: ParsedAXNode[]): string;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=content-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-extractor.d.ts","sourceRoot":"","sources":["../../src/renderer/content-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAmGjE,qBAAa,gBAAgB;IAC3B,cAAc,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM;IAqCjD,kBAAkB,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM;CAsBtD"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isLandmarkRole } from "./accessibility-extractor.js";
|
|
2
|
+
function createEmptyCounts(role, label) {
|
|
3
|
+
return {
|
|
4
|
+
role,
|
|
5
|
+
label,
|
|
6
|
+
headings: 0,
|
|
7
|
+
links: 0,
|
|
8
|
+
buttons: 0,
|
|
9
|
+
inputs: 0,
|
|
10
|
+
images: 0,
|
|
11
|
+
lists: 0,
|
|
12
|
+
tables: 0,
|
|
13
|
+
forms: 0,
|
|
14
|
+
paragraphs: 0,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function countElements(node, counts) {
|
|
18
|
+
switch (node.role) {
|
|
19
|
+
case "heading":
|
|
20
|
+
counts.headings++;
|
|
21
|
+
break;
|
|
22
|
+
case "link":
|
|
23
|
+
counts.links++;
|
|
24
|
+
break;
|
|
25
|
+
case "button":
|
|
26
|
+
counts.buttons++;
|
|
27
|
+
break;
|
|
28
|
+
case "textbox":
|
|
29
|
+
case "searchbox":
|
|
30
|
+
case "combobox":
|
|
31
|
+
case "listbox":
|
|
32
|
+
case "checkbox":
|
|
33
|
+
case "radio":
|
|
34
|
+
case "slider":
|
|
35
|
+
case "spinbutton":
|
|
36
|
+
case "switch":
|
|
37
|
+
counts.inputs++;
|
|
38
|
+
break;
|
|
39
|
+
case "img":
|
|
40
|
+
case "image":
|
|
41
|
+
counts.images++;
|
|
42
|
+
break;
|
|
43
|
+
case "list":
|
|
44
|
+
counts.lists++;
|
|
45
|
+
break;
|
|
46
|
+
case "table":
|
|
47
|
+
counts.tables++;
|
|
48
|
+
break;
|
|
49
|
+
case "form":
|
|
50
|
+
counts.forms++;
|
|
51
|
+
break;
|
|
52
|
+
case "paragraph":
|
|
53
|
+
counts.paragraphs++;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
for (const child of node.children) {
|
|
57
|
+
// Don't recurse into nested landmarks — they get their own counts
|
|
58
|
+
if (!isLandmarkRole(child.role)) {
|
|
59
|
+
countElements(child, counts);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function formatCounts(counts) {
|
|
64
|
+
const parts = [];
|
|
65
|
+
if (counts.headings > 0)
|
|
66
|
+
parts.push(`${counts.headings} headings`);
|
|
67
|
+
if (counts.paragraphs > 0)
|
|
68
|
+
parts.push(`${counts.paragraphs} paragraphs`);
|
|
69
|
+
if (counts.links > 0)
|
|
70
|
+
parts.push(`${counts.links} links`);
|
|
71
|
+
if (counts.buttons > 0)
|
|
72
|
+
parts.push(`${counts.buttons} buttons`);
|
|
73
|
+
if (counts.inputs > 0)
|
|
74
|
+
parts.push(`${counts.inputs} inputs`);
|
|
75
|
+
if (counts.forms > 0)
|
|
76
|
+
parts.push(`${counts.forms} forms`);
|
|
77
|
+
if (counts.images > 0)
|
|
78
|
+
parts.push(`${counts.images} images`);
|
|
79
|
+
if (counts.lists > 0)
|
|
80
|
+
parts.push(`${counts.lists} lists`);
|
|
81
|
+
if (counts.tables > 0)
|
|
82
|
+
parts.push(`${counts.tables} tables`);
|
|
83
|
+
const label = counts.label || counts.role;
|
|
84
|
+
if (parts.length === 0)
|
|
85
|
+
return `${label}: empty`;
|
|
86
|
+
return `${label}: ${parts.join(", ")}`;
|
|
87
|
+
}
|
|
88
|
+
export class ContentExtractor {
|
|
89
|
+
extractSummary(rootNodes) {
|
|
90
|
+
const landmarkSummaries = [];
|
|
91
|
+
const topLevelCounts = createEmptyCounts("page", "page");
|
|
92
|
+
const findLandmarks = (node) => {
|
|
93
|
+
if (isLandmarkRole(node.role)) {
|
|
94
|
+
const counts = createEmptyCounts(node.role, node.name || node.role);
|
|
95
|
+
countElements(node, counts);
|
|
96
|
+
landmarkSummaries.push(formatCounts(counts));
|
|
97
|
+
// Don't recurse into landmark children here — countElements
|
|
98
|
+
// already skips nested landmarks, which get found by continued traversal
|
|
99
|
+
}
|
|
100
|
+
// Always recurse into children to find landmarks at any depth
|
|
101
|
+
for (const child of node.children) {
|
|
102
|
+
findLandmarks(child);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
for (const root of rootNodes) {
|
|
106
|
+
findLandmarks(root);
|
|
107
|
+
// Count non-landmark content at root level (for fallback summary)
|
|
108
|
+
if (!isLandmarkRole(root.role)) {
|
|
109
|
+
countElements(root, topLevelCounts);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// If we have landmark summaries, use those
|
|
113
|
+
if (landmarkSummaries.length > 0) {
|
|
114
|
+
return landmarkSummaries.join("; ");
|
|
115
|
+
}
|
|
116
|
+
// Fallback: summarize the whole page
|
|
117
|
+
return formatCounts(topLevelCounts);
|
|
118
|
+
}
|
|
119
|
+
extractFullContent(rootNodes) {
|
|
120
|
+
const textParts = [];
|
|
121
|
+
const traverse = (node) => {
|
|
122
|
+
// Include text from nodes that represent content
|
|
123
|
+
if (node.role === "StaticText" || node.role === "text") {
|
|
124
|
+
if (node.name)
|
|
125
|
+
textParts.push(node.name);
|
|
126
|
+
}
|
|
127
|
+
else if (node.name && isContentRole(node.role)) {
|
|
128
|
+
textParts.push(node.name);
|
|
129
|
+
}
|
|
130
|
+
for (const child of node.children) {
|
|
131
|
+
traverse(child);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
for (const root of rootNodes) {
|
|
135
|
+
traverse(root);
|
|
136
|
+
}
|
|
137
|
+
return textParts.join("\n");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function isContentRole(role) {
|
|
141
|
+
return (role === "heading" ||
|
|
142
|
+
role === "paragraph" ||
|
|
143
|
+
role === "listitem" ||
|
|
144
|
+
role === "cell" ||
|
|
145
|
+
role === "label" ||
|
|
146
|
+
role === "legend" ||
|
|
147
|
+
role === "caption" ||
|
|
148
|
+
role === "blockquote");
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=content-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-extractor.js","sourceRoot":"","sources":["../../src/renderer/content-extractor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAgB9D,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAa;IACpD,OAAO;QACL,IAAI;QACJ,KAAK;QACL,QAAQ,EAAE,CAAC;QACX,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,CAAC;QACR,UAAU,EAAE,CAAC;KACd,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAkB,EAAE,MAAsB;IAC/D,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,MAAM,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,WAAW,CAAC;QACjB,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS,CAAC;QACf,KAAK,UAAU,CAAC;QAChB,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,QAAQ;YACX,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM;QACR,KAAK,KAAK,CAAC;QACX,KAAK,OAAO;YACV,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM;QACR,KAAK,OAAO;YACV,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM;QACR,KAAK,WAAW;YACd,MAAM,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM;IACV,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,kEAAkE;QAClE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,MAAsB;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,WAAW,CAAC,CAAC;IACnE,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,aAAa,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,KAAK,SAAS,CAAC;IACjD,OAAO,GAAG,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,OAAO,gBAAgB;IAC3B,cAAc,CAAC,SAAyB;QACtC,MAAM,iBAAiB,GAAa,EAAE,CAAC;QACvC,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAEzD,MAAM,aAAa,GAAG,CAAC,IAAkB,EAAQ,EAAE;YACjD,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC5B,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC7C,4DAA4D;gBAC5D,yEAAyE;YAC3E,CAAC;YAED,8DAA8D;YAC9D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClC,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,aAAa,CAAC,IAAI,CAAC,CAAC;YAEpB,kEAAkE;YAClE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,aAAa,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,qCAAqC;QACrC,OAAO,YAAY,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IAED,kBAAkB,CAAC,SAAyB;QAC1C,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,MAAM,QAAQ,GAAG,CAAC,IAAkB,EAAE,EAAE;YACtC,iDAAiD;YACjD,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACvD,IAAI,IAAI,CAAC,IAAI;oBAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,CACL,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,WAAW;QACpB,IAAI,KAAK,UAAU;QACnB,IAAI,KAAK,MAAM;QACf,IAAI,KAAK,OAAO;QAChB,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,YAAY,CACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-path.d.ts","sourceRoot":"","sources":["../../src/renderer/dom-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,GAAG,gBAAgB,CAqC5E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isLandmarkRole } from "./accessibility-extractor.js";
|
|
2
|
+
export function computeDOMPathSignature(node) {
|
|
3
|
+
let nearestLandmarkRole = null;
|
|
4
|
+
let nearestLandmarkLabel = null;
|
|
5
|
+
let nearestLabelledContainer = null;
|
|
6
|
+
// Walk ancestors to find nearest landmark and nearest labelled container
|
|
7
|
+
let ancestor = node.parent;
|
|
8
|
+
while (ancestor) {
|
|
9
|
+
if (!nearestLabelledContainer && ancestor.name) {
|
|
10
|
+
nearestLabelledContainer = ancestor.name;
|
|
11
|
+
}
|
|
12
|
+
if (!nearestLandmarkRole && isLandmarkRole(ancestor.role)) {
|
|
13
|
+
nearestLandmarkRole = ancestor.role;
|
|
14
|
+
nearestLandmarkLabel = ancestor.name || null;
|
|
15
|
+
break; // Landmark is more specific, stop here
|
|
16
|
+
}
|
|
17
|
+
ancestor = ancestor.parent;
|
|
18
|
+
}
|
|
19
|
+
// Compute sibling index: position among siblings with the same role
|
|
20
|
+
let siblingIndex = 0;
|
|
21
|
+
if (node.parent) {
|
|
22
|
+
const sameRoleSiblings = node.parent.children.filter((child) => child.role === node.role);
|
|
23
|
+
siblingIndex = sameRoleSiblings.indexOf(node);
|
|
24
|
+
if (siblingIndex === -1)
|
|
25
|
+
siblingIndex = 0;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
nearestLandmarkRole,
|
|
29
|
+
nearestLandmarkLabel,
|
|
30
|
+
nearestLabelledContainer,
|
|
31
|
+
siblingIndex,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=dom-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-path.js","sourceRoot":"","sources":["../../src/renderer/dom-path.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,MAAM,UAAU,uBAAuB,CAAC,IAAkB;IACxD,IAAI,mBAAmB,GAAkB,IAAI,CAAC;IAC9C,IAAI,oBAAoB,GAAkB,IAAI,CAAC;IAC/C,IAAI,wBAAwB,GAAkB,IAAI,CAAC;IAEnD,yEAAyE;IACzE,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,OAAO,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,wBAAwB,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC/C,wBAAwB,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,mBAAmB,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,mBAAmB,GAAG,QAAQ,CAAC,IAAI,CAAC;YACpC,oBAAoB,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC;YAC7C,MAAM,CAAC,uCAAuC;QAChD,CAAC;QAED,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED,oEAAoE;IACpE,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAClD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CACpC,CAAC;QACF,YAAY,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,YAAY,KAAK,CAAC,CAAC;YAAE,YAAY,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO;QACL,mBAAmB;QACnB,oBAAoB;QACpB,wBAAwB;QACxB,YAAY;KACb,CAAC;AACJ,CAAC"}
|