Skip to content

Instantly share code, notes, and snippets.

@CurtisAccelerate
Last active June 23, 2024 00:41
Show Gist options
  • Save CurtisAccelerate/64a20b1d5df6240119bb0a3f4b5abf31 to your computer and use it in GitHub Desktop.
Save CurtisAccelerate/64a20b1d5df6240119bb0a3f4b5abf31 to your computer and use it in GitHub Desktop.
ChatGPT_Codeblock_Processor
The following is a TamperMonkey script that adds a run eleement to code blocks for HTML content.
- Now works without manual reloading!
- Now works with Javascript. Must disable CSP policy. Disable at own risk.
- Removed the run demo from the frame area
https://chromewebstore.google.com/detail/csp-unblock/lkbelpgpclajeekijigjffllhigbhobd?hl=en
// ==UserScript==
// @name ChatGPT Code Block Processor 200
// @namespace http://tampermonkey.net/
// @version 2.0.1
// @description Add "Run Demo" button as a hover action on code blocks in ChatGPT responses with draggable panel.
// @match https://chatgpt.com/*
// @grant GM_addElement
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
let panel;
let isDragging = false;
let startX;
let startWidth;
function createSlideOutPanel(codeBlock) {
if (panel) {
panel.remove();
}
panel = document.createElement('div');
panel.style.cssText = `
position: fixed;
top: 0;
right: 0;
width: 600px;
height: 100%;
background: #f7f7f8;
box-shadow: -2px 0 5px rgba(0,0,0,0.3);
z-index: 1000;
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.3s ease-in-out;
`;
const header = document.createElement('div');
header.style.cssText = `
padding: 10px;
background: #282c34;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
`;
const closeButton = document.createElement('button');
closeButton.textContent = 'Close';
closeButton.style.cssText = `
background: #ff5f57;
border: none;
border-radius: 5px;
padding: 5px 10px;
cursor: pointer;
color: white;
`;
closeButton.onclick = () => panel.style.transform = 'translateX(100%)';
header.appendChild(closeButton);
panel.appendChild(header);
const contentContainer = document.createElement('div');
contentContainer.style.cssText = `
padding: 10px;
overflow-y: auto;
flex-grow: 1;
`;
const iframe = document.createElement('iframe');
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
margin: 0;
padding: 0;
`;
contentContainer.appendChild(iframe);
panel.appendChild(contentContainer);
document.body.appendChild(panel);
// Clone the code block and remove the "Run Demo" button
const cleanCodeBlock = codeBlock.cloneNode(true);
const runDemoButton = cleanCodeBlock.querySelector('.run-demo-hover-button');
if (runDemoButton) {
runDemoButton.remove();
}
const doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc.write(cleanCodeBlock.textContent);
doc.close();
setTimeout(() => panel.style.transform = 'translateX(0)', 0);
header.addEventListener('mousedown', startDragging);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDragging);
document.addEventListener('click', function(event) {
if (!panel.contains(event.target) && !event.target.closest('.run-demo-hover-button')) {
panel.style.transform = 'translateX(100%)';
}
}, { once: true });
}
function startDragging(e) {
isDragging = true;
startX = e.clientX;
startWidth = parseInt(document.defaultView.getComputedStyle(panel).width, 10);
}
function drag(e) {
if (!isDragging) return;
const width = startWidth - (e.clientX - startX);
panel.style.width = `${width}px`;
}
function stopDragging() {
isDragging = false;
}
function addHoverButton(codeBlock) {
if (!codeBlock.querySelector('.run-demo-hover-button')) {
const hoverButton = document.createElement('button');
hoverButton.textContent = 'Run Demo';
hoverButton.className = 'run-demo-hover-button';
hoverButton.style.cssText = `
position: absolute;
top: 10px;
right: 10px;
padding: 5px 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
display: none;
z-index: 1000;
`;
hoverButton.onclick = (e) => {
e.stopPropagation();
createSlideOutPanel(codeBlock);
};
codeBlock.style.position = 'relative';
codeBlock.appendChild(hoverButton);
codeBlock.addEventListener('mouseenter', () => {
hoverButton.style.display = 'block';
});
codeBlock.addEventListener('mouseleave', () => {
hoverButton.style.display = 'none';
});
}
}
function processCodeBlocks() {
const codeBlocks = document.querySelectorAll('.overflow-y-auto');
codeBlocks.forEach(codeBlock => {
if (codeBlock.closest('.markdown')) {
addHoverButton(codeBlock);
}
});
}
function observeChat() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
const codeBlocks = node.querySelectorAll('.overflow-y-auto');
codeBlocks.forEach(codeBlock => {
if (codeBlock.closest('.markdown')) {
addHoverButton(codeBlock);
}
});
}
});
}
});
});
const chatContainer = document.querySelector('main');
if (chatContainer) {
observer.observe(chatContainer, { childList: true, subtree: true });
}
}
function addIndicator() {
const indicator = document.createElement('div');
indicator.textContent = 'Code Block Processor Active';
indicator.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
z-index: 1000;
padding: 5px 10px;
background-color: #4CAF50;
color: white;
border-radius: 5px;
font-size: 12px;
`;
document.body.appendChild(indicator);
setTimeout(() => indicator.style.display = 'none', 3000);
}
function initializeProcessor() {
addIndicator();
processCodeBlocks();
observeChat();
}
// Use MutationObserver to wait for the chat interface to load
const bodyObserver = new MutationObserver((mutations) => {
if (document.querySelector('main')) {
bodyObserver.disconnect();
initializeProcessor();
}
});
bodyObserver.observe(document.body, { childList: true, subtree: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment