From 06bc919dc608905f9783c5411df80d32aeb9ec00 Mon Sep 17 00:00:00 2001 From: quydev-fs <215090607+quydev-fs@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:19:56 +0700 Subject: [PATCH 1/3] Update Gruvboxize.user.js - fix code rendering --- github/Gruvboxize.user.js | 970 ++++++++++++++------------------------ 1 file changed, 352 insertions(+), 618 deletions(-) diff --git a/github/Gruvboxize.user.js b/github/Gruvboxize.user.js index 0e999be..5823b0a 100644 --- a/github/Gruvboxize.user.js +++ b/github/Gruvboxize.user.js @@ -1,9 +1,9 @@ // ==UserScript== // @name GitHub Gruvbox Theme // @namespace https://github.com/quydev-fs/ViolentMonkeyUserScripts -// @version 1.2 -// @description Apply Gruvbox dark theme to GitHub with enhanced code view -// @author @quydev-fs +// @version 1.6 +// @description Apply Gruvbox dark theme to GitHub without flickering code +// @author quydev-fs // @match https://github.com/* // @match https://*.github.com/* // @grant GM_addStyle @@ -16,20 +16,20 @@ // Gruvbox Dark Colors const gruvbox = { // Backgrounds - bg0_h: "#1d2021", // Darkest - bg0: "#282828", // Standard - bg1: "#3c3836", // Lighter - bg2: "#504945", // Even lighter - bg3: "#665c54", // Even more lighter - bg4: "#7c6f64", // Lightest + bg0_h: "#1d2021", + bg0: "#282828", + bg1: "#3c3836", + bg2: "#504945", + bg3: "#665c54", + bg4: "#7c6f64", // Foreground - fg: "#ebdbb2", // Standard text - fg0: "#fbf1c7", // Bright text - fg1: "#ebdbb2", // Standard text - fg2: "#d5c4a1", // Dim text - fg3: "#bdae93", // Dimmer text - fg4: "#a89984", // Dimmed text + fg: "#ebdbb2", + fg0: "#fbf1c7", + fg1: "#ebdbb2", + fg2: "#d5c4a1", + fg3: "#bdae93", + fg4: "#a89984", // Colors red: "#fb4934", @@ -53,708 +53,442 @@ // UI Specific selection: "#45858833", border: "#3c3836", - - // Syntax highlighting colors - syntax_comment: "#928374", - syntax_keyword: "#fb4934", - syntax_string: "#b8bb26", - syntax_number: "#d3869b", - syntax_function: "#83a598", - syntax_variable: "#ebdbb2", - syntax_type: "#fabd2f", - syntax_constant: "#d3869b", - syntax_operator: "#83a598", - syntax_preprocessor: "#8ec07c", - syntax_attr_name: "#83a598", - syntax_tag: "#fb4934", }; - // Main styling with !important overrides + // Main styling - THÊM CSS để ngăn flickering GM_addStyle(` - /* === ROOT AND BASE STYLES === */ - - :root { - --color-canvas-default: ${gruvbox.bg0} !important; - --color-canvas-subtle: ${gruvbox.bg1} !important; - --color-canvas-inset: ${gruvbox.bg0_h} !important; - --color-border-default: ${gruvbox.bg3} !important; - --color-border-muted: ${gruvbox.bg2} !important; - --color-border-subtle: ${gruvbox.bg1} !important; - --color-fg-default: ${gruvbox.fg} !important; - --color-fg-muted: ${gruvbox.fg3} !important; - --color-fg-subtle: ${gruvbox.fg4} !important; - --color-accent-fg: ${gruvbox.blue} !important; - --color-accent-emphasis: ${gruvbox.bright_blue} !important; - --color-success-fg: ${gruvbox.green} !important; - --color-danger-fg: ${gruvbox.red} !important; - --color-attention-fg: ${gruvbox.yellow} !important; - --color-done-fg: ${gruvbox.purple} !important; + /* === ẨN CODE UNSTYLED KHI ĐANG LOAD === */ + + /* Ẩn tất cả code containers mặc định */ + .react-code-file-contents:not([data-gruvbox-styled]), + .react-code-lines:not([data-gruvbox-styled]), + .react-file-line:not([data-gruvbox-styled]), + .react-code-text:not([data-gruvbox-styled]), + [data-line-number]:not([data-gruvbox-styled]), + .react-line-number:not([data-gruvbox-styled]), + .blob-wrapper:not([data-gruvbox-styled]), + .blob-code:not([data-gruvbox-styled]), + .blob-code-inner:not([data-gruvbox-styled]), + .blob-num:not([data-gruvbox-styled]) { + opacity: 0 !important; + visibility: hidden !important; + } + + /* Hiện styled code */ + .react-code-file-contents[data-gruvbox-styled="true"], + .react-code-lines[data-gruvbox-styled="true"], + .react-file-line[data-gruvbox-styled="true"], + .react-code-text[data-gruvbox-styled="true"], + [data-line-number][data-gruvbox-styled="true"], + .react-line-number[data-gruvbox-styled="true"], + .blob-wrapper[data-gruvbox-styled="true"], + .blob-code[data-gruvbox-styled="true"], + .blob-code-inner[data-gruvbox-styled="true"], + .blob-num[data-gruvbox-styled="true"] { + opacity: 1 !important; + visibility: visible !important; + display: block !important; } /* ================================================================= - GITHUB CODE VIEW SPECIFIC STYLES + CODE VIEW STYLES - KHÔNG DÙNG !important ================================================================= */ - /* === CODE VIEW CONTAINER === */ - - /* Main code view container */ - .react-code-file-contents, - .CodeView-module__SplitPageLayout_Content--qxR1C, - .CodeViewHeader-module__Box--PofRM, - .react-code-view-bottom-padding, - .react-code-view-header-element--narrow, - .react-code-view-header-element--wide, - .react-code-view-header-wrap--narrow { - background-color: ${gruvbox.bg0_h} !important; - border-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; + /* Code containers */ + .react-code-file-contents { + background-color: ${gruvbox.bg0_h}; + color: ${gruvbox.fg}; } - /* Code lines container */ .react-code-lines, - .react-code-line-contents-no-virtualization, - .react-no-virtualization-wrapper, - .react-no-virtualization-wrapper-lines, - .react-line-numbers-no-virtualization { - background-color: ${gruvbox.bg0_h} !important; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; - font-size: 12px !important; - line-height: 1.5 !important; - } - - /* Individual code lines */ - .react-file-line, - .react-code-text, - .react-line-number { - background-color: ${gruvbox.bg0_h} !important; - color: ${gruvbox.fg} !important; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; - } - - /* Line numbers */ - [data-line-number], - .react-line-number, - .code-navigation-cursor { - background-color: ${gruvbox.bg0} !important; - color: ${gruvbox.fg3} !important; - border-right: 1px solid ${gruvbox.bg2} !important; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; - font-size: 12px !important; - padding: 0 10px !important; - text-align: right !important; - min-width: 50px !important; - } - - /* Selected/hovered lines */ - .react-file-line:hover, - .code-navigation-cursor { - background-color: ${gruvbox.bg1} !important; - } - - /* === BLOB VIEW HEADER === */ - - .BlobViewHeader-module__Box--pvsIA, - .BlobViewHeader-module__Box_1--PPihg, - .BlobViewHeader-module__Box_2--G_jCG, - .BlobViewHeader-module__Box_3--Kvpex, - .BlobViewHeader-module__Box_4--vFP89, - .react-blob-sticky-header, - .react-blob-view-header-sticky { - background-color: ${gruvbox.bg0} !important; - border-bottom: 1px solid ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - } - - /* Header buttons */ - .BlobViewHeader-module__IconButton--uO1fA, - .BlobViewHeader-module__IconButton_2--KDy6i, - .BlobViewHeader-module__LinkButton--DMph4, - .react-blob-header-edit-and-raw-actions, - .react-blob-header-edit-and-raw-actions-combined { - background-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - border: 1px solid ${gruvbox.bg3} !important; - border-radius: 6px !important; - } - - .BlobViewHeader-module__IconButton--uO1fA:hover, - .BlobViewHeader-module__IconButton_2--KDy6i:hover { - background-color: ${gruvbox.bg3} !important; - border-color: ${gruvbox.bg4} !important; - } - - /* === SYNTAX HIGHLIGHTING === */ - - /* Base code styling */ - .pl-c, /* comment */ - .pl-c1 /* constant, numeric, label */ { - color: ${gruvbox.syntax_comment} !important; - font-style: italic !important; - } - - .pl-k, /* keyword */ - .pl-kos /* keyword operator symbol */ { - color: ${gruvbox.syntax_keyword} !important; - font-weight: bold !important; - } - - .pl-s, /* string */ - .pl-s1 /* string content */, - .pl-pds /* punctuation definition string */ { - color: ${gruvbox.syntax_string} !important; - } - - .pl-v, /* variable */ - .pl-en /* entity name function */ { - color: ${gruvbox.syntax_variable} !important; + .react-code-line-contents-no-virtualization { + background-color: ${gruvbox.bg0_h}; + color: ${gruvbox.fg}; } - .pl-e, /* entity */ - .pl-ent /* entity tag */ { - color: ${gruvbox.syntax_type} !important; + .react-file-line { + background-color: ${gruvbox.bg0_h}; + color: ${gruvbox.fg}; } - .pl-smi, /* storage modifier import */ - .pl-ent /* entity tag */ { - color: ${gruvbox.syntax_keyword} !important; + .react-code-text { + color: ${gruvbox.fg}; + background-color: ${gruvbox.bg0_h}; } - .pl-ml /* markup list */, - .pl-mh /* markup heading */ { - color: ${gruvbox.syntax_constant} !important; - } - - .pl-cce /* comment constant escape */, - .pl-sr /* string regexp */ { - color: ${gruvbox.syntax_preprocessor} !important; - } - - .pl-ii /* invalid illegal */ { - background-color: ${gruvbox.red} !important; - color: ${gruvbox.bg0} !important; - } - - .pl-iu /* invalid deprecated */ { - color: ${gruvbox.gray} !important; - } - - .pl-mb /* markup bold */ { - font-weight: bold !important; - color: ${gruvbox.fg0} !important; + /* Line numbers */ + [data-line-number], + .react-line-number { + background-color: ${gruvbox.bg0}; + color: ${gruvbox.fg3}; + border-right: 1px solid ${gruvbox.bg2}; } - .pl-mi /* markup italic */ { - font-style: italic !important; - color: ${gruvbox.fg} !important; + /* Blob code */ + .blob-wrapper { + background-color: ${gruvbox.bg0_h}; } - .pl-mc /* markup code */ { - background-color: ${gruvbox.bg1} !important; - color: ${gruvbox.fg} !important; - border-radius: 3px !important; - padding: 2px 4px !important; + .blob-code, + .blob-code-inner { + background-color: ${gruvbox.bg0_h}; + color: ${gruvbox.fg}; } - .pl-mi1 /* markup inserted */ { - color: ${gruvbox.green} !important; + .blob-num { + background-color: ${gruvbox.bg0}; + color: ${gruvbox.fg3}; + border-color: ${gruvbox.bg2}; } - .pl-md /* markup deleted */ { - color: ${gruvbox.red} !important; - text-decoration: line-through !important; + /* Syntax highlighting */ + .pl-c, + .pl-c1 { + color: ${gruvbox.gray}; } - /* === CODE BLOCK SPECIFIC === */ - - /* Read-only text area */ - #read-only-cursor-text-area, - .react-blob-textarea { - background-color: ${gruvbox.bg0_h} !important; - color: ${gruvbox.fg} !important; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; - font-size: 12px !important; - line-height: 1.5 !important; - border: none !important; - padding: 0 !important; - margin: 0 !important; + .pl-k, + .pl-kos { + color: ${gruvbox.red}; } - /* Symbol highlight */ - .symbol-highlight { - background-color: ${gruvbox.bright_blue}33 !important; - border: 1px solid ${gruvbox.bright_blue} !important; - border-radius: 3px !important; + .pl-s, + .pl-s1, + .pl-pds { + color: ${gruvbox.green}; } - /* Find results */ - #find-result-marks-container { - background-color: ${gruvbox.bg0_h} !important; + .pl-v, + .pl-en { + color: ${gruvbox.blue}; } - /* === CODE SIZE BANNER === */ - - .react-code-size-details-banner, - .react-code-size-details-in-header, - .CodeSizeDetails-module__Box--QdxnQ, - .CodeSizeDetails-module__Box_1--_uFDs { - background-color: ${gruvbox.bg1} !important; - color: ${gruvbox.fg} !important; - border: 1px solid ${gruvbox.bg2} !important; - border-radius: 6px !important; + .pl-e, + .pl-ent { + color: ${gruvbox.yellow}; } - .CodeSizeDetails-module__Truncate_1--er0Uk { - color: ${gruvbox.fg} !important; + .pl-smi { + color: ${gruvbox.purple}; } - /* === FILE TREE PANE === */ + /* ================================================================= + GENERAL STYLES - VẪN DÙNG !important + ================================================================= */ - .ReposFileTreePane-module__Pane--D26Sw, - .ReposFileTreePane-module__HidePane--a07q8, - .ReposFileTreePane-module__HideTree--IyYTf, - .react-tree-toggle-button-with-indicator { + body, html { background-color: ${gruvbox.bg0} !important; - border-right: 1px solid ${gruvbox.bg2} !important; color: ${gruvbox.fg} !important; } - /* === FILE NAME HEADER === */ - - .FileNameStickyHeader-module__Box_1--HSpOJ, - .FileNameStickyHeader-module__Box_2--_pDx6, - .FileNameStickyHeader-module__Box_3--AsYoJ, - .FileNameStickyHeader-module__Box_4--IyPx8, - .FileNameStickyHeader-module__Button--SaiiH, - .FileNameStickyHeader-module__GoToTopButton--9lB4x, - #file-name-id, - #sticky-file-name-id, - .StickyHeader { + .page-responsive { background-color: ${gruvbox.bg0} !important; - border-bottom: 1px solid ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; } - /* === BLOB TOP BANNERS === */ - - .BlobTopBanners-module__Box--g_bGk { - background-color: ${gruvbox.bg1} !important; - border-bottom: 1px solid ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - } - - /* === BLOB TAB BUTTONS === */ - - .BlobTabButtons-module__SegmentedControl--JMGov { + .Box { background-color: ${gruvbox.bg1} !important; border-color: ${gruvbox.bg2} !important; - } - - /* === LATEST COMMIT INFO === */ - - .LatestCommit-module__Box--Fimpo, - .LatestCommit-module__Box_1--aQ5OG, - .LatestCommit-module__Box_2--JDY37, - .LatestCommit-module__IconButton--Zxaob, - .CommitAttribution-module__CommitAttributionContainer--Si80C { - background-color: ${gruvbox.bg1} !important; color: ${gruvbox.fg} !important; - border-color: ${gruvbox.bg2} !important; } - /* === EXPAND FILE TREE BUTTON === */ - - .ExpandFileTreeButton-module__Button_1--g8F6Q, - .ExpandFileTreeButton-module__expandButton--oKI1R, - .ExpandFileTreeButton-module__filesButtonBreakpoint--03FKA { - background-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - border-color: ${gruvbox.bg3} !important; - } - - /* === CODE PRINT HIDE === */ - - .react-blob-print-hide { - display: none !important; - } - - /* === LINE CONTAINERS === */ - - .child-of-line-106, - .child-of-line-116, - .child-of-line-126 { - background-color: ${gruvbox.bg0_h} !important; - } - - /* === CODE VIEW SPECIFIC UTILITIES === */ - - .text-mono { - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; - color: ${gruvbox.fg} !important; - } - - .lh-default { - line-height: 1.5 !important; - } - - /* === HIGHLIGHTED LINE MENU === */ - - #highlighted-line-menu-container, - #highlighted-line-menu-positioner { + .feed-content { background-color: ${gruvbox.bg1} !important; - border: 1px solid ${gruvbox.bg3} !important; - border-radius: 6px !important; - box-shadow: 0 8px 24px rgba(0,0,0,0.3) !important; - } - - /* === CLIPBOARD BUTTONS IN CODE VIEW === */ - - #snippet-clipboard-copy-button, - #snippet-clipboard-copy-button-unpositioned, - .CopyToClipboardButton-module__tooltip--HDUYz { - background-color: ${gruvbox.bg2} !important; color: ${gruvbox.fg} !important; - border: 1px solid ${gruvbox.bg3} !important; - border-radius: 6px !important; - } - - #snippet-clipboard-copy-button:hover { - background-color: ${gruvbox.bg3} !important; - border-color: ${gruvbox.bg4} !important; - } - - /* === FILE RESULTS LIST === */ - - .FileResultsList-module__FilesSearchBox--fSAh3, - .CodeViewHeader-module__FileResultsList--bglyC { - background-color: ${gruvbox.bg1} !important; border-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - } - - /* === REPOSITORY CONTENT AREA === */ - - .repository-content, - #repo-content-pjax-container, - #repo-content-turbo-frame, - .js-repo-pjax-container { - background-color: ${gruvbox.bg0} !important; - color: ${gruvbox.fg} !important; } - /* === UNDERLINE NAV IN CODE VIEW === */ - - .UnderlineNav, - .UnderlineNav-body, - .UnderlineNav-actions, - .UnderlineNav-item, - .js-repo-nav { + .AppHeader { background-color: ${gruvbox.bg0} !important; border-bottom-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - } - - .UnderlineNav-item.selected { - background-color: transparent !important; - border-bottom-color: ${gruvbox.blue} !important; - color: ${gruvbox.blue} !important; - } - - /* === SCROLLBAR FOR CODE VIEW === */ - - .react-code-lines::-webkit-scrollbar, - .react-code-file-contents::-webkit-scrollbar { - width: 12px !important; - height: 12px !important; - background-color: ${gruvbox.bg0_h} !important; - } - - .react-code-lines::-webkit-scrollbar-thumb, - .react-code-file-contents::-webkit-scrollbar-thumb { - background-color: ${gruvbox.bg3} !important; - border-radius: 6px !important; - border: 2px solid ${gruvbox.bg0_h} !important; - } - - .react-code-lines::-webkit-scrollbar-thumb:hover, - .react-code-file-contents::-webkit-scrollbar-thumb:hover { - background-color: ${gruvbox.bg4} !important; } - /* === SELECTION IN CODE VIEW === */ + /* ... (giữ nguyên các general styles khác) ... */ - .react-code-lines ::selection, - .react-code-file-contents ::selection { - background-color: ${gruvbox.selection} !important; - color: ${gruvbox.fg0} !important; - } - - /* === CODE LINE SELECTION STATES === */ - - .react-file-line.selected { - background-color: ${gruvbox.bg2} !important; - } - - .react-file-line.highlighted { - background-color: ${gruvbox.bright_blue}22 !important; - } - - /* === RESPONSIVE CODE VIEW === */ - - @media (max-width: 768px) { - .react-code-view-header-element--narrow, - .react-code-view-header-wrap--narrow { - background-color: ${gruvbox.bg0} !important; - } - - .react-code-view-header-mb--narrow { - margin-bottom: 16px !important; - } - - .react-line-number { - min-width: 40px !important; - padding: 0 8px !important; - } - } - - /* === GUTTER STYLING === */ - - [data-line-number]:before { - content: attr(data-line-number); - display: inline-block; - width: 100%; - text-align: right; - color: ${gruvbox.fg3} !important; + /* Ngăn fade-in animations cho code */ + .react-code-file-contents, + .react-code-lines, + .react-file-line { + transition: none !important; + animation: none !important; } - /* === CODE BLOCK BORDERS === */ - + /* Đảm bảo container code có chiều cao đúng */ .react-code-file-contents { - border: 1px solid ${gruvbox.bg2} !important; - border-radius: 6px !important; - overflow: hidden !important; - } - - /* === FOCUS STATES FOR CODE NAVIGATION === */ - - .react-file-line:focus, - .react-code-text:focus { - outline: 2px solid ${gruvbox.blue} !important; - outline-offset: 2px !important; - } - - /* ================================================================= - EXISTING STYLES (from original userscript) - ================================================================= */ - - /* (Include all the existing styles from the original userscript here) */ - /* ... existing styles ... */ - - /* === BASE BODY STYLES === */ - - body, html { - background-color: ${gruvbox.bg0} !important; - color: ${gruvbox.fg} !important; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; - } - - /* === APP HEADER === */ - - .AppHeader, header, [data-testid="AppHeader"] { - background-color: ${gruvbox.bg0} !important; - border-bottom: 1px solid ${gruvbox.bg2} !important; - } - - /* === BUTTONS === */ - - .Button, .btn { - background-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - border: 1px solid ${gruvbox.bg3} !important; - border-radius: 6px !important; - } - - .Button:hover, .btn:hover { - background-color: ${gruvbox.bg3} !important; - } - - /* === LINKS === */ - - a, .Link { - color: ${gruvbox.blue} !important; - } - - a:hover, .Link:hover { - color: ${gruvbox.aqua} !important; - } - - /* === INPUTS === */ - - input, textarea, select, .form-control { - background-color: ${gruvbox.bg1} !important; - color: ${gruvbox.fg} !important; - border: 1px solid ${gruvbox.bg3} !important; - } - - /* === AVATARS === */ - - .avatar { - border: 2px solid ${gruvbox.bg3} !important; - } - - /* === ICONS === */ - - .octicon { - color: ${gruvbox.fg4} !important; - } - - /* === UTILITY CLASSES === */ - - .color-fg-default { - color: ${gruvbox.fg} !important; - } - - .color-fg-muted { - color: ${gruvbox.fg3} !important; - } - - .color-bg-default { - background-color: ${gruvbox.bg0} !important; - } - - .color-bg-subtle { - background-color: ${gruvbox.bg1} !important; - } - - /* === SCROLLBAR === */ - - ::-webkit-scrollbar { - width: 12px !important; - background-color: ${gruvbox.bg1} !important; - } - - ::-webkit-scrollbar-thumb { - background-color: ${gruvbox.bg3} !important; + min-height: 20px !important; } `); - // Function to apply dynamic styles to code view - function applyCodeViewStyles() { - // Apply to dynamically loaded code containers - document.querySelectorAll('.react-code-file-contents, .react-code-lines, .react-file-line').forEach(el => { - if (!el.hasAttribute('data-gruvbox-codestyled')) { - el.setAttribute('data-gruvbox-codestyled', 'true'); - - // Force background colors - if (el.classList.contains('react-code-file-contents')) { - el.style.backgroundColor = gruvbox.bg0_h; - } - if (el.classList.contains('react-code-lines')) { - el.style.backgroundColor = gruvbox.bg0_h; - } - if (el.classList.contains('react-file-line')) { - el.style.backgroundColor = gruvbox.bg0_h; - el.style.color = gruvbox.fg; + // Function để apply styles và ngăn flickering + function applyCodeViewStylesNoFlicker() { + console.log('GitHub Gruvbox: Applying no-flicker code styles'); + + // Tìm tất cả code elements + const codeElements = [ + '.react-code-file-contents', + '.react-code-lines', + '.react-code-line-contents-no-virtualization', + '.react-file-line', + '.react-code-text', + '[data-line-number]', + '.react-line-number', + '.blob-wrapper', + '.blob-code', + '.blob-code-inner', + '.blob-num' + ]; + + // Phase 1: Ẩn unstyled code và style chúng + codeElements.forEach(selector => { + const elements = document.querySelectorAll(selector); + elements.forEach(element => { + // Đánh dấu là đã styled + if (!element.hasAttribute('data-gruvbox-styled')) { + element.setAttribute('data-gruvbox-styled', 'true'); + + // Apply styles + if (element.classList.contains('react-code-file-contents') || + element.classList.contains('react-code-lines') || + element.classList.contains('react-code-line-contents-no-virtualization') || + element.classList.contains('blob-wrapper')) { + element.style.backgroundColor = gruvbox.bg0_h; + element.style.color = gruvbox.fg; + } + + if (element.classList.contains('react-file-line') || + element.classList.contains('react-code-text') || + element.classList.contains('blob-code') || + element.classList.contains('blob-code-inner')) { + element.style.color = gruvbox.fg; + element.style.backgroundColor = gruvbox.bg0_h; + } + + if (element.hasAttribute('data-line-number') || + element.classList.contains('react-line-number') || + element.classList.contains('blob-num')) { + element.style.backgroundColor = gruvbox.bg0; + element.style.color = gruvbox.fg3; + element.style.borderRight = `1px solid ${gruvbox.bg2}`; + } } - } + + // Force hiển thị styled code + element.style.opacity = '1'; + element.style.visibility = 'visible'; + element.style.display = ''; + + // Remove any fade transitions + element.style.transition = 'none'; + element.style.animation = 'none'; + }); }); - // Apply syntax highlighting classes - document.querySelectorAll('.pl-c, .pl-k, .pl-s, .pl-v, .pl-en, .pl-smi').forEach(el => { - if (!el.hasAttribute('data-gruvbox-syntax')) { - el.setAttribute('data-gruvbox-syntax', 'true'); - } + // Phase 2: Apply syntax highlighting + const syntaxSelectors = [ + '.pl-c', '.pl-c1', '.pl-k', '.pl-kos', + '.pl-s', '.pl-s1', '.pl-pds', '.pl-v', + '.pl-en', '.pl-e', '.pl-ent', '.pl-smi' + ]; + + syntaxSelectors.forEach(selector => { + const elements = document.querySelectorAll(selector); + elements.forEach(element => { + if (!element.hasAttribute('data-gruvbox-syntax')) { + element.setAttribute('data-gruvbox-syntax', 'true'); + + if (element.classList.contains('pl-c') || element.classList.contains('pl-c1')) { + element.style.color = gruvbox.gray; + } else if (element.classList.contains('pl-k') || element.classList.contains('pl-kos')) { + element.style.color = gruvbox.red; + } else if (element.classList.contains('pl-s') || element.classList.contains('pl-s1') || element.classList.contains('pl-pds')) { + element.style.color = gruvbox.green; + } else if (element.classList.contains('pl-v') || element.classList.contains('pl-en')) { + element.style.color = gruvbox.blue; + } else if (element.classList.contains('pl-e') || element.classList.contains('pl-ent')) { + element.style.color = gruvbox.yellow; + } else if (element.classList.contains('pl-smi')) { + element.style.color = gruvbox.purple; + } + } + }); }); - // Apply to line numbers - document.querySelectorAll('[data-line-number], .react-line-number').forEach(el => { - if (!el.hasAttribute('data-gruvbox-linenumber')) { - el.setAttribute('data-gruvbox-linenumber', 'true'); - el.style.backgroundColor = gruvbox.bg0; - el.style.color = gruvbox.fg3; - el.style.borderRight = `1px solid ${gruvbox.bg2}`; - } - }); + // Phase 3: Đảm bảo parent containers visible + const parentContainers = document.querySelectorAll( + '.react-code-file-contents, .blob-wrapper, .repository-content' + ); - // Apply to code view header - document.querySelectorAll('.BlobViewHeader-module__Box--pvsIA, .react-blob-sticky-header').forEach(el => { - if (!el.hasAttribute('data-gruvbox-header')) { - el.setAttribute('data-gruvbox-header', 'true'); - el.style.backgroundColor = gruvbox.bg0; - el.style.borderBottom = `1px solid ${gruvbox.bg2}`; - } + parentContainers.forEach(container => { + container.style.opacity = '1'; + container.style.visibility = 'visible'; + + // Force reflow để tránh flicker + void container.offsetHeight; }); } - // Function to enhance existing styles - function enhanceExistingStyles() { - // Apply to all code containers - applyCodeViewStyles(); - - // Force body background + // Function để apply general styles + function applyGeneralStyles() { + // ... (giữ nguyên) ... document.body.style.backgroundColor = gruvbox.bg0; document.body.style.color = gruvbox.fg; - - // Force html background - document.documentElement.style.backgroundColor = gruvbox.bg0; - - // Check for code view specifically - if (document.querySelector('.react-code-file-contents') || - document.querySelector('.blob-wrapper') || - document.querySelector('[data-testid="code-view-content"]')) { + } + + // Function chính với anti-flicker logic + function initializeThemeNoFlicker() { + console.log('GitHub Gruvbox: Initializing no-flicker theme'); + + // Bước 1: Ngay lập tức ẩn code chưa styled + const hideUnstyledCode = () => { + const codeContainers = document.querySelectorAll( + '.react-code-file-contents, .react-code-lines, .blob-wrapper' + ); - console.log('GitHub Gruvbox Theme: Code view detected, applying enhanced styles'); + codeContainers.forEach(container => { + if (!container.hasAttribute('data-gruvbox-styled')) { + container.style.opacity = '0'; + container.style.visibility = 'hidden'; + } + }); + }; + + // Bước 2: Apply styles và hiện styled code + const showStyledCode = () => { + applyCodeViewStylesNoFlicker(); - // Add a custom class to body when in code view - document.body.classList.add('gruvbox-code-view'); - } + // Force re-render + requestAnimationFrame(() => { + applyCodeViewStylesNoFlicker(); + }); + }; + + // Execute + hideUnstyledCode(); + applyGeneralStyles(); + + // Delay một chút rồi hiện styled code + setTimeout(showStyledCode, 10); + setTimeout(showStyledCode, 50); + setTimeout(showStyledCode, 100); + + // Listen for DOM ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + hideUnstyledCode(); + setTimeout(showStyledCode, 10); + applyGeneralStyles(); + }); + } + + // Listen for page load + window.addEventListener('load', () => { + setTimeout(() => { + hideUnstyledCode(); + showStyledCode(); + applyGeneralStyles(); + }, 100); + }); } - // Initial application - enhanceExistingStyles(); + // Khởi động + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeThemeNoFlicker); + } else { + initializeThemeNoFlicker(); + } - // Apply on DOM changes with focus on code containers - const observer = new MutationObserver((mutations) => { - let shouldApply = false; + // Mutation observer chuyên xử lý code view + const codeObserver = new MutationObserver((mutations) => { + let hasCodeElements = false; + mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { - // Check if any added nodes are code-related mutation.addedNodes.forEach(node => { - if (node.nodeType === 1) { // Element node + if (node.nodeType === 1) { + // Check if this is code-related if (node.matches && ( - node.matches('.react-code-file-contents, .react-code-lines, .react-file-line') || - node.querySelector('.react-code-file-contents, .react-code-lines, .react-file-line') + node.matches('.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line') || + node.querySelector('.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line') )) { - shouldApply = true; + hasCodeElements = true; + + // Ngay lập tức ẩn unstyled code + if (node.matches && !node.hasAttribute('data-gruvbox-styled')) { + node.style.opacity = '0'; + node.style.visibility = 'hidden'; + } + + // Check children too + if (node.querySelectorAll) { + node.querySelectorAll( + '.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line' + ).forEach(child => { + if (!child.hasAttribute('data-gruvbox-styled')) { + child.style.opacity = '0'; + child.style.visibility = 'hidden'; + } + }); + } } } }); } }); - if (shouldApply) { - setTimeout(enhanceExistingStyles, 100); + + if (hasCodeElements) { + // Apply styles after a very short delay + setTimeout(() => { + applyCodeViewStylesNoFlicker(); + }, 0); + + // And again after render + requestAnimationFrame(() => { + applyCodeViewStylesNoFlicker(); + }); } }); - observer.observe(document.body, { - childList: true, - subtree: true, - attributes: false, - characterData: false - }); - - // Also apply on page load - window.addEventListener('load', enhanceExistingStyles); - document.addEventListener('DOMContentLoaded', enhanceExistingStyles); + // Start observing + setTimeout(() => { + codeObserver.observe(document.body, { + childList: true, + subtree: true + }); + console.log('GitHub Gruvbox: Code observer started'); + }, 1000); - // Reapply for SPA navigation - const intervalId = setInterval(enhanceExistingStyles, 1000); + // Monitor for SPA navigation + let lastUrl = location.href; + const urlObserver = setInterval(() => { + if (lastUrl !== location.href) { + lastUrl = location.href; + console.log('GitHub Gruvbox: URL changed'); + + // Reapply với anti-flicker + setTimeout(() => { + const hideUnstyled = () => { + document.querySelectorAll( + '.react-code-file-contents, .react-code-lines, .blob-wrapper' + ).forEach(el => { + if (!el.hasAttribute('data-gruvbox-styled')) { + el.style.opacity = '0'; + el.style.visibility = 'hidden'; + } + }); + }; + + hideUnstyled(); + setTimeout(applyCodeViewStylesNoFlicker, 10); + setTimeout(applyCodeViewStylesNoFlicker, 50); + }, 100); + } + }, 1000); // Cleanup window.addEventListener('beforeunload', () => { - clearInterval(intervalId); - observer.disconnect(); + clearInterval(urlObserver); + codeObserver.disconnect(); }); - - console.log('GitHub Gruvbox Theme with Enhanced Code View loaded'); + + console.log('GitHub Gruvbox Theme loaded - No flicker version'); })(); From b5e80c0824d0fd52f920d9a1e4e7985f861eba9b Mon Sep 17 00:00:00 2001 From: quydev-fs <215090607+quydev-fs@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:31:15 +0700 Subject: [PATCH 2/3] GitHub - Fix code rendering --- github/Gruvboxize.user.js | 608 ++++++++++++++++++++++++-------------- 1 file changed, 378 insertions(+), 230 deletions(-) diff --git a/github/Gruvboxize.user.js b/github/Gruvboxize.user.js index 5823b0a..1aa046b 100644 --- a/github/Gruvboxize.user.js +++ b/github/Gruvboxize.user.js @@ -1,9 +1,9 @@ // ==UserScript== -// @name GitHub Gruvbox Theme +// @name GitHub Gruvbox Theme - Perfect Alignment // @namespace https://github.com/quydev-fs/ViolentMonkeyUserScripts -// @version 1.6 -// @description Apply Gruvbox dark theme to GitHub without flickering code -// @author quydev-fs +// @version 1.7 +// @description Apply Gruvbox dark theme to GitHub with perfect code alignment +// @author You // @match https://github.com/* // @match https://*.github.com/* // @grant GM_addStyle @@ -55,11 +55,68 @@ border: "#3c3836", }; - // Main styling - THÊM CSS để ngăn flickering + // Main styling - THÊM ALIGNMENT FIXES GM_addStyle(` - /* === ẨN CODE UNSTYLED KHI ĐANG LOAD === */ + /* === RESET POSITIONS FOR ALIGNMENT === */ - /* Ẩn tất cả code containers mặc định */ + /* Đảm bảo tất cả code elements có position relative */ + .react-code-file-contents[data-gruvbox-styled="true"], + .react-code-lines[data-gruvbox-styled="true"], + .blob-wrapper[data-gruvbox-styled="true"] { + position: relative !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + transform: none !important; + margin: 0 !important; + padding: 0 !important; + } + + /* Fix line alignment */ + .react-file-line[data-gruvbox-styled="true"] { + display: table-row !important; + position: static !important; + width: 100% !important; + } + + /* Fix line number cells */ + [data-line-number][data-gruvbox-styled="true"], + .react-line-number[data-gruvbox-styled="true"], + .blob-num[data-gruvbox-styled="true"] { + display: table-cell !important; + position: static !important; + vertical-align: top !important; + text-align: right !important; + white-space: nowrap !important; + width: 1% !important; + min-width: 50px !important; + padding: 0 10px !important; + box-sizing: border-box !important; + } + + /* Fix code text cells */ + .react-code-text[data-gruvbox-styled="true"], + .blob-code[data-gruvbox-styled="true"], + .blob-code-inner[data-gruvbox-styled="true"] { + display: table-cell !important; + position: static !important; + vertical-align: top !important; + text-align: left !important; + width: 99% !important; + padding-left: 10px !important; + box-sizing: border-box !important; + } + + /* Đảm bảo table layout đúng */ + .react-code-lines[data-gruvbox-styled="true"] { + display: table !important; + width: 100% !important; + border-collapse: collapse !important; + table-layout: fixed !important; + } + + /* Ẩn unstyled code hoàn toàn */ .react-code-file-contents:not([data-gruvbox-styled]), .react-code-lines:not([data-gruvbox-styled]), .react-file-line:not([data-gruvbox-styled]), @@ -72,84 +129,127 @@ .blob-num:not([data-gruvbox-styled]) { opacity: 0 !important; visibility: hidden !important; + display: none !important; } - /* Hiện styled code */ + /* Hiện styled code với đúng layout */ .react-code-file-contents[data-gruvbox-styled="true"], .react-code-lines[data-gruvbox-styled="true"], + .blob-wrapper[data-gruvbox-styled="true"] { + opacity: 1 !important; + visibility: visible !important; + display: block !important; + } + .react-file-line[data-gruvbox-styled="true"], - .react-code-text[data-gruvbox-styled="true"], [data-line-number][data-gruvbox-styled="true"], .react-line-number[data-gruvbox-styled="true"], - .blob-wrapper[data-gruvbox-styled="true"], + .blob-num[data-gruvbox-styled="true"], + .react-code-text[data-gruvbox-styled="true"], .blob-code[data-gruvbox-styled="true"], - .blob-code-inner[data-gruvbox-styled="true"], - .blob-num[data-gruvbox-styled="true"] { + .blob-code-inner[data-gruvbox-styled="true"] { opacity: 1 !important; visibility: visible !important; - display: block !important; } /* ================================================================= - CODE VIEW STYLES - KHÔNG DÙNG !important + CODE VIEW STYLES - VỚI ALIGNMENT ================================================================= */ - /* Code containers */ + /* Main containers */ .react-code-file-contents { background-color: ${gruvbox.bg0_h}; color: ${gruvbox.fg}; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + font-size: 12px; + line-height: 20px; + border: 1px solid ${gruvbox.bg2}; + border-radius: 6px; + overflow: auto; } - .react-code-lines, - .react-code-line-contents-no-virtualization { + .react-code-lines { background-color: ${gruvbox.bg0_h}; color: ${gruvbox.fg}; + font-family: inherit; + font-size: inherit; + line-height: inherit; } + /* Line numbers */ + [data-line-number], + .react-line-number { + background-color: ${gruvbox.bg0}; + color: ${gruvbox.fg3}; + font-family: inherit; + font-size: inherit; + line-height: inherit; + border-right: 1px solid ${gruvbox.bg2}; + user-select: none; + -webkit-user-select: none; + cursor: default; + } + + /* Code text */ .react-file-line { background-color: ${gruvbox.bg0_h}; color: ${gruvbox.fg}; + font-family: inherit; + font-size: inherit; + line-height: inherit; } .react-code-text { color: ${gruvbox.fg}; background-color: ${gruvbox.bg0_h}; + font-family: inherit; + font-size: inherit; + line-height: inherit; + white-space: pre; + word-break: normal; + word-wrap: normal; + overflow-wrap: normal; } - /* Line numbers */ - [data-line-number], - .react-line-number { - background-color: ${gruvbox.bg0}; - color: ${gruvbox.fg3}; - border-right: 1px solid ${gruvbox.bg2}; - } - - /* Blob code */ + /* Blob styles (GitHub classic) */ .blob-wrapper { background-color: ${gruvbox.bg0_h}; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + font-size: 12px; + line-height: 20px; } .blob-code, .blob-code-inner { background-color: ${gruvbox.bg0_h}; color: ${gruvbox.fg}; + font-family: inherit; + font-size: inherit; + line-height: inherit; + white-space: pre; } .blob-num { background-color: ${gruvbox.bg0}; color: ${gruvbox.fg3}; + font-family: inherit; + font-size: inherit; + line-height: inherit; border-color: ${gruvbox.bg2}; + user-select: none; } /* Syntax highlighting */ .pl-c, .pl-c1 { color: ${gruvbox.gray}; + font-style: italic; } .pl-k, .pl-kos { color: ${gruvbox.red}; + font-weight: bold; } .pl-s, @@ -173,7 +273,7 @@ } /* ================================================================= - GENERAL STYLES - VẪN DÙNG !important + GENERAL STYLES ================================================================= */ body, html { @@ -185,47 +285,176 @@ background-color: ${gruvbox.bg0} !important; } - .Box { - background-color: ${gruvbox.bg1} !important; - border-color: ${gruvbox.bg2} !important; - color: ${gruvbox.fg} !important; - } - - .feed-content { - background-color: ${gruvbox.bg1} !important; - color: ${gruvbox.fg} !important; - border-color: ${gruvbox.bg2} !important; - } - - .AppHeader { - background-color: ${gruvbox.bg0} !important; - border-bottom-color: ${gruvbox.bg2} !important; - } - - /* ... (giữ nguyên các general styles khác) ... */ + /* ... (giữ nguyên các styles khác) ... */ - /* Ngăn fade-in animations cho code */ + /* Ngăn animations */ .react-code-file-contents, .react-code-lines, - .react-file-line { + .react-file-line, + .blob-wrapper { transition: none !important; animation: none !important; } - - /* Đảm bảo container code có chiều cao đúng */ - .react-code-file-contents { - min-height: 20px !important; - } `); - // Function để apply styles và ngăn flickering - function applyCodeViewStylesNoFlicker() { - console.log('GitHub Gruvbox: Applying no-flicker code styles'); + // Function để fix alignment + function fixCodeAlignment() { + console.log('GitHub Gruvbox: Fixing code alignment'); + + // Đảm bảo container có đúng layout + const mainContainers = document.querySelectorAll( + '.react-code-file-contents[data-gruvbox-styled="true"], ' + + '.blob-wrapper[data-gruvbox-styled="true"]' + ); + + mainContainers.forEach(container => { + // Reset position + container.style.position = 'relative'; + container.style.top = '0'; + container.style.left = '0'; + container.style.right = '0'; + container.style.bottom = '0'; + container.style.transform = 'none'; + container.style.margin = '0'; + container.style.padding = '0'; + container.style.display = 'block'; + container.style.overflow = 'auto'; + + // Đảm bảo font và line height + container.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; + container.style.fontSize = '12px'; + container.style.lineHeight = '20px'; + }); + + // Fix line containers + const lineContainers = document.querySelectorAll( + '.react-code-lines[data-gruvbox-styled="true"]' + ); + + lineContainers.forEach(container => { + // Set as table for proper alignment + container.style.display = 'table'; + container.style.width = '100%'; + container.style.borderCollapse = 'collapse'; + container.style.tableLayout = 'fixed'; + }); - // Tìm tất cả code elements - const codeElements = [ + // Fix individual lines + const lines = document.querySelectorAll( + '.react-file-line[data-gruvbox-styled="true"]' + ); + + lines.forEach(line => { + line.style.display = 'table-row'; + line.style.position = 'static'; + line.style.width = '100%'; + }); + + // Fix line numbers + const lineNumbers = document.querySelectorAll( + '[data-line-number][data-gruvbox-styled="true"], ' + + '.react-line-number[data-gruvbox-styled="true"], ' + + '.blob-num[data-gruvbox-styled="true"]' + ); + + lineNumbers.forEach(num => { + // Table cell layout + num.style.display = 'table-cell'; + num.style.position = 'static'; + num.style.verticalAlign = 'top'; + num.style.textAlign = 'right'; + num.style.whiteSpace = 'nowrap'; + num.style.width = '1%'; + num.style.minWidth = '50px'; + num.style.padding = '0 10px'; + num.style.boxSizing = 'border-box'; + + // Style + num.style.backgroundColor = gruvbox.bg0; + num.style.color = gruvbox.fg3; + num.style.borderRight = `1px solid ${gruvbox.bg2}`; + num.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; + num.style.fontSize = '12px'; + num.style.lineHeight = '20px'; + num.style.userSelect = 'none'; + num.style.WebkitUserSelect = 'none'; + num.style.cursor = 'default'; + }); + + // Fix code text + const codeTexts = document.querySelectorAll( + '.react-code-text[data-gruvbox-styled="true"], ' + + '.blob-code[data-gruvbox-styled="true"], ' + + '.blob-code-inner[data-gruvbox-styled="true"]' + ); + + codeTexts.forEach(text => { + // Table cell layout + text.style.display = 'table-cell'; + text.style.position = 'static'; + text.style.verticalAlign = 'top'; + text.style.textAlign = 'left'; + text.style.width = '99%'; + text.style.paddingLeft = '10px'; + text.style.boxSizing = 'border-box'; + + // Style + text.style.color = gruvbox.fg; + text.style.backgroundColor = gruvbox.bg0_h; + text.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; + text.style.fontSize = '12px'; + text.style.lineHeight = '20px'; + text.style.whiteSpace = 'pre'; + text.style.wordBreak = 'normal'; + text.style.wordWrap = 'normal'; + text.style.overflowWrap = 'normal'; + }); + + // Apply syntax highlighting + applySyntaxHighlighting(); + } + + // Function để apply syntax highlighting + function applySyntaxHighlighting() { + const syntaxMap = { + 'pl-c': gruvbox.gray, + 'pl-c1': gruvbox.gray, + 'pl-k': gruvbox.red, + 'pl-kos': gruvbox.red, + 'pl-s': gruvbox.green, + 'pl-s1': gruvbox.green, + 'pl-pds': gruvbox.green, + 'pl-v': gruvbox.blue, + 'pl-en': gruvbox.blue, + 'pl-e': gruvbox.yellow, + 'pl-ent': gruvbox.yellow, + 'pl-smi': gruvbox.purple + }; + + Object.keys(syntaxMap).forEach(className => { + const elements = document.querySelectorAll(`.${className}[data-gruvbox-styled="true"]`); + elements.forEach(el => { + el.style.color = syntaxMap[className]; + + // Thêm font styles cho keywords và comments + if (className === 'pl-c' || className === 'pl-c1') { + el.style.fontStyle = 'italic'; + } + if (className === 'pl-k' || className === 'pl-kos') { + el.style.fontWeight = 'bold'; + } + }); + }); + } + + // Function chính để apply styles + function applyCodeViewStyles() { + console.log('GitHub Gruvbox: Applying styles'); + + // Mark và style tất cả code elements + const codeSelectors = [ '.react-code-file-contents', - '.react-code-lines', + '.react-code-lines', '.react-code-line-contents-no-virtualization', '.react-file-line', '.react-code-text', @@ -237,258 +466,177 @@ '.blob-num' ]; - // Phase 1: Ẩn unstyled code và style chúng - codeElements.forEach(selector => { + codeSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { - // Đánh dấu là đã styled if (!element.hasAttribute('data-gruvbox-styled')) { element.setAttribute('data-gruvbox-styled', 'true'); - - // Apply styles - if (element.classList.contains('react-code-file-contents') || - element.classList.contains('react-code-lines') || - element.classList.contains('react-code-line-contents-no-virtualization') || - element.classList.contains('blob-wrapper')) { - element.style.backgroundColor = gruvbox.bg0_h; - element.style.color = gruvbox.fg; - } - - if (element.classList.contains('react-file-line') || - element.classList.contains('react-code-text') || - element.classList.contains('blob-code') || - element.classList.contains('blob-code-inner')) { - element.style.color = gruvbox.fg; - element.style.backgroundColor = gruvbox.bg0_h; - } - - if (element.hasAttribute('data-line-number') || - element.classList.contains('react-line-number') || - element.classList.contains('blob-num')) { - element.style.backgroundColor = gruvbox.bg0; - element.style.color = gruvbox.fg3; - element.style.borderRight = `1px solid ${gruvbox.bg2}`; - } - } - - // Force hiển thị styled code - element.style.opacity = '1'; - element.style.visibility = 'visible'; - element.style.display = ''; - - // Remove any fade transitions - element.style.transition = 'none'; - element.style.animation = 'none'; - }); - }); - - // Phase 2: Apply syntax highlighting - const syntaxSelectors = [ - '.pl-c', '.pl-c1', '.pl-k', '.pl-kos', - '.pl-s', '.pl-s1', '.pl-pds', '.pl-v', - '.pl-en', '.pl-e', '.pl-ent', '.pl-smi' - ]; - - syntaxSelectors.forEach(selector => { - const elements = document.querySelectorAll(selector); - elements.forEach(element => { - if (!element.hasAttribute('data-gruvbox-syntax')) { - element.setAttribute('data-gruvbox-syntax', 'true'); - - if (element.classList.contains('pl-c') || element.classList.contains('pl-c1')) { - element.style.color = gruvbox.gray; - } else if (element.classList.contains('pl-k') || element.classList.contains('pl-kos')) { - element.style.color = gruvbox.red; - } else if (element.classList.contains('pl-s') || element.classList.contains('pl-s1') || element.classList.contains('pl-pds')) { - element.style.color = gruvbox.green; - } else if (element.classList.contains('pl-v') || element.classList.contains('pl-en')) { - element.style.color = gruvbox.blue; - } else if (element.classList.contains('pl-e') || element.classList.contains('pl-ent')) { - element.style.color = gruvbox.yellow; - } else if (element.classList.contains('pl-smi')) { - element.style.color = gruvbox.purple; - } } }); }); - // Phase 3: Đảm bảo parent containers visible - const parentContainers = document.querySelectorAll( - '.react-code-file-contents, .blob-wrapper, .repository-content' - ); + // Apply alignment fixes + setTimeout(fixCodeAlignment, 0); - parentContainers.forEach(container => { - container.style.opacity = '1'; - container.style.visibility = 'visible'; - - // Force reflow để tránh flicker - void container.offsetHeight; + // Force reflow + requestAnimationFrame(() => { + fixCodeAlignment(); + applySyntaxHighlighting(); }); } // Function để apply general styles function applyGeneralStyles() { - // ... (giữ nguyên) ... document.body.style.backgroundColor = gruvbox.bg0; document.body.style.color = gruvbox.fg; + + // Apply to page-responsive + const pageResp = document.querySelector('.page-responsive'); + if (pageResp) { + pageResp.style.backgroundColor = gruvbox.bg0; + } } - // Function chính với anti-flicker logic - function initializeThemeNoFlicker() { - console.log('GitHub Gruvbox: Initializing no-flicker theme'); - - // Bước 1: Ngay lập tức ẩn code chưa styled - const hideUnstyledCode = () => { - const codeContainers = document.querySelectorAll( - '.react-code-file-contents, .react-code-lines, .blob-wrapper' - ); + // Khởi động theme + function initializeTheme() { + console.log('GitHub Gruvbox: Initializing with alignment fixes'); + + // Bước 1: Ẩn unstyled code ngay lập tức + const hideUnstyled = () => { + const unstyledSelectors = [ + '.react-code-file-contents:not([data-gruvbox-styled])', + '.react-code-lines:not([data-gruvbox-styled])', + '.blob-wrapper:not([data-gruvbox-styled])' + ]; - codeContainers.forEach(container => { - if (!container.hasAttribute('data-gruvbox-styled')) { - container.style.opacity = '0'; - container.style.visibility = 'hidden'; - } + unstyledSelectors.forEach(selector => { + const elements = document.querySelectorAll(selector); + elements.forEach(el => { + el.style.opacity = '0'; + el.style.visibility = 'hidden'; + el.style.display = 'none'; + }); }); }; - // Bước 2: Apply styles và hiện styled code - const showStyledCode = () => { - applyCodeViewStylesNoFlicker(); - - // Force re-render - requestAnimationFrame(() => { - applyCodeViewStylesNoFlicker(); - }); + // Bước 2: Apply styles và alignment + const applyAllStyles = () => { + applyGeneralStyles(); + applyCodeViewStyles(); }; // Execute - hideUnstyledCode(); - applyGeneralStyles(); - - // Delay một chút rồi hiện styled code - setTimeout(showStyledCode, 10); - setTimeout(showStyledCode, 50); - setTimeout(showStyledCode, 100); + hideUnstyled(); + setTimeout(applyAllStyles, 10); - // Listen for DOM ready + // Listeners if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { - hideUnstyledCode(); - setTimeout(showStyledCode, 10); - applyGeneralStyles(); + hideUnstyled(); + setTimeout(applyAllStyles, 10); }); } - // Listen for page load window.addEventListener('load', () => { setTimeout(() => { - hideUnstyledCode(); - showStyledCode(); - applyGeneralStyles(); + hideUnstyled(); + applyAllStyles(); }, 100); }); } - // Khởi động + // Start if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeThemeNoFlicker); + document.addEventListener('DOMContentLoaded', initializeTheme); } else { - initializeThemeNoFlicker(); + initializeTheme(); } - // Mutation observer chuyên xử lý code view - const codeObserver = new MutationObserver((mutations) => { - let hasCodeElements = false; + // Mutation observer cho alignment fixes + const alignmentObserver = new MutationObserver((mutations) => { + let needsAlignmentFix = false; mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { - // Check if this is code-related + // Check for code elements if (node.matches && ( - node.matches('.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line') || - node.querySelector('.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line') + node.matches('.react-code-file-contents, .react-code-lines, .blob-wrapper') || + node.querySelector('.react-code-file-contents, .react-code-lines, .blob-wrapper') )) { - hasCodeElements = true; + needsAlignmentFix = true; - // Ngay lập tức ẩn unstyled code + // Hide unstyled immediately if (node.matches && !node.hasAttribute('data-gruvbox-styled')) { node.style.opacity = '0'; node.style.visibility = 'hidden'; - } - - // Check children too - if (node.querySelectorAll) { - node.querySelectorAll( - '.react-code-file-contents, .react-code-lines, .blob-wrapper, .react-file-line' - ).forEach(child => { - if (!child.hasAttribute('data-gruvbox-styled')) { - child.style.opacity = '0'; - child.style.visibility = 'hidden'; - } - }); + node.style.display = 'none'; } } } }); } + + // Also check for style changes that might affect alignment + if (mutation.type === 'attributes' && mutation.attributeName === 'style') { + const target = mutation.target; + if (target.hasAttribute('data-gruvbox-styled')) { + needsAlignmentFix = true; + } + } }); - if (hasCodeElements) { - // Apply styles after a very short delay + if (needsAlignmentFix) { + // Apply fixes with proper timing setTimeout(() => { - applyCodeViewStylesNoFlicker(); + applyCodeViewStyles(); + fixCodeAlignment(); }, 0); - // And again after render requestAnimationFrame(() => { - applyCodeViewStylesNoFlicker(); + fixCodeAlignment(); }); } }); // Start observing setTimeout(() => { - codeObserver.observe(document.body, { + alignmentObserver.observe(document.body, { childList: true, - subtree: true + subtree: true, + attributes: true, + attributeFilter: ['style', 'class'] }); - console.log('GitHub Gruvbox: Code observer started'); - }, 1000); + console.log('GitHub Gruvbox: Alignment observer started'); + }, 1500); - // Monitor for SPA navigation + // SPA navigation detection let lastUrl = location.href; - const urlObserver = setInterval(() => { + setInterval(() => { if (lastUrl !== location.href) { lastUrl = location.href; - console.log('GitHub Gruvbox: URL changed'); + console.log('GitHub Gruvbox: Page changed, reapplying alignment'); - // Reapply với anti-flicker setTimeout(() => { - const hideUnstyled = () => { - document.querySelectorAll( - '.react-code-file-contents, .react-code-lines, .blob-wrapper' - ).forEach(el => { - if (!el.hasAttribute('data-gruvbox-styled')) { - el.style.opacity = '0'; - el.style.visibility = 'hidden'; - } - }); - }; + // Re-hide unstyled + document.querySelectorAll( + '.react-code-file-contents:not([data-gruvbox-styled]), ' + + '.react-code-lines:not([data-gruvbox-styled]), ' + + '.blob-wrapper:not([data-gruvbox-styled])' + ).forEach(el => { + el.style.opacity = '0'; + el.style.visibility = 'hidden'; + el.style.display = 'none'; + }); - hideUnstyled(); - setTimeout(applyCodeViewStylesNoFlicker, 10); - setTimeout(applyCodeViewStylesNoFlicker, 50); - }, 100); + // Reapply + applyCodeViewStyles(); + setTimeout(fixCodeAlignment, 50); + }, 200); } }, 1000); - // Cleanup - window.addEventListener('beforeunload', () => { - clearInterval(urlObserver); - codeObserver.disconnect(); - }); - - console.log('GitHub Gruvbox Theme loaded - No flicker version'); + console.log('GitHub Gruvbox Theme loaded with perfect alignment!'); })(); From e3d9a907b1d5ef750dac5cb67467aa1095180b87 Mon Sep 17 00:00:00 2001 From: quydev-fs <215090607+quydev-fs@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:29:08 +0700 Subject: [PATCH 3/3] Fix global styles --- github/Gruvboxize.user.js | 1404 ++++++++++++++++++++----------------- 1 file changed, 774 insertions(+), 630 deletions(-) diff --git a/github/Gruvboxize.user.js b/github/Gruvboxize.user.js index 1aa046b..31d880e 100644 --- a/github/Gruvboxize.user.js +++ b/github/Gruvboxize.user.js @@ -1,642 +1,786 @@ // ==UserScript== -// @name GitHub Gruvbox Theme - Perfect Alignment -// @namespace https://github.com/quydev-fs/ViolentMonkeyUserScripts -// @version 1.7 -// @description Apply Gruvbox dark theme to GitHub with perfect code alignment -// @author You +// @name GitHub Gruvbox Dark (Clean, No JS Hacks) +// @namespace https://github.com/quydev-fs/ViolentMonkeyUserScripts/ +// @version 2.1 +// @author quydev-fs +// @description Gruvbox Dark for GitHub - CSS only, stable on Kiwi + ViolentMonkey // @match https://github.com/* -// @match https://*.github.com/* // @grant GM_addStyle // @run-at document-start // ==/UserScript== -(function() { - 'use strict'; - - // Gruvbox Dark Colors - const gruvbox = { - // Backgrounds - bg0_h: "#1d2021", - bg0: "#282828", - bg1: "#3c3836", - bg2: "#504945", - bg3: "#665c54", - bg4: "#7c6f64", - - // Foreground - fg: "#ebdbb2", - fg0: "#fbf1c7", - fg1: "#ebdbb2", - fg2: "#d5c4a1", - fg3: "#bdae93", - fg4: "#a89984", - - // Colors - red: "#fb4934", - green: "#b8bb26", - yellow: "#fabd2f", - blue: "#83a598", - purple: "#d3869b", - aqua: "#8ec07c", - orange: "#fe8019", - gray: "#928374", - - // Bright variants - bright_red: "#cc241d", - bright_green: "#98971a", - bright_yellow: "#d79921", - bright_blue: "#458588", - bright_purple: "#b16286", - bright_aqua: "#689d6a", - bright_orange: "#d65d0e", - - // UI Specific - selection: "#45858833", - border: "#3c3836", - }; - - // Main styling - THÊM ALIGNMENT FIXES - GM_addStyle(` - /* === RESET POSITIONS FOR ALIGNMENT === */ - - /* Đảm bảo tất cả code elements có position relative */ - .react-code-file-contents[data-gruvbox-styled="true"], - .react-code-lines[data-gruvbox-styled="true"], - .blob-wrapper[data-gruvbox-styled="true"] { - position: relative !important; - top: 0 !important; - left: 0 !important; - right: 0 !important; - bottom: 0 !important; - transform: none !important; - margin: 0 !important; - padding: 0 !important; - } - - /* Fix line alignment */ - .react-file-line[data-gruvbox-styled="true"] { - display: table-row !important; - position: static !important; - width: 100% !important; - } - - /* Fix line number cells */ - [data-line-number][data-gruvbox-styled="true"], - .react-line-number[data-gruvbox-styled="true"], - .blob-num[data-gruvbox-styled="true"] { - display: table-cell !important; - position: static !important; - vertical-align: top !important; - text-align: right !important; - white-space: nowrap !important; - width: 1% !important; - min-width: 50px !important; - padding: 0 10px !important; - box-sizing: border-box !important; - } - - /* Fix code text cells */ - .react-code-text[data-gruvbox-styled="true"], - .blob-code[data-gruvbox-styled="true"], - .blob-code-inner[data-gruvbox-styled="true"] { - display: table-cell !important; - position: static !important; - vertical-align: top !important; - text-align: left !important; - width: 99% !important; - padding-left: 10px !important; - box-sizing: border-box !important; - } - - /* Đảm bảo table layout đúng */ - .react-code-lines[data-gruvbox-styled="true"] { - display: table !important; - width: 100% !important; - border-collapse: collapse !important; - table-layout: fixed !important; - } - - /* Ẩn unstyled code hoàn toàn */ - .react-code-file-contents:not([data-gruvbox-styled]), - .react-code-lines:not([data-gruvbox-styled]), - .react-file-line:not([data-gruvbox-styled]), - .react-code-text:not([data-gruvbox-styled]), - [data-line-number]:not([data-gruvbox-styled]), - .react-line-number:not([data-gruvbox-styled]), - .blob-wrapper:not([data-gruvbox-styled]), - .blob-code:not([data-gruvbox-styled]), - .blob-code-inner:not([data-gruvbox-styled]), - .blob-num:not([data-gruvbox-styled]) { - opacity: 0 !important; - visibility: hidden !important; - display: none !important; - } - - /* Hiện styled code với đúng layout */ - .react-code-file-contents[data-gruvbox-styled="true"], - .react-code-lines[data-gruvbox-styled="true"], - .blob-wrapper[data-gruvbox-styled="true"] { - opacity: 1 !important; - visibility: visible !important; - display: block !important; - } - - .react-file-line[data-gruvbox-styled="true"], - [data-line-number][data-gruvbox-styled="true"], - .react-line-number[data-gruvbox-styled="true"], - .blob-num[data-gruvbox-styled="true"], - .react-code-text[data-gruvbox-styled="true"], - .blob-code[data-gruvbox-styled="true"], - .blob-code-inner[data-gruvbox-styled="true"] { - opacity: 1 !important; - visibility: visible !important; - } - - /* ================================================================= - CODE VIEW STYLES - VỚI ALIGNMENT - ================================================================= */ - - /* Main containers */ - .react-code-file-contents { - background-color: ${gruvbox.bg0_h}; - color: ${gruvbox.fg}; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; - font-size: 12px; - line-height: 20px; - border: 1px solid ${gruvbox.bg2}; - border-radius: 6px; - overflow: auto; - } - - .react-code-lines { - background-color: ${gruvbox.bg0_h}; - color: ${gruvbox.fg}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - } - - /* Line numbers */ - [data-line-number], - .react-line-number { - background-color: ${gruvbox.bg0}; - color: ${gruvbox.fg3}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - border-right: 1px solid ${gruvbox.bg2}; - user-select: none; - -webkit-user-select: none; - cursor: default; - } - - /* Code text */ - .react-file-line { - background-color: ${gruvbox.bg0_h}; - color: ${gruvbox.fg}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - } - - .react-code-text { - color: ${gruvbox.fg}; - background-color: ${gruvbox.bg0_h}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - white-space: pre; - word-break: normal; - word-wrap: normal; - overflow-wrap: normal; - } - - /* Blob styles (GitHub classic) */ - .blob-wrapper { - background-color: ${gruvbox.bg0_h}; - font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; - font-size: 12px; - line-height: 20px; - } - - .blob-code, - .blob-code-inner { - background-color: ${gruvbox.bg0_h}; - color: ${gruvbox.fg}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - white-space: pre; - } - - .blob-num { - background-color: ${gruvbox.bg0}; - color: ${gruvbox.fg3}; - font-family: inherit; - font-size: inherit; - line-height: inherit; - border-color: ${gruvbox.bg2}; - user-select: none; - } - - /* Syntax highlighting */ - .pl-c, - .pl-c1 { - color: ${gruvbox.gray}; - font-style: italic; - } - - .pl-k, - .pl-kos { - color: ${gruvbox.red}; - font-weight: bold; - } - - .pl-s, - .pl-s1, - .pl-pds { - color: ${gruvbox.green}; - } - - .pl-v, - .pl-en { - color: ${gruvbox.blue}; - } - - .pl-e, - .pl-ent { - color: ${gruvbox.yellow}; - } - - .pl-smi { - color: ${gruvbox.purple}; - } - - /* ================================================================= - GENERAL STYLES - ================================================================= */ - - body, html { - background-color: ${gruvbox.bg0} !important; - color: ${gruvbox.fg} !important; - } - - .page-responsive { - background-color: ${gruvbox.bg0} !important; - } - - /* ... (giữ nguyên các styles khác) ... */ - - /* Ngăn animations */ - .react-code-file-contents, - .react-code-lines, - .react-file-line, - .blob-wrapper { - transition: none !important; - animation: none !important; - } - `); - - // Function để fix alignment - function fixCodeAlignment() { - console.log('GitHub Gruvbox: Fixing code alignment'); - - // Đảm bảo container có đúng layout - const mainContainers = document.querySelectorAll( - '.react-code-file-contents[data-gruvbox-styled="true"], ' + - '.blob-wrapper[data-gruvbox-styled="true"]' - ); - - mainContainers.forEach(container => { - // Reset position - container.style.position = 'relative'; - container.style.top = '0'; - container.style.left = '0'; - container.style.right = '0'; - container.style.bottom = '0'; - container.style.transform = 'none'; - container.style.margin = '0'; - container.style.padding = '0'; - container.style.display = 'block'; - container.style.overflow = 'auto'; - - // Đảm bảo font và line height - container.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; - container.style.fontSize = '12px'; - container.style.lineHeight = '20px'; - }); - - // Fix line containers - const lineContainers = document.querySelectorAll( - '.react-code-lines[data-gruvbox-styled="true"]' - ); - - lineContainers.forEach(container => { - // Set as table for proper alignment - container.style.display = 'table'; - container.style.width = '100%'; - container.style.borderCollapse = 'collapse'; - container.style.tableLayout = 'fixed'; - }); - - // Fix individual lines - const lines = document.querySelectorAll( - '.react-file-line[data-gruvbox-styled="true"]' - ); - - lines.forEach(line => { - line.style.display = 'table-row'; - line.style.position = 'static'; - line.style.width = '100%'; - }); - - // Fix line numbers - const lineNumbers = document.querySelectorAll( - '[data-line-number][data-gruvbox-styled="true"], ' + - '.react-line-number[data-gruvbox-styled="true"], ' + - '.blob-num[data-gruvbox-styled="true"]' - ); - - lineNumbers.forEach(num => { - // Table cell layout - num.style.display = 'table-cell'; - num.style.position = 'static'; - num.style.verticalAlign = 'top'; - num.style.textAlign = 'right'; - num.style.whiteSpace = 'nowrap'; - num.style.width = '1%'; - num.style.minWidth = '50px'; - num.style.padding = '0 10px'; - num.style.boxSizing = 'border-box'; - - // Style - num.style.backgroundColor = gruvbox.bg0; - num.style.color = gruvbox.fg3; - num.style.borderRight = `1px solid ${gruvbox.bg2}`; - num.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; - num.style.fontSize = '12px'; - num.style.lineHeight = '20px'; - num.style.userSelect = 'none'; - num.style.WebkitUserSelect = 'none'; - num.style.cursor = 'default'; - }); - - // Fix code text - const codeTexts = document.querySelectorAll( - '.react-code-text[data-gruvbox-styled="true"], ' + - '.blob-code[data-gruvbox-styled="true"], ' + - '.blob-code-inner[data-gruvbox-styled="true"]' - ); - - codeTexts.forEach(text => { - // Table cell layout - text.style.display = 'table-cell'; - text.style.position = 'static'; - text.style.verticalAlign = 'top'; - text.style.textAlign = 'left'; - text.style.width = '99%'; - text.style.paddingLeft = '10px'; - text.style.boxSizing = 'border-box'; - - // Style - text.style.color = gruvbox.fg; - text.style.backgroundColor = gruvbox.bg0_h; - text.style.fontFamily = "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"; - text.style.fontSize = '12px'; - text.style.lineHeight = '20px'; - text.style.whiteSpace = 'pre'; - text.style.wordBreak = 'normal'; - text.style.wordWrap = 'normal'; - text.style.overflowWrap = 'normal'; - }); - - // Apply syntax highlighting - applySyntaxHighlighting(); - } - - // Function để apply syntax highlighting - function applySyntaxHighlighting() { - const syntaxMap = { - 'pl-c': gruvbox.gray, - 'pl-c1': gruvbox.gray, - 'pl-k': gruvbox.red, - 'pl-kos': gruvbox.red, - 'pl-s': gruvbox.green, - 'pl-s1': gruvbox.green, - 'pl-pds': gruvbox.green, - 'pl-v': gruvbox.blue, - 'pl-en': gruvbox.blue, - 'pl-e': gruvbox.yellow, - 'pl-ent': gruvbox.yellow, - 'pl-smi': gruvbox.purple - }; - - Object.keys(syntaxMap).forEach(className => { - const elements = document.querySelectorAll(`.${className}[data-gruvbox-styled="true"]`); - elements.forEach(el => { - el.style.color = syntaxMap[className]; - - // Thêm font styles cho keywords và comments - if (className === 'pl-c' || className === 'pl-c1') { - el.style.fontStyle = 'italic'; - } - if (className === 'pl-k' || className === 'pl-kos') { - el.style.fontWeight = 'bold'; - } - }); - }); - } - - // Function chính để apply styles - function applyCodeViewStyles() { - console.log('GitHub Gruvbox: Applying styles'); - - // Mark và style tất cả code elements - const codeSelectors = [ - '.react-code-file-contents', - '.react-code-lines', - '.react-code-line-contents-no-virtualization', - '.react-file-line', - '.react-code-text', - '[data-line-number]', - '.react-line-number', - '.blob-wrapper', - '.blob-code', - '.blob-code-inner', - '.blob-num' - ]; - - codeSelectors.forEach(selector => { - const elements = document.querySelectorAll(selector); - elements.forEach(element => { - if (!element.hasAttribute('data-gruvbox-styled')) { - element.setAttribute('data-gruvbox-styled', 'true'); - } - }); - }); - - // Apply alignment fixes - setTimeout(fixCodeAlignment, 0); - - // Force reflow - requestAnimationFrame(() => { - fixCodeAlignment(); - applySyntaxHighlighting(); - }); - } - - // Function để apply general styles - function applyGeneralStyles() { - document.body.style.backgroundColor = gruvbox.bg0; - document.body.style.color = gruvbox.fg; - - // Apply to page-responsive - const pageResp = document.querySelector('.page-responsive'); - if (pageResp) { - pageResp.style.backgroundColor = gruvbox.bg0; - } - } - - // Khởi động theme - function initializeTheme() { - console.log('GitHub Gruvbox: Initializing with alignment fixes'); - - // Bước 1: Ẩn unstyled code ngay lập tức - const hideUnstyled = () => { - const unstyledSelectors = [ - '.react-code-file-contents:not([data-gruvbox-styled])', - '.react-code-lines:not([data-gruvbox-styled])', - '.blob-wrapper:not([data-gruvbox-styled])' - ]; - - unstyledSelectors.forEach(selector => { - const elements = document.querySelectorAll(selector); - elements.forEach(el => { - el.style.opacity = '0'; - el.style.visibility = 'hidden'; - el.style.display = 'none'; - }); - }); - }; - - // Bước 2: Apply styles và alignment - const applyAllStyles = () => { - applyGeneralStyles(); - applyCodeViewStyles(); - }; - - // Execute - hideUnstyled(); - setTimeout(applyAllStyles, 10); - - // Listeners - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - hideUnstyled(); - setTimeout(applyAllStyles, 10); - }); - } - - window.addEventListener('load', () => { - setTimeout(() => { - hideUnstyled(); - applyAllStyles(); - }, 100); - }); - } - - // Start - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeTheme); - } else { - initializeTheme(); - } +(function () { + 'use strict'; + + const c = { + // Backgrounds + bg0_h: "#1d2021", + bg0: "#282828", + bg1: "#3c3836", + bg2: "#504945", + bg3: "#665c54", + bg4: "#7c6f64", - // Mutation observer cho alignment fixes - const alignmentObserver = new MutationObserver((mutations) => { - let needsAlignmentFix = false; - - mutations.forEach((mutation) => { - if (mutation.addedNodes.length > 0) { - mutation.addedNodes.forEach(node => { - if (node.nodeType === 1) { - // Check for code elements - if (node.matches && ( - node.matches('.react-code-file-contents, .react-code-lines, .blob-wrapper') || - node.querySelector('.react-code-file-contents, .react-code-lines, .blob-wrapper') - )) { - needsAlignmentFix = true; - - // Hide unstyled immediately - if (node.matches && !node.hasAttribute('data-gruvbox-styled')) { - node.style.opacity = '0'; - node.style.visibility = 'hidden'; - node.style.display = 'none'; - } - } - } - }); - } - - // Also check for style changes that might affect alignment - if (mutation.type === 'attributes' && mutation.attributeName === 'style') { - const target = mutation.target; - if (target.hasAttribute('data-gruvbox-styled')) { - needsAlignmentFix = true; - } - } - }); - - if (needsAlignmentFix) { - // Apply fixes with proper timing - setTimeout(() => { - applyCodeViewStyles(); - fixCodeAlignment(); - }, 0); - - requestAnimationFrame(() => { - fixCodeAlignment(); - }); - } - }); + // Foreground + fg: "#ebdbb2", + fg0: "#fbf1c7", + fg1: "#ebdbb2", + fg2: "#d5c4a1", + fg3: "#bdae93", + fg4: "#a89984", - // Start observing - setTimeout(() => { - alignmentObserver.observe(document.body, { - childList: true, - subtree: true, - attributes: true, - attributeFilter: ['style', 'class'] - }); - console.log('GitHub Gruvbox: Alignment observer started'); - }, 1500); + // Colors + red: "#fb4934", + green: "#b8bb26", + yellow: "#fabd2f", + blue: "#83a598", + purple: "#d3869b", + aqua: "#8ec07c", + orange: "#fe8019", + gray: "#928374", - // SPA navigation detection - let lastUrl = location.href; - setInterval(() => { - if (lastUrl !== location.href) { - lastUrl = location.href; - console.log('GitHub Gruvbox: Page changed, reapplying alignment'); - - setTimeout(() => { - // Re-hide unstyled - document.querySelectorAll( - '.react-code-file-contents:not([data-gruvbox-styled]), ' + - '.react-code-lines:not([data-gruvbox-styled]), ' + - '.blob-wrapper:not([data-gruvbox-styled])' - ).forEach(el => { - el.style.opacity = '0'; - el.style.visibility = 'hidden'; - el.style.display = 'none'; - }); - - // Reapply - applyCodeViewStyles(); - setTimeout(fixCodeAlignment, 50); - }, 200); - } - }, 1000); + // Bright variants + bright_red: "#cc241d", + bright_green: "#98971a", + bright_yellow: "#d79921", + bright_blue: "#458588", + bright_purple: "#b16286", + bright_aqua: "#689d6a", + bright_orange: "#d65d0e", - console.log('GitHub Gruvbox Theme loaded with perfect alignment!'); + // UI Specific + selection: "#45858855", + border: "#3c3836", + }; + + GM_addStyle(` +/* ================= GLOBAL ================= */ +html, body { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +/* Page responsive - từ homepage JSON */ +.page-responsive { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +.logged-in { + background: ${c.bg0} !important; +} + +.env-production { + background: ${c.bg0} !important; +} + +/* Application main container */ +.application-main, +main { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +/* Repository content */ +.repository-content { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +/* Dashboard */ +.dashboard, +.dashboard-route { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +/* ================= HEADER ================= */ +.AppHeader, +header, +[data-testid="AppHeader"] { + background: ${c.bg0} !important; + border-bottom: 1px solid ${c.bg2} !important; + color: ${c.fg} !important; +} + +.AppHeader-globalBar { + background: ${c.bg0} !important; +} + +.AppHeader-context { + background: ${c.bg0} !important; +} + +.AppHeader-logo { + filter: invert(0.9) hue-rotate(180deg) brightness(1.2) saturate(0.8) !important; +} + +/* ================= BUTTONS ================= */ +.AppHeader-button, +.Button, +.btn { + background: ${c.bg2} !important; + color: ${c.fg} !important; + border: 1px solid ${c.bg3} !important; + border-radius: 6px !important; +} + +.AppHeader-button:hover, +.Button:hover, +.btn:hover { + background: ${c.bg3} !important; + border-color: ${c.bg4} !important; +} + +.Button--primary, +.btn-primary { + background: ${c.blue} !important; + color: ${c.bg0} !important; + border-color: ${c.blue} !important; +} + +.Button--secondary, +.btn-secondary { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; +} + +.Button--invisible, +.btn-invisible { + background: transparent !important; + color: ${c.fg} !important; + border-color: transparent !important; +} + +/* ================= SEARCH ================= */ +.AppHeader-search, +.search-input, +.form-control { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border: 2px solid ${c.bg3} !important; + border-radius: 6px !important; +} + +.AppHeader-search:focus, +.search-input:focus { + border-color: ${c.blue} !important; + box-shadow: 0 0 0 3px ${c.blue}33 !important; +} + +/* ================= BOX COMPONENTS ================= */ +.Box { + background: ${c.bg1} !important; + border: 1px solid ${c.bg2} !important; + color: ${c.fg} !important; + border-radius: 6px !important; +} + +.Box-body { + background: ${c.bg1} !important; + color: ${c.fg} !important; +} + +.Box-header { + background: ${c.bg0_h} !important; + border-bottom: 1px solid ${c.bg2} !important; + color: ${c.fg} !important; +} + +.Box-title { + color: ${c.fg} !important; +} + +.Box--overlay { + background: ${c.bg1} !important; + border-color: ${c.bg3} !important; + box-shadow: 0 16px 48px rgba(0,0,0,0.4) !important; +} + +.Box-btn-octicon { + background: transparent !important; + color: ${c.fg4} !important; +} + +.Box-btn-octicon:hover { + color: ${c.blue} !important; + background: ${c.bg2} !important; +} + +/* ================= FEED CONTENT ================= */ +.feed-content { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border: 1px solid ${c.bg2} !important; + border-radius: 8px !important; + padding: 16px !important; +} + +.feed-content:hover { + background: ${c.bg2} !important; + border-color: ${c.bg3} !important; + box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; +} + +.feed-item-content { + background: ${c.bg1} !important; + border: 1px solid ${c.bg2} !important; + border-radius: 8px !important; +} + +.feed-item-content:hover { + background: ${c.bg2} !important; + border-color: ${c.bg3} !important; +} + +.feed-background { + background: ${c.bg0} !important; +} + +.feed-main { + background: ${c.bg0} !important; +} + +.feed-next { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +.feed-left-sidebar { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +.feed-right-sidebar { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +/* ================= CODE VIEW ================= */ + +/* Main code container */ +.react-code-file-contents, +.blob-wrapper { + background: ${c.bg0_h} !important; + color: ${c.fg} !important; + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace !important; + font-size: 12px !important; + line-height: 20px !important; + border: 1px solid ${c.bg2} !important; + border-radius: 6px !important; + overflow: auto !important; +} + +/* Line numbers */ +.blob-num, +.react-line-number, +[data-line-number] { + background: ${c.bg0} !important; + color: ${c.fg3} !important; + border-right: 1px solid ${c.bg2} !important; + user-select: none !important; + -webkit-user-select: none !important; +} + +/* Code text */ +.blob-code, +.blob-code-inner, +.react-code-text, +.react-file-line { + background: ${c.bg0_h} !important; + color: ${c.fg} !important; + white-space: pre !important; + font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace !important; + font-size: 12px !important; + line-height: 20px !important; +} + +/* ================= SYNTAX ================= */ + +/* comments */ +.pl-c, +.pl-c1 { + color: ${c.gray} !important; + font-style: italic !important; +} + +/* keywords */ +.pl-k, +.pl-kos { + color: ${c.red} !important; + font-weight: 600 !important; +} + +/* strings */ +.pl-s, +.pl-s1, +.pl-pds { + color: ${c.green} !important; +} + +/* variables / names */ +.pl-v, +.pl-en { + color: ${c.blue} !important; +} + +/* functions / entities */ +.pl-e, +.pl-ent { + color: ${c.yellow} !important; +} + +/* types */ +.pl-smi { + color: ${c.purple} !important; +} + +/* ================= LINKS ================= */ +a, +.Link, +.Link--primary { + color: ${c.blue} !important; + text-decoration: none !important; +} + +a:hover, +.Link:hover { + color: ${c.aqua} !important; + text-decoration: underline !important; +} + +.Link--muted { + color: ${c.fg3} !important; +} + +.Link--secondary { + color: ${c.fg2} !important; +} + +/* ================= INPUTS ================= */ +input, +textarea, +select { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border: 1px solid ${c.bg3} !important; + border-radius: 4px !important; + padding: 6px 10px !important; +} + +input:focus, +textarea:focus, +select:focus { + border-color: ${c.blue} !important; + outline: none !important; + box-shadow: 0 0 0 3px ${c.blue}33 !important; +} + +input::placeholder, +textarea::placeholder { + color: ${c.fg4} !important; + opacity: 0.7 !important; +} + +.form-control { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; +} + +.input-contrast { + background: ${c.bg0_h} !important; +} + +.input-block { + width: 100% !important; +} + +.form-actions { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +/* ================= COLOR UTILITIES ================= */ +.color-fg-default { + color: ${c.fg} !important; +} + +.color-fg-muted { + color: ${c.fg3} !important; +} + +.color-fg-subtle { + color: ${c.fg4} !important; +} + +.color-fg-success { + color: ${c.green} !important; +} + +.color-fg-on-emphasis { + color: ${c.bg0} !important; +} + +.color-bg-default { + background: ${c.bg0} !important; +} + +.color-bg-subtle { + background: ${c.bg1} !important; +} + +.color-bg-accent-emphasis { + background: ${c.blue} !important; + color: ${c.bg0} !important; +} + +.color-bg-transparent { + background: transparent !important; +} + +.color-border-default { + border-color: ${c.bg3} !important; +} + +.color-border-muted { + border-color: ${c.bg2} !important; +} + +.color-border-subtle { + border-color: ${c.bg1} !important; +} + +/* ================= TYPOGRAPHY ================= */ +.lh-condensed { + line-height: 1.25 !important; + color: ${c.fg} !important; +} + +.lh-default { + line-height: 1.5 !important; + color: ${c.fg} !important; +} + +.lh-0 { + line-height: 0 !important; +} + +.text-bold { + font-weight: bold !important; + color: ${c.fg0} !important; +} + +.text-normal { + font-weight: normal !important; + color: ${c.fg} !important; +} + +.text-small { + font-size: 12px !important; + color: ${c.fg3} !important; +} + +.text-left { + text-align: left !important; +} + +/* ================= AVATARS ================= */ +.avatar, +.avatar-user, +.avatar-small { + border: 2px solid ${c.bg3} !important; + background: ${c.bg1} !important; +} + +.avatar:hover { + border-color: ${c.blue} !important; + box-shadow: 0 0 0 3px ${c.blue}33 !important; +} + +/* ================= ICONS ================= */ +.octicon { + color: ${c.fg4} !important; + fill: ${c.fg4} !important; +} + +.octicon:hover { + color: ${c.blue} !important; + fill: ${c.blue} !important; +} + +.octicon-star, +.octicon-star-fill { + color: ${c.yellow} !important; + fill: ${c.yellow} !important; +} + +.octicon-repo { + color: ${c.purple} !important; +} + +.octicon-git-pull-request { + color: ${c.green} !important; +} + +.octicon-issue-opened { + color: ${c.red} !important; +} + +.octicon-search { + color: ${c.fg4} !important; +} + +.octicon-home { + color: ${c.blue} !important; +} + +.octicon-mark-github { + filter: invert(0.9) hue-rotate(180deg) brightness(1.2) saturate(0.8) !important; +} + +/* ================= UNDERLINE NAV ================= */ +.UnderlineNav { + background: ${c.bg0} !important; + border-bottom-color: ${c.bg2} !important; +} + +.UnderlineNav-item { + color: ${c.fg} !important; +} + +.UnderlineNav-item.selected { + border-bottom-color: ${c.blue} !important; + color: ${c.blue} !important; +} + +/* ================= ACTION LISTS ================= */ +.ActionList, +.SelectMenu-list, +.dropdown-menu { + background: ${c.bg1} !important; + border: 1px solid ${c.bg3} !important; + border-radius: 6px !important; + box-shadow: 0 8px 24px rgba(0,0,0,0.3) !important; +} + +.ActionListItem, +.SelectMenu-item, +.dropdown-item { + background: ${c.bg1} !important; + color: ${c.fg} !important; + padding: 8px 12px !important; +} + +.ActionListItem:hover, +.SelectMenu-item:hover, +.dropdown-item:hover { + background: ${c.bg2} !important; +} + +.ActionListItem-label { + color: ${c.fg} !important; +} + +.ActionListContent { + color: ${c.fg} !important; +} + +.ActionListWrap { + background: ${c.bg1} !important; +} + +.ActionListWrap--inset { + background: ${c.bg2} !important; +} + +/* ================= OVERLAYS ================= */ +.Overlay, +.Overlay-body, +.Overlay-header, +.Overlay-footer { + background: ${c.bg1} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; +} + +.Overlay-title { + color: ${c.fg0} !important; +} + +.Overlay-description { + color: ${c.fg2} !important; +} + +.Overlay-closeButton { + color: ${c.fg4} !important; + background: transparent !important; +} + +.Overlay-closeButton:hover { + color: ${c.fg} !important; + background: ${c.bg2} !important; +} + +/* ================= MARKDOWN ================= */ +.markdown-body { + background: ${c.bg0} !important; + color: ${c.fg} !important; +} + +.markdown-body pre, +.markdown-body code { + background: ${c.bg0_h} !important; + color: ${c.fg1} !important; + border-color: ${c.bg2} !important; +} + +.markdown-title { + color: ${c.fg0} !important; + font-weight: bold !important; +} + +.comment-body { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; + color: ${c.fg} !important; +} + +/* ================= FLASH MESSAGES ================= */ +.flash, +.flash-container { + background: ${c.bg2} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; + border-radius: 6px !important; +} + +.flash-full { + background: ${c.bg1} !important; +} + +.flash-close { + color: ${c.fg4} !important; +} + +.flash-close:hover { + color: ${c.fg} !important; +} + +/* ================= PROGRESS BARS ================= */ +.Progress, +.progress-pjax-loader { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +.Progress-item, +.progress-pjax-loader-bar { + background: ${c.blue} !important; +} + +/* ================= FOOTER ================= */ +footer, +.footer { + background: ${c.bg0_h} !important; + color: ${c.fg3} !important; + border-top: 1px solid ${c.bg2} !important; +} + +/* ================= SCROLLBARS ================= */ +::-webkit-scrollbar { + width: 12px !important; + height: 12px !important; + background: ${c.bg1} !important; +} + +::-webkit-scrollbar-thumb { + background: ${c.bg3} !important; + border-radius: 6px !important; + border: 2px solid ${c.bg1} !important; +} + +::-webkit-scrollbar-thumb:hover { + background: ${c.bg4} !important; +} + +::-webkit-scrollbar-track { + background: ${c.bg1} !important; + border-radius: 6px !important; +} + +/* ================= SELECTION ================= */ +::selection { + background: ${c.selection} !important; + color: ${c.fg0} !important; +} + +/* ================= FOCUS STATES ================= */ +:focus, +:focus-visible { + outline: 2px solid ${c.blue} !important; + outline-offset: 2px !important; + border-radius: 4px !important; +} + +/* ================= NO ANIMATION ================= */ +* { + transition: none !important; + animation: none !important; +} + +/* ================= RESPONSIVE ================= */ +@media (max-width: 768px) { + .dashboard-sidebar, + .feed-left-sidebar, + .feed-right-sidebar { + background: ${c.bg0} !important; + } + + .feed-item-content { + padding: 12px !important; + margin-bottom: 12px !important; + } + + .feed-content { + padding: 12px !important; + } +} + +/* ================= BADGES ================= */ +.private, +.public { + background: ${c.bg2} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; +} + +.private { + background: ${c.red}22 !important; + color: ${c.red} !important; +} + +.public { + background: ${c.green}22 !important; + color: ${c.green} !important; +} + +/* ================= REACTIONS ================= */ +.reactions-container, +.social-reactions { + background: ${c.bg1} !important; + border-color: ${c.bg2} !important; +} + +.reaction-dropdown-button { + background: ${c.bg2} !important; + color: ${c.fg} !important; + border-color: ${c.bg3} !important; +} +.reaction-dropdown-button:hover { + background: ${c.bg3} !important; +} +`); })();