There are two possible ways DomTerm can detect cooked vs raw mode:
Unfortunately, few modern operating systems provide a good way
to request such notication.
Linux has the
EXTPROC flag which is inspired by
the System V Streams packet mode.
This adds an extra indicator byte to the terminal input stream.
This mode is enabled by the
This is the default when available.
domtermserver, which checks the mode of the terminal: If the PTY is in raw mode, the character is sent to the application. If the PTY is in cooked mode, the character is sent back to the DomTerm front-end, which enters line-edit mode, inserting the character in the input area. Subsequent characters (until Enter) are handled locally by DomTerm without a trip to the backend.
"Line" here refer to "visual line": A section of the DOM that should be treated as a line for cursor movement. Line breaks may come from the back-end, or be inserted by the line break algorithm.
The lineStarts array maps from a line number to the DOM location of the start of the corresponding line.
The lineEnds array maps to the end of each line.
Always points to a span node with the line attribute set.
Normally lineEnds[i] == lineStarts[i+1]; however, sometimes
lineStarts[i] is the start of a
<div> or other block element.
This needs updating.
Escape sequences (for example
"\e[4m" - "underlined", or
"\e[32m" - "set foreground color to green") are translated to
<span> elements with "
style" attributes (for example
‘<span style="text-decoration:underline">‘ or ‘<span style="color: green">‘).
After creating such a ‘<span>‘ the current position is moved inside it.
If we’ve previously processed "set foreground color to green", and we see a request for "underlined" it is easy to ceate a nested ‘<span>‘ for the latter. But what if we then see "set foreground color to red"? We don’t want to nest <span style="color: red">‘ inside <span style="color: green">‘ - that could lead to some deep and ugly nesting. Instead, we move the cursor outside bot existing spans, and then create new spans for red and underlined.
The ‘<span>‘ nodes are created lazily just before characters are inserted, by ‘_adjustStyle‘, which compares the current active styles with the desired ones (set by ‘_pushStyle‘).
A possibly better approach would be to match each highlight style into a ‘class‘ attribute (for example ‘green-foreground-style‘ and ‘underlined-style‘). A default stylesheet can map each style class to the correspoding CSS rules. This has the advantage that one could override the highlighting appearance with a custom style sheet.
The support for remote connections is a relatively thin layering on top of ssh. When you type:
domterm user@host command
it basically translates to:
ssh user@host domterm --browser-pipe command
All connection setup and logging in are handled by
If you do a command like:
domterm user@host status
this will run
domterm --browser-pipe status on the host.
In this case the
--browser-pipe option is irrelevant:
Output from the command will be printed on standard output
ssh then sends back to the local
which prints in on the local terminal.
If the command creates a window, things get more interesting. For example:
domterm --qt user@host attach :1
ssh will execute
domterm --browser-pipe attach :1
on the remote host. This will attach to existing session 1.
domterm would normally create a a new window,
it sees the
--browser-pipe. It prints a special escape character,
which is sent back to the local
domterm server, which switches
from command-proxy mode to display-proxy mode, opening up a window.
(A Qt window because of the
In display-proxy mode, characters and other events from the window
are written to the input of the
while output from the remote user process are sent
via the remote domterm server over ssh to the local domterm server,
which displays them in the window.
┌─────────────────────────────────────┐ Front-end │ Window, input devices (keyboard) │ ├─────────────────────────────────────┤ │ Browser engine (runs terminal.js) │ └-────────────────────────────────────┘ 🠉 WebSockets connection (local) 🠋 ┌─────────────────────────────────────┐ Local │ Proxy (local end) │ back-end ├─────────────────────────────────────┤ │ Ssh client (local session) │ └-────────────────────────────────────┘ 🠉 ssh connection (network) 🠋 ┌─────────────────────────────────────┐ Remote │ Proxy (remote end) │ back-end ├─────────────────────────────────────┤ │ Application (session) │ └─────────────────────────────────────┘
(Compare with diagram at Terminology and Concepts.)
Mosh implements local “tentative echo”, which makes network latency less a problem. DomTerm implements this leveraging the “deferred deletion” mechanism (used for line mode echo).
To do this we use a
<span> that contains predicted input:
an optional text node, the
_caretNode, and an optional text node.
The node has 3 additional properties:
pendingEcho. When output arrives from the server,
_doDeferredDeletion is called,
which replaces the span by the
_caretNode in between; this is “real” (confirmed) output,
before processing the new output.
_doDeferredDeletion when unable to do echo predication.
Handling keyboard input is as follows:
_deferredForDeletion is null, we set it to a fresh span
that wraps the
As needed, any text node immediately before or after can be moved
_deferredForDeletion span, also setting
Then, for a printing character, we insert it before the caret,
and append it to
For left or right arrow, delete, or backspace, if possible we adjust the
_deferredForDeletion span appropriately,
and add a special code to
If not possible, we
_doDeferredDeletion, which we
also do for other keys.
_doDeferredDeletion just before handling output
is correct but suboptimal if the output only contains part
of the pending echo.
In that case we try to create (after handling output) a new
_deferredForDeletion span, whose
is a tail of the previous value.
(We only do this if there are no changes to any other (logical) line.)
A session can have one or more open windows, or it can be in a detached state, in which case the process (and pty) still exist, along with preserved state that can be used to attach a new window to the session. Onc can also attach a new window to a session that has open windows, in which case the open windows can provide the state needed to create the new window.
Preserved state is currently represented in two parts: a snapshot (the DOM slightly sanitized and serialized, plus terminal state properties), and a replay log: the session output sent to the terminal since the last snapshot.
For each session, the sever maintains a detach-count, which is rougly the number of windows that have been explictly closed. This is also the number of attaches “pending“ before a session is closed. When a new non-detached session is created, the detach-count is zero; when a window is detached, the detach-count is incremented; it a window is connected to an existing session, the detach-count is decremented. (If a new session is created in detached state, should the initial connect-count be zero or one?)
A connection (between session and window) can be closed in 4 ways:
exit. The server sends all windows a request to close themselves.
For a terminal emulator we need to preserve (not collapse) whitespace, and (usually) we want to line-break in the middle of a word.
These CSS properties come close:
white-space: pre-wrap; word-break: break-all
This is simple and fast. However:
Hence we need to do the job ourselves.