Last active
June 30, 2025 23:38
-
-
Save intellectronica/4a18b4349d431ed37f842ea2371a3447 to your computer and use it in GitHub Desktop.
ArtiGist: UserScript (for use with a monkey extension) for turning any gist into an "artifact".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name ArtiGist | |
// @version 0.5 | |
// @description Launch single-page HTML apps from GitHub Gists. | |
// @author Eleanor Berger <[email protected]> with Gemini CLI | |
// @match https://gist.github.com/*/* | |
// @match https://gist.githubusercontent.com/*/* | |
// @grant GM_xmlhttpRequest | |
// @grant GM_openInTab | |
// ==/UserScript== | |
/* | |
* ArtiGist: A UserScript to launch single-page HTML apps from GitHub Gists. | |
* | |
* This script enhances GitHub Gists by adding a "Launch Page" button to any Gist | |
* that contains an HTML file and is tagged with #artigist in its description. | |
* Clicking the button opens the HTML file in a new tab, with a permissive | |
* Content Security Policy that allows for fetching remote resources. | |
* | |
* To use this script, simply create a Gist with an HTML file and add | |
* the #artigist tag to the Gist's description. The script will automatically | |
* detect the HTML file and add the launch button. | |
* | |
* To test that the script is working and see an example of a valid artigist, | |
* see https://gist.github.com/intellectronica/6d8ccc38f617643982619cc277af7bee | |
*/ | |
(function() { | |
'use strict'; | |
const GIST_TAG = '#artigist'; | |
function isGistPage() { | |
return window.location.href.includes('gist.github.com'); | |
} | |
function hasGistTag() { | |
const descriptionElement = document.querySelector('div[itemprop="about"]'); | |
return descriptionElement && descriptionElement.textContent.includes(GIST_TAG); | |
} | |
function findHtmlFile() { | |
for (const link of document.querySelectorAll('a')) { | |
const href = link.href; | |
if (href.includes('/raw/') && href.endsWith('.html')) { | |
return href; | |
} | |
} | |
return null; | |
} | |
function getRawHtmlContent(htmlFileUrl, callback) { | |
const rawUrl = htmlFileUrl.replace('github.com', 'githubusercontent.com').replace('/blob/', '/'); | |
GM_xmlhttpRequest({ | |
method: "GET", | |
url: rawUrl, | |
onload: function(response) { | |
if (response.status === 200) { | |
callback(response.responseText); | |
} | |
}, | |
onerror: function(error) { | |
} | |
}); | |
} | |
function launchHtml(htmlContent) { | |
// Inject a more permissive Content Security Policy | |
const csp = ` | |
default-src 'self' data:; | |
script-src 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com; | |
style-src 'unsafe-inline' https://fonts.googleapis.com; | |
img-src 'self' data:; | |
connect-src 'self' https://fonts.googleapis.com https://cdn.tailwindcss.com; | |
`; | |
const cspMetaTag = `<meta http-equiv="Content-Security-Policy" content="${csp.replace(/\s+/g, ' ').trim()}">`; | |
// Find the head tag and insert the CSP meta tag | |
const headEndIndex = htmlContent.indexOf('</head>'); | |
if (headEndIndex !== -1) { | |
htmlContent = htmlContent.substring(0, headEndIndex) + cspMetaTag + htmlContent.substring(headEndIndex); | |
} else { | |
// If no head tag, prepend to the beginning (less ideal but works) | |
htmlContent = cspMetaTag + htmlContent; | |
} | |
GM_openInTab('data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent), { active: true, insert: true }); | |
} | |
function createLaunchButton(htmlFileUrl) { | |
const downloadZipButton = document.querySelector('a[data-ga-click*="download zip"]'); | |
if (downloadZipButton) { | |
const launchButton = document.createElement('a'); | |
launchButton.className = 'btn btn-sm' + ' ml-2'; // Inherit basic button styling and add margin-left | |
launchButton.textContent = 'Launch Page'; | |
launchButton.href = '#'; | |
launchButton.addEventListener('click', (event) => { | |
event.preventDefault(); | |
getRawHtmlContent(htmlFileUrl, launchHtml); | |
}); | |
// Insert the new button after the Download ZIP button | |
downloadZipButton.parentNode.insertBefore(launchButton, downloadZipButton.nextSibling); | |
} | |
} | |
function main() { | |
if (isGistPage() && hasGistTag()) { | |
const htmlFileUrl = findHtmlFile(); | |
if (htmlFileUrl) { | |
createLaunchButton(htmlFileUrl); | |
} | |
} | |
} | |
// Use a MutationObserver to wait for the page to load dynamically | |
const observer = new MutationObserver(function(mutations, me) { | |
const gistHeader = document.querySelector('.pagehead-actions'); | |
if (gistHeader) { | |
main(); | |
me.disconnect(); // stop observing once the element is found | |
} | |
}); | |
observer.observe(document, { | |
childList: true, | |
subtree: true | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ArtiGist
A UserScript to launch single-page HTML apps from GitHub Gists
This script enhances GitHub Gists by adding a "Launch Page" button to any Gist that contains an HTML file and is tagged with #artigist in its description. Clicking the button opens the HTML file in a new tab, with a permissive Content Security Policy that allows for fetching remote resources.
To use this script, simply create a Gist with an HTML file and add the #artigist tag to the Gist's description. The script will automatically detect the HTML file and add the launch button.
To test that the script is working and see an example of a valid artigist, see https://gist.github.com/intellectronica/6d8ccc38f617643982619cc277af7bee
🚀 Have Fun!
Eleanor
ai.intellectronica.net