#
tokens: 44140/50000 10/120 files (page 3/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 8. Use http://codebase.md/hangwin/mcp-chrome?page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   └── workflows
│       └── build-release.yml
├── .gitignore
├── .husky
│   ├── commit-msg
│   └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── .vscode
│   └── extensions.json
├── app
│   ├── chrome-extension
│   │   ├── _locales
│   │   │   ├── de
│   │   │   │   └── messages.json
│   │   │   ├── en
│   │   │   │   └── messages.json
│   │   │   ├── ja
│   │   │   │   └── messages.json
│   │   │   ├── ko
│   │   │   │   └── messages.json
│   │   │   ├── zh_CN
│   │   │   │   └── messages.json
│   │   │   └── zh_TW
│   │   │       └── messages.json
│   │   ├── .env.example
│   │   ├── assets
│   │   │   └── vue.svg
│   │   ├── common
│   │   │   ├── constants.ts
│   │   │   ├── message-types.ts
│   │   │   └── tool-handler.ts
│   │   ├── entrypoints
│   │   │   ├── background
│   │   │   │   ├── index.ts
│   │   │   │   ├── native-host.ts
│   │   │   │   ├── semantic-similarity.ts
│   │   │   │   ├── storage-manager.ts
│   │   │   │   └── tools
│   │   │   │       ├── base-browser.ts
│   │   │   │       ├── browser
│   │   │   │       │   ├── bookmark.ts
│   │   │   │       │   ├── common.ts
│   │   │   │       │   ├── console.ts
│   │   │   │       │   ├── file-upload.ts
│   │   │   │       │   ├── history.ts
│   │   │   │       │   ├── index.ts
│   │   │   │       │   ├── inject-script.ts
│   │   │   │       │   ├── interaction.ts
│   │   │   │       │   ├── keyboard.ts
│   │   │   │       │   ├── network-capture-debugger.ts
│   │   │   │       │   ├── network-capture-web-request.ts
│   │   │   │       │   ├── network-request.ts
│   │   │   │       │   ├── screenshot.ts
│   │   │   │       │   ├── vector-search.ts
│   │   │   │       │   ├── web-fetcher.ts
│   │   │   │       │   └── window.ts
│   │   │   │       └── index.ts
│   │   │   ├── content.ts
│   │   │   ├── offscreen
│   │   │   │   ├── index.html
│   │   │   │   └── main.ts
│   │   │   └── popup
│   │   │       ├── App.vue
│   │   │       ├── components
│   │   │       │   ├── ConfirmDialog.vue
│   │   │       │   ├── icons
│   │   │       │   │   ├── BoltIcon.vue
│   │   │       │   │   ├── CheckIcon.vue
│   │   │       │   │   ├── DatabaseIcon.vue
│   │   │       │   │   ├── DocumentIcon.vue
│   │   │       │   │   ├── index.ts
│   │   │       │   │   ├── TabIcon.vue
│   │   │       │   │   ├── TrashIcon.vue
│   │   │       │   │   └── VectorIcon.vue
│   │   │       │   ├── ModelCacheManagement.vue
│   │   │       │   └── ProgressIndicator.vue
│   │   │       ├── index.html
│   │   │       ├── main.ts
│   │   │       └── style.css
│   │   ├── eslint.config.js
│   │   ├── inject-scripts
│   │   │   ├── click-helper.js
│   │   │   ├── fill-helper.js
│   │   │   ├── inject-bridge.js
│   │   │   ├── interactive-elements-helper.js
│   │   │   ├── keyboard-helper.js
│   │   │   ├── network-helper.js
│   │   │   ├── screenshot-helper.js
│   │   │   └── web-fetcher-helper.js
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── public
│   │   │   ├── icon
│   │   │   │   ├── 128.png
│   │   │   │   ├── 16.png
│   │   │   │   ├── 32.png
│   │   │   │   ├── 48.png
│   │   │   │   └── 96.png
│   │   │   ├── libs
│   │   │   │   └── ort.min.js
│   │   │   └── wxt.svg
│   │   ├── README.md
│   │   ├── tsconfig.json
│   │   ├── utils
│   │   │   ├── content-indexer.ts
│   │   │   ├── i18n.ts
│   │   │   ├── image-utils.ts
│   │   │   ├── lru-cache.ts
│   │   │   ├── model-cache-manager.ts
│   │   │   ├── offscreen-manager.ts
│   │   │   ├── semantic-similarity-engine.ts
│   │   │   ├── simd-math-engine.ts
│   │   │   ├── text-chunker.ts
│   │   │   └── vector-database.ts
│   │   ├── workers
│   │   │   ├── ort-wasm-simd-threaded.jsep.mjs
│   │   │   ├── ort-wasm-simd-threaded.jsep.wasm
│   │   │   ├── ort-wasm-simd-threaded.mjs
│   │   │   ├── ort-wasm-simd-threaded.wasm
│   │   │   ├── simd_math_bg.wasm
│   │   │   ├── simd_math.js
│   │   │   └── similarity.worker.js
│   │   └── wxt.config.ts
│   └── native-server
│       ├── debug.sh
│       ├── install.md
│       ├── jest.config.js
│       ├── package.json
│       ├── README.md
│       ├── src
│       │   ├── cli.ts
│       │   ├── constant
│       │   │   └── index.ts
│       │   ├── file-handler.ts
│       │   ├── index.ts
│       │   ├── mcp
│       │   │   ├── mcp-server-stdio.ts
│       │   │   ├── mcp-server.ts
│       │   │   ├── register-tools.ts
│       │   │   └── stdio-config.json
│       │   ├── native-messaging-host.ts
│       │   ├── scripts
│       │   │   ├── browser-config.ts
│       │   │   ├── build.ts
│       │   │   ├── constant.ts
│       │   │   ├── postinstall.ts
│       │   │   ├── register-dev.ts
│       │   │   ├── register.ts
│       │   │   ├── run_host.bat
│       │   │   ├── run_host.sh
│       │   │   └── utils.ts
│       │   ├── server
│       │   │   ├── index.ts
│       │   │   └── server.test.ts
│       │   └── util
│       │       └── logger.ts
│       └── tsconfig.json
├── commitlint.config.cjs
├── docs
│   ├── ARCHITECTURE_zh.md
│   ├── ARCHITECTURE.md
│   ├── CHANGELOG.md
│   ├── CONTRIBUTING_zh.md
│   ├── CONTRIBUTING.md
│   ├── TOOLS_zh.md
│   ├── TOOLS.md
│   ├── TROUBLESHOOTING_zh.md
│   ├── TROUBLESHOOTING.md
│   └── WINDOWS_INSTALL_zh.md
├── eslint.config.js
├── LICENSE
├── package.json
├── packages
│   ├── shared
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── constants.ts
│   │   │   ├── index.ts
│   │   │   ├── tools.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── wasm-simd
│       ├── .gitignore
│       ├── BUILD.md
│       ├── Cargo.toml
│       ├── package.json
│       ├── README.md
│       └── src
│           └── lib.rs
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── prompt
│   ├── content-analize.md
│   ├── excalidraw-prompt.md
│   └── modify-web.md
├── README_zh.md
├── README.md
├── releases
│   ├── chrome-extension
│   │   └── latest
│   │       └── chrome-mcp-server-lastest.zip
│   └── README.md
└── test-inject-script.js
```

# Files

--------------------------------------------------------------------------------
/app/chrome-extension/_locales/de/messages.json:
--------------------------------------------------------------------------------

```json
{
  "extensionName": {
    "message": "chrome-mcp-server",
    "description": "Erweiterungsname"
  },
  "extensionDescription": {
    "message": "Stellt Browser-Funktionen mit Ihrem eigenen Chrome zur Verfügung",
    "description": "Erweiterungsbeschreibung"
  },
  "nativeServerConfigLabel": {
    "message": "Native Server-Konfiguration",
    "description": "Hauptabschnittstitel für Native Server-Einstellungen"
  },
  "semanticEngineLabel": {
    "message": "Semantische Engine",
    "description": "Hauptabschnittstitel für semantische Engine"
  },
  "embeddingModelLabel": {
    "message": "Embedding-Modell",
    "description": "Hauptabschnittstitel für Modellauswahl"
  },
  "indexDataManagementLabel": {
    "message": "Index-Datenverwaltung",
    "description": "Hauptabschnittstitel für Datenverwaltung"
  },
  "modelCacheManagementLabel": {
    "message": "Modell-Cache-Verwaltung",
    "description": "Hauptabschnittstitel für Cache-Verwaltung"
  },
  "statusLabel": {
    "message": "Status",
    "description": "Allgemeines Statuslabel"
  },
  "runningStatusLabel": {
    "message": "Betriebsstatus",
    "description": "Server-Betriebsstatuslabel"
  },
  "connectionStatusLabel": {
    "message": "Verbindungsstatus",
    "description": "Verbindungsstatuslabel"
  },
  "lastUpdatedLabel": {
    "message": "Zuletzt aktualisiert:",
    "description": "Zeitstempel der letzten Aktualisierung"
  },
  "connectButton": {
    "message": "Verbinden",
    "description": "Verbinden-Schaltflächentext"
  },
  "disconnectButton": {
    "message": "Trennen",
    "description": "Trennen-Schaltflächentext"
  },
  "connectingStatus": {
    "message": "Verbindung wird hergestellt...",
    "description": "Verbindungsstatusmeldung"
  },
  "connectedStatus": {
    "message": "Verbunden",
    "description": "Verbunden-Statusmeldung"
  },
  "disconnectedStatus": {
    "message": "Getrennt",
    "description": "Getrennt-Statusmeldung"
  },
  "detectingStatus": {
    "message": "Erkennung läuft...",
    "description": "Erkennungsstatusmeldung"
  },
  "serviceRunningStatus": {
    "message": "Service läuft (Port: $PORT$)",
    "description": "Service läuft mit Portnummer",
    "placeholders": {
      "port": {
        "content": "$1",
        "example": "12306"
      }
    }
  },
  "serviceNotConnectedStatus": {
    "message": "Service nicht verbunden",
    "description": "Service nicht verbunden Status"
  },
  "connectedServiceNotStartedStatus": {
    "message": "Verbunden, Service nicht gestartet",
    "description": "Verbunden aber Service nicht gestartet Status"
  },
  "mcpServerConfigLabel": {
    "message": "MCP Server-Konfiguration",
    "description": "MCP Server-Konfigurationsabschnittslabel"
  },
  "connectionPortLabel": {
    "message": "Verbindungsport",
    "description": "Verbindungsport-Eingabelabel"
  },
  "refreshStatusButton": {
    "message": "Status aktualisieren",
    "description": "Status aktualisieren Schaltflächen-Tooltip"
  },
  "copyConfigButton": {
    "message": "Konfiguration kopieren",
    "description": "Konfiguration kopieren Schaltflächentext"
  },
  "retryButton": {
    "message": "Wiederholen",
    "description": "Wiederholen-Schaltflächentext"
  },
  "cancelButton": {
    "message": "Abbrechen",
    "description": "Abbrechen-Schaltflächentext"
  },
  "confirmButton": {
    "message": "Bestätigen",
    "description": "Bestätigen-Schaltflächentext"
  },
  "saveButton": {
    "message": "Speichern",
    "description": "Speichern-Schaltflächentext"
  },
  "closeButton": {
    "message": "Schließen",
    "description": "Schließen-Schaltflächentext"
  },
  "resetButton": {
    "message": "Zurücksetzen",
    "description": "Zurücksetzen-Schaltflächentext"
  },
  "initializingStatus": {
    "message": "Initialisierung...",
    "description": "Initialisierung-Fortschrittsmeldung"
  },
  "processingStatus": {
    "message": "Verarbeitung...",
    "description": "Verarbeitung-Fortschrittsmeldung"
  },
  "loadingStatus": {
    "message": "Wird geladen...",
    "description": "Ladefortschrittsmeldung"
  },
  "clearingStatus": {
    "message": "Wird geleert...",
    "description": "Leerungsfortschrittsmeldung"
  },
  "cleaningStatus": {
    "message": "Wird bereinigt...",
    "description": "Bereinigungsfortschrittsmeldung"
  },
  "downloadingStatus": {
    "message": "Wird heruntergeladen...",
    "description": "Download-Fortschrittsmeldung"
  },
  "semanticEngineReadyStatus": {
    "message": "Semantische Engine bereit",
    "description": "Semantische Engine bereit Status"
  },
  "semanticEngineInitializingStatus": {
    "message": "Semantische Engine wird initialisiert...",
    "description": "Semantische Engine Initialisierungsstatus"
  },
  "semanticEngineInitFailedStatus": {
    "message": "Initialisierung der semantischen Engine fehlgeschlagen",
    "description": "Semantische Engine Initialisierung fehlgeschlagen Status"
  },
  "semanticEngineNotInitStatus": {
    "message": "Semantische Engine nicht initialisiert",
    "description": "Semantische Engine nicht initialisiert Status"
  },
  "initSemanticEngineButton": {
    "message": "Semantische Engine initialisieren",
    "description": "Semantische Engine initialisieren Schaltflächentext"
  },
  "reinitializeButton": {
    "message": "Neu initialisieren",
    "description": "Neu initialisieren Schaltflächentext"
  },
  "downloadingModelStatus": {
    "message": "Modell wird heruntergeladen... $PROGRESS$%",
    "description": "Modell-Download-Fortschritt mit Prozentsatz",
    "placeholders": {
      "progress": {
        "content": "$1",
        "example": "50"
      }
    }
  },
  "switchingModelStatus": {
    "message": "Modell wird gewechselt...",
    "description": "Modellwechsel-Fortschrittsmeldung"
  },
  "modelLoadedStatus": {
    "message": "Modell geladen",
    "description": "Modell erfolgreich geladen Status"
  },
  "modelFailedStatus": {
    "message": "Modell konnte nicht geladen werden",
    "description": "Modell-Ladefehler Status"
  },
  "lightweightModelDescription": {
    "message": "Leichtgewichtiges mehrsprachiges Modell",
    "description": "Beschreibung für leichtgewichtige Modelloption"
  },
  "betterThanSmallDescription": {
    "message": "Etwas größer als e5-small, aber bessere Leistung",
    "description": "Beschreibung für mittlere Modelloption"
  },
  "multilingualModelDescription": {
    "message": "Mehrsprachiges semantisches Modell",
    "description": "Beschreibung für mehrsprachige Modelloption"
  },
  "fastPerformance": {
    "message": "Schnell",
    "description": "Schnelle Leistungsanzeige"
  },
  "balancedPerformance": {
    "message": "Ausgewogen",
    "description": "Ausgewogene Leistungsanzeige"
  },
  "accuratePerformance": {
    "message": "Genau",
    "description": "Genaue Leistungsanzeige"
  },
  "networkErrorMessage": {
    "message": "Netzwerkverbindungsfehler, bitte Netzwerk prüfen und erneut versuchen",
    "description": "Netzwerkverbindungsfehlermeldung"
  },
  "modelCorruptedErrorMessage": {
    "message": "Modelldatei beschädigt oder unvollständig, bitte Download wiederholen",
    "description": "Modell-Beschädigungsfehlermeldung"
  },
  "unknownErrorMessage": {
    "message": "Unbekannter Fehler, bitte prüfen Sie, ob Ihr Netzwerk auf HuggingFace zugreifen kann",
    "description": "Unbekannte Fehler-Rückfallmeldung"
  },
  "permissionDeniedErrorMessage": {
    "message": "Zugriff verweigert",
    "description": "Zugriff verweigert Fehlermeldung"
  },
  "timeoutErrorMessage": {
    "message": "Zeitüberschreitung",
    "description": "Zeitüberschreitungsfehlermeldung"
  },
  "indexedPagesLabel": {
    "message": "Indizierte Seiten",
    "description": "Anzahl indizierter Seiten Label"
  },
  "indexSizeLabel": {
    "message": "Indexgröße",
    "description": "Indexgröße Label"
  },
  "activeTabsLabel": {
    "message": "Aktive Tabs",
    "description": "Anzahl aktiver Tabs Label"
  },
  "vectorDocumentsLabel": {
    "message": "Vektordokumente",
    "description": "Anzahl Vektordokumente Label"
  },
  "cacheSizeLabel": {
    "message": "Cache-Größe",
    "description": "Cache-Größe Label"
  },
  "cacheEntriesLabel": {
    "message": "Cache-Einträge",
    "description": "Anzahl Cache-Einträge Label"
  },
  "clearAllDataButton": {
    "message": "Alle Daten löschen",
    "description": "Alle Daten löschen Schaltflächentext"
  },
  "clearAllCacheButton": {
    "message": "Gesamten Cache löschen",
    "description": "Gesamten Cache löschen Schaltflächentext"
  },
  "cleanExpiredCacheButton": {
    "message": "Abgelaufenen Cache bereinigen",
    "description": "Abgelaufenen Cache bereinigen Schaltflächentext"
  },
  "exportDataButton": {
    "message": "Daten exportieren",
    "description": "Daten exportieren Schaltflächentext"
  },
  "importDataButton": {
    "message": "Daten importieren",
    "description": "Daten importieren Schaltflächentext"
  },
  "confirmClearDataTitle": {
    "message": "Datenlöschung bestätigen",
    "description": "Datenlöschung bestätigen Dialogtitel"
  },
  "settingsTitle": {
    "message": "Einstellungen",
    "description": "Einstellungen Dialogtitel"
  },
  "aboutTitle": {
    "message": "Über",
    "description": "Über Dialogtitel"
  },
  "helpTitle": {
    "message": "Hilfe",
    "description": "Hilfe Dialogtitel"
  },
  "clearDataWarningMessage": {
    "message": "Diese Aktion löscht alle indizierten Webseiteninhalte und Vektordaten, einschließlich:",
    "description": "Datenlöschung Warnmeldung"
  },
  "clearDataList1": {
    "message": "Alle Webseitentextinhaltsindizes",
    "description": "Erster Punkt in Datenlöschungsliste"
  },
  "clearDataList2": {
    "message": "Vektor-Embedding-Daten",
    "description": "Zweiter Punkt in Datenlöschungsliste"
  },
  "clearDataList3": {
    "message": "Suchverlauf und Cache",
    "description": "Dritter Punkt in Datenlöschungsliste"
  },
  "clearDataIrreversibleWarning": {
    "message": "Diese Aktion ist unwiderruflich! Nach dem Löschen müssen Sie Webseiten erneut durchsuchen, um den Index neu aufzubauen.",
    "description": "Unwiderrufliche Aktion Warnung"
  },
  "confirmClearButton": {
    "message": "Löschung bestätigen",
    "description": "Löschung bestätigen Aktionsschaltfläche"
  },
  "cacheDetailsLabel": {
    "message": "Cache-Details",
    "description": "Cache-Details Abschnittslabel"
  },
  "noCacheDataMessage": {
    "message": "Keine Cache-Daten vorhanden",
    "description": "Keine Cache-Daten verfügbar Meldung"
  },
  "loadingCacheInfoStatus": {
    "message": "Cache-Informationen werden geladen...",
    "description": "Cache-Informationen laden Status"
  },
  "processingCacheStatus": {
    "message": "Cache wird verarbeitet...",
    "description": "Cache verarbeiten Status"
  },
  "expiredLabel": {
    "message": "Abgelaufen",
    "description": "Abgelaufenes Element Label"
  },
  "bookmarksBarLabel": {
    "message": "Lesezeichenleiste",
    "description": "Lesezeichenleiste Ordnername"
  },
  "newTabLabel": {
    "message": "Neuer Tab",
    "description": "Neuer Tab Label"
  },
  "currentPageLabel": {
    "message": "Aktuelle Seite",
    "description": "Aktuelle Seite Label"
  },
  "menuLabel": {
    "message": "Menü",
    "description": "Menü Barrierefreiheitslabel"
  },
  "navigationLabel": {
    "message": "Navigation",
    "description": "Navigation Barrierefreiheitslabel"
  },
  "mainContentLabel": {
    "message": "Hauptinhalt",
    "description": "Hauptinhalt Barrierefreiheitslabel"
  },
  "languageSelectorLabel": {
    "message": "Sprache",
    "description": "Sprachauswahl Label"
  },
  "themeLabel": {
    "message": "Design",
    "description": "Design-Auswahl Label"
  },
  "lightTheme": {
    "message": "Hell",
    "description": "Helles Design Option"
  },
  "darkTheme": {
    "message": "Dunkel",
    "description": "Dunkles Design Option"
  },
  "autoTheme": {
    "message": "Automatisch",
    "description": "Automatisches Design Option"
  },
  "advancedSettingsLabel": {
    "message": "Erweiterte Einstellungen",
    "description": "Erweiterte Einstellungen Abschnittslabel"
  },
  "debugModeLabel": {
    "message": "Debug-Modus",
    "description": "Debug-Modus Umschalter Label"
  },
  "verboseLoggingLabel": {
    "message": "Ausführliche Protokollierung",
    "description": "Ausführliche Protokollierung Umschalter Label"
  },
  "successNotification": {
    "message": "Vorgang erfolgreich abgeschlossen",
    "description": "Allgemeine Erfolgsmeldung"
  },
  "warningNotification": {
    "message": "Warnung: Bitte prüfen Sie vor dem Fortfahren",
    "description": "Allgemeine Warnmeldung"
  },
  "infoNotification": {
    "message": "Information",
    "description": "Allgemeine Informationsmeldung"
  },
  "configCopiedNotification": {
    "message": "Konfiguration in Zwischenablage kopiert",
    "description": "Konfiguration kopiert Erfolgsmeldung"
  },
  "dataClearedNotification": {
    "message": "Daten erfolgreich gelöscht",
    "description": "Daten gelöscht Erfolgsmeldung"
  },
  "bytesUnit": {
    "message": "Bytes",
    "description": "Bytes Einheit"
  },
  "kilobytesUnit": {
    "message": "KB",
    "description": "Kilobytes Einheit"
  },
  "megabytesUnit": {
    "message": "MB",
    "description": "Megabytes Einheit"
  },
  "gigabytesUnit": {
    "message": "GB",
    "description": "Gigabytes Einheit"
  },
  "itemsUnit": {
    "message": "Elemente",
    "description": "Elemente Zähleinheit"
  },
  "pagesUnit": {
    "message": "Seiten",
    "description": "Seiten Zähleinheit"
  }
}
```

--------------------------------------------------------------------------------
/app/chrome-extension/utils/simd-math-engine.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * SIMD-optimized mathematical computation engine
 * Uses WebAssembly + SIMD instructions to accelerate vector calculations
 */

interface SIMDMathWasm {
  free(): void;
  cosine_similarity(vec_a: Float32Array, vec_b: Float32Array): number;
  batch_similarity(vectors: Float32Array, query: Float32Array, vector_dim: number): Float32Array;
  similarity_matrix(
    vectors_a: Float32Array,
    vectors_b: Float32Array,
    vector_dim: number,
  ): Float32Array;
}

interface WasmModule {
  SIMDMath: new () => SIMDMathWasm;
  memory: WebAssembly.Memory;
  default: (module_or_path?: any) => Promise<any>;
}

export class SIMDMathEngine {
  private wasmModule: WasmModule | null = null;
  private simdMath: SIMDMathWasm | null = null;
  private isInitialized = false;
  private isInitializing = false;
  private initPromise: Promise<void> | null = null;

  private alignedBufferPool: Map<number, Float32Array[]> = new Map();
  private maxPoolSize = 5;

  async initialize(): Promise<void> {
    if (this.isInitialized) return;
    if (this.isInitializing && this.initPromise) return this.initPromise;

    this.isInitializing = true;
    this.initPromise = this._doInitialize().finally(() => {
      this.isInitializing = false;
    });

    return this.initPromise;
  }

  private async _doInitialize(): Promise<void> {
    try {
      console.log('SIMDMathEngine: Initializing WebAssembly module...');

      const wasmUrl = chrome.runtime.getURL('workers/simd_math.js');
      const wasmModule = await import(wasmUrl);

      const wasmInstance = await wasmModule.default();

      this.wasmModule = {
        SIMDMath: wasmModule.SIMDMath,
        memory: wasmInstance.memory,
        default: wasmModule.default,
      };

      this.simdMath = new this.wasmModule.SIMDMath();

      this.isInitialized = true;
      console.log('SIMDMathEngine: WebAssembly module initialized successfully');
    } catch (error) {
      console.error('SIMDMathEngine: Failed to initialize WebAssembly module:', error);
      this.isInitialized = false;
      throw error;
    }
  }

  /**
   * Get aligned buffer (16-byte aligned, suitable for SIMD)
   */
  private getAlignedBuffer(size: number): Float32Array {
    if (!this.alignedBufferPool.has(size)) {
      this.alignedBufferPool.set(size, []);
    }

    const pool = this.alignedBufferPool.get(size)!;
    if (pool.length > 0) {
      return pool.pop()!;
    }

    // Create 16-byte aligned buffer
    const buffer = new ArrayBuffer(size * 4 + 15);
    const alignedOffset = (16 - (buffer.byteLength % 16)) % 16;
    return new Float32Array(buffer, alignedOffset, size);
  }

  /**
   * Release aligned buffer back to pool
   */
  private releaseAlignedBuffer(buffer: Float32Array): void {
    const size = buffer.length;
    const pool = this.alignedBufferPool.get(size);
    if (pool && pool.length < this.maxPoolSize) {
      buffer.fill(0); // Clear to zero
      pool.push(buffer);
    }
  }

  /**
   * Check if vector is already aligned
   */
  private isAligned(array: Float32Array): boolean {
    return array.byteOffset % 16 === 0;
  }

  /**
   * Ensure vector alignment, create aligned copy if not aligned
   */
  private ensureAligned(array: Float32Array): { aligned: Float32Array; needsRelease: boolean } {
    if (this.isAligned(array)) {
      return { aligned: array, needsRelease: false };
    }

    const aligned = this.getAlignedBuffer(array.length);
    aligned.set(array);
    return { aligned, needsRelease: true };
  }

  /**
   * SIMD-optimized cosine similarity calculation
   */
  async cosineSimilarity(vecA: Float32Array, vecB: Float32Array): Promise<number> {
    if (!this.isInitialized) {
      await this.initialize();
    }

    if (!this.simdMath) {
      throw new Error('SIMD math engine not initialized');
    }

    // Ensure vector alignment
    const { aligned: alignedA, needsRelease: releaseA } = this.ensureAligned(vecA);
    const { aligned: alignedB, needsRelease: releaseB } = this.ensureAligned(vecB);

    try {
      const result = this.simdMath.cosine_similarity(alignedA, alignedB);
      return result;
    } finally {
      // Release temporary buffers
      if (releaseA) this.releaseAlignedBuffer(alignedA);
      if (releaseB) this.releaseAlignedBuffer(alignedB);
    }
  }

  /**
   * Batch similarity calculation
   */
  async batchSimilarity(vectors: Float32Array[], query: Float32Array): Promise<number[]> {
    if (!this.isInitialized) {
      await this.initialize();
    }

    if (!this.simdMath) {
      throw new Error('SIMD math engine not initialized');
    }

    const vectorDim = query.length;
    const numVectors = vectors.length;

    // Pack all vectors into contiguous memory layout
    const packedVectors = this.getAlignedBuffer(numVectors * vectorDim);
    const { aligned: alignedQuery, needsRelease: releaseQuery } = this.ensureAligned(query);

    try {
      // Copy vector data
      let offset = 0;
      for (const vector of vectors) {
        packedVectors.set(vector, offset);
        offset += vectorDim;
      }

      // Batch calculation
      const results = this.simdMath.batch_similarity(packedVectors, alignedQuery, vectorDim);
      return Array.from(results);
    } finally {
      this.releaseAlignedBuffer(packedVectors);
      if (releaseQuery) this.releaseAlignedBuffer(alignedQuery);
    }
  }

  /**
   * Similarity matrix calculation
   */
  async similarityMatrix(vectorsA: Float32Array[], vectorsB: Float32Array[]): Promise<number[][]> {
    if (!this.isInitialized) {
      await this.initialize();
    }

    if (!this.simdMath || vectorsA.length === 0 || vectorsB.length === 0) {
      return [];
    }

    const vectorDim = vectorsA[0].length;
    const numA = vectorsA.length;
    const numB = vectorsB.length;

    // Pack vectors
    const packedA = this.getAlignedBuffer(numA * vectorDim);
    const packedB = this.getAlignedBuffer(numB * vectorDim);

    try {
      // Copy data
      let offsetA = 0;
      for (const vector of vectorsA) {
        packedA.set(vector, offsetA);
        offsetA += vectorDim;
      }

      let offsetB = 0;
      for (const vector of vectorsB) {
        packedB.set(vector, offsetB);
        offsetB += vectorDim;
      }

      // Calculate matrix
      const flatResults = this.simdMath.similarity_matrix(packedA, packedB, vectorDim);

      // Convert to 2D array
      const matrix: number[][] = [];
      for (let i = 0; i < numA; i++) {
        const row: number[] = [];
        for (let j = 0; j < numB; j++) {
          row.push(flatResults[i * numB + j]);
        }
        matrix.push(row);
      }

      return matrix;
    } finally {
      this.releaseAlignedBuffer(packedA);
      this.releaseAlignedBuffer(packedB);
    }
  }

  /**
   * Check SIMD support
   */
  static async checkSIMDSupport(): Promise<boolean> {
    try {
      console.log('SIMDMathEngine: Checking SIMD support...');

      // Get browser information
      const userAgent = navigator.userAgent;
      const browserInfo = SIMDMathEngine.getBrowserInfo();
      console.log('Browser info:', browserInfo);
      console.log('User Agent:', userAgent);

      // Check WebAssembly basic support
      if (typeof WebAssembly !== 'object') {
        console.log('WebAssembly not supported');
        return false;
      }
      console.log('✅ WebAssembly basic support: OK');

      // Check WebAssembly.validate method
      if (typeof WebAssembly.validate !== 'function') {
        console.log('❌ WebAssembly.validate not available');
        return false;
      }
      console.log('✅ WebAssembly.validate: OK');

      // Test basic WebAssembly module validation
      const basicWasm = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
      const basicValid = WebAssembly.validate(basicWasm);
      console.log('✅ Basic WASM validation:', basicValid);

      // Check WebAssembly SIMD support - using correct SIMD test module
      console.log('Testing SIMD WASM module...');

      // Method 1: Use standard SIMD detection bytecode
      let wasmSIMDSupported = false;
      try {
        // This is a minimal SIMD module containing v128.const instruction
        const simdWasm = new Uint8Array([
          0x00,
          0x61,
          0x73,
          0x6d, // WASM magic
          0x01,
          0x00,
          0x00,
          0x00, // version
          0x01,
          0x05,
          0x01, // type section
          0x60,
          0x00,
          0x01,
          0x7b, // function type: () -> v128
          0x03,
          0x02,
          0x01,
          0x00, // function section
          0x0a,
          0x0a,
          0x01, // code section
          0x08,
          0x00, // function body
          0xfd,
          0x0c, // v128.const
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x00,
          0x0b, // end
        ]);
        wasmSIMDSupported = WebAssembly.validate(simdWasm);
        console.log('Method 1 - Standard SIMD test result:', wasmSIMDSupported);
      } catch (error) {
        console.log('Method 1 failed:', error);
      }

      // Method 2: If method 1 fails, try simpler SIMD instruction
      if (!wasmSIMDSupported) {
        try {
          // Test using i32x4.splat instruction
          const simpleSimdWasm = new Uint8Array([
            0x00,
            0x61,
            0x73,
            0x6d, // WASM magic
            0x01,
            0x00,
            0x00,
            0x00, // version
            0x01,
            0x06,
            0x01, // type section
            0x60,
            0x01,
            0x7f,
            0x01,
            0x7b, // function type: (i32) -> v128
            0x03,
            0x02,
            0x01,
            0x00, // function section
            0x0a,
            0x07,
            0x01, // code section
            0x05,
            0x00, // function body
            0x20,
            0x00, // local.get 0
            0xfd,
            0x0d, // i32x4.splat
            0x0b, // end
          ]);
          wasmSIMDSupported = WebAssembly.validate(simpleSimdWasm);
          console.log('Method 2 - Simple SIMD test result:', wasmSIMDSupported);
        } catch (error) {
          console.log('Method 2 failed:', error);
        }
      }

      // Method 3: If previous methods fail, try detecting specific SIMD features
      if (!wasmSIMDSupported) {
        try {
          // Check if SIMD feature flags are supported
          const featureTest = WebAssembly.validate(
            new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]),
          );

          if (featureTest) {
            // In Chrome, if basic WebAssembly works and version >= 91, SIMD is usually available
            const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
            if (chromeMatch && parseInt(chromeMatch[1]) >= 91) {
              console.log('Method 3 - Chrome version check: SIMD should be available');
              wasmSIMDSupported = true;
            }
          }
        } catch (error) {
          console.log('Method 3 failed:', error);
        }
      }

      // Output final result
      if (!wasmSIMDSupported) {
        console.log('❌ SIMD not supported. Browser requirements:');
        console.log('- Chrome 91+, Firefox 89+, Safari 16.4+, Edge 91+');
        console.log('Your browser should support SIMD. Possible issues:');
        console.log('1. Extension context limitations');
        console.log('2. Security policies');
        console.log('3. Feature flags disabled');
      } else {
        console.log('✅ SIMD supported!');
      }

      return wasmSIMDSupported;
    } catch (error: any) {
      console.error('SIMD support check failed:', error);
      if (error instanceof Error) {
        console.error('Error details:', {
          name: error.name,
          message: error.message,
          stack: error.stack,
        });
      }
      return false;
    }
  }

  /**
   * Get browser information
   */
  static getBrowserInfo(): { name: string; version: string; supported: boolean } {
    const userAgent = navigator.userAgent;
    let browserName = 'Unknown';
    let version = 'Unknown';
    let supported = false;

    // Chrome
    if (userAgent.includes('Chrome/')) {
      browserName = 'Chrome';
      const match = userAgent.match(/Chrome\/(\d+)/);
      if (match) {
        version = match[1];
        supported = parseInt(version) >= 91;
      }
    }
    // Firefox
    else if (userAgent.includes('Firefox/')) {
      browserName = 'Firefox';
      const match = userAgent.match(/Firefox\/(\d+)/);
      if (match) {
        version = match[1];
        supported = parseInt(version) >= 89;
      }
    }
    // Safari
    else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome/')) {
      browserName = 'Safari';
      const match = userAgent.match(/Version\/(\d+\.\d+)/);
      if (match) {
        version = match[1];
        const versionNum = parseFloat(version);
        supported = versionNum >= 16.4;
      }
    }
    // Edge
    else if (userAgent.includes('Edg/')) {
      browserName = 'Edge';
      const match = userAgent.match(/Edg\/(\d+)/);
      if (match) {
        version = match[1];
        supported = parseInt(version) >= 91;
      }
    }

    return { name: browserName, version, supported };
  }

  getStats() {
    return {
      isInitialized: this.isInitialized,
      isInitializing: this.isInitializing,
      bufferPoolStats: Array.from(this.alignedBufferPool.entries()).map(([size, buffers]) => ({
        size,
        pooled: buffers.length,
        maxPoolSize: this.maxPoolSize,
      })),
    };
  }

  dispose(): void {
    if (this.simdMath) {
      try {
        this.simdMath.free();
      } catch (error) {
        console.warn('Failed to free SIMD math instance:', error);
      }
      this.simdMath = null;
    }

    this.alignedBufferPool.clear();
    this.wasmModule = null;
    this.isInitialized = false;
    this.isInitializing = false;
    this.initPromise = null;
  }
}

```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/tools/browser/screenshot.ts:
--------------------------------------------------------------------------------

```typescript
import { createErrorResponse, ToolResult } from '@/common/tool-handler';
import { BaseBrowserToolExecutor } from '../base-browser';
import { TOOL_NAMES } from 'chrome-mcp-shared';
import { TOOL_MESSAGE_TYPES } from '@/common/message-types';
import { TIMEOUTS, ERROR_MESSAGES } from '@/common/constants';
import {
  canvasToDataURL,
  createImageBitmapFromUrl,
  cropAndResizeImage,
  stitchImages,
  compressImage,
} from '../../../../utils/image-utils';

// Screenshot-specific constants
const SCREENSHOT_CONSTANTS = {
  SCROLL_DELAY_MS: 350, // Time to wait after scroll for rendering and lazy loading
  CAPTURE_STITCH_DELAY_MS: 50, // Small delay between captures in a scroll sequence
  MAX_CAPTURE_PARTS: 50, // Maximum number of parts to capture (for infinite scroll pages)
  MAX_CAPTURE_HEIGHT_PX: 50000, // Maximum height in pixels to capture
  PIXEL_TOLERANCE: 1,
  SCRIPT_INIT_DELAY: 100, // Delay for script initialization
} as {
  readonly SCROLL_DELAY_MS: number;
  CAPTURE_STITCH_DELAY_MS: number; // This one is mutable
  readonly MAX_CAPTURE_PARTS: number;
  readonly MAX_CAPTURE_HEIGHT_PX: number;
  readonly PIXEL_TOLERANCE: number;
  readonly SCRIPT_INIT_DELAY: number;
};

SCREENSHOT_CONSTANTS["CAPTURE_STITCH_DELAY_MS"] = Math.max(1000 / chrome.tabs.MAX_CAPTURE_VISIBLE_TAB_CALLS_PER_SECOND - SCREENSHOT_CONSTANTS.SCROLL_DELAY_MS, SCREENSHOT_CONSTANTS.CAPTURE_STITCH_DELAY_MS)

interface ScreenshotToolParams {
  name: string;
  selector?: string;
  width?: number;
  height?: number;
  storeBase64?: boolean;
  fullPage?: boolean;
  savePng?: boolean;
  maxHeight?: number; // Maximum height to capture in pixels (for infinite scroll pages)
}

/**
 * Tool for capturing screenshots of web pages
 */
class ScreenshotTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.SCREENSHOT;

  /**
   * Execute screenshot operation
   */
  async execute(args: ScreenshotToolParams): Promise<ToolResult> {
    const {
      name = 'screenshot',
      selector,
      storeBase64 = false,
      fullPage = false,
      savePng = true,
    } = args;

    console.log(`Starting screenshot with options:`, args);

    // Get current tab
    const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
    if (!tabs[0]) {
      return createErrorResponse(ERROR_MESSAGES.TAB_NOT_FOUND);
    }
    const tab = tabs[0];

    // Check URL restrictions
    if (
      tab.url?.startsWith('chrome://') ||
      tab.url?.startsWith('edge://') ||
      tab.url?.startsWith('https://chrome.google.com/webstore') ||
      tab.url?.startsWith('https://microsoftedge.microsoft.com/')
    ) {
      return createErrorResponse(
        'Cannot capture special browser pages or web store pages due to security restrictions.',
      );
    }

    let finalImageDataUrl: string | undefined;
    const results: any = { base64: null, fileSaved: false };
    let originalScroll = { x: 0, y: 0 };

    try {
      await this.injectContentScript(tab.id!, ['inject-scripts/screenshot-helper.js']);
      // Wait for script initialization
      await new Promise((resolve) => setTimeout(resolve, SCREENSHOT_CONSTANTS.SCRIPT_INIT_DELAY));
      // 1. Prepare page (hide scrollbars, potentially fixed elements)
      await this.sendMessageToTab(tab.id!, {
        action: TOOL_MESSAGE_TYPES.SCREENSHOT_PREPARE_PAGE_FOR_CAPTURE,
        options: { fullPage },
      });

      // Get initial page details, including original scroll position
      const pageDetails = await this.sendMessageToTab(tab.id!, {
        action: TOOL_MESSAGE_TYPES.SCREENSHOT_GET_PAGE_DETAILS,
      });
      originalScroll = { x: pageDetails.currentScrollX, y: pageDetails.currentScrollY };

      if (fullPage) {
        this.logInfo('Capturing full page...');
        finalImageDataUrl = await this._captureFullPage(tab.id!, args, pageDetails);
      } else if (selector) {
        this.logInfo(`Capturing element: ${selector}`);
        finalImageDataUrl = await this._captureElement(tab.id!, args, pageDetails.devicePixelRatio);
      } else {
        // Visible area only
        this.logInfo('Capturing visible area...');
        finalImageDataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, { format: 'png' });
      }

      if (!finalImageDataUrl) {
        throw new Error('Failed to capture image data');
      }

      // 2. Process output
      if (storeBase64 === true) {
        // Compress image for base64 output to reduce size
        const compressed = await compressImage(finalImageDataUrl, {
          scale: 0.7, // Reduce dimensions by 30%
          quality: 0.8, // 80% quality for good balance
          format: 'image/jpeg', // JPEG for better compression
        });

        // Include base64 data in response (without prefix)
        const base64Data = compressed.dataUrl.replace(/^data:image\/[^;]+;base64,/, '');
        results.base64 = base64Data;
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({ base64Data, mimeType: compressed.mimeType }),
            },
          ],
          isError: false,
        };
      }

      if (savePng === true) {
        // Save PNG file to downloads
        this.logInfo('Saving PNG...');
        try {
          // Generate filename
          const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
          const filename = `${name.replace(/[^a-z0-9_-]/gi, '_') || 'screenshot'}_${timestamp}.png`;

          // Use Chrome's download API to save the file
          const downloadId = await chrome.downloads.download({
            url: finalImageDataUrl,
            filename: filename,
            saveAs: false,
          });

          results.downloadId = downloadId;
          results.filename = filename;
          results.fileSaved = true;

          // Try to get the full file path
          try {
            // Wait a moment to ensure download info is updated
            await new Promise((resolve) => setTimeout(resolve, 100));

            // Search for download item to get full path
            const [downloadItem] = await chrome.downloads.search({ id: downloadId });
            if (downloadItem && downloadItem.filename) {
              // Add full path to response
              results.fullPath = downloadItem.filename;
            }
          } catch (pathError) {
            console.warn('Could not get full file path:', pathError);
          }
        } catch (error) {
          console.error('Error saving PNG file:', error);
          results.saveError = String(error instanceof Error ? error.message : error);
        }
      }
    } catch (error) {
      console.error('Error during screenshot execution:', error);
      return createErrorResponse(
        `Screenshot error: ${error instanceof Error ? error.message : JSON.stringify(error)}`,
      );
    } finally {
      // 3. Reset page
      try {
        await this.sendMessageToTab(tab.id!, {
          action: TOOL_MESSAGE_TYPES.SCREENSHOT_RESET_PAGE_AFTER_CAPTURE,
          scrollX: originalScroll.x,
          scrollY: originalScroll.y,
        });
      } catch (err) {
        console.warn('Failed to reset page, tab might have closed:', err);
      }
    }

    this.logInfo('Screenshot completed!');

    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify({
            success: true,
            message: `Screenshot [${name}] captured successfully`,
            tabId: tab.id,
            url: tab.url,
            name: name,
            ...results,
          }),
        },
      ],
      isError: false,
    };
  }

  /**
   * Log information
   */
  private logInfo(message: string) {
    console.log(`[Screenshot Tool] ${message}`);
  }

  /**
   * Capture specific element
   */
  async _captureElement(
    tabId: number,
    options: ScreenshotToolParams,
    pageDpr: number,
  ): Promise<string> {
    const elementDetails = await this.sendMessageToTab(tabId, {
      action: TOOL_MESSAGE_TYPES.SCREENSHOT_GET_ELEMENT_DETAILS,
      selector: options.selector,
    });

    const dpr = elementDetails.devicePixelRatio || pageDpr || 1;

    // Element rect is viewport-relative, in CSS pixels
    // captureVisibleTab captures in physical pixels
    const cropRectPx = {
      x: elementDetails.rect.x * dpr,
      y: elementDetails.rect.y * dpr,
      width: elementDetails.rect.width * dpr,
      height: elementDetails.rect.height * dpr,
    };

    // Small delay to ensure element is fully rendered after scrollIntoView
    await new Promise((resolve) => setTimeout(resolve, SCREENSHOT_CONSTANTS.SCRIPT_INIT_DELAY));

    const visibleCaptureDataUrl = await chrome.tabs.captureVisibleTab({ format: 'png' });
    if (!visibleCaptureDataUrl) {
      throw new Error('Failed to capture visible tab for element cropping');
    }

    const croppedCanvas = await cropAndResizeImage(
      visibleCaptureDataUrl,
      cropRectPx,
      dpr,
      options.width, // Target output width in CSS pixels
      options.height, // Target output height in CSS pixels
    );
    return canvasToDataURL(croppedCanvas);
  }

  /**
   * Capture full page
   */
  async _captureFullPage(
    tabId: number,
    options: ScreenshotToolParams,
    initialPageDetails: any,
  ): Promise<string> {
    const dpr = initialPageDetails.devicePixelRatio;
    const totalWidthCss = options.width || initialPageDetails.totalWidth; // Use option width if provided
    const totalHeightCss = initialPageDetails.totalHeight; // Full page always uses actual height

    // Apply maximum height limit for infinite scroll pages
    const maxHeightPx = options.maxHeight || SCREENSHOT_CONSTANTS.MAX_CAPTURE_HEIGHT_PX;
    const limitedHeightCss = Math.min(totalHeightCss, maxHeightPx / dpr);

    const totalWidthPx = totalWidthCss * dpr;
    const totalHeightPx = limitedHeightCss * dpr;

    // Viewport dimensions (CSS pixels) - logged for debugging
    this.logInfo(
      `Viewport size: ${initialPageDetails.viewportWidth}x${initialPageDetails.viewportHeight} CSS pixels`,
    );
    this.logInfo(
      `Page dimensions: ${totalWidthCss}x${totalHeightCss} CSS pixels (limited to ${limitedHeightCss} height)`,
    );

    const viewportHeightCss = initialPageDetails.viewportHeight;

    const capturedParts = [];
    let currentScrollYCss = 0;
    let capturedHeightPx = 0;
    let partIndex = 0;

    while (capturedHeightPx < totalHeightPx && partIndex < SCREENSHOT_CONSTANTS.MAX_CAPTURE_PARTS) {
      this.logInfo(
        `Capturing part ${partIndex + 1}... (${Math.round((capturedHeightPx / totalHeightPx) * 100)}%)`,
      );

      if (currentScrollYCss > 0) {
        // Don't scroll for the first part if already at top
        const scrollResp = await this.sendMessageToTab(tabId, {
          action: TOOL_MESSAGE_TYPES.SCREENSHOT_SCROLL_PAGE,
          x: 0,
          y: currentScrollYCss,
          scrollDelay: SCREENSHOT_CONSTANTS.SCROLL_DELAY_MS,
        });
        // Update currentScrollYCss based on actual scroll achieved
        currentScrollYCss = scrollResp.newScrollY;
      }

      // Ensure rendering after scroll
      await new Promise((resolve) =>
        setTimeout(resolve, SCREENSHOT_CONSTANTS.CAPTURE_STITCH_DELAY_MS),
      );

      const dataUrl = await chrome.tabs.captureVisibleTab({ format: 'png' });
      if (!dataUrl) throw new Error('captureVisibleTab returned empty during full page capture');

      const yOffsetPx = currentScrollYCss * dpr;
      capturedParts.push({ dataUrl, y: yOffsetPx });

      const imgForHeight = await createImageBitmapFromUrl(dataUrl); // To get actual captured height
      const lastPartEffectiveHeightPx = Math.min(imgForHeight.height, totalHeightPx - yOffsetPx);

      capturedHeightPx = yOffsetPx + lastPartEffectiveHeightPx;

      if (capturedHeightPx >= totalHeightPx - SCREENSHOT_CONSTANTS.PIXEL_TOLERANCE) break;

      currentScrollYCss += viewportHeightCss;
      // Prevent overscrolling past the document height for the next scroll command
      if (
        currentScrollYCss > totalHeightCss - viewportHeightCss &&
        currentScrollYCss < totalHeightCss
      ) {
        currentScrollYCss = totalHeightCss - viewportHeightCss;
      }
      partIndex++;
    }

    // Check if we hit any limits
    if (partIndex >= SCREENSHOT_CONSTANTS.MAX_CAPTURE_PARTS) {
      this.logInfo(
        `Reached maximum number of capture parts (${SCREENSHOT_CONSTANTS.MAX_CAPTURE_PARTS}). This may be an infinite scroll page.`,
      );
    }
    if (totalHeightCss > limitedHeightCss) {
      this.logInfo(
        `Page height (${totalHeightCss}px) exceeds maximum capture height (${maxHeightPx / dpr}px). Capturing limited portion.`,
      );
    }

    this.logInfo('Stitching image...');
    const finalCanvas = await stitchImages(capturedParts, totalWidthPx, totalHeightPx);

    // If user specified width but not height (or vice versa for full page), resize maintaining aspect ratio
    let outputCanvas = finalCanvas;
    if (options.width && !options.height) {
      const targetWidthPx = options.width * dpr;
      const aspectRatio = finalCanvas.height / finalCanvas.width;
      const targetHeightPx = targetWidthPx * aspectRatio;
      outputCanvas = new OffscreenCanvas(targetWidthPx, targetHeightPx);
      const ctx = outputCanvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(finalCanvas, 0, 0, targetWidthPx, targetHeightPx);
      }
    } else if (options.height && !options.width) {
      const targetHeightPx = options.height * dpr;
      const aspectRatio = finalCanvas.width / finalCanvas.height;
      const targetWidthPx = targetHeightPx * aspectRatio;
      outputCanvas = new OffscreenCanvas(targetWidthPx, targetHeightPx);
      const ctx = outputCanvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(finalCanvas, 0, 0, targetWidthPx, targetHeightPx);
      }
    } else if (options.width && options.height) {
      // Both specified, direct resize
      const targetWidthPx = options.width * dpr;
      const targetHeightPx = options.height * dpr;
      outputCanvas = new OffscreenCanvas(targetWidthPx, targetHeightPx);
      const ctx = outputCanvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(finalCanvas, 0, 0, targetWidthPx, targetHeightPx);
      }
    }

    return canvasToDataURL(outputCanvas);
  }
}

export const screenshotTool = new ScreenshotTool();

```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/offscreen/main.ts:
--------------------------------------------------------------------------------

```typescript
import { SemanticSimilarityEngine } from '@/utils/semantic-similarity-engine';
import {
  MessageTarget,
  SendMessageType,
  OFFSCREEN_MESSAGE_TYPES,
  BACKGROUND_MESSAGE_TYPES,
} from '@/common/message-types';

// Global semantic similarity engine instance
let similarityEngine: SemanticSimilarityEngine | null = null;
interface OffscreenMessage {
  target: MessageTarget | string;
  type: SendMessageType | string;
}

interface SimilarityEngineInitMessage extends OffscreenMessage {
  type: SendMessageType.SimilarityEngineInit;
  config: any;
}

interface SimilarityEngineComputeBatchMessage extends OffscreenMessage {
  type: SendMessageType.SimilarityEngineComputeBatch;
  pairs: { text1: string; text2: string }[];
  options?: Record<string, any>;
}

interface SimilarityEngineGetEmbeddingMessage extends OffscreenMessage {
  type: 'similarityEngineCompute';
  text: string;
  options?: Record<string, any>;
}

interface SimilarityEngineGetEmbeddingsBatchMessage extends OffscreenMessage {
  type: 'similarityEngineBatchCompute';
  texts: string[];
  options?: Record<string, any>;
}

interface SimilarityEngineStatusMessage extends OffscreenMessage {
  type: 'similarityEngineStatus';
}

type MessageResponse = {
  result?: string;
  error?: string;
  success?: boolean;
  similarities?: number[];
  embedding?: number[];
  embeddings?: number[][];
  isInitialized?: boolean;
  currentConfig?: any;
};

// Listen for messages from the extension
chrome.runtime.onMessage.addListener(
  (
    message: OffscreenMessage,
    _sender: chrome.runtime.MessageSender,
    sendResponse: (response: MessageResponse) => void,
  ) => {
    if (message.target !== MessageTarget.Offscreen) {
      return;
    }

    try {
      switch (message.type) {
        case SendMessageType.SimilarityEngineInit:
        case OFFSCREEN_MESSAGE_TYPES.SIMILARITY_ENGINE_INIT: {
          const initMsg = message as SimilarityEngineInitMessage;
          console.log('Offscreen: Received similarity engine init message:', message.type);
          handleSimilarityEngineInit(initMsg.config)
            .then(() => sendResponse({ success: true }))
            .catch((error) => sendResponse({ success: false, error: error.message }));
          break;
        }

        case SendMessageType.SimilarityEngineComputeBatch: {
          const computeMsg = message as SimilarityEngineComputeBatchMessage;
          handleComputeSimilarityBatch(computeMsg.pairs, computeMsg.options)
            .then((similarities) => sendResponse({ success: true, similarities }))
            .catch((error) => sendResponse({ success: false, error: error.message }));
          break;
        }

        case OFFSCREEN_MESSAGE_TYPES.SIMILARITY_ENGINE_COMPUTE: {
          const embeddingMsg = message as SimilarityEngineGetEmbeddingMessage;
          handleGetEmbedding(embeddingMsg.text, embeddingMsg.options)
            .then((embedding) => {
              console.log('Offscreen: Sending embedding response:', {
                length: embedding.length,
                type: typeof embedding,
                constructor: embedding.constructor.name,
                isFloat32Array: embedding instanceof Float32Array,
                firstFewValues: Array.from(embedding.slice(0, 5)),
              });
              const embeddingArray = Array.from(embedding);
              console.log('Offscreen: Converted to array:', {
                length: embeddingArray.length,
                type: typeof embeddingArray,
                isArray: Array.isArray(embeddingArray),
                firstFewValues: embeddingArray.slice(0, 5),
              });
              sendResponse({ success: true, embedding: embeddingArray });
            })
            .catch((error) => sendResponse({ success: false, error: error.message }));
          break;
        }

        case OFFSCREEN_MESSAGE_TYPES.SIMILARITY_ENGINE_BATCH_COMPUTE: {
          const batchMsg = message as SimilarityEngineGetEmbeddingsBatchMessage;
          handleGetEmbeddingsBatch(batchMsg.texts, batchMsg.options)
            .then((embeddings) =>
              sendResponse({
                success: true,
                embeddings: embeddings.map((emb) => Array.from(emb)),
              }),
            )
            .catch((error) => sendResponse({ success: false, error: error.message }));
          break;
        }

        case OFFSCREEN_MESSAGE_TYPES.SIMILARITY_ENGINE_STATUS: {
          handleGetEngineStatus()
            .then((status: any) => sendResponse({ success: true, ...status }))
            .catch((error: any) => sendResponse({ success: false, error: error.message }));
          break;
        }

        default:
          sendResponse({ error: `Unknown message type: ${message.type}` });
      }
    } catch (error) {
      if (error instanceof Error) {
        sendResponse({ error: error.message });
      } else {
        sendResponse({ error: 'Unknown error occurred' });
      }
    }

    // Return true to indicate we'll respond asynchronously
    return true;
  },
);

// Global variable to track current model state
let currentModelConfig: any = null;

/**
 * Check if engine reinitialization is needed
 */
function needsReinitialization(newConfig: any): boolean {
  if (!similarityEngine || !currentModelConfig) {
    return true;
  }

  // Check if key configuration has changed
  const keyFields = ['modelPreset', 'modelVersion', 'modelIdentifier', 'dimension'];
  for (const field of keyFields) {
    if (newConfig[field] !== currentModelConfig[field]) {
      console.log(
        `Offscreen: ${field} changed from ${currentModelConfig[field]} to ${newConfig[field]}`,
      );
      return true;
    }
  }

  return false;
}

/**
 * Progress callback function type
 */
type ProgressCallback = (progress: { status: string; progress: number; message?: string }) => void;

/**
 * Initialize semantic similarity engine
 */
async function handleSimilarityEngineInit(config: any): Promise<void> {
  console.log('Offscreen: Initializing semantic similarity engine with config:', config);
  console.log('Offscreen: Config useLocalFiles:', config.useLocalFiles);
  console.log('Offscreen: Config modelPreset:', config.modelPreset);
  console.log('Offscreen: Config modelVersion:', config.modelVersion);
  console.log('Offscreen: Config modelDimension:', config.modelDimension);
  console.log('Offscreen: Config modelIdentifier:', config.modelIdentifier);

  // Check if reinitialization is needed
  const needsReinit = needsReinitialization(config);
  console.log('Offscreen: Needs reinitialization:', needsReinit);

  if (!needsReinit) {
    console.log('Offscreen: Using existing engine (no changes detected)');
    await updateModelStatus('ready', 100);
    return;
  }

  // If engine already exists, clean up old instance first (support model switching)
  if (similarityEngine) {
    console.log('Offscreen: Cleaning up existing engine for model switch...');
    try {
      // Properly call dispose method to clean up all resources
      await similarityEngine.dispose();
      console.log('Offscreen: Previous engine disposed successfully');
    } catch (error) {
      console.warn('Offscreen: Failed to dispose previous engine:', error);
    }
    similarityEngine = null;
    currentModelConfig = null;

    // Clear vector data in IndexedDB to ensure data consistency
    try {
      console.log('Offscreen: Clearing IndexedDB vector data for model switch...');
      await clearVectorIndexedDB();
      console.log('Offscreen: IndexedDB vector data cleared successfully');
    } catch (error) {
      console.warn('Offscreen: Failed to clear IndexedDB vector data:', error);
    }
  }

  try {
    // Update status to initializing
    await updateModelStatus('initializing', 10);

    // Create progress callback function
    const progressCallback: ProgressCallback = async (progress) => {
      console.log('Offscreen: Progress update:', progress);
      await updateModelStatus(progress.status, progress.progress);
    };

    // Create engine instance and pass progress callback
    similarityEngine = new SemanticSimilarityEngine(config);
    console.log('Offscreen: Starting engine initialization with progress tracking...');

    // Use enhanced initialization method (if progress callback is supported)
    if (typeof (similarityEngine as any).initializeWithProgress === 'function') {
      await (similarityEngine as any).initializeWithProgress(progressCallback);
    } else {
      // Fallback to standard initialization method
      console.log('Offscreen: Using standard initialization (no progress callback support)');
      await updateModelStatus('downloading', 30);
      await similarityEngine.initialize();
      await updateModelStatus('ready', 100);
    }

    // Save current configuration
    currentModelConfig = { ...config };

    console.log('Offscreen: Semantic similarity engine initialized successfully');
  } catch (error) {
    console.error('Offscreen: Failed to initialize semantic similarity engine:', error);
    // Update status to error
    const errorMessage = error instanceof Error ? error.message : 'Unknown initialization error';
    const errorType = analyzeErrorType(errorMessage);
    await updateModelStatus('error', 0, errorMessage, errorType);
    // Clean up failed instance
    similarityEngine = null;
    currentModelConfig = null;
    throw error;
  }
}

/**
 * Clear vector data in IndexedDB
 */
async function clearVectorIndexedDB(): Promise<void> {
  try {
    // Clear vector search related IndexedDB databases
    const dbNames = ['VectorSearchDB', 'ContentIndexerDB', 'SemanticSimilarityDB'];

    for (const dbName of dbNames) {
      try {
        // Try to delete database
        const deleteRequest = indexedDB.deleteDatabase(dbName);
        await new Promise<void>((resolve, _reject) => {
          deleteRequest.onsuccess = () => {
            console.log(`Offscreen: Successfully deleted database: ${dbName}`);
            resolve();
          };
          deleteRequest.onerror = () => {
            console.warn(`Offscreen: Failed to delete database: ${dbName}`, deleteRequest.error);
            resolve(); // 不阻塞其他数据库的清理
          };
          deleteRequest.onblocked = () => {
            console.warn(`Offscreen: Database deletion blocked: ${dbName}`);
            resolve(); // 不阻塞其他数据库的清理
          };
        });
      } catch (error) {
        console.warn(`Offscreen: Error deleting database ${dbName}:`, error);
      }
    }
  } catch (error) {
    console.error('Offscreen: Failed to clear vector IndexedDB:', error);
    throw error;
  }
}

// Analyze error type
function analyzeErrorType(errorMessage: string): 'network' | 'file' | 'unknown' {
  const message = errorMessage.toLowerCase();

  if (
    message.includes('network') ||
    message.includes('fetch') ||
    message.includes('timeout') ||
    message.includes('connection') ||
    message.includes('cors') ||
    message.includes('failed to fetch')
  ) {
    return 'network';
  }

  if (
    message.includes('corrupt') ||
    message.includes('invalid') ||
    message.includes('format') ||
    message.includes('parse') ||
    message.includes('decode') ||
    message.includes('onnx')
  ) {
    return 'file';
  }

  return 'unknown';
}

// Helper function to update model status
async function updateModelStatus(
  status: string,
  progress: number,
  errorMessage?: string,
  errorType?: string,
) {
  try {
    const modelState = {
      status,
      downloadProgress: progress,
      isDownloading: status === 'downloading' || status === 'initializing',
      lastUpdated: Date.now(),
      errorMessage: errorMessage || '',
      errorType: errorType || '',
    };

    // In offscreen document, update storage through message passing to background script
    // because offscreen document may not have direct chrome.storage access
    if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
      await chrome.storage.local.set({ modelState });
    } else {
      // If chrome.storage is not available, pass message to background script
      console.log('Offscreen: chrome.storage not available, sending message to background');
      try {
        await chrome.runtime.sendMessage({
          type: BACKGROUND_MESSAGE_TYPES.UPDATE_MODEL_STATUS,
          modelState: modelState,
        });
      } catch (messageError) {
        console.error('Offscreen: Failed to send status update message:', messageError);
      }
    }
  } catch (error) {
    console.error('Offscreen: Failed to update model status:', error);
  }
}

/**
 * Batch compute semantic similarity
 */
async function handleComputeSimilarityBatch(
  pairs: { text1: string; text2: string }[],
  options: Record<string, any> = {},
): Promise<number[]> {
  if (!similarityEngine) {
    throw new Error('Similarity engine not initialized. Please reinitialize the engine.');
  }

  console.log(`Offscreen: Computing similarities for ${pairs.length} pairs`);
  const similarities = await similarityEngine.computeSimilarityBatch(pairs, options);
  console.log('Offscreen: Similarity computation completed');

  return similarities;
}

/**
 * Get embedding vector for single text
 */
async function handleGetEmbedding(
  text: string,
  options: Record<string, any> = {},
): Promise<Float32Array> {
  if (!similarityEngine) {
    throw new Error('Similarity engine not initialized. Please reinitialize the engine.');
  }

  console.log(`Offscreen: Getting embedding for text: "${text.substring(0, 50)}..."`);
  const embedding = await similarityEngine.getEmbedding(text, options);
  console.log('Offscreen: Embedding computation completed');

  return embedding;
}

/**
 * Batch get embedding vectors for texts
 */
async function handleGetEmbeddingsBatch(
  texts: string[],
  options: Record<string, any> = {},
): Promise<Float32Array[]> {
  if (!similarityEngine) {
    throw new Error('Similarity engine not initialized. Please reinitialize the engine.');
  }

  console.log(`Offscreen: Getting embeddings for ${texts.length} texts`);
  const embeddings = await similarityEngine.getEmbeddingsBatch(texts, options);
  console.log('Offscreen: Batch embedding computation completed');

  return embeddings;
}

/**
 * Get engine status
 */
async function handleGetEngineStatus(): Promise<{
  isInitialized: boolean;
  currentConfig: any;
}> {
  return {
    isInitialized: !!similarityEngine,
    currentConfig: currentModelConfig,
  };
}

console.log('Offscreen: Semantic similarity engine handler loaded');

```

--------------------------------------------------------------------------------
/app/native-server/src/scripts/utils.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs';
import path from 'path';
import os from 'os';
import { execSync } from 'child_process';
import { promisify } from 'util';
import { COMMAND_NAME, DESCRIPTION, EXTENSION_ID, HOST_NAME } from './constant';
import { BrowserType, getBrowserConfig, detectInstalledBrowsers } from './browser-config';

export const access = promisify(fs.access);
export const mkdir = promisify(fs.mkdir);
export const writeFile = promisify(fs.writeFile);

/**
 * 打印彩色文本
 */
export function colorText(text: string, color: string): string {
  const colors: Record<string, string> = {
    red: '\x1b[31m',
    green: '\x1b[32m',
    yellow: '\x1b[33m',
    blue: '\x1b[34m',
    reset: '\x1b[0m',
  };

  return colors[color] + text + colors.reset;
}

/**
 * Get user-level manifest file path
 */
export function getUserManifestPath(): string {
  if (os.platform() === 'win32') {
    // Windows: %APPDATA%\Google\Chrome\NativeMessagingHosts\
    return path.join(
      process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'),
      'Google',
      'Chrome',
      'NativeMessagingHosts',
      `${HOST_NAME}.json`,
    );
  } else if (os.platform() === 'darwin') {
    // macOS: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/
    return path.join(
      os.homedir(),
      'Library',
      'Application Support',
      'Google',
      'Chrome',
      'NativeMessagingHosts',
      `${HOST_NAME}.json`,
    );
  } else {
    // Linux: ~/.config/google-chrome/NativeMessagingHosts/
    return path.join(
      os.homedir(),
      '.config',
      'google-chrome',
      'NativeMessagingHosts',
      `${HOST_NAME}.json`,
    );
  }
}

/**
 * Get system-level manifest file path
 */
export function getSystemManifestPath(): string {
  if (os.platform() === 'win32') {
    // Windows: %ProgramFiles%\Google\Chrome\NativeMessagingHosts\
    return path.join(
      process.env.ProgramFiles || 'C:\\Program Files',
      'Google',
      'Chrome',
      'NativeMessagingHosts',
      `${HOST_NAME}.json`,
    );
  } else if (os.platform() === 'darwin') {
    // macOS: /Library/Google/Chrome/NativeMessagingHosts/
    return path.join('/Library', 'Google', 'Chrome', 'NativeMessagingHosts', `${HOST_NAME}.json`);
  } else {
    // Linux: /etc/opt/chrome/native-messaging-hosts/
    return path.join('/etc', 'opt', 'chrome', 'native-messaging-hosts', `${HOST_NAME}.json`);
  }
}

/**
 * Get native host startup script file path
 */
export async function getMainPath(): Promise<string> {
  try {
    const packageDistDir = path.join(__dirname, '..');
    const wrapperScriptName = process.platform === 'win32' ? 'run_host.bat' : 'run_host.sh';
    const absoluteWrapperPath = path.resolve(packageDistDir, wrapperScriptName);
    return absoluteWrapperPath;
  } catch (error) {
    console.log(colorText('Cannot find global package path, using current directory', 'yellow'));
    throw error;
  }
}

/**
 * 确保关键文件具有执行权限
 */
export async function ensureExecutionPermissions(): Promise<void> {
  try {
    const packageDistDir = path.join(__dirname, '..');

    if (process.platform === 'win32') {
      // Windows 平台处理
      await ensureWindowsFilePermissions(packageDistDir);
      return;
    }

    // Unix/Linux 平台处理
    const filesToCheck = [
      path.join(packageDistDir, 'index.js'),
      path.join(packageDistDir, 'run_host.sh'),
      path.join(packageDistDir, 'cli.js'),
    ];

    for (const filePath of filesToCheck) {
      if (fs.existsSync(filePath)) {
        try {
          fs.chmodSync(filePath, '755');
          console.log(
            colorText(`✓ Set execution permissions for ${path.basename(filePath)}`, 'green'),
          );
        } catch (err: any) {
          console.warn(
            colorText(
              `⚠️ Unable to set execution permissions for ${path.basename(filePath)}: ${err.message}`,
              'yellow',
            ),
          );
        }
      } else {
        console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow'));
      }
    }
  } catch (error: any) {
    console.warn(colorText(`⚠️ Error ensuring execution permissions: ${error.message}`, 'yellow'));
  }
}

/**
 * Windows 平台文件权限处理
 */
async function ensureWindowsFilePermissions(packageDistDir: string): Promise<void> {
  const filesToCheck = [
    path.join(packageDistDir, 'index.js'),
    path.join(packageDistDir, 'run_host.bat'),
    path.join(packageDistDir, 'cli.js'),
  ];

  for (const filePath of filesToCheck) {
    if (fs.existsSync(filePath)) {
      try {
        // 检查文件是否为只读,如果是则移除只读属性
        const stats = fs.statSync(filePath);
        if (!(stats.mode & parseInt('200', 8))) {
          // 检查写权限
          // 尝试移除只读属性
          fs.chmodSync(filePath, stats.mode | parseInt('200', 8));
          console.log(
            colorText(`✓ Removed read-only attribute from ${path.basename(filePath)}`, 'green'),
          );
        }

        // 验证文件可读性
        fs.accessSync(filePath, fs.constants.R_OK);
        console.log(
          colorText(`✓ Verified file accessibility for ${path.basename(filePath)}`, 'green'),
        );
      } catch (err: any) {
        console.warn(
          colorText(
            `⚠️ Unable to verify file permissions for ${path.basename(filePath)}: ${err.message}`,
            'yellow',
          ),
        );
      }
    } else {
      console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow'));
    }
  }
}

/**
 * Create Native Messaging host manifest content
 */
export async function createManifestContent(): Promise<any> {
  const mainPath = await getMainPath();

  return {
    name: HOST_NAME,
    description: DESCRIPTION,
    path: mainPath, // Node.js可执行文件路径
    type: 'stdio',
    allowed_origins: [`chrome-extension://${EXTENSION_ID}/`],
  };
}

/**
 * 验证Windows注册表项是否存在
 */
function verifyWindowsRegistryEntry(registryKey: string, expectedPath: string): boolean {
  if (os.platform() !== 'win32') {
    return true; // 非Windows平台跳过验证
  }

  try {
    const result = execSync(`reg query "${registryKey}" /ve`, { encoding: 'utf8', stdio: 'pipe' });
    const lines = result.split('\n');
    for (const line of lines) {
      if (line.includes('REG_SZ') && line.includes(expectedPath.replace(/\\/g, '\\\\'))) {
        return true;
      }
    }
    return false;
  } catch (error) {
    return false;
  }
}

/**
 * 尝试注册用户级别的Native Messaging主机
 */
export async function tryRegisterUserLevelHost(targetBrowsers?: BrowserType[]): Promise<boolean> {
  try {
    console.log(colorText('Attempting to register user-level Native Messaging host...', 'blue'));

    // 1. 确保执行权限
    await ensureExecutionPermissions();

    // 2. 确定要注册的浏览器
    const browsersToRegister = targetBrowsers || detectInstalledBrowsers();
    if (browsersToRegister.length === 0) {
      // 如果没有检测到浏览器,默认注册Chrome和Chromium
      browsersToRegister.push(BrowserType.CHROME, BrowserType.CHROMIUM);
      console.log(
        colorText('No browsers detected, registering for Chrome and Chromium by default', 'yellow'),
      );
    } else {
      console.log(colorText(`Detected browsers: ${browsersToRegister.join(', ')}`, 'blue'));
    }

    // 3. 创建清单内容
    const manifest = await createManifestContent();

    let successCount = 0;
    const results: { browser: string; success: boolean; error?: string }[] = [];

    // 4. 为每个浏览器注册
    for (const browserType of browsersToRegister) {
      const config = getBrowserConfig(browserType);
      console.log(colorText(`\nRegistering for ${config.displayName}...`, 'blue'));

      try {
        // 确保目录存在
        await mkdir(path.dirname(config.userManifestPath), { recursive: true });

        // 写入清单文件
        await writeFile(config.userManifestPath, JSON.stringify(manifest, null, 2));
        console.log(colorText(`✓ Manifest written to ${config.userManifestPath}`, 'green'));

        // Windows需要额外注册表项
        if (os.platform() === 'win32' && config.registryKey) {
          try {
            const escapedPath = config.userManifestPath.replace(/\\/g, '\\\\');
            const regCommand = `reg add "${config.registryKey}" /ve /t REG_SZ /d "${escapedPath}" /f`;
            execSync(regCommand, { stdio: 'pipe' });

            if (verifyWindowsRegistryEntry(config.registryKey, config.userManifestPath)) {
              console.log(colorText(`✓ Registry entry created for ${config.displayName}`, 'green'));
            } else {
              throw new Error('Registry verification failed');
            }
          } catch (error: any) {
            throw new Error(`Registry error: ${error.message}`);
          }
        }

        successCount++;
        results.push({ browser: config.displayName, success: true });
        console.log(colorText(`✓ Successfully registered ${config.displayName}`, 'green'));
      } catch (error: any) {
        results.push({ browser: config.displayName, success: false, error: error.message });
        console.log(
          colorText(`✗ Failed to register ${config.displayName}: ${error.message}`, 'red'),
        );
      }
    }

    // 5. 报告结果
    console.log(colorText('\n===== Registration Summary =====', 'blue'));
    for (const result of results) {
      if (result.success) {
        console.log(colorText(`✓ ${result.browser}: Success`, 'green'));
      } else {
        console.log(colorText(`✗ ${result.browser}: Failed - ${result.error}`, 'red'));
      }
    }

    return successCount > 0;
  } catch (error) {
    console.log(
      colorText(
        `User-level registration failed: ${error instanceof Error ? error.message : String(error)}`,
        'yellow',
      ),
    );
    return false;
  }
}

// 导入is-admin包(仅在Windows平台使用)
let isAdmin: () => boolean = () => false;
if (process.platform === 'win32') {
  try {
    isAdmin = require('is-admin');
  } catch (error) {
    console.warn('缺少is-admin依赖,Windows平台下可能无法正确检测管理员权限');
    console.warn(error);
  }
}

/**
 * 使用提升权限注册系统级清单
 */
export async function registerWithElevatedPermissions(): Promise<void> {
  try {
    console.log(colorText('Attempting to register system-level manifest...', 'blue'));

    // 1. 确保执行权限
    await ensureExecutionPermissions();

    // 2. 准备清单内容
    const manifest = await createManifestContent();

    // 3. 获取系统级清单路径
    const manifestPath = getSystemManifestPath();

    // 4. 创建临时清单文件
    const tempManifestPath = path.join(os.tmpdir(), `${HOST_NAME}.json`);
    await writeFile(tempManifestPath, JSON.stringify(manifest, null, 2));

    // 5. 检测是否已经有管理员权限
    const isRoot = process.getuid && process.getuid() === 0; // Unix/Linux/Mac
    const hasAdminRights = process.platform === 'win32' ? isAdmin() : false; // Windows平台检测管理员权限
    const hasElevatedPermissions = isRoot || hasAdminRights;

    // 准备命令
    const command =
      os.platform() === 'win32'
        ? `if not exist "${path.dirname(manifestPath)}" mkdir "${path.dirname(manifestPath)}" && copy "${tempManifestPath}" "${manifestPath}"`
        : `mkdir -p "${path.dirname(manifestPath)}" && cp "${tempManifestPath}" "${manifestPath}" && chmod 644 "${manifestPath}"`;

    if (hasElevatedPermissions) {
      // 已经有管理员权限,直接执行命令
      try {
        // 创建目录
        if (!fs.existsSync(path.dirname(manifestPath))) {
          fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
        }

        // 复制文件
        fs.copyFileSync(tempManifestPath, manifestPath);

        // 设置权限(非Windows平台)
        if (os.platform() !== 'win32') {
          fs.chmodSync(manifestPath, '644');
        }

        console.log(colorText('System-level manifest registration successful!', 'green'));
      } catch (error: any) {
        console.error(
          colorText(`System-level manifest installation failed: ${error.message}`, 'red'),
        );
        throw error;
      }
    } else {
      // 没有管理员权限,打印手动操作提示
      console.log(
        colorText('⚠️ Administrator privileges required for system-level installation', 'yellow'),
      );
      console.log(
        colorText(
          'Please run one of the following commands with administrator privileges:',
          'blue',
        ),
      );

      if (os.platform() === 'win32') {
        console.log(colorText('  1. Open Command Prompt as Administrator and run:', 'blue'));
        console.log(colorText(`     ${command}`, 'cyan'));
      } else {
        console.log(colorText('  1. Run with sudo:', 'blue'));
        console.log(colorText(`     sudo ${command}`, 'cyan'));
      }

      console.log(
        colorText('  2. Or run the registration command with elevated privileges:', 'blue'),
      );
      console.log(colorText(`     sudo ${COMMAND_NAME} register --system`, 'cyan'));

      throw new Error('Administrator privileges required for system-level installation');
    }

    // 6. Windows特殊处理 - 设置系统级注册表
    if (os.platform() === 'win32') {
      const registryKey = `HKLM\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`;
      // 确保路径使用正确的转义格式
      const escapedPath = manifestPath.replace(/\\/g, '\\\\');
      const regCommand = `reg add "${registryKey}" /ve /t REG_SZ /d "${escapedPath}" /f`;

      console.log(colorText(`Creating system registry entry: ${registryKey}`, 'blue'));
      console.log(colorText(`Manifest path: ${manifestPath}`, 'blue'));

      if (hasElevatedPermissions) {
        // 已经有管理员权限,直接执行注册表命令
        try {
          execSync(regCommand, { stdio: 'pipe' });

          // 验证注册表项是否创建成功
          if (verifyWindowsRegistryEntry(registryKey, manifestPath)) {
            console.log(colorText('Windows registry entry created successfully!', 'green'));
          } else {
            console.log(colorText('⚠️ Registry entry created but verification failed', 'yellow'));
          }
        } catch (error: any) {
          console.error(
            colorText(`Windows registry entry creation failed: ${error.message}`, 'red'),
          );
          console.error(colorText(`Command: ${regCommand}`, 'red'));
          throw error;
        }
      } else {
        // 没有管理员权限,打印手动操作提示
        console.log(
          colorText(
            '⚠️ Administrator privileges required for Windows registry modification',
            'yellow',
          ),
        );
        console.log(colorText('Please run the following command as Administrator:', 'blue'));
        console.log(colorText(`  ${regCommand}`, 'cyan'));
        console.log(colorText('Or run the registration command with elevated privileges:', 'blue'));
        console.log(
          colorText(
            `  Run Command Prompt as Administrator and execute: ${COMMAND_NAME} register --system`,
            'cyan',
          ),
        );

        throw new Error('Administrator privileges required for Windows registry modification');
      }
    }
  } catch (error: any) {
    console.error(colorText(`注册失败: ${error.message}`, 'red'));
    throw error;
  }
}

```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/tools/browser/common.ts:
--------------------------------------------------------------------------------

```typescript
import { createErrorResponse, ToolResult } from '@/common/tool-handler';
import { BaseBrowserToolExecutor } from '../base-browser';
import { TOOL_NAMES } from 'chrome-mcp-shared';

// Default window dimensions
const DEFAULT_WINDOW_WIDTH = 1280;
const DEFAULT_WINDOW_HEIGHT = 720;

interface NavigateToolParams {
  url?: string;
  newWindow?: boolean;
  width?: number;
  height?: number;
  refresh?: boolean;
}

/**
 * Tool for navigating to URLs in browser tabs or windows
 */
class NavigateTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.NAVIGATE;

  async execute(args: NavigateToolParams): Promise<ToolResult> {
    const { newWindow = false, width, height, url, refresh = false } = args;

    console.log(
      `Attempting to ${refresh ? 'refresh current tab' : `open URL: ${url}`} with options:`,
      args,
    );

    try {
      // Handle refresh option first
      if (refresh) {
        console.log('Refreshing current active tab');

        // Get current active tab
        const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });

        if (!activeTab || !activeTab.id) {
          return createErrorResponse('No active tab found to refresh');
        }

        // Reload the tab
        await chrome.tabs.reload(activeTab.id);

        console.log(`Refreshed tab ID: ${activeTab.id}`);

        // Get updated tab information
        const updatedTab = await chrome.tabs.get(activeTab.id);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                success: true,
                message: 'Successfully refreshed current tab',
                tabId: updatedTab.id,
                windowId: updatedTab.windowId,
                url: updatedTab.url,
              }),
            },
          ],
          isError: false,
        };
      }

      // Validate that url is provided when not refreshing
      if (!url) {
        return createErrorResponse('URL parameter is required when refresh is not true');
      }

      // 1. Check if URL is already open
      // Get all tabs and manually compare URLs
      console.log(`Checking if URL is already open: ${url}`);
      // Get all tabs
      const allTabs = await chrome.tabs.query({});
      // Manually filter matching tabs
      const tabs = allTabs.filter((tab) => {
        // Normalize URLs for comparison (remove trailing slashes)
        const tabUrl = tab.url?.endsWith('/') ? tab.url.slice(0, -1) : tab.url;
        const targetUrl = url.endsWith('/') ? url.slice(0, -1) : url;
        return tabUrl === targetUrl;
      });
      console.log(`Found ${tabs.length} matching tabs`);

      if (tabs && tabs.length > 0) {
        const existingTab = tabs[0];
        console.log(
          `URL already open in Tab ID: ${existingTab.id}, Window ID: ${existingTab.windowId}`,
        );

        if (existingTab.id !== undefined) {
          // Activate the tab
          await chrome.tabs.update(existingTab.id, { active: true });

          if (existingTab.windowId !== undefined) {
            // Bring the window containing this tab to the foreground and focus it
            await chrome.windows.update(existingTab.windowId, { focused: true });
          }

          console.log(`Activated existing Tab ID: ${existingTab.id}`);
          // Get updated tab information and return it
          const updatedTab = await chrome.tabs.get(existingTab.id);

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: true,
                  message: 'Activated existing tab',
                  tabId: updatedTab.id,
                  windowId: updatedTab.windowId,
                  url: updatedTab.url,
                }),
              },
            ],
            isError: false,
          };
        }
      }

      // 2. If URL is not already open, decide how to open it based on options
      const openInNewWindow = newWindow || typeof width === 'number' || typeof height === 'number';

      if (openInNewWindow) {
        console.log('Opening URL in a new window.');

        // Create new window
        const newWindow = await chrome.windows.create({
          url: url,
          width: typeof width === 'number' ? width : DEFAULT_WINDOW_WIDTH,
          height: typeof height === 'number' ? height : DEFAULT_WINDOW_HEIGHT,
          focused: true,
        });

        if (newWindow && newWindow.id !== undefined) {
          console.log(`URL opened in new Window ID: ${newWindow.id}`);

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: true,
                  message: 'Opened URL in new window',
                  windowId: newWindow.id,
                  tabs: newWindow.tabs
                    ? newWindow.tabs.map((tab) => ({
                        tabId: tab.id,
                        url: tab.url,
                      }))
                    : [],
                }),
              },
            ],
            isError: false,
          };
        }
      } else {
        console.log('Opening URL in the last active window.');
        // Try to open a new tab in the most recently active window
        const lastFocusedWindow = await chrome.windows.getLastFocused({ populate: false });

        if (lastFocusedWindow && lastFocusedWindow.id !== undefined) {
          console.log(`Found last focused Window ID: ${lastFocusedWindow.id}`);

          const newTab = await chrome.tabs.create({
            url: url,
            windowId: lastFocusedWindow.id,
            active: true,
          });

          // Ensure the window also gets focus
          await chrome.windows.update(lastFocusedWindow.id, { focused: true });

          console.log(
            `URL opened in new Tab ID: ${newTab.id} in existing Window ID: ${lastFocusedWindow.id}`,
          );

          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: true,
                  message: 'Opened URL in new tab in existing window',
                  tabId: newTab.id,
                  windowId: lastFocusedWindow.id,
                  url: newTab.url,
                }),
              },
            ],
            isError: false,
          };
        } else {
          // In rare cases, if there's no recently active window (e.g., browser just started with no windows)
          // Fall back to opening in a new window
          console.warn('No last focused window found, falling back to creating a new window.');

          const fallbackWindow = await chrome.windows.create({
            url: url,
            width: DEFAULT_WINDOW_WIDTH,
            height: DEFAULT_WINDOW_HEIGHT,
            focused: true,
          });

          if (fallbackWindow && fallbackWindow.id !== undefined) {
            console.log(`URL opened in fallback new Window ID: ${fallbackWindow.id}`);

            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify({
                    success: true,
                    message: 'Opened URL in new window',
                    windowId: fallbackWindow.id,
                    tabs: fallbackWindow.tabs
                      ? fallbackWindow.tabs.map((tab) => ({
                          tabId: tab.id,
                          url: tab.url,
                        }))
                      : [],
                  }),
                },
              ],
              isError: false,
            };
          }
        }
      }

      // If all attempts fail, return a generic error
      return createErrorResponse('Failed to open URL: Unknown error occurred');
    } catch (error) {
      if (chrome.runtime.lastError) {
        console.error(`Chrome API Error: ${chrome.runtime.lastError.message}`, error);
        return createErrorResponse(`Chrome API Error: ${chrome.runtime.lastError.message}`);
      } else {
        console.error('Error in navigate:', error);
        return createErrorResponse(
          `Error navigating to URL: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  }
}
export const navigateTool = new NavigateTool();

interface CloseTabsToolParams {
  tabIds?: number[];
  url?: string;
}

/**
 * Tool for closing browser tabs
 */
class CloseTabsTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.CLOSE_TABS;

  async execute(args: CloseTabsToolParams): Promise<ToolResult> {
    const { tabIds, url } = args;
    let urlPattern = url;
    console.log(`Attempting to close tabs with options:`, args);

    try {
      // If URL is provided, close all tabs matching that URL
      if (urlPattern) {
        console.log(`Searching for tabs with URL: ${url}`);
        if (!urlPattern.endsWith('/')) {
          urlPattern += '/*';
        }
        const tabs = await chrome.tabs.query({ url });

        if (!tabs || tabs.length === 0) {
          console.log(`No tabs found with URL: ${url}`);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: false,
                  message: `No tabs found with URL: ${url}`,
                  closedCount: 0,
                }),
              },
            ],
            isError: false,
          };
        }

        console.log(`Found ${tabs.length} tabs with URL: ${url}`);
        const tabIdsToClose = tabs
          .map((tab) => tab.id)
          .filter((id): id is number => id !== undefined);

        if (tabIdsToClose.length === 0) {
          return createErrorResponse('Found tabs but could not get their IDs');
        }

        await chrome.tabs.remove(tabIdsToClose);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                success: true,
                message: `Closed ${tabIdsToClose.length} tabs with URL: ${url}`,
                closedCount: tabIdsToClose.length,
                closedTabIds: tabIdsToClose,
              }),
            },
          ],
          isError: false,
        };
      }

      // If tabIds are provided, close those tabs
      if (tabIds && tabIds.length > 0) {
        console.log(`Closing tabs with IDs: ${tabIds.join(', ')}`);

        // Verify that all tabIds exist
        const existingTabs = await Promise.all(
          tabIds.map(async (tabId) => {
            try {
              return await chrome.tabs.get(tabId);
            } catch (error) {
              console.warn(`Tab with ID ${tabId} not found`);
              return null;
            }
          }),
        );

        const validTabIds = existingTabs
          .filter((tab): tab is chrome.tabs.Tab => tab !== null)
          .map((tab) => tab.id)
          .filter((id): id is number => id !== undefined);

        if (validTabIds.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  success: false,
                  message: 'None of the provided tab IDs exist',
                  closedCount: 0,
                }),
              },
            ],
            isError: false,
          };
        }

        await chrome.tabs.remove(validTabIds);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                success: true,
                message: `Closed ${validTabIds.length} tabs`,
                closedCount: validTabIds.length,
                closedTabIds: validTabIds,
                invalidTabIds: tabIds.filter((id) => !validTabIds.includes(id)),
              }),
            },
          ],
          isError: false,
        };
      }

      // If no tabIds or URL provided, close the current active tab
      console.log('No tabIds or URL provided, closing active tab');
      const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });

      if (!activeTab || !activeTab.id) {
        return createErrorResponse('No active tab found');
      }

      await chrome.tabs.remove(activeTab.id);

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              message: 'Closed active tab',
              closedCount: 1,
              closedTabIds: [activeTab.id],
            }),
          },
        ],
        isError: false,
      };
    } catch (error) {
      console.error('Error in CloseTabsTool.execute:', error);
      return createErrorResponse(
        `Error closing tabs: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }
}

export const closeTabsTool = new CloseTabsTool();

interface GoBackOrForwardToolParams {
  isForward?: boolean;
}

/**
 * Tool for navigating back or forward in browser history
 */
class GoBackOrForwardTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.GO_BACK_OR_FORWARD;

  async execute(args: GoBackOrForwardToolParams): Promise<ToolResult> {
    const { isForward = false } = args;

    console.log(`Attempting to navigate ${isForward ? 'forward' : 'back'} in browser history`);

    try {
      // Get current active tab
      const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });

      if (!activeTab || !activeTab.id) {
        return createErrorResponse('No active tab found');
      }

      // Navigate back or forward based on the isForward parameter
      if (isForward) {
        await chrome.tabs.goForward(activeTab.id);
        console.log(`Navigated forward in tab ID: ${activeTab.id}`);
      } else {
        await chrome.tabs.goBack(activeTab.id);
        console.log(`Navigated back in tab ID: ${activeTab.id}`);
      }

      // Get updated tab information
      const updatedTab = await chrome.tabs.get(activeTab.id);

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              message: `Successfully navigated ${isForward ? 'forward' : 'back'} in browser history`,
              tabId: updatedTab.id,
              windowId: updatedTab.windowId,
              url: updatedTab.url,
            }),
          },
        ],
        isError: false,
      };
    } catch (error) {
      if (chrome.runtime.lastError) {
        console.error(`Chrome API Error: ${chrome.runtime.lastError.message}`, error);
        return createErrorResponse(`Chrome API Error: ${chrome.runtime.lastError.message}`);
      } else {
        console.error('Error in GoBackOrForwardTool.execute:', error);
        return createErrorResponse(
          `Error navigating ${isForward ? 'forward' : 'back'}: ${
            error instanceof Error ? error.message : String(error)
          }`,
        );
      }
    }
  }
}

export const goBackOrForwardTool = new GoBackOrForwardTool();

interface SwitchTabToolParams {
  tabId: number;
  windowId?: number;
}

/**
 * Tool for switching the active tab
 */
class SwitchTabTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.SWITCH_TAB;

  async execute(args: SwitchTabToolParams): Promise<ToolResult> {
    const { tabId, windowId } = args;

    console.log(`Attempting to switch to tab ID: ${tabId} in window ID: ${windowId}`);

    try {
      if (windowId !== undefined) {
        await chrome.windows.update(windowId, { focused: true });
      }
      await chrome.tabs.update(tabId, { active: true });

      const updatedTab = await chrome.tabs.get(tabId);

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              message: `Successfully switched to tab ID: ${tabId}`,
              tabId: updatedTab.id,
              windowId: updatedTab.windowId,
              url: updatedTab.url,
            }),
          },
        ],
        isError: false,
      };
    } catch (error) {
      if (chrome.runtime.lastError) {
        console.error(`Chrome API Error: ${chrome.runtime.lastError.message}`, error);
        return createErrorResponse(`Chrome API Error: ${chrome.runtime.lastError.message}`);
      } else {
        console.error('Error in SwitchTabTool.execute:', error);
        return createErrorResponse(
          `Error switching tab: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  }
}

export const switchTabTool = new SwitchTabTool();

```

--------------------------------------------------------------------------------
/app/chrome-extension/utils/content-indexer.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Content index manager
 * Responsible for automatically extracting, chunking and indexing tab content
 */

import { TextChunker } from './text-chunker';
import { VectorDatabase, getGlobalVectorDatabase } from './vector-database';
import {
  SemanticSimilarityEngine,
  SemanticSimilarityEngineProxy,
  PREDEFINED_MODELS,
  type ModelPreset,
} from './semantic-similarity-engine';
import { TOOL_MESSAGE_TYPES } from '@/common/message-types';

export interface IndexingOptions {
  autoIndex?: boolean;
  maxChunksPerPage?: number;
  skipDuplicates?: boolean;
}

export class ContentIndexer {
  private textChunker: TextChunker;
  private vectorDatabase!: VectorDatabase;
  private semanticEngine!: SemanticSimilarityEngine | SemanticSimilarityEngineProxy;
  private isInitialized = false;
  private isInitializing = false;
  private initPromise: Promise<void> | null = null;
  private indexedPages = new Set<string>();
  private readonly options: Required<IndexingOptions>;

  constructor(options?: IndexingOptions) {
    this.options = {
      autoIndex: true,
      maxChunksPerPage: 50,
      skipDuplicates: true,
      ...options,
    };

    this.textChunker = new TextChunker();
  }

  /**
   * Get current selected model configuration
   */
  private async getCurrentModelConfig() {
    try {
      const result = await chrome.storage.local.get(['selectedModel', 'selectedVersion']);
      const selectedModel = (result.selectedModel as ModelPreset) || 'multilingual-e5-small';
      const selectedVersion =
        (result.selectedVersion as 'full' | 'quantized' | 'compressed') || 'quantized';

      const modelInfo = PREDEFINED_MODELS[selectedModel];

      return {
        modelPreset: selectedModel,
        modelIdentifier: modelInfo.modelIdentifier,
        dimension: modelInfo.dimension,
        modelVersion: selectedVersion,
        useLocalFiles: false,
        maxLength: 256,
        cacheSize: 1000,
        forceOffscreen: true,
      };
    } catch (error) {
      console.error('ContentIndexer: Failed to get current model config, using default:', error);
      return {
        modelPreset: 'multilingual-e5-small' as const,
        modelIdentifier: 'Xenova/multilingual-e5-small',
        dimension: 384,
        modelVersion: 'quantized' as const,
        useLocalFiles: false,
        maxLength: 256,
        cacheSize: 1000,
        forceOffscreen: true,
      };
    }
  }

  /**
   * Initialize content indexer
   */
  public async initialize(): Promise<void> {
    if (this.isInitialized) return;
    if (this.isInitializing && this.initPromise) return this.initPromise;

    this.isInitializing = true;
    this.initPromise = this._doInitialize().finally(() => {
      this.isInitializing = false;
    });

    return this.initPromise;
  }

  private async _doInitialize(): Promise<void> {
    try {
      // Get current selected model configuration
      const engineConfig = await this.getCurrentModelConfig();

      // Use proxy class to reuse engine instance in offscreen
      this.semanticEngine = new SemanticSimilarityEngineProxy(engineConfig);
      await this.semanticEngine.initialize();

      this.vectorDatabase = await getGlobalVectorDatabase({
        dimension: engineConfig.dimension,
        efSearch: 50,
      });
      await this.vectorDatabase.initialize();

      this.setupTabEventListeners();

      this.isInitialized = true;
    } catch (error) {
      console.error('ContentIndexer: Initialization failed:', error);
      this.isInitialized = false;
      throw error;
    }
  }

  /**
   * Index content of specified tab
   */
  public async indexTabContent(tabId: number): Promise<void> {
    // Check if semantic engine is ready before attempting to index
    if (!this.isSemanticEngineReady() && !this.isSemanticEngineInitializing()) {
      console.log(
        `ContentIndexer: Skipping tab ${tabId} - semantic engine not ready and not initializing`,
      );
      return;
    }

    if (!this.isInitialized) {
      // Only initialize if semantic engine is already ready
      if (!this.isSemanticEngineReady()) {
        console.log(
          `ContentIndexer: Skipping tab ${tabId} - ContentIndexer not initialized and semantic engine not ready`,
        );
        return;
      }
      await this.initialize();
    }

    try {
      const tab = await chrome.tabs.get(tabId);
      if (!tab.url || !this.shouldIndexUrl(tab.url)) {
        console.log(`ContentIndexer: Skipping tab ${tabId} - URL not indexable`);
        return;
      }

      const pageKey = `${tab.url}_${tab.title}`;
      if (this.options.skipDuplicates && this.indexedPages.has(pageKey)) {
        console.log(`ContentIndexer: Skipping tab ${tabId} - already indexed`);
        return;
      }

      console.log(`ContentIndexer: Starting to index tab ${tabId}: ${tab.title}`);

      const content = await this.extractTabContent(tabId);
      if (!content) {
        console.log(`ContentIndexer: No content extracted from tab ${tabId}`);
        return;
      }

      const chunks = this.textChunker.chunkText(content.textContent, content.title);
      console.log(`ContentIndexer: Generated ${chunks.length} chunks for tab ${tabId}`);

      const chunksToIndex = chunks.slice(0, this.options.maxChunksPerPage);
      if (chunks.length > this.options.maxChunksPerPage) {
        console.log(
          `ContentIndexer: Limited chunks from ${chunks.length} to ${this.options.maxChunksPerPage}`,
        );
      }

      for (const chunk of chunksToIndex) {
        try {
          const embedding = await this.semanticEngine.getEmbedding(chunk.text);
          const label = await this.vectorDatabase.addDocument(
            tabId,
            tab.url!,
            tab.title || '',
            chunk,
            embedding,
          );
          console.log(`ContentIndexer: Indexed chunk ${chunk.index} with label ${label}`);
        } catch (error) {
          console.error(`ContentIndexer: Failed to index chunk ${chunk.index}:`, error);
        }
      }

      this.indexedPages.add(pageKey);

      console.log(
        `ContentIndexer: Successfully indexed ${chunksToIndex.length} chunks for tab ${tabId}`,
      );
    } catch (error) {
      console.error(`ContentIndexer: Failed to index tab ${tabId}:`, error);
    }
  }

  /**
   * Search content
   */
  public async searchContent(query: string, topK: number = 10) {
    // Check if semantic engine is ready before attempting to search
    if (!this.isSemanticEngineReady() && !this.isSemanticEngineInitializing()) {
      throw new Error(
        'Semantic engine is not ready yet. Please initialize the semantic engine first.',
      );
    }

    if (!this.isInitialized) {
      // Only initialize if semantic engine is already ready
      if (!this.isSemanticEngineReady()) {
        throw new Error(
          'ContentIndexer not initialized and semantic engine not ready. Please initialize the semantic engine first.',
        );
      }
      await this.initialize();
    }

    try {
      const queryEmbedding = await this.semanticEngine.getEmbedding(query);
      const results = await this.vectorDatabase.search(queryEmbedding, topK);

      console.log(`ContentIndexer: Found ${results.length} results for query: "${query}"`);
      return results;
    } catch (error) {
      console.error('ContentIndexer: Search failed:', error);

      if (error instanceof Error && error.message.includes('not initialized')) {
        console.log(
          'ContentIndexer: Attempting to reinitialize semantic engine and retry search...',
        );
        try {
          await this.semanticEngine.initialize();
          const queryEmbedding = await this.semanticEngine.getEmbedding(query);
          const results = await this.vectorDatabase.search(queryEmbedding, topK);

          console.log(
            `ContentIndexer: Retry successful, found ${results.length} results for query: "${query}"`,
          );
          return results;
        } catch (retryError) {
          console.error('ContentIndexer: Retry after reinitialization also failed:', retryError);
          throw retryError;
        }
      }

      throw error;
    }
  }

  /**
   * Remove tab index
   */
  public async removeTabIndex(tabId: number): Promise<void> {
    if (!this.isInitialized) {
      return;
    }

    try {
      await this.vectorDatabase.removeTabDocuments(tabId);

      for (const pageKey of this.indexedPages) {
        if (pageKey.includes(`tab_${tabId}_`)) {
          this.indexedPages.delete(pageKey);
        }
      }

      console.log(`ContentIndexer: Removed index for tab ${tabId}`);
    } catch (error) {
      console.error(`ContentIndexer: Failed to remove index for tab ${tabId}:`, error);
    }
  }

  /**
   * Check if semantic engine is ready (checks both local and global state)
   */
  public isSemanticEngineReady(): boolean {
    return this.semanticEngine && this.semanticEngine.isInitialized;
  }

  /**
   * Check if global semantic engine is ready (in background/offscreen)
   */
  public async isGlobalSemanticEngineReady(): Promise<boolean> {
    try {
      // Since ContentIndexer runs in background script, directly call the function instead of sending message
      const { handleGetModelStatus } = await import('@/entrypoints/background/semantic-similarity');
      const response = await handleGetModelStatus();
      return (
        response &&
        response.success &&
        response.status &&
        response.status.initializationStatus === 'ready'
      );
    } catch (error) {
      console.error('ContentIndexer: Failed to check global semantic engine status:', error);
      return false;
    }
  }

  /**
   * Check if semantic engine is initializing
   */
  public isSemanticEngineInitializing(): boolean {
    return (
      this.isInitializing || (this.semanticEngine && (this.semanticEngine as any).isInitializing)
    );
  }

  /**
   * Reinitialize content indexer (for model switching)
   */
  public async reinitialize(): Promise<void> {
    console.log('ContentIndexer: Reinitializing for model switch...');

    this.isInitialized = false;
    this.isInitializing = false;
    this.initPromise = null;

    await this.performCompleteDataCleanupForModelSwitch();

    this.indexedPages.clear();
    console.log('ContentIndexer: Cleared indexed pages cache');

    try {
      console.log('ContentIndexer: Creating new semantic engine proxy...');
      const newEngineConfig = await this.getCurrentModelConfig();
      console.log('ContentIndexer: New engine config:', newEngineConfig);

      this.semanticEngine = new SemanticSimilarityEngineProxy(newEngineConfig);
      console.log('ContentIndexer: New semantic engine proxy created');

      await this.semanticEngine.initialize();
      console.log('ContentIndexer: Semantic engine proxy initialization completed');
    } catch (error) {
      console.error('ContentIndexer: Failed to create new semantic engine proxy:', error);
      throw error;
    }

    console.log(
      'ContentIndexer: New semantic engine proxy is ready, proceeding with initialization',
    );

    await this.initialize();

    console.log('ContentIndexer: Reinitialization completed successfully');
  }

  /**
   * Perform complete data cleanup for model switching
   */
  private async performCompleteDataCleanupForModelSwitch(): Promise<void> {
    console.log('ContentIndexer: Starting complete data cleanup for model switch...');

    try {
      // Clear existing vector database instance
      if (this.vectorDatabase) {
        try {
          console.log('ContentIndexer: Clearing existing vector database instance...');
          await this.vectorDatabase.clear();
          console.log('ContentIndexer: Vector database instance cleared successfully');
        } catch (error) {
          console.warn('ContentIndexer: Failed to clear vector database instance:', error);
        }
      }

      try {
        const { clearAllVectorData } = await import('./vector-database');
        await clearAllVectorData();
        console.log('ContentIndexer: Cleared all vector data for model switch');
      } catch (error) {
        console.warn('ContentIndexer: Failed to clear vector data:', error);
      }

      try {
        const keysToRemove = [
          'hnswlib_document_mappings_tab_content_index.dat',
          'hnswlib_document_mappings_content_index.dat',
          'hnswlib_document_mappings_vector_index.dat',
          'vectorDatabaseStats',
          'lastCleanupTime',
        ];
        await chrome.storage.local.remove(keysToRemove);
        console.log('ContentIndexer: Cleared chrome.storage model-related data');
      } catch (error) {
        console.warn('ContentIndexer: Failed to clear chrome.storage data:', error);
      }

      try {
        const deleteVectorDB = indexedDB.deleteDatabase('VectorDatabaseStorage');
        await new Promise<void>((resolve) => {
          deleteVectorDB.onsuccess = () => {
            console.log('ContentIndexer: VectorDatabaseStorage database deleted');
            resolve();
          };
          deleteVectorDB.onerror = () => {
            console.warn('ContentIndexer: Failed to delete VectorDatabaseStorage database');
            resolve(); // Don't block the process
          };
          deleteVectorDB.onblocked = () => {
            console.warn('ContentIndexer: VectorDatabaseStorage database deletion blocked');
            resolve(); // Don't block the process
          };
        });

        // Clean up hnswlib-index database
        const deleteHnswDB = indexedDB.deleteDatabase('/hnswlib-index');
        await new Promise<void>((resolve) => {
          deleteHnswDB.onsuccess = () => {
            console.log('ContentIndexer: /hnswlib-index database deleted');
            resolve();
          };
          deleteHnswDB.onerror = () => {
            console.warn('ContentIndexer: Failed to delete /hnswlib-index database');
            resolve(); // Don't block the process
          };
          deleteHnswDB.onblocked = () => {
            console.warn('ContentIndexer: /hnswlib-index database deletion blocked');
            resolve(); // Don't block the process
          };
        });

        console.log('ContentIndexer: All IndexedDB databases cleared for model switch');
      } catch (error) {
        console.warn('ContentIndexer: Failed to clear IndexedDB databases:', error);
      }

      console.log('ContentIndexer: Complete data cleanup for model switch finished successfully');
    } catch (error) {
      console.error('ContentIndexer: Complete data cleanup for model switch failed:', error);
      throw error;
    }
  }

  /**
   * Manually trigger semantic engine initialization (async, don't wait for completion)
   * Note: This should only be called after the semantic engine is already initialized
   */
  public startSemanticEngineInitialization(): void {
    if (!this.isInitialized && !this.isInitializing) {
      console.log('ContentIndexer: Checking if semantic engine is ready...');

      // Check if global semantic engine is ready before initializing ContentIndexer
      this.isGlobalSemanticEngineReady()
        .then((isReady) => {
          if (isReady) {
            console.log('ContentIndexer: Starting initialization (semantic engine ready)...');
            this.initialize().catch((error) => {
              console.error('ContentIndexer: Background initialization failed:', error);
            });
          } else {
            console.log('ContentIndexer: Semantic engine not ready, skipping initialization');
          }
        })
        .catch((error) => {
          console.error('ContentIndexer: Failed to check semantic engine status:', error);
        });
    }
  }

  /**
   * Get indexing statistics
   */
  public getStats() {
    const vectorStats = this.vectorDatabase
      ? this.vectorDatabase.getStats()
      : {
          totalDocuments: 0,
          totalTabs: 0,
          indexSize: 0,
        };

    return {
      ...vectorStats,
      indexedPages: this.indexedPages.size,
      isInitialized: this.isInitialized,
      semanticEngineReady: this.isSemanticEngineReady(),
      semanticEngineInitializing: this.isSemanticEngineInitializing(),
    };
  }

  /**
   * Clear all indexes
   */
  public async clearAllIndexes(): Promise<void> {
    if (!this.isInitialized) {
      return;
    }

    try {
      await this.vectorDatabase.clear();
      this.indexedPages.clear();
      console.log('ContentIndexer: All indexes cleared');
    } catch (error) {
      console.error('ContentIndexer: Failed to clear indexes:', error);
    }
  }
  private setupTabEventListeners(): void {
    chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
      if (this.options.autoIndex && changeInfo.status === 'complete' && tab.url) {
        setTimeout(() => {
          if (!this.isSemanticEngineReady() && !this.isSemanticEngineInitializing()) {
            console.log(
              `ContentIndexer: Skipping auto-index for tab ${tabId} - semantic engine not ready`,
            );
            return;
          }

          this.indexTabContent(tabId).catch((error) => {
            console.error(`ContentIndexer: Auto-indexing failed for tab ${tabId}:`, error);
          });
        }, 2000);
      }
    });

    chrome.tabs.onRemoved.addListener(async (tabId) => {
      await this.removeTabIndex(tabId);
    });

    if (chrome.webNavigation) {
      chrome.webNavigation.onCommitted.addListener(async (details) => {
        if (details.frameId === 0) {
          await this.removeTabIndex(details.tabId);
        }
      });
    }
  }

  private shouldIndexUrl(url: string): boolean {
    const excludePatterns = [
      /^chrome:\/\//,
      /^chrome-extension:\/\//,
      /^edge:\/\//,
      /^about:/,
      /^moz-extension:\/\//,
      /^file:\/\//,
    ];

    return !excludePatterns.some((pattern) => pattern.test(url));
  }

  private async extractTabContent(
    tabId: number,
  ): Promise<{ textContent: string; title: string } | null> {
    try {
      await chrome.scripting.executeScript({
        target: { tabId },
        files: ['inject-scripts/web-fetcher-helper.js'],
      });

      const response = await chrome.tabs.sendMessage(tabId, {
        action: TOOL_MESSAGE_TYPES.WEB_FETCHER_GET_TEXT_CONTENT,
      });

      if (response.success && response.textContent) {
        return {
          textContent: response.textContent,
          title: response.title || '',
        };
      } else {
        console.error(
          `ContentIndexer: Failed to extract content from tab ${tabId}:`,
          response.error,
        );
        return null;
      }
    } catch (error) {
      console.error(`ContentIndexer: Error extracting content from tab ${tabId}:`, error);
      return null;
    }
  }
}

let globalContentIndexer: ContentIndexer | null = null;

/**
 * Get global ContentIndexer instance
 */
export function getGlobalContentIndexer(): ContentIndexer {
  if (!globalContentIndexer) {
    globalContentIndexer = new ContentIndexer();
  }
  return globalContentIndexer;
}

```

--------------------------------------------------------------------------------
/packages/shared/src/tools.ts:
--------------------------------------------------------------------------------

```typescript
import { type Tool } from '@modelcontextprotocol/sdk/types.js';

export const TOOL_NAMES = {
  BROWSER: {
    GET_WINDOWS_AND_TABS: 'get_windows_and_tabs',
    SEARCH_TABS_CONTENT: 'search_tabs_content',
    NAVIGATE: 'chrome_navigate',
    SCREENSHOT: 'chrome_screenshot',
    CLOSE_TABS: 'chrome_close_tabs',
    SWITCH_TAB: 'chrome_switch_tab',
    GO_BACK_OR_FORWARD: 'chrome_go_back_or_forward',
    WEB_FETCHER: 'chrome_get_web_content',
    CLICK: 'chrome_click_element',
    FILL: 'chrome_fill_or_select',
    GET_INTERACTIVE_ELEMENTS: 'chrome_get_interactive_elements',
    NETWORK_CAPTURE_START: 'chrome_network_capture_start',
    NETWORK_CAPTURE_STOP: 'chrome_network_capture_stop',
    NETWORK_REQUEST: 'chrome_network_request',
    NETWORK_DEBUGGER_START: 'chrome_network_debugger_start',
    NETWORK_DEBUGGER_STOP: 'chrome_network_debugger_stop',
    KEYBOARD: 'chrome_keyboard',
    HISTORY: 'chrome_history',
    BOOKMARK_SEARCH: 'chrome_bookmark_search',
    BOOKMARK_ADD: 'chrome_bookmark_add',
    BOOKMARK_DELETE: 'chrome_bookmark_delete',
    INJECT_SCRIPT: 'chrome_inject_script',
    SEND_COMMAND_TO_INJECT_SCRIPT: 'chrome_send_command_to_inject_script',
    CONSOLE: 'chrome_console',
    FILE_UPLOAD: 'chrome_upload_file',
  },
};

export const TOOL_SCHEMAS: Tool[] = [
  {
    name: TOOL_NAMES.BROWSER.GET_WINDOWS_AND_TABS,
    description: 'Get all currently open browser windows and tabs',
    inputSchema: {
      type: 'object',
      properties: {},
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NAVIGATE,
    description: 'Navigate to a URL or refresh the current tab',
    inputSchema: {
      type: 'object',
      properties: {
        url: { type: 'string', description: 'URL to navigate to the website specified' },
        newWindow: {
          type: 'boolean',
          description: 'Create a new window to navigate to the URL or not. Defaults to false',
        },
        width: { type: 'number', description: 'Viewport width in pixels (default: 1280)' },
        height: { type: 'number', description: 'Viewport height in pixels (default: 720)' },
        refresh: {
          type: 'boolean',
          description:
            'Refresh the current active tab instead of navigating to a URL. When true, the url parameter is ignored. Defaults to false',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.SCREENSHOT,
    description:
      'Take a screenshot of the current page or a specific element(if you want to see the page, recommend to use chrome_get_web_content first)',
    inputSchema: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Name for the screenshot, if saving as PNG' },
        selector: { type: 'string', description: 'CSS selector for element to screenshot' },
        width: { type: 'number', description: 'Width in pixels (default: 800)' },
        height: { type: 'number', description: 'Height in pixels (default: 600)' },
        storeBase64: {
          type: 'boolean',
          description:
            'return screenshot in base64 format (default: false) if you want to see the page, recommend set this to be true',
        },
        fullPage: {
          type: 'boolean',
          description: 'Store screenshot of the entire page (default: true)',
        },
        savePng: {
          type: 'boolean',
          description:
            'Save screenshot as PNG file (default: true),if you want to see the page, recommend set this to be false, and set storeBase64 to be true',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.CLOSE_TABS,
    description: 'Close one or more browser tabs',
    inputSchema: {
      type: 'object',
      properties: {
        tabIds: {
          type: 'array',
          items: { type: 'number' },
          description: 'Array of tab IDs to close. If not provided, will close the active tab.',
        },
        url: {
          type: 'string',
          description: 'Close tabs matching this URL. Can be used instead of tabIds.',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.SWITCH_TAB,
    description: 'Switch to a specific browser tab',
    inputSchema: {
      type: 'object',
      properties: {
        tabId: {
          type: 'number',
          description: 'The ID of the tab to switch to.',
        },
        windowId: {
          type: 'number',
          description: 'The ID of the window where the tab is located.',
        },
      },
      required: ['tabId'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.GO_BACK_OR_FORWARD,
    description: 'Navigate back or forward in browser history',
    inputSchema: {
      type: 'object',
      properties: {
        isForward: {
          type: 'boolean',
          description: 'Go forward in history if true, go back if false (default: false)',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.WEB_FETCHER,
    description: 'Fetch content from a web page',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description: 'URL to fetch content from. If not provided, uses the current active tab',
        },
        htmlContent: {
          type: 'boolean',
          description:
            'Get the visible HTML content of the page. If true, textContent will be ignored (default: false)',
        },
        textContent: {
          type: 'boolean',
          description:
            'Get the visible text content of the page with metadata. Ignored if htmlContent is true (default: true)',
        },

        selector: {
          type: 'string',
          description:
            'CSS selector to get content from a specific element. If provided, only content from this element will be returned',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.CLICK,
    description: 'Click on an element in the current page or at specific coordinates',
    inputSchema: {
      type: 'object',
      properties: {
        selector: {
          type: 'string',
          description:
            'CSS selector for the element to click. Either selector or coordinates must be provided. if coordinates are not provided, the selector must be provided.',
        },
        coordinates: {
          type: 'object',
          description:
            'Coordinates to click at (relative to viewport). If provided, takes precedence over selector.',
          properties: {
            x: {
              type: 'number',
              description: 'X coordinate relative to the viewport',
            },
            y: {
              type: 'number',
              description: 'Y coordinate relative to the viewport',
            },
          },
          required: ['x', 'y'],
        },
        waitForNavigation: {
          type: 'boolean',
          description: 'Wait for page navigation to complete after click (default: false)',
        },
        timeout: {
          type: 'number',
          description:
            'Timeout in milliseconds for waiting for the element or navigation (default: 5000)',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.FILL,
    description: 'Fill a form element or select an option with the specified value',
    inputSchema: {
      type: 'object',
      properties: {
        selector: {
          type: 'string',
          description: 'CSS selector for the input element to fill or select',
        },
        value: {
          type: 'string',
          description: 'Value to fill or select into the element',
        },
      },
      required: ['selector', 'value'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.GET_INTERACTIVE_ELEMENTS,
    description: 'Get interactive elements from the current page',
    inputSchema: {
      type: 'object',
      properties: {
        textQuery: {
          type: 'string',
          description: 'Text to search for within interactive elements (fuzzy search)',
        },
        selector: {
          type: 'string',
          description:
            'CSS selector to filter interactive elements. Takes precedence over textQuery if both are provided.',
        },
        includeCoordinates: {
          type: 'boolean',
          description: 'Include element coordinates in the response (default: true)',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NETWORK_REQUEST,
    description: 'Send a network request from the browser with cookies and other browser context',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description: 'URL to send the request to',
        },
        method: {
          type: 'string',
          description: 'HTTP method to use (default: GET)',
        },
        headers: {
          type: 'object',
          description: 'Headers to include in the request',
        },
        body: {
          type: 'string',
          description: 'Body of the request (for POST, PUT, etc.)',
        },
        timeout: {
          type: 'number',
          description: 'Timeout in milliseconds (default: 30000)',
        },
      },
      required: ['url'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NETWORK_DEBUGGER_START,
    description:
      'Start capturing network requests from a web page using Chrome Debugger API(with responseBody)',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description:
            'URL to capture network requests from. If not provided, uses the current active tab',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NETWORK_DEBUGGER_STOP,
    description:
      'Stop capturing network requests using Chrome Debugger API and return the captured data',
    inputSchema: {
      type: 'object',
      properties: {},
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NETWORK_CAPTURE_START,
    description:
      'Start capturing network requests from a web page using Chrome webRequest API(without responseBody)',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description:
            'URL to capture network requests from. If not provided, uses the current active tab',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.NETWORK_CAPTURE_STOP,
    description:
      'Stop capturing network requests using webRequest API and return the captured data',
    inputSchema: {
      type: 'object',
      properties: {},
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.KEYBOARD,
    description: 'Simulate keyboard events in the browser',
    inputSchema: {
      type: 'object',
      properties: {
        keys: {
          type: 'string',
          description: 'Keys to simulate (e.g., "Enter", "Ctrl+C", "A,B,C" for sequence)',
        },
        selector: {
          type: 'string',
          description:
            'CSS selector for the element to send keyboard events to (optional, defaults to active element)',
        },
        delay: {
          type: 'number',
          description: 'Delay between key sequences in milliseconds (optional, default: 0)',
        },
      },
      required: ['keys'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.HISTORY,
    description: 'Retrieve and search browsing history from Chrome',
    inputSchema: {
      type: 'object',
      properties: {
        text: {
          type: 'string',
          description:
            'Text to search for in history URLs and titles. Leave empty to retrieve all history entries within the time range.',
        },
        startTime: {
          type: 'string',
          description:
            'Start time as a date string. Supports ISO format (e.g., "2023-10-01", "2023-10-01T14:30:00"), relative times (e.g., "1 day ago", "2 weeks ago", "3 months ago", "1 year ago"), and special keywords ("now", "today", "yesterday"). Default: 24 hours ago',
        },
        endTime: {
          type: 'string',
          description:
            'End time as a date string. Supports ISO format (e.g., "2023-10-31", "2023-10-31T14:30:00"), relative times (e.g., "1 day ago", "2 weeks ago", "3 months ago", "1 year ago"), and special keywords ("now", "today", "yesterday"). Default: current time',
        },
        maxResults: {
          type: 'number',
          description:
            'Maximum number of history entries to return. Use this to limit results for performance or to focus on the most relevant entries. (default: 100)',
        },
        excludeCurrentTabs: {
          type: 'boolean',
          description:
            "When set to true, filters out URLs that are currently open in any browser tab. Useful for finding pages you've visited but don't have open anymore. (default: false)",
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.BOOKMARK_SEARCH,
    description: 'Search Chrome bookmarks by title and URL',
    inputSchema: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description:
            'Search query to match against bookmark titles and URLs. Leave empty to retrieve all bookmarks.',
        },
        maxResults: {
          type: 'number',
          description: 'Maximum number of bookmarks to return (default: 50)',
        },
        folderPath: {
          type: 'string',
          description:
            'Optional folder path or ID to limit search to a specific bookmark folder. Can be a path string (e.g., "Work/Projects") or a folder ID.',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.BOOKMARK_ADD,
    description: 'Add a new bookmark to Chrome',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description: 'URL to bookmark. If not provided, uses the current active tab URL.',
        },
        title: {
          type: 'string',
          description: 'Title for the bookmark. If not provided, uses the page title from the URL.',
        },
        parentId: {
          type: 'string',
          description:
            'Parent folder path or ID to add the bookmark to. Can be a path string (e.g., "Work/Projects") or a folder ID. If not provided, adds to the "Bookmarks Bar" folder.',
        },
        createFolder: {
          type: 'boolean',
          description: 'Whether to create the parent folder if it does not exist (default: false)',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.BOOKMARK_DELETE,
    description: 'Delete a bookmark from Chrome',
    inputSchema: {
      type: 'object',
      properties: {
        bookmarkId: {
          type: 'string',
          description: 'ID of the bookmark to delete. Either bookmarkId or url must be provided.',
        },
        url: {
          type: 'string',
          description: 'URL of the bookmark to delete. Used if bookmarkId is not provided.',
        },
        title: {
          type: 'string',
          description: 'Title of the bookmark to help with matching when deleting by URL.',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.SEARCH_TABS_CONTENT,
    description:
      'search for related content from the currently open tab and return the corresponding web pages.',
    inputSchema: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: 'the query to search for related content.',
        },
      },
      required: ['query'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.INJECT_SCRIPT,
    description:
      'inject the user-specified content script into the webpage. By default, inject into the currently active tab',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description:
            'If a URL is specified, inject the script into the webpage corresponding to the URL.',
        },
        type: {
          type: 'string',
          description:
            'the javaScript world for a script to execute within. must be ISOLATED or MAIN',
        },
        jsScript: {
          type: 'string',
          description: 'the content script to inject',
        },
      },
      required: ['type', 'jsScript'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.SEND_COMMAND_TO_INJECT_SCRIPT,
    description:
      'if the script injected using chrome_inject_script listens for user-defined events, this tool can be used to trigger those events',
    inputSchema: {
      type: 'object',
      properties: {
        tabId: {
          type: 'number',
          description:
            'the tab where you previously injected the script(if not provided,  use the currently active tab)',
        },
        eventName: {
          type: 'string',
          description: 'the eventName your injected content script listen for',
        },
        payload: {
          type: 'string',
          description: 'the payload passed to event, must be a json string',
        },
      },
      required: ['eventName'],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.CONSOLE,
    description:
      'Capture and retrieve all console output from the current active browser tab/page. This captures console messages that existed before the tool was called.',
    inputSchema: {
      type: 'object',
      properties: {
        url: {
          type: 'string',
          description:
            'URL to navigate to and capture console from. If not provided, uses the current active tab',
        },
        includeExceptions: {
          type: 'boolean',
          description: 'Include uncaught exceptions in the output (default: true)',
        },
        maxMessages: {
          type: 'number',
          description: 'Maximum number of console messages to capture (default: 100)',
        },
      },
      required: [],
    },
  },
  {
    name: TOOL_NAMES.BROWSER.FILE_UPLOAD,
    description: 'Upload files to web forms with file input elements using Chrome DevTools Protocol',
    inputSchema: {
      type: 'object',
      properties: {
        selector: {
          type: 'string',
          description: 'CSS selector for the file input element (input[type="file"])',
        },
        filePath: {
          type: 'string',
          description: 'Local file path to upload',
        },
        fileUrl: {
          type: 'string',
          description: 'URL to download file from before uploading',
        },
        base64Data: {
          type: 'string',
          description: 'Base64 encoded file data to upload',
        },
        fileName: {
          type: 'string',
          description: 'Optional filename when using base64 or URL (default: "uploaded-file")',
        },
        multiple: {
          type: 'boolean',
          description: 'Whether the input accepts multiple files (default: false)',
        },
      },
      required: ['selector'],
    },
  },
];

```

--------------------------------------------------------------------------------
/app/chrome-extension/entrypoints/background/tools/browser/bookmark.ts:
--------------------------------------------------------------------------------

```typescript
import { createErrorResponse, ToolResult } from '@/common/tool-handler';
import { BaseBrowserToolExecutor } from '../base-browser';
import { TOOL_NAMES } from 'chrome-mcp-shared';
import { getMessage } from '@/utils/i18n';

/**
 * Bookmark search tool parameters interface
 */
interface BookmarkSearchToolParams {
  query?: string; // Search keywords for matching bookmark titles and URLs
  maxResults?: number; // Maximum number of results to return
  folderPath?: string; // Optional, specify which folder to search in (can be ID or path string like "Work/Projects")
}

/**
 * Bookmark add tool parameters interface
 */
interface BookmarkAddToolParams {
  url?: string; // URL to add as bookmark, if not provided use current active tab URL
  title?: string; // Bookmark title, if not provided use page title
  parentId?: string; // Parent folder ID or path string (like "Work/Projects"), if not provided add to "Bookmarks Bar" folder
  createFolder?: boolean; // Whether to automatically create parent folder if it doesn't exist
}

/**
 * Bookmark delete tool parameters interface
 */
interface BookmarkDeleteToolParams {
  bookmarkId?: string; // ID of bookmark to delete
  url?: string; // URL of bookmark to delete (if ID not provided, search by URL)
  title?: string; // Title of bookmark to delete (used for auxiliary matching, used together with URL)
}

// --- Helper Functions ---

/**
 * Get the complete folder path of a bookmark
 * @param bookmarkNodeId ID of the bookmark or folder
 * @returns Returns folder path string (e.g., "Bookmarks Bar > Folder A > Subfolder B")
 */
async function getBookmarkFolderPath(bookmarkNodeId: string): Promise<string> {
  const pathParts: string[] = [];

  try {
    // First get the node itself to check if it's a bookmark or folder
    const initialNodes = await chrome.bookmarks.get(bookmarkNodeId);
    if (initialNodes.length > 0 && initialNodes[0]) {
      const initialNode = initialNodes[0];

      // Build path starting from parent node (same for both bookmarks and folders)
      let pathNodeId = initialNode.parentId;
      while (pathNodeId) {
        const parentNodes = await chrome.bookmarks.get(pathNodeId);
        if (parentNodes.length === 0) break;

        const parentNode = parentNodes[0];
        if (parentNode.title) {
          pathParts.unshift(parentNode.title);
        }

        if (!parentNode.parentId) break;
        pathNodeId = parentNode.parentId;
      }
    }
  } catch (error) {
    console.error(`Error getting bookmark path for node ID ${bookmarkNodeId}:`, error);
    return pathParts.join(' > ') || 'Error getting path';
  }

  return pathParts.join(' > ');
}

/**
 * Find bookmark folder by ID or path string
 * If it's an ID, validate it
 * If it's a path string, try to parse it
 * @param pathOrId Path string (e.g., "Work/Projects") or folder ID
 * @returns Returns folder node, or null if not found
 */
async function findFolderByPathOrId(
  pathOrId: string,
): Promise<chrome.bookmarks.BookmarkTreeNode | null> {
  try {
    const nodes = await chrome.bookmarks.get(pathOrId);
    if (nodes && nodes.length > 0 && !nodes[0].url) {
      return nodes[0];
    }
  } catch (e) {
    // do nothing, try to parse as path string
  }

  const pathParts = pathOrId
    .split('/')
    .map((p) => p.trim())
    .filter((p) => p.length > 0);
  if (pathParts.length === 0) return null;

  const rootChildren = await chrome.bookmarks.getChildren('0');

  let currentNodes = rootChildren;
  let foundFolder: chrome.bookmarks.BookmarkTreeNode | null = null;

  for (let i = 0; i < pathParts.length; i++) {
    const part = pathParts[i];
    foundFolder = null;
    let matchedNodeThisLevel: chrome.bookmarks.BookmarkTreeNode | null = null;

    for (const node of currentNodes) {
      if (!node.url && node.title.toLowerCase() === part.toLowerCase()) {
        matchedNodeThisLevel = node;
        break;
      }
    }

    if (matchedNodeThisLevel) {
      if (i === pathParts.length - 1) {
        foundFolder = matchedNodeThisLevel;
      } else {
        currentNodes = await chrome.bookmarks.getChildren(matchedNodeThisLevel.id);
      }
    } else {
      return null;
    }
  }

  return foundFolder;
}

/**
 * Create folder path (if it doesn't exist)
 * @param folderPath Folder path string (e.g., "Work/Projects/Subproject")
 * @param parentId Optional parent folder ID, defaults to "Bookmarks Bar"
 * @returns Returns the created or found final folder node
 */
async function createFolderPath(
  folderPath: string,
  parentId?: string,
): Promise<chrome.bookmarks.BookmarkTreeNode> {
  const pathParts = folderPath
    .split('/')
    .map((p) => p.trim())
    .filter((p) => p.length > 0);

  if (pathParts.length === 0) {
    throw new Error('Folder path cannot be empty');
  }

  // If no parent ID specified, use "Bookmarks Bar" folder
  let currentParentId: string = parentId || '';
  if (!currentParentId) {
    const rootChildren = await chrome.bookmarks.getChildren('0');
    // Find "Bookmarks Bar" folder (usually ID is '1', but search by title for compatibility)
    const bookmarkBarFolder = rootChildren.find(
      (node) =>
        !node.url &&
        (node.title === getMessage('bookmarksBarLabel') ||
          node.title === 'Bookmarks bar' ||
          node.title === 'Bookmarks Bar'),
    );
    currentParentId = bookmarkBarFolder?.id || '1'; // fallback to default ID
  }

  let currentFolder: chrome.bookmarks.BookmarkTreeNode | null = null;

  // Create or find folders level by level
  for (const folderName of pathParts) {
    const children: chrome.bookmarks.BookmarkTreeNode[] =
      await chrome.bookmarks.getChildren(currentParentId);

    // Check if folder with same name already exists
    const existingFolder: chrome.bookmarks.BookmarkTreeNode | undefined = children.find(
      (child: chrome.bookmarks.BookmarkTreeNode) =>
        !child.url && child.title.toLowerCase() === folderName.toLowerCase(),
    );

    if (existingFolder) {
      currentFolder = existingFolder;
      currentParentId = existingFolder.id;
    } else {
      // Create new folder
      currentFolder = await chrome.bookmarks.create({
        parentId: currentParentId,
        title: folderName,
      });
      currentParentId = currentFolder.id;
    }
  }

  if (!currentFolder) {
    throw new Error('Failed to create folder path');
  }

  return currentFolder;
}

/**
 * Flatten bookmark tree (or node array) to bookmark list (excluding folders)
 * @param nodes Bookmark tree nodes to flatten
 * @returns Returns actual bookmark node array (nodes with URLs)
 */
function flattenBookmarkNodesToBookmarks(
  nodes: chrome.bookmarks.BookmarkTreeNode[],
): chrome.bookmarks.BookmarkTreeNode[] {
  const result: chrome.bookmarks.BookmarkTreeNode[] = [];
  const stack = [...nodes]; // Use stack for iterative traversal to avoid deep recursion issues

  while (stack.length > 0) {
    const node = stack.pop();
    if (!node) continue;

    if (node.url) {
      // It's a bookmark
      result.push(node);
    }

    if (node.children) {
      // Add child nodes to stack for processing
      for (let i = node.children.length - 1; i >= 0; i--) {
        stack.push(node.children[i]);
      }
    }
  }

  return result;
}

/**
 * Find bookmarks by URL and title
 * @param url Bookmark URL
 * @param title Optional bookmark title for auxiliary matching
 * @returns Returns array of matching bookmarks
 */
async function findBookmarksByUrl(
  url: string,
  title?: string,
): Promise<chrome.bookmarks.BookmarkTreeNode[]> {
  // Use Chrome API to search by URL
  const searchResults = await chrome.bookmarks.search({ url });

  if (!title) {
    return searchResults;
  }

  // If title is provided, further filter results
  const titleLower = title.toLowerCase();
  return searchResults.filter(
    (bookmark) => bookmark.title && bookmark.title.toLowerCase().includes(titleLower),
  );
}

/**
 * Bookmark search tool
 * Used to search bookmarks in Chrome browser
 */
class BookmarkSearchTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.BOOKMARK_SEARCH;

  /**
   * Execute bookmark search
   */
  async execute(args: BookmarkSearchToolParams): Promise<ToolResult> {
    const { query = '', maxResults = 50, folderPath } = args;

    console.log(
      `BookmarkSearchTool: Searching bookmarks, keywords: "${query}", folder path: "${folderPath}"`,
    );

    try {
      let bookmarksToSearch: chrome.bookmarks.BookmarkTreeNode[] = [];
      let targetFolderNode: chrome.bookmarks.BookmarkTreeNode | null = null;

      // If folder path is specified, find that folder first
      if (folderPath) {
        targetFolderNode = await findFolderByPathOrId(folderPath);
        if (!targetFolderNode) {
          return createErrorResponse(`Specified folder not found: "${folderPath}"`);
        }
        // Get all bookmarks in that folder and its subfolders
        const subTree = await chrome.bookmarks.getSubTree(targetFolderNode.id);
        bookmarksToSearch =
          subTree.length > 0 ? flattenBookmarkNodesToBookmarks(subTree[0].children || []) : [];
      }

      let filteredBookmarks: chrome.bookmarks.BookmarkTreeNode[];

      if (query) {
        if (targetFolderNode) {
          // Has query keywords and specified folder: manually filter bookmarks from folder
          const lowerCaseQuery = query.toLowerCase();
          filteredBookmarks = bookmarksToSearch.filter(
            (bookmark) =>
              (bookmark.title && bookmark.title.toLowerCase().includes(lowerCaseQuery)) ||
              (bookmark.url && bookmark.url.toLowerCase().includes(lowerCaseQuery)),
          );
        } else {
          // Has query keywords but no specified folder: use API search
          filteredBookmarks = await chrome.bookmarks.search({ query });
          // API search may return folders (if title matches), filter them out
          filteredBookmarks = filteredBookmarks.filter((item) => !!item.url);
        }
      } else {
        // No query keywords
        if (!targetFolderNode) {
          // No folder path specified, get all bookmarks
          const tree = await chrome.bookmarks.getTree();
          bookmarksToSearch = flattenBookmarkNodesToBookmarks(tree);
        }
        filteredBookmarks = bookmarksToSearch;
      }

      // Limit number of results
      const limitedResults = filteredBookmarks.slice(0, maxResults);

      // Add folder path information for each bookmark
      const resultsWithPath = await Promise.all(
        limitedResults.map(async (bookmark) => {
          const path = await getBookmarkFolderPath(bookmark.id);
          return {
            id: bookmark.id,
            title: bookmark.title,
            url: bookmark.url,
            dateAdded: bookmark.dateAdded,
            folderPath: path,
          };
        }),
      );

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: true,
                totalResults: resultsWithPath.length,
                query: query || null,
                folderSearched: targetFolderNode
                  ? targetFolderNode.title || targetFolderNode.id
                  : 'All bookmarks',
                bookmarks: resultsWithPath,
              },
              null,
              2,
            ),
          },
        ],
        isError: false,
      };
    } catch (error) {
      console.error('Error searching bookmarks:', error);
      return createErrorResponse(
        `Error searching bookmarks: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }
}

/**
 * Bookmark add tool
 * Used to add new bookmarks to Chrome browser
 */
class BookmarkAddTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.BOOKMARK_ADD;

  /**
   * Execute add bookmark operation
   */
  async execute(args: BookmarkAddToolParams): Promise<ToolResult> {
    const { url, title, parentId, createFolder = false } = args;

    console.log(`BookmarkAddTool: Adding bookmark, options:`, args);

    try {
      // If no URL provided, use current active tab
      let bookmarkUrl = url;
      let bookmarkTitle = title;

      if (!bookmarkUrl) {
        // Get current active tab
        const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
        if (!tabs[0] || !tabs[0].url) {
          // tab.url might be undefined (e.g., chrome:// pages)
          return createErrorResponse('No active tab with valid URL found, and no URL provided');
        }

        bookmarkUrl = tabs[0].url;
        if (!bookmarkTitle) {
          bookmarkTitle = tabs[0].title || bookmarkUrl; // If tab title is empty, use URL as title
        }
      }

      if (!bookmarkUrl) {
        // Should have been caught above, but as a safety measure
        return createErrorResponse('URL is required to create bookmark');
      }

      // Parse parentId (could be ID or path string)
      let actualParentId: string | undefined = undefined;
      if (parentId) {
        let folderNode = await findFolderByPathOrId(parentId);

        if (!folderNode && createFolder) {
          // If folder doesn't exist and creation is allowed, create folder path
          try {
            folderNode = await createFolderPath(parentId);
          } catch (createError) {
            return createErrorResponse(
              `Failed to create folder path: ${createError instanceof Error ? createError.message : String(createError)}`,
            );
          }
        }

        if (folderNode) {
          actualParentId = folderNode.id;
        } else {
          // Check if parentId might be a direct ID missed by findFolderByPathOrId (e.g., root folder '1')
          try {
            const nodes = await chrome.bookmarks.get(parentId);
            if (nodes && nodes.length > 0 && !nodes[0].url) {
              actualParentId = nodes[0].id;
            } else {
              return createErrorResponse(
                `Specified parent folder (ID/path: "${parentId}") not found or is not a folder${createFolder ? ', and creation failed' : '. You can set createFolder=true to auto-create folders'}`,
              );
            }
          } catch (e) {
            return createErrorResponse(
              `Specified parent folder (ID/path: "${parentId}") not found or invalid${createFolder ? ', and creation failed' : '. You can set createFolder=true to auto-create folders'}`,
            );
          }
        }
      } else {
        // If no parentId specified, default to "Bookmarks Bar"
        const rootChildren = await chrome.bookmarks.getChildren('0');
        const bookmarkBarFolder = rootChildren.find(
          (node) =>
            !node.url &&
            (node.title === getMessage('bookmarksBarLabel') ||
              node.title === 'Bookmarks bar' ||
              node.title === 'Bookmarks Bar'),
        );
        actualParentId = bookmarkBarFolder?.id || '1'; // fallback to default ID
      }
      // If actualParentId is still undefined, chrome.bookmarks.create will use default "Other Bookmarks", but we've set Bookmarks Bar

      // Create bookmark
      const newBookmark = await chrome.bookmarks.create({
        parentId: actualParentId, // If undefined, API uses default value
        title: bookmarkTitle || bookmarkUrl, // Ensure title is never empty
        url: bookmarkUrl,
      });

      // Get bookmark path
      const path = await getBookmarkFolderPath(newBookmark.id);

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(
              {
                success: true,
                message: 'Bookmark added successfully',
                bookmark: {
                  id: newBookmark.id,
                  title: newBookmark.title,
                  url: newBookmark.url,
                  dateAdded: newBookmark.dateAdded,
                  folderPath: path,
                },
                folderCreated: createFolder && parentId ? 'Folder created if necessary' : false,
              },
              null,
              2,
            ),
          },
        ],
        isError: false,
      };
    } catch (error) {
      console.error('Error adding bookmark:', error);
      const errorMessage = error instanceof Error ? error.message : String(error);

      // Provide more specific error messages for common error cases, such as trying to bookmark chrome:// URLs
      if (errorMessage.includes("Can't bookmark URLs of type")) {
        return createErrorResponse(
          `Error adding bookmark: Cannot bookmark this type of URL (e.g., chrome:// system pages). ${errorMessage}`,
        );
      }

      return createErrorResponse(`Error adding bookmark: ${errorMessage}`);
    }
  }
}

/**
 * Bookmark delete tool
 * Used to delete bookmarks in Chrome browser
 */
class BookmarkDeleteTool extends BaseBrowserToolExecutor {
  name = TOOL_NAMES.BROWSER.BOOKMARK_DELETE;

  /**
   * Execute delete bookmark operation
   */
  async execute(args: BookmarkDeleteToolParams): Promise<ToolResult> {
    const { bookmarkId, url, title } = args;

    console.log(`BookmarkDeleteTool: Deleting bookmark, options:`, args);

    if (!bookmarkId && !url) {
      return createErrorResponse('Must provide bookmark ID or URL to delete bookmark');
    }

    try {
      let bookmarksToDelete: chrome.bookmarks.BookmarkTreeNode[] = [];

      if (bookmarkId) {
        // Delete by ID
        try {
          const nodes = await chrome.bookmarks.get(bookmarkId);
          if (nodes && nodes.length > 0 && nodes[0].url) {
            bookmarksToDelete = nodes;
          } else {
            return createErrorResponse(
              `Bookmark with ID "${bookmarkId}" not found, or the ID does not correspond to a bookmark`,
            );
          }
        } catch (error) {
          return createErrorResponse(`Invalid bookmark ID: "${bookmarkId}"`);
        }
      } else if (url) {
        // Delete by URL
        bookmarksToDelete = await findBookmarksByUrl(url, title);
        if (bookmarksToDelete.length === 0) {
          return createErrorResponse(
            `No bookmark found with URL "${url}"${title ? ` (title contains: "${title}")` : ''}`,
          );
        }
      }

      // Delete found bookmarks
      const deletedBookmarks = [];
      const errors = [];

      for (const bookmark of bookmarksToDelete) {
        try {
          // Get path information before deletion
          const path = await getBookmarkFolderPath(bookmark.id);

          await chrome.bookmarks.remove(bookmark.id);

          deletedBookmarks.push({
            id: bookmark.id,
            title: bookmark.title,
            url: bookmark.url,
            folderPath: path,
          });
        } catch (error) {
          const errorMsg = error instanceof Error ? error.message : String(error);
          errors.push(
            `Failed to delete bookmark "${bookmark.title}" (ID: ${bookmark.id}): ${errorMsg}`,
          );
        }
      }

      if (deletedBookmarks.length === 0) {
        return createErrorResponse(`Failed to delete bookmarks: ${errors.join('; ')}`);
      }

      const result: any = {
        success: true,
        message: `Successfully deleted ${deletedBookmarks.length} bookmark(s)`,
        deletedBookmarks,
      };

      if (errors.length > 0) {
        result.partialSuccess = true;
        result.errors = errors;
      }

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
        isError: false,
      };
    } catch (error) {
      console.error('Error deleting bookmark:', error);
      return createErrorResponse(
        `Error deleting bookmark: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }
}

export const bookmarkSearchTool = new BookmarkSearchTool();
export const bookmarkAddTool = new BookmarkAddTool();
export const bookmarkDeleteTool = new BookmarkDeleteTool();

```

--------------------------------------------------------------------------------
/app/chrome-extension/workers/ort-wasm-simd-threaded.mjs:
--------------------------------------------------------------------------------

```
var ortWasmThreaded = (() => {
  var _scriptName = import.meta.url;
  
  return (
async function(moduleArg = {}) {
  var moduleRtn;

var f=moduleArg,aa,ba,ca=new Promise((a,b)=>{aa=a;ba=b}),da="object"==typeof window,k="undefined"!=typeof WorkerGlobalScope,l="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node&&"renderer"!=process.type,m=k&&self.name?.startsWith("em-pthread");if(l){const {createRequire:a}=await import("module");var require=a(import.meta.url),n=require("worker_threads");global.Worker=n.Worker;m=(k=!n.jb)&&"em-pthread"==n.workerData}
f.mountExternalData=(a,b)=>{a.startsWith("./")&&(a=a.substring(2));(f.Sa||(f.Sa=new Map)).set(a,b)};f.unmountExternalData=()=>{delete f.Sa};var SharedArrayBuffer=globalThis.SharedArrayBuffer??(new WebAssembly.Memory({initial:0,maximum:0,lb:!0})).buffer.constructor,ea=Object.assign({},f),fa="./this.program",q=(a,b)=>{throw b;},r="",ha,t;
if(l){var fs=require("fs"),ia=require("path");import.meta.url.startsWith("data:")||(r=ia.dirname(require("url").fileURLToPath(import.meta.url))+"/");t=a=>{a=u(a)?new URL(a):a;return fs.readFileSync(a)};ha=async a=>{a=u(a)?new URL(a):a;return fs.readFileSync(a,void 0)};!f.thisProgram&&1<process.argv.length&&(fa=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);q=(a,b)=>{process.exitCode=a;throw b;}}else if(da||k)k?r=self.location.href:"undefined"!=typeof document&&document.currentScript&&
(r=document.currentScript.src),_scriptName&&(r=_scriptName),r.startsWith("blob:")?r="":r=r.slice(0,r.replace(/[?#].*/,"").lastIndexOf("/")+1),l||(k&&(t=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ha=async a=>{if(u(a))return new Promise((c,d)=>{var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=()=>{200==e.status||0==e.status&&e.response?c(e.response):d(e.status)};e.onerror=d;e.send(null)});
var b=await fetch(a,{credentials:"same-origin"});if(b.ok)return b.arrayBuffer();throw Error(b.status+" : "+b.url);});var ja=console.log.bind(console),ka=console.error.bind(console);l&&(ja=(...a)=>fs.writeSync(1,a.join(" ")+"\n"),ka=(...a)=>fs.writeSync(2,a.join(" ")+"\n"));var la=ja,w=ka;Object.assign(f,ea);ea=null;var x=f.wasmBinary,y,ma,z=!1,A,B,na,oa,pa,qa,ra,C,sa,u=a=>a.startsWith("file://");function D(){y.buffer!=B.buffer&&E();return B}function F(){y.buffer!=B.buffer&&E();return na}
function ta(){y.buffer!=B.buffer&&E();return oa}function G(){y.buffer!=B.buffer&&E();return pa}function H(){y.buffer!=B.buffer&&E();return qa}function va(){y.buffer!=B.buffer&&E();return ra}function I(){y.buffer!=B.buffer&&E();return sa}
if(m){var wa;if(l){var xa=n.parentPort;xa.on("message",b=>onmessage({data:b}));Object.assign(globalThis,{self:global,postMessage:b=>xa.postMessage(b)})}var ya=!1;w=function(...b){b=b.join(" ");l?fs.writeSync(2,b+"\n"):console.error(b)};self.alert=function(...b){postMessage({Ra:"alert",text:b.join(" "),eb:J()})};self.onunhandledrejection=b=>{throw b.reason||b;};function a(b){try{var c=b.data,d=c.Ra;if("load"===d){let e=[];self.onmessage=g=>e.push(g);self.startWorker=()=>{postMessage({Ra:"loaded"});
for(let g of e)a(g);self.onmessage=a};for(const g of c.Za)if(!f[g]||f[g].proxy)f[g]=(...h)=>{postMessage({Ra:"callHandler",Ya:g,args:h})},"print"==g&&(la=f[g]),"printErr"==g&&(w=f[g]);y=c.gb;E();wa(c.hb)}else if("run"===d){za(c.Qa);Aa(c.Qa,0,0,1,0,0);Ba();Ca(c.Qa);ya||=!0;try{Da(c.bb,c.Va)}catch(e){if("unwind"!=e)throw e;}}else"setimmediate"!==c.target&&("checkMailbox"===d?ya&&K():d&&(w(`worker: received unknown command ${d}`),w(c)))}catch(e){throw Ea(),e;}}self.onmessage=a}
function E(){var a=y.buffer;f.HEAP8=B=new Int8Array(a);f.HEAP16=oa=new Int16Array(a);f.HEAPU8=na=new Uint8Array(a);f.HEAPU16=new Uint16Array(a);f.HEAP32=pa=new Int32Array(a);f.HEAPU32=qa=new Uint32Array(a);f.HEAPF32=ra=new Float32Array(a);f.HEAPF64=sa=new Float64Array(a);f.HEAP64=C=new BigInt64Array(a);f.HEAPU64=new BigUint64Array(a)}m||(y=new WebAssembly.Memory({initial:256,maximum:65536,shared:!0}),E());function Fa(){m?startWorker(f):L.$()}var M=0,N=null;
function Ga(){M--;if(0==M&&N){var a=N;N=null;a()}}function O(a){a="Aborted("+a+")";w(a);z=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ba(a);throw a;}var Ha;async function Ia(a){if(!x)try{var b=await ha(a);return new Uint8Array(b)}catch{}if(a==Ha&&x)a=new Uint8Array(x);else if(t)a=t(a);else throw"both async and sync fetching of the wasm failed";return a}
async function Ja(a,b){try{var c=await Ia(a);return await WebAssembly.instantiate(c,b)}catch(d){w(`failed to asynchronously prepare wasm: ${d}`),O(d)}}async function Ka(a){var b=Ha;if(!x&&"function"==typeof WebAssembly.instantiateStreaming&&!u(b)&&!l)try{var c=fetch(b,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(c,a)}catch(d){w(`wasm streaming compile failed: ${d}`),w("falling back to ArrayBuffer instantiation")}return Ja(b,a)}
function La(){Ma={j:Na,b:Oa,E:Pa,f:Qa,U:Ra,A:Sa,C:Ta,V:Ua,S:Va,L:Wa,R:Xa,n:Ya,B:Za,y:$a,T:ab,z:bb,_:cb,O:db,w:eb,F:fb,t:gb,i:hb,N:Ca,X:ib,I:jb,J:kb,K:lb,G:mb,H:nb,u:ob,q:pb,Z:qb,o:rb,k:sb,Y:tb,d:ub,W:vb,x:wb,c:xb,e:yb,h:zb,v:Ab,s:Bb,r:Cb,P:Db,Q:Eb,D:Fb,g:Gb,m:Hb,M:Ib,l:Jb,a:y,p:Kb};return{a:Ma}}
var Mb={802156:(a,b,c,d,e)=>{if("undefined"==typeof f||!f.Sa)return 1;a=Lb(Number(a>>>0));a.startsWith("./")&&(a=a.substring(2));a=f.Sa.get(a);if(!a)return 2;b=Number(b>>>0);c=Number(c>>>0);d=Number(d>>>0);if(b+c>a.byteLength)return 3;try{const g=a.subarray(b,b+c);switch(e){case 0:F().set(g,d>>>0);break;case 1:f.ib?f.ib(d,g):f.kb(d,g);break;default:return 4}return 0}catch{return 4}},802980:()=>"undefined"!==typeof wasmOffsetConverter};function Na(){return"undefined"!==typeof wasmOffsetConverter}
class Nb{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}}
var Ob=a=>{a.terminate();a.onmessage=()=>{}},Pb=[],Sb=a=>{0==Q.length&&(Qb(),Rb(Q[0]));var b=Q.pop();if(!b)return 6;R.push(b);S[a.Qa]=b;b.Qa=a.Qa;var c={Ra:"run",bb:a.ab,Va:a.Va,Qa:a.Qa};l&&b.unref();b.postMessage(c,a.Xa);return 0},T=0,V=(a,b,...c)=>{for(var d=2*c.length,e=Tb(),g=Ub(8*d),h=g>>>3,p=0;p<c.length;p++){var v=c[p];"bigint"==typeof v?(C[h+2*p]=1n,C[h+2*p+1]=v):(C[h+2*p]=0n,I()[h+2*p+1>>>0]=v)}a=Vb(a,0,d,g,b);U(e);return a};
function Kb(a){if(m)return V(0,1,a);A=a;if(!(0<T)){for(var b of R)Ob(b);for(b of Q)Ob(b);Q=[];R=[];S={};z=!0}q(a,new Nb(a))}function Wb(a){if(m)return V(1,0,a);Fb(a)}var Fb=a=>{A=a;if(m)throw Wb(a),"unwind";Kb(a)},Q=[],R=[],Xb=[],S={};function Yb(){for(var a=f.numThreads-1;a--;)Qb();Pb.unshift(()=>{M++;Zb(()=>Ga())})}var ac=a=>{var b=a.Qa;delete S[b];Q.push(a);R.splice(R.indexOf(a),1);a.Qa=0;$b(b)};function Ba(){Xb.forEach(a=>a())}
var Rb=a=>new Promise(b=>{a.onmessage=g=>{g=g.data;var h=g.Ra;if(g.Ta&&g.Ta!=J()){var p=S[g.Ta];p?p.postMessage(g,g.Xa):w(`Internal error! Worker sent a message "${h}" to target pthread ${g.Ta}, but that thread no longer exists!`)}else if("checkMailbox"===h)K();else if("spawnThread"===h)Sb(g);else if("cleanupThread"===h)ac(S[g.cb]);else if("loaded"===h)a.loaded=!0,l&&!a.Qa&&a.unref(),b(a);else if("alert"===h)alert(`Thread ${g.eb}: ${g.text}`);else if("setimmediate"===g.target)a.postMessage(g);else if("callHandler"===
h)f[g.Ya](...g.args);else h&&w(`worker sent an unknown command ${h}`)};a.onerror=g=>{w(`${"worker sent an error!"} ${g.filename}:${g.lineno}: ${g.message}`);throw g;};l&&(a.on("message",g=>a.onmessage({data:g})),a.on("error",g=>a.onerror(g)));var c=[],d=[],e;for(e of d)f.propertyIsEnumerable(e)&&c.push(e);a.postMessage({Ra:"load",Za:c,gb:y,hb:ma})});function Zb(a){m?a():Promise.all(Q.map(Rb)).then(a)}
function Qb(){var a=new Worker(new URL(import.meta.url),{type:"module",workerData:"em-pthread",name:"em-pthread"});Q.push(a)}var za=a=>{E();var b=H()[a+52>>>2>>>0];a=H()[a+56>>>2>>>0];bc(b,b-a);U(b)},W=[],cc,Da=(a,b)=>{T=0;var c=W[a];c||(a>=W.length&&(W.length=a+1),W[a]=c=cc.get(a));a=c(b);0<T?A=a:dc(a)};class ec{constructor(a){this.Ua=a-24}}var fc=0,gc=0;
function Oa(a,b,c){a>>>=0;var d=new ec(a);b>>>=0;c>>>=0;H()[d.Ua+16>>>2>>>0]=0;H()[d.Ua+4>>>2>>>0]=b;H()[d.Ua+8>>>2>>>0]=c;fc=a;gc++;throw fc;}function hc(a,b,c,d){return m?V(2,1,a,b,c,d):Pa(a,b,c,d)}function Pa(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;if("undefined"==typeof SharedArrayBuffer)return 6;var e=[];if(m&&0===e.length)return hc(a,b,c,d);a={ab:c,Qa:a,Va:d,Xa:e};return m?(a.Ra="spawnThread",postMessage(a,e),0):Sb(a)}
var ic="undefined"!=typeof TextDecoder?new TextDecoder:void 0,jc=(a,b=0,c=NaN)=>{b>>>=0;var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&ic)return ic.decode(a.buffer instanceof ArrayBuffer?a.subarray(b,c):a.slice(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var g=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|g);else{var h=a[b++]&63;e=224==(e&240)?(e&15)<<12|g<<6|h:(e&7)<<18|g<<12|h<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|
e&1023))}}else d+=String.fromCharCode(e)}return d},Lb=(a,b)=>(a>>>=0)?jc(F(),a,b):"";function Qa(a,b,c){return m?V(3,1,a,b,c):0}function Ra(a,b){if(m)return V(4,1,a,b)}
var X=(a,b,c)=>{var d=F();b>>>=0;if(0<c){var e=b;c=b+c-1;for(var g=0;g<a.length;++g){var h=a.charCodeAt(g);if(55296<=h&&57343>=h){var p=a.charCodeAt(++g);h=65536+((h&1023)<<10)|p&1023}if(127>=h){if(b>=c)break;d[b++>>>0]=h}else{if(2047>=h){if(b+1>=c)break;d[b++>>>0]=192|h>>6}else{if(65535>=h){if(b+2>=c)break;d[b++>>>0]=224|h>>12}else{if(b+3>=c)break;d[b++>>>0]=240|h>>18;d[b++>>>0]=128|h>>12&63}d[b++>>>0]=128|h>>6&63}d[b++>>>0]=128|h&63}}d[b>>>0]=0;a=b-e}else a=0;return a};
function Sa(a,b){if(m)return V(5,1,a,b)}function Ta(a,b,c){if(m)return V(6,1,a,b,c)}function Ua(a,b,c){return m?V(7,1,a,b,c):0}function Va(a,b){if(m)return V(8,1,a,b)}function Wa(a,b,c){if(m)return V(9,1,a,b,c)}function Xa(a,b,c,d){if(m)return V(10,1,a,b,c,d)}function Ya(a,b,c,d){if(m)return V(11,1,a,b,c,d)}function Za(a,b,c,d){if(m)return V(12,1,a,b,c,d)}function $a(a){if(m)return V(13,1,a)}function ab(a,b){if(m)return V(14,1,a,b)}function bb(a,b,c){if(m)return V(15,1,a,b,c)}var cb=()=>O("");
function db(a){Aa(a>>>0,!k,1,!da,131072,!1);Ba()}var kc=a=>{if(!z)try{if(a(),!(0<T))try{m?dc(A):Fb(A)}catch(b){b instanceof Nb||"unwind"==b||q(1,b)}}catch(b){b instanceof Nb||"unwind"==b||q(1,b)}};function Ca(a){a>>>=0;"function"===typeof Atomics.fb&&(Atomics.fb(G(),a>>>2,a).value.then(K),a+=128,Atomics.store(G(),a>>>2,1))}var K=()=>{var a=J();a&&(Ca(a),kc(lc))};function eb(a,b){a>>>=0;a==b>>>0?setTimeout(K):m?postMessage({Ta:a,Ra:"checkMailbox"}):(a=S[a])&&a.postMessage({Ra:"checkMailbox"})}
var mc=[];function fb(a,b,c,d,e){b>>>=0;d/=2;mc.length=d;c=e>>>0>>>3;for(e=0;e<d;e++)mc[e]=C[c+2*e]?C[c+2*e+1]:I()[c+2*e+1>>>0];return(b?Mb[b]:nc[a])(...mc)}var gb=()=>{T=0};function hb(a){a>>>=0;m?postMessage({Ra:"cleanupThread",cb:a}):ac(S[a])}function ib(a){l&&S[a>>>0].ref()}
function jb(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);G()[b>>>2>>>0]=a.getUTCSeconds();G()[b+4>>>2>>>0]=a.getUTCMinutes();G()[b+8>>>2>>>0]=a.getUTCHours();G()[b+12>>>2>>>0]=a.getUTCDate();G()[b+16>>>2>>>0]=a.getUTCMonth();G()[b+20>>>2>>>0]=a.getUTCFullYear()-1900;G()[b+24>>>2>>>0]=a.getUTCDay();a=(a.getTime()-Date.UTC(a.getUTCFullYear(),0,1,0,0,0,0))/864E5|0;G()[b+28>>>2>>>0]=a}
var oc=a=>0===a%4&&(0!==a%100||0===a%400),pc=[0,31,60,91,121,152,182,213,244,274,305,335],qc=[0,31,59,90,120,151,181,212,243,273,304,334];
function kb(a,b){a=-9007199254740992>a||9007199254740992<a?NaN:Number(a);b>>>=0;a=new Date(1E3*a);G()[b>>>2>>>0]=a.getSeconds();G()[b+4>>>2>>>0]=a.getMinutes();G()[b+8>>>2>>>0]=a.getHours();G()[b+12>>>2>>>0]=a.getDate();G()[b+16>>>2>>>0]=a.getMonth();G()[b+20>>>2>>>0]=a.getFullYear()-1900;G()[b+24>>>2>>>0]=a.getDay();var c=(oc(a.getFullYear())?pc:qc)[a.getMonth()]+a.getDate()-1|0;G()[b+28>>>2>>>0]=c;G()[b+36>>>2>>>0]=-(60*a.getTimezoneOffset());c=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();
var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();a=(c!=d&&a.getTimezoneOffset()==Math.min(d,c))|0;G()[b+32>>>2>>>0]=a}
function lb(a){a>>>=0;var b=new Date(G()[a+20>>>2>>>0]+1900,G()[a+16>>>2>>>0],G()[a+12>>>2>>>0],G()[a+8>>>2>>>0],G()[a+4>>>2>>>0],G()[a>>>2>>>0],0),c=G()[a+32>>>2>>>0],d=b.getTimezoneOffset(),e=(new Date(b.getFullYear(),6,1)).getTimezoneOffset(),g=(new Date(b.getFullYear(),0,1)).getTimezoneOffset(),h=Math.min(g,e);0>c?G()[a+32>>>2>>>0]=Number(e!=g&&h==d):0<c!=(h==d)&&(e=Math.max(g,e),b.setTime(b.getTime()+6E4*((0<c?h:e)-d)));G()[a+24>>>2>>>0]=b.getDay();c=(oc(b.getFullYear())?pc:qc)[b.getMonth()]+
b.getDate()-1|0;G()[a+28>>>2>>>0]=c;G()[a>>>2>>>0]=b.getSeconds();G()[a+4>>>2>>>0]=b.getMinutes();G()[a+8>>>2>>>0]=b.getHours();G()[a+12>>>2>>>0]=b.getDate();G()[a+16>>>2>>>0]=b.getMonth();G()[a+20>>>2>>>0]=b.getYear();a=b.getTime();return BigInt(isNaN(a)?-1:a/1E3)}function mb(a,b,c,d,e,g,h){return m?V(16,1,a,b,c,d,e,g,h):-52}function nb(a,b,c,d,e,g){if(m)return V(17,1,a,b,c,d,e,g)}var Y={},xb=()=>performance.timeOrigin+performance.now();
function ob(a,b){if(m)return V(18,1,a,b);Y[a]&&(clearTimeout(Y[a].id),delete Y[a]);if(!b)return 0;var c=setTimeout(()=>{delete Y[a];kc(()=>rc(a,performance.timeOrigin+performance.now()))},b);Y[a]={id:c,mb:b};return 0}
function pb(a,b,c,d){a>>>=0;b>>>=0;c>>>=0;d>>>=0;var e=(new Date).getFullYear(),g=(new Date(e,0,1)).getTimezoneOffset();e=(new Date(e,6,1)).getTimezoneOffset();var h=Math.max(g,e);H()[a>>>2>>>0]=60*h;G()[b>>>2>>>0]=Number(g!=e);b=p=>{var v=Math.abs(p);return`UTC${0<=p?"-":"+"}${String(Math.floor(v/60)).padStart(2,"0")}${String(v%60).padStart(2,"0")}`};a=b(g);b=b(e);e<g?(X(a,c,17),X(b,d,17)):(X(a,d,17),X(b,c,17))}var tb=()=>Date.now(),sc=1;
function qb(a,b,c){if(!(0<=a&&3>=a))return 28;if(0===a)a=Date.now();else if(sc)a=performance.timeOrigin+performance.now();else return 52;C[c>>>0>>>3]=BigInt(Math.round(1E6*a));return 0}var tc=[];function rb(a,b,c){a>>>=0;b>>>=0;c>>>=0;tc.length=0;for(var d;d=F()[b++>>>0];){var e=105!=d;e&=112!=d;c+=e&&c%8?4:0;tc.push(112==d?H()[c>>>2>>>0]:106==d?C[c>>>3]:105==d?G()[c>>>2>>>0]:I()[c>>>3>>>0]);c+=e?8:4}return Mb[a](...tc)}var sb=()=>{};function ub(a,b){return w(Lb(a>>>0,b>>>0))}
var vb=()=>{T+=1;throw"unwind";};function wb(){return 4294901760}var yb=()=>l?require("os").cpus().length:navigator.hardwareConcurrency;function zb(){O("Cannot use emscripten_pc_get_function without -sUSE_OFFSET_CONVERTER");return 0}
function Ab(a){a>>>=0;var b=F().length;if(a<=b||4294901760<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);a:{d=(Math.min(4294901760,65536*Math.ceil(Math.max(a,d)/65536))-y.buffer.byteLength+65535)/65536|0;try{y.grow(d);E();var e=1;break a}catch(g){}e=void 0}if(e)return!0}return!1}var uc=()=>{O("Cannot use convertFrameToPC (needed by __builtin_return_address) without -sUSE_OFFSET_CONVERTER");return 0},Z={},vc=a=>{a.forEach(b=>{var c=uc();c&&(Z[c]=b)})};
function Bb(){var a=Error().stack.toString().split("\n");"Error"==a[0]&&a.shift();vc(a);Z.Wa=uc();Z.$a=a;return Z.Wa}function Cb(a,b,c){a>>>=0;b>>>=0;if(Z.Wa==a)var d=Z.$a;else d=Error().stack.toString().split("\n"),"Error"==d[0]&&d.shift(),vc(d);for(var e=3;d[e]&&uc()!=a;)++e;for(a=0;a<c&&d[a+e];++a)G()[b+4*a>>>2>>>0]=uc();return a}
var wc={},yc=()=>{if(!xc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:fa||"./this.program"},b;for(b in wc)void 0===wc[b]?delete a[b]:a[b]=wc[b];var c=[];for(b in a)c.push(`${b}=${a[b]}`);xc=c}return xc},xc;
function Db(a,b){if(m)return V(19,1,a,b);a>>>=0;b>>>=0;var c=0;yc().forEach((d,e)=>{var g=b+c;e=H()[a+4*e>>>2>>>0]=g;for(g=0;g<d.length;++g)D()[e++>>>0]=d.charCodeAt(g);D()[e>>>0]=0;c+=d.length+1});return 0}function Eb(a,b){if(m)return V(20,1,a,b);a>>>=0;b>>>=0;var c=yc();H()[a>>>2>>>0]=c.length;var d=0;c.forEach(e=>d+=e.length+1);H()[b>>>2>>>0]=d;return 0}function Gb(a){return m?V(21,1,a):52}function Hb(a,b,c,d){return m?V(22,1,a,b,c,d):52}function Ib(a,b,c,d){return m?V(23,1,a,b,c,d):70}
var zc=[null,[],[]];function Jb(a,b,c,d){if(m)return V(24,1,a,b,c,d);b>>>=0;c>>>=0;d>>>=0;for(var e=0,g=0;g<c;g++){var h=H()[b>>>2>>>0],p=H()[b+4>>>2>>>0];b+=8;for(var v=0;v<p;v++){var P=F()[h+v>>>0],ua=zc[a];0===P||10===P?((1===a?la:w)(jc(ua)),ua.length=0):ua.push(P)}e+=p}H()[d>>>2>>>0]=e;return 0}m||Yb();var nc=[Kb,Wb,hc,Qa,Ra,Sa,Ta,Ua,Va,Wa,Xa,Ya,Za,$a,ab,bb,mb,nb,ob,Db,Eb,Gb,Hb,Ib,Jb],Ma,L;
(async function(){function a(d,e){L=d.exports;L=Ac();Xb.push(L.Da);cc=L.Ea;ma=e;Ga();return L}M++;var b=La();if(f.instantiateWasm)return new Promise(d=>{f.instantiateWasm(b,(e,g)=>{a(e,g);d(e.exports)})});if(m)return new Promise(d=>{wa=e=>{var g=new WebAssembly.Instance(e,La());d(a(g,e))}});Ha??=f.locateFile?f.locateFile?f.locateFile("ort-wasm-simd-threaded.wasm",r):r+"ort-wasm-simd-threaded.wasm":(new URL("ort-wasm-simd-threaded.wasm",import.meta.url)).href;try{var c=await Ka(b);return a(c.instance,
c.module)}catch(d){return ba(d),Promise.reject(d)}})();f._OrtInit=(a,b)=>(f._OrtInit=L.aa)(a,b);f._OrtGetLastError=(a,b)=>(f._OrtGetLastError=L.ba)(a,b);f._OrtCreateSessionOptions=(a,b,c,d,e,g,h,p,v,P)=>(f._OrtCreateSessionOptions=L.ca)(a,b,c,d,e,g,h,p,v,P);f._OrtAppendExecutionProvider=(a,b,c,d,e)=>(f._OrtAppendExecutionProvider=L.da)(a,b,c,d,e);f._OrtAddFreeDimensionOverride=(a,b,c)=>(f._OrtAddFreeDimensionOverride=L.ea)(a,b,c);
f._OrtAddSessionConfigEntry=(a,b,c)=>(f._OrtAddSessionConfigEntry=L.fa)(a,b,c);f._OrtReleaseSessionOptions=a=>(f._OrtReleaseSessionOptions=L.ga)(a);f._OrtCreateSession=(a,b,c)=>(f._OrtCreateSession=L.ha)(a,b,c);f._OrtReleaseSession=a=>(f._OrtReleaseSession=L.ia)(a);f._OrtGetInputOutputCount=(a,b,c)=>(f._OrtGetInputOutputCount=L.ja)(a,b,c);f._OrtGetInputOutputMetadata=(a,b,c,d)=>(f._OrtGetInputOutputMetadata=L.ka)(a,b,c,d);f._OrtFree=a=>(f._OrtFree=L.la)(a);
f._OrtCreateTensor=(a,b,c,d,e,g)=>(f._OrtCreateTensor=L.ma)(a,b,c,d,e,g);f._OrtGetTensorData=(a,b,c,d,e)=>(f._OrtGetTensorData=L.na)(a,b,c,d,e);f._OrtReleaseTensor=a=>(f._OrtReleaseTensor=L.oa)(a);f._OrtCreateRunOptions=(a,b,c,d)=>(f._OrtCreateRunOptions=L.pa)(a,b,c,d);f._OrtAddRunConfigEntry=(a,b,c)=>(f._OrtAddRunConfigEntry=L.qa)(a,b,c);f._OrtReleaseRunOptions=a=>(f._OrtReleaseRunOptions=L.ra)(a);f._OrtCreateBinding=a=>(f._OrtCreateBinding=L.sa)(a);
f._OrtBindInput=(a,b,c)=>(f._OrtBindInput=L.ta)(a,b,c);f._OrtBindOutput=(a,b,c,d)=>(f._OrtBindOutput=L.ua)(a,b,c,d);f._OrtClearBoundOutputs=a=>(f._OrtClearBoundOutputs=L.va)(a);f._OrtReleaseBinding=a=>(f._OrtReleaseBinding=L.wa)(a);f._OrtRunWithBinding=(a,b,c,d,e)=>(f._OrtRunWithBinding=L.xa)(a,b,c,d,e);f._OrtRun=(a,b,c,d,e,g,h,p)=>(f._OrtRun=L.ya)(a,b,c,d,e,g,h,p);f._OrtEndProfiling=a=>(f._OrtEndProfiling=L.za)(a);var J=()=>(J=L.Aa)();f._free=a=>(f._free=L.Ba)(a);f._malloc=a=>(f._malloc=L.Ca)(a);
var Aa=(a,b,c,d,e,g)=>(Aa=L.Fa)(a,b,c,d,e,g),Ea=()=>(Ea=L.Ga)(),Vb=(a,b,c,d,e)=>(Vb=L.Ha)(a,b,c,d,e),$b=a=>($b=L.Ia)(a),dc=a=>(dc=L.Ja)(a),rc=(a,b)=>(rc=L.Ka)(a,b),lc=()=>(lc=L.La)(),bc=(a,b)=>(bc=L.Ma)(a,b),U=a=>(U=L.Na)(a),Ub=a=>(Ub=L.Oa)(a),Tb=()=>(Tb=L.Pa)();function Ac(){var a=L;a=Object.assign({},a);var b=d=>()=>d()>>>0,c=d=>e=>d(e)>>>0;a.Aa=b(a.Aa);a.Ca=c(a.Ca);a.Oa=c(a.Oa);a.Pa=b(a.Pa);a.__cxa_get_exception_ptr=c(a.__cxa_get_exception_ptr);return a}f.stackSave=()=>Tb();f.stackRestore=a=>U(a);
f.stackAlloc=a=>Ub(a);f.setValue=function(a,b,c="i8"){c.endsWith("*")&&(c="*");switch(c){case "i1":D()[a>>>0]=b;break;case "i8":D()[a>>>0]=b;break;case "i16":ta()[a>>>1>>>0]=b;break;case "i32":G()[a>>>2>>>0]=b;break;case "i64":C[a>>>3]=BigInt(b);break;case "float":va()[a>>>2>>>0]=b;break;case "double":I()[a>>>3>>>0]=b;break;case "*":H()[a>>>2>>>0]=b;break;default:O(`invalid type for setValue: ${c}`)}};
f.getValue=function(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return D()[a>>>0];case "i8":return D()[a>>>0];case "i16":return ta()[a>>>1>>>0];case "i32":return G()[a>>>2>>>0];case "i64":return C[a>>>3];case "float":return va()[a>>>2>>>0];case "double":return I()[a>>>3>>>0];case "*":return H()[a>>>2>>>0];default:O(`invalid type for getValue: ${b}`)}};f.UTF8ToString=Lb;f.stringToUTF8=X;
f.lengthBytesUTF8=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b};function Bc(){if(0<M)N=Bc;else if(m)aa(f),Fa();else{for(;0<Pb.length;)Pb.shift()(f);0<M?N=Bc:(f.calledRun=!0,z||(Fa(),aa(f)))}}Bc();f.PTR_SIZE=4;moduleRtn=ca;


  return moduleRtn;
}
);
})();
export default ortWasmThreaded;
var isPthread = globalThis.self?.name?.startsWith('em-pthread');
var isNode = typeof globalThis.process?.versions?.node == 'string';
if (isNode) isPthread = (await import('worker_threads')).workerData === 'em-pthread';

// When running as a pthread, construct a new instance on startup
isPthread && ortWasmThreaded();

```
Page 3/8FirstPrevNextLast