All files / web/src/components/toys/euclid/proof StepCitation.tsx

0% Statements 0/131
0% Branches 0/1
0% Functions 0/1
0% Lines 0/131

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132                                                                                                                                                                                                                                                                       
'use client'

import type React from 'react'
import { CITATIONS } from '../engine/citations'
import { getFoundationHref } from '../foundations/citationUtils'
import { PROOF_COLORS, PROOF_FONTS, getProofFontSizes } from './styles'

interface StepCitationProps {
  citationKey: string
  /**
   * Progressive disclosure ordinal (1 = first time this citation appears, 2 = second, etc.).
   * Controls label and definition text display:
   *   1: full label ("Postulate 1") + definition text
   *   2: full label, no definition text
   *   3+: abbreviated key ("Post.1"), no definition text
   * Defaults to 1 if omitted.
   */
  ordinal?: number
  /** Color for the citation block (state-aware: green for done steps, blue for current) */
  color?: string
  /** Font size for the citation text block */
  fontSize?: number
  /** Font size for the label specifically */
  citationFontSize?: number
  /** Line height */
  lineHeight?: number
  /** Link wrapping */
  href?: string | null
  /** Popover interaction (pointer handlers receive citationKey) */
  onPointerEnter?: (key: string, e: React.PointerEvent) => void
  onPointerLeave?: () => void
  onPointerDown?: (key: string, e: React.PointerEvent) => void
  isMobile?: boolean
}

/**
 * Renders the italic citation text block below a proof step's instruction.
 *
 * Example output (ordinal 1):
 *   Postulate 1 — To draw a straight-line from any point to any point.
 *
 * Label is bold non-italic. If citationKey is a foundation (Post/Def/C.N.) or
 * proposition (I.*), the label is wrapped in a link. Definition text follows
 * after an em-dash separator on first appearance.
 *
 * Shared between GuidedProofPanel and playground ProofLedger.
 */
export function StepCitation({
  citationKey,
  ordinal = 1,
  color,
  fontSize,
  citationFontSize,
  lineHeight,
  href: hrefProp,
  onPointerEnter,
  onPointerLeave,
  onPointerDown,
  isMobile,
}: StepCitationProps) {
  const cit = CITATIONS[citationKey]

  // Progressive disclosure: derive label and showText from ordinal
  const displayLabel = ordinal <= 2 ? (cit?.label ?? citationKey) : citationKey
  const showText = ordinal === 1

  const displayColor = color ?? '#7893ab'

  // Determine href: explicit prop takes precedence, then auto-detect
  const foundationHref = getFoundationHref(citationKey)
  const isProposition = /^I\./.test(citationKey)
  const autoHref =
    foundationHref ?? (isProposition ? `/toys/euclid/${citationKey.replace('I.', '')}` : null)
  const href = hrefProp !== undefined ? hrefProp : autoHref
  const isLink = href != null

  const proofFont = getProofFontSizes(isMobile ?? false)
  const blockFontSize = fontSize ?? proofFont.stepText
  const labelFontSize = citationFontSize ?? proofFont.citation
  const blockLineHeight = lineHeight ?? (isMobile ? 1.25 : 1.4)

  const labelStyle: React.CSSProperties = {
    fontWeight: 600,
    fontStyle: 'normal',
    fontSize: labelFontSize,
  }

  return (
    <div
      data-element="citation-text"
      style={{
        marginTop: 4,
        fontSize: blockFontSize,
        lineHeight: blockLineHeight,
        color: displayColor,
        fontFamily: PROOF_FONTS.serif,
        fontStyle: 'italic',
      }}
    >
      {isLink ? (
        <a
          href={href!}
          target="_blank"
          rel="noopener noreferrer"
          onPointerEnter={onPointerEnter ? (e) => onPointerEnter(citationKey, e) : undefined}
          onPointerLeave={onPointerLeave}
          onPointerDown={onPointerDown ? (e) => onPointerDown(citationKey, e) : undefined}
          style={{
            ...labelStyle,
            color: 'inherit',
            textDecoration: 'underline',
            textDecorationColor: PROOF_COLORS.citationUnderline,
            cursor: 'pointer',
          }}
        >
          {displayLabel}
        </a>
      ) : (
        <span
          style={labelStyle}
          onPointerEnter={onPointerEnter ? (e) => onPointerEnter(citationKey, e) : undefined}
          onPointerLeave={onPointerLeave}
          onPointerDown={onPointerDown ? (e) => onPointerDown(citationKey, e) : undefined}
        >
          {displayLabel}
        </span>
      )}
      {showText && cit?.text && <span style={{ marginLeft: 4 }}>&mdash; {cit.text}</span>}
    </div>
  )
}