@open-feishu-card/renderer 0.2.25 → 0.2.27

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
@@ -1127,8 +1127,6 @@ var BASE_STYLES_CSS = `/**
1127
1127
  .universal-card-header {
1128
1128
  width: 100%;
1129
1129
  overflow: hidden;
1130
- border-top-left-radius: 8px;
1131
- border-top-right-radius: 8px;
1132
1130
  }
1133
1131
  .universal-card-header__bg {
1134
1132
  box-sizing: border-box;
@@ -1262,7 +1260,7 @@ var BASE_STYLES_CSS = `/**
1262
1260
  line-height: 22px;
1263
1261
  }
1264
1262
 
1265
- /* ---- div / note ---- */
1263
+ /* ---- div ---- */
1266
1264
  .universal-card-div > * + * { margin-top: 8px; }
1267
1265
  /* div \u5E26\u524D\u7F6E\u56FE\u6807\uFF1Aicon \u5728\u5DE6\u3001\u6587\u672C\u5728\u53F3\u6392\u6210\u4E00\u884C\uFF08\u590D\u523B\u7F51\u9875\u7248 .universal-card-div \u5E03\u5C40\uFF09 */
1268
1266
  .universal-card-div--with-icon {
@@ -1274,10 +1272,6 @@ var BASE_STYLES_CSS = `/**
1274
1272
  display: flex; align-items: center; overflow: hidden;
1275
1273
  }
1276
1274
  .universal-card-div__content { flex: 1; min-width: 0; }
1277
- .universal-card-note {
1278
- font-size: 12px;
1279
- color: #8f959e;
1280
- }
1281
1275
 
1282
1276
  /* ---- \u5E03\u5C40\uFF1Acolumn_set / column ---- */
1283
1277
  .universal-card-column-set { display: flex; }
@@ -1306,6 +1300,7 @@ var BASE_STYLES_CSS = `/**
1306
1300
  display: inline-flex; align-items: center; justify-content: center;
1307
1301
  gap: 4px;
1308
1302
  min-height: 28px; padding: 4px 12px;
1303
+ font-family: inherit;
1309
1304
  font-size: 14px; line-height: 22px; border-radius: 6px;
1310
1305
  border: 1px solid transparent; background: transparent; color: #1f2329;
1311
1306
  cursor: pointer; text-decoration: none; box-sizing: border-box;
@@ -1318,14 +1313,9 @@ var BASE_STYLES_CSS = `/**
1318
1313
  flex-shrink: 0; height: 1.2em; color: inherit;
1319
1314
  }
1320
1315
  .universal-card-button__icon .universal-card-icon { color: inherit; }
1321
- /* default \u5BBD\u5EA6\uFF1A\u6309\u5185\u5BB9\u6536\u7F29\u3002\u5217\u662F flex column\uFF08\u9ED8\u8BA4 align-items:stretch\uFF09\uFF0C
1322
- body \u53C8\u6709 > * { width:100% }\uFF0C\u4E0D\u52A0\u8FD9\u4E24\u6761\u4F1A\u88AB\u6491\u6EE1\u6240\u5728\u5217\u3002 */
1323
- .universal-card-button:not(.universal-card-button--block) {
1324
- width: auto;
1325
- max-width: 100%;
1326
- align-self: flex-start;
1327
- }
1328
- /* fill\uFF08width:"fill"\uFF09\uFF1A\u6491\u6EE1\u6240\u5728\u5217 */
1316
+ /* \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
1317
+ \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
1318
+ width:"fill" \u663E\u5F0F\u6302 --block\uFF0C\u4E0E\u7F3A\u7701\u6491\u6EE1\u540C\u5BBD\uFF0C\u5E76\u4F9B margin calc \u8BC6\u522B\u3002 */
1329
1319
  .universal-card-button--block { width: 100%; }
1330
1320
 
1331
1321
  /* \u2014\u2014 outlined \u63CF\u8FB9\u65CF \u2014\u2014 */
@@ -1347,11 +1337,12 @@ var BASE_STYLES_CSS = `/**
1347
1337
  }
1348
1338
  .universal-card-button--danger:not(.universal-card-button--static):hover { background: #fef0f0; }
1349
1339
 
1350
- /* \u2014\u2014 text \u7EAF\u6587\u5B57\u65CF\uFF08\u65E0\u8FB9\u6846/\u65E0\u5E95\u8272\uFF0C\u6309\u5185\u5BB9\u5BBD\uFF09 \u2014\u2014 */
1340
+ /* \u2014\u2014 text \u7EAF\u6587\u5B57\u65CF\uFF08\u65E0\u8FB9\u6846/\u65E0\u5E95\u8272\uFF1B\u6491\u6EE1\u65F6\u6587\u5B57\u9760\u5DE6\uFF0C\u5BF9\u9F50\u771F\u673A\uFF09 \u2014\u2014 */
1351
1341
  .universal-card-button--text,
1352
1342
  .universal-card-button--primary_text,
1353
1343
  .universal-card-button--danger_text {
1354
1344
  border-color: transparent; background: transparent; padding: 4px 8px;
1345
+ justify-content: flex-start;
1355
1346
  }
1356
1347
  .universal-card-button--text { color: #1f2329; }
1357
1348
  .universal-card-button--primary_text { color: #1456f0; }
@@ -1384,17 +1375,19 @@ var BASE_STYLES_CSS = `/**
1384
1375
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1385
1376
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1386
1377
  .universal-card-form > * { width: 100%; }
1387
- /* input / select \u7B49\u8F93\u5165\u63A7\u4EF6\uFF1A\u9884\u89C8\u91CC\u6E32\u67D3\u6210\u53EA\u8BFB\u9759\u6001\u6846\uFF0C\u5C55\u793A placeholder */
1378
+ /* input / select\uFF1A\u9759\u6001\u9884\u89C8\u662F\u53EA\u8BFB\u6846\uFF1Binteractive \u53BB\u6389 --static\uFF0C\u6210\u4E3A\u53EF\u8F93\u5165\u63A7\u4EF6 */
1379
+ .universal-card-input,
1388
1380
  .universal-card-input--static {
1389
1381
  min-height: 32px; padding: 6px 12px; box-sizing: border-box;
1390
- font-size: 14px; line-height: 20px; color: #8f959e;
1391
- background: #f5f6f7; border: 1px solid #dee0e3; border-radius: 6px;
1382
+ font-family: inherit; font-size: 14px; line-height: 20px;
1383
+ background: #fff; border: 1px solid #dee0e3; border-radius: 6px;
1384
+ color: #1f2329; width: 100%;
1385
+ }
1386
+ .universal-card-input--static {
1387
+ color: #8f959e; background: #f5f6f7;
1392
1388
  cursor: default; pointer-events: none;
1393
1389
  }
1394
1390
 
1395
- /* ---- action \u5BB9\u5668 ---- */
1396
- .universal-card-action { display: flex; flex-wrap: wrap; gap: 12px; }
1397
-
1398
1391
  /* ---- checker \u52FE\u9009\u5668\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 universal-card-checker\uFF09 ---- */
1399
1392
  .universal-card-checker {
1400
1393
  display: flex; box-sizing: border-box; overflow: hidden; align-items: flex-start;
@@ -1985,7 +1978,6 @@ var BASE_STYLES_CSS = `/**
1985
1978
  }
1986
1979
  .universal-card-root.theme-dark .universal-card-markdown { color: #d6d6d6; }
1987
1980
  .universal-card-root.theme-dark .universal-card-hr { background-color: #2e2e30; }
1988
- .universal-card-root.theme-dark .universal-card-note { color: #8a8a8a; }
1989
1981
  `;
1990
1982
 
1991
1983
  // src/icon-font.ts
@@ -5612,10 +5604,6 @@ function registerBuiltins() {
5612
5604
  }
5613
5605
  return h("div", { className: `${CLS}-div` }, content);
5614
5606
  });
5615
- registerComponent(
5616
- "note",
5617
- (el, ctx) => h("div", { className: `${CLS}-note` }, renderElements(el.property?.elements, ctx))
5618
- );
5619
5607
  registerComponent("column_set", (el, ctx) => {
5620
5608
  const p = el.property || {};
5621
5609
  const gapRaw = p.horizontalSpacingValue?.value ?? p.horizontal_spacing;
@@ -5638,8 +5626,26 @@ function registerBuiltins() {
5638
5626
  const pad = boxToStyle(p.padding);
5639
5627
  if (pad) style.padding = pad;
5640
5628
  const cls = `${CLS}-column-set` + (bg ? ` ${CLS}-column-set--bg` : "");
5629
+ if (isStateHost(el)) {
5630
+ style.position = "absolute";
5631
+ style.width = "0";
5632
+ style.height = "0";
5633
+ style.overflow = "hidden";
5634
+ style.margin = "0";
5635
+ style.padding = "0";
5636
+ }
5641
5637
  return h("div", { className: cls, style }, renderElements(p.columns, ctx));
5642
5638
  });
5639
+ function isStateHost(el) {
5640
+ if (el.id === "card_state") return true;
5641
+ const actions = el.property?.actions;
5642
+ if (!Array.isArray(actions)) return false;
5643
+ return actions.some((item) => {
5644
+ if (!item || typeof item !== "object") return false;
5645
+ const action = item.action;
5646
+ return typeof action?.url === "string" && action.url.includes("msgcard/unsupported_action#s1.");
5647
+ });
5648
+ }
5643
5649
  registerComponent("column", (el, ctx) => {
5644
5650
  const p = el.property || {};
5645
5651
  const style = {
@@ -5978,40 +5984,115 @@ function registerBuiltins() {
5978
5984
  const children = iconVNode ? [iconVNode, label] : [label];
5979
5985
  const actionUrl = Array.isArray(p.actions) ? resolveURL(p.actions.find((a) => a.type === "open_url")?.action?.url) : "";
5980
5986
  const url = actionUrl || resolveURL(p.url) || p.behaviors?.[0]?.default_url?.url;
5981
- const isText = type === "text" || type === "primary_text" || type === "danger_text";
5982
5987
  const wv = p.widthValue;
5983
5988
  const widthTok = p.width ?? wv?.value;
5984
- const block = widthTok === "fill" && !isText;
5989
+ const block = widthTok === "fill";
5985
5990
  const base = `${CLS}-button ${CLS}-button--${type}${block ? ` ${CLS}-button--block` : ""}`;
5986
5991
  const style = {};
5987
5992
  if (p.margin) {
5988
- style.width = block ? "100%" : "auto";
5993
+ style.width = "100%";
5989
5994
  applyOuterMargin(style, p.margin);
5990
5995
  }
5991
5996
  const styleProp = Object.keys(style).length ? { style } : {};
5997
+ const extra = p;
5998
+ const callback = readCallbackValue(extra);
5999
+ const formAction = String(extra.form_action_type ?? extra.formActionType ?? "");
6000
+ if (ctx.options.interactive && callback && !p.disabled && !url) {
6001
+ return h("button", {
6002
+ type: formAction === "reset" ? "reset" : formAction === "submit" ? "submit" : "button",
6003
+ className: base,
6004
+ "data-feishu-action": JSON.stringify(callback),
6005
+ "data-feishu-tag": "button",
6006
+ ...styleProp
6007
+ }, children);
6008
+ }
5992
6009
  if (url && !p.disabled) {
5993
6010
  return h("a", { className: base, href: url, target: "_blank", rel: "noopener", ...styleProp }, children);
5994
6011
  }
5995
6012
  const cls = `${base} ${CLS}-button--static` + (p.disabled ? ` ${CLS}-button--disabled` : "");
5996
6013
  return h("span", { className: cls, ...styleProp }, children);
5997
6014
  });
5998
- registerComponent(
5999
- "form",
6000
- (el, ctx) => h("div", { className: `${CLS}-form` }, renderElements(el.property?.elements, ctx))
6001
- );
6015
+ function isRecord(value) {
6016
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6017
+ }
6018
+ function readCallbackValue(p) {
6019
+ const fromList = (list) => {
6020
+ if (!Array.isArray(list)) return null;
6021
+ for (const item of list) {
6022
+ if (!isRecord(item) || item.type !== "callback") continue;
6023
+ const inner = isRecord(item.action) ? item.action : item;
6024
+ if (isRecord(inner.value)) return inner.value;
6025
+ }
6026
+ return null;
6027
+ };
6028
+ return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6029
+ }
6030
+ registerComponent("form", (el, ctx) => {
6031
+ const children = renderElements(el.property?.elements, ctx);
6032
+ const className = `${CLS}-form`;
6033
+ if (!ctx.options.interactive) return h("div", { className }, children);
6034
+ const name = typeof el.property?.name === "string" ? el.property.name : "";
6035
+ return h("form", { className, "data-feishu-form": name }, children);
6036
+ });
6002
6037
  function renderStaticField(el) {
6003
6038
  const p = el.property || {};
6004
6039
  const ph = extractPlainText(p.placeholder) || "";
6005
6040
  return h("div", { className: `${CLS}-input ${CLS}-input--static` }, ph);
6006
6041
  }
6007
- registerComponent("input", (el) => renderStaticField(el));
6008
- registerComponent("multi_select_static", (el) => renderStaticField(el));
6009
- registerComponent("select_static", (el) => renderStaticField(el));
6010
- registerComponent("date_picker", (el) => renderStaticField(el));
6011
- registerComponent("picker_time", (el) => renderStaticField(el));
6012
- registerComponent("picker_datetime", (el) => renderStaticField(el));
6013
- registerComponent("select_person", (el) => renderStaticField(el));
6014
- registerComponent("multi_select_person", (el) => renderStaticField(el));
6042
+ function fieldText(value) {
6043
+ if (typeof value === "string") return value;
6044
+ return extractPlainText(value);
6045
+ }
6046
+ function renderInteractiveField(el) {
6047
+ const p = el.property || {};
6048
+ const ph = extractPlainText(p.placeholder) || "";
6049
+ const name = typeof p.name === "string" ? p.name : void 0;
6050
+ const nameProp = name ? { name } : {};
6051
+ const callback = readCallbackValue(p);
6052
+ const actionProp = callback ? { "data-feishu-action": JSON.stringify(callback), "data-feishu-tag": el.tag } : {};
6053
+ if (el.tag === "select_static" || el.tag === "multi_select_static") {
6054
+ const initial = p.initial_option ?? p.initialOption;
6055
+ const options = Array.isArray(p.options) ? p.options : [];
6056
+ return h("select", {
6057
+ className: `${CLS}-input`,
6058
+ ...nameProp,
6059
+ ...el.tag === "multi_select_static" ? { multiple: true } : {},
6060
+ ...actionProp
6061
+ }, [
6062
+ h("option", { value: "" }, ph),
6063
+ ...options.map((option) => {
6064
+ const item = isRecord(option) ? option : {};
6065
+ const value = typeof item.value === "string" ? item.value : fieldText(item.text ?? item);
6066
+ const label = fieldText(item.text ?? item.label) || value;
6067
+ return h("option", {
6068
+ value,
6069
+ ...value !== "" && value === initial ? { selected: true } : {}
6070
+ }, label);
6071
+ })
6072
+ ]);
6073
+ }
6074
+ const def = p.default_value ?? p.defaultValue;
6075
+ const inputType = el.tag === "date_picker" ? "date" : el.tag === "picker_time" ? "time" : "text";
6076
+ return h("input", {
6077
+ className: `${CLS}-input`,
6078
+ type: inputType,
6079
+ placeholder: ph,
6080
+ value: typeof def === "string" ? def : "",
6081
+ ...nameProp,
6082
+ ...actionProp
6083
+ });
6084
+ }
6085
+ function renderField(el, ctx) {
6086
+ return ctx.options.interactive ? renderInteractiveField(el) : renderStaticField(el);
6087
+ }
6088
+ registerComponent("input", (el, ctx) => renderField(el, ctx));
6089
+ registerComponent("multi_select_static", (el, ctx) => renderField(el, ctx));
6090
+ registerComponent("select_static", (el, ctx) => renderField(el, ctx));
6091
+ registerComponent("date_picker", (el, ctx) => renderField(el, ctx));
6092
+ registerComponent("picker_time", (el, ctx) => renderField(el, ctx));
6093
+ registerComponent("picker_datetime", (el, ctx) => renderField(el, ctx));
6094
+ registerComponent("select_person", (el, ctx) => renderField(el, ctx));
6095
+ registerComponent("multi_select_person", (el, ctx) => renderField(el, ctx));
6015
6096
  registerComponent("checker", (el, ctx) => {
6016
6097
  const p = el.property || {};
6017
6098
  const checked = p.checked === true;
@@ -6083,12 +6164,6 @@ function registerBuiltins() {
6083
6164
  const Tag = overall && !disabled ? "label" : "div";
6084
6165
  return h(Tag, props, checkbox, content, buttonsVNode);
6085
6166
  });
6086
- registerComponent("action", (el, ctx) => {
6087
- const p = el.property || {};
6088
- const layout = p.layout || "flow";
6089
- const style = { display: "flex", flexWrap: "wrap", gap: "12px" };
6090
- return h("div", { className: `${CLS}-action ${CLS}-action--${layout}`, style }, renderElements(p.actions, ctx));
6091
- });
6092
6167
  registerComponent("at", (el, ctx) => {
6093
6168
  const p = el.property || {};
6094
6169
  if (p.userID === "all") {
package/dist/index.d.cts CHANGED
@@ -305,6 +305,12 @@ interface RenderOptions {
305
305
  avatarURLSuffix?: string;
306
306
  /** 自定义头像地址解析:入参为命中的 PersonInfo,返回最终 src(优先级最高) */
307
307
  resolvePersonAvatar?: (person: PersonInfo) => string | null | undefined;
308
+ /**
309
+ * 交互预览。默认关闭:回调按钮是不可点的 span,输入框是静态占位。
310
+ * 打开后,回调按钮变成带 data-feishu-action 的 button,输入框/下拉框变成真实表单控件。
311
+ * 宿主监听这些控件,自己把点击发回卡片服务。
312
+ */
313
+ interactive?: boolean;
308
314
  }
309
315
  /**
310
316
  * 人员信息:person_v1 / person_list / at 元素只带 ID,姓名与头像 key 在这里。
package/dist/index.d.ts CHANGED
@@ -305,6 +305,12 @@ interface RenderOptions {
305
305
  avatarURLSuffix?: string;
306
306
  /** 自定义头像地址解析:入参为命中的 PersonInfo,返回最终 src(优先级最高) */
307
307
  resolvePersonAvatar?: (person: PersonInfo) => string | null | undefined;
308
+ /**
309
+ * 交互预览。默认关闭:回调按钮是不可点的 span,输入框是静态占位。
310
+ * 打开后,回调按钮变成带 data-feishu-action 的 button,输入框/下拉框变成真实表单控件。
311
+ * 宿主监听这些控件,自己把点击发回卡片服务。
312
+ */
313
+ interactive?: boolean;
308
314
  }
309
315
  /**
310
316
  * 人员信息:person_v1 / person_list / at 元素只带 ID,姓名与头像 key 在这里。
package/dist/index.js CHANGED
@@ -1126,8 +1126,6 @@ var BASE_STYLES_CSS = `/**
1126
1126
  .universal-card-header {
1127
1127
  width: 100%;
1128
1128
  overflow: hidden;
1129
- border-top-left-radius: 8px;
1130
- border-top-right-radius: 8px;
1131
1129
  }
1132
1130
  .universal-card-header__bg {
1133
1131
  box-sizing: border-box;
@@ -1261,7 +1259,7 @@ var BASE_STYLES_CSS = `/**
1261
1259
  line-height: 22px;
1262
1260
  }
1263
1261
 
1264
- /* ---- div / note ---- */
1262
+ /* ---- div ---- */
1265
1263
  .universal-card-div > * + * { margin-top: 8px; }
1266
1264
  /* div \u5E26\u524D\u7F6E\u56FE\u6807\uFF1Aicon \u5728\u5DE6\u3001\u6587\u672C\u5728\u53F3\u6392\u6210\u4E00\u884C\uFF08\u590D\u523B\u7F51\u9875\u7248 .universal-card-div \u5E03\u5C40\uFF09 */
1267
1265
  .universal-card-div--with-icon {
@@ -1273,10 +1271,6 @@ var BASE_STYLES_CSS = `/**
1273
1271
  display: flex; align-items: center; overflow: hidden;
1274
1272
  }
1275
1273
  .universal-card-div__content { flex: 1; min-width: 0; }
1276
- .universal-card-note {
1277
- font-size: 12px;
1278
- color: #8f959e;
1279
- }
1280
1274
 
1281
1275
  /* ---- \u5E03\u5C40\uFF1Acolumn_set / column ---- */
1282
1276
  .universal-card-column-set { display: flex; }
@@ -1305,6 +1299,7 @@ var BASE_STYLES_CSS = `/**
1305
1299
  display: inline-flex; align-items: center; justify-content: center;
1306
1300
  gap: 4px;
1307
1301
  min-height: 28px; padding: 4px 12px;
1302
+ font-family: inherit;
1308
1303
  font-size: 14px; line-height: 22px; border-radius: 6px;
1309
1304
  border: 1px solid transparent; background: transparent; color: #1f2329;
1310
1305
  cursor: pointer; text-decoration: none; box-sizing: border-box;
@@ -1317,14 +1312,9 @@ var BASE_STYLES_CSS = `/**
1317
1312
  flex-shrink: 0; height: 1.2em; color: inherit;
1318
1313
  }
1319
1314
  .universal-card-button__icon .universal-card-icon { color: inherit; }
1320
- /* default \u5BBD\u5EA6\uFF1A\u6309\u5185\u5BB9\u6536\u7F29\u3002\u5217\u662F flex column\uFF08\u9ED8\u8BA4 align-items:stretch\uFF09\uFF0C
1321
- body \u53C8\u6709 > * { width:100% }\uFF0C\u4E0D\u52A0\u8FD9\u4E24\u6761\u4F1A\u88AB\u6491\u6EE1\u6240\u5728\u5217\u3002 */
1322
- .universal-card-button:not(.universal-card-button--block) {
1323
- width: auto;
1324
- max-width: 100%;
1325
- align-self: flex-start;
1326
- }
1327
- /* fill\uFF08width:"fill"\uFF09\uFF1A\u6491\u6EE1\u6240\u5728\u5217 */
1315
+ /* \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
1316
+ \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
1317
+ width:"fill" \u663E\u5F0F\u6302 --block\uFF0C\u4E0E\u7F3A\u7701\u6491\u6EE1\u540C\u5BBD\uFF0C\u5E76\u4F9B margin calc \u8BC6\u522B\u3002 */
1328
1318
  .universal-card-button--block { width: 100%; }
1329
1319
 
1330
1320
  /* \u2014\u2014 outlined \u63CF\u8FB9\u65CF \u2014\u2014 */
@@ -1346,11 +1336,12 @@ var BASE_STYLES_CSS = `/**
1346
1336
  }
1347
1337
  .universal-card-button--danger:not(.universal-card-button--static):hover { background: #fef0f0; }
1348
1338
 
1349
- /* \u2014\u2014 text \u7EAF\u6587\u5B57\u65CF\uFF08\u65E0\u8FB9\u6846/\u65E0\u5E95\u8272\uFF0C\u6309\u5185\u5BB9\u5BBD\uFF09 \u2014\u2014 */
1339
+ /* \u2014\u2014 text \u7EAF\u6587\u5B57\u65CF\uFF08\u65E0\u8FB9\u6846/\u65E0\u5E95\u8272\uFF1B\u6491\u6EE1\u65F6\u6587\u5B57\u9760\u5DE6\uFF0C\u5BF9\u9F50\u771F\u673A\uFF09 \u2014\u2014 */
1350
1340
  .universal-card-button--text,
1351
1341
  .universal-card-button--primary_text,
1352
1342
  .universal-card-button--danger_text {
1353
1343
  border-color: transparent; background: transparent; padding: 4px 8px;
1344
+ justify-content: flex-start;
1354
1345
  }
1355
1346
  .universal-card-button--text { color: #1f2329; }
1356
1347
  .universal-card-button--primary_text { color: #1456f0; }
@@ -1383,17 +1374,19 @@ var BASE_STYLES_CSS = `/**
1383
1374
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1384
1375
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1385
1376
  .universal-card-form > * { width: 100%; }
1386
- /* input / select \u7B49\u8F93\u5165\u63A7\u4EF6\uFF1A\u9884\u89C8\u91CC\u6E32\u67D3\u6210\u53EA\u8BFB\u9759\u6001\u6846\uFF0C\u5C55\u793A placeholder */
1377
+ /* input / select\uFF1A\u9759\u6001\u9884\u89C8\u662F\u53EA\u8BFB\u6846\uFF1Binteractive \u53BB\u6389 --static\uFF0C\u6210\u4E3A\u53EF\u8F93\u5165\u63A7\u4EF6 */
1378
+ .universal-card-input,
1387
1379
  .universal-card-input--static {
1388
1380
  min-height: 32px; padding: 6px 12px; box-sizing: border-box;
1389
- font-size: 14px; line-height: 20px; color: #8f959e;
1390
- background: #f5f6f7; border: 1px solid #dee0e3; border-radius: 6px;
1381
+ font-family: inherit; font-size: 14px; line-height: 20px;
1382
+ background: #fff; border: 1px solid #dee0e3; border-radius: 6px;
1383
+ color: #1f2329; width: 100%;
1384
+ }
1385
+ .universal-card-input--static {
1386
+ color: #8f959e; background: #f5f6f7;
1391
1387
  cursor: default; pointer-events: none;
1392
1388
  }
1393
1389
 
1394
- /* ---- action \u5BB9\u5668 ---- */
1395
- .universal-card-action { display: flex; flex-wrap: wrap; gap: 12px; }
1396
-
1397
1390
  /* ---- checker \u52FE\u9009\u5668\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 universal-card-checker\uFF09 ---- */
1398
1391
  .universal-card-checker {
1399
1392
  display: flex; box-sizing: border-box; overflow: hidden; align-items: flex-start;
@@ -1984,7 +1977,6 @@ var BASE_STYLES_CSS = `/**
1984
1977
  }
1985
1978
  .universal-card-root.theme-dark .universal-card-markdown { color: #d6d6d6; }
1986
1979
  .universal-card-root.theme-dark .universal-card-hr { background-color: #2e2e30; }
1987
- .universal-card-root.theme-dark .universal-card-note { color: #8a8a8a; }
1988
1980
  `;
1989
1981
 
1990
1982
  // src/icon-font.ts
@@ -5611,10 +5603,6 @@ function registerBuiltins() {
5611
5603
  }
5612
5604
  return h("div", { className: `${CLS}-div` }, content);
5613
5605
  });
5614
- registerComponent(
5615
- "note",
5616
- (el, ctx) => h("div", { className: `${CLS}-note` }, renderElements(el.property?.elements, ctx))
5617
- );
5618
5606
  registerComponent("column_set", (el, ctx) => {
5619
5607
  const p = el.property || {};
5620
5608
  const gapRaw = p.horizontalSpacingValue?.value ?? p.horizontal_spacing;
@@ -5637,8 +5625,26 @@ function registerBuiltins() {
5637
5625
  const pad = boxToStyle(p.padding);
5638
5626
  if (pad) style.padding = pad;
5639
5627
  const cls = `${CLS}-column-set` + (bg ? ` ${CLS}-column-set--bg` : "");
5628
+ if (isStateHost(el)) {
5629
+ style.position = "absolute";
5630
+ style.width = "0";
5631
+ style.height = "0";
5632
+ style.overflow = "hidden";
5633
+ style.margin = "0";
5634
+ style.padding = "0";
5635
+ }
5640
5636
  return h("div", { className: cls, style }, renderElements(p.columns, ctx));
5641
5637
  });
5638
+ function isStateHost(el) {
5639
+ if (el.id === "card_state") return true;
5640
+ const actions = el.property?.actions;
5641
+ if (!Array.isArray(actions)) return false;
5642
+ return actions.some((item) => {
5643
+ if (!item || typeof item !== "object") return false;
5644
+ const action = item.action;
5645
+ return typeof action?.url === "string" && action.url.includes("msgcard/unsupported_action#s1.");
5646
+ });
5647
+ }
5642
5648
  registerComponent("column", (el, ctx) => {
5643
5649
  const p = el.property || {};
5644
5650
  const style = {
@@ -5977,40 +5983,115 @@ function registerBuiltins() {
5977
5983
  const children = iconVNode ? [iconVNode, label] : [label];
5978
5984
  const actionUrl = Array.isArray(p.actions) ? resolveURL(p.actions.find((a) => a.type === "open_url")?.action?.url) : "";
5979
5985
  const url = actionUrl || resolveURL(p.url) || p.behaviors?.[0]?.default_url?.url;
5980
- const isText = type === "text" || type === "primary_text" || type === "danger_text";
5981
5986
  const wv = p.widthValue;
5982
5987
  const widthTok = p.width ?? wv?.value;
5983
- const block = widthTok === "fill" && !isText;
5988
+ const block = widthTok === "fill";
5984
5989
  const base = `${CLS}-button ${CLS}-button--${type}${block ? ` ${CLS}-button--block` : ""}`;
5985
5990
  const style = {};
5986
5991
  if (p.margin) {
5987
- style.width = block ? "100%" : "auto";
5992
+ style.width = "100%";
5988
5993
  applyOuterMargin(style, p.margin);
5989
5994
  }
5990
5995
  const styleProp = Object.keys(style).length ? { style } : {};
5996
+ const extra = p;
5997
+ const callback = readCallbackValue(extra);
5998
+ const formAction = String(extra.form_action_type ?? extra.formActionType ?? "");
5999
+ if (ctx.options.interactive && callback && !p.disabled && !url) {
6000
+ return h("button", {
6001
+ type: formAction === "reset" ? "reset" : formAction === "submit" ? "submit" : "button",
6002
+ className: base,
6003
+ "data-feishu-action": JSON.stringify(callback),
6004
+ "data-feishu-tag": "button",
6005
+ ...styleProp
6006
+ }, children);
6007
+ }
5991
6008
  if (url && !p.disabled) {
5992
6009
  return h("a", { className: base, href: url, target: "_blank", rel: "noopener", ...styleProp }, children);
5993
6010
  }
5994
6011
  const cls = `${base} ${CLS}-button--static` + (p.disabled ? ` ${CLS}-button--disabled` : "");
5995
6012
  return h("span", { className: cls, ...styleProp }, children);
5996
6013
  });
5997
- registerComponent(
5998
- "form",
5999
- (el, ctx) => h("div", { className: `${CLS}-form` }, renderElements(el.property?.elements, ctx))
6000
- );
6014
+ function isRecord(value) {
6015
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6016
+ }
6017
+ function readCallbackValue(p) {
6018
+ const fromList = (list) => {
6019
+ if (!Array.isArray(list)) return null;
6020
+ for (const item of list) {
6021
+ if (!isRecord(item) || item.type !== "callback") continue;
6022
+ const inner = isRecord(item.action) ? item.action : item;
6023
+ if (isRecord(inner.value)) return inner.value;
6024
+ }
6025
+ return null;
6026
+ };
6027
+ return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6028
+ }
6029
+ registerComponent("form", (el, ctx) => {
6030
+ const children = renderElements(el.property?.elements, ctx);
6031
+ const className = `${CLS}-form`;
6032
+ if (!ctx.options.interactive) return h("div", { className }, children);
6033
+ const name = typeof el.property?.name === "string" ? el.property.name : "";
6034
+ return h("form", { className, "data-feishu-form": name }, children);
6035
+ });
6001
6036
  function renderStaticField(el) {
6002
6037
  const p = el.property || {};
6003
6038
  const ph = extractPlainText(p.placeholder) || "";
6004
6039
  return h("div", { className: `${CLS}-input ${CLS}-input--static` }, ph);
6005
6040
  }
6006
- registerComponent("input", (el) => renderStaticField(el));
6007
- registerComponent("multi_select_static", (el) => renderStaticField(el));
6008
- registerComponent("select_static", (el) => renderStaticField(el));
6009
- registerComponent("date_picker", (el) => renderStaticField(el));
6010
- registerComponent("picker_time", (el) => renderStaticField(el));
6011
- registerComponent("picker_datetime", (el) => renderStaticField(el));
6012
- registerComponent("select_person", (el) => renderStaticField(el));
6013
- registerComponent("multi_select_person", (el) => renderStaticField(el));
6041
+ function fieldText(value) {
6042
+ if (typeof value === "string") return value;
6043
+ return extractPlainText(value);
6044
+ }
6045
+ function renderInteractiveField(el) {
6046
+ const p = el.property || {};
6047
+ const ph = extractPlainText(p.placeholder) || "";
6048
+ const name = typeof p.name === "string" ? p.name : void 0;
6049
+ const nameProp = name ? { name } : {};
6050
+ const callback = readCallbackValue(p);
6051
+ const actionProp = callback ? { "data-feishu-action": JSON.stringify(callback), "data-feishu-tag": el.tag } : {};
6052
+ if (el.tag === "select_static" || el.tag === "multi_select_static") {
6053
+ const initial = p.initial_option ?? p.initialOption;
6054
+ const options = Array.isArray(p.options) ? p.options : [];
6055
+ return h("select", {
6056
+ className: `${CLS}-input`,
6057
+ ...nameProp,
6058
+ ...el.tag === "multi_select_static" ? { multiple: true } : {},
6059
+ ...actionProp
6060
+ }, [
6061
+ h("option", { value: "" }, ph),
6062
+ ...options.map((option) => {
6063
+ const item = isRecord(option) ? option : {};
6064
+ const value = typeof item.value === "string" ? item.value : fieldText(item.text ?? item);
6065
+ const label = fieldText(item.text ?? item.label) || value;
6066
+ return h("option", {
6067
+ value,
6068
+ ...value !== "" && value === initial ? { selected: true } : {}
6069
+ }, label);
6070
+ })
6071
+ ]);
6072
+ }
6073
+ const def = p.default_value ?? p.defaultValue;
6074
+ const inputType = el.tag === "date_picker" ? "date" : el.tag === "picker_time" ? "time" : "text";
6075
+ return h("input", {
6076
+ className: `${CLS}-input`,
6077
+ type: inputType,
6078
+ placeholder: ph,
6079
+ value: typeof def === "string" ? def : "",
6080
+ ...nameProp,
6081
+ ...actionProp
6082
+ });
6083
+ }
6084
+ function renderField(el, ctx) {
6085
+ return ctx.options.interactive ? renderInteractiveField(el) : renderStaticField(el);
6086
+ }
6087
+ registerComponent("input", (el, ctx) => renderField(el, ctx));
6088
+ registerComponent("multi_select_static", (el, ctx) => renderField(el, ctx));
6089
+ registerComponent("select_static", (el, ctx) => renderField(el, ctx));
6090
+ registerComponent("date_picker", (el, ctx) => renderField(el, ctx));
6091
+ registerComponent("picker_time", (el, ctx) => renderField(el, ctx));
6092
+ registerComponent("picker_datetime", (el, ctx) => renderField(el, ctx));
6093
+ registerComponent("select_person", (el, ctx) => renderField(el, ctx));
6094
+ registerComponent("multi_select_person", (el, ctx) => renderField(el, ctx));
6014
6095
  registerComponent("checker", (el, ctx) => {
6015
6096
  const p = el.property || {};
6016
6097
  const checked = p.checked === true;
@@ -6082,12 +6163,6 @@ function registerBuiltins() {
6082
6163
  const Tag = overall && !disabled ? "label" : "div";
6083
6164
  return h(Tag, props, checkbox, content, buttonsVNode);
6084
6165
  });
6085
- registerComponent("action", (el, ctx) => {
6086
- const p = el.property || {};
6087
- const layout = p.layout || "flow";
6088
- const style = { display: "flex", flexWrap: "wrap", gap: "12px" };
6089
- return h("div", { className: `${CLS}-action ${CLS}-action--${layout}`, style }, renderElements(p.actions, ctx));
6090
- });
6091
6166
  registerComponent("at", (el, ctx) => {
6092
6167
  const p = el.property || {};
6093
6168
  if (p.userID === "all") {