@open-feishu-card/adapter 0.1.1 → 0.2.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 ADDED
@@ -0,0 +1,1051 @@
1
+ 'use strict';
2
+
3
+ // src/emoji-data.ts
4
+ var EMOJI_KEY_TO_NAME = {
5
+ "AINailedIt": "ai_nailed_it_zh_v2",
6
+ "ANGRY": "angry",
7
+ "APPLAUSE": "applaud_v2",
8
+ "ATTENTION": "attention",
9
+ "AWESOME": "awesome",
10
+ "AWESOMEN": "awesomen_v2",
11
+ "Alarm": "alarm",
12
+ "Ambitious": "bepatientandthinklongterm",
13
+ "BEAR": "bear",
14
+ "BEER": "beer",
15
+ "BETRAYED": "betrayed",
16
+ "BIGKISS": "bigkiss",
17
+ "BLACKFACE": "blackface",
18
+ "BLUBBER": "blubber",
19
+ "BLUSH": "grin",
20
+ "BOMB": "bomb",
21
+ "BULL": "bull",
22
+ "Basketball": "basketball",
23
+ "BeAtTheForefront": "beattheforefront_v1",
24
+ "BeamingFace": "beamingface_v1",
25
+ "BubbleTea": "bubbletea",
26
+ "CAKE": "cake_v2",
27
+ "CALF": "calf",
28
+ "CANDIEDHAWS": "candiedhaws",
29
+ "CHUCKLE": "chuckle",
30
+ "CLAP": "clap",
31
+ "CLEAVER": "cleaver_v3",
32
+ "COMFORT": "comfort",
33
+ "CRAZY": "crazy",
34
+ "CRY": "cry",
35
+ "CUCUMBER": "cucumber_v3",
36
+ "CheckMark": "checkmark",
37
+ "ClownFace": "clownface_v1",
38
+ "Coffee": "coffee_v2",
39
+ "ColdSweat": "coldsweat_v1",
40
+ "CrossMark": "crossmark",
41
+ "CustomerSuccess": "customersuccess_v2",
42
+ "DETERGENT": "detergent",
43
+ "DIZZY": "dizzy",
44
+ "DONE": "done",
45
+ "DONNOTGO": "donnotgo",
46
+ "DROOL": "drool",
47
+ "DROWSY": "drowsy",
48
+ "DULL": "dull",
49
+ "DULLSTARE": "dullstare",
50
+ "DarkApplaud": "darkapplaud_v2",
51
+ "DarkClick": "darkclick_v2",
52
+ "DarkFightOn": "darkfighton_v2",
53
+ "DarkFingerHeart": "darkfingerheart_v2",
54
+ "DarkFistBump": "darkfistbump_v2",
55
+ "DarkHighFive": "darkhighfive_v2",
56
+ "DarkOKHand": "darkokhand",
57
+ "DarkSalute": "darksalute_v2",
58
+ "DarkShake": "darkshake_v2",
59
+ "DarkThanks": "darkthanks_v2",
60
+ "DarkThumbsDown": "darkthumbsdown_v2",
61
+ "DarkThumbsup": "darkthumbsup_v2",
62
+ "DarkWavingHand": "darkwavinghand_v1",
63
+ "DarkYouAreTheBest": "darkyouarethebest_v1",
64
+ "Delighted": "delighted_v1",
65
+ "Drumstick": "drumstick",
66
+ "EATING": "eating",
67
+ "EMBARRASSED": "embarrassed",
68
+ "ENOUGH": "enough",
69
+ "ERROR": "errr",
70
+ "EYESCLOSED": "eyesclosed",
71
+ "EatingFood": "eatingfood_v2",
72
+ "FACEPALM": "facepalm",
73
+ "FINGERHEART": "fingerheart_v2",
74
+ "FIRECRACKER": "firecracker_v3",
75
+ "FIREWORKS": "fireworks_v2",
76
+ "FISTBUMP": "fistbump_v2",
77
+ "FOLLOWME": "followme",
78
+ "FORTUNE": "fortune",
79
+ "FROWN": "frown",
80
+ "Fire": "fire",
81
+ "FullMoonFace": "fullmoonface_v1",
82
+ "GIFT": "gift_v2",
83
+ "GLANCE": "glance",
84
+ "GOODJOB": "goodjob",
85
+ "GeneralBusinessTrip": "generalbusinesstrip_v2",
86
+ "GeneralDoNotDisturb": "generaldonotdisturb_v3",
87
+ "GeneralMoonRest": "generalmoonrest_v2",
88
+ "GeneralSun": "generalsun",
89
+ "GeneralTravellingCar": "generaltravellingcar",
90
+ "GeneralVacation": "onleavestatus",
91
+ "GeneralWorkFromHome": "generalworkfromhome",
92
+ "Get": "get_v3",
93
+ "GoGoGo": "gogogo_v4",
94
+ "HAMMER": "hammer_v1",
95
+ "HAUGHTY": "irritated_v2",
96
+ "HEADSET": "headset_v3",
97
+ "HEART": "heart",
98
+ "HEARTBROKEN": "heartbroken",
99
+ "HIGHFIVE": "highfive_v2",
100
+ "HUG": "hug",
101
+ "HUSKY": "husky",
102
+ "HappyDragon": "happydragon_v3",
103
+ "Hundred": "hundred",
104
+ "INNOCENTSMILE": "innocentsmile",
105
+ "JIAYI": "plusone",
106
+ "JOYFUL": "joyful",
107
+ "JubilantRabbit": "jubilantrabbit_v1",
108
+ "KISS": "kiss",
109
+ "LAUGH": "laugh",
110
+ "LGTM": "lgtm_v3",
111
+ "LIPS": "lips_v1",
112
+ "LOL": "lol",
113
+ "LOOKDOWN": "lookdown",
114
+ "LOVE": "love",
115
+ "LUCK": "luck",
116
+ "Lemon": "lemon_v2",
117
+ "LightApplaud": "lightapplaud_v2",
118
+ "LightClick": "lightclick_v2",
119
+ "LightFightOn": "lightfighton_v2",
120
+ "LightFingerHeart": "lightfingerheart_v2",
121
+ "LightFistBump": "lightfistbump_v2",
122
+ "LightHighFive": "lighthighfive_v2",
123
+ "LightOKHand": "lightokhand",
124
+ "LightSalute": "lightsalute_v2",
125
+ "LightShake": "lightshake_v2",
126
+ "LightThanks": "lightthanks_v2",
127
+ "LightThumbsDown": "lightthumbsdown_v2",
128
+ "LightThumbsup": "lightthumbsup_v2",
129
+ "LightWavingHand": "lightwavinghand_v1",
130
+ "LightYouAreTheBest": "lightyouarethebest_v1",
131
+ "Loudspeaker": "loudspeaker",
132
+ "MONEY": "money",
133
+ "MUSCLE": "fighton_v2",
134
+ "MeMeMe": "mememe",
135
+ "MediumApplaud": "mediumapplaud_v2",
136
+ "MediumClick": "mediumclick_v2",
137
+ "MediumDarkApplaud": "mediumdarkapplaud_v2",
138
+ "MediumDarkClick": "mediumdarkclick_v2",
139
+ "MediumDarkFightOn": "mediumdarkfighton_v2",
140
+ "MediumDarkFingerHeart": "mediumdarkfingerheart_v2",
141
+ "MediumDarkFistBump": "mediumdarkfistbump_v2",
142
+ "MediumDarkHighFive": "mediumdarkhighfive_v2",
143
+ "MediumDarkSalute": "mediumdarksalute_v2",
144
+ "MediumDarkShake": "mediumdarkshake_v2",
145
+ "MediumDarkThanks": "mediumdarkthanks_v2",
146
+ "MediumDarkThumbsDown": "mediumdarkthumbsdown_v2",
147
+ "MediumDarkThumbsup": "mediumdarkthumbsup_v2",
148
+ "MediumFightOn": "mediumfighton_v2",
149
+ "MediumFingerHeart": "mediumfingerheart_v2",
150
+ "MediumFistBump": "mediumfistbump_v2",
151
+ "MediumHighFive": "mediumhighfive_v2",
152
+ "MediumLightApplaud": "mediumlightapplaud_v2",
153
+ "MediumLightClick": "mediumlightclick_v2",
154
+ "MediumLightFightOn": "mediumlightfighton_v2",
155
+ "MediumLightFingerHeart": "mediumlightfingerheart_v2",
156
+ "MediumLightFistBump": "mediumlightfistbump_v2",
157
+ "MediumLightHighFive": "mediumlighthighfive_v2",
158
+ "MediumLightSalute": "mediumlightsalute_v2",
159
+ "MediumLightShake": "mediumlightshake_v2",
160
+ "MediumLightThanks": "mediumlightthanks_v2",
161
+ "MediumLightThumbsDown": "mediumlightthumbsdown_v2",
162
+ "MediumLightThumbsup": "mediumlightthumbsup_v2",
163
+ "MediumOKHand": "mediumokhand",
164
+ "MediumSalute": "mediumsalute_v2",
165
+ "MediumShake": "mediumshake_v2",
166
+ "MediumThanks": "mediumthanks_v2",
167
+ "MediumThumbsDown": "mediumthumbsdown_v2",
168
+ "MediumThumbsup": "mediumthumbsup_v2",
169
+ "MediumWavingHand": "mediumwavinghand_v1",
170
+ "MediumYouAreTheBest": "mediumyouarethebest_v1",
171
+ "MediumdarkOKHand": "mediumdarkokhand",
172
+ "MediumdarkWavingHand": "mediumdarkwavinghand_v1",
173
+ "MediumdarkYouAreTheBest": "mediumdarkyouarethebest_v1",
174
+ "MediumlightOKHand": "mediumlightokhand",
175
+ "MediumlightWavingHand": "mediumlightwavinghand_v1",
176
+ "MediumlightYouAreTheBest": "mediumlightyouarethebest_v1",
177
+ "MinusOne": "minusone_v3",
178
+ "MoonRabbit": "moonrabbit_v2",
179
+ "Mooncake": "mooncake_v2",
180
+ "Movie": "movie_v1",
181
+ "Music": "music_v2",
182
+ "NOSEPICK": "nosepick",
183
+ "No": "no_v3",
184
+ "OBSESSED": "hearts_v2",
185
+ "OK": "ok_v2",
186
+ "OKHand": "okhand",
187
+ "OKR": "okr",
188
+ "OnIt": "onit_v1",
189
+ "OneSecond": "onesecond",
190
+ "PARTY": "party",
191
+ "PETRIFIED": "petrified",
192
+ "POOP": "poop",
193
+ "PRAISE": "praise",
194
+ "PROUD": "proud",
195
+ "PUKE": "puke",
196
+ "Partying": "partying_v1",
197
+ "Pepper": "pepper",
198
+ "Pin": "pin",
199
+ "Pumpkin": "pumpkin_v1",
200
+ "PursueUltimate": "pursueultimate_v2",
201
+ "RAINBOWPUKE": "rainbowpuke_v2",
202
+ "REDPACKET": "redpacket",
203
+ "ROSE": "rose",
204
+ "Responsible": "bereliableandtakeownership",
205
+ "RoarForYou": "roarforyou",
206
+ "SALUTE": "salute_v2",
207
+ "SCOWL": "scowl",
208
+ "SHAKE": "shake_v2",
209
+ "SHHH": "shhh",
210
+ "SHOCKED": "shocked",
211
+ "SHOWOFF": "showoff",
212
+ "SHY": "shy",
213
+ "SICK": "sick",
214
+ "SILENT": "silent",
215
+ "SKULL": "skull",
216
+ "SLAP": "slap",
217
+ "SLEEP": "sleep",
218
+ "SLIGHT": "slight",
219
+ "SMART": "smart",
220
+ "SMILE": "smile",
221
+ "SMIRK": "smirk",
222
+ "SMOOCH": "smooch",
223
+ "SMUG": "smug",
224
+ "SOB": "sob",
225
+ "SPEECHLESS": "speechless",
226
+ "SPITBLOOD": "spitblood",
227
+ "STRIVE": "strive",
228
+ "SWEAT": "sweat",
229
+ "SaluteFace": "saluteface_v1",
230
+ "Shrug": "shrug_v1",
231
+ "SiSiASYouWish": "sisiasyouwish_v1",
232
+ "Sigh": "sigh",
233
+ "Snowman": "snowman",
234
+ "Soccer": "soccer",
235
+ "StatusBus": "statusbus",
236
+ "StatusEnjoyLife": "statusenjoylife",
237
+ "StatusInFlight": "statusinflight",
238
+ "StatusReading": "statusreading",
239
+ "Status_PrivateMessage": "status_privatemessage",
240
+ "StickyRiceBalls": "stickyriceballs",
241
+ "TEARS": "tears",
242
+ "TEASE": "tease",
243
+ "TERROR": "terror",
244
+ "THANKS": "thanks_v2",
245
+ "THINKING": "thinking",
246
+ "THUMBSUP": "thumbsup_v2",
247
+ "TOASTED": "toasted",
248
+ "TONGUE": "tongue",
249
+ "TRICK": "trick",
250
+ "TV": "tv_v1",
251
+ "ThanksFace": "thanksface_v1",
252
+ "ThanksHoldingBoard": "thanksholdingboard_v1",
253
+ "ThumbsDown": "thumbsdown_v2",
254
+ "Trophy": "trophy",
255
+ "Typing": "typing",
256
+ "UPPERLEFT": "click_v2",
257
+ "VRHeadset": "vrheadset_v1",
258
+ "WAIL": "wail",
259
+ "WAVE": "wave",
260
+ "WELLDONE": "welldone",
261
+ "WHAT": "what",
262
+ "WHIMPER": "whimper",
263
+ "WINK": "wink",
264
+ "WITTY": "witty",
265
+ "WOW": "wow",
266
+ "WRONGED": "wronged",
267
+ "WavingHand": "wavinghand_v1",
268
+ "XBLUSH": "blush",
269
+ "XmasHat": "xmashat",
270
+ "XmasTree": "xmastree",
271
+ "YAWN": "yawn",
272
+ "YEAH": "yeah",
273
+ "Yes": "yes_v2",
274
+ "YouAreTheBest": "youarethebest"
275
+ };
276
+
277
+ // src/md-parse.ts
278
+ function styleAttrs(s) {
279
+ const attributes = [];
280
+ if (s.bold) attributes.push("bold");
281
+ if (s.italic) attributes.push("italic");
282
+ if (s.strike) attributes.push("strikethrough");
283
+ const hasColor = s.color && s.color !== "default";
284
+ if (attributes.length === 0 && !hasColor) return void 0;
285
+ const ts = {};
286
+ if (attributes.length) ts.attributes = attributes;
287
+ if (hasColor) ts.color = s.color;
288
+ return ts;
289
+ }
290
+ function makeText(content, s) {
291
+ const property = { content };
292
+ const ts = styleAttrs(s);
293
+ if (ts) property.textStyle = ts;
294
+ return { tag: "plain_text", property };
295
+ }
296
+ function parseInline(text) {
297
+ const out = [];
298
+ const style = { bold: false, italic: false, strike: false };
299
+ let buf = "";
300
+ const flush = () => {
301
+ if (buf) {
302
+ out.push(makeText(buf, style));
303
+ buf = "";
304
+ }
305
+ };
306
+ let i = 0;
307
+ const n = text.length;
308
+ const startsWith = (s) => text.startsWith(s, i);
309
+ while (i < n) {
310
+ const c = text[i];
311
+ if (c === "\n") {
312
+ flush();
313
+ out.push({ tag: "br", property: {} });
314
+ i += 1;
315
+ continue;
316
+ }
317
+ if (c === "\\" && i + 1 < n) {
318
+ buf += text[i + 1];
319
+ i += 2;
320
+ continue;
321
+ }
322
+ if (c === ":") {
323
+ const mm = /^:([A-Za-z0-9_]+):/.exec(text.slice(i));
324
+ if (mm && EMOJI_KEY_TO_NAME[mm[1]]) {
325
+ flush();
326
+ out.push({ tag: "emoji", property: { key: mm[1] } });
327
+ i += mm[0].length;
328
+ continue;
329
+ }
330
+ }
331
+ if (startsWith("**")) {
332
+ flush();
333
+ style.bold = !style.bold;
334
+ i += 2;
335
+ continue;
336
+ }
337
+ if (startsWith("~~")) {
338
+ flush();
339
+ style.strike = !style.strike;
340
+ i += 2;
341
+ continue;
342
+ }
343
+ if (c === "*" || c === "_") {
344
+ const prev = text[i - 1];
345
+ const next = text[i + 1];
346
+ const boundaryOk = c === "*" || (!isWord(prev) || !isWord(next));
347
+ if (boundaryOk) {
348
+ flush();
349
+ style.italic = !style.italic;
350
+ i += 1;
351
+ continue;
352
+ }
353
+ }
354
+ if (c === "`") {
355
+ const end = text.indexOf("`", i + 1);
356
+ if (end !== -1) {
357
+ flush();
358
+ out.push({ tag: "code_span", property: { content: text.slice(i + 1, end) } });
359
+ i = end + 1;
360
+ continue;
361
+ }
362
+ }
363
+ if (c === "[") {
364
+ const link = matchLink(text, i);
365
+ if (link) {
366
+ flush();
367
+ const property = { content: link.text, url: link.url };
368
+ const ts = styleAttrs(style);
369
+ if (ts) property.textStyle = ts;
370
+ out.push({ tag: "link", property });
371
+ i = link.end;
372
+ continue;
373
+ }
374
+ }
375
+ if (startsWith("<font")) {
376
+ const m = /^<font\s+color=['"]?([^'">]+)['"]?\s*>/.exec(text.slice(i));
377
+ if (m) {
378
+ flush();
379
+ style.color = m[1];
380
+ i += m[0].length;
381
+ continue;
382
+ }
383
+ }
384
+ if (startsWith("</font>")) {
385
+ flush();
386
+ style.color = void 0;
387
+ i += "</font>".length;
388
+ continue;
389
+ }
390
+ if (startsWith("<at")) {
391
+ const m = /^<at\s+id=['"]?([^'">\s]+)['"]?\s*>\s*<\/at>/.exec(text.slice(i));
392
+ if (m) {
393
+ flush();
394
+ if (m[1] === "all") out.push({ tag: "at_all", property: {} });
395
+ else out.push({ tag: "at", property: { userID: m[1] } });
396
+ i += m[0].length;
397
+ continue;
398
+ }
399
+ }
400
+ if (startsWith("<text_tag")) {
401
+ const m = /^<text_tag(?:\s+color=['"]?([^'">]+)['"]?)?\s*>([\s\S]*?)<\/text_tag>/.exec(text.slice(i));
402
+ if (m) {
403
+ flush();
404
+ const property = {
405
+ text: { tag: "plain_text", property: { content: m[2] } }
406
+ };
407
+ if (m[1]) property.color = m[1];
408
+ out.push({ tag: "text_tag", property });
409
+ i += m[0].length;
410
+ continue;
411
+ }
412
+ }
413
+ if (startsWith("<person")) {
414
+ const m = /^<person\b([^>]*?)(?:\/>|>\s*<\/person>)/.exec(text.slice(i));
415
+ if (m) {
416
+ flush();
417
+ const attrs = parseHtmlAttrs(m[1]);
418
+ const property = {
419
+ userID: attrs.id || attrs.user_id || "",
420
+ // 标记为 markdown 内联:渲染时跟正文字号,而非独立 person 的 medium 默认
421
+ avatarFollowFontSize: true
422
+ };
423
+ if (attrs.show_name != null) property.showName = attrs.show_name !== "false";
424
+ if (attrs.show_avatar != null) property.showAvatar = attrs.show_avatar !== "false";
425
+ if (attrs.size) property.size = attrs.size;
426
+ if (attrs.style === "capsule") property.pillShaped = true;
427
+ out.push({ tag: "person_v1", property });
428
+ i += m[0].length;
429
+ continue;
430
+ }
431
+ }
432
+ if (startsWith("<audio")) {
433
+ const m = /^<audio\b([^>]*?)(?:\/>|>\s*<\/audio>|>)/.exec(text.slice(i));
434
+ if (m) {
435
+ flush();
436
+ out.push({ tag: "markdown_audio", property: audioAttrsToProperty(parseHtmlAttrs(m[1])) });
437
+ i += m[0].length;
438
+ continue;
439
+ }
440
+ }
441
+ buf += c;
442
+ i += 1;
443
+ }
444
+ flush();
445
+ return out;
446
+ }
447
+ function parseHtmlAttrs(s) {
448
+ const out = {};
449
+ const re = /([A-Za-z_][\w-]*)\s*=\s*(?:'([^']*)'|"([^"]*)"|([^\s'">=]+))/g;
450
+ let m;
451
+ while (m = re.exec(s)) {
452
+ out[m[1]] = m[2] ?? m[3] ?? m[4] ?? "";
453
+ }
454
+ return out;
455
+ }
456
+ function attrBool(v) {
457
+ if (v == null) return void 0;
458
+ return v !== "false" && v !== "0";
459
+ }
460
+ function audioAttrsToProperty(attrs) {
461
+ const property = {};
462
+ const fileID = attrs.file_key || attrs.fileID || attrs.file_id;
463
+ if (fileID) property.fileID = fileID;
464
+ const audioID = attrs.audio_id || attrs.audioID;
465
+ if (audioID) property.audioID = audioID;
466
+ const showTime = attrBool(attrs.show_time ?? attrs.showTime);
467
+ if (showTime != null) property.showTime = showTime;
468
+ const showProgress = attrBool(attrs.show_progress_bar ?? attrs.showProgressBar);
469
+ if (showProgress != null) property.showProgressBar = showProgress;
470
+ const disabled = attrBool(attrs.disabled);
471
+ if (disabled != null) property.disabled = disabled;
472
+ const bg = attrs.background_color || attrs.backgroundColor;
473
+ if (bg) property.backgroundColor = bg;
474
+ const fill = attrs.fill_color || attrs.fillColor;
475
+ if (fill) property.fillColor = fill;
476
+ const fallback = attrs.fallback_url || attrs.fallbackUrl;
477
+ if (fallback) property.url = { url: fallback };
478
+ if (attrs.style === "speak" || attrs.style === "normal") {
479
+ property.audioStyle = attrs.style;
480
+ }
481
+ return property;
482
+ }
483
+ function isWord(ch) {
484
+ return !!ch && /[\w一-龥]/.test(ch);
485
+ }
486
+ function matchLink(text, start) {
487
+ let depth = 0;
488
+ let j = start;
489
+ for (; j < text.length; j++) {
490
+ if (text[j] === "[") depth++;
491
+ else if (text[j] === "]") {
492
+ depth--;
493
+ if (depth === 0) break;
494
+ }
495
+ }
496
+ if (j >= text.length || text[j + 1] !== "(") return null;
497
+ const label = text.slice(start + 1, j);
498
+ const close = text.indexOf(")", j + 2);
499
+ if (close === -1) return null;
500
+ const url = text.slice(j + 2, close);
501
+ return { text: label, url, end: close + 1 };
502
+ }
503
+ var RE_HEADING = /^(#{1,6})\s+(.*)$/;
504
+ var RE_HR = /^\s*([-*_])\1{2,}\s*$/;
505
+ var RE_LIST = /^(\s*)([-*+]|\d+\.)\s+(.*)$/;
506
+ var RE_FENCE = /^\s*```(.*)$/;
507
+ function parseMarkdown(content) {
508
+ if (!content) return [];
509
+ const lines = String(content).replace(/\r\n?/g, "\n").split("\n");
510
+ const out = [];
511
+ let para = [];
512
+ const flushPara = () => {
513
+ if (para.length === 0) return;
514
+ const seg = parseInline(para.join("\n"));
515
+ out.push(...seg);
516
+ para = [];
517
+ };
518
+ for (let i = 0; i < lines.length; i++) {
519
+ const line = lines[i];
520
+ if (line.trim() === "") {
521
+ if (para.length) {
522
+ flushPara();
523
+ out.push({ tag: "br", property: {} });
524
+ }
525
+ continue;
526
+ }
527
+ const fence = RE_FENCE.exec(line);
528
+ if (fence) {
529
+ flushPara();
530
+ const language = fence[1].trim();
531
+ const body = [];
532
+ i++;
533
+ for (; i < lines.length; i++) {
534
+ if (/^\s*```/.test(lines[i])) break;
535
+ body.push(lines[i]);
536
+ }
537
+ out.push({
538
+ tag: "code_block",
539
+ property: {
540
+ language,
541
+ contents: body.map((l) => ({ contents: [{ content: l }] }))
542
+ }
543
+ });
544
+ continue;
545
+ }
546
+ const heading = RE_HEADING.exec(line);
547
+ if (heading) {
548
+ flushPara();
549
+ out.push({
550
+ tag: "heading",
551
+ property: { level: heading[1].length, elements: parseInline(heading[2]) }
552
+ });
553
+ continue;
554
+ }
555
+ if (RE_HR.test(line)) {
556
+ flushPara();
557
+ out.push({ tag: "hr", property: {} });
558
+ continue;
559
+ }
560
+ if (/^\s*>\s?/.test(line)) {
561
+ flushPara();
562
+ const quote = [];
563
+ for (; i < lines.length; i++) {
564
+ if (!/^\s*>\s?/.test(lines[i])) {
565
+ i--;
566
+ break;
567
+ }
568
+ quote.push(lines[i].replace(/^\s*>\s?/, ""));
569
+ }
570
+ out.push({ tag: "blockquote", property: { elements: parseInline(quote.join("\n")) } });
571
+ continue;
572
+ }
573
+ if (RE_LIST.test(line)) {
574
+ flushPara();
575
+ const items = [];
576
+ let counter = 0;
577
+ let lastOrdered;
578
+ for (; i < lines.length; i++) {
579
+ const m = RE_LIST.exec(lines[i]);
580
+ if (!m) {
581
+ i--;
582
+ break;
583
+ }
584
+ const indent = m[1].replace(/\t/g, " ").length;
585
+ const level = Math.floor(indent / 2);
586
+ const ordered = /\d+\./.test(m[2]);
587
+ if (ordered !== lastOrdered) counter = 0;
588
+ lastOrdered = ordered;
589
+ const item = {
590
+ type: ordered ? "ol" : "ul",
591
+ level,
592
+ elements: parseInline(m[3])
593
+ };
594
+ if (ordered) {
595
+ counter += 1;
596
+ item.order = counter;
597
+ }
598
+ items.push(item);
599
+ }
600
+ out.push({ tag: "list", property: { items } });
601
+ continue;
602
+ }
603
+ para.push(line);
604
+ }
605
+ flushPara();
606
+ return out;
607
+ }
608
+
609
+ // src/dsl-to-raw.ts
610
+ var KEY_RENAME = {
611
+ text_align: "textAlign",
612
+ text_style: "textStyle",
613
+ background_style: "backgroundStyle",
614
+ background_color: "backgroundColor",
615
+ user_id: "userID",
616
+ show_avatar: "showAvatar",
617
+ show_name: "showName",
618
+ has_border: "hasBorder",
619
+ hover_tips: "hoverTips",
620
+ pill_shaped: "pillShaped",
621
+ tag_style: "tagStyle",
622
+ scale_type: "scaleType",
623
+ drop_invalid_user_id: "dropInvalidUserID",
624
+ // video / audio
625
+ file_key: "fileID",
626
+ audio_id: "audioID",
627
+ show_progress_bar: "showProgressBar",
628
+ show_time: "showTime",
629
+ enable_download: "enableDownload",
630
+ fill_color: "fillColor"
631
+ };
632
+ var TEXT_LEAF_KEYS = /* @__PURE__ */ new Set(["title", "subtitle", "alt", "hover_tips", "placeholder"]);
633
+ function dslToRaw(dsl) {
634
+ const root = isRecord(dsl) ? dsl : {};
635
+ const card = isRecord(root.card) ? root.card : isRecord(root.json_card) ? root.json_card : root;
636
+ const out = { schema: typeof card.schema === "string" ? card.schema : "2.0" };
637
+ if (isRecord(card.header)) out.header = convertHeader(card.header);
638
+ if (isRecord(card.body)) out.body = convertBody(card.body);
639
+ return out;
640
+ }
641
+ function convertHeader(header) {
642
+ const property = {};
643
+ for (const [k, v] of Object.entries(header)) {
644
+ if (k === "tag") continue;
645
+ if (TEXT_LEAF_KEYS.has(k)) {
646
+ property[k] = wrapTextLeaf(v);
647
+ continue;
648
+ }
649
+ if ((k === "icon" || k === "ud_icon") && isRecord(v)) {
650
+ property[k === "ud_icon" ? "udIcon" : "icon"] = convertEl(v);
651
+ continue;
652
+ }
653
+ if (k === "text_tag_list" && Array.isArray(v)) {
654
+ property.text_tag_list = v.map(convertNode);
655
+ continue;
656
+ }
657
+ property[renameKey(k)] = coerceValue(k, v);
658
+ }
659
+ return { tag: "card_header", property };
660
+ }
661
+ function convertBody(body) {
662
+ const property = buildProperty(body, "body");
663
+ const elements = Array.isArray(body.elements) ? body.elements.map(convertNode) : [];
664
+ property.elements = elements.filter(Boolean);
665
+ return { tag: "body", property };
666
+ }
667
+ function convertNode(node) {
668
+ if (isRecord(node) && typeof node.tag === "string") return convertEl(node);
669
+ return node;
670
+ }
671
+ function convertEl(el) {
672
+ const tag = String(el.tag);
673
+ if (tag === "person") {
674
+ const property = buildProperty(el, "person_v1");
675
+ if (property.style === "capsule") {
676
+ property.pillShaped = true;
677
+ delete property.style;
678
+ }
679
+ return { tag: "person_v1", property };
680
+ }
681
+ if (tag === "markdown" || tag === "lark_md") {
682
+ const property = buildProperty(el, "markdown");
683
+ if (typeof el.content === "string") property.elements = parseMarkdown(el.content);
684
+ delete property.content;
685
+ return { tag: "markdown", property };
686
+ }
687
+ if (tag === "table") {
688
+ return { tag, property: convertTableProperty(el) };
689
+ }
690
+ if (tag === "audio" || tag === "video") {
691
+ const property = buildProperty(el, tag);
692
+ if (tag === "audio" && el.width != null) {
693
+ delete property.width;
694
+ if (el.width === "fill" || el.width === "default") {
695
+ property.widthValue = { type: "builtin_width", value: el.width };
696
+ } else if (typeof el.width === "string") {
697
+ const px = pxValue(el.width);
698
+ if (px != null) property.widthValue = { type: "pixels", value: px };
699
+ }
700
+ }
701
+ if (tag === "video") {
702
+ const cover = el.image_key ?? el.cover_image_key ?? el.coverImageID;
703
+ if (typeof cover === "string" && cover) property.coverImageID = cover;
704
+ delete property.image_key;
705
+ delete property.cover_image_key;
706
+ }
707
+ return { tag, property };
708
+ }
709
+ return { tag, property: buildProperty(el, tag) };
710
+ }
711
+ function convertTableProperty(el) {
712
+ const property = buildProperty(el, "table");
713
+ const columns = Array.isArray(property.columns) ? property.columns : [];
714
+ const mdColNames = /* @__PURE__ */ new Set();
715
+ for (const col of columns) {
716
+ if (!isRecord(col)) continue;
717
+ const dt = col.data_type ?? col.dataType;
718
+ if (dt === "lark_md" || dt === "markdown") {
719
+ const name = typeof col.name === "string" ? col.name : "";
720
+ if (name) mdColNames.add(name);
721
+ }
722
+ }
723
+ if (!mdColNames.size || !Array.isArray(property.rows)) return property;
724
+ property.rows = property.rows.map((row) => {
725
+ if (!isRecord(row)) return row;
726
+ const out = { ...row };
727
+ for (const name of mdColNames) {
728
+ const cell = out[name];
729
+ if (typeof cell === "string") {
730
+ out[name] = {
731
+ data: {
732
+ tag: "markdown",
733
+ property: { elements: parseMarkdown(cell), originTag: "lark_md" }
734
+ }
735
+ };
736
+ }
737
+ }
738
+ return out;
739
+ });
740
+ return property;
741
+ }
742
+ function buildProperty(el, tag) {
743
+ const property = {};
744
+ for (const [k, v] of Object.entries(el)) {
745
+ if (k === "tag" || k === "id") continue;
746
+ if (tag === "column" && k === "width" && typeof v === "string") {
747
+ const px = pxValue(v);
748
+ if (px != null) {
749
+ property.widthValue = { type: "pixels", value: px };
750
+ continue;
751
+ }
752
+ }
753
+ if (TEXT_LEAF_KEYS.has(k)) {
754
+ property[renameKey(k)] = wrapTextLeaf(v);
755
+ continue;
756
+ }
757
+ if (k === "text") {
758
+ if (tag === "checker" && isRecord(v) && (v.tag === "markdown" || v.tag === "lark_md")) {
759
+ property.text = convertEl(v);
760
+ } else {
761
+ property.text = wrapTextLeaf(v);
762
+ }
763
+ continue;
764
+ }
765
+ if ((k === "icon" || k === "ud_icon") && isRecord(v)) {
766
+ property[k === "ud_icon" ? "udIcon" : "icon"] = convertEl(v);
767
+ continue;
768
+ }
769
+ if (k === "behaviors" && Array.isArray(v)) {
770
+ property.actions = behaviorsToActions(v);
771
+ continue;
772
+ }
773
+ if (k === "multi_url") {
774
+ property.url = normalizeUrl(v);
775
+ continue;
776
+ }
777
+ if (k === "button_area" && isRecord(v)) {
778
+ const area = { ...v };
779
+ if (Array.isArray(v.buttons)) area.buttons = v.buttons.map(convertNode);
780
+ property.button_area = area;
781
+ continue;
782
+ }
783
+ property[renameKey(k)] = coerceValue(k, v);
784
+ }
785
+ return property;
786
+ }
787
+ function coerceValue(key, value) {
788
+ if ((key === "padding" || key === "margin") && typeof value === "string") {
789
+ return parseBox(value);
790
+ }
791
+ if (Array.isArray(value)) return value.map(convertNode);
792
+ if (key === "url") return normalizeUrl(value);
793
+ return value;
794
+ }
795
+ function renameKey(k) {
796
+ return KEY_RENAME[k] || k;
797
+ }
798
+ function wrapTextLeaf(v) {
799
+ if (typeof v === "string") return { tag: "plain_text", property: { content: v } };
800
+ if (isRecord(v)) {
801
+ const property = { content: typeof v.content === "string" ? v.content : "" };
802
+ if (typeof v.text_align === "string") property.textAlign = v.text_align;
803
+ if (typeof v.text_size === "string") property.text_size = v.text_size;
804
+ if (typeof v.text_color === "string") property.text_color = v.text_color;
805
+ return { tag: typeof v.tag === "string" ? v.tag : "plain_text", property };
806
+ }
807
+ return { tag: "plain_text", property: { content: "" } };
808
+ }
809
+ function behaviorsToActions(behaviors) {
810
+ const out = [];
811
+ for (const b of behaviors) {
812
+ if (!isRecord(b)) continue;
813
+ const type = typeof b.type === "string" ? b.type : "open_url";
814
+ out.push({ type, action: { url: normalizeUrl(b.default_url) } });
815
+ }
816
+ return out;
817
+ }
818
+ function normalizeUrl(v) {
819
+ if (typeof v === "string") return v;
820
+ if (isRecord(v)) return v;
821
+ return v;
822
+ }
823
+ function parseBox(s) {
824
+ const nums = s.trim().split(/\s+/).map((t2) => pxValue(t2) ?? 0);
825
+ let t = 0, r = 0, b = 0, l = 0;
826
+ if (nums.length === 1) {
827
+ t = r = b = l = nums[0];
828
+ } else if (nums.length === 2) {
829
+ t = b = nums[0];
830
+ r = l = nums[1];
831
+ } else if (nums.length === 3) {
832
+ t = nums[0];
833
+ r = l = nums[1];
834
+ b = nums[2];
835
+ } else if (nums.length >= 4) {
836
+ [t, r, b, l] = nums;
837
+ }
838
+ const px = (value) => ({ type: "pixels", value });
839
+ return { top: px(t), right: px(r), bottom: px(b), left: px(l) };
840
+ }
841
+ function pxValue(s) {
842
+ const m = /^(-?\d+(?:\.\d+)?)px?$/.exec(s.trim());
843
+ if (m) return Number(m[1]);
844
+ const n = Number(s);
845
+ return Number.isFinite(n) ? n : null;
846
+ }
847
+ function isRecord(v) {
848
+ return typeof v === "object" && v !== null && !Array.isArray(v);
849
+ }
850
+
851
+ // src/raw-to-dsl.ts
852
+ var CAMEL_RENAME = {
853
+ textAlign: "text_align",
854
+ textStyle: "text_style",
855
+ backgroundStyle: "background_style",
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 };
940
+ }
941
+ function rawToDsl(raw) {
942
+ let card = {};
943
+ if (isRecord2(raw)) {
944
+ const env = isRecord2(raw.json_card) ? raw.json_card : isRecord2(raw.card) ? raw.card : raw;
945
+ if (isRecord2(env)) card = env;
946
+ }
947
+ const out = {
948
+ schema: typeof card.schema === "string" ? card.schema : "2.0"
949
+ };
950
+ if (isRecord2(card.config)) out.config = card.config;
951
+ if (isRecord2(card.header)) out.header = convertHeaderToDsl(card.header);
952
+ if (isRecord2(card.body)) out.body = convertBodyToDsl(card.body);
953
+ else if (Array.isArray(card.elements)) {
954
+ out.body = { elements: card.elements.map(convertElToDsl).filter(Boolean) };
955
+ }
956
+ return out;
957
+ }
958
+ function convertHeaderToDsl(header) {
959
+ const property = isRecord2(header.property) ? header.property : header;
960
+ const out = {};
961
+ for (const [k, v] of Object.entries(property)) {
962
+ if (k === "ud_icon" || k === "udIcon") {
963
+ if (isRecord2(v) && typeof v.tag === "string") {
964
+ out.ud_icon = convertElToDsl(v);
965
+ continue;
966
+ }
967
+ }
968
+ out[CAMEL_RENAME[k] ?? k] = unwrapTextLeaf(v);
969
+ }
970
+ return out;
971
+ }
972
+ function convertBodyToDsl(body) {
973
+ const property = isRecord2(body.property) ? body.property : body;
974
+ const out = {};
975
+ for (const [k, v] of Object.entries(property)) {
976
+ if (k === "elements" && Array.isArray(v)) {
977
+ out.elements = v.map(convertElToDsl).filter(Boolean);
978
+ continue;
979
+ }
980
+ if (isRecord2(v) && typeof v.tag === "string") {
981
+ out[CAMEL_RENAME[k] ?? k] = convertElToDsl(v);
982
+ continue;
983
+ }
984
+ if (Array.isArray(v)) {
985
+ out[CAMEL_RENAME[k] ?? k] = v.map((item) => isRecord2(item) && typeof item.tag === "string" ? convertElToDsl(item) : item);
986
+ continue;
987
+ }
988
+ out[CAMEL_RENAME[k] ?? k] = unwrapBox(v);
989
+ }
990
+ return out;
991
+ }
992
+ function convertElToDsl(el) {
993
+ if (!isRecord2(el)) return void 0;
994
+ const tag = typeof el.tag === "string" ? el.tag : "";
995
+ if (!tag) return void 0;
996
+ if (!KNOWN_TAGS.has(tag)) return degradeUnknown(el);
997
+ if ("fallback" in el && isRecord2(el.fallback)) return el;
998
+ const property = isRecord2(el.property) ? el.property : {};
999
+ const out = {};
1000
+ if (tag !== "plain_text") out.tag = tag === "img" ? "image" : tag;
1001
+ for (const [k, v] of Object.entries(property)) {
1002
+ const key = CAMEL_RENAME[k] ?? k;
1003
+ if (k === "elements" && Array.isArray(v)) {
1004
+ if (tag === "markdown") {
1005
+ out.content = elementsToMarkdown(v);
1006
+ continue;
1007
+ }
1008
+ out.elements = v.map(convertElToDsl).filter(Boolean);
1009
+ continue;
1010
+ }
1011
+ if (k === "columns" && Array.isArray(v)) {
1012
+ out.columns = v.map(convertElToDsl).filter(Boolean);
1013
+ continue;
1014
+ }
1015
+ if (isRecord2(v) && typeof v.tag === "string") {
1016
+ out[key] = convertElToDsl(v);
1017
+ continue;
1018
+ }
1019
+ if (Array.isArray(v)) {
1020
+ out[key] = v.map((item) => isRecord2(item) && typeof item.tag === "string" ? convertElToDsl(item) : item);
1021
+ continue;
1022
+ }
1023
+ out[key] = unwrapBox(unwrapTextLeaf(v));
1024
+ }
1025
+ return out;
1026
+ }
1027
+ function unwrapTextLeaf(v) {
1028
+ if (isRecord2(v) && v.tag === "plain_text") {
1029
+ const p = isRecord2(v.property) ? v.property : {};
1030
+ return typeof p.content === "string" ? p.content : "";
1031
+ }
1032
+ return v;
1033
+ }
1034
+ function unwrapBox(v) {
1035
+ if (!isRecord2(v) || !isRecord2(v.top) || !isRecord2(v.right)) return v;
1036
+ const side = (s) => isRecord2(s) && typeof s.value === "number" ? `${s.value}px` : "0px";
1037
+ return `${side(v.top)} ${side(v.right)} ${side(v.bottom ?? v.top)} ${side(v.left ?? v.right)}`;
1038
+ }
1039
+ function elementsToMarkdown(els) {
1040
+ const parts = [];
1041
+ for (const el of els) {
1042
+ const p = isRecord2(el?.property) ? el.property : {};
1043
+ if (typeof p.content === "string") parts.push(p.content);
1044
+ else if (Array.isArray(p.elements)) parts.push(elementsToMarkdown(p.elements));
1045
+ }
1046
+ return parts.join("");
1047
+ }
1048
+
1049
+ exports.degradeUnknown = degradeUnknown;
1050
+ exports.dslToRaw = dslToRaw;
1051
+ exports.rawToDsl = rawToDsl;