@grove-dev/astro 0.3.2 → 0.3.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grove-dev/astro",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "description": "Composable Astro UI, data adapters, and integration for Grove directories.",
6
6
  "license": "MIT",
@@ -51,13 +51,13 @@
51
51
  "./server": "./src/server/index.ts"
52
52
  },
53
53
  "dependencies": {
54
- "astro": "^6.4.6",
54
+ "astro": "^7.1.3",
55
55
  "marked": "^18.0.0",
56
56
  "sanitize-html": "^2.17.5",
57
- "@grove-dev/core": "0.3.2"
57
+ "@grove-dev/core": "0.3.4"
58
58
  },
59
59
  "peerDependencies": {
60
- "astro": "^6.0.0"
60
+ "astro": "^6.0.0 || ^7.0.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/sanitize-html": "^2.16.0"
@@ -63,8 +63,8 @@ interface Props {
63
63
  */
64
64
  aliases?: Record<string, string>;
65
65
  /**
66
- * Asset ids supplied by a consumer in addition to Grove's bundled registry.
67
- * Unknown stack/platform ids render initials without issuing a failed request.
66
+ * Optional complete asset registry supplied by a consumer. When omitted,
67
+ * Grove attempts the configured asset path and falls back on image error.
68
68
  */
69
69
  available?: string[];
70
70
  /**
@@ -124,25 +124,11 @@ function resolveName(raw: string): string {
124
124
  }
125
125
 
126
126
  const resolved = resolveName(name);
127
- const bundledIcons: Record<"stack" | "platform", Set<string>> = {
128
- stack: new Set([
129
- "android", "apple", "bun", "capacitor", "clojure", "clojurescript",
130
- "dart", "deno", "django", "docker", "firebase", "flutter", "go",
131
- "graphql", "ionic", "java", "javascript", "kotlin", "llm", "mongodb",
132
- "node.js", "nodejs", "python", "rag", "react", "react-native", "rust",
133
- "solidity", "sveltekit", "swift", "tensorflow", "typescript", "vue",
134
- ]),
135
- platform: new Set([
136
- "android", "apple", "chrome", "chromeos", "desktop", "embedded", "ios",
137
- "linux", "macos", "ubuntu", "web", "windows",
138
- ]),
139
- };
140
127
  const availableIcons = available?.map(slugify);
141
128
  const hasAsset =
142
129
  category === "brand" ||
143
- (availableIcons
144
- ? availableIcons.includes(resolved)
145
- : bundledIcons[category].has(resolved));
130
+ availableIcons === undefined ||
131
+ availableIcons.includes(resolved);
146
132
  const effectiveMode: IconMode = resolved === "apple" ? "auto" : mode;
147
133
  const platformStackIcons = new Set(["docker"]);
148
134
  const textOnlyPlatforms = new Set(["self-host", "self-hosted"]);
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import type { IndexRecord } from "@grove-dev/core";
3
3
  import Icon from "./Icon.astro";
4
- import { formatRelative, formatStars, getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl, statusDisplay } from "@grove-dev/astro";
4
+ import { formatRelative, formatStars, getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl, projectStackIds, statusDisplay } from "@grove-dev/astro";
5
5
 
6
6
  interface Props {
7
7
  record: IndexRecord;
@@ -23,7 +23,7 @@ const repoHref = project?.repoUrl ?? item.links.github;
23
23
  const { owner, repo } = getOwnerAndRepoFromRepoUrl(repoHref ?? "");
24
24
  const avatar = project ? project.logoUrl ?? getOwnerAvatarUrl(owner, 80) : undefined;
25
25
  const initials = name.split(/\s+/).map((word) => word[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
26
- const stacks = project ? [project.stack, ...project.stacks].filter(Boolean) : [];
26
+ const stacks = projectStackIds(project);
27
27
  const platforms = project?.platforms ?? [];
28
28
  const stars = project?.github?.stars;
29
29
  const updatedAt = project?.github?.pushedAt ?? (item.kind === "resource" ? item.publishedAt : null);
@@ -25,7 +25,7 @@
25
25
  */
26
26
  import type { IndexRecord, Resource, ProjectRecord, IndexProjectRecord } from "@grove-dev/core";
27
27
  import Icon from "./Icon.astro";
28
- import { getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl } from "@grove-dev/astro";
28
+ import { getOwnerAndRepoFromRepoUrl, getOwnerAvatarUrl, projectStackIds } from "@grove-dev/astro";
29
29
  import { formatStars, formatRelative } from "@grove-dev/astro";
30
30
  import { statusDisplay } from "@grove-dev/astro";
31
31
 
@@ -126,12 +126,7 @@ const platforms = isProject ? (item as { platforms?: string[] }).platforms ?? []
126
126
  const visiblePlatforms = platforms.slice(0, 4);
127
127
  const platformOverflow = platforms.length - visiblePlatforms.length;
128
128
 
129
- const allStacks = isProject
130
- ? [
131
- (item as { stack?: string }).stack,
132
- ...((item as { stacks?: string[] }).stacks ?? []),
133
- ].filter((s): s is string => Boolean(s))
134
- : [];
129
+ const allStacks = isProject ? projectStackIds(item as ProjectRecord) : [];
135
130
  const visibleStacks = allStacks.slice(0, 3);
136
131
  const stackOverflow = allStacks.length - visibleStacks.length;
137
132
 
@@ -24,6 +24,8 @@ interface Props {
24
24
  class?: string;
25
25
  /** Override the visible lens list (defaults to LENSES). */
26
26
  lensIds?: string[];
27
+ /** Consumer noun for the unfiltered lens, e.g. "All apps". */
28
+ allLabel?: string;
27
29
  }
28
30
 
29
31
  const {
@@ -31,6 +33,7 @@ const {
31
33
  pathPrefix = "/",
32
34
  class: className = "",
33
35
  lensIds,
36
+ allLabel,
34
37
  } = Astro.props;
35
38
 
36
39
  // If `lensIds` is provided, filter LENSES to those ids in that order.
@@ -65,7 +68,7 @@ const lenses =
65
68
  data-event="lens_tab_click"
66
69
  data-event-source="smart_lens_tabs"
67
70
  >
68
- {lensDisplay(lens.id)}
71
+ {lens.id === "all" && allLabel ? allLabel : lensDisplay(lens.id)}
69
72
  </a>
70
73
  );
71
74
  })}
@@ -11,8 +11,8 @@
11
11
  * own config (typically `data/generated/site-config.json` for the
12
12
  * showcase).
13
13
  *
14
- * Optional `gaId` adds a GA4 measurement snippet. Omit for
15
- * privacy-first deployments.
14
+ * GA4 can be configured once through `site.analytics.googleAnalyticsId`
15
+ * or overridden per page with `gaId`. Omit both for privacy-first deployments.
16
16
  */
17
17
  import "../styles.css";
18
18
  import Header from "./Header.astro";
@@ -61,6 +61,9 @@ interface Site {
61
61
  };
62
62
  repoUrl?: string;
63
63
  url?: string;
64
+ analytics?: {
65
+ googleAnalyticsId?: string;
66
+ };
64
67
  /** Optional directory-level stats — populated from site-config.json
65
68
  * at generate time. Components use it to render hero counts,
66
69
  * origin card stats, and footer summary lines. */
@@ -103,6 +106,7 @@ const {
103
106
  } = Astro.props;
104
107
  const directoryPath =
105
108
  searchPath ?? `/${site.blueprintConfig?.routeSlug ?? "items"}`;
109
+ const effectiveGaId = gaId ?? site.analytics?.googleAnalyticsId;
106
110
 
107
111
  const favicon =
108
112
  "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%2318181b'/%3E%3C/svg%3E";
@@ -130,14 +134,14 @@ const favicon =
130
134
  title="LLM usage guidance"
131
135
  />
132
136
 
133
- {gaId && (
137
+ {effectiveGaId && (
134
138
  <>
135
139
  <script
136
140
  is:inline
137
141
  async
138
- src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
142
+ src={`https://www.googletagmanager.com/gtag/js?id=${effectiveGaId}`}
139
143
  />
140
- <script is:inline define:vars={{ gaId }}>
144
+ <script is:inline define:vars={{ gaId: effectiveGaId }}>
141
145
  window.dataLayer = window.dataLayer || [];
142
146
  function gtag() {
143
147
  dataLayer.push(arguments);
@@ -290,8 +294,8 @@ const favicon =
290
294
 
291
295
  <slot name="body-end" />
292
296
 
293
- {gaId && (
294
- <script is:inline define:vars={{ gaId }}>
297
+ {effectiveGaId && (
298
+ <script is:inline define:vars={{ gaId: effectiveGaId }}>
295
299
  (function () {
296
300
  // GA4's `gtag('config', id)` call in the <head> already auto-pings
297
301
  // `page_view` on full navigations, so this block only needs to
@@ -19,6 +19,7 @@ import {
19
19
  formatStars,
20
20
  getOwnerAndRepoFromRepoUrl,
21
21
  getOwnerAvatarUrl,
22
+ projectStackIds,
22
23
  statusDisplay,
23
24
  totalPages,
24
25
  } from "@grove-dev/core";
@@ -42,6 +43,9 @@ export interface DirectorySiteConfig {
42
43
  repoUrl?: string;
43
44
  nav?: Array<{ label: string; href: string }>;
44
45
  facets?: string[];
46
+ analytics?: {
47
+ googleAnalyticsId?: string;
48
+ };
45
49
  footer?: {
46
50
  columns?: Array<{
47
51
  heading: string;
@@ -283,7 +287,7 @@ export function getRecordDetailModel(
283
287
  const description = record.description ?? `${name} on ${site.name}`;
284
288
  const repoUrl = proj?.repoUrl ?? record.links?.github ?? "";
285
289
  const homepageUrl = record.links?.website ?? "";
286
- const stacks = proj?.stacks ?? (proj?.stack ? [proj.stack] : []);
290
+ const stacks = projectStackIds(proj);
287
291
  const platforms = proj?.platforms ?? [];
288
292
  const github = proj?.github?.repository;
289
293
  const stars = github?.stargazers_count ?? 0;