Skip to content

Commit aa09c7e

Browse files
committed
skeleton
1 parent 2dde424 commit aa09c7e

14 files changed

+713
-175
lines changed

package-lock.json

Lines changed: 94 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@
5959
"zod": ">= 3"
6060
},
6161
"peerDependenciesMeta": {
62-
"@tanstack/react-query": {"optional":true},
63-
"react": {"optional":true},
64-
"react-dom": {"optional":true}
62+
"@tanstack/react-query": {
63+
"optional": true
64+
},
65+
"react": {
66+
"optional": true
67+
},
68+
"react-dom": {
69+
"optional": true
70+
}
6571
},
6672
"devDependencies": {
6773
"@eslint/js": "^9.19.0",
@@ -77,6 +83,9 @@
7783
"dependencies": {
7884
"@emotion/css": "^11.13.5",
7985
"codehike": "^1.0.4",
80-
"devicons-react": "^1.4.0"
86+
"devicons-react": "^1.4.0",
87+
"highlight.js": "^11.11.1",
88+
"highlightjs": "^9.16.2",
89+
"motion": "^12.4.5"
8190
}
8291
}

src/react-components/code-playground.tsx

Lines changed: 91 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
import { css } from "@emotion/css";
22
import { Pre } from "codehike/code";
3-
import { UsageSnippet } from "../models/components/usagesnippet.js";
4-
import { NonEmptyArray } from "../types/custom.js";
3+
import { useMemo } from "react";
4+
import {
5+
GetCodeSamplesRequest,
6+
MethodPaths,
7+
} from "../models/operations/getcodesamples.js";
8+
import {
9+
useHighlightedCodeSamples,
10+
useSafeSpeakeasyCodeSamplesContext,
11+
useSelectedSnippet,
12+
} from "./hooks.js";
13+
import { LanguageSelector } from "./language-selector.js";
14+
import { lineNumbers } from "./line-numbers.js";
15+
import {
16+
LanguageSelectorSkeleton,
17+
LoadingSkeleton,
18+
TitleSkeleton,
19+
} from "./skeleton.js";
20+
import {
21+
cssVarKey,
22+
getCssVars,
23+
githubColorVars,
24+
useSystemColorMode,
25+
} from "./styles.js";
526
import { CodeSampleTitleComponent } from "./titles.js";
6-
import { prettyLanguageName } from "./utils.js";
7-
import { cssVarKey, getCssVars } from "./styles.js";
8-
import { useCodeHighlighting, useSelectedSnippet } from "./hooks.js";
27+
import { tokenTransitions } from "./token-transitions.js";
928

1029
const classes = {
1130
root: css({
@@ -24,87 +43,100 @@ const classes = {
2443
}),
2544
selector: css({}),
2645
codeContainer: css({
46+
position: "relative",
2747
paddingInline: "0.75rem",
48+
overflowX: "scroll",
49+
}),
50+
pre: css({
51+
position: "relative",
2852
}),
2953
};
3054

55+
type OperationId = string;
56+
3157
export type CodePlaygroundProps = {
32-
snippets: NonEmptyArray<UsageSnippet>;
33-
theme?: "dark" | "light";
58+
theme?: "system" | "dark" | "light";
3459
className?: string | undefined;
3560
title?: CodeSampleTitleComponent;
61+
operation: MethodPaths | OperationId;
3662
};
3763

3864
export function CodePlayground({
39-
snippets,
40-
theme = "light",
65+
theme = "system",
4166
className,
4267
title,
68+
operation,
4369
}: CodePlaygroundProps) {
4470
const TitleComponent = title;
4571

46-
const { selectedSnippet, selectedLang, setSelectedLang } =
47-
useSelectedSnippet(snippets);
72+
const systemColorMode = useSystemColorMode();
4873

49-
//const [selectedLang, setSelectedLang] = useState<string>(
50-
// snippets[0].language,
51-
//);
52-
//
53-
//const selectedSnippet: UsageSnippet = useMemo(() => {
54-
// const snippet = snippets.find((s) => s.language === selectedLang);
55-
// if (!snippet)
56-
// throw Error(`The selected language ${selectedLang} does not exist`);
57-
// return snippet;
58-
//}, [selectedLang]);
59-
//
74+
const codeTheme = useMemo(() => {
75+
if (theme === "system") return githubColorVars[systemColorMode];
76+
return githubColorVars[theme];
77+
}, [theme, systemColorMode]);
6078

61-
const highlighted = useCodeHighlighting(
62-
selectedSnippet.code,
63-
selectedLang,
64-
theme,
65-
);
79+
const request: GetCodeSamplesRequest = useMemo(() => {
80+
if (typeof operation === "string") return { operationIds: [operation] };
81+
return { methoPaths: [operation] };
82+
}, [operation]);
83+
84+
const client = useSafeSpeakeasyCodeSamplesContext();
85+
const { status, data, error } = useHighlightedCodeSamples(client, request);
86+
87+
const { selectedSnippet, selectedLang, setSelectedLang } =
88+
useSelectedSnippet(data);
6689

67-
//const [highlighted, setHighlighted] = useState<HighlightedCode | null>(null);
68-
//
69-
//const updateHighlighted = useCallback(
70-
// async (code: string, language: string, theme: CodeHikeTheme) => {
71-
// const highlighted = await highlightCode(code, language, theme);
72-
// setHighlighted(highlighted);
73-
// },
74-
// [],
75-
//);
90+
const longestCodeHeight = useMemo(() => {
91+
const largestLines = Math.max(
92+
...Object.values(data ?? [])
93+
.filter((snippet) => snippet.code !== undefined)
94+
.map((code) => code.code!.split("\n").length),
95+
);
7696

77-
//useEffect(() => {
78-
// const chTheme = theme === "dark" ? "github-dark" : "github-light";
79-
// updateHighlighted(selectedSnippet.code, selectedLang, chTheme);
80-
//}, [selectedSnippet, selectedLang, theme]);
81-
//
82-
//useEffect(() => {
83-
// const chTheme = theme === "dark" ? "github-dark" : "github-light";
84-
// updateHighlighted(selectedSnippet.code, selectedLang, chTheme);
85-
//}, [selectedLang, theme]);
97+
const lineHeight = 23;
98+
const padding = 12;
99+
return largestLines * lineHeight + padding * 2;
100+
}, [data]);
86101

87102
return (
88103
<div
89-
style={{ ...(getCssVars(theme) as React.CSSProperties) }}
104+
style={{
105+
...codeTheme,
106+
...(getCssVars(
107+
theme === "system" ? systemColorMode : theme,
108+
) as React.CSSProperties),
109+
}}
90110
className={`${classes.root} ${className}`}
91111
>
92112
<div className={classes.heading}>
93-
{TitleComponent ? <TitleComponent {...selectedSnippet} /> : <div></div>}
94-
<select
95-
value={selectedLang}
96-
onChange={(e) => setSelectedLang(e.target.value)}
97-
className={classes.selector}
98-
>
99-
{snippets.map(({ language }, index) => (
100-
<option key={index} value={language}>
101-
{prettyLanguageName(language)}
102-
</option>
103-
))}
104-
</select>
113+
{status === "loading" && error === undefined ? (
114+
<TitleSkeleton />
115+
) : TitleComponent && selectedSnippet ? (
116+
<TitleComponent {...selectedSnippet!.raw} />
117+
) : null}
118+
{status === "loading" && error === undefined ? (
119+
<LanguageSelectorSkeleton />
120+
) : (
121+
<LanguageSelector
122+
value={selectedLang}
123+
onChange={setSelectedLang}
124+
snippets={data ?? []}
125+
className={classes.selector}
126+
/>
127+
)}
105128
</div>
106129
<div className={classes.codeContainer}>
107-
{highlighted && <Pre code={highlighted}></Pre>}
130+
{status === "loading" ? (
131+
<LoadingSkeleton />
132+
) : selectedSnippet ? (
133+
<Pre
134+
className={classes.pre}
135+
style={{ height: longestCodeHeight }}
136+
handlers={[lineNumbers, tokenTransitions]}
137+
code={selectedSnippet}
138+
/>
139+
) : null}
108140
</div>
109141
</div>
110142
);

0 commit comments

Comments
 (0)