formatShellPrompt function

String formatShellPrompt({
  1. required String principal,
  2. required String node,
  3. required String cwd,
  4. String? branch,
  5. String? gitStatus,
  6. String? privilege,
  7. bool color = true,
  8. int width = 0,
})

Formats the interactive shell prompt — user@node:cwd (git) $ — shared by the native CLI connect loop and web clients so the prompt looks the same everywhere. Pure string assembly with no I/O, so it compiles to JavaScript.

When color is true, segments are ANSI-colored: user@node green, cwd cyan, the git segment blue with a red branch and green status counts, and the privilege warning bold red. When false (e.g. NO_COLOR or a non-TTY) a plain prompt is returned. A (⚠ privilege) segment appears only when privilege is set (superuser). branch/gitStatus render a git(...) segment when a branch is present.

When width is a positive terminal column count, the prompt shrinks progressively to fit: the least-aggressive level whose visible width leaves at least _typingRoom columns free is chosen, dropping the git status counts, then @node, then shortening cwd to …/basename, then the whole git segment. The (⚠ privilege) warning is always kept. width <= 0 (unknown, or web clients) always renders the full form.

Implementation

String formatShellPrompt({
  required String principal,
  required String node,
  required String cwd,
  String? branch,
  String? gitStatus,
  String? privilege,
  bool color = true,
  int width = 0,
}) {
  final hasCounts = gitStatus != null && gitStatus.isNotEmpty;
  final shortCwd = _shortCwd(cwd);
  final priv = privilege == null ? '' : ' (⚠ $privilege)';

  // Progressive levels, least → most aggressive: (showNode, useShortCwd,
  // showGit, showCounts). Widths are monotonically non-increasing, so a
  // first-fit scan finds the least-aggressive level that fits.
  const levels = [
    (true, false, true, true), // 0: full
    (true, false, true, false), // 1: drop git counts
    (false, false, true, false), // 2: drop @node
    (false, true, true, false), // 3: cwd → …/basename
    (false, true, false, false), // 4: drop git segment
  ];

  String plain(int level) {
    final (showNode, useShort, showGit, showCounts) = levels[level];
    final nodePart = showNode ? '@$node' : '';
    final cwdPart = useShort ? shortCwd : cwd;
    final counts = showCounts && hasCounts ? ' $gitStatus' : '';
    final git = branch == null || !showGit ? '' : ' git($branch$counts)';
    return '$principal$nodePart:$cwdPart$git$priv \$ ';
  }

  var level = 0;
  if (width > 0) {
    level = levels.length - 1; // fallback: nothing fits, use the most compact.
    for (var i = 0; i < levels.length; i++) {
      if (plain(i).runes.length + _typingRoom <= width) {
        level = i;
        break;
      }
    }
  }

  if (!color) return plain(level);

  const reset = '\u001b[0m';
  const red = '\u001b[31m';
  const boldRed = '\u001b[1;31m';
  const green = '\u001b[32m';
  const blue = '\u001b[34m';
  const cyan = '\u001b[36m';
  final (showNode, useShort, showGit, showCounts) = levels[level];
  final nodePart = showNode ? '@$node' : '';
  final cwdPart = useShort ? shortCwd : cwd;
  final counts = showCounts && hasCounts ? ' $gitStatus' : '';
  final coloredCounts = counts.isEmpty ? '' : '$green$counts$red';
  final git = branch == null || !showGit
      ? ''
      : ' ${blue}git($red$branch$coloredCounts$reset$blue)$reset';
  final coloredPriv = privilege == null ? '' : ' $boldRed(⚠ $privilege)$reset';
  return '$green$principal$nodePart$reset:$cyan$cwdPart$reset$git'
      '$coloredPriv \$ ';
}