@grove-dev/astro 0.5.1 → 0.5.3
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/assets/icons/.grove-icons.json +47 -0
- package/assets/icons/platforms/android.svg +1 -0
- package/assets/icons/platforms/app.svg +4 -0
- package/assets/icons/platforms/chrome.svg +1 -0
- package/assets/icons/platforms/desktop.svg +4 -0
- package/assets/icons/platforms/embedded.svg +5 -0
- package/assets/icons/platforms/ios.svg +1 -0
- package/assets/icons/platforms/linux.svg +1 -0
- package/assets/icons/platforms/macos.svg +1 -0
- package/assets/icons/platforms/ubuntu.svg +1 -0
- package/assets/icons/platforms/web.svg +5 -0
- package/assets/icons/platforms/windows.svg +1 -0
- package/assets/icons/stacks/android.svg +1 -0
- package/assets/icons/stacks/apple.svg +1 -0
- package/assets/icons/stacks/bun.svg +1 -0
- package/assets/icons/stacks/capacitor.svg +1 -0
- package/assets/icons/stacks/clojure.svg +1 -0
- package/assets/icons/stacks/dart.svg +1 -0
- package/assets/icons/stacks/deno.svg +1 -0
- package/assets/icons/stacks/django.svg +1 -0
- package/assets/icons/stacks/docker.svg +1 -0
- package/assets/icons/stacks/firebase.svg +1 -0
- package/assets/icons/stacks/flutter.svg +1 -0
- package/assets/icons/stacks/go.svg +1 -0
- package/assets/icons/stacks/graphql.svg +1 -0
- package/assets/icons/stacks/ionic.svg +1 -0
- package/assets/icons/stacks/java.svg +1 -0
- package/assets/icons/stacks/javascript.svg +1 -0
- package/assets/icons/stacks/kotlin.svg +1 -0
- package/assets/icons/stacks/llm.svg +4 -0
- package/assets/icons/stacks/mongodb.svg +1 -0
- package/assets/icons/stacks/nodejs.svg +1 -0
- package/assets/icons/stacks/python.svg +1 -0
- package/assets/icons/stacks/rag.svg +6 -0
- package/assets/icons/stacks/react-native.svg +1 -0
- package/assets/icons/stacks/react.svg +1 -0
- package/assets/icons/stacks/rust.svg +1 -0
- package/assets/icons/stacks/solidity.svg +1 -0
- package/assets/icons/stacks/sveltekit.svg +1 -0
- package/assets/icons/stacks/swift.svg +1 -0
- package/assets/icons/stacks/tauri.svg +1 -0
- package/assets/icons/stacks/tensorflow.svg +1 -0
- package/assets/icons/stacks/typescript.svg +1 -0
- package/assets/icons/stacks/vue.svg +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/icon-kinds.d.ts +13 -0
- package/dist/lib/icon-kinds.d.ts.map +1 -0
- package/dist/lib/icon-kinds.js +46 -0
- package/dist/lib/icon-kinds.js.map +1 -0
- package/dist/lib/icon-registry.d.ts +40 -0
- package/dist/lib/icon-registry.d.ts.map +1 -0
- package/dist/lib/icon-registry.js +85 -0
- package/dist/lib/icon-registry.js.map +1 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +4 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/packaged-icons.d.ts +9 -0
- package/dist/lib/packaged-icons.d.ts.map +1 -0
- package/dist/lib/packaged-icons.js +21 -0
- package/dist/lib/packaged-icons.js.map +1 -0
- package/package.json +3 -2
- package/src/components/CollectionRow.astro +12 -47
- package/src/components/Icon.astro +74 -152
- package/src/components/IndexRow.astro +7 -25
- package/src/components/ProjectCard.astro +76 -117
- package/src/components/RefinePanel.astro +10 -1
- package/src/components/SmartLensTabs.astro +11 -1
- package/src/index.ts +28 -4
- package/src/layouts/BaseLayout.astro +11 -1
- package/src/layouts/Header.astro +19 -4
- package/src/lib/icon-kinds.ts +59 -0
- package/src/lib/icon-registry.ts +111 -0
- package/src/lib/index.ts +4 -0
- package/src/lib/packaged-icons.ts +32 -0
- package/src/styles.css +61 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon registry resolution — the name → asset lookup shared by
|
|
3
|
+
* `components/Icon.astro` and the icon test suite.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of the component's frontmatter so tests can assert on the
|
|
6
|
+
* exact resolution the component performs instead of reimplementing
|
|
7
|
+
* (and drifting from) the alias and folder rules.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type IconCategory = "stack" | "platform" | "brand";
|
|
11
|
+
export type IconFolder = "stacks" | "platforms" | "brands";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Normalize: lowercase + replace spaces/underscores with dashes.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT `@grove-dev/core`'s `slugify`, which also strips
|
|
17
|
+
* dots and other punctuation. Icon names are file names, so they must
|
|
18
|
+
* survive verbatim apart from case and separator normalization.
|
|
19
|
+
*/
|
|
20
|
+
export function slugifyIconName(s: string): string {
|
|
21
|
+
return s
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.trim()
|
|
24
|
+
.replace(/[\s_]+/g, "-");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Built-in id remaps. Stack-only by design: `category="platform"`
|
|
29
|
+
* resolves straight to the file name so a consumer shipping their own
|
|
30
|
+
* `public/icons/platforms/ios.svg` is never silently bypassed.
|
|
31
|
+
*/
|
|
32
|
+
const defaultAliases: Partial<Record<IconCategory, Record<string, string>>> = {
|
|
33
|
+
stack: {
|
|
34
|
+
ios: "apple",
|
|
35
|
+
"native-ios": "apple",
|
|
36
|
+
ipados: "apple",
|
|
37
|
+
macos: "apple",
|
|
38
|
+
"objective-c": "apple",
|
|
39
|
+
spritekit: "apple",
|
|
40
|
+
swiftui: "apple",
|
|
41
|
+
kmp: "kotlin",
|
|
42
|
+
"kotlin-multiplatform": "kotlin",
|
|
43
|
+
// Names the shipped set covers under a different file.
|
|
44
|
+
"node.js": "nodejs",
|
|
45
|
+
node: "nodejs",
|
|
46
|
+
clojurescript: "clojure",
|
|
47
|
+
svelte: "sveltekit",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Stack marks that `category="platform"` should reuse rather than duplicate. */
|
|
52
|
+
const platformStackIcons = new Set(["docker"]);
|
|
53
|
+
|
|
54
|
+
/** Platforms with no mark at all — always render the initials chip. */
|
|
55
|
+
export const textOnlyPlatforms = new Set(["self-host", "self-hosted"]);
|
|
56
|
+
|
|
57
|
+
export interface ResolvedIconAsset {
|
|
58
|
+
/** Subfolder under the icon base path. */
|
|
59
|
+
folder: IconFolder;
|
|
60
|
+
/** Alias-resolved, slugified file name (no extension). */
|
|
61
|
+
name: string;
|
|
62
|
+
/** `${folder}/${name}` — the key used by the generated kind map. */
|
|
63
|
+
key: string;
|
|
64
|
+
/** True when this name is text-only and must fall back to initials. */
|
|
65
|
+
textOnly: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve a registry id to the asset it points at.
|
|
70
|
+
*
|
|
71
|
+
* Lookup order: `aliases` → the built-in stack aliases → the raw
|
|
72
|
+
* slugified name. Any name not covered by the shipped set simply
|
|
73
|
+
* resolves to `{category-folder}/{slug}`, which is what lets consumers
|
|
74
|
+
* drop their own SVGs in without touching this file.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveIconAsset(
|
|
77
|
+
name: string,
|
|
78
|
+
category: IconCategory,
|
|
79
|
+
aliases?: Record<string, string>,
|
|
80
|
+
): ResolvedIconAsset {
|
|
81
|
+
const resolved = resolveName(name, category, aliases);
|
|
82
|
+
const folder: IconFolder =
|
|
83
|
+
category === "stack" || (category === "platform" && platformStackIcons.has(resolved))
|
|
84
|
+
? "stacks"
|
|
85
|
+
: category === "platform"
|
|
86
|
+
? "platforms"
|
|
87
|
+
: "brands";
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
folder,
|
|
91
|
+
name: resolved,
|
|
92
|
+
key: `${folder}/${resolved}`,
|
|
93
|
+
textOnly: category === "platform" && textOnlyPlatforms.has(resolved),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveName(
|
|
98
|
+
raw: string,
|
|
99
|
+
category: IconCategory,
|
|
100
|
+
aliases?: Record<string, string>,
|
|
101
|
+
): string {
|
|
102
|
+
if (!raw) return "";
|
|
103
|
+
for (const registry of [aliases, defaultAliases[category]]) {
|
|
104
|
+
if (!registry) continue;
|
|
105
|
+
const lower = raw.toLowerCase();
|
|
106
|
+
for (const [k, v] of Object.entries(registry)) {
|
|
107
|
+
if (k.toLowerCase() === lower) return slugifyIconName(v);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return slugifyIconName(raw);
|
|
111
|
+
}
|
package/src/lib/index.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* `lib/search`)
|
|
11
11
|
* - compute taxonomy counts at build time (`lib/taxonomy-counts`)
|
|
12
12
|
* - pretty-print slugs and label/lens/sort/status ids (`lib/display`)
|
|
13
|
+
* - resolve icon registry ids to asset paths (`lib/icon-registry`)
|
|
13
14
|
*
|
|
14
15
|
* All helpers are pure, dependency-free, and typed against
|
|
15
16
|
* `@grove-dev/core` (specifically `ProjectRecord` for the index
|
|
@@ -23,3 +24,6 @@ export * from "./search.js";
|
|
|
23
24
|
export * from "./taxonomy-counts.js";
|
|
24
25
|
export * from "./display.js";
|
|
25
26
|
export * from "./load-collections.js";
|
|
27
|
+
export * from "./icon-registry.js";
|
|
28
|
+
export * from "./icon-kinds.js";
|
|
29
|
+
export * from "./packaged-icons.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate the icon set that ships inside `@grove-dev/astro`.
|
|
3
|
+
*
|
|
4
|
+
* The component and the SVGs it points at live in the same package on
|
|
5
|
+
* purpose: an upgrade that changes how `Icon.astro` resolves a name
|
|
6
|
+
* carries the matching artwork with it, so a consumer can never end up
|
|
7
|
+
* with a component asking for files their site does not have.
|
|
8
|
+
*/
|
|
9
|
+
import { dirname, resolve } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import type { IconSyncOptions, IconSyncResult } from "@grove-dev/core";
|
|
12
|
+
import { syncIconAssets } from "@grove-dev/core";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `packages/astro/assets/icons` — two levels up, which holds for both
|
|
16
|
+
* `src/lib/` (tests, Vite) and `dist/lib/` (published).
|
|
17
|
+
*/
|
|
18
|
+
export const packagedIconsDir = resolve(
|
|
19
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
20
|
+
"..",
|
|
21
|
+
"..",
|
|
22
|
+
"assets",
|
|
23
|
+
"icons",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/** Copy the packaged icon set into a site's `public/`. */
|
|
27
|
+
export function syncPackagedIcons(
|
|
28
|
+
publicDir: string,
|
|
29
|
+
options?: IconSyncOptions,
|
|
30
|
+
): Promise<IconSyncResult> {
|
|
31
|
+
return syncIconAssets(packagedIconsDir, publicDir, options);
|
|
32
|
+
}
|
package/src/styles.css
CHANGED
|
@@ -424,6 +424,67 @@
|
|
|
424
424
|
* Colors come from the package's `ink-*` scale via CSS variables so
|
|
425
425
|
* every consumer gets the same baseline regardless of brand overrides.
|
|
426
426
|
* ────────────────────────────────────────────────────────────────── */
|
|
427
|
+
|
|
428
|
+
/* ──────────────────────────────────────────────────────────────────
|
|
429
|
+
* Monochrome brand marks (`Icon.astro`, kind: "mono")
|
|
430
|
+
*
|
|
431
|
+
* An SVG loaded through `<img src>` is an independent document that
|
|
432
|
+
* page CSS cannot reach, so `currentColor` inside it never resolves
|
|
433
|
+
* against the theme — it silently renders black in both modes. That
|
|
434
|
+
* is why single-color marks (Apple, Rust, Tauri, Deno, …) used to
|
|
435
|
+
* need a hand-maintained `-light`/`-dark` file pair plus a JS `src`
|
|
436
|
+
* swap, and why the pair was easy to get backwards.
|
|
437
|
+
*
|
|
438
|
+
* Masking inverts the relationship: the SVG supplies the *shape*
|
|
439
|
+
* (via its alpha channel) and the page supplies the *color* — no
|
|
440
|
+
* script and no second file.
|
|
441
|
+
*
|
|
442
|
+
* The paint is `--grove-foreground`, NOT `currentColor`. These are
|
|
443
|
+
* brand marks, and a brand mark's real presentation is solid black on
|
|
444
|
+
* light and solid white on dark — not whatever tint the surrounding
|
|
445
|
+
* chip label happens to use. Inheriting `currentColor` washed the
|
|
446
|
+
* Apple mark down to the chip's muted `ink-500`, which reads as a
|
|
447
|
+
* greyed-out logo rather than a logo. Callers that genuinely want a
|
|
448
|
+
* tinted mark can override `--grove-icon-mono`.
|
|
449
|
+
*
|
|
450
|
+
* The URL is per-icon and computed at render time, so it can never
|
|
451
|
+
* appear literally in a source file for Tailwind's extractor to find
|
|
452
|
+
* — arbitrary `mask-[url(…)]` utilities are structurally impossible
|
|
453
|
+
* here. Everything static lives in this class; only the URL travels
|
|
454
|
+
* inline, as `--grove-icon-url`.
|
|
455
|
+
* ────────────────────────────────────────────────────────────────── */
|
|
456
|
+
.grove-icon-mask {
|
|
457
|
+
display: inline-block;
|
|
458
|
+
flex-shrink: 0;
|
|
459
|
+
vertical-align: middle;
|
|
460
|
+
background-color: var(--grove-icon-mono, var(--grove-foreground));
|
|
461
|
+
-webkit-mask-image: var(--grove-icon-url);
|
|
462
|
+
mask-image: var(--grove-icon-url);
|
|
463
|
+
-webkit-mask-size: contain;
|
|
464
|
+
mask-size: contain;
|
|
465
|
+
-webkit-mask-repeat: no-repeat;
|
|
466
|
+
mask-repeat: no-repeat;
|
|
467
|
+
-webkit-mask-position: center;
|
|
468
|
+
mask-position: center;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* Print stylesheets drop background colors, which would erase the
|
|
472
|
+
* glyph entirely — an `<img>` would still print. */
|
|
473
|
+
@media print {
|
|
474
|
+
.grove-icon-mask {
|
|
475
|
+
-webkit-print-color-adjust: exact;
|
|
476
|
+
print-color-adjust: exact;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* Forced-colors mode overrides `background-color`; pin it to the
|
|
481
|
+
* system text color so the mark stays visible. */
|
|
482
|
+
@media (forced-colors: active) {
|
|
483
|
+
.grove-icon-mask {
|
|
484
|
+
background-color: CanvasText;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
427
488
|
.grove-prose {
|
|
428
489
|
font-size: 1.0625rem; /* 17px */
|
|
429
490
|
line-height: 1.75;
|