@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.
- package/dist/index.d.ts +4 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -22
- package/dist/index.js.map +1 -1
- package/dist/layouts/index.d.ts +25 -0
- package/dist/layouts/index.d.ts.map +1 -0
- package/dist/layouts/index.js +25 -0
- package/dist/layouts/index.js.map +1 -0
- package/dist/lib/display.d.ts +33 -0
- package/dist/lib/display.d.ts.map +1 -0
- package/dist/lib/display.js +104 -0
- package/dist/lib/display.js.map +1 -0
- package/dist/lib/format.d.ts +30 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +73 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/index.d.ts +26 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +26 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/lenses.d.ts +96 -0
- package/dist/lib/lenses.d.ts.map +1 -0
- package/dist/lib/lenses.js +181 -0
- package/dist/lib/lenses.js.map +1 -0
- package/dist/lib/repo.d.ts +30 -0
- package/dist/lib/repo.d.ts.map +1 -0
- package/dist/lib/repo.js +44 -0
- package/dist/lib/repo.js.map +1 -0
- package/dist/lib/scores.d.ts +38 -0
- package/dist/lib/scores.d.ts.map +1 -0
- package/dist/lib/scores.js +83 -0
- package/dist/lib/scores.js.map +1 -0
- package/dist/lib/search.d.ts +199 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +357 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/taxonomy-counts.d.ts +25 -0
- package/dist/lib/taxonomy-counts.d.ts.map +1 -0
- package/dist/lib/taxonomy-counts.js +41 -0
- package/dist/lib/taxonomy-counts.js.map +1 -0
- package/package.json +7 -7
- package/src/components/AppSection.astro +83 -0
- package/src/components/AppsIndexRow.astro +204 -0
- package/src/components/AppsPagination.astro +126 -0
- package/src/components/CategoryGrid.astro +86 -17
- package/src/components/ContributorsGrid.astro +149 -0
- package/src/components/DecisionRow.astro +89 -0
- package/src/components/ExploreByCategory.astro +67 -0
- package/src/components/ExploreByStack.astro +72 -0
- package/src/components/Hero.astro +246 -0
- package/src/components/Icon.astro +223 -0
- package/src/components/ItemCard.astro +289 -30
- package/src/components/MinimalAbout.astro +82 -0
- package/src/components/OriginalCollection.astro +116 -0
- package/src/components/RefinePanel.astro +197 -0
- package/src/components/ScoreBars.astro +92 -22
- package/src/components/SmartLensTabs.astro +71 -0
- package/src/components/StackGrid.astro +132 -0
- package/src/components/WhyThisExists.astro +116 -0
- package/src/index.ts +61 -14
- package/src/layouts/BaseLayout.astro +227 -15
- package/src/layouts/Container.astro +57 -0
- package/src/layouts/Footer.astro +235 -0
- package/src/layouts/Header.astro +159 -0
- package/src/layouts/SectionHeader.astro +58 -0
- package/{templates/default/src/components/layout → src/layouts}/Seo.astro +62 -38
- package/src/layouts/ThemeToggle.astro +110 -0
- package/src/layouts/astro-shim.d.ts +29 -0
- package/src/layouts/index.ts +24 -0
- package/src/lib/display.ts +118 -0
- package/src/lib/format.ts +65 -0
- package/src/lib/index.ts +26 -0
- package/src/lib/lenses.ts +241 -0
- package/src/lib/repo.ts +44 -0
- package/src/lib/scores.ts +94 -0
- package/src/lib/search.ts +481 -0
- package/src/lib/taxonomy-counts.ts +60 -0
- package/src/styles.css +366 -638
- package/templates/default/.astro/content.d.ts +155 -0
- package/templates/default/.astro/settings.json +5 -0
- package/templates/default/.astro/types.d.ts +2 -0
- package/templates/default/astro.config.mjs +8 -6
- package/templates/default/content/pages/about.md +66 -0
- package/templates/default/content/pages/methodology.md +103 -0
- package/templates/default/data/generated/records.full.json +8 -0
- package/templates/default/data/generated/records.index.json +7 -0
- package/templates/default/data/generated/records.json +8 -0
- package/templates/default/data/generated/site-config.json +31 -0
- package/templates/default/data/records/.gitkeep +0 -0
- package/templates/default/dist/_astro/BaseLayout.DfpwR3jc.css +1 -0
- package/templates/default/dist/about/index.html +165 -0
- package/templates/default/dist/icons/platforms/android.svg +1 -0
- package/templates/default/dist/icons/platforms/apple-dark.svg +1 -0
- package/templates/default/dist/icons/platforms/apple-light.svg +1 -0
- package/templates/default/dist/icons/platforms/chrome.svg +1 -0
- package/templates/default/dist/icons/platforms/chromeos.svg +5 -0
- package/templates/default/dist/icons/platforms/desktop.svg +4 -0
- package/templates/default/dist/icons/platforms/embedded.svg +5 -0
- package/templates/default/dist/icons/platforms/ios.svg +1 -0
- package/templates/default/dist/icons/platforms/linux.svg +1 -0
- package/templates/default/dist/icons/platforms/macos.svg +1 -0
- package/templates/default/dist/icons/platforms/ubuntu.svg +1 -0
- package/templates/default/dist/icons/platforms/web.svg +5 -0
- package/templates/default/dist/icons/platforms/windows.svg +6 -0
- package/templates/default/dist/icons/stacks/android.svg +1 -0
- package/templates/default/dist/icons/stacks/apple-dark.svg +1 -0
- package/templates/default/dist/icons/stacks/apple-light.svg +1 -0
- package/templates/default/dist/icons/stacks/capacitor.svg +14 -0
- package/templates/default/dist/icons/stacks/dart.svg +1 -0
- package/templates/default/dist/icons/stacks/firebase.svg +1 -0
- package/templates/default/dist/icons/stacks/flutter.svg +1 -0
- package/templates/default/dist/icons/stacks/graphql.svg +1 -0
- package/templates/default/dist/icons/stacks/ionic.svg +4 -0
- package/templates/default/dist/icons/stacks/java.svg +1 -0
- package/templates/default/dist/icons/stacks/javascript.svg +1 -0
- package/templates/default/dist/icons/stacks/kotlin.svg +1 -0
- package/templates/default/dist/icons/stacks/mongodb.svg +1 -0
- package/templates/default/dist/icons/stacks/nodejs.svg +1 -0
- package/templates/default/dist/icons/stacks/python.svg +1 -0
- package/templates/default/dist/icons/stacks/react-native.svg +1 -0
- package/templates/default/dist/icons/stacks/react.svg +1 -0
- package/templates/default/dist/icons/stacks/rust.svg +1 -0
- package/templates/default/dist/icons/stacks/solidity.svg +1 -0
- package/templates/default/dist/icons/stacks/swift.svg +1 -0
- package/templates/default/dist/icons/stacks/tensorflow.svg +1 -0
- package/templates/default/dist/icons/stacks/typescript.svg +1 -0
- package/templates/default/dist/index.html +55 -0
- package/templates/default/dist/llms-full.txt +14 -0
- package/templates/default/dist/llms.txt +11 -0
- package/templates/default/dist/og-image.svg +48 -0
- package/templates/default/dist/projects/index.html +61 -0
- package/templates/default/dist/robots.txt +53 -0
- package/templates/default/dist/sitemap.xml +21 -0
- package/templates/default/dist/submit/index.html +69 -0
- package/templates/default/grove.config.ts +36 -0
- package/templates/default/node_modules/.bin/astro +21 -0
- package/templates/default/node_modules/.bin/astro-check +21 -0
- package/templates/default/node_modules/.bin/grove +21 -0
- package/templates/default/node_modules/.bin/jiti +21 -0
- package/templates/default/node_modules/.bin/prettier +21 -0
- package/templates/default/node_modules/.bin/rollup +21 -0
- package/templates/default/node_modules/.bin/tsc +21 -0
- package/templates/default/node_modules/.bin/tsserver +21 -0
- package/templates/default/node_modules/.bin/tsx +21 -0
- package/templates/default/node_modules/.bin/vite +21 -0
- package/templates/default/node_modules/.bin/yaml +21 -0
- package/templates/default/node_modules/.vite/deps/_metadata.json +79 -0
- package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js +433 -0
- package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/astro___aria-query.js +5 -0
- package/templates/default/node_modules/.vite/deps/astro___aria-query.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/astro___axobject-query.js +5 -0
- package/templates/default/node_modules/.vite/deps/astro___axobject-query.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/astro___html-escaper.js +9 -0
- package/templates/default/node_modules/.vite/deps/astro___html-escaper.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +241 -0
- package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js +1592 -0
- package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js +30 -0
- package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js +186 -0
- package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js +48 -0
- package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js +6779 -0
- package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js +33 -0
- package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js +88 -0
- package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js +72 -0
- package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js +3757 -0
- package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js +54 -0
- package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/package.json +3 -0
- package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js +213 -0
- package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js +525 -0
- package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js +1020 -0
- package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js.map +7 -0
- package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js +147 -0
- package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js.map +7 -0
- package/templates/default/package.json +6 -5
- package/templates/default/public/llms-full.txt +14 -0
- package/templates/default/public/llms.txt +11 -0
- package/templates/default/src/components/layout/BaseLayout.astro +10 -57
- package/templates/default/src/components/layout/Footer.astro +16 -134
- package/templates/default/src/components/layout/Header.astro +25 -126
- package/templates/default/src/data/records.ts +161 -0
- package/templates/default/src/lib/markdown.ts +171 -0
- package/templates/default/src/pages/about.astro +95 -429
- package/templates/default/src/pages/index.astro +258 -61
- package/templates/default/src/pages/projects/[slug].astro +480 -0
- package/templates/default/src/pages/projects/index.astro +193 -0
- package/templates/default/src/pages/sitemap.xml.ts +73 -61
- package/templates/default/src/pages/submit.astro +122 -411
- package/templates/default/src/styles/global.css +42 -68
- package/src/components/ActiveFilterChips.astro +0 -20
- package/src/components/DirectoryFilters.astro +0 -100
- package/src/components/DirectoryHero.astro +0 -32
- package/src/components/HealthBadge.astro +0 -12
- package/src/components/LensTabs.astro +0 -26
- package/src/components/MethodologyPanel.astro +0 -20
- package/src/components/Pagination.astro +0 -24
- package/src/components/ProjectDetail.astro +0 -166
- package/src/components/SubmitDraft.astro +0 -137
- package/templates/default/src/components/AppCard.astro +0 -248
- package/templates/default/src/components/AppSection.astro +0 -58
- package/templates/default/src/components/AppsIndexRow.astro +0 -230
- package/templates/default/src/components/AppsPagination.astro +0 -147
- package/templates/default/src/components/CategoryGrid.astro +0 -80
- package/templates/default/src/components/ContributorsGrid.astro +0 -105
- package/templates/default/src/components/DecisionRow.astro +0 -219
- package/templates/default/src/components/ExploreByCategory.astro +0 -74
- package/templates/default/src/components/ExploreByStack.astro +0 -102
- package/templates/default/src/components/Hero.astro +0 -176
- package/templates/default/src/components/MinimalAbout.astro +0 -57
- package/templates/default/src/components/OriginalCollection.astro +0 -144
- package/templates/default/src/components/RefinePanel.astro +0 -272
- package/templates/default/src/components/ScoreBars.astro +0 -72
- package/templates/default/src/components/SmartLensTabs.astro +0 -65
- package/templates/default/src/components/StackGrid.astro +0 -95
- package/templates/default/src/components/WhyThisExists.astro +0 -81
- package/templates/default/src/components/icons/Icon.astro +0 -148
- package/templates/default/src/components/layout/SectionHeader.astro +0 -42
- package/templates/default/src/components/layout/ThemeToggle.astro +0 -97
- package/templates/default/src/data/apps.ts +0 -149
- package/templates/default/src/data/categories.ts +0 -27
- package/templates/default/src/data/config.ts +0 -28
- package/templates/default/src/data/contributors.ts +0 -96
- package/templates/default/src/data/stacks.ts +0 -47
- package/templates/default/src/data/stats.ts +0 -65
- package/templates/default/src/data/types.ts +0 -248
- package/templates/default/src/lib/apps-search.ts +0 -228
- package/templates/default/src/lib/format.ts +0 -36
- package/templates/default/src/lib/lenses.ts +0 -98
- package/templates/default/src/lib/repo.ts +0 -12
- package/templates/default/src/lib/scores.ts +0 -51
- package/templates/default/src/lib/taxonomy-counts.ts +0 -31
- package/templates/default/src/pages/apps/[slug].astro +0 -709
- package/templates/default/src/pages/apps/index.astro +0 -669
- package/templates/default/src/pages/contributors.astro +0 -142
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated lenses for the list / discovery page.
|
|
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:
|
|
7
|
+
*
|
|
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.)
|
|
14
|
+
*
|
|
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.
|
|
19
|
+
*/
|
|
20
|
+
const DAY_MS = 86_400_000;
|
|
21
|
+
function daysSince(iso) {
|
|
22
|
+
if (!iso)
|
|
23
|
+
return Infinity;
|
|
24
|
+
const d = new Date(iso);
|
|
25
|
+
if (Number.isNaN(d.valueOf()))
|
|
26
|
+
return Infinity;
|
|
27
|
+
return (Date.now() - d.valueOf()) / DAY_MS;
|
|
28
|
+
}
|
|
29
|
+
function hasLabel(record, label) {
|
|
30
|
+
return record.curation?.labels?.includes(label) ?? false;
|
|
31
|
+
}
|
|
32
|
+
const CLEANUP_STATUSES = [
|
|
33
|
+
"stale",
|
|
34
|
+
"inactive",
|
|
35
|
+
"archived",
|
|
36
|
+
"unavailable",
|
|
37
|
+
"needs_review",
|
|
38
|
+
];
|
|
39
|
+
function isCleanupCandidate(record) {
|
|
40
|
+
if (record.health?.cleanupCandidate)
|
|
41
|
+
return true;
|
|
42
|
+
const status = record.health?.status;
|
|
43
|
+
if (!status)
|
|
44
|
+
return false;
|
|
45
|
+
return CLEANUP_STATUSES.includes(status);
|
|
46
|
+
}
|
|
47
|
+
export const LENSES = [
|
|
48
|
+
{
|
|
49
|
+
id: "all",
|
|
50
|
+
label: "All",
|
|
51
|
+
description: "Every record in the directory.",
|
|
52
|
+
match: () => true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "hot",
|
|
56
|
+
label: "Trending",
|
|
57
|
+
description: "Records with recent attention — stars ≥ 1000 or pushed within 30 days.",
|
|
58
|
+
match: (r) => {
|
|
59
|
+
const stars = r.github?.stars ?? 0;
|
|
60
|
+
const pushed = daysSince(r.github?.pushedAt);
|
|
61
|
+
return stars >= 1000 || pushed <= 30;
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "new",
|
|
66
|
+
label: "Recently added",
|
|
67
|
+
description: "Fresh entries — pushed within 7 days or curated with the “new” label.",
|
|
68
|
+
match: (r) => {
|
|
69
|
+
const pushed = daysSince(r.github?.pushedAt);
|
|
70
|
+
return pushed <= 7 || hasLabel(r, "new");
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "mature",
|
|
75
|
+
label: "Established",
|
|
76
|
+
description: "Long-running projects — curated tier and last push > 365 days ago.",
|
|
77
|
+
match: (r) => {
|
|
78
|
+
const tier = r.health?.tier;
|
|
79
|
+
const pushed = daysSince(r.github?.pushedAt);
|
|
80
|
+
return tier === "curated" && pushed > 365;
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "featured",
|
|
85
|
+
label: "Featured",
|
|
86
|
+
description: "Records curated with the “featured” label.",
|
|
87
|
+
match: (r) => hasLabel(r, "featured"),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "needs-review",
|
|
91
|
+
label: "Needs review",
|
|
92
|
+
description: "Cleanup candidates — stale, archived, or unmaintained.",
|
|
93
|
+
match: (r) => isCleanupCandidate(r),
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
/**
|
|
97
|
+
* The lenses rendered as top-row tabs on the list page. Order
|
|
98
|
+
* matters — left to right. Keep this in sync with what the design
|
|
99
|
+
* calls for; URL deep-linking accepts any `LensId` even if it's not
|
|
100
|
+
* in the primary row.
|
|
101
|
+
*/
|
|
102
|
+
export const PRIMARY_LENSES = [
|
|
103
|
+
"all",
|
|
104
|
+
"hot",
|
|
105
|
+
"new",
|
|
106
|
+
"mature",
|
|
107
|
+
"featured",
|
|
108
|
+
"needs-review",
|
|
109
|
+
];
|
|
110
|
+
export function lensById(id) {
|
|
111
|
+
if (!id)
|
|
112
|
+
return undefined;
|
|
113
|
+
return LENSES.find((l) => l.id === id);
|
|
114
|
+
}
|
|
115
|
+
export function isPrimaryLens(id) {
|
|
116
|
+
if (!id)
|
|
117
|
+
return false;
|
|
118
|
+
return PRIMARY_LENSES.includes(id);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the active lens from URL search params. Returns the empty
|
|
122
|
+
* string when no `lens=...` is set, so callers can treat both
|
|
123
|
+
* "absent" and "unknown" identically.
|
|
124
|
+
*/
|
|
125
|
+
export function lensFromSearchParams(params) {
|
|
126
|
+
const v = params.get("lens");
|
|
127
|
+
if (!v)
|
|
128
|
+
return "";
|
|
129
|
+
if (LENSES.some((l) => l.id === v))
|
|
130
|
+
return v;
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `true` when the URL search params currently reflect this lens's
|
|
135
|
+
* filter shape. The "all" lens is the implicit "no filter" state —
|
|
136
|
+
* it's active only when no other lens-shaped params are present.
|
|
137
|
+
*/
|
|
138
|
+
export function isLensActive(lensId, params) {
|
|
139
|
+
if (lensId === "all") {
|
|
140
|
+
return lensFromSearchParams(params) === "" && !params.get("label");
|
|
141
|
+
}
|
|
142
|
+
return lensFromSearchParams(params) === lensId;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Build a `href` for a lens tab. Selecting a lens resets the other
|
|
146
|
+
* "view" params (label, page) and applies only the chosen lens's
|
|
147
|
+
* filter. Other filters (q, stack, platform, category, sort) are
|
|
148
|
+
* preserved.
|
|
149
|
+
*
|
|
150
|
+
* `pathPrefix` is the URL root for the list page — typically
|
|
151
|
+
* `/projects` for the project-directory blueprint, `/resources` for
|
|
152
|
+
* resource-hub, `/entities` for ecosystem-map. Defaults to `/`.
|
|
153
|
+
*/
|
|
154
|
+
export function hrefForLens(lensId, params, pathPrefix = "/") {
|
|
155
|
+
const sp = new URLSearchParams(params);
|
|
156
|
+
sp.delete("lens");
|
|
157
|
+
sp.delete("label");
|
|
158
|
+
sp.delete("page");
|
|
159
|
+
if (lensId && lensId !== "all") {
|
|
160
|
+
sp.set("lens", lensId);
|
|
161
|
+
}
|
|
162
|
+
const qs = sp.toString();
|
|
163
|
+
const base = pathPrefix.replace(/\/$/, "");
|
|
164
|
+
return qs ? `${base}?${qs}` : base || "/";
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Apply the lens filter to a record array. When `lensId` is
|
|
168
|
+
* `""` (no lens) or `"all"`, returns the input unchanged.
|
|
169
|
+
*
|
|
170
|
+
* Type-parameterized so the same helper works on `ProjectRecord[]`
|
|
171
|
+
* and on the lighter index payload that list pages ship.
|
|
172
|
+
*/
|
|
173
|
+
export function applyLens(records, lensId) {
|
|
174
|
+
if (!lensId || lensId === "all")
|
|
175
|
+
return records;
|
|
176
|
+
const def = lensById(lensId);
|
|
177
|
+
if (!def)
|
|
178
|
+
return records;
|
|
179
|
+
return records.filter((r) => def.match(r));
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=lenses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenses.js","sourceRoot":"","sources":["../../src/lib/lenses.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a GitHub repo URL into its owner and repo components.
|
|
3
|
+
*
|
|
4
|
+
* Returns `null` (not `{ owner: null, repo: null }`) if the URL
|
|
5
|
+
* doesn't match the github.com path shape — this lets callers
|
|
6
|
+
* distinguish "no URL" from "URL without owner" with a single
|
|
7
|
+
* null check.
|
|
8
|
+
*
|
|
9
|
+
* Used in:
|
|
10
|
+
* - ItemCard / AppCard avatar fallback (owner avatar via GitHub
|
|
11
|
+
* avatars API).
|
|
12
|
+
* - Detail page (showing owner + repo name in breadcrumb / header).
|
|
13
|
+
* - Anywhere we need to display "@owner / repo" or link to the owner.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getOwnerAndRepoFromRepoUrl(repoUrl: string | null | undefined): {
|
|
16
|
+
owner: string;
|
|
17
|
+
repo: string;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* Build a GitHub avatar URL for a given owner. Defaults to size 80
|
|
21
|
+
* which is the smallest "high quality" size in GitHub's avatar CDN.
|
|
22
|
+
*
|
|
23
|
+
* Returns a string (not nullable) so callers can plug the result
|
|
24
|
+
* straight into an `src` attribute. The avatar endpoint always
|
|
25
|
+
* returns a placeholder image for unknown owners, so the only
|
|
26
|
+
* reason to check `null` upstream is when you specifically want
|
|
27
|
+
* to render a different fallback (e.g. initials).
|
|
28
|
+
*/
|
|
29
|
+
export declare function getOwnerAvatarUrl(owner: string, size?: number): string;
|
|
30
|
+
//# sourceMappingURL=repo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/lib/repo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAOlE"}
|
package/dist/lib/repo.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a GitHub repo URL into its owner and repo components.
|
|
3
|
+
*
|
|
4
|
+
* Returns `null` (not `{ owner: null, repo: null }`) if the URL
|
|
5
|
+
* doesn't match the github.com path shape — this lets callers
|
|
6
|
+
* distinguish "no URL" from "URL without owner" with a single
|
|
7
|
+
* null check.
|
|
8
|
+
*
|
|
9
|
+
* Used in:
|
|
10
|
+
* - ItemCard / AppCard avatar fallback (owner avatar via GitHub
|
|
11
|
+
* avatars API).
|
|
12
|
+
* - Detail page (showing owner + repo name in breadcrumb / header).
|
|
13
|
+
* - Anywhere we need to display "@owner / repo" or link to the owner.
|
|
14
|
+
*/
|
|
15
|
+
export function getOwnerAndRepoFromRepoUrl(repoUrl) {
|
|
16
|
+
if (!repoUrl || !repoUrl.includes("github.com/"))
|
|
17
|
+
return null;
|
|
18
|
+
const m = repoUrl.match(/github\.com\/([^/]+)\/([^/?#]+)/);
|
|
19
|
+
if (!m)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
owner: m[1],
|
|
23
|
+
repo: m[2].replace(/\.git$/, ""),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a GitHub avatar URL for a given owner. Defaults to size 80
|
|
28
|
+
* which is the smallest "high quality" size in GitHub's avatar CDN.
|
|
29
|
+
*
|
|
30
|
+
* Returns a string (not nullable) so callers can plug the result
|
|
31
|
+
* straight into an `src` attribute. The avatar endpoint always
|
|
32
|
+
* returns a placeholder image for unknown owners, so the only
|
|
33
|
+
* reason to check `null` upstream is when you specifically want
|
|
34
|
+
* to render a different fallback (e.g. initials).
|
|
35
|
+
*/
|
|
36
|
+
export function getOwnerAvatarUrl(owner, size = 80) {
|
|
37
|
+
if (!owner) {
|
|
38
|
+
// Defensive: callers may pass a possibly-empty string. Return the
|
|
39
|
+
// generic GitHub identicon so an `<img>` doesn't 404.
|
|
40
|
+
return `https://avatars.githubusercontent.com/identicon?v=4&s=${size}`;
|
|
41
|
+
}
|
|
42
|
+
return `https://avatars.githubusercontent.com/${owner}?v=4&s=${size}`;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/lib/repo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAkC;IAElC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAC3D,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACX,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;KACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,IAAI,GAAG,EAAE;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,kEAAkE;QAClE,sDAAsD;QACtD,OAAO,yDAAyD,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,yCAAyC,KAAK,UAAU,IAAI,EAAE,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score-dimension utilities.
|
|
3
|
+
*
|
|
4
|
+
* Operates on the `scores` field of a `ProjectRecord` (or any
|
|
5
|
+
* object that has the same shape — defined as `Score` in
|
|
6
|
+
* `@grove-dev/core`). All helpers are dependency-free.
|
|
7
|
+
*/
|
|
8
|
+
import type { Score } from "@grove-dev/core";
|
|
9
|
+
/**
|
|
10
|
+
* Format a single score (0-100) into a 0-5 "tier" used by the UI:
|
|
11
|
+
*
|
|
12
|
+
* 0-19 → 0 (very low)
|
|
13
|
+
* 20-39 → 1 (low)
|
|
14
|
+
* 40-59 → 2 (medium)
|
|
15
|
+
* 60-79 → 3 (high)
|
|
16
|
+
* 80-100 → 4 (very high)
|
|
17
|
+
*
|
|
18
|
+
* The list page renders each score as a 4-cell bar; this gives
|
|
19
|
+
* the visual a stable shape regardless of the underlying number.
|
|
20
|
+
*/
|
|
21
|
+
export declare function scoreTier(n: number): 0 | 1 | 2 | 3 | 4;
|
|
22
|
+
/** Compact textual label for a score tier (used in tooltips). */
|
|
23
|
+
export declare function scoreTierLabel(n: number): string;
|
|
24
|
+
/** Short numeric label used inline next to a score bar, e.g. "82". */
|
|
25
|
+
export declare function scoreLabel(n: number | undefined): string;
|
|
26
|
+
/** Order of score dimensions, used by both list and detail. */
|
|
27
|
+
export declare const SCORE_DIMENSIONS: (keyof Score)[];
|
|
28
|
+
/** Human-readable label for a score dimension. */
|
|
29
|
+
export declare const SCORE_LABELS: Record<keyof Score, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Reasoning copy per dimension, surfaced in the detail page so the
|
|
32
|
+
* numbers don't feel magical. These are intentionally short — the
|
|
33
|
+
* curation notes on each record carry the longer story.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SCORE_REASONING: Record<keyof Score, string>;
|
|
36
|
+
/** Re-export the `Score` type for callers that only need the lib API. */
|
|
37
|
+
export type { Score };
|
|
38
|
+
//# sourceMappingURL=scores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scores.d.ts","sourceRoot":"","sources":["../../src/lib/scores.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAMtD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAahD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGxD;AAED,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,EAAE,CAAC,MAAM,KAAK,CAAC,EAO3C,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,KAAK,EAAE,MAAM,CAOpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,KAAK,EAAE,MAAM,CAavD,CAAC;AAEF,yEAAyE;AACzE,YAAY,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score-dimension utilities.
|
|
3
|
+
*
|
|
4
|
+
* Operates on the `scores` field of a `ProjectRecord` (or any
|
|
5
|
+
* object that has the same shape — defined as `Score` in
|
|
6
|
+
* `@grove-dev/core`). All helpers are dependency-free.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Format a single score (0-100) into a 0-5 "tier" used by the UI:
|
|
10
|
+
*
|
|
11
|
+
* 0-19 → 0 (very low)
|
|
12
|
+
* 20-39 → 1 (low)
|
|
13
|
+
* 40-59 → 2 (medium)
|
|
14
|
+
* 60-79 → 3 (high)
|
|
15
|
+
* 80-100 → 4 (very high)
|
|
16
|
+
*
|
|
17
|
+
* The list page renders each score as a 4-cell bar; this gives
|
|
18
|
+
* the visual a stable shape regardless of the underlying number.
|
|
19
|
+
*/
|
|
20
|
+
export function scoreTier(n) {
|
|
21
|
+
if (n < 20)
|
|
22
|
+
return 0;
|
|
23
|
+
if (n < 40)
|
|
24
|
+
return 1;
|
|
25
|
+
if (n < 60)
|
|
26
|
+
return 2;
|
|
27
|
+
if (n < 80)
|
|
28
|
+
return 3;
|
|
29
|
+
return 4;
|
|
30
|
+
}
|
|
31
|
+
/** Compact textual label for a score tier (used in tooltips). */
|
|
32
|
+
export function scoreTierLabel(n) {
|
|
33
|
+
switch (scoreTier(n)) {
|
|
34
|
+
case 0:
|
|
35
|
+
return "Very low";
|
|
36
|
+
case 1:
|
|
37
|
+
return "Low";
|
|
38
|
+
case 2:
|
|
39
|
+
return "Medium";
|
|
40
|
+
case 3:
|
|
41
|
+
return "High";
|
|
42
|
+
case 4:
|
|
43
|
+
return "Very high";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Short numeric label used inline next to a score bar, e.g. "82". */
|
|
47
|
+
export function scoreLabel(n) {
|
|
48
|
+
if (typeof n !== "number")
|
|
49
|
+
return "—";
|
|
50
|
+
return Math.round(n).toString();
|
|
51
|
+
}
|
|
52
|
+
/** Order of score dimensions, used by both list and detail. */
|
|
53
|
+
export const SCORE_DIMENSIONS = [
|
|
54
|
+
"activity",
|
|
55
|
+
"maturity",
|
|
56
|
+
"learning",
|
|
57
|
+
"contribution",
|
|
58
|
+
"docs",
|
|
59
|
+
"overall",
|
|
60
|
+
];
|
|
61
|
+
/** Human-readable label for a score dimension. */
|
|
62
|
+
export const SCORE_LABELS = {
|
|
63
|
+
activity: "Activity",
|
|
64
|
+
maturity: "Maturity",
|
|
65
|
+
learning: "Learning",
|
|
66
|
+
contribution: "Contribution",
|
|
67
|
+
docs: "Docs",
|
|
68
|
+
overall: "Overall",
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Reasoning copy per dimension, surfaced in the detail page so the
|
|
72
|
+
* numbers don't feel magical. These are intentionally short — the
|
|
73
|
+
* curation notes on each record carry the longer story.
|
|
74
|
+
*/
|
|
75
|
+
export const SCORE_REASONING = {
|
|
76
|
+
activity: "Recent commits, open and merged PRs, issue response time, release cadence.",
|
|
77
|
+
maturity: "Repo age, stable structure, real-world production usage, contributor stability.",
|
|
78
|
+
learning: "Codebase readability, useful architecture patterns, comment quality, examples.",
|
|
79
|
+
contribution: "Open issues, recent merged PRs from new contributors, maintainer responsiveness, contribution guide.",
|
|
80
|
+
docs: "README quality, architecture docs, contributing guide, examples, code comments.",
|
|
81
|
+
overall: "Composite judgment — how likely this record is to satisfy a curious developer.",
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=scores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scores.js","sourceRoot":"","sources":["../../src/lib/scores.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,CAAC,CAAC;IACrB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,QAAQ,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC;YACJ,OAAO,UAAU,CAAC;QACpB,KAAK,CAAC;YACJ,OAAO,KAAK,CAAC;QACf,KAAK,CAAC;YACJ,OAAO,QAAQ,CAAC;QAClB,KAAK,CAAC;YACJ,OAAO,MAAM,CAAC;QAChB,KAAK,CAAC;YACJ,OAAO,WAAW,CAAC;IACvB,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,CAAqB;IAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAClC,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,UAAU;IACV,UAAU;IACV,UAAU;IACV,cAAc;IACd,MAAM;IACN,SAAS;CACV,CAAC;AAEF,kDAAkD;AAClD,MAAM,CAAC,MAAM,YAAY,GAAgC;IACvD,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,YAAY,EAAE,cAAc;IAC5B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAgC;IAC1D,QAAQ,EACN,4EAA4E;IAC9E,QAAQ,EACN,iFAAiF;IACnF,QAAQ,EACN,gFAAgF;IAClF,YAAY,EACV,sGAAsG;IACxG,IAAI,EACF,iFAAiF;IACnF,OAAO,EACL,gFAAgF;CACnF,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
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 type { HealthStatus, ProjectRecord } from "@grove-dev/core";
|
|
16
|
+
import { type LensId } from "./lenses.js";
|
|
17
|
+
/**
|
|
18
|
+
* Subset of `ProjectRecord` the search lib actually reads. Defined
|
|
19
|
+
* inline so the lib works on both full records and the lighter
|
|
20
|
+
* index payload. Use `ProjectRecord` from core when you have it;
|
|
21
|
+
* pass any object that satisfies `Searchable` otherwise.
|
|
22
|
+
*
|
|
23
|
+
* `Searchable` is a superset of `AppLike` (the lens-matching shape
|
|
24
|
+
* in `lib/lenses.ts`) — both interfaces share the same optional
|
|
25
|
+
* field shape, so `Searchable` records can be passed to
|
|
26
|
+
* `applyLens()` and `filterApps()` interchangeably.
|
|
27
|
+
*/
|
|
28
|
+
export interface Searchable {
|
|
29
|
+
slug?: string;
|
|
30
|
+
name: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
category?: string;
|
|
33
|
+
tags?: string[];
|
|
34
|
+
stack?: string;
|
|
35
|
+
stacks?: string[];
|
|
36
|
+
platforms?: string[];
|
|
37
|
+
/** Canonical repo URL — used for owner extraction in text search. */
|
|
38
|
+
repoUrl?: string;
|
|
39
|
+
logoUrl?: string;
|
|
40
|
+
/** Curated labels (new, hot, mature, featured). */
|
|
41
|
+
curation?: {
|
|
42
|
+
labels?: string[];
|
|
43
|
+
};
|
|
44
|
+
/** Health block — drives status filter + tier fallback. */
|
|
45
|
+
health?: {
|
|
46
|
+
status?: HealthStatus;
|
|
47
|
+
tier?: "curated" | "listed" | "experimental" | "hidden";
|
|
48
|
+
visibility?: "highlight" | "keep" | "needs_review" | "hide" | "remove" | "historical";
|
|
49
|
+
cleanupCandidate?: boolean;
|
|
50
|
+
};
|
|
51
|
+
/** GitHub metadata used for sort + text search. */
|
|
52
|
+
github?: {
|
|
53
|
+
stars?: number;
|
|
54
|
+
forks?: number;
|
|
55
|
+
pushedAt?: string | null;
|
|
56
|
+
license?: string | null;
|
|
57
|
+
};
|
|
58
|
+
/** Score block — used for "best-overall" sort. */
|
|
59
|
+
scores?: {
|
|
60
|
+
activity?: number;
|
|
61
|
+
maturity?: number;
|
|
62
|
+
learning?: number;
|
|
63
|
+
contribution?: number;
|
|
64
|
+
docs?: number;
|
|
65
|
+
overall?: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export type AppsFilters = {
|
|
69
|
+
q?: string;
|
|
70
|
+
/** Stack slugs, repeated URL key `stack=`. Matches against `stack` AND `stacks[]`. */
|
|
71
|
+
stacks?: string[];
|
|
72
|
+
/** Platform slugs, repeated URL key `platform=`. Matches `platforms[]`. */
|
|
73
|
+
platforms?: string[];
|
|
74
|
+
/** Category names, repeated URL key `category=`. */
|
|
75
|
+
categories?: string[];
|
|
76
|
+
/** Curated label ids (new/hot/mature/featured), repeated URL key `label=`. */
|
|
77
|
+
labels?: string[];
|
|
78
|
+
/** SPDX license ids, repeated URL key `license=`. */
|
|
79
|
+
licenses?: string[];
|
|
80
|
+
/** Health status values, comma-separated repeated URL key `status=`. */
|
|
81
|
+
statuses?: string[];
|
|
82
|
+
/** A curated lens (single-select view). */
|
|
83
|
+
lens?: LensId;
|
|
84
|
+
/** Sort order. Single value, defaults to "recently-updated". */
|
|
85
|
+
sort?: AppsSort;
|
|
86
|
+
/** 1-based page number. */
|
|
87
|
+
page?: number;
|
|
88
|
+
};
|
|
89
|
+
/** Available sort orders, in UI order. */
|
|
90
|
+
export declare const SORT_OPTIONS: readonly [{
|
|
91
|
+
readonly value: "recently-updated";
|
|
92
|
+
readonly label: "Recently updated";
|
|
93
|
+
}, {
|
|
94
|
+
readonly value: "most-starred";
|
|
95
|
+
readonly label: "Most starred";
|
|
96
|
+
}, {
|
|
97
|
+
readonly value: "recently-added";
|
|
98
|
+
readonly label: "Recently added";
|
|
99
|
+
}, {
|
|
100
|
+
readonly value: "best-overall";
|
|
101
|
+
readonly label: "Best overall";
|
|
102
|
+
}, {
|
|
103
|
+
readonly value: "alphabetical";
|
|
104
|
+
readonly label: "Alphabetical";
|
|
105
|
+
}];
|
|
106
|
+
export type AppsSort = (typeof SORT_OPTIONS)[number]["value"];
|
|
107
|
+
/**
|
|
108
|
+
* How many records per page. Constant for now; future versions may
|
|
109
|
+
* vary per blueprint.
|
|
110
|
+
*/
|
|
111
|
+
export declare const PAGE_SIZE = 24;
|
|
112
|
+
/**
|
|
113
|
+
* Read filters from a URLSearchParams (or anything URLSearchParams-
|
|
114
|
+
* shaped, e.g. the result of `new URL(req.url).searchParams`).
|
|
115
|
+
*/
|
|
116
|
+
export declare function filtersFromSearchParams(sp: URLSearchParams): AppsFilters;
|
|
117
|
+
/**
|
|
118
|
+
* Serialize a filters object back to URLSearchParams. Undefined /
|
|
119
|
+
* empty values are dropped. Defaults (sort, page 1) are also dropped
|
|
120
|
+
* to keep URLs short and canonical.
|
|
121
|
+
*/
|
|
122
|
+
export declare function searchParamsFromFilters(f: AppsFilters): URLSearchParams;
|
|
123
|
+
/**
|
|
124
|
+
* True if any filter is active (i.e. the result list isn't "all
|
|
125
|
+
* records, default sort").
|
|
126
|
+
*/
|
|
127
|
+
export declare function hasAnyFilter(f: AppsFilters): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Return the subset of records that match all active filters (AND
|
|
130
|
+
* across dimensions, OR within a dimension).
|
|
131
|
+
*
|
|
132
|
+
* `q` is a case-insensitive substring search across name, owner,
|
|
133
|
+
* description, category, stack/stacks/platforms, tags, and license.
|
|
134
|
+
* Other dimensions are exact match.
|
|
135
|
+
*/
|
|
136
|
+
export declare function filterApps<T extends Searchable>(records: T[], f: AppsFilters): T[];
|
|
137
|
+
/**
|
|
138
|
+
* Apply lens → filter → sort pipeline. Lens is applied first because
|
|
139
|
+
* it's a single-select "view" that may be broader or narrower than
|
|
140
|
+
* the regular filter dimensions.
|
|
141
|
+
*/
|
|
142
|
+
export declare function pipeline<T extends Searchable>(records: T[], filters: AppsFilters): T[];
|
|
143
|
+
/**
|
|
144
|
+
* Sort records in-place-free: returns a new array. Stable for ties.
|
|
145
|
+
* Records missing the sort key (e.g. no stars) sink to the bottom
|
|
146
|
+
* rather than vanishing.
|
|
147
|
+
*/
|
|
148
|
+
export declare function applySort<T extends Searchable>(records: T[], sort: AppsSort): T[];
|
|
149
|
+
/** Slice an array for a given page (1-based). */
|
|
150
|
+
export declare function paginate<T>(items: T[], page: number, pageSize?: number): T[];
|
|
151
|
+
/** Total number of pages given an item count. Always >= 1. */
|
|
152
|
+
export declare function totalPages(itemCount: number, pageSize?: number): number;
|
|
153
|
+
/** Resolve the effective sort from a filters object, applying default. */
|
|
154
|
+
export declare function effectiveSort(f: AppsFilters): AppsSort;
|
|
155
|
+
/** Resolve the effective page from a filters object, applying default. */
|
|
156
|
+
export declare function effectivePage(f: AppsFilters): number;
|
|
157
|
+
/**
|
|
158
|
+
* Return a flat list of active-filter chips for display + removal.
|
|
159
|
+
* Used by the "x" button on each chip in the UI.
|
|
160
|
+
*/
|
|
161
|
+
export declare function activeFilterChips(f: AppsFilters): {
|
|
162
|
+
key: keyof AppsFilters;
|
|
163
|
+
value: string;
|
|
164
|
+
label: string;
|
|
165
|
+
}[];
|
|
166
|
+
/**
|
|
167
|
+
* Build the full list of facet values from the record array,
|
|
168
|
+
* preserving a sensible order (frequency desc, then alphabetical
|
|
169
|
+
* for ties).
|
|
170
|
+
*/
|
|
171
|
+
export declare function buildFacets<T extends Searchable>(records: T[]): {
|
|
172
|
+
stacks: {
|
|
173
|
+
value: string;
|
|
174
|
+
count: number;
|
|
175
|
+
}[];
|
|
176
|
+
platforms: {
|
|
177
|
+
value: string;
|
|
178
|
+
count: number;
|
|
179
|
+
}[];
|
|
180
|
+
categories: {
|
|
181
|
+
value: string;
|
|
182
|
+
count: number;
|
|
183
|
+
}[];
|
|
184
|
+
labels: {
|
|
185
|
+
value: string;
|
|
186
|
+
count: number;
|
|
187
|
+
}[];
|
|
188
|
+
licenses: {
|
|
189
|
+
value: string;
|
|
190
|
+
count: number;
|
|
191
|
+
}[];
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Re-export the `ProjectRecord` type so consumers of the search lib
|
|
195
|
+
* can write `import type { ProjectRecord } from "@grove-dev/astro/lib"`
|
|
196
|
+
* without a second import.
|
|
197
|
+
*/
|
|
198
|
+
export type { ProjectRecord };
|
|
199
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"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"}
|