feat(nx-dev): update terminal markdown embeds

This commit is contained in:
Juri 2023-07-31 16:11:46 +02:00 committed by Juri Strumpflohner
parent 05cbce0de8
commit 9610fdd90c
12 changed files with 239 additions and 120 deletions

View File

@ -67,6 +67,16 @@ You can add specific languages and a filename on the code snippet displayed.
```
````
#### Terminal command
To display a terminal command, use:
````
```shell
npx nx build
```
````
#### Terminal Output
You can display your terminal output with a dedicated component the same way you would show code.
@ -85,6 +95,14 @@ You can optionally also pass a `path` like
```
````
#### Terminal Video Output
You can have a more dynamic visualization of a terminal output by using the following component:
```
{% terminal-video src="/documentation/shared/images/caching/cache-terminal-animation.mp4" /%}
```
#### Custom iframes
We can display a special iframe and setting its width inside the document.

View File

@ -5,24 +5,30 @@ Create a new Nx workspace using the following command:
{% tabs %}
{% tab label="npm" %}
{% terminal-command command="npx create-nx-workspace" /%}
```shell
npx create-nx-workspace
```
{% /tab %}
{% tab label="yarn" %}
{% terminal-command command="npx create-nx-workspace --pm yarn" /%}
```shell
npx create-nx-workspace --pm yarn
```
{% /tab %}
{% tab label="pnpm" %}
{% terminal-command command="npx create-nx-workspace --pm pnpm" /%}
```shell
npx create-nx-workspace --pm pnpm
```
{% /tab %}
{% /tabs %}
This will guide you through the setup, asking whether you want a monorepo or a standalone app and whether you want to start with a blank or a preconfigured setup.
```{% command="npx create-nx-workspace" %}
```{% command="npx create-nx-workspace" path="~" %}
> NX Let's create a new workspace [https://nx.dev/getting-started/intro]
@ -48,7 +54,9 @@ Learn more about [running tasks](/core-features/run-tasks).
If you want to add Nx to an existing repository run:
{% terminal-command command="npx nx@latest init" /%}
```shell
npx nx@latest init
```
You can also manually install the nx NPM package and create a [nx.json](https://nx.dev/reference/nx-json) to configure it. Learn more about [adopting Nx in an existing project](/recipes/adopting-nx)
@ -59,17 +67,23 @@ You can install Nx globally. Depending on your package manager, use one of the f
{% tabs %}
{% tab label="npm" %}
{% terminal-command command="npm install --global nx@latest" /%}
```shell
npm install --global nx@latest
```
{% /tab %}
{% tab label="yarn" %}
{% terminal-command command="yarn global add nx@latest" /%}
```shell
yarn global add nx@latest
```
{% /tab %}
{% tab label="pnpm" %}
{% terminal-command command="pnpm install --global nx@latest" /%}
```shell
pnpm install --global nx@latest
```
{% /tab %}
{% /tabs %}

View File

@ -7,17 +7,23 @@ If instead you want to jump right into it, run the following command. It will gu
{% tabs %}
{% tab label="npm" %}
{% terminal-command command="npx create-nx-workspace" /%}
```shell
npx create-nx-workspace
```
{% /tab %}
{% tab label="yarn" %}
{% terminal-command command="npx create-nx-workspace --pm yarn" /%}
```shell
npx create-nx-workspace --pm yarn
```
{% /tab %}
{% tab label="pnpm" %}
{% terminal-command command="npx create-nx-workspace --pm pnpm" /%}
```shell
npx create-nx-workspace --pm pnpm
```
{% /tab %}
{% /tabs %}
@ -92,7 +98,9 @@ A modern Node server with scaffolding for Express, Fastify or Koa. There's also
If you have an existing project and want to adopt Nx or migrate to Nx just run the following command which guides you through the migration process:
{% terminal-command command="npx nx@latest init" /%}
```shell
npx nx@latest init
```
Alternatively, here are some recipes that give you more details based on the technology stack you're using:

View File

@ -39,9 +39,9 @@ import { Tab, Tabs } from './lib/tags/tabs.component';
import { tab, tabs } from './lib/tags/tabs.schema';
import { YouTube, youtube } from './lib/tags/youtube.component';
import {
TerminalCommand,
terminalCommand,
} from './lib/tags/terminal-command.component';
TerminalVideo,
terminalVideo,
} from './lib/tags/terminal-video.component';
import { VideoLink, videoLink } from './lib/tags/video-link.component';
// TODO fix this export
@ -74,7 +74,7 @@ export const getMarkdocCustomConfig = (
tabs,
'title-card': titleCard,
youtube,
'terminal-command': terminalCommand,
'terminal-video': terminalVideo,
'video-link': videoLink,
},
},
@ -98,7 +98,7 @@ export const getMarkdocCustomConfig = (
Tabs,
TitleCard,
YouTube,
TerminalCommand,
TerminalVideo,
VideoLink,
},
});

View File

@ -27,9 +27,18 @@ function CodeWrapper(options: {
command: string;
path: string;
isMessageBelow: boolean;
language: string;
children: string; // intentionally typed as such
}): ({ children }: { children: ReactNode }) => JSX.Element {
return ({ children }: { children: ReactNode }) =>
options.command ? (
options.language === 'shell' ? (
<TerminalOutput
command={options.children}
path=""
content={null}
isMessageBelow={false}
/>
) : options.command ? (
<TerminalOutput
content={children}
command={options.command}
@ -51,12 +60,14 @@ export function Fence({
path,
fileName,
language,
enableCopy,
}: {
children: string;
command: string;
path: string;
fileName: string;
language: string;
enableCopy: boolean;
}) {
const [copied, setCopied] = useState(false);
useEffect(() => {
@ -76,23 +87,25 @@ export function Fence({
<div className="my-8 w-full">
<div className="code-block group relative w-full">
<div>
<CopyToClipboard
text={command && command !== '' ? command : children}
onCopy={() => {
setCopied(true);
}}
>
<button
type="button"
className="not-prose absolute top-0 right-0 z-10 flex rounded-tr-lg border border-slate-200 bg-slate-50/50 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60"
{enableCopy && enableCopy === true && (
<CopyToClipboard
text={command && command !== '' ? command : children}
onCopy={() => {
setCopied(true);
}}
>
{copied ? (
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
) : (
<ClipboardDocumentIcon className="h-5 w-5" />
)}
</button>
</CopyToClipboard>
<button
type="button"
className="not-prose absolute top-0 right-0 z-10 flex rounded-tr-lg border border-slate-200 bg-slate-50/50 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60"
>
{copied ? (
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
) : (
<ClipboardDocumentIcon className="h-5 w-5" />
)}
</button>
</CopyToClipboard>
)}
<SyntaxHighlighter
useInlineStyles={false}
language={resolveLanguage(language)}
@ -102,6 +115,8 @@ export function Fence({
command,
path,
isMessageBelow: showRescopeMessage,
language,
children,
})}
/>
{showRescopeMessage && (

View File

@ -9,6 +9,7 @@ export const fence: Schema = {
command: { type: 'String', default: '' },
path: { type: 'String', default: '~/workspace' },
process: { type: 'Boolean', render: false, default: true },
enableCopy: { type: 'Boolean', default: true },
},
transform(node, config) {
const attributes = node.transformAttributes(config);

View File

@ -1,6 +1,5 @@
import { cx } from '@nx/nx-dev/ui-primitives';
import { ReactNode } from 'react';
import { VideoLoop } from '../../tags/video-loop.component';
import { TerminalShellWrapper } from './terminal-shell.component';
export function TerminalOutput({
content,
@ -13,44 +12,22 @@ export function TerminalOutput({
isMessageBelow: boolean;
path: string;
}): JSX.Element {
const isVideo = command.indexOf('.mp4') > -1;
return (
<div
className={cx(
'hljs not-prose w-full overflow-x-auto border border-slate-200 bg-slate-50/50 font-mono text-sm dark:border-slate-700 dark:bg-slate-800/60',
isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg'
)}
>
<div className="relative flex justify-center p-2 border-b border-slate-200 bg-slate-100/50 text-slate-400 dark:border-slate-700 dark:bg-slate-700/50 dark:text-slate-500">
<div className="absolute flex items-center gap-2 left-2 top-3">
<span className="w-2 h-2 bg-red-400 rounded-full dark:bg-red-600" />
<span className="w-2 h-2 bg-yellow-400 rounded-full dark:bg-yellow-600" />
<span className="w-2 h-2 bg-green-400 rounded-full dark:bg-green-600" />
<TerminalShellWrapper isMessageBelow={isMessageBelow}>
<div className="p-4 pt-2 overflow-x-auto">
<div className="flex items-center">
<p className="mt-0.5">
{path && (
<span className="text-purple-600 dark:text-fuchsia-500">
{path}
</span>
)}
<span className="mx-1 text-green-600 dark:text-green-400"></span>
</p>
<p className="typing mt-0.5 flex-1 pl-2">{command}</p>
</div>
<span className="h-5"></span>
<div className="flex not-prose">{content}</div>
</div>
{!isVideo && (
<div className="p-4 pt-2 overflow-x-auto">
<div className="flex items-center">
<p className="mt-0.5">
{path && (
<span className="text-purple-600 dark:text-fuchsia-500">
{path}
</span>
)}
<span className="mx-1 text-green-600 dark:text-green-400"></span>
</p>
<p className="typing mt-0.5 flex-1 pl-2">{command}</p>
</div>
<div className="flex not-prose">{content}</div>
</div>
)}
{isVideo && (
<div className="overflow-x-auto">
<VideoLoop src={command}></VideoLoop>
</div>
)}
</div>
</TerminalShellWrapper>
);
}

View File

@ -0,0 +1,29 @@
import { cx } from '@nx/nx-dev/ui-primitives';
import { ReactNode } from 'react';
export function TerminalShellWrapper({
isMessageBelow,
children,
}: {
isMessageBelow: boolean;
children: ReactNode;
}): JSX.Element {
return (
<div
className={cx(
'hljs not-prose w-full overflow-x-auto border border-slate-200 bg-slate-50/50 font-mono text-sm dark:border-slate-700 dark:bg-slate-800/60',
isMessageBelow ? 'rounded-t-lg border-b-0' : 'rounded-lg'
)}
>
<div className="relative flex justify-center p-2 border-b border-slate-200 bg-slate-100/50 text-slate-400 dark:border-slate-700 dark:bg-slate-700/50 dark:text-slate-500">
<div className="absolute flex items-center gap-2 left-2 top-3">
<span className="w-2 h-2 bg-red-400 rounded-full dark:bg-red-600" />
<span className="w-2 h-2 bg-yellow-400 rounded-full dark:bg-yellow-600" />
<span className="w-2 h-2 bg-green-400 rounded-full dark:bg-green-600" />
</div>
<span className="h-5"></span>
</div>
{children}
</div>
);
}

View File

@ -0,0 +1,55 @@
import { Schema } from '@markdoc/markdoc';
import { useEffect, useRef } from 'react';
export const svgAnimation: Schema = {
render: 'SvgAnimation',
attributes: {
src: {
type: 'String',
required: true,
},
title: {
type: 'String',
required: true,
},
},
};
export function SvgAnimation({
src,
title,
}: {
src: string;
title: string;
}): JSX.Element {
const objectRef = useRef<HTMLObjectElement>(null);
useEffect(() => {
if (objectRef.current) {
fetch(src)
.then((response) => response.text())
.then((svgContent) => {
objectRef.current!.data = `data:image/svg+xml,${encodeURIComponent(
svgContent
)}`;
})
.catch((error) => {
console.error('Error fetching SVG:', error);
});
}
}, [src]);
return (
<div className="w-full rounded-md border border-slate-100 bg-slate-50/20 p-4 dark:border-slate-800 dark:bg-slate-800/60">
<object
ref={objectRef}
type="image/svg+xml"
title={title}
className="rounded-sm bg-white p-2 ring-1 ring-slate-50 dark:bg-slate-800/80 dark:ring-slate-800"
>
{title}
</object>
</div>
);
}

View File

@ -1,33 +0,0 @@
import { Schema } from '@markdoc/markdoc';
import { TerminalOutput } from '../nodes/fences/terminal-output.component';
export const terminalCommand: Schema = {
render: 'TerminalCommand',
attributes: {
command: {
type: 'String',
required: true,
},
path: {
type: 'String',
required: false,
},
},
};
export function TerminalCommand({
command,
path,
}: {
command: string;
path: string;
}): JSX.Element {
return (
<TerminalOutput
command={command}
path={path}
content={null}
isMessageBelow={false}
/>
);
}

View File

@ -0,0 +1,33 @@
import { VideoLoop } from './video-loop.component';
import { Schema } from '@markdoc/markdoc';
import { TerminalShellWrapper } from '../nodes/fences/terminal-shell.component';
export const terminalVideo: Schema = {
render: 'TerminalVideo',
attributes: {
src: {
type: 'String',
required: true,
},
alt: {
type: 'String',
required: true,
},
},
};
export function TerminalVideo({
src,
alt,
}: {
src: string;
alt: string;
}): JSX.Element {
return (
<TerminalShellWrapper isMessageBelow={false}>
<div className="overflow-x-auto">
<VideoLoop src={src} alt={alt}></VideoLoop>
</div>
</TerminalShellWrapper>
);
}

View File

@ -1,22 +1,17 @@
import { Schema } from '@markdoc/markdoc';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
export const videoLoop: Schema = {
render: 'VideoLoop',
attributes: {
src: {
type: 'String',
required: true,
},
},
};
export function VideoLoop({ src }: { src: string }): JSX.Element {
const videoRef = useRef(null);
export function VideoLoop({
src,
alt,
}: {
src: string;
alt: string;
}): JSX.Element {
const videoRef = useRef<HTMLVideoElement | null>(null);
useEffect(() => {
let observer: IntersectionObserver;
let videoElement = videoRef.current as HTMLVideoElement | null;
let videoElement = videoRef.current;
if (videoElement) {
observer = new IntersectionObserver(
@ -48,6 +43,13 @@ export function VideoLoop({ src }: { src: string }): JSX.Element {
return (
<video ref={videoRef} autoPlay muted loop>
<source src={src} type="video/mp4" />
<div className="text-center p-4">
<p className="font-bold pb-3">
Your browser does not support the video tag. Here is a description of
the video:
</p>
<p>{alt}</p>
</div>
</video>
);
}