@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 CHANGED
@@ -78,14 +78,19 @@ Common render options include:
78
78
  | Option | Purpose |
79
79
  | --- | --- |
80
80
  | `variables` | Overrides initial schema variables |
81
- | `botId` | Selects bot-scoped custom action handlers |
82
- | `isMobile` | Overrides automatic viewport detection |
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
- /** Force mobile mode; auto-detected from viewport if omitted */
66
+ /** Enable mobile component layout explicitly (default false). */
66
67
  isMobile?: boolean;
67
- /** Mobile sizing conversion. `isMobile: true` defaults to rem/rootValue 100. */
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
- /** Bot ID identifies which bot this instance is for */
286
- botId: string;
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 viewport detection
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, resolveActionRef, createA2UIParameterResolver, createExpressionContext, resolveA2UIDeep, hasExpression, resolveExpression, resolveDeep, resolveExpressionValue, replaceRootContents, isBoundRenderTreeNode, runActionSteps, normalizeSchema, validateSchema, requiresBindingMaterialization, parseSchema, StreamingParser, StreamingEngine, findAffectedRepeatOwners, bindingTopologyFingerprint, findTemplateRepeatOwners, extractPartialSchema, runActionStep, registry } from '@antglobal/copilot-cards-core';
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 ?? DEFAULT_MOBILE_RESPONSIVE;
240
- const unit = mobile.unit;
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 viewport detection
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, options = [], length = 6, } = field;
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
- let isMobile = options.isMobile ?? isMobileViewport();
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
- let isMobile = options.isMobile ?? isMobileViewport();
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
- let isMobile = options.isMobile ?? isMobileViewport();
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(surfaceId) {
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.4",
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.4",
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",