# Prompt Bar

A chat composer. Type `@` to pick a source, `/` to pick a command, or press the plus button for the source menu. The model picker and the effort slider sit in the toolbar; at the top effort the field takes the spark colour and sparks drift up through it, faster as you type. Enter sends, Shift+Enter adds a line, and while `busy` the send tile morphs into a stop square. Arrow keys move through any open menu, Enter or Tab picks, Escape closes.

<demo-prompt-bar>



</demo-prompt-bar>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/prompt-bar.json"
```

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import PromptBar from '~/components/ui/prompt-bar/PromptBar.vue';
  import type { PromptBarSendDetail } from '~/components/ui/prompt-bar/types';

  const busy = ref(false);

  function onSend(text: string, detail: PromptBarSendDetail) {
    busy.value = true;
    console.log(text, detail.model?.name, detail.effort, detail.attachments);
  }

  function attach() {
    return ['brief.pdf'];
  }

  function dictate() {
    return Promise.resolve('Compare the last two quarters of sales');
  }
</script>

<template>
  <PromptBar
    :busy="busy"
    :on-attach="attach"
    :on-dictate="dictate"
    default-effort="High"
    @send="onSend"
    @stop="busy = false"
  />
</template>
```

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        placeholder
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Ask anything'
      </code>
    </td>
    
    <td>
      Shown while the field is empty.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sources
      </code>
    </td>
    
    <td>
      <code>
        PromptBarSource[]
      </code>
    </td>
    
    <td>
      <code>
        DEFAULT_SOURCES
      </code>
    </td>
    
    <td>
      Rows of the @ menu and the plus button: <code>
        key
      </code>
      
      , <code>
        name
      </code>
      
      , <code>
        description
      </code>
      
      , <code>
        icon
      </code>
      
       (Iconify name), and <code>
        attach: true
      </code>
      
       for the row that adds files.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commands
      </code>
    </td>
    
    <td>
      <code>
        PromptBarCommand[]
      </code>
    </td>
    
    <td>
      <code>
        DEFAULT_COMMANDS
      </code>
    </td>
    
    <td>
      Rows of the / menu: <code>
        key
      </code>
      
      , <code>
        name
      </code>
      
       (with the slash), <code>
        description
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        models
      </code>
    </td>
    
    <td>
      <code>
        PromptBarModel[]
      </code>
    </td>
    
    <td>
      <code>
        DEFAULT_MODELS
      </code>
    </td>
    
    <td>
      Rows of the model picker: <code>
        key
      </code>
      
      , <code>
        name
      </code>
      
      , <code>
        tag
      </code>
      
      . An empty list hides the picker.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-model
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Key of the model selected at first. Empty picks the first.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        efforts
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        DEFAULT_EFFORTS
      </code>
    </td>
    
    <td>
      Steps of the effort slider, low to high. The last step turns on the spark wash and sparks. An empty list hides the control.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        default-effort
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      The step selected at first. Empty picks the middle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        busy
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      A response is in flight. The send tile stays ink and its arrow morphs into a stop square.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        on-attach
      </code>
    </td>
    
    <td>
      <code>
        () => string | string[] | Promise<string | string[]>
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Called when the attach row is picked. Return file names, or a promise of them, and they appear as chips.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        on-dictate
      </code>
    </td>
    
    <td>
      <code>
        () => string | Promise<string>
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The mic. Return the transcript, or a promise of it, and it lands in the draft. Omit to hide the mic.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      The field surface, and the glyph on an armed tile.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      The ink: text, icons, and the armed tile.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        menu-background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#323236'
      </code>
    </td>
    
    <td>
      The surface of the menus and the effort popover.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spark-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#b39dff'
      </code>
    </td>
    
    <td>
      The wash, the sparks and the slider at the top effort.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spark-boost
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How strongly typing drives the sparks at the top effort. <code>
        0
      </code>
      
       keeps them calm.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Field width in px, capped at the parent.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Field corner radius in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-rows
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      Rows the field grows to before it scrolls.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        morph-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        240
      </code>
    </td>
    
    <td>
      Arrow to square and back, in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        squash
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.12
      </code>
    </td>
    
    <td>
      Mid-morph pinch. The glyph narrows by this and grows taller to keep its area.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tilt
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        8
      </code>
    </td>
    
    <td>
      Mid-morph lean in degrees, mirrored on the way back.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        press-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.96
      </code>
    </td>
    
    <td>
      Scale of the send tile while a pointer is down.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root.
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        send
      </code>
    </td>
    
    <td>
      <code>
        (text: string, detail: { attachments, model, effort })
      </code>
    </td>
    
    <td>
      Enter or the tile, with a non-empty draft or an attachment. The draft clears.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stop
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The tile was pressed while <code>
        busy
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        effort-change
      </code>
    </td>
    
    <td>
      <code>
        (effort: string)
      </code>
    </td>
    
    <td>
      The effort slider moved.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The menus are a `listbox` of `option` rows (`aria-selected` follows the keyboard cursor); the effort popover is a `dialog` holding a `slider` with `aria-valuetext`. Arrow keys, Home and End move the slider.
- Toolbar buttons expose `aria-expanded` / `aria-pressed` and show a focus ring on keyboard focus.
- With `prefers-reduced-motion: reduce` the sparks, the menu pop, the glyph morph and the press scale are switched off.
- Ported from [React Bits](https://reactbits.dev/micro/prompt-bar) by David Haz.
