Repository navigation
Expand file tree
/
Copy pathlanhu_mcp_server.py
More file actions
8364 lines (7223 loc) · 369 KB
/
Copy pathlanhu_mcp_server.py
File metadata and controls
8364 lines (7223 loc) · 369 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python3
"""
蓝湖Axure文档提取MCP服务器
使用FastMCP实现
"""
import asyncio
import os
import sys
import re
import base64
import json
import hashlib
from pathlib import Path
from datetime import datetime, timezone, timedelta
from typing import Annotated, Optional, Union, List, Any
# BEGIN EMBEDDED LANHU_DESIGN (generated; run scripts/embed_lanhu_design.py)
_EMBEDDED_LANHU_DESIGN_SHA256 = "b7d785234c960800a197979cc98c0c523e06e0f272226e9c7c64354dcc412168"
_EMBEDDED_LANHU_DESIGN_ZIP = """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"""
def _enable_embedded_lanhu_design() -> None:
import importlib.util
import tempfile
force = os.getenv("LANHU_FORCE_EMBEDDED_DESIGN") == "1"
if not force and importlib.util.find_spec("lanhu_design") is not None:
return
payload = base64.b64decode(_EMBEDDED_LANHU_DESIGN_ZIP)
if hashlib.sha256(payload).hexdigest() != _EMBEDDED_LANHU_DESIGN_SHA256:
raise RuntimeError("Embedded lanhu_design payload failed integrity verification")
user_key = str(getattr(os, "getuid", lambda: Path.home())())
cache_dir = Path(tempfile.gettempdir()) / f"lanhu-mcp-embedded-{hashlib.sha256(user_key.encode()).hexdigest()[:12]}"
cache_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
archive = cache_dir / f"lanhu-design-{_EMBEDDED_LANHU_DESIGN_SHA256[:16]}.zip"
if not archive.is_file() or hashlib.sha256(archive.read_bytes()).hexdigest() != _EMBEDDED_LANHU_DESIGN_SHA256:
with tempfile.NamedTemporaryFile(dir=cache_dir, prefix=".lanhu-design-", delete=False) as handle:
handle.write(payload)
temporary = Path(handle.name)
try:
temporary.chmod(0o600)
temporary.replace(archive)
finally:
temporary.unlink(missing_ok=True)
sys.path.insert(0, str(archive))
_enable_embedded_lanhu_design()
# END EMBEDDED LANHU_DESIGN
# 显式配置文件优先,其次源码旁 .env,最后启动目录 .env;不向上搜索。
# override=False 保留进程环境变量(包括 Docker Compose env_file)的优先级。
configured_env_file = os.getenv("LANHU_ENV_FILE")
if configured_env_file is not None:
env_path = Path(configured_env_file).expanduser()
if not env_path.is_file():
raise FileNotFoundError(f"LANHU_ENV_FILE is not a file: {env_path}")
else:
env_path = Path(__file__).resolve().parent / ".env"
if not env_path.is_file():
env_path = Path.cwd() / ".env"
try:
from dotenv import load_dotenv
if env_path.is_file():
load_dotenv(env_path, override=False)
except ImportError:
# 如果 python-dotenv 未安装,跳过加载(使用系统环境变量)
pass
# 东八区时区(北京时间)
CHINA_TZ = timezone(timedelta(hours=8))
from urllib.parse import urlparse
from email.utils import parsedate_to_datetime
# 元数据缓存配置(基于版本号的永久缓存)
_metadata_cache = {} # {cache_key: {'data': {...}, 'version_id': str}}
def _format_lanhu_rfc2822(value: Optional[str]) -> Optional[str]:
"""把蓝湖 /api/project/product_documents 等端点返回的 RFC 2822 时间
(如 'Fri, 09 Jan 2026 10:07:29 GMT') 转成 '%Y-%m-%d %H:%M:%S' 中国时区字符串。
与 _fetch_metadata_from_url 中的 ISO8601 处理风格保持一致。
失败时原样返回,None 返回 None。
"""
if not value:
return None
try:
dt = parsedate_to_datetime(value)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(CHINA_TZ).strftime('%Y-%m-%d %H:%M:%S')
except Exception:
return value
import httpx
from fastmcp import Context
from bs4 import BeautifulSoup
from fastmcp import FastMCP
from fastmcp.utilities.types import Image
from mcp.types import TextContent
from playwright.async_api import async_playwright
# 创建FastMCP服务器
mcp = FastMCP("Lanhu Axure Extractor")
# 全局配置
DEFAULT_COOKIE = "your_lanhu_cookie_here" # 请替换为你的蓝湖Cookie,从浏览器开发者工具中获取
# 从环境变量读取Cookie,如果没有则使用默认值
COOKIE = os.getenv("LANHU_COOKIE", DEFAULT_COOKIE)
BASE_URL = "https://lanhuapp.com"
DDS_BASE_URL = "https://dds.lanhuapp.com"
CDN_URL = "https://axure-file.lanhuapp.com"
DDS_COOKIE = os.getenv("DDS_COOKIE", COOKIE)
# 飞书机器人Webhook配置(支持环境变量)
DEFAULT_FEISHU_WEBHOOK = "https://open.feishu.cn/open-apis/bot/v2/hook/your-webhook-key-here"
FEISHU_WEBHOOK_URL = os.getenv("FEISHU_WEBHOOK_URL", DEFAULT_FEISHU_WEBHOOK)
# 数据存储目录。相对路径固定相对于 .env(或源码目录),不随 MCP 客户端的
# 工作目录变化;否则同一服务从不同项目启动时会写入不同缓存,表现为缓存永不命中。
data_dir_value = Path(os.getenv("DATA_DIR", "./data")).expanduser()
if data_dir_value.is_absolute():
DATA_DIR = data_dir_value
else:
data_dir_base = env_path.parent if env_path.is_file() else Path(__file__).resolve().parent
DATA_DIR = (data_dir_base / data_dir_value).resolve()
DATA_DIR.mkdir(parents=True, exist_ok=True)
# HTTP 请求超时时间(秒)
HTTP_TIMEOUT = float(os.getenv("HTTP_TIMEOUT", "30"))
# 浏览器视口尺寸(影响页面初始渲染,不影响全页截图)
# 注意:截图使用 full_page=True,会自动截取完整页面,不受此限制
VIEWPORT_WIDTH = int(os.getenv("VIEWPORT_WIDTH", "1920"))
VIEWPORT_HEIGHT = int(os.getenv("VIEWPORT_HEIGHT", "1080"))
# Axure visual evidence keeps the established full screenshot and adds readable
# horizontal strips. Requirement tables keep their complete left/right context;
# only the vertical axis is split, with overlap across adjacent strips.
AXURE_SCREENSHOT_CACHE_SCHEMA = 4
AXURE_TILE_WIDTH = int(os.getenv("AXURE_TILE_WIDTH", "1600")) # legacy setting; strips keep full width
AXURE_TILE_HEIGHT = int(os.getenv("AXURE_TILE_HEIGHT", "960"))
AXURE_TILE_OVERLAP = int(os.getenv("AXURE_TILE_OVERLAP", "96"))
AXURE_TILE_TRIGGER_AREA = int(os.getenv("AXURE_TILE_TRIGGER_AREA", "3200000"))
AXURE_TILE_TRIGGER_LONG_EDGE = int(os.getenv("AXURE_TILE_TRIGGER_LONG_EDGE", "2400"))
AXURE_DEFAULT_TILE_LIMIT = int(os.getenv("AXURE_DEFAULT_TILE_LIMIT", "4"))
AXURE_MAX_TILE_LIMIT = int(os.getenv("AXURE_MAX_TILE_LIMIT", "12"))
AXURE_FOCUS_LEVELS = {
"tight": (960, 640),
"context": (1600, 1100),
"section": (VIEWPORT_WIDTH, 1800),
}
AXURE_FOCUS_PREVIEW_SIZE = (720, 420)
AXURE_FOCUS_MAX_CANDIDATES = 8
# 调试模式
DEBUG = os.getenv("DEBUG", "false").lower() == "true"
# 角色枚举(用于识别用户身份)
VALID_ROLES = ["后端", "前端", "客户端", "开发", "运维", "产品", "项目经理"]
# ⚠️ @提醒只允许具体人名,禁止使用角色
# 示例人名列表,请根据你的团队成员修改
MENTION_ROLES = [
"张三", "李四", "王五", "赵六", "钱七", "孙八",
"周九", "吴十", "郑十一", "冯十二", "陈十三", "褚十四",
"卫十五", "蒋十六", "沈十七", "韩十八", "杨十九", "朱二十"
]
# 飞书用户ID映射
# 示例映射,请替换为你团队成员的实际飞书用户ID
# 飞书用户ID可以通过飞书开放平台获取
FEISHU_USER_ID_MAP = {
'张三': '0000000000000000001',
'李四': '0000000000000000002',
'王五': '0000000000000000003',
'赵六': '0000000000000000004',
'钱七': '0000000000000000005',
'孙八': '0000000000000000006',
'周九': '0000000000000000007',
'吴十': '0000000000000000008',
'郑十一': '0000000000000000009',
'冯十二': '0000000000000000010',
'陈十三': '0000000000000000011',
'褚十四': '0000000000000000012',
'卫十五': '0000000000000000013',
'蒋十六': '0000000000000000014',
'沈十七': '0000000000000000015',
'韩十八': '0000000000000000016',
'杨十九': '0000000000000000017',
'朱二十': '0000000000000000018',
}
# 角色映射规则(按优先级排序,越具体的越靠前)
ROLE_MAPPING_RULES = [
# 后端相关
(["后端", "backend", "服务端", "server", "java", "php", "python", "go", "golang", "node", "nodejs", ".net", "c#"], "后端"),
# 前端相关
(["前端", "frontend", "h5", "web", "vue", "react", "angular", "javascript", "js", "ts", "typescript", "css"], "前端"),
# 客户端相关(优先于"开发")
(["客户端", "client", "ios", "android", "安卓", "移动端", "mobile", "app", "flutter", "rn", "react native", "swift", "kotlin", "objective-c", "oc"], "客户端"),
# 运维相关
(["运维", "ops", "devops", "sre", "dba", "运营维护", "系统管理", "infra", "infrastructure"], "运维"),
# 产品相关
(["产品", "product", "pm", "产品经理", "需求"], "产品"),
# 项目经理相关
(["项目经理", "项目", "pmo", "project manager", "scrum", "敏捷"], "项目经理"),
# 开发(通用,优先级最低)
(["开发", "dev", "developer", "程序员", "coder", "engineer", "工程师"], "开发"),
]
# ==================== 设计图JSON转HTML转换器 ====================
_UNITLESS_PROPERTIES = {'zIndex', 'fontWeight', 'opacity', 'flex', 'flexGrow', 'flexShrink', 'order'}
COMMON_CSS_FOR_DESIGN = """
body * {
box-sizing: border-box;
flex-shrink: 0;
}
body {
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
Arial, PingFang SC-Light, Microsoft YaHei;
}
input {
background-color: transparent;
border: 0;
}
button {
margin: 0;
padding: 0;
border: 1px solid transparent;
outline: none;
background-color: transparent;
}
button:active {
opacity: 0.6;
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-center {
display: flex;
justify-content: center;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
.justify-evenly {
display: flex;
justify-content: space-evenly;
}
.justify-around {
display: flex;
justify-content: space-around;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.align-start {
display: flex;
align-items: flex-start;
}
.align-center {
display: flex;
align-items: center;
}
.align-end {
display: flex;
align-items: flex-end;
}
"""
def _camel_to_kebab(s: str) -> str:
"""驼峰命名转换为CSS短横线命名"""
return re.sub(r'([A-Z])', lambda m: f'-{m.group(1).lower()}', s)
def _format_css_value(key: str, value) -> str:
"""格式化CSS值,自动添加px单位"""
if value is None:
return ''
if isinstance(value, (int, float)):
if value == 0:
return '0'
return str(value) if key in _UNITLESS_PROPERTIES else f'{value}px'
if isinstance(value, str):
# 处理rgba格式
if 'rgba(' in value:
def replace_rgba(match):
r, g, b, a = match.groups()
alpha = float(a) if '.' in a else int(a)
return f'rgba({r}, {g}, {b}, {alpha})'
return re.sub(r'rgba\(([\d.]+),\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)\)', replace_rgba, value)
# 检查字符串形式的数字(fontSize可能是"14"或"14px")
if re.match(r'^\d+$', value) and key not in _UNITLESS_PROPERTIES:
return '0' if value == '0' else f'{value}px'
return str(value)
def _merge_padding(styles: dict) -> None:
"""合并padding四边属性"""
pt = styles.get('paddingTop')
pr = styles.get('paddingRight')
pb = styles.get('paddingBottom')
pl = styles.get('paddingLeft')
if pt is not None and pr is not None and pb is not None and pl is not None:
pt_val = pt or 0
pr_val = pr or 0
pb_val = pb or 0
pl_val = pl or 0
if pt_val == pb_val and pl_val == pr_val:
if pt_val == pl_val:
styles['padding'] = f'{pt_val}px'
else:
styles['padding'] = f'{pt_val}px {pr_val}px'
else:
styles['padding'] = f'{pt_val}px {pr_val}px {pb_val}px {pl_val}px'
for k in ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft']:
styles.pop(k, None)
def _merge_margin(styles: dict) -> None:
"""合并margin四边属性"""
mt = styles.get('marginTop')
mr = styles.get('marginRight')
mb = styles.get('marginBottom')
ml = styles.get('marginLeft')
if mt is not None or mr is not None or mb is not None or ml is not None:
mt_val = mt or 0
mr_val = mr or 0
mb_val = mb or 0
ml_val = ml or 0
if mt_val == 0 and mr_val == 0 and mb_val == 0 and ml_val == 0:
pass # 全是0,不输出
elif mt_val == mb_val and ml_val == mr_val:
if mt_val == ml_val:
styles['margin'] = f'{mt_val}px'
else:
styles['margin'] = f'{mt_val}px {mr_val}px'
else:
styles['margin'] = f'{mt_val}px {mr_val}px {mb_val}px {ml_val}px'
for k in ['marginTop', 'marginRight', 'marginBottom', 'marginLeft']:
styles.pop(k, None)
def _should_use_flex(node: dict) -> bool:
"""判断节点是否使用flex布局"""
if not node:
return False
node_style = node.get('style', {})
node_props = node.get('props', {})
node_props_style = node_props.get('style', {})
style = {**node_style, **node_props_style}
return style.get('display') == 'flex' or style.get('flexDirection') is not None
def _get_flex_classes(node: dict) -> list:
"""获取flex相关的CSS类名列表"""
classes = []
if not _should_use_flex(node):
return classes
node_style = node.get('style', {})
node_props = node.get('props', {})
node_props_style = node_props.get('style', {})
style = {**node_style, **node_props_style}
class_name = node_props.get('className', '')
# Flex方向
flex_direction = style.get('flexDirection')
if flex_direction == 'column' or 'flex-col' in class_name:
classes.append('flex-col')
elif flex_direction == 'row' or 'flex-row' in class_name:
classes.append('flex-row')
# 主轴对齐
justify = node.get('alignJustify', {}).get('justifyContent') or style.get('justifyContent')
if justify == 'space-between':
classes.append('justify-between')
elif justify == 'center':
classes.append('justify-center')
elif justify == 'flex-end':
classes.append('justify-end')
elif justify == 'flex-start':
classes.append('justify-start')
elif justify == 'space-around':
classes.append('justify-around')
elif justify == 'space-evenly':
classes.append('justify-evenly')
# 交叉轴对齐
align = node.get('alignJustify', {}).get('alignItems') or style.get('alignItems')
if align == 'flex-start':
classes.append('align-start')
elif align == 'center':
classes.append('align-center')
elif align == 'flex-end':
classes.append('align-end')
return classes
def _clean_styles(node: dict, flex_classes: list) -> dict:
"""清理样式,移除被flex类覆盖的标准值"""
node_props = node.get('props', {})
props_style = node_props.get('style', {})
styles = {}
# 定义被flex类完全覆盖的标准值
standard_justify = {'flex-start', 'center', 'flex-end', 'space-between', 'space-around', 'space-evenly'}
standard_align = {'flex-start', 'center', 'flex-end'}
for key, value in props_style.items():
# 跳过display和flexDirection(由flex-col/flex-row类完全覆盖)
if key in ('display', 'flexDirection'):
if flex_classes:
continue
# justifyContent: 只跳过标准值
if key == 'justifyContent' and flex_classes:
if value in standard_justify:
continue
# alignItems: 只跳过标准值
if key == 'alignItems' and flex_classes:
if value in standard_align:
continue
# 跳过static定位
if key == 'position' and value == 'static':
continue
# 跳过visible溢出
if key == 'overflow' and value == 'visible':
continue
styles[key] = value
# 合并padding和margin
if any(k in styles for k in ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft']):
_merge_padding(styles)
if any(k in styles for k in ['marginTop', 'marginRight', 'marginBottom', 'marginLeft']):
_merge_margin(styles)
return styles
def _get_loop_arr(node: dict) -> list:
"""取节点的 loop 数据:优先 loop,其次 loopData。"""
if not node:
return []
arr = node.get('loop') or node.get('loopData')
return arr if isinstance(arr, list) else []
def _generate_css(node: dict, css_rules: dict, loop_suffixes: list | None = None) -> None:
"""递归生成CSS规则。loop_suffixes 非空时,当前子树为循环模板,类名按 -0/-1/... 展开。"""
if not node:
return
loop_arr = _get_loop_arr(node) if node.get('loopType') else []
if loop_arr and not loop_suffixes:
loop_suffixes = [str(i) for i in range(len(loop_arr))]
node_props = node.get('props', {})
class_name = node_props.get('className')
if class_name:
flex_classes = _get_flex_classes(node)
styles = _clean_styles(node, flex_classes)
style_entries = list(styles.items())
if style_entries or node.get('type') == 'lanhutext':
css_props = []
for key, value in style_entries:
css_key = _camel_to_kebab(key)
css_value = _format_css_value(key, value)
if css_value:
css_props.append(f' {css_key}: {css_value};')
content = '\n'.join(css_props) if css_props else ''
else:
content = ''
if loop_suffixes:
for suf in loop_suffixes:
css_rules[f'{class_name}-{suf}'] = content
else:
css_rules[class_name] = content
children = node.get('children', [])
for child in children:
_generate_css(child, css_rules, loop_suffixes)
def _resolve_loop_placeholder(value: str, loop_item: dict) -> str:
"""this.item.xxx -> loop_item.get('xxx', '')"""
if not value or not isinstance(loop_item, dict):
return value or ''
s = str(value).strip()
m = re.match(r'^this\.item\.(\w+)$', s)
return loop_item.get(m.group(1), '') if m else value
def _generate_html(
node: dict,
indent: int = 2,
loop_context: tuple[list, int] | None = None,
) -> str:
"""递归生成HTML结构。loop_context=(loop_list, index) 时当前为循环项,类名加 -index,占位符用 loop 数据替换。"""
if not node:
return ''
loop_item = loop_context[0][loop_context[1]] if loop_context else None
loop_index = loop_context[1] if loop_context else None
spaces = ' ' * indent
flex_classes = _get_flex_classes(node)
node_props = node.get('props', {})
class_name = node_props.get('className', '')
if loop_index is not None and class_name:
class_name = f'{class_name}-{loop_index}'
all_classes = ' '.join([c for c in [class_name] + flex_classes if c])
node_type = node.get('type')
if node_type == 'lanhutext':
text = node.get('data', {}).get('value') or node_props.get('text') or ''
if loop_item is not None and text and re.match(r'^this\.item\.\w+$', str(text).strip()):
text = _resolve_loop_placeholder(text, loop_item)
elif text and re.match(r'^this\.item\.\w+$', str(text).strip()):
text = ''
return f'{spaces}<span class="{all_classes}">{text}</span>'