Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"next-intl": "^3.26.3",
"next-mdx-remote": "^5.0.0",
"next-themes": "^0.3.0",
"prism-react-renderer": "1.1.0",
"prism-react-renderer": "2.4.1",
"prismjs": "^1.30.0",
"react": "^19.2.4",
"react-chartjs-2": "^5.2.0",
Expand Down
17 changes: 12 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

250 changes: 49 additions & 201 deletions src/components/Codeblock.tsx
Original file line number Diff line number Diff line change
@@ -1,196 +1,42 @@
"use client"

import React, { useState } from "react"
import { Clipboard, ClipboardCheck } from "lucide-react"
import Highlight, {
defaultProps,
Language,
PrismTheme,
} from "prism-react-renderer"
import Prism from "prism-react-renderer/prism"
import { useTheme } from "next-themes"
import { Highlight, Prism, type PrismTheme, themes } from "prism-react-renderer"

// https://github.com/FormidableLabs/prism-react-renderer/tree/master#custom-language-support
import CopyToClipboard from "@/components/CopyToClipboard"
import { Button } from "@/components/ui/buttons/Button"
import { Flex } from "@/components/ui/flex"

import { cn } from "@/lib/utils/cn"

import { LINES_BEFORE_COLLAPSABLE } from "@/lib/constants"

import useColorModeValue from "@/hooks/useColorModeValue"
import { useTranslation } from "@/hooks/useTranslation"

// Register Solidity language support
// https://github.com/FormidableLabs/prism-react-renderer#custom-language-support
;(typeof global !== "undefined" ? global : window).Prism = Prism
require("prismjs/components/prism-solidity")

const TopBarItem = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
className={cn(
"ms-2 rounded border px-2 py-1 shadow-[1px_1px_8px_1px_rgba(var(--black),_0.5)] transition-transform duration-100",
"hover:scale-105 hover:cursor-pointer hover:bg-gray-200 hover:shadow-md hover:transition-transform hover:duration-100",
"bg-background-highlight hover:bg-background",
className
)}
{...props}
/>
)
}
const makeTransparent = (theme: PrismTheme): PrismTheme => ({
...theme,
plain: { ...theme.plain, backgroundColor: "transparent" },
})

const codeTheme = {
light: {
styles: [
{
style: { color: "#6c6783" },
types: ["comment", "prolog", "doctype", "cdata", "punctuation"],
},
{
style: { opacity: 0.7 },
types: ["namespace"],
},
{
style: { color: "#e09142" },
types: ["tag", "operator", "number"],
},
{
style: { color: "#ff7324" },
types: ["property", "function"],
},
{
style: { color: "#888888" },
types: ["tag-id", "selector", "atrule-id"],
},
{
style: { color: "#474b5e" },
types: ["attr-name"],
},
{
style: { color: "#498bb5" },
types: [
"boolean",
"string",
"entity",
"url",
"attr-value",
"keyword",
"control",
"directive",
"unit",
"statement",
"regex",
"at-rule",
"placeholder",
"variable",
],
},
{
style: { textDecorationLine: "line-through" },
types: ["deleted"],
},
{
style: { textDecorationLine: "underline" },
types: ["inserted"],
},
{
style: { fontStyle: "italic" },
types: ["italic"],
},
{
style: { fontWeight: "bold" },
types: ["important", "bold"],
},
{
style: { color: "#c4b9fe" },
types: ["important"],
},
],
},
dark: {
// Pulled from `defaultProps.theme` for potential customization
styles: [
{
style: { color: "#6c6783" },
types: ["comment", "prolog", "doctype", "cdata", "punctuation"],
},
{
style: { opacity: 0.7 },
types: ["namespace"],
},
{
style: { color: "#e09142" },
types: ["tag", "operator", "number"],
},
{
style: { color: "#9a86fd" },
types: ["property", "function"],
},
{
style: { color: "#eeebff" },
types: ["tag-id", "selector", "atrule-id"],
},
{
style: { color: "#c4b9fe" },
types: ["attr-name"],
},
{
style: { color: "#ffcc99" },
types: [
"boolean",
"string",
"entity",
"url",
"attr-value",
"keyword",
"control",
"directive",
"unit",
"statement",
"regex",
"at-rule",
"placeholder",
"variable",
],
},
{
style: { textDecorationLine: "line-through" },
types: ["deleted"],
},
{
style: { textDecorationLine: "underline" },
types: ["inserted"],
},
{
style: { fontStyle: "italic" },
types: ["italic"],
},
{
style: { fontWeight: "bold" },
types: ["important", "bold"],
},
{
style: { color: "#c4b9fe" },
types: ["important"],
},
],
},
}
const lightTheme = makeTransparent(themes.oneLight)
const darkTheme = makeTransparent(themes.duotoneDark)

const getValidChildrenForCodeblock = (child) => {
const getValidChildrenForCodeblock = (child: unknown): string | undefined => {
try {
if (typeof child !== "string") {
return getValidChildrenForCodeblock(child.props.children)
const element = child as React.ReactElement<{ children: unknown }>
return getValidChildrenForCodeblock(element.props.children)
} else {
return child
}
} catch (e) {
/*For now available: code without wrappers like div
* example:
* <Codeblock codeLanguage="language-js">
const web3 = new Web3("wss://eth-mainnet.ws.alchemyapi.io/ws/your-api-key"){"\n"}
web3.eth.getBlockNumber().then(console.log)
</Codeblock>
* */
} catch {
console.error(`Codeblock children is not valid`)
}
}
Expand All @@ -209,7 +55,8 @@ const Codeblock = ({
className,
}: CodeblockProps) => {
const { t } = useTranslation("common")
const selectedTheme = useColorModeValue(codeTheme.light, codeTheme.dark)
const { resolvedTheme } = useTheme()
const codeTheme = resolvedTheme === "dark" ? darkTheme : lightTheme

const codeText = React.Children.toArray(children)
.map((child) => {
Expand Down Expand Up @@ -244,35 +91,28 @@ const Codeblock = ({
/* Context: https://github.com/ethereum/ethereum-org-website/issues/6202 */
<div className={cn("relative", className)} dir="ltr">
<div
className="overflow-scroll rounded bg-background-highlight text-primary"
className="overflow-auto rounded bg-background-highlight text-primary"
style={{
maxHeight: isCollapsed
? `calc((1.2rem * ${LINES_BEFORE_COLLAPSABLE}) + 4.185rem)`
: "none",
}}
>
<Highlight
{...defaultProps}
code={codeText}
language={language as Language}
theme={selectedTheme as PrismTheme}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<Highlight code={codeText} language={language} theme={codeTheme}>
{({ style, tokens, getLineProps, getTokenProps }) => (
<pre
className={cn(
"m-0 w-fit min-w-full overflow-visible py-6 ps-4",
hasTopBar && "pt-[2.75rem]",
className
hasTopBar && "pt-[2.75rem]"
)}
style={style}
>
{tokens.map((line, i) => {
return i === tokens.length - 1 &&
line[0].content === "" ? null : (
return i === tokens.length - 1 && line[0].empty ? null : (
<div
key={i}
style={{ display: "table-row" }}
{...getLineProps({ line, key: i })}
{...getLineProps({ line })}
>
{shouldShowLineNumbers && (
<span className="table-cell select-none pe-8 text-end opacity-40">
Expand All @@ -281,40 +121,48 @@ const Codeblock = ({
)}
<span className="table-cell">
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
<span key={key} {...getTokenProps({ token })} />
))}
</span>
</div>
)
})}
{!fromHomepage && (
<Flex
className={cn("absolute end-4 top-3 justify-end", className)}
>
<Flex className="absolute end-4 top-3 justify-end gap-2">
{allowCollapse &&
totalLines - 1 > LINES_BEFORE_COLLAPSABLE && (
<TopBarItem onClick={() => setIsCollapsed(!isCollapsed)}>
<Button
variant="outline"
className="bg-background-highlight"
size="sm"
onClick={() => setIsCollapsed(!isCollapsed)}
>
{isCollapsed ? t("show-all") : t("show-less")}
</TopBarItem>
</Button>
)}
{shouldShowCopyWidget && (
<CopyToClipboard text={codeText}>
{(isCopied) => (
<TopBarItem>
{!isCopied ? (
<Button
variant="outline"
size="sm"
asChild
className="bg-background-highlight"
>
<CopyToClipboard text={codeText}>
{(isCopied) =>
!isCopied ? (
<>
{t("copy")}
<Clipboard className="mb-1 ms-1 inline-block size-[1em]" />
<Clipboard className="size-[1em]" />
</>
) : (
<>
{t("copied")}
<ClipboardCheck className="mb-1 ms-1 inline-block size-[1em]" />
<ClipboardCheck className="size-[1em]" />
</>
)}
</TopBarItem>
)}
</CopyToClipboard>
)
}
</CopyToClipboard>
</Button>
)}
</Flex>
)}
Expand Down
Loading