claude-session-viewer 0.1.0 → 0.1.2

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.
@@ -1,132 +0,0 @@
1
- import { useState } from 'react'
2
- import { format } from 'date-fns'
3
-
4
- interface Session {
5
- id: string
6
- project: string
7
- timestamp: string
8
- messages: any[]
9
- messageCount: number
10
- title?: string
11
- isAgent?: boolean
12
- agentSessions?: Session[]
13
- }
14
-
15
- interface ProjectGroupProps {
16
- name: string
17
- displayName: string
18
- sessionCount: number
19
- lastActivity: string
20
- sessions: Session[]
21
- selectedId: string | null
22
- onSelectSession: (id: string) => void
23
- }
24
-
25
- export default function ProjectGroup({
26
- displayName,
27
- sessionCount,
28
- lastActivity,
29
- sessions,
30
- selectedId,
31
- onSelectSession,
32
- }: ProjectGroupProps) {
33
- const [isExpanded, setIsExpanded] = useState(false)
34
-
35
- return (
36
- <div className="border-b border-gray-700">
37
- {/* Project Header */}
38
- <button
39
- onClick={() => setIsExpanded(!isExpanded)}
40
- className="sticky top-0 z-10 w-full text-left p-4 bg-gray-900 hover:bg-gray-800 transition-colors flex items-center justify-between"
41
- >
42
- <div className="flex-1 min-w-0">
43
- <div className="flex items-center gap-2">
44
- <svg
45
- className={`w-4 h-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`}
46
- fill="none"
47
- viewBox="0 0 24 24"
48
- stroke="currentColor"
49
- >
50
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
51
- </svg>
52
- <span className="font-semibold text-sm truncate">{displayName}</span>
53
- </div>
54
- <div className="text-xs text-gray-400 mt-1 ml-6">
55
- {sessionCount} session{sessionCount !== 1 ? 's' : ''} · Last activity{' '}
56
- {format(new Date(lastActivity), 'PPp')}
57
- </div>
58
- </div>
59
- </button>
60
-
61
- {/* Sessions List */}
62
- {isExpanded && (
63
- <div className="bg-gray-900/50">
64
- {sessions.map((session) => (
65
- <div key={session.id}>
66
- {/* Main Session */}
67
- <button
68
- onClick={() => onSelectSession(session.id)}
69
- className={`w-full text-left px-4 py-3 pl-8 hover:bg-gray-700/50 transition-colors border-l-2 ${
70
- selectedId === session.id
71
- ? 'bg-gray-700 border-blue-500'
72
- : 'border-transparent'
73
- }`}
74
- >
75
- {session.title && (
76
- <div className="text-sm font-medium text-gray-200 mb-1 truncate">
77
- {session.title}
78
- </div>
79
- )}
80
- <div className="text-xs text-gray-400">
81
- {format(new Date(session.timestamp), 'PPpp')}
82
- </div>
83
- <div className="text-xs text-gray-500 mt-1">
84
- {session.messageCount} message{session.messageCount !== 1 ? 's' : ''}
85
- {session.agentSessions && session.agentSessions.length > 0 && (
86
- <span className="ml-2">
87
- · {session.agentSessions.length} task{session.agentSessions.length !== 1 ? 's' : ''}
88
- </span>
89
- )}
90
- </div>
91
- </button>
92
-
93
- {/* Agent Sessions */}
94
- {session.agentSessions && session.agentSessions.length > 0 && (
95
- <div className="bg-gray-900/70">
96
- {session.agentSessions.map((agentSession) => (
97
- <button
98
- key={agentSession.id}
99
- onClick={() => onSelectSession(agentSession.id)}
100
- className={`w-full text-left px-4 py-2 pl-14 hover:bg-gray-700/50 transition-colors border-l-2 ${
101
- selectedId === agentSession.id
102
- ? 'bg-gray-700 border-blue-500'
103
- : 'border-transparent'
104
- }`}
105
- >
106
- <div className="flex items-center gap-2 mb-1">
107
- <span className="px-1.5 py-0.5 text-xs bg-purple-900/50 text-purple-300 rounded">
108
- TASK
109
- </span>
110
- {agentSession.title && (
111
- <span className="text-sm font-medium text-gray-300 truncate">
112
- {agentSession.title}
113
- </span>
114
- )}
115
- </div>
116
- <div className="text-xs text-gray-400">
117
- {format(new Date(agentSession.timestamp), 'PPpp')}
118
- </div>
119
- <div className="text-xs text-gray-500 mt-1">
120
- {agentSession.messageCount} message{agentSession.messageCount !== 1 ? 's' : ''}
121
- </div>
122
- </button>
123
- ))}
124
- </div>
125
- )}
126
- </div>
127
- ))}
128
- </div>
129
- )}
130
- </div>
131
- )
132
- }
@@ -1,140 +0,0 @@
1
- import { useQuery } from '@tanstack/react-query'
2
- import { format } from 'date-fns'
3
-
4
- interface SessionDetailProps {
5
- sessionId: string
6
- }
7
-
8
- export default function SessionDetail({ sessionId }: SessionDetailProps) {
9
- const { data, isLoading, error } = useQuery({
10
- queryKey: ['session', sessionId],
11
- queryFn: async () => {
12
- const response = await fetch(`/api/sessions/${sessionId}`)
13
- if (!response.ok) throw new Error('Failed to fetch session')
14
- return response.json()
15
- },
16
- })
17
-
18
- if (isLoading) {
19
- return (
20
- <div className="flex items-center justify-center h-full">
21
- <div className="text-gray-400">Loading session...</div>
22
- </div>
23
- )
24
- }
25
-
26
- if (error) {
27
- return (
28
- <div className="flex items-center justify-center h-full">
29
- <div className="text-red-400">Error: {error.message}</div>
30
- </div>
31
- )
32
- }
33
-
34
- const session = data?.session
35
-
36
- return (
37
- <div className="h-full flex flex-col">
38
- {/* Header */}
39
- <div className="border-b border-gray-700 p-6 bg-gray-800">
40
- <div className="flex items-center gap-2">
41
- {session?.isAgent && (
42
- <span className="px-2 py-1 text-xs bg-purple-900/50 text-purple-300 rounded font-semibold">
43
- TASK
44
- </span>
45
- )}
46
- <h2 className="text-2xl font-bold truncate flex-1">{session?.title || 'Untitled Session'}</h2>
47
- </div>
48
- <div className="text-xl text-gray-300">{session?.project}</div>
49
- <div className="flex items-center gap-3 mt-4 text-sm text-gray-400">
50
- <span>
51
- {session?.timestamp && format(new Date(session.timestamp), 'PPpp')}
52
- </span>
53
- <span>•</span>
54
- <span>
55
- {session?.messageCount || 0} message{session?.messageCount !== 1 ? 's' : ''}
56
- </span>
57
- {session?.agentSessions && session.agentSessions.length > 0 && (
58
- <>
59
- <span>•</span>
60
- <span>
61
- {session.agentSessions.length} task{session.agentSessions.length !== 1 ? 's' : ''}
62
- </span>
63
- </>
64
- )}
65
- </div>
66
- </div>
67
-
68
- {/* Messages Timeline */}
69
- <div className="flex-1 overflow-y-auto p-6">
70
- <div className="max-w-4xl mx-auto space-y-6">
71
- {session?.messages.map((message: any, index: number) => (
72
- <div key={index} className="border-l-2 border-gray-700 pl-4">
73
- <div className="flex items-start justify-between mb-2">
74
- <div className="flex items-center gap-2">
75
- <span
76
- className={`px-2 py-1 text-xs rounded ${
77
- message.type === 'user'
78
- ? 'bg-blue-900 text-blue-200'
79
- : message.type === 'assistant'
80
- ? 'bg-green-900 text-green-200'
81
- : 'bg-gray-700 text-gray-300'
82
- }`}
83
- >
84
- {message.type || 'system'}
85
- </span>
86
- {message.timestamp && (
87
- <span className="text-xs text-gray-500">
88
- {format(new Date(message.timestamp), 'HH:mm:ss')}
89
- </span>
90
- )}
91
- </div>
92
- </div>
93
-
94
- {/* Message Content */}
95
- <div className="bg-gray-800 rounded-lg p-4 text-sm">
96
- {message.message?.content && Array.isArray(message.message.content) ? (
97
- <div className="space-y-2">
98
- {message.message.content.map((content: any, idx: number) => (
99
- <div key={idx}>
100
- {content.type === 'text' && (
101
- <p className="whitespace-pre-wrap">{content.text}</p>
102
- )}
103
- {content.type === 'tool_use' && (
104
- <div className="bg-gray-900 p-3 rounded border border-gray-700">
105
- <div className="text-yellow-400 font-mono text-xs mb-2">
106
- 🔧 {content.name}
107
- </div>
108
- <pre className="text-xs overflow-x-auto text-gray-400">
109
- {JSON.stringify(content.input, null, 2)}
110
- </pre>
111
- </div>
112
- )}
113
- {content.type === 'tool_result' && (
114
- <div className="bg-gray-900 p-3 rounded border border-gray-700">
115
- <div className="text-green-400 font-mono text-xs mb-2">
116
- ✓ Tool Result
117
- </div>
118
- <pre className="text-xs overflow-x-auto text-gray-400">
119
- {typeof content.content === 'string'
120
- ? content.content
121
- : JSON.stringify(content.content, null, 2)}
122
- </pre>
123
- </div>
124
- )}
125
- </div>
126
- ))}
127
- </div>
128
- ) : (
129
- <pre className="text-xs overflow-x-auto text-gray-400">
130
- {JSON.stringify(message, null, 2)}
131
- </pre>
132
- )}
133
- </div>
134
- </div>
135
- ))}
136
- </div>
137
- </div>
138
- </div>
139
- )
140
- }
@@ -1,45 +0,0 @@
1
- import ProjectGroup from './ProjectGroup'
2
-
3
- interface Session {
4
- id: string
5
- project: string
6
- timestamp: string
7
- messages: any[]
8
- messageCount: number
9
- title?: string
10
- isAgent?: boolean
11
- agentSessions?: Session[]
12
- }
13
-
14
- interface ProjectGroup {
15
- name: string
16
- displayName: string
17
- sessionCount: number
18
- lastActivity: string
19
- sessions: Session[]
20
- }
21
-
22
- interface SessionListProps {
23
- projects: ProjectGroup[]
24
- selectedId: string | null
25
- onSelect: (id: string) => void
26
- }
27
-
28
- export default function SessionList({ projects, selectedId, onSelect }: SessionListProps) {
29
- return (
30
- <div>
31
- {projects.map((project) => (
32
- <ProjectGroup
33
- key={project.name}
34
- name={project.name}
35
- displayName={project.displayName}
36
- sessionCount={project.sessionCount}
37
- lastActivity={project.lastActivity}
38
- sessions={project.sessions}
39
- selectedId={selectedId}
40
- onSelectSession={onSelect}
41
- />
42
- ))}
43
- </div>
44
- )
45
- }
package/src/index.css DELETED
@@ -1,55 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- :root {
6
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
7
- line-height: 1.5;
8
- font-weight: 400;
9
-
10
- color-scheme: light dark;
11
- color: rgba(255, 255, 255, 0.87);
12
- background-color: #242424;
13
-
14
- font-synthesis: none;
15
- text-rendering: optimizeLegibility;
16
- -webkit-font-smoothing: antialiased;
17
- -moz-osx-font-smoothing: grayscale;
18
- }
19
-
20
- body {
21
- margin: 0;
22
- display: flex;
23
- place-items: center;
24
- min-width: 320px;
25
- min-height: 100vh;
26
- }
27
-
28
- #root {
29
- width: 100%;
30
- min-height: 100vh;
31
- }
32
-
33
- /* Custom scrollbar styles */
34
- * {
35
- scrollbar-width: thin;
36
- scrollbar-color: #4B5563 #1F2937;
37
- }
38
-
39
- *::-webkit-scrollbar {
40
- width: 8px;
41
- height: 8px;
42
- }
43
-
44
- *::-webkit-scrollbar-track {
45
- background: #1F2937;
46
- }
47
-
48
- *::-webkit-scrollbar-thumb {
49
- background-color: #4B5563;
50
- border-radius: 4px;
51
- }
52
-
53
- *::-webkit-scrollbar-thumb:hover {
54
- background-color: #6B7280;
55
- }
package/src/main.tsx DELETED
@@ -1,10 +0,0 @@
1
- import React from 'react'
2
- import ReactDOM from 'react-dom/client'
3
- import App from './App.tsx'
4
- import './index.css'
5
-
6
- ReactDOM.createRoot(document.getElementById('root')!).render(
7
- <React.StrictMode>
8
- <App />
9
- </React.StrictMode>,
10
- )