@grove-dev/astro 0.2.10 → 0.2.14

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 (237) hide show
  1. package/README.md +26 -18
  2. package/dist/lib/display.d.ts +11 -17
  3. package/dist/lib/display.d.ts.map +1 -1
  4. package/dist/lib/display.js +43 -43
  5. package/dist/lib/display.js.map +1 -1
  6. package/dist/lib/format.d.ts +6 -7
  7. package/dist/lib/format.d.ts.map +1 -1
  8. package/dist/lib/format.js +11 -12
  9. package/dist/lib/format.js.map +1 -1
  10. package/dist/lib/lenses.d.ts +35 -73
  11. package/dist/lib/lenses.d.ts.map +1 -1
  12. package/dist/lib/lenses.js +144 -126
  13. package/dist/lib/lenses.js.map +1 -1
  14. package/dist/lib/repo.d.ts +12 -22
  15. package/dist/lib/repo.d.ts.map +1 -1
  16. package/dist/lib/repo.js +13 -25
  17. package/dist/lib/repo.js.map +1 -1
  18. package/dist/lib/scores.d.ts +15 -16
  19. package/dist/lib/scores.d.ts.map +1 -1
  20. package/dist/lib/scores.js +4 -11
  21. package/dist/lib/scores.js.map +1 -1
  22. package/dist/lib/search.d.ts +44 -129
  23. package/dist/lib/search.d.ts.map +1 -1
  24. package/dist/lib/search.js +192 -180
  25. package/dist/lib/search.js.map +1 -1
  26. package/dist/lib/taxonomy-counts.d.ts +7 -21
  27. package/dist/lib/taxonomy-counts.d.ts.map +1 -1
  28. package/dist/lib/taxonomy-counts.js +12 -31
  29. package/dist/lib/taxonomy-counts.js.map +1 -1
  30. package/package.json +4 -4
  31. package/src/components/CategoryGrid.astro +43 -31
  32. package/src/components/ContributorsGrid.astro +3 -3
  33. package/src/components/CurationGrid.astro +41 -0
  34. package/src/components/ExploreByStack.astro +6 -0
  35. package/src/components/FilterGroupMenu.astro +49 -0
  36. package/src/components/FilterOptions.astro +44 -0
  37. package/src/components/Hero.astro +198 -147
  38. package/src/components/Icon.astro +10 -3
  39. package/src/components/IndexRow.astro +75 -0
  40. package/src/components/ItemCard.astro +266 -248
  41. package/src/components/Pagination.astro +63 -0
  42. package/src/components/RecordSection.astro +70 -34
  43. package/src/components/RefinePanel.astro +115 -141
  44. package/src/components/StackGrid.astro +32 -24
  45. package/src/components/WhyThisExists.astro +32 -32
  46. package/src/layouts/BaseLayout.astro +104 -49
  47. package/src/layouts/Container.astro +5 -44
  48. package/src/layouts/Footer.astro +31 -20
  49. package/src/layouts/Header.astro +40 -19
  50. package/src/layouts/SectionHeader.astro +27 -43
  51. package/src/layouts/Seo.astro +38 -56
  52. package/src/layouts/ThemeToggle.astro +35 -48
  53. package/src/lib/display.ts +46 -53
  54. package/src/lib/format.ts +11 -12
  55. package/src/lib/lenses.ts +151 -172
  56. package/src/lib/repo.ts +17 -28
  57. package/src/lib/scores.ts +15 -19
  58. package/src/lib/search.ts +218 -291
  59. package/src/lib/taxonomy-counts.ts +12 -46
  60. package/src/styles.css +209 -1
  61. package/templates/default/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  62. package/templates/default/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  63. package/templates/default/.github/ISSUE_TEMPLATE/record_submission.md +48 -0
  64. package/templates/default/.github/pull_request_template.md +27 -0
  65. package/templates/default/.github/workflows/build.yml +55 -0
  66. package/templates/default/.github/workflows/cleanup-stale-records.yml +48 -0
  67. package/templates/default/.github/workflows/daily-refresh.yml +53 -0
  68. package/templates/default/.github/workflows/sync-contributors.yml +53 -0
  69. package/templates/default/.github/workflows/sync-github-metadata.yml +58 -0
  70. package/templates/default/.github/workflows/validate-data.yml +35 -0
  71. package/templates/default/data/records/coolify.yml +46 -0
  72. package/templates/default/data/records/gitea.yml +46 -0
  73. package/templates/default/data/records/hoppscotch.yml +47 -0
  74. package/templates/default/data/records/inventree.yml +47 -0
  75. package/templates/default/data/records/label-studio.yml +46 -0
  76. package/templates/default/data/records/logseq.yml +48 -0
  77. package/templates/default/data/records/private-gpt.yml +47 -0
  78. package/templates/default/data/records/wakapi.yml +44 -0
  79. package/templates/default/data/taxonomy/categories.yml +47 -0
  80. package/templates/default/data/taxonomy/distribution-channels.yml +43 -0
  81. package/templates/default/data/taxonomy/platforms.yml +27 -0
  82. package/templates/default/data/taxonomy/stacks.yml +41 -0
  83. package/templates/default/grove.config.ts +1 -1
  84. package/templates/default/package.json +5 -4
  85. package/templates/default/public/icons/stacks/bun.svg +1 -0
  86. package/templates/default/public/icons/stacks/clojure.svg +1 -0
  87. package/templates/default/public/icons/stacks/clojurescript.svg +1 -0
  88. package/templates/default/public/icons/stacks/deno.svg +1 -0
  89. package/templates/default/public/icons/stacks/django.svg +1 -0
  90. package/templates/default/public/icons/stacks/docker.svg +1 -0
  91. package/templates/default/public/icons/stacks/go.svg +1 -0
  92. package/templates/default/public/icons/stacks/llm.svg +1 -0
  93. package/templates/default/public/icons/stacks/node.js.svg +1 -0
  94. package/templates/default/public/icons/stacks/sveltekit.svg +1 -0
  95. package/templates/default/public/icons/stacks/vue.svg +1 -0
  96. package/templates/default/public/llms-full.txt +197 -5
  97. package/templates/default/public/llms.txt +107 -6
  98. package/templates/default/public/sitemap.xml +63 -0
  99. package/templates/default/scripts/build-llms.mjs +123 -0
  100. package/templates/default/scripts/build-records-json.mjs +27 -0
  101. package/templates/default/scripts/build-sitemap.mjs +20 -0
  102. package/templates/default/scripts/cleanup-stale-records.mjs +20 -0
  103. package/templates/default/scripts/enrich-github-metadata.mjs +99 -0
  104. package/templates/default/scripts/fetch-icons.mjs +137 -0
  105. package/templates/default/scripts/migrate-legacy-to-schema-v1.mjs +86 -0
  106. package/templates/default/scripts/parse-legacy-readme.mjs +59 -0
  107. package/templates/default/scripts/refresh-records-activity.mjs +24 -0
  108. package/templates/default/scripts/repair-license-format.mjs +72 -0
  109. package/templates/default/scripts/report-cleanup-candidates.mjs +21 -0
  110. package/templates/default/scripts/seed-from-github.mjs +62 -0
  111. package/templates/default/scripts/sync-contributors.mjs +145 -0
  112. package/templates/default/scripts/sync-github-metadata.mjs +28 -0
  113. package/templates/default/scripts/validate-records.mjs +27 -0
  114. package/templates/default/src/data/records.ts +138 -51
  115. package/templates/default/src/pages/404.astro +64 -0
  116. package/templates/default/src/pages/{projects/[slug].astro → [slug]/[recordSlug].astro} +236 -66
  117. package/templates/default/src/pages/[slug]/index.astro +477 -0
  118. package/templates/default/src/pages/about.astro +165 -82
  119. package/templates/default/src/pages/apps/[recordSlug].astro +33 -0
  120. package/templates/default/src/pages/contributors.astro +184 -0
  121. package/templates/default/src/pages/index.astro +119 -162
  122. package/templates/default/src/pages/submit.astro +276 -114
  123. package/templates/default/wrangler.jsonc +28 -0
  124. package/LICENSE +0 -21
  125. package/src/components/AppsIndexRow.astro +0 -204
  126. package/src/components/AppsPagination.astro +0 -126
  127. package/templates/default/.astro/content.d.ts +0 -155
  128. package/templates/default/.astro/settings.json +0 -5
  129. package/templates/default/.astro/types.d.ts +0 -1
  130. package/templates/default/data/generated/apps.json +0 -1
  131. package/templates/default/data/generated/records.full.json +0 -8
  132. package/templates/default/data/generated/records.index.json +0 -7
  133. package/templates/default/data/generated/records.json +0 -8
  134. package/templates/default/data/generated/repo-stats.json +0 -1
  135. package/templates/default/data/generated/site-config.json +0 -31
  136. package/templates/default/dist/_astro/BaseLayout.Dz_HR-68.css +0 -1
  137. package/templates/default/dist/about/index.html +0 -165
  138. package/templates/default/dist/icons/platforms/android.svg +0 -1
  139. package/templates/default/dist/icons/platforms/apple-dark.svg +0 -1
  140. package/templates/default/dist/icons/platforms/apple-light.svg +0 -1
  141. package/templates/default/dist/icons/platforms/chrome.svg +0 -1
  142. package/templates/default/dist/icons/platforms/chromeos.svg +0 -5
  143. package/templates/default/dist/icons/platforms/desktop.svg +0 -4
  144. package/templates/default/dist/icons/platforms/embedded.svg +0 -5
  145. package/templates/default/dist/icons/platforms/ios.svg +0 -1
  146. package/templates/default/dist/icons/platforms/linux.svg +0 -1
  147. package/templates/default/dist/icons/platforms/macos.svg +0 -1
  148. package/templates/default/dist/icons/platforms/ubuntu.svg +0 -1
  149. package/templates/default/dist/icons/platforms/web.svg +0 -5
  150. package/templates/default/dist/icons/platforms/windows.svg +0 -6
  151. package/templates/default/dist/icons/stacks/android.svg +0 -1
  152. package/templates/default/dist/icons/stacks/apple-dark.svg +0 -1
  153. package/templates/default/dist/icons/stacks/apple-light.svg +0 -1
  154. package/templates/default/dist/icons/stacks/capacitor.svg +0 -14
  155. package/templates/default/dist/icons/stacks/dart.svg +0 -1
  156. package/templates/default/dist/icons/stacks/firebase.svg +0 -1
  157. package/templates/default/dist/icons/stacks/flutter.svg +0 -1
  158. package/templates/default/dist/icons/stacks/graphql.svg +0 -1
  159. package/templates/default/dist/icons/stacks/ionic.svg +0 -4
  160. package/templates/default/dist/icons/stacks/java.svg +0 -1
  161. package/templates/default/dist/icons/stacks/javascript.svg +0 -1
  162. package/templates/default/dist/icons/stacks/kotlin.svg +0 -1
  163. package/templates/default/dist/icons/stacks/mongodb.svg +0 -1
  164. package/templates/default/dist/icons/stacks/nodejs.svg +0 -1
  165. package/templates/default/dist/icons/stacks/python.svg +0 -1
  166. package/templates/default/dist/icons/stacks/react-native.svg +0 -1
  167. package/templates/default/dist/icons/stacks/react.svg +0 -1
  168. package/templates/default/dist/icons/stacks/rust.svg +0 -1
  169. package/templates/default/dist/icons/stacks/solidity.svg +0 -1
  170. package/templates/default/dist/icons/stacks/swift.svg +0 -1
  171. package/templates/default/dist/icons/stacks/tensorflow.svg +0 -1
  172. package/templates/default/dist/icons/stacks/typescript.svg +0 -1
  173. package/templates/default/dist/index.html +0 -55
  174. package/templates/default/dist/llms-full.txt +0 -14
  175. package/templates/default/dist/llms.txt +0 -11
  176. package/templates/default/dist/og-image.svg +0 -48
  177. package/templates/default/dist/projects/index.html +0 -61
  178. package/templates/default/dist/robots.txt +0 -53
  179. package/templates/default/dist/sitemap.xml +0 -21
  180. package/templates/default/dist/submit/index.html +0 -69
  181. package/templates/default/node_modules/.bin/astro +0 -21
  182. package/templates/default/node_modules/.bin/astro-check +0 -21
  183. package/templates/default/node_modules/.bin/grove +0 -21
  184. package/templates/default/node_modules/.bin/jiti +0 -21
  185. package/templates/default/node_modules/.bin/prettier +0 -21
  186. package/templates/default/node_modules/.bin/rollup +0 -21
  187. package/templates/default/node_modules/.bin/tsc +0 -21
  188. package/templates/default/node_modules/.bin/tsserver +0 -21
  189. package/templates/default/node_modules/.bin/tsx +0 -21
  190. package/templates/default/node_modules/.bin/vite +0 -21
  191. package/templates/default/node_modules/.bin/yaml +0 -21
  192. package/templates/default/node_modules/.vite/deps/_metadata.json +0 -79
  193. package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js +0 -433
  194. package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js.map +0 -7
  195. package/templates/default/node_modules/.vite/deps/astro___aria-query.js +0 -5
  196. package/templates/default/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  197. package/templates/default/node_modules/.vite/deps/astro___axobject-query.js +0 -5
  198. package/templates/default/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  199. package/templates/default/node_modules/.vite/deps/astro___html-escaper.js +0 -9
  200. package/templates/default/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  201. package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -241
  202. package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -7
  203. package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js +0 -1592
  204. package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js.map +0 -7
  205. package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
  206. package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
  207. package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js +0 -186
  208. package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js.map +0 -7
  209. package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js +0 -48
  210. package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js.map +0 -7
  211. package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js +0 -6779
  212. package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js.map +0 -7
  213. package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js +0 -33
  214. package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js.map +0 -7
  215. package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js +0 -88
  216. package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js.map +0 -7
  217. package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js +0 -72
  218. package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js.map +0 -7
  219. package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js +0 -3757
  220. package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js.map +0 -7
  221. package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js +0 -54
  222. package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js.map +0 -7
  223. package/templates/default/node_modules/.vite/deps/package.json +0 -3
  224. package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js +0 -213
  225. package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js.map +0 -7
  226. package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js +0 -525
  227. package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js.map +0 -7
  228. package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js +0 -1020
  229. package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js.map +0 -7
  230. package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js +0 -147
  231. package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js.map +0 -7
  232. package/templates/default/src/components/layout/BaseLayout.astro +0 -47
  233. package/templates/default/src/components/layout/Container.astro +0 -18
  234. package/templates/default/src/components/layout/Footer.astro +0 -27
  235. package/templates/default/src/components/layout/Header.astro +0 -43
  236. package/templates/default/src/lib/markdown.ts +0 -171
  237. package/templates/default/src/pages/projects/index.astro +0 -193
package/README.md CHANGED
@@ -12,33 +12,40 @@ pnpm add @grove-dev/astro
12
12
 
13
13
  ```txt
14
14
  src/
15
- ├── components/ # ItemCard, CategoryGrid, HealthBadge, ScoreBars,
16
- │ # DirectoryFilters, DirectoryHero, LensTabs,
17
- │ # ActiveFilterChips, MethodologyPanel, Pagination,
18
- │ # ProjectDetail, SubmitDraft
15
+ ├── components/ # ItemCard, CategoryGrid, ScoreBars, IndexRow,
16
+ │ # Pagination, RecordSection, RefinePanel, Hero,
17
+ │ # Icon, SmartLensTabs, ExploreByCategory,
18
+ │ # ExploreByStack, WhyThisExists, CurationGrid,
19
+ │ # ContributorsGrid, StackGrid, MinimalAbout,
20
+ │ # OriginalCollection, DecisionRow, FilterGroupMenu,
21
+ │ # FilterOptions
19
22
  ├── layouts/ # BaseLayout
20
23
  ├── styles.css # design tokens + utility classes
21
- └── index.ts # re-exports @grove-dev/ui
24
+ └── index.ts # re-exports @grove-dev/core + @grove-dev/ui
22
25
 
23
26
  templates/
24
27
  └── default/ # full Astro starter: pages/, layouts/, public/, data/,
25
- # .github/, astro.config.mjs, tailwind.config.mjs
28
+ # .github/, astro.config.mjs
26
29
  ```
27
30
 
28
31
  The package publishes `dist/`, `src/`, and `templates/`. The `templates/` directory is **not** imported by consumers; it is copied by `@grove-dev/cli` at scaffold time.
29
32
 
33
+ > **Component naming:** `ItemCard` is the V1 published API name (kept for stability — it appears in user docs and downstream sites). The internal `IndexRow` / `Pagination` / `RecordSection` aliases are V1 canonical names; the old `AppsIndexRow` / `AppsPagination` / `ItemSection` V0 names have been removed.
34
+
30
35
  ## Usage in a space
31
36
 
32
37
  ```astro
33
38
  ---
34
- import { ItemCard, ScoreBars, HealthBadge, DirectoryFilters } from "@grove-dev/astro";
39
+ import ItemCard from "@grove-dev/astro/components/ItemCard.astro";
40
+ import IndexRow from "@grove-dev/astro/components/IndexRow.astro";
41
+ import { ScoreBars, filterRecords, sortDisplay } from "@grove-dev/astro";
35
42
  import "@grove-dev/astro/styles.css";
36
43
  import BaseLayout from "@grove-dev/astro/layouts/BaseLayout.astro";
37
- import apps from "../data/generated/apps.json";
44
+ import records from "../data/generated/records.json";
38
45
  ---
39
46
  <BaseLayout title="My Grove space">
40
- <DirectoryFilters facets={...} />
41
- {apps.map((app) => <ItemCard item={app} />)}
47
+ <RefinePanel initial={filters} facets={facets} />
48
+ {records.map((r) => <ItemCard record={r} href={`/projects/${r.slug}`} />)}
42
49
  </BaseLayout>
43
50
  ```
44
51
 
@@ -48,16 +55,17 @@ Components are imported by path, not through the barrel — that lets `astro che
48
55
 
49
56
  `templates/default/` is a complete Astro starter. It contains:
50
57
 
51
- - `src/pages/` — index, item detail, search, about, methodology, submit, sitemap
52
- - `src/components/` — layout primitives, page sections
53
- - `src/data/`placeholder dataset; replaced by `grove build-data`
58
+ - `src/pages/` — index, contributors, about, submit, 404, sitemap.xml, plus blueprint-aware dynamic routes at `[slug]/index.astro` (list) and `[slug]/[recordSlug].astro` (detail), plus `apps/[recordSlug].astro` (V0→V1 301 redirect)
59
+ - `src/components/` — site-specific page sections (none by default; the V1 surface is in `packages/astro/src/components/`)
60
+ - `src/data/records.ts`typed loader for `data/generated/records.full.json` (V0 `src/data/config.ts` is replaced)
54
61
  - `src/lib/` — site-specific helpers (no business logic)
55
- - `public/` — icons, OG image, robots
56
- - `data/` — empty tree for the gardener to populate
57
- - `astro.config.mjs`, `tailwind.config.mjs`
58
- - `.github/` issue templates
62
+ - `scripts/` — `build-llms.mjs` (regenerates `llms-full.txt` only, preserves committed `llms.txt`), `fetch-icons.mjs`, `migrate-legacy-to-schema-v1.mjs`
63
+ - `public/` — icons, OG image, robots, `llms.txt`, `llms-full.txt`
64
+ - `data/` — empty tree for the gardener to populate; `data/records/<slug>.yml` is the V1 layout (V0 was `data/apps/*.yml`)
65
+ - `astro.config.mjs` (no `tailwind.config.mjs` by default; plain CSS + design tokens)
66
+ - `.github/` — issue templates + 11 GitHub Actions workflows (validate, build, deploy, sync, cleanup, …)
59
67
 
60
- Business logic (filtering, sorting, scoring, faceting) is **imported from `@grove-dev/ui`**, never re-implemented in the template.
68
+ Business logic (filtering, sorting, scoring, faceting) is **imported from `@grove-dev/ui`**, never re-implemented in the template. The V0 commands (`grove build-data`, `grove build-llms-full`, `grove analyze`, `grove enrich`) have been replaced with V1 names (`grove generate`, `grove llms`, `grove sync github`).
61
69
 
62
70
  ## Layering
63
71
 
@@ -1,33 +1,27 @@
1
1
  /**
2
- * Display-name helpers for curation labels, statuses, lenses, and
3
- * sort options. Internal ids (e.g. "hot", "stale", "production-like")
4
- * stay stable for URL state and data files; this module owns the
5
- * mapping from id → user-facing string.
2
+ * Display-name helpers for curation labels, statuses, lenses, and sort
3
+ * options. Internal ids (e.g. "hot", "stale", "production-like") stay
4
+ * stable for URL state and data files; this module owns the mapping
5
+ * from id → user-facing string.
6
6
  *
7
- * Every export is dependency-free so it can be imported by both
7
+ * Every export here is dependency-free so it can be imported by both
8
8
  * server-rendered Astro components and any client-side script.
9
- *
10
- * Status ids accept the `HealthStatus` union from `@grove-dev/core`
11
- * plus a couple of curated composites (e.g. "needs-maintainer")
12
- * that appear in URL state.
13
9
  */
14
- import type { AppLabel, HealthStatus } from "@grove-dev/core";
15
- export type LabelId = AppLabel;
10
+ export type LabelId = "new" | "hot" | "mature" | "featured";
16
11
  export declare const LABEL_DISPLAY: Record<LabelId, string>;
17
- /** Pretty-print a slug to Title Case. Used for arbitrary slugs. */
18
- export declare function prettySlug(slug: string | null | undefined): string;
19
12
  /** Returns a human-readable name for a label id, or null if unknown. */
20
13
  export declare function labelDisplay(id: string | null | undefined): string | null;
21
14
  /**
22
- * Canonical status display map. Includes the curated
23
- * "needs-maintainer" composite that's emitted by the
24
- * `needs-maintainer` lens.
15
+ * Canonical status ids, including the curated "needs-maintainer"
16
+ * composite that's emitted by the `needs-maintainer` lens (it expands
17
+ * to "stale,quiet" in URL state but presents as one chip).
25
18
  */
26
19
  export declare const STATUS_DISPLAY: Record<string, string>;
27
- export declare function statusDisplay(s: HealthStatus | string | null | undefined): string;
20
+ export declare function statusDisplay(s: string | null | undefined): string;
28
21
  export declare const LENS_DISPLAY: Record<string, string>;
29
22
  export declare function lensDisplay(id: string | null | undefined): string;
30
23
  export declare const SORT_DISPLAY: Record<string, string>;
31
24
  export declare function sortDisplay(id: string | null | undefined): string;
25
+ export declare function prettySlug(value: string | null | undefined): string;
32
26
  export declare const STATUS_OPTIONS: string[];
33
27
  //# sourceMappingURL=display.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/lib/display.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACb,MAAM,iBAAiB,CAAC;AAGzB,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC;AAE/B,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAKjD,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CASlE;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMzE;AAGD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAYjD,CAAC;AAEF,wBAAgB,aAAa,CAC3B,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,MAAM,CAGR;AAGD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO/C,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAGD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAM/C,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAMD,eAAO,MAAM,cAAc,EAAE,MAAM,EAOlC,CAAC"}
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/lib/display.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE5D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAKjD,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGzE;AAGD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAYjD,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGlE;AAGD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAY/C,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAGD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAM/C,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOnE;AAMD,eAAO,MAAM,cAAc,EAAE,MAAM,EAYlC,CAAC"}
@@ -1,15 +1,11 @@
1
1
  /**
2
- * Display-name helpers for curation labels, statuses, lenses, and
3
- * sort options. Internal ids (e.g. "hot", "stale", "production-like")
4
- * stay stable for URL state and data files; this module owns the
5
- * mapping from id → user-facing string.
2
+ * Display-name helpers for curation labels, statuses, lenses, and sort
3
+ * options. Internal ids (e.g. "hot", "stale", "production-like") stay
4
+ * stable for URL state and data files; this module owns the mapping
5
+ * from id → user-facing string.
6
6
  *
7
- * Every export is dependency-free so it can be imported by both
7
+ * Every export here is dependency-free so it can be imported by both
8
8
  * server-rendered Astro components and any client-side script.
9
- *
10
- * Status ids accept the `HealthStatus` union from `@grove-dev/core`
11
- * plus a couple of curated composites (e.g. "needs-maintainer")
12
- * that appear in URL state.
13
9
  */
14
10
  export const LABEL_DISPLAY = {
15
11
  new: "Recently added",
@@ -17,66 +13,56 @@ export const LABEL_DISPLAY = {
17
13
  mature: "Established",
18
14
  featured: "Featured",
19
15
  };
20
- /** Pretty-print a slug to Title Case. Used for arbitrary slugs. */
21
- export function prettySlug(slug) {
22
- if (!slug)
23
- return "";
24
- return slug
25
- .replace(/[-_]+/g, " ")
26
- .replace(/\s+/g, " ")
27
- .trim()
28
- .split(" ")
29
- .map((w) => (w ? w[0].toUpperCase() + w.slice(1) : ""))
30
- .join(" ");
31
- }
32
16
  /** Returns a human-readable name for a label id, or null if unknown. */
33
17
  export function labelDisplay(id) {
34
18
  if (!id)
35
19
  return null;
36
- if (id in LABEL_DISPLAY) {
37
- return LABEL_DISPLAY[id];
38
- }
39
- return prettySlug(id);
20
+ return LABEL_DISPLAY[id] ?? null;
40
21
  }
41
- // ── Status ids (the `health.status` field on a record) ───────────
22
+ // ── Status ids (the `status` field on an item) ───────────────────────
42
23
  /**
43
- * Canonical status display map. Includes the curated
44
- * "needs-maintainer" composite that's emitted by the
45
- * `needs-maintainer` lens.
24
+ * Canonical status ids, including the curated "needs-maintainer"
25
+ * composite that's emitted by the `needs-maintainer` lens (it expands
26
+ * to "stale,quiet" in URL state but presents as one chip).
46
27
  */
47
28
  export const STATUS_DISPLAY = {
48
29
  active: "Active",
49
30
  mature: "Mature",
31
+ quiet: "Quiet",
50
32
  stale: "Not recently active",
51
33
  inactive: "Inactive",
34
+ "needs-maintainer": "Needs maintainer",
52
35
  archived: "Archived",
53
- unknown: "Unknown",
54
36
  historical: "Historical",
55
37
  needs_review: "Needs review",
56
- quiet: "Quiet",
57
38
  unavailable: "Unavailable",
58
- "needs-maintainer": "Needs maintainer",
39
+ unknown: "Unknown",
59
40
  };
60
41
  export function statusDisplay(s) {
61
42
  if (!s)
62
43
  return STATUS_DISPLAY.unknown;
63
- return STATUS_DISPLAY[s] ?? prettySlug(s);
44
+ return STATUS_DISPLAY[s] ?? STATUS_DISPLAY.unknown;
64
45
  }
65
- // ── Lens ids (the curated tabs on /projects) ─────────────────────
46
+ // ── Lens ids (the curated tabs on /items) ────────────────────────────
66
47
  export const LENS_DISPLAY = {
67
- all: "All",
68
- hot: "Trending",
48
+ all: "All items",
69
49
  new: "Recently added",
50
+ hot: "Trending",
70
51
  mature: "Established",
71
- featured: "Featured",
72
- "needs-review": "Needs review",
52
+ "good-to-learn": "Good to learn",
53
+ "production-like": "Production-like",
54
+ "beginner-friendly": "Beginner friendly",
55
+ "contribution-ready": "Contribution ready",
56
+ launches: "Launches",
57
+ "actively-developed": "Active",
58
+ "needs-maintainer": "Needs maintainer",
73
59
  };
74
60
  export function lensDisplay(id) {
75
61
  if (!id)
76
62
  return LENS_DISPLAY.all;
77
- return LENS_DISPLAY[id] ?? prettySlug(id);
63
+ return LENS_DISPLAY[id] ?? id;
78
64
  }
79
- // ── Sort ids (the /projects sort dropdown) ───────────────────────
65
+ // ── Sort ids (the /items sort dropdown) ──────────────────────────────
80
66
  export const SORT_DISPLAY = {
81
67
  "recently-updated": "Recently updated",
82
68
  "most-starred": "Most starred",
@@ -87,18 +73,32 @@ export const SORT_DISPLAY = {
87
73
  export function sortDisplay(id) {
88
74
  if (!id)
89
75
  return SORT_DISPLAY["recently-updated"];
90
- return SORT_DISPLAY[id] ?? prettySlug(id);
76
+ return SORT_DISPLAY[id] ?? id;
77
+ }
78
+ export function prettySlug(value) {
79
+ if (!value)
80
+ return "";
81
+ return value
82
+ .split(/[-_]+/)
83
+ .filter(Boolean)
84
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
85
+ .join(" ");
91
86
  }
92
- // ── Status option list (used by filter dropdowns) ───────────────
87
+ // ── Status option list (used by filter dropdowns) ───────────────────
93
88
  // "needs-maintainer" is included even though it's a derived/composite
94
89
  // status — filter dropdowns and chip rendering need to offer it
95
- // alongside the raw HealthStatus values.
90
+ // alongside the raw AppStatus values.
96
91
  export const STATUS_OPTIONS = [
97
92
  "active",
93
+ "mature",
98
94
  "quiet",
99
95
  "stale",
96
+ "inactive",
100
97
  "needs-maintainer",
101
98
  "archived",
99
+ "historical",
100
+ "needs_review",
101
+ "unavailable",
102
102
  "unknown",
103
103
  ];
104
104
  //# sourceMappingURL=display.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/lib/display.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/lib/display.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,8 @@
1
1
  /**
2
- * Shared formatting helpers.
2
+ * Shared formatting helpers used across components.
3
3
  *
4
- * Dependency-free importable by both server-rendered Astro
5
- * components and any future client-side script. No `Intl`-heavy
6
- * calls that would explode the client bundle.
4
+ * Keep these dependency-free so they can be imported by both server-
5
+ * rendered Astro components and any future client-side scripts.
7
6
  */
8
7
  /**
9
8
  * Format a number compactly: 1234 → "1.2k", 12345 → "12k", 79 → "79".
@@ -11,7 +10,7 @@
11
10
  */
12
11
  export declare function compact(n: number): string;
13
12
  /**
14
- * Format a star count compactly. Returns `null` when input is not a
13
+ * Format a star count compactly. Returns null when input is not a
15
14
  * finite number, so callers can render "—" without an extra guard.
16
15
  */
17
16
  export declare function formatStars(n: number | null | undefined): string | null;
@@ -20,11 +19,11 @@ export declare function formatStars(n: number | null | undefined): string | null
20
19
  * Returns "—" for missing or invalid input, and "today" for future
21
20
  * dates (which can happen with bad upstream timestamps).
22
21
  */
23
- export declare function formatRelative(date: string | null | undefined): string;
22
+ export declare function formatRelative(iso: string | null | undefined): string;
24
23
  /**
25
24
  * Format an ISO date as a localized short date (e.g. "Jan 5, 2026").
26
25
  * Returns the original string for unparseable input, or "—" when
27
26
  * no value is provided.
28
27
  */
29
- export declare function formatDate(date: string | null | undefined): string;
28
+ export declare function formatDate(iso: string | null | undefined): string;
30
29
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CActE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CASlE"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAcrE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CASjE"}
@@ -1,9 +1,8 @@
1
1
  /**
2
- * Shared formatting helpers.
2
+ * Shared formatting helpers used across components.
3
3
  *
4
- * Dependency-free importable by both server-rendered Astro
5
- * components and any future client-side script. No `Intl`-heavy
6
- * calls that would explode the client bundle.
4
+ * Keep these dependency-free so they can be imported by both server-
5
+ * rendered Astro components and any future client-side scripts.
7
6
  */
8
7
  /**
9
8
  * Format a number compactly: 1234 → "1.2k", 12345 → "12k", 79 → "79".
@@ -17,7 +16,7 @@ export function compact(n) {
17
16
  return n.toString();
18
17
  }
19
18
  /**
20
- * Format a star count compactly. Returns `null` when input is not a
19
+ * Format a star count compactly. Returns null when input is not a
21
20
  * finite number, so callers can render "—" without an extra guard.
22
21
  */
23
22
  export function formatStars(n) {
@@ -30,10 +29,10 @@ export function formatStars(n) {
30
29
  * Returns "—" for missing or invalid input, and "today" for future
31
30
  * dates (which can happen with bad upstream timestamps).
32
31
  */
33
- export function formatRelative(date) {
34
- if (!date)
32
+ export function formatRelative(iso) {
33
+ if (!iso)
35
34
  return "—";
36
- const d = new Date(date);
35
+ const d = new Date(iso);
37
36
  if (Number.isNaN(d.valueOf()))
38
37
  return "—";
39
38
  const days = Math.floor((Date.now() - d.valueOf()) / (1000 * 60 * 60 * 24));
@@ -58,12 +57,12 @@ export function formatRelative(date) {
58
57
  * Returns the original string for unparseable input, or "—" when
59
58
  * no value is provided.
60
59
  */
61
- export function formatDate(date) {
62
- if (!date)
60
+ export function formatDate(iso) {
61
+ if (!iso)
63
62
  return "—";
64
- const d = new Date(date);
63
+ const d = new Date(iso);
65
64
  if (Number.isNaN(d.valueOf()))
66
- return date;
65
+ return iso;
67
66
  return d.toLocaleDateString("en-US", {
68
67
  year: "numeric",
69
68
  month: "short",
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACnB,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAA4B;IACtD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAA+B;IAC5D,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IACtB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9B,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChC,IAAI,IAAI,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,OAAO,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACnC,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,GAAG,MAAM,QAAQ,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjC,OAAO,GAAG,KAAK,OAAO,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAA+B;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IACtB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QACnC,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;KACf,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACnB,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAA4B;IACtD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAA8B;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9B,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChC,IAAI,IAAI,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,OAAO,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACnC,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,GAAG,MAAM,QAAQ,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjC,OAAO,GAAG,KAAK,OAAO,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAA8B;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1C,OAAO,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QACnC,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;KACf,CAAC,CAAC;AACL,CAAC"}
@@ -1,96 +1,58 @@
1
1
  /**
2
- * Curated lenses for the list / discovery page.
2
+ * Curated lenses for the list page.
3
3
  *
4
- * A lens is a single-select view that translates into a (possibly
5
- * empty) URL search-param filter. The 6 lenses below cover the
6
- * "what should I look at next?" mental shortcut:
4
+ * Two flavors:
5
+ * - "label-based": maps to a single `labels` value (e.g. new / hot / mature).
6
+ * No extra curation needed every item already has a label.
7
+ * - "curator-assigned": maps to a `lenses` value on the item. The 5 anchor
8
+ * items get these populated. Other items will simply not match.
7
9
  *
8
- * - "all" — no filter
9
- * - "hot" — recent attention: stars 1000 OR pushed within 30d
10
- * - "new" — fresh entries: pushed within 7d OR curated "new" label
11
- * - "mature" — long-running projects: curated tier AND pushed > 365d ago
12
- * - "featured" — curated "featured" label
13
- * - "needs-review" — cleanup candidate (stale / archived / etc.)
10
+ * The lens id is what shows up in the URL as `?lens=...`. For
11
+ * label-based lenses, the lens id is the same as the label.
14
12
  *
15
- * The lens id is what shows up in the URL as `?lens=...`. Filter
16
- * resolution happens in `lib/search.ts` (`filterApps`).
17
- *
18
- * Keep this list in sync with `lib/search.ts` and any UI tabs.
13
+ * The 6 lenses in `PRIMARY_LENSES` are rendered as top-row tabs. The rest
14
+ * remain in the union type for URL deep-linking and future use, but are
15
+ * not shown in the UI by default.
19
16
  */
20
- import type { HealthStatus, ProjectRecord } from "@grove-dev/core";
21
- export type LensId = "all" | "hot" | "new" | "mature" | "featured" | "needs-review";
17
+ export type LensId = "all" | "new" | "hot" | "mature" | "good-to-learn" | "production-like" | "beginner-friendly" | "contribution-ready" | "launches" | "actively-developed" | "needs-maintainer";
22
18
  export interface LensDef {
23
19
  id: LensId;
24
20
  label: string;
25
- description: string;
26
- /**
27
- * Returns true when the record matches this lens. Designed to
28
- * operate on the index payload (`toIndexRecord`) shape — the
29
- * same record the list page already holds — so consumers don't
30
- * need to do a second fetch.
31
- */
32
- match: (record: AppLike) => boolean;
33
- }
34
- /**
35
- * The shape that lens matching actually reads. Decoupled from
36
- * `ProjectRecord` so the search lib can also pass the index payload
37
- * (which omits a few heavy fields) and tests can pass minimal
38
- * fixtures.
39
- */
40
- export interface AppLike {
41
- curation?: {
42
- labels?: string[];
43
- };
44
- health?: {
45
- status?: HealthStatus;
46
- tier?: "curated" | "listed" | "experimental" | "hidden";
47
- cleanupCandidate?: boolean;
48
- };
49
- github?: {
50
- stars?: number;
51
- pushedAt?: string | null;
52
- };
21
+ /** Short blurb shown when the lens is active. */
22
+ description?: string;
23
+ /** What URL state this lens implies. Empty means no filter. */
24
+ toParams: () => Record<string, string | string[]>;
53
25
  }
54
26
  export declare const LENSES: LensDef[];
55
27
  /**
56
- * The lenses rendered as top-row tabs on the list page. Order
57
- * matters — left to right. Keep this in sync with what the design
58
- * calls for; URL deep-linking accepts any `LensId` even if it's not
59
- * in the primary row.
28
+ * The 6 lenses shown as top-row tabs on /items. Order matters — left to right.
29
+ * Keep this list aligned with what the design calls for: All, signal lenses,
30
+ * and the two curator-assigned lenses that have any matches.
60
31
  */
61
32
  export declare const PRIMARY_LENSES: LensId[];
62
33
  export declare function lensById(id: string | null | undefined): LensDef | undefined;
63
- export declare function isPrimaryLens(id: string | null | undefined): id is LensId;
64
34
  /**
65
- * Resolve the active lens from URL search params. Returns the empty
66
- * string when no `lens=...` is set, so callers can treat both
67
- * "absent" and "unknown" identically.
35
+ * Check whether the current URL search params match a given lens's
36
+ * implied filter state. Used to highlight the active tab when a
37
+ * deep-link uses the underlying filter key (e.g. ?label=hot) rather
38
+ * than the abstract lens id (?lens=hot).
68
39
  */
69
- export declare function lensFromSearchParams(params: URLSearchParams): LensId | "";
40
+ export declare function isLensActive(lensId: LensId, sp: URLSearchParams): boolean;
41
+ export declare function isPrimaryLens(id: string | null | undefined): boolean;
70
42
  /**
71
- * `true` when the URL search params currently reflect this lens's
72
- * filter shape. The "all" lens is the implicit "no filter" state —
73
- * it's active only when no other lens-shaped params are present.
43
+ * Turn a lens's `toParams()` output into a URLSearchParams string
44
+ * (with the existing query preserved, then lens params merged on top).
74
45
  */
75
- export declare function isLensActive(lensId: LensId, params: URLSearchParams): boolean;
46
+ export declare function lensToQuery(id: LensId | null | undefined, current: URLSearchParams): string;
76
47
  /**
77
- * Build a `href` for a lens tab. Selecting a lens resets the other
78
- * "view" params (label, page) and applies only the chosen lens's
79
- * filter. Other filters (q, stack, platform, category, sort) are
80
- * preserved.
81
- *
82
- * `pathPrefix` is the URL root for the list page — typically
83
- * `/projects` for the project-directory blueprint, `/resources` for
84
- * resource-hub, `/entities` for ecosystem-map. Defaults to `/`.
48
+ * Read a `?lens=...` value from URL search params.
85
49
  */
86
- export declare function hrefForLens(lensId: LensId, params: URLSearchParams, pathPrefix?: string): string;
50
+ export declare function lensFromSearchParams(sp: URLSearchParams): LensId | null;
87
51
  /**
88
- * Apply the lens filter to a record array. When `lensId` is
89
- * `""` (no lens) or `"all"`, returns the input unchanged.
90
- *
91
- * Type-parameterized so the same helper works on `ProjectRecord[]`
92
- * and on the lighter index payload that list pages ship.
52
+ * Build a `href` for a lens tab. Per the single-select-view rule,
53
+ * clicking a lens resets the "view" params (lens, label, status, page)
54
+ * and applies only the chosen lens's params. Stack/Platform/Category/
55
+ * License/q/sort/density filters are preserved.
93
56
  */
94
- export declare function applyLens<T extends AppLike>(records: T[], lensId: LensId | ""): T[];
95
- export type { ProjectRecord };
57
+ export declare function hrefForLens(lensId: LensId | null | undefined, current: URLSearchParams, pathPrefix?: string): string;
96
58
  //# sourceMappingURL=lenses.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lenses.d.ts","sourceRoot":"","sources":["../../src/lib/lenses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACd,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,MAAM,GACd,KAAK,GACL,KAAK,GACL,KAAK,GACL,QAAQ,GACR,UAAU,GACV,cAAc,CAAC;AAEnB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CACrC;AAID;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACjC,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,YAAY,CAAC;QACtB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,cAAc,GAAG,QAAQ,CAAC;QACxD,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;CACH;AA4BD,eAAO,MAAM,MAAM,EAAE,OAAO,EAmD3B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,EAOlC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAG3E;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,EAAE,IAAI,MAAM,CAGzE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,eAAe,GACtB,MAAM,GAAG,EAAE,CAKb;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAK7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,eAAe,EACvB,UAAU,GAAE,MAAY,GACvB,MAAM,CAWR;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE,GAAG,CAAC,EAAE,CAKnF;AAID,YAAY,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"lenses.d.ts","sourceRoot":"","sources":["../../src/lib/lenses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,MAAM,GAEd,KAAK,GACL,KAAK,GACL,KAAK,GACL,QAAQ,GAER,eAAe,GACf,iBAAiB,GAEjB,mBAAmB,GACnB,oBAAoB,GACpB,UAAU,GAEV,oBAAoB,GACpB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;CACnD;AAED,eAAO,MAAM,MAAM,EAAE,OAAO,EA+D3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,EAOlC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAG3E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,GAAG,OAAO,CAiBzE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGpE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAc3F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAKvE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,OAAO,EAAE,eAAe,EACxB,UAAU,SAAW,GACpB,MAAM,CAsBR"}