@grimoire-rs/indexer 0.1.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 (114) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +48 -0
  3. package/dist/cli/build.d.ts +6 -0
  4. package/dist/cli/build.d.ts.map +1 -0
  5. package/dist/cli/build.js +29 -0
  6. package/dist/cli/build.js.map +1 -0
  7. package/dist/cli/exit.d.ts +28 -0
  8. package/dist/cli/exit.d.ts.map +1 -0
  9. package/dist/cli/exit.js +33 -0
  10. package/dist/cli/exit.js.map +1 -0
  11. package/dist/cli/index.d.ts +3 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +8 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +40 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +317 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/cli/main.d.ts +2 -0
  20. package/dist/cli/main.d.ts.map +1 -0
  21. package/dist/cli/main.js +112 -0
  22. package/dist/cli/main.js.map +1 -0
  23. package/dist/cli/validate.d.ts +18 -0
  24. package/dist/cli/validate.d.ts.map +1 -0
  25. package/dist/cli/validate.js +139 -0
  26. package/dist/cli/validate.js.map +1 -0
  27. package/dist/config.d.ts +76 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +134 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/data/index.d.ts +40 -0
  32. package/dist/data/index.d.ts.map +1 -0
  33. package/dist/data/index.js +103 -0
  34. package/dist/data/index.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +4 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/integration.d.ts +2 -0
  40. package/dist/integration.d.ts.map +1 -0
  41. package/dist/integration.js +4 -0
  42. package/dist/integration.js.map +1 -0
  43. package/dist/renderer/astro/components/Catalog.d.ts +6 -0
  44. package/dist/renderer/astro/components/Catalog.d.ts.map +1 -0
  45. package/dist/renderer/astro/components/Catalog.js +73 -0
  46. package/dist/renderer/astro/components/Catalog.js.map +1 -0
  47. package/dist/renderer/astro/components/Catalog.tsx +256 -0
  48. package/dist/renderer/astro/components/CopyButton.astro +24 -0
  49. package/dist/renderer/astro/components/VersionMenu.astro +105 -0
  50. package/dist/renderer/astro/content.config.ts +30 -0
  51. package/dist/renderer/astro/layouts/Base.astro +623 -0
  52. package/dist/renderer/astro/lib/catalog.d.ts +7 -0
  53. package/dist/renderer/astro/lib/catalog.d.ts.map +1 -0
  54. package/dist/renderer/astro/lib/catalog.js +61 -0
  55. package/dist/renderer/astro/lib/catalog.js.map +1 -0
  56. package/dist/renderer/astro/lib/catalog.ts +69 -0
  57. package/dist/renderer/astro/lib/data.d.ts +3 -0
  58. package/dist/renderer/astro/lib/data.d.ts.map +1 -0
  59. package/dist/renderer/astro/lib/data.js +4 -0
  60. package/dist/renderer/astro/lib/data.js.map +1 -0
  61. package/dist/renderer/astro/lib/data.ts +12 -0
  62. package/dist/renderer/astro/pages/index.astro +69 -0
  63. package/dist/renderer/astro/pages/p/[...slug].astro +367 -0
  64. package/dist/renderer/index.d.ts +20 -0
  65. package/dist/renderer/index.d.ts.map +1 -0
  66. package/dist/renderer/index.js +192 -0
  67. package/dist/renderer/index.js.map +1 -0
  68. package/dist/renderer/public/favicon.svg +4 -0
  69. package/dist/renderer/types.d.ts +34 -0
  70. package/dist/renderer/types.d.ts.map +1 -0
  71. package/dist/renderer/types.js +4 -0
  72. package/dist/renderer/types.js.map +1 -0
  73. package/dist/validate/adapters/files.d.ts +17 -0
  74. package/dist/validate/adapters/files.d.ts.map +1 -0
  75. package/dist/validate/adapters/files.js +50 -0
  76. package/dist/validate/adapters/files.js.map +1 -0
  77. package/dist/validate/adapters/forge.d.ts +25 -0
  78. package/dist/validate/adapters/forge.d.ts.map +1 -0
  79. package/dist/validate/adapters/forge.js +100 -0
  80. package/dist/validate/adapters/forge.js.map +1 -0
  81. package/dist/validate/adapters/http.d.ts +24 -0
  82. package/dist/validate/adapters/http.d.ts.map +1 -0
  83. package/dist/validate/adapters/http.js +75 -0
  84. package/dist/validate/adapters/http.js.map +1 -0
  85. package/dist/validate/adapters/registry.d.ts +16 -0
  86. package/dist/validate/adapters/registry.d.ts.map +1 -0
  87. package/dist/validate/adapters/registry.js +91 -0
  88. package/dist/validate/adapters/registry.js.map +1 -0
  89. package/dist/validate/core/metadata.d.ts +67 -0
  90. package/dist/validate/core/metadata.d.ts.map +1 -0
  91. package/dist/validate/core/metadata.js +149 -0
  92. package/dist/validate/core/metadata.js.map +1 -0
  93. package/dist/validate/core/ownership.d.ts +43 -0
  94. package/dist/validate/core/ownership.d.ts.map +1 -0
  95. package/dist/validate/core/ownership.js +33 -0
  96. package/dist/validate/core/ownership.js.map +1 -0
  97. package/dist/validate/core/paths.d.ts +21 -0
  98. package/dist/validate/core/paths.d.ts.map +1 -0
  99. package/dist/validate/core/paths.js +34 -0
  100. package/dist/validate/core/paths.js.map +1 -0
  101. package/dist/validate/index.d.ts +40 -0
  102. package/dist/validate/index.d.ts.map +1 -0
  103. package/dist/validate/index.js +206 -0
  104. package/dist/validate/index.js.map +1 -0
  105. package/package.json +50 -0
  106. package/templates/README.md +48 -0
  107. package/templates/github-pages.yml +24 -0
  108. package/templates/github-validate.yml +36 -0
  109. package/templates/gitignore +9 -0
  110. package/templates/gitlab-ci.yml +13 -0
  111. package/templates/publish.toml +17 -0
  112. package/templates/reusable/gitlab-index-ci.yml +61 -0
  113. package/templates/reusable/index-pages.yml +80 -0
  114. package/templates/reusable/index-validate.yml +88 -0
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+
4
+ // Presentational helpers only — pure functions, safe to pull into the
5
+ // client bundle. The build-time payload lives in `./data.ts`; the package
6
+ // data itself is compiled by `src/data/` into `<outDir>/all.json`, so
7
+ // nothing here walks the index tree.
8
+ export type { CatalogPackage } from "../../types.js";
9
+
10
+ // Publishing 0.10.0 also moves the rolling tags 0.10, 0 and latest, so the
11
+ // full tag list is mostly history. Return just the current release's chain
12
+ // (latest | 0 | 0.10 | 0.10.0). `tags` must already be sorted newest-first.
13
+ export function versionCascade(version: string | undefined, tags: string[]): string[] {
14
+ const pinned = version ?? tags.find((t) => /^v?\d/.test(t));
15
+ // Split the raw string so a "v" prefix rides along into the rolling tags
16
+ // it was published under ("v1.2.3" -> "v1", "v1.2"), same as unprefixed.
17
+ const [major, minor] = (pinned ?? "").split(".");
18
+ const chain = [...new Set(["latest", major, `${major}.${minor}`, pinned])].filter(
19
+ (t): t is string => !!t && tags.includes(t),
20
+ );
21
+ // Tags nothing recognises as a version (["stable", "beta"]) yield no chain
22
+ // — front the newest few rather than an empty strip.
23
+ return chain.length > 0 ? chain : tags.slice(0, 4);
24
+ }
25
+
26
+ // Tags are published version strings (e.g. "v1.2.0"); sort latest first,
27
+ // numeric-aware so "1.10.0" outranks "1.9.0".
28
+ export function compareVersions(a: string, b: string): number {
29
+ const parts = (s: string) => s.replace(/^v/i, "").split(/[.-]/);
30
+ const pa = parts(a);
31
+ const pb = parts(b);
32
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
33
+ const [na, nb] = [Number(pa[i]), Number(pb[i])];
34
+ if (!Number.isNaN(na) && !Number.isNaN(nb)) {
35
+ if (na !== nb) return nb - na;
36
+ } else if (pa[i] !== pb[i]) {
37
+ return (pb[i] ?? "").localeCompare(pa[i] ?? "");
38
+ }
39
+ }
40
+ return 0;
41
+ }
42
+
43
+ // MDN-standard Intl.RelativeTimeFormat rollup (no date library).
44
+ const RTF = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
45
+ const TIME_DIVISIONS: [number, Intl.RelativeTimeFormatUnit][] = [
46
+ [60, "seconds"],
47
+ [60, "minutes"],
48
+ [24, "hours"],
49
+ [7, "days"],
50
+ [4.34524, "weeks"],
51
+ [12, "months"],
52
+ [Infinity, "years"],
53
+ ];
54
+
55
+ export function timeAgo(iso: string): string {
56
+ const ms = new Date(iso).getTime();
57
+ if (!Number.isFinite(ms)) return ""; // unparseable: caller hides the element
58
+ let duration = (ms - Date.now()) / 1000;
59
+ for (const [amount, unit] of TIME_DIVISIONS) {
60
+ if (Math.abs(duration) < amount) return RTF.format(Math.round(duration), unit);
61
+ duration /= amount;
62
+ }
63
+ return RTF.format(Math.round(duration), "years");
64
+ }
65
+
66
+ /** `vscode://<publisher.extension>/open?repo=<ref>`, or null when disabled. */
67
+ export function vscodeUrl(extension: string | null, ref: string): string | null {
68
+ return extension ? `vscode://${extension}/open?repo=${encodeURIComponent(ref)}` : null;
69
+ }
@@ -0,0 +1,3 @@
1
+ import type { GrimoireData } from "../../types.js";
2
+ export declare const data: GrimoireData;
3
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/renderer/astro/lib/data.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,eAAO,MAAM,IAAI,EAAE,YAAgC,CAAC"}
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+ export const data = __GRIMOIRE_DATA__;
4
+ //# sourceMappingURL=data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.js","sourceRoot":"","sources":["../../../../src/renderer/astro/lib/data.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAUtC,MAAM,CAAC,MAAM,IAAI,GAAiB,iBAAiB,CAAC"}
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 The Grimoire Authors
3
+
4
+ // The build-time bridge. `buildSite` replaces `__GRIMOIRE_DATA__` with a
5
+ // JSON literal through Vite `define`, so this module is server-only by
6
+ // construction — importing it from a hydrated island would inline the
7
+ // whole catalog into the client bundle. Islands take what they need as props.
8
+ import type { GrimoireData } from "../../types.js";
9
+
10
+ declare const __GRIMOIRE_DATA__: GrimoireData;
11
+
12
+ export const data: GrimoireData = __GRIMOIRE_DATA__;
@@ -0,0 +1,69 @@
1
+ ---
2
+ import Base from "../layouts/Base.astro";
3
+ import Catalog from "../components/Catalog.tsx";
4
+ import CopyButton from "../components/CopyButton.astro";
5
+ import { data } from "../lib/data";
6
+
7
+ const { config, packages } = data;
8
+
9
+ // The "add this index" ergonomic (`helm repo add`): the command a visitor
10
+ // needs to point their own grim at *this* index, plus the equivalent
11
+ // hand-edit. Absent `registry` config means the index has no public URL to
12
+ // hand out, so the block is omitted rather than guessed.
13
+ const registryRows = config.registry
14
+ ? [
15
+ {
16
+ os: "add index",
17
+ command: `grim config registry add ${config.registry.alias} --index ${config.registry.index}`,
18
+ block: false,
19
+ label: "Copy the registry add command",
20
+ },
21
+ {
22
+ os: "grimoire.toml",
23
+ command: `[[registries]]\nalias = "${config.registry.alias}"\nindex = "${config.registry.index}"`,
24
+ block: true,
25
+ label: "Copy the registries TOML entry",
26
+ },
27
+ ]
28
+ : [];
29
+ ---
30
+
31
+ <Base title={config.brand} description={config.description}>
32
+ <div class="hero">
33
+ <h1>{config.brand}</h1>
34
+ <p>
35
+ {config.tagline}
36
+ {
37
+ config.installDocsUrl && (
38
+ <Fragment>
39
+ {" "}
40
+ Copy an <code>add</code> command below — or{" "}
41
+ <a href={config.installDocsUrl}>install grim</a> first.
42
+ </Fragment>
43
+ )
44
+ }
45
+ </p>
46
+ {
47
+ (config.install.length > 0 || registryRows.length > 0) && (
48
+ <div class="install">
49
+ {config.install.map((row) => (
50
+ <div class="install-row">
51
+ <span class="os">{row.os}</span>
52
+ <code>{row.command}</code>
53
+ <CopyButton value={row.command} label={`Copy the ${row.os} install command`} />
54
+ </div>
55
+ ))}
56
+ {registryRows.map((row) => (
57
+ <div class="install-row">
58
+ <span class="os">{row.os}</span>
59
+ <code class:list={[{ block: row.block }]}>{row.command}</code>
60
+ <CopyButton value={row.command} label={row.label} />
61
+ </div>
62
+ ))}
63
+ </div>
64
+ )
65
+ }
66
+ </div>
67
+
68
+ <Catalog client:load packages={packages} vscodeExtension={config.vscodeExtension} />
69
+ </Base>
@@ -0,0 +1,367 @@
1
+ ---
2
+ // Frozen public URL: /p/<namespace>/<name>/. grim resolves against it, the
3
+ // VS Code deep link points at it, and search engines index it — the route
4
+ // shape is a contract, not an implementation detail.
5
+ import Base from "../../layouts/Base.astro";
6
+ import VersionMenu from "../../components/VersionMenu.astro";
7
+ import CopyButton from "../../components/CopyButton.astro";
8
+ import { compareVersions, versionCascade, vscodeUrl, type CatalogPackage } from "../../lib/catalog";
9
+ import { data } from "../../lib/data";
10
+ import { getEntry, render } from "astro:content";
11
+
12
+ export function getStaticPaths() {
13
+ return data.packages.map((pkg) => ({
14
+ params: { slug: `${pkg.namespace}/${pkg.name}` },
15
+ props: { pkg },
16
+ }));
17
+ }
18
+
19
+ const { config } = data;
20
+ const { pkg } = Astro.props as { pkg: CatalogPackage };
21
+ const id = `${pkg.namespace}/${pkg.name}`;
22
+
23
+ const readmeEntry = await getEntry("readmes", id);
24
+ const changelogEntry = await getEntry("changelogs", id);
25
+ const Readme = readmeEntry ? (await render(readmeEntry)).Content : null;
26
+ const Changelog = changelogEntry ? (await render(changelogEntry)).Content : null;
27
+
28
+ const tags = [...(pkg.tags ?? [])].sort(compareVersions);
29
+ const cascade = versionCascade(pkg.version, tags);
30
+ const older = tags.filter((t) => !cascade.includes(t));
31
+
32
+ const title = pkg.title ?? pkg.name;
33
+ const description = pkg.summary ?? pkg.description ?? `${pkg.kind} package: ${pkg.ref}`;
34
+ const vscode = vscodeUrl(config.vscodeExtension, pkg.ref);
35
+ const addCommand = `grim add ${pkg.ref}`;
36
+ ---
37
+
38
+ <Base title={`${title} — ${config.brand}`} description={description} image={pkg.logo}>
39
+ <div class="detail">
40
+ <header class="detail-head">
41
+ {
42
+ pkg.logo ? (
43
+ <img class="tile logo" src={pkg.logo} alt="" />
44
+ ) : (
45
+ <div class="tile letter" style={`background: var(--kind-${pkg.kind}, var(--muted))`}>
46
+ {pkg.name[0]?.toUpperCase()}
47
+ </div>
48
+ )
49
+ }
50
+ <div class="detail-head-text">
51
+ <h1>
52
+ {pkg.name} <span class={`badge kind-${pkg.kind}`}>{pkg.kind}</span>
53
+ </h1>
54
+ {description && <p class="summary">{description}</p>}
55
+ </div>
56
+ </header>
57
+
58
+ {
59
+ pkg.deprecated && (
60
+ <p class="deprecated-banner">
61
+ ⚠️ deprecated{pkg.replacedBy ? ` — replaced by ${pkg.replacedBy}` : ""}
62
+ </p>
63
+ )
64
+ }
65
+
66
+ <div class="install-row">
67
+ <code>{addCommand}</code>
68
+ <CopyButton value={addCommand} label="Copy the install command" />
69
+ </div>
70
+
71
+ <div class="detail-body">
72
+ <main class="detail-main">
73
+ {Readme ? <Readme /> : <p class="empty">No README available.</p>}
74
+ {
75
+ Changelog && (
76
+ <details class="changelog">
77
+ <summary>Changelog</summary>
78
+ <Changelog />
79
+ </details>
80
+ )
81
+ }
82
+ </main>
83
+
84
+ <aside class="detail-rail">
85
+ {tags.length > 0 && (
86
+ <section>
87
+ <h2>Versions</h2>
88
+ <div class="cascade">
89
+ {cascade.map((tag) => (
90
+ <VersionMenu pkgRef={pkg.ref} tag={tag} current={tag === pkg.version} />
91
+ ))}
92
+ </div>
93
+ {older.length > 0 && (
94
+ <details class="older">
95
+ <summary>{older.length} older</summary>
96
+ <div class="cascade">
97
+ {older.map((tag) => (
98
+ <VersionMenu pkgRef={pkg.ref} tag={tag} />
99
+ ))}
100
+ </div>
101
+ </details>
102
+ )}
103
+ </section>
104
+ )}
105
+
106
+ <section>
107
+ <h2>Details</h2>
108
+ <dl>
109
+ {pkg.license && (
110
+ <>
111
+ <dt>License</dt>
112
+ <dd>{pkg.license}</dd>
113
+ </>
114
+ )}
115
+ {pkg.repository && (
116
+ <>
117
+ <dt>Repository</dt>
118
+ <dd>
119
+ <a href={pkg.repository} target="_blank" rel="noopener noreferrer">
120
+ source
121
+ </a>
122
+ </dd>
123
+ </>
124
+ )}
125
+ {pkg.owner?.github && (
126
+ <>
127
+ <dt>Owner</dt>
128
+ <dd>
129
+ <a
130
+ href={`https://github.com/${pkg.owner.github}`}
131
+ target="_blank"
132
+ rel="noopener noreferrer"
133
+ >
134
+ {pkg.owner.github}
135
+ </a>
136
+ </dd>
137
+ </>
138
+ )}
139
+ </dl>
140
+ {pkg.keywords && pkg.keywords.length > 0 && (
141
+ <ul class="keywords">
142
+ {pkg.keywords.map((k) => (
143
+ <li>{k}</li>
144
+ ))}
145
+ </ul>
146
+ )}
147
+ {vscode && (
148
+ <a class="vscode-link" href={vscode}>
149
+ Open in VS Code
150
+ </a>
151
+ )}
152
+ </section>
153
+ </aside>
154
+ </div>
155
+ </div>
156
+ </Base>
157
+
158
+ <script>
159
+ // Right-click a version: open its command menu at the pointer. The menu is
160
+ // a `manual` popover (see VersionMenu.astro), so dismissal is ours.
161
+ // Copying itself rides the shared [data-copy] listener in Base.astro.
162
+ let openMenu: HTMLElement | null = null;
163
+ const closeMenu = () => {
164
+ openMenu?.hidePopover();
165
+ openMenu = null;
166
+ };
167
+ // A right-click fires no `click`, so this never eats the gesture that just
168
+ // opened the menu — but it does close on a command item, which is wanted.
169
+ document.addEventListener("click", closeMenu);
170
+ document.addEventListener("scroll", closeMenu, true);
171
+ document.addEventListener("keydown", (e) => {
172
+ if (e.key === "Escape") closeMenu();
173
+ });
174
+
175
+ for (const seg of document.querySelectorAll<HTMLElement>("[data-menu]")) {
176
+ const menu = document.getElementById(seg.dataset.menu ?? "");
177
+ if (!menu) continue;
178
+ seg.addEventListener("contextmenu", (e) => {
179
+ e.preventDefault();
180
+ closeMenu();
181
+ // The keyboard Menu key fires contextmenu at 0,0 — fall back to the
182
+ // segment itself so it doesn't land in the page corner.
183
+ const seat = seg.getBoundingClientRect();
184
+ menu.style.left = `${e.clientX || seat.left}px`;
185
+ menu.style.top = `${e.clientY || seat.bottom}px`;
186
+ menu.showPopover();
187
+ openMenu = menu;
188
+ // Nudge back inside once measured — the pointer can be within a menu's
189
+ // width of the right or bottom edge.
190
+ const box = menu.getBoundingClientRect();
191
+ if (box.right > innerWidth) menu.style.left = `${innerWidth - box.width - 8}px`;
192
+ if (box.bottom > innerHeight) menu.style.top = `${innerHeight - box.height - 8}px`;
193
+ });
194
+ }
195
+ </script>
196
+
197
+ <style>
198
+ .detail {
199
+ display: flex;
200
+ flex-direction: column;
201
+ gap: 1.25rem;
202
+ }
203
+ .detail-head {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 1rem;
207
+ background: var(--card);
208
+ border: 1px solid var(--border);
209
+ border-radius: 10px;
210
+ padding: 1rem 1.25rem;
211
+ }
212
+ .tile {
213
+ flex-shrink: 0;
214
+ width: 3.5rem;
215
+ height: 3.5rem;
216
+ border-radius: 10px;
217
+ object-fit: cover;
218
+ }
219
+ .tile.letter {
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ color: var(--on-accent);
224
+ font-size: 1.5rem;
225
+ font-weight: 700;
226
+ font-family: ui-monospace, "SFMono-Regular", monospace;
227
+ }
228
+ .detail-head-text h1 {
229
+ margin: 0;
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 0.6rem;
233
+ font-size: 1.5rem;
234
+ font-family: ui-monospace, "SFMono-Regular", monospace;
235
+ }
236
+ .summary {
237
+ margin: 0.3rem 0 0;
238
+ color: var(--muted);
239
+ }
240
+ .deprecated-banner {
241
+ margin: 0;
242
+ background: var(--banner-bg);
243
+ color: var(--banner-fg);
244
+ border: 1px solid var(--banner-border);
245
+ border-radius: 8px;
246
+ padding: 0.6rem 0.9rem;
247
+ font-weight: 600;
248
+ }
249
+ .install-row {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: space-between;
253
+ gap: 0.6rem;
254
+ background: var(--card);
255
+ border: 1px solid var(--border);
256
+ border-radius: 8px;
257
+ padding: 0.6rem 0.9rem;
258
+ }
259
+ .install-row code {
260
+ font-size: 0.9rem;
261
+ overflow-x: auto;
262
+ }
263
+ .detail-body {
264
+ display: grid;
265
+ grid-template-columns: 1fr 18rem;
266
+ gap: 2rem;
267
+ align-items: start;
268
+ }
269
+ @media (max-width: 55rem) {
270
+ .detail-body {
271
+ grid-template-columns: 1fr;
272
+ }
273
+ }
274
+ .detail-main {
275
+ min-width: 0;
276
+ background: var(--card);
277
+ border: 1px solid var(--border);
278
+ border-radius: 10px;
279
+ padding: 1.25rem 1.5rem;
280
+ }
281
+ .detail-main :global(img) {
282
+ max-width: 100%;
283
+ }
284
+ .detail-main :global(pre) {
285
+ overflow-x: auto;
286
+ }
287
+ .changelog {
288
+ margin-top: 1.5rem;
289
+ border-top: 1px solid var(--border);
290
+ padding-top: 1rem;
291
+ }
292
+ .changelog summary {
293
+ cursor: pointer;
294
+ font-weight: 600;
295
+ }
296
+ .detail-rail {
297
+ display: flex;
298
+ flex-direction: column;
299
+ gap: 1.25rem;
300
+ }
301
+ .detail-rail section {
302
+ background: var(--card);
303
+ border: 1px solid var(--border);
304
+ border-radius: 10px;
305
+ padding: 1rem;
306
+ }
307
+ .detail-rail h2 {
308
+ margin: 0 0 0.6rem;
309
+ font-size: 0.85rem;
310
+ text-transform: uppercase;
311
+ letter-spacing: 0.04em;
312
+ color: var(--muted);
313
+ }
314
+ /* Loose badges, both for the cascade and the older tags — VersionMenu owns
315
+ the pill itself, this only lays them out. */
316
+ .cascade {
317
+ display: flex;
318
+ flex-wrap: wrap;
319
+ gap: 0.3rem;
320
+ }
321
+ .older {
322
+ margin-top: 0.5rem;
323
+ }
324
+ .older[open] > summary {
325
+ margin-bottom: 0.5rem;
326
+ }
327
+ .older > summary {
328
+ cursor: pointer;
329
+ font-size: 0.8rem;
330
+ color: var(--muted);
331
+ }
332
+ .older > summary:hover {
333
+ color: var(--fg);
334
+ }
335
+ dl {
336
+ margin: 0;
337
+ display: grid;
338
+ grid-template-columns: auto 1fr;
339
+ gap: 0.3rem 0.6rem;
340
+ }
341
+ dt {
342
+ color: var(--muted);
343
+ font-size: 0.85rem;
344
+ }
345
+ dd {
346
+ margin: 0;
347
+ }
348
+ .keywords {
349
+ list-style: none;
350
+ margin: 0.75rem 0 0;
351
+ padding: 0;
352
+ display: flex;
353
+ flex-wrap: wrap;
354
+ gap: 0.35rem;
355
+ }
356
+ .keywords li {
357
+ background: var(--chip-bg);
358
+ border-radius: 999px;
359
+ padding: 0.15rem 0.6rem;
360
+ font-size: 0.78rem;
361
+ }
362
+ .vscode-link {
363
+ display: inline-block;
364
+ margin-top: 1rem;
365
+ font-size: 0.9rem;
366
+ }
367
+ </style>
@@ -0,0 +1,20 @@
1
+ import { type SiteConfig } from "../config.js";
2
+ export interface BuildSiteOptions {
3
+ /** Index repo root. Astro's root; `enrich/` and `public/` resolve here. */
4
+ root: string;
5
+ /** Holds `all.json` on entry; holds the rendered site on return. */
6
+ outDir: string;
7
+ config: SiteConfig;
8
+ }
9
+ /** Thrown when `<outDir>/all.json` is missing or malformed. */
10
+ export declare class RenderInputError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ /** Astro `srcDir` — pages, layouts, components, content config. */
14
+ export declare const ASTRO_SRC_DIR: string;
15
+ /** Default `public/` layer; holds the fallback favicon. */
16
+ export declare const DEFAULT_PUBLIC_DIR: string;
17
+ /** Render the catalog site from `<outDir>/all.json` into `outDir`. */
18
+ export declare function buildSite(opts: BuildSiteOptions): Promise<void>;
19
+ export type { CatalogPackage, GrimoireData } from "./types.js";
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderer/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,+DAA+D;AAC/D,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AA4BD,mEAAmE;AACnE,eAAO,MAAM,aAAa,QAA2B,CAAC;AACtD,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,QAA4B,CAAC;AAkH5D,sEAAsE;AACtE,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCrE;AAED,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}