@huyooo/ai-chat-frontend-react 0.1.4 → 0.1.8

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 (91) hide show
  1. package/README.md +368 -0
  2. package/dist/index.css +2575 -0
  3. package/dist/index.css.map +1 -0
  4. package/dist/index.d.ts +378 -135
  5. package/dist/index.js +3954 -1044
  6. package/dist/index.js.map +1 -1
  7. package/dist/style.css +48 -987
  8. package/package.json +7 -4
  9. package/src/adapter.ts +10 -70
  10. package/src/components/ChatPanel.tsx +373 -117
  11. package/src/components/common/ConfirmDialog.css +136 -0
  12. package/src/components/common/ConfirmDialog.tsx +91 -0
  13. package/src/components/common/CopyButton.css +22 -0
  14. package/src/components/common/CopyButton.tsx +46 -0
  15. package/src/components/common/IndexingSettings.css +207 -0
  16. package/src/components/common/IndexingSettings.tsx +398 -0
  17. package/src/components/common/SettingsPanel.css +256 -0
  18. package/src/components/common/SettingsPanel.tsx +120 -0
  19. package/src/components/common/Toast.css +50 -0
  20. package/src/components/common/Toast.tsx +38 -0
  21. package/src/components/common/ToggleSwitch.css +52 -0
  22. package/src/components/common/ToggleSwitch.tsx +20 -0
  23. package/src/components/header/ChatHeader.css +285 -0
  24. package/src/components/header/ChatHeader.tsx +376 -0
  25. package/src/components/input/AtFilePicker.css +147 -0
  26. package/src/components/input/AtFilePicker.tsx +519 -0
  27. package/src/components/input/ChatInput.css +204 -0
  28. package/src/components/input/ChatInput.tsx +506 -0
  29. package/src/components/input/DropdownSelector.css +159 -0
  30. package/src/components/input/DropdownSelector.tsx +195 -0
  31. package/src/components/input/ImagePreviewModal.css +124 -0
  32. package/src/components/input/ImagePreviewModal.tsx +118 -0
  33. package/src/components/input/at-views/AtBranchView.tsx +34 -0
  34. package/src/components/input/at-views/AtBrowserView.tsx +34 -0
  35. package/src/components/input/at-views/AtChatsView.tsx +34 -0
  36. package/src/components/input/at-views/AtDocsView.tsx +34 -0
  37. package/src/components/input/at-views/AtFilesView.tsx +168 -0
  38. package/src/components/input/at-views/AtTerminalsView.tsx +34 -0
  39. package/src/components/input/at-views/AtViewStyles.css +143 -0
  40. package/src/components/input/at-views/index.ts +9 -0
  41. package/src/components/message/ContentRenderer.css +9 -0
  42. package/src/components/message/ContentRenderer.tsx +63 -0
  43. package/src/components/message/MessageBubble.css +190 -0
  44. package/src/components/message/MessageBubble.tsx +231 -0
  45. package/src/components/message/PartsRenderer.css +4 -0
  46. package/src/components/message/PartsRenderer.tsx +114 -0
  47. package/src/components/message/ToolResultRenderer.tsx +21 -0
  48. package/src/components/message/WelcomeMessage.css +221 -0
  49. package/src/components/message/WelcomeMessage.tsx +93 -0
  50. package/src/components/message/blocks/CodeBlock.tsx +60 -0
  51. package/src/components/message/blocks/TextBlock.tsx +15 -0
  52. package/src/components/message/blocks/blocks.css +141 -0
  53. package/src/components/message/blocks/index.ts +6 -0
  54. package/src/components/message/parts/CollapsibleCard.css +78 -0
  55. package/src/components/message/parts/CollapsibleCard.tsx +77 -0
  56. package/src/components/message/parts/ErrorPart.css +9 -0
  57. package/src/components/message/parts/ErrorPart.tsx +40 -0
  58. package/src/components/message/parts/ImagePart.css +50 -0
  59. package/src/components/message/parts/ImagePart.tsx +54 -0
  60. package/src/components/message/parts/SearchPart.css +44 -0
  61. package/src/components/message/parts/SearchPart.tsx +63 -0
  62. package/src/components/message/parts/TextPart.css +10 -0
  63. package/src/components/message/parts/TextPart.tsx +20 -0
  64. package/src/components/message/parts/ThinkingPart.css +9 -0
  65. package/src/components/message/parts/ThinkingPart.tsx +48 -0
  66. package/src/components/message/parts/ToolCallPart.css +220 -0
  67. package/src/components/message/parts/ToolCallPart.tsx +285 -0
  68. package/src/components/message/parts/ToolResultPart.css +68 -0
  69. package/src/components/message/parts/ToolResultPart.tsx +96 -0
  70. package/src/components/message/parts/index.ts +11 -0
  71. package/src/components/message/tool-results/DefaultToolResult.tsx +26 -0
  72. package/src/components/message/tool-results/SearchResults.tsx +69 -0
  73. package/src/components/message/tool-results/WeatherCard.tsx +63 -0
  74. package/src/components/message/tool-results/index.ts +7 -0
  75. package/src/components/message/tool-results/tool-results.css +179 -0
  76. package/src/components/message/welcome-types.ts +46 -0
  77. package/src/context/AutoRunConfigContext.tsx +13 -0
  78. package/src/context/ChatAdapterContext.tsx +8 -0
  79. package/src/context/ChatInputContext.tsx +40 -0
  80. package/src/context/RenderersContext.tsx +41 -0
  81. package/src/hooks/useChat.ts +855 -237
  82. package/src/hooks/useImageUpload.ts +253 -0
  83. package/src/index.ts +99 -42
  84. package/src/styles.css +48 -987
  85. package/src/types/index.ts +172 -103
  86. package/src/utils/fileIcon.ts +49 -0
  87. package/src/components/ChatInput.tsx +0 -368
  88. package/src/components/chat/messages/ExecutionSteps.tsx +0 -234
  89. package/src/components/chat/messages/MessageBubble.tsx +0 -130
  90. package/src/components/chat/ui/ChatHeader.tsx +0 -301
  91. package/src/components/chat/ui/WelcomeMessage.tsx +0 -107
@@ -0,0 +1,136 @@
1
+ .confirm-dialog-overlay {
2
+ position: fixed;
3
+ inset: 0;
4
+ z-index: 1000;
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ background: rgba(0, 0, 0, 0.5);
9
+ backdrop-filter: blur(2px);
10
+ animation: fadeIn 0.2s ease;
11
+ }
12
+
13
+ .confirm-dialog {
14
+ width: 100%;
15
+ max-width: 400px;
16
+ margin: 16px;
17
+ background: var(--chat-dropdown-bg, #252526);
18
+ border: 1px solid rgba(255, 255, 255, 0.1);
19
+ border-radius: 12px;
20
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
21
+ overflow: hidden;
22
+ animation: scaleIn 0.2s ease;
23
+ }
24
+
25
+ .confirm-dialog-header {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 8px;
29
+ padding: 12px 16px;
30
+ border-bottom: 1px solid var(--chat-border, #333);
31
+ }
32
+
33
+ .confirm-dialog-header .icon {
34
+ flex-shrink: 0;
35
+ }
36
+
37
+ .confirm-dialog-header .icon.warning {
38
+ color: #f59e0b;
39
+ }
40
+
41
+ .confirm-dialog-header .icon.danger {
42
+ color: #ef4444;
43
+ }
44
+
45
+ .confirm-dialog-header .icon.info {
46
+ color: #3b82f6;
47
+ }
48
+
49
+ .confirm-dialog-header .title {
50
+ font-size: 14px;
51
+ font-weight: 600;
52
+ color: var(--chat-text, #fff);
53
+ }
54
+
55
+ .confirm-dialog-content {
56
+ padding: 16px;
57
+ font-size: 13px;
58
+ line-height: 1.6;
59
+ color: var(--chat-text-muted, #999);
60
+ }
61
+
62
+ .confirm-dialog-footer {
63
+ display: flex;
64
+ justify-content: flex-end;
65
+ gap: 8px;
66
+ padding: 12px 16px;
67
+ border-top: 1px solid var(--chat-border, #333);
68
+ }
69
+
70
+ .confirm-dialog-footer .btn {
71
+ padding: 6px 12px;
72
+ font-size: 13px;
73
+ font-weight: 500;
74
+ border: none;
75
+ border-radius: 4px;
76
+ cursor: pointer;
77
+ transition: all 0.15s;
78
+ }
79
+
80
+ .confirm-dialog-footer .btn-cancel {
81
+ background: transparent;
82
+ color: var(--chat-text-muted, #999);
83
+ border: 1px solid var(--chat-border, #444);
84
+ }
85
+
86
+ .confirm-dialog-footer .btn-cancel:hover {
87
+ background: rgba(255, 255, 255, 0.05);
88
+ color: var(--chat-text, #fff);
89
+ }
90
+
91
+ .confirm-dialog-footer .btn-info {
92
+ background: #3b82f6;
93
+ color: #fff;
94
+ }
95
+
96
+ .confirm-dialog-footer .btn-info:hover {
97
+ background: #2563eb;
98
+ }
99
+
100
+ .confirm-dialog-footer .btn-warning {
101
+ background: #f59e0b;
102
+ color: #000;
103
+ }
104
+
105
+ .confirm-dialog-footer .btn-warning:hover {
106
+ background: #d97706;
107
+ }
108
+
109
+ .confirm-dialog-footer .btn-danger {
110
+ background: #ef4444;
111
+ color: #fff;
112
+ }
113
+
114
+ .confirm-dialog-footer .btn-danger:hover {
115
+ background: #dc2626;
116
+ }
117
+
118
+ @keyframes fadeIn {
119
+ from {
120
+ opacity: 0;
121
+ }
122
+ to {
123
+ opacity: 1;
124
+ }
125
+ }
126
+
127
+ @keyframes scaleIn {
128
+ from {
129
+ transform: scale(0.95);
130
+ opacity: 0;
131
+ }
132
+ to {
133
+ transform: scale(1);
134
+ opacity: 1;
135
+ }
136
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * ConfirmDialog Component
3
+ * 自定义确认弹窗组件
4
+ */
5
+
6
+ import { type FC, useEffect } from 'react'
7
+ import { createPortal } from 'react-dom'
8
+ import { Icon } from '@iconify/react'
9
+ import './ConfirmDialog.css'
10
+
11
+ interface ConfirmDialogProps {
12
+ /** 是否显示 */
13
+ visible: boolean
14
+ /** 标题 */
15
+ title?: string
16
+ /** 消息内容 */
17
+ message: string
18
+ /** 类型:info | warning | danger */
19
+ type?: 'info' | 'warning' | 'danger'
20
+ /** 确认按钮文字 */
21
+ confirmText?: string
22
+ /** 取消按钮文字 */
23
+ cancelText?: string
24
+ /** 确认回调 */
25
+ onConfirm?: () => void
26
+ /** 取消回调 */
27
+ onCancel?: () => void
28
+ }
29
+
30
+ export const ConfirmDialog: FC<ConfirmDialogProps> = ({
31
+ visible,
32
+ title = '确认',
33
+ message,
34
+ type = 'warning',
35
+ confirmText = '确定',
36
+ cancelText = '取消',
37
+ onConfirm,
38
+ onCancel,
39
+ }) => {
40
+ // ESC 键关闭
41
+ useEffect(() => {
42
+ const handleKeyDown = (e: KeyboardEvent) => {
43
+ if (e.key === 'Escape' && visible) {
44
+ onCancel?.()
45
+ }
46
+ }
47
+ document.addEventListener('keydown', handleKeyDown)
48
+ return () => document.removeEventListener('keydown', handleKeyDown)
49
+ }, [visible, onCancel])
50
+
51
+ // 阻止滚动
52
+ useEffect(() => {
53
+ if (visible) {
54
+ document.body.style.overflow = 'hidden'
55
+ } else {
56
+ document.body.style.overflow = ''
57
+ }
58
+ return () => {
59
+ document.body.style.overflow = ''
60
+ }
61
+ }, [visible])
62
+
63
+ if (!visible) return null
64
+
65
+ const iconMap = {
66
+ info: 'lucide:info',
67
+ warning: 'lucide:alert-triangle',
68
+ danger: 'lucide:alert-circle',
69
+ }
70
+
71
+ return createPortal(
72
+ <div className="confirm-dialog-overlay" onClick={onCancel}>
73
+ <div className="confirm-dialog" onClick={(e) => e.stopPropagation()}>
74
+ <div className="confirm-dialog-header">
75
+ <Icon icon={iconMap[type]} className={`icon ${type}`} width={18} />
76
+ <span className="title">{title}</span>
77
+ </div>
78
+ <div className="confirm-dialog-content">{message}</div>
79
+ <div className="confirm-dialog-footer">
80
+ <button className="btn btn-cancel" onClick={onCancel}>
81
+ {cancelText}
82
+ </button>
83
+ <button className={`btn btn-${type}`} onClick={onConfirm}>
84
+ {confirmText}
85
+ </button>
86
+ </div>
87
+ </div>
88
+ </div>,
89
+ document.body
90
+ )
91
+ }
@@ -0,0 +1,22 @@
1
+ .copy-btn {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: 4px;
6
+ background: transparent;
7
+ border: none;
8
+ color: var(--chat-text-muted, #888);
9
+ cursor: pointer;
10
+ border-radius: 4px;
11
+ transition: all 0.2s;
12
+ }
13
+
14
+ .copy-btn:hover {
15
+ background: rgba(255, 255, 255, 0.1);
16
+ color: var(--chat-text, #fff);
17
+ }
18
+
19
+ .copy-btn.copied {
20
+ color: var(--chat-success, #22c55e);
21
+ }
22
+
@@ -0,0 +1,46 @@
1
+ import { FC, useState, useCallback } from 'react'
2
+ import { Icon } from '@iconify/react'
3
+ import './CopyButton.css'
4
+
5
+ interface CopyButtonProps {
6
+ /** 要复制的文本 */
7
+ text: string
8
+ /** 按钮标题 */
9
+ title?: string
10
+ /** 图标大小 */
11
+ size?: number
12
+ /** 复制成功回调 */
13
+ onCopy?: (text: string) => void
14
+ }
15
+
16
+ export const CopyButton: FC<CopyButtonProps> = ({
17
+ text,
18
+ title = '复制',
19
+ size = 14,
20
+ onCopy,
21
+ }) => {
22
+ const [copied, setCopied] = useState(false)
23
+
24
+ const handleCopy = useCallback(async (e: React.MouseEvent) => {
25
+ e.stopPropagation()
26
+ try {
27
+ await navigator.clipboard.writeText(text)
28
+ setCopied(true)
29
+ onCopy?.(text)
30
+ setTimeout(() => setCopied(false), 2000)
31
+ } catch (err) {
32
+ console.error('复制失败:', err)
33
+ }
34
+ }, [text, onCopy])
35
+
36
+ return (
37
+ <button
38
+ className={`copy-btn ${copied ? 'copied' : ''}`}
39
+ title={copied ? '已复制' : title}
40
+ onClick={handleCopy}
41
+ >
42
+ <Icon icon={copied ? 'lucide:check' : 'lucide:copy'} width={size} />
43
+ </button>
44
+ )
45
+ }
46
+
@@ -0,0 +1,207 @@
1
+ .indexing-settings {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 0;
5
+ }
6
+
7
+ /* 索引部分 - 使用标准的 setting-item 样式 */
8
+ .indexing-section {
9
+ align-items: flex-start;
10
+ }
11
+
12
+
13
+ .indexing-content {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 12px;
17
+ margin-top: 12px;
18
+ width: 100%;
19
+ min-width: 0;
20
+ }
21
+
22
+ /* 进度条 */
23
+ .progress-container {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 6px;
27
+ }
28
+
29
+ .progress-bar {
30
+ width: 100%;
31
+ height: 4px;
32
+ background: var(--chat-border, #333);
33
+ border-radius: 2px;
34
+ overflow: hidden;
35
+ }
36
+
37
+ .progress-fill {
38
+ height: 100%;
39
+ background: var(--chat-accent, #3b82f6);
40
+ border-radius: 2px;
41
+ transition: width 0.3s ease;
42
+ }
43
+
44
+ .progress-text {
45
+ font-size: 13px;
46
+ color: var(--chat-text-muted, #888);
47
+ }
48
+
49
+ /* 统计信息 */
50
+ .stats-info {
51
+ display: flex;
52
+ flex-wrap: wrap;
53
+ gap: 12px;
54
+ padding: 8px 0;
55
+ }
56
+
57
+ .stat-item {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 6px;
61
+ font-size: 12px;
62
+ color: var(--chat-text-muted, #888);
63
+ }
64
+
65
+ /* 当前文件 */
66
+ .current-file {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 6px;
70
+ font-size: 12px;
71
+ color: var(--chat-text-muted, #888);
72
+ padding: 6px 8px;
73
+ background: var(--chat-muted, #2a2a2a);
74
+ border-radius: 4px;
75
+ width: 100%;
76
+ min-width: 0;
77
+ }
78
+
79
+ .file-path {
80
+ flex: 1;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ min-width: 0;
85
+ }
86
+
87
+ /* 操作按钮 */
88
+ .action-buttons {
89
+ display: flex;
90
+ gap: 8px;
91
+ }
92
+
93
+ .action-buttons .edit-btn {
94
+ margin: 0;
95
+ }
96
+
97
+ .action-buttons .edit-btn:disabled {
98
+ opacity: 0.5;
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ .action-buttons .delete-btn {
103
+ color: #ef4444;
104
+ }
105
+
106
+ .action-buttons .delete-btn:hover:not(:disabled) {
107
+ color: #ef4444;
108
+ }
109
+
110
+ .spinning {
111
+ animation: spin 1s linear infinite;
112
+ }
113
+
114
+ @keyframes spin {
115
+ from {
116
+ transform: rotate(0deg);
117
+ }
118
+ to {
119
+ transform: rotate(360deg);
120
+ }
121
+ }
122
+
123
+ .spinning {
124
+ animation: spin 1s linear infinite;
125
+ }
126
+
127
+ @keyframes spin {
128
+ from {
129
+ transform: rotate(0deg);
130
+ }
131
+ to {
132
+ transform: rotate(360deg);
133
+ }
134
+ }
135
+
136
+ /* 设置项 - 复用父组件的样式,但需要覆盖一些 */
137
+ .indexing-settings .setting-item {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ padding: 16px 0;
142
+ border-bottom: 1px solid var(--chat-border, #333);
143
+ }
144
+
145
+ .indexing-settings .setting-item:last-child {
146
+ border-bottom: none;
147
+ }
148
+
149
+ .indexing-settings .setting-info {
150
+ flex: 1;
151
+ margin-right: 24px;
152
+ min-width: 0;
153
+ }
154
+
155
+ .indexing-settings .setting-label {
156
+ font-size: 14px;
157
+ font-weight: 500;
158
+ color: var(--chat-text, #fff);
159
+ margin-bottom: 4px;
160
+ }
161
+
162
+ .indexing-settings .setting-description {
163
+ font-size: 13px;
164
+ color: var(--chat-text-muted, #888);
165
+ line-height: 1.5;
166
+ margin-bottom: 0;
167
+ }
168
+
169
+ .link-btn {
170
+ display: inline;
171
+ padding: 0;
172
+ background: transparent;
173
+ border: none;
174
+ color: var(--chat-accent, #3b82f6);
175
+ cursor: pointer;
176
+ text-decoration: underline;
177
+ font-size: inherit;
178
+ }
179
+
180
+ .link-btn:hover {
181
+ color: var(--chat-accent-hover, #60a5fa);
182
+ }
183
+
184
+ .indexing-settings .setting-control {
185
+ flex-shrink: 0;
186
+ }
187
+
188
+ .edit-btn {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 6px;
192
+ padding: 6px 12px;
193
+ font-size: 13px;
194
+ font-weight: 500;
195
+ border: 1px solid var(--chat-border, #333);
196
+ border-radius: 6px;
197
+ background: transparent;
198
+ color: var(--chat-text, #fff);
199
+ cursor: pointer;
200
+ transition: all 0.15s;
201
+ }
202
+
203
+ .edit-btn:hover {
204
+ background: var(--chat-muted, #2a2a2a);
205
+ border-color: var(--chat-border, #444);
206
+ }
207
+