@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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
declare module 'astro:content' {
|
|
2
|
+
export interface RenderResult {
|
|
3
|
+
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
|
4
|
+
headings: import('astro').MarkdownHeading[];
|
|
5
|
+
remarkPluginFrontmatter: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
interface Render {
|
|
8
|
+
'.md': Promise<RenderResult>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RenderedContent {
|
|
12
|
+
html: string;
|
|
13
|
+
metadata?: {
|
|
14
|
+
imagePaths: Array<string>;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
|
20
|
+
|
|
21
|
+
export type CollectionKey = keyof DataEntryMap;
|
|
22
|
+
export type CollectionEntry<C extends CollectionKey> = Flatten<DataEntryMap[C]>;
|
|
23
|
+
|
|
24
|
+
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
|
25
|
+
|
|
26
|
+
export type ReferenceDataEntry<
|
|
27
|
+
C extends CollectionKey,
|
|
28
|
+
E extends keyof DataEntryMap[C] = string,
|
|
29
|
+
> = {
|
|
30
|
+
collection: C;
|
|
31
|
+
id: E;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
|
35
|
+
collection: C;
|
|
36
|
+
id: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function getCollection<C extends keyof DataEntryMap, E extends CollectionEntry<C>>(
|
|
40
|
+
collection: C,
|
|
41
|
+
filter?: (entry: CollectionEntry<C>) => entry is E,
|
|
42
|
+
): Promise<E[]>;
|
|
43
|
+
export function getCollection<C extends keyof DataEntryMap>(
|
|
44
|
+
collection: C,
|
|
45
|
+
filter?: (entry: CollectionEntry<C>) => unknown,
|
|
46
|
+
): Promise<CollectionEntry<C>[]>;
|
|
47
|
+
|
|
48
|
+
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
|
49
|
+
collection: C,
|
|
50
|
+
filter?: LiveLoaderCollectionFilterType<C>,
|
|
51
|
+
): Promise<
|
|
52
|
+
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
export function getEntry<
|
|
56
|
+
C extends keyof DataEntryMap,
|
|
57
|
+
E extends keyof DataEntryMap[C] | (string & {}),
|
|
58
|
+
>(
|
|
59
|
+
entry: ReferenceDataEntry<C, E>,
|
|
60
|
+
): E extends keyof DataEntryMap[C]
|
|
61
|
+
? Promise<DataEntryMap[C][E]>
|
|
62
|
+
: Promise<CollectionEntry<C> | undefined>;
|
|
63
|
+
export function getEntry<
|
|
64
|
+
C extends keyof DataEntryMap,
|
|
65
|
+
E extends keyof DataEntryMap[C] | (string & {}),
|
|
66
|
+
>(
|
|
67
|
+
collection: C,
|
|
68
|
+
id: E,
|
|
69
|
+
): E extends keyof DataEntryMap[C]
|
|
70
|
+
? string extends keyof DataEntryMap[C]
|
|
71
|
+
? Promise<DataEntryMap[C][E]> | undefined
|
|
72
|
+
: Promise<DataEntryMap[C][E]>
|
|
73
|
+
: Promise<CollectionEntry<C> | undefined>;
|
|
74
|
+
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
|
75
|
+
collection: C,
|
|
76
|
+
filter: string | LiveLoaderEntryFilterType<C>,
|
|
77
|
+
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
|
78
|
+
|
|
79
|
+
/** Resolve an array of entry references from the same collection */
|
|
80
|
+
export function getEntries<C extends keyof DataEntryMap>(
|
|
81
|
+
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
|
82
|
+
): Promise<CollectionEntry<C>[]>;
|
|
83
|
+
|
|
84
|
+
export function render<C extends keyof DataEntryMap>(
|
|
85
|
+
entry: DataEntryMap[C][string],
|
|
86
|
+
): Promise<RenderResult>;
|
|
87
|
+
|
|
88
|
+
export function reference<
|
|
89
|
+
C extends
|
|
90
|
+
| keyof DataEntryMap
|
|
91
|
+
// Allow generic `string` to avoid excessive type errors in the config
|
|
92
|
+
// if `dev` is not running to update as you edit.
|
|
93
|
+
// Invalid collection names will be caught at build time.
|
|
94
|
+
| (string & {}),
|
|
95
|
+
>(
|
|
96
|
+
collection: C,
|
|
97
|
+
): import('astro/zod').ZodPipe<
|
|
98
|
+
import('astro/zod').ZodString,
|
|
99
|
+
import('astro/zod').ZodTransform<
|
|
100
|
+
C extends keyof DataEntryMap
|
|
101
|
+
? {
|
|
102
|
+
collection: C;
|
|
103
|
+
id: string;
|
|
104
|
+
}
|
|
105
|
+
: never,
|
|
106
|
+
string
|
|
107
|
+
>
|
|
108
|
+
>;
|
|
109
|
+
|
|
110
|
+
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
|
111
|
+
type InferEntrySchema<C extends keyof DataEntryMap> = import('astro/zod').infer<
|
|
112
|
+
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
|
113
|
+
>;
|
|
114
|
+
type ExtractLoaderConfig<T> = T extends { loader: infer L } ? L : never;
|
|
115
|
+
type InferLoaderSchema<
|
|
116
|
+
C extends keyof DataEntryMap,
|
|
117
|
+
L = ExtractLoaderConfig<ContentConfig['collections'][C]>,
|
|
118
|
+
> = L extends { schema: import('astro/zod').ZodSchema }
|
|
119
|
+
? import('astro/zod').infer<L['schema']>
|
|
120
|
+
: any;
|
|
121
|
+
|
|
122
|
+
type DataEntryMap = {
|
|
123
|
+
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
|
127
|
+
infer TData,
|
|
128
|
+
infer TEntryFilter,
|
|
129
|
+
infer TCollectionFilter,
|
|
130
|
+
infer TError
|
|
131
|
+
>
|
|
132
|
+
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
|
133
|
+
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
|
134
|
+
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
|
135
|
+
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
|
136
|
+
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
|
137
|
+
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
|
138
|
+
|
|
139
|
+
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
|
140
|
+
LiveContentConfig['collections'][C]['schema'] extends undefined
|
|
141
|
+
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
|
142
|
+
: import('astro/zod').infer<
|
|
143
|
+
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
|
144
|
+
>;
|
|
145
|
+
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
|
146
|
+
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
|
147
|
+
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
|
148
|
+
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
|
149
|
+
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
|
150
|
+
LiveContentConfig['collections'][C]['loader']
|
|
151
|
+
>;
|
|
152
|
+
|
|
153
|
+
export type ContentConfig = never;
|
|
154
|
+
export type LiveContentConfig = never;
|
|
155
|
+
}
|
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { defineConfig } from 'astro/config';
|
|
4
4
|
import tailwindcss from '@tailwindcss/vite';
|
|
5
|
+
import groveAstro from '@grove-dev/astro';
|
|
5
6
|
|
|
6
7
|
// `site` is the canonical URL the build uses for absolute links
|
|
7
|
-
// (sitemap, OpenGraph, canonical tags, JSON-LD).
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// (sitemap, OpenGraph, canonical tags, JSON-LD). The CLI injects
|
|
9
|
+
// `grove.config.ts` → `site.url` at scaffold time, but it can also
|
|
10
|
+
// be overridden per-build via the `SITE_URL` env var.
|
|
10
11
|
//
|
|
11
|
-
// The sitemap
|
|
12
|
-
// `
|
|
13
|
-
//
|
|
12
|
+
// The sitemap is generated by the CLI's `grove sitemap` command
|
|
13
|
+
// into `public/sitemap.xml`; the public URL is exactly `/sitemap.xml`
|
|
14
|
+
// (no `-index.xml` suffix).
|
|
14
15
|
export default defineConfig({
|
|
15
16
|
site: process.env.SITE_URL || 'https://example.com',
|
|
16
17
|
trailingSlash: 'ignore',
|
|
18
|
+
integrations: [groveAstro()],
|
|
17
19
|
vite: {
|
|
18
20
|
plugins: [tailwindcss()],
|
|
19
21
|
},
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "About"
|
|
3
|
+
description: "What this directory is, who it's for, and how it works."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# About this directory
|
|
7
|
+
|
|
8
|
+
This site is a small, hand-curated index of useful open-source work
|
|
9
|
+
relevant to a specific community. The framing is deliberately
|
|
10
|
+
narrow: we list *one* project per record, with a hand-written
|
|
11
|
+
"why listed" note for each, and we surface the things that matter
|
|
12
|
+
in practice (recent activity, install friction, license, etc.)
|
|
13
|
+
rather than the things that look impressive on a GitHub profile.
|
|
14
|
+
|
|
15
|
+
## How it works
|
|
16
|
+
|
|
17
|
+
Records live as YAML files in `data/records/`. Each record carries:
|
|
18
|
+
|
|
19
|
+
- A `kind` (`project`, `resource`, or `entity` — picked by the
|
|
20
|
+
blueprint in `grove.config.ts`).
|
|
21
|
+
- A `slug` (URL-safe identifier; becomes the path on
|
|
22
|
+
`/<kind>/<slug>`).
|
|
23
|
+
- A `name` and a one-line `description`.
|
|
24
|
+
- Cross-links (`links.github`, `links.website`, etc.) and free-form
|
|
25
|
+
tags.
|
|
26
|
+
- An optional `curation` block with `labels`, `reviewedAt`,
|
|
27
|
+
`bestFor`, `whyListed`, `caveats`, and `scores`.
|
|
28
|
+
- An optional `github` block with the live repository signals
|
|
29
|
+
(stars, forks, latest release, languages, pushed-at).
|
|
30
|
+
|
|
31
|
+
At build time:
|
|
32
|
+
|
|
33
|
+
1. `grove validate` checks every record against the JSON schema.
|
|
34
|
+
2. `grove generate` writes `data/generated/records.{full,index}.json`
|
|
35
|
+
and `data/generated/site-config.json`.
|
|
36
|
+
3. `grove sitemap` writes `public/sitemap.xml`.
|
|
37
|
+
4. `grove llms` writes `public/llms.txt` and `llms-full.txt`.
|
|
38
|
+
5. `astro build` produces the static output.
|
|
39
|
+
|
|
40
|
+
The whole pipeline is reproducible. The records are the source of
|
|
41
|
+
truth; everything else is derived.
|
|
42
|
+
|
|
43
|
+
## Why a static site
|
|
44
|
+
|
|
45
|
+
The directory is a *reference*, not an *app*. Static HTML is the
|
|
46
|
+
right tool:
|
|
47
|
+
|
|
48
|
+
- Every page is a single round-trip. No client-side data layer.
|
|
49
|
+
- The output is auditable: open the deployed HTML and you can see
|
|
50
|
+
the full markup, no hidden hydration.
|
|
51
|
+
- Hosting is cheap (or free, on GitHub Pages / Cloudflare Pages).
|
|
52
|
+
- AI crawlers can read the full content. The `llms.txt` /
|
|
53
|
+
`llms-full.txt` files give them a single fetch to land on.
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
To add a record, open a submission issue. The maintainer will
|
|
58
|
+
review and, if accepted, open a PR that adds the new YAML file.
|
|
59
|
+
See `/submit` for the issue form and a minimal record template.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
The site's source code is MIT. The records are released under CC-BY
|
|
64
|
+
4.0 unless a record's `license` field says otherwise. A record's
|
|
65
|
+
`license` field is the license of the *project it describes*, not
|
|
66
|
+
the license of the directory entry.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Methodology"
|
|
3
|
+
description: "How records are picked, scored, and surfaced in this directory."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Methodology
|
|
7
|
+
|
|
8
|
+
This page describes how entries in the directory are chosen, how the
|
|
9
|
+
curation signals are computed, and what we do (and don't) promise about
|
|
10
|
+
the data. The intent is to make the directory's logic auditable — every
|
|
11
|
+
number on a detail page is reproducible from the record's YAML.
|
|
12
|
+
|
|
13
|
+
## Selection
|
|
14
|
+
|
|
15
|
+
A record is included if it is:
|
|
16
|
+
|
|
17
|
+
1. **Open source.** Source is public under an OSI-approved license
|
|
18
|
+
(or public-domain dedication).
|
|
19
|
+
2. **Actively maintained.** A pushed commit within the last 18 months
|
|
20
|
+
OR a declared maintenance status of `maintained` / `passive`.
|
|
21
|
+
3. **Useful to a curious developer.** A working build, a real user
|
|
22
|
+
base (or a credible path to one), and a clear description of what
|
|
23
|
+
the project does.
|
|
24
|
+
|
|
25
|
+
We exclude: abandoned experiments, vapourware, projects whose entire
|
|
26
|
+
purpose is to sell a hosted plan, and any record that has not been
|
|
27
|
+
hand-checked by a maintainer.
|
|
28
|
+
|
|
29
|
+
## Curation labels
|
|
30
|
+
|
|
31
|
+
The `curation.labels` field is a small, hand-set list. The supported
|
|
32
|
+
labels are:
|
|
33
|
+
|
|
34
|
+
- `hot` — high recent activity or attention (≥ 1 000 stars or a
|
|
35
|
+
commit within the last 30 days).
|
|
36
|
+
- `new` — added to the directory in the last 7 days.
|
|
37
|
+
- `mature` — long-running curated project, pushed > 365 days ago,
|
|
38
|
+
but still maintained.
|
|
39
|
+
- `featured` — editor's pick for the front page.
|
|
40
|
+
- `needs-review` — open submission awaiting a second maintainer
|
|
41
|
+
look.
|
|
42
|
+
|
|
43
|
+
These labels are mutually additive, not exclusive. A `new` record
|
|
44
|
+
can also be `hot`; a `mature` record can also be `featured`.
|
|
45
|
+
|
|
46
|
+
## Scoring
|
|
47
|
+
|
|
48
|
+
The detail page surfaces a five-dimension score block. The five
|
|
49
|
+
dimensions are:
|
|
50
|
+
|
|
51
|
+
- **Documentation** — is the README / docs site enough to onboard
|
|
52
|
+
without reading the source?
|
|
53
|
+
- **Onboarding** — install in < 5 min, first useful result in < 1 h.
|
|
54
|
+
- **Maintenance** — recent commits, issue close rate, release cadence.
|
|
55
|
+
- **Community** — issues answered, discussions active, contributors
|
|
56
|
+
per release.
|
|
57
|
+
- **Production-readiness** — CI, tests, security policy, releases
|
|
58
|
+
with semver.
|
|
59
|
+
|
|
60
|
+
Each dimension is scored 0-5. The overall score on the detail page
|
|
61
|
+
is the mean. Scores are stored on the record, not computed at build
|
|
62
|
+
time, so we can deliberately disagree with the raw signals (a 1-line
|
|
63
|
+
toy that happens to be wildly popular can still score low on
|
|
64
|
+
documentation).
|
|
65
|
+
|
|
66
|
+
## Health signals
|
|
67
|
+
|
|
68
|
+
The `health` block on each record tracks four state machines:
|
|
69
|
+
|
|
70
|
+
- `status` — `healthy` / `slowing` / `stale` / `archived`.
|
|
71
|
+
- `tier` — `curated` / `community` / `experimental`.
|
|
72
|
+
- `issue_close_rate` — moving 90-day average.
|
|
73
|
+
- `last_release` — most recent semver tag.
|
|
74
|
+
|
|
75
|
+
A record's `status` transitions are time-boxed: a project with no
|
|
76
|
+
commits in 365 days flips from `slowing` to `stale`; 730 days flips
|
|
77
|
+
to `archived`. The transition is mechanical; the recovery is
|
|
78
|
+
manual (a maintainer sets `tier` back to `community` after review).
|
|
79
|
+
|
|
80
|
+
## Lenses
|
|
81
|
+
|
|
82
|
+
The directory's home page surfaces three curated lenses:
|
|
83
|
+
|
|
84
|
+
- **Hot** — `curation.labels` includes `hot`, OR `github.stars` ≥
|
|
85
|
+
1 000, OR `pushedAt` within 30 days.
|
|
86
|
+
- **New** — `pushedAt` within 7 days, OR `curation.labels` includes
|
|
87
|
+
`new`.
|
|
88
|
+
- **Mature** — `health.tier === "curated"` AND `pushedAt` > 365 days
|
|
89
|
+
ago.
|
|
90
|
+
|
|
91
|
+
Each lens caps at 6 items on the home page; the full filtered list
|
|
92
|
+
is reachable via the lens tab on `/projects`.
|
|
93
|
+
|
|
94
|
+
## What we don't promise
|
|
95
|
+
|
|
96
|
+
- **Real-time data.** The site is rebuilt on a schedule, not on
|
|
97
|
+
every push. Counts and dates can lag by up to one build cycle.
|
|
98
|
+
- **Verdict-free scores.** A high overall score means "useful to a
|
|
99
|
+
curious developer", not "best in class" — judgement calls are
|
|
100
|
+
documented per record, not baked into the score.
|
|
101
|
+
- **Causal claims.** The "best for" / "why listed" / "caveats"
|
|
102
|
+
blocks are editorial notes, not derived signals. They reflect
|
|
103
|
+
what a maintainer thought at review time.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"blueprint": "project-directory",
|
|
3
|
+
"name": "Grove Directory",
|
|
4
|
+
"tagline": "A curated, health-aware developer directory.",
|
|
5
|
+
"description": "A curated, health-aware developer directory powered by Grove.",
|
|
6
|
+
"siteUrl": "https://example.com",
|
|
7
|
+
"repoUrl": "https://github.com/tortuvshin/grove",
|
|
8
|
+
"nav": [
|
|
9
|
+
{
|
|
10
|
+
"label": "Home",
|
|
11
|
+
"href": "/"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"label": "Browse",
|
|
15
|
+
"href": "/projects"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"label": "About",
|
|
19
|
+
"href": "/about"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"theme": {
|
|
23
|
+
"primaryColor": "#16a34a",
|
|
24
|
+
"radius": "soft",
|
|
25
|
+
"density": "comfortable",
|
|
26
|
+
"containerWidth": "72rem"
|
|
27
|
+
},
|
|
28
|
+
"integrations": {
|
|
29
|
+
"github": false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@theme{ --color-ink-50: #fafafa; --color-ink-100: #f4f4f5; --color-ink-200: #e4e4e7; --color-ink-300: #d4d4d8; --color-ink-400: #a1a1aa; --color-ink-500: #71717a; --color-ink-600: #52525b; --color-ink-700: #3f3f46; --color-ink-800: #27272a; --color-ink-900: #18181b; --color-ink-950: #09090b; --color-accent: #1f6feb; --color-accent-muted: #388bfd1a; --color-emerald-50: #ecfdf5; --color-emerald-300: #6ee7b7; --color-emerald-400: #34d399; --color-emerald-700: #047857; --color-emerald-800: #065f46; --color-emerald-950: #022c22; --color-orange-50: #fff7ed; --color-orange-300: #fdba74; --color-orange-400: #fb923c; --color-orange-700: #c2410c; --color-orange-800: #9a3412; --color-orange-950: #431407; --color-blue-50: #eff6ff; --color-blue-300: #93c5fd; --color-blue-400: #60a5fa; --color-blue-700: #1d4ed8; --color-blue-800: #1e40af; --color-blue-950: #172554; --color-amber-50: #fffbeb; --color-amber-300: #fcd34d; --color-amber-400: #fbbf24; --color-amber-700: #b45309; --color-amber-800: #92400e; --color-amber-950: #451a03; --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --text-2xs: .6875rem; --text-2xs--line-height: 1rem; --container-container: 1140px; --container-wide: 1400px; --container-narrow: 720px; --radius: .375rem; --radius-lg: .5rem; --radius-xl: .75rem; --animate-fade-in-up: fade-in-up .6s ease-out both; --animate-fade-in: fade-in .8s ease-out both; --animate-gradient-shift: gradient-shift 8s ease-in-out infinite; --animate-subtle-bob: subtle-bob 3s ease-in-out infinite; }@keyframes fade-in-up{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes gradient-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}@keyframes subtle-bob{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}@layer base{:root{color-scheme:light dark}html{-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;background:#fff;color:var(--color-ink-900);font-family:var(--font-sans);font-size:15px;line-height:1.55}body.dark{background:var(--color-ink-950);color:var(--color-ink-100)}a{color:inherit;text-decoration:none}::selection{background:var(--color-ink-200);color:var(--color-ink-900)}.dark ::selection{background:var(--color-ink-800);color:var(--color-ink-100)}*{scrollbar-width:thin;scrollbar-color:var(--color-ink-300) transparent}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-thumb{background:var(--color-ink-200);border-radius:4px}.dark *::-webkit-scrollbar-thumb{background:var(--color-ink-800)}input,select,textarea,button{font:inherit}}@layer components{.grove-section{padding-top:3rem;padding-bottom:3rem}@media(min-width:640px){.grove-section{padding-top:4rem;padding-bottom:4rem}}.grove-section-tight{padding-top:2.5rem;padding-bottom:2.5rem}@media(min-width:640px){.grove-section-tight{padding-top:3rem;padding-bottom:3rem}}.grove-card{border:1px solid var(--color-ink-200);background:#fff;border-radius:var(--radius-lg);transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.dark .grove-card{border-color:var(--color-ink-800);background:var(--color-ink-950)}.grove-card-hover:hover{border-color:var(--color-ink-300);background:#f4f4f599;box-shadow:0 1px 2px #0000000a,0 4px 12px -2px #0000000f}.dark .grove-card-hover:hover{border-color:var(--color-ink-700);background:#18181b66;box-shadow:0 1px 2px #0006,0 4px 12px -2px #0000004d}.grove-badge{display:inline-flex;align-items:center;gap:.25rem;border-radius:.25rem;border:1px solid var(--color-ink-200);background:var(--color-ink-50);padding:.125rem .375rem;font-size:var(--text-2xs);line-height:var(--text-2xs--line-height);font-weight:500;color:var(--color-ink-700)}.dark .grove-badge{border-color:var(--color-ink-800);background:#18181b99;color:var(--color-ink-300)}.grove-badge-accent{border-color:var(--color-ink-900);background:var(--color-ink-900);color:#fff}.dark .grove-badge-accent{border-color:var(--color-ink-100);background:var(--color-ink-100);color:var(--color-ink-900)}.grove-badge-new{border-color:#6ee7b799;background:var(--color-emerald-50);color:var(--color-emerald-800)}.dark .grove-badge-new{border-color:#065f4699;background:#022c2266;color:var(--color-emerald-300)}.grove-badge-hot{border-color:#fdba7499;background:var(--color-orange-50);color:var(--color-orange-800)}.dark .grove-badge-hot{border-color:#9a341299;background:#43140766;color:var(--color-orange-300)}.grove-badge-mature{border-color:#93c5fd99;background:var(--color-blue-50);color:var(--color-blue-800)}.dark .grove-badge-mature{border-color:#1e40af99;background:#17255466;color:var(--color-blue-300)}.grove-badge-stale{border-color:#fcd34d99;background:var(--color-amber-50);color:var(--color-amber-800)}.dark .grove-badge-stale{border-color:#92400e99;background:#451a0366;color:var(--color-amber-300)}.grove-btn{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;border-radius:var(--radius);border:1px solid transparent;padding:.375rem .75rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition:color .15s ease,background-color .15s ease,border-color .15s ease;text-decoration:none;cursor:pointer}.grove-btn-ghost{color:var(--color-ink-700)}.grove-btn-ghost:hover{background:var(--color-ink-100)}.dark .grove-btn-ghost{color:var(--color-ink-300)}.dark .grove-btn-ghost:hover{background:var(--color-ink-900)}.grove-btn-outline{border-color:var(--color-ink-200);background:#fff;color:var(--color-ink-900)}.grove-btn-outline:hover{border-color:var(--color-ink-300);background:var(--color-ink-50)}.dark .grove-btn-outline{border-color:var(--color-ink-800);background:var(--color-ink-950);color:var(--color-ink-100)}.dark .grove-btn-outline:hover{border-color:var(--color-ink-700);background:#18181b66}.grove-btn-primary{border-color:var(--color-ink-900);background:var(--color-ink-900);color:#fff}.grove-btn-primary:hover{background:var(--color-ink-800)}.dark .grove-btn-primary{border-color:var(--color-ink-100);background:var(--color-ink-100);color:var(--color-ink-900)}.dark .grove-btn-primary:hover{background:#fff}}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="Astro v6.4.5"><meta name="theme-color" content="#0a0a0c" media="(prefers-color-scheme: dark)"><meta name="theme-color" content="#fafafa" media="(prefers-color-scheme: light)"><link rel="icon" href="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"><link rel="sitemap" href="/sitemap.xml"><link rel="alternate" type="text/plain" href="/llms.txt" title="LLM usage guidance"><!-- Primary meta --><title>About — Grove Directory</title><meta name="description" content="About Grove Directory — A curated, health-aware developer directory."><link rel="canonical" href="https://example.com/about/"><!-- Open Graph --><meta property="og:type" content="website"><meta property="og:url" content="https://example.com/about/"><meta property="og:title" content="About — Grove Directory"><meta property="og:description" content="About Grove Directory — A curated, health-aware developer directory."><meta property="og:image" content="https://example.com/og-image.svg"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta property="og:image:alt" content="Grove Directory — A curated, health-aware developer directory powered by Grove."><meta property="og:site_name" content="Grove Directory"><meta property="og:locale" content="en"><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="About — Grove Directory"><meta name="twitter:description" content="About Grove Directory — A curated, health-aware developer directory."><meta name="twitter:image" content="https://example.com/og-image.svg"><meta name="twitter:image:alt" content="Grove Directory — A curated, health-aware developer directory powered by Grove."><!-- JSON-LD: site-wide identity --><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"Grove Directory","description":"A curated, health-aware developer directory powered by Grove.","url":"https://example.com","inLanguage":"en","potentialAction":{"@type":"SearchAction","target":"https://example.com/projects?q={search_term_string}","query-input":"required name=search_term_string"}}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","name":"Grove Directory","url":"https://example.com","logo":"https://example.com/og-image.svg","sameAs":["https://github.com/tortuvshin/grove"]}</script><!-- JSON-LD: per-page (SoftwareSourceCode for [slug], etc.) --><script>
|
|
2
|
+
// THEME_INIT — runs before paint so the user never sees a
|
|
3
|
+
// flash of the wrong theme. State is persisted in localStorage
|
|
4
|
+
// under the key "grove-theme" (matching ThemeToggle.astro).
|
|
5
|
+
(function () {
|
|
6
|
+
var STORAGE_KEY = "grove-theme";
|
|
7
|
+
function getMode() {
|
|
8
|
+
try {
|
|
9
|
+
var stored = localStorage.getItem(STORAGE_KEY);
|
|
10
|
+
if (stored === "light" || stored === "dark" || stored === "system")
|
|
11
|
+
return stored;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// localStorage may be blocked (private mode, etc.) —
|
|
14
|
+
// fall through to the system default.
|
|
15
|
+
}
|
|
16
|
+
return "system";
|
|
17
|
+
}
|
|
18
|
+
function apply(mode) {
|
|
19
|
+
var resolved =
|
|
20
|
+
mode === "system"
|
|
21
|
+
? (window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
22
|
+
? "dark"
|
|
23
|
+
: "light")
|
|
24
|
+
: mode;
|
|
25
|
+
var root = document.documentElement;
|
|
26
|
+
if (resolved === "dark") root.classList.add("dark");
|
|
27
|
+
else root.classList.remove("dark");
|
|
28
|
+
root.dataset.theme = mode;
|
|
29
|
+
root.dataset.resolvedTheme = resolved;
|
|
30
|
+
}
|
|
31
|
+
apply(getMode());
|
|
32
|
+
window.addEventListener("theme:set", function (e) {
|
|
33
|
+
apply(e.detail);
|
|
34
|
+
});
|
|
35
|
+
var mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
36
|
+
mq.addEventListener("change", function () {
|
|
37
|
+
if (getMode() === "system") apply("system");
|
|
38
|
+
});
|
|
39
|
+
})();
|
|
40
|
+
</script><link rel="stylesheet" href="/_astro/BaseLayout.DfpwR3jc.css">
|
|
41
|
+
<style>.grove-container[data-astro-cid-nj4gdmks]{max-width:var(--container-container, 1140px)}.grove-container-wide[data-astro-cid-nj4gdmks]{max-width:var(--container-wide, 1400px)}.grove-container-narrow[data-astro-cid-nj4gdmks]{max-width:var(--container-narrow, 720px)}
|
|
42
|
+
</style></head> <body class="min-h-screen bg-white text-ink-900 dark:bg-ink-950 dark:text-ink-100"> <header class="sticky top-0 z-50 border-b border-ink-200 bg-white/85 backdrop-blur dark:border-ink-800 dark:bg-ink-950/85"> <div class="mx-auto flex max-w-wide items-center gap-6 px-5 py-3 sm:px-7" style="max-width: var(--container-wide, 1400px);"> <a href="/" class="inline-flex items-center gap-2 text-[0.95rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"> <span aria-hidden="true" class="inline-block h-[18px] w-[18px] rounded bg-ink-900 dark:bg-ink-100"></span> <span>Grove Directory</span> </a> <nav class="hidden flex-1 md:block" aria-label="Primary"> <ul class="m-0 flex list-none gap-5 p-0"> <li> <a href="/" class="text-[0.875rem] font-medium text-ink-500 transition-colors hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"> Home </a> </li><li> <a href="/projects" class="text-[0.875rem] font-medium text-ink-500 transition-colors hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"> Browse </a> </li><li> <a href="/about" class="text-[0.875rem] font-medium text-ink-500 transition-colors hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"> About </a> </li> </ul> </nav> <div class="ml-auto flex items-center gap-2"> <a href="https://github.com/tortuvshin/grove" target="_blank" rel="noopener noreferrer" aria-label="Star" title="Star" class="grove-btn grove-btn-outline"> <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="currentColor"> <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38v-1.33c-2.22.48-2.69-1.07-2.69-1.07-.36-.92-.89-1.17-.89-1.17-.73-.5.05-.49.05-.49.8.06 1.23.83 1.23.83.72 1.22 1.87.87 2.33.67.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48v2.19c0 .21.15.46.55.38A8 8 0 0 0 8 0Z"></path> </svg> <span>Star</span> </a> <button type="button" data-theme-toggle aria-label="Toggle theme" title="Toggle theme" class="relative inline-flex h-8 w-8 items-center justify-center rounded-md border border-ink-200 bg-white text-ink-700 transition-colors hover:border-ink-300 hover:bg-ink-50 dark:border-ink-800 dark:bg-ink-950 dark:text-ink-200 dark:hover:border-ink-700 dark:hover:bg-ink-900/40"> <svg class="hidden h-4 w-4 dark:block" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="12" cy="12" r="4"></circle> <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"></path> </svg> <svg class="block h-4 w-4 dark:hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"></path> </svg> </button> <script type="module">const c="grove-theme";function o(){try{const e=localStorage.getItem(c);return e==="light"||e==="dark"||e==="system"?e:"system"}catch{return"system"}}function s(){const e=["light","dark","system"],n=o(),r=e[(e.indexOf(n)+1)%e.length];try{localStorage.setItem(c,r)}catch{}return window.dispatchEvent(new CustomEvent("theme:set",{detail:r})),r}function t(e){e.title=`Theme: ${o()} (click to switch)`}document.querySelectorAll("[data-theme-toggle]").forEach(e=>{t(e),e.addEventListener("click",()=>{s(),t(e)}),e.addEventListener("mouseenter",()=>t(e)),e.addEventListener("focus",()=>t(e))});</script> </div> </div> </header> <script type="module">document.addEventListener("click",t=>{if(t.defaultPrevented||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.button!==0)return;const r=t.target.closest('a[href^="#"]');if(!r)return;const e=r.getAttribute("href");if(e.length<=1)return;const n=document.querySelector(e);n&&(t.preventDefault(),n.scrollIntoView({behavior:"smooth",block:"start"}),history.replaceState(null,"",e))});</script> <main> <div class="mx-auto grove-container px-5 sm:px-7" data-astro-cid-nj4gdmks> <article class="py-12 sm:py-16"> <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
|
|
43
|
+
About
|
|
44
|
+
</span> <h1 class="mt-1 text-[1.75rem] sm:text-[2.25rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100 m-0">
|
|
45
|
+
About Grove Directory </h1> <p class="mt-4 max-w-[62ch] text-base text-ink-700 dark:text-ink-200 m-0"> Grove Directory is a community knowledge site built with <a href="https://github.com/tortuvshin/grove" target="_blank" rel="noopener noreferrer" class="text-ink-900 underline hover:text-ink-700 dark:text-ink-100 dark:hover:text-white">
|
|
46
|
+
Grove
|
|
47
|
+
</a>
|
|
48
|
+
— an open-source framework for growing useful structured knowledge.
|
|
49
|
+
</p> </article> </div> <section class="grove-section"> <div class="grove-container-narrow mx-auto px-5 sm:px-7"> <header class="mb-6 flex flex-col gap-2"> <h2 class="m-0 text-[1.5rem] sm:text-[1.625rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"> Why this exists </h2> <p class="m-0 text-[0.9375rem] text-ink-500 dark:text-ink-400"> Three principles guide what gets listed and how it's surfaced. </p> </header> <ol class="m-0 flex list-none flex-col gap-5 p-0"> <li class="flex gap-4"> <span aria-hidden="true" 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">
|
|
50
|
+
01 </span> <div> <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"> Curated, not scraped </h3> <p class="m-0 mt-1 text-[0.9375rem] leading-relaxed text-ink-500 dark:text-ink-400"> 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. </p> </div> </li><li class="flex gap-4"> <span aria-hidden="true" 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">
|
|
51
|
+
02 </span> <div> <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"> Plain text all the way down </h3> <p class="m-0 mt-1 text-[0.9375rem] leading-relaxed text-ink-500 dark:text-ink-400"> 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. </p> </div> </li><li class="flex gap-4"> <span aria-hidden="true" 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">
|
|
52
|
+
03 </span> <div> <h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"> Free to fork, free to extend </h3> <p class="m-0 mt-1 text-[0.9375rem] leading-relaxed text-ink-500 dark:text-ink-400"> 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. </p> </div> </li> </ol> </div> </section> <div class="mx-auto grove-container px-5 sm:px-7" data-astro-cid-nj4gdmks> <section class="py-12 sm:py-16" aria-labelledby="methodology-heading"> <span class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">
|
|
53
|
+
Methodology
|
|
54
|
+
</span> <h2 id="methodology-heading" class="mt-1 text-[1.5rem] sm:text-[1.75rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100 m-0">
|
|
55
|
+
How records are picked, scored, and surfaced
|
|
56
|
+
</h2> <p class="mt-3 max-w-[62ch] text-sm text-ink-500 dark:text-ink-400 m-0">
|
|
57
|
+
The full editorial + scoring methodology lives in
|
|
58
|
+
<code class="font-mono text-ink-900 dark:text-ink-100">content/pages/methodology.md</code>.
|
|
59
|
+
Below is the rendered source so reviewers can audit the criteria.
|
|
60
|
+
</p> <pre class="mt-6 overflow-x-auto rounded-md border border-ink-200 bg-ink-50 p-4 text-xs leading-relaxed text-ink-800 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-200"><code>---
|
|
61
|
+
title: "Methodology"
|
|
62
|
+
description: "How records are picked, scored, and surfaced in this directory."
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
# Methodology
|
|
66
|
+
|
|
67
|
+
This page describes how entries in the directory are chosen, how the
|
|
68
|
+
curation signals are computed, and what we do (and don't) promise about
|
|
69
|
+
the data. The intent is to make the directory's logic auditable — every
|
|
70
|
+
number on a detail page is reproducible from the record's YAML.
|
|
71
|
+
|
|
72
|
+
## Selection
|
|
73
|
+
|
|
74
|
+
A record is included if it is:
|
|
75
|
+
|
|
76
|
+
1. **Open source.** Source is public under an OSI-approved license
|
|
77
|
+
(or public-domain dedication).
|
|
78
|
+
2. **Actively maintained.** A pushed commit within the last 18 months
|
|
79
|
+
OR a declared maintenance status of `maintained` / `passive`.
|
|
80
|
+
3. **Useful to a curious developer.** A working build, a real user
|
|
81
|
+
base (or a credible path to one), and a clear description of what
|
|
82
|
+
the project does.
|
|
83
|
+
|
|
84
|
+
We exclude: abandoned experiments, vapourware, projects whose entire
|
|
85
|
+
purpose is to sell a hosted plan, and any record that has not been
|
|
86
|
+
hand-checked by a maintainer.
|
|
87
|
+
|
|
88
|
+
## Curation labels
|
|
89
|
+
|
|
90
|
+
The `curation.labels` field is a small, hand-set list. The supported
|
|
91
|
+
labels are:
|
|
92
|
+
|
|
93
|
+
- `hot` — high recent activity or attention (≥ 1 000 stars or a
|
|
94
|
+
commit within the last 30 days).
|
|
95
|
+
- `new` — added to the directory in the last 7 days.
|
|
96
|
+
- `mature` — long-running curated project, pushed > 365 days ago,
|
|
97
|
+
but still maintained.
|
|
98
|
+
- `featured` — editor's pick for the front page.
|
|
99
|
+
- `needs-review` — open submission awaiting a second maintainer
|
|
100
|
+
look.
|
|
101
|
+
|
|
102
|
+
These labels are mutually additive, not exclusive. A `new` record
|
|
103
|
+
can also be `hot`; a `mature` record can also be `featured`.
|
|
104
|
+
|
|
105
|
+
## Scoring
|
|
106
|
+
|
|
107
|
+
The detail page surfaces a five-dimension score block. The five
|
|
108
|
+
dimensions are:
|
|
109
|
+
|
|
110
|
+
- **Documentation** — is the README / docs site enough to onboard
|
|
111
|
+
without reading the source?
|
|
112
|
+
- **Onboarding** — install in < 5 min, first useful result in < 1 h.
|
|
113
|
+
- **Maintenance** — recent commits, issue close rate, release cadence.
|
|
114
|
+
- **Community** — issues answered, discussions active, contributors
|
|
115
|
+
per release.
|
|
116
|
+
- **Production-readiness** — CI, tests, security policy, releases
|
|
117
|
+
with semver.
|
|
118
|
+
|
|
119
|
+
Each dimension is scored 0-5. The overall score on the detail page
|
|
120
|
+
is the mean. Scores are stored on the record, not computed at build
|
|
121
|
+
time, so we can deliberately disagree with the raw signals (a 1-line
|
|
122
|
+
toy that happens to be wildly popular can still score low on
|
|
123
|
+
documentation).
|
|
124
|
+
|
|
125
|
+
## Health signals
|
|
126
|
+
|
|
127
|
+
The `health` block on each record tracks four state machines:
|
|
128
|
+
|
|
129
|
+
- `status` — `healthy` / `slowing` / `stale` / `archived`.
|
|
130
|
+
- `tier` — `curated` / `community` / `experimental`.
|
|
131
|
+
- `issue_close_rate` — moving 90-day average.
|
|
132
|
+
- `last_release` — most recent semver tag.
|
|
133
|
+
|
|
134
|
+
A record's `status` transitions are time-boxed: a project with no
|
|
135
|
+
commits in 365 days flips from `slowing` to `stale`; 730 days flips
|
|
136
|
+
to `archived`. The transition is mechanical; the recovery is
|
|
137
|
+
manual (a maintainer sets `tier` back to `community` after review).
|
|
138
|
+
|
|
139
|
+
## Lenses
|
|
140
|
+
|
|
141
|
+
The directory's home page surfaces three curated lenses:
|
|
142
|
+
|
|
143
|
+
- **Hot** — `curation.labels` includes `hot`, OR `github.stars` ≥
|
|
144
|
+
1 000, OR `pushedAt` within 30 days.
|
|
145
|
+
- **New** — `pushedAt` within 7 days, OR `curation.labels` includes
|
|
146
|
+
`new`.
|
|
147
|
+
- **Mature** — `health.tier === "curated"` AND `pushedAt` > 365 days
|
|
148
|
+
ago.
|
|
149
|
+
|
|
150
|
+
Each lens caps at 6 items on the home page; the full filtered list
|
|
151
|
+
is reachable via the lens tab on `/projects`.
|
|
152
|
+
|
|
153
|
+
## What we don't promise
|
|
154
|
+
|
|
155
|
+
- **Real-time data.** The site is rebuilt on a schedule, not on
|
|
156
|
+
every push. Counts and dates can lag by up to one build cycle.
|
|
157
|
+
- **Verdict-free scores.** A high overall score means "useful to a
|
|
158
|
+
curious developer", not "best in class" — judgement calls are
|
|
159
|
+
documented per record, not baked into the score.
|
|
160
|
+
- **Causal claims.** The "best for" / "why listed" / "caveats"
|
|
161
|
+
blocks are editorial notes, not derived signals. They reflect
|
|
162
|
+
what a maintainer thought at review time.
|
|
163
|
+
</code></pre> </section> </div> </main> <footer class="mt-16 border-t border-ink-200 bg-white text-ink-500 dark:border-ink-800 dark:bg-ink-950 dark:text-ink-400"> <div class="mx-auto grid max-w-wide grid-cols-2 gap-8 px-5 py-12 md:grid-cols-4 sm:px-7" style="max-width: var(--container-wide, 1400px);"> <div class="col-span-2 md:col-span-1"> <div class="mb-2 inline-flex items-center gap-2 text-[0.95rem] font-semibold text-ink-900 dark:text-ink-100"> <span aria-hidden="true" class="inline-block h-3.5 w-3.5 rounded bg-ink-900 dark:bg-ink-100"></span> <span>Grove Directory</span> </div> <p class="m-0 max-w-[36ch] text-[0.8125rem] leading-relaxed text-ink-500 dark:text-ink-400"> A curated, health-aware developer directory. </p> </div> <div> <h4 class="mb-3 text-2xs font-semibold uppercase tracking-wider text-ink-900 dark:text-ink-100"> Discover </h4> <ul class="m-0 flex list-none flex-col gap-2 p-0"> <li> <a href="https://github.com/tortuvshin/grove" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Source on GitHub </a> </li><li> <a href="https://github.com/tortuvshin/grove/releases" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Latest release </a> </li> </ul> </div> <div> <h4 class="mb-3 text-2xs font-semibold uppercase tracking-wider text-ink-900 dark:text-ink-100"> Contribute </h4> <ul class="m-0 flex list-none flex-col gap-2 p-0"> <li> <a href="https://github.com/tortuvshin/grove/blob/main/CONTRIBUTING.md" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Submit via GitHub </a> </li><li> <a href="https://github.com/tortuvshin/grove/issues" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Report an issue </a> </li><li> <a href="https://github.com/tortuvshin/grove/blob/main/CONTRIBUTING.md" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Contributing guide </a> </li> </ul> </div> <div> <h4 class="mb-3 text-2xs font-semibold uppercase tracking-wider text-ink-900 dark:text-ink-100"> Project </h4> <ul class="m-0 flex list-none flex-col gap-2 p-0"> <li> <a href="https://github.com/tortuvshin/grove/blob/main/README.md" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> README </a> </li><li> <a href="https://github.com/tortuvshin/grove/blob/main/CHANGELOG.md" class="text-sm transition-colors hover:text-ink-900 dark:hover:text-ink-100" target="_blank" rel="noopener noreferrer"> Changelog </a> </li> </ul> </div> </div> <div class="border-t border-ink-200 dark:border-ink-800"> <div class="mx-auto flex max-w-wide flex-wrap items-center gap-3 px-5 py-4 text-2xs text-ink-500 sm:px-7 dark:text-ink-400" style="max-width: var(--container-wide, 1400px);"> <span>© 2026 Grove Directory.</span> <span class="text-ink-300 dark:text-ink-700">·</span> <a href="https://github.com/tortuvshin/grove" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-ink-900 dark:hover:text-ink-100">
|
|
164
|
+
View source on GitHub
|
|
165
|
+
</a> </div> </div> </footer> </body> </html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" viewBox="0 0 256 150"><path fill="#34A853" d="M255.285 143.47c-.084-.524-.164-1.042-.251-1.56a128.1 128.1 0 0 0-12.794-38.288 128.8 128.8 0 0 0-23.45-31.86 129.2 129.2 0 0 0-22.713-18.005c.049-.08.09-.168.14-.25 2.582-4.461 5.172-8.917 7.755-13.38l7.576-13.068c1.818-3.126 3.632-6.26 5.438-9.386a11.78 11.78 0 0 0 .662-10.484 11.67 11.67 0 0 0-4.823-5.536 11.85 11.85 0 0 0-5.004-1.61 12 12 0 0 0-2.218.018 11.74 11.74 0 0 0-8.968 5.798c-1.814 3.127-3.628 6.26-5.438 9.386l-7.576 13.069c-2.583 4.462-5.173 8.918-7.755 13.38-.282.487-.567.973-.848 1.467q-.586-.237-1.172-.462c-14.24-5.43-29.688-8.4-45.836-8.4-.442 0-.879 0-1.324.006-14.357.143-28.152 2.64-41.022 7.12a119 119 0 0 0-4.42 1.642c-.262-.455-.532-.911-.79-1.367-2.583-4.462-5.173-8.918-7.755-13.38l-7.576-13.07a3915 3915 0 0 1-5.439-9.386A11.74 11.74 0 0 0 48.5.048a11.7 11.7 0 0 0-5.01 1.612 11.7 11.7 0 0 0-4.077 4.063c-.281.474-.532.967-.742 1.473a11.8 11.8 0 0 0-.365 8.188c.259.786.594 1.554 1.023 2.296a3973 3973 0 0 1 5.439 9.386c2.53 4.357 5.054 8.713 7.58 13.069 2.582 4.462 5.168 8.918 7.75 13.38.02.038.046.075.065.112A129 129 0 0 0 45.32 64.38a129.7 129.7 0 0 0-22.2 24.015 128 128 0 0 0-9.34 15.24 128.2 128.2 0 0 0-10.843 28.764 131 131 0 0 0-1.951 9.524c-.087.518-.167 1.042-.247 1.56A125 125 0 0 0 0 149.118h256a130 130 0 0 0-.734-5.636z"/><path fill="#202124" d="M194.59 113.712c5.122-3.41 5.867-11.3 1.661-17.62-4.203-6.323-11.763-8.682-16.883-5.273-5.122 3.41-5.868 11.3-1.662 17.621 4.203 6.322 11.764 8.682 16.883 5.272Zm-116.072-5.25c4.206-6.321 3.46-14.21-1.662-17.62-5.123-3.41-12.68-1.05-16.886 5.27-4.203 6.323-3.458 14.212 1.662 17.622 5.122 3.41 12.683 1.05 16.886-5.272"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 814 1000"><path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2m-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 814 1000"><path fill="#fff" d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2m-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3"/></svg>
|