human (Nov 7, 2024, 07:11 AM)
Build an artifact that shows me a textarea and an iframe next to each other (or stacked on top on narrow screens). The textarea shows a basic HTML skeleton with a <style> block and a <script> block implementing a tiny app where you can type in an input and the value updates in the box below.
Set it up so that the iframe has a sandbox attribute which is controlled by a set of checkboxes for all the options in that outer app - and any time I submit the main textarea the iframe srcdoc= updates with my code
assistant (Nov 7, 2024, 07:11 AM)
I'll create a React component that implements this HTML code playground with sandbox controls.
import React, { useState } from 'react';
import { Card } from '@/components/ui/card';
const defaultHtml = `<!DOCTYPE html>
<html>
<head>
<style>
.output {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<input type="text" id="userInput" placeholder="Type something...">
<div class="output" id="output">Output will appear here</div>
<script>
const input = document.getElementById('userInput');
const output = document.getElementById('output');
input.addEventListener('input', (e) => {
output.textContent = e.target.value;
});
</script>
</body>
</html>`;
const sandboxOptions = [
{ value: 'allow-forms', label: 'Forms' },
{ value: 'allow-modals', label: 'Modals' },
{ value: 'allow-orientation-lock', label:
human (Nov 7, 2024, 07:12 AM)
don't use react
assistant (Nov 7, 2024, 07:12 AM)
I'll create a pure HTML/JavaScript version instead.
<!DOCTYPE html>
<html>
<head>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.container {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
}
.editor {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.preview {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
textarea {
width: 100%;
height: 400px;
font-family: monospace;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
}
iframe {
width: 100%;
height: 400px;
border: 1px solid #ccc;
border-radius: 4px;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 10px;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 5px;
}
button {
padding: 8px 16px;
background: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0051a2;
}
label {
user-select: none;
}
</style>
</head>
<body>
<div class="container">
<div class="editor">
<h3>HTML Editor</h3>
<textarea id="codeEditor"></textarea>
<button id="updatePreview">Update Preview</button>
</div>
<div class="preview">
<h3>Preview</h3>
<div class="controls" id="sandboxControls"></div>
<iframe id="preview"></iframe>
</div>
</div>
<script>
// Default HTML template
const defaultHtml = `<!DOCTYPE html>
<html>
<head>
<style>
.output {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<input type="text" id="userInput" placeholder="Type something...">
<div class="output" id="output">Output will appear here</div>
<script>
const input = document.getElementById('userInput');
const output = document.getElementById('output');
input.addEventListener('input', (e) => {
output.textContent = e.target.value;
});
<\/script>
</body>
</html>`;
// Sandbox options
const sandboxOptions = [
{ value: 'allow-forms', label: 'Forms' },
{ value: 'allow-modals', label: 'Modals' },
{ value: 'allow-orientation-lock', label: 'Orientation Lock' },
{ value: 'allow-pointer-lock', label: 'Pointer Lock' },
{ value: 'allow-popups', label: 'Popups' },
{ value: 'allow-popups-to-escape-sandbox', label: 'Popups Escape Sandbox' },
{ value: 'allow-presentation', label: 'Presentation' },
{ value: 'allow-same-origin', label: 'Same Origin' },
{ value: 'allow-scripts', label: 'Scripts' },
{ value: 'allow-top-navigation', label: 'Top Navigation' }
];
// Get DOM elements
const codeEditor = document.getElementById('codeEditor');
const preview = document.getElementById('preview');
const updatePreviewBtn = document.getElementById('updatePreview');
const sandboxControls = document.getElementById('sandboxControls');
// Set initial editor content
codeEditor.value = defaultHtml;
// Create sandbox checkboxes
sandboxOptions.forEach(option => {
const div = document.createElement('div');
div.className = 'checkbox-group';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = option.value;
checkbox.checked = option.value === 'allow-scripts'; // Enable scripts by default
const label = document.createElement('label');
label.htmlFor = option.value;
label.textContent = option.label;
div.appendChild(checkbox);
div.appendChild(label);
sandboxControls.appendChild(div);
});
// Update preview function
function updatePreview() {
const checkedOptions = Array.from(sandboxControls.querySelectorAll('input:checked'))
.map(input => input.id)
.join(' ');
preview.setAttribute('sandbox', checkedOptions);
preview.srcdoc = codeEditor.value;
}
// Event listeners
updatePreviewBtn.addEventListener('click', updatePreview);
sandboxControls.addEventListener('change', updatePreview);
// Initial preview
updatePreview();
</script>
</body>
</html>
I've created a standalone HTML page with a code editor and preview setup. Here's what it includes:
- A split view with a textarea editor on the left and preview iframe on the right (stacks vertically on mobile)
- Checkboxes for all standard sandbox options
- A button to update the preview
- A default HTML template that demonstrates a simple input-to-output app
- Responsive design that works on all screen sizes
- Clean, minimal styling
The sandbox options include all standard HTML iframe sandbox attributes like:
- allow-scripts (enabled by default)
- allow-forms
- allow-same-origin
- allow-popups
- and more
To use it:
- The code editor starts with a default template
- Modify the code as needed
- Click "Update Preview" to see your changes
- Toggle sandbox permissions using the checkboxes
- The preview updates immediately when you change sandbox permissions
Would you like me to modify anything about this setup?
human (Nov 7, 2024, 07:12 AM)
An error occurred while trying to run the generated artifact.
Unexpected token (25:7)
Can you fix this error in html-playground
?
assistant (Nov 7, 2024, 07:12 AM)
The error is likely due to an unclosed script tag in the default HTML template. Let me fix that by properly escaping the closing script tag.
Find this:
<script>
const input = document.getElementById('userInput');
const output = document.getElementById('output');
input.addEventListener('input', (e) => {
output.textContent = e.target.value;
});
</script>
Replace with this:
<script>
const input = document.getElementById('userInput');
const output = document.getElementById('output');
input.addEventListener('input', (e) => {
output.textContent = e.target.value;
});
</script>
I've updated the template to properly escape the <script>
tags in the default HTML content. The error occurred because the template string was interpreting the inner </script>
tag as the end of the outer script block. By using HTML entities (<
and >
), we prevent this interpretation.
Would you like to try it now? The playground should work as intended with the fixed template.
human (Nov 7, 2024, 07:16 AM)
I changed it to this. Now add a feature where as I add and remove sandbox attributes a code sample below those controls shows the current iframe HTML tag with those attributes
paste.txt
<!DOCTYPE html>
<html>
<head>
<title>iframe sandbox</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.container {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
}
.editor {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.preview {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
textarea {
width: 100%;
height: 400px;
font-family: monospace;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
}
iframe {
width: 100%;
height: 400px;
border: 1px solid #ccc;
border-radius: 4px;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 10px;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 5px;
}
button {
padding: 8px 16px;
background: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0051a2;
}
label {
user-select: none;
}
</style>
</head>
<body>
<div class="container">
<div class="editor">
<h3>iframe sandbox</h3>
<textarea id="codeEditor"></textarea>
<button id="updatePreview">Update Preview</button>
</div>
<div class="preview">
<iframe id="preview"></iframe>
Sandbox controls
<div class="controls" id="sandboxControls"></div>
</div>
</div>
<script>
// Default HTML template
const defaultHtml = `<!DOCTYPE html>
<html>
<head>
<style>
.output {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<input type="text" id="userInput" placeholder="Type something...">
<div class="output" id="output">Output will appear here</div>
<script>
const input = document.getElementById('userInput');
const output = document.getElementById('output');
input.addEventListener('input', (e) => {
output.textContent = e.target.value;
});
<\/script>
</body>
</html>`;
// Sandbox options
const sandboxOptions = [
{ value: 'allow-forms', label: 'Forms' },
{ value: 'allow-modals', label: 'Modals' },
{ value: 'allow-orientation-lock', label: 'Orientation Lock' },
{ value: 'allow-pointer-lock', label: 'Pointer Lock' },
{ value: 'allow-popups', label: 'Popups' },
{ value: 'allow-popups-to-escape-sandbox', label: 'Popups Escape Sandbox' },
{ value: 'allow-presentation', label: 'Presentation' },
{ value: 'allow-same-origin', label: 'Same Origin' },
{ value: 'allow-scripts', label: 'Scripts' },
{ value: 'allow-top-navigation', label: 'Top Navigation' }
];
// Get DOM elements
const codeEditor = document.getElementById('codeEditor');
const preview = document.getElementById('preview');
const updatePreviewBtn = document.getElementById('updatePreview');
const sandboxControls = document.getElementById('sandboxControls');
// Set initial editor content
codeEditor.value = defaultHtml;
// Create sandbox checkboxes
sandboxOptions.forEach(option => {
const div = document.createElement('div');
div.className = 'checkbox-group';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = option.value;
checkbox.checked = option.value === 'allow-scripts'; // Enable scripts by default
const label = document.createElement('label');
label.htmlFor = option.value;
label.textContent = option.label;
div.appendChild(checkbox);
div.appendChild(label);
sandboxControls.appendChild(div);
});
// Update preview function
function updatePreview() {
const checkedOptions = Array.from(sandboxControls.querySelectorAll('input:checked'))
.map(input => input.id)
.join(' ');
preview.setAttribute('sandbox', checkedOptions);
preview.srcdoc = codeEditor.value;
}
// Event listeners
updatePreviewBtn.addEventListener('click', updatePreview);
sandboxControls.addEventListener('change', updatePreview);
// Initial preview
updatePreview();
</script>
</body>
</html>
assistant (Nov 7, 2024, 07:16 AM)
I'll update the HTML to add a code sample showing the current iframe tag configuration.
Find this:
<div class="preview">
<iframe id="preview"></iframe>
Sandbox controls
<div class="controls" id="sandboxControls"></div>
</div>
Replace with this:
<div class="preview">
<iframe id="preview"></iframe>
<h3>Sandbox controls</h3>
<div class="controls" id="sandboxControls"></div>
<pre id="iframeCode" style="background: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; margin-top: 10px; font-size: 14px;"></pre>
</div>
Find this:
// Update preview function
function updatePreview() {
const checkedOptions = Array.from(sandboxControls.querySelectorAll('input:checked'))
.map(input => input.id)
.join(' ');
preview.setAttribute('sandbox', checkedOptions);
preview.srcdoc = codeEditor.value;
}
Replace with this:
// Update preview function
function updatePreview() {
const checkedOptions = Array.from(sandboxControls.querySelectorAll('input:checked'))
.map(input => input.id)
.join(' ');
preview.setAttribute('sandbox', checkedOptions);
preview.srcdoc = codeEditor.value;
// Update the code sample
const iframeCode = document.getElementById('iframeCode');
const sandboxAttr = checkedOptions ? ` sandbox="${checkedOptions}"` : '';
iframeCode.textContent = `<iframe${sandboxAttr} src="..."></iframe>`;
}
I've made the following changes:
- Added a
<pre>
element below the sandbox controls to display the iframe code - Updated the
updatePreview
function to also update this code sample whenever the sandbox attributes change - The code sample will show the current sandbox attributes in a properly formatted iframe tag
- Styled the code sample with a light gray background and proper padding for readability
Now as you toggle the sandbox checkboxes, you'll see a live preview of what the iframe HTML tag would look like with those attributes. The code sample will update automatically whenever you change the sandbox controls.