@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
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { Category } from "../data/categories";
|
|
3
|
-
import SectionHeader from "./layout/SectionHeader.astro";
|
|
4
|
-
import Container from "./layout/Container.astro";
|
|
5
|
-
import { countByCategory } from "../lib/taxonomy-counts";
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
categories: Category[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const { categories } = Astro.props;
|
|
12
|
-
const counts = countByCategory();
|
|
13
|
-
|
|
14
|
-
// Hide categories that have zero apps in the directory. We don't lie about
|
|
15
|
-
// coverage. The taxonomy is a "future state" map; the rendered list is the
|
|
16
|
-
// intersection of the taxonomy and what actually exists in data/apps/.
|
|
17
|
-
const visible = categories
|
|
18
|
-
.map((c) => ({ ...c, count: counts.get(c.slug) ?? 0 }))
|
|
19
|
-
.filter((c) => c.count > 0)
|
|
20
|
-
.sort((a, b) => b.count - a.count);
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
<section
|
|
24
|
-
id="categories"
|
|
25
|
-
class="section scroll-mt-20 border-b border-ink-200 dark:border-ink-800"
|
|
26
|
-
>
|
|
27
|
-
<Container>
|
|
28
|
-
<SectionHeader
|
|
29
|
-
eyebrow="Categories"
|
|
30
|
-
title="Browse by category"
|
|
31
|
-
description="Find apps by what they do, not by which stack they use."
|
|
32
|
-
/>
|
|
33
|
-
|
|
34
|
-
<div
|
|
35
|
-
class="overflow-hidden rounded-lg border border-ink-200 dark:border-ink-800"
|
|
36
|
-
>
|
|
37
|
-
<ul
|
|
38
|
-
class="m-0 grid list-none grid-cols-1 divide-y divide-ink-200 p-0 sm:grid-cols-2 sm:divide-x sm:divide-y-0 lg:grid-cols-3 dark:divide-ink-800"
|
|
39
|
-
>
|
|
40
|
-
{
|
|
41
|
-
visible.map((cat) => (
|
|
42
|
-
<li class="divide-x divide-ink-200 dark:divide-ink-800">
|
|
43
|
-
<a
|
|
44
|
-
href={`/apps?category=${encodeURIComponent(cat.name)}`}
|
|
45
|
-
class="group flex h-full items-start gap-3 p-4 no-underline transition-colors hover:bg-ink-50/60 dark:hover:bg-ink-900/40"
|
|
46
|
-
>
|
|
47
|
-
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
48
|
-
<div class="flex items-center gap-2">
|
|
49
|
-
<h3 class="m-0 text-[0.9375rem] font-semibold tracking-tight text-ink-900 group-hover:underline dark:text-ink-100">
|
|
50
|
-
{cat.name}
|
|
51
|
-
</h3>
|
|
52
|
-
<span
|
|
53
|
-
class="inline-flex items-center rounded-md border border-ink-200 bg-ink-50 px-1.5 py-0.5 font-mono text-[10px] font-medium text-ink-600 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-400"
|
|
54
|
-
title={`${cat.count} apps in this category`}
|
|
55
|
-
>
|
|
56
|
-
{cat.count} apps
|
|
57
|
-
</span>
|
|
58
|
-
</div>
|
|
59
|
-
<p class="m-0 text-2xs leading-relaxed text-ink-500 dark:text-ink-400">
|
|
60
|
-
{cat.blurb}
|
|
61
|
-
</p>
|
|
62
|
-
</div>
|
|
63
|
-
<svg
|
|
64
|
-
viewBox="0 0 16 16"
|
|
65
|
-
width="12"
|
|
66
|
-
height="12"
|
|
67
|
-
aria-hidden="true"
|
|
68
|
-
class="mt-1 shrink-0 text-ink-300 transition-colors group-hover:text-ink-500 dark:text-ink-700 dark:group-hover:text-ink-400"
|
|
69
|
-
fill="currentColor"
|
|
70
|
-
>
|
|
71
|
-
<path d="M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z" />
|
|
72
|
-
</svg>
|
|
73
|
-
</a>
|
|
74
|
-
</li>
|
|
75
|
-
))
|
|
76
|
-
}
|
|
77
|
-
</ul>
|
|
78
|
-
</div>
|
|
79
|
-
</Container>
|
|
80
|
-
</section>
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Contributors grid for the home page. Renders all contributors at a
|
|
6
|
-
* uniform avatar size (no big/small distinction — the previous
|
|
7
|
-
* "top 10 + rest" layout implied a hierarchy that doesn't exist).
|
|
8
|
-
*
|
|
9
|
-
* When the contributor count is large enough that the homepage
|
|
10
|
-
* layout would feel cluttered, a "View all" link points to the
|
|
11
|
-
* dedicated `/contributors` page where the full list lives.
|
|
12
|
-
*/
|
|
13
|
-
import type { Contributor } from "../data/contributors";
|
|
14
|
-
import SectionHeader from "./layout/SectionHeader.astro";
|
|
15
|
-
import Container from "./layout/Container.astro";
|
|
16
|
-
|
|
17
|
-
interface Props {
|
|
18
|
-
contributors: Contributor[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const { contributors } = Astro.props;
|
|
22
|
-
const total = contributors.length;
|
|
23
|
-
// Threshold above which we link out to the full /contributors page
|
|
24
|
-
// instead of inlining every avatar on the homepage.
|
|
25
|
-
const INLINE_LIMIT = 100;
|
|
26
|
-
const isCapped = total > INLINE_LIMIT;
|
|
27
|
-
const visible = isCapped ? contributors.slice(0, INLINE_LIMIT) : contributors;
|
|
28
|
-
const more = Math.max(0, total - INLINE_LIMIT);
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
<section
|
|
32
|
-
id="contributors"
|
|
33
|
-
class="section scroll-mt-20 border-b border-ink-200 dark:border-ink-800"
|
|
34
|
-
>
|
|
35
|
-
<Container>
|
|
36
|
-
<SectionHeader
|
|
37
|
-
eyebrow="Community"
|
|
38
|
-
title="Thanks to every contributor."
|
|
39
|
-
description="This directory exists because people keep discovering, submitting, reviewing, and maintaining open-source app references."
|
|
40
|
-
/>
|
|
41
|
-
|
|
42
|
-
<ul
|
|
43
|
-
class="m-0 grid list-none grid-cols-[repeat(auto-fill,minmax(72px,1fr))] gap-x-3 gap-y-5 p-0"
|
|
44
|
-
>
|
|
45
|
-
{
|
|
46
|
-
visible.map((c) => (
|
|
47
|
-
<li class="flex flex-col items-center gap-1.5 text-center">
|
|
48
|
-
<a
|
|
49
|
-
href={c.profileUrl}
|
|
50
|
-
target="_blank"
|
|
51
|
-
rel="noopener noreferrer"
|
|
52
|
-
aria-label={`${c.name ?? c.username} on GitHub`}
|
|
53
|
-
title={`${c.name ?? c.username}${typeof c.contributions === "number" ? ` · ${c.contributions} contributions` : ""}`}
|
|
54
|
-
class="block transition-opacity hover:opacity-80"
|
|
55
|
-
>
|
|
56
|
-
<img
|
|
57
|
-
src={c.avatarUrl}
|
|
58
|
-
alt={`${c.name ?? c.username} avatar`}
|
|
59
|
-
loading="lazy"
|
|
60
|
-
width="40"
|
|
61
|
-
height="40"
|
|
62
|
-
class="h-10 w-10 rounded-full border border-ink-200 bg-white object-cover dark:border-ink-800 dark:bg-ink-900"
|
|
63
|
-
/>
|
|
64
|
-
</a>
|
|
65
|
-
<a
|
|
66
|
-
href={c.profileUrl}
|
|
67
|
-
target="_blank"
|
|
68
|
-
rel="noopener noreferrer"
|
|
69
|
-
title={c.name ?? c.username}
|
|
70
|
-
class="block max-w-full truncate text-2xs text-ink-500 transition-colors hover:text-ink-900 dark:text-ink-400 dark:hover:text-ink-100"
|
|
71
|
-
>
|
|
72
|
-
{c.username}
|
|
73
|
-
</a>
|
|
74
|
-
</li>
|
|
75
|
-
))
|
|
76
|
-
}
|
|
77
|
-
</ul>
|
|
78
|
-
|
|
79
|
-
<div
|
|
80
|
-
class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-2 text-2xs text-ink-500 dark:text-ink-400"
|
|
81
|
-
>
|
|
82
|
-
<span>
|
|
83
|
-
{isCapped
|
|
84
|
-
? <>Showing {visible.length} of {total} contributors on the homepage.</>
|
|
85
|
-
: <>Showing all {total} contributors.</>}
|
|
86
|
-
</span>
|
|
87
|
-
<a
|
|
88
|
-
href="/contributors"
|
|
89
|
-
class="inline-flex items-center gap-1 font-medium text-ink-700 hover:text-ink-900 dark:text-ink-300 dark:hover:text-ink-100"
|
|
90
|
-
>
|
|
91
|
-
View all
|
|
92
|
-
{more > 0 ? ` (${more} more)` : ""} →
|
|
93
|
-
</a>
|
|
94
|
-
<span aria-hidden="true">·</span>
|
|
95
|
-
<a
|
|
96
|
-
href="https://github.com/tortuvshin/open-apps/graphs/contributors"
|
|
97
|
-
target="_blank"
|
|
98
|
-
rel="noopener noreferrer"
|
|
99
|
-
class="text-ink-700 hover:text-ink-900 dark:text-ink-300 dark:hover:text-ink-100"
|
|
100
|
-
>
|
|
101
|
-
GitHub contributors graph
|
|
102
|
-
</a>
|
|
103
|
-
</div>
|
|
104
|
-
</Container>
|
|
105
|
-
</section>
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { OpenSourceApp } from "../data/types";
|
|
3
|
-
import Icon from "./icons/Icon.astro";
|
|
4
|
-
import {
|
|
5
|
-
getOwnerAndRepoFromRepoUrl,
|
|
6
|
-
getOwnerAvatarUrl,
|
|
7
|
-
} from "../lib/repo";
|
|
8
|
-
import { formatStars } from "../lib/format";
|
|
9
|
-
|
|
10
|
-
interface Props {
|
|
11
|
-
app: OpenSourceApp;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const { app } = Astro.props;
|
|
15
|
-
|
|
16
|
-
const { owner, repo } = getOwnerAndRepoFromRepoUrl(app.repoUrl);
|
|
17
|
-
const avatar = app.logoUrl ?? getOwnerAvatarUrl(owner, 80);
|
|
18
|
-
|
|
19
|
-
const label =
|
|
20
|
-
app.labels?.find((l) => l === "new" || l === "hot" || l === "mature") ??
|
|
21
|
-
app.labels?.[0];
|
|
22
|
-
|
|
23
|
-
const allStacks = [app.stack, ...(app.stacks ?? [])].filter(Boolean);
|
|
24
|
-
const visibleStacks = allStacks.slice(0, 5);
|
|
25
|
-
const stackOverflow = allStacks.length - visibleStacks.length;
|
|
26
|
-
|
|
27
|
-
const visiblePlatforms = app.platforms.slice(0, 5);
|
|
28
|
-
const platformOverflow = app.platforms.length - visiblePlatforms.length;
|
|
29
|
-
|
|
30
|
-
const detailHref = `/apps/${app.slug}`;
|
|
31
|
-
|
|
32
|
-
const isCurated = !!app.curation;
|
|
33
|
-
|
|
34
|
-
const projectTypeLabel = (() => {
|
|
35
|
-
switch (app.projectType) {
|
|
36
|
-
case "real-app": return "Real app";
|
|
37
|
-
case "production": return "Production";
|
|
38
|
-
case "reference": return "Reference";
|
|
39
|
-
case "demo": return "Demo";
|
|
40
|
-
case "template": return "Template";
|
|
41
|
-
default: return null;
|
|
42
|
-
}
|
|
43
|
-
})();
|
|
44
|
-
|
|
45
|
-
const starsLabel = formatStars(app.stars);
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
<article
|
|
49
|
-
class="decision-row group relative flex flex-col gap-3 rounded-lg border border-ink-200 bg-white p-4 transition-colors hover:border-ink-300 hover:shadow-[0_1px_2px_rgba(0,0,0,0.04),0_4px_12px_-2px_rgba(0,0,0,0.06)] dark:border-ink-800 dark:bg-ink-950 dark:hover:border-ink-700 dark:hover:shadow-[0_1px_2px_rgba(0,0,0,0.4),0_4px_12px_-2px_rgba(0,0,0,0.3)]"
|
|
50
|
-
data-slug={app.slug}
|
|
51
|
-
>
|
|
52
|
-
{/* Avatar + name + badges */}
|
|
53
|
-
<header class="flex items-start gap-3">
|
|
54
|
-
<a
|
|
55
|
-
href={detailHref}
|
|
56
|
-
class="relative z-10 flex shrink-0"
|
|
57
|
-
aria-hidden="true"
|
|
58
|
-
tabindex="-1"
|
|
59
|
-
>
|
|
60
|
-
{avatar ? (
|
|
61
|
-
<img
|
|
62
|
-
src={avatar}
|
|
63
|
-
alt={`${app.name} avatar`}
|
|
64
|
-
width="44"
|
|
65
|
-
height="44"
|
|
66
|
-
loading="lazy"
|
|
67
|
-
decoding="async"
|
|
68
|
-
class="h-11 w-11 rounded-md border border-ink-200 bg-white object-cover dark:border-ink-800 dark:bg-ink-900"
|
|
69
|
-
/>
|
|
70
|
-
) : (
|
|
71
|
-
<span
|
|
72
|
-
aria-hidden="true"
|
|
73
|
-
class="flex h-11 w-11 items-center justify-center rounded-md border border-ink-200 bg-ink-50 text-sm font-semibold text-ink-700 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-300"
|
|
74
|
-
>
|
|
75
|
-
{app.name.slice(0, 2).toUpperCase()}
|
|
76
|
-
</span>
|
|
77
|
-
)}
|
|
78
|
-
</a>
|
|
79
|
-
|
|
80
|
-
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
81
|
-
<div class="flex flex-wrap items-baseline gap-x-2">
|
|
82
|
-
<h3 class="m-0 text-[1rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100">
|
|
83
|
-
<a
|
|
84
|
-
href={detailHref}
|
|
85
|
-
class="relative z-10 text-inherit no-underline hover:underline"
|
|
86
|
-
>
|
|
87
|
-
{app.name}
|
|
88
|
-
</a>
|
|
89
|
-
</h3>
|
|
90
|
-
{projectTypeLabel && (
|
|
91
|
-
<span class="inline-flex items-center rounded border border-ink-200 bg-ink-50 px-1.5 py-0 text-[10px] font-medium uppercase tracking-wider text-ink-700 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-300">
|
|
92
|
-
{projectTypeLabel}
|
|
93
|
-
</span>
|
|
94
|
-
)}
|
|
95
|
-
{label && (
|
|
96
|
-
<span
|
|
97
|
-
class={`inline-flex items-center rounded border px-1.5 py-0 text-[10px] font-medium uppercase tracking-wider ${
|
|
98
|
-
label === "new"
|
|
99
|
-
? "border-emerald-300/60 bg-emerald-50 text-emerald-800 dark:border-emerald-800/60 dark:bg-emerald-950/40 dark:text-emerald-300"
|
|
100
|
-
: label === "hot"
|
|
101
|
-
? "border-orange-300/60 bg-orange-50 text-orange-800 dark:border-orange-800/60 dark:bg-orange-950/40 dark:text-orange-300"
|
|
102
|
-
: "border-blue-300/60 bg-blue-50 text-blue-800 dark:border-blue-800/60 dark:bg-blue-950/40 dark:text-blue-300"
|
|
103
|
-
}`}
|
|
104
|
-
>
|
|
105
|
-
{label}
|
|
106
|
-
</span>
|
|
107
|
-
)}
|
|
108
|
-
{isCurated && !app.curation?.reviewed && (
|
|
109
|
-
<span class="inline-flex items-center gap-1 rounded border border-amber-300/60 bg-amber-50 px-1.5 py-0 text-[10px] font-medium text-amber-800 dark:border-amber-800/60 dark:bg-amber-950/40 dark:text-amber-300">
|
|
110
|
-
<svg viewBox="0 0 16 16" width="9" height="9" aria-hidden="true" fill="currentColor">
|
|
111
|
-
<path d="M8 1.5a.75.75 0 0 1 .65.376l5.7 9.788A.75.75 0 0 1 13.7 12.75H2.3a.75.75 0 0 1-.65-1.086l5.7-9.788A.75.75 0 0 1 8 1.5ZM8 6a.75.75 0 0 1 .75.75v3a.75.75 0 0 1-1.5 0v-3A.75.75 0 0 1 8 6Zm0 6.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"/>
|
|
112
|
-
</svg>
|
|
113
|
-
Auto-curated · Needs review
|
|
114
|
-
</span>
|
|
115
|
-
)}
|
|
116
|
-
{isCurated && app.curation?.reviewed && (
|
|
117
|
-
<span class="inline-flex items-center gap-1 rounded border border-emerald-300/60 bg-emerald-50 px-1.5 py-0 text-[10px] font-medium text-emerald-800 dark:border-emerald-800/60 dark:bg-emerald-950/40 dark:text-emerald-300">
|
|
118
|
-
<svg viewBox="0 0 16 16" width="9" height="9" aria-hidden="true" fill="currentColor">
|
|
119
|
-
<path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"/>
|
|
120
|
-
</svg>
|
|
121
|
-
Curator reviewed
|
|
122
|
-
</span>
|
|
123
|
-
)}
|
|
124
|
-
</div>
|
|
125
|
-
<div class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-2xs text-ink-500 dark:text-ink-400">
|
|
126
|
-
{owner && (
|
|
127
|
-
<a
|
|
128
|
-
href={app.repoUrl}
|
|
129
|
-
target="_blank"
|
|
130
|
-
rel="noopener noreferrer"
|
|
131
|
-
class="relative z-10 font-mono hover:text-ink-900 dark:hover:text-ink-100"
|
|
132
|
-
>
|
|
133
|
-
<span class="text-ink-400 dark:text-ink-500">@</span>{owner}
|
|
134
|
-
{repo && <span class="text-ink-400 dark:text-ink-500">/{repo}</span>}
|
|
135
|
-
</a>
|
|
136
|
-
)}
|
|
137
|
-
<span class="text-ink-300 dark:text-ink-700">·</span>
|
|
138
|
-
<span>{app.category}</span>
|
|
139
|
-
{app.status && (
|
|
140
|
-
<>
|
|
141
|
-
<span class="text-ink-300 dark:text-ink-700">·</span>
|
|
142
|
-
<span class="capitalize">{app.status}</span>
|
|
143
|
-
</>
|
|
144
|
-
)}
|
|
145
|
-
{starsLabel && (
|
|
146
|
-
<>
|
|
147
|
-
<span class="text-ink-300 dark:text-ink-700">·</span>
|
|
148
|
-
<span class="inline-flex items-center gap-0.5 font-mono">
|
|
149
|
-
<svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" fill="currentColor">
|
|
150
|
-
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/>
|
|
151
|
-
</svg>
|
|
152
|
-
{starsLabel}
|
|
153
|
-
</span>
|
|
154
|
-
</>
|
|
155
|
-
)}
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
<a
|
|
160
|
-
href={detailHref}
|
|
161
|
-
class="relative z-10 hidden shrink-0 self-start text-2xs font-medium text-ink-500 hover:text-ink-900 sm:inline dark:text-ink-400 dark:hover:text-ink-100"
|
|
162
|
-
>
|
|
163
|
-
View details →
|
|
164
|
-
</a>
|
|
165
|
-
</header>
|
|
166
|
-
|
|
167
|
-
{/* One-line summary */}
|
|
168
|
-
<p class="m-0 line-clamp-2 text-[0.8125rem] leading-relaxed text-ink-600 dark:text-ink-300">
|
|
169
|
-
{app.description}
|
|
170
|
-
</p>
|
|
171
|
-
|
|
172
|
-
{/* Metadata grid: stack, platforms, license — compact one-liner each */}
|
|
173
|
-
<dl class="grid grid-cols-1 gap-x-4 gap-y-1 text-2xs sm:grid-cols-3">
|
|
174
|
-
<div class="flex items-baseline gap-1.5">
|
|
175
|
-
<dt class="font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">Stack</dt>
|
|
176
|
-
<dd class="flex flex-wrap items-center gap-1 text-ink-700 dark:text-ink-300">
|
|
177
|
-
{visibleStacks.map((s) => (
|
|
178
|
-
<span class="inline-flex items-center gap-0.5">
|
|
179
|
-
<Icon name={s} category="stack" size={10} class="h-2.5 w-2.5" />
|
|
180
|
-
<span>{s}</span>
|
|
181
|
-
</span>
|
|
182
|
-
))}
|
|
183
|
-
{stackOverflow > 0 && <span class="text-ink-400 dark:text-ink-500">+{stackOverflow}</span>}
|
|
184
|
-
</dd>
|
|
185
|
-
</div>
|
|
186
|
-
<div class="flex items-baseline gap-1.5">
|
|
187
|
-
<dt class="font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">Platforms</dt>
|
|
188
|
-
<dd class="flex flex-wrap items-center gap-1 text-ink-700 dark:text-ink-300">
|
|
189
|
-
{visiblePlatforms.map((p) => (
|
|
190
|
-
<span class="inline-flex items-center gap-0.5">
|
|
191
|
-
<Icon name={p} category="platform" size={10} class="h-2.5 w-2.5" />
|
|
192
|
-
<span>{p}</span>
|
|
193
|
-
</span>
|
|
194
|
-
))}
|
|
195
|
-
{platformOverflow > 0 && <span class="text-ink-400 dark:text-ink-500">+{platformOverflow}</span>}
|
|
196
|
-
</dd>
|
|
197
|
-
</div>
|
|
198
|
-
<div class="flex items-baseline gap-1.5">
|
|
199
|
-
<dt class="font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400">License</dt>
|
|
200
|
-
<dd class="font-mono text-ink-700 dark:text-ink-300">
|
|
201
|
-
{app.license ?? "—"}
|
|
202
|
-
{app.codebaseSize && (
|
|
203
|
-
<>
|
|
204
|
-
<span class="ml-2 text-ink-300 dark:text-ink-700">·</span>
|
|
205
|
-
<span class="ml-1 text-ink-500 dark:text-ink-400">{app.codebaseSize}</span>
|
|
206
|
-
</>
|
|
207
|
-
)}
|
|
208
|
-
{app.difficulty && (
|
|
209
|
-
<>
|
|
210
|
-
<span class="ml-2 text-ink-300 dark:text-ink-700">·</span>
|
|
211
|
-
<span class="ml-1 text-ink-500 dark:text-ink-400">{app.difficulty}</span>
|
|
212
|
-
</>
|
|
213
|
-
)}
|
|
214
|
-
</dd>
|
|
215
|
-
</div>
|
|
216
|
-
</dl>
|
|
217
|
-
|
|
218
|
-
{/* Scores row — kept off the list to avoid bloat. See detail page. */}
|
|
219
|
-
</article>
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { Category } from "../data/categories";
|
|
3
|
-
import Container from "./layout/Container.astro";
|
|
4
|
-
import { countByCategory } from "../lib/taxonomy-counts";
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
categories: Category[];
|
|
8
|
-
class?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const { categories, class: className = "" } = Astro.props;
|
|
12
|
-
const counts = countByCategory();
|
|
13
|
-
|
|
14
|
-
// Sort by count desc, drop empty (already done in CategoryGrid, but
|
|
15
|
-
// re-apply here for safety in case the consumer rendered a raw list).
|
|
16
|
-
const visible = categories
|
|
17
|
-
.map((c) => ({ ...c, count: counts.get(c.name) ?? 0 }))
|
|
18
|
-
.filter((c) => c.count > 0)
|
|
19
|
-
.sort((a, b) => b.count - a.count)
|
|
20
|
-
.slice(0, 12);
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
<section
|
|
24
|
-
id="explore-categories"
|
|
25
|
-
class={`section scroll-mt-20 border-t border-ink-200 dark:border-ink-800 ${className}`}
|
|
26
|
-
>
|
|
27
|
-
<Container>
|
|
28
|
-
<div class="flex flex-col gap-1.5">
|
|
29
|
-
<span
|
|
30
|
-
class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400"
|
|
31
|
-
>Explore</span
|
|
32
|
-
>
|
|
33
|
-
<h2
|
|
34
|
-
class="m-0 text-[1.5rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"
|
|
35
|
-
>
|
|
36
|
-
Browse by category
|
|
37
|
-
</h2>
|
|
38
|
-
<p
|
|
39
|
-
class="m-0 text-[0.875rem] text-ink-500 dark:text-ink-400"
|
|
40
|
-
>
|
|
41
|
-
Jump straight to a category — links take you to a filtered list,
|
|
42
|
-
not a static browse page.
|
|
43
|
-
</p>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<ul class="mt-5 grid list-none grid-cols-1 gap-2 p-0 sm:grid-cols-2 lg:grid-cols-3">
|
|
47
|
-
{
|
|
48
|
-
visible.map((c) => (
|
|
49
|
-
<li>
|
|
50
|
-
<a
|
|
51
|
-
href={`/apps?category=${encodeURIComponent(c.name)}`}
|
|
52
|
-
class="card card-hover group flex h-full items-start gap-3 p-3.5 no-underline"
|
|
53
|
-
>
|
|
54
|
-
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
55
|
-
<h3 class="m-0 text-[0.875rem] font-semibold tracking-tight text-ink-900 group-hover:underline dark:text-ink-100">
|
|
56
|
-
{c.name}
|
|
57
|
-
</h3>
|
|
58
|
-
<p class="m-0 truncate text-2xs text-ink-500 dark:text-ink-400">
|
|
59
|
-
{c.blurb}
|
|
60
|
-
</p>
|
|
61
|
-
</div>
|
|
62
|
-
<span
|
|
63
|
-
class="shrink-0 inline-flex items-center rounded-md border border-ink-200 bg-ink-50 px-1.5 py-0.5 font-mono text-[10px] font-medium text-ink-600 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-400"
|
|
64
|
-
title={`${c.count} apps in this category`}
|
|
65
|
-
>
|
|
66
|
-
{c.count}
|
|
67
|
-
</span>
|
|
68
|
-
</a>
|
|
69
|
-
</li>
|
|
70
|
-
))
|
|
71
|
-
}
|
|
72
|
-
</ul>
|
|
73
|
-
</Container>
|
|
74
|
-
</section>
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { Stack } from "../data/stacks";
|
|
3
|
-
import Container from "./layout/Container.astro";
|
|
4
|
-
import Icon from "./icons/Icon.astro";
|
|
5
|
-
import { countByStack } from "../lib/taxonomy-counts";
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
stacks: Stack[];
|
|
9
|
-
class?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const { stacks, class: className = "" } = Astro.props;
|
|
13
|
-
const counts = countByStack();
|
|
14
|
-
|
|
15
|
-
const statusLabel: Record<Stack["status"], string> = {
|
|
16
|
-
live: "Available now",
|
|
17
|
-
expanding: "Expanding",
|
|
18
|
-
planned: "Planned",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const statusBadge: Record<Stack["status"], string> = {
|
|
22
|
-
live: "border-emerald-300/60 bg-emerald-50 text-emerald-800 dark:border-emerald-800/60 dark:bg-emerald-950/40 dark:text-emerald-300",
|
|
23
|
-
expanding: "border-ink-200 bg-ink-50 text-ink-700 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-300",
|
|
24
|
-
planned: "border-ink-200 bg-white text-ink-500 dark:border-ink-800 dark:bg-ink-950 dark:text-ink-500",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const enriched = stacks
|
|
28
|
-
.map((s) => ({ ...s, count: counts.get(s.name) ?? 0 }))
|
|
29
|
-
.sort((a, b) => b.count - a.count);
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
<section
|
|
33
|
-
id="explore-stacks"
|
|
34
|
-
class={`section scroll-mt-20 border-t border-ink-200 dark:border-ink-800 ${className}`}
|
|
35
|
-
>
|
|
36
|
-
<Container>
|
|
37
|
-
<div class="flex flex-col gap-1.5">
|
|
38
|
-
<span
|
|
39
|
-
class="text-2xs font-medium uppercase tracking-wider text-ink-500 dark:text-ink-400"
|
|
40
|
-
>Explore</span
|
|
41
|
-
>
|
|
42
|
-
<h2
|
|
43
|
-
class="m-0 text-[1.5rem] font-semibold tracking-tight text-ink-900 dark:text-ink-100"
|
|
44
|
-
>
|
|
45
|
-
Browse by stack
|
|
46
|
-
</h2>
|
|
47
|
-
<p
|
|
48
|
-
class="m-0 text-[0.875rem] text-ink-500 dark:text-ink-400"
|
|
49
|
-
>
|
|
50
|
-
Pick a stack to see every curated app built with it. Live stacks
|
|
51
|
-
have apps right now; planned ones are on the roadmap.
|
|
52
|
-
</p>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
<div class="mt-5 grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
|
|
56
|
-
{
|
|
57
|
-
enriched.map((stack) => (
|
|
58
|
-
<a
|
|
59
|
-
href={`/apps?stack=${encodeURIComponent(stack.name)}`}
|
|
60
|
-
class="card card-hover group flex h-full items-center gap-3 p-3.5 no-underline"
|
|
61
|
-
>
|
|
62
|
-
<div
|
|
63
|
-
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-ink-200 bg-white dark:border-ink-800 dark:bg-ink-950"
|
|
64
|
-
aria-hidden="true"
|
|
65
|
-
>
|
|
66
|
-
<Icon
|
|
67
|
-
name={stack.name}
|
|
68
|
-
category="stack"
|
|
69
|
-
size={18}
|
|
70
|
-
class="h-[18px] w-[18px]"
|
|
71
|
-
/>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
75
|
-
<div class="flex items-center gap-2">
|
|
76
|
-
<h3 class="m-0 text-[0.875rem] font-semibold tracking-tight text-ink-900 group-hover:underline dark:text-ink-100">
|
|
77
|
-
{stack.name}
|
|
78
|
-
</h3>
|
|
79
|
-
<span
|
|
80
|
-
class="inline-flex items-center rounded-md border border-ink-200 bg-ink-50 px-1.5 py-0.5 font-mono text-[10px] font-medium text-ink-600 dark:border-ink-800 dark:bg-ink-900/60 dark:text-ink-400"
|
|
81
|
-
title={`${stack.count} apps in this stack`}
|
|
82
|
-
>
|
|
83
|
-
{stack.count} apps
|
|
84
|
-
</span>
|
|
85
|
-
</div>
|
|
86
|
-
<p class="m-0 truncate text-2xs text-ink-500 dark:text-ink-400">
|
|
87
|
-
{stack.blurb}
|
|
88
|
-
</p>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<span
|
|
92
|
-
class={`shrink-0 inline-flex items-center rounded-md border px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wider ${statusBadge[stack.status]}`}
|
|
93
|
-
aria-label={`Status: ${statusLabel[stack.status]}`}
|
|
94
|
-
>
|
|
95
|
-
{statusLabel[stack.status]}
|
|
96
|
-
</span>
|
|
97
|
-
</a>
|
|
98
|
-
))
|
|
99
|
-
}
|
|
100
|
-
</div>
|
|
101
|
-
</Container>
|
|
102
|
-
</section>
|