DreamLake

CodeBlock

A self-contained code surface. Hand it a string and it highlights the string — no build-time pipeline, no pre-rendered markup to pass in. Set editable and the same surface becomes an editor.

Read-only and editing are one CodeMirror view reconfigured, not two components swapped, so toggling between them keeps the syntax colors, the layout and the scroll position exactly where they were.

Read-only

The default. lang accepts a language name, alias or extension and is resolved against CodeMirror's 143-language registry; each grammar loads on demand, so a JSON block never downloads the Rust parser. Anything the registry doesn't recognise renders as plain text rather than throwing — which is what you want for a viewer pointed at arbitrary user files.

greet.tsts
export function greet(name: string) {
  const parts = name.trim().split(/\s+/)
  return `Hello, ${parts[0] ?? 'world'}!`
}

Editable

editable takes three values: false (read-only), true (always editable), and 'toggle' — read-only with an Edit button in the header. Escape leaves editing, which doubles as the escape hatch from Tab-to-indent for keyboard users.

value/onChange are controlled the same way as Input. There is no separate commit callback: onChange fires per keystroke, and onEditingChange tells you when the user left the editor if you want to save at that point instead.

dataset.jsonjson
{
  "workspace": "demo",
  "episodes": 12,
  "tracks": ["video", "joints", "actions"]
}

Unnamed content

Not everything you show is a file. A traceback pasted into a log viewer has no filename worth labelling, so this one passes none — and with no filename there is no header bar. Hover the block and Copy fades in over the top-right corner instead, which is what you want for a traceback someone is about to paste into an issue.

lang is still worth setting even when the content isn't source: a Python traceback is close enough to Python that the grammar colors its paths, line numbers and keywords usefully. wrap soft-wraps rather than scrolling sideways, and maxHeight caps the block and scrolls inside it.

To drop the tools as well, pass header={false}.

python
Traceback (most recent call last):
  File "/opt/dreamlake/pipeline/ingest.py", line 214, in _upload_chunk
    resp = self._session.put(url, data=buf, timeout=self.timeout, headers={"content-type": "application/octet-stream", "x-amz-content-sha256": digest})
  File "/usr/lib/python3.12/site-packages/requests/sessions.py", line 649, in put
    return self.request("PUT", url, data=data, **kwargs)
  File "/usr/lib/python3.12/site-packages/requests/adapters.py", line 507, in send
    raise ConnectTimeout(e, request=request)
requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='s3.us-west-2.amazonaws.com', port=443): Max retries exceeded with url: /dreamlake-chunks/9f2c41e8b7.ts (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x7f3a2c1d8e50>, 'Connection to s3.us-west-2.amazonaws.com timed out. (connect timeout=15)'))

Loading and bundle cost

CodeMirror sits behind a dynamic import, so it never reaches the initial bundle — importing Button from the kit costs nothing, and an app that renders no code never downloads the editor. While that chunk is in flight the block renders a plain <pre> with the same font, size, leading and padding, so its arrival is a recolor rather than a reflow. That fallback is real server-rendered markup, so the code is present in the HTML before any JavaScript runs.

Theming

Colors come from CodeMirror's stock highlight styles — defaultHighlightStyle in light, one-dark's highlight style in dark — while everything around the tokens (surface, gutter, caret, selection) points at uikit tokens.

The block follows <html data-theme> on its own, so it tracks the app's theme whether or not a ThemeProvider is mounted above it. Pass theme to pin it.

Props

PropTypeDefaultDescription
valuestring—Code to show. Controlled; pair with onChange.
defaultValuestring''Initial code when uncontrolled.
onChange(value: string) => void—Fires per keystroke while editing.
langstring—Language name, alias or extension. Falls back to filename's extension. Unknown values render as plain text.
editableboolean | 'toggle'false'toggle' shows an Edit/Done button in the header.
editingboolean—Controlled editing state for editable="toggle".
onEditingChange(editing: boolean) => void—Called when the editing state flips.
headerbooleantrueRender the header bar.
filenamestring—Shown in the header, and seeds language detection. Without one there is no header bar at all — the tools float over the top-right corner instead.
showLangbooleantrueShow the language chip.
copyablebooleantrueShow the Copy button.
actionsReactNode—Extra header controls, before the built-in buttons.
lineNumbersboolean—Controlled line-number gutter. Pinning it without onLineNumbersChange hides the toggle button, since it could never do anything.
defaultLineNumbersbooleanfalseInitial gutter state when uncontrolled.
onLineNumbersChange(on: boolean) => void—Called when the gutter is toggled.
maxHeightnumber | string—Caps the block; content scrolls inside.
minHeightnumber | string—Floor for the code area.
wrapbooleanfalseSoft-wrap instead of scrolling horizontally.
theme'light' | 'dark'—Pin the theme instead of following <html data-theme>.
classNamestring—Extra classes on the outer frame.