@antglobal/copilot-cards-mini-program 0.0.0 → 0.0.20260724033338-dev.13

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.
Files changed (67) hide show
  1. package/README.md +126 -2
  2. package/dist/action-context/index.d.ts +44 -0
  3. package/dist/action-context/index.js +70 -0
  4. package/dist/adapter/alipay.d.ts +22 -0
  5. package/dist/adapter/alipay.js +65 -0
  6. package/dist/adapter/index.d.ts +53 -0
  7. package/dist/adapter/index.js +2 -0
  8. package/dist/adapter/wechat.d.ts +22 -0
  9. package/dist/adapter/wechat.js +65 -0
  10. package/dist/components/card-button/index.acss +61 -0
  11. package/dist/components/card-button/index.axml +9 -0
  12. package/dist/components/card-button/index.d.ts +23 -0
  13. package/dist/components/card-button/index.js +26 -0
  14. package/dist/components/card-button/index.wxml +9 -0
  15. package/dist/components/card-divider/index.acss +8 -0
  16. package/dist/components/card-divider/index.axml +5 -0
  17. package/dist/components/card-divider/index.d.ts +11 -0
  18. package/dist/components/card-divider/index.js +13 -0
  19. package/dist/components/card-divider/index.wxml +5 -0
  20. package/dist/components/card-icon/definition.d.ts +15 -0
  21. package/dist/components/card-icon/definition.js +17 -0
  22. package/dist/components/card-icon/index.acss +19 -0
  23. package/dist/components/card-icon/index.axml +16 -0
  24. package/dist/components/card-icon/index.d.ts +1 -0
  25. package/dist/components/card-icon/index.js +8 -0
  26. package/dist/components/card-icon/index.json +3 -0
  27. package/dist/components/card-icon/index.wxml +16 -0
  28. package/dist/components/card-icon/index.wxss +19 -0
  29. package/dist/components/card-image/index.acss +7 -0
  30. package/dist/components/card-image/index.axml +9 -0
  31. package/dist/components/card-image/index.d.ts +22 -0
  32. package/dist/components/card-image/index.js +22 -0
  33. package/dist/components/card-image/index.wxml +9 -0
  34. package/dist/components/card-input/index.acss +24 -0
  35. package/dist/components/card-input/index.axml +16 -0
  36. package/dist/components/card-input/index.d.ts +25 -0
  37. package/dist/components/card-input/index.js +45 -0
  38. package/dist/components/card-input/index.wxml +16 -0
  39. package/dist/components/card-node/index.acss +5 -0
  40. package/dist/components/card-node/index.axml +30 -0
  41. package/dist/components/card-node/index.d.ts +31 -0
  42. package/dist/components/card-node/index.js +28 -0
  43. package/dist/components/card-node/index.wxml +30 -0
  44. package/dist/components/card-root/index.acss +4 -0
  45. package/dist/components/card-root/index.axml +4 -0
  46. package/dist/components/card-root/index.d.ts +78 -0
  47. package/dist/components/card-root/index.js +93 -0
  48. package/dist/components/card-root/index.wxml +4 -0
  49. package/dist/components/card-text/index.acss +11 -0
  50. package/dist/components/card-text/index.axml +8 -0
  51. package/dist/components/card-text/index.d.ts +23 -0
  52. package/dist/components/card-text/index.js +23 -0
  53. package/dist/components/card-text/index.wxml +8 -0
  54. package/dist/events/index.d.ts +37 -0
  55. package/dist/events/index.js +50 -0
  56. package/dist/icons.d.ts +3 -0
  57. package/dist/icons.js +135 -0
  58. package/dist/index.d.ts +17 -0
  59. package/dist/index.js +16 -0
  60. package/dist/renderer/index.d.ts +67 -0
  61. package/dist/renderer/index.js +158 -0
  62. package/dist/renderer/transform.d.ts +24 -0
  63. package/dist/renderer/transform.js +91 -0
  64. package/dist/streaming/index.d.ts +80 -0
  65. package/dist/streaming/index.js +230 -0
  66. package/package.json +38 -7
  67. package/LEGAL.md +0 -7
@@ -0,0 +1,23 @@
1
+ import { MiniProgramRenderNode } from '../../renderer/index.js';
2
+
3
+ /**
4
+ * card-text — Text component for mini-program.
5
+ * Renders text content with styling support.
6
+ */
7
+
8
+ interface CardTextInstance {
9
+ props: {
10
+ node: MiniProgramRenderNode | null;
11
+ };
12
+ triggerEvent(name: string, detail?: any): void;
13
+ }
14
+ declare function createCardTextComponent(): {
15
+ props: {
16
+ node: null;
17
+ };
18
+ methods: {
19
+ onTap(this: CardTextInstance): void;
20
+ };
21
+ };
22
+
23
+ export { createCardTextComponent };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * card-text — Text component for mini-program.
3
+ * Renders text content with styling support.
4
+ */
5
+ function createCardTextComponent() {
6
+ return {
7
+ props: {
8
+ node: null,
9
+ },
10
+ methods: {
11
+ onTap() {
12
+ if (!this.props.node?.events.includes('onClick'))
13
+ return;
14
+ this.triggerEvent('event', {
15
+ nodeId: this.props.node.id,
16
+ eventName: 'onClick',
17
+ });
18
+ },
19
+ },
20
+ };
21
+ }
22
+
23
+ export { createCardTextComponent };
@@ -0,0 +1,8 @@
1
+ <!-- card-text: WeChat template -->
2
+ <view
3
+ class="card-text {{node.props.bold ? 'card-text--bold' : ''}} {{node.props.italic ? 'card-text--italic' : ''}}"
4
+ style="font-size: {{node.props.fontSize || '14px'}}; color: {{node.props.color || '#333333'}}; line-height: {{node.props.lineHeight || '1.5'}}; text-align: {{node.props.textAlign || 'left'}}; {{node.props.style ? node.props._styleString : ''}}"
5
+ bindtap="onTap"
6
+ >
7
+ <text>{{node.props.content}}</text>
8
+ </view>
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Event system utilities for mini-program card components.
3
+ *
4
+ * Maps between schema event names and mini-program native events,
5
+ * and provides helpers for event bubbling.
6
+ */
7
+ /**
8
+ * Map from mini-program native event names to schema event names.
9
+ * Mini-program templates use `bindtap`, `bindinput`, etc.
10
+ * Schema uses `onClick`, `onInput`, etc.
11
+ */
12
+ declare const nativeToSchemaEventMap: Record<string, string>;
13
+ /**
14
+ * Map from schema event names to Alipay mini-program event bindings.
15
+ */
16
+ declare const schemaToAlipayEventMap: Record<string, string>;
17
+ /**
18
+ * Map from schema event names to WeChat mini-program event bindings.
19
+ */
20
+ declare const schemaToWeChatEventMap: Record<string, string>;
21
+ interface CardEventDetail {
22
+ /** The node ID that triggered the event */
23
+ nodeId: string;
24
+ /** The schema event name (e.g. 'onClick', 'onInput') */
25
+ eventName: string;
26
+ /** Optional event value (e.g. input value) */
27
+ value?: unknown;
28
+ /** Additional event data */
29
+ [key: string]: unknown;
30
+ }
31
+ /**
32
+ * Create a standardized event detail object for triggerEvent.
33
+ */
34
+ declare function createEventDetail(nodeId: string, eventName: string, extra?: Record<string, unknown>): CardEventDetail;
35
+
36
+ export { createEventDetail, nativeToSchemaEventMap, schemaToAlipayEventMap, schemaToWeChatEventMap };
37
+ export type { CardEventDetail };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Event system utilities for mini-program card components.
3
+ *
4
+ * Maps between schema event names and mini-program native events,
5
+ * and provides helpers for event bubbling.
6
+ */
7
+ // ─── Event Name Mapping ──────────────────────────────────────────
8
+ /**
9
+ * Map from mini-program native event names to schema event names.
10
+ * Mini-program templates use `bindtap`, `bindinput`, etc.
11
+ * Schema uses `onClick`, `onInput`, etc.
12
+ */
13
+ const nativeToSchemaEventMap = {
14
+ tap: 'onClick',
15
+ input: 'onInput',
16
+ change: 'onChange',
17
+ confirm: 'onSubmit',
18
+ blur: 'onChange',
19
+ longpress: 'onLongPress',
20
+ };
21
+ /**
22
+ * Map from schema event names to Alipay mini-program event bindings.
23
+ */
24
+ const schemaToAlipayEventMap = {
25
+ onClick: 'onTap',
26
+ onInput: 'onInput',
27
+ onChange: 'onBlur',
28
+ onSubmit: 'onConfirm',
29
+ };
30
+ /**
31
+ * Map from schema event names to WeChat mini-program event bindings.
32
+ */
33
+ const schemaToWeChatEventMap = {
34
+ onClick: 'bindtap',
35
+ onInput: 'bindinput',
36
+ onChange: 'bindblur',
37
+ onSubmit: 'bindconfirm',
38
+ };
39
+ /**
40
+ * Create a standardized event detail object for triggerEvent.
41
+ */
42
+ function createEventDetail(nodeId, eventName, extra) {
43
+ return {
44
+ nodeId,
45
+ eventName,
46
+ ...extra,
47
+ };
48
+ }
49
+
50
+ export { createEventDetail, nativeToSchemaEventMap, schemaToAlipayEventMap, schemaToWeChatEventMap };
@@ -0,0 +1,3 @@
1
+ declare function decorateMiniIconProps(props: Record<string, unknown>): Record<string, unknown>;
2
+
3
+ export { decorateMiniIconProps };
package/dist/icons.js ADDED
@@ -0,0 +1,135 @@
1
+ import { getBuiltinIcon } from '@antglobal/copilot-cards-core';
2
+
3
+ const DEFAULT_ICON_SIZE = 24;
4
+ const REPLACEMENT_CHARACTER = '\uFFFD';
5
+ function normalizeXmlText(value) {
6
+ let normalized = '';
7
+ for (let index = 0; index < value.length; index += 1) {
8
+ const codeUnit = value.charCodeAt(index);
9
+ if (codeUnit >= 0xD800 && codeUnit <= 0xDBFF) {
10
+ const trailing = value.charCodeAt(index + 1);
11
+ if (trailing >= 0xDC00 && trailing <= 0xDFFF) {
12
+ normalized += value[index] + value[index + 1];
13
+ index += 1;
14
+ }
15
+ else {
16
+ normalized += REPLACEMENT_CHARACTER;
17
+ }
18
+ continue;
19
+ }
20
+ if (codeUnit >= 0xDC00 && codeUnit <= 0xDFFF) {
21
+ normalized += REPLACEMENT_CHARACTER;
22
+ continue;
23
+ }
24
+ const isAllowedXmlCharacter = codeUnit === 0x09 ||
25
+ codeUnit === 0x0A ||
26
+ codeUnit === 0x0D ||
27
+ (codeUnit >= 0x20 && codeUnit <= 0xD7FF) ||
28
+ (codeUnit >= 0xE000 && codeUnit <= 0xFFFD);
29
+ normalized += isAllowedXmlCharacter
30
+ ? value[index]
31
+ : REPLACEMENT_CHARACTER;
32
+ }
33
+ return normalized;
34
+ }
35
+ function escapeXmlAttr(value) {
36
+ return normalizeXmlText(value)
37
+ .replace(/&/g, '&amp;')
38
+ .replace(/"/g, '&quot;')
39
+ .replace(/</g, '&lt;')
40
+ .replace(/>/g, '&gt;');
41
+ }
42
+ function resolveIconSize(value) {
43
+ const size = Number(value);
44
+ return Number.isFinite(size) && size > 0 ? size : DEFAULT_ICON_SIZE;
45
+ }
46
+ function normalizeStyleKey(key) {
47
+ if (key.startsWith('--')) {
48
+ return /^--[A-Za-z0-9_-]+$/.test(key) ? key : undefined;
49
+ }
50
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(key))
51
+ return undefined;
52
+ return key
53
+ .replace(/([A-Z])/g, '-$1')
54
+ .toLowerCase();
55
+ }
56
+ function normalizeStyleValue(value) {
57
+ if (typeof value === 'number') {
58
+ return Number.isFinite(value) ? String(value) : undefined;
59
+ }
60
+ if (typeof value !== 'string')
61
+ return undefined;
62
+ const normalized = normalizeXmlText(value);
63
+ if (/[;{}]/.test(normalized))
64
+ return undefined;
65
+ const compact = normalized.replace(/[\s\u0000-\u001f]/g, '').toLowerCase();
66
+ if (/expression\(/.test(compact) ||
67
+ /url\((?:['"]?)(?:javascript|vbscript|file|data:text\/html):/.test(compact)) {
68
+ return undefined;
69
+ }
70
+ return normalized;
71
+ }
72
+ function serializeIconStyle(value) {
73
+ if (!value || typeof value !== 'object' || Array.isArray(value))
74
+ return '';
75
+ const declarations = [];
76
+ for (const [rawKey, rawValue] of Object.entries(value)) {
77
+ const key = normalizeStyleKey(rawKey);
78
+ const styleValue = normalizeStyleValue(rawValue);
79
+ if (!key || styleValue === undefined)
80
+ continue;
81
+ declarations.push(`${key}: ${styleValue}`);
82
+ }
83
+ return declarations.join('; ');
84
+ }
85
+ function sanitizeExternalImageSrc(value) {
86
+ const src = String(value ?? '').trim();
87
+ if (!src)
88
+ return undefined;
89
+ const normalized = src.replace(/[\s\u0000-\u001f]/g, '').toLowerCase();
90
+ if (/^(javascript|vbscript|file):/.test(normalized))
91
+ return undefined;
92
+ if (/^data:/.test(normalized)) {
93
+ return /^data:image\/(?:png|jpeg|jpg|gif|webp);base64,/.test(normalized)
94
+ ? src
95
+ : undefined;
96
+ }
97
+ return src;
98
+ }
99
+ function decorateMiniIconProps(props) {
100
+ const size = resolveIconSize(props.size);
101
+ const name = props.name == null ? undefined : String(props.name);
102
+ const styleString = serializeIconStyle(props.style);
103
+ const decoratedProps = { ...props, _styleString: styleString };
104
+ if (props.src) {
105
+ const src = sanitizeExternalImageSrc(props.src);
106
+ if (src) {
107
+ return {
108
+ ...decoratedProps,
109
+ _iconKind: 'image',
110
+ _iconSrc: src,
111
+ _iconSize: size,
112
+ };
113
+ }
114
+ }
115
+ const icon = getBuiltinIcon(name);
116
+ if (icon) {
117
+ const color = escapeXmlAttr(String(props.color ?? 'currentColor'));
118
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" ` +
119
+ `viewBox="${icon.viewBox}" color="${color}">${icon.body}</svg>`;
120
+ return {
121
+ ...decoratedProps,
122
+ _iconKind: 'image',
123
+ _iconSrc: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`,
124
+ _iconSize: size,
125
+ };
126
+ }
127
+ return {
128
+ ...decoratedProps,
129
+ _iconKind: 'text',
130
+ _iconText: name ?? '?',
131
+ _iconSize: size,
132
+ };
133
+ }
134
+
135
+ export { decorateMiniIconProps };
@@ -0,0 +1,17 @@
1
+ export { MiniProgramAdapter, MiniProgramNavigateOptions, MiniProgramRequestOptions, MiniProgramResponse, MiniProgramSystemInfo, MiniProgramToastOptions } from './adapter/index.js';
2
+ export { AlipayAdapter } from './adapter/alipay.js';
3
+ export { WeChatAdapter } from './adapter/wechat.js';
4
+ export { MiniProgramActionContextOptions, createMiniProgramActionContext } from './action-context/index.js';
5
+ export { MiniProgramCardInstance, MiniProgramRenderNode, MiniProgramRendererOptions, renderForMiniProgram } from './renderer/index.js';
6
+ export { createCardRootComponent } from './components/card-root/index.js';
7
+ export { createCardNodeComponent } from './components/card-node/index.js';
8
+ export { createCardTextComponent } from './components/card-text/index.js';
9
+ export { createCardButtonComponent } from './components/card-button/index.js';
10
+ export { createCardImageComponent } from './components/card-image/index.js';
11
+ export { createCardIconComponent } from './components/card-icon/definition.js';
12
+ export { createCardInputComponent } from './components/card-input/index.js';
13
+ export { createCardDividerComponent } from './components/card-divider/index.js';
14
+ export { CardEventDetail, createEventDetail, nativeToSchemaEventMap, schemaToAlipayEventMap, schemaToWeChatEventMap } from './events/index.js';
15
+ export { DataPatch, MiniProgramStreamingCardInstance, MiniProgramStreamingOptions, createStreamingCardInstance } from './streaming/index.js';
16
+ export { ActionRunnerContext, ActionStep, BuiltinIconName, CardSchema, CardSchemaInput, ElementNode, ExpressionContext, ExpressionValue, RenderTreeNode, convertLegacySchema, createLifecycleManager, hasExpression, isLegacySchema, normalizeSchema, parseSchema, registerActionHandler, resolveDeep, resolveExpression, runActionStep, runActionSteps, validateSchema } from '@antglobal/copilot-cards-core';
17
+ export { transformNode, transformTree } from './renderer/transform.js';
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 { createCardIconComponent } from './components/card-icon/definition.js';
11
+ export { createCardInputComponent } from './components/card-input/index.js';
12
+ export { createCardDividerComponent } from './components/card-divider/index.js';
13
+ export { createEventDetail, nativeToSchemaEventMap, schemaToAlipayEventMap, schemaToWeChatEventMap } from './events/index.js';
14
+ export { createStreamingCardInstance } from './streaming/index.js';
15
+ export { convertLegacySchema, createLifecycleManager, hasExpression, isLegacySchema, normalizeSchema, parseSchema, registerActionHandler, resolveDeep, resolveExpression, runActionStep, runActionSteps, validateSchema } from '@antglobal/copilot-cards-core';
16
+ export { transformNode, transformTree } from './renderer/transform.js';
@@ -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,158 @@
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 };
@@ -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,91 @@
1
+ import { hasExpression, resolveExpression, resolveDeep, resolveExpressionValue } from '@antglobal/copilot-cards-core';
2
+ import { decorateMiniIconProps } from '../icons.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
+ // ─── Transform ───────────────────────────────────────────────────
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
+ function transformNode(node, variables) {
19
+ // 1. Check directives.visible
20
+ let visible = true;
21
+ if (node.directives?.visible) {
22
+ const visibleExpr = node.directives.visible;
23
+ const resolved = hasExpression(visibleExpr)
24
+ ? resolveExpression(visibleExpr, variables)
25
+ : visibleExpr;
26
+ if (resolved === false || resolved === 'false' || resolved === '' || resolved === 0) {
27
+ visible = false;
28
+ }
29
+ }
30
+ // If not visible, return early with minimal node (children won't be resolved)
31
+ if (!visible) {
32
+ return {
33
+ id: node.id,
34
+ type: node.type,
35
+ props: {},
36
+ children: [],
37
+ visible: false,
38
+ disabled: false,
39
+ events: [],
40
+ };
41
+ }
42
+ // 2. Resolve props with variable expressions
43
+ let resolvedProps = resolveDeep(node.props, variables);
44
+ // Resolve content if it's an ExpressionValue object
45
+ if (resolvedProps.content &&
46
+ typeof resolvedProps.content === 'object' &&
47
+ resolvedProps.content !== null &&
48
+ 'type' in resolvedProps.content) {
49
+ resolvedProps.content = resolveExpressionValue(resolvedProps.content, variables);
50
+ }
51
+ if (node.type === 'Icon') {
52
+ resolvedProps = decorateMiniIconProps(resolvedProps);
53
+ }
54
+ // 3. Check directives.disabled
55
+ let disabled = false;
56
+ if (node.directives?.disabled) {
57
+ const disabledExpr = node.directives.disabled;
58
+ const resolved = hasExpression(disabledExpr)
59
+ ? resolveExpression(disabledExpr, variables)
60
+ : disabledExpr;
61
+ disabled = resolved === true || resolved === 'true' || resolved === 1;
62
+ }
63
+ // 4. Collect event names
64
+ const events = [];
65
+ if (node.events && !disabled) {
66
+ for (const eventName of Object.keys(node.events)) {
67
+ if (node.events[eventName]) {
68
+ events.push(eventName);
69
+ }
70
+ }
71
+ }
72
+ // 5. Recursively transform children
73
+ const children = node.children.map((child) => transformNode(child, variables));
74
+ return {
75
+ id: node.id,
76
+ type: node.type,
77
+ props: resolvedProps,
78
+ children,
79
+ visible,
80
+ disabled,
81
+ events,
82
+ };
83
+ }
84
+ /**
85
+ * Transform an entire render tree into a mini-program data tree.
86
+ */
87
+ function transformTree(tree, variables) {
88
+ return transformNode(tree, variables);
89
+ }
90
+
91
+ export { transformNode, transformTree };