Still WIP

This commit is contained in:
spushk 2026-05-13 14:08:53 +05:00 • committed by spushk
commit 6d28a2f53a
Signed by: spushk
GPG key ID: 7797B761E105E8AA

View file

@ -0,0 +1,125 @@
import { TRAVERSE_BEHAVOUR_ATTR, TRAVERSE_BEHAVOUR_IGNORE_MAGIC, traverse_text } from "@/utils/domtraverse";
import { SyncingExtensionConfig } from "@/utils/config";
import { ExtConsole } from "@/utils/logging";
import { sendMessage } from "@/utils/messaging";
const console = ExtConsole("contentscript::text_class");
const CONFIG = new SyncingExtensionConfig("ai_text");
const DOM_OBSERVATORY: { observer: MutationObserver; timer: ReturnType<typeof setTimeout> | null } = {
observer: new MutationObserver((_) => {
if (DOM_OBSERVATORY.timer) clearTimeout(DOM_OBSERVATORY.timer);
DOM_OBSERVATORY.timer = setTimeout(() => {
console.debug("observatory trigger");
traverse();
}, 1000);
}),
timer: null,
};
const VIEWPORT_OBSERVATORY: { observer: IntersectionObserver } = {
observer: new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (entry.isIntersecting) {
const stack = STATE.stack.get(entry.target);
if (stack) {
sendMessage("inference_aiText_classify", { text: stack.text })
.then((resp) => {
console.debug("received inf response", resp);
observer.unobserve(entry.target);
apply_class(entry.target, resp.prob_ai, resp.prob_human);
})
.catch((e) => {
console.warn("inference failed", e, "\nentry will be rescheduled by observer");
});
} else {
console.warn("viewport_observatory triggered on node, but the stack is absent");
observer.unobserve(entry.target);
}
}
}
}),
};
const STATE: {
stack: WeakMap<Element, { text: string }>;
badge: WeakMap<Node, Element>;
} = {
stack: new WeakMap(),
badge: new WeakMap(),
};
export default function setup() {
DOM_OBSERVATORY.observer.observe(document.body, {
subtree: true,
characterData: true,
});
}
export function traverse() {
CONFIG.sync.then(() => {
if (!CONFIG.value.enabled) {
console.debug("disabled");
return;
}
console.debug("traverse start");
for (const stack of traverse_text(600, 20)) {
const target = stack.first_node.parentElement || stack.root.parentElement;
if (target) {
set_badge(target);
STATE.stack.set(target, { text: stack.text });
VIEWPORT_OBSERVATORY.observer.observe(target);
} else {
console.warn("Stack didn't have target");
}
}
});
}
function apply_class(target: Node, prob_ai: number, prob_human: number) {
CONFIG.sync.then(() => {
if (CONFIG.value.show_ai && prob_ai >= CONFIG.value.threshold && prob_ai >= prob_human) {
set_badge(target, { type: "ai", prob: prob_ai });
} else if (CONFIG.value.show_human && prob_human >= CONFIG.value.threshold && prob_human >= prob_ai) {
set_badge(target, { type: "human", prob: prob_human });
} else {
clear_badge(target);
}
});
}
function set_badge(target: Node, data?: { type: "ai" | "human"; prob: number }) {
if (STATE.badge.has(target)) STATE.badge.delete(target);
let badge = document.createElement("mark");
badge.setAttribute(TRAVERSE_BEHAVOUR_ATTR, TRAVERSE_BEHAVOUR_IGNORE_MAGIC);
badge.style.display = "inline";
badge.style.borderRadius = "4px";
badge.style.textAlign = "center";
badge.style.fontFamily = "sans";
badge.style.fontSize = "12px";
badge.style.padding = "1px";
badge.style.marginRight = "4px";
if (data?.type === "ai") {
badge.style.backgroundColor = "#ffc36d";
badge.style.color = "#fff";
badge.textContent = `🤖: ${Math.trunc(data.prob * 100)}%`;
} else if (data?.type === "human") {
badge.style.backgroundColor = "#8fe1a1";
badge.style.color = "#fff";
badge.textContent = `👨‍💼: ${Math.trunc(data.prob * 100)}%`;
} else {
badge.style.backgroundColor = "#ddd";
badge.style.color = "#fff";
badge.textContent = "🦊 •••• ";
}
badge = target.insertBefore(badge, target.firstChild);
STATE.badge.set(target, badge);
}
function clear_badge(target: Node) {
STATE.badge.delete(target);
}