← back to devfellowship__dfl-iterate

Function bodies 89 total

All specs Real LLM only Function bodies
ActivityCard function · typescript · L30-L91 (62 LOC)
src/components/activity/ActivityCard.tsx
export function ActivityCard({ activity, index, onClick, isActive }: ActivityCardProps) {
  const Icon = typeIcons[activity.type];
  const isLocked = activity.status === ActivityStatus.LOCKED;
  const isCompleted = activity.status === ActivityStatus.COMPLETED;
  const isCurrent = activity.status === ActivityStatus.CURRENT;

  return (
    <motion.button
      onClick={onClick}
      disabled={isLocked}
      className={cn(
        "relative w-full p-4 rounded-xl border text-left transition-all duration-300",
        isLocked && "opacity-40 cursor-not-allowed grayscale",
        isCurrent && "border-primary/50 bg-card shadow-lg",
        isCompleted && "border-success/30 bg-success/5",
        !isLocked && !isCurrent && !isCompleted && "border-border bg-card/50 hover:bg-card hover:border-border/80",
        isActive && isCurrent && "ring-2 ring-primary/50"
      )}
      whileHover={!isLocked ? { scale: 1.02 } : undefined}
      whileTap={!isLocked ? { scale: 0.98 } : undefined}
    >
 
ActivityHeader function · typescript · L9-L15 (7 LOC)
src/components/activity/ActivityHeader.tsx
export function ActivityHeader({ activity }: ActivityHeaderProps) {
  return (
    <h1 className="text-2xl font-bold text-foreground">
      {activity.title}
    </h1>
  );
}
BreakAndFix function · typescript · L17-L86 (70 LOC)
src/components/activity/BreakAndFix.tsx
export function BreakAndFix({ activity, errorMessage, onFix, onError, onRequestHint }: BreakAndFixProps) {
  const [code, setCode] = useState(activity.aiGeneratedCode || '');
  const [isTesting, setIsTesting] = useState(false);

  const handleTest = async () => {
    setIsTesting(true);
    
    await new Promise(resolve => setTimeout(resolve, 1500));
    
    const hasOptionalChaining = code.includes('?.') || code.includes('|| []') || code.includes('?? []');
    
    setIsTesting(false);
    
    if (hasOptionalChaining) {
      onFix(code);
    } else {
      onError();
    }
  };

  return (
    <ActivityGameCard
      type={activity.type}
      title={activity.title}
      question="Encontre e corrija o bug no código"
      actions={
        <>
          <GameButton 
            onClick={handleTest} 
            disabled={isTesting}
            variant="primary"
            icon={<Play className="w-5 h-5" />}
          >
            {isTesting ? 'Testando...' : 'Testar'}
          
ConstrainedEdit function · typescript · L15-L73 (59 LOC)
src/components/activity/ConstrainedEdit.tsx
export function ConstrainedEdit({ activity, onSubmit }: ConstrainedEditProps) {
  const initialCode = useMemo(() => {
    const file = initialProjectFiles.find(f => f.path === activity.targetFiles[0]);
    return file?.content || '';
  }, [activity.targetFiles]);

  const [code, setCode] = useState(initialCode);

  return (
    <ActivityGameCard
      type={activity.type}
      title={activity.title}
      question="Melhore o código nas regiões destacadas"
      actions={
        <GameButton onClick={() => onSubmit(code)} variant="primary" icon={<Check className="w-5 h-5" />}>
          Aplicar
        </GameButton>
      }
    >
      <div className="flex-1 flex flex-col gap-4 overflow-hidden">
        {/* Editable Regions Hints */}
        {activity.editableRegions && (
          <motion.div 
            className="flex flex-wrap gap-2 shrink-0"
            initial={{ opacity: 0, y: 10 }}
            animate={{ opacity: 1, y: 0 }}
          >
            {activity.editableRegions.map
DecisionFork function · typescript · L14-L58 (45 LOC)
src/components/activity/DecisionFork.tsx
export function DecisionFork({ activity, onDecide }: DecisionForkProps) {
  const [selectedOption, setSelectedOption] = useState<string | null>(null);
  const [isConfirming, setIsConfirming] = useState(false);

  const handleConfirm = () => {
    if (selectedOption) {
      setIsConfirming(true);
      setTimeout(() => {
        onDecide(selectedOption);
      }, 500);
    }
  };

  return (
    <ActivityGameCard
      type={activity.type}
      title={activity.title}
      question="Qual abordagem você prefere?"
      actions={
        <GameButton 
          onClick={handleConfirm} 
          disabled={!selectedOption || isConfirming}
          variant="primary"
          icon={isConfirming ? <Sparkles className="w-5 h-5 animate-pulse" /> : undefined}
        >
          {isConfirming ? 'Aplicando...' : 'Confirmar'}
        </GameButton>
      }
    >
      {/* Options Grid */}
      <div className="grid gap-4 md:grid-cols-3 h-full content-center">
        {activity.options?.map((opti
OptionCard function · typescript · L68-L113 (46 LOC)
src/components/activity/DecisionFork.tsx
function OptionCard({ option, index, isSelected, onSelect, disabled }: OptionCardProps) {
  return (
    <motion.button
      onClick={onSelect}
      disabled={disabled}
      className={cn(
        "relative p-5 rounded-2xl border-2 text-left transition-all duration-200",
        "bg-card hover:bg-card/80",
        isSelected 
          ? "border-primary ring-4 ring-primary/20 shadow-lg" 
          : "border-border hover:border-primary/40"
      )}
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ delay: 0.1 * index }}
      whileHover={!disabled ? { y: -4, scale: 1.02 } : undefined}
      whileTap={!disabled ? { scale: 0.98 } : undefined}
    >
      {/* Selection indicator */}
      {isSelected && (
        <motion.div 
          className="absolute top-3 right-3 w-7 h-7 rounded-full bg-primary flex items-center justify-center"
          initial={{ scale: 0 }}
          animate={{ scale: 1 }}
        >
          <Check className="w-4 h-4
ImageZoomModal function · typescript · L11-L64 (54 LOC)
src/components/activity/ImageZoomModal.tsx
export function ImageZoomModal({ isOpen, imageUrl, caption, onClose }: ImageZoomModalProps) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          className="fixed inset-0 z-50 flex items-center justify-center p-6"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          onClick={onClose}
        >
          {/* Backdrop */}
          <motion.div 
            className="absolute inset-0 bg-black/90 backdrop-blur-md"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          />

          {/* Modal Content */}
          <motion.div
            className="relative max-w-[90vw] max-h-[90vh]"
            initial={{ scale: 0.9, opacity: 0 }}
            animate={{ scale: 1, opacity: 1 }}
            exit={{ scale: 0.9, opacity: 0 }}
            onClick={(e) => e.stopPropagation()}
          >
            {/* Close Button */}
            <button
     
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
QualityReview function · typescript · L16-L106 (91 LOC)
src/components/activity/QualityReview.tsx
export function QualityReview({ activity, onApprove, onRegenerate, onEdit }: QualityReviewProps) {
  const [isEditing, setIsEditing] = useState(false);
  const [code, setCode] = useState(activity.aiGeneratedCode || '');
  const [showWarning, setShowWarning] = useState(false);

  const handleApprove = () => {
    if (activity.expectedIssues && activity.expectedIssues.length > 0) {
      setShowWarning(true);
    }
    onApprove();
  };

  const handleEdit = () => {
    setIsEditing(true);
  };

  const handleSaveEdit = () => {
    onEdit(code);
    setIsEditing(false);
  };

  return (
    <ActivityGameCard
      type={activity.type}
      title={activity.title}
      question={isEditing ? "Salve suas edições quando terminar" : "O código está pronto para produção?"}
      actions={
        isEditing ? (
          <>
            <GameButton onClick={handleSaveEdit} variant="primary" icon={<Check className="w-5 h-5" />}>
              Salvar
            </GameButton>
            <GameButt
VideoChallenge function · typescript · L16-L137 (122 LOC)
src/components/activity/VideoChallenge.tsx
export function VideoChallenge({ activity, onComplete }: VideoChallengeProps) {
  const [watched, setWatched] = useState(false);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [code, setCode] = useState(activity.aiGeneratedCode || '');

  const videoConfig = activity.videoConfig;
  
  if (!videoConfig) {
    return <div>Video configuration missing</div>;
  }

  const handleVideoClose = () => {
    setIsModalOpen(false);
    setWatched(true);
  };

  const thumbnailUrl = videoConfig.thumbnailUrl || 
    `https://img.youtube.com/vi/${videoConfig.youtubeId}/maxresdefault.jpg`;

  return (
    <ActivityGameCard
      type={ActivityType.VIDEO_CHALLENGE}
      title={activity.title}
      question="Aplique o que você aprendeu no vídeo"
      actions={
        <GameButton
          variant="primary"
          onClick={() => onComplete(code)}
          disabled={!watched}
        >
          ✓ APLICAR
        </GameButton>
      }
    >
      <div className="flex flex-col gap
VideoModal function · typescript · L11-L66 (56 LOC)
src/components/activity/VideoModal.tsx
export function VideoModal({ isOpen, videoId, title, onClose }: VideoModalProps) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          className="fixed inset-0 z-50 flex items-center justify-center p-6"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          onClick={onClose}
        >
          {/* Backdrop */}
          <motion.div 
            className="absolute inset-0 bg-black/90 backdrop-blur-md"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          />

          {/* Modal Content */}
          <motion.div
            className="relative w-full max-w-4xl bg-card rounded-2xl overflow-hidden"
            initial={{ scale: 0.9, opacity: 0 }}
            animate={{ scale: 1, opacity: 1 }}
            exit={{ scale: 0.9, opacity: 0 }}
            onClick={(e) => e.stopPropagation()}
          >
            {/* Close Button */}
         
VisualImplementation function · typescript · L16-L91 (76 LOC)
src/components/activity/VisualImplementation.tsx
export function VisualImplementation({ activity, onComplete }: VisualImplementationProps) {
  const [isZoomed, setIsZoomed] = useState(false);
  const [code, setCode] = useState(activity.aiGeneratedCode || '');

  const visualConfig = activity.visualConfig;
  
  if (!visualConfig) {
    return <div>Visual configuration missing</div>;
  }

  return (
    <ActivityGameCard
      type={ActivityType.VISUAL_IMPLEMENTATION}
      title={activity.title}
      question="Aproxime seu código do design de referência"
      actions={
        <GameButton
          variant="primary"
          onClick={() => onComplete(code)}
        >
          👁️ COMPARAR
        </GameButton>
      }
    >
      <div className="flex flex-col gap-4 flex-1 overflow-hidden">
        {/* Reference Image */}
        <div className="shrink-0">
          <p className="text-sm text-muted-foreground mb-2 font-semibold">Referência</p>
          <motion.div
            className="relative rounded-xl overflow-hidden cursor-zo
AIResponse function · typescript · L9-L84 (76 LOC)
src/components/ai/AIResponse.tsx
export function AIResponse({ text, isStreaming }: AIResponseProps) {
  if (!text && !isStreaming) return null;

  // Parse markdown-like syntax for code blocks
  const renderContent = (content: string) => {
    const parts = content.split(/(```[\s\S]*?```)/g);
    
    return parts.map((part, i) => {
      if (part.startsWith('```') && part.endsWith('```')) {
        const lines = part.slice(3, -3).split('\n');
        const language = lines[0].trim();
        const code = lines.slice(1).join('\n');
        
        return (
          <div key={i} className="my-3 rounded-lg overflow-hidden bg-[#1e1e1e] border border-border">
            {language && (
              <div className="px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground">
                {language}
              </div>
            )}
            <pre className="p-4 overflow-x-auto">
              <code className="text-sm font-mono text-foreground">{code}</code>
            </pre>
          </div>
  
ChooseCard function · typescript · L14-L50 (37 LOC)
src/components/atoms/ChooseCard/ChooseCard.tsx
  export function ChooseCard({ choice, index, isSelected, onSelect, disabled }: ChooseCardProps) {
    return (
      <motion.button
        onClick={onSelect}
        disabled={disabled}
        className={cn(
          "relative p-4 rounded-2xl border-2 flex flex-col items-center justify-start transition-all duration-200",
          "bg-card hover:bg-card/80",
          isSelected 
            ? "border-primary ring-4 ring-primary/20 shadow-lg" 
            : "border-border hover:border-primary/40"
        )}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ delay: 0.1 * index }}
      >
        {/* Selection indicator */}
        {isSelected && (
          <motion.div 
            className="absolute top-3 right-3 w-7 h-7 rounded-full bg-primary flex items-center justify-center"
            initial={{ scale: 0 }}
            animate={{ scale: 1 }}
          >
            <Check className="w-4 h-4 text-primary-foreground" />
        
CodeEditor function · typescript · L12-L102 (91 LOC)
src/components/editor/CodeEditor.tsx
export function CodeEditor({
  value,
  onChange,
  language = 'typescript',
  readOnly = false,
  highlightLines = [],
  fontSize = 14,
}: CodeEditorProps) {
  const handleEditorChange = (newValue: string | undefined) => {
    if (onChange && newValue !== undefined) {
      onChange(newValue);
    }
  };

  return (
    <div className="editor-container h-full" style={{ minHeight: '300px' }}>
      <Editor
        height="100%"
        language={language === 'typescript' ? 'typescript' : language}
        value={value}
        onChange={handleEditorChange}
        theme="vs-dark"
        options={{
          readOnly,
          minimap: { enabled: false },
          fontSize,
          lineHeight: fontSize * 1.6,
          fontFamily: "'JetBrains Mono', monospace",
          fontLigatures: true,
          lineNumbers: 'on',
          scrollBeyondLastLine: false,
          wordWrap: 'on',
          padding: { top: 16, bottom: 16 },
          renderLineHighlight: 'all',
          cursorB
buildTree function · typescript · L20-L60 (41 LOC)
src/components/editor/FileTree.tsx
function buildTree(files: ProjectFile[]): TreeNode[] {
  const root: TreeNode[] = [];
  const pathMap = new Map<string, TreeNode>();

  // Sort files by path
  const sortedFiles = [...files].sort((a, b) => a.path.localeCompare(b.path));

  sortedFiles.forEach(file => {
    const parts = file.path.split('/');
    let currentPath = '';

    parts.forEach((part, index) => {
      const isLast = index === parts.length - 1;
      const parentPath = currentPath;
      currentPath = currentPath ? `${currentPath}/${part}` : part;

      if (!pathMap.has(currentPath)) {
        const node: TreeNode = {
          name: part,
          path: currentPath,
          isDirectory: !isLast,
          children: [],
          file: isLast ? file : undefined,
        };

        pathMap.set(currentPath, node);

        if (parentPath) {
          const parent = pathMap.get(parentPath);
          if (parent) {
            parent.children.push(node);
          }
        } else {
          root.push(node);
Want this analysis on your repo? https://repobility.com/scan/
FileTree function · typescript · L62-L78 (17 LOC)
src/components/editor/FileTree.tsx
export function FileTree({ files, onFileSelect, selectedFile }: FileTreeProps) {
  const tree = buildTree(files);

  return (
    <div className="text-sm">
      {tree.map(node => (
        <TreeNodeComponent
          key={node.path}
          node={node}
          onFileSelect={onFileSelect}
          selectedFile={selectedFile}
          depth={0}
        />
      ))}
    </div>
  );
}
TreeNodeComponent function · typescript · L87-L146 (60 LOC)
src/components/editor/FileTree.tsx
function TreeNodeComponent({ node, onFileSelect, selectedFile, depth }: TreeNodeComponentProps) {
  const [isOpen, setIsOpen] = useState(true);
  const isSelected = node.file?.path === selectedFile;

  if (node.isDirectory) {
    return (
      <div>
        <button
          onClick={() => setIsOpen(!isOpen)}
          className={cn(
            "flex items-center gap-1.5 w-full px-2 py-1 hover:bg-muted/50 rounded text-left",
            "text-muted-foreground hover:text-foreground transition-colors"
          )}
          style={{ paddingLeft: `${depth * 12 + 8}px` }}
        >
          {isOpen ? (
            <ChevronDown className="w-3.5 h-3.5 shrink-0" />
          ) : (
            <ChevronRight className="w-3.5 h-3.5 shrink-0" />
          )}
          {isOpen ? (
            <FolderOpen className="w-4 h-4 text-primary shrink-0" />
          ) : (
            <Folder className="w-4 h-4 text-primary shrink-0" />
          )}
          <span>{node.name}</span>
        </button>
 
ActivityGameCard function · typescript · L52-L98 (47 LOC)
src/components/game/ActivityGameCard.tsx
export function ActivityGameCard({ type, title, question, children, actions }: ActivityGameCardProps) {
  const config = typeConfig[type];
  const Icon = config.icon;

  return (
    <motion.div
      className="flex flex-col h-full"
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.3 }}
    >
      {/* Header with type and title */}
      <div className="text-center mb-4 shrink-0">
        <motion.div 
          className={`inline-flex items-center gap-2 ${config.color} mb-2`}
          initial={{ scale: 0.9 }}
          animate={{ scale: 1 }}
        >
          <Icon className="w-5 h-5" />
          <span className="font-display text-sm font-bold tracking-widest">
            {config.label}
          </span>
        </motion.div>
        <h1 className="font-display text-2xl font-black text-foreground">
          {title}
        </h1>
      </div>

      {/* Main content area - explicit flex-1 with overflow */}
      <div class
AIHistoryButton function · typescript · L9-L21 (13 LOC)
src/components/game/AIHistoryButton.tsx
export function AIHistoryButton({ messageCount, onClick }: AIHistoryButtonProps) {
  return (
    <motion.button
      onClick={onClick}
      className="flex items-center gap-2 px-3 py-2 rounded-full bg-card border border-border hover:bg-muted transition-colors shadow-lg"
      whileHover={{ scale: 1.05 }}
      whileTap={{ scale: 0.95 }}
    >
      <Bot className="w-4 h-4 text-muted-foreground" />
      <span className="text-sm font-bold text-foreground">{messageCount}</span>
    </motion.button>
  );
}
AIHistoryDrawer function · typescript · L14-L164 (151 LOC)
src/components/game/AIHistoryDrawer.tsx
export function AIHistoryDrawer({ isOpen, onClose, messages }: AIHistoryDrawerProps) {
  const [expandedId, setExpandedId] = useState<string | null>(null);

  const toggleExpand = (id: string) => {
    setExpandedId(prev => prev === id ? null : id);
  };

  // Parse markdown-like syntax
  const renderMessage = (content: string) => {
    const lines = content.split('\n');
    
    return lines.map((line, i) => {
      const parts = line.split(/(\*\*.*?\*\*)/g);
      const rendered = parts.map((part, j) => {
        if (part.startsWith('**') && part.endsWith('**')) {
          return <strong key={j} className="font-semibold text-primary">{part.slice(2, -2)}</strong>;
        }
        const codeParts = part.split(/(`[^`]+`)/g);
        return codeParts.map((cp, k) => {
          if (cp.startsWith('`') && cp.endsWith('`')) {
            return (
              <code key={k} className="px-1 py-0.5 rounded bg-muted text-primary text-xs font-mono">
                {cp.slice(1, -1)}
         
CelebrationOverlay function · typescript · L13-L114 (102 LOC)
src/components/game/CelebrationOverlay.tsx
export function CelebrationOverlay({ isVisible, xpEarned, message, onContinue }: CelebrationOverlayProps) {
  // Play celebration sound effect (optional)
  useEffect(() => {
    if (isVisible) {
      // Could add sound here
    }
  }, [isVisible]);

  return (
    <AnimatePresence>
      {isVisible && (
        <motion.div
          className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
        >
          {/* Confetti particles */}
          <div className="absolute inset-0 overflow-hidden pointer-events-none">
            {[...Array(20)].map((_, i) => (
              <motion.div
                key={i}
                className="absolute w-3 h-3 rounded-full"
                style={{
                  left: `${Math.random() * 100}%`,
                  background: ['#f39325', '#22c55e', '#fbbf24', '#ef4444', '#3b82f6'][i % 5],
              
GameButton function · typescript · L22-L53 (32 LOC)
src/components/game/GameButton.tsx
export function GameButton({ 
  className, 
  variant = 'primary', 
  icon, 
  children, 
  disabled,
  onClick,
  type = 'button'
}: GameButtonProps) {
  return (
    <motion.button
      type={type}
      onClick={onClick}
      className={cn(
        "inline-flex items-center justify-center gap-2",
        "px-6 py-3 min-w-[140px]",
        "font-display font-bold text-base uppercase tracking-wide",
        "rounded-2xl",
        "transition-all duration-100",
        "disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none",
        variantStyles[variant],
        className
      )}
      whileHover={!disabled ? { y: -2 } : undefined}
      whileTap={!disabled ? { y: 2 } : undefined}
      disabled={disabled}
    >
      {icon}
      {children}
    </motion.button>
  );
}
GameHeader function · typescript · L11-L64 (54 LOC)
src/components/game/GameHeader.tsx
export function GameHeader({ lives, streak, xp }: GameHeaderProps) {
  return (
    <header className="shrink-0 h-14 px-4 flex items-center justify-between border-b border-border bg-card/50">
      {/* Logo */}
      <div className="flex items-center gap-2">
        <div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center">
          <span className="text-lg font-black text-primary-foreground">i</span>
        </div>
        <span className="font-display font-bold text-foreground hidden sm:block">iterate</span>
      </div>

      {/* Stats */}
      <div className="flex items-center gap-4">
        {/* Lives */}
        <motion.div 
          className="flex items-center gap-1"
          initial={{ scale: 0.9 }}
          animate={{ scale: 1 }}
        >
          {[...Array(3)].map((_, i) => (
            <motion.div
              key={i}
              initial={{ scale: 0 }}
              animate={{ scale: 1 }}
              transition={{ delay: i * 0.1 }}
     
Open data scored by Repobility · https://repobility.com
LessonCompleteScreen function · typescript · L21-L219 (199 LOC)
src/components/game/LessonCompleteScreen.tsx
export function LessonCompleteScreen({
  lessonTitle,
  stats,
  decisions,
  githubRepo,
  onGoHome,
  onShare,
}: LessonCompleteScreenProps) {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    await navigator.clipboard.writeText(`https://${githubRepo}`);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  const handleOpenRepo = () => {
    window.open(`https://${githubRepo}`, '_blank');
  };

  return (
    <div className="min-h-screen bg-background flex items-center justify-center p-6">
      <motion.div
        className="max-w-2xl w-full"
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
      >
        {/* Celebration Header */}
        <motion.div 
          className="text-center mb-8"
          initial={{ scale: 0 }}
          animate={{ scale: 1 }}
          transition={{ delay: 0.2, type: 'spring', damping: 10 }}
        >
          <span className="text-7xl">🎉</span>
        </motion.di
ProgressPills function · typescript · L13-L68 (56 LOC)
src/components/game/ProgressPills.tsx
export function ProgressPills({ activities, currentIndex, onActivityClick }: ProgressPillsProps) {
  return (
    <div className="flex items-center justify-center gap-2 py-4">
      {activities.map((activity, index) => {
        const isCompleted = activity.status === ActivityStatus.COMPLETED;
        const isCurrent = index === currentIndex;
        const isLocked = activity.status === ActivityStatus.LOCKED;

        return (
          <div key={activity.id} className="flex items-center">
            {/* Connector line */}
            {index > 0 && (
              <div 
                className={cn(
                  "w-8 h-1 rounded-full -mr-1",
                  index <= currentIndex ? "bg-primary" : "bg-muted"
                )}
              />
            )}
            
            <motion.button
              onClick={() => !isLocked && onActivityClick(index)}
              disabled={isLocked}
              className={cn(
                "relative w-12 h-12 rounded-full flex i
ResultModal function · typescript · L14-L162 (149 LOC)
src/components/game/ResultModal.tsx
export function ResultModal({
  isOpen,
  isSuccess,
  xpEarned = 25,
  activityTitle,
  aiFeedback,
  onContinue,
  isLessonComplete = false,
}: ResultModalProps) {
  // Parse markdown-like syntax
  const renderFeedback = (content: string) => {
    const lines = content.split('\n');
    
    return lines.map((line, i) => {
      // Handle bold text
      const parts = line.split(/(\*\*.*?\*\*)/g);
      const rendered = parts.map((part, j) => {
        if (part.startsWith('**') && part.endsWith('**')) {
          return <strong key={j} className="font-semibold text-primary">{part.slice(2, -2)}</strong>;
        }
        // Handle inline code
        const codeParts = part.split(/(`[^`]+`)/g);
        return codeParts.map((cp, k) => {
          if (cp.startsWith('`') && cp.endsWith('`')) {
            return (
              <code key={k} className="px-1.5 py-0.5 rounded bg-muted text-primary text-xs font-mono">
                {cp.slice(1, -1)}
              </code>
            );
   
AppShell function · typescript · L17-L69 (53 LOC)
src/components/layout/AppShell.tsx
export function AppShell({
  children,
  projectName,
  projectStatus,
  currentActivity,
  totalActivities,
  trackTitle,
  onBack,
  activityButtons,
}: AppShellProps) {
  const progress = (currentActivity / totalActivities) * 100;

  return (
    <div className="h-screen bg-background flex flex-col overflow-hidden">
      <Header
        projectName={projectName}
        projectStatus={projectStatus}
        trackTitle={trackTitle}
        onBack={onBack}
      />
      
      <motion.main 
        className="flex-1 flex flex-col overflow-hidden"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 0.3 }}
      >
        {/* Gamified Progress Bar */}
        <div className="shrink-0 px-4 py-3 bg-card/30 border-b border-border">
          <div className="flex items-center gap-3">
            {/* Activity Buttons */}
            {activityButtons}
            
            {/* Progress Bar */}
            <div className="flex-1 h-5 bg-muted r
Header function · typescript · L33-L80 (48 LOC)
src/components/layout/Header.tsx
export function Header({
  projectName,
  projectStatus,
  trackTitle,
  onBack,
}: HeaderProps) {
  const status = statusConfig[projectStatus];

  return (
    <header className="shrink-0 z-50 border-b border-border bg-background">
      <div className="flex items-center justify-between h-12 px-4">
        {/* Left section - Logo */}
        <div className="flex items-center gap-3">
          {onBack && (
            <Button 
              variant="ghost" 
              size="icon" 
              onClick={onBack}
              className="h-8 w-8"
            >
              <ArrowLeft className="h-4 w-4" />
            </Button>
          )}
          
          <div className="flex items-center gap-2">
            <span className="text-lg font-bold text-gradient">iterate</span>
            <span className="text-xs text-muted-foreground">by DevFellowship</span>
          </div>
        </div>

        {/* Center - Track Title */}
        {trackTitle && (
          <div className="abso
ReadAndChoose function · typescript · L18-L67 (50 LOC)
src/components/molecules/ReadAndChoose/ReadAndChoose.tsx
export function ReadAndChoose({ activity, onDecide }: ReadAndChooseProps) {
  const code = activity.aiGeneratedCode || '';
  const { selectedOption, setSelectedOption, isConfirming, handleConfirm} =
  useReadAndChoose({ onDecide});

  return (
    <ActivityGameCard
      type={activity.type}
      title={activity.title}
      question={activity.objective || ''}
      actions={
        <GameButton 
          onClick={handleConfirm} 
          disabled={!selectedOption || isConfirming}
          variant="primary"
          icon={isConfirming ? <Sparkles className="w-5 h-5 animate-pulse" /> : undefined}
        >
          {isConfirming ? 'Aplicando...' : 'Confirmar'}
        </GameButton>
      }
    >
      <div className="flex-1 flex flex-col gap-4 pb-4 overflow-hidden">
        {/* Code Snippet (Read-only) */}
        <div className="shrink-0 rounded-xl overflow-hidden border border-border">
          <CodeEditor
            value={code}
            language="typescript"
            r
ErrorBoundary class · typescript · L18-L82 (65 LOC)
src/components/observability/ErrorBoundary.tsx
export class ErrorBoundary extends Component<
  ErrorBoundaryProps,
  ErrorBoundaryState
> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    const tracer = trace.getTracer("@dfl/observability");

    tracer.startActiveSpan("react-error-boundary", (span) => {
      span.setAttribute("error.message", error.message);
      span.setAttribute("error.stack", error.stack ?? "");
      span.setAttribute(
        "error.component_stack",
        errorInfo.componentStack ?? "",
      );

      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: error.message,
      });

      span.recordException(error);
      span.end();
    });
  }

  private handleReload = (): void => {
    window.location.reload();
  };

  render(): ReactNode {
constructor method · typescript · L22-L25 (4 LOC)
src/components/observability/ErrorBoundary.tsx
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false, error: null };
  }
Repobility · code-quality intelligence platform · https://repobility.com
getDerivedStateFromError method · typescript · L27-L29 (3 LOC)
src/components/observability/ErrorBoundary.tsx
  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }
componentDidCatch method · typescript · L31-L50 (20 LOC)
src/components/observability/ErrorBoundary.tsx
  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    const tracer = trace.getTracer("@dfl/observability");

    tracer.startActiveSpan("react-error-boundary", (span) => {
      span.setAttribute("error.message", error.message);
      span.setAttribute("error.stack", error.stack ?? "");
      span.setAttribute(
        "error.component_stack",
        errorInfo.componentStack ?? "",
      );

      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: error.message,
      });

      span.recordException(error);
      span.end();
    });
  }
render method · typescript · L56-L81 (26 LOC)
src/components/observability/ErrorBoundary.tsx
  render(): ReactNode {
    if (this.state.hasError) {
      if (this.props.fallback) {
        return this.props.fallback;
      }

      return (
        <div className="flex min-h-[200px] flex-col items-center justify-center gap-4 rounded-lg border border-red-200 bg-red-50 p-8 text-center dark:border-red-900 dark:bg-red-950">
          <h2 className="text-lg font-semibold text-red-800 dark:text-red-200">
            Something went wrong
          </h2>
          <p className="max-w-md text-sm text-red-600 dark:text-red-400">
            An unexpected error occurred. Please try reloading the page.
          </p>
          <button
            onClick={this.handleReload}
            className="rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:bg-red-700 dark:hover:bg-red-600"
          >
            Reload page
          </button>
        </div>
      );
    }

  
useQueryErrorReporter function · typescript · L12-L68 (57 LOC)
src/components/observability/hooks/useQueryErrorReporter.ts
export function useQueryErrorReporter(): void {
  let queryClient: QueryClient | null = null;
  try {
    // useQueryClient internally calls useContext (preserving hook order)
    // then throws if no provider — the try-catch is safe here.
    // eslint-disable-next-line react-hooks/rules-of-hooks
    queryClient = useQueryClient();
  } catch {
    // Not inside a QueryClientProvider — query error reporting disabled
  }

  useEffect(() => {
    if (!queryClient) return;

    const tracer = trace.getTracer("@dfl/observability");

    const unsubscribe = queryClient.getQueryCache().subscribe((event) => {
      if (event.type === "updated" && event.action.type === "error") {
        const query = event.query;
        const error = query.state.error;

        tracer.startActiveSpan("react-query-error", (span) => {
          const dynamicAttrs = getDynamicAttributes();
          for (const [key, value] of Object.entries(dynamicAttrs)) {
            span.setAttribute(key, value);
          }
useWebVitals function · typescript · L12-L56 (45 LOC)
src/components/observability/hooks/useWebVitals.ts
export function useWebVitals(): void {
  const initialized = useRef(false);

  useEffect(() => {
    if (initialized.current) return;
    initialized.current = true;

    const meter = metrics.getMeter("web-vitals");

    const lcpHistogram = meter.createHistogram("web_vital.lcp", {
      description: "Largest Contentful Paint (ms)",
      unit: "ms",
    });
    const clsHistogram = meter.createHistogram("web_vital.cls", {
      description: "Cumulative Layout Shift",
      unit: "",
    });
    const inpHistogram = meter.createHistogram("web_vital.inp", {
      description: "Interaction to Next Paint (ms)",
      unit: "ms",
    });
    const ttfbHistogram = meter.createHistogram("web_vital.ttfb", {
      description: "Time to First Byte (ms)",
      unit: "ms",
    });

    const report = (
      histogram: ReturnType<typeof meter.createHistogram>,
      metric: Metric,
    ) => {
      histogram.record(metric.value, {
        "metric.id": metric.id,
        "metric.rating": metric.
instrumentFetch function · typescript · L14-L82 (69 LOC)
src/components/observability/lib/fetch-instrumentation.ts
export function instrumentFetch(tracer: Tracer): void {
  if (_originalFetch) {
    // Already instrumented — avoid double-wrapping.
    return;
  }

  _originalFetch = window.fetch.bind(window);

  window.fetch = async (
    input: RequestInfo | URL,
    init?: RequestInit,
  ): Promise<Response> => {
    const url =
      input instanceof Request ? input.url : input instanceof URL ? input.href : input;
    const method = init?.method ?? (input instanceof Request ? input.method : "GET");

    // Filter out OTel collector traffic to avoid infinite loops.
    const collectorUrl = (globalThis as Record<string, unknown>).__otelCollectorUrl;
    if (typeof collectorUrl === "string" && url.startsWith(collectorUrl)) {
      return _originalFetch!(input, init);
    }

    const dynamicAttrs = getDynamicAttributes();

    return tracer.startActiveSpan(
      `HTTP ${method.toUpperCase()}`,
      {
        kind: SpanKind.CLIENT,
        attributes: {
          "http.method": method.toUpperCase(
uninstrumentFetch function · typescript · L87-L92 (6 LOC)
src/components/observability/lib/fetch-instrumentation.ts
export function uninstrumentFetch(): void {
  if (_originalFetch) {
    window.fetch = _originalFetch;
    _originalFetch = null;
  }
}
initOtelBrowser function · typescript · L27-L78 (52 LOC)
src/components/observability/lib/otel-init.ts
export function initOtelBrowser(config: OtelBrowserConfig): OtelProviders {
  const { serviceName, serviceVersion, collectorUrl, apiKey } = config;

  const headers: Record<string, string> = {};
  if (apiKey) {
    headers["x-api-key"] = apiKey;
  }

  const staticAttributes = getStaticAttributes();

  const resource = resourceFromAttributes({
    "service.name": serviceName,
    "service.version": serviceVersion,
    ...staticAttributes,
  });

  // --- Traces ---
  const traceExporter = new OTLPTraceExporter({
    url: `${collectorUrl}/v1/traces`,
    headers,
  });

  const tracerProvider = new WebTracerProvider({
    resource,
    spanProcessors: [new BatchSpanProcessor(traceExporter)],
  });

  tracerProvider.register();

  // --- Metrics ---
  const metricExporter = new OTLPMetricExporter({
    url: `${collectorUrl}/v1/metrics`,
    headers,
  });

  const metricReader = new PeriodicExportingMetricReader({
    exporter: metricExporter,
    exportIntervalMillis: 60_000,
  });

  c
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
shutdownOtel function · typescript · L80-L93 (14 LOC)
src/components/observability/lib/otel-init.ts
export function shutdownOtel(providers: OtelProviders): void {
  const { tracerProvider, meterProvider } = providers;

  trace.disable();
  metrics.disable();

  tracerProvider.shutdown().catch((err) => {
    console.warn("[observability] tracer shutdown error:", err);
  });

  meterProvider.shutdown().catch((err) => {
    console.warn("[observability] meter shutdown error:", err);
  });
}
setUserId function · typescript · L13-L15 (3 LOC)
src/components/observability/lib/resource-attributes.ts
export function setUserId(id: string): void {
  _userId = id;
}
setUserRole function · typescript · L17-L19 (3 LOC)
src/components/observability/lib/resource-attributes.ts
export function setUserRole(role: string): void {
  _userRole = role;
}
getSessionId function · typescript · L22-L30 (9 LOC)
src/components/observability/lib/resource-attributes.ts
function getSessionId(): string {
  const key = "__otel_session_id";
  let id = sessionStorage.getItem(key);
  if (!id) {
    id = crypto.randomUUID();
    sessionStorage.setItem(key, id);
  }
  return id;
}
detectBrowser function · typescript · L38-L59 (22 LOC)
src/components/observability/lib/resource-attributes.ts
function detectBrowser(): BrowserInfo {
  const ua = navigator.userAgent;

  if (ua.includes("Firefox/")) {
    const match = ua.match(/Firefox\/([\d.]+)/);
    return { name: "Firefox", version: match?.[1] ?? "unknown" };
  }
  if (ua.includes("Edg/")) {
    const match = ua.match(/Edg\/([\d.]+)/);
    return { name: "Edge", version: match?.[1] ?? "unknown" };
  }
  if (ua.includes("Chrome/") && !ua.includes("Edg/")) {
    const match = ua.match(/Chrome\/([\d.]+)/);
    return { name: "Chrome", version: match?.[1] ?? "unknown" };
  }
  if (ua.includes("Safari/") && !ua.includes("Chrome/")) {
    const match = ua.match(/Version\/([\d.]+)/);
    return { name: "Safari", version: match?.[1] ?? "unknown" };
  }

  return { name: "unknown", version: "unknown" };
}
detectOS function · typescript · L62-L70 (9 LOC)
src/components/observability/lib/resource-attributes.ts
function detectOS(): string {
  const ua = navigator.userAgent;
  if (ua.includes("Win")) return "Windows";
  if (ua.includes("Mac")) return "macOS";
  if (ua.includes("Linux")) return "Linux";
  if (ua.includes("Android")) return "Android";
  if (/iPhone|iPad|iPod/.test(ua)) return "iOS";
  return "unknown";
}
detectDeviceType function · typescript · L73-L78 (6 LOC)
src/components/observability/lib/resource-attributes.ts
function detectDeviceType(): string {
  const width = window.innerWidth;
  if (width < 768) return "mobile";
  if (width < 1024) return "tablet";
  return "desktop";
}
getEffectiveConnectionType function · typescript · L81-L86 (6 LOC)
src/components/observability/lib/resource-attributes.ts
function getEffectiveConnectionType(): string | undefined {
  const nav = navigator as Navigator & {
    connection?: { effectiveType?: string };
  };
  return nav.connection?.effectiveType;
}
Want this analysis on your repo? https://repobility.com/scan/
getStaticAttributes function · typescript · L94-L115 (22 LOC)
src/components/observability/lib/resource-attributes.ts
export function getStaticAttributes(): Record<string, string> {
  const browser = detectBrowser();
  const attrs: Record<string, string> = {
    "browser.name": browser.name,
    "browser.version": browser.version,
    "os.type": detectOS(),
    "device.type": detectDeviceType(),
    "session.id": getSessionId(),
  };

  // Vite env-based app metadata
  const appName = (import.meta as ImportMeta & { env: Record<string, string> })
    .env?.VITE_APP_NAME;
  const appVersion = (
    import.meta as ImportMeta & { env: Record<string, string> }
  ).env?.VITE_APP_VERSION;

  if (appName) attrs["app.name"] = appName;
  if (appVersion) attrs["app.version"] = appVersion;

  return attrs;
}
getDynamicAttributes function · typescript · L121-L138 (18 LOC)
src/components/observability/lib/resource-attributes.ts
export function getDynamicAttributes(): Record<string, string | number> {
  const attrs: Record<string, string | number> = {
    "viewport.width": window.innerWidth,
    "viewport.height": window.innerHeight,
  };

  if (_userId) attrs["user.id"] = _userId;
  if (_userRole) attrs["user.role"] = _userRole;

  const connType = getEffectiveConnectionType();
  if (connType) attrs["connection.effectiveType"] = connType;

  // page.route is typically set by the ObservabilityProvider via React Router
  const route = (globalThis as Record<string, unknown>).__otelCurrentRoute;
  if (typeof route === "string") attrs["page.route"] = route;

  return attrs;
}
trackEvent function · typescript · L15-L41 (27 LOC)
src/components/observability/lib/track-event.ts
export function trackEvent(
  name: string,
  attributes?: Record<string, string | number | boolean>,
): void {
  const tracer = trace.getTracer("@dfl/observability");

  tracer.startActiveSpan(name, (span) => {
    const dynamicAttrs = getDynamicAttributes();

    span.setAttribute("event.name", name);

    // Attach dynamic resource attributes (viewport, user, route, etc.)
    for (const [key, value] of Object.entries(dynamicAttrs)) {
      span.setAttribute(key, value);
    }

    // Attach caller-provided attributes
    if (attributes) {
      for (const [key, value] of Object.entries(attributes)) {
        span.setAttribute(key, value);
      }
    }

    span.setStatus({ code: SpanStatusCode.OK });
    span.end();
  });
}
page 1 / 2next ›