feat: Add codehighlight for the Code'
This commit is contained in:
11 files changed
+422
-14
No files matched your search
@@ -47,6 +47,7 @@
|
|||||||
"react-aria": "^3.45.0",
|
"react-aria": "^3.45.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-stately": "^3.44.0",
|
"react-stately": "^3.44.0",
|
||||||
|
"shiki": "^4.0.1",
|
||||||
"tw-animate-css": "^1.2.8"
|
"tw-animate-css": "^1.2.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -90,6 +90,24 @@ export const LongWithScroll: StoryObj<typeof CodeBlock> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ShikiHighlight: StoryObj<typeof CodeBlock> = {
|
||||||
|
render: (args) => (
|
||||||
|
<CodeBlock {...args}>
|
||||||
|
<Code>
|
||||||
|
{`import React from 'react';
|
||||||
|
|
||||||
|
export const MyComponent = () => {
|
||||||
|
return <div>Hello World</div>;
|
||||||
|
};`}
|
||||||
|
</Code>
|
||||||
|
</CodeBlock>
|
||||||
|
),
|
||||||
|
args: {
|
||||||
|
label: 'ShikiHighlight.tsx',
|
||||||
|
language: 'tsx',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const PlainText: StoryObj<typeof CodeBlock> = {
|
export const PlainText: StoryObj<typeof CodeBlock> = {
|
||||||
render: (args) => (
|
render: (args) => (
|
||||||
<CodeBlock {...args}>
|
<CodeBlock {...args}>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
|
||||||
import { Code } from './Code';
|
import { Code } from './Code';
|
||||||
import { CodeBlock } from './CodeBlock/CodeBlock';
|
import { CodeBlock } from './CodeBlock/CodeBlock';
|
||||||
|
|
||||||
|
// Mock shiki
|
||||||
|
vi.mock('shiki', () => ({
|
||||||
|
createHighlighter: vi.fn().mockResolvedValue({
|
||||||
|
codeToHtml: vi.fn().mockReturnValue('<pre><code><span class="line">highlighted code</span></code></pre>'),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
// Mock clipboard API
|
// Mock clipboard API
|
||||||
const mockWriteText = vi.fn().mockImplementation(() => Promise.resolve());
|
const mockWriteText = vi.fn().mockImplementation(() => Promise.resolve());
|
||||||
Object.assign(navigator, {
|
Object.assign(navigator, {
|
||||||
@@ -15,6 +23,7 @@ Object.assign(navigator, {
|
|||||||
describe('Code Component', () => {
|
describe('Code Component', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockWriteText.mockClear();
|
mockWriteText.mockClear();
|
||||||
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders inline code correctly', () => {
|
it('renders inline code correctly', () => {
|
||||||
@@ -46,7 +55,6 @@ describe('Code Component', () => {
|
|||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(codeElement);
|
fireEvent.click(codeElement);
|
||||||
await Promise.resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockWriteText).toHaveBeenCalledWith('copied!');
|
expect(mockWriteText).toHaveBeenCalledWith('copied!');
|
||||||
@@ -64,7 +72,6 @@ describe('Code Component', () => {
|
|||||||
const copyButton = screen.getByLabelText('Copy code');
|
const copyButton = screen.getByLabelText('Copy code');
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(copyButton);
|
fireEvent.click(copyButton);
|
||||||
await Promise.resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockWriteText).toHaveBeenCalledWith('block copy');
|
expect(mockWriteText).toHaveBeenCalledWith('block copy');
|
||||||
@@ -76,9 +83,22 @@ describe('Code Component', () => {
|
|||||||
const codeElement = screen.getByText('extracted');
|
const codeElement = screen.getByText('extracted');
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(codeElement);
|
fireEvent.click(codeElement);
|
||||||
await Promise.resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockWriteText).toHaveBeenCalledWith('extracted');
|
expect(mockWriteText).toHaveBeenCalledWith('extracted');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses shiki for highlighting when language is provided in block mode', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
render(
|
||||||
|
<CodeBlock language="typescript">
|
||||||
|
<Code>const x: number = 1;</Code>
|
||||||
|
</CodeBlock>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for shiki async highlighting
|
||||||
|
const highlighted = await screen.findByText('highlighted code');
|
||||||
|
expect(highlighted).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -3,8 +3,12 @@ import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|||||||
import { usePress } from 'react-aria';
|
import { usePress } from 'react-aria';
|
||||||
|
|
||||||
import { useCodeContext } from './contexts/CodeContext/CodeContext';
|
import { useCodeContext } from './contexts/CodeContext/CodeContext';
|
||||||
|
import { codeHighlighter } from './lib/highlighter/highlighter';
|
||||||
|
|
||||||
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
||||||
|
import type { CodeLanguage } from './lib/highlighter/highlighter';
|
||||||
|
|
||||||
|
export type { CodeLanguage };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the Code component.
|
* Props for the Code component.
|
||||||
@@ -18,6 +22,12 @@ export interface CodeProps extends ClassNameProps, TestIdProps {
|
|||||||
* If omitted in inline mode, the component will attempt to extract text from `children`.
|
* If omitted in inline mode, the component will attempt to extract text from `children`.
|
||||||
*/
|
*/
|
||||||
copyValue?: string;
|
copyValue?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The language of the code. Only used when rendered inside a CodeBlock.
|
||||||
|
* If provided, shiki will be used for syntax highlighting.
|
||||||
|
*/
|
||||||
|
language?: CodeLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,12 +40,16 @@ export interface CodeProps extends ClassNameProps, TestIdProps {
|
|||||||
export const Code = memo(function Code({
|
export const Code = memo(function Code({
|
||||||
children,
|
children,
|
||||||
copyValue,
|
copyValue,
|
||||||
|
language: propsLanguage,
|
||||||
className,
|
className,
|
||||||
'data-testid': testId,
|
'data-testid': testId,
|
||||||
}: CodeProps) {
|
}: CodeProps) {
|
||||||
const context = useCodeContext();
|
const context = useCodeContext();
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
const codeRef = useRef<HTMLElement>(null);
|
const codeRef = useRef<HTMLElement>(null);
|
||||||
|
const [highlightedHtml, setHighlightedHtml] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const language = propsLanguage ?? context?.language;
|
||||||
|
|
||||||
const handleCopy = useCallback(async () => {
|
const handleCopy = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -67,6 +81,22 @@ export const Code = memo(function Code({
|
|||||||
}
|
}
|
||||||
}, [isCopied]);
|
}, [isCopied]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (context?.isInCodeBlock && language && typeof children === 'string') {
|
||||||
|
const highlight = async () => {
|
||||||
|
try {
|
||||||
|
const html = await codeHighlighter.highlight(children, language);
|
||||||
|
setHighlightedHtml(html);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to highlight code:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void highlight();
|
||||||
|
} else {
|
||||||
|
setHighlightedHtml(null);
|
||||||
|
}
|
||||||
|
}, [children, language, context?.isInCodeBlock]);
|
||||||
|
|
||||||
const { pressProps } = usePress({
|
const { pressProps } = usePress({
|
||||||
onPress: onCopyPress,
|
onPress: onCopyPress,
|
||||||
});
|
});
|
||||||
@@ -74,7 +104,39 @@ export const Code = memo(function Code({
|
|||||||
if (context?.isInCodeBlock) {
|
if (context?.isInCodeBlock) {
|
||||||
const { showLineNumbers } = context;
|
const { showLineNumbers } = context;
|
||||||
|
|
||||||
let content = children;
|
let content: React.ReactNode = children;
|
||||||
|
|
||||||
|
if (highlightedHtml) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={codeRef as any}
|
||||||
|
className={cn(
|
||||||
|
'block min-w-full font-mono text-sm',
|
||||||
|
'[&>pre]:!bg-transparent [&>pre]:!p-0 [&>pre]:!m-0',
|
||||||
|
showLineNumbers && [
|
||||||
|
'[&>pre>code]:[counter-reset:line]',
|
||||||
|
'[&>pre>code>.line]:relative [&>pre>code>.line]:pl-10',
|
||||||
|
'[&>pre>code>.line]:[counter-increment:line]',
|
||||||
|
'[&>pre>code>.line]:before:content-[counter(line)]',
|
||||||
|
'[&>pre>code>.line]:before:absolute [&>pre>code>.line]:before:left-0 [&>pre>code>.line]:before:top-0',
|
||||||
|
'[&>pre>code>.line]:before:inline-block',
|
||||||
|
'[&>pre>code>.line]:before:w-8',
|
||||||
|
'[&>pre>code>.line]:before:text-right',
|
||||||
|
'[&>pre>code>.line]:before:text-[var(--rb-muted-text)]/50',
|
||||||
|
'[&>pre>code>.line]:before:select-none',
|
||||||
|
'[&>pre>code>.line]:before:border-r',
|
||||||
|
'[&>pre>code>.line]:before:border-[var(--rb-outline)]/10',
|
||||||
|
'[&>pre>code>.line]:before:pr-3',
|
||||||
|
'[&>pre>code>.line]:before:font-mono [&>pre>code>.line]:before:text-[10px]',
|
||||||
|
],
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
|
||||||
|
data-testid={testId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof children === 'string') {
|
if (typeof children === 'string') {
|
||||||
content = children
|
content = children
|
||||||
.trimEnd()
|
.trimEnd()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Container } from '../../Container/Container';
|
|||||||
import { CodeContext, type CodeContextValue } from '../contexts/CodeContext/CodeContext';
|
import { CodeContext, type CodeContextValue } from '../contexts/CodeContext/CodeContext';
|
||||||
|
|
||||||
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl';
|
||||||
|
import type { CodeLanguage } from '../lib/highlighter/highlighter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the CodeBlock component.
|
* Props for the CodeBlock component.
|
||||||
@@ -21,6 +22,11 @@ export interface CodeBlockProps extends ClassNameProps, TestIdProps {
|
|||||||
*/
|
*/
|
||||||
label?: string;
|
label?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The language of the code block.
|
||||||
|
*/
|
||||||
|
language?: CodeLanguage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to render line number gutters.
|
* Whether to render line number gutters.
|
||||||
* Requires children (Code) to be rendered as block-level elements.
|
* Requires children (Code) to be rendered as block-level elements.
|
||||||
@@ -49,6 +55,7 @@ export interface CodeBlockProps extends ClassNameProps, TestIdProps {
|
|||||||
export const CodeBlock = memo(function CodeBlock({
|
export const CodeBlock = memo(function CodeBlock({
|
||||||
children,
|
children,
|
||||||
label,
|
label,
|
||||||
|
language,
|
||||||
showLineNumbers = true,
|
showLineNumbers = true,
|
||||||
copyValue,
|
copyValue,
|
||||||
maxHeight,
|
maxHeight,
|
||||||
@@ -92,8 +99,9 @@ export const CodeBlock = memo(function CodeBlock({
|
|||||||
() => ({
|
() => ({
|
||||||
isInCodeBlock: true,
|
isInCodeBlock: true,
|
||||||
showLineNumbers,
|
showLineNumbers,
|
||||||
|
language,
|
||||||
}),
|
}),
|
||||||
[showLineNumbers]
|
[showLineNumbers, language]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
import type { CodeLanguage } from '../../lib/highlighter/highlighter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value for the Code context.
|
* Value for the Code context.
|
||||||
*/
|
*/
|
||||||
@@ -8,6 +10,8 @@ export interface CodeContextValue {
|
|||||||
isInCodeBlock: boolean;
|
isInCodeBlock: boolean;
|
||||||
/** Whether to show line numbers in the block. */
|
/** Whether to show line numbers in the block. */
|
||||||
showLineNumbers?: boolean;
|
showLineNumbers?: boolean;
|
||||||
|
/** The language of the code in the block. */
|
||||||
|
language?: CodeLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { createHighlighter } from 'shiki';
|
||||||
|
|
||||||
|
import type { Highlighter } from 'shiki';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported languages for syntax highlighting.
|
||||||
|
*/
|
||||||
|
export type CodeLanguage =
|
||||||
|
| 'typescript'
|
||||||
|
| 'javascript'
|
||||||
|
| 'tsx'
|
||||||
|
| 'jsx'
|
||||||
|
| 'bash'
|
||||||
|
| 'shell'
|
||||||
|
| 'json'
|
||||||
|
| 'css'
|
||||||
|
| 'html'
|
||||||
|
| 'markdown'
|
||||||
|
| 'yaml'
|
||||||
|
| 'dockerfile'
|
||||||
|
| 'toml'
|
||||||
|
| 'rust'
|
||||||
|
| 'go'
|
||||||
|
| 'python';
|
||||||
|
|
||||||
|
export const SUPPORTED_LANGUAGES: CodeLanguage[] = [
|
||||||
|
'typescript',
|
||||||
|
'javascript',
|
||||||
|
'tsx',
|
||||||
|
'jsx',
|
||||||
|
'bash',
|
||||||
|
'shell',
|
||||||
|
'json',
|
||||||
|
'css',
|
||||||
|
'html',
|
||||||
|
'markdown',
|
||||||
|
'yaml',
|
||||||
|
'dockerfile',
|
||||||
|
'toml',
|
||||||
|
'rust',
|
||||||
|
'go',
|
||||||
|
'python',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton highlighter service for code blocks.
|
||||||
|
*/
|
||||||
|
class CodeHighlighter {
|
||||||
|
private static instance: CodeHighlighter;
|
||||||
|
private highlighter: Highlighter | null = null;
|
||||||
|
private initializingPromise: Promise<Highlighter> | null = null;
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the singleton instance of the highlighter.
|
||||||
|
*/
|
||||||
|
public static getInstance(): CodeHighlighter {
|
||||||
|
if (!CodeHighlighter.instance) {
|
||||||
|
CodeHighlighter.instance = new CodeHighlighter();
|
||||||
|
}
|
||||||
|
return CodeHighlighter.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the highlighter if it hasn't been already.
|
||||||
|
*/
|
||||||
|
private async getHighlighter(): Promise<Highlighter> {
|
||||||
|
if (this.highlighter) {
|
||||||
|
return this.highlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.initializingPromise) {
|
||||||
|
return this.initializingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initializingPromise = createHighlighter({
|
||||||
|
themes: ['github-dark', 'github-light'],
|
||||||
|
langs: SUPPORTED_LANGUAGES,
|
||||||
|
}).then((highlighter) => {
|
||||||
|
this.highlighter = highlighter;
|
||||||
|
this.initializingPromise = null;
|
||||||
|
return highlighter;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.initializingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights the given code.
|
||||||
|
*
|
||||||
|
* @param code - The raw source code to highlight
|
||||||
|
* @param lang - The language of the code
|
||||||
|
* @returns A promise resolving to the highlighted HTML
|
||||||
|
*/
|
||||||
|
public async highlight(code: string, lang: CodeLanguage): Promise<string> {
|
||||||
|
try {
|
||||||
|
const highlighter = await this.getHighlighter();
|
||||||
|
return highlighter.codeToHtml(code, {
|
||||||
|
lang,
|
||||||
|
themes: {
|
||||||
|
light: 'github-light',
|
||||||
|
dark: 'github-dark',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to highlight code:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const codeHighlighter = CodeHighlighter.getInstance();
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import { Code } from '../../Code/Code';
|
import { Code } from '../../Code/Code';
|
||||||
|
|
||||||
import type { CodeProps } from '../../Code/Code';
|
import type { CodeProps } from '../../Code/Code';
|
||||||
|
import type { CodeLanguage } from '../../Code/lib/highlighter/highlighter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Markdown code component for inline snippets.
|
* A Markdown code component for inline snippets.
|
||||||
*/
|
*/
|
||||||
export const MarkdownCode = memo(function MarkdownCode(props: CodeProps) {
|
export const MarkdownCode = memo(function MarkdownCode(props: CodeProps) {
|
||||||
return <Code {...props} />;
|
const { className, ...rest } = props;
|
||||||
|
|
||||||
|
const language = useMemo(() => {
|
||||||
|
if (className) {
|
||||||
|
const match = /language-(\w+)/.exec(className);
|
||||||
|
if (match) {
|
||||||
|
return match[1] as CodeLanguage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [className]);
|
||||||
|
|
||||||
|
return <Code language={language} {...rest} />;
|
||||||
});
|
});
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import { CodeBlock } from '../../Code/CodeBlock/CodeBlock';
|
import { CodeBlock } from '../../Code/CodeBlock/CodeBlock';
|
||||||
|
|
||||||
import type { CodeBlockProps } from '../../Code/CodeBlock/CodeBlock';
|
import type { CodeBlockProps } from '../../Code/CodeBlock/CodeBlock';
|
||||||
|
import type { CodeLanguage } from '../../Code/lib/highlighter/highlighter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Markdown pre component for code blocks.
|
* A Markdown pre component for code blocks.
|
||||||
@@ -10,5 +11,26 @@ import type { CodeBlockProps } from '../../Code/CodeBlock/CodeBlock';
|
|||||||
* Wraps content in the design system CodeBlock.
|
* Wraps content in the design system CodeBlock.
|
||||||
*/
|
*/
|
||||||
export const MarkdownPre = memo(function MarkdownPre(props: CodeBlockProps) {
|
export const MarkdownPre = memo(function MarkdownPre(props: CodeBlockProps) {
|
||||||
return <CodeBlock {...props} />;
|
const { children, ...rest } = props;
|
||||||
|
|
||||||
|
// MDX often passes a <code> element as the only child of <pre>.
|
||||||
|
// That <code> element usually has the language class (e.g., "language-typescript").
|
||||||
|
const language = useMemo(() => {
|
||||||
|
if (React.isValidElement<{ className?: string }>(children) && children.props.className) {
|
||||||
|
const className = children.props.className;
|
||||||
|
const match = /language-(\w+)/.exec(className);
|
||||||
|
if (match) {
|
||||||
|
// We cast here because we know it comes from markdown,
|
||||||
|
// shiki will handle it or fallback to plaintext if unknown at runtime.
|
||||||
|
return match[1] as CodeLanguage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CodeBlock language={language} {...rest}>
|
||||||
|
{children}
|
||||||
|
</CodeBlock>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
@@ -285,4 +285,8 @@
|
|||||||
);
|
);
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark .shiki span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Generated
+148
-5
@@ -198,7 +198,7 @@ importers:
|
|||||||
version: 7.0.1
|
version: 7.0.1
|
||||||
storybook-react-rsbuild:
|
storybook-react-rsbuild:
|
||||||
specifier: ^1.0.3
|
specifier: ^1.0.3
|
||||||
version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.5.3)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.56.0)(storybook@8.6.15(prettier@3.5.3))(typescript@5.8.3)(webpack@5.105.1)
|
version: 1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.5.3)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.56.0)(storybook@8.6.15(prettier@3.5.3))(typescript@5.8.3)(webpack@5.105.1)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.8.3
|
specifier: ^5.8.3
|
||||||
version: 5.8.3
|
version: 5.8.3
|
||||||
@@ -244,6 +244,9 @@ importers:
|
|||||||
react-stately:
|
react-stately:
|
||||||
specifier: ^3.44.0
|
specifier: ^3.44.0
|
||||||
version: 3.44.0(react@19.2.3)
|
version: 3.44.0(react@19.2.3)
|
||||||
|
shiki:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.1.4
|
specifier: ^4.1.4
|
||||||
version: 4.1.18
|
version: 4.1.18
|
||||||
@@ -295,7 +298,7 @@ importers:
|
|||||||
version: 4.1.18
|
version: 4.1.18
|
||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
specifier: ^0.5.19
|
specifier: ^0.5.19
|
||||||
version: 0.5.19(tailwindcss@4.1.18)
|
version: 0.5.19
|
||||||
'@testing-library/jest-dom':
|
'@testing-library/jest-dom':
|
||||||
specifier: ^6.9.1
|
specifier: ^6.9.1
|
||||||
version: 6.9.1
|
version: 6.9.1
|
||||||
@@ -3057,6 +3060,37 @@ packages:
|
|||||||
'@rushstack/ts-command-line@5.1.5':
|
'@rushstack/ts-command-line@5.1.5':
|
||||||
resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==}
|
resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==}
|
||||||
|
|
||||||
|
'@shikijs/core@4.0.1':
|
||||||
|
resolution: {integrity: sha512-vWvqi9JNgz1dRL9Nvog5wtx7RuNkf7MEPl2mU/cyUUxJeH1CAr3t+81h8zO8zs7DK6cKLMoU9TvukWIDjP4Lzg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@4.0.1':
|
||||||
|
resolution: {integrity: sha512-DJK9NiwtGYqMuKCRO4Ip0FKNDQpmaiS+K5bFjJ7DWFn4zHueDWgaUG8kAofkrnXF6zPPYYQY7J5FYVW9MbZyBg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@4.0.1':
|
||||||
|
resolution: {integrity: sha512-oCWdCTDch3J8Kc0OZJ98KuUPC02O1VqIE3W/e2uvrHqTxYRR21RGEJMtchrgrxhsoJJCzmIciKsqG+q/yD+Cxg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/langs@4.0.1':
|
||||||
|
resolution: {integrity: sha512-v/mluaybWdnGJR4GqAR6zh8qAZohW9k+cGYT28Y7M8+jLbC0l4yG085O1A+WkseHTn+awd+P3UBymb2+MXFc8w==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/primitive@4.0.1':
|
||||||
|
resolution: {integrity: sha512-ns0hHZc5eWZuvuIEJz2pTx3Qecz0aRVYumVQJ8JgWY2tq/dH8WxdcVM49Fc2NsHEILNIT6vfdW9MF26RANWiTA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/themes@4.0.1':
|
||||||
|
resolution: {integrity: sha512-FW41C/D6j/yKQkzVdjrRPiJCtgeDaYRJFEyCKFCINuRJRj9WcmubhP4KQHPZ4+9eT87jruSrYPyoblNRyDFzvA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/types@4.0.1':
|
||||||
|
resolution: {integrity: sha512-EaygPEn57+jJ76mw+nTLvIpJMAcMPokFbrF8lufsZP7Ukk+ToJYEcswN1G0e49nUZAq7aCQtoeW219A8HK1ZOw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2':
|
||||||
|
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||||
|
|
||||||
'@sideway/address@4.1.5':
|
'@sideway/address@4.1.5':
|
||||||
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
|
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
|
||||||
|
|
||||||
@@ -5121,6 +5155,9 @@ packages:
|
|||||||
hast-util-to-estree@3.1.3:
|
hast-util-to-estree@3.1.3:
|
||||||
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
|
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
|
||||||
|
|
||||||
|
hast-util-to-html@9.0.5:
|
||||||
|
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
|
||||||
|
|
||||||
hast-util-to-jsx-runtime@2.3.6:
|
hast-util-to-jsx-runtime@2.3.6:
|
||||||
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
||||||
|
|
||||||
@@ -5154,6 +5191,9 @@ packages:
|
|||||||
engines: {node: ^14.13.1 || >=16.0.0}
|
engines: {node: ^14.13.1 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
html-void-elements@3.0.0:
|
||||||
|
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||||
|
|
||||||
htmlparser2@3.10.1:
|
htmlparser2@3.10.1:
|
||||||
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
|
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
|
||||||
|
|
||||||
@@ -6166,6 +6206,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
oniguruma-parser@0.12.1:
|
||||||
|
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
|
||||||
|
|
||||||
|
oniguruma-to-es@4.3.4:
|
||||||
|
resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
|
||||||
|
|
||||||
open@8.4.2:
|
open@8.4.2:
|
||||||
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -6569,6 +6615,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
regex-recursion@6.0.2:
|
||||||
|
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
|
||||||
|
|
||||||
|
regex-utilities@2.3.0:
|
||||||
|
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
|
||||||
|
|
||||||
|
regex@6.1.0:
|
||||||
|
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
|
||||||
|
|
||||||
regexp.prototype.flags@1.5.4:
|
regexp.prototype.flags@1.5.4:
|
||||||
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -6779,6 +6834,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
shiki@4.0.1:
|
||||||
|
resolution: {integrity: sha512-EkAEhDTN5WhpoQFXFw79OHIrSAfHhlImeCdSyg4u4XvrpxKEmdo/9x/HWSowujAnUrFsGOwWiE58a6GVentMnQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
side-channel-list@1.0.0:
|
side-channel-list@1.0.0:
|
||||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -10716,6 +10775,46 @@ snapshots:
|
|||||||
- '@types/node'
|
- '@types/node'
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@shikijs/core@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/primitive': 4.0.1
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
hast-util-to-html: 9.0.5
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
oniguruma-to-es: 4.3.4
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
|
'@shikijs/langs@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
|
||||||
|
'@shikijs/primitive@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
|
'@shikijs/themes@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
|
||||||
|
'@shikijs/types@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2': {}
|
||||||
|
|
||||||
'@sideway/address@4.1.5':
|
'@sideway/address@4.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/hoek': 9.3.0
|
'@hapi/hoek': 9.3.0
|
||||||
@@ -11206,10 +11305,9 @@ snapshots:
|
|||||||
postcss: 8.5.6
|
postcss: 8.5.6
|
||||||
tailwindcss: 4.1.18
|
tailwindcss: 4.1.18
|
||||||
|
|
||||||
'@tailwindcss/typography@0.5.19(tailwindcss@4.1.18)':
|
'@tailwindcss/typography@0.5.19':
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
tailwindcss: 4.1.18
|
|
||||||
|
|
||||||
'@testing-library/dom@10.4.0':
|
'@testing-library/dom@10.4.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -13226,6 +13324,20 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
hast-util-to-html@9.0.5:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
ccount: 2.0.1
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
hast-util-whitespace: 3.0.0
|
||||||
|
html-void-elements: 3.0.0
|
||||||
|
mdast-util-to-hast: 13.2.1
|
||||||
|
property-information: 7.1.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
stringify-entities: 4.0.4
|
||||||
|
zwitch: 2.0.4
|
||||||
|
|
||||||
hast-util-to-jsx-runtime@2.3.6:
|
hast-util-to-jsx-runtime@2.3.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@@ -13280,6 +13392,8 @@ snapshots:
|
|||||||
relateurl: 0.2.7
|
relateurl: 0.2.7
|
||||||
terser: 5.46.0
|
terser: 5.46.0
|
||||||
|
|
||||||
|
html-void-elements@3.0.0: {}
|
||||||
|
|
||||||
htmlparser2@3.10.1:
|
htmlparser2@3.10.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 1.3.1
|
domelementtype: 1.3.1
|
||||||
@@ -14753,6 +14867,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn: 2.1.0
|
mimic-fn: 2.1.0
|
||||||
|
|
||||||
|
oniguruma-parser@0.12.1: {}
|
||||||
|
|
||||||
|
oniguruma-to-es@4.3.4:
|
||||||
|
dependencies:
|
||||||
|
oniguruma-parser: 0.12.1
|
||||||
|
regex: 6.1.0
|
||||||
|
regex-recursion: 6.0.2
|
||||||
|
|
||||||
open@8.4.2:
|
open@8.4.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-lazy-prop: 2.0.0
|
define-lazy-prop: 2.0.0
|
||||||
@@ -15197,6 +15319,16 @@ snapshots:
|
|||||||
get-proto: 1.0.1
|
get-proto: 1.0.1
|
||||||
which-builtin-type: 1.2.1
|
which-builtin-type: 1.2.1
|
||||||
|
|
||||||
|
regex-recursion@6.0.2:
|
||||||
|
dependencies:
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
|
regex-utilities@2.3.0: {}
|
||||||
|
|
||||||
|
regex@6.1.0:
|
||||||
|
dependencies:
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
regexp.prototype.flags@1.5.4:
|
regexp.prototype.flags@1.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.8
|
call-bind: 1.0.8
|
||||||
@@ -15458,6 +15590,17 @@ snapshots:
|
|||||||
|
|
||||||
shell-quote@1.8.3: {}
|
shell-quote@1.8.3: {}
|
||||||
|
|
||||||
|
shiki@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/core': 4.0.1
|
||||||
|
'@shikijs/engine-javascript': 4.0.1
|
||||||
|
'@shikijs/engine-oniguruma': 4.0.1
|
||||||
|
'@shikijs/langs': 4.0.1
|
||||||
|
'@shikijs/themes': 4.0.1
|
||||||
|
'@shikijs/types': 4.0.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
side-channel-list@1.0.0:
|
side-channel-list@1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -15653,7 +15796,7 @@ snapshots:
|
|||||||
- tslib
|
- tslib
|
||||||
- webpack
|
- webpack
|
||||||
|
|
||||||
storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.5.3)))(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.56.0)(storybook@8.6.15(prettier@3.5.3))(typescript@5.8.3)(webpack@5.105.1):
|
storybook-react-rsbuild@1.0.3(@rsbuild/core@1.7.3)(@storybook/test@8.6.15(storybook@8.6.15(prettier@3.5.3)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.56.0)(storybook@8.6.15(prettier@3.5.3))(typescript@5.8.3)(webpack@5.105.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/pluginutils': 5.3.0(rollup@4.56.0)
|
'@rollup/pluginutils': 5.3.0(rollup@4.56.0)
|
||||||
'@rsbuild/core': 1.7.3
|
'@rsbuild/core': 1.7.3
|
||||||
|
|||||||
Reference in new issue
Block a user