refactor: reorganize imports and improve code formatting across multiple files

This commit is contained in:
ropzislaw
2026-01-21 01:22:04 +08:00
parent 18eb724801
commit 95e21b37cb
13 changed files with 162 additions and 144 deletions

View File

@@ -3,12 +3,6 @@
* Safe to import from any context (background, content, React components)
*/
// Re-export storage adapter (no React dependency)
export {
ChromeStorageAdapter,
chromeStorageAdapter,
} from "@aipexstudio/browser-runtime";
// Re-export types only (no runtime React dependency)
export type {
ChatbotEventHandlers,
@@ -20,6 +14,11 @@ export type {
UseChatOptions,
UseChatReturn,
} from "@aipexstudio/aipex-react";
// Re-export storage adapter (no React dependency)
export {
ChromeStorageAdapter,
chromeStorageAdapter,
} from "@aipexstudio/browser-runtime";
/**
* React hooks should be imported directly from their sources:

View File

@@ -6,9 +6,9 @@
import type { AppSettings } from "@aipexstudio/aipex-core";
import {
type AutomationMode,
STORAGE_KEYS,
aisdk,
SessionStorage,
STORAGE_KEYS,
validateAutomationMode,
} from "@aipexstudio/aipex-core";
import { SYSTEM_PROMPT } from "@aipexstudio/aipex-react/components/chatbot/constants";
@@ -62,16 +62,21 @@ export function useBrowserContextProviders() {
* Filter tools based on automation mode
* In background mode, filter out computer and screenshot-related tools
*/
function filterToolsByMode(tools: typeof allBrowserTools, mode: AutomationMode) {
function filterToolsByMode(
tools: typeof allBrowserTools,
mode: AutomationMode,
) {
// In background mode, filter out computer and screenshot-related tools
if (mode === 'background') {
return tools.filter(tool => {
if (mode === "background") {
return tools.filter((tool) => {
const toolName = tool.name.toLowerCase();
// Filter out computer tool and all screenshot-related tools
return toolName !== 'computer' &&
!toolName.includes('screenshot') &&
!toolName.includes('take_screenshot') &&
!toolName.includes('capture_screenshot');
return (
toolName !== "computer" &&
!toolName.includes("screenshot") &&
!toolName.includes("take_screenshot") &&
!toolName.includes("capture_screenshot")
);
});
}
// In focus mode, include all tools
@@ -83,8 +88,11 @@ function filterToolsByMode(tools: typeof allBrowserTools, mode: AutomationMode)
* In background mode, visual tools (computer, screenshot) are excluded
*/
export function useBrowserTools() {
const [automationModeRaw] = useStorage<string>(STORAGE_KEYS.AUTOMATION_MODE, "focus");
const [automationModeRaw] = useStorage<string>(
STORAGE_KEYS.AUTOMATION_MODE,
"focus",
);
const automationMode: AutomationMode = useMemo(
() => validateAutomationMode(automationModeRaw),
[automationModeRaw],

View File

@@ -1,12 +1,12 @@
/**
* DOM Element Handle
*
*
* Provides ElementHandle/Locator interface using DOM-based operations
* No CDP/debugger required - suitable for background mode
*/
import type { ElementHandle, Locator, TextSnapshotNode } from "./types";
import { DomLocator } from "./dom-locator";
import type { ElementHandle, Locator, TextSnapshotNode } from "./types";
/**
* DOM-based Locator implementation
@@ -84,10 +84,7 @@ class DomLocatorImpl implements Locator {
export class DomElementHandle implements ElementHandle {
private locator: DomLocatorImpl;
constructor(
private tabId: number,
private node: TextSnapshotNode,
) {
constructor(tabId: number, node: TextSnapshotNode) {
this.locator = new DomLocatorImpl(tabId, node.id);
}

View File

@@ -1,6 +1,6 @@
/**
* DOM Locator - Pure DOM-based element operations
*
*
* Uses chrome.scripting.executeScript to interact with elements by UID
* No CDP/debugger required - suitable for background mode
*/
@@ -265,7 +265,7 @@ function runDomAction(payload: DomActionPayload): DomActionResponse<any> {
if (monacoEditor && typeof monacoEditor.getValue === "function") {
return { success: true, data: monacoEditor.getValue() };
}
} catch (e) {
} catch (_e) {
// Ignore
}
@@ -275,7 +275,7 @@ function runDomAction(payload: DomActionPayload): DomActionResponse<any> {
if (codeMirror && typeof codeMirror.getValue === "function") {
return { success: true, data: codeMirror.getValue() };
}
} catch (e) {
} catch (_e) {
// Ignore
}
@@ -285,7 +285,7 @@ function runDomAction(payload: DomActionPayload): DomActionResponse<any> {
if (aceEditor && typeof aceEditor.getValue === "function") {
return { success: true, data: aceEditor.getValue() };
}
} catch (e) {
} catch (_e) {
// Ignore
}

View File

@@ -8,16 +8,10 @@
* The implementation is chosen based on the automation mode setting.
*/
import type { TextSnapshotNode } from "./types";
import { snapshotManager } from "./snapshot-manager";
import { getAutomationMode } from "../runtime/automation-mode";
import {
hasGlobPatterns,
parseSearchQuery,
searchSnapshotText,
type SearchOptions,
SKIP_ROLES,
} from "./query";
import { type SearchOptions, SKIP_ROLES, searchSnapshotText } from "./query";
import { snapshotManager } from "./snapshot-manager";
import type { TextSnapshotNode } from "./types";
/**
* DOM snapshot node structure from @aipexstudio/dom-snapshot
@@ -441,11 +435,7 @@ function formatNode(
const shouldInclude = shouldIncludeInOutput(node);
const attributes = shouldInclude ? getNodeAttributes(node) : [node.role];
// marker: '*' = exact focused node; '→' = ancestor in focus path
const marker = node.focused
? "*"
: focusAncestorSet.has(node.id)
? "→"
: " ";
const marker = node.focused ? "*" : focusAncestorSet.has(node.id) ? "→" : " ";
let result = `${" ".repeat(depth * 1) + marker + attributes.join(" ")}\n`;
// recursively format child nodes

View File

@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
import { ChromeStorageAdapter } from "../storage/storage-adapter.js";
/**
* React hook for Chrome storage (similar to @plasmohq/storage/hook)
*
*
* @throws {Error} If React is not properly loaded (useState is null/undefined)
*/
export function useStorage(key, defaultValue) {
@@ -10,13 +10,13 @@ export function useStorage(key, defaultValue) {
if (!useState || !useEffect || !useMemo) {
throw new Error(
"[useStorage] React hooks are not available. This usually means:\n" +
"1. React is being loaded multiple times (check for duplicate React instances)\n" +
"2. This hook is being imported in a non-React context (e.g., background script)\n" +
"3. Vite/bundler config is externalizing React incorrectly\n" +
"Fix: Ensure 'react' and 'react-dom' are deduped in vite.config.ts"
"1. React is being loaded multiple times (check for duplicate React instances)\n" +
"2. This hook is being imported in a non-React context (e.g., background script)\n" +
"3. Vite/bundler config is externalizing React incorrectly\n" +
"Fix: Ensure 'react' and 'react-dom' are deduped in vite.config.ts",
);
}
const [value, setValue] = useState(defaultValue);
const [isLoading, setIsLoading] = useState(true);
const storage = useMemo(() => new ChromeStorageAdapter(), []);

View File

@@ -3,7 +3,7 @@ import { ChromeStorageAdapter } from "../storage/storage-adapter.js";
/**
* React hook for Chrome storage (similar to @plasmohq/storage/hook)
*
*
* @throws {Error} If React is not properly loaded (useState is null/undefined)
*/
export function useStorage<T = unknown>(
@@ -14,13 +14,13 @@ export function useStorage<T = unknown>(
if (!useState || !useEffect || !useMemo) {
throw new Error(
"[useStorage] React hooks are not available. This usually means:\n" +
"1. React is being loaded multiple times (check for duplicate React instances)\n" +
"2. This hook is being imported in a non-React context (e.g., background script)\n" +
"3. Vite/bundler config is externalizing React incorrectly\n" +
"Fix: Ensure 'react' and 'react-dom' are deduped in vite.config.ts"
"1. React is being loaded multiple times (check for duplicate React instances)\n" +
"2. This hook is being imported in a non-React context (e.g., background script)\n" +
"3. Vite/bundler config is externalizing React incorrectly\n" +
"Fix: Ensure 'react' and 'react-dom' are deduped in vite.config.ts",
);
}
const [value, setValue] = useState<T | undefined>(defaultValue);
const [isLoading, setIsLoading] = useState(true);

View File

@@ -1,9 +1,6 @@
import { tool } from "@aipexstudio/aipex-core";
import { z } from "zod";
import {
type ElementHandle,
SmartElementHandle,
} from "../automation";
import { type ElementHandle, SmartElementHandle } from "../automation";
import { DomElementHandle } from "../automation/dom-element-handle";
import * as snapshotProvider from "../automation/snapshot-provider";
import {

View File

@@ -1,3 +1,8 @@
import type { CollectorOptions, SerializedDomSnapshot } from "./types.js";
export declare function collectDomSnapshot(rootDocument?: Document, options?: Partial<CollectorOptions>): SerializedDomSnapshot;
export declare function collectDomSnapshotInPage(options?: Partial<CollectorOptions>): SerializedDomSnapshot;
export declare function collectDomSnapshot(
rootDocument?: Document,
options?: Partial<CollectorOptions>,
): SerializedDomSnapshot;
export declare function collectDomSnapshotInPage(
options?: Partial<CollectorOptions>,
): SerializedDomSnapshot;

View File

@@ -1,3 +1,5 @@
import type { SerializedDomSnapshot, TextSnapshot } from "./types.js";
export declare function buildTextSnapshot(source: SerializedDomSnapshot): TextSnapshot;
export declare function buildTextSnapshot(
source: SerializedDomSnapshot,
): TextSnapshot;
export declare function formatSnapshot(snapshot: TextSnapshot): string;

View File

@@ -9,27 +9,36 @@ export declare const SKIP_ROLES: string[];
* Search options for snapshot text queries
*/
export interface SearchOptions {
contextLevels?: number;
caseSensitive?: boolean;
useGlob?: boolean;
contextLevels?: number;
caseSensitive?: boolean;
useGlob?: boolean;
}
/**
* Search result containing matched lines and context
*/
export interface SearchResult {
matchedLines: number[];
contextLines: number[];
totalMatches: number;
matchedLines: number[];
contextLines: number[];
totalMatches: number;
}
/**
* Main search entry point
* Searches snapshot text and returns matched lines with surrounding context
*/
export declare function searchSnapshotText(snapshotText: string, query: string, options?: SearchOptions): SearchResult;
export declare function searchSnapshotText(
snapshotText: string,
query: string,
options?: SearchOptions,
): SearchResult;
/**
* Search snapshot and format results with context
*/
export declare function searchAndFormat(snapshot: SerializedDomSnapshot, query: string, contextLevels?: number, options?: Partial<SearchOptions>): string | null;
export declare function searchAndFormat(
snapshot: SerializedDomSnapshot,
query: string,
contextLevels?: number,
options?: Partial<SearchOptions>,
): string | null;
/**
* Parse search query string with "|" separator
* Example: "登录 | Login | Sign In" -> ["登录", "Login", "Sign In"]

View File

@@ -7,7 +7,10 @@ import type { TextSnapshotNode } from "./types.js";
/**
* Check if a node should be included in output with full attributes
*/
export declare function shouldIncludeInOutput(node: TextSnapshotNode, skipRoles?: string[]): boolean;
export declare function shouldIncludeInOutput(
node: TextSnapshotNode,
skipRoles?: string[],
): boolean;
/**
* Get node attributes for formatting
*/
@@ -15,8 +18,16 @@ export declare function getNodeAttributes(node: TextSnapshotNode): string[];
/**
* Format a node recursively into text representation
*/
export declare function formatNode(node: TextSnapshotNode, depth: number, focusAncestorSet: Set<string>, skipRoles?: string[]): string;
export declare function formatNode(
node: TextSnapshotNode,
depth: number,
focusAncestorSet: Set<string>,
skipRoles?: string[],
): string;
/**
* Build focus ancestor set for highlighting focus path
*/
export declare function buildFocusAncestorSet(root: TextSnapshotNode, idToNode: Map<string, TextSnapshotNode>): Set<string>;
export declare function buildFocusAncestorSet(
root: TextSnapshotNode,
idToNode: Map<string, TextSnapshotNode>,
): Set<string>;

View File

@@ -1,88 +1,88 @@
export interface DomSnapshotNode {
id: string;
role: string;
name?: string;
value?: string;
description?: string;
children: DomSnapshotNode[];
tagName?: string;
checked?: boolean | "mixed";
pressed?: boolean | "mixed";
disabled?: boolean;
focused?: boolean;
selected?: boolean;
expanded?: boolean;
placeholder?: string;
href?: string;
title?: string;
textContent?: string;
inputType?: string;
id: string;
role: string;
name?: string;
value?: string;
description?: string;
children: DomSnapshotNode[];
tagName?: string;
checked?: boolean | "mixed";
pressed?: boolean | "mixed";
disabled?: boolean;
focused?: boolean;
selected?: boolean;
expanded?: boolean;
placeholder?: string;
href?: string;
title?: string;
textContent?: string;
inputType?: string;
}
export interface DomSnapshotFlatMap {
[uid: string]: DomSnapshotNode;
[uid: string]: DomSnapshotNode;
}
export interface DomSnapshotResult {
root: DomSnapshotNode;
idToNode: DomSnapshotFlatMap;
totalNodes: number;
timestamp: number;
root: DomSnapshotNode;
idToNode: DomSnapshotFlatMap;
totalNodes: number;
timestamp: number;
}
export interface CollectorOptions {
/**
* Maximum text length stored for StaticText nodes. Defaults to 160.
*/
maxTextLength: number;
/**
* Should we include invisible elements (display:none / hidden). Defaults to false.
*/
includeHidden: boolean;
/**
* Whether to capture raw text nodes as StaticText entries. Defaults to true.
*/
captureTextNodes: boolean;
/**
* Maximum text length stored for StaticText nodes. Defaults to 160.
*/
maxTextLength: number;
/**
* Should we include invisible elements (display:none / hidden). Defaults to false.
*/
includeHidden: boolean;
/**
* Whether to capture raw text nodes as StaticText entries. Defaults to true.
*/
captureTextNodes: boolean;
}
export interface SerializedDomSnapshot extends DomSnapshotResult {
/**
* Additional metadata to help debug or visualize the snapshot.
*/
metadata: {
title: string;
url: string;
collectedAt: string;
options: Partial<CollectorOptions>;
};
/**
* Additional metadata to help debug or visualize the snapshot.
*/
metadata: {
title: string;
url: string;
collectedAt: string;
options: Partial<CollectorOptions>;
};
}
export interface TextSnapshotNode {
id: string;
role: string;
name?: string;
value?: string;
description?: string;
children: TextSnapshotNode[];
backendDOMNodeId?: number;
tagName?: string;
focused?: boolean;
modal?: boolean;
keyshortcuts?: string;
roledescription?: string;
valuetext?: string;
disabled?: boolean;
expanded?: boolean;
selected?: boolean;
checked?: boolean | "mixed";
pressed?: boolean | "mixed";
level?: number;
valuemin?: number;
valuemax?: number;
autocomplete?: string;
haspopup?: string;
invalid?: string;
orientation?: string;
readonly?: boolean;
required?: boolean;
elementHandle?: () => Promise<Element>;
id: string;
role: string;
name?: string;
value?: string;
description?: string;
children: TextSnapshotNode[];
backendDOMNodeId?: number;
tagName?: string;
focused?: boolean;
modal?: boolean;
keyshortcuts?: string;
roledescription?: string;
valuetext?: string;
disabled?: boolean;
expanded?: boolean;
selected?: boolean;
checked?: boolean | "mixed";
pressed?: boolean | "mixed";
level?: number;
valuemin?: number;
valuemax?: number;
autocomplete?: string;
haspopup?: string;
invalid?: string;
orientation?: string;
readonly?: boolean;
required?: boolean;
elementHandle?: () => Promise<Element>;
}
export interface TextSnapshot {
root: TextSnapshotNode;
idToNode: Map<string, TextSnapshotNode>;
root: TextSnapshotNode;
idToNode: Map<string, TextSnapshotNode>;
}