Skip to content

Commit 6bf009e

Browse files
committed
Add package manager picker to landing page
1 parent cb1e228 commit 6bf009e

2 files changed

Lines changed: 61 additions & 11 deletions

File tree

www/components/homepage/Hero.tsx

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,23 @@ import { FancyLink } from "../../components/FancyLink.tsx";
22
import LemonTop from "../../islands/LemonTop.tsx";
33
import LemonBottom from "../../islands/LemonBottom.tsx";
44
import { CopyButton } from "../CopyButton.tsx";
5+
import { PM_NAMES, type PmName } from "../../utils/markdown.ts";
56

6-
export function Hero() {
7+
const PM_COMMANDS: Record<PmName, string> = {
8+
npm: "npm create @frsh/app@latest",
9+
pnpm: "pnpm create @frsh/app",
10+
yarn: "yarn create @frsh/app",
11+
deno: "deno run -A npm:@frsh/create-app",
12+
};
13+
14+
const PM_LABELS: Record<PmName, string> = {
15+
npm: "npm",
16+
pnpm: "pnpm",
17+
yarn: "Yarn",
18+
deno: "Deno",
19+
};
20+
21+
export function Hero(props: { activePm: PmName }) {
722
return (
823
<>
924
<div class="bg-green-300 mt-0 pt-32 md:pt-48 !mb-0 bg-gradient-to-br from-blue-100 via-green-200 to-yellow-100">
@@ -30,9 +45,9 @@ export function Hero() {
3045
Vite, with file-system routing and signals. Server-render by default, and ship
3146
JavaScript only for the islands that need it.
3247
</p>
33-
<div class="mt-12 flex flex-wrap justify-center items-stretch md:justify-start gap-4">
48+
<div class="mt-12 flex flex-col items-stretch md:items-start gap-4">
3449
<FancyLink href="/docs/getting-started">Get started</FancyLink>
35-
<CopyArea code={`npm create @frsh/app@latest`} />
50+
<PmInstall activePm={props.activePm} />
3651
</div>
3752
</div>
3853
<div class="md:col-span-2 flex justify-center items-end pb-8 md:pb-32">
@@ -45,12 +60,41 @@ export function Hero() {
4560
);
4661
}
4762

48-
function CopyArea(props: { code: string }) {
63+
function PmInstall(props: { activePm: PmName }) {
64+
const activePm = PM_NAMES.includes(props.activePm) ? props.activePm : "npm";
4965
return (
50-
<div class="bg-slate-800 rounded-sm text-green-100 flex items-center min-w-0 overflow-x-auto">
51-
<pre class="overflow-x-auto w-full flex-1 px-6 py-4">{props.code}</pre>
52-
53-
<CopyButton code={props.code} />
66+
<div class="pm-tabs w-full max-w-md text-left" data-pm-tabs>
67+
<div class="pm-tabs-bar" role="tablist">
68+
{PM_NAMES.map((pm) => {
69+
const active = pm === activePm;
70+
return (
71+
<button
72+
type="button"
73+
role="tab"
74+
data-pm={pm}
75+
aria-selected={active}
76+
class={`pm-tab${active ? " pm-tab-active" : ""}`}
77+
>
78+
<img src={`/logos/${pm}.svg`} alt="" width={18} height={18} />
79+
{PM_LABELS[pm]}
80+
</button>
81+
);
82+
})}
83+
</div>
84+
{PM_NAMES.map((pm) => {
85+
const active = pm === activePm;
86+
const code = PM_COMMANDS[pm];
87+
return (
88+
<div class={`fenced-code pm-block${active ? " pm-active" : ""}`} data-pm={pm}>
89+
<div class="pm-block-copy text-green-100">
90+
<CopyButton code={code} />
91+
</div>
92+
<pre class="overflow-x-auto px-6 py-4 m-0 rounded-none bg-slate-800 text-green-100 text-left">
93+
{code}
94+
</pre>
95+
</div>
96+
);
97+
})}
5498
</div>
5599
);
56100
}

www/routes/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ import { FormsSection } from "../components/homepage/FormsSection.tsx";
1212
import { SocialProof } from "../components/homepage/SocialProof.tsx";
1313
import { MoreFeatures } from "../components/homepage/MoreFeatures.tsx";
1414
import { RuntimeSection } from "../components/homepage/RuntimeSection.tsx";
15+
import { parsePmCookie } from "../utils/markdown.ts";
1516
export const handlers = handler({
1617
GET(ctx) {
1718
const accept = ctx.req.headers.get("accept");
1819
const userAgent = ctx.req.headers.get("user-agent");
1920
if (userAgent?.includes("Deno/") && !accept?.includes("text/html")) {
2021
return ctx.redirect(`https://deno.land/x/fresh@${VERSIONS[0]}/init.ts`, 307);
2122
}
22-
return { data: { topic: ctx.url.searchParams.get("topic") } };
23+
return {
24+
data: {
25+
topic: ctx.url.searchParams.get("topic"),
26+
activePm: parsePmCookie(ctx.req.headers.get("cookie")),
27+
},
28+
};
2329
},
2430
async POST(ctx) {
2531
const form = await ctx.req.formData();
@@ -29,7 +35,7 @@ export const handlers = handler({
2935
});
3036

3137
export default page(function MainPage(props) {
32-
const topic = props.data.topic;
38+
const { topic, activePm } = props.data;
3339
return (
3440
<div class="flex flex-col min-h-screen bg-white">
3541
<Seo
@@ -42,7 +48,7 @@ export default page(function MainPage(props) {
4248
<Header title="" active="/" />
4349
</div>
4450
<div class="flex flex-col -mt-20 relative">
45-
<Hero />
51+
<Hero activePm={activePm} />
4652
<h2 class="text-3xl sm:text-4xl md:text-5xl text-gray-600 font-extrabold text-center mt-8 md:mt-12 lg:mt-16 px-4 italic">
4753
Take a tour of Fresh
4854
</h2>

0 commit comments

Comments
 (0)