@antglobal/copilot-cards-web 0.0.20260806095059-dev.1 → 0.0.20260811080429-dev.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/dist/index.d.ts CHANGED
@@ -1295,7 +1295,7 @@ interface ChoiceListProps {
1295
1295
  indicatorStyle?: Record<string, any>;
1296
1296
  /** Style merged into selected indicators */
1297
1297
  selectedIndicatorStyle?: Record<string, any>;
1298
- /** Checked mark used by multiple mode; defaults to built-in `check_bold` */
1298
+ /** Selected mark override; single mode keeps its radio dot when omitted, while multiple mode defaults to `check_bold` */
1299
1299
  checkedIcon?: ChoiceIndicatorIcon;
1300
1300
  }
1301
1301
  interface ChoiceIndicatorIcon {
@@ -1323,7 +1323,7 @@ interface ChoiceItemProps {
1323
1323
  indicatorStyle?: Record<string, any>;
1324
1324
  /** Per-item selected indicator style */
1325
1325
  selectedIndicatorStyle?: Record<string, any>;
1326
- /** Per-item checked mark override */
1326
+ /** Per-item selected mark override; also replaces the single-select radio dot */
1327
1327
  checkedIcon?: ChoiceIndicatorIcon;
1328
1328
  }
1329
1329
  interface PasscodeInputProps {
package/dist/index.js CHANGED
@@ -706,6 +706,7 @@ function buildHeatmapOption(config) {
706
706
  */
707
707
  // ─── Slot Layout Constants ──────────────────────────────────────
708
708
  const SLOT_LAYOUT = {
709
+ FLEX: 'flex',
709
710
  COLUMNS: 'columns',
710
711
  GRID: 'grid',
711
712
  HORIZONTAL_SCROLL: 'horizontalScroll',
@@ -716,6 +717,30 @@ const SLOT_LAYOUT = {
716
717
  TABLE: 'table',
717
718
  CHART: 'chart',
718
719
  };
720
+ // ─── Dispatcher ─────────────────────────────────────────────────
721
+ /**
722
+ * Apply host-level layout styles based on the slot key.
723
+ * Called from renderDefault BEFORE props.style so explicit styles can override.
724
+ * Layouts that require wrapper DOM continue to render in renderSlotLayout.
725
+ */
726
+ function applySlotBaseStyle(container, props) {
727
+ const flex = props.slots?.[SLOT_LAYOUT.FLEX];
728
+ if (!flex || typeof flex !== 'object')
729
+ return;
730
+ const config = flex.config && typeof flex.config === 'object' ? flex.config : {};
731
+ container.style.display = 'flex';
732
+ container.style.flexDirection = config.direction ?? 'column';
733
+ container.style.alignItems = config.align ?? 'flex-start';
734
+ container.style.justifyContent = config.justify ?? 'flex-start';
735
+ container.style.flexWrap = config.wrap ?? 'nowrap';
736
+ container.style.gap = resolveFlexGap(config.gap);
737
+ }
738
+ function resolveFlexGap(value) {
739
+ if (typeof value === 'number' && Number.isFinite(value)) {
740
+ return `${value}px`;
741
+ }
742
+ return typeof value === 'string' && value.length > 0 ? value : '0px';
743
+ }
719
744
  /**
720
745
  * Render children into a container using the slot layout strategy.
721
746
  * Returns true if a special layout was applied (children already appended).
@@ -2336,6 +2361,30 @@ function sanitizeIconHtml(icon) {
2336
2361
  walk(tpl.content);
2337
2362
  return tpl.innerHTML;
2338
2363
  }
2364
+ function escapeIconAttribute(value) {
2365
+ return value
2366
+ .replace(/&/g, '&amp;')
2367
+ .replace(/"/g, '&quot;')
2368
+ .replace(/</g, '&lt;')
2369
+ .replace(/>/g, '&gt;');
2370
+ }
2371
+ function isDirectImageUrl(icon) {
2372
+ const normalized = icon.replace(/[\s\x00-\x1f]/g, '').toLowerCase();
2373
+ return /^(?:https?:\/\/|\/\/|data:image\/)/.test(normalized)
2374
+ || /^(?:\.{0,2}\/).+\.(?:avif|gif|jpe?g|png|svg|webp)(?:[?#].*)?$/i.test(icon);
2375
+ }
2376
+ /**
2377
+ * Button.icon historically accepts sanitized HTML or text. A direct image URL
2378
+ * is a common shorthand, so convert only recognizable image URLs to markup and
2379
+ * leave inline SVG, <img> HTML, emoji, and other text on the existing path.
2380
+ */
2381
+ function renderButtonIcon(icon) {
2382
+ const value = String(icon ?? '').trim();
2383
+ if (isDirectImageUrl(value)) {
2384
+ return `<img class="card-btn-url-icon" src="${escapeIconAttribute(value)}" alt="" aria-hidden="true" />`;
2385
+ }
2386
+ return sanitizeIconHtml(value);
2387
+ }
2339
2388
  class CardButton extends BaseElement {
2340
2389
  render() {
2341
2390
  if (!this.shadowRoot || !this._node)
@@ -2490,12 +2539,18 @@ class CardButton extends BaseElement {
2490
2539
  width: 1em;
2491
2540
  height: 1em;
2492
2541
  }
2542
+ .card-btn-url-icon {
2543
+ display: block;
2544
+ width: 100%;
2545
+ height: 100%;
2546
+ object-fit: contain;
2547
+ }
2493
2548
  </style>
2494
2549
  <button
2495
2550
  class="card-btn ${variant} ${isMobile ? 'card-mobile' : 'card-desktop'}"
2496
2551
  ${disabled ? 'disabled' : ''}
2497
2552
  style="${inlineStyle}"
2498
- ><span class="card-btn-content">${icon ? `<span class="card-btn-icon">${sanitizeIconHtml(String(icon))}</span>` : ''}${displayText}</span></button>
2553
+ ><span class="card-btn-content">${icon ? `<span class="card-btn-icon">${renderButtonIcon(icon)}</span>` : ''}${displayText}</span></button>
2499
2554
  `);
2500
2555
  }
2501
2556
  }
@@ -7013,11 +7068,12 @@ class CardChoiceItem extends BaseElement {
7013
7068
  ...(selected ? selectedIndicatorStyle || {} : {}),
7014
7069
  }, isExpressionResultStyle);
7015
7070
  const indicatorRole = selectionMode === 'single' ? 'radio' : 'checkbox';
7016
- const resolvedCheckedIcon = checkedIcon ?? listCheckedIcon ?? {
7071
+ const configuredCheckedIcon = checkedIcon ?? listCheckedIcon;
7072
+ const resolvedCheckedIcon = configuredCheckedIcon ?? {
7017
7073
  name: 'check_bold',
7018
7074
  size: 12,
7019
7075
  };
7020
- const checkedMark = selectionMode === 'multiple'
7076
+ const checkedMark = selectionMode === 'multiple' || configuredCheckedIcon
7021
7077
  ? `<span class="choice-icon" aria-hidden="true">${renderIconContent(resolvedCheckedIcon)}</span>`
7022
7078
  : '<span class="radio-dot" aria-hidden="true"></span>';
7023
7079
  this.setAttribute('data-selected', String(selected));
@@ -7656,6 +7712,8 @@ function renderDefault(node, props, isMobile, responsive) {
7656
7712
  div.className = `card-element card-${node.type.toLowerCase()} ${isMobile ? 'card-mobile' : 'card-desktop'}`;
7657
7713
  div.setAttribute('data-card-id', node.id);
7658
7714
  div.setAttribute('data-card-type', node.type);
7715
+ // Apply slot base styles FIRST (so props.style can override)
7716
+ applySlotBaseStyle(div, props);
7659
7717
  if (props.style && typeof props.style === 'object') {
7660
7718
  const resolvedStyle = buildStyleString(props.isExpressionResultStyle
7661
7719
  ? props.style
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antglobal/copilot-cards-web",
3
- "version": "0.0.20260806095059-dev.1",
3
+ "version": "0.0.20260811080429-dev.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": "0.0.20260806095059-dev.1",
31
+ "@antglobal/copilot-cards-core": "0.0.20260811080429-dev.5",
32
32
  "echarts": "^5.6.0",
33
33
  "marked": "^18.0.5",
34
34
  "tslib": "^2.8.1",