stack-site-builder 1.17.1 → 1.17.3
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 +23 -0
- package/package.json +1 -1
- package/src/components/DeckView.astro +31 -1
- package/src/layouts/BaseLayout.astro +4 -16
- package/src/lib/icons.ts +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,27 @@ 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.3] - 2026-07-22
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Slide transitions degraded after passing embed slides** — interactive
|
|
19
|
+
demo iframes kept their animation loops running offscreen, janking every
|
|
20
|
+
later transition until a refresh. The deck now keeps only the on/near-
|
|
21
|
+
screen slides' iframes live (an IntersectionObserver with one viewport of
|
|
22
|
+
side margin stashes/restores `src`), so a revisited demo reloads fresh and
|
|
23
|
+
transitions stay smooth.
|
|
24
|
+
|
|
25
|
+
## [1.17.2] - 2026-07-22
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **Slide decks ignored the site's custom favicon** — DeckView owns its own
|
|
30
|
+
`<head>` (fullscreen layout) and still hardcoded `/favicon.svg`. Browser
|
|
31
|
+
icons are now resolved once in `lib/icons` and shared by BaseLayout and
|
|
32
|
+
DeckView, so `site.icons` applies everywhere (favicon, apple-touch,
|
|
33
|
+
manifest).
|
|
34
|
+
|
|
14
35
|
## [1.17.1] - 2026-07-22
|
|
15
36
|
|
|
16
37
|
### Fixed
|
|
@@ -244,6 +265,8 @@ catalog sites from a thin content-only repository.
|
|
|
244
265
|
- **Standalone development setup** — a devcontainer and a minimal `playground/`
|
|
245
266
|
consuming site for developing and previewing the theme on its own.
|
|
246
267
|
|
|
268
|
+
[1.17.3]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.2...v1.17.3
|
|
269
|
+
[1.17.2]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.1...v1.17.2
|
|
247
270
|
[1.17.1]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.17.0...v1.17.1
|
|
248
271
|
[1.17.0]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.16.0...v1.17.0
|
|
249
272
|
[1.16.0]: https://github.com/CodeCompose7/stack-site-builder/compare/v1.15.0...v1.16.0
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stack-site-builder",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.17.
|
|
4
|
+
"version": "1.17.3",
|
|
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": {
|
|
@@ -6,6 +6,7 @@ import { render, type CollectionEntry } from 'astro:content';
|
|
|
6
6
|
import MermaidLoader from './MermaidLoader.astro';
|
|
7
7
|
import PrivateGate from './PrivateGate.astro';
|
|
8
8
|
import { useTranslations, type Lang } from '../i18n/ui';
|
|
9
|
+
import { siteIcons } from '../lib/icons';
|
|
9
10
|
|
|
10
11
|
// Renders a full-screen slide deck as a horizontal scroll-snap presentation.
|
|
11
12
|
// A page (src/pages/slides/[deck].astro and its ko/ sibling) picks the deck for
|
|
@@ -33,7 +34,9 @@ const priv = entry.data.private;
|
|
|
33
34
|
<title>{entry.data.title} — {site.name}</title>
|
|
34
35
|
<meta name="description" content={(priv ? entry.data.teaser : entry.data.description) ?? ''} />
|
|
35
36
|
{priv && <meta name="robots" content="noindex" />}
|
|
36
|
-
<link rel="icon" type
|
|
37
|
+
<link rel="icon" {...(siteIcons.faviconType ? { type: siteIcons.faviconType } : {})} href={`${base}${siteIcons.favicon}`} />
|
|
38
|
+
{siteIcons.appleTouch && <link rel="apple-touch-icon" href={`${base}${siteIcons.appleTouch}`} />}
|
|
39
|
+
{siteIcons.manifest && <link rel="manifest" href={`${base}${siteIcons.manifest}`} />}
|
|
37
40
|
{/* Follow the site's light/dark preference before first paint (same logic as
|
|
38
41
|
BaseLayout). The deck's visual theme is a separate CSS class, below. */}
|
|
39
42
|
<script is:inline>
|
|
@@ -170,6 +173,33 @@ const priv = entry.data.private;
|
|
|
170
173
|
const btnNext = document.querySelector<HTMLButtonElement>('[data-next]');
|
|
171
174
|
if (elTot) elTot.textContent = String(total);
|
|
172
175
|
|
|
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) {
|
|
184
|
+
const io = new IntersectionObserver(
|
|
185
|
+
(entries) => {
|
|
186
|
+
for (const e of entries) {
|
|
187
|
+
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;
|
|
193
|
+
f.removeAttribute('src');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{ root: deck, rootMargin: '0px 100% 0px 100%' },
|
|
199
|
+
);
|
|
200
|
+
framedSlides.forEach((s) => io.observe(s));
|
|
201
|
+
}
|
|
202
|
+
|
|
173
203
|
let current = 0;
|
|
174
204
|
// Step reveal (fragments): each slide's `.aas-step` elements are revealed
|
|
175
205
|
// one at a time. `shown[i]` = how many are currently visible.
|
|
@@ -10,6 +10,7 @@ import { getNavPages, pageSlugOf } from '../lib/pages';
|
|
|
10
10
|
import { getNavProducts, getTopProducts, productSlugOf } from '../lib/products';
|
|
11
11
|
import { homeTemplate } from '../lib/home';
|
|
12
12
|
import { sectionEnabled, type SectionKey } from '../lib/sections';
|
|
13
|
+
import { siteIcons } from '../lib/icons';
|
|
13
14
|
|
|
14
15
|
interface Props {
|
|
15
16
|
title: string;
|
|
@@ -33,19 +34,6 @@ const container = wide ? 'max-w-[81rem]' : 'max-w-[75rem]';
|
|
|
33
34
|
const home = getRelativeLocaleUrl(lang, '');
|
|
34
35
|
const base = import.meta.env.BASE_URL;
|
|
35
36
|
|
|
36
|
-
// Site-configurable browser icons (`site.icons`). `favicon` may be any image
|
|
37
|
-
// the browser can render (SVG/PNG/ICO — public/ path); appleTouch/manifest
|
|
38
|
-
// links render only when declared. Defaults to the theme's /favicon.svg.
|
|
39
|
-
const icons =
|
|
40
|
-
(site as { icons?: { favicon?: string; appleTouch?: string; manifest?: string } }).icons ?? {};
|
|
41
|
-
const faviconHref = icons.favicon ?? '/favicon.svg';
|
|
42
|
-
const faviconType = faviconHref.endsWith('.svg')
|
|
43
|
-
? 'image/svg+xml'
|
|
44
|
-
: faviconHref.endsWith('.png')
|
|
45
|
-
? 'image/png'
|
|
46
|
-
: faviconHref.endsWith('.ico')
|
|
47
|
-
? 'image/x-icon'
|
|
48
|
-
: undefined;
|
|
49
37
|
const baseNoSlash = base.replace(/\/$/, '');
|
|
50
38
|
|
|
51
39
|
// Home URL per locale, for the language auto-detect redirect on the root home.
|
|
@@ -173,9 +161,9 @@ const navItems = allNavItems.filter((item) => !item.section || sectionEnabled(it
|
|
|
173
161
|
<title>{title}</title>
|
|
174
162
|
<meta name="description" content={description} />
|
|
175
163
|
{noindex && <meta name="robots" content="noindex" />}
|
|
176
|
-
<link rel="icon" {...(faviconType ? { type: faviconType } : {})} href={`${baseNoSlash}${
|
|
177
|
-
{
|
|
178
|
-
{
|
|
164
|
+
<link rel="icon" {...(siteIcons.faviconType ? { type: siteIcons.faviconType } : {})} href={`${baseNoSlash}${siteIcons.favicon}`} />
|
|
165
|
+
{siteIcons.appleTouch && <link rel="apple-touch-icon" href={`${baseNoSlash}${siteIcons.appleTouch}`} />}
|
|
166
|
+
{siteIcons.manifest && <link rel="manifest" href={`${baseNoSlash}${siteIcons.manifest}`} />}
|
|
179
167
|
{
|
|
180
168
|
sectionEnabled('articles') && (
|
|
181
169
|
<link
|
package/src/lib/icons.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { site } from '@aas-data/site';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The site's browser icons (`site.ts` `icons`), resolved once with the theme
|
|
5
|
+
* default. Shared by every layout that owns a `<head>` (BaseLayout, DeckView)
|
|
6
|
+
* so a site's custom favicon applies everywhere.
|
|
7
|
+
*/
|
|
8
|
+
export interface SiteIcons {
|
|
9
|
+
favicon: string;
|
|
10
|
+
faviconType?: string;
|
|
11
|
+
appleTouch?: string;
|
|
12
|
+
manifest?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const cfg =
|
|
16
|
+
(site as { icons?: { favicon?: string; appleTouch?: string; manifest?: string } }).icons ?? {};
|
|
17
|
+
const favicon = cfg.favicon ?? '/favicon.svg';
|
|
18
|
+
|
|
19
|
+
export const siteIcons: SiteIcons = {
|
|
20
|
+
favicon,
|
|
21
|
+
faviconType: favicon.endsWith('.svg')
|
|
22
|
+
? 'image/svg+xml'
|
|
23
|
+
: favicon.endsWith('.png')
|
|
24
|
+
? 'image/png'
|
|
25
|
+
: favicon.endsWith('.ico')
|
|
26
|
+
? 'image/x-icon'
|
|
27
|
+
: undefined,
|
|
28
|
+
appleTouch: cfg.appleTouch,
|
|
29
|
+
manifest: cfg.manifest,
|
|
30
|
+
};
|