Code Editor
A lightweight, secure code editor built on the textarea-overlay model: a native <textarea> handles all editing (caret, selection, IME, undo/redo, clipboard, accessibility, mobile) while a first-party, ReDoS-safe tokenizer paints a syntax-highlight layer beneath it. No third-party editor and no runtime dependency beyond Vue — and rendering is escaping-safe (no innerHTML).
Ships in the components bundle at @vanduo-oss/vd3-cbun/code-editor. v1 highlights JavaScript/TypeScript, HTML, CSS, JSON, Markdown, Shell, and Python, with a line-number gutter, auto-indent + bracket/quote auto-close, read-only mode, a copy button, a placeholder, and a large-input performance guard. It adapts to the active Vanduo theme via --vd-* tokens.
Interactive playground
Live v-model + reactive props — switch language and toggle features; the bound value updates as you type.
Bound value: 198 chars · 11 lines
Python
JSON
HTML
Markdown
<VdCodeEditor v-model="code" language="python" />
<VdCodeEditor v-model="code" language="json" />
<VdCodeEditor v-model="code" language="markdown" />
<!-- aliases work too: js, ts, py, sh, md -->Read-only
A non-editable, syntax-highlighted viewer. The copy button still works.
<VdCodeEditor :model-value="source" language="typescript" read-only />Placeholder (empty state)
An empty editor shows placeholder text until you type.
Soft wrap
wrap soft-wraps long lines (the gutter and active-line highlight turn off while wrapping).
<VdCodeEditor v-model="text" wrap :line-numbers="false" />API Reference
Install
pnpm add @vanduo-oss/vd3-cbunUsage
<script setup lang="ts">
import { ref } from 'vue';
import { VdCodeEditor } from '@vanduo-oss/vd3-cbun/code-editor';
import '@vanduo-oss/vd3-cbun/code-editor/css';
const code = ref('const hello = "world";');
</script>
<template>
<VdCodeEditor v-model="code" language="javascript" />
</template>Props
| Prop | Type | Description |
|---|---|---|
v-model / modelValue | string | Editor contents (two-way). |
language | string | Language id or alias (javascript, ts, py, json, markdown, shell, …); plaintext = no highlighting. |
readOnly | boolean | Render as a non-editable viewer (copy still works). |
lineNumbers | boolean | Show the line-number gutter (default true; ignored in wrap mode). |
tabSize | number | Indent width in spaces (default 2). |
wrap | boolean | Soft-wrap long lines (disables the gutter + active-line highlight). |
autoClose | boolean | Auto-close brackets/quotes (default true). |
placeholder | string | Empty-state placeholder text. |
maxLength | number | Native maxlength cap on the contents. |
copy | boolean | Show the copy-to-clipboard button (default true). |
highlightActiveLine | boolean | Highlight the caret's line (default true; ignored in wrap mode). |
spellcheck | boolean | Native spellcheck on the textarea (default false). |
ariaLabel | string | Accessible label for the textarea. |
Events
| Event | Description |
|---|---|
@update:modelValue | v-model — emits the new contents string. |
@change | Contents changed; payload is the string value. |
@focus / @blur | Native focus / blur events. |
@ready | Emitted once with the framework-agnostic core instance. |
Exposed methods (template ref)
| Method | Description |
|---|---|
getInstance() | The framework-agnostic core editor instance. |
focus() / blur() | Move focus in / out of the editor. |
getValue() / setValue(v) | Read / replace the contents. |
getSelection() | { start, end, text } of the current selection. |
setSelection(start, end?) | Set the selection range. |
insertText(text) | Insert text at the caret (replacing any selection). |
getContainer() | The root container element. |
Framework-agnostic helpers
The subpath also re-exports the pure tokenizer helpers (no DOM) and the core class as VdCodeEditorCore.
import { tokenize, highlight, LANGUAGES } from '@vanduo-oss/vd3-cbun/code-editor';
// Pure, framework-agnostic helpers (no DOM):
const tokens = tokenize('const x = 1;', 'javascript'); // [{ type, value }, …]
const html = highlight('const x = 1;', 'javascript'); // escaped HTML string
LANGUAGES; // ['plaintext','javascript','typescript','html','css','json','markdown','shell','python']CSS variables
:root {
--vd-code-editor-bg: var(--vd-bg-primary);
--vd-code-editor-text: var(--vd-text-primary);
--vd-code-editor-border: var(--vd-border-color);
--vd-code-editor-gutter-bg: var(--vd-bg-secondary);
--vd-code-editor-gutter-text: var(--vd-text-muted);
--vd-code-editor-caret: var(--vd-color-primary);
--vd-code-editor-font-size: 0.875rem;
--vd-code-editor-line-height: 1.5;
/* Syntax token colors (light; overridden under [data-theme="dark"]) */
--vd-code-editor-tk-keyword: #7c3aed;
--vd-code-editor-tk-string: #16a34a;
--vd-code-editor-tk-comment: #6b7280;
--vd-code-editor-tk-number: #c2410c;
--vd-code-editor-tk-function: #2563eb;
}Accessibility
- Editing is a native
<textarea>— full keyboard support, IME, native undo/redo, and screen-reader text editing come for free. It carries anaria-label(customizable via theariaLabelprop). - The highlight layer and gutter are decorative and marked
aria-hidden="true", so assistive tech reads only the editable text. - The copy button is a real
<button>with anaria-label; it only appears when the Clipboard API is available. - Colors derive from the active theme's
--vd-*tokens; selection stays visible over the painted tokens.