@grove-dev/astro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/components/ActiveFilterChips.astro +20 -0
- package/src/components/CategoryGrid.astro +27 -0
- package/src/components/DirectoryFilters.astro +100 -0
- package/src/components/DirectoryHero.astro +32 -0
- package/src/components/HealthBadge.astro +12 -0
- package/src/components/ItemCard.astro +41 -0
- package/src/components/LensTabs.astro +26 -0
- package/src/components/MethodologyPanel.astro +20 -0
- package/src/components/Pagination.astro +24 -0
- package/src/components/ProjectDetail.astro +166 -0
- package/src/components/ScoreBars.astro +32 -0
- package/src/components/SubmitDraft.astro +137 -0
- package/src/index.ts +17 -0
- package/src/layouts/BaseLayout.astro +32 -0
- package/src/styles.css +675 -0
- package/templates/default/astro.config.mjs +23 -0
- package/templates/default/data/generated/apps.json +1 -0
- package/templates/default/data/generated/repo-stats.json +1 -0
- package/templates/default/package.json +35 -0
- package/templates/default/public/icons/platforms/android.svg +1 -0
- package/templates/default/public/icons/platforms/apple-dark.svg +1 -0
- package/templates/default/public/icons/platforms/apple-light.svg +1 -0
- package/templates/default/public/icons/platforms/chrome.svg +1 -0
- package/templates/default/public/icons/platforms/chromeos.svg +5 -0
- package/templates/default/public/icons/platforms/desktop.svg +4 -0
- package/templates/default/public/icons/platforms/embedded.svg +5 -0
- package/templates/default/public/icons/platforms/ios.svg +1 -0
- package/templates/default/public/icons/platforms/linux.svg +1 -0
- package/templates/default/public/icons/platforms/macos.svg +1 -0
- package/templates/default/public/icons/platforms/ubuntu.svg +1 -0
- package/templates/default/public/icons/platforms/web.svg +5 -0
- package/templates/default/public/icons/platforms/windows.svg +6 -0
- package/templates/default/public/icons/stacks/android.svg +1 -0
- package/templates/default/public/icons/stacks/apple-dark.svg +1 -0
- package/templates/default/public/icons/stacks/apple-light.svg +1 -0
- package/templates/default/public/icons/stacks/capacitor.svg +14 -0
- package/templates/default/public/icons/stacks/dart.svg +1 -0
- package/templates/default/public/icons/stacks/firebase.svg +1 -0
- package/templates/default/public/icons/stacks/flutter.svg +1 -0
- package/templates/default/public/icons/stacks/graphql.svg +1 -0
- package/templates/default/public/icons/stacks/ionic.svg +4 -0
- package/templates/default/public/icons/stacks/java.svg +1 -0
- package/templates/default/public/icons/stacks/javascript.svg +1 -0
- package/templates/default/public/icons/stacks/kotlin.svg +1 -0
- package/templates/default/public/icons/stacks/mongodb.svg +1 -0
- package/templates/default/public/icons/stacks/nodejs.svg +1 -0
- package/templates/default/public/icons/stacks/python.svg +1 -0
- package/templates/default/public/icons/stacks/react-native.svg +1 -0
- package/templates/default/public/icons/stacks/react.svg +1 -0
- package/templates/default/public/icons/stacks/rust.svg +1 -0
- package/templates/default/public/icons/stacks/solidity.svg +1 -0
- package/templates/default/public/icons/stacks/swift.svg +1 -0
- package/templates/default/public/icons/stacks/tensorflow.svg +1 -0
- package/templates/default/public/icons/stacks/typescript.svg +1 -0
- package/templates/default/public/og-image.svg +48 -0
- package/templates/default/public/robots.txt +53 -0
- package/templates/default/src/components/AppCard.astro +248 -0
- package/templates/default/src/components/AppSection.astro +58 -0
- package/templates/default/src/components/AppsIndexRow.astro +230 -0
- package/templates/default/src/components/AppsPagination.astro +147 -0
- package/templates/default/src/components/CategoryGrid.astro +80 -0
- package/templates/default/src/components/ContributorsGrid.astro +105 -0
- package/templates/default/src/components/DecisionRow.astro +219 -0
- package/templates/default/src/components/ExploreByCategory.astro +74 -0
- package/templates/default/src/components/ExploreByStack.astro +102 -0
- package/templates/default/src/components/Hero.astro +176 -0
- package/templates/default/src/components/MinimalAbout.astro +57 -0
- package/templates/default/src/components/OriginalCollection.astro +144 -0
- package/templates/default/src/components/RefinePanel.astro +272 -0
- package/templates/default/src/components/ScoreBars.astro +72 -0
- package/templates/default/src/components/SmartLensTabs.astro +65 -0
- package/templates/default/src/components/StackGrid.astro +95 -0
- package/templates/default/src/components/WhyThisExists.astro +81 -0
- package/templates/default/src/components/icons/Icon.astro +148 -0
- package/templates/default/src/components/layout/BaseLayout.astro +94 -0
- package/templates/default/src/components/layout/Container.astro +18 -0
- package/templates/default/src/components/layout/Footer.astro +145 -0
- package/templates/default/src/components/layout/Header.astro +144 -0
- package/templates/default/src/components/layout/SectionHeader.astro +42 -0
- package/templates/default/src/components/layout/Seo.astro +120 -0
- package/templates/default/src/components/layout/ThemeToggle.astro +97 -0
- package/templates/default/src/data/apps.ts +149 -0
- package/templates/default/src/data/categories.ts +27 -0
- package/templates/default/src/data/config.ts +28 -0
- package/templates/default/src/data/contributors.ts +96 -0
- package/templates/default/src/data/stacks.ts +47 -0
- package/templates/default/src/data/stats.ts +65 -0
- package/templates/default/src/data/types.ts +248 -0
- package/templates/default/src/lib/apps-search.ts +228 -0
- package/templates/default/src/lib/format.ts +36 -0
- package/templates/default/src/lib/lenses.ts +98 -0
- package/templates/default/src/lib/repo.ts +12 -0
- package/templates/default/src/lib/scores.ts +51 -0
- package/templates/default/src/lib/taxonomy-counts.ts +31 -0
- package/templates/default/src/pages/about.astro +433 -0
- package/templates/default/src/pages/apps/[slug].astro +709 -0
- package/templates/default/src/pages/apps/index.astro +669 -0
- package/templates/default/src/pages/contributors.astro +142 -0
- package/templates/default/src/pages/index.astro +66 -0
- package/templates/default/src/pages/sitemap.xml.ts +77 -0
- package/templates/default/src/pages/submit.astro +416 -0
- package/templates/default/src/styles/global.css +84 -0
- package/templates/default/tailwind.config.mjs +130 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `@grove-dev/astro`
|
|
2
|
+
|
|
3
|
+
> Astro framework adapter for Grove.
|
|
4
|
+
|
|
5
|
+
A thin layer of Astro components, layouts, and design tokens, on top of the framework-agnostic `@grove-dev/core` and `@grove-dev/ui`. Includes a default `templates/default/` directory that `grove new` copies into a new project.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @grove-dev/astro
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What it ships
|
|
12
|
+
|
|
13
|
+
```txt
|
|
14
|
+
src/
|
|
15
|
+
├── components/ # ItemCard, CategoryGrid, HealthBadge, ScoreBars,
|
|
16
|
+
│ # DirectoryFilters, DirectoryHero, LensTabs,
|
|
17
|
+
│ # ActiveFilterChips, MethodologyPanel, Pagination,
|
|
18
|
+
│ # ProjectDetail, SubmitDraft
|
|
19
|
+
├── layouts/ # BaseLayout
|
|
20
|
+
├── styles.css # design tokens + utility classes
|
|
21
|
+
└── index.ts # re-exports @grove-dev/ui
|
|
22
|
+
|
|
23
|
+
templates/
|
|
24
|
+
└── default/ # full Astro starter: pages/, layouts/, public/, data/,
|
|
25
|
+
# .github/, astro.config.mjs, tailwind.config.mjs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
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
|
+
|
|
30
|
+
## Usage in a space
|
|
31
|
+
|
|
32
|
+
```astro
|
|
33
|
+
---
|
|
34
|
+
import { ItemCard, ScoreBars, HealthBadge, DirectoryFilters } from "@grove-dev/astro";
|
|
35
|
+
import "@grove-dev/astro/styles.css";
|
|
36
|
+
import BaseLayout from "@grove-dev/astro/layouts/BaseLayout.astro";
|
|
37
|
+
import apps from "../data/generated/apps.json";
|
|
38
|
+
---
|
|
39
|
+
<BaseLayout title="My Grove space">
|
|
40
|
+
<DirectoryFilters facets={...} />
|
|
41
|
+
{apps.map((app) => <ItemCard item={app} />)}
|
|
42
|
+
</BaseLayout>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Components are imported by path, not through the barrel — that lets `astro check` validate them in their own context. The barrel re-exports `@grove-dev/ui` so generic helpers (`filterRecords`, `slugForCategory`, etc.) are available from the same import.
|
|
46
|
+
|
|
47
|
+
## Templates
|
|
48
|
+
|
|
49
|
+
`templates/default/` is a complete Astro starter. It contains:
|
|
50
|
+
|
|
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`
|
|
54
|
+
- `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
|
|
59
|
+
|
|
60
|
+
Business logic (filtering, sorting, scoring, faceting) is **imported from `@grove-dev/ui`**, never re-implemented in the template.
|
|
61
|
+
|
|
62
|
+
## Layering
|
|
63
|
+
|
|
64
|
+
`@grove-dev/astro` is the third layer of the Grove stack:
|
|
65
|
+
|
|
66
|
+
1. **`@grove-dev/core`** — schemas, importers, build pipeline (headless).
|
|
67
|
+
2. **`@grove-dev/ui`** — framework-agnostic UI primitives.
|
|
68
|
+
3. **`@grove-dev/astro`** ← you are here — Astro components, layouts, template.
|
|
69
|
+
|
|
70
|
+
If you need a Svelte or Next.js variant, see `@grove-dev/svelte` and `@grove-dev/nextjs`.
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm --filter @grove-dev/astro build
|
|
76
|
+
pnpm --filter @grove-dev/astro check
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @grove-dev/astro — Astro UI primitives for Grove.
|
|
3
|
+
*
|
|
4
|
+
* This package only owns Astro components, layouts, styles, and
|
|
5
|
+
* Astro-specific integrations. Generic filter/sort/stats/score
|
|
6
|
+
* helpers live in `@grove-dev/ui` and are re-exported from here for
|
|
7
|
+
* convenience.
|
|
8
|
+
*
|
|
9
|
+
* Templates (concrete pages, layouts, public/, .github/) are not
|
|
10
|
+
* published; they're copied into user projects by `grove new`.
|
|
11
|
+
*/
|
|
12
|
+
export * from "@grove-dev/ui";
|
|
13
|
+
export { default as ItemCard } from "./components/ItemCard.astro";
|
|
14
|
+
export { default as CategoryGrid } from "./components/CategoryGrid.astro";
|
|
15
|
+
export { default as HealthBadge } from "./components/HealthBadge.astro";
|
|
16
|
+
export { default as ScoreBars } from "./components/ScoreBars.astro";
|
|
17
|
+
export { default as DirectoryHero } from "./components/DirectoryHero.astro";
|
|
18
|
+
export { default as DirectoryFilters } from "./components/DirectoryFilters.astro";
|
|
19
|
+
export { default as ActiveFilterChips } from "./components/ActiveFilterChips.astro";
|
|
20
|
+
export { default as LensTabs } from "./components/LensTabs.astro";
|
|
21
|
+
export { default as MethodologyPanel } from "./components/MethodologyPanel.astro";
|
|
22
|
+
export { default as Pagination } from "./components/Pagination.astro";
|
|
23
|
+
export { default as ProjectDetail } from "./components/ProjectDetail.astro";
|
|
24
|
+
export { default as SubmitDraft } from "./components/SubmitDraft.astro";
|
|
25
|
+
export { default as BaseLayout } from "./layouts/BaseLayout.astro";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,cAAc,eAAe,CAAC;AAE9B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @grove-dev/astro — Astro UI primitives for Grove.
|
|
3
|
+
*
|
|
4
|
+
* This package only owns Astro components, layouts, styles, and
|
|
5
|
+
* Astro-specific integrations. Generic filter/sort/stats/score
|
|
6
|
+
* helpers live in `@grove-dev/ui` and are re-exported from here for
|
|
7
|
+
* convenience.
|
|
8
|
+
*
|
|
9
|
+
* Templates (concrete pages, layouts, public/, .github/) are not
|
|
10
|
+
* published; they're copied into user projects by `grove new`.
|
|
11
|
+
*/
|
|
12
|
+
export * from "@grove-dev/ui";
|
|
13
|
+
export { default as ItemCard } from "./components/ItemCard.astro";
|
|
14
|
+
export { default as CategoryGrid } from "./components/CategoryGrid.astro";
|
|
15
|
+
export { default as HealthBadge } from "./components/HealthBadge.astro";
|
|
16
|
+
export { default as ScoreBars } from "./components/ScoreBars.astro";
|
|
17
|
+
export { default as DirectoryHero } from "./components/DirectoryHero.astro";
|
|
18
|
+
export { default as DirectoryFilters } from "./components/DirectoryFilters.astro";
|
|
19
|
+
export { default as ActiveFilterChips } from "./components/ActiveFilterChips.astro";
|
|
20
|
+
export { default as LensTabs } from "./components/LensTabs.astro";
|
|
21
|
+
export { default as MethodologyPanel } from "./components/MethodologyPanel.astro";
|
|
22
|
+
export { default as Pagination } from "./components/Pagination.astro";
|
|
23
|
+
export { default as ProjectDetail } from "./components/ProjectDetail.astro";
|
|
24
|
+
export { default as SubmitDraft } from "./components/SubmitDraft.astro";
|
|
25
|
+
export { default as BaseLayout } from "./layouts/BaseLayout.astro";
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,cAAc,eAAe,CAAC;AAE9B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,4BAA4B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grove-dev/astro",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Astro framework adapter for Grove. Components, layouts, design tokens, and a default template.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"grove",
|
|
9
|
+
"astro",
|
|
10
|
+
"ui",
|
|
11
|
+
"template",
|
|
12
|
+
"components",
|
|
13
|
+
"static-site"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/tortuvshin/grove#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/tortuvshin/grove.git",
|
|
19
|
+
"directory": "packages/astro"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/tortuvshin/grove/issues"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"registry": "https://registry.npmjs.org/"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src",
|
|
31
|
+
"templates",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./styles.css": "./src/styles.css",
|
|
40
|
+
"./components/*": "./src/components/*.astro",
|
|
41
|
+
"./layouts/*": "./src/layouts/*.astro",
|
|
42
|
+
"./package.json": "./package.json",
|
|
43
|
+
"./templates/*": "./templates/*"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@grove-dev/core": "0.1.1",
|
|
47
|
+
"@grove-dev/ui": "0.1.1",
|
|
48
|
+
"astro": "^6.4.4"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"astro": "^6.0.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.json",
|
|
55
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
56
|
+
"test": "echo \"No astro package tests yet\""
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { DirectoryFilters } from "@grove-dev/ui";
|
|
3
|
+
import { activeFilterChips } from "@grove-dev/ui";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
filters: DirectoryFilters;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { filters } = Astro.props;
|
|
10
|
+
const chips = activeFilterChips(filters);
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{chips.length > 0 && (
|
|
14
|
+
<div class="oc-active-chips" aria-label="Active filters">
|
|
15
|
+
{chips.map((chip) => (
|
|
16
|
+
<span data-filter-chip={chip.key}>{chip.label}</span>
|
|
17
|
+
))}
|
|
18
|
+
<a href="/projects">Clear all</a>
|
|
19
|
+
</div>
|
|
20
|
+
)}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { slugForCategory } from "@grove-dev/ui";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
categories: Array<{ name: string; count: number }>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { categories } = Astro.props;
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<section class="oc-section">
|
|
12
|
+
<div class="oc-section-header">
|
|
13
|
+
<div>
|
|
14
|
+
<p class="oc-eyebrow">Browse</p>
|
|
15
|
+
<h2>Categories</h2>
|
|
16
|
+
</div>
|
|
17
|
+
<a href="/projects">View all</a>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="oc-category-grid">
|
|
20
|
+
{categories.map((category) => (
|
|
21
|
+
<a class="oc-category-card" href={`/categories/${slugForCategory(category.name)}`}>
|
|
22
|
+
<span>{category.name}</span>
|
|
23
|
+
<strong>{category.count}</strong>
|
|
24
|
+
</a>
|
|
25
|
+
))}
|
|
26
|
+
</div>
|
|
27
|
+
</section>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HealthStatus } from "@grove-dev/core";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
q?: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
tag?: string;
|
|
8
|
+
language?: string;
|
|
9
|
+
license?: string;
|
|
10
|
+
health?: string;
|
|
11
|
+
maintained?: boolean;
|
|
12
|
+
hideArchived?: boolean;
|
|
13
|
+
hasRecentRelease?: boolean;
|
|
14
|
+
categories: string[];
|
|
15
|
+
tags: string[];
|
|
16
|
+
languages: string[];
|
|
17
|
+
licenses: string[];
|
|
18
|
+
action?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
q = "",
|
|
23
|
+
category = "",
|
|
24
|
+
tag = "",
|
|
25
|
+
language = "",
|
|
26
|
+
license = "",
|
|
27
|
+
health = "all",
|
|
28
|
+
maintained = false,
|
|
29
|
+
hideArchived = false,
|
|
30
|
+
hasRecentRelease = false,
|
|
31
|
+
categories,
|
|
32
|
+
tags,
|
|
33
|
+
languages,
|
|
34
|
+
licenses,
|
|
35
|
+
action = "/projects",
|
|
36
|
+
} = Astro.props;
|
|
37
|
+
|
|
38
|
+
const healthOptions: Array<HealthStatus | "all"> = [
|
|
39
|
+
"all",
|
|
40
|
+
"mature",
|
|
41
|
+
"active",
|
|
42
|
+
"stale",
|
|
43
|
+
"needs_review",
|
|
44
|
+
"inactive",
|
|
45
|
+
"archived",
|
|
46
|
+
"unknown",
|
|
47
|
+
];
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<form class="oc-filters" action={action} method="get" role="search">
|
|
51
|
+
<label class="oc-filter-search">
|
|
52
|
+
<span>Search</span>
|
|
53
|
+
<input type="search" name="q" value={q} placeholder="Name, description, tag, license..." />
|
|
54
|
+
</label>
|
|
55
|
+
<label>
|
|
56
|
+
<span>Category</span>
|
|
57
|
+
<select name="category">
|
|
58
|
+
<option value="">All categories</option>
|
|
59
|
+
{categories.map((value) => <option value={value} selected={value === category}>{value}</option>)}
|
|
60
|
+
</select>
|
|
61
|
+
</label>
|
|
62
|
+
<label>
|
|
63
|
+
<span>Health</span>
|
|
64
|
+
<select name="health">
|
|
65
|
+
{healthOptions.map((value) => (
|
|
66
|
+
<option value={value} selected={value === health}>{value.replace(/_/g, " ")}</option>
|
|
67
|
+
))}
|
|
68
|
+
</select>
|
|
69
|
+
</label>
|
|
70
|
+
<label>
|
|
71
|
+
<span>Tag</span>
|
|
72
|
+
<select name="tag">
|
|
73
|
+
<option value="">All tags</option>
|
|
74
|
+
{tags.map((value) => <option value={value} selected={value === tag}>{value}</option>)}
|
|
75
|
+
</select>
|
|
76
|
+
</label>
|
|
77
|
+
<label>
|
|
78
|
+
<span>Language</span>
|
|
79
|
+
<select name="language">
|
|
80
|
+
<option value="">All languages</option>
|
|
81
|
+
{languages.map((value) => <option value={value} selected={value === language}>{value}</option>)}
|
|
82
|
+
</select>
|
|
83
|
+
</label>
|
|
84
|
+
<label>
|
|
85
|
+
<span>License</span>
|
|
86
|
+
<select name="license">
|
|
87
|
+
<option value="">All licenses</option>
|
|
88
|
+
{licenses.map((value) => <option value={value} selected={value === license}>{value}</option>)}
|
|
89
|
+
</select>
|
|
90
|
+
</label>
|
|
91
|
+
<div class="oc-filter-toggles">
|
|
92
|
+
<label><input type="checkbox" name="maintained" value="1" checked={maintained} /> Maintained only</label>
|
|
93
|
+
<label><input type="checkbox" name="hideArchived" value="1" checked={hideArchived} /> Hide archived</label>
|
|
94
|
+
<label><input type="checkbox" name="hasRecentRelease" value="1" checked={hasRecentRelease} /> Recent release</label>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="oc-filter-actions">
|
|
97
|
+
<button type="submit">Apply</button>
|
|
98
|
+
<a href={action}>Reset</a>
|
|
99
|
+
</div>
|
|
100
|
+
</form>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { DirectoryStats } from "@grove-dev/ui";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
name: string;
|
|
6
|
+
tagline: string;
|
|
7
|
+
stats: DirectoryStats;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { name, tagline, stats } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<section class="oc-hero">
|
|
14
|
+
<div>
|
|
15
|
+
<p class="oc-eyebrow">Grove</p>
|
|
16
|
+
<h1>{name}</h1>
|
|
17
|
+
<p class="oc-lede">{tagline}</p>
|
|
18
|
+
</div>
|
|
19
|
+
<form class="oc-hero-search" action="/projects" role="search">
|
|
20
|
+
<label>
|
|
21
|
+
<span class="oc-sr-only">Search projects</span>
|
|
22
|
+
<input type="search" name="q" placeholder="Search projects, tags, licenses..." />
|
|
23
|
+
</label>
|
|
24
|
+
<button type="submit">Search</button>
|
|
25
|
+
</form>
|
|
26
|
+
<dl class="oc-stats">
|
|
27
|
+
<div><dt>Projects</dt><dd>{stats.total}</dd></div>
|
|
28
|
+
<div><dt>Active</dt><dd>{stats.active}</dd></div>
|
|
29
|
+
<div><dt>Mature</dt><dd>{stats.mature}</dd></div>
|
|
30
|
+
<div><dt>Needs review</dt><dd>{stats.needsReview}</dd></div>
|
|
31
|
+
</dl>
|
|
32
|
+
</section>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HealthStatus } from "@grove-dev/core";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
status?: HealthStatus;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { status = "unknown" } = Astro.props;
|
|
9
|
+
const label = status.replace(/_/g, " ");
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<span class={`oc-health oc-health-${status}`}>{label}</span>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { CuratedItem, HealthEntry } from "@grove-dev/core";
|
|
3
|
+
import HealthBadge from "./HealthBadge.astro";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
item: CuratedItem;
|
|
7
|
+
health?: HealthEntry;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { item, health } = Astro.props;
|
|
11
|
+
const href = `/projects/${item.id}`;
|
|
12
|
+
const tags = item.taxonomy.tags.slice(0, 4);
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<article class="oc-card">
|
|
16
|
+
<div class="oc-card-header">
|
|
17
|
+
<div>
|
|
18
|
+
<h2 class="oc-card-title">
|
|
19
|
+
<a href={href}>{item.name}</a>
|
|
20
|
+
</h2>
|
|
21
|
+
<p class="oc-card-category">{item.taxonomy.category}</p>
|
|
22
|
+
</div>
|
|
23
|
+
<HealthBadge status={health?.health.status} />
|
|
24
|
+
</div>
|
|
25
|
+
<p class="oc-card-description">{item.description}</p>
|
|
26
|
+
<div class="oc-card-meta">
|
|
27
|
+
{health?.github?.stars != null && <span>{health.github.stars.toLocaleString()} stars</span>}
|
|
28
|
+
{health?.github?.license && <span>{health.github.license}</span>}
|
|
29
|
+
{health?.github?.language && <span>{health.github.language}</span>}
|
|
30
|
+
</div>
|
|
31
|
+
<div class="oc-card-actions">
|
|
32
|
+
<a href={`/projects/${item.id}`}>Details</a>
|
|
33
|
+
{item.links.github && <a href={item.links.github}>GitHub</a>}
|
|
34
|
+
{item.links.docs && <a href={item.links.docs}>Docs</a>}
|
|
35
|
+
</div>
|
|
36
|
+
{tags.length > 0 && (
|
|
37
|
+
<ul class="oc-tags" aria-label="Tags">
|
|
38
|
+
{tags.map((tag) => <li>{tag}</li>)}
|
|
39
|
+
</ul>
|
|
40
|
+
)}
|
|
41
|
+
</article>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { LENSES } from "@grove-dev/ui";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
current?: string;
|
|
6
|
+
action?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { current = "all", action = "/projects" } = Astro.props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<nav class="oc-lenses" aria-label="Curated lenses">
|
|
13
|
+
{LENSES.map((lens) => {
|
|
14
|
+
const params = new URLSearchParams();
|
|
15
|
+
for (const [key, value] of Object.entries(lens.params)) {
|
|
16
|
+
params.set(key, String(value));
|
|
17
|
+
}
|
|
18
|
+
const href = lens.id === "all" ? action : `${action}?${params.toString()}`;
|
|
19
|
+
const active = current === lens.id || (current === "all" && lens.id === "all");
|
|
20
|
+
return (
|
|
21
|
+
<a href={href} data-lens-id={lens.id} aria-current={active ? "page" : undefined} title={lens.description}>
|
|
22
|
+
{lens.label}
|
|
23
|
+
</a>
|
|
24
|
+
);
|
|
25
|
+
})}
|
|
26
|
+
</nav>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
<section class="oc-methodology">
|
|
5
|
+
<div>
|
|
6
|
+
<p class="oc-eyebrow">Methodology</p>
|
|
7
|
+
<h2>Health is a signal, not an automatic deletion rule.</h2>
|
|
8
|
+
<p>
|
|
9
|
+
Grove checks repository activity, archived state, releases, license,
|
|
10
|
+
adoption, and metadata freshness. Projects can still be kept, highlighted,
|
|
11
|
+
hidden, or preserved as historical through human curation decisions.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
<ul>
|
|
15
|
+
<li>Archived repositories are flagged clearly.</li>
|
|
16
|
+
<li>Recent commits and releases increase confidence.</li>
|
|
17
|
+
<li>Missing metadata becomes unknown or needs review.</li>
|
|
18
|
+
<li>Humans control final visibility in decisions.yml.</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</section>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
page: number;
|
|
4
|
+
pages: number;
|
|
5
|
+
base?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { page, pages, base = "/projects" } = Astro.props;
|
|
9
|
+
|
|
10
|
+
function href(target: number) {
|
|
11
|
+
const current = new URL(Astro.url);
|
|
12
|
+
current.searchParams.set("page", String(target));
|
|
13
|
+
const query = current.searchParams.toString();
|
|
14
|
+
return `${base}${query ? `?${query}` : ""}`;
|
|
15
|
+
}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
{pages > 1 && (
|
|
19
|
+
<nav class="oc-pagination" aria-label="Pagination">
|
|
20
|
+
<a href={href(Math.max(1, page - 1))} aria-disabled={page <= 1 ? "true" : undefined}>Previous</a>
|
|
21
|
+
<span>Page {page} of {pages}</span>
|
|
22
|
+
<a href={href(Math.min(pages, page + 1))} aria-disabled={page >= pages ? "true" : undefined}>Next</a>
|
|
23
|
+
</nav>
|
|
24
|
+
)}
|