# Refine Frame

> Media frame that steps an image through generation stages without any layout shift.

A frame for AI-generated media that reserves its box up front and walks the image through generation stages. Queued is a dim, blurred pulse; generating and refining resolve a coarse mosaic into a sharp image with a soft scan line; complete shows the real media and fades the status chip. An error dims the frame and, when you listen for `retry`, shows a retry pill.

<demo-refine-frame>



</demo-refine-frame>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import RefineFrame from '~/components/ui/refine-frame/RefineFrame.vue';
  import type { RefineFrameStatus } from '~/components/ui/refine-frame/types';

  const status = ref<RefineFrameStatus>('generating');

  function retry() {
    status.value = 'queued';
  }
</script>

<template>
  <RefineFrame
    :status="status"
    aspect-ratio="4 / 3"
    :width="320"
    :stage-duration="400"
    @retry="retry"
  >
    <img src="/generated.jpg" alt="" draggable="false" />
  </RefineFrame>
</template>
```

Put the media in the default slot. An `<img>` gets the pixel-mosaic reveal, drawn on a canvas from the loaded image; any other media (video, canvas) still gets the blur, saturation and scale stages. Images from another origin only need to be displayable, the canvas is never read back.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      <code>
        'queued' | 'generating' | 'refining' | 'complete' | 'error'
      </code>
    </td>
    
    <td>
      <code>
        'generating'
      </code>
    </td>
    
    <td>
      The stage. Each change tweens the media to that stage.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aspect-ratio
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '4 / 3'
      </code>
    </td>
    
    <td>
      The box reserved before and during generation, so nothing shifts.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        320
      </code>
    </td>
    
    <td>
      Frame 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>
      Corner radius in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#27272a'
      </code>
    </td>
    
    <td>
      The paper behind the media, and the chip surface.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#f5f5f5'
      </code>
    </td>
    
    <td>
      The ink: chip text, the sweep and the retry pill.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stage-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Each stage tween, in ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sweep
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      A soft band crosses the frame while it works.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-status
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      The chip with the mark and the stage label.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hide-after
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1200
      </code>
    </td>
    
    <td>
      Ms after completion before the chip fades. <code>
        0
      </code>
      
       keeps it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        labels
      </code>
    </td>
    
    <td>
      <code>
        Partial<Record<RefineFrameStatus, string>>
      </code>
    </td>
    
    <td>
      built-in labels
    </td>
    
    <td>
      Chip text per stage: Queued, Generating, Refining, Ready, Failed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        retry-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Retry'
      </code>
    </td>
    
    <td>
      The pill on an error.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the frame.
    </td>
  </tr>
</tbody>
</table>

## Events

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

<tbody>
  <tr>
    <td>
      <code>
        retry
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      The retry pill was pressed. Without a <code>
        @retry
      </code>
      
       listener, no pill.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      The media: an <code>
        img
      </code>
      
      , <code>
        video
      </code>
      
       or <code>
        canvas
      </code>
      
      . It fills the frame.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The frame is an image labelled with the current stage, and it sets `aria-busy` while it works.
- A visually hidden `role="status"` region announces each stage change.
- The retry pill is a real button with a visible focus ring.
- With `prefers-reduced-motion`, the mosaic jumps straight to each stage, and the sweep, pulse and spinner stop.
- Ported from [React Bits](https://reactbits.dev/micro/refine-frame) by David Haz.
