From 0561b719f19a5e76c87484ec59f18fecb5f7f937 Mon Sep 17 00:00:00 2001 From: sooniter Date: Wed, 15 Oct 2025 00:24:27 +0800 Subject: [PATCH 1/2] chore: init --- e2e/fixtures/code-block-runtime/index.test.ts | 4 +- e2e/fixtures/code-block/doc/FileTree.mdx | 15 ++ .../doc/index.mdx | 2 +- .../doc/langAlias.mdx | 0 .../index.test.ts | 0 .../{plugin-shiki => code-block}/package.json | 0 .../rspress.config.ts | 0 .../tsconfig.json | 0 .../core/src/node/mdx/rehypePlugins/shiki.ts | 8 +- .../rehypePlugins/transformers/add-lang.ts | 19 ++ .../mdx/rehypePlugins/transformers/index.ts | 1 + .../containerSyntax.test.ts.snap | 2 - .../__snapshots__/fileCodeBlock.test.ts.snap | 1 - .../static/global-styles/twoslash.css | 4 +- .../src/components/Callout/index.scss | 2 +- .../src/components/CodeBlockRuntime/index.tsx | 17 +- .../src/components/DocContent/doc.scss | 6 +- .../codeblock/CodeButtonGroup.scss | 2 +- .../docComponents/codeblock/pre.tsx | 20 +- .../src/components/FileTree/index.scss | 106 +++++++++ .../src/components/FileTree/index.tsx | 213 ++++++++++++++++++ packages/theme-default/src/index.ts | 1 + packages/theme-default/src/styles/shiki.scss | 34 ++- pnpm-lock.yaml | 26 +-- .../components/LiveCodeEditor.module.scss | 2 +- 25 files changed, 415 insertions(+), 70 deletions(-) create mode 100644 e2e/fixtures/code-block/doc/FileTree.mdx rename e2e/fixtures/{plugin-shiki => code-block}/doc/index.mdx (97%) rename e2e/fixtures/{plugin-shiki => code-block}/doc/langAlias.mdx (100%) rename e2e/fixtures/{plugin-shiki => code-block}/index.test.ts (100%) rename e2e/fixtures/{plugin-shiki => code-block}/package.json (100%) rename e2e/fixtures/{plugin-shiki => code-block}/rspress.config.ts (100%) rename e2e/fixtures/{plugin-shiki => code-block}/tsconfig.json (100%) create mode 100644 packages/core/src/node/mdx/rehypePlugins/transformers/add-lang.ts create mode 100644 packages/theme-default/src/components/FileTree/index.scss create mode 100644 packages/theme-default/src/components/FileTree/index.tsx diff --git a/e2e/fixtures/code-block-runtime/index.test.ts b/e2e/fixtures/code-block-runtime/index.test.ts index 851335d7d..91c50293d 100644 --- a/e2e/fixtures/code-block-runtime/index.test.ts +++ b/e2e/fixtures/code-block-runtime/index.test.ts @@ -31,7 +31,7 @@ test.describe('', async () => { await expect(container.locator('.rspress-code-title')).toHaveText( 'test.js', ); - const content = container.locator('.rspress-code-content'); + const content = container.locator('.rp-codeblock__content'); const shikiContainer = content.locator('.shiki.css-variables').first(); await expect(shikiContainer).toHaveJSProperty('tagName', 'PRE'); await expect(shikiContainer.locator('code').first()).toHaveText( @@ -53,7 +53,7 @@ test.describe('', async () => { await expect(container.locator('.rspress-code-title')).toHaveText( 'highlight.ts', ); - const content = container.locator('.rspress-code-content'); + const content = container.locator('.rp-codeblock__content'); const shikiContainer = content.locator('.shiki.css-variables').first(); await expect(shikiContainer).toHaveJSProperty('tagName', 'PRE'); await expect(shikiContainer.locator('code').first()).toHaveText( diff --git a/e2e/fixtures/code-block/doc/FileTree.mdx b/e2e/fixtures/code-block/doc/FileTree.mdx new file mode 100644 index 000000000..05453864c --- /dev/null +++ b/e2e/fixtures/code-block/doc/FileTree.mdx @@ -0,0 +1,15 @@ +# FileTree + +import { FileTree } from '@theme'; + + + +```tsx title="foo.tsx" +console.log(11111); +``` + +```tsx title="foo.tsx" +console.log(11111); +``` + + diff --git a/e2e/fixtures/plugin-shiki/doc/index.mdx b/e2e/fixtures/code-block/doc/index.mdx similarity index 97% rename from e2e/fixtures/plugin-shiki/doc/index.mdx rename to e2e/fixtures/code-block/doc/index.mdx index 1c3fbf900..02f053820 100644 --- a/e2e/fixtures/plugin-shiki/doc/index.mdx +++ b/e2e/fixtures/code-block/doc/index.mdx @@ -1,4 +1,4 @@ -# @rspress/plugin-shiki +# Shiki ## transformers diff --git a/e2e/fixtures/plugin-shiki/doc/langAlias.mdx b/e2e/fixtures/code-block/doc/langAlias.mdx similarity index 100% rename from e2e/fixtures/plugin-shiki/doc/langAlias.mdx rename to e2e/fixtures/code-block/doc/langAlias.mdx diff --git a/e2e/fixtures/plugin-shiki/index.test.ts b/e2e/fixtures/code-block/index.test.ts similarity index 100% rename from e2e/fixtures/plugin-shiki/index.test.ts rename to e2e/fixtures/code-block/index.test.ts diff --git a/e2e/fixtures/plugin-shiki/package.json b/e2e/fixtures/code-block/package.json similarity index 100% rename from e2e/fixtures/plugin-shiki/package.json rename to e2e/fixtures/code-block/package.json diff --git a/e2e/fixtures/plugin-shiki/rspress.config.ts b/e2e/fixtures/code-block/rspress.config.ts similarity index 100% rename from e2e/fixtures/plugin-shiki/rspress.config.ts rename to e2e/fixtures/code-block/rspress.config.ts diff --git a/e2e/fixtures/plugin-shiki/tsconfig.json b/e2e/fixtures/code-block/tsconfig.json similarity index 100% rename from e2e/fixtures/plugin-shiki/tsconfig.json rename to e2e/fixtures/code-block/tsconfig.json diff --git a/packages/core/src/node/mdx/rehypePlugins/shiki.ts b/packages/core/src/node/mdx/rehypePlugins/shiki.ts index ece64310d..51aa94e07 100644 --- a/packages/core/src/node/mdx/rehypePlugins/shiki.ts +++ b/packages/core/src/node/mdx/rehypePlugins/shiki.ts @@ -5,6 +5,7 @@ import { SHIKI_TRANSFORMER_LINE_NUMBER, transformerLineNumber, } from './transformers'; +import { transformerAddLang } from './transformers/add-lang'; import { transformerAddTitle } from './transformers/add-title'; const cssVariablesTheme = createCssVariablesTheme({ @@ -20,7 +21,11 @@ function createRehypeShikiOptions( ): RehypeShikiOptions { const { transformers = [], ...restOptions } = options || {}; - const newTransformers = [transformerAddTitle(), ...transformers]; + const newTransformers = [ + transformerAddTitle(), + transformerAddLang(), + ...transformers, + ]; if ( showLineNumbers && !newTransformers.some( @@ -36,7 +41,6 @@ function createRehypeShikiOptions( lazy: true, // Lazy loading all langs except ['tsx', 'ts', 'js'] , @see https://github.com/fuma-nama/fumadocs/blob/9b38baf2e66d7bc6f88d24b90a3857730a15fe3c/packages/core/src/mdx-plugins/rehype-code.ts#L169 langs: ['tsx', 'ts', 'js'], ...restOptions, - addLanguageClass: true, transformers: newTransformers, }; } diff --git a/packages/core/src/node/mdx/rehypePlugins/transformers/add-lang.ts b/packages/core/src/node/mdx/rehypePlugins/transformers/add-lang.ts new file mode 100644 index 000000000..e1b9e21c6 --- /dev/null +++ b/packages/core/src/node/mdx/rehypePlugins/transformers/add-lang.ts @@ -0,0 +1,19 @@ +import type { ShikiTransformer } from 'shiki'; + +export const SHIKI_TRANSFORMER_ADD_LANG = 'shiki-transformer:add-lang'; + +export function transformerAddLang(): ShikiTransformer { + return { + name: SHIKI_TRANSFORMER_ADD_LANG, + pre(pre) { + const lang = this.options.lang; + if (lang.length > 0) { + pre.properties = { + ...pre.properties, + lang, + }; + } + return pre; + }, + }; +} diff --git a/packages/core/src/node/mdx/rehypePlugins/transformers/index.ts b/packages/core/src/node/mdx/rehypePlugins/transformers/index.ts index 8d16bcd3f..039c49e55 100644 --- a/packages/core/src/node/mdx/rehypePlugins/transformers/index.ts +++ b/packages/core/src/node/mdx/rehypePlugins/transformers/index.ts @@ -1,3 +1,4 @@ +export { SHIKI_TRANSFORMER_ADD_LANG, transformerAddLang } from './add-lang'; export { SHIKI_TRANSFORMER_ADD_TITLE, transformerAddTitle } from './add-title'; export { SHIKI_TRANSFORMER_LINE_NUMBER, diff --git a/packages/core/src/node/mdx/remarkPlugins/__snapshots__/containerSyntax.test.ts.snap b/packages/core/src/node/mdx/remarkPlugins/__snapshots__/containerSyntax.test.ts.snap index 29dbc0f94..f76e3b763 100644 --- a/packages/core/src/node/mdx/remarkPlugins/__snapshots__/containerSyntax.test.ts.snap +++ b/packages/core/src/node/mdx/remarkPlugins/__snapshots__/containerSyntax.test.ts.snap @@ -345,7 +345,6 @@ function _createMdxContent(props) { }, tabIndex: "0", children: _jsx(_components.code, { - className: "language-js", children: _jsxs(_components.span, { className: "line", children: [_jsx(_components.span, { @@ -820,7 +819,6 @@ function _createMdxContent(props) { }, tabIndex: "0", children: _jsx(_components.code, { - className: "language-javascript", children: _jsxs(_components.span, { className: "line", children: [_jsx(_components.span, { diff --git a/packages/core/src/node/mdx/remarkPlugins/__snapshots__/fileCodeBlock.test.ts.snap b/packages/core/src/node/mdx/remarkPlugins/__snapshots__/fileCodeBlock.test.ts.snap index 3129bd6dd..0eb759fd2 100644 --- a/packages/core/src/node/mdx/remarkPlugins/__snapshots__/fileCodeBlock.test.ts.snap +++ b/packages/core/src/node/mdx/remarkPlugins/__snapshots__/fileCodeBlock.test.ts.snap @@ -22,7 +22,6 @@ function _createMdxContent(props) { }, tabIndex: "0", children: _jsxs(_components.code, { - className: "language-jsx", children: [_jsxs(_components.span, { className: "line", children: [_jsx(_components.span, { diff --git a/packages/plugin-twoslash/static/global-styles/twoslash.css b/packages/plugin-twoslash/static/global-styles/twoslash.css index 98de48254..e587968fb 100644 --- a/packages/plugin-twoslash/static/global-styles/twoslash.css +++ b/packages/plugin-twoslash/static/global-styles/twoslash.css @@ -144,14 +144,14 @@ font-size: var(--twoslash-code-font-size); } -.twoslash .rspress-code-content { +.twoslash .rp-codeblock__content { padding: 16px 0; color: var(--rp-code-block-color); background-color: var(--rp-code-block-bg); border-radius: var(--rp-radius); } -.twoslash .rspress-code-content [class^='code-button-group'] { +.twoslash .rp-codeblock__content [class^='code-button-group'] { display: none; } diff --git a/packages/theme-default/src/components/Callout/index.scss b/packages/theme-default/src/components/Callout/index.scss index eff7c854c..e1cb66acb 100644 --- a/packages/theme-default/src/components/Callout/index.scss +++ b/packages/theme-default/src/components/Callout/index.scss @@ -97,7 +97,7 @@ background-color: var(--rp-code-title-bg-with-opacity); } - .rspress-code-content { + .rp-codeblock__content { background-color: var(--rp-code-block-bg-with-opacity); } diff --git a/packages/theme-default/src/components/CodeBlockRuntime/index.tsx b/packages/theme-default/src/components/CodeBlockRuntime/index.tsx index 4b86d33dd..4b27a2de6 100644 --- a/packages/theme-default/src/components/CodeBlockRuntime/index.tsx +++ b/packages/theme-default/src/components/CodeBlockRuntime/index.tsx @@ -73,23 +73,10 @@ export function CodeBlockRuntime({ development: false, components: { ...getCustomMDXComponent(), - // implement `addLanguageClass: true` pre: props => ( - - ), - code: ({ className, ...otherProps }) => ( - + ), + code: props => , }, Fragment, }); diff --git a/packages/theme-default/src/components/DocContent/doc.scss b/packages/theme-default/src/components/DocContent/doc.scss index b336d6103..19ae1b878 100644 --- a/packages/theme-default/src/components/DocContent/doc.scss +++ b/packages/theme-default/src/components/DocContent/doc.scss @@ -242,7 +242,7 @@ } // #endregion inline code - &:where(div[class^='language-']) { + &:where(.rp-codeblock) { position: relative; margin: 16px 0; border: var(--rp-code-block-border); @@ -304,14 +304,14 @@ } } - &:where(.rspress-code-title) { + &:where(.rp-codeblock__title) { font-family: var(--rp-font-family-mono); padding: 12px 16px; font-size: var(--rp-code-font-size); background-color: var(--rp-code-title-bg); } - &:where(.rspress-code-content) { + &:where(.rp-codeblock__content) { font-size: var(--rp-code-font-size); font-family: var(--rp-font-family-mono); position: relative; diff --git a/packages/theme-default/src/components/DocContent/docComponents/codeblock/CodeButtonGroup.scss b/packages/theme-default/src/components/DocContent/docComponents/codeblock/CodeButtonGroup.scss index bcaf23631..64a08f196 100644 --- a/packages/theme-default/src/components/DocContent/docComponents/codeblock/CodeButtonGroup.scss +++ b/packages/theme-default/src/components/DocContent/docComponents/codeblock/CodeButtonGroup.scss @@ -1,4 +1,4 @@ -.rspress-code-content:hover .rp-code-button-group__button { +.rp-codeblock__content:hover .rp-code-button-group__button { opacity: 1; } diff --git a/packages/theme-default/src/components/DocContent/docComponents/codeblock/pre.tsx b/packages/theme-default/src/components/DocContent/docComponents/codeblock/pre.tsx index d092ee324..e49c28782 100644 --- a/packages/theme-default/src/components/DocContent/docComponents/codeblock/pre.tsx +++ b/packages/theme-default/src/components/DocContent/docComponents/codeblock/pre.tsx @@ -32,15 +32,19 @@ function ShikiPre({ }: ShikiPreProps) { const { codeWrap, toggleCodeWrap } = useCodeWrap(); return ( -
+
{title && ( -
{title}
+
{title}
)} -
+
             {child}
@@ -72,16 +76,16 @@ export interface PreWithCodeButtonGroupProps
  * expected wrapped pre element is:
  * ```html
  *
- *
test.js
- *
+ *
test.js
+ *
*
- *
+ *      
  *        
  *        
  *      
*
*
- * + * * *
*
diff --git a/packages/theme-default/src/components/FileTree/index.scss b/packages/theme-default/src/components/FileTree/index.scss new file mode 100644 index 000000000..cf3a29531 --- /dev/null +++ b/packages/theme-default/src/components/FileTree/index.scss @@ -0,0 +1,106 @@ +.rp-file-tree { + display: flex; + border: var(--rp-code-block-border); + border-radius: var(--rp-radius); + overflow: hidden; + background-color: var(--rp-code-block-bg); +} + +.rp-file-tree__sidebar { + flex: 0 0 220px; + box-sizing: border-box; + max-width: 100%; + border-right: var(--rp-code-block-border); + background-color: var(--rp-code-title-bg); + padding: 12px 16px; + overflow-y: auto; +} + +.rp-file-tree__content { + flex: 1 1 auto; + min-width: 0; + padding: 12px 16px; + background-color: var(--rp-code-block-bg); + overflow-x: auto; +} + +.rp-file-tree__list { + margin: 0; + padding: 0; + list-style: none; +} + +.rp-file-tree__list .rp-file-tree__list { + margin-top: 4px; + padding-left: 12px; + border-left: 1px solid rgba(0, 0, 0, 0.05); +} + +.dark .rp-file-tree__list .rp-file-tree__list { + border-left: 1px solid rgba(255, 255, 255, 0.08); +} + +.rp-file-tree__item { + margin: 2px 0; +} + +.rp-file-tree__dir { + font-size: 13px; + font-weight: 600; + color: var(--rp-c-text-2); + padding: 4px 0; +} + +.rp-file-tree__dir--active { + color: var(--rp-c-text-1); +} + +.rp-file-tree__file { + width: 100%; + text-align: left; + border: none; + background: none; + padding: 6px 8px; + border-radius: var(--rp-radius-small); + color: var(--rp-c-text-1); + font-size: 13px; + line-height: 1.4; + cursor: pointer; + transition: + background-color 0.2s ease, + color 0.2s ease; +} + +.rp-file-tree__file:hover, +.rp-file-tree__file:focus { + background-color: var(--rp-c-brand-tint); + color: var(--rp-c-text-1); +} + +.rp-file-tree__file:focus-visible { + outline: 2px solid var(--rp-c-brand); + outline-offset: 2px; +} + +.rp-file-tree__file--active { + background-color: var(--rp-c-brand-tint); + color: var(--rp-c-brand-dark); + font-weight: 600; +} + +@media (max-width: 960px) { + .rp-file-tree { + flex-direction: column; + } + + .rp-file-tree__sidebar { + flex: none; + width: 100%; + border-right: none; + border-bottom: var(--rp-code-block-border); + } + + .rp-file-tree__content { + padding-top: 8px; + } +} diff --git a/packages/theme-default/src/components/FileTree/index.tsx b/packages/theme-default/src/components/FileTree/index.tsx new file mode 100644 index 000000000..1a679c578 --- /dev/null +++ b/packages/theme-default/src/components/FileTree/index.tsx @@ -0,0 +1,213 @@ +import clsx from 'clsx'; +import { + Children, + type CSSProperties, + cloneElement, + isValidElement, + type ReactElement, + type ReactNode, + useEffect, + useMemo, + useState, +} from 'react'; +import './index.scss'; + +interface FileEntry { + id: string; + path: string; + label: string; + segments: string[]; + fullTitle: string; + element: ReactElement; +} + +interface TreeNode { + name: string; + path: string; + isFile: boolean; + fileId?: string; + children: TreeNode[]; +} + +export interface FileTreeProps { + children: ReactNode; + className?: string; + style?: CSSProperties; +} + +function buildTree(entries: FileEntry[]): TreeNode[] { + const root: TreeNode = { + name: '', + path: '', + isFile: false, + children: [], + }; + + entries.forEach(entry => { + let current = root; + entry.segments.forEach((segment, index) => { + const isLast = index === entry.segments.length - 1; + const nextPath = current.path ? `${current.path}/${segment}` : segment; + let child = current.children.find( + node => node.name === segment && node.isFile === isLast, + ); + + if (!child) { + child = { + name: segment, + path: nextPath, + isFile: isLast, + fileId: isLast ? entry.id : undefined, + children: [], + }; + current.children.push(child); + } + + if (isLast) { + child.isFile = true; + child.fileId = entry.id; + } + + current = child; + }); + }); + + return root.children; +} + +export function FileTree({ children, className, style }: FileTreeProps) { + const entries = useMemo(() => { + const normalized: FileEntry[] = []; + + Children.forEach(children, (child, index) => { + if (!isValidElement(child)) return; + + const { title: rawTitle } = child.props as { title?: string }; + const trimmedTitle = typeof rawTitle === 'string' ? rawTitle.trim() : ''; + const fallbackLabel = `File ${normalized.length + 1}`; + const sourceForSegments = trimmedTitle || fallbackLabel; + const segments = sourceForSegments + .split('/') + .map(segment => segment.trim()) + .filter(Boolean); + + if (!segments.length) { + segments.push(fallbackLabel); + } + + const path = segments.join('/'); + const label = segments[segments.length - 1]; + const fullTitle = trimmedTitle || path; + + normalized.push({ + id: String(index), + path, + label, + segments, + fullTitle, + element: child, + }); + }); + + return normalized; + }, [children]); + + const entryMap = useMemo(() => { + return new Map(entries.map(entry => [entry.id, entry])); + }, [entries]); + + const [activeId, setActiveId] = useState(entries[0]?.id); + + useEffect(() => { + if (!entries.length) { + if (activeId !== undefined) { + setActiveId(undefined); + } + return; + } + + const hasActiveEntry = activeId + ? entries.some(entry => entry.id === activeId) + : false; + + if (!hasActiveEntry) { + setActiveId(entries[0]?.id); + } + }, [entries, activeId]); + + const activeEntry = activeId ? entryMap.get(activeId) : undefined; + const activePath = activeEntry?.path ?? ''; + const tree = useMemo(() => buildTree(entries), [entries]); + + const renderNodes = (nodes: TreeNode[]): ReactNode => { + if (!nodes.length) return null; + + return ( +
    + {nodes.map(node => { + const isActiveFile = node.isFile && node.fileId === activeId; + const isActiveBranch = + !node.isFile && + Boolean(activePath) && + (activePath === node.path || + activePath.startsWith(`${node.path}/`)); + + if (node.isFile) { + const entry = node.fileId ? entryMap.get(node.fileId) : undefined; + return ( +
  • + +
  • + ); + } + + return ( +
  • +
    + {node.name} +
    + {renderNodes(node.children)} +
  • + ); + })} +
+ ); + }; + + if (!entries.length) { + return <>{children}; + } + + const content = activeEntry + ? cloneElement(activeEntry.element, { key: activeEntry.id }) + : null; + + return ( +
+ +
{content}
+
+ ); +} diff --git a/packages/theme-default/src/index.ts b/packages/theme-default/src/index.ts index 9dc7493cc..72327e0b7 100644 --- a/packages/theme-default/src/index.ts +++ b/packages/theme-default/src/index.ts @@ -22,6 +22,7 @@ export { DocContent } from './components/DocContent/index'; export { DocFooter } from './components/DocFooter/index'; export { EditLink } from './components/EditLink/index'; export { useEditLink } from './components/EditLink/useEditLink'; +export { FileTree, type FileTreeProps } from './components/FileTree/index'; export { HomeBackground } from './components/HomeBackground/index'; export { HomeFeature } from './components/HomeFeature/index'; export { HomeFooter } from './components/HomeFooter/index'; diff --git a/packages/theme-default/src/styles/shiki.scss b/packages/theme-default/src/styles/shiki.scss index 0166adca1..91fa17ecb 100644 --- a/packages/theme-default/src/styles/shiki.scss +++ b/packages/theme-default/src/styles/shiki.scss @@ -36,34 +36,32 @@ color: #f47481; } -.has-diff [class*='language-'] .diff.add { +.shiki.has-diff code .diff.add { background-color: rgba(16, 185, 129, 0.1); padding: 0 20px 0 19px; } -[class*='language-'] code .diff.remove { +.shiki.has-diff code .diff.remove { background-color: rgba(244, 63, 94, 0.1); padding: 0 20px 0 19px; } -.rspress-code-content { - .has-highlighted [class*='language-'] { - .line.highlighted { - width: 100%; - position: static; - display: inline-block; - background-color: rgba(0, 99, 199, 0.1); - } - .line.highlighted.error { - background-color: rgba(244, 63, 94, 0.1); - } - .line.highlighted.warning { - background-color: rgba(234, 179, 8, 0.1); - } +.shiki.has-highlighted { + .line.highlighted { + width: 100%; + position: static; + display: inline-block; + background-color: rgba(0, 99, 199, 0.1); + } + .line.highlighted.error { + background-color: rgba(244, 63, 94, 0.1); + } + .line.highlighted.warning { + background-color: rgba(234, 179, 8, 0.1); } } -.has-focused [class*='language-'] { +.shiki.has-focused { .line:not(.focused) { filter: blur(0.095rem); opacity: 0.4; @@ -79,7 +77,7 @@ } } -.has-line-number code { +.shiki.has-line-number code { counter-reset: step; counter-increment: step 0; & .line-number::before { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87ca947e4..9978bd076 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,6 +151,19 @@ importers: specifier: ^22.8.1 version: 22.10.2 + e2e/fixtures/code-block: + dependencies: + '@rspress/core': + specifier: workspace:* + version: link:../../../packages/core + '@shikijs/transformers': + specifier: ^3.12.2 + version: 3.12.2 + devDependencies: + '@types/node': + specifier: ^22.8.1 + version: 22.10.2 + e2e/fixtures/code-block-runtime: dependencies: '@rspress/core': @@ -529,19 +542,6 @@ importers: specifier: ^22.8.1 version: 22.10.2 - e2e/fixtures/plugin-shiki: - dependencies: - '@rspress/core': - specifier: workspace:* - version: link:../../../packages/core - '@shikijs/transformers': - specifier: ^3.12.2 - version: 3.12.2 - devDependencies: - '@types/node': - specifier: ^22.8.1 - version: 22.10.2 - e2e/fixtures/plugin-twoslash: dependencies: '@rspress/core': diff --git a/website/docs/components/LiveCodeEditor.module.scss b/website/docs/components/LiveCodeEditor.module.scss index d170bf30d..b46aae2a6 100644 --- a/website/docs/components/LiveCodeEditor.module.scss +++ b/website/docs/components/LiveCodeEditor.module.scss @@ -7,7 +7,7 @@ position: relative; width: 100%; overflow: auto; - div[class^='language-'] { + :global(.rp-codeblock) { margin: 0; } } From 351aff6751625ed42101f99d7d4505354b4efe83 Mon Sep 17 00:00:00 2001 From: sooniter Date: Wed, 15 Oct 2025 00:33:48 +0800 Subject: [PATCH 2/2] chore: update --- e2e/fixtures/code-block/doc/FileTree.mdx | 6 +- .../src/components/FileTree/index.scss | 172 +++++++++--------- .../src/components/FileTree/index.tsx | 5 +- 3 files changed, 97 insertions(+), 86 deletions(-) diff --git a/e2e/fixtures/code-block/doc/FileTree.mdx b/e2e/fixtures/code-block/doc/FileTree.mdx index 05453864c..6934872ee 100644 --- a/e2e/fixtures/code-block/doc/FileTree.mdx +++ b/e2e/fixtures/code-block/doc/FileTree.mdx @@ -5,11 +5,11 @@ import { FileTree } from '@theme'; ```tsx title="foo.tsx" -console.log(11111); +console.log(foo); ``` -```tsx title="foo.tsx" -console.log(11111); +```tsx title="doc/foo.tsx" +console.log(bar); ``` diff --git a/packages/theme-default/src/components/FileTree/index.scss b/packages/theme-default/src/components/FileTree/index.scss index cf3a29531..f73c6b61a 100644 --- a/packages/theme-default/src/components/FileTree/index.scss +++ b/packages/theme-default/src/components/FileTree/index.scss @@ -4,103 +4,111 @@ border-radius: var(--rp-radius); overflow: hidden; background-color: var(--rp-code-block-bg); -} -.rp-file-tree__sidebar { - flex: 0 0 220px; - box-sizing: border-box; - max-width: 100%; - border-right: var(--rp-code-block-border); - background-color: var(--rp-code-title-bg); - padding: 12px 16px; - overflow-y: auto; -} + &__sidebar { + flex: 0 0 220px; + box-sizing: border-box; + max-width: 100%; + border-right: var(--rp-code-block-border); + background-color: var(--rp-code-title-bg); + padding: 12px 16px; + overflow-y: auto; + } -.rp-file-tree__content { - flex: 1 1 auto; - min-width: 0; - padding: 12px 16px; - background-color: var(--rp-code-block-bg); - overflow-x: auto; -} + &__content { + flex: 1 1 auto; + min-width: 0; + padding: 12px 16px; + background-color: var(--rp-code-block-bg); + overflow-x: auto; -.rp-file-tree__list { - margin: 0; - padding: 0; - list-style: none; -} + .rp-codeblock { + border: none; + border-radius: 0; + padding: 0; + margin: 0; + .rp-codeblock__title { + display: none; + } + } + } -.rp-file-tree__list .rp-file-tree__list { - margin-top: 4px; - padding-left: 12px; - border-left: 1px solid rgba(0, 0, 0, 0.05); -} + &__list { + margin: 0; + padding: 0; + list-style: none; -.dark .rp-file-tree__list .rp-file-tree__list { - border-left: 1px solid rgba(255, 255, 255, 0.08); -} + & & { + margin-top: 4px; + padding-left: 12px; + border-left: 1px solid rgba(0, 0, 0, 0.05); + } -.rp-file-tree__item { - margin: 2px 0; -} + .dark & & { + border-left: 1px solid rgba(255, 255, 255, 0.08); + } + } -.rp-file-tree__dir { - font-size: 13px; - font-weight: 600; - color: var(--rp-c-text-2); - padding: 4px 0; -} + &__item { + margin: 2px 0; + } -.rp-file-tree__dir--active { - color: var(--rp-c-text-1); -} + &__dir { + font-size: 13px; + font-weight: 600; + color: var(--rp-c-text-2); + padding: 4px 0; -.rp-file-tree__file { - width: 100%; - text-align: left; - border: none; - background: none; - padding: 6px 8px; - border-radius: var(--rp-radius-small); - color: var(--rp-c-text-1); - font-size: 13px; - line-height: 1.4; - cursor: pointer; - transition: - background-color 0.2s ease, - color 0.2s ease; -} + &--active { + color: var(--rp-c-text-1); + } + } -.rp-file-tree__file:hover, -.rp-file-tree__file:focus { - background-color: var(--rp-c-brand-tint); - color: var(--rp-c-text-1); -} + &__file { + width: 100%; + text-align: left; + border: none; + background: none; + padding: 6px 8px; + border-radius: var(--rp-radius-small); + color: var(--rp-c-text-1); + font-size: 13px; + line-height: 1.4; + cursor: pointer; + transition: + background-color 0.2s ease, + color 0.2s ease; -.rp-file-tree__file:focus-visible { - outline: 2px solid var(--rp-c-brand); - outline-offset: 2px; -} + &:hover, + &:focus { + background-color: var(--rp-c-brand-tint); + color: var(--rp-c-text-1); + } -.rp-file-tree__file--active { - background-color: var(--rp-c-brand-tint); - color: var(--rp-c-brand-dark); - font-weight: 600; -} + &:focus-visible { + outline: 2px solid var(--rp-c-brand); + outline-offset: 2px; + } -@media (max-width: 960px) { - .rp-file-tree { - flex-direction: column; + &--active { + background-color: var(--rp-c-brand-tint); + color: var(--rp-c-brand-dark); + font-weight: 600; + } } - .rp-file-tree__sidebar { - flex: none; - width: 100%; - border-right: none; - border-bottom: var(--rp-code-block-border); - } + @media (max-width: 960px) { + flex-direction: column; + + &__sidebar { + flex: none; + width: 100%; + border-right: none; + border-bottom: var(--rp-code-block-border); + } - .rp-file-tree__content { - padding-top: 8px; + &__content { + padding-top: 8px; + } } } diff --git a/packages/theme-default/src/components/FileTree/index.tsx b/packages/theme-default/src/components/FileTree/index.tsx index 1a679c578..591d0e006 100644 --- a/packages/theme-default/src/components/FileTree/index.tsx +++ b/packages/theme-default/src/components/FileTree/index.tsx @@ -81,8 +81,11 @@ export function FileTree({ children, className, style }: FileTreeProps) { Children.forEach(children, (child, index) => { if (!isValidElement(child)) return; + console.log(child.props, 111111); - const { title: rawTitle } = child.props as { title?: string }; + const { title: rawTitle } = (child.props as any).children?.props as { + title?: string; + }; const trimmedTitle = typeof rawTitle === 'string' ? rawTitle.trim() : ''; const fallbackLabel = `File ${normalized.length + 1}`; const sourceForSegments = trimmedTitle || fallbackLabel;