Skip to content

Commit fe761b4

Browse files
authored
fix: add missing CodeSdkTabs component (#169)
The deploy workflow fails after 0xMiden/tutorials#159 was merged because the tutorial pages import `CodeSdkTabs` from `@site/src/components`, which resolves to this repo's `src/components/index.ts` — and that file only exported `CodeTabs`. Copy `CodeSdkTabs.tsx` and `CodeSdkTabs.module.css` from miden-tutorials and re-export from `src/components/index.ts`.
1 parent af7c5f8 commit fe761b4

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* CodeSdkTabs Component Styles */
2+
3+
.codeContainer {
4+
margin: 1rem 0;
5+
border: 1px solid var(--ifm-color-emphasis-300);
6+
border-radius: var(--ifm-global-radius);
7+
overflow: hidden;
8+
background: var(--ifm-background-color);
9+
}
10+
11+
.tabContainer {
12+
background: var(--ifm-color-emphasis-100);
13+
border-bottom: 1px solid var(--ifm-color-emphasis-300);
14+
}
15+
16+
.tabButtons {
17+
display: flex;
18+
gap: 0;
19+
}
20+
21+
.tabButton {
22+
display: flex;
23+
align-items: center;
24+
gap: 0.5rem;
25+
padding: 0.75rem 1rem;
26+
border: none;
27+
background: transparent;
28+
color: var(--ifm-color-content-secondary);
29+
cursor: pointer;
30+
font-size: 0.875rem;
31+
font-weight: 500;
32+
transition: all 0.2s ease;
33+
border-bottom: 2px solid transparent;
34+
}
35+
36+
.tabButton:hover {
37+
color: var(--ifm-color-primary);
38+
background: var(--ifm-color-emphasis-200);
39+
}
40+
41+
.tabButton.active {
42+
color: var(--ifm-color-primary);
43+
background: var(--ifm-color-emphasis-200);
44+
border-bottom: 2px solid var(--ifm-color-primary);
45+
}
46+
47+
.codeSection {
48+
position: relative;
49+
margin: 0;
50+
}
51+
52+
.codeSection pre {
53+
margin: 0;
54+
border-radius: 0;
55+
border: none;
56+
}
57+
58+
/* Remove any extra bottom spacing from the theme code block */
59+
.codeSection :global(.theme-code-block) {
60+
margin-bottom: 0 !important;
61+
}
62+
63+
.outputSection {
64+
border-top: 1px solid var(--ifm-color-emphasis-300);
65+
background: var(--ifm-color-emphasis-50);
66+
}
67+
68+
.outputHeader {
69+
padding: 0.5rem 1rem;
70+
font-size: 0.875rem;
71+
font-weight: 600;
72+
color: var(--ifm-color-content-secondary);
73+
background: var(--ifm-color-emphasis-100);
74+
border-bottom: 1px solid var(--ifm-color-emphasis-200);
75+
}
76+
77+
.outputSection pre {
78+
margin: 0;
79+
border-radius: 0;
80+
border: none;
81+
background: var(--ifm-color-emphasis-50) !important;
82+
}

src/components/CodeSdkTabs.tsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React, { useState } from "react";
2+
import CodeBlock from "@theme/CodeBlock";
3+
import styles from "./CodeSdkTabs.module.css";
4+
5+
interface CodeExample {
6+
react?: {
7+
code: string;
8+
output?: string;
9+
};
10+
typescript?: {
11+
code: string;
12+
output?: string;
13+
};
14+
}
15+
16+
interface CodeSdkTabsProps {
17+
example: CodeExample;
18+
reactFilename?: string;
19+
tsFilename?: string;
20+
}
21+
22+
// Dot-indentation convention for CodeSdkTabs
23+
// ─────────────────────────────────────────────
24+
// MDX/webpack strips leading whitespace from template literals inside JSX props.
25+
// To preserve indentation in code snippets, use leading dots in the markdown
26+
// source. Each dot represents one indent level (2 spaces).
27+
//
28+
// Example in a .md file:
29+
// typescript: { code: `export function foo() {
30+
// .const x = 1;
31+
// .if (x) {
32+
// ..console.log(x);
33+
// .}
34+
// }` }
35+
//
36+
// Renders as:
37+
// export function foo() {
38+
// const x = 1;
39+
// if (x) {
40+
// console.log(x);
41+
// }
42+
// }
43+
//
44+
// Rules:
45+
// 0 dots → top-level declarations (export, import, closing braces)
46+
// 1 dot → first level inside a function/block body
47+
// 2 dots → second level (nested blocks, function call arguments)
48+
// 3+ dots → deeper nesting
49+
function preserveIndent(code: string): string {
50+
return code.replace(/^(\.+)/gm, (match) => ' '.repeat(match.length));
51+
}
52+
53+
export default function CodeSdkTabs({
54+
example,
55+
reactFilename = "index.tsx",
56+
tsFilename = "index.ts",
57+
}: CodeSdkTabsProps): JSX.Element {
58+
const [activeTab, setActiveTab] = useState<"react" | "typescript">(
59+
example.react ? "react" : "typescript"
60+
);
61+
62+
const hasReact = !!example.react;
63+
const hasTypeScript = !!example.typescript;
64+
65+
// Infer syntax language from filename extension (.tsx → tsx, .ts → ts)
66+
const langFor = (filename: string, fallback: string) =>
67+
filename.endsWith(".tsx") ? "tsx" : filename.endsWith(".ts") ? "ts" : fallback;
68+
69+
// Don't show tabs if there's only one language
70+
if (!hasReact || !hasTypeScript) {
71+
const singleLang = hasReact ? "react" : "typescript";
72+
const singleExample = example[singleLang];
73+
const filename = singleLang === "react" ? reactFilename : tsFilename;
74+
75+
return (
76+
<div className={styles.codeContainer}>
77+
<div className={styles.codeSection}>
78+
<CodeBlock
79+
language={langFor(filename, singleLang === "react" ? "tsx" : "ts")}
80+
title={filename}
81+
>
82+
{preserveIndent(singleExample!.code)}
83+
</CodeBlock>
84+
</div>
85+
{singleExample!.output && (
86+
<div className={styles.outputSection}>
87+
<div className={styles.outputHeader}>Output</div>
88+
<CodeBlock language="bash">{singleExample.output}</CodeBlock>
89+
</div>
90+
)}
91+
</div>
92+
);
93+
}
94+
95+
const currentExample = example[activeTab];
96+
const activeFilename = activeTab === "react" ? reactFilename : tsFilename;
97+
98+
return (
99+
<div className={styles.codeContainer}>
100+
<div className={styles.tabContainer}>
101+
<div className={styles.tabButtons}>
102+
<button
103+
className={`${styles.tabButton} ${
104+
activeTab === "react" ? styles.active : ""
105+
}`}
106+
onClick={() => setActiveTab("react")}
107+
>
108+
React
109+
</button>
110+
<button
111+
className={`${styles.tabButton} ${
112+
activeTab === "typescript" ? styles.active : ""
113+
}`}
114+
onClick={() => setActiveTab("typescript")}
115+
>
116+
TypeScript
117+
</button>
118+
</div>
119+
</div>
120+
121+
<div className={styles.codeSection}>
122+
<CodeBlock
123+
language={langFor(activeFilename, activeTab === "react" ? "tsx" : "ts")}
124+
title={activeFilename}
125+
>
126+
{preserveIndent(currentExample!.code)}
127+
</CodeBlock>
128+
</div>
129+
130+
{currentExample!.output && (
131+
<div className={styles.outputSection}>
132+
<div className={styles.outputHeader}>Output</div>
133+
<CodeBlock language="bash">{currentExample.output}</CodeBlock>
134+
</div>
135+
)}
136+
</div>
137+
);
138+
}

src/components/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export { default as CodeTabs } from './CodeTabs';
1+
export { default as CodeTabs } from './CodeTabs';
2+
export { default as CodeSdkTabs } from './CodeSdkTabs';

0 commit comments

Comments
 (0)