@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
|
@@ -1,41 +1,300 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* ItemCard — generic project/resource/entity card.
|
|
4
|
+
*
|
|
5
|
+
* The most-decorated card in the showcase. Used on home (curated
|
|
6
|
+
* sections), the list page (cards-only views), and the search
|
|
7
|
+
* results. Renders:
|
|
8
|
+
*
|
|
9
|
+
* - Logo/avatar with initials fallback (logoUrl -> GitHub owner
|
|
10
|
+
* avatar -> 2-letter initials).
|
|
11
|
+
* - Owner / repo link (when `repoUrl` is github.com).
|
|
12
|
+
* - Stack + platform chips (first 3, then "+N" overflow).
|
|
13
|
+
* - Optional curated mark (label badge) — e.g. "Featured", "New".
|
|
14
|
+
* - Health badge (Active / Mature / Stale / Archived / ...).
|
|
15
|
+
* - License + star count + last-updated date.
|
|
16
|
+
* - Two CTAs: "View repo" (links.github if present, else repoUrl)
|
|
17
|
+
* and "Details" (the page link).
|
|
18
|
+
*
|
|
19
|
+
* Generic: takes any `Resource` (`kind: "project" | "resource" |
|
|
20
|
+
* "entity"`). For "project" we read the project-style fields
|
|
21
|
+
* (`repoUrl`, `logoUrl`, `stacks`, `platforms`, `github`,
|
|
22
|
+
* `curation`, `health`); for other kinds we degrade gracefully to
|
|
23
|
+
* the minimal view.
|
|
24
|
+
*
|
|
25
|
+
* The component is index-payload-friendly: every field it reads is
|
|
26
|
+
* present in the `toIndexRecord` projection (see
|
|
27
|
+
* `@grove-dev/core/schema.ts`).
|
|
28
|
+
*/
|
|
29
|
+
import type { Resource, ProjectRecord, HealthStatus } from "@grove-dev/core";
|
|
30
|
+
import { getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl } from "../lib/repo";
|
|
31
|
+
import { formatRelative, formatStars } from "../lib/format";
|
|
32
|
+
import { statusDisplay, prettySlug, labelDisplay } from "../lib/display";
|
|
4
33
|
|
|
5
34
|
interface Props {
|
|
6
|
-
|
|
7
|
-
|
|
35
|
+
record: Resource;
|
|
36
|
+
/** Detail page href. */
|
|
37
|
+
href: string;
|
|
38
|
+
/** When true, show owner/repo below the title. Default true. */
|
|
39
|
+
showOwner?: boolean;
|
|
40
|
+
/** When true, render the stack/platform chip row. Default true. */
|
|
41
|
+
showChips?: boolean;
|
|
42
|
+
/** When true, render the curated label badge row. Default true. */
|
|
43
|
+
showLabel?: boolean;
|
|
44
|
+
/** When true, render the metadata strip (license/stars/updated). */
|
|
45
|
+
showMeta?: boolean;
|
|
46
|
+
/** Extra class on the outer <article>. */
|
|
47
|
+
class?: string;
|
|
8
48
|
}
|
|
9
49
|
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
|
|
50
|
+
const {
|
|
51
|
+
record,
|
|
52
|
+
href,
|
|
53
|
+
showOwner = true,
|
|
54
|
+
showChips = true,
|
|
55
|
+
showLabel = true,
|
|
56
|
+
showMeta = true,
|
|
57
|
+
class: className = "",
|
|
58
|
+
} = Astro.props;
|
|
59
|
+
|
|
60
|
+
// Project-specific extraction
|
|
61
|
+
const isProject = record.kind === "project";
|
|
62
|
+
const project = isProject ? (record as ProjectRecord) : null;
|
|
63
|
+
|
|
64
|
+
const name =
|
|
65
|
+
record.kind === "resource"
|
|
66
|
+
? (record as { title: string }).title
|
|
67
|
+
: (record as { name: string }).name;
|
|
68
|
+
const description = record.description ?? "";
|
|
69
|
+
const tags = (record.tags ?? []).slice(0, 4);
|
|
70
|
+
const repoUrl = project?.repoUrl;
|
|
71
|
+
const logoUrl = project?.logoUrl;
|
|
72
|
+
const stacks = project?.stacks ?? (project?.stack ? [project.stack] : []);
|
|
73
|
+
const platforms = project?.platforms ?? [];
|
|
74
|
+
const healthStatus: HealthStatus | undefined = project?.health?.status;
|
|
75
|
+
const labels = record.curation?.labels ?? [];
|
|
76
|
+
const github = project?.github?.repository;
|
|
77
|
+
const stars = github?.stargazers_count;
|
|
78
|
+
const licenseSpdx = github?.license?.spdx_id;
|
|
79
|
+
const pushedAt = github?.pushed_at;
|
|
80
|
+
const githubUrl = repoUrl ?? (record.links as { github?: string } | undefined)?.github;
|
|
81
|
+
|
|
82
|
+
// Logo fallback chain
|
|
83
|
+
const ownerRepo = repoUrl ? getOwnerAndRepoFromRepoUrl(repoUrl) : null;
|
|
84
|
+
const avatarSrc =
|
|
85
|
+
logoUrl ?? (ownerRepo ? getOwnerAvatarUrl(ownerRepo.owner, 80) : null);
|
|
86
|
+
const initials = (name || "")
|
|
87
|
+
.replace(/[^a-z0-9]/gi, "")
|
|
88
|
+
.slice(0, 2)
|
|
89
|
+
.toUpperCase();
|
|
90
|
+
|
|
91
|
+
// Chips: 3 stacks/platforms, then overflow count
|
|
92
|
+
function withOverflow(
|
|
93
|
+
items: string[],
|
|
94
|
+
): { shown: string[]; overflow: number } {
|
|
95
|
+
const shown = items.filter(Boolean).slice(0, 3);
|
|
96
|
+
const overflow = Math.max(0, items.filter(Boolean).length - shown.length);
|
|
97
|
+
return { shown, overflow };
|
|
98
|
+
}
|
|
99
|
+
const stackChips = withOverflow(stacks);
|
|
100
|
+
const platformChips = withOverflow(platforms);
|
|
101
|
+
|
|
102
|
+
// Curated mark
|
|
103
|
+
const curatedLabel = labels[0] ?? null;
|
|
104
|
+
|
|
105
|
+
// Health class hook (for badge variant)
|
|
106
|
+
function healthBadgeClass(s: HealthStatus | undefined): string {
|
|
107
|
+
switch (s) {
|
|
108
|
+
case "mature":
|
|
109
|
+
case "active":
|
|
110
|
+
return "grove-badge-new";
|
|
111
|
+
case "stale":
|
|
112
|
+
case "inactive":
|
|
113
|
+
return "grove-badge-stale";
|
|
114
|
+
case "archived":
|
|
115
|
+
case "unavailable":
|
|
116
|
+
return "grove-badge-hot";
|
|
117
|
+
default:
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const healthLabel = healthStatus ? statusDisplay(healthStatus) : null;
|
|
122
|
+
|
|
123
|
+
const starsLabel = formatStars(stars);
|
|
124
|
+
const updatedLabel = formatRelative(pushedAt);
|
|
13
125
|
---
|
|
14
126
|
|
|
15
|
-
<article
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
127
|
+
<article
|
|
128
|
+
class:list={["grove-card grove-card-hover grove-item-card p-4", className]}
|
|
129
|
+
data-record-slug={record.slug}
|
|
130
|
+
data-record-kind={record.kind}
|
|
131
|
+
data-record-category={record.category}
|
|
132
|
+
data-record-stacks={stacks.join(",")}
|
|
133
|
+
data-record-platforms={platforms.join(",")}
|
|
134
|
+
>
|
|
135
|
+
<div class="flex items-start gap-3">
|
|
136
|
+
<div class="grove-item-card__logo flex-shrink-0">
|
|
137
|
+
{avatarSrc ? (
|
|
138
|
+
<img
|
|
139
|
+
src={avatarSrc}
|
|
140
|
+
alt=""
|
|
141
|
+
width="40"
|
|
142
|
+
height="40"
|
|
143
|
+
loading="lazy"
|
|
144
|
+
decoding="async"
|
|
145
|
+
class="h-10 w-10 rounded-md border border-ink-200 object-contain bg-white dark:border-ink-800 dark:bg-ink-950"
|
|
146
|
+
/>
|
|
147
|
+
) : (
|
|
148
|
+
<span
|
|
149
|
+
class="inline-flex h-10 w-10 items-center justify-center rounded-md border border-ink-200 bg-ink-50 text-sm font-semibold text-ink-700 dark:border-ink-800 dark:bg-ink-900 dark:text-ink-200"
|
|
150
|
+
aria-hidden="true"
|
|
151
|
+
>
|
|
152
|
+
{initials || "·"}
|
|
153
|
+
</span>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="min-w-0 flex-1">
|
|
158
|
+
<div class="flex flex-wrap items-baseline gap-2">
|
|
159
|
+
<h2 class="m-0 text-[1rem] font-semibold leading-tight tracking-tight text-ink-900 dark:text-ink-100">
|
|
160
|
+
<a href={href} class="text-inherit no-underline hover:underline">
|
|
161
|
+
{name}
|
|
162
|
+
</a>
|
|
163
|
+
</h2>
|
|
164
|
+
{showLabel && curatedLabel && (
|
|
165
|
+
<span
|
|
166
|
+
class:list={["grove-badge", `grove-badge-${curatedLabel}`]}
|
|
167
|
+
title={`Curated: ${labelDisplay(curatedLabel) ?? curatedLabel}`}
|
|
168
|
+
>
|
|
169
|
+
{labelDisplay(curatedLabel) ?? prettySlug(curatedLabel)}
|
|
170
|
+
</span>
|
|
171
|
+
)}
|
|
172
|
+
{healthLabel && (
|
|
173
|
+
<span class:list={["grove-badge", healthBadgeClass(healthStatus)]}>
|
|
174
|
+
{healthLabel}
|
|
175
|
+
</span>
|
|
176
|
+
)}
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{showOwner && ownerRepo && (
|
|
180
|
+
<p class="m-0 mt-0.5 text-2xs text-ink-500 dark:text-ink-400">
|
|
181
|
+
<a
|
|
182
|
+
href={`https://github.com/${ownerRepo.owner}`}
|
|
183
|
+
target="_blank"
|
|
184
|
+
rel="noopener noreferrer"
|
|
185
|
+
class="text-inherit no-underline hover:text-ink-900 dark:hover:text-ink-100"
|
|
186
|
+
>
|
|
187
|
+
@{ownerRepo.owner}
|
|
188
|
+
</a>
|
|
189
|
+
<span class="mx-1 text-ink-300 dark:text-ink-700">/</span>
|
|
190
|
+
<a
|
|
191
|
+
href={repoUrl}
|
|
192
|
+
target="_blank"
|
|
193
|
+
rel="noopener noreferrer"
|
|
194
|
+
class="text-inherit no-underline hover:text-ink-900 dark:hover:text-ink-100"
|
|
195
|
+
>
|
|
196
|
+
{ownerRepo.repo}
|
|
197
|
+
</a>
|
|
198
|
+
</p>
|
|
199
|
+
)}
|
|
200
|
+
|
|
201
|
+
{description && (
|
|
202
|
+
<p class="m-0 mt-1.5 line-clamp-2 text-[0.8125rem] leading-snug text-ink-500 dark:text-ink-400">
|
|
203
|
+
{description}
|
|
204
|
+
</p>
|
|
205
|
+
)}
|
|
22
206
|
</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
207
|
</div>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
208
|
+
|
|
209
|
+
{showChips && (stackChips.shown.length > 0 || platformChips.shown.length > 0) && (
|
|
210
|
+
<div class="mt-3 flex flex-wrap items-center gap-1.5">
|
|
211
|
+
{stackChips.shown.map((s) => (
|
|
212
|
+
<span class="grove-badge" title={`Stack: ${s}`}>{s}</span>
|
|
213
|
+
))}
|
|
214
|
+
{stackChips.overflow > 0 && (
|
|
215
|
+
<span class="grove-badge" title={`${stackChips.overflow} more stacks`}>
|
|
216
|
+
+{stackChips.overflow}
|
|
217
|
+
</span>
|
|
218
|
+
)}
|
|
219
|
+
{platformChips.shown.map((p) => (
|
|
220
|
+
<span class="grove-badge" title={`Platform: ${p}`}>{p}</span>
|
|
221
|
+
))}
|
|
222
|
+
{platformChips.overflow > 0 && (
|
|
223
|
+
<span
|
|
224
|
+
class="grove-badge"
|
|
225
|
+
title={`${platformChips.overflow} more platforms`}
|
|
226
|
+
>
|
|
227
|
+
+{platformChips.overflow}
|
|
228
|
+
</span>
|
|
229
|
+
)}
|
|
230
|
+
{tags.length > 0 && tags.map((t) => <span class="grove-badge">{t}</span>)}
|
|
231
|
+
</div>
|
|
40
232
|
)}
|
|
233
|
+
|
|
234
|
+
{showMeta && (starsLabel || licenseSpdx || updatedLabel !== "—") && (
|
|
235
|
+
<dl
|
|
236
|
+
class="mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-2xs text-ink-500 dark:text-ink-400"
|
|
237
|
+
aria-label="Project signals"
|
|
238
|
+
>
|
|
239
|
+
{starsLabel && (
|
|
240
|
+
<div class="flex items-center gap-1">
|
|
241
|
+
<dt class="sr-only">Stars</dt>
|
|
242
|
+
<dd class="m-0 font-mono">{starsLabel}</dd>
|
|
243
|
+
<span aria-hidden="true">★</span>
|
|
244
|
+
</div>
|
|
245
|
+
)}
|
|
246
|
+
{licenseSpdx && (
|
|
247
|
+
<div class="flex items-center gap-1">
|
|
248
|
+
<dt class="sr-only">License</dt>
|
|
249
|
+
<dd class="m-0">{licenseSpdx}</dd>
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
{updatedLabel && updatedLabel !== "—" && (
|
|
253
|
+
<div class="flex items-center gap-1">
|
|
254
|
+
<dt class="sr-only">Last updated</dt>
|
|
255
|
+
<dd class="m-0">Updated {updatedLabel}</dd>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</dl>
|
|
259
|
+
)}
|
|
260
|
+
|
|
261
|
+
<div class="mt-4 flex flex-wrap items-center gap-2">
|
|
262
|
+
<a
|
|
263
|
+
href={href}
|
|
264
|
+
class="grove-btn grove-btn-outline"
|
|
265
|
+
data-event="card_details_click"
|
|
266
|
+
data-event-source="item_card"
|
|
267
|
+
data-event-app={record.slug}
|
|
268
|
+
>
|
|
269
|
+
Details
|
|
270
|
+
</a>
|
|
271
|
+
{githubUrl && (
|
|
272
|
+
<a
|
|
273
|
+
href={githubUrl}
|
|
274
|
+
target="_blank"
|
|
275
|
+
rel="noopener noreferrer"
|
|
276
|
+
class="grove-btn grove-btn-ghost"
|
|
277
|
+
data-event="card_repo_click"
|
|
278
|
+
data-event-source="item_card"
|
|
279
|
+
data-event-app={record.slug}
|
|
280
|
+
>
|
|
281
|
+
View repo
|
|
282
|
+
</a>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
41
285
|
</article>
|
|
286
|
+
|
|
287
|
+
<style>
|
|
288
|
+
.grove-item-card {
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
gap: 0.25rem;
|
|
292
|
+
height: 100%;
|
|
293
|
+
}
|
|
294
|
+
.line-clamp-2 {
|
|
295
|
+
display: -webkit-box;
|
|
296
|
+
-webkit-line-clamp: 2;
|
|
297
|
+
-webkit-box-orient: vertical;
|
|
298
|
+
overflow: hidden;
|
|
299
|
+
}
|
|
300
|
+
</style>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* MinimalAbout — 3 short points + title, used on the about page.
|
|
4
|
+
*
|
|
5
|
+
* A trimmed-down sibling of `WhyThisExists` with a different layout
|
|
6
|
+
* (no icons, no "0X" counter, vertical list) for text-heavy pages.
|
|
7
|
+
*
|
|
8
|
+
* Generic: every point comes through props.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface AboutPoint {
|
|
12
|
+
title: string;
|
|
13
|
+
body: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
points?: AboutPoint[];
|
|
18
|
+
title?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
points,
|
|
25
|
+
title = "About this directory",
|
|
26
|
+
description,
|
|
27
|
+
class: className = "",
|
|
28
|
+
} = Astro.props;
|
|
29
|
+
|
|
30
|
+
const defaultPoints: AboutPoint[] = [
|
|
31
|
+
{
|
|
32
|
+
title: "Curated, not scraped",
|
|
33
|
+
body: "Every entry is hand-checked. If something here, it's because someone looked at the source, ran the build, and decided it was worth your time.",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: "Plain text all the way down",
|
|
37
|
+
body: "Records live in plain YAML, content in plain Markdown, and the static output in plain HTML. No database, no JavaScript framework, no CDN with sticky sessions.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
title: "Free to fork, free to extend",
|
|
41
|
+
body: "This whole site is one repo. If you want your own copy, fork it. If you want to add a record, open a PR. The directory is the community.",
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const items = points && points.length > 0 ? points : defaultPoints;
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
<section class:list={["grove-section", className]}>
|
|
49
|
+
<div class="grove-container-narrow mx-auto px-5 sm:px-7">
|
|
50
|
+
<header class="mb-6 flex flex-col gap-2">
|
|
51
|
+
<h2 class="m-0 text-[1.5rem] sm:text-[1.625rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
52
|
+
{title}
|
|
53
|
+
</h2>
|
|
54
|
+
{description && (
|
|
55
|
+
<p class="m-0 text-[0.9375rem] text-ink-500 dark:text-ink-400">
|
|
56
|
+
{description}
|
|
57
|
+
</p>
|
|
58
|
+
)}
|
|
59
|
+
</header>
|
|
60
|
+
|
|
61
|
+
<ol class="m-0 flex list-none flex-col gap-5 p-0">
|
|
62
|
+
{items.map((p, i) => (
|
|
63
|
+
<li class="flex gap-4">
|
|
64
|
+
<span
|
|
65
|
+
aria-hidden="true"
|
|
66
|
+
class="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md border border-ink-200 bg-ink-50 font-mono text-2xs text-ink-500 dark:border-ink-800 dark:bg-ink-900 dark:text-ink-400"
|
|
67
|
+
>
|
|
68
|
+
0{i + 1}
|
|
69
|
+
</span>
|
|
70
|
+
<div>
|
|
71
|
+
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
72
|
+
{p.title}
|
|
73
|
+
</h3>
|
|
74
|
+
<p class="m-0 mt-1 text-[0.9375rem] leading-relaxed text-ink-500 dark:text-ink-400">
|
|
75
|
+
{p.body}
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</li>
|
|
79
|
+
))}
|
|
80
|
+
</ol>
|
|
81
|
+
</div>
|
|
82
|
+
</section>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* OriginalCollection — legacy lineage card.
|
|
4
|
+
*
|
|
5
|
+
* Renders a single wide card that links out to the project's origin
|
|
6
|
+
* repository, with stars/forks/contributors stats. Used on home and
|
|
7
|
+
* about pages to give visitors a way to find the original curated
|
|
8
|
+
* collection this directory descends from.
|
|
9
|
+
*
|
|
10
|
+
* Generic: takes plain `stats` and a `repoUrl`. No blueprint or
|
|
11
|
+
* site-specific copy.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface OriginStats {
|
|
15
|
+
stars?: number;
|
|
16
|
+
forks?: number;
|
|
17
|
+
contributors?: number;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
repoUrl: string;
|
|
23
|
+
repoLabel?: string;
|
|
24
|
+
stats?: OriginStats;
|
|
25
|
+
title?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
repoUrl,
|
|
32
|
+
repoLabel = "View on GitHub",
|
|
33
|
+
stats = {},
|
|
34
|
+
title = "A growing list, forked from the original",
|
|
35
|
+
description = "This directory descends from an earlier curated collection. The original still lives on — same shape, same spirit, fresh data.",
|
|
36
|
+
class: className = "",
|
|
37
|
+
} = Astro.props;
|
|
38
|
+
|
|
39
|
+
function fmt(n: number | undefined): string | null {
|
|
40
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
41
|
+
if (n >= 1000) {
|
|
42
|
+
const k = n / 1000;
|
|
43
|
+
return (k >= 10 ? Math.round(k).toString() : k.toFixed(1)) + "k";
|
|
44
|
+
}
|
|
45
|
+
return n.toString();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const hasStats =
|
|
49
|
+
stats.stars !== undefined ||
|
|
50
|
+
stats.forks !== undefined ||
|
|
51
|
+
stats.contributors !== undefined;
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<section class:list={["grove-section", className]}>
|
|
55
|
+
<div class="grove-container mx-auto px-5 sm:px-7">
|
|
56
|
+
<div
|
|
57
|
+
class="grove-card flex flex-col gap-4 p-6 sm:flex-row sm:items-center sm:justify-between"
|
|
58
|
+
>
|
|
59
|
+
<div class="flex flex-col gap-2">
|
|
60
|
+
<span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
|
|
61
|
+
Origin
|
|
62
|
+
</span>
|
|
63
|
+
<h2 class="m-0 text-[1.25rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
64
|
+
{title}
|
|
65
|
+
</h2>
|
|
66
|
+
<p class="m-0 max-w-2xl text-[0.875rem] text-ink-500 dark:text-ink-400">
|
|
67
|
+
{description}
|
|
68
|
+
</p>
|
|
69
|
+
{stats.description && (
|
|
70
|
+
<p class="m-0 max-w-2xl text-[0.8125rem] text-ink-500 dark:text-ink-400">
|
|
71
|
+
{stats.description}
|
|
72
|
+
</p>
|
|
73
|
+
)}
|
|
74
|
+
{hasStats && (
|
|
75
|
+
<dl
|
|
76
|
+
class="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-2xs text-ink-500 dark:text-ink-400"
|
|
77
|
+
aria-label="Original repository signals"
|
|
78
|
+
>
|
|
79
|
+
{stats.stars !== undefined && (
|
|
80
|
+
<div class="flex items-center gap-1">
|
|
81
|
+
<dd class="m-0 font-mono">{fmt(stats.stars) ?? stats.stars}</dd>
|
|
82
|
+
<dt>stars</dt>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
{stats.forks !== undefined && (
|
|
86
|
+
<div class="flex items-center gap-1">
|
|
87
|
+
<dd class="m-0 font-mono">{fmt(stats.forks) ?? stats.forks}</dd>
|
|
88
|
+
<dt>forks</dt>
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
{stats.contributors !== undefined && (
|
|
92
|
+
<div class="flex items-center gap-1">
|
|
93
|
+
<dd class="m-0 font-mono">
|
|
94
|
+
{fmt(stats.contributors) ?? stats.contributors}
|
|
95
|
+
</dd>
|
|
96
|
+
<dt>contributors</dt>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</dl>
|
|
100
|
+
)}
|
|
101
|
+
</div>
|
|
102
|
+
<div class="flex flex-shrink-0 items-center gap-2">
|
|
103
|
+
<a
|
|
104
|
+
href={repoUrl}
|
|
105
|
+
target="_blank"
|
|
106
|
+
rel="noopener noreferrer"
|
|
107
|
+
class="grove-btn grove-btn-primary"
|
|
108
|
+
data-event="original_collection_click"
|
|
109
|
+
data-event-source="original_collection"
|
|
110
|
+
>
|
|
111
|
+
{repoLabel} →
|
|
112
|
+
</a>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|