This repository was archived by the owner on Mar 26, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJ2MM.js
More file actions
1908 lines (1664 loc) · 91.6 KB
/
Copy pathJ2MM.js
File metadata and controls
1908 lines (1664 loc) · 91.6 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
(() => {
'use strict';
// ----------------------------
// 状态
// ----------------------------
let originalJson = null;
let editedBlocks = null;
let customModules = [];
let blockIdCounter = 0;
// 默认模块配置(内容可写 BBCode / Markdown,输出时自动转)
const defaultModulesConfig = [
{
id: 'module_sign',
title: '工具署名',
content: '[size=2][b]【本文Ai翻译及Ai排版借助了: [url=https://jiubook.github.io/MinecraftJJTools/J2MM_JsonToMcbbsMarkdown.html]J2MM[/url]、[url=https://github.com/jiubook/MinecraftJJTools]JBAiGNN[/url]、[url=https://chatgpt.com/]ChatGPT[/url]、[url=https://claude.com/]Claude[/url]等工具 】[/b][/size]',
position: 'start',
enabled: true
},
{
id: 'module_java_snapshot_header',
title: 'Java版 每周快照 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=48,48]https://www.mcbbs.co/data/attachment/common/ea/common_64_icon.png[/img][/float][size=32px][b][color=#645944]每周快照[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]每周快照[/b]是 Minecraft Java 版的测试机制,用于新特性的展示和反馈收集。\n[*][color=#8E2609]快照有可能导致存档损坏,因此请注意备份,不要直接在你的主存档游玩快照。[/color]\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[*]部分新特性译名仅供参考,不代表最终结果。\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_java_prerelease_header',
title: 'Java版 预发布版 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=48,48]https://www.mcbbs.co/data/attachment/common/ea/common_64_icon.png[/img][/float][size=32px][b][color=#645944]预发布版[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]预发布版[/b]是 Minecraft Java 版的测试机制,主要是为了收集漏洞反馈,为正式发布做好准备。\n[*][color=#8E2609]预发布版有可能导致存档损坏,因此请注意备份,不要直接在你的主存档游玩预发布版。[/color]\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[*]部分新特性译名仅供参考,不代表最终结果。\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_java_rc_header',
title: 'Java版 候选版本 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=48,48]https://www.mcbbs.co/data/attachment/common/ea/common_64_icon.png[/img][/float][size=32px][b][color=#645944]候选版本[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]候选版本[/b]是 Minecraft Java 版的测试机制。如果没有重大漏洞,该版本将会被用于正式发布。\n[*][color=#8E2609]候选版本有可能导致存档损坏,因此请注意备份,不要直接在你的主存档游玩候选版本。[/color]\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[*]部分新特性译名仅供参考,不代表最终结果。\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_java_release_header',
title: 'Java 正式版 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=46,48]https://ooo.0o0.ooo/2017/01/30/588f60bbaaf78.png[/img][/float][size=32px][b][color=#645944] Minecraft Java 版[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]Minecraft Java 版[/b]是指运行在 Windows、macOS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。\n[*][b]正式版[/b]包含所有特性且安全稳定,所有玩家都可以尽情畅享。\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_bedrock_beta_header',
title: '基岩版 测试版 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=48,48]https://www.mcbbs.co/data/attachment/common/ea/common_64_icon.png[/img][/float][size=32px][b][color=#645944]测试版[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]测试版[/b]是 Minecraft 基岩版的测试机制,主要用于下一个正式版的特性预览。\n[*][color=#8E2609]测试版有可能导致存档损坏,因此请注意备份,不要直接在你的主存档游玩测试版。[/color]\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[*]部分新特性译名仅供参考,不代表最终结果。\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_bedrock_release_header',
title: '基岩版 正式版 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=46,48]https://ooo.0o0.ooo/2017/01/30/588f60bbaaf78.png[/img][/float][size=32px][b][color=#645944]Minecraft 基岩版[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]Minecraft 基岩版[/b]是指运行在移动平台(Android、iOS)、Windows 10、主机(Xbox One、Switch、PlayStation 4)上,使用「基岩引擎」(C++语言)开发的 Minecraft 版本。\n[*][b]正式版[/b]包含所有特性且安全稳定,所有玩家都可以尽情畅享。\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_commentary_header',
title: '时评 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=48,48]https://www.mcbbs.co/data/attachment/common/03/common_63_icon.png[/img][/float][size=32px][b][color=#645944]时评[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][b]时评[/b]为玩家对官方消息的分析与探讨,不代表官方意见\n[*]请在交流时保持心平气和\n[*]转载本帖时须要注明原作者以及本帖地址。[size=0px]本帖来自www.mcbbs.co[/size]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_normal_header',
title: '普通资讯/博文 - 开头',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/15/180957i6j4oo2mrn8s6btt.webp[/img][/float][size=24px][b][color=#645944] 转载须知[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]转载本帖时须要注明原作者以及本帖地址。\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'start',
enabled: false
},
{
id: 'module_java_snapshot_footer',
title: 'Java版 每周快照 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://www.minecraft.net/zh-hans/download/server][color=Sienna]官方服务端 jar 下载地址[/color][/url]\n[*][url=https://www.minecraft.net/zh-hans/download/][color=Sienna]正版启动器下载地址[/color][/url]\n[*][url=https://bugs.mojang.com/projects/MC/summary][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩快照?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]对于正版用户:请打开官方启动器,在「配置」选项卡中启用「快照」,选择「最新快照」即可。\n[*]对于非正版用户:请于[url=https://archives.mcbbs.co/read.php?tid=38297][color=Sienna]推荐启动器列表[/color][/url]寻找合适的启动器。目前绝大多数主流启动器都带有下载功能。如仍有疑惑请到[url=https://www.mcbbs.co/forum-59-1.html][color=Sienna]原版问答[/color][/url]板块提问。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_java_prerelease_footer',
title: 'Java版 预发布版 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://www.minecraft.net/zh-hans/download/server][color=Sienna]官方服务端 jar 下载地址[/color][/url]\n[*][url=https://www.minecraft.net/zh-hans/download/][color=Sienna]正版启动器下载地址[/color][/url]\n[*][url=https://bugs.mojang.com/projects/MC/summary][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩预发布版?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]对于正版用户:请打开官方启动器,在「配置」选项卡中启用「快照」,选择「最新快照」即可。\n[*]对于非正版用户:请于[url=https://archives.mcbbs.co/read.php?tid=38297][color=Sienna]推荐启动器列表[/color][/url]寻找合适的启动器。目前绝大多数主流启动器都带有下载功能。如仍有疑惑请到[url=https://www.mcbbs.co/forum-59-1.html][color=Sienna]原版问答[/color][/url]板块提问。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_java_rc_footer',
title: 'Java版 候选版本 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://www.minecraft.net/zh-hans/download/server][color=Sienna]官方服务端 jar 下载地址[/color][/url]\n[*][url=https://www.minecraft.net/zh-hans/download/][color=Sienna]正版启动器下载地址[/color][/url]\n[*][url=https://bugs.mojang.com/projects/MC/summary][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩候选版本?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]对于正版用户:请打开官方启动器,在「配置」选项卡中启用「快照」,选择「最新快照」即可。\n[*]对于非正版用户:请于[url=https://archives.mcbbs.co/read.php?tid=38297][color=Sienna]推荐启动器列表[/color][/url]寻找合适的启动器。目前绝大多数主流启动器都带有下载功能。如仍有疑惑请到[url=https://www.mcbbs.co/forum-59-1.html][color=Sienna]原版问答[/color][/url]板块提问。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_java_release_footer',
title: 'Java 正式版 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://www.minecraft.net/zh-hans/download/server][color=Sienna]官方服务端 jar 下载地址[/color][/url]\n[*][url=https://www.minecraft.net/zh-hans/download/][color=Sienna]正版启动器下载地址[/color][/url]\n[*][url=https://bugs.mojang.com/projects/MC/summary][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩正式版?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]对于正版用户:请打开官方启动器,选择「最新版本」即可。\n[*]对于非正版用户:请于[url=https://archives.mcbbs.co/read.php?tid=38297][color=Sienna]推荐启动器列表[/color][/url]寻找合适的启动器。目前绝大多数主流启动器都带有下载功能。如仍有疑惑请到[url=https://www.mcbbs.co/forum-59-1.html][color=Sienna]原版问答[/color][/url]板块提问。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_bedrock_beta_footer',
title: '基岩版 测试版 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://bugs.mojang.com/projects/MC/summaryPE][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩测试版?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]请访问[url=https://www.minecraft.net/zh-hans/get-minecraft][color=Sienna]官方游戏获取地址[/color][/url],根据您所使用的平台获取游戏。\n[*]基岩测试版/预览版仅限于 Windows 10、Android、iOS、Xbox One 平台。请根据[url=https://archives.mcbbs.co/thread-1299939-1-1.html][color=Sienna]官方指引[/color][/url]启用/关闭测试版/预览版。\n[*]在新建/编辑地图时,请滑动到「实验性游戏内容(Experiments)」,即可体验最新内容。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_bedrock_release_footer',
title: '基岩版 正式版 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 实用链接[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://bugs.mojang.com/projects/MC/summaryPE][color=Sienna]漏洞报告站点(仅限英文)[/color][/url]\n[*][url=https://feedback.minecraft.net/][color=Sienna]官方反馈网站(仅限英文)[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/forum/202603/16/015357jo333134doqo4yyo.webp[/img][/float][size=24px][b][color=#645944] 如何游玩测试版?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*]请访问[url=https://www.minecraft.net/zh-hans/get-minecraft][color=Sienna]官方游戏获取地址[/color][/url],根据您所使用的平台获取游戏。\n[*]在新建/编辑地图时,请滑动到「实验性游戏内容(Experiments)」,即可体验最新内容。\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_commentary_footer',
title: '时评 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/9f/common_56_icon.png[/img][/float][size=24px][b][color=#645944] 本文所涉及的官方消息或媒体评论[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=地址][color=Sienna]标题[/color][/url]\n[*](如有多项请自行添加)\n[/list][/size][/td][/tr]\n[/table][/font][/align]\n[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 幻翼块讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_normal_footer',
title: '普通资讯/博文 - 结尾',
content: '[align=center][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][table=85%]\n[tr=#E3C99E][td][float=left][img=32,32]https://www.mcbbs.co/data/attachment/common/6c/common_45_icon.png[/img][/float][size=24px][b][color=#645944] 想了解更多资讯?[/color][/b][/size][/td][/tr]\n[tr=#FDF6E5][td][size=16px][list]\n[*][url=https://archives.mcbbs.co/read.php?tid=874677][color=Sienna]外部来源以及详细的更新条目追踪[/color][/url]\n[*][url=https://www.mcbbs.co/forum-news-1.html][color=Sienna]我的世界中文论坛 - 新闻资讯板块[/color][/url]\n[/list][/size][/td][/tr]\n[/table][/font][/align]',
position: 'end',
enabled: false
},
{
id: 'module_Agreement',
title: '开源协议',
content: '[size=2][b]【本Ai工具以 [url=https://www.gnu.org/licenses/gpl-3.0.zh-cn.html]GPL-3.0[/url] 协议发布】\n【本Ai翻译作品以 [url=https://creativecommons.org/licenses/by-sa/4.0/deed.zh-hans]CC BY-SA 4.0[/url] 协议发布】[/b][/size]',
position: 'end',
enabled: true
}
];
// ----------------------------
// DOM
// ----------------------------
const $ = (id) => document.getElementById(id);
const uploadSection = $('uploadSection');
const fileInput = $('fileInput');
const selectFileBtn = $('selectFileBtn');
const demoBtn = $('demoBtn');
const translationSection = $('translationSection');
const translationPanel = $('translationPanel');
const outputSection = $('outputSection');
const regenerateBtn = $('regenerateBtn');
const resetEditsBtn = $('resetEditsBtn');
const defaultModulesStartContainer = $('defaultModulesStartContainer');
const defaultModulesEndContainer = $('defaultModulesEndContainer');
const customModulesContainer = $('customModulesContainer');
const addCustomModuleBtn = $('addCustomModuleBtn');
const saveCustomModulesBtn = $('saveCustomModulesBtn');
const loadCustomModulesBtn = $('loadCustomModulesBtn');
const bbcodeOutput = $('bbcodeOutput');
const markdownOutput = $('markdownOutput');
const previewArea = $('previewArea');
const statusText = $('statusText');
const copyMarkdownBtn = $('copyMarkdownBtn');
const copyBBCodeBtn = $('copyBBCodeBtn');
const refreshPreviewBtn = $('refreshPreviewBtn');
// ----------------------------
// 初始化
// ----------------------------
renderDefaultModules();
loadCustomModulesFromLocalStorage(true);
// ----------------------------
// 事件
// ----------------------------
selectFileBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', handleFileUpload);
demoBtn.addEventListener('click', loadDemoData);
regenerateBtn.addEventListener('click', generateOutput);
resetEditsBtn.addEventListener('click', resetEdits);
addCustomModuleBtn.addEventListener('click', addCustomModule);
saveCustomModulesBtn.addEventListener('click', saveCustomModulesToLocalStorage);
loadCustomModulesBtn.addEventListener('click', () => loadCustomModulesFromLocalStorage(false));
copyMarkdownBtn.addEventListener('click', () => copyToClipboard(markdownOutput.value, copyMarkdownBtn));
copyBBCodeBtn.addEventListener('click', () => copyToClipboard(bbcodeOutput.value, copyBBCodeBtn));
refreshPreviewBtn.addEventListener('click', () => {
previewArea.innerHTML = bbcodeToHtml(bbcodeOutput.value || '');
updateStatus('预览已刷新', false);
});
// 默认模块管理事件(使用事件委托)
defaultModulesStartContainer.addEventListener('change', handleDefaultModuleChange);
defaultModulesEndContainer.addEventListener('change', handleDefaultModuleChange);
function handleDefaultModuleChange(e) {
const el = e.target;
const id = el.dataset.mid;
const field = el.dataset.field;
if (!id || !field) return;
const mod = defaultModulesConfig.find(x => x.id === id);
if (!mod) return;
if (field === 'enabled') {
mod.enabled = !!el.checked;
// 双向联动勾选逻辑
let linkedModId = null;
// 如果是开头模块,找到对应的结尾模块
if (mod.position === 'start' && id.includes('_header')) {
linkedModId = id.replace('_header', '_footer');
}
// 如果是结尾模块,找到对应的开头模块
else if (mod.position === 'end' && id.includes('_footer')) {
linkedModId = id.replace('_footer', '_header');
}
// 如果找到了关联模块,同步状态
if (linkedModId) {
const linkedMod = defaultModulesConfig.find(x => x.id === linkedModId);
if (linkedMod) {
linkedMod.enabled = mod.enabled; // 同步勾选状态(勾选或取消)
renderDefaultModules(); // 重新渲染以更新UI
}
}
}
if (field === 'position') mod.position = String(el.value);
generateOutput();
}
// 实时预览:BBCode 输出框变更即刷新预览
const updatePreviewDebounced = debounce(() => {
try {
previewArea.innerHTML = bbcodeToHtml(bbcodeOutput.value || '');
} catch (err) {
previewArea.innerHTML = `<p style=”color: #e74c3c; padding: 20px;”>预览渲染错误: ${escapeHtml(err.message)}</p>`;
}
}, 120);
bbcodeOutput.addEventListener('input', updatePreviewDebounced);
// 拖拽上传
;['dragenter','dragover'].forEach(evt => {
uploadSection.addEventListener(evt, (e) => {
e.preventDefault(); e.stopPropagation();
uploadSection.classList.add('dragover');
});
});
;['dragleave','drop'].forEach(evt => {
uploadSection.addEventListener(evt, (e) => {
e.preventDefault(); e.stopPropagation();
uploadSection.classList.remove('dragover');
});
});
uploadSection.addEventListener('drop', (e) => {
const file = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
if (!file) return;
fileInput.files = e.dataTransfer.files;
handleFileUpload({ target: { files: [file] } });
});
// ----------------------------
// 文件处理
// ----------------------------
function handleFileUpload(event) {
const file = event?.target?.files?.[0];
if (!file) return;
const isJson = file.type === 'application/json' || file.name.toLowerCase().endsWith('.json');
if (!isJson) return updateStatus('错误:请选择 JSON 文件', true);
updateStatus(`正在处理: ${file.name}...`);
const reader = new FileReader();
reader.onload = (e) => {
try {
const parsed = JSON.parse(String(e.target.result || ''));
if (!parsed.blocks || !Array.isArray(parsed.blocks)) throw new Error('JSON 必须包含 blocks 数组');
// 补齐 id
parsed.blocks.forEach(b => { if (!b.id) b.id = `block_${blockIdCounter++}`; });
originalJson = parsed;
editedBlocks = deepClone(parsed.blocks);
renderTranslationPanel();
translationSection.style.display = 'block';
outputSection.style.display = 'flex';
generateOutput();
updateStatus('成功:文件已加载,请进行翻译校对并生成输出', false);
} catch (err) {
updateStatus(`错误:无效的 JSON 文件 - ${err.message}`, true);
}
};
reader.readAsText(file);
}
function loadDemoData() {
const demoJson = {
"title": "Another step towards Vibrant Visuals",
"translated_title": "迈向 Vibrant Visuals 的又一步",
"release_date": "2026-02-18T15:00:34Z",
"url": "https://www.minecraft.net/zh-hans/article/another-step-towards-vibrant-visuals-for-java-edition",
"author": "Staff",
"description": "We’re still hard at work getting Vibrant Visuals ready for Minecraft: Java Edition...",
"blocks": [
{"id":"b0001","type":"p","source_text":"We’re still hard at work getting Vibrant Visuals ready for Minecraft: Java Edition...","translated_text":"我们仍在努力为 Minecraft: Java 版准备 Vibrant Visuals..."},
{"id":"b0002","type":"blockquote","source_text":"This is a quote block.","translated_text":"这是一个引用块。"},
{"id":"b0003","type":"h2","source_text":"What are we changing?","translated_text":"我们要改变什么?"},
{"id":"b0004","type":"p","source_text":"Today, Minecraft: Java Edition uses a technology called OpenGL...","translated_text":"目前,Minecraft: Java 版使用一种名为 OpenGL 的技术..."},
{"id":"b0005","type":"ul","items":["Item 1","Item 2"],"translated_items":["项目 1","项目 2"]},
{"id":"b0006","type":"code","source_text":"System.out.println(\"Hello\");","translated_text":""},
{"id":"b0007","type":"h3","source_text":"Introducing: Vulkan","translated_text":"介绍:Vulkan"},
{"id":"b0008","type":"p","source_text":"Vulkan is a graphics API that has a 10-year history...","translated_text":"Vulkan 是一种已有十年市场历史的图形 API..."}
]
};
originalJson = demoJson;
editedBlocks = deepClone(demoJson.blocks);
renderTranslationPanel();
translationSection.style.display = 'block';
outputSection.style.display = 'flex';
generateOutput();
updateStatus('成功:已加载示例数据,可进行翻译校对', false);
}
// ----------------------------
// 翻译校对面板
// ----------------------------
/**
* 渲染翻译面板:将编辑后的块渲染为可交互的UI元素
* 功能:
* - 为每个块创建可展开/折叠的编辑区域
* - 支持列表类型(ul/ol)和普通文本类型的不同编辑界面
* - 提供保存、上移、下移、删除等操作按钮
* - 支持点击类型标签修改块类型
*/
function renderTranslationPanel() {
translationPanel.innerHTML = '';
if (!editedBlocks) return;
editedBlocks.forEach((block, index) => {
const div = document.createElement('div');
div.className = 'block-item';
div.dataset.blockId = block.id;
const typeLabel = String(block.type || 'p');
// 构建预览文本:中文在上,英文在下(双行模式)
let previewHtml = '';
if (typeLabel === 'img' || typeLabel === 'image') {
// 图片类型:显示缩略图和URL
const imgSrc = block.meta && block.meta.src ? String(block.meta.src).trim() : '';
const imgAlt = block.meta && block.meta.alt ? String(block.meta.alt).trim() : '';
if (imgSrc) {
previewHtml = `<div style="display: flex; align-items: center; gap: 10px;">
<img src="${escapeAttr(imgSrc)}" alt="${escapeAttr(imgAlt)}" style="max-width: 120px; max-height: 80px; object-fit: contain; border: 1px solid #ddd; border-radius: 4px;">
<div style="flex: 1; font-size: 0.85rem; color: #555; word-break: break-all;">${escapeHtml(imgSrc)}</div>
</div>`;
} else {
previewHtml = `<span style="color: #999; font-style: italic;">无图片URL</span>`;
}
} else if (typeLabel === 'ul' || typeLabel === 'ol') {
const translatedItems = Array.isArray(block.translated_items) ? block.translated_items : [];
const items = Array.isArray(block.items) ? block.items : [];
const translatedText = translatedItems.join(', ').slice(0, 80);
const sourceText = items.join(', ').slice(0, 80);
if (translatedText && sourceText && translatedText !== sourceText) {
previewHtml = `<span class="preview-translated">${escapeHtml(translatedText)}</span><span class="preview-source">${escapeHtml(sourceText)}</span>`;
} else if (translatedText) {
previewHtml = `<span class="preview-translated">${escapeHtml(translatedText)}</span>`;
} else if (sourceText) {
previewHtml = `<span class="preview-source">${escapeHtml(sourceText)}</span>`;
}
} else {
const translated = String(block.translated_text || '').trim().slice(0, 80);
const source = String(block.source_text || '').trim().slice(0, 80);
if (translated && source && translated !== source) {
previewHtml = `<span class="preview-translated">${escapeHtml(translated)}</span><span class="preview-source">${escapeHtml(source)}</span>`;
} else if (translated) {
previewHtml = `<span class="preview-translated">${escapeHtml(translated)}</span>`;
} else if (source) {
previewHtml = `<span class="preview-source">${escapeHtml(source)}</span>`;
}
}
let editorHtml = '';
if (typeLabel === 'img' || typeLabel === 'image') {
// 图片类型:只显示URL和alt文本编辑,不需要原文译文
const imgSrc = block.meta && block.meta.src ? String(block.meta.src).trim() : '';
const imgAlt = block.meta && block.meta.alt ? String(block.meta.alt).trim() : '';
editorHtml = `
<div class="block-edit-area" style="grid-template-columns: 1fr;">
<div>
<label>图片URL:</label>
<input type="text" class="block-img-src" value="${escapeAttr(imgSrc)}" placeholder="https://...">
</div>
<div>
<label>图片描述 (Alt Text):</label>
<textarea class="block-img-alt" style="min-height: 60px;">${escapeHtml(imgAlt)}</textarea>
</div>
</div>
`;
} else if (typeLabel === 'ul' || typeLabel === 'ol') {
const items = Array.isArray(block.items) ? block.items : [];
const translatedItems = Array.isArray(block.translated_items) ? block.translated_items : [];
const rows = items.map((item, i) => `
<li>
<input type="text" data-field="source" data-idx="${i}" value="${escapeAttr(item)}" placeholder="原文 ${i+1}">
<input type="text" data-field="translated" data-idx="${i}" value="${escapeAttr(translatedItems[i] || '')}" placeholder="译文 ${i+1}">
</li>
`).join('');
editorHtml = `
<div class="block-edit-area" style="grid-template-columns: 1fr;">
<ul>${rows}</ul>
</div>
`;
} else {
editorHtml = `
<div class="block-edit-area">
<div>
<label>原文 (Source):</label>
<textarea class="block-source-edit">${escapeHtml(block.source_text || '')}</textarea>
</div>
<div>
<label>译文 (Translated):</label>
<textarea class="block-translated-edit">${escapeHtml(block.translated_text || '')}</textarea>
</div>
</div>
`;
}
div.innerHTML = `
<div class="block-header">
<span>Block ${index + 1} <span class="block-type" title="点击修改类型">${escapeHtml(typeLabel)}</span></span>
<span style="font-size: 0.85rem; color: #888;">ID: ${escapeHtml(block.id)}</span>
</div>
<div class="block-content-preview">${previewHtml || ''}</div>
<div class="block-edit-area-wrapper" style="display:none;">
${editorHtml}
<div class="block-actions">
<button class="btn small secondary" data-action="save"><i class="fas fa-save"></i> 保存</button>
<button class="btn small" data-action="up"><i class="fas fa-arrow-up"></i> 上移</button>
<button class="btn small" data-action="down"><i class="fas fa-arrow-down"></i> 下移</button>
<button class="btn small danger" data-action="remove"><i class="fas fa-trash"></i> 删除</button>
</div>
</div>
`;
div.addEventListener('click', (e) => {
const tag = e.target.tagName;
if (tag === 'BUTTON' || tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || e.target.closest('button')) return;
const wrapper = div.querySelector('.block-edit-area-wrapper');
const isExpanded = div.classList.contains('expanded');
document.querySelectorAll('.block-item.expanded').forEach(item => {
if (item !== div) {
item.classList.remove('expanded');
const w = item.querySelector('.block-edit-area-wrapper');
if (w) w.style.display = 'none';
}
});
div.classList.toggle('expanded', !isExpanded);
wrapper.style.display = isExpanded ? 'none' : 'block';
});
div.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (!btn) return;
e.stopPropagation();
const action = btn.dataset.action;
if (action === 'save') updateBlockFromUI(block.id);
if (action === 'up') moveBlock(block.id, -1);
if (action === 'down') moveBlock(block.id, 1);
if (action === 'remove') removeBlock(block.id);
});
// 点击类型标签可修改 block.type(p/h1/h2/...)
div.addEventListener('click', (e) => {
const typeEl = e.target.closest('.block-type');
if (!typeEl) return;
e.stopPropagation();
const allowedTypes = ['p','h1','h2','h3','h4','blockquote','ul','ol','code','img'];
const current = String(block.type || 'p').toLowerCase();
// 用 select 临时替换,修改后重渲染恢复为 span
const sel = document.createElement('select');
sel.className = 'block-type-select';
allowedTypes.forEach(t => {
const opt = document.createElement('option');
opt.value = t;
opt.textContent = t;
sel.appendChild(opt);
});
sel.value = allowedTypes.includes(current) ? current : 'p';
typeEl.replaceWith(sel);
sel.focus();
const commit = () => {
const newType = String(sel.value || 'p').toLowerCase();
setBlockType(block.id, newType);
};
sel.addEventListener('change', commit);
sel.addEventListener('blur', commit);
});
translationPanel.appendChild(div);
});
}
/**
* 规范化行:将文本按行分割,去除空行和首尾空白
* @param {string} t - 输入文本
* @returns {string[]} 规范化后的行数组
*/
function normalizeLines(t) {
return String(t || '')
.replace(/\r\n/g, '\n')
.split('\n')
.map(x => x.trim())
.filter(Boolean);
}
/**
* 设置块的类型:修改块的type属性并重新渲染
* 支持类型转换:文本类型 <-> 列表类型(ul/ol)
* @param {string} blockId - 块ID
* @param {string} newType - 新类型(p/h1/h2/h3/h4/blockquote/ul/ol/code/img)
*/
function setBlockType(blockId, newType) {
if (!editedBlocks) return;
const block = editedBlocks.find(b => b.id === blockId);
if (!block) return;
const oldType = String(block.type || 'p').toLowerCase();
const nt = String(newType || 'p').toLowerCase();
if (oldType === nt) {
renderTranslationPanel();
return;
}
// 转换为img类型
if (nt === 'img' || nt === 'image') {
if (!block.meta) block.meta = {};
// 如果之前没有meta.src,尝试从source_text获取
if (!block.meta.src && block.source_text) {
block.meta.src = block.source_text;
}
block.source_text = '';
block.translated_text = '';
delete block.items;
delete block.translated_items;
}
// 从img类型转换出来
else if (oldType === 'img' || oldType === 'image') {
const imgSrc = block.meta && block.meta.src ? block.meta.src : '';
const imgAlt = block.meta && block.meta.alt ? block.meta.alt : '';
if (nt === 'ul' || nt === 'ol') {
block.items = imgSrc ? [imgSrc] : [];
block.translated_items = imgAlt ? [imgAlt] : [];
delete block.source_text;
delete block.translated_text;
} else {
block.source_text = imgSrc;
block.translated_text = imgAlt;
delete block.items;
delete block.translated_items;
}
}
// ul/ol 需要 items;从文本类型切过来时自动按行拆分
else if (nt === 'ul' || nt === 'ol') {
const srcItems = Array.isArray(block.items) && block.items.length ? block.items : normalizeLines(block.source_text);
const trItems = Array.isArray(block.translated_items) && block.translated_items.length ? block.translated_items : normalizeLines(block.translated_text);
block.items = srcItems;
block.translated_items = trItems;
delete block.source_text;
delete block.translated_text;
} else if (oldType === 'ul' || oldType === 'ol') {
// 从列表切回文本:把 items 合并为多行文本
block.source_text = (Array.isArray(block.items) ? block.items : []).join('\n');
block.translated_text = (Array.isArray(block.translated_items) ? block.translated_items : []).join('\n');
delete block.items;
delete block.translated_items;
}
block.type = nt;
renderTranslationPanel();
generateOutput();
updateStatus('已修改 block 类型', false);
}
/**
* 从UI更新块数据:将用户在界面中的编辑保存到块对象
* @param {string} blockId - 块ID
*/
function updateBlockFromUI(blockId) {
const block = editedBlocks.find(b => b.id === blockId);
if (!block) return;
const container = document.querySelector(`.block-item[data-block-id="${cssEscape(blockId)}"]`);
if (!container) return;
const type = String(block.type || 'p').toLowerCase();
if (type === 'img' || type === 'image') {
// 图片类型:保存到meta字段
const srcInput = container.querySelector('.block-img-src');
const altInput = container.querySelector('.block-img-alt');
if (!block.meta) block.meta = {};
if (srcInput) block.meta.src = srcInput.value;
if (altInput) block.meta.alt = altInput.value;
// 清空source_text和translated_text
block.source_text = '';
block.translated_text = '';
} else if (type === 'ul' || type === 'ol') {
const sources = [...container.querySelectorAll('input[data-field="source"]')];
const trans = [...container.querySelectorAll('input[data-field="translated"]')];
block.items = sources.map(i => i.value);
block.translated_items = trans.map(i => i.value);
} else {
const sourceArea = container.querySelector('.block-source-edit');
const transArea = container.querySelector('.block-translated-edit');
if (sourceArea) block.source_text = sourceArea.value;
if (transArea) block.translated_text = transArea.value;
}
updateStatus('已保存 block 更改', false);
generateOutput();
}
/**
* 移动块:在列表中上移或下移块的位置
* @param {string} blockId - 块ID
* @param {number} direction - 移动方向(-1:上移, 1:下移)
*/
function moveBlock(blockId, direction) {
const idx = editedBlocks.findIndex(b => b.id === blockId);
if (idx < 0) return;
const nidx = idx + direction;
if (nidx < 0 || nidx >= editedBlocks.length) return;
const tmp = editedBlocks[idx];
editedBlocks[idx] = editedBlocks[nidx];
editedBlocks[nidx] = tmp;
renderTranslationPanel();
generateOutput();
}
/**
* 删除块:从列表中移除指定块
* @param {string} blockId - 块ID
*/
function removeBlock(blockId) {
if (!confirm('确定要删除这个 block 吗?')) return;
editedBlocks = editedBlocks.filter(b => b.id !== blockId);
renderTranslationPanel();
generateOutput();
}
/**
* 重置编辑:恢复到原始JSON数据
*/
function resetEdits() {
if (!originalJson) return;
editedBlocks = deepClone(originalJson.blocks);
renderTranslationPanel();
generateOutput();
updateStatus('已重置所有编辑', false);
}
// ----------------------------
// 模块管理
// ----------------------------
function renderDefaultModules() {
defaultModulesStartContainer.innerHTML = '';
defaultModulesEndContainer.innerHTML = '';
// 分离开头和结尾模块
const startModules = defaultModulesConfig.filter(m => m.position === 'start');
const endModules = defaultModulesConfig.filter(m => m.position === 'end');
// 将固定模块和普通模块分开
const normalStartModules = startModules.filter(m => m.id !== 'module_sign');
const fixedStartModule = startModules.find(m => m.id === 'module_sign');
const normalEndModules = endModules.filter(m => m.id !== 'module_Agreement');
const fixedEndModule = endModules.find(m => m.id === 'module_Agreement');
// 渲染开头模块 - 先渲染普通模块,再渲染固定模块
normalStartModules.forEach(m => {
const div = document.createElement('div');
div.className = 'module-item module-default module-compact';
div.innerHTML = `
<label class="module-checkbox">
<input type="checkbox" ${m.enabled ? 'checked' : ''} data-mid="${escapeAttr(m.id)}" data-field="enabled">
<span>${escapeHtml(m.title)}</span>
</label>
`;
defaultModulesStartContainer.appendChild(div);
});
// 渲染固定的工具署名模块(放在最下方)
if (fixedStartModule) {
const div = document.createElement('div');
div.className = 'module-item module-default module-compact';
div.innerHTML = `
<label class="module-checkbox module-fixed">
<input type="checkbox" ${fixedStartModule.enabled ? 'checked' : ''} disabled data-mid="${escapeAttr(fixedStartModule.id)}" data-field="enabled">
<span>${escapeHtml(fixedStartModule.title)}</span>
</label>
`;
defaultModulesStartContainer.appendChild(div);
}
// 渲染结尾模块 - 先渲染普通模块,再渲染固定模块
normalEndModules.forEach(m => {
const div = document.createElement('div');
div.className = 'module-item module-default module-compact';
div.innerHTML = `
<label class="module-checkbox">
<input type="checkbox" ${m.enabled ? 'checked' : ''} data-mid="${escapeAttr(m.id)}" data-field="enabled">
<span>${escapeHtml(m.title)}</span>
</label>
`;
defaultModulesEndContainer.appendChild(div);
});
// 渲染固定的开源协议模块(放在最下方)
if (fixedEndModule) {
const div = document.createElement('div');
div.className = 'module-item module-default module-compact';
div.innerHTML = `
<label class="module-checkbox module-fixed">
<input type="checkbox" ${fixedEndModule.enabled ? 'checked' : ''} disabled data-mid="${escapeAttr(fixedEndModule.id)}" data-field="enabled">
<span>${escapeHtml(fixedEndModule.title)}</span>
</label>
`;
defaultModulesEndContainer.appendChild(div);
}
}
function renderCustomModules() {
customModulesContainer.innerHTML = '';
customModules.forEach((m) => {
const div = document.createElement('div');
div.className = 'module-item';
div.innerHTML = `
<h4>${escapeHtml(m.title)}</h4>
<input type="text" value="${escapeAttr(m.title)}" placeholder="模块标题" data-mid="${escapeAttr(m.id)}" data-field="title">
<textarea placeholder="模块内容(支持 BBCode/Markdown)" data-mid="${escapeAttr(m.id)}" data-field="content">${escapeHtml(m.content)}</textarea>
<div class="module-controls">
<label><input type="checkbox" ${m.enabled ? 'checked' : ''} data-mid="${escapeAttr(m.id)}" data-field="enabled"> 启用</label>
<label>位置:
<select data-mid="${escapeAttr(m.id)}" data-field="position">
<option value="start" ${m.position === 'start' ? 'selected' : ''}>开头</option>
<option value="end" ${m.position === 'end' ? 'selected' : ''}>结尾</option>
</select>
</label>
<button class="btn small danger" data-mid="${escapeAttr(m.id)}" data-action="remove"><i class="fas fa-trash"></i> 删除</button>
</div>
`;
customModulesContainer.appendChild(div);
});
}
customModulesContainer.addEventListener('input', debounce((e) => {
const el = e.target;
const id = el.dataset.mid;
const field = el.dataset.field;
if (!id || !field) return;
const mod = customModules.find(x => x.id === id);
if (!mod) return;
if (field === 'title') mod.title = String(el.value || '');
if (field === 'content') mod.content = String(el.value || '');
generateOutput();
}, 150));
customModulesContainer.addEventListener('change', (e) => {
const el = e.target;
const id = el.dataset.mid;
const field = el.dataset.field;
if (!id || !field) return;
const mod = customModules.find(x => x.id === id);
if (!mod) return;
if (field === 'enabled') mod.enabled = !!el.checked;
if (field === 'position') mod.position = String(el.value);
generateOutput();
});
customModulesContainer.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (!btn) return;
const id = btn.dataset.mid;
const action = btn.dataset.action;
if (action === 'remove') {
customModules = customModules.filter(x => x.id !== id);
renderCustomModules();
generateOutput();
updateStatus('自定义模块已删除', false);
}
});
function addCustomModule() {
const id = `custom_module_${Date.now()}`;
customModules.push({
id,
title: `自定义模块 ${customModules.length + 1}`,
content: '在此处输入自定义模块的内容...',
position: 'end',
enabled: true
});
renderCustomModules();
generateOutput();
updateStatus('已添加自定义模块', false);
}
function saveCustomModulesToLocalStorage() {
try {
localStorage.setItem('customModules', JSON.stringify(customModules));
updateStatus('自定义模块配置已保存到本地', false);
} catch (e) {
updateStatus('保存失败:' + e.message, true);
}
}
function loadCustomModulesFromLocalStorage(silent) {
try {
const saved = localStorage.getItem('customModules');
if (saved) {
const parsed = JSON.parse(saved);
customModules = Array.isArray(parsed) ? parsed : [];
renderCustomModules();
if (!silent) {
generateOutput();
updateStatus('自定义模块配置已从本地加载', false);
}
} else if (!silent) {
updateStatus('未找到保存的配置', true);
}
} catch (e) {
if (!silent) updateStatus('加载失败:' + e.message, true);
}
}
// ----------------------------
// 输出生成:实时生成BBCode和Markdown输出
// ----------------------------
/**
* 生成输出:将编辑后的块转换为BBCode和Markdown格式
* 同时更新预览区域(使用BBCode转HTML)
*/
function generateOutput() {
if (!originalJson || !editedBlocks) return;
try {
const bbcode = convertJsonToBBCode(originalJson, editedBlocks);
const markdown = convertJsonToMarkdown(originalJson, editedBlocks);
bbcodeOutput.value = bbcode;
markdownOutput.value = markdown;
// 预览永远以 BBCode 输出为准
previewArea.innerHTML = bbcodeToHtml(bbcode);
updateStatus('输出已更新', false);
} catch (err) {
updateStatus(`生成输出时出错: ${err.message}`, true);
previewArea.innerHTML = `<p style="color: #e74c3c; padding: 20px;">生成预览时出错: ${escapeHtml(err.message)}</p>`;
}
}
/**
* 将JSON数据转换为BBCode格式
* @param {Object} json - 原始JSON数据(包含标题、作者等元信息)
* @param {Array} blocks - 编辑后的块数组
* @returns {string} BBCode格式的完整文本
*/
function convertJsonToBBCode(json, blocks) {
const title = String((json.translated_title || json.title || '')).trim();
const enTitle = String((json.title || '')).trim();
const url = String((json.url || '')).trim();
const author = String((json.author || '')).trim();
const desc = String((json.description || '')).trim();
const release = formatDateTimeCN(json.release_date);
let out = '';
out += `[align=center][size=5][b]NEWS[/b][/size][/align]\n`;
if (title) out += `[align=center][size=6][b]${escapeBB(title)}[/b][/size][/align]\n`;
if (enTitle && enTitle !== title) out += `[align=center][size=4]${escapeBB(enTitle)}[/size][/align]\n\n`;
const metaLines = [];
if (release) metaLines.push(`[b]时间:[/b] ${escapeBB(release)}`);
if (author) metaLines.push(`[b]作者:[/b] ${escapeBB(author)}`);
if (url) metaLines.push(`[b]原文:[/b] [url=${escapeBB(url)}]${escapeBB(url)}[/url]`);
if (desc) metaLines.push(`[b]简介:[/b][i]${escapeBB(desc)}[/i]`);
if (metaLines.length) out += `[quote]${metaLines.join('\n')}[/quote]`;
out += `\n`;
if (!blocks || !blocks.length) {
out += `[i](未找到 blocks 或 blocks 为空)[/i]`;
return insertModulesBBCode(out.trim());
}
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i];
const blockType = String(block.type || 'p').toLowerCase();
// 如果是 li 类型,收集连续的 li blocks 并构建嵌套列表
if (blockType === 'li') {
const listBlocks = [];
let j = i;
while (j < blocks.length && String(blocks[j].type || 'p').toLowerCase() === 'li') {
listBlocks.push(blocks[j]);
j++;
}
// 渲染嵌套列表
out += renderNestedListBBCode(listBlocks) + '\n';
// 跳过已处理的 li blocks
i = j - 1;
const next = blocks[i + 1];
const nextType = next ? String(next.type || 'p').toLowerCase() : '';
if (['h1','h2'].includes(nextType)) out += `\n[hr]\n`;
continue;
}
out += renderBlockBBCode(block) + '\n';
const next = blocks[i + 1];
const nextType = next ? String(next.type || 'p').toLowerCase() : '';
// 只在主要标题前添加分隔符
if (['h1','h2'].includes(nextType)) out += `\n[hr]\n`;
}
return insertModulesBBCode(out.trim());
}
/**
* 插入模块到BBCode内容:在主内容前后插入启用的模块
* @param {string} mainBB - 主BBCode内容
* @returns {string} 插入模块后的完整BBCode
*/
function insertModulesBBCode(mainBB) {
const modules = collectEnabledModules().map(m => ({
position: m.position,
content: sanitizeForBBCode(m.content)
}));
const start = modules.filter(x => x.position === 'start').map(x => x.content).filter(Boolean).join('\n\n');
const end = modules.filter(x => x.position === 'end').map(x => x.content).filter(Boolean).join('\n\n');
let final = '';
if (start) final += start + '\n\n[hr]\n\n';
final += mainBB;
if (end) final += '\n\n[hr]\n\n' + end;
return final.trim();
}
/**
* 将JSON数据转换为Markdown格式
* @param {Object} json - 原始JSON数据(包含标题、作者等元信息)
* @param {Array} blocks - 编辑后的块数组
* @returns {string} Markdown格式的完整文本
*/
function convertJsonToMarkdown(json, blocks) {