Disconnect Tools

Advanced Tools For VRChat Website

// ==UserScript==
// @name         Disconnect Tools
// @namespace    http://tampermonkey.net/
// @license      MIT
// @version      2025-04-11v0.9.5
// @description  Advanced Tools For VRChat Website
// @author       Disconnect3301
// @match        https://vrchat.com/*
// @grant        GM_addStyle
// @grant        GM_cookie
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @run-at       document-idle
// ==/UserScript==

'use strict';

let savedAvatars = GM_getValue('savedAvatars', []) || [];
let sortSettings = GM_getValue('sortSettings', { sortBy: 'default', isReversed: false });
let isCustomFavoritesMenuOpen = false;
let databaseLink = 'https://api.avtrdb.com/v2/avatar/search/vrcx?search=';
let morePages = 0;

let homeContentElement = null;
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;

const addMetod = {
    current : 'current',
    ID : 'ID'
}
let authorName = null;
let authorId = null;
let created_at = null;
let description = null;
let avatarID = null;
let imageUrl = null;
let avatarName = null;
let releaseStatus = null;
let updated_at = null;
let version = null;

let userId = null;
let authCookie = null;

GM_addStyle(`
    #notification-container {
        position: fixed;
        top: 20px;
        right: 20px;
        z-index: 3050;
        display: flex;
        flex-direction: column-reverse;
        align-items: flex-end;
        gap: 10px;
        pointer-events: none;
        transition: all .15s ease-in-out;
    }
    .notification {
        position: relative;
        min-width: 280px;
        max-width: 400px;
        width: auto;
        padding: 18px 35px 18px 20px;
        border-radius: 12px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        background: linear-gradient(135deg, #2d2d2d, #242424);
        color: #e0e0e0;
        font-family: 'Segoe UI', system-ui, sans-serif;
        font-size: 14px;
        line-height: 1.5;
        cursor: pointer;
        transform: translateY(-20px);
        opacity: 0;
        transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
        pointer-events: auto;
        overflow: hidden;
        border: 1px solid transparent;
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 10px;
    }
    .notification.show {
        transform: translateY(0);
        opacity: 1;
    }
    .notification.hide {
        height: 0 !important; /* Схлопываем высоту */
        padding: 0 !important; /* Убираем отступы */
        margin: 0 !important; /* Убираем внешние отступы */
        opacity: 0;
        transform: translateY(-20px);
        border-radius: 0;
        box-shadow: none;
        pointer-events: none;
    }
    .notification.info {
        border-left: 5px solid #2196F3;
        background-color: #2196F31A;
    }
    .notification.success {
        border-left: 5px solid #4CAF50;
        background-color: #4CAF501A;
    }
    .notification.warning {
        border-left: 5px solid #FFA726;
        background-color: #FFA7261A;
    }
    .notification.error {
        border-left: 5px solid #F44336;
        background-color: #F443361A;
    }
    .status-icon {
        font-size: 20px;
        opacity: 0.9;
    }
    .message {
        flex-grow: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        word-wrap: break-word;
    }
    .progress-bar {
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%; /* Занимает всю ширину */
        height: 3px; /* Фиксированная высота */
        background: linear-gradient(90deg, #ddd, #bbb);
        transform-origin: left;
        transform: scaleX(1); /* Начальное состояние */
        transition: transform 0.1s linear;
    }
    .notification:hover {
        transform: scale(1.02) translateY(-2px);
        box-shadow: 0 8px 18px rgba(0, 0, 0, 0.2);
    }
    @media (max-width: 480px) {
        .notification {
            width: 90vw;
            min-width: unset;
        }
    }
`);

GM_addStyle(`
    .btn-custom {
        --bs-btn-font-family: ;
        --bs-btn-font-weight: normal;
        --bs-btn-line-height: 1.25;
        --bs-btn-color: var(--bs-body-color);
        --bs-btn-bg: transparent;
        --bs-btn-border-width: 1px;
        --bs-btn-border-color: transparent;
        --bs-btn-hover-border-color: transparent;
        --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(10, 10, 13, 0.075);
        --bs-btn-disabled-opacity: 0.65;
        --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);
        display: inline-block;
        padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
        font-family: var(--bs-btn-font-family);
        font-size: var(--bs-btn-font-size);
        font-weight: var(--bs-btn-font-weight);
        line-height: var(--bs-btn-line-height);
        color: var(--bs-btn-color);
        text-align: center;
        vertical-align: middle;
        cursor: pointer;
        user-select: none;
        border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
        border-radius: var(--bs-btn-border-radius);
        background-color: var(--bs-btn-bg);
        background-image: var(--bs-gradient);
        transition: all .15s ease-in-out;
        --bs-btn-padding-y: 0.5rem;
        --bs-btn-padding-x: 1rem;
        --bs-btn-font-size: 1.25rem;
        --bs-btn-border-radius: 0.3rem;
        position: relative;
        flex: 1 1 auto;
        width: 100%;
        margin-top: calc(1px* -1);
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-bottom-right-radius: 0;
        border-bottom-left-radius: 0;
    }
    .btn-custom:active {
        color: var(--bs-btn-active-color);
        background-color: var(--bs-btn-active-bg);
        background-image: none;
        border-color: var(--bs-btn-active-border-color);
        z-index: 1;
    }
    .btn-custom:focus-visible {
        color: var(--bs-btn-hover-color);
        background-color: var(--bs-btn-hover-bg);
        background-image: var(--bs-gradient);
        border-color: var(--bs-btn-hover-border-color);
        outline: 0;
        box-shadow: var(--bs-btn-focus-box-shadow);
    }
    .btn-custom:hover {
        color: var(--bs-btn-hover-color);
        text-decoration: none;
        background-color: var(--bs-btn-hover-bg);
    }
    .css-yjay0l-custom {
        background: rgb(7, 36, 43);
        border: 2px solid rgb(5, 60, 72);
        color: rgb(106, 227, 249);
        display: flex;
        flex-direction: row;
        place-content: start space-between;
        -webkit-box-align: center;
        align-items: center;
        -webkit-box-pack: justify;
        height: 45px;
        border-radius: 8px !important;
        box-shadow: none !important;
        padding: 0px 10px !important;
    }
    .css-yjay0l-custom:hover {
        background: rgb(7, 52, 63);
        border-color: rgb(8, 108, 132);
        transform: scale(1.1);
    }
    .css-yjay0l-custom:active {
        color: var(--bs-btn-active-color);
        background-color: var(--bs-btn-active-bg);
        background-image: none;
        border-color: var(--bs-btn-active-border-color);
        z-index: 1;
    }

    .css-1vrq36y-custom {
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgb(6, 75, 92);
        color: rgb(106, 227, 249);
        padding: 5px;
        box-sizing: border-box;
        flex: 1 1 0%;
        outline: none !important;
        transition: all .15s ease-in-out;
    }
    .css-1vrq36y-custom.syncButton {
        width: 35px;
        height: 35px;
        padding: 0px;
    }
    .css-1vrq36y-custom:hover {
        border-color: rgb(8, 108, 132);
    }

    .avatarCardToggles {
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgb(6, 75, 92);
        color: rgb(106, 227, 249);
        padding: 5px;
        box-sizing: border-box;
        outline: none !important;
        transition: all .15s ease-in-out;
    }
    .avatarCardToggles:hover {
        border-color: rgb(8, 108, 132);
    }
    .avatarCardToggles.Remove {
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgba(255, 0, 0, 0.1); !important
        color: rgb(106, 227, 249);
        padding: 5px;
        box-sizing: border-box;
        outline: none !important;
        font-weight: bold;
    }
    .avatarCardToggles.Remove:hover {
        background: rgb(5, 25, 29) !important;
    }
`);

GM_addStyle(`
    .css-14ngdq4-custom {
        display: flex;
        background: rgb(54, 54, 54);
        border-radius: 4px;
        padding: 0.25rem 0.75rem;
        -webkit-box-align: center;
        align-items: center;
        font-weight: bold;
        font-size: 1.1rem;
        color: rgb(230, 230, 230);
    }
    .css-zjik7-custom {
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
    .AvatarNameSelection {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 20px;
    }
    .avatarCardTogglesSelection {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
        align-content: center;
        flex-direction: row;
        margin-top: 10px;
    }
    .css-qcqlg7-custom {
        display: flex;
        margin-bottom: 0.8rem;
        color: rgb(255, 255, 255);
        text-decoration: none;
        flex-direction: column;
        border-radius: 8px;
        background-color: rgb(24, 27, 31);
        transition: border-color 0.2s ease-in-out;
        overflow: visible;
    }
    .css-qcqlg7-custom:hover .card-top,
    .css-qcqlg7-custom:hover .card-bottom  {
        border-color: rgb(5, 77, 94);
    }
    .css-qcqlg7-custom:hover .platform-background {
        background-color: rgba(0, 0, 0, 0.5);
    }
    .css-1kj6np9-custom {
        padding-top: 75%;
        height: 0px;
        overflow: hidden;
        border-radius: 8px;
        position: relative;
        display: flex;
        flex-shrink: 0;
        margin-bottom: 0.5rem;
    }
    .avatarCardImage {
        width: 100%;
        height: 100%;
        top: 0px;
        left: 0px;
        position: absolute;
        z-index: 0;
        border: 4px solid #656565a8;
    }
    .css-1brgsnm-custom {
        display: flex;
        flex-direction: column;
        padding: 0.9rem;
        background-color: rgb(37, 42, 48);
        border-color: rgb(37, 42, 48);
        border-style: solid;
        border-width: 3px 3px 0px;
        border-radius: 8px 8px 0px 0px;
    }
    .css-1106r7n-custom {
        display: flex;
        flex-direction: column;
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0px;
        left: 0px;
        -webkit-box-align: center;
        align-items: center;
        -webkit-box-pack: end;
        justify-content: flex-end;
        z-index: 1;
        border-radius: 8px;
        border: 4px solid rgba(255, 255, 255, 0.184);
    }
    .css-1il99ht-custom {
        display: grid;
        grid-template-columns: 20px 1fr 1fr;
        gap: 0.25rem 1rem;
        -webkit-box-align: center;
        align-items: center;
        color: rgb(115, 115, 114);
    }
    .css-13sdljk-custom {
        position: relative;
        overflow: hidden;
        border-radius: 4px;
        display: flex;
    }
    .css-13sdljk-2-custom {
        display: flex;
        align-items: center;
        padding: 0.5rem 0.5rem;
    }
    .css-kfjcvw-custom {
        display: flex;
        flex-direction: column;
        padding: 0.9rem;
        border-style: solid;
        background-color: rgb(24, 27, 31);
        border-color: rgb(24, 27, 31);
        border-width: 0px 3px 3px;
        border-radius: 0px 0px 8px 8px;
    }
    .svg-icon {
        color: rgb(84, 181, 197);
        font-size: 20px;
        text-align: center;
        opacity: 1;
        transition: opacity 0.2s ease-in-out;
    }
    .css-1grfcoa-custom {
        display: flex;
        font-weight: bold;
        font-size: 0.85rem;
    }
    .css-so1s8h-custom {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        font-size: 0.85rem;
        white-space: nowrap;
    }
    .css-so1s8h-custom.Excellent {
        color: rgb(81, 255, 0);
    }
    .css-so1s8h-custom.Good {
        color: rgb(0, 255, 55);
    }
    .css-so1s8h-custom.Medium {
        color: rgb(255, 162, 41);
    }
    .css-so1s8h-custom.Poor {
        color: rgb(255, 84, 41);
    }
    .css-so1s8h-custom.VeryPoor {
        color: rgb(255, 0, 0);
    }
    .css-w9ziq0-custom {
        display: flex;
        margin-bottom: 0px;
        -webkit-box-align: center;
        align-items: center;
        height: 50px;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .css-1fttcpj-custom {
        display: flex;
        flex-direction: column;
    }
    .css-1bcpvc0-custom {
        display: flex;
        width: 100%;
        min-width: 10%;
        padding: 0.5rem 0.75rem;
        font-size: 1rem;
        line-height: 1.25;
        background: rgb(5, 25, 29);
        border: 2px solid rgb(5, 60, 72);
        border-radius: 4px;
        color: rgb(106, 227, 249);
        box-shadow: none;
        transition: 250ms ease-in-out;
        outline: none !important;
    }
    .css-1alc1xs-custom {
        padding: 0px;
        margin: 0px;
        color: rgb(14, 155, 177);
        outline: none !important;
    }
    .css-1alc1xs-custom:hover {
        color: rgb(9, 93, 106);
        text-decoration: none;
    }
    .css-1yw163h-custom {
        font-size: 1.2em;
        margin-top: 0.25rem;
        word-break: break-all;
        text-align: left;
        margin-bottom: 0px;
        color: rgb(255, 255, 255);
    }
    .css-1yw163h-custom:hover {
        color: #1fd1ed;
        text-decoration: none;
    }
    .realiseStatus {
        display: flex;
        border: 1px solid var(--bs-primary);
        border-radius: 4px;
        background-color: rgba(31, 209, 237, 0);
        color: var(--bs-primary);
        padding: 2px 10px;
        margin: 2px 5px 0px 5px;
        transition: background-color 0.2s ease-in-out;
        font-size: 0.85rem;
        outline: none !important;
        cursor: default;
    }
    .realiseStatus.private{
        color: rgb(238, 84, 84);
        border-color: rgb(238, 84, 84);
    }
    .platform {
        z-index: 2;
        display: flex;
        flex-direction: row;
        gap: 0.25rem;
        border: 4px solid #505153;
        border-left-width: 0px;
        border-bottom-width: 0px;
        border-bottom-left-radius: 0.5rem;
        border-top-right-radius: 0.5rem;
        
        padding: 0.5rem;
        transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
        --tw-backdrop-saturate: saturate(2);
        
        --tw-backdrop-blur: blur(8px) !important;
        
        backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia) !important;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
        transition-duration: 150ms !important;
        top: 0px !important;
        right: 0px !important;
        position: absolute !important;
    }
    .sync {
        z-index: 2;
        display: flex;
        flex-direction: row;
        gap: 0.25rem;
        border: 4px solid #505153;
        border-right-width: 0px;
        border-bottom-width: 0px;
        // border-bottom-right-radius: 0.5rem;
        // border-top-left-radius: 0.5rem;
        
        padding: 0.2rem;
        // transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
        // --tw-backdrop-saturate: saturate(2);
        
        // --tw-backdrop-blur: blur(8px) !important;
        
        // backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia) !important;
        transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) !important;
        transition-duration: 150ms !important;
        top: 0px !important;
        left: 0px !important;
        position: absolute !important;
    }
    .me-1-custom{
        margin-left: 0.25rem;
    }
    @keyframes modalEnter {
        0% { opacity: 0; transform: scale(1.15); }
        100% { opacity: 1; transform: scale(1); }
    }
    @keyframes modalExit {
        0% { opacity: 1; transform: scale(1); }
        100% { opacity: 0; transform: scale(1.15); }
    }
    .modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.7);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 2000;
        backdrop-filter: blur(4px);
        animation: modalEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
    .modal-overlay.exit {
        animation: modalExit 0.2s ease-out forwards;
    }
    .modal-content {
        background: rgb(24, 27, 31);
        color: #e0e0e0;
        padding: 1.5rem;
        border-radius: 8px;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
        width: 512px;
        border: 2px solid rgb(37, 42, 48);
        position: relative;
    }
    #avatar-search-modal .modal-content {
        background: rgb(24, 27, 31);
        color: #e0e0e0;
        padding: 1.5rem;
        border-radius: 8px;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
        width: 600px;
        max-height: 80vh;
        overflow-y: auto;
        border: 2px solid rgb(37, 42, 48);
        position: relative;
    }
    #avatar-search-modal h3 {
        margin: 0 0 1.25rem 0;
        font-size: 1.5rem;
        color: rgb(106, 227, 249);
        text-align: center;
        font-weight: 500;
    }
    #search-results {
        max-height: 400px;
        overflow-y: auto;
        padding: 1rem;
        border: 2px solid rgb(5, 60, 72);
        border-radius: 8px;
        background-color: rgb(24, 27, 31);

        &::-webkit-scrollbar {
            width: 8px;
        }

        &::-webkit-scrollbar-thumb {
            background-color: rgb(6, 75, 92);
            border-radius: 4px;
        }

        &::-webkit-scrollbar-track {
            background-color: rgb(37, 42, 48);
            border-radius: 4px;
        }
    }
    #avatar-search-modal #search-results {
        display: flex;
        flex-direction: column;
        align-items: center;
        max-height: 400px;
        overflow-y: auto;
        padding: 0.5rem 0;
        border-top: 1px solid rgb(5, 60, 72);
        border-bottom: 1px solid rgb(5, 60, 72);
        transition: all 0.2s ease-in-out;
    }
    .search-result-card {
        display: flex;
        align-items: center;
        background: rgb(37, 42, 48);
        padding: 0.75rem;
        border-radius: 4px;
        transition: all 0.2s ease-in-out;
    }
    .search-result-card:hover {
        background: rgb(10, 57, 69);
    }
    .search-result-card img {
        width: 65px;
        height: 50px;
        border-radius: 5px;
        margin-right: 10px;
        object-fit: cover;
        transition: all 0.2s ease-in-out;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
    }
    .search-result-card img:hover {
        transform: scale(2.5) translate(20%, 20%);
        box-shadow: 0 8px 10px rgb(0, 0, 0);
    }
    .search-result-card .info {
        flex-grow: 1;
    }
    .search-result-card h4 {
        margin: 0;
        font-size: 1rem;
        color: rgb(106, 227, 249);
    }
    .search-result-card p {
        margin: 0.25rem 0 0 0;
        font-size: 0.9rem;
        color: rgb(160, 160, 160);
    }
    .search-result-card button {
        padding: 0.5rem 1rem;
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgb(6, 75, 92);
        color: #e0e0e0;
        font-size: 0.9rem;
        cursor: pointer;
        transition: all 0.2s ease-in-out;
        max-width: 150px;
        min-width: 150px;
    }
    .search-result-card button:hover {
        background: rgb(8, 108, 132);
        border-color: rgb(8, 108, 132);
        transform: scale(1.05);
    }
    #avatar-search-modal #close-modal-btn {
        display: block;
        margin: 1.5rem auto 0;
        padding: 0.35rem 1.5rem;
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgb(6, 75, 92);
        color: #e0e0e0;
        font-size: 1rem;
        cursor: pointer;
        transition: all 0.2s ease-in-out;
    }
    #avatar-search-modal #close-modal-btn:hover {
        background: rgba(8, 108, 132, 0.25);
        border-color: rgb(8, 108, 132);
        transform: scale(1.05);
    }
    #pagination-container {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-bottom: 1rem;
    }
    #pagination-container button {
        margin: 0 5px;
    }
`);

GM_addStyle(`

    .vrc-button {
        border: 2px solid rgb(6, 75, 92);
        border-radius: 4px;
        background: rgb(6, 75, 92);
        color: rgb(106, 227, 249);
        padding: 0.4rem 1.2rem;
        box-sizing: border-box;
        // flex: 1 1 0%;
        outline: none !important;
        font-family: inherit;
        font-size: inherit;
        line-height: inherit;
        transition: all 0.2s ease-in-out;
    }

    .vrc-button:hover {
        border-color: rgb(8, 108, 132);
    }

    .sync-modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.7);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 2000;
        backdrop-filter: blur(4px);
        animation: modalEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .sync-modal-overlay.exit {
        animation: modalExit 0.2s ease-out forwards;
    }

    .modal-sync {
        background: rgb(24, 27, 31);
        color: #e0e0e0;
        padding: 2rem;
        border-radius: 8px;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
        width: 600px;
        max-height: 80vh;
        overflow-y: auto;
        border: 2px solid rgb(37, 42, 48);
        position: relative;
    }

    .modal-sync-title {
        margin: 0 0 1rem 0;
        font-size: 1.5rem;
        color: rgb(106, 227, 249);
        text-align: center;
        font-weight: 500;
    }

    #sync-changes-list {
        max-height: 470px;
        overflow-y: auto;
        padding: 10px;
        // margin: 1rem 0;
        // padding: 1rem;
        // background: rgb(7, 36, 43);
        // border-radius: 4px;
    }

    .sync-change-item {
        display: flex;
        align-items: center;
        padding: 0.75rem 1rem;
        border-radius: 6px;
        background: linear-gradient(135deg, rgba(20, 40, 50, 0.8), rgba(10, 25, 30, 0.8));
        box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
        margin-bottom: 0.5rem;
        transition: all 0.3s ease-in-out;
        border: 2px solid transparent;
        border-color: rgb(0, 40, 55);
    }

    .sync-change-item:hover {
        background: linear-gradient(135deg, rgba(25, 50, 60, 0.9), rgba(15, 35, 40, 0.9));
        border-color: rgba(106, 227, 249, 0.5);
        transform: scale(1.03);
    }

    .sync-change-label {
        font-size: 1rem;
        color: rgb(140, 230, 250);
        font-weight: bold;
        // margin-left: 0.5rem;
        // text-transform: capitalize;
        // letter-spacing: 0.5px;
        width: 100%;
    }

    .sync-change-checkbox {
        appearance: none;
        width: 1.5rem;
        height: 1.5rem;
        border-radius: 4px;
        background: rgb(10, 30, 35);
        border: 2px solid rgb(15, 50, 60);
        outline: none;
        cursor: pointer;
        transition: all 0.3s ease-in-out;
        position: relative;
        margin-right: 0.75rem;
    }
    .sync-change-checkbox:checked {
        // background: linear-gradient(135deg, rgb(106, 227, 249), rgb(30, 150, 170));
        background: rgb(90, 255, 60);
        border-color: rgb(255, 255, 255);
        box-shadow: 0 0 8px rgb(255, 255, 255);
    }


    .sync-field-name {
        font-size: 1rem;
        color: rgb(160, 180, 200);
        font-weight: bold;
        // margin-right: 1rem;
        // text-transform: uppercase;
        letter-spacing: 1px;
    }

    .sync-field-value {
        font-size: 0.9rem;
        color: rgb(180, 200, 220);
        word-break: break-word;
        line-height: 1.4;
        max-width: 200px;
        // white-space: nowrap;
        // overflow: hidden;
        // text-overflow: ellipsis;
        transition: all 0.2s ease-in-out;
    }

    // .sync-field-value:hover {
    //     color: rgb(140, 230, 250);
    //     text-decoration: underline;
    // }

    .modal-sync-buttons {
        display: flex;
        justify-content: space-between;
        // gap: 1rem;
        margin-top: 1rem;
    }

    // #sync-apply-changes,
    // #sync-cancel-changes {
    //     padding: 0.4rem 1.2rem;
    //     border: 2px solid transparent;
    //     border-radius: 4px;
    //     cursor: pointer;
    //     font-weight: 500;
    //     transition: all 0.2s ease-in-out;
    //     background-color: rgb(6, 75, 92);
    //     color: rgb(106, 227, 249);
    // }

    // #sync-apply-changes:hover {
    //     background-color: rgb(8, 108, 132);
    //     border-color: rgb(45, 162, 183);
    //     // transform: scale(1.05);
    // }

    // #sync-cancel-changes {
    //     background-color: rgb(7, 36, 43);
    //     color: rgb(238, 84, 84);
    //     border: 3px solid rgb(5, 60, 72);
    // }

    // #sync-cancel-changes:hover {
    //     background-color: rgb(5, 25, 29);
    //     border-color: rgb(21, 46, 63);
    //     // transform: scale(1.05);
    // }

    @keyframes modalEnter {
        0% { opacity: 0; transform: scale(1.15); }
        100% { opacity: 1; transform: scale(1); }
    }

    @keyframes modalExit {
        0% { opacity: 1; transform: scale(1); }
        100% { opacity: 0; transform: scale(1.15); }
    }

    #sync-changes-list::-webkit-scrollbar {
        width: 8px;
    }

    #sync-changes-list::-webkit-scrollbar-track {
        background: rgb(7, 36, 43);
    }

    #sync-changes-list::-webkit-scrollbar-thumb {
        background: rgba(106, 227, 249, 0.5);
        border-radius: 4px;
    }
    .sync-change-checkbox-container {
        display: flex;
        align-items: center;
    }
    .sync-change-arrow {
        margin-top: auto;
        margin-bottom: auto;
    }
    .sync-image {
        width: 150px;
        // height: 150px;
        border-radius: 10px;
        transition: all 0.2s ease-in-out;
    }
    .sync-image:hover {
        transform: scale(1.4) translateY(-12px);
    }
`);

window.onload = async function () {
    try {
        showNotification('Initializing...', 'info');
        await Get_ID_And_Cookie();
        await handleUrlChange(window.location.href);
        settingsTab();
        Button_CustomFavorites();
        showNotification('Everything is fine!', 'success');
    } catch (error) {
		showNotification(`Error while Initializing: ${error}`, 'error');
    }
};

async function Get_ID_And_Cookie() {
    try {
        authCookie = await getAuthCookieValue();
        const { responseText } = await SendRequest('GET', `https://api.vrchat.cloud/api/1/auth/user`, authCookie);
        const data = JSON.parse(responseText);
        userId = data.id;
    } catch (error) {
        showNotification(`Error while Get_ID_And_Cookie: ${error}`, 'error');
    }
}

history.pushState = function(state, title, url) {
    originalPushState.apply(history, arguments);
    handleUrlChange(url || window.location.href);
};

history.replaceState = function(state, title, url) {
    originalReplaceState.apply(history, arguments);
    handleUrlChange(url || window.location.href);
};

window.addEventListener('popstate', () => {
    handleUrlChange(window.location.href);
});

async function handleUrlChange(url) {
    try {
        const path = new URL(url, window.location.origin).pathname;
        // showNotification(`Path: ${path}`, 'info');
    
        if (path === '/home/custom-favorites' && !isCustomFavoritesMenuOpen) {
            homeContentElement = await awaitForElement('.home-content');
            const firstChild = homeContentElement.firstElementChild;
            if (firstChild === null) {
                openCustomFavorites();
            } else {
                firstChild.style.display = 'none';
                openCustomFavorites();
            }
        } else if (path !== '/home/custom-favorites' && isCustomFavoritesMenuOpen) {
            homeContentElement = await awaitForElement('.home-content');
            const oldCreatedWindow = document.getElementById('custom-favorites-window');
            if (oldCreatedWindow) {
                oldCreatedWindow.remove();
            }
            if (homeContentElement.firstElementChild) {
                homeContentElement.firstElementChild.style.display = 'block';
                isCustomFavoritesMenuOpen = false;
            } else {
                showNotification('Home content element not found, trying to open again', 'error');
                handleUrlChange(url);
            }
        }
    } catch (error) {
        showNotification(
            `Error while handling URL change: ${error.message}\nStack trace: ${error.stack}`,
            'error'
        );
    }
}

async function awaitForElement(selector, timeout = 5000) {
    try {
        await awaitForLoadingToDisappear();

        const element = document.querySelector(selector);
        if (element) return element;

        return new Promise((resolve, reject) => {
            let interval, timeoutId;

            const checkElement = () => {
                const element = document.querySelector(selector);
                if (element) {
                    clearInterval(interval);
                    clearTimeout(timeoutId);
                    resolve(element);
                }
            };

            interval = setInterval(checkElement, 100);

            timeoutId = setTimeout(() => {
                clearInterval(interval);
                showNotification(`Timed out waiting for ${selector}`, 'error');
                reject(new Error(`Timed out waiting for ${selector}`));
            }, timeout);
        });
    } catch (error) {
        showNotification(`Error while waiting for ${selector}: ${error.message}`, 'error');
        throw error;
    }
}

async function awaitForLoadingToDisappear(maxWaitTime = 1000) {
    return new Promise((resolve) => {
        const startTime = Date.now();
        let timeoutId;

        const checkLoading = () => {
            const loadingElement = document.querySelector('[aria-label="Loading"]');
            
            if (!loadingElement) {
                clearTimeout(timeoutId);
                resolve();
            } else if (Date.now() - startTime > maxWaitTime) {
                clearTimeout(timeoutId);
                resolve();
            } else {
                timeoutId = setTimeout(checkLoading, 100);
            }
        };
        
        checkLoading();
    });
}

function showNotification(message, type = 'info', duration = 3000) {
    if (!document.getElementById('notification-container')) {
        const container = document.createElement('div');
        container.id = 'notification-container';
        document.body.appendChild(container);
    }
    const container = document.getElementById('notification-container');
    if (type === 'error') {
        console.error(message);
        duration = 0;
    }

    const notification = document.createElement('div');
    notification.className = `notification ${type}`;
    notification.innerHTML = `
        <div class="status-icon">${getIcon(type)}</div>
        <div class="message">${message}</div>
        <div class="progress-bar"></div>
    `;

    container.prepend(notification);

    enforceNotificationLimit(container);

    setTimeout(() => notification.classList.add('show'), 50);

    const progressBar = notification.querySelector('.progress-bar');
    if (duration > 0) {
        progressBar.style.transitionDuration = `${duration}ms`;
        requestAnimationFrame(() => {
            progressBar.style.transform = 'scaleX(0)';
        });
    } else {
        progressBar.style.display = 'none';
    }

    function close() {
        notification.classList.remove('show');
        notification.classList.add('hide');

        notification.addEventListener('transitionend', () => {
            notification.remove();
        }, { once: true });
    }

    notification.addEventListener('click', close);

    if (duration > 0) {
        setTimeout(close, duration);
    }
}

function enforceNotificationLimit(container) {
    const notifications = Array.from(container.children);
    const maxNotifications = 10;

    if (notifications.length > maxNotifications) {
        const excessCount = notifications.length - maxNotifications;
        for (let i = 0; i < excessCount; i++) {
            const oldNotification = notifications[i];
            oldNotification.classList.remove('show');
            oldNotification.classList.add('hide');
            oldNotification.addEventListener('transitionend', () => {
                oldNotification.remove();
            }, { once: true });
        }
    }
}

function getIcon(type) {
    const icons = {
        info: 'ℹ️',
        success: '✅',
        warning: '⚠️',
        error: '❌'
    };
    return icons[type] || '';
}

async function Button_CustomFavorites() {
    const button = document.createElement('a');
	button.id = 'VRChat_ButtonList';
	button.classList.add('btn-custom', 'css-yjay0l-custom');
    button.title = "Open Custom Favorite Avatars";

    const leftIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    leftIcon.setAttribute('viewBox', '0 0 60 60');
    leftIcon.setAttribute('width', '20');
    leftIcon.setAttribute('height', '20');
    leftIcon.setAttribute('fill', 'none');
    leftIcon.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    leftIcon.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
    button.appendChild(leftIcon);

    const linearGradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
    linearGradient.setAttribute('id', 'paint0_linear_203_10527');
    linearGradient.setAttribute('gradientUnits', 'userSpaceOnUse');
    linearGradient.setAttribute('x1', '30');
    linearGradient.setAttribute('x2', '30');
    linearGradient.setAttribute('y1', '7');
    linearGradient.setAttribute('y2', '53');
    leftIcon.appendChild(linearGradient);

    const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
    stop1.setAttribute('offset', '0');
    stop1.setAttribute('stop-color', '#ce9ffc');
    linearGradient.appendChild(stop1);

    const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
    stop2.setAttribute('offset', '1');
    stop2.setAttribute('stop-color', '#7367f0');
    linearGradient.appendChild(stop2);

    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'url(#paint0_linear_203_10527)');
    leftIcon.appendChild(g);

    const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path1.setAttribute('d', 'm14 44c-.803 0-1.557-.313-2.122-.88l-10.999-10.999c-.566-.564-.879-1.318-.879-2.121s.313-1.557.88-2.122l10.999-10.999c.564-.566 1.318-.879 2.121-.879 1.654 0 3 1.346 3 3 0 .803-.313 1.557-.88 2.122l-8.878 8.878 8.879 8.879c.567.564.879 1.318.879 2.121 0 1.654-1.346 3-3 3z');
    path1.setAttribute('fill', 'url(#paint0_linear_203_10527)');
    g.appendChild(path1);

    const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path2.setAttribute('d', 'm46 45c-1.654 0-3-1.346-3-3 0-.803.313-1.557.88-2.122l8.878-8.878-8.879-8.879c-.566-.566-.879-1.32-.879-2.121 0-1.654 1.346-3 3-3 .803 0 1.557.313 2.122.88l10.999 10.999c.567.566.879 1.32.879 2.121 0 .803-.313 1.557-.88 2.122l-10.999 10.999c-.564.567-1.318.879-2.121.879z');
    path2.setAttribute('fill', 'url(#paint0_linear_203_10527)');
    g.appendChild(path2);

    const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path3.setAttribute('d', 'm21 53c-1.654 0-3-1.346-3-3 0-.398.078-.787.23-1.155l18.001-40.002c.47-1.12 1.557-1.843 2.769-1.843 1.654 0 3 1.346 3 3 0 .398-.078.787-.23 1.155l-18.001 40.002c-.47 1.12-1.557 1.843-2.769 1.843z');
    path3.setAttribute('fill', 'url(#paint0_linear_203_10527)');
    g.appendChild(path3);

    const textDiv = document.createElement('div');
    textDiv.textContent = "Custom Favorites";
    button.appendChild(textDiv);

    const rightIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    rightIcon.setAttribute('aria-hidden', 'true');
    rightIcon.setAttribute('focusable', 'false');
    rightIcon.setAttribute('data-prefix', 'fas');
    rightIcon.setAttribute('data-icon', 'angle-right');
    rightIcon.classList.add('svg-inline--fa', 'fa-angle-right', 'css-1efeorg', 'e9fqopp0');
    rightIcon.setAttribute('role', 'presentation');
    rightIcon.setAttribute('viewBox', '0 0 320 512');
    rightIcon.innerHTML = '<path fill="currentColor" d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"></path>';
    button.appendChild(rightIcon);

    const targetElement = await awaitForElement('div[role="group"].w-100.css-1bfow8s.btn-group-lg.btn-group-vertical');
    targetElement.appendChild(button);
    targetElement.insertBefore(button, targetElement.firstChild);

    button.addEventListener('click', async () => {
        history.pushState({ page: 'custom' }, "Custom Page", "/home/custom-favorites");
        document.title = 'Custom Favorites - VRChat';
	});
}

async function openCustomFavorites() {
    try {
        const HomeContent = homeContentElement;
    
    	const MainWindow = document.createElement('div');
    	MainWindow.id = 'custom-favorites-window';
        MainWindow.classList.add('pb-5', 'css-1fttcpj-custom');
        HomeContent.appendChild(MainWindow);
    
    	const Header = document.createElement('div');
        Header.classList.add('css-zjik7-custom');
        MainWindow.appendChild(Header);
    
    	const HeaderText = document.createElement('h2');
    	HeaderText.classList.add('css-w9ziq0-custom');
    	HeaderText.textContent = "Custom Favorites";
    	Header.appendChild(HeaderText);
    
    	const SearchBar = document.createElement('div');
        SearchBar.classList.add('css-zjik7-custom');
        MainWindow.appendChild(SearchBar);

    	const UserInput = document.createElement('input');
        UserInput.type = 'text';
        UserInput.setAttribute('aria-label', 'Search Favorites');
        UserInput.placeholder = 'Search Avatars';
        UserInput.classList.add('css-1bcpvc0-custom');
        UserInput.value = '';
        UserInput.addEventListener('input', () => {
            const query = UserInput.value.trim().toLowerCase();
            filterAvatars(query);
        });
        SearchBar.appendChild(UserInput);

        const sortContainer = document.createElement('div');
        sortContainer.classList.add('d-flex', 'align-items-center', 'gap-2');
        sortContainer.style.flex = 'none';

        const searchInDBButton = document.createElement('button');
        searchInDBButton.textContent = 'Search in Database';
        searchInDBButton.classList.add('px-3', 'css-1vrq36y-custom');
        searchInDBButton.style.flex = 'none';
        searchInDBButton.style.marginLeft = '10px';
        sortContainer.appendChild(searchInDBButton);

        searchInDBButton.addEventListener('click', () => {
            const query = UserInput.value.trim();
            showAvatarSearchModal(query || undefined);
        });
        
        const sortLabel = document.createElement('span');
        sortLabel.textContent = 'Sorting By:';
        sortLabel.style.color = '#888';
        sortLabel.style.marginLeft = '0.5rem';
        sortLabel.style.marginRight = '0.5rem';
        
        const sortSelect = document.createElement('select');
        sortSelect.classList.add('css-1bcpvc0-custom');
        sortSelect.style.width = '220px';
        sortSelect.innerHTML = `
            <option value="default">Date Added</option>
            <option value="lastUpdated">Last Updated</option>
            <option value="name">Name</option>
            <option value="performance">Performance</option>
        `;
        
        const reverseButton = document.createElement('img');
        reverseButton.src = 'https://img.icons8.com/?size=28&id=Ne3MRho4pubZ&format=png&color=6ae3f9';
        reverseButton.alt = 'Reverse Icon';
        reverseButton.classList.add('css-1vrq36y-custom');
        reverseButton.style.cursor = 'pointer';
        
        sortContainer.append(sortLabel, sortSelect, reverseButton);
        SearchBar.append(UserInput, sortContainer);

        let currentSort = sortSettings.sortBy;
        let isReversed = sortSettings.isReversed;

        sortSelect.addEventListener('change', () => {
            currentSort = sortSelect.value;
            sortAvatars(currentSort, isReversed);
            saveSortSettings(currentSort, isReversed);
        });

        reverseButton.addEventListener('click', () => {
            isReversed = !isReversed;
            sortAvatars(currentSort, isReversed);
            if (isReversed) {
                reverseButton.src = 'https://img.icons8.com/?size=28&id=r1k2t6YcvxL1&format=png&color=6ae3f9';
            } else {
                reverseButton.src = 'https://img.icons8.com/?size=28&id=Ne3MRho4pubZ&format=png&color=6ae3f9';
            }
            saveSortSettings(currentSort, isReversed);
        });

        sortSelect.value = currentSort;
        reverseButton.src = isReversed 
            ? 'https://img.icons8.com/?size=28&id=r1k2t6YcvxL1&format=png&color=6ae3f9'
            : 'https://img.icons8.com/?size=28&id=Ne3MRho4pubZ&format=png&color=6ae3f9';
    
    	const DisplayFunctions = document.createElement('div');
        DisplayFunctions.classList.add('css-zjik7-custom');
        MainWindow.appendChild(DisplayFunctions);
    
    	const NamedList = document.createElement('div');
    	NamedList.classList.add('align-items-center', 'css-zjik7-custom');
    	DisplayFunctions.appendChild(NamedList);
    
    	const NameText = document.createElement('h2');
    	NameText.classList.add('css-w9ziq0-custom');
    	NameText.textContent = "Total Avatars";
    
    	const Counter = document.createElement('div');
    	Counter.classList.add('ms-2', 'css-14ngdq4-custom');
    
        const innerDiv1 = document.createElement('div');
        innerDiv1.classList.add('counter');
        innerDiv1.textContent = '0';
    
        const innerDiv2 = document.createElement('div');
        innerDiv2.classList.add('mx-1');
        innerDiv2.textContent = '/';
    
        const innerDiv3 = document.createElement('div');
        innerDiv3.textContent = '∞';
        Counter.append(innerDiv1, innerDiv2, innerDiv3);
        
    	NamedList.append(NameText, Counter);
    
    	const ButtonsSelection = document.createElement('div');
    	ButtonsSelection.classList.add('align-items-center', 'justify-content-center', 'justify-content-md-end', 'flex-column', 'flex-md-row', 'flex-1', 'css-zjik7-custom');
    	DisplayFunctions.appendChild(ButtonsSelection);
    
    	const Option1 = document.createElement('div');
    	Option1.classList.add('css-13sdljk-custom');
    	ButtonsSelection.appendChild(Option1);
    
    	const SaveCurrentAvatar = document.createElement('button');
    	SaveCurrentAvatar.textContent = 'Save Current Avatar';
    	SaveCurrentAvatar.classList.add('px-3', 'me-1-custom', 'css-1vrq36y-custom');
    	Option1.appendChild(SaveCurrentAvatar);
    	SaveCurrentAvatar.addEventListener('click', async (e) => {
        	e.preventDefault();
    		AddAvatar(addMetod.current);
    	});
    
    	const Option2 = document.createElement('div');
    	Option2.classList.add('css-13sdljk-custom');
    	ButtonsSelection.appendChild(Option2);
    
    	const SaveByID = document.createElement('button');
    	SaveByID.textContent = 'Save By ID';
    	SaveByID.classList.add('px-3', 'me-1-custom', 'css-1vrq36y-custom');
    	Option2.appendChild(SaveByID);
    	SaveByID.addEventListener('click', async (e) => {
        	e.preventDefault();
            showSaveByIDModal();
    	});

    	const AvatarsWindow = document.createElement('div');
    	AvatarsWindow.id = 'custom-avatars';
        AvatarsWindow.classList.add('tw-grid', 'tw-grid-cols-1', 'sm:tw-grid-cols-2', 'lg:tw-grid-cols-3', '3xl:tw-grid-cols-4', 'tw-grid-flow-row', 'tw-gap-4');
        MainWindow.appendChild(AvatarsWindow);
    
        savedAvatars = GM_getValue('savedAvatars', []);
        const chunkSize = 5;
        const chunkedAvatars = Array(Math.ceil(savedAvatars.length / chunkSize))
            .fill(null)
            .map((_, i) => savedAvatars.slice(i * chunkSize, (i + 1) * chunkSize));
        const avatarCreationPromises = chunkedAvatars.map((avatars, index) => {
            return new Promise(resolve => {
                setTimeout(() => {
                    avatars.forEach(createAvatarCard);
                    resolve();
                }, index * 20);
            });
        });
        await Promise.all(avatarCreationPromises);

        updateCounter();
        sortAvatars(currentSort, isReversed);
        isCustomFavoritesMenuOpen = true;
    } catch (error) {
        showNotification(`Error Message: ${error}`, 'error');
    }
}

function parseCustomDate(dateString) {
    if (!dateString) return new Date(0);
    const dateParts = dateString.split(', ');
    if (dateParts.length === 2) {
        const [date, time] = dateParts;
        const [day, month, year] = date.split('.').map(Number);
        const [hours, minutes, seconds] = time.split(':').map(Number);
        return new Date(year, month - 1, day, hours, minutes, seconds);
    } else if (dateString.includes('.')) {
        const [day, month, year] = dateString.split('.').map(Number);
        return new Date(year, month - 1, day);
    } else if (dateString.includes('-')) {
        return new Date(dateString);
    }
    return new Date(dateString);
}

function sortAvatars(sortBy, reverse) {
    const avatarsContainer = document.getElementById('custom-avatars');
    const avatars = Array.from(avatarsContainer.children);

    avatars.sort((a, b) => {
        const avatarA = savedAvatars.find(av => av.avatarID === a.dataset.avatarId);
        const avatarB = savedAvatars.find(av => av.avatarID === b.dataset.avatarId);
        if (!avatarA || !avatarB) return 0;

        let comparison = 0;

        switch (sortBy) {
            case 'default':
                const dateA = parseCustomDate(avatarA.dateAdded);
                const dateB = parseCustomDate(avatarB.dateAdded);
                comparison = dateB - dateA;
                break;

            case 'lastUpdated':
                const updatedA = parseCustomDate(avatarA.updated_at);
                const updatedB = parseCustomDate(avatarB.updated_at);
                comparison = updatedB - updatedA;
                break;

            case 'name':
                comparison = avatarA.avatarName.localeCompare(avatarB.avatarName);
                break;
            
            case 'performance':
                const order = { 'Excellent': 5, 'Good': 4, 'Medium': 3, 'Poor': 2, 'VeryPoor': 1, 'None': 0 };
                comparison = (order[avatarB.PC_Performance] || 0) - (order[avatarA.PC_Performance] || 0);
                break;
        }

        return reverse ? -comparison : comparison;
    });

    avatarsContainer.innerHTML = '';
    avatars.forEach(avatar => avatarsContainer.appendChild(avatar));
}

function saveSortSettings(currentSort, isReversed) {
    sortSettings = { sortBy: currentSort, isReversed: isReversed };
    GM_setValue('sortSettings', sortSettings);
}

function updateCounter() {
    const avatarWindow = document.getElementById('custom-avatars');
    const counter = document.querySelector('.counter');

    if (avatarWindow && counter) {
        const avatarCount = avatarWindow.children.length;
        counter.textContent = avatarCount;
    }
}

function filterAvatars(query) {
    const avatars = document.querySelectorAll('.AvatarCard');
    avatars.forEach(avatar => {
        const avatarName = avatar.querySelector('.css-1yw163h-custom').textContent.toLowerCase();
        const authorName = avatar.querySelector('.css-so1s8h-custom').textContent.toLowerCase();
        const isMatch = avatarName.includes(query) || authorName.includes(query);
        avatar.style.display = isMatch ? 'block' : 'none';
    });
}

async function getAuthCookieValue() {
    return new Promise((resolve) => {
        GM_cookie.list({
            domain: "vrchat.com",
            name: "auth"
        }, (cookies) => {
            resolve(cookies?.[0]?.value || null);
        });
    });
}

async function getUserID(keyName) {
    const data = JSON.parse(localStorage.getItem(keyName));
    if (!Array.isArray(data) || !data[0]?.user_id) {
        throw new Error('user_id not found in first item of array');
    }
    return data[0].user_id;
}

async function SendRequest(method, url, authCookie) {
    // showNotification(`Sending request to ${url}`, 'info', 15000);
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method,
            url,
            headers: {
                "Cookie": `auth=${authCookie}`
            },
            onload: async function(response) {
                if (response.status === 200) {
                    // Debug
                    const { responseText } = response;
                    const data = JSON.parse(responseText);
                    console.dir(data);
                    showNotification(`Request to ${url} completed with status ${response.status}`, 'success');
                    // -------

                    resolve(response);
                } else if (response.status === 404) {
                    showNotification(`Request failed!\nStatus: ${response.status}\nError: ${response.responseText}`, 'warning', 15000);
                    resolve(null);
                } else {
                    showNotification(`Request failed!\nStatus: ${response.status}\nError: ${response.responseText}`, 'warning', 15000);
                }
            }
        });
    });
}

async function GetPrivateAvatarInfo(method, url, authCookie) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method,
            url,
            headers: {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
                "Cookie": `auth=${authCookie}`
            },
            onload: function (response) {
                const htmlContent = response.responseText;
                const parser = new DOMParser();
                const doc = parser.parseFromString(htmlContent, "text/html");

                const metaData = {
                    title: doc.title,
                    metaTags: Array.from(doc.querySelectorAll('meta')).map(tag => ({
                        name: tag.getAttribute('name') || tag.getAttribute('property') || tag.getAttribute('itemprop'),
                        content: tag.getAttribute('content')
                    })),
                    scripts: Array.from(doc.querySelectorAll('script')).map(script => script.src),
                    styles: Array.from(doc.querySelectorAll('link[rel="stylesheet"]')).map(link => link.href)
                };

                const ogImageTag = metaData.metaTags.find(tag => tag.name === 'og:image');
                const ogTitleTag = metaData.metaTags.find(tag => tag.name === 'og:title');
                const ogDescriptionTag = metaData.metaTags.find(tag => tag.name === 'og:description');

                const [name, author] = ogTitleTag.content.trim().split(/[\s]*by[\s]*/).map(x => x.trim());

                const result = {
                    PrivateImage: ogImageTag.content,
                    PrivateName: name,
                    PrivateDescription: ogDescriptionTag.content,
                    PrivateAuthor: author
                };

                resolve(result);
            }
        });
    })
}

function FixDisplayTime(time) {
    let date = new Date(time);
    let day = String(date.getDate()).padStart(2, '0');
    let month = String(date.getMonth() + 1).padStart(2, '0');
    let year = date.getFullYear();
    let formattedDate = `${day}.${month}.${year}`;
    return formattedDate;
}

async function AddAvatar(metod, avatarID = null) {
    let link = null;

    if (metod == addMetod.current) {
        link = `https://api.vrchat.cloud/api/1/users/${userId}/avatar`;
    } else if (metod == addMetod.ID) {
        link = `https://api.vrchat.cloud/api/1/avatars/${avatarID}`
    }

    let isUnavailable = false;
    let unavailableData = null;
    let data = null;

    const response = await SendRequest('GET', `${link}`, authCookie);
    if (response) {
        const { responseText } = response;
        if (responseText) {
            data = JSON.parse(responseText);
        }
    } else {
        isUnavailable = true;
        let privateAvatarInfo = await GetPrivateAvatarInfo('GET', `https://vrchat.com/home/avatar/${avatarID}`, authCookie);

        unavailableData = {
            PrivateImage: privateAvatarInfo.PrivateImage,
            PrivateName: privateAvatarInfo.PrivateName,
            PrivateDescription: privateAvatarInfo.PrivateDescription,
            PrivateAuthor: privateAvatarInfo.PrivateAuthor
        };
    }

    let hasPC = false;
    let hasQuest = false;
    let pcPerformance = '';
    let questPerformance = '';

    if (!isUnavailable && Array.isArray(data.unityPackages) && data.unityPackages.length > 0) {
        data.unityPackages.forEach(pkg => {
            let category = '';
            if (pkg.platform === 'standalonewindows' && pkg.variant === 'security') {
                category = 'pc';
                hasPC = true;
                pcPerformance = pkg.performanceRating;
            } else if (pkg.platform === 'android' && pkg.variant === 'security') {
                category = 'quest';
                hasQuest = true;
                questPerformance = pkg.performanceRating;
            }
        });
    }
    let avatarData = null;

    let currentTime = new Date().toLocaleString();

    if (!isUnavailable) {
        avatarData = {
            authorName: data.authorName,
            authorId: data.authorId,
            created_at: FixDisplayTime(data.created_at),
            description: data.description,
            avatarID: data.id,
            imageUrl: data.imageUrl,
            avatarName: data.name,
            releaseStatus: data.releaseStatus,
            updated_at: FixDisplayTime(data.updated_at),
            version: data.version,
    
            isPlatformPC: hasPC,
            PC_Performance: pcPerformance,
            isPlatformQuest: hasQuest,
            Quest_Performance: questPerformance,

            isUnavalibleAvatar: isUnavailable,
            dateAdded: currentTime
        };
    } else {
        avatarData = {
            authorName: unavailableData.PrivateAuthor,
            authorId: 'Unknown',
            created_at: 'Unknown',
            description: unavailableData.PrivateDescription,
            avatarID: avatarID,
            imageUrl: unavailableData.PrivateImage,
            avatarName: unavailableData.PrivateName,
            releaseStatus: 'private',
            updated_at: 'Unknown',
            version: 'Unknown',
    
            isPlatformPC: hasPC,
            PC_Performance: 'Unknown',
            isPlatformQuest: hasQuest,
            Quest_Performance: 'Unknown',

            isUnavalibleAvatar: isUnavailable,
            dateAdded: currentTime
        };
    }

    if (!savedAvatars.some(a => a.avatarID === avatarData.avatarID)) {
        savedAvatars.push(avatarData);
        GM_setValue('savedAvatars', savedAvatars);
        createAvatarCard(avatarData);
        showNotification(`Saved Avatar: ${avatarData.avatarName}!`, 'success');
    } else {
        showNotification('This avatar is already saved!', 'warning', 7000);
    }
}

function createAvatarCard(avatar) {
    const AvatarsSelection = document.getElementById('custom-avatars');

    const AvatarCard = document.createElement('div');
    AvatarCard.classList.add('css-qcqlg7-custom', 'AvatarCard');
    AvatarCard.setAttribute('data-avatar-id', avatar.avatarID);
    AvatarsSelection.appendChild(AvatarCard);

	const ImageAndName = document.createElement('div');
    ImageAndName.classList.add('css-1brgsnm-custom', 'card-top');
    if (avatar.isUnavalibleAvatar) {
        ImageAndName.style.borderColor = 'rgb(238, 84, 84)';
    }
    if (avatar.PC_Performance === 'None') {
        ImageAndName.style.borderColor = 'rgb(235, 114, 33)';
    }
	AvatarCard.appendChild(ImageAndName);

    const linkElement = document.createElement('a');
    linkElement.setAttribute('aria-label', 'Avatar Image');
    linkElement.classList.add('css-1kj6np9-custom');
    linkElement.href = `/home/avatar/${avatar.avatarID}`;
    ImageAndName.appendChild(linkElement);
    
    const syncContainer = document.createElement('div');
    syncContainer.classList.add('sync');
    linkElement.appendChild(syncContainer);

    const syncImage = document.createElement('img');
    syncImage.src = 'https://img.icons8.com/?size=28&id=YyqIYbMdZ1i5&format=png&color=6ae3f9';
    syncImage.classList.add('css-1vrq36y-custom', 'syncButton');
    syncImage.addEventListener('click', async (event) => {
        event.stopPropagation();
        event.preventDefault();
        await syncAvatar(avatar.avatarID);
    });
    syncContainer.appendChild(syncImage);
    
    const iconDiv = document.createElement('div');
    iconDiv.classList.add('platform', 'platform-background');
    if (avatar.isUnavalibleAvatar) {
        iconDiv.style.display = 'none';
    }
    linkElement.appendChild(iconDiv);

    if (avatar.isPlatformPC) {
        const Windows = document.createElement('div');
        Windows.setAttribute('role', 'note');
        Windows.setAttribute('title', 'Is a Windows Avatar');
        Windows.classList.add('tw-flex', 'tw-items-center', 'tw-justify-center', 'tw-w-6', 'tw-h-6', 'tw-border', 'tw-border-solid', 'tw-border-current', 'tw-rounded-full');
        Windows.style.color = 'rgb(23, 120, 255)';
        iconDiv.appendChild(Windows);
    
        const PC_Icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        PC_Icon.setAttribute('aria-hidden', 'true');
        PC_Icon.setAttribute('focusable', 'false');
        PC_Icon.setAttribute('data-prefix', 'fab');
        PC_Icon.setAttribute('data-icon', 'windows');
        PC_Icon.classList.add('svg-inline--fa', 'fa-windows', 'css-1efeorg', 'e9fqopp0');
        PC_Icon.setAttribute('role', 'presentation');
        PC_Icon.setAttribute('viewBox', '0 0 448 512');
        Windows.appendChild(PC_Icon);
    
        const PC_IconPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        PC_IconPath.setAttribute('fill', 'currentColor');
        PC_IconPath.setAttribute('d', 'M0 93.7l183.6-25.3v177.4H0V93.7zm0 324.6l183.6 25.3V268.4H0v149.9zm203.8 28L448 480V268.4H203.8v177.9zm0-380.6v180.1H448V32L203.8 65.7z');
        PC_Icon.appendChild(PC_IconPath);
    }

    if (avatar.isPlatformQuest) {
        const Quest = document.createElement('div');
        Quest.setAttribute('role', 'note');
        Quest.setAttribute('title', 'Is an Android Avatar');
        Quest.classList.add('tw-flex', 'tw-items-center', 'tw-justify-center', 'tw-w-6', 'tw-h-6', 'tw-border', 'tw-border-solid', 'tw-border-current', 'tw-rounded-full');
        Quest.style.color = 'rgb(43, 207, 92)';
        iconDiv.appendChild(Quest);
    
        const Quest_Icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        Quest_Icon.setAttribute('aria-hidden', 'true');
        Quest_Icon.setAttribute('focusable', 'false');
        Quest_Icon.setAttribute('data-prefix', 'fab');
        Quest_Icon.setAttribute('data-icon', 'windows');
        Quest_Icon.classList.add('svg-inline--fa', 'fa-windows', 'css-1efeorg', 'e9fqopp0');
        Quest_Icon.setAttribute('role', 'presentation');
        Quest_Icon.setAttribute('viewBox', '0 0 576 512');
        Quest.appendChild(Quest_Icon);
    
        const Quest_IconPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        Quest_IconPath.setAttribute('fill', 'currentColor');
        Quest_IconPath.setAttribute('d', 'M420.55,301.93a24,24,0,1,1,24-24,24,24,0,0,1-24,24m-265.1,0a24,24,0,1,1,24-24,24,24,0,0,1-24,24m273.7-144.48,47.94-83a10,10,0,1,0-17.27-10h0l-48.54,84.07a301.25,301.25,0,0,0-246.56,0L116.18,64.45a10,10,0,1,0-17.27,10h0l47.94,83C64.53,202.22,8.24,285.55,0,384H576c-8.24-98.45-64.54-181.78-146.85-226.55');
        Quest_Icon.appendChild(Quest_IconPath);
    }

    const imgElement = document.createElement('img');
    if (avatar.isUnavalibleAvatar && !avatar.authorName) {
        imgElement.src = 'https://assets.vrchat.com/default/unavailable-avatar.png';
    } else{
        imgElement.src = `${avatar.imageUrl}`;
    }
    imgElement.alt = `${avatar.avatarName}`;
    imgElement.classList.add('avatarCardImage');
    linkElement.appendChild(imgElement);

    const avatarDisplayName = document.createElement('div');
    avatarDisplayName.classList.add('AvatarNameSelection');
    ImageAndName.appendChild(avatarDisplayName);

    const OpenAvatarPage = document.createElement('a');
    OpenAvatarPage.setAttribute('aria-label', 'Open Avatar Page');
    OpenAvatarPage.classList.add('css-1alc1xs-custom');
    OpenAvatarPage.href = `/home/avatar/${avatar.avatarID}`;
    avatarDisplayName.appendChild(OpenAvatarPage);

    const avatarH4 = document.createElement('h4');
    avatarH4.classList.add('css-1yw163h-custom');
    avatarH4.textContent = `${avatar.avatarName}`;
    avatarH4.title = `Description:\n${avatar.description}`;
    if (avatarH4.textContent.length > 23) {
        const fontSize = 1.1 - (avatarH4.textContent.length - 23) * 0.02;
        avatarH4.style.fontSize = `${fontSize}rem`;
    }
    OpenAvatarPage.appendChild(avatarH4);

    const releaseStatus = document.createElement('h2');
    releaseStatus.classList.add('realiseStatus');
    if (avatar.releaseStatus === 'private') {
        if (avatar.isUnavalibleAvatar) {
            avatar.releaseStatus = avatar.authorName ? 'private/deleted' : 'Fully Deleted';
        }
    }
    if (['private', 'private/deleted', 'Fully Deleted'].includes(avatar.releaseStatus)) {
        releaseStatus.classList.add('private');
        if (avatar.authorId === userId) {
            releaseStatus.title = 'Only you can use this avatar';
        }
    }
    releaseStatus.textContent = avatar.releaseStatus;
    avatarDisplayName.appendChild(releaseStatus);

	const mainDiv = document.createElement('div');
    mainDiv.classList.add('css-kfjcvw-custom', 'card-bottom');
    if (avatar.isUnavalibleAvatar) {
        mainDiv.style.borderColor = 'rgb(238, 84, 84)';
    }
    if (avatar.PC_Performance === 'None') {
        mainDiv.style.borderColor = 'rgb(235, 114, 33)';
    }
	AvatarCard.appendChild(mainDiv);

    const innerDiv = document.createElement('div');
    innerDiv.classList.add('css-1il99ht-custom');
    mainDiv.appendChild(innerDiv);

    const userSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    userSvg.classList.add('svg-icon');
    userSvg.setAttribute('viewBox', '0 0 448 512');
    userSvg.setAttribute('color', '#54b5c5');
    userSvg.setAttribute('width', '20');
    innerDiv.appendChild(userSvg);

    const userPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    userPath.setAttribute('fill', 'currentColor');
    userPath.setAttribute('d', 'M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z');
    userSvg.appendChild(userPath);

    const authorText = document.createElement('div');
    authorText.classList.add('css-1grfcoa-custom');
    authorText.textContent = 'Author';
    innerDiv.appendChild(authorText);

    const userLink = document.createElement('div');
    userLink.classList.add('css-so1s8h-custom');
    innerDiv.appendChild(userLink);

    const link = document.createElement('a');
    if (!avatar.isUnavalibleAvatar) {
        link.href = `/home/user/${avatar.authorId}`;
    }
    link.textContent = `${avatar.authorName}`;
    userLink.appendChild(link);

    const cloudSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    cloudSvg.classList.add('svg-icon');
    cloudSvg.setAttribute('viewBox', '0 0 640 512');
    cloudSvg.setAttribute('color', '#54b5c5');
    cloudSvg.setAttribute('width', '20');
    innerDiv.appendChild(cloudSvg);

    const cloudPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    cloudPath.setAttribute('fill', 'currentColor');
    cloudPath.setAttribute('d', 'M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128l-368 0zm79-217c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l39-39L296 392c0 13.3 10.7 24 24 24s24-10.7 24-24l0-134.1 39 39c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0l-80 80z');
    cloudSvg.appendChild(cloudPath);

    const updatedText = document.createElement('div');
    updatedText.classList.add('css-1grfcoa-custom');
    updatedText.textContent = 'Last Updated';
    innerDiv.appendChild(updatedText);

    const dateDiv = document.createElement('div');
    dateDiv.classList.add('text-start', 'css-so1s8h-custom');
    dateDiv.textContent = `${avatar.updated_at}`;
    dateDiv.setAttribute('title', `Created: ${avatar.created_at}\nLast Update: ${avatar.updated_at}\nAvatar Version: ${avatar.version}\nAdded In Favortes: ${avatar.dateAdded}`);
    innerDiv.appendChild(dateDiv);

    const perfomanceElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    perfomanceElement.classList.add('svg-icon');
    perfomanceElement.setAttribute('viewBox', '0 0 512 512');
    perfomanceElement.setAttribute('color', '#54b5c5');
    perfomanceElement.setAttribute('width', '20');
    innerDiv.appendChild(perfomanceElement);

    const performanceSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    performanceSVG.setAttribute('fill', 'currentColor');
    performanceSVG.setAttribute('d', 'M159.3 5.4c7.8-7.3 19.9-7.2 27.7 .1c27.6 25.9 53.5 53.8 77.7 84c11-14.4 23.5-30.1 37-42.9c7.9-7.4 20.1-7.4 28 .1c34.6 33 63.9 76.6 84.5 118c20.3 40.8 33.8 82.5 33.8 111.9C448 404.2 348.2 512 224 512C98.4 512 0 404.1 0 276.5c0-38.4 17.8-85.3 45.4-131.7C73.3 97.7 112.7 48.6 159.3 5.4zM225.7 416c25.3 0 47.7-7 68.8-21c42.1-29.4 53.4-88.2 28.1-134.4c-4.5-9-16-9.6-22.5-2l-25.2 29.3c-6.6 7.6-18.5 7.4-24.7-.5c-16.5-21-46-58.5-62.8-79.8c-6.3-8-18.3-8.1-24.7-.1c-33.8 42.5-50.8 69.3-50.8 99.4C112 375.4 162.6 416 225.7 416z');
    perfomanceElement.appendChild(performanceSVG);

    const perfomance = document.createElement('div');
    perfomance.classList.add('css-1grfcoa-custom');
    perfomance.textContent = 'Performance';
    innerDiv.appendChild(perfomance);

    const PCText = document.createElement('div');
    PCText.classList.add('text-start', 'css-so1s8h-custom');
    PCText.textContent = `PC`;
    innerDiv.appendChild(PCText);

    const raiting = document.createElement('div');
    if (avatar.PC_Performance === 'Excellent') {
        raiting.classList.add('text-start', 'css-so1s8h-custom', 'Excellent');
    } else if (avatar.PC_Performance === 'Good') {
        raiting.classList.add('text-start', 'css-so1s8h-custom', 'Good');
    } else if (avatar.PC_Performance === 'Medium') {
        raiting.classList.add('text-start', 'css-so1s8h-custom', 'Medium');
    } else if (avatar.PC_Performance === 'Poor') {
        raiting.classList.add('text-start', 'css-so1s8h-custom', 'Poor');
    } else if (avatar.PC_Performance === 'VeryPoor') {
        raiting.classList.add('text-start', 'css-so1s8h-custom', 'VeryPoor');
    }
    if (avatar.PC_Performance !== 'None') {
        raiting.textContent = `${avatar.PC_Performance}`;
    } else {
        raiting.textContent = 'Security Failed';
        raiting.style.color = 'red';
        raiting.style.textDecoration = 'underline';
        raiting.style.textDecorationLine = 'spelling-error';
    }
    raiting.style.marginLeft = '10px';
    PCText.appendChild(raiting);

    if (avatar.PC_Performance !== 'None' && avatar.PC_Performance !== 'Unknown' || !avatar.isUnavalibleAvatar) {
        const verypoorImage = document.createElement('img');
        if (avatar.PC_Performance === 'Excellent') {
            verypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/b7e99cd3c42a6f1ff2e6f3faaada0e75366945997a7fa5e7e014d26b1d100ef7.svg';
        } else if (avatar.PC_Performance === 'Good') {
            verypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/db3f587335a6602a84d0f0f18d6fbb10904973d0ddb659009f0fc56b3d1f026b.svg';
        } else if (avatar.PC_Performance === 'Medium') {
            verypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/24001ed5aa8ebabaa63a09ffb88ccecccc4c5feb1b4179579e8e6c9f1fed3f16.svg';
        } else if (avatar.PC_Performance === 'Poor') {
            verypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/467c01a863f0a61d30a09465f743678c95a5e6ae6d439b2fecd257464ec111d0.svg';
        } else if (avatar.PC_Performance === 'VeryPoor') {
            verypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/b4bf11dfbd8c3076cb66e8457b3f78659854700e79d5256516e205e37af89247.svg';
        } else if (avatar.PC_Performance === 'None') {
            verypoorImage.style.display = 'none';
        }
        // verypoorImage.alt = 'Avatar Icon';
        verypoorImage.style.width = '20px';
        verypoorImage.style.height = '20px';
        verypoorImage.style.marginLeft = '10px';
        verypoorImage.style.marginRight = '10px';
        verypoorImage.classList.add('css-1il99ht-custom');
        raiting.appendChild(verypoorImage);
    }

    const QuestText = document.createElement('div');
    QuestText.classList.add('text-start', 'css-so1s8h-custom');
    QuestText.textContent = `Quest`;
    PCText.appendChild(QuestText);

    const Qraiting = document.createElement('div');
    if (avatar.Quest_Performance === 'Excellent') {
        Qraiting.classList.add('text-start', 'css-so1s8h-custom', 'Excellent');
    } else if (avatar.Quest_Performance === 'Good') {
        Qraiting.classList.add('text-start', 'css-so1s8h-custom', 'Good');
    } else if (avatar.Quest_Performance === 'Medium') {
        Qraiting.classList.add('text-start', 'css-so1s8h-custom', 'Medium');
    } else if (avatar.Quest_Performance === 'Poor') {
        Qraiting.classList.add('text-start', 'css-so1s8h-custom', 'Poor');
    } else if (avatar.Quest_Performance === 'VeryPoor') {
        Qraiting.classList.add('text-start', 'css-so1s8h-custom', 'VeryPoor');
    }
    if (avatar.Quest_Performance) {
        Qraiting.textContent = `${avatar.Quest_Performance}`;
    } else {
        Qraiting.textContent = 'None';
    }
    Qraiting.style.marginLeft = '10px';
    QuestText.appendChild(Qraiting);

    if (avatar.Quest_Performance && avatar.Quest_Performance !== 'None' && !avatar.isUnavalibleAvatar) {
        const QverypoorImage = document.createElement('img');
        if (avatar.Quest_Performance === 'Excellent') {
            QverypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/b7e99cd3c42a6f1ff2e6f3faaada0e75366945997a7fa5e7e014d26b1d100ef7.svg';
        } else if (avatar.Quest_Performance === 'Good') {
            QverypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/db3f587335a6602a84d0f0f18d6fbb10904973d0ddb659009f0fc56b3d1f026b.svg';
        } else if (avatar.Quest_Performance === 'Medium') {
            QverypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/24001ed5aa8ebabaa63a09ffb88ccecccc4c5feb1b4179579e8e6c9f1fed3f16.svg';
        } else if (avatar.Quest_Performance === 'Poor') {
            QverypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/467c01a863f0a61d30a09465f743678c95a5e6ae6d439b2fecd257464ec111d0.svg';
        } else if (avatar.Quest_Performance === 'VeryPoor') {
            QverypoorImage.src = 'https://dtuitjyhwcl5y.cloudfront.net/b4bf11dfbd8c3076cb66e8457b3f78659854700e79d5256516e205e37af89247.svg';
        }
        QverypoorImage.alt = 'Avatar Icon';
        QverypoorImage.style.width = '20px';
        QverypoorImage.style.height = '20px';
        QverypoorImage.style.marginLeft = '10px';
        QverypoorImage.classList.add('css-1il99ht-custom');
        Qraiting.appendChild(QverypoorImage);
    }

    const ButtonsNew = document.createElement('div');
    ButtonsNew.classList.add('avatarCardTogglesSelection');
    if (avatar.isUnavalibleAvatar) {
        ButtonsNew.style.justifyContent = 'flex-end';
    }
    mainDiv.appendChild(ButtonsNew);

    if (!avatar.isUnavalibleAvatar) {
        const selectAvatar = document.createElement('button');
        selectAvatar.textContent = 'Select Avatar';
        selectAvatar.classList.add('px-3', 'avatarCardToggles');
        selectAvatar.addEventListener('click', async (e) => {
            e.preventDefault();
            changeSelectedAvatar(avatar.avatarID);
        });
        ButtonsNew.appendChild(selectAvatar);
    }

	const deleteAvatar = document.createElement('button');
	deleteAvatar.textContent = 'Remove';
	deleteAvatar.style.color = '#ee5454';
	deleteAvatar.style.border = '2px solid #ee5454';
	deleteAvatar.classList.add('px-3', 'avatarCardToggles', 'Remove');
	deleteAvatar.addEventListener('click', async (e) => {
    	e.preventDefault();
        const avatarCard = e.target.closest('.AvatarCard');
        if (avatarCard) {
            const avatarID = avatarCard.getAttribute('data-avatar-id');
            savedAvatars = savedAvatars.filter(a => a.avatarID !== avatarID);
            GM_setValue('savedAvatars', savedAvatars);
            avatarCard.remove();
            showNotification(`Avatar removed: ${avatar.avatarName}!`, 'info');
            updateCounter();
        } else {
            showNotification('Failed to remove avatar.', 'error');
        }
    });
    ButtonsNew.appendChild(deleteAvatar);

    updateCounter();
    sortAvatars(sortSettings.sortBy, sortSettings.isReversed);
}

async function changeSelectedAvatar(avatarID) {
    const authCookie = await getAuthCookieValue();
    await SendRequest('PUT', `https://api.vrchat.cloud/api/1/avatars/${avatarID}/select`, authCookie);
    showNotification(`Avatar selected: ${avatarID}!`, 'success');
}

async function syncAvatar(avatarID) {
    try {
        let isUnavailable = false;
        let unavailableData = null;
        let newData = null;

        const response = await SendRequest('GET', `https://api.vrchat.cloud/api/1/avatars/${avatarID}`, authCookie);
        if (response) {
            const { responseText } = response;
            if (responseText) {
                newData = JSON.parse(responseText);
            }
        } else {
            isUnavailable = true;
            let privateAvatarInfo = await GetPrivateAvatarInfo('GET', `https://vrchat.com/home/avatar/${avatarID}`, authCookie);

            unavailableData = {
                PrivateImage: privateAvatarInfo.PrivateImage,
                PrivateName: privateAvatarInfo.PrivateName,
                PrivateDescription: privateAvatarInfo.PrivateDescription,
                PrivateAuthor: privateAvatarInfo.PrivateAuthor
            };
        }

        const savedAvatar = savedAvatars.find(a => a.avatarID === avatarID);
        let changes = [];
        let hasPC = false;
        let hasQuest = false;
        let pcPerformance = '';
        let questPerformance = '';
        let releaseStatusPrivate = 'private/deleted';

        if (!isUnavailable && Array.isArray(newData.unityPackages) && newData.unityPackages.length > 0) {
            newData.unityPackages.forEach(pkg => {
                let category = '';
                if (pkg.platform === 'standalonewindows' && pkg.variant === 'security') {
                    category = 'pc';
                    hasPC = true;
                    pcPerformance = pkg.performanceRating;
                } else if (pkg.platform === 'android' && pkg.variant === 'security') {
                    category = 'quest';
                    hasQuest = true;
                    questPerformance = pkg.performanceRating;
                }
            });
        }

        if (isUnavailable) {
            if (savedAvatar.authorName !== unavailableData.PrivateAuthor) {
                changes.push({
                    field: 'authorName',
                    fieldName: 'Author Name',
                    oldVal: savedAvatar.authorName,
                    newVal: unavailableData.PrivateAuthor,
                    apply: false
                });
            }

            if (savedAvatar.description !== unavailableData.PrivateDescription) {
                if (unavailableData.PrivateDescription !== 'VRChat lets you create, publish, and explore virtual worlds with other people from around the world.') {
                    changes.push({
                        field: 'description',
                        fieldName: 'Description',
                        oldVal: savedAvatar.description,
                        newVal: unavailableData.PrivateDescription,
                        apply: false
                    });
                } else {
                    releaseStatusPrivate = 'Fully Deleted';
                }
            }

            if (savedAvatar.imageUrl !== unavailableData.PrivateImage) {
                changes.push({
                    field: 'imageUrl',
                    fieldName: 'Image',
                    oldVal: savedAvatar.imageUrl,
                    newVal: unavailableData.PrivateImage,
                    apply: false
                });
            }
    
            if(savedAvatar.avatarName !== unavailableData.PrivateName) {
                changes.push({
                    field: 'avatarName',
                    fieldName: 'Avatar Name',
                    oldVal: savedAvatar.avatarName,
                    newVal: unavailableData.PrivateName,
                    apply: false
                });
            }

            if (savedAvatar.releaseStatus !== releaseStatusPrivate) {
                changes.push({
                    field: 'releaseStatus',
                    fieldName: 'Release Status',
                    oldVal: savedAvatar.releaseStatus,
                    newVal: releaseStatusPrivate,
                    apply: false
                });
            }

            if (savedAvatar.isUnavalibleAvatar !== true) {
                changes.push({
                    field: 'isUnavalibleAvatar',
                    fieldName: 'Unavalible Avatar',
                    oldVal: savedAvatar.isUnavalibleAvatar,
                    newVal: true,
                    apply: false
                });
            }
        } else {
            if (savedAvatar.authorName !== newData.authorName) {
                changes.push({
                    field: 'authorName',
                    fieldName: 'Author Name',
                    oldVal: savedAvatar.authorName,
                    newVal: newData.authorName,
                    apply: false
                });
            }
    
            if (savedAvatar.authorId !== newData.authorId) {
                changes.push({
                    field: 'authorId',
                    fieldName: 'Author ID',
                    oldVal: savedAvatar.authorId,
                    newVal: newData.authorId,
                    apply: false
                });
            }
    
            if(savedAvatar.created_at !== FixDisplayTime(newData.created_at)) {
                changes.push({
                    field: 'created_at',
                    fieldName: 'Created At',
                    oldVal: savedAvatar.created_at,
                    newVal: FixDisplayTime(newData.created_at),
                    apply: false
                });
            }
            
            if (savedAvatar.description !== newData.description) {
                changes.push({
                    field: 'description',
                    fieldName: 'Description',
                    oldVal: savedAvatar.description,
                    newVal: newData.description,
                    apply: false
                });
            }
    
            if (savedAvatar.avatarID !== newData.id) {
                changes.push({
                    field: 'avatarID',
                    fieldName: 'Avatar ID',
                    oldVal: savedAvatar.avatarID,
                    newVal: newData.id,
                    apply: false
                });
            }
    
            if (savedAvatar.imageUrl !== newData.imageUrl) {
                changes.push({
                    field: 'imageUrl',
                    fieldName: 'Image',
                    oldVal: savedAvatar.imageUrl,
                    newVal: newData.imageUrl,
                    apply: false
                });
            }
    
            if(savedAvatar.avatarName !== newData.name) {
                changes.push({
                    field: 'avatarName',
                    fieldName: 'Avatar Name',
                    oldVal: savedAvatar.avatarName,
                    newVal: newData.name,
                    apply: false
                });
            }
    
            if (savedAvatar.releaseStatus !== newData.releaseStatus) {
                changes.push({
                    field: 'releaseStatus',
                    fieldName: 'Release Status',
                    oldVal: savedAvatar.releaseStatus,
                    newVal: newData.releaseStatus,
                    apply: false
                });
            }
    
            if(savedAvatar.updated_at !== FixDisplayTime(newData.updated_at)) {
                changes.push({
                    field: 'updated_at',
                    fieldName: 'Updated At',
                    oldVal: savedAvatar.updated_at,
                    newVal: FixDisplayTime(newData.updated_at),
                    apply: false
                });
            }
    
            if(savedAvatar.version !== newData.version) {
                changes.push({
                    field: 'version',
                    fieldName: 'Version',
                    oldVal: savedAvatar.version,
                    newVal: newData.version,
                    apply: false
                });
            }
    
            if(savedAvatar.isPlatformPC !== hasPC) {
                changes.push({
                    field: 'isPlatformPC',
                    fieldName: 'PC Avatar',
                    oldVal: savedAvatar.isPlatformPC,
                    newVal: hasPC,
                    apply: false
                });
            }
    
            if(savedAvatar.PC_Performance !== pcPerformance) {
                changes.push({
                    field: 'PC_Performance',
                    fieldName: 'PC Performance',
                    oldVal: savedAvatar.PC_Performance,
                    newVal: pcPerformance,
                    apply: false
                });
            }
    
            if(savedAvatar.isPlatformQuest !== hasQuest) {
                changes.push({
                    field: 'isPlatformQuest',
                    fieldName: 'Quest Avatar',
                    oldVal: savedAvatar.isPlatformQuest,
                    newVal: hasQuest,
                    apply: false
                });
            }
    
            if (savedAvatar.Quest_Performance !== questPerformance) {
                changes.push({
                    field: 'Quest_Performance',
                    fieldName: 'Quest Performance',
                    oldVal: savedAvatar.Quest_Performance,
                    newVal: questPerformance,
                    apply: false
                });
            }
            if (savedAvatar.isUnavalibleAvatar !== false) {
                changes.push({
                    field: 'isUnavalibleAvatar',
                    fieldName: 'Unavalible Avatar',
                    oldVal: savedAvatar.isUnavalibleAvatar,
                    newVal: false,
                    apply: false
                });
            }
        }

        if (changes.length === 0) {
            showNotification('No changes detected!', 'success');
            return;
        }

        showSyncConfirmation(changes, avatarID);
    } catch (error) {
        showNotification(`Error during sync: ${error.message}`, 'error');
    }
}

function showSyncConfirmation(changes, avatarID) {
    const modal = document.createElement('div');
    modal.className = 'sync-modal-overlay';
    modal.innerHTML = `
        <div class="modal-sync" style="width: 600px">
            <h3 class="modal-sync-title">Changes detected</h3>
            <div id="status-warning" class="sync-warning" style="display: none;">
                <strong>⚠️ Avatar is now PRIVATE/DELETED! Some data may become unavailable.</strong>
            </div>
            <div id="sync-changes-list""></div>
            <div class="modal-sync-buttons">
                <div>
                    <button id="sync-select-all" class="vrc-button">Select All</button>
                </div>
                <div style="display: flex; gap: 1rem;">
                    <button id="sync-apply-changes" class="vrc-button">Apply Selected</button>
                    <button id="sync-cancel-changes" class="vrc-button">Cancel</button>
                </div>
            </div>
        </div>
    `;

    const statusChange = changes.find(change => 
        change.field === 'releaseStatus' && 
        (change.newVal === 'private/deleted' || change.newVal === 'Fully Deleted')
    );

    if (statusChange) {
        const warning = modal.querySelector('#status-warning');
        warning.style.display = 'block';
        warning.style.backgroundColor = 'rgb(81, 22, 22)';
        warning.style.padding = '10px';
        warning.style.borderRadius = '5px';
        warning.style.marginBottom = '15px';
    }
    
    const changesList = modal.querySelector('#sync-changes-list');
    changes.forEach(change => {
        const div = document.createElement('div');
        div.className = 'sync-change-item';
        if (change.field === 'imageUrl') {
            div.innerHTML = `
                <label class="sync-change-label">
                    <div class="sync-change-checkbox-container" style="margin-bottom: 0.5rem;">
                        <input type="checkbox" class="sync-change-checkbox">
                        <span class="sync-field-name">${change.fieldName}</span>
                    </div>
                    <div class="sync-change-checkbox-container" style="justify-content: space-around; align-items: center;">
                        <div class="sync-field-value">
                            ${change.oldVal ? `<img src="${change.oldVal}" alt="Old Image" class="sync-image"">` : 'No image'}
                        </div>
                        <p class="sync-change-arrow">➤</p>
                        <div class="sync-field-value">
                            ${change.newVal ? `<img src="${change.newVal}" alt="New Image" class="sync-image">` : 'No image'}
                        </div>
                    </div>
                </label>
            `;
        } else {
            div.innerHTML = `
                <label class="sync-change-label">
                    <div class="sync-change-checkbox-container" style="margin-bottom: 0.5rem;">
                        <input type="checkbox" class="sync-change-checkbox">
                        <span class="sync-field-name">${change.fieldName}</span>
                    </div>
                    <div class="sync-change-checkbox-container" style="justify-content: space-around;">
                        <div class="sync-field-value">${change.oldVal}</div>
                        <p class="sync-change-arrow">➤</p>
                        <div class="sync-field-value">${change.newVal}</div>
                    </div>
                </label>
            `;
        }
        changesList.appendChild(div);
    });

    const checkboxes = modal.querySelectorAll('.sync-change-checkbox');
    if (!statusChange) {
        checkboxes.forEach(checkbox => checkbox.checked = true);
    }

    const selectAllBtn = modal.querySelector('#sync-select-all');
    let allChecked = !statusChange;

    const updateSelectAllBtnText = () => {
        selectAllBtn.textContent = allChecked ? 'Uncheck All' : 'Check All';
    };

    selectAllBtn.addEventListener('click', () => {
        allChecked = !allChecked;
        checkboxes.forEach(checkbox => checkbox.checked = allChecked);
        updateSelectAllBtnText();
        updateApplyButtonText();
    });
    updateSelectAllBtnText();

    const applyBtn = modal.querySelector('#sync-apply-changes');

    const updateApplyButtonText = () => {
        const checkedCount = modal.querySelectorAll('.sync-change-checkbox:checked').length;
        if (checkedCount === 0) {
            applyBtn.style.display = 'none';
        } else {
            applyBtn.style.display = 'block';
        }
    };

    checkboxes.forEach(checkbox => {
        checkbox.addEventListener('change', updateApplyButtonText);
    });
    updateApplyButtonText();

    document.body.appendChild(modal);

    modal.querySelector('#sync-apply-changes').addEventListener('click', () => {
        const selected = Array.from(modal.querySelectorAll('.sync-change-checkbox:checked'))
            .map(cb => changes[Array.from(changesList.children).indexOf(cb.closest('.sync-change-item'))]);
        
        if (selected.length === 0) {
            showNotification('Select somethig first!', 'warning');
            return;
        }

        const avatarIndex = savedAvatars.findIndex(a => a.avatarID === avatarID);
        selected.forEach(change => {
            savedAvatars[avatarIndex][change.field] = change.newVal;
        });

        GM_setValue('savedAvatars', savedAvatars);
        updateAvatarCard(avatarID);
        handleClose();
    });

    const handleClose = () => {
        modal.classList.add('exit');
        setTimeout(() => {
            modal.remove();
        }, 200);
    };

    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape') {
            handleClose();
        }
    });

    modal.querySelector('#sync-cancel-changes').addEventListener('click', () => {
        handleClose();
    });
}

function updateAvatarCard(avatarID) {
    const avatarCard = document.querySelector(`[data-avatar-id="${avatarID}"]`);
    if (avatarCard) {
        avatarCard.remove();
        const avatarData = savedAvatars.find(a => a.avatarID === avatarID);
        createAvatarCard(avatarData);
        showNotification('Avatar Updated!', 'success');
    }
}

function showSaveByIDModal() {
    const modal = document.createElement('div');
    modal.id = 'save-by-id-modal';
    modal.classList.add('modal-overlay');
    modal.innerHTML = `
        <div class="modal-content">
            <h3>Save New Avatar</h3>
            <label>Avatar URL or ID:</label>
            <input type="text" id="avatar-url" placeholder="Enter URL or ID" />
            <label>For example: avtr_26187637-0c30-4a09-86e1-bc928c07309e</label>
            <div class="modal-buttons">
                <button id="save-avatar-btn" disabled>Save</button>
                <button id="cancel-btn">Cancel</button>
            </div>
        </div>
    `;

    GM_addStyle(`
        @keyframes modalEnter {
            0% { opacity: 0; transform: scale(1.15); }
            100% { opacity: 1; transform: scale(1); }
        }
        @keyframes modalExit {
            0% { opacity: 1; transform: scale(1); }
            100% { opacity: 0; transform: scale(1.15); }
        }
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.7);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 2000;
            backdrop-filter: blur(4px);
            animation: modalEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .modal-overlay.exit {
            animation: modalExit 0.2s ease-out forwards;
        }
        .modal-content {
            background: rgb(24, 27, 31);
            color: #e0e0e0;
            padding: 1.5rem;
            border-radius: 8px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            width: 512px;
            border: 2px solid rgb(37, 42, 48);
            position: relative;
        }
        .modal-content h3 {
            margin: 0 0 1.25rem 0;
            font-size: 1.5rem;
            color: rgb(106, 227, 249);
            text-align: center;
            font-weight: 500;
        }
        .modal-content label {
            display: block;
            margin-bottom: 0.5rem;
            font-size: 0.9rem;
            color: rgb(160, 160, 160);
        }
        .modal-content input {
            width: 100%;
            padding: 0.75rem 1rem;
            margin: 0.5rem 0 1.25rem 0;
            border: 2px solid rgb(6, 75, 92);
            border-radius: 4px;
            background: rgb(7, 36, 43);
            color: #e0e0e0;
            font-size: 1rem;
            transition: all 0.2s ease-in-out;
        }
        .modal-content input:focus {
            outline: none;
            border-color: rgb(8, 108, 132);
            box-shadow: 0 0 0 3px rgba(106, 227, 249, 0.1);
        }
        .modal-content input:hover {
            border-color: rgb(8, 108, 132);
        }
        .modal-buttons {
            display: flex;
            gap: 0.75rem;
            margin-top: 1rem;
        }
        #save-avatar-btn, #cancel-btn {
            flex: 1;
            padding: 0.75rem 1.25rem;
            border: 2px solid transparent;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s ease-in-out;
            background-color: rgb(37, 42, 48);
            color: rgb(106, 227, 249);
        }
        #save-avatar-btn {
            background-color: rgb(6, 75, 92);
            border-color: rgb(8, 108, 132);
        }
        #save-avatar-btn:hover {
            background-color: rgb(8, 108, 132);
            border-color: rgb(255, 255, 255);
            transform: scale(1.05);
        }
        #cancel-btn {
            color: rgb(238, 84, 84);
            background-color: rgb(7, 36, 43);
            border: 3px solid rgb(5, 60, 72);
        }
        #cancel-btn:hover {
            background-color: rgb(5, 25, 29);
            border-color: rgb(5, 25, 29)
            transform: scale(1.05);
        }
        .modal-buttons button {
            --bs-btn-border-radius: 4px;
            --bs-btn-padding-y: 0.75rem;
            --bs-btn-padding-x: 1.25rem;
            --bs-btn-font-size: 1rem;
            border-width: 2px;
        }
        .modal-content input:focus {
            background: linear-gradient(#242424, #242424) padding-box,
                        linear-gradient(135deg, rgba(106, 227, 249, 0.4), rgba(8, 108, 132, 0.4)) border-box;
            border: 2px solid transparent;
        }
        .modal-content input.valid {
            border-color: #4CAF50 !important;
        }
        .modal-content input.invalid {
            border-color: #FF5722 !important;
        }
        .modal-content input:hover {
            border-color: inherit;
        }
        #save-avatar-btn:disabled {
            background-color: rgb(37, 42, 48);
            border-color: transparent;
            cursor: not-allowed;
            transform: none;
        }
        #save-avatar-btn:disabled:hover {
            background-color: rgb(37, 42, 48);
            border-color: transparent;
            box-shadow: none;
        }
    `);

    document.body.appendChild(modal);

    const saveBtn = modal.querySelector('#save-avatar-btn');
    const cancelBtn = modal.querySelector('#cancel-btn');
    const inputField = modal.querySelector('#avatar-url');

    const urlPattern = /^https:\/\/vrchat\.com\/home\/avatar\/avtr_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    const idPattern = /^avtr_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

    const validateInput = () => {
        const value = inputField.value.trim();
        const isValidURL = urlPattern.test(value);
        const isValidID = idPattern.test(value);
        const isValid = isValidURL || isValidID;

        inputField.classList.toggle('valid', isValid);
        inputField.classList.toggle('invalid', !isValid);
        saveBtn.disabled = !isValid;
    };

    inputField.addEventListener('input', validateInput);

    const handleClose = () => {
        modal.classList.add('exit');
        setTimeout(() => {
            modal.remove();
        }, 200);
    };

    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape') {
            handleClose();
        }
    });

    saveBtn.addEventListener('click', async () => {
        const value = inputField.value.trim();
        let avatarID;

        if (urlPattern.test(value)) {
        avatarID = value.split('/').pop();
        } else if (idPattern.test(value)) {
            avatarID = value;
        } else {
            showNotification('Invalid URL or ID format!', 'error');
            return;
        }

        try {
            AddAvatar(addMetod.ID, avatarID);
            handleClose();
        } catch (error) {
            showNotification(`Failed to add avatar: ${error.message}`, 'error');
        }

        handleClose();
    });
    inputField.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') {
            saveBtn.click();
        }
    });

    cancelBtn.addEventListener('click', () => {
        handleClose();
    });

    modal.addEventListener('click', (event) => {
        const modalContent = modal.querySelector('.modal-content');
        const modalRect = modalContent.getBoundingClientRect();

        const safeZone = 150;

        const isClickOutsideSafeZone = 
            event.clientX < modalRect.left - safeZone ||
            event.clientX > modalRect.right + safeZone ||
            event.clientY < modalRect.top - safeZone ||
            event.clientY > modalRect.bottom + safeZone;

        if (isClickOutsideSafeZone) {
            handleClose();
        }
    });
}

let allAvatars = [];
let totalPages = 0;
let currentPage = 1;
const avatarsPerPage = 20;

function showAvatarSearchModal(initialQuery = '') {
    const modal = document.createElement('div');
    modal.id = 'avatar-search-modal';
    modal.classList.add('modal-overlay');
    modal.innerHTML = `
        <div class="modal-content" id="avatar-search-modal-content">
            <h3>Search Avatars</h3>
            <div style="display: flex; flex-direction: column;">
                <div style="display: flex;">
                    <input 
                        type="text" 
                        id="search-input" 
                        placeholder="Enter search query"
                        value="${initialQuery}"
                        style="width: 100%; height: 2.5rem; padding: 0.75rem; margin-bottom: 0.5rem; margin-right: 10px; border: 2px solid rgb(6, 75, 92); border-radius: 4px; background: rgb(7, 36, 43); color: #e0e0e0;"
                    >
                    <button id="search-btn" class="css-1vrq36y-custom" style="height: 40px; padding: 0rem 1.5rem; border-radius: 4px; background: rgb(6, 75, 92);">Search</button>
                </div>
                
                <div style="display: flex; justify-content: flex-end; align-items: baseline; gap: 0.5rem;">
                    <span>Database:</span>
                    <select id="search-selection" class="database" style="margin-bottom: 1rem; border: 2px solid rgb(6, 75, 92); border-radius: 4px; background: rgb(7, 36, 43); color: #e0e0e0;">
                        <option value="vrcxv2">VRCX v2</option>
                        <option value="vrcx">VRCX</option>
                    </select>
                </div>
                
            </div>
            <div id="pagination-container"></div>
            <div id="search-results" style="max-height: 400px; overflow-y: auto;">The results will appear here</div>
            <button id="close-modal-btn">Close</button>
        </div>
    `;
    document.body.appendChild(modal);

    const searchInput = modal.querySelector('#search-input');
    const searchBtn = modal.querySelector('#search-btn');
    const resultsContainer = modal.querySelector('#search-results');
    const closeModalBtn = modal.querySelector('#close-modal-btn');
    const databaseSelector = modal.querySelector('.database');

    searchInput.addEventListener('keydown', (event) => {
        if (event.key === 'Enter') searchBtn.click();
    });

    const handleClose = () => {
        modal.classList.add('exit');
        setTimeout(() => modal.remove(), 200);
    };

    closeModalBtn.addEventListener('click', handleClose);
    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape') {
            handleClose();
        }
    });

    modal.addEventListener('click', (event) => {
        const modalContent = modal.querySelector('.modal-content');
        const modalRect = modalContent.getBoundingClientRect();

        const safeZone = 150;

        const isClickOutsideSafeZone = 
            event.clientX < modalRect.left - safeZone ||
            event.clientX > modalRect.right + safeZone ||
            event.clientY < modalRect.top - safeZone ||
            event.clientY > modalRect.bottom + safeZone;

        if (isClickOutsideSafeZone && !event.target.closest('.modal-content')) {
            handleClose();
        }
    });

    databaseSelector.addEventListener('change', () => {
        switch (databaseSelector.value) {
            case 'vrcxv2':
                databaseLink = `https://api.avtrdb.com/v2/avatar/search/vrcx?search=`;
                break;
            case 'vrcx':
                databaseLink = `https://vrcx.vrcdb.com/avatars/Avatar/VRCX?search=`;
                break;
            default:
                showNotification('Invalid database selection', 'error');
                return;
        }
    });

    searchBtn.addEventListener('click', async () => {
        const query = searchInput.value.trim();
        // const database = databaseSelector.value;
        // const count = countSelector.value;
        if (!query) {
            showNotification('Please enter a search query', 'warning');
            return;
        }

        allAvatars = [];

        performAvatarSearch(query, resultsContainer, 1, databaseLink);
        morePages = 0;
    });

    if (initialQuery) {
        performAvatarSearch(initialQuery, resultsContainer, 1);
    }
}

async function performAvatarSearch(query, container, page = 1, database, pageNumber = 0, isNeedUpdate = true) {
    const isGoodDatabase = database === 'https://api.avtrdb.com/v2/avatar/search/vrcx?search=';
    if (container) {
        container.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
    }

    if (!query.trim()) {
        showNotification('Please enter a search query', 'warning');
        return;
    }

    container.innerHTML = '';

    if (isNeedUpdate) {
        try {
            const { response } = await SendRequest('GET', `${database}${query}${!isGoodDatabase ? '' : `&n=50&page=${pageNumber}`}`);
            const data = JSON.parse(response);
    
            if (data.length === 0) {
                container.innerHTML = '<p style="margin: inherit;">No results found</p>';
            }
    
            allAvatars = data;
            totalPages = Math.ceil(allAvatars.length / avatarsPerPage);
        } catch (error) {
            showNotification(`Error while searching: ${error.message}`, 'error');
            return;
        }
    }

    const startIdx = (page - 1) * avatarsPerPage;
    const endIdx = startIdx + avatarsPerPage;
    const currentAvatars = allAvatars.slice(startIdx, endIdx);

    currentAvatars.forEach(avatar => {
        const avatarCard = document.createElement('div');
        avatarCard.classList.add('search-result-card');
        avatarCard.style.display = 'flex';
        avatarCard.style.alignItems = 'center';
        avatarCard.style.marginBottom = '10px';
        avatarCard.style.width = '95%';

        const img = document.createElement('img');
        img.src = avatar.imageUrl || 'https://assets.vrchat.com/default/unavailable-avatar.png';
        avatarCard.appendChild(img);

        const infoDiv = document.createElement('div');
        infoDiv.style.flexGrow = 1;
        infoDiv.style.width = 'min-content';

        const name = document.createElement('h4');
        name.textContent = avatar.avatarName || avatar.name;
        name.title = `Description:\n${avatar.description || 'No description available.'}`;
        name.style.margin = '0';
        infoDiv.appendChild(name);

        const author = document.createElement('p');
        author.textContent = `Created by: ${avatar.authorName}`;
        author.style.margin = '0';
        author.style.fontSize = '0.9em';
        author.style.color = '#aaa';
        infoDiv.appendChild(author);

        avatarCard.appendChild(infoDiv);

        const addButton = document.createElement('button');
        addButton.textContent = 'Add to Favorites';
        addButton.classList.add('css-1vrq36y-custom');
        addButton.addEventListener('click', () => {
            AddAvatar(addMetod.ID, avatar.id);
            showNotification(`Added ${avatar.avatarName} to favorites`, 'success');
        });

        avatarCard.appendChild(addButton);
        container.appendChild(avatarCard);
    });

    addPaginationButtons(container, totalPages, page, database);
}

function addPaginationButtons(container, totalPages, currentPage, database) {
    const isGoodDatabase = database === 'https://api.avtrdb.com/v2/avatar/search/vrcx?search=';
    const paginationContainer = document.getElementById('pagination-container');
    paginationContainer.innerHTML = '';

    const createButton = (text, disabled, onClick) => {
        const btn = document.createElement('button');
        btn.textContent = text;
        btn.classList.add('css-1vrq36y-custom');
        btn.style.margin = '0 5px';
        btn.disabled = disabled;
        btn.addEventListener('click', onClick);
        return btn;
    };

    if (isGoodDatabase) {
        paginationContainer.insertBefore(createButton('Previous', morePages === 0, () => {
            morePages--;
            performAvatarSearch(document.getElementById('search-input').value.trim(), container, 1, database, morePages);
        }), paginationContainer.firstChild);

        const pageInfo = document.createElement('span');
        pageInfo.textContent = `Current Page: ${morePages + 1}`;
        pageInfo.style.margin = '0 10px';
        paginationContainer.appendChild(pageInfo);

        paginationContainer.appendChild(createButton('Next', false, () => {
            morePages++;
            performAvatarSearch(document.getElementById('search-input').value.trim(), container, 1, database, morePages);
        }));
    } else {
        paginationContainer.appendChild(createButton('Previous', currentPage === 1, () => {
        performAvatarSearch(document.getElementById('search-input').value.trim(), container, currentPage - 1, database, undefined, false);
        }));

        const pageInfo = document.createElement('span');
        pageInfo.textContent = `Page ${currentPage} of ${totalPages}`;
        pageInfo.style.margin = '0 10px';
        paginationContainer.appendChild(pageInfo);

        paginationContainer.appendChild(createButton('Next', currentPage >= totalPages, () => {
            performAvatarSearch(document.getElementById('search-input').value.trim(), container, currentPage + 1, database, undefined, false);
        }));
    }
}


async function settingsTab() {
    GM_addStyle(`
        .settings-container {
            display: flex;
            -webkit-box-pack: center;
            justify-content: center;
            -webkit-box-align: center;
            align-items: center;
        }
        .settings-container .dropdown {
            position: relative;
        }
        .nav-btn {
            min-width: 45px;
            min-height: 45px;
            display: flex;
            -webkit-box-pack: center;
            justify-content: center;
            -webkit-box-align: center;
            align-items: center;
            font-weight: bold;
            white-space: nowrap;
            background: rgb(7, 36, 43) !important;
            border-radius: 100px !important;
            border: 3px solid rgb(5, 60, 72) !important;
        }
        .nav-btn:hover {
            background: rgb(5, 25, 29) !important;
            box-shadow: rgb(5, 25, 29) 0px 0px 0px 0.2rem !important;
        }
        .settings-btn {
            --bs-btn-padding-x: 0.75rem;
            --bs-btn-padding-y: 0.5rem;
            --bs-btn-font-family: ;
            --bs-btn-font-size: 1rem;
            --bs-btn-font-weight: normal;
            --bs-btn-line-height: 1.25;
            --bs-btn-color: var(--bs-body-color);
            --bs-btn-bg: transparent;
            --bs-btn-border-width: 1px;
            --bs-btn-border-color: transparent;
            --bs-btn-border-radius: 0.25rem;
            --bs-btn-hover-border-color: transparent;
            --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(10, 10, 13, 0.075);
            --bs-btn-disabled-opacity: 0.65;
            --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);
            display: inline-block;
            padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
            font-family: var(--bs-btn-font-family);
            font-size: var(--bs-btn-font-size);
            font-weight: var(--bs-btn-font-weight);
            line-height: var(--bs-btn-line-height);
            color: var(--bs-btn-color);
            text-align: center;
            vertical-align: middle;
            cursor: pointer;
            user-select: none;
            border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
            border-radius: var(--bs-btn-border-radius);
            background-color: var(--bs-btn-bg);
            background-image: var(--bs-gradient);
            transition: all .15s ease-in-out;
        }
        .settings-btn:hover {
            color: var(--bs-btn-hover-color);
            text-decoration: none;
            background-color: var(--bs-btn-hover-bg);
            border-color: var(--bs-btn-hover-border-color);
        }
        .settings-btn-secondary {
            --bs-gradient: #798897 linear-gradient(180deg, #697683, #798897) repeat-x;
        }
        .padding-1 {
            padding: 0.25rem !important;
        }
        .text-white-custom {
            --bs-text-opacity: 1;
            color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;
        }
        .settings-svg {
            text-align: center;
            opacity: 1;
            transition: opacity 0.2s ease-in-out;
            font-size: 1.25em;
            line-height: 0.05em;
            display: var(--fa-display, inline-block);
            height: 1em;
            vertical-align: -0.125em;
            overflow: visible;
            box-sizing: content-box;
        }

        .dropdown-arrow2::before {
            content: "";
            position: absolute;
            background: transparent;
            left: 50%;
            transform: translateX(-50%);
            border-width: 15px;
            border-style: solid;
            border-color: transparent transparent rgb(5, 60, 72);
            bottom: -20px;
            z-index: 1;
            pointer-events: none;
        }

        .dropdown-arrow2::after {
            content: "";
            position: absolute;
            background: transparent;
            left: 50%;
            transform: translateX(-50%);
            border-width: 22px;
            border-style: solid;
            border-color: transparent transparent rgb(5, 25, 29);
            bottom: -30px;
            z-index: 2;
            pointer-events: none;
        }
        .dropdown-window { 
            position: relative;
            box-shadow: rgba(0, 0, 0, 0.8) 0px 9px 26px 5px;
            padding: 0px !important;
            // transform: translate(calc(-100% + 100:), 20px) !important;
            transform: translate(-45%, 10%);
            background: rgb(5, 25, 29) !important;
            border: 2px solid rgb(5, 60, 72) !important;
            border-radius: 10px !important;
            width: 400px !important;
        }
    `);
    const settingsContainer = await awaitForElement('div.navbar-section.left-nav');
    settingsContainer.querySelector('.p-1.btn.navbar-btn.medium').remove();
    settingsContainer.insertAdjacentHTML('beforeend', `
        <div class="settings-container">
            <div class="dropdown">
                <button type="button" class="padding-1 nav-btn text-white-custom settings-btn settings-btn-secondary settings-btn-event">
                    <svg class="settings-svg" role="presentation" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
                        <path fill="currentColor" d="M308.5 135.3c7.1-6.3 9.9-16.2 6.2-25c-2.3-5.3-4.8-10.5-7.6-15.5L304 89.4c-3-5-6.3-9.9-9.8-14.6c-5.7-7.6-15.7-10.1-24.7-7.1l-28.2 9.3c-10.7-8.8-23-16-36.2-20.9L199 27.1c-1.9-9.3-9.1-16.7-18.5-17.8C173.9 8.4 167.2 8 160.4 8l-.7 0c-6.8 0-13.5 .4-20.1 1.2c-9.4 1.1-16.6 8.6-18.5 17.8L115 56.1c-13.3 5-25.5 12.1-36.2 20.9L50.5 67.8c-9-3-19-.5-24.7 7.1c-3.5 4.7-6.8 9.6-9.9 14.6l-3 5.3c-2.8 5-5.3 10.2-7.6 15.6c-3.7 8.7-.9 18.6 6.2 25l22.2 19.8C32.6 161.9 32 168.9 32 176s.6 14.1 1.7 20.9L11.5 216.7c-7.1 6.3-9.9 16.2-6.2 25c2.3 5.3 4.8 10.5 7.6 15.6l3 5.2c3 5.1 6.3 9.9 9.9 14.6c5.7 7.6 15.7 10.1 24.7 7.1l28.2-9.3c10.7 8.8 23 16 36.2 20.9l6.1 29.1c1.9 9.3 9.1 16.7 18.5 17.8c6.7 .8 13.5 1.2 20.4 1.2s13.7-.4 20.4-1.2c9.4-1.1 16.6-8.6 18.5-17.8l6.1-29.1c13.3-5 25.5-12.1 36.2-20.9l28.2 9.3c9 3 19 .5 24.7-7.1c3.5-4.7 6.8-9.5 9.8-14.6l3.1-5.4c2.8-5 5.3-10.2 7.6-15.5c3.7-8.7 .9-18.6-6.2-25l-22.2-19.8c1.1-6.8 1.7-13.8 1.7-20.9s-.6-14.1-1.7-20.9l22.2-19.8zM112 176a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM504.7 500.5c6.3 7.1 16.2 9.9 25 6.2c5.3-2.3 10.5-4.8 15.5-7.6l5.4-3.1c5-3 9.9-6.3 14.6-9.8c7.6-5.7 10.1-15.7 7.1-24.7l-9.3-28.2c8.8-10.7 16-23 20.9-36.2l29.1-6.1c9.3-1.9 16.7-9.1 17.8-18.5c.8-6.7 1.2-13.5 1.2-20.4s-.4-13.7-1.2-20.4c-1.1-9.4-8.6-16.6-17.8-18.5L583.9 307c-5-13.3-12.1-25.5-20.9-36.2l9.3-28.2c3-9 .5-19-7.1-24.7c-4.7-3.5-9.6-6.8-14.6-9.9l-5.3-3c-5-2.8-10.2-5.3-15.6-7.6c-8.7-3.7-18.6-.9-25 6.2l-19.8 22.2c-6.8-1.1-13.8-1.7-20.9-1.7s-14.1 .6-20.9 1.7l-19.8-22.2c-6.3-7.1-16.2-9.9-25-6.2c-5.3 2.3-10.5 4.8-15.6 7.6l-5.2 3c-5.1 3-9.9 6.3-14.6 9.9c-7.6 5.7-10.1 15.7-7.1 24.7l9.3 28.2c-8.8 10.7-16 23-20.9 36.2L315.1 313c-9.3 1.9-16.7 9.1-17.8 18.5c-.8 6.7-1.2 13.5-1.2 20.4s.4 13.7 1.2 20.4c1.1 9.4 8.6 16.6 17.8 18.5l29.1 6.1c5 13.3 12.1 25.5 20.9 36.2l-9.3 28.2c-3 9-.5 19 7.1 24.7c4.7 3.5 9.5 6.8 14.6 9.8l5.4 3.1c5 2.8 10.2 5.3 15.5 7.6c8.7 3.7 18.6 .9 25-6.2l19.8-22.2c6.8 1.1 13.8 1.7 20.9 1.7s14.1-.6 20.9-1.7l19.8 22.2zM464 304a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"></path>
                    </svg>
                    <div class="dropdown-arrow2" style="display: none;"></div>
                </button>
                <div style="position: absolute;">
                    <div class="dropdown-window my-menu" style="display: none;">
                        <div style="height: 20px; width: 100%; padding-right: 10px; text-align: right; border-bottom: 2px solid rgb(5, 60, 72);"></div>
                        <div style="padding: 20px 15px; background: rgb(4, 18, 21); display: flex; flex-direction: column; gap: 10px;">
                            <button class="vrc-button profile-settings-event">Profile Settings</button>
                            <button class="vrc-button mod-settings-event">Mod Settings</button>
                        </div>
                        <div style="height: 20px; width: 100%; border-top: 2px solid rgb(5, 60, 72);"></div>
                    </div>
                </div>
            </div>
        </div>
    `);

    const mybutton = settingsContainer.querySelector('.settings-btn-event');
    const dropdown = settingsContainer.querySelector('.my-menu');
    const dropdownArrow = settingsContainer.querySelector('.dropdown-arrow2');
    const modSettings = settingsContainer.querySelector('.mod-settings-event');
    const profileSettings = settingsContainer.querySelector('.profile-settings-event');

    profileSettings.addEventListener('click', (event) => {
        event.preventDefault();
        window.location.href = '/home/profile';
    });

    mybutton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
        dropdownArrow.style.display = dropdownArrow.style.display === 'block' ? 'none' : 'block';

    });

    modSettings.addEventListener('click', (event) => {
        event.preventDefault();
        openModSettingsModal();
        dropdown.style.display = 'none';
        dropdownArrow.style.display = 'none';
    });

    document.addEventListener('click', (event) => {
        const isClickInside = mybutton.contains(event.target) || dropdown.contains(event.target);
        if (!isClickInside) {
            dropdown.style.display = 'none';
            dropdownArrow.style.display = 'none';
        }
    });
}

function openModSettingsModal() { 
    const modal = document.createElement('div');
    modal.classList.add('modal-overlay');
    modal.insertAdjacentHTML('beforeend', `
        <div class="modal-content">
            <h3>Mod Settings</h3>
            <p>Coming soon...</p>
            <div class="modal-buttons">
                <button id="close-modal" class="vrc-button">Close</button>
            </div>
        </div>
    `);
    document.body.appendChild(modal);

    const handleClose = () => {
        modal.classList.add('exit');
        setTimeout(() => modal.remove(), 200);
    };

    const closeButton = modal.querySelector('#close-modal');
    closeButton.addEventListener('click', () => {
        handleClose();
    });
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址