WebShellHost constructor
WebShellHost({
- required TerminalView term,
- required ShellSessionPort session,
- required String principal,
- required String nodeId,
- DateTime? startedAt,
- LocalCommandRegistry? commands,
- ClientRuntime? client,
- Future<
List< onComplete()?,String> > - NodeDescriptor? nodeInfo()?,
- Principal? principalInfo,
- RemoteSession? remoteSession,
- CommandHistory? history,
- bool resumedInAltScreen = false,
- void onSessionExit()?,
- void onSessionDetached()?,
Creates the host over term and session, wires the LineEditor +
InteractiveShellController, and starts them.
Implementation
//
// The plain `_field = param` initializers below are intentional: a private
// named parameter can't be an initializing formal, so `this._x` is illegal.
// The lint is suppressed per-line (constructor only) rather than file-wide.
WebShellHost({
required TerminalView term,
required ShellSessionPort session,
required String principal,
required String nodeId,
DateTime? startedAt,
LocalCommandRegistry? commands,
ClientRuntime? client,
Future<List<String>> Function(String word, bool isCommand, String? cwd)?
onComplete,
NodeDescriptor? Function()? nodeInfo,
Principal? principalInfo,
RemoteSession? remoteSession,
CommandHistory? history,
bool resumedInAltScreen = false,
void Function()? onSessionExit,
void Function()? onSessionDetached,
}) : _history = history ?? CommandHistory.inMemory(),
// ignore: prefer_initializing_formals
_resumedInAltScreen = resumedInAltScreen,
_startedAt = startedAt ?? DateTime.now(),
// ignore: prefer_initializing_formals
_term = term,
// ignore: prefer_initializing_formals
_principal = principal,
// ignore: prefer_initializing_formals
_nodeId = nodeId,
// ignore: prefer_initializing_formals
_commands = commands,
// ignore: prefer_initializing_formals
_client = client,
// ignore: prefer_initializing_formals
_onComplete = onComplete,
// ignore: prefer_initializing_formals
_nodeInfo = nodeInfo,
// ignore: prefer_initializing_formals
_principalInfo = principalInfo,
// ignore: prefer_initializing_formals
_remoteSession = remoteSession,
// ignore: prefer_initializing_formals
_onSessionExit = onSessionExit,
// ignore: prefer_initializing_formals
_onSessionDetached = onSessionDetached {
_editor = LineEditor(
input: _input.stream,
output: (s) => _term.write(utf8.encode(s)),
history: _history,
// The editor repaints across wrapped rows using the terminal width; keep
// it current via [setWidth] in [_onResize]. Without it a wrapped prompt
// (narrow phone terminals) staircases a fresh prompt per keystroke.
width: _term.size.cols,
// The browser terminal is always "raw" (xterm delivers keystrokes
// verbatim); there is no mode to toggle.
setRawMode: null,
onInterrupt: _interruptRemote,
onEof: () => unawaited(close()),
onRaw: _onRaw,
onComplete: _onComplete == null ? null : _completeAdapter,
onLine: _onLine,
);
_controller = InteractiveShellController(
session: session,
// Resuming a session whose program is in the alternate screen (nano, vim,
// claude, …): start in passthrough and DON'T prime the prompt — priming
// would run cwd/marker commands that corrupt the full-screen program. The
// replayed output repaints it and its queued marker restores the prompt on
// exit. Mirrors the CLI's `resumedInAltScreen` wiring.
resumedInAltScreen: _resumedInAltScreen,
// Remote output repaints around the input line (matching the CLI), so a
// backgrounded job's bytes appear above the prompt rather than tangled
// with it. While a program owns the screen (passthrough) it just emits.
onOutput: (bytes) => _editor.printAbove(() => _term.write(bytes)),
onPrompt: _onPrompt,
onPassthrough: (active) {
_passthrough = active;
_editor.setPassthrough(active);
},
onExit: _onExit,
);
_term.onInput((data) => _feed(utf8.encode(data)));
_term.onResize(_onResize);
final size = _term.size;
if (size.cols > 0 && size.rows > 0) {
_controller.resize(size.cols, size.rows);
}
_editor.start();
_controller.start();
}