Ideas and projects for DomTerm

Roadmap

Planned improvements (without any promises or timeline), from higher priority to lower.

Conditional Values/Formats

We need a syntax to express values that are the result of substitutions and conditionals.

Compare tmux’s “formats”. One idea is to use ‘%’ as an escape character, like extended printf-style formats.

Value types

A value can be string, number, boolean, hybrid, or a list of any of these. “Hybrid” is used when we need to distinguish a quoted string from unquoted text, or a word that is a concatenation of values. A hybrid is represented using a vector, where even-indexed elements are quoted strings and odd-indexed elements are other values. Distinguishing a hybrid from a list depends on context - a vector is only to be interpreted as a hybrid is that is the desired type of an expression.

The desired types of a template or template can be any of these types.

Syntax

operand-part ::= simple-char | string | substitution
operand ::= operand-part+
operands ::= operand [space operand]+
substitution ::= ‘{operands}condition-option ::= operand | ‘!operand
condition ::= ‘{?space? condition-option [space condition-option]+ space? ‘}condition-separator ::= ‘|’ | newline
conditional ::= condition phrase [condition-separator condition phrase]+ [condition-separator phrase]?

Color settings/themes

color.black = color
color.blue = color
color.bright-blue = color

Old css uses --dt-lightblue - i.e. “light” rather than “bright”.

color.background = color
color.cursor = color
theme.theme-name = object

For example:

theme.solar = {background: #fdf6e3, ... }
style.theme = solar

If a specific color.xxx is defined (non-empty), it overrides the corresponding color in the theme.

Parsing

Parsing results in a nested structure that is encodable using a JSON. The parsing is normally done by the backend; the parse result may be forwarded to frontend as JSON.

The expand command

The expand command is helpful for testing template strings. It takes a template string and returns the evaluated value. By default the evaluation is done in the server, but if a - (--window) option is specified, the parsed template is send to the specified window to be evaluated in that window’s context.

Options can specify a context: --to-bool means evaluate the template to a boolean value. --to-json print the result as JSON-encoded. By default, an array is printed by printing each element followed by a newline.

The --parse-only option just prints on the unevaluated parse tree in JSON format.

Selection mode

Selection mode is a light-weight variation/replacement for View mode.

This feature makes use of multiple selection ranges. Only Firefox/Gecko currently supports this, which means selections would have to be simulated. This Chrome extension may be helpful. That extension makes use of the Rangy library, which could also replace mark.js.

At any given time, there may be multiple selection ranges. There is also a selection caret. The selection caret is the same as in View mode, and displayed the same way. It is separate from any output/editor cursor. However, if there is no explicit selection caret, the output/editor cursor is used as the implicit selection caret.

A problem with simulating selections is it may visibly change the display due to different line-breaking. (Only an issue when using the browser line-breaking for variable-width fonts.) It might also break middle-check (paste selection). A hybrid solution could be to use the native selection for the “main” range (usually the one most recently added), and simulated selectiohs for the other - but that adds complexity.

In selection mode, pressing the Ctrl modifier with a movement key (including the arrow keys) moves the selection caret, without changing the selection. Pressing Shift with a movement key moves the selection caret, but also adds the moved-over text to the selection. Moved-over text that was already selected becomes un-selected. Pressing both Ctrl and Shift does ???.

Find mode is changed so that every match becomes part of the selection. Moving between matches does not change the selection, but moves the selection caret. Two ways to successfully exit Find mode: Keep all selections, or only keep the one with the caret. (Remove the other ranges from the selection.) Another variation: add/remove the matches to any pre-existing selection (ranges before entering Find mode).

Another possible extension: Multiple selection carets. Ctrl and/or Shift with a movement key moves all of them. Some text editors handle multiple cursors - see how they do it. However, one caret should be “preferred” as in being scrolled into view.

Clicking with Ctrl pressed moves the selection caret. Clicking with Shift pressed also extends the selection.

Any changes (output or edits) should remove selection ranges and selection carets follow the changes (in document order). However, ideally selections earlier than the changes should be preserved.

In a file browser or a window browser use similar concepts and key-bindings, except that selection is one or more items, rather than one or more character ranges. Also, the caret indicates a current item rather than a character position.

New line structure

Goals

Better support for one session with multiple windows with differing widths. Support “logical line” (infitinely wide) terminal model. Lazy line-breaking (on a per-line basis) for better performance (at least faster re-size).

Terminology

The controlling window (or primary window?) has the controlling window size (width and height). The window width (or height) is the width (or height) (in characters unless specified) of a given window. The terminal width (or height) is the width (or height) of the controlling window.

A line is logical line (represented as a block element), ignoring soft line-breaks. Line-breaking splits a line into one or more rows. A given line may be split in two different ways (at the same time): A terminal row is the result of splitting a line based on terminal width; standard xterm commands work in therms of terminal rows. A window row is the result of splitting a line based on window width; this is how the line is displayed in a given window.

Line properties

These are properties of line object. We use the DOM block element.

endTRows

If this line contains N terminal rows, then endTRows is an array of length N, pointng to line elements that mark the end of the N’th terminal row. (The last element of the array may be null if the line is terminated by an implicted end-of-block.)

endWRows

Similar to endTRows, but an array of ends of window rows. If this is the controlling windows, same as endTRows, unless noTBreak.

endTValid

True if endTRows is valid. Cleared if controlling window is re-sized.

endWValid

True if endTRows is valid. Cleared if current window is re-sized.

measured

True breakLine1 has been run on the line (since it was last modified).

noTBreak

No terminal breaks, only visual breaks: Treat this line as if the terminal width is infinite, thus it always has a single terminal row. In this case, endTRows has size 1. Cursor motion ignores soft line breaks (in this line).

Shorting line-break changes

New property outputAtEnd: If output cursor at end of logical line (possibly nested in style-span or pprint-group). In insertSimpleOutput if outputAtEnd just append the current line. (Note a deferred line-break may overwrite following lines.)

Window resize: Set rebreakNeeded on all lines. On all likes that might becomes visible, if measureNeeded call unbreakLines, then breakLine1 (to measure).

Terminal emulator functionality

Document any discrepancies from xterm and ANSI

While implementing all of the features of xterm is probably not feasible, we should at least have a list of anything we don’t implement, or implement differently from xterm.

See also the ECMA-48 standard.

Error recovery and state transitions should follow this.

Embedded HTML

If the src attribute of <img> is a relative URL (or optionally a file: URL), then the domterm html command should read the file and embed it as a data: URL. (As long as no --base option is specified.)

Highlighting

We may want to support syntax coloring (including in REPL input lines) and other highlighting. If the DomTerm element is not the only element in the <document>’s <body> then the selection may be lost to another “widget” unless we simulate the selection as a highlighted range.

Improve copy and paste

Think about how to do multi-line paste.

Copying to HTML should convert <div class='domterm-pre'> to <pre> so pasting without stylesheets works better.

Scroll limit

Handle saving and truncating scrolled-out output. Most terminal emulators have “max number of lines in scroll buffer” setting. What makes this a little tricky is because there may multiple windows attached to the same session. So this would require changes to the protocol for saving/restoring and syncorinizing sessions.

Support other embeddable browsers

Some toolkits to explore for integrated browser/application:

Using alternate window managers

DomTerm includes a “window manager” (implemented using GoldenLayout) that supports panes and tabs. If DomTerm is integrated in an application with its own window manager, such as Atom or Visual Studio Code, it would make sense to instead integrate with the latter manager.

The neo.mjs project has cross-window drag-and-drop.

For qtdomterm there is experimental support for more advanced Qt docking librariers: Qt Advanced Docking System and KDDockWidgets.

Theia and Jupyter use PhosphorJS, now archived. A newer version is part of JupyterLab.

Similarly, when running under a tiling window manager using the latter to manage panes and tabs is likely preferable.

Compare other terminal emulators

TermySequence has lots of features. Source code.

Last#cat9 has some interesting ideas. One is that each shell command is a separate job.

Tools and examples for Atom or Electron

Atom packages are in ~/.atom/packages. Seemingly helpful tutorial: “How to Write Atom Packages Using Vanilla JavaScript”.

Web browsers for Atom include atom-web-view (aka web-view), or atom-browser-webview. Neither of them work well, probably because of Atom API changes.

Terminal emulators for Electron: Extraterm (interesting, uses CodeMirror). Black Screen (extra shell features).

Terminal emulators for Atom: Term 3. terminal-plus. atom-terminal-panel. Termrk.

Tabs for Electron (compatible with dragula).

Various Electron Awesome links Community links.

Readline style hooks and improvements

The idea is the line-editing mode would provide the functionality of readline or similar programs.

Readline hooks to recognize when it’s running under DomTerm

The idea is readline would delegate basic line editing (and re-display) to DomTerm, while DomTerm would call back to readline for command completion, history, etc.

This has a couple of advantages over plain readline: One is to have mousing actually work (i.e. no more readline not being able to move the cursor on mouse-clicks). Another advantage is local editing, which is a benefit over slow links (such as satellites) or when you don’t want to interrupt the server needlessly.

Readline should at least behave as if the screen width were infinite, delegating line-wrapping to DomTerm.

Event forwarding to inferior

A process may "print"/paint graphics with event handlers. For example a button. On clicking the button, the click should be bundled as an event objects sent back to the inferior.

Shift selection

Zsh editor (zle) supports Emacs-like mark and region. Emacs has a “shift-selection” state, which we would like to emulate.

On mouse selection, or a shift-motion command (such as Shift+Right), should set the region in shift-selection mode: We send Ctrl+Space to the client (setting the mark) at the “anchor” and move the cursor as needed. We also (locally to DomTerm) set the “shift-selection” mode. If when in shift-selection mode any (special) key is pressed without the shift modfier, then shift-selection mode is turned off, the selection is collapsed, and Escape - Ctrl+Space is sent to decativate the region.

Can do this lazily, so we only get the selection highlighting: On mouse selection or shift-motion command

JavaScript extension

It may be useful to load new JavaScript libraries into DomTerm. We can do this when loading a DomTerm web-page, by having the server add <script> elements to the generated web page. More flexible is do it dynamically:

domterm load-javascript "foo.js"

This works by essentialy doing createElement("script").

For this to be useful we need to better define the DomTerm JavaScipt API with extension hooks.

Plotting

A useful extension library would be a plotting library. The Plotly looks powerful (and big), and controlled by a JSON-able data structure.

Saved notebooks

A "notebook" is a saved (and potentially executable) representation of a session.

IPython/Jupyter has a JSON encoding for "notebooks". This is flexible and extensible, but requires special tools. See also Polynote.

The DomTerm notebook format should just be a simple html page. Essentially a serialization of the DOM. The page may include some generated header information and metadata. It may include references to JavaScript that would allow former execution, as well as viewing actions (like hide/unhide, wrap-to-fit, etc). This references should be simple and relative, so the actual JavaScript loaded can depend on context.

The format must be XML-compatible (XHTML) so it can be parsed by XML tools such as XSLT.

Specific format TBD.

The html page must be viewable and look reasonable in a browser even if JavaScript library or style files are missing or JavaScript is disabled.

A notebook may be include additional resources in other files, such as images. If notebook consists of multiple files, they should be bundled in a zip archive (like LibreOffice does).

Tools to convert to and from Jupyter format would be nice, so we should avoid gratuitous conceptual incompatibility.

Detachable sessions

Detachable sessions (similar to GNU Screen and tmux) means that a front-end can disconnect from a session, while the session (child process) persists. The same or a different front-end can later re-attach to the session.

DomTerm implements detachable sessions using a combination of check-pointing the display state, plus having the backend recording output commands since last check-point.

This can can handle unexpected detaches, as when a connection dies. However, it’s a bit heavy-handed: check-pointing should only save parts of the state that have changed. (This gets a bit tricky when there may be multiple windows displaying the same session, combined with not-yet-implemented auto-trunctating of old output.)

See also Mosh (also LWN article), and EternalTerminal.

Remote sessions [mostly implemented]

A DomTerm front-end can used used to display sessions running on remote backends. The challenge is having the remote sessions persist if connection is lost. Managing state for a detached session is handled by the domterm backend, which therefore needs to run remotely. However, the part that manages windows and commands needs to run locally.

The solution is to run the backend in proxy mode. Supposed the user types:

domterm [user]@host command

This can be implemented bying having the (local) backend do:

ssh [user@]host domterm --browser-pipe command

(A password may have to be requested.) The --browser-pipe option is a special kind of browser-specifier: Instead of output being sent to the browser, it is written to stdout; input from stdin is (mostly) sent to the command. (“Mostly” because the remote domterm processes certain escape sequences. For example the "WS" sequences is used to change the pty window size.)

The local domterm backend acts as a proxy for the remote backend.

It is also useful to be able to connect to a remote backend, directly from a browser, without having to install a local domterm command. That requires a remote https server, and has some more permission and security complications.

Documentation

Create screencasts, maybe using Peek, or Byzanz. Consider using key-mon (available on Fedora) to display clicks etc.