Explorar el Código

Merge pull request #1791 from cprussin/chore-api-reference-refactor

Update get-price description
Connor Prussin hace 1 año
padre
commit
6fbcb1948d

+ 1 - 1
apps/api-reference/package.json

@@ -8,7 +8,7 @@
   },
   "scripts": {
     "build": "next build",
-    "fix": "pnpm fix:format && pnpm fix:lint",
+    "fix": "pnpm fix:lint && pnpm fix:format",
     "fix:format": "prettier --write .",
     "fix:lint": "eslint --fix .",
     "pull:env": "VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_gbljYVzp0m5EpCuOF6nZpM4WMFM6 vercel env pull",

+ 29 - 37
apps/api-reference/src/apis/evm/get-price.ts

@@ -5,47 +5,39 @@ export const getPrice = readApi<"id">({
   name: "getPrice",
   summary: "Get the **latest** price object for the requested price feed ID.",
   description: `
-  This method returns the latest price object for the requested price feed ID.
+This method returns the latest price object for the requested price feed ID.
 
-  The \`Price\` object contains the following fields:
-  1. \`price\`: The price of the asset.
-  2. \`conf\`: The confidence level of the price.
-  3. \`expo\`: The exponent of the price.
-  4. \`publishTime\`: The timestamp of the price update.
+The \`price\` object contains the following fields:
+1. \`price\`: The latest price of the price feed.
+2. \`conf\`: The confidence level of the price feed.
+3. \`expo\`: The exponent of the price feed.
+4. \`publishtime\`: The time when the price feed was last updated.
 
-  Sample \`Price\` object:
-  \`\`\`json
-  {
-    "price": "1234",
-    "conf": "1000",
-    "expo": "-2",
-    "publishTime": 1626892800
-  }
-  \`\`\`
-Get the latest price and confidence interval for the requested price feed id.
-The price feed id is a 32-byte id written as a hexadecimal string; see the
-[price feed ids](https://pyth.network/developers/price-feed-ids) page to look up
-the id for a given symbol. The returned price and confidence are decimal numbers
-written in the form \`a * 10^e\`, where \`e\` is an exponent included in the
-result. For example, a price of 1234 with an exponent of -2 represents the
-number 12.34. The result also includes a \`publishTime\` which is the unix
-timestamp for the price update.
+Sample \`price\` object:
+\`\`\`json
+{
+    price: 123456789,
+    conf: 180726074,
+    expo: -8,
+    publishTime: 1721765108
+}
+\`\`\`
 
-This function reverts with a \`StalePrice\` error if the on-chain price has not
-been updated within the last [getValidTimePeriod()](getValidTimePeriod)
-seconds. The default valid time period is set to a reasonable default on each
-chain and is typically around 1 minute. Call
-[updatePriceFeeds](updatePriceFeeds) to pull a fresh price on-chain and solve
-this problem. If you would like to configure the valid time period, see
-[getPriceNoOlderThan](getPriceNoOlderThan). If you want the latest price
-regardless of when it was updated, see [getPriceUnsafe](getPriceUnsafe).
+The price above is in the format of \`price * 10^expo\`. So, the price in above
+mentioned sample represents the number \`123456789 * 10(-8) = 1.23456789\` in
+this case.
 
-This function reverts with a \`PriceFeedNotFound\` error if the requested feed
-id has never received a price update. This error could either mean that the
-provided price feed id is incorrect, or (more typically) that this is the first
-attempted use of that feed on-chain. In the second case, calling
-[updatePriceFeeds](updatePriceFeeds) will solve this problem.
-  `,
+### Error Response
+
+The above method returns the following error response:
+- \`StalePrice\`: The on-chain price has not been updated within the last
+  [\`getValidTimePeriod()\`](getValidTimePeriod) seconds. Try calling
+  [\`updatePriceFeeds()\`](updatePriceFeeds) to update the price feed with the
+  latest price.
+- \`PriceFeedNotFound\`: The requested price feed has never received a price
+  update or does not exist. Try calling
+  [\`updatePriceFeeds()\`](updatePriceFeeds) to update the price feed.
+`,
   parameters: [
     {
       name: "id",

+ 4 - 4
apps/api-reference/src/components/Code/index.tsx

@@ -5,16 +5,16 @@ import { useMemo, useCallback, type HTMLAttributes } from "react";
 import { useEffect, useState } from "react";
 import type { OffsetOrPosition } from "shiki";
 
-import type { SupportedLanguage } from "./shiki";
 import style from "./style.module.css";
+import type { SupportedLanguage } from "./supported-language";
 import { useHighlightedCode } from "./use-highlighted-code";
 import { getLogger } from "../../browser-logger";
 import { Button } from "../Button";
 
-export type { SupportedLanguage } from "./shiki";
+export * from "./supported-language";
 
 type CodeProps = {
-  language: SupportedLanguage;
+  language?: SupportedLanguage | undefined;
   children: string;
   dimRange?: readonly [OffsetOrPosition, OffsetOrPosition] | undefined;
 };
@@ -113,7 +113,7 @@ const CopyButton = ({ children, className, ...props }: CopyButtonProps) => {
 };
 
 type HighlightedCodeProps = Omit<HTMLAttributes<HTMLElement>, "children"> & {
-  language: SupportedLanguage;
+  language?: SupportedLanguage | undefined;
   children: string;
   dimRange?: readonly [OffsetOrPosition, OffsetOrPosition] | undefined;
 };

+ 8 - 7
apps/api-reference/src/components/Code/shiki.ts

@@ -4,35 +4,36 @@ import {
   getHighlighterCore as shikiGetHighlighterCore,
 } from "shiki/core";
 import javascript from "shiki/langs/javascript.mjs";
+import json from "shiki/langs/json.mjs";
 import solidity from "shiki/langs/solidity.mjs";
 import darkPlus from "shiki/themes/dark-plus.mjs";
 import lightPlus from "shiki/themes/light-plus.mjs";
 import loadWasm from "shiki/wasm";
 
+import type { SupportedLanguage } from "./supported-language";
+
 export type Highlighter = {
   highlight: (
-    lang: SupportedLanguage,
+    lang: SupportedLanguage | undefined,
     code: string,
     options?: HighlightOptions | undefined,
   ) => string;
 };
 
-export type SupportedLanguage = "javascript" | "solidity";
-
 export type HighlightOptions = {
   decorations?: DecorationItem[] | undefined;
 };
 
 export const getHighlighter = async (): Promise<Highlighter> => {
   const highlighterCore = await shikiGetHighlighterCore({
-    langs: [javascript, solidity],
+    langs: [javascript, solidity, json],
     themes: [darkPlus, lightPlus],
     loadWasm,
   });
 
   return {
     highlight: (
-      lang: SupportedLanguage,
+      lang: SupportedLanguage | undefined,
       code: string,
       options?: HighlightOptions | undefined,
     ) => highlight(highlighterCore, lang, code, options),
@@ -41,12 +42,12 @@ export const getHighlighter = async (): Promise<Highlighter> => {
 
 const highlight = (
   highlighter: HighlighterCore,
-  lang: SupportedLanguage,
+  lang: SupportedLanguage | undefined,
   code: string,
   options?: HighlightOptions | undefined,
 ) =>
   highlighter.codeToHtml(code, {
-    lang,
+    lang: lang ?? "text",
     themes: {
       light: "light-plus",
       dark: "dark-plus",

+ 8 - 0
apps/api-reference/src/components/Code/supported-language.ts

@@ -0,0 +1,8 @@
+export const SUPPORTED_LANGUAGES = ["javascript", "solidity", "json"] as const;
+
+export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
+
+export const isSupportedLanguage = (
+  language: string,
+): language is SupportedLanguage =>
+  (SUPPORTED_LANGUAGES as readonly string[]).includes(language);

+ 3 - 2
apps/api-reference/src/components/Code/use-highlighted-code.tsx

@@ -12,7 +12,8 @@ import {
 } from "react";
 import type { OffsetOrPosition } from "shiki";
 
-import type { Highlighter, SupportedLanguage } from "./shiki";
+import type { Highlighter } from "./shiki";
+import type { SupportedLanguage } from "./supported-language";
 import { getLogger } from "../../browser-logger";
 
 const HighlighterContext = createContext<
@@ -45,7 +46,7 @@ const useHighlighter = () => {
 };
 
 export const useHighlightedCode = (
-  language: SupportedLanguage,
+  language: SupportedLanguage | undefined,
   code: string,
   dimRange?: readonly [OffsetOrPosition, OffsetOrPosition] | undefined,
 ) => {

+ 132 - 128
apps/api-reference/src/components/EvmApi/index.tsx

@@ -23,7 +23,6 @@ import {
   useCallback,
   useMemo,
 } from "react";
-import Markdown from "react-markdown";
 import { useSwitchChain, useChainId, useConfig } from "wagmi";
 import { readContract } from "wagmi/actions";
 
@@ -32,11 +31,11 @@ import { ParameterInput } from "./parameter-input";
 import { type EvmApiType, RunButton } from "./run-button";
 import { getLogger } from "../../browser-logger";
 import { getContractAddress } from "../../evm-networks";
-import { MARKDOWN_COMPONENTS } from "../../markdown-components";
 import { useIsMounted } from "../../use-is-mounted";
 import { type SupportedLanguage, Code } from "../Code";
 import { ErrorTooltip } from "../ErrorTooltip";
 import { InlineLink } from "../InlineLink";
+import { Markdown } from "../Markdown";
 import { Select } from "../Select";
 
 export { ParameterType } from "./parameter";
@@ -127,138 +126,143 @@ export const EvmApi = <ParameterName extends string>({
   }, [chainId, chains, isMounted]);
 
   return (
-    <div className="gap-x-20 lg:grid lg:grid-cols-[2fr_1fr]">
-      <h1 className="col-span-2 mb-6 font-mono text-4xl font-medium">{name}</h1>
-      <div className="col-span-2 mb-6 opacity-60">
-        <Markdown components={MARKDOWN_COMPONENTS}>{summary}</Markdown>
+    <>
+      <h1 className="mb-6 font-mono text-4xl font-medium">{name}</h1>
+      <div className="mb-6 opacity-60">
+        <Markdown>{summary}</Markdown>
       </div>
-      <section>
-        <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
-          Description
-        </h2>
-        <Markdown components={MARKDOWN_COMPONENTS}>{description}</Markdown>
-      </section>
-      <section className="flex min-w-0 flex-col">
-        <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
-          Arguments
-        </h2>
-        <div className="mb-8">
-          {parameters.length > 0 ? (
-            <ul className="flex flex-col gap-4">
-              {parameters.map((parameter) => (
-                <li key={parameter.name} className="contents">
-                  <ParameterInput
-                    spec={parameter}
-                    value={paramValues[parameter.name]}
-                    setParamValues={setParamValues}
-                  />
-                </li>
-              ))}
-            </ul>
-          ) : (
-            <div className="rounded-lg bg-neutral-200 p-8 text-center text-sm dark:bg-neutral-800">
-              This API takes no arguments
-            </div>
-          )}
-        </div>
-        {examples && examples.length > 0 && (
+      <div className="grid grid-cols-1 gap-20 lg:grid-cols-[3fr_2fr] 2xl:grid-cols-[1fr_28rem]">
+        <section>
+          <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
+            Description
+          </h2>
+          <Markdown>{description}</Markdown>
+        </section>
+        <section className="flex min-w-0 flex-col">
+          <h2 className="mb-4 border-b border-neutral-200 text-2xl/loose font-medium dark:border-neutral-800">
+            Arguments
+          </h2>
           <div className="mb-8">
-            <h3 className="text-sm font-semibold">Examples</h3>
-            <ul className="ml-2 text-sm">
-              {examples.map((example) => (
-                <li key={example.name}>
-                  <Example example={example} setParamValues={setParamValues} />
-                </li>
-              ))}
-            </ul>
-          </div>
-        )}
-        <Field className="mb-4 flex w-full flex-row items-center gap-2">
-          <Label className="text-sm font-bold">Network</Label>
-          <Select
-            value={currentChain}
-            onChange={({ id }) => {
-              switchChain({ chainId: id });
-            }}
-            renderButtonContents={({ id, name }) => (
-              <div className="flex h-8 grow basis-0 flex-row items-center gap-2 overflow-hidden">
-                {isMounted && (
-                  <>
-                    <ChainIcon id={id} />
-                    <div className="grow basis-0 truncate">{name}</div>
-                  </>
-                )}
-              </div>
-            )}
-            renderOption={({ id, name }) => (
-              <div key={id} className="flex flex-row items-center gap-2">
-                <ChainIcon id={id} />
-                <span>{name}</span>
+            {parameters.length > 0 ? (
+              <ul className="flex flex-col gap-4">
+                {parameters.map((parameter) => (
+                  <li key={parameter.name} className="contents">
+                    <ParameterInput
+                      spec={parameter}
+                      value={paramValues[parameter.name]}
+                      setParamValues={setParamValues}
+                    />
+                  </li>
+                ))}
+              </ul>
+            ) : (
+              <div className="rounded-lg bg-neutral-200 p-8 text-center text-sm dark:bg-neutral-800">
+                This API takes no arguments
               </div>
             )}
-            optionGroups={[
-              {
-                name: "Mainnet",
-                options: chains
-                  .filter((chain) => !chain.testnet)
-                  .sort((a, b) => a.name.localeCompare(b.name)),
-              },
-              {
-                name: "Testnet",
-                options: chains
-                  .filter((chain) => chain.testnet)
-                  .sort((a, b) => a.name.localeCompare(b.name)),
-              },
-            ]}
-            filter={(chains, value) =>
-              chains.filter((chain) =>
-                chain.name.toLowerCase().includes(value.toLowerCase()),
-              )
-            }
-            className="min-w-0 grow"
+          </div>
+          {examples && examples.length > 0 && (
+            <div className="mb-8">
+              <h3 className="text-sm font-semibold">Examples</h3>
+              <ul className="ml-2 text-sm">
+                {examples.map((example) => (
+                  <li key={example.name}>
+                    <Example
+                      example={example}
+                      setParamValues={setParamValues}
+                    />
+                  </li>
+                ))}
+              </ul>
+            </div>
+          )}
+          <Field className="mb-4 flex w-full flex-row items-center gap-2">
+            <Label className="text-sm font-bold">Network</Label>
+            <Select
+              value={currentChain}
+              onChange={({ id }) => {
+                switchChain({ chainId: id });
+              }}
+              renderButtonContents={({ id, name }) => (
+                <div className="flex h-8 grow basis-0 flex-row items-center gap-2 overflow-hidden">
+                  {isMounted && (
+                    <>
+                      <ChainIcon id={id} />
+                      <div className="grow basis-0 truncate">{name}</div>
+                    </>
+                  )}
+                </div>
+              )}
+              renderOption={({ id, name }) => (
+                <div key={id} className="flex flex-row items-center gap-2">
+                  <ChainIcon id={id} />
+                  <span>{name}</span>
+                </div>
+              )}
+              optionGroups={[
+                {
+                  name: "Mainnet",
+                  options: chains
+                    .filter((chain) => !chain.testnet)
+                    .sort((a, b) => a.name.localeCompare(b.name)),
+                },
+                {
+                  name: "Testnet",
+                  options: chains
+                    .filter((chain) => chain.testnet)
+                    .sort((a, b) => a.name.localeCompare(b.name)),
+                },
+              ]}
+              filter={(chains, value) =>
+                chains.filter((chain) =>
+                  chain.name.toLowerCase().includes(value.toLowerCase()),
+                )
+              }
+              className="min-w-0 grow"
+            />
+          </Field>
+          <RunButton
+            functionName={name}
+            parameters={parameters}
+            paramValues={paramValues}
+            {...props}
           />
-        </Field>
-        <RunButton
-          functionName={name}
-          parameters={parameters}
-          paramValues={paramValues}
-          {...props}
-        />
-      </section>
-      <TabGroup className="col-span-2 mt-24">
-        <TabList className="mb-4 flex flex-row gap-2 border-b border-neutral-200 pb-px dark:border-neutral-800">
-          {code.map(({ language }) => (
-            <Tab
-              key={LANGUAGE_TO_DISPLAY_NAME[language]}
-              className="mb-[-2px] border-b-2 border-transparent px-2 text-sm font-medium leading-loose hover:text-pythpurple-600 data-[selected]:cursor-default data-[selected]:border-pythpurple-600 data-[selected]:text-pythpurple-600 dark:hover:text-pythpurple-400 dark:data-[selected]:border-pythpurple-400 dark:data-[selected]:text-pythpurple-400"
-            >
-              {LANGUAGE_TO_DISPLAY_NAME[language]}
-            </Tab>
-          ))}
-        </TabList>
-        <TabPanels>
-          {code.map(({ code: codeContents, language, dimRange }) => (
-            <TabPanel key={LANGUAGE_TO_DISPLAY_NAME[language]}>
-              <Code
-                language={LANUGAGE_TO_SHIKI_NAME[language]}
-                dimRange={dimRange}
+        </section>
+        <TabGroup className="lg:col-span-2">
+          <TabList className="mb-4 flex flex-row gap-2 border-b border-neutral-200 pb-px dark:border-neutral-800">
+            {code.map(({ language }) => (
+              <Tab
+                key={LANGUAGE_TO_DISPLAY_NAME[language]}
+                className="mb-[-2px] border-b-2 border-transparent px-2 text-sm font-medium leading-loose hover:text-pythpurple-600 data-[selected]:cursor-default data-[selected]:border-pythpurple-600 data-[selected]:text-pythpurple-600 dark:hover:text-pythpurple-400 dark:data-[selected]:border-pythpurple-400 dark:data-[selected]:text-pythpurple-400"
               >
-                {codeContents(
-                  isMounted
-                    ? {
-                        name: currentChain.name,
-                        rpcUrl: currentChain.rpcUrls.default.http[0] ?? "",
-                        contractAddress: getContractAddress(chainId) ?? "",
-                      }
-                    : { name: "", rpcUrl: "", contractAddress: "" },
-                  paramValues,
-                )}
-              </Code>
-            </TabPanel>
-          ))}
-        </TabPanels>
-      </TabGroup>
-    </div>
+                {LANGUAGE_TO_DISPLAY_NAME[language]}
+              </Tab>
+            ))}
+          </TabList>
+          <TabPanels>
+            {code.map(({ code: codeContents, language, dimRange }) => (
+              <TabPanel key={LANGUAGE_TO_DISPLAY_NAME[language]}>
+                <Code
+                  language={LANUGAGE_TO_SHIKI_NAME[language]}
+                  dimRange={dimRange}
+                >
+                  {codeContents(
+                    isMounted
+                      ? {
+                          name: currentChain.name,
+                          rpcUrl: currentChain.rpcUrls.default.http[0] ?? "",
+                          contractAddress: getContractAddress(chainId) ?? "",
+                        }
+                      : { name: "", rpcUrl: "", contractAddress: "" },
+                    paramValues,
+                  )}
+                </Code>
+              </TabPanel>
+            ))}
+          </TabPanels>
+        </TabGroup>
+      </div>
+    </>
   );
 };
 

+ 3 - 23
apps/api-reference/src/components/EvmApi/parameter-input.tsx

@@ -12,13 +12,11 @@ import {
   type ChangeEvent,
   type Dispatch,
   type SetStateAction,
-  Fragment,
   useState,
   useCallback,
   useMemo,
   useEffect,
 } from "react";
-import Markdown from "react-markdown";
 
 import {
   type Parameter,
@@ -27,7 +25,6 @@ import {
   getValidationError,
   ParameterType,
 } from "./parameter";
-import { MARKDOWN_COMPONENTS } from "../../markdown-components";
 import {
   type PriceFeed,
   PriceFeedListContextType,
@@ -35,6 +32,7 @@ import {
 } from "../../use-price-feed-list";
 import { InlineLink } from "../InlineLink";
 import { Input } from "../Input";
+import { Markdown } from "../Markdown";
 
 type ParameterProps<ParameterName extends string> = {
   spec: Parameter<ParameterName>;
@@ -101,16 +99,7 @@ const PriceFeedIdInput = <ParameterName extends string>({
         onChange={onChangeInput}
         validationError={validationError}
         label={spec.name}
-        description={
-          <Markdown
-            components={{
-              ...MARKDOWN_COMPONENTS,
-              p: ({ children }) => <Fragment>{children}</Fragment>,
-            }}
-          >
-            {spec.description}
-          </Markdown>
-        }
+        description={<Markdown inline>{spec.description}</Markdown>}
         placeholder={PLACEHOLDERS[spec.type]}
         required={true}
       />
@@ -241,16 +230,7 @@ const DefaultParameterInput = <ParameterName extends string>({
     <Input
       validationError={validationError}
       label={spec.name}
-      description={
-        <Markdown
-          components={{
-            ...MARKDOWN_COMPONENTS,
-            p: ({ children }) => <Fragment>{children}</Fragment>,
-          }}
-        >
-          {spec.description}
-        </Markdown>
-      }
+      description={<Markdown inline>{spec.description}</Markdown>}
       placeholder={PLACEHOLDERS[spec.type]}
       required={true}
       value={internalValue}

+ 1 - 1
apps/api-reference/src/components/EvmApi/run-button.tsx

@@ -88,7 +88,7 @@ export const RunButton = <ParameterName extends string>(
       {status.type === StatusType.Results && (
         <div>
           <h3 className="mb-2 text-lg font-bold">Results</h3>
-          <Code language="javascript">{stringifyResponse(status.data)}</Code>
+          <Code language="json">{stringifyResponse(status.data)}</Code>
         </div>
       )}
       {status.type === StatusType.Error && (

+ 0 - 16
apps/api-reference/src/components/InlineCode/index.tsx

@@ -1,16 +0,0 @@
-import clsx from "clsx";
-import type { HTMLAttributes } from "react";
-
-export const InlineCode = ({
-  className,
-  ...props
-}: HTMLAttributes<HTMLElement>) => (
-  <code
-    className={clsx(
-      "whitespace-nowrap rounded-md border border-neutral-200 bg-neutral-100 px-1 py-0.5 text-[0.9em] dark:border-neutral-700 dark:bg-neutral-800",
-      className,
-    )}
-    {...props}
-  />
-);
-

+ 0 - 4
apps/api-reference/src/components/List/index.tsx

@@ -1,4 +0,0 @@
-import { Styled } from "../Styled";
-
-export const NumberedList = Styled("ol", "list-decimal list-inside mb-6 last:mb-0");
-export const BulletList = Styled("ul", "list-disc list-inside mb-6 last:mb-0");

+ 23 - 0
apps/api-reference/src/components/Markdown/index.tsx

@@ -0,0 +1,23 @@
+import { type ComponentProps, Fragment } from "react";
+import MarkdownComponent from "react-markdown";
+
+import { MARKDOWN_COMPONENTS } from "../../markdown-components";
+
+type Props = Omit<ComponentProps<typeof MarkdownComponent>, "components"> & {
+  inline?: boolean | undefined;
+};
+
+export const Markdown = ({ inline, ...props }: Props) =>
+  inline ? (
+    <MarkdownComponent
+      components={{
+        ...MARKDOWN_COMPONENTS,
+        p: ({ children }) => <Fragment>{children}</Fragment>,
+      }}
+      {...props}
+    />
+  ) : (
+    <div className="flex flex-col gap-4">
+      <MarkdownComponent components={MARKDOWN_COMPONENTS} {...props} />
+    </div>
+  );

+ 1 - 1
apps/api-reference/src/components/MaxWidth/index.tsx

@@ -1,3 +1,3 @@
 import { Styled } from "../Styled";
 
-export const MaxWidth = Styled("div", "mx-auto max-w-7xl px-8");
+export const MaxWidth = Styled("div", "mx-auto px-8");

+ 0 - 3
apps/api-reference/src/components/Paragraph/index.tsx

@@ -1,3 +0,0 @@
-import { Styled } from "../Styled";
-
-export const Paragraph = Styled("p", "mb-6 last:mb-0");

+ 38 - 8
apps/api-reference/src/markdown-components.tsx

@@ -1,15 +1,45 @@
-import { InlineCode } from "./components/InlineCode";
+import type { Components } from "react-markdown";
+
+import { Code, isSupportedLanguage } from "./components/Code";
 import { InlineLink } from "./components/InlineLink";
-import { Paragraph } from "./components/Paragraph";
 import { Styled } from "./components/Styled";
-import { NumberedList, BulletList } from "./components/List";
 
 export const MARKDOWN_COMPONENTS = {
   h1: Styled("h1", "mb-8 text-4xl font-medium"),
-  p: Paragraph,
   a: InlineLink,
-  code: InlineCode,
+  pre: (props) => {
+    const firstChild = props.node?.children[0];
+    if (
+      props.node?.children.length === 1 &&
+      firstChild &&
+      "tagName" in firstChild &&
+      firstChild.tagName === "code"
+    ) {
+      const { className } = firstChild.properties;
+      const className_ = Array.isArray(className) ? className[0] : className;
+      const language = /language-(\w+)/.exec(
+        typeof className_ === "string" ? className_ : "",
+      )?.[1];
+      const codeNode = firstChild.children[0];
+      return (
+        <Code
+          language={
+            language && isSupportedLanguage(language) ? language : undefined
+          }
+        >
+          {codeNode !== undefined && "value" in codeNode ? codeNode.value : ""}
+        </Code>
+      );
+    } else {
+      return <pre {...props} />;
+    }
+  },
+  code: Styled(
+    "code",
+    "whitespace-nowrap rounded-md border border-neutral-200 bg-neutral-100 px-1 py-0.5 text-[0.9em] dark:border-neutral-700 dark:bg-neutral-800",
+  ),
   strong: Styled("strong", "font-semibold"),
-  ul: BulletList,
-  ol: NumberedList,
-};
+  ul: Styled("ul", "list-disc list-inside flex flex-col gap-1"),
+  ol: Styled("ol", "list-decimal list-inside flex flex-col gap-1"),
+  h3: Styled("h3", "text-lg font-semibold mt-4"),
+} satisfies Components;