tinyjuice 0.2.1

Pluggable token compression for OpenHuman.
Documentation
import type { AlgorithmSummary, CompressionRun } from "./types";

type TrendPoint = {
  label: string;
  value: number;
};

export function TokenTrend({ runs }: { runs: CompressionRun[] }) {
  const points = toTrend(runs);
  const width = 720;
  const height = 220;
  const path = linePath(points, width, height);
  const area = path ? `${path} L ${width} ${height} L 0 ${height} Z` : "";

  return (
    <svg className="chart" viewBox={`0 0 ${width} ${height}`} role="img" aria-label="Token savings trend">
      <defs>
        <linearGradient id="trendFill" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#2f8f83" stopOpacity="0.32" />
          <stop offset="100%" stopColor="#2f8f83" stopOpacity="0.02" />
        </linearGradient>
      </defs>
      <rect x="0" y="0" width={width} height={height} rx="8" className="chart-bg" />
      {[0, 1, 2, 3].map((tick) => (
        <line
          key={tick}
          x1="0"
          x2={width}
          y1={(height / 4) * tick + 18}
          y2={(height / 4) * tick + 18}
          className="grid-line"
        />
      ))}
      {area && <path d={area} fill="url(#trendFill)" />}
      {path && <path d={path} className="trend-line" />}
      {points.map((point, index) => {
        const [x, y] = pointCoords(point.value, index, points, width, height);
        return (
          <g key={`${point.label}-${index}`}>
            <circle cx={x} cy={y} r="4" className="trend-dot" />
            {index === points.length - 1 && (
              <text x={Math.max(8, x - 66)} y={Math.max(18, y - 10)} className="chart-label">
                {formatCompact(point.value)} saved
              </text>
            )}
          </g>
        );
      })}
    </svg>
  );
}

export function AlgorithmBars({ summaries }: { summaries: AlgorithmSummary[] }) {
  const maxSaved = Math.max(1, ...summaries.map((summary) => summary.savedTokens));
  return (
    <div className="bars" aria-label="Algorithm token savings">
      {summaries.map((summary) => {
        const width = Math.max(4, (summary.savedTokens / maxSaved) * 100);
        const reduction = summary.originalTokens
          ? (summary.savedTokens / summary.originalTokens) * 100
          : 0;
        return (
          <div className="bar-row" key={summary.algorithm}>
            <div className="bar-meta">
              <span className="bar-title">{labelize(summary.algorithm)}</span>
              <span>{summary.runs} runs</span>
            </div>
            <div className="bar-track">
              <span className="bar-fill" style={{ width: `${width}%` }} />
            </div>
            <div className="bar-values">
              <span>{formatCompact(summary.savedTokens)} tokens</span>
              <span>{reduction.toFixed(1)}% loaded-run reduction</span>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function toTrend(runs: CompressionRun[]): TrendPoint[] {
  let total = 0;
  return runs.map((run) => {
    total += Math.max(0, run.originalTokens - run.compressedTokens);
    return {
      label: run.id,
      value: total,
    };
  });
}

function linePath(points: TrendPoint[], width: number, height: number): string {
  if (!points.length) {
    return "";
  }
  return points
    .map((point, index) => {
      const [x, y] = pointCoords(point.value, index, points, width, height);
      return `${index === 0 ? "M" : "L"} ${x.toFixed(2)} ${y.toFixed(2)}`;
    })
    .join(" ");
}

function pointCoords(
  value: number,
  index: number,
  points: TrendPoint[],
  width: number,
  height: number,
): [number, number] {
  const max = Math.max(1, ...points.map((point) => point.value));
  const x = points.length === 1 ? width / 2 : (index / (points.length - 1)) * width;
  const y = height - (value / max) * (height - 32) - 16;
  return [x, y];
}

function formatCompact(value: number): string {
  return Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 }).format(value);
}

function labelize(value: string): string {
  return value.replace(/_/g, " ");
}