diff --git a/apps/web/app/docs/[[...slug]]/preview-implementation.tsx b/apps/web/app/docs/[[...slug]]/preview-implementation.tsx index 8470002c..6b2e1f4e 100644 --- a/apps/web/app/docs/[[...slug]]/preview-implementation.tsx +++ b/apps/web/app/docs/[[...slug]]/preview-implementation.tsx @@ -4,6 +4,7 @@ import { z } from "zod" import { Demo } from "@/components/demo" import { Pre, RawCode, highlight } from "codehike/code" import { CopyButton } from "@/components/copy-button" +import { ComponentPackLink } from "@/components/component-pack-link" const ContentSchema = Block.extend({ demo: Block, diff --git a/apps/web/components/component-pack-link.tsx b/apps/web/components/component-pack-link.tsx new file mode 100644 index 00000000..8fc9c079 --- /dev/null +++ b/apps/web/components/component-pack-link.tsx @@ -0,0 +1,20 @@ +import { ArrowRight } from "lucide-react" + +export function ComponentPackLink() { + return ( +
+ Don't have time to build it yourself? Get a documentation component + library customized for your tech stack and design system. + +
+ ) +} diff --git a/apps/web/content/blog/content-presentation-gap.mdx b/apps/web/content/blog/content-presentation-gap.mdx index 3825b88a..5ee054f8 100644 --- a/apps/web/content/blog/content-presentation-gap.mdx +++ b/apps/web/content/blog/content-presentation-gap.mdx @@ -6,9 +6,13 @@ authors: [pomber] draft: true --- +import { Demo } from "./content-presentation-gap" + +Notice: content means Markdown, presentation means UI components. So, another title could be "The Markdown-React gap". + Let's tart with an example. -> demo (roman emperors) + This is a UI pattern commonly referred as scrollytelling, where the content is presented in a way that is tightly coupled with the user's scroll position. @@ -19,6 +23,8 @@ It's also used for technical content like [Stripe's API reference](https://strip A component to implement this pattern is not super hard to build, usually it's a combination of an intersection observer and position sticky. ```tsx +import { useEffect, useRef, useState } from "react" + type Props = { steps: { content: React.ReactNode diff --git a/apps/web/content/blog/content-presentation-gap.tsx b/apps/web/content/blog/content-presentation-gap.tsx new file mode 100644 index 00000000..08bf5ef0 --- /dev/null +++ b/apps/web/content/blog/content-presentation-gap.tsx @@ -0,0 +1,100 @@ +"use client" + +import { useEffect, useRef, useState } from "react" + +export function Demo() { + return ( +
+ +

Augustus

+

+ Augustus was the first Roman emperor, reigning from 27 BC + until his death in AD 14. +

+
+ ), + sticker: ( + Augustus + ), + }, + { + content: ( +
+

Nero

+

+ Nero was the last Roman emperor of the Julio-Claudian dynasty. +

+
+ ), + sticker: Nero, + }, + { + content: ( +
+

Trajan

+

Trajan was Roman emperor from 98 to 117.

+
+ ), + sticker: Trajan, + }, + ]} + /> + + ) +} + +type Props = { + steps: { + content: React.ReactNode + sticker: React.ReactNode + }[] +} +function Scrollytelling({ steps }: Props) { + const [currentStep, setCurrentStep] = useState(0) + const ref = useRef(null) + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setCurrentStep(parseInt(entry.target.id)) + } + }, + { + rootMargin: "-48% 0px", + threshold: 0.01, + root: ref.current, + }, + ) + + steps.forEach((_, i) => { + observer.observe(document.getElementById(i.toString())!) + }) + + return () => observer.disconnect() + }, []) + + return ( +
+
+ {steps.map((step, i) => ( +
+ {step.content} +
+ ))} +
+
+
+ {steps[currentStep].sticker} +
+
+ ) +} diff --git a/apps/web/content/blog/the-curse-of-markdown.mdx b/apps/web/content/blog/the-curse-of-markdown.mdx index 93efe7f8..1fde2a96 100644 --- a/apps/web/content/blog/the-curse-of-markdown.mdx +++ b/apps/web/content/blog/the-curse-of-markdown.mdx @@ -25,8 +25,8 @@ Tradeoffs: https://youtu.be/zqhE-CepH2g?si=7iYgDUjAhJNVmYJN&t=446 examples of websites that mdx allow: -- josh's blog -- react docs +- interactive blogs +- interactive docs --- @@ -36,5 +36,3 @@ Examples are: - stripe - swiftui -- nanda -- pudding diff --git a/apps/web/content/docs/api.mdx b/apps/web/content/docs/api.mdx new file mode 100644 index 00000000..24de8438 --- /dev/null +++ b/apps/web/content/docs/api.mdx @@ -0,0 +1,213 @@ +--- +title: API Reference +description: Comprehensive list of codehike's API +--- + +import { Usage } from "./api" + +## `codehike/mdx` + +```tsx +import { remarkCodeHike, recmaCodeHike } from "codehike/mdx" +``` + +### `remarkCodeHike` + +A remark plugin that transform codeblocks and inline code into the specified components. + +- from annotation +- you have to provide the components + +```tsx +import { remarkCodeHike, recmaCodeHike } from "codehike/mdx" + +/** @type {import('codehike/mdx').CodeHikeConfig} */ +const chConfig = { + components: { code: "MyCode", inlineCode: "MyInlineCode" }, + ignoreCode: (codeblock) => codeblock.lang === "mermaid", + syntaxHighlighting: { theme: "github-dark" }, +} + +// what you do with the `mdxOptions` depends on what you use to handle mdx +const mdxOptions = { + remarkPlugins: [[remarkCodeHike, chConfig], ...otherRemarkPlugins], + recmaPlugins: [[recmaCodeHike, chConfig]], +} +``` + +#### `CodeHikeConfig.components.code` + +If you specify a component for `code`, the `remarkCodeHike` plugin will replace code blocks with that component. The language, meta, and value of the code block will be passed inside a `codeblock` prop. + + + +## !caption + +How `remarkCodeHike` compiles codeblocks using the `code` component + +## !left + +```tsx your-config.js +const chConfig = { + // !mark[/MyCode/] 1 + components: { code: "MyCode" }, +} +``` + +{/* prettier-ignore */} +````mdx content.mdx +# Hello + +{/* !mark[/js|lorem ipsum|console.log\(1\)/gm] 5 */} +```js lorem ipsum +console.log(1) +``` +```` + +## !right + +{/* prettier-ignore */} +```jsx compiled output -w +export default function Content(props = {}) { + // !mark[/MyCode/gm] 1 + // !mark[/js|lorem ipsum|console.log\(1\)/gm] 5 + const { MyCode } = props.components + return ( + <> +

Hello

+ + + ) +} +``` + +
+ +FAQ + +- how to pass the components to the `Content` component? +- how to syntax highlight the code? + +#### `CodeHikeConfig.components.inlineCode` + +If you specify a component for `inlineCode`, the `remarkCodeHike` plugin will replace inline code with that component. The value of the inline code will be passed inside a `codeblock` prop. + +Code Hike uses a special syntax to define inline code ``_`code`_``. This syntax also allows you to specify the language and meta for inline code ``_py lorem ipsum`print 5`_``, will give you _`{lang: "py", meta: "lorem ipsum", value: "print 5"}`_. + + + +## !caption + +How `remarkCodeHike` compiles inline code using the `inlineCode` component + +## !left + +```tsx your-config.js +const chConfig = { + // !mark[/MyInlineCode/] 1 + components: { inlineCode: "MyInlineCode" }, +} +``` + +{/* prettier-ignore */} +````mdx content.mdx -w +# Hello + +Case 1: `var x = 10` + +Case 2: _`var x = 10`_ + +Case 3: _css`a { color: #123 }`_ +```` + +## !right + +{/* prettier-ignore */} +```jsx compiled output -w +export default function Content(props = {}) { + // !mark[/MyInlineCode/gm] 1 + const { MyInlineCode } = props.components + return ( + <> +

Hello

+

Case 1: var x = 10

+

Case 2: + +

+

Case 3: + +

+ + ) +} +``` + +
+ +#### `CodeHikeConfig.ignoreCode` + +#### `CodeHikeConfig.syntaxHighlighting` + +### `recmaCodeHike` + +A recma plugin that parses code hike blocks. + +## `codehike` + +```tsx +import { parse } from "codehike" +``` + +## `codehike/blocks` + +```tsx +import { + parseRoot, + parseProps, + Block, + CodeBlock, + HighlightedCodeBlock, + ImageBlock, +} from "codehike/blocks" +``` + +## `codehike/code` + +```tsx +import { + highlight, + Pre, + Inline, + InnerPre, + InnerLine, + InnerToken, + getPreRef, +} from "codehike/code" +``` + +## `codehike/utils` + +```tsx +import { + SelectionProvider, + Selectable, + Selection, + useSelectedIndex, +} from "codehike/utils/selection" + +import { + TokenTransitionsSnapshot, + calculateTransitions, + getStartingSnapshot, +} from "codehike/utils/token-transitions" +``` diff --git a/apps/web/content/docs/api.tsx b/apps/web/content/docs/api.tsx new file mode 100644 index 00000000..fdd1c83d --- /dev/null +++ b/apps/web/content/docs/api.tsx @@ -0,0 +1,22 @@ +import { Block, parseProps } from "codehike/blocks" +import { z } from "zod" + +export function Usage(props: unknown) { + const { left, right, caption } = parseProps( + props, + Block.extend({ left: Block, right: Block, caption: Block.optional() }), + ) + return ( +
+
+
+ {left.children} +
+
+ {right.children} +
+
+
{caption?.children}
+
+ ) +} diff --git a/apps/web/content/docs/api/code.mdx b/apps/web/content/docs/api/code.mdx deleted file mode 100644 index a43ee145..00000000 --- a/apps/web/content/docs/api/code.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Code -description: The Code component ---- - -```txt -// TO DO -``` diff --git a/apps/web/content/docs/api/hike.mdx b/apps/web/content/docs/api/hike.mdx deleted file mode 100644 index fa5e1ab4..00000000 --- a/apps/web/content/docs/api/hike.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Hike -description: The Hike component ---- - -```txt -// TO DO -``` diff --git a/apps/web/content/docs/api/schema.mdx b/apps/web/content/docs/api/schema.mdx deleted file mode 100644 index 59edbe2e..00000000 --- a/apps/web/content/docs/api/schema.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Schema -description: Hike Schema ---- - -```js -// TO DO -```