Browse Source

Straight forward implementation copying the visible lines

all the time.
Scrolling is broken :/
devmil 4 năm trước cách đây
mục cha
commit
f47b58a7aa

+ 4 - 3
lib/frontend/input_behavior.dart

@@ -9,9 +9,10 @@ abstract class InputBehavior {
 
   TextEditingValue get initEditingState;
 
-  void onKeyStroke(RawKeyEvent event, Terminal terminal);
+  void onKeyStroke(RawKeyEvent event, TerminalUiInteraction terminal);
 
-  TextEditingValue? onTextEdit(TextEditingValue value, Terminal terminal);
+  TextEditingValue? onTextEdit(
+      TextEditingValue value, TerminalUiInteraction terminal);
 
-  void onAction(TextInputAction action, Terminal terminal);
+  void onAction(TextInputAction action, TerminalUiInteraction terminal);
 }

+ 5 - 4
lib/frontend/input_behavior_default.dart

@@ -14,7 +14,7 @@ class InputBehaviorDefault extends InputBehavior {
   TextEditingValue get initEditingState => TextEditingValue.empty;
 
   @override
-  void onKeyStroke(RawKeyEvent event, Terminal terminal) {
+  void onKeyStroke(RawKeyEvent event, TerminalUiInteraction terminal) {
     if (event is! RawKeyDownEvent) {
       return;
     }
@@ -33,8 +33,9 @@ class InputBehaviorDefault extends InputBehavior {
   }
 
   @override
-  TextEditingValue? onTextEdit(TextEditingValue value, Terminal terminal) {
-    terminal.onInput(value.text);
+  TextEditingValue? onTextEdit(
+      TextEditingValue value, TerminalUiInteraction terminal) {
+    terminal.raiseOnInput(value.text);
     if (value == TextEditingValue.empty) {
       return null;
     } else {
@@ -43,7 +44,7 @@ class InputBehaviorDefault extends InputBehavior {
   }
 
   @override
-  void onAction(TextInputAction action, Terminal terminal) {
+  void onAction(TextInputAction action, TerminalUiInteraction terminal) {
     //
   }
 }

+ 4 - 3
lib/frontend/input_behavior_mobile.dart

@@ -14,9 +14,10 @@ class InputBehaviorMobile extends InputBehaviorDefault {
     selection: TextSelection.collapsed(offset: 1),
   );
 
-  TextEditingValue onTextEdit(TextEditingValue value, Terminal terminal) {
+  TextEditingValue onTextEdit(
+      TextEditingValue value, TerminalUiInteraction terminal) {
     if (value.text.length > initEditingState.text.length) {
-      terminal.onInput(value.text.substring(1, value.text.length - 1));
+      terminal.raiseOnInput(value.text.substring(1, value.text.length - 1));
     } else if (value.text.length < initEditingState.text.length) {
       terminal.keyInput(TerminalKey.backspace);
     } else {
@@ -30,7 +31,7 @@ class InputBehaviorMobile extends InputBehaviorDefault {
     return initEditingState;
   }
 
-  void onAction(TextInputAction action, Terminal terminal) {
+  void onAction(TextInputAction action, TerminalUiInteraction terminal) {
     print('action $action');
     switch (action) {
       case TextInputAction.done:

+ 30 - 27
lib/frontend/terminal_view.dart

@@ -16,7 +16,7 @@ import 'package:xterm/frontend/input_listener.dart';
 import 'package:xterm/frontend/oscillator.dart';
 import 'package:xterm/frontend/cache.dart';
 import 'package:xterm/mouse/position.dart';
-import 'package:xterm/terminal/terminal.dart';
+import 'package:xterm/terminal/terminal_ui_interaction.dart';
 import 'package:xterm/theme/terminal_style.dart';
 import 'package:xterm/util/bit_flags.dart';
 import 'package:xterm/util/hash_values.dart';
@@ -39,7 +39,7 @@ class TerminalView extends StatefulWidget {
         inputBehavior = inputBehavior ?? InputBehaviors.platform,
         super(key: key ?? ValueKey(terminal));
 
-  final Terminal terminal;
+  final TerminalUiInteraction terminal;
   final TerminalResizeHandler? onResize;
   final FocusNode focusNode;
   final bool autofocus;
@@ -105,7 +105,7 @@ class _TerminalViewState extends State<TerminalView> {
 
   void onTerminalChange() {
     _terminalScrollExtent =
-        _cellSize.cellHeight * widget.terminal.buffer.scrollOffsetFromTop;
+        _cellSize.cellHeight * widget.terminal.scrollOffsetFromTop;
 
     if (mounted) {
       setState(() {});
@@ -188,7 +188,7 @@ class _TerminalViewState extends State<TerminalView> {
 
                 final maxScrollExtent = math.max(
                     0.0,
-                    _cellSize.cellHeight * widget.terminal.buffer.height -
+                    _cellSize.cellHeight * widget.terminal.terminalHeight -
                         constraints.maxHeight);
 
                 // set how much the terminal can scroll
@@ -217,26 +217,26 @@ class _TerminalViewState extends State<TerminalView> {
         print('details : $details');
       },
       onTapDown: (detail) {
-        if (widget.terminal.selection.isEmpty) {
+        if (widget.terminal.selection?.isEmpty ?? true) {
           InputListener.of(context)!.requestKeyboard();
         } else {
-          widget.terminal.selection.clear();
+          widget.terminal.clearSelection();
         }
         final pos = detail.localPosition;
         final offset = getMouseOffset(pos.dx, pos.dy);
-        widget.terminal.mouseMode.onTap(widget.terminal, offset);
+        widget.terminal.onMouseTap(offset);
         widget.terminal.refresh();
       },
       onPanStart: (detail) {
         final pos = detail.localPosition;
         final offset = getMouseOffset(pos.dx, pos.dy);
-        widget.terminal.mouseMode.onPanStart(widget.terminal, offset);
+        widget.terminal.onPanStart(offset);
         widget.terminal.refresh();
       },
       onPanUpdate: (detail) {
         final pos = detail.localPosition;
         final offset = getMouseOffset(pos.dx, pos.dy);
-        widget.terminal.mouseMode.onPanUpdate(widget.terminal, offset);
+        widget.terminal.onPanUpdate(offset);
         widget.terminal.refresh();
       },
       child: Container(
@@ -251,7 +251,7 @@ class _TerminalViewState extends State<TerminalView> {
           ),
         ),
         color:
-            Color(widget.terminal.theme.background).withOpacity(widget.opacity),
+            Color(widget.terminal.backgroundColor).withOpacity(widget.opacity),
       ),
     );
   }
@@ -262,8 +262,8 @@ class _TerminalViewState extends State<TerminalView> {
     final row = (py / _cellSize.cellHeight).floor();
 
     final x = col;
-    final y = widget.terminal.buffer.convertViewLineToRawLine(row) -
-        widget.terminal.buffer.scrollOffsetFromBottom;
+    final y = widget.terminal.convertViewLineToRawLine(row) -
+        widget.terminal.scrollOffsetFromBottom;
 
     return Position(x, y);
   }
@@ -293,7 +293,7 @@ class _TerminalViewState extends State<TerminalView> {
   void onKeyStroke(RawKeyEvent event) {
     // TODO: find a way to stop scrolling immediately after key stroke.
     widget.inputBehavior.onKeyStroke(event, widget.terminal);
-    widget.terminal.buffer.setScrollOffsetFromBottom(0);
+    widget.terminal.setScrollOffsetFromBottom(0);
   }
 
   void onFocus(bool focused) {
@@ -312,7 +312,7 @@ class _TerminalViewState extends State<TerminalView> {
     final bottomOffset = widget.terminal.invisibleHeight - topOffset;
 
     setState(() {
-      widget.terminal.buffer.setScrollOffsetFromBottom(bottomOffset);
+      widget.terminal.setScrollOffsetFromBottom(bottomOffset);
     });
   }
 }
@@ -326,7 +326,7 @@ class TerminalPainter extends CustomPainter {
     required this.charSize,
   });
 
-  final Terminal terminal;
+  final TerminalUiInteraction terminal;
   final TerminalView view;
   final Oscillator oscillator;
   final bool focused;
@@ -355,7 +355,7 @@ class TerminalPainter extends CustomPainter {
       final line = lines[row];
       final offsetY = row * charSize.cellHeight;
       // final cellCount = math.min(terminal.viewWidth, line.length);
-      final cellCount = terminal.viewWidth;
+      final cellCount = terminal.terminalWidth;
 
       for (var col = 0; col < cellCount; col++) {
         final cellWidth = line.cellGetWidth(col);
@@ -395,19 +395,22 @@ class TerminalPainter extends CustomPainter {
   }
 
   void _paintSelection(Canvas canvas) {
+    final selection = terminal.selection;
+    if (selection == null) {
+      return;
+    }
     final paint = Paint()..color = Colors.white.withOpacity(0.3);
 
-    for (var y = 0; y < terminal.viewHeight; y++) {
+    for (var y = 0; y < terminal.terminalHeight; y++) {
       final offsetY = y * charSize.cellHeight;
-      final absoluteY = terminal.buffer.convertViewLineToRawLine(y) -
-          terminal.buffer.scrollOffsetFromBottom;
+      final absoluteY = terminal.convertViewLineToRawLine(y) -
+          terminal.scrollOffsetFromBottom;
 
-      for (var x = 0; x < terminal.viewWidth; x++) {
+      for (var x = 0; x < terminal.terminalWidth; x++) {
         var cellCount = 0;
 
-        while (
-            terminal.selection.contains(Position(x + cellCount, absoluteY)) &&
-                x + cellCount < terminal.viewWidth) {
+        while (selection.contains(Position(x + cellCount, absoluteY)) &&
+            x + cellCount < terminal.terminalWidth) {
           cellCount++;
         }
 
@@ -436,7 +439,7 @@ class TerminalPainter extends CustomPainter {
       final line = lines[row];
       final offsetY = row * charSize.cellHeight;
       // final cellCount = math.min(terminal.viewWidth, line.length);
-      final cellCount = terminal.viewWidth;
+      final cellCount = terminal.terminalWidth;
 
       for (var col = 0; col < cellCount; col++) {
         final width = line.cellGetWidth(col);
@@ -528,17 +531,17 @@ class TerminalPainter extends CustomPainter {
 
   void _paintCursor(Canvas canvas) {
     final screenCursorY = terminal.cursorY + terminal.scrollOffset;
-    if (screenCursorY < 0 || screenCursorY >= terminal.viewHeight) {
+    if (screenCursorY < 0 || screenCursorY >= terminal.terminalHeight) {
       return;
     }
 
     final width = charSize.cellWidth *
-        terminal.buffer.currentLine.cellGetWidth(terminal.cursorX).clamp(1, 2);
+        (terminal.currentLine?.cellGetWidth(terminal.cursorX).clamp(1, 2) ?? 1);
 
     final offsetX = charSize.cellWidth * terminal.cursorX;
     final offsetY = charSize.cellHeight * screenCursorY;
     final paint = Paint()
-      ..color = Color(terminal.theme.cursor)
+      ..color = Color(terminal.cursorColor)
       ..strokeWidth = focused ? 0.0 : 1.0
       ..style = focused ? PaintingStyle.fill : PaintingStyle.stroke;
 

+ 443 - 0
lib/terminal/terminal_isolate.dart

@@ -0,0 +1,443 @@
+import 'dart:isolate';
+
+import 'package:xterm/buffer/buffer_line.dart';
+import 'package:xterm/input/keys.dart';
+import 'package:xterm/mouse/position.dart';
+import 'package:xterm/mouse/selection.dart';
+import 'package:xterm/terminal/platform.dart';
+import 'package:xterm/terminal/terminal.dart';
+import 'package:xterm/terminal/terminal_ui_interaction.dart';
+import 'package:xterm/theme/terminal_color.dart';
+import 'package:xterm/theme/terminal_theme.dart';
+import 'package:xterm/theme/terminal_themes.dart';
+import 'package:xterm/util/observable.dart';
+
+void terminalMain(SendPort port) async {
+  final rp = ReceivePort();
+  port.send(rp.sendPort);
+
+  Terminal? _terminal;
+
+  await for (var msg in rp) {
+    final String action = msg[0];
+    switch (action) {
+      case 'sendPort':
+        port = msg[1];
+        break;
+      case 'init':
+        final TerminalInitData initData = msg[1];
+        _terminal = Terminal(
+            onInput: (String input) {
+              port.send(['onInput', input]);
+            },
+            onTitleChange: (String title) {
+              port.send(['onTitleChange', title]);
+            },
+            onIconChange: (String icon) {
+              port.send(['onIconChange', icon]);
+            },
+            onBell: () {
+              port.send(['onBell']);
+            },
+            platform: initData.platform,
+            theme: initData.theme,
+            maxLines: initData.maxLines);
+        _terminal.addListener(() {
+          port.send(['notify']);
+        });
+        break;
+      case 'write':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.write(msg[1]);
+        break;
+      case 'refresh':
+        if (_terminal == null) {
+          break;
+        }
+
+        _terminal.refresh();
+        break;
+      case 'selection.clear':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.selection.clear();
+        break;
+      case 'mouseMode.onTap':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.mouseMode.onTap(_terminal, msg[1]);
+        break;
+      case 'mouseMode.onPanStart':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.mouseMode.onPanStart(_terminal, msg[1]);
+        break;
+      case 'mouseMode.onPanUpdate':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.mouseMode.onPanUpdate(_terminal, msg[1]);
+        break;
+      case 'setScrollOffsetFromBottom':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.buffer.setScrollOffsetFromBottom(msg[1]);
+        break;
+      case 'resize':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.resize(msg[1], msg[2]);
+        break;
+      case 'setScrollOffsetFromBottom':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.buffer.setScrollOffsetFromBottom(msg[1]);
+        break;
+      case 'keyInput':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.keyInput(msg[1],
+            ctrl: msg[2], alt: msg[3], shift: msg[4], mac: msg[5]);
+        break;
+      case 'requestNewStateWhenDirty':
+        if (_terminal == null) {
+          break;
+        }
+        if (_terminal.dirty) {
+          final newState = TerminalState(
+              _terminal.buffer.scrollOffsetFromBottom,
+              _terminal.buffer.scrollOffsetFromTop,
+              _terminal.buffer.height,
+              _terminal.invisibleHeight,
+              _terminal.viewHeight,
+              _terminal.viewWidth,
+              _terminal.selection,
+              _terminal.getSelectedText(),
+              _terminal.theme.background,
+              _terminal.cursorX,
+              _terminal.cursorY,
+              _terminal.showCursor,
+              _terminal.theme.cursor,
+              _terminal.getVisibleLines(),
+              _terminal.scrollOffset);
+          port.send(['newState', newState]);
+        }
+        break;
+      case 'paste':
+        if (_terminal == null) {
+          break;
+        }
+        _terminal.paste(msg[1]);
+        break;
+    }
+  }
+}
+
+class TerminalInitData {
+  PlatformBehavior platform;
+  TerminalTheme theme;
+  int maxLines;
+
+  TerminalInitData(this.platform, this.theme, this.maxLines);
+}
+
+class TerminalState {
+  int scrollOffsetFromTop;
+  int scrollOffsetFromBottom;
+
+  int bufferHeight;
+  int invisibleHeight;
+
+  int viewHeight;
+  int viewWidth;
+
+  Selection selection;
+  String? selectedText;
+
+  int backgroundColor;
+
+  int cursorX;
+  int cursorY;
+  bool showCursor;
+  int cursorColor;
+
+  List<BufferLine> visibleLines;
+
+  int scrollOffset;
+
+  bool consumed = false;
+
+  TerminalState(
+      this.scrollOffsetFromBottom,
+      this.scrollOffsetFromTop,
+      this.bufferHeight,
+      this.invisibleHeight,
+      this.viewHeight,
+      this.viewWidth,
+      this.selection,
+      this.selectedText,
+      this.backgroundColor,
+      this.cursorX,
+      this.cursorY,
+      this.showCursor,
+      this.cursorColor,
+      this.visibleLines,
+      this.scrollOffset);
+}
+
+void _defaultInputHandler(String _) {}
+void _defaultBellHandler() {}
+void _defaultTitleHandler(String _) {}
+void _defaultIconHandler(String _) {}
+
+class TerminalIsolate with Observable implements TerminalUiInteraction {
+  final _receivePort = ReceivePort();
+  SendPort? _sendPort;
+  late Isolate _isolate;
+
+  final TerminalInputHandler onInput;
+  final BellHandler onBell;
+  final TitleChangeHandler onTitleChange;
+  final IconChangeHandler onIconChange;
+  final PlatformBehavior _platform;
+
+  final TerminalTheme theme;
+  final int maxLines;
+
+  TerminalState? _lastState;
+
+  TerminalState? get lastState {
+    return _lastState;
+  }
+
+  TerminalIsolate(
+      {this.onInput = _defaultInputHandler,
+      this.onBell = _defaultBellHandler,
+      this.onTitleChange = _defaultTitleHandler,
+      this.onIconChange = _defaultIconHandler,
+      PlatformBehavior platform = PlatformBehaviors.unix,
+      this.theme = TerminalThemes.defaultTheme,
+      required this.maxLines})
+      : _platform = platform;
+
+  @override
+  int get scrollOffsetFromBottom => _lastState?.scrollOffsetFromBottom ?? 0;
+
+  @override
+  int get scrollOffsetFromTop => _lastState?.scrollOffsetFromTop ?? 0;
+
+  @override
+  int get scrollOffset => _lastState?.scrollOffset ?? 0;
+
+  @override
+  int get bufferHeight => _lastState?.bufferHeight ?? 0;
+
+  @override
+  int get terminalHeight => _lastState?.viewHeight ?? 0;
+
+  @override
+  int get terminalWidth => _lastState?.viewWidth ?? 0;
+
+  @override
+  int get invisibleHeight => _lastState?.invisibleHeight ?? 0;
+
+  @override
+  Selection? get selection => _lastState?.selection;
+
+  @override
+  bool get showCursor => _lastState?.showCursor ?? true;
+
+  @override
+  List<BufferLine> getVisibleLines() {
+    if (_lastState == null) {
+      return List<BufferLine>.empty();
+    }
+    return _lastState!.visibleLines;
+  }
+
+  @override
+  int get cursorY => _lastState?.cursorY ?? 0;
+
+  @override
+  int get cursorX => _lastState?.cursorX ?? 0;
+
+  @override
+  BufferLine? get currentLine {
+    if (_lastState == null) {
+      return null;
+    }
+
+    int visibleLineIndex =
+        _lastState!.cursorY - _lastState!.scrollOffsetFromTop;
+    if (visibleLineIndex < 0) {
+      visibleLineIndex = _lastState!.cursorY;
+    }
+    return _lastState!.visibleLines[visibleLineIndex];
+  }
+
+  @override
+  int get cursorColor => _lastState?.cursorColor ?? 0;
+
+  @override
+  int get backgroundColor => _lastState?.backgroundColor ?? 0;
+
+  @override
+  bool get dirty {
+    if (_lastState == null) {
+      return false;
+    }
+    if (_lastState!.consumed) {
+      return false;
+    }
+    _lastState!.consumed = true;
+    return true;
+  }
+
+  @override
+  PlatformBehavior get platform => _platform;
+
+  void start() async {
+    var firstReceivePort = ReceivePort();
+    _isolate = await Isolate.spawn(terminalMain, firstReceivePort.sendPort);
+    _sendPort = await firstReceivePort.first;
+    _sendPort!.send(['sendPort', _receivePort.sendPort]);
+    _receivePort.listen((message) {
+      String action = message[0];
+      switch (action) {
+        case 'onInput':
+          this.onInput(message[1]);
+          break;
+        case 'onBell':
+          this.onBell();
+          break;
+        case 'onTitleChange':
+          this.onTitleChange(message[1]);
+          break;
+        case 'onIconChange':
+          this.onIconChange(message[1]);
+          break;
+        case 'notify':
+          poll();
+          break;
+        case 'newState':
+          _lastState = message[1];
+          this.notifyListeners();
+          break;
+      }
+    });
+    _sendPort!.send(
+        ['init', TerminalInitData(this.platform, this.theme, this.maxLines)]);
+  }
+
+  void stop() {
+    _isolate.kill();
+  }
+
+  void poll() {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['requestNewStateWhenDirty']);
+  }
+
+  void refresh() {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['refresh']);
+  }
+
+  void clearSelection() {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['clearSelection']);
+  }
+
+  void onMouseTap(Position position) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['mouseMode.onTap', position]);
+  }
+
+  void onPanStart(Position position) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['mouseMode.onPanStart', position]);
+  }
+
+  void onPanUpdate(Position position) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['mouseMode.onPanUpdate', position]);
+  }
+
+  void setScrollOffsetFromBottom(int offset) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['setScrollOffsetFromBottom', offset]);
+  }
+
+  int convertViewLineToRawLine(int viewLine) {
+    if (_lastState == null) {
+      return 0;
+    }
+    if (_lastState!.viewHeight > _lastState!.bufferHeight) {
+      return viewLine;
+    }
+
+    return viewLine + (_lastState!.bufferHeight - _lastState!.viewHeight);
+  }
+
+  void write(String text) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['write', text]);
+  }
+
+  void paste(String data) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['paste', data]);
+  }
+
+  void resize(int newWidth, int newHeight) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['resize', newWidth, newHeight]);
+  }
+
+  void raiseOnInput(String text) {
+    onInput(text);
+  }
+
+  void keyInput(
+    TerminalKey key, {
+    bool ctrl = false,
+    bool alt = false,
+    bool shift = false,
+    bool mac = false,
+    // bool meta,
+  }) {
+    if (_sendPort == null) {
+      return;
+    }
+    _sendPort!.send(['keyInput', key, ctrl, alt, shift, mac]);
+  }
+}

+ 46 - 0
lib/terminal/terminal_ui_interaction.dart

@@ -0,0 +1,46 @@
+import 'package:xterm/buffer/buffer_line.dart';
+import 'package:xterm/input/keys.dart';
+import 'package:xterm/mouse/position.dart';
+import 'package:xterm/mouse/selection.dart';
+import 'package:xterm/terminal/platform.dart';
+import 'package:xterm/util/observable.dart';
+
+abstract class TerminalUiInteraction with Observable {
+  int get scrollOffsetFromBottom;
+  int get scrollOffsetFromTop;
+  int get scrollOffset;
+  int get bufferHeight;
+  int get terminalHeight;
+  int get terminalWidth;
+  int get invisibleHeight;
+  Selection? get selection;
+  bool get showCursor;
+  List<BufferLine> getVisibleLines();
+  int get cursorY;
+  int get cursorX;
+  BufferLine? get currentLine;
+  int get cursorColor;
+  int get backgroundColor;
+  bool get dirty;
+  PlatformBehavior get platform;
+
+  void refresh();
+  void clearSelection();
+  void onMouseTap(Position position);
+  void onPanStart(Position position);
+  void onPanUpdate(Position position);
+  void setScrollOffsetFromBottom(int offset);
+  int convertViewLineToRawLine(int viewLine);
+  void raiseOnInput(String input);
+  void write(String text);
+  void paste(String data);
+  void resize(int newWidth, int newHeight);
+  void keyInput(
+    TerminalKey key, {
+    bool ctrl = false,
+    bool alt = false,
+    bool shift = false,
+    bool mac = false,
+    // bool meta,
+  });
+}

+ 2 - 1
lib/xterm.dart

@@ -1,4 +1,5 @@
 library xterm;
 
-export 'terminal/terminal.dart';
+export 'terminal/terminal_isolate.dart';
+export 'terminal/terminal_ui_interaction.dart';
 export 'terminal/platform.dart';