@ailog/cli 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +2 -2
  2. package/dist/bin/cli.js +70 -3
  3. package/dist/standalone/.next/BUILD_ID +1 -1
  4. package/dist/standalone/.next/build-manifest.json +2 -2
  5. package/dist/standalone/.next/server/app/_global-error.html +2 -2
  6. package/dist/standalone/.next/server/app/_global-error.rsc +1 -1
  7. package/dist/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/dist/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/dist/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/dist/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/dist/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/dist/standalone/.next/server/app/_not-found.html +1 -1
  13. package/dist/standalone/.next/server/app/_not-found.rsc +1 -1
  14. package/dist/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/dist/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/dist/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/dist/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/dist/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/dist/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/dist/standalone/.next/server/app/api/[[...route]]/route.js.nft.json +1 -1
  21. package/dist/standalone/.next/server/app/index.html +1 -1
  22. package/dist/standalone/.next/server/app/index.rsc +1 -1
  23. package/dist/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  24. package/dist/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  25. package/dist/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  26. package/dist/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  27. package/dist/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  28. package/dist/standalone/.next/server/chunks/[root-of-the-server]__a0f47697._.js +1 -1
  29. package/dist/standalone/.next/server/chunks/ssr/node_modules__bun_4bd60410._.js +1 -1
  30. package/dist/standalone/.next/server/pages/404.html +1 -1
  31. package/dist/standalone/.next/server/pages/500.html +2 -2
  32. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +5 -4
  33. package/dist/standalone/dist/standalone/dist/standalone/package.json +3 -3
  34. package/dist/standalone/dist/standalone/package.json +3 -3
  35. package/dist/standalone/package.json +3 -3
  36. package/package.json +3 -3
  37. package/dist/standalone/.env +0 -0
  38. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  39. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  40. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  41. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  42. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  43. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  44. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  45. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  46. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  47. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  48. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  49. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  50. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  51. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  52. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  53. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  54. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  55. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  56. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  57. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  58. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  59. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  60. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  61. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  62. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  63. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  64. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  65. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  66. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  67. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  68. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  69. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  70. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  71. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  72. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  73. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  74. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  75. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  76. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  77. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  78. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  79. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  80. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/layout.tsx +0 -3
  81. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/page.tsx +0 -238
  82. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/settings/page.tsx +0 -224
  83. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/app/apps/[appId]/threads/[threadId]/page.tsx +0 -283
  84. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components/apps-table.tsx +0 -274
  85. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/components.json +0 -24
  86. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  87. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  88. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  89. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  90. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  91. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  92. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  93. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  94. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  95. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  96. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  97. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  98. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  99. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  100. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/package.json +0 -65
  101. package/dist/standalone/dist/standalone/dist/standalone/dist/standalone/dist/standalone/tsconfig.json +0 -34
  102. /package/dist/standalone/.next/static/{8H5ZwnsPRw0PovUUXMbkb → ePLfvIxIoteKYr9bTCkF7}/_buildManifest.js +0 -0
  103. /package/dist/standalone/.next/static/{8H5ZwnsPRw0PovUUXMbkb → ePLfvIxIoteKYr9bTCkF7}/_clientMiddlewareManifest.json +0 -0
  104. /package/dist/standalone/.next/static/{8H5ZwnsPRw0PovUUXMbkb → ePLfvIxIoteKYr9bTCkF7}/_ssgManifest.js +0 -0
@@ -1,224 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useState, useCallback } from 'react'
4
- import { useParams } from 'next/navigation'
5
- import {
6
- Card,
7
- CardContent,
8
- CardDescription,
9
- CardHeader,
10
- CardTitle,
11
- } from '@/components/ui/card'
12
- import { Skeleton } from '@/components/ui/skeleton'
13
- import { Button } from '@/components/ui/button'
14
- import { Input } from '@/components/ui/input'
15
- import { Label } from '@/components/ui/label'
16
- import { ApiKeysTable } from '@/components/api-keys-table'
17
- import { CreateApiKeyDialog } from '@/components/create-api-key-dialog'
18
- import { rpcClient } from '@/lib/rpc-client'
19
- import { toast } from 'sonner'
20
- import type { App, ApiKey } from '@/lib/api'
21
-
22
- function formatDate(dateString: string) {
23
- return new Date(dateString).toLocaleDateString('en-US', {
24
- year: 'numeric',
25
- month: 'long',
26
- day: 'numeric',
27
- hour: '2-digit',
28
- minute: '2-digit',
29
- })
30
- }
31
-
32
- export default function SettingsPage() {
33
- const params = useParams()
34
- const appId = params.appId as string
35
-
36
- const [app, setApp] = useState<App | null>(null)
37
- const [isLoading, setIsLoading] = useState(true)
38
- const [isSaving, setIsSaving] = useState(false)
39
- const [name, setName] = useState('')
40
-
41
- // API Keys state
42
- const [keys, setKeys] = useState<ApiKey[]>([])
43
- const [isLoadingKeys, setIsLoadingKeys] = useState(true)
44
-
45
- const fetchKeys = useCallback(async () => {
46
- try {
47
- const res = await rpcClient.api.admin.apps[':appId']['api-keys'].$get({
48
- param: { appId },
49
- })
50
- if (res.ok) {
51
- const data = await res.json()
52
- setKeys(data.keys)
53
- }
54
- } catch (error) {
55
- console.error('Failed to fetch API keys:', error)
56
- } finally {
57
- setIsLoadingKeys(false)
58
- }
59
- }, [appId])
60
-
61
- useEffect(() => {
62
- async function fetchData() {
63
- try {
64
- const appRes = await rpcClient.api.admin.apps[':appId'].$get({
65
- param: { appId },
66
- })
67
-
68
- if (appRes.ok) {
69
- const appData = await appRes.json()
70
- setApp(appData as App)
71
- setName(appData.name)
72
- }
73
- } catch (error) {
74
- console.error('Failed to fetch data:', error)
75
- } finally {
76
- setIsLoading(false)
77
- }
78
- }
79
- fetchData()
80
- fetchKeys()
81
- }, [appId, fetchKeys])
82
-
83
- const handleSave = async () => {
84
- if (!name.trim()) {
85
- toast.error('Name is required')
86
- return
87
- }
88
-
89
- setIsSaving(true)
90
- try {
91
- const res = await rpcClient.api.admin.apps[':appId'].$put({
92
- param: { appId },
93
- json: { name: name.trim() },
94
- })
95
-
96
- if (res.ok) {
97
- const updatedApp = await res.json()
98
- setApp(updatedApp as App)
99
- toast.success('App name updated successfully')
100
- } else {
101
- toast.error('Failed to update app name')
102
- }
103
- } catch (error) {
104
- console.error('Failed to update app:', error)
105
- toast.error('Failed to update app name')
106
- } finally {
107
- setIsSaving(false)
108
- }
109
- }
110
-
111
- const hasChanges = app && name !== app.name
112
-
113
- if (isLoading) {
114
- return (
115
- <div className="space-y-6">
116
- <Card>
117
- <CardHeader>
118
- <Skeleton className="h-5 w-24" />
119
- </CardHeader>
120
- <CardContent className="space-y-4">
121
- <Skeleton className="h-4 w-full" />
122
- <Skeleton className="h-4 w-3/4" />
123
- </CardContent>
124
- </Card>
125
- </div>
126
- )
127
- }
128
-
129
- return (
130
- <div className="space-y-6">
131
- <Card>
132
- <CardHeader>
133
- <CardTitle>General</CardTitle>
134
- <CardDescription>
135
- Application settings and information
136
- </CardDescription>
137
- </CardHeader>
138
- <CardContent className="space-y-4">
139
- <div className="space-y-2">
140
- <Label htmlFor="name">App Name</Label>
141
- <div className="flex gap-2">
142
- <Input
143
- id="name"
144
- value={name}
145
- onChange={(e) => setName(e.target.value)}
146
- placeholder="Enter app name"
147
- className="max-w-md"
148
- />
149
- <Button onClick={handleSave} disabled={!hasChanges || isSaving}>
150
- {isSaving ? 'Saving...' : 'Save'}
151
- </Button>
152
- </div>
153
- </div>
154
- <div>
155
- <p className="text-muted-foreground text-sm font-medium">App ID</p>
156
- <p className="font-mono text-sm">{app?.appId}</p>
157
- </div>
158
- <div className="grid grid-cols-2 gap-4">
159
- <div>
160
- <p className="text-muted-foreground text-sm font-medium">
161
- Created
162
- </p>
163
- <p className="text-sm">
164
- {app?.createdAt ? formatDate(app.createdAt) : '-'}
165
- </p>
166
- </div>
167
- <div>
168
- <p className="text-muted-foreground text-sm font-medium">
169
- Last Updated
170
- </p>
171
- <p className="text-sm">
172
- {app?.updatedAt ? formatDate(app.updatedAt) : '-'}
173
- </p>
174
- </div>
175
- </div>
176
- {app?.metadata && Object.keys(app.metadata).length > 0 && (
177
- <div>
178
- <p className="text-muted-foreground mb-2 text-sm font-medium">
179
- Metadata
180
- </p>
181
- <pre className="bg-muted overflow-auto rounded-md p-2 text-xs">
182
- {JSON.stringify(app.metadata, null, 2)}
183
- </pre>
184
- </div>
185
- )}
186
- </CardContent>
187
- </Card>
188
-
189
- <Card>
190
- <CardHeader>
191
- <div className="flex items-center justify-between">
192
- <div>
193
- <CardTitle>API Keys</CardTitle>
194
- <CardDescription>
195
- Manage API keys for this application
196
- </CardDescription>
197
- </div>
198
- <CreateApiKeyDialog
199
- appId={appId}
200
- onKeyCreated={fetchKeys}
201
- isFirstKey={keys.length === 0 && !isLoadingKeys}
202
- />
203
- </div>
204
- </CardHeader>
205
- <CardContent>
206
- {keys.length === 0 && !isLoadingKeys ? (
207
- <div className="py-6 text-center">
208
- <p className="text-muted-foreground text-sm">
209
- No API keys yet. Create one to start using the API.
210
- </p>
211
- </div>
212
- ) : (
213
- <ApiKeysTable
214
- keys={keys}
215
- isLoading={isLoadingKeys}
216
- appId={appId}
217
- onRevoke={fetchKeys}
218
- />
219
- )}
220
- </CardContent>
221
- </Card>
222
- </div>
223
- )
224
- }
@@ -1,283 +0,0 @@
1
- 'use client'
2
-
3
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
- import { useParams } from 'next/navigation'
5
- import { Card, CardContent, CardHeader } from '@/components/ui/card'
6
- import { Button } from '@/components/ui/button'
7
- import { Skeleton } from '@/components/ui/skeleton'
8
- import { ChatView } from '@/components/chat-view'
9
- import { ScrollArea } from '@/components/ui/scroll-area'
10
- import { RequestDetailPanel } from '@/components/request-detail-panel'
11
- import { GenerationDetailPanel } from '@/components/generation-detail-panel'
12
- import { GenerationTimeline } from '@/components/generation-timeline'
13
- import { useKeydown } from '@/hooks/use-keydown'
14
- import { rpcClient } from '@/lib/rpc-client'
15
- import type { Thread, Generation } from '@/lib/api'
16
-
17
- export default function ThreadDetailPage() {
18
- const params = useParams()
19
- const appId = params.appId as string
20
- const threadId = params.threadId as string
21
-
22
- const [thread, setThread] = useState<Thread | null>(null)
23
- const [generations, setGenerations] = useState<Generation[]>([])
24
- const [isLoading, setIsLoading] = useState(true)
25
- const [selectedRequestId, setSelectedRequestId] = useState<string | null>(
26
- null,
27
- )
28
- const [selectedGenerationId, setSelectedGenerationId] = useState<
29
- string | null
30
- >(null)
31
- const [viewMode, setViewMode] = useState<'details' | 'timeline'>('details')
32
-
33
- // Keyboard navigation for generations
34
- const handleKeyDown = useCallback(
35
- (e: KeyboardEvent) => {
36
- if (generations.length === 0) return
37
-
38
- const currentIndex = generations.findIndex(
39
- (g) => g.generationId === selectedGenerationId,
40
- )
41
-
42
- if (e.key === 'ArrowUp') {
43
- e.preventDefault()
44
- if (currentIndex === -1) {
45
- setSelectedGenerationId(generations[0].generationId)
46
- setSelectedRequestId(null)
47
- } else if (currentIndex < generations.length - 1) {
48
- setSelectedGenerationId(generations[currentIndex + 1].generationId)
49
- setSelectedRequestId(null)
50
- }
51
- } else if (e.key === 'ArrowDown') {
52
- e.preventDefault()
53
- if (currentIndex > 0) {
54
- setSelectedGenerationId(generations[currentIndex - 1].generationId)
55
- setSelectedRequestId(null)
56
- } else if (currentIndex === -1 && generations.length > 0) {
57
- setSelectedGenerationId(
58
- generations[generations.length - 1].generationId,
59
- )
60
- setSelectedRequestId(null)
61
- }
62
- }
63
- },
64
- [generations, selectedGenerationId],
65
- )
66
-
67
- useKeydown(handleKeyDown)
68
-
69
- const leftContainerRef = useRef<HTMLDivElement>(null)
70
-
71
- // Scroll to selected generation (within ScrollArea only, not main page)
72
- useEffect(() => {
73
- if (!selectedGenerationId || !leftContainerRef.current) return
74
-
75
- const viewport = leftContainerRef.current.querySelector(
76
- '[data-slot="scroll-area-viewport"]',
77
- )
78
- const element = leftContainerRef.current.querySelector(
79
- `[data-generation-id="${selectedGenerationId}"]`,
80
- )
81
-
82
- if (viewport && element) {
83
- const viewportRect = viewport.getBoundingClientRect()
84
- const elementRect = element.getBoundingClientRect()
85
- const scrollTop = viewport.scrollTop
86
-
87
- const elementTop = elementRect.top - viewportRect.top + scrollTop
88
- const elementBottom = elementTop + elementRect.height
89
- const viewportHeight = viewportRect.height
90
-
91
- if (elementTop < scrollTop) {
92
- viewport.scrollTo({ top: elementTop, behavior: 'smooth' })
93
- } else if (elementBottom > scrollTop + viewportHeight) {
94
- viewport.scrollTo({
95
- top: elementBottom - viewportHeight,
96
- behavior: 'smooth',
97
- })
98
- }
99
- }
100
- }, [selectedGenerationId])
101
-
102
- useEffect(() => {
103
- async function fetchData() {
104
- try {
105
- // Fetch thread and all generations for this thread
106
- const [threadRes, generationsRes] = await Promise.all([
107
- rpcClient.api.admin.apps[':appId'].threads[':threadId'].$get({
108
- param: { appId, threadId },
109
- }),
110
- rpcClient.api.admin.apps[':appId'].generations.$get({
111
- param: { appId },
112
- query: { threadId },
113
- }),
114
- ])
115
-
116
- if (threadRes.ok) {
117
- const threadData = await threadRes.json()
118
- setThread(threadData as Thread)
119
- }
120
-
121
- if (generationsRes.ok) {
122
- const generationsData = await generationsRes.json()
123
- setGenerations(generationsData.generations as Generation[])
124
- }
125
- } catch (error) {
126
- console.error('Failed to fetch data:', error)
127
- } finally {
128
- setIsLoading(false)
129
- }
130
- }
131
- fetchData()
132
- }, [appId, threadId])
133
-
134
- // Derive requests from generations by grouping unique requestIds
135
- const requests = useMemo(() => {
136
- const requestMap = new Map<
137
- string,
138
- { requestId: string; createdAt: string }
139
- >()
140
- for (const gen of generations) {
141
- if (!requestMap.has(gen.requestId)) {
142
- requestMap.set(gen.requestId, {
143
- requestId: gen.requestId,
144
- createdAt: gen.createdAt,
145
- })
146
- } else {
147
- // Use the earliest createdAt for the request
148
- const existing = requestMap.get(gen.requestId)!
149
- if (gen.createdAt < existing.createdAt) {
150
- existing.createdAt = gen.createdAt
151
- }
152
- }
153
- }
154
- return Array.from(requestMap.values())
155
- }, [generations])
156
-
157
- return (
158
- <div className="space-y-4">
159
- {/* Thread Info / Timeline */}
160
- <Card className="py-2">
161
- <CardHeader className="px-3 py-2">
162
- <div className="flex items-center justify-between">
163
- <div className="flex gap-1">
164
- <Button
165
- variant={viewMode === 'details' ? 'secondary' : 'ghost'}
166
- size="sm"
167
- className="h-6 px-2 text-xs"
168
- onClick={() => setViewMode('details')}
169
- >
170
- Details
171
- </Button>
172
- <Button
173
- variant={viewMode === 'timeline' ? 'secondary' : 'ghost'}
174
- size="sm"
175
- className="h-6 px-2 text-xs"
176
- onClick={() => setViewMode('timeline')}
177
- disabled={generations.length === 0}
178
- >
179
- Timeline
180
- </Button>
181
- </div>
182
- {isLoading ? (
183
- <Skeleton className="h-4 w-24" />
184
- ) : (
185
- <span className="text-muted-foreground text-xs">
186
- {thread?.title || '(Untitled)'}
187
- </span>
188
- )}
189
- </div>
190
- </CardHeader>
191
- <CardContent className="px-3 pt-0 pb-2">
192
- {viewMode === 'details' ? (
193
- isLoading ? (
194
- <div className="flex gap-6">
195
- <Skeleton className="h-3 w-24" />
196
- <Skeleton className="h-3 w-24" />
197
- </div>
198
- ) : (
199
- <div className="flex flex-wrap gap-x-6 gap-y-1 text-xs">
200
- <div>
201
- <span className="text-muted-foreground">Thread ID: </span>
202
- <span className="font-mono">{thread?.threadId}</span>
203
- </div>
204
- {thread?.tenantId && (
205
- <div>
206
- <span className="text-muted-foreground">Tenant ID: </span>
207
- <span>{thread.tenantId}</span>
208
- </div>
209
- )}
210
- <div>
211
- <span className="text-muted-foreground">User: </span>
212
- <span className="font-mono">{thread?.userId}</span>
213
- </div>
214
- </div>
215
- )
216
- ) : (
217
- <GenerationTimeline
218
- generations={generations}
219
- selectedGenerationId={selectedGenerationId}
220
- onSelectGeneration={(generationId) => {
221
- setSelectedGenerationId(generationId)
222
- setSelectedRequestId(null)
223
- }}
224
- />
225
- )}
226
- </CardContent>
227
- </Card>
228
-
229
- {/* Split View: Chat + Details */}
230
- <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_minmax(0,2fr)] gap-3">
231
- <ScrollArea
232
- className="h-[calc(100vh-220px)] [&_[data-slot=scroll-area-viewport]>div]:!block"
233
- ref={leftContainerRef}
234
- >
235
- {/* Left: Chat View */}
236
- <ChatView
237
- requests={requests}
238
- generations={generations}
239
- isLoading={isLoading}
240
- selectedRequestId={selectedRequestId}
241
- selectedGenerationId={selectedGenerationId}
242
- onSelectRequest={(requestId) => {
243
- setSelectedRequestId(requestId)
244
- setSelectedGenerationId(null)
245
- }}
246
- onSelectGeneration={(generationId) => {
247
- setSelectedGenerationId(generationId)
248
- setSelectedRequestId(null)
249
- }}
250
- />
251
- </ScrollArea>
252
-
253
- <ScrollArea className="h-[calc(100vh-220px)] border [&_[data-slot=scroll-area-viewport]>div]:!block">
254
- {/* Right: Details Panel */}
255
- <div className="w-full min-w-0 overflow-hidden p-4">
256
- {selectedGenerationId ? (
257
- <GenerationDetailPanel
258
- generation={
259
- generations.find(
260
- (g) => g.generationId === selectedGenerationId,
261
- ) || null
262
- }
263
- />
264
- ) : (
265
- <RequestDetailPanel
266
- request={
267
- requests.find((r) => r.requestId === selectedRequestId) ||
268
- null
269
- }
270
- generations={generations}
271
- selectedGenerationId={selectedGenerationId}
272
- onSelectGeneration={(generationId) => {
273
- setSelectedGenerationId(generationId)
274
- setSelectedRequestId(null)
275
- }}
276
- />
277
- )}
278
- </div>
279
- </ScrollArea>
280
- </div>
281
- </div>
282
- )
283
- }