diff --git a/.changeset/fair-boats-cheer.md b/.changeset/fair-boats-cheer.md new file mode 100644 index 0000000000..1124e838a9 --- /dev/null +++ b/.changeset/fair-boats-cheer.md @@ -0,0 +1,12 @@ +--- +'@leafygreen-ui/code-editor': minor +--- + +- Creates package. +- Adds `CodeEditor` component with the following functionality: + - Basic setup + - Line wrapping + - Hyperlink support + - Forced parsing + - Placeholders +- Adds `renderEditor` test utility which exposes a number of helper methods. diff --git a/README.md b/README.md index b35d5a9877..966b548c51 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ import Button from '@leafygreen-ui/button'; | [@leafygreen-ui/checkbox](./packages/checkbox) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/checkbox)](https://www.npmjs.com/package/@leafygreen-ui/checkbox) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/checkbox?color=white) | [Live Example](http://mongodb.design/component/checkbox/live-example) | | [@leafygreen-ui/chip](./packages/chip) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/chip)](https://www.npmjs.com/package/@leafygreen-ui/chip) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/chip?color=white) | [Live Example](http://mongodb.design/component/chip/live-example) | | [@leafygreen-ui/code](./packages/code) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/code)](https://www.npmjs.com/package/@leafygreen-ui/code) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/code?color=white) | [Live Example](http://mongodb.design/component/code/live-example) | +| [@leafygreen-ui/code-editor](./packages/code-editor) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/code-editor)](https://www.npmjs.com/package/@leafygreen-ui/code-editor) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/code-editor?color=white) | [Live Example](http://mongodb.design/component/code-editor/live-example) | | [@leafygreen-ui/combobox](./packages/combobox) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/combobox)](https://www.npmjs.com/package/@leafygreen-ui/combobox) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/combobox?color=white) | [Live Example](http://mongodb.design/component/combobox/live-example) | | [@leafygreen-ui/confirmation-modal](./packages/confirmation-modal) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/confirmation-modal)](https://www.npmjs.com/package/@leafygreen-ui/confirmation-modal) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/confirmation-modal?color=white) | [Live Example](http://mongodb.design/component/confirmation-modal/live-example) | | [@leafygreen-ui/copyable](./packages/copyable) | [![version](https://img.shields.io/npm/v/@leafygreen-ui/copyable)](https://www.npmjs.com/package/@leafygreen-ui/copyable) | ![downloads](https://img.shields.io/npm/dm/@leafygreen-ui/copyable?color=white) | [Live Example](http://mongodb.design/component/copyable/live-example) | diff --git a/package.json b/package.json index 3bbbea2924..8fab86bc05 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "private": true, "engines": { - "node": ">= 18.12.0", + "node": ">= 18.20.8", "pnpm": ">= 9.15.0" }, "scripts": { diff --git a/packages/code-editor/README.md b/packages/code-editor/README.md new file mode 100644 index 0000000000..69c178cb6c --- /dev/null +++ b/packages/code-editor/README.md @@ -0,0 +1,183 @@ +# Code Editor + +![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/code-editor.svg) + +#### [View on MongoDB.design](https://www.mongodb.design/component/code-editor/live-example/) + +## Installation + +### PNPM + +```shell +pnpm add @leafygreen-ui/code-editor +``` + +### Yarn + +```shell +yarn add @leafygreen-ui/code-editor +``` + +### NPM + +```shell +npm install @leafygreen-ui/code-editor +``` + +## Component + +### `` + +#### Example + +```tsx +import { CodeEditor } from '@leafygreen-ui/code-editor'; + +const sampleCode = `// Your code goes here +function greet(name: string): string { + return \`Hello, \${name}!\`; +} + +console.log(greet('MongoDB user'));`; + +; +``` + +#### Properties + +| Name | Description | Type | Default | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- | ----------- | +| `defaultValue` _(optional)_ | Initial value to render in the editor. | `string` | `undefined` | +| `enableActiveLineHighlighting` _(optional)_ | Enables highlighting of the active line. | `boolean` | `true` | +| `enableClickableUrls` _(optional)_ | Renders URLs as clickable links in the editor. | `boolean` | `true` | +| `enableCodeFolding` _(optional)_ | Enables code folding arrows in the gutter. | `boolean` | `true` | +| `enableLineNumbers` _(optional)_ | Enables line numbers in the editor’s gutter. | `boolean` | `true` | +| `enableLineWrapping` _(optional)_ | Enables line wrapping when the text exceeds the editor’s width. | `boolean` | `true` | +| `forceParsing` _(optional)_ | _**This should be used with caution as it can significantly impact performance!**_

Forces the parsing of the complete document, even parts not currently visible.

By default, the editor optimizes performance by only parsing the code that is visible on the screen, which is especially beneficial when dealing with large amounts of code. Enabling this option overrides this behavior and forces the parsing of all code, visible or not. This should generally be reserved for exceptional circumstances. | `boolean` | `false` | +| `onChange` _(optional)_ | Callback that receives the updated editor value when changes are made. | `(value: string) => void;` | `undefined` | +| `placeholder` _(optional)_ | Value to display in the editor when it is empty. | `HTMLElement \| string` | `undefined` | +| `readOnly` _(optional)_ | Enables read only mode, making the contents uneditable. | `boolean` | `false` | + +## Types + +| Name | Description | +| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CodeEditorProps` | Props that can be passed to the `CodeEditor` component. | +| `CodeEditorSelectors` | Enum-like map of CSS selectors for common elements that make up the code editor. These can be useful in testing. | +| `CodeMirrorExtension` | Underlying CodeMirror editor `Extension` type. For more information see https://codemirror.net/docs/ref/#state.Extension. | +| `CodeMirrorRef` | Underlying CodeMirror editor ref type. When a ref is passed to the `CodeEditor` it will be of this type and give you direct access to CodeMirror internals such as `CodeMirrorState` and `CodeMirrorView` | +| `CodeMirrorState` | Underlying CodeMirror editor `EditorState` type. For more information see https://codemirror.net/docs/ref/#state.EditorState. | +| `CodeMirrorView` | Underlying CodeMirror editor `EditorView` type. For more information see https://codemirror.net/docs/ref/#view.EditorView. | +| `RenderedTestResult` | Type returned by the `renderEditor` test utility. More info in Test Utilities section. | +| `RenderedTestEditorType` | Editor type used to interact with editor in a Jest test. More info in Test Utilities section. | + +## Test Utlities + +### `renderEditor` + +Renders the editor in a Jest test. + +```tsx +function renderEditor(props?: Partial): RenderResult; +``` + +### `RenderResult` + +#### `container` + +HTML element of the container of the editor that was rendered. + +#### `editor` + +Editor object used for querying and interacting with the rendered editor. + +Has the following interface: + +```tsx +{ + // Used to find single element in the editor. Will throw error if not found. + getBySelector( + selector: CodeEditorSelectors, + options?: { text?: string }, + ): Element; + + // Used to find single element in the editor. Will return null if not found. Useful when checking if something has not rendered. + queryBySelector(selector: CodeEditorSelectors, options?: { + text?: string; + }): Element | null; + + // Returns whether editor is in a read only state. + isReadOnly(): boolean; + + // Returns whether line wrapping is enabled in editor. + isLineWrappingEnabled(): boolean; + + // Group of actions that can be performed on the editor. + interactions: { + // Insert text into the editor + insertText(text: string, options?: { to?: number; from?: number; }): undefined; + } +} +``` + +### Examples + +#### Test selector has rendered + +```tsx +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + +test('Line numbers rendered', () => { + const { editor } = renderCodeEditor({ + defaultValue: 'content', + enableLineNumbers: true, + }); + expect( + editor.getBySelector(CodeEditorSelectors.GutterElement, { + text: '1', + }), + ).toBeInTheDocument(); +}); +``` + +#### Test selector has not rendered + +```tsx +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + +test('Fold gutter does not render', () => { + const { editor } = renderCodeEditor({ enableCodeFolding: false }); + expect( + // Note use of queryBy instead of getBy when test if not rendered + editor.queryBySelector(CodeEditorSelectors.FoldGutter), + ).not.toBeInTheDocument(); +}); +``` + +#### Test user interaction + +```tsx +import { TestUtils } from '@leafygreen-ui/code-editor'; + +const { renderEditor } = TestUtils; + +test('Updates value on when user types', () => { + const { editor } = renderCodeEditor(); + + expect( + editor.getBySelector(CodeEditorSelectors.Content), + ).not.toHaveTextContent('new content'); + + act(() => { + editor.interactions.insertText('new content'); + }); + + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( + 'new content', + ); +}); +``` diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json new file mode 100644 index 0000000000..09e81f95f6 --- /dev/null +++ b/packages/code-editor/package.json @@ -0,0 +1,45 @@ +{ + "name": "@leafygreen-ui/code-editor", + "version": "0.1.0", + "description": "LeafyGreen UI Kit Code Editor", + "main": "./dist/umd/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "license": "Apache-2.0", + "exports": { + ".": { + "require": "./dist/umd/index.js", + "import": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts" + } + }, + "scripts": { + "build": "lg-build bundle", + "tsc": "lg-build tsc", + "docs": "lg-build docs" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@codemirror/language": "^6.11.0", + "@leafygreen-ui/emotion": "workspace:^", + "@leafygreen-ui/hooks": "workspace:^", + "@leafygreen-ui/leafygreen-provider": "workspace:^", + "@leafygreen-ui/lib": "workspace:^", + "@leafygreen-ui/tokens": "workspace:^", + "@uiw/codemirror-extensions-hyper-link": "^4.23.12", + "@uiw/react-codemirror": "^4.23.10" + }, + "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/code-editor", + "repository": { + "type": "git", + "url": "https://github.com/mongodb/leafygreen-ui" + }, + "bugs": { + "url": "https://jira.mongodb.org/projects/LG/summary" + }, + "devDependencies": { + "@lg-tools/build": "workspace:^" + } +} diff --git a/packages/code-editor/src/CodeEditor.stories.tsx b/packages/code-editor/src/CodeEditor.stories.tsx new file mode 100644 index 0000000000..adae9d9793 --- /dev/null +++ b/packages/code-editor/src/CodeEditor.stories.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { StoryFn } from '@storybook/react'; + +import { CodeEditor } from '.'; + +export default { + title: 'Components/CodeEditor', + component: CodeEditor, + decorators: [ + Story => ( +
+ +
+ ), + ], + args: { + enableActiveLineHighlighting: true, + enableClickableUrls: true, + enableCodeFolding: true, + enableLineNumbers: true, + enableLineWrapping: true, + defaultValue: '', + forceParsing: false, + placeholder: 'Type your code here...', + readOnly: false, + }, + argTypes: { + enableActiveLineHighlighting: { + control: { type: 'boolean' }, + }, + enableClickableUrls: { + control: { type: 'boolean' }, + }, + enableCodeFolding: { + control: { type: 'boolean' }, + }, + enableLineNumbers: { + control: { type: 'boolean' }, + }, + enableLineWrapping: { + control: { type: 'boolean' }, + }, + placeholder: { + control: { type: 'text' }, + }, + readOnly: { + control: { type: 'boolean' }, + }, + defaultValue: { + control: { type: 'text' }, + }, + }, +}; + +const Template: StoryFn = args => ; + +export const LiveExample = Template.bind({}); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx new file mode 100644 index 0000000000..7bdb4b6918 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.spec.tsx @@ -0,0 +1,160 @@ +import { forceParsing } from '@codemirror/language'; +import { act } from '@testing-library/react'; + +import { renderCodeEditor } from './CodeEditor.testUtils'; +import { CodeEditorSelectors } from '.'; + +global.MutationObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), + takeRecords: jest.fn().mockReturnValue([]), +})); + +jest.mock('@codemirror/language', () => { + const actualModule = jest.requireActual('@codemirror/language'); + return { + ...actualModule, + forceParsing: jest.fn(), + }; +}); + +describe('packages/code-editor', () => { + test('Renders default value in editor', () => { + const { container } = renderCodeEditor({ defaultValue: 'content' }); + expect(container).toHaveTextContent('content'); + }); + + test('Updates value on when user types', () => { + const { editor } = renderCodeEditor(); + expect( + editor.getBySelector(CodeEditorSelectors.Content), + ).not.toHaveTextContent('new content'); + act(() => { + editor.interactions.insertText('new content'); + }); + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( + 'new content', + ); + }); + + test('Fold gutter renders when enabled', () => { + const { editor } = renderCodeEditor({ enableCodeFolding: true }); + expect( + editor.getBySelector(CodeEditorSelectors.FoldGutter), + ).toBeInTheDocument(); + }); + + test('Fold gutter does not render when disabled', () => { + const { editor } = renderCodeEditor({ enableCodeFolding: false }); + expect( + editor.queryBySelector(CodeEditorSelectors.FoldGutter), + ).not.toBeInTheDocument(); + }); + + test('Line numbers render when enabled', () => { + const { editor } = renderCodeEditor({ + defaultValue: 'content', + enableLineNumbers: true, + }); + expect( + editor.getBySelector(CodeEditorSelectors.GutterElement, { + text: '1', + }), + ).toBeInTheDocument(); + }); + + test('Line numbers do not renders when disabled', () => { + const { editor } = renderCodeEditor({ enableLineNumbers: false }); + expect( + editor.queryBySelector(CodeEditorSelectors.GutterElement, { text: '1' }), + ).not.toBeInTheDocument(); + }); + + test('Active line highlighting renders when enabled', () => { + const { editor } = renderCodeEditor({ enableActiveLineHighlighting: true }); + expect( + editor.getBySelector(CodeEditorSelectors.ActiveLine), + ).toBeInTheDocument(); + expect( + editor.getBySelector(CodeEditorSelectors.ActiveLineGutter, { text: '1' }), + ).toBeInTheDocument(); + }); + + test('Active line highlighting does not render when disabled', () => { + const { editor } = renderCodeEditor({ + enableActiveLineHighlighting: false, + }); + expect( + editor.queryBySelector(CodeEditorSelectors.ActiveLine), + ).not.toBeInTheDocument(); + expect( + editor.queryBySelector(CodeEditorSelectors.ActiveLineGutter, { + text: '1', + }), + ).not.toBeInTheDocument(); + }); + + test('Clickable URLs render when enabled', () => { + const { editor } = renderCodeEditor({ + defaultValue: 'https://mongodb.design', + enableClickableUrls: true, + }); + expect( + editor.getBySelector(CodeEditorSelectors.HyperLink), + ).toBeInTheDocument(); + }); + + test('Clickable URLs do not render when disable', () => { + const { editor } = renderCodeEditor({ + defaultValue: 'https://mongodb.design', + enableClickableUrls: false, + }); + expect( + editor.queryBySelector(CodeEditorSelectors.HyperLink), + ).not.toBeInTheDocument(); + }); + + test('Read-only set on editor state when enabled', () => { + const { editor } = renderCodeEditor({ readOnly: true }); + expect(editor.isReadOnly()).toBe(true); + }); + + test('Read-only not set on editor state when disabled', () => { + const { editor } = renderCodeEditor({ readOnly: false }); + expect(editor.isReadOnly()).toBe(false); + }); + + test('Line wrapping enabled when enabled', () => { + const { editor } = renderCodeEditor({ enableLineWrapping: true }); + expect(editor.isLineWrappingEnabled()).toBe(true); + }); + + test('Line wrapping not enabled when disabled', () => { + const { editor } = renderCodeEditor({ enableLineWrapping: false }); + expect(editor.isLineWrappingEnabled()).toBe(false); + }); + + test('Editor displays placeholder when empty', () => { + const { editor } = renderCodeEditor({ + placeholder: 'Type your code here...', + }); + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( + 'Type your code here...', + ); + }); + + test('Editor displays HTMLElement placeholder when empty', () => { + const placeholderElement = document.createElement('div'); + placeholderElement.textContent = 'Type your code here...'; + const { editor } = renderCodeEditor({ placeholder: placeholderElement }); + expect(editor.getBySelector(CodeEditorSelectors.Content)).toHaveTextContent( + 'Type your code here...', + ); + }); + + test('the forceParsing() method is called when enabled', () => { + renderCodeEditor({ forceParsing: true, defaultValue: 'content' }); + expect(forceParsing as jest.Mock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx new file mode 100644 index 0000000000..cb785b165b --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.testUtils.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ChangeSpec } from '@uiw/react-codemirror'; + +import { + CodeEditor, + CodeEditorProps, + CodeEditorSelectors, + CodeMirrorView, +} from '.'; + +let editorView: CodeMirrorView; + +function getBySelector( + selector: CodeEditorSelectors, + options?: { text?: string }, +) { + const elements = editorView.dom.querySelectorAll(selector); + + if (!elements || elements.length === 0) { + throw new Error(`Element with selector "${selector}" not found`); + } + + if (elements.length > 1) { + if (!options?.text) { + throw new Error(`Multiple elements with selector "${selector}" found`); + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if (!matchingElements || matchingElements.length === 0) { + throw new Error( + `Element with selector "${selector}" and text "${options.text}" not found`, + ); + } + + if (matchingElements.length > 1) { + throw new Error( + `Multiple elements with selector "${selector}" and text "${options.text}" found`, + ); + } + + return matchingElements[0]; + } + + return elements[0]; +} + +function queryBySelector( + selector: CodeEditorSelectors, + options?: { text?: string }, +) { + const elements = editorView.dom.querySelectorAll(selector); + + if (!elements || elements.length === 0) { + return null; + } + + if (elements.length > 1) { + if (!options?.text) { + return null; + } + + const matchingElements = Array.from(elements).filter(element => { + return options.text && element.textContent?.includes(options.text); + }); + + if ( + !matchingElements || + matchingElements.length === 0 || + matchingElements.length > 1 + ) { + return null; + } + + return matchingElements[0]; + } + + return elements[0]; +} + +function isReadOnly() { + return editorView.state.readOnly; +} + +function isLineWrappingEnabled() { + return !!queryBySelector(CodeEditorSelectors.LineWrapping); +} + +function insertText(text: string, options?: { from?: number; to?: number }) { + if (!editorView) { + throw new Error('Editor view is not initialized'); + } + + const changes: ChangeSpec = { insert: text, from: options?.from || 0 }; + + if (options?.to) { + changes.to = options.to; + } + + const transaction = editorView.state.update({ + changes, + }); + + editorView.dispatch(transaction); +} + +export const editor = { + getBySelector, + queryBySelector, + isLineWrappingEnabled, + isReadOnly, + interactions: { + insertText, + }, +}; + +export function renderCodeEditor(props: Partial = {}) { + const { container } = render( + { + if (ref && ref.view) { + editorView = ref.view; + } + }} + />, + ); + + return { container, editor }; +} diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.tsx b/packages/code-editor/src/CodeEditor/CodeEditor.tsx new file mode 100644 index 0000000000..24037fb097 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.tsx @@ -0,0 +1,114 @@ +import React, { + forwardRef, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; +import { forceParsing } from '@codemirror/language'; +import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'; +import CodeMirror, { Compartment, EditorView } from '@uiw/react-codemirror'; + +import { useMergeRefs } from '@leafygreen-ui/hooks'; + +import { + type CodeEditorProps, + type CodeMirrorExtension, + type CodeMirrorRef, +} from './CodeEditor.types'; + +const CODE_MIRROR_HEIGHT = '200px'; +const CODE_MIRROR_WIDTH = '100%'; + +export const CodeEditor = forwardRef( + ( + { + defaultValue, + enableActiveLineHighlighting = true, + enableClickableUrls = true, + enableCodeFolding = true, + enableLineNumbers = true, + enableLineWrapping = true, + forceParsing: forceParsingProp = false, + onChange: onChangeProp, + placeholder, + readOnly = false, + ...rest + }, + forwardedRef, + ) => { + const [value, setValue] = useState(defaultValue || ''); + const editorRef = useRef(null); + + const onChange = useCallback( + (val: string) => { + setValue(val); + + if (onChangeProp) { + onChangeProp(val); + } + }, + [onChangeProp], + ); + + const onCreateEditor = useCallback( + (editorView: EditorView) => { + if (forceParsingProp) { + const { state } = editorView; + + if (state.doc.length > 0) { + forceParsing(editorView, state.doc.length, 150); + } + } + }, + [forceParsingProp], + ); + + const extensions = useMemo(() => { + const extensions: Array = []; + + /** + * CodeMirror state is immutable. Once configuration is set, the entire + * state would need to be updated to update one facet. Compartments allow + * us to dynamically change parts of the configuration after + * initialization, without needing to recreate the entire editor state. + * See https://codemirror.net/examples/config/#dynamic-configuration + */ + const hyperLinkCompartment = new Compartment(); + const lineWrappingCompartment = new Compartment(); + + extensions.push( + hyperLinkCompartment.of(enableClickableUrls ? hyperLink : []), + lineWrappingCompartment.of( + enableLineWrapping ? EditorView.lineWrapping : [], + ), + ); + + return extensions; + }, [enableClickableUrls, enableLineWrapping]); + + return ( + + ); + }, +); + +CodeEditor.displayName = 'CodeEditor'; diff --git a/packages/code-editor/src/CodeEditor/CodeEditor.types.ts b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts new file mode 100644 index 0000000000..c4f7a505f8 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/CodeEditor.types.ts @@ -0,0 +1,110 @@ +import type { + EditorState, + EditorView, + Extension, + ReactCodeMirrorRef, +} from '@uiw/react-codemirror'; + +import { DarkModeProps } from '@leafygreen-ui/lib'; + +/** + * Re-export of CodeMirror's {@link Extension} type. + * Extensions provide additional functionality to the editor. + * @see {@link https://codemirror.net/docs/ref/#state.Extension} + */ +export type CodeMirrorExtension = Extension; + +/** + * Re-export of CodeMirror's {@link ReactCodeMirrorRef} type. + * Provides access to the editor instance through React refs. + * @see {@link https://uiwjs.github.io/react-codemirror/#/basic} + */ +export type CodeMirrorRef = ReactCodeMirrorRef; + +/** + * Re-export of CodeMirror's {@link EditorState} type. + * Represents the complete editor state configuration. + * @see {@link https://codemirror.net/docs/ref/#state.EditorState} + */ +export type CodeMirrorState = EditorState; + +/** + * Re-export of CodeMirror's {@link EditorView} type. + * The main editor component that displays and manages the document. + * @see {@link https://codemirror.net/docs/ref/#view.EditorView} + */ +export type CodeMirrorView = EditorView; + +/** + * The important elements in the code mirror editor have regular (non-generated) + * CSS class names, which can be targeted. For example, the outer element has + * class cm-editor. This is a mapping of those selectors which can be used for + * both testing and styling. + */ +export const CodeEditorSelectors = { + ActiveLine: '.cm-activeLine', + ActiveLineGutter: '.cm-activeLineGutter', + Content: '.cm-content', + FoldGutter: '.cm-foldGutter', + GutterElement: '.cm-gutterElement', + HyperLink: '.cm-hyper-link-icon', + LineWrapping: '.cm-lineWrapping', +} as const; +export type CodeEditorSelectors = + (typeof CodeEditorSelectors)[keyof typeof CodeEditorSelectors]; + +export interface CodeEditorProps extends DarkModeProps { + /** + * Initial value to render in the editor. + */ + defaultValue?: string; + + /** + * Enables highlighting of the active line. + */ + enableActiveLineHighlighting?: boolean; + + /** + * Renders URLs as clickable links in the editor. + */ + enableClickableUrls?: boolean; + + /** + * Enables code folding arrows in the gutter. + */ + enableCodeFolding?: boolean; + + /** + * Enables line numbers in the editor’s gutter. + */ + enableLineNumbers?: boolean; + + /** + * Enables line wrapping when the text exceeds the editor’s width. + */ + enableLineWrapping?: boolean; + + /** + * Forces parsing of complete document, even if it is not visible. USE WITH CAUTION! + * By default the editor only parses the visible part of the code on the + * screen. This significantly increases performance when there is a lot of + * code rendered. When enabled, this forces parsing of non-visible code. + * This should only be used in exceptional cases. + */ + forceParsing?: boolean; + + /** + * Callback that receives the updated editor value when changes are made. + */ + onChange?: (value: string) => void; + + /** + * Value to display in the editor when it is empty. + */ + placeholder?: HTMLElement | string; + + /** + * Enables read only mode, making the contents uneditable. + */ + readOnly?: boolean; +} diff --git a/packages/code-editor/src/CodeEditor/index.ts b/packages/code-editor/src/CodeEditor/index.ts new file mode 100644 index 0000000000..ceeb397210 --- /dev/null +++ b/packages/code-editor/src/CodeEditor/index.ts @@ -0,0 +1,10 @@ +export { CodeEditor } from './CodeEditor'; +export { renderCodeEditor } from './CodeEditor.testUtils'; +export { + type CodeEditorProps, + CodeEditorSelectors, + type CodeMirrorExtension, + type CodeMirrorRef, + type CodeMirrorState, + type CodeMirrorView, +} from './CodeEditor.types'; diff --git a/packages/code-editor/src/index.ts b/packages/code-editor/src/index.ts new file mode 100644 index 0000000000..a532790cfe --- /dev/null +++ b/packages/code-editor/src/index.ts @@ -0,0 +1,15 @@ +import { renderCodeEditor } from './CodeEditor'; + +export const TestUtils = { + renderCodeEditor, +}; + +export { + CodeEditor, + type CodeEditorProps, + CodeEditorSelectors, + type CodeMirrorExtension, + type CodeMirrorRef, + type CodeMirrorState, + type CodeMirrorView, +} from './CodeEditor'; diff --git a/packages/code-editor/tsconfig.json b/packages/code-editor/tsconfig.json new file mode 100644 index 0000000000..93bd5b7ef8 --- /dev/null +++ b/packages/code-editor/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@lg-tools/build/config/package.tsconfig.json", + "compilerOptions": { + "paths": { + "@leafygreen-ui/icon/dist/*": ["../icon/src/generated/*"], + "@leafygreen-ui/*": ["../*/src"] + } + }, + "include": ["src/**/*"], + "exclude": ["**/*.spec.*", "**/*.stories.*"], + "references": [ + { + "path": "../emotion" + }, + { + "path": "../hooks" + }, + { + "path": "../lib" + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f216121eec..a3dbd800c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1181,6 +1181,37 @@ importers: specifier: ^4.2.2 version: 4.3.1 + packages/code-editor: + dependencies: + '@codemirror/language': + specifier: ^6.11.0 + version: 6.11.0 + '@leafygreen-ui/emotion': + specifier: workspace:^ + version: link:../emotion + '@leafygreen-ui/hooks': + specifier: workspace:^ + version: link:../hooks + '@leafygreen-ui/leafygreen-provider': + specifier: workspace:^ + version: link:../leafygreen-provider + '@leafygreen-ui/lib': + specifier: workspace:^ + version: link:../lib + '@leafygreen-ui/tokens': + specifier: workspace:^ + version: link:../tokens + '@uiw/codemirror-extensions-hyper-link': + specifier: ^4.23.12 + version: 4.23.12(@codemirror/state@6.5.2)(@codemirror/view@6.36.5) + '@uiw/react-codemirror': + specifier: ^4.23.10 + version: 4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + devDependencies: + '@lg-tools/build': + specifier: workspace:^ + version: link:../../tools/build + packages/combobox: dependencies: '@leafygreen-ui/checkbox': @@ -1795,7 +1826,7 @@ importers: version: 11.0.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250521) + version: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250523) xml2json: specifier: ^0.12.0 version: 0.12.0 @@ -3690,10 +3721,10 @@ importers: version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@storybook/react': specifier: 8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) '@storybook/react-webpack5': specifier: 8.6.12 - version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + version: 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) '@storybook/test': specifier: 8.6.12 version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) @@ -3702,7 +3733,7 @@ importers: version: 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@svgr/webpack': specifier: 8.0.1 - version: 8.0.1(typescript@5.9.0-dev.20250521) + version: 8.0.1(typescript@5.9.0-dev.20250523) assert: specifier: ^2.1.0 version: 2.1.0 @@ -3744,7 +3775,7 @@ importers: version: 18.2.0 react-docgen-typescript: specifier: 2.2.2 - version: 2.2.2(typescript@5.9.0-dev.20250521) + version: 2.2.2(typescript@5.9.0-dev.20250523) react-dom: specifier: ^17.0.0 || ^18.0.0 version: 18.2.0(react@18.2.0) @@ -3895,7 +3926,7 @@ importers: version: 11.1.1 jest: specifier: 29.6.2 - version: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + version: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-axe: specifier: 8.0.0 version: 8.0.0 @@ -4919,6 +4950,30 @@ packages: '@changesets/write@0.2.3': resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} + '@codemirror/autocomplete@6.18.6': + resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} + + '@codemirror/commands@6.8.1': + resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==} + + '@codemirror/language@6.11.0': + resolution: {integrity: sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==} + + '@codemirror/lint@6.8.5': + resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} + + '@codemirror/search@6.5.10': + resolution: {integrity: sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==} + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/theme-one-dark@6.1.2': + resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} + + '@codemirror/view@6.36.5': + resolution: {integrity: sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -5407,12 +5462,24 @@ packages: '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mdx-js/react@2.3.0': resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: @@ -6460,6 +6527,34 @@ packages: resolution: {integrity: sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@uiw/codemirror-extensions-basic-setup@4.23.10': + resolution: {integrity: sha512-zpbmSeNs3OU/f/Eyd6brFnjsBUYwv2mFjWxlAsIRSwTlW+skIT60rQHFBSfsj/5UVSxSLWVeUYczN7AyXvgTGQ==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/codemirror-extensions-hyper-link@4.23.12': + resolution: {integrity: sha512-PVknlqnZNXD9klgYgzCoeCV6yyr7wCrs61igXgavMn2U6MaOuTFsJW6i8PUQc/+8YRGnRzrWyTu5hp8ljqhiDg==} + peerDependencies: + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/react-codemirror@4.23.10': + resolution: {integrity: sha512-AbN4eVHOL4ckRuIXpZxkzEqL/1ChVA+BSdEnAKjIB68pLQvKsVoYbiFP8zkXkYc4+Fcgq5KbAjvYqdo4ewemKw==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} @@ -7065,6 +7160,9 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + codemirror@6.0.1: + resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} + collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -7169,6 +7267,9 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + cross-spawn-async@2.2.5: resolution: {integrity: sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==} deprecated: cross-spawn no longer requires a build toolchain, use it instead @@ -10457,6 +10558,9 @@ packages: peerDependencies: webpack: ^5.0.0 + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} @@ -10757,8 +10861,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.9.0-dev.20250521: - resolution: {integrity: sha512-wxrj9XlTy2D5iqAPWHWKgnXGkHVTgouDmMPhGrxqcX1z5STJKqDCXnebUFKM7xXahfEPtWFIyN7ouyg2G56e7g==} + typescript@5.9.0-dev.20250523: + resolution: {integrity: sha512-igDelnnCrlPHezUmn1QseV7BBn7bITue651a+X+JSKkBDwrvyy5vqd1G6dvM3vrc4PlInB5F6jQDp/23kg9ZEA==} engines: {node: '>=14.17'} hasBin: true @@ -10951,6 +11055,9 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} @@ -13077,6 +13184,58 @@ snapshots: human-id: 1.0.2 prettier: 2.8.8 + '@codemirror/autocomplete@6.18.6': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + + '@codemirror/commands@6.8.1': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + + '@codemirror/language@6.11.0': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/lint@6.8.5': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + crelt: 1.0.6 + + '@codemirror/search@6.5.10': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + crelt: 1.0.6 + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/theme-one-dark@6.1.2': + dependencies: + '@codemirror/language': 6.11.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + '@lezer/highlight': 1.2.1 + + '@codemirror/view@6.36.5': + dependencies: + '@codemirror/state': 6.5.2 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -13493,7 +13652,7 @@ snapshots: - ts-node optional: true - '@jest/core@29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521))': + '@jest/core@29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523))': dependencies: '@jest/console': 29.6.2 '@jest/reporters': 29.6.2 @@ -13507,7 +13666,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.5.0 - jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -13677,6 +13836,16 @@ snapshots: '@juggle/resize-observer@3.4.0': {} + '@lezer/common@1.2.3': {} + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.1 @@ -13693,6 +13862,8 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@marijn/find-cluster-break@1.0.2': {} + '@mdx-js/react@2.3.0(react@18.2.0)': dependencies: '@types/mdx': 2.0.5 @@ -13976,7 +14147,7 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/builder-webpack5@8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/builder-webpack5@8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/core-webpack': 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@types/semver': 7.5.0 @@ -13986,7 +14157,7 @@ snapshots: constants-browserify: 1.0.0 css-loader: 6.8.1(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) es-module-lexer: 1.6.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) html-webpack-plugin: 5.5.3(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) magic-string: 0.30.17 path-browserify: 1.0.1 @@ -14004,7 +14175,7 @@ snapshots: webpack-hot-middleware: 2.25.4 webpack-virtual-modules: 0.6.2 optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@swc/core' - esbuild @@ -14122,11 +14293,11 @@ snapshots: dependencies: storybook: 8.6.12(prettier@3.5.3) - '@storybook/preset-react-webpack@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/preset-react-webpack@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/core-webpack': 8.6.12(storybook@8.6.12(prettier@3.5.3)) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) + '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)) '@types/semver': 7.5.0 find-up: 5.0.0 magic-string: 0.30.17 @@ -14139,7 +14310,7 @@ snapshots: tsconfig-paths: 4.2.0 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -14156,16 +14327,16 @@ snapshots: dependencies: storybook: 8.6.12(prettier@3.5.3) - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2))': dependencies: debug: 4.4.0 endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.0.4 micromatch: 4.0.5 - react-docgen-typescript: 2.2.2(typescript@5.9.0-dev.20250521) + react-docgen-typescript: 2.2.2(typescript@5.9.0-dev.20250523) tslib: 2.6.2 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) transitivePeerDependencies: - supports-color @@ -14182,16 +14353,16 @@ snapshots: react-dom: 18.2.0(react@18.2.0) storybook: 8.6.12(prettier@3.5.3) - '@storybook/react-webpack5@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/react-webpack5@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: - '@storybook/builder-webpack5': 8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) - '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521) + '@storybook/builder-webpack5': 8.6.12(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/preset-react-webpack': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) + '@storybook/react': 8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) storybook: 8.6.12(prettier@3.5.3) optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 transitivePeerDependencies: - '@storybook/test' - '@swc/core' @@ -14230,7 +14401,7 @@ snapshots: '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.5.3)) typescript: 5.8.3 - '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250521)': + '@storybook/react@8.6.12(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.5.3)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(storybook@8.6.12(prettier@3.5.3))(typescript@5.9.0-dev.20250523)': dependencies: '@storybook/components': 8.6.12(storybook@8.6.12(prettier@3.5.3)) '@storybook/global': 5.0.0 @@ -14243,7 +14414,7 @@ snapshots: storybook: 8.6.12(prettier@3.5.3) optionalDependencies: '@storybook/test': 8.6.12(storybook@8.6.12(prettier@3.5.3)) - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 '@storybook/test@8.5.3(storybook@8.6.12(prettier@3.5.3))': dependencies: @@ -14410,12 +14581,12 @@ snapshots: - supports-color - typescript - '@svgr/core@8.0.0(typescript@5.9.0-dev.20250521)': + '@svgr/core@8.0.0(typescript@5.9.0-dev.20250523)': dependencies: '@babel/core': 7.24.3 '@svgr/babel-preset': 8.0.0(@babel/core@7.24.3) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250521) + cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250523) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -14460,11 +14631,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@svgr/plugin-jsx@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))': + '@svgr/plugin-jsx@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))': dependencies: '@babel/core': 7.24.3 '@svgr/babel-preset': 8.0.0(@babel/core@7.24.3) - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: @@ -14495,10 +14666,10 @@ snapshots: transitivePeerDependencies: - typescript - '@svgr/plugin-svgo@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))(typescript@5.9.0-dev.20250521)': + '@svgr/plugin-svgo@8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))(typescript@5.9.0-dev.20250523)': dependencies: - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) - cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) + cosmiconfig: 8.3.6(typescript@5.9.0-dev.20250523) deepmerge: 4.3.1 svgo: 3.0.2 transitivePeerDependencies: @@ -14529,16 +14700,16 @@ snapshots: - supports-color - typescript - '@svgr/webpack@8.0.1(typescript@5.9.0-dev.20250521)': + '@svgr/webpack@8.0.1(typescript@5.9.0-dev.20250523)': dependencies: '@babel/core': 7.24.3 '@babel/plugin-transform-react-constant-elements': 7.22.5(@babel/core@7.24.3) '@babel/preset-env': 7.24.3(@babel/core@7.24.3) '@babel/preset-react': 7.24.1(@babel/core@7.24.3) '@babel/preset-typescript': 7.24.1(@babel/core@7.24.3) - '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250521) - '@svgr/plugin-jsx': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521)) - '@svgr/plugin-svgo': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250521))(typescript@5.9.0-dev.20250521) + '@svgr/core': 8.0.0(typescript@5.9.0-dev.20250523) + '@svgr/plugin-jsx': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523)) + '@svgr/plugin-svgo': 8.0.1(@svgr/core@8.0.0(typescript@5.9.0-dev.20250523))(typescript@5.9.0-dev.20250523) transitivePeerDependencies: - supports-color - typescript @@ -15034,6 +15205,38 @@ snapshots: '@typescript-eslint/types': 8.17.0 eslint-visitor-keys: 4.2.0 + '@uiw/codemirror-extensions-basic-setup@4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)': + dependencies: + '@codemirror/autocomplete': 6.18.6 + '@codemirror/commands': 6.8.1 + '@codemirror/language': 6.11.0 + '@codemirror/lint': 6.8.5 + '@codemirror/search': 6.5.10 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + + '@uiw/codemirror-extensions-hyper-link@4.23.12(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + + '@uiw/react-codemirror@4.23.10(@babel/runtime@7.24.1)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.5)(codemirror@6.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@babel/runtime': 7.24.1 + '@codemirror/commands': 6.8.1 + '@codemirror/state': 6.5.2 + '@codemirror/theme-one-dark': 6.1.2 + '@codemirror/view': 6.36.5 + '@uiw/codemirror-extensions-basic-setup': 4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5) + codemirror: 6.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + '@vitest/expect@2.0.5': dependencies: '@vitest/spy': 2.0.5 @@ -15806,6 +16009,16 @@ snapshots: co@4.6.0: {} + codemirror@6.0.1: + dependencies: + '@codemirror/autocomplete': 6.18.6 + '@codemirror/commands': 6.8.1 + '@codemirror/language': 6.11.0 + '@codemirror/lint': 6.8.5 + '@codemirror/search': 6.5.10 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.36.5 + collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -15879,14 +16092,14 @@ snapshots: optionalDependencies: typescript: 5.8.3 - cosmiconfig@8.3.6(typescript@5.9.0-dev.20250521): + cosmiconfig@8.3.6(typescript@5.9.0-dev.20250523): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 create-ecdh@4.0.4: dependencies: @@ -15912,6 +16125,8 @@ snapshots: create-require@1.1.1: {} + crelt@1.0.6: {} + cross-spawn-async@2.2.5: dependencies: lru-cache: 4.1.5 @@ -16282,7 +16497,7 @@ snapshots: dependencies: semver: 7.6.3 shelljs: 0.8.5 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 dunder-proto@1.0.1: dependencies: @@ -16907,7 +17122,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.0-dev.20250521)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.0-dev.20250523)(webpack@5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2)): dependencies: '@babel/code-frame': 7.24.2 chalk: 4.1.2 @@ -16921,7 +17136,7 @@ snapshots: schema-utils: 3.3.0 semver: 7.6.3 tapable: 2.2.1 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) form-data@2.5.1: @@ -17601,16 +17816,16 @@ snapshots: - ts-node optional: true - jest-cli@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest-cli@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: - '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) '@jest/test-result': 29.6.2 '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-config: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) jest-util: 29.7.0 jest-validate: 29.6.2 prompts: 2.4.2 @@ -17685,7 +17900,7 @@ snapshots: - supports-color optional: true - jest-config@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest-config@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: '@babel/core': 7.24.3 '@jest/test-sequencer': 29.6.2 @@ -17711,7 +17926,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.4.8 - ts-node: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521) + ts-node: 10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -17979,12 +18194,12 @@ snapshots: - ts-node optional: true - jest@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)): + jest@29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)): dependencies: - '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + '@jest/core': 29.6.2(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521)) + jest-cli: 29.6.2(@types/node@20.4.8)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -19419,9 +19634,9 @@ snapshots: dependencies: typescript: 5.8.3 - react-docgen-typescript@2.2.2(typescript@5.9.0-dev.20250521): + react-docgen-typescript@2.2.2(typescript@5.9.0-dev.20250523): dependencies: - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 react-docgen@7.0.3: dependencies: @@ -20217,6 +20432,8 @@ snapshots: dependencies: webpack: 5.88.0(@swc/core@1.4.2(@swc/helpers@0.5.1))(esbuild@0.24.2) + style-mod@4.1.2: {} + style-to-object@0.4.4: dependencies: inline-style-parser: 0.1.1 @@ -20373,7 +20590,7 @@ snapshots: optionalDependencies: '@swc/core': 1.4.2(@swc/helpers@0.5.1) - ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250521): + ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.17.12)(typescript@5.9.0-dev.20250523): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -20387,13 +20604,13 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.4.2(@swc/helpers@0.5.1) - ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250521): + ts-node@10.9.2(@swc/core@1.4.2(@swc/helpers@0.5.1))(@types/node@20.4.8)(typescript@5.9.0-dev.20250523): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -20407,7 +20624,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.0-dev.20250521 + typescript: 5.9.0-dev.20250523 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -20534,7 +20751,7 @@ snapshots: typescript@5.8.3: {} - typescript@5.9.0-dev.20250521: {} + typescript@5.9.0-dev.20250523: {} unbox-primitive@1.0.2: dependencies: @@ -20757,6 +20974,8 @@ snapshots: vm-browserify@1.1.2: {} + w3c-keyname@2.2.8: {} + w3c-xmlserializer@4.0.0: dependencies: xml-name-validator: 4.0.0 diff --git a/tools/install/src/ALL_PACKAGES.ts b/tools/install/src/ALL_PACKAGES.ts index e0ab5f78e4..f627dbd3ed 100644 --- a/tools/install/src/ALL_PACKAGES.ts +++ b/tools/install/src/ALL_PACKAGES.ts @@ -13,6 +13,7 @@ export const ALL_PACKAGES = [ '@leafygreen-ui/checkbox', '@leafygreen-ui/chip', '@leafygreen-ui/code', + '@leafygreen-ui/code-editor', '@leafygreen-ui/combobox', '@leafygreen-ui/confirmation-modal', '@leafygreen-ui/copyable',