run method

void run()

Initializes and runs the app.

  1. Reads terminal size using TerminalFunctions.
  2. Creates a CanvasBuffer for rendering.
  3. Initializes input handling, focus, and interactivity.
  4. Measures layout and renders the component tree.

Implementation

void run() async {
  final AppInstance appInstance = createInstance() as AppInstance;

  final rawTerminalWidth = TerminalFunctions.hasTerminal
      ? TerminalFunctions.terminalWidth
      : 80;
  final rawTerminalHeight = TerminalFunctions.hasTerminal
      ? TerminalFunctions.terminalHeight
      : 20;

  final LayoutEngine engine = LayoutEngine(
    rootInstance: appInstance,
    children: appInstance.childrenInstance,
    direction: direction,
    bounds: Rect(
      x: 0,
      y: 0,
      width: rawTerminalWidth,
      height: rawTerminalHeight,
    ),
  );

  final layoutHeight = engine.fitHeight();
  final layoutWidth = engine.fitWidth();

  final terminalWidth = min(rawTerminalWidth, layoutWidth);
  final terminalHeight = min(rawTerminalHeight, layoutHeight);

  final buffer = CanvasBuffer(width: terminalWidth, height: terminalHeight);
  final RenderManager renderer = RenderManager(buffer: buffer);
  final Context context = Context();

  final InputDispatcher dispatcher = InputDispatcher(renderer: renderer);
  final InputManager inputManager = InputManager(dispatcher: dispatcher);
  final supportsCursor = await inputManager.isCursorSupported();
  if (supportsCursor) {
    Logger.trace(App._tag, "Supports cursor fetching cursor position");
    final point = await inputManager.fetchCursorPosition();
    buffer.setTerminalOffset(point.x + 1, point.y + 1);
    context.setInitialCursorPosition(point.x, point.y);

    inputManager.returnedCursorPositionX = point.x + 1;
    inputManager.returnedCursorPositionY = point.y + terminalHeight + 2;
  } else {
    Logger.warn(
      App._tag,
      "Terminal doesn't support cursor. Ignore if this is a test environment",
    );
  }

  final FocusManager focusManager = FocusManager(context: context);
  final ComponentInputHandler componentInputHandler = ComponentInputHandler(
    focusManager,
  );

  final InteractableRegistry registry = InteractableRegistry();

  registry.registerInteractables(appInstance, focusManager, renderer);

  final List<InputHandler> handlers = [
    focusManager,
    CommandModeHandler(),
    componentInputHandler,
  ];

  for (var handler in handlers) {
    dispatcher.registerHandler(handler);
  }

  for (final handler in InputRegistry.handlers) {
    Logger.trace(App._tag, 'Registering handler $handler');
    dispatcher.registerHandler(handler);
  }

  final measuredSize = appInstance.measure(
    Size(width: rawTerminalWidth, height: rawTerminalHeight),
  );

  final bounds = Rect(
    x: 0,
    y: 0,
    width: measuredSize.width,
    height: measuredSize.height,
  );

  Logger.trace(App._tag, 'Writing Components to Canvas Buffer');
  appInstance.render(buffer, bounds);
  Logger.trace(App._tag, 'READY');
  buffer.render();

  Timer.periodic(Duration(milliseconds: 16), (timer) {
    if (appInstance.shouldRebuild || renderer.needsRecompute) {
      appInstance.shouldRebuild = false;
      appInstance._childrenInstance =
          appInstance.initialChildren; // reassign to reset any tree state

      buffer.clear();

      focusManager.reset();
      registry.registerInteractables(appInstance, focusManager, renderer);

      final engine = LayoutEngine(
        rootInstance: appInstance,
        children: appInstance.childrenInstance,
        direction: direction,
        bounds: Rect(x: 0, y: 0, width: bounds.width, height: bounds.height),
      );

      final int termWidth = min(engine.fitWidth(), rawTerminalWidth);
      final int termHeight = min(engine.fitHeight(), rawTerminalHeight);

      var (x, y) = buffer.getTerminalOffset();

      inputManager.returnedCursorPositionX = x + 1;
      inputManager.returnedCursorPositionY = y + termWidth + 2;

      buffer.updateDimensions(termWidth, termHeight);

      final items = engine.compute(
        Size(width: bounds.width, height: bounds.height),
      );

      renderer.requestRecompute(bounds);
      renderer.needsRecompute = false;
      appInstance._renderPositionedComponents(buffer, items);
      buffer.render();
    }
  });
}