@fe-free/ai 4.1.28 → 4.1.30

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @fe-free/ai
2
2
 
3
+ ## 4.1.30
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: ai
8
+ - @fe-free/core@4.1.30
9
+ - @fe-free/icons@4.1.30
10
+ - @fe-free/tool@4.1.30
11
+
12
+ ## 4.1.29
13
+
14
+ ### Patch Changes
15
+
16
+ - feat: ai
17
+ - @fe-free/core@4.1.29
18
+ - @fe-free/icons@4.1.29
19
+ - @fe-free/tool@4.1.29
20
+
3
21
  ## 4.1.28
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/ai",
3
- "version": "4.1.28",
3
+ "version": "4.1.30",
4
4
  "description": "",
5
5
  "main": "./src/index.ts",
6
6
  "author": "",
@@ -19,7 +19,7 @@
19
19
  "lodash-es": "^4.17.21",
20
20
  "uuid": "^13.0.0",
21
21
  "zustand": "^4.5.7",
22
- "@fe-free/core": "4.1.28"
22
+ "@fe-free/core": "4.1.30"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "antd": "^5.27.1",
@@ -29,8 +29,8 @@
29
29
  "i18next-icu": "^2.4.1",
30
30
  "react": "^19.2.0",
31
31
  "react-i18next": "^16.4.0",
32
- "@fe-free/icons": "4.1.28",
33
- "@fe-free/tool": "4.1.28"
32
+ "@fe-free/icons": "4.1.30",
33
+ "@fe-free/tool": "4.1.30"
34
34
  },
35
35
  "scripts": {
36
36
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -129,7 +129,7 @@ function Component() {
129
129
  Add New Session
130
130
  </Button>
131
131
  </div>
132
- <div className="h-[800px] w-[500px] max-w-full border border-red-500">
132
+ <div className="h-[500px] w-[500px] max-w-full border border-red-500">
133
133
  <Chat
134
134
  end={
135
135
  <div
@@ -141,6 +141,7 @@ function Component() {
141
141
  console.log('onBlur');
142
142
  }}
143
143
  >
144
+ <div>这里是 suggestion 区域这里是 suggestion 区域</div>
144
145
  <MSender
145
146
  value={senderValue}
146
147
  onChange={(v) => setSenderValue(v)}
@@ -0,0 +1,90 @@
1
+ function completeHtml(partialHtml?: string) {
2
+ if (!partialHtml) {
3
+ return '';
4
+ }
5
+
6
+ // 创建一个临时容器来解析 HTML
7
+ const tempDiv = document.createElement('div');
8
+ tempDiv.innerHTML = partialHtml;
9
+
10
+ // 浏览器会自动修正不匹配的标签(如自动闭合、忽略无效标签等)
11
+ // 但 innerHTML 不会包含 <html>, <head>, <body> 等顶层结构
12
+ // 所以我们手动构建一个完整文档
13
+
14
+ // 提取 head 内容(如果有 <head> 标签,通常不会出现在片段中,但以防万一)
15
+ let headContent = '';
16
+ const headMatch = partialHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
17
+ if (headMatch) {
18
+ headContent = headMatch[1] || '';
19
+ }
20
+
21
+ // 使用 tempDiv.innerHTML 获取浏览器修正后的 body 内容
22
+ const bodyContent = tempDiv.innerHTML;
23
+
24
+ // 构建完整 HTML
25
+ const fullHtml = `<!DOCTYPE html>
26
+ <html>
27
+ <head>
28
+ <meta charset="utf-8">
29
+ ${headContent}
30
+ </head>
31
+ <body>
32
+ ${bodyContent}
33
+ </body>
34
+ </html>`;
35
+
36
+ return fullHtml;
37
+ }
38
+
39
+ function completeJson(partialJson?: string): string {
40
+ if (!partialJson) {
41
+ return '';
42
+ }
43
+
44
+ let result = partialJson.trim();
45
+
46
+ const stack: string[] = [];
47
+ let inString = false;
48
+ let escape = false;
49
+
50
+ for (let i = 0; i < result.length; i++) {
51
+ const char = result[i];
52
+
53
+ if (inString) {
54
+ if (escape) {
55
+ escape = false;
56
+ } else if (char === '\\') {
57
+ escape = true;
58
+ } else if (char === '"') {
59
+ inString = false;
60
+ }
61
+ continue;
62
+ }
63
+
64
+ if (char === '"') {
65
+ inString = true;
66
+ continue;
67
+ }
68
+
69
+ if (char === '{') stack.push('}');
70
+ else if (char === '[') stack.push(']');
71
+ else if (char === '}' || char === ']') stack.pop();
72
+ }
73
+
74
+ // 1️⃣ 如果字符串没闭合,补一个 "
75
+ if (inString) {
76
+ result += '"';
77
+ }
78
+
79
+ // 2️⃣ 去掉末尾非法逗号
80
+ result = result.replace(/,\s*([}\]])/g, '$1');
81
+
82
+ // 3️⃣ 补齐所有缺失的 } 或 ]
83
+ while (stack.length) {
84
+ result += stack.pop();
85
+ }
86
+
87
+ return result;
88
+ }
89
+
90
+ export { completeHtml, completeJson };
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import './style.scss';
2
2
  export { Chat } from './chat';
3
3
  export { FileView, FileViewList } from './files';
4
4
  export { generateUUID } from './helper';
5
+ export { completeHtml, completeJson } from './helper/complete';
5
6
  export { MSender } from './m_sender';
6
7
  export type { MSenderProps, MSenderRef } from './m_sender';
7
8
  export { CustomMarkdown, Markdown } from './markdown';
@@ -10,7 +10,7 @@ import type { MSenderProps } from './types';
10
10
  function RecordAction(
11
11
  props: MSenderProps & { setType; refText: RefObject<HTMLTextAreaElement | null> },
12
12
  ) {
13
- const { allowSpeech, setType, refText } = props;
13
+ const { allowSpeech, setType, refText, loading } = props;
14
14
 
15
15
  const containerRef = useRef<HTMLDivElement>(null);
16
16
  const touchStartYRef = useRef<number>(0);
@@ -25,6 +25,10 @@ function RecordAction(
25
25
  // 阻止默认行为,避免触发文本选择、上下文菜单等
26
26
  e.preventDefault();
27
27
 
28
+ if (loading) {
29
+ return;
30
+ }
31
+
28
32
  await allowSpeech?.onRecordStart?.();
29
33
 
30
34
  const touch = e.touches[0];
@@ -35,7 +39,7 @@ function RecordAction(
35
39
 
36
40
  setIsRecording(true);
37
41
  },
38
- [allowSpeech],
42
+ [allowSpeech, loading],
39
43
  );
40
44
 
41
45
  const handleTouchMove = useCallback(
@@ -71,7 +75,7 @@ function RecordAction(
71
75
  async (e: TouchEvent) => {
72
76
  e.preventDefault();
73
77
 
74
- // 没有考试,不继续
78
+ // 没有录音,不继续
75
79
  if (!isRecording) {
76
80
  return;
77
81
  }
@@ -140,10 +144,10 @@ function RecordAction(
140
144
  {isRecording ? (
141
145
  <>
142
146
  <RecordLoading count={30} gap={4} />
143
- {isCancel && <div className="absolute top-0 -mt-[2.5em] text-red08">松开取消</div>}
144
- {!isCancel && (
145
- <div className="absolute top-0 -mt-[2.5em] text-03">松开发送&nbsp;&nbsp;上移取消</div>
146
- )}
147
+ <div className="absolute left-0 right-0 top-0 -mt-[50px] flex h-[50px] items-end justify-center bg-white pb-2 text-03">
148
+ {isCancel && <div className="text-red08">松开取消</div>}
149
+ {!isCancel && <div className="text-03">松开发送&nbsp;&nbsp;上移取消</div>}
150
+ </div>
147
151
  </>
148
152
  ) : (
149
153
  <div className="text-base text-white">按住说话</div>
@@ -117,7 +117,7 @@ function MessageActionOfDislike({
117
117
  );
118
118
  }
119
119
 
120
- function MessageActionOfLinkAndDislike({
120
+ function MessageActionOfLikeAndDislike({
121
121
  value: propsValue,
122
122
  onChange,
123
123
  className,
@@ -160,7 +160,7 @@ const MessageActions = {
160
160
  Copy: MessageActionOfCopy,
161
161
  Like: MessageActionOfLike,
162
162
  Dislike: MessageActionOfDislike,
163
- LinkAndDislike: MessageActionOfLinkAndDislike,
163
+ LikeAndDislike: MessageActionOfLikeAndDislike,
164
164
  };
165
165
 
166
166
  export { MessageActions };
File without changes