toPromptString method

String toPromptString()

Format all elements as a human-readable prompt section for the LLM.

The output is structured to help the LLM understand the screen:

  • Scroll indicators at the top if the screen has more content
  • Elements grouped under header sections for visual clarity
  • Noise filtered out (scrollable containers, empty-label elements)

Implementation

String toPromptString() {
  if (elements.isEmpty) return '(No UI elements detected on screen)';

  final buffer = StringBuffer();

  // Scroll awareness: tell the LLM there's more content.
  if (canScrollDown && canScrollUp) {
    buffer.writeln(
      '** This screen has MORE CONTENT above AND below — scroll to see all content. **',
    );
  } else if (canScrollDown) {
    buffer.writeln(
      '** This screen has MORE CONTENT below — scroll down to see it. **',
    );
  } else if (canScrollUp) {
    buffer.writeln(
      '** This screen has MORE CONTENT above — scroll up to see it. **',
    );
  }

  // Filter out noise:
  // - Scrollable containers (structural, not content)
  // - Elements with empty labels AND no value (invisible/structural)
  final meaningful = elements.where((e) {
    if (e.type == UiElementType.scrollable) return false;
    if (e.label.isEmpty && (e.value == null || e.value!.isEmpty)) {
      return false;
    }
    return true;
  }).toList();

  if (meaningful.isEmpty) {
    buffer.writeln('(No visible UI elements detected)');
    return buffer.toString().trimRight();
  }

  // Group elements under headers for better structure.
  int idx = 1;
  for (final element in meaningful) {
    if (element.type == UiElementType.header) {
      // Headers are visual separators — show them distinctly.
      buffer.writeln();
      buffer.writeln('--- ${element.label} ---');
      continue;
    }
    buffer.writeln('  $idx. ${element.toPromptString()}');
    idx++;
  }

  return buffer.toString().trimRight();
}