@knitli/docs-components 1.1.11 → 1.2.1
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/dist/data/products.d.ts +44 -0
- package/dist/data/products.d.ts.map +1 -0
- package/dist/data/products.js +38 -0
- package/dist/data/products.json +45 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/types/index.d.ts +2 -6
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +13 -5
- package/src/components/AllContributors.astro +38 -0
- package/src/components/Contributors.astro +30 -0
- package/src/components/DocsBreadcrumb.astro +2 -1
- package/src/components/Footer.astro +22 -32
- package/src/components/PageFrame.astro +1 -5
- package/src/components/ProductCard.astro +289 -0
- package/src/components/ProductGrid.astro +54 -0
- package/src/components/ProductShowcase.astro +22 -0
- package/src/components/SeoMeta.astro +53 -0
- package/src/data/products.json +45 -0
- package/src/data/products.ts +79 -0
- package/src/index.ts +10 -1
- package/src/types/index.ts +4 -6
- package/src/templates/AstroConfigTemplate.mjs +0 -324
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Product card for the documentation homepage. Renders a single product
|
|
8
|
+
// from the product catalog with status-driven behavior (clickable link
|
|
9
|
+
// vs static card with "coming soon" stamp).
|
|
10
|
+
|
|
11
|
+
import { Image } from "astro:assets";
|
|
12
|
+
// @ts-expect-error: 2604
|
|
13
|
+
import { Icon } from "@astrojs/starlight/components";
|
|
14
|
+
import type { StarlightIcon } from "@astrojs/starlight/types";
|
|
15
|
+
import * as logos from "@knitli/shared-layouts";
|
|
16
|
+
import type { ImageMetadata } from "astro";
|
|
17
|
+
import type { Product } from "../data/products.js";
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
/** Product data from the catalog */
|
|
21
|
+
product: Product;
|
|
22
|
+
/** Optional Starlight icon (overrides logo) */
|
|
23
|
+
icon?: StarlightIcon;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { product, icon } = Astro.props;
|
|
27
|
+
|
|
28
|
+
/** Resolve a logo key string to an actual ImageMetadata asset from shared-layouts */
|
|
29
|
+
const resolveLogoAsset = (key: string): ImageMetadata | undefined =>
|
|
30
|
+
(logos as Record<string, ImageMetadata>)[key];
|
|
31
|
+
|
|
32
|
+
const logo = product.logo ? resolveLogoAsset(product.logo) : undefined;
|
|
33
|
+
const darkLogo = product.darkLogo
|
|
34
|
+
? resolveLogoAsset(product.darkLogo)
|
|
35
|
+
: undefined;
|
|
36
|
+
const isClickable = product.docsUrl && product.status !== "coming-soon";
|
|
37
|
+
|
|
38
|
+
const imageAttrs = {
|
|
39
|
+
loading: "eager" as const,
|
|
40
|
+
decoding: "async" as const,
|
|
41
|
+
width: 48,
|
|
42
|
+
height: 48,
|
|
43
|
+
alt: `${product.name} logo`,
|
|
44
|
+
};
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
{isClickable ? (
|
|
48
|
+
<a
|
|
49
|
+
href={product.docsUrl}
|
|
50
|
+
target={product.external ? "_blank" : "_self"}
|
|
51
|
+
rel={product.external ? "noopener noreferrer" : undefined}
|
|
52
|
+
>
|
|
53
|
+
<article class="card sl-flex linkcard" aria-label="button" tabindex="0">
|
|
54
|
+
<div class="title-row sl-flex">
|
|
55
|
+
{(logo || darkLogo) && (
|
|
56
|
+
<div class="product-logo">
|
|
57
|
+
{darkLogo && (
|
|
58
|
+
<Image
|
|
59
|
+
src={darkLogo}
|
|
60
|
+
{...imageAttrs}
|
|
61
|
+
class:list={{ "light:sl-hidden": Boolean(logo) }}
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
{logo && (
|
|
65
|
+
<Image
|
|
66
|
+
src={logo}
|
|
67
|
+
{...imageAttrs}
|
|
68
|
+
class="dark:sl-hidden"
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
<p class="title sl-flex">
|
|
74
|
+
{icon && <Icon name={icon} class="icon" size="1.333em" />}
|
|
75
|
+
<span>{product.name}</span>
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="body">
|
|
79
|
+
<p>
|
|
80
|
+
{product.description}
|
|
81
|
+
{product.highlight && (
|
|
82
|
+
<>
|
|
83
|
+
<br /><br />
|
|
84
|
+
<strong>{product.highlight}</strong>
|
|
85
|
+
</>
|
|
86
|
+
)}
|
|
87
|
+
{product.footnote && (
|
|
88
|
+
<>
|
|
89
|
+
<br /><br />
|
|
90
|
+
<em>{product.footnote}</em>
|
|
91
|
+
</>
|
|
92
|
+
)}
|
|
93
|
+
</p>
|
|
94
|
+
<slot />
|
|
95
|
+
</div>
|
|
96
|
+
</article>
|
|
97
|
+
</a>
|
|
98
|
+
) : (
|
|
99
|
+
<article class="card sl-flex">
|
|
100
|
+
<div class="title-row sl-flex">
|
|
101
|
+
{(logo || darkLogo) && (
|
|
102
|
+
<div class="product-logo">
|
|
103
|
+
{darkLogo && (
|
|
104
|
+
<Image
|
|
105
|
+
src={darkLogo}
|
|
106
|
+
{...imageAttrs}
|
|
107
|
+
class:list={{ "light:sl-hidden": Boolean(logo) }}
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
{logo && (
|
|
111
|
+
<Image
|
|
112
|
+
src={logo}
|
|
113
|
+
{...imageAttrs}
|
|
114
|
+
class="dark:sl-hidden"
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
<p class="title sl-flex">
|
|
120
|
+
{icon && <Icon name={icon} class="icon" size="1.333em" />}
|
|
121
|
+
<span>{product.name}</span>
|
|
122
|
+
</p>
|
|
123
|
+
</div>
|
|
124
|
+
<div class="body">
|
|
125
|
+
<p>
|
|
126
|
+
{product.description}
|
|
127
|
+
{product.highlight && (
|
|
128
|
+
<>
|
|
129
|
+
<br /><br />
|
|
130
|
+
<strong>{product.highlight}</strong>
|
|
131
|
+
</>
|
|
132
|
+
)}
|
|
133
|
+
{product.footnote && (
|
|
134
|
+
<>
|
|
135
|
+
<br /><br />
|
|
136
|
+
<em>{product.footnote}</em>
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
</p>
|
|
140
|
+
<slot />
|
|
141
|
+
</div>
|
|
142
|
+
{product.status === "coming-soon" && (
|
|
143
|
+
<span class="status-badge">Docs coming soon</span>
|
|
144
|
+
)}
|
|
145
|
+
</article>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
<style>
|
|
149
|
+
@layer starlight.components {
|
|
150
|
+
.card {
|
|
151
|
+
--sl-card-border: var(--knitli-aubergine);
|
|
152
|
+
--sl-card-bg: var(--sl-color-purple-low);
|
|
153
|
+
border: 1px solid var(--sl-color-gray-5);
|
|
154
|
+
background-color: var(--sl-color-black);
|
|
155
|
+
padding: clamp(1rem, calc(0.125rem + 3vw), 2.5rem);
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
gap: clamp(0.5rem, calc(0.125rem + 1vw), 1rem);
|
|
158
|
+
height: 100%;
|
|
159
|
+
position: relative;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
}
|
|
162
|
+
.title {
|
|
163
|
+
font-weight: 600;
|
|
164
|
+
font-size: var(--sl-text-h4);
|
|
165
|
+
color: var(--sl-color-white);
|
|
166
|
+
line-height: var(--sl-line-height-headings);
|
|
167
|
+
text-decoration: none;
|
|
168
|
+
gap: 1rem;
|
|
169
|
+
align-items: center;
|
|
170
|
+
margin: 0;
|
|
171
|
+
}
|
|
172
|
+
.card .body {
|
|
173
|
+
margin: 0;
|
|
174
|
+
text-decoration: none;
|
|
175
|
+
font-size: clamp(
|
|
176
|
+
var(--sl-text-sm),
|
|
177
|
+
calc(0.5rem + 1vw),
|
|
178
|
+
var(--sl-text-body)
|
|
179
|
+
);
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
}
|
|
183
|
+
.card .body > p {
|
|
184
|
+
flex: 1;
|
|
185
|
+
line-height: 1.6;
|
|
186
|
+
}
|
|
187
|
+
/* Clickable card styles */
|
|
188
|
+
a {
|
|
189
|
+
text-decoration: none !important;
|
|
190
|
+
color: var(--sl-color-text) !important;
|
|
191
|
+
}
|
|
192
|
+
a:hover {
|
|
193
|
+
text-decoration: none !important;
|
|
194
|
+
color: var(--sl-color-text) !important;
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
}
|
|
197
|
+
a .linkcard {
|
|
198
|
+
transition:
|
|
199
|
+
background-color 0.3s,
|
|
200
|
+
border-color 0.3s;
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
}
|
|
203
|
+
a .linkcard:hover,
|
|
204
|
+
a:hover .linkcard {
|
|
205
|
+
background-color: var(--sl-color-bg-inline-code);
|
|
206
|
+
border: 0.1rem solid var(--knitli-rust);
|
|
207
|
+
scale: 1.02;
|
|
208
|
+
transition: transform 0.3s ease;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Remove text decoration from all elements inside linked cards */
|
|
212
|
+
a .linkcard *,
|
|
213
|
+
a .linkcard .title,
|
|
214
|
+
a .linkcard .body,
|
|
215
|
+
a .linkcard .body p {
|
|
216
|
+
text-decoration: none !important;
|
|
217
|
+
color: inherit;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
a .linkcard .title {
|
|
221
|
+
color: var(--sl-color-white);
|
|
222
|
+
}
|
|
223
|
+
.title-row {
|
|
224
|
+
gap: 1rem;
|
|
225
|
+
align-items: center;
|
|
226
|
+
margin-bottom: 1rem;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.card .icon {
|
|
230
|
+
border: 1px solid var(--sl-card-border);
|
|
231
|
+
background-color: var(--sl-card-bg);
|
|
232
|
+
padding: 0.2em;
|
|
233
|
+
border-radius: 0.25rem;
|
|
234
|
+
flex-shrink: 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Add padding when stamp is present so description doesn't overlap */
|
|
238
|
+
.card:has(.status-badge) .body {
|
|
239
|
+
padding-bottom: 3rem;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.product-logo {
|
|
243
|
+
width: 60px;
|
|
244
|
+
height: 60px;
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
flex-shrink: 0;
|
|
249
|
+
}
|
|
250
|
+
.product-logo img {
|
|
251
|
+
width: 100%;
|
|
252
|
+
height: 100%;
|
|
253
|
+
object-fit: contain;
|
|
254
|
+
}
|
|
255
|
+
.product-logo img:hover {
|
|
256
|
+
scale: 1.05;
|
|
257
|
+
transition: transform 0.3s ease;
|
|
258
|
+
}
|
|
259
|
+
.status-badge {
|
|
260
|
+
position: absolute;
|
|
261
|
+
bottom: 1.25rem;
|
|
262
|
+
right: 1.75rem;
|
|
263
|
+
transform: rotate(-8deg);
|
|
264
|
+
transform-origin: center;
|
|
265
|
+
|
|
266
|
+
/* Double-border rubber stamp effect */
|
|
267
|
+
border: 2px solid var(--sl-color-accent);
|
|
268
|
+
outline: 1px solid var(--sl-color-accent);
|
|
269
|
+
outline-offset: 3px;
|
|
270
|
+
border-radius: 1px;
|
|
271
|
+
|
|
272
|
+
color: var(--sl-color-accent);
|
|
273
|
+
background: transparent;
|
|
274
|
+
|
|
275
|
+
font-size: 0.6rem;
|
|
276
|
+
font-weight: 800;
|
|
277
|
+
text-transform: uppercase;
|
|
278
|
+
letter-spacing: 0.18em;
|
|
279
|
+
padding: 0.3em 0.75em;
|
|
280
|
+
|
|
281
|
+
/* Worn ink look */
|
|
282
|
+
opacity: 0.75;
|
|
283
|
+
white-space: nowrap;
|
|
284
|
+
z-index: 10;
|
|
285
|
+
pointer-events: none;
|
|
286
|
+
user-select: none;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Product grid layout — 3-column on desktop with optional staggered offset.
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
staggered?: boolean;
|
|
11
|
+
stagger?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { staggered, stagger = staggered ?? false } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<div class:list={["card-grid", { stagger }]}><slot /></div>
|
|
18
|
+
|
|
19
|
+
<style>
|
|
20
|
+
/** biome-ignore: lint/complexity/noImportantStyles */
|
|
21
|
+
@layer starlight.components {
|
|
22
|
+
.card-grid {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: 100%;
|
|
25
|
+
gap: 1rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.card-grid > :global(*) {
|
|
29
|
+
margin-top: 0 !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (min-width: 50rem) {
|
|
33
|
+
.card-grid {
|
|
34
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
35
|
+
gap: 1.5rem;
|
|
36
|
+
}
|
|
37
|
+
.stagger {
|
|
38
|
+
--stagger-height: 2.5rem;
|
|
39
|
+
padding-top: var(--stagger-height);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Pattern: Up, Baseline, Up */
|
|
43
|
+
.stagger > :global(*):nth-child(3n + 1) {
|
|
44
|
+
transform: translateY(calc(-1 * var(--stagger-height)));
|
|
45
|
+
}
|
|
46
|
+
.stagger > :global(*):nth-child(3n + 2) {
|
|
47
|
+
transform: translateY(0);
|
|
48
|
+
}
|
|
49
|
+
.stagger > :global(*):nth-child(3n + 3) {
|
|
50
|
+
transform: translateY(calc(-1 * var(--stagger-height)));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Composite component that renders the full product showcase grid
|
|
8
|
+
// from the product catalog. Requires zero configuration — just
|
|
9
|
+
// import and use: <ProductShowcase />
|
|
10
|
+
|
|
11
|
+
import { featuredProducts } from "../data/products.js";
|
|
12
|
+
import ProductCard from "./ProductCard.astro";
|
|
13
|
+
import ProductGrid from "./ProductGrid.astro";
|
|
14
|
+
|
|
15
|
+
const products = featuredProducts();
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<ProductGrid staggered>
|
|
19
|
+
{products.map((product) => (
|
|
20
|
+
<ProductCard key={product.name} product={product} />
|
|
21
|
+
))}
|
|
22
|
+
</ProductGrid>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Knitli-branded wrapper around astro-seo-meta Seo component.
|
|
8
|
+
// Pre-applies brand theme color, color scheme, and OG defaults.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// <SeoMeta title="Getting Started" description="..." />
|
|
12
|
+
// <SeoMeta title="API Reference" twitter={{ card: "summary" }} />
|
|
13
|
+
|
|
14
|
+
import { Seo } from "astro-seo-meta";
|
|
15
|
+
|
|
16
|
+
type ColorScheme = "normal" | "light dark" | "dark light" | "only light";
|
|
17
|
+
|
|
18
|
+
export interface Props {
|
|
19
|
+
title?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
keywords?: string[];
|
|
22
|
+
icon?: string;
|
|
23
|
+
/** Theme color for browser chrome. Defaults to Knitli aubergine. */
|
|
24
|
+
themeColor?: string;
|
|
25
|
+
colorScheme?: ColorScheme;
|
|
26
|
+
facebook?: {
|
|
27
|
+
image?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
url?: string;
|
|
30
|
+
};
|
|
31
|
+
twitter?: {
|
|
32
|
+
image?: string;
|
|
33
|
+
card?: string;
|
|
34
|
+
site?: string;
|
|
35
|
+
};
|
|
36
|
+
robots?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
themeColor = "#1e061b",
|
|
41
|
+
colorScheme = "light dark",
|
|
42
|
+
twitter = {},
|
|
43
|
+
...rest
|
|
44
|
+
} = Astro.props;
|
|
45
|
+
|
|
46
|
+
const mergedTwitter = {
|
|
47
|
+
card: "summary_large_image",
|
|
48
|
+
site: "@knitli_inc",
|
|
49
|
+
...twitter,
|
|
50
|
+
};
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<Seo themeColor={themeColor} colorScheme={colorScheme} twitter={mergedTwitter} {...rest} />
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"slug": "recoco",
|
|
4
|
+
"name": "Recoco",
|
|
5
|
+
"tagline": "Intelligent data pipelines",
|
|
6
|
+
"description": "Build intelligent data pipelines and ETL workflows. Only process changed data.",
|
|
7
|
+
"highlight": "Pure Rust, minimal dependencies, maximum efficiency.",
|
|
8
|
+
"status": "available",
|
|
9
|
+
"docsUrl": "/recoco/",
|
|
10
|
+
"external": false,
|
|
11
|
+
"logo": "recocoLogoSm",
|
|
12
|
+
"darkLogo": "recocoLogoSm",
|
|
13
|
+
"order": 1,
|
|
14
|
+
"featured": true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"slug": "codeweaver",
|
|
18
|
+
"name": "CodeWeaver",
|
|
19
|
+
"tagline": "AI code context engine",
|
|
20
|
+
"description": "Your AI code assistants can do better.",
|
|
21
|
+
"highlight": "CodeWeaver gives AI agents tailored code context and project understanding, stopping hallucinations, cutting token waste, and supercharging your AI coding workflows.",
|
|
22
|
+
"footnote": "Now in Alpha.",
|
|
23
|
+
"status": "coming-soon",
|
|
24
|
+
"external": false,
|
|
25
|
+
"logo": "codeweaverPrimary",
|
|
26
|
+
"darkLogo": "codeweaverReverse",
|
|
27
|
+
"order": 2,
|
|
28
|
+
"featured": true
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"slug": "thread",
|
|
32
|
+
"name": "Thread",
|
|
33
|
+
"tagline": "Ultra-fast codebase analysis",
|
|
34
|
+
"description": "An ultra-fast, Rust-native, codebase analysis platform.",
|
|
35
|
+
"highlight": "Real-time code understanding and navigation for the largest codebases. Delivers 50x+ speed improvements over comparable tools with minimal resource usage. Works locally, on the edge, or in the cloud.",
|
|
36
|
+
"footnote": "Limited features available now, full release coming soon.",
|
|
37
|
+
"status": "available",
|
|
38
|
+
"docsUrl": "https://docs.rs/thread",
|
|
39
|
+
"external": true,
|
|
40
|
+
"logo": "threadLogoLight",
|
|
41
|
+
"darkLogo": "threadLogoDark",
|
|
42
|
+
"order": 3,
|
|
43
|
+
"featured": true
|
|
44
|
+
}
|
|
45
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Knitli Inc.
|
|
2
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
3
|
+
//
|
|
4
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
5
|
+
//
|
|
6
|
+
// Product catalog — single source of truth for all Knitli product metadata.
|
|
7
|
+
// Edit products.json to add, remove, reorder, or update products.
|
|
8
|
+
|
|
9
|
+
import catalogData from "./products.json" with { type: "json" };
|
|
10
|
+
|
|
11
|
+
export type ProductStatus = "available" | "coming-soon" | "alpha" | "beta";
|
|
12
|
+
|
|
13
|
+
export interface Product {
|
|
14
|
+
/** URL slug used in docs paths, e.g. "recoco" */
|
|
15
|
+
slug: string;
|
|
16
|
+
/** Display name */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Short tagline for cards/nav */
|
|
19
|
+
tagline: string;
|
|
20
|
+
/** Primary description */
|
|
21
|
+
description: string;
|
|
22
|
+
/** Bold/emphasized callout text */
|
|
23
|
+
highlight?: string;
|
|
24
|
+
/** Italicized footnote (e.g. "Now in Alpha.") */
|
|
25
|
+
footnote?: string;
|
|
26
|
+
/** Product status — drives card rendering and footer links */
|
|
27
|
+
status: ProductStatus;
|
|
28
|
+
/** Docs URL — relative for hosted docs, absolute for external */
|
|
29
|
+
docsUrl?: string;
|
|
30
|
+
/** Whether docs are hosted externally (e.g. docs.rs) */
|
|
31
|
+
external?: boolean;
|
|
32
|
+
/** Logo asset key from DocsAssets */
|
|
33
|
+
logo: string;
|
|
34
|
+
/** Dark-mode logo asset key from DocsAssets */
|
|
35
|
+
darkLogo?: string;
|
|
36
|
+
/** Sort order for display */
|
|
37
|
+
order: number;
|
|
38
|
+
/** Set false to hide from docs-home without deleting */
|
|
39
|
+
featured?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** All products from the catalog */
|
|
43
|
+
export const products: Product[] = catalogData as Product[];
|
|
44
|
+
|
|
45
|
+
/** Products marked as featured (or not explicitly unfeatured), sorted by order */
|
|
46
|
+
export function featuredProducts(): Product[] {
|
|
47
|
+
return products
|
|
48
|
+
.filter((p) => p.featured !== false)
|
|
49
|
+
.sort((a, b) => a.order - b.order);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Products with available documentation (not coming-soon) */
|
|
53
|
+
export function availableProducts(): Product[] {
|
|
54
|
+
return products.filter((p) => p.status !== "coming-soon");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Look up a single product by slug */
|
|
58
|
+
export function getProduct(slug: string): Product | undefined {
|
|
59
|
+
return products.find((p) => p.slug === slug);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Look up a product by display name */
|
|
63
|
+
export function getProductByName(name: string): Product | undefined {
|
|
64
|
+
return products.find((p) => p.name === name);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** All valid product display names, derived from catalog */
|
|
68
|
+
export function productNames(): string[] {
|
|
69
|
+
return products.map((p) => p.name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Generate sitemap URLs for products with hosted (non-external) docs */
|
|
73
|
+
export function getProductSitemaps(
|
|
74
|
+
baseUrl = "https://docs.knitli.com",
|
|
75
|
+
): string[] {
|
|
76
|
+
return products
|
|
77
|
+
.filter((p) => p.docsUrl && !p.external && p.status !== "coming-soon")
|
|
78
|
+
.map((p) => `${baseUrl}/${p.slug}/sitemap-index.xml`);
|
|
79
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
// Main exports for @knitli/docs-components package
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
export * from "./data/products.js";
|
|
9
|
+
// Export type definitions and product catalog
|
|
9
10
|
export * from "./types/index.js";
|
|
10
11
|
|
|
11
12
|
import { faviconIco, faviconSvg } from "./assets/favicon/index.js";
|
|
@@ -54,6 +55,14 @@ const components = {
|
|
|
54
55
|
DocsBreadcrumb: async () => await import("./components/DocsBreadcrumb.astro"),
|
|
55
56
|
DocsFooter: async () => await import("./components/Footer.astro"),
|
|
56
57
|
PageFrame: async () => await import("./components/PageFrame.astro"),
|
|
58
|
+
Contributors: async () => await import("./components/Contributors.astro"),
|
|
59
|
+
AllContributors: async () =>
|
|
60
|
+
await import("./components/AllContributors.astro"),
|
|
61
|
+
SeoMeta: async () => await import("./components/SeoMeta.astro"),
|
|
62
|
+
ProductCard: async () => await import("./components/ProductCard.astro"),
|
|
63
|
+
ProductGrid: async () => await import("./components/ProductGrid.astro"),
|
|
64
|
+
ProductShowcase: async () =>
|
|
65
|
+
await import("./components/ProductShowcase.astro"),
|
|
57
66
|
};
|
|
58
67
|
|
|
59
68
|
export default {
|
package/src/types/index.ts
CHANGED
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
// TypeScript type definitions for docs-components package
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
export type ProductName = "Recoco" | "CodeWeaver" | "Thread";
|
|
8
|
+
// Product catalog types and helpers are exported from ../data/products.ts
|
|
9
|
+
// and re-exported via the package's main index.ts entry point.
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* Site URLs configuration
|
|
@@ -26,7 +24,7 @@ export interface DocsBreadcrumbProps {
|
|
|
26
24
|
/**
|
|
27
25
|
* Current product name
|
|
28
26
|
*/
|
|
29
|
-
product?:
|
|
27
|
+
product?: string;
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
30
|
* URL to the product's documentation home
|
|
@@ -42,5 +40,5 @@ export interface DocsBreadcrumbProps {
|
|
|
42
40
|
* Visual variant of the breadcrumb
|
|
43
41
|
* @default 'default'
|
|
44
42
|
*/
|
|
45
|
-
variant?: "default" | "compact";
|
|
43
|
+
variant?: "default" | "compact" | "inline";
|
|
46
44
|
}
|