@tooark/code
v1.3.0
Published
Tooark Code — a code editor (`<ark-code-editor>`) based on CodeMirror 6 (supporting JSON, JavaScript, and YAML), featuring code completion, formatting, and token-based theming
Readme
@tooark/code
<ark-code-editor>: a CodeMirror 6 editor as a Custom Element — JSON, JavaScript and YAML with completions, formatting, indentation and line-ending options, themed by the Tooark tokens.
🌍 Languages:
English (this file) ·
Português
Contents
📖 Overview
The @tooark/code package provides:
languagejson / javascript / yaml / text, line numbers, folding, search (Ctrl+F), bracket matching, active line, placeholder,readonly,wrap,min-height;- Tab indents (Shift+Tab outdents; Esc then Tab leaves the editor),
indent-stylespaces or tabs,indent-size,line-endingauto/lf/crlfconverted at the value boundary; - completions: the language's own,
variableKeysafter{{,completions(words in any language) andcompletionSource;autocomplete="false"turns them off; - optional, off until you pass them:
variablespaints each{{key}}with its variable's intent (free-form scope, hover tooltip with scope and value),mark-unknown-variablesflags keys that are not defined, andsingle-lineturns the editor into a one-line field at control height (size), Enter emittingark-submit; format()and Shift+Alt+F: JSON built in with the configured indentation, other languages through theformatterhook (Prettier stays in the app);- chrome on the
--ark-color-*tokens with fallbacks,theme="auto"following the page at runtime;createCodeEditorengine without the element; - CodeMirror packages as peer dependencies, so the page keeps one copy of
@codemirror/state.
🔧 Installation
pnpm add @tooark/code @codemirror/state @codemirror/view @codemirror/language @codemirror/commands @codemirror/search @codemirror/autocomplete @codemirror/lang-json @codemirror/lang-javascript @codemirror/lang-yaml @lezer/highlightThe CodeMirror packages are peer dependencies: your page keeps a single copy of each, and you choose the versions.
⚙️ Configuration
Register the element once; the editor styles itself through CodeMirror themes that read the --ark-color-* tokens (with fallbacks, so it works without @tooark/web-components):
import { registerTooarkCode } from "@tooark/code";
registerTooarkCode();In React, Vue or Angular use the tag directly (the framework READMEs show how); a JS property the framework assigns before this call, such as value bound on the tag, is applied when the element upgrades, so the order does not matter.
📦 Components
ark-code-editor
- Attributes:
language(json|javascript|yaml|text),readonly,placeholder,min-height(default8rem),line-numbersandfold(on;"false"turns off),wrap,indent-style(space|tab),indent-size(default 2),line-ending(auto|lf|crlf),tab-indentandautocomplete(on;"false"turns off),mark-unknown-variables,single-line,size(xs…xl, defaultmd: font and side padding, plus the height insingle-line),theme,aria-label(names the editable content, CodeMirror'srole="textbox"),testid. - Properties:
value,variableKeys,variables,completions,completionSource,formatter,canFormat,resolvedLineEnding,resolvedTheme,view(theEditorView), and one per attribute exceptaria-labelandtestid; methodsformat(),focus(). - Events:
change(detail: { value }, user edits only),ark-format-error(detail: { error }),ark-submit(detail: { value }, Enter insingle-line). - Keys: Ctrl/Cmd+F search, Ctrl/Cmd+Z undo, Ctrl+Y redo (Cmd+Shift+Z on macOS), Ctrl+Space completions, Shift+Alt+F format, Tab/Shift+Tab indent, Esc+Tab leave, Ctrl+M (Shift+Alt+M on macOS) toggles CodeMirror's tab-focus mode.
- Hooks: the CodeMirror root carries
data-ark="code-editor"and thetestidasdata-testid; each painted variabledata-ark="code-editor-variable"withdata-key; its tooltipdata-ark="code-editor-variable-tooltip".
Scoped variables
Nothing changes until the app passes variables or sets mark-unknown-variables; variableKeys alone still only completes.
variables:ArkCodeVariable[], one{ key, scope?, intent?, value? }per key. The library knows no scope:scopeis any name the app uses, and when a key exists in several scopes the app resolves the precedence and sends only the winner (a repeated key keeps its first entry).- Each
{{key}}(inner spaces allowed; keys use letters, digits,_,.,-and$) becomes<span class="cm-ark-variable cm-ark-variable-<intent>" data-key data-scope data-intent>with the intent's soft background and text (--ark-color-<intent>-soft/-soft-fg, defaultprimary), also inside JSON strings, and each variable stays a single span. The distinct hues areinfo,success,warning/primaryanddanger(which also marks unknown keys);secondaryandneutralread close to the text color. For more scopes, or your own palette, override a scope's colors with CSS, e.g.ark-code-editor .cm-ark-variable[data-scope="global"] { background: …; color: … }. - A variable with
scopeorvalueshows a hover tooltip with the scope (in its intent) and the value; the completion after{{shows the scope as detail and the value as info. All of that text is the app's: leavevalueout for secrets. mark-unknown-variables: a{{key}}found in neithervariablesnorvariableKeysgetscm-ark-variable-unknownanddata-unknown, painteddangerwith a wavy underline (not color alone).
Single-line field
single-line makes a field such as a URL bar: the height of the controls of the same size (it lines up with ark-button and ark-input), no gutters and no active line, Enter emits ark-submit instead of breaking the line (when the completion list is open, Enter accepts the completion), pasted line breaks are removed as in an <input>, Tab leaves the field and Ctrl/Cmd+F is left to the browser. Turning it on over a multi-line text joins the lines without a change. Variables and completions work the same.
Engine
createCodeEditor(parent, options)→{ view, getValue, setValue, setLanguage, setTheme, setReadonly, setPlaceholder, setLineNumbers, setFold, setWrap, setMinHeight, setIndent, setLineEnding, resolvedLineEnding, setTabIndent, setAutocomplete, setVariableKeys, setVariables, setMarkUnknownVariables, setSingleLine, setSize, setCompletions, setCompletionSource, setFormatter, format, canFormat, resolvedTheme, focus, destroy }; the options mirror the attributes and properties, plusonChange,onFormatErrorandonSubmit.resolveCodeTheme(theme, element).- Types:
ArkCodeEditorInstance,ArkCodeEditorOptions,ArkCodeVariable,ArkCodeLanguage,ArkCodeTheme,ArkCodeIndentStyle,ArkCodeLineEnding,ArkCodeCompletion,ArkCodeCompletionSource,ArkCodeFormatter.
📝 Usage examples
A JSON body editor with variable completions
const editor = document.querySelector("ark-code-editor")!;
editor.setAttribute("language", "json");
editor.setAttribute("indent-size", "4");
editor.variableKeys = ["baseUrl", "token", "user.id"]; // offered after {{
editor.value = JSON.stringify(body, null, 4);
editor.addEventListener("change", (event) => save((event as CustomEvent<{ value: string }>).detail.value));
formatButton.hidden = !editor.canFormat;
formatButton.addEventListener("click", () => editor.format()); // also Shift+Alt+FA URL field with scoped variables
<ark-code-editor id="url" single-line mark-unknown-variables placeholder="{{baseUrl}}/path"></ark-code-editor>const url = document.querySelector("ark-code-editor")!;
// Precedence between scopes is resolved by the app: one entry per key.
url.variables = [
{ key: "baseUrl", scope: "global", intent: "info", value: "https://api.example.com" },
{ key: "token", scope: "environment", intent: "success" }, // no value: kept out of the tooltip
{ key: "userId", scope: "local", intent: "primary", value: "42" },
];
url.value = "{{baseUrl}}/users/{{userId}}";
url.addEventListener("ark-submit", (event) => send((event as CustomEvent<{ value: string }>).detail.value));/* your own color for one scope, instead of an intent */
ark-code-editor .cm-ark-variable[data-scope="environment"] {
background: #ecfeff;
color: #0e7490;
}YAML with schema keys and an app formatter
import { dump, load } from "js-yaml"; // your dependency, not the library's
const editor = document.querySelector("ark-code-editor")!;
editor.setAttribute("language", "yaml");
editor.setAttribute("line-ending", "lf");
editor.completions = [
{ label: "apiVersion", type: "keyword" },
{ label: "kind", type: "keyword" },
{ label: "metadata" },
];
editor.formatter = (value) => dump(load(value), { indent: editor.indentSize });
editor.addEventListener("ark-format-error", (event) => toast.error(String((event as CustomEvent).detail.error)));📋 Dependencies
Installed automatically unless marked as peer; peer dependencies are yours to install (the ranges are what the package declares).
| Package | Version | Description |
| ------------------------------------------------------------------------------------------ | ------------- | --------------------------------------------------------- |
| @tooark/tokens | ^1.3.0 | Design tokens (colors, sizes, motion) and primitive types |
| tslib | ^2.8.1 | TypeScript runtime helpers |
| @codemirror/autocomplete | >=6 (peer) | Completions and bracket closing |
| @codemirror/commands | >=6.6 (peer) | Keymaps, history, indentation commands |
| @codemirror/lang-javascript | >=6 (peer) | JavaScript language and completions |
| @codemirror/lang-json | >=6 (peer) | JSON language |
| @codemirror/lang-yaml | >=6 (peer) | YAML language |
| @codemirror/language | >=6 (peer) | Language support, folding, indentation |
| @codemirror/search | >=6 (peer) | Search panel and selection matches |
| @codemirror/state | >=6 (peer) | Editor state (one copy per page) |
| @codemirror/view | >=6.27 (peer) | Editor view and DOM |
| @lezer/highlight | >=1 (peer) | Syntax highlight tags |
🪪 Contributing
Contributions are welcome! Open issues and pull requests in the Tooark/web-components repository; CONTRIBUTING.md covers the workflow, the commit convention and the checklist. @tooark/code is released in lockstep with every other @tooark/* package.
📄 License
This project is licensed under the Apache License 2.0. See the LICENSE file for details.
