Files
risuai/src/ts/observer.ts
2024-12-15 19:33:26 +09:00

110 lines
3.8 KiB
TypeScript

import { runTrigger } from "./process/triggers";
import { runCharacterJS } from "./plugins/embedscript";
import { sleep } from "./util";
import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte";
function nodeObserve(node:HTMLElement){
const triggerName = node.getAttribute('risu-trigger');
const btnEvent = node.getAttribute('risu-btn');
const observerAdded = node.getAttribute('risu-observer');
const hlLang = node.getAttribute('x-hl-lang');
if(observerAdded){
return
}
if(triggerName){
node.addEventListener('click', async () => {
const currentChar = getCurrentCharacter()
if(currentChar.type === 'group'){
return;
}
const triggerResult = await runTrigger(currentChar, 'manual', {
chat: getCurrentChat(),
manualName: triggerName,
});
if(triggerResult){
setCurrentChat(triggerResult.chat);
}
}, {
passive: true,
});
node.setAttribute('risu-observer', 'true');
return
}
if(btnEvent){
node.addEventListener('click',async ()=>{
await runCharacterJS({
code: null,
mode: 'onButtonClick',
data: btnEvent
})
}, {passive: true})
node.setAttribute('risu-observer', 'true');
return
}
if(hlLang){
node.addEventListener('contextmenu', (e)=>{
e.preventDefault();
const menu = document.createElement('div');
menu.setAttribute('class', 'fixed z-50 min-w-[160px] py-2 bg-gray-800 rounded-lg border border-gray-700')
const copyOption = document.createElement('div');
copyOption.textContent = 'Copy';
copyOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
copyOption.addEventListener('click', ()=>{
navigator.clipboard.writeText(node.getAttribute('x-hl-text'));
menu.remove();
})
const downloadOption = document.createElement('div');
downloadOption.textContent = 'Download';
downloadOption.setAttribute('class', 'px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 cursor-pointer')
downloadOption.addEventListener('click', ()=>{
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([node.getAttribute('x-hl-text')], {type: 'text/plain'}));
a.download = 'code.' + hlLang;
a.click();
menu.remove();
})
menu.appendChild(copyOption);
menu.appendChild(downloadOption);
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
document.body.appendChild(menu);
document.addEventListener('click', ()=>{
menu.remove();
}, {once: true})
})
}
}
export async function startObserveDom(){
//For codeblock we are using MutationObserver since it doesn't appear well
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if(node instanceof HTMLElement){
nodeObserve(node);
}
})
})
})
//We are using a while loop intead of MutationObserver because MutationObserver is expensive for just a few elements
while(true){
document.querySelectorAll('[risu-trigger]').forEach(nodeObserve);
document.querySelectorAll('[risu-btn]').forEach(nodeObserve);
document.querySelectorAll('[x-hl-lang]').forEach(nodeObserve);
await sleep(100);
}
}