wtfai 1.5.8 → 1.6.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/README.md CHANGED
@@ -161,8 +161,64 @@ session.on('error', ({ message }) => {
161
161
  // 发生错误
162
162
  })
163
163
 
164
+ session.on('contentAction', ({ type, payload }) => {
165
+ // 内容区域(如 Markdown)触发的自定义动作
166
+ if (type === 'workflow') {
167
+ const { workflowId, params } = payload
168
+ console.log('跳转工作流:', workflowId, '参数:', params)
169
+ }
170
+ })
164
171
  ```
165
172
 
173
+ **Markdown 交互 (自定义协议)**
174
+
175
+ SDK 的 Markdown 组件支持通过特殊协议触发 `contentAction` 事件,方便在内容中嵌入交互按钮或链接:
176
+
177
+ - **跳转工作流**:使用 `workflow:` 协议。
178
+ 格式:`[文字](workflow:工作流ID?参数1=值1&参数2=值2)`
179
+ 示例:`[查看详情](workflow:cm0p1234?from=chat)`
180
+
181
+ 当用户点击此类链接时,SDK 不会执行页面跳转,而是触发 `contentAction` 事件,由宿主应用决定如何处理(如切换侧边栏、打开弹窗等)。
182
+
183
+ ---
184
+
185
+ ### 结构化内容处理 (WorkflowRegistry)
186
+
187
+ 为了在聊天内容中更美观地展示工作流引用,SDK 提供了 `<workflow>` 自定义标签和全局注册机制。
188
+
189
+ #### 1. 全局注册工作流信息
190
+
191
+ 在应用顶层(或页面容器层)使用 `WorkflowRegistry` 注入工作流的展示名称和图标:
192
+
193
+ ```tsx
194
+ import { WorkflowRegistry } from 'wtfai';
195
+ import { RobotOutlined } from '@ant-design/icons';
196
+
197
+ const MY_WORKFLOWS = {
198
+ 'wf_week_report': {
199
+ name: '自动化周报',
200
+ icon: <RobotOutlined />
201
+ },
202
+ };
203
+
204
+ // 在容器层包裹一次即可
205
+ <WorkflowRegistry mapping={MY_WORKFLOWS}>
206
+ <App />
207
+ </WorkflowRegistry>
208
+ ```
209
+
210
+ #### 2. 在 Markdown 中使用
211
+
212
+ 通过自定义标签 `<workflow id="xxx" />` 引用工作流:
213
+
214
+ - **基本用法**:`<workflow id="wf_week_report" />`
215
+ - **自定义文字**:`<workflow id="wf_week_report">立即查看</workflow>`
216
+
217
+ **渲染效果**:
218
+ SDK 会自动将其渲染为一个美观的“药丸状”卡片按钮,包含图标和名称。如果 `WorkflowRegistry` 中没有匹配到 ID,则会降级显示标签内容或 ID。
219
+
220
+ 点击该卡片会触发 `contentAction` 事件,`type` 为 `'workflow'`。
221
+
166
222
  **最佳实践(推荐组合)**
167
223
 
168
224
  1. **打字机效果**:使用 `token` 拼接流式内容。
@@ -52,8 +52,7 @@ class IframeBridgeHost {
52
52
  const [title] = params;
53
53
  await this.session.updateSessionTitle(title);
54
54
  result = true;
55
- } else if ('getSessionId' === method) result = this.session.getState().threadId;
56
- else if ('uploadFile' === method) {
55
+ } else if ('uploadFile' === method) {
57
56
  const [options] = params;
58
57
  result = await this.session.uploadFileFromIframe(options);
59
58
  } else if ('createRecord' === method) {
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Wtfai } from './client';
2
2
  export default Wtfai;
3
3
  export { WorkflowSession } from './session';
4
4
  export { UploadService } from './upload';
5
- export type { ClientConfig, SessionOptions, SessionState, SessionEventListeners, SendInput, UploadParams, UploadImageParams, CompressOptions, WorkflowInfo, ContentPart, } from './types';
5
+ export type { ClientConfig, SessionOptions, SessionState, SessionEventListeners, SendInput, UploadParams, UploadImageParams, CompressOptions, WorkflowInfo, ContentPart, ContentAction, } from './types';
6
6
  export type { SimpleMessage, Workflow, WorkflowConfig, } from '@our-llm/shared/types';
7
- export { Markdown, type MarkdownProps } from './ui/markdown';
7
+ export { Markdown, type MarkdownProps, WorkflowRegistry } from './ui/markdown';
8
+ export type { WorkflowInfoMapping } from './ui/context';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Wtfai } from "./client.js";
2
2
  import { WorkflowSession } from "./session.js";
3
3
  import { UploadService } from "./upload.js";
4
- import { Markdown } from "./ui/markdown.js";
4
+ import { Markdown, WorkflowRegistry } from "./ui/markdown.js";
5
5
  const src = Wtfai;
6
- export { Markdown, UploadService, WorkflowSession, src as default };
6
+ export { Markdown, UploadService, WorkflowRegistry, WorkflowSession, src as default };
package/dist/session.d.ts CHANGED
@@ -44,8 +44,13 @@ export declare class WorkflowSession {
44
44
  * 移除事件监听
45
45
  */
46
46
  off<K extends keyof SessionEventListeners>(event: K, listener: SessionEventListeners[K]): this;
47
+ /**
48
+ * 触发内容区域(如 Markdown)产生的交互动作
49
+ */
50
+ emitContentAction(action: import('./types').ContentAction): void;
47
51
  /**
48
52
  * 触发事件
53
+ * @internal
49
54
  */
50
55
  private emit;
51
56
  /**
package/dist/session.js CHANGED
@@ -64,6 +64,9 @@ class WorkflowSession {
64
64
  null == (_this_listeners_get = this.listeners.get(event)) || _this_listeners_get.delete(listener);
65
65
  return this;
66
66
  }
67
+ emitContentAction(action) {
68
+ this.emit('contentAction', action);
69
+ }
67
70
  emit(event, ...args) {
68
71
  if (this.disposed) return;
69
72
  const listeners = this.listeners.get(event);
package/dist/types.d.ts CHANGED
@@ -155,7 +155,21 @@ export interface SessionEventListeners {
155
155
  error: (error: {
156
156
  message: string;
157
157
  }) => void;
158
+ contentAction: (action: ContentAction) => void;
158
159
  }
160
+ /**
161
+ * 内容交互动作联合类型
162
+ */
163
+ export type ContentAction = {
164
+ /** 跳转工作流 */
165
+ type: 'workflow';
166
+ payload: {
167
+ workflowId: string;
168
+ /** 暂时没用上 */
169
+ params?: Record<string, string>;
170
+ extra?: Record<string, any>;
171
+ };
172
+ };
159
173
  /**
160
174
  * 会话状态
161
175
  */
@@ -1,3 +1,11 @@
1
1
  import type { WorkflowSession } from '../session';
2
+ export interface WorkflowInfoMapping {
3
+ name: string;
4
+ icon?: React.ReactNode;
5
+ /** 额外的信息,会在点击的时候透传给调用方,方便使用 */
6
+ extra?: Record<string, any>;
7
+ }
2
8
  export declare const SessionContext: import("react").Context<WorkflowSession | null>;
9
+ export declare const WorkflowRegistryContext: import("react").Context<Record<string, WorkflowInfoMapping>>;
3
10
  export declare const useWorkflowSession: () => WorkflowSession | null;
11
+ export declare const useWorkflowRegistry: () => Record<string, WorkflowInfoMapping>;
@@ -1,4 +1,6 @@
1
1
  import { createContext, useContext } from "react";
2
2
  const SessionContext = createContext(null);
3
+ const WorkflowRegistryContext = createContext({});
3
4
  const useWorkflowSession = ()=>useContext(SessionContext);
4
- export { SessionContext, useWorkflowSession };
5
+ const useWorkflowRegistry = ()=>useContext(WorkflowRegistryContext);
6
+ export { SessionContext, WorkflowRegistryContext, useWorkflowRegistry, useWorkflowSession };
@@ -0,0 +1,14 @@
1
+ .workflow-link {
2
+ cursor: pointer;
3
+ align-items: center;
4
+ display: inline-flex;
5
+ }
6
+
7
+ .workflow-link-icon {
8
+ margin-right: 4px;
9
+ }
10
+
11
+ .workflow-link-icon img {
12
+ margin: 0;
13
+ }
14
+
@@ -1,8 +1,16 @@
1
1
  import { XMarkdownProps } from '@ant-design/x-markdown';
2
2
  import '@ant-design/x-markdown/themes/light.css';
3
+ import './markdown.css';
4
+ import { WorkflowInfoMapping } from './context';
3
5
  import type { WorkflowSession } from '../session';
4
6
  export interface MarkdownProps extends XMarkdownProps {
5
7
  session?: WorkflowSession;
6
8
  }
7
- declare const Markdown: ({ className, streaming, config, components, session, ...props }: MarkdownProps) => import("react/jsx-runtime").JSX.Element;
8
- export { Markdown };
9
+ /**
10
+ * 工作流信息注册表提供者
11
+ */
12
+ export declare const WorkflowRegistry: ({ children, mapping, }: {
13
+ children: React.ReactNode;
14
+ mapping: Record<string, WorkflowInfoMapping>;
15
+ }) => import("react/jsx-runtime").JSX.Element;
16
+ export declare const Markdown: ({ className, streaming, config, components, session, ...props }: MarkdownProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,5 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Children } from "react";
2
3
  import { XMarkdown } from "@ant-design/x-markdown";
3
4
  import "@ant-design/x-markdown/themes/light.css";
4
5
  import Latex from "@ant-design/x-markdown/plugins/Latex";
@@ -6,8 +7,25 @@ import zh_CN from "@ant-design/x/locale/zh_CN";
6
7
  import clsx from "clsx";
7
8
  import { Code } from "./code.js";
8
9
  import { XProvider } from "@ant-design/x";
9
- import { SessionContext } from "./context.js";
10
- const Markdown = ({ className, streaming, config, components, session, ...props })=>/*#__PURE__*/ jsx(XProvider, {
10
+ import "./markdown.css";
11
+ import { SessionContext, WorkflowRegistryContext, useWorkflowRegistry } from "./context.js";
12
+ const WorkflowRegistry = ({ children, mapping })=>{
13
+ const parentRegistry = useWorkflowRegistry();
14
+ const mergedRegistry = {
15
+ ...parentRegistry,
16
+ ...mapping
17
+ };
18
+ return /*#__PURE__*/ jsx(SessionContext.Provider, {
19
+ value: null,
20
+ children: /*#__PURE__*/ jsx(WorkflowRegistryContext.Provider, {
21
+ value: mergedRegistry,
22
+ children: children
23
+ })
24
+ });
25
+ };
26
+ const Markdown = ({ className, streaming, config, components, session, ...props })=>{
27
+ const globalRegistry = useWorkflowRegistry();
28
+ return /*#__PURE__*/ jsx(XProvider, {
11
29
  locale: zh_CN,
12
30
  children: /*#__PURE__*/ jsx(SessionContext.Provider, {
13
31
  value: session || null,
@@ -23,10 +41,37 @@ const Markdown = ({ className, streaming, config, components, session, ...props
23
41
  },
24
42
  components: {
25
43
  ...components,
26
- code: Code
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
+ }
27
71
  },
28
72
  ...props
29
73
  })
30
74
  })
31
75
  });
32
- export { Markdown };
76
+ };
77
+ export { Markdown, WorkflowRegistry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtfai",
3
- "version": "1.5.8",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -20,7 +20,7 @@
20
20
  "eslint": "^9.39.3",
21
21
  "eslint-config-prettier": "^10.1.8",
22
22
  "eslint-plugin-prettier": "^5.5.5",
23
- "globals": "^17.3.0",
23
+ "globals": "^17.4.0",
24
24
  "prettier": "^3.8.1",
25
25
  "react": "^19.2.4",
26
26
  "typescript": "^5.9.3",
@@ -31,8 +31,8 @@
31
31
  "react-dom": ">=16.9.0"
32
32
  },
33
33
  "dependencies": {
34
- "@ant-design/x": "^2.2.2",
35
- "@ant-design/x-markdown": "^2.2.2",
34
+ "@ant-design/x": "^2.3.0",
35
+ "@ant-design/x-markdown": "^2.3.0",
36
36
  "@microsoft/fetch-event-source": "^2.0.1",
37
37
  "clsx": "^2.1.1",
38
38
  "compressorjs": "^1.2.1",