Files
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

171 lines
4.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Safe Content Frame Demo</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #333;
}
.demo-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.input-section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.output-section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
textarea {
width: 100%;
height: 300px;
font-family: monospace;
font-size: 14px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
button {
margin-top: 10px;
padding: 10px 20px;
background: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #0060df;
}
#frame-container {
width: 100%;
height: 400px;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.status {
margin-top: 10px;
padding: 10px;
background: #f0f0f0;
border-radius: 4px;
font-size: 12px;
font-family: monospace;
}
.error {
background: #fee;
color: #c00;
}
.success {
background: #efe;
color: #060;
}
</style>
</head>
<body>
<h1>Safe Content Frame Demo</h1>
<p>Test rendering untrusted HTML content in a sandboxed iframe.</p>
<div class="demo-container">
<div class="input-section">
<h3>HTML Content</h3>
<textarea id="html-input"><h1>Hello from Safe Content Frame!</h1>
<p>This content is rendered in a <strong>sandboxed iframe</strong>.</p>
<style>
body {
font-family: system-ui;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
margin: 0;
}
h1 { margin-top: 0; }
</style>
<script>
console.log('Script executed in sandbox!');
document.body.innerHTML += '<p>✅ JavaScript is working!</p>';
</script></textarea>
<button id="render-btn">Render Content</button>
<div id="status" class="status">Ready to render</div>
</div>
<div class="output-section">
<h3>Rendered Output</h3>
<div id="frame-container"></div>
</div>
</div>
<script type="module">
import { SafeContentFrame } from '../src/index.ts';
const scf = new SafeContentFrame('demo', {
sandbox: ['allow-scripts'],
});
const htmlInput = document.getElementById('html-input');
const renderBtn = document.getElementById('render-btn');
const frameContainer = document.getElementById('frame-container');
const status = document.getElementById('status');
let currentFrame = null;
renderBtn.addEventListener('click', async () => {
try {
status.textContent = 'Rendering...';
status.className = 'status';
// Dispose previous frame
if (currentFrame) {
currentFrame.dispose();
currentFrame = null;
}
// Clear container
frameContainer.innerHTML = '';
const html = htmlInput.value;
currentFrame = await scf.renderHtml(html, frameContainer);
status.textContent = `✅ Rendered successfully!\nOrigin: ${currentFrame.origin}`;
status.className = 'status success';
// Try to wait for full load
try {
await currentFrame.fullyLoadedPromiseWithTimeout(5000);
status.textContent += '\n✅ Content fully loaded';
} catch (_e) {
status.textContent += '\n⏱️ Load timeout (content may still be loading)';
}
} catch (error) {
status.textContent = `❌ Error: ${error.message}`;
status.className = 'status error';
console.error(error);
}
});
</script>
</body>
</html>