您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Remove clutter in the comments view
// ==UserScript== // @name Refined GitHub Comments (TJ) // @license MIT // @homepageURL https://github.com/tjx666/user-scripts // @supportURL https://github.com/tjx666/user-scripts/issues // @namespace https://github.com/tjx666/user-scripts // @version 0.4.3 // @description Remove clutter in the comments view // @author YuTengjing // @match https://github.com/*/issues/* // @match https://github.com/*/pull/* // @match https://github.com/*/discussions/* // @match https://github.com/*/commits/* // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com // @grant none // ==/UserScript== // common bots that i already know what they do const authorsToMinimize = [ 'changeset-bot', 'codeflowapp', 'netlify', // 'vercel', 'pkg-pr-new', 'codecov', 'astrobot-houston', 'codspeed-hq', 'lobehubbot', ]; // common comments that don't really add value const commentMatchToMinimize = [ /^![a-z]/, // commands that start with ! /^\/[a-z]/, // commands that start with / /^> [email protected]/, // astro preview release bot /^👍[\s\S]*Thank you for raising your pull request/, // lobehubbot PR thanks /^👀[\s\S]*Thank you for raising an issue/, // lobehubbot issue thanks /^✅[\s\S]*This issue is closed/, // lobehubbot issue closed /^❤️[\s\S]*Great PR/, // lobehubbot PR merged thanks /Bot detected the issue body's language is not English/, // lobehubbot translation ]; // DOM selectors const SELECTORS = { TIMELINE_ELEMENT: '.LayoutHelpers-module__timelineElement--IsjVR, [data-wrapper-timeline-id]', COMMENT_BODY: '[data-testid="markdown-body"] .markdown-body, .IssueCommentViewer-module__IssueCommentBody--xvkt3 .markdown-body', COMMENT_CONTENT: '.IssueCommentViewer-module__IssueCommentBody--xvkt3, [data-testid="markdown-body"]', COMMENT_HEADER: '[data-testid="comment-header"]', AUTHOR_LINK: '.ActivityHeader-module__AuthorLink--D7Ojk, [data-testid="avatar-link"]', COMMENT_ACTIONS: '[data-testid="comment-header-hamburger"], .CommentActions-module__CommentActionsIconButton--EOXv7', TITLE_CONTAINER: '.ActivityHeader-module__TitleContainer--pa99A', FOOTER_CONTAINER: '.ActivityHeader-module__footer--ssKOW, .ActivityHeader-module__FooterContainer--FHEpM', ACTIONS_CONTAINER: '.ActivityHeader-module__ActionsButtonsContainer--L7GUK', }; // Used by `minimizeDiscussionThread` let expandedThread = false; const maxParentThreadHeight = 185; // Used by `minimizeReactBlockquote` to track seen comments const seenReactComments = []; (function () { 'use strict'; run(); // listen to github page loaded event document.addEventListener('pjax:end', () => run()); document.addEventListener('turbo:render', () => run()); })(); function run() { injectCSS(); setTimeout(() => { // Handle React version comments const reactComments = document.querySelectorAll('.react-issue-comment'); reactComments.forEach((comment) => { minimizeReactComment(comment); minimizeReactBlockquote(comment, seenReactComments); }); // Handle PR comments const timelineItems = document.querySelectorAll('.js-timeline-item'); timelineItems.forEach((timelineItem) => { minimizePRComment(timelineItem); }); // Discussion threads view if (location.pathname.includes('/discussions/')) { minimizeDiscussionThread(); } setupDOMObserver(); }, 1000); } function injectCSS() { // Remove existing style if any const existingStyle = document.getElementById('refined-github-comments-style'); if (existingStyle) { existingStyle.remove(); } const style = document.createElement('style'); style.id = 'refined-github-comments-style'; style.textContent = ` /* Layout for minimized React comments */ .refined-github-comments-minimized .ActivityHeader-module__CommentHeaderContentContainer--OOrIN { display: flex !important; flex-direction: row !important; align-items: center !important; flex-wrap: nowrap !important; gap: 4px !important; flex: 1 !important; } .refined-github-comments-minimized .ActivityHeader-module__FooterContainer--FHEpM { display: flex !important; flex-direction: row !important; align-items: center !important; flex: 1 !important; overflow: hidden !important; } .refined-github-comments-minimized .ActivityHeader-module__narrowViewportWrapper--k4ncm.ActivityHeader-module__ActionsContainer--Ebsux { flex-grow: 0 !important; } .refined-github-comments-minimized .ActivityHeader-module__HeaderMutedText--aJAo0 { flex-shrink: 0 !important; } /* Excerpt text styling */ .refined-github-comments-minimized .ActivityHeader-module__FooterContainer--FHEpM .color-fg-muted, .timeline-comment-header .css-truncate-overflow { white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; display: inline-block !important; } /* Toggle button styling */ .refined-github-comments-toggle.timeline-comment-action { padding: 0 6px !important; margin: 0 !important; } /* Hidden elements */ .refined-github-comments-hidden { display: none !important; } `; document.head.appendChild(style); } function setupDOMObserver() { const observer = new MutationObserver((mutations) => { const hasNewComments = mutations.some( (mutation) => mutation.type === 'childList' && Array.from(mutation.addedNodes).some( (node) => node.nodeType === Node.ELEMENT_NODE && (node.classList?.contains('react-issue-comment') || node.querySelector?.('.react-issue-comment') || node.classList?.contains('js-timeline-item') || node.querySelector?.('.js-timeline-item')), ), ); if (hasNewComments) { setTimeout(() => { // Handle React comments document.querySelectorAll('.react-issue-comment').forEach((comment) => { minimizeReactComment(comment); minimizeReactBlockquote(comment, seenReactComments); }); // Handle PR comments document.querySelectorAll('.js-timeline-item').forEach((timelineItem) => { minimizePRComment(timelineItem); }); }, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); } /** * Extract author name from link * @param {HTMLElement} authorLink * @returns {string} */ function getAuthorName(authorLink) { let authorName = authorLink.getAttribute('href')?.replace('/', '') || authorLink.textContent.trim(); if (authorName.startsWith('apps/')) { authorName = authorName.replace('apps/', ''); } return authorName; } /** * Check if comment should be minimized * @param {string} authorName * @param {string} commentText * @returns {boolean} */ function shouldMinimizeComment(authorName, commentText) { const shouldMinimizeByAuthor = authorsToMinimize.includes(authorName); const matchingPattern = commentMatchToMinimize.find((match) => match.test(commentText)); return shouldMinimizeByAuthor || matchingPattern; } /** * Create comment excerpt element * @param {string} text * @returns {HTMLElement} */ function createExcerpt(text) { const excerpt = document.createElement('span'); excerpt.className = 'css-truncate-overflow text-fg-muted text-italic'; excerpt.style.fontSize = '12px'; excerpt.style.opacity = '0.6'; excerpt.style.whiteSpace = 'nowrap'; excerpt.style.overflow = 'hidden'; excerpt.style.textOverflow = 'ellipsis'; excerpt.style.display = 'inline-block'; excerpt.style.verticalAlign = 'middle'; excerpt.textContent = text; return excerpt; } /** * Setup toggle button styling and placement * @param {HTMLElement} commentActions * @param {HTMLElement} toggleBtn * @param {HTMLElement} beforeElement - Optional element to insert before */ function setupToggleButton(commentActions, toggleBtn, beforeElement = null) { commentActions.style.display = 'flex'; commentActions.style.alignItems = 'center'; commentActions.style.gap = '4px'; if (beforeElement) { commentActions.insertBefore(toggleBtn, beforeElement); } else { commentActions.insertBefore(toggleBtn, commentActions.firstChild); } } /** * Handle PR comments * @param {HTMLElement} timelineItem */ function minimizePRComment(timelineItem) { // Skip if already processed if (timelineItem.querySelector('.refined-github-comments-toggle')) { return; } // Find timeline comment const timelineComment = timelineItem.querySelector('.timeline-comment'); if (!timelineComment) return; // Find comment header const header = timelineComment.querySelector('.timeline-comment-header'); if (!header) return; // Find author in h3 strong a structure const authorLink = header.querySelector('h3 strong .author'); if (!authorLink) return; // Find comment body const commentBody = timelineComment.querySelector( '.comment-body.markdown-body.js-comment-body', ); if (!commentBody) return; const authorName = getAuthorName(authorLink); const commentBodyText = commentBody.innerText.trim(); if (shouldMinimizeComment(authorName, commentBodyText)) { // Find comment actions container const commentActions = header.querySelector('.timeline-comment-actions'); if (!commentActions) return; // Hide comment body content const taskLists = timelineComment.querySelector('task-lists'); if (taskLists) { taskLists.style.display = 'none'; } else { const commentBody = timelineComment.querySelector('.comment-body'); if (commentBody) { commentBody.style.display = 'none'; } } // Remove border bottom from header header.style.borderBottom = 'none'; // Hide mention buttons toggleMentionButtons(timelineItem, false); // Add comment excerpt in header const titleContainer = header.querySelector('h3.f5.text-normal'); if (titleContainer) { // Find the div inside h3 to add excerpt there (keep it on same line) const innerDiv = titleContainer.querySelector('div'); if (innerDiv) { const excerpt = createExcerpt(commentBodyText); innerDiv.parentElement.style.overflow = 'hidden'; innerDiv.style.display = 'flex'; innerDiv.style.alignItems = 'center'; innerDiv.style.gap = '4px'; excerpt.style.flex = '1'; innerDiv.appendChild(excerpt); } // Add toggle button const toggleBtn = toggleComment((isShow) => { const currentTaskLists = timelineComment.querySelector('task-lists'); const currentCommentBody = timelineComment.querySelector('.comment-body'); if (isShow) { if (currentTaskLists) { currentTaskLists.style.display = ''; } else if (currentCommentBody) { currentCommentBody.style.display = ''; } header.style.borderBottom = ''; if (innerDiv && innerDiv.querySelector('.css-truncate-overflow')) { innerDiv.querySelector('.css-truncate-overflow').style.display = 'none'; } toggleMentionButtons(timelineItem, true); } else { if (currentTaskLists) { currentTaskLists.style.display = 'none'; } else if (currentCommentBody) { currentCommentBody.style.display = 'none'; } header.style.borderBottom = 'none'; if (innerDiv && innerDiv.querySelector('.css-truncate-overflow')) { innerDiv.querySelector('.css-truncate-overflow').style.display = ''; } toggleMentionButtons(timelineItem, false); } }); // Style and insert toggle button setupToggleButton(commentActions, toggleBtn); } } } /** * Toggle mention buttons visibility * @param {HTMLElement} element - Can be either a React comment or PR timeline item * @param {boolean} show */ function toggleMentionButtons(element, show) { let mentionContainer = null; // Strategy 1: Find mention container directly within the element (for PR comments) mentionContainer = element.querySelector('.avatar-parent-child'); // Strategy 2: Find via closest timeline element (for React comments) if (!mentionContainer) { const timelineElement = element.closest(SELECTORS.TIMELINE_ELEMENT); if (timelineElement) { mentionContainer = timelineElement.querySelector('.avatar-parent-child'); } } // Strategy 3: For issue comments, try to find timeline element as sibling container if (!mentionContainer) { // Look for timeline element that contains both avatar-parent-child and this element const timelineElements = document.querySelectorAll(SELECTORS.TIMELINE_ELEMENT); for (const timeline of timelineElements) { if (timeline.contains(element) && timeline.querySelector('.avatar-parent-child')) { mentionContainer = timeline.querySelector('.avatar-parent-child'); break; } } } // Strategy 4: Direct search for mention buttons in nearby containers if (!mentionContainer) { // Look for mention buttons in the document that might be related to this comment const commentId = element.querySelector('[data-testid="comment-header"]')?.id; if (commentId) { const timelineWrapper = document.querySelector( `[data-wrapper-timeline-id="${commentId}"]`, ); if (timelineWrapper) { mentionContainer = timelineWrapper.querySelector('.avatar-parent-child'); } } } if (!mentionContainer) return; const mentionBtns = mentionContainer.querySelectorAll('.rgh-quick-mention'); mentionBtns.forEach((btn) => { if (show) { btn.classList.remove('refined-github-comments-hidden'); } else { btn.classList.add('refined-github-comments-hidden'); } }); } /** * Handle React version GitHub comments * @param {HTMLElement} reactComment */ function minimizeReactComment(reactComment) { // Skip if already processed if (reactComment.querySelector('.refined-github-comments-toggle')) { return; } // Find comment header const header = reactComment.querySelector(SELECTORS.COMMENT_HEADER); if (!header) return; // Find author const authorLink = header.querySelector(SELECTORS.AUTHOR_LINK); if (!authorLink) return; // Find comment body const commentBody = reactComment.querySelector(SELECTORS.COMMENT_BODY); if (!commentBody) return; const authorName = getAuthorName(authorLink); const commentBodyText = commentBody.innerText.trim(); if (shouldMinimizeComment(authorName, commentBodyText)) { const commentContent = reactComment.querySelector(SELECTORS.COMMENT_CONTENT); if (!commentContent) return; const commentActions = header.querySelector(SELECTORS.COMMENT_ACTIONS); if (!commentActions) return; const titleContainer = header.querySelector(SELECTORS.TITLE_CONTAINER); if (!titleContainer) return; // Hide comment content commentContent.style.display = 'none'; // Remove border bottom from header header.style.borderBottom = 'none'; // Hide mention buttons toggleMentionButtons(reactComment, false); // Add CSS class for layout styling reactComment.classList.add('refined-github-comments-minimized'); // Add comment excerpt const footerContainer = header.querySelector(SELECTORS.FOOTER_CONTAINER); let excerpt = null; if (footerContainer) { excerpt = document.createElement('span'); excerpt.setAttribute('class', 'color-fg-muted text-italic'); excerpt.innerHTML = commentBodyText; excerpt.style.opacity = '0.5'; excerpt.style.fontSize = '12px'; excerpt.style.marginLeft = '4px'; footerContainer.appendChild(excerpt); } // Add toggle button const toggleBtn = toggleComment((isShow) => { if (isShow) { commentContent.style.display = ''; header.style.borderBottom = ''; if (excerpt) excerpt.style.display = 'none'; toggleMentionButtons(reactComment, true); reactComment.classList.remove('refined-github-comments-minimized'); } else { commentContent.style.display = 'none'; header.style.borderBottom = 'none'; if (excerpt) excerpt.style.display = ''; toggleMentionButtons(reactComment, false); reactComment.classList.add('refined-github-comments-minimized'); } }); // Find actions container and setup toggle button const actionsContainer = header.querySelector(SELECTORS.ACTIONS_CONTAINER); if (!actionsContainer) return; setupToggleButton(actionsContainer, toggleBtn, commentActions); } } /** * Handle blockquotes in React comments (new GitHub structure) * @param {HTMLElement} reactComment * @param {{ text: string, id: string, author: string }[]} seenComments */ function minimizeReactBlockquote(reactComment, seenComments) { const commentBody = reactComment.querySelector('[data-testid="markdown-body"] .markdown-body'); if (!commentBody) return; const commentHeader = reactComment.querySelector('[data-testid="comment-header"]'); if (!commentHeader) return; const commentId = commentHeader.id; // e.g., "issuecomment-1528936387" if (!commentId) return; const authorLink = commentHeader.querySelector('[data-testid="avatar-link"]'); if (!authorLink) return; const commentAuthor = authorLink.textContent.trim(); if (!commentAuthor) return; const commentText = commentBody.innerText.trim().replace(/\s+/g, ' '); // bail early in first comment and if comment is already checked before if ( seenComments.length === 0 || commentBody.querySelector('.refined-github-comments-reply-text') ) { seenComments.push({ text: commentText, id: commentId, author: commentAuthor, }); return; } const blockquotes = commentBody.querySelectorAll(':scope > blockquote'); for (const blockquote of blockquotes) { const blockquoteText = blockquote.innerText.trim().replace(/\s+/g, ' '); const dupIndex = seenComments.findIndex((comment) => comment.text === blockquoteText); if (dupIndex >= 0) { const dup = seenComments[dupIndex]; // if replying to the one above, always minimize it if (dupIndex === seenComments.length - 1) { const summary = `\ <span class="js-clear text-italic refined-github-comments-reply-text"> Replying to <strong>@${dup.author}</strong> above </span> `; blockquote.innerHTML = `<details><summary>${summary}</summary>${blockquote.innerHTML}</details>`; } // if replying to a long comment, or a comment with code, always minimize it else if (blockquoteText.length > 200 || blockquote.querySelector('pre')) { const summary = `\ <span class="js-clear text-italic refined-github-comments-reply-text"> Replying to <strong>@${dup.author}</strong>'s <a href="#${dup.id}">comment</a> </span> `; blockquote.innerHTML = `<details><summary>${summary}</summary>${blockquote.innerHTML}</details>`; } // otherwise, just add a hint so we don't have to navigate away a short sentence else { const hint = `\ <span dir="auto" class="js-clear text-italic refined-github-comments-reply-text" style="display: block; margin-top: -0.5rem; opacity: 0.7; font-size: 90%;"> — <strong>@${dup.author}</strong> said in <a href="#${dup.id}">comment</a> </span>`; blockquote.insertAdjacentHTML('beforeend', hint); } continue; } const partialDupIndex = seenComments.findIndex((comment) => comment.text.includes(blockquoteText), ); if (partialDupIndex >= 0) { const dup = seenComments[partialDupIndex]; // get first four words and last four words, craft a text fragment to highlight const splitted = blockquoteText.split(' '); const textFragment = splitted.length < 9 ? `#:~:text=${encodeURIComponent(blockquoteText)}` : `#:~:text=${encodeURIComponent( splitted.slice(0, 4).join(' '), )},${encodeURIComponent(splitted.slice(-4).join(' '))}`; // if replying to the one above, prepend hint if (partialDupIndex === seenComments.length - 1) { const hint = `\ <span dir="auto" class="js-clear text-italic refined-github-comments-reply-text" style="display: block; margin-top: -0.5rem; opacity: 0.7; font-size: 90%;"> — <strong>@${dup.author}</strong> <a href="${textFragment}">said</a> above </span>`; blockquote.insertAdjacentHTML('beforeend', hint); } // prepend generic hint else { const hint = `\ <span dir="auto" class="js-clear text-italic refined-github-comments-reply-text" style="display: block; margin-top: -0.5rem; opacity: 0.7; font-size: 90%;"> — <strong>@${dup.author}</strong> <a href="${textFragment}">said</a> in <a href="#${dup.id}">comment</a> </span>`; blockquote.insertAdjacentHTML('beforeend', hint); } } } seenComments.push({ text: commentText, id: commentId, author: commentAuthor }); } // test urls: // https://github.com/vitejs/vite/discussions/18191 function minimizeDiscussionThread() { if (expandedThread) { _minimizeDiscussionThread(); return; } // Look for the first main timeline comment in discussions const firstMainComment = document.querySelector( '.timeline-comment:not(.nested-discussion-timeline-comment)', ); if (!firstMainComment) return; const tripleDotMenuContainer = firstMainComment.querySelector('.timeline-comment-actions'); if (!tripleDotMenuContainer) return; // Skip if already added if (document.getElementById('refined-github-comments-expand-btn') != null) return; tripleDotMenuContainer.style.display = 'flex'; tripleDotMenuContainer.style.alignItems = 'center'; // Create a "Collapse threads" button to enable this feature const expandBtn = document.createElement('button'); expandBtn.id = 'refined-github-comments-expand-btn'; expandBtn.setAttribute( 'class', 'Button Button--iconOnly Button--invisible Button--medium mr-2', ); expandBtn.innerHTML = `\ <svg class="Button-visual octicon octicon-zap" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"> <path d="M9.504.43a1.516 1.516 0 0 1 2.437 1.713L10.415 5.5h2.123c1.57 0 2.346 1.909 1.22 3.004l-7.34 7.142a1.249 1.249 0 0 1-.871.354h-.302a1.25 1.25 0 0 1-1.157-1.723L5.633 10.5H3.462c-1.57 0-2.346-1.909-1.22-3.004L9.503.429Zm1.047 1.074L3.286 8.571A.25.25 0 0 0 3.462 9H6.75a.75.75 0 0 1 .694 1.034l-1.713 4.188 6.982-6.793A.25.25 0 0 0 12.538 7H9.25a.75.75 0 0 1-.683-1.06l2.008-4.418.003-.006a.036.036 0 0 0-.004-.009l-.006-.006-.008-.001c-.003 0-.006.002-.009.004Z"></path> </svg> `; expandBtn.title = 'Collapse threads'; expandBtn.addEventListener('click', () => { expandedThread = true; _minimizeDiscussionThread(); expandBtn.remove(); }); tripleDotMenuContainer.prepend(expandBtn); } function _minimizeDiscussionThread() { // Find all main timeline comments (not nested replies) const timelineComments = document.querySelectorAll( '.timeline-comment:not(.nested-discussion-timeline-comment)', ); for (const timelineComment of timelineComments) { // Skip if already handled if (timelineComment.querySelector('.refined-github-comments-toggle')) continue; // Look for child comments container based on comment ID // Find the permalink link which contains the comment ID const commentPermalink = timelineComment.querySelector('a[href^="#discussioncomment-"]'); let childCommentsContainer = null; if (commentPermalink) { const href = commentPermalink.getAttribute('href'); // e.g., "#discussioncomment-10741444" const commentId = href.substring(1); // Remove the # to get "discussioncomment-10741444" childCommentsContainer = document.querySelector(`#child-comments-${commentId}`); } // Fallback: look for any child comments container near this comment if (!childCommentsContainer) { // Check if there's a child comments container right after this comment const parentContainer = timelineComment.closest('.d-flex'); if (parentContainer && parentContainer.parentElement) { const possibleChildContainer = parentContainer.parentElement.querySelector('[data-child-comments]'); if (possibleChildContainer) { childCommentsContainer = possibleChildContainer; } } } // Find replies count text (e.g. "4 replies") const repliesTextElement = timelineComment.querySelector( '.f6.mr-3 .color-fg-muted.no-wrap', ); if ( childCommentsContainer && repliesTextElement && repliesTextElement.textContent.includes('replies') ) { const repliesCount = parseInt( repliesTextElement.textContent.match(/(\d+)\s+replies?/)?.[1] || '0', ); // Skip if 0 replies if (repliesCount > 0) { // Find the parent element to insert toggle button const repliesContainer = repliesTextElement.parentElement; if (repliesContainer) { const toggleBtn = toggleComment((isShow) => { if (isShow) { childCommentsContainer.style.display = ''; repliesTextElement.classList.add('color-fg-muted'); } else { childCommentsContainer.style.display = 'none'; repliesTextElement.classList.remove('color-fg-muted'); } }); repliesContainer.insertBefore(toggleBtn, repliesTextElement); childCommentsContainer.style.display = 'none'; repliesTextElement.classList.remove('color-fg-muted'); // Make replies text clickable too repliesTextElement.style.cursor = 'pointer'; repliesTextElement.addEventListener('click', () => { toggleBtn.click(); }); } } } // Handle long comment bodies const commentBody = timelineComment.querySelector( '.comment-body.markdown-body.js-comment-body', ); if (commentBody && commentBody.clientHeight > maxParentThreadHeight) { // Apply height limit and mask const css = `max-height:${maxParentThreadHeight}px;mask-image:linear-gradient(180deg, #000 80%, transparent);-webkit-mask-image:linear-gradient(180deg, #000 80%, transparent);`; commentBody.style.cssText += css; // Add toggle button for comment body const commentActions = timelineComment.querySelector('.timeline-comment-actions'); if (commentActions) { const toggleCommentBodyBtn = toggleComment((isShow) => { if (isShow) { commentBody.style.maxHeight = ''; commentBody.style.maskImage = ''; commentBody.style.webkitMaskImage = ''; } else { commentBody.style.cssText += css; } }); commentActions.style.display = 'flex'; commentActions.style.alignItems = 'center'; commentActions.prepend(toggleCommentBodyBtn); // Auto-expand on first click for nicer UX commentBody.style.cursor = 'pointer'; commentBody.addEventListener('click', () => { if (toggleCommentBodyBtn.dataset.show === 'false') { toggleCommentBodyBtn.click(); } }); } } } } // create the toggle comment like github does when you hide a comment function toggleComment(onClick) { const btn = document.createElement('button'); // copied from github hidden comment style btn.innerHTML = ` <div class="color-fg-muted f6 no-wrap"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-unfold position-relative"> <path d="m8.177.677 2.896 2.896a.25.25 0 0 1-.177.427H8.75v1.25a.75.75 0 0 1-1.5 0V4H5.104a.25.25 0 0 1-.177-.427L7.823.677a.25.25 0 0 1 .354 0ZM7.25 10.75a.75.75 0 0 1 1.5 0V12h2.146a.25.25 0 0 1 .177.427l-2.896 2.896a.25.25 0 0 1-.354 0l-2.896-2.896A.25.25 0 0 1 5.104 12H7.25v-1.25Zm-5-2a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM6 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 6 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM12 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 12 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5Z"></path> </svg> </div> <div class="color-fg-muted f6 no-wrap" style="display: none"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-fold position-relative"> <path d="M10.896 2H8.75V.75a.75.75 0 0 0-1.5 0V2H5.104a.25.25 0 0 0-.177.427l2.896 2.896a.25.25 0 0 0 .354 0l2.896-2.896A.25.25 0 0 0 10.896 2ZM8.75 15.25a.75.75 0 0 1-1.5 0V14H5.104a.25.25 0 0 1-.177-.427l2.896-2.896a.25.25 0 0 1 .354 0l2.896 2.896a.25.25 0 0 1-.177.427H8.75v1.25Zm-6.5-6.5a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM6 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 6 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5ZM12 8a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 12 8Zm2.25.75a.75.75 0 0 0 0-1.5h-.5a.75.75 0 0 0 0 1.5h.5Z"></path> </svg> </div> `; const showNode = btn.querySelector('div:nth-child(1)'); const hideNode = btn.querySelector('div:nth-child(2)'); let isShow = false; btn.setAttribute('type', 'button'); btn.setAttribute('class', 'refined-github-comments-toggle timeline-comment-action btn-link'); btn.dataset.show = isShow; btn.addEventListener('click', () => { isShow = !isShow; btn.dataset.show = isShow; if (isShow) { showNode.style.display = 'none'; hideNode.style.display = ''; } else { showNode.style.display = ''; hideNode.style.display = 'none'; } onClick(isShow); }); return btn; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址