import {
DOMAINS,
ELEMENT_IDS,
MESSAGE_TYPES,
POSTHOG_EVENT_KEY,
STORAGE_KEYS,
UI_CONFIG,
} from "../../utils/constants";
import { createT3InputBarElement, DOMUtils } from "../../utils/ui-components";
let t3DebounceTimeout: NodeJS.Timeout | null = null;
let t3RouteObserver: MutationObserver | null = null;
let t3UrlCheckInterval: NodeJS.Timeout | null = null;
let t3ObserverThrottle: NodeJS.Timeout | null = null;
export function initializeT3() {
if (!DOMUtils.isOnDomain(DOMAINS.T3)) {
return;
}
if (document.body.hasAttribute("data-t3-initialized")) {
return;
}
setTimeout(() => {
console.log("Adding supermemory icon to T3 input");
addSupermemoryIconToT3Input();
setupT3AutoFetch();
}, 2000);
setupT3PromptCapture();
setupT3RouteChangeDetection();
document.body.setAttribute("data-t3-initialized", "true");
}
function setupT3RouteChangeDetection() {
if (t3RouteObserver) {
t3RouteObserver.disconnect();
}
if (t3UrlCheckInterval) {
clearInterval(t3UrlCheckInterval);
}
if (t3ObserverThrottle) {
clearTimeout(t3ObserverThrottle);
t3ObserverThrottle = null;
}
let currentUrl = window.location.href;
const checkForRouteChange = () => {
if (window.location.href !== currentUrl) {
currentUrl = window.location.href;
console.log("T3 route changed, re-adding supermemory icon");
setTimeout(() => {
addSupermemoryIconToT3Input();
setupT3AutoFetch();
}, 1000);
}
};
t3UrlCheckInterval = setInterval(checkForRouteChange, 2000);
t3RouteObserver = new MutationObserver((mutations) => {
if (t3ObserverThrottle) {
return;
}
let shouldRecheck = false;
mutations.forEach((mutation) => {
if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element;
if (
element.querySelector?.("textarea") ||
element.querySelector?.('div[contenteditable="true"]') ||
element.matches?.("textarea") ||
element.matches?.('div[contenteditable="true"]')
) {
shouldRecheck = true;
}
}
});
}
});
if (shouldRecheck) {
t3ObserverThrottle = setTimeout(() => {
try {
t3ObserverThrottle = null;
addSupermemoryIconToT3Input();
setupT3AutoFetch();
} catch (error) {
console.error("Error in T3 observer callback:", error);
}
}, 300);
}
});
try {
t3RouteObserver.observe(document.body, {
childList: true,
subtree: true,
});
} catch (error) {
console.error("Failed to set up T3 route observer:", error);
if (t3UrlCheckInterval) {
clearInterval(t3UrlCheckInterval);
}
t3UrlCheckInterval = setInterval(checkForRouteChange, 1000);
}
}
function addSupermemoryIconToT3Input() {
const targetContainers = document.querySelectorAll(
".flex.min-w-0.items-center.gap-2.overflow-hidden",
);
targetContainers.forEach((container) => {
if (container.hasAttribute("data-supermemory-icon-added")) {
return;
}
const existingIcon = container.querySelector(
`#${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}`,
);
if (existingIcon) {
container.setAttribute("data-supermemory-icon-added", "true");
return;
}
const supermemoryIcon = createT3InputBarElement(async () => {
await getRelatedMemoriesForT3(
POSTHOG_EVENT_KEY.T3_CHAT_MEMORIES_SEARCHED,
);
});
supermemoryIcon.id = `${ELEMENT_IDS.T3_INPUT_BAR_ELEMENT}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
container.setAttribute("data-supermemory-icon-added", "true");
container.insertBefore(supermemoryIcon, container.firstChild);
});
}
async function getRelatedMemoriesForT3(actionSource: string) {
try {
let userQuery = "";
const supermemoryContainer = document.querySelector(
'[data-supermemory-icon-added="true"]',
);
if (
supermemoryContainer?.parentElement?.parentElement?.previousElementSibling
) {
const textareaElement =
supermemoryContainer.parentElement.parentElement.previousElementSibling.querySelector(
"textarea",
);
userQuery = textareaElement?.value || "";
}
if (!userQuery.trim()) {
const textareaElement = document.querySelector(
'div[contenteditable="true"]',
) as HTMLElement;
userQuery =
textareaElement?.innerText || textareaElement?.textContent || "";
}
if (!userQuery.trim()) {
const textareas = document.querySelectorAll("textarea");
for (const textarea of textareas) {
const text = (textarea as HTMLTextAreaElement).value;
if (text?.trim()) {
userQuery = text.trim();
break;
}
}
}
console.log("T3 query extracted:", userQuery);
if (!userQuery.trim()) {
console.log("No query text found for T3");
return;
}
const icon = document.querySelector('[id*="sm-t3-input-bar-element"]');
const iconElement = icon as HTMLElement;
if (!iconElement) {
console.warn("T3 icon element not found, cannot update feedback");
return;
}
updateT3IconFeedback("Searching memories...", iconElement);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(
() => reject(new Error("Memory search timeout")),
UI_CONFIG.API_REQUEST_TIMEOUT,
),
);
const response = await Promise.race([
browser.runtime.sendMessage({
action: MESSAGE_TYPES.GET_RELATED_MEMORIES,
data: userQuery,
actionSource: actionSource,
}),
timeoutPromise,
]);
console.log("T3 memories response:", response);
if (response?.success && response?.data) {
let textareaElement = null;
const supermemoryContainer = document.querySelector(
'[data-supermemory-icon-added="true"]',
);
if (
supermemoryContainer?.parentElement?.parentElement
?.previousElementSibling
) {
textareaElement =
supermemoryContainer.parentElement.parentElement.previousElementSibling.querySelector(
"textarea",
);
}
if (!textareaElement) {
textareaElement = document.querySelector(
'div[contenteditable="true"]',
) as HTMLElement;
}
if (textareaElement) {
if (textareaElement.tagName === "TEXTAREA") {
(textareaElement as HTMLTextAreaElement).dataset.supermemories =
`
Supermemories of user (only for the reference): ${response.data}`;
} else {
(textareaElement as HTMLElement).dataset.supermemories =
`
Supermemories of user (only for the reference): ${response.data}`;
}
iconElement.dataset.memoriesData = response.data;
updateT3IconFeedback("Included Memories", iconElement);
} else {
console.warn("T3 input area not found after successful memory fetch");
updateT3IconFeedback("Memories found", iconElement);
}
} else {
console.warn("No memories found or API response invalid for T3");
updateT3IconFeedback("No memories found", iconElement);
}
} catch (error) {
console.error("Error getting related memories for T3:", error);
try {
const icon = document.querySelector(
'[id*="sm-t3-input-bar-element"]',
) as HTMLElement;
if (icon) {
updateT3IconFeedback("Error fetching memories", icon);
}
} catch (feedbackError) {
console.error("Failed to update T3 error feedback:", feedbackError);
}
}
}
function updateT3IconFeedback(
message: string,
iconElement: HTMLElement,
resetAfter = 0,
) {
if (!iconElement.dataset.originalHtml) {
iconElement.dataset.originalHtml = iconElement.innerHTML;
}
const feedbackDiv = document.createElement("div");
feedbackDiv.style.cssText = `
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
background: #513EA9;
border-radius: 6px;
color: white;
font-size: 12px;
font-weight: 500;
cursor: ${message === "Included Memories" ? "pointer" : "default"};
position: relative;
`;
feedbackDiv.innerHTML = `
✓
${message}
`;
if (message === "Included Memories" && iconElement.dataset.memoriesData) {
const popup = document.createElement("div");
popup.style.cssText = `
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
background: #1a1a1a;
color: white;
padding: 0;
border-radius: 12px;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
max-width: 500px;
max-height: 400px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
z-index: 999999;
display: none;
border: 1px solid #333;
`;
const header = document.createElement("div");
header.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
border-bottom: 1px solid #333;
opacity: 0.8;
`;
header.innerHTML = `
Included Memories
`;
const content = document.createElement("div");
content.style.cssText = `
padding: 0;
max-height: 300px;
overflow-y: auto;
`;
const memoriesText = iconElement.dataset.memoriesData || "";
console.log("Memories text:", memoriesText);
const individualMemories = memoriesText
.split(/[,\n]/)
.map((memory) => memory.trim())
.filter((memory) => memory.length > 0 && memory !== ",");
console.log("Individual memories:", individualMemories);
individualMemories.forEach((memory, index) => {
const memoryItem = document.createElement("div");
memoryItem.style.cssText = `
display: flex;
align-items: center;
gap: 6px;
padding: 10px;
font-size: 13px;
line-height: 1.4;
`;
const memoryText = document.createElement("div");
memoryText.style.cssText = `
flex: 1;
color: #e5e5e5;
`;
memoryText.textContent = memory.trim();
const removeBtn = document.createElement("button");
removeBtn.style.cssText = `
background: transparent;
color: #9ca3af;
border: none;
padding: 4px;
border-radius: 4px;
cursor: pointer;
flex-shrink: 0;
height: fit-content;
display: flex;
align-items: center;
justify-content: center;
`;
removeBtn.innerHTML = ``;
removeBtn.dataset.memoryIndex = index.toString();
removeBtn.addEventListener("mouseenter", () => {
removeBtn.style.color = "#ef4444";
});
removeBtn.addEventListener("mouseleave", () => {
removeBtn.style.color = "#9ca3af";
});
memoryItem.appendChild(memoryText);
memoryItem.appendChild(removeBtn);
content.appendChild(memoryItem);
});
popup.appendChild(header);
popup.appendChild(content);
document.body.appendChild(popup);
feedbackDiv.addEventListener("mouseenter", () => {
const textSpan = feedbackDiv.querySelector("span:last-child");
if (textSpan) {
textSpan.textContent = "Click to see memories";
}
});
feedbackDiv.addEventListener("mouseleave", () => {
const textSpan = feedbackDiv.querySelector("span:last-child");
if (textSpan) {
textSpan.textContent = "Included Memories";
}
});
feedbackDiv.addEventListener("click", (e) => {
e.stopPropagation();
popup.style.display = "block";
});
document.addEventListener("click", (e) => {
if (!popup.contains(e.target as Node)) {
popup.style.display = "none";
}
});
content.querySelectorAll("button[data-memory-index]").forEach((button) => {
const htmlButton = button as HTMLButtonElement;
htmlButton.addEventListener("click", () => {
const index = Number.parseInt(
htmlButton.dataset.memoryIndex || "0",
10,
);
const memoryItem = htmlButton.parentElement;
if (memoryItem) {
content.removeChild(memoryItem);
}
const currentMemories = (iconElement.dataset.memoriesData || "")
.split(/[,\n]/)
.map((memory) => memory.trim())
.filter((memory) => memory.length > 0 && memory !== ",");
currentMemories.splice(index, 1);
const updatedMemories = currentMemories.join(" ,");
iconElement.dataset.memoriesData = updatedMemories;
const textareaElement =
(document.querySelector("textarea") as HTMLTextAreaElement) ||
(document.querySelector(
'div[contenteditable="true"]',
) as HTMLElement);
if (textareaElement) {
textareaElement.dataset.supermemories = `