Spaces:
Running
Running
File size: 4,831 Bytes
5846c4a c59bbe1 5846c4a b8e1b6c 5846c4a b8e1b6c 5846c4a b8e1b6c 5846c4a fee9c1e 5846c4a 72cfb5a c59bbe1 72cfb5a c59bbe1 72cfb5a 5846c4a e329457 5846c4a fee9c1e e329457 fee9c1e e329457 fee9c1e e329457 fee9c1e 72cfb5a fee9c1e 98af9a5 b8e1b6c 98af9a5 fee9c1e 72cfb5a b6281fd 72cfb5a b6281fd 72cfb5a 52307d3 72cfb5a 33aefb9 72cfb5a 33aefb9 72cfb5a 5846c4a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | ---
interface Props { src: string; title?: string; desc?: string; frameless?: boolean; align?: 'left' | 'center' | 'right' }
const { src, title, desc, frameless = false, align = 'left' } = Astro.props as Props;
// Load all .html embeds under src/content/embeds/** as strings (dev & build)
const embeds = (import.meta as any).glob('../content/embeds/**/*.html', { query: '?raw', import: 'default', eager: true }) as Record<string, string>;
function resolveFragment(requested: string): string | null {
// Allow both "banner.html" and "embeds/banner.html"
const needle = requested.replace(/^\/*/, '');
for (const [key, html] of Object.entries(embeds)) {
if (key.endsWith('/' + needle) || key.endsWith('/' + needle.replace(/^embeds\//, ''))) {
return html;
}
}
return null;
}
const html = resolveFragment(src);
const mountId = `frag-${Math.random().toString(36).slice(2)}`;
---
{ html ? (
<figure class="html-embed">
{title && <figcaption class="html-embed__title" style={`text-align:${align}`}>{title}</figcaption>}
<div class={`html-embed__card${frameless ? ' is-frameless' : ''}`}>
<div id={mountId} set:html={html} />
</div>
{desc && <figcaption class="html-embed__desc" style={`text-align:${align}`} set:html={desc}></figcaption>}
</figure>
) : (
<div><!-- Fragment not found: {src} --></div>
) }
<script>
// Re-execute <script> tags inside the injected fragment (innerHTML doesn't run scripts)
const scriptEl = document.currentScript;
const mount = scriptEl ? scriptEl.previousElementSibling : null;
const execute = () => {
if (!mount) return;
const scripts = mount.querySelectorAll('script');
scripts.forEach(old => {
// ignore non-executable types (e.g., application/json)
if (old.type && old.type !== 'text/javascript' && old.type !== 'module' && old.type !== '') return;
if (old.dataset.executed === 'true') return;
old.dataset.executed = 'true';
if (old.src) {
const s = document.createElement('script');
Array.from(old.attributes).forEach(({ name, value }) => s.setAttribute(name, value));
document.body.appendChild(s);
} else {
try {
// run inline
(0, eval)(old.text || '');
} catch (e) {
console.error('HtmlEmbed inline script error:', e);
}
}
});
};
// Ensure execution when ready: run now if Plotly or D3 is present, or when document is ready; otherwise wait for 'load'
// @ts-expect-error: Plotly/d3 are attached globally at runtime via embeds
if (window.Plotly || window.d3 || document.readyState === 'complete') execute();
else window.addEventListener('load', execute, { once: true });
</script>
<style>
.html-embed { margin: 0; }
.html-embed__title {
text-align: left;
font-weight: 600;
font-size: 0.95rem;
color: var(--text-color);
margin: 0 0 6px 0;
}
.html-embed__card {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 8px;
}
.html-embed__card.is-frameless {
background: transparent;
border-color: transparent;
padding: 0;
}
.html-embed__desc {
text-align: left;
font-size: 0.9rem;
color: var(--muted-color);
margin: 6px 0 0 0;
}
@media (prefers-color-scheme: dark) {
[data-theme="dark"] .html-embed__card:not(.is-frameless) { background: #12151b; border-color: rgba(255,255,255,.15); }
}
@media print {
.html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
.html-embed__card { padding: 6px; }
.html-embed__card.is-frameless { padding: 0; }
.html-embed__card svg,
.html-embed__card canvas,
.html-embed__card img { max-width: 100% !important; height: auto !important; }
.html-embed__card > div[id^="frag-"] { width: 100% !important; }
}
@media print {
/* Avoid breaks inside embeds */
.html-embed, .html-embed__card { break-inside: avoid; page-break-inside: avoid; }
/* Constrain width and scale inner content */
.html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; }
.html-embed__card { padding: 6px; }
.html-embed__card.is-frameless { padding: 0; }
.html-embed__card svg,
.html-embed__card canvas,
.html-embed__card img,
.html-embed__card video,
.html-embed__card iframe { max-width: 100% !important; height: auto !important; }
.html-embed__card > div[id^="frag-"] { width: 100% !important; max-width: 100% !important; }
/* Center and constrain the banner (galaxy) when printing */
.html-embed .d3-galaxy { width: 100% !important; max-width: 980px !important; margin-left: auto !important; margin-right: auto !important; }
}
</style>
|