wtfai 1.6.4 → 1.6.6

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/README.md CHANGED
@@ -506,28 +506,30 @@ const url = await IframeBridge.uploadFile(file, {
506
506
  > - `saveData/loadData` 存储在工作流 state 中(适合简单 key-value)
507
507
  > - Session Data API 存储在独立数据表中(适合结构化数据、列表数据)
508
508
 
509
- #### `IframeBridge.createRecord(collection, data)`
509
+ #### `IframeBridge.createRecord(collection, data, options?)`
510
510
 
511
- 在指定集合中创建一条记录。
511
+ 在指定集合中创建一条记录。可以通过 `options.entityId` 字段进行二级分类归属(如老师 ID、学生 ID),后续查询时利用此字段能大幅提升检索性能。
512
512
 
513
513
  ```javascript
514
+ // options = { entityId?: string }
514
515
  const id = await IframeBridge.createRecord('notes', {
515
516
  title: 'My Note',
516
517
  content: 'Hello World',
517
518
  createdAt: Date.now()
518
- });
519
+ }, { entityId: 'user_123' }); // 可选的 entityId
519
520
  console.log('创建成功, ID:', id);
520
521
  ```
521
522
 
522
- #### `IframeBridge.createRecords(collection, dataList)`
523
+ #### `IframeBridge.createRecords(collection, dataList, options?)`
523
524
 
524
- 批量在指定集合中创建多条记录。
525
+ 批量在指定集合中创建多条记录。支持传入可选的配置项如 `entityId`。
525
526
 
526
527
  ```javascript
528
+ // options = { entityId?: string }
527
529
  const ids = await IframeBridge.createRecords('logs', [
528
530
  { level: 'info', message: 'Task started' },
529
531
  { level: 'success', message: 'Task finished' }
530
- ]);
532
+ ], { entityId: 'user_123' });
531
533
  console.log('创建成功, IDs:', ids);
532
534
  ```
533
535
 
@@ -566,13 +568,15 @@ await IframeBridge.deleteRecord('notes', id);
566
568
 
567
569
  #### `IframeBridge.listRecords(collection, options?)`
568
570
 
569
- 分页查询集合中的记录。
571
+ 分页查询集合中的记录。可以通过传入 `entityId` 来极速筛选归属于特定实体的数据。
570
572
 
571
573
  ```javascript
574
+ // options = { page?: number, pageSize?: number, order?: 'ASC' | 'DESC', entityId?: string }
572
575
  const result = await IframeBridge.listRecords('notes', {
573
576
  page: 1,
574
577
  pageSize: 10,
575
- order: 'DESC'
578
+ order: 'DESC',
579
+ entityId: 'user_123' // 可选,利用组合索引极速过滤
576
580
  });
577
581
  console.log(`共 ${result.total} 条记录`);
578
582
  result.records.forEach(r => {
@@ -56,11 +56,11 @@ class IframeBridgeHost {
56
56
  const [options] = params;
57
57
  result = await this.session.uploadFileFromIframe(options);
58
58
  } else if ('createRecord' === method) {
59
- const [collection, data] = params;
60
- result = await this.session.createRecord(collection, data);
59
+ const [collection, data, options] = params;
60
+ result = await this.session.createRecord(collection, data, options);
61
61
  } else if ('createRecords' === method) {
62
- const [collection, dataList] = params;
63
- result = await this.session.createRecords(collection, dataList);
62
+ const [collection, dataList, options] = params;
63
+ result = await this.session.createRecords(collection, dataList, options);
64
64
  } else if ('getRecord' === method) {
65
65
  const [collection, id] = params;
66
66
  result = await this.session.getRecord(collection, id);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Wtfai } from "./client.js";
2
- import { WorkflowSession } from "./session.js";
3
- import { UploadService } from "./upload.js";
4
- import { Markdown, WorkflowRegistry } from "./ui/markdown.js";
5
2
  const src = Wtfai;
6
- export { Markdown, UploadService, WorkflowRegistry, WorkflowSession, src as default };
3
+ export { WorkflowSession } from "./session.js";
4
+ export { UploadService } from "./upload.js";
5
+ export { Markdown, WorkflowRegistry } from "./ui/markdown.js";
6
+ export default src;
package/dist/session.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { SimpleMessage } from '@our-llm/shared/types';
2
- import type { SessionOptions, SessionState, SessionEventListeners, SendInput } from './types';
2
+ import type { SessionOptions, SessionState, SessionEventListeners, SendInput, ListRecordsOptions, ListRecordsResult } from './types';
3
3
  import { UploadService } from './upload';
4
4
  /**
5
5
  * 工作流会话
@@ -82,14 +82,18 @@ export declare class WorkflowSession {
82
82
  * @param data 数据内容
83
83
  * @returns 新创建记录的 ID
84
84
  */
85
- createRecord(collection: string, data: Record<string, unknown>): Promise<string>;
85
+ createRecord(collection: string, data: Record<string, unknown>, options?: {
86
+ entityId?: string;
87
+ }): Promise<string>;
86
88
  /**
87
89
  * 批量创建数据记录
88
90
  * @param collection 集合名称
89
91
  * @param dataList 数据列表
90
92
  * @returns 新创建记录的 ID 列表
91
93
  */
92
- createRecords(collection: string, dataList: Array<Record<string, unknown>>): Promise<string[]>;
94
+ createRecords(collection: string, dataList: Array<Record<string, unknown>>, options?: {
95
+ entityId?: string;
96
+ }): Promise<string[]>;
93
97
  /**
94
98
  * 获取数据记录
95
99
  * @param collection 集合名称
@@ -110,22 +114,7 @@ export declare class WorkflowSession {
110
114
  * @param id 记录 ID
111
115
  */
112
116
  deleteRecord(collection: string, id: string): Promise<void>;
113
- /**
114
- * 列表查询数据记录
115
- * @param collection 集合名称
116
- * @param options 分页选项
117
- */
118
- listRecords(collection: string, options?: {
119
- page?: number;
120
- pageSize?: number;
121
- order?: 'ASC' | 'DESC';
122
- }): Promise<{
123
- records: Array<{
124
- id: string;
125
- data: Record<string, unknown>;
126
- }>;
127
- total: number;
128
- }>;
117
+ listRecords(collection: string, options?: ListRecordsOptions): Promise<ListRecordsResult>;
129
118
  /**
130
119
  * 清空集合中的所有数据
131
120
  * @param collection 集合名称
package/dist/session.js CHANGED
@@ -161,7 +161,7 @@ class WorkflowSession {
161
161
  }, this.headers).catch(reject);
162
162
  });
163
163
  }
164
- async createRecord(collection, data) {
164
+ async createRecord(collection, data, options = {}) {
165
165
  this.assertNotDisposed();
166
166
  if (!this.state.threadId) throw new Error('No active session (missing threadId)');
167
167
  const response = await fetch(`${this.baseUrl}/workflows/${this.workflowId}/data/${encodeURIComponent(collection)}`, {
@@ -172,14 +172,15 @@ class WorkflowSession {
172
172
  },
173
173
  body: JSON.stringify({
174
174
  threadId: this.state.threadId,
175
- data
175
+ data,
176
+ entityId: options.entityId
176
177
  })
177
178
  });
178
179
  if (!response.ok) throw new Error('Failed to create record');
179
180
  const result = await response.json();
180
181
  return result.id;
181
182
  }
182
- async createRecords(collection, dataList) {
183
+ async createRecords(collection, dataList, options = {}) {
183
184
  this.assertNotDisposed();
184
185
  if (!this.state.threadId) throw new Error('No active session (missing threadId)');
185
186
  const response = await fetch(`${this.baseUrl}/workflows/${this.workflowId}/data/${encodeURIComponent(collection)}/batch`, {
@@ -190,7 +191,8 @@ class WorkflowSession {
190
191
  },
191
192
  body: JSON.stringify({
192
193
  threadId: this.state.threadId,
193
- dataList
194
+ dataList,
195
+ entityId: options.entityId
194
196
  })
195
197
  });
196
198
  if (!response.ok) throw new Error('Failed to create records');
@@ -242,9 +244,9 @@ class WorkflowSession {
242
244
  if (!this.state.threadId) throw new Error('No active session (missing threadId)');
243
245
  const url = new URL(`${this.baseUrl}/workflows/${this.workflowId}/data/${encodeURIComponent(collection)}`);
244
246
  url.searchParams.set('threadId', this.state.threadId);
245
- if (null == options ? void 0 : options.page) url.searchParams.set('page', String(options.page));
246
- if (null == options ? void 0 : options.pageSize) url.searchParams.set('pageSize', String(options.pageSize));
247
- if (null == options ? void 0 : options.order) url.searchParams.set('order', options.order);
247
+ Object.entries(options || {}).forEach(([key, value])=>{
248
+ if (void 0 !== value) url.searchParams.set(key, String(value));
249
+ });
248
250
  const response = await fetch(url.toString(), {
249
251
  headers: this.headers
250
252
  });
package/dist/types.d.ts CHANGED
@@ -193,3 +193,26 @@ export interface WorkflowInfo {
193
193
  name: string;
194
194
  description: string;
195
195
  }
196
+ /**
197
+ * 分页与过滤选项
198
+ */
199
+ export interface ListRecordsOptions {
200
+ page?: number;
201
+ pageSize?: number;
202
+ order?: 'ASC' | 'DESC';
203
+ entityId?: string;
204
+ }
205
+ /**
206
+ * 数据记录基类
207
+ */
208
+ export interface BaseRecord {
209
+ id: string;
210
+ data: Record<string, unknown>;
211
+ }
212
+ /**
213
+ * 列表查询返回结构
214
+ */
215
+ export interface ListRecordsResult {
216
+ records: BaseRecord[];
217
+ total: number;
218
+ }
package/dist/ui/code.css CHANGED
@@ -5,3 +5,37 @@
5
5
  display: block;
6
6
  }
7
7
 
8
+ .html-preview-container {
9
+ background: #fff;
10
+ border: 1px solid #e5e7eb;
11
+ border-radius: 8px;
12
+ margin: 16px 0;
13
+ overflow: hidden;
14
+ box-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
15
+ }
16
+
17
+ .html-preview-header {
18
+ color: #4b5563;
19
+ background: #f9fafb;
20
+ border-bottom: 1px solid #e5e7eb;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ padding: 8px 12px;
24
+ font-size: 13px;
25
+ font-weight: 500;
26
+ display: flex;
27
+ }
28
+
29
+ .html-preview-header .preview-label {
30
+ align-items: center;
31
+ gap: 8px;
32
+ display: flex;
33
+ }
34
+
35
+ .html-preview-iframe {
36
+ border: none;
37
+ width: 100%;
38
+ min-height: 400px;
39
+ display: block;
40
+ }
41
+
package/dist/ui/code.js CHANGED
@@ -1,14 +1,16 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef } from "react";
3
3
  import { CodeHighlighter, Mermaid } from "@ant-design/x";
4
+ import { EyeOutlined, LoadingOutlined } from "@ant-design/icons";
4
5
  import { jsonrepair } from "jsonrepair";
5
6
  import { useWorkflowSession } from "./context.js";
6
7
  import { IframeBridgeHost } from "../iframe-bridge.js";
7
8
  import "./code.css";
8
9
  const Code = (props)=>{
9
10
  var _className_match;
10
- const { className, children } = props;
11
- const lang = (null == className ? void 0 : null == (_className_match = className.match(/language-(\w+)/)) ? void 0 : _className_match[1]) || '';
11
+ const { className, children, streamStatus, lang: infoString } = props;
12
+ const fullLang = infoString || (null == className ? void 0 : null == (_className_match = className.match(/language-([\w:-]+)/)) ? void 0 : _className_match[1]) || '';
13
+ const lang = fullLang.split(':')[0];
12
14
  const session = useWorkflowSession();
13
15
  const iframeRef = useRef(null);
14
16
  const bridgeRef = useRef(null);
@@ -38,6 +40,41 @@ const Code = (props)=>{
38
40
  allow: "clipboard-read; clipboard-write; camera; microphone"
39
41
  });
40
42
  } catch {}
43
+ if ('html:run' === fullLang) {
44
+ const isLoading = 'loading' === streamStatus;
45
+ return /*#__PURE__*/ jsxs("div", {
46
+ className: "html-preview-container",
47
+ children: [
48
+ /*#__PURE__*/ jsxs("div", {
49
+ className: "html-preview-header",
50
+ children: [
51
+ /*#__PURE__*/ jsxs("div", {
52
+ className: "preview-label",
53
+ children: [
54
+ /*#__PURE__*/ jsx(EyeOutlined, {}),
55
+ /*#__PURE__*/ jsx("span", {
56
+ children: "预览"
57
+ })
58
+ ]
59
+ }),
60
+ isLoading && /*#__PURE__*/ jsx(LoadingOutlined, {
61
+ spin: true,
62
+ className: "loading-icon",
63
+ style: {
64
+ color: '#3b82f6'
65
+ }
66
+ })
67
+ ]
68
+ }),
69
+ /*#__PURE__*/ jsx("iframe", {
70
+ srcDoc: children,
71
+ className: "html-preview-iframe",
72
+ title: "HTML Preview",
73
+ sandbox: "allow-scripts allow-forms allow-popups"
74
+ })
75
+ ]
76
+ });
77
+ }
41
78
  return /*#__PURE__*/ jsx(CodeHighlighter, {
42
79
  lang: lang,
43
80
  children: children
@@ -7,5 +7,11 @@ export interface WorkflowInfoMapping {
7
7
  }
8
8
  export declare const SessionContext: import("react").Context<WorkflowSession | null>;
9
9
  export declare const WorkflowRegistryContext: import("react").Context<Record<string, WorkflowInfoMapping>>;
10
+ export declare const MarkdownContext: import("react").Context<{
11
+ isStreaming?: boolean;
12
+ }>;
10
13
  export declare const useWorkflowSession: () => WorkflowSession | null;
11
14
  export declare const useWorkflowRegistry: () => Record<string, WorkflowInfoMapping>;
15
+ export declare const useMarkdownContext: () => {
16
+ isStreaming?: boolean;
17
+ };
@@ -1,6 +1,10 @@
1
1
  import { createContext, useContext } from "react";
2
2
  const SessionContext = createContext(null);
3
3
  const WorkflowRegistryContext = createContext({});
4
+ const MarkdownContext = createContext({
5
+ isStreaming: false
6
+ });
4
7
  const useWorkflowSession = ()=>useContext(SessionContext);
5
8
  const useWorkflowRegistry = ()=>useContext(WorkflowRegistryContext);
6
- export { SessionContext, WorkflowRegistryContext, useWorkflowRegistry, useWorkflowSession };
9
+ const useMarkdownContext = ()=>useContext(MarkdownContext);
10
+ export { MarkdownContext, SessionContext, WorkflowRegistryContext, useMarkdownContext, useWorkflowRegistry, useWorkflowSession };
@@ -8,7 +8,7 @@ import clsx from "clsx";
8
8
  import { Code } from "./code.js";
9
9
  import { XProvider } from "@ant-design/x";
10
10
  import "./markdown.css";
11
- import { SessionContext, WorkflowRegistryContext, useWorkflowRegistry } from "./context.js";
11
+ import { MarkdownContext, SessionContext, WorkflowRegistryContext, useWorkflowRegistry } from "./context.js";
12
12
  const WorkflowRegistry = ({ children, mapping })=>{
13
13
  const parentRegistry = useWorkflowRegistry();
14
14
  const mergedRegistry = {
@@ -29,47 +29,52 @@ const Markdown = ({ className, streaming, config, components, session, ...props
29
29
  locale: zh_CN,
30
30
  children: /*#__PURE__*/ jsx(SessionContext.Provider, {
31
31
  value: session || null,
32
- children: /*#__PURE__*/ jsx(XMarkdown, {
33
- className: clsx(className, 'x-markdown-light'),
34
- streaming: {
35
- ...streaming,
36
- enableAnimation: true
32
+ children: /*#__PURE__*/ jsx(MarkdownContext.Provider, {
33
+ value: {
34
+ isStreaming: !!(null == streaming ? void 0 : streaming.hasNextChunk)
37
35
  },
38
- config: {
39
- ...config,
40
- extensions: Latex()
41
- },
42
- components: {
43
- ...components,
44
- code: Code,
45
- workflow: (props)=>{
46
- const { id, children } = props;
47
- const info = id ? globalRegistry[id] : void 0;
48
- const displayContent = (null == info ? void 0 : info.name) || (Children.count(children) > 0 ? children : id);
49
- return /*#__PURE__*/ jsxs("a", {
50
- onClick: (e)=>{
51
- e.preventDefault();
52
- e.stopPropagation();
53
- null == session || session.emitContentAction({
54
- type: 'workflow',
55
- payload: {
56
- workflowId: id,
57
- extra: null == info ? void 0 : info.extra
58
- }
59
- });
60
- },
61
- className: "workflow-link",
62
- children: [
63
- (null == info ? void 0 : info.icon) && /*#__PURE__*/ jsx("span", {
64
- className: "workflow-link-icon",
65
- children: info.icon
66
- }),
67
- displayContent
68
- ]
69
- });
70
- }
71
- },
72
- ...props
36
+ children: /*#__PURE__*/ jsx(XMarkdown, {
37
+ className: clsx(className, 'x-markdown-light'),
38
+ streaming: {
39
+ ...streaming,
40
+ enableAnimation: true
41
+ },
42
+ config: {
43
+ ...config,
44
+ extensions: Latex()
45
+ },
46
+ components: {
47
+ ...components,
48
+ code: Code,
49
+ workflow: (props)=>{
50
+ const { id, children } = props;
51
+ const info = id ? globalRegistry[id] : void 0;
52
+ const displayContent = (null == info ? void 0 : info.name) || (Children.count(children) > 0 ? children : id);
53
+ return /*#__PURE__*/ jsxs("a", {
54
+ onClick: (e)=>{
55
+ e.preventDefault();
56
+ e.stopPropagation();
57
+ null == session || session.emitContentAction({
58
+ type: 'workflow',
59
+ payload: {
60
+ workflowId: id,
61
+ extra: null == info ? void 0 : info.extra
62
+ }
63
+ });
64
+ },
65
+ className: "workflow-link",
66
+ children: [
67
+ (null == info ? void 0 : info.icon) && /*#__PURE__*/ jsx("span", {
68
+ className: "workflow-link-icon",
69
+ children: info.icon
70
+ }),
71
+ displayContent
72
+ ]
73
+ });
74
+ }
75
+ },
76
+ ...props
77
+ })
73
78
  })
74
79
  })
75
80
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtfai",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -13,36 +13,38 @@
13
13
  "dist"
14
14
  ],
15
15
  "devDependencies": {
16
- "@eslint/js": "^9.39.3",
17
- "@rsbuild/plugin-react": "^1.4.5",
18
- "@rslib/core": "^0.19.6",
16
+ "@eslint/js": "^9.39.4",
17
+ "@rsbuild/plugin-react": "^1.4.6",
18
+ "@rslib/core": "^0.20.0",
19
19
  "@types/react": "^19.2.14",
20
- "eslint": "^9.39.3",
20
+ "eslint": "^9.39.4",
21
21
  "eslint-config-prettier": "^10.1.8",
22
22
  "eslint-plugin-prettier": "^5.5.5",
23
23
  "globals": "^17.4.0",
24
24
  "prettier": "^3.8.1",
25
25
  "react": "^19.2.4",
26
26
  "typescript": "^5.9.3",
27
- "typescript-eslint": "^8.56.1"
27
+ "typescript-eslint": "^8.57.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=16.9.0",
31
31
  "react-dom": ">=16.9.0"
32
32
  },
33
33
  "dependencies": {
34
+ "@ant-design/icons": "^6.1.0",
34
35
  "@ant-design/x": "^2.3.0",
35
36
  "@ant-design/x-markdown": "^2.3.0",
36
37
  "@microsoft/fetch-event-source": "^2.0.1",
37
38
  "clsx": "^2.1.1",
38
39
  "compressorjs": "^1.2.1",
39
40
  "cos-js-sdk-v5": "^1.10.1",
40
- "jsonrepair": "^3.13.2",
41
+ "jsonrepair": "^3.13.3",
41
42
  "uuid": "^13.0.0",
42
43
  "@our-llm/shared": "3.0.1"
43
44
  },
44
45
  "scripts": {
45
46
  "build": "rslib build",
47
+ "build:sdk": "rslib build -c rslib.config.sdk.ts",
46
48
  "dev": "rslib build --watch",
47
49
  "format": "prettier --write .",
48
50
  "lint": "eslint ."