add duplicate load guard so extension defers to page native uncloseai.js

This commit is contained in:
russell@unturf.com 2026-02-23 10:57:02 -05:00
parent 3f190723b4
commit 6417d3853f

View file

@ -22,6 +22,12 @@ import * as TTS from "./src/tts.js";
import * as UI from "./src/ui.js";
import { UncloseVault } from "./src/vault.js";
// Duplicate load guard: if a page already includes uncloseai.js natively,
// the extension should not re-initialize. Module exports remain available,
// but window globals, event listeners, and DOM side effects only run once.
const __alreadyLoaded = window.__UNCLOSEAI_LOADED__ === true;
window.__UNCLOSEAI_LOADED__ = true;
// -------------------------
// Vault Support
// -------------------------
@ -120,90 +126,102 @@ export { chatHistory } from "./src/chat.js";
// -------------------------
// Global Scope Exports (for backward compatibility)
// Side effects only run once: if the page already loaded uncloseai.js,
// the extension copy skips initialization but module exports still work.
// -------------------------
// Export functions to global scope for HTML onclick handlers
window.handleUserInput = Chat.handleUserInput;
window.readPageWithHermes = PageReader.readPageWithHermes;
window.sendMessage = Chat.sendMessage;
window.speakText = TTS.speakText;
window.uploadFile = FileUpload.uploadFile;
window.openTTSModal = UI.openTTSModal;
window.openTranslateModal = UI.openTranslateModal;
window.toggleUncloseaiEmbeddedModal = async () =>
await UI.toggleUncloseaiEmbeddedModal();
window.extractWebpageContent = Content.extractWebpageContent;
window.getSelectedModel = Models.getSelectedModel;
window.getSelectedModelEndpoint = Models.getSelectedModelEndpoint;
window.showProgressIndicator = FileUpload.showProgressIndicator;
window.hideProgressIndicator = FileUpload.hideProgressIndicator;
window.handleTTSFromElement = UI.handleTTSFromElement;
window.handleUploadFromElement = UI.handleUploadFromElement;
window.handleSmartTranslate = UI.handleSmartTranslate;
window.UncloseVault = UncloseVault;
if (!__alreadyLoaded) {
// Export functions to global scope for HTML onclick handlers
window.handleUserInput = Chat.handleUserInput;
window.readPageWithHermes = PageReader.readPageWithHermes;
window.sendMessage = Chat.sendMessage;
window.speakText = TTS.speakText;
window.uploadFile = FileUpload.uploadFile;
window.openTTSModal = UI.openTTSModal;
window.openTranslateModal = UI.openTranslateModal;
window.toggleUncloseaiEmbeddedModal = async () =>
await UI.toggleUncloseaiEmbeddedModal();
window.extractWebpageContent = Content.extractWebpageContent;
window.getSelectedModel = Models.getSelectedModel;
window.getSelectedModelEndpoint = Models.getSelectedModelEndpoint;
window.showProgressIndicator = FileUpload.showProgressIndicator;
window.hideProgressIndicator = FileUpload.hideProgressIndicator;
window.handleTTSFromElement = UI.handleTTSFromElement;
window.handleUploadFromElement = UI.handleUploadFromElement;
window.handleSmartTranslate = UI.handleSmartTranslate;
window.UncloseVault = UncloseVault;
// Initialize on page load
window.addEventListener("load", async () => {
console.log("uncloseai.js: window.onload event fired.");
// Initialize on page load
window.addEventListener("load", async () => {
console.log("uncloseai.js: window.onload event fired.");
// Load CryptoJS and initialize vault early
await ensureCryptoJS();
if (UncloseVault.isAvailable()) {
const restored = UncloseVault.init();
console.log("uncloseai.js: Vault initialized, session restored:", restored);
}
// Check skip init flag first
if (window.UNCLOSEAI_SKIP_INIT === true) {
console.log(
"uncloseai.js: Skipping full initialization as requested by flag.",
);
console.log(
"uncloseai.js: Creating floating button only for preview windows.",
);
// Still create floating button for preview windows, but skip other initialization
const SHOW_FLOATING_BUTTON = window.UNCLOSEAI_FLOATING_BUTTON !== false;
if (SHOW_FLOATING_BUTTON) {
UI.createFloatingAIButton();
// Load CryptoJS and initialize vault early
await ensureCryptoJS();
if (UncloseVault.isAvailable()) {
const restored = UncloseVault.init();
console.log(
"uncloseai.js: Vault initialized, session restored:",
restored,
);
}
return;
}
UI.initializeSystem();
});
// Check skip init flag first
if (window.UNCLOSEAI_SKIP_INIT === true) {
console.log(
"uncloseai.js: Skipping full initialization as requested by flag.",
);
console.log(
"uncloseai.js: Creating floating button only for preview windows.",
);
// Export helper functions for class-based integrations
window.handleCustomChat = async (button) => {
const container = button.parentElement;
const input = container.querySelector("[data-chat-input]");
const chatBox = container.querySelector("[data-chat-box]");
const message = input.value.trim();
if (!message) return;
// Add user message
chatBox.innerHTML += `<div style="margin: 2px 0; padding: 2px; background: #e3f2fd; border-radius: 2px; font-size: 0.9em;"><strong>You:</strong> ${message}</div>`;
input.value = "";
// Add AI thinking indicator
const thinkingDiv = document.createElement("div");
thinkingDiv.style.cssText =
"margin: 2px 0; padding: 2px; background: #f5f5f5; border-radius: 2px; font-size: 0.9em;";
thinkingDiv.innerHTML = "<strong>AI:</strong> <em>thinking...</em>";
chatBox.appendChild(thinkingDiv);
chatBox.scrollTop = chatBox.scrollHeight;
try {
let response = "";
for await (const chunk of Chat.sendMessage(message)) {
response += chunk;
thinkingDiv.innerHTML = `<strong>AI:</strong> ${response}`;
chatBox.scrollTop = chatBox.scrollHeight;
// Still create floating button for preview windows, but skip other initialization
const SHOW_FLOATING_BUTTON =
window.UNCLOSEAI_FLOATING_BUTTON !== false;
if (SHOW_FLOATING_BUTTON) {
UI.createFloatingAIButton();
}
return;
}
} catch (error) {
thinkingDiv.innerHTML = `<strong>Error:</strong> ${error.message}`;
}
};
console.log("uncloseai.js: Modular version loaded successfully");
UI.initializeSystem();
});
// Export helper functions for class-based integrations
window.handleCustomChat = async (button) => {
const container = button.parentElement;
const input = container.querySelector("[data-chat-input]");
const chatBox = container.querySelector("[data-chat-box]");
const message = input.value.trim();
if (!message) return;
// Add user message
chatBox.innerHTML += `<div style="margin: 2px 0; padding: 2px; background: #e3f2fd; border-radius: 2px; font-size: 0.9em;"><strong>You:</strong> ${message}</div>`;
input.value = "";
// Add AI thinking indicator
const thinkingDiv = document.createElement("div");
thinkingDiv.style.cssText =
"margin: 2px 0; padding: 2px; background: #f5f5f5; border-radius: 2px; font-size: 0.9em;";
thinkingDiv.innerHTML = "<strong>AI:</strong> <em>thinking...</em>";
chatBox.appendChild(thinkingDiv);
chatBox.scrollTop = chatBox.scrollHeight;
try {
let response = "";
for await (const chunk of Chat.sendMessage(message)) {
response += chunk;
thinkingDiv.innerHTML = `<strong>AI:</strong> ${response}`;
chatBox.scrollTop = chatBox.scrollHeight;
}
} catch (error) {
thinkingDiv.innerHTML = `<strong>Error:</strong> ${error.message}`;
}
};
console.log("uncloseai.js: Modular version loaded successfully");
} else {
console.log(
"uncloseai.js: page already has uncloseai loaded, skipping duplicate initialization",
);
}