Desktop layout and chat style customization
// ==UserScript==
// @name Isekai ZERO Desktop Layout & Customizer
// @namespace Violentmonkey Scripts
// @version 1.0
// @description Desktop layout and chat style customization
// @author Me
// @match https://www.isekai.world/*
// @match https://www.isekaizero.ai/*
// @license MIT
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// CONFIGURATION
const CONFIG = {
maxWidth: 1100,
menuWidth: 90,
fontFamily: 'Roboto',
syncInterval: 500,
storagePrefix: 'isekaiChatStyle_'
};
// ROUTE PATTERNS
const ROUTES = {
CHAT: /^\/chats\/(?!saved|likes|comments|followings)([a-zA-Z0-9_-]+)\/?$/,
STORYLINE: /^\/storylines\/[a-zA-Z0-9_-]+\/?$/,
CHARACTER: /^\/characters\/[a-zA-Z0-9_-]+\/?$/,
MAIN_NAV: /^\/$|^\/explore.*$|^\/(creation|chats|profile)\/?$/
};
// NAVIGATION ITEMS
const NAV_ITEMS = [
{ id: 'nav-index', alt: 'index tab icon', label: 'Home' },
{ id: 'nav-explore', alt: 'explore tab icon', label: 'Explore' },
{ id: 'nav-creation', alt: 'creation tab icon', label: 'Create' },
{ id: 'nav-chats', alt: 'chats tab icon', label: 'Chats' },
{ id: 'nav-profile', alt: 'profile tab icon', label: 'Profile' }
];
// DEFAULT STYLE SETTINGS
const DEFAULT_SETTINGS = {
useDefaultStyle: false,
aiColor: '#111235',
userColor: '#0b1428',
textColor: '#ffffff',
glowEnabled: true,
glowColor: '#9949ca',
bgOpacity: 0.8,
bgBlur: 10
};
// STATE
let currentChatId = null;
let currentSettings = null;
let styleNodes = {};
let elements = {};
// UTILITY FUNCTIONS & STORAGE
function migrateOldSettings() {
const old = localStorage.getItem('isekaiChatStyle');
if (old) {
localStorage.setItem(CONFIG.storagePrefix + 'global', old);
localStorage.removeItem('isekaiChatStyle');
}
}
function getChatId() {
const match = window.location.pathname.match(ROUTES.CHAT);
return match ? match[1] : null;
}
function loadSettings(chatId) {
try {
const globalStr = localStorage.getItem(CONFIG.storagePrefix + 'global');
const globalSet = globalStr ? { ...DEFAULT_SETTINGS, ...JSON.parse(globalStr) } : { ...DEFAULT_SETTINGS };
if (chatId) {
const chatStr = localStorage.getItem(CONFIG.storagePrefix + chatId);
if (chatStr) return { ...globalSet, ...JSON.parse(chatStr) };
}
return globalSet;
} catch (e) {
console.warn('Failed to load settings:', e);
return { ...DEFAULT_SETTINGS };
}
}
function saveSettings() {
try {
if (currentChatId) {
localStorage.setItem(CONFIG.storagePrefix + currentChatId, JSON.stringify(currentSettings));
}
} catch (e) {
console.warn('Failed to save settings:', e);
}
}
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : { r: 0, g: 0, b: 0 };
}
function createStyle(css, id) {
const node = document.createElement('style');
if (id) node.id = id;
node.textContent = css;
document.head.appendChild(node);
return node;
}
function injectFont() {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${CONFIG.fontFamily}`;
document.head.appendChild(link);
}
// CSS TEMPLATES
function getGlobalCSS() {
// Calculate centered group layout metrics
const totalWidth = CONFIG.maxWidth + CONFIG.menuWidth;
const halfTotal = totalWidth / 2;
const uiOffset = halfTotal - CONFIG.menuWidth;
return `
/* Hide the original bottom navigation securely */
div[style*="bottom: 0px"]:has(img[alt="index tab icon"]) {
display: none !important;
opacity: 0 !important;
pointer-events: none !important;
}
/* --- CUSTOM SIDE MENU --- */
#custom-side-menu {
position: fixed;
left: calc(50% - ${halfTotal}px);
top: 0;
width: ${CONFIG.menuWidth}px;
height: 100vh;
background: linear-gradient(180deg, rgba(17, 18, 60, 1) 0%, rgba(9, 25, 77, 1) 100%);
box-shadow: rgba(0, 0, 0, 0.35) 0px 6px 10px 0px;
display: none; /* Controlled by body.has-side-menu */
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 0px;
z-index: 999999;
backdrop-filter: blur(10px);
border-right: 1px solid rgba(102, 126, 234, 0.5);
padding-top: 2%;
}
body.has-side-menu #custom-side-menu {
display: flex;
}
.custom-nav-btn {
background: none;
border: none;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: transform 0.2s ease, background 0.2s ease;
padding: 24px 0;
width: 100%;
}
.custom-nav-btn:hover { background: rgba(255, 255, 255, 0.05); }
.custom-nav-btn img { width: 32px; height: 32px; object-fit: cover; margin-bottom: 6px; transition: opacity 0.3s; }
.custom-nav-label {
color: white;
font-family: ${CONFIG.fontFamily};
font-size: 12px;
font-weight: 300 !important;
text-transform: uppercase;
}
/* --- RESPONSIVE MAIN UI WRAPPER --- */
/* No Side Menu */
.r-1ye8kvj {
max-width: ${CONFIG.maxWidth}px !important;
width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding: 0 !important;
transition: margin 0.3s ease, padding 0.3s ease;
}
/* Chat */
body.is-chat .r-1ye8kvj {
padding: 0 90px !important;
}
/* State 3: Side Menu is visible */
body.has-side-menu .r-1ye8kvj {
margin-left: calc(50% - ${uiOffset}px) !important;
margin-right: auto !important;
padding: 0 !important;
}
/* State 4: Small screen with Side Menu */
@media (max-width: ${totalWidth}px) {
body.has-side-menu #custom-side-menu { left: 0; }
body.has-side-menu .r-1ye8kvj {
margin-left: ${CONFIG.menuWidth}px !important;
margin-right: 0 !important;
width: calc(100% - ${CONFIG.menuWidth}px) !important;
}
}
/* --- WIDGET --- */
#chat-style-widget {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 999;
font-family: ${CONFIG.fontFamily}, sans-serif;
display: none;
flex-direction: column;
align-items: flex-end;
}
#chat-style-toggle {
background: black;
border: 1px solid rgba(255,255,255,0.2);
color: white;
border-radius: 50%;
cursor: pointer;
transition: 0.2s;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
padding: 0;
}
#chat-style-toggle:hover {
background: rgba(20, 20, 20, 1);
}
#chat-style-toggle svg { width: 24px; height: 24px; fill: currentColor; }
#chat-style-panel {
display: none;
margin-bottom: 15px;
background: black;
border: 1px solid rgba(255,255,255,0.1);
padding: 20px;
border-radius: 12px;
width: 240px;
backdrop-filter: blur(10px);
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
color: #eee;
}
.widget-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-size: 12px;
}
.widget-row input[type="color"] { background: none; border: none; width: 30px; height: 30px; cursor: pointer; padding: 0; transition: 0.2s; }
.widget-row input[type="range"] { width: 100px; cursor: pointer; }
.widget-row input[type="checkbox"] { cursor: pointer; transform: scale(1.2); transition: 0.2s; }
.widget-btn-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-top: 20px;
}
.widget-btn {
background: #333;
color: #fff;
border: none;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
width: 100%;
transition: 0.2s;
}
.widget-btn:hover { background: #555; }
`;
}
function getChatCSS() {
return `
.r-49im1t { background-color: black !important; }
.r-18u37iz { margin: auto !important; }
textarea { font-family: ${CONFIG.fontFamily} !important; }
.css-g5y9jx[style*="padding-right: 15px; padding-left: 15px"]:has(.css-g5y9jx[style="flex: 1 1 0%;"]) {
width: 100% !important;
right: 0px !important;
margin: auto !important;
padding-left: 0px !important;
}
.css-g5y9jx [style*="z-index: 100"] { background-color: rgba(0, 0, 0, 0) !important}
.css-g5y9jx [style*="z-index: 100"]:has(.css-g5y9jx.r-1i6wzkk.r-lrvibr.r-1loqt21.r-1otgn73):has(div[style="box-shadow: rgba(0, 0, 0, 0) 0px 0px 8px; padding: 6px 8px; border-radius: 10px; background-color: rgba(0, 0, 0, 0);"]) {
background-color: rgba(0, 0, 0, 0.9) !important; border: 1px solid #555 !important;
}
.css-146c3p1, .css-1jxf684 { color: #e1e1e1 !important; }
.css-1jxf684[data-testid="em"] { color: #c1c1c1 !important; }
.css-g5y9jx { border: none !important; }
/* AI messages */
.css-g5y9jx[style*="padding-bottom: 3px; border-bottom-width: 0.5px"] {
background: linear-gradient(45deg, var(--ai-bg-grad-1) 0%, var(--ai-bg-grad-2) 100%) !important;
border-radius: 30px 30px 30px 0 !important;
width: 70% !important;
transform: translateX(20px) !important;
padding: 5px 0 5px 20px !important;
margin-bottom: 20px !important;
backdrop-filter: blur(var(--chat-blur)) !important;
-webkit-backdrop-filter: blur(var(--chat-blur)) !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7) !important;
}
/* User messages */
.css-g5y9jx[style*="background-color: rgba(22, 22, 26, 0.8)"] {
background: linear-gradient(90deg, var(--user-bg-grad-1) 0%, var(--user-bg-grad-2) 100%) !important;
border-radius: 30px 30px 0 30px !important;
width: auto !important;
min-width: 9% !important;
max-width: 60% !important;
align-items: normal !important;
padding-left: 20px !important;
margin: 0 30px 20px auto !important;
backdrop-filter: blur(var(--chat-blur)) !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7) !important;
transform: translateX(-20px) !important;
}
.css-1jxf684, .css-146c3p1:not([style*="ionicons"], [style*="material"], [style*="FontAwesome"], [style*="feather"], [style*="Fontisto"], [style*="anticon"]), css-11aywtz, css-g5y9jx, r-6taxm2 {
font-family: ${CONFIG.fontFamily};
line-height: 1.6 !important;
}
.css-g5y9jx[style*="background-color: rgb(45, 41, 38)"] {
background-color: rgba(0, 0, 0, 0) !important;
margin-top: 0px !important;
}
.css-g5y9jx[style*="position: absolute; inset: 0px;"] { margin-top: 0px !important; }
.css-g5y9jx[style*="flex: 1 1 0%; padding: 0 0 0 15px;"] { padding-top: 0px !important; }
.css-g5y9jx[style*="background-color: rgb(0, 0, 0)"] { background-color: rgba(0, 0, 0, 0) !important; }
.css-146c3p1[style="color: rgb(0, 0, 0); font-size: 10px; font-weight: bold;"] { color: black !important; }
.css-146c3p1.r-lrvibr[style*="font-size: 20px; color: rgb(0, 0, 0); font-family: ionicons"] { color: white !important; }
button:has(.css-146c3p1.r-lrvibr[style*="font-size: 20px; color: rgb(0, 0, 0); font-family: ionicons"]) {
background-color: transparent !important;
}
.css-g5y9jx[data-testid*="blockquote"] {
background: linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 100%) !important;
border-left: 3px solid rgba(255, 255, 255, 0.5) !important;
margin: 5px 0 !important;
}
.css-1jxf684[data-testid="strong"] { font-weight: bold !important; }
.css-g5y9jx[data-testid*="marked-list-item"] { margin-left: 0 !important; }
.r-h3f8nf { min-height: 1800px !important; }
.r-ytbthy { padding-left: 50px !important; padding-right: 50px !important; }
.css-g5y9jx[style*="align-items: center; z-index: 100; flex-direction: row; padding-top: 0px; position: absolute; top: 0px;"] {
background: linear-gradient(90deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.6) 100%) !important;
}
.css-g5y9jx[style*="position: absolute; inset: 0px; z-index: 100;"] { background-color: rgba(0, 0, 0, 0.1) !important; }
.css-g5y9jx[style*="margin-bottom: 5px; padding-right: 15px; padding-left: 15px; flex-direction: row; justify-content: space-between; align-items: center;"] { padding-left: 0px !important; }
/* Text glow and color */
.css-1jxf684[style*="color: rgb(225, 138, 36)"],
.css-146c3p1[style*="color: rgb(225, 138, 36)"] {
color: var(--chat-text-color) !important;
animation: var(--glow-animation) !important;
overflow: visible !important;
}
@-webkit-keyframes custom-glow {
from { text-shadow: 0 0 1px var(--glow-grad-1); }
to { text-shadow: 0 0 2px var(--glow-grad-2); }
}
.css-1jxf684.r-lrvibr[data-testid="code"] {
font-family: Consolas, "Courier New", monospace !important;
background-color: rgba(0, 0, 0, 0.5) !important;
padding: 2px 4px !important;
}
/* Image fixes */
.css-g5y9jx img, .r-1ye8kvj img, video {
width: 100% !important;
height: 100% !important;
object-fit: contain !important;
}
.css-g5y9jx[style*="z-index: 1"] img { object-fit: cover !important; }
div.css-g5y9jx[style*="z-index: 1"]:has(img:not([src])),
div.css-g5y9jx[style*="z-index: 1"]:has(img[src=""]) {
display: none !important;
}
div.css-g5y9jx[style*="z-index: 1"]:has(img:not([src])) ~ div.css-g5y9jx[style*="z-index: 2"] img,
div.css-g5y9jx[style*="z-index: 1"]:has(img[src=""]) ~ div.css-g5y9jx[style*="z-index: 2"] img {
object-fit: cover !important;
}
button.css-g5y9jx.r-1i6wzkk.r-lrvibr.r-1loqt21.r-1otgn73[style*="padding: 10px 15px; border-width: 1px; border-color: rgb(131, 131, 131)"] {
border: 1px solid #444 !important;
}
div[style="background-color: rgba(0, 0, 0, 0.8); height: 125px; align-items: center; justify-content: flex-end;"] {
background-color: rgba(0, 0, 0, 0) !important;
}
div.css-g5y9jx.r-1uaz6oj.r-qklmqi.r-18u37iz:has(.css-146c3p1.r-jwli3a.r-13awgt0.r-n6v787.r-1sp51qo.r-q4m81j.r-1rvyss1.r-13l2t4g) {
margin: 0 !important;
}
`;
}
function getOtherCSS() {
return `
.css-g5y9jx[style*="width: 600px"] { width: 100% !important; margin: auto !important; }
.css-g5y9jx img, .r-1ye8kvj img, video {
width: 100% !important;
height: 100% !important;
object-fit: contain !important;
}
.css-g5y9jx[style*="overflow: hidden; position: absolute; inset: 0px; border-radius: 12px; opacity: 0.3;"] img,
.css-g5y9jx[style*="overflow: hidden; position: absolute; inset: 0px; border-radius: 10px;"] img {
object-fit: cover !important;
}
div.css-g5y9jx[style*="padding: 50px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding: 0px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding-right: 12px; padding-left: 12px; padding-bottom: 80px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding: 15px 12px 120px"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz) {
align-items: center !important;
}
div.css-g5y9jx.r-1awozwy.r-18u37iz.r-1iud8zs.r-1wtj0ep.r-ubg91z.r-13qz1uu { padding-top: 5px !important; }
div.css-g5y9jx.r-1udh08x.r-xyw6el.r-bnwqim:not(:has(.css-g5y9jx.r-18u37iz.r-1wtj0ep.r-5oul0u)) {
width: 600px !important;
margin: auto !important;
}
`;
}
function getOtherCSS2() {
return `
.css-g5y9jx[style*="width: 600px"] { width: 100% !important; margin: auto !important; }
.css-g5y9jx img, .r-1ye8kvj img, video { object-fit: cover !important; }
.css-g5y9jx[style*="overflow: hidden; position: absolute; bottom: 20px; left: 0px; right: 0px; height: 300px;"] img {
object-fit: contain !important;
}
div.css-g5y9jx[style*="padding: 50px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding: 0px 12px 150px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding-right: 12px; padding-left: 12px; padding-bottom: 80px;"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz):has(.css-g5y9jx.r-1867qdf.r-5oul0u.r-1537yvj.r-zhp00w),
div.css-g5y9jx[style*="padding: 15px 12px 120px"] > div.css-g5y9jx:has(div.css-g5y9jx.r-18u37iz) {
align-items: center !important;
}
div.css-g5y9jx.r-1awozwy.r-18u37iz.r-1iud8zs.r-1wtj0ep.r-ubg91z.r-13qz1uu { padding-top: 5px !important; }
div.css-g5y9jx.r-t23y2h.r-zhp00w:has(.css-g5y9jx.r-1awozwy.r-1q9bdsx.r-1pi2tsx.r-1777fci.r-13qz1uu) {
width: 15vw !important;
}
div.css-g5y9jx.r-18u37iz.r-1wtj0ep.r-117bsoe.r-13qz1uu {
justify-content: center !important;
gap: 20px;
}
div.css-146c3p1.r-8akbws.r-krxsd3.r-dnmrzs.r-1qsk4np.r-1udbk01.r-jwli3a.r-150tq8z.r-1b43r93.r-b88u0q.r-14yzgew,
h1.css-146c3p1.r-8akbws.r-krxsd3.r-dnmrzs.r-1qsk4np.r-1udbk01.r-jwli3a.r-150tq8z.r-1b43r93.r-b88u0q.r-14yzgew.r-wr9vkk {
font-weight: normal !important;
}
div.css-146c3p1.r-ubezar.r-1kfrs79.r-1bymd8e { font-size: 20px; }
`;
}
// STYLE MANAGEMENT
function initStyles() {
injectFont();
styleNodes.global = createStyle(getGlobalCSS(), 'i0-global-styles');
styleNodes.chat = createStyle(getChatCSS(), 'i0-chat-styles');
styleNodes.other = createStyle(getOtherCSS(), 'i0-other-styles');
styleNodes.other2 = createStyle(getOtherCSS2(), 'i0-other2-styles');
}
function getCurrentRouteType() {
const path = window.location.pathname;
if (ROUTES.CHAT.test(path)) return 'chat';
if (ROUTES.STORYLINE.test(path) || ROUTES.CHARACTER.test(path)) return 'content';
return 'default';
}
function updateStyles() {
const routeType = getCurrentRouteType();
const isMainNav = ROUTES.MAIN_NAV.test(window.location.pathname);
const newChatId = getChatId();
// Control layout and visibility via body classes
document.body.classList.toggle('has-side-menu', isMainNav);
document.body.classList.toggle('is-chat', routeType === 'chat');
if (newChatId !== currentChatId) {
currentChatId = newChatId;
currentSettings = loadSettings(currentChatId);
updateWidgetUI();
applyStyleVariables();
}
styleNodes.chat.disabled = routeType !== 'chat';
styleNodes.other.disabled = routeType !== 'content';
styleNodes.other2.disabled = routeType === 'chat' || routeType === 'content';
updateWidgetVisibility(routeType === 'chat');
}
function updateWidgetVisibility(show) {
if (elements.widget) elements.widget.style.display = show ? 'flex' : 'none';
}
function setupRouteListeners() {
const originalPushState = history.pushState.bind(history);
const originalReplaceState = history.replaceState.bind(history);
history.pushState = function(...args) {
originalPushState(...args);
updateStyles();
};
history.replaceState = function(...args) {
originalReplaceState(...args);
updateStyles();
};
window.addEventListener('popstate', updateStyles);
}
// SIDE MENU
function createSideMenu() {
if (elements.menu) return;
const menu = document.createElement('div');
menu.id = 'custom-side-menu';
NAV_ITEMS.forEach(item => {
const btn = document.createElement('button');
btn.className = 'custom-nav-btn';
// Immediately fetch the native element's SRC to avoid broken empty image icons
const originalImg = document.querySelector(`img[alt="${item.alt}"]`);
const src = originalImg ? originalImg.src : '';
btn.innerHTML = `
<img id="custom-img-${item.id}" src="${src}" alt="${item.label}" ${src ? '' : 'style="opacity: 0;"'}>
<span class="custom-nav-label">${item.label}</span>
`;
btn.addEventListener('click', () => handleNavClick(item.alt));
menu.appendChild(btn);
});
document.body.appendChild(menu);
elements.menu = menu;
}
function handleNavClick(altText) {
const originalImg = document.querySelector(`img[alt="${altText}"]`);
if (!originalImg) return;
const reactButton = originalImg.closest('[role="button"]') || originalImg.closest('button');
if (reactButton) reactButton.click();
}
function syncIcons() {
if (!elements.menu) return;
NAV_ITEMS.forEach(item => {
const originalImg = document.querySelector(`img[alt="${item.alt}"]`);
const customImg = document.getElementById(`custom-img-${item.id}`);
if (originalImg && customImg && customImg.src !== originalImg.src) {
customImg.src = originalImg.src;
customImg.style.opacity = '1'; // Show if it was previously hidden
}
});
}
function startIconSync() {
setInterval(() => {
// Wait for native app to load its images before we clone and show our menu
const originalImages = document.querySelectorAll('img[alt$="tab icon"]');
if (originalImages.length > 0) {
createSideMenu();
syncIcons();
}
}, CONFIG.syncInterval);
}
// STYLE WIDGET & VARIABLES
function applyStyleVariables() {
const root = document.documentElement.style;
const s = currentSettings;
const bgOpacity = isNaN(parseFloat(s.bgOpacity)) ? 0.8 : parseFloat(s.bgOpacity);
const bgBlur = isNaN(parseInt(s.bgBlur)) ? 10 : parseInt(s.bgBlur);
const activeAIHex = s.useDefaultStyle ? '#000000' : s.aiColor;
const activeUserHex = s.useDefaultStyle ? '#222222' : s.userColor;
const activeTextHex = s.useDefaultStyle ? '#e19a24' : s.textColor;
const activeGlowEnabled = s.useDefaultStyle ? false : s.glowEnabled;
const ai = hexToRgb(activeAIHex);
const us = hexToRgb(activeUserHex);
const gl = hexToRgb(s.glowColor);
root.setProperty('--chat-text-color', activeTextHex);
root.setProperty('--glow-animation', activeGlowEnabled ? 'custom-glow 1s ease-in-out infinite alternate' : 'none');
root.setProperty('--ai-bg-grad-1', `rgba(${Math.floor(ai.r * 0.4)}, ${Math.floor(ai.g * 0.4)}, ${Math.floor(ai.b * 0.4)}, 1)`);
root.setProperty('--ai-bg-grad-2', `rgba(${ai.r}, ${ai.g}, ${ai.b}, ${bgOpacity})`);
root.setProperty('--user-bg-grad-1', `rgba(${us.r}, ${us.g}, ${us.b}, ${bgOpacity})`);
root.setProperty('--user-bg-grad-2', `rgba(${us.r}, ${us.g}, ${us.b}, ${Math.min(1, bgOpacity + 0.1) + 0.2})`);
root.setProperty('--glow-grad-1', `rgba(${Math.floor(gl.r * 0.8)}, ${Math.floor(gl.g * 0.8)}, ${Math.floor(gl.b * 0.8)}, 1)`);
root.setProperty('--glow-grad-2', `rgba(${gl.r}, ${gl.g}, ${gl.b}, 1)`);
root.setProperty('--chat-blur', `${bgBlur}px`);
updateWidgetInputsState(s.useDefaultStyle);
saveSettings();
}
function updateWidgetInputsState(disabled) {
const inputsToLock = ['wi-aiColor', 'wi-userColor', 'wi-textColor', 'wi-glowColor', 'wi-glowEnabled'];
inputsToLock.forEach(id => {
const el = document.getElementById(id);
if (el) {
el.disabled = disabled;
el.style.opacity = disabled ? '0.4' : '1';
el.style.cursor = disabled ? 'not-allowed' : 'pointer';
}
});
}
function updateWidgetUI() {
if (!elements.widget) return;
const valueInputs = ['aiColor', 'userColor', 'textColor', 'glowColor', 'bgOpacity', 'bgBlur'];
valueInputs.forEach(key => {
const el = document.getElementById(`wi-${key}`);
if (el) el.value = currentSettings[key] !== undefined ? currentSettings[key] : DEFAULT_SETTINGS[key];
});
const glowEl = document.getElementById('wi-glowEnabled');
if (glowEl) glowEl.checked = currentSettings.glowEnabled;
const useDefEl = document.getElementById('wi-useDefaultStyle');
if (useDefEl) useDefEl.checked = currentSettings.useDefaultStyle;
}
function createWidget() {
if (elements.widget) return;
const widget = document.createElement('div');
widget.id = 'chat-style-widget';
widget.innerHTML = buildWidgetHTML();
document.body.appendChild(widget);
elements.widget = widget;
attachWidgetListeners();
}
function buildWidgetHTML() {
return `
<div id="chat-style-panel">
<div class="widget-row" style="margin-bottom: 20px; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 15px;">
<label style="color: #E19A24;">Isekai ZERO Style</label>
<input type="checkbox" id="wi-useDefaultStyle">
</div>
<div class="widget-row">
<label>AI Chat Color</label>
<input type="color" id="wi-aiColor">
</div>
<div class="widget-row">
<label>User Chat Color</label>
<input type="color" id="wi-userColor">
</div>
<hr style="border: 0.5px solid rgba(255,255,255,0.1); margin: 15px 0;" />
<div class="widget-row">
<label>Dialogue Color</label>
<input type="color" id="wi-textColor">
</div>
<div class="widget-row">
<label>Glow Color</label>
<input type="color" id="wi-glowColor">
</div>
<div class="widget-row">
<label>Enable Text Glow</label>
<input type="checkbox" id="wi-glowEnabled">
</div>
<hr style="border: 0.5px solid rgba(255,255,255,0.1); margin: 15px 0;" />
<div class="widget-row">
<label>Background Opacity</label>
<input type="range" id="wi-bgOpacity" min="0" max="1" step="0.1">
</div>
<div class="widget-row">
<label>Background Blur</label>
<input type="range" id="wi-bgBlur" min="0" max="30" step="1">
</div>
<div class="widget-btn-grid">
<button class="widget-btn" id="wi-save-global" title="Make these settings the default for all new chats">Save as Global</button>
<button class="widget-btn" id="wi-reset" title="Wipe this chat's config and revert back to your Global Default">Revert Chat</button>
</div>
</div>
<button id="chat-style-toggle" title="Customize Chat Style">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<path d="M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"/>
</svg>
</button>
`;
}
function attachWidgetListeners() {
document.getElementById('chat-style-toggle').addEventListener('click', () => {
const panel = document.getElementById('chat-style-panel');
panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
});
const valueInputs = ['aiColor', 'userColor', 'textColor', 'glowColor', 'bgOpacity', 'bgBlur'];
valueInputs.forEach(key => {
document.getElementById(`wi-${key}`).addEventListener('input', (e) => {
currentSettings[key] = e.target.value;
applyStyleVariables();
});
});
document.getElementById('wi-glowEnabled').addEventListener('change', (e) => {
currentSettings.glowEnabled = e.target.checked;
applyStyleVariables();
});
document.getElementById('wi-useDefaultStyle').addEventListener('change', (e) => {
currentSettings.useDefaultStyle = e.target.checked;
applyStyleVariables();
});
document.getElementById('wi-save-global').addEventListener('click', (e) => {
localStorage.setItem(CONFIG.storagePrefix + 'global', JSON.stringify(currentSettings));
const btn = e.target;
const oldText = btn.textContent;
btn.textContent = "Saved to Global!";
setTimeout(() => btn.textContent = oldText, 2000);
});
document.getElementById('wi-reset').addEventListener('click', (e) => {
if (currentChatId) localStorage.removeItem(CONFIG.storagePrefix + currentChatId);
currentSettings = loadSettings(currentChatId);
updateWidgetUI();
applyStyleVariables();
const btn = e.target;
const oldText = btn.textContent;
btn.textContent = "Reverted!";
setTimeout(() => btn.textContent = oldText, 2000);
});
}
// INITIALIZATION
function init() {
migrateOldSettings();
currentChatId = getChatId();
currentSettings = loadSettings(currentChatId);
initStyles();
setupRouteListeners();
startIconSync();
createWidget();
updateWidgetUI();
applyStyleVariables();
updateStyles();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();