# 🖥️ React Command Flow

A beautifully interactive CLI-style command interface for React apps.  
Type commands, flow through steps, and build dynamic, conversational UI experiences.

---

## ✨ Features

- 🖋 Terminal-style input & output
- 📋 Step-based command flows
- ✅ Built-in validation (sync & async)
- 🔀 Conditional prompts, branching logic
- ⚡ Dynamic suggestions & tab completion
- 💅 Theme support
- 🔍 Help & clear commands out of the box
- 🔌 Easy to extend and customize

---

## 📦 Installation

```bash
npm install react-command-flow
```

---

## ⚡ Usage

```jsx
import React from "react";
import { CommandLine } from "react-command-flow";
import { commands } from "./commands";

export default function App() {
  return (
    <div style={{ padding: "2rem" }}>
      <h1>React Command Flow Demo</h1>
      <CommandLine commands={commands} />
    </div>
  );
}
```

---

## 📚 Defining Commands

```js
const colors = ["Red", "Green", "Blue", "Yellow", "Purple"];
const shapes = ["Circle", "Square", "Triangle", "Rectangle", "Hexagon"];

const commands = {
  "color-shape-quiz": {
    description: "Start your color and shape quiz 🌈🔺",
    steps: [
      {
        prompt: async () => {
          await new Promise((res) => setTimeout(res, 1000)); // async wait
          return `What is your favorite color?\n${colors
            .map((c, i) => `${i + 1}. ${c}`)
            .join("\n")}\nEnter color index:`;
        },
        field: "colorIndex",
        validate: async (val) => {
          return ["1", "2", "3", "4", "5"].includes(val)
            ? true
            : "❌ Please select a number between 1 and 5.";
        },
        redirect: (val) => {
          const color = colors[parseInt(val) - 1];
          if (color === "Red") return "color-red-flow";
          if (color === "Green") return "color-green-flow";
          if (color === "Blue") return "color-blue-flow";
          if (color === "Yellow") return "color-yellow-flow";
          if (color === "Purple") return "color-purple-flow";
          return "color-other-flow";
        },
      },
    ],
    onComplete: () => "Redirecting...",
  },

  "color-red-flow": {
    description: "Red color path 🔴",
    hidden: true,
    steps: [
      {
        prompt: "What shape do you associate with Red?",
        field: "redShape",
        validate: (val) => {
          return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
        },
      },
    ],
    onComplete: async () => {
      await new Promise((res) => setTimeout(res, 1000)); // fake async post
      return "✅ Red is a powerful color!";
    },
  },

  "color-green-flow": {
    description: "Green color path 🟢",
    hidden: true,
    steps: [
      {
        prompt: "What shape do you associate with Green?",
        field: "greenShape",
        validate: (val) => {
          return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
        },
      },
    ],
    onComplete: async () => {
      await new Promise((res) => setTimeout(res, 1000)); // fake async post
      return "✅ Green is a calming color!";
    },
  },

  "color-blue-flow": {
    description: "Blue color path 🔵",
    hidden: true,
    steps: [
      {
        prompt: "What shape do you associate with Blue?",
        field: "blueShape",
        validate: (val) => {
          return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
        },
      },
    ],
    onComplete: async () => {
      await new Promise((res) => setTimeout(res, 1000)); // fake async post
      return "✅ Blue is a soothing color!";
    },
  },

  "color-yellow-flow": {
    description: "Yellow color path 🟡",
    hidden: true,
    steps: [
      {
        prompt: "What shape do you associate with Yellow?",
        field: "yellowShape",
        validate: (val) => {
          return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
        },
      },
      {
        prompt: "What emotion do you associate with Yellow?",
        field: "yellowEmotion",
        validate: (val) => {
          const emotions = ["happiness", "energy", "warmth", "caution"];
          return emotions.includes(val.toLowerCase())
            ? true
            : "❌ Please enter a valid emotion (happiness, energy, warmth, caution).";
        },
      },
      {
        prompt: "What object do you associate with Yellow?",
        field: "yellowObject",
        validate: (val) => {
          return val.length > 0 ? true : "❌ Please enter a valid object.";
        },
      },
    ],
    onComplete: async () => {
      await new Promise((res) => setTimeout(res, 1000)); // fake async post
      return "✅ Yellow is a bright and cheerful color!";
    },
  },

  "color-purple-flow": {
    description: "Purple color path 🟣",
    hidden: true,
    steps: [
      {
        prompt: "What shape do you associate with Purple?",
        field: "purpleShape",
        validate: (val) => {
          return shapes.includes(val) ? true : "❌ Please enter a valid shape.";
        },
      },
    ],
    onComplete: async () => {
      await new Promise((res) => setTimeout(res, 1000)); // fake async post
      return "✅ Purple is a royal color!";
    },
  },

  "color-other-flow": {
    description: "Other color path 🌈",
    hidden: true,
    steps: [],
    onComplete: () => "💛 All colors are beautiful!",
  },

  help: {
    description: "List available commands",
    steps: [],
    onComplete: (_, __, all) => {
      return Object.entries(all)
        .filter(([_, c]) => !c.hidden)
        .map(([name, c]) => `- ${name}: ${c.description}`)
        .join("\n");
    },
  },
  clear: {
    description: "Clear the screen",
    steps: [],
    onComplete: () => "__CLEAR__",
  },
};

// Alias added outside
commands["?"] = commands.help;

export default commands;
```

---

## 🧠 Advanced Examples

- 🔁 Async prompt resolution
- ✅ Custom validation per step
- 🔀 `onStep` logic to transform values
- 🧭 Conditional steps (based on previous answers)
- 📦 Redirects to subcommands (optional)
- 🔄 Repeat functionality with custom repeat questions

### 🎯 Repeat Configuration

Commands can use a comprehensive `repeat` object for full control over the repeat behavior:

```js
const commands = {
  "job-application": {
    description: "Job application flow",
    steps: [
      /* ... */
    ],
    onComplete: (data) => `Application submitted for ${data.name}`,
    repeat: {
      mode: "ask",
      question: "💼 Submit another application ({{yes}},{{no}})?",
      yes: "sure",
      no: "nope",
      wrongInput: "Please select either {{yes}} or {{no}}:",
      yesReply: "Starting new application...",
      noReply: "Application process completed.",
      targetCommand: "other-command", // Optional: redirect to different command
    },
  },

  survey: {
    description: "User survey",
    steps: [
      /* ... */
    ],
    onComplete: (data) => "Survey completed!",
    repeat: {
      mode: "ask",
      // Function-based custom repeat question
      question: ({ yesOption, noOption, commandName }) =>
        `🔄 Take the survey again? Type '${yesOption}' to continue or '${noOption}' to finish.`,
      yes: "y",
      no: "n",
      wrongInput: "Please select either {{yes}} or {{no}}:",
      yesReply: "Let's do another survey!",
      noReply: "Thanks for participating!",
    },
  },

  "async-demo": {
    description: "Async repeat question demo",
    steps: [
      /* ... */
    ],
    onComplete: (data) => "Task completed!",
    repeat: {
      mode: "ask",
      // Async function-based custom repeat question
      question: async ({ yesOption, noOption, commandName }) => {
        // Simulate async operation (e.g., checking user preferences)
        await new Promise((resolve) => setTimeout(resolve, 500));
        return `🤔 Continue with another task? Type '${yesOption}' or '${noOption}'.`;
      },
      yes: "y",
      no: "n",
      wrongInput: "Please select either {{yes}} or {{no}}:",
      yesReply: "Great! Let's continue...",
      noReply: "Task session ended.",
    },
  },
};
```

**Repeat Object Properties:**

- `mode` - "yes", "no", or "ask"
- `question` - Custom repeat question (string or function)
- `yes` - Custom "yes" option text
- `no` - Custom "no" option text
- `wrongInput` - Custom error message for invalid input
- `yesReply` - Custom message when user selects "yes"
- `noReply` - Custom message when user selects "no"
- `targetCommand` - Optional: different command to run on repeat

**Available placeholders for string-based questions and messages:**

- `{{yes}}` - The "yes" option value
- `{{no}}` - The "no" option value
- `{command}` - The command name (legacy format)

**Function parameters for dynamic questions:**

- `yesOption` - The "yes" option string
- `noOption` - The "no" option string
- `commandName` - The name of the command

---

## 🖌️ Theming

Supports custom themes with colors and fonts.  
You can pass a `theme` prop to `<CommandLine />`.

---

## 💡 Example Commands You Can Try

- `hello`
- `profile-setup`
- `select-server`
- `help`
- `clear`

---

## 📜 License

MIT — free to use, modify, contribute.  
Made with 💛 by Stam & React Command Flow Contributors.

---

## ⭐ Star this repo if you like it!

Let's build the coolest CLI UIs in the React world 🌍
