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