~bigbes/lethe

ref: 0555d5d52feb15256352adfa3f2a4d62835c7dac lethe/web/src/features/session/Transcript.tsx -rw-r--r-- 2.4 KiB
0555d5d5 — Eugene Blikh chore: add BSD-2-Clause license 6 days ago
                                                                                
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
import React from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import type { Turn } from './useSession'

interface TranscriptProps {
  turns: Turn[]
  selected: number
  onSelect: (seq: number) => void
}

export function Transcript({
  turns,
  selected,
  onSelect,
}: TranscriptProps): React.JSX.Element {
  return (
    <main className="transcript">
      {turns.map((t) => {
        const isSel = t.i === selected
        return (
          <div
            key={t.i}
            id={`turn-${t.i}`}
            className={`turn ${t.role}${isSel ? ' selected' : ''}`}
            {...(t.role === 'tool' || t.toolKind != null ? { 'data-tool': '1' } : {})}
            onClick={() => onSelect(t.i)}
          >
            <div className="turn-meta">
              <span className={`role-${t.role.toUpperCase()}`}>
                {t.role.toUpperCase()}
              </span>
              {t.toolKind != null && (
                <span className="tag" style={{ fontSize: 9.5 }}>
                  {t.toolKind}
                </span>
              )}
              {t.toolName != null && (
                <span className="mono" style={{ color: 'var(--ink-3)' }}>
                  {t.toolName}
                </span>
              )}
              {t.model != null && (
                <span className="mono">{t.model}</span>
              )}
              {t.tokensIn != null && t.tokensOut != null && (
                <span className="mono">
                  {t.tokensIn}{t.tokensOut}
                </span>
              )}
              <span style={{ flex: 1 }} />
              <span className="mono" style={{ opacity: 0.5 }}>
                #{t.i}
              </span>
            </div>
            <div className="turn-body">
              {t.role === 'tool' ? (
                t.body
              ) : (
                <ReactMarkdown remarkPlugins={[remarkGfm]}>{t.body}</ReactMarkdown>
              )}
            </div>
            {t.toolOutput != null && (
              <details className="tool-output">
                <summary style={{ fontSize: 11, color: 'var(--ink-2)', cursor: 'pointer' }}>
                  {t.toolName ?? 'tool'} output
                </summary>
                <pre style={{ marginTop: 6, fontSize: 12, maxHeight: 360, overflow: 'auto' }}>
                  {t.toolOutput}
                </pre>
              </details>
            )}
          </div>
        )
      })}
    </main>
  )
}