@dilipod/ui 0.4.23 → 0.4.25
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.
- package/dist/components/support-chat.d.ts +28 -0
- package/dist/components/support-chat.d.ts.map +1 -0
- package/dist/components/worker-spec.d.ts +18 -0
- package/dist/components/worker-spec.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/support-chat.tsx +236 -0
- package/src/components/worker-spec.tsx +155 -0
- package/src/index.ts +5 -1
package/package.json
CHANGED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useRef, useCallback, ReactNode } from 'react'
|
|
4
|
+
import { Button } from './button'
|
|
5
|
+
import { Textarea } from './textarea'
|
|
6
|
+
import {
|
|
7
|
+
CircleNotch,
|
|
8
|
+
PaperPlaneTilt,
|
|
9
|
+
} from '@phosphor-icons/react'
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
export interface SupportChatMessage {
|
|
16
|
+
id: string
|
|
17
|
+
role: 'customer' | 'admin' | 'system'
|
|
18
|
+
content: string
|
|
19
|
+
type: 'message' | 'event' | 'suggestion'
|
|
20
|
+
created_at: string
|
|
21
|
+
metadata?: Record<string, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SupportChatProps {
|
|
25
|
+
/** Fetch messages from this endpoint */
|
|
26
|
+
messagesEndpoint: string
|
|
27
|
+
/** POST new messages to this endpoint */
|
|
28
|
+
sendEndpoint: string
|
|
29
|
+
/** Role of the current user */
|
|
30
|
+
currentRole: 'customer' | 'admin'
|
|
31
|
+
/** Poll interval in ms (0 to disable). Default: 5000 */
|
|
32
|
+
pollInterval?: number
|
|
33
|
+
/** Placeholder for the input */
|
|
34
|
+
placeholder?: string
|
|
35
|
+
/** Custom empty state */
|
|
36
|
+
emptyMessage?: string | ReactNode
|
|
37
|
+
/** Additional CSS class */
|
|
38
|
+
className?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// Helpers
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
function formatMessageTime(dateStr: string): string {
|
|
46
|
+
const d = new Date(dateStr)
|
|
47
|
+
const now = new Date()
|
|
48
|
+
const diffMs = now.getTime() - d.getTime()
|
|
49
|
+
const diffMins = Math.floor(diffMs / 60000)
|
|
50
|
+
const diffHours = Math.floor(diffMs / 3600000)
|
|
51
|
+
|
|
52
|
+
if (diffMins < 1) return 'just now'
|
|
53
|
+
if (diffMins < 60) return `${diffMins}m ago`
|
|
54
|
+
if (diffHours < 24) return `${diffHours}h ago`
|
|
55
|
+
|
|
56
|
+
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================
|
|
60
|
+
// Component
|
|
61
|
+
// ============================================
|
|
62
|
+
|
|
63
|
+
export function SupportChat({
|
|
64
|
+
messagesEndpoint,
|
|
65
|
+
sendEndpoint,
|
|
66
|
+
currentRole,
|
|
67
|
+
pollInterval = 5000,
|
|
68
|
+
placeholder = 'Type a message...',
|
|
69
|
+
emptyMessage = 'No messages yet. Start the conversation!',
|
|
70
|
+
className = '',
|
|
71
|
+
}: SupportChatProps) {
|
|
72
|
+
const [messages, setMessages] = useState<SupportChatMessage[]>([])
|
|
73
|
+
const [loading, setLoading] = useState(true)
|
|
74
|
+
const [sending, setSending] = useState(false)
|
|
75
|
+
const [inputValue, setInputValue] = useState('')
|
|
76
|
+
const scrollRef = useRef<HTMLDivElement>(null)
|
|
77
|
+
const prevMessageCountRef = useRef(0)
|
|
78
|
+
|
|
79
|
+
const fetchMessages = useCallback(async () => {
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(messagesEndpoint)
|
|
82
|
+
if (!res.ok) return
|
|
83
|
+
const data = await res.json()
|
|
84
|
+
setMessages(data.messages || [])
|
|
85
|
+
} catch {
|
|
86
|
+
// Silently fail
|
|
87
|
+
} finally {
|
|
88
|
+
setLoading(false)
|
|
89
|
+
}
|
|
90
|
+
}, [messagesEndpoint])
|
|
91
|
+
|
|
92
|
+
// Initial fetch + polling
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
fetchMessages()
|
|
95
|
+
if (pollInterval > 0) {
|
|
96
|
+
const interval = setInterval(fetchMessages, pollInterval)
|
|
97
|
+
return () => clearInterval(interval)
|
|
98
|
+
}
|
|
99
|
+
}, [fetchMessages, pollInterval])
|
|
100
|
+
|
|
101
|
+
// Auto-scroll when new messages arrive
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (messages.length > prevMessageCountRef.current) {
|
|
104
|
+
scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' })
|
|
105
|
+
}
|
|
106
|
+
prevMessageCountRef.current = messages.length
|
|
107
|
+
}, [messages.length])
|
|
108
|
+
|
|
109
|
+
const handleSend = async () => {
|
|
110
|
+
const content = inputValue.trim()
|
|
111
|
+
if (!content || sending) return
|
|
112
|
+
|
|
113
|
+
setSending(true)
|
|
114
|
+
setInputValue('')
|
|
115
|
+
|
|
116
|
+
// Optimistic update
|
|
117
|
+
const optimisticMsg: SupportChatMessage = {
|
|
118
|
+
id: `temp-${Date.now()}`,
|
|
119
|
+
role: currentRole,
|
|
120
|
+
content,
|
|
121
|
+
type: 'message',
|
|
122
|
+
created_at: new Date().toISOString(),
|
|
123
|
+
}
|
|
124
|
+
setMessages((prev) => [...prev, optimisticMsg])
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const res = await fetch(sendEndpoint, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify({ content }),
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (res.ok) {
|
|
134
|
+
// Replace optimistic message with real one
|
|
135
|
+
await fetchMessages()
|
|
136
|
+
} else {
|
|
137
|
+
// Remove optimistic message on failure
|
|
138
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimisticMsg.id))
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimisticMsg.id))
|
|
142
|
+
} finally {
|
|
143
|
+
setSending(false)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
148
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
149
|
+
e.preventDefault()
|
|
150
|
+
handleSend()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (loading) {
|
|
155
|
+
return (
|
|
156
|
+
<div className={`flex items-center justify-center py-8 ${className}`}>
|
|
157
|
+
<CircleNotch className="w-5 h-5 animate-spin text-muted-foreground" />
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className={`flex flex-col ${className}`}>
|
|
164
|
+
{/* Messages area */}
|
|
165
|
+
<div ref={scrollRef} className="flex-1 overflow-y-auto space-y-3 mb-4 max-h-[400px] min-h-[200px] px-1">
|
|
166
|
+
{messages.length === 0 ? (
|
|
167
|
+
<div className="flex items-center justify-center h-full text-sm text-muted-foreground py-8">
|
|
168
|
+
{typeof emptyMessage === 'string' ? <p>{emptyMessage}</p> : emptyMessage}
|
|
169
|
+
</div>
|
|
170
|
+
) : (
|
|
171
|
+
messages.map((msg) => {
|
|
172
|
+
// System events — centered, subtle
|
|
173
|
+
if (msg.type === 'event' || msg.role === 'system') {
|
|
174
|
+
return (
|
|
175
|
+
<div key={msg.id} className="flex justify-center">
|
|
176
|
+
<div className="bg-gray-50 border border-gray-100 rounded-full px-3 py-1 text-xs text-muted-foreground max-w-[90%] text-center">
|
|
177
|
+
{msg.content}
|
|
178
|
+
<span className="ml-2 opacity-60">{formatMessageTime(msg.created_at)}</span>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Regular messages
|
|
185
|
+
const isCurrentUser = msg.role === currentRole
|
|
186
|
+
return (
|
|
187
|
+
<div key={msg.id} className={`flex ${isCurrentUser ? 'justify-end' : 'justify-start'}`}>
|
|
188
|
+
<div className={`max-w-[80%] rounded-lg px-3 py-2 ${
|
|
189
|
+
isCurrentUser
|
|
190
|
+
? 'bg-[var(--cyan)] text-white'
|
|
191
|
+
: 'bg-gray-100 text-[var(--black)]'
|
|
192
|
+
}`}>
|
|
193
|
+
{!isCurrentUser && (
|
|
194
|
+
<p className="text-xs font-medium mb-0.5 opacity-70">
|
|
195
|
+
{msg.role === 'admin' ? 'Dilipod Team' : 'You'}
|
|
196
|
+
</p>
|
|
197
|
+
)}
|
|
198
|
+
<p className="text-sm whitespace-pre-wrap">{msg.content}</p>
|
|
199
|
+
<p className={`text-[10px] mt-1 ${isCurrentUser ? 'text-white/60' : 'text-muted-foreground'}`}>
|
|
200
|
+
{formatMessageTime(msg.created_at)}
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
})
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{/* Input area */}
|
|
210
|
+
<div className="flex gap-2 border-t border-gray-100 pt-3">
|
|
211
|
+
<Textarea
|
|
212
|
+
value={inputValue}
|
|
213
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
214
|
+
onKeyDown={handleKeyDown}
|
|
215
|
+
placeholder={placeholder}
|
|
216
|
+
rows={1}
|
|
217
|
+
className="resize-none min-h-[36px] py-2 flex-1"
|
|
218
|
+
/>
|
|
219
|
+
<Button
|
|
220
|
+
onClick={handleSend}
|
|
221
|
+
disabled={sending || !inputValue.trim()}
|
|
222
|
+
size="sm"
|
|
223
|
+
className="flex-shrink-0 h-9"
|
|
224
|
+
>
|
|
225
|
+
{sending ? (
|
|
226
|
+
<CircleNotch className="w-4 h-4 animate-spin" />
|
|
227
|
+
) : (
|
|
228
|
+
<PaperPlaneTilt className="w-4 h-4" weight="bold" />
|
|
229
|
+
)}
|
|
230
|
+
</Button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export default SupportChat
|
|
@@ -19,14 +19,45 @@ import {
|
|
|
19
19
|
Sparkle,
|
|
20
20
|
Lightbulb,
|
|
21
21
|
Robot,
|
|
22
|
+
MagnifyingGlass,
|
|
23
|
+
FilmStrip,
|
|
24
|
+
FileText,
|
|
25
|
+
TextT,
|
|
26
|
+
VideoCamera,
|
|
27
|
+
ImageSquare,
|
|
22
28
|
} from '@phosphor-icons/react'
|
|
23
29
|
import { FlowchartDiagram } from './flowchart-diagram'
|
|
24
30
|
import { Badge } from './badge'
|
|
31
|
+
import {
|
|
32
|
+
Dialog,
|
|
33
|
+
DialogTrigger,
|
|
34
|
+
DialogContent,
|
|
35
|
+
DialogHeader,
|
|
36
|
+
DialogTitle,
|
|
37
|
+
DialogDescription,
|
|
38
|
+
} from './dialog'
|
|
25
39
|
|
|
26
40
|
// ============================================
|
|
27
41
|
// Types
|
|
28
42
|
// ============================================
|
|
29
43
|
|
|
44
|
+
export interface AnalysisSource {
|
|
45
|
+
type: string
|
|
46
|
+
name: string
|
|
47
|
+
size: number
|
|
48
|
+
insights?: string[]
|
|
49
|
+
processing_time_ms?: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AnalysisSummary {
|
|
53
|
+
total_sources: number
|
|
54
|
+
video_count: number
|
|
55
|
+
document_count: number
|
|
56
|
+
image_count: number
|
|
57
|
+
total_insights: number
|
|
58
|
+
analyzed_at: string
|
|
59
|
+
}
|
|
60
|
+
|
|
30
61
|
export interface WorkerSpecDocumentation {
|
|
31
62
|
scope: string | null
|
|
32
63
|
goal: string | null
|
|
@@ -50,6 +81,9 @@ export interface WorkerSpecDocumentation {
|
|
|
50
81
|
scenario: string
|
|
51
82
|
handling: string
|
|
52
83
|
}> | null
|
|
84
|
+
analysis_sources: AnalysisSource[] | null
|
|
85
|
+
analysis_summary: AnalysisSummary | null
|
|
86
|
+
analysis_context: string | null
|
|
53
87
|
version: number
|
|
54
88
|
model_used: string | null
|
|
55
89
|
updated_at: string
|
|
@@ -109,6 +143,47 @@ function SectionHeader({
|
|
|
109
143
|
)
|
|
110
144
|
}
|
|
111
145
|
|
|
146
|
+
// ============================================
|
|
147
|
+
// Analysis Context Renderer
|
|
148
|
+
// ============================================
|
|
149
|
+
|
|
150
|
+
function AnalysisContextRenderer({ content }: { content: string }) {
|
|
151
|
+
const sections = content.split(/^## /gm).filter(Boolean)
|
|
152
|
+
|
|
153
|
+
const getIcon = (title: string) => {
|
|
154
|
+
if (title.includes('Request')) return <Target size={14} className="text-[var(--cyan)]" />
|
|
155
|
+
if (title.includes('Video')) return <VideoCamera size={14} className="text-[var(--cyan)]" />
|
|
156
|
+
if (title.includes('Document')) return <FileText size={14} className="text-[var(--cyan)]" />
|
|
157
|
+
if (title.includes('Rules')) return <Lightbulb size={14} className="text-amber-500" />
|
|
158
|
+
if (title.includes('Context')) return <TextT size={14} className="text-[var(--cyan)]" />
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className="space-y-6">
|
|
164
|
+
{sections.map((section, index) => {
|
|
165
|
+
const lines = section.split('\n')
|
|
166
|
+
const title = lines[0]?.trim()
|
|
167
|
+
const body = lines.slice(1).join('\n').trim()
|
|
168
|
+
|
|
169
|
+
if (!body) return null
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div key={index}>
|
|
173
|
+
<h4 className="text-sm font-medium text-[var(--black)] flex items-center gap-2 mb-3">
|
|
174
|
+
{getIcon(title)}
|
|
175
|
+
{title}
|
|
176
|
+
</h4>
|
|
177
|
+
<div className="text-sm text-muted-foreground pl-5 space-y-2 whitespace-pre-line">
|
|
178
|
+
{body}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
})}
|
|
183
|
+
</div>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
112
187
|
// ============================================
|
|
113
188
|
// Main Component
|
|
114
189
|
// ============================================
|
|
@@ -153,9 +228,89 @@ export function WorkerSpec({ documentation, className }: WorkerSpecProps) {
|
|
|
153
228
|
? frequencyLabels[documentation.expected_impact.frequency] || documentation.expected_impact.frequency
|
|
154
229
|
: 'occurrence'
|
|
155
230
|
|
|
231
|
+
const hasAnalysis = documentation.analysis_context || (documentation.analysis_sources && documentation.analysis_sources.length > 0)
|
|
232
|
+
|
|
156
233
|
return (
|
|
157
234
|
<div className={className}>
|
|
158
235
|
<div className="space-y-5">
|
|
236
|
+
{/* What was analyzed */}
|
|
237
|
+
{hasAnalysis && (
|
|
238
|
+
<div>
|
|
239
|
+
<Dialog>
|
|
240
|
+
<DialogTrigger asChild>
|
|
241
|
+
<button className="flex items-center gap-2 text-sm font-medium text-[var(--cyan)] hover:text-[var(--cyan)]/80 transition-colors">
|
|
242
|
+
<MagnifyingGlass size={16} />
|
|
243
|
+
What was analyzed
|
|
244
|
+
{documentation.analysis_summary && (
|
|
245
|
+
<span className="text-xs text-muted-foreground font-normal">
|
|
246
|
+
({documentation.analysis_summary.total_sources} source{documentation.analysis_summary.total_sources !== 1 ? 's' : ''})
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
<CaretRight size={12} />
|
|
250
|
+
</button>
|
|
251
|
+
</DialogTrigger>
|
|
252
|
+
<DialogContent className="max-w-xl max-h-[80vh] overflow-y-auto">
|
|
253
|
+
<DialogHeader>
|
|
254
|
+
<DialogTitle>What was analyzed</DialogTitle>
|
|
255
|
+
{documentation.analysis_summary && (
|
|
256
|
+
<DialogDescription className="flex items-center gap-4 text-xs">
|
|
257
|
+
<span>{documentation.analysis_summary.total_sources} source{documentation.analysis_summary.total_sources !== 1 ? 's' : ''}</span>
|
|
258
|
+
{documentation.analysis_summary.video_count > 0 && (
|
|
259
|
+
<span className="flex items-center gap-1">
|
|
260
|
+
<FilmStrip size={12} />
|
|
261
|
+
{documentation.analysis_summary.video_count} video{documentation.analysis_summary.video_count !== 1 ? 's' : ''}
|
|
262
|
+
</span>
|
|
263
|
+
)}
|
|
264
|
+
{documentation.analysis_summary.document_count > 0 && (
|
|
265
|
+
<span className="flex items-center gap-1">
|
|
266
|
+
<FileText size={12} />
|
|
267
|
+
{documentation.analysis_summary.document_count} doc{documentation.analysis_summary.document_count !== 1 ? 's' : ''}
|
|
268
|
+
</span>
|
|
269
|
+
)}
|
|
270
|
+
</DialogDescription>
|
|
271
|
+
)}
|
|
272
|
+
</DialogHeader>
|
|
273
|
+
|
|
274
|
+
{/* Sources Section */}
|
|
275
|
+
{documentation.analysis_sources && documentation.analysis_sources.length > 0 && (
|
|
276
|
+
<div className="space-y-2">
|
|
277
|
+
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Sources</h4>
|
|
278
|
+
<div className="space-y-1.5">
|
|
279
|
+
{documentation.analysis_sources.map((source, i) => (
|
|
280
|
+
<div key={i} className="flex items-center gap-2 text-sm">
|
|
281
|
+
<div className="flex-shrink-0 text-muted-foreground">
|
|
282
|
+
{source.type === 'video' && <FilmStrip size={14} />}
|
|
283
|
+
{(source.type === 'document' || source.type === 'pdf') && <FileText size={14} />}
|
|
284
|
+
{source.type === 'spreadsheet' && <FileText size={14} />}
|
|
285
|
+
{source.type === 'image' && <ImageSquare size={14} />}
|
|
286
|
+
{source.type === 'description' && <TextT size={14} />}
|
|
287
|
+
</div>
|
|
288
|
+
<span className="truncate text-[var(--black)]">{source.name}</span>
|
|
289
|
+
{source.size > 0 && (
|
|
290
|
+
<span className="text-xs text-muted-foreground flex-shrink-0">
|
|
291
|
+
{source.size > 1024 * 1024
|
|
292
|
+
? `${(source.size / 1024 / 1024).toFixed(1)}MB`
|
|
293
|
+
: `${Math.round(source.size / 1024)}KB`}
|
|
294
|
+
</span>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
))}
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
)}
|
|
301
|
+
|
|
302
|
+
{/* Analysis Details Section */}
|
|
303
|
+
{documentation.analysis_context && (
|
|
304
|
+
<div className="border-t border-gray-100 pt-4 space-y-1">
|
|
305
|
+
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-3">Analysis Details</h4>
|
|
306
|
+
<AnalysisContextRenderer content={documentation.analysis_context} />
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</DialogContent>
|
|
310
|
+
</Dialog>
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
|
|
159
314
|
{/* Goal */}
|
|
160
315
|
{documentation.goal && (
|
|
161
316
|
<div>
|
package/src/index.ts
CHANGED
|
@@ -281,6 +281,10 @@ export type { ImpactMetrics, ImpactMetricsFormProps } from './components/impact-
|
|
|
281
281
|
export { ActivityTimeline } from './components/activity-timeline'
|
|
282
282
|
export type { Activity, ActivityTimelineProps } from './components/activity-timeline'
|
|
283
283
|
|
|
284
|
+
// Support Chat Components
|
|
285
|
+
export { SupportChat } from './components/support-chat'
|
|
286
|
+
export type { SupportChatMessage, SupportChatProps } from './components/support-chat'
|
|
287
|
+
|
|
284
288
|
// Workflow Components
|
|
285
289
|
export { WorkflowFlow } from './components/workflow-flow'
|
|
286
290
|
export type { WorkflowFlowProps, N8nWorkflow as WorkflowFlowN8nWorkflow, N8nNode } from './components/workflow-flow'
|
|
@@ -295,7 +299,7 @@ export type {
|
|
|
295
299
|
|
|
296
300
|
// Worker Spec Components
|
|
297
301
|
export { WorkerSpec } from './components/worker-spec'
|
|
298
|
-
export type { WorkerSpecProps, WorkerSpecDocumentation } from './components/worker-spec'
|
|
302
|
+
export type { WorkerSpecProps, WorkerSpecDocumentation, AnalysisSource, AnalysisSummary } from './components/worker-spec'
|
|
299
303
|
|
|
300
304
|
// Flowchart Diagram Components
|
|
301
305
|
export { FlowchartDiagram } from './components/flowchart-diagram'
|