@nextsparkjs/theme-default 0.1.0-beta.1
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/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, type ReactNode } from 'react'
|
|
4
|
+
import { Card } from '@nextsparkjs/core/components/ui/card'
|
|
5
|
+
import { Button } from '@nextsparkjs/core/components/ui/button'
|
|
6
|
+
import { ScrollArea } from '@nextsparkjs/core/components/ui/scroll-area'
|
|
7
|
+
import { MessageInput } from './MessageInput'
|
|
8
|
+
import { MarkdownRenderer } from './MarkdownRenderer'
|
|
9
|
+
import { TypingIndicator } from './TypingIndicator'
|
|
10
|
+
import { ConversationSidebar } from './ConversationSidebar'
|
|
11
|
+
import { createCyId } from '@nextsparkjs/core/lib/testing-utils'
|
|
12
|
+
import { cn } from '@nextsparkjs/core/lib/utils'
|
|
13
|
+
import { Bot, User, Loader2, Trash2, ListTodo, Users, FileText, Sparkles, X } from 'lucide-react'
|
|
14
|
+
import type { ConversationInfo } from '../../lib/hooks/useConversations'
|
|
15
|
+
import type { LucideIcon } from 'lucide-react'
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// TYPES
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Agent badge configuration for multi-agent support
|
|
23
|
+
*/
|
|
24
|
+
export interface AgentBadge {
|
|
25
|
+
label: string
|
|
26
|
+
icon: LucideIcon
|
|
27
|
+
className: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Agent badge registry - maps agent names to their visual representation
|
|
32
|
+
*/
|
|
33
|
+
export type AgentBadgeRegistry = Record<string, AgentBadge>
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Message with optional agent information
|
|
37
|
+
*/
|
|
38
|
+
export interface ChatMessage {
|
|
39
|
+
id: string
|
|
40
|
+
role: 'user' | 'assistant'
|
|
41
|
+
content: string
|
|
42
|
+
timestamp?: number
|
|
43
|
+
/** Agent that handled this message (for multi-agent mode) */
|
|
44
|
+
agentUsed?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sidebar configuration for conversation management
|
|
49
|
+
*/
|
|
50
|
+
export interface SidebarConfig {
|
|
51
|
+
conversations: ConversationInfo[]
|
|
52
|
+
activeSessionId: string | null
|
|
53
|
+
onSelect: (sessionId: string) => void
|
|
54
|
+
onNew: () => Promise<void>
|
|
55
|
+
onRename: (sessionId: string, name: string) => Promise<void>
|
|
56
|
+
onDelete: (sessionId: string) => Promise<void>
|
|
57
|
+
onTogglePin: (sessionId: string) => Promise<void>
|
|
58
|
+
isLoading: boolean
|
|
59
|
+
isCreating: boolean
|
|
60
|
+
canCreateNew: boolean
|
|
61
|
+
conversationCount: number
|
|
62
|
+
maxConversations: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Header configuration
|
|
67
|
+
*/
|
|
68
|
+
export interface HeaderConfig {
|
|
69
|
+
title: string
|
|
70
|
+
subtitle?: string
|
|
71
|
+
icon?: LucideIcon
|
|
72
|
+
iconClassName?: string
|
|
73
|
+
/** Show clear chat button */
|
|
74
|
+
showClearButton?: boolean
|
|
75
|
+
onClear?: () => void
|
|
76
|
+
/** Additional header content (e.g., agent legend) */
|
|
77
|
+
extra?: ReactNode
|
|
78
|
+
/** Mobile action button */
|
|
79
|
+
mobileAction?: ReactNode
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Empty state configuration
|
|
84
|
+
*/
|
|
85
|
+
export interface EmptyStateConfig {
|
|
86
|
+
icon?: LucideIcon
|
|
87
|
+
title: string
|
|
88
|
+
description?: string
|
|
89
|
+
suggestions?: string[]
|
|
90
|
+
action?: ReactNode
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* ChatPanel props
|
|
95
|
+
*/
|
|
96
|
+
export interface ChatPanelProps {
|
|
97
|
+
/** Unique identifier for testing */
|
|
98
|
+
cyPrefix?: string
|
|
99
|
+
/** Messages to display */
|
|
100
|
+
messages: ChatMessage[]
|
|
101
|
+
/** Current input value */
|
|
102
|
+
input: string
|
|
103
|
+
/** Input change handler */
|
|
104
|
+
onInputChange: (value: string) => void
|
|
105
|
+
/** Send message handler */
|
|
106
|
+
onSend: () => void
|
|
107
|
+
/** Loading state (sending message) */
|
|
108
|
+
isLoading?: boolean
|
|
109
|
+
/** Error message */
|
|
110
|
+
error?: string | null
|
|
111
|
+
/** Header configuration */
|
|
112
|
+
header: HeaderConfig
|
|
113
|
+
/** Sidebar configuration (optional - enables conversation management) */
|
|
114
|
+
sidebar?: SidebarConfig
|
|
115
|
+
/** Agent badge registry (optional - enables multi-agent mode) */
|
|
116
|
+
agentBadges?: AgentBadgeRegistry
|
|
117
|
+
/** Show agent legend in header */
|
|
118
|
+
showAgentLegend?: boolean
|
|
119
|
+
/** Empty state when no messages */
|
|
120
|
+
emptyState?: EmptyStateConfig
|
|
121
|
+
/** Empty state when no session selected (sidebar mode) */
|
|
122
|
+
noSessionState?: EmptyStateConfig
|
|
123
|
+
/** Session ID for debugging */
|
|
124
|
+
sessionId?: string | null
|
|
125
|
+
/** Show session debug info in dev mode */
|
|
126
|
+
showSessionDebug?: boolean
|
|
127
|
+
/** Custom max width class */
|
|
128
|
+
maxWidthClass?: string
|
|
129
|
+
/** Component is ready */
|
|
130
|
+
isReady?: boolean
|
|
131
|
+
/** Loading history */
|
|
132
|
+
isLoadingHistory?: boolean
|
|
133
|
+
/** Has active session (for sidebar mode) */
|
|
134
|
+
hasSession?: boolean
|
|
135
|
+
/** Partial content during streaming */
|
|
136
|
+
streamingContent?: string
|
|
137
|
+
/** Whether currently streaming */
|
|
138
|
+
isStreamingMessage?: boolean
|
|
139
|
+
/** Cancel streaming callback */
|
|
140
|
+
onCancelStream?: () => void
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// SUB-COMPONENTS
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Single chat message with optional agent badge
|
|
149
|
+
*/
|
|
150
|
+
function ChatMessageItem({
|
|
151
|
+
message,
|
|
152
|
+
agentBadges,
|
|
153
|
+
cyPrefix = 'chat',
|
|
154
|
+
}: {
|
|
155
|
+
message: ChatMessage
|
|
156
|
+
agentBadges?: AgentBadgeRegistry
|
|
157
|
+
cyPrefix?: string
|
|
158
|
+
}) {
|
|
159
|
+
const isUser = message.role === 'user'
|
|
160
|
+
const agentBadge = !isUser && message.agentUsed && agentBadges
|
|
161
|
+
? agentBadges[message.agentUsed]
|
|
162
|
+
: null
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div
|
|
166
|
+
data-cy={createCyId(cyPrefix, `message-${message.role}`)}
|
|
167
|
+
className={cn(
|
|
168
|
+
'flex w-full gap-3 p-4',
|
|
169
|
+
isUser ? 'flex-row-reverse' : 'flex-row'
|
|
170
|
+
)}
|
|
171
|
+
>
|
|
172
|
+
<div
|
|
173
|
+
className={cn(
|
|
174
|
+
'flex h-8 w-8 shrink-0 items-center justify-center rounded-full border',
|
|
175
|
+
isUser ? 'bg-primary text-primary-foreground' : 'bg-muted'
|
|
176
|
+
)}
|
|
177
|
+
>
|
|
178
|
+
{isUser ? <User className="h-4 w-4" /> : <Bot className="h-4 w-4" />}
|
|
179
|
+
</div>
|
|
180
|
+
<div className={cn('flex max-w-[80%] flex-col gap-1')}>
|
|
181
|
+
{agentBadge && (
|
|
182
|
+
<div className={cn(
|
|
183
|
+
'inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium w-fit',
|
|
184
|
+
agentBadge.className
|
|
185
|
+
)}>
|
|
186
|
+
<agentBadge.icon className="h-3 w-3" />
|
|
187
|
+
{agentBadge.label}
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
<div
|
|
191
|
+
className={cn(
|
|
192
|
+
'rounded-lg px-4 py-2 text-sm',
|
|
193
|
+
isUser
|
|
194
|
+
? 'bg-primary text-primary-foreground'
|
|
195
|
+
: 'bg-muted'
|
|
196
|
+
)}
|
|
197
|
+
>
|
|
198
|
+
<MarkdownRenderer content={message.content} />
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Agent legend display
|
|
207
|
+
*/
|
|
208
|
+
function AgentLegend({ badges }: { badges: AgentBadgeRegistry }) {
|
|
209
|
+
return (
|
|
210
|
+
<div className="mt-3 flex flex-wrap gap-2 text-xs">
|
|
211
|
+
{Object.entries(badges).map(([key, badge]) => (
|
|
212
|
+
<span
|
|
213
|
+
key={key}
|
|
214
|
+
className={cn(
|
|
215
|
+
'px-2 py-1 rounded flex items-center gap-1',
|
|
216
|
+
badge.className
|
|
217
|
+
)}
|
|
218
|
+
>
|
|
219
|
+
<badge.icon className="h-3 w-3" />
|
|
220
|
+
{badge.label}
|
|
221
|
+
</span>
|
|
222
|
+
))}
|
|
223
|
+
</div>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Empty state display
|
|
229
|
+
*/
|
|
230
|
+
function EmptyState({ config, icon: DefaultIcon = Bot }: { config: EmptyStateConfig; icon?: LucideIcon }) {
|
|
231
|
+
const Icon = config.icon || DefaultIcon
|
|
232
|
+
return (
|
|
233
|
+
<div className="flex-1 flex items-center justify-center p-8">
|
|
234
|
+
<div className="text-center max-w-md">
|
|
235
|
+
<Icon className="h-16 w-16 mx-auto mb-4 text-muted-foreground/50" />
|
|
236
|
+
<h2 className="text-xl font-semibold mb-2">{config.title}</h2>
|
|
237
|
+
{config.description && (
|
|
238
|
+
<p className="text-muted-foreground mb-6">{config.description}</p>
|
|
239
|
+
)}
|
|
240
|
+
{config.suggestions && config.suggestions.length > 0 && (
|
|
241
|
+
<div className="space-y-2 text-sm text-left">
|
|
242
|
+
<p className="text-muted-foreground">Try asking:</p>
|
|
243
|
+
{config.suggestions.map((suggestion, i) => (
|
|
244
|
+
<p key={i} className="bg-muted p-2 rounded">
|
|
245
|
+
"{suggestion}"
|
|
246
|
+
</p>
|
|
247
|
+
))}
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
{config.action}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Loading state display
|
|
258
|
+
*/
|
|
259
|
+
function LoadingState({ message = 'Loading...' }: { message?: string }) {
|
|
260
|
+
return (
|
|
261
|
+
<div className="flex-1 flex items-center justify-center">
|
|
262
|
+
<div className="text-center">
|
|
263
|
+
<Loader2 className="h-8 w-8 animate-spin mx-auto text-muted-foreground" />
|
|
264
|
+
<p className="mt-2 text-sm text-muted-foreground">{message}</p>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// MAIN COMPONENT
|
|
272
|
+
// ============================================================================
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* ChatPanel - Unified chat interface component
|
|
276
|
+
*
|
|
277
|
+
* A flexible chat panel that supports:
|
|
278
|
+
* - Optional sidebar for conversation management
|
|
279
|
+
* - Optional multi-agent badges
|
|
280
|
+
* - Customizable header, empty states, and styling
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Simple chat (like orchestrator)
|
|
284
|
+
* <ChatPanel
|
|
285
|
+
* messages={messages}
|
|
286
|
+
* input={input}
|
|
287
|
+
* onInputChange={setInput}
|
|
288
|
+
* onSend={sendMessage}
|
|
289
|
+
* isLoading={isLoading}
|
|
290
|
+
* header={{ title: 'AI Chat', subtitle: 'Ask me anything' }}
|
|
291
|
+
* />
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* // With sidebar (like single-agent)
|
|
295
|
+
* <ChatPanel
|
|
296
|
+
* messages={messages}
|
|
297
|
+
* input={input}
|
|
298
|
+
* onInputChange={setInput}
|
|
299
|
+
* onSend={sendMessage}
|
|
300
|
+
* sidebar={sidebarConfig}
|
|
301
|
+
* header={{ title: 'AI Assistant' }}
|
|
302
|
+
* />
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* // With multi-agent badges
|
|
306
|
+
* <ChatPanel
|
|
307
|
+
* messages={messages}
|
|
308
|
+
* agentBadges={AGENT_BADGES}
|
|
309
|
+
* showAgentLegend
|
|
310
|
+
* header={{ title: 'Orchestrator' }}
|
|
311
|
+
* />
|
|
312
|
+
*/
|
|
313
|
+
export function ChatPanel({
|
|
314
|
+
cyPrefix = 'chat',
|
|
315
|
+
messages,
|
|
316
|
+
input,
|
|
317
|
+
onInputChange,
|
|
318
|
+
onSend,
|
|
319
|
+
isLoading = false,
|
|
320
|
+
error,
|
|
321
|
+
header,
|
|
322
|
+
sidebar,
|
|
323
|
+
agentBadges,
|
|
324
|
+
showAgentLegend = false,
|
|
325
|
+
emptyState,
|
|
326
|
+
noSessionState,
|
|
327
|
+
sessionId,
|
|
328
|
+
showSessionDebug = false,
|
|
329
|
+
maxWidthClass = 'max-w-4xl',
|
|
330
|
+
isReady = true,
|
|
331
|
+
isLoadingHistory = false,
|
|
332
|
+
hasSession = true,
|
|
333
|
+
streamingContent,
|
|
334
|
+
isStreamingMessage = false,
|
|
335
|
+
onCancelStream,
|
|
336
|
+
}: ChatPanelProps) {
|
|
337
|
+
const scrollRef = useRef<HTMLDivElement>(null)
|
|
338
|
+
const HeaderIcon = header.icon || Bot
|
|
339
|
+
|
|
340
|
+
// Auto-scroll on new messages and streaming content
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
if (scrollRef.current) {
|
|
343
|
+
scrollRef.current.scrollIntoView({ behavior: 'smooth' })
|
|
344
|
+
}
|
|
345
|
+
}, [messages, isLoading, streamingContent])
|
|
346
|
+
|
|
347
|
+
// Determine layout class based on sidebar presence
|
|
348
|
+
const hasSidebar = !!sidebar
|
|
349
|
+
const layoutClass = hasSidebar ? 'max-w-6xl' : maxWidthClass
|
|
350
|
+
|
|
351
|
+
return (
|
|
352
|
+
<div className="h-[calc(100vh-4rem)] p-4 md:p-6 lg:p-8">
|
|
353
|
+
<Card
|
|
354
|
+
data-cy={createCyId(cyPrefix, 'panel')}
|
|
355
|
+
className={cn(
|
|
356
|
+
'flex h-full mx-auto overflow-hidden shadow-lg',
|
|
357
|
+
layoutClass
|
|
358
|
+
)}
|
|
359
|
+
>
|
|
360
|
+
{/* Optional Sidebar */}
|
|
361
|
+
{hasSidebar && (
|
|
362
|
+
<div className="w-64 flex-shrink-0 hidden md:block">
|
|
363
|
+
<ConversationSidebar
|
|
364
|
+
conversations={sidebar.conversations}
|
|
365
|
+
activeSessionId={sidebar.activeSessionId}
|
|
366
|
+
onSelect={sidebar.onSelect}
|
|
367
|
+
onNew={sidebar.onNew}
|
|
368
|
+
onRename={sidebar.onRename}
|
|
369
|
+
onDelete={sidebar.onDelete}
|
|
370
|
+
onTogglePin={sidebar.onTogglePin}
|
|
371
|
+
isLoading={sidebar.isLoading}
|
|
372
|
+
isCreating={sidebar.isCreating}
|
|
373
|
+
canCreateNew={sidebar.canCreateNew}
|
|
374
|
+
conversationCount={sidebar.conversationCount}
|
|
375
|
+
maxConversations={sidebar.maxConversations}
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
|
|
380
|
+
{/* Chat Area */}
|
|
381
|
+
<div className="flex-1 flex flex-col min-w-0">
|
|
382
|
+
{/* Header */}
|
|
383
|
+
<div className="p-4 border-b bg-muted/50">
|
|
384
|
+
<div className="flex items-center justify-between">
|
|
385
|
+
<div className="flex items-center gap-3">
|
|
386
|
+
<div className={cn(
|
|
387
|
+
'flex h-10 w-10 items-center justify-center rounded-full',
|
|
388
|
+
header.iconClassName || 'bg-primary/10'
|
|
389
|
+
)}>
|
|
390
|
+
<HeaderIcon className={cn(
|
|
391
|
+
'h-5 w-5',
|
|
392
|
+
header.iconClassName ? '' : 'text-primary'
|
|
393
|
+
)} />
|
|
394
|
+
</div>
|
|
395
|
+
<div>
|
|
396
|
+
<h1 className="text-lg font-semibold flex items-center gap-2">
|
|
397
|
+
{header.title}
|
|
398
|
+
</h1>
|
|
399
|
+
{header.subtitle && (
|
|
400
|
+
<p className="text-sm text-muted-foreground">
|
|
401
|
+
{header.subtitle}
|
|
402
|
+
</p>
|
|
403
|
+
)}
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
<div className="flex items-center gap-2">
|
|
408
|
+
{/* Clear button */}
|
|
409
|
+
{header.showClearButton && header.onClear && (
|
|
410
|
+
<Button
|
|
411
|
+
data-cy={createCyId(cyPrefix, 'clear-btn')}
|
|
412
|
+
variant="ghost"
|
|
413
|
+
size="sm"
|
|
414
|
+
onClick={header.onClear}
|
|
415
|
+
disabled={messages.length === 0}
|
|
416
|
+
title="Clear chat"
|
|
417
|
+
>
|
|
418
|
+
<Trash2 className="h-4 w-4" />
|
|
419
|
+
</Button>
|
|
420
|
+
)}
|
|
421
|
+
|
|
422
|
+
{/* Mobile action (e.g., new conversation button) */}
|
|
423
|
+
{header.mobileAction && (
|
|
424
|
+
<div className="md:hidden">
|
|
425
|
+
{header.mobileAction}
|
|
426
|
+
</div>
|
|
427
|
+
)}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
431
|
+
{/* Agent Legend */}
|
|
432
|
+
{showAgentLegend && agentBadges && (
|
|
433
|
+
<AgentLegend badges={agentBadges} />
|
|
434
|
+
)}
|
|
435
|
+
|
|
436
|
+
{/* Extra header content */}
|
|
437
|
+
{header.extra}
|
|
438
|
+
|
|
439
|
+
{/* Session Debug */}
|
|
440
|
+
{showSessionDebug && sessionId && process.env.NODE_ENV === 'development' && (
|
|
441
|
+
<div className="mt-2 text-xs text-muted-foreground">
|
|
442
|
+
Session: <code className="bg-muted px-1 rounded">
|
|
443
|
+
{sessionId.length > 16 ? `${sessionId.slice(0, 8)}...` : sessionId}
|
|
444
|
+
</code>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
{/* Content Area */}
|
|
450
|
+
{!isReady ? (
|
|
451
|
+
<LoadingState />
|
|
452
|
+
) : hasSidebar && !hasSession ? (
|
|
453
|
+
/* No session selected (sidebar mode) */
|
|
454
|
+
noSessionState ? (
|
|
455
|
+
<EmptyState config={noSessionState} />
|
|
456
|
+
) : (
|
|
457
|
+
<LoadingState message="Select a conversation" />
|
|
458
|
+
)
|
|
459
|
+
) : isLoadingHistory && messages.length === 0 ? (
|
|
460
|
+
<LoadingState message="Loading conversation..." />
|
|
461
|
+
) : messages.length === 0 && !isLoading ? (
|
|
462
|
+
/* Empty state */
|
|
463
|
+
emptyState ? (
|
|
464
|
+
<EmptyState config={emptyState} />
|
|
465
|
+
) : (
|
|
466
|
+
<div className="flex-1 flex items-center justify-center p-8">
|
|
467
|
+
<p className="text-muted-foreground">Start a conversation</p>
|
|
468
|
+
</div>
|
|
469
|
+
)
|
|
470
|
+
) : (
|
|
471
|
+
/* Messages */
|
|
472
|
+
<ScrollArea
|
|
473
|
+
data-cy={createCyId(cyPrefix, 'message-list')}
|
|
474
|
+
className="flex-1 p-4"
|
|
475
|
+
>
|
|
476
|
+
<div className="flex flex-col gap-4">
|
|
477
|
+
{error && (
|
|
478
|
+
<div
|
|
479
|
+
data-cy={createCyId(cyPrefix, 'error-message')}
|
|
480
|
+
className="p-4 mb-2 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-lg"
|
|
481
|
+
>
|
|
482
|
+
{error}
|
|
483
|
+
</div>
|
|
484
|
+
)}
|
|
485
|
+
{messages.map((message) => (
|
|
486
|
+
<ChatMessageItem
|
|
487
|
+
key={message.id}
|
|
488
|
+
message={message}
|
|
489
|
+
agentBadges={agentBadges}
|
|
490
|
+
cyPrefix={cyPrefix}
|
|
491
|
+
/>
|
|
492
|
+
))}
|
|
493
|
+
{isLoading && (
|
|
494
|
+
<div className="flex items-center gap-2 p-4">
|
|
495
|
+
{isStreamingMessage && streamingContent ? (
|
|
496
|
+
// Show streaming content with blinking cursor
|
|
497
|
+
<div className="flex w-full gap-3">
|
|
498
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border bg-muted">
|
|
499
|
+
<Bot className="h-4 w-4" />
|
|
500
|
+
</div>
|
|
501
|
+
<div className="flex max-w-[80%] flex-col gap-1">
|
|
502
|
+
<div className="rounded-lg px-4 py-2 text-sm bg-muted">
|
|
503
|
+
<MarkdownRenderer content={streamingContent} />
|
|
504
|
+
<span className="inline-block w-2 h-4 ml-1 bg-foreground animate-pulse">|</span>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
) : (
|
|
509
|
+
<TypingIndicator />
|
|
510
|
+
)}
|
|
511
|
+
</div>
|
|
512
|
+
)}
|
|
513
|
+
{/* Cancel button during streaming */}
|
|
514
|
+
{isStreamingMessage && onCancelStream && (
|
|
515
|
+
<div className="flex justify-center py-2">
|
|
516
|
+
<Button
|
|
517
|
+
variant="outline"
|
|
518
|
+
size="sm"
|
|
519
|
+
onClick={onCancelStream}
|
|
520
|
+
data-cy="chat-cancel-button"
|
|
521
|
+
>
|
|
522
|
+
<X className="h-4 w-4 mr-1" />
|
|
523
|
+
Stop generating
|
|
524
|
+
</Button>
|
|
525
|
+
</div>
|
|
526
|
+
)}
|
|
527
|
+
<div ref={scrollRef} />
|
|
528
|
+
</div>
|
|
529
|
+
</ScrollArea>
|
|
530
|
+
)}
|
|
531
|
+
|
|
532
|
+
{/* Input - always show if ready and has session */}
|
|
533
|
+
{isReady && (hasSidebar ? hasSession : true) && (
|
|
534
|
+
<MessageInput
|
|
535
|
+
value={input}
|
|
536
|
+
onChange={onInputChange}
|
|
537
|
+
onSend={onSend}
|
|
538
|
+
isLoading={isLoading}
|
|
539
|
+
/>
|
|
540
|
+
)}
|
|
541
|
+
</div>
|
|
542
|
+
</Card>
|
|
543
|
+
</div>
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// ============================================================================
|
|
548
|
+
// PRESETS
|
|
549
|
+
// ============================================================================
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Default agent badges for orchestrator mode
|
|
553
|
+
*/
|
|
554
|
+
export const ORCHESTRATOR_AGENT_BADGES: AgentBadgeRegistry = {
|
|
555
|
+
task: {
|
|
556
|
+
label: 'Task Agent',
|
|
557
|
+
icon: ListTodo,
|
|
558
|
+
className: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'
|
|
559
|
+
},
|
|
560
|
+
customer: {
|
|
561
|
+
label: 'Customer Agent',
|
|
562
|
+
icon: Users,
|
|
563
|
+
className: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
|
|
564
|
+
},
|
|
565
|
+
page: {
|
|
566
|
+
label: 'Page Agent',
|
|
567
|
+
icon: FileText,
|
|
568
|
+
className: 'bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400'
|
|
569
|
+
},
|
|
570
|
+
orchestrator: {
|
|
571
|
+
label: 'Orchestrator',
|
|
572
|
+
icon: Sparkles,
|
|
573
|
+
className: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400'
|
|
574
|
+
},
|
|
575
|
+
}
|