oli-server 0.1.4

A simple, blazingly fast AI coding assistant server
Documentation
import React, { useMemo } from "react";
import { Box, Text } from "ink";
import theme from "../styles/gruvbox.js";
import { ToolStatus, ToolData } from "../types/index.js";
import AnimatedSpinner from "./AnimatedSpinner.js";

interface ToolStatusIndicatorProps {
  status: ToolStatus;
  data?: ToolData;
  compact?: boolean;
}

const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({
  status,
  data,
  compact = false,
}) => {
  if (!data) {
    return null;
  }

  // Get the appropriate status indicator and color - memoized to prevent rerenders
  const statusIndicator = useMemo(() => {
    switch (status) {
      case "running":
        return <AnimatedSpinner color={theme.colors.dark.blue} />;
      case "success":
        return <Text color={theme.colors.dark.green}>✓</Text>;
      case "error":
        return <Text color={theme.colors.dark.red}>✗</Text>;
      default:
        return <Text color={theme.colors.dark.gray}>⏺</Text>;
    }
  }, [status]);

  // Format tool name and file path - memoized to prevent recalculation
  const toolTitle = useMemo(() => {
    let title = data.name || "Unknown Tool";

    // Extract file path from data or metadata
    const filePath = data.file_path
      ? data.file_path
      : (data as unknown as { metadata?: { file_path?: string } }).metadata
          ?.file_path;

    if (filePath) {
      // Shorten file path if it's too long
      const displayPath =
        filePath.length > 30
          ? `...${filePath.substring(filePath.length - 30)}`
          : filePath;

      title += ` (${displayPath})`;
    }

    // Add ellipsis if running
    if (status === "running") {
      title += "…";
    }

    return title;
  }, [data.name, data.file_path, status]);

  // Format details based on tool type - memoized to prevent recalculation
  const details = useMemo(() => {
    if (compact) return null;

    // Safely access lines - check both direct property and metadata
    const lines = data.lines
      ? data.lines
      : (data as unknown as { metadata?: { lines?: number } }).metadata?.lines;

    if (lines) {
      return `Read ${lines} lines`;
    }

    // Safely access description - check both direct property and metadata
    const description = data.description
      ? data.description
      : (data as unknown as { message?: string }).message ||
        (data as unknown as { metadata?: { description?: string } }).metadata
          ?.description;

    if (description) {
      return description;
    }

    return null;
  }, [data, compact]);

  // Get appropriate color for status
  const statusColor = useMemo(() => {
    switch (status) {
      case "running":
        return theme.colors.dark.blue;
      case "success":
        return theme.colors.dark.green;
      case "error":
        return theme.colors.dark.red;
      default:
        return theme.colors.dark.gray;
    }
  }, [status]);

  return (
    <Box flexDirection="column">
      <Box flexDirection="row">
        {statusIndicator}
        <Text color={statusColor} bold>
          {" "}
          {toolTitle}
        </Text>
      </Box>

      {details && (
        <Box marginLeft={2} flexDirection="row">
          <Text color={theme.colors.dark.gray}> ⎿ {details}</Text>
        </Box>
      )}
    </Box>
  );
};

// Export the component with React.memo
const MemoizedToolStatusIndicator = React.memo(ToolStatusIndicator);
export { MemoizedToolStatusIndicator as ToolStatusIndicator };
export default MemoizedToolStatusIndicator;