@grove-dev/astro 0.1.4 → 0.2.2

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 (246) hide show
  1. package/dist/index.d.ts +4 -25
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +56 -22
  4. package/dist/index.js.map +1 -1
  5. package/dist/layouts/index.d.ts +25 -0
  6. package/dist/layouts/index.d.ts.map +1 -0
  7. package/dist/layouts/index.js +25 -0
  8. package/dist/layouts/index.js.map +1 -0
  9. package/dist/lib/display.d.ts +33 -0
  10. package/dist/lib/display.d.ts.map +1 -0
  11. package/dist/lib/display.js +104 -0
  12. package/dist/lib/display.js.map +1 -0
  13. package/dist/lib/format.d.ts +30 -0
  14. package/dist/lib/format.d.ts.map +1 -0
  15. package/dist/lib/format.js +73 -0
  16. package/dist/lib/format.js.map +1 -0
  17. package/dist/lib/index.d.ts +26 -0
  18. package/dist/lib/index.d.ts.map +1 -0
  19. package/dist/lib/index.js +26 -0
  20. package/dist/lib/index.js.map +1 -0
  21. package/dist/lib/lenses.d.ts +96 -0
  22. package/dist/lib/lenses.d.ts.map +1 -0
  23. package/dist/lib/lenses.js +181 -0
  24. package/dist/lib/lenses.js.map +1 -0
  25. package/dist/lib/repo.d.ts +30 -0
  26. package/dist/lib/repo.d.ts.map +1 -0
  27. package/dist/lib/repo.js +44 -0
  28. package/dist/lib/repo.js.map +1 -0
  29. package/dist/lib/scores.d.ts +38 -0
  30. package/dist/lib/scores.d.ts.map +1 -0
  31. package/dist/lib/scores.js +83 -0
  32. package/dist/lib/scores.js.map +1 -0
  33. package/dist/lib/search.d.ts +199 -0
  34. package/dist/lib/search.d.ts.map +1 -0
  35. package/dist/lib/search.js +357 -0
  36. package/dist/lib/search.js.map +1 -0
  37. package/dist/lib/taxonomy-counts.d.ts +25 -0
  38. package/dist/lib/taxonomy-counts.d.ts.map +1 -0
  39. package/dist/lib/taxonomy-counts.js +41 -0
  40. package/dist/lib/taxonomy-counts.js.map +1 -0
  41. package/package.json +7 -7
  42. package/src/components/AppSection.astro +83 -0
  43. package/src/components/AppsIndexRow.astro +204 -0
  44. package/src/components/AppsPagination.astro +126 -0
  45. package/src/components/CategoryGrid.astro +86 -17
  46. package/src/components/ContributorsGrid.astro +149 -0
  47. package/src/components/DecisionRow.astro +89 -0
  48. package/src/components/ExploreByCategory.astro +67 -0
  49. package/src/components/ExploreByStack.astro +72 -0
  50. package/src/components/Hero.astro +246 -0
  51. package/src/components/Icon.astro +223 -0
  52. package/src/components/ItemCard.astro +289 -30
  53. package/src/components/MinimalAbout.astro +82 -0
  54. package/src/components/OriginalCollection.astro +116 -0
  55. package/src/components/RefinePanel.astro +197 -0
  56. package/src/components/ScoreBars.astro +92 -22
  57. package/src/components/SmartLensTabs.astro +71 -0
  58. package/src/components/StackGrid.astro +132 -0
  59. package/src/components/WhyThisExists.astro +116 -0
  60. package/src/index.ts +61 -14
  61. package/src/layouts/BaseLayout.astro +227 -15
  62. package/src/layouts/Container.astro +57 -0
  63. package/src/layouts/Footer.astro +235 -0
  64. package/src/layouts/Header.astro +159 -0
  65. package/src/layouts/SectionHeader.astro +58 -0
  66. package/{templates/default/src/components/layout → src/layouts}/Seo.astro +62 -38
  67. package/src/layouts/ThemeToggle.astro +110 -0
  68. package/src/layouts/astro-shim.d.ts +29 -0
  69. package/src/layouts/index.ts +24 -0
  70. package/src/lib/display.ts +118 -0
  71. package/src/lib/format.ts +65 -0
  72. package/src/lib/index.ts +26 -0
  73. package/src/lib/lenses.ts +241 -0
  74. package/src/lib/repo.ts +44 -0
  75. package/src/lib/scores.ts +94 -0
  76. package/src/lib/search.ts +481 -0
  77. package/src/lib/taxonomy-counts.ts +60 -0
  78. package/src/styles.css +366 -638
  79. package/templates/default/.astro/content.d.ts +155 -0
  80. package/templates/default/.astro/settings.json +5 -0
  81. package/templates/default/.astro/types.d.ts +2 -0
  82. package/templates/default/astro.config.mjs +8 -6
  83. package/templates/default/content/pages/about.md +66 -0
  84. package/templates/default/content/pages/methodology.md +103 -0
  85. package/templates/default/data/generated/records.full.json +8 -0
  86. package/templates/default/data/generated/records.index.json +7 -0
  87. package/templates/default/data/generated/records.json +8 -0
  88. package/templates/default/data/generated/site-config.json +31 -0
  89. package/templates/default/data/records/.gitkeep +0 -0
  90. package/templates/default/dist/_astro/BaseLayout.DfpwR3jc.css +1 -0
  91. package/templates/default/dist/about/index.html +165 -0
  92. package/templates/default/dist/icons/platforms/android.svg +1 -0
  93. package/templates/default/dist/icons/platforms/apple-dark.svg +1 -0
  94. package/templates/default/dist/icons/platforms/apple-light.svg +1 -0
  95. package/templates/default/dist/icons/platforms/chrome.svg +1 -0
  96. package/templates/default/dist/icons/platforms/chromeos.svg +5 -0
  97. package/templates/default/dist/icons/platforms/desktop.svg +4 -0
  98. package/templates/default/dist/icons/platforms/embedded.svg +5 -0
  99. package/templates/default/dist/icons/platforms/ios.svg +1 -0
  100. package/templates/default/dist/icons/platforms/linux.svg +1 -0
  101. package/templates/default/dist/icons/platforms/macos.svg +1 -0
  102. package/templates/default/dist/icons/platforms/ubuntu.svg +1 -0
  103. package/templates/default/dist/icons/platforms/web.svg +5 -0
  104. package/templates/default/dist/icons/platforms/windows.svg +6 -0
  105. package/templates/default/dist/icons/stacks/android.svg +1 -0
  106. package/templates/default/dist/icons/stacks/apple-dark.svg +1 -0
  107. package/templates/default/dist/icons/stacks/apple-light.svg +1 -0
  108. package/templates/default/dist/icons/stacks/capacitor.svg +14 -0
  109. package/templates/default/dist/icons/stacks/dart.svg +1 -0
  110. package/templates/default/dist/icons/stacks/firebase.svg +1 -0
  111. package/templates/default/dist/icons/stacks/flutter.svg +1 -0
  112. package/templates/default/dist/icons/stacks/graphql.svg +1 -0
  113. package/templates/default/dist/icons/stacks/ionic.svg +4 -0
  114. package/templates/default/dist/icons/stacks/java.svg +1 -0
  115. package/templates/default/dist/icons/stacks/javascript.svg +1 -0
  116. package/templates/default/dist/icons/stacks/kotlin.svg +1 -0
  117. package/templates/default/dist/icons/stacks/mongodb.svg +1 -0
  118. package/templates/default/dist/icons/stacks/nodejs.svg +1 -0
  119. package/templates/default/dist/icons/stacks/python.svg +1 -0
  120. package/templates/default/dist/icons/stacks/react-native.svg +1 -0
  121. package/templates/default/dist/icons/stacks/react.svg +1 -0
  122. package/templates/default/dist/icons/stacks/rust.svg +1 -0
  123. package/templates/default/dist/icons/stacks/solidity.svg +1 -0
  124. package/templates/default/dist/icons/stacks/swift.svg +1 -0
  125. package/templates/default/dist/icons/stacks/tensorflow.svg +1 -0
  126. package/templates/default/dist/icons/stacks/typescript.svg +1 -0
  127. package/templates/default/dist/index.html +55 -0
  128. package/templates/default/dist/llms-full.txt +14 -0
  129. package/templates/default/dist/llms.txt +11 -0
  130. package/templates/default/dist/og-image.svg +48 -0
  131. package/templates/default/dist/projects/index.html +61 -0
  132. package/templates/default/dist/robots.txt +53 -0
  133. package/templates/default/dist/sitemap.xml +21 -0
  134. package/templates/default/dist/submit/index.html +69 -0
  135. package/templates/default/grove.config.ts +36 -0
  136. package/templates/default/node_modules/.bin/astro +21 -0
  137. package/templates/default/node_modules/.bin/astro-check +21 -0
  138. package/templates/default/node_modules/.bin/grove +21 -0
  139. package/templates/default/node_modules/.bin/jiti +21 -0
  140. package/templates/default/node_modules/.bin/prettier +21 -0
  141. package/templates/default/node_modules/.bin/rollup +21 -0
  142. package/templates/default/node_modules/.bin/tsc +21 -0
  143. package/templates/default/node_modules/.bin/tsserver +21 -0
  144. package/templates/default/node_modules/.bin/tsx +21 -0
  145. package/templates/default/node_modules/.bin/vite +21 -0
  146. package/templates/default/node_modules/.bin/yaml +21 -0
  147. package/templates/default/node_modules/.vite/deps/_metadata.json +79 -0
  148. package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js +433 -0
  149. package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js.map +7 -0
  150. package/templates/default/node_modules/.vite/deps/astro___aria-query.js +5 -0
  151. package/templates/default/node_modules/.vite/deps/astro___aria-query.js.map +7 -0
  152. package/templates/default/node_modules/.vite/deps/astro___axobject-query.js +5 -0
  153. package/templates/default/node_modules/.vite/deps/astro___axobject-query.js.map +7 -0
  154. package/templates/default/node_modules/.vite/deps/astro___html-escaper.js +9 -0
  155. package/templates/default/node_modules/.vite/deps/astro___html-escaper.js.map +7 -0
  156. package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +241 -0
  157. package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +7 -0
  158. package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js +1592 -0
  159. package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js.map +7 -0
  160. package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js +30 -0
  161. package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +7 -0
  162. package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js +186 -0
  163. package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js.map +7 -0
  164. package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js +48 -0
  165. package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js.map +7 -0
  166. package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js +6779 -0
  167. package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js.map +7 -0
  168. package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js +33 -0
  169. package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js.map +7 -0
  170. package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js +88 -0
  171. package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js.map +7 -0
  172. package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js +72 -0
  173. package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js.map +7 -0
  174. package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js +3757 -0
  175. package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js.map +7 -0
  176. package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js +54 -0
  177. package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js.map +7 -0
  178. package/templates/default/node_modules/.vite/deps/package.json +3 -0
  179. package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js +213 -0
  180. package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js.map +7 -0
  181. package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js +525 -0
  182. package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js.map +7 -0
  183. package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js +1020 -0
  184. package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js.map +7 -0
  185. package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js +147 -0
  186. package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js.map +7 -0
  187. package/templates/default/package.json +6 -5
  188. package/templates/default/public/llms-full.txt +14 -0
  189. package/templates/default/public/llms.txt +11 -0
  190. package/templates/default/src/components/layout/BaseLayout.astro +10 -57
  191. package/templates/default/src/components/layout/Footer.astro +16 -134
  192. package/templates/default/src/components/layout/Header.astro +25 -126
  193. package/templates/default/src/data/records.ts +161 -0
  194. package/templates/default/src/lib/markdown.ts +171 -0
  195. package/templates/default/src/pages/about.astro +95 -429
  196. package/templates/default/src/pages/index.astro +258 -61
  197. package/templates/default/src/pages/projects/[slug].astro +480 -0
  198. package/templates/default/src/pages/projects/index.astro +193 -0
  199. package/templates/default/src/pages/sitemap.xml.ts +73 -61
  200. package/templates/default/src/pages/submit.astro +122 -411
  201. package/templates/default/src/styles/global.css +42 -68
  202. package/src/components/ActiveFilterChips.astro +0 -20
  203. package/src/components/DirectoryFilters.astro +0 -100
  204. package/src/components/DirectoryHero.astro +0 -32
  205. package/src/components/HealthBadge.astro +0 -12
  206. package/src/components/LensTabs.astro +0 -26
  207. package/src/components/MethodologyPanel.astro +0 -20
  208. package/src/components/Pagination.astro +0 -24
  209. package/src/components/ProjectDetail.astro +0 -166
  210. package/src/components/SubmitDraft.astro +0 -137
  211. package/templates/default/src/components/AppCard.astro +0 -248
  212. package/templates/default/src/components/AppSection.astro +0 -58
  213. package/templates/default/src/components/AppsIndexRow.astro +0 -230
  214. package/templates/default/src/components/AppsPagination.astro +0 -147
  215. package/templates/default/src/components/CategoryGrid.astro +0 -80
  216. package/templates/default/src/components/ContributorsGrid.astro +0 -105
  217. package/templates/default/src/components/DecisionRow.astro +0 -219
  218. package/templates/default/src/components/ExploreByCategory.astro +0 -74
  219. package/templates/default/src/components/ExploreByStack.astro +0 -102
  220. package/templates/default/src/components/Hero.astro +0 -176
  221. package/templates/default/src/components/MinimalAbout.astro +0 -57
  222. package/templates/default/src/components/OriginalCollection.astro +0 -144
  223. package/templates/default/src/components/RefinePanel.astro +0 -272
  224. package/templates/default/src/components/ScoreBars.astro +0 -72
  225. package/templates/default/src/components/SmartLensTabs.astro +0 -65
  226. package/templates/default/src/components/StackGrid.astro +0 -95
  227. package/templates/default/src/components/WhyThisExists.astro +0 -81
  228. package/templates/default/src/components/icons/Icon.astro +0 -148
  229. package/templates/default/src/components/layout/SectionHeader.astro +0 -42
  230. package/templates/default/src/components/layout/ThemeToggle.astro +0 -97
  231. package/templates/default/src/data/apps.ts +0 -149
  232. package/templates/default/src/data/categories.ts +0 -27
  233. package/templates/default/src/data/config.ts +0 -28
  234. package/templates/default/src/data/contributors.ts +0 -96
  235. package/templates/default/src/data/stacks.ts +0 -47
  236. package/templates/default/src/data/stats.ts +0 -65
  237. package/templates/default/src/data/types.ts +0 -248
  238. package/templates/default/src/lib/apps-search.ts +0 -228
  239. package/templates/default/src/lib/format.ts +0 -36
  240. package/templates/default/src/lib/lenses.ts +0 -98
  241. package/templates/default/src/lib/repo.ts +0 -12
  242. package/templates/default/src/lib/scores.ts +0 -51
  243. package/templates/default/src/lib/taxonomy-counts.ts +0 -31
  244. package/templates/default/src/pages/apps/[slug].astro +0 -709
  245. package/templates/default/src/pages/apps/index.astro +0 -669
  246. package/templates/default/src/pages/contributors.astro +0 -142
@@ -0,0 +1,357 @@
1
+ /**
2
+ * URL-driven search/filter/sort state for the directory list page.
3
+ *
4
+ * Multi-value fields use repeated keys: `?stack=Flutter&stack=RN`.
5
+ * Single-value fields (q, sort, page, lens) are scalar. Empty
6
+ * / undefined fields mean "no filter on this dimension".
7
+ *
8
+ * The functions here are generic over a record-like shape that
9
+ * carries the index payload (see `toIndexRecord` in
10
+ * `@grove-dev/core`). The list page only needs `name`, `slug`,
11
+ * `description`, `category`, `tags`, `stack`, `stacks`, `platforms`,
12
+ * `repoUrl`, `logoUrl`, `health`, `curation`, and `github.{stars,
13
+ * pushedAt}` — all of which are present in the index projection.
14
+ */
15
+ import { labelDisplay, statusDisplay } from "./display.js";
16
+ import { applyLens, lensFromSearchParams } from "./lenses.js";
17
+ /** Available sort orders, in UI order. */
18
+ export const SORT_OPTIONS = [
19
+ { value: "recently-updated", label: "Recently updated" },
20
+ { value: "most-starred", label: "Most starred" },
21
+ { value: "recently-added", label: "Recently added" },
22
+ { value: "best-overall", label: "Best overall" },
23
+ { value: "alphabetical", label: "Alphabetical" },
24
+ ];
25
+ const DEFAULT_SORT = "recently-updated";
26
+ const DEFAULT_PAGE = 1;
27
+ /**
28
+ * How many records per page. Constant for now; future versions may
29
+ * vary per blueprint.
30
+ */
31
+ export const PAGE_SIZE = 24;
32
+ const KEYS = {
33
+ q: "q",
34
+ stacks: "stack",
35
+ platforms: "platform",
36
+ categories: "category",
37
+ labels: "label",
38
+ licenses: "license",
39
+ statuses: "status",
40
+ lens: "lens",
41
+ sort: "sort",
42
+ page: "page",
43
+ };
44
+ // ── Search param round-tripping ──────────────────────────────────
45
+ /**
46
+ * Read filters from a URLSearchParams (or anything URLSearchParams-
47
+ * shaped, e.g. the result of `new URL(req.url).searchParams`).
48
+ */
49
+ export function filtersFromSearchParams(sp) {
50
+ const rawSort = sp.get(KEYS.sort);
51
+ // Backwards-compat: old "most-mature" sort now maps to "best-overall".
52
+ const normalizedSort = rawSort === "most-mature" ? "best-overall" : rawSort;
53
+ const sort = normalizedSort && SORT_OPTIONS.some((o) => o.value === normalizedSort)
54
+ ? normalizedSort
55
+ : undefined;
56
+ const rawPage = Number(sp.get(KEYS.page));
57
+ const page = Number.isFinite(rawPage) && rawPage >= 1 ? Math.floor(rawPage) : undefined;
58
+ return {
59
+ q: sp.get(KEYS.q) ?? undefined,
60
+ stacks: sp.getAll(KEYS.stacks).filter(Boolean),
61
+ platforms: sp.getAll(KEYS.platforms).filter(Boolean),
62
+ categories: sp.getAll(KEYS.categories).filter(Boolean),
63
+ labels: sp.getAll(KEYS.labels).filter(Boolean),
64
+ licenses: sp.getAll(KEYS.licenses).filter(Boolean),
65
+ statuses: sp
66
+ .getAll(KEYS.statuses)
67
+ .flatMap((s) => s.split(","))
68
+ .filter(Boolean),
69
+ lens: lensFromSearchParams(sp) || undefined,
70
+ sort,
71
+ page,
72
+ };
73
+ }
74
+ /**
75
+ * Serialize a filters object back to URLSearchParams. Undefined /
76
+ * empty values are dropped. Defaults (sort, page 1) are also dropped
77
+ * to keep URLs short and canonical.
78
+ */
79
+ export function searchParamsFromFilters(f) {
80
+ const sp = new URLSearchParams();
81
+ if (f.q)
82
+ sp.set(KEYS.q, f.q);
83
+ for (const v of f.stacks ?? [])
84
+ sp.append(KEYS.stacks, v);
85
+ for (const v of f.platforms ?? [])
86
+ sp.append(KEYS.platforms, v);
87
+ for (const v of f.categories ?? [])
88
+ sp.append(KEYS.categories, v);
89
+ for (const v of f.labels ?? [])
90
+ sp.append(KEYS.labels, v);
91
+ for (const v of f.licenses ?? [])
92
+ sp.append(KEYS.licenses, v);
93
+ if (f.statuses?.length)
94
+ sp.set(KEYS.statuses, f.statuses.join(","));
95
+ if (f.lens)
96
+ sp.set(KEYS.lens, f.lens);
97
+ if (f.sort && f.sort !== DEFAULT_SORT)
98
+ sp.set(KEYS.sort, f.sort);
99
+ if (f.page && f.page !== DEFAULT_PAGE)
100
+ sp.set(KEYS.page, String(f.page));
101
+ return sp;
102
+ }
103
+ // ── Filter application ───────────────────────────────────────────
104
+ /**
105
+ * True if any filter is active (i.e. the result list isn't "all
106
+ * records, default sort").
107
+ */
108
+ export function hasAnyFilter(f) {
109
+ if (f.q && f.q.trim())
110
+ return true;
111
+ if (f.lens)
112
+ return true;
113
+ return Boolean((f.stacks?.length ?? 0) +
114
+ (f.platforms?.length ?? 0) +
115
+ (f.categories?.length ?? 0) +
116
+ (f.labels?.length ?? 0) +
117
+ (f.licenses?.length ?? 0) +
118
+ (f.statuses?.length ?? 0));
119
+ }
120
+ /**
121
+ * Return the subset of records that match all active filters (AND
122
+ * across dimensions, OR within a dimension).
123
+ *
124
+ * `q` is a case-insensitive substring search across name, owner,
125
+ * description, category, stack/stacks/platforms, tags, and license.
126
+ * Other dimensions are exact match.
127
+ */
128
+ export function filterApps(records, f) {
129
+ const q = f.q?.trim().toLowerCase();
130
+ const stacks = f.stacks?.length ? new Set(f.stacks) : null;
131
+ const platforms = f.platforms?.length ? new Set(f.platforms) : null;
132
+ const categories = f.categories?.length ? new Set(f.categories) : null;
133
+ const labels = f.labels?.length ? new Set(f.labels) : null;
134
+ const licenses = f.licenses?.length ? new Set(f.licenses) : null;
135
+ const statuses = f.statuses?.length ? new Set(f.statuses) : null;
136
+ return records.filter((r) => {
137
+ if (q) {
138
+ const ownerMatch = /github\.com\/([^/]+)\//.exec(r.repoUrl ?? "")?.[1]?.toLowerCase() ??
139
+ "";
140
+ const haystack = [
141
+ r.name,
142
+ ownerMatch,
143
+ r.description ?? "",
144
+ r.category ?? "",
145
+ r.stack ?? "",
146
+ ...(r.stacks ?? []),
147
+ ...(r.platforms ?? []),
148
+ ...(r.tags ?? []),
149
+ r.github?.license ?? "",
150
+ r.health?.status ? statusDisplay(r.health.status) : "",
151
+ ]
152
+ .join(" ")
153
+ .toLowerCase();
154
+ if (!haystack.includes(q))
155
+ return false;
156
+ }
157
+ if (stacks) {
158
+ const allStacks = [r.stack, ...(r.stacks ?? [])].filter((s) => Boolean(s));
159
+ if (!allStacks.some((s) => stacks.has(s)))
160
+ return false;
161
+ }
162
+ if (platforms) {
163
+ if (!r.platforms?.some((p) => platforms.has(p)))
164
+ return false;
165
+ }
166
+ if (categories) {
167
+ if (!r.category || !categories.has(r.category))
168
+ return false;
169
+ }
170
+ if (labels) {
171
+ if (!r.curation?.labels?.some((l) => labels.has(l)))
172
+ return false;
173
+ }
174
+ if (licenses) {
175
+ const license = r.github?.license;
176
+ if (!license || !licenses.has(license))
177
+ return false;
178
+ }
179
+ if (statuses) {
180
+ const status = r.health?.status;
181
+ if (!status || !statuses.has(status))
182
+ return false;
183
+ }
184
+ return true;
185
+ });
186
+ }
187
+ // ── Lenses, sort, pagination ─────────────────────────────────────
188
+ /**
189
+ * Apply lens → filter → sort pipeline. Lens is applied first because
190
+ * it's a single-select "view" that may be broader or narrower than
191
+ * the regular filter dimensions.
192
+ */
193
+ export function pipeline(records, filters) {
194
+ const afterLens = applyLens(records, filters.lens ?? "");
195
+ const afterFilter = filterApps(afterLens, filters);
196
+ return applySort(afterFilter, effectiveSort(filters));
197
+ }
198
+ /**
199
+ * Sort records in-place-free: returns a new array. Stable for ties.
200
+ * Records missing the sort key (e.g. no stars) sink to the bottom
201
+ * rather than vanishing.
202
+ */
203
+ export function applySort(records, sort) {
204
+ const arr = records.slice();
205
+ const ts = (s) => (s ? new Date(s).valueOf() : 0);
206
+ switch (sort) {
207
+ case "most-starred":
208
+ arr.sort((a, b) => (b.github?.stars ?? 0) - (a.github?.stars ?? 0));
209
+ break;
210
+ case "recently-updated":
211
+ arr.sort((a, b) => ts(b.github?.pushedAt) - ts(a.github?.pushedAt));
212
+ break;
213
+ case "recently-added":
214
+ // Index payload doesn't have a dedicated "addedAt" yet — fall
215
+ // back to `pushedAt` so the sort still works. Once we add an
216
+ // explicit addedAt field, swap the key here.
217
+ arr.sort((a, b) => ts(b.github?.pushedAt) - ts(a.github?.pushedAt));
218
+ break;
219
+ case "best-overall": {
220
+ // Composite: scores.overall → scores.maturity → scores.activity →
221
+ // active status → license present → stars. Avoids ranking by
222
+ // stars alone.
223
+ const score = (x, k) => (typeof x.scores?.[k] === "number" ? x.scores[k] : 0);
224
+ const active = (x) => (x.health?.status === "active" ? 1 : 0);
225
+ const licensed = (x) => (x.github?.license ? 1 : 0);
226
+ arr.sort((a, b) => {
227
+ const sOverall = score(b, "overall") - score(a, "overall");
228
+ if (sOverall !== 0)
229
+ return sOverall;
230
+ const sMaturity = score(b, "maturity") - score(a, "maturity");
231
+ if (sMaturity !== 0)
232
+ return sMaturity;
233
+ const sActivity = score(b, "activity") - score(a, "activity");
234
+ if (sActivity !== 0)
235
+ return sActivity;
236
+ const sActive = active(b) - active(a);
237
+ if (sActive !== 0)
238
+ return sActive;
239
+ const sLicensed = licensed(b) - licensed(a);
240
+ if (sLicensed !== 0)
241
+ return sLicensed;
242
+ return (b.github?.stars ?? 0) - (a.github?.stars ?? 0);
243
+ });
244
+ break;
245
+ }
246
+ case "alphabetical":
247
+ arr.sort((a, b) => a.name.localeCompare(b.name));
248
+ break;
249
+ }
250
+ return arr;
251
+ }
252
+ /** Slice an array for a given page (1-based). */
253
+ export function paginate(items, page, pageSize = PAGE_SIZE) {
254
+ const start = (page - 1) * pageSize;
255
+ return items.slice(start, start + pageSize);
256
+ }
257
+ /** Total number of pages given an item count. Always >= 1. */
258
+ export function totalPages(itemCount, pageSize = PAGE_SIZE) {
259
+ return Math.max(1, Math.ceil(itemCount / pageSize));
260
+ }
261
+ /** Resolve the effective sort from a filters object, applying default. */
262
+ export function effectiveSort(f) {
263
+ return f.sort ?? DEFAULT_SORT;
264
+ }
265
+ /** Resolve the effective page from a filters object, applying default. */
266
+ export function effectivePage(f) {
267
+ return f.page ?? DEFAULT_PAGE;
268
+ }
269
+ // ── Facets ───────────────────────────────────────────────────────
270
+ /**
271
+ * Return a flat list of active-filter chips for display + removal.
272
+ * Used by the "x" button on each chip in the UI.
273
+ */
274
+ export function activeFilterChips(f) {
275
+ const out = [];
276
+ if (f.q && f.q.trim()) {
277
+ out.push({ key: "q", value: "", label: `“${f.q}”` });
278
+ }
279
+ for (const v of f.stacks ?? []) {
280
+ out.push({ key: "stacks", value: v, label: `Stack: ${v}` });
281
+ }
282
+ for (const v of f.platforms ?? []) {
283
+ out.push({ key: "platforms", value: v, label: `Platform: ${v}` });
284
+ }
285
+ for (const v of f.categories ?? []) {
286
+ out.push({ key: "categories", value: v, label: `Category: ${v}` });
287
+ }
288
+ for (const v of f.labels ?? []) {
289
+ out.push({ key: "labels", value: v, label: labelDisplay(v) ?? v });
290
+ }
291
+ for (const v of f.licenses ?? []) {
292
+ out.push({ key: "licenses", value: v, label: `License: ${v}` });
293
+ }
294
+ if (f.lens) {
295
+ // Inline map keeps this function dependency-free for unit tests
296
+ // (no need to import from `lenses.ts` to render a chip label).
297
+ const LENS_CHIP_LABELS = {
298
+ all: "All",
299
+ hot: "Trending",
300
+ new: "Recently added",
301
+ mature: "Established",
302
+ featured: "Featured",
303
+ "needs-review": "Needs review",
304
+ };
305
+ out.push({ key: "lens", value: f.lens, label: LENS_CHIP_LABELS[f.lens] ?? f.lens });
306
+ }
307
+ if (f.statuses && f.statuses.length) {
308
+ for (const v of f.statuses) {
309
+ out.push({ key: "statuses", value: v, label: statusDisplay(v) });
310
+ }
311
+ }
312
+ return out;
313
+ }
314
+ /**
315
+ * Build the full list of facet values from the record array,
316
+ * preserving a sensible order (frequency desc, then alphabetical
317
+ * for ties).
318
+ */
319
+ export function buildFacets(records) {
320
+ const counts = {
321
+ stack: new Map(),
322
+ platform: new Map(),
323
+ category: new Map(),
324
+ label: new Map(),
325
+ license: new Map(),
326
+ };
327
+ for (const r of records) {
328
+ const allStacks = new Set([r.stack, ...(r.stacks ?? [])].filter((s) => Boolean(s)));
329
+ for (const s of allStacks) {
330
+ counts.stack.set(s, (counts.stack.get(s) ?? 0) + 1);
331
+ }
332
+ for (const p of r.platforms ?? []) {
333
+ counts.platform.set(p, (counts.platform.get(p) ?? 0) + 1);
334
+ }
335
+ if (r.category) {
336
+ counts.category.set(r.category, (counts.category.get(r.category) ?? 0) + 1);
337
+ }
338
+ for (const l of r.curation?.labels ?? []) {
339
+ counts.label.set(l, (counts.label.get(l) ?? 0) + 1);
340
+ }
341
+ const license = r.github?.license;
342
+ if (license) {
343
+ counts.license.set(license, (counts.license.get(license) ?? 0) + 1);
344
+ }
345
+ }
346
+ const sortByCountThenName = (m) => [...m.entries()]
347
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
348
+ .map(([value, count]) => ({ value, count }));
349
+ return {
350
+ stacks: sortByCountThenName(counts.stack),
351
+ platforms: sortByCountThenName(counts.platform),
352
+ categories: sortByCountThenName(counts.category),
353
+ labels: sortByCountThenName(counts.label),
354
+ licenses: sortByCountThenName(counts.license),
355
+ };
356
+ }
357
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Build-time helpers that compute taxonomy counts from an array of
3
+ * records. Generic over any object that carries the index-payload
4
+ * fields the UI cares about (`category`, `stack` / `stacks`,
5
+ * `curation.labels`). No filesystem / data import — consumers pass
6
+ * the records explicitly so this stays testable.
7
+ *
8
+ * All functions are synchronous and cheap — they're called from
9
+ * Astro page frontmatter at build time, never on the client.
10
+ */
11
+ export interface Taxonomable {
12
+ category?: string;
13
+ stack?: string;
14
+ stacks?: string[];
15
+ curation?: {
16
+ labels?: string[];
17
+ };
18
+ }
19
+ /** Number of records per category name. */
20
+ export declare function countByCategory<T extends Taxonomable>(records: T[]): Map<string, number>;
21
+ /** Number of records per primary stack (combines `stack` and `stacks`). */
22
+ export declare function countByStack<T extends Taxonomable>(records: T[]): Map<string, number>;
23
+ /** Number of records per curated label (curation.labels). */
24
+ export declare function countByLabel<T extends Taxonomable>(records: T[]): Map<string, number>;
25
+ //# sourceMappingURL=taxonomy-counts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taxonomy-counts.d.ts","sourceRoot":"","sources":["../../src/lib/taxonomy-counts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAClC;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EACnD,OAAO,EAAE,CAAC,EAAE,GACX,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrB;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAChD,OAAO,EAAE,CAAC,EAAE,GACX,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAarB;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAChD,OAAO,EAAE,CAAC,EAAE,GACX,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQrB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Build-time helpers that compute taxonomy counts from an array of
3
+ * records. Generic over any object that carries the index-payload
4
+ * fields the UI cares about (`category`, `stack` / `stacks`,
5
+ * `curation.labels`). No filesystem / data import — consumers pass
6
+ * the records explicitly so this stays testable.
7
+ *
8
+ * All functions are synchronous and cheap — they're called from
9
+ * Astro page frontmatter at build time, never on the client.
10
+ */
11
+ /** Number of records per category name. */
12
+ export function countByCategory(records) {
13
+ const m = new Map();
14
+ for (const r of records) {
15
+ const k = r.category || "Other";
16
+ m.set(k, (m.get(k) ?? 0) + 1);
17
+ }
18
+ return m;
19
+ }
20
+ /** Number of records per primary stack (combines `stack` and `stacks`). */
21
+ export function countByStack(records) {
22
+ const m = new Map();
23
+ for (const r of records) {
24
+ const keys = new Set([r.stack, ...(r.stacks ?? [])].filter((s) => Boolean(s)));
25
+ for (const k of keys) {
26
+ m.set(k, (m.get(k) ?? 0) + 1);
27
+ }
28
+ }
29
+ return m;
30
+ }
31
+ /** Number of records per curated label (curation.labels). */
32
+ export function countByLabel(records) {
33
+ const m = new Map();
34
+ for (const r of records) {
35
+ for (const l of r.curation?.labels ?? []) {
36
+ m.set(l, (m.get(l) ?? 0) + 1);
37
+ }
38
+ }
39
+ return m;
40
+ }
41
+ //# sourceMappingURL=taxonomy-counts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taxonomy-counts.js","sourceRoot":"","sources":["../../src/lib/taxonomy-counts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,2CAA2C;AAC3C,MAAM,UAAU,eAAe,CAC7B,OAAY;IAEZ,MAAM,CAAC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;QAChC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,YAAY,CAC1B,OAAY;IAEZ,MAAM,CAAC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAC/B,CACF,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAC1B,OAAY;IAEZ,MAAM,CAAC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grove-dev/astro",
3
- "version": "0.1.4",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "description": "Astro framework adapter for Grove. Components, layouts, design tokens, and a default template.",
6
6
  "license": "MIT",
@@ -37,15 +37,15 @@
37
37
  "import": "./dist/index.js"
38
38
  },
39
39
  "./styles.css": "./src/styles.css",
40
+ "./components/*.astro": "./src/components/*.astro",
40
41
  "./components/*": "./src/components/*.astro",
41
- "./layouts/*": "./src/layouts/*.astro",
42
- "./package.json": "./package.json",
43
- "./templates/*": "./templates/*"
42
+ "./layouts/*.astro": "./src/layouts/*.astro",
43
+ "./layouts/*": "./src/layouts/*.astro"
44
44
  },
45
45
  "dependencies": {
46
- "@grove-dev/core": "0.1.1",
47
- "@grove-dev/ui": "0.1.1",
48
- "astro": "^6.4.4"
46
+ "astro": "^6.4.4",
47
+ "@grove-dev/core": "0.2.2",
48
+ "@grove-dev/ui": "0.2.2"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "astro": "^6.0.0"
@@ -0,0 +1,83 @@
1
+ ---
2
+ /**
3
+ * AppSection — a titled "lens" of records on the home page.
4
+ *
5
+ * Renders a SectionHeader + a 3-column grid of `ItemCard`s, with an
6
+ * optional "View all" link in the header. Generic: passes the
7
+ * record list + per-record href through to `ItemCard`.
8
+ *
9
+ * The component is the workhorse of the home page — featured, hot,
10
+ * new, mature, "by category", "by stack" — all of them are just an
11
+ * `AppSection` with a different `id` + `title` + sliced list.
12
+ */
13
+
14
+ import type { Resource } from "@grove-dev/core";
15
+ import ItemCard from "./ItemCard.astro";
16
+ import SectionHeader from "../layouts/SectionHeader.astro";
17
+
18
+ interface Props {
19
+ /** Used as the section id (so deep links work) and a stable key. */
20
+ id: string;
21
+ title: string;
22
+ description?: string;
23
+ items: Resource[];
24
+ /** Detail page href builder. */
25
+ hrefFor: (record: Resource) => string;
26
+ /** Optional "View all" link rendered in the header. */
27
+ viewAllHref?: string;
28
+ viewAllLabel?: string;
29
+ /** Optional eyebrow. */
30
+ eyebrow?: string;
31
+ /** Empty-state copy. */
32
+ emptyLabel?: string;
33
+ /** Extra class on the outer <section>. */
34
+ class?: string;
35
+ }
36
+
37
+ const {
38
+ id,
39
+ title,
40
+ description,
41
+ items,
42
+ hrefFor,
43
+ viewAllHref,
44
+ viewAllLabel = "View all",
45
+ eyebrow,
46
+ emptyLabel = "No records yet.",
47
+ class: className = "",
48
+ } = Astro.props;
49
+ ---
50
+
51
+ <section
52
+ id={id}
53
+ class:list={["grove-section", className]}
54
+ aria-labelledby={`${id}-heading`}
55
+ >
56
+ <div class="grove-container-wide mx-auto px-5 sm:px-7">
57
+ <SectionHeader
58
+ eyebrow={eyebrow}
59
+ title={title}
60
+ description={description}
61
+ >
62
+ {viewAllHref && (
63
+ <a
64
+ slot="actions"
65
+ href={viewAllHref}
66
+ class="text-sm font-medium text-ink-500 hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"
67
+ >
68
+ {viewAllLabel} →
69
+ </a>
70
+ )}
71
+ </SectionHeader>
72
+
73
+ {items.length === 0 ? (
74
+ <p class="m-0 text-sm text-ink-500 dark:text-ink-400">{emptyLabel}</p>
75
+ ) : (
76
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
77
+ {items.map((record) => (
78
+ <ItemCard record={record} href={hrefFor(record)} />
79
+ ))}
80
+ </div>
81
+ )}
82
+ </div>
83
+ </section>