├── .changeset
├── blue-cameras-serve.md
├── brave-donuts-stare.md
├── cold-doors-occur.md
├── config.json
├── curly-grapes-sip.md
├── dirty-onions-nail.md
├── fifty-donkeys-join.md
├── forty-llamas-mate.md
├── gentle-pots-mix.md
├── heavy-students-swim.md
├── hungry-pugs-chew.md
├── lemon-owls-hammer.md
├── light-carrots-remain.md
├── light-clouds-compete.md
├── loud-impalas-drive.md
├── metal-gifts-remember.md
├── metal-planes-fix.md
├── modern-eagles-give.md
├── neat-buttons-impress.md
├── polite-pumas-whisper.md
├── poor-beers-flow.md
├── popular-zebras-doubt.md
├── pre.json
├── readme.md
├── rich-pears-peel.md
├── silver-trees-wash.md
├── six-deers-film.md
├── smart-poets-report.md
├── soft-moles-act.md
├── spotty-dryers-care.md
├── strange-toys-beam.md
├── stupid-guests-scream.md
├── sweet-eggs-mix.md
├── tall-ravens-march.md
├── three-bees-provide.md
├── tricky-baboons-drop.md
└── wet-lemons-thank.md
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── apps
└── web
│ ├── .gitignore
│ ├── app
│ ├── api
│ │ └── search
│ │ │ └── route.ts
│ ├── blog
│ │ ├── [slug]
│ │ │ ├── page.tsx
│ │ │ └── pomber.jpg
│ │ ├── feed.xml
│ │ │ └── route.ts
│ │ ├── layout.tsx
│ │ ├── og
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── demo
│ │ ├── client.tsx
│ │ ├── content.mdx
│ │ └── page.tsx
│ ├── docs
│ │ ├── [[...slug]]
│ │ │ ├── code-example.tsx
│ │ │ ├── layout-example.tsx
│ │ │ ├── page.tsx
│ │ │ └── preview-implementation.tsx
│ │ └── layout.tsx
│ ├── favicon.ico
│ ├── global.css
│ ├── landing
│ │ ├── boxes.tsx
│ │ ├── demo.md
│ │ ├── demo.tsx
│ │ ├── logo.drivly.png
│ │ ├── logo.github.png
│ │ ├── logo.meta.png
│ │ ├── logo.speakeasy.svg
│ │ ├── logo.uidev.svg
│ │ ├── placeholdifier-monospace.woff2
│ │ ├── placeholdifier.woff2
│ │ ├── profile.md
│ │ ├── sponsors.json
│ │ ├── sponsors.tsx
│ │ ├── sponsors
│ │ │ ├── codehike-transactions.csv
│ │ │ ├── csv.js
│ │ │ ├── gh-sponsors.js
│ │ │ ├── oc-sponsors.js
│ │ │ └── update.js
│ │ └── text.github.png
│ ├── layout.tsx
│ ├── not-found.tsx
│ ├── page.tsx
│ ├── play
│ │ └── page.tsx
│ ├── source.ts
│ ├── test
│ │ ├── client.tsx
│ │ ├── content.md
│ │ └── page.tsx
│ └── top-sponsors.png
│ │ └── route.tsx
│ ├── components.json
│ ├── components
│ ├── all-code-demos.tsx
│ ├── annotations
│ │ ├── callout.tsx
│ │ ├── collapse.tsx
│ │ ├── diff.tsx
│ │ ├── focus.client.tsx
│ │ ├── focus.tsx
│ │ ├── fold.client.tsx
│ │ ├── fold.tsx
│ │ ├── icons.tsx
│ │ ├── line-numbers.tsx
│ │ ├── link.tsx
│ │ ├── mark.tsx
│ │ ├── pill.tsx
│ │ ├── ruler.tsx
│ │ ├── token-transitions.client.tsx
│ │ ├── token-transitions.tsx
│ │ ├── tooltip.tsx
│ │ └── word-wrap.tsx
│ ├── code.tsx
│ ├── code
│ │ ├── blocks-demo.tsx
│ │ ├── code-with-notes.tsx
│ │ └── side-by-side.tsx
│ ├── copy-button.tsx
│ ├── demo.tsx
│ ├── layout-demo.tsx
│ ├── smooth-pre.tsx
│ └── ui
│ │ ├── collapsible.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── resizable.tsx
│ │ ├── select.tsx
│ │ ├── tabs.tsx
│ │ └── tooltip.tsx
│ ├── content
│ ├── blog
│ │ ├── .prettierrc
│ │ ├── bestiary.mdx
│ │ ├── codeblocks.mdx
│ │ ├── fine-grained-markdown.mdx
│ │ ├── fine-grained
│ │ │ ├── code.tsx
│ │ │ └── scrolly.tsx
│ │ ├── from-remark-to-rsc.mdx
│ │ ├── headless-codeblocks.md
│ │ ├── hello-server.mdx
│ │ ├── known-keys.mdx
│ │ ├── markdown.mdx
│ │ ├── migrate-to-ch.mdx
│ │ ├── participation.mdx
│ │ ├── power-of-the-web.mdx
│ │ ├── remotion.mdx
│ │ ├── remotion
│ │ │ ├── .prettierrc
│ │ │ ├── 00.jsx
│ │ │ ├── 01.jsx
│ │ │ ├── 02.jsx
│ │ │ ├── 03.jsx
│ │ │ ├── 04.jsx
│ │ │ ├── 05.jsx
│ │ │ ├── 06.jsx
│ │ │ └── code.tsx
│ │ ├── rich-content-websites.mdx
│ │ ├── rsc-and-css.mdx
│ │ ├── test.mdx
│ │ ├── three-component-problem.mdx
│ │ ├── v1-migration.mdx
│ │ ├── v1.mdx
│ │ └── v1.tsx
│ ├── components
│ │ ├── .prettierrc
│ │ ├── api-reference
│ │ │ ├── api-reference.tsx
│ │ │ ├── collapsible.tsx
│ │ │ └── source.mdx
│ │ └── slideshow
│ │ │ ├── slides.tsx
│ │ │ ├── slideshow.tsx
│ │ │ └── source.mdx
│ └── docs
│ │ ├── api
│ │ ├── code.mdx
│ │ ├── hike.mdx
│ │ └── schema.mdx
│ │ ├── code
│ │ ├── callout.mdx
│ │ ├── classname.mdx
│ │ ├── code-mentions.mdx
│ │ ├── collapse.mdx
│ │ ├── copy-button.mdx
│ │ ├── diff.mdx
│ │ ├── filename.mdx
│ │ ├── focus.mdx
│ │ ├── fold.mdx
│ │ ├── footnotes.mdx
│ │ ├── index.mdx
│ │ ├── language-switcher.mdx
│ │ ├── line-numbers.mdx
│ │ ├── link.mdx
│ │ ├── mark.mdx
│ │ ├── tabs.mdx
│ │ ├── token-transitions.mdx
│ │ ├── tooltip.mdx
│ │ ├── transpile.mdx
│ │ ├── twoslash.mdx
│ │ └── word-wrap.mdx
│ │ ├── concepts
│ │ ├── annotations.mdx
│ │ ├── blocks.mdx
│ │ ├── code.mdx
│ │ ├── theme-picker.tsx
│ │ └── utils.mdx
│ │ ├── examples.mdx
│ │ ├── examples.tsx
│ │ ├── index.mdx
│ │ ├── layouts
│ │ ├── scrollycoding.mdx
│ │ ├── slideshow.mdx
│ │ └── spotlight.mdx
│ │ ├── meta.json
│ │ └── test.mdx
│ ├── demos
│ ├── annotations
│ │ ├── basic
│ │ │ ├── content.md
│ │ │ └── page.tsx
│ │ ├── groups
│ │ │ ├── content.md
│ │ │ └── page.tsx
│ │ ├── mark
│ │ │ ├── content.md
│ │ │ └── page.tsx
│ │ ├── query
│ │ │ ├── content.md
│ │ │ └── page.tsx
│ │ └── regex
│ │ │ ├── content.md
│ │ │ └── page.tsx
│ ├── api-reference
│ │ ├── .prettierrc
│ │ ├── content.md
│ │ └── page.tsx
│ ├── autolink
│ │ ├── content.md
│ │ └── page.tsx
│ ├── callout
│ │ ├── content.md
│ │ └── page.tsx
│ ├── classname
│ │ ├── content.md
│ │ └── page.tsx
│ ├── collapse
│ │ ├── content.md
│ │ └── page.tsx
│ ├── copy-button
│ │ ├── button.tsx
│ │ ├── content.md
│ │ └── page.tsx
│ ├── diff
│ │ ├── content.md
│ │ └── page.tsx
│ ├── filename
│ │ ├── content.md
│ │ └── page.tsx
│ ├── focus
│ │ ├── code.tsx
│ │ ├── content.md
│ │ └── page.tsx
│ ├── fold
│ │ ├── annotations.tsx
│ │ ├── content.md
│ │ └── page.tsx
│ ├── footnotes
│ │ ├── content.md
│ │ └── page.tsx
│ ├── hover
│ │ ├── content.mdx
│ │ ├── page.tsx
│ │ └── styles.css
│ ├── language-switcher
│ │ ├── content.mdx
│ │ ├── multi-code.tsx
│ │ └── page.tsx
│ ├── line-numbers
│ │ ├── content.md
│ │ └── page.tsx
│ ├── link
│ │ ├── content.md
│ │ └── page.tsx
│ ├── mark
│ │ ├── content.md
│ │ └── page.tsx
│ ├── occurrences
│ │ ├── code.tsx
│ │ ├── content.md
│ │ └── page.tsx
│ ├── scrollycoding
│ │ ├── .prettierrc
│ │ ├── content.md
│ │ └── page.tsx
│ ├── slideshow
│ │ ├── .prettierrc
│ │ ├── content.md
│ │ ├── controls.tsx
│ │ └── page.tsx
│ ├── spotlight
│ │ ├── .prettierrc
│ │ ├── content.md
│ │ └── page.tsx
│ ├── tabs
│ │ ├── content.mdx
│ │ └── page.tsx
│ ├── token-transitions
│ │ ├── code.tsx
│ │ ├── content.md
│ │ ├── content.mdx
│ │ └── page.tsx
│ ├── tooltip
│ │ ├── content.mdx
│ │ └── page.tsx
│ ├── transpile
│ │ ├── content.md
│ │ └── page.tsx
│ ├── twoslash
│ │ ├── content.md
│ │ └── page.tsx
│ └── word-wrap
│ │ ├── content.md
│ │ └── page.tsx
│ ├── lib
│ └── utils.ts
│ ├── mdx-components.tsx
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ ├── blog
│ │ ├── fine-grained-markdown.png
│ │ ├── remotion.png
│ │ ├── remotion
│ │ │ ├── 00.mp4
│ │ │ ├── 02.mp4
│ │ │ ├── 04.mp4
│ │ │ ├── 06.mp4
│ │ │ └── examples.mp4
│ │ ├── rich-content-websites.png
│ │ ├── v1-migration.png
│ │ └── v1.png
│ ├── codehike.png
│ ├── dark-grid.svg
│ ├── examples
│ │ ├── docusaurus.png
│ │ ├── fumadocs.png
│ │ ├── nextra-3.png
│ │ ├── remotion.png
│ │ ├── shopify.png
│ │ └── swiftui.png
│ └── logo.png
│ ├── tailwind.config.ts
│ ├── theme.mjs
│ ├── tsconfig.json
│ └── ui
│ ├── dependency-terminal.tsx
│ ├── example-tabs.tsx
│ ├── nav.tsx
│ ├── tabs-toggle.tsx
│ ├── tabs.tsx
│ ├── toggle-group.tsx
│ └── toggle.tsx
├── license
├── package.json
├── packages
└── codehike
│ ├── changelog.md
│ ├── package.json
│ ├── src
│ ├── blocks.ts
│ ├── code
│ │ ├── block.tsx
│ │ ├── extract-annotations.tsx
│ │ ├── highlight.ts
│ │ ├── index.tsx
│ │ ├── inline.tsx
│ │ ├── inner.tsx
│ │ ├── lines.tsx
│ │ ├── merge-props.ts
│ │ ├── pre-ref.tsx
│ │ ├── pre.tsx
│ │ ├── tokens.tsx
│ │ └── types.ts
│ ├── index.ts
│ ├── mdx.ts
│ ├── mdx
│ │ ├── 0.import-code-from-path.ts
│ │ ├── 1.0.transform-hikes.ts
│ │ ├── 1.1.remark-list-to-section.ts
│ │ ├── 1.2.remark-section-to-attribute.ts
│ │ ├── 2.transform-code.ts
│ │ ├── 3.transform-hike-props.ts
│ │ ├── config.ts
│ │ └── estree.ts
│ ├── utils.tsx
│ └── utils
│ │ ├── scroller.tsx
│ │ ├── selection.tsx
│ │ ├── static-fallback.tsx
│ │ └── token-transitions.tsx
│ ├── tests
│ ├── md-suite
│ │ ├── _readme.md
│ │ ├── annotation.0.mdx
│ │ ├── annotation.0.render.tsx
│ │ ├── annotation.9.rendered.html
│ │ ├── assets
│ │ │ └── test.py
│ │ ├── basic.0.mdx
│ │ ├── basic.2.before-remark.json
│ │ ├── basic.3.after-remark.json
│ │ ├── basic.4.after-rehype.json
│ │ ├── basic.5.before-recma-compiled-function.jsx
│ │ ├── basic.5.before-recma-compiled-js.js
│ │ ├── basic.5.before-recma-compiled-jsx.jsx
│ │ ├── basic.5.before-recma-js.json
│ │ ├── basic.5.before-recma-jsx.json
│ │ ├── basic.6.after-recma-js.json
│ │ ├── basic.6.after-recma-jsx.json
│ │ ├── basic.7.compiled-function.jsx
│ │ ├── basic.7.compiled-js.js
│ │ ├── basic.7.compiled-jsx.jsx
│ │ ├── basic.8.parsed-jsx.jsx
│ │ ├── basic.9.rendered.html
│ │ ├── code-empty.0.mdx
│ │ ├── code-empty.7.compiled-jsx.jsx
│ │ ├── code-empty.9.rendered.html
│ │ ├── code.0.mdx
│ │ ├── code.7.compiled-jsx.jsx
│ │ ├── code.9.rendered.html
│ │ ├── empty-block.0.mdx
│ │ ├── empty-block.8.parsed-jsx.jsx
│ │ ├── empty-children-and-import.0.mdx
│ │ ├── empty-children-and-import.7.compiled-jsx.jsx
│ │ ├── empty-line.0.mdx
│ │ ├── empty-line.7.compiled-jsx.jsx
│ │ ├── empty-line.9.rendered.html
│ │ ├── ignore-codeblock.0.mdx
│ │ ├── ignore-codeblock.7.compiled-jsx.jsx
│ │ ├── import-code.0.mdx
│ │ ├── import-code.7.compiled-jsx.jsx
│ │ ├── indentation.0.mdx
│ │ ├── indentation.0.render.tsx
│ │ ├── indentation.9.rendered.html
│ │ ├── inline-code-highlighted.0.mdx
│ │ ├── inline-code-highlighted.2.before-remark.json
│ │ ├── inline-code-highlighted.7.compiled-jsx.jsx
│ │ ├── inline-code-highlighted.9.rendered.html
│ │ ├── inline-code.0.mdx
│ │ ├── inline-code.2.before-remark.json
│ │ ├── inline-code.7.compiled-jsx.jsx
│ │ ├── inline-code.9.rendered.html
│ │ ├── nested-blocks.0.mdx
│ │ ├── nested-blocks.8.parsed-jsx.jsx
│ │ ├── no-hike.0.mdx
│ │ ├── no-hike.7.compiled-jsx.jsx
│ │ ├── only-if-annotated.0.mdx
│ │ ├── only-if-annotated.0.render.tsx
│ │ ├── only-if-annotated.9.rendered.html
│ │ ├── regex-range.0.mdx
│ │ ├── regex-range.0.render.tsx
│ │ ├── regex-range.9.rendered.html
│ │ ├── stacked-line.0.mdx
│ │ ├── stacked-line.0.render.tsx
│ │ ├── stacked-line.9.rendered.html
│ │ ├── stacked-token.0.mdx
│ │ ├── stacked-token.0.render.tsx
│ │ ├── stacked-token.9.rendered.html
│ │ ├── syntax-highlight.0.mdx
│ │ ├── syntax-highlight.7.compiled-jsx.jsx
│ │ ├── two-ps.0.mdx
│ │ ├── two-ps.2.before-remark.json
│ │ ├── two-ps.8.parsed-jsx.jsx
│ │ ├── unknown-lang.0.mdx
│ │ └── unknown-lang.9.rendered.html
│ ├── md.test.tsx
│ ├── utils.ast.tsx
│ └── utils.suite.tsx
│ └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── readme.md
└── turbo.json
/.changeset/blue-cameras-serve.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | New hike syntax
6 |
--------------------------------------------------------------------------------
/.changeset/brave-donuts-stare.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add schemas
6 |
--------------------------------------------------------------------------------
/.changeset/cold-doors-occur.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Steps with multiple elements
6 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "fixed": [],
6 | "linked": [],
7 | "access": "restricted",
8 | "baseBranch": "main",
9 | "updateInternalDependencies": "patch",
10 | "ignore": ["web"]
11 | }
12 |
--------------------------------------------------------------------------------
/.changeset/curly-grapes-sip.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Replace code element with div
6 |
--------------------------------------------------------------------------------
/.changeset/dirty-onions-nail.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Make className optional
6 |
--------------------------------------------------------------------------------
/.changeset/fifty-donkeys-join.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Fix empty lines at the start of codeblock
6 |
--------------------------------------------------------------------------------
/.changeset/forty-llamas-mate.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Fix highlighted schema
6 |
--------------------------------------------------------------------------------
/.changeset/gentle-pots-mix.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add click handler to ScrollyStep
6 |
--------------------------------------------------------------------------------
/.changeset/heavy-students-swim.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add token transitions helper
6 |
--------------------------------------------------------------------------------
/.changeset/hungry-pugs-chew.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Export getHike
6 |
--------------------------------------------------------------------------------
/.changeset/lemon-owls-hammer.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Blocks API
6 |
--------------------------------------------------------------------------------
/.changeset/light-carrots-remain.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Improve annotation stacking
6 |
--------------------------------------------------------------------------------
/.changeset/light-clouds-compete.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Fix parse type
6 |
--------------------------------------------------------------------------------
/.changeset/loud-impalas-drive.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Make parse independent of zod
6 |
--------------------------------------------------------------------------------
/.changeset/metal-gifts-remember.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add static fallback
6 |
--------------------------------------------------------------------------------
/.changeset/metal-planes-fix.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Fix annotation detection for some languages
6 |
--------------------------------------------------------------------------------
/.changeset/modern-eagles-give.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": major
3 | ---
4 |
5 | Init codehike
6 |
--------------------------------------------------------------------------------
/.changeset/neat-buttons-impress.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add ignoreCode to config
6 |
--------------------------------------------------------------------------------
/.changeset/polite-pumas-whisper.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Update lighter
6 |
--------------------------------------------------------------------------------
/.changeset/poor-beers-flow.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Add totalLines
6 |
--------------------------------------------------------------------------------
/.changeset/popular-zebras-doubt.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Move selection utils
6 |
--------------------------------------------------------------------------------
/.changeset/pre.json:
--------------------------------------------------------------------------------
1 | {
2 | "mode": "pre",
3 | "tag": "beta",
4 | "initialVersions": {
5 | "web": "0.1.0",
6 | "codehike": "1.0.0-alpha.21"
7 | },
8 | "changesets": [
9 | "blue-cameras-serve",
10 | "brave-donuts-stare",
11 | "cold-doors-occur",
12 | "curly-grapes-sip",
13 | "dirty-onions-nail",
14 | "fifty-donkeys-join",
15 | "forty-llamas-mate",
16 | "gentle-pots-mix",
17 | "heavy-students-swim",
18 | "hungry-pugs-chew",
19 | "lemon-owls-hammer",
20 | "light-carrots-remain",
21 | "light-clouds-compete",
22 | "loud-impalas-drive",
23 | "metal-gifts-remember",
24 | "metal-planes-fix",
25 | "modern-eagles-give",
26 | "neat-buttons-impress",
27 | "polite-pumas-whisper",
28 | "poor-beers-flow",
29 | "popular-zebras-doubt",
30 | "rich-pears-peel",
31 | "silver-trees-wash",
32 | "six-deers-film",
33 | "smart-poets-report",
34 | "soft-moles-act",
35 | "spotty-dryers-care",
36 | "strange-toys-beam",
37 | "stupid-guests-scream",
38 | "sweet-eggs-mix",
39 | "tall-ravens-march",
40 | "three-bees-provide",
41 | "tricky-baboons-drop",
42 | "wet-lemons-thank"
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/.changeset/readme.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/rich-pears-peel.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Change annotation components layout
6 |
--------------------------------------------------------------------------------
/.changeset/silver-trees-wash.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add parseContent
6 |
--------------------------------------------------------------------------------
/.changeset/six-deers-film.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Add code element to Pre component
6 |
--------------------------------------------------------------------------------
/.changeset/smart-poets-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add remark syntax highlighting for non-rsc
6 |
--------------------------------------------------------------------------------
/.changeset/soft-moles-act.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add component blocks
6 |
--------------------------------------------------------------------------------
/.changeset/spotty-dryers-care.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Fix Image schema
6 |
--------------------------------------------------------------------------------
/.changeset/strange-toys-beam.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Add inline code component and plugin
6 |
--------------------------------------------------------------------------------
/.changeset/stupid-guests-scream.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Support consecutive lines of imgs and values
6 |
--------------------------------------------------------------------------------
/.changeset/sweet-eggs-mix.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Rename getHike to getBlocks
6 |
--------------------------------------------------------------------------------
/.changeset/tall-ravens-march.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Add non-RSC config
6 |
--------------------------------------------------------------------------------
/.changeset/three-bees-provide.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | Tabs indentation
6 |
--------------------------------------------------------------------------------
/.changeset/tricky-baboons-drop.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": minor
3 | ---
4 |
5 | Rename query and steps
6 |
--------------------------------------------------------------------------------
/.changeset/wet-lemons-thank.md:
--------------------------------------------------------------------------------
1 | ---
2 | "codehike": patch
3 | ---
4 |
5 | From annotation in remark
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false
4 | }
5 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "explorer.fileNesting.enabled": true,
3 | "explorer.fileNesting.patterns": {
4 | "*.0.mdx": "${capture}.*"
5 | },
6 | "explorer.fileNesting.expand": false
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/.gitignore:
--------------------------------------------------------------------------------
1 | # deps
2 | /node_modules
3 |
4 | # generated content
5 | .map.ts
6 | .contentlayer
7 |
8 | # test & build
9 | /coverage
10 | /.next/
11 | /out/
12 | /build
13 | *.tsbuildinfo
14 |
15 | # misc
16 | .DS_Store
17 | *.pem
18 | /.pnp
19 | .pnp.js
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | # others
25 | .env*.local
26 | .vercel
27 | next-env.d.ts
28 |
--------------------------------------------------------------------------------
/apps/web/app/api/search/route.ts:
--------------------------------------------------------------------------------
1 | import { docs } from "@/app/source"
2 | import { createSearchAPI } from "next-docs-zeta/search/server"
3 |
4 | export const { GET } = createSearchAPI("advanced", {
5 | indexes: docs.getPages().map((page) => ({
6 | title: page.data.title,
7 | structuredData: page.data.exports.structuredData,
8 | id: page.url,
9 | url: page.url,
10 | })),
11 | })
12 |
--------------------------------------------------------------------------------
/apps/web/app/blog/[slug]/pomber.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/blog/[slug]/pomber.jpg
--------------------------------------------------------------------------------
/apps/web/app/blog/feed.xml/route.ts:
--------------------------------------------------------------------------------
1 | import RSS from "rss"
2 | import { blog } from "../../source"
3 |
4 | const posts = blog.getPages().filter((page) => page.data.draft !== true)
5 |
6 | export const dynamic = "force-static"
7 |
8 | export async function GET() {
9 | const feed = new RSS({
10 | title: "Code Hike",
11 | description: "Markdown, React and everything in between.",
12 | generator: "RSS for Node and Next.js",
13 | feed_url: "https://v1.codehike.org/blog/feed.xml",
14 | site_url: "https://v1.codehike.org/blog",
15 | language: "en-US",
16 | pubDate: new Date().toUTCString(),
17 | ttl: 120,
18 | image_url: "https://v1.codehike.org/logo.png",
19 | })
20 |
21 | posts.map(({ data, url }) => {
22 | feed.item({
23 | title: data.title,
24 | description: data.description || "",
25 | url: `https://v1.codehike.org${url}`,
26 | author: data.authors[0],
27 | date: data.date,
28 | })
29 | })
30 |
31 | return new Response(feed.xml({ indent: true }), {
32 | headers: {
33 | "Content-Type": "application/atom+xml; charset=utf-8",
34 | },
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/apps/web/app/blog/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from "react"
2 |
3 | export default function RootBlogLayout({ children }: { children: ReactNode }) {
4 | return <>{children}>
5 | }
6 |
--------------------------------------------------------------------------------
/apps/web/app/blog/og/page.tsx:
--------------------------------------------------------------------------------
1 | import { CodeHikeLogo } from "../../../ui/nav"
2 |
3 | const title = "Fine-grained Markdown"
4 | const description = "Flexible content, richer presentation"
5 |
6 | export default function Page() {
7 | return (
8 |
9 |
13 |
14 |
15 | Code Hike's blog
16 |
17 |
18 |
19 |
{title}
20 |
21 | {description}
22 |
23 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/apps/web/app/demo/client.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | AnnotationHandler,
5 | CustomPreProps,
6 | InnerPre,
7 | getPreRef,
8 | } from "codehike/code"
9 | import { forwardRef } from "react"
10 | import React from "react"
11 | import {
12 | TokenTransitionsSnapshot,
13 | calculateTransitions,
14 | getStartingSnapshot,
15 | } from "codehike/utils/token-transitions"
16 |
17 | const MAX_TRANSITION_DURATION = 900 // milliseconds
18 | export class SmoothPre extends React.Component {
19 | ref: React.RefObject
20 | constructor(props: CustomPreProps) {
21 | super(props)
22 | this.ref = getPreRef(this.props)
23 | }
24 |
25 | render() {
26 | return
27 | }
28 |
29 | getSnapshotBeforeUpdate() {
30 | return getStartingSnapshot(this.ref.current!)
31 | }
32 |
33 | componentDidUpdate(
34 | prevProps: never,
35 | prevState: never,
36 | snapshot: TokenTransitionsSnapshot,
37 | ) {
38 | const transitions = calculateTransitions(this.ref.current!, snapshot)
39 | transitions.forEach(({ element, keyframes, options }) => {
40 | const { translateX, translateY, ...kf } = keyframes as any
41 | if (translateX && translateY) {
42 | kf.translate = [
43 | `${translateX[0]}px ${translateY[0]}px`,
44 | `${translateX[1]}px ${translateY[1]}px`,
45 | ]
46 | }
47 | element.animate(kf, {
48 | duration: options.duration * MAX_TRANSITION_DURATION,
49 | delay: options.delay * MAX_TRANSITION_DURATION,
50 | easing: options.easing,
51 | fill: "both",
52 | })
53 | })
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/apps/web/app/demo/content.mdx:
--------------------------------------------------------------------------------
1 | normal, extra spacer right
2 |
3 | ```js
4 | function lorem(ipsum, dolor = 1) {
5 | // !mark
6 | dolor = sit - amet(dolor)
7 | dolor = sit - amet(dolor)
8 | dolor = sit - amet(dolor)
9 | dolor = sit - amet(dolor)
10 | // !mark
11 | dolor = sit - amet(dolor)
12 | dolor = sit - amet(dolor)
13 | // !diff +
14 | dolor = sit - amet(dolor)
15 | dolor = sit - amet(dolor)
16 | dolor = sit - amet(dolor)
17 | dolor = sit - amet(dolor)
18 | // !collapse(1:3)
19 | dolor = sit - amet(dolor)
20 | // !callout[/sit/] hey
21 | dolor = sit - amet(dolor)
22 | dolor = sit - amet(dolor)
23 | }
24 | ```
25 |
26 |
27 |
28 | overflow
29 |
30 | ```js
31 | function lorem(ipsum, dolor = 1) {
32 | // !mark
33 | dolor = sit
34 | }
35 | ```
36 |
37 |
38 |
39 |
40 |
41 | wrap
42 |
43 | ```js w
44 | // !callout[/lorem/] hey
45 | function lorem(ipsum) {
46 | // !mark
47 | dolor = sit - amet(dolor) - consectetur(ipsum)
48 | // !callout[/dolor/] hey
49 | if (dolor === 1) {
50 | // !callout[/sit/] hey
51 | dolor = sit
52 | }
53 | }
54 | ```
55 |
56 |
57 |
--------------------------------------------------------------------------------
/apps/web/app/docs/[[...slug]]/code-example.tsx:
--------------------------------------------------------------------------------
1 | import { Block, parseRoot } from "codehike/blocks"
2 | import React from "react"
3 |
4 | const ContentSchema = Block.extend({})
5 |
6 | export function CodeExample({ MDX }: { MDX: any }) {
7 | const { children } = parseRoot(MDX, ContentSchema, {
8 | components: { PreviewContainer },
9 | })
10 |
11 | return children
12 | }
13 |
14 | function PreviewContainer({ children }: { children: React.ReactNode }) {
15 | return (
16 |
17 | {children}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/app/docs/[[...slug]]/preview-implementation.tsx:
--------------------------------------------------------------------------------
1 | import { Block, parseRoot } from "codehike/blocks"
2 | import React from "react"
3 | import { z } from "zod"
4 | import { Demo } from "@/components/demo"
5 | import { Pre, RawCode, highlight } from "codehike/code"
6 | import { CopyButton } from "@/components/copy-button"
7 |
8 | const ContentSchema = Block.extend({
9 | demo: Block,
10 | implementation: Block,
11 | })
12 |
13 | export function PreviewImplementation({ MDX }: { MDX: any }) {
14 | const { demo, implementation } = parseRoot(MDX, ContentSchema, {
15 | components: { Demo },
16 | })
17 |
18 | return (
19 | <>
20 | {demo.children}
21 | Implementation
22 | {implementation.children}
23 | >
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/apps/web/app/docs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { docs } from "../source"
2 | import { DocsLayout } from "next-docs-ui/layout"
3 | import type { ReactNode } from "react"
4 | export default function RootDocsLayout({ children }: { children: ReactNode }) {
5 | return (
6 | <>
7 |
14 | {children}
15 |
16 | >
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/favicon.ico
--------------------------------------------------------------------------------
/apps/web/app/landing/demo.md:
--------------------------------------------------------------------------------
1 | ```jsx !page page.jsx
2 | import Content from "./content.md"
3 | import { parse } from "codehike"
4 |
5 | // !rainbow(1:2)
6 | // extract structured content:
7 | // !tt[/content/] foo
8 | const content = parse(Content)
9 |
10 | export function Page() {
11 | // !rainbow(3:5) 1
12 | return (
13 |
14 | {/* render it as you want: */}
15 |
16 |
17 |
18 | )
19 | }
20 | ```
21 |
22 | ```jsx !content
23 | content = {
24 | // !block(1:4) 1
25 | intro: {
26 | title: "Roman Emperors",
27 | children: The ...
,
28 | },
29 | emperors: [
30 | // !block(1:9) 2
31 | {
32 | title: "Augustus",
33 | children: The ...
,
34 | img: { src: "/one.png" },
35 | code: {
36 | lang: "js",
37 | value: "console.log(1)",
38 | },
39 | },
40 | // !block 3
41 | { title: "Nero", ... },
42 | // !block 4
43 | { title: "Trajan", ... },
44 | ],
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/apps/web/app/landing/logo.drivly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/logo.drivly.png
--------------------------------------------------------------------------------
/apps/web/app/landing/logo.github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/logo.github.png
--------------------------------------------------------------------------------
/apps/web/app/landing/logo.meta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/logo.meta.png
--------------------------------------------------------------------------------
/apps/web/app/landing/logo.uidev.svg:
--------------------------------------------------------------------------------
1 | ui.dev
--------------------------------------------------------------------------------
/apps/web/app/landing/placeholdifier-monospace.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/placeholdifier-monospace.woff2
--------------------------------------------------------------------------------
/apps/web/app/landing/placeholdifier.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/placeholdifier.woff2
--------------------------------------------------------------------------------
/apps/web/app/landing/profile.md:
--------------------------------------------------------------------------------
1 | I ([@pomber](https://github.com/pomber)) am working full-time on this project. The official reason is that _I want to raise the bar on the formatting of technical content_, but the main reason is that I'm having a lot of fun building it.
2 |
3 | The goal of [Code Hike](https://codehike.org) is to provide the best experience for both authors and consumers of technical content on the web.
4 |
5 | Your sponsorship not only helps keep the project running but also supports the innovation that inspired some of your favorite websites.
6 |
7 | Sponsorship perks:
8 |
9 | - **$299/month or more**: I'll join your company's Slack channel for direct feedbacks and feature requests.
10 | - **$499/month or more**: You get a spot on our Top Sponsors (limited to 6 spots).
11 | - **$999/month or more**: You get up to 1 hour a month of personalized support (two 30-minute calls).
12 |
--------------------------------------------------------------------------------
/apps/web/app/landing/sponsors/gh-sponsors.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config({ path: ".env.local" })
2 |
3 | const GITHUB_TOKEN = process.env.GITHUB_TOKEN
4 | if (!GITHUB_TOKEN) {
5 | console.log("Missing process.env.GITHUB_TOKEN")
6 | process.exit(1)
7 | }
8 |
9 | async function fetchSponsors() {
10 | let pageInfo = { hasNextPage: true, endCursor: null }
11 | let sponsors = []
12 |
13 | while (pageInfo.hasNextPage) {
14 | const page = await fetchPage(pageInfo.endCursor)
15 | sponsors = sponsors.concat(page.nodes)
16 | pageInfo = page.pageInfo
17 | }
18 |
19 | return sponsors.map(({ sponsor, amountInCents }) => ({
20 | name: sponsor.login,
21 | amount: Math.round(amountInCents / 100),
22 | }))
23 | }
24 |
25 | async function fetchPage(cursor) {
26 | const query = `{
27 | organization(login: "code-hike") {
28 | lifetimeReceivedSponsorshipValues(first: 100, after: "${cursor}") {
29 | nodes {
30 | amountInCents
31 | sponsor {
32 | ... on User { login }
33 | ... on Organization { login }
34 | }
35 | }
36 | pageInfo {
37 | endCursor
38 | hasNextPage
39 | }
40 | }
41 | }
42 | }`
43 | const r = await fetch("https://api.github.com/graphql", {
44 | method: "POST",
45 | body: JSON.stringify({ query }),
46 | headers: { Authorization: "bearer " + GITHUB_TOKEN },
47 | })
48 | if (!r.ok) {
49 | console.error(r)
50 | return
51 | }
52 | const { data, errors } = await r.json()
53 | if (errors) {
54 | console.error(JSON.stringify(errors))
55 | return
56 | }
57 | return data.organization.lifetimeReceivedSponsorshipValues
58 | }
59 |
60 | module.exports = fetchSponsors
61 |
--------------------------------------------------------------------------------
/apps/web/app/landing/sponsors/oc-sponsors.js:
--------------------------------------------------------------------------------
1 | const parseCSV = require("./csv")
2 | const path = require("path")
3 |
4 | function getSponsors() {
5 | // https://opencollective.com/codehike/transactions?type=CREDIT
6 | const data = parseCSV(path.join(__dirname, "./codehike-transactions.csv"))
7 |
8 | const transactions = data.map((d) => ({
9 | date: d["date"],
10 | name: d["oppositeAccountSlug"],
11 | amount: parseInt(d["amount"]),
12 | }))
13 |
14 | const totals = {}
15 | transactions.forEach((t) => {
16 | totals[t.name] = (totals[t.name] || 0) + t.amount
17 | })
18 |
19 | const list = []
20 | for (let name in totals) {
21 | list.push({ name, amount: Math.round(totals[name]) })
22 | }
23 |
24 | return list
25 | }
26 |
27 | module.exports = getSponsors
28 |
--------------------------------------------------------------------------------
/apps/web/app/landing/sponsors/update.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs")
2 | const path = require("path")
3 |
4 | const getOpenCollectiveSponsors = require("./oc-sponsors.js")
5 | const fetchGitHubSponsors = require("./gh-sponsors.js")
6 |
7 | const ignore = ["outerbounds", "github-sponsors", "guest-fbd7c737"]
8 | const replace = {
9 | speakeasybot: "speakeasy-api",
10 | ndimares: "speakeasy-api",
11 | hamelsmu: "outerbounds",
12 | "severin-ibarluzea": "seveibar",
13 | fbopensource: "facebook",
14 | }
15 | const others = [
16 | // from paypal
17 | { name: "matthiaszepper", amount: Math.round(94.3 + 108.71 + 47) },
18 | // missing from gh api
19 | { name: "github", amount: 20550 },
20 | ]
21 |
22 | Promise.all([getOpenCollectiveSponsors(), fetchGitHubSponsors()]).then(
23 | ([ocSponsors, ghSponsors]) => {
24 | const all = [...ocSponsors, ...ghSponsors, ...others]
25 | .filter((s) => !ignore.includes(s.name))
26 | .map((s) => ({ name: replace[s.name] || s.name, amount: s.amount }))
27 |
28 | const sponsors = []
29 | all.forEach((s) => {
30 | const index = sponsors.findIndex((sp) => sp.name === s.name)
31 | if (index === -1) {
32 | sponsors.push(s)
33 | } else {
34 | sponsors[index].amount += s.amount
35 | }
36 | })
37 |
38 | sponsors.sort((a, b) => b.amount - a.amount)
39 |
40 | console.table(sponsors)
41 | fs.writeFileSync(
42 | path.join(__dirname, "../sponsors.json"),
43 | JSON.stringify(sponsors, null, 2),
44 | )
45 | },
46 | )
47 |
--------------------------------------------------------------------------------
/apps/web/app/landing/text.github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/app/landing/text.github.png
--------------------------------------------------------------------------------
/apps/web/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./global.css"
2 | import { RootProvider } from "next-docs-ui/provider"
3 | import { Inter } from "next/font/google"
4 | import type { ReactNode } from "react"
5 | import { NavBar } from "../ui/nav"
6 | import { Analytics } from "@vercel/analytics/react"
7 |
8 | const inter = Inter({
9 | subsets: ["latin"],
10 | })
11 |
12 | import ch from "codehike/package.json"
13 | import { Metadata } from "next"
14 |
15 | export default function Layout({ children }: { children: ReactNode }) {
16 | return (
17 |
18 |
19 |
20 |
21 | {children}
22 |
23 |
24 |
25 |
26 | )
27 | }
28 | export const metadata: Metadata = {
29 | title: "Code Hike",
30 | description:
31 | "Use Markdown and React to build rich content websites. Documentation, tutorials, blogs, videos, interactive walkthroughs, and more.",
32 | metadataBase: new URL("https://v1.codehike.org"),
33 | openGraph: {
34 | title: "Code Hike",
35 | images: `/codehike.png`,
36 | siteName: "Code Hike",
37 | },
38 | twitter: {
39 | card: "summary_large_image",
40 | site: "@codehike_",
41 | creator: "@pomber",
42 | images: `/codehike.png`,
43 | },
44 | alternates: {
45 | types: {
46 | "application/rss+xml": "https://v1.codehike.org/blog/feed.xml",
47 | },
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/apps/web/app/not-found.tsx:
--------------------------------------------------------------------------------
1 | export default function NotFound() {
2 | return (
3 |
4 | 404
5 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/app/play/page.tsx:
--------------------------------------------------------------------------------
1 | export default function Playground() {
2 | return (
3 |
4 | // TO DO
5 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/app/source.ts:
--------------------------------------------------------------------------------
1 | import { map } from "@/.map"
2 | import { createMDXSource, defaultSchemas } from "next-docs-mdx"
3 | import { loader } from "next-docs-zeta/source"
4 | import { z } from "zod"
5 |
6 | export const docs = loader({
7 | baseUrl: "/docs",
8 | rootDir: "docs",
9 | source: createMDXSource(map, {
10 | schema: {
11 | frontmatter: defaultSchemas.frontmatter.extend({
12 | layout: z.string().default("Docs"),
13 | }),
14 | },
15 | }),
16 | })
17 |
18 | export const blog = loader({
19 | baseUrl: "/blog",
20 | rootDir: "blog",
21 | source: createMDXSource(map, {
22 | schema: {
23 | frontmatter: defaultSchemas.frontmatter.extend({
24 | authors: z.array(z.string()).default([]),
25 | date: z.date().default(new Date()),
26 | draft: z.boolean().default(false),
27 | className: z.string().default(""),
28 | }),
29 | },
30 | }),
31 | })
32 |
--------------------------------------------------------------------------------
/apps/web/app/test/client.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | AnnotationHandler,
5 | CustomPreProps,
6 | InnerPre,
7 | getPreRef,
8 | } from "codehike/code"
9 | import { forwardRef } from "react"
10 | import React from "react"
11 | import {
12 | TokenTransitionsSnapshot,
13 | calculateTransitions,
14 | getStartingSnapshot,
15 | } from "codehike/utils/token-transitions"
16 |
17 | const MAX_TRANSITION_DURATION = 900 // milliseconds
18 | export class SmoothPre extends React.Component {
19 | ref: React.RefObject
20 | constructor(props: CustomPreProps) {
21 | super(props)
22 | this.ref = getPreRef(this.props)
23 | }
24 |
25 | render() {
26 | return
27 | }
28 |
29 | getSnapshotBeforeUpdate() {
30 | return getStartingSnapshot(this.ref.current!)
31 | }
32 |
33 | componentDidUpdate(
34 | prevProps: never,
35 | prevState: never,
36 | snapshot: TokenTransitionsSnapshot,
37 | ) {
38 | const transitions = calculateTransitions(this.ref.current!, snapshot)
39 | transitions.forEach(({ element, keyframes, options }) => {
40 | const { translateX, translateY, ...kf } = keyframes as any
41 | if (translateX && translateY) {
42 | kf.translate = [
43 | `${translateX[0]}px ${translateY[0]}px`,
44 | `${translateX[1]}px ${translateY[1]}px`,
45 | ]
46 | }
47 | element.animate(kf, {
48 | duration: options.duration * MAX_TRANSITION_DURATION,
49 | delay: options.delay * MAX_TRANSITION_DURATION,
50 | easing: options.easing,
51 | fill: "both",
52 | })
53 | })
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/apps/web/app/test/content.md:
--------------------------------------------------------------------------------
1 | ## The mother of all codeblocks
2 |
3 | ```js foo.js -awnc
4 | // !collapse(1:4)
5 | function lorem(ipsum, dolor = 1) {
6 | const sit = ipsum == null ? 0 : ipsum.sit
7 | // !mark(1:2)
8 | dolor = sit - amet(dolor)
9 | // !callout[/sit/] lorem ipsum dolor sit
10 | return sit ? consectetur(ipsum) : []
11 | }
12 |
13 | // !collapse(1:4) collapsed
14 | function ipsum(ipsum, dolor = 1) {
15 | const sit = ipsum == null ? 0 : ipsum.sit
16 | // !mark(1:2)
17 | dolor = sit - amet(dolor)
18 | return sit ? consectetur(ipsum) : []
19 | }
20 | ```
21 |
22 | ```js foo.js -anc
23 | // !collapse(1:4)
24 | function lorem(ipsum, dolor = 1) {
25 | const sit = ipsum == null ? 0 : ipsum.sit
26 | // !mark(1:2)
27 | dolor = sit - amet(dolor)
28 | // !callout[/sit/] lorem ipsum dolor sit
29 | return sit ? consectetur(ipsum) : []
30 | }
31 |
32 | // !collapse(1:4) collapsed
33 | function ipsum(ipsum, dolor = 1) {
34 | const sit = ipsum == null ? 0 : ipsum.sit
35 | // !mark(1:2)
36 | dolor = sit - amet(dolor)
37 | return sit ? consectetur(ipsum) : []
38 | }
39 | ```
40 |
41 | ```js -c
42 | function lorem(ipsum, dolor = 1) {
43 | const sit = ipsum == null ? 0 : ipsum.sit
44 | // !mark(1:2)
45 | dolor = sit - amet(dolor)
46 | // !callout[/sit/] lorem ipsum dolor sit
47 | return sit ? consectetur(ipsum) : []
48 | }
49 |
50 | function ipsum(ipsum, dolor = 1) {
51 | const sit = ipsum == null ? 0 : ipsum.sit
52 | // !mark(1:2)
53 | dolor = sit - amet(dolor)
54 | return sit ? consectetur(ipsum) : []
55 | }
56 | ```
57 |
58 | ```bash -c
59 | npx create-next-app -e https://github.com/code-hike/v1-starter
60 | ```
61 |
--------------------------------------------------------------------------------
/apps/web/app/test/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { Code } from "@/components/code"
3 |
4 | export default function Page() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web/app/top-sponsors.png/route.tsx:
--------------------------------------------------------------------------------
1 | import { ImageResponse } from "next/og"
2 |
3 | export const dynamic = "force-static"
4 |
5 | export async function GET(request: Request) {
6 | return new ImageResponse(
7 | (
8 |
9 | hello, world
10 |
11 |
12 | ),
13 | {
14 | width: 600,
15 | height: 400,
16 | },
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/global.css",
9 | "baseColor": "zinc",
10 | "cssVariables": false,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/web/components/all-code-demos.tsx:
--------------------------------------------------------------------------------
1 | import { docs } from "@/app/source"
2 | import { Block, parseRoot } from "codehike/blocks"
3 | import { Demo } from "@/components/demo"
4 | import { CodeWithNotes } from "@/components/code/code-with-notes"
5 | import Link from "next/link"
6 |
7 | export function AllCodeDemos() {
8 | const p = docs.getPages()
9 | const codePages = p.filter((page) => page.slugs[0] === "code")
10 | const demoPages = codePages.filter(
11 | (page) => page.data.layout === "PreviewAndImplementation",
12 | )
13 |
14 | return demoPages.map((page) => {
15 | const { title, exports } = page.data
16 | const { default: MDX } = exports
17 | const { demo } = parseRoot(MDX, Block.extend({ demo: Block }), {
18 | components: { Demo, CodeWithNotes },
19 | })
20 | const href = `/docs/${page.slugs.join("/")}`
21 |
22 | return (
23 |
24 |
{title}
25 | {demo.children}
26 |
27 | See {title} implementation.
28 |
29 |
30 | )
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/callout.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InlineAnnotation, InnerLine } from "codehike/code"
2 |
3 | export const callout: AnnotationHandler = {
4 | name: "callout",
5 | transform: (annotation: InlineAnnotation) => {
6 | // transform inline annotation to block annotation
7 | const { name, query, lineNumber, fromColumn, toColumn } = annotation
8 | return {
9 | name,
10 | query,
11 | fromLineNumber: lineNumber,
12 | toLineNumber: lineNumber,
13 | data: {
14 | ...annotation.data,
15 | column: (fromColumn + toColumn) / 2,
16 | },
17 | }
18 | },
19 | AnnotatedLine: ({ annotation, ...props }) => {
20 | const { column } = annotation.data
21 | const { indentation, children } = props
22 | return (
23 |
24 | {children}
25 |
32 |
36 | {annotation.data.children || (
37 |
{annotation.query}
38 | )}
39 |
40 |
41 | )
42 | },
43 | }
44 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/diff.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InnerLine, BlockAnnotation } from "codehike/code"
2 |
3 | export const diff: AnnotationHandler = {
4 | name: "diff",
5 | onlyIfAnnotated: true,
6 | transform: (annotation: BlockAnnotation) => {
7 | const color = annotation.query == "-" ? "#f85149" : "#3fb950"
8 | return [annotation, { ...annotation, name: "mark", query: color }]
9 | },
10 | Line: ({ annotation, ...props }) => (
11 | <>
12 |
13 | {annotation?.query}
14 |
15 |
16 | >
17 | ),
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/focus.client.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { CustomPreProps, InnerPre, getPreRef } from "codehike/code"
4 | import { useLayoutEffect, useRef } from "react"
5 |
6 | export function PreWithRef(props: CustomPreProps) {
7 | const ref = getPreRef(props)
8 | useScrollToFocus(ref)
9 | return
10 | }
11 |
12 | function useScrollToFocus(ref: React.RefObject) {
13 | const firstRender = useRef(true)
14 | useLayoutEffect(() => {
15 | if (ref.current) {
16 | // find all descendants whith data-focus="true"
17 | const focusedElements = ref.current.querySelectorAll(
18 | "[data-focus=true]",
19 | ) as NodeListOf
20 |
21 | // container rect
22 | const containerRect = ref.current.getBoundingClientRect()
23 |
24 | // find top and bottom of the focused elements
25 | let top = Infinity
26 | let bottom = -Infinity
27 | focusedElements.forEach((el) => {
28 | const rect = el.getBoundingClientRect()
29 | top = Math.min(top, rect.top - containerRect.top)
30 | bottom = Math.max(bottom, rect.bottom - containerRect.top)
31 | })
32 |
33 | // scroll to the focused elements if any part of them is not visible
34 | if (bottom > containerRect.height || top < 0) {
35 | ref.current.scrollTo({
36 | top: ref.current.scrollTop + top - 10,
37 | behavior: firstRender.current ? "instant" : "smooth",
38 | })
39 | }
40 | firstRender.current = false
41 | }
42 | })
43 | }
44 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/focus.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InnerLine } from "codehike/code"
2 | import { PreWithRef } from "./focus.client"
3 |
4 | export const focus: AnnotationHandler = {
5 | name: "focus",
6 | PreWithRef,
7 | onlyIfAnnotated: true,
8 | Line: (props) => (
9 |
10 | ),
11 | AnnotatedLine: ({ annotation, ...props }) => (
12 |
13 | ),
14 | }
15 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/fold.client.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { AnnotationHandler } from "codehike/code"
4 | import { useState } from "react"
5 |
6 | export const InlineFold: AnnotationHandler["Inline"] = ({
7 | annotation,
8 | children,
9 | }) => {
10 | const [folded, setFolded] = useState(true)
11 | if (!folded) {
12 | return children
13 | }
14 | return (
15 | setFolded(false)}
17 | aria-label="Expand"
18 | title="Click to expand"
19 | className="bg-editorGroupHeader-tabsBackground rounded px-1"
20 | >
21 | ...
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/fold.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler } from "codehike/code"
2 | import { InlineFold } from "./fold.client"
3 |
4 | export const fold: AnnotationHandler = {
5 | name: "fold",
6 | Inline: InlineFold,
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/icons.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode } from "codehike/code"
2 | import { themeIcons } from "seti-icons"
3 |
4 | export function CodeIcon({ title }: { title: string }) {
5 | let filename = title || ""
6 | if (filename.endsWith(".mdx")) {
7 | filename = filename.slice(0, -4)
8 | filename += ".md"
9 | } else if (filename.endsWith(".mjs")) {
10 | filename = filename.slice(0, -4)
11 | filename += ".js"
12 | }
13 |
14 | const { svg, color } = getLightIcon(filename)
15 | const __html = svg.replace(
16 | /svg/,
17 | `svg fill='${color}' height='28' style='margin: -8px'`,
18 | )
19 | return (
20 |
24 | )
25 | }
26 |
27 | const getDarkIcon = themeIcons({
28 | blue: "#519aba",
29 | grey: "#4d5a5e",
30 | "grey-light": "#6d8086",
31 | green: "#8dc149",
32 | orange: "#e37933",
33 | pink: "#f55385",
34 | purple: "#a074c4",
35 | red: "#cc3e44",
36 | white: "#d4d7d6",
37 | yellow: "#cbcb41",
38 | ignore: "#41535b",
39 | })
40 |
41 | const getLightIcon = themeIcons({
42 | blue: "#498ba7",
43 | grey: "#455155",
44 | "grey-light": "#627379",
45 | green: "#7fae42",
46 | orange: "#f05138",
47 | pink: "#dd4b78",
48 | purple: "#9068b0",
49 | red: "#b8383d",
50 | white: "#bfc2c1",
51 | yellow: "#b7b73b",
52 | ignore: "#3b4b52",
53 | })
54 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/line-numbers.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InnerLine } from "codehike/code"
2 |
3 | export const lineNumbers: AnnotationHandler = {
4 | name: "line-numbers",
5 | Line: (props) => {
6 | const width = props.totalLines.toString().length + 1
7 | return (
8 | <>
9 |
13 | {props.lineNumber}
14 |
15 |
16 | >
17 | )
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/link.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler } from "codehike/code"
2 | import Link from "next/link"
3 |
4 | export const link: AnnotationHandler = {
5 | name: "link",
6 | Inline: ({ annotation, children }) => {
7 | const { query } = annotation
8 |
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/mark.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, BlockAnnotation, InnerLine } from "codehike/code"
2 |
3 | export const mark: AnnotationHandler = {
4 | name: "mark",
5 | Line: ({ annotation, ...props }) => {
6 | const color = getColor(annotation)
7 | return (
8 |
16 |
17 |
18 | )
19 | },
20 | Inline: ({ annotation, children }) => {
21 | const color = getColor(annotation)
22 | return (
23 |
30 | {children}
31 |
32 | )
33 | },
34 | }
35 |
36 | function getColor(annotation?: { query?: string }) {
37 | const n = Number(annotation?.query || "2") % colors.length
38 | return colors[n] || annotation?.query
39 | }
40 |
41 | const colors = [
42 | "#22c55e",
43 | "#14b8a6",
44 | "#0ea5e9",
45 | "#8b5cf6",
46 | "#d946ef",
47 | "#ec4899",
48 | ]
49 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/pill.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler } from "codehike/code"
2 |
3 | const colors = [
4 | "bg-green-500/20",
5 | "bg-teal-500/20",
6 | "bg-sky-500/20",
7 | "bg-violet-500/20",
8 | "bg-fuchsia-500/20",
9 | "bg-pink-500/20",
10 | // if adding more colors, dont forget to update global.css
11 | ]
12 |
13 | export const pill: AnnotationHandler = {
14 | name: "pill",
15 | Inline: ({ annotation, children }) => {
16 | const n = Number(annotation.query || "1")
17 | const bg = colors[n % colors.length]
18 | return {children}
19 | },
20 | }
21 |
22 | export function Pill({
23 | children,
24 | n = 1,
25 | }: {
26 | children: React.ReactNode
27 | n: number
28 | }) {
29 | const bg = colors[n % colors.length]
30 | return (
31 |
35 | {children}
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/ruler.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler } from "codehike/code"
2 |
3 | const colors = [
4 | "bg-green-500/20",
5 | "bg-teal-500/20",
6 | "bg-sky-500/20",
7 | "bg-violet-500/20",
8 | "bg-fuchsia-500/20",
9 | "bg-pink-500/20",
10 | // if adding more colors, dont forget to update global.css
11 | ]
12 |
13 | // needs ruler-group class in container
14 |
15 | export const ruler: AnnotationHandler = {
16 | name: "ruler",
17 | Block: ({ annotation, children }) => {
18 | const [k, c] = annotation.query?.split(" ")
19 | const n = Number(k || "1") % colors.length
20 | const bg = colors[n]
21 | return (
22 |
23 |
27 |
30 |
{children}
31 |
32 | )
33 | },
34 | }
35 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/token-transitions.client.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { CustomPreProps, InnerPre, getPreRef } from "codehike/code"
4 | import {
5 | TokenTransitionsSnapshot,
6 | calculateTransitions,
7 | getStartingSnapshot,
8 | } from "codehike/utils/token-transitions"
9 | import React from "react"
10 |
11 | const MAX_TRANSITION_DURATION = 900 // milliseconds
12 | export class PreWithRef extends React.Component {
13 | ref: React.RefObject
14 | constructor(props: CustomPreProps) {
15 | super(props)
16 | this.ref = getPreRef(this.props)
17 | }
18 |
19 | render() {
20 | return
21 | }
22 |
23 | getSnapshotBeforeUpdate() {
24 | return getStartingSnapshot(this.ref.current!)
25 | }
26 |
27 | componentDidUpdate(
28 | prevProps: never,
29 | prevState: never,
30 | snapshot: TokenTransitionsSnapshot,
31 | ) {
32 | const transitions = calculateTransitions(this.ref.current!, snapshot)
33 | transitions.forEach(({ element, keyframes, options }) => {
34 | const { translateX, translateY, ...kf } = keyframes as any
35 | if (translateX && translateY) {
36 | kf.translate = [
37 | `${translateX[0]}px ${translateY[0]}px`,
38 | `${translateX[1]}px ${translateY[1]}px`,
39 | ]
40 | }
41 | element.animate(kf, {
42 | duration: options.duration * MAX_TRANSITION_DURATION,
43 | delay: options.delay * MAX_TRANSITION_DURATION,
44 | easing: options.easing,
45 | fill: "both",
46 | })
47 | })
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/token-transitions.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InnerToken } from "codehike/code"
2 | import { PreWithRef } from "./token-transitions.client"
3 |
4 | export const tokenTransitions: AnnotationHandler = {
5 | name: "token-transitions",
6 | PreWithRef,
7 | Token: (props) => (
8 |
9 | ),
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/tooltip.tsx:
--------------------------------------------------------------------------------
1 | import { AnnotationHandler, InlineAnnotationComponent } from "codehike/code"
2 | import {
3 | TooltipProvider,
4 | Tooltip,
5 | TooltipTrigger,
6 | TooltipContent,
7 | TooltipArrow,
8 | } from "@/components/ui/tooltip"
9 |
10 | export const tooltip: AnnotationHandler = {
11 | name: "tooltip",
12 | Inline: ({ children, annotation }) => {
13 | const { query, data } = annotation
14 | return (
15 |
16 |
17 |
18 | {children}
19 |
20 |
26 | {data?.children || query}
27 |
28 |
29 |
30 | )
31 | },
32 | }
33 |
--------------------------------------------------------------------------------
/apps/web/components/annotations/word-wrap.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AnnotationHandler,
3 | InnerLine,
4 | InnerPre,
5 | InnerToken,
6 | } from "codehike/code"
7 |
8 | export const wordWrap: AnnotationHandler = {
9 | name: "word-wrap",
10 | Pre: (props) => ,
11 | Line: (props) => (
12 |
13 |
19 | {props.children}
20 |
21 |
22 | ),
23 | Token: (props) => ,
24 | }
25 |
--------------------------------------------------------------------------------
/apps/web/components/code/code-with-notes.tsx:
--------------------------------------------------------------------------------
1 | import { highlight } from "codehike/code"
2 | import { Block, CodeBlock, ImageBlock, parseProps } from "codehike/blocks"
3 | import { z } from "zod"
4 | import theme from "@/theme.mjs"
5 | import { HighCode } from "../code"
6 |
7 | const ContentSchema = z.object({
8 | code: CodeBlock,
9 | notes: z
10 | .array(
11 | Block.extend({
12 | code: CodeBlock.optional(),
13 | }),
14 | )
15 | .optional(),
16 | })
17 |
18 | export async function CodeWithNotes(props: unknown) {
19 | const { code, notes = [] } = parseProps(props, ContentSchema)
20 | const highlighted = await highlight(code, theme)
21 |
22 | // find matches between annotations and notes
23 | // and add the note as data to the annotation
24 | highlighted.annotations = await Promise.all(
25 | highlighted.annotations.map(async (a) => {
26 | const note = notes.find((n) => a.query && n.title === a.query)
27 | if (!note) return a
28 |
29 | let children = note.children
30 | if (note.code) {
31 | const highlighted = await highlight(note.code, theme)
32 | children = (
33 |
34 | )
35 | }
36 |
37 | return {
38 | ...a,
39 | data: {
40 | ...a.data,
41 | children,
42 | },
43 | }
44 | }),
45 | )
46 |
47 | return
48 | }
49 |
--------------------------------------------------------------------------------
/apps/web/components/code/side-by-side.tsx:
--------------------------------------------------------------------------------
1 | export function SideBySide(props: React.HTMLProps) {
2 | return (
3 |
7 | )
8 | }
9 |
--------------------------------------------------------------------------------
/apps/web/components/copy-button.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { Copy, Check } from "lucide-react"
4 | import * as React from "react"
5 | import { cn } from "../lib/utils"
6 |
7 | export function CopyButton({
8 | text,
9 | className,
10 | }: {
11 | text: string
12 | className?: string
13 | }) {
14 | const [copied, setCopied] = React.useState(false)
15 |
16 | return (
17 | {
23 | navigator.clipboard.writeText(text)
24 | setCopied(true)
25 | setTimeout(() => setCopied(false), 1200)
26 | }}
27 | aria-label="Copy to clipboard"
28 | >
29 | {copied ? : }
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/apps/web/components/demo.tsx:
--------------------------------------------------------------------------------
1 | import fs from "fs"
2 | import path from "path"
3 | import { Code } from "./code"
4 | import { cn } from "../lib/utils"
5 |
6 | export async function Demo({
7 | name,
8 | children,
9 | className,
10 | content = "content.md",
11 | }: {
12 | name: string
13 | content?: string
14 | children: React.ReactNode
15 | className?: string
16 | }) {
17 | const value = await fs.promises.readFile(
18 | path.join(process.cwd(), "demos", name, content),
19 | "utf-8",
20 | )
21 |
22 | const usage = (
23 |
31 | )
32 |
33 | const { default: Page } = await import(`@/demos/${name}/page`)
34 |
35 | const preview = (
36 |
37 |
38 | {children && (
39 |
40 | {children}
41 |
42 | )}
43 |
44 | )
45 |
46 | return (
47 | *]:flex-1 [&>*]:min-w-72",
50 | className,
51 | )}
52 | >
53 |
{usage}
54 | {preview}
55 |
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/apps/web/components/layout-demo.tsx:
--------------------------------------------------------------------------------
1 | import fs from "fs"
2 | import path from "path"
3 | import { Tab, Tabs } from "next-docs-ui/components/tabs"
4 | import { Code } from "./code"
5 |
6 | export async function LayoutDemo({
7 | name,
8 | children,
9 | content = "content.md",
10 | }: {
11 | name: string
12 | content?: string
13 | children: React.ReactNode
14 | }) {
15 | const value = await fs.promises.readFile(
16 | path.join(process.cwd(), "demos", name, content),
17 | "utf-8",
18 | )
19 |
20 | const { default: Page } = await import(`@/demos/${name}/page`)
21 |
22 | return (
23 |
24 |
28 |
31 |
32 |
33 |
40 |
41 | {children}
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/apps/web/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/apps/web/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider
9 |
10 | const Tooltip = TooltipPrimitive.Root
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger
13 |
14 | const TooltipArrow = TooltipPrimitive.Arrow
15 |
16 | const TooltipContent = React.forwardRef<
17 | React.ElementRef,
18 | React.ComponentPropsWithoutRef
19 | >(({ className, sideOffset = 4, ...props }, ref) => (
20 |
29 | ))
30 | TooltipContent.displayName = TooltipPrimitive.Content.displayName
31 |
32 | export {
33 | Tooltip,
34 | TooltipTrigger,
35 | TooltipContent,
36 | TooltipProvider,
37 | TooltipArrow,
38 | }
39 |
--------------------------------------------------------------------------------
/apps/web/content/blog/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["remotion.mdx", "fine-grained-markdown.mdx"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 |
13 | {
14 | "files": ["v1.mdx"],
15 | "options": {
16 | "printWidth": 52,
17 | "semi": false
18 | }
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/apps/web/content/blog/bestiary.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Technical content bestiary
3 | description: Catalog of all the UI patterns commonly used in technical content.
4 | date: 2024-02-05
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | ## Hello World
10 |
11 | loremp ipsum
12 |
--------------------------------------------------------------------------------
/apps/web/content/blog/codeblocks.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Perfect codeblocks
3 | description: All the small details that make flawless codeblocks.
4 | date: 2024-01-02
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | Lorem ipsum
10 |
11 | ```js
12 | const a = 1
13 |
14 | // !Collapse(1:3) collapsed
15 | function foo() {
16 | let b = 2
17 | return a + b
18 | }
19 |
20 | // !Collapse(1:3)
21 | function bar() {
22 | let b = 2
23 | return a + b
24 | }
25 |
26 | console.log(foo())
27 | ```
28 |
29 | Lorem ipsum
30 |
31 | Lorem ipsum
32 |
33 | Lorem ipsum
34 |
35 | Lorem ipsum
36 |
37 | Lorem ipsum
38 |
39 | Lorem ipsum
40 |
41 | Lorem ipsum
42 |
43 | Lorem ipsum
44 |
45 | Lorem ipsum
46 |
47 | Lorem ipsum
48 |
49 | Lorem ipsum
50 |
51 | Lorem ipsum
52 |
53 | Lorem ipsum
54 |
55 | Lorem ipsum
56 |
57 | Lorem ipsum
58 |
59 | Lorem ipsum
60 |
61 | Lorem ipsum
62 |
63 | Lorem ipsum
64 |
65 | Lorem ipsum
66 |
67 | Lorem ipsum
68 |
69 | Lorem ipsum
70 |
71 | Lorem ipsum
72 |
73 | Lorem ipsum
74 |
75 | Lorem ipsum
76 |
77 | Lorem ipsum
78 |
79 | Lorem ipsum
80 |
--------------------------------------------------------------------------------
/apps/web/content/blog/fine-grained/code.tsx:
--------------------------------------------------------------------------------
1 | import { InnerToken, RawCode } from "codehike/code"
2 | import { Code as TheCode } from "@/components/code"
3 | import { AnnotationHandler, InnerLine } from "codehike/code"
4 | import { cn } from "@/lib/utils"
5 |
6 | export async function Code(props: { codeblock: RawCode; className?: string }) {
7 | return
8 | }
9 |
10 | export const fblock: AnnotationHandler = {
11 | name: "fblock",
12 | Line: ({ annotation, ...props }) => {
13 | const bg = "bg-sky-500/10"
14 | const border = "border-sky-500"
15 |
16 | return (
17 |
25 |
26 |
27 | )
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web/content/blog/from-remark-to-rsc.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: From Remark to React Server Components
3 | description: The right abstraction for better flexibility and composability
4 | date: 2024-02-20
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | A remark plugin is a function that transforms pieces of a markdown file.
10 |
--------------------------------------------------------------------------------
/apps/web/content/blog/headless-codeblocks.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Headless Codeblocks
3 | description: Headless codeblocks
4 | date: 2024-08-15
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | TO DO
10 |
--------------------------------------------------------------------------------
/apps/web/content/blog/hello-server.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: React Soup Components
3 | description: A fan-fiction about React and Food
4 | date: 2024-01-25
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | Hello Server
10 |
--------------------------------------------------------------------------------
/apps/web/content/blog/known-keys.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Known keys
3 | description: When you need an object where all the keys are mapped to one type except some known keys that are mapped to different types.
4 | date: 2024-02-20
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | Sources:
10 |
11 | - https://github.com/microsoft/TypeScript/issues/31153
12 | - https://github.dev/sindresorhus/type-fest/blob/main/source/merge.d.ts
13 |
14 | ```ts
15 | type MyType = {
16 | [s: string]: string
17 | foo: number
18 | }
19 | ```
20 |
21 | When you need an object where all the keys are mapped to one type except some known keys that are mapped to different types.
22 |
23 | Why Omit doesn't work:
24 |
25 | ```ts
26 | type Omit = Pick>
27 | ```
28 |
29 | ```ts
30 | interface FancyIndices {
31 | [x: symbol]: number
32 | [x: `${string}Token`]: string
33 | }
34 | ```
35 |
36 | does this work?
37 |
--------------------------------------------------------------------------------
/apps/web/content/blog/markdown.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Skeuomorphic Markdown
3 | description: Breaking the shackles of static content.
4 | date: 2023-12-24
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | This is how markdown is usually used:
10 |
11 | ```js
12 | import { frontmatter, Content } from "./some.md"
13 | ```
14 |
15 | Examples:
16 |
17 | - https://docs.astro.build/en/guides/content-collections/
18 | - https://contentlayer.dev/docs/concepts/content-modeling-dc68721f
19 | - https://nuejs.org/blog/introducing-nuemark/
20 |
21 | One of the reasons why markdown has so good authoring experience is because of its focus on content, as oppose to presentation.
22 |
23 | This suits linear layouts, but it's not enough for more complex layouts. For more complex layouts we need to group pieces of content in sections, and put those sections in a hierarchy.
24 |
--------------------------------------------------------------------------------
/apps/web/content/blog/migrate-to-ch.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrate to Code Hike
3 | description: You have an existing website and want to migrate to Code Hike.
4 | date: 2024-01-18
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
--------------------------------------------------------------------------------
/apps/web/content/blog/participation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: The participation continuum
3 | description: Active vs passive learning
4 | date: 2024-01-19
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | ## Hello World
10 |
11 | loremp ipsum
12 |
--------------------------------------------------------------------------------
/apps/web/content/blog/power-of-the-web.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: The power of the Web
3 | description: Embracing the uniqueness of the medium.
4 | date: 2023-12-29
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | Interactivity, personalization, accessiblity, collaboration.
10 |
--------------------------------------------------------------------------------
/apps/web/content/blog/remotion/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.jsx"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/apps/web/content/blog/remotion/00.jsx:
--------------------------------------------------------------------------------
1 | // !mark 2
2 | import Content from "./content.md"
3 |
4 | // !f
5 |
6 | import {
7 | parseRoot,
8 | Block,
9 | } from "code-hike/blocks"
10 | import { z } from "zod"
11 |
12 | const Schema = Block.extend({
13 | steps: z.array(Block),
14 | })
15 |
16 | // !mark(1:4) 4
17 | const { steps } = parseRoot(
18 | Content,
19 | Schema,
20 | )
21 |
22 | import {
23 | AbsoluteFill,
24 | Sequence,
25 | Composition,
26 | } from "remotion"
27 |
28 | const STEP_FRAMES = 60
29 | function Video({ steps }) {
30 | return (
31 |
34 | {/* !mark(1:10) 0 */}
35 | {steps.map((step, i) => (
36 |
42 | {step.children}
43 |
44 | ))}
45 |
46 | )
47 | }
48 |
49 | export function RemotionRoot() {
50 | return (
51 |
62 | )
63 | }
64 |
--------------------------------------------------------------------------------
/apps/web/content/blog/remotion/01.jsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 |
3 | // !mark(1:11)
4 | // !focus(1:11)
5 | const Schema = Block.extend({
6 | steps: z.array(
7 | Block.extend({
8 | code: HighlightedCodeBlock,
9 | }),
10 | ),
11 | })
12 | const { steps } = parseRoot(
13 | Content,
14 | Schema,
15 | )
16 |
17 | const STEP_FRAMES = 60
18 | function Video({ steps }) {
19 | return (
20 |
26 | {steps.map((step, i) => (
27 |
33 |
34 |
35 | ))}
36 |
37 | )
38 | }
39 |
40 | export function RemotionRoot() {
41 | return (
42 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/apps/web/content/blog/remotion/02.jsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 |
3 | const Schema = Block.extend({
4 | steps: z.array(
5 | Block.extend({
6 | code: HighlightedCodeBlock,
7 | }),
8 | ),
9 | })
10 | const { steps } = parseRoot(
11 | Content,
12 | Schema,
13 | )
14 |
15 | const STEP_FRAMES = 60
16 | function Video({ steps }) {
17 | return (
18 |
24 | {steps.map((step, i) => (
25 |
31 | {/* !focus */}
32 | {/* !mark */}
33 |
34 |
35 | ))}
36 |
37 | )
38 | }
39 |
40 | // !focus(1:3)
41 | // !mark(1:3)
42 | function Code({ code }) {
43 | return
44 | }
45 |
46 | export function RemotionRoot() {
47 | return (
48 |
59 | )
60 | }
61 |
--------------------------------------------------------------------------------
/apps/web/content/blog/remotion/03.jsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 |
3 | const Schema = Block.extend({
4 | steps: z.array(
5 | Block.extend({
6 | code: HighlightedCodeBlock,
7 | }),
8 | ),
9 | })
10 | const { steps } = parseRoot(
11 | Content,
12 | Schema,
13 | )
14 |
15 | const STEP_FRAMES = 60
16 | function Video({ steps }) {
17 | return (
18 |
24 | {steps.map((step, i) => (
25 |
31 |
32 |
33 | ))}
34 |
35 | )
36 | }
37 |
38 | // !focus(1:17)
39 | // !mark(1:17)
40 | function Code({ code }) {
41 | return (
42 |
43 | )
44 | }
45 |
46 | const mark = {
47 | name: "mark",
48 | Block: ({ children }) => {
49 | const background = "#F2CC6044"
50 | return (
51 |
52 | {children}
53 |
54 | )
55 | },
56 | }
57 |
58 | export function RemotionRoot() {
59 | return (
60 |
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/apps/web/content/blog/rsc-and-css.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: The server and the cascade
3 | description: How React Server Components make you better at CSS.
4 | authors: [pomber]
5 | date: 2024-02-10
6 | draft: true
7 | ---
8 |
--------------------------------------------------------------------------------
/apps/web/content/blog/test.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Test post
3 | description: test description
4 | date: 2024-06-20
5 | authors: [pomber]
6 | draft: true
7 | ---
8 |
9 | test
10 |
--------------------------------------------------------------------------------
/apps/web/content/components/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.mdx"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 | {
13 | "files": ["*.tsx"],
14 | "options": {
15 | "printWidth": 60,
16 | "semi": false
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/content/components/api-reference/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
3 |
4 | const Collapsible = CollapsiblePrimitive.Root
5 | const CollapsibleTrigger =
6 | CollapsiblePrimitive.CollapsibleTrigger
7 | const CollapsibleContent =
8 | CollapsiblePrimitive.CollapsibleContent
9 |
10 | export {
11 | Collapsible,
12 | CollapsibleTrigger,
13 | CollapsibleContent,
14 | }
15 |
--------------------------------------------------------------------------------
/apps/web/content/components/slideshow/slides.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import * as React from "react"
3 |
4 | const StepIndexContext = React.createContext<
5 | [number, React.Dispatch>]
6 | >([0, () => {}])
7 |
8 | export function Slides({
9 | slides,
10 | }: {
11 | slides: React.ReactNode[]
12 | }) {
13 | const [selectedIndex, setSelectedIndex] =
14 | React.useState(0)
15 |
16 | return (
17 |
20 | {slides[selectedIndex]}
21 |
22 | )
23 | }
24 |
25 | export function Controls({ length }: { length: number }) {
26 | const [selectedIndex, setSelectedIndex] =
27 | React.useContext(StepIndexContext)
28 |
29 | return (
30 |
31 |
34 | setSelectedIndex(Math.max(0, selectedIndex - 1))
35 | }
36 | >
37 | Prev
38 |
39 | {[...Array(length)].map((_, i) => (
40 | setSelectedIndex(i)}
46 | />
47 | ))}
48 |
51 | setSelectedIndex(
52 | Math.min(length - 1, selectedIndex + 1),
53 | )
54 | }
55 | >
56 | Next
57 |
58 |
59 | )
60 | }
61 |
--------------------------------------------------------------------------------
/apps/web/content/components/slideshow/slideshow.tsx:
--------------------------------------------------------------------------------
1 | import { Slides, Controls } from "./slides"
2 | import { Pre, RawCode, highlight } from "codehike/code"
3 |
4 | export function Slideshow({ hike }: { hike: any }) {
5 | return (
6 | (
8 |
9 |
10 |
11 |
12 |
{step.children}
13 |
14 | ))}
15 | />
16 | )
17 | }
18 |
19 | async function Code({ codeblock }: { codeblock: RawCode }) {
20 | const info = await highlight(codeblock, "github-dark")
21 | return (
22 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/apps/web/content/docs/api/code.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Code
3 | description: The Code component
4 | ---
5 |
6 | ```txt
7 | // TO DO
8 | ```
9 |
--------------------------------------------------------------------------------
/apps/web/content/docs/api/hike.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hike
3 | description: The Hike component
4 | ---
5 |
6 | ```txt
7 | // TO DO
8 | ```
9 |
--------------------------------------------------------------------------------
/apps/web/content/docs/api/schema.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Schema
3 | description: Hike Schema
4 | ---
5 |
6 | ```js
7 | // TO DO
8 | ```
9 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/classname.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: ClassName
3 | description: Add a class name to a piece of code
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Add a class name to a piece of code.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | ```tsx classname.tsx -c
16 | import { AnnotationHandler } from "codehike/code"
17 |
18 | export const className: AnnotationHandler = {
19 | name: "className",
20 | Block: ({ annotation, children }) => (
21 | {children}
22 | ),
23 | Inline: ({ annotation, children }) => (
24 | {children}
25 | ),
26 | }
27 | ```
28 |
29 | And then add the handler to your `Code` component:
30 |
31 | ```tsx code.tsx -c
32 | import { RawCode, Pre, highlight } from "codehike/code"
33 | import { className } from "./classname"
34 |
35 | async function Code({ codeblock }: { codeblock: RawCode }) {
36 | const highlighted = await highlight(codeblock, "github-dark")
37 | return
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/copy-button.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Copy Button
3 | description: Copy Button
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Add a copy button to a code block.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | First we make the button component:
16 |
17 | ```tsx button.tsx -c
18 | "use client"
19 |
20 | import { Copy, Check } from "lucide-react"
21 | import { useState } from "react"
22 |
23 | // !fold[/className="(.*?)"/gm]
24 | export function CopyButton({ text }: { text: string }) {
25 | const [copied, setCopied] = useState(false)
26 |
27 | return (
28 | {
32 | navigator.clipboard.writeText(text)
33 | setCopied(true)
34 | setTimeout(() => setCopied(false), 1200)
35 | }}
36 | >
37 | {copied ? : }
38 |
39 | )
40 | }
41 | ```
42 |
43 | And then we use it when rendering the code block:
44 |
45 | ```tsx code.tsx -c
46 | import { Pre, RawCode, highlight } from "codehike/code"
47 | import { CopyButton } from "./button"
48 |
49 | // !fold[/className="(.*?)"/gm]
50 | async function Code({ codeblock }: { codeblock: RawCode }) {
51 | const highlighted = await highlight(codeblock, "github-dark")
52 |
53 | return (
54 |
58 | )
59 | }
60 | ```
61 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/diff.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Diff
3 | description: Diff
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Show inserted and deleted lines.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | There are two parts:
16 |
17 | - for the `+` and `-` icons, we customize the `Line` and prepend the `annotation.query`
18 | - for the border and color we use the `transform` function to add `mark` annotations and use the `AnnotationHandler` from the [mark example](/docs/code/mark)
19 |
20 | ```tsx diff.tsx -c
21 | import { AnnotationHandler, InnerLine, BlockAnnotation } from "codehike/code"
22 | // !fold[/className="(.*?)"/gm]
23 |
24 | export const diff: AnnotationHandler = {
25 | name: "diff",
26 | onlyIfAnnotated: true,
27 | transform: (annotation: BlockAnnotation) => {
28 | const color = annotation.query == "-" ? "#f85149" : "#3fb950"
29 | return [annotation, { ...annotation, name: "mark", query: color }]
30 | },
31 | Line: ({ annotation, ...props }) => (
32 | <>
33 |
34 | {annotation?.query}
35 |
36 |
37 | >
38 | ),
39 | }
40 | ```
41 |
42 | Then pass the `mark` and `diff` handlers to the `Pre` component:
43 |
44 | ```tsx code.tsx -c
45 | import { diff } from "./diff"
46 | import { mark } from "./mark"
47 |
48 | async function Code({ codeblock }: { codeblock: RawCode }) {
49 | const highlighted = await highlight(codeblock, "github-dark")
50 | return
51 | }
52 | ```
53 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/filename.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: File name
3 | description: Show the file name in the code block
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Show a title or file name with the code block.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | ```tsx code.tsx -cw
16 | import { RawCode, Pre, highlight } from "codehike/code"
17 |
18 | // !fold[/className="(.*?)"/gm]
19 | async function Code({ codeblock }: { codeblock: RawCode }) {
20 | const highlighted = await highlight(codeblock, "github-dark")
21 | return (
22 |
23 |
24 | {highlighted.meta}
25 |
26 |
27 |
28 | )
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/fold.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Fold
3 | description: Fold annotation
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Fold inline content.
10 |
11 | Click on the ... to unfold the className
12 |
13 | ## !implementation
14 |
15 | ```tsx fold.tsx -c
16 | "use client"
17 | import { AnnotationHandler } from "codehike/code"
18 | import { useState } from "react"
19 |
20 | export const InlineFold: AnnotationHandler["Inline"] = ({ children }) => {
21 | const [folded, setFolded] = useState(true)
22 | if (!folded) {
23 | return children
24 | }
25 | return (
26 | setFolded(false)} aria-label="Expand">
27 | ...
28 |
29 | )
30 | }
31 | ```
32 |
33 | And then add the handler to your `Code` component:
34 |
35 | ```tsx code.tsx -c
36 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
37 | import { InlineFold } from "./fold"
38 |
39 | async function Code({ codeblock }: { codeblock: RawCode }) {
40 | const highlighted = await highlight(codeblock, "github-dark")
41 | return
42 | }
43 |
44 | const fold: AnnotationHandler = {
45 | name: "fold",
46 | Inline: InlineFold,
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Code Examples
3 | description: Code examples
4 | layout: CodeIndex
5 | ---
6 |
7 | These are examples of codeblock components you can build using Code Hike.
8 |
9 | You can find the explanation and implementation in each example's page. Feel free to copy, paste, modify and combine them to create the perfect codeblocks for your project.
10 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/line-numbers.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Line Numbers
3 | description: Line numbers
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Display line numbers.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | Using a [custom line component](/docs/concepts/annotations#customizing-line-and-token-components):
16 |
17 | ```tsx line-numbers.tsx -c
18 | import { AnnotationHandler, InnerLine } from "codehike/code"
19 |
20 | // !fold[/className="(.*?)"/gm]
21 | export const lineNumbers: AnnotationHandler = {
22 | name: "line-numbers",
23 | Line: (props) => {
24 | const width = props.totalLines.toString().length + 1
25 | return (
26 |
27 |
31 | {props.lineNumber}
32 |
33 |
34 |
35 | )
36 | },
37 | }
38 | ```
39 |
40 | Pass it to the `handlers` prop of the `Pre` component:
41 |
42 | ```tsx code.tsx -c
43 | import { lineNumbers } from "./line-numbers"
44 |
45 | async function Code({ codeblock }: { codeblock: RawCode }) {
46 | const highlighted = await highlight(codeblock, "github-dark")
47 | return
48 | }
49 | ```
50 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/link.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Link
3 | description: Add links to code snippets
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Add links to code snippets.
10 |
11 | **ipsum** is a link to example.com
12 |
13 | ## !implementation
14 |
15 | ```tsx code.tsx
16 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
17 |
18 | const link: AnnotationHandler = {
19 | name: "link",
20 | Inline: ({ annotation, children }) => {
21 | const { query } = annotation
22 | return {children}
23 | },
24 | }
25 |
26 | async function Code({ codeblock }: { codeblock: RawCode }) {
27 | const highlighted = await highlight(codeblock, "github-dark")
28 | return
29 | }
30 | ```
31 |
32 | ### Autolinking
33 |
34 | Instead of manually adding links to code snippets, you can use a regex to automatically link URLs.
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/tabs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tabs
3 | description: Tabs
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Show multiple code blocks in tabs.
10 |
11 |
12 |
13 | ## !implementation
14 |
15 | We use the [`Tabs` components](https://ui.shadcn.com/docs/components/tabs) from shadcn/ui:
16 |
17 | ```bash -c
18 | npx shadcn-ui@latest add tabs
19 | ```
20 |
21 | And then create a component to handle multiple codeblocks as tabs:
22 |
23 | ```tsx code.tsx -c
24 | import { Block, CodeBlock, parseProps } from "codehike/blocks"
25 | import { Pre, highlight } from "codehike/code"
26 | import { z } from "zod"
27 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
28 |
29 | // !fold[/className="(.*?)"/gm]
30 | const Schema = Block.extend({ tabs: z.array(CodeBlock) })
31 | async function CodeWithTabs(props: unknown) {
32 | const { tabs } = parseProps(props, Schema)
33 | return
34 | }
35 |
36 | export async function CodeTabs(props: { tabs: RawCode[] }) {
37 | const { tabs } = props
38 | const highlighted = await Promise.all(
39 | tabs.map((tab) => highlight(tab, "github-dark")),
40 | )
41 | return (
42 |
43 |
44 | {tabs.map((tab) => (
45 |
46 | {tab.meta}
47 |
48 | ))}
49 |
50 | {tabs.map((tab, i) => (
51 |
52 |
53 |
54 | ))}
55 |
56 | )
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/transpile.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Transpile
3 | description: Show code together with its transpiled version
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Sometimes you have a code block that you want to show together with its versions in different languages. It may be typescript and javascript, or sass and css, or maybe you want to transform a cURL command into calls to SDKs in different languages.
10 |
11 | If you are using React Server Components and you have a function to transpile the code, you can call that function inside the component.
12 |
13 | Here's an example showing how to transpile a typescript code block to javascript, and then showing both versions in tabs:
14 |
15 |
16 |
17 | ## !implementation
18 |
19 | ```tsx code.tsx -c
20 | import { RawCode } from "codehike/code"
21 | // !link[/tabs example/] tabs
22 | // CodeTabs is the component from the tabs example
23 | // !link[/CodeTabs/] tabs
24 | import { CodeTabs } from "@/components/tabs"
25 | import ts from "typescript"
26 |
27 | async function Code({ codeblock }: { codeblock: RawCode }) {
28 | // Since this is a RSC we can transpile stuff here
29 | // (there are probably more efficient ways to do this)
30 | const result = ts.transpileModule(codeblock.value, {
31 | compilerOptions: {
32 | module: ts.ModuleKind.CommonJS,
33 | target: ts.ScriptTarget.ESNext,
34 | },
35 | })
36 |
37 | const tsCode = {
38 | ...codeblock,
39 | meta: "typescript",
40 | }
41 | const jsCode = {
42 | ...codeblock,
43 | value: result.outputText,
44 | lang: "js",
45 | meta: "javascript",
46 | }
47 |
48 | return
49 | }
50 | ```
51 |
--------------------------------------------------------------------------------
/apps/web/content/docs/code/word-wrap.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Word Wrap
3 | description: Word wrap
4 | layout: PreviewAndImplementation
5 | ---
6 |
7 | ## !demo
8 |
9 | Wrap lines to avoid horizontal overflow.
10 |
11 | Drag the right handle to resize the width
12 |
13 | ## !implementation
14 |
15 | The easy way to make the code wrap is to style the ` ` component with `white-space: pre-wrap;`. But, when wrapping code, it's better to wrap the lines at the same indentation level. To do this, we can adjust the `text-indent` of each line:
16 |
17 | ```tsx word-wrap.tsx -c
18 | import {
19 | AnnotationHandler,
20 | InnerLine,
21 | InnerPre,
22 | InnerToken,
23 | } from "codehike/code"
24 |
25 | export const wordWrap: AnnotationHandler = {
26 | name: "word-wrap",
27 | Pre: (props) => ,
28 | Line: (props) => (
29 |
30 |
36 | {props.children}
37 |
38 |
39 | ),
40 | Token: (props) => ,
41 | }
42 | ```
43 |
44 | Pass it to the `handlers` prop of the `Pre` component:
45 |
46 | ```tsx code.tsx -c
47 | import { wordWrap } from "./word-wrap"
48 |
49 | async function Code({ codeblock }: { codeblock: RawCode }) {
50 | const highlighted = await highlight(codeblock, "github-dark")
51 | return
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/apps/web/content/docs/examples.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Full Examples and Templates
3 | description: Code Hike examples and templates
4 | ---
5 |
6 | import { Examples } from "./examples.tsx"
7 |
8 |
9 |
10 | Examples with minimal content showing how to use Code Hike with different frameworks and libraries.
11 |
12 | ## !! Next.js + Fumadocs
13 |
14 | 
15 | 
16 |
17 | ## !! Docusaurus
18 |
19 | 
20 | 
21 |
22 | ## !! Remotion
23 |
24 | 
25 | 
26 |
27 | ## !! Nextra
28 |
29 | 
30 | 
31 |
32 |
33 |
34 |
35 |
36 | Using Code Hike to rebuild some inspiring websites.
37 |
38 | ## !! Shopify API Reference
39 |
40 | 
41 | 
42 |
43 | ## !! Swift UI Tutorials
44 |
45 | 
46 | 
47 |
48 |
49 |
--------------------------------------------------------------------------------
/apps/web/content/docs/examples.tsx:
--------------------------------------------------------------------------------
1 | import { Block, ImageBlock, parseProps } from "codehike/blocks"
2 | import { z } from "zod"
3 |
4 | export function Examples(props: unknown) {
5 | const { blocks, title, children } = parseProps(
6 | props,
7 | Block.extend({
8 | blocks: z.array(
9 | Block.extend({
10 | repo: ImageBlock,
11 | img: ImageBlock,
12 | }),
13 | ),
14 | }),
15 | )
16 | return (
17 |
18 | {title}
19 | {children}
20 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/apps/web/content/docs/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Docs",
3 | "pages": [
4 | "index",
5 | "concepts/blocks",
6 | "concepts/code",
7 | "concepts/annotations",
8 | "concepts/utils",
9 | "---Examples---",
10 | "code",
11 | "layouts",
12 | "examples"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/apps/web/content/docs/test.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Components
3 | description: Components
4 | ---
5 |
6 | ## Code Block
7 |
8 | ```mdx
9 | import { Hike } from "codehike"
10 | import { MyLayout } from "./my-layout.tsx"
11 |
12 |
13 |
14 | # !main Hello World
15 |
16 | Something
17 |
18 |
19 | ```
20 |
21 | ## Cards
22 |
23 |
24 |
25 |
29 |
30 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/basic/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | // !border(1:2)
3 | const lorem = ipsum == null ? 0 : 1
4 | dolor = lorem - sit(dolor)
5 | // !bg[5:16]
6 | let amet = lorem ? consectetur(ipsum) : 3
7 | ```
8 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/basic/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight } from "codehike/code"
3 | import { AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 | return (
12 |
17 | )
18 | }
19 | const borderHandler: AnnotationHandler = {
20 | name: "border",
21 | Block: ({ annotation, children }) => (
22 | {children}
23 | ),
24 | }
25 |
26 | const bgHandler: AnnotationHandler = {
27 | name: "bg",
28 | Inline: ({ annotation, children }) => (
29 | {children}
30 | ),
31 | }
32 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/groups/content.md:
--------------------------------------------------------------------------------
1 | ```jsx
2 | // !border[/className="(.*?)"/gm] pink
3 | function Foo() {
4 | return (
5 |
6 | hey
7 |
8 | )
9 | }
10 | ```
11 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/groups/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight } from "codehike/code"
3 | import { AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 | return (
12 |
17 | )
18 | }
19 | const borderHandler: AnnotationHandler = {
20 | name: "border",
21 | Inline: ({ annotation, children }) => {
22 | const borderColor = annotation.query || "yellow"
23 | return {children}
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/mark/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | const lorem = ipsum == null ? 0 : 1
3 | // !mark(1:2)
4 | dolor = lorem - sit(dolor)
5 | let amet = lorem ? consectetur(ipsum) : 3
6 | ```
7 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/mark/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight, InnerLine } from "codehike/code"
3 | import { AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 | return (
12 |
17 | )
18 | }
19 | const mark: AnnotationHandler = {
20 | name: "mark",
21 | AnnotatedLine: ({ annotation, ...props }) => (
22 |
23 | ),
24 | Line: (props) => (
25 |
29 | ),
30 | }
31 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/query/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | // !border(1:2) purple
3 | const lorem = ipsum == null ? 0 : 1
4 | dolor = lorem - sit(dolor)
5 | // !bg[5:16] darkblue
6 | let amet = lorem ? consectetur(ipsum) : 3
7 | ```
8 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/query/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight } from "codehike/code"
3 | import { AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 | return (
12 |
17 | )
18 | }
19 | const borderHandler: AnnotationHandler = {
20 | name: "border",
21 | Block: ({ annotation, children }) => {
22 | const borderColor = annotation.query || "red"
23 | return {children}
24 | },
25 | }
26 |
27 | const bgHandler: AnnotationHandler = {
28 | name: "bg",
29 | Inline: ({ annotation, children }) => {
30 | const background = annotation.query || "#2d26"
31 | return {children}
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/regex/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | // !border[/ipsum/] yellow
3 | const lorem = ipsum == null ? ipsum : 1
4 | // !border[/dolor/g] lime
5 | dolor = lorem - sit(dolor)
6 | let amet = dolor ? consectetur(ipsum) : 3
7 | ```
8 |
9 | ```js
10 | // !border[/ipsum/gm] orange
11 | const lorem = ipsum == null ? ipsum : 1
12 | dolor = lorem - sit(dolor)
13 | let amet = dolor ? consectetur(ipsum) : 3
14 | ```
15 |
--------------------------------------------------------------------------------
/apps/web/demos/annotations/regex/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight } from "codehike/code"
3 | import { AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 | return (
12 |
17 | )
18 | }
19 | const borderHandler: AnnotationHandler = {
20 | name: "border",
21 | Inline: ({ annotation, children }) => {
22 | const borderColor = annotation.query || "yellow"
23 | return {children}
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/apps/web/demos/api-reference/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.md"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 | {
13 | "files": ["*.tsx"],
14 | "options": {
15 | "printWidth": 60,
16 | "semi": false
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/demos/autolink/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | // !link[/"(http.*?)"/gm]
3 | function lorem(ipsum, dolor = 1) {
4 | const links = [
5 | "https://codehike.org",
6 | "https://twitter.com/codehike_",
7 | "https://github.com/code-hike",
8 | ]
9 | return links
10 | }
11 | ```
12 |
--------------------------------------------------------------------------------
/apps/web/demos/autolink/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
2 | import Content from "./content.md"
3 |
4 | export default function Page() {
5 | return
6 | }
7 |
8 | async function Code({ codeblock }: { codeblock: RawCode }) {
9 | const highlighted = await highlight(codeblock, "github-dark")
10 | return (
11 |
12 | )
13 | }
14 |
15 | const link: AnnotationHandler = {
16 | name: "link",
17 | Inline: ({ annotation, children }) => {
18 | const { query } = annotation
19 | return {children}
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/apps/web/demos/callout/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | const lorem = ipsum(dolor, sit)
3 | // !callout[/amet/] This is a callout
4 | const [amet, consectetur] = [0, 0]
5 | lorem.adipiscing((sed, elit) => {
6 | if (sed) {
7 | amet += elit
8 | }
9 | })
10 | ```
11 |
--------------------------------------------------------------------------------
/apps/web/demos/callout/page.tsx:
--------------------------------------------------------------------------------
1 | import Content from "./content.md"
2 | import { RawCode, Pre, highlight } from "codehike/code"
3 | import { InlineAnnotation, AnnotationHandler } from "codehike/code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | export async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 |
12 | return (
13 |
18 | )
19 | }
20 |
21 | const callout: AnnotationHandler = {
22 | name: "callout",
23 | transform: (annotation: InlineAnnotation) => {
24 | const { name, query, lineNumber, fromColumn, toColumn, data } = annotation
25 | return {
26 | name,
27 | query,
28 | fromLineNumber: lineNumber,
29 | toLineNumber: lineNumber,
30 | data: { ...data, column: (fromColumn + toColumn) / 2 },
31 | }
32 | },
33 | Block: ({ annotation, children }) => {
34 | const { column } = annotation.data
35 | return (
36 | <>
37 | {children}
38 |
42 |
46 | {annotation.query}
47 |
48 | >
49 | )
50 | },
51 | }
52 |
--------------------------------------------------------------------------------
/apps/web/demos/classname/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | // !className line-through
4 | const sit = ipsum == null ? 0 : ipsum.sit
5 | dolor = sit - amet(dolor)
6 | // !className[/sit/] bg-red-700 rounded-lg px-1
7 | return sit ? consectetur(ipsum) : []
8 | }
9 | ```
10 |
--------------------------------------------------------------------------------
/apps/web/demos/classname/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
2 | import Content from "./content.md"
3 |
4 | export default function Page() {
5 | return
6 | }
7 |
8 | async function Code({ codeblock }: { codeblock: RawCode }) {
9 | const highlighted = await highlight(codeblock, "github-dark")
10 | return (
11 |
16 | )
17 | }
18 |
19 | const className: AnnotationHandler = {
20 | name: "className",
21 | Block: ({ annotation, children }) => (
22 | {children}
23 | ),
24 | Inline: ({ annotation, children }) => (
25 | {children}
26 | ),
27 | }
28 |
--------------------------------------------------------------------------------
/apps/web/demos/collapse/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | // !collapse(1:4)
3 | function lorem(ipsum, dolor = 1) {
4 | const sit = ipsum == null ? 0 : 1
5 | dolor = sit - amet(dolor)
6 | return sit ? consectetur(ipsum) : []
7 | }
8 |
9 | // !collapse(1:4) collapsed
10 | function ipsum(ipsum, dolor = 1) {
11 | const sit = ipsum == null ? 0 : 1
12 | dolor = sit - amet(dolor)
13 | return sit ? consectetur(ipsum) : []
14 | }
15 | ```
16 |
--------------------------------------------------------------------------------
/apps/web/demos/copy-button/button.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { Copy, Check } from "lucide-react"
4 | import { useState } from "react"
5 |
6 | export function CopyButton({ text }: { text: string }) {
7 | const [copied, setCopied] = useState(false)
8 |
9 | return (
10 | {
14 | navigator.clipboard.writeText(text)
15 | setCopied(true)
16 | setTimeout(() => setCopied(false), 1200)
17 | }}
18 | >
19 | {copied ? (
20 |
21 | ) : (
22 |
23 | )}
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/apps/web/demos/copy-button/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : ipsum.sit
4 | dolor = sit - amet(dolor)
5 | return sit ? consectetur(ipsum) : []
6 | }
7 | ```
8 |
--------------------------------------------------------------------------------
/apps/web/demos/copy-button/page.tsx:
--------------------------------------------------------------------------------
1 | import { Pre, RawCode, highlight } from "codehike/code"
2 | import Content from "./content.md"
3 | import { CopyButton } from "./button"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const highlighted = await highlight(codeblock, "github-dark")
11 |
12 | return (
13 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/demos/diff/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : ipsum.sit
4 | // !diff -
5 | dolor = ipsum - sit
6 | // !diff +
7 | dolor = sit - amet(dolor)
8 | return sit ? consectetur(ipsum) : []
9 | }
10 | ```
11 |
--------------------------------------------------------------------------------
/apps/web/demos/filename/content.md:
--------------------------------------------------------------------------------
1 | ```js index.js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : 1
4 | dolor = sit - amet(dolor)
5 | return sit ? consectetur(ipsum) : []
6 | }
7 | ```
8 |
--------------------------------------------------------------------------------
/apps/web/demos/filename/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight } from "codehike/code"
2 | import Content from "./content.md"
3 |
4 | export default function Page() {
5 | return
6 | }
7 |
8 | async function Code({ codeblock }: { codeblock: RawCode }) {
9 | const highlighted = await highlight(codeblock, "github-dark")
10 | return (
11 |
12 |
13 | {highlighted.meta}
14 |
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/demos/focus/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : ipsum.sit
4 | dolor = sit - amet(dolor)
5 | return sit ? consectetur(ipsum) : []
6 | }
7 |
8 | function ipsum(ipsum, dolor = 1) {
9 | return dolor
10 | }
11 |
12 | // !focus(1:5)
13 | function dolor(ipsum, dolor = 1) {
14 | const sit = ipsum == null ? 0 : ipsum.sit
15 | dolor = sit - amet(dolor)
16 | return sit ? consectetur(ipsum) : []
17 | }
18 | ```
19 |
--------------------------------------------------------------------------------
/apps/web/demos/focus/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, highlight } from "codehike/code"
2 | import Content from "./content.md"
3 | import { CodeContainer } from "./code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const info = await highlight(codeblock, "github-dark")
11 | return
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web/demos/fold/annotations.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { AnnotationHandler } from "codehike/code"
4 | import { useState } from "react"
5 |
6 | export const InlineFold: AnnotationHandler["Inline"] = ({ children }) => {
7 | const [folded, setFolded] = useState(true)
8 | if (!folded) {
9 | return children
10 | }
11 | return (
12 | setFolded(false)} aria-label="Expand">
13 | ...
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/demos/fold/content.md:
--------------------------------------------------------------------------------
1 | ```jsx
2 | // !fold[/className="(.*?)"/gm]
3 | function Foo() {
4 | return (
5 |
6 | hey
7 |
8 | )
9 | }
10 | ```
11 |
--------------------------------------------------------------------------------
/apps/web/demos/fold/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
2 | import Content from "./content.md"
3 | import { InlineFold } from "./annotations"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const info = await highlight(codeblock, "github-dark")
11 |
12 | return
13 | }
14 |
15 | export const fold: AnnotationHandler = {
16 | name: "fold",
17 | Inline: InlineFold,
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web/demos/footnotes/content.md:
--------------------------------------------------------------------------------
1 | ```rb
2 | # !ref Library import
3 | require 'sinatra'
4 |
5 | # !ref URL mapping
6 | get '/hi' do
7 | "Hello World!"
8 | end
9 | ```
10 |
--------------------------------------------------------------------------------
/apps/web/demos/footnotes/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | RawCode,
3 | Pre,
4 | highlight,
5 | AnnotationHandler,
6 | InnerLine,
7 | } from "codehike/code"
8 | import Content from "./content.md"
9 |
10 | export default function Page() {
11 | return
12 | }
13 |
14 | async function Code({ codeblock }: { codeblock: RawCode }) {
15 | const highlighted = await highlight(codeblock, "github-dark")
16 |
17 | const noteAnnotations = highlighted.annotations.filter(
18 | ({ name }) => name == "ref",
19 | )
20 | const notes = noteAnnotations.map(({ query }) => query)
21 |
22 | noteAnnotations.forEach((a, index) => {
23 | a.data = { n: index + 1 }
24 | })
25 | return (
26 |
27 |
32 |
33 | {notes.map((ref, index) => (
34 |
35 |
36 | {ref}
37 |
38 | ))}
39 |
40 |
41 | )
42 | }
43 |
44 | export const footnotes: AnnotationHandler = {
45 | name: "ref",
46 | AnnotatedLine: ({ annotation, ...props }) => {
47 | return (
48 |
49 |
50 |
51 |
52 | )
53 | },
54 | }
55 |
56 | function Number({ n }: { n: number }) {
57 | return (
58 |
62 | )
63 | }
64 |
--------------------------------------------------------------------------------
/apps/web/demos/hover/content.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 | The [base case](hover:one) returns 1.
4 |
5 | ```cpp
6 | int factorial(int n) {
7 | if (n == 0) {
8 | // !hover one
9 | return 1;
10 | } else {
11 | // !hover two
12 | return n * factorial(n - 1);
13 | }
14 | }
15 | ```
16 |
17 | The [recursive case](hover:two) multiplies something.
18 |
19 |
20 |
--------------------------------------------------------------------------------
/apps/web/demos/hover/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AnnotationHandler,
3 | InnerLine,
4 | Pre,
5 | RawCode,
6 | highlight,
7 | } from "codehike/code"
8 | import Content from "./content.mdx"
9 | import "./styles.css"
10 |
11 | import React from "react"
12 |
13 | export default function Page() {
14 | return
15 | }
16 |
17 | function HoverContainer(props: { children: React.ReactNode }) {
18 | return (
19 |
20 | {props.children}
21 |
22 | )
23 | }
24 |
25 | function Link(props: { href?: string; children?: React.ReactNode }) {
26 | if (props.href?.startsWith("hover:")) {
27 | const hover = props.href.slice("hover:".length)
28 | return (
29 |
33 | {props.children}
34 |
35 | )
36 | } else {
37 | return
38 | }
39 | }
40 |
41 | async function Code({ codeblock }: { codeblock: RawCode }) {
42 | const highlighted = await highlight(codeblock, "github-dark")
43 | return
44 | }
45 |
46 | const hover: AnnotationHandler = {
47 | name: "hover",
48 | onlyIfAnnotated: true,
49 | Line: ({ annotation, ...props }) => (
50 |
55 | ),
56 | }
57 |
--------------------------------------------------------------------------------
/apps/web/demos/hover/styles.css:
--------------------------------------------------------------------------------
1 | .hover-container:has([data-hover="one"]:hover)
2 | [data-line]:not([data-line="one"]),
3 | .hover-container:has([data-hover="two"]:hover)
4 | [data-line]:not([data-line="two"]) {
5 | opacity: 0.4;
6 | }
7 |
--------------------------------------------------------------------------------
/apps/web/demos/language-switcher/multi-code.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { HighlightedCode, Pre, RawCode, highlight } from "codehike/code"
4 |
5 | import { useState } from "react"
6 | import {
7 | Select,
8 | SelectContent,
9 | SelectItem,
10 | SelectTrigger,
11 | SelectValue,
12 | } from "@/components/ui/select"
13 | import { tokenTransitions } from "@/components/annotations/token-transitions"
14 |
15 | export function Code({ highlighted }: { highlighted: HighlightedCode[] }) {
16 | const [selectedLang, setSelectedLang] = useState(highlighted[0].lang)
17 | const selectedCode = highlighted.find((code) => code.lang === selectedLang)!
18 |
19 | return (
20 |
21 |
26 |
27 |
32 |
33 |
34 |
35 |
36 | {highlighted.map(({ lang }, index) => (
37 |
38 | {lang}
39 |
40 | ))}
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/apps/web/demos/language-switcher/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, highlight } from "codehike/code"
2 | import Content from "./content.mdx"
3 |
4 | import { Code } from "./multi-code"
5 |
6 | export default function Page() {
7 | return
8 | }
9 |
10 | async function CodeSwitcher(props: { code: RawCode[] }) {
11 | const highlighted = await Promise.all(
12 | props.code.map((codeblock) => highlight(codeblock, "github-dark")),
13 | )
14 | return
15 | }
16 |
--------------------------------------------------------------------------------
/apps/web/demos/line-numbers/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : 1
4 | dolor = sit - amet(dolor)
5 | return sit ? consectetur(ipsum) : []
6 | }
7 | ```
8 |
--------------------------------------------------------------------------------
/apps/web/demos/line-numbers/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | RawCode,
3 | Pre,
4 | highlight,
5 | AnnotationHandler,
6 | InnerLine,
7 | } from "codehike/code"
8 | import Content from "./content.md"
9 |
10 | export default function Page() {
11 | return
12 | }
13 |
14 | async function Code({ codeblock }: { codeblock: RawCode }) {
15 | const info = await highlight(codeblock, "github-dark")
16 |
17 | return (
18 |
23 | )
24 | }
25 |
26 | export const lineNumbers: AnnotationHandler = {
27 | name: "line-numbers",
28 | Line: (props) => {
29 | const width = props.totalLines.toString().length + 1
30 | return (
31 |
32 |
36 | {props.lineNumber}
37 |
38 |
39 |
40 | )
41 | },
42 | }
43 |
--------------------------------------------------------------------------------
/apps/web/demos/link/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | const sit = ipsum == null ? 0 : ipsum.sit
4 | dolor = sit - amet(dolor)
5 | // !link[/ipsum/] https://example.com
6 | return sit ? consectetur(ipsum) : []
7 | }
8 | ```
9 |
--------------------------------------------------------------------------------
/apps/web/demos/link/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight, AnnotationHandler } from "codehike/code"
2 | import Content from "./content.md"
3 |
4 | export default function Page() {
5 | return
6 | }
7 |
8 | async function Code({ codeblock }: { codeblock: RawCode }) {
9 | const highlighted = await highlight(codeblock, "github-dark")
10 | return (
11 |
12 | )
13 | }
14 |
15 | const link: AnnotationHandler = {
16 | name: "link",
17 | Inline: ({ annotation, children }) => {
18 | const { query } = annotation
19 | return {children}
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/apps/web/demos/mark/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor = 1) {
3 | // !mark
4 | return dolor
5 | }
6 |
7 | function ipsum(lorem, dolor = 1) {
8 | // !mark(1:2) gold
9 | const sit = lorem == null ? 0 : lorem.sit
10 | dolor = sit - amet(dolor)
11 | // !mark[/sit/] pink
12 | return sit ? consectetur(lorem) : []
13 | }
14 | ```
15 |
--------------------------------------------------------------------------------
/apps/web/demos/mark/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | RawCode,
3 | Pre,
4 | highlight,
5 | AnnotationHandler,
6 | InnerLine,
7 | } from "codehike/code"
8 | import Content from "./content.md"
9 |
10 | export default function Page() {
11 | return
12 | }
13 |
14 | const mark: AnnotationHandler = {
15 | name: "mark",
16 | Line: ({ annotation, ...props }) => {
17 | const color = annotation?.query || "rgb(14 165 233)"
18 | return (
19 |
27 |
28 |
29 | )
30 | },
31 | Inline: ({ annotation, children }) => {
32 | const color = annotation?.query || "rgb(14 165 233)"
33 | return (
34 |
41 | {children}
42 |
43 | )
44 | },
45 | }
46 |
47 | async function Code({ codeblock }: { codeblock: RawCode }) {
48 | const highlighted = await highlight(codeblock, "github-dark")
49 | return (
50 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/apps/web/demos/occurrences/code.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { HighlightedCode, Pre } from "codehike/code"
4 | import React from "react"
5 |
6 | export function CodeWithOccurrences({ code }: { code: HighlightedCode }) {
7 | const ref = React.useRef(null)
8 | React.useEffect(() => {
9 | const handler: EventListener = (e) => {
10 | const selected = document.getSelection()!.toString().trim()
11 | ref.current!.querySelectorAll("span:not(:has(*))").forEach((element) => {
12 | if (element.textContent === selected) {
13 | element.setAttribute("data-selected", "true")
14 | } else {
15 | element.removeAttribute("data-selected")
16 | }
17 | })
18 | }
19 | document.addEventListener("selectionchange", handler)
20 | return () => {
21 | document.removeEventListener("selectionchange", handler)
22 | }
23 | }, [])
24 |
25 | return (
26 |
31 | )
32 | }
33 |
34 | // const Token: TokenComponent = ({ value, lineNumber, ...props }) => {
35 | // return (
36 | //
40 | // {value}
41 | //
42 | // )
43 | // }
44 |
--------------------------------------------------------------------------------
/apps/web/demos/occurrences/content.md:
--------------------------------------------------------------------------------
1 | ## Lorem Ipsum
2 |
3 | One thing I miss while reading code on websites is the ability to highlight all occurrences of a particular piece of code.
4 |
5 | This works similar to a code editor, you can select a token and all occurrences of that token will be highlighted.
6 |
7 | ```js
8 | function foo(ipsum, dolor = 1) {
9 | const sit = ipsum == null ? 0 : ipsum.sit
10 | "use client"
11 |
12 | import { useState } from "react"
13 | import { Carousel } from "acme-carousel"
14 |
15 | export default function Gallery() {
16 | let [isOpen, setIsOpen] = useState(false)
17 |
18 | return (
19 |
20 | setIsOpen(true)}>Show
21 | {isOpen && }
22 |
23 | )
24 | }
25 | ```
26 |
27 | It also works if you selecte a word from a paragraph, try: button or `Carousel`.
28 |
--------------------------------------------------------------------------------
/apps/web/demos/occurrences/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode, Pre, highlight } from "codehike/code"
2 | import Content from "./content.md"
3 | import { CodeWithOccurrences } from "./code"
4 |
5 | export default function Page() {
6 | return
7 | }
8 |
9 | async function Code({ codeblock }: { codeblock: RawCode }) {
10 | const info = await highlight(codeblock, "github-dark")
11 | return
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web/demos/scrollycoding/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.md"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 | {
13 | "files": ["*.tsx"],
14 | "options": {
15 | "printWidth": 60,
16 | "semi": false
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/demos/slideshow/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.md"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 | {
13 | "files": ["*.tsx"],
14 | "options": {
15 | "printWidth": 60,
16 | "semi": false
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/demos/slideshow/content.md:
--------------------------------------------------------------------------------
1 | ## !!steps
2 |
3 | This is the first step. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
4 |
5 | ```js !
6 | function lorem(ipsum) {
7 | const sit = ipsum == null ? 0 : 1
8 | dolor = sit - amet(dolor)
9 | return consectetur(ipsum)
10 | }
11 | ```
12 |
13 | ## !!steps
14 |
15 | The second step, lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
16 |
17 | ```js !
18 | function lorem(ipsum, dolor = 1) {
19 | const sit = ipsum == null ? 0 : 1
20 | dolor = sit - amet(dolor)
21 | return sit ? consectetur(ipsum) : []
22 | }
23 | ```
24 |
25 | ## !!steps
26 |
27 | And the third step, lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
28 |
29 | ```js !
30 | function lorem(ipsum, dolor = 1) {
31 | const sit = ipsum == null ? 0 : 1
32 | dolor = sit - amet(dolor)
33 | if (dolor) {
34 | dolor += 100
35 | }
36 | return sit ? consectetur(ipsum) : []
37 | }
38 | ```
39 |
--------------------------------------------------------------------------------
/apps/web/demos/slideshow/controls.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useSelectedIndex } from "codehike/utils/selection"
3 |
4 | export function Controls({ length }: { length: number }) {
5 | const [selectedIndex, setSelectedIndex] =
6 | useSelectedIndex()
7 |
8 | return (
9 |
10 |
13 | setSelectedIndex(Math.max(0, selectedIndex - 1))
14 | }
15 | >
16 | Prev
17 |
18 | {[...Array(length)].map((_, i) => (
19 | setSelectedIndex(i)}
25 | />
26 | ))}
27 |
30 | setSelectedIndex(
31 | Math.min(length - 1, selectedIndex + 1),
32 | )
33 | }
34 | >
35 | Next
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/apps/web/demos/slideshow/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Block,
3 | CodeBlock,
4 | parseRoot,
5 | } from "codehike/blocks"
6 | import Content from "./content.md"
7 | import { z } from "zod"
8 | import {
9 | Selection,
10 | SelectionProvider,
11 | } from "codehike/utils/selection"
12 | import { Pre, RawCode, highlight } from "codehike/code"
13 | import { Controls } from "./controls"
14 | import { tokenTransitions } from "@/components/annotations/token-transitions"
15 |
16 | const Schema = Block.extend({
17 | steps: z.array(Block.extend({ code: CodeBlock })),
18 | })
19 |
20 | export default function Page() {
21 | const { steps } = parseRoot(Content, Schema)
22 | return (
23 |
24 | (
26 |
27 | ))}
28 | />
29 |
30 |
31 | step.children)}
33 | />
34 |
35 |
36 | )
37 | }
38 |
39 | async function Code({ codeblock }: { codeblock: RawCode }) {
40 | const highlighted = await highlight(
41 | codeblock,
42 | "github-dark",
43 | )
44 | return (
45 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/apps/web/demos/spotlight/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "semi": false,
4 | "overrides": [
5 | {
6 | "files": ["*.md"],
7 | "options": {
8 | "printWidth": 42,
9 | "semi": false
10 | }
11 | },
12 | {
13 | "files": ["*.tsx"],
14 | "options": {
15 | "printWidth": 60,
16 | "semi": false
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/demos/tabs/content.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 | ```js !!tabs main.js
4 | function lorem(ipsum, dolor = 1) {
5 | const sit = ipsum == null ? 0 : ipsum.sit
6 | dolor = sit - amet(dolor)
7 | return sit ? consectetur(ipsum) : []
8 | }
9 | ```
10 |
11 | ```css !!tabs styles.css
12 | body {
13 | margin: 0;
14 | padding: 0;
15 | }
16 | ```
17 |
18 |
19 |
--------------------------------------------------------------------------------
/apps/web/demos/tabs/page.tsx:
--------------------------------------------------------------------------------
1 | import { Pre, RawCode, highlight } from "codehike/code"
2 | import Content from "./content.mdx"
3 | import { Block, CodeBlock, parseProps } from "codehike/blocks"
4 | import { z } from "zod"
5 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
6 |
7 | export default async function Page() {
8 | return
9 | }
10 |
11 | const Schema = Block.extend({ tabs: z.array(CodeBlock) })
12 |
13 | async function CodeWithTabs(props: unknown) {
14 | const { tabs } = parseProps(props, Schema)
15 | return
16 | }
17 |
18 | export async function CodeTabs(props: { tabs: RawCode[] }) {
19 | const { tabs } = props
20 | const highlighted = await Promise.all(
21 | tabs.map((tab) => highlight(tab, "github-dark")),
22 | )
23 | return (
24 |
25 |
26 | {tabs.map((tab) => (
27 |
28 | {tab.meta}
29 |
30 | ))}
31 |
32 | {tabs.map((tab, i) => (
33 |
34 |
35 |
36 | ))}
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/apps/web/demos/token-transitions/code.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import React from "react"
4 | import { HighlightedCode } from "codehike/code"
5 | import { Pre } from "codehike/code"
6 | import { tokenTransitions } from "@/components/annotations/token-transitions"
7 |
8 | export function CodeSwitcher({ infos }: { infos: HighlightedCode[] }) {
9 | const [index, setIndex] = React.useState(0)
10 | const next = () => setIndex((index + 1) % infos.length)
11 |
12 | return (
13 | <>
14 |
15 |
16 |
17 | Switch code
18 |
19 |
20 | >
21 | )
22 | }
23 |
24 | export function CodeClient(props: { highlighted: HighlightedCode }) {
25 | const { highlighted } = props
26 | return (
27 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/apps/web/demos/token-transitions/content.md:
--------------------------------------------------------------------------------
1 | ```scala
2 | object Main {
3 | def factorial(n: Int): Int = {
4 | if (n == 0) {
5 | return 1
6 | } else {
7 | return n * factorial(n - 1)
8 | }
9 | }
10 | }
11 | ```
12 |
13 | ```python
14 | def factorial(n):
15 | if n == 0:
16 | return 1
17 | else:
18 | return n * factorial(n - 1)
19 | ```
20 |
--------------------------------------------------------------------------------
/apps/web/demos/token-transitions/content.mdx:
--------------------------------------------------------------------------------
1 | ```scala !!
2 | object Main {
3 | def factorial(n: Int): Int = {
4 | if (n == 0) {
5 | return 1
6 | } else {
7 | return n * factorial(n - 1)
8 | }
9 | }
10 | }
11 | ```
12 |
13 | ```python !!
14 | def factorial(n):
15 | if n == 0:
16 | return 1
17 | else:
18 | return n * factorial(n - 1)
19 | ```
20 |
--------------------------------------------------------------------------------
/apps/web/demos/token-transitions/page.tsx:
--------------------------------------------------------------------------------
1 | import { highlight } from "codehike/code"
2 | import Content from "./content.mdx"
3 | import { CodeSwitcher } from "./code"
4 | import { Block, CodeBlock, parseRoot } from "codehike/blocks"
5 | import { z } from "zod"
6 |
7 | export default async function Page() {
8 | const { code } = parseRoot(
9 | Content,
10 | Block.extend({ code: z.array(CodeBlock) }),
11 | )
12 |
13 | const infos = await Promise.all(
14 | code.map((codeblock: any) => highlight(codeblock, "github-dark")),
15 | )
16 | return
17 | }
18 |
--------------------------------------------------------------------------------
/apps/web/demos/tooltip/content.mdx:
--------------------------------------------------------------------------------
1 |
2 |
3 | ```js !code
4 | // !tooltip[/lorem/] description
5 | function lorem(ipsum, dolor = 1) {
6 | const sit = ipsum == null ? 0 : ipsum.sit
7 | dolor = sit - amet(dolor)
8 | // !tooltip[/consectetur/] inspect
9 | return sit ? consectetur(ipsum) : []
10 | }
11 | ```
12 |
13 | ## !!tooltips description
14 |
15 | ### Hello world
16 |
17 | Lorem ipsum **dolor** sit amet `consectetur`.
18 |
19 | Adipiscing elit _sed_ do eiusmod.
20 |
21 | ## !!tooltips inspect
22 |
23 | ```js
24 | function consectetur(ipsum) {
25 | const { a, b } = ipsum
26 | return a + b
27 | }
28 | ```
29 |
30 |
31 |
--------------------------------------------------------------------------------
/apps/web/demos/transpile/content.md:
--------------------------------------------------------------------------------
1 | ```ts
2 | interface Greeter {
3 | greet(): string
4 | }
5 |
6 | function sayHello(greeter: Greeter) {
7 | console.log(greeter.greet())
8 | }
9 | ```
10 |
--------------------------------------------------------------------------------
/apps/web/demos/transpile/page.tsx:
--------------------------------------------------------------------------------
1 | import { RawCode } from "codehike/code"
2 |
3 | import Content from "./content.md"
4 | import ts from "typescript"
5 | import { CodeTabs } from "../tabs/page"
6 |
7 | export default function Page() {
8 | return
9 | }
10 |
11 | async function Code({ codeblock }: { codeblock: RawCode }) {
12 | // Since this is a RSC we can transpile stuff here
13 | // (there are probably more efficient ways to do this)
14 | const result = ts.transpileModule(codeblock.value, {
15 | compilerOptions: {
16 | module: ts.ModuleKind.CommonJS,
17 | target: ts.ScriptTarget.ESNext,
18 | },
19 | })
20 |
21 | const tsCode = { ...codeblock, meta: "typescript" }
22 | const jsCode = {
23 | ...codeblock,
24 | value: result.outputText,
25 | lang: "js",
26 | meta: "javascript",
27 | }
28 |
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/apps/web/demos/twoslash/content.md:
--------------------------------------------------------------------------------
1 | ```ts
2 | const hi = "Hello"
3 | const msg = `${hi}, world`
4 | // ^?
5 |
6 | // @errors: 2588
7 | msg = 123
8 | ```
9 |
--------------------------------------------------------------------------------
/apps/web/demos/word-wrap/content.md:
--------------------------------------------------------------------------------
1 | ```js
2 | function lorem(ipsum, dolor, sit) {
3 | ipsum.amet(
4 | { consectetur: [0, 1] },
5 | {
6 | adipiscing: elit.sed,
7 | eiusmod: "lorem ipsum dolor sit amet",
8 | sit,
9 | },
10 | )
11 | }
12 | ```
13 |
--------------------------------------------------------------------------------
/apps/web/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/apps/web/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import type { MDXComponents } from "mdx/types"
2 | import defaultComponents from "next-docs-ui/mdx/default"
3 | import { Code, InlineCode } from "./components/code"
4 |
5 | export function useMDXComponents(components: MDXComponents): MDXComponents {
6 | return {
7 | ...defaultComponents,
8 | ...components,
9 | Code,
10 | InlineCode,
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web/next.config.mjs:
--------------------------------------------------------------------------------
1 | import createNextDocsMDX from "next-docs-mdx/config"
2 | import { remarkCodeHike, recmaCodeHike } from "codehike/mdx"
3 |
4 | /** @type {import('codehike/mdx').CodeHikeConfig} */
5 | const chConfig = {
6 | components: {
7 | code: "Code",
8 | inlineCode: "InlineCode",
9 | },
10 | // ignoreCode: (codeblock) => codeblock.lang === "mermaid",
11 | // syntaxHighlighting: {
12 | // theme: "github-dark",
13 | // },
14 | }
15 |
16 | const withMDX = createNextDocsMDX({
17 | mdxOptions: {
18 | remarkPlugins: [[remarkCodeHike, chConfig]],
19 | recmaPlugins: [[recmaCodeHike, chConfig]],
20 | jsx: true,
21 | },
22 | })
23 |
24 | /** @type {import('next').NextConfig} */
25 | const config = {
26 | reactStrictMode: true,
27 | webpack: (config) => {
28 | // fix https://github.com/microsoft/TypeScript-Website/pull/3022
29 | config.module.exprContextCritical = false
30 | return config
31 | },
32 | images: {
33 | remotePatterns: [
34 | {
35 | protocol: "https",
36 | hostname: "github.com",
37 | port: "",
38 | pathname: "/**",
39 | },
40 | ],
41 | },
42 | }
43 |
44 | export default withMDX(config)
45 |
--------------------------------------------------------------------------------
/apps/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "build": "next build",
7 | "dev": "next dev",
8 | "start": "next start",
9 | "update-sponsors": "node app/landing/sponsors/update.js"
10 | },
11 | "dependencies": {
12 | "@code-hike/lighter": "0.9.4",
13 | "@radix-ui/react-collapsible": "^1.0.3",
14 | "@radix-ui/react-dropdown-menu": "^2.1.1",
15 | "@radix-ui/react-select": "^2.1.1",
16 | "@radix-ui/react-tabs": "^1.0.4",
17 | "@radix-ui/react-toggle": "^1.0.3",
18 | "@radix-ui/react-toggle-group": "^1.0.4",
19 | "@radix-ui/react-tooltip": "^1.0.7",
20 | "@types/mdx": "^2.0.10",
21 | "@vercel/analytics": "^1.2.2",
22 | "class-variance-authority": "^0.7.0",
23 | "clsx": "^2.1.0",
24 | "codehike": "workspace:*",
25 | "diff": "^5.1.0",
26 | "dotenv": "^16.4.1",
27 | "lucide-react": "^0.303.0",
28 | "next": "14.1.0",
29 | "next-docs-mdx": "7.1.2",
30 | "next-docs-ui": "7.1.2",
31 | "next-docs-zeta": "7.1.2",
32 | "react": "18.2.0",
33 | "react-dom": "18.2.0",
34 | "react-resizable-panels": "^2.0.16",
35 | "rss": "^1.2.2",
36 | "seti-icons": "^0.0.4",
37 | "sharp": "0.33.1",
38 | "tailwind-merge": "^2.2.0",
39 | "tailwindcss-animate": "^1.0.7",
40 | "twoslash": "^0.2.4",
41 | "zod": "^3.22.4"
42 | },
43 | "devDependencies": {
44 | "@types/diff": "^5.0.9",
45 | "@types/node": "^20.10.6",
46 | "@types/react": "18.2.48",
47 | "@types/react-dom": "18.2.18",
48 | "@types/rss": "^0.0.32",
49 | "autoprefixer": "10.4.16",
50 | "postcss": "8.4.32",
51 | "tailwindcss": "3.4.3",
52 | "typescript": "^5"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/apps/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/web/public/blog/fine-grained-markdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/fine-grained-markdown.png
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion.png
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion/00.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion/00.mp4
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion/02.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion/02.mp4
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion/04.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion/04.mp4
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion/06.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion/06.mp4
--------------------------------------------------------------------------------
/apps/web/public/blog/remotion/examples.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/remotion/examples.mp4
--------------------------------------------------------------------------------
/apps/web/public/blog/rich-content-websites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/rich-content-websites.png
--------------------------------------------------------------------------------
/apps/web/public/blog/v1-migration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/v1-migration.png
--------------------------------------------------------------------------------
/apps/web/public/blog/v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/blog/v1.png
--------------------------------------------------------------------------------
/apps/web/public/codehike.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/codehike.png
--------------------------------------------------------------------------------
/apps/web/public/dark-grid.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/apps/web/public/examples/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/docusaurus.png
--------------------------------------------------------------------------------
/apps/web/public/examples/fumadocs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/fumadocs.png
--------------------------------------------------------------------------------
/apps/web/public/examples/nextra-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/nextra-3.png
--------------------------------------------------------------------------------
/apps/web/public/examples/remotion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/remotion.png
--------------------------------------------------------------------------------
/apps/web/public/examples/shopify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/shopify.png
--------------------------------------------------------------------------------
/apps/web/public/examples/swiftui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/examples/swiftui.png
--------------------------------------------------------------------------------
/apps/web/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-hike/v1/6a8b19251c2c7b2e0dd68d1a0007679bc2fa5b77/apps/web/public/logo.png
--------------------------------------------------------------------------------
/apps/web/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss"
2 | import { createPreset } from "next-docs-ui/tailwind-plugin"
3 |
4 | const config = {
5 | darkMode: "class",
6 | presets: [createPreset() as any],
7 | content: [
8 | "./ui/**/*.{ts,tsx,js,jsx}",
9 | "./content/**/*.{md,mdx,ts,tsx,js,jsx}",
10 | "./demos/**/*.{md,mdx,ts,tsx,js,jsx}",
11 | "./components/**/*.{ts,tsx,js,jsx}",
12 | "./app/**/*.{ts,tsx,js,jsx}",
13 | "./demos/**/*.{ts,tsx,js,jsx}",
14 | "./mdx-components.{ts,tsx}",
15 | "./node_modules/next-docs-ui/dist/**/*.js",
16 | ],
17 | prefix: "",
18 | theme: {
19 | container: {
20 | center: true,
21 | padding: "2rem",
22 | screens: {
23 | "2xl": "1400px",
24 | },
25 | },
26 | extend: {
27 | colors: {
28 | "editor-rangeHighlightBackground": "var(--ch-18)",
29 | "editor-background": "var(--ch-16)",
30 | "editor-foreground": "var(--ch-4)",
31 | "editor-selectionBackground": "var(--ch-20)",
32 | "editorGroup-border": "var(--ch-23)",
33 | "editorGroupHeader-tabsBackground": "var(--ch-22)",
34 | "tab-activeForeground": "var(--ch-4)",
35 | },
36 | keyframes: {
37 | "accordion-down": {
38 | from: { height: "0" },
39 | to: { height: "var(--radix-accordion-content-height)" },
40 | },
41 | "accordion-up": {
42 | from: { height: "var(--radix-accordion-content-height)" },
43 | to: { height: "0" },
44 | },
45 | },
46 | animation: {
47 | "accordion-down": "accordion-down 0.2s ease-out",
48 | "accordion-up": "accordion-up 0.2s ease-out",
49 | },
50 | },
51 | },
52 | plugins: [require("tailwindcss-animate")],
53 | } satisfies Config
54 |
55 | export default config
56 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/apps/web/ui/dependency-terminal.tsx:
--------------------------------------------------------------------------------
1 | import { CopyButton } from "@/components/copy-button"
2 | import { TerminalSquare } from "lucide-react"
3 | import { TabsContent, TabsList, TabsToggle } from "./tabs-toggle"
4 | import { RawCode, Pre, highlight } from "codehike/code"
5 |
6 | async function Code({ codeblock }: { codeblock: RawCode }) {
7 | const info = await highlight(codeblock, "github-dark")
8 | return
9 | }
10 |
11 | export function DependencyTerminal({ codeblock }: { codeblock: RawCode }) {
12 | const options = ["npm install", "yarn add", "pnpm install"].map(
13 | (command) => ({
14 | name: command.split(" ")[0],
15 | content: (
16 |
23 | ),
24 | }),
25 | )
26 |
27 | return (
28 |
32 |
33 |
34 | Terminal
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/apps/web/ui/tabs-toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { ToggleGroup, ToggleGroupItem } from "./toggle-group"
3 |
4 | import React from "react"
5 |
6 | const TabsContext = React.createContext({
7 | selectedIndex: 0,
8 | setSelectedIndex: () => {},
9 | options: [],
10 | })
11 |
12 | export function TabsToggle({ children, className, options }: any) {
13 | const [selectedIndex, setSelectedIndex] = React.useState(0)
14 | return (
15 |
16 | {children}
17 |
18 | )
19 | }
20 |
21 | export function TabsList({ className }: any) {
22 | const { selectedIndex, setSelectedIndex, options } =
23 | React.useContext(TabsContext)
24 | return (
25 | {
30 | console.log(value)
31 | setSelectedIndex(Number(value))
32 | }}
33 | value={String(selectedIndex)}
34 | >
35 | {options.map((option: any, i: number) => (
36 |
42 | {option.name}
43 |
44 | ))}
45 |
46 | )
47 | }
48 |
49 | export function TabsContent({ className }: any) {
50 | const { selectedIndex, options } = React.useContext(TabsContext)
51 | const option = options[selectedIndex]
52 | return {option.content}
53 | }
54 |
--------------------------------------------------------------------------------
/apps/web/ui/toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TogglePrimitive from "@radix-ui/react-toggle"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const toggleVariants = cva(
10 | "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
11 | {
12 | variants: {
13 | variant: {
14 | default: "bg-transparent",
15 | tabs: "hover:bg-zinc-700 hover:text-zinc-200 text-zinc-400 data-[state=on]:text-zinc-100",
16 | outline:
17 | "border border-zinc-200 bg-transparent hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
18 | },
19 | size: {
20 | default: "h-10 px-3",
21 | sm: "h-6 px-2 -m-1 text-xs",
22 | lg: "h-11 px-5",
23 | },
24 | },
25 | defaultVariants: {
26 | variant: "default",
27 | size: "default",
28 | },
29 | },
30 | )
31 |
32 | const Toggle = React.forwardRef<
33 | React.ElementRef,
34 | React.ComponentPropsWithoutRef &
35 | VariantProps
36 | >(({ className, variant, size, ...props }, ref) => (
37 |
42 | ))
43 |
44 | Toggle.displayName = TogglePrimitive.Root.displayName
45 |
46 | export { Toggle, toggleVariants }
47 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Code Hike
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "build": "turbo build --filter=codehike...",
5 | "web": "turbo dev --filter=web --filter=codehike... ",
6 | "test": "turbo test",
7 | "clean": "turbo clean && rm -rf node_modules",
8 | "format": "prettier --write \"**/*.{ts,tsx,md,mdx}\"",
9 | "changeset": "changeset",
10 | "version-packages": "changeset version",
11 | "release": "changeset publish"
12 | },
13 | "devDependencies": {
14 | "@changesets/cli": "2.27.1",
15 | "prettier": "^3.1.1",
16 | "turbo": "^1.11.2"
17 | },
18 | "packageManager": "pnpm@9.7.1",
19 | "repository": "code-hike/codehike",
20 | "homepage": "https://codehike.org",
21 | "funding": {
22 | "type": "github",
23 | "url": "https://github.com/sponsors/code-hike"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/codehike/src/code/extract-annotations.tsx:
--------------------------------------------------------------------------------
1 | import { Annotation, extractAnnotations } from "@code-hike/lighter"
2 |
3 | export async function splitAnnotationsAndCode(
4 | code: string,
5 | lang: string,
6 | annotationPrefix: string,
7 | ) {
8 | let annotations: Annotation[] = []
9 | let codeWithoutAnnotations = code
10 |
11 | const { code: newCode, annotations: newAnnotations } =
12 | await extractCommentAnnotations(
13 | codeWithoutAnnotations,
14 | lang,
15 | annotationPrefix,
16 | )
17 | annotations = [...annotations, ...newAnnotations]
18 | codeWithoutAnnotations = newCode
19 |
20 | return { code: codeWithoutAnnotations, annotations }
21 | }
22 |
23 | async function extractCommentAnnotations(
24 | code: string,
25 | lang: string,
26 | annotationPrefix = "!",
27 | ) {
28 | const extractor = (comment: string) => {
29 | // const regex = /\s*(!?[\w-]+)?(\([^\)]*\)|\[[^\]]*\])?(.*)$/
30 | const regex = new RegExp(
31 | `\\s*(${annotationPrefix}?[\\w-]+)?(\\([^\\)]*\\)|\\[[^\\]]*\\])?(.*)$`,
32 | )
33 |
34 | const match = comment.match(regex)
35 | if (!match) {
36 | return null
37 | }
38 | const name = match[1]
39 | const rangeString = match[2]
40 | const query = match[3]?.trim()
41 | if (!name || !name.startsWith(annotationPrefix)) {
42 | return null
43 | }
44 |
45 | return {
46 | name: name.slice(annotationPrefix.length),
47 | rangeString,
48 | query,
49 | }
50 | }
51 |
52 | const { code: codeWithoutComments, annotations } = await extractAnnotations(
53 | code,
54 | lang,
55 | extractor,
56 | )
57 | return { code: codeWithoutComments, annotations }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/codehike/src/code/index.tsx:
--------------------------------------------------------------------------------
1 | import type {
2 | Token,
3 | RawCode,
4 | HighlightedCode,
5 | CodeAnnotation,
6 | Theme,
7 | InlineAnnotation,
8 | BlockAnnotation,
9 | InlineAnnotationComponent,
10 | BlockAnnotationComponent,
11 | AnnotationHandler,
12 | CustomPreProps,
13 | CustomPre,
14 | CustomLine,
15 | CustomLineWithAnnotation,
16 | CustomToken,
17 | CustomTokenWithAnnotation,
18 | InlineProps,
19 | } from "./types.js"
20 |
21 | import { highlight } from "./highlight.js"
22 | import { Pre, Inline } from "./pre.js"
23 | import { InnerPre, getPreRef, InnerLine, InnerToken } from "./inner.js"
24 |
25 | export type {
26 | RawCode,
27 | HighlightedCode,
28 | Token,
29 | InlineAnnotation,
30 | BlockAnnotation,
31 | CodeAnnotation,
32 | // AnnotationHandler:
33 | AnnotationHandler,
34 | InlineProps,
35 | CustomPre,
36 | CustomPreProps,
37 | BlockAnnotationComponent,
38 | CustomLine,
39 | CustomLineWithAnnotation,
40 | InlineAnnotationComponent,
41 | CustomToken,
42 | CustomTokenWithAnnotation,
43 | Theme,
44 | }
45 |
46 | export { highlight, Pre, InnerPre, InnerLine, InnerToken, getPreRef, Inline }
47 |
--------------------------------------------------------------------------------
/packages/codehike/src/code/pre-ref.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useRef } from "react"
4 | import { InnerPre } from "./inner.js"
5 | import { CustomPre } from "./types.js"
6 |
7 | export const AddRefIfNedded: CustomPre = (props: any) => {
8 | const innerRef = useRef(null)
9 | const ref = props._ref || innerRef
10 | // @ts-ignore
11 | return
12 | }
13 |
--------------------------------------------------------------------------------
/packages/codehike/src/index.ts:
--------------------------------------------------------------------------------
1 | import { MDXProps } from "mdx/types.js"
2 |
3 | type MDXContent = (props: MDXProps) => JSX.Element
4 |
5 | export function parse(Content: MDXContent, props: MDXProps = {}) {
6 | return Content({ _returnBlocks: true, ...props }) as any
7 | }
8 |
--------------------------------------------------------------------------------
/packages/codehike/src/mdx.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "unified"
2 | // this adds the jsx node types to Root
3 | import "mdast-util-mdx-jsx"
4 | import { Root } from "mdast"
5 |
6 | import { transformImportedCode } from "./mdx/0.import-code-from-path.js"
7 | import { transformAllHikes } from "./mdx/1.0.transform-hikes.js"
8 | import { transformAllCode } from "./mdx/2.transform-code.js"
9 | import { transformHikeProps } from "./mdx/3.transform-hike-props.js"
10 | import { CodeHikeConfig } from "./mdx/config.js"
11 |
12 | export type { CodeHikeConfig }
13 |
14 | export const remarkCodeHike: Plugin<[CodeHikeConfig?], Root, Root> = (
15 | config,
16 | ) => {
17 | const safeConfig = config || {}
18 | return async (root, file) => {
19 | let tree = await transformImportedCode(root, file)
20 | tree = await transformAllHikes(tree, safeConfig)
21 | tree = await transformAllCode(tree, safeConfig)
22 | return tree
23 | }
24 | }
25 |
26 | export const recmaCodeHike: Plugin<[CodeHikeConfig?], Root, Root> = (
27 | config,
28 | ) => {
29 | return async (root) => {
30 | let tree = transformHikeProps(root) as any
31 | return tree as any
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/codehike/src/mdx/1.0.transform-hikes.ts:
--------------------------------------------------------------------------------
1 | import { Root } from "mdast"
2 | import { MdxJsxFlowElement } from "mdast-util-mdx-jsx"
3 | import { visit } from "unist-util-visit"
4 | import { isHikeElement, listToSection } from "./1.1.remark-list-to-section.js"
5 | import { sectionToAttribute } from "./1.2.remark-section-to-attribute.js"
6 | import { CodeHikeConfig } from "./config.js"
7 |
8 | export async function transformAllHikes(root: Root, config: CodeHikeConfig) {
9 | let tree = wrapInHike(root)
10 |
11 | const hikes: MdxJsxFlowElement[] = []
12 |
13 | visit(tree, "mdxJsxFlowElement", (node) => {
14 | if (node.children.some(isHikeElement)) {
15 | hikes.push(node)
16 | }
17 | })
18 |
19 | await Promise.all(hikes.map((h) => transformRemarkHike(h, config)))
20 |
21 | return tree
22 | }
23 |
24 | function wrapInHike(root: Root) {
25 | // if we find any hikeable element outside of s,
26 | // let's wrap everything in a
27 | if (root.children.some(isHikeElement)) {
28 | root.children = [
29 | {
30 | type: "mdxJsxFlowElement",
31 | name: "slot",
32 | attributes: [],
33 | // todo what is different between RootContent and (BlockContent | DefinitionContent)
34 | children: root.children as any,
35 | },
36 | ]
37 | }
38 | return root
39 | }
40 |
41 | async function transformRemarkHike(
42 | node: MdxJsxFlowElement,
43 | config: CodeHikeConfig,
44 | ) {
45 | const section = await listToSection(node, config)
46 | const { children, attributes } = sectionToAttribute(section)
47 |
48 | node.children = children
49 | node.attributes.push(...attributes)
50 |
51 | return node
52 | }
53 |
--------------------------------------------------------------------------------
/packages/codehike/src/mdx/config.ts:
--------------------------------------------------------------------------------
1 | import { Theme } from "@code-hike/lighter"
2 | import { RawCode } from "../code/types.js"
3 |
4 | /**
5 | * Code Hike configuration object
6 | * @see [configuration documentation](https://codehike.org/docs)
7 | */
8 | export type CodeHikeConfig = {
9 | components?: {
10 | code?: string
11 | inlineCode?: string
12 | }
13 | ignoreCode?: (codeblock: RawCode) => boolean
14 | syntaxHighlighting?: {
15 | theme?: Theme
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/_readme.md:
--------------------------------------------------------------------------------
1 | # MD Test Suite
2 |
3 | All the `*.0.mdx` files in this directory are tests
4 |
5 | Add the snapshots you are interested in testing to the `snapshots` array in the frontmatter of the mdx file:
6 |
7 | ```md
8 | ---
9 | syntaxHighlight: github-light
10 | snapshots:
11 | - before-remark
12 | - after-remark
13 | - after-rehype
14 | - before-recma-compiled-js
15 | - before-recma-compiled-jsx
16 | - before-recma-js
17 | - before-recma-jsx
18 | - after-recma-js
19 | - after-recma-jsx
20 | - compiled-js
21 | - compiled-jsx
22 | - parsed-jsx
23 | ---
24 | ```
25 |
26 | ## Errors
27 |
28 | If there are errors in the test, it will be snapshoted in the `testname.1.error.md` file
29 |
30 | ## Running only one test:
31 |
32 | filter by test name: `pnpm watch -t foo` and then `r` to rerun the current test
33 |
34 | ## TODO
35 |
36 | - rerun tests when md file changes
37 | - remove carriage returns from snapshots
38 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/annotation.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```js
7 | // !mark
8 | const a = 1
9 | const b = 2
10 | ```
11 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/annotation.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerPre,
6 | Pre,
7 | RawCode,
8 | } from "../../src/code"
9 | import React from "react"
10 |
11 | export function render(Content: MDXContent) {
12 | // @ts-ignore
13 | return
14 | }
15 |
16 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
17 | const highlighted = await highlight(codeblock, "github-dark")
18 | return
19 | }
20 |
21 | const mark: AnnotationHandler = {
22 | name: "mark",
23 | Pre: (props) => ,
24 | Block: ({ children }) => {children} ,
25 | }
26 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/annotation.9.rendered.html:
--------------------------------------------------------------------------------
1 | const a = 1
8 |
const b = 2
9 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/assets/test.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | my_list = [1, 'a', 32, 'c', 'd', 31]
4 | print(random.choice(my_list))
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - before-remark
4 | - after-remark
5 | - after-rehype
6 | - before-recma-compiled-js
7 | - before-recma-compiled-jsx
8 | - before-recma-compiled-function
9 | - before-recma-js
10 | - before-recma-jsx
11 | - after-recma-js
12 | - after-recma-jsx
13 | - compiled-js
14 | - compiled-jsx
15 | - compiled-function
16 | - parsed-jsx
17 | - rendered
18 | ---
19 |
20 | # Lorem
21 |
22 | !hello world
23 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.2.before-remark.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "root",
3 | "children": [
4 | {
5 | "type": "heading",
6 | "depth": 1,
7 | "children": [{ "type": "text", "value": "Lorem" }]
8 | },
9 | {
10 | "type": "paragraph",
11 | "children": [{ "type": "text", "value": "!hello world" }]
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.5.before-recma-compiled-function.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | "use strict"
3 | function _createMdxContent(props) {
4 | const _components = {
5 | h1: "h1",
6 | slot: "slot",
7 | ...props.components,
8 | }
9 | return (
10 | <_components.slot
11 | __hike={{
12 | children: "",
13 | title: "",
14 | _data: {
15 | header: "",
16 | },
17 | hello: "world",
18 | }}
19 | >
20 | <_components.slot path="">
21 | <_components.h1>{"Lorem"}
22 |
23 |
24 | )
25 | }
26 | function MDXContent(props = {}) {
27 | const { wrapper: MDXLayout } = props.components || {}
28 | return MDXLayout ? (
29 |
30 | <_createMdxContent {...props} />
31 |
32 | ) : (
33 | _createMdxContent(props)
34 | )
35 | }
36 | return {
37 | default: MDXContent,
38 | }
39 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.5.before-recma-compiled-js.js:
--------------------------------------------------------------------------------
1 | import { jsx as _jsx } from "react/jsx-runtime"
2 | function _createMdxContent(props) {
3 | const _components = {
4 | h1: "h1",
5 | slot: "slot",
6 | ...props.components,
7 | }
8 | return _jsx(_components.slot, {
9 | __hike: {
10 | children: "",
11 | title: "",
12 | _data: {
13 | header: "",
14 | },
15 | hello: "world",
16 | },
17 | children: _jsx(_components.slot, {
18 | path: "",
19 | children: _jsx(_components.h1, {
20 | children: "Lorem",
21 | }),
22 | }),
23 | })
24 | }
25 | export default function MDXContent(props = {}) {
26 | const { wrapper: MDXLayout } = props.components || {}
27 | return MDXLayout
28 | ? _jsx(MDXLayout, {
29 | ...props,
30 | children: _jsx(_createMdxContent, {
31 | ...props,
32 | }),
33 | })
34 | : _createMdxContent(props)
35 | }
36 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.5.before-recma-compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const _components = {
4 | h1: "h1",
5 | slot: "slot",
6 | ...props.components,
7 | }
8 | return (
9 | <_components.slot
10 | __hike={{
11 | children: "",
12 | title: "",
13 | _data: {
14 | header: "",
15 | },
16 | hello: "world",
17 | }}
18 | >
19 | <_components.slot path="">
20 | <_components.h1>{"Lorem"}
21 |
22 |
23 | )
24 | }
25 | export default function MDXContent(props = {}) {
26 | const { wrapper: MDXLayout } = props.components || {}
27 | return MDXLayout ? (
28 |
29 | <_createMdxContent {...props} />
30 |
31 | ) : (
32 | _createMdxContent(props)
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.7.compiled-function.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | "use strict"
3 | function _createMdxContent(props) {
4 | const _components = {
5 | h1: "h1",
6 | slot: "slot",
7 | ...props.components,
8 | }
9 | const _blocks = {
10 | children: <_components.h1>{"Lorem"},
11 | title: "",
12 | _data: {
13 | header: "",
14 | },
15 | hello: "world",
16 | }
17 | if (props._returnBlocks) {
18 | return _blocks
19 | }
20 | return _blocks.children
21 | }
22 | function MDXContent(props = {}) {
23 | const { wrapper: MDXLayout } = props.components || {}
24 | return MDXLayout ? (
25 |
26 | <_createMdxContent {...props} />
27 |
28 | ) : (
29 | _createMdxContent(props)
30 | )
31 | }
32 | return {
33 | default: MDXContent,
34 | }
35 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.7.compiled-js.js:
--------------------------------------------------------------------------------
1 | import { jsx as _jsx } from "react/jsx-runtime"
2 | function _createMdxContent(props) {
3 | const _components = {
4 | h1: "h1",
5 | slot: "slot",
6 | ...props.components,
7 | }
8 | const _blocks = {
9 | children: _jsx(_components.h1, {
10 | children: "Lorem",
11 | }),
12 | title: "",
13 | _data: {
14 | header: "",
15 | },
16 | hello: "world",
17 | }
18 | if (props._returnBlocks) {
19 | return _blocks
20 | }
21 | return _blocks.children
22 | }
23 | export default function MDXContent(props = {}) {
24 | const { wrapper: MDXLayout } = props.components || {}
25 | return MDXLayout
26 | ? _jsx(MDXLayout, {
27 | ...props,
28 | children: _jsx(_createMdxContent, {
29 | ...props,
30 | }),
31 | })
32 | : _createMdxContent(props)
33 | }
34 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const _components = {
4 | h1: "h1",
5 | slot: "slot",
6 | ...props.components,
7 | }
8 | const _blocks = {
9 | children: <_components.h1>{"Lorem"},
10 | title: "",
11 | _data: {
12 | header: "",
13 | },
14 | hello: "world",
15 | }
16 | if (props._returnBlocks) {
17 | return _blocks
18 | }
19 | return _blocks.children
20 | }
21 | export default function MDXContent(props = {}) {
22 | const { wrapper: MDXLayout } = props.components || {}
23 | return MDXLayout ? (
24 |
25 | <_createMdxContent {...props} />
26 |
27 | ) : (
28 | _createMdxContent(props)
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.8.parsed-jsx.jsx:
--------------------------------------------------------------------------------
1 | var block = {
2 | "_data": {
3 | "header": "",
4 | },
5 | "children":
6 | Lorem
7 | ,
8 | "hello": "world",
9 | "title": "",
10 | }
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/basic.9.rendered.html:
--------------------------------------------------------------------------------
1 | Lorem
2 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code-empty.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | - rendered
5 | ---
6 |
7 | {/* prettier-ignore */}
8 | ```
9 | ```
10 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code-empty.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | /*prettier-ignore*/
3 | function _createMdxContent(props) {
4 | const {MyCode} = props.components || ({});
5 | if (!MyCode) _missingMdxReference("MyCode", true);
6 | return <>{}{"\n"} >;
11 | }
12 | export default function MDXContent(props = {}) {
13 | const { wrapper: MDXLayout } = props.components || {}
14 | return MDXLayout ? (
15 |
16 | <_createMdxContent {...props} />
17 |
18 | ) : (
19 | _createMdxContent(props)
20 | )
21 | }
22 | function _missingMdxReference(id, component) {
23 | throw new Error(
24 | "Expected " +
25 | (component ? "component" : "object") +
26 | " `" +
27 | id +
28 | "` to be defined: you likely forgot to import, pass, or provide it.",
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code-empty.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | - rendered
5 | ---
6 |
7 | ```js
8 | console.log(1)
9 | ```
10 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const { MyCode } = props.components || {}
4 | if (!MyCode) _missingMdxReference("MyCode", true)
5 | return (
6 |
13 | )
14 | }
15 | export default function MDXContent(props = {}) {
16 | const { wrapper: MDXLayout } = props.components || {}
17 | return MDXLayout ? (
18 |
19 | <_createMdxContent {...props} />
20 |
21 | ) : (
22 | _createMdxContent(props)
23 | )
24 | }
25 | function _missingMdxReference(id, component) {
26 | throw new Error(
27 | "Expected " +
28 | (component ? "component" : "object") +
29 | " `" +
30 | id +
31 | "` to be defined: you likely forgot to import, pass, or provide it.",
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/code.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | console.
4 | log
5 | (
6 | 1
7 | )
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-block.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - parsed-jsx
4 | ---
5 |
6 | hello
7 |
8 | # !foo hey
9 |
10 | # /
11 |
12 | hey
13 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-block.8.parsed-jsx.jsx:
--------------------------------------------------------------------------------
1 | var block = {
2 | "_data": {
3 | "header": "",
4 | },
5 | "children":
6 |
7 | hello
8 |
9 |
10 | hey
11 |
12 | ,
13 | "foo": {
14 | "_data": {
15 | "header": "# !foo hey",
16 | },
17 | "children": ,
18 | "title": "hey",
19 | },
20 | "title": "",
21 | }
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-children-and-import.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | ---
5 |
6 | import { x } from "@/components/code/code-with-notes"
7 |
8 | ## !demo
9 |
10 | Add callouts inside your code blocks.
11 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-children-and-import.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | import { x } from "@/components/code/code-with-notes"
3 | function _createMdxContent(props) {
4 | const _components = {
5 | p: "p",
6 | slot: "slot",
7 | ...props.components,
8 | }
9 | const _blocks = {
10 | children: undefined,
11 | title: "",
12 | _data: {
13 | header: "",
14 | },
15 | demo: {
16 | children: (
17 | <_components.p>{"Add callouts inside your code blocks."}
18 | ),
19 | title: "",
20 | _data: {
21 | header: "## !demo",
22 | },
23 | },
24 | }
25 | if (props._returnBlocks) {
26 | return _blocks
27 | }
28 | return _blocks.children
29 | }
30 | export default function MDXContent(props = {}) {
31 | const { wrapper: MDXLayout } = props.components || {}
32 | return MDXLayout ? (
33 |
34 | <_createMdxContent {...props} />
35 |
36 | ) : (
37 | _createMdxContent(props)
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-line.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | - rendered
5 | ---
6 |
7 | {/* prettier-ignore */}
8 | ```js
9 |
10 | // !m 343
11 | const a = 1
12 | const b = 2
13 | ```
14 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-line.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | /*prettier-ignore*/
3 | function _createMdxContent(props) {
4 | const {MyCode} = props.components || ({});
5 | if (!MyCode) _missingMdxReference("MyCode", true);
6 | return <>{}{"\n"} >;
11 | }
12 | export default function MDXContent(props = {}) {
13 | const { wrapper: MDXLayout } = props.components || {}
14 | return MDXLayout ? (
15 |
16 | <_createMdxContent {...props} />
17 |
18 | ) : (
19 | _createMdxContent(props)
20 | )
21 | }
22 | function _missingMdxReference(id, component) {
23 | throw new Error(
24 | "Expected " +
25 | (component ? "component" : "object") +
26 | " `" +
27 | id +
28 | "` to be defined: you likely forgot to import, pass, or provide it.",
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/empty-line.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | const
5 | a
6 | =
7 | 1
8 |
9 |
10 | const
11 | b
12 | =
13 | 2
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/ignore-codeblock.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | ---
5 |
6 | hey
7 |
8 | ```mermaid foo
9 | graph TD;
10 | A-->B;
11 | A-->C;
12 | B-->D;
13 | C-->D;
14 | ```
15 |
16 | ```js index.js
17 | console.log(2)
18 | ```
19 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/ignore-codeblock.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const _components = {
4 | code: "code",
5 | p: "p",
6 | pre: "pre",
7 | ...props.components,
8 | },
9 | { MyCode } = _components
10 | if (!MyCode) _missingMdxReference("MyCode", true)
11 | return (
12 | <>
13 | <_components.p>{"hey"}
14 | {"\n"}
15 | <_components.pre>
16 | <_components.code className="language-mermaid">
17 | {
18 | "graph TD;\r\n A-->B;\r\n A-->C;\r\n B-->D;\r\n C-->D;\n"
19 | }
20 |
21 |
22 | {"\n"}
23 |
30 | >
31 | )
32 | }
33 | export default function MDXContent(props = {}) {
34 | const { wrapper: MDXLayout } = props.components || {}
35 | return MDXLayout ? (
36 |
37 | <_createMdxContent {...props} />
38 |
39 | ) : (
40 | _createMdxContent(props)
41 | )
42 | }
43 | function _missingMdxReference(id, component) {
44 | throw new Error(
45 | "Expected " +
46 | (component ? "component" : "object") +
47 | " `" +
48 | id +
49 | "` to be defined: you likely forgot to import, pass, or provide it.",
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/import-code.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | ---
5 |
6 | hello
7 |
8 | ```py
9 | !from ./assets/test.py
10 | ```
11 |
12 | ```py !
13 | !from ./assets/test.py
14 | ```
15 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/import-code.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const _components = {
4 | p: "p",
5 | slot: "slot",
6 | ...props.components,
7 | },
8 | { MyCode } = _components
9 | if (!MyCode) _missingMdxReference("MyCode", true)
10 | const _blocks = {
11 | children: (
12 | <>
13 | <_components.p>{"hello"}
14 |
22 | >
23 | ),
24 | title: "",
25 | _data: {
26 | header: "",
27 | },
28 | code: {
29 | value:
30 | "import random\r\n\r\nmy_list = [1, 'a', 32, 'c', 'd', 31]\r\nprint(random.choice(my_list))",
31 | lang: "py",
32 | meta: "",
33 | },
34 | }
35 | if (props._returnBlocks) {
36 | return _blocks
37 | }
38 | return _blocks.children
39 | }
40 | export default function MDXContent(props = {}) {
41 | const { wrapper: MDXLayout } = props.components || {}
42 | return MDXLayout ? (
43 |
44 | <_createMdxContent {...props} />
45 |
46 | ) : (
47 | _createMdxContent(props)
48 | )
49 | }
50 | function _missingMdxReference(id, component) {
51 | throw new Error(
52 | "Expected " +
53 | (component ? "component" : "object") +
54 | " `" +
55 | id +
56 | "` to be defined: you likely forgot to import, pass, or provide it.",
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/indentation.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | {/* prettier-ignore */}
7 | ```js
8 | if (true)
9 | return 3
10 | ```
11 |
12 | {/* prettier-ignore */}
13 | ```py
14 | if True:
15 | return 1
16 | return 2
17 | ```
18 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/indentation.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerLine,
6 | Pre,
7 | RawCode,
8 | } from "../../src/code"
9 | import React from "react"
10 |
11 | export function render(Content: MDXContent) {
12 | // @ts-ignore
13 | return
14 | }
15 |
16 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
17 | const highlighted = await highlight(codeblock, "github-dark")
18 | return
19 | }
20 |
21 | const line: AnnotationHandler = {
22 | name: "line",
23 | Pre: ({ _stack, ...props }) => ,
24 | Line: (props) => ,
25 | }
26 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/indentation.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | if
4 | (
5 | true
6 | )
7 |
8 |
9 | return
10 | 3
11 |
12 |
13 |
14 |
15 |
16 | if
17 | True
18 | :
19 |
20 |
21 | return
22 | 1
23 |
24 |
25 | return
26 | 2
27 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/inline-code-highlighted.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | syntaxHighlight: github-dark
3 | snapshots:
4 | - before-remark
5 | - compiled-jsx
6 | - rendered
7 | ---
8 |
9 | _`var x = 1`_
10 |
11 | hello _html`hey
`_ world
12 |
13 | two lines _`var x = 1
14 | var y = 2`_
15 |
16 | extra meta _jsx foo`var x = 1`bar bar_
17 |
18 | lorem _just emphasize_ ipsum
19 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/inline-code-highlighted.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | var
4 | x
5 | =
6 | 1
7 |
8 |
9 |
10 | hello
11 |
12 | <
13 | div
14 | class
15 | =
16 | "2"
17 | >hey</
18 | div
19 | >
20 |
21 | world
22 |
23 |
24 | two lines
25 |
26 | var
27 | x
28 | =
29 | 1
30 | var
31 | y
32 | =
33 | 2
34 |
35 |
36 |
37 | extra meta
38 |
39 | var
40 | x
41 | =
42 | 1
43 |
44 |
45 |
46 | lorem
47 | just emphasize
48 | ipsum
49 |
50 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/inline-code.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - before-remark
4 | - compiled-jsx
5 | - rendered
6 | ---
7 |
8 | _`var x = 1`_
9 |
10 | hello _html`hey
`_ world
11 |
12 | two lines _`var x = 1
13 | var y = 2`_
14 |
15 | extra meta _jsx foo`var x = 1`bar bar_
16 |
17 | lorem _just emphasize_ ipsum
18 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/inline-code.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | var
4 | x
5 | =
6 | 1
7 |
8 |
9 |
10 | hello
11 |
12 | <
13 | div
14 | class
15 | =
16 | "2"
17 | >hey</
18 | div
19 | >
20 |
21 | world
22 |
23 |
24 | two lines
25 |
26 | var
27 | x
28 | =
29 | 1
30 | var
31 | y
32 | =
33 | 2
34 |
35 |
36 |
37 | extra meta
38 |
39 | var
40 | x
41 | =
42 | 1
43 |
44 |
45 |
46 | lorem
47 | just emphasize
48 | ipsum
49 |
50 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/nested-blocks.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - parsed-jsx
4 | ---
5 |
6 | hello
7 |
8 | # !one uno
9 |
10 | 1
11 |
12 | ## !!foo 111
13 |
14 | 1.1
15 |
16 | ### !bar b
17 |
18 | 1.1.1
19 |
20 | ### /
21 |
22 | more
23 |
24 | ## !!foo 222
25 |
26 | hey
27 |
28 | ## /
29 |
30 | ho
31 |
32 | ## !extra
33 |
34 | extra
35 |
36 | # /
37 |
38 | hey
39 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/nested-blocks.8.parsed-jsx.jsx:
--------------------------------------------------------------------------------
1 | var block = {
2 | "_data": {
3 | "header": "",
4 | },
5 | "children":
6 |
7 | hello
8 |
9 |
10 | hey
11 |
12 | ,
13 | "one": {
14 | "_data": {
15 | "header": "# !one uno",
16 | },
17 | "children":
18 |
19 | 1
20 |
21 |
22 | ho
23 |
24 | ,
25 | "extra": {
26 | "_data": {
27 | "header": "## !extra",
28 | },
29 | "children":
30 | extra
31 |
,
32 | "title": "",
33 | },
34 | "foo": [
35 | {
36 | "_data": {
37 | "header": "## !!foo 111",
38 | },
39 | "bar": {
40 | "_data": {
41 | "header": "### !bar b",
42 | },
43 | "children":
44 | 1.1.1
45 |
,
46 | "title": "b",
47 | },
48 | "children":
49 |
50 | 1.1
51 |
52 |
53 | more
54 |
55 | ,
56 | "title": "111",
57 | },
58 | {
59 | "_data": {
60 | "header": "## !!foo 222",
61 | },
62 | "children":
63 | hey
64 |
,
65 | "title": "222",
66 | },
67 | ],
68 | "title": "uno",
69 | },
70 | "title": "",
71 | }
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/no-hike.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - compiled-jsx
4 | ---
5 |
6 | ## Hello world
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/no-hike.7.compiled-jsx.jsx:
--------------------------------------------------------------------------------
1 | /*@jsxRuntime automatic @jsxImportSource react*/
2 | function _createMdxContent(props) {
3 | const _components = {
4 | h2: "h2",
5 | ...props.components,
6 | }
7 | return (
8 | <>
9 | <_components.h2>{"Hello world"}
10 | {"\n"}
11 |
16 | >
17 | )
18 | }
19 | export default function MDXContent(props = {}) {
20 | const { wrapper: MDXLayout } = props.components || {}
21 | return MDXLayout ? (
22 |
23 | <_createMdxContent {...props} />
24 |
25 | ) : (
26 | _createMdxContent(props)
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/only-if-annotated.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```js
7 | a
8 | // !mark
9 | b
10 | ```
11 |
12 | ```js
13 | a
14 | b
15 | ```
16 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/only-if-annotated.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerLine,
6 | Pre,
7 | RawCode,
8 | } from "../../src/code"
9 | import React from "react"
10 |
11 | export function render(Content: MDXContent) {
12 | // @ts-ignore
13 | return
14 | }
15 |
16 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
17 | const highlighted = await highlight(codeblock, "github-dark")
18 | return
19 | }
20 |
21 | const mark: AnnotationHandler = {
22 | name: "mark",
23 | onlyIfAnnotated: true,
24 | Line: (props) => ,
25 | }
26 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/only-if-annotated.9.rendered.html:
--------------------------------------------------------------------------------
1 |
7 |
13 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/regex-range.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```jsx
7 | // !fold[/className="(.*?)"/gm] folded
8 | const Foo = () => (
9 |
10 | hey
11 |
12 | )
13 | ```
14 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/regex-range.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerToken,
6 | Pre,
7 | RawCode,
8 | } from "../../src/code"
9 | import React from "react"
10 |
11 | export function render(Content: MDXContent) {
12 | // @ts-ignore
13 | return
14 | }
15 |
16 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
17 | const highlighted = await highlight(codeblock, "github-dark")
18 | return
19 | }
20 |
21 | const fold: AnnotationHandler = {
22 | name: "fold",
23 | Pre: ({ children }) => {children}
,
24 | Token: ({ annotation, ...props }) => (
25 |
26 |
27 |
28 | ),
29 | }
30 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-line.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```js
7 | const a = 1
8 | // !mark(1:3) 1
9 | const b = 2
10 | // !mark 2
11 | const c = 3
12 | ```
13 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-line.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerLine,
6 | Pre,
7 | RawCode,
8 | } from "../../src/code"
9 | import React from "react"
10 |
11 | export function render(Content: MDXContent) {
12 | // @ts-ignore
13 | return
14 | }
15 |
16 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
17 | const highlighted = await highlight(codeblock, "github-dark")
18 | return
19 | }
20 |
21 | const mark: AnnotationHandler = {
22 | name: "mark",
23 | Pre: ({ children }) => {children}
,
24 | Line: ({ annotation, ...props }) => (
25 |
26 |
27 |
28 | ),
29 | }
30 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-line.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | const
5 | a
6 | =
7 | 1
8 |
9 |
10 |
11 |
12 | const
13 | b
14 | =
15 | 2
16 |
17 |
18 |
19 |
20 |
21 | const
22 | c
23 | =
24 | 3
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-token.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```js
7 | const a = 1
8 | // !mark(1:3) 1
9 | const b = 2
10 | // !mark 2
11 | const c = 3
12 | ```
13 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-token.0.render.tsx:
--------------------------------------------------------------------------------
1 | import { MDXContent } from "mdx/types"
2 | import {
3 | AnnotationHandler,
4 | highlight,
5 | InnerPre,
6 | InnerToken,
7 | Pre,
8 | RawCode,
9 | } from "../../src/code"
10 | import React from "react"
11 |
12 | export function render(Content: MDXContent) {
13 | // @ts-ignore
14 | return
15 | }
16 |
17 | async function MyCode({ codeblock }: { codeblock: RawCode }) {
18 | const highlighted = await highlight(codeblock, "github-dark")
19 | return
20 | }
21 |
22 | const mark: AnnotationHandler = {
23 | name: "mark",
24 | Pre: ({ children }) => {children}
,
25 | Token: ({ annotation, ...props }) => (
26 |
27 |
28 |
29 | ),
30 | }
31 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/stacked-token.9.rendered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | const
4 | a
5 | =
6 | 1
7 |
8 |
9 | const
10 | b
11 | =
12 | 2
13 |
14 |
15 |
16 | const
17 |
18 |
19 | c
20 |
21 |
22 | =
23 |
24 |
25 | 3
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/syntax-highlight.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | syntaxHighlight: github-dark
3 | snapshots:
4 | - compiled-jsx
5 | ---
6 |
7 | ```js lorem.js
8 | // !mark[6:10] hey
9 | const a = 1
10 | ```
11 |
12 | ```js !foo bar.js
13 | // !mark 1
14 | const b = 2
15 | ```
16 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/two-ps.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - before-remark
4 | - parsed-jsx
5 | ---
6 |
7 | ## !valid
8 |
9 | !a1 1
10 | !!a2 2
11 | 2
12 | !a3 3
13 |
14 | 
15 | 
16 |
17 | !c1 1
18 | cc
19 | 
20 |
21 | 
22 | !!d2 2
23 |
24 | !e1 1
25 | ee ee
26 | ee
27 |
28 | ## !invalid
29 |
30 | ff ff
31 | !f1 dd
32 |
33 | gg gg
34 | 
35 |
36 | 
37 | hh hh
38 |
39 | ii  ii
40 |
41 | 
42 | jjj
43 | !j2 2
44 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/unknown-lang.0.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | snapshots:
3 | - rendered
4 | ---
5 |
6 | ```firulete
7 | console.log(1)
8 | ```
9 |
--------------------------------------------------------------------------------
/packages/codehike/tests/md-suite/unknown-lang.9.rendered.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/codehike/tests/utils.suite.tsx:
--------------------------------------------------------------------------------
1 | import fs from "node:fs/promises"
2 | import { expect } from "vitest"
3 |
4 | expect.addSnapshotSerializer({
5 | serialize(val, config, indentation, depth, refs, printer) {
6 | const r = printer(
7 | val.block,
8 | { ...config, escapeString: true },
9 | indentation,
10 | depth,
11 | refs,
12 | )
13 | if (depth > 0) return r
14 |
15 | return `var block = ${r}`
16 | },
17 | test(val) {
18 | return val && Object.prototype.hasOwnProperty.call(val, "block")
19 | },
20 | })
21 | expect.addSnapshotSerializer({
22 | test(val) {
23 | return typeof val === "string"
24 | },
25 | serialize(val) {
26 | return JSON.stringify(val)
27 | },
28 | })
29 |
30 | export async function getTestNames(dataPath: string) {
31 | const allNames = await fs.readdir(dataPath)
32 | const mdNames = allNames.filter((f) => f.endsWith(".0.mdx"))
33 | return mdNames.map((f) => f.replace(".0.mdx", ""))
34 | }
35 |
36 | interface SerializableError extends Error {
37 | [key: string]: any
38 | }
39 |
40 | export function errorToMd(error: SerializableError): string {
41 | let markdown = `# Error: ${error.name}\n\n`
42 | markdown += `${error.message}\n\n`
43 |
44 | if (error.stack) {
45 | markdown += `\`\`\`\n${error.stack}\n\`\`\`\n\n`
46 | }
47 |
48 | // Include additional properties
49 | const additionalProperties = Object.getOwnPropertyNames(error).filter(
50 | (prop) => !["name", "message", "stack"].includes(prop),
51 | )
52 | if (additionalProperties.length > 0) {
53 | markdown += `## Additional Properties\n`
54 | additionalProperties.forEach((prop) => {
55 | markdown += `- **${prop}:** ${error[prop]}\n`
56 | })
57 | }
58 |
59 | return markdown
60 | }
61 |
--------------------------------------------------------------------------------
/packages/codehike/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "compilerOptions": {
5 | "composite": false,
6 | "declaration": true,
7 | "declarationMap": true,
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "inlineSources": false,
11 | "isolatedModules": true,
12 | "moduleResolution": "NodeNext",
13 | "noUnusedLocals": false,
14 | "noUnusedParameters": false,
15 | "preserveWatchOutput": true,
16 | "skipLibCheck": true,
17 | "strict": true,
18 | "jsx": "react-jsx",
19 | "lib": ["dom", "es2019"],
20 | "module": "NodeNext",
21 | "target": "es2019",
22 | "outDir": "dist"
23 | },
24 | "include": ["src"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 | - "packages/*"
4 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Moved to https://github.com/code-hike/codehike
2 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalDependencies": ["**/.env.*local"],
4 | "pipeline": {
5 | "build": {
6 | "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
7 | "dependsOn": ["^build"]
8 | },
9 | "test": {
10 | "outputs": ["coverage/**"],
11 | "dependsOn": []
12 | },
13 | "dev": {
14 | "cache": false,
15 | "persistent": true
16 | },
17 | "clean": {
18 | "cache": false
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------