Add browse dashboard, tags, visibility control, export, QR sharing, and embed mode
- Browse dashboard at / with search, framework filter, tag pills, and pagination - Tags system with autocomplete datalist and per-fiddle tag management - Listed/unlisted toggle for visibility control (unlisted still accessible via direct URL) - Export standalone HTML with inlined CSS/JS and framework CDN tags - QR code modal for sharing fiddle URLs - Embed mode at /embed/:id for minimal preview-only rendering - Extract shared loadScript() utility from 4 files into utils.js - Database schema: listed column, tags and fiddle_tags tables with index
This commit is contained in:
58
public/js/export.js
Normal file
58
public/js/export.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { getFrameworkRuntime } from './js-preprocessors.js';
|
||||
|
||||
/**
|
||||
* Export a fiddle as a standalone HTML file and trigger download.
|
||||
*/
|
||||
export function exportHtml({ title, html, css, js, mode, extraCss = '', isModule = false }) {
|
||||
const runtime = getFrameworkRuntime(mode);
|
||||
const allCss = extraCss ? `${css}\n${extraCss}` : css;
|
||||
|
||||
let bodyContent;
|
||||
if (mode === 'vue' || mode === 'svelte') {
|
||||
bodyContent = html ? `${html}\n${runtime.bodyHtml}` : runtime.bodyHtml;
|
||||
} else if (runtime.bodyHtml) {
|
||||
bodyContent = `${html}\n${runtime.bodyHtml}`;
|
||||
} else {
|
||||
bodyContent = html;
|
||||
}
|
||||
|
||||
let scripts = '';
|
||||
if (js) {
|
||||
if (isModule) {
|
||||
scripts = `<script type="module">\n${js}\n<\/script>`;
|
||||
} else {
|
||||
for (const url of runtime.scripts) {
|
||||
scripts += `<script src="${url}"><\/script>\n`;
|
||||
}
|
||||
scripts += `<script>\n${js}\n<\/script>`;
|
||||
}
|
||||
}
|
||||
|
||||
const doc = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${escHtml(title)}</title>
|
||||
<style>
|
||||
${allCss}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${bodyContent}
|
||||
${scripts}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const blob = new Blob([doc], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${title.replace(/[^a-zA-Z0-9_-]/g, '_')}.html`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function escHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
Reference in New Issue
Block a user