@open-feishu-card/adapter 0.2.1 → 0.3.1
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 +979 -162
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +978 -163
- package/dist/raw-to-dsl.d.ts +60 -6
- package/dist/raw-to-dsl.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -642,6 +642,10 @@ function convertHeader(header) {
|
|
|
642
642
|
const property = {};
|
|
643
643
|
for (const [k, v] of Object.entries(header)) {
|
|
644
644
|
if (k === "tag") continue;
|
|
645
|
+
if (k === "title" || k === "subtitle") {
|
|
646
|
+
property[k] = wrapHeaderText(v);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
645
649
|
if (TEXT_LEAF_KEYS.has(k)) {
|
|
646
650
|
property[k] = wrapTextLeaf(v);
|
|
647
651
|
continue;
|
|
@@ -806,6 +810,22 @@ function wrapTextLeaf(v) {
|
|
|
806
810
|
}
|
|
807
811
|
return { tag: "plain_text", property: { content: "" } };
|
|
808
812
|
}
|
|
813
|
+
function wrapHeaderText(v) {
|
|
814
|
+
if (typeof v === "string") return { tag: "plain_text", property: { content: v } };
|
|
815
|
+
if (!isRecord(v)) return { tag: "plain_text", property: { content: "" } };
|
|
816
|
+
const tag = typeof v.tag === "string" ? v.tag : "plain_text";
|
|
817
|
+
const content = typeof v.content === "string" ? v.content : "";
|
|
818
|
+
if (tag === "lark_md" || tag === "markdown") {
|
|
819
|
+
return {
|
|
820
|
+
tag: "markdown",
|
|
821
|
+
property: {
|
|
822
|
+
elements: parseMarkdown(content),
|
|
823
|
+
originTag: "lark_md"
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
return wrapTextLeaf(v);
|
|
828
|
+
}
|
|
809
829
|
function behaviorsToActions(behaviors) {
|
|
810
830
|
const out = [];
|
|
811
831
|
for (const b of behaviors) {
|
|
@@ -849,203 +869,1000 @@ function isRecord(v) {
|
|
|
849
869
|
}
|
|
850
870
|
|
|
851
871
|
// src/raw-to-dsl.ts
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
backgroundColor: "background_color",
|
|
857
|
-
userID: "user_id",
|
|
858
|
-
showAvatar: "show_avatar",
|
|
859
|
-
showName: "show_name",
|
|
860
|
-
hasBorder: "has_border",
|
|
861
|
-
hoverTips: "hover_tips",
|
|
862
|
-
pillShaped: "pill_shaped",
|
|
863
|
-
tagStyle: "tag_style",
|
|
864
|
-
scaleType: "scale_type",
|
|
865
|
-
dropInvalidUserID: "drop_invalid_user_id",
|
|
866
|
-
udIcon: "ud_icon",
|
|
867
|
-
// video / audio
|
|
868
|
-
fileID: "file_key",
|
|
869
|
-
coverImageID: "image_key",
|
|
870
|
-
showTime: "show_time",
|
|
871
|
-
enableDownload: "enable_download",
|
|
872
|
-
audioID: "audio_id",
|
|
873
|
-
showProgressBar: "show_progress_bar",
|
|
874
|
-
fillColor: "fill_color"
|
|
875
|
-
};
|
|
876
|
-
var KNOWN_TAGS = /* @__PURE__ */ new Set([
|
|
877
|
-
"markdown",
|
|
878
|
-
"lark_md",
|
|
879
|
-
"div",
|
|
880
|
-
"column_set",
|
|
881
|
-
"column",
|
|
882
|
-
"hr",
|
|
883
|
-
"image",
|
|
884
|
-
"img",
|
|
885
|
-
"button",
|
|
886
|
-
"overflow",
|
|
887
|
-
"select_static",
|
|
888
|
-
"select_multi",
|
|
889
|
-
"input",
|
|
890
|
-
"date_picker",
|
|
891
|
-
"picker_time",
|
|
892
|
-
"picker_date",
|
|
893
|
-
"checker",
|
|
894
|
-
"chart",
|
|
895
|
-
"table",
|
|
896
|
-
"iframe",
|
|
897
|
-
"person",
|
|
898
|
-
"person_list",
|
|
899
|
-
"interactive_container",
|
|
900
|
-
"tag",
|
|
901
|
-
"text_tag",
|
|
902
|
-
"multi_image",
|
|
903
|
-
"plain_text",
|
|
904
|
-
"lark_md_inline",
|
|
905
|
-
"image_text",
|
|
906
|
-
"carousel",
|
|
907
|
-
"grid",
|
|
908
|
-
"speaking",
|
|
909
|
-
"vote",
|
|
910
|
-
"lottery",
|
|
911
|
-
"calender",
|
|
912
|
-
"schedule",
|
|
913
|
-
"share_chat",
|
|
914
|
-
"share_user",
|
|
915
|
-
"add_on",
|
|
916
|
-
"whole_text",
|
|
917
|
-
"cropper_image",
|
|
918
|
-
"highlight",
|
|
919
|
-
"local_file",
|
|
920
|
-
"file_preview",
|
|
921
|
-
"input_select",
|
|
922
|
-
"picker_datetime",
|
|
923
|
-
"picker_multi",
|
|
924
|
-
"rule",
|
|
925
|
-
"statistic",
|
|
926
|
-
"task",
|
|
927
|
-
"card_header",
|
|
928
|
-
"body",
|
|
929
|
-
"audio",
|
|
930
|
-
"video",
|
|
931
|
-
"markdown_audio",
|
|
932
|
-
"unknown"
|
|
933
|
-
]);
|
|
934
|
-
function isRecord2(v) {
|
|
935
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
936
|
-
}
|
|
937
|
-
function degradeUnknown(el) {
|
|
938
|
-
const rawTag = typeof el.tag === "string" ? el.tag : "undefined";
|
|
939
|
-
return { tag: "unknown", raw_tag: rawTag, fallback: el };
|
|
872
|
+
function isRawCardEnvelope(value) {
|
|
873
|
+
if (typeof value !== "object" || value === null) return false;
|
|
874
|
+
const v = value;
|
|
875
|
+
return typeof v.json_card === "string" && typeof v.card_schema === "number";
|
|
940
876
|
}
|
|
941
|
-
function
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
877
|
+
function rawCardToDsl(envelope) {
|
|
878
|
+
if (!isRawCardEnvelope(envelope)) return envelope;
|
|
879
|
+
const env = envelope;
|
|
880
|
+
let inner;
|
|
881
|
+
try {
|
|
882
|
+
inner = JSON.parse(env.json_card ?? "");
|
|
883
|
+
} catch {
|
|
884
|
+
return envelope;
|
|
946
885
|
}
|
|
947
|
-
const
|
|
948
|
-
|
|
886
|
+
const ctx = {
|
|
887
|
+
images: env.json_attachment?.images ?? {},
|
|
888
|
+
atUsers: env.json_attachment?.at_users ?? {}
|
|
949
889
|
};
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
890
|
+
return convertRoot(inner, ctx);
|
|
891
|
+
}
|
|
892
|
+
var RAW_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
893
|
+
"new_body",
|
|
894
|
+
"source",
|
|
895
|
+
// Editor line-count mirror on markdown/plain_text. Schema 2.0 has no `lines`
|
|
896
|
+
// field; Feishu rejects it with code 200621 ("unknown property, property:
|
|
897
|
+
// lines"). Raw emits it on every text node (lines:0), so drop it everywhere.
|
|
898
|
+
"lines"
|
|
899
|
+
]);
|
|
900
|
+
function isI18nMirror(key) {
|
|
901
|
+
return key.startsWith("i18n");
|
|
902
|
+
}
|
|
903
|
+
var IMG_RAW_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
904
|
+
"compact_width",
|
|
905
|
+
"custom_width",
|
|
906
|
+
"mode"
|
|
907
|
+
]);
|
|
908
|
+
var VIDEO_RAW_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
909
|
+
// duration lives on im.file.create upload, not on the video component.
|
|
910
|
+
"duration",
|
|
911
|
+
"duration_ms",
|
|
912
|
+
// editor instance id — public audio has audio_id, video does NOT have video_id.
|
|
913
|
+
"video_id"
|
|
914
|
+
]);
|
|
915
|
+
var VIDEO_PUBLIC_FIELDS = /* @__PURE__ */ new Set([
|
|
916
|
+
"element_id",
|
|
917
|
+
"margin",
|
|
918
|
+
"file_key",
|
|
919
|
+
"i18n_file_key",
|
|
920
|
+
"enable_download",
|
|
921
|
+
"show_time",
|
|
922
|
+
"cover",
|
|
923
|
+
"hover_tips",
|
|
924
|
+
"fallback"
|
|
925
|
+
]);
|
|
926
|
+
var AUDIO_PUBLIC_FIELDS = /* @__PURE__ */ new Set([
|
|
927
|
+
"element_id",
|
|
928
|
+
"margin",
|
|
929
|
+
"file_key",
|
|
930
|
+
"i18n_file_key",
|
|
931
|
+
"audio_id",
|
|
932
|
+
"disabled",
|
|
933
|
+
"disabled_tips",
|
|
934
|
+
"show_progress_bar",
|
|
935
|
+
"show_time",
|
|
936
|
+
"time_display",
|
|
937
|
+
"time_position",
|
|
938
|
+
"border",
|
|
939
|
+
"style",
|
|
940
|
+
"padding",
|
|
941
|
+
"background_style",
|
|
942
|
+
"fill_color",
|
|
943
|
+
"width",
|
|
944
|
+
"fallback"
|
|
945
|
+
]);
|
|
946
|
+
var TAG_DEFAULT_DROPS = {
|
|
947
|
+
column: {
|
|
948
|
+
disabled: false
|
|
949
|
+
},
|
|
950
|
+
column_set: {
|
|
951
|
+
disabled: false,
|
|
952
|
+
horizontal_spacing: "default"
|
|
953
|
+
},
|
|
954
|
+
button: {
|
|
955
|
+
disabled: false
|
|
956
|
+
},
|
|
957
|
+
interactive_container: {
|
|
958
|
+
disabled: false,
|
|
959
|
+
required: false,
|
|
960
|
+
has_border: false,
|
|
961
|
+
height: "auto"
|
|
955
962
|
}
|
|
963
|
+
};
|
|
964
|
+
var VALUE_ECHO_TARGETS = {
|
|
965
|
+
horizontal_spacing_value: "horizontal_spacing",
|
|
966
|
+
vertical_spacing_value: "vertical_spacing",
|
|
967
|
+
width_value: "width",
|
|
968
|
+
height_value: "height",
|
|
969
|
+
corner_radius_value: "corner_radius",
|
|
970
|
+
size_value: "size"
|
|
971
|
+
};
|
|
972
|
+
function convertRoot(root, ctx) {
|
|
973
|
+
if (!isRecord2(root)) return root;
|
|
974
|
+
const out = {};
|
|
975
|
+
out.schema = typeof root.schema === "string" ? root.schema : "2.0";
|
|
976
|
+
out.config = convertConfig(isRecord2(root.config) ? root.config : {});
|
|
977
|
+
if (isRecord2(root.body)) out.body = convertBody2(root.body, ctx);
|
|
978
|
+
if (isRecord2(root.header)) out.header = convertHeader2(root.header, ctx);
|
|
956
979
|
return out;
|
|
957
980
|
}
|
|
958
|
-
function
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
981
|
+
function convertConfig(_node) {
|
|
982
|
+
return { update_multi: true };
|
|
983
|
+
}
|
|
984
|
+
function convertHeader2(node, ctx) {
|
|
985
|
+
const property = isRecord2(node.property) ? node.property : node;
|
|
986
|
+
const out = convertProperty(property, "header", ctx);
|
|
987
|
+
delete out.tag;
|
|
988
|
+
return out;
|
|
989
|
+
}
|
|
990
|
+
function convertHeaderText(rawValue, ctx) {
|
|
991
|
+
const tag = typeof rawValue.tag === "string" ? rawValue.tag : "";
|
|
992
|
+
const wrapped = isRecord2(rawValue.property);
|
|
993
|
+
const property = wrapped ? rawValue.property : rawValue;
|
|
994
|
+
if (tag === "plain_text") {
|
|
995
|
+
const content = typeof property.content === "string" ? property.content : "";
|
|
996
|
+
return { tag: "plain_text", content };
|
|
997
|
+
}
|
|
998
|
+
if (tag === "markdown" || tag === "lark_md") {
|
|
999
|
+
if (wrapped && Array.isArray(property.elements)) {
|
|
1000
|
+
const rebuilt = rebuildMarkdown(property, ctx);
|
|
1001
|
+
return { tag: "lark_md", content: typeof rebuilt.content === "string" ? rebuilt.content : "" };
|
|
1002
|
+
}
|
|
1003
|
+
const content = typeof property.content === "string" ? property.content : "";
|
|
1004
|
+
return { tag: "lark_md", content };
|
|
1005
|
+
}
|
|
1006
|
+
if (!wrapped && typeof rawValue.content === "string" && !tag) {
|
|
1007
|
+
return { tag: "plain_text", content: rawValue.content };
|
|
1008
|
+
}
|
|
1009
|
+
return void 0;
|
|
1010
|
+
}
|
|
1011
|
+
function convertBody2(node, ctx) {
|
|
1012
|
+
const property = isRecord2(node.property) ? node.property : node;
|
|
1013
|
+
return convertProperty(property, "body", ctx);
|
|
1014
|
+
}
|
|
1015
|
+
function unwrap(node, ctx) {
|
|
1016
|
+
const tag = typeof node.tag === "string" ? node.tag : void 0;
|
|
1017
|
+
const property = isRecord2(node.property) ? node.property : {};
|
|
1018
|
+
if (tag === "markdown") return rebuildMarkdown(property, ctx);
|
|
1019
|
+
if (tag === "plain_text") {
|
|
1020
|
+
const content = typeof property.content === "string" ? property.content : "";
|
|
1021
|
+
return { tag: "plain_text", content };
|
|
1022
|
+
}
|
|
1023
|
+
if (tag === "standard_icon") {
|
|
1024
|
+
const out = { tag: "standard_icon" };
|
|
1025
|
+
if (typeof property.token === "string") out.token = property.token;
|
|
1026
|
+
if (typeof property.color === "string") out.color = property.color;
|
|
1027
|
+
const widthPx = isPixelScalar(property.widthValue) ? pixelOf(property.widthValue) : void 0;
|
|
1028
|
+
const heightPx = isPixelScalar(property.heightValue) ? pixelOf(property.heightValue) : void 0;
|
|
1029
|
+
if (widthPx !== void 0 && heightPx !== void 0) {
|
|
1030
|
+
out.size = `${widthPx}px ${heightPx}px`;
|
|
1031
|
+
} else if (isRecord2(property.size) && hasSide(property.size)) out.size = formatBox(property.size);
|
|
1032
|
+
else if (isPixelScalar(property.size)) out.size = `${pixelOf(property.size)}px`;
|
|
1033
|
+
else if (typeof property.size === "string") out.size = property.size;
|
|
1034
|
+
return out;
|
|
1035
|
+
}
|
|
1036
|
+
if (tag === "ud_icon") {
|
|
1037
|
+
const token = typeof property.token === "string" ? property.token : void 0;
|
|
1038
|
+
const out = { tag: "standard_icon" };
|
|
1039
|
+
if (token) out.token = token;
|
|
1040
|
+
return out;
|
|
1041
|
+
}
|
|
1042
|
+
if (tag === "chart") {
|
|
1043
|
+
const out = { tag: "chart" };
|
|
1044
|
+
if (typeof property.colorTheme === "string") out.color_theme = property.colorTheme;
|
|
1045
|
+
if (typeof property.preview === "boolean") out.preview = property.preview;
|
|
1046
|
+
if (property.chartSpec !== void 0) out.chart_spec = property.chartSpec;
|
|
1047
|
+
return out;
|
|
1048
|
+
}
|
|
1049
|
+
if (tag === "table") {
|
|
1050
|
+
const out = { tag: "table" };
|
|
1051
|
+
const rawColumns = Array.isArray(property.columns) ? property.columns : [];
|
|
1052
|
+
out.columns = rawColumns.map((col) => {
|
|
1053
|
+
if (!isRecord2(col)) return {};
|
|
1054
|
+
const c = {};
|
|
1055
|
+
if (typeof col.name === "string") c.name = col.name;
|
|
1056
|
+
const displayName = typeof col.displayName === "string" ? col.displayName : typeof col.display_name === "string" ? col.display_name : void 0;
|
|
1057
|
+
if (displayName !== void 0) c.display_name = displayName;
|
|
1058
|
+
const dataType = typeof col.dataType === "string" ? col.dataType : typeof col.data_type === "string" ? col.data_type : void 0;
|
|
1059
|
+
if (dataType !== void 0) c.data_type = dataType;
|
|
1060
|
+
const hAlign = typeof col.horizontalAlign === "string" ? col.horizontalAlign : typeof col.horizontal_align === "string" ? col.horizontal_align : void 0;
|
|
1061
|
+
if (hAlign !== void 0) c.horizontal_align = hAlign;
|
|
1062
|
+
const wv = col.widthValue;
|
|
1063
|
+
if (isRecord2(wv)) {
|
|
1064
|
+
if (wv.type === "pixels" && typeof wv.value === "number") c.width = `${wv.value}px`;
|
|
1065
|
+
else if (typeof wv.value === "string") c.width = wv.value;
|
|
1066
|
+
} else if (typeof col.width === "string" || typeof col.width === "number") {
|
|
1067
|
+
c.width = col.width;
|
|
1068
|
+
}
|
|
1069
|
+
if (isRecord2(col.format)) c.format = col.format;
|
|
1070
|
+
return c;
|
|
1071
|
+
});
|
|
1072
|
+
const rawRows = Array.isArray(property.rows) ? property.rows : [];
|
|
1073
|
+
out.rows = rawRows.map((row) => {
|
|
1074
|
+
if (!isRecord2(row)) return {};
|
|
1075
|
+
const r = {};
|
|
1076
|
+
for (const [k, cell] of Object.entries(row)) {
|
|
1077
|
+
let value = isRecord2(cell) && "data" in cell ? cell.data : cell;
|
|
1078
|
+
if (isRecord2(value) && value.tag === "markdown" && isRecord2(value.property)) {
|
|
1079
|
+
const elements = Array.isArray(value.property.elements) ? value.property.elements : [];
|
|
1080
|
+
value = renderChildren(elements, ctx);
|
|
1081
|
+
}
|
|
1082
|
+
r[k] = value;
|
|
966
1083
|
}
|
|
1084
|
+
return r;
|
|
1085
|
+
});
|
|
1086
|
+
if (isRecord2(property.headerStyle)) {
|
|
1087
|
+
const hs = {};
|
|
1088
|
+
for (const [k, v] of Object.entries(property.headerStyle)) {
|
|
1089
|
+
const sk = snake(k);
|
|
1090
|
+
if (sk === "platform_text_size") continue;
|
|
1091
|
+
if (sk === "lines_value") {
|
|
1092
|
+
if (isRecord2(v) && typeof v.value === "number") hs.lines = v.value;
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
hs[sk] = v;
|
|
1096
|
+
}
|
|
1097
|
+
out.header_style = hs;
|
|
967
1098
|
}
|
|
968
|
-
|
|
1099
|
+
const rhv = property.rowHeightValue;
|
|
1100
|
+
if (isRecord2(rhv) && typeof rhv.value === "string") out.row_height = rhv.value;
|
|
1101
|
+
else if (typeof property.rowHeight === "string") out.row_height = property.rowHeight;
|
|
1102
|
+
if (typeof property.pageSize === "number") out.page_size = property.pageSize;
|
|
1103
|
+
if (typeof property.freezeFirstColumn === "boolean") out.freeze_first_column = property.freezeFirstColumn;
|
|
1104
|
+
return out;
|
|
969
1105
|
}
|
|
970
|
-
|
|
1106
|
+
if (tag === "input") {
|
|
1107
|
+
const allow = /* @__PURE__ */ new Set([
|
|
1108
|
+
"name",
|
|
1109
|
+
"placeholder",
|
|
1110
|
+
"default_value",
|
|
1111
|
+
"value",
|
|
1112
|
+
"label",
|
|
1113
|
+
"label_position",
|
|
1114
|
+
"margin",
|
|
1115
|
+
"max_length",
|
|
1116
|
+
"input_type",
|
|
1117
|
+
"width"
|
|
1118
|
+
]);
|
|
1119
|
+
const out = { tag: "input" };
|
|
1120
|
+
const converted2 = convertProperty(property, "input", ctx);
|
|
1121
|
+
for (const [k, v] of Object.entries(converted2)) {
|
|
1122
|
+
if (allow.has(k)) out[k] = v;
|
|
1123
|
+
}
|
|
1124
|
+
if (converted2.required === true) out.required = true;
|
|
1125
|
+
return out;
|
|
1126
|
+
}
|
|
1127
|
+
if (tag === "person" || tag === "person_v1") {
|
|
1128
|
+
const out = { tag: "person" };
|
|
1129
|
+
if (typeof property.userID === "string") out.user_id = property.userID;
|
|
1130
|
+
else if (typeof property.user_id === "string") out.user_id = property.user_id;
|
|
1131
|
+
if (typeof property.size === "string") out.size = property.size;
|
|
1132
|
+
if (typeof property.showAvatar === "boolean") out.show_avatar = property.showAvatar;
|
|
1133
|
+
if (typeof property.showName === "boolean") out.show_name = property.showName;
|
|
1134
|
+
if (typeof property.style === "string") out.style = property.style;
|
|
1135
|
+
if (isRecord2(property.margin) && hasSide(property.margin)) out.margin = formatBox(property.margin);
|
|
1136
|
+
else if (typeof property.margin === "string") out.margin = property.margin;
|
|
1137
|
+
return out;
|
|
1138
|
+
}
|
|
1139
|
+
if (tag === "person_list") {
|
|
1140
|
+
const out = { tag: "person_list" };
|
|
1141
|
+
if (typeof property.size === "string") out.size = property.size;
|
|
1142
|
+
if (typeof property.showAvatar === "boolean") out.show_avatar = property.showAvatar;
|
|
1143
|
+
if (typeof property.showName === "boolean") out.show_name = property.showName;
|
|
1144
|
+
if (typeof property.lines === "number") out.lines = property.lines;
|
|
1145
|
+
if (typeof property.dropInvalidUserID === "boolean") out.drop_invalid_user_id = property.dropInvalidUserID;
|
|
1146
|
+
else if (typeof property.drop_invalid_user_id === "boolean") out.drop_invalid_user_id = property.drop_invalid_user_id;
|
|
1147
|
+
if (isRecord2(property.margin) && hasSide(property.margin)) out.margin = formatBox(property.margin);
|
|
1148
|
+
else if (typeof property.margin === "string") out.margin = property.margin;
|
|
1149
|
+
const rawPersons = Array.isArray(property.persons) ? property.persons : [];
|
|
1150
|
+
out.persons = rawPersons.map((p) => {
|
|
1151
|
+
if (!isRecord2(p)) return void 0;
|
|
1152
|
+
const id = typeof p.id === "string" ? p.id : typeof p.userID === "string" ? p.userID : void 0;
|
|
1153
|
+
return id ? { id } : void 0;
|
|
1154
|
+
}).filter((p) => p !== void 0);
|
|
1155
|
+
const iconSrc = isRecord2(property.icon) ? property.icon : isRecord2(property.udIcon) ? property.udIcon : void 0;
|
|
1156
|
+
if (iconSrc) {
|
|
1157
|
+
const ip = isRecord2(iconSrc.property) ? iconSrc.property : iconSrc;
|
|
1158
|
+
const icon = {};
|
|
1159
|
+
const iconTag = typeof iconSrc.tag === "string" ? iconSrc.tag : typeof ip.tag === "string" ? ip.tag : "standard_icon";
|
|
1160
|
+
icon.tag = iconTag === "ud_icon" ? "standard_icon" : iconTag;
|
|
1161
|
+
if (typeof ip.token === "string") icon.token = ip.token;
|
|
1162
|
+
if (typeof ip.color === "string") icon.color = ip.color;
|
|
1163
|
+
if (typeof ip.imageID === "string") icon.img_key = ip.imageID;
|
|
1164
|
+
else if (typeof ip.img_key === "string") icon.img_key = ip.img_key;
|
|
1165
|
+
if (icon.token !== void 0 || icon.img_key !== void 0) out.icon = icon;
|
|
1166
|
+
}
|
|
1167
|
+
return out;
|
|
1168
|
+
}
|
|
1169
|
+
if (tag === "div" && isRecord2(property.text) && property.fields === void 0) {
|
|
1170
|
+
const text = property.text;
|
|
1171
|
+
const textTag = typeof text.tag === "string" ? text.tag : "";
|
|
1172
|
+
if ((textTag === "markdown" || textTag === "lark_md") && isRecord2(text.property)) {
|
|
1173
|
+
return unwrap(text, ctx);
|
|
1174
|
+
}
|
|
1175
|
+
if (textTag === "plain_text") {
|
|
1176
|
+
const out = { tag: "div" };
|
|
1177
|
+
const widthVal = property.widthValue;
|
|
1178
|
+
if (isRecord2(widthVal) && typeof widthVal.value === "string" && typeof widthVal.type === "string" && widthVal.type.startsWith("builtin_")) {
|
|
1179
|
+
out.width = widthVal.value;
|
|
1180
|
+
} else if (typeof property.width === "string") {
|
|
1181
|
+
out.width = property.width;
|
|
1182
|
+
}
|
|
1183
|
+
const tp = isRecord2(text.property) ? text.property : {};
|
|
1184
|
+
const content = typeof tp.content === "string" ? tp.content : "";
|
|
1185
|
+
const textAlign = typeof tp.textAlign === "string" ? tp.textAlign : "left";
|
|
1186
|
+
out.text = { tag: "plain_text", content, text_align: textAlign };
|
|
1187
|
+
if (isRecord2(property.icon)) {
|
|
1188
|
+
out.icon = unwrap(property.icon, ctx);
|
|
1189
|
+
}
|
|
1190
|
+
return out;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const converted = convertProperty(property, tag, ctx);
|
|
1194
|
+
return tag ? { tag, ...converted } : converted;
|
|
971
1195
|
}
|
|
972
|
-
|
|
973
|
-
|
|
1196
|
+
var DROP = /* @__PURE__ */ Symbol("drop");
|
|
1197
|
+
function convertProperty(property, ownerTag, ctx) {
|
|
974
1198
|
const out = {};
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1199
|
+
const siblings = {
|
|
1200
|
+
buttonActionType: ownerTag === "button" && typeof property.actionType === "string" ? property.actionType : void 0
|
|
1201
|
+
};
|
|
1202
|
+
for (const [rawKey, rawValue] of Object.entries(property)) {
|
|
1203
|
+
const result = convertField(rawKey, rawValue, ownerTag, ctx, siblings);
|
|
1204
|
+
if (result === DROP) continue;
|
|
1205
|
+
const [outKey, outValue] = result;
|
|
1206
|
+
if (outValue === DROP) continue;
|
|
1207
|
+
out[outKey] = outValue;
|
|
1208
|
+
}
|
|
1209
|
+
for (const [echoKey, enumKey] of Object.entries(VALUE_ECHO_TARGETS)) {
|
|
1210
|
+
if (!(echoKey in out)) continue;
|
|
1211
|
+
const echoVal = out[echoKey];
|
|
1212
|
+
const enumVal = out[enumKey];
|
|
1213
|
+
delete out[echoKey];
|
|
1214
|
+
if (!isRecord2(echoVal)) continue;
|
|
1215
|
+
const echoType = typeof echoVal.type === "string" ? echoVal.type : "";
|
|
1216
|
+
const echoValue = echoVal.value;
|
|
1217
|
+
if (echoType.includes("pixels") && typeof echoValue === "number") {
|
|
1218
|
+
if (enumVal === void 0 || enumVal === "default" || enumVal === "auto" || enumVal === "weighted") {
|
|
1219
|
+
out[enumKey] = `${echoValue}px`;
|
|
1220
|
+
}
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
if (echoType === "aspect_ratio" && isRecord2(echoValue)) {
|
|
1224
|
+
const w = echoValue.width;
|
|
1225
|
+
const h = echoValue.height;
|
|
1226
|
+
if (typeof w === "number" && typeof h === "number") {
|
|
1227
|
+
out["size"] = `${w}:${h}`;
|
|
1228
|
+
}
|
|
978
1229
|
continue;
|
|
979
1230
|
}
|
|
980
|
-
if (
|
|
981
|
-
|
|
1231
|
+
if (echoType === "width_height_pixels" && isRecord2(echoValue)) {
|
|
1232
|
+
const w = echoValue.width;
|
|
1233
|
+
const h = echoValue.height;
|
|
1234
|
+
if (typeof w === "number" && typeof h === "number") {
|
|
1235
|
+
out["size"] = `${w}px ${h}px`;
|
|
1236
|
+
}
|
|
982
1237
|
continue;
|
|
983
1238
|
}
|
|
984
|
-
if (
|
|
985
|
-
|
|
1239
|
+
if (echoType === "builtin_image_size") continue;
|
|
1240
|
+
if (echoType.startsWith("builtin_") && typeof echoValue === "string") {
|
|
1241
|
+
if (enumVal === void 0) out[enumKey] = echoValue;
|
|
986
1242
|
continue;
|
|
987
1243
|
}
|
|
988
|
-
|
|
1244
|
+
}
|
|
1245
|
+
if (ownerTag && TAG_DEFAULT_DROPS[ownerTag]) {
|
|
1246
|
+
const defaults = TAG_DEFAULT_DROPS[ownerTag];
|
|
1247
|
+
for (const [k, defVal] of Object.entries(defaults)) {
|
|
1248
|
+
if (out[k] === defVal) delete out[k];
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
if (ownerTag === "video") {
|
|
1252
|
+
for (const k of Object.keys(out)) {
|
|
1253
|
+
if (!VIDEO_PUBLIC_FIELDS.has(k)) delete out[k];
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
if (ownerTag === "audio") {
|
|
1257
|
+
for (const k of Object.keys(out)) {
|
|
1258
|
+
if (!AUDIO_PUBLIC_FIELDS.has(k)) delete out[k];
|
|
1259
|
+
}
|
|
989
1260
|
}
|
|
990
1261
|
return out;
|
|
991
1262
|
}
|
|
992
|
-
function
|
|
993
|
-
if (
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
996
|
-
if (
|
|
997
|
-
if ("
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
if (
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1263
|
+
function convertField(rawKey, rawValue, ownerTag, ctx, siblings = {}) {
|
|
1264
|
+
if (rawKey === "originTag" || rawKey === "markdownElements") return DROP;
|
|
1265
|
+
const key = snake(rawKey);
|
|
1266
|
+
if (RAW_ONLY_FIELDS.has(key)) return DROP;
|
|
1267
|
+
if (isI18nMirror(key)) return DROP;
|
|
1268
|
+
if (key === "actions" && Array.isArray(rawValue) && rawValue.length === 0) {
|
|
1269
|
+
return DROP;
|
|
1270
|
+
}
|
|
1271
|
+
if ((key === "padding" || key === "margin") && isRecord2(rawValue) && hasSide(rawValue)) {
|
|
1272
|
+
return [key, formatBox(rawValue)];
|
|
1273
|
+
}
|
|
1274
|
+
if (key in VALUE_ECHO_TARGETS) {
|
|
1275
|
+
return [key, rawValue];
|
|
1276
|
+
}
|
|
1277
|
+
if (isPixelScalar(rawValue)) {
|
|
1278
|
+
return [key, `${pixelOf(rawValue)}px`];
|
|
1279
|
+
}
|
|
1280
|
+
if (ownerTag === "img" && key === "image_id") {
|
|
1281
|
+
const id = typeof rawValue === "string" || typeof rawValue === "number" ? String(rawValue) : void 0;
|
|
1282
|
+
const origin = id !== void 0 ? ctx.images[id]?.origin_key : void 0;
|
|
1283
|
+
if (origin) return ["img_key", origin];
|
|
1284
|
+
return [key, rawValue];
|
|
1285
|
+
}
|
|
1286
|
+
if (ownerTag === "img" && IMG_RAW_ONLY_FIELDS.has(key)) return DROP;
|
|
1287
|
+
if (ownerTag === "video" && key === "cover_image_id") {
|
|
1288
|
+
const imgKey = resolveAttachmentImgKey(rawValue, ctx);
|
|
1289
|
+
if (!imgKey) return DROP;
|
|
1290
|
+
return ["cover", { img_key: imgKey }];
|
|
1291
|
+
}
|
|
1292
|
+
if (ownerTag === "video" && key === "cover") {
|
|
1293
|
+
const cover = normalizeVideoCover(rawValue, ctx);
|
|
1294
|
+
return cover ? ["cover", cover] : DROP;
|
|
1295
|
+
}
|
|
1296
|
+
if ((ownerTag === "video" || ownerTag === "audio") && key === "file_id") {
|
|
1297
|
+
if (typeof rawValue === "string" && rawValue) return ["file_key", rawValue];
|
|
1298
|
+
return DROP;
|
|
1299
|
+
}
|
|
1300
|
+
if (ownerTag === "video" && VIDEO_RAW_ONLY_FIELDS.has(key)) return DROP;
|
|
1301
|
+
if ((key === "alt" || key === "hover_tips") && isRecord2(rawValue) && rawValue.tag === "plain_text") {
|
|
1302
|
+
const p = isRecord2(rawValue.property) ? rawValue.property : {};
|
|
1303
|
+
return [key, { tag: "plain_text", content: typeof p.content === "string" ? p.content : "" }];
|
|
1304
|
+
}
|
|
1305
|
+
if (key === "text" && isRecord2(rawValue) && rawValue.tag === "plain_text") {
|
|
1306
|
+
const p = isRecord2(rawValue.property) ? rawValue.property : {};
|
|
1307
|
+
return [key, { tag: "plain_text", content: typeof p.content === "string" ? p.content : "" }];
|
|
1308
|
+
}
|
|
1309
|
+
if (ownerTag === "header" && (key === "title" || key === "subtitle") && isRecord2(rawValue)) {
|
|
1310
|
+
const headerText = convertHeaderText(rawValue, ctx);
|
|
1311
|
+
if (headerText) return [key, headerText];
|
|
1312
|
+
}
|
|
1313
|
+
if (ownerTag === "header" && key === "ud_icon" && isRecord2(rawValue) && rawValue.tag === "ud_icon") {
|
|
1314
|
+
const p = isRecord2(rawValue.property) ? rawValue.property : {};
|
|
1315
|
+
const token = typeof p.token === "string" ? p.token : void 0;
|
|
1316
|
+
if (!token) return DROP;
|
|
1317
|
+
return ["icon", { tag: "standard_icon", token }];
|
|
1318
|
+
}
|
|
1319
|
+
if (ownerTag === "collapsible_panel" && key === "header" && isRecord2(rawValue)) {
|
|
1320
|
+
const cleaned = {};
|
|
1321
|
+
for (const [k, v] of Object.entries(rawValue)) {
|
|
1322
|
+
const sk = snake(k);
|
|
1323
|
+
if (sk === "position" || sk === "width" || sk === "vertical_align") continue;
|
|
1324
|
+
const sub = convertField(k, v, "collapsible_panel_header", ctx, siblings);
|
|
1325
|
+
if (sub === DROP) continue;
|
|
1326
|
+
const [subKey, subVal] = sub;
|
|
1327
|
+
if (subVal === DROP) continue;
|
|
1328
|
+
cleaned[subKey] = subVal;
|
|
1329
|
+
}
|
|
1330
|
+
return [key, cleaned];
|
|
1331
|
+
}
|
|
1332
|
+
if (ownerTag === "button" && key === "action_type") return DROP;
|
|
1333
|
+
if (ownerTag === "button" && key === "actions" && Array.isArray(rawValue)) {
|
|
1334
|
+
const openUrls = [];
|
|
1335
|
+
for (const a of rawValue) {
|
|
1336
|
+
if (!isRecord2(a)) continue;
|
|
1337
|
+
const inner = isRecord2(a.action) ? a.action : {};
|
|
1338
|
+
const t = typeof a.type === "string" ? a.type : void 0;
|
|
1339
|
+
if (t === "open_url" && typeof inner.url === "string") openUrls.push(inner.url);
|
|
1340
|
+
}
|
|
1341
|
+
if (openUrls.length === 0) return DROP;
|
|
1342
|
+
if (siblings.buttonActionType === "link") {
|
|
1343
|
+
return ["multi_url", { url: openUrls[0] }];
|
|
1344
|
+
}
|
|
1345
|
+
return [
|
|
1346
|
+
"behaviors",
|
|
1347
|
+
openUrls.map((url) => ({ type: "open_url", default_url: url }))
|
|
1348
|
+
];
|
|
1349
|
+
}
|
|
1350
|
+
if (ownerTag === "interactive_container" && key === "actions" && Array.isArray(rawValue)) {
|
|
1351
|
+
const behaviors = rawValue.map((a) => {
|
|
1352
|
+
if (!isRecord2(a)) return void 0;
|
|
1353
|
+
const inner = isRecord2(a.action) ? a.action : {};
|
|
1354
|
+
const t = typeof a.type === "string" ? a.type : void 0;
|
|
1355
|
+
if (t === "open_url" && typeof inner.url === "string") {
|
|
1356
|
+
return { type: "open_url", default_url: inner.url };
|
|
1007
1357
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1358
|
+
if (t === "callback") {
|
|
1359
|
+
return { type: "callback", value: inner.value ?? {} };
|
|
1360
|
+
}
|
|
1361
|
+
return void 0;
|
|
1362
|
+
}).filter((b) => b !== void 0);
|
|
1363
|
+
if (behaviors.length === 0) return DROP;
|
|
1364
|
+
return ["behaviors", behaviors];
|
|
1365
|
+
}
|
|
1366
|
+
if (key === "border" && isRecord2(rawValue)) {
|
|
1367
|
+
const b = rawValue;
|
|
1368
|
+
const out = {};
|
|
1369
|
+
if (typeof b.borderColor === "string") out.color = b.borderColor;
|
|
1370
|
+
else if (typeof b.color === "string") out.color = b.color;
|
|
1371
|
+
if (isPixelScalar(b.cornerRadius)) out.corner_radius = `${pixelOf(b.cornerRadius)}px`;
|
|
1372
|
+
else if (typeof b.cornerRadius === "string") out.corner_radius = b.cornerRadius;
|
|
1373
|
+
else if (b.cornerRadius !== void 0) out.corner_radius = b.cornerRadius;
|
|
1374
|
+
for (const [bk, bv] of Object.entries(b)) {
|
|
1375
|
+
if (bk === "borderColor" || bk === "cornerRadius" || bk === "color") continue;
|
|
1376
|
+
out[snake(bk)] = convertGeneric(bv, ctx);
|
|
1010
1377
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1378
|
+
return [key, out];
|
|
1379
|
+
}
|
|
1380
|
+
if (key === "url" && isRecord2(rawValue)) {
|
|
1381
|
+
const v = rawValue;
|
|
1382
|
+
const main = typeof v.url === "string" ? v.url : "";
|
|
1383
|
+
const others = ["androidURL", "iosURL", "pcURL", "harmonyURL"].map((k) => v[k]).filter((x) => typeof x === "string" && x.length > 0);
|
|
1384
|
+
if (others.length === 0) return [key, main];
|
|
1385
|
+
return [
|
|
1386
|
+
key,
|
|
1387
|
+
{
|
|
1388
|
+
url: main,
|
|
1389
|
+
android_url: typeof v.androidURL === "string" ? v.androidURL : "",
|
|
1390
|
+
ios_url: typeof v.iosURL === "string" ? v.iosURL : "",
|
|
1391
|
+
pc_url: typeof v.pcURL === "string" ? v.pcURL : ""
|
|
1392
|
+
}
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1395
|
+
return [key, convertGeneric(rawValue, ctx)];
|
|
1396
|
+
}
|
|
1397
|
+
function renderInlinePerson(prop) {
|
|
1398
|
+
const id = typeof prop.userID === "string" ? prop.userID : typeof prop.user_id === "string" ? prop.user_id : typeof prop.id === "string" ? prop.id : "";
|
|
1399
|
+
if (!id) return "";
|
|
1400
|
+
const parts = [`id='${id}'`];
|
|
1401
|
+
if (typeof prop.showName === "boolean") parts.push(`show_name=${prop.showName}`);
|
|
1402
|
+
else if (typeof prop.show_name === "boolean") parts.push(`show_name=${prop.show_name}`);
|
|
1403
|
+
if (typeof prop.showAvatar === "boolean") parts.push(`show_avatar=${prop.showAvatar}`);
|
|
1404
|
+
else if (typeof prop.show_avatar === "boolean") parts.push(`show_avatar=${prop.show_avatar}`);
|
|
1405
|
+
if (typeof prop.style === "string") parts.push(`style='${prop.style}'`);
|
|
1406
|
+
return `<person ${parts.join(" ")}></person>`;
|
|
1407
|
+
}
|
|
1408
|
+
function renderInlineAudio(prop) {
|
|
1409
|
+
const fileKey = typeof prop.fileID === "string" ? prop.fileID : typeof prop.file_key === "string" ? prop.file_key : typeof prop.file_id === "string" ? prop.file_id : "";
|
|
1410
|
+
if (!fileKey) return "";
|
|
1411
|
+
const attrs = [`file_key='${fileKey}'`];
|
|
1412
|
+
const audioId = typeof prop.audioID === "string" ? prop.audioID : typeof prop.audio_id === "string" ? prop.audio_id : void 0;
|
|
1413
|
+
if (audioId) attrs.push(`audio_id='${audioId}'`);
|
|
1414
|
+
if (prop.showTime === true || prop.show_time === true) attrs.push("show_time=true");
|
|
1415
|
+
const style = typeof prop.style === "string" ? prop.style : void 0;
|
|
1416
|
+
if (style) attrs.push(`style='${style}'`);
|
|
1417
|
+
const bg = typeof prop.backgroundColor === "string" ? prop.backgroundColor : typeof prop.background_color === "string" ? prop.background_color : void 0;
|
|
1418
|
+
if (bg) attrs.push(`background_color='${bg}'`);
|
|
1419
|
+
const fill = typeof prop.fillColor === "string" ? prop.fillColor : typeof prop.fill_color === "string" ? prop.fill_color : void 0;
|
|
1420
|
+
if (fill) attrs.push(`fill_color='${fill}'`);
|
|
1421
|
+
const urlObj = isRecord2(prop.url) ? prop.url : void 0;
|
|
1422
|
+
if (urlObj) {
|
|
1423
|
+
if (typeof urlObj.url === "string" && urlObj.url) attrs.push(`fallback_url='${urlObj.url}'`);
|
|
1424
|
+
if (typeof urlObj.pcURL === "string" && urlObj.pcURL) attrs.push(`fallback_pc_url='${urlObj.pcURL}'`);
|
|
1425
|
+
else if (typeof urlObj.pc_url === "string" && urlObj.pc_url) attrs.push(`fallback_pc_url='${urlObj.pc_url}'`);
|
|
1426
|
+
if (typeof urlObj.iosURL === "string" && urlObj.iosURL) attrs.push(`fallback_ios_url='${urlObj.iosURL}'`);
|
|
1427
|
+
else if (typeof urlObj.ios_url === "string" && urlObj.ios_url) attrs.push(`fallback_ios_url='${urlObj.ios_url}'`);
|
|
1428
|
+
if (typeof urlObj.androidURL === "string" && urlObj.androidURL) attrs.push(`fallback_android_url='${urlObj.androidURL}'`);
|
|
1429
|
+
else if (typeof urlObj.android_url === "string" && urlObj.android_url) attrs.push(`fallback_android_url='${urlObj.android_url}'`);
|
|
1430
|
+
if (typeof urlObj.harmonyURL === "string" && urlObj.harmonyURL) attrs.push(`fallback_harmony_url='${urlObj.harmonyURL}'`);
|
|
1431
|
+
else if (typeof urlObj.harmony_url === "string" && urlObj.harmony_url) attrs.push(`fallback_harmony_url='${urlObj.harmony_url}'`);
|
|
1432
|
+
} else if (typeof prop.fallback_url === "string" && prop.fallback_url) {
|
|
1433
|
+
attrs.push(`fallback_url='${prop.fallback_url}'`);
|
|
1434
|
+
}
|
|
1435
|
+
const fb = isRecord2(prop.fallbackText) ? prop.fallbackText : isRecord2(prop.fallback_text) ? prop.fallback_text : void 0;
|
|
1436
|
+
if (fb) {
|
|
1437
|
+
const fbProp = isRecord2(fb.property) ? fb.property : fb;
|
|
1438
|
+
const fbContent = typeof fbProp.content === "string" ? fbProp.content : typeof fb.content === "string" ? fb.content : "";
|
|
1439
|
+
if (fbContent) attrs.push(`fallback_text='${fbContent}'`);
|
|
1440
|
+
}
|
|
1441
|
+
return `<audio ${attrs.join(" ")}></audio>`;
|
|
1442
|
+
}
|
|
1443
|
+
function resolveAtUserId(prop, ctx) {
|
|
1444
|
+
if (typeof prop.userID === "string" && prop.userID) return prop.userID;
|
|
1445
|
+
if (typeof prop.userId === "string" && prop.userId) return prop.userId;
|
|
1446
|
+
if (typeof prop.user_id === "string" && prop.user_id) return prop.user_id;
|
|
1447
|
+
const key = typeof prop.atUserKey === "string" ? prop.atUserKey : typeof prop.at_user_key === "string" ? prop.at_user_key : void 0;
|
|
1448
|
+
if (!key || !ctx.atUsers[key]) return void 0;
|
|
1449
|
+
if (/^(ou_|on_)/.test(key)) return key;
|
|
1450
|
+
return ctx.atUsers[key].user_id;
|
|
1451
|
+
}
|
|
1452
|
+
function rebuildMarkdown(property, ctx) {
|
|
1453
|
+
const out = { tag: "markdown" };
|
|
1454
|
+
const elements = Array.isArray(property.elements) ? property.elements : [];
|
|
1455
|
+
const content = renderChildren(elements, ctx);
|
|
1456
|
+
if (content) out.content = content;
|
|
1457
|
+
for (const [rawKey, rawValue] of Object.entries(property)) {
|
|
1458
|
+
if (rawKey === "elements" || rawKey === "markdownElements" || rawKey === "originTag") continue;
|
|
1459
|
+
if (rawKey === "udIcon") continue;
|
|
1460
|
+
if (rawKey === "textStyle") {
|
|
1461
|
+
const ts = isRecord2(rawValue) ? rawValue : {};
|
|
1462
|
+
const size = typeof ts.size === "string" ? ts.size : void 0;
|
|
1463
|
+
if (size) out.text_size = size;
|
|
1013
1464
|
continue;
|
|
1014
1465
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1466
|
+
const result = convertField(rawKey, rawValue, "markdown", ctx);
|
|
1467
|
+
if (result === DROP) continue;
|
|
1468
|
+
const [outKey, outValue] = result;
|
|
1469
|
+
if (outValue === DROP) continue;
|
|
1470
|
+
out[outKey] = outValue;
|
|
1471
|
+
}
|
|
1472
|
+
return out;
|
|
1473
|
+
}
|
|
1474
|
+
function wrapEmphasis(text, bold, color) {
|
|
1475
|
+
if (!bold && !color) return text;
|
|
1476
|
+
const lead = text.match(/^\s*/)[0];
|
|
1477
|
+
const trail = text.match(/\s*$/)[0];
|
|
1478
|
+
const core = text.slice(lead.length, text.length - trail.length);
|
|
1479
|
+
if (core.length === 0) return text;
|
|
1480
|
+
let wrapped = core;
|
|
1481
|
+
if (bold) wrapped = `**${wrapped}**`;
|
|
1482
|
+
if (color) wrapped = `<font color='${color}'>${wrapped}</font>`;
|
|
1483
|
+
return lead + wrapped + trail;
|
|
1484
|
+
}
|
|
1485
|
+
function renderChildren(children, ctx) {
|
|
1486
|
+
let out = "";
|
|
1487
|
+
let buffer = [];
|
|
1488
|
+
function flushBuffer() {
|
|
1489
|
+
if (buffer.length === 0) return;
|
|
1490
|
+
let group = [];
|
|
1491
|
+
function emitGroup() {
|
|
1492
|
+
if (group.length === 0) return;
|
|
1493
|
+
const inner = group.map((s) => s.content).join("");
|
|
1494
|
+
const { bold, color } = group[0];
|
|
1495
|
+
out += wrapEmphasis(inner, bold, color);
|
|
1496
|
+
group = [];
|
|
1018
1497
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1498
|
+
for (const seg of buffer) {
|
|
1499
|
+
if (group.length === 0) {
|
|
1500
|
+
group.push(seg);
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
const head = group[0];
|
|
1504
|
+
if (head.bold === seg.bold && head.color === seg.color) group.push(seg);
|
|
1505
|
+
else {
|
|
1506
|
+
emitGroup();
|
|
1507
|
+
group.push(seg);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
emitGroup();
|
|
1511
|
+
buffer = [];
|
|
1512
|
+
}
|
|
1513
|
+
for (const child of children) {
|
|
1514
|
+
if (!isRecord2(child)) continue;
|
|
1515
|
+
const inline = childToInlineSegment(child, ctx);
|
|
1516
|
+
if (inline) {
|
|
1517
|
+
buffer.push(inline);
|
|
1021
1518
|
continue;
|
|
1022
1519
|
}
|
|
1023
|
-
|
|
1520
|
+
flushBuffer();
|
|
1521
|
+
out += childToMarkdown(child, ctx);
|
|
1024
1522
|
}
|
|
1523
|
+
flushBuffer();
|
|
1025
1524
|
return out;
|
|
1026
1525
|
}
|
|
1027
|
-
function
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1526
|
+
function childToInlineSegment(child, ctx) {
|
|
1527
|
+
const tag = child.tag;
|
|
1528
|
+
const prop = isRecord2(child.property) ? child.property : {};
|
|
1529
|
+
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1530
|
+
const bold = !!(ts && Array.isArray(ts.attributes) && ts.attributes.includes("bold"));
|
|
1531
|
+
const rawColor = ts && typeof ts.color === "string" ? ts.color : void 0;
|
|
1532
|
+
const color = rawColor === "default" ? void 0 : rawColor;
|
|
1533
|
+
switch (tag) {
|
|
1534
|
+
case "plain_text":
|
|
1535
|
+
return {
|
|
1536
|
+
content: typeof prop.content === "string" ? prop.content : "",
|
|
1537
|
+
bold,
|
|
1538
|
+
color
|
|
1539
|
+
};
|
|
1540
|
+
case "code_span":
|
|
1541
|
+
return {
|
|
1542
|
+
content: `\`${typeof prop.content === "string" ? prop.content : ""}\``,
|
|
1543
|
+
bold,
|
|
1544
|
+
color
|
|
1545
|
+
};
|
|
1546
|
+
case "link": {
|
|
1547
|
+
const text = typeof prop.content === "string" ? prop.content : "";
|
|
1548
|
+
const urlObj = isRecord2(prop.url) ? prop.url : void 0;
|
|
1549
|
+
const url = urlObj && typeof urlObj.url === "string" ? urlObj.url : "";
|
|
1550
|
+
return { content: `[${text}](${url})`, bold, color };
|
|
1551
|
+
}
|
|
1552
|
+
case "at_all":
|
|
1553
|
+
return { content: "<at id=all></at>", bold, color };
|
|
1554
|
+
case "at": {
|
|
1555
|
+
const userId = resolveAtUserId(prop, ctx);
|
|
1556
|
+
return { content: userId ? `<at id=${userId}></at>` : "<at></at>", bold, color };
|
|
1557
|
+
}
|
|
1558
|
+
case "text_tag": {
|
|
1559
|
+
const tagColor = typeof prop.color === "string" ? prop.color : void 0;
|
|
1560
|
+
const inner = isRecord2(prop.text) ? prop.text : void 0;
|
|
1561
|
+
const innerProp = inner && isRecord2(inner.property) ? inner.property : void 0;
|
|
1562
|
+
const innerContent = innerProp && typeof innerProp.content === "string" ? innerProp.content : "";
|
|
1563
|
+
const rendered = tagColor ? `<text_tag color='${tagColor}'>${innerContent}</text_tag>` : `<text_tag>${innerContent}</text_tag>`;
|
|
1564
|
+
return { content: rendered, bold: false };
|
|
1565
|
+
}
|
|
1566
|
+
case "emoji": {
|
|
1567
|
+
const key = typeof prop.key === "string" ? prop.key : typeof prop.emojiKey === "string" ? prop.emojiKey : "";
|
|
1568
|
+
return key ? { content: `:${key}:`, bold: false } : void 0;
|
|
1569
|
+
}
|
|
1570
|
+
case "person":
|
|
1571
|
+
case "person_v1": {
|
|
1572
|
+
const rendered = renderInlinePerson(prop);
|
|
1573
|
+
return rendered ? { content: rendered, bold: false } : void 0;
|
|
1574
|
+
}
|
|
1575
|
+
case "markdown_audio": {
|
|
1576
|
+
const rendered = renderInlineAudio(prop);
|
|
1577
|
+
return rendered ? { content: rendered, bold: false } : void 0;
|
|
1578
|
+
}
|
|
1579
|
+
default:
|
|
1580
|
+
return void 0;
|
|
1031
1581
|
}
|
|
1032
|
-
return v;
|
|
1033
1582
|
}
|
|
1034
|
-
function
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1583
|
+
function childToMarkdown(child, ctx) {
|
|
1584
|
+
const tag = child.tag;
|
|
1585
|
+
const prop = isRecord2(child.property) ? child.property : {};
|
|
1586
|
+
switch (tag) {
|
|
1587
|
+
case "plain_text": {
|
|
1588
|
+
const content = typeof prop.content === "string" ? prop.content : "";
|
|
1589
|
+
const ts = isRecord2(prop.textStyle) ? prop.textStyle : void 0;
|
|
1590
|
+
const color = ts && typeof ts.color === "string" ? ts.color : void 0;
|
|
1591
|
+
const attrs = ts && Array.isArray(ts.attributes) ? ts.attributes : [];
|
|
1592
|
+
return wrapEmphasis(content, attrs.includes("bold"), color);
|
|
1593
|
+
}
|
|
1594
|
+
case "br":
|
|
1595
|
+
return "\n";
|
|
1596
|
+
case "emoji": {
|
|
1597
|
+
const key = typeof prop.key === "string" ? prop.key : typeof prop.emojiKey === "string" ? prop.emojiKey : "";
|
|
1598
|
+
return key ? `:${key}:` : "";
|
|
1599
|
+
}
|
|
1600
|
+
case "at_all":
|
|
1601
|
+
return "<at id=all></at>";
|
|
1602
|
+
case "at": {
|
|
1603
|
+
const userId = resolveAtUserId(prop, ctx);
|
|
1604
|
+
return userId ? `<at id=${userId}></at>` : "<at></at>";
|
|
1605
|
+
}
|
|
1606
|
+
case "person":
|
|
1607
|
+
case "person_v1":
|
|
1608
|
+
return renderInlinePerson(prop);
|
|
1609
|
+
case "markdown_audio":
|
|
1610
|
+
return renderInlineAudio(prop);
|
|
1611
|
+
case "link": {
|
|
1612
|
+
const text = typeof prop.content === "string" ? prop.content : "";
|
|
1613
|
+
const urlObj = isRecord2(prop.url) ? prop.url : void 0;
|
|
1614
|
+
const url = urlObj && typeof urlObj.url === "string" ? urlObj.url : "";
|
|
1615
|
+
return `[${text}](${url})`;
|
|
1616
|
+
}
|
|
1617
|
+
case "code_span": {
|
|
1618
|
+
const text = typeof prop.content === "string" ? prop.content : "";
|
|
1619
|
+
return `\`${text}\``;
|
|
1620
|
+
}
|
|
1621
|
+
case "text_tag": {
|
|
1622
|
+
const color = typeof prop.color === "string" ? prop.color : void 0;
|
|
1623
|
+
const inner = isRecord2(prop.text) ? prop.text : void 0;
|
|
1624
|
+
const innerProp = inner && isRecord2(inner.property) ? inner.property : void 0;
|
|
1625
|
+
const innerContent = innerProp && typeof innerProp.content === "string" ? innerProp.content : "";
|
|
1626
|
+
if (color) return `<text_tag color='${color}'>${innerContent}</text_tag>`;
|
|
1627
|
+
return `<text_tag>${innerContent}</text_tag>`;
|
|
1628
|
+
}
|
|
1629
|
+
case "code_block": {
|
|
1630
|
+
const lang = typeof prop.language === "string" ? prop.language : "";
|
|
1631
|
+
const blocks = Array.isArray(prop.contents) ? prop.contents : [];
|
|
1632
|
+
const lines = blocks.map((blk) => {
|
|
1633
|
+
if (!isRecord2(blk)) return "";
|
|
1634
|
+
const inner = Array.isArray(blk.contents) ? blk.contents : [];
|
|
1635
|
+
let line = "";
|
|
1636
|
+
for (const seg of inner) {
|
|
1637
|
+
if (isRecord2(seg) && typeof seg.content === "string") line += seg.content;
|
|
1638
|
+
}
|
|
1639
|
+
return line;
|
|
1640
|
+
});
|
|
1641
|
+
const body = lines.join("\n").replace(/\n+$/, "");
|
|
1642
|
+
return `
|
|
1643
|
+
\`\`\`${lang}
|
|
1644
|
+
${body}
|
|
1645
|
+
\`\`\`
|
|
1646
|
+
`;
|
|
1647
|
+
}
|
|
1648
|
+
case "list": {
|
|
1649
|
+
const items = Array.isArray(prop.items) ? prop.items : [];
|
|
1650
|
+
const lines = [];
|
|
1651
|
+
const levelCounters = [];
|
|
1652
|
+
for (const item of items) {
|
|
1653
|
+
if (!isRecord2(item)) continue;
|
|
1654
|
+
const level = typeof item.level === "number" ? item.level : 0;
|
|
1655
|
+
const type = typeof item.type === "string" ? item.type : "ul";
|
|
1656
|
+
const indent = " ".repeat(level);
|
|
1657
|
+
const inner = Array.isArray(item.elements) ? item.elements : [];
|
|
1658
|
+
let text = "";
|
|
1659
|
+
for (const seg of inner) {
|
|
1660
|
+
if (!isRecord2(seg)) continue;
|
|
1661
|
+
text += childToMarkdown(seg, ctx);
|
|
1662
|
+
}
|
|
1663
|
+
if (type === "ol") {
|
|
1664
|
+
let n;
|
|
1665
|
+
if (typeof item.order === "number" && Number.isFinite(item.order) && item.order > 0) {
|
|
1666
|
+
n = Math.floor(item.order);
|
|
1667
|
+
levelCounters.length = level + 1;
|
|
1668
|
+
levelCounters[level] = n;
|
|
1669
|
+
} else {
|
|
1670
|
+
levelCounters.length = level + 1;
|
|
1671
|
+
levelCounters[level] = (levelCounters[level] ?? 0) + 1;
|
|
1672
|
+
n = levelCounters[level];
|
|
1673
|
+
}
|
|
1674
|
+
lines.push(`${indent}${n}. ${text}`);
|
|
1675
|
+
} else {
|
|
1676
|
+
lines.push(`${indent}- ${text}`);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return `
|
|
1680
|
+
${lines.join("\n")}
|
|
1681
|
+
|
|
1682
|
+
`;
|
|
1683
|
+
}
|
|
1684
|
+
case "heading": {
|
|
1685
|
+
const level = typeof prop.level === "number" ? prop.level : 1;
|
|
1686
|
+
const inner = Array.isArray(prop.elements) ? prop.elements : [];
|
|
1687
|
+
let body = "";
|
|
1688
|
+
for (const seg of inner) {
|
|
1689
|
+
if (isRecord2(seg)) body += childToMarkdown(seg, ctx);
|
|
1690
|
+
}
|
|
1691
|
+
return `
|
|
1692
|
+
|
|
1693
|
+
${"#".repeat(level)} ${body}
|
|
1694
|
+
|
|
1695
|
+
`;
|
|
1696
|
+
}
|
|
1697
|
+
case "hr": {
|
|
1698
|
+
return "\n\n---\n\n";
|
|
1699
|
+
}
|
|
1700
|
+
case "table": {
|
|
1701
|
+
const columns = Array.isArray(prop.columns) ? prop.columns : [];
|
|
1702
|
+
const rows = Array.isArray(prop.rows) ? prop.rows : [];
|
|
1703
|
+
if (columns.length === 0) return "";
|
|
1704
|
+
const headers = columns.map(
|
|
1705
|
+
(col) => isRecord2(col) && typeof col.displayName === "string" ? col.displayName : ""
|
|
1706
|
+
);
|
|
1707
|
+
const headerLine = `| ${headers.join(" | ")} |`;
|
|
1708
|
+
const separator = `| ${columns.map(() => "--------").join(" | ")} |`;
|
|
1709
|
+
const bodyLines = rows.map((row) => {
|
|
1710
|
+
if (!isRecord2(row)) return `| ${columns.map(() => "").join(" | ")} |`;
|
|
1711
|
+
const cells = columns.map((col) => {
|
|
1712
|
+
const name = isRecord2(col) && typeof col.name === "string" ? col.name : "";
|
|
1713
|
+
const cell = isRecord2(row[name]) ? row[name] : void 0;
|
|
1714
|
+
const data = cell && isRecord2(cell.data) ? cell.data : void 0;
|
|
1715
|
+
if (!data || data.tag !== "markdown") return "";
|
|
1716
|
+
const cellProp = isRecord2(data.property) ? data.property : {};
|
|
1717
|
+
const inner = Array.isArray(cellProp.elements) ? cellProp.elements : [];
|
|
1718
|
+
const text = renderChildren(inner, ctx);
|
|
1719
|
+
return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
1720
|
+
});
|
|
1721
|
+
return `| ${cells.join(" | ")} |`;
|
|
1722
|
+
});
|
|
1723
|
+
return `
|
|
1724
|
+
|
|
1725
|
+
${headerLine}
|
|
1726
|
+
${separator}
|
|
1727
|
+
${bodyLines.join("\n")}
|
|
1728
|
+
|
|
1729
|
+
`;
|
|
1730
|
+
}
|
|
1731
|
+
case "blockquote": {
|
|
1732
|
+
const inner = Array.isArray(prop.elements) ? prop.elements : [];
|
|
1733
|
+
let body = "";
|
|
1734
|
+
for (const seg of inner) {
|
|
1735
|
+
if (isRecord2(seg)) body += childToMarkdown(seg, ctx);
|
|
1736
|
+
}
|
|
1737
|
+
return `
|
|
1738
|
+
|
|
1739
|
+
> ${body.replace(/\n/g, "\n> ")}
|
|
1740
|
+
|
|
1741
|
+
`;
|
|
1742
|
+
}
|
|
1743
|
+
default:
|
|
1744
|
+
return typeof prop.content === "string" ? prop.content : "";
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
function convertGeneric(value, ctx) {
|
|
1748
|
+
if (Array.isArray(value)) {
|
|
1749
|
+
return value.map((item) => {
|
|
1750
|
+
if (isRecord2(item) && typeof item.tag === "string" && isRecord2(item.property)) {
|
|
1751
|
+
return unwrap(item, ctx);
|
|
1752
|
+
}
|
|
1753
|
+
return convertGeneric(item, ctx);
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
if (isRecord2(value)) {
|
|
1757
|
+
if (typeof value.tag === "string" && isRecord2(value.property)) {
|
|
1758
|
+
return unwrap(value, ctx);
|
|
1759
|
+
}
|
|
1760
|
+
if (hasSide(value)) {
|
|
1761
|
+
return formatBox(value);
|
|
1762
|
+
}
|
|
1763
|
+
if (isPixelScalar(value)) {
|
|
1764
|
+
return `${pixelOf(value)}px`;
|
|
1765
|
+
}
|
|
1766
|
+
const out = {};
|
|
1767
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1768
|
+
out[snake(k)] = convertGeneric(v, ctx);
|
|
1769
|
+
}
|
|
1770
|
+
return out;
|
|
1771
|
+
}
|
|
1772
|
+
return value;
|
|
1773
|
+
}
|
|
1774
|
+
function isRecord2(v) {
|
|
1775
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1038
1776
|
}
|
|
1039
|
-
function
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1777
|
+
function resolveAttachmentImgKey(rawValue, ctx) {
|
|
1778
|
+
if (typeof rawValue !== "string" && typeof rawValue !== "number") return void 0;
|
|
1779
|
+
const id = String(rawValue);
|
|
1780
|
+
const origin = ctx.images[id]?.origin_key;
|
|
1781
|
+
if (typeof origin === "string" && origin) return origin;
|
|
1782
|
+
if (id.startsWith("img_")) return id;
|
|
1783
|
+
return void 0;
|
|
1784
|
+
}
|
|
1785
|
+
function normalizeVideoCover(rawValue, ctx) {
|
|
1786
|
+
if (typeof rawValue === "string" || typeof rawValue === "number") {
|
|
1787
|
+
const imgKey2 = resolveAttachmentImgKey(rawValue, ctx);
|
|
1788
|
+
return imgKey2 ? { img_key: imgKey2 } : void 0;
|
|
1789
|
+
}
|
|
1790
|
+
if (!isRecord2(rawValue)) return void 0;
|
|
1791
|
+
if (typeof rawValue.img_key === "string" && rawValue.img_key) {
|
|
1792
|
+
return { img_key: rawValue.img_key };
|
|
1793
|
+
}
|
|
1794
|
+
const candidate = rawValue.imageID ?? rawValue.image_id ?? rawValue.cover_image_id ?? rawValue.coverImageId;
|
|
1795
|
+
const imgKey = resolveAttachmentImgKey(candidate, ctx);
|
|
1796
|
+
return imgKey ? { img_key: imgKey } : void 0;
|
|
1797
|
+
}
|
|
1798
|
+
function hasSide(v) {
|
|
1799
|
+
return ("top" in v || "bottom" in v || "left" in v || "right" in v) && Object.values(v).some((side) => isRecord2(side) && "type" in side && "value" in side);
|
|
1800
|
+
}
|
|
1801
|
+
function formatBox(v) {
|
|
1802
|
+
return `${pixelOf(v.top)}px ${pixelOf(v.right)}px ${pixelOf(v.bottom)}px ${pixelOf(v.left)}px`;
|
|
1803
|
+
}
|
|
1804
|
+
function isPixelScalar(v) {
|
|
1805
|
+
if (!isRecord2(v)) return false;
|
|
1806
|
+
return typeof v.type === "string" && v.type.includes("pixels") && typeof v.value === "number";
|
|
1807
|
+
}
|
|
1808
|
+
function pixelOf(side) {
|
|
1809
|
+
if (!isRecord2(side)) return 0;
|
|
1810
|
+
const v = side.value;
|
|
1811
|
+
if (typeof v === "number") return v;
|
|
1812
|
+
if (typeof v === "string") {
|
|
1813
|
+
const n = Number(v);
|
|
1814
|
+
return Number.isFinite(n) ? n : 0;
|
|
1815
|
+
}
|
|
1816
|
+
return 0;
|
|
1817
|
+
}
|
|
1818
|
+
function snake(key) {
|
|
1819
|
+
return key.replace(/ID$/g, "_id").replace(/URL$/g, "_url").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
|
|
1820
|
+
}
|
|
1821
|
+
function rawToDsl(raw) {
|
|
1822
|
+
if (isRawCardEnvelope(raw)) return rawCardToDsl(raw);
|
|
1823
|
+
if (typeof raw === "string") {
|
|
1824
|
+
try {
|
|
1825
|
+
return rawToDsl(JSON.parse(raw));
|
|
1826
|
+
} catch {
|
|
1827
|
+
return raw;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
if (!isRecord2(raw)) return raw;
|
|
1831
|
+
if (typeof raw.json_card === "string" || isRecord2(raw.json_card)) {
|
|
1832
|
+
let inner = raw.json_card;
|
|
1833
|
+
if (typeof inner === "string") {
|
|
1834
|
+
try {
|
|
1835
|
+
inner = JSON.parse(inner);
|
|
1836
|
+
} catch {
|
|
1837
|
+
return raw;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
const attachment = isRecord2(raw.json_attachment) ? raw.json_attachment : {};
|
|
1841
|
+
const ctx = {
|
|
1842
|
+
images: isRecord2(attachment.images) ? attachment.images : {},
|
|
1843
|
+
atUsers: isRecord2(attachment.at_users) ? attachment.at_users : {}
|
|
1844
|
+
};
|
|
1845
|
+
return convertRoot(inner, ctx);
|
|
1846
|
+
}
|
|
1847
|
+
if (isRecord2(raw.card)) {
|
|
1848
|
+
const card = raw.card;
|
|
1849
|
+
if (typeof card.json_card === "string" || isRecord2(card.json_card)) {
|
|
1850
|
+
return rawToDsl(card);
|
|
1851
|
+
}
|
|
1852
|
+
return convertRoot(card, { images: {}, atUsers: {} });
|
|
1853
|
+
}
|
|
1854
|
+
if ("body" in raw || "header" in raw || "schema" in raw) {
|
|
1855
|
+
return convertRoot(raw, { images: {}, atUsers: {} });
|
|
1856
|
+
}
|
|
1857
|
+
return raw;
|
|
1858
|
+
}
|
|
1859
|
+
function degradeUnknown(el) {
|
|
1860
|
+
const rawTag = typeof el.tag === "string" ? el.tag : "undefined";
|
|
1861
|
+
return { tag: "unknown", raw_tag: rawTag, fallback: el };
|
|
1047
1862
|
}
|
|
1048
1863
|
|
|
1049
1864
|
exports.degradeUnknown = degradeUnknown;
|
|
1050
1865
|
exports.dslToRaw = dslToRaw;
|
|
1866
|
+
exports.isRawCardEnvelope = isRawCardEnvelope;
|
|
1867
|
+
exports.rawCardToDsl = rawCardToDsl;
|
|
1051
1868
|
exports.rawToDsl = rawToDsl;
|