Skip to content

Instantly share code, notes, and snippets.

@intellectronica
Last active June 30, 2025 23:38
Show Gist options
  • Save intellectronica/4a18b4349d431ed37f842ea2371a3447 to your computer and use it in GitHub Desktop.
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".
// ==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
});
})();
@intellectronica
Copy link
Author

intellectronica commented Jun 29, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment