@grove-dev/astro 0.2.10 → 0.2.13
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/lib/display.d.ts +11 -17
- package/dist/lib/display.d.ts.map +1 -1
- package/dist/lib/display.js +43 -43
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/format.d.ts +6 -7
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +11 -12
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/lenses.d.ts +35 -73
- package/dist/lib/lenses.d.ts.map +1 -1
- package/dist/lib/lenses.js +144 -126
- package/dist/lib/lenses.js.map +1 -1
- package/dist/lib/repo.d.ts +11 -22
- package/dist/lib/repo.d.ts.map +1 -1
- package/dist/lib/repo.js +12 -25
- package/dist/lib/repo.js.map +1 -1
- package/dist/lib/scores.d.ts +15 -16
- package/dist/lib/scores.d.ts.map +1 -1
- package/dist/lib/scores.js +4 -11
- package/dist/lib/scores.js.map +1 -1
- package/dist/lib/search.d.ts +47 -129
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +195 -180
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/taxonomy-counts.d.ts +7 -21
- package/dist/lib/taxonomy-counts.d.ts.map +1 -1
- package/dist/lib/taxonomy-counts.js +12 -31
- package/dist/lib/taxonomy-counts.js.map +1 -1
- package/package.json +4 -4
- package/src/components/AppsIndexRow.astro +55 -184
- package/src/components/AppsPagination.astro +45 -108
- package/src/components/CategoryGrid.astro +43 -31
- package/src/components/ContributorsGrid.astro +3 -3
- package/src/components/CurationGrid.astro +41 -0
- package/src/components/ExploreByStack.astro +6 -0
- package/src/components/FilterGroupMenu.astro +49 -0
- package/src/components/FilterOptions.astro +44 -0
- package/src/components/Hero.astro +198 -147
- package/src/components/Icon.astro +10 -3
- package/src/components/ItemCard.astro +266 -248
- package/src/components/RecordSection.astro +68 -33
- package/src/components/RefinePanel.astro +115 -141
- package/src/components/StackGrid.astro +32 -24
- package/src/components/WhyThisExists.astro +32 -32
- package/src/layouts/BaseLayout.astro +104 -49
- package/src/layouts/Container.astro +5 -44
- package/src/layouts/Footer.astro +31 -20
- package/src/layouts/Header.astro +40 -19
- package/src/layouts/SectionHeader.astro +27 -43
- package/src/layouts/Seo.astro +38 -56
- package/src/layouts/ThemeToggle.astro +35 -48
- package/src/lib/display.ts +46 -53
- package/src/lib/format.ts +11 -12
- package/src/lib/lenses.ts +151 -172
- package/src/lib/repo.ts +16 -28
- package/src/lib/scores.ts +15 -19
- package/src/lib/search.ts +222 -290
- package/src/lib/taxonomy-counts.ts +12 -46
- package/src/styles.css +207 -1
- package/templates/default/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/templates/default/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/templates/default/.github/ISSUE_TEMPLATE/record_submission.md +48 -0
- package/templates/default/.github/pull_request_template.md +27 -0
- package/templates/default/.github/workflows/build.yml +55 -0
- package/templates/default/.github/workflows/cleanup-stale-records.yml +48 -0
- package/templates/default/.github/workflows/daily-refresh.yml +53 -0
- package/templates/default/.github/workflows/sync-contributors.yml +53 -0
- package/templates/default/.github/workflows/sync-github-metadata.yml +58 -0
- package/templates/default/.github/workflows/validate-data.yml +35 -0
- package/templates/default/data/records/coolify.yml +46 -0
- package/templates/default/data/records/gitea.yml +46 -0
- package/templates/default/data/records/hoppscotch.yml +47 -0
- package/templates/default/data/records/inventree.yml +47 -0
- package/templates/default/data/records/label-studio.yml +46 -0
- package/templates/default/data/records/logseq.yml +48 -0
- package/templates/default/data/records/private-gpt.yml +47 -0
- package/templates/default/data/records/wakapi.yml +44 -0
- package/templates/default/data/taxonomy/categories.yml +47 -0
- package/templates/default/data/taxonomy/distribution-channels.yml +43 -0
- package/templates/default/data/taxonomy/platforms.yml +27 -0
- package/templates/default/data/taxonomy/stacks.yml +41 -0
- package/templates/default/grove.config.ts +1 -1
- package/templates/default/package.json +5 -4
- package/templates/default/public/icons/stacks/bun.svg +1 -0
- package/templates/default/public/icons/stacks/clojure.svg +1 -0
- package/templates/default/public/icons/stacks/clojurescript.svg +1 -0
- package/templates/default/public/icons/stacks/deno.svg +1 -0
- package/templates/default/public/icons/stacks/django.svg +1 -0
- package/templates/default/public/icons/stacks/docker.svg +1 -0
- package/templates/default/public/icons/stacks/go.svg +1 -0
- package/templates/default/public/icons/stacks/llm.svg +1 -0
- package/templates/default/public/icons/stacks/node.js.svg +1 -0
- package/templates/default/public/icons/stacks/sveltekit.svg +1 -0
- package/templates/default/public/icons/stacks/vue.svg +1 -0
- package/templates/default/public/llms-full.txt +197 -5
- package/templates/default/public/llms.txt +107 -6
- package/templates/default/public/sitemap.xml +63 -0
- package/templates/default/scripts/build-llms.mjs +123 -0
- package/templates/default/scripts/build-records-json.mjs +27 -0
- package/templates/default/scripts/build-sitemap.mjs +20 -0
- package/templates/default/scripts/cleanup-stale-records.mjs +20 -0
- package/templates/default/scripts/enrich-github-metadata.mjs +99 -0
- package/templates/default/scripts/fetch-icons.mjs +137 -0
- package/templates/default/scripts/migrate-legacy-to-schema-v1.mjs +86 -0
- package/templates/default/scripts/parse-legacy-readme.mjs +59 -0
- package/templates/default/scripts/refresh-records-activity.mjs +24 -0
- package/templates/default/scripts/repair-license-format.mjs +72 -0
- package/templates/default/scripts/report-cleanup-candidates.mjs +21 -0
- package/templates/default/scripts/seed-from-github.mjs +62 -0
- package/templates/default/scripts/sync-contributors.mjs +145 -0
- package/templates/default/scripts/sync-github-metadata.mjs +28 -0
- package/templates/default/scripts/validate-records.mjs +27 -0
- package/templates/default/src/data/records.ts +134 -51
- package/templates/default/src/pages/404.astro +64 -0
- package/templates/default/src/pages/{projects/[slug].astro → [slug]/[itemSlug].astro} +236 -66
- package/templates/default/src/pages/[slug]/index.astro +476 -0
- package/templates/default/src/pages/about.astro +165 -82
- package/templates/default/src/pages/apps/[itemSlug].astro +301 -0
- package/templates/default/src/pages/apps/index.astro +146 -0
- package/templates/default/src/pages/contributors.astro +184 -0
- package/templates/default/src/pages/index.astro +119 -162
- package/templates/default/src/pages/submit.astro +276 -114
- package/templates/default/wrangler.jsonc +28 -0
- package/LICENSE +0 -21
- package/templates/default/.astro/content.d.ts +0 -155
- package/templates/default/.astro/settings.json +0 -5
- package/templates/default/.astro/types.d.ts +0 -1
- package/templates/default/data/generated/apps.json +0 -1
- package/templates/default/data/generated/records.full.json +0 -8
- package/templates/default/data/generated/records.index.json +0 -7
- package/templates/default/data/generated/records.json +0 -8
- package/templates/default/data/generated/repo-stats.json +0 -1
- package/templates/default/data/generated/site-config.json +0 -31
- package/templates/default/dist/_astro/BaseLayout.Dz_HR-68.css +0 -1
- package/templates/default/dist/about/index.html +0 -165
- package/templates/default/dist/icons/platforms/android.svg +0 -1
- package/templates/default/dist/icons/platforms/apple-dark.svg +0 -1
- package/templates/default/dist/icons/platforms/apple-light.svg +0 -1
- package/templates/default/dist/icons/platforms/chrome.svg +0 -1
- package/templates/default/dist/icons/platforms/chromeos.svg +0 -5
- package/templates/default/dist/icons/platforms/desktop.svg +0 -4
- package/templates/default/dist/icons/platforms/embedded.svg +0 -5
- package/templates/default/dist/icons/platforms/ios.svg +0 -1
- package/templates/default/dist/icons/platforms/linux.svg +0 -1
- package/templates/default/dist/icons/platforms/macos.svg +0 -1
- package/templates/default/dist/icons/platforms/ubuntu.svg +0 -1
- package/templates/default/dist/icons/platforms/web.svg +0 -5
- package/templates/default/dist/icons/platforms/windows.svg +0 -6
- package/templates/default/dist/icons/stacks/android.svg +0 -1
- package/templates/default/dist/icons/stacks/apple-dark.svg +0 -1
- package/templates/default/dist/icons/stacks/apple-light.svg +0 -1
- package/templates/default/dist/icons/stacks/capacitor.svg +0 -14
- package/templates/default/dist/icons/stacks/dart.svg +0 -1
- package/templates/default/dist/icons/stacks/firebase.svg +0 -1
- package/templates/default/dist/icons/stacks/flutter.svg +0 -1
- package/templates/default/dist/icons/stacks/graphql.svg +0 -1
- package/templates/default/dist/icons/stacks/ionic.svg +0 -4
- package/templates/default/dist/icons/stacks/java.svg +0 -1
- package/templates/default/dist/icons/stacks/javascript.svg +0 -1
- package/templates/default/dist/icons/stacks/kotlin.svg +0 -1
- package/templates/default/dist/icons/stacks/mongodb.svg +0 -1
- package/templates/default/dist/icons/stacks/nodejs.svg +0 -1
- package/templates/default/dist/icons/stacks/python.svg +0 -1
- package/templates/default/dist/icons/stacks/react-native.svg +0 -1
- package/templates/default/dist/icons/stacks/react.svg +0 -1
- package/templates/default/dist/icons/stacks/rust.svg +0 -1
- package/templates/default/dist/icons/stacks/solidity.svg +0 -1
- package/templates/default/dist/icons/stacks/swift.svg +0 -1
- package/templates/default/dist/icons/stacks/tensorflow.svg +0 -1
- package/templates/default/dist/icons/stacks/typescript.svg +0 -1
- package/templates/default/dist/index.html +0 -55
- package/templates/default/dist/llms-full.txt +0 -14
- package/templates/default/dist/llms.txt +0 -11
- package/templates/default/dist/og-image.svg +0 -48
- package/templates/default/dist/projects/index.html +0 -61
- package/templates/default/dist/robots.txt +0 -53
- package/templates/default/dist/sitemap.xml +0 -21
- package/templates/default/dist/submit/index.html +0 -69
- package/templates/default/node_modules/.bin/astro +0 -21
- package/templates/default/node_modules/.bin/astro-check +0 -21
- package/templates/default/node_modules/.bin/grove +0 -21
- package/templates/default/node_modules/.bin/jiti +0 -21
- package/templates/default/node_modules/.bin/prettier +0 -21
- package/templates/default/node_modules/.bin/rollup +0 -21
- package/templates/default/node_modules/.bin/tsc +0 -21
- package/templates/default/node_modules/.bin/tsserver +0 -21
- package/templates/default/node_modules/.bin/tsx +0 -21
- package/templates/default/node_modules/.bin/vite +0 -21
- package/templates/default/node_modules/.bin/yaml +0 -21
- package/templates/default/node_modules/.vite/deps/_metadata.json +0 -79
- package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js +0 -433
- package/templates/default/node_modules/.vite/deps/astro-KRVZHBC5.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/astro___aria-query.js +0 -5
- package/templates/default/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/astro___axobject-query.js +0 -5
- package/templates/default/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/astro___html-escaper.js +0 -9
- package/templates/default/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -241
- package/templates/default/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js +0 -1592
- package/templates/default/node_modules/.vite/deps/audit-GYHQ3ITH.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
- package/templates/default/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js +0 -186
- package/templates/default/node_modules/.vite/deps/chunk-7P4KIK53.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js +0 -48
- package/templates/default/node_modules/.vite/deps/chunk-BQGHFQJH.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js +0 -6779
- package/templates/default/node_modules/.vite/deps/chunk-CJCDKWP2.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js +0 -33
- package/templates/default/node_modules/.vite/deps/chunk-FHJ3XBDE.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js +0 -88
- package/templates/default/node_modules/.vite/deps/chunk-FQEMNIUQ.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js +0 -72
- package/templates/default/node_modules/.vite/deps/chunk-HBSVDAQD.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js +0 -3757
- package/templates/default/node_modules/.vite/deps/chunk-HDOQ2U3U.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js +0 -54
- package/templates/default/node_modules/.vite/deps/chunk-XEBKRCNE.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/package.json +0 -3
- package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js +0 -213
- package/templates/default/node_modules/.vite/deps/settings-C5H4VHXU.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js +0 -525
- package/templates/default/node_modules/.vite/deps/toolbar-H5DB6HVP.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js +0 -1020
- package/templates/default/node_modules/.vite/deps/ui-library-JGHU6JE2.js.map +0 -7
- package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js +0 -147
- package/templates/default/node_modules/.vite/deps/xray-EFCQOW4E.js.map +0 -7
- package/templates/default/src/components/layout/BaseLayout.astro +0 -47
- package/templates/default/src/components/layout/Container.astro +0 -18
- package/templates/default/src/components/layout/Footer.astro +0 -27
- package/templates/default/src/components/layout/Header.astro +0 -43
- package/templates/default/src/lib/markdown.ts +0 -171
- package/templates/default/src/pages/projects/index.astro +0 -193
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Detail page — one record at a time.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* 1. Breadcrumb — back to
|
|
5
|
+
* Layout (top to bottom):
|
|
6
|
+
* 1. Breadcrumb — back to the directory index
|
|
7
7
|
* 2. AppCard-style header — logo + name + owner + description + CTAs
|
|
8
8
|
* 3. ScoreBars — 5-dimension + overall score block
|
|
9
9
|
* 4. BestFor / WhyListed / Caveats — 3-column curation grid
|
|
@@ -14,14 +14,24 @@
|
|
|
14
14
|
* body) when present
|
|
15
15
|
* 9. JSON-LD SoftwareSourceCode — for SEO + AI crawlers
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Generic across the 3 record kinds (project / resource / entity) —
|
|
18
|
+
* non-project fields simply render nothing. The URL is dynamic on
|
|
19
|
+
* both `[slug]` (blueprint route, e.g. `projects`/`resources`/
|
|
20
|
+
* `entities`) and `[itemSlug]` (the record slug), so a single page
|
|
21
|
+
* file serves every blueprint and every record.
|
|
19
22
|
*/
|
|
20
23
|
import siteConfig from "../../../data/generated/site-config.json";
|
|
21
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
fullRecords,
|
|
26
|
+
projectBySlug,
|
|
27
|
+
resourceBySlug,
|
|
28
|
+
entityBySlug,
|
|
29
|
+
indexSlug,
|
|
30
|
+
} from "../../data/records";
|
|
22
31
|
import BaseLayout from "@grove-dev/astro/layouts/BaseLayout.astro";
|
|
23
32
|
import Icon from "@grove-dev/astro/components/Icon.astro";
|
|
24
33
|
import ScoreBars from "@grove-dev/astro/components/ScoreBars.astro";
|
|
34
|
+
import CurationGrid from "@grove-dev/astro/components/CurationGrid.astro";
|
|
25
35
|
import { getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl } from "@grove-dev/astro";
|
|
26
36
|
import { formatRelative, formatStars } from "@grove-dev/astro";
|
|
27
37
|
import { statusDisplay, prettySlug, labelDisplay } from "@grove-dev/astro";
|
|
@@ -29,15 +39,27 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
29
39
|
import { fileURLToPath } from "node:url";
|
|
30
40
|
import { dirname, resolve } from "node:path";
|
|
31
41
|
import type { ProjectRecord } from "@grove-dev/core";
|
|
42
|
+
import { marked } from "marked";
|
|
32
43
|
|
|
33
|
-
export function getStaticPaths() {
|
|
44
|
+
export async function getStaticPaths() {
|
|
45
|
+
// Walk every record and emit a path for it. The directory slug
|
|
46
|
+
// (`projects` / `resources` / `entities`) is taken from the
|
|
47
|
+
// blueprint config so a `resource-hub` blueprint produces
|
|
48
|
+
// `/resources/<slug>` URLs without any per-blueprint fork.
|
|
49
|
+
let dirSlug = "projects";
|
|
50
|
+
try {
|
|
51
|
+
const raw = await import("../../../data/generated/site-config.json");
|
|
52
|
+
dirSlug = raw.default?.blueprintConfig?.routeSlug ?? "projects";
|
|
53
|
+
} catch {
|
|
54
|
+
/* not present */
|
|
55
|
+
}
|
|
34
56
|
return fullRecords.map((record) => ({
|
|
35
|
-
params: { slug: record.slug },
|
|
57
|
+
params: { slug: dirSlug, itemSlug: record.slug },
|
|
36
58
|
}));
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
const slug = indexSlug();
|
|
40
|
-
const recordSlug = Astro.params.
|
|
61
|
+
const slug = Astro.params.slug ?? indexSlug();
|
|
62
|
+
const recordSlug = Astro.params.itemSlug ?? "";
|
|
41
63
|
const project = projectBySlug(recordSlug);
|
|
42
64
|
const resource = resourceBySlug(recordSlug);
|
|
43
65
|
const entity = entityBySlug(recordSlug);
|
|
@@ -55,6 +77,7 @@ const name =
|
|
|
55
77
|
|
|
56
78
|
const title = `${name} — ${siteConfig.name}`;
|
|
57
79
|
const description = record.description ?? `${name} on ${siteConfig.name}`;
|
|
80
|
+
const itemSingular = siteConfig.blueprintConfig?.labelSingular ?? record.kind;
|
|
58
81
|
|
|
59
82
|
const repoUrl = proj?.repoUrl ?? (record.links as { github?: string } | undefined)?.github ?? "";
|
|
60
83
|
const homepageUrl = record.links?.website ?? "";
|
|
@@ -96,25 +119,50 @@ if (isProject && proj?.content) {
|
|
|
96
119
|
const contentPath = candidates.find((p) => existsSync(p));
|
|
97
120
|
if (contentPath) {
|
|
98
121
|
try {
|
|
99
|
-
// Cheap markdown → HTML using a minimal renderer would be
|
|
100
|
-
// nice, but we don't want to pull another dep into the
|
|
101
|
-
// template. Render the file as a <pre> for now so the body
|
|
102
|
-
// still surfaces on the page. Consumers can replace this
|
|
103
|
-
// with a real markdown pipeline (`marked`, `remark`, ...)
|
|
104
|
-
// by overriding the `[slug].astro` page.
|
|
105
122
|
const text = readFileSync(contentPath, "utf8");
|
|
106
|
-
contentHtml = text
|
|
107
|
-
.replace(/&/g, "&")
|
|
108
|
-
.replace(/</g, "<")
|
|
109
|
-
.replace(/>/g, ">");
|
|
123
|
+
contentHtml = await marked.parse(text);
|
|
110
124
|
} catch {
|
|
111
125
|
contentHtml = null;
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
|
|
116
|
-
// ──
|
|
117
|
-
|
|
130
|
+
// ── Detail-page decision-signal data (5 sections) ────────────
|
|
131
|
+
// Hoist out of the template body so the JSX stays free of
|
|
132
|
+
// complex TypeScript type assertions.
|
|
133
|
+
type MonthlyCommit = number | { month: string; commits: number };
|
|
134
|
+
type ProjectExtras = {
|
|
135
|
+
github?: {
|
|
136
|
+
activity?: { monthlyCommits?: MonthlyCommit[] };
|
|
137
|
+
files?: Record<string, boolean>;
|
|
138
|
+
languages?: Record<string, number>;
|
|
139
|
+
};
|
|
140
|
+
health?: { status?: string; tier?: string };
|
|
141
|
+
};
|
|
142
|
+
const extras = record as ProjectExtras;
|
|
143
|
+
const monthlyCommits = (extras.github?.activity?.monthlyCommits ?? []).map(
|
|
144
|
+
(it): { month: string; commits: number } =>
|
|
145
|
+
typeof it === "number" ? { month: "", commits: it } : { month: it.month ?? "", commits: it.commits ?? 0 },
|
|
146
|
+
);
|
|
147
|
+
const maxMonthlyCommits = Math.max(1, ...monthlyCommits.map((i) => i.commits));
|
|
148
|
+
const contributionFiles = extras.github?.files ?? {};
|
|
149
|
+
const contributionLabels: Record<string, string> = {
|
|
150
|
+
readme: "README",
|
|
151
|
+
contributing: "Contributing",
|
|
152
|
+
security: "Security",
|
|
153
|
+
issueTemplates: "Issue templates",
|
|
154
|
+
pullRequestTemplate: "PR template",
|
|
155
|
+
};
|
|
156
|
+
const contributionSignals = (Object.keys(contributionLabels) as Array<keyof typeof contributionLabels>)
|
|
157
|
+
.map((k) => ({ key: k, label: contributionLabels[k], ok: contributionFiles[k] }))
|
|
158
|
+
.filter((s) => s.ok !== undefined);
|
|
159
|
+
const languages = extras.github?.languages
|
|
160
|
+
? Object.entries(extras.github.languages).sort((a, b) => b[1] - a[1]).slice(0, 5)
|
|
161
|
+
: [];
|
|
162
|
+
const totalLanguageBytes = Math.max(1, languages.reduce((sum, [, b]) => sum + b, 0));
|
|
163
|
+
|
|
164
|
+
// ── Kind-aware JSON-LD ─────────────────────────────────────────
|
|
165
|
+
let jsonLd: Record<string, unknown>;
|
|
118
166
|
if (isProject && proj) {
|
|
119
167
|
jsonLd = {
|
|
120
168
|
"@context": "https://schema.org",
|
|
@@ -129,6 +177,33 @@ if (isProject && proj) {
|
|
|
129
177
|
? { "@type": "Organization", name: ownerRepo.owner, url: `https://github.com/${ownerRepo.owner}` }
|
|
130
178
|
: undefined,
|
|
131
179
|
};
|
|
180
|
+
} else if (record.kind === "resource") {
|
|
181
|
+
const resourceSchemaType: Record<string, string> = {
|
|
182
|
+
article: "Article",
|
|
183
|
+
book: "Book",
|
|
184
|
+
course: "Course",
|
|
185
|
+
podcast: "PodcastSeries",
|
|
186
|
+
video: "VideoObject",
|
|
187
|
+
};
|
|
188
|
+
jsonLd = {
|
|
189
|
+
"@context": "https://schema.org",
|
|
190
|
+
"@type": resourceSchemaType[record.type] ?? "CreativeWork",
|
|
191
|
+
name,
|
|
192
|
+
description: record.description || undefined,
|
|
193
|
+
url: homepageUrl || undefined,
|
|
194
|
+
author: record.author ? { "@type": "Person", name: record.author } : undefined,
|
|
195
|
+
datePublished: record.publishedAt || undefined,
|
|
196
|
+
};
|
|
197
|
+
} else {
|
|
198
|
+
jsonLd = {
|
|
199
|
+
"@context": "https://schema.org",
|
|
200
|
+
"@type": record.type === "person" ? "Person" : "Organization",
|
|
201
|
+
name,
|
|
202
|
+
description: record.description || undefined,
|
|
203
|
+
url: homepageUrl || undefined,
|
|
204
|
+
foundingDate: record.founded || undefined,
|
|
205
|
+
location: record.location || undefined,
|
|
206
|
+
};
|
|
132
207
|
}
|
|
133
208
|
---
|
|
134
209
|
|
|
@@ -137,7 +212,7 @@ if (isProject && proj) {
|
|
|
137
212
|
description={description}
|
|
138
213
|
site={siteConfig}
|
|
139
214
|
type="article"
|
|
140
|
-
jsonLd={jsonLd
|
|
215
|
+
jsonLd={jsonLd}
|
|
141
216
|
submitHref="/submit"
|
|
142
217
|
submitLabel={`Submit ${name}`}
|
|
143
218
|
>
|
|
@@ -267,45 +342,13 @@ if (isProject && proj) {
|
|
|
267
342
|
)}
|
|
268
343
|
|
|
269
344
|
{/* ── BestFor / WhyListed / Caveats grid ──────────────── */}
|
|
270
|
-
{isProject && (
|
|
271
|
-
<
|
|
272
|
-
{bestFor
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
<ul class="mt-3 m-0 flex list-none flex-col gap-1.5 p-0">
|
|
278
|
-
{bestFor.map((b) => (
|
|
279
|
-
<li class="text-sm text-ink-700 dark:text-ink-200">— {b}</li>
|
|
280
|
-
))}
|
|
281
|
-
</ul>
|
|
282
|
-
</div>
|
|
283
|
-
)}
|
|
284
|
-
{whyListed.length > 0 && (
|
|
285
|
-
<div class="grove-card p-5">
|
|
286
|
-
<h3 class="m-0 text-sm font-semibold uppercase tracking-wider text-ink-500 dark:text-ink-400">
|
|
287
|
-
Why listed
|
|
288
|
-
</h3>
|
|
289
|
-
<ul class="mt-3 m-0 flex list-none flex-col gap-1.5 p-0">
|
|
290
|
-
{whyListed.map((w) => (
|
|
291
|
-
<li class="text-sm text-ink-700 dark:text-ink-200">— {w}</li>
|
|
292
|
-
))}
|
|
293
|
-
</ul>
|
|
294
|
-
</div>
|
|
295
|
-
)}
|
|
296
|
-
{caveats.length > 0 && (
|
|
297
|
-
<div class="grove-card p-5">
|
|
298
|
-
<h3 class="m-0 text-sm font-semibold uppercase tracking-wider text-ink-500 dark:text-ink-400">
|
|
299
|
-
Caveats
|
|
300
|
-
</h3>
|
|
301
|
-
<ul class="mt-3 m-0 flex list-none flex-col gap-1.5 p-0">
|
|
302
|
-
{caveats.map((c) => (
|
|
303
|
-
<li class="text-sm text-ink-700 dark:text-ink-200">— {c}</li>
|
|
304
|
-
))}
|
|
305
|
-
</ul>
|
|
306
|
-
</div>
|
|
307
|
-
)}
|
|
308
|
-
</section>
|
|
345
|
+
{isProject && (
|
|
346
|
+
<CurationGrid
|
|
347
|
+
bestFor={bestFor}
|
|
348
|
+
whyListed={whyListed}
|
|
349
|
+
caveats={caveats}
|
|
350
|
+
class="mt-8"
|
|
351
|
+
/>
|
|
309
352
|
)}
|
|
310
353
|
|
|
311
354
|
{/* ── GitHub signals dl ────────────────────────────────── */}
|
|
@@ -372,6 +415,132 @@ if (isProject && proj) {
|
|
|
372
415
|
</section>
|
|
373
416
|
)}
|
|
374
417
|
|
|
418
|
+
{/* ── Decision signals (3-col) ──────────────────────────── */}
|
|
419
|
+
{isProject && github && (
|
|
420
|
+
<section class="mt-8 grid grid-cols-1 gap-4 lg:grid-cols-3" aria-label="Decision signals">
|
|
421
|
+
{/* Repository health 3-col card */}
|
|
422
|
+
<div class="grove-card p-5">
|
|
423
|
+
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
424
|
+
Repository health
|
|
425
|
+
</h3>
|
|
426
|
+
<dl class="mt-4 grid gap-3 text-sm">
|
|
427
|
+
<div class="flex items-center justify-between gap-3">
|
|
428
|
+
<dt class="text-ink-500 dark:text-ink-400">Status</dt>
|
|
429
|
+
<dd class="font-medium text-ink-900 dark:text-ink-100">
|
|
430
|
+
{healthLabel ?? "—"}
|
|
431
|
+
</dd>
|
|
432
|
+
</div>
|
|
433
|
+
<div class="flex items-center justify-between gap-3">
|
|
434
|
+
<dt class="text-ink-500 dark:text-ink-400">Stars</dt>
|
|
435
|
+
<dd class="font-mono text-ink-900 dark:text-ink-100">
|
|
436
|
+
{starsLabel ?? "—"}
|
|
437
|
+
</dd>
|
|
438
|
+
</div>
|
|
439
|
+
<div class="flex items-center justify-between gap-3">
|
|
440
|
+
<dt class="text-ink-500 dark:text-ink-400">Forks</dt>
|
|
441
|
+
<dd class="font-mono text-ink-900 dark:text-ink-100">
|
|
442
|
+
{forks}
|
|
443
|
+
</dd>
|
|
444
|
+
</div>
|
|
445
|
+
<div class="flex items-center justify-between gap-3">
|
|
446
|
+
<dt class="text-ink-500 dark:text-ink-400">Open issues</dt>
|
|
447
|
+
<dd class="font-mono text-ink-900 dark:text-ink-100">
|
|
448
|
+
{github?.open_issues_count ?? "—"}
|
|
449
|
+
</dd>
|
|
450
|
+
</div>
|
|
451
|
+
{licenseSpdx && (
|
|
452
|
+
<div class="flex items-center justify-between gap-3">
|
|
453
|
+
<dt class="text-ink-500 dark:text-ink-400">License</dt>
|
|
454
|
+
<dd class="font-mono text-ink-900 dark:text-ink-100">{licenseSpdx}</dd>
|
|
455
|
+
</div>
|
|
456
|
+
)}
|
|
457
|
+
</dl>
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
{/* Activity bar chart */}
|
|
461
|
+
<div class="grove-card p-5">
|
|
462
|
+
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
463
|
+
Activity
|
|
464
|
+
</h3>
|
|
465
|
+
{monthlyCommits.length > 0 ? (
|
|
466
|
+
<figure class="mt-4 m-0">
|
|
467
|
+
<div
|
|
468
|
+
class="flex h-28 items-end gap-1.5"
|
|
469
|
+
role="img"
|
|
470
|
+
aria-label="Monthly commits over the last 12 months"
|
|
471
|
+
>
|
|
472
|
+
{monthlyCommits.map((item) => (
|
|
473
|
+
<div class="flex min-w-0 flex-1 flex-col items-center gap-1">
|
|
474
|
+
<div
|
|
475
|
+
class="w-full rounded-sm bg-ink-700 dark:bg-ink-200"
|
|
476
|
+
style={`height: ${Math.max(8, Math.round((item.commits / maxMonthlyCommits) * 96))}px`}
|
|
477
|
+
title={`${item.month || "month"}: ${item.commits} commits`}
|
|
478
|
+
/>
|
|
479
|
+
<span class="max-w-full truncate text-[10px] text-ink-500 dark:text-ink-400">
|
|
480
|
+
{item.month ? item.month.slice(5) : item.commits}
|
|
481
|
+
</span>
|
|
482
|
+
</div>
|
|
483
|
+
))}
|
|
484
|
+
</div>
|
|
485
|
+
<figcaption class="sr-only">
|
|
486
|
+
Monthly commit count over the last 12 months
|
|
487
|
+
</figcaption>
|
|
488
|
+
</figure>
|
|
489
|
+
) : (
|
|
490
|
+
<p class="mt-4 m-0 text-sm text-ink-500 dark:text-ink-400">
|
|
491
|
+
Monthly commit history is not yet populated for this {itemSingular}.
|
|
492
|
+
</p>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
{/* Contribution readiness chips */}
|
|
497
|
+
<div class="grove-card p-5">
|
|
498
|
+
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
499
|
+
Contribution readiness
|
|
500
|
+
</h3>
|
|
501
|
+
{contributionSignals.length > 0 ? (
|
|
502
|
+
<ul class="mt-4 m-0 grid list-none gap-2 p-0 text-sm">
|
|
503
|
+
{contributionSignals.map((signal) => (
|
|
504
|
+
<li class="flex items-center justify-between gap-3">
|
|
505
|
+
<span class="text-ink-700 dark:text-ink-200">{signal.label}</span>
|
|
506
|
+
<span class={signal.ok ? "text-emerald-700 dark:text-emerald-400" : "text-ink-400 dark:text-ink-600"}>
|
|
507
|
+
{signal.ok ? "Found" : "Missing"}
|
|
508
|
+
</span>
|
|
509
|
+
</li>
|
|
510
|
+
))}
|
|
511
|
+
</ul>
|
|
512
|
+
) : (
|
|
513
|
+
<p class="mt-4 m-0 text-sm text-ink-500 dark:text-ink-400">
|
|
514
|
+
Contribution files have not been synced yet.
|
|
515
|
+
</p>
|
|
516
|
+
)}
|
|
517
|
+
</div>
|
|
518
|
+
</section>
|
|
519
|
+
)}
|
|
520
|
+
|
|
521
|
+
{/* ── Language mix ─────────────────────────────────────── */}
|
|
522
|
+
{isProject && languages.length > 0 && (
|
|
523
|
+
<section class="mt-4 grove-card p-5">
|
|
524
|
+
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
525
|
+
Language mix
|
|
526
|
+
</h3>
|
|
527
|
+
<dl class="mt-4 grid gap-2 text-sm">
|
|
528
|
+
{languages.map(([language, bytes]) => {
|
|
529
|
+
const pct = Math.round((bytes / totalLanguageBytes) * 100);
|
|
530
|
+
return (
|
|
531
|
+
<div class="grid grid-cols-[8rem_minmax(0,1fr)_3rem] items-center gap-3">
|
|
532
|
+
<dt class="truncate text-ink-700 dark:text-ink-200">{language}</dt>
|
|
533
|
+
<dd class="h-2 overflow-hidden rounded-sm bg-ink-200 dark:bg-ink-800">
|
|
534
|
+
<div class="h-full bg-ink-700 dark:bg-ink-200" style={`width: ${pct}%`} />
|
|
535
|
+
</dd>
|
|
536
|
+
<dd class="text-right font-mono text-2xs text-ink-500 dark:text-ink-400">{pct}%</dd>
|
|
537
|
+
</div>
|
|
538
|
+
);
|
|
539
|
+
})}
|
|
540
|
+
</dl>
|
|
541
|
+
</section>
|
|
542
|
+
)}
|
|
543
|
+
|
|
375
544
|
{/* ── Stack + Platform chips ───────────────────────────── */}
|
|
376
545
|
{isProject && (stacks.length > 0 || platforms.length > 0) && (
|
|
377
546
|
<section class="mt-8" aria-labelledby="stacks-heading">
|
|
@@ -464,15 +633,16 @@ if (isProject && proj) {
|
|
|
464
633
|
</section>
|
|
465
634
|
)}
|
|
466
635
|
|
|
467
|
-
{/* ── Content markdown
|
|
636
|
+
{/* ── Content markdown ─────────────────────────────────── */}
|
|
468
637
|
{contentHtml && (
|
|
469
638
|
<section class="mt-8" aria-labelledby="content-heading">
|
|
470
639
|
<h2 id="content-heading" class="m-0 text-lg font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
471
640
|
Notes
|
|
472
641
|
</h2>
|
|
473
|
-
<
|
|
474
|
-
class="
|
|
475
|
-
|
|
642
|
+
<div
|
|
643
|
+
class="grove-prose mt-4"
|
|
644
|
+
set:html={contentHtml}
|
|
645
|
+
/>
|
|
476
646
|
</section>
|
|
477
647
|
)}
|
|
478
648
|
</div>
|