@antglobal/copilot-cards-mini-program 0.0.0 → 1.0.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 +1 -3
- package/dist/action-context/index.d.ts +44 -0
- package/dist/action-context/index.js +71 -0
- package/dist/action-context/index.js.map +1 -0
- package/dist/adapter/alipay.d.ts +22 -0
- package/dist/adapter/alipay.js +66 -0
- package/dist/adapter/alipay.js.map +1 -0
- package/dist/adapter/index.d.ts +53 -0
- package/dist/adapter/index.js +3 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/adapter/wechat.d.ts +22 -0
- package/dist/adapter/wechat.js +66 -0
- package/dist/adapter/wechat.js.map +1 -0
- package/dist/components/card-button/index.acss +61 -0
- package/dist/components/card-button/index.axml +9 -0
- package/dist/components/card-button/index.d.ts +23 -0
- package/dist/components/card-button/index.js +27 -0
- package/dist/components/card-button/index.js.map +1 -0
- package/dist/components/card-button/index.wxml +9 -0
- package/dist/components/card-divider/index.acss +8 -0
- package/dist/components/card-divider/index.axml +5 -0
- package/dist/components/card-divider/index.d.ts +11 -0
- package/dist/components/card-divider/index.js +14 -0
- package/dist/components/card-divider/index.js.map +1 -0
- package/dist/components/card-divider/index.wxml +5 -0
- package/dist/components/card-image/index.acss +7 -0
- package/dist/components/card-image/index.axml +9 -0
- package/dist/components/card-image/index.d.ts +22 -0
- package/dist/components/card-image/index.js +23 -0
- package/dist/components/card-image/index.js.map +1 -0
- package/dist/components/card-image/index.wxml +9 -0
- package/dist/components/card-input/index.acss +24 -0
- package/dist/components/card-input/index.axml +16 -0
- package/dist/components/card-input/index.d.ts +25 -0
- package/dist/components/card-input/index.js +46 -0
- package/dist/components/card-input/index.js.map +1 -0
- package/dist/components/card-input/index.wxml +16 -0
- package/dist/components/card-node/index.acss +5 -0
- package/dist/components/card-node/index.axml +27 -0
- package/dist/components/card-node/index.d.ts +31 -0
- package/dist/components/card-node/index.js +29 -0
- package/dist/components/card-node/index.js.map +1 -0
- package/dist/components/card-node/index.wxml +27 -0
- package/dist/components/card-root/index.acss +4 -0
- package/dist/components/card-root/index.axml +4 -0
- package/dist/components/card-root/index.d.ts +78 -0
- package/dist/components/card-root/index.js +94 -0
- package/dist/components/card-root/index.js.map +1 -0
- package/dist/components/card-root/index.wxml +4 -0
- package/dist/components/card-text/index.acss +11 -0
- package/dist/components/card-text/index.axml +8 -0
- package/dist/components/card-text/index.d.ts +23 -0
- package/dist/components/card-text/index.js +24 -0
- package/dist/components/card-text/index.js.map +1 -0
- package/dist/components/card-text/index.wxml +8 -0
- package/dist/events/index.d.ts +37 -0
- package/dist/events/index.js +51 -0
- package/dist/events/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer/index.d.ts +67 -0
- package/dist/renderer/index.js +159 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/transform.d.ts +24 -0
- package/dist/renderer/transform.js +88 -0
- package/dist/renderer/transform.js.map +1 -0
- package/dist/streaming/index.d.ts +80 -0
- package/dist/streaming/index.js +231 -0
- package/dist/streaming/index.js.map +1 -0
- package/package.json +30 -7
- package/LEGAL.md +0 -7
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { AlipayAdapter } from './adapter/alipay.js';
|
|
2
|
+
export { WeChatAdapter } from './adapter/wechat.js';
|
|
3
|
+
export { createMiniProgramActionContext } from './action-context/index.js';
|
|
4
|
+
export { renderForMiniProgram } from './renderer/index.js';
|
|
5
|
+
export { createCardRootComponent } from './components/card-root/index.js';
|
|
6
|
+
export { createCardNodeComponent } from './components/card-node/index.js';
|
|
7
|
+
export { createCardTextComponent } from './components/card-text/index.js';
|
|
8
|
+
export { createCardButtonComponent } from './components/card-button/index.js';
|
|
9
|
+
export { createCardImageComponent } from './components/card-image/index.js';
|
|
10
|
+
export { createCardInputComponent } from './components/card-input/index.js';
|
|
11
|
+
export { createCardDividerComponent } from './components/card-divider/index.js';
|
|
12
|
+
export { createEventDetail, nativeToSchemaEventMap, schemaToAlipayEventMap, schemaToWeChatEventMap } from './events/index.js';
|
|
13
|
+
export { createStreamingCardInstance } from './streaming/index.js';
|
|
14
|
+
export { convertLegacySchema, createLifecycleManager, hasExpression, isLegacySchema, normalizeSchema, parseSchema, registerActionHandler, resolveDeep, resolveExpression, runActionStep, runActionSteps, validateSchema } from '@antglobal/copilot-cards-core';
|
|
15
|
+
export { transformNode, transformTree } from './renderer/transform.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ExpressionContext, CardSchemaInput } from '@antglobal/copilot-cards-core';
|
|
2
|
+
import { MiniProgramAdapter } from '../adapter/index.js';
|
|
3
|
+
export { transformNode, transformTree } from './transform.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mini-program renderer — converts a CardSchema into mini-program
|
|
7
|
+
* component-compatible data structures.
|
|
8
|
+
*
|
|
9
|
+
* Since mini-programs use their own template engines (WXML / AXML),
|
|
10
|
+
* this renderer doesn't produce DOM. Instead, it produces a normalised
|
|
11
|
+
* data tree that can be consumed by mini-program templates.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface MiniProgramRenderNode {
|
|
15
|
+
id: string;
|
|
16
|
+
type: string;
|
|
17
|
+
props: Record<string, unknown>;
|
|
18
|
+
children: MiniProgramRenderNode[];
|
|
19
|
+
visible: boolean;
|
|
20
|
+
/** Whether this element is in disabled state */
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
/** Flattened event handler keys, e.g. ['onClick', 'onChange'] */
|
|
23
|
+
events: string[];
|
|
24
|
+
}
|
|
25
|
+
interface MiniProgramRendererOptions {
|
|
26
|
+
/** Initial variables data context */
|
|
27
|
+
data?: ExpressionContext;
|
|
28
|
+
/** Platform adapter instance */
|
|
29
|
+
adapter: MiniProgramAdapter;
|
|
30
|
+
/** Bot ID for bot-scoped action handlers */
|
|
31
|
+
botId?: string;
|
|
32
|
+
/** Event emitter — called when an emit action fires */
|
|
33
|
+
onEmit?: (eventName: string, payload?: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
interface MiniProgramCardInstance {
|
|
36
|
+
/** Get the current render data tree (pass to setData in mini-program component) */
|
|
37
|
+
getData(): {
|
|
38
|
+
tree: MiniProgramRenderNode;
|
|
39
|
+
};
|
|
40
|
+
/** Update variables and re-compute the data tree */
|
|
41
|
+
updateVariables(vars: Record<string, any>): {
|
|
42
|
+
tree: MiniProgramRenderNode;
|
|
43
|
+
};
|
|
44
|
+
/** Handle a component event (called from template bindtap/bindinput etc.) */
|
|
45
|
+
handleEvent(nodeId: string, eventName: string, eventDetail?: any): Promise<void>;
|
|
46
|
+
/** Dispose the card instance and clean up resources */
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a mini-program card instance from a schema.
|
|
51
|
+
*
|
|
52
|
+
* Unlike the web `renderCard` which directly produces DOM, this function
|
|
53
|
+
* produces a data tree that mini-program templates consume via setData.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const instance = renderForMiniProgram(schema, {
|
|
58
|
+
* adapter: new AlipayAdapter(),
|
|
59
|
+
* data: { userName: 'Alice' },
|
|
60
|
+
* });
|
|
61
|
+
* this.setData({ tree: instance.getData().tree });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function renderForMiniProgram(schemaInput: CardSchemaInput, options: MiniProgramRendererOptions): MiniProgramCardInstance;
|
|
65
|
+
|
|
66
|
+
export { renderForMiniProgram };
|
|
67
|
+
export type { MiniProgramCardInstance, MiniProgramRenderNode, MiniProgramRendererOptions };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { normalizeSchema, validateSchema, parseSchema, createLifecycleManager, runActionSteps, resolveActionRef } from '@antglobal/copilot-cards-core';
|
|
2
|
+
import { createMiniProgramActionContext } from '../action-context/index.js';
|
|
3
|
+
import { transformTree } from './transform.js';
|
|
4
|
+
export { transformNode } from './transform.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mini-program renderer — converts a CardSchema into mini-program
|
|
8
|
+
* component-compatible data structures.
|
|
9
|
+
*
|
|
10
|
+
* Since mini-programs use their own template engines (WXML / AXML),
|
|
11
|
+
* this renderer doesn't produce DOM. Instead, it produces a normalised
|
|
12
|
+
* data tree that can be consumed by mini-program templates.
|
|
13
|
+
*/
|
|
14
|
+
// ─── Main API ────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Create a mini-program card instance from a schema.
|
|
17
|
+
*
|
|
18
|
+
* Unlike the web `renderCard` which directly produces DOM, this function
|
|
19
|
+
* produces a data tree that mini-program templates consume via setData.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const instance = renderForMiniProgram(schema, {
|
|
24
|
+
* adapter: new AlipayAdapter(),
|
|
25
|
+
* data: { userName: 'Alice' },
|
|
26
|
+
* });
|
|
27
|
+
* this.setData({ tree: instance.getData().tree });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function renderForMiniProgram(schemaInput, options) {
|
|
31
|
+
// 0. Normalize schema (auto-convert legacy format)
|
|
32
|
+
const schema = normalizeSchema(schemaInput);
|
|
33
|
+
// 1. Validate
|
|
34
|
+
const errors = validateSchema(schema);
|
|
35
|
+
if (errors.length > 0) {
|
|
36
|
+
throw new Error(`[renderForMiniProgram] Invalid schema:\n${errors.join('\n')}`);
|
|
37
|
+
}
|
|
38
|
+
// 2. Parse into render tree
|
|
39
|
+
const tree = parseSchema(schema);
|
|
40
|
+
// 3. Reactive variables store
|
|
41
|
+
let variables = { ...schema.variables, ...options.data };
|
|
42
|
+
// 4. Schema-level action definitions
|
|
43
|
+
const schemaActions = schema.actions ?? {};
|
|
44
|
+
// 5. Event-node mapping (for fast lookup on handleEvent)
|
|
45
|
+
const nodeEventsMap = buildNodeEventsMap(tree, schemaActions);
|
|
46
|
+
// 6. Lifecycle manager
|
|
47
|
+
const lifecycleManager = createLifecycleManager();
|
|
48
|
+
const abortController = new AbortController();
|
|
49
|
+
// 7. Action context
|
|
50
|
+
let actionContext = createMiniProgramActionContext(options.adapter, {
|
|
51
|
+
setVariable: (key, value) => {
|
|
52
|
+
variables[key] = value;
|
|
53
|
+
},
|
|
54
|
+
emit: options.onEmit,
|
|
55
|
+
variables,
|
|
56
|
+
botId: options.botId,
|
|
57
|
+
abortSignal: abortController.signal,
|
|
58
|
+
});
|
|
59
|
+
function rebuildActionContext() {
|
|
60
|
+
actionContext = createMiniProgramActionContext(options.adapter, {
|
|
61
|
+
setVariable: (key, value) => {
|
|
62
|
+
variables[key] = value;
|
|
63
|
+
},
|
|
64
|
+
emit: options.onEmit,
|
|
65
|
+
variables,
|
|
66
|
+
botId: options.botId,
|
|
67
|
+
abortSignal: abortController.signal,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// 8. Register lifecycles and trigger mount
|
|
71
|
+
registerLifecycles(tree, lifecycleManager, actionContext);
|
|
72
|
+
// 9. Compute initial data
|
|
73
|
+
let currentTree = transformTree(tree, variables);
|
|
74
|
+
// ── Instance API ───────────────────────────────────────────────
|
|
75
|
+
return {
|
|
76
|
+
getData() {
|
|
77
|
+
return { tree: currentTree };
|
|
78
|
+
},
|
|
79
|
+
updateVariables(newVars) {
|
|
80
|
+
variables = { ...variables, ...newVars };
|
|
81
|
+
rebuildActionContext();
|
|
82
|
+
currentTree = transformTree(tree, variables);
|
|
83
|
+
return { tree: currentTree };
|
|
84
|
+
},
|
|
85
|
+
async handleEvent(nodeId, eventName, eventDetail) {
|
|
86
|
+
// Write event detail to _event variable
|
|
87
|
+
if (eventDetail != null) {
|
|
88
|
+
variables._event = eventDetail;
|
|
89
|
+
}
|
|
90
|
+
// Handle variableKey auto-sync for input events
|
|
91
|
+
if (eventName === 'onInput' || eventName === 'onChange') {
|
|
92
|
+
const nodeInfo = nodeEventsMap.get(nodeId);
|
|
93
|
+
if (nodeInfo?.variableKey && eventDetail?.value !== undefined) {
|
|
94
|
+
variables[nodeInfo.variableKey] = eventDetail.value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Look up action steps for this node + event
|
|
98
|
+
const nodeInfo = nodeEventsMap.get(nodeId);
|
|
99
|
+
const steps = nodeInfo?.events[eventName];
|
|
100
|
+
if (steps) {
|
|
101
|
+
rebuildActionContext();
|
|
102
|
+
await runActionSteps(steps, actionContext);
|
|
103
|
+
}
|
|
104
|
+
// Re-compute tree after action execution (variables may have changed)
|
|
105
|
+
rebuildActionContext();
|
|
106
|
+
currentTree = transformTree(tree, variables);
|
|
107
|
+
},
|
|
108
|
+
dispose() {
|
|
109
|
+
abortController.abort();
|
|
110
|
+
lifecycleManager.dispose(actionContext);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Build a map of nodeId → { events, variableKey } for fast event lookup.
|
|
116
|
+
*/
|
|
117
|
+
function buildNodeEventsMap(tree, schemaActions) {
|
|
118
|
+
const map = new Map();
|
|
119
|
+
function walk(node) {
|
|
120
|
+
const events = {};
|
|
121
|
+
if (node.events) {
|
|
122
|
+
for (const [eventName, eventValue] of Object.entries(node.events)) {
|
|
123
|
+
if (!eventValue)
|
|
124
|
+
continue;
|
|
125
|
+
const resolvedSteps = resolveActionRef(eventValue, schemaActions);
|
|
126
|
+
if (resolvedSteps) {
|
|
127
|
+
events[eventName] = resolvedSteps;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const variableKey = node.props?.variableKey;
|
|
132
|
+
if (Object.keys(events).length > 0 || variableKey) {
|
|
133
|
+
map.set(node.id, { events, variableKey });
|
|
134
|
+
}
|
|
135
|
+
for (const child of node.children) {
|
|
136
|
+
walk(child);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
walk(tree);
|
|
140
|
+
return map;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Register lifecycle hooks for all nodes in the tree.
|
|
144
|
+
*/
|
|
145
|
+
function registerLifecycles(tree, lifecycleManager, actionContext) {
|
|
146
|
+
function walk(node) {
|
|
147
|
+
if (node.lifecycle) {
|
|
148
|
+
lifecycleManager.register(node.id, node.lifecycle);
|
|
149
|
+
lifecycleManager.mount(node.id, actionContext);
|
|
150
|
+
}
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
walk(child);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
walk(tree);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export { renderForMiniProgram, transformTree };
|
|
159
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RenderTreeNode } from '@antglobal/copilot-cards-core';
|
|
2
|
+
import { MiniProgramRenderNode } from './index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transform — converts a RenderTreeNode into a MiniProgramRenderNode.
|
|
6
|
+
*
|
|
7
|
+
* Performs expression resolution, directive evaluation, and event collection
|
|
8
|
+
* to produce a plain data object consumable by mini-program templates via setData.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Recursively transform a RenderTreeNode into a MiniProgramRenderNode.
|
|
13
|
+
*
|
|
14
|
+
* This resolves all expressions against the current variables,
|
|
15
|
+
* evaluates directives (visible, disabled), and flattens events
|
|
16
|
+
* into a list of event names for template binding.
|
|
17
|
+
*/
|
|
18
|
+
declare function transformNode(node: RenderTreeNode, variables: Record<string, any>): MiniProgramRenderNode;
|
|
19
|
+
/**
|
|
20
|
+
* Transform an entire render tree into a mini-program data tree.
|
|
21
|
+
*/
|
|
22
|
+
declare function transformTree(tree: RenderTreeNode, variables: Record<string, any>): MiniProgramRenderNode;
|
|
23
|
+
|
|
24
|
+
export { transformNode, transformTree };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { hasExpression, resolveExpression, resolveDeep, resolveExpressionValue } from '@antglobal/copilot-cards-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transform — converts a RenderTreeNode into a MiniProgramRenderNode.
|
|
5
|
+
*
|
|
6
|
+
* Performs expression resolution, directive evaluation, and event collection
|
|
7
|
+
* to produce a plain data object consumable by mini-program templates via setData.
|
|
8
|
+
*/
|
|
9
|
+
// ─── Transform ───────────────────────────────────────────────────
|
|
10
|
+
/**
|
|
11
|
+
* Recursively transform a RenderTreeNode into a MiniProgramRenderNode.
|
|
12
|
+
*
|
|
13
|
+
* This resolves all expressions against the current variables,
|
|
14
|
+
* evaluates directives (visible, disabled), and flattens events
|
|
15
|
+
* into a list of event names for template binding.
|
|
16
|
+
*/
|
|
17
|
+
function transformNode(node, variables) {
|
|
18
|
+
// 1. Check directives.visible
|
|
19
|
+
let visible = true;
|
|
20
|
+
if (node.directives?.visible) {
|
|
21
|
+
const visibleExpr = node.directives.visible;
|
|
22
|
+
const resolved = hasExpression(visibleExpr)
|
|
23
|
+
? resolveExpression(visibleExpr, variables)
|
|
24
|
+
: visibleExpr;
|
|
25
|
+
if (resolved === false || resolved === 'false' || resolved === '' || resolved === 0) {
|
|
26
|
+
visible = false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// If not visible, return early with minimal node (children won't be resolved)
|
|
30
|
+
if (!visible) {
|
|
31
|
+
return {
|
|
32
|
+
id: node.id,
|
|
33
|
+
type: node.type,
|
|
34
|
+
props: {},
|
|
35
|
+
children: [],
|
|
36
|
+
visible: false,
|
|
37
|
+
disabled: false,
|
|
38
|
+
events: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// 2. Resolve props with variable expressions
|
|
42
|
+
const resolvedProps = resolveDeep(node.props, variables);
|
|
43
|
+
// Resolve content if it's an ExpressionValue object
|
|
44
|
+
if (resolvedProps.content &&
|
|
45
|
+
typeof resolvedProps.content === 'object' &&
|
|
46
|
+
resolvedProps.content !== null &&
|
|
47
|
+
'type' in resolvedProps.content) {
|
|
48
|
+
resolvedProps.content = resolveExpressionValue(resolvedProps.content, variables);
|
|
49
|
+
}
|
|
50
|
+
// 3. Check directives.disabled
|
|
51
|
+
let disabled = false;
|
|
52
|
+
if (node.directives?.disabled) {
|
|
53
|
+
const disabledExpr = node.directives.disabled;
|
|
54
|
+
const resolved = hasExpression(disabledExpr)
|
|
55
|
+
? resolveExpression(disabledExpr, variables)
|
|
56
|
+
: disabledExpr;
|
|
57
|
+
disabled = resolved === true || resolved === 'true' || resolved === 1;
|
|
58
|
+
}
|
|
59
|
+
// 4. Collect event names
|
|
60
|
+
const events = [];
|
|
61
|
+
if (node.events && !disabled) {
|
|
62
|
+
for (const eventName of Object.keys(node.events)) {
|
|
63
|
+
if (node.events[eventName]) {
|
|
64
|
+
events.push(eventName);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 5. Recursively transform children
|
|
69
|
+
const children = node.children.map((child) => transformNode(child, variables));
|
|
70
|
+
return {
|
|
71
|
+
id: node.id,
|
|
72
|
+
type: node.type,
|
|
73
|
+
props: resolvedProps,
|
|
74
|
+
children,
|
|
75
|
+
visible,
|
|
76
|
+
disabled,
|
|
77
|
+
events,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Transform an entire render tree into a mini-program data tree.
|
|
82
|
+
*/
|
|
83
|
+
function transformTree(tree, variables) {
|
|
84
|
+
return transformNode(tree, variables);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { transformNode, transformTree };
|
|
88
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { StreamingCommand, ExpressionContext, StreamingParserOptions } from '@antglobal/copilot-cards-core';
|
|
2
|
+
import { MiniProgramRenderNode } from '../renderer/index.js';
|
|
3
|
+
import { MiniProgramAdapter } from '../adapter/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Streaming Adapter for Mini-Program — enables progressive card rendering
|
|
7
|
+
* in WeChat/Alipay/DingTalk mini-programs via streaming commands.
|
|
8
|
+
*
|
|
9
|
+
* Since mini-programs cannot directly manipulate DOM, this module
|
|
10
|
+
* produces incremental setData patches that the framework can apply efficiently.
|
|
11
|
+
*
|
|
12
|
+
* Key differences from Web streaming:
|
|
13
|
+
* - Returns data patches instead of manipulating DOM
|
|
14
|
+
* - Uses path-based setData for efficient mini-program updates
|
|
15
|
+
* - Computes minimal diff for appendContent operations
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface MiniProgramStreamingOptions {
|
|
19
|
+
/** Platform adapter instance */
|
|
20
|
+
adapter: MiniProgramAdapter;
|
|
21
|
+
/** Initial variables data context */
|
|
22
|
+
data?: ExpressionContext;
|
|
23
|
+
/** Bot ID for bot-scoped action handlers */
|
|
24
|
+
botId?: string;
|
|
25
|
+
/** Event emitter — called when an emit action fires */
|
|
26
|
+
onEmit?: (eventName: string, payload?: unknown) => void;
|
|
27
|
+
/** Parser options */
|
|
28
|
+
parserOptions?: StreamingParserOptions;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Represents a data patch that should be applied via mini-program's setData.
|
|
32
|
+
*/
|
|
33
|
+
interface DataPatch {
|
|
34
|
+
/** The path-value pairs for setData, e.g. { 'tree.children[0].props.content': 'new text' } */
|
|
35
|
+
data: Record<string, any>;
|
|
36
|
+
}
|
|
37
|
+
interface MiniProgramStreamingCardInstance {
|
|
38
|
+
/** Apply a single streaming command, returns setData patch or null */
|
|
39
|
+
applyCommand(command: StreamingCommand): DataPatch | null;
|
|
40
|
+
/** Apply multiple commands, returns array of patches */
|
|
41
|
+
applyCommands(commands: StreamingCommand[]): DataPatch[];
|
|
42
|
+
/** Feed raw text chunk, returns array of patches */
|
|
43
|
+
feed(chunk: string): DataPatch[];
|
|
44
|
+
/** Flush parser buffer, returns remaining patches */
|
|
45
|
+
flush(): DataPatch[];
|
|
46
|
+
/** Get current full render data */
|
|
47
|
+
getData(): {
|
|
48
|
+
tree: MiniProgramRenderNode | null;
|
|
49
|
+
};
|
|
50
|
+
/** Handle a component event */
|
|
51
|
+
handleEvent(nodeId: string, eventName: string, eventDetail?: any): Promise<DataPatch | null>;
|
|
52
|
+
/** Dispose the instance */
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a mini-program streaming card instance.
|
|
57
|
+
*
|
|
58
|
+
* Unlike the web streaming renderer which directly updates DOM,
|
|
59
|
+
* this returns data patches that should be applied via the
|
|
60
|
+
* mini-program framework's setData mechanism.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* // In mini-program component
|
|
65
|
+
* const streaming = createStreamingCardInstance({
|
|
66
|
+
* adapter: new AlipayAdapter(),
|
|
67
|
+
* data: { userName: 'Alice' },
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* // Feed streaming data (e.g. from WebSocket)
|
|
71
|
+
* socket.onmessage = (e) => {
|
|
72
|
+
* const patches = streaming.feed(e.data);
|
|
73
|
+
* patches.forEach(patch => this.setData(patch.data));
|
|
74
|
+
* };
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function createStreamingCardInstance(options: MiniProgramStreamingOptions): MiniProgramStreamingCardInstance;
|
|
78
|
+
|
|
79
|
+
export { createStreamingCardInstance };
|
|
80
|
+
export type { DataPatch, MiniProgramStreamingCardInstance, MiniProgramStreamingOptions };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { StreamingParser, StreamingEngine, normalizeSchema } from '@antglobal/copilot-cards-core';
|
|
2
|
+
import { renderForMiniProgram } from '../renderer/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Streaming Adapter for Mini-Program — enables progressive card rendering
|
|
6
|
+
* in WeChat/Alipay/DingTalk mini-programs via streaming commands.
|
|
7
|
+
*
|
|
8
|
+
* Since mini-programs cannot directly manipulate DOM, this module
|
|
9
|
+
* produces incremental setData patches that the framework can apply efficiently.
|
|
10
|
+
*
|
|
11
|
+
* Key differences from Web streaming:
|
|
12
|
+
* - Returns data patches instead of manipulating DOM
|
|
13
|
+
* - Uses path-based setData for efficient mini-program updates
|
|
14
|
+
* - Computes minimal diff for appendContent operations
|
|
15
|
+
*/
|
|
16
|
+
// ─── Main API ────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Create a mini-program streaming card instance.
|
|
19
|
+
*
|
|
20
|
+
* Unlike the web streaming renderer which directly updates DOM,
|
|
21
|
+
* this returns data patches that should be applied via the
|
|
22
|
+
* mini-program framework's setData mechanism.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // In mini-program component
|
|
27
|
+
* const streaming = createStreamingCardInstance({
|
|
28
|
+
* adapter: new AlipayAdapter(),
|
|
29
|
+
* data: { userName: 'Alice' },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Feed streaming data (e.g. from WebSocket)
|
|
33
|
+
* socket.onmessage = (e) => {
|
|
34
|
+
* const patches = streaming.feed(e.data);
|
|
35
|
+
* patches.forEach(patch => this.setData(patch.data));
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function createStreamingCardInstance(options) {
|
|
40
|
+
const parser = new StreamingParser(options.parserOptions);
|
|
41
|
+
let currentInstance = null;
|
|
42
|
+
let currentTree = null;
|
|
43
|
+
let variables = { ...options.data };
|
|
44
|
+
// Track node paths for efficient patching
|
|
45
|
+
const nodePathMap = new Map(); // elementId → setData path
|
|
46
|
+
/**
|
|
47
|
+
* Build the node path map by traversing the tree.
|
|
48
|
+
*/
|
|
49
|
+
function buildNodePaths(node, basePath) {
|
|
50
|
+
nodePathMap.set(node.id, basePath);
|
|
51
|
+
node.children.forEach((child, index) => {
|
|
52
|
+
buildNodePaths(child, `${basePath}.children[${index}]`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a full tree patch (for initial render or full re-render).
|
|
57
|
+
*/
|
|
58
|
+
function createFullPatch() {
|
|
59
|
+
return { data: { tree: currentTree } };
|
|
60
|
+
}
|
|
61
|
+
// ─── Engine Setup ───────────────────────────────────────────────
|
|
62
|
+
const pendingPatches = [];
|
|
63
|
+
const engine = new StreamingEngine({
|
|
64
|
+
onSurfaceCreated(surfaceId, schemaInput) {
|
|
65
|
+
if (schemaInput) {
|
|
66
|
+
const schema = normalizeSchema(schemaInput);
|
|
67
|
+
variables = { ...schema.variables, ...options.data };
|
|
68
|
+
currentInstance = renderForMiniProgram(schema, {
|
|
69
|
+
adapter: options.adapter,
|
|
70
|
+
data: variables,
|
|
71
|
+
botId: options.botId,
|
|
72
|
+
onEmit: options.onEmit,
|
|
73
|
+
});
|
|
74
|
+
currentTree = currentInstance.getData().tree;
|
|
75
|
+
if (currentTree) {
|
|
76
|
+
nodePathMap.clear();
|
|
77
|
+
buildNodePaths(currentTree, 'tree');
|
|
78
|
+
}
|
|
79
|
+
pendingPatches.push(createFullPatch());
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
onComponentsUpdated(surfaceId, changes) {
|
|
83
|
+
// For mini-program, component tree changes require full re-render
|
|
84
|
+
// because we can't insert DOM nodes — we need to regenerate the data tree
|
|
85
|
+
const schema = engine.getSchema(surfaceId);
|
|
86
|
+
if (!schema)
|
|
87
|
+
return;
|
|
88
|
+
currentInstance = renderForMiniProgram(schema, {
|
|
89
|
+
adapter: options.adapter,
|
|
90
|
+
data: variables,
|
|
91
|
+
botId: options.botId,
|
|
92
|
+
onEmit: options.onEmit,
|
|
93
|
+
});
|
|
94
|
+
currentTree = currentInstance.getData().tree;
|
|
95
|
+
if (currentTree) {
|
|
96
|
+
nodePathMap.clear();
|
|
97
|
+
buildNodePaths(currentTree, 'tree');
|
|
98
|
+
}
|
|
99
|
+
pendingPatches.push(createFullPatch());
|
|
100
|
+
},
|
|
101
|
+
onDataModelUpdated(surfaceId, path, value) {
|
|
102
|
+
if (!currentInstance)
|
|
103
|
+
return;
|
|
104
|
+
// Update variables and regenerate tree
|
|
105
|
+
const pathParts = path.replace(/^\//, '').split('/').filter(Boolean);
|
|
106
|
+
if (pathParts.length === 0) {
|
|
107
|
+
if (typeof value === 'object' && value !== null) {
|
|
108
|
+
variables = { ...variables, ...value };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
let current = variables;
|
|
113
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
114
|
+
if (current[pathParts[i]] === undefined)
|
|
115
|
+
current[pathParts[i]] = {};
|
|
116
|
+
current = current[pathParts[i]];
|
|
117
|
+
}
|
|
118
|
+
current[pathParts[pathParts.length - 1]] = value;
|
|
119
|
+
}
|
|
120
|
+
const result = currentInstance.updateVariables(variables);
|
|
121
|
+
currentTree = result.tree;
|
|
122
|
+
if (currentTree) {
|
|
123
|
+
nodePathMap.clear();
|
|
124
|
+
buildNodePaths(currentTree, 'tree');
|
|
125
|
+
}
|
|
126
|
+
pendingPatches.push(createFullPatch());
|
|
127
|
+
},
|
|
128
|
+
onContentAppended(surfaceId, elementId, content) {
|
|
129
|
+
// Try to do a targeted patch for text content
|
|
130
|
+
const nodePath = nodePathMap.get(elementId);
|
|
131
|
+
if (nodePath && currentTree) {
|
|
132
|
+
// Find the node and update its content in-place
|
|
133
|
+
const node = findNodeById(currentTree, elementId);
|
|
134
|
+
if (node) {
|
|
135
|
+
const currentContent = typeof node.props.content === 'string'
|
|
136
|
+
? node.props.content : '';
|
|
137
|
+
node.props.content = currentContent + content;
|
|
138
|
+
// Generate a path-based setData patch for efficiency
|
|
139
|
+
pendingPatches.push({
|
|
140
|
+
data: { [`${nodePath}.props.content`]: node.props.content },
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Fallback: full re-render
|
|
146
|
+
if (currentInstance) {
|
|
147
|
+
const schema = engine.getSchema(surfaceId);
|
|
148
|
+
if (schema) {
|
|
149
|
+
variables = { ...schema.variables, ...variables };
|
|
150
|
+
const result = currentInstance.updateVariables(variables);
|
|
151
|
+
currentTree = result.tree;
|
|
152
|
+
pendingPatches.push(createFullPatch());
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
onSurfaceDeleted(surfaceId) {
|
|
157
|
+
currentInstance?.dispose();
|
|
158
|
+
currentInstance = null;
|
|
159
|
+
currentTree = null;
|
|
160
|
+
nodePathMap.clear();
|
|
161
|
+
pendingPatches.push({ data: { tree: null } });
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
165
|
+
function findNodeById(node, id) {
|
|
166
|
+
if (node.id === id)
|
|
167
|
+
return node;
|
|
168
|
+
for (const child of node.children) {
|
|
169
|
+
const found = findNodeById(child, id);
|
|
170
|
+
if (found)
|
|
171
|
+
return found;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
function drainPatches() {
|
|
176
|
+
const patches = [...pendingPatches];
|
|
177
|
+
pendingPatches.length = 0;
|
|
178
|
+
return patches;
|
|
179
|
+
}
|
|
180
|
+
// ─── Public API ─────────────────────────────────────────────────
|
|
181
|
+
return {
|
|
182
|
+
applyCommand(command) {
|
|
183
|
+
engine.apply(command);
|
|
184
|
+
const patches = drainPatches();
|
|
185
|
+
return patches.length > 0 ? patches[patches.length - 1] : null;
|
|
186
|
+
},
|
|
187
|
+
applyCommands(commands) {
|
|
188
|
+
engine.applyBatch(commands);
|
|
189
|
+
return drainPatches();
|
|
190
|
+
},
|
|
191
|
+
feed(chunk) {
|
|
192
|
+
const commands = parser.parse(chunk);
|
|
193
|
+
for (const cmd of commands) {
|
|
194
|
+
engine.apply(cmd);
|
|
195
|
+
}
|
|
196
|
+
return drainPatches();
|
|
197
|
+
},
|
|
198
|
+
flush() {
|
|
199
|
+
const commands = parser.flush();
|
|
200
|
+
for (const cmd of commands) {
|
|
201
|
+
engine.apply(cmd);
|
|
202
|
+
}
|
|
203
|
+
return drainPatches();
|
|
204
|
+
},
|
|
205
|
+
getData() {
|
|
206
|
+
return { tree: currentTree };
|
|
207
|
+
},
|
|
208
|
+
async handleEvent(nodeId, eventName, eventDetail) {
|
|
209
|
+
if (!currentInstance)
|
|
210
|
+
return null;
|
|
211
|
+
await currentInstance.handleEvent(nodeId, eventName, eventDetail);
|
|
212
|
+
currentTree = currentInstance.getData().tree;
|
|
213
|
+
if (currentTree) {
|
|
214
|
+
nodePathMap.clear();
|
|
215
|
+
buildNodePaths(currentTree, 'tree');
|
|
216
|
+
}
|
|
217
|
+
return createFullPatch();
|
|
218
|
+
},
|
|
219
|
+
dispose() {
|
|
220
|
+
currentInstance?.dispose();
|
|
221
|
+
currentInstance = null;
|
|
222
|
+
currentTree = null;
|
|
223
|
+
nodePathMap.clear();
|
|
224
|
+
engine.dispose();
|
|
225
|
+
parser.reset();
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export { createStreamingCardInstance };
|
|
231
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|