run method

Future<T?> run()

Starts the inline prompt loop and returns a Future containing the final result.

Implementation

Future<T?> run() async {
  final termSize = await terminal.size;
  final width = termSize.x;

  // Autosize the height dynamically if not explicitly specified.
  final computedHeight = height ?? widget.getIntrinsicHeight(width);

  // Create a temporary buffer and inline renderer.
  final buffer = Buffer.blank(width, computedHeight);
  final renderer = Renderer(
    width,
    computedHeight,
    mode: RenderingMode.inline,
  );

  _completer = Completer<T?>();
  _isDisposed = false;

  // Wrap the widget tree in a PromptScope to expose the clean completion API
  final scopedWidget = PromptScope(
    onDone: (result) {
      final comp = _completer;
      if (comp != null && !comp.isCompleted) {
        comp.complete(result as T?);
      }
    },
    child: widget,
  );

  // Mount the widget tree element persistently so states and focus configuration
  // are retained between keyboard event loop iterations.
  final rootElement = scopedWidget.createElement()..mount(null);
  _rootElement = rootElement;

  void draw() {
    if (_isDisposed) return;
    // Rebuild the element tree to consume any new state modifications.
    rootElement.rebuild();

    buffer.clear();
    // Render the rebuilt element tree on our double buffer canvas.
    rootElement.render(buffer, Rect(0, 0, width, computedHeight));

    final sb = StringBuffer();
    renderer.render(buffer, sb);
    if (sb.isNotEmpty) {
      terminal.backend.write(sb.toString());
    }
  }

  // Connect the static repainter callback so that inner setState() invocations
  // (e.g. from typing inside a TextField) trigger screen updates.
  State.onNeedRepaint = draw;

  // Initial frame draw
  draw();

  final subscription = terminal.events.listen(
    (event) {
      if (_completer!.isCompleted) return;

      if (event is term.KeyEvent) {
        var isDone = false;

        // Step 1: Custom Interceptor
        if (onKeyEvent != null) {
          isDone = onKeyEvent!(event);
        }

        // Step 2: Widget Event Routing
        if (!isDone) {
          isDone = _routeKeyEvent(rootElement, event);
        }

        // Step 3: Standard & System Exit Evaluation
        if (!isDone) {
          final trigger = _detectTrigger(event);
          if (trigger != null && exitConditions.containsKey(trigger)) {
            _handleAction(trigger, event);
            return;
          }
        }

        // Force a redraw to reflect any selections or edits.
        draw();
      }
    },
    onError: (e, stack) {
      if (!_completer!.isCompleted) {
        _completer!.completeError(e, stack);
      }
    },
  );

  try {
    return await _completer!.future;
  } finally {
    _isDisposed = true;
    await subscription.cancel();
    // Restore cursor visibility
    terminal.showCursor();
    // Reset the repaint handler to avoid leaks.
    State.onNeedRepaint = null;
    _rootElement?.unmount();
  }
}