import { MarkdownWorkspace } from "@elabs-ai/components-editor/markdown";npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-editorPaste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui MarkdownWorkspace component (from @elabs-ai/components-editor) to this project. 1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`. 2. Look MarkdownWorkspace up before writing code: `npx -y @elabs-ai/components-cli docs MarkdownWorkspace` prints its real props, variants, what it pairs with and what to avoid. 3. If @elabs-ai/components-editor is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-editor` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-editor`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`. 4. Use it here: [the screen or file, and what it should show] 5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop. 6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.
Examples
Focus Writing
Focus writing (Ulysses Phase A): typewriter scrolling + paragraph focus in the WYSIWYG mode. The "Focus" toggle in the mode row turns it on; the active paragraph stays at full opacity while the rest dim.
No Focus Toggle
`focusWriting={false}` — the host opts out of the Focus writing toggle. The toggle is completely hidden (no keyboard path), and focus-writing state is forced off. The mode switch remains and WYSIWYG editing is unaffected. (#270)
Host Owned Mode Switch
`modeSwitch={false}` — the host supplies its own mode switch via `toolbarActions`. The built-in Source / Split / Preview-edit toggle group is hidden in both toolbar branches so the host owns the view switch. Controlled `mode`/`onModeChange` still drive the panes. (#272)
Reveal Line
`MarkdownWorkspaceHandle` — imperative `revealLine` + `scrollToHeading` (#273). Use the buttons to call `ref.current.revealLine(N)` or `ref.current.scrollToHeading(slug)` programmatically. In Source/Split mode the Monaco editor scrolls to the exact line; in WYSIWYG (Preview-edit) mode the nearest heading scrolls into view (best-effort, heading-anchored). `getElement()` returns the root `<div>` (the old `HTMLDivElement` ref access); `getEditor()` returns the live Monaco instance (null in WYSIWYG).
Source: type / to insert a block
**Type \`/\` to open the block menu in the Monaco SOURCE pane** — the conflict-free trigger (no keybinding), mirroring the WYSIWYG \`/\` menu. A \`/\` at a line start (or after whitespace) opens the branded listbox at the caret; the \`/\` is replaced by the block you pick (Esc removes it, leaving the doc untouched). The story's play step inserts a \`/\` to open it on load — you can also just type \`/\` in the editor.
Source: ⌘⇧O hotkey
**Hotkey** path (#271): \`slashMenu.shortcut\` opens the SAME menu. The default is **⌘⇧O** (free in Chrome/macOS) — NOT \`Mod-/\`, which Monaco binds to "Toggle Line Comment". ⚠️ ⌘⇧O / Ctrl-Shift-O is the bookmarks shortcut in Firefox and on Windows/Linux Chrome, so override \`slashMenu.shortcut\` (or rely on typing \`/\`) for cross-browser apps. The play fires the bound command to open it on load.
Completions: [[wikilink]] autocomplete
**`completions` prop (#283)** — a declarative `[[wikilink]]` autocomplete provider, wired with ZERO `monaco-editor` imports in this story. The library owns the Monaco `registerCompletionItemProvider` registration (registered once, refcounted across mounted workspaces, disposed with the last one) and renders Monaco's OWN themed suggest widget — no custom popup on this path. Works in both Source and Split (the pane rendering Monaco either way). The play step verifies the part that is THIS library's responsibility end to end in a real browser: typing `[[` reaches the registered provider with the correct live context, which resolves the right candidates (logged below the editor). Whether Monaco's OWN native suggest popup paints itself in any given host is Monaco's own long-tested internal behavior, not new code — open this story in the Storybook UI to see it render live.
Source: run-only command (runInSource)
**`runInSource` (#299)** — a run-only command (no `snippet`) that used to be WYSIWYG-only now works in the Monaco SOURCE pane too. Selecting "Ask AI" strips the typed `/query`, then calls `runInSource` with the live editor + engine-agnostic content access — the 12 default (snippet-bearing) commands are unchanged and still insert their snippet.