shadimage
mui-treasury
itemai-contextsource: ok · hit

Ai Context

A ai-context item.

npx shadcn add "https://mui-treasury.com/r/ai-context.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
@mui/materialaitokenlens@emotion/react@emotion/styled

Files2 · source cached 10/14/2026, 2:16:38 PM

components/ai-context/ai-context.tsx
'use client';

import {
  type ComponentProps,
  type ReactNode,
  createContext,
  useContext,
} from 'react';

import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import LinearProgress from '@mui/material/LinearProgress';
import Typography from '@mui/material/Typography';
import type { LanguageModelUsage } from 'ai';
import { type ModelId, estimateCost } from 'tokenlens';

const PERCENT_MAX = 100;
const ICON_RADIUS = 10;
const ICON_VIEWBOX = 24;
const ICON_CENTER = 12;
const ICON_STROKE_WIDTH = 2;

type ContextSchema = {
  usedTokens: number;
  maxTokens: number;
  usage?: LanguageModelUsage;
  modelId?: ModelId;
};

const ContextContext = createContext<ContextSchema | null>(null);

const useContextValue = () => {
  const context = useContext(ContextContext);

  if (!context) {
    throw new Error('Context components must be used within Context');
  }

  return context;
};

export type ContextProps = {
  children: ReactNode;
} & ContextSchema;

export const Context = ({
  usedTokens,
  maxTokens,
  usage,
  modelId,
  children,
}: ContextProps) => (
  <ContextContext.Provider
    value={{
      usedTokens,
      maxTokens,
      usage,
      modelId,
    }}
  >
    {children}
  </ContextContext.Provider>
);

const ContextIcon = () => {
  const { usedTokens, maxTokens } = useContextValue();
  const circumference = 2 * Math.PI * ICON_RADIUS;
  const usedPercent = usedTokens / maxTokens;
  const dashOffset = circumference * (1 - usedPercent);

  return (
    <svg
      aria-label="Model context usage"
      height="20"
      role="img"
      style={{ color: 'currentcolor' }}
      viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`}
      width="20"
    >
      <circle
        cx={ICON_CENTER}
        cy={ICON_CENTER}
        fill="none"
        opacity="0.25"
        r={ICON_RADIUS}
        stroke="currentColor"
        strokeWidth={ICON_STROKE_WIDTH}
      />
      <circle
        cx={ICON_CENTER}
        cy={ICON_CENTER}
        fill="none"
        opacity="0.7"
        r={ICON_RADIUS}
        stroke="currentColor"
        strokeDasharray={`${circumference} ${circumference}`}
        strokeDashoffset={dashOffset}
        strokeLinecap="round"
        strokeWidth={ICON_STROKE_WIDTH}
        style={{ transformOrigin: 'center', transform: 'rotate(-90deg)' }}
      />
    </svg>
  );
};

export type ContextTriggerProps = ComponentProps<typeof Button> & {
  onOpen?: (event: React.MouseEvent<HTMLElement>) => void;
};

export const ContextTrigger = ({
  children,
  onOpen,
  sx,
  ...props
}: ContextTriggerProps) => {
  const { usedTokens, maxTokens } = useContextValue();
  const usedPercent = usedTokens / maxTokens;
  const renderedPercent = new Intl.NumberFormat('en-US', {
    style: 'percent',
    maximumFractionDigits: 1,
  }).format(usedPercent);

  return (
    children ?? (
      <Button
        type="button"
        variant="text"
        onClick={onOpen}
        sx={{
          minWidth: 'auto',
          p: 1,
          color: 'text.secondary',
          '&:hover': {
            bgcolor: 'action.hover',
          },
          ...sx,
        }}
        {...props}
      >
        <Typography
          component="span"
          sx={{ fontWeight: 500, mr: 1, color: 'text.secondary' }}
        >
          {renderedPercent}
        </Typography>
        <ContextIcon />
      </Button>
    )
  );
};

export type ContextContentProps = ComponentProps<typeof Box>;

export const ContextContent = ({
  sx,
  children,
  ...props
}: ContextContentProps) => (
  <Box
    sx={{
      minWidth: 240,
      '& > *:not(:last-child)': {
        borderBottom: 1,
        borderColor: 'divider',
      },
      ...sx,
    }}
    {...props}
  >
    {children}
  </Box>
);

export type ContextContentHeaderProps = ComponentProps<typeof Box>;

export const ContextContentHeader = ({
  children,
  sx,
  ...props
}: ContextContentHeaderProps) => {
  const { usedTokens, maxTokens } = useContextValue();
  const usedPercent = usedTokens / maxTokens;
  const displayPct = new Intl.NumberFormat('en-US', {
    style: 'percent',
    maximumFractionDigits: 1,
  }).format(usedPercent);
  const used = new Intl.NumberFormat('en-US', {
    notation: 'compact',
  }).format(usedTokens);
  const total = new Intl.NumberFormat('en-US', {
    notation: 'compact',
  }).format(maxTokens);

  return (
    <Box sx={{ width: '100%', p: 1.5, ...sx }} {...props}>
      {children ?? (
        <>
          <Box
            sx={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'space-between',
              gap: 1.5,
              mb: 1,
            }}
          >
            <Typography variant="caption">{displayPct}</Typography>
            <Typography
              variant="caption"
              sx={{ fontFamily: 'monospace', color: 'text.secondary' }}
            >
              {used} / {total}
            </Typography>
          </Box>
          <LinearProgress
            variant="determinate"
            value={usedPercent * PERCENT_MAX}
            sx={{ height: 6, borderRadius: 1, bgcolor: 'action.hover' }}
          />
        </>
      )}
    </Box>
  );
};

export type ContextContentBodyProps = ComponentProps<typeof Box>;

export const ContextContentBody = ({
  children,
  sx,
  ...props
}: ContextContentBodyProps) => (
  <Box sx={{ width: '100%', p: 1.5, ...sx }} {...props}>
    {children}
  </Box>
);

export type ContextContentFooterProps = ComponentProps<typeof Box>;

export const ContextContentFooter = ({
  children,
  sx,
  ...props
}: ContextContentFooterProps) => {
  const { modelId, usage } = useContextValue();
  const costUSD = modelId
    ? estimateCost({
        modelId,
        usage: {
          input: usage?.inputTokens ?? 0,
          output: usage?.outputTokens ?? 0,
        },
      }).totalUSD
    : undefined;
  const totalCost = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(costUSD ?? 0);

  return (
    <Box
      sx={{
        display: 'flex',
        width: '100%',
        alignItems: 'center',
        justifyContent: 'space-between',
        gap: 1.5,
        bgcolor: 'action.hover',
        p: 1.5,
        ...sx,
      }}
      {...props}
    >
      {children ?? (
        <>
          <Typography variant="caption" sx={{ color: 'text.secondary' }}>
            Total cost
          </Typography>
          <Typography variant="caption">{totalCost}</Typography>
        </>
      )}
    </Box>
  );
};

export type ContextInputUsageProps = ComponentProps<typeof Box>;

export const ContextInputUsage = ({
  sx,
  children,
  ...props
}: ContextInputUsageProps) => {
  const { usage, modelId } = useContextValue();
  const inputTokens = usage?.inputTokens ?? 0;

  if (children) {
    return <>{children}</>;
  }

  if (!inputTokens) {
    return null;
  }

  const inputCost = modelId
    ? estimateCost({
        modelId,
        usage: { input: inputTokens, output: 0 },
      }).totalUSD
    : undefined;
  const inputCostText = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(inputCost ?? 0);

  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        ...sx,
      }}
      {...props}
    >
      <Typography variant="caption" sx={{ color: 'text.secondary' }}>
        Input
      </Typography>
      <TokensWithCost costText={inputCostText} tokens={inputTokens} />
    </Box>
  );
};

export type ContextOutputUsageProps = ComponentProps<typeof Box>;

export const ContextOutputUsage = ({
  sx,
  children,
  ...props
}: ContextOutputUsageProps) => {
  const { usage, modelId } = useContextValue();
  const outputTokens = usage?.outputTokens ?? 0;

  if (children) {
    return <>{children}</>;
  }

  if (!outputTokens) {
    return null;
  }

  const outputCost = modelId
    ? estimateCost({
        modelId,
        usage: { input: 0, output: outputTokens },
      }).totalUSD
    : undefined;
  const outputCostText = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(outputCost ?? 0);

  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        ...sx,
      }}
      {...props}
    >
      <Typography variant="caption" sx={{ color: 'text.secondary' }}>
        Output
      </Typography>
      <TokensWithCost costText={outputCostText} tokens={outputTokens} />
    </Box>
  );
};

export type ContextReasoningUsageProps = ComponentProps<typeof Box>;

export const ContextReasoningUsage = ({
  sx,
  children,
  ...props
}: ContextReasoningUsageProps) => {
  const { usage, modelId } = useContextValue();
  const reasoningTokens = usage?.reasoningTokens ?? 0;

  if (children) {
    return <>{children}</>;
  }

  if (!reasoningTokens) {
    return null;
  }

  const reasoningCost = modelId
    ? estimateCost({
        modelId,
        usage: { reasoningTokens },
      }).totalUSD
    : undefined;
  const reasoningCostText = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(reasoningCost ?? 0);

  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        ...sx,
      }}
      {...props}
    >
      <Typography variant="caption" sx={{ color: 'text.secondary' }}>
        Reasoning
      </Typography>
      <TokensWithCost costText={reasoningCostText} tokens={reasoningTokens} />
    </Box>
  );
};

export type ContextCacheUsageProps = ComponentProps<typeof Box>;

export const ContextCacheUsage = ({
  sx,
  children,
  ...props
}: ContextCacheUsageProps) => {
  const { usage, modelId } = useContextValue();
  const cacheTokens = usage?.cachedInputTokens ?? 0;

  if (children) {
    return <>{children}</>;
  }

  if (!cacheTokens) {
    return null;
  }

  const cacheCost = modelId
    ? estimateCost({
        modelId,
        usage: { cacheReads: cacheTokens, input: 0, output: 0 },
      }).totalUSD
    : undefined;
  const cacheCostText = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(cacheCost ?? 0);

  return (
    <Box
      sx={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        ...sx,
      }}
      {...props}
    >
      <Typography variant="caption" sx={{ color: 'text.secondary' }}>
        Cache
      </Typography>
      <TokensWithCost costText={cacheCostText} tokens={cacheTokens} />
    </Box>
  );
};

const TokensWithCost = ({
  tokens,
  costText,
}: {
  tokens?: number;
  costText?: string;
}) => (
  <Typography component="span" variant="caption">
    {tokens === undefined
      ? '—'
      : new Intl.NumberFormat('en-US', {
          notation: 'compact',
        }).format(tokens)}
    {costText ? (
      <Typography
        component="span"
        variant="caption"
        sx={{ ml: 1, color: 'text.secondary' }}
      >
        • {costText}
      </Typography>
    ) : null}
  </Typography>
);
components/ai-context/index.ts
export * from './ai-context';

More from mui-treasury

Browse all components in this library →