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.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dev/local-samples-client.js +284 -0
  4. package/dev/local-samples.mjs +594 -0
  5. package/index.d.ts +12 -0
  6. package/index.mjs +124 -0
  7. package/markdown.mjs +394 -0
  8. package/package.json +52 -0
  9. package/src/components/ArrowUpRight.astro +24 -0
  10. package/src/components/ArticleCard.astro +56 -0
  11. package/src/components/ArticleDetail.astro +100 -0
  12. package/src/components/ArticleLink.astro +45 -0
  13. package/src/components/BackToTop.astro +47 -0
  14. package/src/components/BlogIndex.astro +137 -0
  15. package/src/components/Breadcrumb.astro +37 -0
  16. package/src/components/CategoryIndex.astro +115 -0
  17. package/src/components/ChevronDown.astro +23 -0
  18. package/src/components/CodeSamples.astro +141 -0
  19. package/src/components/CollapsibleGrid.astro +50 -0
  20. package/src/components/ConceptCard.astro +59 -0
  21. package/src/components/ConceptDetail.astro +102 -0
  22. package/src/components/ConceptIndex.astro +143 -0
  23. package/src/components/ConceptLink.astro +43 -0
  24. package/src/components/ConceptTools.astro +49 -0
  25. package/src/components/DeckView.astro +772 -0
  26. package/src/components/DetailTabs.astro +219 -0
  27. package/src/components/FilterControls.astro +407 -0
  28. package/src/components/Glossary.astro +264 -0
  29. package/src/components/Home.astro +90 -0
  30. package/src/components/LanguageSwitcher.astro +86 -0
  31. package/src/components/ListControls.astro +182 -0
  32. package/src/components/MermaidLoader.astro +96 -0
  33. package/src/components/PricingSection.astro +100 -0
  34. package/src/components/ProjectFileList.astro +68 -0
  35. package/src/components/ProjectFileTree.astro +95 -0
  36. package/src/components/ProjectRelatedTools.astro +70 -0
  37. package/src/components/ProjectViewer.astro +431 -0
  38. package/src/components/RelatedArticles.astro +52 -0
  39. package/src/components/RelatedConcepts.astro +60 -0
  40. package/src/components/RelatedTools.astro +55 -0
  41. package/src/components/SampleIndex.astro +464 -0
  42. package/src/components/SamplePage.astro +95 -0
  43. package/src/components/SampleProject.astro +56 -0
  44. package/src/components/Slide.astro +36 -0
  45. package/src/components/SlidesIndex.astro +60 -0
  46. package/src/components/StackCard.astro +183 -0
  47. package/src/components/StackDetail.astro +507 -0
  48. package/src/components/StarIcon.astro +21 -0
  49. package/src/components/TagIndex.astro +51 -0
  50. package/src/components/ThemeToggle.astro +106 -0
  51. package/src/components/Toc.astro +39 -0
  52. package/src/components/TocRail.astro +74 -0
  53. package/src/components/ToolAvatar.astro +133 -0
  54. package/src/components/ToolMeta.astro +95 -0
  55. package/src/components/VendorIndex.astro +51 -0
  56. package/src/content.ts +228 -0
  57. package/src/i18n/ui.ts +337 -0
  58. package/src/layouts/BaseLayout.astro +287 -0
  59. package/src/lib/articles.ts +27 -0
  60. package/src/lib/assets.ts +19 -0
  61. package/src/lib/category-tree.ts +64 -0
  62. package/src/lib/concepts.ts +35 -0
  63. package/src/lib/dates.ts +21 -0
  64. package/src/lib/facets.ts +57 -0
  65. package/src/lib/github.ts +186 -0
  66. package/src/lib/inline-md.ts +30 -0
  67. package/src/lib/pricing.ts +22 -0
  68. package/src/lib/project.ts +309 -0
  69. package/src/lib/samples.ts +88 -0
  70. package/src/lib/slides.ts +25 -0
  71. package/src/lib/stacks.ts +129 -0
  72. package/src/lib/toc-rail-client.ts +79 -0
  73. package/src/pages/article/[...id].astro +24 -0
  74. package/src/pages/article/category/[id].astro +17 -0
  75. package/src/pages/article/index.astro +12 -0
  76. package/src/pages/categories/[id].astro +25 -0
  77. package/src/pages/concept/[...id].astro +24 -0
  78. package/src/pages/concept/category/[id].astro +17 -0
  79. package/src/pages/concept/index.astro +12 -0
  80. package/src/pages/glossary.astro +12 -0
  81. package/src/pages/index.astro +9 -0
  82. package/src/pages/ko/article/[...id].astro +24 -0
  83. package/src/pages/ko/article/category/[id].astro +17 -0
  84. package/src/pages/ko/article/index.astro +12 -0
  85. package/src/pages/ko/categories/[id].astro +24 -0
  86. package/src/pages/ko/concept/[...id].astro +24 -0
  87. package/src/pages/ko/concept/category/[id].astro +17 -0
  88. package/src/pages/ko/concept/index.astro +12 -0
  89. package/src/pages/ko/glossary.astro +12 -0
  90. package/src/pages/ko/index.astro +9 -0
  91. package/src/pages/ko/sample/[folder].astro +16 -0
  92. package/src/pages/ko/sample/index.astro +12 -0
  93. package/src/pages/ko/slides/[deck].astro +14 -0
  94. package/src/pages/ko/slides/index.astro +12 -0
  95. package/src/pages/ko/stack/[...id].astro +51 -0
  96. package/src/pages/ko/tags/[tag].astro +18 -0
  97. package/src/pages/ko/vendors/[vendor].astro +18 -0
  98. package/src/pages/sample/[folder].astro +16 -0
  99. package/src/pages/sample/index.astro +12 -0
  100. package/src/pages/slides/[deck].astro +14 -0
  101. package/src/pages/slides/index.astro +12 -0
  102. package/src/pages/stack/[...id].astro +51 -0
  103. package/src/pages/tags/[tag].astro +18 -0
  104. package/src/pages/vendors/[vendor].astro +18 -0
  105. 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>