@antglobal/copilot-cards-web 1.0.4 → 1.0.5
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 +9 -2
- package/dist/index.d.ts +10 -5
- package/dist/index.js +38 -49
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,14 +78,19 @@ Common render options include:
|
|
|
78
78
|
| Option | Purpose |
|
|
79
79
|
| --- | --- |
|
|
80
80
|
| `variables` | Overrides initial schema variables |
|
|
81
|
-
| `botId` |
|
|
82
|
-
| `isMobile` |
|
|
81
|
+
| `botId` | Optionally selects bot-scoped custom action handlers |
|
|
82
|
+
| `isMobile` | Explicitly enables mobile component layout; defaults to `false` |
|
|
83
|
+
| `responsive` | Explicitly enables mobile px-to-rem conversion when configured |
|
|
83
84
|
| `fetch` | Supplies a custom request implementation |
|
|
84
85
|
| `showToast` | Connects toast actions to the host UI |
|
|
85
86
|
| `navigate` | Connects URL actions to host navigation |
|
|
86
87
|
| `emit` | Receives events emitted by a card |
|
|
87
88
|
| `copyText` | Connects copy actions to the host clipboard |
|
|
88
89
|
|
|
90
|
+
The SDK does not infer mobile mode or CSS units from viewport width. Pass only
|
|
91
|
+
`isMobile: true` for mobile layout with px output. To opt into REM conversion,
|
|
92
|
+
also pass `responsive: { mobile: { unit: "rem", rootValue: 100 } }`.
|
|
93
|
+
|
|
89
94
|
### Streaming
|
|
90
95
|
|
|
91
96
|
Use `renderStreamingCard` when the card arrives incrementally from an AI model or server:
|
|
@@ -121,6 +126,8 @@ const bot = new BotSDK({
|
|
|
121
126
|
await bot.renderCard(container, schema);
|
|
122
127
|
```
|
|
123
128
|
|
|
129
|
+
`botId` is optional. Omit it when the application uses only one default action scope; provide it when isolating custom handlers or loading per-bot action configuration.
|
|
130
|
+
|
|
124
131
|
## Built-in components
|
|
125
132
|
|
|
126
133
|
The renderer includes:
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ interface ResponsiveContext {
|
|
|
42
42
|
resolveLength(value: string | number): string;
|
|
43
43
|
convertCSS(value: string): string;
|
|
44
44
|
}
|
|
45
|
+
/** Reusable REM preset; applied only when passed explicitly as `responsive.mobile`. */
|
|
45
46
|
declare const DEFAULT_MOBILE_RESPONSIVE: ResponsiveMobileOptions;
|
|
46
47
|
/** Convert CSS px lengths while preserving strings, comments, and URLs. */
|
|
47
48
|
declare function convertPixelTokens(value: string, rootValue: number): string;
|
|
@@ -62,9 +63,9 @@ declare function createResponsiveContext(isMobile: boolean, responsive?: Respons
|
|
|
62
63
|
*/
|
|
63
64
|
|
|
64
65
|
interface RenderCardOptions extends WebActionContextOptions {
|
|
65
|
-
/**
|
|
66
|
+
/** Enable mobile component layout explicitly (default false). */
|
|
66
67
|
isMobile?: boolean;
|
|
67
|
-
/**
|
|
68
|
+
/** Optional mobile sizing conversion; omitted values preserve px output. */
|
|
68
69
|
responsive?: ResponsiveOptions;
|
|
69
70
|
/** External variables to merge into schema.variables (overrides schema defaults) */
|
|
70
71
|
variables?: Record<string, any>;
|
|
@@ -282,8 +283,8 @@ declare function connectSSE(instance: StreamingCardInstance, options: SSEConnect
|
|
|
282
283
|
*/
|
|
283
284
|
|
|
284
285
|
interface BotSDKOptions {
|
|
285
|
-
/**
|
|
286
|
-
botId
|
|
286
|
+
/** Optional bot ID used to scope custom actions and action configuration */
|
|
287
|
+
botId?: string;
|
|
287
288
|
/** Base URL for business API requests (e.g. 'https://api.example.com') */
|
|
288
289
|
baseUrl?: string;
|
|
289
290
|
/**
|
|
@@ -433,7 +434,7 @@ declare function buildStyleString(styles: Record<string, string | number | undef
|
|
|
433
434
|
* common style helpers. Designed to be used by `renderCard` which
|
|
434
435
|
* passes resolved props via `setData()`.
|
|
435
436
|
*
|
|
436
|
-
* Note: lifecycle management, event binding, and
|
|
437
|
+
* Note: lifecycle management, event binding, and mobile-mode selection
|
|
437
438
|
* are handled externally by `renderCard` / the render pipeline.
|
|
438
439
|
* BaseElement keeps itself lightweight and focused on DOM rendering.
|
|
439
440
|
*/
|
|
@@ -620,6 +621,8 @@ declare class CardButton extends BaseElement {
|
|
|
620
621
|
declare class CardInput extends BaseElement {
|
|
621
622
|
static readonly is = "ai-card-input";
|
|
622
623
|
protected render(): void;
|
|
624
|
+
private getAutoFocusControl;
|
|
625
|
+
private isFirstAvailableAutoFocusInput;
|
|
623
626
|
/** Escape HTML entities for safe insertion. */
|
|
624
627
|
private escapeHtml;
|
|
625
628
|
/** Escape attribute values for safe insertion. */
|
|
@@ -1017,6 +1020,8 @@ type InputAffix = string | InputAffixConfig;
|
|
|
1017
1020
|
interface InputProps {
|
|
1018
1021
|
/** Placeholder text */
|
|
1019
1022
|
placeholder?: string;
|
|
1023
|
+
/** Focus the native input after the component is mounted (default false) */
|
|
1024
|
+
autoFocus?: boolean;
|
|
1020
1025
|
/** HTML input type: text / textarea / password / number etc. */
|
|
1021
1026
|
inputType?: 'text' | 'textarea' | 'password' | (string & {});
|
|
1022
1027
|
/** Label text displayed above the input */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getBuiltinIcon, cloneJsonData, createLifecycleManager, materializeCard,
|
|
1
|
+
import { getBuiltinIcon, cloneJsonData, createLifecycleManager, materializeCard, createA2UIParameterResolver, createExpressionContext, resolveActionRef, replaceRootContents, resolveA2UIDeep, hasExpression, resolveExpression, resolveDeep, resolveExpressionValue, isBoundRenderTreeNode, runActionSteps, normalizeSchema, validateSchema, requiresBindingMaterialization, parseSchema, StreamingParser, StreamingEngine, findAffectedRepeatOwners, bindingTopologyFingerprint, findTemplateRepeatOwners, extractPartialSchema, runActionStep, registry } from '@antglobal/copilot-cards-core';
|
|
2
2
|
export { ActionRegistry, a2uiComponentToElement, a2uiToCommand, convertLegacySchema, createLifecycleManager, hasExpression, isA2UIEnvelope, isLegacySchema, normalizeSchema, parseSchema, registerActionHandler, registry, resolveActionRef, resolveDeep, resolveExpression, resolveExpressionValue, runActionStep, runActionSteps, validateSchema } from '@antglobal/copilot-cards-core';
|
|
3
3
|
import * as echarts from 'echarts/core';
|
|
4
4
|
import { LineChart, BarChart, PieChart, ScatterChart, FunnelChart, HeatmapChart } from 'echarts/charts';
|
|
@@ -96,6 +96,7 @@ function buildStyleString(styles) {
|
|
|
96
96
|
.join(';');
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/** Reusable REM preset; applied only when passed explicitly as `responsive.mobile`. */
|
|
99
100
|
const DEFAULT_MOBILE_RESPONSIVE = Object.freeze({
|
|
100
101
|
unit: 'rem',
|
|
101
102
|
rootValue: 100,
|
|
@@ -236,10 +237,10 @@ function convertPixelTokens(value, rootValue) {
|
|
|
236
237
|
return result;
|
|
237
238
|
}
|
|
238
239
|
function createResponsiveContext(isMobile, responsive) {
|
|
239
|
-
const mobile = responsive?.mobile
|
|
240
|
-
const unit = mobile
|
|
241
|
-
const rootValue = mobile.rootValue;
|
|
242
|
-
if (!Number.isFinite(rootValue) || rootValue <= 0) {
|
|
240
|
+
const mobile = responsive?.mobile;
|
|
241
|
+
const unit = mobile?.unit;
|
|
242
|
+
const rootValue = mobile?.rootValue ?? DEFAULT_MOBILE_RESPONSIVE.rootValue;
|
|
243
|
+
if (mobile && (!Number.isFinite(rootValue) || rootValue <= 0)) {
|
|
243
244
|
throw new Error('[renderCard] responsive.mobile.rootValue must be a positive number');
|
|
244
245
|
}
|
|
245
246
|
const active = isMobile && unit === 'rem';
|
|
@@ -1647,7 +1648,7 @@ function renderTableSlot(container, slotContent) {
|
|
|
1647
1648
|
* common style helpers. Designed to be used by `renderCard` which
|
|
1648
1649
|
* passes resolved props via `setData()`.
|
|
1649
1650
|
*
|
|
1650
|
-
* Note: lifecycle management, event binding, and
|
|
1651
|
+
* Note: lifecycle management, event binding, and mobile-mode selection
|
|
1651
1652
|
* are handled externally by `renderCard` / the render pipeline.
|
|
1652
1653
|
* BaseElement keeps itself lightweight and focused on DOM rendering.
|
|
1653
1654
|
*/
|
|
@@ -2681,7 +2682,7 @@ class CardInput extends BaseElement {
|
|
|
2681
2682
|
render() {
|
|
2682
2683
|
if (!this.shadowRoot || !this._node)
|
|
2683
2684
|
return;
|
|
2684
|
-
const { placeholder = '', inputType = 'text', label, defaultValue = '', disabled = false, readonly: readOnly = false, maxLength, rows, min, max, step = 1, controls = true, prefix, suffix, style, inputStyle, isExpressionResultStyle, } = this._props;
|
|
2685
|
+
const { placeholder = '', autoFocus = false, inputType = 'text', label, defaultValue = '', disabled = false, readonly: readOnly = false, maxLength, rows, min, max, step = 1, controls = true, prefix, suffix, style, inputStyle, isExpressionResultStyle, } = this._props;
|
|
2685
2686
|
const inlineStyle = this.buildInlineStyle(style, isExpressionResultStyle);
|
|
2686
2687
|
const legacyInputStyle = style && typeof style === 'object' && style.resize != null
|
|
2687
2688
|
? { resize: style.resize }
|
|
@@ -2967,6 +2968,14 @@ class CardInput extends BaseElement {
|
|
|
2967
2968
|
detail: { value: inputEl.value },
|
|
2968
2969
|
}));
|
|
2969
2970
|
});
|
|
2971
|
+
if (autoFocus && !disabled) {
|
|
2972
|
+
queueMicrotask(() => {
|
|
2973
|
+
const control = this.getAutoFocusControl();
|
|
2974
|
+
if (control && this.isFirstAvailableAutoFocusInput()) {
|
|
2975
|
+
control.focus();
|
|
2976
|
+
}
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2970
2979
|
if (isNumber &&
|
|
2971
2980
|
showNumberStepper &&
|
|
2972
2981
|
inputEl instanceof HTMLInputElement) {
|
|
@@ -2995,6 +3004,22 @@ class CardInput extends BaseElement {
|
|
|
2995
3004
|
}
|
|
2996
3005
|
}
|
|
2997
3006
|
// ─── Helpers ──────────────────────────────────────────────────
|
|
3007
|
+
getAutoFocusControl() {
|
|
3008
|
+
if (!this.isConnected
|
|
3009
|
+
|| !this._props.autoFocus
|
|
3010
|
+
|| this.hasAttribute('data-disabled')) {
|
|
3011
|
+
return null;
|
|
3012
|
+
}
|
|
3013
|
+
const control = this.shadowRoot?.querySelector('.card-input');
|
|
3014
|
+
return control && !control.disabled ? control : null;
|
|
3015
|
+
}
|
|
3016
|
+
isFirstAvailableAutoFocusInput() {
|
|
3017
|
+
const root = this.getRootNode();
|
|
3018
|
+
if (!('querySelectorAll' in root))
|
|
3019
|
+
return false;
|
|
3020
|
+
const firstAvailable = Array.from(root.querySelectorAll(CardInput.is)).find(input => input.getAutoFocusControl() !== null);
|
|
3021
|
+
return firstAvailable === this;
|
|
3022
|
+
}
|
|
2998
3023
|
/** Escape HTML entities for safe insertion. */
|
|
2999
3024
|
escapeHtml(str) {
|
|
3000
3025
|
return str
|
|
@@ -4716,7 +4741,7 @@ class CardForm extends BaseElement {
|
|
|
4716
4741
|
this._bindEvents(fields, disabled);
|
|
4717
4742
|
}
|
|
4718
4743
|
_renderField(field, index) {
|
|
4719
|
-
const { name, label, type = 'text', placeholder = '', required = false, rules = [], prefix, suffix,
|
|
4744
|
+
const { name, label, type = 'text', placeholder = '', required = false, rules = [], prefix, suffix, length = 6, } = field;
|
|
4720
4745
|
const val = this._values[name] ?? '';
|
|
4721
4746
|
const error = this._errors[name] || '';
|
|
4722
4747
|
const errorClass = error ? 'error' : '';
|
|
@@ -7713,18 +7738,9 @@ function renderBoundCard(container, schema, options) {
|
|
|
7713
7738
|
let currentMaterialized;
|
|
7714
7739
|
let revision = 0;
|
|
7715
7740
|
let disposed = false;
|
|
7716
|
-
|
|
7741
|
+
const isMobile = options.isMobile === true;
|
|
7717
7742
|
let actionQueue = Promise.resolve();
|
|
7718
7743
|
let lifecycleQueue = Promise.resolve();
|
|
7719
|
-
const removeViewportListener = options.isMobile == null
|
|
7720
|
-
? onViewportChange((mobile) => {
|
|
7721
|
-
if (disposed)
|
|
7722
|
-
return;
|
|
7723
|
-
isMobile = mobile;
|
|
7724
|
-
const candidate = prepareCandidate(variables);
|
|
7725
|
-
publishDOM(candidate, false);
|
|
7726
|
-
})
|
|
7727
|
-
: () => { };
|
|
7728
7744
|
function expressionContextFor(node) {
|
|
7729
7745
|
return isBoundRenderTreeNode(node)
|
|
7730
7746
|
? createExpressionContext(node.scope)
|
|
@@ -8041,7 +8057,6 @@ function renderBoundCard(container, schema, options) {
|
|
|
8041
8057
|
return;
|
|
8042
8058
|
disposed = true;
|
|
8043
8059
|
abortController.abort();
|
|
8044
|
-
removeViewportListener();
|
|
8045
8060
|
disposeChartsIn(container);
|
|
8046
8061
|
container.replaceChildren();
|
|
8047
8062
|
const lifecycleNodes = [...activeLifecycleNodes.entries()];
|
|
@@ -8192,13 +8207,7 @@ function renderStaticCard(container, schema, options) {
|
|
|
8192
8207
|
}
|
|
8193
8208
|
let actionContext = buildActionContext();
|
|
8194
8209
|
// 5. Responsive
|
|
8195
|
-
|
|
8196
|
-
const removeViewportListener = options.isMobile == null
|
|
8197
|
-
? onViewportChange((mobile) => {
|
|
8198
|
-
isMobile = mobile;
|
|
8199
|
-
rerender();
|
|
8200
|
-
})
|
|
8201
|
-
: () => { };
|
|
8210
|
+
const isMobile = options.isMobile === true;
|
|
8202
8211
|
// 6. Schema-level action definitions (for string references in events)
|
|
8203
8212
|
const schemaActions = schema.actions ?? {};
|
|
8204
8213
|
// 7. Render function
|
|
@@ -8222,7 +8231,6 @@ function renderStaticCard(container, schema, options) {
|
|
|
8222
8231
|
return {
|
|
8223
8232
|
dispose() {
|
|
8224
8233
|
abortController.abort();
|
|
8225
|
-
removeViewportListener();
|
|
8226
8234
|
lifecycleManager.dispose(actionContext);
|
|
8227
8235
|
disposeChartsIn(container);
|
|
8228
8236
|
container.innerHTML = '';
|
|
@@ -8495,7 +8503,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8495
8503
|
let partialVariablesSent = false;
|
|
8496
8504
|
let partialFinalized = false;
|
|
8497
8505
|
let variables = { ...options.variables };
|
|
8498
|
-
|
|
8506
|
+
const isMobile = options.isMobile === true;
|
|
8499
8507
|
let currentSchema = null;
|
|
8500
8508
|
let currentSurfaceId = null;
|
|
8501
8509
|
let currentMaterialized = null;
|
|
@@ -8507,16 +8515,6 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8507
8515
|
const activeBoundLifecycles = new Map();
|
|
8508
8516
|
const mountedBoundLifecycles = new Map();
|
|
8509
8517
|
const boundLifecycleGenerations = new Map();
|
|
8510
|
-
// Responsive viewport detection
|
|
8511
|
-
const removeViewportListener = options.isMobile == null
|
|
8512
|
-
? onViewportChange((mobile) => {
|
|
8513
|
-
isMobile = mobile;
|
|
8514
|
-
// Re-render all elements with new mobile state if schema exists
|
|
8515
|
-
if (currentSchema) {
|
|
8516
|
-
rerenderAll();
|
|
8517
|
-
}
|
|
8518
|
-
})
|
|
8519
|
-
: () => { };
|
|
8520
8518
|
// ─── Action Context ─────────────────────────────────────────────
|
|
8521
8519
|
function buildActionContext() {
|
|
8522
8520
|
return {
|
|
@@ -9888,14 +9886,6 @@ function renderStreamingCard(container, options = {}) {
|
|
|
9888
9886
|
console.warn('[renderStreamingCard] Deferred render (incomplete schema):', error);
|
|
9889
9887
|
}
|
|
9890
9888
|
}
|
|
9891
|
-
/**
|
|
9892
|
-
* Re-render all elements (viewport changes — isMobile affects every renderer's
|
|
9893
|
-
* output, so the incremental diff cache cannot be reused here).
|
|
9894
|
-
*/
|
|
9895
|
-
function rerenderAll() {
|
|
9896
|
-
actionContext = buildActionContext();
|
|
9897
|
-
safeRenderFull();
|
|
9898
|
-
}
|
|
9899
9889
|
// ─── Engine Event Handlers ──────────────────────────────────────
|
|
9900
9890
|
const engine = new StreamingEngine({
|
|
9901
9891
|
onSurfaceCreated(surfaceId, schemaInput) {
|
|
@@ -10240,7 +10230,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
10240
10230
|
}
|
|
10241
10231
|
}
|
|
10242
10232
|
},
|
|
10243
|
-
onSurfaceDeleted(
|
|
10233
|
+
onSurfaceDeleted(_surfaceId) {
|
|
10244
10234
|
disposeChartsIn(container); // release old ECharts instances before clearing
|
|
10245
10235
|
container.innerHTML = '';
|
|
10246
10236
|
elementMap.clear();
|
|
@@ -10335,7 +10325,6 @@ function renderStreamingCard(container, options = {}) {
|
|
|
10335
10325
|
disposed = true;
|
|
10336
10326
|
boundRevision += 1;
|
|
10337
10327
|
abortController.abort();
|
|
10338
|
-
removeViewportListener();
|
|
10339
10328
|
teardownBoundLifecycles();
|
|
10340
10329
|
lifecycleManager.dispose(actionContext);
|
|
10341
10330
|
disposeChartsIn(container); // release ECharts instances before clearing
|
|
@@ -10504,7 +10493,7 @@ class BotSDK {
|
|
|
10504
10493
|
this._instances = [];
|
|
10505
10494
|
/** Declarative action chains loaded from actionProvider */
|
|
10506
10495
|
this._actionChains = new Map();
|
|
10507
|
-
this.botId = options.botId;
|
|
10496
|
+
this.botId = options.botId ?? '';
|
|
10508
10497
|
this.baseUrl = options.baseUrl ?? '';
|
|
10509
10498
|
// Source B: Batch-register action handler functions (sync, immediate)
|
|
10510
10499
|
if (options.onAction) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antglobal/copilot-cards-web",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Web Component renderer for copilot bot card SDK — PC + Mobile + WebView unified rendering via Custom Elements",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@antglobal/copilot-cards-core": "^1.0.
|
|
31
|
+
"@antglobal/copilot-cards-core": "^1.0.5",
|
|
32
32
|
"echarts": "^5.6.0",
|
|
33
33
|
"marked": "^18.0.5",
|
|
34
34
|
"tslib": "^2.8.1",
|