Incicle Dark Theme

Tema de nord dark para o kanban do incicle

// ==UserScript==
// @name        Incicle Dark Theme
// @license MIT
// @namespace   Violentmonkey Scripts
// @match       https://projects.incicle.com/*
// @grant       none
// @version     1.1
// @author      JV
// @description Tema de nord dark para o kanban do incicle
// ==/UserScript==

(function() {
'use strict';

// Nord Color Palette
const COLORS = {
  // Polar Night
  nord0: '#2e3440',  // Background
  nord1: '#3b4252',  // Background lighter
  nord2: '#434c5e',  // Selection background
  nord3: '#4c566a',  // Comments, invisibles
  
  // Snow Storm
  nord4: '#d8dee9',  // Dark text
  nord5: '#e5e9f0',  // Text
  nord6: '#eceff4',  // Light text
  
  // Frost
  nord7: '#8fbcbb',  // Secondary
  nord8: '#88c0d0',  // Light blue
  nord9: '#81a1c1',  // Secondary blue
  nord10: '#5e81ac', // Primary
  
  // Aurora
  nord11: '#bf616a', // Red
  nord12: '#d08770', // Orange
  nord13: '#ebcb8b', // Yellow
  nord14: '#a3be8c'  // Green
};

const cssModules = {
  // CSS Variables
  variables: `
    :root,
    html:root {
      --primary: ${COLORS.nord10} !important;
      --secondary-blue: ${COLORS.nord9} !important;
      --second: ${COLORS.nord7} !important;
      --red: ${COLORS.nord11} !important;
      --light-blue: ${COLORS.nord8} !important;
      --orange: ${COLORS.nord12} !important;
      --green: ${COLORS.nord14} !important;
      --text-color: ${COLORS.nord5} !important;
      --text-darker: ${COLORS.nord4} !important;
      --opaque-bkg: ${COLORS.nord3} !important;
      --light-yellow: ${COLORS.nord13} !important;
      --nord-bg: ${COLORS.nord0} !important;
      --nord-bg2: ${COLORS.nord1} !important;
      --nord-bg3: ${COLORS.nord2} !important;
      --nord-bg4: ${COLORS.nord3} !important;
    }
  `,

  // Base styles
  base: `
    body,
    html body {
      background-color: var(--nord-bg) !important;
      color: var(--text-color) !important;
    }
  `,

  // Links with high specificity
  links: `
    a,
    html a,
    body a,
    div a,
    p a,
    span a,
    li a,
    td a,
    th a,
    header a,
    nav a,
    main a,
    footer a,
    article a,
    section a,
    .link,
    [href],
    a.link,
    a[class],
    a[id],
    body div a,
    html body a,
    div.container a,
    .nav-link,
    .menu-link,
    .btn-link {
      color: var(--text-color) !important;
    }
  `,

  // Header styles
  header: `
    header,
    body header,
    html body header,
    div header,
    .header,
    #header,
    [class*="header"],
    nav header,
    main header {
      background-color: var(--nord-bg2) !important;
    }
    
    .css-1vb8iuf {
      color: var(--text-darker) !important;
    }
  `,

  // Kanban specific styles
  kanban: `
    /* Kanban Cards */
    .kyhxic,
    [class*="card"],
    .kanban-card {
      background-color: var(--nord-bg3) !important;
    }

    /* Kanban Columns */
    .hNtkvk,
    [class*="column"] {
      background-color: var(--nord-bg4) !important;
    }

    .jHmrSI {
      color: var(--text-darker) !important;
      border-color: var(--text-darker) !important;
    }

    .css-18st10o .MuiInputBase-root {
      background-color: transparent !important;
    }
  `,

  // Modal/Card styles
  modals: `
    .css-1tx53sv {
      background-color: var(--nord-bg2) !important;
    }

    .css-1tx53sv h3, 
    .css-1tx53sv button {
      color: var(--text-darker) !important;
    }

    .css-9aqn0s {
      background-color: var(--nord-bg3) !important;
    }

    .css-18z8a8c fieldset {
      color: var(--text-color) !important;
    }

    .css-13e5wxt {
      color: var(--text-darker) !important;
    }
  `
};

class IncicleThemeManager {
  constructor() {
    this.retryCount = 0;
    this.maxRetries = 5;
    this.retryDelay = 200;
  }

  // Combina todos os módulos CSS
  getCombinedCSS() {
    return Object.values(cssModules).join('\n\n');
  }

  // Aplica estilos via Greasemonkey/Violentmonkey
  applyWithGM() {
    if (typeof GM_addStyle !== "undefined") {
      GM_addStyle(this.getCombinedCSS());
      return true;
    }
    return false;
  }

  // Aplica estilos via DOM
  applyWithDOM() {
    const styleNode = document.createElement("style");
    styleNode.appendChild(document.createTextNode(this.getCombinedCSS()));
    styleNode.setAttribute('data-userscript', 'incicle-dark-theme');
    styleNode.setAttribute('data-version', '1.1');
    
    const head = document.querySelector("head") || document.documentElement;
    head.appendChild(styleNode);
  }

  // Força estilos via JavaScript para elementos problemáticos
  forceStyles() {
    // Headers
    const headers = document.querySelectorAll('header, .header, #header, [class*="header"]');
    headers.forEach(el => {
      el.style.setProperty('background-color', COLORS.nord1, 'important');
    });

    // Kanban cards
    const cards = document.querySelectorAll('.kyhxic, [class*="card"]');
    cards.forEach(el => {
      el.style.setProperty('background-color', COLORS.nord2, 'important');
    });

    // Links
    const links = document.querySelectorAll('a, [href]');
    links.forEach(el => {
      el.style.setProperty('color', COLORS.nord5, 'important');
    });
  }

  // Aplica estilos com retry
  applyStyles() {
    // Tenta aplicar via GM primeiro
    if (!this.applyWithGM()) {
      this.applyWithDOM();
    }

    // Força estilos específicos após delay
    setTimeout(() => this.forceStyles(), 100);
  }

  // Observer para mudanças dinâmicas
  setupObserver() {
    const observer = new MutationObserver((mutations) => {
      let needsReapply = false;
      
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          needsReapply = true;
        }
      });

      if (needsReapply) {
        setTimeout(() => this.forceStyles(), 50);
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }

  // Inicialização
  init() {
    // Aplica imediatamente
    this.applyStyles();

    // Aplica após DOM carregar
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => {
        this.applyStyles();
        this.setupObserver();
      });
    } else {
      this.setupObserver();
    }

    // Retry periódico para elementos que carregam dinamicamente
    const retryInterval = setInterval(() => {
      this.forceStyles();
      this.retryCount++;
      
      if (this.retryCount >= this.maxRetries) {
        clearInterval(retryInterval);
      }
    }, this.retryDelay * this.retryCount);
  }
}

// Inicializar
const themeManager = new IncicleThemeManager();
themeManager.init();

})();