feat(nx-dev): update terminal markdown embeds
This commit is contained in:
parent
05cbce0de8
commit
9610fdd90c
@ -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.
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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);
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
55
nx-dev/ui-markdoc/src/lib/tags/svg-animation.component.tsx
Normal file
55
nx-dev/ui-markdoc/src/lib/tags/svg-animation.component.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
33
nx-dev/ui-markdoc/src/lib/tags/terminal-video.component.tsx
Normal file
33
nx-dev/ui-markdoc/src/lib/tags/terminal-video.component.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user