Skip to content

Commit d22dc31

Browse files
committed
feat: add setup for examples
1 parent 43678b3 commit d22dc31

File tree

4 files changed

+74
-11
lines changed

4 files changed

+74
-11
lines changed

src/client/App.tsx

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import {
2020
} from "@/client/components/Tooltip";
2121
import { useTheme } from "@/client/contexts/theme";
2222
import { defaultCode } from "@/client/snippets";
23+
import { examples } from "@/examples";
2324
import type {
2425
ParameterWithSource,
2526
PreviewOutput,
2627
WorkspaceOwner,
2728
} from "@/gen/types";
29+
import { mockUsers } from "@/owner";
2830
import { rpc } from "@/utils/rpc";
2931
import {
3032
type WasmLoadState,
@@ -36,7 +38,6 @@ import { MoonIcon, ShareIcon, SunIcon, SunMoonIcon } from "lucide-react";
3638
import { type FC, useEffect, useMemo, useRef, useState } from "react";
3739
import { type LoaderFunctionArgs, useLoaderData } from "react-router";
3840
import { useDebouncedValue } from "./hooks/debounce";
39-
import { mockUsers } from "@/owner";
4041

4142
/**
4243
* Load the shared code if present.
@@ -67,7 +68,9 @@ export const App = () => {
6768
return "loading";
6869
});
6970
const loadedCode = useLoaderData<typeof loader>();
70-
const [code, setCode] = useState(loadedCode ?? defaultCode);
71+
const [code, setCode] = useState(
72+
loadedCode ?? window.EXAMPLE_CODE ?? defaultCode,
73+
);
7174
const [debouncedCode, isDebouncing] = useDebouncedValue(code, 1000);
7275
const [parameterValues, setParameterValues] = useState<
7376
Record<string, string>
@@ -203,14 +206,7 @@ export const App = () => {
203206
>
204207
Docs
205208
</a>
206-
<a
207-
href="https://coder.com"
208-
target="_blank"
209-
rel="noreferrer"
210-
className="font-light text-content-secondary text-sm hover:text-content-primary"
211-
>
212-
Support
213-
</a>
209+
<ExampleSelector />
214210
<ThemeSelector />
215211
</div>
216212
</nav>
@@ -330,3 +326,31 @@ const ShareButton: FC<ShareButtonProps> = ({ code }) => {
330326
</Tooltip>
331327
);
332328
};
329+
330+
const ExampleSelector: FC = () => {
331+
return (
332+
<DropdownMenu>
333+
<DropdownMenuTrigger className="font-light text-content-secondary text-sm hover:text-content-primary">
334+
Examples
335+
</DropdownMenuTrigger>
336+
337+
<DropdownMenuPortal>
338+
<DropdownMenuContent>
339+
{examples.map(({ title, slug }) => {
340+
const params = new URLSearchParams();
341+
params.append("example", slug);
342+
343+
const href = `${window.location.origin}/parameters?${params.toString()}`;
344+
return (
345+
<DropdownMenuItem key={slug} asChild={true}>
346+
<a href={href} target="_blank" rel="noreferrer">
347+
{title}
348+
</a>
349+
</DropdownMenuItem>
350+
);
351+
})}
352+
</DropdownMenuContent>
353+
</DropdownMenuPortal>
354+
</DropdownMenu>
355+
);
356+
};

src/examples.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
type Example = {
2+
title: string;
3+
slug: string;
4+
code: string;
5+
};
6+
7+
export const examples: Example[] = [
8+
{
9+
title: "Example 1",
10+
slug: "example-1",
11+
code: "// Example 1",
12+
},
13+
{
14+
title: "Example 2",
15+
slug: "example-2",
16+
code: "// Example 2",
17+
},
18+
{
19+
title: "Example 3",
20+
slug: "example-4",
21+
code: "// Example 3",
22+
},
23+
];

src/server/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { examples } from "@/examples";
12
import { api } from "@/server/api";
23
import { Hono } from "hono";
34
import { renderToString } from "react-dom/server";
@@ -17,6 +18,15 @@ app.route("/api", api);
1718

1819
// Serves the main web application. This must come after the API route.
1920
app.get("*", (c) => {
21+
const getExampleCode = () => {
22+
const { example } = c.req.query();
23+
if (!example) {
24+
return;
25+
}
26+
27+
return examples.find((e) => e.slug === example)?.code;
28+
};
29+
2030
// Along with the vite React plugin this enables HMR within react while
2131
// running the dev server.
2232
const { url } = c.req;
@@ -45,6 +55,9 @@ app.get("*", (c) => {
4555
: "/wasm_exec.js";
4656
const iconPath = import.meta.env.PROD ? "/assets/logo.svg" : "/logo.svg";
4757

58+
const exampleCode = getExampleCode();
59+
const loadExampleCodeScript = `window.EXAMPLE_CODE = "${exampleCode}"`;
60+
4861
return c.html(
4962
[
5063
"<!doctype html>",
@@ -64,6 +77,9 @@ app.get("*", (c) => {
6477
</head>
6578
<body>
6679
<div id="root"></div>
80+
{exampleCode ? (
81+
<script type="module">{loadExampleCodeScript}</script>
82+
) : null}
6783
<script type="module" src={clientScriptPath}></script>
6884
</body>
6985
</html>,

src/utils/wasm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ declare global {
2020
// Loaded from wasm
2121
go_preview?: GoPreviewDef;
2222
Go: { new (): Go };
23-
CODE?: string;
23+
EXAMPLE_CODE?: string;
2424
}
2525
}
2626

0 commit comments

Comments
 (0)