stack-site-builder 1.10.0
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/LICENSE +21 -0
- package/README.md +45 -0
- package/dev/local-samples-client.js +284 -0
- package/dev/local-samples.mjs +594 -0
- package/index.d.ts +12 -0
- package/index.mjs +124 -0
- package/markdown.mjs +394 -0
- package/package.json +52 -0
- package/src/components/ArrowUpRight.astro +24 -0
- package/src/components/ArticleCard.astro +56 -0
- package/src/components/ArticleDetail.astro +100 -0
- package/src/components/ArticleLink.astro +45 -0
- package/src/components/BackToTop.astro +47 -0
- package/src/components/BlogIndex.astro +137 -0
- package/src/components/Breadcrumb.astro +37 -0
- package/src/components/CategoryIndex.astro +115 -0
- package/src/components/ChevronDown.astro +23 -0
- package/src/components/CodeSamples.astro +141 -0
- package/src/components/CollapsibleGrid.astro +50 -0
- package/src/components/ConceptCard.astro +59 -0
- package/src/components/ConceptDetail.astro +102 -0
- package/src/components/ConceptIndex.astro +143 -0
- package/src/components/ConceptLink.astro +43 -0
- package/src/components/ConceptTools.astro +49 -0
- package/src/components/DeckView.astro +772 -0
- package/src/components/DetailTabs.astro +219 -0
- package/src/components/FilterControls.astro +407 -0
- package/src/components/Glossary.astro +264 -0
- package/src/components/Home.astro +90 -0
- package/src/components/LanguageSwitcher.astro +86 -0
- package/src/components/ListControls.astro +182 -0
- package/src/components/MermaidLoader.astro +96 -0
- package/src/components/PricingSection.astro +100 -0
- package/src/components/ProjectFileList.astro +68 -0
- package/src/components/ProjectFileTree.astro +95 -0
- package/src/components/ProjectRelatedTools.astro +70 -0
- package/src/components/ProjectViewer.astro +431 -0
- package/src/components/RelatedArticles.astro +52 -0
- package/src/components/RelatedConcepts.astro +60 -0
- package/src/components/RelatedTools.astro +55 -0
- package/src/components/SampleIndex.astro +464 -0
- package/src/components/SamplePage.astro +95 -0
- package/src/components/SampleProject.astro +56 -0
- package/src/components/Slide.astro +36 -0
- package/src/components/SlidesIndex.astro +60 -0
- package/src/components/StackCard.astro +183 -0
- package/src/components/StackDetail.astro +507 -0
- package/src/components/StarIcon.astro +21 -0
- package/src/components/TagIndex.astro +51 -0
- package/src/components/ThemeToggle.astro +106 -0
- package/src/components/Toc.astro +39 -0
- package/src/components/TocRail.astro +74 -0
- package/src/components/ToolAvatar.astro +133 -0
- package/src/components/ToolMeta.astro +95 -0
- package/src/components/VendorIndex.astro +51 -0
- package/src/content.ts +228 -0
- package/src/i18n/ui.ts +337 -0
- package/src/layouts/BaseLayout.astro +287 -0
- package/src/lib/articles.ts +27 -0
- package/src/lib/assets.ts +19 -0
- package/src/lib/category-tree.ts +64 -0
- package/src/lib/concepts.ts +35 -0
- package/src/lib/dates.ts +21 -0
- package/src/lib/facets.ts +57 -0
- package/src/lib/github.ts +186 -0
- package/src/lib/inline-md.ts +30 -0
- package/src/lib/pricing.ts +22 -0
- package/src/lib/project.ts +309 -0
- package/src/lib/samples.ts +88 -0
- package/src/lib/slides.ts +25 -0
- package/src/lib/stacks.ts +129 -0
- package/src/lib/toc-rail-client.ts +79 -0
- package/src/pages/article/[...id].astro +24 -0
- package/src/pages/article/category/[id].astro +17 -0
- package/src/pages/article/index.astro +12 -0
- package/src/pages/categories/[id].astro +25 -0
- package/src/pages/concept/[...id].astro +24 -0
- package/src/pages/concept/category/[id].astro +17 -0
- package/src/pages/concept/index.astro +12 -0
- package/src/pages/glossary.astro +12 -0
- package/src/pages/index.astro +9 -0
- package/src/pages/ko/article/[...id].astro +24 -0
- package/src/pages/ko/article/category/[id].astro +17 -0
- package/src/pages/ko/article/index.astro +12 -0
- package/src/pages/ko/categories/[id].astro +24 -0
- package/src/pages/ko/concept/[...id].astro +24 -0
- package/src/pages/ko/concept/category/[id].astro +17 -0
- package/src/pages/ko/concept/index.astro +12 -0
- package/src/pages/ko/glossary.astro +12 -0
- package/src/pages/ko/index.astro +9 -0
- package/src/pages/ko/sample/[folder].astro +16 -0
- package/src/pages/ko/sample/index.astro +12 -0
- package/src/pages/ko/slides/[deck].astro +14 -0
- package/src/pages/ko/slides/index.astro +12 -0
- package/src/pages/ko/stack/[...id].astro +51 -0
- package/src/pages/ko/tags/[tag].astro +18 -0
- package/src/pages/ko/vendors/[vendor].astro +18 -0
- package/src/pages/sample/[folder].astro +16 -0
- package/src/pages/sample/index.astro +12 -0
- package/src/pages/slides/[deck].astro +14 -0
- package/src/pages/slides/index.astro +12 -0
- package/src/pages/stack/[...id].astro +51 -0
- package/src/pages/tags/[tag].astro +18 -0
- package/src/pages/vendors/[vendor].astro +18 -0
- package/src/styles/global.css +2350 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
3
|
+
import { useTranslations, type Lang } from '../i18n/ui';
|
|
4
|
+
import { getDecks, deckSlugOf } from '../lib/slides';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
lang: Lang;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { lang } = Astro.props;
|
|
11
|
+
const t = useTranslations(lang);
|
|
12
|
+
|
|
13
|
+
// Decks are locale-partitioned (content/slides/<lang>/); list this locale's and
|
|
14
|
+
// link to its localized deck page. `related` points at the concept it summarizes.
|
|
15
|
+
const decks = await getDecks(lang);
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<section class="py-6">
|
|
19
|
+
<h1 class="text-4xl font-bold tracking-tight">{t('slides.title')}</h1>
|
|
20
|
+
<p class="mt-3 max-w-2xl text-lg text-[var(--aas-muted)]">{t('slides.tagline')}</p>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<div class="mt-6 grid gap-5 sm:grid-cols-2">
|
|
24
|
+
{
|
|
25
|
+
decks.map((deck) => (
|
|
26
|
+
<div class="overflow-hidden rounded-3xl border border-[var(--aas-border)] bg-[var(--aas-panel)]">
|
|
27
|
+
<a
|
|
28
|
+
href={getRelativeLocaleUrl(lang, `slides/${deckSlugOf(deck)}/`)}
|
|
29
|
+
class="aas-lift block p-5 no-underline"
|
|
30
|
+
aria-label={deck.data.title}
|
|
31
|
+
>
|
|
32
|
+
<p class="text-xs font-medium tracking-wide text-[var(--aas-muted)] uppercase">
|
|
33
|
+
{t('slides.deck')}
|
|
34
|
+
</p>
|
|
35
|
+
<h2 class="mt-1 text-xl font-semibold text-[var(--aas-text)]">{deck.data.title}</h2>
|
|
36
|
+
<p class="mt-2 text-sm leading-relaxed text-[var(--aas-muted)]">{deck.data.description}</p>
|
|
37
|
+
<span class="mt-4 inline-flex items-center gap-1 text-sm font-medium text-[var(--aas-text)]">
|
|
38
|
+
{t('slides.open')}
|
|
39
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
40
|
+
<path d="M5 12h14" />
|
|
41
|
+
<path d="m12 5 7 7-7 7" />
|
|
42
|
+
</svg>
|
|
43
|
+
</span>
|
|
44
|
+
</a>
|
|
45
|
+
{
|
|
46
|
+
deck.data.related && (
|
|
47
|
+
<div class="border-t border-[var(--aas-border)] px-5 py-3">
|
|
48
|
+
<a
|
|
49
|
+
href={getRelativeLocaleUrl(lang, `concept/${deck.data.related}/`)}
|
|
50
|
+
class="text-sm text-[var(--aas-muted)] no-underline hover:text-[var(--aas-text)]"
|
|
51
|
+
>
|
|
52
|
+
{t('slides.readConcept')} →
|
|
53
|
+
</a>
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
</div>
|
|
58
|
+
))
|
|
59
|
+
}
|
|
60
|
+
</div>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
3
|
+
import { pricingLabels, useTranslations, type Lang } from '../i18n/ui';
|
|
4
|
+
import { getRepoStats, formatStars, stalenessOf } from '../lib/github';
|
|
5
|
+
import { pricingTags, COMPLETELY_FREE } from '../lib/pricing';
|
|
6
|
+
import { isImageRef, resolveImageUrl } from '../lib/assets';
|
|
7
|
+
import { inlineMd } from '../lib/inline-md';
|
|
8
|
+
import ToolAvatar from './ToolAvatar.astro';
|
|
9
|
+
import ToolMeta from './ToolMeta.astro';
|
|
10
|
+
import StarIcon from './StarIcon.astro';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
slug: string;
|
|
14
|
+
lang: Lang;
|
|
15
|
+
name: string;
|
|
16
|
+
formerNames?: string[];
|
|
17
|
+
vendor?: string;
|
|
18
|
+
description: string;
|
|
19
|
+
tags: string[];
|
|
20
|
+
pricing: string[];
|
|
21
|
+
license?: string;
|
|
22
|
+
language?: string;
|
|
23
|
+
category?: string;
|
|
24
|
+
logo?: string;
|
|
25
|
+
logoDark?: string;
|
|
26
|
+
repo?: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
deprecated?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { slug, lang, name, formerNames = [], vendor, description, tags, pricing, license, language, category, logo, logoDark, repo, version, deprecated } =
|
|
32
|
+
Astro.props;
|
|
33
|
+
const t = useTranslations(lang);
|
|
34
|
+
const href = getRelativeLocaleUrl(lang, `stack/${slug}/`);
|
|
35
|
+
|
|
36
|
+
// Resolved logo URL (for the blurred backdrop in the gallery view). Only image
|
|
37
|
+
// logos qualify; monograms/emoji have nothing to blur.
|
|
38
|
+
const logoUrl = isImageRef(logo) ? resolveImageUrl(logo) : undefined;
|
|
39
|
+
|
|
40
|
+
// Live stats from GitHub (build-time), with the manual `version` as fallback.
|
|
41
|
+
const stats = await getRepoStats(repo);
|
|
42
|
+
const displayVersion = stats?.version ?? version;
|
|
43
|
+
// Warn when an open-source project's latest release is getting old (6m / 1y).
|
|
44
|
+
const staleness = pricing.includes('open-source') ? stalenessOf(stats?.releasedAt) : 'fresh';
|
|
45
|
+
// Months since the latest release, for the "last updated" freshness filter
|
|
46
|
+
// (open-source only; others have no meaningful release cadence to flag).
|
|
47
|
+
const releaseMs = pricing.includes('open-source') ? Date.parse(stats?.releasedAt ?? '') : NaN;
|
|
48
|
+
const updateMonths = Number.isNaN(releaseMs)
|
|
49
|
+
? ''
|
|
50
|
+
: String(Math.floor((Date.now() - releaseMs) / (30.44 * 24 * 60 * 60 * 1000)));
|
|
51
|
+
|
|
52
|
+
// Data for client-side search / filter / sort (used on the homepage).
|
|
53
|
+
const langTokens = (language ?? '')
|
|
54
|
+
.split('/')
|
|
55
|
+
.map((s) => s.trim())
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
const searchText = `${name} ${description} ${tags.join(' ')}`.toLowerCase();
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
{/* The card is a container, not a link: the name is the stretched primary link
|
|
61
|
+
(its ::after covers the whole card), while tag links sit above it (z-10) so
|
|
62
|
+
they remain individually clickable. */}
|
|
63
|
+
<div
|
|
64
|
+
class="aas-lift relative isolate flex flex-col gap-3 overflow-hidden rounded-3xl border border-[var(--aas-border)] bg-[var(--aas-panel)] p-5 min-[1000px]:flex-row"
|
|
65
|
+
data-tool
|
|
66
|
+
data-name={name}
|
|
67
|
+
data-search={searchText}
|
|
68
|
+
data-pricing={pricingTags(pricing).join('|')}
|
|
69
|
+
data-license={license ?? ''}
|
|
70
|
+
data-langs={langTokens.join('|')}
|
|
71
|
+
data-stars={typeof stats?.stars === 'number' ? String(stats.stars) : ''}
|
|
72
|
+
data-date={stats?.releasedAt?.slice(0, 10) ?? ''}
|
|
73
|
+
data-updatemonths={updateMonths}
|
|
74
|
+
>
|
|
75
|
+
{/* Gallery view only: an enlarged, blurred copy of the logo as an ambient
|
|
76
|
+
backdrop behind the header (hidden in the standard view via CSS). */}
|
|
77
|
+
{logoUrl && (
|
|
78
|
+
<div class="aas-gallery-bg" aria-hidden="true" style={`background-image:url("${logoUrl}")`} />
|
|
79
|
+
)}
|
|
80
|
+
|
|
81
|
+
{/* Left column: the content. Grows; chips on the right don't push it down. */}
|
|
82
|
+
<div class="flex min-w-0 flex-1 flex-col" data-card-body>
|
|
83
|
+
<div class="flex min-w-0 items-center gap-3" data-card-head>
|
|
84
|
+
<ToolAvatar name={name} logo={logo} logoDark={logoDark} category={category} size={40} />
|
|
85
|
+
<div class="min-w-0">
|
|
86
|
+
<h3 class="truncate text-base font-semibold text-[var(--aas-text)]">
|
|
87
|
+
<a href={href} class="no-underline after:absolute after:inset-0 after:content-['']">
|
|
88
|
+
{name}
|
|
89
|
+
</a>
|
|
90
|
+
</h3>
|
|
91
|
+
{vendor && <p class="truncate text-xs text-[var(--aas-muted)]">{vendor}</p>}
|
|
92
|
+
{formerNames.length > 0 && (
|
|
93
|
+
<p class="truncate text-xs text-[var(--aas-muted)]">
|
|
94
|
+
{t('meta.formerly')} {formerNames[0]}
|
|
95
|
+
</p>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<p
|
|
101
|
+
class="aas-md mt-2 text-sm leading-relaxed text-[var(--aas-muted)]"
|
|
102
|
+
data-card-desc
|
|
103
|
+
set:html={inlineMd(description)}
|
|
104
|
+
/>
|
|
105
|
+
|
|
106
|
+
{/* Secondary facts: version · release date · language · license.
|
|
107
|
+
Hidden in the compact gallery view (see [data-view=gallery] in CSS). */}
|
|
108
|
+
<div data-card-meta>
|
|
109
|
+
<ToolMeta
|
|
110
|
+
lang={lang}
|
|
111
|
+
pricing={[]}
|
|
112
|
+
license={license}
|
|
113
|
+
language={language}
|
|
114
|
+
version={displayVersion}
|
|
115
|
+
released={stats?.releasedAt?.slice(0, 10)}
|
|
116
|
+
deprecated={deprecated}
|
|
117
|
+
staleness={staleness}
|
|
118
|
+
class="mt-3"
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
{/* Keyword tags — clickable links to per-tag pages, above the card overlay */}
|
|
123
|
+
{
|
|
124
|
+
tags.length > 0 && (
|
|
125
|
+
<div
|
|
126
|
+
data-card-tags
|
|
127
|
+
class="relative z-10 mt-auto flex flex-wrap gap-x-3 gap-y-1 pt-3 text-xs text-[var(--aas-muted)]"
|
|
128
|
+
>
|
|
129
|
+
{tags.slice(0, 4).map((tag) => (
|
|
130
|
+
<a
|
|
131
|
+
href={getRelativeLocaleUrl(lang, `tags/${tag}/`)}
|
|
132
|
+
class="no-underline opacity-80 hover:text-[var(--aas-accent)] hover:opacity-100"
|
|
133
|
+
>
|
|
134
|
+
#{tag}
|
|
135
|
+
</a>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
{/* Key facts (stars + pricing). On desktop they sit in a right-hand column so
|
|
143
|
+
the chip count never shifts the description; on mobile they drop below the
|
|
144
|
+
content as a row instead of stealing width from the title/description. */}
|
|
145
|
+
<div class="flex shrink-0 gap-1.5 text-xs min-[1000px]:flex-col min-[1000px]:items-end" data-card-foot>
|
|
146
|
+
{
|
|
147
|
+
typeof stats?.stars === 'number' && (
|
|
148
|
+
<span class="aas-chip inline-flex items-center gap-1 rounded-lg px-2.5 py-1">
|
|
149
|
+
<StarIcon class="text-[var(--aas-text)]" />
|
|
150
|
+
{formatStars(stats.stars)}
|
|
151
|
+
</span>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
{
|
|
155
|
+
pricingTags(pricing).map((p) => (
|
|
156
|
+
<span
|
|
157
|
+
data-pricing-tag={p}
|
|
158
|
+
title={pricingLabels[lang][p] ?? p}
|
|
159
|
+
class:list={[p === COMPLETELY_FREE ? 'aas-free' : 'aas-pricing', 'rounded-lg px-2.5 py-1']}
|
|
160
|
+
>
|
|
161
|
+
{p === 'open-source' && (
|
|
162
|
+
<svg
|
|
163
|
+
class="aas-oss-icon"
|
|
164
|
+
width="15"
|
|
165
|
+
height="15"
|
|
166
|
+
viewBox="0 0 24 24"
|
|
167
|
+
fill="none"
|
|
168
|
+
stroke="currentColor"
|
|
169
|
+
stroke-width="2"
|
|
170
|
+
stroke-linecap="round"
|
|
171
|
+
stroke-linejoin="round"
|
|
172
|
+
aria-hidden="true"
|
|
173
|
+
>
|
|
174
|
+
<polyline points="16 18 22 12 16 6" />
|
|
175
|
+
<polyline points="8 6 2 12 8 18" />
|
|
176
|
+
</svg>
|
|
177
|
+
)}
|
|
178
|
+
<span data-chip-label>{pricingLabels[lang][p] ?? p}</span>
|
|
179
|
+
</span>
|
|
180
|
+
))
|
|
181
|
+
}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|