hydro-ai-helper 1.8.5

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 (182) hide show
  1. package/.eslintrc.json +31 -0
  2. package/.github/workflows/sync-releases-to-gitee.yml +119 -0
  3. package/.github/workflows/sync-to-gitee.yml +53 -0
  4. package/.vercelignore +31 -0
  5. package/CONTRIBUTING.md +165 -0
  6. package/README.md +273 -0
  7. package/TROUBLESHOOTING.md +355 -0
  8. package/api/badge-active.ts +88 -0
  9. package/api/badge-installs.ts +75 -0
  10. package/api/report.ts +242 -0
  11. package/assets/screenshots/1.png +0 -0
  12. package/assets/screenshots/2.png +0 -0
  13. package/assets/screenshots/3.png +0 -0
  14. package/assets/screenshots/4.png +0 -0
  15. package/assets/screenshots/5.png +0 -0
  16. package/assets/screenshots/6.png +0 -0
  17. package/assets/screenshots/7.png +0 -0
  18. package/dist/__tests__/__mocks__/hydrooj.js +29 -0
  19. package/dist/__tests__/__mocks__/hydrooj.js.map +1 -0
  20. package/dist/constants/jailbreakRules.js +29 -0
  21. package/dist/constants/jailbreakRules.js.map +1 -0
  22. package/dist/constants/permissions.js +42 -0
  23. package/dist/constants/permissions.js.map +1 -0
  24. package/dist/handlers/adminConfigHandler.js +347 -0
  25. package/dist/handlers/adminConfigHandler.js.map +1 -0
  26. package/dist/handlers/adminHandler.js +378 -0
  27. package/dist/handlers/adminHandler.js.map +1 -0
  28. package/dist/handlers/analyticsHandler.js +433 -0
  29. package/dist/handlers/analyticsHandler.js.map +1 -0
  30. package/dist/handlers/dashboardHandler.js +22 -0
  31. package/dist/handlers/dashboardHandler.js.map +1 -0
  32. package/dist/handlers/exportHandler.js +118 -0
  33. package/dist/handlers/exportHandler.js.map +1 -0
  34. package/dist/handlers/studentHandler.js +430 -0
  35. package/dist/handlers/studentHandler.js.map +1 -0
  36. package/dist/handlers/teacherHandler.js +246 -0
  37. package/dist/handlers/teacherHandler.js.map +1 -0
  38. package/dist/handlers/testHandler.js +27 -0
  39. package/dist/handlers/testHandler.js.map +1 -0
  40. package/dist/handlers/updateHandler.js +84 -0
  41. package/dist/handlers/updateHandler.js.map +1 -0
  42. package/dist/handlers/versionHandler.js +61 -0
  43. package/dist/handlers/versionHandler.js.map +1 -0
  44. package/dist/index.d.ts +22 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +151 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib/crypto.js +110 -0
  49. package/dist/lib/crypto.js.map +1 -0
  50. package/dist/lib/httpHelpers.js +61 -0
  51. package/dist/lib/httpHelpers.js.map +1 -0
  52. package/dist/lib/queryHelpers.js +81 -0
  53. package/dist/lib/queryHelpers.js.map +1 -0
  54. package/dist/models/aiConfig.js +255 -0
  55. package/dist/models/aiConfig.js.map +1 -0
  56. package/dist/models/conversation.js +209 -0
  57. package/dist/models/conversation.js.map +1 -0
  58. package/dist/models/jailbreakLog.js +68 -0
  59. package/dist/models/jailbreakLog.js.map +1 -0
  60. package/dist/models/message.js +174 -0
  61. package/dist/models/message.js.map +1 -0
  62. package/dist/models/pluginInstall.js +95 -0
  63. package/dist/models/pluginInstall.js.map +1 -0
  64. package/dist/models/rateLimitRecord.js +54 -0
  65. package/dist/models/rateLimitRecord.js.map +1 -0
  66. package/dist/models/versionCache.js +89 -0
  67. package/dist/models/versionCache.js.map +1 -0
  68. package/dist/services/effectivenessService.js +131 -0
  69. package/dist/services/effectivenessService.js.map +1 -0
  70. package/dist/services/exportService.js +152 -0
  71. package/dist/services/exportService.js.map +1 -0
  72. package/dist/services/migrationService.js +90 -0
  73. package/dist/services/migrationService.js.map +1 -0
  74. package/dist/services/openaiClient.js +412 -0
  75. package/dist/services/openaiClient.js.map +1 -0
  76. package/dist/services/promptService.js +467 -0
  77. package/dist/services/promptService.js.map +1 -0
  78. package/dist/services/rateLimitService.js +108 -0
  79. package/dist/services/rateLimitService.js.map +1 -0
  80. package/dist/services/telemetryService.js +173 -0
  81. package/dist/services/telemetryService.js.map +1 -0
  82. package/dist/services/updateService.js +388 -0
  83. package/dist/services/updateService.js.map +1 -0
  84. package/dist/services/versionService.js +289 -0
  85. package/dist/services/versionService.js.map +1 -0
  86. package/dist/types/hydrooj.js +7 -0
  87. package/dist/types/hydrooj.js.map +1 -0
  88. package/dist/types/mongo.js +7 -0
  89. package/dist/types/mongo.js.map +1 -0
  90. package/dist/utils/domainHelper.js +16 -0
  91. package/dist/utils/domainHelper.js.map +1 -0
  92. package/dist/utils/mongo.js +16 -0
  93. package/dist/utils/mongo.js.map +1 -0
  94. package/frontend/admin/ConfigPanel.tsx +1379 -0
  95. package/frontend/admin/VersionBadge.tsx +655 -0
  96. package/frontend/ai_helper.page.tsx +52 -0
  97. package/frontend/ai_helper_admin_config.page.tsx +47 -0
  98. package/frontend/ai_helper_analytics.page.tsx +52 -0
  99. package/frontend/ai_helper_conversation_detail.page.tsx +55 -0
  100. package/frontend/ai_helper_conversations.page.tsx +56 -0
  101. package/frontend/components/AIHelperDashboard.tsx +125 -0
  102. package/frontend/problem_detail.page.tsx +395 -0
  103. package/frontend/student/AIAssistantPanel.tsx +1839 -0
  104. package/frontend/student/AIChatPanel.tsx +944 -0
  105. package/frontend/student/ThreeColumnLayout.tsx +240 -0
  106. package/frontend/teacher/AnalyticsPage.tsx +614 -0
  107. package/frontend/teacher/ConversationDetail.tsx +504 -0
  108. package/frontend/teacher/ConversationList.tsx +568 -0
  109. package/frontend/teacher/ExportDialog.tsx +291 -0
  110. package/frontend/test.page.ts +88 -0
  111. package/frontend/utils/domainUtils.ts +37 -0
  112. package/index.js +6 -0
  113. package/jest.config.js +17 -0
  114. package/lib/mongodb.ts +60 -0
  115. package/locales/en.yaml +4 -0
  116. package/locales/zh.yaml +77 -0
  117. package/package.json +48 -0
  118. package/public/.gitkeep +0 -0
  119. package/public/README.md +1 -0
  120. package/src/__tests__/__mocks__/hydrooj.ts +27 -0
  121. package/src/__tests__/services/exportService.test.ts +156 -0
  122. package/src/__tests__/services/promptService.test.ts +141 -0
  123. package/src/__tests__/services/rateLimitService.test.ts +145 -0
  124. package/src/__tests__/utils/domainHelper.test.ts +47 -0
  125. package/src/constants/jailbreakRules.ts +28 -0
  126. package/src/constants/permissions.ts +43 -0
  127. package/src/handlers/adminConfigHandler.ts +363 -0
  128. package/src/handlers/adminHandler.ts +427 -0
  129. package/src/handlers/analyticsHandler.ts +517 -0
  130. package/src/handlers/dashboardHandler.ts +20 -0
  131. package/src/handlers/exportHandler.ts +134 -0
  132. package/src/handlers/studentHandler.ts +483 -0
  133. package/src/handlers/teacherHandler.ts +317 -0
  134. package/src/handlers/testHandler.ts +25 -0
  135. package/src/handlers/updateHandler.ts +85 -0
  136. package/src/handlers/versionHandler.ts +62 -0
  137. package/src/index.ts +198 -0
  138. package/src/lib/crypto.ts +125 -0
  139. package/src/lib/httpHelpers.ts +75 -0
  140. package/src/lib/queryHelpers.ts +108 -0
  141. package/src/models/aiConfig.ts +375 -0
  142. package/src/models/conversation.ts +328 -0
  143. package/src/models/jailbreakLog.ts +108 -0
  144. package/src/models/message.ts +246 -0
  145. package/src/models/pluginInstall.ts +138 -0
  146. package/src/models/rateLimitRecord.ts +81 -0
  147. package/src/models/versionCache.ts +127 -0
  148. package/src/services/effectivenessService.ts +161 -0
  149. package/src/services/exportService.ts +191 -0
  150. package/src/services/migrationService.ts +111 -0
  151. package/src/services/openaiClient.ts +548 -0
  152. package/src/services/promptService.ts +573 -0
  153. package/src/services/rateLimitService.ts +122 -0
  154. package/src/services/telemetryService.ts +218 -0
  155. package/src/services/updateService.ts +441 -0
  156. package/src/services/versionService.ts +351 -0
  157. package/src/types/hydrooj-extensions.d.ts +55 -0
  158. package/src/types/hydrooj.ts +38 -0
  159. package/src/types/mongo.ts +23 -0
  160. package/src/utils/domainHelper.ts +16 -0
  161. package/src/utils/mongo.ts +15 -0
  162. package/templates/ai-helper/admin_config.html +8 -0
  163. package/templates/ai-helper/analytics.html +8 -0
  164. package/templates/ai-helper/dashboard.html +8 -0
  165. package/templates/ai-helper/teacher_conversation_detail.html +8 -0
  166. package/templates/ai-helper/teacher_conversations.html +8 -0
  167. package/tests/integration/README.md +86 -0
  168. package/tests/integration/curl/01-admin-config-set.sh +56 -0
  169. package/tests/integration/curl/02-student-chat.sh +67 -0
  170. package/tests/integration/curl/03-teacher-list.sh +52 -0
  171. package/tests/integration/curl/04-teacher-detail.sh +37 -0
  172. package/tests/integration/curl/05-export.sh +49 -0
  173. package/tests/integration/curl/06-analytics.sh +43 -0
  174. package/tests/performance/README.md +85 -0
  175. package/tests/performance/data/prepare-sample-data.md +57 -0
  176. package/tests/performance/scripts/export-csv-k6.js +41 -0
  177. package/tests/performance/scripts/student-chat-k6.js +44 -0
  178. package/tests/performance/scripts/teacher-conversations-k6.js +33 -0
  179. package/tsconfig.json +42 -0
  180. package/tsconfig.vercel.json +27 -0
  181. package/types/hydrooj/index.d.ts +73 -0
  182. package/vercel.json +3 -0
@@ -0,0 +1,240 @@
1
+ /**
2
+ * 三列布局组件 - LeetCode 风格
3
+ * 左侧:题目描述
4
+ * 中间:代码编辑器(使用 HydroOJ 原有的 Scratchpad)
5
+ * 右侧:AI 学习助手
6
+ */
7
+
8
+ import React, { useState, useEffect, useRef } from 'react';
9
+ import { AIChatPanel } from './AIChatPanel';
10
+
11
+ interface ThreeColumnLayoutProps {
12
+ problemId: string;
13
+ }
14
+
15
+ /**
16
+ * 三列布局组件
17
+ * 检测屏幕宽度 >= 1200px 时启用三列模式
18
+ */
19
+ export const ThreeColumnLayout: React.FC<ThreeColumnLayoutProps> = ({ problemId }) => {
20
+ const [leftWidth, setLeftWidth] = useState(33);
21
+ const [rightWidth, setRightWidth] = useState(34);
22
+ const [isDraggingLeft, setIsDraggingLeft] = useState(false);
23
+ const [isDraggingRight, setIsDraggingRight] = useState(false);
24
+ const containerRef = useRef<HTMLDivElement>(null);
25
+
26
+ // 处理左侧分割线拖拽
27
+ useEffect(() => {
28
+ if (!isDraggingLeft) return;
29
+
30
+ const handleMouseMove = (e: MouseEvent) => {
31
+ if (!containerRef.current) return;
32
+ const rect = containerRef.current.getBoundingClientRect();
33
+ const newLeftWidth = ((e.clientX - rect.left) / rect.width) * 100;
34
+ // 限制在 20% - 50% 之间
35
+ setLeftWidth(Math.max(20, Math.min(50, newLeftWidth)));
36
+ };
37
+
38
+ const handleMouseUp = () => {
39
+ setIsDraggingLeft(false);
40
+ document.body.style.cursor = '';
41
+ document.body.style.userSelect = '';
42
+ };
43
+
44
+ document.body.style.cursor = 'col-resize';
45
+ document.body.style.userSelect = 'none';
46
+ document.addEventListener('mousemove', handleMouseMove);
47
+ document.addEventListener('mouseup', handleMouseUp);
48
+
49
+ return () => {
50
+ document.removeEventListener('mousemove', handleMouseMove);
51
+ document.removeEventListener('mouseup', handleMouseUp);
52
+ };
53
+ }, [isDraggingLeft]);
54
+
55
+ // 处理右侧分割线拖拽
56
+ useEffect(() => {
57
+ if (!isDraggingRight) return;
58
+
59
+ const handleMouseMove = (e: MouseEvent) => {
60
+ if (!containerRef.current) return;
61
+ const rect = containerRef.current.getBoundingClientRect();
62
+ const newRightWidth = ((rect.right - e.clientX) / rect.width) * 100;
63
+ // 限制在 25% - 45% 之间
64
+ setRightWidth(Math.max(25, Math.min(45, newRightWidth)));
65
+ };
66
+
67
+ const handleMouseUp = () => {
68
+ setIsDraggingRight(false);
69
+ document.body.style.cursor = '';
70
+ document.body.style.userSelect = '';
71
+ };
72
+
73
+ document.body.style.cursor = 'col-resize';
74
+ document.body.style.userSelect = 'none';
75
+ document.addEventListener('mousemove', handleMouseMove);
76
+ document.addEventListener('mouseup', handleMouseUp);
77
+
78
+ return () => {
79
+ document.removeEventListener('mousemove', handleMouseMove);
80
+ document.removeEventListener('mouseup', handleMouseUp);
81
+ };
82
+ }, [isDraggingRight]);
83
+
84
+ const middleWidth = 100 - leftWidth - rightWidth;
85
+
86
+ return (
87
+ <div
88
+ ref={containerRef}
89
+ style={{
90
+ display: 'flex',
91
+ width: '100%',
92
+ height: 'calc(100vh - 60px)', // 减去顶部导航栏高度
93
+ background: '#f8fafc',
94
+ overflow: 'hidden'
95
+ }}
96
+ >
97
+ {/* 左侧:题目描述 */}
98
+ <div
99
+ style={{
100
+ width: `${leftWidth}%`,
101
+ height: '100%',
102
+ display: 'flex',
103
+ flexDirection: 'column',
104
+ background: '#ffffff',
105
+ borderRight: '1px solid #e2e8f0'
106
+ }}
107
+ >
108
+ <div style={{
109
+ padding: '12px 16px',
110
+ borderBottom: '1px solid #e2e8f0',
111
+ background: '#f8fafc',
112
+ fontWeight: '600',
113
+ fontSize: '14px',
114
+ color: '#1e293b',
115
+ display: 'flex',
116
+ alignItems: 'center',
117
+ gap: '8px'
118
+ }}>
119
+ <span style={{ fontSize: '16px' }}>📄</span>
120
+ 题目描述
121
+ </div>
122
+ <div
123
+ id="problem-content-wrapper"
124
+ style={{
125
+ flex: 1,
126
+ overflowY: 'auto',
127
+ padding: '16px'
128
+ }}
129
+ >
130
+ {/* 题目内容将通过 DOM 操作移动到这里 */}
131
+ </div>
132
+ </div>
133
+
134
+ {/* 左侧分割线 */}
135
+ <div
136
+ onMouseDown={() => setIsDraggingLeft(true)}
137
+ style={{
138
+ width: '6px',
139
+ cursor: 'col-resize',
140
+ background: isDraggingLeft ? '#6366f1' : 'transparent',
141
+ transition: 'background 0.2s',
142
+ display: 'flex',
143
+ alignItems: 'center',
144
+ justifyContent: 'center'
145
+ }}
146
+ onMouseEnter={(e) => {
147
+ if (!isDraggingLeft) e.currentTarget.style.background = '#e2e8f0';
148
+ }}
149
+ onMouseLeave={(e) => {
150
+ if (!isDraggingLeft) e.currentTarget.style.background = 'transparent';
151
+ }}
152
+ >
153
+ <div style={{
154
+ width: '2px',
155
+ height: '40px',
156
+ background: '#cbd5e1',
157
+ borderRadius: '1px'
158
+ }} />
159
+ </div>
160
+
161
+ {/* 中间:代码编辑器 */}
162
+ <div
163
+ style={{
164
+ width: `${middleWidth}%`,
165
+ height: '100%',
166
+ display: 'flex',
167
+ flexDirection: 'column',
168
+ background: '#ffffff',
169
+ borderRight: '1px solid #e2e8f0'
170
+ }}
171
+ >
172
+ <div style={{
173
+ padding: '12px 16px',
174
+ borderBottom: '1px solid #e2e8f0',
175
+ background: '#f8fafc',
176
+ fontWeight: '600',
177
+ fontSize: '14px',
178
+ color: '#1e293b',
179
+ display: 'flex',
180
+ alignItems: 'center',
181
+ gap: '8px'
182
+ }}>
183
+ <span style={{ fontSize: '16px' }}>💻</span>
184
+ 代码编辑器
185
+ </div>
186
+ <div
187
+ id="code-editor-wrapper"
188
+ style={{
189
+ flex: 1,
190
+ overflow: 'hidden'
191
+ }}
192
+ >
193
+ {/* 代码编辑器将通过 DOM 操作移动到这里 */}
194
+ </div>
195
+ </div>
196
+
197
+ {/* 右侧分割线 */}
198
+ <div
199
+ onMouseDown={() => setIsDraggingRight(true)}
200
+ style={{
201
+ width: '6px',
202
+ cursor: 'col-resize',
203
+ background: isDraggingRight ? '#6366f1' : 'transparent',
204
+ transition: 'background 0.2s',
205
+ display: 'flex',
206
+ alignItems: 'center',
207
+ justifyContent: 'center'
208
+ }}
209
+ onMouseEnter={(e) => {
210
+ if (!isDraggingRight) e.currentTarget.style.background = '#e2e8f0';
211
+ }}
212
+ onMouseLeave={(e) => {
213
+ if (!isDraggingRight) e.currentTarget.style.background = 'transparent';
214
+ }}
215
+ >
216
+ <div style={{
217
+ width: '2px',
218
+ height: '40px',
219
+ background: '#cbd5e1',
220
+ borderRadius: '1px'
221
+ }} />
222
+ </div>
223
+
224
+ {/* 右侧:AI 学习助手 */}
225
+ <div
226
+ style={{
227
+ width: `${rightWidth}%`,
228
+ height: '100%',
229
+ display: 'flex',
230
+ flexDirection: 'column',
231
+ background: '#ffffff'
232
+ }}
233
+ >
234
+ <AIChatPanel problemId={problemId} />
235
+ </div>
236
+ </div>
237
+ );
238
+ };
239
+
240
+ export default ThreeColumnLayout;