@antglobal/copilot-cards-web 1.0.3 → 1.0.4
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 +36 -32
- package/dist/index.js +405 -142
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -96,6 +96,206 @@ function buildStyleString(styles) {
|
|
|
96
96
|
.join(';');
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
const DEFAULT_MOBILE_RESPONSIVE = Object.freeze({
|
|
100
|
+
unit: 'rem',
|
|
101
|
+
rootValue: 100,
|
|
102
|
+
});
|
|
103
|
+
function formatRem(value) {
|
|
104
|
+
return Number(value.toFixed(6)).toString();
|
|
105
|
+
}
|
|
106
|
+
function isIdentifierChar(value) {
|
|
107
|
+
if (value == null)
|
|
108
|
+
return false;
|
|
109
|
+
const codePoint = value.codePointAt(0) ?? 0;
|
|
110
|
+
return /[a-zA-Z0-9_-]/.test(value) || value === '\\' || codePoint >= 0x80;
|
|
111
|
+
}
|
|
112
|
+
function quotedEnd(value, start) {
|
|
113
|
+
const quote = value[start];
|
|
114
|
+
let cursor = start + 1;
|
|
115
|
+
while (cursor < value.length) {
|
|
116
|
+
if (value[cursor] === '\\') {
|
|
117
|
+
cursor += 2;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (value[cursor] === quote)
|
|
121
|
+
return cursor + 1;
|
|
122
|
+
cursor++;
|
|
123
|
+
}
|
|
124
|
+
return value.length;
|
|
125
|
+
}
|
|
126
|
+
function identifierEscape(value, start) {
|
|
127
|
+
if (value[start] !== '\\' || start + 1 >= value.length)
|
|
128
|
+
return undefined;
|
|
129
|
+
let cursor = start + 1;
|
|
130
|
+
if (/[\n\r\f]/.test(value[cursor]))
|
|
131
|
+
return undefined;
|
|
132
|
+
let hex = '';
|
|
133
|
+
while (cursor < value.length && hex.length < 6 && /[0-9a-f]/i.test(value[cursor])) {
|
|
134
|
+
hex += value[cursor];
|
|
135
|
+
cursor++;
|
|
136
|
+
}
|
|
137
|
+
if (hex) {
|
|
138
|
+
const codePoint = Number.parseInt(hex, 16);
|
|
139
|
+
if (/\s/.test(value[cursor] ?? ''))
|
|
140
|
+
cursor++;
|
|
141
|
+
return {
|
|
142
|
+
decoded: codePoint === 0 || codePoint > 0x10ffff
|
|
143
|
+
? '\uFFFD'
|
|
144
|
+
: String.fromCodePoint(codePoint),
|
|
145
|
+
end: cursor,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return { decoded: value[cursor], end: cursor + 1 };
|
|
149
|
+
}
|
|
150
|
+
function urlEnd(value, start) {
|
|
151
|
+
if (value[start]?.toLowerCase() !== 'u' && value[start] !== '\\') {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
if (isIdentifierChar(value[start - 1]))
|
|
155
|
+
return undefined;
|
|
156
|
+
let cursor = start;
|
|
157
|
+
let name = '';
|
|
158
|
+
while (cursor < value.length) {
|
|
159
|
+
const char = value[cursor];
|
|
160
|
+
if (/[a-zA-Z0-9_-]/.test(char) || (char.codePointAt(0) ?? 0) >= 0x80) {
|
|
161
|
+
name += char;
|
|
162
|
+
cursor++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const escaped = identifierEscape(value, cursor);
|
|
166
|
+
if (!escaped)
|
|
167
|
+
break;
|
|
168
|
+
name += escaped.decoded;
|
|
169
|
+
cursor = escaped.end;
|
|
170
|
+
}
|
|
171
|
+
if (name.toLowerCase() !== 'url')
|
|
172
|
+
return undefined;
|
|
173
|
+
while (/\s/.test(value[cursor] ?? ''))
|
|
174
|
+
cursor++;
|
|
175
|
+
if (value[cursor] !== '(')
|
|
176
|
+
return undefined;
|
|
177
|
+
cursor++;
|
|
178
|
+
let depth = 1;
|
|
179
|
+
while (cursor < value.length && depth > 0) {
|
|
180
|
+
const char = value[cursor];
|
|
181
|
+
if (char === '"' || char === "'") {
|
|
182
|
+
cursor = quotedEnd(value, cursor);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (char === '\\') {
|
|
186
|
+
cursor += 2;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (char === '(')
|
|
190
|
+
depth++;
|
|
191
|
+
if (char === ')')
|
|
192
|
+
depth--;
|
|
193
|
+
cursor++;
|
|
194
|
+
}
|
|
195
|
+
return cursor;
|
|
196
|
+
}
|
|
197
|
+
/** Convert CSS px lengths while preserving strings, comments, and URLs. */
|
|
198
|
+
function convertPixelTokens(value, rootValue) {
|
|
199
|
+
let result = '';
|
|
200
|
+
let cursor = 0;
|
|
201
|
+
while (cursor < value.length) {
|
|
202
|
+
const char = value[cursor];
|
|
203
|
+
if (char === '"' || char === "'") {
|
|
204
|
+
const end = quotedEnd(value, cursor);
|
|
205
|
+
result += value.slice(cursor, end);
|
|
206
|
+
cursor = end;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (value.startsWith('/*', cursor)) {
|
|
210
|
+
const closing = value.indexOf('*/', cursor + 2);
|
|
211
|
+
const end = closing < 0 ? value.length : closing + 2;
|
|
212
|
+
result += value.slice(cursor, end);
|
|
213
|
+
cursor = end;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const protectedUrlEnd = urlEnd(value, cursor);
|
|
217
|
+
if (protectedUrlEnd !== undefined) {
|
|
218
|
+
result += value.slice(cursor, protectedUrlEnd);
|
|
219
|
+
cursor = protectedUrlEnd;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const canStartPixel = /[\d.]/.test(char)
|
|
223
|
+
|| (char === '-' && /[\d.]/.test(value[cursor + 1] ?? ''));
|
|
224
|
+
const pixel = canStartPixel && !isIdentifierChar(value[cursor - 1])
|
|
225
|
+
? /^(-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)px/i.exec(value.slice(cursor))
|
|
226
|
+
: null;
|
|
227
|
+
const pixelEnd = pixel ? cursor + pixel[0].length : cursor;
|
|
228
|
+
if (pixel && !isIdentifierChar(value[pixelEnd])) {
|
|
229
|
+
result += `${formatRem(Number(pixel[1]) / rootValue)}rem`;
|
|
230
|
+
cursor += pixel[0].length;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
result += char;
|
|
234
|
+
cursor++;
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
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) {
|
|
243
|
+
throw new Error('[renderCard] responsive.mobile.rootValue must be a positive number');
|
|
244
|
+
}
|
|
245
|
+
const active = isMobile && unit === 'rem';
|
|
246
|
+
const context = {
|
|
247
|
+
active,
|
|
248
|
+
rootValue,
|
|
249
|
+
resolveLength(value) {
|
|
250
|
+
if (typeof value === 'number') {
|
|
251
|
+
return active
|
|
252
|
+
? `${formatRem(value / rootValue)}rem`
|
|
253
|
+
: `${value}px`;
|
|
254
|
+
}
|
|
255
|
+
return active ? convertPixelTokens(value, rootValue) : value;
|
|
256
|
+
},
|
|
257
|
+
convertCSS(value) {
|
|
258
|
+
return active ? convertPixelTokens(value, rootValue) : value;
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
return Object.freeze(context);
|
|
262
|
+
}
|
|
263
|
+
/** Convert CSS declarations without touching element text or SVG geometry. */
|
|
264
|
+
function applyResponsiveStyles(root, context) {
|
|
265
|
+
if (!context.active)
|
|
266
|
+
return;
|
|
267
|
+
const isPreservedContentDescendant = (element) => {
|
|
268
|
+
const boundary = element.closest('[data-responsive-content="preserve"]');
|
|
269
|
+
if (boundary == null || boundary === element)
|
|
270
|
+
return false;
|
|
271
|
+
const boundaryBelongsToRoot = root instanceof Element
|
|
272
|
+
? root.contains(boundary)
|
|
273
|
+
: boundary.getRootNode() === root;
|
|
274
|
+
return boundaryBelongsToRoot;
|
|
275
|
+
};
|
|
276
|
+
const convertElement = (element) => {
|
|
277
|
+
if (isPreservedContentDescendant(element))
|
|
278
|
+
return;
|
|
279
|
+
if (element instanceof SVGElement
|
|
280
|
+
&& !element.classList.contains('icon-svg'))
|
|
281
|
+
return;
|
|
282
|
+
const cssText = element.getAttribute('style');
|
|
283
|
+
if (cssText?.includes('px')) {
|
|
284
|
+
element.setAttribute('style', context.convertCSS(cssText));
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
if (root instanceof HTMLElement)
|
|
288
|
+
convertElement(root);
|
|
289
|
+
root.querySelectorAll('[style]').forEach(convertElement);
|
|
290
|
+
root.querySelectorAll('style').forEach((style) => {
|
|
291
|
+
if (isPreservedContentDescendant(style))
|
|
292
|
+
return;
|
|
293
|
+
if (style.textContent?.includes('px')) {
|
|
294
|
+
style.textContent = context.convertCSS(style.textContent);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
99
299
|
/**
|
|
100
300
|
* Chart renderer using ECharts (tree-shaken import).
|
|
101
301
|
* Supports: line, bar, pie/donut, scatter, funnel, heatmap.
|
|
@@ -1459,6 +1659,7 @@ class BaseElement extends HTMLElement {
|
|
|
1459
1659
|
this._node = null;
|
|
1460
1660
|
this._props = {};
|
|
1461
1661
|
this._isMobile = false;
|
|
1662
|
+
this._responsive = createResponsiveContext(false);
|
|
1462
1663
|
this.attachShadow({ mode: 'open' });
|
|
1463
1664
|
}
|
|
1464
1665
|
// ─── Data Interface ─────────────────────────────────────────
|
|
@@ -1466,10 +1667,12 @@ class BaseElement extends HTMLElement {
|
|
|
1466
1667
|
* Set component data and trigger render.
|
|
1467
1668
|
* Called by the component renderer (from `renderCard` pipeline).
|
|
1468
1669
|
*/
|
|
1469
|
-
setData(node, props, isMobile) {
|
|
1670
|
+
setData(node, props, isMobile, responsive) {
|
|
1470
1671
|
this._node = node;
|
|
1471
1672
|
this._props = props;
|
|
1472
1673
|
this._isMobile = isMobile;
|
|
1674
|
+
this._responsiveOptions = responsive;
|
|
1675
|
+
this._responsive = createResponsiveContext(isMobile, responsive);
|
|
1473
1676
|
// Expose identity on the host element for querying / debugging
|
|
1474
1677
|
this.setAttribute('data-card-id', node.id);
|
|
1475
1678
|
this.setAttribute('data-card-type', node.type);
|
|
@@ -1478,10 +1681,13 @@ class BaseElement extends HTMLElement {
|
|
|
1478
1681
|
/**
|
|
1479
1682
|
* Update props only (e.g. on variable change + re-render).
|
|
1480
1683
|
*/
|
|
1481
|
-
updateProps(props, isMobile) {
|
|
1684
|
+
updateProps(props, isMobile, responsive) {
|
|
1482
1685
|
this._props = props;
|
|
1483
1686
|
if (isMobile !== undefined)
|
|
1484
1687
|
this._isMobile = isMobile;
|
|
1688
|
+
if (responsive !== undefined)
|
|
1689
|
+
this._responsiveOptions = responsive;
|
|
1690
|
+
this._responsive = createResponsiveContext(this._isMobile, this._responsiveOptions);
|
|
1485
1691
|
this.render();
|
|
1486
1692
|
}
|
|
1487
1693
|
// ─── Helpers ────────────────────────────────────────────────
|
|
@@ -1501,11 +1707,18 @@ class BaseElement extends HTMLElement {
|
|
|
1501
1707
|
const processed = isExpressionResult
|
|
1502
1708
|
? style
|
|
1503
1709
|
: this.resolveSizeInStyle(style);
|
|
1504
|
-
return this.escapeAttribute(buildStyleString(processed));
|
|
1710
|
+
return this.escapeAttribute(this._responsive.convertCSS(buildStyleString(processed)));
|
|
1505
1711
|
}
|
|
1506
1712
|
/** Resolve a single size value (number → px). */
|
|
1507
1713
|
toCSS(value) {
|
|
1508
|
-
return
|
|
1714
|
+
return this._responsive.resolveLength(value);
|
|
1715
|
+
}
|
|
1716
|
+
/** Assign component markup and convert only its CSS declarations. */
|
|
1717
|
+
setShadowHTML(html) {
|
|
1718
|
+
if (!this.shadowRoot)
|
|
1719
|
+
return;
|
|
1720
|
+
this.shadowRoot.innerHTML = html;
|
|
1721
|
+
applyResponsiveStyles(this.shadowRoot, this._responsive);
|
|
1509
1722
|
}
|
|
1510
1723
|
/**
|
|
1511
1724
|
* Escape a value before interpolating it into a double-quoted HTML
|
|
@@ -1531,13 +1744,13 @@ class BaseElement extends HTMLElement {
|
|
|
1531
1744
|
// line-height is ambiguous: numbers < 4 are CSS multipliers
|
|
1532
1745
|
// (1.5 = 1.5×font-size), larger numbers keep the SDK-wide
|
|
1533
1746
|
// number→px convention (22 = 22px) for schema compat.
|
|
1534
|
-
resolved[key] = value < 4 ? value :
|
|
1747
|
+
resolved[key] = value < 4 ? value : this.toCSS(value);
|
|
1535
1748
|
}
|
|
1536
1749
|
else if (BaseElement.UNITLESS_PROPS.has(prop)) {
|
|
1537
1750
|
resolved[key] = value;
|
|
1538
1751
|
}
|
|
1539
1752
|
else {
|
|
1540
|
-
resolved[key] =
|
|
1753
|
+
resolved[key] = this.toCSS(value);
|
|
1541
1754
|
}
|
|
1542
1755
|
}
|
|
1543
1756
|
return resolved;
|
|
@@ -1600,7 +1813,7 @@ class CardText extends BaseElement {
|
|
|
1600
1813
|
this._tooltipTimer = 0;
|
|
1601
1814
|
}
|
|
1602
1815
|
// ─── setData override: reset streaming state on re-bindé ─────
|
|
1603
|
-
setData(node, props, isMobile) {
|
|
1816
|
+
setData(node, props, isMobile, responsive) {
|
|
1604
1817
|
// Reset streaming state so full re-render is triggered
|
|
1605
1818
|
if (this._streamTimer) {
|
|
1606
1819
|
clearInterval(this._streamTimer);
|
|
@@ -1611,12 +1824,7 @@ class CardText extends BaseElement {
|
|
|
1611
1824
|
this._displayedContent = '';
|
|
1612
1825
|
this._contentEl = null;
|
|
1613
1826
|
this._cursorEl = null;
|
|
1614
|
-
|
|
1615
|
-
this._props = props;
|
|
1616
|
-
this._isMobile = isMobile;
|
|
1617
|
-
this.setAttribute('data-card-id', node.id);
|
|
1618
|
-
this.setAttribute('data-card-type', node.type);
|
|
1619
|
-
this.render();
|
|
1827
|
+
super.setData(node, props, isMobile, responsive);
|
|
1620
1828
|
}
|
|
1621
1829
|
render() {
|
|
1622
1830
|
if (!this.shadowRoot || !this._node)
|
|
@@ -1670,7 +1878,7 @@ class CardText extends BaseElement {
|
|
|
1670
1878
|
: rawText;
|
|
1671
1879
|
// Use <span> for plain text (legacy compat), <div> for markdown (block elements)
|
|
1672
1880
|
const tag = isMarkdown || isStreaming ? 'div' : 'span';
|
|
1673
|
-
this.
|
|
1881
|
+
this.setShadowHTML(`
|
|
1674
1882
|
<style>
|
|
1675
1883
|
:host {
|
|
1676
1884
|
display: inline-block;
|
|
@@ -1782,8 +1990,8 @@ class CardText extends BaseElement {
|
|
|
1782
1990
|
<${tag}
|
|
1783
1991
|
class="card-text ${isMobile ? 'card-mobile' : 'card-desktop'}${maxLines ? ' clamped' : ''}"
|
|
1784
1992
|
style="${combinedStyle}"
|
|
1785
|
-
><span class="card-text-content">${initialHTML}</span>${isStreaming ? '<span class="streaming-cursor"></span>' : ''}</${tag}>
|
|
1786
|
-
|
|
1993
|
+
><span class="card-text-content" data-responsive-content="preserve">${initialHTML}</span>${isStreaming ? '<span class="streaming-cursor"></span>' : ''}</${tag}>
|
|
1994
|
+
`);
|
|
1787
1995
|
// Cache DOM references for streaming updates
|
|
1788
1996
|
this._contentEl = this.shadowRoot.querySelector('.card-text-content');
|
|
1789
1997
|
this._cursorEl = this.shadowRoot.querySelector('.streaming-cursor');
|
|
@@ -1907,7 +2115,12 @@ class CardText extends BaseElement {
|
|
|
1907
2115
|
* Remove cursor when streaming ends.
|
|
1908
2116
|
* Called externally when streaming prop changes to false.
|
|
1909
2117
|
*/
|
|
1910
|
-
updateProps(props, isMobile) {
|
|
2118
|
+
updateProps(props, isMobile, responsive) {
|
|
2119
|
+
if (isMobile !== undefined)
|
|
2120
|
+
this._isMobile = isMobile;
|
|
2121
|
+
if (responsive !== undefined)
|
|
2122
|
+
this._responsiveOptions = responsive;
|
|
2123
|
+
this._responsive = createResponsiveContext(this._isMobile, this._responsiveOptions);
|
|
1911
2124
|
const wasStreaming = this._props.streaming === true || this._props.streaming === 'true';
|
|
1912
2125
|
const willStream = props.streaming === true || props.streaming === 'true';
|
|
1913
2126
|
// If streaming just ended, remove cursor and stop animation
|
|
@@ -1928,13 +2141,9 @@ class CardText extends BaseElement {
|
|
|
1928
2141
|
: this._escapeHTML(rawText);
|
|
1929
2142
|
}
|
|
1930
2143
|
this._props = props;
|
|
1931
|
-
if (isMobile !== undefined)
|
|
1932
|
-
this._isMobile = isMobile;
|
|
1933
2144
|
return;
|
|
1934
2145
|
}
|
|
1935
2146
|
this._props = props;
|
|
1936
|
-
if (isMobile !== undefined)
|
|
1937
|
-
this._isMobile = isMobile;
|
|
1938
2147
|
this.render();
|
|
1939
2148
|
}
|
|
1940
2149
|
// ─── Helpers ──────────────────────────────────────────────────
|
|
@@ -2141,7 +2350,7 @@ class CardButton extends BaseElement {
|
|
|
2141
2350
|
large: { padding: '12px 28px', fontSize: '16px' },
|
|
2142
2351
|
};
|
|
2143
2352
|
const sizeConfig = sizeMap[size] ?? sizeMap.medium;
|
|
2144
|
-
this.
|
|
2353
|
+
this.setShadowHTML(`
|
|
2145
2354
|
<style>
|
|
2146
2355
|
:host {
|
|
2147
2356
|
display: ${block ? 'block' : 'inline-block'};
|
|
@@ -2286,7 +2495,7 @@ class CardButton extends BaseElement {
|
|
|
2286
2495
|
${disabled ? 'disabled' : ''}
|
|
2287
2496
|
style="${inlineStyle}"
|
|
2288
2497
|
><span class="card-btn-content">${icon ? `<span class="card-btn-icon">${sanitizeIconHtml(String(icon))}</span>` : ''}${displayText}</span></button>
|
|
2289
|
-
|
|
2498
|
+
`);
|
|
2290
2499
|
}
|
|
2291
2500
|
}
|
|
2292
2501
|
CardButton.is = 'ai-card-button';
|
|
@@ -2317,20 +2526,40 @@ function sanitizeImageSrc(src) {
|
|
|
2317
2526
|
return '';
|
|
2318
2527
|
return trimmed;
|
|
2319
2528
|
}
|
|
2529
|
+
function normalizeIconSize(value) {
|
|
2530
|
+
if (typeof value === 'number') {
|
|
2531
|
+
const size = Number.isFinite(value) && value > 0 ? value : 24;
|
|
2532
|
+
return { css: `${size}px`, intrinsic: size };
|
|
2533
|
+
}
|
|
2534
|
+
const raw = String(value ?? '').trim();
|
|
2535
|
+
if (/^(?:\d+\.?\d*|\.\d+)$/.test(raw)) {
|
|
2536
|
+
const size = Number(raw) || 24;
|
|
2537
|
+
return { css: `${size}px`, intrinsic: size };
|
|
2538
|
+
}
|
|
2539
|
+
if (/^(?:\d+\.?\d*|\.\d+)(?:px|rem|em|%|vw|vh|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/.test(raw)) {
|
|
2540
|
+
return { css: raw };
|
|
2541
|
+
}
|
|
2542
|
+
return { css: '24px', intrinsic: 24 };
|
|
2543
|
+
}
|
|
2320
2544
|
function renderIconContent(input) {
|
|
2321
|
-
const
|
|
2545
|
+
const normalizedSize = normalizeIconSize(input.size);
|
|
2546
|
+
const size = normalizedSize.css;
|
|
2322
2547
|
const name = input.name == null ? undefined : String(input.name);
|
|
2548
|
+
const sizeStyle = `width:${escapeAttr$1(size)};height:${escapeAttr$1(size)};`;
|
|
2549
|
+
const intrinsicAttrs = normalizedSize.intrinsic == null
|
|
2550
|
+
? ''
|
|
2551
|
+
: `width="${normalizedSize.intrinsic}" height="${normalizedSize.intrinsic}" `;
|
|
2323
2552
|
if (input.src) {
|
|
2324
2553
|
return `<img class="icon-img" src="${escapeAttr$1(sanitizeImageSrc(input.src))}" ` +
|
|
2325
|
-
|
|
2554
|
+
`${intrinsicAttrs}style="${sizeStyle}" alt="${escapeAttr$1(name ?? 'icon')}" />`;
|
|
2326
2555
|
}
|
|
2327
2556
|
const icon = getBuiltinIcon(name);
|
|
2328
2557
|
if (icon) {
|
|
2329
|
-
return `<svg class="icon-svg" viewBox="${escapeAttr$1(icon.viewBox)}"
|
|
2330
|
-
`
|
|
2558
|
+
return `<svg class="icon-svg" viewBox="${escapeAttr$1(icon.viewBox)}" ${intrinsicAttrs}style="${sizeStyle}" ` +
|
|
2559
|
+
`color="${escapeAttr$1(String(input.color ?? 'currentColor'))}" ` +
|
|
2331
2560
|
`aria-hidden="true">${icon.body}</svg>`;
|
|
2332
2561
|
}
|
|
2333
|
-
return `<span class="icon-text" style="font-size:${size}
|
|
2562
|
+
return `<span class="icon-text" style="font-size:${escapeAttr$1(size)};line-height:1;">` +
|
|
2334
2563
|
`${escapeText$1(name ?? '?')}</span>`;
|
|
2335
2564
|
}
|
|
2336
2565
|
|
|
@@ -2542,7 +2771,7 @@ class CardInput extends BaseElement {
|
|
|
2542
2771
|
${numberAttributes}
|
|
2543
2772
|
style="${nativeInlineStyle}"
|
|
2544
2773
|
/>`;
|
|
2545
|
-
this.
|
|
2774
|
+
this.setShadowHTML(`
|
|
2546
2775
|
<style>
|
|
2547
2776
|
:host {
|
|
2548
2777
|
display: block;
|
|
@@ -2716,7 +2945,7 @@ class CardInput extends BaseElement {
|
|
|
2716
2945
|
${numberStepperHtml}
|
|
2717
2946
|
</div>
|
|
2718
2947
|
</div>
|
|
2719
|
-
|
|
2948
|
+
`);
|
|
2720
2949
|
// Wire up native input/change events that bubble out of Shadow DOM.
|
|
2721
2950
|
// renderCard listens for these standard event names (mapped from onInput / onChange).
|
|
2722
2951
|
// The `detail.value` carries the current input value so that:
|
|
@@ -2839,7 +3068,7 @@ class CardImage extends BaseElement {
|
|
|
2839
3068
|
imgStyles.push(`object-fit:${objectFit}`);
|
|
2840
3069
|
if (inlineStyle)
|
|
2841
3070
|
imgStyles.push(inlineStyle);
|
|
2842
|
-
this.
|
|
3071
|
+
this.setShadowHTML(`
|
|
2843
3072
|
<style>
|
|
2844
3073
|
:host {
|
|
2845
3074
|
display: inline-block;
|
|
@@ -2948,7 +3177,7 @@ class CardImage extends BaseElement {
|
|
|
2948
3177
|
<button class="lightbox-close" aria-label="Close">×</button>
|
|
2949
3178
|
<img class="lightbox-image" src="${imgSrc}" alt="${imgAlt}" />
|
|
2950
3179
|
</div>
|
|
2951
|
-
|
|
3180
|
+
`);
|
|
2952
3181
|
this.bindEvents(preview);
|
|
2953
3182
|
}
|
|
2954
3183
|
bindEvents(preview) {
|
|
@@ -3040,7 +3269,7 @@ class CardDivider extends BaseElement {
|
|
|
3040
3269
|
const isVertical = direction === 'vertical';
|
|
3041
3270
|
const borderStyle = dashed ? 'dashed' : 'solid';
|
|
3042
3271
|
const displayText = text ? this.resolveContent(text) : '';
|
|
3043
|
-
this.
|
|
3272
|
+
this.setShadowHTML(`
|
|
3044
3273
|
<style>
|
|
3045
3274
|
:host {
|
|
3046
3275
|
display: ${isVertical ? 'inline-flex' : 'flex'};
|
|
@@ -3064,7 +3293,7 @@ class CardDivider extends BaseElement {
|
|
|
3064
3293
|
</style>
|
|
3065
3294
|
<div class="divider-line"></div>
|
|
3066
3295
|
${displayText ? `<span class="divider-text">${displayText}</span><div class="divider-line"></div>` : ''}
|
|
3067
|
-
|
|
3296
|
+
`);
|
|
3068
3297
|
if (inlineStyle) {
|
|
3069
3298
|
this.style.cssText += ';' + inlineStyle;
|
|
3070
3299
|
}
|
|
@@ -3176,7 +3405,7 @@ class CardRate extends BaseElement {
|
|
|
3176
3405
|
return `<span class="star ${isFull ? 'filled' : ''}" data-value="${starIndex}"
|
|
3177
3406
|
style="${starInline};${itemInline}">★</span>`;
|
|
3178
3407
|
}).join('');
|
|
3179
|
-
this.
|
|
3408
|
+
this.setShadowHTML(`
|
|
3180
3409
|
<style>
|
|
3181
3410
|
:host {
|
|
3182
3411
|
display: inline-flex;
|
|
@@ -3234,7 +3463,7 @@ class CardRate extends BaseElement {
|
|
|
3234
3463
|
aria-readonly="${Boolean(readOnly)}"
|
|
3235
3464
|
style="${inlineStyle}"
|
|
3236
3465
|
>${stars}</div>
|
|
3237
|
-
|
|
3466
|
+
`);
|
|
3238
3467
|
if (!interactive)
|
|
3239
3468
|
return;
|
|
3240
3469
|
// Bind click handlers
|
|
@@ -3383,7 +3612,7 @@ class CardCounter extends BaseElement {
|
|
|
3383
3612
|
const numInline = this.buildInlineStyle(valueStyle, isExpressionResultStyle);
|
|
3384
3613
|
const minusContent = this.renderIcon(minusIcon, this.defaultMinusGlyph(btnSize), btnSize);
|
|
3385
3614
|
const plusContent = this.renderIcon(plusIcon, this.defaultPlusGlyph(btnSize), btnSize);
|
|
3386
|
-
this.
|
|
3615
|
+
this.setShadowHTML(`
|
|
3387
3616
|
<style>
|
|
3388
3617
|
:host {
|
|
3389
3618
|
display: inline-flex;
|
|
@@ -3458,7 +3687,7 @@ class CardCounter extends BaseElement {
|
|
|
3458
3687
|
${plusDisabled ? 'disabled' : ''} aria-label="${this.escapeAttr(String(increaseAriaLabel))}"
|
|
3459
3688
|
style="${plusInline}">${plusContent}</button>
|
|
3460
3689
|
</div>
|
|
3461
|
-
|
|
3690
|
+
`);
|
|
3462
3691
|
if (disabled || readOnly)
|
|
3463
3692
|
return;
|
|
3464
3693
|
const minusBtn = this.shadowRoot.querySelector('.c-minus');
|
|
@@ -3573,7 +3802,7 @@ class CardTag extends BaseElement {
|
|
|
3573
3802
|
const bgColor = isCustomColor ? color + '1a' : preset.bg;
|
|
3574
3803
|
const borderColor = isCustomColor ? color : preset.border;
|
|
3575
3804
|
const textColor = isCustomColor ? color : preset.text;
|
|
3576
|
-
this.
|
|
3805
|
+
this.setShadowHTML(`
|
|
3577
3806
|
<style>
|
|
3578
3807
|
:host {
|
|
3579
3808
|
display: inline-flex;
|
|
@@ -3609,7 +3838,7 @@ class CardTag extends BaseElement {
|
|
|
3609
3838
|
${text}
|
|
3610
3839
|
${closable ? '<span class="close-btn">✕</span>' : ''}
|
|
3611
3840
|
</span>
|
|
3612
|
-
|
|
3841
|
+
`);
|
|
3613
3842
|
if (closable) {
|
|
3614
3843
|
this.shadowRoot.querySelector('.close-btn')?.addEventListener('click', () => {
|
|
3615
3844
|
this.dispatchEvent(new CustomEvent('close', {
|
|
@@ -3664,7 +3893,7 @@ class CardSelect extends BaseElement {
|
|
|
3664
3893
|
render() {
|
|
3665
3894
|
if (!this.shadowRoot || !this._node)
|
|
3666
3895
|
return;
|
|
3667
|
-
const { options = [], placeholder = '请选择', value: externalValue, defaultValue = '', disabled = false, readonly: readOnly = false, ariaLabel = placeholder, size = 'medium', variant = 'outlined', accentColor = '#1677ff', style, styles = {}, isExpressionResultStyle, } = this._props;
|
|
3896
|
+
const { options = [], placeholder = '请选择', value: externalValue, defaultValue = '', disabled = false, readonly: readOnly = false, ariaLabel = placeholder, size = 'medium', variant = 'outlined', accentColor = '#1677ff', arrowSize, style, styles = {}, isExpressionResultStyle, } = this._props;
|
|
3668
3897
|
const propValue = String(externalValue ?? defaultValue ?? '');
|
|
3669
3898
|
const resolvedSize = (['small', 'medium', 'large'].includes(size)
|
|
3670
3899
|
? size
|
|
@@ -3702,6 +3931,7 @@ class CardSelect extends BaseElement {
|
|
|
3702
3931
|
optionPadding: '5px 12px',
|
|
3703
3932
|
},
|
|
3704
3933
|
}[resolvedSize];
|
|
3934
|
+
const resolvedArrowSize = arrowSize ?? sizePreset.arrowSize;
|
|
3705
3935
|
const variantPreset = {
|
|
3706
3936
|
outlined: {
|
|
3707
3937
|
background: '#fff',
|
|
@@ -3725,13 +3955,14 @@ class CardSelect extends BaseElement {
|
|
|
3725
3955
|
this.applyHostStyles(style, isExpressionResultStyle, String(accentColor));
|
|
3726
3956
|
const triggerInline = this.buildInlineStyle(semanticStyles.trigger, isExpressionResultStyle);
|
|
3727
3957
|
const dropdownInline = this.buildInlineStyle(semanticStyles.dropdown, isExpressionResultStyle);
|
|
3958
|
+
const arrowInline = this.buildInlineStyle(semanticStyles.arrow, isExpressionResultStyle);
|
|
3728
3959
|
const selectedOption = options.find((o) => o.value === value);
|
|
3729
3960
|
const displayText = selectedOption ? selectedOption.label : placeholder;
|
|
3730
3961
|
const isPlaceholder = !selectedOption;
|
|
3731
3962
|
const openClass = this._open ? 'open' : '';
|
|
3732
3963
|
const arrowIcon = renderIconContent({
|
|
3733
3964
|
name: 'caret_down',
|
|
3734
|
-
size:
|
|
3965
|
+
size: this.toCSS(resolvedArrowSize),
|
|
3735
3966
|
color: 'currentColor',
|
|
3736
3967
|
});
|
|
3737
3968
|
const listboxId = `${this._node.id}-listbox`;
|
|
@@ -3740,7 +3971,7 @@ class CardSelect extends BaseElement {
|
|
|
3740
3971
|
&& this._activeIndex < options.length)
|
|
3741
3972
|
? `${this._node.id}-option-${this._activeIndex}`
|
|
3742
3973
|
: '';
|
|
3743
|
-
this.
|
|
3974
|
+
this.setShadowHTML(`
|
|
3744
3975
|
<style>
|
|
3745
3976
|
:host {
|
|
3746
3977
|
display: inline-block;
|
|
@@ -3797,8 +4028,8 @@ class CardSelect extends BaseElement {
|
|
|
3797
4028
|
flex: none;
|
|
3798
4029
|
align-items: center;
|
|
3799
4030
|
justify-content: center;
|
|
3800
|
-
width: ${
|
|
3801
|
-
height: ${
|
|
4031
|
+
width: ${this.toCSS(resolvedArrowSize)};
|
|
4032
|
+
height: ${this.toCSS(resolvedArrowSize)};
|
|
3802
4033
|
margin-left: 8px;
|
|
3803
4034
|
color: ${disabled ? '#bfbfbf' : '#999'};
|
|
3804
4035
|
transition: transform 0.2s;
|
|
@@ -3885,7 +4116,7 @@ class CardSelect extends BaseElement {
|
|
|
3885
4116
|
style="${triggerInline}"
|
|
3886
4117
|
>
|
|
3887
4118
|
<span class="select-text">${this.escapeText(String(displayText))}</span>
|
|
3888
|
-
<span class="select-arrow ${openClass}">${arrowIcon}</span>
|
|
4119
|
+
<span class="select-arrow ${openClass}" style="${arrowInline}">${arrowIcon}</span>
|
|
3889
4120
|
</div>
|
|
3890
4121
|
<div
|
|
3891
4122
|
class="dropdown ${openClass}"
|
|
@@ -3911,7 +4142,7 @@ class CardSelect extends BaseElement {
|
|
|
3911
4142
|
><span class="option-label">${this.escapeText(String(opt.label))}</span></div>`);
|
|
3912
4143
|
}).join('')}
|
|
3913
4144
|
</div>
|
|
3914
|
-
|
|
4145
|
+
`);
|
|
3915
4146
|
if (disabled)
|
|
3916
4147
|
return;
|
|
3917
4148
|
const trigger = this.shadowRoot.querySelector('.select-trigger');
|
|
@@ -3938,13 +4169,13 @@ class CardSelect extends BaseElement {
|
|
|
3938
4169
|
disconnectedCallback() {
|
|
3939
4170
|
document.removeEventListener('click', this._onDocClick);
|
|
3940
4171
|
}
|
|
3941
|
-
updateProps(props, isMobile) {
|
|
4172
|
+
updateProps(props, isMobile, responsive) {
|
|
3942
4173
|
if (Object.prototype.hasOwnProperty.call(props, 'value')
|
|
3943
4174
|
|| Object.prototype.hasOwnProperty.call(props, 'defaultValue')) {
|
|
3944
4175
|
this._localValue = null;
|
|
3945
4176
|
this._propValue = undefined;
|
|
3946
4177
|
}
|
|
3947
|
-
super.updateProps(props, isMobile);
|
|
4178
|
+
super.updateProps(props, isMobile, responsive);
|
|
3948
4179
|
}
|
|
3949
4180
|
_setOpen(open) {
|
|
3950
4181
|
if (this._open === open)
|
|
@@ -4092,7 +4323,7 @@ class CardPasscodeInput extends BaseElement {
|
|
|
4092
4323
|
autocomplete="one-time-code"
|
|
4093
4324
|
${disabled ? 'disabled' : ''}
|
|
4094
4325
|
/>`).join('');
|
|
4095
|
-
this.
|
|
4326
|
+
this.setShadowHTML(`
|
|
4096
4327
|
<style>
|
|
4097
4328
|
:host {
|
|
4098
4329
|
display: inline-flex;
|
|
@@ -4128,7 +4359,7 @@ class CardPasscodeInput extends BaseElement {
|
|
|
4128
4359
|
}
|
|
4129
4360
|
</style>
|
|
4130
4361
|
<div class="passcode-wrapper">${inputs}</div>
|
|
4131
|
-
|
|
4362
|
+
`);
|
|
4132
4363
|
if (disabled)
|
|
4133
4364
|
return;
|
|
4134
4365
|
const cells = this.shadowRoot.querySelectorAll('.passcode-cell');
|
|
@@ -4211,10 +4442,10 @@ class CardIcon extends BaseElement {
|
|
|
4211
4442
|
const iconContent = renderIconContent({
|
|
4212
4443
|
name,
|
|
4213
4444
|
src,
|
|
4214
|
-
size: iconSize,
|
|
4445
|
+
size: this.toCSS(iconSize),
|
|
4215
4446
|
color,
|
|
4216
4447
|
});
|
|
4217
|
-
this.
|
|
4448
|
+
this.setShadowHTML(`
|
|
4218
4449
|
<style>
|
|
4219
4450
|
:host {
|
|
4220
4451
|
display: inline-flex;
|
|
@@ -4237,7 +4468,7 @@ class CardIcon extends BaseElement {
|
|
|
4237
4468
|
}
|
|
4238
4469
|
</style>
|
|
4239
4470
|
<span class="icon-wrapper" style="${inlineStyle}">${iconContent}</span>
|
|
4240
|
-
|
|
4471
|
+
`);
|
|
4241
4472
|
}
|
|
4242
4473
|
}
|
|
4243
4474
|
CardIcon.is = 'ai-card-icon';
|
|
@@ -4319,7 +4550,7 @@ class CardForm extends BaseElement {
|
|
|
4319
4550
|
const fieldItems = fields
|
|
4320
4551
|
.map((field, index) => this._renderField(field, index))
|
|
4321
4552
|
.join('');
|
|
4322
|
-
this.
|
|
4553
|
+
this.setShadowHTML(`
|
|
4323
4554
|
<style>
|
|
4324
4555
|
:host {
|
|
4325
4556
|
display: block;
|
|
@@ -4479,7 +4710,7 @@ class CardForm extends BaseElement {
|
|
|
4479
4710
|
${fieldItems}
|
|
4480
4711
|
<button class="submit-btn" ${disabled ? 'disabled' : ''} style="${isHorizontal ? `margin-left: 84px;` : ''}">${submitText}</button>
|
|
4481
4712
|
</div>
|
|
4482
|
-
|
|
4713
|
+
`);
|
|
4483
4714
|
this._initializeSelectFields(fields, disabled);
|
|
4484
4715
|
this._initializeRateFields(fields, disabled);
|
|
4485
4716
|
this._bindEvents(fields, disabled);
|
|
@@ -4600,7 +4831,7 @@ class CardForm extends BaseElement {
|
|
|
4600
4831
|
events: undefined,
|
|
4601
4832
|
directives: undefined,
|
|
4602
4833
|
};
|
|
4603
|
-
selectElement.setData(node, props, this._isMobile);
|
|
4834
|
+
selectElement.setData(node, props, this._isMobile, this._responsiveOptions);
|
|
4604
4835
|
const label = selectElement
|
|
4605
4836
|
.closest('.form-field')
|
|
4606
4837
|
?.querySelector('.field-label');
|
|
@@ -4653,7 +4884,7 @@ class CardForm extends BaseElement {
|
|
|
4653
4884
|
events: undefined,
|
|
4654
4885
|
directives: undefined,
|
|
4655
4886
|
};
|
|
4656
|
-
rateElement.setData(node, props, this._isMobile);
|
|
4887
|
+
rateElement.setData(node, props, this._isMobile, this._responsiveOptions);
|
|
4657
4888
|
const label = rateElement
|
|
4658
4889
|
.closest('.form-field')
|
|
4659
4890
|
?.querySelector('.field-label');
|
|
@@ -4964,7 +5195,7 @@ class CardLoading extends BaseElement {
|
|
|
4964
5195
|
const displayText = text ? this.resolveContent(text) : '';
|
|
4965
5196
|
const px = typeof size === 'number' ? `${size}px` : size;
|
|
4966
5197
|
const dur = typeof duration === 'number' ? `${duration}s` : duration;
|
|
4967
|
-
this.
|
|
5198
|
+
this.setShadowHTML(`
|
|
4968
5199
|
<style>
|
|
4969
5200
|
:host {
|
|
4970
5201
|
display: inline-flex;
|
|
@@ -4993,7 +5224,7 @@ class CardLoading extends BaseElement {
|
|
|
4993
5224
|
</style>
|
|
4994
5225
|
<div class="spinner"></div>
|
|
4995
5226
|
${displayText ? `<span class="loading-text">${displayText}</span>` : ''}
|
|
4996
|
-
|
|
5227
|
+
`);
|
|
4997
5228
|
if (inlineStyle) {
|
|
4998
5229
|
this.style.cssText += ';' + inlineStyle;
|
|
4999
5230
|
}
|
|
@@ -5185,6 +5416,7 @@ class CardProgress extends BaseElement {
|
|
|
5185
5416
|
track.setAttribute('aria-valuetext', valueText);
|
|
5186
5417
|
}
|
|
5187
5418
|
this.shadowRoot.replaceChildren(styleElement, wrapper);
|
|
5419
|
+
applyResponsiveStyles(this.shadowRoot, this._responsive);
|
|
5188
5420
|
}
|
|
5189
5421
|
buildLegend(segments, position, style, isExpressionResultStyle) {
|
|
5190
5422
|
const labeled = segments.filter((segment) => segment.label?.trim());
|
|
@@ -5378,7 +5610,7 @@ class CardSteps extends BaseElement {
|
|
|
5378
5610
|
</div>
|
|
5379
5611
|
`;
|
|
5380
5612
|
}).join('');
|
|
5381
|
-
this.
|
|
5613
|
+
this.setShadowHTML(`
|
|
5382
5614
|
<style>
|
|
5383
5615
|
:host {
|
|
5384
5616
|
display: block;
|
|
@@ -5483,7 +5715,7 @@ class CardSteps extends BaseElement {
|
|
|
5483
5715
|
}
|
|
5484
5716
|
</style>
|
|
5485
5717
|
<div class="steps-wrapper" style="${inlineStyle}">${stepsHTML}</div>
|
|
5486
|
-
|
|
5718
|
+
`);
|
|
5487
5719
|
// Bind click only on clickable steps → dispatch `step-click` event
|
|
5488
5720
|
this.shadowRoot.querySelectorAll('.step-clickable').forEach((el) => {
|
|
5489
5721
|
el.addEventListener('click', (e) => {
|
|
@@ -5577,10 +5809,12 @@ class CardCollapse extends BaseElement {
|
|
|
5577
5809
|
// Arrow HTML: custom image or built-in SVG
|
|
5578
5810
|
const arrowHTML = arrowIconUrl
|
|
5579
5811
|
? `<img src="${arrowIconUrl}" width="7" height="7" style="display:block" />`
|
|
5580
|
-
:
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5812
|
+
: renderIconContent({
|
|
5813
|
+
name: 'caret_down',
|
|
5814
|
+
size: this.toCSS(12),
|
|
5815
|
+
color: 'currentColor',
|
|
5816
|
+
});
|
|
5817
|
+
this.setShadowHTML(`
|
|
5584
5818
|
<style>
|
|
5585
5819
|
:host {
|
|
5586
5820
|
display: block;
|
|
@@ -5703,11 +5937,13 @@ class CardCollapse extends BaseElement {
|
|
|
5703
5937
|
</span>
|
|
5704
5938
|
</div>
|
|
5705
5939
|
<div class="collapse-body ${this._expanded ? 'expanded' : ''}" style="${bodyInlineStyle}">
|
|
5706
|
-
<div class="collapse-content" style="${contentInlineStyle}"
|
|
5940
|
+
<div class="collapse-content" style="${contentInlineStyle}">
|
|
5941
|
+
<div class="collapse-markdown" data-responsive-content="preserve">${bodyHTML ? bodyHTML : ''}</div>
|
|
5942
|
+
</div>
|
|
5707
5943
|
<div class="collapse-slot"><slot></slot></div>
|
|
5708
5944
|
</div>
|
|
5709
5945
|
</div>
|
|
5710
|
-
|
|
5946
|
+
`);
|
|
5711
5947
|
// Bind click event
|
|
5712
5948
|
const header = this.shadowRoot.querySelector('.collapse-header');
|
|
5713
5949
|
header?.addEventListener('click', () => this._toggle());
|
|
@@ -5879,7 +6115,7 @@ class CardHtml extends BaseElement {
|
|
|
5879
6115
|
}
|
|
5880
6116
|
if (inlineStyle)
|
|
5881
6117
|
wrapperStyles.push(inlineStyle);
|
|
5882
|
-
this.
|
|
6118
|
+
this.setShadowHTML(`
|
|
5883
6119
|
<style>
|
|
5884
6120
|
/* Functional styles ONLY — content presentation is owned by the HTML
|
|
5885
6121
|
itself (browser semantic defaults + the model's inline styles).
|
|
@@ -5899,9 +6135,10 @@ class CardHtml extends BaseElement {
|
|
|
5899
6135
|
</style>
|
|
5900
6136
|
<div
|
|
5901
6137
|
class="card-html ${isMobile ? 'card-mobile' : 'card-desktop'}"
|
|
6138
|
+
data-responsive-content="preserve"
|
|
5902
6139
|
style="${wrapperStyles.join(';')}"
|
|
5903
6140
|
>${safeHtml}</div>
|
|
5904
|
-
|
|
6141
|
+
`);
|
|
5905
6142
|
// Restore scroll state after replacement (only relevant with maxHeight)
|
|
5906
6143
|
if (maxHeight != null) {
|
|
5907
6144
|
const wrapper = this.shadowRoot.querySelector('.card-html');
|
|
@@ -6072,7 +6309,7 @@ class CardDatePicker extends BaseElement {
|
|
|
6072
6309
|
displayText = showTodayPrefix && isToday(date) ? `${todayText}, ${formatted}` : formatted;
|
|
6073
6310
|
}
|
|
6074
6311
|
const hostWidth = width != null && width !== '' ? `width: ${this.toCSS(width)};` : '';
|
|
6075
|
-
this.
|
|
6312
|
+
this.setShadowHTML(`
|
|
6076
6313
|
<style>
|
|
6077
6314
|
:host {
|
|
6078
6315
|
display: inline-block;
|
|
@@ -6175,7 +6412,7 @@ class CardDatePicker extends BaseElement {
|
|
|
6175
6412
|
<span class="trigger-arrow ${this._open ? 'open' : ''}">${CHEVRON_ICON}</span>
|
|
6176
6413
|
</div>
|
|
6177
6414
|
<div class="panel ${this._open ? 'open' : ''}"><div class="panel-calendar"></div></div>
|
|
6178
|
-
|
|
6415
|
+
`);
|
|
6179
6416
|
if (disabled)
|
|
6180
6417
|
return;
|
|
6181
6418
|
const trigger = this.shadowRoot.querySelector('.trigger');
|
|
@@ -6407,7 +6644,7 @@ class CardAudio extends BaseElement {
|
|
|
6407
6644
|
const valid = isSafeMediaSrc(src);
|
|
6408
6645
|
// Audible autoplay is blocked by browsers — only ever autoplay muted
|
|
6409
6646
|
const effectiveMuted = muted || autoplay;
|
|
6410
|
-
this.
|
|
6647
|
+
this.setShadowHTML(`
|
|
6411
6648
|
<style>
|
|
6412
6649
|
:host {
|
|
6413
6650
|
display: block;
|
|
@@ -6438,7 +6675,7 @@ class CardAudio extends BaseElement {
|
|
|
6438
6675
|
style="${inlineStyle}"
|
|
6439
6676
|
></audio>`
|
|
6440
6677
|
: `<div class="placeholder" style="${inlineStyle}">暂无可播放的媒体源</div>`}
|
|
6441
|
-
|
|
6678
|
+
`);
|
|
6442
6679
|
const media = this.shadowRoot.querySelector('audio');
|
|
6443
6680
|
if (media) {
|
|
6444
6681
|
// The `muted` content attribute only applies before load — set the
|
|
@@ -6492,7 +6729,7 @@ class CardVideo extends BaseElement {
|
|
|
6492
6729
|
const videoHeight = height != null && height !== '' ? `height: ${this.toCSS(height)};` : '';
|
|
6493
6730
|
const valid = isSafeMediaSrc(src);
|
|
6494
6731
|
const effectiveMuted = muted || autoplay;
|
|
6495
|
-
this.
|
|
6732
|
+
this.setShadowHTML(`
|
|
6496
6733
|
<style>
|
|
6497
6734
|
:host {
|
|
6498
6735
|
display: block;
|
|
@@ -6530,7 +6767,7 @@ class CardVideo extends BaseElement {
|
|
|
6530
6767
|
style="${inlineStyle}"
|
|
6531
6768
|
></video>`
|
|
6532
6769
|
: `<div class="placeholder" style="${inlineStyle}">暂无可播放的媒体源</div>`}
|
|
6533
|
-
|
|
6770
|
+
`);
|
|
6534
6771
|
const media = this.shadowRoot.querySelector('video');
|
|
6535
6772
|
if (media) {
|
|
6536
6773
|
media.muted = effectiveMuted;
|
|
@@ -6574,7 +6811,7 @@ class CardSwitch extends BaseElement {
|
|
|
6574
6811
|
...(thumbStyle || {}),
|
|
6575
6812
|
backgroundColor: thumbColor,
|
|
6576
6813
|
}, isExpressionResultStyle);
|
|
6577
|
-
this.
|
|
6814
|
+
this.setShadowHTML(`
|
|
6578
6815
|
<style>
|
|
6579
6816
|
:host {
|
|
6580
6817
|
display: inline-flex;
|
|
@@ -6642,7 +6879,7 @@ class CardSwitch extends BaseElement {
|
|
|
6642
6879
|
<span class="switch-thumb" part="thumb" style="${thumbInline}"></span>
|
|
6643
6880
|
</button>
|
|
6644
6881
|
</div>
|
|
6645
|
-
|
|
6882
|
+
`);
|
|
6646
6883
|
const button = this.shadowRoot.querySelector('.switch-track');
|
|
6647
6884
|
button?.addEventListener('click', () => {
|
|
6648
6885
|
if (disabled || readOnly)
|
|
@@ -6761,7 +6998,7 @@ class CardChoiceItem extends BaseElement {
|
|
|
6761
6998
|
this.setAttribute('data-selected', String(selected));
|
|
6762
6999
|
this.toggleAttribute('data-disabled', fullyDisabled);
|
|
6763
7000
|
this.toggleAttribute('data-selection-blocked', selectionBlocked);
|
|
6764
|
-
this.
|
|
7001
|
+
this.setShadowHTML(`
|
|
6765
7002
|
<style>
|
|
6766
7003
|
:host {
|
|
6767
7004
|
display: block;
|
|
@@ -6852,7 +7089,7 @@ class CardChoiceItem extends BaseElement {
|
|
|
6852
7089
|
</div>
|
|
6853
7090
|
${indicatorPosition === 'end' ? this.renderIndicator(indicatorRole, selected, selectionUnavailable, tabIndex, ariaLabel, indicatorInline, checkedMark) : ''}
|
|
6854
7091
|
</div>
|
|
6855
|
-
|
|
7092
|
+
`);
|
|
6856
7093
|
const root = this.shadowRoot.querySelector('.choice-item');
|
|
6857
7094
|
const indicator = this.shadowRoot.querySelector('.choice-indicator');
|
|
6858
7095
|
indicator?.addEventListener('click', (event) => {
|
|
@@ -6993,7 +7230,7 @@ class CardChoiceList extends BaseElement {
|
|
|
6993
7230
|
this.addEventListener('choice-request-change', this._onChoiceRequest);
|
|
6994
7231
|
this.removeEventListener('choice-navigate', this._onChoiceNavigate);
|
|
6995
7232
|
this.addEventListener('choice-navigate', this._onChoiceNavigate);
|
|
6996
|
-
this.
|
|
7233
|
+
this.setShadowHTML(`
|
|
6997
7234
|
<style>
|
|
6998
7235
|
:host {
|
|
6999
7236
|
display: block;
|
|
@@ -7016,7 +7253,7 @@ class CardChoiceList extends BaseElement {
|
|
|
7016
7253
|
>
|
|
7017
7254
|
<slot></slot>
|
|
7018
7255
|
</div>
|
|
7019
|
-
|
|
7256
|
+
`);
|
|
7020
7257
|
this.shadowRoot.querySelector('slot')?.addEventListener('slotchange', () => this.syncItems());
|
|
7021
7258
|
queueMicrotask(() => this.syncItems());
|
|
7022
7259
|
}
|
|
@@ -7227,159 +7464,159 @@ if (typeof customElements !== 'undefined'
|
|
|
7227
7464
|
* Render a Text node as an `<ai-card-text>` Custom Element.
|
|
7228
7465
|
* All text styling is scoped inside Shadow DOM.
|
|
7229
7466
|
*/
|
|
7230
|
-
function renderText(node, props, isMobile) {
|
|
7467
|
+
function renderText(node, props, isMobile, responsive) {
|
|
7231
7468
|
const el = document.createElement(CardText.is);
|
|
7232
|
-
el.setData(node, props, isMobile);
|
|
7469
|
+
el.setData(node, props, isMobile, responsive);
|
|
7233
7470
|
return el;
|
|
7234
7471
|
}
|
|
7235
7472
|
/**
|
|
7236
7473
|
* Render a Button node as an `<ai-card-button>` Custom Element.
|
|
7237
7474
|
* Supports variants (primary/secondary/text/danger), sizes, disabled state.
|
|
7238
7475
|
*/
|
|
7239
|
-
function renderButton(node, props, isMobile) {
|
|
7476
|
+
function renderButton(node, props, isMobile, responsive) {
|
|
7240
7477
|
const el = document.createElement(CardButton.is);
|
|
7241
|
-
el.setData(node, props, isMobile);
|
|
7478
|
+
el.setData(node, props, isMobile, responsive);
|
|
7242
7479
|
return el;
|
|
7243
7480
|
}
|
|
7244
7481
|
/**
|
|
7245
7482
|
* Render an Input node as an `<ai-card-input>` Custom Element.
|
|
7246
7483
|
* Supports text/password/number/textarea, label, placeholder, validation.
|
|
7247
7484
|
*/
|
|
7248
|
-
function renderInput(node, props, isMobile) {
|
|
7485
|
+
function renderInput(node, props, isMobile, responsive) {
|
|
7249
7486
|
const el = document.createElement(CardInput.is);
|
|
7250
|
-
el.setData(node, props, isMobile);
|
|
7487
|
+
el.setData(node, props, isMobile, responsive);
|
|
7251
7488
|
return el;
|
|
7252
7489
|
}
|
|
7253
7490
|
/**
|
|
7254
7491
|
* Render an Image node as an `<ai-card-image>` Custom Element.
|
|
7255
7492
|
* Supports click-to-zoom lightbox preview.
|
|
7256
7493
|
*/
|
|
7257
|
-
function renderImage(node, props, isMobile) {
|
|
7494
|
+
function renderImage(node, props, isMobile, responsive) {
|
|
7258
7495
|
const el = document.createElement(CardImage.is);
|
|
7259
|
-
el.setData(node, props, isMobile);
|
|
7496
|
+
el.setData(node, props, isMobile, responsive);
|
|
7260
7497
|
return el;
|
|
7261
7498
|
}
|
|
7262
7499
|
/** Render a Divider node. */
|
|
7263
|
-
function renderDivider(node, props, isMobile) {
|
|
7500
|
+
function renderDivider(node, props, isMobile, responsive) {
|
|
7264
7501
|
const el = document.createElement(CardDivider.is);
|
|
7265
|
-
el.setData(node, props, isMobile);
|
|
7502
|
+
el.setData(node, props, isMobile, responsive);
|
|
7266
7503
|
return el;
|
|
7267
7504
|
}
|
|
7268
7505
|
/** Render a Rate node. */
|
|
7269
|
-
function renderRate(node, props, isMobile) {
|
|
7506
|
+
function renderRate(node, props, isMobile, responsive) {
|
|
7270
7507
|
const el = document.createElement(CardRate.is);
|
|
7271
|
-
el.setData(node, props, isMobile);
|
|
7508
|
+
el.setData(node, props, isMobile, responsive);
|
|
7272
7509
|
return el;
|
|
7273
7510
|
}
|
|
7274
7511
|
/** Render a Counter node. */
|
|
7275
|
-
function renderCounter(node, props, isMobile) {
|
|
7512
|
+
function renderCounter(node, props, isMobile, responsive) {
|
|
7276
7513
|
const el = document.createElement(CardCounter.is);
|
|
7277
|
-
el.setData(node, props, isMobile);
|
|
7514
|
+
el.setData(node, props, isMobile, responsive);
|
|
7278
7515
|
return el;
|
|
7279
7516
|
}
|
|
7280
7517
|
/** Render a Tag node. */
|
|
7281
|
-
function renderTag(node, props, isMobile) {
|
|
7518
|
+
function renderTag(node, props, isMobile, responsive) {
|
|
7282
7519
|
const el = document.createElement(CardTag.is);
|
|
7283
|
-
el.setData(node, props, isMobile);
|
|
7520
|
+
el.setData(node, props, isMobile, responsive);
|
|
7284
7521
|
return el;
|
|
7285
7522
|
}
|
|
7286
7523
|
/** Render a Select node. */
|
|
7287
|
-
function renderSelect(node, props, isMobile) {
|
|
7524
|
+
function renderSelect(node, props, isMobile, responsive) {
|
|
7288
7525
|
const el = document.createElement(CardSelect.is);
|
|
7289
|
-
el.setData(node, props, isMobile);
|
|
7526
|
+
el.setData(node, props, isMobile, responsive);
|
|
7290
7527
|
return el;
|
|
7291
7528
|
}
|
|
7292
7529
|
/** Render a PasscodeInput node. */
|
|
7293
|
-
function renderPasscodeInput(node, props, isMobile) {
|
|
7530
|
+
function renderPasscodeInput(node, props, isMobile, responsive) {
|
|
7294
7531
|
const el = document.createElement(CardPasscodeInput.is);
|
|
7295
|
-
el.setData(node, props, isMobile);
|
|
7532
|
+
el.setData(node, props, isMobile, responsive);
|
|
7296
7533
|
return el;
|
|
7297
7534
|
}
|
|
7298
7535
|
/** Render an Icon node. */
|
|
7299
|
-
function renderIcon(node, props, isMobile) {
|
|
7536
|
+
function renderIcon(node, props, isMobile, responsive) {
|
|
7300
7537
|
const el = document.createElement(CardIcon.is);
|
|
7301
|
-
el.setData(node, props, isMobile);
|
|
7538
|
+
el.setData(node, props, isMobile, responsive);
|
|
7302
7539
|
return el;
|
|
7303
7540
|
}
|
|
7304
7541
|
/** Render a Form node. */
|
|
7305
|
-
function renderForm(node, props, isMobile) {
|
|
7542
|
+
function renderForm(node, props, isMobile, responsive) {
|
|
7306
7543
|
const el = document.createElement(CardForm.is);
|
|
7307
|
-
el.setData(node, props, isMobile);
|
|
7544
|
+
el.setData(node, props, isMobile, responsive);
|
|
7308
7545
|
return el;
|
|
7309
7546
|
}
|
|
7310
7547
|
/** Render a Loading node. */
|
|
7311
|
-
function renderLoading(node, props, isMobile) {
|
|
7548
|
+
function renderLoading(node, props, isMobile, responsive) {
|
|
7312
7549
|
const el = document.createElement(CardLoading.is);
|
|
7313
|
-
el.setData(node, props, isMobile);
|
|
7550
|
+
el.setData(node, props, isMobile, responsive);
|
|
7314
7551
|
return el;
|
|
7315
7552
|
}
|
|
7316
7553
|
/** Render a Progress node. */
|
|
7317
|
-
function renderProgress(node, props, isMobile) {
|
|
7554
|
+
function renderProgress(node, props, isMobile, responsive) {
|
|
7318
7555
|
const el = document.createElement(CardProgress.is);
|
|
7319
|
-
el.setData(node, props, isMobile);
|
|
7556
|
+
el.setData(node, props, isMobile, responsive);
|
|
7320
7557
|
return el;
|
|
7321
7558
|
}
|
|
7322
7559
|
/** Render a Steps node. */
|
|
7323
|
-
function renderSteps(node, props, isMobile) {
|
|
7560
|
+
function renderSteps(node, props, isMobile, responsive) {
|
|
7324
7561
|
const el = document.createElement(CardSteps.is);
|
|
7325
|
-
el.setData(node, props, isMobile);
|
|
7562
|
+
el.setData(node, props, isMobile, responsive);
|
|
7326
7563
|
return el;
|
|
7327
7564
|
}
|
|
7328
7565
|
/**
|
|
7329
7566
|
* Render a Collapse node as an `<ai-card-collapse>` Custom Element.
|
|
7330
7567
|
* Expandable/collapsible panel for AI thinking content.
|
|
7331
7568
|
*/
|
|
7332
|
-
function renderCollapse(node, props, isMobile) {
|
|
7569
|
+
function renderCollapse(node, props, isMobile, responsive) {
|
|
7333
7570
|
const el = document.createElement(CardCollapse.is);
|
|
7334
|
-
el.setData(node, props, isMobile);
|
|
7571
|
+
el.setData(node, props, isMobile, responsive);
|
|
7335
7572
|
return el;
|
|
7336
7573
|
}
|
|
7337
7574
|
/**
|
|
7338
7575
|
* Render an Html node as an `<ai-card-html>` Custom Element.
|
|
7339
7576
|
* Model-emitted HTML fragments, allow-list sanitized before display.
|
|
7340
7577
|
*/
|
|
7341
|
-
function renderHtml(node, props, isMobile) {
|
|
7578
|
+
function renderHtml(node, props, isMobile, responsive) {
|
|
7342
7579
|
const el = document.createElement(CardHtml.is);
|
|
7343
|
-
el.setData(node, props, isMobile);
|
|
7580
|
+
el.setData(node, props, isMobile, responsive);
|
|
7344
7581
|
return el;
|
|
7345
7582
|
}
|
|
7346
7583
|
/**
|
|
7347
7584
|
* Render a DatePicker node as an `<ai-card-date-picker>` Custom Element.
|
|
7348
7585
|
* Trigger + popup calendar panel (vanilla-calendar-pro).
|
|
7349
7586
|
*/
|
|
7350
|
-
function renderDatePicker(node, props, isMobile) {
|
|
7587
|
+
function renderDatePicker(node, props, isMobile, responsive) {
|
|
7351
7588
|
const el = document.createElement(CardDatePicker.is);
|
|
7352
|
-
el.setData(node, props, isMobile);
|
|
7589
|
+
el.setData(node, props, isMobile, responsive);
|
|
7353
7590
|
return el;
|
|
7354
7591
|
}
|
|
7355
7592
|
/** Render an Audio node as an `<ai-card-audio>` Custom Element. */
|
|
7356
|
-
function renderAudio(node, props, isMobile) {
|
|
7593
|
+
function renderAudio(node, props, isMobile, responsive) {
|
|
7357
7594
|
const el = document.createElement(CardAudio.is);
|
|
7358
|
-
el.setData(node, props, isMobile);
|
|
7595
|
+
el.setData(node, props, isMobile, responsive);
|
|
7359
7596
|
return el;
|
|
7360
7597
|
}
|
|
7361
7598
|
/** Render a Video node as an `<ai-card-video>` Custom Element. */
|
|
7362
|
-
function renderVideo(node, props, isMobile) {
|
|
7599
|
+
function renderVideo(node, props, isMobile, responsive) {
|
|
7363
7600
|
const el = document.createElement(CardVideo.is);
|
|
7364
|
-
el.setData(node, props, isMobile);
|
|
7601
|
+
el.setData(node, props, isMobile, responsive);
|
|
7365
7602
|
return el;
|
|
7366
7603
|
}
|
|
7367
7604
|
/** Render a Switch node. */
|
|
7368
|
-
function renderSwitch(node, props, isMobile) {
|
|
7605
|
+
function renderSwitch(node, props, isMobile, responsive) {
|
|
7369
7606
|
const el = document.createElement(CardSwitch.is);
|
|
7370
|
-
el.setData(node, props, isMobile);
|
|
7607
|
+
el.setData(node, props, isMobile, responsive);
|
|
7371
7608
|
return el;
|
|
7372
7609
|
}
|
|
7373
7610
|
/** Render a ChoiceList node. */
|
|
7374
|
-
function renderChoiceList(node, props, isMobile) {
|
|
7611
|
+
function renderChoiceList(node, props, isMobile, responsive) {
|
|
7375
7612
|
const el = document.createElement(CardChoiceList.is);
|
|
7376
|
-
el.setData(node, props, isMobile);
|
|
7613
|
+
el.setData(node, props, isMobile, responsive);
|
|
7377
7614
|
return el;
|
|
7378
7615
|
}
|
|
7379
7616
|
/** Render a ChoiceItem node. */
|
|
7380
|
-
function renderChoiceItem(node, props, isMobile) {
|
|
7617
|
+
function renderChoiceItem(node, props, isMobile, responsive) {
|
|
7381
7618
|
const el = document.createElement(CardChoiceItem.is);
|
|
7382
|
-
el.setData(node, props, isMobile);
|
|
7619
|
+
el.setData(node, props, isMobile, responsive);
|
|
7383
7620
|
return el;
|
|
7384
7621
|
}
|
|
7385
7622
|
/**
|
|
@@ -7389,7 +7626,7 @@ function renderChoiceItem(node, props, isMobile) {
|
|
|
7389
7626
|
*
|
|
7390
7627
|
* Layout is handled by the slot system in slots.ts (called from renderCard).
|
|
7391
7628
|
*/
|
|
7392
|
-
function renderDefault(node, props, isMobile) {
|
|
7629
|
+
function renderDefault(node, props, isMobile, responsive) {
|
|
7393
7630
|
const div = document.createElement('div');
|
|
7394
7631
|
div.className = `card-element card-${node.type.toLowerCase()} ${isMobile ? 'card-mobile' : 'card-desktop'}`;
|
|
7395
7632
|
div.setAttribute('data-card-id', node.id);
|
|
@@ -7401,6 +7638,7 @@ function renderDefault(node, props, isMobile) {
|
|
|
7401
7638
|
if (resolvedStyle)
|
|
7402
7639
|
div.style.cssText += ';' + resolvedStyle;
|
|
7403
7640
|
}
|
|
7641
|
+
applyResponsiveStyles(div, createResponsiveContext(isMobile, responsive));
|
|
7404
7642
|
return div;
|
|
7405
7643
|
}
|
|
7406
7644
|
// ─── Registry ────────────────────────────────────────────────────
|
|
@@ -7545,7 +7783,7 @@ function renderBoundCard(container, schema, options) {
|
|
|
7545
7783
|
|| resolved === 1);
|
|
7546
7784
|
}
|
|
7547
7785
|
const renderer = (componentRenderers[node.type] ?? componentRenderers._default);
|
|
7548
|
-
const el = renderer(node, resolvedProps, isMobile);
|
|
7786
|
+
const el = renderer(node, resolvedProps, isMobile, options.responsive);
|
|
7549
7787
|
if (isDisabled) {
|
|
7550
7788
|
el.setAttribute('data-disabled', 'true');
|
|
7551
7789
|
el.style.background = '#F5F5F5';
|
|
@@ -7608,6 +7846,7 @@ function renderBoundCard(container, schema, options) {
|
|
|
7608
7846
|
htmlChild.style.setProperty('--card-disabled-color', '#C0C0C0');
|
|
7609
7847
|
});
|
|
7610
7848
|
}
|
|
7849
|
+
applyResponsiveStyles(el, createResponsiveContext(isMobile, options.responsive));
|
|
7611
7850
|
return el;
|
|
7612
7851
|
}
|
|
7613
7852
|
function prepareCandidate(draft) {
|
|
@@ -7636,9 +7875,13 @@ function renderBoundCard(container, schema, options) {
|
|
|
7636
7875
|
return selected;
|
|
7637
7876
|
}
|
|
7638
7877
|
function createLifecycleActionContext(node) {
|
|
7639
|
-
const writeLiveVariable = (key, value) => {
|
|
7878
|
+
const writeLiveVariable = (key, value, silent = false) => {
|
|
7640
7879
|
if (disposed)
|
|
7641
7880
|
return;
|
|
7881
|
+
if (silent) {
|
|
7882
|
+
writeDraftVariable(variables, key, cloneJsonData(value));
|
|
7883
|
+
return;
|
|
7884
|
+
}
|
|
7642
7885
|
updateVariables({ [key]: value });
|
|
7643
7886
|
};
|
|
7644
7887
|
return {
|
|
@@ -7652,7 +7895,9 @@ function renderBoundCard(container, schema, options) {
|
|
|
7652
7895
|
parameterResolver: node.bindingDialect === 'a2ui'
|
|
7653
7896
|
? createA2UIParameterResolver(variables, node.dataPath)
|
|
7654
7897
|
: undefined,
|
|
7655
|
-
variableWriter: (key, value) =>
|
|
7898
|
+
variableWriter: (key, value, options) => {
|
|
7899
|
+
writeLiveVariable(key, value, options.silent);
|
|
7900
|
+
},
|
|
7656
7901
|
botId: options.botId,
|
|
7657
7902
|
inflightRequests,
|
|
7658
7903
|
};
|
|
@@ -7962,7 +8207,7 @@ function renderStaticCard(container, schema, options) {
|
|
|
7962
8207
|
const mediaStates = captureMediaStates(container);
|
|
7963
8208
|
disposeChartsIn(container); // tear down old chart instances before clearing
|
|
7964
8209
|
container.innerHTML = '';
|
|
7965
|
-
const dom = renderNode(tree, variables, actionContext, isMobile, lifecycleManager, schemaActions);
|
|
8210
|
+
const dom = renderNode(tree, variables, actionContext, isMobile, options.responsive, lifecycleManager, schemaActions);
|
|
7966
8211
|
container.appendChild(dom);
|
|
7967
8212
|
restoreScrollPositions(container, scrollPositions);
|
|
7968
8213
|
restoreMediaStates(container, mediaStates);
|
|
@@ -8060,7 +8305,7 @@ function restoreMediaStates(root, states) {
|
|
|
8060
8305
|
});
|
|
8061
8306
|
}
|
|
8062
8307
|
// ─── Recursive Node Renderer ─────────────────────────────────────
|
|
8063
|
-
function renderNode(node, variables, actionContext, isMobile, lifecycleManager, schemaActions) {
|
|
8308
|
+
function renderNode(node, variables, actionContext, isMobile, responsive, lifecycleManager, schemaActions) {
|
|
8064
8309
|
// Check directives.visible
|
|
8065
8310
|
if (node.directives?.visible) {
|
|
8066
8311
|
const visibleExpr = node.directives.visible;
|
|
@@ -8092,7 +8337,7 @@ function renderNode(node, variables, actionContext, isMobile, lifecycleManager,
|
|
|
8092
8337
|
}
|
|
8093
8338
|
// Lookup component renderer
|
|
8094
8339
|
const renderer = componentRenderers[node.type] ?? componentRenderers['_default'];
|
|
8095
|
-
const el = renderer(node, resolvedProps, isMobile);
|
|
8340
|
+
const el = renderer(node, resolvedProps, isMobile, responsive);
|
|
8096
8341
|
// Apply disabled styling & attribute
|
|
8097
8342
|
if (isDisabled) {
|
|
8098
8343
|
el.setAttribute('data-disabled', 'true');
|
|
@@ -8154,7 +8399,7 @@ function renderNode(node, variables, actionContext, isMobile, lifecycleManager,
|
|
|
8154
8399
|
lifecycleManager.mount(node.id, actionContext);
|
|
8155
8400
|
}
|
|
8156
8401
|
// Render children — use slot layout if applicable, otherwise flat append
|
|
8157
|
-
const renderChild = (child) => renderNode(child, variables, actionContext, isMobile, lifecycleManager, schemaActions);
|
|
8402
|
+
const renderChild = (child) => renderNode(child, variables, actionContext, isMobile, responsive, lifecycleManager, schemaActions);
|
|
8158
8403
|
// Build children-by-id map for layouts that reference IDs (columns groups, float overlays)
|
|
8159
8404
|
const childrenMap = {};
|
|
8160
8405
|
for (const child of node.children) {
|
|
@@ -8175,6 +8420,7 @@ function renderNode(node, variables, actionContext, isMobile, lifecycleManager,
|
|
|
8175
8420
|
htmlChild.style.setProperty('--card-disabled-color', '#C0C0C0');
|
|
8176
8421
|
});
|
|
8177
8422
|
}
|
|
8423
|
+
applyResponsiveStyles(el, createResponsiveContext(isMobile, responsive));
|
|
8178
8424
|
return el;
|
|
8179
8425
|
}
|
|
8180
8426
|
/** Map schema event names → DOM event names */
|
|
@@ -8410,7 +8656,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8410
8656
|
indexes.props.set(node.id, boundNodeFingerprint(node, renderVariables, resolvedProps));
|
|
8411
8657
|
const isDisabled = computeBoundDisabled(node, renderVariables);
|
|
8412
8658
|
const renderer = (componentRenderers[node.type] ?? componentRenderers._default);
|
|
8413
|
-
const element = renderer(node, resolvedProps, isMobile);
|
|
8659
|
+
const element = renderer(node, resolvedProps, isMobile, options.responsive);
|
|
8414
8660
|
if (isDisabled) {
|
|
8415
8661
|
element.setAttribute('data-disabled', 'true');
|
|
8416
8662
|
element.style.background = '#F5F5F5';
|
|
@@ -8461,6 +8707,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8461
8707
|
element.appendChild(renderChild(child));
|
|
8462
8708
|
}
|
|
8463
8709
|
}
|
|
8710
|
+
applyResponsiveStyles(element, createResponsiveContext(isMobile, options.responsive));
|
|
8464
8711
|
return element;
|
|
8465
8712
|
}
|
|
8466
8713
|
/**
|
|
@@ -8502,7 +8749,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8502
8749
|
}
|
|
8503
8750
|
// Create element
|
|
8504
8751
|
const renderer = componentRenderers[node.type] ?? componentRenderers['_default'];
|
|
8505
|
-
const el = renderer(node, resolvedProps, isMobile);
|
|
8752
|
+
const el = renderer(node, resolvedProps, isMobile, options.responsive);
|
|
8506
8753
|
// Apply disabled
|
|
8507
8754
|
if (isDisabled) {
|
|
8508
8755
|
el.setAttribute('data-disabled', 'true');
|
|
@@ -8559,6 +8806,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8559
8806
|
el.appendChild(renderChild(child));
|
|
8560
8807
|
}
|
|
8561
8808
|
}
|
|
8809
|
+
applyResponsiveStyles(el, createResponsiveContext(isMobile, options.responsive));
|
|
8562
8810
|
return el;
|
|
8563
8811
|
}
|
|
8564
8812
|
// ─── Incremental Rendering Helpers ──────────────────────────────
|
|
@@ -8689,7 +8937,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
8689
8937
|
if (propsCache.get(id) === key)
|
|
8690
8938
|
continue;
|
|
8691
8939
|
if ('updateProps' in el) {
|
|
8692
|
-
el.updateProps(resolved, isMobile);
|
|
8940
|
+
el.updateProps(resolved, isMobile, options.responsive);
|
|
8693
8941
|
propsCache.set(id, key);
|
|
8694
8942
|
}
|
|
8695
8943
|
else if (!replaceSubtree(schema, id, el)) {
|
|
@@ -9651,6 +9899,21 @@ function renderStreamingCard(container, options = {}) {
|
|
|
9651
9899
|
// ─── Engine Event Handlers ──────────────────────────────────────
|
|
9652
9900
|
const engine = new StreamingEngine({
|
|
9653
9901
|
onSurfaceCreated(surfaceId, schemaInput) {
|
|
9902
|
+
if (schemaInput) {
|
|
9903
|
+
const nextSchema = engine.getSchema(surfaceId)
|
|
9904
|
+
?? normalizeSchema(schemaInput);
|
|
9905
|
+
if (requiresBindingMaterialization(nextSchema)) {
|
|
9906
|
+
const baseRevision = boundRevision;
|
|
9907
|
+
const draft = cloneJsonData({
|
|
9908
|
+
...nextSchema.variables,
|
|
9909
|
+
...options.variables,
|
|
9910
|
+
});
|
|
9911
|
+
const prepared = prepareBoundFull(nextSchema, cloneJsonData(draft));
|
|
9912
|
+
commitBoundFull(prepared, nextSchema, draft, baseRevision, () => replaceRootContents(nextSchema.variables, draft));
|
|
9913
|
+
currentSurfaceId = surfaceId;
|
|
9914
|
+
return;
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
9654
9917
|
teardownBoundLifecycles();
|
|
9655
9918
|
boundRevision += 1;
|
|
9656
9919
|
currentSurfaceId = surfaceId;
|
|
@@ -9840,7 +10103,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
9840
10103
|
continue;
|
|
9841
10104
|
const typeChanged = el.getAttribute('data-card-type') !== element.type;
|
|
9842
10105
|
if (!typeChanged && 'updateProps' in el) {
|
|
9843
|
-
el.updateProps(resolved, isMobile);
|
|
10106
|
+
el.updateProps(resolved, isMobile, options.responsive);
|
|
9844
10107
|
propsCache.set(id, key);
|
|
9845
10108
|
}
|
|
9846
10109
|
else if (!typeChanged && isChildrenOnlyChange(propsCache.get(id), key, element)) {
|
|
@@ -9950,7 +10213,7 @@ function renderStreamingCard(container, options = {}) {
|
|
|
9950
10213
|
const node = indexBoundNodes(nextMaterialized.root).get(elementId);
|
|
9951
10214
|
if (node && node.id === node.sourceId && 'updateProps' in el) {
|
|
9952
10215
|
const resolved = resolveBoundNodeProps(node, variables);
|
|
9953
|
-
el.updateProps(resolved, isMobile);
|
|
10216
|
+
el.updateProps(resolved, isMobile, options.responsive);
|
|
9954
10217
|
propsCache.set(elementId, boundNodeFingerprint(node, variables, resolved));
|
|
9955
10218
|
currentMaterialized = nextMaterialized;
|
|
9956
10219
|
boundRevision += 1;
|
|
@@ -10422,4 +10685,4 @@ class RemoteActionConfigProvider {
|
|
|
10422
10685
|
}
|
|
10423
10686
|
}
|
|
10424
10687
|
|
|
10425
|
-
export { BaseElement, BotSDK, CardButton, CardChoiceItem, CardChoiceList, CardCollapse, CardCounter, CardDivider, CardForm, CardHtml, CardIcon, CardImage, CardInput, CardLoading, CardPasscodeInput, CardProgress, CardRate, CardSelect, CardSteps, CardSwitch, CardTag, CardText, LocalActionConfigProvider, RemoteActionConfigProvider, buildStyleString, componentRenderers, connectSSE, connectStreaming, createWebActionContext, isMobileViewport, onViewportChange, pxToRem, pxToVw, registerComponent, renderCard, renderStreamingCard, resolveSize, sanitizeHtml, trimIncompleteTag };
|
|
10688
|
+
export { BaseElement, BotSDK, CardButton, CardChoiceItem, CardChoiceList, CardCollapse, CardCounter, CardDivider, CardForm, CardHtml, CardIcon, CardImage, CardInput, CardLoading, CardPasscodeInput, CardProgress, CardRate, CardSelect, CardSteps, CardSwitch, CardTag, CardText, DEFAULT_MOBILE_RESPONSIVE, LocalActionConfigProvider, RemoteActionConfigProvider, buildStyleString, componentRenderers, connectSSE, connectStreaming, convertPixelTokens, createResponsiveContext, createWebActionContext, isMobileViewport, onViewportChange, pxToRem, pxToVw, registerComponent, renderCard, renderStreamingCard, resolveSize, sanitizeHtml, trimIncompleteTag };
|