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-cbun

Usage

<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

PropTypeDescription
v-model / modelValuestringEditor contents (two-way).
languagestringLanguage id or alias (javascript, ts, py, json, markdown, shell, …); plaintext = no highlighting.
readOnlybooleanRender as a non-editable viewer (copy still works).
lineNumbersbooleanShow the line-number gutter (default true; ignored in wrap mode).
tabSizenumberIndent width in spaces (default 2).
wrapbooleanSoft-wrap long lines (disables the gutter + active-line highlight).
autoClosebooleanAuto-close brackets/quotes (default true).
placeholderstringEmpty-state placeholder text.
maxLengthnumberNative maxlength cap on the contents.
copybooleanShow the copy-to-clipboard button (default true).
highlightActiveLinebooleanHighlight the caret's line (default true; ignored in wrap mode).
spellcheckbooleanNative spellcheck on the textarea (default false).
ariaLabelstringAccessible label for the textarea.

Events

EventDescription
@update:modelValuev-model — emits the new contents string.
@changeContents changed; payload is the string value.
@focus / @blurNative focus / blur events.
@readyEmitted once with the framework-agnostic core instance.

Exposed methods (template ref)

MethodDescription
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 an aria-label (customizable via the ariaLabel prop).
  • 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 an aria-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.