@grove-dev/astro 0.6.1 → 0.8.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.
Files changed (111) hide show
  1. package/README.md +77 -5
  2. package/dist/index.d.ts +3 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +22 -33
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/display.d.ts +1 -1
  7. package/dist/lib/display.js +1 -1
  8. package/dist/lib/format.d.ts +1 -1
  9. package/dist/lib/format.js +1 -1
  10. package/dist/lib/icon-kinds.d.ts +1 -1
  11. package/dist/lib/icon-kinds.js +43 -43
  12. package/dist/lib/icon-registry.d.ts +2 -2
  13. package/dist/lib/icon-registry.js +23 -23
  14. package/dist/lib/index.d.ts +9 -9
  15. package/dist/lib/index.d.ts.map +1 -1
  16. package/dist/lib/index.js +9 -9
  17. package/dist/lib/index.js.map +1 -1
  18. package/dist/lib/lenses.d.ts +1 -1
  19. package/dist/lib/lenses.js +1 -1
  20. package/dist/lib/packaged-icons.d.ts +1 -1
  21. package/dist/lib/packaged-icons.js +4 -4
  22. package/dist/lib/repo.d.ts +1 -1
  23. package/dist/lib/repo.js +1 -1
  24. package/dist/lib/search.d.ts +1 -1
  25. package/dist/lib/search.js +1 -1
  26. package/dist/lib/taxonomy-counts.d.ts +1 -1
  27. package/dist/lib/taxonomy-counts.js +1 -1
  28. package/package.json +7 -17
  29. package/src/index.ts +23 -38
  30. package/src/lib/display.ts +1 -1
  31. package/src/lib/format.ts +1 -1
  32. package/src/lib/icon-kinds.ts +44 -44
  33. package/src/lib/icon-registry.ts +25 -25
  34. package/src/lib/index.ts +9 -9
  35. package/src/lib/lenses.ts +1 -1
  36. package/src/lib/packaged-icons.ts +8 -8
  37. package/src/lib/repo.ts +1 -1
  38. package/src/lib/search.ts +1 -1
  39. package/src/lib/taxonomy-counts.ts +1 -1
  40. package/src/server/collections.ts +53 -42
  41. package/src/server/contrast.ts +14 -9
  42. package/src/server/directory.ts +22 -17
  43. package/src/server/index.ts +3 -2
  44. package/src/server/models.ts +404 -213
  45. package/src/server/seo.ts +34 -31
  46. package/src/styles.css +235 -91
  47. package/dist/layouts/index.d.ts +0 -25
  48. package/dist/layouts/index.d.ts.map +0 -1
  49. package/dist/layouts/index.js +0 -25
  50. package/dist/layouts/index.js.map +0 -1
  51. package/dist/ui/button.d.ts +0 -53
  52. package/dist/ui/button.d.ts.map +0 -1
  53. package/dist/ui/button.js +0 -90
  54. package/dist/ui/button.js.map +0 -1
  55. package/src/components/CardGrid.astro +0 -41
  56. package/src/components/CardIcon.astro +0 -67
  57. package/src/components/CategoryGrid.astro +0 -114
  58. package/src/components/CollectionCard.astro +0 -56
  59. package/src/components/CollectionIndex.astro +0 -102
  60. package/src/components/CollectionPage.astro +0 -128
  61. package/src/components/CollectionRow.astro +0 -81
  62. package/src/components/CollectionTeaser.astro +0 -47
  63. package/src/components/ContributorsGrid.astro +0 -143
  64. package/src/components/DirectoryIndexClient.astro +0 -510
  65. package/src/components/EditorialSummary.astro +0 -58
  66. package/src/components/FilterGroupMenu.astro +0 -69
  67. package/src/components/FilterOptions.astro +0 -49
  68. package/src/components/FinalCta.astro +0 -43
  69. package/src/components/Hero.astro +0 -243
  70. package/src/components/Icon.astro +0 -191
  71. package/src/components/IndexRow.astro +0 -37
  72. package/src/components/LanguageBreakdown.astro +0 -67
  73. package/src/components/MarkdownBody.astro +0 -37
  74. package/src/components/OriginalCollection.astro +0 -117
  75. package/src/components/Pagination.astro +0 -50
  76. package/src/components/PoweredBy.astro +0 -60
  77. package/src/components/ProjectCard.astro +0 -321
  78. package/src/components/RecordHeader.astro +0 -221
  79. package/src/components/RecordSection.astro +0 -128
  80. package/src/components/RecordSidebar.astro +0 -387
  81. package/src/components/RefinePanel.astro +0 -168
  82. package/src/components/SmartLensTabs.astro +0 -81
  83. package/src/components/StackGrid.astro +0 -151
  84. package/src/components/StackPlatformChips.astro +0 -73
  85. package/src/components/SubmissionClient.astro +0 -254
  86. package/src/components/TableOfContents.astro +0 -140
  87. package/src/components/WhyThisExists.astro +0 -116
  88. package/src/layouts/BaseLayout.astro +0 -415
  89. package/src/layouts/Container.astro +0 -18
  90. package/src/layouts/Footer.astro +0 -231
  91. package/src/layouts/Header.astro +0 -214
  92. package/src/layouts/SectionHeader.astro +0 -43
  93. package/src/layouts/Seo.astro +0 -171
  94. package/src/layouts/ThemeToggle.astro +0 -118
  95. package/src/layouts/astro-shim.d.ts +0 -29
  96. package/src/layouts/index.ts +0 -24
  97. package/src/server/collections.test.ts +0 -212
  98. package/src/server/contrast.test.ts +0 -82
  99. package/src/server/directory.test.ts +0 -177
  100. package/src/server/github-repo.test.ts +0 -88
  101. package/src/server/models-home.test.ts +0 -101
  102. package/src/server/models.test.ts +0 -135
  103. package/src/server/seo.test.ts +0 -123
  104. package/src/ui/Badge.astro +0 -38
  105. package/src/ui/Button.astro +0 -42
  106. package/src/ui/EmptyState.astro +0 -38
  107. package/src/ui/FilterDrawer.astro +0 -124
  108. package/src/ui/PageHeader.astro +0 -39
  109. package/src/ui/SearchField.astro +0 -106
  110. package/src/ui/button.test.ts +0 -54
  111. package/src/ui/button.ts +0 -107
@@ -1,221 +0,0 @@
1
- ---
2
- /**
3
- * RecordHeader.astro
4
- *
5
- * Compact project-identity header used at the top of every record
6
- * detail page. The header carries:
7
- * - avatar / initials
8
- * - pills row (category + curation labels + "Curated" check +
9
- * optional activity badge)
10
- * - record name + owner/repo line
11
- * - short description
12
- * - GitHub + Homepage CTAs
13
- *
14
- * Tags + activity metadata are surfaced separately (sidebar), so
15
- * the header stays narrow on mobile and doesn't compete with the
16
- * two-column layout below.
17
- *
18
- * Tone classes for the activity badge are static `[data-activity-tone]`
19
- * selectors in `styles.css` — Tailwind purges any classes built from
20
- * dynamic strings, so the package ships the rules statically and
21
- * the badge's `data-activity-tone` attribute picks one at runtime.
22
- */
23
- import type { RecordDetailModel } from "../server/models.js";
24
- import { buttonClass } from "../ui/button.js";
25
-
26
- interface Props {
27
- detail: RecordDetailModel;
28
- }
29
-
30
- const { detail } = Astro.props;
31
- const {
32
- record,
33
- repoUrl,
34
- homepageUrl,
35
- ownerRepo,
36
- avatarSrc,
37
- initials,
38
- curationLabels,
39
- activityBadge,
40
- summary,
41
- sourceDescription,
42
- screenshots,
43
- category,
44
- name,
45
- } = detail;
46
- ---
47
-
48
- <header class="mb-8">
49
- {/* Identity row: logo sized to the name+repo block beside it — no
50
- tall avatar column leaving dead space under the logo. Pills sit
51
- at the row's right edge on wide viewports and wrap below on
52
- mobile. */}
53
- <div class="flex items-center gap-4">
54
- {/* Initials stay in the DOM behind the logo and are revealed by the
55
- image's own `onerror` — a dead logo URL or a renamed GitHub org
56
- would otherwise render the broken-image glyph. */}
57
- {avatarSrc && (
58
- <img
59
- src={avatarSrc}
60
- alt=""
61
- width="56"
62
- height="56"
63
- loading="lazy"
64
- decoding="async"
65
- onerror="this.hidden=true;this.nextElementSibling.hidden=false"
66
- class="h-12 w-12 shrink-0 rounded-lg border border-ink-200 object-contain bg-white p-1 dark:border-ink-800 sm:h-14 sm:w-14"
67
- />
68
- )}
69
- <span
70
- aria-hidden="true"
71
- hidden={Boolean(avatarSrc)}
72
- data-grove-card-fallback-initials
73
- class="inline-flex h-12 w-12 shrink-0 items-center justify-center rounded-lg border border-ink-200 bg-ink-50 text-xl font-semibold text-ink-700 dark:border-ink-800 dark:bg-ink-900 dark:text-ink-200 sm:h-14 sm:w-14"
74
- >
75
- {initials || "·"}
76
- </span>
77
-
78
- <div class="min-w-0 flex-1">
79
- <h1 class="m-0 text-[1.75rem] font-semibold leading-tight tracking-tight text-ink-900 dark:text-ink-100 sm:text-[2rem]">
80
- {name}
81
- </h1>
82
- {ownerRepo && (
83
- <p class="m-0 mt-0.5 text-sm text-ink-500 dark:text-ink-400">
84
- <a
85
- href={`https://github.com/${ownerRepo.owner}`}
86
- target="_blank"
87
- rel="noopener noreferrer"
88
- class="text-inherit no-underline hover:text-ink-900 dark:hover:text-ink-100"
89
- >
90
- @{ownerRepo.owner}
91
- </a>
92
- <span class="mx-1 text-ink-300 dark:text-ink-700">/</span>
93
- <a
94
- href={repoUrl}
95
- target="_blank"
96
- rel="noopener noreferrer"
97
- class="text-inherit no-underline hover:text-ink-900 dark:hover:text-ink-100"
98
- >
99
- {ownerRepo.repo}
100
- </a>
101
- </p>
102
- )}
103
- </div>
104
-
105
- {/* Pills — right-aligned beside the name on wide viewports. */}
106
- <div class="hidden shrink-0 flex-wrap items-center justify-end gap-1.5 md:flex">
107
- {category && (
108
- <span class="rounded-full border border-ink-200 px-2 py-0.5 text-2xs font-medium text-ink-700 dark:border-ink-800 dark:text-ink-300">
109
- {category}
110
- </span>
111
- )}
112
- {curationLabels.map((l) => (
113
- <span class="rounded-full bg-secondary px-2 py-0.5 text-2xs font-medium text-secondary-foreground">
114
- {l}
115
- </span>
116
- ))}
117
- {/* Curated = reviewed provenance, not a success status. */}
118
- {record.curation?.reviewed && (
119
- <span class="inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-2xs font-medium text-secondary-foreground">
120
- <svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" fill="currentColor">
121
- <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0Zm3.41 5.09a.75.75 0 0 0-1.06 0L6.75 8.69 5.65 7.59a.75.75 0 1 0-1.06 1.06l1.65 1.65a.75.75 0 0 0 1.06 0l4.11-4.11a.75.75 0 0 0 0-1.06Z" />
122
- </svg>
123
- Curated
124
- </span>
125
- )}
126
- {activityBadge && (
127
- <span
128
- data-activity-tone={activityBadge.tone}
129
- class="grove-pill grove-pill--activity inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-2xs font-medium"
130
- >
131
- <span class="relative flex h-1.5 w-1.5">
132
- <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-current opacity-60"></span>
133
- <span class="relative inline-flex h-1.5 w-1.5 rounded-full bg-current"></span>
134
- </span>
135
- {activityBadge.label}
136
- </span>
137
- )}
138
- </div>
139
- </div>
140
-
141
- {/* Same pills below the identity row on narrow viewports. */}
142
- <div class="mt-3 flex flex-wrap items-center gap-1.5 md:hidden">
143
- {category && (
144
- <span class="rounded-full border border-ink-200 px-2 py-0.5 text-2xs font-medium text-ink-700 dark:border-ink-800 dark:text-ink-300">
145
- {category}
146
- </span>
147
- )}
148
- {curationLabels.map((l) => (
149
- <span class="rounded-full bg-secondary px-2 py-0.5 text-2xs font-medium text-secondary-foreground">
150
- {l}
151
- </span>
152
- ))}
153
- {record.curation?.reviewed && (
154
- <span class="inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-2xs font-medium text-secondary-foreground">
155
- <svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" fill="currentColor">
156
- <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0Zm3.41 5.09a.75.75 0 0 0-1.06 0L6.75 8.69 5.65 7.59a.75.75 0 1 0-1.06 1.06l1.65 1.65a.75.75 0 0 0 1.06 0l4.11-4.11a.75.75 0 0 0 0-1.06Z" />
157
- </svg>
158
- Curated
159
- </span>
160
- )}
161
- </div>
162
-
163
- {/* Description — full width below the identity row. */}
164
- {summary && (
165
- <p class="m-0 mt-4 max-w-[72ch] text-base leading-relaxed text-ink-700 dark:text-ink-200">
166
- {summary}
167
- </p>
168
- )}
169
- {sourceDescription && sourceDescription !== summary && (
170
- <p class="m-0 mt-2 max-w-[72ch] text-sm leading-relaxed text-ink-500 dark:text-ink-400">
171
- <span class="text-2xs font-semibold uppercase tracking-wider">From the project's README: </span>
172
- {sourceDescription}
173
- </p>
174
- )}
175
-
176
- {/* CTA row — primary actions only. */}
177
- <div class="mt-4 flex flex-wrap items-center gap-2">
178
- {repoUrl && (
179
- <a
180
- href={repoUrl}
181
- target="_blank"
182
- rel="noopener noreferrer"
183
- class={buttonClass("primary", "sm")}
184
- >
185
- View on GitHub →
186
- </a>
187
- )}
188
- {homepageUrl && (
189
- <a
190
- href={homepageUrl}
191
- target="_blank"
192
- rel="noopener noreferrer"
193
- class={buttonClass("secondary", "sm")}
194
- >
195
- Homepage
196
- </a>
197
- )}
198
- </div>
199
- {screenshots && screenshots.length > 0 && (
200
- <div class="mt-6 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3" aria-label="Screenshots">
201
- {screenshots.map((shot) => (
202
- <figure class="m-0 overflow-hidden rounded-[var(--radius-lg)] border border-ink-200 bg-muted dark:border-ink-800">
203
- <img
204
- src={shot.src}
205
- alt={shot.alt}
206
- loading="lazy"
207
- decoding="async"
208
- width={shot.width}
209
- height={shot.height}
210
- class="block h-auto w-full object-cover"
211
- />
212
- {shot.source && (
213
- <figcaption class="px-3 py-1.5 text-2xs text-ink-500 dark:text-ink-400">
214
- Source: <a href={shot.source} target="_blank" rel="noopener noreferrer" class="text-inherit no-underline hover:underline">{shot.source.replace(/^https?:\/\//, "")}</a>
215
- </figcaption>
216
- )}
217
- </figure>
218
- ))}
219
- </div>
220
- )}
221
- </header>
@@ -1,128 +0,0 @@
1
- ---
2
- /**
3
- * RecordSection — generic lens-style section wrapper for the
4
- * home page.
5
- *
6
- * Renders a SectionHeader + a 3-column grid of `ProjectCard`s
7
- * (the v0.5.0 refined card), plus an optional "View all" link
8
- * in the header. Generic: takes any
9
- * `IndexRecord[]` (the slim list-page shape, discriminated union
10
- * over `kind`).
11
- *
12
- * Two equivalent APIs are supported so the same template works
13
- * against both the workspace source and the published
14
- * `@grove-dev/astro@0.2.10` package:
15
- *
16
- * - **`hrefFor(record)`** callback — V1 published API. Caller
17
- * passes a function that returns the detail-page URL for
18
- * each item.
19
- * - **`pathPrefix` string** — convenience API for the common
20
- * case `${pathPrefix}/${item.slug}`. Mutually exclusive with
21
- * `hrefFor`; if both are provided, `hrefFor` wins.
22
- *
23
- * Empty-state copy is rendered when the list has no items, so the
24
- * homepage never collapses to a blank section.
25
- */
26
- import type { IndexRecord } from "@grove-dev/core";
27
- import CardGrid from "./CardGrid.astro";
28
- import ProjectCard from "./ProjectCard.astro";
29
- import Container from "../layouts/Container.astro";
30
- import SectionHeader from "../layouts/SectionHeader.astro";
31
-
32
- interface Props {
33
- /** Used as the section id (so deep links work) and a stable key. */
34
- id: string;
35
- title: string;
36
- description?: string;
37
- items: IndexRecord[];
38
- /** Detail-page href builder (V1 published API). */
39
- hrefFor?: (record: IndexRecord) => string;
40
- /** Detail-page URL prefix (convenience API). */
41
- pathPrefix?: string;
42
- /** Optional "View all" link rendered in the header. */
43
- viewAllHref?: string;
44
- viewAllLabel?: string;
45
- /** Optional eyebrow. */
46
- eyebrow?: string;
47
- /** Empty-state copy. */
48
- emptyLabel?: string;
49
- /** Extra class on the outer <section>. */
50
- class?: string;
51
- /** How many records to render. Default 3 — the refined grid shows one row. */
52
- limit?: number;
53
- }
54
-
55
- const {
56
- id,
57
- title,
58
- description,
59
- items,
60
- hrefFor,
61
- pathPrefix = "/items",
62
- viewAllHref,
63
- viewAllLabel = "View all",
64
- eyebrow,
65
- emptyLabel = "No records yet.",
66
- class: className = "",
67
- limit = 3,
68
- } = Astro.props;
69
-
70
- const visible = items.slice(0, limit);
71
- ---
72
-
73
- <section
74
- id={id}
75
- class={`py-12 sm:py-16 scroll-mt-20 ${className}`}
76
- >
77
- <Container>
78
- <div class="mb-6 flex flex-wrap items-end justify-between gap-3">
79
- <SectionHeader
80
- eyebrow={eyebrow}
81
- title={title}
82
- description={description}
83
- />
84
- {viewAllHref && (
85
- <a
86
- href={viewAllHref}
87
- class="hidden shrink-0 pb-2 text-2xs font-medium text-ink-500 transition-colors hover:text-ink-900 sm:inline dark:text-ink-400 dark:hover:text-ink-100"
88
- >
89
- {viewAllLabel} →
90
- </a>
91
- )}
92
- </div>
93
-
94
- {visible.length === 0 ? (
95
- <p class="m-0 text-sm text-ink-500 dark:text-ink-400">
96
- {emptyLabel}
97
- </p>
98
- ) : (
99
- <CardGrid>
100
- {visible.map((item) => {
101
- // Resolve href: caller-supplied `hrefFor` callback wins;
102
- // otherwise fall back to `${pathPrefix}/${slug}`. The href
103
- // is what each card actually navigates to — getting it
104
- // right is the difference between a working home page
105
- // and a 404-storm of `/items/<slug>` links.
106
- const base = pathPrefix.replace(/\/$/, "");
107
- const fallbackHref = `${base}/${item.slug}`;
108
- const finalHref =
109
- typeof hrefFor === "function" ? hrefFor(item) : fallbackHref;
110
- return (
111
- <li class="m-0 p-0">
112
- <ProjectCard record={item} href={finalHref} />
113
- </li>
114
- );
115
- })}
116
- </CardGrid>
117
- )}
118
-
119
- {viewAllHref && (
120
- <div class="mt-4 sm:hidden">
121
- <a
122
- href={viewAllHref}
123
- class="text-2xs font-medium text-ink-500 transition-colors hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"
124
- >{viewAllLabel} →</a>
125
- </div>
126
- )}
127
- </Container>
128
- </section>