stack-site-builder 1.17.3 → 1.17.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,18 @@ content schema, while a consuming site supplies only content, taxonomy data and
11
11
  config. Sites track the theme with `pnpm up stack-site-builder`, so each release
12
12
  here is a plain version bump they pull in.
13
13
 
14
+ ## [1.17.4] - 2026-07-22
15
+
16
+ ### Changed
17
+
18
+ - **Deck embeds are click-to-run** — 1.17.3 only paused demos you had merely
19
+ scrolled past; once you *interacted* with one, its (now heavier) loops kept
20
+ janking transitions while its slide was near the screen. Every slide iframe
21
+ now starts blank behind a themed "Run the demo" overlay (`slides.runEmbed`
22
+ UI string), loads only on click, and unloads back to the overlay as soon as
23
+ its slide leaves the screen — the deck never carries a live embed between
24
+ slides.
25
+
14
26
  ## [1.17.3] - 2026-07-22
15
27
 
16
28
  ### Fixed
@@ -265,6 +277,7 @@ catalog sites from a thin content-only repository.
265
277
  - **Standalone development setup** — a devcontainer and a minimal `playground/`
266
278
  consuming site for developing and previewing the theme on its own.
267
279
 
280
+ [1.17.4]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.3...v1.17.4
268
281
  [1.17.3]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.2...v1.17.3
269
282
  [1.17.2]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.1...v1.17.2
270
283
  [1.17.1]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.0...v1.17.1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stack-site-builder",
3
3
  "type": "module",
4
- "version": "1.17.3",
4
+ "version": "1.17.4",
5
5
  "license": "MIT",
6
6
  "description": "The engine behind the awesome-*-stack catalog sites: an Astro theme with the catalog/concepts/articles/slides/samples routes, components, styles and markdown pipeline. Sites provide content, taxonomy data and config.",
7
7
  "repository": {
@@ -64,6 +64,7 @@ const priv = entry.data.private;
64
64
  <main
65
65
  class:list={['aas-deck', themeClass]}
66
66
  data-deck
67
+ data-run-embed-label={t('slides.runEmbed')}
67
68
  data-transition={entry.data.transition}
68
69
  data-toc={String(entry.data.toc)}
69
70
  data-toc-level={String(entry.data.toc_level)}
@@ -173,31 +174,47 @@ const priv = entry.data.private;
173
174
  const btnNext = document.querySelector<HTMLButtonElement>('[data-next]');
174
175
  if (elTot) elTot.textContent = String(total);
175
176
 
176
- // Heavy embeds (interactive demo iframes) keep animating offscreen and
177
- // make every later slide transition jank. Keep only the on/near-screen
178
- // slides' iframes live: leaving a slide blanks its frames (src stashed
179
- // on the element), returning restores them the demo reloads fresh,
180
- // exactly like a page refresh. One viewport of margin each side means
181
- // the neighbor slide is already loading while you transition into it.
182
- const framedSlides = slides.filter((s) => s.querySelector('iframe'));
183
- if (framedSlides.length) {
177
+ // Heavy embeds (interactive demo iframes) jank slide transitions the
178
+ // moment they run worse once the viewer has interacted with one. So
179
+ // embeds are click-to-run: each iframe starts blank behind a "run"
180
+ // overlay (src stashed), loads only when the viewer asks, and unloads
181
+ // back to the overlay as soon as its slide leaves the screen. The deck
182
+ // itself never carries a live embed between slides.
183
+ const runLabel = deck.dataset.runEmbedLabel || 'Run';
184
+ const embeds = Array.from(deck.querySelectorAll<HTMLIFrameElement>('.aas-slide iframe'));
185
+ if (embeds.length) {
186
+ for (const f of embeds) {
187
+ f.dataset.aasSrc = f.getAttribute('src') ?? '';
188
+ f.removeAttribute('src');
189
+ const wrap = document.createElement('div');
190
+ wrap.className = 'aas-embed';
191
+ f.parentNode?.insertBefore(wrap, f);
192
+ wrap.appendChild(f);
193
+ const btn = document.createElement('button');
194
+ btn.type = 'button';
195
+ btn.className = 'aas-embed-run';
196
+ btn.innerHTML = `<span aria-hidden="true">▶</span> ${runLabel}`;
197
+ btn.addEventListener('click', () => {
198
+ if (f.dataset.aasSrc) f.setAttribute('src', f.dataset.aasSrc);
199
+ wrap.classList.add('running');
200
+ });
201
+ wrap.appendChild(btn);
202
+ }
184
203
  const io = new IntersectionObserver(
185
204
  (entries) => {
186
205
  for (const e of entries) {
206
+ if (e.isIntersecting) continue;
187
207
  for (const f of Array.from(e.target.querySelectorAll<HTMLIFrameElement>('iframe'))) {
188
- const src = f.getAttribute('src');
189
- if (e.isIntersecting) {
190
- if (!src && f.dataset.aasSrc) f.setAttribute('src', f.dataset.aasSrc);
191
- } else if (src) {
192
- f.dataset.aasSrc = src;
208
+ if (f.getAttribute('src')) {
193
209
  f.removeAttribute('src');
210
+ f.closest('.aas-embed')?.classList.remove('running');
194
211
  }
195
212
  }
196
213
  }
197
214
  },
198
- { root: deck, rootMargin: '0px 100% 0px 100%' },
215
+ { root: deck },
199
216
  );
200
- framedSlides.forEach((s) => io.observe(s));
217
+ slides.filter((s) => s.querySelector('iframe')).forEach((s) => io.observe(s));
201
218
  }
202
219
 
203
220
  let current = 0;
package/src/i18n/ui.ts CHANGED
@@ -90,6 +90,7 @@ export const ui = {
90
90
  'slides.zoom': 'Enlarge',
91
91
  'slides.close': 'Close',
92
92
  'slides.overview': 'Overview',
93
+ 'slides.runEmbed': 'Run the demo',
93
94
  'sample.title': 'Samples',
94
95
  'sample.tagline':
95
96
  'The runnable mini-projects behind the catalog — download a folder and run it with Docker.',
@@ -253,6 +254,7 @@ export const ui = {
253
254
  'slides.zoom': '크게 보기',
254
255
  'slides.close': '닫기',
255
256
  'slides.overview': '한눈에 보기',
257
+ 'slides.runEmbed': '데모 실행',
256
258
  'sample.title': '샘플',
257
259
  'sample.tagline': '카탈로그 뒤의 실행 가능한 미니 프로젝트 — 폴더를 받아 Docker로 실행해 보세요.',
258
260
  'sample.usedBy': '이 샘플을 쓰는 곳',
@@ -2355,3 +2355,32 @@ summary {
2355
2355
  min-width: 3.5ch;
2356
2356
  text-align: center;
2357
2357
  }
2358
+
2359
+ /* Click-to-run embeds in slide decks (DeckView wraps each slide iframe): the
2360
+ frame stays blank behind this overlay until the viewer runs it; leaving the
2361
+ slide unloads the frame and brings the overlay back. */
2362
+ .aas-embed {
2363
+ position: relative;
2364
+ display: block;
2365
+ }
2366
+ .aas-embed .aas-embed-run {
2367
+ position: absolute;
2368
+ inset: 0;
2369
+ display: flex;
2370
+ align-items: center;
2371
+ justify-content: center;
2372
+ gap: 0.5rem;
2373
+ border: 1px dashed var(--aas-border);
2374
+ border-radius: 12px;
2375
+ background: var(--aas-panel);
2376
+ color: var(--aas-text);
2377
+ font-weight: 600;
2378
+ font-size: 1rem;
2379
+ cursor: pointer;
2380
+ }
2381
+ .aas-embed .aas-embed-run:hover {
2382
+ background: var(--aas-tint);
2383
+ }
2384
+ .aas-embed.running .aas-embed-run {
2385
+ display: none;
2386
+ }