wtfai 1.8.6 → 1.8.8

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
@@ -262,6 +262,76 @@ import { WorkflowRegistry } from 'wtfai';
262
262
 
263
263
  这种机制非常适合在不修改 SDK 源码的前提下,为特定类型的内容增加业务相关的 UI 装饰(如操作按钮、免责声明、权限校验提示等)。
264
264
 
265
+ #### 4. 消息导出
266
+
267
+ SDK 提供 `exportMessages` 用于导出当前会话消息,并会把 Mermaid 等图表内容导出为图片数据。导出结果不会自动下载文件;如需生成文件内容,可使用 `createExportedMessagesBlob` 得到 `Blob`,后续上传、保存或下载由宿主应用自行决定。
268
+
269
+ **配置导出上下文**
270
+
271
+ 导出需要知道消息数据和对应的 Markdown 容器 DOM。通常在渲染消息列表的外层传入 `exportConfig`:
272
+
273
+ ```tsx
274
+ import { useRef } from 'react'
275
+ import {
276
+ WorkflowRegistry,
277
+ exportMessages,
278
+ createExportedMessagesBlob,
279
+ type WorkflowExportConfig,
280
+ } from 'wtfai'
281
+
282
+ const messageListRef = useRef<HTMLDivElement>(null)
283
+
284
+ const exportConfig: WorkflowExportConfig = {
285
+ containerRef: messageListRef,
286
+ messages,
287
+ }
288
+
289
+ async function handleExport() {
290
+ const payload = await exportMessages(exportConfig)
291
+ const blob = createExportedMessagesBlob(payload)
292
+
293
+ // SDK 只返回数据,不假设业务如何消费。
294
+ // 下面仅演示浏览器下载,实际也可以上传到服务器或写入 IndexedDB。
295
+ const url = URL.createObjectURL(blob)
296
+ const link = document.createElement('a')
297
+ link.href = url
298
+ link.download = `chat-export-${Date.now()}.json`
299
+ link.click()
300
+ URL.revokeObjectURL(url)
301
+ }
302
+
303
+ return (
304
+ <div ref={messageListRef}>
305
+ <WorkflowRegistry exportConfig={exportConfig}>
306
+ <MessageList messages={messages} />
307
+ </WorkflowRegistry>
308
+ </div>
309
+ )
310
+ ```
311
+
312
+ **导出结果结构**
313
+
314
+ 每条消息会保留原始内容,同时提供一个普通 Markdown 渲染器可直接展示的内容字段:
315
+
316
+ ```typescript
317
+ type ExportedMessage = {
318
+ id: string
319
+ role: 'user' | 'assistant'
320
+ content: string
321
+ renderableContent: string
322
+ attachments?: unknown
323
+ chartImages: Array<{
324
+ kind: string
325
+ format: 'png'
326
+ dataUrl: string
327
+ }>
328
+ }
329
+ ```
330
+
331
+ - `content`:原始消息内容,不做修改。
332
+ - `renderableContent`:可直接渲染的 Markdown 内容。SDK 会把已成功导出的 Mermaid 等代码块替换为 Markdown 图片标记,图片地址为 base64 data URL。
333
+ - `chartImages`:结构化的图表图片数据,便于业务侧单独存储、上传或二次处理。
334
+
265
335
  **最佳实践(推荐组合)**
266
336
 
267
337
  1. **打字机效果**:使用 `token` 拼接流式内容。
@@ -0,0 +1,2 @@
1
+ import type { ChartExportAdapter } from '../export';
2
+ export declare function createMermaidChartExportAdapter(): ChartExportAdapter;
@@ -0,0 +1,101 @@
1
+ const DEFAULT_SVG_WIDTH = 800;
2
+ const DEFAULT_SVG_HEIGHT = 600;
3
+ let mermaidRenderId = 0;
4
+ let mermaidInitialized = false;
5
+ function parseSvgDimension(value) {
6
+ if (!value) return null;
7
+ const numeric = Number.parseFloat(value);
8
+ return Number.isFinite(numeric) && numeric > 0 ? numeric : null;
9
+ }
10
+ function getSvgSize(svg) {
11
+ const rect = svg.getBoundingClientRect();
12
+ if (rect.width > 0 && rect.height > 0) return {
13
+ width: rect.width,
14
+ height: rect.height
15
+ };
16
+ const viewBox = svg.getAttribute('viewBox');
17
+ if (viewBox) {
18
+ const parts = viewBox.trim().split(/[\s,]+/).map((part)=>Number.parseFloat(part));
19
+ if (4 === parts.length && Number.isFinite(parts[2]) && Number.isFinite(parts[3]) && parts[2] > 0 && parts[3] > 0) return {
20
+ width: parts[2],
21
+ height: parts[3]
22
+ };
23
+ }
24
+ const width = parseSvgDimension(svg.getAttribute('width'));
25
+ const height = parseSvgDimension(svg.getAttribute('height'));
26
+ return {
27
+ width: null != width ? width : DEFAULT_SVG_WIDTH,
28
+ height: null != height ? height : DEFAULT_SVG_HEIGHT
29
+ };
30
+ }
31
+ async function svgToPngDataUrl(svgElement) {
32
+ const svgString = new XMLSerializer().serializeToString(svgElement);
33
+ const { width, height } = getSvgSize(svgElement);
34
+ const dpr = window.devicePixelRatio || 1;
35
+ const canvas = document.createElement('canvas');
36
+ canvas.width = Math.max(1, Math.floor(width * dpr));
37
+ canvas.height = Math.max(1, Math.floor(height * dpr));
38
+ canvas.style.width = `${width}px`;
39
+ canvas.style.height = `${height}px`;
40
+ const ctx = canvas.getContext('2d');
41
+ if (!ctx) throw new Error('Cannot create 2D canvas context');
42
+ ctx.scale(dpr, dpr);
43
+ const img = new Image();
44
+ const dataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
45
+ await new Promise((resolve, reject)=>{
46
+ img.onload = ()=>{
47
+ ctx.drawImage(img, 0, 0, width, height);
48
+ resolve();
49
+ };
50
+ img.onerror = ()=>{
51
+ reject(new Error('Failed to load SVG data into Image'));
52
+ };
53
+ img.src = dataUrl;
54
+ });
55
+ return canvas.toDataURL('image/png', 1);
56
+ }
57
+ async function renderMermaidSourceToSvg(source) {
58
+ var _module_default;
59
+ const module = await import("mermaid");
60
+ const mermaid = null != (_module_default = module.default) ? _module_default : module;
61
+ if (!mermaidInitialized) {
62
+ var _mermaid_initialize;
63
+ null == (_mermaid_initialize = mermaid.initialize) || _mermaid_initialize.call(mermaid, {
64
+ startOnLoad: false
65
+ });
66
+ mermaidInitialized = true;
67
+ }
68
+ const renderId = `wtfai-mermaid-export-${Date.now()}-${mermaidRenderId++}`;
69
+ const container = document.createElement('div');
70
+ container.style.position = 'fixed';
71
+ container.style.left = '-10000px';
72
+ container.style.top = '0';
73
+ container.style.visibility = 'hidden';
74
+ document.body.appendChild(container);
75
+ try {
76
+ const result = await mermaid.render(renderId, source, container);
77
+ const doc = new DOMParser().parseFromString(result.svg, 'image/svg+xml');
78
+ const svg = doc.querySelector('svg');
79
+ if (!svg) throw new Error('Mermaid render result did not include an SVG');
80
+ return document.importNode(svg, true);
81
+ } finally{
82
+ container.remove();
83
+ }
84
+ }
85
+ function createMermaidChartExportAdapter() {
86
+ return {
87
+ kind: 'mermaid',
88
+ toImage: async ({ block })=>{
89
+ const source = block.dataset.exportSource;
90
+ const svg = source ? await renderMermaidSourceToSvg(source) : block.querySelector('svg');
91
+ if (!svg) return null;
92
+ const dataUrl = await svgToPngDataUrl(svg);
93
+ return {
94
+ kind: 'mermaid',
95
+ format: 'png',
96
+ dataUrl
97
+ };
98
+ }
99
+ };
100
+ }
101
+ export { createMermaidChartExportAdapter };
@@ -0,0 +1,51 @@
1
+ export type ChartExportKind = 'mermaid' | 'iframe' | 'html-run' | string;
2
+ import type { WorkflowExportConfig } from './ui/context';
3
+ export { createMermaidChartExportAdapter } from './adapters/mermaid';
4
+ export interface ChartImageExport {
5
+ kind: ChartExportKind;
6
+ format: 'png';
7
+ dataUrl: string;
8
+ }
9
+ export interface ChartExportAdapterContext {
10
+ block: HTMLElement;
11
+ messageNode: HTMLElement;
12
+ messageId: string;
13
+ }
14
+ export interface ChartExportAdapter {
15
+ kind: ChartExportKind;
16
+ toImage: ((context: ChartExportAdapterContext) => Promise<ChartImageExport | null>) | ((context: ChartExportAdapterContext) => ChartImageExport | null);
17
+ }
18
+ export interface MessageExportOptions {
19
+ adapters?: ChartExportAdapter[];
20
+ includeDefaultAdapters?: boolean;
21
+ markdownRootSelector?: string;
22
+ debug?: boolean;
23
+ logger?: (message: string, extra?: unknown) => void;
24
+ onAdapterError?: (error: unknown, context: ChartExportAdapterContext, adapter: ChartExportAdapter) => void;
25
+ }
26
+ export interface MessageExportInput {
27
+ id: string;
28
+ role: 'user' | 'assistant';
29
+ content: string;
30
+ attachments?: unknown;
31
+ }
32
+ export interface ExportedMessage<T extends MessageExportInput> {
33
+ id: string;
34
+ role: T['role'];
35
+ content: string;
36
+ renderableContent: string;
37
+ attachments: T['attachments'];
38
+ chartImages: ChartImageExport[];
39
+ }
40
+ export interface ExportedMessagePayload<T extends MessageExportInput> {
41
+ exportedAt: string;
42
+ schemaVersion: 1;
43
+ messages: Array<ExportedMessage<T>>;
44
+ }
45
+ export declare function exportMessagesWithCharts<T extends MessageExportInput>(messages: T[], container: HTMLElement, options?: MessageExportOptions): Promise<ExportedMessagePayload<T>>;
46
+ export declare function exportMessages<T extends MessageExportInput>(exportConfig?: WorkflowExportConfig<T>, overrides?: {
47
+ messages?: T[];
48
+ container?: HTMLElement | null;
49
+ options?: MessageExportOptions;
50
+ }): Promise<ExportedMessagePayload<T>>;
51
+ export declare function createExportedMessagesBlob<T extends MessageExportInput>(payload: ExportedMessagePayload<T>): Blob;
package/dist/export.js ADDED
@@ -0,0 +1,160 @@
1
+ import { createMermaidChartExportAdapter } from "./adapters/mermaid.js";
2
+ const DEFAULT_MARKDOWN_ROOT_SELECTOR = '.sdk-markdown-root';
3
+ const defaultAdapters = [
4
+ createMermaidChartExportAdapter()
5
+ ];
6
+ function createAdapterMap(options) {
7
+ var _options_adapters;
8
+ const map = new Map();
9
+ if ((null == options ? void 0 : options.includeDefaultAdapters) !== false) defaultAdapters.forEach((adapter)=>{
10
+ map.set(adapter.kind, adapter);
11
+ });
12
+ null == options || null == (_options_adapters = options.adapters) || _options_adapters.forEach((adapter)=>{
13
+ map.set(adapter.kind, adapter);
14
+ });
15
+ return map;
16
+ }
17
+ function logExport(options, message, extra) {
18
+ if (!(null == options ? void 0 : options.debug)) return;
19
+ if (options.logger) return void options.logger(message, extra);
20
+ if (void 0 !== extra) console.log(`[wtfai/export] ${message}`, extra);
21
+ else console.log(`[wtfai/export] ${message}`);
22
+ }
23
+ function buildAssistantMessageNodeQueue(container, selector) {
24
+ return Array.from(container.querySelectorAll(selector));
25
+ }
26
+ function normalizeCodeBlockKind(info) {
27
+ const lang = info.trim().split(/\s+/)[0];
28
+ if (!lang) return null;
29
+ if ('html:run' === lang) return 'html-run';
30
+ return lang;
31
+ }
32
+ function chartKindToAlt(kind) {
33
+ if ('mermaid' === kind) return 'Mermaid chart';
34
+ return `${kind} chart`;
35
+ }
36
+ function createMarkdownImage(image) {
37
+ return `![${chartKindToAlt(image.kind)}](${image.dataUrl})`;
38
+ }
39
+ function createRenderableContent(content, chartImages) {
40
+ if (0 === chartImages.length) return content;
41
+ let imageIndex = 0;
42
+ return content.replace(/(^|\n)([ \t]*)(`{3,}|~{3,})([^\r\n]*)\r?\n[\s\S]*?\r?\n[ \t]*\3[ \t]*(?=\r?\n|$)/g, (match, prefix, indent, _fence, info)=>{
43
+ const kind = normalizeCodeBlockKind(info);
44
+ if (!kind) return match;
45
+ const nextImageIndex = chartImages.findIndex((image, index)=>index >= imageIndex && image.kind === kind);
46
+ if (nextImageIndex < 0) return match;
47
+ const image = chartImages[nextImageIndex];
48
+ imageIndex = nextImageIndex + 1;
49
+ return `${prefix}${indent}${createMarkdownImage(image)}`;
50
+ });
51
+ }
52
+ async function exportChartsFromMessageNode(messageNode, messageId, adapterMap, options) {
53
+ const blocks = Array.from(messageNode.querySelectorAll('[data-export-block="true"]'));
54
+ logExport(options, 'scan export blocks', {
55
+ messageId,
56
+ blockCount: blocks.length
57
+ });
58
+ const images = await Promise.all(blocks.map(async (block)=>{
59
+ const kind = block.dataset.exportKind;
60
+ if (!kind) {
61
+ logExport(options, 'skip block without export kind', {
62
+ messageId
63
+ });
64
+ return null;
65
+ }
66
+ const adapter = adapterMap.get(kind);
67
+ if (!adapter) {
68
+ logExport(options, 'skip block without adapter', {
69
+ messageId,
70
+ kind
71
+ });
72
+ return null;
73
+ }
74
+ const context = {
75
+ block,
76
+ messageNode,
77
+ messageId
78
+ };
79
+ try {
80
+ const result = await adapter.toImage(context);
81
+ logExport(options, 'adapter execution completed', {
82
+ messageId,
83
+ kind,
84
+ success: !!result
85
+ });
86
+ return result;
87
+ } catch (error) {
88
+ var _options_onAdapterError;
89
+ logExport(options, 'adapter execution failed', {
90
+ messageId,
91
+ kind,
92
+ error
93
+ });
94
+ null == options || null == (_options_onAdapterError = options.onAdapterError) || _options_onAdapterError.call(options, error, context, adapter);
95
+ return null;
96
+ }
97
+ }));
98
+ return images.filter((item)=>null !== item);
99
+ }
100
+ async function exportMessagesWithCharts(messages, container, options) {
101
+ var _ref, _ref1;
102
+ const adapterMap = createAdapterMap(options);
103
+ logExport(options, 'start export messages', {
104
+ messageCount: messages.length,
105
+ adapterKinds: Array.from(adapterMap.keys())
106
+ });
107
+ const assistantMessageNodeQueue = buildAssistantMessageNodeQueue(container, null != (_ref = null == options ? void 0 : options.markdownRootSelector) ? _ref : DEFAULT_MARKDOWN_ROOT_SELECTOR);
108
+ logExport(options, 'assistant markdown roots found', {
109
+ count: assistantMessageNodeQueue.length,
110
+ selector: null != (_ref1 = null == options ? void 0 : options.markdownRootSelector) ? _ref1 : DEFAULT_MARKDOWN_ROOT_SELECTOR
111
+ });
112
+ let assistantNodeIndex = 0;
113
+ const exportedMessages = await Promise.all(messages.map(async (message)=>{
114
+ let messageNode;
115
+ if ('assistant' === message.role) {
116
+ messageNode = assistantMessageNodeQueue[assistantNodeIndex];
117
+ logExport(options, 'match assistant message to markdown root', {
118
+ messageId: message.id,
119
+ assistantNodeIndex,
120
+ matched: !!messageNode
121
+ });
122
+ assistantNodeIndex += 1;
123
+ } else logExport(options, 'skip non-assistant message for chart export', {
124
+ messageId: message.id,
125
+ role: message.role
126
+ });
127
+ const chartImages = messageNode ? await exportChartsFromMessageNode(messageNode, message.id, adapterMap, options) : [];
128
+ return {
129
+ id: message.id,
130
+ role: message.role,
131
+ content: message.content,
132
+ renderableContent: createRenderableContent(message.content, chartImages),
133
+ attachments: message.attachments,
134
+ chartImages
135
+ };
136
+ }));
137
+ return {
138
+ exportedAt: new Date().toISOString(),
139
+ schemaVersion: 1,
140
+ messages: exportedMessages
141
+ };
142
+ }
143
+ async function exportMessages(exportConfig, overrides) {
144
+ var _ref, _ref1, _ref2;
145
+ var _exportConfig_containerRef;
146
+ const messages = null != (_ref = null == overrides ? void 0 : overrides.messages) ? _ref : null == exportConfig ? void 0 : exportConfig.messages;
147
+ const container = null != (_ref1 = null == overrides ? void 0 : overrides.container) ? _ref1 : null == exportConfig ? void 0 : null == (_exportConfig_containerRef = exportConfig.containerRef) ? void 0 : _exportConfig_containerRef.current;
148
+ const options = null != (_ref2 = null == overrides ? void 0 : overrides.options) ? _ref2 : null == exportConfig ? void 0 : exportConfig.options;
149
+ if (!messages) throw new Error('Messages are required for export');
150
+ if (!container) throw new Error('Export container is required for export');
151
+ return exportMessagesWithCharts(messages, container, options);
152
+ }
153
+ function createExportedMessagesBlob(payload) {
154
+ return new Blob([
155
+ JSON.stringify(payload, null, 2)
156
+ ], {
157
+ type: 'application/json;charset=utf-8'
158
+ });
159
+ }
160
+ export { createExportedMessagesBlob, createMermaidChartExportAdapter, exportMessages, exportMessagesWithCharts };
package/dist/index.d.ts CHANGED
@@ -6,4 +6,5 @@ export { RealtimeService } from './realtime';
6
6
  export type { ClientConfig, SessionOptions, SessionState, SessionEventListeners, SendInput, UploadParams, UploadImageParams, CompressOptions, WorkflowInfo, ContentPart, ContentAction, } from './types';
7
7
  export type { SimpleMessage, Workflow, WorkflowConfig, RealtimeEvent, RealtimePresence, RealtimePushDto, } from '@our-llm/shared/types';
8
8
  export { Markdown, type MarkdownProps, WorkflowRegistry } from './ui/markdown';
9
- export type { WorkflowInfoMapping } from './ui/context';
9
+ export type { WorkflowInfoMapping, WorkflowExportConfig } from './ui/context';
10
+ export { exportMessages, createExportedMessagesBlob, createMermaidChartExportAdapter, type ChartImageExport, type ChartExportKind, type ChartExportAdapter, type ChartExportAdapterContext, type MessageExportInput, type MessageExportOptions, type ExportedMessage, type ExportedMessagePayload, } from './export';
package/dist/index.js CHANGED
@@ -4,4 +4,5 @@ export { WorkflowSession } from "./session.js";
4
4
  export { UploadService } from "./upload.js";
5
5
  export { RealtimeService } from "./realtime.js";
6
6
  export { Markdown, WorkflowRegistry } from "./ui/markdown.js";
7
+ export { createExportedMessagesBlob, createMermaidChartExportAdapter, exportMessages } from "./export.js";
7
8
  export default src;
package/dist/realtime.js CHANGED
@@ -10,6 +10,7 @@ function _define_property(obj, key, value) {
10
10
  else obj[key] = value;
11
11
  return obj;
12
12
  }
13
+ const REALTIME_RECONNECT_INTERVAL = 1000;
13
14
  class RealtimeService {
14
15
  async subscribe(topic, onEvent) {
15
16
  const deviceId = await getFingerprint();
@@ -35,6 +36,10 @@ class RealtimeService {
35
36
  },
36
37
  onerror (err) {
37
38
  console.error('[SDK Realtime] Connection error', err);
39
+ return REALTIME_RECONNECT_INTERVAL;
40
+ },
41
+ onclose () {
42
+ throw new Error('[SDK Realtime] Connection closed; reconnecting');
38
43
  }
39
44
  });
40
45
  return {
package/dist/ui/code.js CHANGED
@@ -25,22 +25,31 @@ const Code = (props)=>{
25
25
  session
26
26
  ]);
27
27
  let node = null;
28
+ let exportKind = null;
29
+ let exportSource = null;
28
30
  if ('string' != typeof children) node = null;
29
- else if ('mermaid' === lang) node = /*#__PURE__*/ jsx(Mermaid, {
30
- children: children
31
- });
32
- else if ('tmpl' === lang) try {
33
- const json = JSON.parse(jsonrepair(children));
34
- if ('iframe' === json.type) node = /*#__PURE__*/ jsx("iframe", {
35
- ref: iframeRef,
36
- src: json.data.src,
37
- className: "iframe-code",
38
- width: "100%",
39
- allow: "clipboard-read; clipboard-write; camera; microphone"
31
+ else if ('mermaid' === lang) {
32
+ exportKind = 'mermaid';
33
+ exportSource = children;
34
+ node = /*#__PURE__*/ jsx(Mermaid, {
35
+ children: children
40
36
  });
37
+ } else if ('tmpl' === lang) try {
38
+ const json = JSON.parse(jsonrepair(children));
39
+ if ('iframe' === json.type) {
40
+ exportKind = 'iframe';
41
+ node = /*#__PURE__*/ jsx("iframe", {
42
+ ref: iframeRef,
43
+ src: json.data.src,
44
+ className: "iframe-code",
45
+ width: "100%",
46
+ allow: "clipboard-read; clipboard-write; camera; microphone"
47
+ });
48
+ }
41
49
  } catch {}
42
50
  else if ('html:run' === lang) {
43
51
  const isLoading = 'loading' === streamStatus;
52
+ exportKind = 'html-run';
44
53
  node = /*#__PURE__*/ jsx("iframe", {
45
54
  srcDoc: children,
46
55
  className: clsx('html-preview-iframe', {
@@ -54,10 +63,17 @@ const Code = (props)=>{
54
63
  prismLightMode: true,
55
64
  children: children
56
65
  });
66
+ let renderedNode = node;
57
67
  if (null == contentWrapper ? void 0 : contentWrapper.code) {
58
68
  const result = contentWrapper.code(props);
59
- if (false !== result && null != result) return result(node);
69
+ if (false !== result && null != result) renderedNode = result(node);
60
70
  }
61
- return node;
71
+ if (exportKind) return /*#__PURE__*/ jsx("div", {
72
+ "data-export-block": "true",
73
+ "data-export-kind": exportKind,
74
+ "data-export-source": null != exportSource ? exportSource : void 0,
75
+ children: renderedNode
76
+ });
77
+ return renderedNode;
62
78
  };
63
79
  export { Code };
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import type { WorkflowSession } from '../session';
3
3
  import { type ComponentProps } from '@ant-design/x-markdown';
4
+ import type { MessageExportInput, MessageExportOptions } from '../export';
4
5
  export interface WorkflowInfoMapping {
5
6
  name: string;
6
7
  icon?: React.ReactNode;
@@ -10,10 +11,16 @@ export interface WorkflowInfoMapping {
10
11
  export type ContentWrapperConfig = {
11
12
  code?: (props: ComponentProps) => ((children: ReactNode) => ReactNode) | null | undefined | false;
12
13
  };
14
+ export interface WorkflowExportConfig<T extends MessageExportInput = MessageExportInput> {
15
+ containerRef?: React.RefObject<HTMLElement | null>;
16
+ messages?: T[];
17
+ options?: MessageExportOptions;
18
+ }
13
19
  export declare const SessionContext: import("react").Context<WorkflowSession | null>;
14
20
  export declare const WorkflowRegistryContext: import("react").Context<{
15
21
  mapping?: Record<string, WorkflowInfoMapping>;
16
22
  contentWrapper?: ContentWrapperConfig;
23
+ exportConfig?: WorkflowExportConfig;
17
24
  }>;
18
25
  export declare const MarkdownContext: import("react").Context<{
19
26
  isStreaming?: boolean;
@@ -22,6 +29,7 @@ export declare const useWorkflowSession: () => WorkflowSession | null;
22
29
  export declare const useWorkflowRegistry: () => {
23
30
  mapping?: Record<string, WorkflowInfoMapping>;
24
31
  contentWrapper?: ContentWrapperConfig;
32
+ exportConfig?: WorkflowExportConfig;
25
33
  };
26
34
  export declare const useMarkdownContext: () => {
27
35
  isStreaming?: boolean;
@@ -1,7 +1,7 @@
1
1
  import { XMarkdownProps } from '@ant-design/x-markdown';
2
2
  import '@ant-design/x-markdown/themes/light.css';
3
3
  import './markdown.css';
4
- import { WorkflowInfoMapping, ContentWrapperConfig } from './context';
4
+ import { WorkflowInfoMapping, ContentWrapperConfig, WorkflowExportConfig } from './context';
5
5
  import type { WorkflowSession } from '../session';
6
6
  export interface MarkdownProps extends XMarkdownProps {
7
7
  session?: WorkflowSession;
@@ -9,7 +9,7 @@ export interface MarkdownProps extends XMarkdownProps {
9
9
  /**
10
10
  * 工作流信息注册表提供者
11
11
  */
12
- export declare const WorkflowRegistry: ({ children, mapping, contentWrapper, }: {
12
+ export declare const WorkflowRegistry: ({ children, mapping, contentWrapper, exportConfig, }: {
13
13
  children: React.ReactNode;
14
14
  /**
15
15
  * 工作流信息映射,控制工作流跳转 a 标签的样式
@@ -19,5 +19,9 @@ export declare const WorkflowRegistry: ({ children, mapping, contentWrapper, }:
19
19
  * 内容包装器,对渲染的内容进行包装
20
20
  */
21
21
  contentWrapper?: ContentWrapperConfig;
22
+ /**
23
+ * 导出配置,可用于在 SDK 内统一管理导出参数
24
+ */
25
+ exportConfig?: WorkflowExportConfig;
22
26
  }) => import("react/jsx-runtime").JSX.Element;
23
27
  export declare const Markdown: ({ className, streaming, config, components, session, ...props }: MarkdownProps) => import("react/jsx-runtime").JSX.Element;
@@ -9,7 +9,7 @@ import { Code } from "./code.js";
9
9
  import { XProvider } from "@ant-design/x";
10
10
  import "./markdown.css";
11
11
  import { MarkdownContext, SessionContext, WorkflowRegistryContext, useWorkflowRegistry } from "./context.js";
12
- const WorkflowRegistry = ({ children, mapping, contentWrapper })=>{
12
+ const WorkflowRegistry = ({ children, mapping, contentWrapper, exportConfig })=>{
13
13
  const parentRegistry = useWorkflowRegistry();
14
14
  const mergedRegistry = {
15
15
  ...parentRegistry.mapping,
@@ -19,12 +19,17 @@ const WorkflowRegistry = ({ children, mapping, contentWrapper })=>{
19
19
  ...parentRegistry.contentWrapper,
20
20
  ...contentWrapper
21
21
  };
22
+ const mergedExportConfig = {
23
+ ...parentRegistry.exportConfig,
24
+ ...exportConfig
25
+ };
22
26
  return /*#__PURE__*/ jsx(SessionContext.Provider, {
23
27
  value: null,
24
28
  children: /*#__PURE__*/ jsx(WorkflowRegistryContext.Provider, {
25
29
  value: {
26
30
  mapping: mergedRegistry,
27
- contentWrapper: mergedContentWrapper
31
+ contentWrapper: mergedContentWrapper,
32
+ exportConfig: mergedExportConfig
28
33
  },
29
34
  children: children
30
35
  })
@@ -41,7 +46,7 @@ const Markdown = ({ className, streaming, config, components, session, ...props
41
46
  isStreaming: !!(null == streaming ? void 0 : streaming.hasNextChunk)
42
47
  },
43
48
  children: /*#__PURE__*/ jsx(XMarkdown, {
44
- className: clsx(className, 'x-markdown-light'),
49
+ className: clsx(className, 'x-markdown-light', 'sdk-markdown-root'),
45
50
  streaming: {
46
51
  ...streaming,
47
52
  enableAnimation: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtfai",
3
- "version": "1.8.6",
3
+ "version": "1.8.8",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -39,6 +39,7 @@
39
39
  "compressorjs": "^1.3.0",
40
40
  "cos-js-sdk-v5": "^1.10.1",
41
41
  "jsonrepair": "^3.14.0",
42
+ "mermaid": "^11.12.2",
42
43
  "uuid": "^13.0.2",
43
44
  "@our-llm/shared": "3.0.2"
44
45
  },