Instagram - Better Layout

Instagram Index Page Better Layout. IG 首页的布局更好看

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Instagram - Better Layout
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Instagram Index Page Better Layout. IG 首页的布局更好看
// @author       Martin______X, Gemini AI
// @match        https://www.instagram.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=instagram.com
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let $CREATED = false;
    let $WIDGET_INSTANCE = null;

    // --- Main Loop ---
    setInterval(process_interval, 1);

    function process_interval() {
        try {
            // --- 1. Strict URL Validation ---
            if (!is_homepage()) {
                hide_widget(true); // true = force close
                return;
            }

            const main_element = document.querySelector("main");
            if (!main_element) return;

            // --- 2. Homepage Layout Adjustment (80vw) ---
            adjust_central_layout(main_element);

            // --- 3. Enhanced Chat Window Visibility Detection ---
            const is_chat_visible = check_chat_visibility();

            // --- 4. Dynamic main_right Detection ---
            const main_right = get_main_right(main_element);

            // Core visibility logic: If no right sidebar, or chat window is visible, hide the button
            if (should_hide_widget(main_right, is_chat_visible)) {
                hide_widget(false); // false = just hide, don't force close state
                return;
            }

            // --- 5. Widget Creation and Refresh ---
            update_or_create_widget(main_element, main_right);

            // Story Tray Scaling Logic
            adjust_story_tray(main_element);

        } catch (e) {}
    }

    // --- Helper Functions ---

    function is_homepage() {
        return window.location.pathname === '/' || window.location.pathname === '';
    }

    function hide_widget(force_close) {
        if ($WIDGET_INSTANCE) {
            $WIDGET_INSTANCE.element.style.setProperty('display', 'none', 'important');
            if (force_close) {
                $WIDGET_INSTANCE.close();
            }
        }
    }

    function adjust_central_layout(main_element) {
        const central_container = get_central_container(main_element);
        if (central_container && central_container.style.maxWidth !== '80vw') {
            central_container.style.setProperty('max-width', '80vw', 'important');
            central_container.style.setProperty('width', '100%', 'important');
        }
    }

    function get_central_container(main_element) {
        if (!main_element || !main_element.firstElementChild) return null;
        return main_element.firstElementChild.firstElementChild;
    }

    function check_chat_visibility() {
        const chat_list = document.querySelector('[data-pagelet="IGDChatTabThreadList"]');
        if (chat_list) {
            const style = window.getComputedStyle(chat_list);
            // Simultaneously detect multiple hiding methods: offsetParent, CSS display, CSS visibility
            return (chat_list.offsetParent !== null) &&
                   (style.display !== 'none') &&
                   (style.visibility !== 'hidden');
        }
        return false;
    }

    function get_main_right(main_element) {
        if (!main_element || !main_element.firstElementChild) return null;
        return main_element.firstElementChild.children[1];
    }

    function should_hide_widget(main_right, is_chat_visible) {
        return !main_right || is_chat_visible;
    }

    function update_or_create_widget(main_element, main_right) {
        const current_bg_color = window.getComputedStyle(main_element).backgroundColor;

        if (!$CREATED) {
            main_right.style.margin = "0px";
            main_right.style.padding = "0px";
            $WIDGET_INSTANCE = create_floating_widget({
                content: main_right,
                btn_color: '#007AFF',
                max_height: '90vh',
                bg_color: current_bg_color
            });
            $CREATED = true;
        } else {
            // Meets display conditions, show and refresh content
            $WIDGET_INSTANCE.element.style.display = 'flex';
            if (main_right.style.display !== 'none') {
                main_right.style.marginLeft = "0px";
                main_right.style.paddingLeft = "0px";
                $WIDGET_INSTANCE.refresh_content(main_right);
            }
            $WIDGET_INSTANCE.update_bg(current_bg_color);
        }
    }

    function adjust_story_tray(main_element) {
        const central_container = get_central_container(main_element);
        if (!central_container) return;

        var target = central_container.querySelector('[data-pagelet="story_tray"] div > div > div > [role="presentation"]');
        if (target) {
            let sibling = target.nextElementSibling;
            while (sibling) {
                const btn = sibling.tagName === 'BUTTON' ? sibling : sibling.querySelector('button');
                if (btn) btn.style.setProperty('transform', 'scale(1.5) translateY(-15px)', 'important');
                sibling = sibling.nextElementSibling;
            }
            target.style.cssText = "max-width: 80% !important; margin-left: 10% !important; margin-right: 10% !important; width: 100% !important;";
        }
    }

    // --- create_floating_widget Internal Logic Splitting ---

    function get_dynamic_shadow(rgb_str) {
        const rgb = rgb_str.match(/\d+/g);
        if (!rgb || rgb.length < 3) return '0 10px 40px rgba(0,0,0,0.5)';
        const brightness = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
        return brightness < 128 ? '0 0 25px 5px rgba(255, 255, 255, 0.12), 0 20px 50px rgba(0, 0, 0, 0.9)' : '0 15px 40px 5px rgba(0, 0, 0, 0.25), 0 5px 15px rgba(0, 0, 0, 0.1)';
    }

    function ensure_widget_styles(btn_color, max_height) {
        if (!document.getElementById('fw-style-tag')) {
            const style_tag = document.createElement('style');
            style_tag.id = 'fw-style-tag';
            style_tag.innerHTML = `
                .fw-wrapper {
                    position: fixed; right: 0; top: 50%;
                    transform: translateY(-50%);
                    display: flex; flex-direction: row-reverse;
                    align-items: center; z-index: 2147483647 !important;
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
                    pointer-events: none; padding: 0 10px 0 40px;
                }
                .fw-panel {
                    width: 350px; height: auto; max-height: ${max_height};
                    border-radius: 12px; opacity: 0; visibility: hidden;
                    transition: opacity 0.5s ease, visibility 0.5s ease, background-color 0.3s;
                    display: flex; flex-direction: column;
                    pointer-events: none; overflow-y: auto; scrollbar-width: none;
                    box-sizing: border-box; overscroll-behavior: contain !important;
                }
                .fw-panel::-webkit-scrollbar { display: none !important; }
                .fw-wrapper.is_open .fw-panel { opacity: 1; visibility: visible; pointer-events: auto; }
                .fw-content_inner { width: 100%; padding: 15px; box-sizing: border-box; }

                .fw-btn {
                    width: 50px !important; height: 50px !important;
                    background-color: ${btn_color} !important;
                    color: white !important; border: none !important;
                    border-radius: 50% !important; cursor: pointer !important;
                    display: grid !important; place-items: center !important;
                    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease !important;
                    z-index: 2147483647 !important; margin-left: 15px !important;
                    pointer-events: auto !important; flex-shrink: 0;
                    position: relative; top: -12vh;
                }
                .fw-btn:hover { transform: scale(1.1) !important; }

                .fw-icon-stack { position: relative; width: 100%; height: 100%; display: grid; place-items: center; }
                .fw-icon { grid-area: 1/1; font-size: 28px; transition: all 0.4s ease; line-height: 1; }
                .fw-icon.filled { opacity: 1; transform: scale(1) rotate(0deg); }
                .fw-icon.outline { opacity: 0; transform: scale(0.5) rotate(-45deg); }
                .fw-wrapper.is_open .fw-icon.filled { opacity: 0; transform: scale(0.5) rotate(45deg); }
                .fw-wrapper.is_open .fw-icon.outline { opacity: 1; transform: scale(1) rotate(0deg); }
            `;
            document.head.appendChild(style_tag);
        }
    }

    function create_widget_dom() {
        const wrapper_div = document.createElement('div');
        wrapper_div.className = 'fw-wrapper';
        const panel_div = document.createElement('div');
        panel_div.className = 'fw-panel';
        const content_inner_div = document.createElement('div');
        content_inner_div.className = 'fw-content_inner';
        const main_btn = document.createElement('button');
        main_btn.className = 'fw-btn';

        main_btn.innerHTML = `<div class="fw-icon-stack"><span class="fw-icon filled">✦</span><span class="fw-icon outline">✧</span></div>`;

        return { wrapper_div, panel_div, content_inner_div, main_btn };
    }

    function apply_widget_theme(panel_div, main_btn, color_val) {
        panel_div.style.backgroundColor = color_val;
        const shadow_val = get_dynamic_shadow(color_val);
        panel_div.style.setProperty('box-shadow', shadow_val, 'important');
        main_btn.style.setProperty('box-shadow', shadow_val, 'important');
    }

    function inject_widget_content(content_inner_div, target_el) {
        content_inner_div.innerHTML = '';
        if (target_el instanceof HTMLElement) {
            target_el.style.display = 'none';
            const clone_node = target_el.cloneNode(true);
            clone_node.style.display = 'block';
            content_inner_div.appendChild(clone_node);
        }
    }

    function toggle_widget_state(wrapper_div, panel_div, force_close) {
        const is_currently_open = wrapper_div.classList.contains('is_open');
        const should_be_open = force_close ? false : !is_currently_open;
        if (should_be_open) {
            wrapper_div.classList.add('is_open');
            panel_div.scrollTo(0, 0);
        } else {
            wrapper_div.classList.remove('is_open');
        }
    }

    function setup_widget_events(main_btn, wrapper_div, toggle_fn) {
        main_btn.onclick = (e) => {
            e.stopPropagation();
            toggle_fn();
        };
        document.onclick = (e) => {
            if (!wrapper_div.contains(e.target) && wrapper_div.classList.contains('is_open')) toggle_fn(true);
        };
    }

    function create_floating_widget(options = {}) {
        const {
            content = '', btn_color = '#2563eb', max_height = '80vh', bg_color = 'rgb(255, 255, 255)'
        } = options;

        ensure_widget_styles(btn_color, max_height);

        const { wrapper_div, panel_div, content_inner_div, main_btn } = create_widget_dom();

        const toggle = (force_close) => toggle_widget_state(wrapper_div, panel_div, force_close);
        const update_bg = (new_c) => apply_widget_theme(panel_div, main_btn, new_c);
        const refresh_content = (new_t) => inject_widget_content(content_inner_div, new_t);

        // Initialization
        refresh_content(content);
        panel_div.appendChild(content_inner_div);
        wrapper_div.append(main_btn, panel_div);
        document.body.appendChild(wrapper_div);
        update_bg(bg_color);

        setup_widget_events(main_btn, wrapper_div, toggle);

        $CREATED = true;
        return {
            element: wrapper_div,
            update_bg: update_bg,
            refresh_content: refresh_content,
            close: () => toggle(true)
        };
    }
})();