CAI Avatar Size Increaser

I have no idea what am i doing

// ==UserScript==
// @name         CAI Avatar Size Increaser
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  I have no idea what am i doing
// @author       koskleimenov
// @match        *://character.ai/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

  const SIZE_PX = 120;
  const GAP_PX = 12;
  const LOG = true;

  const BLACKLIST_KEYWORDS = ['sidebar','card','cards','profile','toolbar','nav','header','menu','discover','create','search'];

  function lg(...args){ if(LOG) console.log('[CAI-avatar-scope]', ...args); }

  function looksLikeAvatar(img){
    if(!img || !img.src) return false;
    const s = img.src.toLowerCase();
    if(s.includes('/avatars/') || s.includes('/avatar/') || s.includes('avatar-')) return true;
    const cls = (img.className||'').toLowerCase();
    if(cls.includes('avatar') || cls.includes('profile')) return true;
    return false;
  }

  function isBlacklisted(el){
    if(!el) return false;
    let cur = el;
    while(cur){
      const combined = (cur.className||'') + ' ' + (cur.id||'') + ' ' + (cur.getAttribute && (cur.getAttribute('data-testid')||''));
      for(const kw of BLACKLIST_KEYWORDS){
        if(combined.toLowerCase().includes(kw)) return true;
      }
      cur = cur.parentElement;
    }
    return false;
  }

  function findChatPanel(){
    const els = Array.from(document.querySelectorAll('main, section, div'));
    let best = null, bestScore = 0;
    for(const el of els){
      try {
        if (!el.offsetParent) continue; // hidden
        if (isBlacklisted(el)) continue;
        const imgs = el.querySelectorAll('img');
        let avatarCount = 0;
        for(const img of imgs) if(looksLikeAvatar(img)) avatarCount++;
        if(avatarCount < 1) continue;
        const textLen = (el.innerText || '').trim().length;
        const score = avatarCount * 3 + (textLen > 40 ? 1 : 0);
        if(score > bestScore){
          best = el;
          bestScore = score;
        }
      } catch(e){}
    }
    if(bestScore > 0){
      lg('picked chat panel candidate (score):', best, bestScore);
      return best;
    }
    return null;
  }

  function findMessageAncestor(node){
    if(!node) return null;
    return node.closest('[class*="message"], [class*="msg-"], [data-testid*="message"], [role="article"], [role="listitem"]') || node.closest('div');
  }

  function findAvatarContainer(img){
    if(!img) return null;
    let cur = img;
    for(let i=0;i<8 && cur;i++){
      if(!cur) break;
      const cname = (cur.className||'').toLowerCase();
      if(cname.includes('avatar') || cname.includes('avatar-wrap') || cur.tagName.toLowerCase()==='figure') return cur;
      cur = cur.parentElement;
    }
    return img.parentElement;
  }

  function findBubbleElement(msgAncestor, avatarContainer){
    if(!msgAncestor) return null;
    const sel = ['[class*="bubble"]','[class*="msg-bubble"]','[data-testid*="message-text"]','[class*="messageBody"]','[class*="content"]'];
    for(const s of sel){
      try {
        const el = msgAncestor.querySelector(s);
        if(el) return el;
      } catch(e){}
    }
    try{
      const nodes = Array.from(msgAncestor.querySelectorAll('*')).filter(n=>{
        if(n === avatarContainer) return false;
        try { return (n.innerText||'').trim().length > 0; } catch(e){ return false; }
      });
      return nodes.length ? nodes[0] : null;
    } catch(e){}
    return null;
  }

  function styleOneAvatar(img, chatScopeEl){
    try{
      if(!img || img.__cai_scoped) return false;
      if(!looksLikeAvatar(img)) return false;
      if(!chatScopeEl || !chatScopeEl.contains(img)) return false;

      const msg = findMessageAncestor(img);
      if(!msg) { lg('skip: not in message ancestor', img); return false; }
      if(isBlacklisted(msg)) { lg('skip: message in blacklisted area', msg); return false; }

      const container = findAvatarContainer(img);
      if(!container) { lg('skip: no container', img); return false; }

      container.style.width = `${SIZE_PX}px`;
      container.style.height = `${SIZE_PX}px`;
      container.style.minWidth = `${SIZE_PX}px`;
      container.style.minHeight = `${SIZE_PX}px`;
      container.style.flex = `0 0 ${SIZE_PX}px`;
      container.style.display = 'flex';
      container.style.alignItems = 'center';
      container.style.justifyContent = 'center';
      container.style.overflow = 'visible';
      container.style.boxSizing = 'border-box';
      container.style.gap = '0';

      img.style.width = '100%';
      img.style.height = '100%';
      img.style.maxWidth = 'none';
      img.style.maxHeight = 'none';
      img.style.objectFit = 'contain';
      img.style.borderRadius = '10px';
      img.style.transform = 'none';
      img.style.display = 'block';

      const bubble = findBubbleElement(msg, container);
      if(bubble){
        try {
          const aRect = container.getBoundingClientRect();
          const bRect = bubble.getBoundingClientRect();
          if(aRect.left < bRect.left) {
            bubble.style.marginLeft = `${SIZE_PX + GAP_PX}px`;
            bubble.style.marginRight = '';
          } else {
            bubble.style.marginRight = `${SIZE_PX + GAP_PX}px`;
            bubble.style.marginLeft = '';
          }
        } catch(e){
          bubble.style.marginLeft = `${SIZE_PX + GAP_PX}px`;
        }
      }

      img.__cai_scoped = true;
      lg('styled avatar:', img.src || img, 'containerClass=' + (container.className||container.tagName), 'bubble=', bubble && (bubble.className || bubble.tagName));
      return true;
    } catch(err){
      console.error('CAI-scope error', err);
      return false;
    }
  }

  function processAllInScope(chatScopeEl){
    if(!chatScopeEl) return 0;
    let changed = 0;
    const imgs = chatScopeEl.querySelectorAll('img');
    imgs.forEach(img => {
      if(styleOneAvatar(img, chatScopeEl)) changed++;
    });
    return changed;
  }

  (function start(){
    let attempts = 0;
    const maxAttempts = 40;
    const t = setInterval(()=>{
      attempts++;
      const chat = findChatPanel();
      if(chat){
        clearInterval(t);
        lg('CHAT SCOPE FOUND:', chat);
        try { chat.setAttribute('data-cai-chat-scope','1'); } catch(e){}
        processAllInScope(chat);

        const mo = new MutationObserver((mutations)=>{
          for(const m of mutations){
            if(!m.addedNodes) continue;
            m.addedNodes.forEach(node=>{
              if(node.nodeType !== 1) return;
              if(node.tagName && node.tagName.toLowerCase() === 'img'){
                styleOneAvatar(node, chat);
              } else {
                const imgs = node.querySelectorAll ? node.querySelectorAll('img') : [];
                imgs.forEach(i => styleOneAvatar(i, chat));
              }
            });
          }
        });
        mo.observe(chat, { childList: true, subtree: true });
        setInterval(()=>processAllInScope(chat), 3000);
        lg('CAI avatar scoper running (SIZE_PX=' + SIZE_PX + ')');
      } else {
        if(attempts >= maxAttempts){
          clearInterval(t);
          lg('Unable to find a confident chat panel candidate. Falling back to limited global scan (will attempt to avoid sidebars).');
          processAllInScope(document);
          const globalMo = new MutationObserver((mutations)=>{
            for(const m of mutations){
              if(!m.addedNodes) continue;
              m.addedNodes.forEach(node=>{
                if(node.nodeType !== 1) return;
                const imgs = node.querySelectorAll ? node.querySelectorAll('img') : [];
                imgs.forEach(i=>{
                  try{
                    if(!isBlacklisted(i) && looksLikeAvatar(i)) styleOneAvatar(i, document);
                  }catch(e){}
                });
              });
            }
          });
          globalMo.observe(document, { childList: true, subtree: true });
        } else {
          // keep trying
        }
      }
    }, 300);
  })();
})();