YAML editor
The documents the crew write in YAML — job definitions, procedures, pipeline steps — with validation read on the line it is about.
On this page
YAML, kept by bosund once it validates.
Tab indents a line. To leave the editor, press Escape, then Tab.
Code
import { YamlEditor } from "@bosunkit/components/yaml-editor";
import type { ReactElement } from "react";
import { useState } from "react";
export const description: string =
"A job definition as the crew edit it: a text area with a line gutter, and the validation the caller passes in — here none, because every step names an action.";
const DEFINITION: string = [
"name: haul anchor",
"schedule: on_command",
"steps:",
" - action: anchor_up",
" wait: 5s",
" - action: report",
" message: anchor up, watch set",
"",
].join("\n");
export default function ValidDefinition(): ReactElement {
const [value, setValue] = useState<string>(DEFINITION);
return (
<YamlEditor
label="Job definition"
hint="YAML, kept by bosund once it validates."
value={value}
onValueChange={setValue}
/>
);
}
The YAML editor is for the documents the crew write rather than the values they choose: a job definition, a procedure, a pipeline's steps. It is a text area with a line gutter, a monospace face, the document painted underneath it and the validation its caller hands it.
It reads the document itself, with a scanner written against the YAML grammar CodeMirror's @codemirror/lang-yaml
is built on: keys, values, quoted strings, block literals, comments, separators and anchors each take a colour from
the kit's tokens, through a plain text layer under the field, so the caret and the selection stay the browser's. The
colour categories are that grammar's highlighter's — keys are properties, quoted and block text is string, plain
scalars are content, : , - are separators.
It holds no validator. diagnostics arrives as display text the caller has already translated — whatever
validates the document on the vessel, by way of the wire — and the editor's job is to put each problem where it
belongs: the line marked in the gutter, the message listed with its line and column, and the first error carried
as the field's own error line. That keeps one answer to "is this valid" in the stack, and it means the editor never
disagrees with the thing that will reject the document.
Reading a problem where it is
YAML, kept by bosund once it validates. Put the caret on a line to see what its key means.
Tab indents a line. To leave the editor, press Escape, then Tab.
action must be one of anchor_up, report, wait
- 4:5 action must be one of anchor_up, report, wait
- 6 wait is set on a step that does nothing after it
name What the job is called in the job list.
Code
import { YamlEditor } from "@bosunkit/components/yaml-editor";
import type { YamlDiagnostic, YamlHelp } from "@bosunkit/components/yaml-editor";
import type { ReactElement } from "react";
import { useState } from "react";
export const description: string =
"What a validator found, read where it is: each problem with the line it is about, those lines marked in the gutter, and the first error carried as the field's own.";
const BROKEN: string = [
"name: haul anchor",
"schedule: on_command",
"steps:",
" - action: anchro_up",
" - action: report",
" wait: 5s",
"",
].join("\n");
const FOUND: readonly YamlDiagnostic[] = [
{ line: 4, column: 5, severity: "error", message: "action must be one of anchor_up, report, wait" },
{ line: 6, severity: "attention", message: "wait is set on a step that does nothing after it" },
];
const MEANING: readonly YamlHelp[] = [
{ key: "name", text: "What the job is called in the job list." },
{ key: "schedule", text: "When bosund runs it: on_command, at_anchor_up, or a ship's-time watch." },
{ key: "steps", text: "What the job does, in order." },
{ key: "action", text: "The step's action: anchor_up, report or wait." },
{ key: "wait", text: "How long to hold before the next step." },
{ key: "message", text: "What bosun says when the step runs." },
];
export default function InvalidDefinition(): ReactElement {
const [value, setValue] = useState<string>(BROKEN);
return (
<YamlEditor
label="Job definition"
hint="YAML, kept by bosund once it validates. Put the caret on a line to see what its key means."
value={value}
onValueChange={setValue}
diagnostics={FOUND}
help={MEANING}
/>
);
}
An error marks its line red and an attention marks it amber, and the list repeats each one in words, so the colour is never the only signal. The field is marked invalid for anything reading it (a screen reader, a form's submit logic), and it describes itself with the list, so tabbing into the document announces what is wrong with it.
Help while typing
help is what the keys mean, and the editor shows the entry for the key the caret is on, under the field: the same
words the caller's validator knows the key by, so help and validation cannot drift apart. A line with a problem shows
that problem there instead.
Typing in it
Tab indents every line the selection touches by two spaces and Shift+Tab takes one back, because nested YAML cannot
be written without it. Enter keeps the indentation of the line it ends, and indents under a key that opens a block
or a step that follows a -. Escape then Tab moves focus on, and Escape then Shift+Tab back; Shift+Tab on a line with
nothing to outdent moves focus back without the Escape. A line under the editor says so, and the editor is described by
it, so the field never traps the keyboard. The editor takes no edit while pending, and a read-only document is shown
but not changed.
Editing, validation and execution are separate
The editor preserves authored whitespace and highlights syntax. Highlighting does not establish that a document is valid for a particular schema or safe to execute. The caller supplies validation results and owns Save, Apply and any permission boundary. Keep those actions distinct where saving a draft does not activate it.
Retain the draft after a parse or save failure, show diagnostics at their line and keep a useful overall error summary. Long YAML and EPL lines scroll horizontally; do not insert soft-wrap newlines into copied or saved source. Verify round trips with indentation, blank lines, comments and multiline scalar values.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| ref | Ref<HTMLTextAreaElement> | undefined | – | The textarea. |
| labelrequired | string | – | What the document is, shown above the editor in a <label>. |
| valuerequired | string | – | The document being edited. |
| onValueChangerequired | (value: string) => void | – | Called with the document as the crew type it, never with a value the editor invented. |
| diagnostics | readonly YamlDiagnostic[] | undefined | [] | What validation found, in document order. The editor marks each line in its gutter and lists every problem with the line it is about, so a problem is read where it is rather than in a summary somewhere else. An empty list says the caller's last check found nothing; when that check runs is the caller's business. |
| help | readonly YamlHelp[] | undefined | [] | What the keys mean, shown while the caret is on the line of one: the editor's inline help. |
| hint | string | undefined | – | What the document is for, in one sentence under the label. |
| pending | boolean | undefined | false | True while a save or a validation is in flight. The editor takes no edits then, and nothing about the document is shown as saved before bosund confirms it. |
| readOnly | boolean | undefined | false | True when the document is shown but not edited. |
| minRows | number | undefined | DEFAULT_MIN_ROWS | The fewest lines the editor shows; 12 by default, because a pipeline is a document and not a sentence. |
| maxRows | number | undefined | DEFAULT_MAX_ROWS | The most lines the crew can drag the editor to; 24 by default. A longer document scrolls in the field. |
| className | string | undefined | – | Classes added to the root. |
| testId | string | undefined | – | Becomes data-testid on the root; the gutter is ${testId}--gutter and the problem list ${testId}--problems. |