@open-feishu-card/renderer 0.2.29 → 0.2.31
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/all.css +49 -0
- package/dist/index.cjs +108 -1
- package/dist/index.d.cts +50 -3
- package/dist/index.d.ts +50 -3
- package/dist/index.js +107 -2
- package/dist/styles.css +49 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -671,7 +671,8 @@ function resolveImageSrc(p, ctx) {
|
|
|
671
671
|
if (p.src) return p.src;
|
|
672
672
|
if (p.url) return resolveURL(p.url) || null;
|
|
673
673
|
if (p.img_url) return p.img_url;
|
|
674
|
-
|
|
674
|
+
const imageId = p.imageID || p.img_key;
|
|
675
|
+
if (imageId && ctx.options.imageBaseURL) return ctx.options.imageBaseURL + imageId;
|
|
675
676
|
return null;
|
|
676
677
|
}
|
|
677
678
|
function resolveMediaSrc(p, kind, ctx) {
|
|
@@ -701,6 +702,7 @@ function personAvatarSrc(person, ctx) {
|
|
|
701
702
|
const r = ctx.options.resolvePersonAvatar(person);
|
|
702
703
|
if (r) return r;
|
|
703
704
|
}
|
|
705
|
+
if (person.avatar_url) return person.avatar_url;
|
|
704
706
|
if (!person.avatar_key) return null;
|
|
705
707
|
const base = ctx.options.avatarBaseURL || PERSON_AVATAR_BASE;
|
|
706
708
|
const suffix = ctx.options.avatarURLSuffix ?? PERSON_AVATAR_SUFFIX;
|
|
@@ -2017,6 +2019,55 @@ var BASE_STYLES_CSS = `/**
|
|
|
2017
2019
|
}
|
|
2018
2020
|
.universal-card-root.theme-dark .universal-card-markdown { color: #d6d6d6; }
|
|
2019
2021
|
.universal-card-root.theme-dark .universal-card-hr { background-color: #2e2e30; }
|
|
2022
|
+
|
|
2023
|
+
/* ---- Toast \u5BBF\u4E3B\uFF08\u5BF9\u9F50\u5BA2\u6237\u7AEF .ud__toast.ud__notice\uFF1A\u9876\u90E8\u4E0B\u62C9\u3001\u6DF1\u8272\u80F6\u56CA\uFF09 ---- */
|
|
2024
|
+
.universal-card-toast-host {
|
|
2025
|
+
position: fixed;
|
|
2026
|
+
top: 32px; /* \u5BA2\u6237\u7AEF\u7EC4\u4EF6\u914D\u7F6E toast:{top:32} */
|
|
2027
|
+
left: 50%;
|
|
2028
|
+
transform: translateX(-50%);
|
|
2029
|
+
display: flex;
|
|
2030
|
+
flex-direction: column;
|
|
2031
|
+
align-items: center;
|
|
2032
|
+
z-index: 300;
|
|
2033
|
+
pointer-events: none;
|
|
2034
|
+
}
|
|
2035
|
+
.universal-card-toast {
|
|
2036
|
+
display: inline-flex;
|
|
2037
|
+
align-items: center;
|
|
2038
|
+
gap: 8px;
|
|
2039
|
+
max-width: 600px; /* \u5BA2\u6237\u7AEF .ud__toast max-width:600px */
|
|
2040
|
+
padding: 10px 16px;
|
|
2041
|
+
border-radius: 8px;
|
|
2042
|
+
background: rgba(31, 35, 41, 0.92); /* N900 \u6DF1\u8272\u534A\u900F\u660E\uFF0C\u767D\u5B57 */
|
|
2043
|
+
color: #fff;
|
|
2044
|
+
font-size: 14px;
|
|
2045
|
+
line-height: 1.5;
|
|
2046
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16);
|
|
2047
|
+
margin-bottom: 16px; /* \u5BA2\u6237\u7AEF .ud__toast margin-bottom:16px */
|
|
2048
|
+
animation: universal-card-toast-in 0.3s cubic-bezier(0.2, 0, 0, 1);
|
|
2049
|
+
}
|
|
2050
|
+
.universal-card-toast--leave {
|
|
2051
|
+
animation: universal-card-toast-out 0.3s cubic-bezier(0.2, 0, 0, 1) forwards;
|
|
2052
|
+
}
|
|
2053
|
+
.universal-card-toast__icon {
|
|
2054
|
+
flex: none;
|
|
2055
|
+
display: inline-flex;
|
|
2056
|
+
}
|
|
2057
|
+
.universal-card-toast__text {
|
|
2058
|
+
display: -webkit-box;
|
|
2059
|
+
-webkit-box-orient: vertical;
|
|
2060
|
+
overflow: hidden;
|
|
2061
|
+
word-break: break-word;
|
|
2062
|
+
}
|
|
2063
|
+
@keyframes universal-card-toast-in {
|
|
2064
|
+
from { opacity: 0; transform: translateY(-100%); }
|
|
2065
|
+
to { opacity: 1; transform: translateY(0); }
|
|
2066
|
+
}
|
|
2067
|
+
@keyframes universal-card-toast-out {
|
|
2068
|
+
from { opacity: 1; transform: translateY(0); }
|
|
2069
|
+
to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
|
|
2070
|
+
}
|
|
2020
2071
|
`;
|
|
2021
2072
|
|
|
2022
2073
|
// src/icon-font.ts
|
|
@@ -6816,6 +6867,60 @@ function feishuCardStyleTag() {
|
|
|
6816
6867
|
return `<style data-feishu-card>${ALL_CSS}</style>`;
|
|
6817
6868
|
}
|
|
6818
6869
|
|
|
6870
|
+
// src/toast.ts
|
|
6871
|
+
var DEFAULT_SHOW_TIME = 2e3;
|
|
6872
|
+
var TYPE_ICON = {
|
|
6873
|
+
success: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#34c724"/><path d="M4.5 8.2l2.2 2.2 4.8-4.8" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
|
6874
|
+
info: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#3370ff"/><circle cx="8" cy="4.6" r="1" fill="#fff"/><rect x="7.2" y="7" width="1.6" height="4.2" rx="0.8" fill="#fff"/></svg>',
|
|
6875
|
+
warning: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#fad355"/><rect x="7.2" y="3.8" width="1.6" height="4.6" rx="0.8" fill="#1f2329"/><circle cx="8" cy="11.8" r="1" fill="#1f2329"/></svg>',
|
|
6876
|
+
error: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#f54a45"/><path d="M5.6 5.6l4.8 4.8M10.4 5.6l-4.8 4.8" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>'
|
|
6877
|
+
};
|
|
6878
|
+
function escapeHTML(s) {
|
|
6879
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6880
|
+
}
|
|
6881
|
+
function renderToastHTML(type, opts) {
|
|
6882
|
+
const maxLines = opts.maxLines ?? 1;
|
|
6883
|
+
const styleAttr = maxLines > 1 ? ` style="-webkit-line-clamp:${maxLines};"` : "";
|
|
6884
|
+
return `<div class="universal-card-toast universal-card-toast--${type}" role="status" aria-live="polite"><span class="universal-card-toast__icon">${TYPE_ICON[type]}</span><span class="universal-card-toast__text"${styleAttr}>${escapeHTML(opts.content)}</span></div>`;
|
|
6885
|
+
}
|
|
6886
|
+
function createToastHost(container) {
|
|
6887
|
+
const hostEl = typeof document === "undefined" ? null : container ?? document.body;
|
|
6888
|
+
const ensureHost = () => {
|
|
6889
|
+
if (!hostEl) return null;
|
|
6890
|
+
let el = hostEl.querySelector(".universal-card-toast-host");
|
|
6891
|
+
if (!el) {
|
|
6892
|
+
el = document.createElement("div");
|
|
6893
|
+
el.className = "universal-card-toast-host";
|
|
6894
|
+
hostEl.appendChild(el);
|
|
6895
|
+
}
|
|
6896
|
+
return el;
|
|
6897
|
+
};
|
|
6898
|
+
const push = (type) => (opts) => {
|
|
6899
|
+
const host = ensureHost();
|
|
6900
|
+
if (!host || typeof document === "undefined") return;
|
|
6901
|
+
const wrap = document.createElement("div");
|
|
6902
|
+
wrap.style.cssText = "display:contents";
|
|
6903
|
+
wrap.innerHTML = renderToastHTML(type, opts);
|
|
6904
|
+
const toastEl = wrap.firstElementChild;
|
|
6905
|
+
host.appendChild(toastEl);
|
|
6906
|
+
const showTime = opts.showTime ?? DEFAULT_SHOW_TIME;
|
|
6907
|
+
setTimeout(() => {
|
|
6908
|
+
toastEl.classList.add("universal-card-toast--leave");
|
|
6909
|
+
toastEl.addEventListener("animationend", () => toastEl.remove(), { once: true });
|
|
6910
|
+
setTimeout(() => toastEl.remove(), 400);
|
|
6911
|
+
}, showTime);
|
|
6912
|
+
};
|
|
6913
|
+
return {
|
|
6914
|
+
success: push("success"),
|
|
6915
|
+
info: push("info"),
|
|
6916
|
+
warning: push("warning"),
|
|
6917
|
+
error: push("error"),
|
|
6918
|
+
destroy: () => {
|
|
6919
|
+
hostEl?.querySelectorAll(".universal-card-toast-host .universal-card-toast").forEach((el) => el.remove());
|
|
6920
|
+
}
|
|
6921
|
+
};
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6819
6924
|
// src/remote.ts
|
|
6820
6925
|
var DEFAULT_API_BASE = "https://open.feishu.cn/open-apis/im/v1";
|
|
6821
6926
|
function getFetch(options) {
|
|
@@ -7072,6 +7177,7 @@ exports.HEADER_BG_LIGHT = HEADER_BG_LIGHT;
|
|
|
7072
7177
|
exports.ICON_CODEPOINTS = ICON_CODEPOINTS;
|
|
7073
7178
|
exports.ICON_FONT_CSS = ICON_FONT_CSS;
|
|
7074
7179
|
exports.arrayBufferToBase64 = arrayBufferToBase64;
|
|
7180
|
+
exports.createToastHost = createToastHost;
|
|
7075
7181
|
exports.feishuAudioHydrateJs = feishuAudioHydrateJs;
|
|
7076
7182
|
exports.feishuAudioHydrateScript = feishuAudioHydrateScript;
|
|
7077
7183
|
exports.feishuCardStyleTag = feishuCardStyleTag;
|
|
@@ -7095,3 +7201,4 @@ exports.renderCardToStringAsync = renderCardToStringAsync;
|
|
|
7095
7201
|
exports.renderDsl = renderDsl;
|
|
7096
7202
|
exports.renderDslToString = renderDslToString;
|
|
7097
7203
|
exports.renderFeishuChart = renderFeishuChart;
|
|
7204
|
+
exports.renderToastHTML = renderToastHTML;
|
package/dist/index.d.cts
CHANGED
|
@@ -81,6 +81,8 @@ interface ElementProperty {
|
|
|
81
81
|
hoverTips?: CardElement;
|
|
82
82
|
hasBorder?: boolean;
|
|
83
83
|
imageID?: string;
|
|
84
|
+
/** DSL 公开字段;dslToRaw 常原样留在 property(未改名为 imageID) */
|
|
85
|
+
img_key?: string;
|
|
84
86
|
origin_key?: string;
|
|
85
87
|
src?: string;
|
|
86
88
|
url?: string | LinkURL;
|
|
@@ -291,7 +293,7 @@ interface RenderOptions {
|
|
|
291
293
|
/** 音视频地址前缀:最终 src = mediaBaseURL + fileID */
|
|
292
294
|
mediaBaseURL?: string;
|
|
293
295
|
/**
|
|
294
|
-
* 人员表(open_id -> { content 姓名, avatar_key 头像 })。
|
|
296
|
+
* 人员表(open_id -> { content 姓名, avatar_key / avatar_url 头像 })。
|
|
295
297
|
* person_v1 / person_list 元素只带 ID,姓名/头像在此反查。
|
|
296
298
|
* 异步渲染(renderCardToStringAsync)会自动从 json_attachment.persons 填入;
|
|
297
299
|
* 同步渲染若想出名字/头像,需手动传入。缺省时人员降级为固定头像 +「赵天雄」。
|
|
@@ -326,7 +328,8 @@ interface RenderOptions {
|
|
|
326
328
|
* 人员信息:person_v1 / person_list / at 元素只带 ID,姓名与头像 key 在这里。
|
|
327
329
|
* 取自飞书消息 content 的 json_attachment.persons(按 open_id「ou_...」索引);
|
|
328
330
|
* fetchCardMessage 还会把信封 mentions 与 at_users 合并进来(含 @_user_N 别名键)。
|
|
329
|
-
* avatar_key 是公开可访问的头像图 key(无需鉴权,见 PERSON_AVATAR_BASE
|
|
331
|
+
* avatar_key 是公开可访问的头像图 key(无需鉴权,见 PERSON_AVATAR_BASE);
|
|
332
|
+
* 本地预览也可用 avatar_url(含 data URI)直接给头像地址。
|
|
330
333
|
*/
|
|
331
334
|
interface PersonInfo {
|
|
332
335
|
person_id?: string;
|
|
@@ -335,6 +338,11 @@ interface PersonInfo {
|
|
|
335
338
|
type?: number;
|
|
336
339
|
/** 头像图 key(形如 v3_00te_...),拼 avatarBaseURL 取图 */
|
|
337
340
|
avatar_key?: string;
|
|
341
|
+
/**
|
|
342
|
+
* 直接可用的头像 URL(含 data URI)。
|
|
343
|
+
* 本地预览虚拟用户没有飞书 avatar_key 时用这个;优先级低于 resolvePersonAvatar,高于 avatar_key。
|
|
344
|
+
*/
|
|
345
|
+
avatar_url?: string;
|
|
338
346
|
}
|
|
339
347
|
/**
|
|
340
348
|
* 消息信封上的 @ 提及(data.items[0].mentions),不在 body.content 内。
|
|
@@ -606,6 +614,45 @@ declare function feishuAudioHydrateJs(): string;
|
|
|
606
614
|
*/
|
|
607
615
|
declare const CHART_PALETTE: string[];
|
|
608
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Toast 宿主(对齐飞书客户端 kernel.toast)。
|
|
619
|
+
*
|
|
620
|
+
* 事实依据(feishu-card-extract,pcsdk 7.72.0):
|
|
621
|
+
* - 卡片 action 请求响应里带 `toast: { code, content, ... }`(如复制成功、表单校验失败、
|
|
622
|
+
* 错误码提示),Kernel 收到后调 `options.toast.success/info/warning/error` 弹出。
|
|
623
|
+
* - 调用形态:`{ type: "TOAST_SUCCESS", content, showTime: 2e3, maxLines: 1 }`。
|
|
624
|
+
* - DOM 为 `.ud__toast.ud__notice`:顶部下拉动画(translateY(-100%)→0)、
|
|
625
|
+
* max-width:600px、box-shadow、margin-bottom:16px、top:32px。
|
|
626
|
+
*
|
|
627
|
+
* renderer 是 SSR 字符串渲染器,此处提供浏览器侧 toast 宿主:预览交互里模拟
|
|
628
|
+
* action 响应(如按钮点击回调)时调用对应方法,视觉与客户端一致。
|
|
629
|
+
*/
|
|
630
|
+
type ToastType = 'success' | 'info' | 'warning' | 'error';
|
|
631
|
+
interface ToastOptions {
|
|
632
|
+
/** toast 文本内容 */
|
|
633
|
+
content: string;
|
|
634
|
+
/** 显示时长 ms,默认 2000(客户端 showTime 常见值 2e3) */
|
|
635
|
+
showTime?: number;
|
|
636
|
+
/** 最大行数,超出截断省略,默认 1(客户端 maxLines:1) */
|
|
637
|
+
maxLines?: number;
|
|
638
|
+
}
|
|
639
|
+
interface ToastAPI {
|
|
640
|
+
success: (opts: ToastOptions) => void;
|
|
641
|
+
info: (opts: ToastOptions) => void;
|
|
642
|
+
warning: (opts: ToastOptions) => void;
|
|
643
|
+
error: (opts: ToastOptions) => void;
|
|
644
|
+
/** 清空当前所有 toast */
|
|
645
|
+
destroy: () => void;
|
|
646
|
+
}
|
|
647
|
+
/** 生成单个 toast 的 HTML 字符串(纯函数,SSR/测试可用) */
|
|
648
|
+
declare function renderToastHTML(type: ToastType, opts: ToastOptions): string;
|
|
649
|
+
/**
|
|
650
|
+
* 创建 toast 宿主(浏览器侧)。容器挂在 container(默认 document.body),
|
|
651
|
+
* fixed 顶部居中;每次调用追加一条 toast,showTime 后自动移除。
|
|
652
|
+
* Node/SSR 环境无 DOM 时返回 no-op API(不抛错),仅可用 renderToastHTML。
|
|
653
|
+
*/
|
|
654
|
+
declare function createToastHost(container?: HTMLElement | null): ToastAPI;
|
|
655
|
+
|
|
609
656
|
/**
|
|
610
657
|
* 异步渲染:用飞书 access token 自动拉取卡片图片并内联(data URI),
|
|
611
658
|
* 或直接按 message_id 把整条消息拉回来再渲染。
|
|
@@ -681,4 +728,4 @@ declare function renderDslToString(dsl: unknown, options?: RenderOptions): strin
|
|
|
681
728
|
/** 输入公开 DSL,渲染为真实 DOM 节点(浏览器)。= renderCard(dslToRaw(dsl)) */
|
|
682
729
|
declare function renderDsl(dsl: unknown, options?: RenderOptions): Node;
|
|
683
730
|
|
|
684
|
-
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, type AsyncRenderOptions, type AtUserInfo, type BoxSpacing, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, type CardElement, type CardInput, type CardMessageInput, type ComponentFn, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, type ElementProperty, type FetchedCardMessage, HEADER_BG_DARK, HEADER_BG_LIGHT, type HeaderTemplate, ICON_CODEPOINTS, ICON_FONT_CSS, type JsonAttachment, type JsonCard, type LinkURL, type ListItem, type MessageMention, type NamedColor, type PersonInfo, type RenderContext, type RenderOptions, type SizeName, type TableColumn, type TableHeaderStyle, type TextAlign, type TextAttribute, type TextStyle, type VNode, type VNodeChild, type VNodeProps, arrayBufferToBase64, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart };
|
|
731
|
+
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, type AsyncRenderOptions, type AtUserInfo, type BoxSpacing, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, type CardElement, type CardInput, type CardMessageInput, type ComponentFn, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, type ElementProperty, type FetchedCardMessage, HEADER_BG_DARK, HEADER_BG_LIGHT, type HeaderTemplate, ICON_CODEPOINTS, ICON_FONT_CSS, type JsonAttachment, type JsonCard, type LinkURL, type ListItem, type MessageMention, type NamedColor, type PersonInfo, type RenderContext, type RenderOptions, type SizeName, type TableColumn, type TableHeaderStyle, type TextAlign, type TextAttribute, type TextStyle, type ToastAPI, type ToastOptions, type ToastType, type VNode, type VNodeChild, type VNodeProps, arrayBufferToBase64, createToastHost, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart, renderToastHTML };
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,8 @@ interface ElementProperty {
|
|
|
81
81
|
hoverTips?: CardElement;
|
|
82
82
|
hasBorder?: boolean;
|
|
83
83
|
imageID?: string;
|
|
84
|
+
/** DSL 公开字段;dslToRaw 常原样留在 property(未改名为 imageID) */
|
|
85
|
+
img_key?: string;
|
|
84
86
|
origin_key?: string;
|
|
85
87
|
src?: string;
|
|
86
88
|
url?: string | LinkURL;
|
|
@@ -291,7 +293,7 @@ interface RenderOptions {
|
|
|
291
293
|
/** 音视频地址前缀:最终 src = mediaBaseURL + fileID */
|
|
292
294
|
mediaBaseURL?: string;
|
|
293
295
|
/**
|
|
294
|
-
* 人员表(open_id -> { content 姓名, avatar_key 头像 })。
|
|
296
|
+
* 人员表(open_id -> { content 姓名, avatar_key / avatar_url 头像 })。
|
|
295
297
|
* person_v1 / person_list 元素只带 ID,姓名/头像在此反查。
|
|
296
298
|
* 异步渲染(renderCardToStringAsync)会自动从 json_attachment.persons 填入;
|
|
297
299
|
* 同步渲染若想出名字/头像,需手动传入。缺省时人员降级为固定头像 +「赵天雄」。
|
|
@@ -326,7 +328,8 @@ interface RenderOptions {
|
|
|
326
328
|
* 人员信息:person_v1 / person_list / at 元素只带 ID,姓名与头像 key 在这里。
|
|
327
329
|
* 取自飞书消息 content 的 json_attachment.persons(按 open_id「ou_...」索引);
|
|
328
330
|
* fetchCardMessage 还会把信封 mentions 与 at_users 合并进来(含 @_user_N 别名键)。
|
|
329
|
-
* avatar_key 是公开可访问的头像图 key(无需鉴权,见 PERSON_AVATAR_BASE
|
|
331
|
+
* avatar_key 是公开可访问的头像图 key(无需鉴权,见 PERSON_AVATAR_BASE);
|
|
332
|
+
* 本地预览也可用 avatar_url(含 data URI)直接给头像地址。
|
|
330
333
|
*/
|
|
331
334
|
interface PersonInfo {
|
|
332
335
|
person_id?: string;
|
|
@@ -335,6 +338,11 @@ interface PersonInfo {
|
|
|
335
338
|
type?: number;
|
|
336
339
|
/** 头像图 key(形如 v3_00te_...),拼 avatarBaseURL 取图 */
|
|
337
340
|
avatar_key?: string;
|
|
341
|
+
/**
|
|
342
|
+
* 直接可用的头像 URL(含 data URI)。
|
|
343
|
+
* 本地预览虚拟用户没有飞书 avatar_key 时用这个;优先级低于 resolvePersonAvatar,高于 avatar_key。
|
|
344
|
+
*/
|
|
345
|
+
avatar_url?: string;
|
|
338
346
|
}
|
|
339
347
|
/**
|
|
340
348
|
* 消息信封上的 @ 提及(data.items[0].mentions),不在 body.content 内。
|
|
@@ -606,6 +614,45 @@ declare function feishuAudioHydrateJs(): string;
|
|
|
606
614
|
*/
|
|
607
615
|
declare const CHART_PALETTE: string[];
|
|
608
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Toast 宿主(对齐飞书客户端 kernel.toast)。
|
|
619
|
+
*
|
|
620
|
+
* 事实依据(feishu-card-extract,pcsdk 7.72.0):
|
|
621
|
+
* - 卡片 action 请求响应里带 `toast: { code, content, ... }`(如复制成功、表单校验失败、
|
|
622
|
+
* 错误码提示),Kernel 收到后调 `options.toast.success/info/warning/error` 弹出。
|
|
623
|
+
* - 调用形态:`{ type: "TOAST_SUCCESS", content, showTime: 2e3, maxLines: 1 }`。
|
|
624
|
+
* - DOM 为 `.ud__toast.ud__notice`:顶部下拉动画(translateY(-100%)→0)、
|
|
625
|
+
* max-width:600px、box-shadow、margin-bottom:16px、top:32px。
|
|
626
|
+
*
|
|
627
|
+
* renderer 是 SSR 字符串渲染器,此处提供浏览器侧 toast 宿主:预览交互里模拟
|
|
628
|
+
* action 响应(如按钮点击回调)时调用对应方法,视觉与客户端一致。
|
|
629
|
+
*/
|
|
630
|
+
type ToastType = 'success' | 'info' | 'warning' | 'error';
|
|
631
|
+
interface ToastOptions {
|
|
632
|
+
/** toast 文本内容 */
|
|
633
|
+
content: string;
|
|
634
|
+
/** 显示时长 ms,默认 2000(客户端 showTime 常见值 2e3) */
|
|
635
|
+
showTime?: number;
|
|
636
|
+
/** 最大行数,超出截断省略,默认 1(客户端 maxLines:1) */
|
|
637
|
+
maxLines?: number;
|
|
638
|
+
}
|
|
639
|
+
interface ToastAPI {
|
|
640
|
+
success: (opts: ToastOptions) => void;
|
|
641
|
+
info: (opts: ToastOptions) => void;
|
|
642
|
+
warning: (opts: ToastOptions) => void;
|
|
643
|
+
error: (opts: ToastOptions) => void;
|
|
644
|
+
/** 清空当前所有 toast */
|
|
645
|
+
destroy: () => void;
|
|
646
|
+
}
|
|
647
|
+
/** 生成单个 toast 的 HTML 字符串(纯函数,SSR/测试可用) */
|
|
648
|
+
declare function renderToastHTML(type: ToastType, opts: ToastOptions): string;
|
|
649
|
+
/**
|
|
650
|
+
* 创建 toast 宿主(浏览器侧)。容器挂在 container(默认 document.body),
|
|
651
|
+
* fixed 顶部居中;每次调用追加一条 toast,showTime 后自动移除。
|
|
652
|
+
* Node/SSR 环境无 DOM 时返回 no-op API(不抛错),仅可用 renderToastHTML。
|
|
653
|
+
*/
|
|
654
|
+
declare function createToastHost(container?: HTMLElement | null): ToastAPI;
|
|
655
|
+
|
|
609
656
|
/**
|
|
610
657
|
* 异步渲染:用飞书 access token 自动拉取卡片图片并内联(data URI),
|
|
611
658
|
* 或直接按 message_id 把整条消息拉回来再渲染。
|
|
@@ -681,4 +728,4 @@ declare function renderDslToString(dsl: unknown, options?: RenderOptions): strin
|
|
|
681
728
|
/** 输入公开 DSL,渲染为真实 DOM 节点(浏览器)。= renderCard(dslToRaw(dsl)) */
|
|
682
729
|
declare function renderDsl(dsl: unknown, options?: RenderOptions): Node;
|
|
683
730
|
|
|
684
|
-
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, type AsyncRenderOptions, type AtUserInfo, type BoxSpacing, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, type CardElement, type CardInput, type CardMessageInput, type ComponentFn, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, type ElementProperty, type FetchedCardMessage, HEADER_BG_DARK, HEADER_BG_LIGHT, type HeaderTemplate, ICON_CODEPOINTS, ICON_FONT_CSS, type JsonAttachment, type JsonCard, type LinkURL, type ListItem, type MessageMention, type NamedColor, type PersonInfo, type RenderContext, type RenderOptions, type SizeName, type TableColumn, type TableHeaderStyle, type TextAlign, type TextAttribute, type TextStyle, type VNode, type VNodeChild, type VNodeProps, arrayBufferToBase64, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart };
|
|
731
|
+
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, type AsyncRenderOptions, type AtUserInfo, type BoxSpacing, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, type CardElement, type CardInput, type CardMessageInput, type ComponentFn, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, type ElementProperty, type FetchedCardMessage, HEADER_BG_DARK, HEADER_BG_LIGHT, type HeaderTemplate, ICON_CODEPOINTS, ICON_FONT_CSS, type JsonAttachment, type JsonCard, type LinkURL, type ListItem, type MessageMention, type NamedColor, type PersonInfo, type RenderContext, type RenderOptions, type SizeName, type TableColumn, type TableHeaderStyle, type TextAlign, type TextAttribute, type TextStyle, type ToastAPI, type ToastOptions, type ToastType, type VNode, type VNodeChild, type VNodeProps, arrayBufferToBase64, createToastHost, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart, renderToastHTML };
|
package/dist/index.js
CHANGED
|
@@ -670,7 +670,8 @@ function resolveImageSrc(p, ctx) {
|
|
|
670
670
|
if (p.src) return p.src;
|
|
671
671
|
if (p.url) return resolveURL(p.url) || null;
|
|
672
672
|
if (p.img_url) return p.img_url;
|
|
673
|
-
|
|
673
|
+
const imageId = p.imageID || p.img_key;
|
|
674
|
+
if (imageId && ctx.options.imageBaseURL) return ctx.options.imageBaseURL + imageId;
|
|
674
675
|
return null;
|
|
675
676
|
}
|
|
676
677
|
function resolveMediaSrc(p, kind, ctx) {
|
|
@@ -700,6 +701,7 @@ function personAvatarSrc(person, ctx) {
|
|
|
700
701
|
const r = ctx.options.resolvePersonAvatar(person);
|
|
701
702
|
if (r) return r;
|
|
702
703
|
}
|
|
704
|
+
if (person.avatar_url) return person.avatar_url;
|
|
703
705
|
if (!person.avatar_key) return null;
|
|
704
706
|
const base = ctx.options.avatarBaseURL || PERSON_AVATAR_BASE;
|
|
705
707
|
const suffix = ctx.options.avatarURLSuffix ?? PERSON_AVATAR_SUFFIX;
|
|
@@ -2016,6 +2018,55 @@ var BASE_STYLES_CSS = `/**
|
|
|
2016
2018
|
}
|
|
2017
2019
|
.universal-card-root.theme-dark .universal-card-markdown { color: #d6d6d6; }
|
|
2018
2020
|
.universal-card-root.theme-dark .universal-card-hr { background-color: #2e2e30; }
|
|
2021
|
+
|
|
2022
|
+
/* ---- Toast \u5BBF\u4E3B\uFF08\u5BF9\u9F50\u5BA2\u6237\u7AEF .ud__toast.ud__notice\uFF1A\u9876\u90E8\u4E0B\u62C9\u3001\u6DF1\u8272\u80F6\u56CA\uFF09 ---- */
|
|
2023
|
+
.universal-card-toast-host {
|
|
2024
|
+
position: fixed;
|
|
2025
|
+
top: 32px; /* \u5BA2\u6237\u7AEF\u7EC4\u4EF6\u914D\u7F6E toast:{top:32} */
|
|
2026
|
+
left: 50%;
|
|
2027
|
+
transform: translateX(-50%);
|
|
2028
|
+
display: flex;
|
|
2029
|
+
flex-direction: column;
|
|
2030
|
+
align-items: center;
|
|
2031
|
+
z-index: 300;
|
|
2032
|
+
pointer-events: none;
|
|
2033
|
+
}
|
|
2034
|
+
.universal-card-toast {
|
|
2035
|
+
display: inline-flex;
|
|
2036
|
+
align-items: center;
|
|
2037
|
+
gap: 8px;
|
|
2038
|
+
max-width: 600px; /* \u5BA2\u6237\u7AEF .ud__toast max-width:600px */
|
|
2039
|
+
padding: 10px 16px;
|
|
2040
|
+
border-radius: 8px;
|
|
2041
|
+
background: rgba(31, 35, 41, 0.92); /* N900 \u6DF1\u8272\u534A\u900F\u660E\uFF0C\u767D\u5B57 */
|
|
2042
|
+
color: #fff;
|
|
2043
|
+
font-size: 14px;
|
|
2044
|
+
line-height: 1.5;
|
|
2045
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16);
|
|
2046
|
+
margin-bottom: 16px; /* \u5BA2\u6237\u7AEF .ud__toast margin-bottom:16px */
|
|
2047
|
+
animation: universal-card-toast-in 0.3s cubic-bezier(0.2, 0, 0, 1);
|
|
2048
|
+
}
|
|
2049
|
+
.universal-card-toast--leave {
|
|
2050
|
+
animation: universal-card-toast-out 0.3s cubic-bezier(0.2, 0, 0, 1) forwards;
|
|
2051
|
+
}
|
|
2052
|
+
.universal-card-toast__icon {
|
|
2053
|
+
flex: none;
|
|
2054
|
+
display: inline-flex;
|
|
2055
|
+
}
|
|
2056
|
+
.universal-card-toast__text {
|
|
2057
|
+
display: -webkit-box;
|
|
2058
|
+
-webkit-box-orient: vertical;
|
|
2059
|
+
overflow: hidden;
|
|
2060
|
+
word-break: break-word;
|
|
2061
|
+
}
|
|
2062
|
+
@keyframes universal-card-toast-in {
|
|
2063
|
+
from { opacity: 0; transform: translateY(-100%); }
|
|
2064
|
+
to { opacity: 1; transform: translateY(0); }
|
|
2065
|
+
}
|
|
2066
|
+
@keyframes universal-card-toast-out {
|
|
2067
|
+
from { opacity: 1; transform: translateY(0); }
|
|
2068
|
+
to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
|
|
2069
|
+
}
|
|
2019
2070
|
`;
|
|
2020
2071
|
|
|
2021
2072
|
// src/icon-font.ts
|
|
@@ -6815,6 +6866,60 @@ function feishuCardStyleTag() {
|
|
|
6815
6866
|
return `<style data-feishu-card>${ALL_CSS}</style>`;
|
|
6816
6867
|
}
|
|
6817
6868
|
|
|
6869
|
+
// src/toast.ts
|
|
6870
|
+
var DEFAULT_SHOW_TIME = 2e3;
|
|
6871
|
+
var TYPE_ICON = {
|
|
6872
|
+
success: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#34c724"/><path d="M4.5 8.2l2.2 2.2 4.8-4.8" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
|
6873
|
+
info: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#3370ff"/><circle cx="8" cy="4.6" r="1" fill="#fff"/><rect x="7.2" y="7" width="1.6" height="4.2" rx="0.8" fill="#fff"/></svg>',
|
|
6874
|
+
warning: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#fad355"/><rect x="7.2" y="3.8" width="1.6" height="4.6" rx="0.8" fill="#1f2329"/><circle cx="8" cy="11.8" r="1" fill="#1f2329"/></svg>',
|
|
6875
|
+
error: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><circle cx="8" cy="8" r="7" fill="#f54a45"/><path d="M5.6 5.6l4.8 4.8M10.4 5.6l-4.8 4.8" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>'
|
|
6876
|
+
};
|
|
6877
|
+
function escapeHTML(s) {
|
|
6878
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6879
|
+
}
|
|
6880
|
+
function renderToastHTML(type, opts) {
|
|
6881
|
+
const maxLines = opts.maxLines ?? 1;
|
|
6882
|
+
const styleAttr = maxLines > 1 ? ` style="-webkit-line-clamp:${maxLines};"` : "";
|
|
6883
|
+
return `<div class="universal-card-toast universal-card-toast--${type}" role="status" aria-live="polite"><span class="universal-card-toast__icon">${TYPE_ICON[type]}</span><span class="universal-card-toast__text"${styleAttr}>${escapeHTML(opts.content)}</span></div>`;
|
|
6884
|
+
}
|
|
6885
|
+
function createToastHost(container) {
|
|
6886
|
+
const hostEl = typeof document === "undefined" ? null : container ?? document.body;
|
|
6887
|
+
const ensureHost = () => {
|
|
6888
|
+
if (!hostEl) return null;
|
|
6889
|
+
let el = hostEl.querySelector(".universal-card-toast-host");
|
|
6890
|
+
if (!el) {
|
|
6891
|
+
el = document.createElement("div");
|
|
6892
|
+
el.className = "universal-card-toast-host";
|
|
6893
|
+
hostEl.appendChild(el);
|
|
6894
|
+
}
|
|
6895
|
+
return el;
|
|
6896
|
+
};
|
|
6897
|
+
const push = (type) => (opts) => {
|
|
6898
|
+
const host = ensureHost();
|
|
6899
|
+
if (!host || typeof document === "undefined") return;
|
|
6900
|
+
const wrap = document.createElement("div");
|
|
6901
|
+
wrap.style.cssText = "display:contents";
|
|
6902
|
+
wrap.innerHTML = renderToastHTML(type, opts);
|
|
6903
|
+
const toastEl = wrap.firstElementChild;
|
|
6904
|
+
host.appendChild(toastEl);
|
|
6905
|
+
const showTime = opts.showTime ?? DEFAULT_SHOW_TIME;
|
|
6906
|
+
setTimeout(() => {
|
|
6907
|
+
toastEl.classList.add("universal-card-toast--leave");
|
|
6908
|
+
toastEl.addEventListener("animationend", () => toastEl.remove(), { once: true });
|
|
6909
|
+
setTimeout(() => toastEl.remove(), 400);
|
|
6910
|
+
}, showTime);
|
|
6911
|
+
};
|
|
6912
|
+
return {
|
|
6913
|
+
success: push("success"),
|
|
6914
|
+
info: push("info"),
|
|
6915
|
+
warning: push("warning"),
|
|
6916
|
+
error: push("error"),
|
|
6917
|
+
destroy: () => {
|
|
6918
|
+
hostEl?.querySelectorAll(".universal-card-toast-host .universal-card-toast").forEach((el) => el.remove());
|
|
6919
|
+
}
|
|
6920
|
+
};
|
|
6921
|
+
}
|
|
6922
|
+
|
|
6818
6923
|
// src/remote.ts
|
|
6819
6924
|
var DEFAULT_API_BASE = "https://open.feishu.cn/open-apis/im/v1";
|
|
6820
6925
|
function getFetch(options) {
|
|
@@ -7053,4 +7158,4 @@ function renderDsl(dsl, options = {}) {
|
|
|
7053
7158
|
return renderCard(dslToRaw(dsl), options);
|
|
7054
7159
|
}
|
|
7055
7160
|
|
|
7056
|
-
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, HEADER_BG_DARK, HEADER_BG_LIGHT, ICON_CODEPOINTS, ICON_FONT_CSS, arrayBufferToBase64, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart };
|
|
7161
|
+
export { ALL_CSS, AUDIO_HYDRATED_ATTR, AUDIO_PAUSE_ICON_SVG, AUDIO_PLAY_ICON_SVG, CHART_HYDRATED_ATTR, CHART_PALETTE, CHART_SPEC_ATTR, EMOJI_KEY_TO_NAME, EMOJI_SPRITE_CSS, HEADER_BG_DARK, HEADER_BG_LIGHT, ICON_CODEPOINTS, ICON_FONT_CSS, arrayBufferToBase64, createToastHost, feishuAudioHydrateJs, feishuAudioHydrateScript, feishuCardStyleTag, feishuChartHydrateJs, feishuChartHydrateScript, fetchCardMessage, fetchImageDataUri, getComponent, h, hydrateFeishuAudios, hydrateFeishuCharts, mergeMentionsIntoPersons, parseInline, parseMarkdown, prefetchImages, registerComponent, renderCard, renderCardAsync, renderCardToString, renderCardToStringAsync, renderDsl, renderDslToString, renderFeishuChart, renderToastHTML };
|
package/dist/styles.css
CHANGED
|
@@ -1049,3 +1049,52 @@
|
|
|
1049
1049
|
}
|
|
1050
1050
|
.universal-card-root.theme-dark .universal-card-markdown { color: #d6d6d6; }
|
|
1051
1051
|
.universal-card-root.theme-dark .universal-card-hr { background-color: #2e2e30; }
|
|
1052
|
+
|
|
1053
|
+
/* ---- Toast 宿主(对齐客户端 .ud__toast.ud__notice:顶部下拉、深色胶囊) ---- */
|
|
1054
|
+
.universal-card-toast-host {
|
|
1055
|
+
position: fixed;
|
|
1056
|
+
top: 32px; /* 客户端组件配置 toast:{top:32} */
|
|
1057
|
+
left: 50%;
|
|
1058
|
+
transform: translateX(-50%);
|
|
1059
|
+
display: flex;
|
|
1060
|
+
flex-direction: column;
|
|
1061
|
+
align-items: center;
|
|
1062
|
+
z-index: 300;
|
|
1063
|
+
pointer-events: none;
|
|
1064
|
+
}
|
|
1065
|
+
.universal-card-toast {
|
|
1066
|
+
display: inline-flex;
|
|
1067
|
+
align-items: center;
|
|
1068
|
+
gap: 8px;
|
|
1069
|
+
max-width: 600px; /* 客户端 .ud__toast max-width:600px */
|
|
1070
|
+
padding: 10px 16px;
|
|
1071
|
+
border-radius: 8px;
|
|
1072
|
+
background: rgba(31, 35, 41, 0.92); /* N900 深色半透明,白字 */
|
|
1073
|
+
color: #fff;
|
|
1074
|
+
font-size: 14px;
|
|
1075
|
+
line-height: 1.5;
|
|
1076
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16);
|
|
1077
|
+
margin-bottom: 16px; /* 客户端 .ud__toast margin-bottom:16px */
|
|
1078
|
+
animation: universal-card-toast-in 0.3s cubic-bezier(0.2, 0, 0, 1);
|
|
1079
|
+
}
|
|
1080
|
+
.universal-card-toast--leave {
|
|
1081
|
+
animation: universal-card-toast-out 0.3s cubic-bezier(0.2, 0, 0, 1) forwards;
|
|
1082
|
+
}
|
|
1083
|
+
.universal-card-toast__icon {
|
|
1084
|
+
flex: none;
|
|
1085
|
+
display: inline-flex;
|
|
1086
|
+
}
|
|
1087
|
+
.universal-card-toast__text {
|
|
1088
|
+
display: -webkit-box;
|
|
1089
|
+
-webkit-box-orient: vertical;
|
|
1090
|
+
overflow: hidden;
|
|
1091
|
+
word-break: break-word;
|
|
1092
|
+
}
|
|
1093
|
+
@keyframes universal-card-toast-in {
|
|
1094
|
+
from { opacity: 0; transform: translateY(-100%); }
|
|
1095
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1096
|
+
}
|
|
1097
|
+
@keyframes universal-card-toast-out {
|
|
1098
|
+
from { opacity: 1; transform: translateY(0); }
|
|
1099
|
+
to { opacity: 0; transform: translateY(-100%); margin-bottom: 0; }
|
|
1100
|
+
}
|