Veer Factor

Paste a quote, get a Veer Factor score. How far outside their expertise is the speaker? From 'In Lane' to 'Car Crash'.

code.tsx

import React, { useState, useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";

interface Insight {
  person: string;
  quote: string;
  score: number;
  field: string;
  expertise: string;
  explanation: string;
  experts: { name: string; credential: string }[];
  expertsSummary: string;
}

function veerLabel(score: number): string {
  if (score <= 15) return "In Lane";
  if (score <= 35) return "Drifting";
  if (score <= 55) return "Swerving";
  if (score <= 75) return "Wrong Lane";
  return "Car Crash";
}

function veerColor(score: number): string {
  if (score <= 20) return "#16a34a";
  if (score <= 40) return "#22c55e";
  if (score <= 60) return "#fbbf24";
  if (score <= 80) return "#f97316";
  return "#ef4444";
}

function Road({ score, expertise, field, numExperts }: { score: number; expertise: string; field: string; numExperts: number }) {
  const roadRef = useRef<HTMLDivElement>(null);
  const walshRef = useRef<HTMLDivElement>(null);
  const expertRefs = useRef<(HTMLDivElement | null)[]>([]);

  useEffect(() => {
    const road = roadRef.current;
    const walsh = walshRef.current;
    if (!road || !walsh) return;

    let frame: number;
    const start = performance.now();
    const walshCycle = 4;
    const expertCycle = 8;
    const carH = 34;
    const carW = 18;
    const crashCooldowns = new Set<number>();

    const animate = () => {
      const t = (performance.now() - start) / 1000;
      const roadH = road.clientHeight;

      // Walsh: top to bottom
      const walshProgress = (t % walshCycle) / walshCycle;
      const walshY = -carH + walshProgress * (roadH + carH);

      const maxSwerve = (score / 100) * 30;
      const swerve =
        Math.sin(t * 1.7) * 0.5 +
        Math.sin(t * 2.9) * 0.3 +
        Math.sin(t * 4.3) * 0.2;
      const walshX = 6 + maxSwerve * (swerve + 1) / 2;

      walsh.style.top = `${walshY}px`;
      walsh.style.left = `${walshX}px`;

      // Expert cars: bottom to top, staggered
      let anyHit = false;
      for (let i = 0; i < numExperts; i++) {
        const el = expertRefs.current[i];
        if (!el) continue;

        const offset = i / numExperts;
        const expertProgress = ((t / expertCycle + offset) % 1);
        const expertY = roadH + carH - expertProgress * (roadH + carH * 2);
        const expertX = 34;

        el.style.top = `${expertY}px`;
        el.style.left = `${expertX}px`;

        const hit = Math.abs(walshY - expertY) < carH && Math.abs(walshX - expertX) < carW;
        if (hit) {
          anyHit = true;
          if (!crashCooldowns.has(i)) {
            crashCooldowns.add(i);
            const burst = document.createElement('div');
            burst.className = 'crash-burst';
            burst.style.top = `${(walshY + expertY) / 2 + carH / 2}px`;
            burst.style.left = `${(walshX + expertX) / 2 + carW / 2}px`;
            road.appendChild(burst);
            setTimeout(() => burst.remove(), 600);
          }
          el.style.filter = 'brightness(2)';
        } else {
          crashCooldowns.delete(i);
          el.style.filter = '';
        }
      }
      walsh.style.filter = anyHit ? 'brightness(2)' : '';

      frame = requestAnimationFrame(animate);
    };

    frame = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(frame);
  }, [score, numExperts]);

  return (
    <div className="road" ref={roadRef}>
      <div className="lane">
        <span className="lane-text" style={{ color: veerColor(score) }}>{expertise}</span>
      </div>
      <div className="lane-divider" />
      <div className="lane">
        <span className="lane-text lane-text-field">{field}</span>
      </div>
      <div className="car" ref={walshRef} style={{ backgroundColor: veerColor(score) }} />
      {Array.from({ length: numExperts }, (_, i) => (
        <div
          key={i}
          className="car car-expert"
          ref={el => { expertRefs.current[i] = el; }}
        />
      ))}
    </div>
  );
}

function App() {
  const [insight, setInsight] = useState<Insight | null>(() => {
    try { return tb.insight<Insight>(); } catch { return null; }
  });
  const [isGenerating, setIsGenerating] = useState(false);

  const handleAnalyze = async () => {
    setIsGenerating(true);
    try {
      const result = await tb.infer<Insight>();
      if (result) setInsight(result);
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div className="app">
      <header>
        <div>
          <h1>Veer Factor</h1>
          <span className="tagline">Stay in your lane.</span>
        </div>
        <button className={`analyze-btn ${isGenerating ? "loading" : ""}`} onClick={handleAnalyze}>
          {isGenerating ? "Analyzing..." : "Analyze Quote"}
        </button>
      </header>

      {insight && (
        <div className="card">
          <div className="score-label" style={{ color: veerColor(insight.score) }}>
            <div className="score-value">{Math.round(insight.score / 10)}/10</div>
            <div className="score-text">{veerLabel(insight.score)}</div>
          </div>
          <div className="card-body">
            <div className="body-left">
              <div className="card-person" style={{ color: veerColor(insight.score) }}>{insight.person}</div>
              <blockquote className="card-quote">"{insight.quote}"</blockquote>
            </div>
            <Road score={insight.score} expertise={insight.expertise} field={insight.field} numExperts={insight.experts?.length ?? 0} />
            <div className="body-right">
              {insight.experts && insight.experts.length > 0 && (
                <>
                  <div className="experts-list">
                    {insight.experts.map((e, i) => (
                      <div key={i} className="expert-row">
                        <span className="expert-name" style={{ color: '#16a34a' }}>{e.name}</span>
                        <span className="expert-credential">{e.credential}</span>
                      </div>
                    ))}
                  </div>
                  {insight.expertsSummary && (
                    <div className="experts-summary">{insight.expertsSummary}</div>
                  )}
                </>
              )}
              <p className="card-explanation">{insight.explanation}</p>
            </div>
          </div>
        </div>
      )}

      {!insight && (
        <div className="empty">
          <p>Paste a quote to the Data tab, then click "Analyze Quote".</p>
          <p className="hint">Format: Name: "What they said"</p>
        </div>
      )}
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);

Markdown source · More bulbs by samples · Typebulb home