@molecule/app-code-editor
Core interface · code-editor · App (browser) · v1.0.2 · Apache-2.0
Code editor interface with file management and syntax highlighting
npm install @molecule/app-code-editorHow it works
@molecule/app-code-editor is the code-editor core interface on the app (browser) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/app-code-editor-monaco.
import { setProvider, requireProvider } from '@molecule/app-code-editor'
import { provider } from '@molecule/app-code-editor-monaco'
setProvider(provider) // once, at app startup (bonds.ts)
const editor = requireProvider()
await editor.mount(containerElement, { theme: 'dark', fontSize: 13 })
editor.openFile({ path: 'src/index.ts', content: source, language: 'typescript' })
const unsubscribe = editor.onChange((event) => save(event.path, event.content))Providers (1): @molecule/app-code-editor-monaco
Works with: @molecule/app-bond, @molecule/app-i18n
Live demos
Running apps built with Molecule that install this package — open the app or study the template.
Reference
Code editor core interface for molecule.dev.
Framework-agnostic contract for an embeddable code editor (tabs, file
content, cursor, diagnostics, diff view, optional LSP). Bond a provider
(e.g. @molecule/app-code-editor-monaco) at startup, then drive the
editor through the provider anywhere.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, requireProvider } from '@molecule/app-code-editor'
import { provider } from '@molecule/app-code-editor-monaco'
setProvider(provider) // once, at app startup (bonds.ts)
const editor = requireProvider()
await editor.mount(containerElement, { theme: 'dark', fontSize: 13 })
editor.openFile({ path: 'src/index.ts', content: source, language: 'typescript' })
const unsubscribe = editor.onChange((event) => save(event.path, event.content))
Type
core
Installation
npm install @molecule/app-code-editor @molecule/app-bond @molecule/app-i18n
API
Interfaces
EditorFile
A file opened in the code editor, with its path, content, and language.
interface EditorFile {
path: string
content: string
language?: string
isDirty?: boolean
isPreview?: boolean
}
EditorPosition
A line/column cursor position in the editor.
interface EditorPosition {
line: number
column: number
}
EditorSelection
A text selection range defined by start and end positions.
interface EditorSelection {
start: EditorPosition
end: EditorPosition
}
EditorTab
Represents a tab in the editor's tab bar, tracking its file path, display label, dirty state, and active state.
interface EditorTab {
path: string
label: string
isDirty: boolean
isActive: boolean
isPreview?: boolean
/** Diff tabs show a side-by-side comparison and cannot be pinned. */
isDiff?: boolean
diagnostics?: { errors: number; warnings: number }
}
EditorConfig
Editor display and behavior configuration options.
interface EditorConfig {
theme?: string
fontFamily?: string
fontSize?: number
tabSize?: number
wordWrap?: boolean
minimap?: boolean
readOnly?: boolean
/** Allow scrolling past the last line (editor default: true). Disable on small screens to reclaim vertical space. */
scrollBeyondLastLine?: boolean
/** Show code-folding controls in the gutter (editor default: true). Disable on small screens for a narrower gutter. */
folding?: boolean
}
EditorState
Snapshot of the editor's current state including open tabs, active file, and cursor.
interface EditorState {
openFiles: EditorTab[]
activeFile: string | null
cursorPosition: EditorPosition | null
}
EditorChangeEvent
Event emitted when file content changes in the editor.
interface EditorChangeEvent {
path: string
content: string
version: number
}
EditorDiagnostic
A single diagnostic (error, warning, etc.) reported by the editor's language service.
interface EditorDiagnostic {
message: string
severity: 'error' | 'warning' | 'info' | 'hint'
startLine: number
startColumn: number
endLine: number
endColumn: number
source?: string
}
FixWithAIRequest
Request to interact with AI about code, triggered from the editor's lightbulb quick fix or right-click context menu.
interface FixWithAIRequest {
/** File path of the file containing the code. */
path: string
/** Diagnostics (errors/warnings) at the target location, if any. */
diagnostics: EditorDiagnostic[]
/** Source code surrounding the target location for context. */
codeContext?: string
/** Exact text the user had selected, if any. */
selectedCode?: string
/** Line number where the cursor or selection starts. */
line?: number
}
DiffFile
A file diff with original (committed) and modified (current) content, used to drive a side-by-side diff view in the editor.
interface DiffFile {
path: string
originalContent: string
modifiedContent: string
language?: string
}
EditorProvider
Code editor provider interface that all editor bond packages must implement. Provides mounting, file management, cursor control, and change subscription.
interface EditorProvider {
readonly name: string
mount(element: HTMLElement, config?: EditorConfig): void | Promise<void>
dispose(): void
openFile(file: EditorFile): void
closeFile(path: string): void
getContent(): string | null
setContent(path: string, content: string): void
/** Silently update file content without firing change events or marking dirty. Preserves cursor position. */
setContentSilent?(path: string, content: string): void
getCursorPosition(): EditorPosition | null
setCursorPosition(position: EditorPosition): void
focus(): void
onChange(callback: (event: EditorChangeEvent) => void): () => void
getTabs(): EditorTab[]
setActiveTab(path: string): void
updateConfig(config: Partial<EditorConfig>): void
/** Read the current effective editor configuration. Optional — lets consumers capture a baseline before applying temporary overrides (e.g. mobile viewport tuning). */
getConfig?(): EditorConfig
/** Open a side-by-side diff view for a file. Optional — providers may not support this. */
openDiff?(file: DiffFile): void
/** Close the diff view and restore the normal editor. */
closeDiff?(): void
/** Clear the dirty flag on a tab after a successful save. */
markSaved?(path: string): void
/** Promote a preview tab to a permanent tab. */
pinTab?(path: string): void
/** Add a type definition or virtual file for module resolution. */
addExtraLib?(content: string, filePath: string): void
/** Remove all registered extra libs. */
clearExtraLibs?(): void
/** Connect to an LSP server for language intelligence. */
connectLsp?(wsUrl: string): Promise<void>
/** Disconnect from the LSP server. */
disconnectLsp?(): void
/** Register a callback for when the LSP connection drops unexpectedly. */
onLspDisconnect?(cb: () => void): void
/** Set a file resolver for Go to Definition / Peek Definition cross-file navigation. */
setFileResolver?(
resolver: (path: string) => Promise<{ content: string; language?: string } | null>,
): void
/** Register a callback for "Fix with AI" requests from the editor (lightbulb quick fix or context menu). Returns an unsubscribe function. */
onFixWithAI?(callback: (request: FixWithAIRequest) => void): () => void
/** Get current diagnostics (errors/warnings) for a file from the language service. */
getDiagnostics?(path: string): EditorDiagnostic[]
}
Functions
setProvider(provider)
Registers a code editor provider as the active singleton.
function setProvider(provider: EditorProvider): void
provider— The editor provider implementation to bond.
getProvider()
Retrieves the bonded code editor provider, or null if none is bonded.
function getProvider(): EditorProvider | null
Returns: The bonded editor provider, or null.
hasProvider()
Checks whether a code editor provider is currently bonded.
function hasProvider(): boolean
Returns: true if a code editor provider is bonded.
requireProvider()
Retrieves the bonded code editor provider, throwing if none is configured.
function requireProvider(): EditorProvider
Returns: The bonded editor provider.
Available Providers
| Provider | Package |
|---|---|
| Monaco | @molecule/app-code-editor-monaco |
Injection Notes
Requirements
Peer dependencies:
@molecule/app-bond^1.0.1@molecule/app-i18n^1.0.1
Runtime Dependencies
-
@molecule/app-bond -
@molecule/app-i18n -
Wire the bond BEFORE any editor call.
setProvider(provider)in the app's bond setup;requireProvider()throws if nothing is bonded. -
mount()needs a real, attached DOM element and may be async (providers can lazy-load their engine). In component frameworks, mount in the after-render lifecycle hook (a ref/effect), and calldispose()on unmount — never re-mount over a live instance. -
Feature-detect optional capabilities.
openDiff,connectLsp,setContentSilent,markSaved,onFixWithAI,getDiagnostics, etc. are optional provider methods — checkeditor.openDiff?.(…)before relying on them. -
onChangereturns an unsubscribe function — keep it and call it on teardown to avoid leaked listeners across route changes. -
The editor renders its own chrome; any surrounding UI you add (toolbars, tab labels) styles via
getClassMap()/cm.*and localizes viat('key', values, { defaultValue }).
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. Drive it through the preview: navigate_preview to the screen that embeds the editor, read_preview_ui to read the rendered editor surface and its file tabs/controls, and interact_preview to click and type — target the app's own data-mol-id controls (file-tree entries, tab bar, save button), since the editor engine renders its own inner chrome. A box you can't check is an integration bug to fix — not a skip:
- Opening a file loads its content into the editor surface, and syntax
highlighting matches the file's language — a
.tsfile colorizes as TypeScript (keywords, strings, comments tokenized in distinct colors), not flat plain text. Opening a second file of a different language re-highlights for that language. - Typing into the editor edits the content and the surface reflects every
keystroke: read_preview_ui shows the new text, and
getContent()for that path returns it. - Changing content marks the file dirty — its tab/label shows the unsaved
indicator (dot/asterisk) — and saving clears it back to clean (
markSaved). After save, reopening the file (close the tab, open it again) shows the SAVED text, not the pre-edit content — the change was persisted, not just held in the buffer. - With multiple files open as tabs, switching tabs preserves each file's own content and cursor position: edit file A, switch to B, switch back, and A still has its edit with the caret where you left it (no bleed-over between files, no reset to the top).
- A file the app opens read-only (readOnly config) cannot be edited — typing or paste does not change its content and it never shows a dirty indicator.
- Change (and save) events fire so the app can react: whatever this app
wires onto
onChange(autosave, live validation/diagnostics, an unsaved guard on navigation) actually triggers when you edit and when you save.
Translations
Translation strings are provided by @molecule/app-locales-code-editor.