diff --git a/packages/ratio-ui-codeeditor/package.json b/packages/ratio-ui-codeeditor/package.json new file mode 100644 index 0000000..5b9b4df --- /dev/null +++ b/packages/ratio-ui-codeeditor/package.json @@ -0,0 +1,52 @@ +{ + "name": "@eventuras/ratio-ui-codeeditor", + "version": "0.0.0", + "description": "Alpha spike — CodeMirror 6 code editor with ratio-ui styling", + "license": "MPL-2.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./CodeEditor": { + "types": "./dist/CodeEditor/index.d.ts", + "import": "./dist/CodeEditor/index.js" + } + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc && vite build", + "lint": "eslint ." + }, + "dependencies": { + "@codemirror/lang-javascript": "^6", + "@codemirror/lang-json": "^6", + "@codemirror/lang-xml": "^6", + "@codemirror/language": "^6", + "@codemirror/lint": "^6", + "@codemirror/state": "^6", + "@codemirror/view": "^6", + "@lezer/highlight": "^1", + "codemirror": "^6" + }, + "devDependencies": { + "@ratio-ui/eslint-config": "workspace:*", + "@ratio-ui/typescript-config": "workspace:*", + "@ratio-ui/vite-config": "workspace:*", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "typescript": "6.0.3", + "vite": "8.1.3" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + } +} diff --git a/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.stories.tsx b/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.stories.tsx new file mode 100644 index 0000000..19fc346 --- /dev/null +++ b/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.stories.tsx @@ -0,0 +1,137 @@ +// ratio-ui · design system for knowledge sharing +// SPDX-FileCopyrightText: 2026 Losol AS +// SPDX-License-Identifier: MPL-2.0 + +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { CodeEditor, type EditorDiagnostic } from './CodeEditor'; + +/** + * A CodeMirror 6 editor wearing ratio-ui — spike for the FHIR-resource editor: + * write a JSON/XML resource, upload it, and see the server's `OperationOutcome` + * as **inline diagnostics** (squiggles + gutter marks). Colors follow the app's + * light/dark mode; toggle it to compare. + */ +const meta = { + title: 'Code/CodeEditor', + component: CodeEditor, + parameters: { layout: 'padded' }, + tags: ['autodocs'], + args: { value: '' }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const FHIR_PATIENT_JSON = `{ + "resourceType": "Patient", + "id": "example", + "active": true, + "name": [ + { + "use": "official", + "family": "Chalmers", + "given": ["Peter", "James"] + } + ], + "gender": "unknown-value", + "birthDate": "1974-12-25" +}`; + +// As if mapped from a FHIR OperationOutcome (line numbers → editor positions). +const FHIR_JSON_ISSUES: EditorDiagnostic[] = [ + { + line: 12, + severity: 'error', + message: + 'Patient.gender: "unknown-value" is not valid — required binding administrative-gender (male | female | other | unknown).', + }, + { + line: 5, + severity: 'warning', + message: 'Patient.name: consider adding a text representation (name.text) for display.', + }, +]; + +const FHIR_PATIENT_XML = ` + + + + + + + + + +`; + +const FHIR_XML_ISSUES: EditorDiagnostic[] = [ + { + line: 9, + severity: 'error', + message: 'Patient.gender: "unknown-value" is not in the required value set (administrative-gender).', + }, +]; + +const TS_SAMPLE = `import type { EditorDiagnostic } from '@eventuras/ratio-ui-codeeditor'; + +// Map a FHIR OperationOutcome issue to an inline editor diagnostic. +export function toDiagnostic(issue: { + severity: 'error' | 'warning' | 'information'; + diagnostics?: string; + line: number; +}): EditorDiagnostic { + return { + line: issue.line, + severity: issue.severity === 'information' ? 'info' : issue.severity, + message: issue.diagnostics ?? 'Validation issue', + }; +}`; + +/** FHIR Patient (JSON) with a validation error + a warning shown inline. */ +export const FhirJson: Story = { + render: function FhirJsonStory() { + const [value, setValue] = useState(FHIR_PATIENT_JSON); + return ( +
+ +
+ ); + }, +}; + +/** The same resource as FHIR XML, with an inline validation error. */ +export const FhirXml: Story = { + render: function FhirXmlStory() { + const [value, setValue] = useState(FHIR_PATIENT_XML); + return ( +
+ +
+ ); + }, +}; + +/** TypeScript — showing js/ts highlighting in the same ratio-ui theme. */ +export const TypeScript: Story = { + render: function TypeScriptStory() { + const [value, setValue] = useState(TS_SAMPLE); + return ( +
+ +
+ ); + }, +}; diff --git a/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.tsx b/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.tsx new file mode 100644 index 0000000..910dbf9 --- /dev/null +++ b/packages/ratio-ui-codeeditor/src/CodeEditor/CodeEditor.tsx @@ -0,0 +1,155 @@ +// ratio-ui · design system for knowledge sharing +// SPDX-FileCopyrightText: 2026 Losol AS +// SPDX-License-Identifier: MPL-2.0 + +'use client'; + +import React, { useEffect, useRef } from 'react'; +import { basicSetup } from 'codemirror'; +import { EditorView } from '@codemirror/view'; +import { EditorState, Compartment, type Extension } from '@codemirror/state'; +import { json } from '@codemirror/lang-json'; +import { xml } from '@codemirror/lang-xml'; +import { javascript } from '@codemirror/lang-javascript'; +import { lintGutter, setDiagnostics, type Diagnostic } from '@codemirror/lint'; +import { ratioChrome, highlightExtension, isDarkMode } from './theme'; + +export type CodeEditorLanguage = 'js' | 'ts' | 'json' | 'xml'; + +/** A diagnostic to render inline — e.g. mapped from a FHIR `OperationOutcome`. */ +export interface EditorDiagnostic { + /** 1-based line number the issue points at. */ + line: number; + message: string; + severity?: 'error' | 'warning' | 'info'; +} + +export interface CodeEditorProps { + value: string; + onChange?: (value: string) => void; + /** @default 'json' */ + language?: CodeEditorLanguage; + /** Externally-provided diagnostics rendered as inline squiggles + gutter marks. */ + diagnostics?: EditorDiagnostic[]; + readOnly?: boolean; + className?: string; + 'aria-label'?: string; +} + +function languageExtension(language: CodeEditorLanguage): Extension { + switch (language) { + case 'json': + return json(); + case 'xml': + return xml(); + case 'ts': + return javascript({ typescript: true }); + default: + return javascript(); + } +} + +function toCmDiagnostics(state: EditorState, diagnostics: EditorDiagnostic[]): Diagnostic[] { + const { doc } = state; + return diagnostics + .filter((d) => d.line >= 1 && d.line <= doc.lines) + .map((d) => { + const line = doc.line(d.line); + return { from: line.from, to: line.to, severity: d.severity ?? 'error', message: d.message }; + }); +} + +/** + * A CodeMirror 6 editor dressed in ratio-ui: chrome from the `--code-*` tokens, + * github light/dark syntax colors that follow the app's mode, JSON/XML/JS/TS + * languages, and inline `diagnostics` (squiggles + gutter). Controlled via + * `value` / `onChange`. + */ +export function CodeEditor({ + value, + onChange, + language = 'json', + diagnostics, + readOnly = false, + className, + 'aria-label': ariaLabel, +}: Readonly) { + const host = useRef(null); + const view = useRef(null); + const language_ = useRef(new Compartment()); + const readOnly_ = useRef(new Compartment()); + const highlight_ = useRef(new Compartment()); + const onChangeRef = useRef(onChange); + onChangeRef.current = onChange; + + // Create the editor once. + useEffect(() => { + if (!host.current) return; + const editor = new EditorView({ + parent: host.current, + state: EditorState.create({ + doc: value, + extensions: [ + basicSetup, + language_.current.of(languageExtension(language)), + readOnly_.current.of(EditorState.readOnly.of(readOnly)), + lintGutter(), + ratioChrome, + highlight_.current.of(highlightExtension(isDarkMode())), + EditorView.updateListener.of((u) => { + if (u.docChanged) onChangeRef.current?.(u.state.doc.toString()); + }), + ], + }), + }); + editor.dom.classList.add('ratio-cm'); + if (ariaLabel) editor.contentDOM.setAttribute('aria-label', ariaLabel); + view.current = editor; + + // Follow the app's light/dark mode: re-highlight when data-theme flips. + let observer: MutationObserver | undefined; + if (typeof MutationObserver !== 'undefined') { + observer = new MutationObserver(() => { + editor.dispatch({ effects: highlight_.current.reconfigure(highlightExtension(isDarkMode())) }); + }); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-theme', 'data-color-scheme'], + }); + } + + return () => { + observer?.disconnect(); + editor.destroy(); + view.current = null; + }; + // Created once; prop changes are synced by the effects below. + }, []); + + // NOTE (spike): `value` is the initial document only — the editor is + // uncontrolled after mount. This isolates whether the controlled value-sync + // was what wiped highlighting on edit. A robust external-value sync comes + // later (or remount via a `key`). + + // Reconfigure language / read-only. + useEffect(() => { + view.current?.dispatch({ effects: language_.current.reconfigure(languageExtension(language)) }); + }, [language]); + useEffect(() => { + view.current?.dispatch({ + effects: readOnly_.current.reconfigure(EditorState.readOnly.of(readOnly)), + }); + }, [readOnly]); + + // Push externally-provided diagnostics. + useEffect(() => { + const v = view.current; + if (!v) return; + v.dispatch(setDiagnostics(v.state, toCmDiagnostics(v.state, diagnostics ?? []))); + }, [diagnostics]); + + return
; +} + +CodeEditor.displayName = 'CodeEditor'; +export default CodeEditor; diff --git a/packages/ratio-ui-codeeditor/src/CodeEditor/index.ts b/packages/ratio-ui-codeeditor/src/CodeEditor/index.ts new file mode 100644 index 0000000..97d489e --- /dev/null +++ b/packages/ratio-ui-codeeditor/src/CodeEditor/index.ts @@ -0,0 +1,10 @@ +// ratio-ui · design system for knowledge sharing +// SPDX-FileCopyrightText: 2026 Losol AS +// SPDX-License-Identifier: MPL-2.0 + +export { + CodeEditor, + type CodeEditorProps, + type CodeEditorLanguage, + type EditorDiagnostic, +} from './CodeEditor'; diff --git a/packages/ratio-ui-codeeditor/src/CodeEditor/theme.ts b/packages/ratio-ui-codeeditor/src/CodeEditor/theme.ts new file mode 100644 index 0000000..ba95601 --- /dev/null +++ b/packages/ratio-ui-codeeditor/src/CodeEditor/theme.ts @@ -0,0 +1,115 @@ +// ratio-ui · design system for knowledge sharing +// SPDX-FileCopyrightText: 2026 Losol AS +// SPDX-License-Identifier: MPL-2.0 + +import { EditorView } from '@codemirror/view'; +import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'; +import { tags as t } from '@lezer/highlight'; +import type { Extension } from '@codemirror/state'; + +/** + * Editor chrome + tooltips — surface, gutter, cursor, lint popup. Uses ratio-ui's + * own `--code-*` tokens, which already flip with the app's mode, so the chrome + * re-themes via CSS. Scoped under the editor theme so it beats CodeMirror's own + * base themes (incl. the lint tooltip). + */ +export const ratioChrome: Extension = EditorView.theme({ + '&': { + backgroundColor: 'var(--code-surface)', + color: 'var(--code-text)', + fontSize: '13px', + borderRadius: 'var(--radius-lg, 12px)', + border: '1px solid var(--code-border, rgba(127,127,127,0.2))', + overflow: 'hidden', + }, + '.cm-scroller': { + fontFamily: 'var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)', + lineHeight: '1.6', + }, + '.cm-content': { padding: '10px 0', caretColor: 'var(--code-text)' }, + '&.cm-focused': { outline: 'none', boxShadow: 'var(--shadow-focus, 0 0 0 3px rgba(127,178,214,0.4))' }, + '.cm-gutters': { + backgroundColor: 'var(--code-surface)', + color: 'var(--code-gutter)', + border: 'none', + }, + '.cm-lineNumbers .cm-gutterElement': { padding: '0 8px 0 14px' }, + '.cm-activeLineGutter': { backgroundColor: 'transparent', color: 'var(--code-text)' }, + '.cm-activeLine': { backgroundColor: 'color-mix(in srgb, var(--code-text) 4%, transparent)' }, + '.cm-cursor, .cm-dropCursor': { borderLeftColor: 'var(--code-text)' }, + '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': { + backgroundColor: 'color-mix(in srgb, var(--code-accent, #7fb2d6) 28%, transparent)', + }, + '.cm-matchingBracket': { + backgroundColor: 'color-mix(in srgb, var(--code-accent, #7fb2d6) 22%, transparent)', + outline: 'none', + }, + // Tooltips (lint messages, autocomplete) — readable in dark mode. + '.cm-tooltip': { + backgroundColor: 'var(--code-header-bg, var(--code-surface))', + color: 'var(--code-text)', + border: '1px solid var(--code-border, rgba(127,127,127,0.25))', + borderRadius: 'var(--radius-md, 8px)', + boxShadow: '0 6px 22px rgba(0,0,0,0.28)', + }, + '.cm-tooltip.cm-tooltip-lint': { padding: '0', overflow: 'hidden' }, + '.cm-tooltip-lint .cm-diagnostic': { + fontFamily: 'var(--font-sans, system-ui, sans-serif)', + fontSize: '12.5px', + lineHeight: '1.45', + padding: '7px 11px', + color: 'var(--code-text)', + borderLeft: '3px solid transparent', + whiteSpace: 'normal', + }, + '.cm-diagnostic-error': { borderLeftColor: '#e5534b' }, + '.cm-diagnostic-warning': { borderLeftColor: '#d4a72c' }, + '.cm-diagnostic-info': { borderLeftColor: '#539bf5' }, +}); + +// GitHub palettes — fixed hex, so token colors always resolve (no dependency on +// injected CSS variables). Non-syntax text stays --code-text. +// +// Hues are assigned by *role* so a JSON key never reads like its value: +// blue = keys / XML tags · green = strings · orange = numbers +// red = booleans, null, keywords · purple = attributes, functions +const LIGHT = { + key: '#0550ae', str: '#0a7c42', num: '#953800', atom: '#cf222e', kw: '#cf222e', + com: '#6e7781', tag: '#0550ae', attr: '#8250df', fn: '#8250df', type: '#953800', +}; +const DARK = { + key: '#79c0ff', str: '#7ee787', num: '#ffa657', atom: '#ff7b72', kw: '#ff7b72', + com: '#8b949e', tag: '#79c0ff', attr: '#d2a8ff', fn: '#d2a8ff', type: '#ffa657', +}; + +function highlightStyle(dark: boolean): HighlightStyle { + const c = dark ? DARK : LIGHT; + return HighlightStyle.define([ + { tag: [t.propertyName, t.definition(t.propertyName)], color: c.key }, + { tag: [t.string, t.special(t.string), t.attributeValue], color: c.str }, + { tag: [t.number], color: c.num }, + { tag: [t.bool, t.null, t.atom], color: c.atom }, + { tag: [t.keyword, t.operator, t.modifier], color: c.kw }, + { tag: [t.comment, t.lineComment, t.blockComment], color: c.com, fontStyle: 'italic' }, + { tag: [t.tagName, t.angleBracket], color: c.tag }, + { tag: [t.attributeName], color: c.attr }, + { tag: [t.function(t.variableName), t.function(t.propertyName)], color: c.fn }, + { tag: [t.typeName, t.className], color: c.type }, + { + tag: [t.variableName, t.name, t.content, t.punctuation, t.separator, t.bracket, t.brace, t.squareBracket, t.paren], + color: 'var(--code-text)', + }, + ]); +} + +/** Syntax highlighting for the given mode (the only highlighter — no basicSetup default). */ +export function highlightExtension(dark: boolean): Extension { + return syntaxHighlighting(highlightStyle(dark)); +} + +/** Read the current mode from the document root (`data-theme` / `data-color-scheme`). */ +export function isDarkMode(): boolean { + if (typeof document === 'undefined') return false; + const r = document.documentElement; + return r.getAttribute('data-theme') === 'dark' || r.getAttribute('data-color-scheme') === 'dark'; +} diff --git a/packages/ratio-ui-codeeditor/src/index.ts b/packages/ratio-ui-codeeditor/src/index.ts new file mode 100644 index 0000000..97d489e --- /dev/null +++ b/packages/ratio-ui-codeeditor/src/index.ts @@ -0,0 +1,10 @@ +// ratio-ui · design system for knowledge sharing +// SPDX-FileCopyrightText: 2026 Losol AS +// SPDX-License-Identifier: MPL-2.0 + +export { + CodeEditor, + type CodeEditorProps, + type CodeEditorLanguage, + type EditorDiagnostic, +} from './CodeEditor'; diff --git a/packages/ratio-ui-codeeditor/tsconfig.json b/packages/ratio-ui-codeeditor/tsconfig.json new file mode 100644 index 0000000..9f3ac7b --- /dev/null +++ b/packages/ratio-ui-codeeditor/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ratio-ui/typescript-config/react-library.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "jsx": "react-jsx" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/ratio-ui-codeeditor/vite.config.ts b/packages/ratio-ui-codeeditor/vite.config.ts new file mode 100644 index 0000000..c62b0c0 --- /dev/null +++ b/packages/ratio-ui-codeeditor/vite.config.ts @@ -0,0 +1,21 @@ +import { defineReactLibConfig } from '@ratio-ui/vite-config/react-lib'; + +const banner = + '/*! @eventuras/ratio-ui-codeeditor · CodeMirror 6 editor with ratio-ui styling · ' + + 'https://github.com/losol/ratio-ui · (c) 2026 Losol AS · MPL-2.0 */'; + +export default defineReactLibConfig({ + entry: 'src/**/index.{ts,tsx}', + // CodeMirror packages must stay singletons — keep them external so the app + // dedupes one copy (multiple @codemirror/state instances break at runtime). + external: [/^@codemirror\//, /^@lezer\//, 'codemirror'], + viteConfig: { + build: { + rollupOptions: { + output: { + banner, + }, + }, + }, + }, +}); diff --git a/packages/ratio-ui-shiki/src/CodeBlock/CodeBlock.stories.tsx b/packages/ratio-ui-shiki/src/CodeBlock/CodeBlock.stories.tsx index 7c1168f..00bb96e 100644 --- a/packages/ratio-ui-shiki/src/CodeBlock/CodeBlock.stories.tsx +++ b/packages/ratio-ui-shiki/src/CodeBlock/CodeBlock.stories.tsx @@ -16,7 +16,7 @@ import { CodeBlock } from './CodeBlock'; * pre-computed `highlightedLines` (built via `shikiToDualLines` + `DUAL_THEME_CSS`). */ const meta = { - title: 'Shiki/CodeBlock', + title: 'Code/CodeBlock', component: CodeBlock, parameters: { layout: 'padded' }, tags: ['autodocs'], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90578e1..e90b6dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,6 +219,64 @@ importers: specifier: 8.1.3 version: 8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.5) + packages/ratio-ui-codeeditor: + dependencies: + '@codemirror/lang-javascript': + specifier: ^6 + version: 6.2.5 + '@codemirror/lang-json': + specifier: ^6 + version: 6.0.2 + '@codemirror/lang-xml': + specifier: ^6 + version: 6.1.0 + '@codemirror/language': + specifier: ^6 + version: 6.12.4 + '@codemirror/lint': + specifier: ^6 + version: 6.9.7 + '@codemirror/state': + specifier: ^6 + version: 6.7.1 + '@codemirror/view': + specifier: ^6 + version: 6.43.6 + '@lezer/highlight': + specifier: ^1 + version: 1.2.3 + codemirror: + specifier: ^6 + version: 6.0.2 + react: + specifier: ^19.0.0 + version: 19.2.7 + react-dom: + specifier: ^19.0.0 + version: 19.2.7(react@19.2.7) + devDependencies: + '@ratio-ui/eslint-config': + specifier: workspace:* + version: link:../../config/eslint-config + '@ratio-ui/typescript-config': + specifier: workspace:* + version: link:../../config/typescript-config + '@ratio-ui/vite-config': + specifier: workspace:* + version: link:../../config/vite-config + '@types/react': + specifier: ^19.2.17 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: 8.1.3 + version: 8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.5) + packages/ratio-ui-next: devDependencies: '@eventuras/ratio-ui': @@ -427,6 +485,36 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@codemirror/autocomplete@6.20.3': + resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} + + '@codemirror/commands@6.10.4': + resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==} + + '@codemirror/lang-javascript@6.2.5': + resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} + + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} + + '@codemirror/lang-xml@6.1.0': + resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==} + + '@codemirror/language@6.12.4': + resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==} + + '@codemirror/lint@6.9.7': + resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} + + '@codemirror/search@6.7.1': + resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==} + + '@codemirror/state@6.7.1': + resolution: {integrity: sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==} + + '@codemirror/view@6.43.6': + resolution: {integrity: sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==} + '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} @@ -856,12 +944,33 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lezer/common@1.5.2': + resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} + + '@lezer/highlight@1.2.3': + resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + + '@lezer/javascript@1.5.4': + resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} + + '@lezer/json@1.0.3': + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} + + '@lezer/lr@1.4.10': + resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} + + '@lezer/xml@1.0.6': + resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==} + '@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.3': + resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==} + '@mdx-js/react@3.1.1': resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} peerDependencies: @@ -2089,6 +2198,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + codemirror@6.0.2: + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -2104,6 +2216,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + crelt@1.0.7: + resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3136,6 +3251,9 @@ packages: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} + style-mod@4.1.3: + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -3449,6 +3567,9 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} @@ -3756,6 +3877,76 @@ snapshots: human-id: 4.2.0 prettier: 2.8.8 + '@codemirror/autocomplete@6.20.3': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + '@lezer/common': 1.5.2 + + '@codemirror/commands@6.10.4': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + '@lezer/common': 1.5.2 + + '@codemirror/lang-javascript@6.2.5': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + '@lezer/common': 1.5.2 + '@lezer/javascript': 1.5.4 + + '@codemirror/lang-json@6.0.2': + dependencies: + '@codemirror/language': 6.12.4 + '@lezer/json': 1.0.3 + + '@codemirror/lang-xml@6.1.0': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + '@lezer/common': 1.5.2 + '@lezer/xml': 1.0.6 + + '@codemirror/language@6.12.4': + dependencies: + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + style-mod: 4.1.3 + + '@codemirror/lint@6.9.7': + dependencies: + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + crelt: 1.0.7 + + '@codemirror/search@6.7.1': + dependencies: + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + crelt: 1.0.7 + + '@codemirror/state@6.7.1': + dependencies: + '@marijn/find-cluster-break': 1.0.3 + + '@codemirror/view@6.43.6': + dependencies: + '@codemirror/state': 6.7.1 + crelt: 1.0.7 + style-mod: 4.1.3 + w3c-keyname: 2.2.8 + '@emnapi/core@1.11.1': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -4059,6 +4250,34 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@lezer/common@1.5.2': {} + + '@lezer/highlight@1.2.3': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/javascript@1.5.4': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/json@1.0.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/lr@1.4.10': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/xml@1.0.6': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -4075,6 +4294,8 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@marijn/find-cluster-break@1.0.3': {} + '@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: '@types/mdx': 2.0.14 @@ -5171,6 +5392,16 @@ snapshots: clsx@2.1.1: {} + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.10.4 + '@codemirror/language': 6.12.4 + '@codemirror/lint': 6.9.7 + '@codemirror/search': 6.7.1 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.6 + comma-separated-tokens@2.0.3: {} compare-versions@6.1.1: {} @@ -5181,6 +5412,8 @@ snapshots: convert-source-map@2.0.0: {} + crelt@1.0.7: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -6275,6 +6508,8 @@ snapshots: strip-indent@4.1.1: {} + style-mod@4.1.3: {} + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 @@ -6522,6 +6757,8 @@ snapshots: vscode-uri@3.1.0: {} + w3c-keyname@2.2.8: {} + webpack-virtual-modules@0.6.2: {} which@2.0.2: