Skip to content
phroun edited this page Aug 22, 2026 · 1 revision

Wire name editor

A monospaced, multiline text editor. TextInput owns single-line input; this is the counterpart for a body of text.

ed=new editor caption="Notes" value="hello" placeholder="Nothing yet"
sub ed commit
sub ed dirty

Properties

Property Type Default Meaning
caption string Title on the editor frame.
caret string Cursor position line:col (mew honors; placeholder ignores).
filename string Host-granted file handle (placeholder: names the temp file).
line_numbers bool default Show line numbers (mew honors; placeholder ignores).
placeholder string Hint shown when empty.
readonly flag false View only; disables the edit affordance.
syntax string default Grammar/language, or auto (mew honors; placeholder ignores).
tab_size int default Tab width (mew honors; placeholder ignores).
value string The text content (read back via the commit event).
wrap bool default Soft-wrap long lines (mew honors; placeholder ignores).

Plus the common properties.

Events

cancel — Editing was abandoned; the content is unchanged.

Field Type Meaning
trinket uint The editor's object ID.

commit — The editor's content was accepted.

Field Type Meaning
trinket uint The editor's object ID.
value string The accepted content.

dirty — The unsaved-change count changed.

Field Type Meaning
trinket uint The editor's object ID.
dirty int Outstanding unsaved changes; zero means clean.

One contract, two implementations

editor is a contract rather than a single widget. Which implementation answers it is decided when the toolkit is built, and exactly one exists in any build:

Build What editor is
stock KittyTK a deliberately minimal placeholder
mew's build the full mew editor

An application targets the contract and runs on either without changes. That is the whole point of the split: an application that wants a text editor still works on a stock build, rather than failing to find one.

The two are mutually exclusive build tags, so there is never a choice to make at runtime and never two editor types to tell apart.

Core and rich properties

The five core properties are honored everywhere:

value placeholder caption readonly filename

The five rich ones — wrap, tab_size, syntax, line_numbers, caret — are honored by mew's editor and accepted and ignored by the placeholder. An application sets the whole vocabulary uniformly and does not have to ask which build it landed on.

That division is deliberate: the contract is bounded by what the minimal implementation can honor. It is also why there is no per-keystroke change event — the placeholder hands the text to an external editor and physically cannot report one.

The rich properties accept any value at all

This is the one place in the protocol where a bad value is silent. A property that does not exist is rejected as usual:

new editor nosuchthing=1
  ->  property "nosuchthing" is not supported by this type

But every rich property is accepted whatever you write:

new editor wrap=nonsense       # accepted
new editor tab_size=banana     # accepted
new editor caret="not a position"   # accepted

The placeholder's appliers do nothing and never fail, so nothing is there to object. On a build where these matter the values are real and a bad one is a bug you will not hear about from the wire. Write them as though they were checked.

Their default is default — the inherit sentinel — not a literal. Leaving them alone means "let the editor decide," which on mew's build means the user's own configuration still governs inside an embedded editor. Override only with a reason.

filename and value

They are two different ways to have content, and filename wins when both are set.

value is ephemeral text: seed content, read back through commit. Use it for something that is not a file — a commit message, a note, a scratch buffer.

filename is a handle granted by the host, not a path you choose. The host brokers a permission-scoped filesystem, hands out the handle, and does the disk I/O itself, off the wire. A full editor opens and saves through that, and the file is the content.

ed=new editor filename="<a handle the host gave you>" syntax=auto

Files sit below this contract on purpose. The trinket surface is about editing; it is not a way to reach the filesystem, and a client cannot open an arbitrary path by naming it here.

syntax=auto detects the grammar from the filename; syntax="" is none.

Not yet on the stock placeholder. The placeholder does not open or save the named file today: the text still comes from value, the edit happens in a temporary file, and the handle is used only to give that file a matching extension so the user's editor picks sensible highlighting. It is a gap in the placeholder rather than a second contract — filename wins everywhere it is implemented, and the placeholder is meant to catch up.

Events

Three are registered: commit, cancel and dirty.

commit is the user accepting the content, and it carries one of two things. An ephemeral edit carries the value, which is the only way to read that text back — no property returns it. A file-backed edit carries the filename instead and arrives after the save has gone through the host's filesystem, so the content is read from the file rather than off the wire.

The stock placeholder only ever has a value to report, which is why its generated table above names one field where a build with real file handling names both.

cancel is the user abandoning the edit, with the content unchanged.

The two are decided by whether the text actually changed, not by how the user left. Closing the editor having typed nothing raises cancel, even though nothing was cancelled in the usual sense. A client that treats cancel as "the user pressed Escape" will read a no-op edit as a refusal.

dirty reports the outstanding unsaved-change count, where zero means clean. The placeholder only ever says 0 or 1; a richer editor may count buffers.

Setting value from the wire raises nothing. The client that wrote it already knows. Events report what the user did.

Two traps around subscribing

Nothing arrives until you sub. An editor you never subscribed to looks exactly like an editor that is not working.

sub does not check the event name. Any word is accepted, and a name that does not exist simply never delivers:

sub ed commit     # works
sub ed comit      # accepted, silently delivers nothing
sub ed saved      # accepted, silently delivers nothing

saved, focus, blur and caret appear in the older design notes and are not registered on any build today. Subscribing to them succeeds and gets you nothing, which is the same thing a typo gets you. Treat the generated event table above as the list that exists.

It takes no children

new editor children={ new label caption="x" }
  ->  this type does not accept children

The content is value or filename. Anything around the editor — a toolbar, a status line — is a sibling inside a Panel.

What the placeholder actually does

Worth knowing, because it shapes what an application should expect on a stock build:

  • It draws a clearly-marked frame showing the caption, the placeholder, and a short preview of the text.
  • Activating that frame writes the text to a temporary file, launches the user's own editor ($VISUAL / $EDITOR) as a separate process, and reads the file back when it exits — then updates value and raises commit.
  • There is no inline editing, no highlighting and no cursor reporting.

So commit can arrive a long time after the user began, and it arrives once rather than continuously. An application that assumes live text will not get it here.

See also

TextInput — single-line input · Terminal — a child process's screen, not a text buffer · Panel · Common Properties

Clone this wiki locally