您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Renders Cursor Rules (*.mdc) markdown on GitHub into actual Markdown locally, using the marked library.
当前为
// ==UserScript== // @name Cursor Rule Markdown Renderer for GitHub // @namespace http://tampermonkey.net/ // @version 2025-05-27 // @description Renders Cursor Rules (*.mdc) markdown on GitHub into actual Markdown locally, using the marked library. // @author Texarkanine // @match https://github.com/* // @icon data:image/png;base64,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 // @grant GM_addStyle // @require https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.7/marked.min.js // ==/UserScript== (function() { 'use strict'; // Set to true to enable debug logging const DEBUG = false; // GitHub uses these specific selectors for file content display const CONTENT_SECTION = 'section'; const MARKDOWN_SOURCE = '#read-only-cursor-text-area'; // Only process .mdc files on GitHub const MDC_FILE_REGEX = /^https:\/\/github\.com\/.*\.mdc$/; // Cursor rules have YAML frontmatter that needs special handling const YAML_FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/; // GitHub-compatible markdown styles using GitHub's CSS variables for theme compatibility const MARKDOWN_CSS = ` .markdown-body { box-sizing: border-box; min-width: 200px; max-width: 980px; margin: 0 auto; padding: 45px; background: var(--color-canvas-default, #fff); color: var(--color-fg-default, #24292f); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.5; word-wrap: break-word; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { margin-top: 24px; margin-bottom: 16px; font-weight: 600; line-height: 1.25; } .markdown-body p { margin-top: 0; margin-bottom: 10px; } .markdown-body pre { background-color: #f6f8fa; padding: 16px; overflow: auto; border-radius: 6px; } .markdown-body code { background-color: #f6f8fa; padding: 0.2em 0.4em; border-radius: 6px; font-size: 85%; } .markdown-body blockquote { padding: 0 1em; color: #6a737d; border-left: 0.25em solid #dfe2e5; } .markdown-body ul, .markdown-body ol { padding-left: 2em; } .markdown-body table { border-collapse: collapse; display: block; width: 100%; overflow: auto; } .markdown-body th, .markdown-body td { border: 1px solid #dfe2e5; padding: 6px 13px; } `; let cssInjected = false; /** * Processes .mdc content by wrapping YAML frontmatter in code blocks. * This is necessary because cursor rules use YAML frontmatter that should * be displayed as code rather than parsed as markdown. */ function processContent(rawContent) { const match = rawContent.match(YAML_FRONTMATTER_REGEX); if (match) { const [, yamlContent, markdownContent] = match; return `\`\`\`yaml\n${yamlContent}\n\`\`\`\n\n${markdownContent}`; } return rawContent; } /** * Injects GitHub-compatible markdown styles. * Uses GitHub's CSS variables to match the current theme automatically. */ function injectStyles() { if (cssInjected) return; GM_addStyle(MARKDOWN_CSS); cssInjected = true; } /** * Renders the markdown content by replacing the section content. * Preserves the original textarea as hidden for potential future reference. */ function renderMarkdown() { const section = document.querySelector(CONTENT_SECTION); const textarea = document.querySelector(MARKDOWN_SOURCE); if (!section || !textarea) { return false; } const rawContent = textarea.textContent; if (!rawContent) { return false; } // Process content and render markdown const processedContent = processContent(rawContent); const markdownDiv = document.createElement('div'); markdownDiv.className = 'markdown-body'; markdownDiv.innerHTML = marked.parse(processedContent); // Replace section content while preserving the original textarea section.innerHTML = ''; textarea.style.display = 'none'; // Keep textarea but hide it section.appendChild(textarea); section.appendChild(markdownDiv); injectStyles(); return true; } let contentObserver = null; let lastContent = ''; /** * Sets up observation of the textarea content changes. * This ensures we only render when the actual content changes, not stale content. */ function observeContentChanges() { // Clean up any existing observer if (contentObserver) { contentObserver.disconnect(); contentObserver = null; } const textarea = document.querySelector(MARKDOWN_SOURCE); if (!textarea) { return false; } // Check if content is different from last render const currentContent = textarea.textContent; if (currentContent && currentContent !== lastContent) { lastContent = currentContent; if (renderMarkdown()) { DEBUG && console.log('[mdc-render] Successfully rendered markdown'); } } // Set up observer for future content changes contentObserver = new MutationObserver(() => { const newContent = textarea.textContent; if (newContent && newContent !== lastContent) { lastContent = newContent; if (renderMarkdown()) { DEBUG && console.log('[mdc-render] Content changed, re-rendered markdown'); } } }); // Observe changes to the textarea's text content contentObserver.observe(textarea, { childList: true, subtree: true, characterData: true }); return true; } /** * Waits for the textarea to appear, then sets up content observation. * GitHub loads content asynchronously, so we need to wait for the textarea. */ function waitForTextareaAndObserve() { let attempts = 0; const maxAttempts = 100; // 10 seconds at 100ms intervals const checkInterval = setInterval(() => { attempts++; if (observeContentChanges()) { clearInterval(checkInterval); DEBUG && console.log('[mdc-render] Set up content observation'); } else if (attempts >= maxAttempts) { clearInterval(checkInterval); DEBUG && console.log('[mdc-render] Timeout waiting for textarea'); } }, 100); } /** * Handles URL changes to detect navigation to .mdc files. * GitHub is a SPA, so we need to monitor URL changes via DOM mutations. */ function handleUrlChange() { if (MDC_FILE_REGEX.test(location.href)) { DEBUG && console.log('[mdc-render] MDC file detected:', location.href); waitForTextareaAndObserve(); } } // Initialize: handle current page and set up URL change detection let currentUrl = location.href; handleUrlChange(); // Monitor for URL changes in GitHub's SPA new MutationObserver(() => { if (location.href !== currentUrl) { currentUrl = location.href; handleUrlChange(); } }).observe(document, { subtree: true, childList: true }); })();