@ai-coding-agent/react-assistant 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AI Coding Agent
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # @ai-coding-agent/react-assistant
2
+
3
+ 一个 React 代码助手组件,基于 AI 分析 Git 仓库并回答相关问题。
4
+
5
+ ## 📦 安装
6
+
7
+ ```bash
8
+ npm install @ai-coding-agent/react-assistant
9
+ ```
10
+
11
+ ## 🚀 快速开始
12
+
13
+ ### 1. 基本使用
14
+
15
+ ```tsx
16
+ import { CodingAssistant } from '@ai-coding-agent/react-assistant';
17
+
18
+ function App() {
19
+ return (
20
+ <div>
21
+ <h1>我的项目</h1>
22
+
23
+ {/* 添加 AI 助手 */}
24
+ <CodingAssistant
25
+ apiUrl="https://your-backend.com"
26
+ repoUrl="https://github.com/user/repo.git"
27
+ branch="main"
28
+ position="bottom-right"
29
+ />
30
+ </div>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ### 2. 配置选项
36
+
37
+ ```typescript
38
+ interface CodingAssistantProps {
39
+ // 必需
40
+ apiUrl: string; // 后端 API 地址
41
+ repoUrl: string; // Git 仓库 URL
42
+
43
+ // 可选
44
+ accessToken?: string; // 访问私有仓库的令牌
45
+ branch?: string; // 分支名称(默认 "main")
46
+ position?: 'bottom-right' | 'bottom-left'; // 位置(默认 "bottom-right")
47
+ }
48
+ ```
49
+
50
+ ### 3. 私有仓库
51
+
52
+ ```tsx
53
+ <CodingAssistant
54
+ apiUrl="https://your-backend.com"
55
+ repoUrl="https://github.com/user/private-repo.git"
56
+ accessToken="your-github-token"
57
+ />
58
+ ```
59
+
60
+ ## 📖 完整示例
61
+
62
+ ```tsx
63
+ import React from 'react';
64
+ import { CodingAssistant } from '@ai-coding-agent/react-assistant';
65
+ import '@ai-coding-agent/react-assistant/dist/index.css';
66
+
67
+ function App() {
68
+ return (
69
+ <div className="min-h-screen bg-gray-50">
70
+ {/* 你的应用内容 */}
71
+ <header>
72
+ <h1>我的项目</h1>
73
+ </header>
74
+
75
+ <main>
76
+ <p>这是我的项目内容...</p>
77
+ </main>
78
+
79
+ {/* AI 助手 */}
80
+ <CodingAssistant
81
+ apiUrl="https://your-backend.com"
82
+ repoUrl="https://github.com/user/repo.git"
83
+ position="bottom-right"
84
+ />
85
+ </div>
86
+ );
87
+ }
88
+
89
+ export default App;
90
+ ```
91
+
92
+ ## 🎨 功能特性
93
+
94
+ - ✅ 悬浮按钮设计
95
+ - ✅ 实时 AI 对话
96
+ - ✅ 代码仓库分析
97
+ - ✅ 多轮对话支持
98
+ - ✅ 错误处理
99
+ - ✅ 加载状态
100
+ - ✅ 响应式设计
101
+
102
+ ## 🔧 开发
103
+
104
+ ### 克隆仓库
105
+
106
+ ```bash
107
+ git clone https://github.com/your-org/ai-coding-agent.git
108
+ cd ai-coding-agent/packages/react-assistant
109
+ ```
110
+
111
+ ### 安装依赖
112
+
113
+ ```bash
114
+ npm install
115
+ ```
116
+
117
+ ### 启动开发服务器
118
+
119
+ ```bash
120
+ npm run dev
121
+ ```
122
+
123
+ 访问 http://localhost:5173 查看演示应用。
124
+
125
+ ### 构建 npm 包
126
+
127
+ ```bash
128
+ npm run build
129
+ ```
130
+
131
+ 生成的文件在 `dist/` 目录。
132
+
133
+ ### 发布到 npm
134
+
135
+ ```bash
136
+ npm login
137
+ npm publish --access public
138
+ ```
139
+
140
+ ## 📁 项目结构
141
+
142
+ ```
143
+ react-assistant/
144
+ ├── src/ # 库源代码(发布到 npm)
145
+ │ ├── components/ # React 组件
146
+ │ │ ├── FloatingButton.tsx
147
+ │ │ ├── ChatWindow.tsx
148
+ │ │ └── CodingAssistant.tsx
149
+ │ ├── hooks/ # React Hooks
150
+ │ │ └── useChat.ts
151
+ │ ├── types.ts # TypeScript 类型
152
+ │ ├── index.tsx # 入口文件
153
+ │ └── index.css # 样式文件
154
+
155
+ ├── demo/ # 演示应用(不发布)
156
+ │ ├── App.tsx # 演示页面
157
+ │ ├── main.tsx # 入口
158
+ │ └── index.html # HTML
159
+
160
+ ├── dist/ # 构建输出(发布到 npm)
161
+ ├── package.json
162
+ ├── vite.config.ts # Vite 配置
163
+ └── README.md
164
+ ```
165
+
166
+ ## 🔑 依赖要求
167
+
168
+ ```json
169
+ {
170
+ "peerDependencies": {
171
+ "react": "^18.0.0 || ^19.0.0",
172
+ "react-dom": "^18.0.0 || ^19.0.0"
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## 🐛 常见问题
178
+
179
+ ### 1. 样式没有生效
180
+
181
+ 确保导入了样式文件:
182
+
183
+ ```tsx
184
+ import '@ai-coding-agent/react-assistant/dist/index.css';
185
+ ```
186
+
187
+ ### 2. 无法连接后端
188
+
189
+ 检查 `apiUrl` 配置是否正确:
190
+
191
+ ```tsx
192
+ // ❌ 错误:缺少协议
193
+ <CodingAssistant apiUrl="localhost:3001" />
194
+
195
+ // ✅ 正确:包含协议
196
+ <CodingAssistant apiUrl="http://localhost:3001" />
197
+ ```
198
+
199
+ ### 3. 私有仓库访问失败
200
+
201
+ 确保提供了正确的访问令牌:
202
+
203
+ ```tsx
204
+ <CodingAssistant
205
+ repoUrl="https://github.com/user/private-repo.git"
206
+ accessToken="ghp_xxxxxxxxxxxx" // GitHub Token
207
+ />
208
+ ```
209
+
210
+ ## 📝 License
211
+
212
+ MIT
213
+
214
+ ## 🤝 贡献
215
+
216
+ 欢迎提交 Issue 和 Pull Request!
217
+
218
+ ## 📮 联系方式
219
+
220
+ - GitHub: https://github.com/your-org/ai-coding-agent
221
+ - Issues: https://github.com/your-org/ai-coding-agent/issues
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ import { ChatMessage } from '../types';
3
+ interface ChatWindowProps {
4
+ messages: ChatMessage[];
5
+ isLoading: boolean;
6
+ onSendMessage: (message: string) => void;
7
+ onClose: () => void;
8
+ position?: 'bottom-right' | 'bottom-left';
9
+ }
10
+ export declare const ChatWindow: React.FC<ChatWindowProps>;
11
+ export {};
@@ -0,0 +1,40 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * 悬浮按钮的 Props 接口
4
+ *
5
+ * 属性说明:
6
+ * - isOpen: 聊天窗口是否打开
7
+ * - onClick: 点击事件处理函数
8
+ * - position: 按钮在屏幕上的位置
9
+ */
10
+ interface FloatingButtonProps {
11
+ isOpen: boolean;
12
+ onClick: () => void;
13
+ position?: 'bottom-right' | 'bottom-left';
14
+ }
15
+ /**
16
+ * 悬浮按钮组件
17
+ *
18
+ * 参数说明:
19
+ * - isOpen: 控制显示的图标类型
20
+ * * true: 显示关闭图标 (X)
21
+ * * false: 显示聊天图标
22
+ *
23
+ * - onClick: 点击按钮时调用的函数
24
+ * * 通常用于切换聊天窗口的显示/隐藏
25
+ *
26
+ * - position: 按钮位置(可选,默认 'bottom-right')
27
+ * * 'bottom-right': 右下角(推荐)
28
+ * * 'bottom-left': 左下角
29
+ *
30
+ * 样式说明:
31
+ * - fixed: 固定定位
32
+ * - w-14 h-14: 宽高 56px
33
+ * - bg-gradient-to-r: 渐变背景
34
+ * - shadow-lg: 大阴影
35
+ * - hover:scale-110: 悬停时放大 10%
36
+ * - transition-all: 平滑过渡动画
37
+ * - z-50: 层级 50(确保在最上层)
38
+ */
39
+ export declare const FloatingButton: React.FC<FloatingButtonProps>;
40
+ export {};
@@ -0,0 +1,43 @@
1
+ import { ChatMessage, CodingAssistantConfig } from '../types';
2
+ /**
3
+ * 聊天 Hook
4
+ *
5
+ * 参数说明:
6
+ * - config: 助手配置对象,包含 apiUrl、repoUrl 等
7
+ *
8
+ * 返回值:
9
+ * 返回一个对象,包含:
10
+ * - messages: 消息列表
11
+ * - isLoading: 加载状态
12
+ * - error: 错误信息
13
+ * - sendMessage: 发送消息的函数
14
+ * - clearMessages: 清空消息的函数
15
+ *
16
+ * 使用示例:
17
+ * ```tsx
18
+ * function ChatComponent() {
19
+ * const { messages, isLoading, sendMessage } = useChat({
20
+ * apiUrl: 'http://localhost:3001',
21
+ * repoUrl: 'https://github.com/user/repo.git'
22
+ * });
23
+ *
24
+ * return (
25
+ * <div>
26
+ * {messages.map(msg => (
27
+ * <div key={msg.id}>{msg.content}</div>
28
+ * ))}
29
+ * <button onClick={() => sendMessage('Hello')} disabled={isLoading}>
30
+ * 发送
31
+ * </button>
32
+ * </div>
33
+ * );
34
+ * }
35
+ * ```
36
+ */
37
+ export declare function useChat(config: CodingAssistantConfig): {
38
+ messages: ChatMessage[];
39
+ isLoading: boolean;
40
+ error: string | null;
41
+ sendMessage: (content: string) => Promise<void>;
42
+ clearMessages: () => void;
43
+ };
package/dist/index.css ADDED
@@ -0,0 +1,2 @@
1
+ *,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.bottom-20{bottom:5rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-6{left:1.5rem}.right-0{right:0}.right-4{right:1rem}.right-6{right:1.5rem}.top-4{top:1rem}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.block{display:block}.flex{display:flex}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[500px\]{height:500px}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-96{width:24rem}.max-w-\[80\%\]{max-width:80%}.flex-1{flex:1}.transform{transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.animate-bounce{animation:1s infinite bounce}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-gradient-to-r{background-image:linear-gradient(to right, var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from:#3b82f6 var(--tw-gradient-from-position);--tw-gradient-to:#3b82f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.to-purple-600{--tw-gradient-to:#9333ea var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-4{padding:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.text-center{text-align:center}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-semibold{font-weight:600}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-300{transition-duration:.3s}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-white\/20:hover{background-color:#fff3}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.focus\:border-transparent:focus{border-color:#0000}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}
2
+ /*$vite$:1*/
@@ -0,0 +1,6 @@
1
+ import { default as React } from 'react';
2
+ import { CodingAssistantConfig } from './types';
3
+ export interface CodingAssistantProps extends CodingAssistantConfig {
4
+ }
5
+ export declare const CodingAssistant: React.FC<CodingAssistantProps>;
6
+ export default CodingAssistant;
package/dist/index.js ADDED
@@ -0,0 +1,433 @@
1
+ import { useCallback as e, useEffect as t, useRef as n, useState as r } from "react";
2
+ //#region \0rolldown/runtime.js
3
+ var i = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), a = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
4
+ if (typeof require < "u") return require.apply(this, arguments);
5
+ throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
6
+ }), o = /* @__PURE__ */ i(((e) => {
7
+ var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
8
+ function r(e, n, r) {
9
+ var i = null;
10
+ if (r !== void 0 && (i = "" + r), n.key !== void 0 && (i = "" + n.key), "key" in n) for (var a in r = {}, n) a !== "key" && (r[a] = n[a]);
11
+ else r = n;
12
+ return n = r.ref, {
13
+ $$typeof: t,
14
+ type: e,
15
+ key: i,
16
+ ref: n === void 0 ? null : n,
17
+ props: r
18
+ };
19
+ }
20
+ e.Fragment = n, e.jsx = r, e.jsxs = r;
21
+ })), s = /* @__PURE__ */ i(((e) => {
22
+ process.env.NODE_ENV !== "production" && (function() {
23
+ function t(e) {
24
+ if (e == null) return null;
25
+ if (typeof e == "function") return e.$$typeof === k ? null : e.displayName || e.name || null;
26
+ if (typeof e == "string") return e;
27
+ switch (e) {
28
+ case v: return "Fragment";
29
+ case b: return "Profiler";
30
+ case y: return "StrictMode";
31
+ case w: return "Suspense";
32
+ case T: return "SuspenseList";
33
+ case O: return "Activity";
34
+ }
35
+ if (typeof e == "object") switch (typeof e.tag == "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), e.$$typeof) {
36
+ case _: return "Portal";
37
+ case S: return e.displayName || "Context";
38
+ case x: return (e._context.displayName || "Context") + ".Consumer";
39
+ case C:
40
+ var n = e.render;
41
+ return e = e.displayName, e ||= (e = n.displayName || n.name || "", e === "" ? "ForwardRef" : "ForwardRef(" + e + ")"), e;
42
+ case E: return n = e.displayName || null, n === null ? t(e.type) || "Memo" : n;
43
+ case D:
44
+ n = e._payload, e = e._init;
45
+ try {
46
+ return t(e(n));
47
+ } catch {}
48
+ }
49
+ return null;
50
+ }
51
+ function n(e) {
52
+ return "" + e;
53
+ }
54
+ function r(e) {
55
+ try {
56
+ n(e);
57
+ var t = !1;
58
+ } catch {
59
+ t = !0;
60
+ }
61
+ if (t) {
62
+ t = console;
63
+ var r = t.error, i = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
64
+ return r.call(t, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", i), n(e);
65
+ }
66
+ }
67
+ function i(e) {
68
+ if (e === v) return "<>";
69
+ if (typeof e == "object" && e && e.$$typeof === D) return "<...>";
70
+ try {
71
+ var n = t(e);
72
+ return n ? "<" + n + ">" : "<...>";
73
+ } catch {
74
+ return "<...>";
75
+ }
76
+ }
77
+ function o() {
78
+ var e = A.A;
79
+ return e === null ? null : e.getOwner();
80
+ }
81
+ function s() {
82
+ return Error("react-stack-top-frame");
83
+ }
84
+ function c(e) {
85
+ if (j.call(e, "key")) {
86
+ var t = Object.getOwnPropertyDescriptor(e, "key").get;
87
+ if (t && t.isReactWarning) return !1;
88
+ }
89
+ return e.key !== void 0;
90
+ }
91
+ function l(e, t) {
92
+ function n() {
93
+ P || (P = !0, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", t));
94
+ }
95
+ n.isReactWarning = !0, Object.defineProperty(e, "key", {
96
+ get: n,
97
+ configurable: !0
98
+ });
99
+ }
100
+ function u() {
101
+ var e = t(this.type);
102
+ return F[e] || (F[e] = !0, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")), e = this.props.ref, e === void 0 ? null : e;
103
+ }
104
+ function d(e, t, n, r, i, a) {
105
+ var o = n.ref;
106
+ return e = {
107
+ $$typeof: g,
108
+ type: e,
109
+ key: t,
110
+ props: n,
111
+ _owner: r
112
+ }, (o === void 0 ? null : o) === null ? Object.defineProperty(e, "ref", {
113
+ enumerable: !1,
114
+ value: null
115
+ }) : Object.defineProperty(e, "ref", {
116
+ enumerable: !1,
117
+ get: u
118
+ }), e._store = {}, Object.defineProperty(e._store, "validated", {
119
+ configurable: !1,
120
+ enumerable: !1,
121
+ writable: !0,
122
+ value: 0
123
+ }), Object.defineProperty(e, "_debugInfo", {
124
+ configurable: !1,
125
+ enumerable: !1,
126
+ writable: !0,
127
+ value: null
128
+ }), Object.defineProperty(e, "_debugStack", {
129
+ configurable: !1,
130
+ enumerable: !1,
131
+ writable: !0,
132
+ value: i
133
+ }), Object.defineProperty(e, "_debugTask", {
134
+ configurable: !1,
135
+ enumerable: !1,
136
+ writable: !0,
137
+ value: a
138
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
139
+ }
140
+ function f(e, n, i, a, s, u) {
141
+ var f = n.children;
142
+ if (f !== void 0) if (a) if (M(f)) {
143
+ for (a = 0; a < f.length; a++) p(f[a]);
144
+ Object.freeze && Object.freeze(f);
145
+ } else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
146
+ else p(f);
147
+ if (j.call(n, "key")) {
148
+ f = t(e);
149
+ var m = Object.keys(n).filter(function(e) {
150
+ return e !== "key";
151
+ });
152
+ a = 0 < m.length ? "{key: someKey, " + m.join(": ..., ") + ": ...}" : "{key: someKey}", R[f + a] || (m = 0 < m.length ? "{" + m.join(": ..., ") + ": ...}" : "{}", console.error("A props object containing a \"key\" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />", a, f, m, f), R[f + a] = !0);
153
+ }
154
+ if (f = null, i !== void 0 && (r(i), f = "" + i), c(n) && (r(n.key), f = "" + n.key), "key" in n) for (var h in i = {}, n) h !== "key" && (i[h] = n[h]);
155
+ else i = n;
156
+ return f && l(i, typeof e == "function" ? e.displayName || e.name || "Unknown" : e), d(e, f, i, o(), s, u);
157
+ }
158
+ function p(e) {
159
+ m(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e && e.$$typeof === D && (e._payload.status === "fulfilled" ? m(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
160
+ }
161
+ function m(e) {
162
+ return typeof e == "object" && !!e && e.$$typeof === g;
163
+ }
164
+ var h = a("react"), g = Symbol.for("react.transitional.element"), _ = Symbol.for("react.portal"), v = Symbol.for("react.fragment"), y = Symbol.for("react.strict_mode"), b = Symbol.for("react.profiler"), x = Symbol.for("react.consumer"), S = Symbol.for("react.context"), C = Symbol.for("react.forward_ref"), w = Symbol.for("react.suspense"), T = Symbol.for("react.suspense_list"), E = Symbol.for("react.memo"), D = Symbol.for("react.lazy"), O = Symbol.for("react.activity"), k = Symbol.for("react.client.reference"), A = h.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, j = Object.prototype.hasOwnProperty, M = Array.isArray, N = console.createTask ? console.createTask : function() {
165
+ return null;
166
+ };
167
+ h = { react_stack_bottom_frame: function(e) {
168
+ return e();
169
+ } };
170
+ var P, F = {}, I = h.react_stack_bottom_frame.bind(h, s)(), L = N(i(s)), R = {};
171
+ e.Fragment = v, e.jsx = function(e, t, n) {
172
+ var r = 1e4 > A.recentlyCreatedOwnerStacks++;
173
+ return f(e, t, n, !1, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
174
+ }, e.jsxs = function(e, t, n) {
175
+ var r = 1e4 > A.recentlyCreatedOwnerStacks++;
176
+ return f(e, t, n, !0, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
177
+ };
178
+ })();
179
+ })), c = (/* @__PURE__ */ i(((e, t) => {
180
+ process.env.NODE_ENV === "production" ? t.exports = o() : t.exports = s();
181
+ })))(), l = ({ isOpen: e, onClick: t, position: n = "bottom-right" }) => /* @__PURE__ */ (0, c.jsx)("button", {
182
+ onClick: t,
183
+ className: `fixed ${n === "bottom-right" ? "right-6 bottom-6" : "left-6 bottom-6"} w-14 h-14 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full shadow-lg hover:shadow-xl transform hover:scale-110 transition-all duration-300 flex items-center justify-center z-50`,
184
+ "aria-label": "Toggle chat",
185
+ children: e ? /* @__PURE__ */ (0, c.jsx)("svg", {
186
+ className: "w-6 h-6",
187
+ fill: "none",
188
+ stroke: "currentColor",
189
+ viewBox: "0 0 24 24",
190
+ children: /* @__PURE__ */ (0, c.jsx)("path", {
191
+ strokeLinecap: "round",
192
+ strokeLinejoin: "round",
193
+ strokeWidth: 2,
194
+ d: "M6 18L18 6M6 6l12 12"
195
+ })
196
+ }) : /* @__PURE__ */ (0, c.jsx)("svg", {
197
+ className: "w-6 h-6",
198
+ fill: "none",
199
+ stroke: "currentColor",
200
+ viewBox: "0 0 24 24",
201
+ children: /* @__PURE__ */ (0, c.jsx)("path", {
202
+ strokeLinecap: "round",
203
+ strokeLinejoin: "round",
204
+ strokeWidth: 2,
205
+ d: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
206
+ })
207
+ })
208
+ }), u = ({ messages: e, isLoading: i, onSendMessage: a, onClose: o, position: s = "bottom-right" }) => {
209
+ let [l, u] = r(""), d = n(null), f = () => {
210
+ d.current?.scrollIntoView({ behavior: "smooth" });
211
+ };
212
+ t(() => {
213
+ f();
214
+ }, [e]);
215
+ let p = (e) => {
216
+ e.preventDefault(), l.trim() && !i && (a(l.trim()), u(""));
217
+ };
218
+ return /* @__PURE__ */ (0, c.jsxs)("div", {
219
+ className: `fixed bottom-20 ${s === "bottom-right" ? "right-0" : "left-0"} w-96 h-[500px] bg-white rounded-lg shadow-2xl flex flex-col overflow-hidden border border-gray-200`,
220
+ children: [
221
+ /* @__PURE__ */ (0, c.jsxs)("div", {
222
+ className: "bg-gradient-to-r from-blue-500 to-purple-600 text-white px-4 py-3 flex justify-between items-center",
223
+ children: [/* @__PURE__ */ (0, c.jsxs)("div", { children: [/* @__PURE__ */ (0, c.jsx)("h3", {
224
+ className: "font-semibold text-lg",
225
+ children: "AI Coding Assistant"
226
+ }), /* @__PURE__ */ (0, c.jsx)("p", {
227
+ className: "text-xs text-blue-100",
228
+ children: "Ask me about your code"
229
+ })] }), /* @__PURE__ */ (0, c.jsx)("button", {
230
+ onClick: o,
231
+ className: "text-white hover:bg-white/20 rounded-full p-1 transition-colors",
232
+ "aria-label": "Close chat",
233
+ children: /* @__PURE__ */ (0, c.jsx)("svg", {
234
+ className: "w-5 h-5",
235
+ fill: "none",
236
+ stroke: "currentColor",
237
+ viewBox: "0 0 24 24",
238
+ children: /* @__PURE__ */ (0, c.jsx)("path", {
239
+ strokeLinecap: "round",
240
+ strokeLinejoin: "round",
241
+ strokeWidth: 2,
242
+ d: "M6 18L18 6M6 6l12 12"
243
+ })
244
+ })
245
+ })]
246
+ }),
247
+ /* @__PURE__ */ (0, c.jsxs)("div", {
248
+ className: "flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50",
249
+ children: [
250
+ e.length === 0 ? /* @__PURE__ */ (0, c.jsxs)("div", {
251
+ className: "text-center text-gray-500 mt-10",
252
+ children: [
253
+ /* @__PURE__ */ (0, c.jsx)("svg", {
254
+ className: "w-16 h-16 mx-auto mb-4 text-gray-300",
255
+ fill: "none",
256
+ stroke: "currentColor",
257
+ viewBox: "0 0 24 24",
258
+ children: /* @__PURE__ */ (0, c.jsx)("path", {
259
+ strokeLinecap: "round",
260
+ strokeLinejoin: "round",
261
+ strokeWidth: 1.5,
262
+ d: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
263
+ })
264
+ }),
265
+ /* @__PURE__ */ (0, c.jsx)("p", {
266
+ className: "text-sm",
267
+ children: "你好!我是你的代码助手。"
268
+ }),
269
+ /* @__PURE__ */ (0, c.jsx)("p", {
270
+ className: "text-xs mt-1",
271
+ children: "问我关于这个代码库的任何问题"
272
+ })
273
+ ]
274
+ }) : e.map((e) => /* @__PURE__ */ (0, c.jsx)("div", {
275
+ className: `flex ${e.role === "user" ? "justify-end" : "justify-start"}`,
276
+ children: /* @__PURE__ */ (0, c.jsxs)("div", {
277
+ className: `max-w-[80%] rounded-2xl px-4 py-2 ${e.role === "user" ? "bg-blue-500 text-white" : "bg-white text-gray-800 border border-gray-200"}`,
278
+ children: [/* @__PURE__ */ (0, c.jsx)("p", {
279
+ className: "text-sm whitespace-pre-wrap break-words",
280
+ children: e.content
281
+ }), /* @__PURE__ */ (0, c.jsx)("span", {
282
+ className: "text-xs opacity-70 mt-1 block",
283
+ children: new Date(e.timestamp).toLocaleTimeString()
284
+ })]
285
+ })
286
+ }, e.id)),
287
+ i && /* @__PURE__ */ (0, c.jsx)("div", {
288
+ className: "flex justify-start",
289
+ children: /* @__PURE__ */ (0, c.jsx)("div", {
290
+ className: "bg-white border border-gray-200 rounded-2xl px-4 py-3",
291
+ children: /* @__PURE__ */ (0, c.jsxs)("div", {
292
+ className: "flex space-x-2",
293
+ children: [
294
+ /* @__PURE__ */ (0, c.jsx)("div", {
295
+ className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
296
+ style: { animationDelay: "0ms" }
297
+ }),
298
+ /* @__PURE__ */ (0, c.jsx)("div", {
299
+ className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
300
+ style: { animationDelay: "150ms" }
301
+ }),
302
+ /* @__PURE__ */ (0, c.jsx)("div", {
303
+ className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce",
304
+ style: { animationDelay: "300ms" }
305
+ })
306
+ ]
307
+ })
308
+ })
309
+ }),
310
+ /* @__PURE__ */ (0, c.jsx)("div", { ref: d })
311
+ ]
312
+ }),
313
+ /* @__PURE__ */ (0, c.jsx)("form", {
314
+ onSubmit: p,
315
+ className: "p-4 bg-white border-t border-gray-200",
316
+ children: /* @__PURE__ */ (0, c.jsxs)("div", {
317
+ className: "flex space-x-2",
318
+ children: [/* @__PURE__ */ (0, c.jsx)("input", {
319
+ type: "text",
320
+ value: l,
321
+ onChange: (e) => u(e.target.value),
322
+ placeholder: "输入你的问题...",
323
+ disabled: i,
324
+ className: "flex-1 px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
325
+ }), /* @__PURE__ */ (0, c.jsx)("button", {
326
+ type: "submit",
327
+ disabled: i || !l.trim(),
328
+ className: "px-4 py-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
329
+ children: i ? /* @__PURE__ */ (0, c.jsxs)("svg", {
330
+ className: "w-5 h-5 animate-spin",
331
+ fill: "none",
332
+ viewBox: "0 0 24 24",
333
+ children: [/* @__PURE__ */ (0, c.jsx)("circle", {
334
+ className: "opacity-25",
335
+ cx: "12",
336
+ cy: "12",
337
+ r: "10",
338
+ stroke: "currentColor",
339
+ strokeWidth: "4"
340
+ }), /* @__PURE__ */ (0, c.jsx)("path", {
341
+ className: "opacity-75",
342
+ fill: "currentColor",
343
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
344
+ })]
345
+ }) : /* @__PURE__ */ (0, c.jsx)("svg", {
346
+ className: "w-5 h-5",
347
+ fill: "none",
348
+ stroke: "currentColor",
349
+ viewBox: "0 0 24 24",
350
+ children: /* @__PURE__ */ (0, c.jsx)("path", {
351
+ strokeLinecap: "round",
352
+ strokeLinejoin: "round",
353
+ strokeWidth: 2,
354
+ d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
355
+ })
356
+ })
357
+ })]
358
+ })
359
+ })
360
+ ]
361
+ });
362
+ };
363
+ //#endregion
364
+ //#region src/hooks/useChat.ts
365
+ function d(t) {
366
+ let [n, i] = r([]), [a, o] = r(!1), [s, c] = r(null);
367
+ return {
368
+ messages: n,
369
+ isLoading: a,
370
+ error: s,
371
+ sendMessage: e(async (e) => {
372
+ let n = {
373
+ id: Date.now().toString(),
374
+ role: "user",
375
+ content: e,
376
+ timestamp: /* @__PURE__ */ new Date()
377
+ };
378
+ i((e) => [...e, n]), o(!0), c(null);
379
+ try {
380
+ let n = await fetch(`${t.apiUrl}/api/chat`, {
381
+ method: "POST",
382
+ headers: { "Content-Type": "application/json" },
383
+ body: JSON.stringify({
384
+ message: e,
385
+ repoUrl: t.repoUrl,
386
+ accessToken: t.accessToken,
387
+ branch: t.branch
388
+ })
389
+ });
390
+ if (!n.ok) throw Error(`HTTP error! status: ${n.status}`);
391
+ let r = await n.json(), a = {
392
+ id: (Date.now() + 1).toString(),
393
+ role: "assistant",
394
+ content: r.response,
395
+ timestamp: /* @__PURE__ */ new Date()
396
+ };
397
+ i((e) => [...e, a]);
398
+ } catch (e) {
399
+ c(e instanceof Error ? e.message : "Unknown error"), console.error("Error sending message:", e);
400
+ } finally {
401
+ o(!1);
402
+ }
403
+ }, [t]),
404
+ clearMessages: e(() => {
405
+ i([]), c(null);
406
+ }, [])
407
+ };
408
+ }
409
+ //#endregion
410
+ //#region src/index.tsx
411
+ var f = (e) => {
412
+ let [t, n] = r(!1), { messages: i, isLoading: a, error: o, sendMessage: s, clearMessages: f } = d(e);
413
+ return /* @__PURE__ */ (0, c.jsxs)(c.Fragment, { children: [
414
+ /* @__PURE__ */ (0, c.jsx)(l, {
415
+ isOpen: t,
416
+ onClick: () => n(!t),
417
+ position: e.position
418
+ }),
419
+ t && /* @__PURE__ */ (0, c.jsx)(u, {
420
+ messages: i,
421
+ isLoading: a,
422
+ onSendMessage: s,
423
+ onClose: () => n(!1),
424
+ position: e.position
425
+ }),
426
+ o && /* @__PURE__ */ (0, c.jsxs)("div", {
427
+ className: "fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-lg shadow-lg z-50",
428
+ children: ["Error: ", o]
429
+ })
430
+ ] });
431
+ };
432
+ //#endregion
433
+ export { f as CodingAssistant, f as default };
@@ -0,0 +1,6 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`react`)):typeof define==`function`&&define.amd?define([`exports`,`react`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.CodingAssistant={},e.React))})(this,function(e,t){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),l=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};t=((e,t,i)=>(i=e==null?{}:n(o(e)),l(t||!e||!e.__esModule?r(i,`default`,{value:e,enumerable:!0}):i,e)))(t);var u=c((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),d=c((e=>{process.env.NODE_ENV!==`production`&&(function(){function t(e){if(e==null)return null;if(typeof e==`function`)return e.$$typeof===O?null:e.displayName||e.name||null;if(typeof e==`string`)return e;switch(e){case _:return`Fragment`;case y:return`Profiler`;case v:return`StrictMode`;case C:return`Suspense`;case w:return`SuspenseList`;case D:return`Activity`}if(typeof e==`object`)switch(typeof e.tag==`number`&&console.error(`Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.`),e.$$typeof){case g:return`Portal`;case x:return e.displayName||`Context`;case b:return(e._context.displayName||`Context`)+`.Consumer`;case S:var n=e.render;return e=e.displayName,e||=(e=n.displayName||n.name||``,e===``?`ForwardRef`:`ForwardRef(`+e+`)`),e;case T:return n=e.displayName||null,n===null?t(e.type)||`Memo`:n;case E:n=e._payload,e=e._init;try{return t(e(n))}catch{}}return null}function n(e){return``+e}function r(e){try{n(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,i=typeof Symbol==`function`&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||`Object`;return r.call(t,`The provided key is an unsupported type %s. This value must be coerced to a string before using it here.`,i),n(e)}}function i(e){if(e===_)return`<>`;if(typeof e==`object`&&e&&e.$$typeof===E)return`<...>`;try{var n=t(e);return n?`<`+n+`>`:`<...>`}catch{return`<...>`}}function a(){var e=k.A;return e===null?null:e.getOwner()}function o(){return Error(`react-stack-top-frame`)}function s(e){if(A.call(e,`key`)){var t=Object.getOwnPropertyDescriptor(e,`key`).get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function c(e,t){function n(){N||(N=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}n.isReactWarning=!0,Object.defineProperty(e,`key`,{get:n,configurable:!0})}function l(){var e=t(this.type);return P[e]||(P[e]=!0,console.error(`Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.`)),e=this.props.ref,e===void 0?null:e}function u(e,t,n,r,i,a){var o=n.ref;return e={$$typeof:h,type:e,key:t,props:n,_owner:r},(o===void 0?null:o)===null?Object.defineProperty(e,`ref`,{enumerable:!1,value:null}):Object.defineProperty(e,`ref`,{enumerable:!1,get:l}),e._store={},Object.defineProperty(e._store,`validated`,{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,`_debugInfo`,{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,`_debugStack`,{configurable:!1,enumerable:!1,writable:!0,value:i}),Object.defineProperty(e,`_debugTask`,{configurable:!1,enumerable:!1,writable:!0,value:a}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function d(e,n,i,o,l,d){var p=n.children;if(p!==void 0)if(o)if(j(p)){for(o=0;o<p.length;o++)f(p[o]);Object.freeze&&Object.freeze(p)}else console.error(`React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.`);else f(p);if(A.call(n,`key`)){p=t(e);var m=Object.keys(n).filter(function(e){return e!==`key`});o=0<m.length?`{key: someKey, `+m.join(`: ..., `)+`: ...}`:`{key: someKey}`,L[p+o]||(m=0<m.length?`{`+m.join(`: ..., `)+`: ...}`:`{}`,console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,o,p,m,p),L[p+o]=!0)}if(p=null,i!==void 0&&(r(i),p=``+i),s(n)&&(r(n.key),p=``+n.key),`key`in n)for(var h in i={},n)h!==`key`&&(i[h]=n[h]);else i=n;return p&&c(i,typeof e==`function`?e.displayName||e.name||`Unknown`:e),u(e,p,i,a(),l,d)}function f(e){p(e)?e._store&&(e._store.validated=1):typeof e==`object`&&e&&e.$$typeof===E&&(e._payload.status===`fulfilled`?p(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function p(e){return typeof e==`object`&&!!e&&e.$$typeof===h}var m=require(`react`),h=Symbol.for(`react.transitional.element`),g=Symbol.for(`react.portal`),_=Symbol.for(`react.fragment`),v=Symbol.for(`react.strict_mode`),y=Symbol.for(`react.profiler`),b=Symbol.for(`react.consumer`),x=Symbol.for(`react.context`),S=Symbol.for(`react.forward_ref`),C=Symbol.for(`react.suspense`),w=Symbol.for(`react.suspense_list`),T=Symbol.for(`react.memo`),E=Symbol.for(`react.lazy`),D=Symbol.for(`react.activity`),O=Symbol.for(`react.client.reference`),k=m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A=Object.prototype.hasOwnProperty,j=Array.isArray,M=console.createTask?console.createTask:function(){return null};m={react_stack_bottom_frame:function(e){return e()}};var N,P={},F=m.react_stack_bottom_frame.bind(m,o)(),I=M(i(o)),L={};e.Fragment=_,e.jsx=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!1,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)},e.jsxs=function(e,t,n){var r=1e4>k.recentlyCreatedOwnerStacks++;return d(e,t,n,!0,r?Error(`react-stack-top-frame`):F,r?M(i(e)):I)}})()})),f=c(((e,t)=>{process.env.NODE_ENV===`production`?t.exports=u():t.exports=d()}))(),p=({isOpen:e,onClick:t,position:n=`bottom-right`})=>(0,f.jsx)(`button`,{onClick:t,className:`fixed ${n===`bottom-right`?`right-6 bottom-6`:`left-6 bottom-6`} w-14 h-14 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full shadow-lg hover:shadow-xl transform hover:scale-110 transition-all duration-300 flex items-center justify-center z-50`,"aria-label":`Toggle chat`,children:e?(0,f.jsx)(`svg`,{className:`w-6 h-6`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,children:(0,f.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M6 18L18 6M6 6l12 12`})}):(0,f.jsx)(`svg`,{className:`w-6 h-6`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,children:(0,f.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z`})})}),m=({messages:e,isLoading:n,onSendMessage:r,onClose:i,position:a=`bottom-right`})=>{let[o,s]=(0,t.useState)(``),c=(0,t.useRef)(null),l=()=>{c.current?.scrollIntoView({behavior:`smooth`})};(0,t.useEffect)(()=>{l()},[e]);let u=e=>{e.preventDefault(),o.trim()&&!n&&(r(o.trim()),s(``))};return(0,f.jsxs)(`div`,{className:`fixed bottom-20 ${a===`bottom-right`?`right-0`:`left-0`} w-96 h-[500px] bg-white rounded-lg shadow-2xl flex flex-col overflow-hidden border border-gray-200`,children:[(0,f.jsxs)(`div`,{className:`bg-gradient-to-r from-blue-500 to-purple-600 text-white px-4 py-3 flex justify-between items-center`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`h3`,{className:`font-semibold text-lg`,children:`AI Coding Assistant`}),(0,f.jsx)(`p`,{className:`text-xs text-blue-100`,children:`Ask me about your code`})]}),(0,f.jsx)(`button`,{onClick:i,className:`text-white hover:bg-white/20 rounded-full p-1 transition-colors`,"aria-label":`Close chat`,children:(0,f.jsx)(`svg`,{className:`w-5 h-5`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,children:(0,f.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M6 18L18 6M6 6l12 12`})})})]}),(0,f.jsxs)(`div`,{className:`flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50`,children:[e.length===0?(0,f.jsxs)(`div`,{className:`text-center text-gray-500 mt-10`,children:[(0,f.jsx)(`svg`,{className:`w-16 h-16 mx-auto mb-4 text-gray-300`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,children:(0,f.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:1.5,d:`M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z`})}),(0,f.jsx)(`p`,{className:`text-sm`,children:`你好!我是你的代码助手。`}),(0,f.jsx)(`p`,{className:`text-xs mt-1`,children:`问我关于这个代码库的任何问题`})]}):e.map(e=>(0,f.jsx)(`div`,{className:`flex ${e.role===`user`?`justify-end`:`justify-start`}`,children:(0,f.jsxs)(`div`,{className:`max-w-[80%] rounded-2xl px-4 py-2 ${e.role===`user`?`bg-blue-500 text-white`:`bg-white text-gray-800 border border-gray-200`}`,children:[(0,f.jsx)(`p`,{className:`text-sm whitespace-pre-wrap break-words`,children:e.content}),(0,f.jsx)(`span`,{className:`text-xs opacity-70 mt-1 block`,children:new Date(e.timestamp).toLocaleTimeString()})]})},e.id)),n&&(0,f.jsx)(`div`,{className:`flex justify-start`,children:(0,f.jsx)(`div`,{className:`bg-white border border-gray-200 rounded-2xl px-4 py-3`,children:(0,f.jsxs)(`div`,{className:`flex space-x-2`,children:[(0,f.jsx)(`div`,{className:`w-2 h-2 bg-gray-400 rounded-full animate-bounce`,style:{animationDelay:`0ms`}}),(0,f.jsx)(`div`,{className:`w-2 h-2 bg-gray-400 rounded-full animate-bounce`,style:{animationDelay:`150ms`}}),(0,f.jsx)(`div`,{className:`w-2 h-2 bg-gray-400 rounded-full animate-bounce`,style:{animationDelay:`300ms`}})]})})}),(0,f.jsx)(`div`,{ref:c})]}),(0,f.jsx)(`form`,{onSubmit:u,className:`p-4 bg-white border-t border-gray-200`,children:(0,f.jsxs)(`div`,{className:`flex space-x-2`,children:[(0,f.jsx)(`input`,{type:`text`,value:o,onChange:e=>s(e.target.value),placeholder:`输入你的问题...`,disabled:n,className:`flex-1 px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed`}),(0,f.jsx)(`button`,{type:`submit`,disabled:n||!o.trim(),className:`px-4 py-2 bg-blue-500 text-white rounded-full hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors`,children:n?(0,f.jsxs)(`svg`,{className:`w-5 h-5 animate-spin`,fill:`none`,viewBox:`0 0 24 24`,children:[(0,f.jsx)(`circle`,{className:`opacity-25`,cx:`12`,cy:`12`,r:`10`,stroke:`currentColor`,strokeWidth:`4`}),(0,f.jsx)(`path`,{className:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z`})]}):(0,f.jsx)(`svg`,{className:`w-5 h-5`,fill:`none`,stroke:`currentColor`,viewBox:`0 0 24 24`,children:(0,f.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:2,d:`M12 19l9 2-9-18-9 18 9-2zm0 0v-8`})})})]})})]})};function h(e){let[n,r]=(0,t.useState)([]),[i,a]=(0,t.useState)(!1),[o,s]=(0,t.useState)(null);return{messages:n,isLoading:i,error:o,sendMessage:(0,t.useCallback)(async t=>{let n={id:Date.now().toString(),role:`user`,content:t,timestamp:new Date};r(e=>[...e,n]),a(!0),s(null);try{let n=await fetch(`${e.apiUrl}/api/chat`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({message:t,repoUrl:e.repoUrl,accessToken:e.accessToken,branch:e.branch})});if(!n.ok)throw Error(`HTTP error! status: ${n.status}`);let i=await n.json(),a={id:(Date.now()+1).toString(),role:`assistant`,content:i.response,timestamp:new Date};r(e=>[...e,a])}catch(e){s(e instanceof Error?e.message:`Unknown error`),console.error(`Error sending message:`,e)}finally{a(!1)}},[e]),clearMessages:(0,t.useCallback)(()=>{r([]),s(null)},[])}}var g=e=>{let[n,r]=(0,t.useState)(!1),{messages:i,isLoading:a,error:o,sendMessage:s,clearMessages:c}=h(e);return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(p,{isOpen:n,onClick:()=>r(!n),position:e.position}),n&&(0,f.jsx)(m,{messages:i,isLoading:a,onSendMessage:s,onClose:()=>r(!1),position:e.position}),o&&(0,f.jsxs)(`div`,{className:`fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-lg shadow-lg z-50`,children:[`Error: `,o]})]})};e.CodingAssistant=g,e.default=g});
@@ -0,0 +1,208 @@
1
+ /**
2
+ * AI Coding Assistant - 类型定义文件
3
+ *
4
+ * 文件说明:
5
+ * 定义了整个前端应用使用的 TypeScript 接口和类型
6
+ *
7
+ * 作用:
8
+ * 1. 提供类型安全,避免运行时错误
9
+ * 2. 作为代码文档,说明数据结构
10
+ * 3. 在组件和 Hook 之间共享类型定义
11
+ */
12
+ /**
13
+ * 编码助手配置接口
14
+ *
15
+ * 定义了初始化 AI 助手所需的配置参数
16
+ *
17
+ * 使用场景:
18
+ * 在使用 CodingAssistant 组件时传入配置
19
+ *
20
+ * 示例:
21
+ * ```tsx
22
+ * <CodingAssistant
23
+ * apiUrl="http://localhost:3001"
24
+ * repoUrl="https://github.com/user/repo.git"
25
+ * branch="main"
26
+ * position="bottom-right"
27
+ * />
28
+ * ```
29
+ */
30
+ export interface CodingAssistantConfig {
31
+ /**
32
+ * 后端 API 的基础 URL
33
+ *
34
+ * 说明:
35
+ * - 这是后端服务的地址
36
+ * - 用于发送聊天请求
37
+ * - 必须包含协议(http:// 或 https://)
38
+ *
39
+ * 示例值:
40
+ * - 开发环境: "http://localhost:3001"
41
+ * - 生产环境: "https://api.your-domain.com"
42
+ */
43
+ apiUrl: string;
44
+ /**
45
+ * Git 仓库的 URL
46
+ *
47
+ * 说明:
48
+ * - 要分析的代码仓库地址
49
+ * - 支持 HTTPS 和 SSH 格式
50
+ * - 私有仓库需要提供 accessToken
51
+ *
52
+ * 示例值:
53
+ * - HTTPS: "https://github.com/user/repo.git"
54
+ * - SSH: "git@github.com:user/repo.git"
55
+ */
56
+ repoUrl: string;
57
+ /**
58
+ * 访问令牌(可选)
59
+ *
60
+ * 说明:
61
+ * - 用于访问私有仓库
62
+ * - GitHub/GitLab 的 Personal Access Token
63
+ * - 不会暴露在前端代码中(仅作为配置传递)
64
+ *
65
+ * 安全提示:
66
+ * - 不要在公共仓库中硬编码令牌
67
+ * - 使用环境变量或配置管理
68
+ */
69
+ accessToken?: string;
70
+ /**
71
+ * Git 分支名称(可选)
72
+ *
73
+ * 说明:
74
+ * - 指定要分析的分支
75
+ * - 如果不指定,使用仓库的默认分支
76
+ *
77
+ * 常见值:
78
+ * - "main"(现代仓库的默认分支)
79
+ * - "master"(旧仓库的默认分支)
80
+ * - "develop"(开发分支)
81
+ */
82
+ branch?: string;
83
+ /**
84
+ * 悬浮按钮位置(可选)
85
+ *
86
+ * 说明:
87
+ * - 控制悬浮按钮和聊天窗口在屏幕上的位置
88
+ * - 默认值: "bottom-right"
89
+ *
90
+ * 可选值:
91
+ * - "bottom-right": 右下角(推荐)
92
+ * - "bottom-left": 左下角
93
+ */
94
+ position?: 'bottom-right' | 'bottom-left';
95
+ }
96
+ /**
97
+ * 聊天消息接口
98
+ *
99
+ * 定义了单条聊天消息的数据结构
100
+ *
101
+ * 消息流程:
102
+ * 用户输入 → 创建 user 消息 → 发送到后端 → 接收响应 → 创建 assistant 消息
103
+ */
104
+ export interface ChatMessage {
105
+ /**
106
+ * 消息的唯一标识符
107
+ *
108
+ * 说明:
109
+ * - 用于 React 的 key 属性,优化列表渲染
110
+ * - 使用时间戳生成,保证唯一性
111
+ *
112
+ * 生成方式:
113
+ * Date.now().toString()
114
+ */
115
+ id: string;
116
+ /**
117
+ * 消息角色
118
+ *
119
+ * 说明:
120
+ * - 标识消息的发送者
121
+ *
122
+ * 可选值:
123
+ * - "user": 用户发送的消息
124
+ * - "assistant": AI 助手回复的消息
125
+ */
126
+ role: 'user' | 'assistant';
127
+ /**
128
+ * 消息内容
129
+ *
130
+ * 说明:
131
+ * - 消息的文本内容
132
+ * - 支持 Markdown 格式(前端渲染时解析)
133
+ * - 可以包含代码块、链接等
134
+ *
135
+ * 示例:
136
+ * - 用户: "这个项目是做什么的?"
137
+ * - AI: "这是一个 TypeScript 项目..."
138
+ */
139
+ content: string;
140
+ /**
141
+ * 消息时间戳
142
+ *
143
+ * 说明:
144
+ * - 记录消息创建的时间
145
+ * - 用于显示消息时间
146
+ * - 格式: JavaScript Date 对象
147
+ *
148
+ * 显示格式:
149
+ * 在 UI 中使用 toLocaleTimeString() 转换为本地时间
150
+ * 例如: "14:30:25"
151
+ */
152
+ timestamp: Date;
153
+ }
154
+ /**
155
+ * 聊天响应接口
156
+ *
157
+ * 定义了后端 API 返回的数据格式
158
+ *
159
+ * API 端点:
160
+ * POST /api/chat
161
+ *
162
+ * 请求示例:
163
+ * ```json
164
+ * {
165
+ * "message": "这个项目是做什么的?",
166
+ * "repoUrl": "https://github.com/user/repo.git"
167
+ * }
168
+ * ```
169
+ *
170
+ * 响应示例:
171
+ * ```json
172
+ * {
173
+ * "response": "这是一个 TypeScript 项目...",
174
+ * "sources": ["src/index.ts", "src/utils.ts"]
175
+ * }
176
+ * ```
177
+ */
178
+ export interface ChatResponse {
179
+ /**
180
+ * AI 的回答内容
181
+ *
182
+ * 说明:
183
+ * - AI 生成的文本回复
184
+ * - 可能包含 Markdown 格式
185
+ * - 可能包含代码块
186
+ *
187
+ * 前端处理:
188
+ * 需要使用 Markdown 渲染器显示(如 react-markdown)
189
+ */
190
+ response: string;
191
+ /**
192
+ * 引用的源文件列表(可选)
193
+ *
194
+ * 说明:
195
+ * - AI 回答时参考的代码文件
196
+ * - 用于追溯回答的依据
197
+ * - 增强回答的可信度
198
+ *
199
+ * 用途:
200
+ * - 显示在聊天界面中
201
+ * - 提供文件链接(可点击跳转)
202
+ * - 帮助用户理解上下文
203
+ *
204
+ * 示例:
205
+ * ["src/index.ts", "src/components/App.tsx", "backend/api.ts"]
206
+ */
207
+ sources?: string[];
208
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@ai-coding-agent/react-assistant",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered coding assistant for React applications",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "dev": "vite",
16
+ "build": "vite build --mode lib",
17
+ "preview": "vite preview",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": [
21
+ "ai",
22
+ "assistant",
23
+ "coding",
24
+ "react",
25
+ "chatbot",
26
+ "code-analysis",
27
+ "developer-tools"
28
+ ],
29
+ "author": "Chenyuanhang <2468111274@qq.com>",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/15736108323/aiCodingAgent.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/15736108323/aiCodingAgent/issues"
37
+ },
38
+ "homepage": "https://github.com/15736108323/aiCodingAgent#readme",
39
+ "peerDependencies": {
40
+ "react": "^18.0.0 || ^19.0.0",
41
+ "react-dom": "^18.0.0 || ^19.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/react": "^19.2.14",
45
+ "@types/react-dom": "^19.2.3",
46
+ "@vitejs/plugin-react": "^6.0.1",
47
+ "autoprefixer": "^10.4.21",
48
+ "postcss": "^8.5.3",
49
+ "tailwindcss": "^3.4.1",
50
+ "typescript": "~5.9.3",
51
+ "vite": "^8.0.1",
52
+ "vite-plugin-dts": "^4.5.3"
53
+ }
54
+ }