From ff1467b45a9725ef9843c7b7a61cbfd642143d8f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 16:38:44 +0000
Subject: [PATCH 1/4] Initial plan
From 39bdf6e46ebb4ccf51f49d0227226ffe9e614dca Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 16:41:24 +0000
Subject: [PATCH 2/4] Add Chrome extension structure with PR link copy and
Copilot review features
Co-authored-by: devm33 <1682753+devm33@users.noreply.github.com>
---
README.md | 77 +++++++++++++++++-
content.js | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++
icon.svg | 4 +
icon128.png | Bin 0 -> 413 bytes
icon16.png | Bin 0 -> 109 bytes
icon48.png | Bin 0 -> 184 bytes
manifest.json | 21 +++++
7 files changed, 312 insertions(+), 2 deletions(-)
create mode 100644 content.js
create mode 100644 icon.svg
create mode 100644 icon128.png
create mode 100644 icon16.png
create mode 100644 icon48.png
create mode 100644 manifest.json
diff --git a/README.md b/README.md
index 1b35689..7e89323 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,75 @@
-# github-ui-mods
-set of small mods for the github ui
+# GitHub UI Mods
+
+A Chrome extension that adds useful modifications to the GitHub pull request interface.
+
+## Features
+
+### 1. Copy PR Link Button
+Adds a button next to the PR title that copies the PR title and link to your clipboard in the format: `PR Title: https://link-to-pr`
+
+### 2. Request Copilot Review Button (Draft PRs)
+When viewing a draft PR, adds a "Request Review" button next to the Copilot reviewer link (`/apps/copilot-pull-request-reviewer`) to easily request a review while the PR is still in draft status.
+
+## Installation
+
+### Load as Unpacked Extension (For Development/Testing)
+
+1. Clone or download this repository:
+ ```bash
+ git clone https://github.com/devm33/github-ui-mods.git
+ cd github-ui-mods
+ ```
+
+2. Open Chrome and navigate to `chrome://extensions/`
+
+3. Enable "Developer mode" by toggling the switch in the top-right corner
+
+4. Click "Load unpacked" button
+
+5. Select the `github-ui-mods` directory (the one containing `manifest.json`)
+
+6. The extension should now be installed and active!
+
+### Verify Installation
+
+Navigate to any GitHub pull request page. You should see:
+- A "Copy PR Link" button next to the PR title
+- (If it's a draft PR) A "Request Review" button next to the Copilot reviewer
+
+## Usage
+
+### Copy PR Link
+1. Navigate to any GitHub pull request
+2. Click the "Copy PR Link" button next to the PR title
+3. The PR title and URL will be copied to your clipboard
+4. The button will show "Copied!" briefly to confirm
+
+### Request Copilot Review (Draft PRs)
+1. Navigate to a GitHub pull request that is in draft status
+2. Find the "Request Review" button next to the Copilot reviewer link
+3. Click the button to request a review from Copilot
+4. The button will show "Requested!" briefly to confirm
+
+## Development
+
+The extension consists of:
+- `manifest.json` - Extension configuration
+- `content.js` - Content script that modifies GitHub PR pages
+- `icon*.png` - Extension icons in various sizes
+
+To modify the extension:
+1. Edit the files as needed
+2. Go to `chrome://extensions/`
+3. Click the refresh icon on the extension card
+4. Refresh any GitHub PR pages to see your changes
+
+## Browser Compatibility
+
+This extension is built for Chrome (Manifest V3) but should also work in:
+- Microsoft Edge
+- Brave
+- Other Chromium-based browsers
+
+## License
+
+See LICENSE file for details.
diff --git a/content.js b/content.js
new file mode 100644
index 0000000..db0002e
--- /dev/null
+++ b/content.js
@@ -0,0 +1,212 @@
+// GitHub UI Mods - Content Script
+// Adds custom buttons to GitHub PR pages
+
+(function() {
+ 'use strict';
+
+ // Feature 1: Add "Copy PR Link" button next to PR title
+ function addCopyPRLinkButton() {
+ // Find the PR title element
+ const titleElement = document.querySelector('.js-issue-title');
+ if (!titleElement) {
+ return false;
+ }
+
+ // Check if button already exists
+ if (document.getElementById('copy-pr-link-btn')) {
+ return true;
+ }
+
+ // Create the button
+ const button = document.createElement('button');
+ button.id = 'copy-pr-link-btn';
+ button.className = 'btn btn-sm';
+ button.style.marginLeft = '8px';
+ button.style.verticalAlign = 'middle';
+ button.innerHTML = `
+
+ Copy PR Link
+ `;
+ button.title = 'Copy PR title and link to clipboard';
+
+ // Add click handler
+ button.addEventListener('click', async function(e) {
+ e.preventDefault();
+
+ // Get PR title and URL
+ const prTitle = titleElement.textContent.trim();
+ const prUrl = window.location.href;
+ const textToCopy = `${prTitle}: ${prUrl}`;
+
+ // Copy to clipboard
+ try {
+ await navigator.clipboard.writeText(textToCopy);
+
+ // Visual feedback
+ const originalText = button.innerHTML;
+ button.innerHTML = `
+
+ Copied!
+ `;
+ button.style.color = '#1a7f37';
+
+ setTimeout(() => {
+ button.innerHTML = originalText;
+ button.style.color = '';
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy:', err);
+ button.textContent = 'Failed to copy';
+ setTimeout(() => {
+ button.innerHTML = originalText;
+ }, 2000);
+ }
+ });
+
+ // Insert button next to title
+ const titleContainer = titleElement.parentElement;
+ if (titleContainer) {
+ titleContainer.style.display = 'flex';
+ titleContainer.style.alignItems = 'center';
+ titleElement.after(button);
+ return true;
+ }
+
+ return false;
+ }
+
+ // Feature 2: Add "Request Review" button next to Copilot reviewer for draft PRs
+ function addCopilotReviewButton() {
+ // Check if this is a draft PR
+ const draftBadge = document.querySelector('.State[title="Status: Draft"]');
+ if (!draftBadge) {
+ // Not a draft PR, no need to add the button
+ return false;
+ }
+
+ // Find the Copilot reviewer link
+ const copilotLink = document.querySelector('a[href="/apps/copilot-pull-request-reviewer"]');
+ if (!copilotLink) {
+ return false;
+ }
+
+ // Check if button already exists
+ if (document.getElementById('copilot-request-review-btn')) {
+ return true;
+ }
+
+ // Create the button
+ const button = document.createElement('button');
+ button.id = 'copilot-request-review-btn';
+ button.className = 'btn btn-sm';
+ button.style.marginLeft = '8px';
+ button.innerHTML = `
+
+ Request Review
+ `;
+ button.title = 'Request Copilot review for draft PR';
+
+ // Add click handler
+ button.addEventListener('click', async function(e) {
+ e.preventDefault();
+
+ // Find the review request form/button
+ // GitHub's UI typically has a button or action to request reviews
+ // We'll try to find and click it programmatically
+
+ // Try to find the "Reviewers" section and expand it if needed
+ const reviewersSection = document.querySelector('.discussion-sidebar-item.js-discussion-sidebar-item');
+ if (reviewersSection) {
+ const requestReviewBtn = reviewersSection.querySelector('button[aria-label*="request"]');
+ if (requestReviewBtn) {
+ requestReviewBtn.click();
+
+ // Wait a bit for the modal/dropdown to appear
+ setTimeout(() => {
+ // Try to find and click on Copilot in the reviewer list
+ const copilotReviewerOption = document.querySelector('[data-filterable-for*="copilot"]');
+ if (copilotReviewerOption) {
+ copilotReviewerOption.click();
+ }
+ }, 100);
+
+ // Visual feedback
+ const originalText = button.innerHTML;
+ button.innerHTML = `
+
+ Requested!
+ `;
+ button.style.color = '#1a7f37';
+
+ setTimeout(() => {
+ button.innerHTML = originalText;
+ button.style.color = '';
+ }, 2000);
+ } else {
+ // Fallback: provide feedback that manual action is needed
+ alert('Please request the review manually from the Reviewers section');
+ }
+ } else {
+ alert('Could not find Reviewers section. Please request the review manually.');
+ }
+ });
+
+ // Insert button next to Copilot link
+ const copilotContainer = copilotLink.parentElement;
+ if (copilotContainer) {
+ copilotLink.after(button);
+ return true;
+ }
+
+ return false;
+ }
+
+ // Initialize the modifications
+ function init() {
+ // Try to add features immediately
+ addCopyPRLinkButton();
+ addCopilotReviewButton();
+
+ // Also observe DOM changes in case elements load dynamically
+ const observer = new MutationObserver(function(mutations) {
+ addCopyPRLinkButton();
+ addCopilotReviewButton();
+ });
+
+ // Observe the main container for changes
+ const container = document.querySelector('#partial-discussion-header') || document.body;
+ observer.observe(container, {
+ childList: true,
+ subtree: true
+ });
+
+ // Also check periodically in case of dynamic loading
+ const checkInterval = setInterval(() => {
+ const btn1Added = addCopyPRLinkButton();
+ const btn2Added = addCopilotReviewButton();
+
+ // If both buttons are added or not needed, we can stop checking so frequently
+ if (btn1Added) {
+ clearInterval(checkInterval);
+ }
+ }, 1000);
+
+ // Clear interval after 10 seconds regardless
+ setTimeout(() => clearInterval(checkInterval), 10000);
+ }
+
+ // Wait for DOM to be ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+ } else {
+ init();
+ }
+})();
diff --git a/icon.svg b/icon.svg
new file mode 100644
index 0000000..c84d9df
--- /dev/null
+++ b/icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/icon128.png b/icon128.png
new file mode 100644
index 0000000000000000000000000000000000000000..72297ae8367c3382803b86b240d061d62737a668
GIT binary patch
literal 413
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRh7^U`+9JaSW-L^Y*$SUz3A?L!hBd
zYa18$diSSabu1>et`!maCG>Os9o~~F_LDapHqoDM5pH|nrYM6pLm0yvh6u(DObL(g
z9j(1vXL0O*wOyY4`P(pI)&pD(q71id+27p!J^$?Oi{FE#fqW!=5Cub0;PFNNcdx&-
w8lGFt3-%@46N$6LZ4a;9$_Ne^mmdKI;Vst0HHN|WB>pF
literal 0
HcmV?d00001
diff --git a/icon16.png b/icon16.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8a3d44bf3e9f3a6ea5c6027df23943b04148d04
GIT binary patch
literal 109
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|CY~;iAr*6yLoQxPS76;>U|=xg
zN}NK_*O_U|Viw&CvO0G9sO(G+Im~g1!Poo9a(lO+$>t0xtQ_+epTAK8)X(7Q>gTe~
HDWM4f<#GC718?UX{;VA
Date: Sun, 25 Jan 2026 16:43:27 +0000
Subject: [PATCH 3/4] Improve error handling and user feedback in content
script
Co-authored-by: devm33 <1682753+devm33@users.noreply.github.com>
---
TESTING.md | 152 +++++++++++++++++++++++++++++++++++++++
content.js | 60 +++++++++++++---
test.html | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 411 insertions(+), 9 deletions(-)
create mode 100644 TESTING.md
create mode 100644 test.html
diff --git a/TESTING.md b/TESTING.md
new file mode 100644
index 0000000..7fa6bdf
--- /dev/null
+++ b/TESTING.md
@@ -0,0 +1,152 @@
+# Testing & Verification Guide
+
+## Extension Verification Checklist
+
+### Installation Verification
+- [ ] Extension loads without errors in Chrome
+- [ ] Extension icon appears in Chrome toolbar
+- [ ] No errors in Chrome DevTools console
+- [ ] Extension shows as active in `chrome://extensions/`
+
+### Feature 1: Copy PR Link Button
+**Test Scenarios:**
+
+1. **Regular PR (Open)**
+ - Navigate to: Any open GitHub pull request
+ - Expected: "Copy PR Link" button appears next to PR title
+ - Action: Click the button
+ - Expected:
+ - Text copied to clipboard in format: `{PR Title}: {PR URL}`
+ - Button shows "Copied!" with green checkmark
+ - Button returns to normal state after 2 seconds
+
+2. **Draft PR**
+ - Navigate to: Any draft GitHub pull request
+ - Expected: Same behavior as regular PR for Feature 1
+
+3. **Closed/Merged PR**
+ - Navigate to: A closed or merged pull request
+ - Expected: Same behavior - button should still work
+
+### Feature 2: Request Copilot Review Button
+**Test Scenarios:**
+
+1. **Draft PR with Copilot Available**
+ - Navigate to: A draft pull request where Copilot is available
+ - Expected: "Request Review" button appears next to Copilot reviewer link
+ - Action: Click the button
+ - Expected:
+ - Attempts to open reviewer selection UI
+ - Button shows "Requested!" with green checkmark
+ - Button returns to normal state after 2 seconds
+
+2. **Non-Draft PR**
+ - Navigate to: An open (non-draft) pull request
+ - Expected: "Request Review" button does NOT appear
+ - Reason: Feature only activates on draft PRs
+
+3. **Draft PR without Copilot**
+ - Navigate to: A draft PR where Copilot is not listed as a reviewer option
+ - Expected: Button does not appear (no Copilot link to attach to)
+
+### Cross-Browser Testing
+- [ ] Chrome (primary target)
+- [ ] Microsoft Edge (Chromium-based)
+- [ ] Brave (Chromium-based)
+
+### Edge Cases
+- [ ] Button positioning is correct and doesn't break layout
+- [ ] Buttons don't duplicate on dynamic page updates
+- [ ] Extension works with GitHub's dark and light themes
+- [ ] Extension works on private repositories
+- [ ] Extension works on organization repositories
+- [ ] Copy function works correctly with special characters in PR title
+- [ ] Extension doesn't interfere with other GitHub functionality
+
+## Manual Test Results
+
+### Test Environment
+- Browser: _____________
+- Browser Version: _____________
+- Date Tested: _____________
+- Tester: _____________
+
+### Feature 1 Results
+| Test Case | Status | Notes |
+|-----------|--------|-------|
+| Button appears | ⬜ Pass / ⬜ Fail | |
+| Copy to clipboard works | ⬜ Pass / ⬜ Fail | |
+| Format is correct | ⬜ Pass / ⬜ Fail | |
+| Visual feedback works | ⬜ Pass / ⬜ Fail | |
+
+### Feature 2 Results
+| Test Case | Status | Notes |
+|-----------|--------|-------|
+| Button appears on draft PR | ⬜ Pass / ⬜ Fail | |
+| Button hidden on non-draft PR | ⬜ Pass / ⬜ Fail | |
+| Click action works | ⬜ Pass / ⬜ Fail | |
+| Visual feedback works | ⬜ Pass / ⬜ Fail | |
+
+## Troubleshooting
+
+### Extension not loading
+1. Check that Developer mode is enabled in `chrome://extensions/`
+2. Verify all files (manifest.json, content.js, icons) are present
+3. Check for errors in the Extensions page
+4. Try removing and re-adding the extension
+
+### Buttons not appearing
+1. Open DevTools console (F12) and check for JavaScript errors
+2. Verify you're on a GitHub pull request page (URL: github.com/*/*/pull/*)
+3. Refresh the page
+4. Check if GitHub has changed their DOM structure (selectors might need updating)
+
+### Copy to clipboard not working
+1. Check browser permissions for clipboard access
+2. Try on a different GitHub PR page
+3. Check DevTools console for errors
+4. Ensure the page is loaded over HTTPS
+
+### Request Review button not working
+1. Verify the PR is actually in draft status
+2. Check that Copilot reviewer is available on the repository
+3. Check DevTools console for errors
+4. May need to manually adjust based on GitHub's current UI structure
+
+## Debugging Tips
+
+### View Extension Console Logs
+1. Go to `chrome://extensions/`
+2. Find "GitHub UI Mods"
+3. Click "Inspect views: background page" or inspect the content script from DevTools
+
+### Inspect Content Script
+1. Open any GitHub PR page
+2. Open DevTools (F12)
+3. Go to Console tab
+4. Look for messages from the extension
+5. Use Sources tab to set breakpoints in content.js
+
+### Test on Local HTML
+1. Open `test.html` in your browser
+2. Verify buttons appear on the simulated GitHub page
+3. This tests the extension logic without needing actual GitHub access
+
+## Known Limitations
+
+1. **Request Review functionality**: This feature attempts to automate clicking through GitHub's UI. If GitHub changes their DOM structure, this may break and require updates to the selectors in content.js.
+
+2. **Dynamic content**: GitHub uses dynamic content loading. The extension uses MutationObserver to handle this, but there may be edge cases where buttons don't appear immediately.
+
+3. **Permissions**: The extension only has access to GitHub pull request pages (for security and performance).
+
+## Reporting Issues
+
+If you encounter issues:
+1. Check the Troubleshooting section above
+2. Document the issue with:
+ - Browser and version
+ - GitHub page URL (without sensitive info)
+ - Steps to reproduce
+ - Console errors (if any)
+ - Screenshots (if applicable)
diff --git a/content.js b/content.js
index db0002e..9e25f83 100644
--- a/content.js
+++ b/content.js
@@ -41,11 +41,12 @@
const textToCopy = `${prTitle}: ${prUrl}`;
// Copy to clipboard
+ // Save original button content before try/catch
+ const originalText = button.innerHTML;
try {
await navigator.clipboard.writeText(textToCopy);
// Visual feedback
- const originalText = button.innerHTML;
button.innerHTML = `