From 0dc553373c2feb68e7ac7709517ef8895ce4403d Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 11:56:44 +0000 Subject: [PATCH 01/10] Fix transitions --- .gitpod.yml | 9 +++ package.json | 3 +- packages/mini-editor/src/editor-spring.tsx | 77 +++++++++++++++++----- packages/smooth-code/src/code-spring.tsx | 69 +++++++++++++++---- yarn.lock | 5 ++ 5 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 .gitpod.yml diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..95ac4cb2 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,9 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) +# and commit this file to your remote git repository to share the goodness with others. + +tasks: + - init: yarn install && yarn run build + command: yarn run playground + + diff --git a/package.json b/package.json index 7cd6b141..f0ccc17e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ }, "devDependencies": { "auto": "^10.18.7", - "lerna": "^4.0.0" + "lerna": "^4.0.0", + "prettier": "^2.5.1" }, "repository": "code-hike/codehike", "author": "pomber ", diff --git a/packages/mini-editor/src/editor-spring.tsx b/packages/mini-editor/src/editor-spring.tsx index 32133df1..a6a199bd 100644 --- a/packages/mini-editor/src/editor-spring.tsx +++ b/packages/mini-editor/src/editor-spring.tsx @@ -60,28 +60,73 @@ function EditorSpring({ function useStepSpring( step: EditorStep, springConfig: SpringConfig = defaultSpring -) { - const [{ target, prev, next }, setState] = React.useState( - { - target: 0, - prev: step, - next: step, - } - ) +): { prev: EditorStep; next: EditorStep; t: number } { + const [{ target, steps, index }, setState] = + React.useState({ + target: 2, + steps: [step, step, step], + index: 0, + }) React.useEffect(() => { - if (next != step) { - setState(s => ({ - target: s.target + 1, - prev: next, - next: step, - })) + const lastStep = steps[steps.length - 1] + if (lastStep != step) { + setState(s => updateStepSpring(s, step, progress)) } }, [step]) const [progress] = useSpring(target, springConfig) - const t = progress % 1 + const trioProgress = progress - index + + const result = + trioProgress <= 1 + ? { + prev: steps[0], + next: steps[1], + t: trioProgress, + } + : { + prev: steps[1], + next: steps[2], + t: trioProgress - 1, + } + + return result +} + +type StepSpringState = { + target: number + steps: [EditorStep, EditorStep, EditorStep] + index: number +} - return { prev, next, t: t || 1 } +function updateStepSpring( + state: StepSpringState, + newStep: EditorStep, + progress: number +): StepSpringState { + const { steps, target, index } = state + const stepsClone = + steps.slice() as StepSpringState["steps"] + + const trioProgress = progress - index + + if (trioProgress < 1) { + stepsClone[2] = newStep + return { + ...state, + steps: stepsClone, + } + } else { + stepsClone[0] = steps[1] + stepsClone[1] = steps[2] + stepsClone[2] = newStep + return { + ...state, + steps: stepsClone, + target: target + 1, + index: index + 1, + } + } } diff --git a/packages/smooth-code/src/code-spring.tsx b/packages/smooth-code/src/code-spring.tsx index 9950fed8..86bc1ad2 100644 --- a/packages/smooth-code/src/code-spring.tsx +++ b/packages/smooth-code/src/code-spring.tsx @@ -44,23 +44,70 @@ function useStepSpring( step: CodeStep, springConfig: SpringConfig = defaultSpring ): { tween: FullTween; t: number } { - const [{ target, tween }, setState] = React.useState({ - target: 0, - tween: { prev: step, next: step }, - }) + const [{ target, steps, index }, setState] = + React.useState({ + target: 2, + steps: [step, step, step], + index: 0, + }) React.useEffect(() => { - if (tween.next != step) { - setState(s => ({ - target: s.target + 1, - tween: { prev: tween.next, next: step }, - })) + const lastStep = steps[steps.length - 1] + if (lastStep != step) { + setState(s => updateStepSpring(s, step, progress)) } }, [step]) const [progress] = useSpring(target, springConfig) - const t = progress % 1 + const trioProgress = progress - index - return { tween, t: t || 1 } + const result = + trioProgress <= 1 + ? { + tween: { prev: steps[0], next: steps[1] }, + t: trioProgress, + } + : { + tween: { prev: steps[1], next: steps[2] }, + t: trioProgress - 1, + } + + return result +} + +type StepSpringState = { + target: number + steps: [CodeStep, CodeStep, CodeStep] + index: number +} + +function updateStepSpring( + state: StepSpringState, + newStep: CodeStep, + progress: number +): StepSpringState { + const { steps, target, index } = state + const stepsClone = + steps.slice() as StepSpringState["steps"] + + const trioProgress = progress - index + + if (trioProgress < 1) { + stepsClone[2] = newStep + return { + ...state, + steps: stepsClone, + } + } else { + stepsClone[0] = steps[1] + stepsClone[1] = steps[2] + stepsClone[2] = newStep + return { + ...state, + steps: stepsClone, + target: target + 1, + index: index + 1, + } + } } diff --git a/yarn.lock b/yarn.lock index 7dbbdb6c..41a71597 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15110,6 +15110,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + prettier@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" From 3be43dbb545a98e65b0f6860cd1a117ea61f5d4b Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 12:55:25 +0000 Subject: [PATCH 02/10] Update shiki --- packages/highlighter/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/highlighter/package.json b/packages/highlighter/package.json index bcaac602..5e666342 100644 --- a/packages/highlighter/package.json +++ b/packages/highlighter/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@code-hike/utils": "^0.3.0-next.0", - "shiki": "^0.9.14" + "shiki": "^0.10.1" }, "homepage": "https://codehike.org", "repository": "code-hike/codehike", diff --git a/yarn.lock b/yarn.lock index 41a71597..0a5e914a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16796,10 +16796,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shiki@^0.9.14: - version "0.9.14" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.14.tgz#6b3e369edf76049ae7ad7c2b0498c35c200b8dd7" - integrity sha512-uLHjjyJdNsMzF9GOF8vlOuZ8BwigiYPraMN5yjC826k8K7Xu90JQcC5GUNrzRibLgT2EOk9597I1IX+jRdA8nw== +shiki@^0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" + integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== dependencies: jsonc-parser "^3.0.0" vscode-oniguruma "^1.6.1" From 942167fcb83950aebfff716f2e1fa7272b438abe Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 13:14:22 +0000 Subject: [PATCH 03/10] Change code link to hover --- packages/mdx/src/client/section.tsx | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/mdx/src/client/section.tsx b/packages/mdx/src/client/section.tsx index cf04e679..94013cf3 100644 --- a/packages/mdx/src/client/section.tsx +++ b/packages/mdx/src/client/section.tsx @@ -62,9 +62,8 @@ export function Section({ } export function SectionCode() { - const { props, setFocus } = React.useContext( - SectionContext - ) + const { props, setFocus } = + React.useContext(SectionContext) const onTabClick = (filename: string) => { setFocus({ fileName: filename, focus: null, id: "" }) @@ -84,16 +83,13 @@ export function SectionLink({ file?: string children: React.ReactNode }) { - const { - setFocus, - resetFocus, - selectedId, - } = React.useContext(SectionContext) + const { setFocus, resetFocus, selectedId } = + React.useContext(SectionContext) const isSelected = selectedId === id - const handleClick = isSelected - ? resetFocus - : () => setFocus({ fileName: file, focus, id }) + // const handleClick = isSelected + // ? resetFocus + // : () => setFocus({ fileName: file, focus, id }) return ( + setFocus({ fileName: file, focus, id }) + } + onMouseOut={resetFocus} /> ) } From 63c113e52657757ba9e5d2479daf35feacb2c2c3 Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 18:16:03 +0000 Subject: [PATCH 04/10] Support links in scrollycoding --- packages/mdx/src/client/scrollycoding.tsx | 25 ++- packages/mdx/src/client/section.tsx | 97 +++++++-- packages/mdx/src/plugin/scrollycoding.ts | 3 + packages/mdx/src/plugin/section.ts | 2 +- packages/playground/content/scrollycoding.mdx | 187 +++++++++--------- packages/playground/content/test.mdx | 24 ++- 6 files changed, 216 insertions(+), 122 deletions(-) diff --git a/packages/mdx/src/client/scrollycoding.tsx b/packages/mdx/src/client/scrollycoding.tsx index 5807282c..26aa673e 100644 --- a/packages/mdx/src/client/scrollycoding.tsx +++ b/packages/mdx/src/client/scrollycoding.tsx @@ -9,6 +9,7 @@ import { Step as ScrollerStep, } from "@code-hike/scroller" import { Preview, PresetConfig } from "./preview" +import { LinkableSection } from "./section" export function Scrollycoding({ children, @@ -45,6 +46,19 @@ export function Scrollycoding({ setState({ ...state, step: newStep }) } + function onLinkActivation( + stepIndex: number, + filename: string | undefined, + focus: string | null + ) { + const newStep = updateEditorStep( + editorSteps[stepIndex], + filename, + focus + ) + setState({ ...state, stepIndex, step: newStep }) + } + return (
- {children} + { + onLinkActivation(i, fileName, focus) + }} + onReset={() => { + onStepChange(i) + }} + > + {children} + ))} diff --git a/packages/mdx/src/client/section.tsx b/packages/mdx/src/client/section.tsx index 94013cf3..49a8ad2c 100644 --- a/packages/mdx/src/client/section.tsx +++ b/packages/mdx/src/client/section.tsx @@ -4,17 +4,14 @@ import { InnerCode, updateEditorStep } from "./code" const SectionContext = React.createContext<{ props: EditorProps - selectedId?: string setFocus: (x: { fileName?: string focus: string | null id: string }) => void - resetFocus: () => void }>({ props: null!, setFocus: () => {}, - resetFocus: () => {}, }) export function Section({ @@ -48,16 +45,21 @@ export function Section({ const { selectedId, ...rest } = state return ( - -
{children}
-
+
+ + + {children} + + +
) } @@ -83,10 +85,10 @@ export function SectionLink({ file?: string children: React.ReactNode }) { - const { setFocus, resetFocus, selectedId } = - React.useContext(SectionContext) + const { activate, reset, activatedId } = + React.useContext(LinkableContext) - const isSelected = selectedId === id + const isSelected = activatedId === id // const handleClick = isSelected // ? resetFocus // : () => setFocus({ fileName: file, focus, id }) @@ -96,7 +98,7 @@ export function SectionLink({ style={{ textDecoration: "underline", textDecorationStyle: "dotted", - cursor: "pointer", + // cursor: "pointer", backgroundColor: isSelected ? "#bae6fd66" : undefined, @@ -104,9 +106,66 @@ export function SectionLink({ // onClick={handleClick} children={children} onMouseOver={() => - setFocus({ fileName: file, focus, id }) + activate({ fileName: file, focus, id }) } - onMouseOut={resetFocus} + onMouseOut={reset} /> ) } + +// --- + +const LinkableContext = React.createContext<{ + activate: (x: { + fileName?: string + focus: string | null + id: string + }) => void + reset: () => void + activatedId: string | undefined +}>({ + activatedId: undefined, + activate: () => {}, + reset: () => {}, +}) + +export function LinkableSection({ + onActivation, + onReset, + children, +}: { + onActivation: (x: { + fileName?: string + focus: string | null + id: string + }) => void + onReset: () => void + children: React.ReactNode +}) { + const [activatedId, setActivatedId] = + React.useState(undefined) + + const activate = React.useCallback( + x => { + setActivatedId(x.id) + onActivation(x) + }, + [onActivation] + ) + const reset = React.useCallback(() => { + setActivatedId(undefined) + onReset() + }, [onReset]) + + return ( + + {children} + + ) +} diff --git a/packages/mdx/src/plugin/scrollycoding.ts b/packages/mdx/src/plugin/scrollycoding.ts index 1efc4f9b..648ac4f0 100644 --- a/packages/mdx/src/plugin/scrollycoding.ts +++ b/packages/mdx/src/plugin/scrollycoding.ts @@ -6,6 +6,7 @@ import { import { Node, Parent } from "unist" import { extractStepsInfo } from "./steps" import { getPresetConfig } from "./preview" +import { transformLinks } from "./section" export async function transformScrollycodings( tree: Node, @@ -35,6 +36,8 @@ async function transformScrollycoding( (node as any).attributes ) + transformLinks(node) + toJSX(node, { props: { codeConfig: CH_CODE_CONFIG_PLACEHOLDER, diff --git a/packages/mdx/src/plugin/section.ts b/packages/mdx/src/plugin/section.ts index 9661470b..2f06fb83 100644 --- a/packages/mdx/src/plugin/section.ts +++ b/packages/mdx/src/plugin/section.ts @@ -46,7 +46,7 @@ async function transformSection( } } -function transformLinks(tree: Node) { +export function transformLinks(tree: Node) { visit(tree, "link", (linkNode: any) => { const url = decodeURI(linkNode["url"]) if (url.startsWith("focus://")) { diff --git a/packages/playground/content/scrollycoding.mdx b/packages/playground/content/scrollycoding.mdx index 40bb91a5..5647caed 100644 --- a/packages/playground/content/scrollycoding.mdx +++ b/packages/playground/content/scrollycoding.mdx @@ -1,124 +1,135 @@ -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. +_Based on [Next.js official docs](https://nextjs.org/docs/advanced-features/using-mdx)._ - +Start by installing next and react on an empty directory: -## Step 1 +```bash +npm install next react react-dom +``` -Lorem ipsum dolor sit amet, consectetur adipiscing something about points, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. + -Venenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. Gravida in fermentum et sollicitudin ac orci phasellus egestas tellus. Volutpat consequat mauris nunc congue nisi vitae. +Then also install the [mdx plugin for next](focus://1[13:21]), the [mdx loader](focus://1[23:36]), and [Code Hike](focus://1[38:56]). -Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. +```bash +npm install @next/mdx @mdx-js/loader @code-hike/mdx@next +``` -```jsx App.jsx -import { BasisCurve } from "curve" + -const points = [ - [10, 90], - [70, 10], - [130, 80], - [190, 20], -] +
-export default function App() { - return ( - - - - ) -} + + +Create a `next.config.js` file at the root of your project. + +Here we use the [_`@next/mdx`_](focus://1,6,8) plugin to set up MDX imports. + +Also, make sure you include `"md"` and `"mdx"` on the _`pageExtensions`_ setting. + +After this step, you can use MDX files in your project, but you can't use Code Hike yet. + +{/* prettier-ignore */} +```js next.config.js +const withMDX = require("@next/mdx")({ + extension: /\.mdx?$/, + options: { + remarkPlugins: [], + }, +}) + +module.exports = withMDX({ + pageExtensions: [ + "ts", "tsx", "js", + "jsx", "md", "mdx" + ], +}) ``` --- -## Step 2 +To set up Code Hike you need to [import the @code-hike/mdx plugin](focus://1,6[1:6]), and add it to the remarkPlugins array in the next.config.js file. + +You will also want to import a theme. You can import one from shiki, or make a custom one. + +Pass the theme into Code Hike's config object, there are a few more settings you can use, like lineNumbers for example. -Venenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. Gravida in fermentum et sollicitudin ac orci phasellus egestas tellus. Volutpat consequat mauris nunc congue nisi vitae. +{/* prettier-ignore */} +```js next.config.js focus=1:2,7:9 +const { remarkCH } = require("@code-hike/mdx") +const theme = require("shiki/themes/nord.json") -Praesent elementum facilisis leo vel fringilla est ullamcorper eget. +const withMDX = require("@next/mdx")({ + extension: /\.mdx?$/, + options: { + remarkPlugins: [ + [remarkCH, { theme }] + ], + }, +}) -Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. +module.exports = withMDX({ + pageExtensions: [ + "ts", "tsx", "js", + "jsx", "md", "mdx" + ], +}) +``` -Morbi quis commodo. +--- -```jsx App.jsx -import { NaturalCurve } from "curve" +Then you need to create a `pages/_app.js` file if you don't have one. -const points = [ - [10, 90], - [70, 10], - [130, 80], - [190, 20], -] +You can find more information about the `_app.js` file in the [Next.js official docs](https://nextjs.org/docs/advanced-features/custom-app). -export default function App() { - return ( - - - - ) +{/* prettier-ignore */} +```js pages/_app.js +function MyApp({ Component, pageProps }) { + return } + +export default MyApp ``` --- -## Step 3 +The pages/\_app.js file is where you add global stylesheets in Next.js. + +Here we need to import Code Hike's CSS. -Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. +If you want to customize Code Hike's styles with a global stylesheet make sure to import it after this import to avoid specificity issues. -```jsx App.jsx +{/* prettier-ignore */} +```js pages/_app.js focus=1 +import "@code-hike/mdx/dist/index.css" +function MyApp({ Component, pageProps }) { + return +} + +export default MyApp ``` --- -## Step 4 - -Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. - -```jsx App.jsx -import { NaturalCurve } from "curve" - -const points = [ - [10, 90], - [70, 10], - [130, 80], - [190, 20], -] - -export default function App() { - return ( - - - - ) -} +Now you can create mdx files using codehike. + +Markdown (.md) files should also work. + +{/* prettier-ignore */} +~~~md pages/my.mdx +# Hello + +Lorem ipsum dolor sit amet. + +```python hello.py +print("Rendered with Code Hike") ``` - +Lorem ipsum dolor sit amet. +~~~ -Lorem ipsum dolor sit amet, consectetur adipiscing something about points, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. + -Venenatis cras sed felis eget velit. Consectetur libero id faucibus nisl tincidunt. Gravida in fermentum et sollicitudin ac orci phasellus egestas tellus. Volutpat consequat mauris nunc congue nisi vitae. +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. -Id aliquet risus feugiat in ante metus dictum at tempor. Sed blandit libero volutpat sed cras. Sed odio morbi quis commodo odio aenean sed adipiscing. Velit euismod in pellentesque massa placerat. Mi bibendum neque egestas congue quisque egestas diam in arcu. Nisi lacus sed viverra tellus in. Nibh cras pulvinar mattis nunc sed. Luctus accumsan tortor posuere ac ut consequat semper viverra. Fringilla ut morbi tincidunt augue interdum velit euismod. +Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. At imperdiet dui accumsan sit amet nulla facilities morbi tempus. Praesent elementum facilisis leo vel fringilla. Congue mauris rhoncus aenean vel. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. diff --git a/packages/playground/content/test.mdx b/packages/playground/content/test.mdx index bb652611..789f89b4 100644 --- a/packages/playground/content/test.mdx +++ b/packages/playground/content/test.mdx @@ -1,14 +1,12 @@ -```js lorem.js -// mark[1:8] -function lorem(ipsum, dolor = 1) { - // mark[24:32] - const sit = ipsum == undefined - // mark[8:16] - if ("consectur") { - // mark[20:27] - return ipsum + 12345678 - } - // mark[3:12] - adipiscing(dolor + amet) -} + + +Then also install the [mdx plugin for next](focus://2),[mdx](focus://3),[foo bar](focus://4). + +```bash focus=1 +npm install +@next/mdx @mdx-js/loader +@code-hike/ +mdx@next ``` + + From c14b4ba1da93db781fb2f6811e6b9c6eb81c936b Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 18:50:40 +0000 Subject: [PATCH 05/10] Transform emphasis code into InlineCode --- packages/mdx/src/index.scss | 4 ++-- packages/mdx/src/plugin/inline-code.ts | 16 ++++++++++++++++ packages/playground/content/test.mdx | 21 ++++++++++++--------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/mdx/src/index.scss b/packages/mdx/src/index.scss index 24a15305..f2385f4c 100644 --- a/packages/mdx/src/index.scss +++ b/packages/mdx/src/index.scss @@ -27,8 +27,8 @@ } .ch-inline-code > code { - padding: 0.2em 0.4em; - margin: 0.1em -0.1em; + padding: 0.2em 0.15em; + margin: 0.1em -0.05em; border-radius: 0.25em; font-size: 0.9em; } diff --git a/packages/mdx/src/plugin/inline-code.ts b/packages/mdx/src/plugin/inline-code.ts index 62c062a3..ec97124a 100644 --- a/packages/mdx/src/plugin/inline-code.ts +++ b/packages/mdx/src/plugin/inline-code.ts @@ -4,8 +4,24 @@ import { CH_CODE_CONFIG_PLACEHOLDER, } from "./unist-utils" import { Node } from "unist" +import visit from "unist-util-visit" +import { Parent } from "hast-util-to-estree" export async function transformInlineCodes(tree: Node) { + visit(tree, "emphasis", (node: Parent) => { + if ( + node.children && + node.children.length === 1 && + node.children[0].type === "inlineCode" + ) { + node.type = "mdxJsxTextElement" + node.name = "CH.InlineCode" + node.children = [ + { type: "text", value: node.children[0].value }, + ] + } + }) + await visitAsync( tree, ["mdxJsxFlowElement", "mdxJsxTextElement"], diff --git a/packages/playground/content/test.mdx b/packages/playground/content/test.mdx index 789f89b4..4602d195 100644 --- a/packages/playground/content/test.mdx +++ b/packages/playground/content/test.mdx @@ -1,12 +1,15 @@ - +Lorem ipsum dolor sit amet. _`console.log("foo bar")`_. -Then also install the [mdx plugin for next](focus://2),[mdx](focus://3),[foo bar](focus://4). +Lorem ipsum dolor sit amet. `console.log("foo bar")`. -```bash focus=1 -npm install -@next/mdx @mdx-js/loader -@code-hike/ -mdx@next -``` +_`console.log("foo bar")`_ - +_`console.log("foo bar")`_ + +X console.log("foo bar") + +Lorem ipsum `var x = 1` + +_Lorem ipsum `var x = 1`_ + +**`console.log("foo bar")`** From 9fcdf18ed5173fa14c4bd80acbb3eca4a6acaa0e Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 19:11:37 +0000 Subject: [PATCH 06/10] Highlight inline code --- packages/mdx/src/client/inline-code.tsx | 11 ++++++++++- packages/mdx/src/plugin.ts | 12 +++++++----- packages/mdx/src/plugin/inline-code.ts | 15 +++++++++++++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/mdx/src/client/inline-code.tsx b/packages/mdx/src/client/inline-code.tsx index 2341b02c..340351a4 100644 --- a/packages/mdx/src/client/inline-code.tsx +++ b/packages/mdx/src/client/inline-code.tsx @@ -4,19 +4,24 @@ import { getColor, transparent, ColorName, + Code, } from "@code-hike/utils" export function InlineCode({ className, codeConfig, children, + code, ...rest }: { className: string + code: Code children?: React.ReactNode codeConfig: { theme: EditorTheme } }) { const { theme } = codeConfig + const { lines } = code + const allTokens = lines.flatMap(line => line.tokens) return ( - {children} + {allTokens.map((token, j) => ( + + {token.content} + + ))} ) diff --git a/packages/mdx/src/plugin.ts b/packages/mdx/src/plugin.ts index 5c2851d9..b03685be 100644 --- a/packages/mdx/src/plugin.ts +++ b/packages/mdx/src/plugin.ts @@ -34,7 +34,6 @@ export function remarkCodeHike( } }) - addConfig(tree as Parent, config) if (config.autoImport && !hasCodeHikeImport) { @@ -42,7 +41,7 @@ export function remarkCodeHike( } try { - await transformInlineCodes(tree) + await transformInlineCodes(tree, config) await transformPreviews(tree) await transformScrollycodings(tree, config) await transformSpotlights(tree, config) @@ -60,7 +59,11 @@ export function remarkCodeHike( function addConfigDefaults( config: Partial | undefined ): CodeHikeConfig { - return { ...config, theme: config?.theme || {}, autoImport: config?.autoImport === false ? false : true } + return { + ...config, + theme: config?.theme || {}, + autoImport: config?.autoImport === false ? false : true, + } } function addConfig(tree: Parent, config: CodeHikeConfig) { @@ -124,8 +127,7 @@ function addImportNode(tree: Parent) { type: "Literal", value: "@code-hike/mdx/dist/components.cjs.js", - raw: - '"@code-hike/mdx/dist/components.cjs.js"', + raw: '"@code-hike/mdx/dist/components.cjs.js"', }, }, ], diff --git a/packages/mdx/src/plugin/inline-code.ts b/packages/mdx/src/plugin/inline-code.ts index ec97124a..d20a00b4 100644 --- a/packages/mdx/src/plugin/inline-code.ts +++ b/packages/mdx/src/plugin/inline-code.ts @@ -6,8 +6,13 @@ import { import { Node } from "unist" import visit from "unist-util-visit" import { Parent } from "hast-util-to-estree" +import { highlight } from "@code-hike/highlighter" -export async function transformInlineCodes(tree: Node) { +export async function transformInlineCodes( + tree: Node, + { theme }: { theme: any } +) { + // transform *`foo`* to foo visit(tree, "emphasis", (node: Parent) => { if ( node.children && @@ -25,10 +30,16 @@ export async function transformInlineCodes(tree: Node) { await visitAsync( tree, ["mdxJsxFlowElement", "mdxJsxTextElement"], - async node => { + async (node: Parent) => { if (node.name === "CH.InlineCode") { + const code = await highlight({ + code: node.children[0].value as string, + lang: "jsx", + theme, + }) toJSX(node, { props: { + code, codeConfig: CH_CODE_CONFIG_PLACEHOLDER, }, appendProps: true, From f79d50f535c2a09454b8a4ab9df228cafa1e6c4a Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Mon, 28 Feb 2022 19:39:49 +0000 Subject: [PATCH 07/10] Better link styles --- packages/mdx/src/client/inline-code.tsx | 7 ++++++- packages/mdx/src/client/section.tsx | 14 ++++---------- packages/mdx/src/index.scss | 14 ++++++++++++++ packages/playground/content/scrollycoding.mdx | 2 +- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/mdx/src/client/inline-code.tsx b/packages/mdx/src/client/inline-code.tsx index 340351a4..1bbf355f 100644 --- a/packages/mdx/src/client/inline-code.tsx +++ b/packages/mdx/src/client/inline-code.tsx @@ -22,6 +22,10 @@ export function InlineCode({ const { theme } = codeConfig const { lines } = code const allTokens = lines.flatMap(line => line.tokens) + const foreground = getColor( + theme, + ColorName.CodeForeground + ) return ( {allTokens.map((token, j) => ( diff --git a/packages/mdx/src/client/section.tsx b/packages/mdx/src/client/section.tsx index 49a8ad2c..4f900181 100644 --- a/packages/mdx/src/client/section.tsx +++ b/packages/mdx/src/client/section.tsx @@ -74,6 +74,8 @@ export function SectionCode() { return } +// --- + export function SectionLink({ focus, file, @@ -95,14 +97,8 @@ export function SectionLink({ return ( @@ -113,8 +109,6 @@ export function SectionLink({ ) } -// --- - const LinkableContext = React.createContext<{ activate: (x: { fileName?: string diff --git a/packages/mdx/src/index.scss b/packages/mdx/src/index.scss index f2385f4c..4778b820 100644 --- a/packages/mdx/src/index.scss +++ b/packages/mdx/src/index.scss @@ -32,3 +32,17 @@ border-radius: 0.25em; font-size: 0.9em; } + +.ch-section-link, +.ch-section-link * { + text-decoration: underline; + text-decoration-style: dotted; + text-decoration-color: var( + --ch-code-foreground, + currentColor + ); +} + +.ch-section-link[data-active="true"] { + background-color: #bae6fd66; +} diff --git a/packages/playground/content/scrollycoding.mdx b/packages/playground/content/scrollycoding.mdx index 5647caed..5affd0a0 100644 --- a/packages/playground/content/scrollycoding.mdx +++ b/packages/playground/content/scrollycoding.mdx @@ -22,7 +22,7 @@ npm install @next/mdx @mdx-js/loader @code-hike/mdx@next Create a `next.config.js` file at the root of your project. -Here we use the [_`@next/mdx`_](focus://1,6,8) plugin to set up MDX imports. +Here we use the [_`require("@next/mdx")`_](focus://1,6,8) plugin to set up MDX imports. Also, make sure you include `"md"` and `"mdx"` on the _`pageExtensions`_ setting. From 9c91385661c93b7b2af0a0dd8b550013ff519bf1 Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Tue, 1 Mar 2022 10:17:01 +0000 Subject: [PATCH 08/10] Use tokens from editor step --- packages/mdx/package.json | 3 +- packages/mdx/src/index.scss | 7 +- packages/mdx/src/plugin.ts | 2 +- packages/mdx/src/plugin/inline-code.ts | 155 +++++++++++++++++++++++- packages/mdx/src/plugin/section.ts | 13 +- packages/mdx/src/plugin/steps.tsx | 13 +- packages/playground/content/section.mdx | 3 + 7 files changed, 175 insertions(+), 21 deletions(-) diff --git a/packages/mdx/package.json b/packages/mdx/package.json index c53f9c98..d93cc23f 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -34,7 +34,8 @@ "node-fetch": "^2.0.0", "remark-rehype": "^8.1.0", "unified": "^9.2.2", - "unist-util-visit": "^2.0.0" + "unist-util-visit": "^2.0.0", + "unist-util-visit-parents": "^2.0.0" }, "peerDependencies": { "react": "^16.8.3 || ^17 || ^18" diff --git a/packages/mdx/src/index.scss b/packages/mdx/src/index.scss index 4778b820..c394e97c 100644 --- a/packages/mdx/src/index.scss +++ b/packages/mdx/src/index.scss @@ -37,12 +37,17 @@ .ch-section-link * { text-decoration: underline; text-decoration-style: dotted; + text-decoration-thickness: 1px; text-decoration-color: var( --ch-code-foreground, currentColor ); } - .ch-section-link[data-active="true"] { background-color: #bae6fd66; } + +.ch-section-link[data-active="true"], +.ch-section-link[data-active="true"] * { + text-decoration-thickness: 1.5px; +} diff --git a/packages/mdx/src/plugin.ts b/packages/mdx/src/plugin.ts index b03685be..0447ad85 100644 --- a/packages/mdx/src/plugin.ts +++ b/packages/mdx/src/plugin.ts @@ -41,12 +41,12 @@ export function remarkCodeHike( } try { - await transformInlineCodes(tree, config) await transformPreviews(tree) await transformScrollycodings(tree, config) await transformSpotlights(tree, config) await transformSlideshows(tree, config) await transformSections(tree, config) + await transformInlineCodes(tree, config) await transformEditorNodes(tree, config) await transformCodeNodes(tree, config) } catch (e) { diff --git a/packages/mdx/src/plugin/inline-code.ts b/packages/mdx/src/plugin/inline-code.ts index d20a00b4..f5b34e1e 100644 --- a/packages/mdx/src/plugin/inline-code.ts +++ b/packages/mdx/src/plugin/inline-code.ts @@ -5,8 +5,11 @@ import { } from "./unist-utils" import { Node } from "unist" import visit from "unist-util-visit" +import visitParents from "unist-util-visit-parents" import { Parent } from "hast-util-to-estree" import { highlight } from "@code-hike/highlighter" +import { EditorStep } from "@code-hike/mini-editor" +import { Code } from "@code-hike/utils" export async function transformInlineCodes( tree: Node, @@ -32,14 +35,18 @@ export async function transformInlineCodes( ["mdxJsxFlowElement", "mdxJsxTextElement"], async (node: Parent) => { if (node.name === "CH.InlineCode") { - const code = await highlight({ - code: node.children[0].value as string, - lang: "jsx", - theme, - }) + const inlinedCode = node.children[0].value as string + const lang = node.attributes?.lang + toJSX(node, { props: { - code, + code: await getCode( + tree, + node, + inlinedCode, + lang, + theme + ), codeConfig: CH_CODE_CONFIG_PLACEHOLDER, }, appendProps: true, @@ -48,3 +55,139 @@ export async function transformInlineCodes( } ) } + +async function getCode( + tree: Node, + node: Parent, + inlinedCode: string, + lang: string | undefined, + theme: any +) { + const ancestors = getAncestors(tree, node) + const sectionNode = ancestors.find( + n => n.data?.editorStep + ) + + // if node isn't inside a section-like parent, use provided lang or "jsx" + if (!sectionNode) { + return await highlight({ + code: inlinedCode, + lang: lang || "jsx", + theme, + }) + } + + const editorStep = sectionNode.data + .editorStep as any as EditorStep + + // if the same code is present in the editor step, use it + const existingCode = getExistingCode( + editorStep.files, + inlinedCode + ) + + if (existingCode) { + return existingCode + } + + // or else, try to guess the language from somewhere + const activeFile = + editorStep.files?.find( + f => f.name === editorStep.northPanel?.active + ) || editorStep.files[0] + + const activeLang = activeFile?.code?.lang + + return await highlight({ + code: inlinedCode, + lang: lang || activeLang || "jsx", + theme, + }) +} + +function getAncestors(tree: Node, node: Node): Parent[] { + let ancestors: Parent[] = [] + visitParents(tree, node, (node, nodeAncestors) => { + ancestors = nodeAncestors + }) + return ancestors +} + +function getExistingCode( + files: EditorStep["files"] | undefined, + inlinedCode: string +): Code | undefined { + if (!files) { + return undefined + } + + for (const file of files) { + for (const line of file.code.lines) { + const lineContent = line.tokens + .map(t => t.content) + .join("") + const index = lineContent.indexOf(inlinedCode) + if (index !== -1) { + const tokens = sliceTokens( + line, + index, + inlinedCode.length + ) + return { lang: file.code.lang, lines: [{ tokens }] } + } + } + } + return undefined +} + +function sliceTokens( + line: Code["lines"][0], + start: number, + length: number +) { + const tokens = line.tokens + let currentLength = 0 + + let headTokens = [] as Code["lines"][0]["tokens"] + + for (let i = 0; i < tokens.length; i++) { + if (currentLength === start) { + headTokens = tokens.slice(i) + break + } + if (currentLength + tokens[i].content.length > start) { + const newToken = { + ...tokens[i], + content: tokens[i].content.slice( + start - currentLength + ), + } + headTokens = [newToken].concat(tokens.slice(i + 1)) + break + } + currentLength += tokens[i].content.length + } + + currentLength = 0 + for (let i = 0; i < headTokens.length; i++) { + if (currentLength === length) { + return headTokens.slice(0, i) + } + if ( + currentLength + headTokens[i].content.length > + length + ) { + const newToken = { + ...headTokens[i], + content: headTokens[i].content.slice( + 0, + length - currentLength + ), + } + + return headTokens.slice(0, i).concat([newToken]) + } + currentLength += headTokens[i].content.length + } + return [] +} diff --git a/packages/mdx/src/plugin/section.ts b/packages/mdx/src/plugin/section.ts index 2f06fb83..39be3ea6 100644 --- a/packages/mdx/src/plugin/section.ts +++ b/packages/mdx/src/plugin/section.ts @@ -26,17 +26,20 @@ async function transformSection( await visitAsync( node, ["mdxJsxFlowElement", "code"], - async (node, index, parent) => { - if (isEditorNode(node)) { + async (editorNode, index, parent) => { + if (isEditorNode(editorNode)) { props = await mapAnyCodeNode( - { node, index, parent: parent! }, + { node: editorNode, index, parent: parent! }, config ) - toJSX(node, { name: "CH.SectionCode", props: {} }) + toJSX(editorNode, { + name: "CH.SectionCode", + props: {}, + }) } } ) - + node.data = { editorStep: props } transformLinks(node) if (props) { diff --git a/packages/mdx/src/plugin/steps.tsx b/packages/mdx/src/plugin/steps.tsx index 13b62386..952ae170 100644 --- a/packages/mdx/src/plugin/steps.tsx +++ b/packages/mdx/src/plugin/steps.tsx @@ -29,13 +29,11 @@ export async function extractStepsInfo( steps[stepIndex] = steps[stepIndex] || { children: [] } const step = steps[stepIndex] if (!step.editorStep && isEditorNode(child)) { - const { - codeConfig, - ...editorStep - } = await mapAnyCodeNode( - { node: child, parent, index: i }, - config - ) + const { codeConfig, ...editorStep } = + await mapAnyCodeNode( + { node: child, parent, index: i }, + config + ) if (stepIndex === 0) { // for the header props, keep it as it is @@ -58,6 +56,7 @@ export async function extractStepsInfo( return { type: "mdxJsxFlowElement", children: step.children, + data: { editorStep: step.editorStep }, } }) diff --git a/packages/playground/content/section.mdx b/packages/playground/content/section.mdx index 13fdc784..e18218d4 100644 --- a/packages/playground/content/section.mdx +++ b/packages/playground/content/section.mdx @@ -5,6 +5,7 @@ Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor [incididunt](focus://4:7) ut labore et dolore magna aliqua. Praesent elementum facilisis leo vel fringilla est ullamcorper eget. +_`orem`_, _`sum`_ ```js function lorem(ipsum, dolor) { @@ -45,4 +46,6 @@ function lorem(ipsum, dolor) { +_`ackground-color: va`_ + From 2125f571c23cfda7129d77b688e5db7df694cc52 Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Tue, 1 Mar 2022 10:42:23 +0000 Subject: [PATCH 09/10] Update uarn lock --- yarn.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/yarn.lock b/yarn.lock index 0a5e914a..3c9ffd45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18379,6 +18379,11 @@ unist-util-generated@^2.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== +unist-util-is@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" + integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== + unist-util-is@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.3.tgz#e8b44db55fc20c43752b3346c116344d45d7c91d" @@ -18442,6 +18447,13 @@ unist-util-stringify-position@^3.0.0: dependencies: "@types/unist" "^2.0.0" +unist-util-visit-parents@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" + integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== + dependencies: + unist-util-is "^3.0.0" + unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" From 45d5e45f79085154ca0b49cf4721bbe0a24ace93 Mon Sep 17 00:00:00 2001 From: Rodrigo Pombo Date: Tue, 1 Mar 2022 10:57:26 +0000 Subject: [PATCH 10/10] Fix dependency --- packages/mdx/package.json | 2 +- yarn.lock | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/mdx/package.json b/packages/mdx/package.json index d93cc23f..aec680c0 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -35,7 +35,7 @@ "remark-rehype": "^8.1.0", "unified": "^9.2.2", "unist-util-visit": "^2.0.0", - "unist-util-visit-parents": "^2.0.0" + "unist-util-visit-parents": "^3.0.0" }, "peerDependencies": { "react": "^16.8.3 || ^17 || ^18" diff --git a/yarn.lock b/yarn.lock index 3c9ffd45..0a5e914a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18379,11 +18379,6 @@ unist-util-generated@^2.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== -unist-util-is@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" - integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== - unist-util-is@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.0.3.tgz#e8b44db55fc20c43752b3346c116344d45d7c91d" @@ -18447,13 +18442,6 @@ unist-util-stringify-position@^3.0.0: dependencies: "@types/unist" "^2.0.0" -unist-util-visit-parents@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" - integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== - dependencies: - unist-util-is "^3.0.0" - unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6"