File size: 3,578 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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
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: 12px 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;
  }
  .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 { break-inside: avoid; page-break-inside: avoid; }
  }
</style>