@vibe-forge/client 0.5.0 → 0.7.0

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 (134) hide show
  1. package/AGENTS.md +37 -0
  2. package/dist/assets/{arc-C4ymrcSQ.js → arc-DidNmF3v.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CeB7-kgP.js → blockDiagram-c4efeb88-CDnN61xv.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-C935Im8S.js → c4Diagram-c83219d4-B3k8deko.js} +1 -1
  5. package/dist/assets/channel-Cde3vJGp.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-B9IV13KI.js → classDiagram-beda092f-BdANZ7yV.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-CXF_K4fE.js → classDiagram-v2-2358418a-ow-iRMSq.js} +1 -1
  8. package/dist/assets/clone-LUH_zyxR.js +1 -0
  9. package/dist/assets/{createText-1719965b-DwX8iC5F.js → createText-1719965b-Wz6GZTHU.js} +1 -1
  10. package/dist/assets/{edges-96097737-9P1uH1RE.js → edges-96097737-D0RiugNT.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-ixeGTFvg.js → erDiagram-0228fc6a-CthowAHZ.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-G1gSTTBI.js → flowDb-c6c81e3f-C-joVb9m.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-CzrG99nD.js → flowDiagram-50d868cf-Bx-hAyRl.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-CVxV-0Ew.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-sFCoysWa.js → flowchart-elk-definition-6af322e1-CpGiH0RQ.js} +1 -1
  16. package/dist/assets/{ganttDiagram-a2739b55-Ccsk_Lru.js → ganttDiagram-a2739b55-B2rBCB_P.js} +1 -1
  17. package/dist/assets/{gitGraphDiagram-82fe8481-CwathJ6H.js → gitGraphDiagram-82fe8481-BH6XyEeG.js} +1 -1
  18. package/dist/assets/{graph-DRCU-8Rz.js → graph-De4BWXYq.js} +1 -1
  19. package/dist/assets/{index-5325376f-Bq-fg2i_.js → index-5325376f-DciqDx2s.js} +1 -1
  20. package/dist/assets/index-IW7UIMcY.js +545 -0
  21. package/dist/assets/{index-CHMuZ5-1.css → index-fcJ9v94I.css} +1 -1
  22. package/dist/assets/{infoDiagram-8eee0895-JBcUkJ6T.js → infoDiagram-8eee0895-CiV1aI2C.js} +1 -1
  23. package/dist/assets/{journeyDiagram-c64418c1-DsdQU-R8.js → journeyDiagram-c64418c1-DeClao2V.js} +1 -1
  24. package/dist/assets/{layout-s0slG1OL.js → layout-B5GMBoJz.js} +1 -1
  25. package/dist/assets/{line-CymFqgW6.js → line-CgBpZ2AR.js} +1 -1
  26. package/dist/assets/{linear-lDQVZ6aQ.js → linear-Cd0IpBll.js} +1 -1
  27. package/dist/assets/{mermaid.core-Cmlqga_E.js → mermaid.core-CZnO8AvM.js} +4 -4
  28. package/dist/assets/{mindmap-definition-8da855dc-CqqTDJn_.js → mindmap-definition-8da855dc-0XmBTp3E.js} +1 -1
  29. package/dist/assets/{pieDiagram-a8764435-BL2Ajx7Z.js → pieDiagram-a8764435-BNAROg0P.js} +1 -1
  30. package/dist/assets/{quadrantDiagram-1e28029f-ClL_3ASt.js → quadrantDiagram-1e28029f-Df_gmUZ3.js} +1 -1
  31. package/dist/assets/{requirementDiagram-08caed73-CB1RgE3K.js → requirementDiagram-08caed73-CJucWU8H.js} +1 -1
  32. package/dist/assets/{sankeyDiagram-a04cb91d-tgleEYiD.js → sankeyDiagram-a04cb91d-CHiMn2x-.js} +1 -1
  33. package/dist/assets/{sequenceDiagram-c5b8d532-DlatQT5R.js → sequenceDiagram-c5b8d532-C9TpmcH3.js} +1 -1
  34. package/dist/assets/{stateDiagram-1ecb1508-B--MLqRs.js → stateDiagram-1ecb1508-Cvptb-Zj.js} +1 -1
  35. package/dist/assets/{stateDiagram-v2-c2b004d7-CRMZ6Dpx.js → stateDiagram-v2-c2b004d7-nRJ7hzpx.js} +1 -1
  36. package/dist/assets/{styles-b4e223ce-CPiYHfUz.js → styles-b4e223ce-iTClb1YD.js} +1 -1
  37. package/dist/assets/{styles-ca3715f6-B9UKPAzX.js → styles-ca3715f6-BstDoS6h.js} +1 -1
  38. package/dist/assets/{styles-d45a18b0-BC1Ak1So.js → styles-d45a18b0-Ci8lW61F.js} +1 -1
  39. package/dist/assets/{svgDrawCommon-b86b1483-DV8R0g-n.js → svgDrawCommon-b86b1483-DxM1HDTD.js} +1 -1
  40. package/dist/assets/{timeline-definition-faaaa080-CiqGS5DC.js → timeline-definition-faaaa080-bmNqY66u.js} +1 -1
  41. package/dist/assets/{xychartDiagram-f5964ef8-h6VSD3GE.js → xychartDiagram-f5964ef8-0OjuuJfh.js} +1 -1
  42. package/dist/index.html +2 -2
  43. package/package.json +10 -8
  44. package/src/App.tsx +20 -168
  45. package/src/api/base.ts +116 -7
  46. package/src/api.ts +3 -1
  47. package/src/components/ArchiveView.tsx +5 -5
  48. package/src/components/ConfigView.tsx +3 -3
  49. package/src/components/{AutomationView → automation-view}/index.tsx +10 -9
  50. package/src/components/{BenchmarkView → benchmark-view}/index.tsx +5 -4
  51. package/src/components/chat/ChatHeader.tsx +6 -6
  52. package/src/components/chat/ChatHistoryView.tsx +64 -16
  53. package/src/components/chat/ChatTimelineView.tsx +3 -3
  54. package/src/components/chat/CurrentTodoList.scss +56 -27
  55. package/src/components/chat/{Sender → sender}/Sender.scss +201 -71
  56. package/src/components/chat/{Sender → sender}/Sender.tsx +104 -42
  57. package/src/components/chat/tools/core/ToolGroup.tsx +1 -1
  58. package/src/components/config/ConfigSectionForm.tsx +1 -1
  59. package/src/components/layout/AppShell.scss +19 -0
  60. package/src/components/layout/AppShell.tsx +45 -0
  61. package/src/hooks/chat/model-selector.ts +150 -0
  62. package/src/hooks/chat/use-chat-adapter.ts +20 -8
  63. package/src/hooks/chat/use-chat-models.tsx +79 -74
  64. package/src/hooks/chat/use-chat-permission-mode.ts +14 -10
  65. package/src/hooks/chat/use-chat-session-actions.ts +19 -11
  66. package/src/hooks/chat/use-chat-session-messages.ts +46 -6
  67. package/src/hooks/chat/use-chat-session.ts +42 -1
  68. package/src/hooks/use-app-preferences.ts +41 -0
  69. package/src/hooks/use-session-subscription.ts +101 -0
  70. package/src/hooks/use-sidebar-navigation.ts +35 -0
  71. package/src/resources/locales/en.json +6 -0
  72. package/src/resources/locales/zh.json +6 -0
  73. package/src/routes/AppRoutes.tsx +22 -0
  74. package/src/routes/ArchiveRoute.tsx +5 -0
  75. package/src/routes/AutomationRoute.tsx +5 -0
  76. package/src/routes/BenchmarkRoute.tsx +5 -0
  77. package/src/{components/Chat.scss → routes/ChatRoute.scss} +35 -0
  78. package/src/{components/Chat.tsx → routes/ChatRoute.tsx} +54 -28
  79. package/src/routes/ConfigRoute.tsx +5 -0
  80. package/src/routes/KnowledgeRoute.tsx +5 -0
  81. package/dist/assets/channel-84s1ACzD.js +0 -1
  82. package/dist/assets/clone-B2E8tddE.js +0 -1
  83. package/dist/assets/flowDiagram-v2-4f6560a1-CJfJYbME.js +0 -1
  84. package/dist/assets/index-cGZvDhhU.js +0 -542
  85. /package/src/components/{AutomationView → automation-view}/RuleFormPanel.scss +0 -0
  86. /package/src/components/{AutomationView → automation-view}/RuleFormPanel.tsx +0 -0
  87. /package/src/components/{AutomationView → automation-view}/RuleSidebar.scss +0 -0
  88. /package/src/components/{AutomationView → automation-view}/RuleSidebar.tsx +0 -0
  89. /package/src/components/{AutomationView → automation-view}/RunHistoryPanel.scss +0 -0
  90. /package/src/components/{AutomationView → automation-view}/RunHistoryPanel.tsx +0 -0
  91. /package/src/components/{AutomationView → automation-view}/TaskList.scss +0 -0
  92. /package/src/components/{AutomationView → automation-view}/TaskList.tsx +0 -0
  93. /package/src/components/{AutomationView → automation-view}/TriggerList.scss +0 -0
  94. /package/src/components/{AutomationView → automation-view}/TriggerList.tsx +0 -0
  95. /package/src/components/{AutomationView/AutomationView.scss → automation-view/index.scss} +0 -0
  96. /package/src/components/{AutomationView → automation-view}/types.ts +0 -0
  97. /package/src/components/{BenchmarkView → benchmark-view}/BenchmarkCasePanel.scss +0 -0
  98. /package/src/components/{BenchmarkView → benchmark-view}/BenchmarkCasePanel.tsx +0 -0
  99. /package/src/components/{BenchmarkView → benchmark-view}/BenchmarkSidebar.scss +0 -0
  100. /package/src/components/{BenchmarkView → benchmark-view}/BenchmarkSidebar.tsx +0 -0
  101. /package/src/components/{BenchmarkView → benchmark-view}/BenchmarkView.scss +0 -0
  102. /package/src/components/{BenchmarkView → benchmark-view}/types.ts +0 -0
  103. /package/src/components/{BenchmarkView → benchmark-view}/utils.ts +0 -0
  104. /package/src/components/chat/{Messages → messages}/MessageFooter.tsx +0 -0
  105. /package/src/components/chat/{Messages → messages}/MessageItem.scss +0 -0
  106. /package/src/components/chat/{Messages → messages}/MessageItem.tsx +0 -0
  107. /package/src/components/chat/{Messages → messages}/message-utils.ts +0 -0
  108. /package/src/components/chat/{Sender → sender}/CompletionMenu.scss +0 -0
  109. /package/src/components/chat/{Sender → sender}/CompletionMenu.tsx +0 -0
  110. /package/src/components/chat/{Sender → sender}/ThinkingStatus.scss +0 -0
  111. /package/src/components/chat/{Sender → sender}/ThinkingStatus.tsx +0 -0
  112. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/EventList.scss +0 -0
  113. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/EventList.tsx +0 -0
  114. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/gantt.ts +0 -0
  115. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/git-graph.ts +0 -0
  116. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/index.scss +0 -0
  117. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/index.tsx +0 -0
  118. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/mermaid.ts +0 -0
  119. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/types.ts +0 -0
  120. /package/src/components/chat/{SessionTimelinePanel → session-timeline-panel}/utils.ts +0 -0
  121. /package/src/components/config/{recordEditors → record-editors}/BooleanRecordEditor.scss +0 -0
  122. /package/src/components/config/{recordEditors → record-editors}/BooleanRecordEditor.tsx +0 -0
  123. /package/src/components/config/{recordEditors → record-editors}/ChannelRecordEditor.scss +0 -0
  124. /package/src/components/config/{recordEditors → record-editors}/ChannelRecordEditor.tsx +0 -0
  125. /package/src/components/config/{recordEditors → record-editors}/KeyValueEditor.scss +0 -0
  126. /package/src/components/config/{recordEditors → record-editors}/KeyValueEditor.tsx +0 -0
  127. /package/src/components/config/{recordEditors → record-editors}/McpServersRecordEditor.scss +0 -0
  128. /package/src/components/config/{recordEditors → record-editors}/McpServersRecordEditor.tsx +0 -0
  129. /package/src/components/config/{recordEditors → record-editors}/ModelServicesRecordEditor.scss +0 -0
  130. /package/src/components/config/{recordEditors → record-editors}/ModelServicesRecordEditor.tsx +0 -0
  131. /package/src/components/config/{recordEditors → record-editors}/RecordEditors.scss +0 -0
  132. /package/src/components/config/{recordEditors → record-editors}/RecordJsonEditor.scss +0 -0
  133. /package/src/components/config/{recordEditors → record-editors}/RecordJsonEditor.tsx +0 -0
  134. /package/src/components/config/{recordEditors → record-editors}/index.tsx +0 -0
@@ -0,0 +1,101 @@
1
+ import { useEffect } from 'react'
2
+ import { useSWRConfig } from 'swr'
3
+
4
+ import type { Session, WSEvent } from '@vibe-forge/core'
5
+
6
+ import { createSocket } from '#~/ws.js'
7
+
8
+ interface SessionListResponse {
9
+ sessions: Session[]
10
+ }
11
+ type SessionUpdate = Session | { id: string; isDeleted: boolean }
12
+
13
+ const sortSessions = (sessions: Session[]) => {
14
+ return [...sessions].sort((a, b) => {
15
+ const starredDelta = Number(b.isStarred === true) - Number(a.isStarred === true)
16
+ if (starredDelta !== 0) return starredDelta
17
+ return (b.createdAt ?? 0) - (a.createdAt ?? 0)
18
+ })
19
+ }
20
+
21
+ const mergeSessionList = (
22
+ prev: SessionListResponse | undefined,
23
+ updatedSession: SessionUpdate,
24
+ filter: 'active' | 'archived'
25
+ ) => {
26
+ if (prev?.sessions == null) return prev
27
+
28
+ if ('isDeleted' in updatedSession && updatedSession.isDeleted) {
29
+ return {
30
+ ...prev,
31
+ sessions: prev.sessions.filter((session) => session.id !== updatedSession.id)
32
+ }
33
+ }
34
+
35
+ const shouldInclude = filter === 'archived'
36
+ ? updatedSession.isArchived === true
37
+ : updatedSession.isArchived !== true
38
+ const existing = prev.sessions.find((session) => session.id === updatedSession.id)
39
+
40
+ if (!shouldInclude) {
41
+ return {
42
+ ...prev,
43
+ sessions: prev.sessions.filter((session) => session.id !== updatedSession.id)
44
+ }
45
+ }
46
+
47
+ const nextSessions = existing
48
+ ? prev.sessions.map((session) => session.id === updatedSession.id ? { ...session, ...updatedSession } : session)
49
+ : [updatedSession, ...prev.sessions]
50
+
51
+ return {
52
+ ...prev,
53
+ sessions: sortSessions(nextSessions)
54
+ }
55
+ }
56
+
57
+ export function useSessionSubscription() {
58
+ const { mutate } = useSWRConfig()
59
+
60
+ useEffect(() => {
61
+ let disposed = false
62
+ let socket: WebSocket | undefined
63
+ let reconnectTimer: ReturnType<typeof setTimeout> | undefined
64
+
65
+ const connect = () => {
66
+ if (disposed) return
67
+
68
+ socket = createSocket({
69
+ onMessage: (data: WSEvent) => {
70
+ if (data.type !== 'session_updated') return
71
+ const updatedSession = data.session as SessionUpdate
72
+
73
+ void mutate('/api/sessions', (prev: SessionListResponse | undefined) => {
74
+ return mergeSessionList(prev, updatedSession, 'active')
75
+ }, false)
76
+
77
+ void mutate('/api/sessions/archived', (prev: SessionListResponse | undefined) => {
78
+ return mergeSessionList(prev, updatedSession, 'archived')
79
+ }, false)
80
+ },
81
+ onClose: () => {
82
+ if (disposed) return
83
+ reconnectTimer = setTimeout(connect, 1000)
84
+ },
85
+ onError: () => {
86
+ socket?.close()
87
+ }
88
+ }, { subscribe: 'sessions' })
89
+ }
90
+
91
+ connect()
92
+
93
+ return () => {
94
+ disposed = true
95
+ if (reconnectTimer) {
96
+ clearTimeout(reconnectTimer)
97
+ }
98
+ socket?.close()
99
+ }
100
+ }, [mutate])
101
+ }
@@ -0,0 +1,35 @@
1
+ import { useAtomValue } from 'jotai'
2
+ import { useCallback } from 'react'
3
+ import { matchPath, useLocation, useNavigate } from 'react-router-dom'
4
+
5
+ import type { Session } from '@vibe-forge/core'
6
+
7
+ import { sidebarWidthAtom } from '#~/store'
8
+
9
+ const SESSION_ROUTE_PATTERN = '/session/:sessionId'
10
+
11
+ export function useSidebarNavigation() {
12
+ const navigate = useNavigate()
13
+ const location = useLocation()
14
+ const sidebarWidth = useAtomValue(sidebarWidthAtom)
15
+ const sessionMatch = matchPath({ path: SESSION_ROUTE_PATTERN, end: true }, location.pathname)
16
+ const activeSessionId = sessionMatch?.params.sessionId
17
+ const showSidebar = location.pathname === '/' || activeSessionId != null
18
+
19
+ const handleSelectSession = useCallback((session: Session, _isNew?: boolean) => {
20
+ void navigate(session.id === '' ? '/' : `/session/${session.id}`)
21
+ }, [navigate])
22
+
23
+ const handleDeletedSession = useCallback((deletedId: string, nextId?: string) => {
24
+ if (activeSessionId !== deletedId) return
25
+ void navigate(nextId ? `/session/${nextId}` : '/')
26
+ }, [activeSessionId, navigate])
27
+
28
+ return {
29
+ activeSessionId,
30
+ handleDeletedSession,
31
+ handleSelectSession,
32
+ showSidebar,
33
+ sidebarWidth
34
+ }
35
+ }
@@ -306,11 +306,17 @@
306
306
  "modelSearchPlaceholder": "Search models or services",
307
307
  "modelUnavailable": "No models available",
308
308
  "modelConfigRequired": "Add a model service in config before starting a session",
309
+ "connectionErrorTitle": "Connection error",
310
+ "connectionError": "WebSocket connection failed. Check the server and try again.",
311
+ "connectionClosed": "WebSocket connection closed. Try reconnecting.",
312
+ "retryConnection": "Retry",
309
313
  "imageTooLarge": "Image must be smaller than 5MB",
310
314
  "imageReadFailed": "Failed to read image",
311
315
  "imageNotSupportedInInteraction": "Images are not supported for this interaction",
312
316
  "modelGroupRecommended": "Recommended Models",
313
317
  "availableTools": "Available Tools",
318
+ "toolGroupChromeDevtools": "ChromeDevtools",
319
+ "toolGroupSystem": "System",
314
320
  "toolsCount": "{{count}} tools",
315
321
  "usedTools": "Used {{count}} tools",
316
322
  "viewInitInfo": "Double click to view initialization info",
@@ -307,11 +307,17 @@
307
307
  "modelSearchPlaceholder": "搜索模型或服务",
308
308
  "modelUnavailable": "暂无可用模型",
309
309
  "modelConfigRequired": "请先在配置中添加模型服务后再开始会话",
310
+ "connectionErrorTitle": "连接异常",
311
+ "connectionError": "WebSocket 连接失败,请检查服务状态后重试",
312
+ "connectionClosed": "WebSocket 连接已关闭,请重试",
313
+ "retryConnection": "重试连接",
310
314
  "imageTooLarge": "图片大小不能超过 5MB",
311
315
  "imageReadFailed": "图片读取失败",
312
316
  "imageNotSupportedInInteraction": "当前交互不支持图片",
313
317
  "modelGroupRecommended": "推荐模型",
314
318
  "availableTools": "可用工具",
319
+ "toolGroupChromeDevtools": "ChromeDevtools",
320
+ "toolGroupSystem": "系统",
315
321
  "toolsCount": "{{count}} 个工具",
316
322
  "usedTools": "使用了 {{count}} 个工具",
317
323
  "viewInitInfo": "双击查看初始化信息",
@@ -0,0 +1,22 @@
1
+ import { Route, Routes } from 'react-router-dom'
2
+
3
+ import { ArchiveRoute } from '#~/routes/ArchiveRoute'
4
+ import { AutomationRoute } from '#~/routes/AutomationRoute'
5
+ import { BenchmarkRoute } from '#~/routes/BenchmarkRoute'
6
+ import { ChatRoute } from '#~/routes/ChatRoute'
7
+ import { ConfigRoute } from '#~/routes/ConfigRoute'
8
+ import { KnowledgeRoute } from '#~/routes/KnowledgeRoute'
9
+
10
+ export function AppRoutes() {
11
+ return (
12
+ <Routes>
13
+ <Route path='/' element={<ChatRoute />} />
14
+ <Route path='/session/:sessionId' element={<ChatRoute />} />
15
+ <Route path='/archive' element={<ArchiveRoute />} />
16
+ <Route path='/benchmark' element={<BenchmarkRoute />} />
17
+ <Route path='/automation' element={<AutomationRoute />} />
18
+ <Route path='/knowledge' element={<KnowledgeRoute />} />
19
+ <Route path='/config' element={<ConfigRoute />} />
20
+ </Routes>
21
+ )
22
+ }
@@ -0,0 +1,5 @@
1
+ import { ArchiveView } from '#~/components/ArchiveView'
2
+
3
+ export function ArchiveRoute() {
4
+ return <ArchiveView />
5
+ }
@@ -0,0 +1,5 @@
1
+ import { AutomationView } from '#~/components/automation-view'
2
+
3
+ export function AutomationRoute() {
4
+ return <AutomationView />
5
+ }
@@ -0,0 +1,5 @@
1
+ import { BenchmarkView } from '#~/components/benchmark-view'
2
+
3
+ export function BenchmarkRoute() {
4
+ return <BenchmarkView />
5
+ }
@@ -43,6 +43,33 @@
43
43
  }
44
44
  }
45
45
 
46
+ .chat-messages-content {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 12px;
50
+ min-height: 100%;
51
+ }
52
+
53
+ .chat-pending-session-banner {
54
+ align-self: center;
55
+ display: inline-flex;
56
+ align-items: center;
57
+ gap: 8px;
58
+ margin: 0 auto 4px;
59
+ padding: 8px 14px;
60
+ border: 1px solid rgba(59, 130, 246, .18);
61
+ border-radius: 999px;
62
+ background: linear-gradient(135deg, rgba(239, 246, 255, .92), rgba(255, 255, 255, .96));
63
+ color: #2563eb;
64
+ font-size: 13px;
65
+ font-weight: 500;
66
+ box-shadow: 0 8px 24px rgba(59, 130, 246, .08);
67
+
68
+ .material-symbols-rounded {
69
+ font-size: 16px;
70
+ }
71
+ }
72
+
46
73
  .sender-container {
47
74
  flex: 0;
48
75
  display: flex;
@@ -87,3 +114,11 @@
87
114
  overflow: auto;
88
115
  padding: 20px 24px 24px;
89
116
  }
117
+
118
+ .chat-route__empty-state {
119
+ height: 100%;
120
+ display: grid;
121
+ place-items: center;
122
+ align-content: center;
123
+ gap: 16px;
124
+ }
@@ -1,13 +1,46 @@
1
- import './Chat.scss'
1
+ import './ChatRoute.scss'
2
2
 
3
- import type { ChatMessage, ChatMessageContent, Session } from '@vibe-forge/core'
4
- import { ChatHeader } from './chat/ChatHeader.js'
5
- import { ChatHistoryView } from './chat/ChatHistoryView.js'
6
- import { ChatSettingsView } from './chat/ChatSettingsView.js'
7
- import { ChatTimelineView } from './chat/ChatTimelineView.js'
3
+ import { Button, Empty } from 'antd'
4
+ import { useTranslation } from 'react-i18next'
5
+ import { useNavigate, useParams } from 'react-router-dom'
6
+ import useSWR from 'swr'
7
+
8
+ import type { Session } from '@vibe-forge/core'
9
+
10
+ import { listSessions } from '#~/api'
11
+ import { ChatHeader } from '#~/components/chat/ChatHeader.js'
12
+ import { ChatHistoryView } from '#~/components/chat/ChatHistoryView.js'
13
+ import { ChatSettingsView } from '#~/components/chat/ChatSettingsView.js'
14
+ import { ChatTimelineView } from '#~/components/chat/ChatTimelineView.js'
8
15
  import { useChatSession } from '#~/hooks/chat/use-chat-session'
9
16
 
10
- export function Chat({
17
+ export function ChatRoute() {
18
+ const { t } = useTranslation()
19
+ const { sessionId } = useParams()
20
+ const navigate = useNavigate()
21
+ const { data: sessionsRes, isLoading } = useSWR<{ sessions: Session[] }>(
22
+ sessionId ? '/api/sessions' : null,
23
+ () => listSessions('active')
24
+ )
25
+ const session = sessionId == null ? undefined : sessionsRes?.sessions.find(item => item.id === sessionId)
26
+
27
+ if (sessionId != null && isLoading) {
28
+ return null
29
+ }
30
+
31
+ if (sessionId != null && session == null) {
32
+ return (
33
+ <div className='chat-route__empty-state'>
34
+ <Empty description={t('common.sessionNotFound')} />
35
+ <Button type='primary' onClick={() => void navigate('/')}>{t('common.backToHome')}</Button>
36
+ </div>
37
+ )
38
+ }
39
+
40
+ return <ChatRouteView session={session} />
41
+ }
42
+
43
+ function ChatRouteView({
11
44
  session
12
45
  }: {
13
46
  session?: Session
@@ -17,6 +50,8 @@ export function Chat({
17
50
  sessionInfo,
18
51
  interactionRequest,
19
52
  isReady,
53
+ connectionError,
54
+ retryConnection,
20
55
  activeView,
21
56
  setActiveView,
22
57
  handleInteractionResponse,
@@ -35,30 +70,20 @@ export function Chat({
35
70
  hasAvailableModels,
36
71
  modelUnavailable
37
72
  } = useChatSession({ session })
38
- const buildUserMessage = (content: string | ChatMessageContent[]): ChatMessage => {
39
- const id = globalThis.crypto?.randomUUID
40
- ? globalThis.crypto.randomUUID()
41
- : `local-${Date.now()}-${Math.random().toString(16).slice(2)}`
42
- return {
43
- id,
44
- role: 'user' as const,
45
- content,
46
- createdAt: Date.now()
47
- }
48
- }
73
+ const isEmptyNewSession = !session?.id && messages.length === 0
49
74
 
50
75
  return (
51
- <div className={`chat-container ${isReady ? 'ready' : ''} ${!session?.id ? 'is-new-session' : ''}`}>
76
+ <div className={`chat-container ${isReady ? 'ready' : ''} ${isEmptyNewSession ? 'is-new-session' : ''}`}>
52
77
  {session?.id && (
53
78
  <ChatHeader
54
79
  sessionInfo={sessionInfo}
55
- sessionId={session?.id}
56
- sessionTitle={session?.title}
57
- isStarred={session?.isStarred}
58
- isArchived={session?.isArchived}
59
- tags={session?.tags}
60
- lastMessage={session?.lastMessage}
61
- lastUserMessage={session?.lastUserMessage}
80
+ sessionId={session.id}
81
+ sessionTitle={session.title}
82
+ isStarred={session.isStarred}
83
+ isArchived={session.isArchived}
84
+ tags={session.tags}
85
+ lastMessage={session.lastMessage}
86
+ lastUserMessage={session.lastUserMessage}
62
87
  activeView={activeView}
63
88
  onViewChange={setActiveView}
64
89
  />
@@ -70,11 +95,12 @@ export function Chat({
70
95
  messages={messages}
71
96
  session={session}
72
97
  sessionInfo={sessionInfo}
98
+ connectionError={connectionError}
99
+ onRetryConnection={retryConnection}
73
100
  interactionRequest={interactionRequest}
74
101
  onInteractionResponse={handleInteractionResponse}
102
+ setMessages={setMessages}
75
103
  onClearMessages={() => setMessages([])}
76
- onSend={(text) => setMessages((prev) => [...prev, buildUserMessage(text)])}
77
- onSendContent={(content) => setMessages((prev) => [...prev, buildUserMessage(content)])}
78
104
  placeholder={placeholder}
79
105
  modelOptions={modelOptions}
80
106
  selectedModel={selectedModel}
@@ -0,0 +1,5 @@
1
+ import { ConfigView } from '#~/components/ConfigView'
2
+
3
+ export function ConfigRoute() {
4
+ return <ConfigView />
5
+ }
@@ -0,0 +1,5 @@
1
+ import { KnowledgeBaseView } from '#~/components/knowledge-base'
2
+
3
+ export function KnowledgeRoute() {
4
+ return <KnowledgeBaseView />
5
+ }
@@ -1 +0,0 @@
1
- import{al as o,am as n}from"./mermaid.core-Cmlqga_E.js";const l=(a,r)=>o.lang.round(n.parse(a)[r]);export{l as c};
@@ -1 +0,0 @@
1
- import{a as r}from"./graph-DRCU-8Rz.js";var a=4;function n(o){return r(o,a)}export{n as c};
@@ -1 +0,0 @@
1
- import{f as o,p as e}from"./flowDb-c6c81e3f-G1gSTTBI.js";import{f as a,g as t}from"./styles-d45a18b0-BC1Ak1So.js";import{ar as i}from"./mermaid.core-Cmlqga_E.js";import"./graph-DRCU-8Rz.js";import"./layout-s0slG1OL.js";import"./index-cGZvDhhU.js";import"./index-5325376f-Bq-fg2i_.js";import"./clone-B2E8tddE.js";import"./edges-96097737-9P1uH1RE.js";import"./createText-1719965b-DwX8iC5F.js";import"./line-CymFqgW6.js";import"./array-BKyUJesY.js";import"./path-CbwjOpE9.js";import"./channel-84s1ACzD.js";const n={parser:e,db:o,renderer:t,styles:a,init:r=>{r.flowchart||(r.flowchart={}),r.flowchart.arrowMarkerAbsolute=r.arrowMarkerAbsolute,i({flowchart:{arrowMarkerAbsolute:r.arrowMarkerAbsolute}}),t.setConf(r.flowchart),o.clear(),o.setGen("gen-2")}};export{n as diagram};