@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +254 -0
  4. package/dist/browser/browser-manager.d.ts +14 -0
  5. package/dist/browser/browser-manager.d.ts.map +1 -0
  6. package/dist/browser/browser-manager.js +72 -0
  7. package/dist/browser/browser-manager.js.map +1 -0
  8. package/dist/browser/cdp-session.d.ts +7 -0
  9. package/dist/browser/cdp-session.d.ts.map +1 -0
  10. package/dist/browser/cdp-session.js +35 -0
  11. package/dist/browser/cdp-session.js.map +1 -0
  12. package/dist/browser/page-manager.d.ts +30 -0
  13. package/dist/browser/page-manager.d.ts.map +1 -0
  14. package/dist/browser/page-manager.js +123 -0
  15. package/dist/browser/page-manager.js.map +1 -0
  16. package/dist/dev/auditor.d.ts +39 -0
  17. package/dist/dev/auditor.d.ts.map +1 -0
  18. package/dist/dev/auditor.js +474 -0
  19. package/dist/dev/auditor.js.map +1 -0
  20. package/dist/dev/dev-mode-state.d.ts +24 -0
  21. package/dist/dev/dev-mode-state.d.ts.map +1 -0
  22. package/dist/dev/dev-mode-state.js +93 -0
  23. package/dist/dev/dev-mode-state.js.map +1 -0
  24. package/dist/dev/file-watcher.d.ts +20 -0
  25. package/dist/dev/file-watcher.d.ts.map +1 -0
  26. package/dist/dev/file-watcher.js +78 -0
  27. package/dist/dev/file-watcher.js.map +1 -0
  28. package/dist/dev/static-server.d.ts +18 -0
  29. package/dist/dev/static-server.d.ts.map +1 -0
  30. package/dist/dev/static-server.js +73 -0
  31. package/dist/dev/static-server.js.map +1 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +60 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/renderer/accessibility-extractor.d.ts +19 -0
  37. package/dist/renderer/accessibility-extractor.d.ts.map +1 -0
  38. package/dist/renderer/accessibility-extractor.js +138 -0
  39. package/dist/renderer/accessibility-extractor.js.map +1 -0
  40. package/dist/renderer/content-extractor.d.ts +6 -0
  41. package/dist/renderer/content-extractor.d.ts.map +1 -0
  42. package/dist/renderer/content-extractor.js +150 -0
  43. package/dist/renderer/content-extractor.js.map +1 -0
  44. package/dist/renderer/dom-path.d.ts +4 -0
  45. package/dist/renderer/dom-path.d.ts.map +1 -0
  46. package/dist/renderer/dom-path.js +34 -0
  47. package/dist/renderer/dom-path.js.map +1 -0
  48. package/dist/renderer/element-id-generator.d.ts +19 -0
  49. package/dist/renderer/element-id-generator.d.ts.map +1 -0
  50. package/dist/renderer/element-id-generator.js +73 -0
  51. package/dist/renderer/element-id-generator.js.map +1 -0
  52. package/dist/renderer/interactive-extractor.d.ts +13 -0
  53. package/dist/renderer/interactive-extractor.d.ts.map +1 -0
  54. package/dist/renderer/interactive-extractor.js +161 -0
  55. package/dist/renderer/interactive-extractor.js.map +1 -0
  56. package/dist/renderer/layout-extractor.d.ts +8 -0
  57. package/dist/renderer/layout-extractor.d.ts.map +1 -0
  58. package/dist/renderer/layout-extractor.js +48 -0
  59. package/dist/renderer/layout-extractor.js.map +1 -0
  60. package/dist/renderer/renderer-pipeline.d.ts +26 -0
  61. package/dist/renderer/renderer-pipeline.d.ts.map +1 -0
  62. package/dist/renderer/renderer-pipeline.js +163 -0
  63. package/dist/renderer/renderer-pipeline.js.map +1 -0
  64. package/dist/server.d.ts +19 -0
  65. package/dist/server.d.ts.map +1 -0
  66. package/dist/server.js +39 -0
  67. package/dist/server.js.map +1 -0
  68. package/dist/state/differ.d.ts +9 -0
  69. package/dist/state/differ.d.ts.map +1 -0
  70. package/dist/state/differ.js +295 -0
  71. package/dist/state/differ.js.map +1 -0
  72. package/dist/state/snapshot-store.d.ts +52 -0
  73. package/dist/state/snapshot-store.d.ts.map +1 -0
  74. package/dist/state/snapshot-store.js +98 -0
  75. package/dist/state/snapshot-store.js.map +1 -0
  76. package/dist/tools/dev-mode.d.ts +4 -0
  77. package/dist/tools/dev-mode.d.ts.map +1 -0
  78. package/dist/tools/dev-mode.js +160 -0
  79. package/dist/tools/dev-mode.js.map +1 -0
  80. package/dist/tools/evaluate.d.ts +10 -0
  81. package/dist/tools/evaluate.d.ts.map +1 -0
  82. package/dist/tools/evaluate.js +109 -0
  83. package/dist/tools/evaluate.js.map +1 -0
  84. package/dist/tools/interaction.d.ts +4 -0
  85. package/dist/tools/interaction.d.ts.map +1 -0
  86. package/dist/tools/interaction.js +680 -0
  87. package/dist/tools/interaction.js.map +1 -0
  88. package/dist/tools/navigation.d.ts +4 -0
  89. package/dist/tools/navigation.d.ts.map +1 -0
  90. package/dist/tools/navigation.js +136 -0
  91. package/dist/tools/navigation.js.map +1 -0
  92. package/dist/tools/observation.d.ts +4 -0
  93. package/dist/tools/observation.d.ts.map +1 -0
  94. package/dist/tools/observation.js +278 -0
  95. package/dist/tools/observation.js.map +1 -0
  96. package/dist/tools/session.d.ts +4 -0
  97. package/dist/tools/session.d.ts.map +1 -0
  98. package/dist/tools/session.js +372 -0
  99. package/dist/tools/session.js.map +1 -0
  100. package/dist/tools/tool-helpers.d.ts +89 -0
  101. package/dist/tools/tool-helpers.d.ts.map +1 -0
  102. package/dist/tools/tool-helpers.js +127 -0
  103. package/dist/tools/tool-helpers.js.map +1 -0
  104. package/dist/types/config.d.ts +7 -0
  105. package/dist/types/config.d.ts.map +1 -0
  106. package/dist/types/config.js +7 -0
  107. package/dist/types/config.js.map +1 -0
  108. package/dist/types/element-id.d.ts +8 -0
  109. package/dist/types/element-id.d.ts.map +1 -0
  110. package/dist/types/element-id.js +19 -0
  111. package/dist/types/element-id.js.map +1 -0
  112. package/dist/types/errors.d.ts +22 -0
  113. package/dist/types/errors.d.ts.map +1 -0
  114. package/dist/types/errors.js +30 -0
  115. package/dist/types/errors.js.map +1 -0
  116. package/dist/types/page-representation.d.ts +84 -0
  117. package/dist/types/page-representation.d.ts.map +1 -0
  118. package/dist/types/page-representation.js +2 -0
  119. package/dist/types/page-representation.js.map +1 -0
  120. package/dist/types/snapshot.d.ts +22 -0
  121. package/dist/types/snapshot.d.ts.map +1 -0
  122. package/dist/types/snapshot.js +2 -0
  123. package/dist/types/snapshot.js.map +1 -0
  124. package/dist/utils/hash.d.ts +2 -0
  125. package/dist/utils/hash.d.ts.map +1 -0
  126. package/dist/utils/hash.js +6 -0
  127. package/dist/utils/hash.js.map +1 -0
  128. package/dist/utils/logger.d.ts +9 -0
  129. package/dist/utils/logger.d.ts.map +1 -0
  130. package/dist/utils/logger.js +31 -0
  131. package/dist/utils/logger.js.map +1 -0
  132. package/dist/utils/wait.d.ts +21 -0
  133. package/dist/utils/wait.d.ts.map +1 -0
  134. package/dist/utils/wait.js +55 -0
  135. package/dist/utils/wait.js.map +1 -0
  136. 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":"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"}
@@ -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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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ParsedAXNode } from "./accessibility-extractor.js";
2
+ import type { DOMPathSignature } from "../types/element-id.js";
3
+ export declare function computeDOMPathSignature(node: ParsedAXNode): DOMPathSignature;
4
+ //# sourceMappingURL=dom-path.d.ts.map
@@ -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"}