@open-feishu-card/renderer 0.3.4 → 0.3.6

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.cjs CHANGED
@@ -256,7 +256,7 @@ var HEADER_TEMPLATE = (() => {
256
256
  text: PALETTE[textException[n] || letter + "600"]
257
257
  };
258
258
  }
259
- out.default = { bg: "#f5f6f7", text: "#1f2329" };
259
+ out.default = { bg: "#ffffff", text: "#1f2329" };
260
260
  return out;
261
261
  })();
262
262
  var TEXT_FONT_COLOR_TOKEN = {
@@ -687,7 +687,8 @@ function resolveMediaSrc(p, kind, ctx) {
687
687
  return null;
688
688
  }
689
689
  function resolveVideoCoverSrc(p, ctx) {
690
- const coverId = p.coverImageID ?? p.image_key ?? p.cover_image_key;
690
+ const cover = p.cover;
691
+ const coverId = p.coverImageID ?? p.image_key ?? p.cover_image_key ?? (typeof cover === "string" ? cover : cover?.img_key ?? cover?.imageID ?? cover?.image_id ?? cover?.cover_image_id ?? cover?.coverImageId);
691
692
  if (!coverId) return null;
692
693
  return resolveImageSrc({ imageID: coverId }, ctx);
693
694
  }
@@ -997,6 +998,9 @@ var BASE_STYLES_CSS = `/**
997
998
  width: 100%;
998
999
  max-width: 605px;
999
1000
  box-sizing: border-box;
1001
+ /* \u771F\u673A\u6309\u300C\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px\u300D\u533A\u5206\u5BBD/\u7A84\u5C4F\u6309\u94AE\u6863\u4F4D\uFF0C\u8FD9\u91CC\u7528\u5BB9\u5668\u67E5\u8BE2\u590D\u523B */
1002
+ container-type: inline-size;
1003
+ container-name: universal-card;
1000
1004
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
1001
1005
  /* \u6B63\u6587 color:var(--O400) \u7B49\u4F9D\u8D56\u8FD9\u4E9B\u53D8\u91CF\uFF1B\u4E0E\u98DE\u4E66 :root light \u8272\u9636\u5BF9\u9F50 */
1002
1006
  --B50: #f0f4ff;
@@ -1179,6 +1183,8 @@ var BASE_STYLES_CSS = `/**
1179
1183
  /* \u5BF9\u9F50\u98DE\u4E66\uFF1Amax(100%, 605px) 140px\uFF0C\u7A84\u5361\u4E0D\u628A\u9AD8\u5149\u62C9\u7A00 */
1180
1184
  background-size: max(100%, 605px) 140px;
1181
1185
  }
1186
+ /* default/disabled \u6A21\u677F\u771F\u673A\u4E0D\u8BBE\u80CC\u666F\u8272\uFF08bg_color \u4E3A\u7A7A\u4E32\uFF09\uFF0C\u6B64\u65F6 header \u4E0D\u7559\u5E95\u90E8\u5185\u8FB9\u8DDD */
1187
+ .universal-card-header__bg--no-template { padding-bottom: 0; }
1182
1188
  /* \u5185\u5BB9\u884C\uFF1A\u5DE6\u4FA7\u56FE\u6807 + \u53F3\u4FA7\u6807\u9898\u533A\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 header__content = row\uFF09 */
1183
1189
  .universal-card-header__content {
1184
1190
  width: 100%;
@@ -1245,7 +1251,9 @@ var BASE_STYLES_CSS = `/**
1245
1251
  column-gap: 12px;
1246
1252
  align-items: flex-start;
1247
1253
  }
1248
- .universal-card-body > * { width: 100%; box-sizing: border-box; }
1254
+ /* \u4EA4\u4E92\u63A7\u4EF6\u4E0D\u53C2\u4E0E\u300C\u5B50\u5143\u7D20\u6491\u6EE1\u300D\uFF1A\u6309\u94AE\u7684\u5BBD\u5EA6\u7531\u771F\u673A\u89C4\u5219\u51B3\u5B9A\uFF08\u89C1 .universal-card-button--auto\uFF09\uFF0C
1255
+ \u82E5\u88AB\u8FD9\u6761 blanket \u89C4\u5219\u62C9\u5230 100%\uFF0C\u7A84\u5C4F/\u5BBD\u5C4F\u5206\u6863\u5C31\u5931\u6548\u4E86\u3002 */
1256
+ .universal-card-body > *:not(.universal-card-button) { width: 100%; box-sizing: border-box; }
1249
1257
 
1250
1258
  /* ---- markdown \u6BB5\u843D ---- */
1251
1259
  /* \u5BF9\u9F50\u7F51\u9875\u7248\uFF1Aline-height \u9ED8\u8BA4 1.6\uFF1B\u5177\u4F53\u5B57\u53F7/\u884C\u9AD8\u7531\u7EC4\u4EF6\u5185\u8054 style \u6309 text_size \u7ED9\u51FA */
@@ -1365,10 +1373,14 @@ var BASE_STYLES_CSS = `/**
1365
1373
  flex-shrink: 0; height: 1.2em; color: inherit;
1366
1374
  }
1367
1375
  .universal-card-button__icon .universal-card-icon { color: inherit; }
1368
- /* \u5BBD\u5EA6\uFF1A\u5BF9\u9F50\u771F\u673A\u2014\u2014body > * { width:100% }\u3001\u5217 flex stretch \u4F1A\u628A\u6309\u94AE\u6491\u6EE1\u6240\u5728\u884C/\u5217\u3002
1369
- \u4E0D\u518D\u5F3A\u5236 width:auto\uFF08\u90A3\u662F\u7F51\u9875\u7248\u89C2\u611F\uFF1B\u771F\u673A\u72EC\u7ACB\u6210\u884C\u7684\u6309\u94AE\u9ED8\u8BA4\u5360\u6EE1\u6574\u884C\uFF09\u3002
1370
- width:"fill" \u663E\u5F0F\u6302 --block\uFF0C\u4E0E\u7F3A\u7701\u6491\u6EE1\u540C\u5BBD\uFF0C\u5E76\u4F9B margin calc \u8BC6\u522B\u3002 */
1371
- .universal-card-button--block { width: 100%; }
1376
+ /* \u5BBD\u5EA6\uFF1A\u5BF9\u9F50\u771F\u673A ud__button \u7684 isWide \u5206\u6863\uFF08\u771F\u673A\u6309\u300C\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px\u300D\u5224\u5B9A\uFF09\uFF1A
1377
+ \u5BBD\u5C4F \u2192 \u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\uFF08--auto\uFF09\uFF1B\u7A84\u5C4F\uFF08\u2264320px\uFF09\u2192 \u6491\u6EE1\u6574\u884C\uFF08\u4E0B\u65B9 @container \u89C4\u5219\u8986\u76D6\uFF09\u3002
1378
+ text \u65CF\u4E0E width:"fill" \u6052\u5B9A\u6491\u6EE1\uFF08--block\uFF09\u3002
1379
+ align-self:flex-start \u2014\u2014 \u7236\u7EA7\uFF08body/form/column\uFF09\u9ED8\u8BA4 align-items:stretch\uFF0C
1380
+ \u53EA\u5199 width:auto \u4ECD\u4F1A\u88AB\u62C9\u4F38\uFF0C\u5FC5\u987B\u540C\u65F6\u963B\u6B62\u62C9\u4F38\u3002 */
1381
+ .universal-card-button { min-width: 58px; align-self: flex-start; }
1382
+ .universal-card-button--block { width: 100%; min-width: unset; }
1383
+ .universal-card-button--auto { width: auto; }
1372
1384
 
1373
1385
  /* \u2014\u2014 outlined \u63CF\u8FB9\u65CF \u2014\u2014 */
1374
1386
  /* default\uFF1A\u7070\u63CF\u8FB9 + \u767D\u5E95 + \u6DF1\u5B57\uFF08\u672A\u52A0\u53D8\u4F53\u7C7B\u7684\u88F8 .universal-card-button \u5DF2\u662F\u767D\u5E95\u6DF1\u5B57\uFF0C
@@ -1395,6 +1407,8 @@ var BASE_STYLES_CSS = `/**
1395
1407
  .universal-card-button--danger_text {
1396
1408
  border-color: transparent; background: transparent; padding: 4px 8px;
1397
1409
  justify-content: flex-start;
1410
+ /* \u771F\u673A text \u65CF\u6309\u94AE\u4E0D\u8BBE\u6700\u5C0F\u5BBD\u5EA6 */
1411
+ min-width: unset;
1398
1412
  }
1399
1413
  .universal-card-button--text { color: #1f2329; }
1400
1414
  .universal-card-button--primary_text { color: #1456f0; }
@@ -1430,6 +1444,8 @@ var BASE_STYLES_CSS = `/**
1430
1444
  .universal-card-overflow--static { display: inline-flex; }
1431
1445
  .universal-card-overflow__summary {
1432
1446
  list-style: none; cursor: pointer; display: inline-flex;
1447
+ /* \u771F\u673A\u6298\u53E0\u6309\u94AE\u7684\u6700\u5C0F\u5BBD\u5EA6\u662F 32px\uFF08\u666E\u901A\u6309\u94AE\u662F 58px\uFF09 */
1448
+ min-width: 32px;
1433
1449
  }
1434
1450
  /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1435
1451
  .universal-card-overflow__summary::-webkit-details-marker { display: none; }
@@ -1451,8 +1467,9 @@ var BASE_STYLES_CSS = `/**
1451
1467
  .universal-card-overflow__item:hover { background: #f5f6f7; }
1452
1468
 
1453
1469
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1454
- .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1455
- .universal-card-form > * { width: 100%; }
1470
+ /* row-gap 12px\uFF1A\u5BF9\u9F50\u771F\u673A schema 2.0 \u8868\u5355\u5BB9\u5668 */
1471
+ .universal-card-form { display: flex; flex-direction: column; row-gap: 12px; }
1472
+ .universal-card-form > *:not(.universal-card-button) { width: 100%; }
1456
1473
  /* input / select\uFF1A\u9759\u6001\u9884\u89C8\u662F\u53EA\u8BFB\u6846\uFF1Binteractive \u53BB\u6389 --static\uFF0C\u6210\u4E3A\u53EF\u8F93\u5165\u63A7\u4EF6 */
1457
1474
  .universal-card-input,
1458
1475
  .universal-card-input--static {
@@ -1460,6 +1477,13 @@ var BASE_STYLES_CSS = `/**
1460
1477
  font-family: inherit; font-size: 14px; line-height: 20px;
1461
1478
  background: #fff; border: 1px solid #dee0e3; border-radius: 6px;
1462
1479
  color: #1f2329; width: 100%;
1480
+ /* \u771F\u673A input \u9ED8\u8BA4 max-width:282px\uFF08width:"fill" \u65F6\u7531\u5185\u8054\u6837\u5F0F\u653E\u5F00\uFF09 */
1481
+ max-width: 282px;
1482
+ }
1483
+ /* select type:"text"\uFF1A\u771F\u673A\u662F\u65E0\u8FB9\u6846\u3001\u5185\u5BB9\u81EA\u9002\u5E94\u5BBD\u7684\u4E0B\u62C9 */
1484
+ .universal-card-input--text {
1485
+ border-color: transparent; background: transparent;
1486
+ min-height: 26px; padding: 1px 7px; width: auto;
1463
1487
  }
1464
1488
  .universal-card-input--static {
1465
1489
  color: #8f959e; background: #f5f6f7;
@@ -2127,6 +2151,13 @@ var BASE_STYLES_CSS = `/**
2127
2151
  from { opacity: 1; transform: translateY(0); }
2128
2152
  to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
2129
2153
  }
2154
+
2155
+ /* ---- \u7A84\u5C4F\u5206\u6863\uFF08\u771F\u673A isWide\uFF1A\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px \u4E3A\u5BBD\u5C4F\uFF09 ----
2156
+ \u771F\u673A\u89C4\u5219\uFF1Adefault/primary/primary_filled/danger/danger_filled/laser \u6309\u94AE\u5728\u7A84\u5C4F\u6491\u6EE1\u6574\u884C\uFF0C
2157
+ \u5BBD\u5C4F\u5219\u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\u3002@container \u4E0D\u589E\u52A0\u9009\u62E9\u5668\u6743\u91CD\uFF0C\u9760\u6E90\u7801\u987A\u5E8F\u5728 --auto \u89C4\u5219\u4E4B\u540E\u8986\u76D6\u3002 */
2158
+ @container universal-card (max-width: 320px) {
2159
+ .universal-card-button--auto { width: 100%; }
2160
+ }
2130
2161
  `;
2131
2162
 
2132
2163
  // src/icon-font.ts
@@ -5694,6 +5725,8 @@ function registerBuiltins() {
5694
5725
  ),
5695
5726
  subtitleVNode
5696
5727
  ];
5728
+ const tplKey2 = p.template || "default";
5729
+ const noTemplate = tplKey2 === "default" || tplKey2 === "disabled";
5697
5730
  return h(
5698
5731
  "div",
5699
5732
  {
@@ -5703,7 +5736,7 @@ function registerBuiltins() {
5703
5736
  h(
5704
5737
  "div",
5705
5738
  {
5706
- className: `${CLS}-header__bg`,
5739
+ className: `${CLS}-header__bg${noTemplate ? ` ${CLS}-header__bg--no-template` : ""}`,
5707
5740
  // 内联 base64 高光纹理;图宽=header 宽使亮带居中;padding 透传 DSL(默认 12px)
5708
5741
  style: (() => {
5709
5742
  const s = { backgroundImage: `url(${HEADER_BG_LIGHT})` };
@@ -6236,12 +6269,26 @@ function registerBuiltins() {
6236
6269
  const url = actionUrl || resolveURL(p.url) || p.behaviors?.[0]?.default_url?.url;
6237
6270
  const wv = p.widthValue;
6238
6271
  const widthTok = p.width ?? wv?.value;
6239
- const block = widthTok === "fill";
6240
- const base = `${CLS}-button ${CLS}-button--${type}${block ? ` ${CLS}-button--block` : ""}`;
6272
+ const isFill = widthTok === "fill";
6273
+ const pxWidth = /^\d+(\.\d+)?px$/i.test(String(widthTok ?? "")) ? String(widthTok) : "";
6274
+ const textFamily = type === "text" || type === "primary_text" || type === "danger_text";
6275
+ const block = isFill || textFamily;
6276
+ const auto = !block && !pxWidth;
6277
+ const base = `${CLS}-button ${CLS}-button--${type}` + (block ? ` ${CLS}-button--block` : "") + (auto ? ` ${CLS}-button--auto` : "");
6241
6278
  const style = {};
6242
- if (p.margin) {
6243
- style.width = "100%";
6244
- applyOuterMargin(style, p.margin);
6279
+ if (pxWidth) {
6280
+ style.width = pxWidth;
6281
+ if (p.margin) applyOuterMargin(style, p.margin);
6282
+ } else if (p.margin) {
6283
+ if (block) {
6284
+ style.width = "100%";
6285
+ applyOuterMargin(style, p.margin);
6286
+ } else {
6287
+ applyOuterMargin(style, p.margin);
6288
+ const { left, right } = horizontalMarginPx(p.margin);
6289
+ const inset = left + right;
6290
+ if (inset > 0) style.maxWidth = `calc(100% - ${inset}px)`;
6291
+ }
6245
6292
  }
6246
6293
  const styleProp = Object.keys(style).length ? { style } : {};
6247
6294
  const extra = p;
@@ -6322,15 +6369,53 @@ function registerBuiltins() {
6322
6369
  const name = typeof el.property?.name === "string" ? el.property.name : "";
6323
6370
  return h("form", { className, "data-feishu-form": name }, children);
6324
6371
  });
6372
+ function fieldWidthStyle(p, kind) {
6373
+ const wv = p.widthValue;
6374
+ const raw = String(p.width ?? wv?.value ?? "");
6375
+ const style = {};
6376
+ if (/^\d+(\.\d+)?px$/i.test(raw)) {
6377
+ style.width = raw;
6378
+ style.maxWidth = "100%";
6379
+ return style;
6380
+ }
6381
+ if (raw === "fill") {
6382
+ style.width = "100%";
6383
+ style.maxWidth = "none";
6384
+ return style;
6385
+ }
6386
+ if (kind === "select") {
6387
+ if (raw === "auto") {
6388
+ style.width = "100%";
6389
+ style.maxWidth = "100%";
6390
+ return style;
6391
+ }
6392
+ if (p.type === "text") {
6393
+ style.width = "auto";
6394
+ style.maxWidth = "100%";
6395
+ return style;
6396
+ }
6397
+ style.width = "282px";
6398
+ style.maxWidth = "100%";
6399
+ return style;
6400
+ }
6401
+ style.width = "100%";
6402
+ style.maxWidth = "282px";
6403
+ return style;
6404
+ }
6325
6405
  function renderStaticField(el) {
6326
6406
  const p = el.property || {};
6327
6407
  const ph = extractPlainText(p.placeholder) || "";
6328
- return h("div", { className: `${CLS}-input ${CLS}-input--static` }, ph);
6408
+ const kind = isSelectTag(el.tag) ? "select" : "input";
6409
+ const cls = `${CLS}-input ${CLS}-input--static` + (kind === "select" && p.type === "text" ? ` ${CLS}-input--text` : "");
6410
+ return h("div", { className: cls, style: fieldWidthStyle(p, kind) }, ph);
6329
6411
  }
6330
6412
  function fieldText(value) {
6331
6413
  if (typeof value === "string") return value;
6332
6414
  return extractPlainText(value);
6333
6415
  }
6416
+ function isSelectTag(tag) {
6417
+ return tag === "select_static" || tag === "multi_select_static" || tag === "select_person" || tag === "multi_select_person";
6418
+ }
6334
6419
  function renderInteractiveField(el) {
6335
6420
  const p = el.property || {};
6336
6421
  const ph = extractPlainText(p.placeholder) || "";
@@ -6338,13 +6423,15 @@ function registerBuiltins() {
6338
6423
  const nameProp = name ? { name } : {};
6339
6424
  const callback = readCallbackValue(p);
6340
6425
  const actionProp = callback ? { "data-feishu-action": JSON.stringify(callback), "data-feishu-tag": el.tag } : {};
6341
- if (el.tag === "select_static" || el.tag === "multi_select_static") {
6426
+ if (isSelectTag(el.tag)) {
6342
6427
  const initial = p.initial_option ?? p.initialOption;
6343
6428
  const options = Array.isArray(p.options) ? p.options : [];
6429
+ const cls = `${CLS}-input` + (p.type === "text" ? ` ${CLS}-input--text` : "");
6344
6430
  return h("select", {
6345
- className: `${CLS}-input`,
6431
+ className: cls,
6432
+ style: fieldWidthStyle(p, "select"),
6346
6433
  ...nameProp,
6347
- ...el.tag === "multi_select_static" ? { multiple: true } : {},
6434
+ ...el.tag === "multi_select_static" || el.tag === "multi_select_person" ? { multiple: true } : {},
6348
6435
  ...actionProp
6349
6436
  }, [
6350
6437
  h("option", { value: "" }, ph),
@@ -6363,6 +6450,7 @@ function registerBuiltins() {
6363
6450
  const inputType = el.tag === "date_picker" ? "date" : el.tag === "picker_time" ? "time" : "text";
6364
6451
  return h("input", {
6365
6452
  className: `${CLS}-input`,
6453
+ style: fieldWidthStyle(p, "input"),
6366
6454
  type: inputType,
6367
6455
  placeholder: ph,
6368
6456
  value: typeof def === "string" ? def : "",
package/dist/index.d.cts CHANGED
@@ -100,6 +100,14 @@ interface ElementProperty {
100
100
  coverImageID?: string;
101
101
  image_key?: string;
102
102
  cover_image_key?: string;
103
+ /** 公开 DSL 官方封面形态 cover: { img_key };部分 RAW 嵌套 imageID / image_id 等 */
104
+ cover?: string | {
105
+ img_key?: string;
106
+ imageID?: string;
107
+ image_id?: string;
108
+ cover_image_id?: string;
109
+ coverImageId?: string;
110
+ };
103
111
  /** 是否显示时长(视频角标 / 音频时间) */
104
112
  showTime?: boolean;
105
113
  show_time?: boolean;
package/dist/index.d.ts CHANGED
@@ -100,6 +100,14 @@ interface ElementProperty {
100
100
  coverImageID?: string;
101
101
  image_key?: string;
102
102
  cover_image_key?: string;
103
+ /** 公开 DSL 官方封面形态 cover: { img_key };部分 RAW 嵌套 imageID / image_id 等 */
104
+ cover?: string | {
105
+ img_key?: string;
106
+ imageID?: string;
107
+ image_id?: string;
108
+ cover_image_id?: string;
109
+ coverImageId?: string;
110
+ };
103
111
  /** 是否显示时长(视频角标 / 音频时间) */
104
112
  showTime?: boolean;
105
113
  show_time?: boolean;
package/dist/index.js CHANGED
@@ -255,7 +255,7 @@ var HEADER_TEMPLATE = (() => {
255
255
  text: PALETTE[textException[n] || letter + "600"]
256
256
  };
257
257
  }
258
- out.default = { bg: "#f5f6f7", text: "#1f2329" };
258
+ out.default = { bg: "#ffffff", text: "#1f2329" };
259
259
  return out;
260
260
  })();
261
261
  var TEXT_FONT_COLOR_TOKEN = {
@@ -686,7 +686,8 @@ function resolveMediaSrc(p, kind, ctx) {
686
686
  return null;
687
687
  }
688
688
  function resolveVideoCoverSrc(p, ctx) {
689
- const coverId = p.coverImageID ?? p.image_key ?? p.cover_image_key;
689
+ const cover = p.cover;
690
+ const coverId = p.coverImageID ?? p.image_key ?? p.cover_image_key ?? (typeof cover === "string" ? cover : cover?.img_key ?? cover?.imageID ?? cover?.image_id ?? cover?.cover_image_id ?? cover?.coverImageId);
690
691
  if (!coverId) return null;
691
692
  return resolveImageSrc({ imageID: coverId }, ctx);
692
693
  }
@@ -996,6 +997,9 @@ var BASE_STYLES_CSS = `/**
996
997
  width: 100%;
997
998
  max-width: 605px;
998
999
  box-sizing: border-box;
1000
+ /* \u771F\u673A\u6309\u300C\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px\u300D\u533A\u5206\u5BBD/\u7A84\u5C4F\u6309\u94AE\u6863\u4F4D\uFF0C\u8FD9\u91CC\u7528\u5BB9\u5668\u67E5\u8BE2\u590D\u523B */
1001
+ container-type: inline-size;
1002
+ container-name: universal-card;
999
1003
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
1000
1004
  /* \u6B63\u6587 color:var(--O400) \u7B49\u4F9D\u8D56\u8FD9\u4E9B\u53D8\u91CF\uFF1B\u4E0E\u98DE\u4E66 :root light \u8272\u9636\u5BF9\u9F50 */
1001
1005
  --B50: #f0f4ff;
@@ -1178,6 +1182,8 @@ var BASE_STYLES_CSS = `/**
1178
1182
  /* \u5BF9\u9F50\u98DE\u4E66\uFF1Amax(100%, 605px) 140px\uFF0C\u7A84\u5361\u4E0D\u628A\u9AD8\u5149\u62C9\u7A00 */
1179
1183
  background-size: max(100%, 605px) 140px;
1180
1184
  }
1185
+ /* default/disabled \u6A21\u677F\u771F\u673A\u4E0D\u8BBE\u80CC\u666F\u8272\uFF08bg_color \u4E3A\u7A7A\u4E32\uFF09\uFF0C\u6B64\u65F6 header \u4E0D\u7559\u5E95\u90E8\u5185\u8FB9\u8DDD */
1186
+ .universal-card-header__bg--no-template { padding-bottom: 0; }
1181
1187
  /* \u5185\u5BB9\u884C\uFF1A\u5DE6\u4FA7\u56FE\u6807 + \u53F3\u4FA7\u6807\u9898\u533A\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 header__content = row\uFF09 */
1182
1188
  .universal-card-header__content {
1183
1189
  width: 100%;
@@ -1244,7 +1250,9 @@ var BASE_STYLES_CSS = `/**
1244
1250
  column-gap: 12px;
1245
1251
  align-items: flex-start;
1246
1252
  }
1247
- .universal-card-body > * { width: 100%; box-sizing: border-box; }
1253
+ /* \u4EA4\u4E92\u63A7\u4EF6\u4E0D\u53C2\u4E0E\u300C\u5B50\u5143\u7D20\u6491\u6EE1\u300D\uFF1A\u6309\u94AE\u7684\u5BBD\u5EA6\u7531\u771F\u673A\u89C4\u5219\u51B3\u5B9A\uFF08\u89C1 .universal-card-button--auto\uFF09\uFF0C
1254
+ \u82E5\u88AB\u8FD9\u6761 blanket \u89C4\u5219\u62C9\u5230 100%\uFF0C\u7A84\u5C4F/\u5BBD\u5C4F\u5206\u6863\u5C31\u5931\u6548\u4E86\u3002 */
1255
+ .universal-card-body > *:not(.universal-card-button) { width: 100%; box-sizing: border-box; }
1248
1256
 
1249
1257
  /* ---- markdown \u6BB5\u843D ---- */
1250
1258
  /* \u5BF9\u9F50\u7F51\u9875\u7248\uFF1Aline-height \u9ED8\u8BA4 1.6\uFF1B\u5177\u4F53\u5B57\u53F7/\u884C\u9AD8\u7531\u7EC4\u4EF6\u5185\u8054 style \u6309 text_size \u7ED9\u51FA */
@@ -1364,10 +1372,14 @@ var BASE_STYLES_CSS = `/**
1364
1372
  flex-shrink: 0; height: 1.2em; color: inherit;
1365
1373
  }
1366
1374
  .universal-card-button__icon .universal-card-icon { color: inherit; }
1367
- /* \u5BBD\u5EA6\uFF1A\u5BF9\u9F50\u771F\u673A\u2014\u2014body > * { width:100% }\u3001\u5217 flex stretch \u4F1A\u628A\u6309\u94AE\u6491\u6EE1\u6240\u5728\u884C/\u5217\u3002
1368
- \u4E0D\u518D\u5F3A\u5236 width:auto\uFF08\u90A3\u662F\u7F51\u9875\u7248\u89C2\u611F\uFF1B\u771F\u673A\u72EC\u7ACB\u6210\u884C\u7684\u6309\u94AE\u9ED8\u8BA4\u5360\u6EE1\u6574\u884C\uFF09\u3002
1369
- width:"fill" \u663E\u5F0F\u6302 --block\uFF0C\u4E0E\u7F3A\u7701\u6491\u6EE1\u540C\u5BBD\uFF0C\u5E76\u4F9B margin calc \u8BC6\u522B\u3002 */
1370
- .universal-card-button--block { width: 100%; }
1375
+ /* \u5BBD\u5EA6\uFF1A\u5BF9\u9F50\u771F\u673A ud__button \u7684 isWide \u5206\u6863\uFF08\u771F\u673A\u6309\u300C\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px\u300D\u5224\u5B9A\uFF09\uFF1A
1376
+ \u5BBD\u5C4F \u2192 \u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\uFF08--auto\uFF09\uFF1B\u7A84\u5C4F\uFF08\u2264320px\uFF09\u2192 \u6491\u6EE1\u6574\u884C\uFF08\u4E0B\u65B9 @container \u89C4\u5219\u8986\u76D6\uFF09\u3002
1377
+ text \u65CF\u4E0E width:"fill" \u6052\u5B9A\u6491\u6EE1\uFF08--block\uFF09\u3002
1378
+ align-self:flex-start \u2014\u2014 \u7236\u7EA7\uFF08body/form/column\uFF09\u9ED8\u8BA4 align-items:stretch\uFF0C
1379
+ \u53EA\u5199 width:auto \u4ECD\u4F1A\u88AB\u62C9\u4F38\uFF0C\u5FC5\u987B\u540C\u65F6\u963B\u6B62\u62C9\u4F38\u3002 */
1380
+ .universal-card-button { min-width: 58px; align-self: flex-start; }
1381
+ .universal-card-button--block { width: 100%; min-width: unset; }
1382
+ .universal-card-button--auto { width: auto; }
1371
1383
 
1372
1384
  /* \u2014\u2014 outlined \u63CF\u8FB9\u65CF \u2014\u2014 */
1373
1385
  /* default\uFF1A\u7070\u63CF\u8FB9 + \u767D\u5E95 + \u6DF1\u5B57\uFF08\u672A\u52A0\u53D8\u4F53\u7C7B\u7684\u88F8 .universal-card-button \u5DF2\u662F\u767D\u5E95\u6DF1\u5B57\uFF0C
@@ -1394,6 +1406,8 @@ var BASE_STYLES_CSS = `/**
1394
1406
  .universal-card-button--danger_text {
1395
1407
  border-color: transparent; background: transparent; padding: 4px 8px;
1396
1408
  justify-content: flex-start;
1409
+ /* \u771F\u673A text \u65CF\u6309\u94AE\u4E0D\u8BBE\u6700\u5C0F\u5BBD\u5EA6 */
1410
+ min-width: unset;
1397
1411
  }
1398
1412
  .universal-card-button--text { color: #1f2329; }
1399
1413
  .universal-card-button--primary_text { color: #1456f0; }
@@ -1429,6 +1443,8 @@ var BASE_STYLES_CSS = `/**
1429
1443
  .universal-card-overflow--static { display: inline-flex; }
1430
1444
  .universal-card-overflow__summary {
1431
1445
  list-style: none; cursor: pointer; display: inline-flex;
1446
+ /* \u771F\u673A\u6298\u53E0\u6309\u94AE\u7684\u6700\u5C0F\u5BBD\u5EA6\u662F 32px\uFF08\u666E\u901A\u6309\u94AE\u662F 58px\uFF09 */
1447
+ min-width: 32px;
1432
1448
  }
1433
1449
  /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1434
1450
  .universal-card-overflow__summary::-webkit-details-marker { display: none; }
@@ -1450,8 +1466,9 @@ var BASE_STYLES_CSS = `/**
1450
1466
  .universal-card-overflow__item:hover { background: #f5f6f7; }
1451
1467
 
1452
1468
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1453
- .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1454
- .universal-card-form > * { width: 100%; }
1469
+ /* row-gap 12px\uFF1A\u5BF9\u9F50\u771F\u673A schema 2.0 \u8868\u5355\u5BB9\u5668 */
1470
+ .universal-card-form { display: flex; flex-direction: column; row-gap: 12px; }
1471
+ .universal-card-form > *:not(.universal-card-button) { width: 100%; }
1455
1472
  /* input / select\uFF1A\u9759\u6001\u9884\u89C8\u662F\u53EA\u8BFB\u6846\uFF1Binteractive \u53BB\u6389 --static\uFF0C\u6210\u4E3A\u53EF\u8F93\u5165\u63A7\u4EF6 */
1456
1473
  .universal-card-input,
1457
1474
  .universal-card-input--static {
@@ -1459,6 +1476,13 @@ var BASE_STYLES_CSS = `/**
1459
1476
  font-family: inherit; font-size: 14px; line-height: 20px;
1460
1477
  background: #fff; border: 1px solid #dee0e3; border-radius: 6px;
1461
1478
  color: #1f2329; width: 100%;
1479
+ /* \u771F\u673A input \u9ED8\u8BA4 max-width:282px\uFF08width:"fill" \u65F6\u7531\u5185\u8054\u6837\u5F0F\u653E\u5F00\uFF09 */
1480
+ max-width: 282px;
1481
+ }
1482
+ /* select type:"text"\uFF1A\u771F\u673A\u662F\u65E0\u8FB9\u6846\u3001\u5185\u5BB9\u81EA\u9002\u5E94\u5BBD\u7684\u4E0B\u62C9 */
1483
+ .universal-card-input--text {
1484
+ border-color: transparent; background: transparent;
1485
+ min-height: 26px; padding: 1px 7px; width: auto;
1462
1486
  }
1463
1487
  .universal-card-input--static {
1464
1488
  color: #8f959e; background: #f5f6f7;
@@ -2126,6 +2150,13 @@ var BASE_STYLES_CSS = `/**
2126
2150
  from { opacity: 1; transform: translateY(0); }
2127
2151
  to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
2128
2152
  }
2153
+
2154
+ /* ---- \u7A84\u5C4F\u5206\u6863\uFF08\u771F\u673A isWide\uFF1A\u5361\u7247\u5BB9\u5668\u5BBD\u5EA6 > 320px \u4E3A\u5BBD\u5C4F\uFF09 ----
2155
+ \u771F\u673A\u89C4\u5219\uFF1Adefault/primary/primary_filled/danger/danger_filled/laser \u6309\u94AE\u5728\u7A84\u5C4F\u6491\u6EE1\u6574\u884C\uFF0C
2156
+ \u5BBD\u5C4F\u5219\u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\u3002@container \u4E0D\u589E\u52A0\u9009\u62E9\u5668\u6743\u91CD\uFF0C\u9760\u6E90\u7801\u987A\u5E8F\u5728 --auto \u89C4\u5219\u4E4B\u540E\u8986\u76D6\u3002 */
2157
+ @container universal-card (max-width: 320px) {
2158
+ .universal-card-button--auto { width: 100%; }
2159
+ }
2129
2160
  `;
2130
2161
 
2131
2162
  // src/icon-font.ts
@@ -5693,6 +5724,8 @@ function registerBuiltins() {
5693
5724
  ),
5694
5725
  subtitleVNode
5695
5726
  ];
5727
+ const tplKey2 = p.template || "default";
5728
+ const noTemplate = tplKey2 === "default" || tplKey2 === "disabled";
5696
5729
  return h(
5697
5730
  "div",
5698
5731
  {
@@ -5702,7 +5735,7 @@ function registerBuiltins() {
5702
5735
  h(
5703
5736
  "div",
5704
5737
  {
5705
- className: `${CLS}-header__bg`,
5738
+ className: `${CLS}-header__bg${noTemplate ? ` ${CLS}-header__bg--no-template` : ""}`,
5706
5739
  // 内联 base64 高光纹理;图宽=header 宽使亮带居中;padding 透传 DSL(默认 12px)
5707
5740
  style: (() => {
5708
5741
  const s = { backgroundImage: `url(${HEADER_BG_LIGHT})` };
@@ -6235,12 +6268,26 @@ function registerBuiltins() {
6235
6268
  const url = actionUrl || resolveURL(p.url) || p.behaviors?.[0]?.default_url?.url;
6236
6269
  const wv = p.widthValue;
6237
6270
  const widthTok = p.width ?? wv?.value;
6238
- const block = widthTok === "fill";
6239
- const base = `${CLS}-button ${CLS}-button--${type}${block ? ` ${CLS}-button--block` : ""}`;
6271
+ const isFill = widthTok === "fill";
6272
+ const pxWidth = /^\d+(\.\d+)?px$/i.test(String(widthTok ?? "")) ? String(widthTok) : "";
6273
+ const textFamily = type === "text" || type === "primary_text" || type === "danger_text";
6274
+ const block = isFill || textFamily;
6275
+ const auto = !block && !pxWidth;
6276
+ const base = `${CLS}-button ${CLS}-button--${type}` + (block ? ` ${CLS}-button--block` : "") + (auto ? ` ${CLS}-button--auto` : "");
6240
6277
  const style = {};
6241
- if (p.margin) {
6242
- style.width = "100%";
6243
- applyOuterMargin(style, p.margin);
6278
+ if (pxWidth) {
6279
+ style.width = pxWidth;
6280
+ if (p.margin) applyOuterMargin(style, p.margin);
6281
+ } else if (p.margin) {
6282
+ if (block) {
6283
+ style.width = "100%";
6284
+ applyOuterMargin(style, p.margin);
6285
+ } else {
6286
+ applyOuterMargin(style, p.margin);
6287
+ const { left, right } = horizontalMarginPx(p.margin);
6288
+ const inset = left + right;
6289
+ if (inset > 0) style.maxWidth = `calc(100% - ${inset}px)`;
6290
+ }
6244
6291
  }
6245
6292
  const styleProp = Object.keys(style).length ? { style } : {};
6246
6293
  const extra = p;
@@ -6321,15 +6368,53 @@ function registerBuiltins() {
6321
6368
  const name = typeof el.property?.name === "string" ? el.property.name : "";
6322
6369
  return h("form", { className, "data-feishu-form": name }, children);
6323
6370
  });
6371
+ function fieldWidthStyle(p, kind) {
6372
+ const wv = p.widthValue;
6373
+ const raw = String(p.width ?? wv?.value ?? "");
6374
+ const style = {};
6375
+ if (/^\d+(\.\d+)?px$/i.test(raw)) {
6376
+ style.width = raw;
6377
+ style.maxWidth = "100%";
6378
+ return style;
6379
+ }
6380
+ if (raw === "fill") {
6381
+ style.width = "100%";
6382
+ style.maxWidth = "none";
6383
+ return style;
6384
+ }
6385
+ if (kind === "select") {
6386
+ if (raw === "auto") {
6387
+ style.width = "100%";
6388
+ style.maxWidth = "100%";
6389
+ return style;
6390
+ }
6391
+ if (p.type === "text") {
6392
+ style.width = "auto";
6393
+ style.maxWidth = "100%";
6394
+ return style;
6395
+ }
6396
+ style.width = "282px";
6397
+ style.maxWidth = "100%";
6398
+ return style;
6399
+ }
6400
+ style.width = "100%";
6401
+ style.maxWidth = "282px";
6402
+ return style;
6403
+ }
6324
6404
  function renderStaticField(el) {
6325
6405
  const p = el.property || {};
6326
6406
  const ph = extractPlainText(p.placeholder) || "";
6327
- return h("div", { className: `${CLS}-input ${CLS}-input--static` }, ph);
6407
+ const kind = isSelectTag(el.tag) ? "select" : "input";
6408
+ const cls = `${CLS}-input ${CLS}-input--static` + (kind === "select" && p.type === "text" ? ` ${CLS}-input--text` : "");
6409
+ return h("div", { className: cls, style: fieldWidthStyle(p, kind) }, ph);
6328
6410
  }
6329
6411
  function fieldText(value) {
6330
6412
  if (typeof value === "string") return value;
6331
6413
  return extractPlainText(value);
6332
6414
  }
6415
+ function isSelectTag(tag) {
6416
+ return tag === "select_static" || tag === "multi_select_static" || tag === "select_person" || tag === "multi_select_person";
6417
+ }
6333
6418
  function renderInteractiveField(el) {
6334
6419
  const p = el.property || {};
6335
6420
  const ph = extractPlainText(p.placeholder) || "";
@@ -6337,13 +6422,15 @@ function registerBuiltins() {
6337
6422
  const nameProp = name ? { name } : {};
6338
6423
  const callback = readCallbackValue(p);
6339
6424
  const actionProp = callback ? { "data-feishu-action": JSON.stringify(callback), "data-feishu-tag": el.tag } : {};
6340
- if (el.tag === "select_static" || el.tag === "multi_select_static") {
6425
+ if (isSelectTag(el.tag)) {
6341
6426
  const initial = p.initial_option ?? p.initialOption;
6342
6427
  const options = Array.isArray(p.options) ? p.options : [];
6428
+ const cls = `${CLS}-input` + (p.type === "text" ? ` ${CLS}-input--text` : "");
6343
6429
  return h("select", {
6344
- className: `${CLS}-input`,
6430
+ className: cls,
6431
+ style: fieldWidthStyle(p, "select"),
6345
6432
  ...nameProp,
6346
- ...el.tag === "multi_select_static" ? { multiple: true } : {},
6433
+ ...el.tag === "multi_select_static" || el.tag === "multi_select_person" ? { multiple: true } : {},
6347
6434
  ...actionProp
6348
6435
  }, [
6349
6436
  h("option", { value: "" }, ph),
@@ -6362,6 +6449,7 @@ function registerBuiltins() {
6362
6449
  const inputType = el.tag === "date_picker" ? "date" : el.tag === "picker_time" ? "time" : "text";
6363
6450
  return h("input", {
6364
6451
  className: `${CLS}-input`,
6452
+ style: fieldWidthStyle(p, "input"),
6365
6453
  type: inputType,
6366
6454
  placeholder: ph,
6367
6455
  value: typeof def === "string" ? def : "",
package/dist/styles.css CHANGED
@@ -26,6 +26,9 @@
26
26
  width: 100%;
27
27
  max-width: 605px;
28
28
  box-sizing: border-box;
29
+ /* 真机按「卡片容器宽度 > 320px」区分宽/窄屏按钮档位,这里用容器查询复刻 */
30
+ container-type: inline-size;
31
+ container-name: universal-card;
29
32
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
30
33
  /* 正文 color:var(--O400) 等依赖这些变量;与飞书 :root light 色阶对齐 */
31
34
  --B50: #f0f4ff;
@@ -208,6 +211,8 @@
208
211
  /* 对齐飞书:max(100%, 605px) 140px,窄卡不把高光拉稀 */
209
212
  background-size: max(100%, 605px) 140px;
210
213
  }
214
+ /* default/disabled 模板真机不设背景色(bg_color 为空串),此时 header 不留底部内边距 */
215
+ .universal-card-header__bg--no-template { padding-bottom: 0; }
211
216
  /* 内容行:左侧图标 + 右侧标题区(对齐网页版 header__content = row) */
212
217
  .universal-card-header__content {
213
218
  width: 100%;
@@ -274,7 +279,9 @@
274
279
  column-gap: 12px;
275
280
  align-items: flex-start;
276
281
  }
277
- .universal-card-body > * { width: 100%; box-sizing: border-box; }
282
+ /* 交互控件不参与「子元素撑满」:按钮的宽度由真机规则决定(见 .universal-card-button--auto),
283
+ 若被这条 blanket 规则拉到 100%,窄屏/宽屏分档就失效了。 */
284
+ .universal-card-body > *:not(.universal-card-button) { width: 100%; box-sizing: border-box; }
278
285
 
279
286
  /* ---- markdown 段落 ---- */
280
287
  /* 对齐网页版:line-height 默认 1.6;具体字号/行高由组件内联 style 按 text_size 给出 */
@@ -394,10 +401,14 @@
394
401
  flex-shrink: 0; height: 1.2em; color: inherit;
395
402
  }
396
403
  .universal-card-button__icon .universal-card-icon { color: inherit; }
397
- /* 宽度:对齐真机——body > * { width:100% }、列 flex stretch 会把按钮撑满所在行/列。
398
- 不再强制 width:auto(那是网页版观感;真机独立成行的按钮默认占满整行)。
399
- width:"fill" 显式挂 --block,与缺省撑满同宽,并供 margin calc 识别。 */
400
- .universal-card-button--block { width: 100%; }
404
+ /* 宽度:对齐真机 ud__button 的 isWide 分档(真机按「卡片容器宽度 > 320px」判定):
405
+ 宽屏 → 自适应内容宽(--auto);窄屏(≤320px)→ 撑满整行(下方 @container 规则覆盖)。
406
+ text 族与 width:"fill" 恒定撑满(--block)。
407
+ align-self:flex-start —— 父级(body/form/column)默认 align-items:stretch,
408
+ 只写 width:auto 仍会被拉伸,必须同时阻止拉伸。 */
409
+ .universal-card-button { min-width: 58px; align-self: flex-start; }
410
+ .universal-card-button--block { width: 100%; min-width: unset; }
411
+ .universal-card-button--auto { width: auto; }
401
412
 
402
413
  /* —— outlined 描边族 —— */
403
414
  /* default:灰描边 + 白底 + 深字(未加变体类的裸 .universal-card-button 已是白底深字,
@@ -424,6 +435,8 @@
424
435
  .universal-card-button--danger_text {
425
436
  border-color: transparent; background: transparent; padding: 4px 8px;
426
437
  justify-content: flex-start;
438
+ /* 真机 text 族按钮不设最小宽度 */
439
+ min-width: unset;
427
440
  }
428
441
  .universal-card-button--text { color: #1f2329; }
429
442
  .universal-card-button--primary_text { color: #1456f0; }
@@ -459,6 +472,8 @@
459
472
  .universal-card-overflow--static { display: inline-flex; }
460
473
  .universal-card-overflow__summary {
461
474
  list-style: none; cursor: pointer; display: inline-flex;
475
+ /* 真机折叠按钮的最小宽度是 32px(普通按钮是 58px) */
476
+ min-width: 32px;
462
477
  }
463
478
  /* 去掉 <summary> 的浏览器默认三角标记 */
464
479
  .universal-card-overflow__summary::-webkit-details-marker { display: none; }
@@ -480,8 +495,9 @@
480
495
  .universal-card-overflow__item:hover { background: #f5f6f7; }
481
496
 
482
497
  /* ---- 表单(预览:仅透传,无表单语义) ---- */
483
- .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
484
- .universal-card-form > * { width: 100%; }
498
+ /* row-gap 12px:对齐真机 schema 2.0 表单容器 */
499
+ .universal-card-form { display: flex; flex-direction: column; row-gap: 12px; }
500
+ .universal-card-form > *:not(.universal-card-button) { width: 100%; }
485
501
  /* input / select:静态预览是只读框;interactive 去掉 --static,成为可输入控件 */
486
502
  .universal-card-input,
487
503
  .universal-card-input--static {
@@ -489,6 +505,13 @@
489
505
  font-family: inherit; font-size: 14px; line-height: 20px;
490
506
  background: #fff; border: 1px solid #dee0e3; border-radius: 6px;
491
507
  color: #1f2329; width: 100%;
508
+ /* 真机 input 默认 max-width:282px(width:"fill" 时由内联样式放开) */
509
+ max-width: 282px;
510
+ }
511
+ /* select type:"text":真机是无边框、内容自适应宽的下拉 */
512
+ .universal-card-input--text {
513
+ border-color: transparent; background: transparent;
514
+ min-height: 26px; padding: 1px 7px; width: auto;
492
515
  }
493
516
  .universal-card-input--static {
494
517
  color: #8f959e; background: #f5f6f7;
@@ -1156,3 +1179,10 @@
1156
1179
  from { opacity: 1; transform: translateY(0); }
1157
1180
  to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
1158
1181
  }
1182
+
1183
+ /* ---- 窄屏分档(真机 isWide:卡片容器宽度 > 320px 为宽屏) ----
1184
+ 真机规则:default/primary/primary_filled/danger/danger_filled/laser 按钮在窄屏撑满整行,
1185
+ 宽屏则自适应内容宽。@container 不增加选择器权重,靠源码顺序在 --auto 规则之后覆盖。 */
1186
+ @container universal-card (max-width: 320px) {
1187
+ .universal-card-button--auto { width: 100%; }
1188
+ }