PixelDosa UI

Cost Estimate

What a run will cost and how long it will take, before it starts — as a range, with the basis named.

Before this runs

Research the top 5 competitors · All 5 competitors, 12 sources

Estimated cost$1.10 to $1.80

Estimated time4 to 6 minutes

Medium confidencevia your last 20 runs
  • Searching 12 sources$0.30
  • Reading 40 pagesLong pages cost more$0.60 to $1.20
  • Writing the comparison$0.20

$8.20 of $50 left this month

An estimate, not a quote.

Installation

bash
npx shadcn@latest add @pixeldosa/cost-estimate

Requires `cn` at `@/lib/utils` and @pixeldosa/confidence-meter, which installs automatically. Format `cost`, `duration` and `balance` yourself: the component never formats currency or time.

Usage

import { CostEstimate } from "@/components/ui/cost-estimate";

<CostEstimate
  task="Research the top 5 competitors"
  cost={{ low: "$1.10", high: "$1.80" }}
  duration="4 to 6 minutes"
  confidence="medium"
  basis="your last 20 runs"
  balance={{ label: "$8.20 of $50 left this month", used: 0.82 }}
  onRun={start}
  onCancel={dismiss}
/>

You format every figure. The component never guesses a currency, a credit unit or a duration.

Why it exists

People decide whether to let an agent run on two numbers nobody usually shows them: money and time. Showing them turns "just try it and see" into a decision, and it is the honest moment to admit an estimate is an estimate.

Features

  • A range, not a figure. cost takes low and an optional high. Passing only low is a deliberate claim that you know the price.
  • Confidence in tiers. Reuses Confidence Meter, never a percentage.
  • The basis is named. "Based on your last 20 runs" lets someone judge the number instead of taking it on faith.
  • A breakdown in words. A short list of what the spend is made of: pages read, images generated. No chart.
  • Budget stated before the button. With balance.exceeds, the component says the run would go over the budget, in the blocked colour, above the actions.
  • Holds completely still. It's your turn, and this one is about money.

Variants

With a breakdown and a budget

Before this runs

Generate 40 product images

Estimated cost480 credits to 600 credits

Estimated timeabout 8 minutes

High confidencevia the last 200 images
  • 40 images at 1024×1024400 credits
  • Upscaling the 10 you keepOnly if you upscale80 to 200 credits

1,250 credits left

An estimate, not a quote.

Over budget

Before this runs

Re-run the full catalogue

Estimated cost$62 to $78

Estimated timeabout 40 minutes

Low confidencevia one similar run

$18.40 of $50 left this month · this run would go over it

An estimate, not a quote.

A single figure you're sure of

Before this runs

Summarise this thread

Estimated cost2 credits

Estimated timea few seconds

An estimate, not a quote.

Props

PropTypeNotes
cost{ low: string; high?: string }Required, formatted by you. Omit high only when the price is known.
taskstringWhat the run is, in the user's words.
durationstringPlain words: "4 to 6 minutes".
confidence"low" | "medium" | "high"Renders Confidence Meter.
basisstringWhere the estimate comes from.
items{ id, label, amount?, detail? }[]What the spend is made of.
balance{ label, used?, exceeds? }Budget or credits left. used is measured, 0–1.
onRun / runLabel() => void / stringDefaults to "Run it".
onAdjust / adjustLabel() => void / stringFor "Narrow the scope".
onCancel() => void
titlestringDefaults to "Before this runs".
headingLevel2 | 3 | 4 | 5 | 6Defaults to 3.

Accessibility

  • A labelled <section> with a real heading, so it fits your page outline.
  • Every state is in words. The over-budget warning is a sentence, not a colour.
  • The budget bar is decorative and hidden; its label carries the same information.
  • Nothing animates, so there is nothing to pause.

When not to use

  • During a run. Live spend belongs to a meter beside the run, not to this.
  • On a billing page. This is a decision surface, not an invoice.
  • For token counts. Tokens aren't a unit people can judge. Convert to money, credits or time first.

why it's built this way

Engineering Notes

The API refuses the two lies this surface usually tells. First, a single figure: `cost` is a range, and a caller who genuinely knows the price passes only `low`, which makes precision an explicit choice rather than the default. Second, a percentage of certainty: confidence reuses the shipped three-tier Confidence Meter instead of inventing a number, and `basis` names the evidence so the estimate can be judged. Everything arrives pre-formatted as strings because currency, locale and credit units belong to the host product; a component that formatted money would have to guess a currency and would be wrong in half the places it renders. The only proportion drawn is the budget bar, which comes from a measured `used` value — the same rule the rest of the system applies to progress. `exceeds` is a separate flag rather than something derived from the numbers, because only the caller knows whether the estimate's high end, the low end, or an average is the figure that counts against a budget.

how it moves, and when it doesn't

Motion Notes

Nothing moves. This is the user's turn in the fullest sense — a decision about money — so the component holds completely still, with no shimmer, pulse or progress. The only transitions are the standard instant colour changes on its buttons, and those are disabled under `prefers-reduced-motion`.