~bigbes/lethe

ref: 26d77892c6cfd3215a6d55bdb6315ec2d2b2fb00 lethe/web/src/routes/session.$tool.$host.$id.tsx -rw-r--r-- 4.2 KiB
26d77892 — Eugene Blikh web: align tool-call hide selector 30 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
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import React, { useState } from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { SubBar } from '../shell/SubBar'
import { Tag } from '../primitives'
import { useSession } from '../features/session/useSession'
import { TurnList } from '../features/session/TurnList'
import { Transcript } from '../features/session/Transcript'
import { AuthError, APIError } from '../api/client'
import '../styles/session.css'

export const Route = createFileRoute('/session/$tool/$host/$id')({
  component: SessionRoute,
})

function SessionRoute(): React.JSX.Element {
  const { tool, host, id } = Route.useParams()
  const { data: session, isLoading, error } = useSession(tool, host, id)

  const firstSeq = session?.turns[0]?.i ?? 0
  const [selected, setSelected] = useState<number>(firstSeq)

  // When turns load and selected hasn't been set yet (still at 0), set to first
  const effectiveSelected =
    session != null && selected === 0 && session.turns.length > 0
      ? session.turns[0].i
      : selected

  function handleSelect(seq: number): void {
    setSelected(seq)
    const el = document.getElementById(`turn-${seq}`)
    if (el != null) {
      el.scrollIntoView({ behavior: 'smooth', block: 'start' })
    }
  }

  if (isLoading) {
    return (
      <>
        <SubBar>
          <Tag kind="tool">{tool}</Tag>
          <Tag kind="host">{host}</Tag>
        </SubBar>
        <div
          className="body body-pad"
          style={{ display: 'flex', justifyContent: 'center', paddingTop: 40 }}
        >
          <span className="mono muted">loading session</span>
        </div>
      </>
    )
  }

  if (error != null) {
    if (error instanceof AuthError) {
      return (
        <>
          <SubBar>
            <Tag kind="tool">{tool}</Tag>
            <Tag kind="host">{host}</Tag>
          </SubBar>
          <div
            className="body body-pad"
            style={{
              display: 'flex',
              justifyContent: 'center',
              paddingTop: 60,
            }}
          >
            <div className="card" style={{ padding: '24px 32px', textAlign: 'center' }}>
              <div className="uppercase-mono" style={{ marginBottom: 8 }}>
                not authenticated
              </div>
              <div className="muted">Sign in to view this session.</div>
            </div>
          </div>
        </>
      )
    }
    const detail = error instanceof APIError ? error.message : String(error)
    return (
      <>
        <SubBar>
          <Tag kind="tool">{tool}</Tag>
          <Tag kind="host">{host}</Tag>
        </SubBar>
        <div
          className="body body-pad"
          style={{
            display: 'flex',
            justifyContent: 'center',
            paddingTop: 60,
          }}
        >
          <div className="card" style={{ padding: '24px 32px', textAlign: 'center' }}>
            <div className="uppercase-mono" style={{ marginBottom: 8 }}>
              error
            </div>
            <div className="muted">{detail}</div>
          </div>
        </div>
      </>
    )
  }

  if (session == null) {
    return <></>
  }

  const summaryPreview =
    session.summary.length > 56
      ? session.summary.slice(0, 56) + '…'
      : session.summary

  return (
    <>
      <SubBar>
        {session.cwd !== '' && (
          <span className="mono muted truncate" style={{ maxWidth: 200 }}>
            {session.cwd}
          </span>
        )}
        <span style={{ fontWeight: 600 }}>{summaryPreview}</span>
        <Tag kind="tool">{tool}</Tag>
        <Tag kind="host">{host}</Tag>
        {session.model != null && (
          <Tag kind="neutral">{session.model}</Tag>
        )}
        <span style={{ flex: 1 }} />
        <span className="mono muted">
          {session.turns.length} turns
        </span>
      </SubBar>

      <div
        style={{
          flex: 1,
          display: 'flex',
          overflow: 'hidden',
          minHeight: 0,
        }}
      >
        <TurnList
          turns={session.turns}
          selected={effectiveSelected}
          onSelect={handleSelect}
        />
        <Transcript
          turns={session.turns}
          selected={effectiveSelected}
          onSelect={handleSelect}
        />
      </div>
    </>
  )
}